Practical 1a - Getting Started

Introductory Information

In this course you will be using a Xilinx Zynq-7000 FPGA on a Digilent Zybo Z7-10 board. The Zynq is a system-on-chip made from a combination of a dual-core ARM processor, an area of FPGA programmable logic, and various supporting peripherals.

You will be creating software in C/C++ which runs on the ARM processors, and custom hardware accelerators which run in the programmable logic. Together, the Zynq can create complex systems for processing high volumes of data in a short amount of time. In these practicals we will learn how to do this stage by stage, starting with the ARM processors.

Remote Access

Hopefully the majority of you will be able to work on this module in the Hardware Labs. However, it will be possible to work remotely. We have written detailed instructions for installing the Xilinx tools on your own computer. They are free to use for anyone with the type of FPGA that we have. Be warned that they are huge and require at least ~60GB of free disk space (around 0.8 CoD Warzones).

If you find that you have to self isolate and cannot install the tools yourself, please let Ian know and we will set you up with a remotely accessible computer.

Vivado

Vivado is the main software suite for creating Xilinx FPGA designs. We will be using version 2020.2. Launch it by booting into Linux, opening a terminal, and running:

vivado

Creating a Project

Important

Never use spaces in the name or path of a Vivado project. This can cause issues later on.

  • Open Vivado and select Create Project.

  • Set a project name and location. 

  • Click Next and select "RTL Project" and "Do not specify sources at this time".

  • Click Next past adding Constraints as well.

  • It will then ask you which FPGA you are building the project for. Select "Boards", set vendor to digilentinc.com. and choose Zybo Z7-10.

  • If this board is not visible then it has not yet been installed. You can do this by clicking Install/Update Boards (this will take a long time) and then in the "XHub Store" selecting "Boards → Digilent Inc. → Single Part → Zybo Z7-10". Right click it and select Install. A green tick mark should appear. The board is now installed. Return to the New Project menu and select it.

  • Select Next, and Finish.

Installing the Zybo board files

Now stop and think. Did you select "boards", "digilentinc.com", "Zybo Z7-10"? From my experience, at least 4 of you selected something else by mistake so go back and correct it.

Creating a Block Design

We now have a project ready for the Zybo. To describe the hardware in the system we create a Block Design.

  • In the Flow Navigator window on the left-hand side of the main UI, select Create Block Design and click OK (using the default name is fine here, as you will generally have only one block design per Vivado project).

This creates a blank design, so we can now start adding "IP Cores". IP Cores are pre-made components that can be used like Lego blocks to build a system quickly.

Adding the Zynq Processing System

The first IP we are going to add and configure the ARM processors, the memory interface, and other fixed peripherals on the SoC (together known as the Processing System or PS).

  • Press one of the "Add IP" buttons, as shown in the screenshot below.

  • Select from the list "ZYNQ7 Processing System".

  • After a good long think, your block diagram should be updated with a "Zynq" block.

  • The block represents the (fixed) ARM cores on the FPGA. Everything else we are about to add goes into the reconfigurable logic of the FPGA.

Adding LEDs and buttons

We want to connect to the buttons and LEDs on the board, so they can be controlled from code running on the ARM.

  • Select "Board" from the centre top panel and you should see a list of the simple I/O on the board (see the screenshot below).

  • Double click "4 LEDs" and click OK. Vivado will add a GPIO IP core which connects to the LEDs for you.

  • Double click "4 Buttons" and click OK. Vivado will also connect our IP core to the buttons. 

What did that do? We just created an instance of the axi_gpio core, which is a chunk of reconfigurable logic that allows general purpose IO (wires!) to be controlled from the main system bus (called the AXI bus). It is a very simple bit of logic. It just listens for bus transactions from the CPU. When written to by the CPU it sets the output wires, and when read by the CPU it reports the state of the wires.

However, as you can see, while the GPIO core is in our design and is connected to the LED and Button inputs and outputs, it is not actually connected to the CPU (the Zynq block). We will address that now.

Vivado Designer Assistance

At this point, we have some IP blocks but they are not connected to each other. Fortunately, Vivado has noticed this and provides "Designer Assistance".

  • In the green prompt at the top of your design click "Run Connection Automation" and click OK.

