FPGA programming using Device Tree Overlay (DTO)

Linux Drivers

Release Date
2023-07-22

The Device Tree Overlay (DTO) is used to reprogram an FPGA while Linux is running. The DTO overlay will add the child node and the fragments from the .dtbo file to the base device tree,, The newly added device node/drivers will be probed after Bitstream programming DTO contains:

  • Target FPGA Region - "target-path" or "target" - The insertion point where the the contents of the overlay will go into the live tree. target-path is a full path, while target is a phandle.
  • FPGA Image firmware file name - "firmware-name" - Specifies the name of the FPGA image file on the firmware search path. The search path is described in the firmware class documentation.
  • Image specific information - external-fpga-config : boolean, set if the FPGA has already been configured prior to Linux boot up.
  • Child devices - child nodes corresponding to hardware that will be loaded in this region of the FPGA.

Notes:

  • If the user tries to load the un-aligned bit/bin file the PL configuration takes a longer time when compared with aligned(word-aligned) bit/bin files.
  • Using overlay’s we can add a new node or add/update the existing node properties. But it will not allow replacing the existing nodes which are already part of the live tree.

Steps for programming the Full Bitstream using overlay

Copy the Full Bitstream (.bin) and pl.dtbo files into firmware folder

  • mkdir /configfs

  • mount -t configfs configfs /configfs

  • cd /configfs/device-tree/overlays/

  • mkdir full

  • echo -n "pl.dtbo" > full/path

Steps to remove the drivers got added as part of DTO

  • rmdir /configfs/device-tree/overlays/full

Devicetree overlay file contents example:

//Device Tree Example: Full Reconfiguration along with PS PL Configuration (Clock, resets and AFI)
// HSI Generated overlay/pl.dtsi file.
// Enable the axi-gpio interface
/dts-v1/;
/plugin/;
/ {
	fragment@0 { /* Bitstream  Fragment */ 
		target = <&fpga_full>;
		overlay0: __overlay__ {
			#address-cells = <2>;
			#size-cells = <2>;
			firmware-name = "design_1_wrapper.bit.bin";
			resets = <&rst 116>;
		};
	};
	fragment@1 { /* PS-PL configuration Fragment */
		target = <&amba>;
		overlay1: __overlay__ {
			afi0: afi0 {
				compatible = "xlnx,afi-fpga";
				config-afi = < 0 0>, <1 0>, <2 0>, <3 0>, <4 0>, <5 0>, <6 0>, <7 0>, <8 0>, <9 0>, <10 0>, <11 0>, <12 0>, <13 0>, <14 0>, <14 0>, <15 0x000>;
			};
			clocking0: clocking0 {
				#clock-cells = <0>;
				assigned-clock-rates = <99990005>;
				assigned-clocks = <&clk 71>;
				clock-output-names = "fabric_clk";
				clocks = <&clk 71>;
				compatible = "xlnx,fclk";
			};
		};
	};
	fragment@2 { /* PL Drivers Fragment */
		target = <&amba>;
		overlay2: __overlay__ {
			axi_gpio_0: gpio@a0000000 {
				#gpio-cells = <3>;
				#interrupt-cells = <2>;
				clock-names = "s_axi_aclk";
				clocks = <&clk 71>;
				compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";
				gpio-controller ;
				interrupt-controller ;
				interrupt-names = "ip2intc_irpt";
				interrupt-parent = <&gic>;
				interrupts = <0 89 4>;
				reg = <0x0 0xa0000000 0x0 0x1000>;
				xlnx,all-inputs = <0x1>;
				xlnx,all-inputs-2 = <0x0>;
				xlnx,all-outputs = <0x0>;
				xlnx,all-outputs-2 = <0x1>;
				xlnx,dout-default = <0x00000000>;
				xlnx,dout-default-2 = <0xAAAAAAAA>;
				xlnx,gpio-width = <0x8>;
				xlnx,gpio2-width = <0x8>;
				xlnx,interrupt-present = <0x1>;
				xlnx,is-dual = <0x1>;
				xlnx,tri-default = <0xFFFFFFFF>;
				xlnx,tri-default-2 = <0xFFFFFFFF>;
			};
			psu_ctrl_ipi: PERIPHERAL@ff380000 {
				/* This is a place holder node for a custom IP, user may need to update the entries */
				compatible = "xlnx,PERIPHERAL-1.0";
				reg = <0x0 0xff380000 0x0 0x80000>;
			};
			psu_message_buffers: PERIPHERAL@ff990000 {
				/* This is a place holder node for a custom IP, user may need to update the entries */
				compatible = "xlnx,PERIPHERAL-1.0";
				reg = <0x0 0xff990000 0x0 0x10000>;
			};
		};
	};
};