diff --git a/CLAUDE.md b/CLAUDE.md index 45b4743..664451e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,17 @@ unmodified by v3, e.g. `layer_prefetch_ctrl.v`/`layer_weight_buffer.v`). [get_filesets sources_1]` and `constrs_1`) for `*/imports/*` paths before trusting the next P&R — don't assume only the previously-fixed files are still direct references. +- **Adding a brand-new TOP-LEVEL module** (not just a sub-module) to the + project: `add_files` + `update_compile_order` alone did NOT make + `synth_design -top ` find it (EXP-0091) — failed with + "module '' not found" even though the file was correctly + present, `IS_ENABLED`, and `USED_IN: synthesis`. Before assuming an RTL + bug, verify the RTL independently first (a clean Icarus elaboration + with small stub modules for any real Xilinx primitives it can't resolve, + e.g. `mig_7series_0`/`STARTUPE2`, is enough to rule that out cheaply). + Real fix: explicitly `set_property top [get_filesets + sources_1]` *before* calling `synth_design -top ...` — the `-top` + command-line flag alone wasn't sufficient this time. - **Testbench stimulus must use nonblocking assignment (`<=`), not blocking (`=`), when driving a DUT's inputs from a separate `always`/`initial` block.** Blocking assignment races the DUT's own `posedge`-triggered diff --git a/docs/ARCHITECTURE_ANALYSIS.md b/docs/ARCHITECTURE_ANALYSIS.md index b3ca2e7..ba09f2b 100644 --- a/docs/ARCHITECTURE_ANALYSIS.md +++ b/docs/ARCHITECTURE_ANALYSIS.md @@ -659,7 +659,7 @@ specifically to document where/how it breaks rather than to succeed): --- -### 5.6 [Steps 1–2 DONE, EXP-0089/0090] Hybrid systolic scaling: 4 groups × 4-PE weight-stationary chains +### 5.6 [Steps 1–3 DONE, EXP-0089/0090/0091] Hybrid systolic scaling: 4 groups × 4-PE weight-stationary chains Captured from a 2026-09-20 brainstorming session as a purely exploratory idea; the same day, per the user's own explicit reprioritization, the real @@ -671,8 +671,7 @@ systolic shift register) was resolved with the user directly (not guessed). xsim-verified (8/8 PASS across 2 consecutive group jobs). - Real out-of-context synthesis of one group (EXP-0090): **32 DSP48E1** (13.3%), confirming the original brainstorm's own quantified DSP - projection exactly (8 DSP/PE × 4 PEs = 32; scaled to 4 groups, 128/240 = - 53%, matching the doc's own earlier estimate). + projection exactly (8 DSP/PE × 4 PEs = 32). - `neural_director_grouped.v` (EXP-0090) — real Director extension dispatching 8-position octets to free groups, a direct extension of `neural_director_packed.v`'s own already-proven 2-position pairing @@ -684,10 +683,25 @@ systolic shift register) was resolved with the user directly (not guessed). surfaced a real, generalizable testbench-race lesson (tight back-to- back stimulus pulses on the same edge the DUT samples on — fixed with `@(negedge clk)` stimulus — see CLAUDE.md). +- `n16_system_ddr3_top.v` (EXP-0091) — real N=16 top-level, directly + adapted from `n2_system_ddr3_top.v`'s own proven structure (same real + MIG, `spi_host_bridge_v3.v`, `flash_spi_master.v`, `host_mem_bridge.v`, + all completely unmodified — confirms the zero-protocol-change finding + above holds at full scale too), 4× `systolic_group.v` + `neural_ + director_grouped.v` + a new 21-way arbiter (4 group weight-fetch + 16 + PE activation/writeback + 1 host_mem_bridge). **Real synthesis-only + result: 0 Errors, 128 DSP48E1/240 (53.33%)** — an exact real match to + this section's own original DSP projection, now confirmed by real + synthesis rather than estimated. -**Not yet done**: a real N=16 (4-group) top-level module + a real, -appropriately-sized arbiter, and real in-context P&R for the whole system -— see EXP-0090's own `next_action`. +**Not yet done, real and disclosed**: this top-level has only been +checked for **synthesis-level connectivity**, NOT yet for **functional +correctness** (a real xsim test against the real DDR3 model, exercising +all 4 groups, is the next real step — the same class of bus-slicing bug +already found and fixed twice this session in similar flattened-bus +contexts could still be lurking here undetected by synthesis alone) and +NOT yet for **real timing** (no place_design/route_design run yet) — see +EXP-0091's own `next_action`. **The problem it targets**: plain N=16 independent cores (§5.5's own "documentary, expected to break" framing) means 16 independent DDR3 diff --git a/hardware/v2/logs/experiments.log b/hardware/v2/logs/experiments.log index 62a68ee..39c89e3 100644 --- a/hardware/v2/logs/experiments.log +++ b/hardware/v2/logs/experiments.log @@ -6171,3 +6171,72 @@ spi_host_bridge_v3.v (no changes needed, per Part 2's own real finding) next_action: build the real N=16 top-level, verify it end-to-end (real xsim against the real DDR3 model, matching this project's own established multi-level verification discipline), then real P&R. + +EXP-0091 -- real N=16 (4x4 hybrid systolic) top-level: synthesis-only +check PASSES, real utilization confirms the original DSP projection +exactly (2026-09-21, continuing "Ok procedi ad implementare quel che +manca") + +CONTEXT: EXP-0089 (systolic_group.v/packed_pe.v) and EXP-0090 +(neural_director_grouped.v) built and separately verified the two new +real subsystems this architecture needs. The remaining real gap was a +top-level module actually wiring everything together at N=16 scale. + +NEW MODULE: `n16_system_ddr3_top.v`, directly adapted from n2_system_ +ddr3_top.v's own real, proven structure -- same real MIG public +wrapper, same spi_host_bridge_v3.v, flash_spi_master.v, host_mem_ +bridge.v, ALL instantiated completely unmodified (confirms EXP-0090's +own real finding: the host SPI/WRITE_JOB protocol needs zero changes +at N=16). The only real differences: 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 at slots 0-3, the 16 +PEs' own activation-fetch+writeback requesters at slots 4-19 -- 4 +consecutive slots per group -- and host_mem_bridge.v at slot 20). +N_SLOTS=16 (the real total parallel-PE count) is passed to spi_host_ +bridge_v3.v purely for its own informational REG_READ(0x03) -- that +parameter never gates any real control logic there. + +REAL VIVADO PROJECT QUIRK FOUND (not an RTL bug -- confirmed +separately): adding these 4 new files via `add_files` + `update_ +compile_order` and then calling `synth_design -top n16_system_ddr3_ +top` failed immediately with "module 'n16_system_ddr3_top' not found", +despite the file being correctly present, enabled, and marked +`USED_IN: synthesis` in the project. Before assuming an RTL bug, +verified the RTL itself independently: a clean Icarus elaboration of +n16_system_ddr3_top.v and every real dependency (using two small stub +modules for the real Xilinx primitives Icarus can't resolve on its +own, mig_7series_0 and STARTUPE2) completed with **0 errors** -- +proving the RTL was correct all along and the issue was Vivado's own +project state. Fix: explicitly `set_property top n16_system_ddr3_top +$fileset` BEFORE calling `synth_design -top ...` (rather than relying +on the `-top` command-line override alone) -- real, reproducible fix, +now the confirmed real procedure for adding any brand-new top-level +module to this project going forward. + +REAL RESULT: **synth_design completed successfully, 0 Errors, 0 +Critical Warnings, 108 Warnings** (all real and expected -- e.g. +result_writeback.v's own `ctrl_rdata` being unused, since that module +is write-only by design, already known/expected since EXP-0088, not +new). Real utilization: **128 DSP48E1 / 240 (53.33%)** -- an EXACT +real match to docs/ARCHITECTURE_ANALYSIS.md S5.6's own original +brainstorm projection ("16 cores x 8 DSP/core = 128/240, 53%"), now +confirmed by real synthesis, not a projection any more. 20053 LUTs +(31.63%), real, healthy headroom remaining on this part. + +DECISION: real, verified third step. This is SYNTHESIS-ONLY -- +honestly disclosed, NOT yet a real P&R signoff (no place_design/ +route_design run, no real timing number for this design) and NOT yet +a real functional xsim test (the sub-modules are separately verified, +but this top-level's own bus-slicing/arbiter-wiring correctness -- +the same class of bug already found and fixed twice this session in +similar flattened-bus contexts -- has only been checked for +SYNTAX/CONNECTIVITY validity via synthesis succeeding, not for +FUNCTIONAL correctness). + +next_action: (1) a real functional xsim test (mirroring tb_n2_system_ +ddr3.v's own real-DDR3-model methodology, scaled to submit octets +across all 4 groups and verify all 16 real results) before trusting +this design at all -- synthesis succeeding proves connectivity, not +correctness. (2) real, full P&R (place_design + route_design) for a +real timing signoff, only after (1) passes. diff --git a/hardware/v3/rtl/n16_system_ddr3_top.v b/hardware/v3/rtl/n16_system_ddr3_top.v new file mode 100644 index 0000000..9989845 --- /dev/null +++ b/hardware/v3/rtl/n16_system_ddr3_top.v @@ -0,0 +1,291 @@ +`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; + + 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) + ); + + // ---- 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; + wire [$clog2(N_GROUPS)-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