Merge "Add clock gating tests for video/audio clocks"
diff --git a/hw/bitstream/vivado/BUILD b/hw/bitstream/vivado/BUILD
index acc80f9..b192368 100644
--- a/hw/bitstream/vivado/BUILD
+++ b/hw/bitstream/vivado/BUILD
@@ -121,11 +121,30 @@
tags = ["manual"],
)
+bitstream_splice(
+ name = "fpga_nexus_mask_test_rom_no_otp",
+ testonly = True,
+ src = ":fpga_nexus_test_rom",
+ data = "//sw/device/lib/testing/test_rom:test_rom_no_otp_fpga_nexus_scr_vmem",
+ meminfo = ":rom_mmi",
+ tags = ["manual"],
+)
+
+bitstream_splice(
+ name = "fpga_nexus_mask_test_rom_no_otp_empty",
+ testonly = True,
+ src = ":fpga_nexus_mask_test_rom_no_otp",
+ data = "//hw/top_matcha/data:otp_img_empty",
+ meminfo = ":otp_mmi",
+ tags = ["manual"],
+)
+
pkg_tar(
name = "fpga_nexus_smoketest",
testonly = True,
srcs = [
":fpga_nexus_mask_test_rom",
+ ":fpga_nexus_mask_test_rom_no_otp_empty",
":logs",
":otp_mmi",
":rom_mmi",
diff --git a/hw/top_matcha/data/BUILD b/hw/top_matcha/data/BUILD
index 6fd346d..ee7aa69 100644
--- a/hw/top_matcha/data/BUILD
+++ b/hw/top_matcha/data/BUILD
@@ -2,7 +2,7 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
-load("@lowrisc_opentitan//rules:otp.bzl", "otp_image")
+load("@lowrisc_opentitan//rules:otp.bzl", "otp_image", "otp_json", "otp_partition")
package(default_visibility = ["//visibility:public"])
@@ -30,3 +30,199 @@
"@lowrisc_opentitan//hw/ip/otp_ctrl/data:otp_json_hw_cfg",
],
)
+
+################
+
+otp_json(
+ name = "otp_json_empty",
+ partitions = [
+ otp_partition(
+ name = "SECRET0",
+ items = {
+ "TEST_UNLOCK_TOKEN": "0",
+ "TEST_EXIT_TOKEN": "0",
+ },
+ lock = False,
+ ),
+ otp_partition(
+ name = "SECRET1",
+ items = {
+ "FLASH_ADDR_KEY_SEED": "0",
+ "FLASH_DATA_KEY_SEED": "0",
+ "SRAM_DATA_KEY_SEED": "0",
+ },
+ lock = False,
+ ),
+ otp_partition(
+ name = "SECRET2",
+ items = {
+ "RMA_TOKEN": "0",
+ "CREATOR_ROOT_KEY_SHARE0": "0",
+ "CREATOR_ROOT_KEY_SHARE1": "0",
+ },
+ lock = False,
+ ),
+ otp_partition(
+ name = "LIFE_CYCLE",
+ count = 8,
+ # Should we set this to TEST_UNLOCKED0?
+ state = "TEST_UNLOCKED0",
+ ),
+ ],
+ seed = "0",
+)
+
+otp_json(
+ name = "otp_json_creator_sw_cfg",
+ partitions = [
+ otp_partition(
+ name = "CREATOR_SW_CFG",
+ items = {
+ "CREATOR_SW_CFG_DIGEST": "0x0",
+ # Use software mod_exp implementation for signature
+ # verification. See the definition of `hardened_bool_t` in
+ # sw/device/lib/base/hardened.h.
+ "CREATOR_SW_CFG_SIGVERIFY_RSA_MOD_EXP_IBEX_EN": "0",
+ # Mark the first three keys as valid and remaining as invalid
+ # since we currently have only three keys. See the definition of
+ # `hardened_byte_bool_t` in sw/device/lib/base/hardened.h.
+ "CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN": "0",
+ # Enable use of entropy for countermeasures. See the definition
+ # of `hardened_bool_t` in sw/device/lib/base/hardened.h.
+ "CREATOR_SW_CFG_RNG_EN": "0",
+ # ROM execution is enabled if this item is set to a non-zero
+ # value.
+ "CREATOR_SW_CFG_ROM_EXEC_EN": "0",
+ # Value to write to the cpuctrl CSR in `rom_init()`.
+ # See:
+ # https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html#cpu-control-register-cpuctrl
+ "CREATOR_SW_CFG_CPUCTRL": "0",
+ "CREATOR_SW_CFG_JITTER_EN": "0",
+ # Value of the min_security_version_rom_ext field of the
+ # default boot data.
+ "CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT": "0x0",
+ # Value of the min_security_version_bl0 field of the default
+ # boot data.
+ "CREATOR_SW_CFG_MIN_SEC_VER_BL0": "0x0",
+ # Enable the default boot data in PROD and PROD_END life cycle
+ # states. See the definition of `hardened_bool_t` in
+ # sw/device/lib/base/hardened.h.
+ "CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN": "0",
+ # Enable AST initialization.
+ "CREATOR_SW_CFG_AST_INIT_EN": "0",
+ # TODO: This enables a busyloop in the ROM to give time to
+ # trigger an RMA lifecycle transition via JTAG. The current
+ # value of 10 cycles is useful for test code which verifies
+ # the path through the ROM. This value is not useful for a
+ # real chip.
+ "CREATOR_SW_CFG_RMA_SPIN_EN": "0",
+ "CREATOR_SW_CFG_RMA_SPIN_CYCLES": "0",
+ # Entropy source health check default values. This needs to be
+ # populated when `CREATOR_SW_CFG_RNG_EN` is set to true.
+ "CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS": "0",
+ "CREATOR_SW_CFG_RNG_ALERT_THRESHOLD": "0",
+ "CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST": "0",
+ },
+ ),
+ ],
+)
+
+otp_json(
+ name = "otp_json_owner_sw_cfg",
+ partitions = [
+ otp_partition(
+ name = "OWNER_SW_CFG",
+ items = {
+ "OWNER_SW_CFG_DIGEST": "0x0",
+ # Enable bootstrap. See `hardened_bool_t` in
+ # sw/device/lib/base/hardened.h.
+ "OWNER_SW_CFG_ROM_BOOTSTRAP_EN": "0",
+ # Set to 0x739 to use the ROM_EXT hash measurement as the key
+ # manager attestation binding value.
+ "OWNER_SW_CFG_ROM_KEYMGR_ROM_EXT_MEAS_EN": "0x0",
+ # Report errors without any redaction.
+ "OWNER_SW_CFG_ROM_ERROR_REPORTING": "0",
+ # Set the enables to kAlertEnableNone.
+ # See `alert_enable_t` in
+ # sw/device/silicon_creator/lib/drivers/alert.h
+ "OWNER_SW_CFG_ROM_ALERT_CLASS_EN": "0",
+ # Set the esclation policies to kAlertEscalateNone.
+ # See `alert_escalate_t` in
+ # sw/device/silicon_creator/lib/drivers/alert.h
+ "OWNER_SW_CFG_ROM_ALERT_ESCALATION": "0",
+ # Set the classifiactions to kAlertClassX.
+ # See `alert_class_t` in
+ # sw/device/silicon_creator/lib/drivers/alert.h
+ "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION": ["0"] * 80,
+ # Set the classifiactions to kAlertClassX. See `alert_class_t` in
+ # sw/device/silicon_creator/lib/drivers/alert.h
+ "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION": ["0"] * 16,
+ # Set the alert accumulation thresholds to 0 per class.
+ "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH": ["0x00000000"] * 4,
+ # Set the alert timeout cycles to 0 per class.
+ "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES": ["0x00000000"] * 4,
+ # Set the alert phase cycles to 0,10,10,0xFFFFFFFF for classes
+ # A and B, and to all zeros for classes C and D.
+ "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES": [
+ "0x0",
+ "0",
+ "0",
+ "0",
+ "0x0",
+ "0",
+ "0",
+ "0",
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0",
+ ],
+ },
+ ),
+ ],
+)
+
+otp_json(
+ name = "otp_json_hw_cfg",
+ partitions = [
+ otp_partition(
+ name = "HW_CFG",
+ items = {
+ "DEVICE_ID": "0",
+ # Cryptolib and chip-level tests require access to the CSRNG
+ # software interfaces.
+ "EN_CSRNG_SW_APP_READ": False,
+ "EN_SRAM_IFETCH": False,
+ # Cryptolib and chip-level tests require access to the
+ # entropy_src FW data interface.
+ "EN_ENTROPY_SRC_FW_READ": False,
+ # Cryptolib and chip-level tests require access to the
+ # entropy_src FW override interface.
+ "EN_ENTROPY_SRC_FW_OVER": False,
+ },
+ lock = False,
+ ),
+ ],
+)
+
+otp_image(
+ name = "otp_img_empty",
+ src = ":otp_json_empty",
+ overlays = [
+ ":otp_json_creator_sw_cfg",
+ ":otp_json_owner_sw_cfg",
+ ":otp_json_hw_cfg",
+ ],
+)
diff --git a/hw/top_matcha/data/clocks_nexus.xdc b/hw/top_matcha/data/clocks_nexus.xdc
index 74cbd85..8763a73 100644
--- a/hw/top_matcha/data/clocks_nexus.xdc
+++ b/hw/top_matcha/data/clocks_nexus.xdc
@@ -2,7 +2,7 @@
## Licensed under the Apache License, Version 2.0, see LICENSE for details.
## SPDX-License-Identifier: Apache-2.0
-## Clock Signal
+## Clock Signal - 100M >> 1/100e6 = 1e-8s = 10ns
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports IO_CLK]
## Rename MMCM outputs for less bug-prone parsing.
@@ -11,6 +11,11 @@
create_generated_clock -name clk_usb_48 [get_pin clkgen/pll/CLKOUT1]
create_generated_clock -name clk_aon [get_pin clkgen/pll/CLKOUT4]
+# Store clock periods in variables
+set clk_main_period [get_property PERIOD [get_clocks clk_main]]
+set clk_usb_48_period [get_property PERIOD [get_clocks clk_usb_48]]
+set clk_aon_period [get_property PERIOD [get_clocks clk_aon]]
+
## Clock Domain Crossings
set clks_10_unbuf [get_clocks -of_objects [get_pin clkgen/pll/CLKOUT0]]
set clks_48_unbuf [get_clocks -of_objects [get_pin clkgen/pll/CLKOUT1]]
@@ -42,15 +47,15 @@
] \
]
-## JTAG clocks
+## JTAG clocks TODO - clock frequency
create_clock -add -name lc_jtag_tck -period 200.00 -waveform {0 5} [get_pin top_*/u_pinmux_aon/u_pinmux_strap_sampling/u_pinmux_jtag_buf_lc/prim_clock_buf_tck/gen_xilinx.u_impl_xilinx/gen_fpga_buf.gen_bufg.bufg_i/O]
create_clock -add -name rv_jtag_tck -period 200.00 -waveform {0 5} [get_pin top_*/u_pinmux_aon/u_pinmux_strap_sampling/u_pinmux_jtag_buf_rv/prim_clock_buf_tck/gen_xilinx.u_impl_xilinx/gen_fpga_buf.gen_bufg.bufg_i/O]
## SPI clocks
set spi_dev_data [get_ports {SPI_DEV_D0 SPI_DEV_D1 SPI_DEV_D2 SPI_DEV_D3}]
-create_clock -add -name clk_spi -period 100.00 -waveform {0 50} [get_ports SPI_DEV_CLK]
-create_clock -add -name clk_cs -period 1000.00 -waveform {0 50} [get_ports SPI_DEV_CS_L]
-set_input_delay -clock clk_spi 5 ${spi_dev_data} -add_delay
+create_clock -add -name clk_spi -period $clk_main_period -waveform {0 50} [get_ports SPI_DEV_CLK]
+create_clock -add -name clk_cs -period [expr $clk_main_period * 10] -waveform {0 50} [get_ports SPI_DEV_CS_L]
+set_input_delay -clock clk_spi 5 ${spi_dev_data} -add_delay
set_output_delay -clock clk_spi 5 ${spi_dev_data} -add_delay
# set_input_delay -clock clk_spi -clock_fall -min ${spi_dev_in_delay_min} ${spi_dev_data} -add_delay
# set_input_delay -clock clk_spi -clock_fall -max ${spi_dev_in_delay_max} ${spi_dev_data} -add_delay
diff --git a/hw/top_matcha/dv/chip_sim_cfg.hjson b/hw/top_matcha/dv/chip_sim_cfg.hjson
index 227194e..e9ced65 100644
--- a/hw/top_matcha/dv/chip_sim_cfg.hjson
+++ b/hw/top_matcha/dv/chip_sim_cfg.hjson
@@ -2091,6 +2091,7 @@
"chip_sw_sysrst_ctrl_inputs",
"chip_sw_uart_smoketest",
"chip_sw_uart_tx_rx",
+ "chip_tap_straps_rma",
]
}
{
diff --git a/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c b/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c
index fcc9184..026607f 100644
--- a/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c
+++ b/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c
@@ -6,13 +6,15 @@
#include "sw/device/lib/testing/test_framework/check.h"
void load_smc(void) {
- CHECK_DIF_OK(
- load_file_from_tar("smc.bin", (void*)TOP_MATCHA_RAM_SMC_BASE_ADDR));
+ CHECK_DIF_OK(load_file_from_tar(
+ "smc.bin", (void*)TOP_MATCHA_RAM_SMC_BASE_ADDR,
+ (TOP_MATCHA_RAM_SMC_BASE_ADDR + TOP_MATCHA_RAM_SMC_SIZE_BYTES)));
}
void load_kelvin(void) {
- CHECK_DIF_OK(load_file_from_tar("kelvin.bin",
- (void*)TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR));
+ CHECK_DIF_OK(load_file_from_tar(
+ "kelvin.bin", (void*)TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR,
+ (TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR + TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES)));
}
void load_init(void) { spi_flash_init(); }
diff --git a/sw/device/lib/spi_flash.c b/sw/device/lib/spi_flash.c
index 5d1e2be..0e8932e 100644
--- a/sw/device/lib/spi_flash.c
+++ b/sw/device/lib/spi_flash.c
@@ -58,34 +58,47 @@
return size;
}
-int contains(const void* src, const char* str2) {
- const char* str1 = (const char*)src;
- const char* cursor = str2;
- while (*str1 && *cursor) {
- cursor = (*str1 == *cursor) ? cursor + 1 : str2;
- str1++;
+size_t str_size(const char* str) {
+ size_t size = 0;
+ char* ptr = (char*)str;
+ while (*ptr++) {
+ size++;
}
- return *cursor ? 0 : 1;
+ return size;
}
-// Returns the offset of the file in the tar.
+// Returns the offset of the file(or prefix) in the tar.
// `size_out` will be filled with the size of the file.
-static size_t find_file_in_tar(const char* filename, size_t* size_out) {
+static size_t find_file_in_tar(const char* filename, size_t start_cursor,
+ size_t* size_out, char* found_filename,
+ size_t filename_max_len) {
if (!size_out || !filename) {
return 0;
}
-
+ const char kTarMagic[] = "ustar";
tar_header tar;
- size_t cursor = 0;
+ size_t cursor = start_cursor;
CHECK_DIF_OK(spi_flash_read_page(cursor, (uint8_t*)&tar));
while (cursor < kSpiFlashBytes) {
+ // Check the tar header magic field to validate the header info.
+ if (memcmp((const char*)&tar.magic, kTarMagic, str_size(kTarMagic)) != 0) {
+ break;
+ }
int size = parse_octal((uint8_t*)&tar.size);
cursor += 512;
-
- if (contains(&tar.name, filename)) {
+ const char* tar_name = (const char*)&tar.name;
+ size_t tar_name_len = str_size(tar_name);
+ if (memcmp(tar_name, filename, str_size(filename)) == 0) {
if (size_out) {
*size_out = size;
}
+ if (found_filename) {
+ size_t copy_len = (tar_name_len < filename_max_len - 1)
+ ? tar_name_len
+ : filename_max_len - 1;
+ memcpy(found_filename, tar_name, copy_len);
+ found_filename[copy_len] = '\0';
+ }
return cursor;
}
@@ -96,11 +109,11 @@
return 0;
}
-dif_result_t load_file_from_tar(const char* filename, void* addr) {
- size_t size = 0;
- size_t bin_offset = find_file_in_tar(filename, &size);
- if (!bin_offset) return kDifError;
-
+static dif_result_t copy_flash_to_mem(size_t bin_offset, size_t size,
+ void* addr) {
+ if (!addr) {
+ return kDifBadArg;
+ }
uint8_t page[SPI_PAGE_SIZE];
size_t remaining = size;
while (remaining > 0) {
@@ -116,3 +129,38 @@
return kDifOk;
}
+
+dif_result_t load_file_from_tar(const char* filename, void* addr,
+ size_t max_mem_addr) {
+ if (!filename) {
+ return kDifBadArg;
+ }
+ size_t size = 0;
+ size_t bin_offset =
+ find_file_in_tar(filename, /*start_cursor=*/0, &size,
+ /*found_filename=*/NULL, /*max_filename_len=*/0);
+ if (!bin_offset) return kDifError;
+ if ((size_t)addr + size > max_mem_addr) {
+ return kDifBadArg;
+ }
+ return copy_flash_to_mem(bin_offset, size, addr);
+}
+
+dif_result_t load_file_prefix_from_tar(const char* file_prefix, void* addr,
+ size_t max_mem_addr, size_t* tar_offset,
+ char* filename, size_t filename_len) {
+ if (!tar_offset || !file_prefix || !filename) {
+ return kDifBadArg;
+ }
+ size_t size = 0;
+ size_t start_offset = *tar_offset;
+ size_t bin_offset = find_file_in_tar(file_prefix, start_offset, &size,
+ filename, filename_len);
+ if (!bin_offset) return kDifOutOfRange;
+ if ((size_t)addr + size > max_mem_addr) {
+ return kDifBadArg;
+ }
+ // Update the tar_offset for the future call.
+ *tar_offset = bin_offset + ((size + 511) & ~511);
+ return copy_flash_to_mem(bin_offset, size, addr);
+}
diff --git a/sw/device/lib/spi_flash.h b/sw/device/lib/spi_flash.h
index 649fd85..746bc23 100644
--- a/sw/device/lib/spi_flash.h
+++ b/sw/device/lib/spi_flash.h
@@ -18,7 +18,12 @@
dif_result_t spi_flash_read_page(uint32_t page, uint8_t* buf);
/* Tar filesystem related methods */
-dif_result_t load_file_from_tar(const char* filename, void* addr);
+dif_result_t load_file_from_tar(const char* filename, void* addr,
+ size_t max_mem_addr);
+
+dif_result_t load_file_prefix_from_tar(const char* file_prefix, void* addr,
+ size_t max_mem_addr, size_t* bin_offset,
+ char* filename, size_t filename_len);
#if defined(__cplusplus)
}
diff --git a/sw/device/lib/testing/test_rom/BUILD b/sw/device/lib/testing/test_rom/BUILD
index 31ef4bc..c5fa7d0 100644
--- a/sw/device/lib/testing/test_rom/BUILD
+++ b/sw/device/lib/testing/test_rom/BUILD
@@ -28,15 +28,39 @@
name = "test_rom",
deps = [
":linker_script",
+ ":test_rom_otp_lib",
+ ],
+)
+
+opentitan_rom_binary(
+ name = "test_rom_no_otp",
+ srcs = [
+ "test_rom.c",
+ "test_rom_start.S",
+ ],
+ defines = ["OTP_IS_RAM"],
+ deps = [
+ ":linker_script",
":test_rom_lib",
+ "//sw/device/silicon_creator/rom:bootstrap_no_otp",
+ ],
+)
+
+cc_library(
+ name = "test_rom_otp_lib",
+ srcs = [
+ "test_rom.c",
+ "test_rom_start.S",
+ ],
+ deps = [
+ ":test_rom_lib",
+ "//sw/device/silicon_creator/rom:bootstrap",
],
)
cc_library(
name = "test_rom_lib",
srcs = [
- "test_rom.c",
- "test_rom_start.S",
],
target_compatible_with = [OPENTITAN_CPU],
deps = [
@@ -46,8 +70,8 @@
"//hw/top_matcha/ip/flash_ctrl/data/autogen:flash_ctrl_regs",
"//hw/top_matcha/ip/sensor_ctrl/data:sensor_ctrl_regs",
"//hw/top_matcha/sw/autogen:top_matcha",
- "//sw/device/lib/testing:pinmux_testutils",
"//sw/device/lib/dif:pinmux",
+ "//sw/device/lib/testing:pinmux_testutils",
"@lowrisc_opentitan//hw/ip/csrng/data:csrng_regs",
"@lowrisc_opentitan//hw/ip/edn/data:edn_regs",
"@lowrisc_opentitan//hw/ip/entropy_src/data:entropy_src_regs",
@@ -79,7 +103,6 @@
"@lowrisc_opentitan//sw/device/silicon_creator/lib/base:static_critical_sec_mmio",
"@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:flash_ctrl",
"@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:retention_sram",
- "@lowrisc_opentitan//sw/device/silicon_creator/rom:bootstrap",
],
)
diff --git a/sw/device/lib/testing/test_rom/test_rom.c b/sw/device/lib/testing/test_rom/test_rom.c
index 02303a0..0f2ed0e 100644
--- a/sw/device/lib/testing/test_rom/test_rom.c
+++ b/sw/device/lib/testing/test_rom/test_rom.c
@@ -73,9 +73,14 @@
OT_WEAK
bool rom_test_main(void) {
// Check the otp to see if execute should start
+ // val: 0xFFFFFFFF
+#if defined(OTP_IS_RAM)
+ uint32_t otp_val = 0xffffffff;
+#else
uint32_t otp_val = abs_mmio_read32(
TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR + OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
OTP_CTRL_PARAM_CREATOR_SW_CFG_ROM_EXEC_EN_OFFSET);
+#endif
if (otp_val == 0) {
test_status_set(kTestStatusInBootRomHalt);
@@ -86,9 +91,14 @@
// Initialize Ibex cpuctrl (contains icache / security feature enablements).
uint32_t cpuctrl_csr;
CSR_READ(CSR_REG_CPUCTRL, &cpuctrl_csr);
+ // val: 0x1
+#if defined(OTP_IS_RAM)
+ uint32_t cpuctrl_otp_val = 0x1;
+#else
uint32_t cpuctrl_otp_val = abs_mmio_read32(
TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR + OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
OTP_CTRL_PARAM_CREATOR_SW_CFG_CPUCTRL_OFFSET);
+#endif
cpuctrl_csr = bitfield_field32_write(
cpuctrl_csr, (bitfield_field32_t){.mask = 0x3f, .index = 0},
cpuctrl_otp_val);
@@ -170,9 +180,14 @@
}
// Check the otp to see if flash scramble should be enabled.
+ // val: 0x0
+#if defined(OTP_IS_RAM)
+ otp_val = 0;
+#else
otp_val = abs_mmio_read32(
TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR + OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
OTP_CTRL_PARAM_CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG_OFFSET);
+#endif
if (otp_val != 0) {
dif_flash_ctrl_region_properties_t default_properties;
diff --git a/sw/device/lib/testing/test_rom/test_rom_start.S b/sw/device/lib/testing/test_rom/test_rom_start.S
index 8db5211..a326517 100644
--- a/sw/device/lib/testing/test_rom/test_rom_start.S
+++ b/sw/device/lib/testing/test_rom/test_rom_start.S
@@ -219,6 +219,7 @@
#endif
_start:
#if !OT_IS_ENGLISH_BREAKFAST
+#if !OTP_IS_RAM
// Check if AST initialization should be skipped.
li a0, (TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR + \
OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET)
@@ -233,6 +234,9 @@
OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + \
OTP_CTRL_PARAM_CREATOR_SW_CFG_AST_CFG_OFFSET)
call crt_section_copy
+#else
+ j .L_ast_init_skip
+#endif
// Wait for AST initialization to complete.
li a0, TOP_MATCHA_SENSOR_CTRL_BASE_ADDR
diff --git a/sw/device/silicon_creator/BUILD b/sw/device/silicon_creator/BUILD
new file mode 100644
index 0000000..34910b0
--- /dev/null
+++ b/sw/device/silicon_creator/BUILD
@@ -0,0 +1,6 @@
+# Copyright 2022 Google LLC.
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+package(default_visibility = ["//visibility:public"])
diff --git a/sw/device/silicon_creator/rom/BUILD b/sw/device/silicon_creator/rom/BUILD
new file mode 100644
index 0000000..9e1d2ff
--- /dev/null
+++ b/sw/device/silicon_creator/rom/BUILD
@@ -0,0 +1,37 @@
+# Copyright 2022 Google LLC.
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+package(default_visibility = ["//visibility:public"])
+
+BOOTSTRAP_DEPS = [
+ "//hw/top_matcha/sw/autogen:top_matcha",
+ "//hw/top_matcha/ip/flash_ctrl/data/autogen:flash_ctrl_regs",
+ "@lowrisc_opentitan//hw/ip/gpio/data:gpio_regs",
+ "@lowrisc_opentitan//hw/ip/otp_ctrl/data:otp_ctrl_regs",
+ "@lowrisc_opentitan//sw/device/lib/base:abs_mmio",
+ "@lowrisc_opentitan//sw/device/lib/base:bitfield",
+ "@lowrisc_opentitan//sw/device/lib/base:hardened",
+ "@lowrisc_opentitan//sw/device/silicon_creator/lib:error",
+ "@lowrisc_opentitan//sw/device/silicon_creator/lib/base:chip",
+ "@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:flash_ctrl",
+ "@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:otp",
+ "@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:rstmgr",
+ "@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:spi_device",
+ ]
+
+cc_library(
+ name = "bootstrap",
+ srcs = ["bootstrap.c"],
+ hdrs = ["bootstrap.h"],
+ deps = BOOTSTRAP_DEPS,
+)
+
+cc_library(
+ name = "bootstrap_no_otp",
+ srcs = ["bootstrap.c"],
+ hdrs = ["bootstrap.h"],
+ defines = ["OTP_IS_RAM"],
+ deps = BOOTSTRAP_DEPS,
+)
diff --git a/sw/device/silicon_creator/rom/bootstrap.c b/sw/device/silicon_creator/rom/bootstrap.c
new file mode 100644
index 0000000..f77c592
--- /dev/null
+++ b/sw/device/silicon_creator/rom/bootstrap.c
@@ -0,0 +1,392 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/rom/bootstrap.h"
+
+#include <stdalign.h>
+
+#include "sw/device/lib/base/abs_mmio.h"
+#include "sw/device/lib/base/bitfield.h"
+#include "sw/device/lib/base/hardened.h"
+#include "sw/device/silicon_creator/lib/base/chip.h"
+#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
+#include "sw/device/silicon_creator/lib/drivers/otp.h"
+#include "sw/device/silicon_creator/lib/drivers/rstmgr.h"
+#include "sw/device/silicon_creator/lib/drivers/spi_device.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#include "flash_ctrl_regs.h"
+#include "gpio_regs.h"
+#include "hw/top_matcha/sw/autogen/top_matcha.h"
+#include "otp_ctrl_regs.h"
+
+enum {
+ /*
+ * Maximum flash address, exclusive.
+ */
+ kMaxAddress =
+ FLASH_CTRL_PARAM_BYTES_PER_BANK * FLASH_CTRL_PARAM_REG_NUM_BANKS,
+};
+
+static_assert(FLASH_CTRL_PARAM_REG_NUM_BANKS == 2, "Flash must have 2 banks");
+
+/**
+ * Bootstrap states.
+ *
+ * OpenTitan bootstrap consists of three states between which the chip
+ * transitions sequentially.
+ *
+ * Encoding generated with
+ * $ ./util/design/sparse-fsm-encode.py -d 5 -m 3 -n 32 \
+ * -s 375382971 --language=c
+ *
+ * Minimum Hamming distance: 17
+ * Maximum Hamming distance: 19
+ * Minimum Hamming weight: 16
+ * Maximum Hamming weight: 19
+ */
+typedef enum bootstrap_state {
+ /**
+ * Initial bootstrap state where the chip waits for a SECTOR_ERASE or
+ * CHIP_ERASE command.
+ */
+ kBootstrapStateErase = 0xd4576543,
+ /**
+ * Second bootstrap state where the chip verifies that all data banks have
+ * been erased.
+ */
+ kBootstrapStateEraseVerify = 0xf3c71bac,
+ /**
+ * Final bootstrap state. This is the main program loop where the chip handles
+ * erase, program, and reset commands.
+ */
+ kBootstrapStateProgram = 0xbdd8ca60,
+} bootstrap_state_t;
+
+/**
+ * Handles access permissions and erases both data banks of the embedded flash.
+ *
+ * @return Result of the operation.
+ */
+static rom_error_t bootstrap_chip_erase(void) {
+ flash_ctrl_bank_erase_perms_set(kHardenedBoolTrue);
+ rom_error_t err_0 = flash_ctrl_data_erase(0, kFlashCtrlEraseTypeBank);
+ rom_error_t err_1 = flash_ctrl_data_erase(FLASH_CTRL_PARAM_BYTES_PER_BANK,
+ kFlashCtrlEraseTypeBank);
+ flash_ctrl_bank_erase_perms_set(kHardenedBoolFalse);
+
+ HARDENED_RETURN_IF_ERROR(err_0);
+ return err_1;
+}
+
+/**
+ * Handles access permissions and erases a 4 KiB region in the data partition of
+ * the embedded flash.
+ *
+ * Since OpenTitan's flash page size is 2 KiB, this function erases two
+ * consecutive pages.
+ *
+ * @param addr Address that falls within the 4 KiB region being deleted.
+ * @return Result of the operation.
+ */
+static rom_error_t bootstrap_sector_erase(uint32_t addr) {
+ static_assert(FLASH_CTRL_PARAM_BYTES_PER_PAGE == 2048,
+ "Page size must be 2 KiB");
+ enum {
+ /**
+ * Mask for truncating `addr` to the lower 4 KiB aligned address.
+ */
+ kPageAddrMask = ~UINT32_C(4096) + 1,
+ };
+
+ if (addr >= kMaxAddress) {
+ return kErrorBootstrapEraseAddress;
+ }
+ addr &= kPageAddrMask;
+
+ flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
+ .read = kMultiBitBool4False,
+ .write = kMultiBitBool4False,
+ .erase = kMultiBitBool4True,
+ });
+ rom_error_t err_0 = flash_ctrl_data_erase(addr, kFlashCtrlEraseTypePage);
+ rom_error_t err_1 = flash_ctrl_data_erase(
+ addr + FLASH_CTRL_PARAM_BYTES_PER_PAGE, kFlashCtrlEraseTypePage);
+ flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
+ .read = kMultiBitBool4False,
+ .write = kMultiBitBool4False,
+ .erase = kMultiBitBool4False,
+ });
+
+ HARDENED_RETURN_IF_ERROR(err_0);
+ return err_1;
+}
+
+/**
+ * Handles access permissions and programs up to 256 bytes of flash memory
+ * starting at `addr`.
+ *
+ * If `byte_count` is not a multiple of flash word size, it's rounded up to next
+ * flash word and missing bytes in `data` are set to `0xff`.
+ *
+ * @param addr Address to write to, must be flash word aligned.
+ * @param byte_count Number of bytes to write. Rounded up to next flash word if
+ * not a multiple of flash word size. Missing bytes in `data` are set to `0xff`.
+ * @param data Data to write, must be word aligned. If `byte_count` is not a
+ * multiple of flash word size, `data` must have enough space until the next
+ * flash word.
+ * @return Result of the operation.
+ */
+static rom_error_t bootstrap_page_program(uint32_t addr, size_t byte_count,
+ uint8_t *data) {
+ static_assert(__builtin_popcount(FLASH_CTRL_PARAM_BYTES_PER_WORD) == 1,
+ "Bytes per flash word must be a power of two.");
+ enum {
+ /**
+ * Mask for checking that `addr` is flash word aligned.
+ */
+ kFlashWordMask = FLASH_CTRL_PARAM_BYTES_PER_WORD - 1,
+ /**
+ * SPI flash programming page size in bytes.
+ */
+ kFlashProgPageSize = 256,
+ /**
+ * Mask for checking whether `addr` is flash programming page aligned.
+ *
+ * Flash programming page size is 256 bytes, writes that start at an `addr`
+ * with a non-zero LSB wrap to the start of the 256 byte region.
+ */
+ kFlashProgPageMask = kFlashProgPageSize - 1,
+ };
+
+ if (addr & kFlashWordMask || addr >= kMaxAddress) {
+ return kErrorBootstrapProgramAddress;
+ }
+
+ // Round up to next flash word and fill missing bytes with `0xff`.
+ size_t flash_word_misalignment = byte_count & kFlashWordMask;
+ if (flash_word_misalignment > 0) {
+ size_t padding_byte_count =
+ FLASH_CTRL_PARAM_BYTES_PER_WORD - flash_word_misalignment;
+ for (size_t i = 0; i < padding_byte_count; ++i) {
+ data[byte_count++] = 0xff;
+ }
+ }
+ size_t rem_word_count = byte_count / sizeof(uint32_t);
+
+ flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
+ .read = kMultiBitBool4False,
+ .write = kMultiBitBool4True,
+ .erase = kMultiBitBool4False,
+ });
+ // Perform two writes if the start address is not page-aligned (256 bytes).
+ // Note: Address is flash-word-aligned (8 bytes) due to the check above.
+ rom_error_t err_0 = kErrorOk;
+ size_t prog_page_misalignment = addr & kFlashProgPageMask;
+ if (prog_page_misalignment > 0) {
+ size_t word_count =
+ (kFlashProgPageSize - prog_page_misalignment) / sizeof(uint32_t);
+ if (word_count > rem_word_count) {
+ word_count = rem_word_count;
+ }
+ err_0 = flash_ctrl_data_write(addr, word_count, data);
+ rem_word_count -= word_count;
+ data += word_count * sizeof(uint32_t);
+ // Wrap to the beginning of the current page since PAGE_PROGRAM modifies
+ // a single page only.
+ addr &= ~kFlashProgPageMask;
+ }
+ rom_error_t err_1 = kErrorOk;
+ if (rem_word_count > 0) {
+ err_1 = flash_ctrl_data_write(addr, rem_word_count, data);
+ }
+ flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
+ .read = kMultiBitBool4False,
+ .write = kMultiBitBool4False,
+ .erase = kMultiBitBool4False,
+ });
+
+ HARDENED_RETURN_IF_ERROR(err_0);
+ return err_1;
+}
+
+/**
+ * Bootstrap state 1: Wait for an erase command and erase the data
+ * partition.
+ *
+ * This function erases both data banks of the flash regardless of the type of
+ * the erase command (CHIP_ERASE or SECTOR_ERASE).
+ *
+ * @param state Bootstrap state.
+ * @return Result of the operation.
+ */
+static rom_error_t bootstrap_handle_erase(bootstrap_state_t *state) {
+ HARDENED_CHECK_EQ(*state, kBootstrapStateErase);
+
+ spi_device_cmd_t cmd;
+ RETURN_IF_ERROR(spi_device_cmd_get(&cmd));
+ // Erase requires WREN, ignore if WEL is not set.
+ if (!bitfield_bit32_read(spi_device_flash_status_get(), kSpiDeviceWelBit)) {
+ return kErrorOk;
+ }
+
+ rom_error_t error = kErrorUnknown;
+ switch (cmd.opcode) {
+ case kSpiDeviceOpcodeChipErase:
+ case kSpiDeviceOpcodeSectorErase:
+ error = bootstrap_chip_erase();
+ HARDENED_RETURN_IF_ERROR(error);
+ *state = kBootstrapStateEraseVerify;
+ // Note: We clear WIP and WEN bits in `bootstrap_handle_erase_verify()`
+ // after checking that both data banks have been erased.
+ break;
+ default:
+ // Ignore any other command, e.g. PAGE_PROGRAM, RESET, and clear WIP and
+ // WEN bits right away.
+ spi_device_flash_status_clear();
+ error = kErrorOk;
+ }
+
+ return error;
+}
+
+/**
+ * Bootstrap state 2: Verify that all data banks have been erased.
+ *
+ * This function also clears the WIP and WEN bits of the flash status register.
+ *
+ * @param state Bootstrap state.
+ * @return Result of the operation.
+ */
+static rom_error_t bootstrap_handle_erase_verify(bootstrap_state_t *state) {
+ HARDENED_CHECK_EQ(*state, kBootstrapStateEraseVerify);
+
+ rom_error_t err_0 = flash_ctrl_data_erase_verify(0, kFlashCtrlEraseTypeBank);
+ rom_error_t err_1 = flash_ctrl_data_erase_verify(
+ FLASH_CTRL_PARAM_BYTES_PER_BANK, kFlashCtrlEraseTypeBank);
+ HARDENED_RETURN_IF_ERROR(err_0);
+ HARDENED_RETURN_IF_ERROR(err_1);
+
+ *state = kBootstrapStateProgram;
+ spi_device_flash_status_clear();
+ return err_0;
+}
+
+/**
+ * Bootstrap state 3: (Erase/)Program loop.
+ *
+ * @param state Bootstrap state.
+ * @return Result of the operation.
+ */
+static rom_error_t bootstrap_handle_program(bootstrap_state_t *state) {
+ static_assert(alignof(spi_device_cmd_t) >= sizeof(uint32_t) &&
+ offsetof(spi_device_cmd_t, payload) >= sizeof(uint32_t),
+ "Payload must be word aligned.");
+ static_assert(
+ sizeof((spi_device_cmd_t){0}.payload) % FLASH_CTRL_PARAM_BYTES_PER_WORD ==
+ 0,
+ "Payload size must be a multiple of flash word size.");
+
+ HARDENED_CHECK_EQ(*state, kBootstrapStateProgram);
+
+ spi_device_cmd_t cmd;
+ RETURN_IF_ERROR(spi_device_cmd_get(&cmd));
+ // Erase and program require WREN, ignore if WEL is not set.
+ if (cmd.opcode != kSpiDeviceOpcodeReset &&
+ !bitfield_bit32_read(spi_device_flash_status_get(), kSpiDeviceWelBit)) {
+ return kErrorOk;
+ }
+
+ rom_error_t error = kErrorUnknown;
+ switch (cmd.opcode) {
+ case kSpiDeviceOpcodeChipErase:
+ error = bootstrap_chip_erase();
+ break;
+ case kSpiDeviceOpcodeSectorErase:
+ error = bootstrap_sector_erase(cmd.address);
+ break;
+ case kSpiDeviceOpcodePageProgram:
+ error = bootstrap_page_program(cmd.address, cmd.payload_byte_count,
+ cmd.payload);
+ break;
+ case kSpiDeviceOpcodeReset:
+ rstmgr_reset();
+#ifdef OT_PLATFORM_RV32
+ HARDENED_UNREACHABLE();
+#else
+ // If this is an off-target test, return `kErrorUnknown` to be able to
+ // test without requiring EXPECT_DEATH.
+ error = kErrorUnknown;
+#endif
+ break;
+ default:
+ // We don't expect any other commands but we can potentially end up
+ // here with a 0x0 opcode due to glitches on SPI or strap lines (see
+ // #11871).
+ error = kErrorOk;
+ }
+ HARDENED_RETURN_IF_ERROR(error);
+
+ spi_device_flash_status_clear();
+ return error;
+}
+
+hardened_bool_t bootstrap_requested(void) {
+#if defined(OTP_IS_RAM)
+ uint32_t res = kHardenedBoolTrue;
+#else
+ uint32_t res =
+ otp_read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET);
+#endif
+ if (launder32(res) != kHardenedBoolTrue) {
+ return kHardenedBoolFalse;
+ }
+ HARDENED_CHECK_EQ(res, kHardenedBoolTrue);
+
+ // A single read is sufficient since we expect strong pull-ups on the strap
+ // pins.
+ res ^= SW_STRAP_BOOTSTRAP;
+ res ^=
+ abs_mmio_read32(TOP_MATCHA_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET) &
+ SW_STRAP_MASK;
+ if (launder32(res) != kHardenedBoolTrue) {
+ return kHardenedBoolFalse;
+ }
+ HARDENED_CHECK_EQ(res, kHardenedBoolTrue);
+ return res;
+}
+
+rom_error_t bootstrap(void) {
+ hardened_bool_t requested = bootstrap_requested();
+ if (launder32(requested) != kHardenedBoolTrue) {
+ return kErrorBootstrapNotRequested;
+ }
+ HARDENED_CHECK_EQ(requested, kHardenedBoolTrue);
+
+ spi_device_init();
+
+ // Bootstrap event loop.
+ bootstrap_state_t state = kBootstrapStateErase;
+ rom_error_t error = kErrorUnknown;
+ while (true) {
+ switch (launder32(state)) {
+ case kBootstrapStateErase:
+ HARDENED_CHECK_EQ(state, kBootstrapStateErase);
+ error = bootstrap_handle_erase(&state);
+ break;
+ case kBootstrapStateEraseVerify:
+ HARDENED_CHECK_EQ(state, kBootstrapStateEraseVerify);
+ error = bootstrap_handle_erase_verify(&state);
+ break;
+ case kBootstrapStateProgram:
+ HARDENED_CHECK_EQ(state, kBootstrapStateProgram);
+ error = bootstrap_handle_program(&state);
+ break;
+ default:
+ error = kErrorBootstrapInvalidState;
+ }
+ HARDENED_RETURN_IF_ERROR(error);
+ }
+ HARDENED_UNREACHABLE();
+}
diff --git a/sw/device/silicon_creator/rom/bootstrap.h b/sw/device/silicon_creator/rom/bootstrap.h
new file mode 100644
index 0000000..fd4c725
--- /dev/null
+++ b/sw/device/silicon_creator/rom/bootstrap.h
@@ -0,0 +1,48 @@
+// Copyright 2023 Google LLC.
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef SW_DEVICE_SILICON_CREATOR_ROM_BOOTSTRAP_H_
+#define SW_DEVICE_SILICON_CREATOR_ROM_BOOTSTRAP_H_
+
+#include "sw/device/lib/base/hardened.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Checks whether bootstrap is requested.
+ *
+ * The return value of this function also depends on the `ROM_BOOTSTRAP_EN` OTP
+ * item.
+ *
+ * @return Whether bootstrap is requested.
+ */
+hardened_bool_t bootstrap_requested(void);
+
+/**
+ * Bootstraps the data partition of the embedded flash with data received by the
+ * spi_device.
+ *
+ * OpenTitan bootstrap uses the typical SPI flash EEPROM commands. A typical
+ * bootstrap session involves:
+ * - Asserting bootstrap pins to enter bootstrap mode,
+ * - Erasing the chip (WREN, CHIP_ERASE, busy loop ...),
+ * - Programming the chip (WREN, PAGE_PROGRAM, busy loop ...), and
+ * - Resetting the chip (RESET).
+ *
+ * This function only returns on error since a successful bootstrap ends with a
+ * chip reset.
+ *
+ * @return Result of the operation.
+ */
+rom_error_t bootstrap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SW_DEVICE_SILICON_CREATOR_ROM_BOOTSTRAP_H_
diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD
index fc00761..21913dc 100644
--- a/sw/device/tests/BUILD
+++ b/sw/device/tests/BUILD
@@ -670,7 +670,7 @@
deps = [
":test_lib_base",
"//sw/device/lib/testing/test_rom:linker_script",
- "//sw/device/lib/testing/test_rom:test_rom_lib",
+ "//sw/device/lib/testing/test_rom:test_rom_otp_lib",
],
)
@@ -994,10 +994,9 @@
matcha_dv_test(
name = "clkmgr_reset_frequency_test",
- srcs = ["@lowrisc_opentitan//sw/device/tests:clkmgr_reset_frequency_test.c"],
+ srcs = [":clkmgr_reset_frequency_test.c"],
deps = [
- ":test_dv_lib_opentitan",
- "//sw/device/lib/arch:device",
+ ":test_dv_lib",
"//sw/device/lib/testing:clkmgr_testutils",
"@lowrisc_opentitan//sw/device/lib/dif:sensor_ctrl",
"@lowrisc_opentitan//sw/device/lib/testing:aon_timer_testutils",
diff --git a/sw/device/tests/clkmgr_reset_frequency_test.c b/sw/device/tests/clkmgr_reset_frequency_test.c
new file mode 100644
index 0000000..1a574af
--- /dev/null
+++ b/sw/device/tests/clkmgr_reset_frequency_test.c
@@ -0,0 +1,92 @@
+// Copyright 2023 Google LLC.
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "hw/top_matcha/sw/autogen/top_matcha.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/dif/dif_sensor_ctrl.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/aon_timer_testutils.h"
+#include "sw/device/lib/testing/clkmgr_testutils.h"
+#include "sw/device/lib/testing/rstmgr_testutils.h"
+#include "sw/device/lib/testing/sensor_ctrl_testutils.h"
+#include "sw/device/lib/testing/test_framework/check.h"
+#include "sw/device/lib/testing/test_framework/ottf_main.h"
+
+OTTF_DEFINE_TEST_CONFIG();
+
+/**
+ * This test measure clock counts with clkmgr frequency measurements, performing
+ * 100 measurements per round. Measurement errors (fast or slow clocks) are
+ * recorded as recoverable error in clkmgr.
+ *
+ * This test configures the clock thresholds so it generates errors, after 100
+ * measurements it checks that some errors are found, then a reset is triggered,
+ * and it checks the measurements should be disabled, and no errors should be
+ * reported.
+ *
+ * Notice the test overrides the hardware behavior so it comes out with
+ * calibrated USB clock, otherwise the USB clock frequency will be incorrect.
+ * USB calibration should be a separate test, and may be vendor-specific.
+ */
+const unsigned int kMeasurementsPerRound = 100;
+
+bool test_main(void) {
+ dif_clkmgr_t clkmgr;
+ dif_rstmgr_t rstmgr;
+ dif_sensor_ctrl_t sensor_ctrl;
+
+ const uint32_t kMeasurementDelayMicros =
+ aon_timer_testutils_get_us_from_aon_cycles(kMeasurementsPerRound);
+
+ CHECK_DIF_OK(dif_clkmgr_init(
+ mmio_region_from_addr(TOP_MATCHA_CLKMGR_AON_BASE_ADDR), &clkmgr));
+ CHECK_DIF_OK(dif_sensor_ctrl_init(
+ mmio_region_from_addr(TOP_MATCHA_SENSOR_CTRL_BASE_ADDR), &sensor_ctrl));
+ CHECK_DIF_OK(dif_rstmgr_init(
+ mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr));
+
+ LOG_INFO("TEST: wait for ast init");
+ IBEX_SPIN_FOR(sensor_ctrl_ast_init_done(&sensor_ctrl),
+ 1000); // Time out at 1000us
+ LOG_INFO("TEST: done ast init");
+
+ if (rstmgr_testutils_reset_info_any(&rstmgr, kDifRstmgrResetInfoPor)) {
+ LOG_INFO("POR reset");
+
+ // Configure the counters to trigger an error by setting them for external
+ // clocks.
+ clkmgr_testutils_enable_clock_counts_with_expected_thresholds(
+ &clkmgr, /*jitter_enabled=*/false, /*external_clk=*/true,
+ /*low_speed=*/true);
+ busy_spin_micros(kMeasurementDelayMicros);
+
+ // Check we get errors, but let the counters keep going.
+ dif_clkmgr_recov_err_codes_t err_codes;
+ CHECK_DIF_OK(dif_clkmgr_recov_err_code_get_codes(&clkmgr, &err_codes));
+ CHECK(err_codes != 0);
+
+ // Trigger a rstmgr SW reset.
+ CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
+
+ // Add delay such that the software reset can take effect
+ busy_spin_micros(100);
+
+ // This should not be reached.
+ LOG_ERROR("This is unreachable since a reset should have been triggered");
+ return false;
+ } else if (rstmgr_testutils_reset_info_any(&rstmgr, kDifRstmgrResetInfoSw)) {
+ LOG_INFO("Back from rstmgr SW reset");
+ CHECK(clkmgr_testutils_check_measurement_enables(&clkmgr,
+ kDifToggleDisabled));
+ CHECK(clkmgr_testutils_check_measurement_counts(&clkmgr));
+ return true;
+ } else {
+ dif_rstmgr_reset_info_bitfield_t rst_info;
+ CHECK_DIF_OK(dif_rstmgr_reset_info_get(&rstmgr, &rst_info));
+ LOG_ERROR("Unexpected rst_info 0x%x", rst_info);
+ return false;
+ }
+ return true;
+}
diff --git a/sw/device/tests/kelvin/fpga_tests/BUILD b/sw/device/tests/kelvin/fpga_tests/BUILD
index 5c0f975..364d0cf 100644
--- a/sw/device/tests/kelvin/fpga_tests/BUILD
+++ b/sw/device/tests/kelvin/fpga_tests/BUILD
@@ -11,8 +11,6 @@
name = "kelvin_test_sc_extflash",
srcs = [
"kelvin_test_sc.c",
- "kelvin_test_sc_loaders.h",
- "kelvin_test_sc_loaders_extflash.c",
],
copts = [
"-nostdlib",
diff --git a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c b/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c
index d27bf6f..0031960 100644
--- a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c
+++ b/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c
@@ -6,11 +6,11 @@
#include "sw/device/lib/dif/dif_smc_ctrl.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/print.h"
+#include "sw/device/lib/spi_flash.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
#include "sw/device/lib/testing/test_framework/status.h"
#include "sw/device/lib/testing/test_framework/test_util.h"
-#include "sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders.h"
/*
* Sample program to run some code on all cores in the system.
@@ -28,11 +28,12 @@
test_status_set(kTestStatusInTest);
init_uart(TOP_MATCHA_UART0_BASE_ADDR, &uart);
LOG_INFO("kelvin_test_sc");
- // Do any initialization needed before loading SMC and Kelvin.
- load_init();
+ spi_flash_init();
// Copy binary to SMC RAM.
- load_smc();
+ CHECK_DIF_OK(load_file_from_tar(
+ "smc.bin", (void*)TOP_MATCHA_RAM_SMC_BASE_ADDR,
+ (TOP_MATCHA_RAM_SMC_BASE_ADDR + TOP_MATCHA_RAM_SMC_SIZE_BYTES)));
mmio_region_t ml_dmem =
mmio_region_from_addr(TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR);
@@ -43,34 +44,64 @@
mmio_region_write32(ml_dmem, i * sizeof(uint32_t), 0);
}
- // Copy binary to ML_DMEM.
- load_kelvin();
-
// Enable SMC.
CHECK_DIF_OK(dif_smc_ctrl_init(
mmio_region_from_addr(TOP_MATCHA_SMC_CTRL_BASE_ADDR), &smc_ctrl));
CHECK_DIF_OK(dif_smc_ctrl_set_en(&smc_ctrl));
- // Start up Kelvin.
+ // Reset and Freeze Kelvin.
mmio_region_t base_addr =
mmio_region_from_addr(TOP_MATCHA_ML_TOP_CORE_BASE_ADDR);
mmio_region_write32(base_addr, ML_TOP_CTRL_REG_OFFSET,
- ML_TOP_CTRL_REG_RESVAL);
- mmio_region_write32(base_addr, ML_TOP_CTRL_REG_OFFSET, 0x0);
- uint32_t intr_state =
- mmio_region_read32(base_addr, ML_TOP_INTR_STATE_REG_OFFSET);
- // TODO(ykwang): Change this to interrupt handler.
- while (intr_state == 0x0) {
+ ML_TOP_CTRL_REG_RESVAL | (1 << ML_TOP_CTRL_FREEZE_BIT));
+
+ // Copy binary to ML_DMEM.
+ char kelvin_bin_name[256] = {'\0'};
+ size_t bin_offset = 0;
+ dif_result_t kelvin_load_result;
+ int test_num = 0;
+ int failed_test = 0;
+ while ((kelvin_load_result = load_file_prefix_from_tar(
+ "kelvin_", (void*)TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR,
+ (TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR +
+ TOP_MATCHA_ML_TOP_DMEM_SIZE_BYTES),
+ &bin_offset, kelvin_bin_name, sizeof(kelvin_bin_name))) !=
+ kDifOutOfRange) {
+ CHECK_DIF_OK(kelvin_load_result, "Failed to load %s", kelvin_bin_name);
+ test_num++;
+ LOG_INFO("Test %d: Run kelvin binary %s", test_num, kelvin_bin_name);
+ // Start up Kelvin.
+ mmio_region_write32(base_addr, ML_TOP_CTRL_REG_OFFSET, 0x0);
+ uint32_t intr_state =
+ mmio_region_read32(base_addr, ML_TOP_INTR_STATE_REG_OFFSET);
+ // TODO(ykwang): Change this to interrupt handler.
+ while (intr_state == 0x0) {
+ busy_spin_micros(10 * 1000); // Wait for 10ms.
+ intr_state = mmio_region_read32(base_addr, ML_TOP_INTR_STATE_REG_OFFSET);
+ }
+ // Received interrupts from Kelvin core, check if only FINISH asserted
+ if (intr_state != (1 << ML_TOP_INTR_STATE_FINISH_BIT)) {
+ LOG_ERROR("INTR_STATE read out: expected : 0x%x | actual: 0x%x",
+ (1 << ML_TOP_INTR_STATE_FINISH_BIT), intr_state);
+ failed_test++;
+ }
+ // Reset and halt Kelvin
+ mmio_region_write32(base_addr, ML_TOP_CTRL_REG_OFFSET,
+ ML_TOP_CTRL_REG_RESVAL | (1 << ML_TOP_CTRL_FREEZE_BIT));
+ // Write 1 to clear INTR_STATE
+ mmio_region_write32(base_addr, ML_TOP_INTR_STATE_REG_OFFSET, intr_state);
intr_state = mmio_region_read32(base_addr, ML_TOP_INTR_STATE_REG_OFFSET);
- busy_spin_micros(10 * 1000); // Wait for 10ms.
+ CHECK(
+ intr_state == 0,
+ "ML_TOP_Core offset 0 INTR_STATE read again - Expected: 0 | Actual: "
+ "0x%x",
+ intr_state);
}
-
- LOG_INFO("Kelvin finished executing.");
- // Received interrupts from Kelvin core, check if only FINISH asserted
- CHECK(intr_state == (1 << ML_TOP_INTR_STATE_FINISH_BIT),
- "INTR_STATE read out: expected : 0x%x | actual: 0x%x",
- (1 << ML_TOP_INTR_STATE_FINISH_BIT), intr_state);
-
- test_status_set(kTestStatusPassed);
+ LOG_INFO("Total test: %d, failed test: %d", test_num, failed_test);
+ if (failed_test == 0) {
+ test_status_set(kTestStatusPassed);
+ } else {
+ test_status_set(kTestStatusFailed);
+ }
asm volatile("wfi");
}
diff --git a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders.h b/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders.h
deleted file mode 100644
index fd0161f..0000000
--- a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2023 Google LLC
-
-#ifndef SW_DEVICE_TESTS_KELVIN_FPGA_TESTS_KELVIN_TEST_SC_LOADERS_H_
-#define SW_DEVICE_TESTS_KELVIN_FPGA_TESTS_KELVIN_TEST_SC_LOADERS_H_
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-void load_init(void);
-void load_smc(void);
-void load_kelvin(void);
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif // SW_DEVICE_TESTS_KELVIN_FPGA_TESTS_KELVIN_TEST_SC_LOADERS_H_
diff --git a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders_extflash.c b/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders_extflash.c
deleted file mode 100644
index 15abee6..0000000
--- a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders_extflash.c
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2023 Google LLC
-
-#include "hw/top_matcha/sw/autogen/top_matcha.h"
-#include "sw/device/lib/spi_flash.h"
-#include "sw/device/lib/testing/test_framework/check.h"
-#include "sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_loaders.h"
-
-void load_smc(void) {
- CHECK_DIF_OK(
- load_file_from_tar("smc.bin", (void*)TOP_MATCHA_RAM_SMC_BASE_ADDR));
-}
-
-void load_kelvin(void) {
- CHECK_DIF_OK(load_file_from_tar("kelvin.bin",
- (void*)TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR));
-}
-
-void load_init(void) { spi_flash_init(); }
diff --git a/third_party/kelvin-binary/BUILD b/third_party/kelvin-binary/BUILD
index 8f15b4e..beb141e 100644
--- a/third_party/kelvin-binary/BUILD
+++ b/third_party/kelvin-binary/BUILD
@@ -5,12 +5,16 @@
load("@matcha//rules:matcha.bzl", "matcha_extflash_tar")
-[matcha_extflash_tar(
- name = "kelvin_fpga_test_{}_extflash".format(test[:-4]),
- kelvin_binary = "@kelvin-binary//:{}".format(test),
+tarball_data = {}
+
+[tarball_data.update({"@kelvin-binary//:{}".format(test): "kelvin_{}".format(test)}) for test in glob(["*.bin"])]
+
+matcha_extflash_tar(
+ name = "kelvin_fpga_test_extflash",
sc_binary = "@matcha//sw/device/tests/kelvin/fpga_tests:kelvin_test_sc_extflash_fpga_nexus_bin",
smc_binary = "@matcha//sw/device/tests/kelvin/fpga_tests:kelvin_test_smc_fpga_nexus_bin",
+ data = tarball_data,
tags = [
"kelvin_fpga",
],
-) for test in glob(["*.bin"])]
+)