System-on-Chip (SoC) Design

EE382M.20, Fall 2018

Board Tutorial



       This is a tutorial related to the class project.

       Please use the discussion board on Piazza for Q&A.

       Please check relevant web pages. 


1       Overview 

The goal of this tutorial is to:

       Give an introduction to the Xilinx tools and the ZedBoard


This tutorial includes the following:

       Use of the ZedBoard to run and co-verify a multiply-and-accumulate (MAC) example.

       An application that reads inputs, calls the hardware function with these inputs (modifying a HAL implementation adapted from Lab 2), gets the output, and prints it.


We will use three Xilinx tools to prototype the application on the board:

       Vivado HLS is Xilinx’s high-level synthesis (HLS) tool that is used for C-to-RTL synthesis (you already used Vivado HLS in Lab 3).  

       Vivado Design Suite will be used for RTL-to-gate synthesis and FPGA bitstream generation.

       Vivado’s IP Integrator lets you create complex system designs by instantiating and interconnecting IP cores.


2       Resources and Documentation

Follow the Vivado IP Integrator training material given in the extra class session by Xilinx (Lab 4, also available at /home/projects/gerstl/ee382m/vivado on the LRC machines):

      For those lab exercises, you need to configure the board to boot from JTAG by setting the boot mode jumpers (JP11-JP7 corresponding to MIO[6:2]) all to '0'/GND.

      Make sure to reconfigure the board to boot Linux from QSPI Flash again (jumper JP10/MIO05 set to '1'/3.3V) once you are done.


Please refer to the following materials for additional information:

       The Xilinx Vivado HLS Tutorial (UG871) already provided in Lab 3

       Vivado Design Suite User Guide, Embedded Design (UG898)

       Vivado Design Suite Tutorial, Embedded Design (UG940)


3       Example

This example takes two integers as inputs, calculates their product, and gives the accumulated sum as the output. The multiplication and accumulation take place in the FPGA, which is called from the software.


(a)   Hardware part

1.     Download the hardware source code (hls_macc.tar.gz) into your directory and unzip the file:

      The example code, hls_macc.c/.h, is the C source code annotated with Xilinx-specific synthesis directives (pragmas), which are used to automatically infer a bus and register interface.

       The application code comes with a testbench (hls_macc_test.c).

2.     Launch Vivado HLS and synthesize the example code. The steps are equivalent to what you did in Lab 3, following the Vivado HLS Tutorial (UG871) and/or training materials:

a.     Create a new project by using the following information:

Project name: hls_macc (or whatever you want.)

Location: Wherever you want on the LRC machines (/misc/scratch)

Top Function: hls_macc

Design Files: hls_macc.c

TestBench Files: hls_macc_test.c

Solution Name: solution1 (or whatever you want)

Clock Period: 4

Part Selection: RTL tool: Auto, Specify: Boards->‘ZedBoard’ (‘zynq’)

b.    Run C Simulation

c.     Run C Synthesis

d.    Run C/RTL Cosimulation

3.     Click ‘Solution’ -> ‘Export RTL’ -> ‘OK’. This will make the generated RTL code usable in Vivado Design Suite as a custom hardware IP. If this step is successful, you will see a zip file appear under ‘solution1’ -> ‘impl’ -> ‘ip’ in the explorer pane.

4.     Exit Vivado HLS.

5.     Launch Vivado Design Suite on an LRC machine:

% module load xilinx/2017.4
% vivado

6.     Follow Steps 2 through 5 of Lab 1 (“Implement Vivado HLS IP on a Zynq Device”) in Chapter 10 (“Using HLS IP in a Zynq AP SoC Design”) of the Vivado HLS Tutorial (UG871) to integrate the HLS IP into an overall system design and generate the FPGA bitstream. Make sure to select a Zedboard setup wherever necessary:

       ‘Default Part’ when you create a new project (Step 2.4)

       ‘Presets’ when re-customizing IP (Step 4.3)

7.     From the Vivado menu, select ‘Export’->‘Export Hardware’, make sure the ‘Include Bitstream’ option is enabled, and click OK

8.     Then from the Vivado menu, select ‘Launch SDK’ and click OK. Click the ‘system.hdf’ to see your system memory map and relevant information.


(b)   Integrate hardware on the board

1.     We first need to integrate the synthesized hardware design into the device tree blob (DTB) for the Linux kernel. Open a new terminal on an LRC machine. Similar to what we did in Lab 2, create a new PetaLinux project:

% module load xilinx/2017.4
% source /usr/local/packages/xilinx_2017.4/petalinux/2017.4/settings.[c]sh /usr/local/packages/xilinx_2017.4/petalinux/2017.4
umask 022

petalinux-create -t project –n Project -s /home/projects/gerstl/ee382m/avnet-digilent-zedboard-v2017.4-final.bsp
% cd Project

2.     Configure the new project by pointing to the hardware description produced by the Xilinx SDK:

