Skip to content

[dv] Turn on the icache ECC checks - #2515

Merged
andreaskurth merged 5 commits into
lowRISC:masterfrom
kulan-pal:dv/icache-ecc-checks
Oct 17, 2026
Merged

andreaskurth merged 5 commits into
lowRISC:masterfrom
kulan-pal:dv/icache-ecc-checks

Conversation

@kulan-pal

@kulan-pal kulan-pal commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

The ECC checks in the icache testbench are switched off while the icache runs. They pass rst_n as the reset of ASSERT, which expects an active-high reset. With the reset fixed, one check fails on master:

UVM_ERROR @ 376010000 ps: (ibex_icache_ram_if.sv:103) [ASSERT FAILED] DataErrChk_A

That check asks too much. On a lookup, the icache checks the data ECC only of the way that hit. The other ways may never have been written, so their ECC can be wrong. With this PR, the checks require:

  • an error for a corrupted tag on every lookup;
  • an error for corrupted data in the only way that hit;
  • no error unless the testbench injected one, and no X on the error output.

The testbench finds the way that hit from the uncorrupted tags, using the icache's lookup address and tag tweak. Data errors in a line stored in two ways stay unchecked, because the icache combines both copies first. #2523 changes that and adds a check for it.

The checks also found a testbench bug, fixed in the first commit. The testbench fetched a new scrambling key on every invalidate, even when the icache was already waiting for one. The extra fetch could start in the middle of the invalidation, and the RAMs ignore writes during a key fetch. Some tags were not cleared and later read back as ECC errors. The testbench now fetches a key only when the icache asks for one, as ibex_top does, and checks that it asks when it should.

The error injection now uses logic instead of bit, so an X from the RAMs reaches the icache instead of turning into 0. VCS coverage now includes the key-request and RAM interface checks, so the report shows whether each one ever succeeds. That config comes from #2523.

Dependencies: none.

I ran all icache tests with dvsim on VCS U-2023.03-SP2 and the default testbench parameters, on 20 random seeds each and on seed 123. All passed. With coverage on, every assertion this PR adds or changes had real successes and no incomplete attempts on seed 1. To make sure the checks can fail, I ran local fault versions, such as a stuck or X error output, a missing or extra key request, a wrong or unknown hit reference, or missing RAM read valid flags. Each one makes the intended check fail. The one that drops data errors passes some seeds, because those seeds never hit in the cache. I did not run Xcelium.

AI disclosure (CLA §9): written with help from Claude Code and reviewed with OpenAI Codex; I have reviewed and understood every change and take full responsibility for it.

@bonnie-banks123

Copy link
Copy Markdown

The assertion polarity and lookup checks look correct. The duplicate-way limitation is explicitly handled by #2523. These changes also passed the 15 ECC/reset runs on #2523; I haven’t run this head independently.

@andreaskurth andreaskurth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kulan-pal, turning these checks on and finding the key-fetch bug with them is a valuable fix. I have a few minor comments; details inline.

Could the ecc entry in dv/uvm/icache/data/ibex_icache_testplan.hjson also mention that ecc_error_o is now checked against the injected errors, and that a line held in two ways is not checked yet?

Comment on lines +122 to +123
`ASSERT(TagValidChk_A, always (& ic_tag_rvalid == (| ic_tag_rvalid)), clk, !rst_n)
`ASSERT(DataValidChk_A, always (| ic_data_rvalid) == (& ic_data_rvalid), clk, !rst_n)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that these are live, always makes each clock edge start an attempt that never completes, so open attempts accumulate over the test. Since a concurrent assertion is already evaluated every cycle, could we drop always here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I moved the commit that drops always from #2523 into this PR. I also moved the coverage config from #2523, so the report now shows these checks succeeding.


`ASSERT(TagValidChk_A, always (& ic_tag_rvalid == (| ic_tag_rvalid)), clk, rst_n)
`ASSERT(DataValidChk_A, always (| ic_data_rvalid) == (& ic_data_rvalid), clk, rst_n)
`ASSERT(NoUnexpectedErrChk_A, ecc_err |-> ecc_err_allowed, clk, !rst_n)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecc_err is declared as bit, so an X on ecc_error_o reads as 0 and passes this check silently. Should it be logic, with an `ASSERT_KNOWN on it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecc_err is now logic, and EccErrKnown_A checks that it is known outside reset. A local mutant that drives X instead of 0 fails it and passes the old checks. The error injection had the same problem with X read from the RAMs, so it uses logic now too.

