feat: real 32-bit DDR3 channel widening - functionally complete, timing NOT yet closed (EXP-0084)
Real 32-bit DDR3 widening (2x MT41J128M16JT-125:K chips ganged in parallel, user's own MIG wizard session). Full RTL adaptation across the shared ctrl bus (16-bit word -> 32-bit word, BURST_LEN=8 unchanged, burst payload 128->256 bits): - mig_native_adapter.v: app_wdf_data/app_rd_data 64->128 bits (real, confirmed against the regenerated MIG wrapper), beat count unchanged. - act_tile_fetch.v: real logic change - burst now holds 4 tiles instead of 2 (sel_lat extended to 2 registered bits, 4-way case mux instead of 2-way ternary, same request-time-registered-select discipline as EXP-0081). Not a further bytes/MAC reduction, just what's needed to keep 100% packing utilization at the larger burst. - host_mem_bridge.v: real addressing redesign - host-facing 16-bit-word contract kept unchanged (ESP32 firmware unaffected), internally translated onto the new 32-bit-native ctrl bus. - sdram_arbiter_n.v, layer_prefetch_ctrl.v, packed_slot.v, ddr_prefetch_mgr.v, n2_system_ddr3_top.v: mechanical width bump plus doubled ddr3_dq/dqs/dm pins and the real differential sys_clk/clk_ref top-level ports the regenerated MIG now requires. New burst_mem_model32.v: explicitly synthetic 32-bit test-only burst memory (the real 16-bit SDR model is genuinely fixed-width, shared by 20+ other tests, correctly not touched). Found and fixed a real address-aliasing bug in it during bring-up (MEM_ADDR_BITS=16 silently wrapped a real 0x10000 test address to 0). Real verification: all isolated testbenches re-verified (10/10, 33/33, 32/32, 7/7, 9/9 PASS), plus real xsim against the real 2-chip DDR3 model (tb_mig_native_adapter.v 12/12 PASS, tb_n2_system_ddr3.v 8/8 PASS, both chips visibly returning different real data). Real P&R: 5 real bugs found and fixed across iterations (stale single-ended MIG clock ports, a real VCCO conflict between the flash SPI bus and the differential reference clock in bank 14 - fixed by moving flash to bank 16, a stale imported XDC - same bug class as EXP-0078 but for constraints this time, missing IOSTANDARDs, and two previously-silently-broken XDC property bugs). Route completes 100%, but real timing does NOT close: WNS -0.618ns, 213 failing endpoints. Honest root cause: the violation is inside neural_processor_packed.v's own packed-MAC accumulation tree, unchanged since EXP-0059 - it has real margin at the old 155.039MHz ui_clk but not at the new 172.414MHz the paired clock-period change produced. This is NOT caused by the 32-bit width change itself. Width alone, even at the old clock, already delivers the full intended 2x bandwidth gain (1.24 -> ~2.48 GB/s) - width and clock rate are separable levers. Current trustworthy timing signoff remains EXP-0083 (16-bit, +0.073ns) until the clock period is reverted toward 3225ps (keeping Data Width=32) in one more real, user-gated MIG wizard session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
This commit is contained in:
@@ -15,30 +15,38 @@
|
||||
// so buffering it on-chip would only add complexity for zero benefit.
|
||||
// This engine reads DIRECTLY from DDR3 per tile instead.
|
||||
//
|
||||
// MEMORY LAYOUT CONVENTION v2 (EXP-0081, real, disclosed, and REQUIRED
|
||||
// MEMORY LAYOUT CONVENTION v3 (EXP-0084, real, disclosed, and REQUIRED
|
||||
// of whoever prepares activation data in DDR3 -- documented in the
|
||||
// physical realization doc too): TWO consecutive tiles (P_IN=8 INT8
|
||||
// values each) share ONE full BURST_LEN=8-word (128-bit) burst -- even
|
||||
// tile index in the LOW 64 bits, odd tile index in the HIGH 64 bits.
|
||||
// Tile t's burst address is `base + (t>>1)*BURST_LEN`. This HALVES
|
||||
// real DDR3 bytes-moved-per-useful-byte versus the original EXP-0079
|
||||
// "1 tile = 1 burst" layout (real measured 1.24GB/s ceiling could only
|
||||
// sustain ~25% of one core's peak DSP throughput under that layout --
|
||||
// see docs/ARCHITECTURE_ANALYSIS.md S3.2 -- this doubles the real
|
||||
// achievable fraction).
|
||||
// physical realization doc too): FOUR consecutive tiles (P_IN=8 INT8
|
||||
// values each, 64 bits each) share ONE full BURST_LEN=8-word burst --
|
||||
// since EXP-0084's real 32-bit DDR3 channel widening, one burst is now
|
||||
// 8*32=256 bits (up from 128 bits at the old 16-bit width), and 4
|
||||
// tiles of 64 bits exactly fill it (100% utilization, same packing
|
||||
// EFFICIENCY as EXP-0081's "2 tiles fill a 128-bit burst" -- this is
|
||||
// NOT a further bytes-per-MAC reduction beyond EXP-0081's already-
|
||||
// optimal 1 byte/MAC, it is what's REQUIRED to keep that same 100%
|
||||
// utilization at the new, larger burst size instead of leaving half
|
||||
// of it newly wasted). Tile index within the burst selects a quarter:
|
||||
// tile parity 0/1/2/3 (tcnt[1:0]) -> bits [63:0]/[127:64]/[191:128]/
|
||||
// [255:192] of the burst response. Tile t's burst address is
|
||||
// `base + (t>>2)*BURST_LEN`.
|
||||
//
|
||||
// WHY THIS IS TIMING-SAFE (the thing EXP-0079 deliberately avoided):
|
||||
// the tile index's own LSB (which half of the burst to use) is known
|
||||
// at REQUEST time, not at response time -- it's registered into
|
||||
// the tile index's own low 2 bits (which quarter of the burst to use)
|
||||
// are known at REQUEST time, not at response time -- registered into
|
||||
// `sel_lat` the SAME cycle `tcnt` is latched, many ui_clk cycles
|
||||
// BEFORE the real DDR3 round-trip completes and `ctrl_rdata` becomes
|
||||
// valid. The eventual data-select mux therefore selects using an
|
||||
// already-long-stable registered bit, never a bit racing the read
|
||||
// data itself -- this is NOT the runtime-indexed-part-select-on-the-
|
||||
// critical-path pattern weight_tile_gather.v's own header (EXP-0061)
|
||||
// warned about; that pattern is about a select signal arriving
|
||||
// LATE/simultaneously with the data it gates. Confirmed via a real
|
||||
// P&R re-check after this change (see the log), not just asserted.
|
||||
// valid. The eventual data-select mux (a real `case` on the registered
|
||||
// 2-bit `sel_lat`, not a runtime-indexed part-select expression --
|
||||
// deliberately written as explicit constant-offset case arms, see
|
||||
// below) therefore selects using an already-long-stable registered
|
||||
// value, never bits racing the read data itself -- this is NOT the
|
||||
// runtime-indexed-part-select-on-the-critical-path pattern weight_
|
||||
// tile_gather.v's own header (EXP-0061) warned about; that pattern is
|
||||
// about a select signal arriving LATE/simultaneously with the data it
|
||||
// gates. Same real discipline EXP-0081 already established for the
|
||||
// 1-bit case, now extended to 2 bits -- confirmed via a real P&R
|
||||
// re-check after this change (see the log), not just asserted.
|
||||
//
|
||||
// PROTOCOL: one request (`req` pulse + base_a/base_b/tcnt) triggers
|
||||
// TWO SEQUENTIAL burst reads (lane A then lane B) over the SAME
|
||||
@@ -74,14 +82,14 @@ module act_tile_fetch #(
|
||||
output reg ctrl_req,
|
||||
output reg ctrl_wr,
|
||||
output reg [ADDR_WIDTH-1:0] ctrl_addr,
|
||||
output wire [16*BURST_LEN-1:0] ctrl_wdata,
|
||||
output wire [2*BURST_LEN-1:0] ctrl_wmask,
|
||||
input wire [16*BURST_LEN-1:0] ctrl_rdata,
|
||||
output wire [32*BURST_LEN-1:0] ctrl_wdata,
|
||||
output wire [4*BURST_LEN-1:0] ctrl_wmask,
|
||||
input wire [32*BURST_LEN-1:0] ctrl_rdata,
|
||||
input wire ctrl_ready,
|
||||
input wire ctrl_busy
|
||||
);
|
||||
assign ctrl_wdata = {(16*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = {(2*BURST_LEN){1'b0}}; // read-only engine, mask unused
|
||||
assign ctrl_wdata = {(32*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = {(4*BURST_LEN){1'b0}}; // read-only engine, mask unused
|
||||
|
||||
localparam S_IDLE = 3'd0,
|
||||
S_MEMWAIT = 3'd1,
|
||||
@@ -92,12 +100,12 @@ module act_tile_fetch #(
|
||||
reg [2:0] state;
|
||||
reg [ADDR_WIDTH-1:0] base_a_lat, base_b_lat;
|
||||
reg [15:0] tcnt_lat;
|
||||
reg sel_lat; // registered at request time -- see header
|
||||
reg [1:0] sel_lat; // registered at request time -- see header
|
||||
|
||||
assign mem_active = (state != S_IDLE);
|
||||
|
||||
// burst index = tcnt/2 (integer division -- two tiles share one burst)
|
||||
wire [ADDR_WIDTH-1:0] tile_offset = {{(ADDR_WIDTH-15){1'b0}}, tcnt_lat[15:1]} * BURST_LEN[ADDR_WIDTH-1:0];
|
||||
// burst index = tcnt/4 (integer division -- four tiles share one burst)
|
||||
wire [ADDR_WIDTH-1:0] tile_offset = {{(ADDR_WIDTH-14){1'b0}}, tcnt_lat[15:2]} * BURST_LEN[ADDR_WIDTH-1:0];
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
@@ -105,7 +113,7 @@ module act_tile_fetch #(
|
||||
ctrl_req <= 1'b0; ctrl_wr <= 1'b0; ctrl_addr <= {ADDR_WIDTH{1'b0}};
|
||||
valid <= 1'b0; data_a <= {(DATA_WIDTH*P_IN){1'b0}}; data_b <= {(DATA_WIDTH*P_IN){1'b0}};
|
||||
base_a_lat <= {ADDR_WIDTH{1'b0}}; base_b_lat <= {ADDR_WIDTH{1'b0}}; tcnt_lat <= 16'd0;
|
||||
sel_lat <= 1'b0;
|
||||
sel_lat <= 2'd0;
|
||||
end else begin
|
||||
ctrl_req <= 1'b0;
|
||||
valid <= 1'b0;
|
||||
@@ -116,7 +124,7 @@ module act_tile_fetch #(
|
||||
base_a_lat <= base_a;
|
||||
base_b_lat <= base_b;
|
||||
tcnt_lat <= tcnt;
|
||||
sel_lat <= tcnt[0];
|
||||
sel_lat <= tcnt[1:0];
|
||||
state <= S_MEMWAIT;
|
||||
end
|
||||
end
|
||||
@@ -132,8 +140,16 @@ module act_tile_fetch #(
|
||||
|
||||
S_REQ_A: begin
|
||||
if (ctrl_ready) begin
|
||||
data_a <= sel_lat ? ctrl_rdata[DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]
|
||||
: ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
// explicit constant-offset case arms, not a
|
||||
// runtime-indexed part-select expression -- see
|
||||
// header (EXP-0084, extends EXP-0081's same
|
||||
// discipline from 1 to 2 select bits).
|
||||
case (sel_lat)
|
||||
2'd0: data_a <= ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
2'd1: data_a <= ctrl_rdata[64 +: DATA_WIDTH*P_IN];
|
||||
2'd2: data_a <= ctrl_rdata[128 +: DATA_WIDTH*P_IN];
|
||||
2'd3: data_a <= ctrl_rdata[192 +: DATA_WIDTH*P_IN];
|
||||
endcase
|
||||
ctrl_addr <= base_b_lat + tile_offset;
|
||||
ctrl_wr <= 1'b0;
|
||||
state <= S_GAP;
|
||||
@@ -156,8 +172,12 @@ module act_tile_fetch #(
|
||||
|
||||
S_REQ_B: begin
|
||||
if (ctrl_ready) begin
|
||||
data_b <= sel_lat ? ctrl_rdata[DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]
|
||||
: ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
case (sel_lat)
|
||||
2'd0: data_b <= ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
2'd1: data_b <= ctrl_rdata[64 +: DATA_WIDTH*P_IN];
|
||||
2'd2: data_b <= ctrl_rdata[128 +: DATA_WIDTH*P_IN];
|
||||
2'd3: data_b <= ctrl_rdata[192 +: DATA_WIDTH*P_IN];
|
||||
endcase
|
||||
valid <= 1'b1;
|
||||
state <= S_IDLE;
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user