petalinux-config --get-hw-description=<vivado_dir>/<project_name>/<project_name>.sdk/system_wrapper_hw_platform_0/-p ./

Select Image Packaging Configuration->Root filesystem type->SD Card

Save the config and exit

3.     Now build the DTB:

petalinux-build -c device-tree

4.     The new DTB file is located under ./images/linux. You should see system.dtb and system_wrapper.bit there.

5.     Now we need to merge the new DTB with the original device tree for the Zedboard. The original DTB itself is located in the boot partition on the SD card. Mount the boot partition on the board as the root user and copy uImage-zynq-zed.dtb to the PetaLinux working directory on the LRC machines:

$ sudo mount /dev/mmcblk0p1 /boot

$ sudo scp /boot/uImage-zynq-zed.dtb <user>@<server><petalinux_dir>

6.     Then decompile the uImage-zynq-zed.dtb to an editable source file using the following command back on the LRC machines:

% cd images/linux

% dtc –I dtb –O dts –o zedboard.dts uImage-zynq-zed.dtb

7.     Similarly decompile the new system.dtb to an editable source file using the following command:

dtc –I dtb –O dts –o system.dts system.dtb

8.     Edit the zedboard.dts file. Remove the entire amba_pl { … }; section and replace it with the amba_pl { … }; section from the system.dts file.

9.     Compile the modified zedboard.dts into an updated DTB file use the following command:

% dtc –I dts –O dtb –o system.dtb zedboard.dts

10.  Now copy the updated DTB file and the FPGA bitstream to the board. First back up the current configuration on your SD card by making dated copies of the original DTB and bitstream files: 

% mv uImage-zynq-zed.dtb uImage-zynq-zed.dtb.`date +%m.%d.%y`

% mv system_wrapper.bit  system_wrapper.bit.`date +%m.%d.%y`

Then copy the new system.dtb and system_wrapper.bit on the LRC machines to the previously mounted boot partition of the SD card:

$ sudo scp <user>@<server><petalinux_dir>
  /images/linux/<project_name>.bit /boot/system_wrapper.bit

$ sudo scp <user>@<server><petalinux_dir>
  /images/linux/system.dtb /boot/uImage-zynq-zed.dtb

$ sudo umount /boot

11.  Reboot the board. This will program the FPGA with the bitstream on the SD card and load the new device tree into the Linux kernel.


(c)   Software part

1.     Download the source code for a software application example (board_app.tar.gz) that initializes the hardware IP, feeds two operands into the hardware, waits for the result, and reads/prints the output. Note that Vivado HLS will automatically synthesize a set of registers into the hardware that allow the software to configure and control its operation. This includes interrupt enable registers that the above application code needs to first initialize in order for the hardware to generate interrupts. To find the register map automatically defined by Vivado HLS, open the xhls_macc_hw.h file found under ‘<your HLS project> -> <your solution> -> impl -> drivers -> hls_macc_top_v1_0 -> src’ in the Vivado HLS explorer window.

2.     The application example includes an updated kernel module and device driver (fpga_drv.c) for the board. This driver was modified from Lab 2 to match the compatible name, properly acknowledge and clear interrupts, and access the correct memory-mapped addresses and interrupt status register in the synthesized hardware. Note that it is always a good idea to double-check the system memory and interrupt mapping and compatible naming, as the kernel and driver rely on this information to interface software and hardware. Once you rebooted your board, check the information of your hardware module in the kernel’s device tree:

% dtc -I fs /sys/firmware/devicetree/base

You should see an amba_pl { … }; section listing your hls_macc device information. An example output should be as follows:

hls_macc@43c00000 {

xlnx,s-axi-hls-macc-periph-bus-addr-width = <0x6>;

compatible = "xlnx,hls-macc-1.0";

xlnx,s-axi-hls-macc-periph-bus-data-width = <0x20>;

interrupt-parent = <0x4>;

interrupts = <0x0 0x1d 0x4>;

reg = <0x43c00000 0x10000>;



In the above example, the accelerator base address is located at 0x43c00000, the interrupt ID is 0x1D (29), and the compatible name is "xlnx,hls-macc-1.0". Make sure this information is aligned to what you have in your driver code. Make necessary changes in the driver code if necessary.

3.     Compile the application example and driver code either on the LRC machines as you have done for Lab 2 or directly on the board. If you cross-compiled the application on the LRC machines,  copy the compiled example binary and kernel module to the board:

$ scp <user>@<server><path>/example .

$ scp <user>@<server><path>/fpga_drv.ko .

4.     Run the commands to insert the device driver on the board:

$ sudo insmod fpga_drv.ko

You can look at the kernel logs including any messages generated by the driver using:

$ dmesg

5.     Finally, run the application example:

$ ./example <number1> <number2>

If everything works fine, you should see the following message in your terminal:

A is <number1>

B is <number2>

C += A*B is <number3>