Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 14 - Add textural information inside the PL to provide project & build information (10 December 2021)

Introduction

This tutorial details the steps required to add a read-only register bank inside the PL that will contain product identification, version & timestamp information. The tutorial covers quite a range of activities and includes the use of numerous languages; C, HTML, Javascript, System Verilog, TCL & Verilog.

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. Create AXI4-Lite peripheral (HDL)
    3. Open block design
    4. Re-customize AXI GPIO
    5. Add peripheral to block design
    6. Edit address map
    7. Create, move & modify HDL wrapper
    8. Create build hook scripts
    9. Generate bitstream
    10. Export hardware platform

    Part 4 - OS Development

    1. Create peek string C application
    2. Update website to display ID strings
    3. Update Javascript to peek ID strings
    4. Update hardware platform
    5. Build & package PetaLinux

    Part 5 - Hardware Deployment

    1. Setup Zedboard hardware
    2. Launch MiniCom terminal emulator
    3. Deploy PetaLinux on Zedboard
    4. Check everything is working as expected

    Part 6 - Revision Control

    1. Commit new & updated files

    Part 6 - Quickstart

    1. Obtain tutorial files from Bitbucket, create & build projects, deploy on Zedboard
    #### 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. Create AXI4-Lite peripheral (HDL)

    Add a new HDL source file to Vivado by right clicking on Design Sources within Sources and selecting Add Sources... from the context menu. Missing Image! In the Add Sources dialog select Add or create design sources and then click Next. Missing Image! In the Add Sources - Add or Create Design Sources dialog click on Create File. Missing Image! In the Create Source File dialog set File type to Verilog, File name to axi_identification and File location to ~/projects/zedboard_leds_switches/fw/src/design. Click OK to continue. Missing Image! In the Add Sources - Add or Create Design Sources dialog click Finish. Missing Image! In the Define Module dialog click OK to continue. Missing Image! Edit the newly added axi_identication by double clicking on it within Sources. Missing Image! Using the AXI4 peripheral generated in the previous tutorial as a starting point a new AXI4 peripheral can be created containing registers that hold textural information. No write access to the registers is required but the write logic is left in to make the AXI interface complete (most of it will be optimised away). Modify axi_identication.v and replace its stub code with something similar to the following.

    axi_identification.v

    1. //
    2. // File .......... axi_identification.v
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 5 December 2021
    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. // Product Description ........ 128 Characters - 32 Registers - 0x000 - 0x07F
    13. // Company ....................  64 Characters - 16 Registers - 0x080 - 0x0BF
    14. // Author .....................  64 Characters - 16 Registers - 0x0C0 - 0x0FF
    15. // Product Version ............  16 Characters -  4 Registers - 0x100 - 0x10F
    16. // Firmware Build Timestamp ...  32 Characters -  8 Registers - 0x110 - 0x12F
    17. // Unused ..................... 208 Characters - 52 Registers - 0x130 - 0x1FF
    18. //                              ---              ---            -------------
    19. //                              512              128          - 0x000 - 0x1FF
    20. //


    21. `timescale 1 ns / 1 ps


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


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


    81.   // Signals
    82.   reg aw_en;
    83.   reg [c_addr_w-$clog2(c_data_w / 8) - 1 : 0] axi_awaddr;
    84.   reg [c_addr_w-$clog2(c_data_w / 8) - 1 : 0] axi_araddr;
    85.   reg [c_data_w-1 : 0] registers_wr [no_regs - 1 : 0];
    86.   wire [c_data_w-1 : 0] registers_rd [no_regs - 1 : 0];


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


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


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


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


    124.   // Write Response
    125.   always @(posedge aclk)
    126.     if (!aresetn) begin
    127.       s_axi_bvalid <= 1'b0;
    128.       s_axi_bresp  <= 2'b00;
    129.     end else if (s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready && !s_axi_bvalid) begin
    130.       s_axi_bvalid <= 1'b1;
    131.       s_axi_bresp  <= 2'b00;
    132.     end else if (s_axi_bvalid && s_axi_bready)
    133.       s_axi_bvalid <= 1'b0;


    134.   // Read Address Ready
    135.   always @(posedge aclk)
    136.     if (!aresetn)
    137.       s_axi_arready <= 1'b0;
    138.     else if (s_axi_arvalid && !s_axi_arready)
    139.       s_axi_arready <= 1'b1;
    140.     else
    141.       s_axi_arready <= 1'b0;


    142.   // Read Address Latch
    143.   always @(posedge aclk)
    144.     if (!aresetn)
    145.       axi_araddr  <= {c_addr_w{1'b0}};
    146.     else if (s_axi_arvalid && !s_axi_arready)
    147.       axi_araddr <= s_axi_araddr[c_addr_w - 1 : $clog2(c_data_w/8)];


    148.   // Read Response
    149.   always @(posedge aclk)
    150.     if (!aresetn) begin
    151.       s_axi_rvalid <= 1'b0;
    152.       s_axi_rresp  <= 2'b00;
    153.     end else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid) begin
    154.       s_axi_rvalid <= 1'b1;
    155.       s_axi_rresp  <= 2'b00;
    156.     end else if (s_axi_rvalid && s_axi_rready)
    157.       s_axi_rvalid <= 1'b0;


    158.   // Read Data
    159.   always @(posedge aclk)
    160.     if (!aresetn)
    161.       s_axi_rdata <= {c_data_w{1'b0}};
    162.     else if (s_axi_arvalid && s_axi_arready && !s_axi_rvalid)
    163.       s_axi_rdata <= registers_rd[axi_araddr];


    164.   // Read Register Constants
    165.   genvar j;
    166.   generate
    167.     for (j = 0; j <= no_regs - 1; j = j + 1) begin
    168.       if      (j <= id_description_hi) assign registers_rd[j] = id_description[32*(j-id_description_lo) +: 32];
    169.       else if (j <= id_company_hi    ) assign registers_rd[j] = id_company    [32*(j-id_company_lo    ) +: 32];
    170.       else if (j <= id_author_hi     ) assign registers_rd[j] = id_author     [32*(j-id_author_lo     ) +: 32];
    171.       else if (j <= id_version_hi    ) assign registers_rd[j] = id_version    [32*(j-id_version_lo    ) +: 32];
    172.       else if (j <= id_timestamp_hi  ) assign registers_rd[j] = id_timestamp  [32*(j-id_timestamp_lo  ) +: 32];
    173.       else                             assign registers_rd[j] = 32'h00000000;
    174.     end
    175.   endgenerate


    176. endmodule

    5. Open block design

    Open the existing block design by clicking on Open Block Design under IP INTEGRATOR. Get a better view of the Block Design by clicking on its Float Missing Image! icon. Missing Image!

    6. Re-customize AXI GPIO

    Double click on the AXI GPIO module to begin re-customization. Change the Default Output Value of GPIO to 0x00000082 so the updated PL design is easily identifiable from the previous one. Click OK to commit the changes. Missing Image!

    7. Add peripheral to block design

    Add the newly created peripheral to the Block Design by dragging it from Sources and dropping it in Diagram. Note the reason Verilog is being used for this and not System Verilog is because this operation is currently not supported for System Verilog. Missing Image! Connect the Register Bank to the rest of the system by clicking on Run Connection Automation.

    In the Run Connection Automation dialog tick All Automation and click OK to proceed. Missing Image! Make the ID signals external to the Block Design by highlighting them and selecting Make External from the right mouse context menu. Note the reason signals are being used and not parameters is because passing parameters into a Block Design is currently not supported. Missing Image! Rename the external signals to remove the _0 from their names. Missing Image! Verify the block design is error free by clicking on the Validate Design Missing Image! icon. All being well the Validation successful dialog should now appear.

    8. Edit address map

    Check the address map assignment inside the Address Editor under the BLOCK DESIGN section. The axi_identification_0 module should have a Master Base Address of 0x43C1_0000 and a range of 64K. Change the Master Base Address to 0x4000_0000and leave the Range set to 64K. The product identification will now reside at the very start of the PL address space. Missing Image! Save the Block Design.

    9. Create, move & modify HDL wrapper

    The HDL wrapper must now be hand edited so the ID information can be provided using parameters, this means the wrapper can no longer be managed automatically by Vivado.

    Create an updated wrapper to include the new ID signals by right clicking on the system_i block design inside Sources and selecting Create HDL Wrapper... from the context menu.

    In the Create HDL Wrapper dialog select Copy generated wrapper to allow user edits and then click OK. Missing Image! Copy the generated wrapper into the user area outside of Vivado and rename it such that it becomes a System Verilog source.
    steve@Desktop:~/projects/zedboard_leds_switches$ cp fw/vivado/project.srcs/sources_1/imports/hdl/system_wrapper.v fw/src/diagram/system/hdl/system_wrapper.sv
    Remove the generated wrapper from Vivado by right clicking on system_wrapper inside Sources and selecting Remove File from Project... from the context menu.

    In the Remove Sources dialog select Also delete the project local file/directory from disk and then click OK to continue. Missing Image! Add system_wrapper.sv back into the project from its new location /home/steve/projets/zedboard_leds_switches/fw/src/diagram/system/hdl.

    Edit the wrapper file as follows:- The resulting source should look similar to the following...

    system_wrapper.sv

    1. //
    2. // File .......... system_wrapper.sv
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 6 December 2021
    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:Firmware 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 Number ......... Max  16 Characters
    19.   parameter string id_timestamp   = ""   // P:Build Timestamp ........ Max  32 Characters
    20. )
    21. (
    22.   // LEDs
    23.   output [ 7:0] leds_tri_o,         // O:LEDs
    24.   // Switches
    25.   input  [ 7:0] switches_tri_i,     // I:Switches
    26.   // System
    27.   inout  [14:0] DDR_addr,           // B:Address
    28.   inout  [ 2:0] DDR_ba,             // B:Bank Address
    29.   inout         DDR_cas_n,          // B:Column Address Select
    30.   inout         DDR_ck_n,           // B:Clock (Neg)
    31.   inout         DDR_ck_p,           // B:Clock (Pos)
    32.   inout         DDR_cke,            // B:Clock Enable
    33.   inout         DDR_cs_n,           // B:Chip Select
    34.   inout  [ 3:0] DDR_dm,             // B:Data Mask
    35.   inout  [31:0] DDR_dq,             // B:Data Input/Output
    36.   inout  [ 3:0] DDR_dqs_n,          // B:Data Strobe (Neg)
    37.   inout  [ 3:0] DDR_dqs_p,          // B:Data Strobe (Pos)
    38.   inout         DDR_odt,            // B:Output Dynamic Termination
    39.   inout         DDR_ras_n,          // B:Row Address Select
    40.   inout         DDR_reset_n,        // B:Reset
    41.   inout         DDR_we_n,           // B:Write Enable
    42.   inout         FIXED_IO_ddr_vrn,   // B:Termination Voltage
    43.   inout         FIXED_IO_ddr_vrp,   // B:Termination Voltage
    44.   inout  [53:0] FIXED_IO_mio,       // B:Peripheral Input/Output
    45.   inout         FIXED_IO_ps_clk,    // B:System Reference Clock
    46.   inout         FIXED_IO_ps_porb,   // B:Power On Reset
    47.   inout         FIXED_IO_ps_srstb   // B:External System Reset
    48. );


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


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


    94. endmodule

    10. Create build hook scripts

    To pass textural information into the parameters of system_wrapper.sv a pre-synthesis TCL script can be used. Versioning and timestamping can also be achieved using this script. Care must be taken when a build fails and a bitstream isn't generated not to leave a version/revision missing from the build history. A post-bitstream TCL script can be used to assist with the missing version/revision problem. Note there are very many ways to achieve versioning with fallback, most of which are dependant on how revision control is used with the build process and committing of source & bitstream. The approach used here shall be a product identification file containing information pertinent to a successfully created bitstream. After a successful build the product identification file along with the new bitstream could be committed into revision control. If source is committed into revision control prior to a build being undertaken a tag from the commit could be included in the product identification file & bitstream to link the build back to the version of the source used to create it.

    Create an initial product identification file and edit it to include the product information fields. Note the version will be incremented during the build process so this is set to 0.0 initially ready for it becoming 1.0. The timestamp will be overwritten so this can be set to anything.
    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. 0.0
    5. 00-Xxx-0000 - 00:00:00

    Create pre-synthesis & post-bitstream TCL scripts and edit to include the required code.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/script/{pre_synth.tcl,post_bit.tcl}

    pre_synth.tcl

    1. #
    2. # File .......... pre_synth.tcl
    3. # Author ........ Steve Haywood
    4. # Version ....... 1.0
    5. # Date .......... 24 October 2021
    6. # Description ...
    7. #   Script to read product/project information from a text file, increase
    8. # the version/revision, get current timestamp and set the generics/parameters
    9. # on the top level module.
    10. #

    11. # Read Product/Project Information
    12. set id_file [open "../../../project.txt" r]
    13. gets $id_file id_description
    14. gets $id_file id_company
    15. gets $id_file id_author
    16. gets $id_file id_version
    17. gets $id_file id_timestamp
    18. close $id_file

    19. # Increase Version
    20. set id_version [expr $id_version+1]

    21. # Increase Revision
    22. #set id_version [expr $id_version+0.1]

    23. # Get Timestamp
    24. set id_timestamp "[clock format [clock seconds] -format {%d-%b-%Y - %H:%M:%S}]"

    25. # Write New Product/Project Information
    26. set id_file [open "../../../project_new.txt" w]
    27. puts $id_file $id_description
    28. puts $id_file $id_company
    29. puts $id_file $id_author
    30. puts $id_file $id_version
    31. puts $id_file $id_timestamp
    32. close $id_file

    33. # Replace space character with its octal code
    34. proc space_replace {str} {
    35.   return [string map {" " "\\040"} $str]
    36. }

    37. # Replace spaces in information strings
    38. set id_description [space_replace $id_description]
    39. set id_company     [space_replace $id_company]
    40. set id_author      [space_replace $id_author]
    41. set id_version     [space_replace $id_version]
    42. set id_timestamp   [space_replace $id_timestamp]

    43. # Set Generics/Parameters
    44. set_property generic " \
    45.   id_description=\"$id_description\" \
    46.   id_company=\"$id_company\" \
    47.   id_author=\"$id_author\" \
    48.   id_version=\"$id_version\" \
    49.   id_timestamp=\"$id_timestamp\" \
    50. " [current_fileset]

    post_bit.tcl

    1. #
    2. # File .......... post_bit.tcl
    3. # Author ........ Steve Haywood
    4. # Version ....... 1.0
    5. # Date .......... 24 October 2021
    6. # Description ...
    7. #   Script to replace 'project.txt' (Information about last version of firmware)
    8. # with 'project_new.txt' (Information about new version of firmware). The file
    9. # replace only occurs if the bitstream was generated successfully.
    10. #

    11. file delete ../../../project.txt
    12. file rename ../../../project_new.txt ../../../project.txt

    Add the pre-synthesis & post-bitstream hooks into Vivado's build process by navigating Tools » Settings....

    In the Settings dialog select Synthesis under Project Settings and click the ... button across from tcl.pre to select the pre-synthesis file. Missing Image! In the Select A Tcl Script dialog select New script and set this to /home/steve/projets/zedboard_leds_switches/fw/src/script/pre_synth.tcl. Click OK to continue. Missing Image! In the Settings dialog select Bitstream under Project Settings and click the ... button across from tcl.post to select the post-bitstream file. Missing Image! In the Select A Tcl Script dialog select New script and set this to /home/steve/projets/zedboard_leds_switches/fw/src/script/post_bit.tcl. Click OK to continue. Missing Image!

    11. Generate bitstream

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

    12. Export hardware platform

    Export the hardware platform by selecting File » Export » Export Hardware... from the main menu. Missing Image! In the Export Hardware Platform dialog click Next. Missing Image! In the Output dialog check Include bitstream and click Next. Missing Image! In the Files dialog set the Export to field to ~/projects/zedboard_leds_switches/fw (by removing the last /vivado part of the path). Click Next. Missing Image! If the Module Already Exported dialog appears click Yes. Missing Image! In the Exporting Hardware Platform dialog click Fniish. Missing Image!
    #### Part 4 - OS Development ####

    13. Create peek string C application

    Using peek.c as a starting point create a new C application to perform the reading of a string from the PL address space.
    steve@Desktop:~/projects/zedboard_leds_switches$ cd ../zedboard_linux/os/petalinux
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/peekpokecgi/files/peekstring.c
    Something similar to the following should do the trick.

    peekstring.c

    1. //
    2. // File .......... peekstring.c
    3. // Author ........ Steve Haywood
    4. // Version ....... 1.0
    5. // Date .......... 29 October 2021
    6. // Description ...
    7. //   CGI type module to read a string from memory and return it.
    8. // Usage ...
    9. //   http://<web address>/cgi-bin/peekstring?base&size&offset&maxchars
    10. // base = Base address of memory region to be used
    11. // size = Size of memory region to be used
    12. // offset = Offset address of string within memory region
    13. // maxchars = Maximum length of return string
    14. //


    15. #include <stdio.h>
    16. #include <string.h>
    17. #include <stdlib.h>
    18. #include <fcntl.h>
    19. #include <sys/mman.h>
    20. #include <stdint.h>
    21. #include <unistd.h>


    22. int main()
    23. {
    24.   int fd;              // File descriptor for mapped memory
    25.   char *map;           // Pointer to mapped memory
    26.   uint32_t base;       // Start address of area to map
    27.   uint32_t size;       // Size of area to map
    28.   uint32_t addr;       // Address within area to access
    29.   uint32_t length;     // Maximum length of string to return
    30.   char *ptr;           // Pointer to character of string
    31.   char *querystring;   // Pointer to QUERY_STRING
    32.   char *rest;          // Pointer to next token within QUERY_STRING
    33.   char *token;         // Pointer to extracted token
    34.   char *endptr = NULL; // Pointer to end of string in str2int conversion
    35.   uint32_t count = 0;  // Number of character output from string

    36.   printf("Content-Type: text/plain;charset=us-ascii\n\n");

    37.   querystring = getenv("QUERY_STRING");
    38.   if (querystring)
    39.   {
    40.     rest = querystring;
    41.     token = strtok_r(rest, "&", &rest);
    42.     if (token != NULL) {
    43.       base = strtoul(token, &endptr, 0);
    44.       if (token != endptr) {
    45.         token = strtok_r(rest, "&", &rest);
    46.         if (token != NULL) {
    47.           size = strtoul(token, &endptr, 0);
    48.           if (token != endptr) {
    49.             token = strtok_r(rest, "&", &rest);
    50.             if (token != NULL) {
    51.               addr = strtoul(token, &endptr, 0);
    52.               if (token != endptr) {
    53.                 token = strtok_r(rest, "&", &rest);
    54.                 if (token != NULL) {
    55.                   length = strtoul(token, &endptr, 0);
    56.                   if (token != endptr) {
    57.                     fd = open( "/dev/mem", O_RDWR);
    58.                     if (fd > 0) {
    59.                       map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, base);
    60.                       if (map != MAP_FAILED) {
    61.                         ptr = (char *)(map + addr);
    62.                         while (*ptr != '\0' && count < length) {
    63.                           printf("%c", *ptr);
    64.                           ptr++;
    65.                           count++;
    66.                         }
    67.                         munmap(map, size);
    68.                       } else printf("Error: Memory to mmap");
    69.                       close(fd);
    70.                     } else printf("Error: Failed to open /dev/mem");
    71.                   } else printf("Error: Invalid string length");
    72.                 } else printf("Error: Missing string length");
    73.               } else printf("Error: Invalid string offset address");
    74.             } else printf("Error: Missing string offset address");
    75.           } else printf("Error: Invalid memory map size");
    76.         } else printf("Error: Missing memory map size");
    77.       } else printf("Error: Invalid memory map base address");
    78.     } else printf("Error: Missing memory map base address");
    79.   } else printf("Error: No QUERY_STRING");
    80. }

    Modify the Makefile to include the peekstring application.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/peekpokecgi/files/Makefile

    Makefile

    1. PEEK = peek
    2. POKE = poke
    3. PEEKSTRING = peekstring

    4. # Add any other object files to this list below
    5. PEEK_OBJS = peek.o
    6. POKE_OBJS = poke.o
    7. PEEKSTRING_OBJS = peekstring.o

    8. all: $(PEEK) $(POKE) $(PEEKSTRING)

    9. $(PEEKSTRING): $(PEEKSTRING_OBJS)
    10.   $(CC) $(LDFLAGS) -o $@ $(PEEKSTRING_OBJS) $(LDLIBS)

    11. $(POKE): $(POKE_OBJS)
    12.   $(CC) $(LDFLAGS) -o $@ $(POKE_OBJS) $(LDLIBS)

    13. $(PEEK): $(PEEK_OBJS)
    14.   $(CC) $(LDFLAGS) -o $@ $(PEEK_OBJS) $(LDLIBS)

    15. clean:
    16.   -rm -f $(PEEKSTRING) $(POKE) $(PEEK) *.elf *.gdb *.o

    Modify the BitBake recipe to include the peekstring application.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/peekpokecgi/peekpokecgi.bb

    peekpokecgi.bb

    1. #
    2. # This is the peekpokecgi aplication recipe
    3. #
    4. #

    5. SUMMARY = "peekpokecgi application"
    6. SECTION = "PETALINUX/apps"
    7. LICENSE = "MIT"
    8. LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
    9. SRC_URI = "file://peek.c \
    10.            file://poke.c \
    11.            file://peekstring.c \
    12.            file://Makefile \
    13.           "
    14. FILES_${PN} += "/srv/www/cgi-bin"
    15. S = "${WORKDIR}"
    16. CFLAGS_prepend = "-I ${S}/include"
    17. do_compile() {
    18.         oe_runmake
    19. }
    20. do_install() {
    21.         install -d ${D}/srv/www/cgi-bin
    22.         install -m 0755 ${S}/peek ${D}/srv/www/cgi-bin
    23.         install -m 0755 ${S}/poke ${D}/srv/www/cgi-bin
    24.         install -m 0755 ${S}/peekstring ${D}/srv/www/cgi-bin
    25. }


    14. Update website to display ID strings

    Edit the existing HTML to have a Product Identification section, this will include a table of information and an update button that calls a Javascript function to perform the webpage update. Lines 33-62 illustrate the potential code required.
    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">Firmware Product Information (Read from Firmware) <input type="submit" value="Read ID" id="read_ids" onclick="read_ids()"></th>
    32. </tr>
    33. </thead>
    34. <tbody>
    35. <tr>
    36. <td style="text-align:right">Description :</td>
    37. <td style="text-align:left" id="id_0">Unknown</td>
    38. <td><img id="sid_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="id_1">Unknown</td>
    43. <td><img id="sid_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="id_2">Unknown</td>
    48. <td><img id="sid_2" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    49. </tr>
    50. <tr>
    51. <td style="text-align:right">Build Version :</td>
    52. <td style="text-align:left" id="id_3">Unknown</td>
    53. <td><img id="sid_3" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    54. </tr>
    55. <tr>
    56. <td style="text-align:right">Build Timestamp :</td>
    57. <td style="text-align:left" id="id_4">Unknown</td>
    58. <td><img id="sid_4" style="vertical-align:middle" src="../amber.gif" title="Unknown!" alt="Missing Image!"></td>
    59. </tr>
    60. </tbody>
    61. </table>
    62. </div>

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

    64. <div class="section">

    65. <table>
    66. <tr><th colspan="2">System</th></tr>
    67. <tr><td>Hostname</td>
    68. <td>${sys_host}</td>
    69. </tr><tr><td>Time</td><td>${sys_time}</td></tr>
    70. <tr><td>Uptime</td><td><span id="uptime_text">${sys_up}</span> seconds <button onclick="get_uptime()">Refresh</button> Auto :
    71. <select id="uptime" onchange="uptime();">
    72.   <option value="0">Off</option>
    73.   <option value="1">1s</option>
    74.   <option value="5">5s</option>
    75. </select>
    76. </td></tr>
    77. </table>

    78. <table>
    79. <tr><th colspan="2">CPU</th></tr>
    80. <tr><td>Model</td>
    81. <td>${cpu_model}</td></tr>
    82. <tr><td>Cores</td><td>${cpu_cores}</td></tr>
    83. <tr><td>Load</td><td>${sys_load}</td></tr>
    84. </table>

    85. <table>
    86. <tr><th colspan="2">Memory</th></tr>
    87. <tr><td>Total</td><td>${mem_total} Mb</td></tr>
    88. <tr><td>Used</td><td>${mem_used} Mb</td></tr>
    89. <tr><td>Free</td><td>${mem_free} Mb</td></tr>
    90. </table>

    91. <table>
    92. <tr><th colspan="2">Network</th></tr>
    93. <tr><td>MAC Address</td><td>${net_mac}</td></tr>
    94. <tr><td>Internal IP</td><td>${net_ip_loc}</td></tr>
    95. <tr><td>External IP</td><td>${net_ip_ext}</td></tr>
    96. </table>

    97. </div>

    98. <div class="section">
    99. <table id="registers">
    100. <tr>
    101. <th>Address</th>
    102. <th>Peek Value</th>
    103. <th>Sel</th>
    104. <th>Peek</th>
    105. <th>Status</th>
    106. <th>Copy</th>
    107. <th>Poke Value</th>
    108. <th>Sel</th>
    109. <th>Poke</th>
    110. <th>Status</th>
    111. <th>Description</th>
    112. </tr>
    113. </table>
    114. <br><br>
    115. <input title="Add new row to end of address table" type="button" value="Add" onclick="add_row()">
    116. <select title="Set type of row to add to address table" id="type">
    117.   <option value="0">Register</option>
    118.   <option value="1">Section</option>
    119. </select>
    120. <input title="Remove last address from table" type="button" value="Remove" onclick="rem_register()">
    121. <input title="Peek all selected addresses in table" type="button" value="Peek All" onclick="peek_all()">
    122. <input title="Copy all table peek values into poke values" type="button" value="Copy All" onclick="copy_all()">
    123. <input title="Poke all selected addresses in table" type="button" value="Poke All" onclick="poke_all()">
    124. Peek Refresh :
    125. <select title="Set timer interval for automatic peek of table addresses" id="timer" onchange="timer()">
    126.   <option value="0">Off</option>
    127.   <option value="1">1s</option>
    128.   <option value="5">5s</option>
    129. </select>
    130. Number Format :
    131. <select title="Set number format for peek and poke values" id="format" onchange="format()">
    132.   <option value="0">Hexadecimal</option>
    133.   <option value="1">Unsigned</option>
    134. </select>
    135. Configuration :
    136. <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>
    137. <input title="Read configuration file into table" type="file" id="load_config">
    138. </div>

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

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

    141. </body>
    142. </html>
    143. EOF

    15. Update Javascript to peek ID strings

    Edit the existing Javascript to add the functions required to support reading strings from the PL address space and dynamically updating the the webpage accordingly. Lines 5-32 illustrate the potential code required.
    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. // Peek all strings
    5. function read_ids() {
    6.   read_id(0x000, 0);
    7.   read_id(0x080, 1);
    8.   read_id(0x0C0, 2);
    9.   read_id(0x100, 3);
    10.   read_id(0x110, 4);
    11. }

    12. // Peek string & display result
    13. function read_id(offset, reg) {
    14.   var url = "/cgi-bin/peekstring?0x40000000&4096&" + offset + "&128";
    15.   if (window.XMLHttpRequest) {
    16.     var ajaxReq = new XMLHttpRequest();
    17.     ajaxReq.onreadystatechange = function() {
    18.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    19.         var respText = ajaxReq.responseText;
    20.         var img_obj = document.getElementById("sid_" + reg);
    21.         // Unique number is added to image to avoid caching issues on separate animations
    22.         const now = Date.now();
    23.         if (respText.substr(0,6) == "Error:") {
    24.           img_obj.src = "../red.png?" + now;
    25.           img_obj.title = "Last peekstring failed : " + respText.substr(7);
    26.         } else {
    27.           const now = Date.now();
    28.           img_obj.src = "../green.gif?" + now;
    29.           img_obj.title = "Last peekstring successful";
    30.           document.getElementById("id_" + reg).innerHTML = respText;
    31.         }
    32.       }
    33.     }
    34.     ajaxReq.open("POST", url, true);
    35.     ajaxReq.send(null);
    36.   }
    37. }

    38. // Get uptime
    39. function get_uptime(reg) {
    40.   var url = "cgi-bin/uptime.cgi";
    41.   if (window.XMLHttpRequest) {
    42.     var ajaxReq = new XMLHttpRequest();
    43.     ajaxReq.onreadystatechange = function() {
    44.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    45.         var respText = ajaxReq.responseText;
    46.         txtObj = document.getElementById("uptime_text");
    47.         if (txtObj) {
    48.           txtObj.innerHTML = respText;
    49.         }
    50.       }
    51.     }
    52.     ajaxReq.open("POST", url, true);
    53.     ajaxReq.send(null);
    54.   }
    55. }

    56. // Update uptime timer
    57. function uptime() {
    58.   clearInterval(timer_uptime);
    59.   var uptime = document.getElementById("uptime");
    60.   var interval = uptime.value;
    61.   if (interval > 0) {
    62.     timer_uptime = setInterval("get_uptime()", 1000 * interval);
    63.   }
    64. }

    65. // Update peek_all timer
    66. function timer() {
    67.   clearInterval(timer_peek_all);
    68.   var timer = document.getElementById("timer");
    69.   interval = timer.value;
    70.   if (interval > 0) {
    71.     timer_peek_all = setInterval("peek_all()", 1000 * interval);
    72.   }
    73. }

    74. // Update number format
    75. function format() {
    76.   var table = document.getElementById("registers");
    77.   var rows = table.rows.length;
    78.   var peek;
    79.   var poke;
    80.   for (var index = 0; index < rows; index++) {
    81.     peek = document.getElementById("peek_" + index);
    82.     if (peek) {
    83.       peek.value = fmtUnsignedLong(parseInt(peek.value));
    84.     }
    85.     poke = document.getElementById("poke_" + index);
    86.     if (poke) {
    87.       poke.value = fmtUnsignedLong(parseInt(poke.value));
    88.     }
    89.   }
    90. }

    91. // Convert unsigned long to dec/hex string
    92. function fmtUnsignedLong(value) {
    93.   var format = document.getElementById("format");
    94.   var hexStr;
    95.   if (format.value == 0) {
    96.     hexStr = value.toString(16).toUpperCase();
    97.     hexStr = "0x" + "00000000".substr(0, 8 - hexStr.length) + hexStr;
    98.   } else {
    99.     hexStr = value.toString(10);
    100.   }
    101.   return hexStr;
    102. }

    103. // Copy peek to poke
    104. function copy(reg) {
    105.   var peek = document.getElementById("peek_" + reg);
    106.   if (peek) {
    107.     var poke = document.getElementById("poke_" + reg);
    108.     if (poke) {
    109.       poke.value = peek.value;
    110.     }
    111.   }
    112. }

    113. // Copy all peek to poke
    114. function copy_all() {
    115.   var table = document.getElementById("registers");
    116.   var rows = table.rows.length - 1;
    117.   for (var index = 0; index < rows; index++) {
    118.     copy(index);
    119.   }
    120. }

    121. // Peek address & display result
    122. function peek(reg) {
    123.   var url = "/cgi-bin/peek?" + document.getElementById("addr_" + reg).value;
    124.   if (window.XMLHttpRequest) {
    125.     var ajaxReq = new XMLHttpRequest();
    126.     ajaxReq.onreadystatechange = function() {
    127.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    128.         var respText = ajaxReq.responseText;
    129.         var img_obj = document.getElementById("speek_" + reg);
    130.         // Unique number is added to image to avoid caching issues on separate animations
    131.         const now = Date.now();
    132.         if (respText.substr(0,6) == "Error:") {
    133.           img_obj.src = "../red.gif?" + now;
    134.           img_obj.title = "Last peek failed : " + respText.substr(7);
    135.         } else {
    136.           img_obj.src = "../green.gif?" + now;
    137.           img_obj.title = "Last peek successful";
    138.           document.getElementById("peek_" + reg).value = fmtUnsignedLong(parseInt(respText));
    139.         }
    140.       }
    141.     }
    142.     ajaxReq.open("POST", url, true);
    143.     ajaxReq.send(null);
    144.   }
    145. }

    146. // Peek all selected addresses in section
    147. function peek_section(row) {
    148.   var obj_sel;
    149.   do {
    150.     row++;
    151.     obj_sel = document.getElementById("peek_sel_" + row);
    152.     if (obj_sel) {
    153.       if (obj_sel.checked) {
    154.         peek(row);
    155.       }
    156.     }
    157.   } while (obj_sel);
    158. }

    159. // Peek all selected addresses in table
    160. function peek_all() {
    161.   var table = document.getElementById("registers");
    162.   var rows = table.rows.length - 1;
    163.   for (var index = 0; index < rows; index++) {
    164.     const obj_sel = document.getElementById("peek_sel_" + index);
    165.     if (obj_sel) {
    166.       if (obj_sel.checked) {
    167.         peek(index);
    168.       }
    169.     }
    170.   }
    171. }

    172. // Poke address & display result
    173. function poke(reg) {
    174.   var url = "/cgi-bin/poke?" + document.getElementById("addr_" + reg).value + "&" + document.getElementById("poke_" + reg).value;
    175.   if (window.XMLHttpRequest) {
    176.     var ajaxReq = new XMLHttpRequest();
    177.     ajaxReq.onreadystatechange = function() {
    178.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    179.         var respText = ajaxReq.responseText;
    180.         var img_obj = document.getElementById("spoke_" + reg);
    181.         // Unique number is added to image to avoid caching issues on separate animations
    182.         const now = Date.now();
    183.         if (respText.substr(0,6) == "Error:") {
    184.           img_obj.src = "../red.gif?" + now;
    185.           img_obj.title = "Last poke failed : " + respText.substr(7);
    186.         } else {
    187.           img_obj.src = "../green.gif?" + now;
    188.           img_obj.title = "Last poke successful";
    189.         }
    190.       }
    191.     }
    192.     ajaxReq.open("POST", url, true);
    193.     ajaxReq.send(null);
    194.   }
    195. }

    196. // Poke all selected addresses in section
    197. function poke_section(row) {
    198.   var obj_sel;
    199.   do {
    200.     row++;
    201.     obj_sel = document.getElementById("poke_sel_" + row);
    202.     if (obj_sel) {
    203.       if (obj_sel.checked) {
    204.         poke(row);
    205.       }
    206.     }
    207.   } while (obj_sel);
    208. }

    209. // Poke all selected addresses in table
    210. function poke_all() {
    211.   var table = document.getElementById("registers");
    212.   var rows = table.rows.length - 1;
    213.   for (var index = 0; index < rows; index++) {
    214.     const obj_sel = document.getElementById("poke_sel_" + index);
    215.     if (obj_sel) {
    216.       if (obj_sel.checked) {
    217.         poke(index);
    218.       }
    219.     }
    220.   }
    221. }

    222. // Add row to table
    223. function add_row() {
    224.   const obj_type = document.getElementById("type");
    225.   const row_type = obj_type.value;
    226.   switch(row_type) {
    227.     case "0":
    228.       add_register();
    229.       break;
    230.     case "1":
    231.       add_section();
    232.       break;
    233.     default:
    234.       break;
    235.   }
    236. }

    237. // Add row to table
    238. function add_register(reg) {
    239.   var table = document.getElementById("registers");
    240.   var next = table.rows.length - 1;
    241.   var row = table.insertRow(-1);
    242.   var newcell;
    243.   var addr = 0x41200000;
    244.   if (next > 0) {
    245.     const obj_addr = document.getElementById("addr_" + (next - 1));
    246.     if (obj_addr) {
    247.       addr = parseInt(document.getElementById("addr_" + (next - 1)).value) + 4;
    248.     }
    249.   }
    250.   newcell = row.insertCell(0);
    251.   newcell.innerHTML = '<input title="Address to peek/poke" type="text" id="addr_' + next + '" value="' + fmtUnsignedLong(addr) + '" size="10">';
    252.   newcell = row.insertCell(1);
    253.   newcell.innerHTML = '<input title="Value peeked at address" type="text" id="peek_' + next + '" value="0x00000000" size="10" readonly="readonly"></td>';
    254.   newcell = row.insertCell(2);
    255.   newcell.innerHTML = '<input title="Select address for peeking" type="checkbox" id="peek_sel_' + next + '" checked></td>';
    256.   newcell = row.insertCell(3);
    257.   newcell.innerHTML = '<input title="Peek address" type="submit" value="Peek" onclick="peek(' + next + ')">';
    258.   newcell = row.insertCell(4);
    259.   newcell.innerHTML = '<img title="Peek status" id="speek_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
    260.   newcell = row.insertCell(5);
    261.   newcell.innerHTML = '<input title="Copy peek value into poke value" type="submit" value=">>" onclick="copy(' + next + ')">';
    262.   newcell = row.insertCell(6);
    263.   newcell.innerHTML = '<input title="Value to poke at address" type="text" id="poke_' + next + '" value="0x00000000" size="10">';
    264.   newcell = row.insertCell(7);
    265.   newcell.innerHTML = '<input title="Select address for poking" type="checkbox" id="poke_sel_' + next + '" checked></td>';
    266.   newcell = row.insertCell(8);
    267.   newcell.innerHTML = '<input title="Poke address" type="submit" value="Poke" onclick="poke(' + next + ')">';
    268.   newcell = row.insertCell(9);
    269.   newcell.innerHTML = '<img title="Poke status" id="spoke_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
    270.   newcell = row.insertCell(10);
    271.   newcell.innerHTML = '<input title="Description of address" type="text" id="name_' + next + '" value="Register @ ' + fmtUnsignedLong(addr) + '" size="40">';
    272. }

    273. // Add group row to table
    274. function add_section(reg) {
    275.   var table = document.getElementById("registers");
    276.   var next = table.rows.length - 1;
    277.   var row = table.insertRow(-1);
    278.   var newcell;
    279.   newcell = row.insertCell(0);
    280.   newcell.colSpan = "3";
    281.   newcell.innerHTML = "--- Section ---";
    282.   newcell = row.insertCell(1);
    283.   newcell.innerHTML = '<input title="Peek all selected addresses in section" type="submit" value="Peek" onclick="peek_section(' + next + ')">';
    284.   newcell = row.insertCell(2);
    285.   newcell.colSpan = "4";
    286.   newcell.innerHTML = "--- Section ---";
    287.   newcell = row.insertCell(3);
    288.   newcell.innerHTML = '<input title="Poke all selected addresses in section" type="submit" value="Poke" onclick="poke_section(' + next + ')">';
    289.   newcell = row.insertCell(4);
    290.   newcell = row.insertCell(5);
    291.   newcell.colSpan = "2";
    292.   newcell.innerHTML = '<input title="Description of section" type="text" id="name_' + next + '" value="Section Description" size="40">';
    293. }

    294. // Remove row from table
    295. function rem_register(reg) {
    296.   var table = document.getElementById("registers");
    297.   if (table.rows.length > 1) {
    298.     table.deleteRow(-1);
    299.   }
    300. }

    301. // Remove all rows from table
    302. function remove_all() {
    303.   var table = document.getElementById("registers");
    304.   var rows = table.rows.length - 1;
    305.   for (var index = 0; index < rows; index++) {
    306.     table.deleteRow(-1);
    307.   }
    308. }

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

    310. var config_file = null;

    311. // Create virtual configuration file from address table for user to download
    312. function create_config() {
    313.   var text = "";
    314.   var table = document.getElementById("registers");
    315.   const rows = table.rows.length - 1;
    316.   for (var index = 0; index < rows; index++) {
    317.     const obj_addr = document.getElementById("addr_" + index);
    318.     if (obj_addr) { // Register type
    319.       var addr = document.getElementById("addr_" + index).value;
    320.       var peek_sel = document.getElementById("peek_sel_" + index).checked;
    321.       var poke = document.getElementById("poke_" + index).value;
    322.       var poke_sel = document.getElementById("poke_sel_" + index).checked;
    323.       var name = document.getElementById("name_" + index).value;
    324.       text += "reg" + "|" + addr + "|" + peek_sel + "|" + poke + "|" + poke_sel + "|"+ name + "\n";
    325.     } else { // Section type
    326.       var name = document.getElementById("name_" + index).value;
    327.       text += "sec" + "|" + name + "\n";
    328.     }
    329.   }
    330.   const data = new Blob([text], {type: 'text/plain'});
    331.   if (config_file !== null) {
    332.     URL.revokeObjectURL(config_file);
    333.   }
    334.   config_file = URL.createObjectURL(data);
    335.   var link = document.getElementById('download');
    336.   link.href = config_file;
    337.   link.style.display = 'inline';
    338. }

    339. // Read configuration file and update address table
    340. function load_config(input) {
    341.   var file = input.target.files[0];
    342.   if (file) {
    343.     var reader = new FileReader();
    344.     reader.onload = function(input) {
    345.       var contents = input.target.result;
    346.       const lines = contents.split(/\r\n|\n/);
    347.       remove_all();
    348.       lines.forEach((line) => {
    349.         if (line.length > 0) {
    350.           var table = document.getElementById("registers");
    351.           var next = table.rows.length - 1;
    352.           const values = line.split("|");
    353.           switch(values[0]) {
    354.             case "reg":
    355.               add_register();
    356.               document.getElementById("addr_" + next).value = values[1];
    357.               if (values[2] == "false") {
    358.                 document.getElementById("peek_sel_" + next).checked = false;
    359.               }
    360.               document.getElementById("poke_" + next).value = values[3];
    361.               if (values[4] == "false") {
    362.                 document.getElementById("poke_sel_" + next).checked = false;
    363.               }
    364.               document.getElementById("name_" + next).value = values[5];
    365.               break;
    366.             case "sec":
    367.               add_section();
    368.               document.getElementById("name_" + next).value = values[1];
    369.               break;
    370.             default:
    371.               alert("Error: Unrecognized table type found (" + values[0] + "), ignoring.");
    372.           }
    373.         }
    374.       });
    375.     };
    376.     reader.readAsText(file);
    377.   }
    378. }

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

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

    17. Build & package PetaLinux

    Rebuild PetaLinux to include the updates and package it ready for deployment.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-build
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-package --prebuilt --force
    #### Part 5 - 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 PetaLinux 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 button to read the Identification information from the PL address space. Use the Browse... button to select the previously saved configuration file zedboard_leds_switches.txt from ~/projects/zedboard_linux/os/petalinux/src/other. Poke the LEDs & Peek the Switches. Poke Register Bank 1, Poke Register Bank 3 and Peek All selected address in the Register Bank. All being well the webpage should look similar to the following... Missing Image!
    #### Part 6 - Revision Control ####

    22. Commit new & updated files

    Add and commit the new & updated files, create annotated tags and push the commits & tags up to the remote repositories.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git add project-spec/meta-user/recipes-apps/peekpokecgi/files/peekstring.c
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git commit -am "Enhanced webpage for website application to have access to product ID strings within PL address space."
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git tag -a v9.0 -m "PetaLinux, Peek/Poke, LED Runner, Webserver, Peek/Poke CGI, PL Access, Style Sheet, Register Bank & ID Strings with XSA from zedboard_leds_switches v3.0"
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git push
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ cd ../../../zedboard_leds_switches
    steve@Desktop:~/projects/zedboard_leds_switches$ git add fw/src/design/axi_identification.v
    steve@Desktop:~/projects/zedboard_leds_switches$ git add fw/src/diagram/system/hdl/system_wrapper.sv
    steve@Desktop:~/projects/zedboard_leds_switches$ git add fw/project.txt
    steve@Desktop:~/projects/zedboard_leds_switches$ git add fw/src/script/pre_synth.tcl
    steve@Desktop:~/projects/zedboard_leds_switches$ git add fw/src/script/post_bit.tcl
    steve@Desktop:~/projects/zedboard_leds_switches$ git commit -am "Added AXI Identification to the design."
    steve@Desktop:~/projects/zedboard_leds_switches$ git tag -a v3.0 -m "ZYNQ, GPIO, Register Bank & Identification"
    steve@Desktop:~/projects/zedboard_leds_switches$ git push
    #### Part 6 - Quickstart ####

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

    The source files relating to this tutorial for both Firmware & OS can be obtained from Bitbucket. The Firmare repository is optional and only required if the Firmware is to be modyfied and rebuilt. The OS repository is essential as it contains both PetaLinux and a local copy of the exported hardware (firmware).

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

    Part A (optional) - Obtain firmware source, create Vivado project, build firmware & export hardware.
    steve@Desktop:~$ cd ~/projects
    steve@Desktop:~/projects$ git clone -b v3.0 https://bitbucket.org/spacewire_firmware/zedboard_leds_switches
    steve@Desktop:~/projects$ cd zedboard_leds_switches
    steve@Desktop:~/projects/zedboard_leds_switches$ create_vivado_project.sh
    Do something with the Firmware (if required) then perform the following steps :-
    1. Generate bitstream
    2. Export hardware platform
    Part B - Obtain OS source, build & deploy on Zedboard.
    steve@Desktop:~$ cd ~/projects
    steve@Desktop:~/projects$ git clone -b v9.0 https://bitbucket.org/spacewire_firmware/zedboard_linux
    steve@Desktop:~/projects$ cd zedboard_linux/os/petalinux
    If Part A was executed perform the following step :-
    1. Update hardware platform
    Do something with the OS (if required) then perform the following steps :-
    1. Build & package PetaLinux
    2. Setup Zedboard hardware
    3. Launch MiniCom terminal emulator
    4. Deploy PetaLinux on Zedboard
    5. Check everything is working as expected