feat: SDRAM 8MB->64MB upgrade (AS4C32M16SB-7BIN) + N_SLOTS=8 support
Memory upgrade, at the user's own explicit request: Alliance Memory AS4C4M16SA-6TIN (64Mbit/8MB) -> AS4C32M16SB-7BIN (512Mbit/64MB, 54-ball TFBGA), the largest same-family SDR SDRAM Alliance Memory offers. Real-datasheet-driven (whole AS4C4M16SA/AS4C8M16SA/AS4C16M16SA/ AS4C32M16SA family investigated): 13 row bits (was 12, one new FPGA pin sdram_a[12]/ball F1), 10 column bits (was 8), real -7-grade AC timing (tRCD/tRP improved to 15ns, tREFI halved to 7.8us for the doubled row count). sdram_controller.v and sdram_model.v gained real ROW_BITS/COL_BITS/BANK_BITS parameters (was hardcoded 12/8/2). ADDR_WIDTH widened 23->26 bits across the live instantiation tree. This required a real SPI protocol change (spi_host_bridge.v): a 26-bit byte address no longer fits in 3 bytes -- every address field widened 3->4 bytes (WRITE_JOB 15->18 payload bytes, WRITE_MEM/READ_MEM header 5->6 bytes). Found and fixed two real timing regressions via nextpnr-ecp5 P&R (not assumed): neural_director.v's own runtime-indexed demux write (ERR-0027, was silently synthesizing an extra MULT18X18D) and nms_activation_fill_ctrl_v3.v's own linear N_SLOTS-wide max-scan (ERR-0028, became dominant at N_SLOTS=8) -- both replaced with constant-indexed/tree-based equivalents, bit-exact same behavior, confirmed via full D-Stress N=2/4/8 regression (identical cycle counts). N_SLOTS=4 now fully closes timing at 64MHz (8/8 seeds); N_SLOTS=8 significantly improved but not yet fully reliable (5/8 seeds) -- honestly disclosed, not claimed complete. Full regression re-verified: sdram_controller (461/461, 18 configs), tb_sdram_boundary (21/21), D-Stress N=2/4/8 (bit-exact), spi_host_bridge (18/18), board-level SPI smoke test (11/11), unified backend (40/40). See hardware/v2/docs/MEMORY_UPGRADE_64MB_N8.md for the full investigation, and errors.log/decisions.log (ERR-0027, ERR-0028, DEC-0039) for the complete root-cause writeups. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
@@ -18,9 +18,15 @@
|
||||
// ============================================================
|
||||
module tb #(
|
||||
parameter BURST_LEN = 4,
|
||||
parameter CLK_FREQ_MHZ = 166
|
||||
parameter CLK_FREQ_MHZ = 64
|
||||
);
|
||||
localparam ADDR_WIDTH = 22;
|
||||
// AS4C32M16SA-7TIN (64MB): 13 row bits (A0-A12), 10 col bits
|
||||
// (A0-A9), 2 bank bits (BA0-BA1) -- see sdram_controller.v's own
|
||||
// header for the full datasheet cross-reference.
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
|
||||
reg clk = 0;
|
||||
@@ -35,12 +41,15 @@ module tb #(
|
||||
wire ready, busy;
|
||||
|
||||
wire sdram_cke, sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n;
|
||||
wire [1:0] sdram_ba;
|
||||
wire [11:0] sdram_a;
|
||||
wire [BANK_BITS-1:0] sdram_ba;
|
||||
wire [ROW_BITS-1:0] sdram_a;
|
||||
wire [15:0] sdram_dq;
|
||||
wire [1:0] sdram_dqm;
|
||||
|
||||
sdram_controller #(.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)) dut (
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) dut (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(req), .wr(wr), .addr(addr), .wdata(wdata), .wmask(wmask), .rdata(rdata), .ready(ready), .busy(busy),
|
||||
.sdram_cke(sdram_cke), .sdram_cs_n(sdram_cs_n), .sdram_ras_n(sdram_ras_n),
|
||||
@@ -48,7 +57,10 @@ module tb #(
|
||||
.sdram_ba(sdram_ba), .sdram_a(sdram_a), .sdram_dq(sdram_dq), .sdram_dqm(sdram_dqm)
|
||||
);
|
||||
|
||||
sdram_model #(.CLK_FREQ_MHZ(CLK_FREQ_MHZ)) mem (
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) mem (
|
||||
.clk(clk), .cke(sdram_cke), .cs_n(sdram_cs_n), .ras_n(sdram_ras_n),
|
||||
.cas_n(sdram_cas_n), .we_n(sdram_we_n), .ba(sdram_ba), .a(sdram_a),
|
||||
.dq(sdram_dq), .dqm(sdram_dqm)
|
||||
@@ -123,7 +135,7 @@ module tb #(
|
||||
while (busy) @(posedge clk); // real power-up/init sequence
|
||||
|
||||
// ---- A: write -> read single ----
|
||||
check_word(22'd0, 16'hA5A5);
|
||||
check_word({ADDR_WIDTH{1'b0}}, 16'hA5A5);
|
||||
|
||||
// ---- B: sequential addresses ----
|
||||
trace_on = 1'b1;
|
||||
@@ -134,25 +146,25 @@ module tb #(
|
||||
check_word(i*BURST_LEN, 16'h1000 + i);
|
||||
|
||||
// ---- E: row change (same bank 0, different row) ----
|
||||
check_word({2'b00, 12'd0, 8'd0}, 16'h2000);
|
||||
check_word({2'b00, 12'd1, 8'd0}, 16'h2001);
|
||||
check_word({2'b00, 12'd100, 8'd0}, 16'h2002);
|
||||
check_word({2'b00, 13'd0, 10'd0}, 16'h2000);
|
||||
check_word({2'b00, 13'd1, 10'd0}, 16'h2001);
|
||||
check_word({2'b00, 13'd100, 10'd0}, 16'h2002);
|
||||
|
||||
// ---- F: bank change ----
|
||||
check_word({2'b00, 12'd5, 8'd0}, 16'h3000);
|
||||
check_word({2'b01, 12'd5, 8'd0}, 16'h3001);
|
||||
check_word({2'b10, 12'd5, 8'd0}, 16'h3002);
|
||||
check_word({2'b11, 12'd5, 8'd0}, 16'h3003);
|
||||
check_word({2'b00, 13'd5, 10'd0}, 16'h3000);
|
||||
check_word({2'b01, 13'd5, 10'd0}, 16'h3001);
|
||||
check_word({2'b10, 13'd5, 10'd0}, 16'h3002);
|
||||
check_word({2'b11, 13'd5, 10'd0}, 16'h3003);
|
||||
|
||||
// ---- I: address limits ----
|
||||
check_word({2'b00, 12'd0, 8'd0}, 16'h4000); // row 0, col 0
|
||||
check_word({2'b11, 12'd4095, 8'(256-BURST_LEN)}, 16'h4001); // max bank/row, last valid burst-aligned col
|
||||
check_word({2'b00, 12'd4095, 8'd0}, 16'h4002);
|
||||
check_word({2'b11, 12'd0, 8'd0}, 16'h4003);
|
||||
// ---- I: address limits (AS4C32M16SA: 8192 rows, 1024 cols) ----
|
||||
check_word({2'b00, 13'd0, 10'd0}, 16'h4000); // row 0, col 0
|
||||
check_word({2'b11, 13'd8191, 10'(1024-BURST_LEN)}, 16'h4001); // max bank/row, last valid burst-aligned col
|
||||
check_word({2'b00, 13'd8191, 10'd0}, 16'h4002);
|
||||
check_word({2'b11, 13'd0, 10'd0}, 16'h4003);
|
||||
|
||||
// ---- H: pseudo-random pattern ----
|
||||
for (i = 0; i < 32; i = i + 1) begin
|
||||
rnd_addr = ($random(seed) % (4*4096*256/BURST_LEN)) * BURST_LEN;
|
||||
rnd_addr = ($random(seed) % (4*8192*1024/BURST_LEN)) * BURST_LEN;
|
||||
check_word(rnd_addr, 16'h5000 + i);
|
||||
end
|
||||
|
||||
@@ -176,7 +188,7 @@ module tb #(
|
||||
reg [2*BURST_LEN-1:0] m;
|
||||
integer w, elapsed_j;
|
||||
reg [ADDR_WIDTH-1:0] addr_j;
|
||||
addr_j = 22'd50000;
|
||||
addr_j = 25'd50000;
|
||||
// seed a known full pattern first (no masking)
|
||||
for (w = 0; w < BURST_LEN; w = w + 1) full_pat[w*16 +: 16] = 16'h7000 + w[15:0];
|
||||
wmask = {(2*BURST_LEN){1'b0}};
|
||||
|
||||
Reference in New Issue
Block a user