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(