Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sram_ctrl,dv] Add sec_cm_mem_readback test #24699

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions hw/dv/sv/mem_bkdr_scb/mem_bkdr_scb.sv
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,19 @@ virtual class mem_bkdr_scb #(int AddrWidth = bus_params_pkg::BUS_AW,
function void read_finish(mem_data_t act_data,
mem_addr_t addr = 0,
mem_mask_t mask = '1,
bit en_check_consistency = 1);
bit en_check_consistency = 1,
bit en_check_exp = 1);
mem_item_t exp_item;
`DV_CHECK_NE(read_item_q.size, 0)
exp_item = read_item_q.pop_front();
act_data &= expand_bit_mask(mask);

if (en_check_consistency) check_item_consistency(exp_item, addr, mask);
`DV_CHECK_EQ(act_data, exp_item.data, $sformatf("addr 0x%0h read out mismatch", exp_item.addr))

if (en_check_exp) begin
`DV_CHECK_EQ(act_data, exp_item.data,
$sformatf("addr 0x%0h read out mismatch", exp_item.addr))
end

`uvm_info(`gfn, $sformatf("read_finish: Addr[0x%0h], data[0x%0h], Mask[0x%0h]",
exp_item.addr, act_data, exp_item.mask), UVM_MEDIUM)
Expand All @@ -148,7 +153,8 @@ virtual class mem_bkdr_scb #(int AddrWidth = bus_params_pkg::BUS_AW,
// write_item_q
function void write_finish(mem_addr_t addr = 0,
mem_mask_t mask = '1,
bit en_check_consistency = 1);
bit en_check_consistency = 1,
bit en_check_exp = 1);
mem_data_t act_data, exp_data;
mem_data_t bit_mask;
mem_item_t exp_item;
Expand All @@ -160,7 +166,11 @@ virtual class mem_bkdr_scb #(int AddrWidth = bus_params_pkg::BUS_AW,

act_data = get_bkdr_val(exp_item.addr) & bit_mask;
exp_data = exp_item.data & bit_mask;
`DV_CHECK_EQ(act_data, exp_data, $sformatf("addr 0x%0h write mismatch", exp_item.addr))

if (en_check_exp) begin
`DV_CHECK_EQ(act_data, exp_data,
$sformatf("addr 0x%0h write mismatch", exp_item.addr))
end

`uvm_info(`gfn, $sformatf("write_finish: Addr[0x%0h], data[0x%0h], Mask[0x%0h]",
exp_item.addr, act_data, exp_item.mask), UVM_MEDIUM)
Expand Down
5 changes: 3 additions & 2 deletions hw/ip/sram_ctrl/data/sram_ctrl_sec_cm_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@
name: sec_cm_mem_readback
desc: '''Verify the countermeasure(s) MEM.READBACK.

Test needs to be implemented, see lowRISC/opentitan#23322.
Injects bit-flips during memory reads/writes and checks whether
an error is triggered.
'''
stage: V2S
tests: ["{name}_smoke"]
tests: ["{name}_readback_err"]
}
{
name: sec_cm_mem_scramble
Expand Down
4 changes: 3 additions & 1 deletion hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_base_vseq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ class sram_ctrl_base_vseq #(parameter int AddrWidth = `SRAM_ADDR_WIDTH) extends
readback_running = 1;
if (uvm_re_match("*throughput*", get_type_name()) &&
uvm_re_match("*sec_cm*", get_type_name()) &&
uvm_re_match("*readback_err*", get_type_name()) &&
uvm_re_match("*throughput*", common_seq_type) &&
uvm_re_match("*sec_cm*", common_seq_type)) begin
uvm_re_match("*sec_cm*", common_seq_type) &&
uvm_re_match("*readback_err*", common_seq_type)) begin
// Configure the SRAM TLUL agent to wait at least 2 cycles before dropping
// a request.
cfg.m_tl_agent_cfgs[cfg.sram_ral_name].a_valid_len_min = 2;
Expand Down
156 changes: 156 additions & 0 deletions hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_readback_err_vseq.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// readback_err_vseq
class sram_ctrl_readback_err_vseq extends sram_ctrl_base_vseq;
`uvm_object_utils(sram_ctrl_readback_err_vseq)

`uvm_object_new

