15.1"Zacas": Atomic Compare-and-Swap

Part III Linux boot: optional Vol. I (Unprivileged) pp. 76–80 · ~3 min read

LR/SC gives RISC-V lock-free primitives with a forward-progress asterisk; Zacas adds the industry-standard alternative — single-instruction compare-and-swap at 32, 64, and (RV64) 128 bits. Depends on Zaamo; encoded as an ordinary AMO:

001013127aq26rl25rs22420rs11915010/011/1001412rd117010111160AMOCAS.W/D/Q (AMO opcode)
Click a field for its role.

Semantics: temp = mem[rs1]; if (temp == rd) mem[rs1] = rs2; rd = temp. The dual-role rd is the ergonomic win — a failed CAS returns the fresh value, so retry loops never reload.

The width matrix
RV32RV64
AMOCAS.W32-bit, plain registers.Compares against rd[31:0], stores rs2[31:0], result SIGN-EXTENDED into rd.
AMOCAS.D64-bit via EVEN register pairs rd/rd+1, rs2/rs2+1.64-bit, plain registers.
AMOCAS.Q128-bit via even pairs — the ABA-killer width.
Dotted-underlined cells have explanations — click one.

The expected-value pair may be assembled with two ordinary loads that race against writers — harmless: the CAS’s own load-pair is atomic, and any inconsistency simply fails the compare while handing back the truth.

Ordering fine print

Success: aq/rl work as on any AMO. Failure: acquire (if aq) but never release, whatever rl says — there’s no meaningful publish when nothing was stored. A failed CAS may write the old value back or skip the write, but either way it’s an AMO for RVWMO’s PPO rules — and consequently always requires write permission, even to fail.

Hardware Designer Notes

AMOCAS.Q is the sizing question: a 128-bit atomic needs a 16-byte single-beat path to the coherence point (or line-locking). If your data bus is 64 bits, budget the Q variant as a two-beat locked sequence in the L1 controller — and get the failure-ordering corner (acquire-not-release) into your litmus-test suite early.

Minimal Linux-boot hart MUST

  • Extend the AMO datapath with a compare-before-write: the ALU stage that computed add/and/or for AMOs gains an equality path gating the store
  • For paired widths: an atomic load-pair/store-pair path and the even-register decode check (odd → illegal)
  • Enforce write permission on the address regardless of comparison outcome, and the no-release-on-failure rule in your fence/ordering logic

MAY simplify / trap-and-emulate

  • Implement the maybe-write on failure as an unconditional writeback of the old value — legal, and it simplifies single-beat memory systems
  • Skip Zacas for a Linux-boot v1 (kernel falls back to LR/SC) — RVA23 lists it; 128-bit CAS is what makes userspace lock-free libraries portable from x86

Check yourself — AMOCAS

1.What double duty does rd serve in AMOCAS, and why is that clever?

2.How does RV32 get a 64-bit CAS (and RV64 a 128-bit one)?

3.A CAS with .aqrl FAILS the comparison. What ordering does it provide?

4.Why does the Michael-Scott queue example use AMOCAS.Q on a pointer that's only 64 bits?

4 questions