diff --git a/run-docker.sh b/run-docker.sh index e010733080b7cae205119e2bc136cff836f71fa5..f5c9f64b7d89e7def72c5b39131f37c22fcf57bf 100755 --- a/run-docker.sh +++ b/run-docker.sh @@ -89,7 +89,7 @@ git clone $EXAMPLES_REPO $EXAMPLES_LOCAL || git -C "$EXAMPLES_LOCAL" checkout f git clone $CNPY_REPO $CNPY_LOCAL || git -C "$CNPY_LOCAL" pull git clone $FINN_HLS_REPO $FINN_HLS_LOCAL || git -C "$FINN_HLS_LOCAL" checkout master; git -C "$FINN_HLS_LOCAL" pull git clone $PYVERILATOR_REPO $PYVERILATOR_LOCAL || git -C "$PYVERILATOR_LOCAL" pull -git clone $PYNQSHELL_REPO $PYNQSHELL_LOCAL || git -C "$PYNQSHELL_LOCAL" pull +git clone $PYNQSHELL_REPO $PYNQSHELL_LOCAL || git -C "$PYNQSHELL_LOCAL" checkout feature/synth_rpt; git -C "$PYNQSHELL_LOCAL" pull # ensure build dir exists locally mkdir -p $BUILD_LOCAL diff --git a/src/finn/analysis/fpgadataflow/hls_synth_res_estimation.py b/src/finn/analysis/fpgadataflow/hls_synth_res_estimation.py index 0334c316b80a5c0628d00b75eb40776436cb8434..c7db5b1d9d22ea89740f4c82633c96746a6fa5ee 100644 --- a/src/finn/analysis/fpgadataflow/hls_synth_res_estimation.py +++ b/src/finn/analysis/fpgadataflow/hls_synth_res_estimation.py @@ -34,9 +34,9 @@ import finn.util.basic as util def hls_synth_res_estimation(model): - """Extracts the results from the vivado synthesis. + """Extracts the FPGA resource results from the Vivado HLS synthesis estimates. - Returns {node name : resource estimation}.""" + Returns {node name : resources_dict}.""" res_dict = {} for node in model.graph.node: @@ -60,14 +60,12 @@ def hls_synth_res_estimation(model): ) if os.path.isfile(xmlfile): - res_dict[node.name] = [] + res_dict[node.name] = dict() tree = ET.parse(xmlfile) root = tree.getroot() for item in root.findall("AreaEstimates/Resources"): for child in item: - res_dict[node.name].append( - ["{} : {}".format(child.tag, child.text)] - ) + res_dict[node.name][child.tag] = child.text else: raise Exception( """Please run "HLSSynth_IPGen" first diff --git a/src/finn/analysis/fpgadataflow/post_synth_res.py b/src/finn/analysis/fpgadataflow/post_synth_res.py new file mode 100644 index 0000000000000000000000000000000000000000..508c34aaed50f2935f4915cdcea29a3e92641b3c --- /dev/null +++ b/src/finn/analysis/fpgadataflow/post_synth_res.py @@ -0,0 +1,80 @@ +# Copyright (c) 2020, Xilinx +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of FINN nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import xml.etree.ElementTree as ET + +from finn.transformation.move_reshape import _is_fpgadataflow_node + + +def post_synth_res(model): + """Extracts the FPGA resource results from the Vivado synthesis. + + Returns {node name : resources_dict}.""" + + res_dict = {} + synth_report_filename = model.get_metadata_prop("vivado_synth_rpt") + if os.path.isfile(synth_report_filename): + tree = ET.parse(synth_report_filename) + root = tree.getroot() + all_cells = root.findall(".//tablecell") + # strip all whitespace from table cell contents + for cell in all_cells: + cell.attrib["contents"] = cell.attrib["contents"].strip() + else: + raise Exception("Please run synthesis first") + + for node in model.graph.node: + if _is_fpgadataflow_node(node): + row = root.findall(".//*[@contents='%s']/.." % node.name) + if row != []: + node_dict = {} + row = row[0].getchildren() + """ Expected XML structure: +<tablerow class="" suppressoutput="0" wordwrap="0"> + <tableheader class="" contents="Instance" halign="3" width="-1"/> + <tableheader class="" contents="Module" halign="3" width="-1"/> + <tableheader class="" contents="Total LUTs" halign="3" width="-1"/> + <tableheader class="" contents="Logic LUTs" halign="3" width="-1"/> + <tableheader class="" contents="LUTRAMs" halign="3" width="-1"/> + <tableheader class="" contents="SRLs" halign="3" width="-1"/> + <tableheader class="" contents="FFs" halign="3" width="-1"/> + <tableheader class="" contents="RAMB36" halign="3" width="-1"/> + <tableheader class="" contents="RAMB18" halign="3" width="-1"/> + <tableheader class="" contents="DSP48 Blocks" halign="3" width="-1"/> +</tablerow> + """ + node_dict["LUT"] = int(row[2].attrib["contents"]) + node_dict["SRL"] = int(row[5].attrib["contents"]) + node_dict["FF"] = int(row[6].attrib["contents"]) + node_dict["BRAM_36K"] = int(row[7].attrib["contents"]) + node_dict["BRAM_18K"] = int(row[8].attrib["contents"]) + node_dict["DSP48"] = int(row[9].attrib["contents"]) + res_dict[node.name] = node_dict + + return res_dict diff --git a/src/finn/custom_op/fpgadataflow/__init__.py b/src/finn/custom_op/fpgadataflow/__init__.py index 32c09d655efc12882e720cae0421bf93e56c30b9..4231be7c523a5a510de89fb1202dc7bbcf30d39f 100644 --- a/src/finn/custom_op/fpgadataflow/__init__.py +++ b/src/finn/custom_op/fpgadataflow/__init__.py @@ -73,15 +73,18 @@ class HLSCustomOp(CustomOp): "exec_mode": ("s", False, ""), "sim_cycles": ("i", False, 0), "rtlsim_trace": ("s", False, ""), + "res_estimate": ("s", False, ""), + "res_hls": ("s", False, ""), + "res_synth": ("s", False, ""), } def node_res_estimation(self): """Returns summarized resource estimation of BRAMs and LUTs - of the node.""" - resources = [] - resources.append("BRAMs: " + str(self.bram_estimation())) - resources.append("LUTs: " + str(self.lut_estimation())) - return resources + of the node as a dictionary.""" + ret = dict() + ret["BRAM_18K"] = self.bram_estimation() + ret["LUT"] = self.lut_estimation() + return ret def bram_estimation(self): """Function for BRAM resource estimation, is member function of diff --git a/src/finn/custom_op/fpgadataflow/convolutioninputgenerator.py b/src/finn/custom_op/fpgadataflow/convolutioninputgenerator.py index 7e098fd705055047f9987a20628d128fe82fe105..2ef5d350fb972e448b9a3745eb8c98197ab87d94 100644 --- a/src/finn/custom_op/fpgadataflow/convolutioninputgenerator.py +++ b/src/finn/custom_op/fpgadataflow/convolutioninputgenerator.py @@ -132,12 +132,6 @@ class ConvolutionInputGenerator(HLSCustomOp): def verify_node(self): pass - def bram_estimation(self): - pass - - def lut_estimation(self): - pass - def get_input_datatype(self): """Returns FINN DataType of input.""" return DataType[self.get_nodeattr("inputDataType")] diff --git a/src/finn/custom_op/fpgadataflow/streamingdatawidthconverter_batch.py b/src/finn/custom_op/fpgadataflow/streamingdatawidthconverter_batch.py index 1d9a10804f81f4039316daac2ca74c212cbbce6e..5e4c99aa41216b05f66da8341870269c620c6c40 100644 --- a/src/finn/custom_op/fpgadataflow/streamingdatawidthconverter_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingdatawidthconverter_batch.py @@ -28,6 +28,7 @@ import os import numpy as np + try: from pyverilator import PyVerilator except ModuleNotFoundError: @@ -209,12 +210,6 @@ class StreamingDataWidthConverter_Batch(HLSCustomOp): return info_messages - def bram_estimation(self): - pass - - def lut_estimation(self): - pass - def global_includes(self): self.code_gen_dict["$GLOBALS$"] = ['#include "streamtools.h"'] diff --git a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py index 9b9f7bcead9f0063fa646bb4beb2bc886a9526c7..a7c2d5166b6af41327abcfeaa5cb5ae25fd23856 100644 --- a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py @@ -150,12 +150,6 @@ class StreamingMaxPool_Batch(HLSCustomOp): return info_messages - def bram_estimation(self): - pass - - def lut_estimation(self): - pass - def global_includes(self): self.code_gen_dict["$GLOBALS$"] = ['#include "maxpool.h"'] diff --git a/src/finn/transformation/fpgadataflow/annotate_resources.py b/src/finn/transformation/fpgadataflow/annotate_resources.py new file mode 100644 index 0000000000000000000000000000000000000000..d192372a7d9c1f6ee2f088c6a058b994d21f6c99 --- /dev/null +++ b/src/finn/transformation/fpgadataflow/annotate_resources.py @@ -0,0 +1,78 @@ +# Copyright (c) 2020, Xilinx +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of FINN nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import finn.custom_op.registry as registry +from finn.transformation import Transformation +from finn.transformation.move_reshape import _is_fpgadataflow_node +from finn.analysis.fpgadataflow.res_estimation import res_estimation +from finn.analysis.fpgadataflow.hls_synth_res_estimation import hls_synth_res_estimation +from finn.analysis.fpgadataflow.post_synth_res import post_synth_res + + +class AnnotateResources(Transformation): + """Annotate the amount of FPGA resources taken by each fpgadataflow + node as an attribute on the node, depending on the mode parameter: + * 'estimate' -- use the analytical estimation model + * 'hls' -- use results from the HLS synthesis report + + No annotations can be provided unless the relevant transformation for the + chosen mode (e.g. HLSSynth_IPGen for hls) was previously run. + """ + + def __init__(self, mode): + super().__init__() + self.mode = mode + + def apply(self, model): + graph = model.graph + if self.mode == "estimate": + res_fxn = res_estimation + elif self.mode == "hls": + res_fxn = hls_synth_res_estimation + elif self.mode == "synth": + res_fxn = post_synth_res + else: + raise Exception("Unrecognized mode for AnnotateResources") + res_dict = model.analysis(res_fxn) + total_dict = {} + for lname in res_dict.keys(): + layer_res_dict = res_dict[lname] + for r_type in layer_res_dict.keys(): + r_amount = layer_res_dict[r_type] + r_amount = float(r_amount) + if r_type in total_dict.keys(): + total_dict[r_type] += r_amount + else: + total_dict[r_type] = r_amount + model.set_metadata_prop("res_total_" + self.mode, str(total_dict)) + for node in graph.node: + if _is_fpgadataflow_node(node) and node.name in res_dict.keys(): + op_inst = registry.getCustomOp(node) + op_inst.set_nodeattr("res_" + self.mode, str(res_dict[node.name])) + + return (model, False) diff --git a/src/finn/transformation/fpgadataflow/make_pynq_proj.py b/src/finn/transformation/fpgadataflow/make_pynq_proj.py index c2c3802635ba8b1be9bf7f0c71e48ad13b79771f..9921ce7caf2aaffd197f9bc863ab77502a963647 100644 --- a/src/finn/transformation/fpgadataflow/make_pynq_proj.py +++ b/src/finn/transformation/fpgadataflow/make_pynq_proj.py @@ -113,11 +113,15 @@ class MakePYNQProject(Transformation): # create a temporary folder for the project vivado_pynq_proj_dir = make_build_dir(prefix="vivado_pynq_proj_") model.set_metadata_prop("vivado_pynq_proj", vivado_pynq_proj_dir) + # filename for the synth utilization report + synth_report_filename = vivado_pynq_proj_dir + "/synth_report.xml" + model.set_metadata_prop("vivado_synth_rpt", synth_report_filename) ip_config_tcl = templates.ip_config_tcl_template % ( vivado_pynq_proj_dir, ip_dirs_str, vivado_pynq_proj_dir, + synth_report_filename, vivado_stitch_vlnv, in_bytes, out_bytes, diff --git a/src/finn/transformation/fpgadataflow/templates.py b/src/finn/transformation/fpgadataflow/templates.py index edbf28c4e9d49129d22da12985f3b8c003e3d745..81cb954bb4503c8daf18bad5881661018e9d17b7 100644 --- a/src/finn/transformation/fpgadataflow/templates.py +++ b/src/finn/transformation/fpgadataflow/templates.py @@ -38,6 +38,7 @@ variable config_ip_use_axilite variable config_ip_project_dir variable config_output_products_dir variable config_remote_cache +variable config_util_report_filename # for arguments involving paths below: use absolute paths or relative to the # platform/overlay/bitstream folder @@ -47,6 +48,8 @@ set config_ip_project_dir %s set config_ip_repo %s # where the produced bitfile and .hwh file will be placed set config_output_products_dir %s +# where the synth util XML report will be written +set config_util_report_filename %s # non-path arguments # VLNV of the IP block diff --git a/tests/end2end/test_end2end_cnv_w1a1.py b/tests/end2end/test_end2end_cnv_w1a1.py index 30440bdaa23fa4af792816bedef060dee574f5d6..1a59191a085616d08d0910b28a9e62cb6596b7c4 100644 --- a/tests/end2end/test_end2end_cnv_w1a1.py +++ b/tests/end2end/test_end2end_cnv_w1a1.py @@ -68,9 +68,9 @@ from finn.transformation.fpgadataflow.make_pynq_driver import MakePYNQDriver from finn.transformation.fpgadataflow.make_pynq_proj import MakePYNQProject from finn.transformation.fpgadataflow.synth_pynq_proj import SynthPYNQProject from finn.transformation.fpgadataflow.make_deployment import DeployToPYNQ - from finn.util.basic import pynq_part_map from finn.util.test import get_test_model_trained +from finn.transformation.fpgadataflow.annotate_resources import AnnotateResources build_dir = "/tmp/" + os.environ["FINN_INST_NAME"] @@ -164,14 +164,16 @@ def test_end2end_cnv_w1a1_fold_and_tlastmarker(): model = model.transform(InsertDWC()) model = model.transform(InsertTLastMarker()) + model = model.transform(GiveUniqueNodeNames()) + model = model.transform(AnnotateResources("estimate")) model.save(build_dir + "/end2end_cnv_w1a1_folded.onnx") def test_end2end_cnv_w1a1_gen_hls_ip(): model = ModelWrapper(build_dir + "/end2end_cnv_w1a1_folded.onnx") - model = model.transform(GiveUniqueNodeNames()) model = model.transform(CodeGen_ipgen(test_fpga_part, target_clk_ns)) model = model.transform(HLSSynth_IPGen()) + model = model.transform(AnnotateResources("hls")) model.save(build_dir + "/end2end_cnv_w1a1_ipgen.onnx") @@ -267,6 +269,7 @@ def test_end2end_cnv_w1a1_make_pynq_proj(): def test_end2end_cnv_w1a1_synth_pynq_project(): model = ModelWrapper(build_dir + "/end2end_cnv_w1a1_pynq_project.onnx") model = model.transform(SynthPYNQProject()) + model = model.transform(AnnotateResources("synth")) model.save(build_dir + "/end2end_cnv_w1a1_synth.onnx") diff --git a/tests/end2end/test_end2end_tfc_w1a1.py b/tests/end2end/test_end2end_tfc_w1a1.py index d61ea8d275b0be4d9ff31e04025510d019d40b9e..03d6f92f1c148ce444f08fd65a867ad9390a18fd 100644 --- a/tests/end2end/test_end2end_tfc_w1a1.py +++ b/tests/end2end/test_end2end_tfc_w1a1.py @@ -70,6 +70,7 @@ from finn.transformation.streamline import Streamline from finn.transformation.streamline.round_thresholds import RoundAndClipThresholds from finn.util.basic import pynq_part_map from finn.util.test import get_test_model_trained +from finn.transformation.fpgadataflow.annotate_resources import AnnotateResources build_dir = "/tmp/" + os.environ["FINN_INST_NAME"] test_pynq_board = os.getenv("PYNQ_BOARD", default="Pynq-Z1") @@ -146,14 +147,16 @@ def test_end2end_tfc_w1a1_fold_and_tlastmarker(): fc3w.set_nodeattr("outFIFODepth", 50) model = model.transform(InsertDWC()) model = model.transform(InsertTLastMarker()) + model = model.transform(GiveUniqueNodeNames()) + model = model.transform(AnnotateResources("estimate")) model.save(build_dir + "/end2end_tfc_w1a1_folded.onnx") def test_end2end_tfc_w1a1_gen_hls_ip(): model = ModelWrapper(build_dir + "/end2end_tfc_w1a1_folded.onnx") - model = model.transform(GiveUniqueNodeNames()) model = model.transform(CodeGen_ipgen(test_fpga_part, target_clk_ns)) model = model.transform(HLSSynth_IPGen()) + model = model.transform(AnnotateResources("hls")) model.save(build_dir + "/end2end_tfc_w1a1_ipgen.onnx") @@ -243,6 +246,7 @@ def test_end2end_tfc_w1a1_make_pynq_proj(): def test_end2end_tfc_w1a1_synth_pynq_project(): model = ModelWrapper(build_dir + "/end2end_tfc_w1a1_pynq_project.onnx") model = model.transform(SynthPYNQProject()) + model = model.transform(AnnotateResources("synth")) model.save(build_dir + "/end2end_tfc_w1a1_synth.onnx") diff --git a/tests/end2end/test_end2end_tfc_w1a2.py b/tests/end2end/test_end2end_tfc_w1a2.py index 2ac344af516b773a737bf153659fd633ba244074..7fef331b99a78b43f8e808c8cdf978a5c8233f92 100644 --- a/tests/end2end/test_end2end_tfc_w1a2.py +++ b/tests/end2end/test_end2end_tfc_w1a2.py @@ -66,6 +66,7 @@ from finn.transformation.infer_shapes import InferShapes from finn.transformation.streamline import Streamline from finn.util.basic import pynq_part_map from finn.util.test import get_test_model_trained +from finn.transformation.fpgadataflow.annotate_resources import AnnotateResources build_dir = "/tmp/" + os.environ["FINN_INST_NAME"] test_pynq_board = os.getenv("PYNQ_BOARD", default="Pynq-Z1") @@ -137,14 +138,16 @@ def test_end2end_tfc_w1a2_fold_and_tlastmarker(): fc3w.set_nodeattr("PE", 10) fc3w.set_nodeattr("outFIFODepth", 50) model = model.transform(InsertTLastMarker()) + model = model.transform(GiveUniqueNodeNames()) + model = model.transform(AnnotateResources("estimate")) model.save(build_dir + "/end2end_tfc_w1a2_folded.onnx") def test_end2end_tfc_w1a2_gen_hls_ip(): model = ModelWrapper(build_dir + "/end2end_tfc_w1a2_folded.onnx") - model = model.transform(GiveUniqueNodeNames()) model = model.transform(CodeGen_ipgen(test_fpga_part, target_clk_ns)) model = model.transform(HLSSynth_IPGen()) + model = model.transform(AnnotateResources("hls")) model.save(build_dir + "/end2end_tfc_w1a2_ipgen.onnx") @@ -234,6 +237,7 @@ def test_end2end_tfc_w1a2_make_pynq_proj(): def test_end2end_tfc_w1a2_synth_pynq_project(): model = ModelWrapper(build_dir + "/end2end_tfc_w1a2_pynq_project.onnx") model = model.transform(SynthPYNQProject()) + model = model.transform(AnnotateResources("synth")) model.save(build_dir + "/end2end_tfc_w1a2_synth.onnx") diff --git a/tests/end2end/test_end2end_tfc_w2a2.py b/tests/end2end/test_end2end_tfc_w2a2.py index acc01b568f9300d643d02879b22ef037c848009e..c78be7b66fe2c2f84e6f9a1a520c3e22e769c82f 100644 --- a/tests/end2end/test_end2end_tfc_w2a2.py +++ b/tests/end2end/test_end2end_tfc_w2a2.py @@ -66,6 +66,7 @@ from finn.transformation.infer_shapes import InferShapes from finn.transformation.streamline import Streamline from finn.util.basic import pynq_part_map from finn.util.test import get_test_model_trained +from finn.transformation.fpgadataflow.annotate_resources import AnnotateResources build_dir = "/tmp/" + os.environ["FINN_INST_NAME"] test_pynq_board = os.getenv("PYNQ_BOARD", default="Pynq-Z1") @@ -137,14 +138,16 @@ def test_end2end_tfc_w2a2_fold_and_tlastmarker(): fc3w.set_nodeattr("PE", 10) fc3w.set_nodeattr("outFIFODepth", 50) model = model.transform(InsertTLastMarker()) + model = model.transform(GiveUniqueNodeNames()) + model = model.transform(AnnotateResources("estimate")) model.save(build_dir + "/end2end_tfc_w2a2_folded.onnx") def test_end2end_tfc_w2a2_gen_hls_ip(): model = ModelWrapper(build_dir + "/end2end_tfc_w2a2_folded.onnx") - model = model.transform(GiveUniqueNodeNames()) model = model.transform(CodeGen_ipgen(test_fpga_part, target_clk_ns)) model = model.transform(HLSSynth_IPGen()) + model = model.transform(AnnotateResources("hls")) model.save(build_dir + "/end2end_tfc_w2a2_ipgen.onnx") @@ -234,6 +237,7 @@ def test_end2end_tfc_w2a2_make_pynq_proj(): def test_end2end_tfc_w2a2_synth_pynq_project(): model = ModelWrapper(build_dir + "/end2end_tfc_w2a2_pynq_project.onnx") model = model.transform(SynthPYNQProject()) + model = model.transform(AnnotateResources("synth")) model.save(build_dir + "/end2end_tfc_w2a2_synth.onnx") diff --git a/tests/fpgadataflow/test_fpgadataflow_res_estimate.py b/tests/fpgadataflow/test_fpgadataflow_res_estimate.py index 0dd3fd7a9fefaaad9777ac98a35806a9eaa35188..38f792ed3cdd52044b28b4c19ac0603da4e502e6 100644 --- a/tests/fpgadataflow/test_fpgadataflow_res_estimate.py +++ b/tests/fpgadataflow/test_fpgadataflow_res_estimate.py @@ -92,7 +92,7 @@ def test_res_estimate(): model = model.transform(GiveUniqueNodeNames()) prod_resource_estimation = model.analysis(res_estimation) expect_resource_estimation = { - "StreamingFCLayer_Batch_0": ["BRAMs: 1", "LUTs: 304.4"] + "StreamingFCLayer_Batch_0": {"BRAM_18K": 1, "LUT": 304.4} } assert check_two_dict_for_equality(