blob: ec7c6e30fbe3546dd471aba4490eb49a12852565 [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_base_vseq #(
type RAL_T = chip_ral_pkg::chip_reg_block
) extends cip_base_vseq #(
.CFG_T (chip_env_cfg),
.RAL_T (RAL_T),
.COV_T (chip_env_cov),
.VIRTUAL_SEQUENCER_T(chip_virtual_sequencer)
);
`uvm_object_utils(chip_base_vseq)
jtag_dmi_reg_block jtag_dmi_ral;
// knobs to enable pre_start routines
// knobs to enable post_start routines
// various knobs to enable certain routines
// Local queue for holding received UART TX data.
byte uart_tx_data_q[$];
`uvm_object_new
virtual function void set_handles();
super.set_handles();
jtag_dmi_ral = cfg.jtag_dmi_ral;
endfunction // set_handles
task post_start();
do_clear_all_interrupts = 0;
super.post_start();
endtask
virtual task apply_reset(string kind = "HARD");
lc_ctrl_state_pkg::lc_state_e lc_state;
callback_vseq.pre_apply_reset();
// Note: The JTAG reset does not have a dedicated pad and is muxed with other chip IOs.
// These IOs have pad attributes that are driven from registers, and as long as
// the reset line of those registers is X, the registers and hence the pad outputs
// will also be X. This causes the JTAG reset to not properly propagate, and hence we
// have to assert the main reset before that (release can happen in a randomized way
// via the apply_reset task later on).
// assert_por_reset();
`uvm_info(`gfn, "Asserting POR_N", UVM_LOW)
cfg.chip_vif.por_n_if.drive(0);
#10us; // TODO: revisit this.
cfg.chip_vif.por_n_if.drive(1);
`uvm_info(`gfn, "POR_N complete", UVM_LOW)
// TODO: Cannot assert different types of resets in parallel; due to randomization
// resets de-assert at different times. If the main rst_n de-asserts before others,
// the CPU starts executing right away which can cause breakages.
cfg.m_jtag_riscv_agent_cfg.m_jtag_agent_cfg.vif.do_trst_n();
super.apply_reset(kind);
if (jtag_dmi_ral != null) jtag_dmi_ral.reset(kind);
callback_vseq.post_apply_reset();
endtask
virtual task apply_resets_concurrently(int reset_duration_ps = 0);
cfg.chip_vif.por_n_if.drive(0);
// At least 6 AON clock cycles.
// TODO: Tentatively this is set to 100us. Fetch the individual clock freqs from AST via
// chip_vif.
reset_duration_ps = max2(reset_duration_ps, 100_000_000 /* 100us */);
super.apply_resets_concurrently(reset_duration_ps);
cfg.chip_vif.por_n_if.drive(1);
endtask
chip_callback_vseq callback_vseq;
// This task populates the ROM with random but correctly scrambled and ECC encoded data with a
// valid KMAC digest at the end. It is used to ensure the ROM check can pass successfully, without
// having to rely on a ROM software build prior to running the simulation.
virtual function void random_rom_init_with_digest();
bit [TL_DW-1:0] rnd_data;
`uvm_info(`gfn, "Random ROM init with digest", UVM_MEDIUM)
// Randomize the memory contents.
//
// We can't just use the mem_bkdr_util randomize_mem function because that doesn't obey the
// scrambling key. This wouldn't be a problem (the memory is supposed to be random!), except
// that we also need to pick ECC values that match.
for (int addr = 0; addr < RomMaxCheckAddr; addr += TL_DW/8) begin
`DV_CHECK_STD_RANDOMIZE_FATAL(rnd_data)
cfg.mem_bkdr_util_h[Rom].rom_encrypt_write32_integ(
addr,
rnd_data,
top_earlgrey_rnd_cnst_pkg::RndCnstRomCtrlScrKey,
top_earlgrey_rnd_cnst_pkg::RndCnstRomCtrlScrNonce,
1'b1); // Enable scrambling.
end
// Update the ROM digest.
cfg.mem_bkdr_util_h[Rom].update_rom_digest(
top_earlgrey_rnd_cnst_pkg::RndCnstRomCtrlScrKey,
top_earlgrey_rnd_cnst_pkg::RndCnstRomCtrlScrNonce);
endfunction
// Iniitializes the DUT.
//
// Initializes DUT inputs, internal memories, etc., brings the DUT out of reset (performs a reset
// cycle) and performs immediate post-reset steps to prime the design for stimulus. The
// base class method invoked by super.dut_init() applies the reset.
virtual task dut_init(string reset_kind = "HARD");
bit otp_clear_hw_cfg, otp_clear_secret0, otp_clear_secret1, otp_clear_secret2;
// Connect the external clock source if the test needs it.
//
// TODO: This is a functional interface which should ideally be connected only in the extended
// test sequences. Revisit this later.
callback_vseq.pre_dut_init();
if (cfg.chip_clock_source != ChipClockSourceInternal) begin
`uvm_info(`gfn, {"Connecting and driving external clock source with frequency ",
$sformatf("%0dMhz", cfg.chip_clock_source)}, UVM_LOW)
cfg.chip_vif.ext_clk_if.set_active(.drive_clk_val(1), .drive_rst_n_val(0));
cfg.chip_vif.ext_clk_if.set_freq_mhz(cfg.chip_clock_source);
end
// Connect DIOs
cfg.chip_vif.enable_spi_host = 1;
// Initialize all memories via backdoor.
cfg.mem_bkdr_util_h[FlashBank0Info].set_mem();
cfg.mem_bkdr_util_h[FlashBank1Info].set_mem();
// Backdoor load the OTP image.
cfg.mem_bkdr_util_h[Otp].load_mem_from_file(cfg.otp_images[cfg.use_otp_image]);
// Plusargs to selectively clear the provisioning state of some of the OTP partitions.
// This is useful in tests that make front-door accesses for provisioning purposes.
void'($value$plusargs("otp_clear_hw_cfg=%0d", otp_clear_hw_cfg));
void'($value$plusargs("otp_clear_secret0=%0d", otp_clear_secret0));
void'($value$plusargs("otp_clear_secret1=%0d", otp_clear_secret1));
void'($value$plusargs("otp_clear_secret2=%0d", otp_clear_secret2));
if (otp_clear_hw_cfg) begin
cfg.mem_bkdr_util_h[Otp].otp_clear_hw_cfg_partition();
end
if (otp_clear_secret0) begin
cfg.mem_bkdr_util_h[Otp].otp_clear_secret0_partition();
end
if (otp_clear_secret1) begin
cfg.mem_bkdr_util_h[Otp].otp_clear_secret1_partition();
end
if (otp_clear_secret2) begin
cfg.mem_bkdr_util_h[Otp].otp_clear_secret2_partition();
end
initialize_otp_sig_verify();
initialize_otp_creator_sw_cfg_ast_cfg();
// Initialize selected memories to all 0. This is required for some chip-level tests such as
// otbn_mem_scramble that may intentionally read memories before writing them. Reading these
// memories still triggeres ECC integrity errors that need to be handled by the test.
cfg.mem_bkdr_util_h[OtbnImem].clear_mem();
for (int ram_idx = 0; ram_idx < cfg.num_otbn_dmem_tiles; ram_idx++) begin
cfg.mem_bkdr_util_h[chip_mem_e'(OtbnDmem0 + ram_idx)].clear_mem();
end
// Bring the chip out of reset.
super.dut_init(reset_kind);
alert_ping_en_shorten();
callback_vseq.post_dut_init();
endtask
virtual task dut_shutdown();
// check for pending chip operations and wait for them to complete
// TODO
endtask
virtual task wait_rom_check_done();
// The CSR tests (handled by this class) need to wait until the lc_ctrl has initialized and
// rom_ctrl block has finished running KMAC before they can start issuing reads and writes.
// Otherwise, they might write to a KMAC register while KMAC is in operation, or access a
// register that is gated by life cycle.
// This would either generate an error or have no effect and a subsequent read
// from the register would show a mismatched value. We handle this by considering rom_ctrl's
// operation as "part of reset".
// Same for the test that uses jtag to access CSRs. We need to wait until rom check is done.
//
// This function is meant to be called once the base class reset is finished.
// Use backdoor, so that this task can be used with or without stub mode enabled.
`uvm_info(`gfn, "waiting for rom_ctrl after reset", UVM_MEDIUM)
csr_spinwait(.ptr(ral.rom_ctrl_regs.digest[0]), .exp_data(0), .compare_op(CompareOpNe),
.backdoor(1), .spinwait_delay_ns(1000));
`uvm_info(`gfn, "rom_ctrl check done after reset", UVM_HIGH)
csr_spinwait(.ptr(ral.lc_ctrl.status.ready), .exp_data(1), .backdoor(1),
.spinwait_delay_ns(1000));
`uvm_info(`gfn, "lc_ctrl has been initialized", UVM_HIGH)
endtask
virtual task pre_start();
// Do DUT init after some additional settings.
bit do_dut_init_save = do_dut_init;
do_dut_init = 1'b0;
`uvm_create_on(callback_vseq, p_sequencer);
`DV_CHECK_RANDOMIZE_FATAL(callback_vseq)
super.pre_start();
// Randomize the ROM image with valid ECC and digest. Subclasses that have an actual ROM image
// will load a "real" ROM image later. If the ROM integrity check is disabled, no digest needs
// to be calculated and we can just randomize the memory.
`ifdef DISABLE_ROM_INTEGRITY_CHECK
cfg.mem_bkdr_util_h[Rom].randomize_mem();
`else
random_rom_init_with_digest();
`endif
do_dut_init = do_dut_init_save;
// Now safe to do DUT init.
if (do_dut_init) dut_init();
endtask
// Configures and connects the UART agent for driving data over RX and receiving data on TX.
//
// Note that to fetch packets over the TX port, the get_uart_tx_items() task needs to be called
// separately as a forked thread in the test sequence.
//
// uart_idx: The UART instance.
// enable: 1: enable (configures and connects), 0: disable (disables sampling and disconnects)
// enable_tx_monitor: Enable sampling data on the TX port (default on).
// enable_rx_monitor: Enable sampling data on the RX port (default off).
// en_parity: Enable parity when driving RX traffic.
// odd_parity: Compute odd parity when driving RX traffic.
// baud_rate: The baud rate.
virtual function void configure_uart_agent(int uart_idx,
bit enable,
bit enable_tx_monitor = 1'b1,
bit enable_rx_monitor = 1'b0,
bit en_parity = 1'b0,
bit odd_parity = 1'b0,
baud_rate_e baud_rate = cfg.uart_baud_rate);
if (enable) begin
`uvm_info(`gfn, $sformatf("Configuring and connecting UART%0d", uart_idx), UVM_LOW)
cfg.m_uart_agent_cfgs[uart_idx].set_parity(en_parity, odd_parity);
cfg.m_uart_agent_cfgs[uart_idx].set_baud_rate(cfg.uart_baud_rate);
cfg.m_uart_agent_cfgs[uart_idx].en_tx_monitor = enable_tx_monitor;
cfg.m_uart_agent_cfgs[uart_idx].en_rx_monitor = enable_rx_monitor;
cfg.chip_vif.enable_uart(uart_idx, 1);
end else begin
`uvm_info(`gfn, $sformatf("Disconnecting UART%0d", uart_idx), UVM_LOW)
cfg.m_uart_agent_cfgs[uart_idx].en_tx_monitor = 0;
cfg.m_uart_agent_cfgs[uart_idx].en_rx_monitor = 0;
cfg.chip_vif.enable_uart(uart_idx, 0);
end
endfunction
// Grab packets sent by the DUT over the UART TX port.
virtual task get_uart_tx_items(int uart_idx = 0);
uart_item item;
forever begin
p_sequencer.uart_tx_fifos[uart_idx].get(item);
`uvm_info(`gfn, $sformatf("Received UART data over TX:\n%0h", item.data), UVM_HIGH)
uart_tx_data_q.push_back(item.data);
end
endtask
// Shorten the alert handler ping timer wait cycles.
//
// This is done to speed up the simulation while achieving coverage on alert pings to various
// blocks.
// TODO; plusargs should be sought in a singla place (we do it in the base test class).
// TODO: Nothing may happen after calling this function, becuase it internally fetches a plusarg
// which can result in a nop. Refactor this later.
task alert_ping_en_shorten();
bit shorten_ping_en;
void'($value$plusargs("shorten_ping_en=%0d", shorten_ping_en));
if (shorten_ping_en) begin
void'(cfg.chip_vif.signal_probe_alert_handler_ping_timer_wait_cyc_mask_i(
SignalProbeForce, 16'h3F));
end
endtask : alert_ping_en_shorten
// Initialize the OTP creator SW cfg region to use otbn for signature verification.
virtual function void initialize_otp_sig_verify();
// Use otbn mod_exp implementation for signature
// verification. See the definition of `hardened_bool_t` in
// sw/device/lib/base/hardened.h.
cfg.mem_bkdr_util_h[Otp].write32(otp_ctrl_reg_pkg::CreatorSwCfgSigverifyRsaModExpIbexEnOffset,
32'h1d4);
endfunction : initialize_otp_sig_verify
// Initialize the OTP creator SW cfg region with AST configuration data.
virtual function void initialize_otp_creator_sw_cfg_ast_cfg();
// The knob controls whether the AST is actually programmed.
if (cfg.do_creator_sw_cfg_ast_cfg) begin
cfg.mem_bkdr_util_h[Otp].write32(otp_ctrl_reg_pkg::CreatorSwCfgAstInitEnOffset,
prim_mubi_pkg::MuBi4True);
end
// Ensure that the allocated size of the AST cfg region in OTP is equal to the number of AST
// registers to be programmed.
`DV_CHECK_EQ_FATAL(otp_ctrl_reg_pkg::CreatorSwCfgAstCfgSize, ast_pkg::AstRegsNum * 4)
foreach (cfg.creator_sw_cfg_ast_cfg_data[i]) begin
`uvm_info(`gfn, $sformatf(
"OTP: Preloading creator_sw_cfg_ast_cfg_data[%0d] with 0x%0h via backdoor",
i,
cfg.creator_sw_cfg_ast_cfg_data[i]
), UVM_MEDIUM)
cfg.mem_bkdr_util_h[Otp].write32(otp_ctrl_reg_pkg::CreatorSwCfgAstCfgOffset + i * 4,
cfg.creator_sw_cfg_ast_cfg_data[i]);
end
endfunction
// Set the ROM_EXEC_EN bit in OTP if we are not in RAW state
virtual function void set_otp_creator_sw_cfg_rom_exec_en(bit [31:0] value);
lc_ctrl_state_pkg::lc_state_e lc_state;
logic [31:0] otp_raw_val;
logic [31:0] chk_vector;
// Set rom_exec_en only when we are not in RAW state.
lc_state = cfg.mem_bkdr_util_h[Otp].otp_read_lc_partition_state();
// If we are already 1, we cannot set to 0.
// This should probably be relocated to mem_bkdr_util eventually as an option for writes
otp_raw_val = cfg.mem_bkdr_util_h[Otp].read32(otp_ctrl_reg_pkg::CreatorSwCfgRomExecEnOffset);
chk_vector = ~value & (otp_raw_val ^ value);
`DV_CHECK(chk_vector == '0);
if (lc_state != LcStRaw) begin
`uvm_info(`gfn, "Automatically set rom_exec_en", UVM_LOW)
cfg.mem_bkdr_util_h[Otp].write32(otp_ctrl_reg_pkg::CreatorSwCfgRomExecEnOffset, value);
end
endfunction
task test_mem_rw(uvm_mem mem, int max_access = 2048);
int unsigned expected_data[int unsigned];
int offmax = mem.get_size() - 1;
int sizemax = offmax / 4;
`uvm_info(`gfn, $sformatf("Mem writes to %s %0d times", mem.get_full_name(), max_access),
UVM_MEDIUM)
for (int i = 0; i < max_access; ++i) begin
int unsigned offset = $urandom_range(sizemax, 0);
int unsigned wdata = $urandom();
expected_data[offset] = wdata;
`uvm_info(`gfn, $sformatf("Writing 0x%x to offset 0x%x in %s", wdata, offset, mem.get_name()),
UVM_MEDIUM)
if (mem.get_access() == "RW") begin
mem_wr(.ptr(mem), .offset(offset), .data(wdata));
end else begin // if (mem.get_access() == "RW")
// deposit random data to rom
int byte_addr = offset * 4;
cfg.mem_bkdr_util_h[Rom].rom_encrypt_write32_integ(
.addr(byte_addr), .data(wdata), .key(RndCnstRomCtrlScrKey),
.nonce(RndCnstRomCtrlScrNonce), .scramble_data(1));
end
end
`uvm_info(`gfn, $sformatf("Writes to %s is complete, read back start...", mem.get_full_name()),
UVM_MEDIUM)
foreach (expected_data[address]) begin
int unsigned rdata;
int unsigned exp_data = expected_data[address];
mem_rd(.ptr(mem), .offset(address), .data(rdata));
`DV_CHECK_EQ(rdata, exp_data, $sformatf(
"read back check for offset 0x%x failed, got 0x%x, expected 0x%x",
address, rdata, exp_data))
end
`uvm_info(`gfn, $sformatf("read check from %s is complete", mem.get_full_name()),
UVM_MEDIUM)
endtask : test_mem_rw
// POR_N needs to stay asserted for at least 6 AON clock cycles.
task assert_por_reset(int delay = 0);
repeat (delay) @cfg.chip_vif.pwrmgr_low_power_if.fast_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
endclass : chip_base_vseq