Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 17 - Add GPIO Interrupts through PetaLinux using User Space I/O subsystem

Introduction

This tutorial details the steps required to activate the PetaLinux Userspace I/O Device Driver and create a Userspace Application to communicate with it. An Interrupt line will be included to show how interrupts are dealt with inside PetaLinux. The end result isn't too dissimilar to the one from Part 4 but instead of using Bare Metal a more flexible PetaLinux approach is adapted. The Xilinx AXI GPIO block will be repaced with a new module to allow for more than two channels and address the issues pointed out in Part 4. Also the Auto Generated AXI Register Bank IP will be replaced with a more flexible RTL module.

Aims

The aims of this tutorial are as follows :-

    Part 1 - Project Setup

    1. Setup environment
    2. Obtain tutorial files from Bitbucket (optional)

    Part 2 - Firmware Development

    1. Change present working directory
    2. Bump Version
    3. Improve GIT Ignore
    4. Create a new AXI4-Lite GPIO module (HDL)
    5. Create a new AXI4-Lite Register Bank module (HDL)
    6. Launch Vivado
    7. Open block design
    8. Re-customize ZYNQ IP
    9. Remove old AXI GPIO component
    10. Add new AXI4 GPIO component
    11. Remove old AXI Register Bank component
    12. Add new AXI4 Register Bank component
    13. Edit address map
    14. Validate block design
    15. Modify HDL Wrapper
    16. Modify pin constraints
    17. Revision control
    18. Generate bitstream
    19. Export Hardware Platform

    Part 3 - OS Development

    1. Change present working directory
    2. Bump Version
    3. Update LED Runner application
    4. Add Hardware Platform
    5. Configure the Kernel
    6. Examine & add to the device tree
    7. Build & package PetaLinux

    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 (general)
    5. Check everything is working as expected (command-line)
    6. Check everything is working as expected (GUI)

    Part 5 - Software Development

    1. Create C application
    2. Edit C application
    3. Cross compile application
    4. Copy application to PetaLinux
    5. Check application is working as expected
    6. Revision control
    7. Final checks
    #### Part 1 - Project Setup ####

    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. Obtain tutorial files from Bitbucket (optional)

    Starting from this point, having not followed the previous tutorials, can be achieved by obtaining the required files from BitBucket. The only prerequisite is that Part 1 - Installation of tools, setup of environment and creation of project area as been completed.

    Part A - Obtain Firmware source.
    steve@Desktop:~$ cd ~/projects
    steve@Desktop:~/projects$ git clone -b v4.0 https://bitbucket.org/spacewire_firmware/zedboard_leds_switches
    steve@Desktop:~/projects$ cd zedboard_leds_switches
    steve@Desktop:~/projects/zedboard_leds_switches$ create_vivado_project.sh
    
    Part B - Obtain OS source.
    steve@Desktop:~$ cd ~/projects
    steve@Desktop:~/projects$ git clone -b v10.0 https://bitbucket.org/spacewire_firmware/zedboard_linux
    
    #### Part 2 - Firmware Development ####

    3. Change present working directory

    Change the present working directory to be the project directory.
    steve@Desktop:~$ cd ~/projects/zedboard_leds_switches
    

    4. Bump Version

    Change the version (or revision) number for this new development, this prevents ghost (post-release, same version) builds from appearing.
    steve@Desktop:~/projects/zedboard_leds_switches$ sed -i 's/4.0/5.0/g' fw/project.txt
    

    project.txt

    Zedboard LEDs & Switches Example Design
    SpaceWire UK
    Steve Haywood
    5.0
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/fw/project.txt -O fw/project.txt
    

    5. Improve GIT Ignore

    Improve the GIT Ignore mechanism to make it a little less brutal (work in progress!).
    steve@Desktop:~/projects/zedboard_leds_switches$ subl .gitignore
    

    .gitignore

    *.jou
    *.log
    *.str
    *.xsa
    /fw/vivado
    # Still too brutal...
    /fw/src/diagram
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/.gitignore -O .gitignore
    

    6. Create a new AXI4-Lite GPIO module (HDL)

    Due to the issues with the AXI GPIO discovered in Part 4, coupled with the fact it only has two channels and three would be desirable; LEDs, Switches & Buttons, the time has come to roll our own. Using the AXI Identification module has a starting point along the AXI GPIO v2.0 Product Guide (PG144) create a new AXI GPIO module that offers the following basic features :-
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/design/axi_gpio_zed.v
    

    axi_gpio_zed.v

    //
    // File .......... axi_gpio_zed.v
    // Author ........ Steve Haywood
    // Version ....... 1.0
    // Date .......... 29 October 2022
    // Standard ...... IEEE 1364-2001
    // Description ...
    //   AXI4-Lite controlled GPIO block written in basic Verilog. Primary purpose
    // is to provide access to the LEDs, Switches & Push Buttons of the Zedboard.
    // Design offers an Interrupt that can be thrown when any of the Push Buttons
    // are pressed or released. Address map is compatible with the one used in the
    // AXI GPIO IP from Xilinx, albeit a cut-down version.
    //
    
    
    `timescale 1 ns / 1 ps
    
    
    module axi_gpio_zed #
    (
      // Parameters (AXI4-Lite)
      localparam c_addr_w = 9,                                // P:Address Bus Width (bits)
      localparam c_data_w = 32,                               // P:Data Bus Width (bits)
      // GPIO Channels
      parameter  c_leds_w     = 8,                            // P:LEDs Width (bits)
      parameter  c_switches_w = 8,                            // P:Switches Width (bits)
      parameter  c_buttons_w  = 5                             // P:Buttons Width (bits)
    )
    (
      // Clock & Reset
      input  wire                             aclk,           // I:Clock
      input  wire                             aresetn,        // I:Reset
      // GPIO Channels
      output wire [c_leds_w-1 : 0]            leds,           // O:LEDs
      input  wire [c_switches_w-1 : 0]        switches,       // I:Switches
      input  wire [c_buttons_w-1 : 0]         buttons,        // I:Buttons
      // Interrupt
    
      output wire                             interrupt,      // O:Output
      // AXI4-Lite - Write Address Channel
      input  wire [c_addr_w-1 : 0]            s_axi_awaddr,   // I:Write Address
      input  wire [2 : 0]                     s_axi_awprot,   // I:Write Protection Type
      input  wire                             s_axi_awvalid,  // I:Write Address Valid
      output reg                              s_axi_awready,  // O:Write Address Ready
      // AXI4-Lite - Write Data Channel
      input  wire [c_data_w-1 : 0]            s_axi_wdata,    // I:Write Data
      input  wire [(c_data_w/8)-1 : 0]        s_axi_wstrb,    // I:Write Strobes
      input  wire                             s_axi_wvalid,   // I:Write Valid
      output reg                              s_axi_wready,   // O:Write Ready
      // AXI4-Lite - Write Response Channel
      output reg  [1 : 0]                     s_axi_bresp,    // O:Write Response
      output reg                              s_axi_bvalid,   // O:Write Response Valid
      input  wire                             s_axi_bready,   // I:Write Response Ready
      // AXI4-Lite - Read Address Channel
      input  wire [c_addr_w-1 : 0]            s_axi_araddr,   // I:Read Address
      input  wire [2 : 0]                     s_axi_arprot,   // I:Read Protection Type
      input  wire                             s_axi_arvalid,  // I:Read Address Valid
      output reg                              s_axi_arready,  // O:Read Address Ready
      // AXI4-Lite - Read Data Channel
      output reg  [c_data_w-1 : 0]            s_axi_rdata,    // O:Read Data
      output reg  [1 : 0]                     s_axi_rresp,    // O:Read Response
      output reg                              s_axi_rvalid,   // O:Read Valid
      input  wire                             s_axi_rready    // I:Read Ready
    );
    
    
      // Constants
      localparam c_bytes = c_data_w / 8;                         // Number of bytes in data bus
      localparam c_registers = 2**(c_addr_w - $clog2(c_bytes));  // Number of registers
      localparam c_channels = 3;                                 // Number of channels
    
      // Channel positions
      localparam c_chan_leds     = 0;  // LEDs
      localparam c_chan_switches = 1;  // Switches
      localparam c_chan_buttons  = 2;  // Buttons
    
      // Register positions
      localparam c_reg_leds      = 'h000 / c_bytes;  // LEDs
      localparam c_reg_switches  = 'h008 / c_bytes;  // Switches
      localparam c_reg_buttons   = 'h010 / c_bytes;  // Buttons
      localparam c_reg_isr       = 'h120 / c_bytes;  // Interrupt Status
      localparam c_reg_ier       = 'h128 / c_bytes;  // Interrupt Enable
      localparam c_reg_gie       = 'h11C / c_bytes;  // Global Interrupt Enable
    
      // Bit positions
      localparam c_gie_gintr_enable = 31; // Global Interrupt Enable
    
      // Signals
      reg aw_en;
      reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_awaddr;
      reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_araddr;
      reg [c_data_w-1 : 0] registers [c_registers - 1 : 0];
    
      wire [c_buttons_w+c_switches_w-1 : 0] inp;   // Debouncer Inputs
      wire [c_buttons_w+c_switches_w-1 : 0] outp;  // Debouncer Outputs
    
      wire [c_switches_w-1 : 0] switches_int;  // Debounced Switches
      wire [c_buttons_w-1  : 0] buttons_int;   // Debounced Buttons
      reg  [c_buttons_w-1  : 0] buttons_reg;   // Registered Buttons
    
      reg  [c_chan_buttons : c_chan_buttons] isr;  // Interrupt Status
    
    
      // Write Address Ready
      always @(posedge aclk)
        if (!aresetn) begin
          s_axi_awready <= 1'b0;
          aw_en <= 1'b1;
        end else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en) begin
          s_axi_awready <= 1'b1;
          aw_en <= 1'b0;
        end else if (s_axi_bvalid && s_axi_bready) begin
          s_axi_awready <= 1'b0;
          aw_en <= 1'b1;
        end else
          s_axi_awready <= 1'b0;
    
    
      // Write Address Latch
      always @(posedge aclk)
        if (!aresetn)
          axi_awaddr <= {c_addr_w{1'b0}};
        else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en)
          axi_awaddr <= s_axi_awaddr[c_addr_w - 1 : $clog2(c_bytes)];
    
    
      // Write Data Ready
      always @(posedge aclk)
        if (!aresetn)
          s_axi_wready <= 1'b0;
        else if (s_axi_awvalid && s_axi_wvalid && !s_axi_wready && aw_en)
          s_axi_wready <= 1'b1;
        else
          s_axi_wready <= 1'b0;
    
    
      // Write Data
      integer i;
      always @(posedge aclk)
        if (!aresetn)
          for (i = 0; i <= c_registers - 1; i = i + 1)
            registers[i] <= {c_data_w{1'b0}};
        else begin
          // Latch & hold Push Buttons
          for (i = 0; i < c_buttons_w; i = i + 1)
            if (buttons_int[i])
              registers[c_reg_buttons][i] <= 1'b1;
          // Special handling for toggle-on-write
          registers[c_reg_isr] <= {c_data_w{1'b0}};
          if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready)
            for (i = 0; i <= c_bytes - 1; i = i + 1)
              if (s_axi_wstrb[i])
                registers[axi_awaddr][i*8 +: 8] <= s_axi_wdata[i*8 +: 8]; // Byte-wise write
        end
    
    
      // Write Response
      always @(posedge aclk)
        if (!aresetn) begin
          s_axi_bvalid <= 1'b0;
          s_axi_bresp  <= 2'b00;
        end else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready && !s_axi_bvalid) begin
          s_axi_bvalid <= 1'b1;
          s_axi_bresp  <= 2'b00;
        end else if (s_axi_bvalid && s_axi_bready)
          s_axi_bvalid <= 1'b0;
    
    
      // Read Address Ready
      always @(posedge aclk)
        if (!aresetn)
          s_axi_arready <= 1'b0;
        else if (s_axi_arvalid && !s_axi_arready)
          s_axi_arready <= 1'b1;
        else
          s_axi_arready <= 1'b0;
    
    
      // Read Address Latch
      always @(posedge aclk)
        if (!aresetn)
          axi_araddr  <= {c_addr_w{1'b0}};
        else if (s_axi_arvalid && !s_axi_arready)
          axi_araddr <= s_axi_araddr[c_addr_w - 1 : $clog2(c_bytes)];
    
    
      // Read Response
      always @(posedge aclk)
        if (!aresetn) begin
          s_axi_rvalid <= 1'b0;
          s_axi_rresp  <= 2'b00;
        end else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
          s_axi_rvalid <= 1'b1;
          s_axi_rresp  <= 2'b00;
        end else if (s_axi_rvalid && s_axi_rready)
          s_axi_rvalid <= 1'b0;
    
    
      // Read Data
      always @(posedge aclk)
        if (!aresetn)
          s_axi_rdata <= {c_data_w{1'b0}};
        else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
          // Default
          s_axi_rdata <= {c_data_w{1'b0}};
          // Select
          case (axi_araddr)
            c_reg_leds     : s_axi_rdata[c_leds_w-1     : 0             ] <= registers[c_reg_leds][c_leds_w-1:0];
            c_reg_switches : s_axi_rdata[c_switches_w-1 : 0             ] <= switches_int;
            c_reg_buttons  : s_axi_rdata[c_buttons_w-1  : 0             ] <= registers[c_reg_buttons][c_buttons_w-1:0];
            c_reg_isr      : s_axi_rdata[c_chan_buttons : c_chan_buttons] <= isr;
            c_reg_ier      : s_axi_rdata[c_chan_buttons : c_chan_buttons] <= registers[c_reg_ier][c_chan_buttons : c_chan_buttons];
            c_reg_gie      : s_axi_rdata[c_gie_gintr_enable             ] <= registers[c_reg_gie][c_gie_gintr_enable];
            default        : s_axi_rdata                                  <= {c_data_w{1'b0}};
          endcase
        end
    
    
      // Debouncer Connections
      assign inp = {buttons, switches};
      assign {buttons_int, switches_int} = outp;
    
    
      // Debouncer
      genvar j;
      generate for (j = 0; j < c_buttons_w + c_switches_w; j = j + 1)
        debounce #
         (
          // Parameters
          .c_cycles ( 65536   )   // Debounce Cycles
         )
        debounce
         (
          // Clock & Reset
          .aclk     ( aclk    ),  // Clock
          .aresetn  ( aresetn ),  // Reset
          // Input & Output
          .inp      ( inp[j]  ),  // Input (Dirty)
          .outp     ( outp[j] )   // Output (Clean)
         );
      endgenerate
    
    
      // Register Buttons for Edge Detection
      always @(posedge aclk)
        if (!aresetn)
          buttons_reg <= {c_buttons_w{1'b0}};
        else
          buttons_reg <= buttons_int;
    
    
      // Throw an Interrupt when a button is pressed or released
      always @(posedge aclk)
        if (!aresetn)
          isr[c_chan_buttons] <= 1'b0;
        else if (registers[c_reg_gie][c_gie_gintr_enable] && registers[c_reg_ier][c_chan_buttons]) begin
    //    if (|(~buttons_reg & buttons_int)) // Button(s) pressed
          if (|(buttons_reg & ~buttons_int)) // Button(s) released
    //    if (buttons_reg != buttons_int) // Button(s) changed state
            isr[c_chan_buttons] <= 1'b1;
          else if (registers[c_reg_isr][c_chan_buttons])
            isr[c_chan_buttons] <= !isr[c_chan_buttons]; // Can be set & cleared when Interrupt is enabled
        end else if (registers[c_reg_isr][c_chan_buttons])
          isr[c_chan_buttons] <= 1'b0; // Can only be cleared when Interrupt is disabled
    
    
      // Interrupt output
      assign interrupt = |isr;
    
    
      // LEDs output
      assign leds = registers[c_reg_leds][c_leds_w-1:0];
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/fw/src/design/axi_gpio_zed.v -O fw/src/design/axi_gpio_zed.v
    
    Create a simple debounce module to accompany the above design.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/design/debounce.v
    

    debounce.v

    //
    // File .......... debounce.v
    // Author ........ Steve Haywood
    // Version ....... 1.0
    // Date .......... 29 October 2022
    // Standard ...... IEEE 1364-2001
    // Description ...
    //   Simple switch debouncer.
    //
    
    
    `timescale 1 ns / 1 ps
    
    
    module debounce #
    (
      // Parameters
      parameter  c_cycles = 65536  // Debounce Cycles
    )
    (
      // Clock & Reset
      input      aclk,             // Clock
      input      aresetn,          // Reset
      // Input & Output
      input      inp,              // Input (Dirty)
      output reg outp              // Output (Clean)
    );
    
    
      // Signals
      reg inp_meta;
      reg inp_int;
      reg [$clog2(c_cycles)-1:0] count;
    
    
      // Metastability hardener
      always @(posedge aclk)
        if (!aresetn) begin
          inp_meta <= 1'b0;
          inp_int  <= 1'b0;
        end else begin
          inp_meta <= inp;
          inp_int  <= inp_meta;
         end
    
    
      // Debouncer
      always @(posedge aclk)
        if (!aresetn) begin
          outp  <= 1'b0;
          count <= 32'b0;
        end else if (outp == inp_int) begin
          count <= 32'b0;
        end else if (count == c_cycles - 1) begin
          outp  <= inp;
          count <= 32'b0;
        end else
          count <= count + 1'b1;
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/fw/src/design/debounce.v -O fw/src/design/debounce.v
    

    7. Create a new AXI4-Lite Register Bank module (HDL)

    Create a simple but flexible AXI4-Lite register bank module to replace the rather limited and cumbersome auto-generated one.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/design/axi_register_bank.v
    

    axi_register_bank.v

    //
    // File .......... axi_register_bank.v
    // Author ........ Steve Haywood
    // Version ....... 1.0
    // Date .......... 29 October 2022
    // Standard ...... IEEE 1364-2001
    // Description ...
    //   AXI4-Lite controlled general purpose register bank written in basic
    // Verilog. This module is intended to be a building block for something more
    // useful.
    //
    // With c_data_w = 32 the number of registers is as follows :-
    //
    // c_addr_w - c_registers
    // ~~~~~~~~ - ~~~~~~~~~~~
    // 2        -   1
    // 3        -   2
    // 4        -   4
    // 5        -   8
    // 6        -  16
    // 7        -  32
    // 8        -  64
    // 9        - 128
    // 10       - 256
    //
    
    
    `timescale 1 ns / 1 ps
    
    
    module axi_register_bank #
    (
      // Parameters
      parameter  c_addr_w = 4,                                // P:Address Bus Width (bits)
      localparam c_data_w = 32                                // P:Data Bus Width (bits)
    )
    (
      // Clock & Reset
      input  wire                             aclk,           // I:Clock
      input  wire                             aresetn,        // I:Reset
      // AXI4-Lite - Write Address Channel
      input  wire [c_addr_w-1 : 0]            s_axi_awaddr,   // I:Write Address
      input  wire [2 : 0]                     s_axi_awprot,   // I:Write Protection Type
      input  wire                             s_axi_awvalid,  // I:Write Address Valid
      output reg                              s_axi_awready,  // O:Write Address Ready
      // AXI4-Lite - Write Data Channel
      input  wire [c_data_w-1 : 0]            s_axi_wdata,    // I:Write Data
      input  wire [(c_data_w/8)-1 : 0]        s_axi_wstrb,    // I:Write Strobes
      input  wire                             s_axi_wvalid,   // I:Write Valid
      output reg                              s_axi_wready,   // O:Write Ready
      // AXI4-Lite - Write Response Channel
      output reg  [1 : 0]                     s_axi_bresp,    // O:Write Response
      output reg                              s_axi_bvalid,   // O:Write Response Valid
      input  wire                             s_axi_bready,   // I:Write Response Ready
      // AXI4-Lite - Read Address Channel
      input  wire [c_addr_w-1 : 0]            s_axi_araddr,   // I:Read Address
      input  wire [2 : 0]                     s_axi_arprot,   // I:Read Protection Type
      input  wire                             s_axi_arvalid,  // I:Read Address Valid
      output reg                              s_axi_arready,  // O:Read Address Ready
      // AXI4-Lite - Read Data Channel
      output reg  [c_data_w-1 : 0]            s_axi_rdata,    // O:Read Data
      output reg  [1 : 0]                     s_axi_rresp,    // O:Read Response
      output reg                              s_axi_rvalid,   // O:Read Valid
      input  wire                             s_axi_rready    // I:Read Ready
    );
    
    
      // Constants
      localparam c_bytes = c_data_w / 8;                         // Number of bytes in data bus
      localparam c_registers = 2**(c_addr_w - $clog2(c_bytes));  // Number of registers
    
      // Signals
      reg aw_en;
      reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_awaddr;
      reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_araddr;
      reg [c_data_w-1 : 0] registers [c_registers - 1 : 0];
    
    
      // Write Address Ready
      always @(posedge aclk)
        if (!aresetn) begin
          s_axi_awready <= 1'b0;
          aw_en <= 1'b1;
        end else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en) begin
          s_axi_awready <= 1'b1;
          aw_en <= 1'b0;
        end else if (s_axi_bvalid && s_axi_bready) begin
          s_axi_awready <= 1'b0;
          aw_en <= 1'b1;
        end else
          s_axi_awready <= 1'b0;
    
    
      // Write Address Latch
      always @(posedge aclk)
        if (!aresetn)
          axi_awaddr <= {c_addr_w{1'b0}};
        else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en)
          axi_awaddr <= s_axi_awaddr[c_addr_w - 1 : $clog2(c_bytes)];
    
    
      // Write Data Ready
      always @(posedge aclk)
        if (!aresetn)
          s_axi_wready <= 1'b0;
        else if (s_axi_awvalid && s_axi_wvalid && !s_axi_wready && aw_en)
          s_axi_wready <= 1'b1;
        else
          s_axi_wready <= 1'b0;
    
    
      // Write Data
      integer i;
      always @(posedge aclk)
        if (!aresetn)
          for (i = 0; i <= c_registers - 1; i = i + 1)
            registers[i] <= {c_data_w{1'b0}};
        else begin
          if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready)
            for (i = 0; i <= c_bytes - 1; i = i + 1)
              if (s_axi_wstrb[i])
                registers[axi_awaddr][i*8 +: 8] <= s_axi_wdata[i*8 +: 8]; // Byte-wise write
        end
    
    
      // Write Response
      always @(posedge aclk)
        if (!aresetn) begin
          s_axi_bvalid <= 1'b0;
          s_axi_bresp  <= 2'b00;
        end else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready && !s_axi_bvalid) begin
          s_axi_bvalid <= 1'b1;
          s_axi_bresp  <= 2'b00;
        end else if (s_axi_bvalid && s_axi_bready)
          s_axi_bvalid <= 1'b0;
    
    
      // Read Address Ready
      always @(posedge aclk)
        if (!aresetn)
          s_axi_arready <= 1'b0;
        else if (s_axi_arvalid && !s_axi_arready)
          s_axi_arready <= 1'b1;
        else
          s_axi_arready <= 1'b0;
    
    
      // Read Address Latch
      always @(posedge aclk)
        if (!aresetn)
          axi_araddr  <= {c_addr_w{1'b0}};
        else if (s_axi_arvalid && !s_axi_arready)
          axi_araddr <= s_axi_araddr[c_addr_w - 1 : $clog2(c_bytes)];
    
    
      // Read Response
      always @(posedge aclk)
        if (!aresetn) begin
          s_axi_rvalid <= 1'b0;
          s_axi_rresp  <= 2'b00;
        end else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
          s_axi_rvalid <= 1'b1;
          s_axi_rresp  <= 2'b00;
        end else if (s_axi_rvalid && s_axi_rready)
          s_axi_rvalid <= 1'b0;
    
    
      // Read Data
      always @(posedge aclk)
        if (!aresetn)
          s_axi_rdata <= {c_data_w{1'b0}};
        else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid)
          s_axi_rdata <= registers[axi_araddr];
    
    
    endmodule 
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/fw/src/design/axi_register_bank.v -O fw/src/design/axi_register_bank.v
    

    8. Launch Vivado

    Launch Vivado quietly from a Terminal.
    steve@Desktop:~/projects/zedboard_leds_switches$ vivado -nojournal -nolog -notrace fw/vivado/project.xpr &
    

    9. Open block design

    From the Vivado cockpit window 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. Missing Image!

    10. 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 IRQ_F2P[15:0]. Missing Image! Click OK to commit the changes.

    11. Remove old AXI GPIO component

    Remove the axi_gpio_0 (AXI GPIO) component by selecting it and pressing the Delete key. Remove the dangling leds & switches external ports by selecting them and pressing the Delete key.

    12. Add new AXI4 GPIO component

    Add the new GPIO component and its accompanying Debouncer to the Vivado project by entering the following commands in the Tcl Console.
    add_files fw/src/design/axi_gpio_zed.v
    add_files fw/src/design/debounce.v
    Add the new GPIO component to the Block Design by dragging it from Sources and dropping it in Diagram.

    Connect axi_gpio_zed_0 to the rest of the system by clicking on Run Connection Automation.

    Ensure All Automation is checked in the Run Connection Automation dialog and click OK to perform the changes.

    Make the switches, buttons & leds ports on axi_gpio_zed_0 external by highlighting them and selecting Make External from the right mouse context menu.

    Remove the _0 prefix from the three external ports by renaming them.

    Connect the output Interrupt port interrupt on axi_gpio_zed_0 to the input Interrupt port IRQ_F2P on processing_system7_0.

    All being well the Block Design should now look similar to the one below. Missing Image!

    13. Remove old AXI Register Bank component

    Remove the register_bank_0 (AXI Register Bank) component by selecting it and pressing the Delete key.

    14. Add new AXI4 Register Bank component

    Add the new Register Bank component and to the Vivado project by entering the following commands in the Tcl Console.
    add_files fw/src/design/axi_register_bank.v
    Add the new Register Bank component to the Block Design by dragging it from Sources and dropping it in Diagram.

    Connect axi_register_bank_0 to the rest of the system by clicking on Run Connection Automation.

    All being well the Block Design should now look similar to the one below. Missing Image!

    15. Edit address map

    Check the address map assignment inside the Address Editor under the BLOCK DESIGN section.

    For ease of use butt axi_gpio_0 up against axi_identification_0 inside the next 64K block. To do this change the Master Base Address to 0x4001_0000 and ensure the Range is set to 64K, if not change it.

    Also butt axi_register_bank_0 up against axi_gpio_0 inside the next 64K block.  To do this change the Master Base Address to 0x4002_0000 and ensure the Range is set to 64K, if not change it. Missing Image!

    16. 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.

    17. Modify HDL Wrapper

    Edit the HDL Wrapper to add the new Buttons port and take care of the name changes on the LEDs & Switches ports.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/diagram/system/hdl/system_wrapper.sv
    

    system_wrapper.sv

    //
    // File .......... system_wrapper.sv
    // Author ........ Steve Haywood
    // Version ....... 1.2
    // Date .......... 29 October 2022
    // Standard ...... IEEE 1800-2017
    // Description ...
    //   Top level wrapper for the underlying block design.
    //
    
    
    timeunit      1ns;
    timeprecision 1ps;
    
    
    module system_wrapper #
    (
      // Parameters
      parameter string id_description = "",  // P:Description ............ Max 128 Characters
      parameter string id_company     = "",  // P:Company ................ Max  64 Characters
      parameter string id_author      = "",  // P:Author ................. Max  64 Characters
      parameter string id_version     = "",  // P:Version ................ Max  32 Characters
      parameter string id_timestamp   = "",  // P:Timestamp .............. Max  32 Characters
      parameter string id_hash        = ""   // P:Hash ................... Max  64 Characters
    )
    (
      // LEDs
      output [ 7:0] leds,               // O:LEDs
      // DIP Switches
      input  [ 7:0] switches,           // I:DIP Switches
      // Push Buttons
      input  [ 4:0] buttons,            // I:Push Buttons
      // System
      inout  [14:0] DDR_addr,           // B:Address
      inout  [ 2:0] DDR_ba,             // B:Bank Address
      inout         DDR_cas_n,          // B:Column Address Select
      inout         DDR_ck_n,           // B:Clock (Neg)
      inout         DDR_ck_p,           // B:Clock (Pos)
      inout         DDR_cke,            // B:Clock Enable
      inout         DDR_cs_n,           // B:Chip Select
      inout  [ 3:0] DDR_dm,             // B:Data Mask
      inout  [31:0] DDR_dq,             // B:Data Input/Output
      inout  [ 3:0] DDR_dqs_n,          // B:Data Strobe (Neg)
      inout  [ 3:0] DDR_dqs_p,          // B:Data Strobe (Pos)
      inout         DDR_odt,            // B:Output Dynamic Termination
      inout         DDR_ras_n,          // B:Row Address Select
      inout         DDR_reset_n,        // B:Reset
      inout         DDR_we_n,           // B:Write Enable
      inout         FIXED_IO_ddr_vrn,   // B:Termination Voltage
      inout         FIXED_IO_ddr_vrp,   // B:Termination Voltage
      inout  [53:0] FIXED_IO_mio,       // B:Peripheral Input/Output
      inout         FIXED_IO_ps_clk,    // B:System Reference Clock
      inout         FIXED_IO_ps_porb,   // B:Power On Reset
      inout         FIXED_IO_ps_srstb   // B:External System Reset
    );
    
    
      // Function to convert a string to a vector (max 128 characters)
      function [1023:0] fmt(input string str);
        int len;
        bit [1023:0] tmp;
        len = str.len();
        for (int i=0; i<len; i++)
          tmp[8*i +: 8] = str.getc(i);
        fmt = tmp;
      endfunction
    
    
      // Top-Level Block Design
      system system_i
       (
        // Identification Strings
        .id_description    ( fmt(id_description) ),  // P:Description
        .id_company        ( fmt(id_company)     ),  // P:Company
        .id_author         ( fmt(id_author)      ),  // P:Author
        .id_version        ( fmt(id_version)     ),  // P:Version
        .id_timestamp      ( fmt(id_timestamp)   ),  // P:Timestamp
        .id_hash           ( fmt(id_hash)        ),  // P:Hash
        // LEDs
        .leds              ( leds                ),  // O:LEDs
        // DIP Switches
        .switches          ( switches            ),  // I:DIP Switches
        // Push Buttons
        .buttons           ( buttons             ),  // I:Push Buttons
        // System
        .DDR_addr          ( DDR_addr            ),  // B:Address
        .DDR_ba            ( DDR_ba              ),  // B:Bank Address
        .DDR_cas_n         ( DDR_cas_n           ),  // B:Column Address Select
        .DDR_ck_n          ( DDR_ck_n            ),  // B:Clock (Neg)
        .DDR_ck_p          ( DDR_ck_p            ),  // B:Clock (Pos)
        .DDR_cke           ( DDR_cke             ),  // B:Clock Enable
        .DDR_cs_n          ( DDR_cs_n            ),  // B:Chip Select
        .DDR_dm            ( DDR_dm              ),  // B:Data Mask
        .DDR_dq            ( DDR_dq              ),  // B:Data Input/Output
        .DDR_dqs_n         ( DDR_dqs_n           ),  // B:Data Strobe (Neg)
        .DDR_dqs_p         ( DDR_dqs_p           ),  // B:Data Strobe (Pos)
        .DDR_odt           ( DDR_odt             ),  // B:Output Dynamic Termination
        .DDR_ras_n         ( DDR_ras_n           ),  // B:Row Address Select
        .DDR_reset_n       ( DDR_reset_n         ),  // B:Reset
        .DDR_we_n          ( DDR_we_n            ),  // B:Write Enable
        .FIXED_IO_ddr_vrn  ( FIXED_IO_ddr_vrn    ),  // B:Termination Voltage
        .FIXED_IO_ddr_vrp  ( FIXED_IO_ddr_vrp    ),  // B:Termination Voltage
        .FIXED_IO_mio      ( FIXED_IO_mio        ),  // B:Peripheral Input/Output
        .FIXED_IO_ps_clk   ( FIXED_IO_ps_clk     ),  // B:System Reference Clock
        .FIXED_IO_ps_porb  ( FIXED_IO_ps_porb    ),  // B:Power On Reset
        .FIXED_IO_ps_srstb ( FIXED_IO_ps_srstb   )   // B:External System Reset
       );
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/fw/src/diagram/system/hdl/system_wrapper.sv -O fw/src/diagram/system/hdl/system_wrapper.sv
    
    Check out the changes.
    steve@Desktop:~/projects/zedboard_leds_switches$ git difftool fw/src/diagram/system/hdl/system_wrapper.sv
    

    18. Modify pin constraints

    Modify the constraints to remove the _tri_o postfix from the 8 LED pins and the _tri_i postfix from the 8 DIP Switch pins. Include the 5 additional Push Buttons pins.

    Double click on the existing constraints zedboard.xdc within Sources » Constraints » constrs_1 to edit. Alternatively edit with the preferred text editor. Missing Image! Adjust zedboard.xdc and save the file.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/constraint/zedboard.xdc
    

    zedboard.xdc

    # User LEDs - Bank 33
    set_property PACKAGE_PIN T22 [get_ports {leds[0]}];  # "LD0"
    set_property PACKAGE_PIN T21 [get_ports {leds[1]}];  # "LD1"
    set_property PACKAGE_PIN U22 [get_ports {leds[2]}];  # "LD2"
    set_property PACKAGE_PIN U21 [get_ports {leds[3]}];  # "LD3"
    set_property PACKAGE_PIN V22 [get_ports {leds[4]}];  # "LD4"
    set_property PACKAGE_PIN W22 [get_ports {leds[5]}];  # "LD5"
    set_property PACKAGE_PIN U19 [get_ports {leds[6]}];  # "LD6"
    set_property PACKAGE_PIN U14 [get_ports {leds[7]}];  # "LD7"
    
    
    # User Push Buttons - Bank 34
    set_property PACKAGE_PIN P16 [get_ports {buttons[0]}];  # "BTNC"
    set_property PACKAGE_PIN R16 [get_ports {buttons[1]}];  # "BTND"
    set_property PACKAGE_PIN N15 [get_ports {buttons[2]}];  # "BTNL"
    set_property PACKAGE_PIN R18 [get_ports {buttons[3]}];  # "BTNR"
    set_property PACKAGE_PIN T18 [get_ports {buttons[4]}];  # "BTNU"
    
    
    # User DIP Switches - Bank 34 & 35
    set_property PACKAGE_PIN F22 [get_ports {switches[0]}];  # "SW0"
    set_property PACKAGE_PIN G22 [get_ports {switches[1]}];  # "SW1"
    set_property PACKAGE_PIN H22 [get_ports {switches[2]}];  # "SW2"
    set_property PACKAGE_PIN F21 [get_ports {switches[3]}];  # "SW3"
    set_property PACKAGE_PIN H19 [get_ports {switches[4]}];  # "SW4"
    set_property PACKAGE_PIN H18 [get_ports {switches[5]}];  # "SW5"
    set_property PACKAGE_PIN H17 [get_ports {switches[6]}];  # "SW6"
    set_property PACKAGE_PIN M15 [get_ports {switches[7]}];  # "SW7"
    
    
    # Banks
    set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 33]];
    set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 34]];
    set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 35]];
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_leds_switches$ wget https://spacewire.co.uk/tutorial/shared/repos/0022/zedboard_leds_switches/fw/src/constraint/zedboard.xdc -O fw/src/constraint/zedboard.xdc
    
    Check out the changes.
    steve@Desktop:~/projects/zedboard_leds_switches$ git difftool fw/src/constraint/zedboard.xdc
    

    19. Revision control

    Check GIT status to make sure all is well and there are no spurious elements.
    steve@Desktop:~/projects/zedboard_leds_switches$ git status
    On branch master
    Your branch is up-to-date with 'origin/master'.
    
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory)
            modified:   .gitignore      
            modified:   fw/project.txt      
            modified:   fw/src/constraint/zedboard.xdc      
            modified:   fw/src/diagram/system/hdl/system_wrapper.sv      
            modified:   fw/src/diagram/system/system.bd      
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
            fw/src/design/axi_gpio_zed.v      
            fw/src/design/axi_register_bank.v      
            fw/src/design/debounce.v      
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    Looks good! Remove the old auto-generated AXI Register Bank. Commit the new & updated files and push the commits up to the remote repository.
    steve@Desktop:~/projects/zedboard_leds_switches$ git rm -r fw/src/ip_repo
    steve@Desktop:~/projects/zedboard_leds_switches$ git add fw/src/design/*
    steve@Desktop:~/projects/zedboard_leds_switches$ git commit -a -m "Replaced Xilinx GPIO with custom GPIO for the Zedboard. Also replaced auto-generated Register Bank with custom version."
    steve@Desktop:~/projects/zedboard_leds_switches$ git push
    steve@Desktop:~/projects/zedboard_leds_switches$ git tag -a v5.0 -m "ZYNQ, GPIO Zed, Register Bank & Identification"
    steve@Desktop:~/projects/zedboard_leds_switches$ git push origin v5.0
    

    20. Generate bitstream

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

    21. Export Hardware Platform

    Export the hardware platform by entering the following command in the Tcl Console.
    write_hw_platform -fixed -include_bit -force -file ~/projects/zedboard_leds_switches/fw/system_wrapper.xsa
    #### Part 3 - OS Development ####

    22. Change present working directory

    Change the present working directory to be the project directory.
    steve@Desktop:~$ cd ~/projects/zedboard_linux
    

    23. Bump Version

    Change the version (or revision) number for this new development, this prevents ghost (post-release, same version) builds from appearing.
    steve@Desktop:~/projects/zedboard_linux$ sed -i 's/10.0/11.0/g' os/petalinux/project-spec/meta-user/recipes-apps/website/files/project.txt
    

    project.txt

    Zedboard PetaLinux Example Design
    SpaceWire UK
    Steve Haywood
    11.0
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_linux$ wget https://spacewire.co.uk/tutorial/shared/repos/0023/zedboard_linux/os/petalinux/project-spec/meta-user/recipes-apps/website/files/project.txt -O os/petalinux/project-spec/meta-user/recipes-apps/website/files/project.txt
    

    24. Update LED Runner application

    A little housekeeping to start with!

    Update the LED Runner application to deal with the new base address of the AXI GPIO that controls the LEDs. Change the poke addresses in the script from 0x41200000 to 0x40010000. Ah the joys (and dangers) of magic numbers!
    steve@Desktop:~/projects/zedboard_linux$ sed -i 's/0x4120/0x4001/g' os/petalinux/project-spec/meta-user/recipes-apps/led-runner/files/led-runner
    

    led-runner

    #!/bin/sh
    
    poke 0x40010000 0x18
    usleep 125000
    poke 0x40010000 0x3C
    usleep 125000
    poke 0x40010000 0x7E
    usleep 125000
    poke 0x40010000 0xFF
    usleep 125000
    poke 0x40010000 0x7E
    usleep 125000
    poke 0x40010000 0x3C
    usleep 125000
    poke 0x40010000 0x18
    usleep 125000
    poke 0x40010000 0x00
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_linux$ wget https://spacewire.co.uk/tutorial/shared/repos/0023/zedboard_linux/os/petalinux/project-spec/meta-user/recipes-apps/led-runner/files/led-runner -O os/petalinux/project-spec/meta-user/recipes-apps/led-runner/files/led-runner
    

    25. Add Hardware Platform

    Configure the PetaLinux project to use the exported hardware platform from the zedboard_leds_switches Vivado project.
    steve@Desktop:~/projects/zedboard_linux$ cd os/petalinux
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-config --get-hw-description ../../../zedboard_leds_switches/fw/system_wrapper.xsa
    
    The configuration menu now appears.

    Select Exit to leave the configuration menu.Missing Image!Select Yes when prompted to save the new configuration.Missing Image!

    26. Configure the Kernel

    Configure the PetaLinux Kernel to load the Userspace I/O Drivers at startup.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-config -c kernel
    
    Once the Configuration window appears select Device Drivers.. Missing Image! Then select Userspace I/O drivers. Missing Image! Change the Userspace I/O platform driver with generic IRQ handling option from <M> to <*>. Missing Image! Select < Save > to commit the configuration changes. Missing Image! Select < Ok > to use the default filename provided. Missing Image! Select < Exit > to close the dialog. Missing Image! Select < Exit > to leave the Userspace I/O drivers submenu. Missing Image! Select < Exit > to leave the Device Drivers submenu. Missing Image! Select the final < Exit > to close the Configuration window. Missing Image!

    27. Examine & add to the device tree

    Firstly examine the automatically generated Device Tree Source Include. As can be seen, the interrupt output of the axi_gpio_zed_0 module as already been inferred as being an Interrupt (due to the pin being connected to the Zynq IRQ_F2P Interrupt pin).
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ cd ../..
    
    steve@Desktop:~/projects/zedboard_linux$ subl os/petalinux/components/plnx_workspace/device-tree/device-tree/pl.dtsi
    

    pl.dtsi

    /*
     * CAUTION: This file is automatically generated by Xilinx.
     * Version:  
     * Today is: Tue Aug  8 08:31:05 2023
     */
    
    
    / {
    	amba_pl: amba_pl {
    		#address-cells = <1>;
    		#size-cells = <1>;
    		compatible = "simple-bus";
    		ranges ;
    		axi_gpio_zed_0: axi_gpio_zed@40010000 {
    			clock-names = "aclk";
    			clocks = <&clkc 15>;
    			compatible = "xlnx,axi-gpio-zed-1.0";
    			interrupt-names = "interrupt";
    			interrupt-parent = <&intc>;
    			interrupts = <0 29 4>;
    			reg = <0x40010000 0x10000>;
    		};
    		axi_identification_0: axi_identification@40000000 {
    			clock-names = "aclk";
    			clocks = <&clkc 15>;
    			compatible = "xlnx,axi-identification-1.0";
    			reg = <0x40000000 0x10000>;
    		};
    		axi_register_bank_0: axi_register_bank@40020000 {
    			clock-names = "aclk";
    			clocks = <&clkc 15>;
    			compatible = "xlnx,axi-register-bank-1.0";
    			reg = <0x40020000 0x10000>;
    		};
    	};
    };
    
    There are other Device Tree Source Include files that can be used to add/overwrite elements of the automatically generated one.
    steve@Desktop:~/projects/zedboard_linux$ subl os/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/pl-custom.dtsi
    

    pl-custom.dtsi

    /*Add pl custom nodes for pl.dtsi which is generated from base xsa file.
    Changes in this file reflects only when enabled the FPGA manager/Device tree overlay.*/
    / {
    };
    
    steve@Desktop:~/projects/zedboard_linux$ subl os/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
    

    system-user.dtsi

    /include/ "system-conf.dtsi"
    / {
    };
    
    Edit system-user.dtsi to add some extra Interrupt specifics, override the active high level-sensitive (4) Interrupt trigger with low-to-high edge triggered (1) and associate axi_gpio_zed_0 with the uio. Add the uio arguments to bootargs.
    steve@Desktop:~/projects/zedboard_linux$ subl os/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
    

    system-user.dtsi

    /include/ "system-conf.dtsi"
    
    / {
        chosen
        {
          bootargs = "uio_pdrv_genirq.of_id=generic-uio";
        };
    };
    
    &axi_gpio_zed_0
    {
      #interrupt-cells = <2>;
      interrupt-controller;
      interrupts = <0 29 1>;
      compatible = "generic-uio";
    };
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_linux$ wget https://spacewire.co.uk/tutorial/shared/repos/0023/zedboard_linux/os/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi -O os/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
    

    28. Build & package PetaLinux

    steve@Desktop:~/projects/zedboard_linux$ cd os/petalinux
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-build
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-package --prebuilt --force
    
    #### Part 4 - Hardware Deployment ####

    29. Setup Zedboard hardware

    If not already, connect up the hardware as follows :-
    1. Xubuntu PC USB ⇄ Zedboard USB JTAG/Debug
    2. Xubuntu PC USB ⇄ Zedboard USB UART
    3. Zedboard Ethernet ⇄ Router
    4. Xubuntu PC Ethenet ⇄ Router
    5. Router ⇄ Internet
    Missing Image!Set the boot mode jumpers on the Zedboard for JTAG.Missing Image!Power on the Zedboard.

    30. 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
    

    31. Deploy firmware & software on Zedboard

    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-boot --jtag --prebuilt 3
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ cd ../..
    

    32. Check everything is working as expected (general)

    All being well the following sequence of events should be observed.
    1. The blue done LED illuminates indicating the Programmable Logic (PL) has been programmed.
    2. The software runs on the Processor System (PS).
    3. PetaLinux starts to boot.
    4. The led-runner application launches and executes the expanding & contracting LED illumination sequence.
    5. The PetaLinux login prompt appears in the terminal emulator.
    Login to PetaLinux using the root credentials (username = root & password = root).

    Firstly check to see if the Userspace I/O device driver exists.
    root@petalinux:~# ls -la /dev/ui*
    
    All being well the above command should return the following :-
    crw-------    1 root     root      246,   0 Jan  1  1970 /dev/uio0
    
    Now check to see if the GPIO interrupt exists.
    root@petalinux:~# cat /proc/interrupts
    
    All being well the above command should return the following (the axi_gpio_zed entry is the one required) :-
               CPU0       CPU1
     24:          0          0     GIC-0  27 Edge      gt
     25:      38988      57081     GIC-0  29 Edge      twd
     26:          0          0     GIC-0  37 Level     arm-pmu
     27:          0          0     GIC-0  38 Level     arm-pmu
     28:         43          0     GIC-0  39 Level     f8007100.adc
     31:          0          0     GIC-0  35 Level     f800c000.ocmc
     32:        222          0     GIC-0  82 Level     xuartps
     33:          0          0     GIC-0  51 Level     e000d000.spi
     34:     127627          0     GIC-0  54 Level     eth0
     35:        264          0     GIC-0  56 Level     mmc0
     36:          0          0     GIC-0  45 Level     f8003000.dmac
     37:          0          0     GIC-0  46 Level     f8003000.dmac
     38:          0          0     GIC-0  47 Level     f8003000.dmac
     39:          0          0     GIC-0  48 Level     f8003000.dmac
     40:          0          0     GIC-0  49 Level     f8003000.dmac
     41:          0          0     GIC-0  72 Level     f8003000.dmac
     42:          0          0     GIC-0  73 Level     f8003000.dmac
     43:          0          0     GIC-0  74 Level     f8003000.dmac
     44:          0          0     GIC-0  75 Level     f8003000.dmac
     45:          0          0     GIC-0  40 Level     f8007000.devcfg
     47:          0          0     GIC-0  43 Level     ttc_clockevent
     52:          0          0     GIC-0  41 Edge      f8005000.watchdog
     53:          0          0     GIC-0  61 Edge      axi_gpio_zed
    IPI0:          0          0  CPU wakeup interrupts
    IPI1:          0          0  Timer broadcast interrupts
    IPI2:       3631       5888  Rescheduling interrupts
    IPI3:        147        218  Function call interrupts
    IPI4:          0          0  CPU stop interrupts
    IPI5:          0          0  IRQ work interrupts
    IPI6:          0          0  completion interrupts
    Err:          0
    
    The uio0 character device should now be available in /sys/class/uio.
    root@petalinux:~# ls /sys/class/uio/uio0
    dev        event      name       subsystem  version
    device     maps       power      uevent
    
    Examine some of its elements, which should look familiar.
    root@petalinux:~# cat /sys/class/uio/uio0/name
    axi_gpio_zed
    root@petalinux:~# cat /sys/class/uio/uio0/maps/map0/{addr,name,offset,size}
    0x40010000
    axi_gpio_zed@40010000
    0x0
    0x00010000
    

    33. Check everything is working as expected (command-line)

    Enable and check the interrupt mechanism using the command-line (or skip to GUI version).

    1. Check the LED's register can be written & read back from. This time use devmem instead of peek & poke.
    root@petalinux:~# devmem 0x40010000 w 0x18
    root@petalinux:~# devmem 0x40010000
    0x00000018
    
    All being well LED's 3 & 4 should illuminate with the first command and the second command should return the written value of 0x18.

    2. Set the Global Interrupt Enable Register inside the Programmable Logic (PL) AXI GPIO Zed module.
    root@petalinux:~# devmem 0x4001011c w 0x80000000
    
    3. Enable interrupts for Channel 3 (Push Buttons) by setting the 3rd bit of the Interrupt Enable Register.
    root@petalinux:~# devmem 0x40010128 w 0x4
    
    The interrupt mechanism is now ready.

    4. Press and release one of the Push Buttons on the Zedboard.

    5. Check the Kernel for an interrupt. All being well the interrupt counter should have incremented by 1 indicating that an interrupt has occurred.
    root@petalinux:~# cat /proc/interrupts | grep axi_gpio_zed
     53:          1          0     GIC-0  61 Edge      axi_gpio_zed
    
    6. Determine which GPIO Channel the interrupt occurred on by reading the Interrupt Status Register. Should show Channel 3 (3rd bit set).
    root@petalinux:~# devmem 0x40010120
    0x00000004
    
    7. Determine which Push Button was pressed by reading Channel 3's Data Register. 0x2 indicates the down button.
    root@petalinux:~# devmem 0x40010010
    0x00000002
    
    8. Clear Channel 3's Data Register.
    root@petalinux:~# devmem 0x40010010 w 0x0
    
    9. Rearm Channel 3's interrupt by clearing its set bit (toggle-on-write) in the Interrupt Status Register.
    root@petalinux:~# devmem 0x40010120 w 0x4
    
    10. Rearm the Kernel interrupt by writing a 1 to the device.
    root@petalinux:~# echo 0x1>/dev/uio0
    
    Steps 4 through 10 can now be repeated. Each iteration should show an incremented count for the axi_gpio_zed interrupt.

    An Interrupt can be generated without pressing a button by simply setting a bit in the Interrupt Status Register (this is handled in exactly the same way as a Push Button generated interrupt).

    11. Clear the Global Interrupt Enable Register.
    root@petalinux:~# devmem 0x4001011c w 0x00000000
    
    12. Disable interrupts for Channel 3 by clearing the 3rd bit in the Interrupt Enable Register.
    root@petalinux:~# devmem 0x40010128 w 0x0
    

    34. Check everything is working as expected (GUI)

    Enable and check the interrupt mechanism using the GUI.

    Access the webserver running on the Zedboard using a browser pointing at the Zedboard's IP address (192.168.2.87).

    Edit the Peek & Poke section to reflect the following. Missing Image! Create a configuration file from the address table by clicking on Create.... A generated link (config.txt) should now appear next to the Create... button. Right click on the link and select Save Link As..., set the name to axi_gpio_zed.txt and save the file in ~/projects/zedboard_linux/os/src/other.
    steve@Desktop:~/projects/zedboard_linux$ subl os/src/other/axi_gpio_zed.txt
    

    axi_gpio_zed.txt

    sec|AXI General Purpose IO - Zedboard Specific
    reg|0x40010000|false|0x00000018|true|LEDs
    reg|0x40010008|true|0x00000000|false|DIP Switches
    reg|0x40010010|true|0x00000000|true|Push Buttons
    reg|0x4001011C|false|0x80000000|true|Global Interrupt Enable
    reg|0x40010128|false|0x00000004|true|Interrupt Enable
    reg|0x40010120|true|0x00000004|true|Interrupt Status
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_linux$ wget https://spacewire.co.uk/tutorial/shared/repos/0023/zedboard_linux/os/src/other/axi_gpio_zed.txt -O os/src/other/axi_gpio_zed.txt
    
    Do a little housekeeping on the other LEDs & Switches configuration file to keep it up to date with the address map changes.
    steve@Desktop:~/projects/zedboard_linux$ subl os/src/other/zedboard_leds_switches.txt
    

    zedboard_leds_switches.txt

    sec|AXI GPIO
    reg|0x40010000|true|0x00000018|true|LEDs
    reg|0x40010008|true|0x00000000|false|DIP Switches
    reg|0x40010010|true|0x00000000|true|Push Buttons
    sec|AXI Register Bank
    reg|0x40020000|true|0x00000000|true|Register 0
    reg|0x40020004|true|0x456789AB|true|Register 1
    reg|0x40020008|true|0x00000000|true|Register 2
    reg|0x4002000C|true|0x0000FF00|true|Register 3
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_linux$ wget https://spacewire.co.uk/tutorial/shared/repos/0023/zedboard_linux/os/src/other/zedboard_leds_switches.txt -O os/src/other/zedboard_leds_switches.txt
    
    Set Peek Refresh to 1s to see what is happening on the DIP Switch & Push Button inputs and also on the Interrupt Status register.

    The test sequence for the GUI is much the same as for the command-line :-
    1. Poke LEDs with 0x18
    2. Poke Global Interrupt Enable with 0x80000000 (Enable Interrupts)
    3. Poke Interrupt Enable with 0x4 (Enable Channel 3 Interrupts - Push Buttons)
    4. Press the centre Push Button
      • The Interrupt Status register should show 0x4 indicating there is an Interrupt on Channel 3
      • The Push Buttons register should show 0x1 indicating the centre Push Button was pressed
    5. Check the Kernel for an interrupt (count should have increased by 1)
      root@petalinux:~# cat /proc/interrupts | grep axi_gpio_zed
       53:          2          0     GIC-0  61 Edge      axi_gpio_zed
      
    6. Poke Push Buttons register with 0x0 to clear it.
    7. Poke Interrupt Status register with 0x4 to rearm Channel 3 interrupts
    8. Rearm the Kernel interrupt by writing a 1 to the device.
      root@petalinux:~# echo 0x1>/dev/uio0
      
    Steps 4 to 8 can now be repeated.
    #### Part 5 - Software Development ####

    35. Create C application

    Create a new auto-enabled C application.
    steve@Desktop:~/projects/zedboard_linux$ cd os/petalinux
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-create --type apps --template c --name axi-gpio-zed-test --enable
    
    Examine the file structure of the newly created C application.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ tree project-spec/meta-user/recipes-apps/axi-gpio-zed-test
    project-spec/meta-user/recipes-apps/axi-gpio-zed-test
    ├── files
    │   ├── Makefile
    │   └── axi-gpio-zed-test.c
    ├── axi-gpio-zed-test.bb
    └── README
    
    1 directory, 4 files
    

    36. Edit C application

    Edit the source to create an application that waits for an interrupt, decodes the Push Buttons and illuminates the LED's accordingly. This application should be a combination of the above interrupt sequence along with the Push Button & LED code from Part 4.

    Something similar to the following should do the trick :-
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ cd ../..
    
    steve@Desktop:~/projects/zedboard_linux$ subl os/petalinux/project-spec/meta-user/recipes-apps/axi-gpio-zed-test/files/axi-gpio-zed-test.c
    

    axi-gpio-zed-test.c

    //
    // File .......... axi-gpio-zed-test.c
    // Author ........ Steve Haywood
    // Version ....... 1.0
    // Date .......... 29 October 2022
    // Description ...
    //   Very simple program to access a GPIO device using the User Space I/O
    // subsystem. Demonstrates a blocking wait on interrupt routine.
    //
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    
    #define XGPIO_DATA_OFFSET           0x0
    #define XGPIO_TRI_OFFSET            0x4
    #define XGPIO_DATA2_OFFSET          0x8
    #define XGPIO_TRI2_OFFSET           0xC
    #define XGPIO_DATA3_OFFSET          0x10
    #define XGPIO_TRI3_OFFSET           0x14
    #define XGPIO_GIE_OFFSET            0x11C
    #define XGPIO_ISR_OFFSET            0x120
    #define XGPIO_IER_OFFSET            0x128
    #define XGPIO_IR_MASK               0x7
    #define XGPIO_IR_CH1_MASK           0x1
    #define XGPIO_IR_CH2_MASK           0x2
    #define XGPIO_IR_CH3_MASK           0x4
    #define XGPIO_GIE_GINTR_ENABLE_MASK 0x80000000
    
    #define BUTTON_CENTER 0x01
    #define BUTTON_DOWN   0x02
    #define BUTTON_LEFT   0x04
    #define BUTTON_RIGHT  0x08
    #define BUTTON_UP     0x10
    
    
    // Read a value from a GPIO register.
    unsigned int gpio_read(void *base, unsigned int offset)
    {
      return *((volatile unsigned int *)(base + offset));
    }
    
    
    // Write a value to a GPIO register.
    void gpio_write(void *base, unsigned int offset, unsigned int data)
    {
      *((volatile unsigned int *)(base + offset)) = data;
    }
    
    
    // Rotate right
    unsigned char ror(unsigned char num) {
      return (num >> 1) | (num << 7);
    }
    
    
    // Rotate left
    unsigned char rol(unsigned char num) {
      return (num << 1) | (num >> 7);
    }
    
    
    // GPIO button decode & LED driver
    void buttons_to_leds(void *base)
    {
      unsigned int leds;
      unsigned int btns;
      unsigned int width = 0;
    
      // Obtain register values for LED's & buttons
      leds = gpio_read(base, XGPIO_DATA_OFFSET);
      btns = gpio_read(base, XGPIO_DATA3_OFFSET);
    
      // Print button status
      printf("Interrupt detected, button register = 0x%08x\n", btns);
    
      // Determine LED bar width
      for (unsigned int i=0; i<=7; i++) {
        if ((0b00000001 << i) & leds) {
          width++;
        }
      }
    
        // Set LED bar width to 4
        if (btns & BUTTON_CENTER) {
          leds = 0b00111100;
        }
    
        // Rotate LED's left
        if (btns & BUTTON_LEFT) {
          if (width == 0) {
            leds = 0b00000001;
          } else {
            leds = (unsigned char)rol(leds);
          }
        }
    
        // Rotate LED's right
        if (btns & BUTTON_RIGHT) {
          if (width == 0) {
            leds = 0b10000000;
          } else {
            leds = (unsigned char)ror(leds);
          }
        }
    
        // Increase illuminated LED's
        if (btns & BUTTON_UP) {
          if (width == 0) {
            leds = 0b00001000;
          } else if (width & 1) {
            leds = leds | rol(leds);
          } else {
            leds = leds | ror(leds);
          }
        }
    
        // Decrease illuminated LED's
        if (btns & BUTTON_DOWN) {
          if (width == 8) {
            leds = 0b01111111;
          } else if (width & 1) {
            leds = leds & rol(leds);
          } else {
            leds = leds & ror(leds);
          }
        }
    
      // Clear (all latched) Buttons
      gpio_write(base, XGPIO_DATA3_OFFSET, 0x0);
    
      // Update LED's
      gpio_write(base, XGPIO_DATA_OFFSET, leds);
    }
    
    
    // Wait for an interrupt from GPIO device
    void wait_interrupt(int fd, void *base)
    {
      unsigned int pending = 0;
      unsigned int reenable = 1;
      unsigned int data;
    
      // Wait for User Space interrupt (blocking read)
      read(fd, (void *)&pending, sizeof(pending));
    
      // Read GPIO Interrupt Status Register
      data = gpio_read(base, XGPIO_ISR_OFFSET);
    
      // Check for interrupt on GPIO Channel 3 & clear if present
      if (data == XGPIO_IR_CH3_MASK)
        gpio_write(base, XGPIO_ISR_OFFSET, XGPIO_IR_CH3_MASK);
    
      // Decode buttons & drive LEDs
      buttons_to_leds(base);
    
      // Re-enable User Space interrupt
      write(fd, (void *)&reenable, sizeof(reenable));
    }
    
    
    // Get device driver memory size
    unsigned int get_device_size(char *filename)
    {
      FILE *fp;
      unsigned int size;
    
      // Open ASCII file
      fp = fopen(filename, "r");
      if (!fp) {
        printf("Error: Failed to open %s file\n", filename);
        exit(EXIT_FAILURE);
      }
    
      // Convert hexadecimal size string into a number
      fscanf(fp, "0x%08X", &size);
    
      // Close ASCII file
      fclose(fp);
    
      // Return device size
      return size;
    }
    
    
    // Main function
    int main(int argc, char *argv[])
    {
      char *devicename = "/dev/uio0";
      char *filename = "/sys/class/uio/uio0/maps/map0/size";
      int fd;
      unsigned int devicesize;
      void *deviceptr;
    
      // Open GPIO device with read/write access
      fd = open(devicename, O_RDWR);
      if (fd < 1) {
        printf("Error: Failed to open %s device\n", devicename);
        exit(EXIT_FAILURE);
      }
    
      // Get GPIO device memory size
      devicesize = get_device_size(filename);
    
      // Map GPIO device memory region
      deviceptr = mmap(NULL, devicesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
      if (deviceptr == MAP_FAILED) {
        printf("Error: Failed to mmap");
        exit(EXIT_FAILURE);
      }
    
      // Print header
      printf("Simple User Space device driver example with interrupts\n\n");
      printf("Device in use ... %s\n", devicename);
      printf("Memory size ..... %u (obtained from %s)\n\n", devicesize, filename);
      printf("Use the buttons to control the LED's :-\n\n");
      printf("Centre - Reset LED's to 00111100\n");
      printf("Left   - Rotate LED's left\n");
      printf("Right  - Rotate LED's right\n");
      printf("Up     - Increase illuminated LED's\n");
      printf("Down   - Decrease illuminated LED's\n\n");
      printf("Press Ctrl-C to quit application\n");
    
      // Enable Interrupts for GPIO Channel 3
      gpio_write(deviceptr, XGPIO_GIE_OFFSET, XGPIO_GIE_GINTR_ENABLE_MASK);
      gpio_write(deviceptr, XGPIO_IER_OFFSET, XGPIO_IR_CH3_MASK);
    
      // Wait for Interrupt
      while (1) {
        wait_interrupt(fd, deviceptr);
      }
    
      // Disable Interrupts for GPIO Channel 3
      gpio_write(deviceptr, XGPIO_GIE_OFFSET, 0x0);
      gpio_write(deviceptr, XGPIO_IER_OFFSET, 0x0);
    
      // Unmap GPIO device memory
      munmap(deviceptr, devicesize);
    
      // Close GPIO device
      close(fd);
    
      return EXIT_SUCCESS;
    }
    
    Direct download available here :-
    steve@Desktop:~/projects/zedboard_linux$ wget https://spacewire.co.uk/tutorial/shared/repos/0023/zedboard_linux/os/petalinux/project-spec/meta-user/recipes-apps/axi-gpio-zed-test/files/axi-gpio-zed-test.c -O os/petalinux/project-spec/meta-user/recipes-apps/axi-gpio-zed-test/files/axi-gpio-zed-test.c
    

    37. Cross compile application

    Cross compile the C code targetting the ARM processor.
    steve@Desktop:~/projects/zedboard_linux$ arm-linux-gnueabihf-gcc os/petalinux/project-spec/meta-user/recipes-apps/axi-gpio-zed-test/files/axi-gpio-zed-test.c -o /tmp/axi-gpio-zed-test
    

    38. Copy application to PetaLinux

    Copy the compiled application over to PetaLinux.
    steve@Desktop:~/projects/zedboard_linux$ scp /tmp/axi-gpio-zed-test root@192.168.2.87:/home/root
    

    39. Check application is working as expected

    Run the application from PetaLinux using the MiniCom terminal emulator.
    root@petalinux:~# ./axi-gpio-zed-test
    
    All being well the application should run and display the following :-
    Simple User Space device driver example with interrupts
    
    Device in use ... /dev/uio0
    Memory size ..... 65536 (obtained from /sys/class/uio/uio0/maps/map0/size)
    
    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 Ctrl-C to quit application
    
    Pressing a Push Button should perform the action stated and display the GPIO3 data register, for example :-
    Interrupt detected, button register = 0x00000001
    Interrupt detected, button register = 0x00000004
    
    The application can be made resident in PetaLinux by using the PetaLinux create C application process.

    40. Revision control

    Check GIT status to make sure all is well and there are no spurious elements.
    steve@Desktop:~/projects/zedboard_linux$ git status
    On branch master
    Your branch is up-to-date with 'origin/master'.
    
    Changes not staged for commit:
      (use "git add/rm <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory)
            modified:   os/petalinux/.petalinux/metadata      
            modified:   os/petalinux/project-spec/configs/rootfs_config      
            deleted:    os/petalinux/project-spec/hw-description/drivers/register_bank_v1_0/data/register_bank.mdd      
            deleted:    os/petalinux/project-spec/hw-description/drivers/register_bank_v1_0/data/register_bank.tcl      
            deleted:    os/petalinux/project-spec/hw-description/drivers/register_bank_v1_0/src/Makefile      
            deleted:    os/petalinux/project-spec/hw-description/drivers/register_bank_v1_0/src/register_bank.c      
            deleted:    os/petalinux/project-spec/hw-description/drivers/register_bank_v1_0/src/register_bank.h      
            deleted:    os/petalinux/project-spec/hw-description/drivers/register_bank_v1_0/src/register_bank_selftest.c      
            modified:   os/petalinux/project-spec/hw-description/system.xsa      
            modified:   os/petalinux/project-spec/hw-description/system_wrapper.bit      
            modified:   os/petalinux/project-spec/meta-user/conf/user-rootfsconfig      
            modified:   os/petalinux/project-spec/meta-user/recipes-apps/led-runner/files/led-runner      
            modified:   os/petalinux/project-spec/meta-user/recipes-apps/website/files/project.txt      
            modified:   os/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi      
            modified:   os/petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx_%.bbappend      
            modified:   os/src/other/zedboard_leds_switches.txt      
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
            os/petalinux/project-spec/meta-user/recipes-apps/axi-gpio-zed-test/      
            os/petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/user_2023-08-08-08-28-00.cfg      
            os/src/other/axi_gpio_zed.txt      
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    Looks good!

    Add and commit the new & updated files, create an annotated tag and push the commits & tag up to the remote repository.
    steve@Desktop:~/projects/zedboard_linux$ git add os/petalinux/project-spec/meta-user/recipes-apps/axi-gpio-zed-test
    steve@Desktop:~/projects/zedboard_linux$ git add os/petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/user_2023-08-08-08-28-00.cfg
    steve@Desktop:~/projects/zedboard_linux$ git add os/src/other/axi_gpio_zed.txt
    steve@Desktop:~/projects/zedboard_linux$ git commit -a -m "Enabled Userspace IO Driver & added application to test new PL AXI GPIO Zed module."
    steve@Desktop:~/projects/zedboard_linux$ git push
    steve@Desktop:~/projects/zedboard_linux$ git tag -a v11.0 -m "PetaLinux, Peek/Poke, LED Runner, Webserver, Peek/Poke CGI, PL Access, Style Sheet, Register Bank, ID Strings, UIO & GPIO Zed Test with XSA from zedboard_leds_switches v5.0"
    steve@Desktop:~/projects/zedboard_linux$ git push origin v11.0
    

    41. Final checks

    Rebuild PetaLinux to include the updated GIT status, package it and deploy on the Zedboard.

    Access the webserver running on the Zedboard using a browser pointing at the Zedboard's IP address (192.168.2.87). Click the Read ID buttons in both the Operating System Information & Firmware Information sectons to read the Identification information from the OS filesystem & PL address space. All being well the following should be displayed in the Operating System Information & Firmware Information sections.

    There should be no unsavoury comments after the version numbers!