Skip to content

Implement the Zawrs extension (rva23) - #1942

Open
davidharrishmc wants to merge 2 commits into
openhwfoundation:rva23from
davidharrishmc:dh/zawrs-rva23
Open

davidharrishmc wants to merge 2 commits into
openhwfoundation:rva23from
davidharrishmc:dh/zawrs-rva23

Conversation

@davidharrishmc

Copy link
Copy Markdown
Contributor

What

Adds the Zawrs wait-on-reservation-set instructions, wrs.nto and wrs.sto, to the rva23 branch. They are enabled on rv64gc and gated by ZAWRS_SUPPORTED elsewhere.

Targets rva23. The first commit is a cherry-pick of the wfi wait commit from #1941 (targeting main). Review only the Zawrs commit, "Implement the Zawrs extension". The duplicate drops out once #1941 is merged to main and main is merged into rva23.

#1941's #1925 merge and its #1930 trap-CSR commit are not included. #1930's tests need #1925's ExternalStall injector, and Zawrs does not depend on either.

Design

Zawrs adds wrs terms to the unified wait mechanism and changes nothing else in it.

  • Wait condition: WaitM = (wfiM | (wrsntoM | wrsstoM) & ReservationValidW) & ~IntPendingM & ~TWTimeoutM & ~STOTimeoutM
  • Reservation: with one hart, no other agent can write the reservation set. A wrs waits only while the committed reservation (ReservationValidW, exported from lrsc through atomic and lsu) is valid. Once it holds one, only an interrupt or a timeout ends the wait.
wrs.nto wrs.sto
No reservation completes at once completes at once
Interrupt already enabled and pending trap on the wrs same
Woken by an interrupt retires; an enabled interrupt is taken with mepc = pc + 4 same
Below M, mstatus.TW = 1 illegal instruction after 2^WRSNTO_TIMEOUT_BIT not affected
Otherwise waits for an interrupt (no trap in U mode when TW = 0) completes after 2^WRSSTO_TIMEOUT_BIT

Parameters on every configuration:

  • WFI_TIMEOUT_BIT = 14, WRSNTO_TIMEOUT_BIT = 13, WRSSTO_TIMEOUT_BIT = 12.
  • The wait counter is sized by the derived WAIT_TIMEOUT_BIT.
  • ImperasDV runs with Zawrs=T.
  • riscvassertions requires Zalrsc.

Hypervisor support and hstatus.VTW are out of scope.

Testing

check (rv64gc unless noted) result
Verilator: wrsWait, wfitimeoutnext, wfiBackToBack, wfiForward, csrwfiInt, wfitimeoutint, wfiTW all pass
wfitimeout runs to completion (it has no self-check)
coverage64gc SUCCESS
Lockstep wrsWait, csrwfiInt 0 mismatches
ACT ZawrsSm/S/U + InterruptsSm/S, Zawrs-enabled config 35/35
ACT InterruptsSm/S, rv32gc 32/32
lint-wally clean

The wfi tests also pass on the cherry-picked wait commit alone.

wrsWait covers: no reservation, the short timeout, the TW limit, U mode with TW = 0, an interrupt already pending, and forwarding of the lr result across the wait. Without the wait mechanism, the lr result before a wrs was stale.

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

The results above used a local config with both changes.

#1894's privdec cleanup that dropped the redundant rd = 0 checks is not included. It can follow separately.

Replaces #1894.

🤖 Generated with Claude Code

davidharrishmc and others added 2 commits October 6, 2026 15:32
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>
(cherry picked from commit c7cbd78)
wrs.nto and wrs.sto wait on the reservation set, enabled on rv64gc and
gated by ZAWRS_SUPPORTED elsewhere.  From openhwfoundation#1894 (2551aae..033ec6f),
rebuilt on the unified wait mechanism: the wrs terms are added to WaitM,
the wait counter and the timeouts, and nothing else in the wait logic
changes.

- Decode wrs.nto and wrs.sto in privdec; both are legal in all modes.
- With one hart no other agent can write the reservation set, so a wrs
  waits only while the committed reservation (ReservationValidW, exported
  from lrsc through atomic and lsu) is valid, and then only an interrupt or
  a timeout ends the wait.  Without a reservation it completes at once.
- wrs.sto completes after 2^WRSSTO_TIMEOUT_BIT cycles.  wrs.nto below M
  mode with mstatus.TW = 1 raises an illegal instruction after
  2^WRSNTO_TIMEOUT_BIT cycles; otherwise it waits for an interrupt.  Unlike
  wfi it does not trap in U mode when TW = 0.
- WFI_TIMEOUT_BIT drops to 14, WRSNTO_TIMEOUT_BIT = 13, WRSSTO_TIMEOUT_BIT
  = 12 on every configuration; the wait counter is sized by the derived
  WAIT_TIMEOUT_BIT.
- ImperasDV runs with Zawrs enabled; riscvassertions requires Zalrsc.

Test: wrsWait (no reservation, short timeout, TW limit, U mode with TW = 0,
an interrupt already pending, and forwarding of the lr result across the
wait).

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