Add write skid buffer for same-register read-modify-write - #330
Open
tancheng wants to merge 2 commits into
Open
Conversation
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
force-pushed
the
register-bank-write-skid-buffer
branch
from
July 17, 2026 04:19
2c43ee9 to
b672077
Compare
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.
Follow-up to #322 (based on its branch; retarget to
masterafter #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, SOUTHin 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).
outport_wr_rdy = ~skid_valid— a write is accepted whenever the skid is free. Pure registered state: the producer'srdynever combinationally depends on any consumer's readiness (deliberately not onskid_commit, which derives fromctrl_proceedand 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.ctrl_proceedpulse — 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.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-wordINCaccumulator 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.ctrl_proceed), since commit timing is tied to step completion; historical expectations are preserved, with stalled-sink tests observing one leading unarmed read.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_vectorignoringsend_accepted) is separate and remains open in #286 — this PR handles the register-file half (stability + RMW liveness).