blob: 9a5e0560c1dabd565e113b82bc2a45d399f1d7eb [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class chip_sw_flash_init_vseq extends chip_sw_base_vseq;
`uvm_object_utils(chip_sw_flash_init_vseq)
`uvm_object_new
localparam string FLASH_DATA_KEY_PATH =
"tb.dut.top_earlgrey.u_flash_ctrl.u_flash_hw_if.data_key_o";
localparam string FLASH_ADDR_KEY_PATH =
"tb.dut.top_earlgrey.u_flash_ctrl.u_flash_hw_if.addr_key_o";
localparam string FLASH_INIT_BUSY_PATH =
"tb.dut.top_earlgrey.u_flash_ctrl.u_flash_hw_if.init_busy_o";
localparam string KEYMGR_SEEDS_PATH = "tb.dut.top_earlgrey.u_flash_ctrl.u_flash_hw_if.seeds_o";
localparam string FLASH_LC_SEED_HW_RD_EN_PATH =
"tb.dut.top_earlgrey.u_flash_ctrl.lc_seed_hw_rd_en_i";
localparam string SRAM_CTRL_RET_HDL_PATH =
"tb.dut.top_earlgrey.u_sram_ctrl_ret_aon.u_prim_ram_1p_scr";
localparam string SRAM_CTRL_RET_NONCE_PATH = {SRAM_CTRL_RET_HDL_PATH, ".nonce_i"};
localparam string SRAM_CTRL_RET_KEY_PATH = {SRAM_CTRL_RET_HDL_PATH, ".key_i"};
localparam uint KeyWidthAddrBits = otp_ctrl_reg_pkg::FlashAddrKeySeedSize * 8;
localparam uint KeyWidthAddrBytes = KeyWidthAddrBits / 8;
localparam uint KeyWidthDataBits = otp_ctrl_reg_pkg::FlashDataKeySeedSize * 8;
localparam uint KeyWidthDataBytes = KeyWidthDataBits / 8;
localparam uint KeyWidthSramBits = otp_ctrl_reg_pkg::SramDataKeySeedSize * 8;
localparam uint KeyWidthSramBytes = KeyWidthSramBits / 8;
localparam uint SEED_WIDTH = flash_ctrl_pkg::SeedWidth;
localparam uint FLASH_PAGE_SIZE_BYTES = flash_ctrl_reg_pkg::BytesPerPage;
localparam uint FLASH_PAGES_PER_BANK = flash_ctrl_reg_pkg::RegPagesPerBank;
localparam uint CREATOR_SECRET_PAGE_ID = flash_ctrl_pkg::CreatorInfoPage;
localparam uint OWNER_SECRET_PAGE_ID = flash_ctrl_pkg::OwnerInfoPage;
localparam uint ISO_PART_PAGE_ID = flash_ctrl_pkg::IsolatedInfoPage;
localparam uint NUM_TEST_WORDS = 16;
typedef enum {
UnscrambledTest[0:2],
ScrambledTest[0:1],
BackdoorTest[0:1],
KeyMgrPrep,
KeyMgrTest[0:1],
LastPhase
} test_phases_e;
bit [sram_scrambler_pkg::SRAM_BLOCK_WIDTH-1:0] sram_ret_nonce;
bit [sram_scrambler_pkg::SRAM_KEY_WIDTH-1:0] sram_ret_key;
rand bit [7:0] secret_flash_addr_key[KeyWidthAddrBytes];
rand bit [7:0] secret_flash_data_key[KeyWidthDataBytes];
rand bit [7:0] secret_sram_key[KeyWidthSramBytes];
rand bit [31:0] creator_secret_data[NUM_TEST_WORDS];
rand bit [31:0] owner_secret_data[NUM_TEST_WORDS];
rand bit [31:0] iso_part_data[NUM_TEST_WORDS];
rand bit [31:0] bank0_page0_data[NUM_TEST_WORDS];
rand bit [31:0] bank1_page0_data[NUM_TEST_WORDS];
rand bit [otp_ctrl_reg_pkg::CreatorRootKeyShare0Size*8-1:0] root_key0;
rand bit [otp_ctrl_reg_pkg::CreatorRootKeyShare0Size*8-1:0] root_key1;
bit [FlashKeyWidth-1:0] flash_data_key;
bit [FlashKeyWidth-1:0] flash_addr_key;
bit write_scrambled;
bit do_keymgr_check;
event init_done_event;
typedef enum int {
DV_EXP_RMA,
DV_EXP_PROD
} exp_lc_st_e;
exp_lc_st_e exp_lc_st;
virtual task ret_backdoor_data_write();
`DV_CHECK(uvm_hdl_read(SRAM_CTRL_RET_NONCE_PATH, sram_ret_nonce));
`DV_CHECK(uvm_hdl_read(SRAM_CTRL_RET_KEY_PATH, sram_ret_key));
for (int i = 0; i < NUM_TEST_WORDS; i++) begin
ret_sram_bkdr_write32((i * 4), creator_secret_data[i], sram_ret_key, sram_ret_nonce);
ret_sram_bkdr_write32((NUM_TEST_WORDS * 4) + (i * 4), owner_secret_data[i], sram_ret_key,
sram_ret_nonce);
ret_sram_bkdr_write32((NUM_TEST_WORDS * 8) + (i * 4), iso_part_data[i], sram_ret_key,
sram_ret_nonce);
ret_sram_bkdr_write32((NUM_TEST_WORDS * 12) + (i * 4), bank0_page0_data[i], sram_ret_key,
sram_ret_nonce);
ret_sram_bkdr_write32((NUM_TEST_WORDS * 16) + (i * 4), bank1_page0_data[i], sram_ret_key,
sram_ret_nonce);
end
endtask
virtual task randomize_data();
`DV_CHECK_STD_RANDOMIZE_FATAL(creator_secret_data)
`DV_CHECK_STD_RANDOMIZE_FATAL(owner_secret_data)
`DV_CHECK_STD_RANDOMIZE_FATAL(iso_part_data)
`DV_CHECK_STD_RANDOMIZE_FATAL(bank0_page0_data)
`DV_CHECK_STD_RANDOMIZE_FATAL(bank1_page0_data)
`DV_CHECK_STD_RANDOMIZE_FATAL(root_key0)
`DV_CHECK_STD_RANDOMIZE_FATAL(root_key1)
endtask
virtual function bit [KeyWidthAddrBits-1:0] get_flash_otp_key(
bit [7:0] key_in[KeyWidthAddrBytes]);
bit [kmac_pkg::AppDigestW-1:0] digest_bits;
bit [7:0] dpi_digest[kmac_pkg::AppDigestW/8];
digestpp_dpi_pkg::c_dpi_cshake128(key_in, "", "FLASH_CTRL", KeyWidthAddrBytes,
kmac_pkg::AppDigestW / 8, dpi_digest);
digest_bits = {<<byte{dpi_digest}};
return (digest_bits[KeyWidthAddrBits-1:0]);
endfunction
virtual function bit [KeyWidthSramBits-1:0] get_sram_otp_key(
bit [7:0] key_in[KeyWidthSramBytes]);
bit [kmac_pkg::AppDigestW-1:0] digest_bits;
bit [7:0] dpi_digest[kmac_pkg::AppDigestW/8];
digestpp_dpi_pkg::c_dpi_cshake128(key_in, "", "SRAM_CTRL", KeyWidthSramBytes,
kmac_pkg::AppDigestW / 8, dpi_digest);
digest_bits = {<<byte{dpi_digest}};
return (digest_bits[KeyWidthSramBits-1:0]);
endfunction
virtual task randomize_keys();
`DV_CHECK_STD_RANDOMIZE_FATAL(secret_flash_addr_key)
`DV_CHECK_STD_RANDOMIZE_FATAL(secret_flash_data_key)
cfg.mem_bkdr_util_h[Otp].otp_write_secret1_partition(
.flash_addr_key_seed(get_flash_otp_key(secret_flash_addr_key)),
.flash_data_key_seed(get_flash_otp_key(secret_flash_data_key)),
.sram_data_key_seed(get_sram_otp_key(secret_sram_key)));
endtask
virtual task calculate_and_write_scrambled();
bit [15:0] base_addr_bytes;
for (int i = 0; i < NUM_TEST_WORDS / 2; i++) begin
base_addr_bytes = 16'h0;
cfg.mem_bkdr_util_h[FlashBank0Data].flash_write_scrambled(
{bank0_page0_data[(i*2)+1], bank0_page0_data[i*2]}, base_addr_bytes + (i * 8),
flash_addr_key, flash_data_key);
base_addr_bytes = FLASH_PAGE_SIZE_BYTES * FLASH_PAGES_PER_BANK;
cfg.mem_bkdr_util_h[FlashBank1Data].flash_write_scrambled(
{bank1_page0_data[(i*2)+1], bank1_page0_data[i*2]}, base_addr_bytes + (i * 8),
flash_addr_key, flash_data_key);
base_addr_bytes = FLASH_PAGE_SIZE_BYTES * CREATOR_SECRET_PAGE_ID;
cfg.mem_bkdr_util_h[FlashBank0Info].flash_write_scrambled(
{creator_secret_data[(i*2)+1], creator_secret_data[i*2]}, base_addr_bytes + (i * 8),
flash_addr_key, flash_data_key);
base_addr_bytes = FLASH_PAGE_SIZE_BYTES * OWNER_SECRET_PAGE_ID;
cfg.mem_bkdr_util_h[FlashBank0Info].flash_write_scrambled(
{owner_secret_data[(i*2)+1], owner_secret_data[i*2]}, base_addr_bytes + (i * 8),
flash_addr_key, flash_data_key);
base_addr_bytes = FLASH_PAGE_SIZE_BYTES * ISO_PART_PAGE_ID;
cfg.mem_bkdr_util_h[FlashBank0Info].flash_write_scrambled(
{iso_part_data[(i*2)+1], iso_part_data[i*2]}, base_addr_bytes + (i * 8), flash_addr_key,
flash_data_key);
end
endtask
virtual task check_hdl_paths();
int retval;
retval = uvm_hdl_check_path(FLASH_DATA_KEY_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", FLASH_DATA_KEY_PATH))
retval = uvm_hdl_check_path(FLASH_ADDR_KEY_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", FLASH_ADDR_KEY_PATH))
retval = uvm_hdl_check_path(FLASH_INIT_BUSY_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", FLASH_INIT_BUSY_PATH))
retval = uvm_hdl_check_path(KEYMGR_SEEDS_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", KEYMGR_SEEDS_PATH))
retval = uvm_hdl_check_path(FLASH_LC_SEED_HW_RD_EN_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", FLASH_LC_SEED_HW_RD_EN_PATH))
retval = uvm_hdl_check_path(SRAM_CTRL_RET_NONCE_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", SRAM_CTRL_RET_NONCE_PATH))
retval = uvm_hdl_check_path(SRAM_CTRL_RET_KEY_PATH);
`DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
"Hierarchical path %0s appears to be invalid.", SRAM_CTRL_RET_KEY_PATH))
endtask
virtual task wait_init_done();
int retval;
bit init_busy;
bit init_busy_last_value;
forever begin
init_busy_last_value = init_busy;
retval = uvm_hdl_read(FLASH_INIT_BUSY_PATH, init_busy);
`DV_CHECK_EQ(retval, 1, $sformatf("uvm_hdl_read failed for %0s", FLASH_INIT_BUSY_PATH))
if (init_busy == 0 && init_busy_last_value == 1) begin
->init_done_event;
end
// Use top level clock to advance time between samples.
cfg.clk_rst_vif.wait_clks(1);
end
endtask
virtual task get_keys();
int retval;
forever begin
@(init_done_event);
if (write_scrambled == 1) begin
retval = uvm_hdl_read(FLASH_DATA_KEY_PATH, flash_data_key);
`DV_CHECK_EQ(retval, 1, $sformatf("uvm_hdl_read failed for %0s", FLASH_DATA_KEY_PATH))
retval = uvm_hdl_read(FLASH_ADDR_KEY_PATH, flash_addr_key);
`DV_CHECK_EQ(retval, 1, $sformatf("uvm_hdl_read failed for %0s", FLASH_ADDR_KEY_PATH))
calculate_and_write_scrambled();
end
end
endtask
virtual task check_keymgr_seeds();
int retval;
bit [(SEED_WIDTH*2)-1:0] seeds;
bit [SEED_WIDTH-1:0] owner_seed;
bit [SEED_WIDTH-1:0] creator_seed;
bit [(SEED_WIDTH*2)-1:0] expected_owner_seed;
bit [(SEED_WIDTH*2)-1:0] expected_creator_seed;
bit [lc_ctrl_pkg::TxWidth-1:0] lc_seed_hw_rd_en;
bit [(DecLcStateNumRep*DecLcStateWidth)-1:0] lc_state;
dec_lc_state_e dec_lc_state;
forever begin
@(init_done_event);
if (do_keymgr_check == 1) begin
expected_owner_seed = {<<32{owner_secret_data}};
expected_creator_seed = {<<32{creator_secret_data}};
retval = uvm_hdl_read(FLASH_LC_SEED_HW_RD_EN_PATH, lc_seed_hw_rd_en);
`DV_CHECK_EQ(retval, 1, $sformatf("uvm_hdl_read failed for %0s", FLASH_LC_SEED_HW_RD_EN_PATH
))
retval = uvm_hdl_read(KEYMGR_SEEDS_PATH, seeds);
`DV_CHECK_EQ(retval, 1, $sformatf("uvm_hdl_read failed for %0s", KEYMGR_SEEDS_PATH))
owner_seed = seeds[(SEED_WIDTH*flash_ctrl_pkg::OwnerSeedIdx)+:SEED_WIDTH];
creator_seed = seeds[(SEED_WIDTH*flash_ctrl_pkg::CreatorSeedIdx)+:SEED_WIDTH];
// Check 'lc_seed_hw_rd_en'
// This can be set to 'on' only if
// 1. lc_state = RMA or
// 2. otp.secret0_digest != 0 && otp.secret2_digest != 0.
// and lc state should be one of {DEV, PROD, PROD_END}
`uvm_info("SEQ", $sformatf("EXP LC STATE: %s", exp_lc_st.name), UVM_MEDIUM)
if (exp_lc_st == DV_EXP_RMA) begin
`DV_CHECK(lc_seed_hw_rd_en, lc_ctrl_pkg::On)
end else begin
bit [63:0] secret0_digest, secret2_digest;
csr_peek(ral.otp_ctrl_core.secret0_digest[0], secret0_digest[31:0]);
csr_peek(ral.otp_ctrl_core.secret0_digest[1], secret0_digest[63:32]);
csr_peek(ral.otp_ctrl_core.secret2_digest[0], secret2_digest[31:0]);
csr_peek(ral.otp_ctrl_core.secret2_digest[1], secret2_digest[63:32]);
`uvm_info("SEQ", $sformatf("read secret digest: digest0:0x%16x digest2:0x%16x",
secret0_digest, secret2_digest), UVM_MEDIUM)
if (secret0_digest == 0 || secret2_digest == 0) begin
`DV_CHECK_NE(lc_seed_hw_rd_en, lc_ctrl_pkg::On)
end else begin
`DV_CHECK_EQ(lc_seed_hw_rd_en, lc_ctrl_pkg::On)
end
end
// If 'lc_seed_hw_rd_en' is off, creator/owner seed cannot be updated and
// retains the default value.
// Make sure your bus is the right size cause
// full 64bit bus might contain x's.
if (lc_seed_hw_rd_en == lc_ctrl_pkg::Off) begin
`DV_CHECK_EQ(creator_seed,
top_earlgrey_rnd_cnst_pkg::RndCnstFlashCtrlAllSeeds[SEED_WIDTH-1:0]);
`DV_CHECK_EQ(owner_seed,
top_earlgrey_rnd_cnst_pkg::RndCnstFlashCtrlAllSeeds[SEED_WIDTH +: SEED_WIDTH]);
end else begin
`DV_CHECK_EQ(creator_seed, expected_creator_seed[SEED_WIDTH-1:0]);
`DV_CHECK_EQ(owner_seed, expected_owner_seed[SEED_WIDTH-1:0]);
end
end
end
endtask
virtual task body();
bit [7:0] array_data[1];
bit random_data = 0;
super.body();
check_hdl_paths();
cfg.disable_d_user_data_intg_check_for_passthru_mem = 1;
cfg.en_scb_mem_chk = 0;
// Write randomly generated data to retention SRAM for use in SW.
ret_backdoor_data_write();
// Fork the tasks for monitoring the key updates and
// checking the keymgr seed update where applicable.
fork
wait_init_done();
get_keys();
check_keymgr_seeds();
join_none
// Allow most test phases to write seed partition, but do not allow hardware to read
cfg.mem_bkdr_util_h[Otp].otp_write_lc_partition_state(LcStProd);
cfg.mem_bkdr_util_h[Otp].write64(otp_ctrl_reg_pkg::Secret2DigestOffset, 0);
// Looping through all test phases.
for (int current_phase = UnscrambledTest0; current_phase <= LastPhase; current_phase++) begin
// Backdoor overwrite the SW variable for the test phase.
array_data[0] = current_phase;
sw_symbol_backdoor_overwrite("kTestPhase", array_data, SwTypeRom);
`uvm_info(`gfn, $sformatf("Testing %d", current_phase), UVM_LOW)
if (current_phase < LastPhase) begin
do_keymgr_check = 0;
write_scrambled = 0;
random_data = 0;
case (current_phase)
UnscrambledTest0, UnscrambledTest1, UnscrambledTest2,
ScrambledTest0, ScrambledTest1: begin
random_data = 1;
end
BackdoorTest0, BackdoorTest1: begin
write_scrambled = 1;
random_data = 1;
end
KeyMgrPrep: begin
write_scrambled = 1;
end
KeyMgrTest0: begin
do_keymgr_check = 1;
`uvm_info(`gfn, $sformatf("Lock OTP secret2 partition and enable seed reading"),
UVM_LOW)
// The actual data is irrelevant as long as the partition becomes locked.
cfg.mem_bkdr_util_h[Otp].otp_write_secret2_partition(
.rma_unlock_token('0), .creator_root_key0('0), .creator_root_key1('0));
end
KeyMgrTest1: begin
`uvm_info(`gfn, $sformatf("Zero secret2 partition and disable seed reading"),
UVM_LOW)
cfg.mem_bkdr_util_h[Otp].write64(otp_ctrl_reg_pkg::Secret2DigestOffset, 0);
do_keymgr_check = 1;
end
default:;
endcase // case (current_phase)
// Wait for the test to start and enter the WFI state,
`uvm_info(`gfn, $sformatf("Wating to be in test"), UVM_MEDIUM)
`DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest)
`uvm_info(`gfn, $sformatf("Wating to be see WFI"), UVM_MEDIUM)
`DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInWfi)
// Randomize the data and keys for the next test phase.
if (random_data) begin
randomize_data();
randomize_keys();
ret_backdoor_data_write();
end
apply_reset();
cfg.clk_rst_vif.wait_clks(1000);
end else begin
// final phase cleanup.
disable fork;
end
end
endtask
endclass