Skip to content

Implement Zawrs and wait for wfi, wrs.nto and wrs.sto in one place - #1894

Closed
davidharrishmc wants to merge 9 commits into
openhwfoundation:mainfrom
davidharrishmc:dh/zawrs
Closed

davidharrishmc wants to merge 9 commits into
openhwfoundation:mainfrom
davidharrishmc:dh/zawrs

Conversation

@davidharrishmc

@davidharrishmc davidharrishmc commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds the Zawrs wait-on-reservation-set instructions, wrs.nto and wrs.sto, enabled on rv64gc and gated by ZAWRS_SUPPORTED elsewhere. It also rebuilds the wfi wait so that wfi, wrs.nto and wrs.sto share one wait mechanism. This supersedes #1923, #1933 and #1935 and carries their tests. #1926, now merged, is kept: FlushWCause = TrapM.

Wait design

A waiting instruction stays in M. WaitM stalls the whole pipeline through StallW, as an LSU stall does, so the instruction in W keeps forwarding. One flop, WaitedM, records that the instruction in M has waited, and one counter of waiting cycles sets the time limits. The counter holds while the instruction stays in M and clears when M advances.

  • hazard: StallWCause |= WaitM & ~FlushWCause, FlushWCause = TrapM, StallM = StallW
  • trap: ValidIntsM = (Committed | WaitedM) ? 0 : EnabledIntsM. InterruptM no longer gates on wfi.
  • Removed: WFIStallM, WFIInterruptedM, StallMCause, LatestUnstalledW, wfiW and the ~wfiM | wfiW interrupt gate.
wfi wrs.nto wrs.sto
Waits while no locally enabled interrupt is pending the reservation is valid and no interrupt is pending same as wrs.nto
Enabled interrupt already pending trap on the instruction itself same same
Woken by an interrupt retires; an enabled interrupt is taken with mepc = pc + 4 same same
No reservation — completes at once completes at once
Time limit below M with TW = 1, or U mode with S: illegal instruction after 2^WFI_TIMEOUT_BIT cycles, mepc = the wfi, not retired below M with TW = 1: illegal instruction after 2^WRSNTO_TIMEOUT_BIT; otherwise waits completes after 2^WRSSTO_TIMEOUT_BIT

The limits are 2^14, 2^13 and 2^12 cycles on every configuration. The counter is sized by WAIT_TIMEOUT_BIT, the largest limit in use.

With a single hart, no other agent can write the reservation set. Once a wrs holds a reservation, only an interrupt or a timeout ends the wait. The committed ReservationValidW is exported from lrsc.sv. Hypervisor support and hstatus.VTW are out of scope.

Bugs fixed

Also in this PR

PrivilegedM already requires funct3 = 0 and rd = 0, so the redundant rdzeroM terms on the fence encodings are dropped. The illegal encodings still trap.

Testing

New self-checking tests in tests/coverage, all listed in coverage64gc:

  • From the superseded PRs: wfitimeoutnext, wfiBackToBack, wfiForward, csrwfiInt.
  • New:
    • 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.
    • wrsWait: no reservation, the short timeout, the TW limit, U mode with TW = 0, an interrupt already pending, and forwarding across the wait.
test main Zawrs alone this PR
wfitimeoutnext fail pass pass
wfiBackToBack, wfiForward, csrwfiInt fail fail pass
wfitimeoutint fail fail pass
wfiTW, wfitimeout pass pass pass
wrsWait fail fail (stale lr result) pass

ACT priv suites (Zawrs, Interrupts, Sm, S, U, Ssstrict, Zicntr, Exceptions) were built with Zawrs enabled and all pass:

  • rv64gc: 366/366
  • rv32gc: 372/372

ImperasDV lockstep:

  • 0 mismatches on the ACT Zawrs and Interrupts ELFs and on wrsWait, wfiBackToBack, wfiForward, csrwfiInt and wfitimeout.
  • wfiTW, wfitimeoutint and wfitimeoutnext are waived in regression-wally. ImperasDV traps a TW = 1 wfi below M at once, while Wally waits a bounded time. Both are legal: mstatus_tw_always_illegal allows the first and mstatus_tw_op the second.

lint-wally is clean, and buildroot Linux boots to the login prompt.

Synthesis, sky130 rv64gc full core at 200 MHz, compared with the Zawrs-only version:

  • Total area is unchanged within noise.
  • Hazard + privdec + trap area is 8.6% smaller.
  • No path through StallW, StallM, FlushW, StallD or InterruptM gets slower.
  • The interrupt-pending-to-stall path improves from 5.77 to 5.58 ns.

Note for the reviewer

The ACT test-generation config for cvw-rv64gc must declare Zawrs, or the Ssstrict illegal-encoding sweep expects wrs.nto and wrs.sto to trap. Two files need changing:

  • the UDB yaml: add Zawrs and set ZAWRS_NTO_IS_NOP: false
  • sail.json: set Zawrs.supported: true

Supersedes #1923, #1933 and #1935.

🤖 Generated with Claude Code

davidharrishmc and others added 2 commits September 18, 2026 10:37
wrs.nto and wrs.sto wait on the reservation set.  Wally has a single hart, so no
other agent can write the reservation set and only an interrupt or the timeout can
end the wait, which makes both instructions behave like wfi.  They therefore reuse
the wfi stall path, the wfi interrupt delay, and the wfi timeout counter, so the
only new state is a pair of decoders.

The counter now clears whenever no wfi or wrs is waiting and saturates at the
threshold, rather than clearing only on a trap.  wfi relied on its timeout always
causing a trap to clear the counter, but a wrs timeout retires normally, so the
counter has to clear itself.

