Setting Up Your Pynq Z2 Dev Environment
While the Pynq Z2 development board is marketed as being a Python environment with FPGA hardware accelerators, it is a very capable FPGA development board. Due to the python+accelerator is the main marketing pitch, there isn’t a lot of documentation on getting things set up for “standard” Zynq development.
In this post, I will walk you through setting up your environment to be able to create your own .bit and .elf files. I am not covering how to create your own logic or how to connect peripherals in this tutorial.
Xilinx and Board File Installation
To program your Zynq board, you are going to need to install a few things!
From Xilinx, you are going to want to download and install Vivado and Vitis 2019.2. You can do this by going to the downloads page (https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools.html) and clicking “Xilinx Unified Installer 2019.2: Windows Self Extracting Web Installer”. You will need a Xilinx account to download it. Once that downloads, double click it to open it. Go through the prompts until it asks you what you want to install – choose “Vitis” (which will include all of the other tools you will need). When asked what options you want, leave the options in their defaults unless you need something extra.
Note that you should install the Xilinx tools only on a very capable computer. You will need a decent size hard drive (the install is 100GB by itself), a good chunk of RAM, and a fairly fast multicore processor. While you can definitely use a slower or older computer, synthesizing (what we call “compiling” for hardware) takes a very long time.
After you install the Xilinx tools, take a trip to TUL’s website (the manufacturer of the Pynq Z2 board) and download the board file. These contain information on what I/O map to where on your board. You will also want to download any and all documentation that you can in the event you want to come back to it or need to verify something. See here: http://www.tul.com.tw/ProductsPYNQ-Z2.html
After you download the board file, unzip it. Copy the contents to C:\Xilinx\Vivado\2019.2\data\boards\board_files
If Vivado was already running, restart it. Otherwise, go ahead and open it up.
Note that this process should be fairly similar to most dev boards. If you are doing something from scratch, make sure you pick the correct chipset that you are using rather than choose a board. If you are going this route, you will need to take a number of other steps like configuring the type of RAM you are using and such (which is outside the scope of this particular tutorial).
Create A New Project
Open Vivado and create a new project (File >> Project >> New). Click “Next” and then name your project and determine where you want to save it.
Select RTL projects. The other projects allow you to make things like sub modules which we will being doing later. “Click “Next”.
Select either Verilog of VHDL and click “Next”. I know VHDL and this is likely to be most of my sources, so I am going to pick VHDL. I’m not worried about adding sources at the moment as we are going to create those as we go.
Ignore the constraints for now. Click “Next”.
When asked to pick a part number, click “Board” and then find the Pynq Z2 board we installed earlier. Click “Next”.
Skim over the summary to make sure nothing looks odd. Click “Finish” when you are done.
Create A New Block Design
The block design is where you create the interconnections between the multiple individual parts of your project. With this, you can create custom logic that talks to the ARM processor on Zynq chip as well as to the outside world. This is also where you will configure your ARM chip and peripherals like clocks, ADCs, and more. Starting off from the first screen, click “Create Block Design” on the left hand side.
Give it a name like “primary” or something similar and click “OK”.
In the new block design, click the “+” symbol and search for “zynq”. Double click the “Zynq Processing System” to add it to your window.
When prompted, click “Run Block Automation” with the default settings. This tool is used to make sure that everything is connected correctly and usually does a good job making sure that intermediary blocks are correctly connected. As you get more experience with the Zynq platform, and Xilinx tools in general, you can do all of this by hand as needed. For our purposes, this is going to set up the DDR RAM connection and the fixed I/O channels. You will also need to manually connect M_AXI_GP0_ACLK to FCLK_CLK0 by clicking one of the pins and dragging a connection to the other (if we had added anything else to our design, this would have been done for us).
Save your project by pressing [CONTROL] + [S].
Validate your design by going to Tools >> Validate Design. When “Validation Successful.”popup appears, click “OK”.
Create the HDL Wrapper
The “HDL” wrapper is what we will end up using to export our project to Vitis IDE so that we can write code for it. If we had added other things to our block level diagram, such as hardware accelerators, BRAM, or peripherals like an I2C master, the system would create hardware addresses in the ARM code that map to the physical hardware we created. For now, we just want to get things set up so we can export things a bit easier down the road.
In the Block Design panel, click the “Sources” tab, open the Design Sources (if they aren’t already), and then right click your block diagram (I named mine “primary”). Select “Create HDL Wrapper…” to open a dialog and allow the Vivado to automatically handle things.
You can see the wrapper around our block design now.
Save your project.
Generate and Export Your Hardware
From here, we need to generate the hardware files we need to use to in Vitis and to program the FPGA. To do this, click “Create Bitstream”, go through the prompts with default settings and allow it to run. It should take a few minutes.
After generating the bitstream, we need to export the hardware for Vitis to read in. Go to File >> Export >> Export Hardware. Use the default settings with the “Include Bitstream” box checked.
Creating a Bootable Binary and Flashing Your Board
To create a binary file you can flash, right click the top level project and click “Build Project”. This can take a few minutes to complete the first time.
After this completes, the console should show “Build Finished”. Right click the upper level again and this time select “Create Boot Image”. Use the default settings and click “Create Image”.
Set the power jumper on your board to USB and the boot jumper to “JTAG” (top row, two right-most headers). Connect to your computer and turn on the power switch. In Vitis, right click the upper level project and select “Program Flash”. Change the project type to “Application” and set the Image File to “<project_directory>vitis_workspace\<project_name_system\Debug\sd_card\BOOT.BIN”. Press Program.
After the flash completes, turn the board off with the power switch and change the jumper to QSPI. When you turn on the board again with a terminal connected, you should see “Hello World”. (Hint: If you have the Arduino IDE installed, you can use its terminal to quickly connect to your board. Otherwise, use PuTTy or a similar terminal depending on your OS.)
From this point on, it is time to develop some Zynq applications! What is truly fantastic about this family of chips is that you have a fairly powerful, dual core ARM processor (known as the PS) on top of a significant amount of FPGA space (known as the PL). While the FPGA portion is relatively limited, you can do some pretty fantastic and powerful things with it. Everything from motion control, to graphics processing, to machine vision, to you name it.
Because you can run Linux on the chip, you can also take advantage of a number of Linux services like scheduling and networking on the ARM side to do some pretty nifty things as well without a lot of extra effort.
Moving forward, it is my plan to do a few more tutorials on how to incorporate FPGA logic and ARM code together to create a complete product. I will go over a few key features of the board that I’ve found to be helpful in past projects and a few tips I’ve learned along the way.
Support This And Other Projects
Find this content useful? Consider supporting this project and future ones by donating via PayPal.
The easiest way to do this is by using my dedicated link here: paypal.me/foxrobotics
Since I recognize “paypal.me” is probably unfamiliar to many folks, here is a link on PayPal’s website explaining what it is. In a nutshell, it’s just a convenient way to let folks send money via PayPal. Feel free to contact me with any questions 🙂
While this is a free website for you, it isn’t free for me. Web-hosting, licenses, and all of the materials I use to make each post get pretty expensive pretty quickly. With your help to cover these costs, I can focus on making great content instead of digging in my sofa for quarters 😉
I like your example because it really is minimal and can be created quickly. There seem to be a few steps skipped between Export Hardware and Build Project. It’s hard to find good Vitis examples, everything still references the SDK, so I figured I would list the steps I used to get the design to run here in case someone else needs them. I used 2020 tools, so there may have been slight differences due to that as well.
After creating bitstream and outputing hardware
Create new application project
This will require picking a workspace.
The project directory you created will work for a quick test.
Pick xsa file from exported project for platform
Give project a name
Accept defaults and pick Hello World application
Modify Hello World project to generate more output — see below.
Build Project as indicated
Create Boot Image as inidcated
Hello World Comments
Hello World is only written once in sample project. For applicaiton only project,
it printed before my com port was connected, so I addded while(1) to loop ouput so it
would show up on terminal.
This generated output, but it was not clear text. I assume there is some problem with the
uart getting too much input and not “blocking” the calling function. I added a volatile for
loop in the while loop, and the text was clear:
volatile int i;
for (i = 0; i < 1000000; i++) ;