[dv] Add random backdoor for csr_hw_reset
1. Add random backdoor for csr_hw_reset, so that we can test RO register
which is updated by hw
2. Add hier_path for IP that has extra hierarchy for reg block
3. backdoor support to top-level
Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/dv/sv/csr_utils/csr_seq_lib.sv b/hw/dv/sv/csr_utils/csr_seq_lib.sv
index 1b5322b..71d076c 100644
--- a/hw/dv/sv/csr_utils/csr_seq_lib.sv
+++ b/hw/dv/sv/csr_utils/csr_seq_lib.sv
@@ -158,6 +158,8 @@
// checks. It is run as the first step of the CSR HW reset test.
//--------------------------------------------------------------------------------------------------
class csr_write_seq extends csr_base_seq;
+ static bit test_backdoor_path_done; // only run once
+ bit en_rand_backdoor_write;
`uvm_object_utils(csr_write_seq)
`uvm_object_new
@@ -165,7 +167,20 @@
virtual task body();
uvm_reg_data_t wdata;
+ // check all hdl paths are valid
+ if (!test_backdoor_path_done) begin
+ uvm_reg_mem_hdl_paths_seq hdl_check_seq;
+ hdl_check_seq = uvm_reg_mem_hdl_paths_seq::type_id::create("hdl_check_seq");
+ foreach (models[i]) begin
+ hdl_check_seq.model = models[i];
+ hdl_check_seq.start(null);
+ end
+ test_backdoor_path_done = 1;
+ end
+
foreach (test_csrs[i]) begin
+ dv_base_reg dv_csr;
+ bit backdoor;
// check if parent block or register is excluded from write
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrHwResetTest)) begin
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
@@ -178,7 +193,14 @@
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrHwResetTest, m_csr_excl_item);
- csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
+
+ `downcast(dv_csr, test_csrs[i])
+ if (en_rand_backdoor_write && !dv_csr.get_is_ext_reg()) begin
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(backdoor,
+ backdoor dist {0 :/ 7, 1 :/ 3};)
+ end
+
+ csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .backdoor(backdoor));
end
endtask
diff --git a/hw/dv/sv/csr_utils/csr_utils_pkg.sv b/hw/dv/sv/csr_utils/csr_utils_pkg.sv
index bb0252f..0e08a19 100644
--- a/hw/dv/sv/csr_utils/csr_utils_pkg.sv
+++ b/hw/dv/sv/csr_utils/csr_utils_pkg.sv
@@ -232,8 +232,7 @@
input bit predict = 0,
input uvm_reg_map map = null);
if (backdoor) begin
- csr_poke(csr, value, check);
- if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_DIRECT)));
+ csr_poke(csr, value, check, predict);
end else if (blocking) begin
csr_wr_sub(csr, value, check, path, timeout_ns, map);
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
@@ -286,13 +285,24 @@
// backdoor write csr
task automatic csr_poke(input uvm_reg csr,
input uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK);
+ input uvm_check_e check = UVM_CHECK,
+ input bit predict = 0);
uvm_status_e status;
string msg_id = {csr_utils_pkg::msg_id, "::csr_poke"};
+ uvm_reg_data_t old_mirrored_val = csr.get_mirrored_value();
csr.poke(.status(status), .value(value));
- if (check == UVM_CHECK) begin
- `DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
+ if (check == UVM_CHECK && status != UVM_IS_OK) begin
+ string str;
+ uvm_hdl_path_concat paths[$];
+ csr.get_full_hdl_path(paths);
+ foreach (paths[0].slices[i]) str = $sformatf("%0s\n%0s", str, paths[0].slices[i].path);
+ `uvm_fatal(msg_id, $sformatf("poke failed for %0s, check below paths %0s",
+ csr.get_full_name(), str))
+ end
+ // poke always updates predict value, if predict == 0, revert back to old mirrored value
+ if (!predict) begin
+ void'(csr.predict(.value(old_mirrored_val), .kind(UVM_PREDICT_DIRECT)));
end
endtask
diff --git a/hw/dv/sv/dv_base_reg/dv_base_reg_field.sv b/hw/dv/sv/dv_base_reg/dv_base_reg_field.sv
index 6bcb400..d1c1a3e 100644
--- a/hw/dv/sv/dv_base_reg/dv_base_reg_field.sv
+++ b/hw/dv/sv/dv_base_reg/dv_base_reg_field.sv
@@ -11,10 +11,6 @@
// when use UVM_PREDICT_WRITE and the CSR access is WO, this function will return the default
// val of the register, rather than the written value
- // TODO, need to handle predict value when backdoor write happens WO reg
- // 1. for read, design ties the read data to default value
- // 2. when backdoor write updates internal reg, backdoor read can get the written value, but
- // frontdoor read always returns the default value.
virtual function uvm_reg_data_t XpredictX(uvm_reg_data_t cur_val,
uvm_reg_data_t wr_val,
uvm_reg_map map);
diff --git a/hw/dv/sv/dv_lib/dv_base_vseq.sv b/hw/dv/sv/dv_lib/dv_base_vseq.sv
index 7cade3b..9bbf7b9 100644
--- a/hw/dv/sv/dv_lib/dv_base_vseq.sv
+++ b/hw/dv/sv/dv_lib/dv_base_vseq.sv
@@ -172,6 +172,7 @@
m_csr_write_seq.models = cfg.ral_models;
m_csr_write_seq.set_csr_excl_item(csr_excl);
m_csr_write_seq.external_checker = cfg.en_scb;
+ m_csr_write_seq.en_rand_backdoor_write = 1;
if (!enable_asserts_in_hw_reset_rand_wr) $assertoff;
m_csr_write_seq.start(null);
diff --git a/hw/ip/alert_handler/data/alert_handler.hjson b/hw/ip/alert_handler/data/alert_handler.hjson
index 1f264af..8b15774 100644
--- a/hw/ip/alert_handler/data/alert_handler.hjson
+++ b/hw/ip/alert_handler/data/alert_handler.hjson
@@ -17,6 +17,7 @@
clock_primary: "clk_i",
bus_device: "tlul",
regwidth: "32",
+ hier_path: "i_reg_wrap"
param_list: [
{ name: "NAlerts",
desc: "Number of peripheral inputs",
diff --git a/hw/ip/alert_handler/data/alert_handler.hjson.tpl b/hw/ip/alert_handler/data/alert_handler.hjson.tpl
index 09adb7c..50aacb6 100644
--- a/hw/ip/alert_handler/data/alert_handler.hjson.tpl
+++ b/hw/ip/alert_handler/data/alert_handler.hjson.tpl
@@ -20,6 +20,7 @@
clock_primary: "clk_i",
bus_device: "tlul",
regwidth: "32",
+ hier_path: "i_reg_wrap"
##############################################################################
param_list: [
{ name: "NAlerts",
diff --git a/hw/top_earlgrey/ip/alert_handler/data/autogen/alert_handler.hjson b/hw/top_earlgrey/ip/alert_handler/data/autogen/alert_handler.hjson
index ccb9597..4bf94f9 100644
--- a/hw/top_earlgrey/ip/alert_handler/data/autogen/alert_handler.hjson
+++ b/hw/top_earlgrey/ip/alert_handler/data/autogen/alert_handler.hjson
@@ -25,6 +25,7 @@
clock_primary: "clk_i",
bus_device: "tlul",
regwidth: "32",
+ hier_path: "i_reg_wrap"
param_list: [
{ name: "NAlerts",
desc: "Number of peripheral inputs",
diff --git a/util/reggen/data.py b/util/reggen/data.py
index f933356..dd0fefe 100644
--- a/util/reggen/data.py
+++ b/util/reggen/data.py
@@ -211,6 +211,7 @@
addr_width = 12
base_addr = 0
name = ""
+ hier_path = ""
regs = []
wins = []
blocks = []
@@ -222,6 +223,7 @@
self.addr_width = 12
self.base_addr = 0
self.name = ""
+ self.hier_path = ""
self.regs = []
self.wins = []
self.blocks = []
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index c4a5991..a788c48 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -145,6 +145,8 @@
block.params = obj["param_list"] if "param_list" in obj else []
+ block.hier_path = obj["hier_path"] if "hier_path" in obj else ""
+
for r in obj["registers"]:
# Check if any exception condition hit
if 'reserved' in r:
diff --git a/util/reggen/uvm_reg.sv.tpl b/util/reggen/uvm_reg.sv.tpl
index d18a2a8..e2229ca 100644
--- a/util/reggen/uvm_reg.sv.tpl
+++ b/util/reggen/uvm_reg.sv.tpl
@@ -12,6 +12,9 @@
% endfor
<%
regs_flat = block.get_regs_flat()
+hier_path = ""
+if (block.hier_path):
+ hier_path = block.hier_path + "."
%>\
// Block: ${block.name}
@@ -38,7 +41,8 @@
% for r in regs_flat:
<%
reg_width = block.width
- reg_name= r.name
+ reg_name = r.name
+ is_ext = 0
%>\
// Class: ${gen_dv.rcname(block, r)}
class ${gen_dv.rcname(block, r)} extends dv_base_reg;
@@ -70,6 +74,15 @@
else:
field_volatile = 1
field_tags = f.tags
+
+ if r.hwext or (f.hwaccess == HwAccess.NONE and f.swrdaccess == SwRdAccess.RD and
+ f.swwraccess == SwWrAccess.NONE):
+ is_ext = 1
+
+ if len(r.fields) == 1:
+ reg_field_name = reg_name
+ else:
+ reg_field_name = reg_name + "_" + f.name
%>\
${f.name} = dv_base_reg_field::type_id::create("${f.name}");
${f.name}.configure(
@@ -83,26 +96,28 @@
.is_rand(1),
.individually_accessible(1));
${f.name}.set_original_access("${field_access}");
- % if r.hwext:
- set_is_ext_reg(1);
- % endif
- % if len(r.fields) == 1:
- add_hdl_path_slice("u_${reg_name}.q", ${f.lsb}, ${field_size});
+ % if f.hwaccess == HwAccess.NONE and f.swrdaccess == SwRdAccess.RD and f.swwraccess == SwWrAccess.NONE:
+ // constant reg
+ add_hdl_path_slice("${hier_path}u_reg.${reg_field_name}_qs", ${f.lsb}, ${field_size});
% else:
- add_hdl_path_slice("u_${reg_name}_${f.name}.q", ${f.lsb}, ${field_size});
+ add_hdl_path_slice("${hier_path}u_reg.u_${reg_field_name}.q${"s" if r.hwext else ""}", ${f.lsb}, ${field_size});
% endif
-% if field_tags:
+ % if field_tags:
// create field tags
-% for field_tag in field_tags:
+ % for field_tag in field_tags:
<%
tag = field_tag.split(":")
%>\
-% if tag[0] == "excl":
+ % if tag[0] == "excl":
csr_excl.add_excl(${f.name}.get_full_name(), ${tag[2]}, ${tag[1]});
-% endif
+ % endif
+ % endfor
+ % endif
% endfor
+
+% if is_ext:
+ set_is_ext_reg(1);
% endif
-% endfor
endfunction : build
endclass : ${gen_dv.rcname(block, r)}
@@ -178,12 +193,12 @@
${b.name} = ${gen_dv.bcname(b)}::type_id::create("${b.name}");
${b.name}.configure(.parent(this));
${b.name}.build(.base_addr(base_addr + ${gen_dv.sv_base_addr(b)}), .csr_excl(csr_excl));
- ${b.name}.set_hdl_path_root("tb.dut.top_earlgrey.u_${b.name}.u_reg");
+ ${b.name}.set_hdl_path_root("tb.dut.top_earlgrey.u_${b.name}");
default_map.add_submap(.child_map(${b.name}.default_map),
.offset(base_addr + ${gen_dv.sv_base_addr(b)}));
% endfor
% if regs_flat:
- set_hdl_path_root("tb.dut.u_reg");
+ set_hdl_path_root("tb.dut");
// create registers
% endif