feat: real hierarchical 2-level arbiter, real N=16 timing WNS -0.913ns -> -0.646ns (EXP-0094)

sdram_arbiter_hier.v: fixes EXP-0093's own real, traced P&R timing
failure (flat 21-way req_wdata mux, route-delay-dominated). Reuses
sdram_arbiter_n.v unmodified, twice: 4 leaf instances (NUM_REQ=5, one
per group) + 1 top instance (NUM_REQ=5: 4 groups + host, host
bypassed/unpipelined), one real pipeline register stage between
levels. Isolated verification (tb_sdram_arbiter_hier.v): 23/23 PASS.

Two real bugs found and fixed via signal tracing: a testbench helper
not waiting for grant before firing req, and a genuine RTL lost-pulse
bug at the leaf-to-top boundary (a transient one-shot request could be
dropped if the top level was busy with a different group) -- fixed
with a sticky per-group pending_req_r latch.

Wired into n16_system_ddr3_top.v (drop-in). Real, full P&R re-run:
WNS improved -0.913ns -> -0.646ns, TNS -690ns -> -97.5ns, failing
endpoints 3021 -> 771 -- substantial, measured improvement, confirming
the arbiter was correctly root-caused (bottleneck moved elsewhere:
neural_processor_packed.v's own already-thin-margin MAC datapath,
eroded by N=16's higher overall congestion). Functional xsim still
32/32 PASS. Timing not yet fully closed -- real next steps documented,
not yet attempted without further direction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
This commit is contained in:
2026-09-21 02:25:44 +02:00
co-authored by Claude Sonnet 5
parent 50c940a41d
commit 9e1f16db47
7 changed files with 840 additions and 19 deletions
+20 -5
View File
@@ -327,9 +327,17 @@ module tb;
.dir_state(dir_state), .dir_error(dir_error), .queue_empty(queue_empty)
);
// ---- real 20-way arbiter (4 groups' own weight-fetch + 16 PEs' own
// activation-fetch+writeback) + 4x systolic_group.v ----
localparam NUM_REQ = N_GROUPS + N_PES; // 4 + 16 = 20
// ---- EXP-0094: real, hierarchical 2-level arbiter (4 groups' own
// weight-fetch + 16 PEs' own activation-fetch+writeback + 1 host
// slot, matching n16_system_ddr3_top.v's own real, fixed topology
// exactly -- sdram_arbiter_hier.v always includes a host slot, so
// this testbench ties it off inactive (it doesn't instantiate
// host_mem_bridge.v at all, same real precedent as
// tb_n2_system_ddr3.v never instantiating spi_host_bridge_v3.v) --
// was a real 20-way flat sdram_arbiter_n.v; replaced after EXP-0093
// found the flat 21-way version's own real P&R timing failure. ----
localparam NUM_REQ = N_GROUPS + N_PES + 1; // 4 + 16 + 1 = 21
localparam HOST_SLOT = NUM_REQ - 1; // 20, tied off inactive below
wire [NUM_REQ-1:0] req_active, req_grant, req_req, req_wr;
wire [NUM_REQ-1:0] req_ready, req_busy;
@@ -338,8 +346,15 @@ module tb;
wire [NUM_REQ*4*BURST_LEN-1:0] req_wmask;
wire [NUM_REQ*32*BURST_LEN-1:0] req_rdata;
sdram_arbiter_n #(
.NUM_REQ(NUM_REQ), .ADDR_WIDTH(MIG_ADDR_WIDTH), .BURST_LEN(BURST_LEN)
assign req_active[HOST_SLOT] = 1'b0;
assign req_req[HOST_SLOT] = 1'b0;
assign req_wr[HOST_SLOT] = 1'b0;
assign req_addr[HOST_SLOT*MIG_ADDR_WIDTH +: MIG_ADDR_WIDTH] = {MIG_ADDR_WIDTH{1'b0}};
assign req_wdata[HOST_SLOT*32*BURST_LEN +: 32*BURST_LEN] = {(32*BURST_LEN){1'b0}};
assign req_wmask[HOST_SLOT*4*BURST_LEN +: 4*BURST_LEN] = {(4*BURST_LEN){1'b0}};
sdram_arbiter_hier #(
.N_GROUPS(N_GROUPS), .PES_PER_GROUP(4), .ADDR_WIDTH(MIG_ADDR_WIDTH), .BURST_LEN(BURST_LEN)
) u_arb (
.clk(ui_clk), .rst(ui_clk_sync_rst),
.req_active(req_active), .req_grant(req_grant),