Skip to content

Commit

Permalink
[otp_ctrl,gen] Fix error code coverage collection
Browse files Browse the repository at this point in the history
The change to make the err_code multi-reg non-compact broke coverage
collection. This changes the error code coverage collection functions
to one for compact multi-regs, and one suitable for both non-compact
multi-regs and for the individual fields of a compact one.

The scoreboard is changed to use non-compact coverage collection.

Signed-off-by: Guillermo Maturana <[email protected]>
  • Loading branch information
matutem authored and msfschaffner committed Jan 25, 2024
1 parent 41496e9 commit efd00fb
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 63 deletions.
55 changes: 31 additions & 24 deletions hw/ip/otp_ctrl/data/otp_ctrl_env_cov.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ from topgen.lib import Name
parts_without_lc = [part for part in otp_mmap.config["partitions"] if
part["variant"] in ["Buffered", "Unbuffered"]]

unbuffered_parts_with_digest = [part for part in otp_mmap.config["partitions"] if
part["variant"] == "Unbuffered" and
unbuffered_parts = [part for part in otp_mmap.config["partitions"] if
part["variant"] == "Unbuffered"]

unbuffered_parts_with_digest = [part for part in unbuffered_parts if
(part["sw_digest"] or part["hw_digest"])]

buffered_nonsecret_parts_with_digest = [part for part in otp_mmap.config["partitions"] if
part["variant"] == "Buffered" and
buffered_parts = [part for part in otp_mmap.config["partitions"] if
part["variant"] == "Buffered"]

buffered_nonsecret_parts_with_digest = [part for part in buffered_parts if
(part["sw_digest"] or part["hw_digest"]) and
not part["secret"]]

buffered_secret_parts_with_digest = [part for part in otp_mmap.config["partitions"] if
part["variant"] == "Buffered" and
buffered_secret_parts_with_digest = [part for part in buffered_parts if
(part["sw_digest"] or part["hw_digest"]) and
part["secret"]]
## Partitions + LCI + DAI
Expand Down Expand Up @@ -333,36 +336,40 @@ class otp_ctrl_env_cov extends cip_base_env_cov #(.CFG_T(otp_ctrl_env_cfg));
end
endfunction

function void collect_err_code_cov(bit [TL_DW-1:0] val, int part_idx = DaiIdx);
// Collect coverage for err_code when it is a compact multi-reg. For DAI error it uses the given
// access_part_idx as the target of the DAI access.
function void collect_compact_err_code_cov(bit [TL_DW-1:0] val, int access_part_idx = DaiIdx);
dv_base_reg_field err_code_flds[$];
for (int k = 0; k <= OtpLciErrIdx; k++) begin
cfg.ral.err_code[k].get_dv_base_reg_fields(err_code_flds);
collect_err_code_field_cov(k, get_field_val(err_code_flds[0], val), part_idx);
cfg.ral.err_code[0].get_dv_base_reg_fields(err_code_flds);
foreach (err_code_flds[part]) begin
collect_err_code_cov(part, get_field_val(err_code_flds[part], val), access_part_idx);
end
endfunction

// Collect coverage according to the field_index. For DAI index, user needs to input
// which partition causes the DAI error. Default part_idx `DaiIdx` won't be collected.
function void collect_err_code_field_cov(int field_idx, bit [TL_DW-1:0] val,
int part_idx = DaiIdx);
case (field_idx)
OtpVendorTestErrIdx, OtpCreatorSwCfgErrIdx, OtpOwnerSwCfgErrIdx: begin
unbuf_err_code_cg_wrap[field_idx].unbuf_err_code_cg.sample(val);
end
OtpHwCfg0ErrIdx, OtpHwCfg1ErrIdx, OtpSecret0ErrIdx, OtpSecret1ErrIdx, OtpSecret2ErrIdx,
OtpSecret3ErrIdx, OtpLifeCycleErrIdx: begin
buf_err_code_cg_wrap[field_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
// Collect coverage for a given partition error_code. For DAI error it uses the given
// access_part_idx as the target of the DAI access.
function void collect_err_code_cov(int part_idx, bit [TL_DW-1:0] val,
int access_part_idx = DaiIdx);
case (part_idx)
% for part in otp_mmap.config["partitions"]:
<% part_name = Name.from_snake_case(part["name"]) %>\
Otp${part_name.as_camel_case()}ErrIdx: begin
% if part in unbuffered_parts:
unbuf_err_code_cg_wrap[part_idx].unbuf_err_code_cg.sample(val);
% elif part in buffered_parts:
buf_err_code_cg_wrap[part_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
% endif
end
% endfor
OtpDaiErrIdx: begin
dai_err_code_cg.sample(val, part_idx);
dai_err_code_cg.sample(val, access_part_idx);
end
OtpLciErrIdx: begin
lci_err_code_cg.sample(val);
end
default: begin
`uvm_fatal(`gfn, $sformatf("invalid err_code index %0d", field_idx))
`uvm_fatal(`gfn, $sformatf("invalid err_code index %0d", part_idx))
end
endcase
endfunction

endclass
19 changes: 12 additions & 7 deletions hw/ip/otp_ctrl/data/otp_ctrl_scoreboard.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -940,18 +940,23 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg)
end
end
end
// For error codes, if lc_prog in progress, err_code might update anytime in DUT. Ignore
// checking until req is acknowledged.

% for k in range(num_err_code):
"err_code_${k}"${": begin" if loop.last else ","}
% endfor
// If lc_prog in progress, err_code might update anytime in DUT. Ignore checking until req
// is acknowledged.
<%
# This code should depend on whether the error code is compact. This
# assumes it is not compact.
%>\
"err_code_${k}": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check) begin
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(item.d_data, part_idx);
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(${k}, item.d_data, access_part_idx);
end
end
% endfor
% for part in write_locked_digest_parts:
<% part_name_snake = Name.from_snake_case(part["name"]).as_snake_case() %>\
"${part_name_snake}_digest_0", "${part_name_snake}_digest_1"${": begin" if loop.last else ","}
Expand Down
51 changes: 34 additions & 17 deletions hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cov.sv
Original file line number Diff line number Diff line change
Expand Up @@ -326,36 +326,53 @@ class otp_ctrl_env_cov extends cip_base_env_cov #(.CFG_T(otp_ctrl_env_cfg));
end
endfunction

function void collect_err_code_cov(bit [TL_DW-1:0] val, int part_idx = DaiIdx);
// Collect coverage for err_code when it is a compact multi-reg. For DAI error it uses the given
// access_part_idx as the target of the DAI access.
function void collect_compact_err_code_cov(bit [TL_DW-1:0] val, int access_part_idx = DaiIdx);
dv_base_reg_field err_code_flds[$];
for (int k = 0; k <= OtpLciErrIdx; k++) begin
cfg.ral.err_code[k].get_dv_base_reg_fields(err_code_flds);
collect_err_code_field_cov(k, get_field_val(err_code_flds[0], val), part_idx);
cfg.ral.err_code[0].get_dv_base_reg_fields(err_code_flds);
foreach (err_code_flds[part]) begin
collect_err_code_cov(part, get_field_val(err_code_flds[part], val), access_part_idx);
end
endfunction

// Collect coverage according to the field_index. For DAI index, user needs to input
// which partition causes the DAI error. Default part_idx `DaiIdx` won't be collected.
function void collect_err_code_field_cov(int field_idx, bit [TL_DW-1:0] val,
int part_idx = DaiIdx);
case (field_idx)
OtpVendorTestErrIdx, OtpCreatorSwCfgErrIdx, OtpOwnerSwCfgErrIdx: begin
unbuf_err_code_cg_wrap[field_idx].unbuf_err_code_cg.sample(val);
// Collect coverage for a given partition error_code. For DAI error it uses the given
// access_part_idx as the target of the DAI access.
function void collect_err_code_cov(int part_idx, bit [TL_DW-1:0] val,
int access_part_idx = DaiIdx);
case (part_idx)
OtpVendorTestErrIdx: begin
unbuf_err_code_cg_wrap[part_idx].unbuf_err_code_cg.sample(val);
end
OtpHwCfg0ErrIdx, OtpHwCfg1ErrIdx, OtpSecret0ErrIdx, OtpSecret1ErrIdx, OtpSecret2ErrIdx,
OtpSecret3ErrIdx, OtpLifeCycleErrIdx: begin
buf_err_code_cg_wrap[field_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
OtpCreatorSwCfgErrIdx: begin
unbuf_err_code_cg_wrap[part_idx].unbuf_err_code_cg.sample(val);
end
OtpOwnerSwCfgErrIdx: begin
unbuf_err_code_cg_wrap[part_idx].unbuf_err_code_cg.sample(val);
end
OtpHwCfg0ErrIdx: begin
buf_err_code_cg_wrap[part_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
end
OtpSecret0ErrIdx: begin
buf_err_code_cg_wrap[part_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
end
OtpSecret1ErrIdx: begin
buf_err_code_cg_wrap[part_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
end
OtpSecret2ErrIdx: begin
buf_err_code_cg_wrap[part_idx - NumPartUnbuf].buf_err_code_cg.sample(val);
end
OtpLifeCycleErrIdx: begin
end
OtpDaiErrIdx: begin
dai_err_code_cg.sample(val, part_idx);
dai_err_code_cg.sample(val, access_part_idx);
end
OtpLciErrIdx: begin
lci_err_code_cg.sample(val);
end
default: begin
`uvm_fatal(`gfn, $sformatf("invalid err_code index %0d", field_idx))
`uvm_fatal(`gfn, $sformatf("invalid err_code index %0d", part_idx))
end
endcase
endfunction

endclass
92 changes: 78 additions & 14 deletions hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
Original file line number Diff line number Diff line change
Expand Up @@ -930,23 +930,87 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg)
end
end
end
"err_code_0",
"err_code_1",
"err_code_2",
"err_code_3",
"err_code_4",
"err_code_5",
"err_code_6",
"err_code_7",
"err_code_8",
// For error codes, if lc_prog in progress, err_code might update anytime in DUT. Ignore
// checking until req is acknowledged.

"err_code_0": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(0, item.d_data, access_part_idx);
end
end
"err_code_1": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(1, item.d_data, access_part_idx);
end
end
"err_code_2": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(2, item.d_data, access_part_idx);
end
end
"err_code_3": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(3, item.d_data, access_part_idx);
end
end
"err_code_4": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(4, item.d_data, access_part_idx);
end
end
"err_code_5": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(5, item.d_data, access_part_idx);
end
end
"err_code_6": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(6, item.d_data, access_part_idx);
end
end
"err_code_7": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(7, item.d_data, access_part_idx);
end
end
"err_code_8": begin
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(8, item.d_data, access_part_idx);
end
end
"err_code_9": begin
// If lc_prog in progress, err_code might update anytime in DUT. Ignore checking until req
// is acknowledged.
if (cfg.m_lc_prog_pull_agent_cfg.vif.req) do_read_check = 0;
if (cfg.en_cov && do_read_check) begin
if (cfg.en_cov && do_read_check && data_phase_read) begin
bit [TL_DW-1:0] dai_addr = `gmv(ral.direct_access_address) >> 2 << 2;
int part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(item.d_data, part_idx);
int access_part_idx = get_part_index(dai_addr);
cov.collect_err_code_cov(9, item.d_data, access_part_idx);
end
end
"vendor_test_digest_0", "vendor_test_digest_1",
Expand Down
2 changes: 1 addition & 1 deletion hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_init_fail_vseq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class otp_ctrl_init_fail_vseq extends otp_ctrl_smoke_vseq;
end else if (err_code != OtpFsmStateError) begin
`uvm_error(`gfn, $sformatf("Unexpected error code_%0d: %0s", i, err_code.name))
end
if (cfg.en_cov) cov.collect_err_code_field_cov(i, err_code);
if (cfg.en_cov) cov.collect_err_code_cov(i, err_code);
end
end

Expand Down

0 comments on commit efd00fb

Please sign in to comment.