Context
tech/INVARIANTS.md R1 says: "parent design = macros + wires, no logic." compute_array.sv currently violates this with ~302 parent flops on the BCAST_PIPE forward and reverse pipes — all on parent chip clk. These were the right call during B4/B5/B6 iteration when BCAST_PIPE was a tuneable knob. With B6 (PR #40/#41) landed and stable, the routing problem that motivated BCAST_PIPE has been eliminated by the abutment chain topology.
This issue tracks TWO related cleanups. They must be sequenced — option 2 may subsume option 1.
Background: what BCAST_PIPE was for
PR #27 added BCAST_PIPE to solve the long fanout from cmd_unit to all 32 skew_lanes. Without it, push_a_bytes[256:0] had to fan out to 32 distant endpoints across ~1.5 mm of die. BCAST_PIPE=1 broke that net in half with a parent flop stage.
B6 (#40) eliminated this fanout entirely. cmd_unit now drives exactly one destination per signal — the chain head (sa_chain_w_s[0], sb_chain_w_w[0]). The chain register inside each skew_lane_a/b instance then propagates by abutment, no parent routing involved.
So BCAST_PIPE's original justification is gone. What remains is one combinational arc per signal from cmd_unit out → chain head in:
[cmd_unit internal flop] → cmd_unit M-layer output → parent route to skew_a[0] → skew_a[0] pin → [chain register]
With the current B6 layout, cmd_unit and skew_a[0] are both in the SW corner — short hop.
Two violations of R1
Forward pipe (compute_array.sv ~lines 165-230)
Registers cmd_unit's push_* outputs before feeding chain heads. With BCAST_PIPE=1:
push_now_piped (1 b) × 1 stage = 1 flop
push_slot_piped (2 b) × 1 stage = 2 flops
push_accum_piped (1 b) × 1 stage = 1 flop
push_a_bytes_piped (256 b) × 1 stage = 256 flops
- (drain_* / scrub_en piped similarly — verify exact count when implementing)
- Total: ~258-260 parent flops
Reverse pipe (compute_array.sv:247-299)
Registers cmd_unit's status outputs symmetrically with the forward pipe so chip-external completion events arrive in lockstep with the cells that actually finished the work.
mb_pipe (mma_busy, 1 b) × 1 = 1
md_pipe (mma_done, 1 b) × 1 = 1
ae_pipe (arrive_en, 1 b) × 1 = 1
ab_pipe (arrive_bar_id, 32 b) × 1 = 32
db_pipe (drain_busy, 1 b) × 1 = 1
dd_pipe (drain_done, 1 b) × 1 = 1
drv_pipe (drain_row_valid, 1 b) × 1 = 1
dri_pipe (drain_row_idx, 5 b) × 1 = 5
dl_pipe (drain_last, 1 b) × 1 = 1
- Total: ~44 parent flops
Combined: ~302 parent flops on chip clk that should not exist at parent level.
Option 1 — Delete BCAST_PIPE entirely (recommended; try first)
The cleanest fix. B6's chain topology eliminated the routing problem BCAST_PIPE was designed to solve, so the entire mechanism is likely scaffolding we forgot to remove.
Plan
- Edit
tech/asap7/orfs/compute_array_abut.config.mk: VERILOG_FILES → /work/build/sv2v/chip_top_bcast0.v (BCAST_PIPE=0).
- Re-harden compute_array_abut (~40 min wall).
- Verify: 0 DRC, positive setup slack on the cmd_unit → chain-head paths. Current B6 close has +295 ps slack to spare, so the extra combinational arc has a lot of room — but not certain to close. If it fails by a small amount, we know empirically that BCAST_PIPE=1 IS needed and the experiment was worth running.
- If it closes:
- Delete forward pipe (compute_array.sv ~165-230)
- Delete reverse pipe (compute_array.sv:247-299)
- Delete BCAST_PIPE parameter (compute_array.sv:13-25 comment block + parameter handling)
- Delete
chip_top_bcast{0,1,2,3}.v — replaced by single chip_top.v
- Delete
sv2v-bcast-sweep make target in tech/sky130/Makefile
- Update pymodel to remove BCAST_PIPE cycle compensation
- cocotb compute_array regression — must remain PASS 2/2 vs pymodel
- One more compute_array_abut re-harden to confirm post-cleanup
Estimated savings
- ~302 parent flops gone (off chip clk parent CTS)
- ~4 sv2v sweep files deleted (~MB of build artifacts)
- BCAST_PIPE parametric machinery in
compute_array.sv, cmd_unit.sv, pymodel — all simpler
Risk
- If BCAST_PIPE=0 fails to close, we waste one harden cycle (~40 min). Falls back to Option 2.
Option 2 — Absorb pipes into cmd_unit (fallback if Option 1 fails)
If the cmd_unit → chain-head combinational arc is too long to close at BCAST_PIPE=0, the pipe stage is genuinely load-bearing. In that case, move it inside cmd_unit instead of leaving it at parent.
Plan
- Edit
cmd_unit/cmd_unit.sv: add internal register stages for all output signals (push_* forward, status reverse).
- Delete forward + reverse pipes from compute_array.sv.
- Re-harden cmd_unit (~3-5 min — tiny macro).
- Re-harden compute_array_abut (~40 min).
- Verify cocotb + 0 DRC + positive slack.
Delta to macro IO
Zero. cmd_unit's pin contract is unchanged:
- Same 9 status output signals (45 bits total)
- Same push_* forward outputs
- Same widths, same pin TCL geometry
- Just the register stage location moves from parent to cmd_unit
cmd_unit is already stateful (FSM, drain pulser, arrive handshake), already has reset, already has clk-to-output timing arcs in its .lib. Adding ~302 more flops to a macro that already has thousands is rounding error for size/power.
Architectural cost
Locks BCAST_PIPE value into hardened cmd_unit silicon. Future tuning requires re-harden. Acceptable post-B6 since:
- B6 made BCAST_PIPE no longer the load-bearing knob it once was
- cmd_unit re-harden is 3-5 min — not a real blocker
Sequencing & dependencies
Recommendation: Option 1 first. Cheap experiment (one harden), high upside (large cleanup). If it succeeds, Option 2 becomes unnecessary. If it fails, Option 2 is the natural fallback.
Either option must:
- Maintain cocotb
compute_array PASS 2/2 vs pymodel
- Maintain 0 DRC on the 32×32 harden
- Maintain positive setup + hold slack
Do not touch this before #28 (chip_top integration) — chip_top may surface other timing pressures that change the calculus. Keeping BCAST_PIPE alive as an escape hatch through chip_top closure is conservative; doing the experiment AFTER #28 lets the chip_top closure data inform whether the knob is still worth having.
Acceptance criteria
Related
Context
tech/INVARIANTS.mdR1 says: "parent design = macros + wires, no logic." compute_array.sv currently violates this with ~302 parent flops on the BCAST_PIPE forward and reverse pipes — all on parent chip clk. These were the right call during B4/B5/B6 iteration when BCAST_PIPE was a tuneable knob. With B6 (PR #40/#41) landed and stable, the routing problem that motivated BCAST_PIPE has been eliminated by the abutment chain topology.This issue tracks TWO related cleanups. They must be sequenced — option 2 may subsume option 1.
Background: what BCAST_PIPE was for
PR #27 added BCAST_PIPE to solve the long fanout from cmd_unit to all 32 skew_lanes. Without it,
push_a_bytes[256:0]had to fan out to 32 distant endpoints across ~1.5 mm of die. BCAST_PIPE=1 broke that net in half with a parent flop stage.B6 (#40) eliminated this fanout entirely. cmd_unit now drives exactly one destination per signal — the chain head (
sa_chain_w_s[0],sb_chain_w_w[0]). The chain register inside each skew_lane_a/b instance then propagates by abutment, no parent routing involved.So BCAST_PIPE's original justification is gone. What remains is one combinational arc per signal from cmd_unit out → chain head in:
With the current B6 layout, cmd_unit and skew_a[0] are both in the SW corner — short hop.
Two violations of R1
Forward pipe (compute_array.sv ~lines 165-230)
Registers cmd_unit's
push_*outputs before feeding chain heads. With BCAST_PIPE=1:push_now_piped(1 b) × 1 stage = 1 floppush_slot_piped(2 b) × 1 stage = 2 flopspush_accum_piped(1 b) × 1 stage = 1 floppush_a_bytes_piped(256 b) × 1 stage = 256 flopsReverse pipe (compute_array.sv:247-299)
Registers cmd_unit's status outputs symmetrically with the forward pipe so chip-external completion events arrive in lockstep with the cells that actually finished the work.
mb_pipe(mma_busy, 1 b) × 1 = 1md_pipe(mma_done, 1 b) × 1 = 1ae_pipe(arrive_en, 1 b) × 1 = 1ab_pipe(arrive_bar_id, 32 b) × 1 = 32db_pipe(drain_busy, 1 b) × 1 = 1dd_pipe(drain_done, 1 b) × 1 = 1drv_pipe(drain_row_valid, 1 b) × 1 = 1dri_pipe(drain_row_idx, 5 b) × 1 = 5dl_pipe(drain_last, 1 b) × 1 = 1Combined: ~302 parent flops on chip clk that should not exist at parent level.
Option 1 — Delete BCAST_PIPE entirely (recommended; try first)
The cleanest fix. B6's chain topology eliminated the routing problem BCAST_PIPE was designed to solve, so the entire mechanism is likely scaffolding we forgot to remove.
Plan
tech/asap7/orfs/compute_array_abut.config.mk:VERILOG_FILES→/work/build/sv2v/chip_top_bcast0.v(BCAST_PIPE=0).chip_top_bcast{0,1,2,3}.v— replaced by singlechip_top.vsv2v-bcast-sweepmake target intech/sky130/MakefileEstimated savings
compute_array.sv,cmd_unit.sv, pymodel — all simplerRisk
Option 2 — Absorb pipes into cmd_unit (fallback if Option 1 fails)
If the cmd_unit → chain-head combinational arc is too long to close at BCAST_PIPE=0, the pipe stage is genuinely load-bearing. In that case, move it inside cmd_unit instead of leaving it at parent.
Plan
cmd_unit/cmd_unit.sv: add internal register stages for all output signals (push_* forward, status reverse).Delta to macro IO
Zero. cmd_unit's pin contract is unchanged:
cmd_unit is already stateful (FSM, drain pulser, arrive handshake), already has reset, already has clk-to-output timing arcs in its
.lib. Adding ~302 more flops to a macro that already has thousands is rounding error for size/power.Architectural cost
Locks BCAST_PIPE value into hardened cmd_unit silicon. Future tuning requires re-harden. Acceptable post-B6 since:
Sequencing & dependencies
Recommendation: Option 1 first. Cheap experiment (one harden), high upside (large cleanup). If it succeeds, Option 2 becomes unnecessary. If it fails, Option 2 is the natural fallback.
Either option must:
compute_arrayPASS 2/2 vs pymodelDo not touch this before #28 (chip_top integration) — chip_top may surface other timing pressures that change the calculus. Keeping BCAST_PIPE alive as an escape hatch through chip_top closure is conservative; doing the experiment AFTER #28 lets the chip_top closure data inform whether the knob is still worth having.
Acceptance criteria
compute_arrayPASS 2/2 (no functional regression)tech/INVARIANTS.mdR1 violation list updated (remove the "should be absorbed into cmd_unit per Manifest-driven build system for hardened-macro abutment designs #43-followup" line)Related
tech/INVARIANTS.md