Add nexus helper functions to build

Change-Id: Icdb3599ea8f0cc4f1ce5b7180a03b5757f725cb9
diff --git a/platforms/nexus/setup.sh b/platforms/nexus/setup.sh
index c6275e5..578b466 100644
--- a/platforms/nexus/setup.sh
+++ b/platforms/nexus/setup.sh
@@ -45,6 +45,11 @@
 # Input for topgen_matcha.py to generate hw configuration
 export TOP_MATCHA_HJSON="${ROOTDIR}/hw/matcha/hw/top_matcha/data/top_matcha.hjson"
 
+# Standard locations of Nexus configs and binaries
+export OT_TOOL="${ROOTDIR}/out/opentitantool"
+export NEXUS_JSON="${ROOTDIR}/out/nexus.json"
+export NEXUS_SPI_PASSTHRU="${ROOTDIR}/out/spi_passthrough_fpga_nexus.bin"
+
 function parting_messages() {
     if [[ ! -d "${RUSTDIR}" ]] ||
        [[ ! -d "${ROOTDIR}/cache/toolchain" ]] ||
@@ -61,13 +66,14 @@
         [[ -d "${ROOTDIR}/cache/toolchain_iree_rv32imf" ]] || echo "${ROOTDIR}/cache/toolchain_iree_rv32imf is missing!"
         [[ -d "${ROOTDIR}/cache/renode" ]] || echo "${ROOTDIR}/cache/renode is missing!"
     fi
+    echo Run \'set-nexus-id NN\' to set your Nexus board ID
 }
 
 function sim_kelvin
 {
     # Run the ELF/Bin program with kelvin_sim
-    local bin_file=$(realpath $1)
-    local magic_bytes=$(xxd -p -l 4 "${bin_file}")
+    local bin_file="$(realpath $1)"
+    local magic_bytes="$(xxd -p -l 4 ${bin_file})"
     local -a flags=()
     local is_elf=false
     if [[ ${magic_bytes} == "7f454c46" ]]; then
@@ -90,7 +96,7 @@
 function sim_kelvin_renode
 {
     # Run the Bin program with renode
-    local bin_file=$(realpath $1)
+    local bin_file="$(realpath $1)"
     local command="start;"
 
     (cd "${ROOTDIR}" && renode -e "\$bin=@${bin_file}; i @sim/config/kelvin.resc; \
@@ -98,3 +104,196 @@
         --disable-xwt --console)
 
 }
+
+function set-nexus-id
+{
+    local nexus_id="${1}"; shift
+
+    if [[ -z "${nexus_id}" ]]; then
+        (
+            echo "Usage: set-nexus-id <NN>"
+            echo
+            echo "Sets the target Nexus board to run on.  Must be a two digit number"
+            echo
+        ) | fmt
+        return 1
+    fi
+    if [[ "${#nexus_id}" -ne 2 ]]; then
+        echo "Nexus number must be two digits"
+        return 1
+    fi
+
+    export NEXUS_ID="${nexus_id}"
+    # TODO(b/316200811) track down all the places we use a different variable name:
+    export NEXUS_BOARD="${nexus_id}"
+    export FPGA_BOARD_ID="${nexus_id}"
+}
+
+function opentitantool
+{
+    # Run built opentitantool with standard flags
+    if [[ -z "${NEXUS_ID}" ]]; then
+        echo "Run set-nexus-id first"
+        return 1
+    fi
+
+    "${OT_TOOL}" --conf "${NEXUS_JSON}" --interface nexus --usb-serial \
+        "Nexus-FTDI-${NEXUS_ID}" "$@"
+}
+
+function nexus_reset
+{
+    # Reset nexus board
+    if [[ -z "${NEXUS_ID}" ]]; then
+        echo "Run set-nexus-id first"
+        return 1
+    fi
+
+    opentitantool gpio write RESET true
+    opentitantool gpio write RESET false
+    opentitantool gpio write RESET true
+}
+
+function nexus_flash
+{
+    # Write tar file to nexus SPI flash and verify
+
+    local flash_tar="$(realpath $1)"
+    if [[ -z "${flash_tar}" ]]; then
+        (
+            echo "Usage: nexus_flash path/to/file.tar"
+            echo
+            echo "Writes tar file to Nexus SPI flash for booting."
+            echo
+        ) | fmt
+        return 1
+    fi
+
+    if [[ -z "${NEXUS_ID}" ]]; then
+        echo "Run set-nexus-id first"
+        return 1
+    fi
+
+    if [[ ! -f "${NEXUS_SPI_PASSTHRU}" ]]; then
+        echo "INFO: spi_passthrough binary not found at ${NEXUS_SPI_PASSTHRU}"
+        echo "running `m spi_passthrough` to build it"
+        m spi_passthrough
+        if [[ "$?" -ne 0 ]]; then
+            echo "ERROR: Unable to find or build spi_passthrough!"
+            return 1
+        fi
+    fi
+
+    nexus_reset
+
+    local read_size="$(du -b ${flash_tar} | awk '{print $1}')"
+    local erase_size="$(du -b ${flash_tar} \
+        | awk '{ print $1 + (n - $1 % n) % n }' n=262144)"
+
+    echo "Program SPI using ${NEXUS_SPI_PASSTHRU} on board ${NEXUS_ID}"
+    opentitantool bootstrap "${NEXUS_SPI_PASSTHRU}"
+    opentitantool spi block-erase --start 0 --length "${erase_size}"
+    opentitantool spi program --start 0 "${flash_tar}"
+
+    local readback_file="$(mktemp -d)/$(basename ${flash_tar}).readback"
+    opentitantool  spi read --start 0 --length "${read_size}" "${readback_file}"
+    diff "${flash_tar}" "${readback_file}"
+    if [[ "$?" -ne 0 ]]; then
+        echo "ERROR: Nexus flash verify failed!"
+        return 1
+    fi
+    rm "${readback_file}"
+
+    nexus_reset
+}
+
+function nexus_clear_flash
+{
+    # Clear the first page of the SPI flash to prevent restarting from the
+    # loaded program at the next reset.
+    if [[ -z "${NEXUS_ID}" ]]; then
+        echo "Run set-nexus-id first"
+        return 1
+    fi
+
+    echo "Clearing nexus SPI flash"
+    nexus_reset
+    opentitantool bootstrap "${NEXUS_SPI_PASSTHRU}"
+    opentitantool spi block-erase --start 0 --length 262144
+}
+
+function nexus_mcu_write() {
+    # Slowly send characters to the Nexus MCU
+    if [[ -z "${NEXUS_ID}" ]]; then
+        echo "Run set-nexus-id first"
+        return 1
+    fi
+
+    local string="$1"
+    local length="${#string}"
+    for ((i = 0; i < length; i++)); do
+        local char="${string:i:1}"
+        echo -n "$char" > "/dev/Nexus-FTDI-${NEXUS_ID}-MCU-UART"
+        sleep 0.5
+    done
+    echo -e "\r" > "/dev/Nexus-FTDI-${NEXUS_ID}-MCU-UART"
+}
+
+function nexus_load_bitstream
+{
+    # Load bitstream file onto Nexus FPGA
+    local bitstream_path="$(realpath 1)"
+    if [[ -z "${bitstream_path}" ]]; then
+        (
+            echo "Usage: nexus_load_bitstream path/to/file.bit"
+            echo
+            echo "Loads bitstream file onto Nexus FPGA"
+            echo "This command may ask you for the Nexus SOM root password"
+            echo
+        ) | fmt
+        return 1
+    fi
+
+    if [[ -z "${NEXUS_ID}" ]]; then
+        echo "Run set-nexus-id first"
+        return 1
+    fi
+
+    if [[ ! -f "${bitstream_path}" ]]; then
+        echo "ERROR: bitstream not found at ${bitstream_path}"
+        return 1
+    fi
+
+    # Issue a no-op command to flush the character buffer in case it isn't empty
+    nexus_mcu_write "help"
+    scp "${bitstream_path}" "root@nexus${NEXUS_ID}:/mnt/mmcp1/"
+    nexus_mcu_write "camera_powerdown"
+    sleep 5
+    local bitstream_name="$(basename ${bitstream_path})"
+    ssh "root@nexus${NEXUS_ID}" "/mnt/mmcp1/zturn -d a /mnt/mmcp1/${bitstream_name}"
+    nexus_mcu_write "camera_powerup"
+}
+
+function nexus_boot
+{
+    # Boot the Nexus board.  If flashed, will boot from flash
+    # This command is a synomym for `opentitantool boot`
+    opentitantool boot
+}
+
+function nexus_bootstrap
+{
+    # Bootstrap a binary on the Nexus board
+
+    local binary="$(realpath $1)"
+    if [[ -z "${binary}" ]]; then
+        (
+            echo "Usage: nexus_flash path/to/file.bin"
+            echo
+            echo "Run a binary on the nexus board."
+            echo
+        ) | fmt
+        return 1
+    fi
+    opentitantool bootstrap "${binary}"
+}