diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 72a9688505..6bb6b7d07d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ exclude: '^docs/conf.py' default_language_version: - python: python3.10 + python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/src/finn/builder/build_dataflow_config.py b/src/finn/builder/build_dataflow_config.py index 5d69802337..008eeec836 100644 --- a/src/finn/builder/build_dataflow_config.py +++ b/src/finn/builder/build_dataflow_config.py @@ -34,7 +34,6 @@ from enum import Enum from typing import Any, List, Optional -from finn.transformation.fpgadataflow.vitis_build import VitisOptStrategy from finn.util.basic import alveo_default_platform, alveo_part_map, pynq_part_map @@ -65,17 +64,24 @@ class DataflowOutputType(str, Enum): DEPLOYMENT_PACKAGE = "deployment_package" -class VitisOptStrategyCfg(str, Enum): - """Vitis optimization strategy with serializable string enum values.""" +class VitisOptStrategy(Enum): + "Values applicable to VitisBuild optimization strategy." - DEFAULT = "default" - POWER = "power" - PERFORMANCE = "performance" - PERFORMANCE_BEST = "performance_best" - SIZE = "size" + DEFAULT = "0" + POWER = "1" + PERFORMANCE = "2" + PERFORMANCE_BEST = "3" + SIZE = "s" BUILD_SPEED = "quick" +class FpgaMemoryType(str, Enum): + "Memory Type used by the FPGA to store input/output data" + + DEFAULT = "default" + HOST_MEM = "host_memory" + + class LargeFIFOMemStyle(str, Enum): """Type of memory resource to use for large FIFOs.""" @@ -304,7 +310,11 @@ class DataflowBuildConfig: #: Vitis optimization strategy #: Only relevant when `shell_flow_type = ShellFlowType.VITIS_ALVEO` - vitis_opt_strategy: Optional[VitisOptStrategyCfg] = VitisOptStrategyCfg.DEFAULT + vitis_opt_strategy: Optional[VitisOptStrategy] = VitisOptStrategy.DEFAULT + + #: FPGA memory type + #: Can be used to use host memory for input/output data instead of DDR or HBM memory + fpga_memory: Optional[FpgaMemoryType] = FpgaMemoryType.DEFAULT #: Whether intermediate ONNX files will be saved during the build process. #: These can be useful for debugging if the build fails. @@ -388,18 +398,6 @@ def _resolve_cycles_per_frame(self): n_cycles_per_frame = n_clock_cycles_per_sec / self.target_fps return int(n_cycles_per_frame) - def _resolve_vitis_opt_strategy(self): - # convert human-readable enum to value expected by v++ - name_to_strategy = { - VitisOptStrategyCfg.DEFAULT: VitisOptStrategy.DEFAULT, - VitisOptStrategyCfg.POWER: VitisOptStrategy.POWER, - VitisOptStrategyCfg.PERFORMANCE: VitisOptStrategy.PERFORMANCE, - VitisOptStrategyCfg.PERFORMANCE_BEST: VitisOptStrategy.PERFORMANCE_BEST, - VitisOptStrategyCfg.SIZE: VitisOptStrategy.SIZE, - VitisOptStrategyCfg.BUILD_SPEED: VitisOptStrategy.BUILD_SPEED, - } - return name_to_strategy[self.vitis_opt_strategy] - def _resolve_vitis_platform(self): if self.vitis_platform is not None: return self.vitis_platform diff --git a/src/finn/builder/build_dataflow_steps.py b/src/finn/builder/build_dataflow_steps.py index ab2280554c..25ca01c48e 100644 --- a/src/finn/builder/build_dataflow_steps.py +++ b/src/finn/builder/build_dataflow_steps.py @@ -832,10 +832,11 @@ def step_synthesize_bitfile(model: ModelWrapper, cfg: DataflowBuildConfig): cfg._resolve_fpga_part(), cfg.synth_clk_period_ns, cfg._resolve_vitis_platform(), - strategy=cfg._resolve_vitis_opt_strategy(), + strategy=cfg.vitis_opt_strategy, enable_debug=cfg.enable_hw_debug, floorplan_file=cfg.vitis_floorplan_file, partition_model_dir=partition_model_dir, + fpga_memory_type=cfg.fpga_memory, ) ) copy(model.get_metadata_prop("bitfile"), bitfile_dir + "/finn-accel.xclbin") diff --git a/src/finn/transformation/fpgadataflow/vitis_build.py b/src/finn/transformation/fpgadataflow/vitis_build.py index 157d81cf35..36a89696ae 100644 --- a/src/finn/transformation/fpgadataflow/vitis_build.py +++ b/src/finn/transformation/fpgadataflow/vitis_build.py @@ -30,7 +30,6 @@ import json import os import subprocess -from enum import Enum from qonnx.core.modelwrapper import ModelWrapper from qonnx.custom_op.registry import getCustomOp from qonnx.transformation.base import Transformation @@ -40,6 +39,7 @@ RemoveUnusedTensors, ) +from finn.builder.build_dataflow_config import FpgaMemoryType, VitisOptStrategy from finn.transformation.fpgadataflow.create_dataflow_partition import ( CreateDataflowPartition, ) @@ -64,17 +64,6 @@ def _check_vitis_envvars(): ), "XILINX_XRT must be set for Vitis, ensure the XRT env is sourced" -class VitisOptStrategy(Enum): - "Values applicable to VitisBuild optimization strategy." - - DEFAULT = "0" - POWER = "1" - PERFORMANCE = "2" - PERFORMANCE_BEST = "3" - SIZE = "s" - BUILD_SPEED = "quick" - - class CreateVitisXO(Transformation): """Create a Vitis object file from a stitched FINN ip. @@ -175,12 +164,14 @@ def __init__( f_mhz=200, strategy=VitisOptStrategy.PERFORMANCE, enable_debug=False, + fpga_memory_type="default", ): super().__init__() self.platform = platform self.f_mhz = f_mhz self.strategy = strategy self.enable_debug = enable_debug + self.fpga_memory_type = fpga_memory_type def apply(self, model): _check_vitis_envvars() @@ -229,28 +220,42 @@ def apply(self, model): if node_slr != -1: config.append("slr=%s:SLR%d" % (instance_names[node.name], node_slr)) # assign memory banks - if producer is None or consumer is None: + if producer is None or consumer is None or consumer == []: node_mem_port = sdp_node.get_nodeattr("mem_port") if node_mem_port == "": - # configure good defaults based on board - if "u50" in self.platform or "u280" in self.platform or "u55c" in self.platform: - # Use HBM where available (also U50 does not have DDR) - mem_type = "HBM" - mem_idx = 0 - elif "u200" in self.platform: - # Use DDR controller in static region of U200 - mem_type = "DDR" - mem_idx = 1 - elif "u250" in self.platform: - # Use DDR controller on the node's SLR if set, otherwise 0 - mem_type = "DDR" - if node_slr == -1: + if self.fpga_memory_type == FpgaMemoryType.DEFAULT: + # configure good defaults based on board + if ( + "u50" in self.platform + or "u280" in self.platform + or "u55c" in self.platform + ): + # Use HBM where available (also U50 does not have DDR) + mem_type = "HBM" mem_idx = 0 + elif "u200" in self.platform: + # Use DDR controller in static region of U200 + mem_type = "DDR" + mem_idx = 1 + elif "u250" in self.platform: + # Use DDR controller on the node's SLR if set, otherwise 0 + mem_type = "DDR" + if node_slr == -1: + mem_idx = 0 + else: + mem_idx = node_slr else: - mem_idx = node_slr + mem_type = "DDR" + mem_idx = 1 + elif self.fpga_memory_type == FpgaMemoryType.HOST_MEM: + mem_type = "HOST" + mem_idx = 0 else: - mem_type = "DDR" - mem_idx = 1 + raise RuntimeError( + "Unknown fpga memory type: " + + str(self.fpga_memory_type) + + ". Aborting!" + ) node_mem_port = "%s[%d]" % (mem_type, mem_idx) config.append("sp=%s.m_axi_gmem0:%s" % (instance_names[node.name], node_mem_port)) # connect streams @@ -357,6 +362,7 @@ class VitisBuild(Transformation): Must be parse-able by the ApplyConfig transform. :parameter enable_link: enable linking kernels (.xo files), otherwise just synthesize them independently. + :parameter fpga_memory_type: Specify whether Host or FPGA memory such as DDR/HBM should be used """ def __init__( @@ -369,6 +375,7 @@ def __init__( floorplan_file=None, enable_link=True, partition_model_dir=None, + fpga_memory_type=FpgaMemoryType.DEFAULT, ): super().__init__() self.fpga_part = fpga_part @@ -379,6 +386,7 @@ def __init__( self.floorplan_file = floorplan_file self.enable_link = enable_link self.partition_model_dir = partition_model_dir + self.fpga_memory_type = fpga_memory_type def apply(self, model): _check_vitis_envvars() @@ -425,6 +433,7 @@ def apply(self, model): round(1000 / self.period_ns), strategy=self.strategy, enable_debug=self.enable_debug, + fpga_memory_type=self.fpga_memory_type, ) ) # set platform attribute for correct remote execution