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
153 lines
7.0 KiB
Verilog
153 lines
7.0 KiB
Verilog
`timescale 1ns/1ps
|
|
|
|
// ============================================================
|
|
// V3 -- host raw-memory-access bridge: the missing piece flagged
|
|
// re-auditing spi_host_bridge.v against V3's real architecture.
|
|
// spi_host_bridge.v's WRITE_MEM/READ_MEM opcodes drive a single-
|
|
// 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, 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 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.
|
|
//
|
|
// 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,
|
|
parameter ADDR_WIDTH = 25 // word address, matches sdram_arbiter_n.v's own convention
|
|
)(
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
// ---- 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 -- UNCHANGED by EXP-0084,
|
|
// see header) ----
|
|
input wire mem_req,
|
|
input wire mem_wr,
|
|
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,
|
|
output reg [15:0] mem_rdata,
|
|
output reg mem_ready,
|
|
|
|
// ---- arbiter-facing requester port (matches sdram_arbiter_n.v's
|
|
// own per-slot req_active/req_grant/req_req/req_wr/req_addr/
|
|
// req_wdata/req_wmask -> req_rdata/req_ready/req_busy naming) ----
|
|
output wire req_active,
|
|
input wire req_grant,
|
|
output reg req_req,
|
|
output reg req_wr,
|
|
output reg [ADDR_WIDTH-1:0] req_addr,
|
|
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); // 3: which of the BURST_LEN 32-bit words in the burst
|
|
|
|
localparam S_IDLE = 2'd0,
|
|
S_MEMWAIT = 2'd1,
|
|
S_XFER = 2'd2,
|
|
S_DONE = 2'd3;
|
|
|
|
reg [1:0] state;
|
|
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;
|
|
req_req <= 1'b0;
|
|
mem_ready <= 1'b0;
|
|
end else begin
|
|
req_req <= 1'b0;
|
|
mem_ready <= 1'b0;
|
|
|
|
case (state)
|
|
S_IDLE: begin
|
|
if (mem_req) begin
|
|
// 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 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).
|
|
// 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
|
|
end
|
|
|
|
S_MEMWAIT: begin
|
|
if (req_grant) begin
|
|
req_req <= 1'b1;
|
|
state <= S_XFER;
|
|
end
|
|
end
|
|
|
|
S_XFER: begin
|
|
if (req_ready) begin
|
|
if (!req_wr)
|
|
mem_rdata <= req_rdata[byte_offset*8 +: 16];
|
|
state <= S_DONE;
|
|
end
|
|
end
|
|
|
|
S_DONE: begin
|
|
mem_ready <= 1'b1;
|
|
state <= S_IDLE;
|
|
end
|
|
|
|
default: state <= S_IDLE;
|
|
endcase
|
|
end
|
|
end
|
|
endmodule
|