diff --git a/src/finn/core/execute_custom_node.py b/src/finn/core/execute_custom_node.py index 0294e9ff6e0c59692a3e93d574bf8e95d9fad9e0..e8ad421e6bff58048e53a59ab3e0a527d7b67f4f 100644 --- a/src/finn/core/execute_custom_node.py +++ b/src/finn/core/execute_custom_node.py @@ -8,7 +8,7 @@ def execute_custom_node(node, context, graph): try: # lookup op_type in registry of CustomOps inst = registry.custom_op[op_type](node) - inst.execute_node(node, context, graph) + inst.execute_node(context, graph) except KeyError: # exception if op_type is not supported raise Exception("Custom op_type %s is currently not supported." % op_type) diff --git a/src/finn/custom_op/__init__.py b/src/finn/custom_op/__init__.py index c15bc8eb0b4d5a6230240180d171f174e17e1dd5..e187ba5704cedd13e4bfe0d7241cdd6b18dfc2c9 100644 --- a/src/finn/custom_op/__init__.py +++ b/src/finn/custom_op/__init__.py @@ -8,13 +8,13 @@ class CustomOp(ABC): # TODO consider specifying a list of allowed attributes @abstractmethod - def make_shape_compatible_op(self, node): + def make_shape_compatible_op(self): pass @abstractmethod - def infer_node_datatype(self, node, model): + def infer_node_datatype(self, model): pass @abstractmethod - def execute_node(self, node, context, graph): + def execute_node(self, context, graph): pass diff --git a/src/finn/custom_op/fpgadataflow/__init__.py b/src/finn/custom_op/fpgadataflow/__init__.py index 3dfab19a01b31f3eddfb79938d4f15a2433cc3cc..dbceefb30f7886cfe481c236766c25a56ab3efa1 100644 --- a/src/finn/custom_op/fpgadataflow/__init__.py +++ b/src/finn/custom_op/fpgadataflow/__init__.py @@ -38,18 +38,19 @@ class HLSCustomOp(CustomOp): self.tmp_dir = " " @abstractmethod - def get_attributes(self, node): + def get_attributes(self): pass - def code_generation(self, node): - self.get_attributes(node) - self.global_includes(node) - self.defines(node) - self.read_npy_data(node) - self.strm_decl(node) - self.docompute(node) - self.dataoutstrm(node) - self.save_as_npy(node) + def code_generation(self): + node = self.onnx_node + self.get_attributes() + self.global_includes() + self.defines() + self.read_npy_data() + self.strm_decl() + self.docompute() + self.dataoutstrm() + self.save_as_npy() template = self.docompute_template @@ -63,29 +64,29 @@ class HLSCustomOp(CustomOp): f.close() @abstractmethod - def global_includes(self, node): + def global_includes(self): pass @abstractmethod - def defines(self, node): + def defines(self): pass @abstractmethod - def read_npy_data(self, node): + def read_npy_data(self): pass @abstractmethod - def strm_decl(self, node): + def strm_decl(self): pass @abstractmethod - def docompute(self, node): + def docompute(self): pass @abstractmethod - def dataoutstrm(self, node): + def dataoutstrm(self): pass @abstractmethod - def save_as_npy(self, node): + def save_as_npy(self): pass diff --git a/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py b/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py index e8f75ce0219e2b2977b3f77629c12c4919a52ad2..b8540afcaccb4d73fecf9267adc009d5f7f78a98 100644 --- a/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py @@ -16,13 +16,14 @@ class StreamingFCLayer_Batch(HLSCustomOp): self.WMEM = 0 self.TMEM = 0 - def make_shape_compatible_op(self, node): + def make_shape_compatible_op(self): pass - def infer_node_datatype(self, node, model): + def infer_node_datatype(self, model): pass - def execute_node(self, node, context, graph): + def execute_node(self, context, graph): + node = self.onnx_node # make temporary directory for generated files self.tmp_dir = tmp.mkdtemp(prefix=str(node.op_type) + "_") @@ -31,7 +32,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): temp_files = [] # get attributes for correct packing of weights and thresholds - self.get_attributes(node) + self.get_attributes() # create a npy file fore each input of the node (in_ind is input index) in_ind = 0 @@ -89,7 +90,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): in_ind += 1 # code generation - self.code_generation(node) + self.code_generation() # c++ compilation and execution flow temp_files.append("{}/execute_{}.cpp".format(self.tmp_dir, node.op_type)) @@ -114,7 +115,8 @@ class StreamingFCLayer_Batch(HLSCustomOp): # for temp_file in temp_files: # os.remove(temp_file) - def get_attributes(self, node): + def get_attributes(self): + node = self.onnx_node self.resType = utils.get_by_name(node.attribute, "resType").s.decode("utf-8") self.MW = utils.get_by_name(node.attribute, "MW").i self.MH = utils.get_by_name(node.attribute, "MH").i @@ -124,7 +126,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): "utf-8" ) - def global_includes(self, node): + def global_includes(self): self.code_gen_dict["$GLOBALS$"] = '#include "weights.hpp" \n' self.code_gen_dict["$GLOBALS$"] += '#include "activations.hpp" \n' if self.WMEM != 0: @@ -134,7 +136,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): # TODO find a better way of checking for no pregenerated thresholds self.code_gen_dict["$GLOBALS$"] += '#include "thresh.h" \n' - def defines(self, node): + def defines(self): numReps = 2 self.code_gen_dict["$DEFINES$"] = [ """#define MW1 {}\n #define MH1 {}\n #define SIMD1 {}\n @@ -144,7 +146,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): ) ] - def read_npy_data(self, node): + def read_npy_data(self): # c++ code to read out an npy file # and put it in hls::stream in the correct order self.code_gen_dict["$READNPYDATA$"] = [] @@ -175,7 +177,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): self.code_gen_dict["$READNPYDATA$"].append("in0 << dat0;") self.code_gen_dict["$READNPYDATA$"].append("}") - def strm_decl(self, node): + def strm_decl(self): self.code_gen_dict["$STREAMDECLARATIONS$"] = [] self.code_gen_dict["$STREAMDECLARATIONS$"].append( 'hls::stream<ap_uint<{}>> in0 ("in0");'.format(self.SIMD) @@ -184,7 +186,8 @@ class StreamingFCLayer_Batch(HLSCustomOp): 'hls::stream<ap_uint<{}>> out ("out");'.format(self.PE) ) - def docompute(self, node): + def docompute(self): + node = self.onnx_node self.code_gen_dict["$DOCOMPUTE$"] = [ """{}<MW1, MH1, SIMD1, PE1, {}> (in0, out, weights, threshs, numReps, {});""".format( @@ -192,7 +195,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): ) ] - def dataoutstrm(self, node): + def dataoutstrm(self): self.code_gen_dict["$DATAOUTSTREAM$"] = [ "ap_uint<{}> out_data;\n std::vector<ap_uint<{}>> out_data_vector;".format( self.PE, self.PE @@ -222,7 +225,7 @@ class StreamingFCLayer_Batch(HLSCustomOp): ) self.code_gen_dict["$DATAOUTSTREAM$"].append("}") - def save_as_npy(self, node): + def save_as_npy(self): self.code_gen_dict["$SAVEASCNPY$"] = [ """cnpy::npy_save("{}/output.npy",&output_data_vector[0], {{1,{},{}}},"w");""".format( diff --git a/src/finn/custom_op/fpgadataflow/streamingmaxpool.py b/src/finn/custom_op/fpgadataflow/streamingmaxpool.py index e137951eaa63e9ec50c4cbf173fa4bf040d70a07..212f91ca5eaa6778e871e6b4bce1bb548eae60d2 100644 --- a/src/finn/custom_op/fpgadataflow/streamingmaxpool.py +++ b/src/finn/custom_op/fpgadataflow/streamingmaxpool.py @@ -9,13 +9,14 @@ from finn.custom_op.fpgadataflow import HLSCustomOp class StreamingMaxPool(HLSCustomOp): - def make_shape_compatible_op(self, node): + def make_shape_compatible_op(self): pass - def infer_node_datatype(self, node, model): + def infer_node_datatype(self, model): pass - def execute_node(self, node, context, graph): + def execute_node(self, context, graph): + node = self.onnx_node # make temporary directory for generated files self.tmp_dir = tmp.mkdtemp(prefix=str(node.op_type) + "_") @@ -34,7 +35,7 @@ class StreamingMaxPool(HLSCustomOp): in_ind += 1 # code generation - self.code_generation(node) + self.code_generation() # c++ compilation and execution flow temp_files.append("{}/execute_{}.cpp".format(self.tmp_dir, node.op_type)) @@ -59,22 +60,24 @@ class StreamingMaxPool(HLSCustomOp): # for temp_file in temp_files: # os.remove(temp_file) - def get_attributes(self, node): + def get_attributes(self): + node = self.onnx_node self.ImgDim = get_by_name(node.attribute, "ImgDim").i self.PoolDim = get_by_name(node.attribute, "PoolDim").i self.NumChannels = get_by_name(node.attribute, "NumChannels").i - def global_includes(self, node): + def global_includes(self): self.code_gen_dict["$GLOBALS$"] = ['#include "maxpool.h"'] - def defines(self, node): + def defines(self): self.code_gen_dict["$DEFINES$"] = [ "#define ImgDim {}\n #define PoolDim {}\n #define NumChannels {}".format( self.ImgDim, self.PoolDim, self.NumChannels ) ] - def read_npy_data(self, node): + def read_npy_data(self): + node = self.onnx_node # c++ code to read out an npy file # and put it in hls::stream in the correct order self.code_gen_dict["$READNPYDATA$"] = [] @@ -113,7 +116,8 @@ class StreamingMaxPool(HLSCustomOp): self.code_gen_dict["$READNPYDATA$"].append("}") input_ind += 1 - def strm_decl(self, node): + def strm_decl(self): + node = self.onnx_node self.code_gen_dict["$STREAMDECLARATIONS$"] = [] input_ind = 0 for inputs in node.input: @@ -127,12 +131,13 @@ class StreamingMaxPool(HLSCustomOp): 'hls::stream<ap_uint<{}>> out ("out");'.format(self.NumChannels) ) - def docompute(self, node): + def docompute(self): + node = self.onnx_node self.code_gen_dict["$DOCOMPUTE$"] = [ "{}<ImgDim, PoolDim, NumChannels>(in0, out);".format(node.op_type) ] - def dataoutstrm(self, node): + def dataoutstrm(self): self.code_gen_dict["$DATAOUTSTREAM$"] = [ "ap_uint<{}> out_data;\n std::vector<ap_uint<{}>> out_data_vector;".format( self.NumChannels, self.NumChannels @@ -162,7 +167,7 @@ class StreamingMaxPool(HLSCustomOp): ) self.code_gen_dict["$DATAOUTSTREAM$"].append("}") - def save_as_npy(self, node): + def save_as_npy(self): self.code_gen_dict["$SAVEASCNPY$"] = [ """cnpy::npy_save("{}/output.npy",&output_data_vector[0], {{{},{},{}}},"w");""".format( diff --git a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py index c9b5b0b37726173c6a35179ec9107fb11fcdd8a3..11705e34c06cd045bc24a34eff14c548559e515b 100644 --- a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py @@ -9,13 +9,14 @@ from finn.custom_op.fpgadataflow import HLSCustomOp class StreamingMaxPool_Batch(HLSCustomOp): - def make_shape_compatible_op(self, node): + def make_shape_compatible_op(self): pass - def infer_node_datatype(self, node, model): + def infer_node_datatype(self, model): pass - def execute_node(self, node, context, graph): + def execute_node(self, context, graph): + node = self.onnx_node # make temporary directory for generated files self.tmp_dir = tmp.mkdtemp(prefix=str(node.op_type) + "_") @@ -34,7 +35,7 @@ class StreamingMaxPool_Batch(HLSCustomOp): in_ind += 1 # code generation - self.code_generation(node) + self.code_generation() # c++ compilation and execution flow temp_files.append("{}/execute_{}.cpp".format(self.tmp_dir, node.op_type)) @@ -59,15 +60,16 @@ class StreamingMaxPool_Batch(HLSCustomOp): # for temp_file in temp_files: # os.remove(temp_file) - def get_attributes(self, node): + def get_attributes(self): + node = self.onnx_node self.ImgDim = get_by_name(node.attribute, "ImgDim").i self.PoolDim = get_by_name(node.attribute, "PoolDim").i self.NumChannels = get_by_name(node.attribute, "NumChannels").i - def global_includes(self, node): + def global_includes(self): self.code_gen_dict["$GLOBALS$"] = ['#include "maxpool.h"'] - def defines(self, node): + def defines(self): numReps = 2 self.code_gen_dict["$DEFINES$"] = [ """#define ImgDim {}\n #define PoolDim {}\n @@ -76,7 +78,8 @@ class StreamingMaxPool_Batch(HLSCustomOp): ) ] - def read_npy_data(self, node): + def read_npy_data(self): + node = self.onnx_node # c++ code to read out an npy file # and put it in hls::stream in the correct order self.code_gen_dict["$READNPYDATA$"] = [] @@ -115,7 +118,8 @@ class StreamingMaxPool_Batch(HLSCustomOp): self.code_gen_dict["$READNPYDATA$"].append("}") input_ind += 1 - def strm_decl(self, node): + def strm_decl(self): + node = self.onnx_node self.code_gen_dict["$STREAMDECLARATIONS$"] = [] input_ind = 0 for inputs in node.input: @@ -129,12 +133,13 @@ class StreamingMaxPool_Batch(HLSCustomOp): 'hls::stream<ap_uint<{}>> out ("out");'.format(self.NumChannels) ) - def docompute(self, node): + def docompute(self): + node = self.onnx_node self.code_gen_dict["$DOCOMPUTE$"] = [ "{}<ImgDim, PoolDim, NumChannels>(in0, out, numReps);".format(node.op_type) ] - def dataoutstrm(self, node): + def dataoutstrm(self): self.code_gen_dict["$DATAOUTSTREAM$"] = [ "ap_uint<{}> out_data;\n std::vector<ap_uint<{}>> out_data_vector;".format( self.NumChannels, self.NumChannels @@ -164,7 +169,7 @@ class StreamingMaxPool_Batch(HLSCustomOp): ) self.code_gen_dict["$DATAOUTSTREAM$"].append("}") - def save_as_npy(self, node): + def save_as_npy(self): numReps = 2 self.code_gen_dict["$SAVEASCNPY$"] = [ """cnpy::npy_save("{}/output.npy",&output_data_vector[0], diff --git a/src/finn/custom_op/multithreshold.py b/src/finn/custom_op/multithreshold.py index 16c0c302725b539dc8a110b9a8d3e5dcc8b2102b..5d471db8e5e5df198b42403247f9a7b3b44fba59 100644 --- a/src/finn/custom_op/multithreshold.py +++ b/src/finn/custom_op/multithreshold.py @@ -58,10 +58,12 @@ def multithreshold(v, thresholds, out_scale=None, out_bias=None): class MultiThreshold(CustomOp): - def make_shape_compatible_op(self, node): + def make_shape_compatible_op(self): + node = self.onnx_node return helper.make_node("Relu", [node.input[0]], [node.output[0]]) - def infer_node_datatype(self, node, model): + def infer_node_datatype(self, model): + node = self.onnx_node try: odt = get_by_name(node.attribute, "out_dtype").s.decode("utf-8") model.set_tensor_datatype(node.output[0], DataType[odt]) @@ -72,7 +74,8 @@ class MultiThreshold(CustomOp): odtype = DataType.get_smallest_possible(n_thres) model.set_tensor_datatype(node.output[0], odtype) - def execute_node(self, node, context, graph): + def execute_node(self, context, graph): + node = self.onnx_node # save inputs v = context[node.input[0]] thresholds = context[node.input[1]] diff --git a/src/finn/custom_op/xnorpopcount.py b/src/finn/custom_op/xnorpopcount.py index 98ff123dad7bed0f87ed2ac3b03bb4727f5cb62e..134a299c1cad8a9433e0c101018960d7640b1fbf 100644 --- a/src/finn/custom_op/xnorpopcount.py +++ b/src/finn/custom_op/xnorpopcount.py @@ -31,19 +31,22 @@ def xnorpopcountmatmul(inp0, inp1): class XnorPopcountMatMul(CustomOp): - def make_shape_compatible_op(self, node): + def make_shape_compatible_op(self): + node = self.onnx_node return helper.make_node( "MatMul", [node.input[0], node.input[1]], [node.output[0]] ) - def infer_node_datatype(self, node, model): + def infer_node_datatype(self, model): + node = self.onnx_node # ensure inputs are binary assert model.get_tensor_datatype(node.input[0]) == DataType["BINARY"] assert model.get_tensor_datatype(node.input[1]) == DataType["BINARY"] # XNOR-popcount produces unsigned integers, assume uint32 model.set_tensor_datatype(node.output[0], DataType["UINT32"]) - def execute_node(self, node, context, graph): + def execute_node(self, context, graph): + node = self.onnx_node # save inputs inp0 = context[node.input[0]] inp1 = context[node.input[1]] diff --git a/src/finn/transformation/infer_datatypes.py b/src/finn/transformation/infer_datatypes.py index 83da0baacb04217c9ba56cd427f75b34ca5f764d..eaade9ebb7f09e39442e9cb8baba7c257720b45b 100644 --- a/src/finn/transformation/infer_datatypes.py +++ b/src/finn/transformation/infer_datatypes.py @@ -14,7 +14,7 @@ def _infer_node_datatype(model, node): try: # lookup op_type in registry of CustomOps inst = registry.custom_op[op_type](node) - inst.infer_node_datatype(node, model) + inst.infer_node_datatype(model) except KeyError: # exception if op_type is not supported raise Exception("Custom op_type %s is currently not supported." % op_type) diff --git a/src/finn/transformation/infer_shapes.py b/src/finn/transformation/infer_shapes.py index eaf91dd7955fff68427f13d0b5d84388839d0ff9..2bddc44673bcd09305401a9d50033141795b00c9 100644 --- a/src/finn/transformation/infer_shapes.py +++ b/src/finn/transformation/infer_shapes.py @@ -13,7 +13,7 @@ def _make_shape_compatible_op(node): try: # lookup op_type in registry of CustomOps inst = registry.custom_op[op_type](node) - return inst.make_shape_compatible_op(node) + return inst.make_shape_compatible_op() except KeyError: # exception if op_type is not supported raise Exception("Custom op_type %s is currently not supported." % op_type)