Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 22 - Repository restructure

Introduction

This tutorial details the steps required to enhance the newly created repository from the last tutorial. The aim being to lay a better foundation for the next few projects.

Requirements :-

Aims

The aims of this tutorial are as follows :-

    Part 1 - Setup environment

    1. Pre-requisites

    Part 2 - Shell initialisation enhancement

    1. Create SpaceWire UK Tutorial shell initialisation script

    Part 3 - Remove superfluous files

    1. Remove 2021.2 Xilinx tool selection script
    2. Remove superfluous configuration file
    3. Remove superfluous system generated files
    4. Remove Zedboard master XDC constraints file
    5. Remove Zedboard presets script

    Part 4 - Introduce new shared source files

    1. Create a simple metastability hardener module
    2. Create a simple heartbeat module

    Part 5 - Relocate shared source files

    1. Relocate Pre-Synthesis TCL script
    2. Relocate AXI GPIO Zed source module
    3. Relocate AXI Identification source module
    4. Relocate AXI Register Bank souce module
    5. Relocate Debounce source module

    Part 6 - Improve the .gitignore system

    1. Change details for .gitignore

    Part 7 - Restructure Zedboard LEDs & Buttons project

    1. Rename & update testbench
    2. Rename & update constraints

    Part 8 - Restructure Zedboard LEDs & Switches project

    1. Rename & update top-level
    2. Rename & enhance testbench
    3. Rename & update constraints
    4. Update software

    Part 9 - Archive build files for easy retrieval

    1. Setup local directory
    2. Obtain Hardware Platform (handoff) archives
    3. Obtain Bitstream (firmware) files
    4. Obtain Binary (firmware) files
    5. Download the PetaLinux (boot & OS) files

    Part 10 - Create system (baseline) block design

    1. Create system (baseline) block design TCL script

    Part 11 - Rename & enhance Xilinx tool selection script

    1. Rename & enhance the script

    Part 12 - Rename & enhance create project structure script

    1. Rename & enhance the script

    Part 13 - Rename & update create Vivado project scripts

    1. Rename & update create Vivado project Bash script
    2. Rename & update create vivado project TCL script
    3. Check everything is working as expected

    Part 14 - Desktop & PetaLinux communication & transfers (Part A)

    1. Create script for JTAG debug with the Zedboard
    2. Establish SSH link between Desktop & PetaLinux
    3. Upload BIN (firmware) files to Zedboard's SD-Card

    Part 15 - Rename & update create Vitis project scripts

    1. Rename & update create Vitis project Bash script
    2. Rename & update create Vitis project TCL script
    3. Check everything is working as expected

    Part 16 - Rename & update PetaLinux build script

    1. Rename & enhance the script
    2. Check everything is working as expected

    Part 17 - Desktop & PetaLinux communication & transfers (Part B)

    1. Upload BOOT (OS) files to Zedboard's SD-Card

    Part 18 - Check aliases & scripts

    1. Check that swuk_ aliases & scripts are readily available

    Part 19 - Final checks

    1. Check everything is working as expected

    Part 20 - Revision Control

    1. Commit new & updated files
    #### Part 1 - Setup environment ####

    1. Pre-requisites

    The newly spliced repository from the last tutorial will now be used going forward. The initial setup for this is very simple as things will further evolve throughout this tutorial.
    #### Part 2 - Shell initialisation enhancement ####

    2. Create SpaceWire UK Tutorial shell initialisation script

    Improve the shell initialisation setup such that it is controlled & maintained from within the repository. Only the bare minimum should be placed inside the Bash shell initialisation script.

    Create a specific SpaceWire UK Tutorial shell initialisation script.
    steve@Desktop:~/swuk_tutorial$ subl common/other/src/script/swuk_sis
    

    swuk_sis

    #!/bin/bash
    
    #
    # File .......... swuk_sis
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 16 Jun 2025
    # Version ....... 1.0
    # Description ...
    #   Additional shell initialisation script for use with building SWUK projects.
    #
    
    # Get script location
    whereami=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
    
    # Locations
    export swuk_tutorial=$(sed 's_\(.*\)\(/.*\)\{4\}$_\1_' <<< $whereami)
    export swuk_user="$HOME/Documents/swuk_tutorial"
    
    # Paths
    PATH="$swuk_tutorial/common/fw/src/script:"$PATH
    PATH="$swuk_tutorial/common/os/src/script:"$PATH
    PATH="$swuk_tutorial/common/other/src/script:"$PATH
    PATH="$swuk_tutorial/common/sw/src/script:"$PATH
    
    # Aliases
    alias swuk_xilinx="source $(which swuk_xilinx)"
    
    # Variables
    export swuk_zedboard_ip='192.168.2.87'
    
    # Setup Xilinx tools for 2021.2 Vivado, SDK, Vitis & 2021.2 PetaLinux
    swuk_xilinx 2021.2 2021.2 > /dev/null 2>&1
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/other/src/script/swuk_sis -O common/other/src/script/swuk_sis
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/other/src/script/swuk_sis
    
    Call the SpaceWire UK Tutorial SIS from within the Bash SIS. Edit the path as required.
    steve@Desktop:~/swuk_tutorial$ echo -e '\n# SpaceWire UK Tutorial' >> ~/.bashrc
    steve@Desktop:~/swuk_tutorial$ echo 'source $HOME/swuk_tutorial/common/other/src/script/swuk_sis' >> ~/.bashrc
    
    Source the Bash SIS to kick everything into life.
    steve@Desktop:~/swuk_tutorial$ source ~/.bashrc
    
    Check everything working as expected.
    steve@Desktop:~/swuk_tutorial$ echo ${swuk_tutorial} ${swuk_user}
    ~/swuk_tutorial /home/steve/Documents/swuk_tutorial
    
    Looks good!
    #### Part 3 - Remove superfluous files ####

    3. Remove 2021.2 Xilinx tool selection script

    Remove the superfluous source file.
    steve@Desktop:~/swuk_tutorial$ git rm common/other/src/script/xilinx_2021_2.sh
    

    4. Remove superfluous configuration file

    Relocate misplaced configuration file & remove it from the repository.
    steve@Desktop:~/swuk_tutorial$ mkdir ${swuk_user}/configuration
    steve@Desktop:~/swuk_tutorial$ cp zedboard_linux/os/src/other/zedboard_leds_switches.txt ${swuk_user}/configuration
    
    Remove the superfluous source file.
    steve@Desktop:~/swuk_tutorial$ git rm zedboard_linux/os/src/other/zedboard_leds_switches.txt
    

    5. Remove superfluous system generated files

    Remove the superfluous source file.
    steve@Desktop:~/swuk_tutorial$ git rm zedboard_hello_world/fw/system_wrapper.xsa
    
    Remove the superfluous source file.
    steve@Desktop:~/swuk_tutorial$ git rm zedboard_leds_buttons/fw/system_wrapper.xsa
    

    6. Remove Zedboard master XDC constraints file

    Remove the superfluous source file.
    steve@Desktop:~/swuk_tutorial$ git rm common/fw/src/constraint/zedboard_master_XDC_RevC_D_v3.xdc
    

    7. Remove Zedboard presets script

    Remove the superfluous source file.
    steve@Desktop:~/swuk_tutorial$ git rm common/fw/src/script/zedboard_presets.tcl
    
    #### Part 4 - Introduce new shared source files ####

    8. Create a simple metastability hardener module

    Create missing directory.
    steve@Desktop:~/swuk_tutorial$ mkdir common/fw/src/design
    
    Create a new module to deal with clock domain crossing.
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/design/meta_hardener.v
    

    meta_hardener.v

    //
    // File .......... meta_hardener.v
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Common (SpaceWire UK Tutorial)
    // Date .......... 10 Jun 2025
    // Version ....... 1.0
    // Description ...
    //   Metastability hardener.
    //
    
    
    `timescale 1 ns / 1 ps
    
    
    module meta_hardener #
    (
      // Parameters
      parameter  c_reset   = 1'b0,  // Reset polarity (1'b0 or 1'b1)
      parameter  c_default = 1'b0   // Flip-flop defaults (1'b0 or 1'b1)
    )
    (
      // Clock & Reset
      input  clk,                   // Clock
      input  rst,                   // Reset
      // Input & Output
      input  async,                 // Asynchronous input
      output sync                   // Synchronous output
    );
    
    
      // Signals
      reg meta = c_default;
      reg outp = c_default;
    
    
      // Metastability hardener
      always @(posedge clk)
        if (rst == c_reset) begin
          meta <= c_default;
          outp <= c_default;
        end else begin
          meta <= async;
          outp <= meta;
        end
    
      // Output
      assign sync = outp;
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/design/meta_hardener.v -O common/fw/src/design/meta_hardener.v
    

    9. Create a simple heartbeat module

    Create a new module to provide a simple heartbeat pulse from a clock source.
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/design/heartbeat.v
    

    heartbeat.v

    //
    // File .......... heartbeat.v
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Common (SpaceWire UK Tutorial)
    // Date .......... 10 Jun 2025
    // Version ....... 1.0
    // Description ...
    //   Very simple clock to beat.
    //
    
    
    `timescale 1 ns / 1 ps
    
    
    module heartbeat #
    (
      // Parameters
      parameter c_clk_freq = 100_000_000,  // P:Clock Frequency (Hz)
      parameter c_beat_freq = 1            // P:Beat Frequency (Hz)
    )
    (
      // Clock & Reset
      input  wire aclk,     // I:Clock
      input  wire aresetn,  // I:Reset
      // Heartbeat
      output reg  beat      // O:Beat
    );
    
    
      // Constants
      localparam c_terminal = c_clk_freq / 2 * c_beat_freq - 1;  // Terminal count value
      localparam c_counter_w = $clog2(c_terminal);               // Counter Width
    
      // Signals
      reg [c_counter_w-1 : 0] counter; // Counter
    
    
      // Counter
      always @(posedge aclk)
        if (!aresetn) begin
          counter <= {c_counter_w{1'b0}};
          beat    <= 1'b0;
        end else if (counter == c_terminal) begin
          counter <= {c_counter_w{1'b0}};
          beat    <= !beat;
        end else
          counter <= counter + 1;
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/design/heartbeat.v -O common/fw/src/design/heartbeat.v
    
    #### Part 5 - Relocate shared source files ####

    10. Relocate Pre-Synthesis TCL script

    Move shared source file into common area to avoid duplication later on.

    Requirements :-
    1. Rename & relocate the source file.
    2. Update header to reflect field changes - File, Project, Version & History.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/script/pre_synth.tcl common/fw/src/script/swuk_pre_synth.tcl
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_pre_synth.tcl
    

    swuk_pre_synth.tcl

    #
    # File .......... swuk_pre_synth.tcl
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 3.0
    # History .......
    #   2.0 zedboard_leds_switches/fw/src/script/pre_synth.tcl
    # Description ...
    #   Script to read product/project information from a text file, obtain GIT
    # repository information and set the generics/parameters on the top level
    # module.
    #
    
    # Read Product/Project Information
    if { [catch {set id_file [open "../../../project.txt" r]} msg] } {
      set id_description "?"
      set id_company "?"
      set id_author "?"
      set id_version "?"
      puts "ERROR :-"
      puts $msg
    } {
      gets $id_file id_description
      gets $id_file id_company
      gets $id_file id_author
      gets $id_file id_version
      close $id_file
    }
    
    # Get GIT timestamp
    set dfmt "%d-%b-%Y - %H:%M:%S"
    if { [catch {set id_timestamp [exec git log -1 --pretty=%cd --date=format:$dfmt]} msg] } {
      set id_timestamp "00-Xxx-0000 - 00:00:00"
      puts "ERROR :-"
      puts $msg
    }
    
    # Get GIT hash
    if { [catch {set id_hash [exec git log -1 --pretty=%H]} msg] } {
      set id_hash "0000000000000000000000000000000000000000"
      puts "ERROR :-"
      puts $msg
    }
    
    # Get GIT status
    if { [catch {set status [exec git status -s]} msg] } {
      append id_version " (undefined)"
      puts "ERROR :-"
      puts $msg
    } {
      if {$status ne ""} {
        append id_version " (unstaged)"
      } {
        if { [catch {set status [exec git branch -r --contains $id_hash]} msg] } {
          append id_version " (undefined)"
          puts "ERROR :-"
          puts $msg
        } {
          if {$status eq ""} {
            append id_version " (unpushed)"
          }
        }
      }
    }
    
    # Replace space character with its octal code
    proc space_replace {str} {
      return [string map {" " "\\040"} $str]
    }
    
    # Replace spaces in information strings
    set id_description [space_replace $id_description]
    set id_company     [space_replace $id_company]
    set id_author      [space_replace $id_author]
    set id_version     [space_replace $id_version]
    set id_timestamp   [space_replace $id_timestamp]
    set id_hash        [space_replace $id_hash]
    
    # Set Generics/Parameters
    set_property generic " \
      id_description=\"$id_description\" \
      id_company=\"$id_company\" \
      id_author=\"$id_author\" \
      id_version=\"$id_version\" \
      id_timestamp=\"$id_timestamp\" \
      id_hash=\"$id_hash\" \
    " [current_fileset]
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_pre_synth.tcl -O common/fw/src/script/swuk_pre_synth.tcl
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/script/swuk_pre_synth.tcl
    

    11. Relocate AXI GPIO Zed source module

    Move shared source file into common area to avoid duplication later on.

    Requirements :-
    1. Relocate the source file.
    2. Update header to reflect field changes - Project, Version & History.
    3. Edit to add a configurable bypass for the LEDs. The bypass will use a newly added mask to select which of the LEDs is bypassed. A bypassed LED (and its register) will be updated via a direct input instead of being updated by an AXI write. Adding this feature allows a way to take control of some of the LEDs without doing it ad hock in the top level wrapper.
    4. Add breakout signals for debounced Buttons & Switches.
    5. Make General improvements to the code with regards to metastability & debounce.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/design/axi_gpio_zed.v common/fw/src/design/axi_gpio_zed.v
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/design/axi_gpio_zed.v
    

    axi_gpio_zed.v

    //
    // File .......... axi_gpio_zed.v
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Common (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 2.0
    // History .......
    //   1.0 zedboard_leds_switches/fw/src/design/axi_gpio_zed.v
    // 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. A bypassed LED (and its
    // corresponding register bit) will be updated via a direct signal input (if
    // its mask bit is set), this will supersede any AXI write to that bit. The
    // LEDs Bypass input is double sampled so it can be driven asynchronous to
    // aclk.
    //
    
    
    `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)
      parameter  [c_leds_w-1 : 0] c_leds_default = {c_leds_w{1'b0}},  // P:LEDs Default Values
      parameter  [c_leds_w-1 : 0] c_leds_mask    = {c_leds_w{1'b0}}   // P:LEDs Bypass Mask
    )
    (
      // Clock & Reset
      input  wire                      aclk,           // I:Clock
      input  wire                      aresetn,        // I:Reset
      // GPIO External 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
      // GPIO Internal Channels
      input  wire [    c_leds_w-1 : 0] leds_in,        // I:LEDs In (bypass using mask)
      output wire [c_switches_w-1 : 0] switches_out,   // O:Switches Out (post meta & debounce)
      output wire [ c_buttons_w-1 : 0] buttons_out,    // O:Buttons Out (post meta & debounce)
      // 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
    
      // Channel widths
      localparam c_sb_w          = c_switches_w + c_buttons_w;
      localparam c_lsb_w         = c_leds_w + c_sb_w;
    
      // 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_lsb_w-1 : 0] meta_ip; // Metastability hardener inputs
      wire [c_lsb_w-1 : 0] meta_op; // Metastability hardener outputs
      wire [ c_sb_w-1 : 0] debounce_op; // Debounce outputs
    
      wire [    c_leds_w-1 : 0] leds_int;      // Stable LEDs
      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) begin
          for (i = 0; i <= c_registers - 1; i = i + 1)
            registers[i] <= {c_data_w{1'b0}};
          // LEDs default
          registers[c_chan_leds] <= c_leds_default;
        end 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
          // Overwrite LEDs with any bypass values
          for (i = 0; i < c_leds_w; i = i + 1)
            if (c_leds_mask[i])
              registers[c_reg_leds][i] <= leds_int[i];
        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
    
    
      // Metastability hardener
      assign meta_ip = {leds_in, switches, buttons};
    
      genvar lsb;
      for (lsb = 0; lsb < c_lsb_w; lsb = lsb + 1)
        meta_hardener #
        (
          // Parameters
          .c_reset   ( 1'b0         ),  // Reset polarity (1'b0 or 1'b1)
          .c_default ( 1'b0         )   // Flip-flop defaults (1'b0 or 1'b1)
        ) meta_hardener
        (
          // Clock & Reset
          .clk       ( aclk         ),  // Clock
          .rst       ( aresetn      ),  // Reset
          // Input & Output
          .async     ( meta_ip[lsb] ),  // Asynchronous input
          .sync      ( meta_op[lsb] )   // Synchronous output
        );
    
      assign leds_int = meta_op[c_sb_w+:c_leds_w];
    
    
      // Switch debouncer
      genvar sb;
      for (sb = 0; sb < c_sb_w; sb = sb + 1)
        debounce #
         (
          // Parameters
          .c_cycles ( 65536           )   // Debounce Cycles
         )
        debounce
         (
          // Clock & Reset
          .aclk     ( aclk            ),  // Clock
          .aresetn  ( aresetn         ),  // Reset
          // Input & Output
          .inp      ( meta_op[sb]     ),  // Input (Dirty)
          .outp     ( debounce_op[sb] )   // Output (Clean)
         );
    
      assign {switches_int, buttons_int} = debounce_op;
    
    
      // 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;
    
    
      // GPIO outputs
      assign leds         = registers[c_reg_leds][c_leds_w-1:0]; // LEDs
      assign switches_out = registers[c_chan_switches];          // Switches (after meta & debounce)
      assign buttons_out  = registers[c_chan_buttons];           // Buttons (after meta & debounce)
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/design/axi_gpio_zed.v -O common/fw/src/design/axi_gpio_zed.v
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/design/axi_gpio_zed.v
    

    12. Relocate AXI Identification source module

    Move shared source file into common area to avoid duplication later on.

    Requirements :-
    1. Relocate the source file.
    2. Update header to reflect field changes - Project, Version & History.
    3. Correct sizes & addresses in header Description field.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/design/axi_identification.v common/fw/src/design/axi_identification.v
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/design/axi_identification.v
    

    axi_identification.v

    //
    // File .......... axi_identification.v
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Common (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 3.0
    // History .......
    //   2.0 zedboard_leds_switches/fw/src/design/axi_identification.v
    // Description ...
    //   AXI4-Lite controlled register bank written in basic Verilog. The register
    // bank contains the following fixed length identification and timestamp
    // strings.
    //
    // Description ................ 128 Characters - 32 Registers - 0x000 - 0x07F
    // Company ....................  64 Characters - 16 Registers - 0x080 - 0x0BF
    // Author .....................  64 Characters - 16 Registers - 0x0C0 - 0x0FF
    // Version ....................  32 Characters -  8 Registers - 0x100 - 0x11F
    // Timestamp ..................  32 Characters -  8 Registers - 0x120 - 0x13F
    // Hash .......................  64 Characters - 16 Registers - 0x140 - 0x17F
    // Unused ..................... 128 Characters - 32 Registers - 0x180 - 0x1FF
    //                              ---              ---            -------------
    //                              512              128          - 0x000 - 0x1FF
    //
    
    
    `timescale 1 ns / 1 ps
    
    
    module axi_identification #
     (
      // Parameters (AXI4-Lite)
      localparam c_addr_w           = 9,                       // P:Address Bus Width (bits)
      localparam c_data_w           = 32,                      // P:Data Bus Width (bits)
      // Parameters (ID Strings)
      localparam c_id_description_w = 128,                     // P:Description Width (chars)
      localparam c_id_company_w     = 64,                      // P:Company Width (chars)
      localparam c_id_author_w      = 64,                      // P:Author Width (chars)
      localparam c_id_version_w     = 32,                      // P:Version Width (chars)
      localparam c_id_timestamp_w   = 32,                      // P:Timestamp Width (chars)
      localparam c_id_hash_w        = 64                       // P:Hash Width (chars)
     )
     (
      // Clock & Reset
      input  wire                             aclk,            // I:Clock
      input  wire                             aresetn,         // I:Reset
      // Identification Strings
      input  wire [8*c_id_description_w-1 :0] id_description,  // I:Description
      input  wire [8*c_id_company_w-1 : 0]    id_company,      // I:Company
      input  wire [8*c_id_author_w-1 : 0]     id_author,       // I:Author
      input  wire [8*c_id_version_w-1 : 0]    id_version,      // I:Version
      input  wire [8*c_id_timestamp_w-1 : 0]  id_timestamp,    // I:Timestamp
      input  wire [8*c_id_hash_w-1 : 0]       id_hash,         // I:Hash
      // 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 no_regs = 2**(c_addr_w - $clog2(c_data_w / 8));
      localparam id_description_lo = 0;
      localparam id_description_hi = id_description_lo + (c_id_description_w / 4) - 1;
      localparam id_company_lo     = 1 + id_description_hi;
      localparam id_company_hi     = id_company_lo + (c_id_company_w / 4) - 1;
      localparam id_author_lo      = 1 + id_company_hi;
      localparam id_author_hi      = id_author_lo + (c_id_author_w / 4) -1;
      localparam id_version_lo     = 1 + id_author_hi;
      localparam id_version_hi     = id_version_lo + (c_id_version_w / 4) - 1;
      localparam id_timestamp_lo   = 1 + id_version_hi;
      localparam id_timestamp_hi   = id_timestamp_lo + (c_id_timestamp_w / 4) - 1;
      localparam id_hash_lo        = 1 + id_timestamp_hi;
      localparam id_hash_hi        = id_hash_lo + (c_id_hash_w / 4) - 1;
    
    
      // Signals
      reg aw_en;
      reg [c_addr_w-$clog2(c_data_w / 8) - 1 : 0] axi_awaddr;
      reg [c_addr_w-$clog2(c_data_w / 8) - 1 : 0] axi_araddr;
      reg [c_data_w-1 : 0] registers_wr [no_regs - 1 : 0];
      wire [c_data_w-1 : 0] registers_rd [no_regs - 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_data_w/8)];
    
    
      // 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 <= no_regs - 1; i = i + 1)
            registers_wr[i] <= {c_data_w{1'b0}};
        else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready)
          for (i = 0; i <= c_data_w/8 - 1; i = i + 1)
            if (s_axi_wstrb[i])
              registers_wr[axi_awaddr][i*8 +: 8] <= s_axi_wdata[i*8 +: 8]; // Byte-Wise Write
    
    
      // 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_data_w/8)];
    
    
      // 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_rd[axi_araddr];
    
    
      // Read Register Constants
      genvar j;
      generate
        for (j = 0; j <= no_regs - 1; j = j + 1) begin
          if      (j <= id_description_hi) assign registers_rd[j] = id_description[32*(j-id_description_lo) +: 32];
          else if (j <= id_company_hi    ) assign registers_rd[j] = id_company    [32*(j-id_company_lo    ) +: 32];
          else if (j <= id_author_hi     ) assign registers_rd[j] = id_author     [32*(j-id_author_lo     ) +: 32];
          else if (j <= id_version_hi    ) assign registers_rd[j] = id_version    [32*(j-id_version_lo    ) +: 32];
          else if (j <= id_timestamp_hi  ) assign registers_rd[j] = id_timestamp  [32*(j-id_timestamp_lo  ) +: 32];
          else if (j <= id_hash_hi       ) assign registers_rd[j] = id_hash       [32*(j-id_hash_lo       ) +: 32];
          else                             assign registers_rd[j] = 32'h00000000;
        end
      endgenerate
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/design/axi_identification.v -O common/fw/src/design/axi_identification.v
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/design/axi_identification.v
    

    13. Relocate AXI Register Bank souce module

    Move shared source file into common area to avoid duplication later on.

    Requirements :-
    1. Relocate the source file.
    2. Update header to reflect field changes - Project, Version & History.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/design/axi_register_bank.v common/fw/src/design/axi_register_bank.v
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/design/axi_register_bank.v
    

    axi_register_bank.v

    //
    // File .......... axi_register_bank.v
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Common (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 2.0
    // History .......
    //   1.0 zedboard_leds_switches/fw/src/design/axi_register_bank.v
    // 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:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/design/axi_register_bank.v -O common/fw/src/design/axi_register_bank.v
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/design/axi_register_bank.v
    

    14. Relocate Debounce source module

    Move shared source file into common area to avoid duplication later on.

    Requirements :-
    1. Relocate the source file.
    2. Update header to reflect field changes - Project, Version & History.
    3. Remove metastability hardener logic.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/design/debounce.v common/fw/src/design/debounce.v
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/design/debounce.v
    

    debounce.v

    //
    // File .......... debounce.v
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Common (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 2.0
    // History .......
    //   1.0 zedboard_leds_switches/fw/src/design/debounce.v
    // 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 [$clog2(c_cycles)-1:0] count;
    
    
      // Debouncer
      always @(posedge aclk)
        if (!aresetn) begin
          outp  <= 1'b0;
          count <= 32'b0;
        end else if (outp == inp) 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:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/design/debounce.v -O common/fw/src/design/debounce.v
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/design/debounce.v
    
    #### Part 6 - Improve the .gitignore system ####

    15. Change details for .gitignore

    Due to the separate project repositories being disbanded and replaced with a global projects repository some work is required with the .gitignore files to make this fundamental change work.

    Introduce a global .gitignore that basically says ignore everything other than directories, subdirectories & other .gitignore files.
    steve@Desktop:/swuk_tutorial$ cat << EOF > .gitignore
    steve@Desktop:/swuk_tutorial$ *
    steve@Desktop:/swuk_tutorial$ !*/
    steve@Desktop:/swuk_tutorial$ !.gitignore
    steve@Desktop:/swuk_tutorial$ EOF
    steve@Desktop:/swuk_tutorial$ echo -e '*\n!*/\n!.gitignore' > .gitignore
    
    Introduce a project (local) .gitignore that basically says include everything other than the vitis/vivado build directories & the superfluous generated files/directories in the block design directory.
    steve@Desktop:/swuk_tutorial$ cat << EOF > common/.gitignore
    steve@Desktop:/swuk_tutorial$ !*
    steve@Desktop:/swuk_tutorial$ fw/vivado
    steve@Desktop:/swuk_tutorial$ fw/src/ip/*
    steve@Desktop:/swuk_tutorial$ !fw/src/ip/*/
    steve@Desktop:/swuk_tutorial$ fw/src/ip/*/*
    steve@Desktop:/swuk_tutorial$ !fw/src/ip/*/*.xci
    steve@Desktop:/swuk_tutorial$ fw/src/diagram/*
    steve@Desktop:/swuk_tutorial$ !fw/src/diagram/*/
    steve@Desktop:/swuk_tutorial$ fw/src/diagram/*/*
    steve@Desktop:/swuk_tutorial$ !fw/src/diagram/*/*.bd
    steve@Desktop:/swuk_tutorial$ sw/vitis
    steve@Desktop:/swuk_tutorial$ EOF
    steve@Desktop:/swuk_tutorial$ echo -e '!*\nfw/vivado\nfw/src/ip/*\n!fw/src/ip/*/\nfw/src/ip/*/*\n!fw/src/ip/*/*.xci\nfw/src/diagram/*\n!fw/src/diagram/*/\nfw/src/diagram/*/*\n!fw/src/diagram/*/*.bd\nsw/vitis' > common/.gitignore
    
    The project (local) .gitignore is copied into all the current project directories :-
    1. common/.gitignore (done)
    2. zedboard_hello_world/.gitignore
    3. zedboard_leds_buttons/.gitignore
    4. zedboard_leds_switches/.gitignore
    5. zedboard_linux/.gitignore
    steve@Desktop:/swuk_tutorial$ find zed* -maxdepth 0 -type d -exec cp common/.gitignore {} \;
    
    #### Part 7 - Restructure Zedboard LEDs & Buttons project ####

    16. Rename & update testbench

    Make the naming convention for the top-level testbench consistent with the project name. Prefix this with tb_, i.e. tb_<project>.[v|sv|vhdl]. By adopting this naming convention the process of identifying the top-level testbench module becomes much easier for the swuk_create_vivado_project script. At present it seems Vivado struggles to automatically identify the testbench top-level.

    Requirements :-
    1. Rename & relocate the testbench module.
    2. Update header to reflect field changes - File, Version, History & Description.
    3. Edit the source :-
      1. Change the module name.
      2. ???????????? Vastly improve testing capabilities.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_buttons/fw/src/testbench/testbench.sv zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv
    
    steve@Desktop:~/swuk_tutorial$ subl zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv
    

    tb_zedboard_leds_buttons.sv

    //
    // File .......... tb_zedboard_leds_buttons.sv
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Zedboard LEDs & Buttons (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 2.0
    // History .......
    //   1.0 zedboard_leds_buttons/fw/src/testbench/testbench.sv
    // Description ...
    //   Very simple testbench to check correct connectivity of the block diagram.
    // Uses the Zynq Verification IP suite to read/write the GPIO LED register and
    // read the GPIO switch register.
    //
    
    
    timeunit      1ns;
    timeprecision 1ps;
    
    
    module tb_zedboard_leds_buttons;
    
      // Constants
      localparam [31:0] led_addr = 32'h41200000;
      localparam [31:0] sws_addr = 32'h41200008;
      localparam [31:0] led_init = 32'h00000018;
      localparam [31:0] led_next = 32'h00000081;
      localparam [ 7:0] sws_next =  8'b11000011;
    
    
      // Signals
      wire       ps_clk;
      wire       ps_porb;
      wire       ps_srstb;
      bit [ 7:0] leds_8bits;
      bit [ 7:0] sws_8bits;
      bit        clk;
      bit        resetn;
      bit        bresp;
      bit [31:0] rdata;
      bit        rresp;
    
    
      // 50MHz Clock
      always #10 clk = !clk;
    
    
      // Reset
      initial begin
        repeat(20)
          @(posedge clk);
        resetn = 1'b1;
      end
    
    
      // Drive PL Clock & Reset
      assign ps_clk   = clk;
      assign ps_porb  = resetn;
      assign ps_srstb = resetn;
    
    
      // Stimulus
      initial begin
        // Allow reset time to work
    
        @(posedge resetn);
        repeat(10)
          @(posedge clk);
    
        // Reset PL
        testbench.system_wrapper_i.system_i.processing_system7_0.inst.fpga_soft_reset(32'h1);
        testbench.system_wrapper_i.system_i.processing_system7_0.inst.fpga_soft_reset(32'h0);
    
        // Read GPIO LED Register
        testbench.system_wrapper_i.system_i.processing_system7_0.inst.read_data(led_addr, 4, rdata, rresp);
        $display ("Read of GPIO LED Register = 32'h%x (%s)", rdata, (rdata == led_init) ? "expected" : "unexpected");
    
        // Write GPIO LED Register
        testbench.system_wrapper_i.system_i.processing_system7_0.inst.write_data(led_addr, 4, led_next, bresp);
    
        // Read GPIO LED Register
        testbench.system_wrapper_i.system_i.processing_system7_0.inst.read_data(led_addr, 4, rdata, rresp);
        $display ("Read of GPIO LED Register = 32'h%x (%s)", rdata, (rdata == led_next) ? "expected" : "unexpected");
    
        // Set some of the switches to 'on'
        sws_8bits = sws_next;
    
        // Read GPIO Switch Register
        testbench.system_wrapper_i.system_i.processing_system7_0.inst.read_data(sws_addr, 4, rdata, rresp);
        $display ("Read of GPIO Switch Register = 32'h%x (%s)", rdata, (rdata == sws_next) ? "expected" : "unexpected");
    
        // All done
        $display ("That's all folks!");
        $stop;
    
      end
    
    
      // Unit Under Test
      system_wrapper system_wrapper_i
       (
        // LEDs
        .leds_tri_o        ( leds_8bits ),  // O:LEDs
        // Switches
        .switches_tri_i    ( sws_8bits  ),  // I:Switches
        // System
        .DDR_addr          (            ),  // B:Address
        .DDR_ba            (            ),  // B:Bank Address
        .DDR_cas_n         (            ),  // B:Column Address Select
        .DDR_ck_n          (            ),  // B:Clock (Neg)
        .DDR_ck_p          (            ),  // B:Clock (Pos)
        .DDR_cke           (            ),  // B:Clock Enable
        .DDR_cs_n          (            ),  // B:Chip Select
        .DDR_dm            (            ),  // B:Data Mask
        .DDR_dq            (            ),  // B:Data Input/Output
        .DDR_dqs_n         (            ),  // B:Data Strobe (Neg)
        .DDR_dqs_p         (            ),  // B:Data Strobe (Pos)
        .DDR_odt           (            ),  // B:Output Dynamic Termination
        .DDR_ras_n         (            ),  // B:Row Address Select
        .DDR_reset_n       (            ),  // B:Reset
        .DDR_we_n          (            ),  // B:Write Enable
        .FIXED_IO_ddr_vrn  (            ),  // B:Termination Voltage
        .FIXED_IO_ddr_vrp  (            ),  // B:Termination Voltage
        .FIXED_IO_mio      (            ),  // B:Peripheral Input/Output
        .FIXED_IO_ps_clk   ( ps_clk     ),  // B:System Reference Clock
        .FIXED_IO_ps_porb  ( ps_porb    ),  // B:Power On Reset
        .FIXED_IO_ps_srstb ( ps_srstb   )   // B:External System Reset
       );
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv -O zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv
    
    TBD

    17. Rename & update constraints

    Make the naming convention for the top-level constraints consistent with the top-level design. By adopting this naming convention the process of identifying the top-level constraints becomes much easier for the swuk_create_vivado_project script.

    Requirements :-
    1. Rename & relocate the constraints file.
    2. Update header to reflect field changes - File, Version, History & Description.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_buttons/fw/src/constraint/zedboard.xdc zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc
    
    steve@Desktop:~/swuk_tutorial$ subl zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc
    

    zedboard_leds_buttons.xdc

    #
    # File .......... zedboard_leds_buttons.xdc
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Zedboard LEDs & Buttons (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 zedboard_leds_buttons/fw/src/constraint/zedboard.xdc
    # Description ...
    #   Top level pin & timing constraints.
    #
    
    # User LEDs - Bank 33
    set_property PACKAGE_PIN T22 [get_ports {leds_tri_o[0]}];  # "LD0"
    set_property PACKAGE_PIN T21 [get_ports {leds_tri_o[1]}];  # "LD1"
    set_property PACKAGE_PIN U22 [get_ports {leds_tri_o[2]}];  # "LD2"
    set_property PACKAGE_PIN U21 [get_ports {leds_tri_o[3]}];  # "LD3"
    set_property PACKAGE_PIN V22 [get_ports {leds_tri_o[4]}];  # "LD4"
    set_property PACKAGE_PIN W22 [get_ports {leds_tri_o[5]}];  # "LD5"
    set_property PACKAGE_PIN U19 [get_ports {leds_tri_o[6]}];  # "LD6"
    set_property PACKAGE_PIN U14 [get_ports {leds_tri_o[7]}];  # "LD7"
    
    
    # User DIP Switches - Bank 34 & 35
    set_property PACKAGE_PIN F22 [get_ports {switches_tri_i[0]}];  # "SW0"
    set_property PACKAGE_PIN G22 [get_ports {switches_tri_i[1]}];  # "SW1"
    set_property PACKAGE_PIN H22 [get_ports {switches_tri_i[2]}];  # "SW2"
    set_property PACKAGE_PIN F21 [get_ports {switches_tri_i[3]}];  # "SW3"
    set_property PACKAGE_PIN H19 [get_ports {switches_tri_i[4]}];  # "SW4"
    set_property PACKAGE_PIN H18 [get_ports {switches_tri_i[5]}];  # "SW5"
    set_property PACKAGE_PIN H17 [get_ports {switches_tri_i[6]}];  # "SW6"
    set_property PACKAGE_PIN M15 [get_ports {switches_tri_i[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:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc -O zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc
    
    TBD
    #### Part 8 - Restructure Zedboard LEDs & Switches project ####

    18. Rename & update top-level

    So far the top-level HDL of the main system block design has been held inside the <project>/fw/src/diagram/system/hdl directory and called system_wrapper.sv. This is not ideal, the top-level would be better placed inside the <project>/fw/src/design directory and called <project>.[v|sv|vhdl]. By adopting this naming convention the process of identifying the top-level module becomes much easier for the swuk_create_vivado_project script. At present it seems Vivado struggles to automatically identify the design top-level.

    Requirements :-
    1. Rename & relocate the top-level module.
    2. Update header to reflect field changes - File, Version, History & Description.
    3. Edit the source :-
      1. Change the module name.
      2. Make the fmt function automatic to fix issues with simulation.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/diagram/system/hdl/system_wrapper.sv zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv
    
    steve@Desktop:~/swuk_tutorial$ subl zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv
    

    zedboard_leds_switches.sv

    //
    // File .......... zedboard_leds_switches.sv
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Zedboard LEDs & Switches (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 4.0
    // History .......
    //   3.0 zedboard_leds_switches/fw/src/diagram/system/hdl/system_wrapper.sv
    // Description ...
    //   Top level wrapper for the system block design.
    //
    
    
    timeunit      1ns;
    timeprecision 1ps;
    
    
    module zedboard_leds_switches #
    (
      // 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 automatic [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:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv -O zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv
    
    TBD

    19. Rename & enhance testbench

    Make the naming convention for the top-level testbench consistent with the top-level design. Prefix this with tb_, i.e. tb_<project>.[v|sv|vhdl]. By adopting this naming convention the process of identifying the top-level testbench module becomes much easier for the swuk_create_vivado_project script. At present it seems Vivado struggles to automatically identify the testbench top-level.

    Requirements :-
    1. Rename & relocate the testbench module.
    2. Update header to reflect field changes - File, Version, History & Description.
    3. Edit the source :-
      1. Change the module name.
      2. Vastly improve testing capabilities.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/testbench/testbench.sv zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv
    
    steve@Desktop:~/swuk_tutorial$ subl zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv
    

    tb_zedboard_leds_switches.sv

    //
    // File .......... tb_zedboard_leds_switches.sv
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Zedboard LEDs & Switches (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 4.0
    // History .......
    //   3.0 zedboard_leds_switches/fw/src/testbench/testbench.sv
    // Description ...
    //   Very simple testbench to check correct connectivity of the block
    // diagram. Uses the Zynq Verification IP suite to read & write to & from
    // the various peripherals (Identification, GPIO Zed & Register Bank).
    //
    
    
    module tb_zedboard_leds_switches;
    
      // Time unit & precision
      timeunit 1ns;
      timeprecision 1ps;
    
      // Identification base address, local addresses & constants
      localparam bit [31:0] base_identification = 32'h40000000;
        localparam int id_fields [6:0][1:0] =
          '{
            '{12'h180, 128},  // id_unknown
            '{12'h140,  64},  // id_hash
            '{12'h120,  32},  // id_timestamp
            '{12'h100,  32},  // id_version
            '{12'h0C0,  64},  // id_author
            '{12'h080,  64},  // id_company
            '{12'h000, 128}   // id_description
          };
        localparam string id_strings [6:0] =
          '{
            "Unknown",
            "Hash",
            "Timestamp",
            "Version",
            "Author",
            "Company",
            "Description"
          };
    
      // GPIO base address, local addresses & constants
      localparam bit [31:0] base_gpio_zed     = 32'h40010000;
        localparam bit [7:0] gpio_led           = 8'h00;
        localparam bit [7:0] gpio_dip_switches  = 8'h08;
        localparam bit [7:0] gpio_push_buttons  = 8'h10;
    
      // Register Bank base address, local addresses & constants
      localparam bit [31:0] base_register_bank = 32'h40020000;
    
      // Signals
      wire       ps_clk;
      wire       ps_porb;
      wire       ps_srstb;
      bit [ 7:0] leds;
      bit [ 7:0] switches;
      bit [ 4:0] buttons;
      bit        clk;
      bit        resetn;
      bit        bresp;
      bit [31:0] rdata;
      bit        rresp;
    
    
      // 50MHz Clock
      always #10 clk = !clk;
    
    
      // Reset
      initial begin
        repeat(20)
          @(posedge clk);
        resetn = 1'b1;
      end
    
    
      // Drive PL Clock & Reset
      assign ps_clk   = clk;
      assign ps_porb  = resetn;
      assign ps_srstb = resetn;
    
    
      // Stimulus
      initial begin
    
        // AXI Address & Data
        bit [31:0] addr;
        bit [31:0] data;
    
        // ID string
        string name;
    
        // Scoreboard
        int tests;
        int fails;
    
        // Allow reset time to work
        @(posedge resetn);
        repeat(10)
          @(posedge clk);
    
        // Minimise VIP debug messages
        uut.system_i.processing_system7_0.inst.set_debug_level_info(0);
    
        // Reset PL
        uut.system_i.processing_system7_0.inst.fpga_soft_reset(1);
        uut.system_i.processing_system7_0.inst.fpga_soft_reset(0);
    
        // Read GPIO LED Register
        addr = base_gpio_zed + gpio_led;
        data = 8'h18;
        uut.system_i.processing_system7_0.inst.read_data(addr, 4, rdata, rresp);
        $display ("Read from GPIO LED Register: Address = 32'h%x, Expected Data = 32'h%x, Received Data = 32'h%x (%s)", addr, data, rdata, (rdata == data) ? "Passed" : "Failed");
        if (rdata != data)
          fails++;
        tests++;
    
        // Write to GPIO LED Register
        addr = base_gpio_zed + gpio_led;
        data = 8'h81;
        uut.system_i.processing_system7_0.inst.write_data(addr, 4, data, bresp);
    
        // Read from GPIO LED Register
        addr = base_gpio_zed + gpio_led;
        uut.system_i.processing_system7_0.inst.read_data(addr, 4, rdata, rresp);
        $display ("Read from GPIO LED Register: Address = 32'h%x, Expected Data = 32'h%x, Received Data = 32'h%x (%s)", addr, data, rdata, (rdata == data) ? "Passed" : "Failed");
        if (rdata != data)
          fails++;
        tests++;
    
        // Set some of the switches to 'on'
        switches = 8'b11000011;
    
        // Wait for switch debounce
        #656000ns;
    
        // Read GPIO Switch Register
        addr = base_gpio_zed + gpio_dip_switches;
        data = switches;
        uut.system_i.processing_system7_0.inst.read_data(addr, 4, rdata, rresp);
        $display ("Read from GPIO Switch Register: Address = 32'h%x, Expected Data = 32'h%x, Received Data = 32'h%x (%s)", addr, data, rdata, (rdata == data) ? "Passed" : "Failed");
        if (rdata != data)
          fails++;
        tests++;
    
        // Write to Register Bank
        for (int i = 0; i < 4; i++) begin
          addr = base_register_bank + i * 4;
          data = 65536 + i;
          $display("Write to Register Bank: Address = 32'h%x, Data = 32'h%x", addr, data);
          uut.system_i.processing_system7_0.inst.write_data(addr, 4, data, bresp);
        end
    
        // Read from Register Bank
        for (int i = 3; i >= 0; i--) begin
          addr = base_register_bank + i * 4;
          data = 65536 + i;
          uut.system_i.processing_system7_0.inst.read_data(addr, 4, rdata, rresp);
          $display ("Read from Register Bank: Address = 32'h%x, Expected Data = 32'h%x, Received Data = 32'h%x (%s)", addr, data, rdata, (rdata == data) ? "Passed" : "Failed");
          if (rdata != data)
            fails++;
          tests++;
        end
    
        // Read from Identification
        for (int h = 0; h < 6; h++) begin
          name = "";
          for (int i = 0; i < id_fields[h][0]; i = i + 4) begin
            addr = base_identification + id_fields[h][1] + i;
            uut.system_i.processing_system7_0.inst.read_data(addr, 4, rdata, rresp);
            for (int j = 0; j < 4; j++) begin
              if (rdata[8*j+:8] != 8'h00)
                name = {name, rdata[8*j+:8]};
            end
          end
          $display ("Read from Identification: Address = 32'h%x, Expected Data = %s, Received Data = %s (%s)", id_fields[h][1], id_strings[h], name, (id_strings[h] == name) ? "Passed" : "Failed");
          if (id_strings[h] != name)
            fails++;
          tests++;
        end
    
        // Scoreboard
        $display;
        $display("Performed %0d tests, %0d passed, %0d failed.", tests, tests-fails, fails);
    
        // All done
        $display;
        $display ("That's all folks!");
        $display;
    
        $stop;
    
      end
    
    
      // Unit Under Test
      zedboard_leds_switches #
       (
        // Parameters
        .id_description ( id_strings[0] ),  // P:Firmware Description ... Max 128 Characters
        .id_company     ( id_strings[1] ),  // P:Company ................ Max  64 Characters
        .id_author      ( id_strings[2] ),  // P:Author ................. Max  64 Characters
        .id_version     ( id_strings[3] ),  // P:Version Number ......... Max  16 Characters
        .id_timestamp   ( id_strings[4] ),  // P:Build Timestamp ........ Max  32 Characters
        .id_hash        ( id_strings[5] )   // P:Build Timestamp ........ Max  32 Characters
       )
      uut
       (
        // LEDs
        .leds              ( leds       ),  // O:LEDs
        // Switches
        .switches          ( switches   ),  // I:Switches
        // Push Buttons
        .buttons           ( buttons    ),  // I:Push Buttons
        // System
        .DDR_addr          (            ),  // B:Address
        .DDR_ba            (            ),  // B:Bank Address
        .DDR_cas_n         (            ),  // B:Column Address Select
        .DDR_ck_n          (            ),  // B:Clock (Neg)
        .DDR_ck_p          (            ),  // B:Clock (Pos)
        .DDR_cke           (            ),  // B:Clock Enable
        .DDR_cs_n          (            ),  // B:Chip Select
        .DDR_dm            (            ),  // B:Data Mask
        .DDR_dq            (            ),  // B:Data Input/Output
        .DDR_dqs_n         (            ),  // B:Data Strobe (Neg)
        .DDR_dqs_p         (            ),  // B:Data Strobe (Pos)
        .DDR_odt           (            ),  // B:Output Dynamic Termination
        .DDR_ras_n         (            ),  // B:Row Address Select
        .DDR_reset_n       (            ),  // B:Reset
        .DDR_we_n          (            ),  // B:Write Enable
        .FIXED_IO_ddr_vrn  (            ),  // B:Termination Voltage
        .FIXED_IO_ddr_vrp  (            ),  // B:Termination Voltage
        .FIXED_IO_mio      (            ),  // B:Peripheral Input/Output
        .FIXED_IO_ps_clk   ( ps_clk     ),  // B:System Reference Clock
        .FIXED_IO_ps_porb  ( ps_porb    ),  // B:Power On Reset
        .FIXED_IO_ps_srstb ( ps_srstb   )   // B:External System Reset
       );
    
    
    endmodule
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv -O zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv
    
    TBD

    20. Rename & update constraints

    Make the naming convention for the top-level constraints consistent with the top-level design. By adopting this naming convention the process of identifying the top-level constraints becomes much easier for the swuk_create_vivado_project script.

    Requirements :-
    1. Rename & relocate the constraints file.
    2. Update header to reflect field changes - File, Version, History & Description.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv zedboard_leds_switches/fw/src/constraint/zedboard.xdc zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc
    
    steve@Desktop:~/swuk_tutorial$ subl zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc
    

    zedboard_leds_switches.xdc

    #
    # File .......... zedboard_leds_switches.xdc
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Zedboard LEDs & Switches (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 3.0
    # History .......
    #   2.0 zedboard_leds_switches/fw/src/constraint/zedboard.xdc
    # Description ...
    #   Top level pin & timing constraints.
    #
    
    # 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:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc -O zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc
    
    TBD

    21. Update software

    Update the very simple bare metal software to use the Zed GPIO instead of the Xilinx GPIO. This should have been done in Tutorial XX.

    Requirements :-
    1. Replace the XGPIO register access with generic access for the Zed GPIO.
    2. Update header to reflect field changes - Version & Description.
    steve@Desktop:~/swuk_tutorial$ subl zedboard_leds_switches/sw/src/c/zedboard_leds_switches.c
    

    zedboard_leds_switches.c

    //
    // File .......... zedboard_leds_switches.c
    // Author ........ Steve Haywood
    // Website ....... http://www.spacewire.co.uk
    // Project ....... Zedboard LEDs & Switches (SpaceWire UK Tutorial)
    // Date .......... 25 Jun 2025
    // Version ....... 2.0
    // Description ...
    //   Very simple LEDs & Switches example design.
    //
    
    
    #include <unistd.h>
    #include "xparameters.h"
    #include "xil_printf.h"
    
    
    #define XGPIO_LEDS_OFFSET     0x0
    #define XGPIO_SWITCHES_OFFSET 0x8
    #define XGPIO_BUTTONS_OFFSET  0x10
    
    
    // Read a value from a GPIO register.
    u32 gpio_read(u32 base, u32 offset)
    {
      return *((volatile u32 *)(base + offset));
    }
    
    
    // Write a value to a GPIO register.
    void gpio_write(u32 base, u32 offset, u32 data)
    {
      *((volatile u32 *)(base + offset)) = data;
    }
    
    
    // Print LSB 'bits' from 'number' in binary format
    void print_bin(u32 number, u8 bits)
    {
      u32 mask = 1 << (bits - 1);
      for(; mask; mask >>= 1)
        xil_printf("%d", (number & mask) != 0);
    }
    
    
    int main()
    {
      u32 gpio_0_data;
    
      // Print header
      print("LED & Switch Example\n\n\r");
      print("Select required operation :-\n\r");
      print("r - Read from switch register\n\r");
      print("w - Write to LED register\n\r");
      print("q - Quit application\n\r");
    
      // Wait for user input & perform required operation
      char8 key;
      do
      {
        key = inbyte();
    
        switch (key)
        {
          case 'r':
        	gpio_0_data = gpio_read(XPAR_AXI_GPIO_ZED_0_BASEADDR, XGPIO_SWITCHES_OFFSET);
            xil_printf("Switch Register @ 0x%x = 0b", gpio_0_data);
            print_bin(gpio_0_data, 8);
            print("\n\r");
            break;
    
          case 'w':
            gpio_0_data = 0b00000001;
            for (u8 i = 0; i <= 15; i++)
            {
           	  gpio_write(XPAR_AXI_GPIO_ZED_0_BASEADDR, XGPIO_LEDS_OFFSET, gpio_0_data);
              xil_printf("LED Register @ 0x%x = 0b", gpio_0_data);
              print_bin(gpio_0_data, 8);
              print("\n\r");
              usleep(100000);
              if (i < 7)
                gpio_0_data <<= 1;
              else
                gpio_0_data >>= 1;
            }
            break;
        }
      } while (key != 'q');
    
      // Print footer
      print("All done!\n\n\r");
    
      // Exit application
      return 0;
    }
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/zedboard_leds_switches/sw/src/c/zedboard_leds_switches.c -O zedboard_leds_switches/sw/src/c/zedboard_leds_switches.c
    
    TBD
    #### Part 9 - Archive build files for easy retrieval ####

    22. Setup local directory

    Create the required local user directory.
    steve@Desktop:~/swuk_tutorial$ mkdir -p ${swuk_user}/{hardware,firmware,petalinux}
    

    23. Obtain Hardware Platform (handoff) archives

    Extract the historical Hardware Platform archives from the SpaceWire UK Tutorial Repository.
    steve@Desktop:~/swuk_tutorial$ git show zedboard_hello_world_v1.0:zedboard_hello_world/fw/system_wrapper.xsa > ${swuk_user}/hardware/zedboard_hello_world_v1.0.xsa
    steve@Desktop:~/swuk_tutorial$ git show zedboard_leds_buttons_v1.0:zedboard_leds_buttons/fw/system_wrapper.xsa > ${swuk_user}/hardware/zedboard_leds_buttons_v1.0.xsa
    steve@Desktop:~/swuk_tutorial$ git show zedboard_linux_v1.0:zedboard_linux/os/petalinux/project-spec/hw-description/system.xsa > ${swuk_user}/hardware/zedboard_leds_switches_v1.0.xsa
    steve@Desktop:~/swuk_tutorial$ git show zedboard_linux_v8.0:zedboard_linux/os/petalinux/project-spec/hw-description/system.xsa > ${swuk_user}/hardware/zedboard_leds_switches_v2.0.xsa
    steve@Desktop:~/swuk_tutorial$ git show zedboard_linux_v9.0:zedboard_linux/os/petalinux/project-spec/hw-description/system.xsa > ${swuk_user}/hardware/zedboard_leds_switches_v3.0.xsa
    steve@Desktop:~/swuk_tutorial$ git show zedboard_linux_v10.0:zedboard_linux/os/petalinux/project-spec/hw-description/system.xsa > ${swuk_user}/hardware/zedboard_leds_switches_v4.0.xsa
    steve@Desktop:~/swuk_tutorial$ git show zedboard_linux_v11.0:zedboard_linux/os/petalinux/project-spec/hw-description/system.xsa > ${swuk_user}/hardware/zedboard_leds_switches_v5.0.xsa
    

    24. Obtain Bitstream (firmware) files

    Extract the Bitstream files from the Hardware Platform archives.
    steve@Desktop:~/swuk_tutorial$ cd ${swuk_user}/hardware
    steve@Desktop:~/Documents/swuk_tutorial/hardware$ for file in *.xsa; do
    steve@Desktop:~/Documents/swuk_tutorial/hardware$   unzip -p $file system_wrapper.bit > ${swuk_user}/firmware/${file%.*}.bit
    steve@Desktop:~/Documents/swuk_tutorial/hardware$ done
    steve@Desktop:~/Documents/swuk_tutorial/hardware$ cd -
    

    25. Obtain Binary (firmware) files

    Extract the Binary files from the Bitstream files.
    steve@Desktop:~/swuk_tutorial$ cd ${swuk_user}/firmware
    steve@Desktop:~/Documents/swuk_tutorial/firmware$ for file in *.bit; do
    steve@Desktop:~/Documents/swuk_tutorial/firmware$   echo -e "all:\n{\n  $file\n}" > ${file%.*}.bif
    steve@Desktop:~/Documents/swuk_tutorial/firmware$   bootgen -arch zynq -image ${file%.*}.bif -process_bitstream bin -w
    steve@Desktop:~/Documents/swuk_tutorial/firmware$   mv ${file}.bin ${file%.*}.bin
    steve@Desktop:~/Documents/swuk_tutorial/firmware$ done
    steve@Desktop:~/Documents/swuk_tutorial/firmware$ rm *.bif
    steve@Desktop:~/Documents/swuk_tutorial/firmware$ cd -
    

    26. Download the PetaLinux (boot & OS) files

    Download the PetaLinux boot files from the SpaceWire UK website.
    steve@Desktop:~/swuk_tutorial$ cd ${swuk_user}
    steve@Desktop:~/Documents/swuk_tutorial$ wget -r -np -nH --cut-dirs=5 -R "index.html*" 192.168.2.20/spacewire.co.uk/tutorial/shared/documents/swuk_tutorial/petalinux/
    steve@Desktop:~/Documents/swuk_tutorial$ cd -
    
    #### Part 10 - Create system (baseline) block design ####

    27. Create system (baseline) block design TCL script

    Create a Vivado TCL script that creates a brand new system (baseline) block design that can be used as the initial building block for any new design.

    Requirements :-
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_create_system_bd.tcl
    

    swuk_create_system_bd.tcl

    #
    # File .......... swuk_create_system_bd.tcl
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 24 Jun 2025
    # Version ....... 1.0
    # Description ...
    #   Create & connect up a baseline top-level block design for the Zedboard.
    #
    # Design to include the following basic components :-
    #   - ZYNQ7 Processing System
    #   - Processor System Reset
    #   - AXI Interconnect
    #   - Constant
    #   - axi_identification (RTL)
    #   - axi_gpio_zed (RTL)
    #   - axi_register_bank (RTL)
    #
    
    #############################################################################
    # Set block design path
    
    set dir_user        "../src"
    set dir_diagram     "$dir_user/diagram"
    
    #############################################################################
    # Get block design name from script filename (xxx_xxx_yyy_xxx.tcl = yyy)
    
    set whoami [file normalize [info script]]
    set fbasename [file rootname [file tail $whoami]]
    set parts [split $fbasename "_"]
    set bdname [lindex $parts 2]
    
    #####################################
    # Exit if block design already exists
    set bd_file [get_files -quiet $bdname.bd]
    if {$bd_file != ""} {
      puts "Not creating new block design as it already exists at $bd_file."
      return
    }
    
    ########################
    # Create block design
    
    create_bd_design $bdname
    
    ########################
    # Create interface ports
    
    set DDR      [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:ddrx_rtl:1.0 DDR ]
    set FIXED_IO [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_processing_system7:fixedio_rtl:1.0 FIXED_IO ]
    
    ##############
    # Create ports
    
    set id_description [ create_bd_port -dir I -from 1023 -to 0 id_description ]
    set id_company     [ create_bd_port -dir I -from  511 -to 0 id_company ]
    set id_author      [ create_bd_port -dir I -from  511 -to 0 id_author ]
    set id_version     [ create_bd_port -dir I -from  255 -to 0 id_version ]
    set id_timestamp   [ create_bd_port -dir I -from  255 -to 0 id_timestamp ]
    set id_hash        [ create_bd_port -dir I -from  511 -to 0 id_hash ]
    set switches       [ create_bd_port -dir I -from    7 -to 0 switches ]
    set buttons        [ create_bd_port -dir I -from    4 -to 0 buttons ]
    set leds           [ create_bd_port -dir O -from    7 -to 0 leds ]
    
    ###################################
    # Create instances & set properties
    
    create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0
    # Avnet BSP Configuration
    set_property -dict [list \
      CONFIG.PCW_ACT_ENET0_PERIPHERAL_FREQMHZ {125.000000} \
      CONFIG.PCW_ACT_FPGA0_PERIPHERAL_FREQMHZ {100.000000} \
      CONFIG.PCW_ACT_QSPI_PERIPHERAL_FREQMHZ {200.000000} \
      CONFIG.PCW_ACT_SDIO_PERIPHERAL_FREQMHZ {50.000000} \
      CONFIG.PCW_ACT_UART_PERIPHERAL_FREQMHZ {50.000000} \
      CONFIG.PCW_APU_PERIPHERAL_FREQMHZ {666.666667} \
      CONFIG.PCW_CLK0_FREQ {100000000} \
      CONFIG.PCW_ENET0_ENET0_IO {MIO 16 .. 27} \
      CONFIG.PCW_ENET0_GRP_MDIO_ENABLE {1} \
      CONFIG.PCW_ENET0_GRP_MDIO_IO {MIO 52 .. 53} \
      CONFIG.PCW_ENET0_PERIPHERAL_DIVISOR0 {8} \
      CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {1} \
      CONFIG.PCW_ENET_RESET_ENABLE {1} \
      CONFIG.PCW_ENET_RESET_SELECT {Share reset pin} \
      CONFIG.PCW_EN_EMIO_TTC0 {1} \
      CONFIG.PCW_EN_ENET0 {1} \
      CONFIG.PCW_EN_GPIO {1} \
      CONFIG.PCW_EN_QSPI {1} \
      CONFIG.PCW_EN_SDIO0 {1} \
      CONFIG.PCW_EN_TTC0 {1} \
      CONFIG.PCW_EN_UART1 {1} \
      CONFIG.PCW_EN_USB0 {1} \
      CONFIG.PCW_FCLK0_PERIPHERAL_DIVISOR0 {5} \
      CONFIG.PCW_FCLK0_PERIPHERAL_DIVISOR1 {2} \
      CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ {100.000000} \
      CONFIG.PCW_FPGA1_PERIPHERAL_FREQMHZ {150.000000} \
      CONFIG.PCW_FPGA2_PERIPHERAL_FREQMHZ {50.000000} \
      CONFIG.PCW_GPIO_MIO_GPIO_ENABLE {1} \
      CONFIG.PCW_GPIO_MIO_GPIO_IO {MIO} \
      CONFIG.PCW_I2C_RESET_ENABLE {1} \
      CONFIG.PCW_IOPLL_CTRL_FBDIV {30} \
      CONFIG.PCW_IO_IO_PLL_FREQMHZ {1000.000} \
      CONFIG.PCW_MIO_0_DIRECTION {inout} \
      CONFIG.PCW_MIO_0_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_0_PULLUP {disabled} \
      CONFIG.PCW_MIO_0_SLEW {slow} \
      CONFIG.PCW_MIO_1_DIRECTION {out} \
      CONFIG.PCW_MIO_1_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_1_PULLUP {disabled} \
      CONFIG.PCW_MIO_1_SLEW {fast} \
      CONFIG.PCW_MIO_2_DIRECTION {inout} \
      CONFIG.PCW_MIO_2_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_2_PULLUP {disabled} \
      CONFIG.PCW_MIO_2_SLEW {fast} \
      CONFIG.PCW_MIO_3_DIRECTION {inout} \
      CONFIG.PCW_MIO_3_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_3_PULLUP {disabled} \
      CONFIG.PCW_MIO_3_SLEW {fast} \
      CONFIG.PCW_MIO_4_DIRECTION {inout} \
      CONFIG.PCW_MIO_4_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_4_PULLUP {disabled} \
      CONFIG.PCW_MIO_4_SLEW {fast} \
      CONFIG.PCW_MIO_5_DIRECTION {inout} \
      CONFIG.PCW_MIO_5_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_5_PULLUP {disabled} \
      CONFIG.PCW_MIO_5_SLEW {fast} \
      CONFIG.PCW_MIO_6_DIRECTION {out} \
      CONFIG.PCW_MIO_6_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_6_PULLUP {disabled} \
      CONFIG.PCW_MIO_6_SLEW {fast} \
      CONFIG.PCW_MIO_7_DIRECTION {out} \
      CONFIG.PCW_MIO_7_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_7_PULLUP {disabled} \
      CONFIG.PCW_MIO_7_SLEW {slow} \
      CONFIG.PCW_MIO_8_DIRECTION {out} \
      CONFIG.PCW_MIO_8_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_8_PULLUP {disabled} \
      CONFIG.PCW_MIO_8_SLEW {fast} \
      CONFIG.PCW_MIO_9_DIRECTION {inout} \
      CONFIG.PCW_MIO_9_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_9_PULLUP {disabled} \
      CONFIG.PCW_MIO_9_SLEW {slow} \
      CONFIG.PCW_MIO_10_DIRECTION {inout} \
      CONFIG.PCW_MIO_10_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_10_PULLUP {disabled} \
      CONFIG.PCW_MIO_10_SLEW {slow} \
      CONFIG.PCW_MIO_11_DIRECTION {inout} \
      CONFIG.PCW_MIO_11_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_11_PULLUP {disabled} \
      CONFIG.PCW_MIO_11_SLEW {slow} \
      CONFIG.PCW_MIO_12_DIRECTION {inout} \
      CONFIG.PCW_MIO_12_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_12_PULLUP {disabled} \
      CONFIG.PCW_MIO_12_SLEW {slow} \
      CONFIG.PCW_MIO_13_DIRECTION {inout} \
      CONFIG.PCW_MIO_13_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_13_PULLUP {disabled} \
      CONFIG.PCW_MIO_13_SLEW {slow} \
      CONFIG.PCW_MIO_14_DIRECTION {inout} \
      CONFIG.PCW_MIO_14_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_14_PULLUP {disabled} \
      CONFIG.PCW_MIO_14_SLEW {slow} \
      CONFIG.PCW_MIO_15_DIRECTION {inout} \
      CONFIG.PCW_MIO_15_IOTYPE {LVCMOS 3.3V} \
      CONFIG.PCW_MIO_15_PULLUP {disabled} \
      CONFIG.PCW_MIO_15_SLEW {slow} \
      CONFIG.PCW_MIO_16_DIRECTION {out} \
      CONFIG.PCW_MIO_16_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_16_PULLUP {disabled} \
      CONFIG.PCW_MIO_16_SLEW {fast} \
      CONFIG.PCW_MIO_17_DIRECTION {out} \
      CONFIG.PCW_MIO_17_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_17_PULLUP {disabled} \
      CONFIG.PCW_MIO_17_SLEW {fast} \
      CONFIG.PCW_MIO_18_DIRECTION {out} \
      CONFIG.PCW_MIO_18_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_18_PULLUP {disabled} \
      CONFIG.PCW_MIO_18_SLEW {fast} \
      CONFIG.PCW_MIO_19_DIRECTION {out} \
      CONFIG.PCW_MIO_19_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_19_PULLUP {disabled} \
      CONFIG.PCW_MIO_19_SLEW {fast} \
      CONFIG.PCW_MIO_20_DIRECTION {out} \
      CONFIG.PCW_MIO_20_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_20_PULLUP {disabled} \
      CONFIG.PCW_MIO_20_SLEW {fast} \
      CONFIG.PCW_MIO_21_DIRECTION {out} \
      CONFIG.PCW_MIO_21_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_21_PULLUP {disabled} \
      CONFIG.PCW_MIO_21_SLEW {fast} \
      CONFIG.PCW_MIO_22_DIRECTION {in} \
      CONFIG.PCW_MIO_22_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_22_PULLUP {disabled} \
      CONFIG.PCW_MIO_22_SLEW {fast} \
      CONFIG.PCW_MIO_23_DIRECTION {in} \
      CONFIG.PCW_MIO_23_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_23_PULLUP {disabled} \
      CONFIG.PCW_MIO_23_SLEW {fast} \
      CONFIG.PCW_MIO_24_DIRECTION {in} \
      CONFIG.PCW_MIO_24_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_24_PULLUP {disabled} \
      CONFIG.PCW_MIO_24_SLEW {fast} \
      CONFIG.PCW_MIO_25_DIRECTION {in} \
      CONFIG.PCW_MIO_25_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_25_PULLUP {disabled} \
      CONFIG.PCW_MIO_25_SLEW {fast} \
      CONFIG.PCW_MIO_26_DIRECTION {in} \
      CONFIG.PCW_MIO_26_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_26_PULLUP {disabled} \
      CONFIG.PCW_MIO_26_SLEW {fast} \
      CONFIG.PCW_MIO_27_DIRECTION {in} \
      CONFIG.PCW_MIO_27_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_27_PULLUP {disabled} \
      CONFIG.PCW_MIO_27_SLEW {fast} \
      CONFIG.PCW_MIO_28_DIRECTION {inout} \
      CONFIG.PCW_MIO_28_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_28_PULLUP {disabled} \
      CONFIG.PCW_MIO_28_SLEW {fast} \
      CONFIG.PCW_MIO_29_DIRECTION {in} \
      CONFIG.PCW_MIO_29_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_29_PULLUP {disabled} \
      CONFIG.PCW_MIO_29_SLEW {fast} \
      CONFIG.PCW_MIO_30_DIRECTION {out} \
      CONFIG.PCW_MIO_30_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_30_PULLUP {disabled} \
      CONFIG.PCW_MIO_30_SLEW {fast} \
      CONFIG.PCW_MIO_31_DIRECTION {in} \
      CONFIG.PCW_MIO_31_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_31_PULLUP {disabled} \
      CONFIG.PCW_MIO_31_SLEW {fast} \
      CONFIG.PCW_MIO_32_DIRECTION {inout} \
      CONFIG.PCW_MIO_32_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_32_PULLUP {disabled} \
      CONFIG.PCW_MIO_32_SLEW {fast} \
      CONFIG.PCW_MIO_33_DIRECTION {inout} \
      CONFIG.PCW_MIO_33_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_33_PULLUP {disabled} \
      CONFIG.PCW_MIO_33_SLEW {fast} \
      CONFIG.PCW_MIO_34_DIRECTION {inout} \
      CONFIG.PCW_MIO_34_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_34_PULLUP {disabled} \
      CONFIG.PCW_MIO_34_SLEW {fast} \
      CONFIG.PCW_MIO_35_DIRECTION {inout} \
      CONFIG.PCW_MIO_35_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_35_PULLUP {disabled} \
      CONFIG.PCW_MIO_35_SLEW {fast} \
      CONFIG.PCW_MIO_36_DIRECTION {in} \
      CONFIG.PCW_MIO_36_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_36_PULLUP {disabled} \
      CONFIG.PCW_MIO_36_SLEW {fast} \
      CONFIG.PCW_MIO_37_DIRECTION {inout} \
      CONFIG.PCW_MIO_37_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_37_PULLUP {disabled} \
      CONFIG.PCW_MIO_37_SLEW {fast} \
      CONFIG.PCW_MIO_38_DIRECTION {inout} \
      CONFIG.PCW_MIO_38_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_38_PULLUP {disabled} \
      CONFIG.PCW_MIO_38_SLEW {fast} \
      CONFIG.PCW_MIO_39_DIRECTION {inout} \
      CONFIG.PCW_MIO_39_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_39_PULLUP {disabled} \
      CONFIG.PCW_MIO_39_SLEW {fast} \
      CONFIG.PCW_MIO_40_DIRECTION {inout} \
      CONFIG.PCW_MIO_40_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_40_PULLUP {disabled} \
      CONFIG.PCW_MIO_40_SLEW {fast} \
      CONFIG.PCW_MIO_41_DIRECTION {inout} \
      CONFIG.PCW_MIO_41_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_41_PULLUP {disabled} \
      CONFIG.PCW_MIO_41_SLEW {fast} \
      CONFIG.PCW_MIO_42_DIRECTION {inout} \
      CONFIG.PCW_MIO_42_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_42_PULLUP {disabled} \
      CONFIG.PCW_MIO_42_SLEW {fast} \
      CONFIG.PCW_MIO_43_DIRECTION {inout} \
      CONFIG.PCW_MIO_43_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_43_PULLUP {disabled} \
      CONFIG.PCW_MIO_43_SLEW {fast} \
      CONFIG.PCW_MIO_44_DIRECTION {inout} \
      CONFIG.PCW_MIO_44_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_44_PULLUP {disabled} \
      CONFIG.PCW_MIO_44_SLEW {fast} \
      CONFIG.PCW_MIO_45_DIRECTION {inout} \
      CONFIG.PCW_MIO_45_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_45_PULLUP {disabled} \
      CONFIG.PCW_MIO_45_SLEW {fast} \
      CONFIG.PCW_MIO_46_DIRECTION {in} \
      CONFIG.PCW_MIO_46_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_46_PULLUP {disabled} \
      CONFIG.PCW_MIO_46_SLEW {slow} \
      CONFIG.PCW_MIO_47_DIRECTION {in} \
      CONFIG.PCW_MIO_47_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_47_PULLUP {disabled} \
      CONFIG.PCW_MIO_47_SLEW {slow} \
      CONFIG.PCW_MIO_48_DIRECTION {out} \
      CONFIG.PCW_MIO_48_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_48_PULLUP {disabled} \
      CONFIG.PCW_MIO_48_SLEW {slow} \
      CONFIG.PCW_MIO_49_DIRECTION {in} \
      CONFIG.PCW_MIO_49_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_49_PULLUP {disabled} \
      CONFIG.PCW_MIO_49_SLEW {slow} \
      CONFIG.PCW_MIO_50_DIRECTION {inout} \
      CONFIG.PCW_MIO_50_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_50_PULLUP {disabled} \
      CONFIG.PCW_MIO_50_SLEW {slow} \
      CONFIG.PCW_MIO_51_DIRECTION {inout} \
      CONFIG.PCW_MIO_51_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_51_PULLUP {disabled} \
      CONFIG.PCW_MIO_51_SLEW {slow} \
      CONFIG.PCW_MIO_52_DIRECTION {out} \
      CONFIG.PCW_MIO_52_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_52_PULLUP {disabled} \
      CONFIG.PCW_MIO_52_SLEW {slow} \
      CONFIG.PCW_MIO_53_DIRECTION {inout} \
      CONFIG.PCW_MIO_53_IOTYPE {LVCMOS 1.8V} \
      CONFIG.PCW_MIO_53_PULLUP {disabled} \
      CONFIG.PCW_MIO_53_SLEW {slow} \
      CONFIG.PCW_MIO_TREE_PERIPHERALS {GPIO#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#UART 1#UART 1#GPIO#GPIO#Enet 0#Enet 0} \
      CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#gpio[7]#gpio[8]#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#gpio[14]#gpio[15]#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#wp#cd#tx#rx#gpio[50]#gpio[51]#mdc#mdio} \
      CONFIG.PCW_M_AXI_GP0_FREQMHZ {100} \
      CONFIG.PCW_PCAP_PERIPHERAL_DIVISOR0 {5} \
      CONFIG.PCW_PERIPHERAL_BOARD_PRESET {part0} \
      CONFIG.PCW_PRESET_BANK1_VOLTAGE {LVCMOS 1.8V} \
      CONFIG.PCW_QSPI_GRP_SINGLE_SS_ENABLE {1} \
      CONFIG.PCW_QSPI_GRP_SINGLE_SS_IO {MIO 1 .. 6} \
      CONFIG.PCW_QSPI_PERIPHERAL_DIVISOR0 {5} \
      CONFIG.PCW_QSPI_PERIPHERAL_ENABLE {1} \
      CONFIG.PCW_QSPI_PERIPHERAL_FREQMHZ {200.000000} \
      CONFIG.PCW_QSPI_QSPI_IO {MIO 1 .. 6} \
      CONFIG.PCW_SD0_GRP_CD_ENABLE {1} \
      CONFIG.PCW_SD0_GRP_CD_IO {MIO 47} \
      CONFIG.PCW_SD0_GRP_WP_ENABLE {1} \
      CONFIG.PCW_SD0_GRP_WP_IO {MIO 46} \
      CONFIG.PCW_SD0_PERIPHERAL_ENABLE {1} \
      CONFIG.PCW_SD0_SD0_IO {MIO 40 .. 45} \
      CONFIG.PCW_SDIO_PERIPHERAL_DIVISOR0 {20} \
      CONFIG.PCW_SDIO_PERIPHERAL_FREQMHZ {50} \
      CONFIG.PCW_SDIO_PERIPHERAL_VALID {1} \
      CONFIG.PCW_SINGLE_QSPI_DATA_MODE {x4} \
      CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {1} \
      CONFIG.PCW_TTC0_TTC0_IO {EMIO} \
      CONFIG.PCW_UART1_PERIPHERAL_ENABLE {1} \
      CONFIG.PCW_UART1_UART1_IO {MIO 48 .. 49} \
      CONFIG.PCW_UART_PERIPHERAL_DIVISOR0 {20} \
      CONFIG.PCW_UART_PERIPHERAL_FREQMHZ {50} \
      CONFIG.PCW_UART_PERIPHERAL_VALID {1} \
      CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.41} \
      CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.411} \
      CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.341} \
      CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.358} \
      CONFIG.PCW_UIPARAM_DDR_DEVICE_CAPACITY {2048 MBits} \
      CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {0.025} \
      CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {0.028} \
      CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {0.001} \
      CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {0.001} \
      CONFIG.PCW_UIPARAM_DDR_DRAM_WIDTH {16 Bits} \
      CONFIG.PCW_UIPARAM_DDR_FREQ_MHZ {533.333313} \
      CONFIG.PCW_UIPARAM_DDR_PARTNO {MT41J128M16 HA-15E} \
      CONFIG.PCW_UIPARAM_DDR_T_FAW {45.0} \
      CONFIG.PCW_UIPARAM_DDR_T_RAS_MIN {36.0} \
      CONFIG.PCW_UIPARAM_DDR_T_RC {49.5} \
      CONFIG.PCW_UIPARAM_DDR_USE_INTERNAL_VREF {1} \
      CONFIG.PCW_USB0_PERIPHERAL_ENABLE {1} \
      CONFIG.PCW_USB0_USB0_IO {MIO 28 .. 39} \
      CONFIG.PCW_USB_RESET_ENABLE {1} \
      CONFIG.PCW_USB_RESET_SELECT {Share reset pin}
    ] [get_bd_cells processing_system7_0]
    # User Configuration
    set_property -dict \
    [ list \
      CONFIG.PCW_USB0_PERIPHERAL_ENABLE {0} \
      CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {0} \
      CONFIG.PCW_USE_FABRIC_INTERRUPT {1} \
      CONFIG.PCW_IRQ_F2P_INTR {1} \
    ] [get_bd_cells processing_system7_0]
    
    create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 proc_sys_reset_0
    
    create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_0
    set_property -dict \
    [ list \
       CONFIG.NUM_MI {3} \
    ] [get_bd_cells axi_interconnect_0]
    
    create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_0
    set_property -dict [list \
      CONFIG.CONST_VAL {0} \
      CONFIG.CONST_WIDTH {8} \
    ] [get_bd_cells xlconstant_0]
    
    create_bd_cell -type module -reference axi_gpio_zed axi_gpio_zed_0
    
    create_bd_cell -type module -reference axi_identification axi_identification_0
    
    create_bd_cell -type module -reference axi_register_bank axi_register_bank_0
    
    ##############################
    # Create interface connections
    
    connect_bd_intf_net [get_bd_intf_pins processing_system7_0/DDR] \
      [get_bd_intf_ports DDR]
    
    connect_bd_intf_net [get_bd_intf_pins processing_system7_0/FIXED_IO] \
      [get_bd_intf_ports FIXED_IO]
    
    connect_bd_intf_net [get_bd_intf_pins processing_system7_0/M_AXI_GP0] \
      [get_bd_intf_pins axi_interconnect_0/S00_AXI]
    
    connect_bd_intf_net [get_bd_intf_pins axi_interconnect_0/M00_AXI] \
      [get_bd_intf_pins axi_identification_0/s_axi]
    
    connect_bd_intf_net [get_bd_intf_pins axi_interconnect_0/M01_AXI] \
      [get_bd_intf_pins axi_gpio_zed_0/s_axi]
    
    connect_bd_intf_net [get_bd_intf_pins axi_interconnect_0/M02_AXI] \
      [get_bd_intf_pins axi_register_bank_0/s_axi]
    
    #########################
    # Create port connections
    
    connect_bd_net [get_bd_ports id_description] \
      [get_bd_pins axi_identification_0/id_description]
    
    connect_bd_net [get_bd_ports id_company] \
      [get_bd_pins axi_identification_0/id_company]
    
    connect_bd_net [get_bd_ports id_author] \
      [get_bd_pins axi_identification_0/id_author]
    
    connect_bd_net [get_bd_ports id_version] \
      [get_bd_pins axi_identification_0/id_version]
    
    connect_bd_net [get_bd_ports id_timestamp] \
      [get_bd_pins axi_identification_0/id_timestamp]
    
    connect_bd_net [get_bd_ports id_hash] \
      [get_bd_pins axi_identification_0/id_hash]
    
    connect_bd_net  [get_bd_pins processing_system7_0/FCLK_CLK0] \
      [get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] \
      [get_bd_pins proc_sys_reset_0/slowest_sync_clk] \
      [get_bd_pins axi_interconnect_0/ACLK] \
      [get_bd_pins axi_interconnect_0/S00_ACLK] \
      [get_bd_pins axi_interconnect_0/M00_ACLK] \
      [get_bd_pins axi_interconnect_0/M01_ACLK] \
      [get_bd_pins axi_interconnect_0/M02_ACLK] \
      [get_bd_pins axi_identification_0/aclk] \
      [get_bd_pins axi_gpio_zed_0/aclk] \
      [get_bd_pins axi_register_bank_0/aclk]
    
    connect_bd_net [get_bd_pins processing_system7_0/FCLK_RESET0_N] \
      [get_bd_pins proc_sys_reset_0/ext_reset_in]
    
    connect_bd_net [get_bd_pins proc_sys_reset_0/interconnect_aresetn] \
      [get_bd_pins axi_interconnect_0/ARESETN]
    
    connect_bd_net [get_bd_pins proc_sys_reset_0/peripheral_aresetn] \
      [get_bd_pins axi_interconnect_0/S00_ARESETN] \
      [get_bd_pins axi_interconnect_0/M00_ARESETN] \
      [get_bd_pins axi_interconnect_0/M01_ARESETN] \
      [get_bd_pins axi_interconnect_0/M02_ARESETN] \
      [get_bd_pins axi_identification_0/aresetn] \
      [get_bd_pins axi_gpio_zed_0/aresetn] \
      [get_bd_pins axi_register_bank_0/aresetn]
    
    connect_bd_net [get_bd_pins axi_gpio_zed_0/leds] \
      [get_bd_ports leds]
    
    connect_bd_net [get_bd_ports buttons] \
      [get_bd_pins axi_gpio_zed_0/buttons]
    
    connect_bd_net [get_bd_ports switches] \
      [get_bd_pins axi_gpio_zed_0/switches]
    
    connect_bd_net [get_bd_pins axi_gpio_zed_0/interrupt] \
      [get_bd_pins processing_system7_0/IRQ_F2P]
    
    connect_bd_net [get_bd_pins xlconstant_0/dout] \
      [get_bd_pins axi_gpio_zed_0/leds_in]
    
    #########################
    # Create address segments
    
    assign_bd_address -offset 0x40000000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_identification_0/s_axi/reg0] -force
    assign_bd_address -offset 0x40010000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_gpio_zed_0/s_axi/reg0] -force
    assign_bd_address -offset 0x40020000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_register_bank_0/s_axi/reg0] -force
    
    #################################################
    # Regenerate, validate, save & close block design
    
    #regenerate_bd_layout
    validate_bd_design
    save_bd_design
    #close_bd_design $bdname
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_create_system_bd.tcl -O common/fw/src/script/swuk_create_system_bd.tcl
    
    TBD
    #### Part 11 - Rename & enhance Xilinx tool selection script ####

    28. Rename & enhance the script

    Rename the script & edit it to add the ability of passing in separate version arguments for both Vitis/Vivado & PetaLinux. This will allow the script to run with or without user interaction and also allow different versions of the tools to be selected.

    Requirements :-
    1. Rename file to use the new swuk_ prefix & remove the .sh extension.
    2. Update header to reflect field changes - File, Version, History & Description.
    3. Add in new functionality.
    4. Remove commented out code.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/other/src/script/xilinx.sh common/other/src/script/swuk_xilinx
    
    steve@Desktop:~/swuk_tutorial$ subl common/other/src/script/swuk_xilinx
    

    swuk_xilinx

    #!/bin/bash
    
    #
    # File .......... swuk_xilinx
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/other/src/script/xilinx.sh
    # Description ...
    #   Determine and list which Xilinx tools are available and let the user select
    # the require ones to use. Does not reverse any previously selected tools upon
    # selecting news ones so it is cleaner to use in a new Terminal session.
    #
    # Examples :-
    #
    # Bring up menu for user selection of tools
    # source swuk_xilinx
    #
    # Auto-select 2024.2 Vivado, SDK, Vitis & PetaLinux
    # source swuk_xilinx 2024.2
    #
    # Auto-select 2024.4 Vivado, SDK & Vitis & 2021.2 PetaLinux
    # source swuk_xilinx 2024.2 2021.2
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Source the setup scripts for the selected tools.
    # Arguments ...... None
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... lookup, tools
    # Shared (Out) ... None
    source_tools() {
      # Declare local variables
      local -i  tindex         # Int: Current tool index (into c_tools array)
      local     loc            # Str: Path to the script directory
      local     bin            # Str: Binary location
    
      echo -e "\nTools are as follows :-\n"
    
      # Iterate through list of tools
      for ((tindex=0; tindex<${#c_tools[@]}; tindex++)); do
        # Source tool setup script
        echo -ne "${c_cyan}${c_tools[tindex]}${c_normal} @ "
        if [[ " ${lookup[*]} " =~ " ${tools[tindex]}-${tindex} " ]]; then
          loc="${c_install}/${c_tools[tindex]}/${tools[tindex]}"
          if [ $((tindex+1)) -eq ${#c_tools[@]} ]; then  # PetaLinux
            source "${loc}/${c_settings[tindex]}" "${loc}/tool" > /dev/null 2>&1
          else
            source "${loc}/${c_settings[tindex]}" > /dev/null 2>&1
          fi
          # Display tool location
          bin=$(which "${c_executable[tindex]}")
          [ -z ${bin} ] && echo -ne "${c_red}Not found!" || echo -ne "${c_green}${bin}"
        else
          echo -ne "${c_red}Not available!"
        fi
        echo -e ${c_normal}
      done
    }
    
    
    ################################################################################
    # Display table of the available tools.
    # Arguments ...... None
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... installs*, lookup*, tools
    # Shared (Out) ... installs*, lookup*
    display_tools() {
      # Declare local variables
      local -i index    # Int: Current install index (into installs array)
      local    install  # Str: Current install name (from installs array)
      local -i tindex   # Int: Current tool index (into c_tools array)
      local    tool     # Str: Current tool name (from c_tools array)
    
      # Check for tool installation directory
      if [ -d ${c_install} ]; then
    
        # Get a list of all subdirectories
        readarray -t installs < <(find ${c_install}/${c_tools[0]} -mindepth 1 -maxdepth 1 \( -type l -o -type d \) -printf '%f\n' | sort )
        [[ ${#installs[@]} -eq 0 ]] && echo "Tools installation directory (${c_install}/${c_tools[0]}) does not contain any ${c_tools[0]} subdirectories!" && return 1
    
        # Obtain & display table of availability tools
        [[ ${tools[c_vivado]} == "" ]] &&
          echo -e "\nXilinx tools @ ${c_install} :-\n"
    
        # Iterate through list of subdirectories
        for ((index=0; index<${#installs[@]}; index++)); do
          install=${installs[${index}]}
          # Display table
          [[ ${tools[c_vivado]} == "" ]] &&
            printf "%2.0f) %s" "$((index+1))" "${install}"
          # Iterate through list of tools
          for ((tindex=0; tindex<${#c_tools[@]}; tindex++)); do
            # Add available tool (version + type) to array
            local available=$([ -d "${c_install}/${c_tools[${tindex}]}/${install}" ]; echo $?)
            [ ${available} -eq 0 ] && lookup+=("${install}-${tindex}")
            # Display table
            [[ ${tools[c_vivado]} == "" ]] &&
              echo -ne " - ${c_pass_fail[${available}]}" &&
              echo -ne "${c_tools[${tindex}]}" &&
              echo -ne "${c_normal}"
          done
          [[ ${tools[c_vivado]} == "" ]] &&
            echo
        done
    
      else
        echo "Tools installation directory (${c_install}) not found!"
        return 1
      fi
    }
    
    
    ################################################################################
    # User selection of tools.
    # Arguments ...... None
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... installs, tools*
    # Shared (Out) ... tools*
    user_select() {
      # Declare local variables
      local -i valid          # Int: Input valid (0=no, 1=yes)
      local    input=""       # Str: User input (raw)
      local -a selections=()  # Arr: User input split into array
      local -i index          # Int: Index into selections array
    
      echo -e "\nq) Quit\n"
    
      # Clear selected tool versions
      tools=("" "" "" "")
    
      while true; do
    
        valid=1
    
        # Get user selection(s)
        read -e -p "Select tool(s) required [1-${#installs[@]}] or quit : " -i "${#installs[@]} ${#installs[@]}" input
    
        if [[ ("${input}" == "q") || ("${input}" == "Q") ]]; then
          # Leave
          echo "Quiting without selection!"
          break
        fi
    
        # Split user selection(s) into array
        read -ra selections <<< "${input}"
    
        # Initial selection(s) check
        if [[ (${#selections[@]} -eq 1) || (${#selections[@]} -eq 2) ]]; then
          # Iterate through selection(s)
          for ((index=0; index<${#selections[@]}; index++)); do
            if ! [[ "${selections[index]}" =~ ^[0-9]+$ ]] ; then
              echo -e "${c_red}Selection $((index+1)) (${selections[index]}) is not a number!${c_normal}"
              valid=0
              break
            elif [[ (${selections[index]} -lt 1) || (${selections[index]} -gt ${#installs[@]}) ]]; then
              echo -e "${c_red}Selection $((index+1)) (${selections[index]}) is out of range!${c_normal}"
              valid=0
              break
            fi
          done
        else
          echo -e "${c_red}Wrong number of selections, expecting 1 or 2!${c_normal}"
          valid=0
        fi
    
        # Obtain versions from selections
        if [[ (${valid} -eq 1) && (${index} -eq ${#selections[@]}) ]]; then
          if [[ ${#selections[@]} -eq 1 ]]; then
            tools[c_vivado]=${installs[$((selections[0]-1))]}
            tools[c_sdk]=${tools[c_vivado]}
            tools[c_vitis]=${tools[c_vivado]}
            tools[c_petalinux]=${tools[c_vivado]}
          else
            tools[c_vivado]=${installs[$((selections[0]-1))]}
            tools[c_sdk]=${tools[c_vivado]}
            tools[c_vitis]=${tools[c_vivado]}
            tools[c_petalinux]=${installs[$((selections[1]-1))]}
          fi
          break
        fi
    
      done
    }
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... installs*, lookup*, tools*
    main()
    {
      # Exit if script wasn't sourced
      if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
        echo "Script needs to be sourced : source ${BASH_SOURCE[0]}"
        exit 1
      fi
    
      # Declare tool specific constants
      local -r  c_install="/opt/Xilinx"
      local -ra c_tools=("Vivado" "SDK" "Vitis" "PetaLinux")
      local -ra c_settings=("settings64.sh" "settings64.sh" "settings64.sh" "tool/settings.sh")
      local -ra c_executable=("vivado" "xsdk" "vitis" "petalinux-build")
    
      # Declare tools array index constants
      local -r  c_vivado=0
      local -r  c_sdk=1
      local -r  c_vitis=2
      local -r  c_petalinux=3
    
      # Declare terminal colour constants
      local -r  c_red='\033[0;31m'
      local -r  c_green='\033[0;32m'
      local -r  c_blue='\033[0;34m'
      local -r  c_cyan='\033[0;36m'
      local -r  c_normal='\033[0m'
      local -r  c_pass_fail=(${c_green} ${c_red})
    
      # Declare constants
      local -r  c_opthelp="--help"   # Str: Help option name
      local -Ar c_options=(          # ARR: Options & associated help information
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(         # Arr: Help options display order
        ${c_opthelp}
      )
      local -ar argv=(${@})          # Arr: Get argument values (space-separated) into array
    
      # Declare variables
      local     arg                  # Str: Current argument from argv array
      local     option               # Str: Current option from c_optorder array
    
      # Declare shared variables
    
      local -a  installs=()          # Arr: Array of install directories (alphanumeric order)
      local -a  lookup=()            # Arr: Array of available tools
      local -a  tools=("" "" "" "")  # Arr: Requested tools (Vivado, SDK, Vitis, PetaLinux)
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) [ALL]... [FIRMWARE/SOFTWARE EMBEDDED]... [OPTION]..."
        echo "Select which version of the Xilinx tools to use; FIRMWARE=Vivado, SOFTWARE=SDK or Vitis & EMBEDDED=PetaLinux."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        return 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] && echo "Option (${arg}) is not recognised!" && return 1
        elif [[ "${arg}" =~ ^20[0-9]{2}\.[1-4]{1}$ ]]; then  # Version NNNN.N
          if [ -v ${tools[c_vivado]} ]; then
            tools[c_vivado]=${arg}
          elif [ -v ${tools[c_petalinux]} ]; then
            tools[c_petalinux]=${arg}
          else
            echo "Too many arguments found, expecting 1 or 2!"
            return 1
          fi
        else
          echo "Unexpected argument (${arg}) found!"
          return 1
        fi
      done
    
      # Ripple single (vivado) argument into sdk, vitis & petalinux
      tools[c_sdk]=${tools[c_vivado]}
      tools[c_vitis]=${tools[c_vivado]}
      [[ (-n ${tools[c_vivado]}) && (! -n ${tools[c_petalinux]}) ]] &&
        tools[c_petalinux]=${tools[c_vivado]}
    
      display_tools
      [[ ! -n ${tools[c_vivado]} ]] && user_select
      [[ -n ${tools[c_vivado]} ]] && source_tools
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    return 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/other/src/script/swuk_xilinx -O common/other/src/script/swuk_xilinx
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/other/src/script/swuk_xilinx
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/other/src/script/swuk_xilinx
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_xilinx --help
    Usage: bash [ALL]... [FIRMWARE/SOFTWARE EMBEDDED]... [OPTION]...
    Select which version of the Xilinx tools to use; FIRMWARE=Vivado, SOFTWARE=SDK or Vitis & EMBEDDED=PetaLinux.
    
          --help        Display this help and exit.
    
    Check everything is working as expected.
    steve@Desktop:~/swuk_tutorial$ swuk_xilinx 2024.2 2021.2
    
    Xilinx tools @ /opt/Xilinx :-
    
    1) 2021.2 - Vivado - SDK - Vitis - PetaLinux
    2) 2021.1 - Vivado - SDK - Vitis - PetaLinux
    3) 2024.2 - Vivado - SDK - Vitis - PetaLinux
    
    q) Quit
    
    Select tools required or exit : 1 / 3
    
    Tools are as follows :-
    
    vivado @ /opt/Xilinx/Vivado/2024.2/bin/vivado
    vitis @ /opt/Xilinx/Vitis/2024.2/bin/vitis
    petalinux-build @ /opt/Xilinx/PetaLinux/2021.2/tool/tools/common/petalinux/bin/petalinux-build
    
    Looks good! Vivado & Vitis on 2024.2. PetaLinux on 2021.2.

    Check the SWUK shell initialisation script is working as expected.
    steve@Desktop:~/swuk_tutorial$ source ~/.bashrc
    steve@Desktop:~/swuk_tutorial$ which vivado vitis petalinux-build
    /opt/Xilinx/Vivado/2021.2/bin/vivado
    /opt/Xilinx/Vitis/2021.2/bin/vitis
    /opt/Xilinx/PetaLinux/2021.2/tool/tools/common/petalinux/bin/petalinux-build
    
    Looks good! All tools on 2021.2.
    #### Part 12 - Rename & enhance create project structure script ####

    29. Rename & enhance the script

    Edit the script to make the following changes.

    Requirements :-
    1. Rename file to use the new swuk_ prefix, remove the _structure & remove the .sh extension.
    2. Update header to reflect field changes - File, Version History & Description.
    3. Remove creation of vivado & vitis directories.
    4. Remove creation of C source starter file.
    5. Add option to create a baseline project.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/other/src/script/create_project_structure.sh common/other/src/script/swuk_create_project
    
    steve@Desktop:~/swuk_tutorial$ subl common/other/src/script/swuk_create_project
    

    swuk_create_project

    #!/bin/bash
    
    #
    # File .......... swuk_create_project
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 24 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/other/src/script/create_project_structure.sh
    # Description ...
    #   Simple script to create a possible project directory structure for combined
    # firmware, hardware, operating system & software development. Very much work
    # in progress and by all means not a golden solution to anything. Now includes
    # the option of creating a baseline project. Edit the 'User constants' &
    # 'Directory structure' to suit.
    #
    
    # Strict
    set -euo pipefail
    
    # User constants
    declare -r c_author="Steve Haywood"                # Str: Author
    declare -r c_company="SpaceWire UK"                # Str: Company
    declare -r c_website="http://www.spacewire.co.uk"  # Str: Website
    declare -r c_project="Zedboard Baseline"           # Str: Project
    declare -r c_tutorial="SpaceWire UK Tutorial"      # Str: Tutorial
    declare -r c_date=$(date '+%d %b %Y')              # Str: Date
    declare -r c_version="1.0"                         # Str: Version
    
    # Directory structure
    declare -ra c_structure=(  # Arr: Project subdirectories
      # Firmware
      "fw/src/constraint"
      "fw/src/design"
      "fw/src/diagram"
      "fw/src/document"
      "fw/src/ip"
      "fw/src/ip_repo"
      "fw/src/other"
      "fw/src/script"
      "fw/src/testbench"
      # Hardware
      "hw/src/schematic"
      # Operating System
      "os/src/other"
      # Software
      "sw/src/c"
      "sw/src/other"
      "sw/src/script"
    )
    
    
    ################################################################################
    # Create directory structure.
    # Arguments ...... $1 ... Str: Project directory
    # Return ......... None
    # Shared (In) .... None
    # Shared (Out) ... None
    create_structure()
    {
      # Declare local constants
      local -r  projdir=$1  # Str: Project directory
    
      # Declare local variables
      local     dir         # Str: Current directory from c_structure array
    
      # Echo operation
      echo "Creating project directory structure..."
    
      # Iterate through directories
      for dir in "${c_structure[@]}"
      do
        mkdir -p "${projdir}/${dir}"
      done
    }
    
    
    ################################################################################
    # Create baseline project.
    # Arguments ...... $1 ... Str: Project directory
    #                  $2 ... Str: Author
    #                  $3 ... Str: Company
    #                  $4 ... Str: Website
    #                  $5 ... Str: Project
    #                  $6 ... Str: Tutorial
    # Return ......... None
    # Shared (In) .... None
    # Shared (Out) ... None
    create_baseline()
    {
      # Declare local constants
      local -r projdir=$1   # Str: Project directory
      local -r author=$2    # Str: Author
      local -r company=$3   # Str: Company
      local -r website=$4   # Str: Website
      local -r project=$5   # Str: Project
      local -r tutorial=$6  # Str: Tutorial
    
      [[ -z ${tutorial} ]] && local -r combo="$project" || local -r combo="$project ($tutorial)"
    
      # Echo operation
      echo "Creating baseline project..."
    
      # Create constraints file
    cat << EOF > ${projdir}/fw/src/constraint/${projdir}.xdc
    #
    # File .......... ${projdir}.xdc
    # Author ........ ${author}
    # Website ....... ${website}
    # Project ....... ${combo}
    # Date .......... ${c_date}
    # Version ....... ${c_version}
    # Description ...
    #   Top level pin & timing constraints.
    #
    
    
    # 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]];
    
    
    # False Paths
    set_false_path -to [get_pins {system_i/axi_gpio_zed_0/inst/genblk1[*].meta_hardener/meta_reg/D}]
    EOF
    
      # Create top-level design file
    cat << EOF > ${projdir}/fw/src/design/${projdir}.sv
    //
    // File .......... ${projdir}.sv
    // Author ........ ${author}
    // Website ....... ${website}
    // Project ....... ${combo}
    // Date .......... ${c_date}
    // Version ....... ${c_version}
    // Description ...
    //   Top level wrapper for the system block design.
    //
    
    
    timeunit      1ns;
    timeprecision 1ps;
    
    
    module ${projdir} #
    (
      // 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 automatic [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
    EOF
    
      # Create project file
    cat << EOF > ${projdir}/fw/project.txt
    ${project}
    ${company}
    ${author}
    ${c_version}
    EOF
    
      # Copy .gitignore
      cp ${swuk_tutorial}/common/.gitignore ${projdir}
    }
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare constants
      local -r  c_optbase="--baseline"  # Str: Baseline option name
      local -r  c_opthelp="--help"      # Str: Help option name
      local -Ar c_options=(             # ARR: Options & associated help information
        [${c_optbase}]="Create a baseline project inside the project directory structure."
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(            # Arr: Help options display order
        ${c_optbase}
        ${c_opthelp}
      )
      local -ar argv=(${@})             # Arr: Get argument values (space-separated) into array
    
      # Declare variables
      local     arg                     # Str: Current argument from argv array
      local     option                  # Str: Current option from c_optorder array
      local     projdir=""              # Str: Project directory [arg] (mandatory)
    
      local     author                  # Str: Author
      local     company                 # Str: Company
      local     website                 # Str: Website
      local     project                 # Str: Project
      local     tutorial                # Str: Tutorial
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) PROJECT-DIRECTORY... [OPTION]..."
        echo "Create a new SWUK project (directory structure) in the PROJECT-DIRECTORY & optionally generate a baseline project."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] && echo "Option (${arg}) is not recognised!" >&2 && exit 1
        elif [[ -z ${projdir} ]]; then  # Project directory
          projdir=${arg}
          [[ -d ${projdir} ]] && echo "Project directory (${arg}) already exists!" >&2 && exit 1
        else
          echo "Unexpected argument (${arg}) found!" >&2
          exit 1
        fi
      done
    
      # Further check the arguments
      [[ -z ${projdir} ]] && echo "No project directory specified!" >&2 && exit 1
    
      if [[ " ${argv[*]} " =~ " ${c_optbase} " ]]; then
        echo "Enter details for the header blocks..."
        echo "File .......... ${projdir}"
        read -e -p "Author ........ " -i "${c_author}" author
        [[ -z ${author} ]] && echo "No author specified!" >&2 && exit 1
        read -e -p "Company ....... " -i "${c_company}" company
        [[ -z ${company} ]] && echo "No company specified!" >&2 && exit 1
        read -e -p "Website ....... " -i "${c_website}" website
        [[ -z ${website} ]] && echo "No website specified!" >&2 && exit 1
        read -e -p "Project ....... " -i "${c_project}" project
        [[ -z ${project} ]] && echo "No project specified!" >&2 && exit 1
        read -e -p "Tutorial ...... " -i "${c_tutorial}" tutorial
        echo "Date .......... ${c_date}"
        echo "Version ....... ${c_version}"
      fi
    
      # Create directory structure.
      create_structure ${projdir}
    
      # Create baseline project.
      [[ " ${argv[*]} " =~ " ${c_optbase} " ]] &&
        create_baseline "${projdir}" "${author}" "${company}" "${website}" \
                        "${project}" "${tutorial}"
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/other/src/script/swuk_create_project -O common/other/src/script/swuk_create_project
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/other/src/script/swuk_create_project
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/other/src/script/swuk_create_project
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_create_project --help
    Usage: swuk_create_project PROJECT-DIRECTORY... [OPTION]...
    Create a new SWUK project (directory structure) in the PROJECT-DIRECTORY & optionally generate a baseline project.
    
          --baseline    Create a baseline project inside the project directory structure.
          --help        Display this help and exit.
    
    Check everything is working as expected. Create a new baseline project (edit fields as required).
    steve@Desktop:~/swuk_tutorial$ swuk_create_project zedboard_baseline --baseline
    Enter details for the header blocks...
    File .......... zedboard_baseline
    Author ........ Steve Haywood
    Company ....... SpaceWire UK
    Website ....... http://www.spacewire.co.uk
    Project ....... Zedboard Baseline
    Tutorial ...... SpaceWire UK Tutorial
    Date .......... 25 Jan 2026
    Version ....... 1.0
    Creating project directory structure...
    Creating baseline project...
    steve@Desktop:~/swuk_tutorial$ tree -a zedboard_baseline
    zedboard_baseline
    ├── fw
    │   ├── project.txt
    │   └── src
    │       ├── constraint
    │       │   └── zedboard_baseline.xdc
    │       ├── design
    │       │   └── zedboard_baseline.sv
    │       ├── diagram
    │       ├── document
    │       ├── ip
    │       ├── ip_repo
    │       ├── other
    │       ├── script
    │       └── testbench
    ├── .gitignore
    ├── hw
    │   └── src
    │       └── schematic
    ├── os
    │   └── src
    │       └── other
    └── sw
        └── src
            ├── c
            ├── other
            └── script
    
    22 directories, 4 files
    
    Looks good! Structure is as expected & baseline files are present.
    #### Part 13 - Rename & update create Vivado project scripts ####

    30. Rename & update create Vivado project Bash script

    Edit the script to make the following changes.

    Requirements :-
    1. Rename file to use the new swuk_ prefix & remove the .sh extension.
    2. Update header to reflect field changes - File, Version, History & Description.
    3. Improve the source to include :-
      1. Account for the new swuk_ prefix.
      2. Creation of the vivado directory (cd into this to run the TCL script).
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/fw/src/script/create_vivado_project.sh common/fw/src/script/swuk_create_vivado_project
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_create_vivado_project
    

    swuk_create_vivado_project

    #!/bin/bash
    
    #
    # File .......... swuk_create_vivado_project
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/fw/src/script/create_vivado_project.sh
    # Description ...
    #   Very simple script to launch Vivado and run the TCL script. Should be run
    # in the project host directory that contains the fw & sw subdirectories, for
    # example :-
    #
    # user@host:~/swuk_tutorial/project$ swuk_create_vivado_project
    # user@host:~/swuk_tutorial/project$ swuk_create_vivado_project --build
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare argument constants
      local -r  c_optbuild="--build"  # Str: Build option name
      local -r  c_opthelp="--help"    # Str: Help option name
      local -Ar c_options=(           # ARR: Options & associated help information
        [${c_optbuild}]="Build the project after creation."
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(          # Arr: Help options display order
        ${c_optbuild}
        ${c_opthelp}
      )
      local -ar argv=(${@})           # Arr: Get argument values (space-separated) into array
    
      # Declare argument variables
      local     arg                   # Str: Current argument from argv array
      local     option                # Str: Current option from c_optorder array
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) [OPTION]..."
        echo "Create a new Vivado project using the $(basename ${0}).tcl script & optionally build it."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        else
          echo "Unexpected argument (${arg}) found!" >&2
          exit 1
        fi
      done
    
      # Declare operational constants & variables
      local -r c_dir_script=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
                                      # Str: Script location directory
      local -r c_dir_fw="fw"          # Str: Firmware base directory
      local -r c_dir_vivado="vivado"  # Str: Vivado project directory
      local    tclargs=""             # Str: Arguments sent to TCL script
    
      # Sanity check directory structure
      [ ! -d ${c_dir_fw} ] &&
        echo "Firmware base directory ${c_dir_fw} does not exists!" >&2 &&
        exit 1
    
      [ -d ${c_dir_fw}/${c_dir_vivado} ] &&
        echo "Vivado project directory ${c_dir_fw}/${c_dir_vivado} already exists!" >&2 &&
        exit 1
    
      # Create & move into the new project directory
      mkdir -p ${c_dir_fw}/${c_dir_vivado}
      cd ${c_dir_fw}/${c_dir_vivado}
    
      # Check for build argument
      [[ " ${argv[*]} " =~ " ${c_optbuild} " ]] && tclargs="-tclargs build"
    
      # Launch Vivado
      vivado -nojournal -nolog -notrace -mode gui -source "${c_dir_script}/$(basename ${0}).tcl" ${tclargs} &
    
      # Move back out of project directory
      cd ../..
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_create_vivado_project -O common/fw/src/script/swuk_create_vivado_project
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/script/swuk_create_vivado_project
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/fw/src/script/swuk_create_vivado_project
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_create_vivado_project --help
    Usage: swuk_create_vivado_project [OPTION]...
    Create a new Vivado project using the swuk_create_vivado_project.tcl script & optionally build it.
    
          --build       Build the project after creation.
          --help        Display this help and exit.
    

    31. Rename & update create vivado project TCL script

    Edit the script to make the following changes.

    Requirements :-
    1. Rename file to use the new swuk_ prefix.
    2. Update header to reflect field changes - File, Version & History.
    3. Edit the source :-
      1. Remove creation of the vivado directory, which is now done is the bash script (do this to let vivado run inside its own directory, keeping the vivado bile from spewing out).
      2. Replace selective generation of block design wrappers with a generate all wrappers solution.
      3. Sort out the issue with the top-level not being selected correctly, done for both the design and testbench.
      4. Make use of shared source files in the common area.
      5. Adjust the path where the .xsa gets created such that it matches the Vivado default.
      6. Add a new scripted approach to create a block design.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/fw/src/script/create_vivado_project.tcl common/fw/src/script/swuk_create_vivado_project.tcl
    
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_create_vivado_project.tcl
    

    swuk_create_vivado_project.tcl

    #
    # File .......... swuk_create_vivado_project.tcl
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/fw/src/script/create_vivado_project.tcl
    # Description ...
    #   Simple Tcl script to create a new Vivado project, import external
    # sources and (optionally) generate the bitstream & export the hardware.
    #
    
    puts "Set Global variables"
    set dir_common      "../../../common/fw/src"
    set dir_user        "../src"
    set dir_diagram     "$dir_user/diagram"
    set dir_design_com  "$dir_common/design"
    set dir_design      "$dir_user/design"
    set dir_ip          "$dir_user/ip"
    set dir_ip_repo     "$dir_user/ip_repo"
    set dir_constraint  "$dir_user/constraint"
    set dir_testbench   "$dir_user/testbench"
    set dir_script_com  "$dir_common/script"
    set dir_script      "$dir_user/script"
    set parts [split [pwd] "/"]
    set project [lindex $parts end-2]
    
    puts "NOTE: Create project"
    create_project $project . -part xc7z020clg484-1
    
    puts "NOTE: Add local IP Repository"
    if {[file exist $dir_ip_repo]} {
      set_property  ip_repo_paths  $dir_ip_repo [current_project]
      update_ip_catalog
    }
    
    puts "NOTE: Add scripts"
    add_files -quiet -norecurse -fileset utils_1 $dir_script_com
    set tcls [get_files *.tcl]
    foreach tcl $tcls {
      set tcl_name [file tail $tcl]
      switch $tcl_name {
        "swuk_pre_synth.tcl" {
          set_property STEPS.SYNTH_DESIGN.TCL.PRE [ get_files $tcl -of [get_fileset utils_1] ] [get_runs synth_1]
        }
        "swuk_post_bit.tcl" {
          set_property STEPS.WRITE_BITSTREAM.TCL.POST [ get_files $tcl -of [get_fileset utils_1] ] [get_runs impl_1]
        }
        default {
          puts "NOTE: Unfamiliar tcl script found - $tcl_name"
        }
      }
    }
    
    puts "NOTE: Add block designs"
    add_files -quiet -norecurse [glob -nocomplain "$dir_diagram/*/*.bd"]
    
    puts "NOTE: Add design sources"
    add_files -quiet -norecurse -fileset sources_1 $dir_design_com
    add_files -quiet -norecurse -fileset sources_1 $dir_design
    
    puts "NOTE: Add IPs"
    add_files -quiet [glob -nocomplain "$dir_ip/*/*.xci"]
    
    puts "NOTE: Add constraints"
    add_files -quiet -norecurse -fileset constrs_1 $dir_constraint
    
    puts "NOTE: Add testbench sources"
    add_files -quiet -norecurse -fileset sim_1 $dir_testbench
    
    puts "NOTE: Recreate (by opening) all block designs..."
    set bds [glob -nocomplain "$dir_diagram/*/*.bd"]
    foreach bd $bds {
      puts "NOTE: Open \"$bd\" block design"
      open_bd_design $bd
    }
    
    puts "NOTE: Generate all block design wrappers..."
    set bds [get_bd_designs -quiet]
    foreach bd $bds {
      set bd_file [get_files $bd.bd]
      set bd_path [file dirname $bd_file]
      set wrapper_file [make_wrapper -files $bd_file -top]
      puts "NOTE: Generate \"$bd\" block design wrapper"
      add_files -quiet -norecurse $bd_path/hdl
    }
    
    puts "NOTE: Close all block designs..."
    set bds [get_bd_designs -quiet]
    foreach bd $bds {
      puts "NOTE: Close \"$bd\" block design"
      close_bd_design $bd
    }
    
    puts "NOTE: Top-level detection for design"
    set parts [split [pwd] "/"]
    set project [lindex $parts [expr {[llength $parts] - 3}]]
    set tops [find_top]
    if {$project in $tops} {
      puts "NOTE: Set $project as design top-level"
      set_property top $project [get_filesets sources_1]
    } elseif {"system_wrapper" in $tops} {
      puts "NOTE: Set system_wrapper as design top-level"
      set_property top "system_wrapper" [get_filesets sources_1]
    } else {
      puts "NOTE: Did not set $project as design top-level (not found)"
    }
    
    puts "NOTE: Top-level detection for testbench"
    set project "tb_$project"
    set tops [find_top -fileset sim_1]
    if {$project in $tops} {
      puts "NOTE: Set $project as testbench top-level"
      set_property top $project [get_filesets sim_1]
    } else {
      puts "NOTE: Did not set $project as testbench top-level (not found)"
    }
    
    puts "NOTE: Execute any Vivado specific scripts (global)"
    set bdscripts [glob -nocomplain $dir_script_com/swuk_create_*_bd.tcl]
    foreach bdscript $bdscripts {
      set fbasename [file rootname [file tail $bdscript]]
      set parts [split $fbasename "_"]
      set bdname [lindex $parts 2]
      puts "NOTE: Extracted BD name = $bdname"
      if {[file exist $dir_diagram/$bdname/$bdname.bd]} {
        puts "NOTE: Found $bdscript, NOT attempting execution due to existing BD design $dir_diagram/$bdname.bd"
      } else {
        puts "NOTE: Found $bdscript, attempting execution"
        source $bdscript
      }
    }
    
    puts "NOTE: Execute any Vivado specific scripts (local)"
    set bdscripts [glob -nocomplain $dir_script/user_update_*_bd.tcl]
    foreach bdscript $bdscripts {
      set fbasename [file rootname [file tail $bdscript]]
      set parts [split $fbasename "_"]
      set bdname [lindex $parts 2]
      puts "NOTE: Extracted BD name = $bdname"
      if {[file exist $dir_diagram/$bdname/$bdname.bd]} {
        puts "NOTE: Found $bdscript, NOT attempting execution due to existing BD design $dir_diagram/$bdname.bd"
      } else {
        puts "NOTE: Found $bdscript, attempting execution"
        source $bdscript
      }
    }
    
    puts "NOTE: Check for build argument..."
    if { $argc == 1 } {
      set arg0 [lindex $argv 0]
      if { $arg0 == "build" } {
        puts "NOTE: Generate bitstream & export hardware"
        launch_runs impl_1 -to_step write_bitstream -jobs 4
        wait_on_run impl_1
        write_hw_platform -fixed -include_bit -force -file system_wrapper.xsa
      }
    }
    
    puts "NOTE: That's all folks!"
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_create_vivado_project.tcl -O common/fw/src/script/swuk_create_vivado_project.tcl
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/fw/src/script/swuk_create_vivado_project.tcl
    

    32. Check everything is working as expected

    Attempt to build the Vivado part of the newly created project (zedboard_baseline).
    steve@Desktop:~/swuk_tutorial$ cd zedboard_baseline
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ swuk_create_vivado_project --build
    
    The script generated block design is shown below, note the cleaner names on the modules. Missing Image! All being well the end result (after Vivado completes its run) should be a newly created Hardware Platform file.
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ ls -la fw/vivado/system_wrapper.xsa
    -rw-rw-r-- 1 steve steve 358688 Jan 10 09:52 fw/vivado/system_wrapper.xsa
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ cd -
    
    #### Part 14 - Desktop & PetaLinux communication & transfers (Part A) ####

    33. Create script for JTAG debug with the Zedboard

    Replace the previous alias with a dedicated script.
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_terminal
    

    swuk_terminal

    #!/bin/bash
    
    #
    # File .......... swuk_terminal
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 1.0
    # Description ...
    #   Launch the Minicom terminal emulator.
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare constants
      local -r  c_opthelp="--help"       # Str: Help option name
      local -Ar c_options=(              # ARR: Options & associated help information
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(             # Arr: Help options display order
        ${c_opthelp}
      )
      local -ar argv=(${@})              # Arr: Get argument values (space-separated) into array
      local -r  c_device="/dev/ttyACM0"  # Str: Device
      local -r  c_rate="115200"          # Str: Baud rate
    
      # Declare variables
      local     arg                      # Str: Current argument from argv array
      local     option                   # Str: Current option from c_optorder array
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) [OPTION]..."
        echo "Launch a terminal emulator to allow communication with a remote system via USB."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        else
          echo "Too many arguments found, expecting 0!" >&2
          exit 1
        fi
      done
    
      # Check for XFCE4 desktop environment
      xfce4-about --version > /dev/null 2>&1
      if [ ${?} -eq 0 ]; then
        # Launch termnal emulator in seperate window
        xfce4-terminal \
          --geometry 80x24 \
          --title "Debug console" \
          --working-directory="${swuk_tutorial}" \
          --command " \
            bash -c 'minicom -D ${c_device} -b ${c_rate} 2> /dev/null; \
            [ \${?} -ne 0 ] && \
              echo \"Terminal emulator failed to launch (${c_device} @ ${c_rate}), check connection!\" >&2; \
            bash \
          '"
      else
        # Launch termnal emulator in same window
        minicom -D ${c_device} -b ${c_rate} 2> /dev/null
        [ ${?} -ne 0 ] &&
          echo "Terminal emulator failed to launch (${c_device} @ ${c_rate}), check connection!" >&2 &&
          exit 1
      fi
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_terminal -O common/fw/src/script/swuk_terminal
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/fw/src/script/swuk_terminal
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_terminal --help
    Usage: swuk_terminal [OPTION]...
    Launch a terminal emulator to allow communication with a remote system via USB.
    
          --help        Display this help and exit.
    
    Check the script is working as expected. If running on Xubuntu a separate terminal should launch, if not the same terminal should be used.
    steve@Desktop:~/swuk_tutorial$ swuk_terminal
    
    Welcome to minicom 2.7.1
    
    OPTIONS: I18n
    Compiled on Dec 23 2019, 02:06:26.
    Port /dev/ttyACM0, 08:20:17
    
    Press CTRL-A Z for help on special keys
    
    
    petalinux login:
    

    34. Establish SSH link between Desktop & PetaLinux

    Create a script that sets up a secure shell connection to/from Linux (Desktop) and PetaLinux (Zedboard).
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_ssh
    

    swuk_ssh

    #!/bin/bash
    
    #
    # File .......... swuk_ssh
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 18 Jun 2025
    # Version ....... 1.0
    # Description ...
    #   Enable pathway to PetaLinux running on the Zedboard.
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare constants
      local -r  c_opthelp="--help"  # Str: Help option name
      local -Ar c_options=(         # ARR: Options & associated help information
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(        # Arr: Help options display order
        ${c_opthelp}
      )
      local -ar argv=(${@})         # Arr: Get argument values (space-separated) into array
                                    # Str: Known hosts file
      local -r  hosts="${HOME}/.ssh/known_hosts"
                                    # Str: IP validation regular expression
      local -r  c_ipre='^(0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))\.){3}0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))$'
    
      # Declare variables
      local     arg                 # Str: Current argument from argv array
      local     option              # Str: Current option from c_optorder array
      local     ip=""               # Str: IP Address (x.x.x.x)
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) IP-ADDRESS... [OPTION]..."
        echo "Establish a secure shell connection to the remote system located at IP-ADDRESS."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        elif [[ -z ${ip} ]]; then  # IP address
          ip=${arg}
        else
          echo "Unexpected argument (${arg}) found!" >&2
          exit 1
        fi
      done
    
      # Further check the arguments
      [[ -z ${ip} ]] &&
        echo "No IP-ADDRESS specified!" >&2 &&
        exit 1
    
      # Check the IP ADDRESS
      [[ ! ${ip} =~ ${c_ipre} ]] &&
        echo "The IP-ADDRESS (${ip}) is incorrectly specified!" >&2 &&
        exit 1
    
      # Check IP-ADDRESS is present on LAN
      ping -W 1 -c 1 ${ip} > /dev/null 2>&1
      [ ${?} -ne 0 ] &&
        echo "No ping response ${ip}, check connection!" >&2 &&
        exit 3
    
      # Get SSH public key from the IP-ADDRESS
      key=$(ssh-keyscan -H ${ip} 2> /dev/null)
      if [[ -n ${key} ]]; then
        # Remove all previous keys belonging to the IP ADDRESS
        ssh-keygen -R ${ip} -f ${hosts} > /dev/null 2>&1
        [ ${?} -ne 0 ] &&
          echo "Failed to remove ${ip} from Known Hosts file ${hosts}!" >&2 &&
          exit 1
        # Add new key belonging to the IP ADDRESS
        echo ${key} >> ${hosts}
        [ ${?} -ne 0 ] &&
          echo "Failed to add ${ip} to Known Hosts file ${hosts}!" >&2 &&
          exit 1
      else
        echo "Failed to get SSH public key from ${ip}!" >&2
        exit 2
      fi
    
      # Success message
      echo -e "\nEstablished SSH connection with ${ip}."
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_ssh -O common/fw/src/script/swuk_ssh
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/fw/src/script/swuk_ssh
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_ssh --help
    Usage: swuk_ssh IP-ADDRESS... [OPTION]...
    Establish a secure shell connection to the remote system located at IP-ADDRESS.
    
          --help        Display this help and exit.
    
    Check the script is working as expected.
    steve@Desktop:~/swuk_tutorial$ swuk_ssh ${swuk_zedboard_ip}
    
    Established SSH connection with 192.168.2.87.
    steve@Desktop:~/swuk_tutorial$ sshpass -p root scp root@${swuk_zedboard_ip}:/srv/www/project.txt /tmp
    steve@Desktop:~/swuk_tutorial$ cat /tmp/project.txt
    Zedboard PetaLinux Example Design
    SpaceWire UK
    Steve Haywood
    14.0
    03-Mar-2024 - 17:12:16
    ec080e82dc5c4f5d7fd2198ad166a35133df08eb
    
    Looks good!

    35. Upload BIN (firmware) files to Zedboard's SD-Card

    Create a script that creates the bitstream only binary (.bin) from the bitstream (.bit) and uploads it to the SD-Card on the Zedboard.
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_upload_bin
    

    swuk_upload_bin

    #!/bin/bash
    
    #
    # File .......... swuk_upload_bin
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 18 Jun 2025
    # Version ....... 1.0
    # Description ...
    #   Create a binary file from a bitstream file & upload it.
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare terminal colour constants
      local -r  c_red='\033[0;31m'
      local -r  c_green='\033[0;32m'
      local -r  c_normal='\033[0m'
    
      # Declare argument constants
      local -r  c_opthelp="--help"  # Str: Help option name
      local -Ar c_options=(         # ARR: Options & associated help information
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(        # Arr: Help options display order
        ${c_opthelp}
      )
      local -ar argv=(${@})         # Arr: Get argument values (space-separated) into array
    
      # Declare constants
      local -r  c_ipre='^(0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))\.){3}0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))$'
                                                           # Str: IP validation regular expression
      local -r  c_pwd=$(pwd)                               # Str: Current direcory
      local -r  c_base=$(basename ${c_pwd})                # Str: Project name
      local -r  c_local="fw/vivado/${c_base}.runs/impl_1"  # Str: Location of project Bitstream
      local -r  c_archive="${swuk_user}/firmware"          # Str: Location of user Bitstreams
      local -r  c_remote="/media/sd-mmcblk0p1/firmware"    # Str: Firmware location
      local -r  c_user="root"                              # Str: PetaLinux username
      local -r  c_password="root"                          # Str: PetaLinux password
    
      # Declare variables
      local     arg       # Str: Current argument from argv array
      local     option    # Str: Current option from c_optorder array
      local     ip=""     # Str: IP Address (x.x.x.x)
      local -i  selcnt=0  # Int: Selection count
      local -A  table     # ARR: Selection table
      local     sel       # Str: User selection input
      local     fpath     # Str: Bitstream location (path & filename)
      local     fname     # Str: Bitstream filename (excluding extension)
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) IP-ADDRESS... [OPTION]..."
        echo "Upload the binary part of a bitstream to the remote system located at IP-ADDRESS."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        elif [[ -z ${ip} ]]; then  # IP address
          ip=${arg}
        else
          echo "Unexpected argument (${arg}) found!" >&2
          exit 1
        fi
      done
    
      # Further check the arguments
      [[ -z ${ip} ]] &&
        echo "No IP address specified!" >&2 &&
        exit 1
    
      # Check the IP ADDRESS
      [[ ! ${ip} =~ ${c_ipre} ]] &&
        echo "The IP address (${ip}) is incorrectly specified!" >&2 &&
        exit 1
    
      # Check directory at local location
      echo -e "\nBitstream files @ ${c_local} :-\n"
      if [ -d "${c_local}" ]; then
        # Get file at local location
        file="${c_base}.bit"
        if [ -f "${c_local}/${file}" ]; then
          # Add local file to selection list
          table[${selcnt}]="${c_local}/${file}"
          # Display file details
          echo -e "${selcnt}) - ${c_green}${file}${c_normal}"
          # Increase selection count
          selcnt=$((selcnt+1))
        else
          echo -e "${c_red}Bitstream file does not exist in directory!${c_normal}"
        fi
      else
        echo -e "${c_red}Directory does not exist!${c_normal}"
      fi
    
      # Check directory at archive location
      echo -e "\nBitstream files @ ${c_archive} :-\n"
      if [ -d "${c_archive}" ]; then
        # Get list of files at archive location
        readarray -t files < <(find "${c_archive}" -mindepth 1 -maxdepth 1 -type f -name "*.bit" -printf '%P\n')
        # Sort list of files
        files=($(IFS=$'\n'; echo "${files[*]}" | sort))
        # Iterate through list of files
        for file in "${files[@]}"; do
          # Add archive file to selection list
          table[${selcnt}]="${c_archive}/${file}"
          # Display file details
          echo -e "${selcnt}) - ${c_green}${file}${c_normal}"
          # Increase selection count
          selcnt=$((selcnt+1))
        done
      else
        echo -e "${c_red}Directory does not exist!${c_normal}"
      fi
    
      # Obtain user input, if good attempt upload
      echo -e "\nq) Quit\n"
      echo -n "Select file for upload or quit"
      while true; do
        read -p " : " sel
        if [[ ("${sel}" == "q") || ("${sel}" == "Q") ]]; then
          echo "Quitting without selection!"
          exit 0
        elif ! [[ "${sel}" =~ ^[0-9]+$ ]] ; then
          # User input is not a number
          echo -n "Selection is not a number, try again"
        elif [ ${sel} -lt 0 ] || [ ${sel} -ge ${selcnt} ]; then
          # User input is out of range
          echo -n "Selection is out of range, try again"
        else
          break
        fi
      done
    
      # Extract filename from full path
      fpath=${table[${sel}]}
      fname=$(basename -- "${fpath}")
      fname="${fname%.*}"
    
      # Establish SSH connection
      swuk_ssh ${ip} > /dev/null
      [ ${?} -ne 0 ] && exit 1
    
      # Extract binary from bitstream
      cp "${fpath}" /tmp/bitstream.bit
      [ ${?} -ne 0 ] && exit 1
      echo -e "all:\n{\n  /tmp/bitstream.bit\n}" > /tmp/bitstream.bif
      bootgen -arch zynq -image /tmp/bitstream.bif -process_bitstream bin -w > /dev/null
      [ ${?} -ne 0 ] && exit 1
    
      # Upload binary to
      sshpass -p ${c_password} scp /tmp/bitstream.bit.bin ${c_user}@${ip}:${c_remote}/${fname}.bin
      [ ${?} -ne 0 ] && exit 1
      rm /tmp/{bitstream.bit,bitstream.bif,bitstream.bit.bin}
    
      # Success message
      echo -e "\nUploaded ${fname}.bin to ${c_remote} @ ${ip}."
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_upload_bin -O common/fw/src/script/swuk_upload_bin
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/fw/src/script/swuk_upload_bin
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_upload_bin --help
    Usage: swuk_upload_bin IP-ADDRESS... [OPTION]...
    Upload the binary part of a bitstream to the remote system located at IP-ADDRESS.
    
          --help        Display this help and exit.
    
    Check the script is working as expected. Select 0 to upload the newly created project binary.
    steve@Desktop:~/swuk_tutorial$ cd zedboard_baseline
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ swuk_upload_bin ${swuk_zedboard_ip}
    
    Bitstream files @ fw/vivado/zedboard_baseline.runs/impl_1 :-
    
    0) - zedboard_baseline.bit
    
    Bitstream files @ /home/steve/Documents/swuk_tutorial/firmware :-
    
    1) - zedboard_hello_world_v1.0.bit
    2) - zedboard_leds_buttons_v1.0.bit
    3) - zedboard_leds_switches_v1.0.bit
    4) - zedboard_leds_switches_v2.0.bit
    5) - zedboard_leds_switches_v3.0.bit
    6) - zedboard_leds_switches_v4.0.bit
    7) - zedboard_leds_switches_v5.0.bit
    
    q) Quit
    
    Select file for upload or quit : 0
    
    Uploaded zedboard_baseline.bin to /media/sd-mmcblk0p1/firmware @ 192.168.2.87.
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ sshpass -p root ssh -t root@${swuk_zedboard_ip} "ls -la /media/sd-mmcblk0p1/firmware/zedboard_baseline.bin"
    -rwxr-xr-x    1 root     root       4045568 Jan 10 14:17 /media/sd-mmcblk0p1/firmware/zedboard_baseline.bin
    Connection to 192.168.2.87 closed.
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ cd -
    
    Looks good! File date as expected!
    #### Part 15 - Rename & update create Vitis project scripts ####

    36. Rename & update create Vitis project Bash script

    Edit the script to make the following changes.

    Requirements :-
    1. Rename file to use the new swuk_ prefix & remove the .sh extension.
    2. Update header to reflect field changes - File, Version, History & Description.
    3. Edit source to account for the new swuk_ prefix.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/sw/src/script/create_vitis_project.sh common/sw/src/script/swuk_create_vitis_project
    
    steve@Desktop:~/swuk_tutorial$ subl common/sw/src/script/swuk_create_vitis_project
    

    swuk_create_vitis_project

    #!/bin/bash
    
    #
    # File .......... swuk_create_vitis_project
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/sw/src/script/create_vitis_project.sh
    # Description ...
    #   Very simple script to launch the Xilinx software command-line tool,
    # run the TCL script and launch Vitis. Should be run in the project host
    # directory that contains the fw & sw subdirectories, for example :-
    #
    # user@host:~/swuk_tutorial/project$ swuk_create_vitis_project
    # user@host:~/swuk_tutorial/project$ swuk_create_vitis_project --build
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare argument constants
      local -r  c_optbuild="--build"  # Str: Build option name
      local -r  c_opthelp="--help"    # Str: Help option name
      local -Ar c_options=(           # ARR: Options & associated help information
        [${c_optbuild}]="Build the project after creation."
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(          # Arr: Help options display order
        ${c_optbuild}
        ${c_opthelp}
      )
      local -ar argv=(${@})           # Arr: Get argument values (space-separated) into array
    
      # Declare argument variables
      local     arg                   # Str: Current argument from argv array
      local     option                # Str: Current option from c_optorder array
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) [OPTION]..."
        echo "Create a new Vitis project using the $(basename ${0}).tcl script & optionally build it."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        else
          echo "Unexpected argument (${arg}) found!" >&2
          exit 1
        fi
      done
    
      # Declare operational constants & variables
      local -r c_dir_script=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
                                           # Str: Script location directory
      local -r c_dir_pwd=$(pwd)            # Str: Current working directory (full path)
      local -r c_project=${c_dir_pwd##*/}  # Str: Last element of pwd (project name)
      local -r c_dir_base="sw"             # Str: Software base directory
      local -r c_dir_project="vitis"       # Str: Vitis project directory
      local    tclargs=""                  # Str: Arguments sent to TCL script
    
      # Sanity check directory structure
      [ ! -d ${c_dir_base} ] &&
        echo "Software base directory ${c_dir_base} does not exists!" >&2 &&
        exit 1
    
      [ -d ${c_dir_base}/${c_dir_project} ] &&
        echo "Vitis project directory ${c_dir_base}/${c_dir_project} already exists!" >&2 &&
        exit 1
    
      # Create & move into the new project directory
      mkdir -p ${c_dir_base}/${c_dir_project}
      cd ${c_dir_base}/${c_dir_project}
    
      # Check for build argument
      [[ " ${argv[*]} " =~ " ${c_optbuild} " ]] && tclargs="build"
    
      # Call Xilinx Software Commandline Tool
      xsct "${c_dir_script}/$(basename ${0}).tcl" "${c_project}" ${tclargs}
    
      # Launch Vitis
      vitis -workspace . &
    
      # Move back out of project directory
      cd ../..
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/sw/src/script/swuk_create_vitis_project -O common/sw/src/script/swuk_create_vitis_project
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/sw/src/script/swuk_create_vitis_project
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/sw/src/script/swuk_create_vitis_project
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_create_vitis_project --help
    Usage: swuk_create_vitis_project [OPTION]...
    Create a new Vitis project using the swuk_create_vitis_project.tcl script & optionally build it.
    
          --build       Build the project after creation.
          --help        Display this help and exit.
    

    37. Rename & update create Vitis project TCL script

    Edit the script to make the following changes.

    Requirements :-
    1. Rename file to use the new swuk_ prefix.
    2. Update header to reflect field changes - File, Version & History.
    3. Adjust path where the .xsa gets picked up from such that it matches the Vivado default.
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/sw/src/script/create_vitis_project.tcl common/sw/src/script/swuk_create_vitis_project.tcl
    
    steve@Desktop:~/swuk_tutorial$ subl common/sw/src/script/swuk_create_vitis_project.tcl
    

    swuk_create_vitis_project.tcl

    #
    # File .......... swuk_create_vitis_project.tcl
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/sw/src/script/create_vitis_project.tcl
    # Description ...
    #   Simple Tcl script to create a new Vitis application, import external
    # sources and (optionally) build the application.
    #
    
    # Check for project name argument, exit if not found.
    if { $argc == 0 } {
      puts "Error: No project name provided, exiting..."
      exit 1
    }
    set project [lindex $argv 0]
    
    # Create and build new project.
    setws .
    app create -name $project -hw ../../fw/vivado/system_wrapper.xsa -os standalone -proc ps7_cortexa9_0 -template {Empty Application(C)}
    set abs_path [file normalize ../src/c]
    importsources -name $project -path $abs_path -soft-link
    if { $argc > 1 } {
      set arg1 [lindex $argv 1]
      if { $arg1 == "build" } {
        app build -name $project
      }
    }
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/sw/src/script/swuk_create_vitis_project.tcl -O common/sw/src/script/swuk_create_vitis_project.tcl
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/sw/src/script/swuk_create_vitis_project.tcl
    

    38. Check everything is working as expected

    Attempt to build the Vitis part of the newly created project (zedboard_baseline) using the newly created Hardware Platform.
    steve@Desktop:~/swuk_tutorial$ cd zedboard_baseline
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ swuk_create_vitis_project --build
    steve@Desktop:~/swuk_tutorial/zedboard_baseline$ cd -
    
    All being well the end result (after Vitis completes its run) should be TBD....
    #### Part 16 - Rename & update PetaLinux build script ####

    39. Rename & enhance the script

    Edit the script to make the following changes.

    Requirements :-
    1. Rename file to use the new swuk_ prefix, remove the _id & remove the .sh extension.
    2. Move the script to the correct location.
    3. Update header to reflect field changes - File, Version & History.
    Create the missing directory.
    steve@Desktop:~/swuk_tutorial$ mkdir -p common/os/src/script
    
    Relocate source file in the repository & file system.
    steve@Desktop:~/swuk_tutorial$ git mv common/other/src/script/petalinux-build-id.sh common/os/src/script/swuk_petalinux-build
    
    steve@Desktop:~/swuk_tutorial$ subl common/os/src/script/swuk_petalinux-build
    

    swuk_petalinux-build

    #!/bin/bash
    
    #
    # File .......... swuk_petalinux-build
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 25 Jun 2025
    # Version ....... 2.0
    # History .......
    #   1.0 common/other/src/script/petalinux-build-id.sh
    # Description ...
    #   Script to read product identification file, obtain GIT repository
    # information, append project identification file with GIT information, build
    # PetaLinux project and then restore project identification file.
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare constants
      local -r  c_opthelp="--help"  # Str: Help option name
      local -Ar c_options=(         # ARR: Options & associated help information
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(        # Arr: Help options display order
        ${c_opthelp}
      )
      local -ar argv=(${@})         # Arr: Get argument values (space-separated) into array
                                    # Str: Project identification file
      local -r  c_project="project-spec/meta-user/recipes-apps/website/files/project.txt"
    
      # Declare variables
      local     arg                 # Str: Current argument from argv array
      local     option              # Str: Current option from c_optorder array
      local -a  ids                 # Arr: Product identification (Description, Company, Author)
      local     id_timestamp        # Str: GIT timestamp
      local     id_hash             # Str: GIT hash
      local     id_append           # Str: Version appendage
      local     status              # Str: GIT status
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) [OPTION]..."
        echo "Build a PetaLinux project that incorporates the product identification along with the GIT repository information."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        else
          echo "Too many arguments found, expecting 0!" >&2
          exit 1
        fi
      done
    
      # Only perform bulk of actions if identification file exists
      if [ -f "${c_project}" ]; then
    
        # Read project identification file
        readarray -t ids < "${c_project}"
    
        # Display project identification information
        echo -e '\033[1mNOTE\033[0m: Building PetaLinux with the following identification'
        echo "Description ... ${ids[0]}"
        echo "Company ....... ${ids[1]}"
        echo "Author ........ ${ids[2]}"
    
        # Get GIT timestamp
        id_timestamp=$(git log -1 --pretty=%cd --date=format:"%d-%b-%Y - %H:%M:%S")
        if [ $? -ne 0 ]; then
          id_timestamp="00-Xxx-0000 - 00:00:00"
        fi
    
        # Get GIT hash
        id_hash=$(git log -1 --pretty=%H)
        if [ $? -ne 0 ]; then
          id_hash="0000000000000000000000000000000000000000"
        fi
    
        # Get GIT status
        id_append=""
        status=$(git status -s)
        if [ $? -ne 0 ]; then
          id_append="(undefined)"
        else
          if [ ! -z "${status}" ]; then
            id_append="(unstaged)"
          else
            status=$(git branch -r --contains ${id_hash})
            if [ $? -ne 0 ]; then
              id_append="(undefined)"
            else
              if [ -z "${status}" ]; then
                id_append="(unpushed)"
              fi
            fi
          fi
        fi
    
        # Display project identification information
        echo "Version ....... ${ids[3]} ${id_append}"
        echo "Timestamp ..... ${id_timestamp}"
        echo "Hash .......... ${id_hash}"
    
        # Modify project identification file
        echo ${ids[0]}               > ${c_project}  # Description
        echo ${ids[1]}              >> ${c_project}  # Company
        echo ${ids[2]}              >> ${c_project}  # Author
        echo ${ids[3]} ${id_append} >> ${c_project}  # Version
        echo ${id_timestamp}        >> ${c_project}  # Timestamp
        echo ${id_hash}             >> ${c_project}  # Hash
    
        # Build PetaLinux
        $(which petalinux-build)
        jobs -l
        wait
    
        # Restore project identification file
        echo ${ids[0]}  > ${c_project}  # Description
        echo ${ids[1]} >> ${c_project}  # Company
        echo ${ids[2]} >> ${c_project}  # Author
        echo ${ids[3]} >> ${c_project}  # Version
    
      else
    
        # Build PetaLinux
        $(which petalinux-build)
    
      fi
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/os/src/script/swuk_petalinux-build -O common/os/src/script/swuk_petalinux-build
    
    Check out the changes.
    steve@Desktop:~/swuk_tutorial$ git difftool common/os/src/script/swuk_petalinux-build
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/os/src/script/swuk_petalinux-build
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_petalinux-build --help
    Usage: swuk_petalinux-build [OPTION]...
    Build a PetaLinux project that incorporates the product identification along with the GIT repository information.
    
          --help        Display this help and exit.
    

    40. Check everything is working as expected

    Attempt to build PetaLinux from scratch using the newly created Hardware Platform provided from the newly created zedboard_baseline project.

    Note: A fresh build of PetaLinux takes a long time to complete and can be prone to failure due to internet downloads. If the build process fails simple execute the build command again (and possibly again) until it finally completes.
    steve@Desktop:~/swuk_tutorial$ rm -rf /tftpboot/*
    steve@Desktop:~/swuk_tutorial$ cd zedboard_linux/os/petalinux
    steve@Desktop:~/swuk_tutorial/zedboard_linux/os/petalinux$ petalinux-config --get-hw-description ../../../zedboard_baseline/fw/vivado/system_wrapper.xsa
    steve@Desktop:~/swuk_tutorial/zedboard_linux/os/petalinux$ swuk_petalinux-build
    NOTE: Building PetaLinux with the following identification
    Description ... Zedboard PetaLinux Example Design
    Company ....... SpaceWire UK
    Author ........ Steve Haywood
    Version ....... 14.0 (unstaged)
    Timestamp ..... 11-Jan-2026 - 18:29:26
    Hash .......... 498aea52e5ec78d000a428e3e5a101ace925ee32
    [INFO] Sourcing buildtools
    [INFO] Building project
    ...
    ...
    ...
    INFO: Successfully copied built images to tftp dir: /tftpboot
    [INFO] Successfully built project
    steve@Desktop:~/swuk_tutorial/zedboard_linux/os/petalinux$ petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --uboot images/linux/u-boot.elf
    steve@Desktop:~/swuk_tutorial/zedboard_linux/os/petalinux$ cd -
    
    Looks good! Picks up the project.txt file for integration into the build process.

    After the PetaLinux build completes, check the created files in /tftboot.
    steve@Desktop:~/swuk_tutorial$ ls -la /tftpboot/{BOOT.BIN,boot.scr,image.ub}
    -rw-rw-r-- 1 steve steve  5212724 Feb  9  2025 /tftpboot/BOOT.BIN
    -rw-r--r-- 1 steve steve     2710 Jan 10 11:18 /tftpboot/boot.scr
    -rw-r--r-- 1 steve steve 31191640 Jan 10 11:18 /tftpboot/image.ub
    
    Looks good! File dates as expected!
    #### Part 17 - Desktop & PetaLinux communication & transfers (Part B) ####

    41. Upload BOOT (OS) files to Zedboard's SD-Card

    Create a script that uploads the boot files (BOOT.BIN, boot.src & image.ub) from either /tftpboot or $HOME/Documents/swuk_tutorial\petalinux\<version>. The boot files are to be uploaded to the root of the Zedboard SD-Card (/media/sd-mmcblk0p1). The script should offer the user the option of selecting the source location to upload the files from. It should also back up the existing boot files on the SD-Card.
    steve@Desktop:~/swuk_tutorial$ subl common/fw/src/script/swuk_upload_boot
    

    swuk_upload_boot

    #!/bin/bash
    
    #
    # File .......... swuk_upload_boot
    # Author ........ Steve Haywood
    # Website ....... http://www.spacewire.co.uk
    # Project ....... Common (SpaceWire UK Tutorial)
    # Date .......... 20 Jun 2025
    # Version ....... 1.0
    # Description ...
    #   Upload boot files (BOOT.BIN, boot.src & image.ub) to a remote system.
    # Process involves backing up the boot files on the remote system, uploading the
    # new ones and rebooting the system.
    #
    
    # Strict
    #set -euo pipefail
    
    
    ################################################################################
    # Check existence of boot files
    # Arguments ...... $1 ... Str: Files directory
    # Return ......... None
    # Exit code ...... None
    # Shared (In) .... c_bfiles
    # Shared (Out) ... None
    check_boot_files () {
      # Declare variables
      local -r dir=${1}  # Str: Files directory
      local    bfile     # Str: Current file from the directory
    
      # Iterate through files
      for bfile in "${c_bfiles[@]}"
      do
        echo -n " - "
        [ -f ${dir}/${bfile} ] && echo -en "${c_green}" || echo -en "${c_red}";
        echo -en "${bfile}${c_normal}"
      done
      echo
    }
    
    
    ################################################################################
    # Main function.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main()
    {
      # Declare terminal colour constants
      local -r  c_red='\033[0;31m'
      local -r  c_green='\033[0;32m'
      local -r  c_normal='\033[0m'
    
      # Declare argument constants
      local -r  c_opthelp="--help"  # Str: Help option name
      local -Ar c_options=(         # ARR: Options & associated help information
        [${c_opthelp}]="Display this help and exit."
      )
      local -ar c_optorder=(        # Arr: Help options display order
        ${c_opthelp}
      )
      local -ar argv=(${@})         # Arr: Get argument values (space-separated) into array
    
      local -r  hosts="${HOME}/.ssh/known_hosts"             # Str: Known hosts file
      local -r  c_ipre='^(0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))\.){3}0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))$'
                                                             # Str: IP validation regular expression
      local -r  c_local="/tftpboot"                          # Str: Location of most recent OS files
      local -r  c_archive="${swuk_user}/petalinux"           # Str: Location of user OS files
      local -r  c_remote="/media/sd-mmcblk0p1"               # Str: Remote SD-Card location
      local -r  c_bfiles=("BOOT.BIN" "boot.scr" "image.ub")  # Str: Boot files
      local -r  c_user="root"                                # Str: PetaLinux username
      local -r  c_password="root"                            # Str: PetaLinux password
      local -ri c_timedout=30                                # Int: Timeout limit (wait, ping, ssh)
    
      # Declare variables
      local     arg                      # Str: Current argument from argv array
      local     option                   # Str: Current option from c_optorder array
      local     ip=""                    # Str: IP Address (x.x.x.x)
      local -i  selcnt=0                 # Int: Selection count
      local -A  table                    # ARR: Selection table
      local -a  dirs                     # Arr: List of archive directories
      local     dir                      # Str: Current archive directory
      local     sel                      # Str: User selection input
      local     fpath                    # Str: Boot files location (path)
      local     bfile                    # Str: Boot file from array
      local -i  timeout                  # Int: Timeout for reboot (wait, ping, ssh)
    
      # Display help information
      if [[ " ${argv[*]} " =~ " ${c_opthelp} " ]]; then
        echo "Usage: $(basename ${0}) IP-ADDRESS... [OPTION]..."
        echo "Upload the boot files ${c_bfiles[0]}, ${c_bfiles[1]} & ${c_bfiles[2]} to the remote system located at IP-ADDRESS."
        echo
        for option in ${c_optorder[@]}
        do
          echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${c_options[${option}]}"
        done
        echo
        exit 0
      fi
    
      # Get & check the arguments
      for arg in ${argv[@]}; do
        if [[ ${arg:0:2} == "--" ]]; then  # Option
          [[ ! -v c_options[${arg}] ]] &&
            echo "Option (${arg}) is not recognised!" >&2 &&
            exit 1
        elif [[ -z ${ip} ]]; then  # IP address
          ip=${arg}
        else
          echo "Unexpected argument (${arg}) found!" >&2
          exit 1
        fi
      done
    
      # Further check the arguments
      [[ -z ${ip} ]] &&
        echo "No IP address specified!" >&2 &&
        exit 1
    
      # Check the IP ADDRESS
      [[ ! ${ip} =~ ${c_ipre} ]] &&
        echo "The IP address (${ip}) is incorrectly specified!" >&2 &&
        exit 1
    
      # Check directory at local location
      echo -e "\nBoot files @ ${c_local} :-\n"
      if [ -d "${c_local}" ]; then
        # Add local to selection list
        table[${selcnt}]=${c_local}
        # Check existence of boot files
        echo -n "${selcnt})"
        check_boot_files ${c_local}
        # Increase selection count
        selcnt=$((selcnt+1))
      else
        echo "Directory does not exist!"
      fi
    
      # Check directory at archive location
      echo -e "\nBoot files @ ${c_archive} :-\n"
      if [ -d "${c_archive}" ]; then
        # Get list of directories at archive location
        readarray -t dirs < <(find "${c_archive}" -mindepth 1 -maxdepth 1 -type d -printf '%P\n')
        # Check existence of subdirectories
        if [ ${#dirs[@]} -ne 0 ]; then
          # Sort list of directories
          dirs=($(IFS=$'\n'; echo "${dirs[*]}" | sort))
          # List what directories are present
          for dir in "${dirs[@]}"; do
            # Add archive subdirectory to selection list
            table[${selcnt}]="${c_archive}/${dir}"
            # Check existence of boot files
            echo -n "${selcnt}) ${dir}"
            check_boot_files "${c_archive}/${dir}"
            # Increase selection count
            selcnt=$((selcnt+1))
          done
        else
          echo "Directory does not contain any subdirectories!"
        fi
      else
        echo "Directory does not exist!"
      fi
    
      # Obtain user input, if good attempt upload
      echo -e "\nq) Quit\n"
      echo -n "Select boot files for upload or quit"
      while true; do
        read -p " : " sel
        if [[ ("${sel}" == "q") || ("${sel}" == "Q") ]]; then
          echo "Quitting without selection!"
          exit 0
        elif ! [[ "${sel}" =~ ^[0-9]+$ ]] ; then
          # User input is not a number
          echo -n "Selection is not a number, try again"
        elif [ ${sel} -lt 0 ] || [ ${sel} -ge ${selcnt} ]; then
          # User input is out of range
          echo -n "Selection is out of range, try again"
        else
          break
        fi
      done
    
      # Get path from table
      fpath=${table[${sel}]}
    
      # Iterate through list of boot files to check for existence
      for bfile in "${c_bfiles[@]}"; do
        [ ! -f ${fpath}/${bfile} ] &&
          echo -e "Boot file ($bfile) not found!" >&2 &&
          exit 1
      done
    
      # Establish SSH connection
      swuk_ssh ${ip} > /dev/null
      [ ${?} -ne 0 ] && exit 1
    
      # Backup boot files
      sshpass -p ${c_password} ssh -t ${c_user}@${ip} "cd ${c_remote}; for file in ${c_bfiles[0]} ${c_bfiles[1]} ${c_bfiles[2]}; do cp \$file \$file.bak; done" > /dev/null 2>&1
      [ ${?} -ne 0 ] && exit 1
    
      # Upload new boot files
      sshpass -p ${c_password} scp ${fpath}/{${c_bfiles[0]},${c_bfiles[1]},${c_bfiles[2]}} ${c_user}@${ip}:${c_remote}
      [ ${?} -ne 0 ] && exit 1
    
      # Reboot system
      sshpass -p ${c_password} ssh -t ${c_user}@${ip} "/sbin/reboot" > /dev/null 2>&1
      [ ${?} -ne 0 ] && exit 1
    
      # Allow time for system to reboot
      echo
      tput civis
      for ((timeout=0; timeout<=${c_timedout}; timeout++)); do
        echo -ne "Remote reboot (wait): $timeout\033[0K / ${c_timedout}s\r"
        sleep 1
      done
      tput cnorm
      echo
    
      tput civis
      # Check IP-ADDRESS is present on LAN
      for ((timeout=0; timeout<=${c_timedout}; timeout++)); do
        echo -ne "Remote reboot (ping): $timeout\033[0K / ${c_timedout}s\r"
        swuk_ssh ${ip} > /dev/null 2>&1
        case ${?} in
          0)  # All done (early). Ping & SSH established
            break
          ;;
          3)  # Ping fail (Ping already waited 1s)
            :
          ;;
          2)  # Ping OK but no SSH just yet
            break
          ;;
          1)  # Ping & SSH established but issues with host file
            echo "Failed to access known hosts file ${hosts}!" >&2
            exit 1
          ;;
          *)
            :
          ;;
        esac
      done
      tput cnorm
      echo
    
      [[ ${timeout} -gt ${c_timedout} ]] &&
        echo "Timed out, check connection!" >&2 &&
        exit 1
    
      tput civis
      # Get SSH public key from the IP-ADDRESS
      for ((timeout=0; timeout<=${c_timedout}; timeout++)); do
        echo -ne "Remote reboot (ssh): $timeout\033[0K / ${c_timedout}s\r"
        swuk_ssh ${ip} > /dev/null 2>&1
        case ${?} in
          0)  # All done. Ping & SSH established
            break
          ;;
          3)  # Ping fail (Ping already waited 1s)
            :
          ;;
          2)  # Ping OK but no SSH just yet
            sleep 1
          ;;
          1)  # Ping & SSH established but issues with host file
            echo "Failed to access known hosts file ${hosts}!" >&2
            exit 1
          ;;
          *)
            :
          ;;
        esac
      done
      tput cnorm
      echo
    
      [[ ${timeout} -gt ${c_timedout} ]] &&
        echo "Timed out, check connection!" >&2 &&
        exit 1
    
      # Success message
      echo -e "\nUploaded ${c_bfiles[0]}, ${c_bfiles[1]} & ${c_bfiles[2]} to ${c_remote} @ ${ip}."
    
      # Display remote details
      sshpass -p ${c_password} scp ${c_user}@${ip}:/srv/www/project.txt /tmp
      [ ${?} -ne 0 ] && exit 1
      echo -e "\nRemote system credentials :-"
      cat /tmp/project.txt
    }
    
    
    ################################################################################
    # Opening gambit.
    # Arguments ...... ${@}
    # Return ......... None
    # Exit code ...... Status (0=success, 1=failure)
    # Shared (In) .... None
    # Shared (Out) ... None
    main ${@}
    exit 0
    
    Direct download available here :-
    steve@Desktop:~/swuk_tutorial$ wget https://spacewire.co.uk/tutorial/shared/repos/0028/common/fw/src/script/swuk_upload_boot -O common/fw/src/script/swuk_upload_boot
    
    Make the script executable.
    steve@Desktop:~/swuk_tutorial$ chmod +x common/fw/src/script/swuk_upload_boot
    
    Examine the script options.
    steve@Desktop:~/swuk_tutorial$ swuk_upload_boot --help
    Usage: swuk_upload_boot IP-ADDRESS... [OPTION]...
    Upload the boot files BOOT.BIN, boot.scr & image.ub to the remote system located at IP-ADDRESS.
    
          --help        Display this help and exit.
    
    Check the script is working as expected. Select 0 to upload the newly created boot files.
    steve@Desktop:~/swuk_tutorial$ swuk_upload_boot ${swuk_zedboard_ip}
    
    Boot files @ /tftpboot :-
    
    0) - BOOT.BIN - boot.scr - image.ub
    
    Boot files @ /home/steve/Documents/swuk_tutorial/petalinux :-
    
    1) v10.0 - BOOT.BIN - boot.scr - image.ub
    2) v11.0 - BOOT.BIN - boot.scr - image.ub
    3) v12.0 - BOOT.BIN - boot.scr - image.ub
    4) v13.0 - BOOT.BIN - boot.scr - image.ub
    5) v14.0 - BOOT.BIN - boot.scr - image.ub
    
    q) Quit
    
    Select boot files for upload or quit : 0
    
    Remote reboot (wait): 30 / 30s
    Remote reboot (ping): 4 / 30s
    Remote reboot (ssh): 10 / 30s
    
    Uploaded BOOT.BIN, boot.scr & image.ub to /media/sd-mmcblk0p1 @ 192.168.2.87.
    
    Remote system credentials :-
    Zedboard PetaLinux Example Design
    SpaceWire UK
    Steve Haywood
    14.0 (unstaged)
    27-Mar-2025 - 16:28:14
    aed28369fbe90a57d4758730ba45ced127d01d60
    
    Looks good! Credentials are as expected!
    #### Part 18 - Check aliases & scripts ####

    42. Check that swuk_ aliases & scripts are readily available

    In a terminal type swuk_ & hit the tab key a couple of times.
    steve@Desktop:~/swuk_tutorial$ swuk_
    swuk_create_project         swuk_repos_splice
    swuk_create_vitis_project   swuk_repos_split
    swuk_create_vivado_project  swuk_sis
    swuk_petalinux-build        swuk_ssh
    swuk_repos_fix_headers      swuk_terminal
    swuk_repos_gen_fix_headers  swuk_upload_bin
    swuk_repos_history          swuk_upload_boot
    swuk_repos_join             swuk_xilinx
    
    All 16 present and correct, 10 from this tutorial & 6 from the last one.
    #### Part 19 - Final checks ####

    43. Check everything is working as expected

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

    Select System from the menu bar. All being well the following should be displayed in the Operating System Information & Firmware Information sections. Missing Image! Looks good! Both the OS & Firmware are correctly shown as unstaged (nothing checked in to the repository yet). The Timestamp & Hash are from the last set of committed & pushed files, tagged spliced_repository.
    steve@Desktop:~/swuk_tutorial$ git log -1
    commit 498aea52e5ec78d000a428e3e5a101ace925ee32 (HEAD -> master, tag: spliced_repository, origin/master, origin/HEAD)
    Author: Steve Haywood <steve@spacewire.co.uk>
    Date:   Sun Jan 11 18:29:26 2026 +0000
    
        Added scripts required to take the four separate SpaceWire UK project repositories and merge them into one spliced repository.
    
    Select Peek & Poke from the menu bar. Click on the Browse button and load the zedboard_leds_switches.txt configuration from within the ${swuk_user}/configuration directory.

    All being well the Zedboard Baseline firmware should behave exactly like the Zedboard LEDs & Switches firmware.
    Missing Image! Looks good!
    #### Part 20 - Revision Control ####

    44. Commit new & updated files

    Check GIT status to make sure all is well and there are no spurious elements.
    steve@Desktop:~/swuk_tutorial$ git status -u
    On branch my_master
    Your branch is up-to-date with 'origin/my_master'.
    
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
            new file:   .gitignore      
            new file:   common/.gitignore      
            deleted:    common/fw/src/constraint/zedboard_master_XDC_RevC_D_v3.xdc      
            renamed:    zedboard_leds_switches/fw/src/design/axi_gpio_zed.v -> common/fw/src/design/axi_gpio_zed.v      
            renamed:    zedboard_leds_switches/fw/src/design/axi_identification.v -> common/fw/src/design/axi_identification.v      
            renamed:    zedboard_leds_switches/fw/src/design/axi_register_bank.v -> common/fw/src/design/axi_register_bank.v      
            renamed:    zedboard_leds_switches/fw/src/design/debounce.v -> common/fw/src/design/debounce.v      
            renamed:    common/fw/src/script/create_vivado_project.sh -> common/fw/src/script/swuk_create_vivado_project      
            renamed:    common/fw/src/script/create_vivado_project.tcl -> common/fw/src/script/swuk_create_vivado_project.tcl      
            renamed:    zedboard_leds_switches/fw/src/script/pre_synth.tcl -> common/fw/src/script/swuk_pre_synth.tcl      
            deleted:    common/fw/src/script/zedboard_presets.tcl      
            renamed:    common/other/src/script/petalinux-build-id.sh -> common/os/src/script/swuk_petalinux-build      
            renamed:    common/other/src/script/create_project_structure.sh -> common/other/src/script/swuk_create_project      
            renamed:    common/other/src/script/xilinx.sh -> common/other/src/script/swuk_xilinx      
            deleted:    common/other/src/script/xilinx_2021_2.sh      
            renamed:    common/sw/src/script/create_vitis_project.sh -> common/sw/src/script/swuk_create_vitis_project      
            renamed:    common/sw/src/script/create_vitis_project.tcl -> common/sw/src/script/swuk_create_vitis_project.tcl      
            new file:   zedboard_hello_world/.gitignore      
            deleted:    zedboard_hello_world/fw/system_wrapper.xsa      
            new file:   zedboard_leds_buttons/.gitignore      
            renamed:    zedboard_leds_buttons/fw/src/constraint/zedboard.xdc -> zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc      
            renamed:    zedboard_leds_buttons/fw/src/testbench/testbench.sv -> zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv      
            deleted:    zedboard_leds_buttons/fw/system_wrapper.xsa      
            modified:   zedboard_leds_switches/.gitignore      
            renamed:    zedboard_leds_switches/fw/src/constraint/zedboard.xdc -> zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc      
            renamed:    zedboard_leds_switches/fw/src/diagram/system/hdl/system_wrapper.sv -> zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv      
            renamed:    zedboard_leds_switches/fw/src/testbench/testbench.sv -> zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv      
            new file:   zedboard_linux/.gitignore      
            deleted:    zedboard_linux/os/src/other/zedboard_leds_switches.txt      
    
    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:   common/.gitignore      
            modified:   common/fw/src/design/axi_gpio_zed.v      
            modified:   common/fw/src/design/axi_identification.v      
            modified:   common/fw/src/design/axi_register_bank.v      
            modified:   common/fw/src/design/debounce.v      
            modified:   common/fw/src/script/swuk_create_vivado_project      
            modified:   common/fw/src/script/swuk_create_vivado_project.tcl      
            modified:   common/fw/src/script/swuk_pre_synth.tcl      
            modified:   common/os/src/script/swuk_petalinux-build      
            modified:   common/other/src/script/swuk_create_project      
            modified:   common/other/src/script/swuk_xilinx      
            modified:   common/sw/src/script/swuk_create_vitis_project      
            modified:   common/sw/src/script/swuk_create_vitis_project.tcl      
            modified:   zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc      
            modified:   zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv      
            modified:   zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc      
            modified:   zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv      
            modified:   zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv      
            modified:   zedboard_leds_switches/sw/src/c/zedboard_leds_switches.c      
            modified:   zedboard_linux/os/petalinux/.petalinux/metadata      
            modified:   zedboard_linux/os/petalinux/project-spec/hw-description/system.xsa      
            modified:   zedboard_linux/os/petalinux/project-spec/hw-description/system_wrapper.bit      
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
            common/fw/src/design/heartbeat.v      
            common/fw/src/design/meta_hardener.v      
            common/fw/src/script/swuk_create_system_bd.tcl      
            common/fw/src/script/swuk_ssh      
            common/fw/src/script/swuk_terminal      
            common/fw/src/script/swuk_upload_bin      
            common/fw/src/script/swuk_upload_boot      
            common/other/src/script/swuk_sis      
            zedboard_baseline/.gitignore      
            zedboard_baseline/fw/project.txt      
            zedboard_baseline/fw/src/constraint/zedboard_baseline.xdc      
            zedboard_baseline/fw/src/design/zedboard_baseline.sv      
            zedboard_baseline/fw/src/script/user_post.tcl      
            zedboard_baseline/fw/src/script/user_pre.tcl      
            zedboard_linux/os/petalinux/.Xil/-3854345-Desktop/system.bda      
    
    As expected but needs a little cleaning to remove the temporary files created for testing purposes.
    steve@Desktop:~/swuk_tutorial$ rm -rf zedboard_baseline
    steve@Desktop:~/swuk_tutorial$ git restore zedboard_linux
    steve@Desktop:~/swuk_tutorial$ rm -rf zedboard_linux/os/petalinux/.Xil
    
    Check GIT status again to make sure all is well and there are no spurious elements.
    steve@Desktop:~/swuk_tutorial$ git status -u
    On branch my_master
    Your branch is up-to-date with 'origin/my_master'.
    
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
            new file:   .gitignore      
            new file:   common/.gitignore      
            deleted:    common/fw/src/constraint/zedboard_master_XDC_RevC_D_v3.xdc      
            renamed:    zedboard_leds_switches/fw/src/design/axi_gpio_zed.v -> common/fw/src/design/axi_gpio_zed.v      
            renamed:    zedboard_leds_switches/fw/src/design/axi_identification.v -> common/fw/src/design/axi_identification.v      
            renamed:    zedboard_leds_switches/fw/src/design/axi_register_bank.v -> common/fw/src/design/axi_register_bank.v      
            renamed:    zedboard_leds_switches/fw/src/design/debounce.v -> common/fw/src/design/debounce.v      
            renamed:    common/fw/src/script/create_vivado_project.sh -> common/fw/src/script/swuk_create_vivado_project      
            renamed:    common/fw/src/script/create_vivado_project.tcl -> common/fw/src/script/swuk_create_vivado_project.tcl      
            renamed:    zedboard_leds_switches/fw/src/script/pre_synth.tcl -> common/fw/src/script/swuk_pre_synth.tcl      
            deleted:    common/fw/src/script/zedboard_presets.tcl      
            renamed:    common/other/src/script/petalinux-build-id.sh -> common/os/src/script/swuk_petalinux-build      
            renamed:    common/other/src/script/create_project_structure.sh -> common/other/src/script/swuk_create_project      
            renamed:    common/other/src/script/xilinx.sh -> common/other/src/script/swuk_xilinx      
            deleted:    common/other/src/script/xilinx_2021_2.sh      
            renamed:    common/sw/src/script/create_vitis_project.sh -> common/sw/src/script/swuk_create_vitis_project      
            renamed:    common/sw/src/script/create_vitis_project.tcl -> common/sw/src/script/swuk_create_vitis_project.tcl      
            new file:   zedboard_hello_world/.gitignore      
            deleted:    zedboard_hello_world/fw/system_wrapper.xsa      
            new file:   zedboard_leds_buttons/.gitignore      
            renamed:    zedboard_leds_buttons/fw/src/constraint/zedboard.xdc -> zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc      
            renamed:    zedboard_leds_buttons/fw/src/testbench/testbench.sv -> zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv      
            deleted:    zedboard_leds_buttons/fw/system_wrapper.xsa      
            modified:   zedboard_leds_switches/.gitignore      
            renamed:    zedboard_leds_switches/fw/src/constraint/zedboard.xdc -> zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc      
            renamed:    zedboard_leds_switches/fw/src/diagram/system/hdl/system_wrapper.sv -> zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv      
            renamed:    zedboard_leds_switches/fw/src/testbench/testbench.sv -> zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv      
            new file:   zedboard_linux/.gitignore      
            deleted:    zedboard_linux/os/src/other/zedboard_leds_switches.txt      
    
    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:   common/.gitignore      
            modified:   common/fw/src/design/axi_gpio_zed.v      
            modified:   common/fw/src/design/axi_identification.v      
            modified:   common/fw/src/design/axi_register_bank.v      
            modified:   common/fw/src/design/debounce.v      
            modified:   common/fw/src/script/swuk_create_vivado_project      
            modified:   common/fw/src/script/swuk_create_vivado_project.tcl      
            modified:   common/fw/src/script/swuk_pre_synth.tcl      
            modified:   common/os/src/script/swuk_petalinux-build      
            modified:   common/other/src/script/swuk_create_project      
            modified:   common/other/src/script/swuk_xilinx      
            modified:   common/sw/src/script/swuk_create_vitis_project      
            modified:   common/sw/src/script/swuk_create_vitis_project.tcl      
            modified:   zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc      
            modified:   zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv      
            modified:   zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc      
            modified:   zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv      
            modified:   zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv      
            modified:   zedboard_leds_switches/sw/src/c/zedboard_leds_switches.c      
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
            common/fw/src/design/heartbeat.v      
            common/fw/src/design/meta_hardener.v      
            common/fw/src/script/swuk_create_system_bd.tcl      
            common/fw/src/script/swuk_ssh      
            common/fw/src/script/swuk_terminal      
            common/fw/src/script/swuk_upload_bin      
            common/fw/src/script/swuk_upload_boot      
            common/other/src/script/swuk_sis      
    
    Looks good! Add changes and check GIT status.
    steve@Desktop:~/swuk_tutorial$ git add -A
    steve@Desktop:~/swuk_tutorial$ git status -u
    On branch my_master
    Your branch is up-to-date with 'origin/my_master'.
    
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
            new file:   .gitignore      
            new file:   common/.gitignore      
            deleted:    common/fw/src/constraint/zedboard_master_XDC_RevC_D_v3.xdc      
            renamed:    zedboard_leds_switches/fw/src/design/axi_gpio_zed.v -> common/fw/src/design/axi_gpio_zed.v      
            renamed:    zedboard_leds_switches/fw/src/design/axi_identification.v -> common/fw/src/design/axi_identification.v      
            renamed:    zedboard_leds_switches/fw/src/design/axi_register_bank.v -> common/fw/src/design/axi_register_bank.v      
            renamed:    zedboard_leds_switches/fw/src/design/debounce.v -> common/fw/src/design/debounce.v      
            new file:   common/fw/src/design/heartbeat.v      
            new file:   common/fw/src/design/meta_hardener.v      
            deleted:    common/fw/src/script/create_vivado_project.sh      
            deleted:    common/fw/src/script/create_vivado_project.tcl      
            new file:   common/fw/src/script/swuk_create_system_bd.tcl      
            new file:   common/fw/src/script/swuk_create_vivado_project      
            new file:   common/fw/src/script/swuk_create_vivado_project.tcl      
            renamed:    zedboard_leds_switches/fw/src/script/pre_synth.tcl -> common/fw/src/script/swuk_pre_synth.tcl      
            new file:   common/fw/src/script/swuk_ssh      
            new file:   common/fw/src/script/swuk_terminal      
            new file:   common/fw/src/script/swuk_upload_bin      
            new file:   common/fw/src/script/swuk_upload_boot      
            deleted:    common/fw/src/script/zedboard_presets.tcl      
            new file:   common/os/src/script/swuk_petalinux-build      
            deleted:    common/other/src/script/create_project_structure.sh      
            deleted:    common/other/src/script/petalinux-build-id.sh      
            new file:   common/other/src/script/swuk_create_project      
            new file:   common/other/src/script/swuk_sis      
            new file:   common/other/src/script/swuk_xilinx      
            deleted:    common/other/src/script/xilinx.sh      
            deleted:    common/other/src/script/xilinx_2021_2.sh      
            deleted:    common/sw/src/script/create_vitis_project.sh      
            new file:   common/sw/src/script/swuk_create_vitis_project      
            renamed:    common/sw/src/script/create_vitis_project.tcl -> common/sw/src/script/swuk_create_vitis_project.tcl      
            new file:   zedboard_hello_world/.gitignore      
            deleted:    zedboard_hello_world/fw/system_wrapper.xsa      
            new file:   zedboard_leds_buttons/.gitignore      
            renamed:    zedboard_leds_buttons/fw/src/constraint/zedboard.xdc -> zedboard_leds_buttons/fw/src/constraint/zedboard_leds_buttons.xdc      
            renamed:    zedboard_leds_buttons/fw/src/testbench/testbench.sv -> zedboard_leds_buttons/fw/src/testbench/tb_zedboard_leds_buttons.sv      
            deleted:    zedboard_leds_buttons/fw/system_wrapper.xsa      
            modified:   zedboard_leds_switches/.gitignore      
            renamed:    zedboard_leds_switches/fw/src/constraint/zedboard.xdc -> zedboard_leds_switches/fw/src/constraint/zedboard_leds_switches.xdc      
            renamed:    zedboard_leds_switches/fw/src/diagram/system/hdl/system_wrapper.sv -> zedboard_leds_switches/fw/src/design/zedboard_leds_switches.sv      
            new file:   zedboard_leds_switches/fw/src/testbench/tb_zedboard_leds_switches.sv      
            deleted:    zedboard_leds_switches/fw/src/testbench/testbench.sv      
            modified:   zedboard_leds_switches/sw/src/c/zedboard_leds_switches.c      
            new file:   zedboard_linux/.gitignore      
            deleted:    zedboard_linux/os/src/other/zedboard_leds_switches.txt      
    
    Looks good! Commit the updates, create an annotated tag and push the commit & tag up to the remote repository.
    steve@Desktop:~/swuk_tutorial$ git commit -am "File system changes required to turn the spliced repository into a more consistent single repository."
    steve@Desktop:~/swuk_tutorial$ git push
    steve@Desktop:~/swuk_tutorial$ git tag -a my_swuk_tutorial -m "SpaceWire UK Tutorial"
    steve@Desktop:~/swuk_tutorial$ git push origin my_swuk_tutorial