Hello!
This guide will show you how can you port PYNQ for Eclypse-Z7. This should work with any tools version without major changes.
Project setup.First we need to download tools. We will need basic BSP for PYNQ project. Later I will show you how can you create device-tree overlay for ZMOD's so you could use them with PYNQ.
git clone https://github.com/Xilinx/PYNQ.git -b v2.5.1;
git clone https://github.com/Digilent/Eclypse-Z7 --recursive -b master;
You can find branches by looking at "Branches" or "Tags" in GitHub website.
Go to "Eclypse-Z7/os folder" and create petalinux bsp.
cd Eclypse-Z7/os;
petalinux-package --bsp -p ${PWD} -o Eclypse-Z7.bsp;
Building PYNQNow go to PYNQ folder and create new directory in "boards" named after your board.
Copy generated.bsp and create.spec file. All file names should be the same.
Create specification file. You can add more packages by appending them to "STAGE4_PACKAGES_Eclypse-Z7 := <some package>"
!IMPORTANT!
If you do not have license for HDMI IP-cores your build will fail, to prevent this download xilinx-zcu104-v2019.1-final.bsp and put it in ZCU104 folder.
We need unlimited sudo time, in console write
sudo visudo
Add:
<username> ALL=(ALL) NOPASSWD:ALL
at the end of the file.
Set up your system for PYNQ. Run setup_host.sh script.
cd PYNQ/sdbuild/scripts
./setup_host.sh
Go to "PYNQ/sdbuild" and run:
source <Xilinx/Vivado/..../settings64.sh>
source <Petalinux/..../settings.sh>
cd PYNQ/sdbuild
make BOARDS=Eclypse-Z7
After some time ready image should be in "PYNQ/sdbuild/output folder". Plug in SD-Card and flash it with Eclypse-Z7 image. Change boot method to SD-Card and power on board.
Open terminal check if the image is working.
Check your Eclypse-Z7 IP-address
Open browser and connect to board.
192.168.0.102:9090/
Default password is "xilinx".
Now we have the default image for built for Eclypse-Z7 without the programmable logic.
Basic PYNQ IP + Floating point adderNow we will create some IP cores for Eclypse-Z7 based on official PYNQ tutorial.
You can download ready project from github.
git clone https://github.com/bartokon/Eclypse-Z7-Notebooks.git;
cd Eclypse-Z7-Notebooks;
unzip PynqIPs -d PynqIPs/;
Open Vivado HLS 2019.1 and click open project and select extracted "PynqIPs" folder.
Right click on Source and Add files, select "pynqips.cpp" and "pynqips.h".
"pynqips.cpp"
#include "pynqips.h"
void mult_constant(stream_type* in_data, stream_type* out_data, ap_int<32> constant) {
#pragma HLS INTERFACE s_axilite register port=constant
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE axis port=in_data
#pragma HLS INTERFACE axis port=out_data
out_data->data = in_data->data * constant;
out_data->dest = in_data->dest;
out_data->id = in_data->id;
out_data->keep = in_data->keep;
out_data->last = in_data->last;
out_data->strb = in_data->strb;
out_data->user = in_data->user;
}
void add(int a, int b, int& c) {
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE s_axilite port=a
#pragma HLS INTERFACE s_axilite port=b
#pragma HLS INTERFACE s_axilite port=c
c = a + b;
}
void addfloat(float a, float b, float &c) {
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE s_axilite port=a
#pragma HLS INTERFACE s_axilite port=b
#pragma HLS INTERFACE s_axilite port=c
c = a + b;
}
"pynqips.h"
#include "hls_stream.h"
#include "ap_fixed.h"
#include "ap_axi_sdata.h"
typedef ap_axiu<32,1,1,1> stream_type;
void mult_constant(stream_type* in_data, stream_type* out_data, ap_int<32> constant);
void add(int a, int b, int& c);
void addfloat(float a, float b, float &c);
If you want to regenerate IP'core.
Basic project
Advanced project
Constrains for button and LEDs.
## Buttons
set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { btn0 }]; #IO_L11P_T1_SRCC Sch=btn[0]
set_property -dict { PACKAGE_PIN C18 IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L11N_T1_SRCC Sch=btn[1]
## RGB LEDs
set_property -dict { PACKAGE_PIN A17 IOSTANDARD LVCMOS33 } [get_ports { led0_b }]; #IO_L9N_T1_DQS_AD3N Sch=led0_b
set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports { led0_g }]; #IO_L8P_T1_AD10P Sch=led0_g
set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 } [get_ports { led0_r }]; #IO_L8N_T1_AD10N Sch=led0_r
set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports { led1_b }]; #IO_L9P_T1_DQS_AD3P Sch=led1_b
set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { led1_g }]; #IO_L10P_T1_AD11P Sch=led1_g
set_property -dict { PACKAGE_PIN A19 IOSTANDARD LVCMOS33 } [get_ports { led1_r }]; #IO_L10N_T1_AD11N Sch=led1_r
You can upload.bit and.hwh and.ipynb to "/home/xilinx/jupyter_notebooks" folder on your Eclypse-Z7.
Open "Welcome to PYNQ Eclypse-Z7.ipynb"
Now you can begin testing your system :)
#Work in progress
Creating device-tree overlayNow you need to create overlay with ZMOD's, the easiest thing to do is to clone ZMOD_ADC_DAC project from Digilent.
git clone https://github.com/Digilent/Eclypse-Z7 --recursive -b zmod_adc_dac/master;
Find system-user.dtsi and remove references to ZMOD's
System-user.dtsi is located in "Eclypse-Z7/os/project-spec/meta-user/recipes-bsp/device-tree/files/"
Why are we doing this?
ZMOD's are located in PL fabric, using PYNQ means that our system is not bound by PL, and we can reprogram it in any moment, that's why we are using device-tree overlays, to modify the existing device tree.
PYNQ by default enables FPGA-manager and device-tree-overlays, we can do that manually with petalinux, and we will get "raw" pl.dtsi file.
Go to Eclypse-Z7 "os" folder and configure petalinux to use FPGA-manager and overlays.
petalinux-config
Exit and save changes.
Run command:
petalinux-build -c device-tree
New device-tree with overlay fragments should be generated in "Eclypse-Z7/os/components/plnx_workspace/device-tree/device-tree/"
The next step is to create overlay for ZMODS, for that I will use PYNQ-PRIO.
git clone https://github.com/byuccl/PYNQ-PRIO.git -b master
cd PYNQ-PRIO/boards;
mkdir Eclypse-Z7;
cd Eclypse-Z7;
mkdir prio_linux;
cd prio_linux;
mkdir dtbo;
mkdir dtso;
Copy generated "pl.dtsi" to dtso folder with.dts extension.
cp Eclypse-Z7/os/components/plnx_workspace/device-tree/device-tree/pl.dtsi PYNQ-PRIO/boards/Eclypse-Z7/prio_linux/dtso/pl.dts
First remove firmware-name or else you will get some parsing bugs with.hwh file.
Let's apply modifications to "pl.dts" based on original device-tree appends.
&ZmodADC_0_AXI_ZmodADC1410_1 {
compatible = "generic-uio";
};
&ZmodDAC_0_AXI_ZmodDAC1411_v1_0_0 {
compatible = "generic-uio";
};
&amba_pl {
axidma_chrdev_0: axidma_chrdev@0 {
compatible = "xlnx,axidma-chrdev";
dmas = <&ZmodADC_0_axi_dma_0 0>;
dma-names = "rx_channel";
index = <0>;
};
axidma_chrdev_1: axidma_chrdev@1 {
compatible = "xlnx,axidma-chrdev";
dmas = <&ZmodDAC_0_axi_dma_1 0>;
dma-names = "tx_channel";
index = <1>;
};
};
#EDIT
Changing compatible to "generic-uio" means changing ZMOD driver for DAC/ADC, this could be the reason why segmentation fault occurs.
Find "ZmodADC_0_AXI_ZmodADC1410_1" in "pl.dts" and change:
compatible = "xlnx,AXI-ZmodADC1410-1.0";
to:
compatible = "generic-uio";
Apply same for "ZmodDAC_0_AXI_ZmodDAC1411_v1_0_0"
Now copy
axidma_chrdev_0: axidma_chrdev@0 {
compatible = "xlnx,axidma-chrdev";
dmas = <&ZmodADC_0_axi_dma_0 0>;
dma-names = "rx_channel";
index = <0>;
};
axidma_chrdev_1: axidma_chrdev@1 {
compatible = "xlnx,axidma-chrdev";
dmas = <&ZmodDAC_0_axi_dma_1 0>;
dma-names = "tx_channel";
index = <1>;
};
under "ZmodDAC_0_axi_dma_1"
You should also increment interrupt pins in device-overlay to prevent conflict with PYNQ
/ { amba {
fabric@40000000 {
compatible = "generic-uio";
reg = <0x40000000 0x10000>;
interrupt-parent = <&intc>;
interrupts = <0x0 0x1d 0x4>;
};
};
};
Ultimately you should get something like this:
/*
* CAUTION: This file is automatically generated by Xilinx.
* Version:
* Today is: Sat Oct 24 14:39:58 2020
*/
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&fpga_full>;
overlay0: __overlay__ {
#address-cells = <1>;
#size-cells = <1>;
};
};
fragment@1 {
target = <&amba>;
overlay1: __overlay__ {
afi0: afi0@f8008000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "xlnx,afi-fpga";
reg = <0xF8008000 0x1000>;
xlnx,afi-width = <0>;
};
clocking0: clocking0 {
#clock-cells = <0>;
assigned-clock-rates = <100000000>;
assigned-clocks = <&clkc 15>;
clock-output-names = "fabric_clk";
clocks = <&clkc 15>;
compatible = "xlnx,fclk";
};
};
};
fragment@2 {
target = <&amba>;
overlay2: __overlay__ {
#address-cells = <1>;
#size-cells = <1>;
ZmodADC_0_AXI_ZmodADC1410_1: AXI_ZmodADC1410@43c00000 {
clock-names = "s00_axi_aclk";
clocks = <&misc_clk_0>;
compatible = "generic-uio";
interrupt-names = "lIrqOut";
interrupt-parent = <&intc>;
interrupts = <0 30 4>;
reg = <0x43c00000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
misc_clk_0: misc_clk_0 {
#clock-cells = <0>;
clock-frequency = <50000000>;
compatible = "fixed-clock";
};
ZmodADC_0_axi_dma_0: dma@40400000 {
#dma-cells = <1>;
clock-names = "s_axi_lite_aclk", "m_axi_s2mm_aclk";
clocks = <&misc_clk_0>, <&misc_clk_1>;
compatible = "xlnx,axi-dma-7.1", "xlnx,axi-dma-1.00.a";
interrupt-names = "s2mm_introut";
interrupt-parent = <&intc>;
interrupts = <0 31 4>;
reg = <0x40400000 0x10000>;
xlnx,addrwidth = <0x20>;
xlnx,sg-length-width = <0x10>;
dma-channel@40400030 {
compatible = "xlnx,axi-dma-s2mm-channel";
dma-channels = <0x1>;
interrupts = <0 31 4>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
};
misc_clk_1: misc_clk_1 {
#clock-cells = <0>;
clock-frequency = <100000000>;
compatible = "fixed-clock";
};
ZmodDAC_0_AXI_ZmodDAC1411_v1_0_0: AXI_ZmodDAC1411_v1_0@43c10000 {
clock-names = "s00_axi_aclk";
clocks = <&misc_clk_0>;
compatible = "generic-uio";
reg = <0x43c10000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
ZmodDAC_0_axi_dma_1: dma@40410000 {
#dma-cells = <1>;
clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk";
clocks = <&misc_clk_0>, <&misc_clk_1>;
compatible = "xlnx,axi-dma-7.1", "xlnx,axi-dma-1.00.a";
interrupt-names = "mm2s_introut";
interrupt-parent = <&intc>;
interrupts = <0 32 4>;
reg = <0x40410000 0x10000>;
xlnx,addrwidth = <0x20>;
xlnx,sg-length-width = <0xe>;
dma-channel@40410000 {
compatible = "xlnx,axi-dma-mm2s-channel";
dma-channels = <0x1>;
interrupts = <0 32 4>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x1>;
};
};
};
};
fragment@3 {
target = <&amba>;
overlay3: __overlay__ {
#address-cells = <1>;
#size-cells = <1>;
axidma_chrdev_0: axidma_chrdev@0 {
compatible = "xlnx,axidma-chrdev";
dmas = <&ZmodADC_0_axi_dma_0 0>;
dma-names = "rx_channel";
index = <0>;
};
axidma_chrdev_1: axidma_chrdev@1 {
compatible = "xlnx,axidma-chrdev";
dmas = <&ZmodDAC_0_axi_dma_1 0>;
dma-names = "tx_channel";
index = <1>;
};
};
};
};
Save changes and go to "PYNQ-PRIO/device_tree_overlays".
Run "Install.sh" script.
cd PYNQ-PRIO/device_tree_overlays
./install.sh
Now if you want to create overlay type:
make BOARDS=Eclypse-Z7
New overlay should be located in "Eclypse-Z7/prio_linux/dtbo" folder.
Now we also need a valid bitstream that was used to generate overlay. You can use the one located in petalinux zmod_adc_dac project.
We will also need.hwh file to access IP's from PYNQ api, it is located in "Eclypse-Z7/hw" folder.
Upload all the files to Eclypse-Z7. Remember that.bit and.hwh should have the same name.
You can create new Jupyter notebook, or use already existing.
Running example Zmod project will lead to segmentation fault. That's what it is still work in progress.
#Update
If it's not an issue, disable runtime reconfiguration and ZMOD's should work as always. I think ZMOD driver doesn't support reconfiguration yet.
Take care!
Comments