Vivado will make some drastic looking changes to your design, but they are quite straightforward. It has done three things:

  1. Create and connect a "Processor System Reset". This is an IP core which handles reset signals. Resets can be quite complex in an embedded system because different parts of the system run at different clock speeds. This core ensures that a stable reset signal is sent to the entire design. The tools will largely automate this.

  2. Create an AXI Interconnect. AXI is a system bus that allows the ARM processors to talk to other parts of the system. In this case, the GPIO core that we created. Again, the tools should largely handle this automatically.

  3. Connect the Processing System and the IO core to the AXI interconnect. AXI is a Master/Slave bus. Masters can initiate transactions, slaves can only reply to them. The Processing System's general-purpose master AXI port (M_AXI_GP0) and the GPIO core's slave port (S_AXI) are connected together through the AXI Interconnect.

There should still be some Designer Assistance available because the Processing System block has some board-specific options which are not set up.

  • Click "Run Block Automation" and click OK.

This makes less-visible changes, but is essential as it attaches DDR and other fixed I/O connections in the processing system to output pins on the FPGA device, and sets up internal parameters such as clock speeds.

Vivado has probably left everything looking a bit of a mess, so click the "Regenerate Layout" button in the toolbar to the top of the block diagram (third from right) to tidy up the layout. You can also manually drag the blocks around to improve the layout if needed.

Your design should now look similar to this:

Create HDL Wrapper

We need to tell Vivado that our block design should be instantiated on the FPGA.

  • Select Sources in the centre top panel.

  • Right click your block design (design_1.bd), and click "Create HDL Wrapper".

  • On the next popup ensure "Let Vivado manage wrapper and auto-update" is selected and click OK.

A "critical warning" may appear with the following messages:

CRITICAL WARNING: [PSU-1] Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 has negative value -0.050 . PS DDR interfaces might fail when entering negative DQS skew values. 
CRITICAL WARNING: [PSU-2] Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 has negative value -0.044 . PS DDR interfaces might fail when entering negative DQS skew values.
CRITICAL WARNING: [PSU-3] Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 has negative value -0.035 . PS DDR interfaces might fail when entering negative DQS skew values.
CRITICAL WARNING: [PSU-4] Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 has negative value -0.100 . PS DDR interfaces might fail when entering negative DQS skew values.

This is a known issue and can be ignored. However, any other "critical" warnings are likely to indicate a problem with your design.

Generate Bitstream

We are now ready to build the design.

  • From the Flow Navigator on the left-hand side click Generate Bitstream.

Before the tools can create the bitstream (also called a bitfile) they will need to run synthesis and implementation on your design. It will prompt you about this if up-to-date results are not available, so click Yes to run the whole process.

This will take a long time; welcome to FPGA development! For now this will only be a couple of minutes, but things can take much longer once your design gets more complicated.

You can see progress by clicking the Design Runs tab at the bottom of the window. This will show you which stage is currently running and the elapsed time, but unfortunately not how long is remaining.

Synthesis and Implementation Reports

Once implementation is complete, select "Open Implemented Design" in the dialog box and click OK, or alternatively click "Open Implemented Design" in the Flow Navigator on the left.

In future you can just click Cancel to this dialog box (or select any other option), but for now let's inspect what the tools have done.

The Device view shows the layout of your design in terms of FPGA logic – the look-up tables (LUTs), multiplexors (MUXs), etc. that the device is made up of.
Click the "Routing Resources" button in the toolbar (second from top) to also show the signals that are routed through the FPGA.

From here, you can zoom in by dragging a box around components, and even inspect the truth tables of individual LUTs in the design. Don't change anything here, but have a look around to appreciate how the FPGA works.

Changing "Default Layout" in the top toolbar to "I/O Planning" allows you to see the physical pin assignments of the FPGA chip.

   

You can also view various reports (e.g. timing, power and utilisation) using the options in the flow navigator. This can be useful to see how much space your design is using on the FPGA fabric. Clicking "Report Utilization" brings up a summary of how your design fits on the device.

Whereas a software program only really consumes one resource, memory, FPGAs have many different resources. In the report you can see LUTs and Registers (a measure of logic space) and whether those LUTs are used to make logic or memory. If any of these numbers get larger than the number available on the device (shown in parentheses) then your design will not fit and the tools will error.

If you're curious, you can also explore a logical schematic of your design as generated by the synthesis process, by choosing "Open Synthesized Design" then "Schematic" in the Flow Navigator.

Program the FPGA

Once you've finished having a look at implementation and synthesis output, we can program the FPGA with the generated bitstream.

