Recently I posted a project tutorial showing how to utilize peripherals such as the PMODs and Raspberry Pi GPIO header on the Kria KR260 carrier board that are connected to the Kria K26 FPGA via its programmable logic (PL). This is done by generating a bitstream in the KR260's Vivado project with the updates to the block design and/or HDL code that also includes the output of a.bin bitstream file, then exporting the hardware platform file (XSA) to generate a new device tree blob with that includes the overlay fragments that were added to the block design and/or HDL code in Vivado. This.bin bitstream file and new device tree blob are then loaded on the KR260 using the xmutil command just like an accelerated application would be.
In my previous post, I demonstrated how to access the PMODs and RPi header on the KR260 from the Linux userspace using the Sysfs driver to control the AXI GPIO I had added to the block design in Vivado. In that case the custom PL design was just adding an interface for a peripheral, but I thought it was worth demonstrating how an RTL design can be made and ran in the PL of the KR260 completely independent of the Linux image running on the ARM-core processors of the KR260.
Because I happened upon the circuit still wired up on one of my breadboards, I decided on using the RTL design I created back at the beginning of this year for my QDSP-6061 vintage bubble displays. This is a simple state machine that runs through a loop with the proper timing of setting each of the common-cathode 7-segment displays to scroll some hard-coded text across the 5 of them from right to left. It was the perfect thing to use in this example since the logic is fully contained within itself and doesn't require any other input aside from a 100MHz clock and a reset signal.
In my original project, it had it hard-coded to "hello 2022" since it was my first post of the year. I slightly tweaked it to say "hello KR260" for this project, but otherwise left the logic the same. So refer to that original post if you want the details of how the QDSP-6061 works and is being driven, the intent of this post is just to demonstrate the divide between the KR260's Linux image and PL.
To start, the custom RTL design needs to be added to the Vivado project and a bitstream generated for it. So I'm adding the QDSP-6061 controller state machine in Verilog and routing it to the PMOD1 and PMOD2 IO pins I have the breadboarded circuit connected to: the 8 anode pins are all on PMOD1 and the 5 cathode pins are on PMOD2.
Also, if it's not already apparent, I'm using the Vivado project I made for the KR260 that has the AXI GPIO for interfacing with the PMODs and RPi header. I'm only going to reroute the 8 PMOD1 IO pins and 5 PMOD2 IO pins needed to the output of the QDSP-6061 controller state machine and leave the rest routed to the AXI GPIO blocks in the block design so I can still use them from the Linux userspace on the KR260 for other stuff.
First, like I mentioned before, the state machine controlling the QDSP-6061 just needs a 100MHz clock and a reset signal for inputs. Luckily the pl_clk1 and corresponding pl_resetn1 signals from the Zynq MPSoC processing system IP are exactly what I need and are currently unused, so I simply made them available externally to the block design by right-clicking on each of the pins and selecting the Make External option. This simply creates a port and connects the pins to it so they show up in the instantiation template of the block design of the top level file.
With the pl_clk1 and corresponding pl_resetn1 signals added, the block design modifications are complete so I ran validation, saved it, and generated it by selecting IP Integrator > Generate Block Design.
Next Add QDSP-6061 Verilog to Vivado project by creating a new design source file and copy+pasting my old code into it (see qdsp_6061_cntlr.v attached below).
Like I mentioned in the last post, I've created a custom top level HDL wrapper (attached below) instead of letting Vivado auto-manage one. So after adding the pl_clk1 and corresponding pl_resetn1 signals to the block design instantiation template, I commented out the routing of the 8 PMOD1 IO signals:
As well as the routing of the first 5 PMOD2 IO signals:
Then instantiated the QDSP-6061 controller state machine and assigned the PMOD1 and PMOD2 signals to the anode and cathode registers.
With the PMOD signals rerouted in the top level HDL wrapper, there is no need to change the original PMOD constraints file I create in my previous post (also attached below).
At this point, the hardware design is complete and a bitstream needs to be generated for it. However, since the ARM processor is already running by the time that the PL is being flashed with this bitstream, the bitstream file does need the extra header configuration that a.bit file contains. Instead, a.bin file bitstream is what is needed. By default, Vivado doesn't output a.bin since it isn't typically needed so it needs to be enabled in the project settings.
Open Settings from the Flow Navigator window, then in the Bitstream tab, enable the option for -bin_file.
Click Apply, then OK to close the Setting window. Run synthesis, implementation, and generate bitstream for the design, as well as export the platform the same way as before. Be sure that in the exported platform, the option to include the bitstream is enabled:
Just to keep everything organized, I created a folder in the top level directory of the Vivado project to store the files for this hardware design and create the device tree blob for it in:
~$ cd ./Kria_KR260/
~/Kria_KR260$ mkdir -p ./qdsp_6061_rtl
~/Kria_KR260$ cd ./qdsp_6061_rtl/
The device tree blob containing the overlay nodes needs to be compiled for the design, which is easily done by using the Xilinx Software Command Line Tools (XSCT):
~/Kria_KR260/qdsp_6061_rtl$ source /tools/Xilinx/Vitis/2022.1/settings64.sh
~/Kria_KR260/qdsp_6061_rtl$ xsct
Open the exported XSA from Vivado and use the createdts
command to create the device tree source files for the PL design:
xsct% hsi::open_hw_design ../kria_base.xsa
xsct% createdts -hw ../kria_base.xsa -zocl -platform-name kria_kr260 -git-branch xlnx_rel_v2022.1 -overlay -compile -out ./dtg_qdsp_6061
xsct% exit
After exiting XSCT, use the standard Linux device tree compiler (dtc) to compile the source files into the needed device tree blob:
~/Kria_KR260/qdsp_6061_rtl$ dtc -@ -O dtb -o ./dtg_qdsp_6061/dtg_qdsp_6061/kria_kr260/psu_cortexa53_0/device_tree_domain/bsp/pl.dtbo ./dtg_qdsp_6061/dtg_qdsp_6061/kria_kr260/psu_cortexa53_0/device_tree_domain/bsp/pl.dtsi
Transfer RTL Design Files to KR260I also like to create a folder to copy the necessary design files to that need to be uploaded to the KR260 (note: the Kria_KR260 directory is the top level directory of the Vivado project):
~/Kria_KR260/qdsp_6061_rtl$ mkdir -p file_transfer
~/Kria_KR260/qdsp_6061_rtl$ cd ./file_transfer/
It’s here I’ll create the description file, shell.json, for the design:
~/Kria_KR260//qdsp_6061_rtl/file_transfer$ gedit shell.json
And copy+paste the following to shell.json:
{
”shell_type" : “XRT_FLAT",
"num_slots": "1"
}
Then copy the generated device tree blob and bitstream.bin file into the folder:
~/Kria_KR260/qdsp_6061_file_transfer$ cp ../dtg_qdsp_6061/dtg_qdsp_6061/kria_kr260/psu_cortexa53_0/device_tree_domain/bsp/pl.dtbo ./
~/Kria_KR260/qdsp_6061_file_transfer$ cp ../../Kria_KR260.runs/impl_1/kr260_top.bin ./
Rename the device tree blob and.bin file to the same thing (the only difference should be their respect file extensions (also change the.bin extension to.bit.bin):
~/Kria_KR260//qdsp_6061_rtl/file_transfer$ mv kr260_top.bin kr260_qdsp_6061.bit.bin
~/Kria_KR260//qdsp_6061_rtl/file_transfer$ mv pl.dtbo kr260_qdsp_6061.dtbo
Then with the KR260 booted up and running with the new SD card image generated in the previous steps and connected to the local network, transfer the new PL design files to it:
~/Kria_KR260/qdsp_6061_rtl/file_transfer$ scp kr260_qdsp_6061.dtbo kr260_qdsp_6061.bit.bin shell.json petalinux@<KR260’s IP address>:/home/petalinux
Run RTL Design on KR260After the files have been successfully transferred to the KR260, create a directory in the /lib/firmware/xilinx
directory with the same name as was given to the device tree blob and.bin file and copy them into it:
xilinx-kr260-starterkit-20221:~$ sudo mkdir /lib/firmware/xilinx/kr260_qdsp_6061
xilinx-kr260-starterkit-20221:~$ sudo cp kr260_qdsp_6061.dtbo kr260_qdsp_6061.bit.bin shell.json /lib/firmware/xilinx/kr260_qdsp_6061
At this point, the RTL design will show up just like an accelerated application would using the xmutil commands:
xilinx-kr260-starterkit-20221:~$ sudo xmutil listapps
Unload the default application then load the PL design which flashes the new bitstream into the PL and loads its device tree overlay:
xilinx-kr260-starterkit-20221:~$ sudo xmutil unloadapp
xilinx-kr260-starterkit-20221:~$ sudo xmutil loadapp kr260_qdsp_6061
When the device tree overlay loads, the terminal will print out as such indicating the new device tree nodes for each of the AXI GPIO IP blocks are now present in the system:
Since the PL is flashed with the bitstream containing the logic with the QDSP-6061 controller state machine at this point, it fires up with the "hello KR260" text scrolling across it without any further intervention from the Linux userspace:
And just to demonstrate the difference (and flexibility) in routing in the PL, I left the other 3 IOs on PMOD2 connected to the AXI GPIO block so I could also play with my new succulent LEDs I found on Adafruit:
xilinx-kr260-starterkit-20221:~$ ls /sys/class/gpio/
export gpiochip0 gpiochip440 gpiochip468 gpiochip476 gpiochip484 gpiochip492 gpiochip500 gpiochip508 unexport
xilinx-kr260-starterkit-20221:~$ cat /sys/class/gpio/gpiochip484/label
80020000.gpio
xilinx-kr260-starterkit-20221:~$ echo 491 | sudo tee /sys/class/gpio/export
491
xilinx-kr260-starterkit-20221:~$ echo out | sudo tee /sys/class/gpio/gpio491/direction
out
xilinx-kr260-starterkit-20221:~$ echo 1 | sudo tee /sys/class/gpio/gpio491/value
1
xilinx-kr260-starterkit-20221:~$ echo 0 | sudo tee /sys/class/gpio/gpio491/value
0
Comments