Summary
finn-rtllib/mvu/mvu_4sx4u.sv fails synthesis with "part select index out of range" for configurations whose ACCU_WIDTH > 27 (reproduced with SIMD=1/PE=2, ACCU_WIDTH=32, on Vivado 2025.2 and 2026.1, multiple parts). The offending part-select is p3[52:21] on a [47:0] bus.
Root cause (source-level)
Stage #4's cross-SIMD reduction iterates all four lanes unconditionally:
uwire [47:0] p3[SIMD]; // line ~139
...
for(genvar i = 0; i < 4; i++) begin // line ~514
...
if(i < PE_REM) assign lo4[i] = '0;
else begin : genLo
localparam int unsigned LO_WIDTH = lo_width(i); // OFFSETS[i+1]-OFFSETS[i]
...
for(genvar s = 0; s < SIMD; s++)
assign tree[SIMD-1+s] = p3[s][OFFSETS[i]+:LO_WIDTH]; // line 556
sliceLanes() (VERSION=1, NARROW_WEIGHTS) returns OFFSETS = {ACCU_WIDTH+21, 21, 14, 7, 0}, so for lane i=3:
LO_WIDTH = OFFSETS[4]-OFFSETS[3] = ACCU_WIDTH, and the part-select becomes p3[21 +: ACCU_WIDTH].
p3 is only 48 bits wide, so any ACCU_WIDTH > 27 (48-21) makes the part-select exceed the bus:
- ACCU_WIDTH=32 ->
p3[52:21] -> Vivado: "part select index [52:21] out of range [47:0]".
The if(i < PE_REM) guard does not protect lane 3 for PE < 4 (e.g. PE=2 gives PE_REM=2, and i=3 takes the genLo branch).
Repro
- FINN MVAU config: SIMD=1, PE=2 (ACCU_WIDTH resolves to 32); any config with ACCU_WIDTH >= 28 should hit the same error.
- Run Vivado synthesis on the generated
mvu_4sx4u.sv (e.g. synth_design on the MVAU wrapper): fails with part-select out of range; genLo block at line ~556 (plus the corresponding p3 write at line ~501 and the X3/pp slice at ~492 in the same lane-3 path).
Suggested fix
Guard the lane-3 low-part reduction on the actual PE count / lane width (the loop processes i=0..3 but only lanes < PE are real; lane 3's wide slice must not be emitted when the configuration doesn't use it), or clamp LO_WIDTH/OFFSETS for lane 3 to ACCU_WIDTH only when the lane is populated.
Summary
finn-rtllib/mvu/mvu_4sx4u.svfails synthesis with "part select index out of range" for configurations whoseACCU_WIDTH > 27(reproduced with SIMD=1/PE=2, ACCU_WIDTH=32, on Vivado 2025.2 and 2026.1, multiple parts). The offending part-select isp3[52:21]on a[47:0]bus.Root cause (source-level)
Stage #4's cross-SIMD reduction iterates all four lanes unconditionally:
sliceLanes()(VERSION=1, NARROW_WEIGHTS) returnsOFFSETS = {ACCU_WIDTH+21, 21, 14, 7, 0}, so for lanei=3:LO_WIDTH = OFFSETS[4]-OFFSETS[3] = ACCU_WIDTH, and the part-select becomesp3[21 +: ACCU_WIDTH].p3is only 48 bits wide, so anyACCU_WIDTH > 27(48-21) makes the part-select exceed the bus:p3[52:21]-> Vivado: "part select index [52:21] out of range [47:0]".The
if(i < PE_REM)guard does not protect lane 3 forPE < 4(e.g. PE=2 gives PE_REM=2, and i=3 takes thegenLobranch).Repro
mvu_4sx4u.sv(e.g.synth_designon the MVAU wrapper): fails with part-select out of range;genLoblock at line ~556 (plus the correspondingp3write at line ~501 and theX3/ppslice at ~492 in the same lane-3 path).Suggested fix
Guard the lane-3 low-part reduction on the actual PE count / lane width (the loop processes
i=0..3but only lanes< PEare real; lane 3's wide slice must not be emitted when the configuration doesn't use it), or clampLO_WIDTH/OFFSETSfor lane 3 toACCU_WIDTHonly when the lane is populated.