[bazel] Add SRAM program test rule for CW310
Issue #13968
Signed-off-by: Dan McArdle <dmcardle@google.com>
diff --git a/rules/opentitan_gdb_test.bzl b/rules/opentitan_gdb_test.bzl
new file mode 100644
index 0000000..d843b3a
--- /dev/null
+++ b/rules/opentitan_gdb_test.bzl
@@ -0,0 +1,125 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+load("@lowrisc_opentitan//rules:rv.bzl", "rv_rule")
+
+def _opentitan_gdb_fpga_cw310_test(ctx):
+ test_script = '''#!/usr/bin/env bash
+ set -e
+
+ # Do not mask failures in the left-hand side of pipelines.
+ set -o pipefail
+
+ COLOR_RED='\\x1b[0;31m'
+ COLOR_GREEN='\\x1b[0;32m'
+ COLOR_PURPLE='\\x1b[0;35m'
+
+ function prefix_lines() {{
+ LABEL="$1"
+ COLOR="$2"
+ COLOR_RESET='\\x1b[m'
+ sed -Eu "s/(.*)/$COLOR[$LABEL]$COLOR_RESET \\1/"
+ }}
+
+ set -x
+
+ ./opentitantool load-bitstream --rom-kind=TestRom rom.bit
+
+ (openocd -f /usr/share/openocd/scripts/interface/ftdi/olimex-arm-usb-tiny-h.cfg \\
+ -c "adapter speed 500; transport select jtag; reset_config trst_and_srst" \\
+ -f {openocd_earlgrey_config} \\
+ |& prefix_lines OPENOCD "$COLOR_PURPLE") &
+ OPENOCD_PID=$!
+
+ # For debugging, it may be useful to use `--init-command`, which causes
+ # GDB to drop to the interactive prompt when the script ends rather than
+ # exiting.
+ (/tools/riscv/bin/riscv32-unknown-elf-gdb --command=script.gdb \\
+ |& prefix_lines GDB "$COLOR_GREEN") &
+
+ ./opentitantool console \\
+ --timeout 15s \\
+ --exit-success='{exit_success_pattern}' \\
+ |& prefix_lines CONSOLE "$COLOR_RED"
+
+ # Send TERM signal to OpenOCD and wait for all background jobs to complete. Note
+ # that GDB will exit naturally at the end of its script.
+ kill $OPENOCD_PID
+ wait
+ '''.format(
+ openocd_earlgrey_config = ctx.file._openocd_earlgrey_config.path,
+ exit_success_pattern = ctx.attr.exit_success_pattern,
+ )
+
+ # Write the GDB script to disk and load it with GDB's `--command` argument.
+ # This enables us to separate lines with whitespace, whereas if we piped the
+ # string into GDB's stdin, each newline would cause it to repeat the
+ # previous command.
+ gdb_script_file = ctx.actions.declare_file("{}.gdb".format(ctx.label.name))
+ test_script_file = ctx.actions.declare_file("{}.sh".format(ctx.label.name))
+
+ ctx.actions.write(output = gdb_script_file, content = ctx.attr.gdb_script)
+ ctx.actions.write(output = test_script_file, content = test_script)
+
+ # Construct a dict that we can pass to `ctx.runfiles()`, mapping symlink
+ # names to real file paths.
+ gdb_script_symlinks_flipped = {}
+ for label in ctx.attr.gdb_script_symlinks:
+ label_files = label.files.to_list()
+ if len(label_files) != 1:
+ fail("gdb_script_symlinks labels must have exactly one file, but", label, "has these files:", label_files)
+ gdb_script_symlinks_flipped[ctx.attr.gdb_script_symlinks[label]] = label_files[0]
+
+ gdb_script_runfiles = ctx.runfiles(
+ symlinks = gdb_script_symlinks_flipped,
+ files = gdb_script_symlinks_flipped.values(),
+ )
+ test_script_runfiles = ctx.runfiles(
+ # Relative paths provided by `File.path` seem to not work
+ # for generated files. Symlinking the runtime files wherever
+ # Bazel will run `test_script_file` sidesteps the issue.
+ symlinks = {
+ "opentitantool": ctx.file._opentitantool,
+ "rom.bit": ctx.file.rom_bitstream,
+ "script.gdb": gdb_script_file,
+ },
+ files = [
+ ctx.file._openocd_earlgrey_config,
+ ctx.file._opentitantool,
+ ctx.file.rom_bitstream,
+ gdb_script_file,
+ ],
+ )
+ runfiles = test_script_runfiles.merge(gdb_script_runfiles)
+
+ return [DefaultInfo(
+ executable = test_script_file,
+ runfiles = runfiles,
+ )]
+
+# Orchestrate opentitantool, OpenOCD, and GDB to load the given program into
+# SRAM and execute it in-place. This rule assumes that a CW310 FPGA and an
+# ARM-USB-TINY-H JTAG debugger are attached to the host.
+opentitan_gdb_fpga_cw310_test = rv_rule(
+ implementation = _opentitan_gdb_fpga_cw310_test,
+ attrs = {
+ "exit_success_pattern": attr.string(mandatory = True),
+ "gdb_script": attr.string(mandatory = True),
+ "gdb_script_symlinks": attr.label_keyed_string_dict(allow_files = True),
+ "rom_bitstream": attr.label(
+ mandatory = True,
+ allow_single_file = True,
+ ),
+ "_opentitantool": attr.label(
+ default = "//sw/host/opentitantool",
+ allow_single_file = True,
+ cfg = "exec",
+ ),
+ "_openocd_earlgrey_config": attr.label(
+ default = "//util/openocd/target:lowrisc-earlgrey.cfg",
+ allow_single_file = True,
+ ),
+ },
+ test = True,
+)
diff --git a/sw/device/examples/sram_program/BUILD b/sw/device/examples/sram_program/BUILD
index a2b8754..de13c81 100644
--- a/sw/device/examples/sram_program/BUILD
+++ b/sw/device/examples/sram_program/BUILD
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
load("//rules:opentitan.bzl", "opentitan_ram_binary")
+load("//rules:opentitan_gdb_test.bzl", "opentitan_gdb_fpga_cw310_test")
load("//rules:linker.bzl", "ld_library")
package(default_visibility = ["//visibility:public"])
@@ -31,6 +32,48 @@
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/base:macros",
"//sw/device/lib/runtime:log",
+ "//sw/device/lib/testing:pinmux_testutils",
"//sw/device/lib/testing/test_framework:check",
],
)
+
+opentitan_gdb_fpga_cw310_test(
+ name = "sram_program_fpga_cw310_test",
+ exit_success_pattern = "sram_program\\.c:47\\] PC: 0x1000208c, SRAM: \\[0x10000000, 0x10020000\\)",
+ gdb_script = """
+ target extended-remote :3333
+ set pagination off
+
+ echo :::: Send OpenOCD the 'reset halt' command.\\n
+ monitor reset halt
+
+ echo :::: Set RAM as RWX by executing code between _start and test_rom_main()\\n
+ file test_rom.elf
+ break rom_test_main
+ continue
+ delete breakpoints
+
+ echo :::: Load the SRAM program onto the device and check integrity.\\n
+ file sram_program.elf
+ load sram_program.elf
+ compare-sections
+
+ info registers
+ echo :::: Update registers before calling sram_main().\\n
+ set $sp = _stack_end
+ set $gp = __global_pointer$
+ info registers
+
+ echo :::: Call sram_main().\\n
+ print sram_main()
+
+ echo :::: Done.\\n
+ """,
+ gdb_script_symlinks = {
+ ":sram_program_fpga_cw310.elf": "sram_program.elf",
+ "//sw/device/lib/testing/test_rom:test_rom_fpga_cw310.elf": "test_rom.elf",
+ },
+ # TODO(#13968) Switch to ROM instead of Test ROM.
+ rom_bitstream = "//hw/bitstream:test_rom",
+ tags = ["manual"],
+)
diff --git a/sw/device/examples/sram_program/sram_program.c b/sw/device/examples/sram_program/sram_program.c
index 248682c..28095dd 100644
--- a/sw/device/examples/sram_program/sram_program.c
+++ b/sw/device/examples/sram_program/sram_program.c
@@ -9,11 +9,13 @@
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/runtime/print.h"
+#include "sw/device/lib/testing/pinmux_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
static dif_uart_t uart0;
+static dif_pinmux_t pinmux;
enum {
kSramStart = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR,
@@ -22,6 +24,11 @@
};
void sram_main() {
+ // Configure the pinmux.
+ CHECK_DIF_OK(dif_pinmux_init(
+ mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux));
+ pinmux_testutils_init(&pinmux);
+
// Initialize UART.
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
diff --git a/util/openocd/target/BUILD b/util/openocd/target/BUILD
new file mode 100644
index 0000000..896cbde
--- /dev/null
+++ b/util/openocd/target/BUILD
@@ -0,0 +1,5 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+exports_files(["lowrisc-earlgrey.cfg"])