Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 16 - Enhance build system to generate repeatable bitstreams & add ID to PetaLinux (23 October 2022)

Introduction

This tutorial details the steps required to adapt the build system and project identification strings in such a way that identical bitstreams can be produced from a like-for-like checkout from GIT. The self-incrementing version/revision will be replaced with manual user editing to allow for better control of the version number. The build timestamp will be replaced with the GIT timestamp of the last GIT commit. The hash of the last GIT commit will be added to the list of strings to identify the exact set of source used to produce the bitstream. The build system will query GIT to obtain the information required to ensure the produced bitstream is from a fully committed & pushed set of source. Project identification will also be added to PetaLinux to make it consistent with the Firmware Identification.

Aims

The aims of this tutorial are as follows :-

    Part 1 - Project Setup

    1. Setup environment
    2. Change present working directory

    Part 2 - Firmware Development

    1. Launch Vivado & open project
    2. Add GIT hash entry to AXI Identification module
    3. Open & edit block design
    4. Add GIT hash entry to top level HDL
    5. Modify build hook scripts
    6. Check build hook script is working as expected

    Part 3 - OS Development

    1. Create project information file
    2. Modify BitBake recipe
    3. Create a PetaLinux build script
    4. Commit new & updated files
    5. Update website to display new PetaLinux information & Firmware hash
    6. Update Javascript to deal with new PetaLinux information & Firmware hash
    7. Update hardware platform
    8. Commit new & updated files
    9. Build & package PetaLinux

    Part 4 - Hardware Deployment

    1. Setup Zedboard hardware
    2. Launch MiniCom terminal emulator
    3. Deploy firmware & software on Zedboard
    4. Check everything is working as expected
    #### Part 1 - Project Setup ####

    1. Setup environment

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

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

    2. Change present working directory

    Change the present working directory to be the project directory.
    steve@Desktop:~$ cd ~/projects/zedboard_leds_switches
    #### Part 2 - Firmware Development ####

    3. Launch Vivado & open project

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

    4. Add GIT hash entry to AXI Identification module

    Update axi_identication.v to add the new GIT hash entry (id_hash) to the list of identification strings. Increase the id_version width from 16 to 32 characters. Do a little housekeeping on the source code as well.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/design/axi_identification.v

    axi_identification.v

    1. //
    2. // File .......... axi_identification.v
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.1
    5. // Date .......... 2 October 2022
    6. // Standard ...... IEEE 1364-2001
    7. // Description ...
    8. //   AXI4-Lite controlled register bank written in basic Verilog. The register
    9. // bank contains the following fixed length identification and timestamp
    10. // strings.
    11. //
    12. // Description ................ 128 Characters - 32 Registers - 0x000 - 0x07F
    13. // Company ....................  64 Characters - 16 Registers - 0x080 - 0x0BF
    14. // Author .....................  64 Characters - 16 Registers - 0x0C0 - 0x0FF
    15. // Version ....................  32 Characters -  8 Registers - 0x100 - 0x11F
    16. // Timestamp ..................  32 Characters -  8 Registers - 0x120 - 0x13F
    17. // Hash .......................  64 Characters - 16 Registers - 0x140 - 0x17F
    18. // Unused ..................... 144 Characters - 36 Registers - 0x180 - 0x20F
    19. //                              ---              ---            -------------
    20. //                              528              132          - 0x000 - 0x20F
    21. //


    22. `timescale 1 ns / 1 ps


    23. module axi_identification #
    24. (
    25.   // Parameters (AXI4-Lite)
    26.   localparam c_addr_w           = 9,                       // P:Address Bus Width (bits)
    27.   localparam c_data_w           = 32,                      // P:Data Bus Width (bits)
    28.   // Parameters (ID Strings)
    29.   localparam c_id_description_w = 128,                     // P:Description Width (chars)
    30.   localparam c_id_company_w     = 64,                      // P:Company Width (chars)
    31.   localparam c_id_author_w      = 64,                      // P:Author Width (chars)
    32.   localparam c_id_version_w     = 32,                      // P:Version Width (chars)
    33.   localparam c_id_timestamp_w   = 32,                      // P:Timestamp Width (chars)
    34.   localparam c_id_hash_w        = 64                       // P:Hash Width (chars)
    35. )
    36. (
    37.   // Clock & Reset
    38.   input  wire                             aclk,            // I:Clock
    39.   input  wire                             aresetn,         // I:Reset
    40.   // Identification Strings
    41.   input  wire [8*c_id_description_w-1 :0] id_description,  // I:Description
    42.   input  wire [8*c_id_company_w-1 : 0]    id_company,      // I:Company
    43.   input  wire [8*c_id_author_w-1 : 0]     id_author,       // I:Author
    44.   input  wire [8*c_id_version_w-1 : 0]    id_version,      // I:Version
    45.   input  wire [8*c_id_timestamp_w-1 : 0]  id_timestamp,    // I:Timestamp
    46.   input  wire [8*c_id_hash_w-1 : 0]       id_hash,         // I:Hash
    47.   // AXI4-Lite - Write Address Channel
    48.   input  wire [c_addr_w-1 : 0]            s_axi_awaddr,    // I:Write Address
    49.   input  wire [2 : 0]                     s_axi_awprot,    // I:Write Protection Type
    50.   input  wire                             s_axi_awvalid,   // I:Write Address Valid
    51.   output reg                              s_axi_awready,   // O:Write Address Ready
    52.   // AXI4-Lite - Write Data Channel
    53.   input  wire [c_data_w-1 : 0]            s_axi_wdata,     // I:Write Data
    54.   input  wire [(c_data_w/8)-1 : 0]        s_axi_wstrb,     // I:Write Strobes
    55.   input  wire                             s_axi_wvalid,    // I:Write Valid
    56.   output reg                              s_axi_wready,    // O:Write Ready
    57.   // AXI4-Lite - Write Response Channel
    58.   output reg  [1 : 0]                     s_axi_bresp,     // O:Write Response
    59.   output reg                              s_axi_bvalid,    // O:Write Response Valid
    60.   input  wire                             s_axi_bready,    // I:Write Response Ready
    61.   // AXI4-Lite - Read Address Channel
    62.   input  wire [c_addr_w-1 : 0]            s_axi_araddr,    // I:Read Address
    63.   input  wire [2 : 0]                     s_axi_arprot,    // I:Read Protection Type
    64.   input  wire                             s_axi_arvalid,   // I:Read Address Valid
    65.   output reg                              s_axi_arready,   // O:Read Address Ready
    66.   // AXI4-Lite - Read Data Channel
    67.   output reg  [c_data_w-1 : 0]            s_axi_rdata,     // O:Read Data
    68.   output reg  [1 : 0]                     s_axi_rresp,     // O:Read Response
    69.   output reg                              s_axi_rvalid,    // O:Read Valid
    70.   input  wire                             s_axi_rready     // I:Read Ready
    71. );


    72.   // Constants
    73.   localparam no_regs = 2**(c_addr_w - $clog2(c_data_w / 8));
    74.   localparam id_description_lo = 0;
    75.   localparam id_description_hi = id_description_lo + (c_id_description_w / 4) - 1;
    76.   localparam id_company_lo     = 1 + id_description_hi;
    77.   localparam id_company_hi     = id_company_lo + (c_id_company_w / 4) - 1;
    78.   localparam id_author_lo      = 1 + id_company_hi;
    79.   localparam id_author_hi      = id_author_lo + (c_id_author_w / 4) -1;
    80.   localparam id_version_lo     = 1 + id_author_hi;
    81.   localparam id_version_hi     = id_version_lo + (c_id_version_w / 4) - 1;
    82.   localparam id_timestamp_lo   = 1 + id_version_hi;
    83.   localparam id_timestamp_hi   = id_timestamp_lo + (c_id_timestamp_w / 4) - 1;
    84.   localparam id_hash_lo        = 1 + id_timestamp_hi;
    85.   localparam id_hash_hi        = id_hash_lo + (c_id_hash_w / 4) - 1;


    86.   // Signals
    87.   reg aw_en;
    88.   reg [c_addr_w-$clog2(c_data_w / 8) - 1 : 0] axi_awaddr;
    89.   reg [c_addr_w-$clog2(c_data_w / 8) - 1 : 0] axi_araddr;
    90.   reg [c_data_w-1 : 0] registers_wr [no_regs - 1 : 0];
    91.   wire [c_data_w-1 : 0] registers_rd [no_regs - 1 : 0];


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


    105.   // Write Address Latch
    106.   always @(posedge aclk)
    107.     if (!aresetn)
    108.       axi_awaddr <= {c_addr_w{1'b0}};
    109.     else if (s_axi_awvalid && !s_axi_awready && s_axi_wvalid && aw_en)
    110.       axi_awaddr <= s_axi_awaddr[c_addr_w - 1 : $clog2(c_data_w/8)];


    111.   // Write Data Ready
    112.   always @(posedge aclk)
    113.     if (!aresetn)
    114.       s_axi_wready <= 1'b0;
    115.     else if (s_axi_awvalid && s_axi_wvalid && !s_axi_wready && aw_en)
    116.       s_axi_wready <= 1'b1;
    117.     else
    118.       s_axi_wready <= 1'b0;


    119.   // Write Data
    120.   integer i;
    121.   always @(posedge aclk)
    122.     if (!aresetn)
    123.       for (i = 0; i <= no_regs - 1; i = i + 1)
    124.         registers_wr[i] <= {c_data_w{1'b0}};
    125.     else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready)
    126.       for (i = 0; i <= c_data_w/8 - 1; i = i + 1)
    127.         if (s_axi_wstrb[i])
    128.           registers_wr[axi_awaddr][i*8 +: 8] <= s_axi_wdata[i*8 +: 8]; // Byte-Wise Write


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


    139.   // Read Address Ready
    140.   always @(posedge aclk)
    141.     if (!aresetn)
    142.       s_axi_arready <= 1'b0;
    143.     else if (s_axi_arvalid && !s_axi_arready)
    144.       s_axi_arready <= 1'b1;
    145.     else
    146.       s_axi_arready <= 1'b0;


    147.   // Read Address Latch
    148.   always @(posedge aclk)
    149.     if (!aresetn)
    150.       axi_araddr  <= {c_addr_w{1'b0}};
    151.     else if (s_axi_arvalid && !s_axi_arready)
    152.       axi_araddr <= s_axi_araddr[c_addr_w - 1 : $clog2(c_data_w/8)];


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


    163.   // Read Data
    164.   always @(posedge aclk)
    165.     if (!aresetn)
    166.       s_axi_rdata <= {c_data_w{1'b0}};
    167.     else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid)
    168.       s_axi_rdata <= registers_rd[axi_araddr];


    169.   // Read Register Constants
    170.   genvar j;
    171.   generate
    172.     for (j = 0; j <= no_regs - 1; j = j + 1) begin
    173.       if      (j <= id_description_hi) assign registers_rd[j] = id_description[32*(j-id_description_lo) +: 32];
    174.       else if (j <= id_company_hi    ) assign registers_rd[j] = id_company    [32*(j-id_company_lo    ) +: 32];
    175.       else if (j <= id_author_hi     ) assign registers_rd[j] = id_author     [32*(j-id_author_lo     ) +: 32];
    176.       else if (j <= id_version_hi    ) assign registers_rd[j] = id_version    [32*(j-id_version_lo    ) +: 32];
    177.       else if (j <= id_timestamp_hi  ) assign registers_rd[j] = id_timestamp  [32*(j-id_timestamp_lo  ) +: 32];
    178.       else if (j <= id_hash_hi       ) assign registers_rd[j] = id_hash       [32*(j-id_hash_lo       ) +: 32];
    179.       else                             assign registers_rd[j] = 32'h00000000;
    180.     end
    181.   endgenerate


    182. endmodule

    5. Open & edit block design

    Open the block design by clicking on Open Block Design under the IP INTEGRATOR heading inside the Flow Navigator section.

    At the top of the the BLOCK DESIGN section there should be a message saying Module references are out-of-date. Refresh Changed Modules. Click on Refresh Changed Modules to update the block design. Missing Image! The axi_identification_0 module in the block design should now show an extra input port called id_hash. Bring the new port out of the block design by right clicking on it and selecting Make External from the context menu. Rename the new external port to id_hash by removing the _0 from its name.

    The id_version external port requires its width to be increased. Click on the id_version external port and in the External Ports Properties pane select Properties and change LEFT from 127 to 255. Missing Image! Verify the block design is error free by clicking on the Validate Design Missing Image! icon. Once validated save the block design. Missing Image!

    6. Add GIT hash entry to top level HDL

    Update system_wrapper.sv to add the new GIT hash entry id_hash to the list of identification strings. Do a little housekeeping on the source code as well.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/diagram/system/hdl/system_wrapper.sv

    system_wrapper.sv

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


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


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


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


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


    96. endmodule


    7. Modify build hook scripts

    Remove the build timestamp field from the product identification file as it is no longer required (the GIT timestamp will be used instead). Manually change the version to 4.0 which will be the version number for the next release of firmware. Note the previous version did not align with the GIT tag which was a mistake, this will now be corrected by using version 4.0.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/project.txt

    project.txt

    1. Zedboard LEDs & Switches Example Design
    2. SpaceWire UK
    3. Steve Haywood
    4. 4.0

    Edit the pre-synthesis TCL script to remove the auto-incrementing version/revision functionality and add in the necessary GIT queries to obtain the identification fields and determine if the build source is clean (no pending commits or pushes).
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/script/pre_synth.tcl

    pre_synth.tcl

    1. #
    2. # File .......... pre_synth.tcl
    3. # Author ........ Steve Haywood
    4. # Version ....... 1.1
    5. # Date .......... 2 October 2022
    6. # Description ...
    7. #   Script to read product/project information from a text file, obtain GIT
    8. # repository information and set the generics/parameters on the top level
    9. # module.
    10. #

    11. # Read Product/Project Information
    12. if { [catch {set id_file [open "../../../project.txt" r]} msg] } {
    13.   set id_description "?"
    14.   set id_company "?"
    15.   set id_author "?"
    16.   set id_version "?"
    17.   puts "ERROR :-"
    18.   puts $msg
    19. } {
    20.   gets $id_file id_description
    21.   gets $id_file id_company
    22.   gets $id_file id_author
    23.   gets $id_file id_version
    24.   close $id_file
    25. }

    26. # Get GIT timestamp
    27. set dfmt "%d-%b-%Y - %H:%M:%S"
    28. if { [catch {set id_timestamp [exec git log -1 --pretty=%cd --date=format:$dfmt]} msg] } {
    29.   set id_timestamp "00-Xxx-0000 - 00:00:00"
    30.   puts "ERROR :-"
    31.   puts $msg
    32. }

    33. # Get GIT hash
    34. if { [catch {set id_hash [exec git log -1 --pretty=%H]} msg] } {
    35.   set id_hash "0000000000000000000000000000000000000000"
    36.   puts "ERROR :-"
    37.   puts $msg
    38. }

    39. # Get GIT status
    40. if { [catch {set status [exec git status -s]} msg] } {
    41.   append id_version " (undefined)"
    42.   puts "ERROR :-"
    43.   puts $msg
    44. } {
    45.   if {$status ne ""} {
    46.     append id_version " (unstaged)"
    47.   } {
    48.     if { [catch {set status [exec git branch -r --contains $id_hash]} msg] } {
    49.       append id_version " (undefined)"
    50.       puts "ERROR :-"
    51.       puts $msg
    52.     } {
    53.       if {$status eq ""} {
    54.         append id_version " (unpushed)"
    55.       }
    56.     }
    57.   }
    58. }

    59. # Replace space character with its octal code
    60. proc space_replace {str} {
    61.   return [string map {" " "\\040"} $str]
    62. }

    63. # Replace spaces in information strings
    64. set id_description [space_replace $id_description]
    65. set id_company     [space_replace $id_company]
    66. set id_author      [space_replace $id_author]
    67. set id_version     [space_replace $id_version]
    68. set id_timestamp   [space_replace $id_timestamp]
    69. set id_hash        [space_replace $id_hash]

    70. # Set Generics/Parameters
    71. set_property generic " \
    72.   id_description=\"$id_description\" \
    73.   id_company=\"$id_company\" \
    74.   id_author=\"$id_author\" \
    75.   id_version=\"$id_version\" \
    76.   id_timestamp=\"$id_timestamp\" \
    77.   id_hash=\"$id_hash\" \
    78. " [current_fileset]

    Remove the post-bitstream hook from Vivado's build process by navigating Tools » Settings.... In the Settings dialog select Bitstream under Project Settings and highlight the previously entered TCL script ~/projects/zedboard_leds_switches/fw/src/script/post_bit.tcl. Press the Delete key to remove it and then click Apply followed by OK.

    Remove the post-bitstream TCL script from the repository.
    steve@Desktop:~/projects/zedboard_leds_switches$ git rm fw/src/script/post_bit.tcl
    For the modified script to work properly untracked files need to be kept away from the status command. To do this a blanket GIT ignore can be added that ignores everything GIT isn't tracking.
    steve@Desktop:~/projects/zedboard_leds_switches$ echo "/*" > .gitignore
    steve@Desktop:~/projects/zedboard_leds_switches$ git add -f .gitignore
    Just like the bitstream, the hardware platform can be generated from source, hence it is prudent at this point to remove it from the repository.
    steve@Desktop:~/projects/zedboard_leds_switches$ git rm fw/system_wrapper.xsa

    8. Check build hook script is working as expected

    Check the current status of the GIT repository.
    steve@Desktop:~/projects/zedboard_leds_switches$ git status
    On branch master
    Your branch is up-to-date with 'origin/master'.

    Changes to be committed:
      (use "git restore --staged ..." to unstage)
      new file:   .gitignore
      deleted:    fw/src/script/post_bit.tcl
      deleted:    fw/system_wrapper.xsa


    Changes not staged for commit:
      (use "git add ..." to update what will be committed)
      (use "git restore ..." to discard changes in working directory)
      modified:   fw/project.txt
      modified:   fw/src/design/axi_identification.v
      modified:   fw/src/diagram/system/hdl/system_wrapper.sv
      modified:   fw/src/diagram/system/system.bd
      modified:   fw/src/script/pre_synth.tcl

    Examine the last log entry for the GIT repository.
    steve@Desktop:~/projects/zedboard_leds_switches$ git log -1
    commit 42e2ca1aceecb80a55942e7540060b3a1b75d17f (HEAD -> master, tag: v3.0, origin/master, origin/HEAD)
    Author: Steve Haywood <steve@spacewire.co.uk>
    Date:   Sun Jan 16 12:48:44 2022 +0000

        Added AXI Identification to the design.
    Run synthesis to see what values are placed in the identification parameters by the pre_synth TCL script. Click on Run Synthesis under the SYNTHESIS heading inside the Flow Navigator section. Missing Image! Examine the Log tab at the bottom of the Vivado cockpit window, scrolling down a little to see the parameter report. Missing Image! The id_timestamp & id_hash are as shown in the log of the last GIT commit. The id_version is as entered in the project.txt file but is appended with (unstaged) to illustrate the bitstream will NOT be produced from files committed in the local repository.

    Commit the files.
    steve@Desktop:~/projects/zedboard_leds_switches$ git commit -am "Updated Firmware Identification fields to use static information from GIT"
    steve@Desktop:~/projects/zedboard_leds_switches$ git tag -a v4.0 -m "ZYNQ, GPIO, Register Bank & Identification (timestamp & hash from GIT)"
    Rerun GIT status & log.
    steve@Desktop:~/projects/zedboard_leds_switches$ git status
    On branch master
    Your branch is ahead of 'origin/master' by 1 commit.
      (use "git push" to publish your local commits)

    nothing to commit, working tree clean
    steve@Desktop:~/projects/zedboard_leds_switches$ git log -1
    commit cc42e7ba8be329a22b17f7a78f3d28d9784f59a0 (HEAD -> master, tag: v4.0)
    Author: Steve Haywood <steve@spacewire.co.uk>
    Date:   Sun Oct 23 09:25:16 2022 +0100

        Updated Firmware Identification fields to use static information from GIT
    Reset the synthesis run by right clicking on Run Synthesis under SYNTHESIS and selecting Reset Synthesis Run,. Missing Image! Run synthesis again. Missing Image! Examine the Log tab again. Missing Image! The id_timestamp & id_hash are as shown in the log of the last GIT commit (now updated). The id_version is as entered in the project.txt file but is appended with (unpushed) to illustrate the bitstream will NOT be produced from files pushed to the remote repository.

    Push the files to the remote repository.
    steve@Desktop:~/projects/zedboard_leds_switches$ git push
    Again, reset the synthesis run, run the synthesis and check the parameter report in the log tab. Missing Image! Bingo! The id_timestamp & id_hash are as shown in the log of the last GIT commit. The id_version is as entered in the project.txt and is clean (no appended text) illustrating the bitstream will be produced from source safely held in the remote repository.

    A generated bitstream (.bit) contains a 112 byte header that contains various pieces of dynamic information such as the build timestamp. A secondary bitstream (.bin) file can also be created that doesn't contain this header. To enable the creatation of the alternative bitstream navigate Tools » Settings... from the main menu and select Bitstream under Project Settings. Check the -bin_file option and click OK to commit the changes. Missing Image! Generate the programmable logic bitstream by clicking on Generate Bitstream under the PROGRAM AND DEBUG heading inside the Flow Navigator section. Missing Image! Obtain the checksum of the bitstream by using either system_wrapper.bit (less the header) or system_wrapper.bin.
    steve@Desktop:~/projects/zedboard_leds_switches$ dd if=fw/vivado/project.runs/impl_1/system_wrapper.bit skip=112 bs=512 iflag=skip_bytes | md5sum
    7901+1 records in
    7901+1 records out
    4045564 bytes (4.0 MB, 3.9 MiB) copied, 0.00829275 s, 488 MB/s
    aa330b5b405de83453b6bdb36abf8197  -
    steve@Desktop:~/projects/zedboard_leds_switches$ md5sum fw/vivado/project.runs/impl_1/system_wrapper.bin
    aa330b5b405de83453b6bdb36abf8197  fw/vivado/project.runs/impl_1/system_wrapper.bin
    Quit Vivado and delete (or rename) the project folder.
    steve@Desktop:~/projects/zedboard_leds_switches$ cd ..
    steve@Desktop:~/projects$ rm -rf zedboard_leds_switches
    Clone the GIT repository of the project, checkout the version as shown by the hash held in the Firmware and rebuilt the project from scratch. The hardware platform is automatically exported by the build script after the bitstream is generated.
    steve@Desktop:~/projects$ git clone git@192.168.2.20:zedboard_leds_switches.git
    steve@Desktop:~/projects$ cd zedboard_leds_switches
    steve@Desktop:~/projects/zedboard_leds_switches$ git checkout cc42e7ba8be329a22b17f7a78f3d28d9784f59a0
    steve@Desktop:~/projects/zedboard_leds_switches$ create_vivado_project.sh build
    Obtain the checksum of the bitstream by using system_wrapper.bit (less the header).
    steve@Desktop:~/projects/zedboard_leds_switches$ dd if=fw/vivado/project.runs/impl_1/system_wrapper.bit skip=112 bs=512 iflag=skip_bytes | md5sum
    7901+1 records in
    7901+1 records out
    4045564 bytes (4.0 MB, 3.9 MiB) copied, 0.00829275 s, 488 MB/s
    aa330b5b405de83453b6bdb36abf8197  -
    Lovely Jubbly! The bitstreams from the previous build and the new build are identical.
    #### Part 3 - OS Development ####

    9. Create project information file

    Create a project information file for the PetaLinux project just like the one used for for the Vivado project. Set the version to 10.0 to align with the next GIT tag that will be used.
    Steve@Desktop:~/projects/zedboard_leds_switches$ cd ~/projects/zedboard_linux/os/petalinux
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/website/files/project.txt

    project.txt

    1. Zedboard PetaLinux Example Design
    2. SpaceWire UK
    3. Steve Haywood
    4. 10.0

    10. Modify BitBake recipe

    Modify the BitBake recipe to include the new project information file.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/website/website.bb

    website.bb

    1. #
    2. # This file is the website recipe.
    3. #

    4. SUMMARY = "Simple website application"
    5. SECTION = "PETALINUX/apps"
    6. LICENSE = "MIT"
    7. LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

    8. SRC_URI = "file://index.html"
    9. SRC_URI += "file://uptime.js"
    10. SRC_URI += "file://zedboard.png"
    11. SRC_URI += "file://styles.css"
    12. SRC_URI += "file://cgi-bin/index.cgi"
    13. SRC_URI += "file://cgi-bin/uptime.cgi"
    14. SRC_URI += "file://amber.gif"
    15. SRC_URI += "file://green.gif"
    16. SRC_URI += "file://red.gif"
    17. SRC_URI += "file://project.txt"

    18. FILES_${PN} += "/srv/www"

    19. S = "${WORKDIR}"

    20. do_install() {
    21.      install -d ${D}/srv/www
    22.      install -m 0644 ${S}/index.html ${D}/srv/www/index_original.html
    23.      install -m 0644 ${S}/uptime.js ${D}/srv/www
    24.      install -m 0644 ${S}/zedboard.png ${D}/srv/www
    25.      install -m 0644 ${S}/styles.css ${D}/srv/www
    26.      install -m 0644 ${S}/amber.gif ${D}/srv/www
    27.      install -m 0644 ${S}/green.gif ${D}/srv/www
    28.      install -m 0644 ${S}/red.gif ${D}/srv/www
    29.      install -m 0644 ${S}/project.txt ${D}/srv/www
    30.      install -d ${D}/srv/www/cgi-bin
    31.      install -m 0755 ${S}/cgi-bin/index.cgi ${D}/srv/www/cgi-bin
    32.      install -m 0755 ${S}/cgi-bin/uptime.cgi ${D}/srv/www/cgi-bin
    33. }

    11. Create a PetaLinux build script

    Create a PetaLinux build script similar to the pre-synthesis TCL script. This script will query GIT and update the project information file with the timestamp & hash of the last commit, the one that is currently being used to build the PetaLinux project.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ cd ~/projects/common
    steve@Desktop:~/projects/common$ subl other/src/script/petalinux-build-id.sh
    steve@Desktop:~/projects/common$ chmod +x other/src/script/petalinux-build-id.sh

    petalinux-build-id.sh

    1. #!/bin/bash

    2. #
    3. # File .......... petalinux-build-id.tcl
    4. # Author ........ Steve Haywood
    5. # Version ....... 1.0
    6. # Date .......... 2 October 2022
    7. # Description ...
    8. #   Script to read project information file, obtain GIT repository information,
    9. # append project information file with GIT information, build PetaLinux project
    10. # and then restore project information file.
    11. #
    12. #

    13. # Set identification file
    14. project="project-spec/meta-user/recipes-apps/website/files/project.txt"

    15. # Only perform bulk of actions if identification file
    16. if [ -f "$project" ]; then

    17.   # Read project information
    18.   readarray -t ids < project-spec/meta-user/recipes-apps/website/files/project.txt

    19.   # Display identification information
    20.   echo -e '\033[1mNOTE\033[0m: Building PetaLinux with the following identification'
    21.   echo "Description ... ${ids[0]}"
    22.   echo "Company ....... ${ids[1]}"
    23.   echo "Author ........ ${ids[2]}"

    24.   # Get GIT timestamp
    25.   id_timestamp=$(git log -1 --pretty=%cd --date=format:"%d-%b-%Y - %H:%M:%S")
    26.   if [ $? -ne 0 ]; then
    27.     id_timestamp="00-Xxx-0000 - 00:00:00"
    28.   fi

    29.   # Get GIT hash
    30.   id_hash=$(git log -1 --pretty=%H)
    31.   if [ $? -ne 0 ]; then
    32.     id_hash="0000000000000000000000000000000000000000"
    33.   fi

    34.   # Get GIT status
    35.   id_append=""
    36.   status=$(git status -s)
    37.   if [ $? -ne 0 ]; then
    38.     id_append="(undefined)"
    39.   else
    40.     if [ ! -z "$status" ]; then
    41.       id_append="(unstaged)"
    42.     else
    43.       status=$(git branch -r --contains ${id_hash})
    44.       if [ $? -ne 0 ]; then
    45.         id_append="(undefined)"
    46.       else
    47.         if [ -z "$status" ]; then
    48.           id_append="(unpushed)"
    49.         fi
    50.       fi
    51.     fi
    52.   fi

    53.   # Display identification information
    54.   echo "Version ....... ${ids[3]} ${id_append}"
    55.   echo "Timestamp ..... $id_timestamp"
    56.   echo "Hash .......... $id_hash"

    57.   # Modify project information file
    58.   fname="$project"
    59.   echo ${ids[0]}               > ${fname} # Description
    60.   echo ${ids[1]}              >> ${fname} # Company
    61.   echo ${ids[2]}              >> ${fname} # Author
    62.   echo ${ids[3]} ${id_append} >> ${fname} # Version
    63.   echo ${id_timestamp}        >> ${fname} # Timestamp
    64.   echo ${id_hash}             >> ${fname} # Hash

    65.   # Build PetaLinux
    66.   $(which petalinux-build)
    67.   jobs -l
    68.   wait

    69.   # Restore project information file
    70.   echo ${ids[0]}  > ${fname} # Description
    71.   echo ${ids[1]} >> ${fname} # Company
    72.   echo ${ids[2]} >> ${fname} # Author
    73.   echo ${ids[3]} >> ${fname} # Version

    74. else

    75.   # Build PetaLinux
    76.   $(which petalinux-build)

    77. fi

    Make life easier by using an alias for petalinux-build such that it calls petalinux-build-id, add this within the shell initialization script.
    steve@Desktop:~/projects/common$ subl ~/.bashrc

    .bashrc (partial)

    1. # Aliases
    2. alias petalinux-build='~/projects/common/other/src/script/petalinux-build-id.sh'

    12. Commit new & updated files

    Commit the updated Common files and push the commit up to the remote repository.
    steve@Desktop:~/projects/common$ git add other/src/script/petalinux-build-id.sh
    steve@Desktop:~/projects/common$ git commit -am "Added PetaLinux build script that updates the project information file with timestamp & hash."
    steve@Desktop:~/projects/common$ git push
    steve@Desktop:~/projects/common$ cd ~/projects/zedboard_linux/os/petalinux

    13. Update website to display new PetaLinux information & Firmware hash

    Update the existing HTML to include a new PetaLinux Information section & add the GIT hash entry in the Firmware Information section.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/website/files/cgi-bin/index.cgi

    index.cgi

    1. #!/bin/sh

    2. # Output Header
    3. printf "Content-type: text/html\n\n"

    4. # Get information
    5. sys_host=$(hostname)
    6. sys_time=$(date)
    7. sys_load=$(awk '{print $1}' /proc/loadavg)
    8. sys_up=$(awk '{print $1}' /proc/uptime)
    9. cpu_model=$(grep model /proc/cpuinfo | cut -d : -f2 | tail -1 | sed 's/\s//')
    10. cpu_cores=$(grep -c ^processor /proc/cpuinfo)
    11. mem_total=$(free -m | awk 'NR==2{print $2}')
    12. mem_used=$(free -m | awk 'NR==2{print $3}')
    13. mem_free=$(free -m | awk 'NR==2{print $4}')
    14. net_mac=$(cat /sys/class/net/eth0/address)
    15. net_ip_loc=$(ip a | grep inet | grep -vw lo | grep -v inet6 | cut -d \/ -f1 | sed 's/[^0-9\.]*//g')
    16. net_ip_ext=$(wget -q -O- http://ipecho.net/plain)

    17. # Output HTML
    18. cat <<EOF
    19. <!DOCTYPE html>
    20. <html lang="en">
    21. <head>
    22. <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    23. <link href="../styles.css" rel="stylesheet">
    24. <title>Zedboard Webserver</title>
    25. </head>
    26. <body onload="add_register()">

    27. <div class="section"><h2>Zedboard Webserver</h2></div>

    28. <div class="section">
    29. <table>
    30. <thead>
    31. <tr><th colspan="3">Operating System Information <button onclick="read_os_ids()">Read ID</button></th>
    32. </tr>
    33. </thead>
    34. <tbody>
    35. <tr>
    36. <td style="text-align:right">Description :</td>
    37. <td style="text-align:left" id="oid_0">Unknown</td>
    38. <td><img id="osid_0" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    39. </tr>
    40. <tr>
    41. <td style="text-align:right">Company :</td>
    42. <td style="text-align:left" id="oid_1">Unknown</td>
    43. <td><img id="osid_1" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    44. </tr>
    45. <tr>
    46. <td style="text-align:right">Author :</td>
    47. <td style="text-align:left" id="oid_2">Unknown</td>
    48. <td><img id="osid_2" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    49. </tr>
    50. <tr>
    51. <td style="text-align:right">Version :</td>
    52. <td style="text-align:left" id="oid_3">Unknown</td>
    53. <td><img id="osid_3" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    54. </tr>
    55. <tr>
    56. <td style="text-align:right">Timestamp :</td>
    57. <td style="text-align:left" id="oid_4">Unknown</td>
    58. <td><img id="osid_4" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    59. </tr>
    60. <tr>
    61. <td style="text-align:right">Hash :</td>
    62. <td style="text-align:left" id="oid_5">Unknown</td>
    63. <td><img id="osid_5" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    64. </tr>
    65. </tbody>
    66. </table>
    67. <table>
    68. <thead>
    69. <tr><th colspan="3">Firmware Information <input type="submit" value="Read ID" id="read_ids" onclick="read_ids()"></th>
    70. </tr>
    71. </thead>
    72. <tbody>
    73. <tr>
    74. <td style="text-align:right">Description :</td>
    75. <td style="text-align:left" id="id_0">Unknown</td>
    76. <td><img id="sid_0" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    77. </tr>
    78. <tr>
    79. <td style="text-align:right">Company :</td>
    80. <td style="text-align:left" id="id_1">Unknown</td>
    81. <td><img id="sid_1" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    82. </tr>
    83. <tr>
    84. <td style="text-align:right">Author :</td>
    85. <td style="text-align:left" id="id_2">Unknown</td>
    86. <td><img id="sid_2" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    87. </tr>
    88. <tr>
    89. <td style="text-align:right">Version :</td>
    90. <td style="text-align:left" id="id_3">Unknown</td>
    91. <td><img id="sid_3" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    92. </tr>
    93. <tr>
    94. <td style="text-align:right">Timestamp :</td>
    95. <td style="text-align:left" id="id_4">Unknown</td>
    96. <td><img id="sid_4" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    97. </tr>
    98. <tr>
    99. <td style="text-align:right">Hash :</td>
    100. <td style="text-align:left" id="id_5">Unknown</td>
    101. <td><img id="sid_5" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    102. </tr>
    103. </tbody>
    104. </table>
    105. </div>

    106. <div class="section"><img src="../zedboard.png" alt="Missing Image!"></div>

    107. <div class="section">

    108. <table>
    109. <tr><th colspan="2">System</th></tr>
    110. <tr><td>Hostname</td>
    111. <td>${sys_host}</td>
    112. </tr><tr><td>Time</td><td>${sys_time}</td></tr>
    113. <tr><td>Uptime</td><td><span id="uptime_text">${sys_up}</span> seconds <button onclick="get_uptime()">Refresh</button> Auto :
    114. <select id="uptime" onchange="uptime();">
    115.   <option value="0">Off</option>
    116.   <option value="1">1s</option>
    117.   <option value="5">5s</option>
    118. </select>
    119. </td></tr>
    120. </table>

    121. <table>
    122. <tr><th colspan="2">CPU</th></tr>
    123. <tr><td>Model</td>
    124. <td>${cpu_model}</td></tr>
    125. <tr><td>Cores</td><td>${cpu_cores}</td></tr>
    126. <tr><td>Load</td><td>${sys_load}</td></tr>
    127. </table>

    128. <table>
    129. <tr><th colspan="2">Memory</th></tr>
    130. <tr><td>Total</td><td>${mem_total} Mb</td></tr>
    131. <tr><td>Used</td><td>${mem_used} Mb</td></tr>
    132. <tr><td>Free</td><td>${mem_free} Mb</td></tr>
    133. </table>

    134. <table>
    135. <tr><th colspan="2">Network</th></tr>
    136. <tr><td>MAC Address</td><td>${net_mac}</td></tr>
    137. <tr><td>Internal IP</td><td>${net_ip_loc}</td></tr>
    138. <tr><td>External IP</td><td>${net_ip_ext}</td></tr>
    139. </table>

    140. </div>

    141. <div class="section">
    142. <table id="registers">
    143. <tr>
    144. <th>Address</th>
    145. <th>Peek Value</th>
    146. <th>Sel</th>
    147. <th>Peek</th>
    148. <th>Status</th>
    149. <th>Copy</th>
    150. <th>Poke Value</th>
    151. <th>Sel</th>
    152. <th>Poke</th>
    153. <th>Status</th>
    154. <th>Description</th>
    155. </tr>
    156. </table>
    157. <br><br>
    158. <input title="Add new row to end of address table" type="button" value="Add" onclick="add_row()">
    159. <select title="Set type of row to add to address table" id="type">
    160.   <option value="0">Register</option>
    161.   <option value="1">Section</option>
    162. </select>
    163. <input title="Remove last address from table" type="button" value="Remove" onclick="rem_register()">
    164. <input title="Peek all selected addresses in table" type="button" value="Peek All" onclick="peek_all()">
    165. <input title="Copy all table peek values into poke values" type="button" value="Copy All" onclick="copy_all()">
    166. <input title="Poke all selected addresses in table" type="button" value="Poke All" onclick="poke_all()">
    167. Peek Refresh :
    168. <select title="Set timer interval for automatic peek of table addresses" id="timer" onchange="timer()">
    169.   <option value="0">Off</option>
    170.   <option value="1">1s</option>
    171.   <option value="5">5s</option>
    172. </select>
    173. Number Format :
    174. <select title="Set number format for peek and poke values" id="format" onchange="format()">
    175.   <option value="0">Hexadecimal</option>
    176.   <option value="1">Unsigned</option>
    177. </select>
    178. Configuration :
    179. <button title="Create configuration file from table" onclick="create_config()">Create...</button> <a title="Right click and Save Link As... to locate and rename this file" download="config.txt" id="download" href="" style="display: none">config.txt</a>
    180. <input title="Read configuration file into table" type="file" id="load_config">
    181. </div>

    182. <div class="section">Designed by Steve Haywood @ 2021</div>

    183. <script src="../uptime.js"></script>

    184. </body>
    185. </html>
    186. EOF

    14. Update Javascript to deal with new PetaLinux information & Firmware hash

    Update the existing Javascript to include code for reading and displayed the PetaLinux Information, and reading & displaying the GIT hash string in the Firmware Information section.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/website/files/uptime.js

    uptime.js

    1. // Requests
    2. var timer_uptime;
    3. var timer_peek_all;

    4. // Download OS information file & display result
    5. async function read_os_ids() {
    6.   let response = await fetch("/project.txt");
    7.   if (response.status == 200) {
    8.     let ids = await response.text();
    9.     fields = ids.split(/\r?\n/);
    10.   }
    11.   for (var i = 0; i < 6; i++) {
    12.     const now = Date.now();
    13.     const txt_obj = document.getElementById("oid_" + i);
    14.     const img_obj = document.getElementById("osid_" + i);
    15.     if (response.status == 200) {
    16.       if (i < fields.length && fields[i] != "") {
    17.         img_obj.src = "../green.gif?" + now;
    18.         img_obj.title = "Last file fetch successful";
    19.         txt_obj.innerHTML = fields[i];
    20.       } else {
    21.         img_obj.src = "../red.gif?" + now;
    22.         img_obj.title = "Missing field information";
    23.         txt_obj.innerHTML = "Unknown";
    24.       }
    25.     } else {
    26.       img_obj.src = "../red.gif?" + now;
    27.       img_obj.title = "Last file fetch failed";
    28.       txt_obj.innerHTML = "Unknown";
    29.     }
    30.   }
    31. }

    32. // Peek all strings
    33. function read_ids() {
    34.   read_id(0x000, 0);
    35.   read_id(0x080, 1);
    36.   read_id(0x0C0, 2);
    37.   read_id(0x100, 3);
    38.   read_id(0x120, 4);
    39.   read_id(0x140, 5);
    40. }

    41. // Peek string & display result
    42. function read_id(offset, reg) {
    43.   var url = "/cgi-bin/peekstring?0x40000000&4096&" + offset + "&128";
    44.   if (window.XMLHttpRequest) {
    45.     var ajaxReq = new XMLHttpRequest();
    46.     ajaxReq.onreadystatechange = function() {
    47.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    48.         var respText = ajaxReq.responseText;
    49.         var img_obj = document.getElementById("sid_" + reg);
    50.         // Unique number is added to image to avoid caching issues on separate animations
    51.         const now = Date.now();
    52.         if (respText.substr(0,6) == "Error:") {
    53.           img_obj.src = "../red.png?" + now;
    54.           img_obj.title = "Last peekstring failed : " + respText.substr(7);
    55.         } else {
    56.           const now = Date.now();
    57.           img_obj.src = "../green.gif?" + now;
    58.           img_obj.title = "Last peekstring successful";
    59.           document.getElementById("id_" + reg).innerHTML = respText;
    60.         }
    61.       }
    62.     }
    63.     ajaxReq.open("POST", url, true);
    64.     ajaxReq.send(null);
    65.   }
    66. }

    67. // Get uptime
    68. function get_uptime(reg) {
    69.   var url = "cgi-bin/uptime.cgi";
    70.   if (window.XMLHttpRequest) {
    71.     var ajaxReq = new XMLHttpRequest();
    72.     ajaxReq.onreadystatechange = function() {
    73.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    74.         var respText = ajaxReq.responseText;
    75.         txtObj = document.getElementById("uptime_text");
    76.         if (txtObj) {
    77.           txtObj.innerHTML = respText;
    78.         }
    79.       }
    80.     }
    81.     ajaxReq.open("POST", url, true);
    82.     ajaxReq.send(null);
    83.   }
    84. }

    85. // Update uptime timer
    86. function uptime() {
    87.   clearInterval(timer_uptime);
    88.   var uptime = document.getElementById("uptime");
    89.   var interval = uptime.value;
    90.   if (interval > 0) {
    91.     timer_uptime = setInterval("get_uptime()", 1000 * interval);
    92.   }
    93. }

    94. // Update peek_all timer
    95. function timer() {
    96.   clearInterval(timer_peek_all);
    97.   var timer = document.getElementById("timer");
    98.   interval = timer.value;
    99.   if (interval > 0) {
    100.     timer_peek_all = setInterval("peek_all()", 1000 * interval);
    101.   }
    102. }

    103. // Update number format
    104. function format() {
    105.   var table = document.getElementById("registers");
    106.   var rows = table.rows.length;
    107.   var peek;
    108.   var poke;
    109.   for (var index = 0; index < rows; index++) {
    110.     peek = document.getElementById("peek_" + index);
    111.     if (peek) {
    112.       peek.value = fmtUnsignedLong(parseInt(peek.value));
    113.     }
    114.     poke = document.getElementById("poke_" + index);
    115.     if (poke) {
    116.       poke.value = fmtUnsignedLong(parseInt(poke.value));
    117.     }
    118.   }
    119. }

    120. // Convert unsigned long to dec/hex string
    121. function fmtUnsignedLong(value) {
    122.   var format = document.getElementById("format");
    123.   var hexStr;
    124.   if (format.value == 0) {
    125.     hexStr = value.toString(16).toUpperCase();
    126.     hexStr = "0x" + "00000000".substr(0, 8 - hexStr.length) + hexStr;
    127.   } else {
    128.     hexStr = value.toString(10);
    129.   }
    130.   return hexStr;
    131. }

    132. // Copy peek to poke
    133. function copy(reg) {
    134.   var peek = document.getElementById("peek_" + reg);
    135.   if (peek) {
    136.     var poke = document.getElementById("poke_" + reg);
    137.     if (poke) {
    138.       poke.value = peek.value;
    139.     }
    140.   }
    141. }

    142. // Copy all peek to poke
    143. function copy_all() {
    144.   var table = document.getElementById("registers");
    145.   var rows = table.rows.length - 1;
    146.   for (var index = 0; index < rows; index++) {
    147.     copy(index);
    148.   }
    149. }

    150. // Peek address & display result
    151. function peek(reg) {
    152.   var url = "/cgi-bin/peek?" + document.getElementById("addr_" + reg).value;
    153.   if (window.XMLHttpRequest) {
    154.     var ajaxReq = new XMLHttpRequest();
    155.     ajaxReq.onreadystatechange = function() {
    156.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    157.         var respText = ajaxReq.responseText;
    158.         var img_obj = document.getElementById("speek_" + reg);
    159.         // Unique number is added to image to avoid caching issues on separate animations
    160.         const now = Date.now();
    161.         if (respText.substr(0,6) == "Error:") {
    162.           img_obj.src = "../red.gif?" + now;
    163.           img_obj.title = "Last peek failed : " + respText.substr(7);
    164.         } else {
    165.           img_obj.src = "../green.gif?" + now;
    166.           img_obj.title = "Last peek successful";
    167.           document.getElementById("peek_" + reg).value = fmtUnsignedLong(parseInt(respText));
    168.         }
    169.       }
    170.     }
    171.     ajaxReq.open("POST", url, true);
    172.     ajaxReq.send(null);
    173.   }
    174. }

    175. // Peek all selected addresses in section
    176. function peek_section(row) {
    177.   var obj_sel;
    178.   do {
    179.     row++;
    180.     obj_sel = document.getElementById("peek_sel_" + row);
    181.     if (obj_sel) {
    182.       if (obj_sel.checked) {
    183.         peek(row);
    184.       }
    185.     }
    186.   } while (obj_sel);
    187. }

    188. // Peek all selected addresses in table
    189. function peek_all() {
    190.   var table = document.getElementById("registers");
    191.   var rows = table.rows.length - 1;
    192.   for (var index = 0; index < rows; index++) {
    193.     const obj_sel = document.getElementById("peek_sel_" + index);
    194.     if (obj_sel) {
    195.       if (obj_sel.checked) {
    196.         peek(index);
    197.       }
    198.     }
    199.   }
    200. }

    201. // Poke address & display result
    202. function poke(reg) {
    203.   var url = "/cgi-bin/poke?" + document.getElementById("addr_" + reg).value + "&" + document.getElementById("poke_" + reg).value;
    204.   if (window.XMLHttpRequest) {
    205.     var ajaxReq = new XMLHttpRequest();
    206.     ajaxReq.onreadystatechange = function() {
    207.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    208.         var respText = ajaxReq.responseText;
    209.         var img_obj = document.getElementById("spoke_" + reg);
    210.         // Unique number is added to image to avoid caching issues on separate animations
    211.         const now = Date.now();
    212.         if (respText.substr(0,6) == "Error:") {
    213.           img_obj.src = "../red.gif?" + now;
    214.           img_obj.title = "Last poke failed : " + respText.substr(7);
    215.         } else {
    216.           img_obj.src = "../green.gif?" + now;
    217.           img_obj.title = "Last poke successful";
    218.         }
    219.       }
    220.     }
    221.     ajaxReq.open("POST", url, true);
    222.     ajaxReq.send(null);
    223.   }
    224. }

    225. // Poke all selected addresses in section
    226. function poke_section(row) {
    227.   var obj_sel;
    228.   do {
    229.     row++;
    230.     obj_sel = document.getElementById("poke_sel_" + row);
    231.     if (obj_sel) {
    232.       if (obj_sel.checked) {
    233.         poke(row);
    234.       }
    235.     }
    236.   } while (obj_sel);
    237. }

    238. // Poke all selected addresses in table
    239. function poke_all() {
    240.   var table = document.getElementById("registers");
    241.   var rows = table.rows.length - 1;
    242.   for (var index = 0; index < rows; index++) {
    243.     const obj_sel = document.getElementById("poke_sel_" + index);
    244.     if (obj_sel) {
    245.       if (obj_sel.checked) {
    246.         poke(index);
    247.       }
    248.     }
    249.   }
    250. }

    251. // Add row to table
    252. function add_row() {
    253.   const obj_type = document.getElementById("type");
    254.   const row_type = obj_type.value;
    255.   switch(row_type) {
    256.     case "0":
    257.       add_register();
    258.       break;
    259.     case "1":
    260.       add_section();
    261.       break;
    262.     default:
    263.       break;
    264.   }
    265. }

    266. // Add row to table
    267. function add_register(reg) {
    268.   var table = document.getElementById("registers");
    269.   var next = table.rows.length - 1;
    270.   var row = table.insertRow(-1);
    271.   var newcell;
    272.   var addr = 0x41200000;
    273.   if (next > 0) {
    274.     const obj_addr = document.getElementById("addr_" + (next - 1));
    275.     if (obj_addr) {
    276.       addr = parseInt(document.getElementById("addr_" + (next - 1)).value) + 4;
    277.     }
    278.   }
    279.   newcell = row.insertCell(0);
    280.   newcell.innerHTML = '<input title="Address to peek/poke" type="text" id="addr_' + next + '" value="' + fmtUnsignedLong(addr) + '" size="10">';
    281.   newcell = row.insertCell(1);
    282.   newcell.innerHTML = '<input title="Value peeked at address" type="text" id="peek_' + next + '" value="0x00000000" size="10" readonly="readonly"></td>';
    283.   newcell = row.insertCell(2);
    284.   newcell.innerHTML = '<input title="Select address for peeking" type="checkbox" id="peek_sel_' + next + '" checked></td>';
    285.   newcell = row.insertCell(3);
    286.   newcell.innerHTML = '<input title="Peek address" type="submit" value="Peek" onclick="peek(' + next + ')">';
    287.   newcell = row.insertCell(4);
    288.   newcell.innerHTML = '<img title="Peek status" id="speek_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
    289.   newcell = row.insertCell(5);
    290.   newcell.innerHTML = '<input title="Copy peek value into poke value" type="submit" value=">>" onclick="copy(' + next + ')">';
    291.   newcell = row.insertCell(6);
    292.   newcell.innerHTML = '<input title="Value to poke at address" type="text" id="poke_' + next + '" value="0x00000000" size="10">';
    293.   newcell = row.insertCell(7);
    294.   newcell.innerHTML = '<input title="Select address for poking" type="checkbox" id="poke_sel_' + next + '" checked></td>';
    295.   newcell = row.insertCell(8);
    296.   newcell.innerHTML = '<input title="Poke address" type="submit" value="Poke" onclick="poke(' + next + ')">';
    297.   newcell = row.insertCell(9);
    298.   newcell.innerHTML = '<img title="Poke status" id="spoke_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
    299.   newcell = row.insertCell(10);
    300.   newcell.innerHTML = '<input title="Description of address" type="text" id="name_' + next + '" value="Register @ ' + fmtUnsignedLong(addr) + '" size="40">';
    301. }

    302. // Add group row to table
    303. function add_section(reg) {
    304.   var table = document.getElementById("registers");
    305.   var next = table.rows.length - 1;
    306.   var row = table.insertRow(-1);
    307.   var newcell;
    308.   newcell = row.insertCell(0);
    309.   newcell.colSpan = "3";
    310.   newcell.innerHTML = "--- Section ---";
    311.   newcell = row.insertCell(1);
    312.   newcell.innerHTML = '<input title="Peek all selected addresses in section" type="submit" value="Peek" onclick="peek_section(' + next + ')">';
    313.   newcell = row.insertCell(2);
    314.   newcell.colSpan = "4";
    315.   newcell.innerHTML = "--- Section ---";
    316.   newcell = row.insertCell(3);
    317.   newcell.innerHTML = '<input title="Poke all selected addresses in section" type="submit" value="Poke" onclick="poke_section(' + next + ')">';
    318.   newcell = row.insertCell(4);
    319.   newcell = row.insertCell(5);
    320.   newcell.colSpan = "2";
    321.   newcell.innerHTML = '<input title="Description of section" type="text" id="name_' + next + '" value="Section Description" size="40">';
    322. }

    323. // Remove row from table
    324. function rem_register(reg) {
    325.   var table = document.getElementById("registers");
    326.   if (table.rows.length > 1) {
    327.     table.deleteRow(-1);
    328.   }
    329. }

    330. // Remove all rows from table
    331. function remove_all() {
    332.   var table = document.getElementById("registers");
    333.   var rows = table.rows.length - 1;
    334.   for (var index = 0; index < rows; index++) {
    335.     table.deleteRow(-1);
    336.   }
    337. }

    338. // Note: browser file access is made difficult for security reasons - there maybe a better way of doing file read & write.

    339. var config_file = null;

    340. // Create virtual configuration file from address table for user to download
    341. function create_config() {
    342.   var text = "";
    343.   var table = document.getElementById("registers");
    344.   const rows = table.rows.length - 1;
    345.   for (var index = 0; index < rows; index++) {
    346.     const obj_addr = document.getElementById("addr_" + index);
    347.     if (obj_addr) { // Register type
    348.       var addr = document.getElementById("addr_" + index).value;
    349.       var peek_sel = document.getElementById("peek_sel_" + index).checked;
    350.       var poke = document.getElementById("poke_" + index).value;
    351.       var poke_sel = document.getElementById("poke_sel_" + index).checked;
    352.       var name = document.getElementById("name_" + index).value;
    353.       text += "reg" + "|" + addr + "|" + peek_sel + "|" + poke + "|" + poke_sel + "|"+ name + "\n";
    354.     } else { // Section type
    355.       var name = document.getElementById("name_" + index).value;
    356.       text += "sec" + "|" + name + "\n";
    357.     }
    358.   }
    359.   const data = new Blob([text], {type: 'text/plain'});
    360.   if (config_file !== null) {
    361.     URL.revokeObjectURL(config_file);
    362.   }
    363.   config_file = URL.createObjectURL(data);
    364.   var link = document.getElementById('download');
    365.   link.href = config_file;
    366.   link.style.display = 'inline';
    367. }

    368. // Read configuration file and update address table
    369. function load_config(input) {
    370.   var file = input.target.files[0];
    371.   if (file) {
    372.     var reader = new FileReader();
    373.     reader.onload = function(input) {
    374.       var contents = input.target.result;
    375.       const lines = contents.split(/\r\n|\n/);
    376.       remove_all();
    377.       lines.forEach((line) => {
    378.         if (line.length > 0) {
    379.           var table = document.getElementById("registers");
    380.           var next = table.rows.length - 1;
    381.           const values = line.split("|");
    382.           switch(values[0]) {
    383.             case "reg":
    384.               add_register();
    385.               document.getElementById("addr_" + next).value = values[1];
    386.               if (values[2] == "false") {
    387.                 document.getElementById("peek_sel_" + next).checked = false;
    388.               }
    389.               document.getElementById("poke_" + next).value = values[3];
    390.               if (values[4] == "false") {
    391.                 document.getElementById("poke_sel_" + next).checked = false;
    392.               }
    393.               document.getElementById("name_" + next).value = values[5];
    394.               break;
    395.             case "sec":
    396.               add_section();
    397.               document.getElementById("name_" + next).value = values[1];
    398.               break;
    399.             default:
    400.               alert("Error: Unrecognized table type found (" + values[0] + "), ignoring.");
    401.           }
    402.         }
    403.       });
    404.     };
    405.     reader.readAsText(file);
    406.   }
    407. }

    408. document.getElementById('load_config').addEventListener('change', load_config, false);

    15. Update hardware platform

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

    Select Save to save the configuration. Missing Image! Select Ok to confirm the save. Missing Image! Select Exit to continue. Missing Image! Select Exit to exit the menu. Missing Image!

    16. Commit new & updated files

    Commit the updated PetaLinux files, create an annotated tag and push the commit & tag up to the remote repository.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git commit -am "Updated webpage to include PetaLinux ID table & add hash field to Firmware ID table."
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git tag -a v10.0 -m "PetaLinux, Peek/Poke, LED Runner, Webserver, Peek/Poke CGI, PL Access, Style Sheet, Register Bank & ID Strings (PetaLinux & Firmware hash) with XSA from zedboard_leds_switches v4.0"
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git push

    17. Build & package PetaLinux

    Rebuild PetaLinux to include the updates.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ source ~/.bashrc
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-build
    Note that the petalinux-build command now diverts off to petalinux-build-id.sh so the following message should be displayed atfer calling petalinux-build.
    NOTE: Building PetaLinux with the following identification
    Description ... Zedboard PetaLinux Example Design
    Company ....... SpaceWire UK
    Author ........ Steve Haywood
    Version ....... 10.0 (unstaged)
    Timestamp ..... 23-Oct-2022 - 10:04:44
    Hash .......... 980d0a89f629f246e18f79df9f69bb7d35c0a185
    Hold the phone! Stop the build process with Ctrl-C. Somehow the build is using unstaged files. Forget to add the new project.txt file!

    Add the missing file and perform the necessary GIT finger work.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git add project-spec/meta-user/recipes-apps/website/files/project.txt
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git commit -am "Updated webpage to include PetaLinux ID table & add hash field to Firmware ID table."
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git tag -d v10.0
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git tag -a v10.0 -m "PetaLinux, Peek/Poke, LED Runner, Webserver, Peek/Poke CGI, PL Access, Style Sheet, Register Bank & ID Strings (PetaLinux & Firmware hash) with XSA from zedboard_leds_switches v4.0"
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git push
    Attempt to rebuild PetaLinux again.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-build
    This time the build should be deemed as using fully staged and pushed files with no unsavoury comments after the version number.
    NOTE: Building PetaLinux with the following identification
    Description ... Zedboard PetaLinux Example Design
    Company ....... SpaceWire UK
    Author ........ Steve Haywood
    Version ....... 10.0
    Timestamp ..... 23-Oct-2022 - 10:08:02
    Hash .......... 9bea7fd2705efc6da3b975e0e05b28ec57872afd
    Package up PetaLinux ready for deployment.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-package --prebuilt --force
    #### Part 4 - Hardware Deployment ####

    18. Setup Zedboard hardware

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

    19. Launch MiniCom terminal emulator

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

    Welcome to minicom 2.7.1

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

    Press CTRL-A Z for help on special keys

    20. Deploy firmware & software on Zedboard

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

    21. Check everything is working as expected

    All being well the following sequence of events should be observed, pay particular attention to point 2.
    1. The blue done LED illuminates indicating the Programmable Logic (PL) has been programmed.
    2. LED 7 and 1 illuminate indicating the bitstream from the updated zedboard_leds_switches project is in use.
    3. The software runs on the Processor System (PS).
    4. PetaLinux starts to boot.
    5. The led-runner application launches and executes the expanding & contracting LED illumination sequence.
    6. The PetaLinux login prompt appears in the terminal emulator.
    Access the webserver running on the Zedboard using a browser pointing at the Zedboard's IP address (192.168.2.87). Click the Read ID buttons in both the Operating System Information & Firmware Information sectons to read the Identification information from the OS filesystem & PL address space. All being well the following should be displayed in the Operating System Information & Firmware Information sections.



    Seems silly not to use the GIT tag for the Version since they are in lockstep, but as seen previously, a GIT tag is not immutable unlike a GIT timestamp & GIT hash.

    By including the hashes and marking any unclean builds with either unstaged or unpushed means there is always a direct link back to the original files that created the builds.