Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 4 - Modify & rebuild previous Firmware & Software projects then deploy on Zedboard

Introduction

This tutorial details the steps required to create Firmware with Vivado & Software with Vitis to allow access to the LEDs & Push Buttons on the Zedboard via a GPIO module.  The Firmware will be setup to generate interrupts from the Push Buttons and the software setup to handle these interrupts and control the LEDs. The tutorial covers generation of Programmable Logic (PL) & Processor System (PS) for the Zynq-7000 SoC using the previous project as a starting point. Deployment of the PL & PS images will be via JTAG and the resulting output provided via a UART upto the host PC.

Aims

The aims of this tutorial are as follows :-

    Part 1 - Project Setup

    1. Setup environment
    2. Create project area

    Part 2 - Firmware Development

    1. Recreate Vivado project & launch Vivado
    2. Open block design
    3. Re-customize ZYNQ IP
    4. Re-customize AXI GPIO IP
    5. Manually wire up connection
    6. Rename external ports
    7. Validate block design
    8. Modify pin constraints
    9. Generate bitstream
    10. Export Hardware Platform

    Part 3 - Software Development

    1. Recreate Vitis project & launch Vitis
    2. Edit source file
    3. Build platform
    4. Build project

    Part 4 - Hardware Deployment

    1. Setup Zedboard hardware
    2. Launch MiniCom terminal emulator
    3. Deploy firmware & software on Zedboard
    4. Check everything is working as expected
    5. Improvements

    Part 5 - Revision Control

    1. Commit to repository

    Part 6 - Quickstart

    1. Obtain tutorial files from Bitbucket, create & build projects, deploy on Zedboard

    1. Setup environment

    Setup Xilinx design environment for the 2021.2 toolset.
    steve@Desktop:~$ xilinx
    Xilinx tools available tools at /opt/Xilinx :-
    1) 2021.2 - Vivado - SDK - Vitis - PetaLinux
    0) Exit
    Please select tools required or exit : 1

    Tools are as follows :-
    vivado @ /opt/Xilinx/Vivado/2021.2/bin/vivado
    vitis @ /opt/Xilinx/Vitis/2021.2/bin/vitis
    petalinux-build @ /opt/Xilinx/PetaLinux/2021.2/tool/tools/common/petalinux/bin/petalinux-build

    2. Create project area

    This project is going to be a branch of the previous project as that project is a very good starting point for this one. Clone the previous project (zedboard_leds_switches) into a new project directory (zedboard_leds_buttons) and switch to the v1.0 tag. Change the present working directory (pwd) to be at the project root and rename the main software file to suit the new project name.
    steve@Desktop:~$ cd ~/projects
    steve@Desktop:~/projects$ git clone git@192.168.2.20:zedboard_leds_switches.git zedboard_leds_buttons
    steve@Desktop:~/projects$ cd zedboard_leds_buttons
    steve@Desktop:~/projects/zedboard_leds_buttons$ git checkout -b zedboard_leds_buttons v1.0
    steve@Desktop:~/projects/zedboard_leds_buttons$ git mv sw/src/c/zedboard_leds_switches.c sw/src/c/zedboard_leds_buttons.c

    3. Recreate Vivado project & launch Vivado

    To recreate the Vivado project from its sources execute the Create Vivado Project shell script.
    steve@Desktop:~/projects/zedboard_leds_buttons$ create_vivado_project.sh
    The Vivado GUI should now appear and be executing the create_vivado_project.tcl script to recreate the zedboard_leds_switches project inside the zedboard_leds_buttons directory. The Vivado cockpit window should show the newly created zedboard_leds_buttons project. Missing Image!

    4. Open block design

    Once Vivado completes execution of the Tcl script open the block design by clicking on Open Block Design under the IP INTEGRATOR heading inside the Flow Navigator section. Expand the Diagram by clicking on the Float Missing Image! icon in the Diagram pane inside the BLOCK DESIGN section.

    5. Re-customize ZYNQ IP

    Double click on the ZYNQ7 Processing System module to begin re-customization.

    An interrupt input is required for this design to allow triggered communication from the GPIO to the ZYNQ7 Processing System To enable the IRQ_F2P connection select Interrupts, expand Fabric Interrupts, expand PL-PS Interrupt Ports, tick Fabric Interrupts and then tick Fabric Interrupts. Missing Image! Click OK to commit the changes.

    6. Re-customize AXI GPIO IP

    Double click on the AXI GPIO module to begin re-customization.

    Connections to both the LED's and Push Buttons on the ZedBoard are required for this design. Change the GPIO Width parameter in the GPIO2 section from 8 to 5. Connection to the interrupt output ip2intc_irpt on the AXI GPIO is also required. To enable this tick Enable Interrupt. Missing Image! Click OK to commit the changes.

    7. Manually wire up connection

    To connect up the interrupt, left click (keeping the mouse button held down afterwards) on the ip2intc_irpt output of the AXI GPIO and drag the new connection across to the IRQ_F2P input of the ZYNQ7 Processing System, release the mouse button once the connection is established. Missing Image!

    8. Rename external ports

    Rename the external port that GPIO2 is connected to so it has a more meaningful name. Highlight switches and goto the Vivado cockpit window, in the External Interface Properties under BLOCK DESIGN change the Name field from switches to buttons. Missing Image! The block design should now reflect the changes. Missing Image!

    9. Validate block design

    Verify the block design is error free by clicking on the Validate Design Missing Image! icon. Once validated save the block design and return the floating Diagram pane back to Vivado by clicking on the Dock Missing Image! icon.

    10. Modify pin constraints

    Using the zedboard_master_XDC_RevC_D_v3.xdc from ~/projects/common/fw/src/constraint as a reference the constraints from the previous project can be adjusted to suit this project.

    Double click on the existing constraints zedboard.xdc within Sources » Constraints » constrs_1 to edit. Missing Image! Adjust zedboard.xdc and save the file.

    zedboard.xdc

    1. # User LEDs - Bank 33
    2. set_property PACKAGE_PIN T22 [get_ports {leds_tri_o[0]}];  # "LD0"
    3. set_property PACKAGE_PIN T21 [get_ports {leds_tri_o[1]}];  # "LD1"
    4. set_property PACKAGE_PIN U22 [get_ports {leds_tri_o[2]}];  # "LD2"
    5. set_property PACKAGE_PIN U21 [get_ports {leds_tri_o[3]}];  # "LD3"
    6. set_property PACKAGE_PIN V22 [get_ports {leds_tri_o[4]}];  # "LD4"
    7. set_property PACKAGE_PIN W22 [get_ports {leds_tri_o[5]}];  # "LD5"
    8. set_property PACKAGE_PIN U19 [get_ports {leds_tri_o[6]}];  # "LD6"
    9. set_property PACKAGE_PIN U14 [get_ports {leds_tri_o[7]}];  # "LD7"

    10. # User Push Buttons - Bank 34
    11. set_property PACKAGE_PIN P16 [get_ports {buttons_tri_i[0]}];  # "BTNC"
    12. set_property PACKAGE_PIN R16 [get_ports {buttons_tri_i[1]}];  # "BTND"
    13. set_property PACKAGE_PIN N15 [get_ports {buttons_tri_i[2]}];  # "BTNL"
    14. set_property PACKAGE_PIN R18 [get_ports {buttons_tri_i[3]}];  # "BTNR"
    15. set_property PACKAGE_PIN T18 [get_ports {buttons_tri_i[4]}];  # "BTNU"

    16. # Banks
    17. set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 33]];
    18. set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 34]];

    11. Generate bitstream

    Generate the programmable logic bitstream by clicking on Generate Bitstream under the PROGRAM AND DEBUG heading inside the Flow Navigator section.

    12. Export Hardware Platform

    Export the hardware platform by selecting File » Export » Export Hardware... from the main menu. Include the bitstream in the exported hardware file and save as ~/projects/zedboard_leds_buttons/fw/system_wrapper.xsa.

    13. Recreate Vitis project & launch Vitis

    To recreate the Vitis project from its sources execute the Create Vitis Project shell script.
    steve@Desktop:~/projects/zedboard_leds_buttons$ create_vitis_project.sh
    The Vitis GUI should now appear and be executing the create_vitis_project.tcl script to recreate the zedboard_leds_switches project inside the zedboard_leds_buttons directory.

    Close the Welcome tab by clicking on the X to the right of Welcome. Missing Image! The Vitis cockpit window should now show the newly created zedboard_leds_buttons project. Missing Image!

    14. Edit source file

    Edit the main source file by double clicking on zedboard_leds_buttons.c inside the Explorer tab under zedboard_leds_buttons_system » zedboard_leds_buttons » src. Missing Image! Remove all of the existing code from the previous project and replace it with something similar to the following.

    zedboard_leds_buttons.c

    1. //
    2. // File .......... zedboard_leds_buttons.c
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 15 January 2022
    6. // Description ...
    7. //   Very simple LEDs & Push Buttons interrupt example design.
    8. //


    9. // Includes
    10. #include "xparameters.h"
    11. #include "xgpio.h"
    12. #include "xscugic.h"
    13. #include "xil_exception.h"
    14. #include <stdio.h>


    15. // Defines
    16. #define CHANNEL_LEDS 1
    17. #define CHANNEL_BTNS 2

    18. #define BUTTON_CENTER 0x01
    19. #define BUTTON_DOWN   0x02
    20. #define BUTTON_LEFT   0x04
    21. #define BUTTON_RIGHT  0x08
    22. #define BUTTON_UP     0x10

    23. // Global Variables
    24. static XScuGic Intc;


    25. // Instances
    26. XGpio Gpio;


    27. // Rotate right
    28. u8 ror(u8 num) {
    29.   return (num >> 1) | (num << 7);
    30. }


    31. // Rotate left
    32. u8 rol(u8 num) {
    33.   return (num << 1) | (num >> 7);
    34. }


    35. // Interrupt service routine
    36. void GpioIsr(void *InstancePtr)
    37. {
    38.   u32 btns;
    39.   u32 leds;
    40.   u8 width = 0;

    41.   XGpio *GpioPtr = (XGpio *)InstancePtr;

    42.   // Disable button interrupts
    43.   XGpio_InterruptDisable(GpioPtr, XGPIO_IR_CH2_MASK);

    44.   // Only deal with button interrupts
    45.   if ((XGpio_InterruptGetStatus(GpioPtr) & XGPIO_IR_CH2_MASK) != XGPIO_IR_CH2_MASK) {
    46.     return;
    47.   }

    48.   // Obtain register values for LED's & buttons
    49.   leds = XGpio_DiscreteRead(&Gpio, CHANNEL_LEDS);
    50.   btns = XGpio_DiscreteRead(&Gpio, CHANNEL_BTNS);

    51.   // Print button status
    52.   xil_printf("ISR called, button register = 0x%x\n\r", btns);

    53.   // Determine LED bar width
    54.   for (u8 i=0; i<=7; i++) {
    55.     if ((0b00000001 << i) & leds) {
    56.       width++;
    57.     }
    58.   }

    59.   // Handle button functionality
    60.   switch (btns) {

    61.     // Set LED bar width to 4
    62.     case BUTTON_CENTER :
    63.       leds = 0b00111100;
    64.       break;

    65.     // Rotate LED's left
    66.     case BUTTON_LEFT :
    67.       if (width == 0) {
    68.         leds = 0b00000001;
    69.       } else {
    70.         leds = (u8)rol(leds);
    71.       }
    72.       break;

    73.     // Rotate LED's right
    74.     case BUTTON_RIGHT :
    75.       if (width == 0) {
    76.         leds = 0b10000000;
    77.       } else {
    78.         leds = (u8)ror(leds);
    79.       }
    80.       break;

    81.     // Increase illuminated LED's
    82.     case BUTTON_UP :
    83.       if (width == 0) {
    84.      leds = 0b00001000;
    85.       } else if (width & 1) {
    86.         leds = leds | rol(leds);
    87.       } else {
    88.         leds = leds | ror(leds);
    89.       }
    90.       break;

    91.     // Decrease illuminated LED's
    92.     case BUTTON_DOWN :
    93.       if (width == 8) {
    94.      leds = 0b01111111;
    95.       } else if (width & 1) {
    96.         leds = leds & rol(leds);
    97.       } else {
    98.         leds = leds & ror(leds);
    99.       }
    100.       break;

    101.     // Do nothing
    102.     default:
    103.       break;
    104.   }

    105.   // Update LED's
    106.   XGpio_DiscreteWrite(&Gpio, CHANNEL_LEDS, leds);

    107.   // Clear pending interrupts
    108.   XGpio_InterruptClear(GpioPtr, XGPIO_IR_CH2_MASK);

    109.   // Enable button interrupts
    110.   XGpio_InterruptEnable(GpioPtr, XGPIO_IR_CH2_MASK);
    111. }


    112. // Setup interrupt system
    113. int SetupInterruptSystem()
    114. {
    115.   int Result;
    116.   XScuGic *IntcInstancePtr = &Intc;

    117.   XScuGic_Config *IntcConfig;

    118.   // Initialise interrupt controller
    119.   IntcConfig = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
    120.   if (NULL == IntcConfig) {
    121.     return XST_FAILURE;
    122.   }

    123.   // Comment
    124.   Result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
    125.   if (Result != XST_SUCCESS) {
    126.     return XST_FAILURE;
    127.   }

    128.   // Comment
    129.   XScuGic_SetPriorityTriggerType(IntcInstancePtr, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR, 0xA0, 0x3);

    130.   // Connect interrupt handler
    131.   Result = XScuGic_Connect(IntcInstancePtr, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR, (Xil_ExceptionHandler)GpioIsr, &Gpio);
    132.   if (Result != XST_SUCCESS) {
    133.     return Result;
    134.   }

    135.   // Enable GPIO interrupt
    136.   XScuGic_Enable(IntcInstancePtr, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR);

    137.   // Enable button interrupts
    138.   XGpio_InterruptEnable(&Gpio, XGPIO_IR_CH2_MASK);

    139.   // Enable GPIO interrupts
    140.   XGpio_InterruptGlobalEnable(&Gpio);

    141.   // Initialise the exception table
    142.   Xil_ExceptionInit();

    143.   // Register interrupt controller handle with exception table
    144.   Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstancePtr);

    145.   // Enable non-critical exceptions
    146.   Xil_ExceptionEnable();

    147.   return XST_SUCCESS;
    148. }


    149. // Main function
    150. int main(void)
    151. {
    152.   int Status;

    153.   // Initialise GPIO driver
    154.   Status = XGpio_Initialize(&Gpio, XPAR_AXI_GPIO_0_DEVICE_ID);
    155.   if (Status != XST_SUCCESS) {
    156.     return XST_FAILURE;
    157.   }

    158.   // Clear LED's
    159.   XGpio_DiscreteWrite(&Gpio, CHANNEL_LEDS, 0);

    160.   // Setup interrupt system
    161.   Status = SetupInterruptSystem();
    162.   if (Status != XST_SUCCESS) {
    163.     return XST_FAILURE;
    164.   }

    165.   // Print header
    166.   print("--== LED & Push Buttons interrupt example ==--\n\n\r");
    167.   print("Use the buttons to control the LED's :-\n\n\r");
    168.   print("Centre - Reset LED's to 00111100\n\r");
    169.   print("Left   - Rotate LED's left\n\r");
    170.   print("Right  - Rotate LED's right\n\r");
    171.   print("Up     - Increase illuminated LED's\n\r");
    172.   print("Down   - Decrease illuminated LED's\n\n\r");
    173.   print("Press enter key to quit application\n\r");

    174.   // Wait for user input
    175.   getc(stdin);

    176.   // Clear LED's
    177.   XGpio_DiscreteWrite(&Gpio, CHANNEL_LEDS, 0);

    178.   // Print footer
    179.   print("All done!\n\n\r");

    180.   return XST_SUCCESS;
    181. }

    15. Build platform

    Build the platform by right clicking on system_wrapper inside the Explorer tab and selecting Build Project. Missing Image!

    16. Build project

    Build the project by right clicking on zedboard_leds_buttons under zedboard_leds_buttons_system inside the Explorer tab and selecting Build Project. Missing Image!

    17. Setup Zedboard hardware

    Connect up the hardware as follows :-
    1. Xubuntu PC USB ⇄ Zedboard USB JTAG/Debug
    2. Xubuntu PC USB ⇄ Zedboard USB UART
    Missing Image! Set the boot mode jumpers on the Zedboard for JTAG. Missing Image! Power on the Zedboard.

    18. Launch MiniCom terminal emulator

    If not already running, open up a new terminal and launch the MiniCom terminal emulator.
    steve@Desktop:~$ minized

    Welcome to minicom 2.7.1

    OPTIONS: I18n
    Compiled on Dec 23 2019, 02:06:26.
    Port /dev/ttyACM0, 06:34:25

    Press CTRL-A Z for help on special keys

    19. Deploy firmware & software on Zedboard

    To program the PL & PS part of the Zynq-7000 FPGA right click on zedboard_leds_buttons under zedboard_leds_buttons_system inside the Explorer tab and select Run As » Launch on Hardware (Single Application Debug) from the menu. The blue done LED will illuminate once the PL part of the FPGA has been programmed and after this the software will run on the PS part. Missing Image!

    20. Check everything is working as expected

    All being well the following sequence of events should be observed.
    --== LED & Push Buttons interrupt example ==--

    Use the buttons to control the LED's :-

    Centre - Reset LED's to 00111100
    Left   - Rotate LED's left
    Right  - Rotate LED's right
    Up     - Increase illuminated LED's
    Down   - Decrease illuminated LED's

    Press enter key to quit application

    21. Improvements

    This design can be improved by added debounce logic to the push button inputs as sometimes multiple interrupts are detected for a single button press due to switch bounce. Another improvement would be to make the GPIO2 register latch and hold logic 1's for each of the push buttons when they are pressed, these held values would then be cleared by software after an interrupt is detected and the GPIO2 register read. If there is a large delay between an interrupt being detected and the GPIO2 register being read the users finger will be off the push button so it will not be detected as pressed.

    22. Commit to repository

    Commit the updated files, create an annotated tag and push the commit & tag up to the remote repository.
    steve@Desktop:~/projects/zedboard_leds_buttons$ git commit -am "Changed GPIO2 connection from DIP Switches (8-bit) to Push Buttons (5-bit)."
    steve@Desktop:~/projects/zedboard_leds_buttons$ git push --set-upstream origin zedboard_leds_buttons
    steve@Desktop:~/projects/zedboard_leds_buttons$ git tag -a v2.0_int -m "ZYNQ, GPIO & Interrupts"
    steve@Desktop:~/projects/zedboard_leds_buttons$ git push origin v2.0_int

    23. Obtain tutorial files from Bitbucket, create & build projects, deploy on Zedboard

    The source files relating to this tutorial for both Firmware & Software can be obtained from Bitbucket.

    The instructions below assume that Part 1 - Installation of tools, setup of environment and creation of project area has been completed in full and that the environment has been setup as per 1. Setup environment. The root project area ~/projects should be present and contain the common project. The zedboard_leds_buttons project should NOT be present. Adjust the commands below to suit if the above differs.

    Obtain firmware & software source, create & build Vivado project, export hardware, then create & build Vitis project.
    steve@Desktop:~$ cd ~/projects
    steve@Desktop:~/projects$ git clone -b v1.0 https://bitbucket.org/spacewire_firmware/zedboard_leds_buttons
    steve@Desktop:~/projects$ cd zedboard_leds_buttons
    steve@Desktop:~/projects/zedboard_leds_buttons$ create_vivado_project.sh build
    steve@Desktop:~/projects/zedboard_leds_buttons$ create_vitis_project.sh build
    With the projects now created & built perform the following steps :-
    1. Setup Zedboard hardware
    2. Launch MiniCom terminal emulator
    3. Deploy firware & software on Zedboard
    4. Check everything is working as expected