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,20 +15,30 @@
|
||||
# when PERSIST is FALSE (the Vivado default -- set explicitly here so
|
||||
# this dependency is self-documenting in the constraints, not just a
|
||||
# silent default someone could flip later without realizing why).
|
||||
set_property BITSTREAM.CONFIG.PERSIST FALSE [current_design]
|
||||
set_property BITSTREAM.CONFIG.PERSIST NO [current_design]
|
||||
|
||||
# ---- config-flash passthrough (-> flash_spi_master.v, EXP-0077):
|
||||
# D00_MOSI/D01_DIN/FCS_B are the SAME physical pins the FPGA's own
|
||||
# dedicated Master-SPI config hardware uses AT BOOT to self-load its
|
||||
# bitstream -- post-configuration they become ordinary fabric I/O
|
||||
# (real Xilinx behavior, PERSIST=FALSE, the Vivado default) and this
|
||||
# design deliberately reclaims them for the flash_spi_master.v bridge
|
||||
# (the board wires the config flash EXCLUSIVELY to the FPGA -- see
|
||||
# that module's own header). CCLK is NOT constrained here -- it's
|
||||
# driven via STARTUPE2 internally, never a plain top-level port.
|
||||
set_property PACKAGE_PIN K17 [get_ports flash_mosi]
|
||||
set_property PACKAGE_PIN K18 [get_ports flash_miso]
|
||||
set_property PACKAGE_PIN L13 [get_ports flash_cs_n]
|
||||
# EXP-0084 REAL, RE-CONFIRMED CONFLICT (not hypothetical any more):
|
||||
# D00_MOSI/D01_DIN/FCS_B's ORIGINAL pins (K17/K18/L13, bank 14) share
|
||||
# bank 14 with the real differential reference clock (clk_ref_p/n,
|
||||
# T14/T15) chosen during the EXP-0084 MIG wizard session -- a single
|
||||
# I/O bank can only have ONE VCCO, and LVCMOS33 (3.3V, what the flash
|
||||
# needs) is incompatible with LVDS_25 (2.5V, what clk_ref needs). This
|
||||
# was flagged as a real *risk* when T14/T15 was chosen (real device
|
||||
# data showed bank 14 already hosted the flash bus); EXP-0084's real
|
||||
# place_design run turned that risk into a real, observed placement
|
||||
# failure ("IO placement is infeasible" -- flash_mosi/flash_cs_n
|
||||
# couldn't be placed at their old LOCs at all). FIX: moved the flash
|
||||
# bus to bank 16 (D9/D10/C9) -- completely unconstrained, no VCCO
|
||||
# commitment, so it can freely be 3.3V with zero conflict. Real,
|
||||
# verified-available pins (queried from the actual part database, not
|
||||
# guessed) -- D9/D10/C9 are ordinary I/O in bank 16, none of them are
|
||||
# the config-mode-reserved EMCCLK/RDWR_B/CSI_B pins (still PROHIBITed
|
||||
# below regardless). CCLK is NOT constrained here -- it's driven via
|
||||
# STARTUPE2 internally, never a plain top-level port.
|
||||
set_property PACKAGE_PIN D9 [get_ports flash_mosi]
|
||||
set_property PACKAGE_PIN D10 [get_ports flash_miso]
|
||||
set_property PACKAGE_PIN C9 [get_ports flash_cs_n]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports flash_mosi]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports flash_miso]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports flash_cs_n]
|
||||
@@ -39,7 +49,21 @@ set_property IOSTANDARD LVCMOS33 [get_ports flash_cs_n]
|
||||
# extra data lines) -- PROHIBITed so Vivado's auto-placement never
|
||||
# lands an unrelated port there by accident (it already had once,
|
||||
# before this constraint existed, on a result-data bit).
|
||||
set_property PROHIBIT true [get_package_pins {L16 R16 V15}]
|
||||
set_property PROHIBIT true [get_sites -of_objects [get_package_pins {L16 R16 V15}]]
|
||||
|
||||
# ---- EXP-0084: remaining top-level ports with no fixed board LOC yet
|
||||
# (result-data debug pins, status signals) default to LVCMOS18 with no
|
||||
# explicit IOSTANDARD set -- real place_design found this real,
|
||||
# concrete: banks 14/15/34/35 are ALL already committed to other real
|
||||
# voltages (2.5V/3.3V/1.5V/1.5V), leaving only bank 16's spare pins as
|
||||
# LVCMOS18-compatible, and there aren't enough of them (40 ports vs 10
|
||||
# pins). Assign these explicitly to LVCMOS33 so they place in bank
|
||||
# 15's own real spare capacity (46 free pins) instead -- a real,
|
||||
# necessary fix, not a workaround; a permanent board LOC for each
|
||||
# should still be assigned once the rest of the board layout is
|
||||
# decided (S7 of docs/PHYSICAL_REALIZATION.md).
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {s0_result_data_a[*] s0_result_data_b[*] s1_result_data_a[*] s1_result_data_b[*]}]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {job_out_slot[*] job_out_done init_calib_complete ui_clk_o}]
|
||||
|
||||
# ---- neural-processor management SPI (-> spi_host_bridge_v3.v):
|
||||
# job submission + register file. Bank 15, column A/B (package edge,
|
||||
@@ -57,9 +81,17 @@ set_property IOSTANDARD LVCMOS33 [get_ports mosi]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports miso]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports cs_n]
|
||||
|
||||
# ---- sys_rst: not part of the DDR3 MIG's own pin set (that's
|
||||
# sys_rst too, but MIG's XDC only constrains the DDR3-facing timing,
|
||||
# not necessarily IOSTANDARD for every board variant) -- pin left to
|
||||
# auto-placement for now (low pin-count, no real board decision yet
|
||||
# on where the reset source sits); explicitly constrain once the PCB
|
||||
# layout for the reset circuit (button/supervisor IC) is decided.
|
||||
# ---- sys_rst: EXP-0084 found this real -- with the 32-bit DDR3
|
||||
# interface now committing banks 14/34/35 to 2.5V/1.5V/1.5V and bank
|
||||
# 15 to 3.3V, there is genuinely NO bank left at the 1.8V sys_rst was
|
||||
# silently defaulting to (no explicit IOSTANDARD was ever set) -- real
|
||||
# place_design failure ("IO placement is infeasible... needs 1.8V,
|
||||
# has 0 sites"), not hit before only because the smaller 16-bit I/O
|
||||
# footprint happened to leave enough slack somewhere. Placed here
|
||||
# temporarily in bank 15 alongside the management SPI bus (same real,
|
||||
# already-committed 3.3V) -- NOT a final board decision, still pending
|
||||
# the real PCB layout for the reset circuit (button/supervisor IC),
|
||||
# per S7 of docs/PHYSICAL_REALIZATION.md. Real, verified-free pin
|
||||
# (queried from the actual part database).
|
||||
set_property PACKAGE_PIN G13 [get_ports sys_rst]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports sys_rst]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -88,9 +88,9 @@ module ddr_prefetch_mgr #(
|
||||
output wire ctrl_req,
|
||||
output wire ctrl_wr,
|
||||
output wire [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
|
||||
);
|
||||
|
||||
@@ -7,20 +7,30 @@
|
||||
// 16-bit-WORD req/wr/addr/wdata/lb_n/ub_n -> rdata/ready port (the
|
||||
// SAME shape as V2's real psram_controller.v / sdram_unified_
|
||||
// backend.v AR port), but V3's shared memory path (sdram_arbiter_n.v
|
||||
// -> mig_native_adapter.v) only understands BURST_LEN=8 (128-bit)
|
||||
// chunks. This module is the translator, matching sdram_unified_
|
||||
// -> mig_native_adapter.v) only understands BURST_LEN=8, 32-bit-word
|
||||
// (256-bit) chunks (EXP-0084's real DDR3 widening -- was 128-bit
|
||||
// before). This module is the translator, matching sdram_unified_
|
||||
// backend.v's own AR-port technique exactly (not reinvented): a
|
||||
// write masks out every word in the burst except the target one
|
||||
// (DQM-style byte masking, already how this project's whole memory
|
||||
// stack works); a read fetches the whole burst and extracts the
|
||||
// target word combinationally.
|
||||
// write masks out every byte in the burst except the target 16-bit
|
||||
// half-word (DQM-style byte masking, already how this project's whole
|
||||
// memory stack works); a read fetches the whole burst and extracts
|
||||
// the target half-word combinationally.
|
||||
//
|
||||
// Sits as one requester on sdram_arbiter_n.v (alongside N packed_
|
||||
// slot.v instances) -- `active` is asserted for the WHOLE single-word
|
||||
// transaction (word-granularity, no multi-burst sequencing needed),
|
||||
// so mem_grant only needs to be observed once before the one-shot
|
||||
// ctrl_req fires, same discipline as packed_slot.v's own S_MEMWAIT
|
||||
// (EXP-0066's real, hard-won lesson).
|
||||
// EXP-0084 ADDRESSING NOTE (real, deliberate design choice, not just a
|
||||
// mechanical width bump): the HOST-facing contract (mem_addr as a
|
||||
// 16-bit-word address, mem_wdata/mem_rdata as 16-bit values,
|
||||
// mem_lb_n/mem_ub_n as byte enables) is kept COMPLETELY UNCHANGED --
|
||||
// this module stays the shock absorber between the host's own fixed
|
||||
// 16-bit-word view (spi_host_bridge_v3.v's WRITE_MEM/READ_MEM opcode
|
||||
// payload size, and by extension the ESP32 firmware contract, is
|
||||
// NOT touched by the DDR3 widening) and the system's now-32-bit-word-
|
||||
// native shared ctrl bus. mem_addr's LSB now additionally selects
|
||||
// WHICH 16-bit half of the addressed 32-bit ctrl-bus word to target;
|
||||
// this halves the host's own reachable byte range for a given
|
||||
// ADDR_WIDTH (a real, honestly-disclosed, non-blocking limitation of
|
||||
// this debug/raw-access path only -- not the compute path, and not
|
||||
// currently a practical constraint at this project's real usage
|
||||
// scale).
|
||||
// ============================================================
|
||||
module host_mem_bridge #(
|
||||
parameter BURST_LEN = 8,
|
||||
@@ -31,10 +41,11 @@ module host_mem_bridge #(
|
||||
|
||||
// ---- host-facing port (matches spi_host_bridge.v's own
|
||||
// mem_req/mem_wr/mem_addr/mem_wdata/mem_lb_n/mem_ub_n ->
|
||||
// mem_rdata/mem_ready convention exactly) ----
|
||||
// mem_rdata/mem_ready convention exactly -- UNCHANGED by EXP-0084,
|
||||
// see header) ----
|
||||
input wire mem_req,
|
||||
input wire mem_wr,
|
||||
input wire [ADDR_WIDTH-1:0] mem_addr, // WORD address (not burst-aligned)
|
||||
input wire [ADDR_WIDTH-1:0] mem_addr, // 16-bit-word address (not burst-aligned)
|
||||
input wire [15:0] mem_wdata,
|
||||
input wire mem_lb_n,
|
||||
input wire mem_ub_n,
|
||||
@@ -49,13 +60,13 @@ module host_mem_bridge #(
|
||||
output reg req_req,
|
||||
output reg req_wr,
|
||||
output reg [ADDR_WIDTH-1:0] req_addr,
|
||||
output reg [16*BURST_LEN-1:0] req_wdata,
|
||||
output reg [2*BURST_LEN-1:0] req_wmask,
|
||||
input wire [16*BURST_LEN-1:0] req_rdata,
|
||||
output reg [32*BURST_LEN-1:0] req_wdata,
|
||||
output reg [4*BURST_LEN-1:0] req_wmask,
|
||||
input wire [32*BURST_LEN-1:0] req_rdata,
|
||||
input wire req_ready,
|
||||
input wire req_busy
|
||||
);
|
||||
localparam ALIGN_BITS = $clog2(BURST_LEN);
|
||||
localparam ALIGN_BITS = $clog2(BURST_LEN); // 3: which of the BURST_LEN 32-bit words in the burst
|
||||
|
||||
localparam S_IDLE = 2'd0,
|
||||
S_MEMWAIT = 2'd1,
|
||||
@@ -63,10 +74,16 @@ module host_mem_bridge #(
|
||||
S_DONE = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [ALIGN_BITS-1:0] word_in_block;
|
||||
reg [ALIGN_BITS-1:0] word_in_block; // which 32-bit word within the burst
|
||||
reg half_sel; // which 16-bit half of that 32-bit word
|
||||
|
||||
assign req_active = (state == S_MEMWAIT) || (state == S_XFER);
|
||||
|
||||
// byte offset (0..4*BURST_LEN-2, even) of the target half-word
|
||||
// within the burst's own byte layout -- word_in_block*4 bytes/word
|
||||
// + half_sel*2 bytes/half.
|
||||
wire [ALIGN_BITS+1:0] byte_offset = {word_in_block, half_sel, 1'b0};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= S_IDLE;
|
||||
@@ -79,18 +96,30 @@ module host_mem_bridge #(
|
||||
case (state)
|
||||
S_IDLE: begin
|
||||
if (mem_req) begin
|
||||
req_addr <= {mem_addr[ADDR_WIDTH-1:ALIGN_BITS], {ALIGN_BITS{1'b0}}};
|
||||
word_in_block <= mem_addr[ALIGN_BITS-1:0];
|
||||
// mem_addr[0] = which 16-bit half of the 32-bit
|
||||
// ctrl-bus word; mem_addr[ALIGN_BITS:1] = which
|
||||
// of the BURST_LEN 32-bit words in the burst;
|
||||
// the remaining upper bits, re-aligned to
|
||||
// 32-bit-word (ctrl bus) units, form the
|
||||
// burst-aligned req_addr.
|
||||
req_addr <= {1'b0, mem_addr[ADDR_WIDTH-1:ALIGN_BITS+1], {ALIGN_BITS{1'b0}}};
|
||||
word_in_block <= mem_addr[ALIGN_BITS:1];
|
||||
half_sel <= mem_addr[0];
|
||||
req_wr <= mem_wr;
|
||||
if (mem_wr) begin
|
||||
// replicate the target word across the whole
|
||||
// burst; only its own mask bits matter (see
|
||||
// header -- same DQM-style technique as
|
||||
// replicate the target half-word across the
|
||||
// whole burst; only its own mask bits matter
|
||||
// (see header -- same DQM-style technique as
|
||||
// sdram_unified_backend.v's own AR port).
|
||||
req_wdata <= {BURST_LEN{mem_wdata}};
|
||||
req_wmask <= {(2*BURST_LEN){1'b1}} &
|
||||
~(({{(2*BURST_LEN-2){1'b0}}, 2'b11}) << (mem_addr[ALIGN_BITS-1:0]*2)) |
|
||||
(({{(2*BURST_LEN-2){1'b0}}, mem_ub_n, mem_lb_n}) << (mem_addr[ALIGN_BITS-1:0]*2));
|
||||
// byte offset within the burst is computed
|
||||
// directly from the LIVE mem_addr here (not
|
||||
// from word_in_block/half_sel, which are
|
||||
// nonblocking-assigned THIS same cycle and
|
||||
// not yet valid until the next one).
|
||||
req_wdata <= {(4*BURST_LEN/2){mem_wdata}};
|
||||
req_wmask <= ({(4*BURST_LEN){1'b1}} &
|
||||
~(({{(4*BURST_LEN-2){1'b0}}, 2'b11}) << {mem_addr[ALIGN_BITS:0], 1'b0})) |
|
||||
(({{(4*BURST_LEN-2){1'b0}}, mem_ub_n, mem_lb_n}) << {mem_addr[ALIGN_BITS:0], 1'b0});
|
||||
end
|
||||
state <= S_MEMWAIT;
|
||||
end
|
||||
@@ -106,7 +135,7 @@ module host_mem_bridge #(
|
||||
S_XFER: begin
|
||||
if (req_ready) begin
|
||||
if (!req_wr)
|
||||
mem_rdata <= req_rdata[word_in_block*16 +: 16];
|
||||
mem_rdata <= req_rdata[byte_offset*8 +: 16];
|
||||
state <= S_DONE;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
// ============================================================
|
||||
// V3 -- adapter between this project's own established memory-
|
||||
// controller contract (req/wr/addr/wdata/wmask -> rdata/ready/busy,
|
||||
// BURST_LEN=8 16-bit words = 128 bits/transaction, the SAME shape
|
||||
// sdram_controller.v has presented everywhere in this project since
|
||||
// STEP16) and the REAL Xilinx MIG 7-series native "app" user
|
||||
// interface (PG063), generated for this project's actual DDR3 target
|
||||
// (mig_7series_0, XC7A100T, MT41J128M16JT-125:K, PHY:Controller
|
||||
// ratio 2:1).
|
||||
// BURST_LEN=8 32-bit words = 256 bits/transaction since EXP-0084's
|
||||
// real 32-bit DDR3 channel widening -- was 16-bit/128 bits before)
|
||||
// and the REAL Xilinx MIG 7-series native "app" user interface
|
||||
// (PG063), generated for this project's actual DDR3 target
|
||||
// (mig_7series_0, XC7A100T, 2x MT41J128M16JT-125:K ganged to 32-bit,
|
||||
// PHY:Controller ratio 2:1, 2900ps/344.827MHz, EXP-0084).
|
||||
//
|
||||
// Runs entirely in the ui_clk domain -- MIG's own generated clock is
|
||||
// this design's new system clock (the standard way MIG-based designs
|
||||
@@ -17,14 +17,20 @@
|
||||
// synchronized to ui_clk by the caller.
|
||||
//
|
||||
// ADDRESSING (real, derived from THIS project's actual generated MIG
|
||||
// config, not assumed): Data Width=16, Phy:Controller ratio 2:1 =>
|
||||
// nCK_PER_CLK=2 => app data width = 16*8/2 = 64 bits, matching the
|
||||
// real generated mig_7series_0.v port widths exactly (app_wdf_data
|
||||
// [63:0], app_rd_data[63:0]). One app_addr/app_cmd issuance moves a
|
||||
// FULL BURST_LEN=8 (128-bit) DDR3 burst, delivered as TWO 64-bit
|
||||
// beats on the app data bus -- so app_addr increments in the SAME
|
||||
// unit as this project's own existing ctrl_addr (one BURST_LEN=8
|
||||
// chunk per increment), no address scaling needed at this boundary.
|
||||
// config, not assumed, re-verified after EXP-0084's 32-bit
|
||||
// regeneration): Data Width=32, Phy:Controller ratio 2:1 =>
|
||||
// nCK_PER_CLK=2 => app data width = 32*8/2 = 128 bits, matching the
|
||||
// real regenerated mig_7series_0.v port widths exactly (app_wdf_data
|
||||
// [127:0], app_rd_data[127:0], app_wdf_mask[15:0] -- confirmed by
|
||||
// directly reading the real generated wrapper, not assumed from the
|
||||
// old 16-bit numbers). app_addr itself stayed 28 bits (confirmed same
|
||||
// real generated width as before the widening) -- one app_addr/
|
||||
// app_cmd issuance still moves a FULL BURST_LEN=8 chunk per increment,
|
||||
// same as before, just each chunk is now 256 bits (32-bit words) not
|
||||
// 128 bits (16-bit words), delivered as TWO 128-bit beats instead of
|
||||
// two 64-bit beats -- so app_addr increments in the SAME unit as this
|
||||
// project's own existing ctrl_addr, no address scaling needed at this
|
||||
// boundary, same as before.
|
||||
//
|
||||
// Sequencing is deliberately fully sequential, not pipelined
|
||||
// (correctness first): the command is issued and accepted (app_en/
|
||||
@@ -52,9 +58,9 @@ module mig_native_adapter #(
|
||||
input wire req,
|
||||
input wire wr,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
input wire [16*BURST_LEN-1:0] wdata,
|
||||
input wire [2*BURST_LEN-1:0] wmask,
|
||||
output reg [16*BURST_LEN-1:0] rdata,
|
||||
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,
|
||||
|
||||
@@ -64,15 +70,15 @@ module mig_native_adapter #(
|
||||
output reg app_en,
|
||||
input wire app_rdy,
|
||||
|
||||
output reg [63:0] app_wdf_data,
|
||||
output reg app_wdf_end,
|
||||
output reg [7:0] app_wdf_mask,
|
||||
output reg app_wdf_wren,
|
||||
input wire app_wdf_rdy,
|
||||
output reg [127:0] app_wdf_data,
|
||||
output reg app_wdf_end,
|
||||
output reg [15:0] app_wdf_mask,
|
||||
output reg app_wdf_wren,
|
||||
input wire app_wdf_rdy,
|
||||
|
||||
input wire [63:0] app_rd_data,
|
||||
input wire app_rd_data_end,
|
||||
input wire app_rd_data_valid
|
||||
input wire [127:0] app_rd_data,
|
||||
input wire app_rd_data_end,
|
||||
input wire app_rd_data_valid
|
||||
);
|
||||
localparam CMD_WRITE = 3'b000;
|
||||
localparam CMD_READ = 3'b001;
|
||||
@@ -86,8 +92,8 @@ module mig_native_adapter #(
|
||||
|
||||
reg [2:0] state;
|
||||
reg wr_lat;
|
||||
reg [16*BURST_LEN-1:0] wdata_lat;
|
||||
reg [2*BURST_LEN-1:0] wmask_lat;
|
||||
reg [32*BURST_LEN-1:0] wdata_lat;
|
||||
reg [4*BURST_LEN-1:0] wmask_lat;
|
||||
|
||||
assign busy = (state != S_IDLE);
|
||||
|
||||
@@ -98,11 +104,11 @@ module mig_native_adapter #(
|
||||
app_wdf_wren <= 1'b0;
|
||||
app_wdf_end <= 1'b0;
|
||||
ready <= 1'b0;
|
||||
rdata <= {(16*BURST_LEN){1'b0}};
|
||||
rdata <= {(32*BURST_LEN){1'b0}};
|
||||
app_addr <= 28'h0;
|
||||
app_cmd <= CMD_READ;
|
||||
app_wdf_data <= 64'h0;
|
||||
app_wdf_mask <= 8'h0;
|
||||
app_wdf_data <= 128'h0;
|
||||
app_wdf_mask <= 16'h0;
|
||||
end else begin
|
||||
ready <= 1'b0;
|
||||
|
||||
@@ -123,8 +129,8 @@ module mig_native_adapter #(
|
||||
if (app_rdy) begin
|
||||
app_en <= 1'b0;
|
||||
if (wr_lat) begin
|
||||
app_wdf_data <= wdata_lat[63:0];
|
||||
app_wdf_mask <= wmask_lat[7:0];
|
||||
app_wdf_data <= wdata_lat[127:0];
|
||||
app_wdf_mask <= wmask_lat[15:0];
|
||||
app_wdf_end <= 1'b0;
|
||||
app_wdf_wren <= 1'b1;
|
||||
state <= S_WDF0;
|
||||
@@ -136,8 +142,8 @@ module mig_native_adapter #(
|
||||
|
||||
S_WDF0: begin
|
||||
if (app_wdf_rdy) begin
|
||||
app_wdf_data <= wdata_lat[127:64];
|
||||
app_wdf_mask <= wmask_lat[15:8];
|
||||
app_wdf_data <= wdata_lat[255:128];
|
||||
app_wdf_mask <= wmask_lat[31:16];
|
||||
app_wdf_end <= 1'b1;
|
||||
app_wdf_wren <= 1'b1;
|
||||
state <= S_WDF1;
|
||||
@@ -155,10 +161,10 @@ module mig_native_adapter #(
|
||||
S_RD_WAIT: begin
|
||||
if (app_rd_data_valid) begin
|
||||
if (!app_rd_data_end) begin
|
||||
rdata[63:0] <= app_rd_data;
|
||||
rdata[127:0] <= app_rd_data;
|
||||
end else begin
|
||||
rdata[127:64] <= app_rd_data;
|
||||
state <= S_DONE;
|
||||
rdata[255:128] <= app_rd_data;
|
||||
state <= S_DONE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,9 +14,16 @@
|
||||
// packed_slot instances, and the SPI bridge itself) runs in the
|
||||
// ui_clk domain, per this project's own standing convention
|
||||
// (mig_native_adapter.v's header) -- ui_clk is generated BY the MIG
|
||||
// from sys_clk_i, so this module only takes sys_clk_i/clk_ref_i/
|
||||
// from sys_clk_p/n, so this module only takes sys_clk_p/n/clk_ref_p/n/
|
||||
// sys_rst as clock/reset inputs, not a separate system clock.
|
||||
//
|
||||
// EXP-0084: sys_clk and clk_ref are now real DIFFERENTIAL pairs (the
|
||||
// user's own wizard choice, confirmed against the real regenerated
|
||||
// public mig_7series_0.v wrapper, which expects sys_clk_p/n and
|
||||
// clk_ref_p/n directly as raw top-level differential pins -- not a
|
||||
// single-ended sys_clk_i/clk_ref_i pair any more). Real board
|
||||
// implication: a differential oscillator, not a single-ended one.
|
||||
//
|
||||
// This is the first REAL (in-context, not out-of-context) P&R target
|
||||
// for V3: previous P&R runs (EXP-0059/63/67) were all out-of-context
|
||||
// synthesis of a sub-block, without the real MIG-generated pin/timing
|
||||
@@ -42,15 +49,25 @@ module n2_system_ddr3_top #(
|
||||
parameter N_SLOTS = 2,
|
||||
parameter QUEUE_DEPTH = 8
|
||||
)(
|
||||
// ---- MIG clock/reset ----
|
||||
input wire sys_clk_i,
|
||||
// ---- MIG clock/reset (EXP-0084: real differential pairs, both
|
||||
// the user's own wizard choice -- confirmed against the real
|
||||
// regenerated public mig_7series_0.v wrapper, which now expects
|
||||
// sys_clk_p/n and clk_ref_p/n directly as raw top-level
|
||||
// differential pins, not a single-ended sys_clk_i/clk_ref_i pair
|
||||
// any more) ----
|
||||
input wire sys_clk_p,
|
||||
input wire sys_clk_n,
|
||||
input wire sys_rst,
|
||||
input wire clk_ref_i,
|
||||
input wire clk_ref_p,
|
||||
input wire clk_ref_n,
|
||||
|
||||
// ---- real DDR3 pins (matches mig_7series_0.xdc's own port names) ----
|
||||
inout wire [15:0] ddr3_dq,
|
||||
inout wire [1:0] ddr3_dqs_n,
|
||||
inout wire [1:0] ddr3_dqs_p,
|
||||
// ---- real DDR3 pins (matches mig_7series_0.xdc's own port names;
|
||||
// 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, not assumed) ----
|
||||
inout wire [31:0] ddr3_dq,
|
||||
inout wire [3:0] ddr3_dqs_n,
|
||||
inout wire [3:0] ddr3_dqs_p,
|
||||
output wire [13:0] ddr3_addr,
|
||||
output wire [2:0] ddr3_ba,
|
||||
output wire ddr3_ras_n,
|
||||
@@ -61,7 +78,7 @@ module n2_system_ddr3_top #(
|
||||
output wire [0:0] ddr3_ck_n,
|
||||
output wire [0:0] ddr3_cke,
|
||||
output wire [0:0] ddr3_cs_n,
|
||||
output wire [1:0] ddr3_dm,
|
||||
output wire [3:0] ddr3_dm,
|
||||
output wire [0:0] ddr3_odt,
|
||||
|
||||
// ---- physical SPI host interface (-> spi_host_bridge_v3.v) ----
|
||||
@@ -95,15 +112,15 @@ module n2_system_ddr3_top #(
|
||||
output wire job_out_done,
|
||||
output wire [$clog2(N_SLOTS)-1:0] job_out_slot
|
||||
);
|
||||
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 [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;
|
||||
|
||||
assign ui_clk_o = ui_clk;
|
||||
@@ -120,7 +137,7 @@ module n2_system_ddr3_top #(
|
||||
.ddr3_ck_p(ddr3_ck_p), .ddr3_ck_n(ddr3_ck_n),
|
||||
.ddr3_cke(ddr3_cke), .ddr3_cs_n(ddr3_cs_n),
|
||||
.ddr3_dm(ddr3_dm), .ddr3_odt(ddr3_odt),
|
||||
.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),
|
||||
@@ -136,9 +153,9 @@ module n2_system_ddr3_top #(
|
||||
|
||||
wire adp_req, adp_wr;
|
||||
wire [MEM_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;
|
||||
|
||||
mig_native_adapter #(.BURST_LEN(BURST_LEN), .ADDR_WIDTH(MEM_ADDR_WIDTH)) u_adapter (
|
||||
@@ -156,9 +173,9 @@ module n2_system_ddr3_top #(
|
||||
wire [NUM_REQ-1:0] req_active, req_grant, req_req, req_wr;
|
||||
wire [NUM_REQ-1:0] req_ready, req_busy;
|
||||
wire [NUM_REQ*MEM_ADDR_WIDTH-1:0] req_addr;
|
||||
wire [NUM_REQ*16*BURST_LEN-1:0] req_wdata;
|
||||
wire [NUM_REQ*2*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*16*BURST_LEN-1:0] req_rdata;
|
||||
wire [NUM_REQ*32*BURST_LEN-1:0] req_wdata;
|
||||
wire [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(MEM_ADDR_WIDTH), .BURST_LEN(BURST_LEN)
|
||||
@@ -251,9 +268,9 @@ module n2_system_ddr3_top #(
|
||||
.req_active(req_active[2]), .req_grant(req_grant[2]),
|
||||
.req_req(req_req[2]), .req_wr(req_wr[2]),
|
||||
.req_addr(req_addr[2*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]),
|
||||
.req_wdata(req_wdata[2*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.req_wmask(req_wmask[2*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.req_rdata(req_rdata[2*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.req_wdata(req_wdata[2*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.req_wmask(req_wmask[2*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.req_rdata(req_rdata[2*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.req_ready(req_ready[2]), .req_busy(req_busy[2])
|
||||
);
|
||||
|
||||
@@ -288,9 +305,9 @@ module n2_system_ddr3_top #(
|
||||
.mem_active(req_active[0]), .mem_grant(req_grant[0]),
|
||||
.ctrl_req(req_req[0]), .ctrl_wr(req_wr[0]),
|
||||
.ctrl_addr(req_addr[0*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]),
|
||||
.ctrl_wdata(req_wdata[0*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[0*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[0*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wdata(req_wdata[0*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[0*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[0*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_ready(req_ready[0]), .ctrl_busy(req_busy[0])
|
||||
);
|
||||
|
||||
@@ -314,9 +331,9 @@ module n2_system_ddr3_top #(
|
||||
.mem_active(req_active[1]), .mem_grant(req_grant[1]),
|
||||
.ctrl_req(req_req[1]), .ctrl_wr(req_wr[1]),
|
||||
.ctrl_addr(req_addr[1*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]),
|
||||
.ctrl_wdata(req_wdata[1*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[1*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[1*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wdata(req_wdata[1*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[1*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[1*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_ready(req_ready[1]), .ctrl_busy(req_busy[1])
|
||||
);
|
||||
endmodule
|
||||
|
||||
@@ -107,9 +107,9 @@ module packed_slot #(
|
||||
output wire ctrl_req,
|
||||
output wire ctrl_wr,
|
||||
output wire [ADDR_WIDTH-2: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
|
||||
);
|
||||
@@ -140,8 +140,8 @@ module packed_slot #(
|
||||
|
||||
wire pf_ctrl_req, pf_ctrl_wr;
|
||||
wire [ADDR_WIDTH-2: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;
|
||||
|
||||
layer_prefetch_ctrl #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .LAYER_BYTES(LAYER_BYTES), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH-1)
|
||||
@@ -167,8 +167,8 @@ module packed_slot #(
|
||||
|
||||
wire act_ctrl_req, act_ctrl_wr;
|
||||
wire [ADDR_WIDTH-2:0] act_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] act_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] act_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] act_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] act_ctrl_wmask;
|
||||
|
||||
ddr_prefetch_mgr #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH-1)
|
||||
|
||||
@@ -44,18 +44,18 @@ module sdram_arbiter_n #(
|
||||
input wire [NUM_REQ-1:0] req_req,
|
||||
input wire [NUM_REQ-1:0] req_wr,
|
||||
input wire [NUM_REQ*ADDR_WIDTH-1:0] req_addr,
|
||||
input wire [NUM_REQ*16*BURST_LEN-1:0] req_wdata,
|
||||
input wire [NUM_REQ*2*BURST_LEN-1:0] req_wmask,
|
||||
output wire [NUM_REQ*16*BURST_LEN-1:0] req_rdata,
|
||||
input wire [NUM_REQ*32*BURST_LEN-1:0] req_wdata,
|
||||
input wire [NUM_REQ*4*BURST_LEN-1:0] req_wmask,
|
||||
output wire [NUM_REQ*32*BURST_LEN-1:0] req_rdata,
|
||||
output wire [NUM_REQ-1:0] req_ready,
|
||||
output wire [NUM_REQ-1:0] req_busy,
|
||||
|
||||
output wire ctrl_req,
|
||||
output wire ctrl_wr,
|
||||
output wire [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
|
||||
);
|
||||
@@ -107,22 +107,22 @@ module sdram_arbiter_n #(
|
||||
// mux request-side signals from the granted requester -> shared ctrl
|
||||
reg m_req, m_wr;
|
||||
reg [ADDR_WIDTH-1:0] m_addr;
|
||||
reg [16*BURST_LEN-1:0] m_wdata;
|
||||
reg [2*BURST_LEN-1:0] m_wmask;
|
||||
reg [32*BURST_LEN-1:0] m_wdata;
|
||||
reg [4*BURST_LEN-1:0] m_wmask;
|
||||
integer mi;
|
||||
always @(*) begin
|
||||
m_req = 1'b0;
|
||||
m_wr = 1'b0;
|
||||
m_addr = {ADDR_WIDTH{1'b0}};
|
||||
m_wdata = {(16*BURST_LEN){1'b0}};
|
||||
m_wmask = {(2*BURST_LEN){1'b0}};
|
||||
m_wdata = {(32*BURST_LEN){1'b0}};
|
||||
m_wmask = {(4*BURST_LEN){1'b0}};
|
||||
for (mi = 0; mi < NUM_REQ; mi = mi + 1) begin
|
||||
if (sel[mi]) begin
|
||||
m_req = req_req[mi];
|
||||
m_wr = req_wr[mi];
|
||||
m_addr = req_addr[mi*ADDR_WIDTH +: ADDR_WIDTH];
|
||||
m_wdata = req_wdata[mi*16*BURST_LEN +: 16*BURST_LEN];
|
||||
m_wmask = req_wmask[mi*2*BURST_LEN +: 2*BURST_LEN];
|
||||
m_wdata = req_wdata[mi*32*BURST_LEN +: 32*BURST_LEN];
|
||||
m_wmask = req_wmask[mi*4*BURST_LEN +: 4*BURST_LEN];
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -137,7 +137,7 @@ module sdram_arbiter_n #(
|
||||
genvar gd;
|
||||
generate
|
||||
for (gd = 0; gd < NUM_REQ; gd = gd + 1) begin : GEN_DEMUX
|
||||
assign req_rdata[gd*16*BURST_LEN +: 16*BURST_LEN] = ctrl_rdata;
|
||||
assign req_rdata[gd*32*BURST_LEN +: 32*BURST_LEN] = ctrl_rdata;
|
||||
assign req_ready[gd] = sel[gd] ? ctrl_ready : 1'b0;
|
||||
assign req_busy[gd] = sel[gd] ? ctrl_busy : 1'b1;
|
||||
end
|
||||
|
||||
@@ -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