Scope

As implemented in the Xillinux distribution for Cyclone V SoC, this post outlines the considerations for setting the parameters of a custom IP's entry in the device tree.

The issue of device trees for Embedded Linux is discussed in general in a separate tutorial, which highlights Xilinx’ Zynq devices. On this page, the specific details of Altera’s Cyclone V SoC device are shown.

Memory mapping

The preferred way to connect a register-mapped peripheral to the processor, is implementing a full AXI3 slave (as opposed to AXI Lite), and attach it to the HPS’ h2f_lw_axi_master lightweight master. It’s also possible to use an IP core for this, such as Xillybus Lite, which is included in Xillinux.

When attaching the peripheral in Qsys, its address on the bus is chosen. It may be somewhat confusing that the addresses are based at zero, but when a slave is connected to the LW master, the processor maps it at an offset of 0xff2000000 (see page 44 (1-16) of the Cyclone V Device Handbook, volume 3). Physical address zero is occupied by the SDRAM.

For example, in the sample Qsys project for Sockit/Linaro, Altera’s VGA controller (a.k.a. alt_vip_vfr_vga) is mapped as an Avalon Slave at addresses 0x100-0x17f. The relevant segment in the DTS file goes

		bridge@0xff200000 {
			compatible = "altr,h2f_lw_bridge-1.0", "simple-bus";
			reg = <0xff200000 0x200000>;
			#address-cells = <0x1>;
			#size-cells = <0x1>;
			ranges = <0x100 0xff200100 0x80>;

			vip@0x100 {
				compatible = "ALTR,vip-frame-reader-13.0", "ALTR,vip-frame-reader-9.1";
				reg = <0x100 0x80>;
				max-width = <0x400>;
				max-height = <0x300>;
				mem-word-width = <0x80>;
				bits-per-color = <0x8>;
			};
		};

The device is enclosed in a “bridge” subclause, as a way to express that the peripheral is attached to the lightweight bridge, and therefore needs an address shift. Consequently, the I/O memory mapping on a live system can look as follows:

# cat /proc/iomem
00000000-3fffffff : System RAM
  00008000-004f720b : Kernel code
  00528000-0059642b : Kernel data
ff200100-ff20017f : ff200100.vip
ff702000-ff703fff : /soc/ethernet@ff702000
ff704000-ff704fff : /soc/dwmmc0@ff704000
ff705000-ff705fff : ff705000.spi
ff900000-ff9fffff : denali-dt
ffa00000-ffa00fff : ff705000.spi
ffb40000-ffb4fffe : dwc_otg
ffb80000-ffb8ffff : denali-dt
ffc02000-ffc0201f : serial
ffc03000-ffc0301f : serial
ffc04000-ffc04fff : ffc04000.i2c
fff00000-fff00fff : fff00000.spi
fff01000-fff01fff : fff01000.spi

This is consistent with the DTS above.

Note that the bridge itself doesn’t appear in /proc/iomem, but the driver using the specific segment. As a matter of fact, it appears like no Linux driver matches the “compatible” assignment for the bridge in the DTS file, so it seems to have no other effect than shifting the addresses.

While the bridge enclosure is elegant, the kernel API support for it is incomplete as of kernel version 3.8.0. In particular, the of_address_to_resource() call fails if the device is enclosed in a bridge clause in the DTS file.  The current recommendation is therefore to use the good old notation, and drop the bridge in the DTS: Map to 0xff200100 instead of 0x100. For example,

xillybus@ff200100 {
    compatible = "xlnx,xillybus-1.00.a";
    reg = < 0xff200100 0x00000080 >;
    interrupts = < 0 40 1 >;
    interrupt-parent = <&intc>;
} ;

Interrupt settings

The hardware processor has 64 interrupt request inputs, divided into two vectors, each 32 bits wide. In Qsys, these vectors are presented as two “rails” for connecting interrupts to the HPS processor. When an interrupt is attached, a number appears at the attachment point. The question is how this translates into numbers in the “interrupts” assignment in the DTS file’s entry.

As seen in the example above, three numbers are assigned to “interrupt” (0, 40 and 1 in the example). The rules for choosing these numbers are given for the Zynq processor in this blog post. The blog post, which relates to a device by Xilinx, relies on the interrupt number that is given by Xilinx’ tool for picking the middle number (40 in the example). The two other numbers have the same meaning as in Zynq, so this is not repeated here.

For the sake of the impatient, this is the quick recipe for finding the middle number: Look up the interrupt’s connection in the .qsys file. It will be something like this:

 <connection
   kind="interrupt"
   version="13.0"
   start="hps_0.f2h_irq0"
   end="xillybus_0.interrupt_sender">
  <parameter name="irqNumber" value="0" />

 </connection>

If it says “f2h_irq0″ in the assignment to “start” (as shown above), take the number assigned to “irqNumber” (zero in the example above) and add 40 (that is, 0x28). If it says “f2h_irq1″, add 72 (that is, 0x48) instead.

As mentioned above, this covers the second out of three numbers assigned to “interrupts” in the DTS’ entry. The other two follow the exact rules of  that other post. In the sample (Xillybus) DTS entry above, an edge-triggered interrupt number 0 on f2h_irq0 is chosen.

Interrupt numbers explained

The HPS’ interrupt numbers are listed in table 6-3 in Cyclone V Device Handbook, vol. 3 (these numbers match those that appear in /proc/interrupts). The interrupts from the FPGA are listed as FPGA_IRQn, where nn goes from 0 to 63. The first 32 interrupts are related to the first 32-bit wide interrupt vector going to the HPS (a.k.a. f2h_irq0) and the following 32 interrupts relate to the second vector (f2h_irq1). FPGA_IRQ00 is mapped to interrupt number 72 of the processor, and these numbers are incremented along with n.

Since the bottom line rule in the post about Zynq was to subtract 32 from the processor’s interrupt number, we have a DTS identifier of 72-32=40 for the n=0. As for interrupt zero on f2h_irq1, it’s n=32, so we have 104-32=72.