[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',