Repository navigation
Wait for wfi in one place; record traps only when taken - #1941
Open
davidharrishmc wants to merge 7 commits into
Open
davidharrishmc wants to merge 7 commits into
davidharrishmc wants to merge 7 commits into
Conversation
A divide mispredicted as a jump raises FlushE in its DONE cycle; when StallM held it in Execute the divider FSM went IDLE and restarted with stale operands. Reset the FSMs only when Execute advances; add an opt-in testbench ExternalStall injector and the divflushstall test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
…v.sv on rv32gc Use FlushE & ~StallE, matching flopenrc, which clears only when enabled. rv64gc divides on the FPU, so add divflushstall32 for rv32gc to exercise div.sv. RV32 self-checking tests live in tests/coverage32 (built by 'make coverage', run by the self-checked coverage32gc suite) so the nightly lockstep run over tests/coverage on rv64gc does not pick them up. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
StallE equals StallM whenever FlushE is asserted: FlushECause masks the divider's own StallECause, and LatestUnstalledE implies ~StallE. FlushE & ~StallM is therefore the same condition, and it needs no new StallE port through fpu, fdivsqrt, mdu, the core and testbench_fp. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
The net now has two drivers, the RVVI wrapper and the stall injector, so name it after the core port it drives. rvvitbwrapper keeps its RVVIStall port. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
A waiting wfi now stalls the whole pipeline through StallW (WaitM), like an LSU stall, instead of stalling M and flushing W. One flop (WaitedM) records that the wfi in M has waited, and one counter of waiting cycles, held while the wfi stays in M and cleared when M advances, provides the mstatus.TW time limit (WFI_TIMEOUT_BIT, unchanged). This replaces WFIStallM, WFIInterruptedM, StallMCause, LatestUnstalledW, wfiW and the wfiM | wfiW interrupt gate, and fixes: - the instruction after a woken wfi used a stale operand, because W was flushed every waiting cycle and forwarding stopped (openhwfoundation#1933); - an interrupt already enabled and pending when a wfi executed, e.g. just enabled by a CSR write or xRET, was taken after the wfi retired; it is now taken on the wfi (openhwfoundation#1935). A wfi woken by an interrupt still retires and the interrupt is taken with mepc = pc + 4; - a trapping wfi both retired and trapped: FlushWCause = TrapM (openhwfoundation#1926, reverted on main by openhwfoundation#1939, restored here with its wfiBackToBack test and the corrected ecall/ebreak comments); - the next instruction took a spurious TW trap when a wfi woke on the cycle its count reached the limit (openhwfoundation#1923); the count no longer depends on TrapM, so it cannot change while a trap waits for StallW; - a TW timeout coinciding with an enabled interrupt wrote mcause = 5 without the interrupt bit: WaitedM gates ValidIntsM, so CauseM agrees. The wait logic is written so that Zawrs only adds wrs terms to WaitM, the counter and the timeouts. Tests: wfitimeoutnext, wfiBackToBack, wfiForward, csrwfiInt, wfitimeoutint and wfiTW. wfiTW, wfitimeoutint and wfitimeoutnext are waived in lockstep: ImperasDV traps a wfi below M with mstatus.TW = 1 at once while Wally waits a bounded time, and both are legal (mstatus_tw_always_illegal, mstatus_tw_op). The FPGA debug lists and wave.do drop the removed names. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
From openhwfoundation#1930. xEPC, xCAUSE and xTVAL were written every cycle TrapM was high, while mstatus, the privilege mode and the PC wait for ~StallW. If the trap changes during a stall (an M timer interrupt overtaking a delegated illegal instruction under ExternalStall), scause/sepc/stval were overwritten for a trap that was never taken. MTrapM and STrapM, and HPM events 22 (interrupts) and 23 (exceptions), are now gated with TrapM & ~StallW at each use, so each trap is recorded and counted once. openhwfoundation#1930 also gated the WFI counter reset with ~StallW. That part is not needed: the wait counter no longer uses TrapM. It holds while the wait instruction stays in M and clears when M advances, which for a trap is exactly when the trap is taken. Tests: trapcsrstall and hpmtrapcount (they use the openhwfoundation#1925 ExternalStall injector). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: David Harris <David_Harris@hmc.edu>
This was referenced Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A waiting wfi now stalls the whole pipeline through
StallW, as an LSU stall does, instead of stalling M and flushing W. Trap CSRs and trap HPM events are recorded only when the trap is taken.This PR contains #1925's commits (merged, not rebased) and should merge after or together with #1925. It replaces #1930 (closed) and covers #1926, which was reverted by #1939. It supersedes #1923, #1933 and #1935.
Wait design
A waiting wfi stays in M.
WaitMstalls the whole pipeline, so the instruction in W keeps forwarding.WaitM = wfiM & ~IntPendingM & ~TWTimeoutMWaitedM <= StallM & (WaitM | WaitedM), records that the wfi in M has waited.WaitCount <= StallM ? WaitCount + (WaitM & ~msb) : 0, sets the mstatus.TW limit.WFI_TIMEOUT_BITis unchanged. The counter holds while the wfi stays in M and clears when M advances.StallWCause |= WaitM & ~FlushWCause,FlushWCause = TrapM,StallM = StallWValidIntsM = (Committed | WaitedM) ? 0 : EnabledIntsM. InterruptM no longer gates on wfi.WFIStallM,WFIInterruptedM,StallMCause,LatestUnstalledW,wfiWand the~wfiM | wfiWgate.Behavior:
wfi_mepc_val).The logic is written so that Zawrs only adds wrs terms to
WaitM, the counter and the timeouts.Bugs fixed
li a0,42; wfi; addi a1,a0,0gave the old a0, and a following store wrote stale data.intr_mip_mie_xret_csrwrrequires.FlushWCause = TrapM, with the corrected ecall/ebreak comments.ValidIntsMwithWaitedMkeepsCauseMconsistent.TrapMwas high, while mstatus, the privilege mode and the PC wait for~StallW. Under ExternalStall, a trap that changed during the stall overwrote scause/sepc/stval for a trap that was never taken.MTrapM/STrapMand HPM events 22 and 23 now useTrapM & ~StallWat each use. There is no namedTrapTakenMsignal.~StallWgate on the WFI counter reset is not needed. The counter no longer usesTrapM; it clears when M advances, which for a trap is exactly when the trap is taken.Testing
Tests in
tests/coverage, listed incoverage64gc:wfitimeoutnext,wfiBackToBack,wfiForward,csrwfiInt.wfitimeoutint: a TW timeout racing an enabled interrupt.wfiTW: a TW wait woken before the limit; a timed-out wfi that does not retire; U mode.trapcsrstall,hpmtrapcount.divflushstallwfitimeoutwfiForward,csrwfiIntlint-wallyOn main,
wfitimeoutnext,wfiBackToBack,wfiForward,csrwfiIntandwfitimeoutintfail.wfiTW,wfitimeoutintandwfitimeoutnextare waived in lockstep. ImperasDV traps a wfi below M with TW = 1 at once, while Wally waits a bounded time. Both are legal:mstatus_tw_always_illegalallows the first andmstatus_tw_opthe second.Synthesis was measured on the combined wfi + Zawrs version (sky130, rv64gc core, 200 MHz). Compared with main + Zawrs:
It has not been re-measured for this wfi-only version.
🤖 Generated with Claude Code