On timeout, wrs.nto raises an illegal instruction below M mode when mstatus.TW is
set, matching wfi.  Unlike wfi it does not trap in U mode when TW is clear, and
wrs.sto never traps; both simply complete.  The reservation set is not consulted,
which the specification permits since an implementation may end the stall for any
reason.  Hypervisor support and hstatus.VTW are out of scope.

Enabled on rv64gc only, gated by ZAWRS_SUPPORTED everywhere else.  Verified with
the ZawrsSm, ZawrsS, and ZawrsU arch-test suites on rv64gc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Shorten the wfi timeout from 2^16 to 2^14 cycles on every configuration, and give
the Zawrs instructions their own thresholds: 2^13 for wrs.nto and 2^12 for wrs.sto.
One counter still serves all three; each waiting instruction taps its own bit, so
the only cost is a two-input mux that collapses away when Zawrs is disabled.

WAIT_TIMEOUT_BIT sizes the counter to the largest threshold in use, and ignores the
wrs thresholds when Zawrs is disabled so those configurations keep the same flops.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidharrishmc and others added 3 commits September 18, 2026 11:25
The specification allows the hart to stall only while the reservation set is valid,
so a wrs with no reservation has to complete rather than waiting out its timeout.
Export the committed reservation state from lrsc up through atomic and lsu to
privdec, and require it before wrs waits.

Timing: ReservationValidW is the right signal to sample. The lr that sets it is in
W when the wrs reaches M, so it is already updated. It then holds for the whole
wait, because a wrs in M is not a memory operation and a wait stall does not assert
StallW, which is what gates the flop.

The timeout counter still keys off wrsM rather than the gated wait signal. Keying it
off the gated signal would clear the counter as soon as the timeout fired, re-arming
the wait and livelocking.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
wrs.nto has no timeout: only an interrupt, an invalidated reservation, or the
mstatus.TW illegal instruction below M mode ends it, so like wfi it waits
indefinitely otherwise.  wrs.sto keeps completing when its timeout expires.

Completing wrs.nto on timeout was only there to keep a wrs with no reservation from
hanging, which the reservation check now handles properly.

WRSNTO_TIMEOUT_BIT still sets the bounded time limit after which mstatus.TW turns
the wait into an illegal instruction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
privdec is instantiated only when Zicsr is supported, and that is the first term
selecting the controller's exact decode of the privileged fields, so PrivilegedM
already requires funct3 = 0 and rd = 0.  The rdzeroM terms on sinval.vma,
sfence.w.inval, sfence.inval.ir, and sfence.vma were therefore redundant, and
rdzeroM itself is now unused.

An encoding probe confirms the behavior is unchanged: the four well-formed fence
encodings still execute and all four with rd = x1 still raise illegal instruction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidharrishmc and others added 3 commits October 6, 2026 07:45
…t redesign

upstream/main has since reverted Zimop, Zcmop and Zabha; the conflicts keep
main's side and take only the Zawrs lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Harris <David_Harris@hmc.edu>
A waiting wfi or wrs 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 instruction in M has waited, and one counter of
waiting cycles, held while the instruction stays in M and cleared when M
advances, provides the mstatus.TW limit (wfi, wrs.nto) and the wrs.sto
short timeout.

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 or wrs 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);
- the next instruction took a spurious TW trap when a wfi woke on the
  cycle its count reached the limit (openhwfoundation#1923), and the count no longer
  depends on TrapM, so it cannot change while a trap waits for StallW
  (openhwfoundation#1930);
- a TW timeout coinciding with an enabled interrupt wrote mcause = 5
  without the interrupt bit: WaitedM gates ValidIntsM, so CauseM agrees.

wrs.nto now shares the TW limit with wfi (WRSNTO_TIMEOUT_BIT and
WAIT_TIMEOUT_BIT are removed); the counter is sized in privdec.

Tests: wfitimeoutnext, wfiBackToBack, wfiForward and csrwfiInt from
openhwfoundation#1923/openhwfoundation#1926/openhwfoundation#1933/openhwfoundation#1935, plus wfitimeoutint (TW timeout racing an enabled
interrupt), wfiTW (TW wait woken before the limit, timeout not retired,
U mode) and wrsWait (Zawrs wake, timeout and forwarding cases).

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

Bring back openhwfoundation#1894's WRSNTO_TIMEOUT_BIT and derived WAIT_TIMEOUT_BIT, so
wfi taps WFI_TIMEOUT_BIT, wrs.nto taps WRSNTO_TIMEOUT_BIT and wrs.sto taps
WRSSTO_TIMEOUT_BIT on the one wait counter, sized by WAIT_TIMEOUT_BIT.
The configuration files are again identical to openhwfoundation#1894.

Waive wfiTW, wfitimeoutint and wfitimeoutnext in lockstep: ImperasDV traps
a wfi below M mode with mstatus.TW = 1 at once, while Wally waits a bounded
time, and both are legal (mstatus_tw_always_illegal, mstatus_tw_op).

Replace the stale wfiW, WFIStallM and WFITimeoutM names in the FPGA debug
lists and wave.do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Harris <David_Harris@hmc.edu>
@davidharrishmc davidharrishmc changed the title Implement the Zawrs extension Implement Zawrs and wait for wfi, wrs.nto and wrs.sto in one place Oct 6, 2026
@davidharrishmc
davidharrishmc marked this pull request as ready for review October 6, 2026 22:10
…enhwfoundation#1932) into the wait redesign

Resolve hazard.sv and wave.do in favor of the unified wait (WaitM), which
already has openhwfoundation#1926's FlushWCause = TrapM, and drop the duplicate
wfiBackToBack entry in coverage64gc.

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

Split in two: the wfi wait fix is #1941 (against main), and Zawrs is #1942 (against rva23, with #1941's wait commit cherry-picked first, so review only its Zawrs commit).

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.

1 participant