From 94b63705be4f46227cb8963e3e6e59f249c556ed Mon Sep 17 00:00:00 2001 From: manvalan Date: Wed, 16 Sep 2026 23:29:54 +0200 Subject: [PATCH] exp: N=8 packed-core array real P&R, isolates placement density from interconnect (EXP-0060) New hardware/v3/rtl/np_packed_array.v: flat array of 8 unmodified neural_processor_packed.v instances, fully independent I/O, zero shared arbiter/Director logic -- isolates exactly one variable (DSP/placement density) from EXP-0059's single-core baseline. Real Vivado post-route: 64/240 DSP48E1 (26.67%), WNS -2.592ns @ 200MHz -> Fmax ~131.7MHz, only -2.4% vs the single-core 134.9MHz. Placement density alone is NOT the main driver of the ECP5-era gap between isolated-core and full-system Fmax -- narrows the question for the still-unbuilt real Director/arbiter/memory integration. Full writeup in hardware/v2/logs/experiments.log EXP-0060. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC --- hardware/v2/logs/experiments.log | 48 +++++++++++ hardware/v3/rtl/np_packed_array.v | 80 +++++++++++++++++++ .../v3/synth/synth_np_packed_array_n8.tcl | 11 +++ 3 files changed, 139 insertions(+) create mode 100644 hardware/v3/rtl/np_packed_array.v create mode 100644 hardware/v3/synth/synth_np_packed_array_n8.tcl diff --git a/hardware/v2/logs/experiments.log b/hardware/v2/logs/experiments.log index 88c305d..48885ff 100644 --- a/hardware/v2/logs/experiments.log +++ b/hardware/v2/logs/experiments.log @@ -3713,3 +3713,51 @@ actually targeted (part number confirmed xc7a100tcsg324-1, package/ board pinout not yet chosen) is still needed before any real bring-up, matching this project's own "no board target skipped" discipline from V2/STEP19. + +EXP-0060 -- N=8 neural_processor_packed.v placement/routing density +check, real Vivado P&R, isolating the congestion variable from any +Director/arbiter/memory RTL (2026-09-17) + +CONTEXT: EXP-0059's own next_action flagged that the isolated single- +core Fmax (134.9MHz real post-route) is not the same question as real +N-core system Fmax -- on V2/ECP5 the full board-level system Fmax +(64-97MHz) was measurably lower than the isolated dataflow-core Fmax +(92.63MHz). Before building the (larger, riskier, unverified) real +Director+arbiter+memory integration, this experiment isolates ONE +variable first, per this project's own "one variable at a time" rule: +what does pure DSP/placement DENSITY alone do to Fmax, with zero +shared interconnect logic between cores? + +METHOD: new `hardware/v3/rtl/np_packed_array.v`, N_CORES=8 flat array +of unmodified neural_processor_packed.v instances, each core's I/O fully +independent (flattened N*WIDTH buses, sliced per-instance, generate +block) -- deliberately NO arbiter/Director/shared bus, so any Fmax +change vs EXP-0059's single-core number is attributable ONLY to +placement/routing congestion from DSP/LUT/FF density, not to any new +(unverified) integration logic. Real Vivado 2026.1 out-of-context +synth + opt_design + place_design + route_design, same 200MHz +(-period 5.000) constraint and part (xc7a100tcsg324-1) as EXP-0059, +via `hardware/v3/synth/synth_np_packed_array_n8.tcl`. + +RESULT (real, post-route, not estimated): 64/240 DSP48E1 (26.67%, +exactly 8 cores x 8 DSP, matches EXP-0059's per-core count). WNS +-2.592ns @ 200MHz -> real critical path 7.592ns -> Fmax ~131.7MHz. +Vs EXP-0059's single-core 134.9MHz: a real but SMALL degradation, +-2.4%, from placement/routing density alone at 8 independent cores / +~27% DSP utilization. + +DECISION: the earlier V2/ECP5 gap between isolated-core and full- +system Fmax is NOT mostly explained by raw compute-array placement +density (this experiment's -2.4% is far smaller than ECP5's ~30%+ +isolated-vs-system gap) -- the real driver is most likely the shared +Director/arbiter/memory-path interconnect logic itself, not yet built +or tested here. This narrows, not answers, the open question from +EXP-0059 -- still no real system-level number exists. + +next_action: build the real Director + memory-feed path for the +packed 2-job-per-core model (neural_director.v needs real changes to +dispatch PAIRS of jobs per core, not a 1:1 port) as a properly +correctness-verified (isolated testbench, bit-exact vs golden model) +integration BEFORE the next real P&R congestion check -- do not +synthesize unverified integration RTL just to get another Fmax number, +per this project's own correctness-first standard. diff --git a/hardware/v3/rtl/np_packed_array.v b/hardware/v3/rtl/np_packed_array.v new file mode 100644 index 0000000..214d5aa --- /dev/null +++ b/hardware/v3/rtl/np_packed_array.v @@ -0,0 +1,80 @@ +`timescale 1ns/1ps + +// ============================================================ +// EXP-0059 follow-up -- N independent neural_processor_packed.v +// cores, flat array, NO Director/arbiter/memory path yet. +// +// PURPOSE: isolate exactly one variable -- what real P&R placement/ +// routing congestion does to Fmax once N copies of the DSP48-packed +// core sit side by side on XC7A100T -- before adding any new +// (unverified) integration RTL (Director, arbiter, memory path). +// Each core keeps its own independent, unshared I/O (flattened to +// N*WIDTH buses, sliced per-instance below); there is deliberately NO +// interconnect logic here to conflate with the placement-density +// question this experiment is asking. Matches this project's own +// "one variable at a time" convention (see decisions.log). +// ============================================================ +module np_packed_array #( + parameter DATA_WIDTH = 8, + parameter P_IN = 8, + parameter ACC_WIDTH = 32, + parameter N_CORES = 8 +)( + input wire clk, + input wire rst, + + input wire [N_CORES-1:0] job_valid, + output wire [N_CORES-1:0] job_ready, + input wire [N_CORES*16-1:0] job_node_id_a, + input wire [N_CORES*16-1:0] job_node_id_b, + input wire [N_CORES*DATA_WIDTH-1:0] job_bias, + input wire [N_CORES*2-1:0] job_activation, + + input wire [N_CORES-1:0] operand_valid, + output wire [N_CORES-1:0] operand_ready, + input wire [N_CORES*DATA_WIDTH*P_IN-1:0] input_data_a, + input wire [N_CORES*DATA_WIDTH*P_IN-1:0] input_data_b, + input wire [N_CORES*DATA_WIDTH*P_IN-1:0] weight_data, + input wire [N_CORES-1:0] tile_last, + + output wire [N_CORES-1:0] result_valid, + input wire [N_CORES-1:0] result_ready, + output wire [N_CORES*DATA_WIDTH-1:0] result_data_a, + output wire [N_CORES*DATA_WIDTH-1:0] result_data_b, + output wire [N_CORES*16-1:0] result_node_id_a, + output wire [N_CORES*16-1:0] result_node_id_b, + + output wire [N_CORES*4-1:0] np_state, + output wire [N_CORES-1:0] np_error +); + genvar gc; + generate + for (gc = 0; gc < N_CORES; gc = gc + 1) begin : GEN_CORE + neural_processor_packed #( + .DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ACC_WIDTH(ACC_WIDTH) + ) u_core ( + .clk(clk), .rst(rst), + .job_valid(job_valid[gc]), + .job_ready(job_ready[gc]), + .job_node_id_a(job_node_id_a[gc*16 +: 16]), + .job_node_id_b(job_node_id_b[gc*16 +: 16]), + .job_bias(job_bias[gc*DATA_WIDTH +: DATA_WIDTH]), + .job_activation(job_activation[gc*2 +: 2]), + .operand_valid(operand_valid[gc]), + .operand_ready(operand_ready[gc]), + .input_data_a(input_data_a[gc*DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]), + .input_data_b(input_data_b[gc*DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]), + .weight_data(weight_data[gc*DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]), + .tile_last(tile_last[gc]), + .result_valid(result_valid[gc]), + .result_ready(result_ready[gc]), + .result_data_a(result_data_a[gc*DATA_WIDTH +: DATA_WIDTH]), + .result_data_b(result_data_b[gc*DATA_WIDTH +: DATA_WIDTH]), + .result_node_id_a(result_node_id_a[gc*16 +: 16]), + .result_node_id_b(result_node_id_b[gc*16 +: 16]), + .np_state(np_state[gc*4 +: 4]), + .np_error(np_error[gc]) + ); + end + endgenerate +endmodule diff --git a/hardware/v3/synth/synth_np_packed_array_n8.tcl b/hardware/v3/synth/synth_np_packed_array_n8.tcl new file mode 100644 index 0000000..65f88c3 --- /dev/null +++ b/hardware/v3/synth/synth_np_packed_array_n8.tcl @@ -0,0 +1,11 @@ +read_verilog -sv /home/michele/Develop/FPGA-Neural/hardware/v3/rtl/neural_processor_packed.v +read_verilog -sv /home/michele/Develop/FPGA-Neural/hardware/v3/rtl/np_packed_array.v +synth_design -top np_packed_array -part xc7a100tcsg324-1 -mode out_of_context -generic N_CORES=8 +create_clock -name clk -period 5.000 [get_ports clk] +opt_design +report_utilization -file /tmp/util_np_array_n8_postsynth.rpt +place_design +route_design +report_utilization -file /tmp/util_np_array_n8_postroute.rpt +report_timing_summary -file /tmp/timing_np_array_n8_postroute.rpt +write_checkpoint -force /tmp/np_array_n8_postroute.dcp