diff --git a/src/finn/custom_op/__init__.py b/src/finn/custom_op/__init__.py index 4ae7b9ebffaab6ca6be04b8d73f647b2db22dc78..08207a3519bf00936ac5ae4eb52e9de86d92e88c 100644 --- a/src/finn/custom_op/__init__.py +++ b/src/finn/custom_op/__init__.py @@ -26,101 +26,65 @@ # 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. -from abc import ABC, abstractmethod -from finn.util.basic import get_by_name -import onnx.helper as helper +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) -class CustomOp(ABC): - """CustomOp class all custom op nodes are based on. Contains different functions - every custom node should have. Some as abstract methods, these have to be - filled when writing a new custom op node.""" +from finn.custom_op.registry import custom_op - def __init__(self, onnx_node): - super().__init__() - self.onnx_node = onnx_node +# make sure new CustomOp subclasses are imported here so that they get +# registered and plug in correctly into the infrastructure +from finn.custom_op.fpgadataflow.convolutioninputgenerator import ( + ConvolutionInputGenerator, +) +from finn.custom_op.fpgadataflow.downsampler import DownSampler +from finn.custom_op.fpgadataflow.streamingfclayer_batch import StreamingFCLayer_Batch +from finn.custom_op.fpgadataflow.streamingmaxpool_batch import StreamingMaxPool_Batch +from finn.custom_op.fpgadataflow.streamingfifo import StreamingFIFO +from finn.custom_op.fpgadataflow.tlastmarker import TLastMarker +from finn.custom_op.fpgadataflow.streamingdatawidthconverter_batch import ( + StreamingDataWidthConverter_Batch, +) +from finn.custom_op.fpgadataflow.globalaccpool_batch import GlobalAccPool_Batch +from finn.custom_op.fpgadataflow.pool_batch import Pool_Batch +from finn.custom_op.fpgadataflow.fmpadding_batch import FMPadding_Batch +from finn.custom_op.fpgadataflow.thresholding_batch import Thresholding_Batch +from finn.custom_op.fpgadataflow.addstreams_batch import AddStreams_Batch +from finn.custom_op.fpgadataflow.labelselect_batch import LabelSelect_Batch +from finn.custom_op.fpgadataflow.duplicatestreams_batch import DuplicateStreams_Batch +from finn.custom_op.fpgadataflow.vector_vector_activate_batch import ( + Vector_Vector_Activate_Batch, +) +from finn.custom_op.fpgadataflow.channelwise_op_batch import ChannelwiseOp_Batch +from finn.custom_op.fpgadataflow.iodma import IODMA - def get_nodeattr(self, name): - """Get a node attribute by name. Data is stored inside the ONNX node's - AttributeProto container. Attribute must be part of get_nodeattr_types. - Default value is returned if attribute is not set.""" - try: - (dtype, req, def_val) = self.get_nodeattr_types()[name] - attr = get_by_name(self.onnx_node.attribute, name) - if attr is not None: - # dtype indicates which ONNX Attribute member to use - # (such as i, f, s...) - ret = attr.__getattribute__(dtype) - if dtype == "s": - # decode string attributes - ret = ret.decode("utf-8") - return ret - else: - if req: - raise Exception( - """Required attribute %s unspecified in - a %s node""" - % (name, self.onnx_node.op_type) - ) - else: - # not set, return default value - return def_val - except KeyError: - raise AttributeError("Op has no such attribute: " + name) - def set_nodeattr(self, name, value): - """Set a node attribute by name. Data is stored inside the ONNX node's - AttributeProto container. Attribute must be part of get_nodeattr_types.""" - try: - (dtype, req, def_val) = self.get_nodeattr_types()[name] - attr = get_by_name(self.onnx_node.attribute, name) - if attr is not None: - # dtype indicates which ONNX Attribute member to use - # (such as i, f, s...) - if dtype == "s": - # encode string attributes - value = value.encode("utf-8") - attr.__setattr__(dtype, value) - else: - # not set, create and insert AttributeProto - attr_proto = helper.make_attribute(name, value) - self.onnx_node.attribute.append(attr_proto) - except KeyError: - raise AttributeError("Op has no such attribute: " + name) +custom_op["DownSampler"] = DownSampler +custom_op["StreamingMaxPool_Batch"] = StreamingMaxPool_Batch +custom_op["StreamingFCLayer_Batch"] = StreamingFCLayer_Batch +custom_op["ConvolutionInputGenerator"] = ConvolutionInputGenerator +custom_op["TLastMarker"] = TLastMarker +custom_op["StreamingDataWidthConverter_Batch"] = StreamingDataWidthConverter_Batch +custom_op["StreamingFIFO"] = StreamingFIFO +custom_op["GlobalAccPool_Batch"] = GlobalAccPool_Batch +custom_op["Pool_Batch"] = Pool_Batch +custom_op["FMPadding_Batch"] = FMPadding_Batch +custom_op["Thresholding_Batch"] = Thresholding_Batch +custom_op["AddStreams_Batch"] = AddStreams_Batch +custom_op["LabelSelect_Batch"] = LabelSelect_Batch +custom_op["DuplicateStreams_Batch"] = DuplicateStreams_Batch +custom_op["Vector_Vector_Activate_Batch"] = Vector_Vector_Activate_Batch +custom_op["ChannelwiseOp_Batch"] = ChannelwiseOp_Batch +custom_op["IODMA"] = IODMA - @abstractmethod - def get_nodeattr_types(self): - """Returns a dict of permitted attributes for node, where: - returned_dict[attribute_name] = (dtype, require, default_value) - - dtype indicates which member of the ONNX AttributeProto - will be utilized - - require indicates whether this attribute is required - - default_val indicates the default value that will be used if the - attribute is not set - """ - pass - @abstractmethod - def make_shape_compatible_op(self, model): - """Returns a standard ONNX op which is compatible with this CustomOp - for performing shape inference.""" - pass - - @abstractmethod - def infer_node_datatype(self, model): - """Set the DataType annotations corresponding to the outputs of this - node.""" - pass - - @abstractmethod - def execute_node(self, context, graph): - """Execute this CustomOp instance, given the execution context and - ONNX graph.""" - pass - - @abstractmethod - def verify_node(self): - """Verifies that all attributes the node needs are there and - that particular attributes are set correctly. Also checks if - the number of inputs is equal to the expected number.""" - pass +def getCustomOp(node): + "Return a FINN CustomOp instance for the given ONNX node, if it exists." + op_type = node.op_type + try: + # lookup op_type in registry of CustomOps + inst = custom_op[op_type](node) + return inst + 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/fpgadataflow/__init__.py b/src/finn/custom_op/fpgadataflow/__init__.py index 7de6cce936ee54d58d9a526e926ff79dcd35b90d..df03a036f9c9932a820a5798336ea893cddd433f 100644 --- a/src/finn/custom_op/fpgadataflow/__init__.py +++ b/src/finn/custom_op/fpgadataflow/__init__.py @@ -25,12 +25,16 @@ # 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. +# namespace package, extend path +from pkgutil import extend_path + +__path__ = extend_path(__path__, __name__) from abc import abstractmethod import numpy as np import os import subprocess -from finn.custom_op import CustomOp +from finn.custom_op.custom_op import CustomOp from finn.util.basic import ( CppBuilder, make_build_dir, diff --git a/src/finn/transformation/streamline/__init__.py b/src/finn/transformation/streamline/__init__.py index d7686eaadcbc800542ab96c5f45145857412b773..bf0307031b4a9158ee2eb39ee30842e6c5a686b7 100644 --- a/src/finn/transformation/streamline/__init__.py +++ b/src/finn/transformation/streamline/__init__.py @@ -25,6 +25,10 @@ # 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. +# namespace package, extend path +from pkgutil import extend_path + +__path__ = extend_path(__path__, __name__) from finn.transformation import Transformation from finn.transformation.infer_datatypes import InferDataTypes