// Indicates the number of memory accesses to be performed in this test.
rand int num_ops;

// Indicates at which memory access the fault is injected.
rand int do_fi_op;

constraint num_ops_c {
num_ops inside {[10 : 200]};
}

constraint do_fi_op_c {
do_fi_op inside {[10 : num_ops]};
}

int fi_iteration_position = 0;

task drive_reqs(int iterations, bit write);
// Perform random read/write operations.
bit [TL_AW-1:0] addr;
bit [TL_AW-1:0] sram_addr_mask = cfg.ral_models[cfg.sram_ral_name].get_addr_mask();
bit [TL_AW-1:0] max_offset = {sram_addr_mask[TL_AW-1:2], 2'd0};
logic [TL_DW-1:0] rdata;
for (int iteration = 0; iteration < iterations; iteration++) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(addr, (addr & sram_addr_mask) < max_offset;)
if (write) begin
do_single_write(.addr(addr), .data(iteration), .mask('1), .blocking(0));
end else begin
do_single_read(.addr(addr), .mask('1), .check_rdata(0), .blocking(0), .rdata(rdata));
end
end
endtask

task inject_fault(int iterations, int iteration_fi_position, string fi_path,
int fi_mask, string sram_req_path, string sram_we_path,
bit write_op, string alert_signal);
// Inject a fault into the signal indicated by fi_path & monitor the response.
bit req;
int value;
int value_faulty;
for (int iteration = 0; iteration < iterations; iteration++) begin
// Wait until the sram_we (for writes) or the sram_req (for reads) arrives.
`DV_SPINWAIT(
do begin
cfg.clk_rst_vif.wait_n_clks(1);
if (write_op) begin
uvm_hdl_read(sram_we_path, req);
end else begin
uvm_hdl_read(sram_req_path, req);
end
end while(!req);
)

// Only inject if we reached the selected read/write transaction.
if (iteration == iteration_fi_position) begin
`uvm_info(`gfn, $sformatf(
"Injecting fault into %s in memory operation %d and check the %s alert",
fi_path, iteration_fi_position, alert_signal),
UVM_LOW)
fork
begin : inject_fault
uvm_hdl_read(fi_path, value);
value_faulty = value ^ fi_mask;
`DV_CHECK(uvm_hdl_force(fi_path, value_faulty))
// Release the faulty signal after one clock cycle.
cfg.clk_rst_vif.wait_n_clks(1);
`DV_CHECK(uvm_hdl_release(fi_path))
end : inject_fault
begin : monitor_response
// Check if alert_signal was triggered.
cfg.scb.set_exp_alert(alert_signal, .is_fatal(1'b1), .max_delay(20));
wait_alert_trigger (alert_signal, .max_wait_cycle(20), .wait_complete(0));
// Reset to get the DUT out of terminal state.
apply_resets_concurrently();
end : monitor_response
join
end
cfg.clk_rst_vif.wait_clks(1);
end
endtask

task body();
// Define the signal we are targeting to inject a fault. This list covers the
// most relevant signals for SRAM read/write requests. The rdata signal for reads
// is indirectly covered by the addr as a faulty address triggers the same behavior
// as a faulted rdata.
string fi_paths[3] = {"tb.dut.sram_addr", "tb.dut.sram_wdata", "tb.dut.sram_we"};
int fi_masks[3] = {2, 2, 1};
// These variables hold the randomly selected target signal.
string fi_path;
int fi_mask;
// Used to keep track of the incoming TL-UL requests.
string sram_req_path = "tb.dut.sram_req";
// The readback feature raises the fatal_error alert on a mismatch.
string alert_signal = "fatal_error";
string sram_we_path = "tb.dut.sram_we";
// Are we targeting a read or write operation?
bit target_write = $urandom_range(0, 1);

// Disable certain checks for FI.
cfg.is_fi_test = 1'b1;

// If we are faulting the sram_we signal, this assertion would trigger. Disable it.
$assertoff(0, "tb.dut.u_tlul_adapter_sram.u_sram_byte.gen_integ_handling");

`DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_ops)
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(do_fi_op)

// Request memory init & enable SRAM readback feature.
req_mem_init();
csr_wr(.ptr(ral.readback), .value(MuBi4True));

// Set the target FI path depending whether we are targeting a write or read.
if (target_write) begin
// Either target the adress, the write data, or the write enable signal.
int idx = $urandom_range(0, 2);
fi_path = fi_paths[idx];
fi_mask = fi_masks[idx];
end else begin
// Target the read address.
fi_path = fi_paths[0];
fi_mask = fi_masks[0];
end

// Sanity check some static paths we use in this test.
`DV_CHECK(uvm_hdl_check_path(fi_path),
$sformatf("Hierarchical path %0s appears to be invalid.", fi_path))
`DV_CHECK(uvm_hdl_check_path(sram_req_path),
$sformatf("Hierarchical path %0s appears to be invalid.", sram_req_path))
`DV_CHECK(uvm_hdl_check_path(sram_we_path),
$sformatf("Hierarchical path %0s appears to be invalid.", sram_we_path))

// As we have at least 10 memory requests, correct the offset.
fi_iteration_position = do_fi_op - 10;

// Start the parallel threads.
fork
// Driver task.
drive_reqs(num_ops, target_write);

// Fault injector task.
inject_fault(num_ops, fi_iteration_position, fi_path, fi_mask, sram_req_path,
sram_we_path, target_write, alert_signal);
join_any
endtask : body

endclass : sram_ctrl_readback_err_vseq
1 change: 1 addition & 0 deletions hw/ip/sram_ctrl/dv/env/seq_lib/sram_ctrl_vseq_list.sv
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
`include "sram_ctrl_regwen_vseq.sv"
`include "sram_ctrl_ram_cfg_vseq.sv"
`include "sram_ctrl_stress_all_vseq.sv"
`include "sram_ctrl_readback_err_vseq.sv"
1 change: 1 addition & 0 deletions hw/ip/sram_ctrl/dv/env/sram_ctrl_env.core
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ filesets:
- seq_lib/sram_ctrl_regwen_vseq.sv: {is_include_file: true}
- seq_lib/sram_ctrl_ram_cfg_vseq.sv: {is_include_file: true}
- seq_lib/sram_ctrl_stress_all_vseq.sv: {is_include_file: true}
- seq_lib/sram_ctrl_readback_err_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource

generate:
Expand Down
3 changes: 3 additions & 0 deletions hw/ip/sram_ctrl/dv/env/sram_ctrl_env_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class sram_ctrl_env_cfg #(parameter int AddrWidth = 10)
// This tracks when the ram is undergoing initialization in the scoreboard.
bit in_init = 1'b0;

// Disables certain checks for FI tests.
bit is_fi_test = 1'b0;

// ext component cfgs
rand push_pull_agent_cfg#(.DeviceDataWidth(KDI_DATA_SIZE)) m_kdi_cfg;

Expand Down
5 changes: 3 additions & 2 deletions hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor
cfg.clk_rst_vif.wait_clks(1);
#1ps;

mem_bkdr_scb.write_finish(decrypt_addr, item.mask);
mem_bkdr_scb.write_finish(decrypt_addr, item.mask, !cfg.is_fi_test, !cfg.is_fi_test);
`uvm_info(`gfn, $sformatf("Currently num of pending write items is %0d", write_item_q.size),
UVM_MEDIUM)
end
Expand Down Expand Up @@ -442,7 +442,8 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor
`DV_CHECK_EQ(cfg.in_init, 0, "No item is accepted during init")

if (status_lc_esc == EscNone && !item.is_write()) begin
mem_bkdr_scb.read_finish(item.d_data, simplify_addr(item.a_addr), item.a_mask);
mem_bkdr_scb.read_finish(item.d_data, simplify_addr(item.a_addr),
item.a_mask, !cfg.is_fi_test, !cfg.is_fi_test);
end
endtask

Expand Down
4 changes: 4 additions & 0 deletions hw/ip/sram_ctrl/dv/sram_ctrl_base_sim_cfg.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@
uvm_test_seq: "{name}_common_vseq"
run_opts: ["+run_mem_partial_access"]
}
{
name: "{name}_readback_err"
uvm_test_seq: sram_ctrl_readback_err_vseq
}
]

// List of regressions.
Expand Down
Loading