feat: MILESTONE - real activation-fetch engine, full N=2 system verified on real DDR3 (EXP-0079)
Closes the last major disclosed functional gap: packed_slot.v's activation data was read through a combinational stand-in since EXP-0062. New act_tile_fetch.v reads activation tiles directly from DDR3 (no on-chip buffering needed, unlike weights -- activation data has no reuse), sharing each slot's existing ctrl port with its own weight-prefetch engine. Real memory layout: one full BURST_LEN=8-word burst per tile, deliberately avoiding any runtime-indexed part-select given this project's thin P&R timing margin (EXP-0078). Verified at three levels: act_tile_fetch.v alone (6/6), packed_slot.v with real preloaded activation data (9/9), and the full N=2 system against real DDR3 via xsim (8/8, 0 errors) -- the first time this project's compute path has been verified end-to-end with real DDR3 for both weights and activations. Retired hardware/v3/rtl/n2_system_top.v and its testbench (pre-DDR3 SDR-placeholder era, fully superseded by n2_system_ddr3_top.v). Also: docs/PHYSICAL_REALIZATION.md (real pinout/parts/timing/protocol reference for the physical board) and CLAUDE.md (persistent project instructions for future Claude Code sessions). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
This commit is contained in:
@@ -9,11 +9,12 @@
|
||||
// promotion from testbench-sequence to real RTL (EXP-0062 -> this)
|
||||
// preserves bit-exact correctness.
|
||||
//
|
||||
// Activation stand-in (see packed_slot.v's own header): a simple
|
||||
// combinational behavioral memory here, addressed by act_tile_addr_a/b
|
||||
// (tile-index-based, matching packed_slot.v's own addressing:
|
||||
// x_base + tile_count), standing in for the real (not yet built)
|
||||
// activation fetch engine.
|
||||
// EXP-0079 UPDATE: packed_slot.v now wraps a REAL act_tile_fetch.v
|
||||
// (real DDR3 reads, no stand-in port left) -- this test now preloads
|
||||
// activation data into the SAME real SDR SDRAM placeholder backend
|
||||
// already used for weights (preload_sdram_activations, matching
|
||||
// act_tile_fetch.v's own real memory layout: one full BURST_LEN=8-word
|
||||
// burst per tile), instead of a combinational behavioral lookup.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
@@ -119,15 +120,36 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- activation stand-in: act_tile_addr = x_base + tile_index
|
||||
// (packed_slot.v's own addressing) -- x_base itself is chosen as
|
||||
// li*1000 + pos*100 below so a simple decode recovers (li,pos,t) ----
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] act_data_a, act_data_b;
|
||||
wire [ADDR_WIDTH-1:0] act_addr_a, act_addr_b;
|
||||
// ---- 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). ----
|
||||
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);
|
||||
endfunction
|
||||
|
||||
// act_tile_addr = x_base + tile_index (packed_slot.v's own
|
||||
// addressing); x_base itself encodes (li,pos) as li*100000+pos*1000
|
||||
// so tile_index occupies the low 3 decimal digits directly.
|
||||
task automatic preload_sdram_activations;
|
||||
integer li, pos, t, 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
|
||||
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);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- packed_slot.v (DUT) ----
|
||||
reg job_start;
|
||||
@@ -151,33 +173,12 @@ module tb;
|
||||
.result_data_a(result_data_a), .result_data_b(result_data_b),
|
||||
.result_node_id_a(result_node_id_a), .result_node_id_b(result_node_id_b),
|
||||
.result_addr_a_out(result_addr_a_out), .result_addr_b_out(result_addr_b_out),
|
||||
.act_tile_addr_a(act_addr_a), .act_tile_addr_b(act_addr_b),
|
||||
.act_tile_data_a(act_data_a), .act_tile_data_b(act_data_b),
|
||||
.mem_grant(1'b1), // no arbiter in this single-slot test
|
||||
.ctrl_req(slot_ctrl_req), .ctrl_wr(slot_ctrl_wr), .ctrl_addr(slot_ctrl_addr),
|
||||
.ctrl_wdata(slot_ctrl_wdata), .ctrl_wmask(slot_ctrl_wmask),
|
||||
.ctrl_rdata(ctrl_rdata), .ctrl_ready(ctrl_ready), .ctrl_busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// real activation decode: x_base encodes (li,pos) as li*100000+pos*1000;
|
||||
// act_tile_addr = x_base + tile_index (0..N_TILES-1), so
|
||||
// tile_index = act_addr % 1000, pos = (act_addr/1000) % 100, li = act_addr/100000
|
||||
function automatic signed [DATA_WIDTH*P_IN-1:0] act_lookup(input [ADDR_WIDTH-1:0] addr);
|
||||
integer li_d, pos_d, tidx_d, k;
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] r;
|
||||
begin
|
||||
li_d = addr / 100000;
|
||||
pos_d = (addr / 1000) % 100;
|
||||
tidx_d = addr % 1000;
|
||||
for (k = 0; k < P_IN; k = k + 1)
|
||||
r[k*DATA_WIDTH +: DATA_WIDTH] = input_byte(li_d, pos_d, tidx_d*P_IN + k);
|
||||
act_lookup = r;
|
||||
end
|
||||
endfunction
|
||||
|
||||
always @(*) act_data_a = act_lookup(act_addr_a);
|
||||
always @(*) act_data_b = act_lookup(act_addr_b);
|
||||
|
||||
integer errors, tests;
|
||||
integer li_i, pp_i;
|
||||
integer acc_a, acc_b, s_a, s_b, k, tt;
|
||||
@@ -189,8 +190,8 @@ module tb;
|
||||
tests = tests + 1;
|
||||
@(posedge clk);
|
||||
job_start = 1'b1;
|
||||
x_base_a = li*100000 + pos_a*1000;
|
||||
x_base_b = li*100000 + pos_b*1000;
|
||||
x_base_a = act_x_base(li, pos_a);
|
||||
x_base_b = act_x_base(li, pos_b);
|
||||
w_base = li*WORDS_PER_LAYER; // WORD address, matching layer_prefetch_ctrl.v's
|
||||
// own convention (EXP-0057/58/62) and this
|
||||
// testbench's own preload_sdram_layers addressing
|
||||
@@ -245,6 +246,8 @@ module tb;
|
||||
|
||||
$display("=== preload SDRAM with %0d resident-filter weight sets ===", L);
|
||||
preload_sdram_layers;
|
||||
$display("=== preload SDRAM with real activation data (EXP-0079) ===");
|
||||
preload_sdram_activations;
|
||||
@(posedge clk);
|
||||
pre_active = 1'b0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user