[dv/sram] implement covergroups
This PR implements the covergroups listed in #6710,
and sample them in the scoreboard when appropriate.
Signed-off-by: Udi Jonnalagadda <udij@google.com>
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 3f89812..0f0f038 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
@@ -16,11 +16,111 @@
// sram_ctrl_env_cfg: cfg
// covergroups
- // [add covergroups here]
+
+ // cover that all access granularities have been tested for both memory reads and writes
+ covergroup subword_access_cg with function sample(bit we, bit [TL_DBW-1:0] mask);
+ subword_we: coverpoint we;
+ subword_granularity: coverpoint mask {
+ bins zero_access = {'b0};
+ bins byte_access = {'b0001, 'b0010, 'b0100, 'b1000};
+ bins halfword_access = {'b0011, 'b0110, 'b1100};
+ bins triple_byte_access = {'b0111, 'b1110};
+ bins word_access = {'b1111};
+ illegal_bins ill_access = default;
+ }
+ subword_access: cross subword_we, subword_granularity;
+ endgroup
+
+ // cover that SRAM handles mem accesses during key requests, both reads and writes
+ covergroup access_during_key_req_cg with function sample(tlul_pkg::tl_a_op_e opcode);
+ access_during_key_req: coverpoint opcode {
+ bins write = {tlul_pkg::PutFullData, tlul_pkg::PutPartialData};
+ bins read = {tlul_pkg::Get};
+ illegal_bins ill_type = default;
+ }
+ endgroup
+
+ // covers SRAM receiving a key in Off/On states,
+ // with both valid/invalid key seeds.
+ covergroup key_seed_valid_cg with function sample(bit in_lc_esc, bit seed_valid);
+ key_seed_valid: coverpoint seed_valid;
+ escalated: coverpoint in_lc_esc;
+
+ key_seed_valid_cross: cross key_seed_valid, escalated;
+ endgroup
+
+ // covers RAW hazards that are detected in the SRAM,
+ // specifically, whether these hazards are due to address collisions or not.
+ covergroup raw_hazard_cg with function sample(bit addr_collision);
+ raw_hazard: coverpoint addr_collision;
+ endgroup
+
+ // covers that all combinations of reads/writes can create b2b scenario
+ covergroup b2b_access_types_cg with function sample(bit first_write_en, bit second_write_en);
+ b2b_access_types: cross first_write_en, second_write_en;
+ endgroup
+
+ covergroup lc_escalation_rst_cg with function sample(bit rst_n);
+ lc_escalation_rst: coverpoint rst_n;
+ endgroup
+
+ // covers various scenarios that enable/disable SRAM executability
+ covergroup executable_cg with function sample(logic [3:0] lc_hw_debug_en,
+ logic [7:0] en_sram_ifetch,
+ logic [2:0] csr_exec);
+ // placeholder comment
+ en_sram_ifetch_cp: coverpoint en_sram_ifetch {
+ bins sram_ifetch_enable = {otp_ctrl_pkg::Enabled};
+ bins sram_ifetch_valid_disable = {otp_ctrl_pkg::Disabled};
+ bins sram_ifetch_invalid_disable = {
+ [0 : otp_ctrl_pkg::Disabled-1],
+ [otp_ctrl_pkg::Disabled+1 : otp_ctrl_pkg::Enabled-1],
+ [otp_ctrl_pkg::Enabled+1 : '1]};
+ }
+ lc_hw_debug_en_cp: coverpoint lc_hw_debug_en {
+ bins hw_debug_en_on = {lc_ctrl_pkg::On};
+ bins hw_debug_en_valid_off = {lc_ctrl_pkg::Off};
+ bins hw_debug_en_invalid_off = {[0 : lc_ctrl_pkg::Off-1],
+ [lc_ctrl_pkg::Off+1 : lc_ctrl_pkg::On-1],
+ [lc_ctrl_pkg::On+1 : '1]};
+ }
+ csr_exec_cp : coverpoint csr_exec {
+ bins instr_en = {tlul_pkg::InstrEn};
+ bins instr_valid_dis = {tlul_pkg::InstrDis};
+ bins instr_invalid_dis = {[0 : tlul_pkg::InstrDis-1],
+ [tlul_pkg::InstrDis+1 : tlul_pkg::InstrEn-1],
+ [tlul_pkg::InstrEn+1 : '1]};
+ }
+ executable_cross: cross lc_hw_debug_en_cp, en_sram_ifetch_cp, csr_exec_cp {
+ bins csr_exec_en = binsof(en_sram_ifetch_cp.sram_ifetch_enable) &&
+ binsof(csr_exec_cp.instr_en) &&
+ binsof(lc_hw_debug_en_cp);
+
+ bins lc_exec_en = binsof(en_sram_ifetch_cp.sram_ifetch_enable) &&
+ binsof(lc_hw_debug_en_cp.hw_debug_en_on) &&
+ binsof(csr_exec_cp);
+
+ bins valid_exec_dis = (binsof(en_sram_ifetch_cp.sram_ifetch_enable) &&
+ binsof(csr_exec_cp.instr_valid_dis)) ||
+ (binsof(en_sram_ifetch_cp.sram_ifetch_valid_disable) &&
+ binsof(lc_hw_debug_en_cp.hw_debug_en_valid_off));
+
+ bins invalid_exec_dis = (binsof(en_sram_ifetch_cp.sram_ifetch_enable) &&
+ binsof(csr_exec_cp.instr_invalid_dis)) ||
+ (binsof(en_sram_ifetch_cp.sram_ifetch_invalid_disable) &&
+ binsof(lc_hw_debug_en_cp.hw_debug_en_invalid_off));
+ }
+ endgroup
function new(string name, uvm_component parent);
super.new(name, parent);
- // [instantiate covergroups here]
+ subword_access_cg = new();
+ access_during_key_req_cg = new();
+ key_seed_valid_cg = new();
+ raw_hazard_cg = new();
+ b2b_access_types_cg = new();
+ lc_escalation_rst_cg = new();
+ executable_cg = new();
endfunction : new
virtual function void build_phase(uvm_phase phase);
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 22b843f..39a69a1 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv
@@ -234,6 +234,7 @@
task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
+ sample_key_req_access_cg();
process_sram_init();
process_lc_escalation();
process_sram_executable();
@@ -244,6 +245,35 @@
join_none
endtask
+ // This task spins forever and samples the appropriate covergroup whenever
+ // in_key_req is high and a new valid addr_phase transaction is seen on the memory bus.
+ virtual task sample_key_req_access_cg();
+ forever begin
+ @(negedge cfg.under_reset);
+ `DV_SPINWAIT_EXIT(
+ forever begin
+ @(posedge in_key_req);
+ `DV_SPINWAIT_EXIT(
+ forever begin
+ // sample the covergroup every time a new TL request is seen
+ // while a key request is outstanding.
+ @(posedge cfg.m_sram_cfg.vif.h2d.a_valid);
+ // zero delay to allow bus values to settle
+ #0;
+ if (cfg.en_cov) begin
+ cov.access_during_key_req_cg.sample(cfg.m_sram_cfg.vif.h2d.a_opcode);
+ end
+ end
+ ,
+ @(negedge in_key_req);
+ )
+ end
+ ,
+ wait(cfg.under_reset == 1);
+ )
+ end
+ endtask
+
virtual task process_sram_init();
// SRAM initialization happens once at the beginning of each simulation and requires a key to be
// provisioned from OTP first.
@@ -322,6 +352,11 @@
// lc escalation status will be dropped after reset, no further action needed
wait(cfg.lc_vif.lc_esc_en == lc_ctrl_pkg::Off);
+
+ // sample coverage
+ if (cfg.en_cov) begin
+ cov.lc_escalation_rst_cg.sample(cfg.clk_rst_vif.rst_n);
+ end
end
endtask
@@ -335,6 +370,13 @@
detected_hw_debug_en = cfg.exec_vif.lc_hw_debug_en;
detected_en_sram_ifetch = cfg.exec_vif.otp_en_sram_ifetch;
+ // sample executability-related coverage
+ if (cfg.en_cov) begin
+ cov.executable_cg.sample(detected_hw_debug_en,
+ detected_en_sram_ifetch,
+ detected_csr_exec);
+ end
+
`uvm_info(`gfn, $sformatf("detected_hw_debug_en: %0b", detected_hw_debug_en), UVM_HIGH)
`uvm_info(`gfn, $sformatf("detected_en_sram_ifetch: %0b", detected_en_sram_ifetch), UVM_HIGH)
@@ -359,7 +401,9 @@
if (!cfg.en_scb) continue;
- if (in_key_req) continue;
+ if (in_key_req) begin
+ `uvm_error(`gfn, "Received SRAM_TLUL transaction while requesting a new key")
+ end
// If the escalation propagation has finished,
// do not process anymore addr_phase transactions
@@ -507,6 +551,11 @@
//
// as a result we need to check for an address collision then act accordingly.
+ // sample b2b-related coovergroup
+ if (cfg.en_cov) begin
+ cov.b2b_access_types_cg.sample(data_trans.we, addr_trans.we);
+ end
+
// if we have an address collision (read address is the same as the pending write address)
// return data based on the `held_data`
if (eq_sram_addr(data_trans.addr, held_trans.addr)) begin
@@ -542,6 +591,11 @@
`uvm_info(`gfn, $sformatf("addr_trans: %0p", addr_trans), UVM_HIGH)
+ // sample b2b-related covergroup
+ if (cfg.en_cov) begin
+ cov.b2b_access_types_cg.sample(data_trans.we, addr_trans.we);
+ end
+
if (addr_trans.we == 0) begin
// if we see a read directly after a write and we are not currently in a RAW hazard
// handling state, we need to do the following:
@@ -559,6 +613,11 @@
waddr = {data_trans.addr[TL_AW-1:2], 2'b00};
held_data = cfg.mem_bkdr_vif.sram_encrypt_read32(waddr, data_trans.key, data_trans.nonce);
+ // sample covergroup
+ if (cfg.en_cov) begin
+ cov.raw_hazard_cg.sample(waddr == word_align_addr(addr_trans.addr));
+ end
+
for (int i = 0; i < TL_DBW; i++) begin
if (data_trans.mask[i]) begin
held_data[i*8 +: 8] = data_trans.data[i*8 +: 8];
@@ -596,6 +655,11 @@
// When KDI item is seen, update key, nonce
{key, nonce, seed_valid} = item.d_data;
+ // sample coverage on seed_valid
+ if (cfg.en_cov) begin
+ cov.key_seed_valid_cg.sample(status_lc_esc, seed_valid);
+ end
+
// scr_key_valid simply denotes that a successful handshake with OTP has completed,
// so this will be 1 whenever we get a copmleted transaction item
exp_status[SramCtrlScrKeyValid] = 1;
@@ -621,6 +685,11 @@
forever begin
completed_trans_mbox.get(trans);
+ // sample access granularity for each completed transaction
+ if (cfg.en_cov) begin
+ cov.subword_access_cg.sample(trans.we, trans.mask);
+ end
+
`uvm_info({`gfn, "::process_completed_trans()"},
$sformatf("Checking SRAM memory transaction: %0p", trans),
UVM_HIGH)