Skip to content

Commit 1d6121e

Browse files
committed
[MVAU] Update weight file generation and fix double pumping path
1 parent d227eea commit 1d6121e

4 files changed

Lines changed: 38 additions & 16 deletions

File tree

finn-rtllib/fetch_weights/fetch_weights.sv

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,12 @@ module fetch_weights #(
214214
);
215215

216216
assign dma_addr = l_offsets[q_idx_dat];
217-
assign dma_len = ((MH*MW*WEIGHT_WIDTH+7)/8) & ~7;
217+
// Same byte-aligned per-IWSIMD-group packing as the tiled path (see above):
218+
// each of the MH*MW/IWSIMD groups occupies roundup(IWSIMD*WEIGHT_WIDTH, 8)
219+
// bits (= DS_BITS_BA) in external memory. Using tight bit-packing here would
220+
// under-fetch whenever IWSIMD*WEIGHT_WIDTH is not byte-aligned (e.g. SIMD=1,
221+
// sub-byte weights). Reduces to the tight value when it is byte-aligned.
222+
assign dma_len = (((MH*MW/IWSIMD) * ((IWSIMD*WEIGHT_WIDTH+7)/8)) + 7) & ~7;
218223

219224
end : genDirect
220225

src/finn/custom_op/fpgadataflow/matrixvectoractivation.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -746,15 +746,28 @@ def make_weight_file(self, weights, weight_file_mode, weight_file_name):
746746
# AXI-MM / fetch_weights path (MLO and external_mem): external memory
747747
# (DDR, HBM, ...) holds one IWSIMD group per DWC output beat, each
748748
# byte-aligned to DS_BITS_BA = roundup(IWSIMD*bitwidth, 8) bits. IWSIMD
749-
# is (PE*SIMD)/TH for TH>1, SIMD otherwise. The within-group ordering
750-
# must match the memstreamer (PE-flipped, TH sub-tile order undone):
751-
# unflipped would swap PE lanes when IWSIMD*bitwidth <= 8 packs several
752-
# PEs into one byte. For TH>1 weight_tensor_pe_flipped is already in
753-
# IWSIMD-sized chunks (tinner == (PE*SIMD)/TH == IWSIMD).
749+
# is (PE*SIMD)/TH for TH>1, SIMD otherwise.
750+
#
751+
# The within-group ordering depends on how fetch_weights delivers the
752+
# stream to the MVU:
753+
# - TH>1 (tiled MVAU): the stream passes straight through to the tiled
754+
# MVU, which expects the PE-flipped, TH-sub-tile-undone ordering.
755+
# weight_tensor_pe_flipped is already in IWSIMD-sized chunks
756+
# (tinner == (PE*SIMD)/TH == IWSIMD).
757+
# - TH=1 (standard MVAU): the stream goes through local_weight_buffer,
758+
# which distributes consecutive SIMD groups across PE lanes 0..PE-1
759+
# (pe-minor) and pairs weight SIMD lane s with activation SIMD lane s.
760+
# That reconstruction needs the natural (unflipped) PE/SIMD order:
761+
# PE-flipping reverses the PE lanes and SIMD-flipping scrambles the
762+
# dot product.
754763
th = self.get_nodeattr("TH")
755764
iwsimd = (pe * simd) // th if th > 1 else simd
756765
iwsimd_group_bits = roundup_to_integer_multiple(iwsimd * export_wdt.bitwidth(), 8)
757-
weight_tensor_iwsimd_groups = weight_tensor_pe_flipped.reshape(1, -1, iwsimd)
766+
if th > 1:
767+
weight_tensor_iwsimd = weight_tensor_pe_flipped
768+
else:
769+
weight_tensor_iwsimd = weight_tensor_unflipped.reshape(1, -1, pe * simd).copy()
770+
weight_tensor_iwsimd_groups = weight_tensor_iwsimd.reshape(1, -1, iwsimd)
758771
weight_tensor_iwsimd_groups = pack_innermost_dim_as_hex_string(
759772
weight_tensor_iwsimd_groups, export_wdt, iwsimd_group_bits, prefix=""
760773
)

src/finn/custom_op/fpgadataflow/rtl/matrixvectoractivation_rtl.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,12 @@ def instantiate_ip(self, cmd):
241241
node_name,
242242
)
243243
)
244-
# if using 2x pumped compute, connect the MVU's 2x clk input
245-
# to the 2x clock port. Otherwise connect 2x clk to regular clk port
244+
# For 2x pumped compute the MVU's 2x clk input is driven by the external
245+
# 2x clock port, which CreateStitchedIP exposes and connects for this
246+
# (flat) cell - nothing to connect here. Otherwise drive the unused 2x
247+
# clk input from the regular clock.
246248
clk_name = self.get_verilog_top_module_intf_names()["clk"][0]
247-
if self.get_nodeattr("pumpedCompute"):
248-
clk2x_name = self.get_verilog_top_module_intf_names()["clk2x"][0]
249-
cmd.append(
250-
"connect_bd_net [get_bd_pins %s/%s] [get_bd_pins %s/%s]"
251-
% (node_name, clk2x_name, node_name, clk2x_name)
252-
)
253-
else:
249+
if not self.get_nodeattr("pumpedCompute"):
254250
cmd.append(
255251
"connect_bd_net [get_bd_pins %s/%s] [get_bd_pins %s/ap_clk2x]"
256252
% (node_name, clk_name, node_name)

tests/fpgadataflow/test_fpgadataflow_mvau.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,10 @@ def test_mvau_fifocharacterize_rtlsim(
777777
(9, 8, 9, "external_mem"), # TH=9 high tiling, CHAINLEN=3
778778
(9, 16, 3, "external_mem"), # CHAINLEN=6
779779
(18, 32, 3, "internal_decoupled"), # max fold, CHAINLEN=11, decoupled
780+
# external_mem + TH=1: standard MVAU fed by fetch_weights from external memory
781+
# (PE>1 catches PE-lane swaps, SIMD=1 covers the sub-word byte-packing edge).
782+
(9, 16, 1, "external_mem"),
783+
(18, 1, 1, "external_mem"),
780784
],
781785
)
782786
@pytest.mark.parametrize(
@@ -808,6 +812,10 @@ def test_fpgadataflow_rtl_mvau(
808812
if simd == 1 and pumpedCompute:
809813
pytest.skip("""Clock pumping an input of SIMD=1 is not meaningful. Skipping test""")
810814

815+
# External memory has no on-chip weight memory to clock-pump.
816+
if mem_mode == "external_mem" and pumpedMemory:
817+
pytest.skip("External memory has no on-chip weight memory to clock-pump")
818+
811819
# Tiled MVAU (TH>1) requires DSP58 (Versal) and is not combined with clock pumping.
812820
# The (PE * SIMD) % TH == 0 constraint is guaranteed by the parameter tuples above.
813821
if th > 1:

0 commit comments

Comments
 (0)