diff --git a/src/finn/core/utils.py b/src/finn/core/utils.py
index 0ea2392d903a7ea570b6972a33fab3945a051b43..0a6e5718d86edb861046b2753b4ab8a4b594c5cc 100644
--- a/src/finn/core/utils.py
+++ b/src/finn/core/utils.py
@@ -1,6 +1,7 @@
 import random
 import string
 import subprocess
+import os
 
 import numpy as np
 import onnx
@@ -248,3 +249,31 @@ class CppBuilder:
         bash_command = ["bash", self.compile_script]
         process_compile = subprocess.Popen(bash_command, stdout=subprocess.PIPE)
         process_compile.communicate()
+
+class IPGenBuilder:
+    def __init__(self):
+        self.tcl_script = ""
+        self.ipgen_path = ""
+        self.code_gen_dir = ""
+        self.ipgen_script=""
+
+    def append_tcl(self, tcl_script):
+        self.tcl_script = tcl_script
+
+    def set_ipgen_path(self, path):
+        self.ipgen_path = path
+
+    def build(self, code_gen_dir):
+        self.code_gen_dir = code_gen_dir
+        self.ipgen_script = str(self.code_gen_dir) + "/ipgen.sh"
+        working_dir = os.environ["PWD"]
+        f = open(self.ipgen_script, "w")
+        f.write("#!/bin/bash \n")
+        f.write("cd {}\n".format(code_gen_dir))
+        f.write("vivado_hls {}\n".format(self.tcl_script))
+        f.write("cd {}\n".format(working_dir))
+        f.close()
+        bash_command = ["bash", self.ipgen_script]
+        process_compile = subprocess.Popen(bash_command, stdout=subprocess.PIPE)
+        process_compile.communicate()
+        
diff --git a/src/finn/custom_op/fpgadataflow/__init__.py b/src/finn/custom_op/fpgadataflow/__init__.py
index be2e9e3a3f19145b2505c10d4f81eeac1853982a..5a9634896d440d1dd4ddd8291e731e8ca44f8505 100644
--- a/src/finn/custom_op/fpgadataflow/__init__.py
+++ b/src/finn/custom_op/fpgadataflow/__init__.py
@@ -3,7 +3,7 @@ import numpy as np
 import os
 import subprocess
 from finn.custom_op import CustomOp
-from finn.core.utils import CppBuilder
+from finn.core.utils import CppBuilder, IPGenBuilder
 
 
 class HLSCustomOp(CustomOp):
@@ -68,8 +68,7 @@ class HLSCustomOp(CustomOp):
         set config_clkperiod $CLKPERIOD$
 
         open_project $config_proj_name
-        add_files $config_hwsrcdir/top_$TOPFXN$.cpp -cflags
-        "-std=c++0x -I$config_bnnlibdir"
+        add_files $config_hwsrcdir/top_$TOPFXN$.cpp -cflags "-std=c++0x -I$config_bnnlibdir"
 
         set_top $config_toplevelfxn
         open_solution sol1
@@ -89,6 +88,7 @@ class HLSCustomOp(CustomOp):
             "code_gen_dir_npysim": ("s", False, ""),
             "code_gen_dir_ipgen": ("s", False, ""),
             "executable_path": ("s", False, ""),
+            "ipgen_path": ("s", False, ""),
         }
 
     def code_generation_ipgen(self, model, fpgapart, clk):
@@ -133,6 +133,15 @@ class HLSCustomOp(CustomOp):
         f.close()
         self.code_gen_dict.clear()
 
+    def ipgen_singlenode_code(self):
+        node = self.onnx_node
+        code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen")
+        builder = IPGenBuilder()
+        builder.append_tcl(code_gen_dir + "/hls_syn_{}.tcl".format(node.name))
+        builder.set_ipgen_path(code_gen_dir + "/project_{}".format(node.name))
+        builder.build(code_gen_dir)
+        self.set_nodeattr("ipgen_path", builder.ipgen_path)
+
     def code_generation_npysim(self, model):
         node = self.onnx_node
         self.generate_params(model)
diff --git a/src/finn/transformation/fpgadataflow/ipgen.py b/src/finn/transformation/fpgadataflow/ipgen.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9457863fdc7bb3b2c70784887d0c8f1e016c026
--- /dev/null
+++ b/src/finn/transformation/fpgadataflow/ipgen.py
@@ -0,0 +1,36 @@
+import finn.core.utils as util
+import finn.custom_op.registry as registry
+from finn.transformation import Transformation
+
+
+class IPGen(Transformation):
+    """Compile for all nodes in model"""
+
+    def __init__(self):
+        super().__init__()
+
+    def apply(self, model):
+        for node in model.graph.node:
+            op_type = node.op_type
+            if node.domain == "finn":
+                backend_attribute = util.get_by_name(node.attribute, "backend")
+                if backend_attribute is None:
+                    continue
+                backend_value = backend_attribute.s.decode("UTF-8")
+                if backend_value == "fpgadataflow":
+                    try:
+                        # lookup op_type in registry of CustomOps
+                        inst = registry.custom_op[op_type](node)
+                        # ensure that code is generated
+                        assert inst.get_nodeattr("code_gen_dir_ipgen") != ""
+                        # call the compilation function for this node
+                        inst.ipgen_singlenode_code()
+                        # ensure that executable path is now set
+                        assert inst.get_nodeattr("ipgen_path") != ""
+                    except KeyError:
+                        # exception if op_type is not supported
+                        raise Exception(
+                            "Custom op_type %s is currently not supported." % op_type
+                        )
+        return (model, False)
+