[dv] Add ROM-only chip-level test example
A few chip-level tests (e.g., `chip_sw_flash_init`) must run and
complete entirely in the ROM stage. This means that the conventional
mechanism for running chip-level tests at the flash (ROM_EXT) stage is
not suitable for such tests.
This commit overcomes this by adding a mechanism to build a chip-level
test to be run in ROM using the meson build system. (Conveniently, the
`opentitan_functest` rule already provides such a mechanism using the
`test_in_rom` rule option.) Additionally, this commit updates the DV
testbench to skip loading of a flash image (either via bootstrap or via
the backdoor mem util mechanism) if no such flash image is provided at
runtime. Lastly, an example ROM-stage chip-level test is added to verify
the correctness of the added infrastructure, and to provide a reference
point for future chip-level test development.
Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index 2c78574..d8268cd 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -2652,6 +2652,20 @@
// System level scenarios //
////////////////////////////
{
+ name: chip_sw_example_test_from_rom_or_flash
+ desc: '''Run examples tests developed for each boot stage.
+
+ Our test infrastructure defaults to running tests out flash, at the
+ ROM_EXT stage, but also supports running tests in the ROM stage. We
+ develop example tests to demonstrate these capabilities, and need to
+ run them in DV to ensure the integrity of our infrastructure.
+ '''
+ milestone: V1
+ tests: ["chip_sw_example_flash",
+ "chip_sw_example_rom",
+ ]
+ }
+ {
name: chip_sw_smoketest
desc: '''Run smoke tests developed for each IP.
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 47a62661..8f3fef6 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -206,6 +206,18 @@
// This allows an arbitrary number of SW images to be supplied to the TB.
tests: [
{
+ name: chip_sw_example_flash
+ uvm_test_seq: chip_sw_base_vseq
+ sw_images: ["sw/device/tests/example_test_from_flash:1"]
+ en_run_modes: ["sw_test_mode_test_rom"]
+ }
+ {
+ name: chip_sw_example_rom
+ uvm_test_seq: chip_sw_base_vseq
+ sw_images: ["sw/device/tests/example_test_from_rom:0"]
+ en_run_modes: ["sw_test_mode_common"]
+ }
+ {
name: chip_sw_sleep_pwm_pulses
uvm_test_seq: chip_sw_pwm_pulses_vseq
sw_images: ["sw/device/tests/sleep_pwm_pulses_test:1"]
@@ -303,7 +315,7 @@
uvm_test_seq: chip_sw_base_vseq
sw_images: ["sw/device/tests/flash_ctrl_idle_low_power_test:1"]
en_run_modes: ["sw_test_mode_test_rom"]
- }
+ }
{
name: chip_sw_lc_ctrl_otp_hw_cfg
uvm_test_seq: chip_sw_base_vseq
diff --git a/hw/top_earlgrey/dv/env/chip_env_cfg.sv b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
index 7553b24..6e26460 100644
--- a/hw/top_earlgrey/dv/env/chip_env_cfg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
@@ -128,11 +128,6 @@
m_pwm_monitor_cfg[i].is_active = 0;
end
- // By default, assume SW images in PWD with these generic names.
- sw_images[SwTypeRom] = "./rom";
- sw_images[SwTypeTest] = "./sw";
- sw_images[SwTypeOtbn] = "./otbn";
-
// By default, assume these OTP image paths.
otp_images[lc_ctrl_state_pkg::LcStRaw] = "otp_ctrl_img_raw.vmem";
otp_images[lc_ctrl_state_pkg::LcStDev] = "otp_ctrl_img_dev.vmem";
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
index eeead99..70aeaba 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
@@ -66,12 +66,14 @@
cfg.mem_bkdr_util_h[Rom].load_mem_from_file({cfg.sw_images[SwTypeRom], ".scr.39.vmem"});
// TODO: the location of the main execution image should be randomized to either bank in future.
- if (cfg.use_spi_load_bootstrap) begin
- `uvm_info(`gfn, "Initializing SPI flash bootstrap", UVM_MEDIUM)
- spi_device_load_bootstrap({cfg.sw_images[SwTypeTest], ".frames.vmem"});
- end else begin
- cfg.mem_bkdr_util_h[FlashBank0Data].load_mem_from_file(
- {cfg.sw_images[SwTypeTest], ".64.scr.vmem"});
+ if (cfg.sw_images.exists(SwTypeTest)) begin
+ if (cfg.use_spi_load_bootstrap) begin
+ `uvm_info(`gfn, "Initializing SPI flash bootstrap", UVM_MEDIUM)
+ spi_device_load_bootstrap({cfg.sw_images[SwTypeTest], ".frames.vmem"});
+ end else begin
+ cfg.mem_bkdr_util_h[FlashBank0Data].load_mem_from_file(
+ {cfg.sw_images[SwTypeTest], ".64.scr.vmem"});
+ end
end
cfg.sw_test_status_vif.sw_test_status = SwTestStatusBooted;
diff --git a/sw/device/tests/example_chip_level_test.c b/sw/device/tests/example_test_from_flash.c
similarity index 100%
rename from sw/device/tests/example_chip_level_test.c
rename to sw/device/tests/example_test_from_flash.c
diff --git a/sw/device/tests/example_test_from_rom.c b/sw/device/tests/example_test_from_rom.c
new file mode 100644
index 0000000..3a540f4
--- /dev/null
+++ b/sw/device/tests/example_test_from_rom.c
@@ -0,0 +1,56 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/arch/device.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_uart.h"
+#include "sw/device/lib/pinmux.h"
+#include "sw/device/lib/runtime/hart.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/runtime/print.h"
+#include "sw/device/lib/testing/check.h"
+#include "sw/device/lib/testing/test_framework/test_status.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
+
+static dif_uart_t uart0;
+
+void _boot_start(void) {
+ // We need to set the test status as "in test" to indicate to the test code
+ // has been reached, even though this test is also in the "boot ROM".
+ test_status_set(kTestStatusInTest);
+ pinmux_init();
+
+ // We need to initialize the UART regardless if we LOG any messages, since
+ // Verilator and FPGA platforms use the UART to communicate the test results.
+ if (kDeviceType != kDeviceSimDV) {
+ CHECK_DIF_OK(dif_uart_init(
+ mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
+ CHECK_DIF_OK(
+ dif_uart_configure(&uart0, (dif_uart_config_t){
+ .baudrate = kUartBaudrate,
+ .clk_freq_hz = kClockFreqPeripheralHz,
+ .parity_enable = kDifToggleDisabled,
+ .parity = kDifUartParityEven,
+ }));
+ base_uart_stdout(&uart0);
+ }
+
+ bool result = false;
+
+ /**
+ * Place test code here.
+ */
+
+ /**
+ * Set test result to true if the test succeeds. Otherwise, set to false.
+ */
+ result = true;
+
+ // Report test status.
+ test_status_set(result ? kTestStatusPassed : kTestStatusFailed);
+
+ // Unreachable.
+ abort();
+}
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index cd5613b..99e05a8 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -19,35 +19,63 @@
# after the OTTF initialization assembly (`ottf_start.S`) runs.
sw_tests = {
# 'test_name': {
- # 'library': test_lib,
- # 'dv_frames': true/false, # (can be omitted, defaults to `false`)
- # 'sign': true/false, # (can be omitted, defaults to `false`)
- # 'ottf_start_only': true/false, # (can be omitted, defaults to `false`)
+ # 'library': test_lib,
+ # 'dv_frames': true/false, # (can be omitted, defaults to `false`)
+ # 'sign': true/false, # (can be omitted, defaults to `false`)
+ # 'ottf_start_only': true/false, # (can be omitted, defaults to `false`)
+ # 'run_at_rom_stage': true/false, # (can be omitted, defaults to `false`)
# },
}
###############################################################################
-# Example Chip-Level Test
+# Example Chip-Level Tests
###############################################################################
-example_chip_level_test_lib = declare_dependency(
+# Most chip-level tests run at the flash stage. These can rely on the OTTF to
+# provide common HW initialization (e.g., pinmux, UART, flash, IRQ vector).
+example_test_from_flash_lib = declare_dependency(
link_with: static_library(
- 'example_chip_level_test_lib',
- sources: ['example_chip_level_test.c'],
+ 'example_test_from_flash_lib ',
+ sources: ['example_test_from_flash.c'],
dependencies: [
# Add dependencies here.
],
),
)
sw_tests += {
- 'example_chip_level_test': {
- 'library': example_chip_level_test_lib,
+ 'example_test_from_flash': {
+ 'library': example_test_from_flash_lib,
+ }
+}
+
+# A few chip-level tests need to run at the ROM stage. These may not rely on the
+# OTTF, and therefore must do all required initializations within the test.
+example_test_from_rom_lib = declare_dependency(
+ link_with: static_library(
+ 'example_test_from_rom_lib',
+ sources: [
+ 'example_test_from_rom.c',
+ ],
+ dependencies: [
+ sw_lib_dif_uart,
+ sw_lib_mmio,
+ sw_lib_pinmux,
+ sw_lib_runtime_hart,
+ sw_lib_runtime_print,
+ sw_lib_runtime_log,
+ sw_lib_testing_test_status,
+ ],
+ ),
+)
+sw_tests += {
+ 'example_test_from_rom': {
+ 'library': example_test_from_rom_lib,
+ 'run_at_rom_stage': true,
}
}
###############################################################################
# Cryptolib Tests
###############################################################################
-
subdir('crypto')
###############################################################################
@@ -1066,49 +1094,76 @@
targets_to_export = []
shared_test_deps = [device_lib]
- # unsigned programs loaded with test ROM
- if 'ottf_start_only' in sw_test_info and sw_test_info['ottf_start_only']
- # Explicitly ONLY pull in the OTTF startup library since these tests need
- # to run right after ottf_start.S is done executing. Additionally, the
- # startup library contains default OTTF ISRs. While these tests may not
- # override any of the default ISR symbols, they should be linked in since
- # the `mtvec` is set to point to these in the `ottf_start.S`
- # initialization assembly (contained in the ottf_start_lib target below).
- shared_test_deps += [
- ottf_start_lib,
- ]
- else
- shared_test_deps += [
- ottf_lib,
- ]
- endif
-
- if (get_option('closed_source_dir') != '') and \
- 'link_with_closed_configs' in sw_test_info and \
- sw_test_info['link_with_closed_configs']
+ if 'run_at_rom_stage' in sw_test_info and sw_test_info['run_at_rom_stage']
+
+ # Build the test ELF for ROM.
sw_test_elf = executable(
sw_test_name + '_' + device_name,
name_suffix: 'elf',
- # Need to force the linker to examine (strong) symbols that may have a
- # weak implementation in the (static) test library.
- link_whole: closed_source_config_hooks_lib,
+ link_args: test_rom_link_args,
+ link_depends: test_rom_link_deps,
+ sources: [
+ hw_ip_ast_reg_h,
+ hw_ip_clkmgr_reg_h,
+ hw_ip_csrng_reg_h,
+ hw_ip_edn_reg_h,
+ hw_ip_entropy_src_reg_h,
+ hw_ip_otp_ctrl_reg_h,
+ hw_ip_sram_ctrl_reg_h,
+ hw_ip_sensor_ctrl_reg_h,
+ meson.project_source_root() / 'sw/device/lib/testing/test_rom/test_rom_start.S',
+ ],
dependencies: [
shared_test_deps,
+ sw_lib_crt,
sw_test_info['library'],
- closed_source_config_hooks_dep,
],
)
else
- sw_test_elf = executable(
- sw_test_name + '_' + device_name,
- name_suffix: 'elf',
- dependencies: [
- shared_test_deps,
- sw_test_info['library'],
- ],
- )
+ if 'ottf_start_only' in sw_test_info and sw_test_info['ottf_start_only']
+ # Explicitly ONLY pull in the OTTF startup library since these tests need
+ # to run right after ottf_start.S is done executing. Additionally, the
+ # startup library contains default OTTF ISRs. While these tests may not
+ # override any of the default ISR symbols, they should be linked in since
+ # the `mtvec` is set to point to these in the `ottf_start.S`
+ # initialization assembly (contained in the ottf_start_lib target below).
+ shared_test_deps += [
+ ottf_start_lib,
+ ]
+ else
+ shared_test_deps += [
+ ottf_lib,
+ ]
+ endif
+
+ # Build the test ELF for flash.
+ if (get_option('closed_source_dir') != '') and \
+ 'link_with_closed_configs' in sw_test_info and \
+ sw_test_info['link_with_closed_configs']
+ sw_test_elf = executable(
+ sw_test_name + '_' + device_name,
+ name_suffix: 'elf',
+ # Need to force the linker to examine (strong) symbols that may have a
+ # weak implementation in the (static) test library.
+ link_whole: closed_source_config_hooks_lib,
+ dependencies: [
+ shared_test_deps,
+ sw_test_info['library'],
+ closed_source_config_hooks_dep,
+ ],
+ )
+ else
+ sw_test_elf = executable(
+ sw_test_name + '_' + device_name,
+ name_suffix: 'elf',
+ dependencies: [
+ shared_test_deps,
+ sw_test_info['library'],
+ ],
+ )
+ endif
endif
-
+
target_name = sw_test_name + '_@0@_' + device_name
sw_test_dis = custom_target(
@@ -1123,116 +1178,136 @@
kwargs: elf_to_bin_custom_target_args,
)
- sw_test_vmem64 = custom_target(
- target_name.format('vmem64'),
- input: sw_test_bin,
- kwargs: bin_to_vmem64_custom_target_args,
- )
-
- sw_test_scr_vmem64 = custom_target(
- target_name.format('scrambled'),
- input: sw_test_vmem64,
- output: flash_image_outputs,
- command: flash_image_command,
- depend_files: flash_image_depend_files,
- build_by_default: true,
- )
-
targets_to_export += [
sw_test_elf,
sw_test_dis,
sw_test_bin,
- sw_test_vmem64,
- sw_test_scr_vmem64,
]
- # signed programs loaded with mask ROM
- if 'sign' in sw_test_info and sw_test_info['sign']
- foreach key_name, key_info : signing_keys
- signed_target_name = '_'.join([
- 'signed',
- sw_test_name,
- key_name,
- '@0@',
- device_name,
- ])
-
- sw_test_signed_bin = custom_target(
- signed_target_name.format('bin'),
- input: sw_test_bin,
- output: '@BASENAME@.@0@.signed.bin'.format(key_name),
- command: [
- rom_ext_signer_export.full_path(),
- 'rom_ext',
- '@INPUT@',
- key_info['path'],
- sw_test_elf.full_path(),
- '@OUTPUT@',
- ],
- depends: rom_ext_signer_export,
- build_by_default: true,
- )
-
- sw_test_signed_vmem64 = custom_target(
- signed_target_name.format('vmem64'),
- input: sw_test_signed_bin,
- kwargs: bin_to_vmem64_custom_target_args,
- )
-
- sw_test_signed_scr_vmem64 = custom_target(
- signed_target_name.format('scrambled'),
- input: sw_test_signed_vmem64,
- output: flash_image_outputs,
- command: flash_image_command,
- depend_files: flash_image_depend_files,
- build_by_default: true,
- )
-
- targets_to_export += [
- sw_test_signed_bin,
- sw_test_signed_vmem64,
- sw_test_signed_scr_vmem64,
- ]
- endforeach
- endif
-
- sw_test_sim_dv_frames = []
- if device_name == 'sim_dv' and \
- sw_test_info.has_key('dv_frames') and sw_test_info['dv_frames']
- sw_test_sim_dv_frames_bin = custom_target(
- sw_test_name + '_sim_dv_frames_bin',
- command: [
- spiflash_bin,
- '--input=@INPUT@',
- '--dump-frames=@OUTPUT@',
- ],
- input: sw_test_bin,
- output: '@BASENAME@.frames.bin',
- )
-
- sw_test_sim_dv_frames_vmem = custom_target(
- sw_test_name + '_sim_dv_frames_vmem',
- command: [
- prog_srec_cat,
- '@INPUT@',
- '--binary',
- '--offset', '0x0',
- '--byte-swap', '4',
- '--fill', '0xff',
- '-within', '@INPUT@',
- '-binary',
- '-range-pad', '4',
- '--output', '@OUTPUT@',
- '--vmem',
- ],
- input: sw_test_sim_dv_frames_bin,
- output: '@BASENAME@.vmem',
+ if 'run_at_rom_stage' in sw_test_info and sw_test_info['run_at_rom_stage']
+ # Test image is destined for ROM.
+ sw_test_scr_vmem32 = custom_target(
+ target_name.format('scr_vmem32'),
+ command: scramble_image_command,
+ depend_files: scramble_image_depend_files,
+ input: sw_test_elf,
+ output: scramble_image_outputs,
+ build_by_default: true,
)
targets_to_export += [
- sw_test_sim_dv_frames_bin,
- sw_test_sim_dv_frames_vmem,
+ sw_test_scr_vmem32,
]
+ else
+ # Unsigned test image destined for flash, loaded with the test ROM.
+ sw_test_vmem64 = custom_target(
+ target_name.format('vmem64'),
+ input: sw_test_bin,
+ kwargs: bin_to_vmem64_custom_target_args,
+ )
+
+ sw_test_scr_vmem64 = custom_target(
+ target_name.format('scr_vmem64'),
+ input: sw_test_vmem64,
+ output: flash_image_outputs,
+ command: flash_image_command,
+ depend_files: flash_image_depend_files,
+ build_by_default: true,
+ )
+
+ targets_to_export += [
+ sw_test_vmem64,
+ sw_test_scr_vmem64,
+ ]
+
+ # Signed test image destined for flash, loaded with the mask ROM.
+ if 'sign' in sw_test_info and sw_test_info['sign']
+ foreach key_name, key_info : signing_keys
+ signed_target_name = '_'.join([
+ 'signed',
+ sw_test_name,
+ key_name,
+ '@0@',
+ device_name,
+ ])
+
+ sw_test_signed_bin = custom_target(
+ signed_target_name.format('bin'),
+ input: sw_test_bin,
+ output: '@BASENAME@.@0@.signed.bin'.format(key_name),
+ command: [
+ rom_ext_signer_export.full_path(),
+ 'rom_ext',
+ '@INPUT@',
+ key_info['path'],
+ sw_test_elf.full_path(),
+ '@OUTPUT@',
+ ],
+ depends: rom_ext_signer_export,
+ build_by_default: true,
+ )
+
+ sw_test_signed_vmem64 = custom_target(
+ signed_target_name.format('vmem64'),
+ input: sw_test_signed_bin,
+ kwargs: bin_to_vmem64_custom_target_args,
+ )
+
+ sw_test_signed_scr_vmem64 = custom_target(
+ signed_target_name.format('scrambled'),
+ input: sw_test_signed_vmem64,
+ output: flash_image_outputs,
+ command: flash_image_command,
+ depend_files: flash_image_depend_files,
+ build_by_default: true,
+ )
+
+ targets_to_export += [
+ sw_test_signed_bin,
+ sw_test_signed_vmem64,
+ sw_test_signed_scr_vmem64,
+ ]
+ endforeach
+ endif
+
+ # DV sim bootstrap image (for loading flash images via bootstrap).
+ if device_name == 'sim_dv' and \
+ sw_test_info.has_key('dv_frames') and sw_test_info['dv_frames']
+ sw_test_sim_dv_frames_bin = custom_target(
+ sw_test_name + '_sim_dv_frames_bin',
+ command: [
+ spiflash_bin,
+ '--input=@INPUT@',
+ '--dump-frames=@OUTPUT@',
+ ],
+ input: sw_test_bin,
+ output: '@BASENAME@.frames.bin',
+ )
+
+ sw_test_sim_dv_frames_vmem = custom_target(
+ sw_test_name + '_sim_dv_frames_vmem',
+ command: [
+ prog_srec_cat,
+ '@INPUT@',
+ '--binary',
+ '--offset', '0x0',
+ '--byte-swap', '4',
+ '--fill', '0xff',
+ '-within', '@INPUT@',
+ '-binary',
+ '-range-pad', '4',
+ '--output', '@OUTPUT@',
+ '--vmem',
+ ],
+ input: sw_test_sim_dv_frames_bin,
+ output: '@BASENAME@.vmem',
+ )
+
+ targets_to_export += [
+ sw_test_sim_dv_frames_bin,
+ sw_test_sim_dv_frames_vmem,
+ ]
+ endif
endif
if device_name == 'sim_dv'