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:
2026-09-20 09:55:16 +02:00
co-authored by Claude Sonnet 5
parent 27cf5f36da
commit 8ad04987de
5 changed files with 153 additions and 50 deletions
+12 -12
View File
@@ -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