`timescale 1ns/1ps // ============================================================ // V3 -- REAL synthesis/P&R top for the physically-interfaced N=2 // system: real DDR3 (via the public mig_7series_0 wrapper, NOT the // sim-only _mig inner module used by this project's testbenches -- // the public wrapper always runs real calibration, SIM_BYPASS_INIT_ // CAL is not exposed/forced here, matching real board behavior) + // spi_host_bridge_v3.v (EXP-0072) as the physical host interface + // host_mem_bridge.v (EXP-0071) as a 3rd arbiter requester, giving the // host a real raw-DDR3-access path alongside the 2 compute slots. // // Everything downstream of the MIG (adapter, arbiter, Director, both // 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_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 // XDC constraints -- this module plus mig_7series_0.xdc together are // meant to be built with the genuine `vivado -mode batch` synth+impl // flow for a real, board-accurate Fmax signoff (the user's own // explicit request: "un timing reale... un confronto affidabile e // veritiero"). // // Activation stand-in ports (see packed_slot.v's own header) remain a // disclosed, separate gap -- no real activation-fetch engine exists // yet, so both slots' act_tile_* ports are still exposed at the top // level rather than connected to anything internal. // ============================================================ module n2_system_ddr3_top #( parameter DATA_WIDTH = 8, parameter P_IN = 8, parameter ACC_WIDTH = 32, parameter BURST_LEN = 8, parameter JOB_ADDR_WIDTH = 26, // Director/packed_slot byte-base-address convention parameter MEM_ADDR_WIDTH = 25, // arbiter/adapter word/burst-address convention parameter LAYER_BYTES = 128, parameter N_SLOTS = 2, parameter QUEUE_DEPTH = 8 )( // ---- 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_p, input wire clk_ref_n, // ---- 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, output wire ddr3_cas_n, output wire ddr3_we_n, output wire ddr3_reset_n, output wire [0:0] ddr3_ck_p, output wire [0:0] ddr3_ck_n, output wire [0:0] ddr3_cke, output wire [0:0] ddr3_cs_n, output wire [3:0] ddr3_dm, output wire [0:0] ddr3_odt, // ---- physical SPI host interface (-> spi_host_bridge_v3.v) ---- input wire sclk, input wire mosi, output wire miso, input wire cs_n, // ---- config-flash passthrough physical pins (this project's own // board pins D00_MOSI=K17/D01_DIN=K18/FCS_B=L13, reclaimed as // ordinary fabric I/O post-configuration -- see flash_spi_master.v's // own header for the real Xilinx PERSIST/STARTUPE2 requirements // this depends on). CCLK is NOT a port here -- flash_spi_master.v // drives it internally via STARTUPE2, a dedicated pin that can // never be an ordinary top-level port. ---- output wire flash_cs_n, output wire flash_mosi, input wire flash_miso, // ---- results: EXP-0088 REMOVES the literal per-slot top-level // result pins that used to live here (s0_result_data_a/b, // s1_result_data_a/b) -- they were flagged (docs/ARCHITECTURE_ // ANALYSIS.md S4.6/S5.3) as the exact same class of scaling mistake // already caught once for activation data (EXP-0074): fine at N=2 // (4 pins), a hard blocker at N=16 (8 bits x 2 lanes x 16 cores = // 256 pins on this port alone). Each packed_slot.v instance now // writes its own result directly into DDR3 via its own internal // result_writeback.v (see packed_slot.v's own header) -- the host // reads results back via the already-existing READ_MEM (0x02) SPI // opcode, no new top-level port needed at any N. // ---- status ---- output wire ui_clk_o, output wire init_calib_complete, output wire job_out_done, output wire [$clog2(N_SLOTS)-1:0] job_out_slot, // ---- host notification (real feature, user-requested, EXP-0084): // active-low, sticky IRQ so the ESP32 can be interrupt-driven // instead of polling STATUS. See spi_host_bridge_v3.v's own header // for the real set/clear semantics. ---- output wire data_ready_n ); 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; // real DDR3 memory controller -- public wrapper (always runs real // calibration; the SIM_BYPASS_INIT_CAL override this project's // testbenches use is only exposed on the inner _mig module, never // instantiated here). mig_7series_0 u_mig ( .ddr3_dq(ddr3_dq), .ddr3_dqs_n(ddr3_dqs_n), .ddr3_dqs_p(ddr3_dqs_p), .ddr3_addr(ddr3_addr), .ddr3_ba(ddr3_ba), .ddr3_ras_n(ddr3_ras_n), .ddr3_cas_n(ddr3_cas_n), .ddr3_we_n(ddr3_we_n), .ddr3_reset_n(ddr3_reset_n), .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_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), .app_rd_data(app_rd_data), .app_rd_data_end(app_rd_data_end), .app_rd_data_valid(app_rd_data_valid), .app_rdy(app_rdy), .app_wdf_rdy(app_wdf_rdy), .app_sr_req(1'b0), .app_ref_req(1'b0), .app_zq_req(1'b0), .app_sr_active(), .app_ref_ack(), .app_zq_ack(), .ui_clk(ui_clk), .ui_clk_sync_rst(ui_clk_sync_rst), .init_calib_complete(init_calib_complete), .device_temp(), .sys_rst(sys_rst) ); wire adp_req, adp_wr; wire [MEM_ADDR_WIDTH-1:0] adp_addr; 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 ( .clk(ui_clk), .rst(ui_clk_sync_rst), .req(adp_req), .wr(adp_wr), .addr(adp_addr), .wdata(adp_wdata), .wmask(adp_wmask), .rdata(adp_rdata), .ready(adp_ready), .busy(adp_busy), .app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en), .app_rdy(app_rdy), .app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end), .app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren), .app_wdf_rdy(app_wdf_rdy), .app_rd_data(app_rd_data), .app_rd_data_end(app_rd_data_end), .app_rd_data_valid(app_rd_data_valid) ); // ---- 3-way arbiter: slot0, slot1, host_mem_bridge (SPI raw access) ---- localparam NUM_REQ = 3; 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*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) ) u_arb ( .clk(ui_clk), .rst(ui_clk_sync_rst), .req_active(req_active), .req_grant(req_grant), .req_req(req_req), .req_wr(req_wr), .req_addr(req_addr), .req_wdata(req_wdata), .req_wmask(req_wmask), .req_rdata(req_rdata), .req_ready(req_ready), .req_busy(req_busy), .ctrl_req(adp_req), .ctrl_wr(adp_wr), .ctrl_addr(adp_addr), .ctrl_wdata(adp_wdata), .ctrl_wmask(adp_wmask), .ctrl_rdata(adp_rdata), .ctrl_ready(adp_ready), .ctrl_busy(adp_busy) ); // ---- Director + job submission (fed directly by the SPI bridge, same clock domain) ---- wire job_in_valid, job_in_ready; wire [JOB_ADDR_WIDTH-1:0] job_in_x_base, job_in_w_base, job_in_result_addr; wire [15:0] job_in_n_tiles, job_in_node_id; wire [N_SLOTS-1:0] slot_job_start; wire [JOB_ADDR_WIDTH*N_SLOTS-1:0] slot_x_base_a, slot_x_base_b, slot_w_base; wire [JOB_ADDR_WIDTH*N_SLOTS-1:0] slot_result_addr_a, slot_result_addr_b; wire [16*N_SLOTS-1:0] slot_n_tiles, slot_node_id_a, slot_node_id_b; wire [N_SLOTS-1:0] slot_job_done; wire [3:0] dir_state; wire dir_error; wire queue_empty; neural_director_packed #( .ADDR_WIDTH(JOB_ADDR_WIDTH), .N_SLOTS(N_SLOTS), .QUEUE_DEPTH(QUEUE_DEPTH) ) u_dir ( .clk(ui_clk), .rst(ui_clk_sync_rst), .job_in_valid(job_in_valid), .job_in_ready(job_in_ready), .job_in_x_base(job_in_x_base), .job_in_w_base(job_in_w_base), .job_in_n_tiles(job_in_n_tiles), .job_in_result_addr(job_in_result_addr), .job_in_node_id(job_in_node_id), .slot_job_start(slot_job_start), .slot_x_base_a(slot_x_base_a), .slot_x_base_b(slot_x_base_b), .slot_w_base(slot_w_base), .slot_n_tiles(slot_n_tiles), .slot_result_addr_a(slot_result_addr_a), .slot_result_addr_b(slot_result_addr_b), .slot_node_id_a(slot_node_id_a), .slot_node_id_b(slot_node_id_b), .slot_job_done(slot_job_done), .job_out_done(job_out_done), .job_out_slot(job_out_slot), .dir_state(dir_state), .dir_error(dir_error), .queue_empty(queue_empty) ); // ---- physical SPI host interface: submits jobs to the Director, // and drives req[2] (host_mem_bridge.v) for raw DDR3 access ---- wire mem_req, mem_wr, mem_lb_n, mem_ub_n, mem_ready; wire [MEM_ADDR_WIDTH-1:0] mem_addr; wire [15:0] mem_wdata, mem_rdata; wire soft_rst_pulse; wire flash_xfer_active, flash_byte_req, flash_byte_done; wire [7:0] flash_byte_wdata, flash_byte_rdata; spi_host_bridge_v3 #( .JOB_ADDR_WIDTH(JOB_ADDR_WIDTH), .MEM_ADDR_WIDTH(MEM_ADDR_WIDTH), .N_SLOTS(N_SLOTS) ) u_spi ( .clk(ui_clk), .rst(ui_clk_sync_rst), .sclk(sclk), .mosi(mosi), .miso(miso), .cs_n(cs_n), .init_calib_complete(init_calib_complete), .dir_error(dir_error), .job_out_done(job_out_done), .data_ready_n(data_ready_n), .job_in_valid(job_in_valid), .job_in_ready(job_in_ready), .job_in_x_base(job_in_x_base), .job_in_w_base(job_in_w_base), .job_in_n_tiles(job_in_n_tiles), .job_in_result_addr(job_in_result_addr), .job_in_node_id(job_in_node_id), .mem_req(mem_req), .mem_wr(mem_wr), .mem_addr(mem_addr), .mem_wdata(mem_wdata), .mem_lb_n(mem_lb_n), .mem_ub_n(mem_ub_n), .mem_rdata(mem_rdata), .mem_ready(mem_ready), .flash_xfer_active(flash_xfer_active), .flash_byte_req(flash_byte_req), .flash_byte_wdata(flash_byte_wdata), .flash_byte_rdata(flash_byte_rdata), .flash_byte_done(flash_byte_done), .soft_rst_pulse(soft_rst_pulse) ); flash_spi_master u_flash ( .clk(ui_clk), .rst(ui_clk_sync_rst), .xfer_active(flash_xfer_active), .byte_req(flash_byte_req), .byte_wdata(flash_byte_wdata), .byte_rdata(flash_byte_rdata), .byte_done(flash_byte_done), .busy(), .flash_cs_n(flash_cs_n), .flash_mosi(flash_mosi), .flash_miso(flash_miso) ); host_mem_bridge #( .BURST_LEN(BURST_LEN), .ADDR_WIDTH(MEM_ADDR_WIDTH) ) u_host_bridge ( .clk(ui_clk), .rst(ui_clk_sync_rst), .mem_req(mem_req), .mem_wr(mem_wr), .mem_addr(mem_addr), .mem_wdata(mem_wdata), .mem_lb_n(mem_lb_n), .mem_ub_n(mem_ub_n), .mem_rdata(mem_rdata), .mem_ready(mem_ready), .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*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]) ); wire [15:0] s0_nid_a, s0_nid_b, s1_nid_a, s1_nid_b; wire [JOB_ADDR_WIDTH-1:0] s0_raddr_a, s0_raddr_b, s1_raddr_a, s1_raddr_b; // EXP-0088: plain internal debug wires now (no longer top-level // pins) -- the real result is written to DDR3 by each slot's own // internal result_writeback.v; these remain wired from packed_ // slot.v's own output ports purely for internal observability. wire signed [DATA_WIDTH-1:0] s0_result_data_a, s0_result_data_b; wire signed [DATA_WIDTH-1:0] s1_result_data_a, s1_result_data_b; // ---- activation fetch: REAL now (EXP-0079) -- each packed_slot // instance owns its own act_tile_fetch.v internally, sharing that // SAME slot's existing ctrl_req/addr/etc port (already wired to // the arbiter below) with its own weight-prefetch engine. No // top-level activation ports exist any more -- the old stand-in // (act_tile_addr_a/b -> act_tile_data_a/b, and before that, a // free-running counter stub that nearly blew the package's whole // I/O budget, see git history) is gone; this is fully internal. packed_slot #( .DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ACC_WIDTH(ACC_WIDTH), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(JOB_ADDR_WIDTH), .LAYER_BYTES(LAYER_BYTES) ) u_slot0 ( .clk(ui_clk), .rst(ui_clk_sync_rst), .job_start(slot_job_start[0]), .x_base_a(slot_x_base_a[0*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .x_base_b(slot_x_base_b[0*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .w_base(slot_w_base[0*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .n_tiles(slot_n_tiles[0*16 +: 16]), .result_addr_a(slot_result_addr_a[0*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .result_addr_b(slot_result_addr_b[0*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .node_id_a(slot_node_id_a[0*16 +: 16]), .node_id_b(slot_node_id_b[0*16 +: 16]), .job_done(slot_job_done[0]), .result_data_a(s0_result_data_a), .result_data_b(s0_result_data_b), .result_node_id_a(s0_nid_a), .result_node_id_b(s0_nid_b), .result_addr_a_out(s0_raddr_a), .result_addr_b_out(s0_raddr_b), .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*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]) ); packed_slot #( .DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ACC_WIDTH(ACC_WIDTH), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(JOB_ADDR_WIDTH), .LAYER_BYTES(LAYER_BYTES) ) u_slot1 ( .clk(ui_clk), .rst(ui_clk_sync_rst), .job_start(slot_job_start[1]), .x_base_a(slot_x_base_a[1*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .x_base_b(slot_x_base_b[1*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .w_base(slot_w_base[1*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .n_tiles(slot_n_tiles[1*16 +: 16]), .result_addr_a(slot_result_addr_a[1*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .result_addr_b(slot_result_addr_b[1*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .node_id_a(slot_node_id_a[1*16 +: 16]), .node_id_b(slot_node_id_b[1*16 +: 16]), .job_done(slot_job_done[1]), .result_data_a(s1_result_data_a), .result_data_b(s1_result_data_b), .result_node_id_a(s1_nid_a), .result_node_id_b(s1_nid_b), .result_addr_a_out(s1_raddr_a), .result_addr_b_out(s1_raddr_b), .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*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