Files
FPGA-Neural/hardware/v2/nms/rtl/ideal_banked_activation.v
T
micheleandClaude Sonnet 5 8e014d8d49 V2.0.0 hardware freeze - single SDRAM
FASE #1 hardware freeze for FPGA-Neural V2, N4/P8, single external
SDRAM (Alliance Memory AS4C4M16SA-6TIN) serving weights, activations,
and results through one physical sdram_controller.v instance. Removes
the PSRAM dependency (hardware/v1/rtl/psram_controller.v +
memory_interface.v) from the V2 physical path entirely -- V1 itself
remains fully unmodified, the golden reference.

New RTL: sdram_unified_backend.v (2-way W/AR arbitration over one
SDRAM controller, real per-byte DQM write masking added to
sdram_controller.v for correct single-byte result writes with no
read-modify-write), nms_neural_multiprocessor_sdram_unified.v (the
frozen top-level). Two real bugs found and fixed via full-system
testing before being accepted (ERR-0023): a deadlock and an off-by-one
data-shift bug in the new arbitration logic.

Real results: N=4 and N=2 D-Stress bit-exact (256/256 neurons), 40
real AUTO REFRESH events interleaved with zero corruption, real
Yosys+nextpnr-ecp5 synthesis/P&R for LFE5U-45F-8CABGA381 (149/245
TRELLIS_IO, a real 45-pin reduction from the prior dual-memory
design). Timing is MARGINAL (1/8 P&R seeds >=80MHz), reported honestly
rather than masked by the best seed.

Real, sourced ball-level pinout for the SDRAM bus + clk/rst (39/149
signals, P&R-verified) using the official Lattice ECP5U-45 pinout CSV
found on disk during this step's own pre-commit review -- corrects an
earlier draft that wrongly assumed no real pinout data was available.

Chip readiness: NO. Real, disclosed blockers remain (no physical host
interface exists yet -- the RTL's own reg_* ports are a 110-pin raw
test-harness bus; clock source/PLL decision; power/configuration
component selection) -- see hardware/v2/docs/{HARDWARE_FREEZE,
CHIP_READINESS,OPEN_ITEMS}.md for the complete, itemized status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 13:39:55 +02:00

70 lines
3.0 KiB
Verilog

// ============================================================
// Neural Memory System (NMS) -- STEP 3: banked activation SRAM
// contention model. SIMULATION-ONLY, NEVER SYNTHESIZED.
//
// Models a single shared activation vector striped across N_BANKS
// banks by tile index (bank = tile_idx % N_BANKS). Each bank can serve
// ONE DISTINCT address per cycle, but BROADCASTS that address's data
// to every requester currently wanting it (a shared-producer, many-
// consumer read costs exactly one read, not one per consumer -- see
// hardware/v2/nms/rtl.. STEP2's own architecture.log note and the
// user's own NMS spec §11). A cycle where two+ requesters mapped to
// the same bank want DIFFERENT tile indices serves only one of them
// (lowest-index requester wins this round); the others simply retry
// next cycle (idempotent -- an ideal SRAM read has no state cost).
//
// Read latency is modeled as ZERO cycles (grant and data-available
// are the same cycle) -- this file isolates the BANK CONTENTION
// question specifically, decoupled from backing-store latency, which
// STEP1 (hardware/v2/nms/rtl/ideal_memory_model.v, EXP-0017) already
// characterized separately. Fill-from-PSRAM is out of scope here too
// (the vector is assumed already resident, i.e. steady-state
// consumption after prefetch -- the question this file answers is
// "does the on-chip organization itself let N_SLOTS scale", not
// "how do we hide PSRAM latency" (already answered by STEP1).
// ============================================================
module ideal_banked_activation #(
parameter NREQ = 4,
parameter N_BANKS = 2,
parameter AW = 32
)(
input [NREQ-1:0] req_valid,
input [NREQ*AW-1:0] req_addr_flat,
output reg [NREQ-1:0] ack
);
integer b, i;
reg [AW-1:0] req_addr [0:NREQ-1];
reg [AW-1:0] served_addr;
reg have_served;
integer bank_of_i;
always @* begin
for (i = 0; i < NREQ; i = i + 1)
req_addr[i] = req_addr_flat[i*AW +: AW];
ack = {NREQ{1'b0}};
for (b = 0; b < N_BANKS; b = b + 1) begin
have_served = 1'b0;
served_addr = {AW{1'b0}};
// first pass: lowest-index valid requester in this bank sets
// the address served this cycle
for (i = 0; i < NREQ; i = i + 1) begin
bank_of_i = req_addr[i] % N_BANKS;
if (req_valid[i] && (bank_of_i == b) && !have_served) begin
served_addr = req_addr[i];
have_served = 1'b1;
end
end
// second pass: broadcast ack to every requester in this bank
// that wants the SAME address (free, one read serves all)
if (have_served) begin
for (i = 0; i < NREQ; i = i + 1) begin
bank_of_i = req_addr[i] % N_BANKS;
if (req_valid[i] && (bank_of_i == b) && (req_addr[i] == served_addr))
ack[i] = 1'b1;
end
end
end
end
endmodule