`timescale 1ns/1ps // ============================================================ // V3 -- REAL synthesis/P&R top for the N=16 (4 groups x 4 PEs) hybrid // systolic system (EXP-0089/0090/0091), directly adapted from // n2_system_ddr3_top.v's own real, proven structure -- same MIG/ // adapter, same host SPI bridge, same flash bridge, same host_mem_ // bridge.v raw-access path, ALL UNCHANGED. The only real differences // from n2_system_ddr3_top.v: neural_director_grouped.v replaces // neural_director_packed.v, 4x systolic_group.v replace 2x // packed_slot.v, and the shared arbiter grows from NUM_REQ=3 to // NUM_REQ=21 (4 groups' own weight-fetch requesters + 16 PEs' own // activation-fetch/writeback requesters + host_mem_bridge.v). // // REAL, CONFIRMED FINDING (EXP-0090): the SPI/WRITE_JOB host protocol // needs ZERO changes for this -- spi_host_bridge_v3.v is instantiated // completely unmodified below. It never references job_out_slot (only // the simple job_out_done completion pulse), so neural_director_ // grouped.v's own job_out_group output is left unconnected at this // top level (real, disclosed: a future real status register could // expose it, not needed for this real milestone). N_SLOTS is passed // as 16 (the real, total parallel-PE count) purely for the host's own // informational REG_READ(0x03) -- N_SLOTS never gates any real control // logic in spi_host_bridge_v3.v. // // ARBITER SLOT MAP (real, NUM_REQ=21): slots 0-3 = groups 0-3's own // weight-fetch; slots 4-19 = the 16 PEs' own activation-fetch+ // writeback, 4 consecutive slots per group (group g's PEs at slots // 4+4g .. 4+4g+3); slot 20 = host_mem_bridge.v. // ============================================================ module n16_system_ddr3_top #( parameter DATA_WIDTH = 8, parameter P_IN = 8, parameter ACC_WIDTH = 32, parameter BURST_LEN = 8, parameter JOB_ADDR_WIDTH = 26, parameter MEM_ADDR_WIDTH = 25, parameter LAYER_BYTES = 128, parameter N_GROUPS = 4, parameter QUEUE_DEPTH = 16 )( input wire sys_clk_p, input wire sys_clk_n, input wire sys_rst, input wire clk_ref_p, input wire clk_ref_n, 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, input wire sclk, input wire mosi, output wire miso, input wire cs_n, output wire flash_cs_n, output wire flash_mosi, input wire flash_miso, output wire ui_clk_o, output wire init_calib_complete, output wire job_out_done, output wire data_ready_n ); localparam N_PES = N_GROUPS * 4; // 16 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; 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) ); // ---- real, real, appropriately-sized arbiter: 4 group weight- // fetch + 16 PE activation/writeback + 1 host_mem_bridge = 21 ---- localparam NUM_REQ = N_GROUPS + N_PES + 1; // 4 + 16 + 1 = 21 localparam HOST_SLOT = NUM_REQ - 1; // 20 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; // EXP-0094: real, hierarchical 2-level arbiter (fixes EXP-0093's own // real, measured P&R timing failure on the flat 21-way version -- // see sdram_arbiter_hier.v's own header). Drop-in replacement: same // real external port shape/NUM_REQ/slot-index convention this // module's own surrounding req_active/req_grant/etc wiring already // uses, no other change needed here. sdram_arbiter_hier #( .N_GROUPS(N_GROUPS), .PES_PER_GROUP(4), .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) ); // ---- grouped Director ---- 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_GROUPS-1:0] group_job_start; wire [JOB_ADDR_WIDTH*N_GROUPS-1:0] group_w_base; wire [16*N_GROUPS-1:0] group_n_tiles; wire [4*JOB_ADDR_WIDTH*N_GROUPS-1:0] group_pe_x_base_a, group_pe_x_base_b; wire [4*JOB_ADDR_WIDTH*N_GROUPS-1:0] group_pe_result_addr_a, group_pe_result_addr_b; wire [4*16*N_GROUPS-1:0] group_pe_node_id_a, group_pe_node_id_b; wire [N_GROUPS-1:0] group_job_done; // real fix (N_GROUPS=1 real N=4 scaling test): matches neural_ // director_grouped.v's own real GROUP_IDX_WIDTH guard exactly -- // bare $clog2(N_GROUPS) is 0 (invalid [-1:0]) for N_GROUPS=1. localparam GROUP_IDX_WIDTH = (N_GROUPS <= 1) ? 1 : $clog2(N_GROUPS); wire [GROUP_IDX_WIDTH-1:0] job_out_group_w; wire [3:0] dir_state; wire dir_error; wire queue_empty; neural_director_grouped #( .ADDR_WIDTH(JOB_ADDR_WIDTH), .N_GROUPS(N_GROUPS), .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), .group_job_start(group_job_start), .group_w_base(group_w_base), .group_n_tiles(group_n_tiles), .group_pe_x_base_a(group_pe_x_base_a), .group_pe_x_base_b(group_pe_x_base_b), .group_pe_result_addr_a(group_pe_result_addr_a), .group_pe_result_addr_b(group_pe_result_addr_b), .group_pe_node_id_a(group_pe_node_id_a), .group_pe_node_id_b(group_pe_node_id_b), .group_job_done(group_job_done), .job_out_done(job_out_done), .job_out_group(job_out_group_w), .dir_state(dir_state), .dir_error(dir_error), .queue_empty(queue_empty) ); // ---- physical SPI host interface: UNMODIFIED (EXP-0090's own real // finding -- zero protocol changes needed). N_SLOTS=16 reported // purely informationally (REG_READ 0x03), matches the real total // parallel-PE count, never gates any control logic in this module. ---- 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_PES) ) 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[HOST_SLOT]), .req_grant(req_grant[HOST_SLOT]), .req_req(req_req[HOST_SLOT]), .req_wr(req_wr[HOST_SLOT]), .req_addr(req_addr[HOST_SLOT*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]), .req_wdata(req_wdata[HOST_SLOT*32*BURST_LEN +: 32*BURST_LEN]), .req_wmask(req_wmask[HOST_SLOT*4*BURST_LEN +: 4*BURST_LEN]), .req_rdata(req_rdata[HOST_SLOT*32*BURST_LEN +: 32*BURST_LEN]), .req_ready(req_ready[HOST_SLOT]), .req_busy(req_busy[HOST_SLOT]) ); // ---- 4x systolic_group.v, each: 1 arbiter slot for its own // weight-fetch (slots 0..N_GROUPS-1), 4 arbiter slots for its own // 4 PEs' activation-fetch+writeback (slots N_GROUPS+4g .. // N_GROUPS+4g+3) ---- genvar gg; generate for (gg = 0; gg < N_GROUPS; gg = gg + 1) begin : GEN_GROUP localparam PE_BASE = N_GROUPS + gg*4; systolic_group #( .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_group ( .clk(ui_clk), .rst(ui_clk_sync_rst), .job_start(group_job_start[gg]), .w_base(group_w_base[gg*JOB_ADDR_WIDTH +: JOB_ADDR_WIDTH]), .n_tiles(group_n_tiles[gg*16 +: 16]), .pe_x_base_a(group_pe_x_base_a[gg*4*JOB_ADDR_WIDTH +: 4*JOB_ADDR_WIDTH]), .pe_x_base_b(group_pe_x_base_b[gg*4*JOB_ADDR_WIDTH +: 4*JOB_ADDR_WIDTH]), .pe_result_addr_a(group_pe_result_addr_a[gg*4*JOB_ADDR_WIDTH +: 4*JOB_ADDR_WIDTH]), .pe_result_addr_b(group_pe_result_addr_b[gg*4*JOB_ADDR_WIDTH +: 4*JOB_ADDR_WIDTH]), .pe_node_id_a(group_pe_node_id_a[gg*4*16 +: 4*16]), .pe_node_id_b(group_pe_node_id_b[gg*4*16 +: 4*16]), .job_done(group_job_done[gg]), .pe_result_data_a(), .pe_result_data_b(), .pe_result_node_id_a(), .pe_result_node_id_b(), .pe_result_addr_a_out(), .pe_result_addr_b_out(), .mem_active(req_active[gg]), .mem_grant(req_grant[gg]), .ctrl_req(req_req[gg]), .ctrl_wr(req_wr[gg]), .ctrl_addr(req_addr[gg*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]), .ctrl_wdata(req_wdata[gg*32*BURST_LEN +: 32*BURST_LEN]), .ctrl_wmask(req_wmask[gg*4*BURST_LEN +: 4*BURST_LEN]), .ctrl_rdata(req_rdata[gg*32*BURST_LEN +: 32*BURST_LEN]), .ctrl_ready(req_ready[gg]), .ctrl_busy(req_busy[gg]), .pe_mem_active(req_active[PE_BASE +: 4]), .pe_mem_grant(req_grant[PE_BASE +: 4]), .pe_ctrl_req(req_req[PE_BASE +: 4]), .pe_ctrl_wr(req_wr[PE_BASE +: 4]), .pe_ctrl_addr(req_addr[PE_BASE*MEM_ADDR_WIDTH +: 4*MEM_ADDR_WIDTH]), .pe_ctrl_wdata(req_wdata[PE_BASE*32*BURST_LEN +: 4*32*BURST_LEN]), .pe_ctrl_wmask(req_wmask[PE_BASE*4*BURST_LEN +: 4*4*BURST_LEN]), .pe_ctrl_rdata(req_rdata[PE_BASE*32*BURST_LEN +: 4*32*BURST_LEN]), .pe_ctrl_ready(req_ready[PE_BASE +: 4]), .pe_ctrl_busy(req_busy[PE_BASE +: 4]) ); end endgenerate endmodule