Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 21 - 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 :-
  1. Setup environment
  2. Change present working directory
  3. Launch Vivado & open project
  4. Create AXI4-Lite peripheral (HDL)
  5. Open block design
  6. Re-customize AXI GPIO
  7. Add peripheral to block design
  8. Edit address map
  9. Create, move & modify HDL wrapper
  10. Create build hook scripts
  11. Generate bitstream
  12. Export hardware platform
  13. Create peek string C application
  14. Update website to display ID strings
  15. Update Javascript to peek ID strings
  16. Rebuild PetaLinux to include updates
  17. Setup Zedboard hardware
  18. Deploy PetaLinux on Zedboard
  19. Check everything is working as expected
  20. Files for revision control

1. Setup environment

Setup Xilinx design environment for the 2021.2 toolset.
steve@Linux-Steve:/home/steve$ source xilinx.sh
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@Linux-Steve:/home/steve$ cd /home/steve/projects/leds_switches/fw

3. Launch Vivado & open project

Launch Vivado quietly from a Terminal.
steve@Linux-Steve:/home/steve/projects/leds_switches/fw$ vivado -nojournal -nolog -notrace 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 /home/steve/projects/leds_switches/usr/hdl. 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 these to 0x4000_0000 and 4K respectively. 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@Linux-Steve:/home/steve/projects/leds_switches/fw$ cp project.srcs/sources_1/imports/hdl/system_wrapper.v ../usr/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/projects/leds_switches/usr/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@Linux-Steve:/home/steve/projects/leds_switches/fw$ subl 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@Linux-Steve:/home/steve/projects/leds_switches/fw$ subl {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/projects/leds_switches/fw/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/projects/leds_switches/fw/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. Include the bitstream in the exported hardware file and save as /home/steve/projects/leds_switches/fw/system_wrapper.xsa.

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@Linux-Steve:/home/steve/projects/leds_switches/fw$ cd ../../petalinux
steve@Linux-Steve:/home/steve/projects/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@Linux-Steve:/home/steve/projects/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@Linux-Steve:/home/steve/projects/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@Linux-Steve:/home/steve/projects/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="2">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. </tr>
  39. <tr>
  40. <td style="text-align:right">Company :</td>
  41. <td style="text-align:left" id="id_1">Unknown</td>
  42. </tr>
  43. <tr>
  44. <td style="text-align:right">Author :</td>
  45. <td style="text-align:left" id="id_2">Unknown</td>
  46. </tr>
  47. <tr>
  48. <td style="text-align:right">Build Version :</td>
  49. <td style="text-align:left" id="id_3">Unknown</td>
  50. </tr>
  51. <tr>
  52. <td style="text-align:right">Build Timestamp :</td>
  53. <td style="text-align:left" id="id_4">Unknown</td>
  54. </tr>
  55. </tbody>
  56. </table>
  57. </div>

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

  59. <div class="section">

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

  73. <table>
  74. <tr><th colspan="2">CPU</th></tr>
  75. <tr><td>Model</td>
  76. <td>${cpu_model}</td></tr>
  77. <tr><td>Cores</td><td>${cpu_cores}</td></tr>
  78. <tr><td>Load</td><td>${sys_load}</td></tr>
  79. </table>

  80. <table>
  81. <tr><th colspan="2">Memory</th></tr>
  82. <tr><td>Total</td><td>${mem_total} Mb</td></tr>
  83. <tr><td>Used</td><td>${mem_used} Mb</td></tr>
  84. <tr><td>Free</td><td>${mem_free} Mb</td></tr>
  85. </table>

  86. <table>
  87. <tr><th colspan="2">Network</th></tr>
  88. <tr><td>MAC Address</td><td>${net_mac}</td></tr>
  89. <tr><td>Internal IP</td><td>${net_ip_loc}</td></tr>
  90. <tr><td>External IP</td><td>${net_ip_ext}</td></tr>
  91. </table>

  92. </div>

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

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

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

  130. </body>
  131. </html>
  132. 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@Linux-Steve:/home/steve/projects/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.         if (respText.substr(0,6) == "Error:") {
  21.           alert("Error!");
  22.         } else {
  23.           document.getElementById("id_" + reg).innerHTML = respText;
  24.         }
  25.       }
  26.     }
  27.     ajaxReq.open("POST", url, true);
  28.     ajaxReq.send(null);
  29.   }
  30. }

  31. // Get uptime
  32. function get_uptime(reg) {
  33.   var url = "cgi-bin/uptime.cgi";
  34.   if (window.XMLHttpRequest) {
  35.     var ajaxReq = new XMLHttpRequest();
  36.     ajaxReq.onreadystatechange = function() {
  37.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
  38.         var respText = ajaxReq.responseText;
  39.         txtObj = document.getElementById("uptime_text");
  40.         if (txtObj) {
  41.           txtObj.innerHTML = respText;
  42.         }
  43.       }
  44.     }
  45.     ajaxReq.open("POST", url, true);
  46.     ajaxReq.send(null);
  47.   }
  48. }

  49. // Update uptime timer
  50. function uptime() {
  51.   clearInterval(timer_uptime);
  52.   var uptime = document.getElementById("uptime");
  53.   var interval = uptime.value;
  54.   if (interval > 0) {
  55.     timer_uptime = setInterval("get_uptime()", 1000 * interval);
  56.   }
  57. }

  58. // Update peek_all timer
  59. function timer() {
  60.   clearInterval(timer_peek_all);
  61.   var timer = document.getElementById("timer");
  62.   interval = timer.value;
  63.   if (interval > 0) {
  64.     timer_peek_all = setInterval("peek_all()", 1000 * interval);
  65.   }
  66. }

  67. // Update number format
  68. function format() {
  69.   var table = document.getElementById("registers");
  70.   var rows = table.rows.length;
  71.   var peek;
  72.   var poke;
  73.   for (var index = 0; index < rows; index++) {
  74.     peek = document.getElementById("peek_" + index);
  75.     if (peek) {
  76.       peek.value = fmtUnsignedLong(parseInt(peek.value));
  77.     }
  78.     poke = document.getElementById("poke_" + index);
  79.     if (poke) {
  80.       poke.value = fmtUnsignedLong(parseInt(poke.value));
  81.     }
  82.   }
  83. }

  84. // Convert unsigned long to dec/hex string
  85. function fmtUnsignedLong(value) {
  86.   var format = document.getElementById("format");
  87.   var hexStr;
  88.   if (format.value == 0) {
  89.     hexStr = value.toString(16).toUpperCase();
  90.     hexStr = "0x" + "00000000".substr(0, 8 - hexStr.length) + hexStr;
  91.   } else {
  92.     hexStr = value.toString(10);
  93.   }
  94.   return hexStr;
  95. }

  96. // Copy peek to poke
  97. function copy(reg) {
  98.   var peek = document.getElementById("peek_" + reg);
  99.   if (peek) {
  100.     var poke = document.getElementById("poke_" + reg);
  101.     if (poke) {
  102.       poke.value = peek.value;
  103.     }
  104.   }
  105. }

  106. // Copy all peek to poke
  107. function copy_all() {
  108.   var table = document.getElementById("registers");
  109.   var rows = table.rows.length - 1;
  110.   for (var index = 0; index < rows; index++) {
  111.     copy(index);
  112.   }
  113. }

  114. // Peek all addresses
  115. function peek_all() {
  116.   var table = document.getElementById("registers");
  117.   var rows = table.rows.length - 1;
  118.   for (var index = 0; index < rows; index++) {
  119.     peek(index);
  120.   }
  121. }

  122. // Poke all addresses
  123. function poke_all() {
  124.   var table = document.getElementById("registers");
  125.   var rows = table.rows.length - 1;
  126.   for (var index = 0; index < rows; index++) {
  127.     poke(index);
  128.   }
  129. }

  130. // Add row to table
  131. function add_register(reg) {
  132.   var table = document.getElementById("registers");
  133.   var next = table.rows.length - 1;
  134.   var row = table.insertRow(-1);
  135.   var newcell;
  136.   var addr = 0x41200000;
  137.   if (next > 0) {
  138.     addr = parseInt(document.getElementById("addr_" + (next - 1)).value) + 4;
  139.   }
  140.   newcell = row.insertCell(0);
  141.   newcell.innerHTML = '<input type="text" id="addr_' + next + '" value="' + fmtUnsignedLong(addr) + '" size="10">';
  142.   newcell = row.insertCell(1);
  143.   newcell.innerHTML = '<input type="text" id="peek_' + next + '" value="0x00000000" size="10" readonly="readonly"></td>';
  144.   newcell = row.insertCell(2);
  145.   newcell.innerHTML = '<input type="submit" value="Peek" onclick="peek(' + next + ')">';
  146.   newcell = row.insertCell(3);
  147.   newcell.innerHTML = '<span id="speek_' + next + '">-</span>';
  148.   newcell = row.insertCell(4);
  149.   newcell.innerHTML = '<input type="submit" value=">>" onclick="copy(' + next + ')">';
  150.   newcell = row.insertCell(5);
  151.   newcell.innerHTML = '<input type="text" id="poke_' + next + '" value="0x00000000" size="10">';
  152.   newcell = row.insertCell(6);
  153.   newcell.innerHTML = '<input type="submit" value="Poke" onclick="poke(' + next + ')">';
  154.   newcell = row.insertCell(7);
  155.   newcell.innerHTML = '<span id="spoke_' + next + '">-</span>';
  156.   newcell = row.insertCell(8);
  157.   newcell.innerHTML = '<input type="text" id="name_' + next + '" value="Register @ ' + fmtUnsignedLong(addr) + '" size="40">';
  158. }

  159. // Remove all rows from table
  160. function remove_all() {
  161.   var table = document.getElementById("registers");
  162.   var rows = table.rows.length - 1;
  163.   for (var index = 0; index < rows; index++) {
  164.     table.deleteRow(-1);
  165.   }
  166. }

  167. // Remove row from table
  168. function rem_register(reg) {
  169.   var table = document.getElementById("registers");
  170.   if (table.rows.length > 1) {
  171.     table.deleteRow(-1);
  172.   }
  173. }

  174. // Poke address & display result
  175. function poke(reg) {
  176.   var url = "/cgi-bin/poke?" + document.getElementById("addr_" + reg).value + "&" + document.getElementById("poke_" + reg).value;
  177.   if (window.XMLHttpRequest) {
  178.     var ajaxReq = new XMLHttpRequest();
  179.     ajaxReq.onreadystatechange = function() {
  180.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
  181.         var respText = ajaxReq.responseText;
  182.         if (respText.substr(0,6) == "Error:") {
  183.           document.getElementById("spoke_" + reg).innerHTML = "Failed";
  184.         } else {
  185.           document.getElementById("spoke_" + reg).innerHTML = "Success";
  186.         }
  187.       }
  188.     }
  189.     ajaxReq.open("POST", url, true);
  190.     ajaxReq.send(null);
  191.   }
  192. }

  193. // Peek address & display result
  194. function peek(reg) {
  195.   var url = "/cgi-bin/peek?" + document.getElementById("addr_" + reg).value;
  196.   if (window.XMLHttpRequest) {
  197.     var ajaxReq = new XMLHttpRequest();
  198.     ajaxReq.onreadystatechange = function() {
  199.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
  200.         var respText = ajaxReq.responseText;
  201.         if (respText.substr(0,6) == "Error:") {
  202.           document.getElementById("speek_" + reg).innerHTML = "Failed";
  203.         } else {
  204.           document.getElementById("speek_" + reg).innerHTML = "Success";
  205.           document.getElementById("peek_" + reg).value = fmtUnsignedLong(parseInt(respText));
  206.         }
  207.       }
  208.     }
  209.     ajaxReq.open("POST", url, true);
  210.     ajaxReq.send(null);
  211.   }
  212. }

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

  214. var config_file = null;

  215. // Create virtual configuration file from address table for user to download
  216. function create_config() {
  217.   var text = "";
  218.   var table = document.getElementById("registers");
  219.   var rows = table.rows.length - 1;
  220.   for (var index = 0; index < rows; index++) {
  221.     var addr = document.getElementById("addr_" + index).value;
  222.     var poke = document.getElementById("poke_" + index).value;
  223.     var name = document.getElementById("name_" + index).value;
  224.     text += addr + "|" + poke + "|" + name + "\n";
  225.   }
  226.   const data = new Blob([text], {type: 'text/plain'});
  227.   if (config_file !== null) {
  228.     URL.revokeObjectURL(config_file);
  229.   }
  230.   config_file = URL.createObjectURL(data);
  231.   var link = document.getElementById('download');
  232.   link.href = config_file;
  233.   link.style.display = 'inline';
  234. }

  235. // Read configuration file and update address table
  236. function load_config(input) {
  237.   var file = input.target.files[0];
  238.   if (file) {
  239.     var reader = new FileReader();
  240.     reader.onload = function(input) {
  241.       var contents = input.target.result;
  242.       const lines = contents.split(/\r\n|\n/);
  243.       remove_all();
  244.       lines.forEach((line) => {
  245.         if (line.length > 0) {
  246.           var table = document.getElementById("registers");
  247.           var next = table.rows.length - 1;
  248.           add_register();
  249.           const values = line.split("|");
  250.           document.getElementById("addr_" + next).value = values[0];
  251.           document.getElementById("poke_" + next).value = values[1];
  252.           document.getElementById("name_" + next).value = values[2];
  253.         }
  254.       });
  255.     };
  256.     reader.readAsText(file);
  257.   }
  258. }

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

16. Rebuild PetaLinux to include updates

Rebuild PetaLinux to include the updated FPGA bitstream, the updated peekpokecgi application & the updated website application.
steve@Linux-Steve:/home/steve/projects/petalinux$ petalinux-config --get-hw-description ../leds_switches/fw/system_wrapper.xsa
steve@Linux-Steve:/home/steve/projects/petalinux$ petalinux-build
steve@Linux-Steve:/home/steve/projects/petalinux$ petalinux-package --prebuilt --force

17. Setup Zedboard hardware

If not already, connect up the hardware as follows and power on the Zedboard :-
  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!

18. Deploy PetaLinux on Zedboard

Deploy the updated PetaLinux image via JTAG.
steve@Linux-Steve:/home/steve/projects/petalinux$ petalinux-boot --jtag --prebuilt 3

19. 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 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.
Using a web browser access the Zedboard Webserver at 192.168.2.77 and 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 (leds_switched.txt). Poke the Register @ 0x0, Poke the Register @ 0x8 and then Peek All the registers. All being well the webpage should look similar to the following... Missing Image!

20. Files for revision control

The following files/directories are the new ones to add to the repository.