blob: 0799ab1c8f3cf9bc521b4cd3e845c11b41507e58 [file] [log] [blame]
Louis Verhaarde8a5a782020-11-02 18:04:27 +01001# Copyright (C) 2020 Arm Limited or its affiliates. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the License); you may
6# not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an AS IS BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# Description:
18# Contains data types used in the external API for code generation
19from enum import auto
20from enum import Enum
21from typing import List
22from typing import NamedTuple
23from typing import Optional
24from typing import Tuple
25
Patrik Gustavssonc8a22f12020-11-18 17:05:50 +010026API_version_major = 1
27API_version_minor = 0
28api_version = f"{API_version_major}.{API_version_minor}"
29
Louis Verhaarde8a5a782020-11-02 18:04:27 +010030
31class NpuElementWiseOp(Enum):
32 """
33 Elementwise operation
34 """
35
36 ADD = auto()
37 SUB = auto()
38 MUL = auto()
39 ABS = auto()
40 MIN = auto()
41 MAX = auto()
42 LRELU = auto() # Leaky relu
43 CLZ = auto() # Number leading zeros
44 SHR = auto() # Rounded right-shift
45 SHL = auto() # Bitwise shift-left
46
47
48class NpuPoolingOp(Enum):
49 """
50 Pooling operation
51 """
52
53 MAX = auto()
54 AVERAGE = auto()
55 REDUCE_SUM = auto()
56
57
58class NpuActivationOp(Enum):
59 """
60 Activation function
61 """
62
63 NONE_OR_RELU = auto() # Clamps output using min/max
64 TANH = auto()
65 SIGMOID = auto()
66 TABLE_LOOKUP = auto() # Performs table look-up, using the provided table lookup index
67
68
69class NpuRoundingMode(Enum):
70 """
71 Available rounding modes
72 """
73
74 TFL = auto() # TensorFlow Lite rounding
75 TRUNCATE = auto() # Truncate towards zero
76 NATURAL = auto() # Round to nearest with x.5 rounded up, towards +infinity
77
78
79class NpuLayout(Enum):
80 """
81 Tensor layout of feature maps
82 """
83
84 NHWC = auto()
85 NHCWB16 = auto()
86
87 def __str__(self):
88 return self.name
89
90
91class NpuResamplingMode(Enum):
92 """
93 Resampling mode
94 """
95
96 NONE = auto() # No resampling is performed
97 NEAREST = auto() # 2x2 insert nearest
98 TRANSPOSE = auto() # 2x2 transpose
99
100
101class NpuBlockTraversal(Enum):
102 """
103 Block-traversal of weights
104 """
105
106 DEPTH_FIRST = auto()
107 PART_KERNEL_FIRST = auto()
108
109
110class NpuDataType(Enum):
111 """
112 Supported data types in feature maps
113 """
114
115 UINT8 = 8, False, auto()
116 INT8 = 8, True, auto()
117 UINT16 = 16, False, auto()
118 INT16 = 16, True, auto()
119 INT32 = 32, True, auto()
120
121 def is_signed(self) -> bool:
122 """Checks if this data type is signed or unsigned"""
123 return self.value[1]
124
125 def size_in_bits(self) -> int:
126 """ Size of the data type in bits"""
127 return self.value[0]
128
129 def size_in_bytes(self) -> int:
130 """ Size of the data type in bytes"""
131 return self.value[0] // 8
132
133 def min_value(self) -> int:
134 """Minimum value of this type"""
135 if self.is_signed():
136 return -(1 << (self.size_in_bits() - 1))
137 else:
138 return 0
139
140 def max_value(self) -> int:
141 """Maximum value of this type"""
142 if self.is_signed():
143 return (1 << (self.size_in_bits() - 1)) - 1
144 else:
145 return (1 << self.size_in_bits()) - 1
146
147 def __str__(self):
148 return self.name
149
150 __repr__ = __str__
151
152
153class NpuAddressRange(NamedTuple):
154 """
155 Address range
156 """
157
158 region: int # Memory region, a value between 0 and 7
159 address: int # Address, offset from the region's base address
160 length: int # The length of the range, in bytes
161
162 def __str__(self):
163 return f"(region={self.region}, address={hex(self.address)}, length={self.length})"
164
165
166class NpuTileBox(NamedTuple):
167 """
168 Specifies the addresses and dimensions of the tiles of a feature map.
169 A feature map can use 1 to 4 tiles
170 """
171
172 height_0: int # The height of tile 0
173 height_1: int # The height of tile 1, 0 if unused
174 width_0: int # the width of tile 0, and tile 2 (if used)
175 addresses: List[int] # A list of 4 addresses, set unused addresses to 0
176
177
178class NpuShape3D(NamedTuple):
179 """
180 Shape of (part of) a feature map
181 """
182
183 height: int
184 width: int
185 depth: int
186
187
188class NpuQuantization(NamedTuple):
189 """
190 Quantization parameters
191 """
192
193 scale_f32: Optional[float]
194 zero_point: int
195
196
197class NpuPadding(NamedTuple):
198 """
199 Padding to be applied to a convolution operation
200 """
201
202 top: int
203 left: int
204 bottom: int
205 right: int
206
207
208class NpuActivation:
209 """
210 Activation function, fused with NPU operations
211 """
212
213 def __init__(self, op_type: NpuActivationOp):
214 self.op_type = op_type # The activation operation to be performed
215 # min/max are optional
216 self.min: Optional[float] = None # E.g. set to 0.0 for RELU
217 self.max: Optional[float] = None # E.g. set to 6.0 for RELU6
218 # Table lookup index, only applicable for TABLE_LOOKUP activation, 0-7
219 self.lookup_table_index: int = 0
220
221
222class NpuFeatureMap:
223 """
224 Basic information about IFM, IFM2, OFM
225 """
226
227 def __init__(self):
228 self.data_type: NpuDataType = NpuDataType.UINT8
229 # The memory region, a value 0-7
230 self.region: int = 0
231 # Shape of the feature map
232 self.shape: NpuShape3D = NpuShape3D(height=0, width=0, depth=0)
233 # The tiles that comprise the feature map. In the normal case when only 1 tile is used,
234 # height_0 == self.shape.height, height_1 is 0, width_0 == self.shape.width, addresses[1:] are set to 0
235 self.tiles: NpuTileBox = NpuTileBox(height_0=0, height_1=0, width_0=0, addresses=[0, 0, 0, 0])
236 self.quantization: Optional[NpuQuantization]
237 self.layout: NpuLayout = NpuLayout.NHWC
238 # x/y/c strides used by the NPU when traversing the feature map, if None, vela will use default strides
239 self.strides: Optional[NpuShape3D] = None
240
241
242class NpuKernel:
243 """
244 Kernel information for NPU operations
245 """
246
247 def __init__(self, w: int, h: int, stride_x: int = 1, stride_y: int = 1, dilation_x: int = 1, dilation_y: int = 1):
248 assert stride_x > 0 and stride_y > 0
249 assert dilation_x > 0 and dilation_y > 0
250 self.width = w
251 self.height = h
252 self.stride_x = stride_x
253 self.stride_y = stride_y
254 self.dilation_x = dilation_x
255 self.dilation_y = dilation_y
256
257
258class NpuOperationType(Enum):
259 """
260 Type of NPU operation
261 """
262
263 Dma = auto()
264 Conv2D = auto()
265 ConvDepthWise = auto()
266 Pooling = auto()
267 ElementWise = auto()
268
269
270class NpuOperation:
271 """
272 Base class for all NPU operations
273 """
274
275 def __init__(self, op_type: NpuOperationType):
276 self.op_type = op_type
277
278
279class NpuDmaOperation(NpuOperation):
280 """
281 DMA operation
282 """
283
284 def __init__(self, src: NpuAddressRange, dest: NpuAddressRange):
285 super().__init__(NpuOperationType.Dma)
286 self.src = src
287 self.dest = dest
288 # DMA channel, usually 0 (user channel)
289 self.channel: int = 0
290 # Channel mode, 0 = external, 1 = internal (should usually be 0)
291 self.mode: int = 0
292
293
294class NpuBlockOperation(NpuOperation):
295 """
296 Base class for operations which produce an OFM
297 """
298
299 def __init__(self, op_type: NpuOperationType):
300 super().__init__(op_type)
301 self.ifm: Optional[NpuFeatureMap] = None
302 self.ifm2: Optional[NpuFeatureMap] = None
303 # The non-quantized scalar value in a binary elementwise operation. Only set if IFM2 is scalar
304 self.ifm2_scalar: Optional[float] = None
305 self.ofm: Optional[NpuFeatureMap] = None
306 self.kernel: Optional[NpuKernel] = None
307 # Weights, one element for each NPU core, empty if no weights are used.
308 # Must have been compressed using weight_compressor.encode_weights()
309 self.weights: List[NpuAddressRange] = []
310 # Biases, one element for each NPU core, empty if no bias is used.
311 # Must have been encoded using weight_compressor.encode_bias()
312 self.biases: List[NpuAddressRange] = []
313 self.padding: Optional[NpuPadding] = None
314 # Optional activation function to be applied
315 self.activation: Optional[NpuActivation] = None
316 # The block config is the unit of work in which the NPU generates the OFM.
317 # If the operation has weights, the depth of the block config must be the same as
318 # the ofm depth used in the call to weight_compressor.encode_weights()
319 # If set to None, vela will determine a suitable block size (can only be used if there are no weights)
320 # If block_config.width and height are set to -1, vela will determine suitable width/height
321 self.block_config: Optional[NpuShape3D] = None # OFM_BLK parameters
322 self.rounding_mode: NpuRoundingMode = NpuRoundingMode.TFL
323 # Set to True if the operations is fused with a Quantize operation (affects scaling)
324 self.fused_quantize: bool = False
325 # IFM upscaling to be applied
326 self.ifm_upscale: NpuResamplingMode = NpuResamplingMode.NONE
327
328
329class NpuConv2DOperation(NpuBlockOperation):
330 """
331 NPU_OP_CONV operation
332 """
333
334 def __init__(self):
335 super().__init__(NpuOperationType.Conv2D)
336 # Block traversal must be consistent with the block_traversal parameter specified in
337 # weight_compressor.encode_weights()
338 self.block_traversal: NpuBlockTraversal = NpuBlockTraversal.PART_KERNEL_FIRST
339
340
341class NpuConvDepthWiseOperation(NpuBlockOperation):
342 """
343 NPU_OP_DEPTHWISE operation
344 """
345
346 def __init__(self):
347 super().__init__(NpuOperationType.ConvDepthWise)
348
349
350class NpuPoolingOperation(NpuBlockOperation):
351 """
352 NPU_OP_POOL operation
353 """
354
355 def __init__(self, pooling_op_type: NpuPoolingOp):
356 super().__init__(NpuOperationType.Pooling)
357 self.sub_op_type: NpuPoolingOp = pooling_op_type
358 # Set to a float value for ResizeBilinear operations (affects scaling), else to None
359 self.rescale: Optional[float] = None
360
361
362class NpuElementWiseOperation(NpuBlockOperation):
363 """
364 NPU_OP_ELEMENTWISE operation
365 """
366
367 def __init__(self, elementwise_op_type: NpuElementWiseOp):
368 super().__init__(NpuOperationType.ElementWise)
369 self.sub_op_type: NpuElementWiseOp = elementwise_op_type
370 # Set to True for binary operators where IFM2 should be used as first operand
371 self.reversed_operands: bool = False
372 # Set to a tuple (scale, shift) for explicit rescale, else to None
373 self.rescale: Optional[Tuple] = None
Patrik Gustavssonc8a22f12020-11-18 17:05:50 +0100374
375
376def npu_get_API_version():
377 """
378 Public facing API to get the API version
379 :return: int, the 16 most significant bits, corresponding to major version
380 the 16 least significant bits, corresponding to minor version
381 """
382 version = (API_version_major << 16) | (API_version_minor & 0xFFFF)
383 return version