blob: 5a27d3278cad2ef916e4d832d34124e939d41b7a [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_base_vseq extends chip_base_vseq;
`uvm_object_utils(chip_sw_base_vseq)
// Default only iterate through SW code once.
constraint num_trans_c {
num_trans == 1;
}
`uvm_object_new
virtual task pre_start();
super.pre_start();
// Disable mem checks in scoreboard - it does not factor in memory scrambling.
cfg.en_scb_mem_chk = 1'b0;
endtask
virtual task dut_init(string reset_kind = "HARD");
// Reset the sw_test_status.
cfg.sw_test_status_vif.sw_test_status = SwTestStatusUnderReset;
// Initialize the SW strap pins - these are sort of dedicated.
// TODO: add logic to drive / undrive this only when the ROM / test ROM code is active.
cfg.chip_vif.sw_straps_if.drive({3{cfg.use_spi_load_bootstrap}});
// Bring the chip out of reset.
super.dut_init(reset_kind);
endtask
// Initialize the chip to enable SW to boot up and execute code.
//
// Backdoor load the sw test image, initialize memories, sw logger and test status interfaces.
// Note that this function is called the moment POR_N asserts. The chip resources including the
// CPU are brought out of reset much later, after the pwrmgr has gone through the wakeup sequence.
// Invoke cfg.chip_vif.cpu_clk_rst_vif.wait_for_reset() to bring the simulation to the point where
// the CPU is out of reset and ready to execute code.
virtual task cpu_init();
int size_bytes;
int total_bytes;
`uvm_info(`gfn, "Starting cpu_init", UVM_MEDIUM)
// Initialize the sw logger interface.
foreach (cfg.sw_images[i]) begin
if (i inside {SwTypeRom, SwTypeDebug, SwTypeTestSlotA, SwTypeTestSlotB}) begin
cfg.sw_logger_vif.add_sw_log_db(cfg.sw_images[i]);
end
end
cfg.sw_logger_vif.sw_log_addr = SW_DV_LOG_ADDR;
cfg.sw_logger_vif.write_sw_logs_to_file = cfg.write_sw_logs_to_file;
cfg.sw_logger_vif.ready();
// Initialize the sw test status.
cfg.sw_test_status_vif.sw_test_status_addr = SW_DV_TEST_STATUS_ADDR;
`uvm_info(`gfn, "Initializing SRAMs", UVM_MEDIUM)
// Assume each tile contains the same number of bytes.
size_bytes = cfg.mem_bkdr_util_h[chip_mem_e'(RamMain0)].get_size_bytes();
total_bytes = size_bytes * cfg.num_ram_main_tiles;
// Randomize the main SRAM.
for (int addr = 0; addr < total_bytes; addr = addr + 4) begin
bit [31:0] rand_val;
`DV_CHECK_STD_RANDOMIZE_FATAL(rand_val, "Randomization failed!")
main_sram_bkdr_write32(addr, rand_val);
end
// Initialize the data partition in all flash banks to all 1s.
`uvm_info(`gfn, "Initializing flash banks (data partition only)", UVM_MEDIUM)
cfg.mem_bkdr_util_h[FlashBank0Data].set_mem();
cfg.mem_bkdr_util_h[FlashBank1Data].set_mem();
// Randomize retention memory. This is done intentionally with wrong integrity
// as early portions of ROM will initialize it to the correct value.
// The randomization here is just to ensure we do not have x's in the memory.
for (int ram_idx = 0; ram_idx < cfg.num_ram_ret_tiles; ram_idx++) begin
cfg.mem_bkdr_util_h[chip_mem_e'(RamRet0 + ram_idx)].randomize_mem();
end
`uvm_info(`gfn, "Initializing ROM", UVM_MEDIUM)
// Backdoor load memories with sw images.
`ifdef DISABLE_ROM_INTEGRITY_CHECK
cfg.mem_bkdr_util_h[Rom].load_mem_from_file({cfg.sw_images[SwTypeRom], ".32.vmem"});
`else
cfg.mem_bkdr_util_h[Rom].load_mem_from_file({cfg.sw_images[SwTypeRom], ".39.scr.vmem"});
`endif
if (cfg.sw_images.exists(SwTypeTestSlotA)) begin
if (cfg.use_spi_load_bootstrap) begin
`uvm_info(`gfn, "Initializing SPI flash bootstrap", UVM_MEDIUM)
spi_device_load_bootstrap({cfg.sw_images[SwTypeTestSlotA], ".64.vmem"});
end else begin
cfg.mem_bkdr_util_h[FlashBank0Data].load_mem_from_file(
{cfg.sw_images[SwTypeTestSlotA], ".64.scr.vmem"});
end
end
if (cfg.sw_images.exists(SwTypeTestSlotB)) begin
// TODO: support bootstrapping entire flash address space, not just slot A.
cfg.mem_bkdr_util_h[FlashBank1Data].load_mem_from_file(
{cfg.sw_images[SwTypeTestSlotB], ".64.scr.vmem"});
end
config_jitter();
`uvm_info(`gfn, "cpu_init completed", UVM_MEDIUM)
endtask
// The jitter enable mechanism is different from test_rom and rom right now.
// That's why below there is both a symbol overwrite and an otp backdoor load.
// Once test_rom and rom are consistent in this area, the symbol backdoor load
// can be removed.
task config_jitter();
bit en_jitter;
void'($value$plusargs("en_jitter=%0d", en_jitter));
if (en_jitter) begin
// enable for test_rom
bit [7:0] en_jitter_arr[] = {1};
sw_symbol_backdoor_overwrite("kJitterEnabled", en_jitter_arr, SwTypeRom);
// enable for rom
cfg.mem_bkdr_util_h[Otp].write32(otp_ctrl_reg_pkg::CreatorSwCfgJitterEnOffset,
prim_mubi_pkg::MuBi4True);
end else begin
// rom blindly copies from otp, backdoor load a false value
cfg.mem_bkdr_util_h[Otp].write32(otp_ctrl_reg_pkg::CreatorSwCfgJitterEnOffset,
prim_mubi_pkg::MuBi4False);
end
endtask
virtual function void main_sram_bkdr_write32(
bit [bus_params_pkg::BUS_AW-1:0] addr,
bit [31:0] data,
bit [sram_scrambler_pkg::SRAM_KEY_WIDTH-1:0] key = RndCnstSramCtrlMainSramKey,
bit [sram_scrambler_pkg::SRAM_BLOCK_WIDTH-1:0] nonce = RndCnstSramCtrlMainSramNonce,
bit [38:0] flip_bits = '0);
_sram_bkdr_write32(addr, data, 1, key, nonce, flip_bits);
endfunction
virtual function void ret_sram_bkdr_write32(
bit [bus_params_pkg::BUS_AW-1:0] addr,
bit [31:0] data,
bit [sram_scrambler_pkg::SRAM_KEY_WIDTH-1:0] key = RndCnstSramCtrlRetAonSramKey,
bit [sram_scrambler_pkg::SRAM_BLOCK_WIDTH-1:0] nonce = RndCnstSramCtrlRetAonSramNonce,
bit [38:0] flip_bits = '0);
_sram_bkdr_write32(addr, data, 0, key, nonce, flip_bits);
endfunction
// scrambled address may cross the tile, this function will find out what tile the address is
// located and backdoor write to it.
protected function void _sram_bkdr_write32(
bit [bus_params_pkg::BUS_AW-1:0] addr,
bit [31:0] data,
bit is_main_ram, // if 1, main ram, otherwise, ret ram
bit [sram_scrambler_pkg::SRAM_KEY_WIDTH-1:0] key,
bit [sram_scrambler_pkg::SRAM_BLOCK_WIDTH-1:0] nonce,
bit [38:0] flip_bits);
chip_mem_e mem;
int num_tiles;
bit [31:0] addr_scr;
bit [38:0] data_scr;
bit [31:0] addr_mask;
int tile_idx;
int size_bytes;
// Use the 1st tile of the RAM for now. Based on the scrambled address, will find out which
// tile to write.
if (is_main_ram) begin
mem = RamMain0;
num_tiles = cfg.num_ram_main_tiles;
end else begin
mem = RamRet0;
num_tiles = cfg.num_ram_ret_tiles;
end
// Assume each tile contains the same number of bytes
size_bytes = cfg.mem_bkdr_util_h[mem].get_size_bytes();
addr_mask = size_bytes - 1;
// calculate the scramble address
addr_scr = cfg.mem_bkdr_util_h[mem].get_sram_encrypt_addr(
addr, nonce, $clog2(num_tiles));
// determine which tile the scrambled address belongs
tile_idx = addr_scr / size_bytes;
// calculate the scrambled data
data_scr = cfg.mem_bkdr_util_h[mem].get_sram_encrypt32_intg_data(
addr, data, key, nonce,
$clog2(num_tiles));
// write the scrambled data into the targetted memory tile
mem = chip_mem_e'(mem + tile_idx);
cfg.mem_bkdr_util_h[mem].write39integ(addr_scr & addr_mask, data_scr ^ flip_bits);
endfunction
virtual task body();
cfg.sw_test_status_vif.set_num_iterations(num_trans);
// Initialize the CPU to kick off the sw test. TODO: Should be called in pre_start() instead.
cpu_init();
endtask
virtual task post_start();
super.post_start();
// Wait for sw test to finish before exiting.
wait_for_sw_test_done();
endtask
// Monitors the SW test status.
virtual task wait_for_sw_test_done();
`uvm_info(`gfn, "Waiting for the SW test to finish", UVM_MEDIUM)
fork
begin: isolation_thread
fork
wait (cfg.sw_test_status_vif.sw_test_done);
#(cfg.sw_test_timeout_ns * 1ns);
join_any
disable fork;
log_sw_test_status();
end: isolation_thread
join
endtask
// Print pass / fail message to the log.
virtual function void log_sw_test_status();
case (cfg.sw_test_status_vif.sw_test_status)
SwTestStatusPassed: `uvm_info(`gfn, "SW TEST PASSED!", UVM_LOW)
SwTestStatusFailed: `uvm_error(`gfn, "SW TEST FAILED!")
default: begin
// If the SW test has not reached the passed / failed state, then it timed out.
`uvm_error(`gfn, $sformatf("SW TEST TIMED OUT. STATE: %0s, TIMEOUT = %0d ns\n",
cfg.sw_test_status_vif.sw_test_status.name(), cfg.sw_test_timeout_ns))
end
endcase
endfunction
// Configure the provided spi_agent_cfg to use flash mode, and add the
// specification for the following common commands:
// ReadSFDP, ReadStatus1, WriteEnable, ChipErase, and PageProgram.
virtual function void spi_agent_configure_flash_cmds(spi_agent_cfg agent_cfg);
spi_flash_cmd_info info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashReadSfdp;
info.num_lanes = 1;
info.dummy_cycles = 8;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashReadJedec;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashReadSts1;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashReadSts2;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashReadSts3;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrCfg;
info.opcode = SpiFlashReadNormal;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrCfg;
info.opcode = SpiFlashReadFast;
info.num_lanes = 1;
info.dummy_cycles = 8;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrCfg;
info.opcode = SpiFlashReadDual;
info.num_lanes = 2;
info.dummy_cycles = 8;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrCfg;
info.opcode = SpiFlashReadQuad;
info.num_lanes = 4;
info.dummy_cycles = 8;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashWriteEnable;
info.num_lanes = 0;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashWriteSts1;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 1;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashWriteSts2;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 1;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashWriteSts3;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 1;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrDisabled;
info.opcode = SpiFlashChipErase;
info.num_lanes = 0;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrCfg;
info.opcode = SpiFlashSectorErase;
info.num_lanes = 0;
info.dummy_cycles = 0;
info.write_command = 0;
agent_cfg.add_cmd_info(info);
info = spi_flash_cmd_info::type_id::create("info");
info.addr_mode = SpiFlashAddrCfg;
info.opcode = SpiFlashPageProgram;
info.num_lanes = 1;
info.dummy_cycles = 0;
info.write_command = 1;
agent_cfg.add_cmd_info(info);
agent_cfg.spi_func_mode = SpiModeFlash;
endfunction
// Periodically probe the device for its busy bit and wait for up to
// `timeout_ns` nanoseconds for it to be de-asserted. Commands sent to the
// device will be spaced no less than `min_interval_ns` nanoseconds (with a
// random additional delay). In some cases, using a longer interval can speed
// up simulation.
virtual task spi_host_wait_on_busy(
uint timeout_ns = default_spinwait_timeout_ns,
uint min_interval_ns = 1000);
spi_host_flash_seq m_spi_host_seq;
`uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h)
`DV_SPINWAIT(
while (1) begin
cfg.clk_rst_vif.wait_clks($urandom_range(1, 100));
`DV_CHECK_RANDOMIZE_WITH_FATAL(m_spi_host_seq,
opcode == SpiFlashReadSts1;
address_q.size() == 0;
payload_q.size() == 1;
read_size == 1;)
`uvm_send(m_spi_host_seq)
// bit 0 is busy bit
if (m_spi_host_seq.rsp.payload_q[0][0] === 0) break;
#(min_interval_ns);
end,
,
timeout_ns
)
endtask
// Performs the write command sequence on the spi_host agent, with
// a WriteEnable command, followed by the specified command from the
// `write_command`, then polling for the busy bit to clear. `busy_timeout_ns`
// and `busy_poll_interval_ns` work similarly as the parameters for
// `spi_host_wait_on_busy`.
virtual task spi_host_flash_issue_write_cmd(
spi_host_flash_seq write_command,
uint busy_timeout_ns = default_spinwait_timeout_ns,
uint busy_poll_interval_ns = 1000);
spi_host_flash_seq m_spi_host_seq;
`uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h)
m_spi_host_seq.opcode = SpiFlashWriteEnable;
`uvm_send(m_spi_host_seq);
`uvm_send(write_command);
spi_host_wait_on_busy(busy_timeout_ns, busy_poll_interval_ns);
endtask
// Load the flash binary specified by the `sw_image` path by sending a chip
// erase, then programming pages in sequence via the SPI flash interface
// presented by the ROM. Afterwards, bring the software straps back to 0,
// and issue a power-on reset.
// The `sw_image` path should point to an image usable by the
// `read_sw_frames` task.
// This task assumes the device was booted with software straps set before
// entry. In addition, it expects that the spi_agent was connected to the
// spi_device and is ready to issue flash transactions.
virtual task spi_device_load_bootstrap(string sw_image);
spi_host_flash_seq m_spi_host_seq;
byte sw_byte_q[$];
uint bytes_to_write;
uint byte_cnt = 0;
uint SPI_FLASH_PAGE_SIZE = 256;
// Set CSB inactive times to reasonable values. sys_clk is at 24 MHz, and
// it needs to capture CSB pulses.
cfg.m_spi_host_agent_cfg.min_idle_ns_after_csb_drop = 50;
cfg.m_spi_host_agent_cfg.max_idle_ns_after_csb_drop = 200;
// Configure the spi_agent for flash mode and add command info.
spi_agent_configure_flash_cmds(cfg.m_spi_host_agent_cfg);
// Wait for the commands to be ready
csr_spinwait(
.ptr(ral.spi_device.cmd_info[spi_device_pkg::CmdInfoReadSfdp].opcode),
.exp_data(SpiFlashReadSfdp),
.backdoor(1),
.spinwait_delay_ns(5000));
csr_spinwait(
.ptr(ral.spi_device.cmd_info[spi_device_pkg::CmdInfoReadStatus1].opcode),
.exp_data(SpiFlashReadSts1),
.backdoor(1),
.spinwait_delay_ns(5000));
csr_spinwait(
.ptr(ral.spi_device.cmd_info_wren.opcode),
.exp_data(SpiFlashWriteEnable),
.backdoor(1),
.spinwait_delay_ns(5000));
read_sw_frames(sw_image, sw_byte_q);
`uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h)
m_spi_host_seq.opcode = SpiFlashChipErase;
spi_host_flash_issue_write_cmd(
.write_command(m_spi_host_seq),
.busy_timeout_ns(40_000_000),
.busy_poll_interval_ns(1_000_000));
while (sw_byte_q.size > byte_cnt) begin
`uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h)
m_spi_host_seq.opcode = SpiFlashPageProgram;
m_spi_host_seq.address_q = {byte_cnt[23:16], byte_cnt[15:8], byte_cnt[7:0]};
if (SPI_FLASH_PAGE_SIZE < (sw_byte_q.size() - byte_cnt)) begin
bytes_to_write = SPI_FLASH_PAGE_SIZE;
end else begin
bytes_to_write = sw_byte_q.size() - byte_cnt;
end
for (int i = 0; i < bytes_to_write; i++) begin
m_spi_host_seq.payload_q.push_back(sw_byte_q[byte_cnt + i]);
end
spi_host_flash_issue_write_cmd(m_spi_host_seq);
byte_cnt += bytes_to_write;
end
cfg.chip_vif.sw_straps_if.drive(3'h0);
assert_por_reset();
endtask
// Read the flash image pointed to by the `sw_image` path, and place the
// data into the `sw_byte_q`. The flash image is assumed to consist of
// contiguous data starting from the base of flash.
virtual function void read_sw_frames(string sw_image, ref byte sw_byte_q[$]);
int num_returns;
int mem_fd = $fopen(sw_image, "r");
bit [63:0] word_data[4];
string addr;
while (!$feof(mem_fd)) begin
num_returns = $fscanf(mem_fd, "%s %h %h %h %h", addr, word_data[0], word_data[1],
word_data[2], word_data[3]);
if (num_returns <= 1) continue;
for (int i = 0; i < num_returns - 1; i++) begin
repeat (8) begin
sw_byte_q.push_back(word_data[i][7:0]);
word_data[i] = word_data[i] >> 8;
end
end
end
$fclose(mem_fd);
endfunction
// Backdoor-read or override a const symbol in SW to modify the behavior of the test.
//
// In the extended test vseq, override the cpu_init() to add this function call.
// TODO: bootstrap mode not supported.
// TODO: Need to deal with scrambling.
virtual function void sw_symbol_backdoor_access(input string symbol,
inout bit [7:0] data[],
input sw_type_e sw_type = SwTypeTestSlotA,
input bit does_not_exist_ok = 0,
input bit is_write = 0);
bit [bus_params_pkg::BUS_AW-1:0] addr, mem_addr;
chip_mem_e mem;
uint size;
uint addr_mask;
string image;
bit ret;
// Elf file name checks.
`DV_CHECK_FATAL(cfg.sw_images.exists(sw_type))
`DV_CHECK_STRNE_FATAL(cfg.sw_images[sw_type], "")
// Find the symbol in the sw elf file.
image = $sformatf("%0s.elf", cfg.sw_images[sw_type]);
ret = dv_utils_pkg::sw_symbol_get_addr_size(image, symbol, does_not_exist_ok, addr, size);
if (!ret) begin
string msg = $sformatf("Failed to find symbol %0s in %0s", symbol, image);
if (does_not_exist_ok) begin
`uvm_info(`gfn, msg, UVM_LOW)
return;
end else `uvm_fatal(`gfn, msg)
end
`DV_CHECK_EQ_FATAL(size, data.size())
// Infer mem from address.
`DV_CHECK(cfg.get_mem_from_addr(addr, mem))
`DV_CHECK_FATAL(mem inside {Rom, [RamMain0:RamMain15], FlashBank0Data, FlashBank1Data},
$sformatf("SW symbol %0s is not expected to appear in %0s mem", symbol, mem))
addr_mask = (2**$clog2(cfg.mem_bkdr_util_h[mem].get_size_bytes()))-1;
mem_addr = addr & addr_mask;
if (is_write) begin
`uvm_info(`gfn, $sformatf({"Overwriting symbol \"%s\" via backdoor in %0s: ",
"abs addr = 0x%0h, mem addr = 0x%0h, size = %0d, ",
"addr_mask = 0x%0h"},
symbol, mem, addr, mem_addr, size, addr_mask), UVM_LOW)
for (int i = 0; i < size; i++) mem_bkdr_write8(mem, mem_addr + i, data[i]);
// TODO: Move this specialization to an extended class called rom_bkdr_util.
if (mem == Rom) begin
`uvm_info(`gfn, "Regenerate ROM digest and update via backdoor", UVM_LOW)
cfg.mem_bkdr_util_h[mem].update_rom_digest(RndCnstRomCtrlScrKey, RndCnstRomCtrlScrNonce);
end
end else begin
`uvm_info(`gfn, $sformatf({"Reading symbol \"%s\" via backdoor in %0s: ",
"abs addr = 0x%0h, mem addr = 0x%0h, size = %0d, ",
"addr_mask = 0x%0h"},
symbol, mem, addr, mem_addr, size, addr_mask), UVM_LOW)
for (int i = 0; i < size; i++) mem_bkdr_read8(mem, mem_addr + i, data[i]);
end
endfunction
// Backdoor-read a const symbol in SW to make decisions based on SW constants.
//
// Wrapper function for reads via sw_symbol_backdoor_access.
virtual function void sw_symbol_backdoor_read(input string symbol,
inout bit [7:0] data[],
input sw_type_e sw_type = SwTypeTestSlotA,
input bit does_not_exist_ok = 0);
sw_symbol_backdoor_access(symbol, data, sw_type, does_not_exist_ok, 0);
endfunction
// Backdoor-override a const symbol in SW to modify the behavior of the test.
//
// Wrapper function for writes via sw_symbol_backdoor_access.
virtual function void sw_symbol_backdoor_overwrite(input string symbol,
input bit [7:0] data[],
input sw_type_e sw_type = SwTypeTestSlotA,
input bit does_not_exist_ok = 0);
sw_symbol_backdoor_access(symbol, data, sw_type, does_not_exist_ok, 1);
endfunction
// General-use function to backdoor write a byte of data to any selected memory type
//
// TODO: Add support for tiled RAM memories.
virtual function void mem_bkdr_write8(input chip_mem_e mem,
input bit [bus_params_pkg::BUS_AW-1:0] addr,
input byte data);
byte prev_data;
// TODO: Move these specializations to extended classes so that no special handling is needed at
// the call site.
if (mem == Rom) begin
bit [127:0] key = RndCnstRomCtrlScrKey;
bit [63:0] nonce = RndCnstRomCtrlScrNonce;
prev_data = cfg.mem_bkdr_util_h[mem].rom_encrypt_read8(addr, key, nonce);
cfg.mem_bkdr_util_h[mem].rom_encrypt_write8(addr, data, key, nonce);
end else begin // flash
prev_data = cfg.mem_bkdr_util_h[mem].read8(addr);
cfg.mem_bkdr_util_h[mem].write8(addr, data);
end
`uvm_info(`gfn, $sformatf("addr %0h = 0x%0h --> 0x%0h", addr, prev_data, data), UVM_HIGH)
endfunction
// General-use function to backdoor read a byte of data from any selected memory type
//
// TODO: Add support for tiled RAM memories.
virtual function void mem_bkdr_read8(input chip_mem_e mem,
input bit [bus_params_pkg::BUS_AW-1:0] addr,
output byte data);
// TODO: Move these specializations to extended classes so that no special handling is needed at
// the call site.
if (mem == Rom) begin
bit [127:0] key = RndCnstRomCtrlScrKey;
bit [63:0] nonce = RndCnstRomCtrlScrNonce;
data = cfg.mem_bkdr_util_h[mem].rom_encrypt_read8(addr, key, nonce);
end else begin // flash
data = cfg.mem_bkdr_util_h[mem].read8(addr);
end
`uvm_info(`gfn, $sformatf("addr %0h = 0x%0h", addr, data), UVM_HIGH)
endfunction
// LC state transition tasks
// This function takes the token value from the four LC_CTRL token CSRs, then runs through
// cshake128 to get a 768-bit XORed token output.
// The first 128 bits of the decoded token should match the OTP partition's descrambled tokens
// value.
virtual function bit [TokenWidthBit-1:0] dec_otp_token_from_lc_csrs(
bit [7:0] token_in[TokenWidthByte]);
bit [7:0] dpi_digest[kmac_pkg::AppDigestW/8];
bit [kmac_pkg::AppDigestW-1:0] digest_bits;
digestpp_dpi_pkg::c_dpi_cshake128(token_in, "", "LC_CTRL", TokenWidthByte,
kmac_pkg::AppDigestW/8, dpi_digest);
digest_bits = {<< byte {dpi_digest}};
return (digest_bits[TokenWidthBit-1:0]);
endfunction
// LC_CTRL JTAG tasks
virtual task wait_lc_status(lc_ctrl_status_e expect_status, int max_attempt = 5000);
int i;
for (i = 0; i < max_attempt; i++) begin
bit [TL_DW-1:0] status_val;
lc_ctrl_status_e dummy;
cfg.clk_rst_vif.wait_clks($urandom_range(0, 10));
jtag_riscv_agent_pkg::jtag_read_csr(ral.lc_ctrl.status.get_offset(),
p_sequencer.jtag_sequencer_h,
status_val);
// Ensure that none of the other status bits are set.
`DV_CHECK_EQ(status_val >> dummy.num(), 0,
$sformatf("Unexpected status error %0h", status_val))
if (status_val[expect_status]) begin
`uvm_info(`gfn, $sformatf("LC status %0s.", expect_status.name), UVM_LOW)
break;
end
end
if (i >= max_attempt) begin
`uvm_fatal(`gfn, $sformatf("max attempt reached to get lc status %0s!", expect_status.name))
end
endtask
virtual task wait_lc_initialized(bit allow_err = 1, int max_attempt = 5000);
cfg.m_jtag_riscv_agent_cfg.allow_errors = allow_err;
wait_lc_status(LcInitialized, max_attempt);
cfg.m_jtag_riscv_agent_cfg.allow_errors = 0;
endtask
virtual task wait_lc_ready(bit allow_err = 1, int max_attempt = 5000);
cfg.m_jtag_riscv_agent_cfg.allow_errors = allow_err;
wait_lc_status(LcReady, max_attempt);
cfg.m_jtag_riscv_agent_cfg.allow_errors = 0;
endtask
virtual task wait_lc_transition_successful(bit allow_err = 1, int max_attempt = 5000);
cfg.m_jtag_riscv_agent_cfg.allow_errors = allow_err;
wait_lc_status(LcTransitionSuccessful, max_attempt);
cfg.m_jtag_riscv_agent_cfg.allow_errors = 0;
endtask
// Use JTAG interface to transit LC_CTRL from one state to the valid next state.
// Currently support the following transitions:
// 1). RAW state -> test unlock state N
// This transition will use default raw unlock token.
// 2). Test lock state N -> test unlock state N+1
// This transition requires user to input the correct test unlock token.
virtual task jtag_lc_state_transition(dec_lc_state_e src_state,
dec_lc_state_e dest_state,
bit [TokenWidthBit-1:0] test_unlock_token = 0);
bit [TL_DW-1:0] actual_src_state;
bit valid_transition;
int max_attempt;
// Check that the LC controller is ready to accept a transition.
wait_lc_ready();
jtag_riscv_agent_pkg::jtag_read_csr(ral.lc_ctrl.lc_state.get_offset(),
p_sequencer.jtag_sequencer_h,
actual_src_state);
`DV_CHECK_EQ({DecLcStateNumRep{src_state}}, actual_src_state)
// Check if the requested transition is valid.
case (src_state)
DecLcStRaw: begin
if (dest_state inside {DecLcStTestUnlocked0, DecLcStTestUnlocked1, DecLcStTestUnlocked2,
DecLcStTestUnlocked3, DecLcStTestUnlocked4, DecLcStTestUnlocked5,
DecLcStTestUnlocked6, DecLcStTestUnlocked7}) begin
valid_transition = 1;
test_unlock_token = RndCnstRawUnlockToken;
end else if (dest_state == DecLcStScrap) begin
// This transition is unconditional and can use test_unlock_token = 0.
valid_transition = 1;
end
end
DecLcStTestLocked0: begin
if (dest_state inside {DecLcStTestUnlocked1, DecLcStTestUnlocked2, DecLcStTestUnlocked3,
DecLcStTestUnlocked4, DecLcStTestUnlocked5, DecLcStTestUnlocked6,
DecLcStTestUnlocked7, DecLcStScrap}) begin
valid_transition = 1;
end
end
DecLcStTestLocked1: begin
if (dest_state inside {DecLcStTestUnlocked2, DecLcStTestUnlocked3, DecLcStTestUnlocked4,
DecLcStTestUnlocked5, DecLcStTestUnlocked6,DecLcStTestUnlocked7,
DecLcStScrap}) begin
valid_transition = 1;
end
end
DecLcStTestLocked2: begin
if (dest_state inside {DecLcStTestUnlocked3, DecLcStTestUnlocked4, DecLcStTestUnlocked5,
DecLcStTestUnlocked6, DecLcStTestUnlocked7, DecLcStScrap}) begin
valid_transition = 1;
end
end
DecLcStTestLocked3: begin
if (dest_state inside {DecLcStTestUnlocked4, DecLcStTestUnlocked5, DecLcStTestUnlocked6,
DecLcStTestUnlocked7, DecLcStScrap}) begin
valid_transition = 1;
end
end
DecLcStTestLocked4: begin
if (dest_state inside {DecLcStTestUnlocked5, DecLcStTestUnlocked6, DecLcStTestUnlocked7,
DecLcStScrap}) begin
valid_transition = 1;
end
end
DecLcStTestLocked5: begin
if (dest_state inside {DecLcStTestUnlocked6, DecLcStTestUnlocked7, DecLcStScrap}) begin
valid_transition = 1;
end
end
DecLcStTestLocked6: begin
if (dest_state inside {DecLcStTestUnlocked7, DecLcStScrap}) valid_transition = 1;
end
DecLcStTestUnlocked0,
DecLcStTestUnlocked1,
DecLcStTestUnlocked2,
DecLcStTestUnlocked3,
DecLcStTestUnlocked4,
DecLcStTestUnlocked5,
DecLcStTestUnlocked6,
DecLcStTestUnlocked7: begin
if (dest_state inside {DecLcStProd, DecLcStScrap}) valid_transition = 1;
end
DecLcStDev,
DecLcStProd: begin
if(dest_state inside {DecLcStRma, DecLcStScrap}) valid_transition = 1;
end
DecLcStProdEnd,
DecLcStRma: begin
if(dest_state inside {DecLcStScrap}) valid_transition = 1;
end
default: `uvm_fatal(`gfn, $sformatf("%0s src state not supported", src_state.name))
endcase
if (!valid_transition) begin
`uvm_fatal(`gfn, $sformatf("invalid state transition request from %0s state to %0s",
src_state.name, dest_state.name))
end
`uvm_info(`gfn, $sformatf("Start LC transition request from %0s state to %0s state",
src_state.name, dest_state.name), UVM_LOW)
jtag_riscv_agent_pkg::jtag_write_csr(ral.lc_ctrl.claim_transition_if.get_offset(),
p_sequencer.jtag_sequencer_h,
prim_mubi_pkg::MuBi8True);
// Write LC state transition token.
begin
bit [TL_DW-1:0] token_csr_vals[4] = {<< 32 {{>> 8 {test_unlock_token}}}};
foreach (token_csr_vals[index]) begin
jtag_riscv_agent_pkg::jtag_write_csr(ral.lc_ctrl.transition_token[index].get_offset(),
p_sequencer.jtag_sequencer_h,
token_csr_vals[index]);
end
end
jtag_riscv_agent_pkg::jtag_write_csr(ral.lc_ctrl.transition_target.get_offset(),
p_sequencer.jtag_sequencer_h,
{DecLcStateNumRep{dest_state}});
jtag_riscv_agent_pkg::jtag_write_csr(ral.lc_ctrl.transition_cmd.get_offset(),
p_sequencer.jtag_sequencer_h,
1);
`uvm_info(`gfn, "Sent LC transition request", UVM_LOW)
// Transitions into RMA take much longer, hence we increase this number.
if (dest_state == DecLcStRma) begin
max_attempt = 50_000;
end else begin
max_attempt = 5_000;
end
wait_lc_transition_successful(.max_attempt(max_attempt));
`uvm_info(`gfn, "LC transition request succeeded successfully!", UVM_LOW)
endtask
// Use JTAG interface to program OTP fields.
virtual task jtag_otp_program32(int addr,
bit [31:0] data);
bit [TL_DW-1:0] status;
bit [TL_DW-1:0] err_mask = 0;
bit idle = 0;
int base_addr = top_earlgrey_pkg::TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR;
jtag_riscv_agent_pkg::jtag_write_csr(base_addr +
ral.otp_ctrl_core.direct_access_address.get_offset(),
p_sequencer.jtag_sequencer_h,
addr);
jtag_riscv_agent_pkg::jtag_write_csr(base_addr +
ral.otp_ctrl_core.direct_access_wdata[0].get_offset(),
p_sequencer.jtag_sequencer_h,
data[31:0]);
jtag_riscv_agent_pkg::jtag_write_csr(base_addr +
ral.otp_ctrl_core.direct_access_cmd.get_offset(),
p_sequencer.jtag_sequencer_h,
1 << ral.otp_ctrl_core.direct_access_cmd.wr.get_lsb_pos());
while (!idle) begin
jtag_riscv_agent_pkg::jtag_read_csr(base_addr +
ral.otp_ctrl_core.status.get_offset(),
p_sequencer.jtag_sequencer_h,
status);
idle = dv_base_reg_pkg::get_field_val(ral.otp_ctrl_core.status.dai_idle, status);
err_mask = ~((1 << ral.otp_ctrl_core.status.dai_idle.get_lsb_pos()) |
(1 << ral.otp_ctrl_core.status.check_pending.get_lsb_pos()));
`uvm_info(`gfn, $sformatf("Waiting for DAI to become idle = 1, actual: %d!", idle),
UVM_MEDIUM)
// If any bits other than dai_idle and check pending are set, error back.
`DV_CHECK((status & err_mask) == '0, "Otp program failed")
end
endtask : jtag_otp_program32
// End the test with status.
//
// SW test code finishes the test sequence usually by returing true or false
// in the `test_main()` function. However, some tests may need vseq to
// finish the tests. For example, `chip_sw_sleep_pin_mio_dio_val` checks the
// PADs output value then finishes the test without waking up the SW again.
//
// If pass is 1, then `sw_test_status` is set to SwTestStatusPassed.
virtual function void override_test_status_and_finish(bit passed);
cfg.sw_test_status_vif.sw_test_status = (passed) ? SwTestStatusPassed
: SwTestStatusFailed;
cfg.sw_test_status_vif.sw_test_done = 1'b 1;
endfunction : override_test_status_and_finish
task assert_por_reset_deep_sleep (int delay = 0);
repeat (delay) @cfg.chip_vif.pwrmgr_low_power_if.cb;
cfg.chip_vif.por_n_if.drive(0);
repeat (6) @cfg.chip_vif.pwrmgr_low_power_if.cb;
cfg.clk_rst_vif.wait_clks(10);
cfg.chip_vif.por_n_if.drive(1);
endtask // assert_por_reset_deep_sleep
// push button 50us;
// this task requires proper sysrst_ctrl config
// see sw/device/tests/pwrmgr_b2b_sleep_reset_test.c
// 'static void prgm_push_button_wakeup()' for example
task push_button();
cfg.chip_vif.pwrb_in_if.drive(0);
#50us;
cfg.chip_vif.pwrb_in_if.drive(1);
endtask // push_button
endclass : chip_sw_base_vseq