At this point you should grab an FPGA board and connect it via USB to your computer. In the Flow Navigator click "Open Hardware Manager", then "Open Target" and "Open New Target". You should see following description of the connected FPGA:

  • To program the FPGA you can right click its part number (xc7z010) and click Program Device, or use the green prompt at the top of the screen.

  • In the program dialog it should have selected the file design_1_wrapper.bit (assuming you kept the name design_1 for your block diagram).

  • Click Program.

The programming should complete successfully (note the green DONE light on the FPGA board), but nothing will happen because the ARM cores have no software to make them do anything. Let's write some!

Create a Software Project

To create software we need to use another application, Vitis IDE. Vitis' job is to build software for the varying dynamic hardware architectures that we make in Vivado.

In Vivado select File -> Export -> Export Hardware. Select Next, "Include bitstream", Next, and Finish.
Then select Tools -> Launch Vitis IDE and click OK. Welcome to Vitis, which is definitely not just a reskinned Eclipse...

Vitis has three types of project:

  • Platform

    • This is what we just "exported". It is a description of the design that you just built in Vivado, along with the bitfile that you just created. 

    • Contains low-level drivers to coordinate the boot up of CPUs, and descriptions of the IP cores in the system.

  • System

    • Generated by the tools. Contains drivers, kernels, and frameworks that your applications can use. You can think of this as the Operating System, and is required by application projects.

  • Application Project

    • Your code goes into application projects. These are built on top of a given System, for a given Platform.

You may be asked to create a Vitis workspace, give this a sensible name with no spaces.

  • Select File -> New -> Application Project.

  • Click Next, and select "Create a new platform from hardware (XSA)".

    • Click browse and browse to what we exported from Vivado. If you didn't change anything this will be in your Vivado project and called design_1_wrapper.xsa.

    • Click Next.

  • Now it wants an Application Name. Use "basicIO".

    • Note that it is also going to "Create New..." under System project because we currently have none of those either.

    • We can use the default System name as "basicIO_system"

    • Click Next

  • We are also going to let it create a new Domain. Click next.

  • Finally it asks us if we want to use a template project. Select "Hello World" and Finish.

After it is done, look at what it has made us:

"basicIO" is our program that we are going to write. It sits in "basicIO_system" which is our "Operating System". In this module we won't use a full OS but it could be a full as a full Linux kernel. For us however, the System is a collection of drivers for all the bits of the hardware that we created in Vivado. "design_1_wrapper" is a description of that Vivado hardware that we built.

Expand basicIO and the src folder inside it, and double-click helloworld.c to see the example code SDK has created for you.

C or C++?

For the purpose of EMBS you can use C or C++, the tools support both equally. However select C for this first application because C projects have a range of "hello world" templates to get you started, whereas C++ projects begin blank.

The EMBS documentation will be in C, but you may use C++ if you wish. Remember that C++ can, if misused, cause a very large runtime overhead. Also, we would recommend that you do not rely on the very newest features of the language in case support hasn't made it into the Xilinx tools yet. Whilst the compiler is standard GCC, the runtime is not standard glibc++ but a Xilinx-created embedded variant.

Test the Software Project

We need to first build the project. Before we run it though we need to connect a serial terminal so that we can see the output. Make sure that the FPGA is connected and turned on, start a Terminal and run:

screen /dev/ttyUSB2 115200

In this command, screen is a terminal application, and /dev/ttyUSB2 is the device that the FPGA appears as. Finally 115200 is the baud rate to communicate at. If this works, you will get a blank window. To exit screen press Ctrl-a, release, then k (or just turn off the FPGA).

To run an application you must first build it. Use the Assistant panel in the bottom left. In it you will see all the Applications that you have in your workspace. Select the System that you want to run then click the Hammer icon to build. After a while you will see in the Console panel something like:

12:42:41 Build Finished (took 3s.539ms)

If there are errors, look in the console to see what went wrong. Once built, use the green Run arrow and select "Launch Hardware". This will create a “run configuration” called something like SystemDebugger_app_name_system which you can select in later runs.

After a short time, "Hello World" should appear in the screen terminal that you opened earlier. Note, this is not the Console in Vitis.

Change the software to print a different string, rebuild, and relaunch. You should see the printed string change. Note that you have to remember to rebuild your software!




Once you have the hang of the tools, and have successfully programmed the FPGA and run some software you are done for our first session!







Contents