aboutsummaryrefslogtreecommitdiff
path: root/ethosu/vela/operation.py
diff options
context:
space:
mode:
Diffstat (limited to 'ethosu/vela/operation.py')
-rw-r--r--ethosu/vela/operation.py45
1 files changed, 42 insertions, 3 deletions
diff --git a/ethosu/vela/operation.py b/ethosu/vela/operation.py
index 161b17fd..52f06cf0 100644
--- a/ethosu/vela/operation.py
+++ b/ethosu/vela/operation.py
@@ -21,6 +21,7 @@ from __future__ import annotations
import copy
from collections import namedtuple
+from enum import auto
from enum import Enum
from typing import Any
from typing import Dict
@@ -29,7 +30,6 @@ from typing import Optional
from typing import Tuple
from typing import TYPE_CHECKING
-from .api import NpuRoundingMode
from .errors import VelaError
from .ethos_u55_regs.ethos_u55_regs import resampling_mode
from .numeric_util import full_shape
@@ -44,6 +44,13 @@ PointXY = namedtuple("PointXY", "x y")
PointXYZ = namedtuple("PointXYZ", "x y z")
+class RoundingMode(Enum):
+ TFLite = auto() # Round like TensorFlow Lite
+ ToZero = auto() # Round towards zero (truncate)
+ HalfUp = auto() # Round to nearest with x.5 rounded up towards positive infinity (natural)
+ AwayZero = auto() # Round away from zero (towards infinity)
+
+
class NpuBlockType(Enum):
Default = 0
ConvolutionMxN = 1
@@ -491,7 +498,7 @@ class Operation:
"rescale",
"read_offsets",
"read_shapes",
- "rounding_mode",
+ "_rounding_mode",
"explicit_scaling",
"write_offset",
"write_shape",
@@ -528,7 +535,7 @@ class Operation:
self.ofm_shapes: List[Shape4D] = []
self.read_offsets: List[Optional[Shape4D]] = [None, None] # offset for [ifm, ifm2]
self.read_shapes: List[Optional[Shape4D]] = [None, None] # read shape for [ifm, ifm2]
- self.rounding_mode: Optional[NpuRoundingMode] = None
+ self._rounding_mode: Optional[RoundingMode] = None
# Rescale op in TOSA supplies explicit multiplier and shift values
self.explicit_scaling: Optional[ExplicitScaling] = None
# Write offset, for operations that only produce a part of the OFM
@@ -587,6 +594,38 @@ class Operation:
return self._original_type
@property
+ def rounding_mode(self):
+ return self._rounding_mode
+
+ @rounding_mode.setter
+ def rounding_mode(self, mode: RoundingMode):
+ # All rounding modes are supported by all operators with the exception of rounding away from zero (see comment
+ # below)
+ is_supported = True
+ if mode == RoundingMode.AwayZero:
+ # Rounding away from zero does not have direct hardware support and so the compiler implements it indirectly
+ # in different ways. The exact process depends upon the operator type and not all operators are supported.
+ # Basically, rounding away from zero works by adjusting the accumulated value by a "small" amount before
+ # rounding up with the addition of a half (natural rounding). This "small" amount should be big enough to
+ # cause x.5 to be rounded correctly but small enough that smaller values are not incorrectly rounded. This
+ # is done by slightly adjusting the scale and shift on the ofm tensor using the scale and bias tensor,
+ # it has no affect on global scaling (i.e. the ofm_scale register). In addition, the zero points of the
+ # input and/or output tensors may also require forcing to zero but the exact behaviour also depends upon the
+ # corresponding optimisation performed in graph_optimisation.py where the rounding mode is set
+ is_supported = False
+ if self.original_type == Op.ResizeBilinear and self.type == Op.DepthwiseConv2DBias:
+ is_supported = True
+ if self.original_type == Op.AvgPool and self.type == Op.DepthwiseConv2DBias:
+ is_supported = True
+
+ if is_supported:
+ self._rounding_mode = mode
+ else:
+ assert (
+ False
+ ), f"Setting rounding mode = {mode} on {self.original_type} operator '{self.name}' is not supported."
+
+ @property
def type_changed(self):
return self.type != self.original_type