Closes the flash #1 RTL gap flagged in DEC-0041: real, unmodified V1 subsystem (flash_slot_manager.v/flash_copy_engine.v/spi_flash_master.v/ crc32.v) now instantiated in fpga_neural_v2_top.v, bridged to the AR memory bus via a new flash_mem_adapter.v (byte<->word, matches nms_memory_manager_stream_wide.v's own real masking convention), and commandable over SPI via a new spi_host_bridge.v opcode (OP_FLASH_CMD, 0x30) using the same byte-counting idiom as OP_WRITE_JOB. Real balls now in the LPF: flash_sclk=B2, flash_mosi=E2, flash_miso=F2, flash_cs_n=F3. New tb_flash_integration_smoke.v: real SPI-triggered OP_FLASH_READ_BLOCK verified bit-exact (64/64 bytes) against a real V1 flash_model.v instance, through the new adapter and the widened (2->3 port) host-arb arbiter; WRITE_JOB regression confirms the new 3rd port doesn't disturb existing traffic. Full existing regression re-run clean: D-Stress N=4/ N=8 (bit-exact + data_ready PASS), board-level smoke test (11/11), isolated spi_host_bridge test (18/18). Honest, disclosed finding: a full 8-seed P&R re-verification shows N_SLOTS=4 @ 64MHz regressed from 8/8 to 3/8 PASS (worst 60.18MHz). Root cause traced via the real critical-path report: the SAME pre-existing arbiter-to-sdram-backend bottleneck already documented all session, made worse by flash's added die-area placement pressure -- not a new path through the flash logic itself. N_SLOTS=8 essentially unchanged (6/8, was 5/8). See decisions.log DEC-0042 for full detail and open decision points. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
85 lines
3.2 KiB
Verilog
85 lines
3.2 KiB
Verilog
`timescale 1ns/1ps
|
|
|
|
// ================================================================
|
|
// FLASH_MEM_ADAPTER -- bridges flash_slot_manager.v's real, unmodified
|
|
// V1 "Port D" (PSRAM-style byte interface: d_req/d_wr/d_addr/d_wdata/
|
|
// d_rdata/d_ready, byte-addressed, 8-bit signed data) to V2's real AR-
|
|
// port convention (word address, 16-bit data, lb_n/ub_n byte lane
|
|
// masking) used by slot_mem_arbiter.v's clients.
|
|
//
|
|
// Byte<->word convention matches nms_memory_manager_stream_wide.v's
|
|
// own real, already-verified result-writeback logic EXACTLY (not
|
|
// invented): word_addr = byte_addr[ADDR_WIDTH-1:1], byte_addr[0]==0
|
|
// selects the LOWER lane (lb_n=0,ub_n=1), byte_addr[0]==1 selects the
|
|
// UPPER lane (lb_n=1,ub_n=0); write data is replicated to both halves
|
|
// of the 16-bit word, the mask picks which half the SDRAM controller
|
|
// actually writes.
|
|
//
|
|
// Simple valid/ready passthrough: request held (s_req) from issue
|
|
// until the arbiter/backend returns s_ready, matching the same
|
|
// "hold, don't pulse" idiom already used throughout this project
|
|
// (nms_memory_manager_stream_wide.v, spi_host_bridge.v's own mem_req).
|
|
// ================================================================
|
|
module flash_mem_adapter #(
|
|
parameter ADDR_WIDTH = 26, // AR-side word-address bus width
|
|
parameter BYTE_ADDR_WIDTH = 26 // flash_slot_manager's own PSRAM_ADDR_WIDTH
|
|
)(
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
// ---- flash_slot_manager's own real "Port D" ----
|
|
input wire d_req,
|
|
input wire d_wr,
|
|
input wire [BYTE_ADDR_WIDTH-1:0] d_addr,
|
|
input wire signed [7:0] d_wdata,
|
|
output reg signed [7:0] d_rdata,
|
|
output reg d_ready,
|
|
|
|
// ---- AR-port-style client, into slot_mem_arbiter.v ----
|
|
output reg s_req,
|
|
output reg s_wr,
|
|
output reg [ADDR_WIDTH-1:0] s_addr,
|
|
output reg [15:0] s_wdata,
|
|
output reg s_lb_n,
|
|
output reg s_ub_n,
|
|
input wire [15:0] s_rdata,
|
|
input wire s_ready
|
|
);
|
|
|
|
reg pending;
|
|
reg lane;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
s_req <= 1'b0;
|
|
s_wr <= 1'b0;
|
|
s_addr <= {ADDR_WIDTH{1'b0}};
|
|
s_wdata <= 16'h0;
|
|
s_lb_n <= 1'b1;
|
|
s_ub_n <= 1'b1;
|
|
d_ready <= 1'b0;
|
|
d_rdata <= 8'sd0;
|
|
pending <= 1'b0;
|
|
lane <= 1'b0;
|
|
end else begin
|
|
d_ready <= 1'b0;
|
|
if (!pending && d_req) begin
|
|
s_req <= 1'b1;
|
|
s_wr <= d_wr;
|
|
s_addr <= d_addr[BYTE_ADDR_WIDTH-1:1];
|
|
lane <= d_addr[0];
|
|
s_wdata <= d_addr[0] ? {d_wdata, 8'h00} : {8'h00, d_wdata};
|
|
s_lb_n <= d_addr[0] ? 1'b1 : 1'b0;
|
|
s_ub_n <= d_addr[0] ? 1'b0 : 1'b1;
|
|
pending <= 1'b1;
|
|
end else if (pending && s_ready) begin
|
|
s_req <= 1'b0;
|
|
d_rdata <= lane ? s_rdata[15:8] : s_rdata[7:0];
|
|
d_ready <= 1'b1;
|
|
pending <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|