feat: denser activation packing, real bandwidth ceiling doubled (EXP-0081)
Implements the highest-leverage fix from EXP-0080's bottleneck analysis: act_tile_fetch.v now packs 2 consecutive tiles per DDR3 burst (even tile low 64 bits, odd tile high 64 bits) instead of 1 tile per burst, halving real DDR3 bytes-per-useful-byte. Timing-safe by construction: the tile-index select bit is registered at request time, long before the real DDR3 round-trip completes, never racing the arriving read data (unlike the runtime part-select pattern EXP-0079 deliberately avoided). Re-verified at all 3 levels (isolated engine 8/8, packed_slot.v 9/9 with bit-identical results to EXP-0079, full N=2 system on real DDR3 8/8) -- the real JEDEC trace now shows no half-burst padding, direct confirmation the fix works in practice, not just in theory. Also: real device data gathered on this package's I/O bank layout (only 5 banks total, 14/15/16/34/35) informing the next bandwidth step (32-bit-wide single controller recommended over a second independent channel, given the pin/logic cost comparison). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
This commit is contained in:
@@ -147,40 +147,54 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
|
||||
$display("=== preload 4 burst-aligned tile slots (2 lanes x 2 tiles) ===");
|
||||
// lane A base = 0, lane B base = 100 (arbitrary, word-address units)
|
||||
for (wi = 0; wi < 2; wi = wi + 1) begin // wi = tile index
|
||||
for (k = 0; k < BURST_LEN; k = k + 1)
|
||||
burst[k*16 +: 16] = (k < P_IN/2) ? {act_byte(0, wi, 2*k+1), act_byte(0, wi, 2*k)} : 16'h0000;
|
||||
$display("=== preload 2 bursts/lane, 2 tiles packed per burst (EXP-0081 layout) ===");
|
||||
// lane A base = 0, lane B base = 100 (arbitrary, word-address units).
|
||||
// burst pair p holds tile 2p (low 64 bits) and tile 2p+1 (high 64 bits).
|
||||
for (wi = 0; wi < 2; wi = wi + 1) begin // wi = burst-pair index (0 -> tiles 0/1, 1 -> tiles 2/3)
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(0, 2*wi, 2*k+1), act_byte(0, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(0, 2*wi+1, 2*k+1), act_byte(0, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(0 + wi*BURST_LEN, burst);
|
||||
for (k = 0; k < BURST_LEN; k = k + 1)
|
||||
burst[k*16 +: 16] = (k < P_IN/2) ? {act_byte(100, wi, 2*k+1), act_byte(100, wi, 2*k)} : 16'h0000;
|
||||
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(100, 2*wi, 2*k+1), act_byte(100, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(100, 2*wi+1, 2*k+1), act_byte(100, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(100 + wi*BURST_LEN, burst);
|
||||
end
|
||||
@(posedge clk);
|
||||
pre_active = 1'b0;
|
||||
|
||||
$display("=== TEST 1: fetch tile 0, both lanes ===");
|
||||
$display("=== TEST 1: fetch tile 0 (even -> low half), both lanes ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd0);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_b[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(100, 0, k);
|
||||
check(data_a === exp_a, "T1: lane A tile 0 bit-exact");
|
||||
check(data_b === exp_b, "T1: lane B tile 0 bit-exact");
|
||||
|
||||
$display("=== TEST 2: fetch tile 1, both lanes (different burst address) ===");
|
||||
$display("=== TEST 2: fetch tile 1 (odd -> high half, SAME burst address as tile 0) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd1);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 1, k);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_b[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(100, 1, k);
|
||||
check(data_a === exp_a, "T2: lane A tile 1 bit-exact");
|
||||
check(data_b === exp_b, "T2: lane B tile 1 bit-exact");
|
||||
|
||||
$display("=== TEST 3: back-to-back fetches (tile 0 then tile 1 immediately) ===");
|
||||
$display("=== TEST 3: fetch tile 2 (even -> low half, NEW burst address) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd2);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 2, k);
|
||||
check(data_a === exp_a, "T3: lane A tile 2 bit-exact (new burst)");
|
||||
|
||||
$display("=== TEST 4: back-to-back fetches, alternating even/odd tiles ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd0);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k);
|
||||
check(data_a === exp_a, "T3a: back-to-back fetch 1, lane A correct");
|
||||
check(data_a === exp_a, "T4a: back-to-back fetch 1 (tile 0, even), lane A correct");
|
||||
do_fetch(25'd0, 25'd100, 16'd1);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 1, k);
|
||||
check(data_a === exp_a, "T3b: back-to-back fetch 2, lane A correct");
|
||||
check(data_a === exp_a, "T4b: back-to-back fetch 2 (tile 1, odd), lane A correct");
|
||||
do_fetch(25'd0, 25'd100, 16'd3);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 3, k);
|
||||
check(data_a === exp_a, "T4c: back-to-back fetch 3 (tile 3, odd, new burst), lane A correct");
|
||||
|
||||
$display("=== %0d/%0d tests, %0d errors ===", tests-errors, tests, errors);
|
||||
if (errors == 0) $display("ALL TESTS PASSED (tb_act_tile_fetch)");
|
||||
|
||||
@@ -240,28 +240,30 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- real activation preload (EXP-0079: packed_slot.v now wraps
|
||||
// a real act_tile_fetch.v, no more stand-in) -- same convention as
|
||||
// tb_packed_slot.v/tb_act_tile_fetch.v: one full BURST_LEN=8-word
|
||||
// burst per tile, P_IN=8 bytes in the low 64 bits. ----
|
||||
// ---- real activation preload (EXP-0081 layout: TWO consecutive
|
||||
// tiles share one BURST_LEN=8-word burst -- even tile in the low
|
||||
// 64 bits, odd tile in the high 64 bits, see act_tile_fetch.v's
|
||||
// own header). ----
|
||||
localparam [MIG_ADDR_WIDTH-1:0] ACT_MEM_BASE = 25'h10000;
|
||||
function automatic [ADDR_WIDTH-1:0] act_x_base(input integer li, input integer pos);
|
||||
act_x_base = {{(ADDR_WIDTH-MIG_ADDR_WIDTH){1'b0}}, ACT_MEM_BASE} + (li*M + pos) * (N_TILES*BURST_LEN);
|
||||
act_x_base = {{(ADDR_WIDTH-MIG_ADDR_WIDTH){1'b0}}, ACT_MEM_BASE} + (li*M + pos) * ((N_TILES/2)*BURST_LEN);
|
||||
endfunction
|
||||
|
||||
task automatic preload_ddr3_activations;
|
||||
integer li, pos, t, k;
|
||||
integer li, pos, tp, k;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (pos = 0; pos < M; pos = pos + 1) begin
|
||||
base = act_x_base(li, pos);
|
||||
for (t = 0; t < N_TILES; t = t + 1) begin
|
||||
for (tp = 0; tp < N_TILES/2; tp = tp + 1) begin
|
||||
burst_data = {(16*BURST_LEN){1'b0}};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[k*16 +: 16] = {input_byte(li, pos, t*P_IN + 2*k+1), input_byte(li, pos, t*P_IN + 2*k)};
|
||||
sdram_write_burst(base[MIG_ADDR_WIDTH-1:0] + t*BURST_LEN, burst_data);
|
||||
burst_data[k*16 +: 16] = {input_byte(li, pos, (2*tp)*P_IN + 2*k+1), input_byte(li, pos, (2*tp)*P_IN + 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[(P_IN/2+k)*16 +: 16] = {input_byte(li, pos, (2*tp+1)*P_IN + 2*k+1), input_byte(li, pos, (2*tp+1)*P_IN + 2*k)};
|
||||
sdram_write_burst(base[MIG_ADDR_WIDTH-1:0] + tp*BURST_LEN, burst_data);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -120,31 +120,31 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- real activation preload (EXP-0079: act_tile_fetch.v replaces
|
||||
// the old combinational stand-in) -- one full BURST_LEN=8-word
|
||||
// burst PER TILE (act_tile_fetch.v's own real memory layout
|
||||
// convention, see that module's header), P_IN=8 bytes in the low
|
||||
// 64 bits, upper 64 bits padding. x_base(li,pos) = ACT_MEM_BASE +
|
||||
// (li*M+pos)*(N_TILES*BURST_LEN), well clear of the weight region
|
||||
// (word addresses 0..L*WORDS_PER_LAYER-1). ----
|
||||
// ---- real activation preload (EXP-0081 layout: TWO consecutive
|
||||
// tiles share one BURST_LEN=8-word burst -- even tile in the low
|
||||
// 64 bits, odd tile in the high 64 bits, see act_tile_fetch.v's
|
||||
// own header). x_base(li,pos) = ACT_MEM_BASE + (li*M+pos)*
|
||||
// (N_TILES/2*BURST_LEN), well clear of the weight region. ----
|
||||
localparam [ADDR_WIDTH-1:0] ACT_MEM_BASE = 26'h10000;
|
||||
function automatic [ADDR_WIDTH-1:0] act_x_base(input integer li, input integer pos);
|
||||
act_x_base = ACT_MEM_BASE + (li*M + pos) * (N_TILES*BURST_LEN);
|
||||
act_x_base = ACT_MEM_BASE + (li*M + pos) * ((N_TILES/2)*BURST_LEN);
|
||||
endfunction
|
||||
|
||||
task automatic preload_sdram_activations;
|
||||
integer li, pos, t, k;
|
||||
integer li, pos, tp, k;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (pos = 0; pos < M; pos = pos + 1) begin
|
||||
base = act_x_base(li, pos);
|
||||
for (t = 0; t < N_TILES; t = t + 1) begin
|
||||
for (tp = 0; tp < N_TILES/2; tp = tp + 1) begin // tp = burst-pair index
|
||||
burst_data = {(16*BURST_LEN){1'b0}};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[k*16 +: 16] = {input_byte(li, pos, t*P_IN + 2*k+1), input_byte(li, pos, t*P_IN + 2*k)};
|
||||
sdram_write_burst(base[SDRAM_ADDR_WIDTH-1:0] + t*BURST_LEN, burst_data);
|
||||
burst_data[k*16 +: 16] = {input_byte(li, pos, (2*tp)*P_IN + 2*k+1), input_byte(li, pos, (2*tp)*P_IN + 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[(P_IN/2+k)*16 +: 16] = {input_byte(li, pos, (2*tp+1)*P_IN + 2*k+1), input_byte(li, pos, (2*tp+1)*P_IN + 2*k)};
|
||||
sdram_write_burst(base[SDRAM_ADDR_WIDTH-1:0] + tp*BURST_LEN, burst_data);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user