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 (8 August 2023)

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

    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

    1. *.jou
    2. *.log
    3. *.str
    4. *.xsa
    5. /fw/vivado
    6. # Still too brutal...
    7. /fw/src/diagram

    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
    The following should provide a good starting point.

    axi_gpio_zed.v

    1. //
    2. // File .......... axi_gpio_zed.v
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 29 October 2022
    6. // Standard ...... IEEE 1364-2001
    7. // Description ...
    8. //   AXI4-Lite controlled GPIO block written in basic Verilog. Primary purpose
    9. // is to provide access to the LEDs, Switches & Push Buttons of the Zedboard.
    10. // Design offers an Interrupt that can be thrown when any of the Push Buttons
    11. // are pressed or released. Address map is compatible with the one used in the
    12. // AXI GPIO IP from Xilinx, albeit a cut-down version.
    13. //


    14. `timescale 1 ns / 1 ps


    15. module axi_gpio_zed #
    16. (
    17.   // Parameters (AXI4-Lite)
    18.   localparam c_addr_w = 9,                                // P:Address Bus Width (bits)
    19.   localparam c_data_w = 32,                               // P:Data Bus Width (bits)
    20.   // GPIO Channels
    21.   parameter  c_leds_w     = 8,                            // P:LEDs Width (bits)
    22.   parameter  c_switches_w = 8,                            // P:Switches Width (bits)
    23.   parameter  c_buttons_w  = 5                             // P:Buttons Width (bits)
    24. )
    25. (
    26.   // Clock & Reset
    27.   input  wire                             aclk,           // I:Clock
    28.   input  wire                             aresetn,        // I:Reset
    29.   // GPIO Channels
    30.   output wire [c_leds_w-1 : 0]            leds,           // O:LEDs
    31.   input  wire [c_switches_w-1 : 0]        switches,       // I:Switches
    32.   input  wire [c_buttons_w-1 : 0]         buttons,        // I:Buttons
    33.   // Interrupt

    34.   output wire                             interrupt,      // O:Output
    35.   // AXI4-Lite - Write Address Channel
    36.   input  wire [c_addr_w-1 : 0]            s_axi_awaddr,   // I:Write Address
    37.   input  wire [2 : 0]                     s_axi_awprot,   // I:Write Protection Type
    38.   input  wire                             s_axi_awvalid,  // I:Write Address Valid
    39.   output reg                              s_axi_awready,  // O:Write Address Ready
    40.   // AXI4-Lite - Write Data Channel
    41.   input  wire [c_data_w-1 : 0]            s_axi_wdata,    // I:Write Data
    42.   input  wire [(c_data_w/8)-1 : 0]        s_axi_wstrb,    // I:Write Strobes
    43.   input  wire                             s_axi_wvalid,   // I:Write Valid
    44.   output reg                              s_axi_wready,   // O:Write Ready
    45.   // AXI4-Lite - Write Response Channel
    46.   output reg  [1 : 0]                     s_axi_bresp,    // O:Write Response
    47.   output reg                              s_axi_bvalid,   // O:Write Response Valid
    48.   input  wire                             s_axi_bready,   // I:Write Response Ready
    49.   // AXI4-Lite - Read Address Channel
    50.   input  wire [c_addr_w-1 : 0]            s_axi_araddr,   // I:Read Address
    51.   input  wire [2 : 0]                     s_axi_arprot,   // I:Read Protection Type
    52.   input  wire                             s_axi_arvalid,  // I:Read Address Valid
    53.   output reg                              s_axi_arready,  // O:Read Address Ready
    54.   // AXI4-Lite - Read Data Channel
    55.   output reg  [c_data_w-1 : 0]            s_axi_rdata,    // O:Read Data
    56.   output reg  [1 : 0]                     s_axi_rresp,    // O:Read Response
    57.   output reg                              s_axi_rvalid,   // O:Read Valid
    58.   input  wire                             s_axi_rready    // I:Read Ready
    59. );


    60.   // Constants
    61.   localparam c_bytes = c_data_w / 8;                         // Number of bytes in data bus
    62.   localparam c_registers = 2**(c_addr_w - $clog2(c_bytes));  // Number of registers
    63.   localparam c_channels = 3;                                 // Number of channels

    64.   // Channel positions
    65.   localparam c_chan_leds     = 0;  // LEDs
    66.   localparam c_chan_switches = 1;  // Switches
    67.   localparam c_chan_buttons  = 2;  // Buttons

    68.   // Register positions
    69.   localparam c_reg_leds      = 'h000 / c_bytes;  // LEDs
    70.   localparam c_reg_switches  = 'h008 / c_bytes;  // Switches
    71.   localparam c_reg_buttons   = 'h010 / c_bytes;  // Buttons
    72.   localparam c_reg_isr       = 'h120 / c_bytes;  // Interrupt Status
    73.   localparam c_reg_ier       = 'h128 / c_bytes;  // Interrupt Enable
    74.   localparam c_reg_gie       = 'h11C / c_bytes;  // Global Interrupt Enable

    75.   // Bit positions
    76.   localparam c_gie_gintr_enable = 31; // Global Interrupt Enable

    77.   // Signals
    78.   reg aw_en;
    79.   reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_awaddr;
    80.   reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_araddr;
    81.   reg [c_data_w-1 : 0] registers [c_registers - 1 : 0];

    82.   wire [c_buttons_w+c_switches_w-1 : 0] inp;   // Debouncer Inputs
    83.   wire [c_buttons_w+c_switches_w-1 : 0] outp;  // Debouncer Outputs

    84.   wire [c_switches_w-1 : 0] switches_int;  // Debounced Switches
    85.   wire [c_buttons_w-1  : 0] buttons_int;   // Debounced Buttons
    86.   reg  [c_buttons_w-1  : 0] buttons_reg;   // Registered Buttons

    87.   reg  [c_chan_buttons : c_chan_buttons] isr;  // Interrupt Status


    88.   // Write Address Ready
    89.   always @(posedge aclk)
    90.     if (!aresetn) begin
    91.       s_axi_awready <= 1'b0;
    92.       aw_en <= 1'b1;
    93.     end else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en) begin
    94.       s_axi_awready <= 1'b1;
    95.       aw_en <= 1'b0;
    96.     end else if (s_axi_bvalid && s_axi_bready) begin
    97.       s_axi_awready <= 1'b0;
    98.       aw_en <= 1'b1;
    99.     end else
    100.       s_axi_awready <= 1'b0;


    101.   // Write Address Latch
    102.   always @(posedge aclk)
    103.     if (!aresetn)
    104.       axi_awaddr <= {c_addr_w{1'b0}};
    105.     else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en)
    106.       axi_awaddr <= s_axi_awaddr[c_addr_w - 1 : $clog2(c_bytes)];


    107.   // Write Data Ready
    108.   always @(posedge aclk)
    109.     if (!aresetn)
    110.       s_axi_wready <= 1'b0;
    111.     else if (s_axi_awvalid && s_axi_wvalid && !s_axi_wready && aw_en)
    112.       s_axi_wready <= 1'b1;
    113.     else
    114.       s_axi_wready <= 1'b0;


    115.   // Write Data
    116.   integer i;
    117.   always @(posedge aclk)
    118.     if (!aresetn)
    119.       for (i = 0; i <= c_registers - 1; i = i + 1)
    120.         registers[i] <= {c_data_w{1'b0}};
    121.     else begin
    122.       // Latch & hold Push Buttons
    123.       for (i = 0; i < c_buttons_w; i = i + 1)
    124.         if (buttons_int[i])
    125.           registers[c_reg_buttons][i] <= 1'b1;
    126.       // Special handling for toggle-on-write
    127.       registers[c_reg_isr] <= {c_data_w{1'b0}};
    128.       if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready)
    129.         for (i = 0; i <= c_bytes - 1; i = i + 1)
    130.           if (s_axi_wstrb[i])
    131.             registers[axi_awaddr][i*8 +: 8] <= s_axi_wdata[i*8 +: 8]; // Byte-wise write
    132.     end


    133.   // Write Response
    134.   always @(posedge aclk)
    135.     if (!aresetn) begin
    136.       s_axi_bvalid <= 1'b0;
    137.       s_axi_bresp  <= 2'b00;
    138.     end else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready && !s_axi_bvalid) begin
    139.       s_axi_bvalid <= 1'b1;
    140.       s_axi_bresp  <= 2'b00;
    141.     end else if (s_axi_bvalid && s_axi_bready)
    142.       s_axi_bvalid <= 1'b0;


    143.   // Read Address Ready
    144.   always @(posedge aclk)
    145.     if (!aresetn)
    146.       s_axi_arready <= 1'b0;
    147.     else if (s_axi_arvalid && !s_axi_arready)
    148.       s_axi_arready <= 1'b1;
    149.     else
    150.       s_axi_arready <= 1'b0;


    151.   // Read Address Latch
    152.   always @(posedge aclk)
    153.     if (!aresetn)
    154.       axi_araddr  <= {c_addr_w{1'b0}};
    155.     else if (s_axi_arvalid && !s_axi_arready)
    156.       axi_araddr <= s_axi_araddr[c_addr_w - 1 : $clog2(c_bytes)];


    157.   // Read Response
    158.   always @(posedge aclk)
    159.     if (!aresetn) begin
    160.       s_axi_rvalid <= 1'b0;
    161.       s_axi_rresp  <= 2'b00;
    162.     end else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
    163.       s_axi_rvalid <= 1'b1;
    164.       s_axi_rresp  <= 2'b00;
    165.     end else if (s_axi_rvalid && s_axi_rready)
    166.       s_axi_rvalid <= 1'b0;


    167.   // Read Data
    168.   always @(posedge aclk)
    169.     if (!aresetn)
    170.       s_axi_rdata <= {c_data_w{1'b0}};
    171.     else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
    172.       // Default
    173.       s_axi_rdata <= {c_data_w{1'b0}};
    174.       // Select
    175.       case (axi_araddr)
    176.         c_reg_leds     : s_axi_rdata[c_leds_w-1     : 0             ] <= registers[c_reg_leds][c_leds_w-1:0];
    177.         c_reg_switches : s_axi_rdata[c_switches_w-1 : 0             ] <= switches_int;
    178.         c_reg_buttons  : s_axi_rdata[c_buttons_w-1  : 0             ] <= registers[c_reg_buttons][c_buttons_w-1:0];
    179.         c_reg_isr      : s_axi_rdata[c_chan_buttons : c_chan_buttons] <= isr;
    180.         c_reg_ier      : s_axi_rdata[c_chan_buttons : c_chan_buttons] <= registers[c_reg_ier][c_chan_buttons : c_chan_buttons];
    181.         c_reg_gie      : s_axi_rdata[c_gie_gintr_enable             ] <= registers[c_reg_gie][c_gie_gintr_enable];
    182.         default        : s_axi_rdata                                  <= {c_data_w{1'b0}};
    183.       endcase
    184.     end


    185.   // Debouncer Connections
    186.   assign inp = {buttons, switches};
    187.   assign {buttons_int, switches_int} = outp;


    188.   // Debouncer
    189.   genvar j;
    190.   generate for (j = 0; j < c_buttons_w + c_switches_w; j = j + 1)
    191.     debounce #
    192.      (
    193.       // Parameters
    194.       .c_cycles ( 65536   )   // Debounce Cycles
    195.      )
    196.     debounce
    197.      (
    198.       // Clock & Reset
    199.       .aclk     ( aclk    ),  // Clock
    200.       .aresetn  ( aresetn ),  // Reset
    201.       // Input & Output
    202.       .inp      ( inp[j]  ),  // Input (Dirty)
    203.       .outp     ( outp[j] )   // Output (Clean)
    204.      );
    205.   endgenerate


    206.   // Register Buttons for Edge Detection
    207.   always @(posedge aclk)
    208.     if (!aresetn)
    209.       buttons_reg <= {c_buttons_w{1'b0}};
    210.     else
    211.       buttons_reg <= buttons_int;


    212.   // Throw an Interrupt when a button is pressed or released
    213.   always @(posedge aclk)
    214.     if (!aresetn)
    215.       isr[c_chan_buttons] <= 1'b0;
    216.     else if (registers[c_reg_gie][c_gie_gintr_enable] && registers[c_reg_ier][c_chan_buttons]) begin
    217. //    if (|(~buttons_reg & buttons_int)) // Button(s) pressed
    218.       if (|(buttons_reg & ~buttons_int)) // Button(s) released
    219. //    if (buttons_reg != buttons_int) // Button(s) changed state
    220.         isr[c_chan_buttons] <= 1'b1;
    221.       else if (registers[c_reg_isr][c_chan_buttons])
    222.         isr[c_chan_buttons] <= !isr[c_chan_buttons]; // Can be set & cleared when Interrupt is enabled
    223.     end else if (registers[c_reg_isr][c_chan_buttons])
    224.       isr[c_chan_buttons] <= 1'b0; // Can only be cleared when Interrupt is disabled


    225.   // Interrupt output
    226.   assign interrupt = |isr;


    227.   // LEDs output
    228.   assign leds = registers[c_reg_leds][c_leds_w-1:0];


    229. endmodule

    Create a simple debounce module to accompany the above design.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/design/debounce.v

    debounce.v

    1. //
    2. // File .......... debounce.v
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 29 October 2022
    6. // Standard ...... IEEE 1364-2001
    7. // Description ...
    8. //   Simple switch debouncer.
    9. //


    10. `timescale 1 ns / 1 ps


    11. module debounce #
    12. (
    13.   // Parameters
    14.   parameter  c_cycles = 65536  // Debounce Cycles
    15. )
    16. (
    17.   // Clock & Reset
    18.   input      aclk,             // Clock
    19.   input      aresetn,          // Reset
    20.   // Input & Output
    21.   input      inp,              // Input (Dirty)
    22.   output reg outp              // Output (Clean)
    23. );


    24.   // Signals
    25.   reg inp_meta;
    26.   reg inp_int;
    27.   reg [$clog2(c_cycles)-1:0] count;


    28.   // Metastability hardener
    29.   always @(posedge aclk)
    30.     if (!aresetn) begin
    31.       inp_meta <= 1'b0;
    32.       inp_int  <= 1'b0;
    33.     end else begin
    34.       inp_meta <= inp;
    35.       inp_int  <= inp_meta;
    36.      end


    37.   // Debouncer
    38.   always @(posedge aclk)
    39.     if (!aresetn) begin
    40.       outp  <= 1'b0;
    41.       count <= 32'b0;
    42.     end else if (outp == inp_int) begin
    43.       count <= 32'b0;
    44.     end else if (count == c_cycles - 1) begin
    45.       outp  <= inp;
    46.       count <= 32'b0;
    47.     end else
    48.       count <= count + 1'b1;


    49. endmodule

    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

    1. //
    2. // File .......... axi_register_bank.v
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 29 October 2022
    6. // Standard ...... IEEE 1364-2001
    7. // Description ...
    8. //   AXI4-Lite controlled general purpose register bank written in basic
    9. // Verilog. This module is intended to be a building block for something more
    10. // useful.
    11. //
    12. // With c_data_w = 32 the number of registers is as follows :-
    13. //
    14. // c_addr_w - c_registers
    15. // ~~~~~~~~ - ~~~~~~~~~~~
    16. // 2        -   1
    17. // 3        -   2
    18. // 4        -   4
    19. // 5        -   8
    20. // 6        -  16
    21. // 7        -  32
    22. // 8        -  64
    23. // 9        - 128
    24. // 10       - 256
    25. //


    26. `timescale 1 ns / 1 ps


    27. module axi_register_bank #
    28. (
    29.   // Parameters
    30.   parameter  c_addr_w = 4,                                // P:Address Bus Width (bits)
    31.   localparam c_data_w = 32                                // P:Data Bus Width (bits)
    32. )
    33. (
    34.   // Clock & Reset
    35.   input  wire                             aclk,           // I:Clock
    36.   input  wire                             aresetn,        // I:Reset
    37.   // AXI4-Lite - Write Address Channel
    38.   input  wire [c_addr_w-1 : 0]            s_axi_awaddr,   // I:Write Address
    39.   input  wire [2 : 0]                     s_axi_awprot,   // I:Write Protection Type
    40.   input  wire                             s_axi_awvalid,  // I:Write Address Valid
    41.   output reg                              s_axi_awready,  // O:Write Address Ready
    42.   // AXI4-Lite - Write Data Channel
    43.   input  wire [c_data_w-1 : 0]            s_axi_wdata,    // I:Write Data
    44.   input  wire [(c_data_w/8)-1 : 0]        s_axi_wstrb,    // I:Write Strobes
    45.   input  wire                             s_axi_wvalid,   // I:Write Valid
    46.   output reg                              s_axi_wready,   // O:Write Ready
    47.   // AXI4-Lite - Write Response Channel
    48.   output reg  [1 : 0]                     s_axi_bresp,    // O:Write Response
    49.   output reg                              s_axi_bvalid,   // O:Write Response Valid
    50.   input  wire                             s_axi_bready,   // I:Write Response Ready
    51.   // AXI4-Lite - Read Address Channel
    52.   input  wire [c_addr_w-1 : 0]            s_axi_araddr,   // I:Read Address
    53.   input  wire [2 : 0]                     s_axi_arprot,   // I:Read Protection Type
    54.   input  wire                             s_axi_arvalid,  // I:Read Address Valid
    55.   output reg                              s_axi_arready,  // O:Read Address Ready
    56.   // AXI4-Lite - Read Data Channel
    57.   output reg  [c_data_w-1 : 0]            s_axi_rdata,    // O:Read Data
    58.   output reg  [1 : 0]                     s_axi_rresp,    // O:Read Response
    59.   output reg                              s_axi_rvalid,   // O:Read Valid
    60.   input  wire                             s_axi_rready    // I:Read Ready
    61. );


    62.   // Constants
    63.   localparam c_bytes = c_data_w / 8;                         // Number of bytes in data bus
    64.   localparam c_registers = 2**(c_addr_w - $clog2(c_bytes));  // Number of registers

    65.   // Signals
    66.   reg aw_en;
    67.   reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_awaddr;
    68.   reg [c_addr_w-$clog2(c_bytes) - 1 : 0] axi_araddr;
    69.   reg [c_data_w-1 : 0] registers [c_registers - 1 : 0];


    70.   // Write Address Ready
    71.   always @(posedge aclk)
    72.     if (!aresetn) begin
    73.       s_axi_awready <= 1'b0;
    74.       aw_en <= 1'b1;
    75.     end else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en) begin
    76.       s_axi_awready <= 1'b1;
    77.       aw_en <= 1'b0;
    78.     end else if (s_axi_bvalid && s_axi_bready) begin
    79.       s_axi_awready <= 1'b0;
    80.       aw_en <= 1'b1;
    81.     end else
    82.       s_axi_awready <= 1'b0;


    83.   // Write Address Latch
    84.   always @(posedge aclk)
    85.     if (!aresetn)
    86.       axi_awaddr <= {c_addr_w{1'b0}};
    87.     else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en)
    88.       axi_awaddr <= s_axi_awaddr[c_addr_w - 1 : $clog2(c_bytes)];


    89.   // Write Data Ready
    90.   always @(posedge aclk)
    91.     if (!aresetn)
    92.       s_axi_wready <= 1'b0;
    93.     else if (s_axi_awvalid && s_axi_wvalid && !s_axi_wready && aw_en)
    94.       s_axi_wready <= 1'b1;
    95.     else
    96.       s_axi_wready <= 1'b0;


    97.   // Write Data
    98.   integer i;
    99.   always @(posedge aclk)
    100.     if (!aresetn)
    101.       for (i = 0; i <= c_registers - 1; i = i + 1)
    102.         registers[i] <= {c_data_w{1'b0}};
    103.     else begin
    104.       if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready)
    105.         for (i = 0; i <= c_bytes - 1; i = i + 1)
    106.           if (s_axi_wstrb[i])
    107.             registers[axi_awaddr][i*8 +: 8] <= s_axi_wdata[i*8 +: 8]; // Byte-wise write
    108.     end


    109.   // Write Response
    110.   always @(posedge aclk)
    111.     if (!aresetn) begin
    112.       s_axi_bvalid <= 1'b0;
    113.       s_axi_bresp  <= 2'b00;
    114.     end else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready && !s_axi_bvalid) begin
    115.       s_axi_bvalid <= 1'b1;
    116.       s_axi_bresp  <= 2'b00;
    117.     end else if (s_axi_bvalid && s_axi_bready)
    118.       s_axi_bvalid <= 1'b0;


    119.   // Read Address Ready
    120.   always @(posedge aclk)
    121.     if (!aresetn)
    122.       s_axi_arready <= 1'b0;
    123.     else if (s_axi_arvalid && !s_axi_arready)
    124.       s_axi_arready <= 1'b1;
    125.     else
    126.       s_axi_arready <= 1'b0;


    127.   // Read Address Latch
    128.   always @(posedge aclk)
    129.     if (!aresetn)
    130.       axi_araddr  <= {c_addr_w{1'b0}};
    131.     else if (s_axi_arvalid && !s_axi_arready)
    132.       axi_araddr <= s_axi_araddr[c_addr_w - 1 : $clog2(c_bytes)];


    133.   // Read Response
    134.   always @(posedge aclk)
    135.     if (!aresetn) begin
    136.       s_axi_rvalid <= 1'b0;
    137.       s_axi_rresp  <= 2'b00;
    138.     end else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
    139.       s_axi_rvalid <= 1'b1;
    140.       s_axi_rresp  <= 2'b00;
    141.     end else if (s_axi_rvalid && s_axi_rready)
    142.       s_axi_rvalid <= 1'b0;


    143.   // Read Data
    144.   always @(posedge aclk)
    145.     if (!aresetn)
    146.       s_axi_rdata <= {c_data_w{1'b0}};
    147.     else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid)
    148.       s_axi_rdata <= registers[axi_araddr];


    149. endmodule

    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

    1. //
    2. // File .......... system_wrapper.sv
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.2
    5. // Date .......... 29 October 2022
    6. // Standard ...... IEEE 1800-2017
    7. // Description ...
    8. //   Top level wrapper for the underlying block design.
    9. //


    10. timeunit      1ns;
    11. timeprecision 1ps;


    12. module system_wrapper #
    13. (
    14.   // Parameters
    15.   parameter string id_description = "",  // P:Description ............ Max 128 Characters
    16.   parameter string id_company     = "",  // P:Company ................ Max  64 Characters
    17.   parameter string id_author      = "",  // P:Author ................. Max  64 Characters
    18.   parameter string id_version     = "",  // P:Version ................ Max  32 Characters
    19.   parameter string id_timestamp   = "",  // P:Timestamp .............. Max  32 Characters
    20.   parameter string id_hash        = ""   // P:Hash ................... Max  64 Characters
    21. )
    22. (
    23.   // LEDs
    24.   output [ 7:0] leds,               // O:LEDs
    25.   // DIP Switches
    26.   input  [ 7:0] switches,           // I:DIP Switches
    27.   // Push Buttons
    28.   input  [ 4:0] buttons,            // I:Push Buttons
    29.   // System
    30.   inout  [14:0] DDR_addr,           // B:Address
    31.   inout  [ 2:0] DDR_ba,             // B:Bank Address
    32.   inout         DDR_cas_n,          // B:Column Address Select
    33.   inout         DDR_ck_n,           // B:Clock (Neg)
    34.   inout         DDR_ck_p,           // B:Clock (Pos)
    35.   inout         DDR_cke,            // B:Clock Enable
    36.   inout         DDR_cs_n,           // B:Chip Select
    37.   inout  [ 3:0] DDR_dm,             // B:Data Mask
    38.   inout  [31:0] DDR_dq,             // B:Data Input/Output
    39.   inout  [ 3:0] DDR_dqs_n,          // B:Data Strobe (Neg)
    40.   inout  [ 3:0] DDR_dqs_p,          // B:Data Strobe (Pos)
    41.   inout         DDR_odt,            // B:Output Dynamic Termination
    42.   inout         DDR_ras_n,          // B:Row Address Select
    43.   inout         DDR_reset_n,        // B:Reset
    44.   inout         DDR_we_n,           // B:Write Enable
    45.   inout         FIXED_IO_ddr_vrn,   // B:Termination Voltage
    46.   inout         FIXED_IO_ddr_vrp,   // B:Termination Voltage
    47.   inout  [53:0] FIXED_IO_mio,       // B:Peripheral Input/Output
    48.   inout         FIXED_IO_ps_clk,    // B:System Reference Clock
    49.   inout         FIXED_IO_ps_porb,   // B:Power On Reset
    50.   inout         FIXED_IO_ps_srstb   // B:External System Reset
    51. );


    52.   // Function to convert a string to a vector (max 128 characters)
    53.   function [1023:0] fmt(input string str);
    54.     int len;
    55.     bit [1023:0] tmp;
    56.     len = str.len();
    57.     for (int i=0; i<len; i++)
    58.       tmp[8*i +: 8] = str.getc(i);
    59.     fmt = tmp;
    60.   endfunction


    61.   // Top-Level Block Design
    62.   system system_i
    63.    (
    64.     // Identification Strings
    65.     .id_description    ( fmt(id_description) ),  // P:Description
    66.     .id_company        ( fmt(id_company)     ),  // P:Company
    67.     .id_author         ( fmt(id_author)      ),  // P:Author
    68.     .id_version        ( fmt(id_version)     ),  // P:Version
    69.     .id_timestamp      ( fmt(id_timestamp)   ),  // P:Timestamp
    70.     .id_hash           ( fmt(id_hash)        ),  // P:Hash
    71.     // LEDs
    72.     .leds              ( leds                ),  // O:LEDs
    73.     // DIP Switches
    74.     .switches          ( switches            ),  // I:DIP Switches
    75.     // Push Buttons
    76.     .buttons           ( buttons             ),  // I:Push Buttons
    77.     // System
    78.     .DDR_addr          ( DDR_addr            ),  // B:Address
    79.     .DDR_ba            ( DDR_ba              ),  // B:Bank Address
    80.     .DDR_cas_n         ( DDR_cas_n           ),  // B:Column Address Select
    81.     .DDR_ck_n          ( DDR_ck_n            ),  // B:Clock (Neg)
    82.     .DDR_ck_p          ( DDR_ck_p            ),  // B:Clock (Pos)
    83.     .DDR_cke           ( DDR_cke             ),  // B:Clock Enable
    84.     .DDR_cs_n          ( DDR_cs_n            ),  // B:Chip Select
    85.     .DDR_dm            ( DDR_dm              ),  // B:Data Mask
    86.     .DDR_dq            ( DDR_dq              ),  // B:Data Input/Output
    87.     .DDR_dqs_n         ( DDR_dqs_n           ),  // B:Data Strobe (Neg)
    88.     .DDR_dqs_p         ( DDR_dqs_p           ),  // B:Data Strobe (Pos)
    89.     .DDR_odt           ( DDR_odt             ),  // B:Output Dynamic Termination
    90.     .DDR_ras_n         ( DDR_ras_n           ),  // B:Row Address Select
    91.     .DDR_reset_n       ( DDR_reset_n         ),  // B:Reset
    92.     .DDR_we_n          ( DDR_we_n            ),  // B:Write Enable
    93.     .FIXED_IO_ddr_vrn  ( FIXED_IO_ddr_vrn    ),  // B:Termination Voltage
    94.     .FIXED_IO_ddr_vrp  ( FIXED_IO_ddr_vrp    ),  // B:Termination Voltage
    95.     .FIXED_IO_mio      ( FIXED_IO_mio        ),  // B:Peripheral Input/Output
    96.     .FIXED_IO_ps_clk   ( FIXED_IO_ps_clk     ),  // B:System Reference Clock
    97.     .FIXED_IO_ps_porb  ( FIXED_IO_ps_porb    ),  // B:Power On Reset
    98.     .FIXED_IO_ps_srstb ( FIXED_IO_ps_srstb   )   // B:External System Reset
    99.    );


    100. endmodule


    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. 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[0]}];  # "LD0"
    3. set_property PACKAGE_PIN T21 [get_ports {leds[1]}];  # "LD1"
    4. set_property PACKAGE_PIN U22 [get_ports {leds[2]}];  # "LD2"
    5. set_property PACKAGE_PIN U21 [get_ports {leds[3]}];  # "LD3"
    6. set_property PACKAGE_PIN V22 [get_ports {leds[4]}];  # "LD4"
    7. set_property PACKAGE_PIN W22 [get_ports {leds[5]}];  # "LD5"
    8. set_property PACKAGE_PIN U19 [get_ports {leds[6]}];  # "LD6"
    9. set_property PACKAGE_PIN U14 [get_ports {leds[7]}];  # "LD7"


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


    16. # User DIP Switches - Bank 34 & 35
    17. set_property PACKAGE_PIN F22 [get_ports {switches[0]}];  # "SW0"
    18. set_property PACKAGE_PIN G22 [get_ports {switches[1]}];  # "SW1"
    19. set_property PACKAGE_PIN H22 [get_ports {switches[2]}];  # "SW2"
    20. set_property PACKAGE_PIN F21 [get_ports {switches[3]}];  # "SW3"
    21. set_property PACKAGE_PIN H19 [get_ports {switches[4]}];  # "SW4"
    22. set_property PACKAGE_PIN H18 [get_ports {switches[5]}];  # "SW5"
    23. set_property PACKAGE_PIN H17 [get_ports {switches[6]}];  # "SW6"
    24. set_property PACKAGE_PIN M15 [get_ports {switches[7]}];  # "SW7"


    25. # Banks
    26. set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 33]];
    27. set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 34]];
    28. set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 35]];

    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 -am "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/os/petalinux

    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/os/petalinux$ sed -i 's/10.0/11.0/g' 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/os/petalinux$ sed -i 's/0x4120/0x4001/g' project-spec/meta-user/recipes-apps/led-runner/files/led-runner

    25. Add Hardware Platform

    Configure the PetaLinux project to use the newly exported hardware platform from the zedboard_leds_switches Vivado project.
    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$ subl components/plnx_workspace/device-tree/device-tree/pl.dtsi

    pl.dtsi

    1. /*
    2. * CAUTION: This file is automatically generated by Xilinx.
    3. * Version:  
    4. * Today is: Tue Aug  8 08:31:05 2023
    5. */


    6. / {
    7.   amba_pl: amba_pl {
    8.     #address-cells = <1>;
    9.     #size-cells = <1>;
    10.     compatible = "simple-bus";
    11.     ranges ;
    12.     axi_gpio_zed_0: axi_gpio_zed@40010000 {
    13.       clock-names = "aclk";
    14.       clocks = <&clkc 15>;
    15.       compatible = "xlnx,axi-gpio-zed-1.0";
    16.       interrupt-names = "interrupt";
    17.       interrupt-parent = <&intc>;
    18.       interrupts = <0 29 4>;
    19.       reg = <0x40010000 0x10000>;
    20.     };
    21.     axi_identification_0: axi_identification@40000000 {
    22.       clock-names = "aclk";
    23.       clocks = <&clkc 15>;
    24.       compatible = "xlnx,axi-identification-1.0";
    25.       reg = <0x40000000 0x10000>;
    26.     };
    27.     axi_register_bank_0: axi_register_bank@40020000 {
    28.       clock-names = "aclk";
    29.       clocks = <&clkc 15>;
    30.       compatible = "xlnx,axi-register-bank-1.0";
    31.       reg = <0x40020000 0x10000>;
    32.     };
    33.   };
    34. };

    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/os/petalinux$ subl project-spec/meta-user/recipes-bsp/device-tree/files/{pl-custom.dtsi,system-user.dtsi}

    pl-custom.dtsi

    1. /*Add pl custom nodes for pl.dtsi which is generated from base xsa file.
    2. Changes in this file reflects only when enabled the FPGA manager/Device tree overlay.*/
    3. / {
    4. };

    system-user.dtsi

    1. /include/ "system-conf.dtsi"
    2. / {
    3. };

    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.

    system-user.dtsi

    1. /include/ "system-conf.dtsi"

    2. / {
    3.     chosen
    4.     {
    5.       bootargs = "uio_pdrv_genirq.of_id=generic-uio";
    6.     };
    7. };

    8. &axi_gpio_zed_0
    9. {
    10.   #interrupt-cells = <2>;
    11.   interrupt-controller;
    12.   interrupts = <0 29 1>;
    13.   compatible = "generic-uio";
    14. };

    28. Build & package PetaLinux

    Rebuild PetaLinux to include the updates and package it ready for deployment.
    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

    Deploy PetaLinux to the Zedboard via JTAG.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-boot --jtag --prebuilt 3

    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.

    axi_gpio_zed.txt

    1. sec|AXI General Purpose IO - Zedboard Specific
    2. reg|0x40010000|false|0x00000018|true|LEDs
    3. reg|0x40010008|true|0x00000000|false|DIP Switches
    4. reg|0x40010010|true|0x00000000|true|Push Buttons
    5. reg|0x4001011C|false|0x80000000|true|Global Interrupt Enable
    6. reg|0x40010128|false|0x00000004|true|Interrupt Enable
    7. reg|0x40010120|true|0x00000004|true|Interrupt Status

    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/os/petalinux$ subl ../src/other/zedboard_leds_switches.txt

    zedboard_leds_switches.txt

    1. sec|AXI GPIO
    2. reg|0x40010000|true|0x00000018|true|LEDs
    3. reg|0x40010008|true|0x00000000|false|DIP Switches
    4. reg|0x40010010|true|0x00000000|true|Push Buttons
    5. sec|AXI Register Bank
    6. reg|0x40020000|true|0x00000000|true|Register 0
    7. reg|0x40020004|true|0x456789AB|true|Register 1
    8. reg|0x40020008|true|0x00000000|true|Register 2
    9. reg|0x4002000C|true|0x0000FF00|true|Register 3

    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_leds_switches/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_leds_switches/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.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/axi-gpio-zed-test/files/axi-gpio-zed-test.c
    Something similar to the following should do the trick :-

    axi-gpio-zed-test.c

    1. //
    2. // File .......... axi-gpio-zed-test.c
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 29 October 2022
    6. // Description ...
    7. //   Very simple program to access a GPIO device using the User Space I/O
    8. // subsystem. Demonstrates a blocking wait on interrupt routine.
    9. //

    10. #include <stdio.h>
    11. #include <stdlib.h>
    12. #include <unistd.h>
    13. #include <sys/mman.h>
    14. #include <fcntl.h>

    15. #define XGPIO_DATA_OFFSET           0x0
    16. #define XGPIO_TRI_OFFSET            0x4
    17. #define XGPIO_DATA2_OFFSET          0x8
    18. #define XGPIO_TRI2_OFFSET           0xC
    19. #define XGPIO_DATA3_OFFSET          0x10
    20. #define XGPIO_TRI3_OFFSET           0x14
    21. #define XGPIO_GIE_OFFSET            0x11C
    22. #define XGPIO_ISR_OFFSET            0x120
    23. #define XGPIO_IER_OFFSET            0x128
    24. #define XGPIO_IR_MASK               0x7
    25. #define XGPIO_IR_CH1_MASK           0x1
    26. #define XGPIO_IR_CH2_MASK           0x2
    27. #define XGPIO_IR_CH3_MASK           0x4
    28. #define XGPIO_GIE_GINTR_ENABLE_MASK 0x80000000

    29. #define BUTTON_CENTER 0x01
    30. #define BUTTON_DOWN   0x02
    31. #define BUTTON_LEFT   0x04
    32. #define BUTTON_RIGHT  0x08
    33. #define BUTTON_UP     0x10


    34. // Read a value from a GPIO register.
    35. unsigned int gpio_read(void *base, unsigned int offset)
    36. {
    37.   return *((volatile unsigned int *)(base + offset));
    38. }


    39. // Write a value to a GPIO register.
    40. void gpio_write(void *base, unsigned int offset, unsigned int data)
    41. {
    42.   *((volatile unsigned int *)(base + offset)) = data;
    43. }


    44. // Rotate right
    45. unsigned char ror(unsigned char num) {
    46.   return (num >> 1) | (num << 7);
    47. }


    48. // Rotate left
    49. unsigned char rol(unsigned char num) {
    50.   return (num << 1) | (num >> 7);
    51. }


    52. // GPIO button decode & LED driver
    53. void buttons_to_leds(void *base)
    54. {
    55.   unsigned int leds;
    56.   unsigned int btns;
    57.   unsigned int width = 0;

    58.   // Obtain register values for LED's & buttons
    59.   leds = gpio_read(base, XGPIO_DATA_OFFSET);
    60.   btns = gpio_read(base, XGPIO_DATA3_OFFSET);

    61.   // Print button status
    62.   printf("Interrupt detected, button register = 0x%08x\n", btns);

    63.   // Determine LED bar width
    64.   for (unsigned int i=0; i<=7; i++) {
    65.     if ((0b00000001 << i) & leds) {
    66.       width++;
    67.     }
    68.   }

    69.     // Set LED bar width to 4
    70.     if (btns & BUTTON_CENTER) {
    71.       leds = 0b00111100;
    72.     }

    73.     // Rotate LED's left
    74.     if (btns & BUTTON_LEFT) {
    75.       if (width == 0) {
    76.         leds = 0b00000001;
    77.       } else {
    78.         leds = (unsigned char)rol(leds);
    79.       }
    80.     }

    81.     // Rotate LED's right
    82.     if (btns & BUTTON_RIGHT) {
    83.       if (width == 0) {
    84.         leds = 0b10000000;
    85.       } else {
    86.         leds = (unsigned char)ror(leds);
    87.       }
    88.     }

    89.     // Increase illuminated LED's
    90.     if (btns & BUTTON_UP) {
    91.       if (width == 0) {
    92.         leds = 0b00001000;
    93.       } else if (width & 1) {
    94.         leds = leds | rol(leds);
    95.       } else {
    96.         leds = leds | ror(leds);
    97.       }
    98.     }

    99.     // Decrease illuminated LED's
    100.     if (btns & BUTTON_DOWN) {
    101.       if (width == 8) {
    102.         leds = 0b01111111;
    103.       } else if (width & 1) {
    104.         leds = leds & rol(leds);
    105.       } else {
    106.         leds = leds & ror(leds);
    107.       }
    108.     }

    109.   // Clear (all latched) Buttons
    110.   gpio_write(base, XGPIO_DATA3_OFFSET, 0x0);

    111.   // Update LED's
    112.   gpio_write(base, XGPIO_DATA_OFFSET, leds);
    113. }


    114. // Wait for an interrupt from GPIO device
    115. void wait_interrupt(int fd, void *base)
    116. {
    117.   unsigned int pending = 0;
    118.   unsigned int reenable = 1;
    119.   unsigned int data;

    120.   // Wait for User Space interrupt (blocking read)
    121.   read(fd, (void *)&pending, sizeof(pending));

    122.   // Read GPIO Interrupt Status Register
    123.   data = gpio_read(base, XGPIO_ISR_OFFSET);

    124.   // Check for interrupt on GPIO Channel 3 & clear if present
    125.   if (data == XGPIO_IR_CH3_MASK)
    126.     gpio_write(base, XGPIO_ISR_OFFSET, XGPIO_IR_CH3_MASK);

    127.   // Decode buttons & drive LEDs
    128.   buttons_to_leds(base);

    129.   // Re-enable User Space interrupt
    130.   write(fd, (void *)&reenable, sizeof(reenable));
    131. }


    132. // Get device driver memory size
    133. unsigned int get_device_size(char *filename)
    134. {
    135.   FILE *fp;
    136.   unsigned int size;

    137.   // Open ASCII file
    138.   fp = fopen(filename, "r");
    139.   if (!fp) {
    140.     printf("Error: Failed to open %s file\n", filename);
    141.     exit(EXIT_FAILURE);
    142.   }

    143.   // Convert hexadecimal size string into a number
    144.   fscanf(fp, "0x%08X", &size);

    145.   // Close ASCII file
    146.   fclose(fp);

    147.   // Return device size
    148.   return size;
    149. }


    150. // Main function
    151. int main(int argc, char *argv[])
    152. {
    153.   char *devicename = "/dev/uio0";
    154.   char *filename = "/sys/class/uio/uio0/maps/map0/size";
    155.   int fd;
    156.   unsigned int devicesize;
    157.   void *deviceptr;

    158.   // Open GPIO device with read/write access
    159.   fd = open(devicename, O_RDWR);
    160.   if (fd < 1) {
    161.     printf("Error: Failed to open %s device\n", devicename);
    162.     exit(EXIT_FAILURE);
    163.   }

    164.   // Get GPIO device memory size
    165.   devicesize = get_device_size(filename);

    166.   // Map GPIO device memory region
    167.   deviceptr = mmap(NULL, devicesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    168.   if (deviceptr == MAP_FAILED) {
    169.     printf("Error: Failed to mmap");
    170.     exit(EXIT_FAILURE);
    171.   }

    172.   // Print header
    173.   printf("Simple User Space device driver example with interrupts\n\n");
    174.   printf("Device in use ... %s\n", devicename);
    175.   printf("Memory size ..... %u (obtained from %s)\n\n", devicesize, filename);
    176.   printf("Use the buttons to control the LED's :-\n\n");
    177.   printf("Centre - Reset LED's to 00111100\n");
    178.   printf("Left   - Rotate LED's left\n");
    179.   printf("Right  - Rotate LED's right\n");
    180.   printf("Up     - Increase illuminated LED's\n");
    181.   printf("Down   - Decrease illuminated LED's\n\n");
    182.   printf("Press Ctrl-C to quit application\n");

    183.   // Enable Interrupts for GPIO Channel 3
    184.   gpio_write(deviceptr, XGPIO_GIE_OFFSET, XGPIO_GIE_GINTR_ENABLE_MASK);
    185.   gpio_write(deviceptr, XGPIO_IER_OFFSET, XGPIO_IR_CH3_MASK);

    186.   // Wait for Interrupt
    187.   while (1) {
    188.     wait_interrupt(fd, deviceptr);
    189.   }

    190.   // Disable Interrupts for GPIO Channel 3
    191.   gpio_write(deviceptr, XGPIO_GIE_OFFSET, 0x0);
    192.   gpio_write(deviceptr, XGPIO_IER_OFFSET, 0x0);

    193.   // Unmap GPIO device memory
    194.   munmap(deviceptr, devicesize);

    195.   // Close GPIO device
    196.   close(fd);

    197.   return EXIT_SUCCESS;
    198. }

    37. Cross compile application

    Cross compile the C code targetting the ARM processor.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ arm-linux-gnueabihf-gcc 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/os/petalinux$ 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/os/petalinux$ cd ../..
    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 -am "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!