[dv/sram] SRAM smoke test, bring to V1 status
This PR implements the smoke test for `sram_ctrl` as laid out in the
testplan.
Note that this PR requires #4794 to be merged first, CI will fail until
then.
This PR also incorporates the changes made by @weicaiyang in #5122 to
fix the RAL HDL hierarchy, and adds the SRAM configs to the nightly
regression.
Finally, this PR also updates the `sram_ctrl` to V1 status as all items
have been completed.
Signed-off-by: Udi Jonnalagadda <udij@google.com>
diff --git a/hw/ip/sram_ctrl/data/sram_ctrl.hjson b/hw/ip/sram_ctrl/data/sram_ctrl.hjson
index f5ebb40..4127c2c 100644
--- a/hw/ip/sram_ctrl/data/sram_ctrl.hjson
+++ b/hw/ip/sram_ctrl/data/sram_ctrl.hjson
@@ -199,6 +199,8 @@
hwqe: "true",
hwext: "true",
regwen: "CTRL_REGWEN"
+ tags: [// avoid writing to CTRL, as this will cause STATUS to be modified
+ "excl:CsrNonInitTests:CsrExclWrite"]
fields: [
{ bits: "0",
name: "RENEW_SCR_KEY",
diff --git a/hw/ip/sram_ctrl/data/sram_ctrl.prj.hjson b/hw/ip/sram_ctrl/data/sram_ctrl.prj.hjson
index 1087e5b..70c3625 100644
--- a/hw/ip/sram_ctrl/data/sram_ctrl.prj.hjson
+++ b/hw/ip/sram_ctrl/data/sram_ctrl.prj.hjson
@@ -5,12 +5,12 @@
{
name: "sram_ctrl",
design_spec: "hw/ip/sram_ctrl/doc",
- dv_doc: "",
+ dv_doc: "hw/ip/sram_ctrl/doc/dv",
hw_checklist: "hw/ip/sram_ctrl/doc/checklist",
- sw_checklist: ""
+ sw_checklist: "",
version: "0.1",
life_stage: "L1",
design_stage: "D2",
- verification_stage: "V0",
+ verification_stage: "V1",
notes: "",
}
diff --git a/hw/ip/sram_ctrl/data/sram_ctrl_base_testplan.hjson b/hw/ip/sram_ctrl/data/sram_ctrl_base_testplan.hjson
index 33f029f..2f7df21 100644
--- a/hw/ip/sram_ctrl/data/sram_ctrl_base_testplan.hjson
+++ b/hw/ip/sram_ctrl/data/sram_ctrl_base_testplan.hjson
@@ -39,14 +39,14 @@
- Perform a number of random memory accesses to the SRAM
- Verify that all memory access succeed even if the scrambling key changes at arbitrary
intervals
- ''
+ '''
milestone: V2
tests: ["{name}_multiple_keys"]
}
{
name: stress_pipeline
desc: '''
- This test is the same as the multiple_keys_test, but we now do a series of back-to-back
+ This test is the same as the multiple_keys_test but we now do a series of back-to-back
memory accesses at each random address in order to create read/write conflicts and
stress the encryption pipeline.
'''
diff --git a/hw/ip/sram_ctrl/doc/checklist.md b/hw/ip/sram_ctrl/doc/checklist.md
index d8fcf51..4359054 100644
--- a/hw/ip/sram_ctrl/doc/checklist.md
+++ b/hw/ip/sram_ctrl/doc/checklist.md
@@ -110,28 +110,28 @@
Type | Item | Resolution | Note/Collaterals
--------------|---------------------------------------|-------------|------------------
-Documentation | [DV_DOC_DRAFT_COMPLETED][] | Not Started |
-Documentation | [DV_PLAN_COMPLETED][] | Not Started |
-Testbench | [TB_TOP_CREATED][] | Not Started |
-Testbench | [PRELIMINARY_ASSERTION_CHECKS_ADDED][]| Not Started |
-Testbench | [SIM_TB_ENV_CREATED][] | Not Started |
-Testbench | [SIM_RAL_MODEL_GEN_AUTOMATED][] | Not Started |
-Testbench | [CSR_CHECK_GEN_AUTOMATED][] | Not Started |
-Testbench | [TB_GEN_AUTOMATED][] | Not Started |
-Tests | [SIM_SMOKE_TEST_PASSING][] | Not Started |
-Tests | [SIM_CSR_MEM_TEST_SUITE_PASSING][] | Not Started |
-Tests | [FPV_MAIN_ASSERTIONS_PROVEN][] | Not Started |
-Tool Setup | [SIM_ALT_TOOL_SETUP][] | Not Started |
-Regression | [SIM_SMOKE_REGRESSION_SETUP][] | Not Started |
-Regression | [SIM_NIGHTLY_REGRESSION_SETUP][] | Not Started |
-Regression | [FPV_REGRESSION_SETUP][] | Not Started |
-Coverage | [SIM_COVERAGE_MODEL_ADDED][] | Not Started |
-Code Quality | [TB_LINT_SETUP][] | Not Started |
-Integration | [PRE_VERIFIED_SUB_MODULES_V1][] | Not Started |
-Review | [DESIGN_SPEC_REVIEWED][] | Not Started |
-Review | [DV_PLAN_REVIEWED][] | Not Started |
-Review | [STD_TEST_CATEGORIES_PLANNED][] | Not Started | Exception (?)
-Review | [V2_CHECKLIST_SCOPED][] | Not Started |
+Documentation | [DV_DOC_DRAFT_COMPLETED][] | Done |
+Documentation | [DV_PLAN_COMPLETED][] | Done |
+Testbench | [TB_TOP_CREATED][] | Done |
+Testbench | [PRELIMINARY_ASSERTION_CHECKS_ADDED][]| Done |
+Testbench | [SIM_TB_ENV_CREATED][] | Done |
+Testbench | [SIM_RAL_MODEL_GEN_AUTOMATED][] | Done |
+Testbench | [CSR_CHECK_GEN_AUTOMATED][] | Done |
+Testbench | [TB_GEN_AUTOMATED][] | N/A |
+Tests | [SIM_SMOKE_TEST_PASSING][] | Done |
+Tests | [SIM_CSR_MEM_TEST_SUITE_PASSING][] | Done |
+Tests | [FPV_MAIN_ASSERTIONS_PROVEN][] | N/A |
+Tool Setup | [SIM_ALT_TOOL_SETUP][] | Done |
+Regression | [SIM_SMOKE_REGRESSION_SETUP][] | Done |
+Regression | [SIM_NIGHTLY_REGRESSION_SETUP][] | Done |
+Regression | [FPV_REGRESSION_SETUP][] | Done |
+Coverage | [SIM_COVERAGE_MODEL_ADDED][] | Done |
+Code Quality | [TB_LINT_SETUP][] | Done |
+Integration | [PRE_VERIFIED_SUB_MODULES_V1][] | Done |
+Review | [DESIGN_SPEC_REVIEWED][] | Done |
+Review | [DV_PLAN_REVIEWED][] | Done |
+Review | [STD_TEST_CATEGORIES_PLANNED][] | Done | Exception (Security/Power/Debug)
+Review | [V2_CHECKLIST_SCOPED][] | Done |
[DV_DOC_DRAFT_COMPLETED]: {{<relref "/doc/project/checklist.md#dv_doc_draft_completed" >}}
[DV_PLAN_COMPLETED]: {{<relref "/doc/project/checklist.md#dv_plan_completed" >}}
diff --git a/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_base_vseq.sv b/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_base_vseq.sv
index 022e0e4..e086082 100644
--- a/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_base_vseq.sv
+++ b/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_base_vseq.sv
@@ -9,17 +9,24 @@
.VIRTUAL_SEQUENCER_T (sram_ctrl_virtual_sequencer)
);
`uvm_object_utils(sram_ctrl_base_vseq)
+ `uvm_object_new
// various knobs to enable certain routines
bit do_sram_ctrl_init = 1'b1;
- `uvm_object_new
-
virtual task dut_init(string reset_kind = "HARD");
super.dut_init();
if (do_sram_ctrl_init) sram_ctrl_init();
endtask
+ virtual task apply_reset(string kind = "HARD");
+ super.apply_reset();
+ cfg.lc_vif.init();
+ if (kind == "HARD") begin
+ cfg.otp_clk_rst_vif.apply_reset();
+ end
+ endtask
+
virtual task dut_shutdown();
// check for pending sram_ctrl operations and wait for them to complete
// TODO
@@ -27,7 +34,58 @@
// setup basic sram_ctrl features
virtual task sram_ctrl_init();
- `uvm_error(`gfn, "FIXME")
+ cfg.mem_bkdr_vif.clear_mem();
+ endtask
+
+ // Request a new scrambling key from the OTP interface.
+ //
+ // Will trigger a request to the KDI push_pull agent.
+ virtual task req_scr_key();
+ csr_wr(.csr(ral.ctrl), .value(1'b1));
+ endtask
+
+ // Task to perform a single SRAM read at the specified location
+ virtual task do_single_read(bit [TL_AW-1:0] addr,
+ bit blocking = $urandom_range(0, 1));
+ logic [TL_DW-1:0] rdata;
+ tl_access(.addr(addr),
+ .data(rdata),
+ .mask(get_rand_contiguous_mask()),
+ .write(1'b0),
+ .blocking(blocking),
+ .tl_sequencer_h(p_sequencer.sram_tl_sequencer_h));
+ endtask
+
+ // Task to perform a single SRAM write at the specified location
+ virtual task do_single_write(bit [TL_AW-1:0] addr,
+ bit [TL_DW-1:0] data,
+ bit blocking = $urandom_range(0, 1));
+ tl_access(.addr(addr),
+ .data(data),
+ .mask(get_rand_contiguous_mask()),
+ .write(1'b1),
+ .blocking(blocking),
+ .tl_sequencer_h(p_sequencer.sram_tl_sequencer_h));
+ endtask
+
+ // Task to perform `num_ops` fully randomized memory transactions.
+ virtual task do_rand_ops(int num_ops,
+ bit blocking = $urandom_range(0, 1));
+ bit [TL_DW-1:0] data;
+ bit [TL_AW-1:0] addr;
+ bit we;
+ repeat (num_ops) begin
+ `DV_CHECK_STD_RANDOMIZE_FATAL(data)
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(we, we inside {0, 1};)
+ `DV_CHECK_STD_RANDOMIZE_FATAL(addr)
+
+ tl_access(.addr(addr),
+ .data(data),
+ .mask(get_rand_contiguous_mask()),
+ .write(we),
+ .blocking(blocking),
+ .tl_sequencer_h(p_sequencer.sram_tl_sequencer_h));
+ end
endtask
endclass : sram_ctrl_base_vseq
diff --git a/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_smoke_vseq.sv b/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_smoke_vseq.sv
index 54a3667..0b60326 100644
--- a/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_smoke_vseq.sv
+++ b/hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_smoke_vseq.sv
@@ -8,8 +8,66 @@
`uvm_object_new
+ // Indicates the number of memory accesses to be performed
+ // before requesting a new scrambling key
+ rand int num_ops;
+
+ // Indicates the number of memory accesses to be performed
+ // when SRAM comes out of reset
+ rand int num_ops_after_reset;
+
+ // An SRAM "transaction" is a full round of:
+ // - Provisioning a new scrambling key from OTP
+ // - Executing a random number of memory accesses to SRAM
+ constraint num_trans_c {
+ num_trans == 1;
+ }
+
+ // TODO: 10_000 iterations takes roughly 150s CPU time during simulation.
+ // If this is too much, modify the constraint.
+ constraint num_ops_c {
+ num_ops dist {
+ [1 : 999] :/ 1,
+ [1000 : 4999] :/ 3,
+ [5000 : 9999] :/ 5,
+ 10_000 :/ 1
+ };
+ }
+
+ // This can be much smaller than `num_ops`, as we only perform some memory accesses
+ // after reset to make sure that things are working normally.
+ constraint num_ops_after_reset_c {
+ num_ops_after_reset inside {[20 : 50]};
+ }
+
task body();
- `uvm_error(`gfn, "FIXME")
+
+ // do some memory transactions right after reset (zeroed key and nonce)
+ `uvm_info(`gfn,
+ $sformatf("Performing %0d random memory accesses after reset!", num_ops_after_reset),
+ UVM_LOW)
+ do_rand_ops(num_ops_after_reset, 1);
+
+ `uvm_info(`gfn, $sformatf("Starting %0d SRAM transactions", num_trans), UVM_LOW)
+ for (int i = 0; i < num_trans; i++) begin
+ `uvm_info(`gfn, $sformatf("iteration: %0d", i), UVM_LOW)
+
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_ops)
+
+ // Request a new scrambling key
+ req_scr_key();
+
+ // wait for a valid KDI transaction to be completed
+ //
+ // STATUS.scr_key_seed_valid return value will be checked by scoreboard
+ csr_spinwait(.ptr(ral.status.scr_key_valid), .exp_data(1'b1));
+
+ // Do some random memory accesses
+ `uvm_info(`gfn,
+ $sformatf("Performing %0d random memory accesses!", num_ops),
+ UVM_LOW)
+ do_rand_ops(num_ops);
+ end
endtask : body
endclass : sram_ctrl_smoke_vseq
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env.sv
index fab5b7b..f84ece9 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env.sv
@@ -25,11 +25,10 @@
if (!uvm_config_db#(virtual clk_rst_if)::get(this, "", "otp_clk_rst_vif", cfg.otp_clk_rst_vif)) begin
`uvm_fatal(`gfn, "failed to get otp_clk_rst_if from uvm_config_db")
end
- // TODO: eventually set the OTP clock to a different frequency
- cfg.otp_clk_rst_vif.set_freq_mhz(cfg.clk_freq_mhz);
+ cfg.otp_clk_rst_vif.set_freq_mhz(cfg.otp_freq_mhz);
// Get the LC interface
- if (!uvm_config_db#(lc_vif)::get(this, "", "lc_vif", cfg.lc_vif)) begin
+ if (!uvm_config_db#(virtual sram_ctrl_lc_if)::get(this, "", "lc_vif", cfg.lc_vif)) begin
`uvm_fatal(`gfn, "failed to get lc_vif from uvm_config_db")
end
@@ -54,6 +53,17 @@
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
+
+ virtual_sequencer.sram_tl_sequencer_h = m_sram_tl_agent.sequencer;
+
+ if (cfg.en_scb) begin
+ // connect SRAM TLUL ports
+ m_sram_tl_agent.monitor.a_chan_port.connect(scoreboard.sram_tl_a_chan_fifo.analysis_export);
+ m_sram_tl_agent.monitor.d_chan_port.connect(scoreboard.sram_tl_d_chan_fifo.analysis_export);
+
+ // connect KDI port
+ m_kdi_agent.monitor.analysis_port.connect(scoreboard.kdi_fifo.analysis_export);
+ end
endfunction
endclass
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cfg.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cfg.sv
index c50fe16..8434aa7 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cfg.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cfg.sv
@@ -15,18 +15,38 @@
// ext interfaces
virtual clk_rst_if otp_clk_rst_vif;
- lc_vif lc_vif;
+ virtual sram_ctrl_lc_if lc_vif;
mem_bkdr_vif mem_bkdr_vif;
+ // otp clk freq
+ rand dv_utils_pkg::clk_freq_mhz_e otp_freq_mhz;
+
virtual function void initialize(bit [31:0] csr_base_addr = '1);
list_of_alerts = sram_ctrl_env_pkg::LIST_OF_ALERTS;
super.initialize(csr_base_addr);
- // Build KDI cfg object
+ ral.set_hdl_path_root("tb.dut.u_sram_ctrl", "BkdrRegPathRtl");
+ ral.set_hdl_path_root("tb.dut.u_sram_ctrl", "BkdrRegPathRtlCommitted");
+ ral.set_hdl_path_root("tb.dut.u_sram_ctrl", "BkdrRegPathRtlShadow");
+
+ // Build KDI cfg object and configure
m_kdi_cfg = push_pull_agent_cfg#(.DeviceDataWidth(KDI_DATA_SIZE))::type_id::create("m_kdi_cfg");
+ m_kdi_cfg.agent_type = push_pull_agent_pkg::PullAgent;
+ m_kdi_cfg.if_mode = dv_utils_pkg::Device;
+
+ // CDC synchronization between OTP and SRAM clock domains requires that the scrambling seed data
+ // should be held for at least a few cycles before it can be safely latched by the SRAM domain.
+ // Easy way to do this is just to force the push_pull_agent to hold the data until the next key
+ // request is sent out.
+ m_kdi_cfg.hold_d_data_until_next_req = 1'b1;
+
+ // KDI interface will never need zero delay mode.
+ // As per SRAM spec, KDI process will generally take around 800 CPU cyclesj
+ m_kdi_cfg.zero_delays.rand_mode(0);
// Build SRAM TL cfg object
m_sram_cfg = tl_agent_cfg::type_id::create("m_sram_cfg");
+ m_sram_cfg.if_mode = dv_utils_pkg::Host;
endfunction
endclass
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cov.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cov.sv
index bc8e78c..3f89812 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cov.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cov.sv
@@ -9,6 +9,7 @@
*/
class sram_ctrl_env_cov extends cip_base_env_cov #(.CFG_T(sram_ctrl_env_cfg));
+
`uvm_component_utils(sram_ctrl_env_cov)
// the base class provides the following handles for use:
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv
index 26d1c01..ef837d8 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv
@@ -31,8 +31,7 @@
parameter int KDI_DATA_SIZE = 1 + otp_ctrl_pkg::SramKeyWidth + otp_ctrl_pkg::SramNonceWidth;
// types
- typedef virtual mem_bkdr_if #(.MEM_ADDR_WIDTH(`SRAM_ADDR_WIDTH),
- .MEM_BYTES_PER_WORD(`SRAM_DATA_WIDTH >> 3)) mem_bkdr_vif;
+ typedef virtual mem_bkdr_if #(.MEM_PARITY(1)) mem_bkdr_vif;
typedef virtual sram_ctrl_lc_if lc_vif;
// package sources
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_lc_if.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_lc_if.sv
index 1d3d584..d488e12 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_lc_if.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_lc_if.sv
@@ -4,4 +4,14 @@
interface sram_ctrl_lc_if;
lc_ctrl_pkg::lc_tx_t lc_esc_en;
+
+ // LC escalation signal must be stable before reset ends
+ task automatic init();
+ lc_esc_en = lc_ctrl_pkg::Off;
+ endtask
+
+ task automatic drive_lc_esc_en(lc_ctrl_pkg::lc_tx_t esc_en);
+ lc_esc_en = esc_en;
+ endtask
+
endinterface
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv
index f70e083..b6f012b 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv
@@ -8,17 +8,58 @@
.COV_T(sram_ctrl_env_cov)
);
`uvm_component_utils(sram_ctrl_scoreboard)
+ `uvm_component_new
// local variables
- // TLM agent fifos
+ typedef struct {
+ // 1 for writes, 0 for reads
+ bit we;
+
+ // TLUL address
+ bit [TL_AW-1:0] addr;
+
+ // Contains either the requested write data
+ // or the read response data
+ bit [TL_DW-1:0] data;
+
+ // only writes are masked, all reads are full-word
+ bit [TL_DBW-1:0] mask;
+
+ // Tag the memory transaction with the appropriate key and nonce,
+ // so that we can keep track even if a new key is requested
+ otp_ctrl_pkg::sram_key_t key;
+ otp_ctrl_pkg::sram_nonce_t nonce;
+
+ } sram_trans_t;
+
+ // TLM agent fifos for the tl_agent connected to the SRAM memory itself
+ uvm_tlm_analysis_fifo #(tl_seq_item) sram_tl_a_chan_fifo;
+ uvm_tlm_analysis_fifo #(tl_seq_item) sram_tl_d_chan_fifo;
+
+ uvm_tlm_analysis_fifo #(push_pull_item#(.DeviceDataWidth(KDI_DATA_SIZE))) kdi_fifo;
// local queues to hold incoming packets pending comparison
- `uvm_component_new
+ // mailbox to send a sram_trans_t struct to the data phase collection,
+ // where read addresses can be collected.
+ mailbox #(sram_trans_t) data_phase_mbox;
+
+ // mailbox that all completed sram_trans_t structs will be pushed into for processing.
+ mailbox #(sram_trans_t) completed_trans_mbox;
+
+ otp_ctrl_pkg::sram_key_t key = sram_ctrl_pkg::RndCnstSramKeyDefault;
+ otp_ctrl_pkg::sram_nonce_t nonce = sram_ctrl_pkg::RndCnstSramNonceDefault;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
+ sram_tl_a_chan_fifo = new("sram_tl_a_chan_fifo", this);
+ sram_tl_d_chan_fifo = new("sram_tl_d_chan_fifo", this);
+
+ kdi_fifo = new("kdi_fifo", this);
+
+ data_phase_mbox = new();
+ completed_trans_mbox = new();
endfunction
function void connect_phase(uvm_phase phase);
@@ -28,9 +69,159 @@
task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
+ process_sram_tl_a_chan_fifo();
+ process_sram_tl_d_chan_fifo();
+ process_kdi_fifo();
+ process_sram_trans();
join_none
endtask
+ virtual task process_sram_tl_a_chan_fifo();
+ tl_seq_item item;
+ sram_trans_t addr_trans;
+ forever begin
+ sram_tl_a_chan_fifo.get(item);
+ if (!cfg.en_scb) continue;
+ `uvm_info(`gfn, $sformatf("Received sram_tl_a_chan item:\n%0s", item.sprint()), UVM_HIGH)
+
+ // TODO: need to ensure that we don't process TLUL errors
+
+ addr_trans.we = item.is_write();
+ addr_trans.addr = item.a_addr;
+ addr_trans.mask = item.a_mask;
+
+ addr_trans.key = key;
+ addr_trans.nonce = nonce;
+
+ // Only set data in address phase if a write is detected
+ if (item.is_write()) begin
+ addr_trans.data = item.a_data;
+ // write the completed transaction to be processed
+ completed_trans_mbox.put(addr_trans);
+ `uvm_info({`gfn, "::process_sram_tl_a_chan_fifo()"},
+ $sformatf("Put COMPLETED_WRITE transaction into completed_trans_mbox: %0p", addr_trans),
+ UVM_HIGH)
+ end else begin
+ // on a read transaction, set the address and then send the incomplete transaction item
+ // to be completed in the data phase
+ data_phase_mbox.put(addr_trans);
+ `uvm_info({`gfn, "::process_sram_tl_a_chan_fifo()"},
+ $sformatf("Put INCOMPLETE_READ transaction into data_phase_mbox: %0p", addr_trans),
+ UVM_HIGH)
+ end
+ end
+ endtask
+
+ virtual task process_sram_tl_d_chan_fifo();
+ tl_seq_item item;
+ sram_trans_t data_trans;
+ forever begin
+ sram_tl_d_chan_fifo.get(item);
+ if (!cfg.en_scb) continue;
+ `uvm_info(`gfn, $sformatf("Received sram_tl_d_chan item:\n%0s", item.sprint()), UVM_HIGH)
+
+ // TODO: need to ensure that we don't process TLUL errors
+
+ // check packet integrity
+ void'(item.is_ok());
+
+ // here we only want to process read responses
+ //
+ // TODO: for now...
+ if (item.d_opcode == tlul_pkg::AccessAckData) begin
+ // if data channel has a read response, then there is an incomplete transaction
+ // waiting in the data_phase_mbox
+ data_phase_mbox.get(data_trans);
+ `uvm_info({`gfn, "::process_sram_tl_d_chan_fifo()"},
+ $sformatf("Got INCOMPLETE_READ transaction from data_phase_mbox: %0p", data_trans),
+ UVM_HIGH)
+ // set the read response data
+ data_trans.data = item.d_data;
+ completed_trans_mbox.put(data_trans);
+ `uvm_info({`gfn, "::process_sram_tl_d_chan_fifo()"},
+ $sformatf("Put COMPLETED_READ transaction into completed_trans_mbox: %0p", data_trans),
+ UVM_HIGH)
+ end
+ end
+ endtask
+
+ // This task polls the kdi_fifo for completed key request transactions
+ virtual task process_kdi_fifo();
+ bit seed_valid;
+ push_pull_item #(.DeviceDataWidth(KDI_DATA_SIZE)) item;
+ forever begin
+ kdi_fifo.get(item);
+ `uvm_info(`gfn, $sformatf("Received transaction from kdi_fifo:\n%0s", item.convert2string()), UVM_HIGH)
+
+ // When KDI item is seen, update key, nonce
+ //
+ // TODO: update STATUS.scr_key_seed_valid
+ {key, nonce, seed_valid} = item.d_data;
+
+ `uvm_info(`gfn, $sformatf("Updated key: 0x%0x", key), UVM_HIGH)
+ `uvm_info(`gfn, $sformatf("Updated nonce: 0x%0x", nonce), UVM_HIGH)
+ end
+ endtask
+
+ // This task continuously pulls items from the completed_trans_mbox
+ // and checks them for correctness by using the mem_bkdr_if.
+ virtual task process_sram_trans();
+ sram_trans_t trans;
+ forever begin
+ completed_trans_mbox.get(trans);
+
+ // SRAM writes take 2 cycles to execute, while reads return data in the TLUL response.
+ if (trans.we) begin
+ cfg.clk_rst_vif.wait_clks(2);
+ end
+
+ `uvm_info({`gfn, "::process_sram_trans()"},
+ $sformatf("Received COMPLETED transaction from completed_trans_mbox: %0p", trans),
+ UVM_HIGH)
+ check_mem_trans(trans);
+ end
+ endtask
+
+ // Given a complete memory transaction item as input,
+ // this function compares against the SRAM for correctness
+ // using the mem_bkdr_if.
+ //
+ // TLUL allows partial reads and writes, so we first need to construct a bit-mask
+ // based off of the TLUL mask field.
+ // We then read from the memory using the backdoor interface, and can then directly compare
+ // the TLUL response data to the backdoor-read data using the bit-mask.
+ virtual function void check_mem_trans(sram_trans_t t);
+ bit [TL_AW-1:0] word_addr;
+ bit [TL_DW-1:0] bit_mask;
+
+ // data read from SRAM through backdoor
+ bit [TL_DW-1:0] exp_data;
+ bit [TL_DW-1:0] exp_masked_data;
+ bit [TL_DW-1:0] act_masked_data;
+
+ `uvm_info(`gfn, $sformatf("Checking SRAM memory transaction: %0p", t), UVM_HIGH)
+
+ // Word align the request address
+ word_addr = {t.addr[TL_AW-1:2], 2'b00};
+ `uvm_info(`gfn, $sformatf("word_addr: 0x%0x", word_addr), UVM_HIGH)
+
+ // Expand the byte-oriented mask into a full bit mask
+ for (int i = 0; i < TL_DBW; i++) begin
+ bit_mask[i*8 +: 8] = {8{t.mask[i]}};
+ end
+
+ // backdoor read the mem
+ exp_data = cfg.mem_bkdr_vif.sram_encrypt_read32(word_addr, t.key, t.nonce);
+
+ exp_masked_data = exp_data & bit_mask;
+ act_masked_data = t.data & bit_mask;
+
+ `uvm_info(`gfn, $sformatf("exp_masked_data: 0x%0x", exp_masked_data), UVM_HIGH)
+ `uvm_info(`gfn, $sformatf("act_masked_data: 0x%0x", act_masked_data), UVM_HIGH)
+
+ `DV_CHECK_EQ_FATAL(exp_masked_data, act_masked_data)
+ endfunction
+
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
uvm_reg csr;
bit do_read_check = 1'b1;
@@ -71,6 +262,27 @@
"intr_test": begin
// FIXME
end
+ "exec_regwen": begin
+ // do nothing
+ end
+ "exec": begin
+ end
+ "status": begin
+ // TODO
+ do_read_check = 1'b0;
+ end
+ "ctrl_regwen": begin
+ // do nothing
+ end
+ "ctrl": begin
+ // do nothing if 0 is written
+ if (addr_phase_write && item.a_data) begin
+ end
+ end
+ "error_address": begin
+ // TODO
+ do_read_check = 1'b0;
+ end
default: begin
`uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
end
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_virtual_sequencer.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_virtual_sequencer.sv
index 710d753..e51f543 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_virtual_sequencer.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_virtual_sequencer.sv
@@ -8,6 +8,7 @@
);
`uvm_component_utils(sram_ctrl_virtual_sequencer)
+ tl_sequencer sram_tl_sequencer_h;
`uvm_component_new
diff --git a/hw/ip/sram_ctrl/dv/sram_ctrl_base_sim_cfg.hjson b/hw/ip/sram_ctrl/dv/sram_ctrl_base_sim_cfg.hjson
index 7829b75..0da9022 100644
--- a/hw/ip/sram_ctrl/dv/sram_ctrl_base_sim_cfg.hjson
+++ b/hw/ip/sram_ctrl/dv/sram_ctrl_base_sim_cfg.hjson
@@ -51,11 +51,11 @@
build_modes: [
{
name: sram_ctrl_main
- build_opts: ["+define+SRAM_ADDR_WIDTH=14", "+define+SRAM_DATA_WIDTH=32"]
+ build_opts: ["+define+SRAM_ADDR_WIDTH=14"]
}
{
name: sram_ctrl_ret
- build_opts: ["+define+SRAM_ADDR_WIDTH=10", "+define+SRAM_DATA_WIDTH=32"]
+ build_opts: ["+define+SRAM_ADDR_WIDTH=10"]
}
]
diff --git a/hw/ip/sram_ctrl/dv/tb.sv b/hw/ip/sram_ctrl/dv/tb.sv
index 646433a..32a837b 100644
--- a/hw/ip/sram_ctrl/dv/tb.sv
+++ b/hw/ip/sram_ctrl/dv/tb.sv
@@ -105,9 +105,8 @@
// bind mem_bkdr_if
`define SRAM_CTRL_MEM_HIER \
dut.u_ram1p_sram.u_prim_ram_1p_adv.u_mem
- bind `SRAM_CTRL_MEM_HIER mem_bkdr_if #(.MEM_ADDR_WIDTH(`SRAM_ADDR_WIDTH),
- .MEM_BYTES_PER_WORD(`SRAM_DATA_WIDTH >> 3),
- .MEM_PARITY(1)) mem_bkdr_if ();
+
+ bind `SRAM_CTRL_MEM_HIER mem_bkdr_if #(.MEM_PARITY(1)) mem_bkdr_if ();
initial begin
// drive clk and rst_n from clk_if
diff --git a/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson b/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
index fd31d1d..b7aa412 100644
--- a/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
+++ b/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
@@ -33,6 +33,8 @@
"{proj_root}/hw/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson",
"{proj_root}/hw/ip/rv_timer/dv/rv_timer_sim_cfg.hjson",
"{proj_root}/hw/ip/spi_device/dv/spi_device_sim_cfg.hjson",
+ "{proj_root}/hw/ip/sram_ctrl/dv/sram_ctrl_main_sim_cfg.hjson",
+ "{proj_root}/hw/ip/sram_ctrl/dv/sram_ctrl_ret_sim_cfg.hjson",
"{proj_root}/hw/ip/uart/dv/uart_sim_cfg.hjson",
"{proj_root}/hw/ip/usbdev/dv/usbdev_sim_cfg.hjson",
// Top level IPs.