From e5f65cfe1a0d2118a12e4219196cb81fef3c364a Mon Sep 17 00:00:00 2001 From: Christoph Berganski Date: Sun, 28 Jan 2024 16:26:33 +0100 Subject: [PATCH 01/11] [Deps] Add "pyyaml==6.0.1" dependency to Dockefile --- docker/Dockerfile.finn | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile.finn b/docker/Dockerfile.finn index 2ceb1f4195..4f1fc163de 100644 --- a/docker/Dockerfile.finn +++ b/docker/Dockerfile.finn @@ -112,6 +112,7 @@ RUN pip install pytest-metadata==1.7.0 RUN pip install pytest-html==3.0.0 RUN pip install pytest-html-merger==0.0.8 RUN pip install pytest-cov==4.1.0 +RUN pip install pyyaml==6.0.1 # extra dependencies from other FINN deps # installed in Docker image to make entrypoint script go faster From 6ace464c455d0b9a2535d80443670263f4e2b022 Mon Sep 17 00:00:00 2001 From: Christoph Berganski Date: Thu, 30 May 2024 17:49:29 +0200 Subject: [PATCH 02/11] [Lookup] Relax input datatype constraints --- .../custom_op/fpgadataflow/hls/lookup_hls.py | 2 +- .../fpgadataflow/rtl/streamingfifo_rtl.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py index ba44deb898..97597dd88e 100644 --- a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py +++ b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py @@ -273,7 +273,7 @@ def execute_node(self, context, graph): ) inp = context[node.input[0]] - assert inp.dtype == np.int64, "Inputs must be contained in int64 ndarray" + # assert inp.dtype == np.int64, "Inputs must be contained in int64 ndarray" assert inp.shape == exp_ishape, """Input shape doesn't match expected shape.""" export_idt = self.get_input_datatype() odt = self.get_output_datatype() diff --git a/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py b/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py index f8f27cb647..77dcb288c9 100644 --- a/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py +++ b/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py @@ -133,10 +133,18 @@ def execute_node(self, context, graph): elif mode == "rtlsim": code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen") # create a npy file for the input of the node - assert ( - str(inp.dtype) == "float32" - ), """Input datatype is - not float32 as expected.""" + + # Make sure the inpout has the right container datatype + if inp.dtype != np.float32: + # Issue a warning to make the user aware of this type-cast + warnings.warn( + f"{node.name}: Changing input datatype from " + f"{inp.dtype} to {np.float32}" + ) + # Convert the input to floating point representation as the + # container datatype + inp = inp.astype(np.float32) + expected_inp_shape = self.get_folded_input_shape() reshaped_input = inp.reshape(expected_inp_shape) if DataType[self.get_nodeattr("dataType")] == DataType["BIPOLAR"]: From ae9e25cc3988ae5c872a56b651f4daade29e1f48 Mon Sep 17 00:00:00 2001 From: Christoph Berganski Date: Fri, 13 Sep 2024 16:18:10 +0200 Subject: [PATCH 03/11] [Docker] Derive FINN_DOCKER_TAG from the SCRIPTPATH Previously the FINN_DOCKER_TAG has been derived by "git describe" in the current working directory, which fails if this is not the base of the FINN repository. As we know the SCRIPTPATH, which always resides in the base of the FINN repository, we can nagivate there, derive the docker tag and navigate back to the working directory. --- run-docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-docker.sh b/run-docker.sh index b1fe44eb0c..e85d9f1fdc 100755 --- a/run-docker.sh +++ b/run-docker.sh @@ -88,7 +88,7 @@ SCRIPTPATH=$(dirname "$SCRIPT") : ${PLATFORM_REPO_PATHS="/opt/xilinx/platforms"} : ${XRT_DEB_VERSION="xrt_202220.2.14.354_22.04-amd64-xrt"} : ${FINN_HOST_BUILD_DIR="/tmp/$DOCKER_INST_NAME"} -: ${FINN_DOCKER_TAG="xilinx/finn:$(git describe --always --tags --dirty).$XRT_DEB_VERSION"} +: ${FINN_DOCKER_TAG="xilinx/finn:$(OLD_PWD=$(pwd); cd $SCRIPTPATH; git describe --always --tags --dirty; cd $OLD_PWD).$XRT_DEB_VERSION"} : ${FINN_DOCKER_PREBUILT="0"} : ${FINN_DOCKER_RUN_AS_ROOT="0"} : ${FINN_DOCKER_GPU="$(docker info | grep nvidia | wc -m)"} From 4cf25719da08fa919baf253e15f2a3b841a4b9cf Mon Sep 17 00:00:00 2001 From: Felix Jentzsch Date: Tue, 28 Jan 2025 21:37:29 +0000 Subject: [PATCH 04/11] Replace distutil copy_tree with shutil --- src/finn/builder/build_dataflow_steps.py | 11 ++++++----- tests/end2end/test_end2end_bnn_pynq.py | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/finn/builder/build_dataflow_steps.py b/src/finn/builder/build_dataflow_steps.py index ab2280554c..5163b2dbdb 100644 --- a/src/finn/builder/build_dataflow_steps.py +++ b/src/finn/builder/build_dataflow_steps.py @@ -33,7 +33,6 @@ import shutil import warnings from copy import deepcopy -from distutils.dir_util import copy_tree from functools import partial from qonnx.core.modelwrapper import ModelWrapper from qonnx.custom_op.registry import getCustomOp @@ -656,7 +655,9 @@ def step_create_stitched_ip(model: ModelWrapper, cfg: DataflowBuildConfig): ) ) # TODO copy all ip sources into output dir? as zip? - copy_tree(model.get_metadata_prop("vivado_stitch_proj"), stitched_ip_dir) + shutil.copytree( + model.get_metadata_prop("vivado_stitch_proj"), stitched_ip_dir, dirs_exist_ok=True + ) print("Vivado stitched IP written into " + stitched_ip_dir) if VerificationStepType.STITCHED_IP_RTLSIM in cfg._resolve_verification_steps(): # prepare ip-stitched rtlsim @@ -761,7 +762,7 @@ def step_make_pynq_driver(model: ModelWrapper, cfg: DataflowBuildConfig): if DataflowOutputType.PYNQ_DRIVER in cfg.generate_outputs: driver_dir = cfg.output_dir + "/driver" model = model.transform(MakePYNQDriver(cfg._resolve_driver_platform())) - copy_tree(model.get_metadata_prop("pynq_driver_dir"), driver_dir) + shutil.copytree(model.get_metadata_prop("pynq_driver_dir"), driver_dir, dirs_exist_ok=True) print("PYNQ Python driver written into " + driver_dir) return model @@ -862,8 +863,8 @@ def step_deployment_package(model: ModelWrapper, cfg: DataflowBuildConfig): bitfile_dir = cfg.output_dir + "/bitfile" driver_dir = cfg.output_dir + "/driver" os.makedirs(deploy_dir, exist_ok=True) - copy_tree(bitfile_dir, deploy_dir + "/bitfile") - copy_tree(driver_dir, deploy_dir + "/driver") + shutil.copytree(bitfile_dir, deploy_dir + "/bitfile", dirs_exist_ok=True) + shutil.copytree(driver_dir, deploy_dir + "/driver", dirs_exist_ok=True) return model diff --git a/tests/end2end/test_end2end_bnn_pynq.py b/tests/end2end/test_end2end_bnn_pynq.py index 0d3418624a..688423e581 100644 --- a/tests/end2end/test_end2end_bnn_pynq.py +++ b/tests/end2end/test_end2end_bnn_pynq.py @@ -40,7 +40,6 @@ import warnings from brevitas.export import export_qonnx from dataset_loading import cifar, mnist -from distutils.dir_util import copy_tree from qonnx.core.datatype import DataType from qonnx.core.modelwrapper import ModelWrapper from qonnx.custom_op.registry import getCustomOp @@ -59,7 +58,7 @@ from qonnx.transformation.lower_convs_to_matmul import LowerConvsToMatMul from qonnx.transformation.merge_onnx_models import MergeONNXModels from qonnx.util.cleanup import cleanup as qonnx_cleanup -from shutil import copy +from shutil import copy, copytree import finn.transformation.fpgadataflow.convert_to_hw_layers as to_hw import finn.transformation.streamline.absorb as absorb @@ -357,7 +356,7 @@ def deploy_based_on_board(model, model_title, topology, wbits, abits, board): # driver.py and python libraries pynq_driver_dir = model.get_metadata_prop("pynq_driver_dir") - copy_tree(pynq_driver_dir, deployment_dir) + copytree(pynq_driver_dir, deployment_dir, dirs_exist_ok=True) model.set_metadata_prop("pynq_deploy_dir", deployment_dir) From 3de81d0c194cdd95624dd28d2c1f9ecdffb619c4 Mon Sep 17 00:00:00 2001 From: Christoph Berganski Date: Wed, 29 Jan 2025 12:54:38 +0100 Subject: [PATCH 05/11] [Lookup] Reintroduce prior assertion as warning and fix type comparison --- src/finn/custom_op/fpgadataflow/hls/lookup_hls.py | 14 +++++++++++++- .../fpgadataflow/rtl/streamingfifo_rtl.py | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py index 97597dd88e..5c251bbdf1 100644 --- a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py +++ b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py @@ -28,6 +28,7 @@ import numpy as np import os +import warnings from math import ceil, log2 from qonnx.core.datatype import DataType @@ -273,7 +274,18 @@ def execute_node(self, context, graph): ) inp = context[node.input[0]] - # assert inp.dtype == np.int64, "Inputs must be contained in int64 ndarray" + + # Make sure the input has the right container datatype + if inp.dtype is not np.float32: + # Issue a warning to make the user aware of this type-cast + warnings.warn( + f"{node.name}: Changing input container datatype from " + f"{inp.dtype} to {np.float32}" + ) + # Convert the input to floating point representation as the + # container datatype + inp = inp.astype(np.float32) + assert inp.shape == exp_ishape, """Input shape doesn't match expected shape.""" export_idt = self.get_input_datatype() odt = self.get_output_datatype() diff --git a/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py b/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py index 77dcb288c9..1b240eeff8 100644 --- a/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py +++ b/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py @@ -134,11 +134,11 @@ def execute_node(self, context, graph): code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen") # create a npy file for the input of the node - # Make sure the inpout has the right container datatype - if inp.dtype != np.float32: + # Make sure the input has the right container datatype + if inp.dtype is not np.float32: # Issue a warning to make the user aware of this type-cast warnings.warn( - f"{node.name}: Changing input datatype from " + f"{node.name}: Changing input container datatype from " f"{inp.dtype} to {np.float32}" ) # Convert the input to floating point representation as the From 6bd073a0c7c0926ca938aa8e51abc119db47ddd6 Mon Sep 17 00:00:00 2001 From: auphelia Date: Fri, 7 Feb 2025 17:15:47 +0000 Subject: [PATCH 06/11] [Notebooks] Replace disutil copy_tree with shutil in notebooks --- notebooks/end2end_example/bnn-pynq/cnv_end2end_example.ipynb | 5 ++--- notebooks/end2end_example/bnn-pynq/tfc_end2end_example.ipynb | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/notebooks/end2end_example/bnn-pynq/cnv_end2end_example.ipynb b/notebooks/end2end_example/bnn-pynq/cnv_end2end_example.ipynb index 8b8cff8ee9..507b1022e6 100644 --- a/notebooks/end2end_example/bnn-pynq/cnv_end2end_example.ipynb +++ b/notebooks/end2end_example/bnn-pynq/cnv_end2end_example.ipynb @@ -484,8 +484,7 @@ "metadata": {}, "outputs": [], "source": [ - "from shutil import copy\n", - "from distutils.dir_util import copy_tree\n", + "from shutil import copy, copytree\n", "\n", "# create directory for deployment files\n", "deployment_dir = make_build_dir(prefix=\"pynq_deployment_\")\n", @@ -503,7 +502,7 @@ "\n", "# driver.py and python libraries\n", "pynq_driver_dir = model.get_metadata_prop(\"pynq_driver_dir\")\n", - "copy_tree(pynq_driver_dir, deployment_dir)" + "copytree(pynq_driver_dir, deployment_dir, dirs_exist_ok=True)" ] }, { diff --git a/notebooks/end2end_example/bnn-pynq/tfc_end2end_example.ipynb b/notebooks/end2end_example/bnn-pynq/tfc_end2end_example.ipynb index 675ba23d2d..bb5e357b66 100644 --- a/notebooks/end2end_example/bnn-pynq/tfc_end2end_example.ipynb +++ b/notebooks/end2end_example/bnn-pynq/tfc_end2end_example.ipynb @@ -895,8 +895,7 @@ "metadata": {}, "outputs": [], "source": [ - "from shutil import copy\n", - "from distutils.dir_util import copy_tree\n", + "from shutil import copy, copytree\n", "\n", "# create directory for deployment files\n", "deployment_dir = make_build_dir(prefix=\"pynq_deployment_\")\n", @@ -914,7 +913,7 @@ "\n", "# driver.py and python libraries\n", "pynq_driver_dir = model.get_metadata_prop(\"pynq_driver_dir\")\n", - "copy_tree(pynq_driver_dir, deployment_dir)" + "copytree(pynq_driver_dir, deployment_dir, dirs_exist_ok=True)" ] }, { From 15c6e387aad38ded0a7a4705f991d7f0ec9055ea Mon Sep 17 00:00:00 2001 From: Christoph Berganski Date: Fri, 14 Feb 2025 17:03:37 +0100 Subject: [PATCH 07/11] [Lookup] Test data should also be read as float32 container type --- src/finn/custom_op/fpgadataflow/hls/lookup_hls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py index 5c251bbdf1..c0ca207613 100644 --- a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py +++ b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py @@ -98,7 +98,7 @@ def read_npy_data(self): packed_bits = self.get_instream_width() packed_hls_type = "ap_uint<%d>" % packed_bits elem_hls_type = dtype.get_hls_datatype_str() - npy_type = "int64_t" + npy_type = "float" npy_in = "%s/input_0.npy" % code_gen_dir self.code_gen_dict["$READNPYDATA$"] = [] self.code_gen_dict["$READNPYDATA$"].append( From 20e0c2ba66bbcab92588da31c1f1d3eb1e58912a Mon Sep 17 00:00:00 2001 From: auphelia Date: Wed, 19 Feb 2025 16:02:28 +0000 Subject: [PATCH 08/11] [Concat] Remove instream width padded duplicate --- src/finn/custom_op/fpgadataflow/concat.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/finn/custom_op/fpgadataflow/concat.py b/src/finn/custom_op/fpgadataflow/concat.py index 210b6b7fdd..985ac83ea6 100644 --- a/src/finn/custom_op/fpgadataflow/concat.py +++ b/src/finn/custom_op/fpgadataflow/concat.py @@ -29,7 +29,6 @@ import numpy as np from qonnx.core.datatype import DataType -from qonnx.util.basic import roundup_to_integer_multiple from finn.custom_op.fpgadataflow.hwcustomop import HWCustomOp @@ -134,10 +133,6 @@ def execute_node(self, context, graph): result = np.concatenate(inp_values, axis=-1) context[node.output[0]] = result - def get_instream_width_padded(self, ind=0): - in_width = self.get_instream_width(ind) - return roundup_to_integer_multiple(in_width, 8) - def get_verilog_top_module_intf_names(self): intf_names = super().get_verilog_top_module_intf_names() n_inputs = self.get_n_inputs() From ff38e0457822f87be31711978b9da63a50dfed73 Mon Sep 17 00:00:00 2001 From: John Monks Date: Wed, 19 Feb 2025 22:32:31 +0000 Subject: [PATCH 09/11] [ci] Update Jenkinsfile build artifact gathering Previously, hw_deployment_* directories were being found. The result of this "find" was a list of directory paths which Jenkins was not handling correctly. Removed this find and instead wrapped the copy in a try/catch, if an error occurs catch it and error accordingly. --- docker/jenkins/Jenkinsfile | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/docker/jenkins/Jenkinsfile b/docker/jenkins/Jenkinsfile index 6d51fffd64..5149e8cd0e 100644 --- a/docker/jenkins/Jenkinsfile +++ b/docker/jenkins/Jenkinsfile @@ -324,21 +324,17 @@ void runDockerPytestWithMarker(String marker, String testResultsFilename, String sh """./run-docker.sh python -m pytest -m ${marker} --junitxml=${testResultsFilename}.xml --html=${testResultsFilename}.html --self-contained-html ${additionalOptions}""" } -def findBoardBuildFiles(String searchDir, String dirToFind) { - def result = sh(script: "find $searchDir -type d -name \"$dirToFind*\"", returnStdout: true).trim() - if (result.empty) { - error "Directory containing '$dirToFind' not found." - } - return result -} - void findCopyZip(String board, String findDir, String copyDir) { - def buildDir = findBoardBuildFiles(findDir, "hw_deployment_${board}") - sh "cp -r ${buildDir}/${board} ${copyDir}/" - dir(copyDir) { - sh "zip -r ${board}.zip ${board}/" - sh "mkdir -p ${env.ARTIFACT_DIR}/${copyDir}/" - sh "cp ${board}.zip ${env.ARTIFACT_DIR}/${copyDir}/" + sh "mkdir -p ${copyDir}" + try { + sh "cp -r ${findDir}/hw_deployment_*/${board} ${copyDir}/" + dir(copyDir) { + sh "zip -r ${board}.zip ${board}/" + sh "mkdir -p ${env.ARTIFACT_DIR}/${copyDir}/" + sh "cp ${board}.zip ${env.ARTIFACT_DIR}/${copyDir}/" + } + } catch (err) { + error "No ${board} hw_deployment_* build artifacts found in ${findDir}" } } From e60694d7bea710358cf2323f190c13a74a428e97 Mon Sep 17 00:00:00 2001 From: John Monks Date: Wed, 19 Feb 2025 22:52:50 +0000 Subject: [PATCH 10/11] [ci] Run fpgadataflow in parallel --- docker/jenkins/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/jenkins/Jenkinsfile b/docker/jenkins/Jenkinsfile index 5149e8cd0e..cca3436363 100644 --- a/docker/jenkins/Jenkinsfile +++ b/docker/jenkins/Jenkinsfile @@ -93,7 +93,7 @@ pipeline { cleanPreviousBuildFiles(env.FINN_HOST_BUILD_DIR) // Pass in the marker to run with pytest and the XML test results filename - runDockerPytestWithMarker("fpgadataflow", "${env.TEST_NAME}", "--cov --cov-report=html:coverage_fpgadataflow") + runDockerPytestWithMarker("fpgadataflow", "${env.TEST_NAME}", "--cov --cov-report=html:coverage_fpgadataflow -n ${env.NUM_PYTEST_WORKERS} --dist worksteal") // Stash the test results file(s) stash name: env.TEST_NAME, includes: "${env.TEST_NAME}.xml,${env.TEST_NAME}.html" From 32fbff093fdcbd65081d520b2694eea8279f4974 Mon Sep 17 00:00:00 2001 From: auphelia Date: Thu, 20 Feb 2025 16:26:28 +0000 Subject: [PATCH 11/11] [LookUp] With npy type being float we can use parent class method for read_npy_data --- .../custom_op/fpgadataflow/hls/lookup_hls.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py index c0ca207613..ad40b62d8c 100644 --- a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py +++ b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py @@ -88,31 +88,6 @@ def defines(self, var): my_defines.append("#define EmbeddingType %s" % emb_hls_type) self.code_gen_dict["$DEFINES$"] = my_defines - def read_npy_data(self): - code_gen_dir = self.get_nodeattr("code_gen_dir_cppsim") - dtype = self.get_input_datatype() - if dtype == DataType["BIPOLAR"]: - # use binary for bipolar storage - dtype = DataType["BINARY"] - elem_bits = dtype.bitwidth() - packed_bits = self.get_instream_width() - packed_hls_type = "ap_uint<%d>" % packed_bits - elem_hls_type = dtype.get_hls_datatype_str() - npy_type = "float" - npy_in = "%s/input_0.npy" % code_gen_dir - self.code_gen_dict["$READNPYDATA$"] = [] - self.code_gen_dict["$READNPYDATA$"].append( - 'npy2apintstream<%s, %s, %d, %s>("%s", in0_%s);' - % ( - packed_hls_type, - elem_hls_type, - elem_bits, - npy_type, - npy_in, - self.hls_sname(), - ) - ) - def dataoutstrm(self): code_gen_dir = self.get_nodeattr("code_gen_dir_cppsim") dtype = self.get_output_datatype()