Comment thread dv/uvm/icache/dv/tb/tb.sv
.ic_scr_key_valid_i ( scramble_key_valid_q ),
// TODO: Hook up to monitor and appropriate checking
.ic_scr_key_req_o ( ),
.ic_scr_key_req_o ( ic_scr_key_req ),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TODO about checking the key request is removed, but nothing checks it yet. If the icache never raised ic_scr_key_req_o, scramble_key_valid_q would stay high and invalidations would complete with the old key unnoticed. Could we keep the TODO, or add an assertion that an invalidate leads to a key fetch?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ScrKeyReqChk_A in tb.sv: the icache must request a key exactly when an invalidation arrives while it is not waiting for one. Local mutants that never request a key, or request one while already waiting, fail it.

Comment thread dv/uvm/icache/dv/tb/tb.sv Outdated
Comment on lines +102 to +103
assign ram_if.lookup_valid = dut.lookup_valid_ic1;
assign ram_if.tag_match = dut.tag_match_ic1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using tag_match_ic1 as the reference mostly restates the RTL's own ecc_err_ic1 expression, so the "which way counts" decision is not checked independently. Would it be feasible to compute the expected hit in the TB from the uncorrupted ic_tag_rdata_in, the index-based tag tweak and lookup_addr_ic1? For lookup_valid, a sanity check such as lookup_valid |-> (&ic_tag_rvalid) && (&ic_data_rvalid) would help. If that is too much, a comment noting that the reference comes from the DUT would be fine too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checks now find the hit from the uncorrupted tags, using the DUT's tweak and lookup address. I also added your sanity check as LookupReadChk_A, and checks that the lookup valid and the expected hit are known, so an X there cannot switch the checks off. A local mutant where way 1 never hits fails the new checks on some seeds, and the old checks pass on those same seeds.

When an invalidate arrived while the icache was already waiting for a
scrambling key, the testbench fetched a second key. That fetch could
land in the icache's invalidation sweep. The RAMs reject writes while a
key is fetched, so some tags were not cleared and later read back as
ECC errors.

Fetch a key only when the icache asks for one, as ibex_top does. Check
that it asks exactly when an invalidation arrives outside a key wait.

Signed-off-by: Kulan Palanichamy <kulan.palanichamy@opentitan.org>
The ECC assertions in the RAM interface passed rst_n where ASSERT
expects an active-high reset, so they were off while the icache ran.

Pass !rst_n. On a valid lookup, require an error for an injected tag
error, and for corrupted data in the only way that hit, found from the
uncorrupted tags. Require known error, lookup and hit signals, and
reads of all ways. Reject unexpected errors. Data errors in a line
held in two ways stay unchecked. Make the monitor's read data inputs
and describe the checks in the testplan.

Signed-off-by: Kulan Palanichamy <kulan.palanichamy@opentitan.org>
TagValidChk_A and DataValidChk_A wrap their conditions in always.
Each assertion already starts an attempt on every clock. With always,
each attempt stays open until a failure, a reset or the end of the
test, so open attempts pile up and none ever succeeds.

Remove always so each enabled clock checks the condition once. A
mismatch still fails on that clock.

Signed-off-by: Kulan Palanichamy <kulan.palanichamy@opentitan.org>
The RAM interface builds the read data it passes to the icache in
two-state variables while errors are injected. Every way's read data
goes through them, so an X read from the RAMs reaches the icache as 0.

Make them four-state, so the icache sees the same X with and without
injection.

Signed-off-by: Kulan Palanichamy <kulan.palanichamy@opentitan.org>
VCS assertion coverage only includes assertions inside the DUT. The
checks in tb and in the RAM interface never appeared in the coverage
report, so nothing showed whether they ever succeed.

Add a bench cover config for those two scopes and pass it after the
common ones.

Signed-off-by: Kulan Palanichamy <kulan.palanichamy@opentitan.org>
@kulan-pal
kulan-pal force-pushed the dv/icache-ecc-checks branch from bc2f83e to 5bc6761 Compare October 16, 2026 18:48
@kulan-pal

Copy link
Copy Markdown
Contributor Author

Thanks @andreaskurth. I updated the ecc testplan entry as you suggested: it now says that ecc_error_o is checked against the injected errors, and that a data error in a line held in two ways is not checked yet.

@andreaskurth andreaskurth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, many thanks @kulan-pal!

@andreaskurth
andreaskurth added this pull request to the merge queue Oct 16, 2026
Merged via the queue into lowRISC:master with commit 21a9a53 Oct 17, 2026
13 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants