From 4a9989dff13225862f09cd2f19ad9124c9fe1132 Mon Sep 17 00:00:00 2001 From: Yaman Umuroglu <maltanar@gmail.com> Date: Tue, 3 Dec 2019 00:50:02 +0000 Subject: [PATCH] [CodeGen] refactor the single node compilation transformation --- src/finn/core/utils.py | 58 +++++-------------- src/finn/custom_op/fpgadataflow/__init__.py | 16 +++++ .../fpgadataflow/streamingfclayer_batch.py | 2 +- .../fpgadataflow/streamingmaxpool_batch.py | 2 +- .../compilation_transformation.py | 48 +++++---------- 5 files changed, 49 insertions(+), 77 deletions(-) diff --git a/src/finn/core/utils.py b/src/finn/core/utils.py index e036c9134..794661e4d 100644 --- a/src/finn/core/utils.py +++ b/src/finn/core/utils.py @@ -1,5 +1,6 @@ import random import string +import subprocess import numpy as np import onnx @@ -195,7 +196,7 @@ def gen_finn_dt_tensor(finn_dt, tensor_shape): return tensor_values.astype(np.float32) -class CallCppCompiler: +class CppBuilder: def __init__(self): self.include_paths = [] self.cpp_files = [] @@ -207,56 +208,27 @@ class CallCppCompiler: def append_includes(self, library_path): self.include_paths.append(library_path) - def prepare_cpp_files(self, node): - if not self.code_gen_dir: - raise ValueError( - """There is no generated code to compile - for node of op type {}""".format( - node.op_type - ) - ) - else: - self.cpp_files.append( - str(self.code_gen_dir) + "/execute_" + str(node.op_type) + ".cpp" - ) - for lib in self.include_paths: - if "cnpy" in lib: - self.cpp_files.append("/workspace/cnpy/cnpy.cpp") - self.append_includes("-lz") - - def set_executable_path(self, node): - if not self.code_gen_dir: - raise ValueError( - """There is no generated code to compile - for node of op type {}""".format( - node.op_type - ) - ) - else: - self.executable_path = ( - str(self.code_gen_dir) + "/execute_" + str(node.op_type) - ) + def append_sources(self, cpp_file): + self.cpp_files.append(cpp_file) - def build(self, node): + def set_executable_path(self, path): + self.executable_path = path + + def build(self, code_gen_dir): # raise error if includes are empty - self.code_gen_dir = (get_by_name(node.attribute, "code_gen_dir")).s.decode( - "UTF-8" - ) - self.prepare_cpp_files(node) - self.set_executable_path(node) + self.code_gen_dir = code_gen_dir self.compile_components.append("g++ -o " + str(self.executable_path)) for cpp_file in self.cpp_files: self.compile_components.append(cpp_file) for lib in self.include_paths: self.compile_components.append(lib) - bash_compile = "" - for component in self.compile_components: bash_compile += str(component) + " " - self.compile_script = str(self.code_gen_dir) + "/compile.sh" - f = open(self.compile_script, "w") - f.write("#!/bin/sh \n") - f.write(bash_compile) - f.close() + with open(self.compile_script, "w") as f: + f.write("#!/bin/bash \n") + f.write(bash_compile + "\n") + bash_command = ["bash", self.compile_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 ae8d8aa69..0d9a306ba 100644 --- a/src/finn/custom_op/fpgadataflow/__init__.py +++ b/src/finn/custom_op/fpgadataflow/__init__.py @@ -1,6 +1,7 @@ from abc import abstractmethod import os from finn.custom_op import CustomOp +from finn.core.utils import CppBuilder class HLSCustomOp(CustomOp): @@ -62,6 +63,21 @@ class HLSCustomOp(CustomOp): f.write(template) f.close() + def compile_singlenode_code(self): + code_gen_dir = self.get_nodeattr("code_gen_dir") + builder = CppBuilder() + builder.append_includes("-I/workspace/finn/src/finn/data/cpp") + builder.append_includes("-I/workspace/cnpy/") + builder.append_includes("-I/workspace/finn-hlslib") + builder.append_includes("-I/workspace/vivado-hlslib") + builder.append_includes("--std=c++11") + builder.append_sources(code_gen_dir + "/*.cpp") + builder.append_sources("/workspace/cnpy/cnpy.cpp") + builder.append_includes("-lz") + builder.set_executable_path(code_gen_dir + "/node_model") + builder.build(code_gen_dir) + self.set_nodeattr("executable_path", builder.executable_path) + @abstractmethod def generate_weights(self, context): pass diff --git a/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py b/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py index 97831485d..fdc66ce2b 100644 --- a/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py @@ -247,7 +247,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): in_ind += 1 # execute precompiled executable executable_path = self.get_nodeattr("executable_path") - # TODO sanity check executable + assert executable_path != "" process_execute = subprocess.Popen(executable_path, stdout=subprocess.PIPE) process_execute.communicate() # load output npy file diff --git a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py index 56d43cac7..8f497168e 100644 --- a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py @@ -38,7 +38,7 @@ class StreamingMaxPool_Batch(HLSCustomOp): in_ind += 1 # execute precompiled executable executable_path = self.get_nodeattr("executable_path") - # TODO sanity check executable + assert executable_path != "" process_execute = subprocess.Popen(executable_path, stdout=subprocess.PIPE) process_execute.communicate() # load output npy file diff --git a/src/finn/transformation/fpgadataflow/compilation_transformation.py b/src/finn/transformation/fpgadataflow/compilation_transformation.py index d06af8682..8d158c4a5 100644 --- a/src/finn/transformation/fpgadataflow/compilation_transformation.py +++ b/src/finn/transformation/fpgadataflow/compilation_transformation.py @@ -1,7 +1,5 @@ -import subprocess - import finn.core.utils as util -from finn.core.utils import CallCppCompiler +import finn.custom_op.registry as registry from finn.transformation import Transformation @@ -10,40 +8,26 @@ class Compilation(Transformation): def __init__(self): super().__init__() - self.compiler_call = CallCppCompiler() - - def get_includes(self): - # step by step addition of include paths to ensure easy extension - self.compiler_call.append_includes("-I/workspace/finn/src/finn/data/cpp") - self.compiler_call.append_includes("-I/workspace/cnpy/") - self.compiler_call.append_includes("-I/workspace/finn-hlslib") - self.compiler_call.append_includes("-I/workspace/vivado-hlslib") - self.compiler_call.append_includes("--std=c++11") - - def prepare_bash_command(self, node): - self.get_includes() - self.compiler_call.build(node) - bash_command = "chmod +x " + str(self.compiler_call.compile_script) - process_compile = subprocess.Popen(bash_command.split(), stdout=subprocess.PIPE) - process_compile.communicate() - print(self.compiler_call.code_gen_dir) 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") backend_value = backend_attribute.s.decode("UTF-8") if backend_value == "fpgadataflow": - self.prepare_bash_command(node) - bash_command = self.compiler_call.compile_script - process_compile = subprocess.Popen( - bash_command.split(), stdout=subprocess.PIPE - ) - process_compile.communicate() - - model.set_attribute( - node, "executable_path", self.compiler_call.executable_path - ) - + 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") != "" + # call the compilation function for this node + inst.compile_singlenode_code() + # ensure that executable path is now set + assert inst.get_nodeattr("executable_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) -- GitLab