Skip to content

Add write skid buffer for same-register read-modify-write - #330

Open
tancheng wants to merge 2 commits into
321-register-bank-token-trackingfrom
register-bank-write-skid-buffer
Open

Add write skid buffer for same-register read-modify-write#330
tancheng wants to merge 2 commits into
321-register-bank-token-trackingfrom
register-bank-write-skid-buffer

Conversation

@tancheng

Copy link
Copy Markdown
Owner

Follow-up to #322 (based on its branch; retarget to master after #322 merges). Removes #322's known limitation and addresses the register half of #281/#286.

Problem

Under #322's token discipline, an operation that reads and writes the same register within one ctrl step deadlocked: the write was rejected while the operand's token was unconsumed, and the step could not complete until the write was delivered. This pattern is real — the mapper emits it (NOT $0 -> $0, SOUTH in the histogram kernel from #281), and it is the natural encoding of an accumulator.

Design

Each bank gains a one-entry write skid buffer (one write port ⇒ at most one blocked write, so one entry per bank suffices; the cluster/tile interfaces are unchanged).

  • Acceptance: outport_wr_rdy = ~skid_valid — a write is accepted whenever the skid is free. Pure registered state: the producer's rdy never combinationally depends on any consumer's readiness (deliberately not on skid_commit, which derives from ctrl_proceed and would close a loop through the FU's rdy chain), and a direct write can never collide with a commit on the single write port.
  • Routing: an accepted write lands directly if that cannot disturb anything (target token-free and not read by the current step), or at the reading step's boundary (write-through on the ctrl_proceed pulse — the new token atomically replaces the consumed one; set wins over clear). Otherwise it parks in the skid and commits once its target is no longer read by an open step.
  • Stability invariant: a register being read by an open step never changes value until the step completes. This matters beyond RMW: the previous behavior (direct write whenever token-free) allowed [P0] Simulator and rtl comparison (latency gap) debug #281's partial-multicast corruption — a speculative register write landing while another fan-out leg is backpressured, then re-execution on the changed operand. The tile-level test below caught exactly this during development.
  • Skid state is cleared on task switch along with the token bookkeeping.

With the write-through path, a same-register accumulator runs at full rate (one iteration per step, no bubble).

Tests

  • test_reg_cluster_write_skid_buffer: three back-to-back writes to one register with a stalled consumer flow through park→commit→park; all three delivered in order.
  • test_tile_same_register_accumulate: single-ctrl-word INC accumulator through one register that also fans out to a stalled tile outport — the [P0] Simulator and rtl comparison (latency gap) debug #281 shape end-to-end at tile level. Deadlocks at max-cycles against the Prevent overwriting unconsumed tokens in register banks #322 base (verified as a negative control); completes with the skid. The unarmed first read seeds the accumulator with the register default, so the outport observes 1, 2, 3.
  • Cluster test harnesses now always emulate the tile's per-step done-tracking (ctrl_proceed), since commit timing is tied to step completion; historical expectations are preserved, with stalled-sink tests observing one leading unarmed read.
  • Kernel regression sweep (everything that ever failed on Prevent overwriting unconsumed tokens in register banks #322's CI rounds: CgraRTL_test ×3, FIR terminate, streaming ×2, vector global reduce, migration ×3): 10/10 pass locally.

Relation to #286

The xbar half of #281 (send_rdy_vector ignoring send_accepted) is separate and remains open in #286 — this PR handles the register-file half (stability + RMW liveness).

Under token discipline, an operation that reads and writes the same
register within one ctrl step (an accumulator, or #281's
`NOT $0 -> $0, SOUTH` under backpressure) deadlocked: the write was
rejected while the operand token was unconsumed, and the step could
not complete until the write was delivered.

Each bank now has a one-entry write skid buffer (one write port ->
at most one blocked write). A write is accepted whenever the skid is
free (outport_wr_rdy = ~skid_valid, pure registered state, so the
producer's rdy never combinationally depends on any consumer's
readiness and never collides with a commit on the single write port):
- It lands directly if that cannot disturb an in-flight read (target
  token-free and not read by the current step), or at the boundary of
  the reading step (the write's token atomically replaces the consumed
  one; set wins over clear in the token update).
- Otherwise it parks in the skid and commits once its target is no
  longer read by an open step: at that step's completion pulse, or
  immediately if the target is not being read and holds no token.

The stability invariant — a register being read holds its value until
the step completes — also fixes the partial-multicast corruption from
issue #281 (a speculative register write landing while another fan-out
leg is backpressured, followed by re-execution on the changed value),
which the previous direct-write-if-token-free behavior still allowed.

Tests:
- test_reg_cluster_write_skid_buffer: three back-to-back writes with a
  stalled consumer flow through park/commit in order.
- test_tile_same_register_accumulate: single-ctrl-word INC accumulator
  through one register with a stalled tile outport (the #281 shape);
  deadlocks without the skid buffer (verified), completes with it.
- The cluster test harnesses now always emulate the tile's per-step
  done-tracking to drive inport_ctrl_proceed, since commit timing is
  tied to step completion.
@tancheng
tancheng force-pushed the register-bank-write-skid-buffer branch from 2c43ee9 to b672077 Compare July 17, 2026 04:19
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