diff --git a/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py b/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py index 6685f3c17b6a2299480c6fa24118db854330eda3..519d3466ee8ac2274b851c39fe6f254ade777f48 100644 --- a/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py @@ -1,8 +1,9 @@ import os import subprocess -import numpy as np import tempfile as tmp +import numpy as np + import finn.core.utils as utils from finn.backend.fpgadataflow.utils import numpy_to_hls_code from finn.core.datatype import DataType @@ -22,11 +23,22 @@ class StreamingFCLayer_Batch(HLSCustomOp): pass def execute_node(self, node, context, graph): + # make temporary directory for generated files self.tmp_dir = tmp.mkdtemp(prefix=str(node.op_type) + "_") + + # create empty list for temporary files to enable the option + # to delete the files after the execution + temp_files = [] + + # get attributes for correct packing of weights and thresholds self.get_attributes(node) + + # create a npy file fore each input of the node (in_ind is input index) in_ind = 0 - temp_files = [] for inputs in node.input: + # it is assumed that the first input of the node is the data input + # the second input are the weights + # the third input are the thresholds if in_ind == 0: np.save( os.path.join(self.tmp_dir, "input_{}.npy".format(in_ind)), @@ -36,12 +48,15 @@ class StreamingFCLayer_Batch(HLSCustomOp): elif in_ind == 1: weights = context[inputs] self.WMEM = weights.shape[2] + # transpose and expand the weights to get the right shape + # for the code generation weights = np.transpose(weights, (1, 2, 0)) weights = np.expand_dims(weights, 0) weights = numpy_to_hls_code( weights, DataType.BINARY, "weights", True, True ) + # write weights into params.h f_weights = open("{}/params.h".format(self.tmp_dir), "w") f_weights.write( "static BinaryWeights<{},{},{}> weights = ".format( @@ -55,11 +70,15 @@ class StreamingFCLayer_Batch(HLSCustomOp): else: thresholds = context[inputs] self.TMEM = thresholds.shape[0] + # transpose and expand the thresholds to get the right shape + # for the code generation thresholds = np.transpose(thresholds, (1, 0, 2)) thresholds = np.expand_dims(thresholds, 0) thresholds = numpy_to_hls_code( thresholds, DataType.BINARY, "thresholds", True, True ) + + # write weights into thresh.h f_thresh = open("{}/thresh.h".format(self.tmp_dir), "w") f_thresh.write( """static ThresholdsActivation<{},{},1,ap_uint<16>, @@ -73,7 +92,10 @@ class StreamingFCLayer_Batch(HLSCustomOp): in_ind += 1 + # code generation self.code_generation(node) + + # c++ compilation and execution flow temp_files.append("{}/execute_{}.cpp".format(self.tmp_dir, node.op_type)) bash_compile = """g++ -o {}/execute_{} {}/execute_{}.cpp /workspace/cnpy/cnpy.cpp -I/workspace/cnpy/ @@ -88,6 +110,8 @@ class StreamingFCLayer_Batch(HLSCustomOp): process_execute.communicate() temp_files.append("{}/execute_{}".format(self.tmp_dir, node.op_type)) temp_files.append("{}/output.npy".format(self.tmp_dir)) + + # load output npy file output = np.load("{}/output.npy".format(self.tmp_dir)) context[node.output[0]] = output # deleting temporary files @@ -121,6 +145,8 @@ class StreamingFCLayer_Batch(HLSCustomOp): ] def read_npy_data(self, node): + # c++ code to read out an npy file + # and put it in hls::stream in the correct order self.code_gen_dict["$READNPYDATA$"] = [] self.code_gen_dict["$READNPYDATA$"].append( """cnpy::NpyArray arr0 = cnpy::npy_load("{}/input_0.npy");\n diff --git a/src/finn/custom_op/fpgadataflow/streamingmaxpool.py b/src/finn/custom_op/fpgadataflow/streamingmaxpool.py index 3387f15b4bb54da131998fc2507579d400681b8d..e137951eaa63e9ec50c4cbf173fa4bf040d70a07 100644 --- a/src/finn/custom_op/fpgadataflow/streamingmaxpool.py +++ b/src/finn/custom_op/fpgadataflow/streamingmaxpool.py @@ -1,8 +1,8 @@ import os -import numpy as np import subprocess import tempfile as tmp +import numpy as np from finn.core.utils import get_by_name from finn.custom_op.fpgadataflow import HLSCustomOp @@ -16,22 +16,34 @@ class StreamingMaxPool(HLSCustomOp): pass def execute_node(self, node, context, graph): + # make temporary directory for generated files self.tmp_dir = tmp.mkdtemp(prefix=str(node.op_type) + "_") - in_ind = 0 + + # create empty list for temporary files to enable the option + # to delete the files after the execution temp_files = [] + + # create a npy file fore each input of the node (in_ind is input index) + in_ind = 0 for inputs in node.input: - np.save(os.path.join(self.tmp_dir, "input_{}.npy".format(in_ind)), - context[inputs],) + np.save( + os.path.join(self.tmp_dir, "input_{}.npy".format(in_ind)), + context[inputs], + ) temp_files.append("{}/input_{}.npy".format(self.tmp_dir, in_ind)) in_ind += 1 + + # code generation self.code_generation(node) + + # c++ compilation and execution flow temp_files.append("{}/execute_{}.cpp".format(self.tmp_dir, node.op_type)) bash_compile = """g++ -o {}/execute_{} {}/execute_{}.cpp /workspace/cnpy/cnpy.cpp -I/workspace/cnpy/ -I/workspace/finn-hlslib -I/workspace/vivado-hlslib --std=c++11 -lz""".format( self.tmp_dir, node.op_type, self.tmp_dir, node.op_type - ) + ) process_compile = subprocess.Popen(bash_compile.split(), stdout=subprocess.PIPE) process_compile.communicate() bash_execute = "{}/execute_{}".format(self.tmp_dir, node.op_type) @@ -39,13 +51,14 @@ class StreamingMaxPool(HLSCustomOp): process_execute.communicate() temp_files.append("{}/execute_{}".format(self.tmp_dir, node.op_type)) temp_files.append("{}/output.npy".format(self.tmp_dir)) - output = np.load("{}/output.npy".format(self.tmp_dir)) + + # load output npy file + output = np.load("{}/output.npy".format(self.tmp_dir)) context[node.output[0]] = output # deleting temporary files - #for temp_file in temp_files: + # for temp_file in temp_files: # os.remove(temp_file) - def get_attributes(self, node): self.ImgDim = get_by_name(node.attribute, "ImgDim").i self.PoolDim = get_by_name(node.attribute, "PoolDim").i @@ -62,6 +75,8 @@ class StreamingMaxPool(HLSCustomOp): ] def read_npy_data(self, node): + # c++ code to read out an npy file + # and put it in hls::stream in the correct order self.code_gen_dict["$READNPYDATA$"] = [] input_ind = 0 input_file_names = [] diff --git a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py index a89c4a07ec22f27b7b2cb3855105621e896cb650..c9b5b0b37726173c6a35179ec9107fb11fcdd8a3 100644 --- a/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py +++ b/src/finn/custom_op/fpgadataflow/streamingmaxpool_batch.py @@ -1,8 +1,8 @@ import os -import numpy as np import subprocess import tempfile as tmp +import numpy as np from finn.core.utils import get_by_name from finn.custom_op.fpgadataflow import HLSCustomOp @@ -14,24 +14,36 @@ class StreamingMaxPool_Batch(HLSCustomOp): def infer_node_datatype(self, node, model): pass - + def execute_node(self, node, context, graph): + # make temporary directory for generated files self.tmp_dir = tmp.mkdtemp(prefix=str(node.op_type) + "_") - in_ind = 0 + + # create empty list for temporary files to enable the option + # to delete the files after the execution temp_files = [] + + # create a npy file fore each input of the node (in_ind is input index) + in_ind = 0 for inputs in node.input: - np.save(os.path.join(self.tmp_dir, "input_{}.npy".format(in_ind)), - context[inputs],) + np.save( + os.path.join(self.tmp_dir, "input_{}.npy".format(in_ind)), + context[inputs], + ) temp_files.append("{}/input_{}.npy".format(self.tmp_dir, in_ind)) in_ind += 1 + + # code generation self.code_generation(node) + + # c++ compilation and execution flow temp_files.append("{}/execute_{}.cpp".format(self.tmp_dir, node.op_type)) bash_compile = """g++ -o {}/execute_{} {}/execute_{}.cpp /workspace/cnpy/cnpy.cpp -I/workspace/cnpy/ -I/workspace/finn-hlslib -I/workspace/vivado-hlslib --std=c++11 -lz""".format( self.tmp_dir, node.op_type, self.tmp_dir, node.op_type - ) + ) process_compile = subprocess.Popen(bash_compile.split(), stdout=subprocess.PIPE) process_compile.communicate() bash_execute = "{}/execute_{}".format(self.tmp_dir, node.op_type) @@ -39,10 +51,12 @@ class StreamingMaxPool_Batch(HLSCustomOp): process_execute.communicate() temp_files.append("{}/execute_{}".format(self.tmp_dir, node.op_type)) temp_files.append("{}/output.npy".format(self.tmp_dir)) - output = np.load("{}/output.npy".format(self.tmp_dir)) + + # load output npy file + output = np.load("{}/output.npy".format(self.tmp_dir)) context[node.output[0]] = output # deleting temporary files - #for temp_file in temp_files: + # for temp_file in temp_files: # os.remove(temp_file) def get_attributes(self, node): @@ -63,6 +77,8 @@ class StreamingMaxPool_Batch(HLSCustomOp): ] def read_npy_data(self, node): + # c++ code to read out an npy file + # and put it in hls::stream in the correct order self.code_gen_dict["$READNPYDATA$"] = [] input_ind = 0 input_file_names = [] @@ -153,7 +169,7 @@ class StreamingMaxPool_Batch(HLSCustomOp): self.code_gen_dict["$SAVEASCNPY$"] = [ """cnpy::npy_save("{}/output.npy",&output_data_vector[0], {{{},{},{}}},"w");""".format( - self.tmp_dir, + self.tmp_dir, numReps, self.NumChannels, int(self.ImgDim / self.PoolDim),