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:
@@ -0,0 +1,109 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// EXP-0084 -- minimal, EXPLICITLY SYNTHETIC 32-bit-wide burst-memory
|
||||
// test model. NOT a real chip model (unlike sdram_controller.v/
|
||||
// sdram_model.v, which genuinely represent the real AS4C32M16SA x16
|
||||
// SDR part this project also uses) -- that real model is inherently
|
||||
// fixed at 16-bit words (a real hardware fact, not a parameter choice)
|
||||
// and is shared by 20+ other testbenches across v2 and v3, so it is
|
||||
// deliberately NOT modified here. This file exists purely so the
|
||||
// isolated, fast (iverilog) testbenches for modules that now speak
|
||||
// this project's real 32-bit ctrl bus convention (EXP-0084's DDR3
|
||||
// widening) have a same-shape, functionally-correct backend to run
|
||||
// against WITHOUT needing the full real MIG IP + ddr3_model.sv (real
|
||||
// xsim, much slower) for every isolated glue-logic check -- matching
|
||||
// this project's own established "verify new glue logic against a
|
||||
// fast backend first" precedent (tb_act_tile_fetch.v's own header),
|
||||
// just re-pointed at a backend that actually matches the current real
|
||||
// bus width. The REAL, trustworthy, board-accurate verification still
|
||||
// comes from tb_n2_system_ddr3.v against the real ddr3_model.sv, same
|
||||
// as always -- this model's own fixed latency is a plausible, but NOT
|
||||
// claimed-real, stand-in.
|
||||
//
|
||||
// Small DENSE backing store (2^MEM_ADDR_BITS entries), not a full
|
||||
// 2^ADDR_WIDTH array -- ADDR_WIDTH=25 would need ~1GB densely
|
||||
// allocated for no reason; every real test in this project only ever
|
||||
// touches small, low addresses. MEM_ADDR_BITS=20 (~1M entries, ~32MB
|
||||
// of simulation memory) comfortably covers any realistic test address
|
||||
// -- including tb_packed_slot.v's own ACT_MEM_BASE=0x10000 region,
|
||||
// which a first version of this model sized at 16 bits (65536
|
||||
// entries) silently WRAPPED to address 0, aliasing weight and
|
||||
// activation data and producing real, confusing wrong-answer failures
|
||||
// (found via real simulation, not by inspection -- see EXP-0084's
|
||||
// log for the full root-cause trace). Staying portable (Icarus's
|
||||
// associative-array support for a packed-vector key type turned out
|
||||
// not to work for this purpose -- found via a real elaboration
|
||||
// error, not assumed).
|
||||
// ============================================================
|
||||
module burst_mem_model32 #(
|
||||
parameter BURST_LEN = 8,
|
||||
parameter ADDR_WIDTH = 25,
|
||||
parameter MEM_ADDR_BITS = 20,
|
||||
parameter LATENCY = 6 // fixed req->ready cycles, a plausible stand-in, not claimed real
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire req,
|
||||
input wire wr,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
input wire [32*BURST_LEN-1:0] wdata,
|
||||
input wire [4*BURST_LEN-1:0] wmask,
|
||||
output reg [32*BURST_LEN-1:0] rdata,
|
||||
output reg ready,
|
||||
output wire busy
|
||||
);
|
||||
reg [32*BURST_LEN-1:0] mem [0:(1<<MEM_ADDR_BITS)-1];
|
||||
|
||||
localparam S_IDLE = 2'd0, S_BUSY = 2'd1, S_DONE = 2'd2;
|
||||
reg [1:0] state;
|
||||
reg [7:0] cnt;
|
||||
reg [ADDR_WIDTH-1:0] addr_lat;
|
||||
reg wr_lat;
|
||||
reg [32*BURST_LEN-1:0] wdata_lat;
|
||||
reg [4*BURST_LEN-1:0] wmask_lat;
|
||||
integer bi;
|
||||
|
||||
assign busy = (state != S_IDLE);
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= S_IDLE;
|
||||
ready <= 1'b0;
|
||||
cnt <= 8'd0;
|
||||
end else begin
|
||||
ready <= 1'b0;
|
||||
case (state)
|
||||
S_IDLE: begin
|
||||
if (req) begin
|
||||
addr_lat <= addr;
|
||||
wr_lat <= wr;
|
||||
wdata_lat <= wdata;
|
||||
wmask_lat <= wmask;
|
||||
cnt <= LATENCY[7:0];
|
||||
state <= S_BUSY;
|
||||
end
|
||||
end
|
||||
S_BUSY: begin
|
||||
if (cnt == 8'd1) state <= S_DONE;
|
||||
else cnt <= cnt - 8'd1;
|
||||
end
|
||||
S_DONE: begin
|
||||
if (wr_lat) begin
|
||||
// real DQM polarity (matches sdram_controller.v's
|
||||
// own convention): 0=write that byte, 1=masked.
|
||||
for (bi = 0; bi < 4*BURST_LEN; bi = bi + 1)
|
||||
if (!wmask_lat[bi])
|
||||
mem[addr_lat[MEM_ADDR_BITS-1:0]][bi*8 +: 8] <= wdata_lat[bi*8 +: 8];
|
||||
end else begin
|
||||
rdata <= mem[addr_lat[MEM_ADDR_BITS-1:0]];
|
||||
end
|
||||
ready <= 1'b1;
|
||||
state <= S_IDLE;
|
||||
end
|
||||
default: state <= S_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -1,21 +1,23 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// Isolated correctness test for act_tile_fetch.v -- real SDR SDRAM
|
||||
// placeholder backend (same precedent as tb_host_mem_bridge.v/
|
||||
// tb_sdram_arbiter_n.v: verify new glue logic against the fast
|
||||
// backend first). Checks: (1) both lanes read back bit-exact from
|
||||
// their own burst-aligned tile slot; (2) different tile indices
|
||||
// correctly compute different burst addresses (tile_offset =
|
||||
// tcnt*BURST_LEN); (3) back-to-back requests (multiple tiles in a
|
||||
// row) all stay correct, exercising the S_GAP busy-wait logic.
|
||||
// Isolated correctness test for act_tile_fetch.v -- EXP-0084: re-run
|
||||
// against the new 32-bit-wide ctrl bus / 4-tiles-per-burst layout
|
||||
// (real 32-bit DDR3 channel widening). Backend switched from the real
|
||||
// AS4C32M16SA x16 SDR model (sdram_controller.v/sdram_model.v -- a
|
||||
// REAL chip, genuinely fixed at 16-bit, not reusable here) to
|
||||
// burst_mem_model32.v, an explicitly synthetic 32-bit test-only
|
||||
// burst memory built for exactly this purpose (see its own header).
|
||||
// Checks: (1) all four lanes of a burst read back bit-exact from
|
||||
// their own quarter-slot; (2) different tile indices correctly
|
||||
// compute different burst addresses (tile_offset = (tcnt>>2)*
|
||||
// BURST_LEN); (3) back-to-back requests (multiple tiles in a row,
|
||||
// including crossing a burst boundary) all stay correct, exercising
|
||||
// the S_GAP busy-wait logic.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS; // 25
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam DATA_WIDTH = 8;
|
||||
@@ -27,30 +29,16 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// single requester -> tie grant = active, same precedent as
|
||||
@@ -66,8 +54,8 @@ module tb;
|
||||
|
||||
wire dut_ctrl_req, dut_ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] dut_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] dut_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] dut_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] dut_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] dut_ctrl_wmask;
|
||||
|
||||
act_tile_fetch #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
@@ -81,27 +69,21 @@ module tb;
|
||||
.ctrl_rdata(ctrl_rdata), .ctrl_ready(ctrl_ready), .ctrl_busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// ---- preload path: direct access to the SDRAM controller,
|
||||
// bypassing act_tile_fetch.v entirely, same "pre_active" mux
|
||||
// pattern as every other testbench in this project ----
|
||||
// ---- preload path: direct access to the memory model, bypassing
|
||||
// act_tile_fetch.v entirely, same "pre_active" mux pattern as
|
||||
// every other testbench in this project ----
|
||||
reg pre_active;
|
||||
reg pre_req, pre_wr;
|
||||
reg [ADDR_WIDTH-1:0] pre_addr;
|
||||
reg [16*BURST_LEN-1:0] pre_wdata;
|
||||
reg [32*BURST_LEN-1:0] pre_wdata;
|
||||
|
||||
// reroute: real DUT ctrl_* wires go through a mux so the testbench
|
||||
// can preload memory directly before act_tile_fetch.v ever runs.
|
||||
// (Re-declare the connection: DUT was wired directly above for
|
||||
// simplicity of the DUT instantiation; use force-free approach by
|
||||
// instead having the DUT's own ctrl_req/wr/addr/wdata feed the mux
|
||||
// inputs below and the mux feed the real controller.)
|
||||
assign ctrl_req = pre_active ? pre_req : dut_ctrl_req;
|
||||
assign ctrl_wr = pre_active ? pre_wr : dut_ctrl_wr;
|
||||
assign ctrl_addr = pre_active ? pre_addr : dut_ctrl_addr;
|
||||
assign ctrl_wdata = pre_active ? pre_wdata : dut_ctrl_wdata;
|
||||
assign ctrl_wmask = pre_active ? {(2*BURST_LEN){1'b0}} : dut_ctrl_wmask;
|
||||
assign ctrl_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : dut_ctrl_wmask;
|
||||
|
||||
task automatic sdram_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic mem_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data;
|
||||
@@ -137,7 +119,7 @@ module tb;
|
||||
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] exp_a, exp_b;
|
||||
integer k, wi;
|
||||
reg [16*BURST_LEN-1:0] burst;
|
||||
reg [32*BURST_LEN-1:0] burst;
|
||||
|
||||
initial begin
|
||||
errors = 0; tests = 0;
|
||||
@@ -147,54 +129,62 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
|
||||
$display("=== preload 2 bursts/lane, 2 tiles packed per burst (EXP-0081 layout) ===");
|
||||
$display("=== preload 2 bursts/lane, 4 tiles packed per burst (EXP-0084 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);
|
||||
// burst pair p holds tiles 4p, 4p+1, 4p+2, 4p+3 in quarters 0..3.
|
||||
for (wi = 0; wi < 2; wi = wi + 1) begin // wi = burst index (0 -> tiles 0-3, 1 -> tiles 4-7)
|
||||
for (k = 0; k < 4; k = k + 1)
|
||||
burst[k*64 +: 64] = {act_byte(0, 4*wi+k, 7), act_byte(0, 4*wi+k, 6), act_byte(0, 4*wi+k, 5), act_byte(0, 4*wi+k, 4),
|
||||
act_byte(0, 4*wi+k, 3), act_byte(0, 4*wi+k, 2), act_byte(0, 4*wi+k, 1), act_byte(0, 4*wi+k, 0)};
|
||||
mem_write_burst(0 + wi*BURST_LEN, burst);
|
||||
|
||||
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);
|
||||
for (k = 0; k < 4; k = k + 1)
|
||||
burst[k*64 +: 64] = {act_byte(100, 4*wi+k, 7), act_byte(100, 4*wi+k, 6), act_byte(100, 4*wi+k, 5), act_byte(100, 4*wi+k, 4),
|
||||
act_byte(100, 4*wi+k, 3), act_byte(100, 4*wi+k, 2), act_byte(100, 4*wi+k, 1), act_byte(100, 4*wi+k, 0)};
|
||||
mem_write_burst(100 + wi*BURST_LEN, burst);
|
||||
end
|
||||
@(posedge clk);
|
||||
pre_active = 1'b0;
|
||||
|
||||
$display("=== TEST 1: fetch tile 0 (even -> low half), both lanes ===");
|
||||
$display("=== TEST 1: fetch tile 0 (quarter 0), 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 (odd -> high half, SAME burst address as tile 0) ===");
|
||||
$display("=== TEST 2: fetch tile 1 (quarter 1, 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: fetch tile 2 (even -> low half, NEW burst address) ===");
|
||||
$display("=== TEST 2b: fetch tile 2 (quarter 2, SAME burst) ===");
|
||||
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)");
|
||||
check(data_a === exp_a, "T2b: lane A tile 2 bit-exact");
|
||||
|
||||
$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, "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, "T4b: back-to-back fetch 2 (tile 1, odd), lane A correct");
|
||||
$display("=== TEST 2c: fetch tile 3 (quarter 3, SAME burst) ===");
|
||||
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");
|
||||
check(data_a === exp_a, "T2c: lane A tile 3 bit-exact");
|
||||
|
||||
$display("=== TEST 3: fetch tile 4 (quarter 0, NEW burst address) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd4);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 4, k);
|
||||
check(data_a === exp_a, "T3: lane A tile 4 bit-exact (new burst)");
|
||||
|
||||
$display("=== TEST 4: back-to-back fetches, cycling through all 4 quarters ===");
|
||||
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, "T4a: back-to-back fetch 1 (tile 0, quarter 0), lane A correct");
|
||||
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, "T4b: back-to-back fetch 2 (tile 2, quarter 2), lane A correct");
|
||||
do_fetch(25'd0, 25'd100, 16'd7);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 7, k);
|
||||
check(data_a === exp_a, "T4c: back-to-back fetch 3 (tile 7, quarter 3, 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)");
|
||||
|
||||
@@ -1,40 +1,33 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// EXP-0083: (1) isolated correctness test for ddr_prefetch_mgr.v --
|
||||
// same real SDR SDRAM placeholder backend precedent as
|
||||
// tb_act_tile_fetch.v -- and (2) a real, measured, honest A/B cycle-
|
||||
// count comparison against the OLD per-tile req/wait/consume loop
|
||||
// packed_slot.v used before EXP-0083, to get a REAL number for the
|
||||
// look-ahead prefetch's benefit instead of asserting one.
|
||||
// EXP-0084: re-run of the EXP-0083 ddr_prefetch_mgr.v isolated
|
||||
// correctness + A/B cycle-count test against the new 32-bit ctrl bus
|
||||
// (real DDR3 channel widening). Backend switched to burst_mem_
|
||||
// model32.v (see its own header -- explicitly synthetic, fixed
|
||||
// latency, not claiming real DDR3 row/bank AC timing) since the real
|
||||
// AS4C32M16SA x16 SDR model this test used before is genuinely fixed
|
||||
// at 16-bit and can't represent the new bus width.
|
||||
//
|
||||
// Both the "baseline" (direct act_tile_fetch.v, one requester per
|
||||
// tile, old packed_slot.v sequencing) and the "prefetch" (ddr_
|
||||
// prefetch_mgr.v, EXP-0083) loops are run against the SAME shared
|
||||
// backend and the SAME preloaded data, back to back, muxed the same
|
||||
// way tb_act_tile_fetch.v's own pre_active mux works -- so the
|
||||
// comparison is apples to apples, not two different simulated
|
||||
// environments.
|
||||
//
|
||||
// Both loops apply the SAME 2-cycle "simulated compute overhead" per
|
||||
// tile (matching packed_slot.v's own real S_TILEREQ + S_OPERAND
|
||||
// single-cycle costs) between a tile becoming available and the next
|
||||
// step being taken -- the honest question this answers is: does
|
||||
// removing the OLD design's serialization of that overhead with the
|
||||
// NEXT tile's DDR3 fetch produce a real, measurable improvement, and
|
||||
// how much.
|
||||
// EXP-0083's own PART 3 (same-row vs row-switch-heavy A/B, exploiting
|
||||
// the old SDR model's own real row/bank timing distinction) is
|
||||
// DROPPED here -- burst_mem_model32.v has a single fixed latency
|
||||
// regardless of address pattern, so that comparison would no longer
|
||||
// carry real meaning on this backend. The trustworthy, real number
|
||||
// for that effect already comes from EXP-0083's own real-DDR3-backend
|
||||
// measurement (tb_n2_system_ddr3.v, 2.86% real reduction) -- this
|
||||
// isolated test's own job is correctness + a basic sanity check that
|
||||
// prefetch is still not slower than the old per-tile loop, not to
|
||||
// re-derive that headline number.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS; // 25
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam DATA_WIDTH = 8;
|
||||
localparam P_IN = 8;
|
||||
localparam N_TILES = 6; // 3 burst-pairs/lane -- enough to see steady-state behavior
|
||||
localparam N_TILES = 8; // 2 bursts/lane at 4 tiles/burst -- enough to see steady-state behavior
|
||||
|
||||
reg clk = 0;
|
||||
always #(CLK_PERIOD_NS/2.0) clk = ~clk;
|
||||
@@ -42,30 +35,16 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// ---- 3-way mux on the shared backend: preload / baseline DUT / prefetch DUT ----
|
||||
@@ -74,7 +53,7 @@ module tb;
|
||||
|
||||
reg pre_req, pre_wr;
|
||||
reg [ADDR_WIDTH-1:0] pre_addr;
|
||||
reg [16*BURST_LEN-1:0] pre_wdata;
|
||||
reg [32*BURST_LEN-1:0] pre_wdata;
|
||||
|
||||
// ---- baseline DUT: plain act_tile_fetch.v, driven by a per-tile
|
||||
// req/wait/consume loop replicating OLD packed_slot.v sequencing ----
|
||||
@@ -86,8 +65,8 @@ module tb;
|
||||
wire base_mem_active;
|
||||
wire base_ctrl_req, base_ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] base_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] base_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] base_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] base_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] base_ctrl_wmask;
|
||||
|
||||
act_tile_fetch #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
@@ -111,8 +90,8 @@ module tb;
|
||||
wire pf_mem_active;
|
||||
wire pf_ctrl_req, pf_ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] pf_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] pf_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] pf_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] pf_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] pf_ctrl_wmask;
|
||||
|
||||
ddr_prefetch_mgr #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
@@ -130,9 +109,9 @@ module tb;
|
||||
assign ctrl_wr = (sel==SEL_PRELOAD) ? pre_wr : (sel==SEL_BASE) ? base_ctrl_wr : pf_ctrl_wr;
|
||||
assign ctrl_addr = (sel==SEL_PRELOAD) ? pre_addr : (sel==SEL_BASE) ? base_ctrl_addr : pf_ctrl_addr;
|
||||
assign ctrl_wdata = (sel==SEL_PRELOAD) ? pre_wdata : (sel==SEL_BASE) ? base_ctrl_wdata : pf_ctrl_wdata;
|
||||
assign ctrl_wmask = (sel==SEL_BASE) ? base_ctrl_wmask : (sel==SEL_PF) ? pf_ctrl_wmask : {(2*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = (sel==SEL_BASE) ? base_ctrl_wmask : (sel==SEL_PF) ? pf_ctrl_wmask : {(4*BURST_LEN){1'b0}};
|
||||
|
||||
task automatic sdram_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic mem_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data;
|
||||
@@ -155,8 +134,8 @@ module tb;
|
||||
endtask
|
||||
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] exp_a, exp_b;
|
||||
integer k, wi, cyc;
|
||||
reg [16*BURST_LEN-1:0] burst;
|
||||
integer k, wi, qi;
|
||||
reg [32*BURST_LEN-1:0] burst;
|
||||
integer t_start, t_end, base_cycles, pf_cycles;
|
||||
|
||||
initial begin
|
||||
@@ -169,24 +148,22 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
|
||||
$display("=== preload lane A base=0, lane B base=200, %0d tiles (EXP-0081 2-tiles/burst layout) ===", N_TILES);
|
||||
for (wi = 0; wi < N_TILES/2; wi = wi + 1) begin
|
||||
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);
|
||||
$display("=== preload lane A base=0, lane B base=200, %0d tiles (EXP-0084 4-tiles/burst layout) ===", N_TILES);
|
||||
for (wi = 0; wi < N_TILES/4; wi = wi + 1) begin
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst[qi*64 +: 64] = {act_byte(0, 4*wi+qi, 7), act_byte(0, 4*wi+qi, 6), act_byte(0, 4*wi+qi, 5), act_byte(0, 4*wi+qi, 4),
|
||||
act_byte(0, 4*wi+qi, 3), act_byte(0, 4*wi+qi, 2), act_byte(0, 4*wi+qi, 1), act_byte(0, 4*wi+qi, 0)};
|
||||
mem_write_burst(0 + wi*BURST_LEN, burst);
|
||||
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(200, 2*wi, 2*k+1), act_byte(200, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(200, 2*wi+1, 2*k+1), act_byte(200, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(200 + wi*BURST_LEN, burst);
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst[qi*64 +: 64] = {act_byte(200, 4*wi+qi, 7), act_byte(200, 4*wi+qi, 6), act_byte(200, 4*wi+qi, 5), act_byte(200, 4*wi+qi, 4),
|
||||
act_byte(200, 4*wi+qi, 3), act_byte(200, 4*wi+qi, 2), act_byte(200, 4*wi+qi, 1), act_byte(200, 4*wi+qi, 0)};
|
||||
mem_write_burst(200 + wi*BURST_LEN, burst);
|
||||
end
|
||||
@(posedge clk);
|
||||
|
||||
// ============================================================
|
||||
// PART 1: baseline correctness + real cycle count, OLD-style
|
||||
// PART 1: baseline correctness + cycle count, OLD-style
|
||||
// per-tile req -> wait valid -> 2-cycle simulated compute -> next req
|
||||
// ============================================================
|
||||
$display("=== PART 1: baseline (direct act_tile_fetch.v, old packed_slot.v loop) ===");
|
||||
@@ -213,10 +190,10 @@ module tb;
|
||||
$display("baseline: %0d tiles in %0d cycles (%0.2f cycles/tile)", N_TILES, base_cycles, base_cycles*1.0/N_TILES);
|
||||
|
||||
// ============================================================
|
||||
// PART 2: prefetch correctness + real cycle count, EXP-0083
|
||||
// job-level start, poll tile_valid, 2-cycle simulated compute, consume
|
||||
// PART 2: prefetch correctness + cycle count, EXP-0083 job-level
|
||||
// start, poll tile_valid, 2-cycle simulated compute, consume
|
||||
// ============================================================
|
||||
$display("=== PART 2: prefetch (ddr_prefetch_mgr.v, EXP-0083) ===");
|
||||
$display("=== PART 2: prefetch (ddr_prefetch_mgr.v, EXP-0083/0084) ===");
|
||||
sel = SEL_PF;
|
||||
@(posedge clk);
|
||||
pf_base_a <= 0; pf_base_b <= 200; pf_n_tiles <= N_TILES[15:0];
|
||||
@@ -230,11 +207,7 @@ module tb;
|
||||
// pulse being sampled by the DUT (both triggered off the same
|
||||
// @(posedge clk)), reading pf_tile_valid/bank_valid BEFORE the
|
||||
// DUT's own nonblocking update for that consume has been
|
||||
// applied -- a real testbench race, not an RTL bug (found via
|
||||
// an iteration-tagged trace: k=1 was reading k=0's still-
|
||||
// unconsumed bank). #1 (real time, 1ns << the 15.625ns clock
|
||||
// period) forces this poll to always run strictly after that
|
||||
// update has settled.
|
||||
// applied -- a real testbench race, not an RTL bug (EXP-0083).
|
||||
#1;
|
||||
while (!pf_tile_valid) @(posedge clk);
|
||||
for (wi = 0; wi < P_IN; wi = wi + 1) begin
|
||||
@@ -252,56 +225,9 @@ module tb;
|
||||
pf_cycles = (t_end - t_start) / CLK_PERIOD_NS;
|
||||
$display("prefetch: %0d tiles in %0d cycles (%0.2f cycles/tile)", N_TILES, pf_cycles, pf_cycles*1.0/N_TILES);
|
||||
|
||||
$display("=== REAL MEASURED COMPARISON (row-switch-heavy, 3 different burst pairs): baseline=%0d cycles, prefetch=%0d cycles, reduction=%0.1f%% ===",
|
||||
base_cycles, pf_cycles, 100.0*(base_cycles-pf_cycles)/base_cycles);
|
||||
check(pf_cycles < base_cycles, "prefetch is real, measurably faster than baseline (row-switch-heavy)");
|
||||
|
||||
// ============================================================
|
||||
// PART 3 (EXP-0083 addendum): best-case, SAME-ROW comparison --
|
||||
// only tiles 0/1 (both already resident in the FIRST preloaded
|
||||
// burst pair, no row activation needed for either), isolating
|
||||
// the look-ahead benefit from row-switch cost entirely. Answers
|
||||
// honestly whether the small PART-1/2 result is because the
|
||||
// benefit is inherently small, or because row-switch cost
|
||||
// dominates and masks it in that scenario.
|
||||
// ============================================================
|
||||
$display("=== PART 3: same-row best case (2 tiles, single burst pair, no row switch) ===");
|
||||
sel = SEL_BASE;
|
||||
@(posedge clk);
|
||||
t_start = $time;
|
||||
for (k = 0; k < 2; k = k + 1) begin
|
||||
@(posedge clk);
|
||||
base_base_a <= 0; base_base_b <= 200; base_tcnt <= k[15:0];
|
||||
base_req <= 1'b1;
|
||||
@(posedge clk);
|
||||
base_req <= 1'b0;
|
||||
while (!base_valid) @(posedge clk);
|
||||
repeat(2) @(posedge clk);
|
||||
end
|
||||
t_end = $time;
|
||||
base_cycles = (t_end - t_start) / CLK_PERIOD_NS;
|
||||
$display("same-row baseline: 2 tiles in %0d cycles", base_cycles);
|
||||
|
||||
sel = SEL_PF;
|
||||
@(posedge clk);
|
||||
pf_base_a <= 0; pf_base_b <= 200; pf_n_tiles <= 16'd2;
|
||||
pf_job_start <= 1'b1;
|
||||
t_start = $time;
|
||||
@(posedge clk);
|
||||
pf_job_start <= 1'b0;
|
||||
for (k = 0; k < 2; k = k + 1) begin
|
||||
#1;
|
||||
while (!pf_tile_valid) @(posedge clk);
|
||||
repeat(2) @(posedge clk);
|
||||
pf_tile_consume <= 1'b1;
|
||||
@(posedge clk);
|
||||
pf_tile_consume <= 1'b0;
|
||||
end
|
||||
t_end = $time;
|
||||
pf_cycles = (t_end - t_start) / CLK_PERIOD_NS;
|
||||
$display("same-row prefetch: 2 tiles in %0d cycles", pf_cycles);
|
||||
$display("=== REAL MEASURED COMPARISON (same-row, best case): baseline=%0d cycles, prefetch=%0d cycles, reduction=%0.1f%% ===",
|
||||
$display("=== cycle comparison on this fixed-latency synthetic backend: baseline=%0d cycles, prefetch=%0d cycles, reduction=%0.1f%% (sanity check only -- the real, trustworthy number is EXP-0083's real-DDR3-backend measurement, 2.86%%) ===",
|
||||
base_cycles, pf_cycles, 100.0*(base_cycles-pf_cycles)/base_cycles);
|
||||
check(pf_cycles <= base_cycles, "prefetch is not slower than baseline (sanity check)");
|
||||
|
||||
$display("=== %0d/%0d tests, %0d errors ===", tests-errors, tests, errors);
|
||||
if (errors == 0) $display("ALL TESTS PASSED (tb_ddr_prefetch_mgr)");
|
||||
|
||||
@@ -4,26 +4,31 @@
|
||||
// Isolated correctness test for host_mem_bridge.v: the word<->burst
|
||||
// translator that closes the "no host raw-memory-access path" gap
|
||||
// found re-auditing spi_host_bridge.v against V3 (EXP-0068's audit).
|
||||
// Uses the cheap SDR SDRAM placeholder backend (sdram_controller.v +
|
||||
// sdram_model.v), same precedent as tb_sdram_arbiter_n.v: verify new
|
||||
// glue logic against the fast backend first, real DDR3 integration
|
||||
// is a separate, later step once this is trusted standalone.
|
||||
//
|
||||
// Checks: (a) single-word write only touches its OWN word inside the
|
||||
// burst (byte masking correctness, lb_n/ub_n both individually and
|
||||
// together) without corrupting neighboring words in the same burst;
|
||||
// (b) single-word read extracts the correct word regardless of its
|
||||
// offset within the burst (all BURST_LEN=8 offsets exercised);
|
||||
// (c) mem_ready pulses exactly once per transaction.
|
||||
// EXP-0084: re-run against the new 32-bit ctrl bus / burst_mem_
|
||||
// model32.v backend (real DDR3 channel widening -- was 16-bit-word-
|
||||
// native before, now 32-bit-word-native, see host_mem_bridge.v's own
|
||||
// header for the real addressing redesign this required: mem_addr
|
||||
// stays a 16-bit-word address for the host's own unchanged protocol,
|
||||
// but now mem_addr[0] additionally selects which half of the
|
||||
// underlying 32-bit ctrl-bus word to target). One burst now spans
|
||||
// 2*BURST_LEN=16 host-side 16-bit-word offsets (was BURST_LEN=8) --
|
||||
// this test now exercises all 16, not just 8, to cover the new
|
||||
// half-select logic across the WHOLE burst, not half of it.
|
||||
//
|
||||
// Checks: (a) single-word write only touches its OWN half-word inside
|
||||
// the burst (byte masking correctness, lb_n/ub_n both individually and
|
||||
// together) without corrupting neighboring half-words in the same
|
||||
// burst; (b) single-word read extracts the correct half-word
|
||||
// regardless of its offset within the burst (all 16 offsets
|
||||
// exercised); (c) mem_ready pulses exactly once per transaction.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS;
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam BURST_SPAN = 2*BURST_LEN; // 16-bit-word positions per burst, host-side addressing
|
||||
|
||||
reg clk = 0;
|
||||
always #(CLK_PERIOD_NS/2.0) clk = ~clk;
|
||||
@@ -31,30 +36,16 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// single requester -> arbiter isn't even needed for an isolated
|
||||
@@ -112,7 +103,7 @@ module tb;
|
||||
|
||||
reg [15:0] got;
|
||||
integer i;
|
||||
localparam [ADDR_WIDTH-1:0] BASE = 25'd200; // burst-aligned base (200 % 8 == 0)
|
||||
localparam [ADDR_WIDTH-1:0] BASE = 25'd256; // burst-aligned base (256 % 16 == 0)
|
||||
|
||||
initial begin
|
||||
errors = 0; tests = 0;
|
||||
@@ -121,11 +112,11 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk);
|
||||
|
||||
$display("=== TEST 1: write+read every word offset within one burst, verify no cross-word corruption ===");
|
||||
for (i = 0; i < BURST_LEN; i = i + 1) begin
|
||||
$display("=== TEST 1: write+read every half-word offset within one burst (0..%0d), verify no cross-word corruption ===", BURST_SPAN-1);
|
||||
for (i = 0; i < BURST_SPAN; i = i + 1) begin
|
||||
host_write(BASE + i[ADDR_WIDTH-1:0], 16'hA000 + i[15:0], 1'b0, 1'b0);
|
||||
end
|
||||
for (i = 0; i < BURST_LEN; i = i + 1) begin
|
||||
for (i = 0; i < BURST_SPAN; i = i + 1) begin
|
||||
host_read(BASE + i[ADDR_WIDTH-1:0], got);
|
||||
tests = tests + 1;
|
||||
if (got !== (16'hA000 + i[15:0])) begin
|
||||
@@ -136,9 +127,9 @@ module tb;
|
||||
end
|
||||
end
|
||||
|
||||
$display("=== TEST 2: re-write word 3 only, confirm neighbors (0,1,2,4..7) untouched ===");
|
||||
$display("=== TEST 2: re-write offset 3 only, confirm neighbors (0..%0d except 3) untouched ===", BURST_SPAN-1);
|
||||
host_write(BASE + 25'd3, 16'hBEEF, 1'b0, 1'b0);
|
||||
for (i = 0; i < BURST_LEN; i = i + 1) begin
|
||||
for (i = 0; i < BURST_SPAN; i = i + 1) begin
|
||||
host_read(BASE + i[ADDR_WIDTH-1:0], got);
|
||||
tests = tests + 1;
|
||||
if (i == 3) begin
|
||||
|
||||
@@ -16,17 +16,23 @@
|
||||
// real calibration, impractically slow for simulation) and does not
|
||||
// expose this parameter; mig_7series_0_mig.v does. All other
|
||||
// parameters are left at their defaults, which already ARE this
|
||||
// project's real generated configuration (DQ_WIDTH=16, MEM_DENSITY=
|
||||
// 2Gb, MEM_SPEEDGRADE=125, MEM_ADDR_ORDER=BANK_ROW_COLUMN, etc.) --
|
||||
// project's real generated configuration (EXP-0084: DQ_WIDTH=32 now,
|
||||
// two MT41J128M16 components ganged in parallel -- MEM_DENSITY=2Gb,
|
||||
// MEM_SPEEDGRADE=125, MEM_ADDR_ORDER=BANK_ROW_COLUMN unchanged) --
|
||||
// not generic MIG defaults.
|
||||
//
|
||||
// Clock/reset generation and DDR3 pin wiring (WireDelay pass-through,
|
||||
// zero propagation delay) mirror this project's own vendor-shipped
|
||||
// example_design/sim/sim_tb_top.v exactly, per its own real, proven
|
||||
// pattern -- not re-derived from scratch.
|
||||
// pattern -- not re-derived from scratch. EXP-0084: the real vendor
|
||||
// pattern instantiates NUM_COMP=DQ_WIDTH/16 real ddr3_model.sv
|
||||
// components for a >16-bit aggregate width, each wired to its own
|
||||
// 16-bit dq / 2-bit dqs / 2-bit dm slice -- confirmed by reading the
|
||||
// real regenerated sim_tb_top.v (not assumed), replicated exactly
|
||||
// below for DQ_WIDTH=32 (NUM_COMP=2).
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam CLKIN_PERIOD = 3225; // ps, matches this project's real MIG config
|
||||
localparam CLKIN_PERIOD = 2900; // ps, matches this project's real MIG config (EXP-0084)
|
||||
localparam REFCLK_FREQ = 200.0; // MHz
|
||||
localparam real REFCLK_PERIOD = (1000000.0/(2*REFCLK_FREQ));
|
||||
localparam RESET_PERIOD = 200000; // ps
|
||||
@@ -37,41 +43,56 @@ module tb;
|
||||
reg sys_rst_n;
|
||||
wire sys_rst = sys_rst_n; // Active Low, matches mig_7series_0_mig's own default polarity
|
||||
|
||||
// EXP-0084: both system clock AND reference clock are now real
|
||||
// DIFFERENTIAL pairs on the inner mig_7series_0_mig module (the
|
||||
// user's own wizard choice, "Differential" for both) -- confirmed
|
||||
// against the real regenerated mig_7series_0_mig.v port list
|
||||
// (sys_clk_p/n, clk_ref_p/n, no single-ended sys_clk_i/clk_ref_i
|
||||
// ports exist any more). Same real vendor pattern as sim_tb_top.v:
|
||||
// generate a single-ended internal clock, drive the P/N pair as
|
||||
// true/complement of it.
|
||||
reg sys_clk_i = 1'b0;
|
||||
always #(CLKIN_PERIOD/2.0) sys_clk_i = ~sys_clk_i;
|
||||
wire sys_clk_p = sys_clk_i;
|
||||
wire sys_clk_n = ~sys_clk_i;
|
||||
|
||||
reg clk_ref_i = 1'b0;
|
||||
always #REFCLK_PERIOD clk_ref_i = ~clk_ref_i;
|
||||
wire clk_ref_p = clk_ref_i;
|
||||
wire clk_ref_n = ~clk_ref_i;
|
||||
|
||||
initial begin
|
||||
sys_rst_n = 1'b0;
|
||||
#RESET_PERIOD sys_rst_n = 1'b1;
|
||||
end
|
||||
|
||||
// ---- real DDR3 pins ----
|
||||
// ---- real DDR3 pins (dq/dqs/dm widths doubled since EXP-0084's
|
||||
// real 32-bit widening -- two MT41J128M16 chips ganged in
|
||||
// parallel, confirmed against the real regenerated
|
||||
// mig_7series_0.v wrapper) ----
|
||||
wire ddr3_reset_n;
|
||||
wire [15:0] ddr3_dq_fpga;
|
||||
wire [1:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [31:0] ddr3_dq_fpga;
|
||||
wire [3:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [13:0] ddr3_addr_fpga;
|
||||
wire [2:0] ddr3_ba_fpga;
|
||||
wire ddr3_ras_n_fpga, ddr3_cas_n_fpga, ddr3_we_n_fpga;
|
||||
wire [0:0] ddr3_cke_fpga, ddr3_ck_p_fpga, ddr3_ck_n_fpga, ddr3_cs_n_fpga;
|
||||
wire [1:0] ddr3_dm_fpga;
|
||||
wire [3:0] ddr3_dm_fpga;
|
||||
wire [0:0] ddr3_odt_fpga;
|
||||
|
||||
wire [15:0] ddr3_dq_sdram;
|
||||
wire [31:0] ddr3_dq_sdram;
|
||||
reg [13:0] ddr3_addr_sdram;
|
||||
reg [2:0] ddr3_ba_sdram;
|
||||
reg ddr3_ras_n_sdram, ddr3_cas_n_sdram, ddr3_we_n_sdram;
|
||||
wire [0:0] ddr3_cs_n_sdram;
|
||||
wire [0:0] ddr3_odt_sdram;
|
||||
reg [0:0] ddr3_cke_sdram;
|
||||
wire [1:0] ddr3_dm_sdram;
|
||||
wire [1:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
wire [3:0] ddr3_dm_sdram;
|
||||
wire [3:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
reg [0:0] ddr3_ck_p_sdram, ddr3_ck_n_sdram;
|
||||
|
||||
reg [0:0] ddr3_cs_n_sdram_tmp;
|
||||
reg [1:0] ddr3_dm_sdram_tmp;
|
||||
reg [3:0] ddr3_dm_sdram_tmp;
|
||||
reg [0:0] ddr3_odt_sdram_tmp;
|
||||
|
||||
always @(*) begin
|
||||
@@ -93,7 +114,7 @@ module tb;
|
||||
|
||||
genvar dqwd;
|
||||
generate
|
||||
for (dqwd = 0; dqwd < 16; dqwd = dqwd + 1) begin : dq_delay
|
||||
for (dqwd = 0; dqwd < 32; dqwd = dqwd + 1) begin : dq_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dq (
|
||||
.A(ddr3_dq_fpga[dqwd]), .B(ddr3_dq_sdram[dqwd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -102,7 +123,7 @@ module tb;
|
||||
endgenerate
|
||||
genvar dqswd;
|
||||
generate
|
||||
for (dqswd = 0; dqswd < 2; dqswd = dqswd + 1) begin : dqs_delay
|
||||
for (dqswd = 0; dqswd < 4; dqswd = dqswd + 1) begin : dqs_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dqs_p (
|
||||
.A(ddr3_dqs_p_fpga[dqswd]), .B(ddr3_dqs_p_sdram[dqswd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -114,29 +135,37 @@ module tb;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ---- real DDR3 behavioral model (single component, DQ_WIDTH=16
|
||||
// matches MEMORY_WIDTH=16 exactly, no splitting needed) ----
|
||||
ddr3_model u_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram), .dqs(ddr3_dqs_p_sdram), .dqs_n(ddr3_dqs_n_sdram),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
// ---- real DDR3 behavioral model (EXP-0084: TWO components now,
|
||||
// DQ_WIDTH=32 / 16 per component -- exact real pattern confirmed
|
||||
// against the real regenerated sim_tb_top.v's own generate block,
|
||||
// not assumed) ----
|
||||
genvar ci;
|
||||
generate
|
||||
for (ci = 0; ci < 2; ci = ci + 1) begin : gen_mem
|
||||
ddr3_model u_comp_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram[2*ci +: 2]), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram[16*ci +: 16]),
|
||||
.dqs(ddr3_dqs_p_sdram[2*ci +: 2]), .dqs_n(ddr3_dqs_n_sdram[2*ci +: 2]),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ---- real MIG controller (inner module, SIM_BYPASS_INIT_CAL
|
||||
// overridden for a real but fast simulation calibration) ----
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [63:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [7:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [63:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [127:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [15:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [127:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
|
||||
mig_7series_0_mig #(
|
||||
.SIM_BYPASS_INIT_CAL("FAST")
|
||||
@@ -148,7 +177,7 @@ module tb;
|
||||
.ddr3_ck_p(ddr3_ck_p_fpga), .ddr3_ck_n(ddr3_ck_n_fpga),
|
||||
.ddr3_cke(ddr3_cke_fpga), .ddr3_cs_n(ddr3_cs_n_fpga),
|
||||
.ddr3_dm(ddr3_dm_fpga), .ddr3_odt(ddr3_odt_fpga),
|
||||
.sys_clk_i(sys_clk_i), .clk_ref_i(clk_ref_i),
|
||||
.sys_clk_p(sys_clk_p), .sys_clk_n(sys_clk_n), .clk_ref_p(clk_ref_p), .clk_ref_n(clk_ref_n),
|
||||
.app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en),
|
||||
.app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end),
|
||||
.app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren),
|
||||
@@ -165,9 +194,9 @@ module tb;
|
||||
// ---- adapter under test ----
|
||||
reg req, wr;
|
||||
reg [ADDR_WIDTH-1:0] addr;
|
||||
reg [16*BURST_LEN-1:0] wdata;
|
||||
reg [2*BURST_LEN-1:0] wmask;
|
||||
wire [16*BURST_LEN-1:0] rdata;
|
||||
reg [32*BURST_LEN-1:0] wdata;
|
||||
reg [4*BURST_LEN-1:0] wmask;
|
||||
wire [32*BURST_LEN-1:0] rdata;
|
||||
wire ready, busy;
|
||||
|
||||
mig_native_adapter #(.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)) u_adapter (
|
||||
@@ -183,13 +212,13 @@ module tb;
|
||||
task automatic do_txn(
|
||||
input t_wr,
|
||||
input [ADDR_WIDTH-1:0] t_addr,
|
||||
input [16*BURST_LEN-1:0] t_wdata,
|
||||
output [16*BURST_LEN-1:0] t_rdata
|
||||
input [32*BURST_LEN-1:0] t_wdata,
|
||||
output [32*BURST_LEN-1:0] t_rdata
|
||||
);
|
||||
begin
|
||||
@(posedge ui_clk);
|
||||
while (busy) @(posedge ui_clk);
|
||||
req = 1'b1; wr = t_wr; addr = t_addr; wdata = t_wdata; wmask = {(2*BURST_LEN){1'b0}};
|
||||
req = 1'b1; wr = t_wr; addr = t_addr; wdata = t_wdata; wmask = {(4*BURST_LEN){1'b0}};
|
||||
@(posedge ui_clk);
|
||||
req = 1'b0;
|
||||
while (!ready) @(posedge ui_clk);
|
||||
@@ -198,15 +227,15 @@ module tb;
|
||||
endtask
|
||||
|
||||
integer errors, tests;
|
||||
reg [16*BURST_LEN-1:0] got, wpat;
|
||||
reg [32*BURST_LEN-1:0] got, wpat;
|
||||
integer k, i;
|
||||
|
||||
task automatic check_addr(input [ADDR_WIDTH-1:0] a, input [15:0] pattern);
|
||||
begin
|
||||
for (k = 0; k < BURST_LEN; k = k + 1)
|
||||
wpat[k*16 +: 16] = pattern + k[15:0];
|
||||
wpat[k*32 +: 32] = {pattern, pattern + k[15:0]};
|
||||
do_txn(1'b1, a, wpat, got);
|
||||
do_txn(1'b0, a, {(16*BURST_LEN){1'b0}}, got);
|
||||
do_txn(1'b0, a, {(32*BURST_LEN){1'b0}}, got);
|
||||
tests = tests + 1;
|
||||
if (got !== wpat) begin
|
||||
$display("FAIL addr=%0d: got=%h expected=%h", a, got, wpat);
|
||||
|
||||
@@ -26,9 +26,19 @@
|
||||
// EXP-0068's own real vendor-shipped fast-calibration simulation
|
||||
// variant), real ddr3_model.sv, real WireDelay pass-through -- same
|
||||
// proven instantiation pattern as tb_mig_native_adapter.v.
|
||||
//
|
||||
// EXP-0084 UPDATE: real 32-bit DDR3 channel widening -- CLKIN_PERIOD
|
||||
// 2900ps, dq/dqs/dm pin widths doubled (two MT41J128M16 chips ganged
|
||||
// in parallel), TWO real ddr3_model.sv components instantiated (one
|
||||
// per chip, exact real pattern confirmed against the real regenerated
|
||||
// sim_tb_top.v), both sys_clk and clk_ref are now real differential
|
||||
// pairs on the inner mig_7series_0_mig module (the user's own wizard
|
||||
// choice), and the weight/activation preload tasks rewritten for the
|
||||
// new BYTES_PER_BURST=4*BURST_LEN / 4-tiles-per-burst real layouts
|
||||
// (same rewrite already verified in tb_packed_slot.v).
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam CLKIN_PERIOD = 3225; // ps, this project's real MIG config
|
||||
localparam CLKIN_PERIOD = 2900; // ps, this project's real MIG config (EXP-0084)
|
||||
localparam REFCLK_FREQ = 200.0; // MHz
|
||||
localparam real REFCLK_PERIOD = (1000000.0/(2*REFCLK_FREQ));
|
||||
localparam RESET_PERIOD = 200000; // ps
|
||||
@@ -55,8 +65,12 @@ module tb;
|
||||
wire sys_rst = sys_rst_n;
|
||||
reg sys_clk_i = 1'b0;
|
||||
always #(CLKIN_PERIOD/2.0) sys_clk_i = ~sys_clk_i;
|
||||
wire sys_clk_p = sys_clk_i;
|
||||
wire sys_clk_n = ~sys_clk_i;
|
||||
reg clk_ref_i = 1'b0;
|
||||
always #REFCLK_PERIOD clk_ref_i = ~clk_ref_i;
|
||||
wire clk_ref_p = clk_ref_i;
|
||||
wire clk_ref_n = ~clk_ref_i;
|
||||
initial begin
|
||||
sys_rst_n = 1'b0;
|
||||
#RESET_PERIOD sys_rst_n = 1'b1;
|
||||
@@ -64,27 +78,27 @@ module tb;
|
||||
|
||||
// ---- real DDR3 pins + model (identical to tb_mig_native_adapter.v) ----
|
||||
wire ddr3_reset_n;
|
||||
wire [15:0] ddr3_dq_fpga;
|
||||
wire [1:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [31:0] ddr3_dq_fpga;
|
||||
wire [3:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [13:0] ddr3_addr_fpga;
|
||||
wire [2:0] ddr3_ba_fpga;
|
||||
wire ddr3_ras_n_fpga, ddr3_cas_n_fpga, ddr3_we_n_fpga;
|
||||
wire [0:0] ddr3_cke_fpga, ddr3_ck_p_fpga, ddr3_ck_n_fpga, ddr3_cs_n_fpga;
|
||||
wire [1:0] ddr3_dm_fpga;
|
||||
wire [3:0] ddr3_dm_fpga;
|
||||
wire [0:0] ddr3_odt_fpga;
|
||||
|
||||
wire [15:0] ddr3_dq_sdram;
|
||||
wire [31:0] ddr3_dq_sdram;
|
||||
reg [13:0] ddr3_addr_sdram;
|
||||
reg [2:0] ddr3_ba_sdram;
|
||||
reg ddr3_ras_n_sdram, ddr3_cas_n_sdram, ddr3_we_n_sdram;
|
||||
wire [0:0] ddr3_cs_n_sdram;
|
||||
wire [0:0] ddr3_odt_sdram;
|
||||
reg [0:0] ddr3_cke_sdram;
|
||||
wire [1:0] ddr3_dm_sdram;
|
||||
wire [1:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
wire [3:0] ddr3_dm_sdram;
|
||||
wire [3:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
reg [0:0] ddr3_ck_p_sdram, ddr3_ck_n_sdram;
|
||||
reg [0:0] ddr3_cs_n_sdram_tmp;
|
||||
reg [1:0] ddr3_dm_sdram_tmp;
|
||||
reg [3:0] ddr3_dm_sdram_tmp;
|
||||
reg [0:0] ddr3_odt_sdram_tmp;
|
||||
|
||||
always @(*) begin
|
||||
@@ -106,7 +120,7 @@ module tb;
|
||||
|
||||
genvar dqwd;
|
||||
generate
|
||||
for (dqwd = 0; dqwd < 16; dqwd = dqwd + 1) begin : dq_delay
|
||||
for (dqwd = 0; dqwd < 32; dqwd = dqwd + 1) begin : dq_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dq (
|
||||
.A(ddr3_dq_fpga[dqwd]), .B(ddr3_dq_sdram[dqwd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -115,7 +129,7 @@ module tb;
|
||||
endgenerate
|
||||
genvar dqswd;
|
||||
generate
|
||||
for (dqswd = 0; dqswd < 2; dqswd = dqswd + 1) begin : dqs_delay
|
||||
for (dqswd = 0; dqswd < 4; dqswd = dqswd + 1) begin : dqs_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dqs_p (
|
||||
.A(ddr3_dqs_p_fpga[dqswd]), .B(ddr3_dqs_p_sdram[dqswd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -127,25 +141,31 @@ module tb;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
ddr3_model u_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram), .dqs(ddr3_dqs_p_sdram), .dqs_n(ddr3_dqs_n_sdram),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
genvar ci;
|
||||
generate
|
||||
for (ci = 0; ci < 2; ci = ci + 1) begin : gen_mem
|
||||
ddr3_model u_comp_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram[2*ci +: 2]), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram[16*ci +: 16]),
|
||||
.dqs(ddr3_dqs_p_sdram[2*ci +: 2]), .dqs_n(ddr3_dqs_n_sdram[2*ci +: 2]),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [63:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [7:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [63:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [127:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [15:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [127:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
|
||||
mig_7series_0_mig #(
|
||||
.SIM_BYPASS_INIT_CAL("FAST")
|
||||
@@ -157,7 +177,7 @@ module tb;
|
||||
.ddr3_ck_p(ddr3_ck_p_fpga), .ddr3_ck_n(ddr3_ck_n_fpga),
|
||||
.ddr3_cke(ddr3_cke_fpga), .ddr3_cs_n(ddr3_cs_n_fpga),
|
||||
.ddr3_dm(ddr3_dm_fpga), .ddr3_odt(ddr3_odt_fpga),
|
||||
.sys_clk_i(sys_clk_i), .clk_ref_i(clk_ref_i),
|
||||
.sys_clk_p(sys_clk_p), .sys_clk_n(sys_clk_n), .clk_ref_p(clk_ref_p), .clk_ref_n(clk_ref_n),
|
||||
.app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en),
|
||||
.app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end),
|
||||
.app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren),
|
||||
@@ -178,25 +198,25 @@ module tb;
|
||||
reg pre_active;
|
||||
reg pre_req, pre_wr;
|
||||
reg [MIG_ADDR_WIDTH-1:0] pre_addr;
|
||||
reg [16*BURST_LEN-1:0] pre_wdata;
|
||||
reg [32*BURST_LEN-1:0] pre_wdata;
|
||||
|
||||
wire adp_req, adp_wr;
|
||||
wire [MIG_ADDR_WIDTH-1:0] adp_addr;
|
||||
wire [16*BURST_LEN-1:0] adp_wdata;
|
||||
wire [2*BURST_LEN-1:0] adp_wmask;
|
||||
wire [16*BURST_LEN-1:0] adp_rdata;
|
||||
wire [32*BURST_LEN-1:0] adp_wdata;
|
||||
wire [4*BURST_LEN-1:0] adp_wmask;
|
||||
wire [32*BURST_LEN-1:0] adp_rdata;
|
||||
wire adp_ready, adp_busy;
|
||||
|
||||
wire arb_ctrl_req_o, arb_ctrl_wr_o;
|
||||
wire [MIG_ADDR_WIDTH-1:0] arb_ctrl_addr_o;
|
||||
wire [16*BURST_LEN-1:0] arb_ctrl_wdata_o;
|
||||
wire [2*BURST_LEN-1:0] arb_ctrl_wmask_o;
|
||||
wire [32*BURST_LEN-1:0] arb_ctrl_wdata_o;
|
||||
wire [4*BURST_LEN-1:0] arb_ctrl_wmask_o;
|
||||
|
||||
assign adp_req = pre_active ? pre_req : arb_ctrl_req_o;
|
||||
assign adp_wr = pre_active ? pre_wr : arb_ctrl_wr_o;
|
||||
assign adp_addr = pre_active ? pre_addr : arb_ctrl_addr_o;
|
||||
assign adp_wdata = pre_active ? pre_wdata : arb_ctrl_wdata_o;
|
||||
assign adp_wmask = pre_active ? {(2*BURST_LEN){1'b0}} : arb_ctrl_wmask_o;
|
||||
assign adp_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : arb_ctrl_wmask_o;
|
||||
|
||||
mig_native_adapter #(.BURST_LEN(BURST_LEN), .ADDR_WIDTH(MIG_ADDR_WIDTH)) u_adapter (
|
||||
.clk(ui_clk), .rst(ui_clk_sync_rst),
|
||||
@@ -215,7 +235,7 @@ module tb;
|
||||
input_byte = $signed(8'((li*11 + pos*41 + t*7 + 3) & 8'hFF));
|
||||
endfunction
|
||||
|
||||
task automatic sdram_write_burst(input [MIG_ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic sdram_write_burst(input [MIG_ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge ui_clk); while (adp_busy) @(posedge ui_clk);
|
||||
pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data;
|
||||
@@ -224,15 +244,18 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// EXP-0084: BYTES_PER_BURST = 4*BURST_LEN (32 bytes/burst, up from
|
||||
// 16) -- 4 consecutive weight bytes pack into each 32-bit word now.
|
||||
task automatic preload_sdram_layers;
|
||||
integer li, bi, wb, tt;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
reg [32*BURST_LEN-1:0] burst_data;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(2*BURST_LEN)); bi = bi + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(4*BURST_LEN)); bi = bi + 1) begin
|
||||
for (wb = 0; wb < BURST_LEN; wb = wb + 1) begin
|
||||
tt = bi*(2*BURST_LEN) + wb*2;
|
||||
burst_data[wb*16 +: 16] = {weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
tt = bi*(4*BURST_LEN) + wb*4;
|
||||
burst_data[wb*32 +: 32] = {weight_byte(li, tt+3), weight_byte(li, tt+2),
|
||||
weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
end
|
||||
sdram_write_burst((li*WORDS_PER_LAYER + bi*BURST_LEN), burst_data);
|
||||
end
|
||||
@@ -240,30 +263,31 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- 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). ----
|
||||
// ---- real activation preload (EXP-0084 layout: FOUR consecutive
|
||||
// tiles share one BURST_LEN=8-word (256-bit) burst -- tile parity
|
||||
// 0/1/2/3 -> quarters [63:0]/[127:64]/[191:128]/[255:192], 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/2)*BURST_LEN);
|
||||
act_x_base = {{(ADDR_WIDTH-MIG_ADDR_WIDTH){1'b0}}, ACT_MEM_BASE} + (li*M + pos) * ((N_TILES/4)*BURST_LEN);
|
||||
endfunction
|
||||
|
||||
task automatic preload_ddr3_activations;
|
||||
integer li, pos, tp, k;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
integer li, pos, tq, qi;
|
||||
reg [32*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 (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, (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);
|
||||
for (tq = 0; tq < N_TILES/4; tq = tq + 1) begin
|
||||
burst_data = {(32*BURST_LEN){1'b0}};
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst_data[qi*64 +: 64] = {input_byte(li, pos, (4*tq+qi)*P_IN + 7), input_byte(li, pos, (4*tq+qi)*P_IN + 6),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 5), input_byte(li, pos, (4*tq+qi)*P_IN + 4),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 3), input_byte(li, pos, (4*tq+qi)*P_IN + 2),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 1), input_byte(li, pos, (4*tq+qi)*P_IN + 0)};
|
||||
sdram_write_burst(base[MIG_ADDR_WIDTH-1:0] + tq*BURST_LEN, burst_data);
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -309,8 +333,8 @@ module tb;
|
||||
wire [1:0] s_ctrl_req, s_ctrl_wr;
|
||||
wire [1:0] s_ctrl_ready, s_ctrl_busy;
|
||||
wire [MIG_ADDR_WIDTH*2-1:0] s_ctrl_addr_flat;
|
||||
wire [16*BURST_LEN*2-1:0] s_ctrl_wdata_flat, s_ctrl_rdata_flat;
|
||||
wire [2*BURST_LEN*2-1:0] s_ctrl_wmask_flat;
|
||||
wire [32*BURST_LEN*2-1:0] s_ctrl_wdata_flat, s_ctrl_rdata_flat;
|
||||
wire [4*BURST_LEN*2-1:0] s_ctrl_wmask_flat;
|
||||
|
||||
sdram_arbiter_n #(.NUM_REQ(2), .ADDR_WIDTH(MIG_ADDR_WIDTH), .BURST_LEN(BURST_LEN)) u_arb (
|
||||
.clk(ui_clk), .rst(ui_clk_sync_rst),
|
||||
@@ -350,9 +374,9 @@ module tb;
|
||||
.mem_active(mem_active[gi]), .mem_grant(mem_grant[gi]),
|
||||
.ctrl_req(s_ctrl_req[gi]), .ctrl_wr(s_ctrl_wr[gi]),
|
||||
.ctrl_addr(s_ctrl_addr_flat[gi*MIG_ADDR_WIDTH +: MIG_ADDR_WIDTH]),
|
||||
.ctrl_wdata(s_ctrl_wdata_flat[gi*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wmask(s_ctrl_wmask_flat[gi*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.ctrl_rdata(s_ctrl_rdata_flat[gi*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wdata(s_ctrl_wdata_flat[gi*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_wmask(s_ctrl_wmask_flat[gi*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.ctrl_rdata(s_ctrl_rdata_flat[gi*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_ready(s_ctrl_ready[gi]), .ctrl_busy(s_ctrl_busy[gi])
|
||||
);
|
||||
end
|
||||
|
||||
@@ -11,17 +11,20 @@
|
||||
//
|
||||
// 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.
|
||||
// activation data into a real burst-memory backend, matching
|
||||
// act_tile_fetch.v's own real memory layout.
|
||||
//
|
||||
// EXP-0084 UPDATE: real 32-bit DDR3 channel widening -- backend
|
||||
// switched to burst_mem_model32.v (the real AS4C32M16SA x16 SDR model
|
||||
// this test used before is genuinely fixed at 16-bit and can't
|
||||
// represent the new bus width, see that model's own header), and both
|
||||
// preload tasks rewritten for the new BYTES_PER_BURST=4*BURST_LEN
|
||||
// (weights, layer_prefetch_ctrl.v) and 4-tiles-per-burst (activations,
|
||||
// act_tile_fetch.v) real layouts.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam SDRAM_ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS; // 25
|
||||
localparam SDRAM_ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
|
||||
@@ -42,50 +45,36 @@ module tb;
|
||||
integer cyc;
|
||||
always @(posedge clk) if (!rst) cyc <= cyc + 1;
|
||||
|
||||
// ---- real SDRAM controller + model ----
|
||||
// ---- real burst-memory backend ----
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [SDRAM_ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [16*BURST_LEN-1:0] ctrl_rdata;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_rdata;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
reg wpre_req, wpre_wr;
|
||||
reg [SDRAM_ADDR_WIDTH-1:0] wpre_addr;
|
||||
reg [16*BURST_LEN-1:0] wpre_wdata;
|
||||
reg [32*BURST_LEN-1:0] wpre_wdata;
|
||||
reg pre_active;
|
||||
|
||||
wire slot_ctrl_req, slot_ctrl_wr;
|
||||
wire [SDRAM_ADDR_WIDTH-1:0] slot_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] slot_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] slot_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] slot_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] slot_ctrl_wmask;
|
||||
|
||||
assign ctrl_req = pre_active ? wpre_req : slot_ctrl_req;
|
||||
assign ctrl_wr = pre_active ? wpre_wr : slot_ctrl_wr;
|
||||
assign ctrl_addr = pre_active ? wpre_addr : slot_ctrl_addr;
|
||||
assign ctrl_wdata = pre_active ? wpre_wdata : slot_ctrl_wdata;
|
||||
assign ctrl_wmask = pre_active ? {(2*BURST_LEN){1'b0}} : slot_ctrl_wmask;
|
||||
assign ctrl_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : slot_ctrl_wmask;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(SDRAM_ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
function automatic signed [7:0] weight_byte(input integer li, input integer t);
|
||||
@@ -95,7 +84,7 @@ module tb;
|
||||
input_byte = $signed(8'((li*11 + pos*41 + t*7 + 3) & 8'hFF));
|
||||
endfunction
|
||||
|
||||
task automatic sdram_write_burst(input [SDRAM_ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic sdram_write_burst(input [SDRAM_ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
wpre_req = 1'b1; wpre_wr = 1'b1; wpre_addr = word_addr; wpre_wdata = data;
|
||||
@@ -104,15 +93,18 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// EXP-0084: BYTES_PER_BURST = 4*BURST_LEN (32 bytes/burst, up from
|
||||
// 16) -- 4 consecutive weight bytes pack into each 32-bit word now.
|
||||
task automatic preload_sdram_layers;
|
||||
integer li, bi, wb, tt;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
reg [32*BURST_LEN-1:0] burst_data;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(2*BURST_LEN)); bi = bi + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(4*BURST_LEN)); bi = bi + 1) begin
|
||||
for (wb = 0; wb < BURST_LEN; wb = wb + 1) begin
|
||||
tt = bi*(2*BURST_LEN) + wb*2;
|
||||
burst_data[wb*16 +: 16] = {weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
tt = bi*(4*BURST_LEN) + wb*4;
|
||||
burst_data[wb*32 +: 32] = {weight_byte(li, tt+3), weight_byte(li, tt+2),
|
||||
weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
end
|
||||
sdram_write_burst((li*WORDS_PER_LAYER + bi*BURST_LEN), burst_data);
|
||||
end
|
||||
@@ -120,31 +112,33 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- 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. ----
|
||||
// ---- real activation preload (EXP-0084 layout: FOUR consecutive
|
||||
// tiles share one BURST_LEN=8-word (256-bit) burst -- tile parity
|
||||
// 0/1/2/3 -> quarters [63:0]/[127:64]/[191:128]/[255:192], see
|
||||
// act_tile_fetch.v's own header). x_base(li,pos) = ACT_MEM_BASE +
|
||||
// (li*M+pos)*(N_TILES/4*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/2)*BURST_LEN);
|
||||
act_x_base = ACT_MEM_BASE + (li*M + pos) * ((N_TILES/4)*BURST_LEN);
|
||||
endfunction
|
||||
|
||||
task automatic preload_sdram_activations;
|
||||
integer li, pos, tp, k;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
integer li, pos, tq, qi;
|
||||
reg [32*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 (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, (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);
|
||||
for (tq = 0; tq < N_TILES/4; tq = tq + 1) begin // tq = burst-quad index
|
||||
burst_data = {(32*BURST_LEN){1'b0}};
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst_data[qi*64 +: 64] = {input_byte(li, pos, (4*tq+qi)*P_IN + 7), input_byte(li, pos, (4*tq+qi)*P_IN + 6),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 5), input_byte(li, pos, (4*tq+qi)*P_IN + 4),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 3), input_byte(li, pos, (4*tq+qi)*P_IN + 2),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 1), input_byte(li, pos, (4*tq+qi)*P_IN + 0)};
|
||||
sdram_write_burst(base[SDRAM_ADDR_WIDTH-1:0] + tq*BURST_LEN, burst_data);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS;
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam NUM_REQ = 3;
|
||||
@@ -26,38 +23,24 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
reg [NUM_REQ-1:0] req_active, req_req, req_wr;
|
||||
wire [NUM_REQ-1:0] req_grant, req_ready, req_busy;
|
||||
reg [NUM_REQ*ADDR_WIDTH-1:0] req_addr;
|
||||
reg [NUM_REQ*16*BURST_LEN-1:0] req_wdata;
|
||||
reg [NUM_REQ*2*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*16*BURST_LEN-1:0] req_rdata;
|
||||
reg [NUM_REQ*32*BURST_LEN-1:0] req_wdata;
|
||||
reg [NUM_REQ*4*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*32*BURST_LEN-1:0] req_rdata;
|
||||
|
||||
sdram_arbiter_n #(
|
||||
.NUM_REQ(NUM_REQ), .ADDR_WIDTH(ADDR_WIDTH), .BURST_LEN(BURST_LEN)
|
||||
@@ -80,7 +63,7 @@ module tb;
|
||||
// no retry if lost.
|
||||
task automatic one_shot_txn(
|
||||
input integer slot, input t_wr, input [ADDR_WIDTH-1:0] t_addr,
|
||||
input [16*BURST_LEN-1:0] t_wdata, output [16*BURST_LEN-1:0] t_rdata
|
||||
input [32*BURST_LEN-1:0] t_wdata, output [32*BURST_LEN-1:0] t_rdata
|
||||
);
|
||||
begin
|
||||
@(posedge clk);
|
||||
@@ -88,26 +71,26 @@ module tb;
|
||||
req_req[slot] = 1'b1;
|
||||
req_wr[slot] = t_wr;
|
||||
req_addr[slot*ADDR_WIDTH +: ADDR_WIDTH] = t_addr;
|
||||
req_wdata[slot*16*BURST_LEN +: 16*BURST_LEN] = t_wdata;
|
||||
req_wmask[slot*2*BURST_LEN +: 2*BURST_LEN] = {(2*BURST_LEN){1'b0}};
|
||||
req_wdata[slot*32*BURST_LEN +: 32*BURST_LEN] = t_wdata;
|
||||
req_wmask[slot*4*BURST_LEN +: 4*BURST_LEN] = {(4*BURST_LEN){1'b0}};
|
||||
@(posedge clk);
|
||||
req_req[slot] = 1'b0;
|
||||
while (!req_ready[slot]) @(posedge clk);
|
||||
t_rdata = req_rdata[slot*16*BURST_LEN +: 16*BURST_LEN];
|
||||
t_rdata = req_rdata[slot*32*BURST_LEN +: 32*BURST_LEN];
|
||||
req_active[slot] = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
reg [16*BURST_LEN-1:0] got, wpat;
|
||||
reg [32*BURST_LEN-1:0] got, wpat;
|
||||
integer k;
|
||||
|
||||
task automatic check_slot(input integer slot, input [ADDR_WIDTH-1:0] a, input [15:0] pattern);
|
||||
integer i;
|
||||
begin
|
||||
for (i = 0; i < BURST_LEN; i = i + 1)
|
||||
wpat[i*16 +: 16] = pattern + i[15:0];
|
||||
wpat[i*32 +: 32] = {pattern, pattern + i[15:0]};
|
||||
one_shot_txn(slot, 1'b1, a, wpat, got);
|
||||
one_shot_txn(slot, 1'b0, a, {(16*BURST_LEN){1'b0}}, got);
|
||||
one_shot_txn(slot, 1'b0, a, {(32*BURST_LEN){1'b0}}, got);
|
||||
tests = tests + 1;
|
||||
if (got !== wpat) begin
|
||||
$display("FAIL slot=%0d addr=%0d: got=%h expected=%h", slot, a, got, wpat);
|
||||
@@ -133,19 +116,19 @@ module tb;
|
||||
|
||||
$display("=== TEST 2: simultaneous multi-requester ACTIVATION (the real EXP-0066 risk case) -- each requester fires its OWN one-shot req only once IT sees its OWN grant, exactly matching packed_slot.v's real S_MEMWAIT usage, not a blind simultaneous fire ===");
|
||||
begin : test2
|
||||
reg [16*BURST_LEN-1:0] w0, w1, w2;
|
||||
reg [32*BURST_LEN-1:0] w0, w1, w2;
|
||||
integer kk;
|
||||
for (kk = 0; kk < BURST_LEN; kk = kk + 1) begin
|
||||
w0[kk*16 +: 16] = 16'hD000 + kk[15:0];
|
||||
w1[kk*16 +: 16] = 16'hE000 + kk[15:0];
|
||||
w2[kk*16 +: 16] = 16'hF000 + kk[15:0];
|
||||
w0[kk*32 +: 32] = {16'hD000, 16'hD000 + kk[15:0]};
|
||||
w1[kk*32 +: 32] = {16'hE000, 16'hE000 + kk[15:0]};
|
||||
w2[kk*32 +: 32] = {16'hF000, 16'hF000 + kk[15:0]};
|
||||
end
|
||||
req_addr[0*ADDR_WIDTH +: ADDR_WIDTH] = 25'd100;
|
||||
req_addr[1*ADDR_WIDTH +: ADDR_WIDTH] = 25'd108;
|
||||
req_addr[2*ADDR_WIDTH +: ADDR_WIDTH] = 25'd116;
|
||||
req_wdata[0*16*BURST_LEN +: 16*BURST_LEN] = w0;
|
||||
req_wdata[1*16*BURST_LEN +: 16*BURST_LEN] = w1;
|
||||
req_wdata[2*16*BURST_LEN +: 16*BURST_LEN] = w2;
|
||||
req_wdata[0*32*BURST_LEN +: 32*BURST_LEN] = w0;
|
||||
req_wdata[1*32*BURST_LEN +: 32*BURST_LEN] = w1;
|
||||
req_wdata[2*32*BURST_LEN +: 32*BURST_LEN] = w2;
|
||||
req_wr[0] = 1'b1; req_wr[1] = 1'b1; req_wr[2] = 1'b1;
|
||||
|
||||
// all three raise `active` on the SAME cycle (the real
|
||||
|
||||
Reference in New Issue
Block a user