[dv/chip] Lc_walkthrough test part1
This PR adds one walkthrough test from RAW to ProdEnd state.
Upcoming PRs will walkthrough other paths of LC state transitions.
Signed-off-by: Cindy Chen <chencindy@opentitan.org>
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index 546a777..9df2022 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -2744,7 +2744,7 @@
Verify that the features that should indeed be disabled are indeed disabled.
'''
milestone: V2
- tests: []
+ tests: ["chip_sw_lc_walkthrough"]
}
{
name: chip_sw_device_ownership
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 26ee36d..2e9c17b 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -410,6 +410,14 @@
reseed: 15
}
{
+ name: chip_sw_lc_walkthrough
+ uvm_test_seq: chip_sw_lc_walkthrough_vseq
+ sw_images: ["sw/device/tests/lc_walkthrough_test:1"]
+ en_run_modes: ["sw_test_mode_test_rom"]
+ run_opts: ["+use_otp_image=LcStRaw"]
+ reseed: 1
+ }
+ {
name: chip_sw_rstmgr_sw_req
uvm_test_seq: chip_sw_base_vseq
sw_images: ["sw/device/tests/rstmgr_sw_req_test:1"]
diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core
index 1709d5a..8b5d5f3 100644
--- a/hw/top_earlgrey/dv/env/chip_env.core
+++ b/hw/top_earlgrey/dv/env/chip_env.core
@@ -51,6 +51,7 @@
- seq_lib/chip_sw_flash_ctrl_lc_rw_en_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_flash_rma_unlocked_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_lc_ctrl_transition_vseq.sv: {is_include_file: true}
+ - seq_lib/chip_sw_lc_walkthrough_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_spi_tx_rx_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_rom_ctrl_integrity_check_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_sram_ctrl_execution_main_vseq.sv: {is_include_file: true}
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_lc_walkthrough_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_lc_walkthrough_vseq.sv
new file mode 100644
index 0000000..f0f7d3a
--- /dev/null
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_lc_walkthrough_vseq.sv
@@ -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
+
+class chip_sw_lc_walkthrough_vseq extends chip_sw_base_vseq;
+ `uvm_object_utils(chip_sw_lc_walkthrough_vseq)
+
+ `uvm_object_new
+
+ // LC sends two 64-bit msg as input token.
+ localparam uint TokenWidthBit = kmac_pkg::MsgWidth * 2;
+ localparam uint TokenWidthByte = TokenWidthBit / 8;
+
+ rand bit [7:0] lc_exit_token[TokenWidthByte];
+ rand bit [7:0] lc_unlock_token[TokenWidthByte];
+ bit [7:0] otp_exit_token[TokenWidthByte];
+ bit [7:0] otp_unlock_token[TokenWidthByte];
+
+ // Reassign `select_jtag` variable to drive LC JTAG tap and disable mubi assertion errors.
+ virtual task pre_start();
+ select_jtag = SelectLCJtagTap;
+ otp_raw_img_mubi_assertion_ctrl(.enable(0));
+ super.pre_start();
+ endtask
+
+ virtual task body();
+ bit [TokenWidthBit-1:0] otp_exit_token_bits, otp_unlock_token_bits;
+ super.body();
+
+ otp_exit_token_bits = dec_otp_token_from_lc_csrs(lc_exit_token);
+ otp_unlock_token_bits = dec_otp_token_from_lc_csrs(lc_unlock_token);
+
+ otp_unlock_token = {<< 8{otp_unlock_token_bits}};
+ otp_exit_token = {<< 8{otp_exit_token_bits}};
+
+ `uvm_info(`gfn, $sformatf("OTP unlock token %0h and OTP exit token %0h",
+ otp_unlock_token_bits, otp_exit_token_bits), UVM_LOW)
+
+ // Override the C test tokens with random data.
+ sw_symbol_backdoor_overwrite("kLcExitToken", lc_exit_token);
+ sw_symbol_backdoor_overwrite("kOtpExitToken", otp_exit_token);
+ sw_symbol_backdoor_overwrite("kOtpUnlockToken", otp_unlock_token);
+
+ wait_lc_ready(1);
+ jtag_lc_state_transition(DecLcStRaw, DecLcStTestUnlocked0);
+ apply_reset();
+
+ wait (cfg.sw_logger_vif.printed_log == "Written and locked OTP secret0 partition!");
+ apply_reset();
+
+ wait (cfg.sw_logger_vif.printed_log == "Waiting for LC transtition done and reboot.");
+ wait_lc_status(LcTransitionSuccessful);
+ apply_reset();
+ endtask
+
+endclass
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
index 6de71fa..2597f49 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
@@ -19,6 +19,7 @@
`include "chip_sw_flash_ctrl_lc_rw_en_vseq.sv"
`include "chip_sw_flash_rma_unlocked_vseq.sv"
`include "chip_sw_lc_ctrl_transition_vseq.sv"
+`include "chip_sw_lc_walkthrough_vseq.sv"
`include "chip_sw_spi_tx_rx_vseq.sv"
`include "chip_sw_rom_ctrl_integrity_check_vseq.sv"
`include "chip_sw_sram_ctrl_execution_main_vseq.sv"
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD
index 407312e..077558a 100644
--- a/sw/device/tests/sim_dv/BUILD
+++ b/sw/device/tests/sim_dv/BUILD
@@ -129,6 +129,24 @@
)
opentitan_functest(
+ name = "lc_walkthrough_test",
+ srcs = ["lc_walkthrough_test.c"],
+ targets = ["dv"],
+ deps = [
+ "//hw/top_earlgrey/sw/autogen:top_earlgrey",
+ "//sw/device/lib/base:memory",
+ "//sw/device/lib/base:mmio",
+ "//sw/device/lib/dif:lc_ctrl",
+ "//sw/device/lib/dif:otp_ctrl",
+ "//sw/device/lib/runtime:log",
+ "//sw/device/lib/testing:lc_ctrl_testutils",
+ "//sw/device/lib/testing:otp_ctrl_testutils",
+ "//sw/device/lib/testing/test_framework:check",
+ "//sw/device/lib/testing/test_framework:ottf_main",
+ ],
+)
+
+opentitan_functest(
name = "clkmgr_external_clk_src_for_lc_test",
srcs = ["clkmgr_external_clk_src_for_lc_test.c"],
targets = ["dv"],
diff --git a/sw/device/tests/sim_dv/lc_walkthrough_test.c b/sw/device/tests/sim_dv/lc_walkthrough_test.c
new file mode 100644
index 0000000..f462a97
--- /dev/null
+++ b/sw/device/tests/sim_dv/lc_walkthrough_test.c
@@ -0,0 +1,166 @@
+// 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/base/memory.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_lc_ctrl.h"
+#include "sw/device/lib/dif/dif_otp_ctrl.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/lc_ctrl_testutils.h"
+#include "sw/device/lib/testing/otp_ctrl_testutils.h"
+#include "sw/device/lib/testing/test_framework/check.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+#define LC_TOKEN_SIZE 16
+
+static dif_lc_ctrl_t lc;
+static dif_otp_ctrl_t otp;
+
+/**
+ * Track LC state transition tokens.
+ *
+ * These tokens will be further randomized and overridden by the testbench.
+ */
+
+// LC exit token value for LC state transition.
+static volatile const uint8_t kLcExitToken[LC_TOKEN_SIZE] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
+
+// LC exit token value in OTP secret0 partition.
+static volatile const uint8_t kOtpExitToken[LC_TOKEN_SIZE] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
+
+// LC unlock token value in OTP secret0 partition.
+static volatile const uint8_t kOtpUnlockToken[LC_TOKEN_SIZE] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
+
+static void check_lc_state_transition_count(uint8_t exp_lc_count) {
+ LOG_INFO("Read LC count and check with expect_val=%0d", exp_lc_count);
+ uint8_t lc_count;
+ CHECK_DIF_OK(dif_lc_ctrl_get_attempts(&lc, &lc_count),
+ "Read lc_count register failed!");
+ CHECK(lc_count == exp_lc_count,
+ "LC_count error, expected %0d but actual count is %0d", exp_lc_count,
+ lc_count);
+}
+
+/**
+ * Walkthrough LC state: RAW -> TestUnlock0 -> ProdEnd.
+ *
+ * 1). Preload OTP RAW image file.
+ * 2). DV sequence drives JTAG interface to write RawUnlockToken and
+ * transfers LC state to TestUnlock0 state.
+ * 3). In TestUnlock0 state, SW programs OTP secret0 partition to write
+ * ExitToken and TestUnlockToken.
+ * 4). DV sequence issues reset to lock OTP secret0 partition.
+ * 5). SW requests a LC state transfer to ProdEnd state with the correct
+ * TestLockToken.
+ * 6). DV sequence issues reset and SW check if LC transfers to ProdEnd state.
+ */
+
+bool test_main(void) {
+ LOG_INFO("Start LC walkthrough ProdEnd test.");
+
+ mmio_region_t lc_reg = mmio_region_from_addr(TOP_EARLGREY_LC_CTRL_BASE_ADDR);
+ CHECK_DIF_OK(dif_lc_ctrl_init(lc_reg, &lc));
+
+ mmio_region_t otp_reg =
+ mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR);
+ CHECK_DIF_OK(dif_otp_ctrl_init(otp_reg, &otp));
+
+ LOG_INFO("Read and check LC state and count.");
+ dif_lc_ctrl_state_t curr_state;
+ CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
+
+ if (curr_state == kDifLcCtrlStateTestUnlocked0) {
+ check_lc_state_transition_count(1);
+ bool secret0_locked;
+ CHECK_DIF_OK(dif_otp_ctrl_is_digest_computed(
+ &otp, kDifOtpCtrlPartitionSecret0, &secret0_locked));
+
+ if (secret0_locked == false) {
+ // Write LC tokens to OTP secret0 partition.
+ uint64_t otp_unlock_token_0 = 0;
+ uint64_t otp_unlock_token_1 = 0;
+ for (int i = 0; i < LC_TOKEN_SIZE; i++) {
+ if (i < LC_TOKEN_SIZE / 2) {
+ otp_unlock_token_0 =
+ otp_unlock_token_0 | ((uint64_t)kOtpUnlockToken[i] << (i * 8));
+ } else {
+ otp_unlock_token_1 =
+ otp_unlock_token_1 |
+ ((uint64_t)kOtpUnlockToken[i] << ((i - LC_TOKEN_SIZE / 2) * 8));
+ }
+ }
+
+ uint64_t otp_exit_token_0 = 0;
+ uint64_t otp_exit_token_1 = 0;
+ for (int i = 0; i < LC_TOKEN_SIZE; i++) {
+ if (i < LC_TOKEN_SIZE / 2) {
+ otp_exit_token_0 =
+ otp_exit_token_0 | ((uint64_t)kOtpExitToken[i] << (i * 8));
+ } else {
+ otp_exit_token_1 =
+ otp_exit_token_1 |
+ ((uint64_t)kOtpExitToken[i] << ((i - LC_TOKEN_SIZE / 2) * 8));
+ }
+ }
+
+ CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
+ /*address=*/0x0,
+ /*value=*/otp_unlock_token_0));
+ otp_ctrl_testutils_wait_for_dai(&otp);
+
+ CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
+ /*address=*/0x8,
+ /*value=*/otp_unlock_token_1));
+ otp_ctrl_testutils_wait_for_dai(&otp);
+
+ CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
+ /*address=*/0x10,
+ /*value=*/otp_exit_token_0));
+ otp_ctrl_testutils_wait_for_dai(&otp);
+
+ CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
+ /*address=*/0x18,
+ /*value=*/otp_exit_token_1));
+ otp_ctrl_testutils_wait_for_dai(&otp);
+
+ CHECK_DIF_OK(dif_otp_ctrl_dai_digest(&otp, kDifOtpCtrlPartitionSecret0,
+ /*digest=*/0));
+ otp_ctrl_testutils_wait_for_dai(&otp);
+
+ LOG_INFO("Written and locked OTP secret0 partition!");
+ return true;
+ } else {
+ // Issue a LC state transfer to ProdEnd state.
+ dif_lc_ctrl_token_t token;
+ for (int i = 0; i < LC_TOKEN_SIZE; i++) {
+ token.data[i] = kLcExitToken[i];
+ }
+ CHECK_DIF_OK(dif_lc_ctrl_mutex_try_acquire(&lc));
+ dif_lc_ctrl_settings_t settings;
+ // TODO: randomize using external or internal clock.
+ settings.clock_select = kDifLcCtrlExternalClockEn;
+ CHECK_DIF_OK(dif_lc_ctrl_transition(&lc, kDifLcCtrlStateProdEnd, &token,
+ &settings),
+ "LC_transition failed!");
+
+ LOG_INFO("Waiting for LC transtition done and reboot.");
+ return true;
+ }
+ } else {
+ // Check LC enters ProdEnd state.
+ CHECK(curr_state == kDifLcCtrlStateProdEnd);
+ check_lc_state_transition_count(2);
+ return true;
+ }
+}
diff --git a/sw/device/tests/sim_dv/meson.build b/sw/device/tests/sim_dv/meson.build
index a1cc74f..9e90694 100644
--- a/sw/device/tests/sim_dv/meson.build
+++ b/sw/device/tests/sim_dv/meson.build
@@ -156,6 +156,30 @@
}
}
+lc_walkthrough_test_lib = declare_dependency(
+ link_with: static_library(
+ 'lc_walkthrough_test_lib',
+ sources: [
+ 'lc_walkthrough_test.c',
+ ],
+ dependencies: [
+ sw_lib_dif_lc_ctrl,
+ sw_lib_dif_otp_ctrl,
+ sw_lib_mmio,
+ sw_lib_runtime_hart,
+ sw_lib_runtime_log,
+ sw_lib_testing_otp_ctrl_testutils,
+ sw_lib_testing_lc_ctrl_testutils,
+ top_earlgrey,
+ ],
+ ),
+)
+sw_tests += {
+ 'lc_walkthrough_test': {
+ 'library': lc_walkthrough_test_lib,
+ }
+}
+
pwrmgr_main_power_glitch_test_lib = declare_dependency(
link_with: static_library(
'pwrmgr_main_power_glitch_test_lib',