Skip to content

IFU: do not spill a fetch whose first half faults - #1982

Merged
rosethompson merged 3 commits into
openhwfoundation:mainfrom
davidharrishmc:dh/spill-firsthalf-fault
Oct 16, 2026
Merged

rosethompson merged 3 commits into
openhwfoundation:mainfrom
davidharrishmc:dh/spill-firsthalf-fault

Conversation

@davidharrishmc

Copy link
Copy Markdown
Contributor

Fixes finding E in #1980. When the first half of a fetch that crosses a cache line or page faulted, the IFU still decided to spill from the faulting fetch's stale bits and ORed in the second half's page fault, so it reported an instruction page fault with xtval at the first half where Sail and ImperasDV report the first half's access fault. The spill is now taken only when the first half fetches cleanly, so a faulting first half is reported alone, and the first-half fault latch added for #1838 is no longer needed.

🤖 Generated with Claude Code

When the first half of a fetch that crosses a cache line or page faults, its
bits are not the instruction, so its length is unknown. The IFU still decided
to spill from those stale bits and ORed the second half's page fault into the
first half's access fault, reporting an instruction page fault with xtval at
the first half (openhwfoundation#1980, finding E). Take the spill only when the first half
fetches cleanly; a faulting first half is then reported alone, which also
makes the first-half fault latch unnecessary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Harris <David_Harris@hmc.edu>
@davidharrishmc

Copy link
Copy Markdown
Contributor Author

Review notes: the RTL is correct and adds no timing depth (IFUFaultF already gates CacheRWF, so TakeSpillF already sat behind the PMA/PMP fault path). One gap:

  • No test fails without the fix. The bug reproduced only with the FORCE ELF. spillfault.S covers "first half faults, second half clean", which guards the removed latch but not this bug (first half an access fault, second half a page fault; expected cause 1 with xtval = PC). A deterministic case fits in spillfault.S: a PMP no-execute grain on the last halfword of page A, with page B non-executable. Expect mcause 1 and mtval = A+0xFFE; main reports 12.
  • Maintainability: correctness for a first-half walk fault relies on the ITLBMissOrUpdateAF port being wired to ITLBMissOrUpdateRawF (ifu.sv:167). Renaming the port, or adding a ~ITLBWalkFaultF term, would make the new comment's invariant ("the spill is not taken if it faults") explicit.

🤖 Generated with Claude Code

@davidharrishmc

Copy link
Copy Markdown
Contributor Author

Merge order: pairs with #1968 (IFU fetch-buffer reset; merge both) and with #1986, the LSU counterpart (no spill on a first-half fault). The three merge cleanly together.

🤖 Generated with Claude Code

spillbothhalvesfault.S fetches a 32-bit instruction whose first half is in a
PMP no-execute grain at the end of page A and whose second half is on a
non-executable page B.  The first half's access fault must be reported alone
(mcause 1, mtval at the first half); main reports the second half's page fault
(mcause 12), and ImperasDV lockstep flags the mismatch.

Rename the spill port ITLBMissOrUpdateAF to ITLBMissOrUpdateRawF: it must be
the miss before masking by a walk fault, since that is what keeps a faulting
first-half walk from taking the spill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@davidharrishmc

Copy link
Copy Markdown
Contributor Author

Addressed the review notes:

  • Directed test. New tests/coverage/spillbothhalvesfault.S fetches a 32-bit instruction whose first half is in a PMP no-execute grain at the end of page A and whose second half is on a non-executable page B, after a warm-up pass that puts the line in the I$. It expects one instruction access fault with mepc = mtval = the first half and no page fault. On main it fails: Wally reports mcause 12 where ImperasDV reports 1. With this PR it passes on Questa, Verilator and in lockstep.
  • Invariant made explicit. The spill port ITLBMissOrUpdateAF is renamed ITLBMissOrUpdateRawF, with comments in spill.sv. The port must carry the miss before masking by a walk fault, because that is what stops a faulting first-half walk from taking the spill: walk faults are not in InstrPageFaultF/InstrAccessFaultF. wave.do follows the rename.
  • Merged main.

🤖 Generated with Claude Code

@rosethompson
rosethompson merged commit 670db37 into openhwfoundation:main Oct 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants