Bar
SpaceWire UK
Specialist providers of VHDL Intellectual Property & Design Services
BarBarBarBar
Tutorial
Missing Image!
Part 13 - Add bank of registers inside PL and access via Webserver (28 November 2021)

Introduction

This tutorial details the steps required to add a general purpose bank of registers inside the Programmable Logic part of the FPGA. Control of these registers shall be via the Webserver, which will be enhanced to allow access to more than just one register. The peek & CGI access will be updated to use the binaries instead of the scripts that call the binaries. Note Part 17 of this tutorial is newly added and is a prerequisite for this part.

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
    3. Open block design
    4. Re-customize AXI GPIO
    5. Add peripheral to block design
    6. Edit address map
    7. Generate bitstream
    8. Export hardware platform

    Part 3 - Hardware Deployment 1

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

    Part 4 - OS Development

    1. Enhance website peek & poke access
    2. Update Javascript for enhanced website
    3. Modify BitBake recipe
    4. Quick check enhanced website is working as expected
    5. Update hardware platform
    6. Build & package PetaLinux

    Part 5 - Hardware Deployment 2

    1. Deploy PetaLinux on Zedboard
    2. 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

    To create an AXI4-Lite peripheral (register bank) navigate Tools » Create and Package New IP....

    In the Create and Package New IP dialog click Next to proceed. Missing Image! Select Create AXI4 Peripheral in the Create and Package New IP - Create Peripheral, Package IP or Package a Block Design dialog and click Next to proceed. Missing Image! Populate the following details in the Create and Package New IP - Peripheral Details dialog and click Next to proceed. Missing Image! In the Create and Package New IP - Add Interfaces dialog leave the defaults as shown and click Next to proceed. Missing Image! Review the information provided in the Create and Package New IP - Create Peripheral dialog and click Finish to proceed. Missing Image! Quite a few useful files have now been created in the ip_repo directory, including a testbench that uses the Xilinx AXI4 Verification IP. The two main files of interest are the HDL sources, which can be found in the hdl directory. Examine these to get a better understanding of what they are doing. The operational code is provided in register_bank_v1_0_S00_AXI.v along with a wrapper provided in register_bank_v1_0.v.
    steve@Desktop:~/projects/zedboard_leds_switches$ subl fw/src/ip_repo/register_bank_1.0/hdl/{register_bank_v1_0_S00_AXI.v,register_bank_v1_0.v}

    5. Open block design

    To open the existing block design click 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 0x00000081 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 clicking on Add IP Missing Image! and then selecting AXI Register Bank from the context menu. 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! 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. Missing Image!

    8. Edit address map

    Check the address map assignment inside the Address Editor under the BLOCK DESIGN section. The register_bank_0 module should have a Master Base Address of 0x43C0_0000 and a Range of 64K, if not change them. Missing Image! Save the Block Design.

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

    10. 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 ~/projects/zedboard_leds_switches/fw/system_wrapper.xsa.
    #### Part 3 - Hardware Deployment 1 ####

    11. 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.

    12. 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

    13. Deploy PetaLinux on Zedboard

    If not already, deploy the PetaLinux image via JTAG.
    steve@Desktop:~/projects/zedboard_leds_switches$ cd ../zedboard_linux/os/petalinux
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-boot --jtag --prebuilt 3

    14. Program PL independently

    The Programmable Logic part of the FPGA can be reloaded while Linux is running without requiring a Linux reboot. To achieve this safely the interfaces between the PL & PS must be inactive and not transferring data.

    Program the PL from Vivado by clicking on Open Hardware Manager under PROGRAM AND DEBUG. Missing Image! In the HARDWARE MANAGER click on Open target and select Auto Connect from the context menu. Missing Image! In the HARDWARE MANAGER click on Program device. Missing Image! In the Program Device dialog click Program to program the PL. Missing Image!

    15. Check PL is working as expected

    All being well there should now exist four 32-bit registers within the address space 0x43C0_0000 to 0x43C0_000F.

    Using the Client URL (curl) command enter the following URL's to command the peek & poke CGI binaries to read & write from & to the address space.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ curl -w "\nReceived %{size_download} bytes\n" "http://192.168.2.87/cgi-bin/peek?0x43C00000"
    0x00000000
    Received 10 bytes
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ curl -w "\nReceived %{size_download} bytes\n" "http://192.168.2.87/cgi-bin/poke?0x43C00000&0x456789ab"
    Success
    Received 7 bytes
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ curl -w "\nReceived %{size_download} bytes\n" "http://192.168.2.87/cgi-bin/peek?0x43C00000"
    0x456789AB
    Received 10 bytes
    #### Part 4 - OS Development ####

    16. Enhance website peek & poke access

    Edit the existing webpage to enhance its peek & poke capabilities such that multiple registers can be accessed. Add in global controls for the multiple registers and also file access so the register configuration can be saved and re-loaded.
    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"><img src="../zedboard.png" alt="Missing Image!"></div>

    29. <div class="section">

    30. <table>
    31. <tr><th colspan="2">System</th></tr>
    32. <tr><td>Hostname</td>
    33. <td>${sys_host}</td>
    34. </tr><tr><td>Time</td><td>${sys_time}</td></tr>
    35. <tr><td>Uptime</td><td><span id="uptime_text">${sys_up}</span> seconds <button onclick="get_uptime()">Refresh</button> Auto :
    36. <select id="uptime" onchange="uptime();">
    37.   <option value="0">Off</option>
    38.   <option value="1">1s</option>
    39.   <option value="5">5s</option>
    40. </select>
    41. </td></tr>
    42. </table>

    43. <table>
    44. <tr><th colspan="2">CPU</th></tr>
    45. <tr><td>Model</td>
    46. <td>${cpu_model}</td></tr>
    47. <tr><td>Cores</td><td>${cpu_cores}</td></tr>
    48. <tr><td>Load</td><td>${sys_load}</td></tr>
    49. </table>

    50. <table>
    51. <tr><th colspan="2">Memory</th></tr>
    52. <tr><td>Total</td><td>${mem_total} Mb</td></tr>
    53. <tr><td>Used</td><td>${mem_used} Mb</td></tr>
    54. <tr><td>Free</td><td>${mem_free} Mb</td></tr>
    55. </table>

    56. <table>
    57. <tr><th colspan="2">Network</th></tr>
    58. <tr><td>MAC Address</td><td>${net_mac}</td></tr>
    59. <tr><td>Internal IP</td><td>${net_ip_loc}</td></tr>
    60. <tr><td>External IP</td><td>${net_ip_ext}</td></tr>
    61. </table>

    62. </div>

    63. <div class="section">
    64. <table id="registers">
    65. <tr>
    66. <th>Address</th>
    67. <th>Peek Value</th>
    68. <th>Sel</th>
    69. <th>Peek</th>
    70. <th>Status</th>
    71. <th>Copy</th>
    72. <th>Poke Value</th>
    73. <th>Sel</th>
    74. <th>Poke</th>
    75. <th>Status</th>
    76. <th>Description</th>
    77. </tr>
    78. </table>
    79. <br><br>
    80. <input title="Add new row to end of address table" type="button" value="Add" onclick="add_row()">
    81. <select title="Set type of row to add to address table" id="type">
    82.   <option value="0">Register</option>
    83.   <option value="1">Section</option>
    84. </select>
    85. <input title="Remove last address from table" type="button" value="Remove" onclick="rem_register()">
    86. <input title="Peek all selected addresses in table" type="button" value="Peek All" onclick="peek_all()">
    87. <input title="Copy all table peek values into poke values" type="button" value="Copy All" onclick="copy_all()">
    88. <input title="Poke all selected addresses in table" type="button" value="Poke All" onclick="poke_all()">
    89. Peek Refresh :
    90. <select title="Set timer interval for automatic peek of table addresses" id="timer" onchange="timer()">
    91.   <option value="0">Off</option>
    92.   <option value="1">1s</option>
    93.   <option value="5">5s</option>
    94. </select>
    95. Number Format :
    96. <select title="Set number format for peek and poke values" id="format" onchange="format()">
    97.   <option value="0">Hexadecimal</option>
    98.   <option value="1">Unsigned</option>
    99. </select>
    100. Configuration :
    101. <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>
    102. <input title="Read configuration file into table" type="file" id="load_config">
    103. </div>

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

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

    106. </body>
    107. </html>
    108. EOF

    17. Update Javascript for enhanced website

    Firstly download the following 3 image files (Missing Image!, Missing Image!, Missing Image!) into /home/steve/zedboard_leds_switches/project-spec/meta-user/recipes-apps/website/files. Note the second & third images are a play-once animations. Now edit the existing Javascript to add in the functions required to support the enhanced website. Also change the status reporting for the Peek & Poke operations to use the image files, adding to this a status message that appears when the mouse hovers over an image. The Javascript at this point is updated to be more flexible for future use. Note the addition of file operations for configuration file access.
    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. // Get uptime
    5. function get_uptime(reg) {
    6.   var url = "cgi-bin/uptime.cgi";
    7.   if (window.XMLHttpRequest) {
    8.     var ajaxReq = new XMLHttpRequest();
    9.     ajaxReq.onreadystatechange = function() {
    10.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    11.         var respText = ajaxReq.responseText;
    12.         txtObj = document.getElementById("uptime_text");
    13.         if (txtObj) {
    14.           txtObj.innerHTML = respText;
    15.         }
    16.       }
    17.     }
    18.     ajaxReq.open("POST", url, true);
    19.     ajaxReq.send(null);
    20.   }
    21. }

    22. // Update uptime timer
    23. function uptime() {
    24.   clearInterval(timer_uptime);
    25.   var uptime = document.getElementById("uptime");
    26.   var interval = uptime.value;
    27.   if (interval > 0) {
    28.     timer_uptime = setInterval("get_uptime()", 1000 * interval);
    29.   }
    30. }

    31. // Update peek_all timer
    32. function timer() {
    33.   clearInterval(timer_peek_all);
    34.   var timer = document.getElementById("timer");
    35.   interval = timer.value;
    36.   if (interval > 0) {
    37.     timer_peek_all = setInterval("peek_all()", 1000 * interval);
    38.   }
    39. }

    40. // Update number format
    41. function format() {
    42.   var table = document.getElementById("registers");
    43.   var rows = table.rows.length;
    44.   var peek;
    45.   var poke;
    46.   for (var index = 0; index < rows; index++) {
    47.     peek = document.getElementById("peek_" + index);
    48.     if (peek) {
    49.       peek.value = fmtUnsignedLong(parseInt(peek.value));
    50.     }
    51.     poke = document.getElementById("poke_" + index);
    52.     if (poke) {
    53.       poke.value = fmtUnsignedLong(parseInt(poke.value));
    54.     }
    55.   }
    56. }

    57. // Convert unsigned long to dec/hex string
    58. function fmtUnsignedLong(value) {
    59.   var format = document.getElementById("format");
    60.   var hexStr;
    61.   if (format.value == 0) {
    62.     hexStr = value.toString(16).toUpperCase();
    63.     hexStr = "0x" + "00000000".substr(0, 8 - hexStr.length) + hexStr;
    64.   } else {
    65.     hexStr = value.toString(10);
    66.   }
    67.   return hexStr;
    68. }

    69. // Copy peek to poke
    70. function copy(reg) {
    71.   var peek = document.getElementById("peek_" + reg);
    72.   if (peek) {
    73.     var poke = document.getElementById("poke_" + reg);
    74.     if (poke) {
    75.       poke.value = peek.value;
    76.     }
    77.   }
    78. }

    79. // Copy all peek to poke
    80. function copy_all() {
    81.   var table = document.getElementById("registers");
    82.   var rows = table.rows.length - 1;
    83.   for (var index = 0; index < rows; index++) {
    84.     copy(index);
    85.   }
    86. }

    87. // Peek address & display result
    88. function peek(reg) {
    89.   var url = "/cgi-bin/peek?" + document.getElementById("addr_" + reg).value;
    90.   if (window.XMLHttpRequest) {
    91.     var ajaxReq = new XMLHttpRequest();
    92.     ajaxReq.onreadystatechange = function() {
    93.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    94.         var respText = ajaxReq.responseText;
    95.         var img_obj = document.getElementById("speek_" + reg);
    96.         // Unique number is added to image to avoid caching issues on separate animations
    97.         const now = Date.now();
    98.         if (respText.substr(0,6) == "Error:") {
    99.           img_obj.src = "../red.gif?" + now;
    100.           img_obj.title = "Last peek failed : " + respText.substr(7);
    101.         } else {
    102.           img_obj.src = "../green.gif?" + now;
    103.           img_obj.title = "Last peek successful";
    104.           document.getElementById("peek_" + reg).value = fmtUnsignedLong(parseInt(respText));
    105.         }
    106.       }
    107.     }
    108.     ajaxReq.open("POST", url, true);
    109.     ajaxReq.send(null);
    110.   }
    111. }

    112. // Peek all selected addresses in section
    113. function peek_section(row) {
    114.   var obj_sel;
    115.   do {
    116.     row++;
    117.     obj_sel = document.getElementById("peek_sel_" + row);
    118.     if (obj_sel) {
    119.       if (obj_sel.checked) {
    120.         peek(row);
    121.       }
    122.     }
    123.   } while (obj_sel);
    124. }

    125. // Peek all selected addresses in table
    126. function peek_all() {
    127.   var table = document.getElementById("registers");
    128.   var rows = table.rows.length - 1;
    129.   for (var index = 0; index < rows; index++) {
    130.     const obj_sel = document.getElementById("peek_sel_" + index);
    131.     if (obj_sel) {
    132.       if (obj_sel.checked) {
    133.         peek(index);
    134.       }
    135.     }
    136.   }
    137. }

    138. // Poke address & display result
    139. function poke(reg) {
    140.   var url = "/cgi-bin/poke?" + document.getElementById("addr_" + reg).value + "&" + document.getElementById("poke_" + reg).value;
    141.   if (window.XMLHttpRequest) {
    142.     var ajaxReq = new XMLHttpRequest();
    143.     ajaxReq.onreadystatechange = function() {
    144.       if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
    145.         var respText = ajaxReq.responseText;
    146.         var img_obj = document.getElementById("spoke_" + reg);
    147.         // Unique number is added to image to avoid caching issues on separate animations
    148.         const now = Date.now();
    149.         if (respText.substr(0,6) == "Error:") {
    150.           img_obj.src = "../red.gif?" + now;
    151.           img_obj.title = "Last poke failed : " + respText.substr(7);
    152.         } else {
    153.           img_obj.src = "../green.gif?" + now;
    154.           img_obj.title = "Last poke successful";
    155.         }
    156.       }
    157.     }
    158.     ajaxReq.open("POST", url, true);
    159.     ajaxReq.send(null);
    160.   }
    161. }

    162. // Poke all selected addresses in section
    163. function poke_section(row) {
    164.   var obj_sel;
    165.   do {
    166.     row++;
    167.     obj_sel = document.getElementById("poke_sel_" + row);
    168.     if (obj_sel) {
    169.       if (obj_sel.checked) {
    170.         poke(row);
    171.       }
    172.     }
    173.   } while (obj_sel);
    174. }

    175. // Poke all selected addresses in table
    176. function poke_all() {
    177.   var table = document.getElementById("registers");
    178.   var rows = table.rows.length - 1;
    179.   for (var index = 0; index < rows; index++) {
    180.     const obj_sel = document.getElementById("poke_sel_" + index);
    181.     if (obj_sel) {
    182.       if (obj_sel.checked) {
    183.         poke(index);
    184.       }
    185.     }
    186.   }
    187. }

    188. // Add row to table
    189. function add_row() {
    190.   const obj_type = document.getElementById("type");
    191.   const row_type = obj_type.value;
    192.   switch(row_type) {
    193.     case "0":
    194.       add_register();
    195.       break;
    196.     case "1":
    197.       add_section();
    198.       break;
    199.     default:
    200.       break;
    201.   }
    202. }

    203. // Add row to table
    204. function add_register(reg) {
    205.   var table = document.getElementById("registers");
    206.   var next = table.rows.length - 1;
    207.   var row = table.insertRow(-1);
    208.   var newcell;
    209.   var addr = 0x41200000;
    210.   if (next > 0) {
    211.     const obj_addr = document.getElementById("addr_" + (next - 1));
    212.     if (obj_addr) {
    213.       addr = parseInt(document.getElementById("addr_" + (next - 1)).value) + 4;
    214.     }
    215.   }
    216.   newcell = row.insertCell(0);
    217.   newcell.innerHTML = '<input title="Address to peek/poke" type="text" id="addr_' + next + '" value="' + fmtUnsignedLong(addr) + '" size="10">';
    218.   newcell = row.insertCell(1);
    219.   newcell.innerHTML = '<input title="Value peeked at address" type="text" id="peek_' + next + '" value="0x00000000" size="10" readonly="readonly"></td>';
    220.   newcell = row.insertCell(2);
    221.   newcell.innerHTML = '<input title="Select address for peeking" type="checkbox" id="peek_sel_' + next + '" checked></td>';
    222.   newcell = row.insertCell(3);
    223.   newcell.innerHTML = '<input title="Peek address" type="submit" value="Peek" onclick="peek(' + next + ')">';
    224.   newcell = row.insertCell(4);
    225.   newcell.innerHTML = '<img title="Peek status" id="speek_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
    226.   newcell = row.insertCell(5);
    227.   newcell.innerHTML = '<input title="Copy peek value into poke value" type="submit" value=">>" onclick="copy(' + next + ')">';
    228.   newcell = row.insertCell(6);
    229.   newcell.innerHTML = '<input title="Value to poke at address" type="text" id="poke_' + next + '" value="0x00000000" size="10">';
    230.   newcell = row.insertCell(7);
    231.   newcell.innerHTML = '<input title="Select address for poking" type="checkbox" id="poke_sel_' + next + '" checked></td>';
    232.   newcell = row.insertCell(8);
    233.   newcell.innerHTML = '<input title="Poke address" type="submit" value="Poke" onclick="poke(' + next + ')">';
    234.   newcell = row.insertCell(9);
    235.   newcell.innerHTML = '<img title="Poke status" id="spoke_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
    236.   newcell = row.insertCell(10);
    237.   newcell.innerHTML = '<input title="Description of address" type="text" id="name_' + next + '" value="Register @ ' + fmtUnsignedLong(addr) + '" size="40">';
    238. }

    239. // Add group row to table
    240. function add_section(reg) {
    241.   var table = document.getElementById("registers");
    242.   var next = table.rows.length - 1;
    243.   var row = table.insertRow(-1);
    244.   var newcell;
    245.   newcell = row.insertCell(0);
    246.   newcell.colSpan = "3";
    247.   newcell.innerHTML = "--- Section ---";
    248.   newcell = row.insertCell(1);
    249.   newcell.innerHTML = '<input title="Peek all selected addresses in section" type="submit" value="Peek" onclick="peek_section(' + next + ')">';
    250.   newcell = row.insertCell(2);
    251.   newcell.colSpan = "4";
    252.   newcell.innerHTML = "--- Section ---";
    253.   newcell = row.insertCell(3);
    254.   newcell.innerHTML = '<input title="Poke all selected addresses in section" type="submit" value="Poke" onclick="poke_section(' + next + ')">';
    255.   newcell = row.insertCell(4);
    256.   newcell = row.insertCell(5);
    257.   newcell.colSpan = "2";
    258.   newcell.innerHTML = '<input title="Description of section" type="text" id="name_' + next + '" value="Section Description" size="40">';
    259. }

    260. // Remove row from table
    261. function rem_register(reg) {
    262.   var table = document.getElementById("registers");
    263.   if (table.rows.length > 1) {
    264.     table.deleteRow(-1);
    265.   }
    266. }

    267. // Remove all rows from table
    268. function remove_all() {
    269.   var table = document.getElementById("registers");
    270.   var rows = table.rows.length - 1;
    271.   for (var index = 0; index < rows; index++) {
    272.     table.deleteRow(-1);
    273.   }
    274. }

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

    276. var config_file = null;

    277. // Create virtual configuration file from address table for user to download
    278. function create_config() {
    279.   var text = "";
    280.   var table = document.getElementById("registers");
    281.   const rows = table.rows.length - 1;
    282.   for (var index = 0; index < rows; index++) {
    283.     const obj_addr = document.getElementById("addr_" + index);
    284.     if (obj_addr) { // Register type
    285.       var addr = document.getElementById("addr_" + index).value;
    286.       var peek_sel = document.getElementById("peek_sel_" + index).checked;
    287.       var poke = document.getElementById("poke_" + index).value;
    288.       var poke_sel = document.getElementById("poke_sel_" + index).checked;
    289.       var name = document.getElementById("name_" + index).value;
    290.       text += "reg" + "|" + addr + "|" + peek_sel + "|" + poke + "|" + poke_sel + "|"+ name + "\n";
    291.     } else { // Section type
    292.       var name = document.getElementById("name_" + index).value;
    293.       text += "sec" + "|" + name + "\n";
    294.     }
    295.   }
    296.   const data = new Blob([text], {type: 'text/plain'});
    297.   if (config_file !== null) {
    298.     URL.revokeObjectURL(config_file);
    299.   }
    300.   config_file = URL.createObjectURL(data);
    301.   var link = document.getElementById('download');
    302.   link.href = config_file;
    303.   link.style.display = 'inline';
    304. }

    305. // Read configuration file and update address table
    306. function load_config(input) {
    307.   var file = input.target.files[0];
    308.   if (file) {
    309.     var reader = new FileReader();
    310.     reader.onload = function(input) {
    311.       var contents = input.target.result;
    312.       const lines = contents.split(/\r\n|\n/);
    313.       remove_all();
    314.       lines.forEach((line) => {
    315.         if (line.length > 0) {
    316.           var table = document.getElementById("registers");
    317.           var next = table.rows.length - 1;
    318.           const values = line.split("|");
    319.           switch(values[0]) {
    320.             case "reg":
    321.               add_register();
    322.               document.getElementById("addr_" + next).value = values[1];
    323.               if (values[2] == "false") {
    324.                 document.getElementById("peek_sel_" + next).checked = false;
    325.               }
    326.               document.getElementById("poke_" + next).value = values[3];
    327.               if (values[4] == "false") {
    328.                 document.getElementById("poke_sel_" + next).checked = false;
    329.               }
    330.               document.getElementById("name_" + next).value = values[5];
    331.               break;
    332.             case "sec":
    333.               add_section();
    334.               document.getElementById("name_" + next).value = values[1];
    335.               break;
    336.             default:
    337.               alert("Error: Unrecognized table type found (" + values[0] + "), ignoring.");
    338.           }
    339.         }
    340.       });
    341.     };
    342.     reader.readAsText(file);
    343.   }
    344. }

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

    18. Modify BitBake recipe

    Edit the BitBake recipe to add in the entries for the amber, green & red image files such that they will be installed into /srv/www.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ subl project-spec/meta-user/recipes-apps/website/website.bb

    website.bb

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

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

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

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

    18. S = "${WORKDIR}"

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

    19. Quick check enhanced website is working as expected

    Copy the modified files over to PetaLinux to check they work.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ sshpass -p root scp project-spec/meta-user/recipes-apps/website/files/cgi-bin/index.cgi root@192.168.2.87:/srv/www/cgi-bin
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ sshpass -p root scp project-spec/meta-user/recipes-apps/website/files/uptime.js root@192.168.2.87:/srv/www
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ sshpass -p root scp project-spec/meta-user/recipes-apps/website/files/amber.gif root@192.168.2.87:/srv/www
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ sshpass -p root scp project-spec/meta-user/recipes-apps/website/files/green.gif root@192.168.2.87:/srv/www
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ sshpass -p root scp project-spec/meta-user/recipes-apps/website/files/red.gif root@192.168.2.87:/srv/www
    Access the webserver running on the Zedboard using a browser pointing at the Zedboard's IP address (192.168.2.87). All being well something akin to the following webpage should be displayed. Missing Image! The controls in the Peek & Poke section of the enhanced website are as follows :- Create the above register table by using the Add button to add new registers & sections, the Address column to edit the address locations, the Poke Value column to edit the values to poke and the Description column to provide a description of what the registers & sections are used for. Poke the LEDs register and Poke two registers inside the Register Bank at addresses 0x43C00004 and 0x43C0000C. Use Peek All to peek all the registers shown in the table, Peek (section) to peek all the selected registers in a section or Peek to peek an individual register.

    Create a configuration file from the address table by clicking on Create.... A generated link (config.txt) should now appear next to the Create... button. Right click on the link and select Save Link As..., set the name to zedboard_leds_switches.txt and save the file in ~/projects/zedboard_linux/os/petalinux/src/other.

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

    21. Build & package PetaLinux

    Both the FPGA bitstream and enhanced website are currently soft-loaded for test purposes. Since the testing as proven successful PetaLinux can be rebuilt to include the updates and packaged 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 2 ####

    22. Deploy PetaLinux on Zedboard

    Reload PetaLinux to double check it now contains the updated files.
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ petalinux-boot --jtag --prebuilt 3

    23. 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 0 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). Reload the saved configuration by clicking on Browse... and selecting zedboard_leds_switches.txt from ~/projects/zedboard_linux/os/petalinux/src/other.
    #### Part 6 - Revision Control ####

    24. 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/hw-description/drivers
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git add project-spec/meta-user/recipes-apps/website/files/amber.gif
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git add project-spec/meta-user/recipes-apps/website/files/green.gif
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git add project-spec/meta-user/recipes-apps/website/files/red.gif
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git commit -am "Enhanced webpage for website application to have multiple location access into PL address space."
    steve@Desktop:~/projects/zedboard_linux/os/petalinux$ git tag -a v8.0 -m "PetaLinux, Peek/Poke, LED Runner, Webserver, Peek/Poke CGI, PL Access, Style Sheet & Register Bank with XSA from zedboard_leds_switches v2.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/ip_repo
    steve@Desktop:~/projects/zedboard_leds_switches$ git commit -am "Added general purpose AXI register bank."
    steve@Desktop:~/projects/zedboard_leds_switches$ git tag -a v2.0 -m "ZYNQ, GPIO & Register Bank"
    steve@Desktop:~/projects/zedboard_leds_switches$ git push
    #### Part 6 - Quickstart ####

    25. 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 v2.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 v8.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