aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Petit <kevin.petit@arm.com>2024-01-08 15:27:25 +0000
committerKevin Petit <kevin.petit@arm.com>2024-01-15 10:19:01 +0000
commita7ac313a13f8f82b4b3ca9730bd746392f6600d9 (patch)
tree242c97650a951f48a5de96b44e0d5c124ea1c212
parent8754ec288dc491f3a9e936e68a1fd35783c9808e (diff)
downloadspecification-a7ac313a13f8f82b4b3ca9730bd746392f6600d9.tar.gz
Move operator pseudocode to separate files
This makes it easier to process the pseudocode automatically. Change-Id: I84394192598e589de07d43a7af60b96788e14f86 Signed-off-by: Kevin Petit <kevin.petit@arm.com>
-rw-r--r--Makefile10
-rw-r--r--chapters/activation_funcs.adoc46
-rw-r--r--chapters/comparison.adoc44
-rw-r--r--chapters/control_flow.adoc42
-rw-r--r--chapters/data_layout.adoc129
-rw-r--r--chapters/ewise_binary.adoc237
-rw-r--r--chapters/ewise_ternary.adoc19
-rw-r--r--chapters/ewise_unary.adoc87
-rw-r--r--chapters/image.adoc68
-rw-r--r--chapters/reduction.adoc105
-rw-r--r--chapters/scatter_gather.adoc32
-rw-r--r--chapters/tensor_ops.adoc293
-rw-r--r--chapters/type_conversion.adoc71
-rw-r--r--chapters/variable.adoc67
-rw-r--r--pseudocode/operators/ABS.tosac19
-rw-r--r--pseudocode/operators/ADD.tosac26
-rw-r--r--pseudocode/operators/ARGMAX.tosac37
-rw-r--r--pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac29
-rw-r--r--pseudocode/operators/AVG_POOL2D.tosac50
-rw-r--r--pseudocode/operators/BITWISE_AND.tosac18
-rw-r--r--pseudocode/operators/BITWISE_NOT.tosac14
-rw-r--r--pseudocode/operators/BITWISE_OR.tosac18
-rw-r--r--pseudocode/operators/BITWISE_XOR.tosac18
-rw-r--r--pseudocode/operators/CAST.tosac27
-rw-r--r--pseudocode/operators/CEIL.tosac14
-rw-r--r--pseudocode/operators/CLAMP.tosac15
-rw-r--r--pseudocode/operators/CLZ.tosac14
-rw-r--r--pseudocode/operators/CONCAT.tosac32
-rw-r--r--pseudocode/operators/COND_IF.tosac23
-rw-r--r--pseudocode/operators/CONV2D.tosac40
-rw-r--r--pseudocode/operators/CONV3D.tosac43
-rw-r--r--pseudocode/operators/DEPTHWISE_CONV2D.tosac40
-rw-r--r--pseudocode/operators/DIM.tosac11
-rw-r--r--pseudocode/operators/EQUAL.tosac22
-rw-r--r--pseudocode/operators/EXP.tosac14
-rw-r--r--pseudocode/operators/FFT2D.tosac31
-rw-r--r--pseudocode/operators/FLOOR.tosac14
-rw-r--r--pseudocode/operators/FULLY_CONNECTED.tosac25
-rw-r--r--pseudocode/operators/GATHER.tosac15
-rw-r--r--pseudocode/operators/GREATER.tosac22
-rw-r--r--pseudocode/operators/GREATER_EQUAL.tosac22
-rw-r--r--pseudocode/operators/INTDIV.tosac31
-rw-r--r--pseudocode/operators/LOG.tosac14
-rw-r--r--pseudocode/operators/LOGICAL_AND.tosac18
-rw-r--r--pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac19
-rw-r--r--pseudocode/operators/LOGICAL_NOT.tosac14
-rw-r--r--pseudocode/operators/LOGICAL_OR.tosac18
-rw-r--r--pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac20
-rw-r--r--pseudocode/operators/LOGICAL_XOR.tosac18
-rw-r--r--pseudocode/operators/MATMUL.tosac21
-rw-r--r--pseudocode/operators/MAXIMUM.tosac18
-rw-r--r--pseudocode/operators/MAX_POOL2D.tosac33
-rw-r--r--pseudocode/operators/MINIMUM.tosac18
-rw-r--r--pseudocode/operators/MUL.tosac37
-rw-r--r--pseudocode/operators/NEGATE.tosac22
-rw-r--r--pseudocode/operators/PAD.tosac27
-rw-r--r--pseudocode/operators/POW.tosac18
-rw-r--r--pseudocode/operators/RECIPROCAL.tosac14
-rw-r--r--pseudocode/operators/REDUCE_ALL.tosac25
-rw-r--r--pseudocode/operators/REDUCE_ANY.tosac25
-rw-r--r--pseudocode/operators/REDUCE_MAX.tosac25
-rw-r--r--pseudocode/operators/REDUCE_MIN.tosac25
-rw-r--r--pseudocode/operators/REDUCE_PRODUCT.tosac25
-rw-r--r--pseudocode/operators/REDUCE_SUM.tosac26
-rw-r--r--pseudocode/operators/RESCALE.tosac58
-rw-r--r--pseudocode/operators/RESHAPE.tosac21
-rw-r--r--pseudocode/operators/RESIZE.tosac74
-rw-r--r--pseudocode/operators/REVERSE.tosac16
-rw-r--r--pseudocode/operators/RFFT2D.tosac24
-rw-r--r--pseudocode/operators/RSQRT.tosac20
-rw-r--r--pseudocode/operators/SCATTER.tosac31
-rw-r--r--pseudocode/operators/SELECT.tosac25
-rw-r--r--pseudocode/operators/SIGMOID.tosac14
-rw-r--r--pseudocode/operators/SLICE.tosac28
-rw-r--r--pseudocode/operators/SUB.tosac26
-rw-r--r--pseudocode/operators/TABLE.tosac21
-rw-r--r--pseudocode/operators/TANH.tosac14
-rw-r--r--pseudocode/operators/TILE.tosac20
-rw-r--r--pseudocode/operators/TRANSPOSE.tosac35
-rw-r--r--pseudocode/operators/TRANSPOSE_CONV2D.tosac35
-rw-r--r--pseudocode/operators/VARIABLE.tosac33
-rw-r--r--pseudocode/operators/VARIABLE_READ.tosac26
-rw-r--r--pseudocode/operators/VARIABLE_WRITE.tosac29
-rw-r--r--pseudocode/operators/WHILE_LOOP.tosac33
-rw-r--r--pseudocode/operators/tables/ERF.tosac16
-rw-r--r--pseudocode/operators/tables/SIGMOID.tosac16
-rw-r--r--pseudocode/operators/tables/TANH.tosac17
87 files changed, 1888 insertions, 1158 deletions
diff --git a/Makefile b/Makefile
index c294fbb..877a8a2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
#
# This confidential and proprietary software may be used only as
# authorised by a licensing agreement from ARM Limited
-# (C) COPYRIGHT 2020-2022 ARM Limited
+# (C) COPYRIGHT 2020-2024 ARM Limited
# ALL RIGHTS RESERVED
# The entire notice above must be reproduced on all authorised
# copies and copies may only be made to the extent permitted
@@ -17,11 +17,13 @@ XMLLINT = xmllint
HTMLDIR=out/html
PDFDIR=out/pdf
GENDIR=out/gen
+PSEUDOCODEDIR := pseudocode
-COMMON_ARGS= -a generated="$(abspath $(GENDIR))"
+COMMON_ARGS= -a generated="$(abspath $(GENDIR))" -a pseudocode="$(abspath $(PSEUDOCODEDIR))"
SPECSRC := tosa_spec.adoc
ADOCFILES = $(wildcard chapters/[A-Za-z]*.adoc) $(wildcard $(GENDIR)/*/*.adoc)
+PSEUDOCODEFILES = $(wildcard $(PSEUDOCODEDIR)/operators/*.tosac) $(wildcard $(PSEUDOCODEDIR)/operators/tables/*.tosac)
SPECFILES = $(ADOCFILES) tosa.css
FIGURES = $(wildcard figures/*.svg)
SPECXML := tosa.xml
@@ -76,11 +78,11 @@ $(GEN): $(SPECXML) $(GENSCRIPTS)
tools/genspec.py --xml $(SPECXML) --outdir $(GENDIR)
@touch $@
-$(HTMLDIR)/tosa_spec.html: $(SPECSRC) $(SPECFILES) $(GEN)
+$(HTMLDIR)/tosa_spec.html: $(SPECSRC) $(SPECFILES) $(GEN) $(PSEUDOCODEFILES)
$(MKDIR) $(HTMLDIR)
$(ASCIIDOC) -b html5 -a stylesheet=tosa.css $(COMMON_ARGS) -o $@ $<
-$(PDFDIR)/tosa_spec.pdf: $(SPECSRC) $(SPECFILES) $(GEN)
+$(PDFDIR)/tosa_spec.pdf: $(SPECSRC) $(SPECFILES) $(GEN) $(PSEUDOCODEFILES)
$(MKDIR) $(PDFDIR)
$(ASCIIDOC) -r asciidoctor-pdf -b pdf $(COMMON_ARGS) -o $@ $(SPECSRC)
diff --git a/chapters/activation_funcs.adoc b/chapters/activation_funcs.adoc
index 1acaf56..1be3168 100644
--- a/chapters/activation_funcs.adoc
+++ b/chapters/activation_funcs.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2021 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -20,12 +20,7 @@ include::{generated}/operators/CLAMP.adoc[]
[source,c++]
----
-ERROR_IF(max_val < min_val);
-for_each(index in shape) {
- in_out_t value = tensor_read<in_out_t>(input, shape, index);
- value = apply_clip<in_out_t>(value, min_val, max_val);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/CLAMP.tosac[lines=10..-1]
----
==== ERF
@@ -43,13 +38,7 @@ The ERF table has 513 entries each of 16-bit precision and covering the input ra
[source,c++]
----
-int16_t erf_reference(int16_t x) { // input x range is -256 to + 256 inclusive
- F64 v = (double)x / (double)64;
- v = erf(v);
- return round_to_nearest_int(32768.0 * v);
-}
-
-generate_lookup_table(&erf_table, &erf_reference);
+include::{pseudocode}/operators/tables/ERF.tosac[lines=10..-1]
----
include::{generated}/operators/ERF.adoc[]
@@ -72,24 +61,14 @@ This sigmoid table has 513 entries each of 16-bit precision and covering the inp
.Code for generating 16-bit sigmoid table
[source,c++]
----
-int16_t sigmoid_reference(int16_t x) { // input x range is -256 to + 256 inclusive
- fp64_t v = (fp64_t)x / (fp64_t)16;
- v = 1.0/(1.0 + exp(-v));
- return round_to_nearest_int(32768.0 * v);
-}
-
-generate_lookup_table(&sigmoid_table, &sigmoid_reference);
+include::{pseudocode}/operators/tables/SIGMOID.tosac[lines=10..-1]
----
include::{generated}/operators/SIGMOID.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input, shape, index);
- value = sigmoid<in_out_t>(value1);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/SIGMOID.tosac[lines=10..-1]
----
==== TANH
@@ -109,23 +88,12 @@ This tanh_table has 513 entries each of 16-bit precision and covering the input
.Calculation of an example 16-bit tanh table
[source,c++]
----
-int16_t tanh_reference(int16_t x) { // input x range is -256 to +256 inclusive
- fp64_t v = (fp64_t)x/(fp64_t)32;
- v = exp(-2.0*v);
- v = (1.0-v)/(1.0+v);
- return round_to_nearest_int(32768.0 * v);
-}
-
-generate_lookup_table(&tanh_table, &tanh_reference);
+include::{pseudocode}/operators/tables/TANH.tosac[lines=10..-1]
----
include::{generated}/operators/TANH.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input, shape, index);
- value = tanh<in_out_t>(value1);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/TANH.tosac[lines=10..-1]
----
diff --git a/chapters/comparison.adoc b/chapters/comparison.adoc
index 4ef52d6..5535b40 100644
--- a/chapters/comparison.adoc
+++ b/chapters/comparison.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2022 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -17,19 +17,7 @@ include::{generated}/operators/EQUAL.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_t value1 = tensor_read<in_t>(input1, shape1, index1);
- in_t value2 = tensor_read<in_t>(input2, shape2, index2);
- out_t result;
- if (isNaN(value1) || isNaN(value2))
- result = False;
- else
- result = (value1 == value2) ? True : False;
- tensor_write<out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/EQUAL.tosac[lines=10..-1]
----
==== GREATER
@@ -40,19 +28,7 @@ include::{generated}/operators/GREATER.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_t value1 = tensor_read<in_t>(input1, shape1, index1);
- in_t value2 = tensor_read<in_t>(input2, shape2, index2);
- out_t result;
- if (isNaN(value1) || isNaN(value2))
- result = False;
- else
- result = (value1 > value2) ? True : False;
- tensor_write<out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/GREATER.tosac[lines=10..-1]
----
==== GREATER_EQUAL
@@ -63,17 +39,5 @@ include::{generated}/operators/GREATER_EQUAL.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_t value1 = tensor_read<in_t>(input1, shape1, index1);
- in_t value2 = tensor_read<in_t>(input2, shape2, index2);
- out_t result;
- if (isNaN(value1) || isNaN(value2))
- result = False;
- else
- result = (value1 >= value2) ? True : False;
- tensor_write<out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/GREATER_EQUAL.tosac[lines=10..-1]
----
diff --git a/chapters/control_flow.adoc b/chapters/control_flow.adoc
index 9de9c72..2da2424 100644
--- a/chapters/control_flow.adoc
+++ b/chapters/control_flow.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2021 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -19,20 +19,7 @@ include::{generated}/operators/COND_IF.adoc[]
[source,c++]
----
-ERROR_IF(tosa_nesting_depth >= MAX_NESTING);
-ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(then_graph));
-ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(else_graph));
-ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(then_graph));
-ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(else_graph));
-ERROR_IF(tensor_size(shape) != 1);
-
-tosa_nesting_depth++;
-if (condition[0]) {
- tosa_execute_graph(then_graph, input_list, output_list);
-} else {
- tosa_execute_graph(else_graph, input_list, output_list);
-}
-tosa_nesting_depth--;
+include::{pseudocode}/operators/COND_IF.tosac[lines=10..-1]
----
==== WHILE_LOOP
@@ -43,28 +30,5 @@ include::{generated}/operators/WHILE_LOOP.adoc[]
[source,c++]
----
-ERROR_IF(tosa_nesting_depth >= MAX_NESTING);
-ERROR_IF(tensor_list_shape(input_list) != tosa_list_shape(output_list));
-ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(cond_graph));
-ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(body_graph));
-ERROR_IF(tensor_list_shape(input_list) != tosa_output_shape(body_graph));
-// Condition graph output must be a single element tensor with a single bool value
-ERROR_IF(tensor_size(tosa_output_shape(cond_graph)) != 1);
-ERROR_IF(tosa_output_type(cond_graph) != bool_t);
-
-// The iteration number 'i' is included to give unique names to variables
-// in each iteration of the loop and is not required by implementations
-int32_t i=0; // iteration number
-tensor_list_t list[]; // array of tensor lists indexed by iteration
-bool_t *condition[]; // array of condition tensors indexed by iteration
-list[i] = input_list; // copy input data as list[0]
-tosa_nesting_depth++;
-tosa_execute_graph(cond_graph, list[i], [ condition[i] ]); // initial condition
-while (condition[i][0]) {
- tosa_execute_graph(body_graph, list[i], list[i+1]);
- i = i+1;
- tosa_execute_graph(cond_graph, list[i], [ condition[i] ]);
-}
-tosa_nesting_depth--;
-output_list = list[i];
+include::{pseudocode}/operators/WHILE_LOOP.tosac[lines=10..-1]
----
diff --git a/chapters/data_layout.adoc b/chapters/data_layout.adoc
index 2d48eb1..1ce92be 100644
--- a/chapters/data_layout.adoc
+++ b/chapters/data_layout.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -17,30 +17,7 @@ include::{generated}/operators/CONCAT.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= max(1,rank(shapes1[0])));
-ERROR_IF(shape[axis] != sum(shape_dim(shapes1[k], axis) for all k))
-ERROR_IF(in_out_t == shape_t && rank(shape) > 1);
-// The following checks ensure all inputs are compatible for concatenation
-for_each(input_shape in shapes1) {
- ERROR_IF(rank(input_shape) != rank(shapes1[0]));
- for_each(index in input_shape) {
- ERROR_IF(index != axis && input_shape[index] != shapes1[0][index]);
- }
-}
-for_each(index1 in shape) {
- dim_t index2 = index1;
- for (tensor t = 0; t < length(input1); t++) {
- // Continue to concatenate along axis from each tensor
- // For each output location, we are looking for the
- // appropriate input tensor
- if (index2[axis] >= 0 && index2[axis] < shape_dim(shapes1[t], axis)) {
- in_out_t value = tensor_read<in_out_t>(input1[t], shapes1[t], index2);
- tensor_write<in_out_t>(output, shape, index1, value);
- }
- index2[axis] = index2[axis] - shape_dim(shapes1[t], axis);
- }
-}
-
+include::{pseudocode}/operators/CONCAT.tosac[lines=10..-1]
----
==== PAD
@@ -53,24 +30,7 @@ include::{generated}/operators/PAD.adoc[]
[source,c++]
----
-// Check output shape matches the padded input shape
-ERROR_IF(rank(shape) != rank(shape1));
-for (i = 0; i < rank(shape); i++) {
- ERROR_IF(padding[i,0] < 0 || padding[i,1] < 0);
- ERROR_IF(shape[i] != padding[i, 0] + shape1[i] + padding[i, 1]);
-}
-for_each(index in shape) {
- dim_t index1 = index;
- bool_t is_pad = false;
- for(i = 0; i < rank(shape); i++) {
- index1[i] = index1[i] - padding[i,0];
- if (index1[i] < 0 || index[i] >= length(shape[i])) {
- is_pad = true;
- }
- }
- in_out_t value = is_pad ? pad_const : tensor_read<in_out_t>(input1, shape1, index1);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/PAD.tosac[lines=10..-1]
----
==== DIM
@@ -81,8 +41,7 @@ include::{generated}/operators/DIM.adoc[]
[source,c++]
----
-ERROR_IF(axis >= rank(shape));
-tensor_write<shape_t>(output, [], [], shape_dim(shape, axis));
+include::{pseudocode}/operators/DIM.tosac[lines=10..-1]
----
==== RESHAPE
@@ -93,18 +52,7 @@ include::{generated}/operators/RESHAPE.adoc[]
[source,c++]
----
-ERROR_IF(tensor_size(shape1) != tensor_size(shape));
-
-for_each(index in shape) {
- // Calculate flattened index for the output location (index)
- size_t offset = tensor_index_to_offset(shape, index);
- // Now convert to the location in the input
- dim_t tmp_index = tensor_offset_to_index(shape1, offset);
-
- // Now read/write the value
- in_out_t val = tensor_read<in_out_t>(input1, shape1, tmp_index);
- tensor_write<in_out_t>(output, shape, index, val);
-}
+include::{pseudocode}/operators/RESHAPE.tosac[lines=10..-1]
----
==== REVERSE
@@ -115,13 +63,7 @@ include::{generated}/operators/REVERSE.adoc[]
[source,c++]
----
-ERROR_IF(axis < 0 || axis >= rank(shape));
-for_each(index in shape) {
- dim_t tmp_index = index;
- tmp_index[axis] = shape[axis] - 1 - index[axis];
- in_out_t value = tensor_read<in_out_t>(input, shape, tmp_index);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/REVERSE.tosac[lines=10..-1]
----
==== SLICE
@@ -133,25 +75,7 @@ include::{generated}/operators/SLICE.adoc[]
[source,c++]
----
-ERROR_IF(rank(shape1) != length(start) || rank(shape1) != length(size));
-ERROR_IF(rank(shape1) != rank(shape));
-// Sanity check the given coordinates, ensure start and end are
-// within tensor bounds
-for_each(index in rank(shape1)) {
- ERROR_IF(start[index] < 0);
- ERROR_IF(size[index] <= 0); //Output must be positive size
- ERROR_IF(start[index] + size[index] > shape1[index]);
- ERROR_IF(shape[index] != size[index]);
-}
-
-for_each(index in shape) {
- dim_t tmp_index = index;
- for(i = 0; i < rank(shape); i++) {
- tmp_index[i] = index[i] + start[i];
- }
- in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/SLICE.tosac[lines=10..-1]
----
==== TILE
@@ -162,17 +86,7 @@ include::{generated}/operators/TILE.adoc[]
[source,c++]
----
-ERROR_IF(rank(shape1) != rank(shape));
-
-for_each(index in shape) {
- dim_t tmp_index = index;
- for(i = 0; i < rank(shape); i++) {
- ERROR_IF(shape1[i] * multiples[i] != shape[i]);
- tmp_index[i] = index[i] % shape1[i];
- }
- in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/TILE.tosac[lines=10..-1]
----
==== TRANSPOSE
@@ -184,30 +98,5 @@ include::{generated}/operators/TRANSPOSE.adoc[]
[source,c++]
----
-ERROR_IF(rank(shape1) != rank(shape));
-ERROR_IF(tensor_size(shape1) != tensor_size(shape));
-
-for_each(index in perms) {
- // Ensure each perms value is a valid value
- ERROR_IF(index >= rank(shape1));
- ERROR_IF(index < 0);
- // Ensure ranks aren't repeated
- ERROR_IF(indexes_used[index] == true);
- indexes_used[index] = true;
-}
-
-// Ensure that the output shapes have the properly
-// permuted shapes
-for(i = 0; i < rank(shape); i++) {
- ERROR_IF(shape1[perms[i]] != shape[i])
-}
-
-for_each(index in shape) {
- dim_t tmp_index = index;
- for(i = 0; i < rank(shape); i++) {
- tmp_index[perms[i]] = index[i]
- }
- in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index);
- tensor_write<in_out_t>(output, shape, index, value);
-}
+include::{pseudocode}/operators/TRANSPOSE.tosac[lines=10..-1]
----
diff --git a/chapters/ewise_binary.adoc b/chapters/ewise_binary.adoc
index 876ab4b..3cc2ecb 100644
--- a/chapters/ewise_binary.adoc
+++ b/chapters/ewise_binary.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -18,23 +18,7 @@ include::{generated}/operators/ADD.adoc[]
[source,c++]
----
-if (in_out_t == shape_t) {
- ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
- shape_t value1 = tensor_read<shape_t>(input1, [], []);
- shape_t value2 = tensor_read<shape_t>(input2, [], []);
- shape_t result = apply_add_s<shape_t>(value1, value2);
- tensor_write<shape_t>(output, [], [], result);
-} else {
- ERROR_IF(shape != broadcast_shape(shape1, shape2));
- for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = apply_add_s<in_out_t>(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
- }
-}
+include::{pseudocode}/operators/ADD.tosac[lines=10..-1]
----
==== ARITHMETIC_RIGHT_SHIFT
@@ -46,26 +30,7 @@ include::{generated}/operators/ARITHMETIC_RIGHT_SHIFT.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
-
- // Ensure that shift amount is appropriate for the data type
- REQUIRE((in_out_t == i32_t && 0 <= value2 && value2 <= 31) ||
- (in_out_t == i16_t && 0 <= value2 && value2 <= 15) ||
- (in_out_t == i8_t && 0 <= value2 && value2 <= 7));
-
- in_out_t result = apply_arith_rshift<in_out_t>(value1, value2);
- if (round == true && static_cast<int32_t>(value2) > 0 &&
- (apply_arith_rshift<in_out_t>(value1, apply_sub_s<in_out_t>(value2, 1)) & 1 != 0) {
- result = result + 1;
- }
- result = apply_clip_s<in_out_t>(result, minimum_s<in_out_t>, maximum_s<in_out_t>);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/ARITHMETIC_RIGHT_SHIFT.tosac[lines=10..-1]
----
==== BITWISE_AND
@@ -77,15 +42,7 @@ include::{generated}/operators/BITWISE_AND.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = value1 & value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/BITWISE_AND.tosac[lines=10..-1]
----
==== BITWISE_OR
@@ -97,15 +54,7 @@ include::{generated}/operators/BITWISE_OR.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = value1 | value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/BITWISE_OR.tosac[lines=10..-1]
----
==== BITWISE_XOR
@@ -117,15 +66,7 @@ include::{generated}/operators/BITWISE_XOR.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = value1 ^ value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/BITWISE_XOR.tosac[lines=10..-1]
----
==== INTDIV
@@ -140,28 +81,7 @@ include::{generated}/operators/INTDIV.adoc[]
[source,c++]
----
-if (in_out_t == shape_t) {
- ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
- shape_t value1 = tensor_read<shape_t>(input1, [], []);
- shape_t value2 = tensor_read<shape_t>(input2, [], []);
- REQUIRE(value2 != 0);
- shape_t result = value1 / value2;
- tensor_write<shape_t>(output, [], [], result);
-} else {
- ERROR_IF(shape != broadcast_shape(shape1, shape2));
- for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- REQUIRE(value2 != 0);
- // This catches the case where we divide minimum<in_out_t> by -1
- // which is not representable in two's complement
- REQUIRE(static_cast<int64_t>(value1) / static_cast<int64_t>(value2) <= maximum_s<in_out_t>);
- in_out_t result = apply_intdiv_s<in_out_t>(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
- }
-}
+include::{pseudocode}/operators/INTDIV.tosac[lines=10..-1]
----
==== LOGICAL_AND
@@ -173,15 +93,7 @@ include::{generated}/operators/LOGICAL_AND.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = value1 && value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOGICAL_AND.tosac[lines=10..-1]
----
==== LOGICAL_LEFT_SHIFT
@@ -193,16 +105,7 @@ include::{generated}/operators/LOGICAL_LEFT_SHIFT.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- REQUIRE(0 <= value2 && value2 <= 31);
- in_out_t result = value1 << value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOGICAL_LEFT_SHIFT.tosac[lines=10..-1]
----
==== LOGICAL_RIGHT_SHIFT
@@ -214,17 +117,7 @@ include::{generated}/operators/LOGICAL_RIGHT_SHIFT.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- REQUIRE(0 <= static_cast<int32_t>(value2) && static_cast<int32_t>(value2) <= 31);
- // Logical shifts happen as unsigned types internally
- in_out_t result = apply_logical_rshift<in_out_t>(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOGICAL_RIGHT_SHIFT.tosac[lines=10..-1]
----
==== LOGICAL_OR
@@ -236,15 +129,7 @@ include::{generated}/operators/LOGICAL_OR.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = value1 || value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOGICAL_OR.tosac[lines=10..-1]
----
==== LOGICAL_XOR
@@ -256,15 +141,7 @@ include::{generated}/operators/LOGICAL_XOR.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = value1 != value2;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOGICAL_XOR.tosac[lines=10..-1]
----
==== MAXIMUM
@@ -276,15 +153,7 @@ include::{generated}/operators/MAXIMUM.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = apply_max_s<in_out_t>(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/MAXIMUM.tosac[lines=10..-1]
----
==== MINIMUM
@@ -296,15 +165,7 @@ include::{generated}/operators/MINIMUM.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = apply_min_s(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/MINIMUM.tosac[lines=10..-1]
----
==== MUL
@@ -316,34 +177,7 @@ include::{generated}/operators/MUL.adoc[]
[source,c++]
----
-if (in_out_t == shape_t) {
- ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
- shape_t value1 = tensor_read<shape_t>(input1, [], []);
- shape_t value2 = tensor_read<shape_t>(input2, [], []);
- shape_t result = value1 * value2;
- tensor_write<shape_t>(output, [], [], result);
-} else {
- REQUIRE(0 <= shift && shift <= 63);
- REQUIRE(in_t == int32_t || shift == 0);
- ERROR_IF(shape != broadcast_shape(shape1, shape2));
- for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_t value1 = tensor_read<in_t>(input1, shape1, index1);
- in_t value2 = tensor_read<in_t>(input2, shape2, index2);
- out_t result;
- if (in_t == i32_t && shift > 0) {
- int64_t product = sign_extend<int64_t>(value1) * sign_extend<int64_t>(value2);
- int64_t round = static_cast<int64_t>(1) << (shift - 1);
- product = (product + round) >> shift;
- REQUIRE(product >= minimum_s<i32_t> && product <= maximum_s<i32_t>)
- result = product;
- } else {
- result = apply_mul_s(value1, value2); // low 32-bits of result for i32_t
- }
- tensor_write<out_t>(output, shape, index, result);
- }
-}
+include::{pseudocode}/operators/MUL.tosac[lines=10..-1]
----
==== POW
@@ -355,15 +189,7 @@ include::{generated}/operators/POW.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(shape1, shape2));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = apply_pow<in_out_t>(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/POW.tosac[lines=10..-1]
----
==== SUB
@@ -375,23 +201,7 @@ include::{generated}/operators/SUB.adoc[]
[source,c++]
----
-if (in_out_t == shape_t) {
- ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
- shape_t value1 = tensor_read<shape_t>(input1, [], []);
- shape_t value2 = tensor_read<shape_t>(input2, [], []);
- shape_t result = apply_sub<shape_t>(value1, value2);
- tensor_write<shape_t>(output, [], [], result);
-} else {
- ERROR_IF(shape != broadcast_shape(shape1, shape2));
- for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t result = apply_sub_s<in_out_t>(value1, value2);
- tensor_write<in_out_t>(output, shape, index, result);
- }
-}
+include::{pseudocode}/operators/SUB.tosac[lines=10..-1]
----
==== TABLE
@@ -414,16 +224,5 @@ include::{generated}/operators/TABLE.adoc[]
[source,c++]
----
-REQUIRE(length(table) == TABLE_SIZE);
-for_each(index in shape) {
- in_t value = tensor_read<in_t>(input, shape, index);
- out_t result;
- if (in_t == i8_t) {
- // value is a signed int, convert to a 0 based index
- result = table[static_cast<int16_t>(value) + 128];
- } else {
- result = apply_lookup_s(static_cast<int16_t>(table), static_cast<int16_t>(value));
- }
- tensor_write<out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/TABLE.tosac[lines=10..-1]
----
diff --git a/chapters/ewise_ternary.adoc b/chapters/ewise_ternary.adoc
index 57cf599..d6ea453 100644
--- a/chapters/ewise_ternary.adoc
+++ b/chapters/ewise_ternary.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2021 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -17,20 +17,5 @@ include::{generated}/operators/SELECT.adoc[]
[source,c++]
----
-ERROR_IF(shape != broadcast_shape(broadcast_shape(shape1, shape2), shape3));
-for_each(index in shape) {
- dim_t index1 = apply_broadcast(shape, shape1, index);
- dim_t index2 = apply_broadcast(shape, shape2, index);
- dim_t index3 = apply_broadcast(shape, shape3, index);
- bool_t value1 = tensor_read<bool_t>(input1, shape1, index1);
- in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
- in_out_t value3 = tensor_read<in_out_t>(input3, shape3, index3);
- in_out_t result;
- if (value1) {
- result = value2;
- } else {
- result = value3;
- }
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/SELECT.tosac[lines=10..-1]
----
diff --git a/chapters/ewise_unary.adoc b/chapters/ewise_unary.adoc
index d3eacc4..4b8cd4d 100644
--- a/chapters/ewise_unary.adoc
+++ b/chapters/ewise_unary.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -24,16 +24,7 @@ include::{generated}/operators/ABS.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- if (is_floating_point(in_out_t) && value1 == -0.0) {
- value1 = 0.0;
- }
- if (static_cast<int32_t>(value1) < 0.0) {
- value1 = apply_sub_s<in_out_t>(0, value1);
- }
- tensor_write<in_out_t>(output, shape, index, value1);
-}
+include::{pseudocode}/operators/ABS.tosac[lines=10..-1]
----
==== BITWISE_NOT
@@ -44,11 +35,7 @@ include::{generated}/operators/BITWISE_NOT.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- in_out_t result = ~value1;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/BITWISE_NOT.tosac[lines=10..-1]
----
==== CEIL
@@ -66,11 +53,7 @@ include::{generated}/operators/CEIL.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- in_out_t result = apply_ceil<in_out_t>(value1);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/CEIL.tosac[lines=10..-1]
----
==== CLZ
@@ -81,11 +64,7 @@ include::{generated}/operators/CLZ.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- in_out_t result = count_leading_zeros(value1);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/CLZ.tosac[lines=10..-1]
----
==== EXP
@@ -103,11 +82,7 @@ include::{generated}/operators/EXP.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- in_out_t result = apply_exp<in_out_t>(value1);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/EXP.tosac[lines=10..-1]
----
==== FLOOR
@@ -125,11 +100,7 @@ include::{generated}/operators/FLOOR.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- in_out_t result = apply_floor<in_out_t>(value1);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/FLOOR.tosac[lines=10..-1]
----
==== LOG
@@ -147,11 +118,7 @@ include::{generated}/operators/LOG.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- in_out_t result = apply_log<in_out_t>(value1);
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOG.tosac[lines=10..-1]
----
==== LOGICAL_NOT
@@ -162,11 +129,7 @@ include::{generated}/operators/LOGICAL_NOT.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index);
- in_out_t result = !value1;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/LOGICAL_NOT.tosac[lines=10..-1]
----
==== NEGATE
@@ -184,19 +147,7 @@ include::{generated}/operators/NEGATE.adoc[]
[source,c++]
----
-ERROR_IF(in_out_t != i8_t && input1_zp != 0) // Zero point only for int8_t
-ERROR_IF(in_out_t != i8_t && output_zp != 0) // Zero point only for int8_t
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
- acc_t value = apply_sub_s<acc_t>(sign_extend<acc_t>(value1),
- sign_extend<acc_t>(input1_zp));
- value = apply_sub_s<acc_t>(0, value);
- value = apply_add_s<acc_t>(value, sign_extend<acc_t>(output_zp));
- in_out_t result = truncate<in_out_t>(apply_clip_s<acc_t>(value,
- minimum_s<in_out_t>,
- maximum_s<in_out_t>));
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/NEGATE.tosac[lines=10..-1]
----
==== RECIPROCAL
@@ -214,11 +165,7 @@ include::{generated}/operators/RECIPROCAL.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index);
- in_out_t result = 1.0 / value1;
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/RECIPROCAL.tosac[lines=10..-1]
----
==== RSQRT
@@ -236,15 +183,5 @@ include::{generated}/operators/RSQRT.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index);
- in_out_t result;
- if (value1 < 0) {
- result = NaN;
- }
- else {
- result = 1.0 / apply_sqrt<in_out_t>(value1);
- }
- tensor_write<in_out_t>(output, shape, index, result);
-}
+include::{pseudocode}/operators/RSQRT.tosac[lines=10..-1]
----
diff --git a/chapters/image.adoc b/chapters/image.adoc
index 59e5ddf..1d2a212 100644
--- a/chapters/image.adoc
+++ b/chapters/image.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -61,69 +61,5 @@ include::{generated}/operators/RESIZE.adoc[]
[source,c++]
----
-// Ensure the image size is supported by GPU APIs and that for integer
-// implementations, position * stride does not overflow int32_t.
-ERROR_IF(max(OH,OW,IH,IW) >= 16384);
-ERROR_IF(scale_y_n <= 0 || scale_y_d <= 0 || scale_x_n <= 0 || scale_x_d <= 0);
-// if in_t=int8_t ensure that an int32_t accumulator can be used
-ERROR_IF(scale_y_n > (1 << 11) || scale_x_n > (1 << 11));
-// set a consistent lower limit of 1/16 downscale to simplify implementations
-ERROR_IF(scale_y_d >= 16 * scale_y_n || scale_x_d >= 16 * scale_x_n);
-ERROR_IF(offset_y < -scale_y_n || offset_y >= 16 * scale_y_n);
-ERROR_IF(offset_x < -scale_x_n || offset_x >= 16 * scale_x_n);
-ERROR_IF(border_y < -16 * scale_y_n || border_y >= scale_y_n);
-ERROR_IF(border_x < -16 * scale_x_n || border_x >= scale_x_n);
-ERROR_IF(OH != idiv_check((IH - 1) * scale_y_n - offset_y + border_y, scale_y_d) + 1);
-ERROR_IF(OW != idiv_check((IW - 1) * scale_x_n - offset_x + border_x, scale_x_d) + 1);
-for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C) {
- out_t acc;
- resize_t dx, dy;
- resize_t unit_x, unit_y;
-
- unit_x = (is_floating_point(resize_t)) ? 1.0 : scale_x_n;
- unit_y = (is_floating_point(resize_t)) ? 1.0 : scale_y_n;
-
- int32_t y = oy * scale_y_d + offset_y;
- int32_t x = ox * scale_x_d + offset_x;
- int16_t iy = idiv_floor(y, scale_y_n);
- int16_t ix = idiv_floor(x, scale_x_n);
- int16_t ry = y - iy * scale_y_n; // (y % scale_y_n)
- int16_t rx = x - ix * scale_x_n; // (x % scale_x_n)
-
- if (is_floating_point(resize_t)) {
- dy = static_cast<resize_t>(ry) / static_cast<resize_t>(scale_y_n);
- dx = static_cast<resize_t>(rx) / static_cast<resize_t>(scale_x_n);
- } else {
- dy = ry;
- dx = rx;
- }
- // Note that -1 <= iy < IH and -1 <= ix < IW
- int16_t iy0 = apply_max_s(iy, 0);
- int16_t iy1 = apply_min_s(iy + 1, IH - 1);
- int16_t ix0 = apply_max_s(ix, 0);
- int16_t ix1 = apply_min_s(ix + 1, IW - 1);
- if (mode==BILINEAR) {
- using in_s_t = make_signed(in_t); // Use signed calculations for i8/i16
- in_s_t v00 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix0,c]));
- in_s_t v01 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix1,c]));
- in_s_t v10 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix0,c]));
- in_s_t v11 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix1,c]));
- acc = v00 * (unit_y - dy) * (unit_x - dx);
- acc += v01 * (unit_y - dy) * dx;
- acc += v10 * dy * (unit_x - dx);
- acc += v11 * dy * dx;
- tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc);
- } else if (mode==NEAREST) {
- int32_t iy, ix;
- if (is_floating_point(resize_t)) {
- iy = (dy >= 0.5) ? iy1 : iy0;
- ix = (dx >= 0.5) ? ix1 : ix0;
- } else {
- iy = (2 * dy >= scale_y_n) ? iy1 : iy0;
- ix = (2 * dx >= scale_x_n) ? ix1 : ix0;
- }
- in_t v = tensor_read<in_t>(input, [N,IH,IW,C], [n,iy,ix,c]);
- tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], v);
- }
-}
+include::{pseudocode}/operators/RESIZE.tosac[lines=10..-1]
----
diff --git a/chapters/reduction.adoc b/chapters/reduction.adoc
index 19ff4ed..e3692de 100644
--- a/chapters/reduction.adoc
+++ b/chapters/reduction.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -17,22 +17,7 @@ include::{generated}/operators/REDUCE_ALL.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-ERROR_IF(shape[axis] != 1);
-left_shape = (axis > 1) ? shape[0:axis-1] : [];
-right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- in_out_t acc = true;
- for (i = 0; i < shape1[axis]; i++) {
- index = flatten(left_index, [i], right_index);
- in_out_t value = tensor_read<in_out_t>(input, shape1, index);
- acc = acc && value;
- }
- out_index = flatten(left_index, [0], right_index);
- tensor_write<in_out_t>(output, shape, out_index, acc);
- }
-}
+include::{pseudocode}/operators/REDUCE_ALL.tosac[lines=10..-1]
----
==== REDUCE_ANY
@@ -43,22 +28,7 @@ include::{generated}/operators/REDUCE_ANY.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-ERROR_IF(shape[axis] != 1);
-left_shape = (axis > 1) ? shape[0:axis-1] : [];
-right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- in_out_t acc = false;
- for (i = 0; i < shape1[axis]; i++) {
- index = flatten(left_index, [i], right_index);
- in_out_t value = tensor_read<in_out_t>(input, shape1, index);
- acc = acc || value;
- }
- out_index = flatten(left_index, [0], right_index);
- tensor_write<in_out_t>(output, shape, out_index, acc);
- }
-}
+include::{pseudocode}/operators/REDUCE_ANY.tosac[lines=10..-1]
----
==== REDUCE_MAX
@@ -69,22 +39,7 @@ include::{generated}/operators/REDUCE_MAX.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-ERROR_IF(shape[axis] != 1);
-left_shape = (axis > 1) ? shape[0:axis-1] : [];
-right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- in_out_t acc = minimum<in_out_t>;
- for (i = 0; i < shape1[axis]; i++) {
- index = flatten(left_index, [i], right_index);
- in_out_t value = tensor_read<in_out_t>(input, shape1, index);
- acc = apply_max_s<in_out_t>(acc, value);
- }
- out_index = flatten(left_index, [0], right_index);
- tensor_write<in_out_t>(output, shape, out_index, acc);
- }
-}
+include::{pseudocode}/operators/REDUCE_MAX.tosac[lines=10..-1]
----
==== REDUCE_MIN
@@ -95,22 +50,7 @@ include::{generated}/operators/REDUCE_MIN.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-ERROR_IF(shape[axis] != 1);
-left_shape = (axis > 1) ? shape[0:axis-1] : [];
-right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- in_out_t acc = maximum<in_out_t>;
- for (i = 0; i < shape1[axis]; i++) {
- index = flatten(left_index, [i], right_index);
- in_out_t value = tensor_read<in_out_t>(input, shape1, index);
- acc = apply_min_s<in_out_t>(acc, value);
- }
- out_index = flatten(left_index, [0], right_index);
- tensor_write<in_out_t>(output, shape, out_index, out);
- }
-}
+include::{pseudocode}/operators/REDUCE_MIN.tosac[lines=10..-1]
----
==== REDUCE_PRODUCT
@@ -121,22 +61,7 @@ include::{generated}/operators/REDUCE_PRODUCT.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-ERROR_IF(shape[axis] != 1);
-left_shape = (axis > 1) ? shape[0:axis-1] : [];
-right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- in_out_t acc = 1.0;
- for (i = 0; i < shape1[axis]; i++) {
- index = flatten(left_index, [i], right_index);
- in_out_t value = tensor_read<in_out_t>(input, shape1, index);
- acc = apply_mul_s<in_out_t>(acc, value);
- }
- out_index = flatten(left_index, [0], right_index);
- tensor_write<in_out_t>(output, shape, out_index, acc);
- }
-}
+include::{pseudocode}/operators/REDUCE_PRODUCT.tosac[lines=10..-1]
----
==== REDUCE_SUM
@@ -147,21 +72,5 @@ include::{generated}/operators/REDUCE_SUM.adoc[]
[source,c]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-ERROR_IF(shape[axis] != 1);
-left_shape = (axis > 1) ? shape[0:axis-1] : [];
-right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- acc_t acc = 0;
- for (i = 0; i < shape1[axis]; i++) {
- index = flatten(left_index, [i], right_index);
- acc_t value = tensor_read<in_out_t>(input, shape1, index);
- acc = apply_add_s<acc_t>(acc, value);
- }
- out_index = flatten(left_index, [0], right_index);
- in_out_t result = static_cast<in_out_t>(acc);
- tensor_write<in_out_t>(output, shape, out_index, result);
- }
-}
+include::{pseudocode}/operators/REDUCE_SUM.tosac[lines=10..-1]
----
diff --git a/chapters/scatter_gather.adoc b/chapters/scatter_gather.adoc
index d694d39..4632e3a 100644
--- a/chapters/scatter_gather.adoc
+++ b/chapters/scatter_gather.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2021 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -18,12 +18,7 @@ include::{generated}/operators/GATHER.adoc[]
[source,c++]
----
-for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) {
- index_t k = tensor_read<index_t>(indices, [N,W], [n,w]);
- REQUIRE(0 <= k && k < K);
- in_out_t value = tensor_read<in_out_t>(values, [N,K,C], [n,k,c]);
- tensor_write<in_out_t>(output, [N,W,C], [n,w,c], value);
-}
+include::{pseudocode}/operators/GATHER.tosac[lines=10..-1]
----
==== SCATTER
@@ -38,26 +33,5 @@ include::{generated}/operators/SCATTER.adoc[]
[source,c++]
----
-
-// The following array is used to check compliance that an output position
-// is modified at most once.
-bool_t output_modified[N,K,C];
-
-// Copy the values_in tensor to the values_out tensor.
-// Values not written by the scatter operation are unchanged in the output.
-for_each(0 <= n < N, 0 <= k < K, 0 <= c < C) {
- in_out_t value = tensor_read<in_out_t>(values_in, [N,K,C], [n,k,c]);
- tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value);
- output_modified[n,k,c]=false;
-}
-
-// Now perform the SCATTER operation, modifying the positions from the indices tensor
-for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) {
- index_t k = tensor_read<index_t>(indices, [N,W], [n,w]);
- REQUIRE(0 <= k && k < K);
- REQUIRE(output_modified[n,k,c] == false);
- in_out_t value = tensor_read<in_out_t>(input, [N,W,C], [n,w,c]);
- tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value);
- output_modified[n,k,c] = true;
-}
+include::{pseudocode}/operators/SCATTER.tosac[lines=10..-1]
----
diff --git a/chapters/tensor_ops.adoc b/chapters/tensor_ops.adoc
index b3de433..3de5150 100644
--- a/chapters/tensor_ops.adoc
+++ b/chapters/tensor_ops.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -17,34 +17,7 @@ include::{generated}/operators/ARGMAX.adoc[]
[source,c++]
----
-ERROR_IF(axis < 0 || axis >= rank(shape1));
-if (axis == 0) {
- left_shape = [];
-} else {
- left_shape = shape1[0:axis - 1];
-}
-if (axis == rank(shape1)-1) {
- right_shape = [];
-} else {
- right_shape = shape1[axis+1:rank(shape1) - 1];
-}
-ERROR_IF(flatten(left_shape, right_shape) != shape);
-for_each(left_index in left_shape) {
- for_each(right_index in right_shape) {
- in_t max_value = minimum_s<in_t>;
- out_t max_index = 0;
- for (i = 0; i < shape[axis]; i++) {
- dim_t index = flatten(left_index, [i], right_index);
- in_t value = tensor_read<in_t>(input, shape1, index);
- if (apply_max_s<in_t>(value, max_value) != max_value) {
- max_value = value;
- max_index = i;
- }
- }
- dim_t index = flatten(left_index, right_index);
- tensor_write<out_t>(output, shape, index, max_index);
- }
-}
+include::{pseudocode}/operators/ARGMAX.tosac[lines=10..-1]
----
==== AVG_POOL2D
@@ -57,47 +30,7 @@ include::{generated}/operators/AVG_POOL2D.adoc[]
[source,c++]
----
-ERROR_IF(in_out_t != i8_t && input_zp != 0); // Zero point only for int8_t
-ERROR_IF(in_out_t != i8_t && output_zp != 0); // Zero point only for int8_t
-ERROR_IF(kernel_y < 1 || kernel_x < 1); // kernel size must be >= 1
-ERROR_IF(stride_y < 1 || stride_x < 1);
-ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
-// Padding must be less than kernel size to avoid
-// a divide-by-zero.
-ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x);
-ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y);
-ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1);
-ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1);
-
-for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW, 0 <= c < C ) {
- in_out_t output_val;
- acc_t acc = 0;
- int count = 0;
- index_t iy = oy * stride_y - pad_top;
- index_t ix = ox * stride_x - pad_left;
- for_each(0 <= ky < kernel_y, 0 <= kx < kernel_x) {
- index_t y = iy + ky;
- index_t x = ix + kx;
- // Only values from the input tensor are used to calculate the
- // average, padding does not count
- if (0 <= y < IH and 0 <= x < IW) {
- count++;
- acc_t value = sign_extend<acc_t>(tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c]));
- value = apply_sub_s<acc_t>(value, sign_extend<acc_t>(input_zp));
- acc = apply_add_s<acc_t>(acc, value);
- }
- }
- if (is_float(in_out_t)) {
- output_val = acc / static_cast<in_out_t>(count);
- } else {
- scale_t scale = reciprocal_scale(count);
- acc = apply_scale_32(acc, scale.multiplier, scale.shift, false);
- acc = apply_add_s<acc_t>(acc, sign_extend<acc_t>(output_zp));
- acc = apply_clip_s<acc_t>(acc, minimum_s<in_out_t>, maximum_s<in_out_t>);
- output_val = static_cast<in_out_t>(acc);
- }
- tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], output_val);
-}
+include::{pseudocode}/operators/AVG_POOL2D.tosac[lines=10..-1]
----
==== CONV2D
@@ -108,37 +41,7 @@ include::{generated}/operators/CONV2D.adoc[]
[source,c++]
----
-ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
-ERROR_IF(weight_t != int8_t && weight_zp != 0);
-ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
-ERROR_IF(stride_y < 1 || stride_x < 1);
-ERROR_IF(dilation_y < 1 || dilation_x < 1);
-ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1);
-ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1);
-ERROR_IF(BC != OC && BC != 1);
-
-for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) {
- out_t acc = 0;
- index_t iy = oy * stride_y - pad_top;
- index_t ix = ox * stride_x - pad_left;
- for_each(0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) {
- index_t y = iy + ky * dilation_y;
- index_t x = ix + kx * dilation_x;
- if (0 <= y < IH && 0 <= x < IW) {
- out_t value = static_cast<out_t>(tensor_read<in_t>(input,
- [N,IH,IW,IC],
- [n,y,x,ic]));
- out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight,
- [OC,KH,KW,IC],
- [oc,ky,kx,ic]));
- value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
- weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
- acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
- }
- }
- acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]);
- tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc);
-}
+include::{pseudocode}/operators/CONV2D.tosac[lines=10..-1]
----
==== CONV3D
@@ -149,40 +52,7 @@ include::{generated}/operators/CONV3D.adoc[]
[source,c++]
----
-ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
-ERROR_IF(weight_t != i8_t && weight_zp != 0);
-ERROR_IF(pad_d0 < 0 || pad_d1 < 0 || pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
-ERROR_IF(stride_d < 1 || stride_y < 1 || stride_x < 1);
-ERROR_IF(dilation_d < 1 || dilation_y < 1 || dilation_x < 1);
-ERROR_IF(OD != idiv_check(ID - 1 + pad_d0 + pad_d1 - (KD - 1) * dilation_d, stride_d) + 1);
-ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1);
-ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1);
-ERROR_IF(BC != OC && BC != 1);
-
-for_each(0 <= n < N, 0 <= od < OD, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) {
- out_t acc = 0;
- index_t id = od * stride_d - pad_d0;
- index_t iy = oy * stride_y - pad_top;
- index_t ix = ox * stride_x - pad_left;
- for_each(0 <= kd < KD, 0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) {
- index_t d = id + kd * dilation_d;
- index_t y = iy + ky * dilation_y;
- index_t x = ix + kx * dilation_x;
- if (0 <= x < IW && 0 <= y < IH && 0 <= d < ID) {
- out_t value = static_cast<out_t>(tensor_read<in_t>(input,
- [N,ID,IH,IW,IC],
- [n,d,y,x,ic]));
- out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight,
- [OC,KD,KH,KW,IC],
- [oc,kd,ky,kx,ic]));
- value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
- weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
- acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
- }
- }
- acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]);
- tensor_write<out_t>(output, [N,OD,OH,OW,OC], [n,od,oy,ox,oc], acc);
-}
+include::{pseudocode}/operators/CONV3D.tosac[lines=10..-1]
----
==== DEPTHWISE_CONV2D
@@ -193,37 +63,7 @@ include::{generated}/operators/DEPTHWISE_CONV2D.adoc[]
[source,c++]
----
-ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
-ERROR_IF(weight_t != i8_t && weight_zp != 0);
-ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
-ERROR_IF(stride_y < 1 || stride_x < 1);
-ERROR_IF(dilation_y < 1 || dilation_x < 1);
-ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1);
-ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1);
-ERROR_IF(BC != C*M && BC != 1);
-
-for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C, 0 <= m < M) {
- out_t acc = 0;
- index_t iy = oy * stride_y - pad_top;
- index_t ix = ox * stride_x - pad_left;
- for_each(0 <= ky < KH, 0 <= kx < KW) {
- index_t y = iy + ky * dilation_y;
- index_t x = ix + kx * dilation_x;
- if (0 <= y < IH && 0 <= x < IW) {
- out_t value = static_cast<out_t>(tensor_read<in_t>(input,
- [N,IH,IW,C],
- [n,y,x,c]));
- out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight,
- [KH,KW,C,M],
- [ky,kx,c,m]));
- value = apply_sub_s<out_t>(value, static_cast<out_t>input_zp);
- weight = apply_sub_s<out_t>(weight, static_cast<out_t>weight_zp);
- acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
- }
- }
- acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : (c * M) + m]);
- tensor_write<out_t>(output, [N,OH,OW,C * M], [n,oy,ox,c * M + m], acc);
-}
+include::{pseudocode}/operators/DEPTHWISE_CONV2D.tosac[lines=10..-1]
----
==== FFT2D
@@ -247,28 +87,7 @@ include::{generated}/operators/FFT2D.adoc[]
[source,c++]
----
-ERROR_IF(!power_of_two(H));
-ERROR_IF(!power_of_two(W));
-
-float sign_val = 1.0;
-
-if (inverse) {
- sign_val = -1.0;
-}
-
-for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W) {
- in_out_t sum_real = 0.0;
- in_out_t sum_imag = 0.0;
- for_each(0 <= iy < H, 0 <= ix < W) {
- in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]);
- in_out_t val_imag = tensor_read<in_out_t>(input_imag, [N,H,W], [n,iy,ix]);
- float_t a = sign_val * 2 * pi() * ((iy * oy) / H + (ix * ox) / W);
- sum_real += val_real * cos(a) + val_imag * sin(a);
- sum_imag += -val_real * sin(a) + val_imag * cos(a);
- }
- tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real);
- tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag);
-}
+include::{pseudocode}/operators/FFT2D.tosac[lines=10..-1]
----
==== FULLY_CONNECTED
@@ -279,22 +98,7 @@ include::{generated}/operators/FULLY_CONNECTED.adoc[]
[source,c++]
----
-ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
-ERROR_IF(weight_t != i8_t && weight_zp != 0);
-ERROR_IF(BC != OC && BC != 1);
-
-for_each(0 <= n < N, 0 <= oc < OC) {
- out_t acc = 0;
- for_each(0 <= ic < IC) {
- out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IC], [n,ic]));
- out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,IC], [oc,ic]));
- value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
- weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
- acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
- }
- acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]);
- tensor_write<out_t>(output, [N,OC], [n,oc], acc);
-}
+include::{pseudocode}/operators/FULLY_CONNECTED.tosac[lines=10..-1]
----
==== MATMUL
@@ -305,18 +109,7 @@ include::{generated}/operators/MATMUL.adoc[]
[source,c++]
----
-ERROR_IF(in_t != i8_t && (A_zp != 0 || B_zp != 0)); // Zero point only for int8_t
-for_each(0 <= n < N, 0 <= h < H, 0 <= w < W) {
- out_t acc = 0;
- for_each(0 <= c < C) {
- out_t value1 = static_cast<out_t>(tensor_read<in_t>(A, [N,H,C], [n,h,c]));
- out_t value2 = static_cast<out_t>(tensor_read<in_t>(B, [N,C,W], [n,c,w]));
- value1 = apply_sub_s<out_t>(value1, static_cast<out_t>(A_zp));
- value2 = apply_sub_s<out_t>(value2, static_cast<out_t>(B_zp));
- acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value1 * value2));
- }
- tensor_write<out_t>(output, [N,H,W], [n,h,w], acc);
-}
+include::{pseudocode}/operators/MATMUL.tosac[lines=10..-1]
----
==== MAX_POOL2D
@@ -327,30 +120,7 @@ include::{generated}/operators/MAX_POOL2D.adoc[]
[source,c++]
----
-ERROR_IF(kernel_y < 1 || kernel_x < 1); // kernel size must be >= 1
-ERROR_IF(stride_y < 1 || stride_x < 1);
-ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
-// Padding must be less than kernel size, otherwise no
-// input values will be used.
-ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x);
-ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y);
-ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1);
-ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1);
-
-for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W, 0 <= c < C ) {
- in_out_t acc = minimum_value<in_out_t>;
- index_t iy = oy * stride_y - pad_top;
- index_t ix = ox * stride_x - pad_left;
- for_each( 0 <= ky < kernel_y, 0 <= kx < kernel_x ) {
- index_t y = iy + ky;
- index_t x = ix + kx;
- if (y >= 0 && y < IH && x >= 0 && x < IW) {
- in_out_t value = tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c]);
- acc = apply_max_s<in_out_t>(acc, value);
- }
- }
- tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc);
-}
+include::{pseudocode}/operators/MAX_POOL2D.tosac[lines=10..-1]
----
==== RFFT2D
@@ -368,21 +138,7 @@ include::{generated}/operators/RFFT2D.adoc[]
[source,c++]
----
-ERROR_IF(!power_of_two(H));
-ERROR_IF(!power_of_two(W));
-
-for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W/2 + 1) {
- in_out_t sum_real = 0.0;
- in_out_t sum_imag = 0.0;
- for_each(0 <= iy < H, 0 <= ix < W) {
- in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]);
- float_t a = 2 * pi() * ((iy * oy) / H + (ix * ox) / W);
- sum_real += val_real * cos(a);
- sum_imag += -val_real * sin(a);
- }
- tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real);
- tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag);
-}
+include::{pseudocode}/operators/RFFT2D.tosac[lines=10..-1]
----
==== TRANSPOSE_CONV2D
@@ -393,30 +149,5 @@ include::{generated}/operators/TRANSPOSE_CONV2D.adoc[]
[source,c++]
----
-ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only allowed for int8_t
-ERROR_IF(weight_t != i8_t && weight_zp != 0);
-ERROR_IF(out_pad_top <= -KH || out_pad_bottom <= -KH);
-ERROR_IF(out_pad_left <= -KW || out_pad_right <= -KW);
-ERROR_IF(stride_y < 1 || stride_x < 1);
-ERROR_IF(OH != (IH - 1) * stride_y + out_pad_top + out_pad_bottom + KH);
-ERROR_IF(OW != (IW - 1) * stride_x + out_pad_left + out_pad_right + KW);
-ERROR_IF(BC != OC && BC != 1);
-
-for_each(index in [N, OH, OW, OC]) {
- tensor_write<out_t>(output, [N,OH,OW,OC], index, bias[(BC == 1) ? 0 : index[3]])
-}
-for_each(0 <= n < N, 0 <= iy < IH, 0 <= ix < IW, 0 <= oc < OC,
- 0 <= ic < IC, 0 <= ky < KH, 0 <= kx < KW) {
- index_t oy = iy * stride_y + out_pad_top + ky;
- index_t ox = ix * stride_x + out_pad_left + kx;
- if (oy >= 0 && oy < OH && ox >= 0 && ox < OW) {
- out_t acc = static_cast<out_t>(tensor_read<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc]));
- out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IH,IW,IC], [n,iy,ix,ic]));
- out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,KH,KW,IC], [oc,ky,kx,ic]));
- value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
- weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
- acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
- tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc);
- }
-}
+include::{pseudocode}/operators/TRANSPOSE_CONV2D.tosac[lines=10..-1]
----
diff --git a/chapters/type_conversion.adoc b/chapters/type_conversion.adoc
index 90aca2d..71be3ac 100644
--- a/chapters/type_conversion.adoc
+++ b/chapters/type_conversion.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2020-2023 ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -17,24 +17,7 @@ include::{generated}/operators/CAST.adoc[]
[source,c++]
----
-for_each(index in shape) {
- in_t in = tensor_read<in_t>(input, shape, index);
- out_t out;
- if (out_t == bool_t) {
- out = (in != 0) ? true : false;
- } else if (in_t == bool_t) {
- out = (in) ? 1 : 0;
- } else if (out_t == fp16_t || out_t == bf16_t || out_t == fp32_t) {
- out = round_to_nearest_float(in);
- } else if (in_t == fp16_t || in_t == bf16_t || in_t == fp32_t) {
- out = apply_clip<out_t>(round_to_nearest_int(in), minimum<out_t>, maximum<out_t>);
- } else if (sizeof(out_t) >= sizeof(in_t)) {
- out = sign_extend<out_t>(in);
- } else {
- out = truncate(in);
- }
- tensor_write<out_t>(output, shape, index, out)
-}
+include::{pseudocode}/operators/CAST.tosac[lines=10..-1]
----
==== RESCALE
@@ -45,53 +28,5 @@ include::{generated}/operators/RESCALE.adoc[]
[source,c++]
----
-for_each(index in shape) {
- // uint16 values can have zero_point 0 or 32768
- // int8/uint8 can have zero point within their valid range
- // No other types can have zero point != 0
- ERROR_IF(in_t != i8_t &&
- (in_t != i16_t || input_unsigned == False) && input_zp != 0);
- ERROR_IF(out_t != i8_t &&
- (out_t != i16_t || output_unsigned == False) && output_zp != 0);
- ERROR_IF(in_t == i16_t && input_unsigned == True && input_zp != 0 && input_zp != 32768);
- ERROR_IF(out_t == i16_t && output_unsigned == True && output_zp != 0 && output_zp != 32768);
- ERROR_IF(scale32 && in_t == i48_t);
- ERROR_IF(!scale32 && double_round);
- ERROR_IF(in_t == i16_t && out_t == i32_t && input_unsigned);
- ERROR_IF(in_t == i32_t && out_t == i16_t && output_unsigned);
-
- in_t in_value = tensor_read<in_t>(input, shape, index);
-
- int48_t value, extended_in_zp;
- if (input_unsigned) {
- value = zero_extend<int48_t>(in_value);
- extended_in_zp = zero_extend<int48_t>(input_zp);
- }
- else {
- value = sign_extend<int48_t>(value);
- extended_in_zp = sign_extend<int48_t>(input_zp);
- }
-
- value = value - extended_in_zp;
- int c = (per_channel) ? index[rank(input) - 1] : 0;
- int32_t result = (scale32) ?
- apply_scale_32(value, multiplier[c], shift[c], double_round) :
- apply_scale_16(value, multiplier[c], shift[c]);
-
- if (output_unsigned) {
- int32_t extended_out_zp = zero_extend<int32_t>(output_zp);
- result = apply_add_s<int32_t>(result, extended_out_zp);
- out_t out = static_cast<out_t>(apply_clip<int32_t>(result,
- minimum_u<out_t>,
- maximum_u<out_t>));
- }
- else {
- int32_t extended_out_zp = sign_extend<int32_t>(output_zp);
- result = apply_add_s<int32_t>(result, extended_out_zp);
- out_t out = static_cast<out_t>(apply_clip<int32_t>(result,
- minimum_s<out_t>,
- maximum_s<out_t>));
- }
- tensor_write<out_t>(output, shape, index, out);
-}
+include::{pseudocode}/operators/RESCALE.tosac[lines=10..-1]
----
diff --git a/chapters/variable.adoc b/chapters/variable.adoc
index 1f7da51..8522f5e 100644
--- a/chapters/variable.adoc
+++ b/chapters/variable.adoc
@@ -1,7 +1,7 @@
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
-// (C) COPYRIGHT 2023 ARM Limited
+// (C) COPYRIGHT 2023-2024 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
@@ -21,31 +21,7 @@ include::{generated}/operators/VARIABLE.adoc[]
[source,c++]
----
-
-tensor_t var_tensor = variable_tensor_lookup(uid);
-
-// Invocation for the first time
-if (var_tensor == NULL) {
- // Allocate the persistent mutable memory for the variable tensor
- tensor_t var_tensor = variable_tensor_allocate<var_t>(var_shape, uid);
-
- if (initial_value != NULL) {
- REQUIRE(var_t == in_t);
- REQUIRE(var_shape == shape);
- for_each (index in shape) {
- // Copy data from initial_value to var_tensor
- in_t value = tensor_read<in_t>(initial_value, shape, index);
- tensor_write<in_t>(var_tensor.data, var_shape, index, value);
- }
- var_tensor.is_written = true;
- }
-} else { // Variable tensor has already been declared
- // It's invalid to declare the second variable with the same uid in a single graph execution,
- REQUIRE(!var_tensor.seen);
-}
-
-var_tensor.seen = true;
-
+include::{pseudocode}/operators/VARIABLE.tosac[lines=10..-1]
----
==== VARIABLE_WRITE
@@ -56,26 +32,7 @@ include::{generated}/operators/VARIABLE_WRITE.adoc[]
[source,c++]
----
-
-tensor_t. variable_tensor = variable_tensor_lookup(uid);
-// Check this variable tensor has been declared
-REQUIRE(variable_tensor);
-// The tensor has to be seen before to be written to
-// The seen variable is cleared before each graph execution and set in declaration
-REQUIRE(variable_tensor.seen);
-// Input tensor's shape and variable_tensor's shape have to match
-REQUIRE(variable_tensor.shape == shape);
-// Input tensor's shape and variable_tensor's type have to match
-REQUIRE(variable_tensor.type == in_t);
-
-for_each (index in shape) {
- // Write data from the input to the pseudo-buffer resource
- in_t value = tensor_read<in_t>(input1, shape, index);
- tensor_write<tensor_t>(variable_tensor.data, variable_tensor.shape, index, value);
-}
-
-variable_tensor.is_written = true;
-
+include::{pseudocode}/operators/VARIABLE_WRITE.tosac[lines=10..-1]
----
==== VARIABLE_READ
@@ -86,21 +43,5 @@ include::{generated}/operators/VARIABLE_READ.adoc[]
[source,c++]
----
-
-tensor_t variable_tensor = variable_tensor_lookup(uid);
-// Check this variable tensor has been decalred
-REQUIRE(variable_tensor != NULL);
-// Check this variable tensor has been written
-REQUIRE(variable_tensor.is_written);
-// Output tensor's shape and variable_tensor's shape have to match
-REQUIRE(variable_tensor.shape == shape);
-// Output tensor's shape and variable_tensor's type have to match
-REQUIRE(variable_tensor.type == out_t);
-
-for_each (index in shape) {
- // Read data from pseudo-buffer resource to the output
- out_t value = tensor_read<tensor_t>(variable_tensor.data, variable_tensor.shape, index);
- tensor_write<out_t>(input1, shape, index, value);
-}
-
+include::{pseudocode}/operators/VARIABLE_READ.tosac[lines=10..-1]
----
diff --git a/pseudocode/operators/ABS.tosac b/pseudocode/operators/ABS.tosac
new file mode 100644
index 0000000..5f7f56e
--- /dev/null
+++ b/pseudocode/operators/ABS.tosac
@@ -0,0 +1,19 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ if (is_floating_point(in_out_t) && value1 == -0.0) {
+ value1 = 0.0;
+ }
+ if (static_cast<int32_t>(value1) < 0.0) {
+ value1 = apply_sub_s<in_out_t>(0, value1);
+ }
+ tensor_write<in_out_t>(output, shape, index, value1);
+}
diff --git a/pseudocode/operators/ADD.tosac b/pseudocode/operators/ADD.tosac
new file mode 100644
index 0000000..61db3f6
--- /dev/null
+++ b/pseudocode/operators/ADD.tosac
@@ -0,0 +1,26 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+if (in_out_t == shape_t) {
+ ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
+ shape_t value1 = tensor_read<shape_t>(input1, [], []);
+ shape_t value2 = tensor_read<shape_t>(input2, [], []);
+ shape_t result = apply_add_s<shape_t>(value1, value2);
+ tensor_write<shape_t>(output, [], [], result);
+} else {
+ ERROR_IF(shape != broadcast_shape(shape1, shape2));
+ for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = apply_add_s<in_out_t>(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+ }
+}
diff --git a/pseudocode/operators/ARGMAX.tosac b/pseudocode/operators/ARGMAX.tosac
new file mode 100644
index 0000000..bbe301f
--- /dev/null
+++ b/pseudocode/operators/ARGMAX.tosac
@@ -0,0 +1,37 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+if (axis == 0) {
+ left_shape = [];
+} else {
+ left_shape = shape1[0:axis - 1];
+}
+if (axis == rank(shape1)-1) {
+ right_shape = [];
+} else {
+ right_shape = shape1[axis+1:rank(shape1) - 1];
+}
+ERROR_IF(flatten(left_shape, right_shape) != shape);
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ in_t max_value = minimum_s<in_t>;
+ out_t max_index = 0;
+ for (i = 0; i < shape[axis]; i++) {
+ dim_t index = flatten(left_index, [i], right_index);
+ in_t value = tensor_read<in_t>(input, shape1, index);
+ if (apply_max_s<in_t>(value, max_value) != max_value) {
+ max_value = value;
+ max_index = i;
+ }
+ }
+ dim_t index = flatten(left_index, right_index);
+ tensor_write<out_t>(output, shape, index, max_index);
+ }
+}
diff --git a/pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac b/pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac
new file mode 100644
index 0000000..4077146
--- /dev/null
+++ b/pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac
@@ -0,0 +1,29 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+
+ // Ensure that shift amount is appropriate for the data type
+ REQUIRE((in_out_t == i32_t && 0 <= value2 && value2 <= 31) ||
+ (in_out_t == i16_t && 0 <= value2 && value2 <= 15) ||
+ (in_out_t == i8_t && 0 <= value2 && value2 <= 7));
+
+ in_out_t result = apply_arith_rshift<in_out_t>(value1, value2);
+ if (round == true && static_cast<int32_t>(value2) > 0 &&
+ (apply_arith_rshift<in_out_t>(value1, apply_sub_s<in_out_t>(value2, 1)) & 1 != 0) {
+ result = result + 1;
+ }
+ result = apply_clip_s<in_out_t>(result, minimum_s<in_out_t>, maximum_s<in_out_t>);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/AVG_POOL2D.tosac b/pseudocode/operators/AVG_POOL2D.tosac
new file mode 100644
index 0000000..f452d12
--- /dev/null
+++ b/pseudocode/operators/AVG_POOL2D.tosac
@@ -0,0 +1,50 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_out_t != i8_t && input_zp != 0); // Zero point only for int8_t
+ERROR_IF(in_out_t != i8_t && output_zp != 0); // Zero point only for int8_t
+ERROR_IF(kernel_y < 1 || kernel_x < 1); // kernel size must be >= 1
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+// Padding must be less than kernel size to avoid
+// a divide-by-zero.
+ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x);
+ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y);
+ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1);
+ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1);
+
+for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW, 0 <= c < C ) {
+ in_out_t output_val;
+ acc_t acc = 0;
+ int count = 0;
+ index_t iy = oy * stride_y - pad_top;
+ index_t ix = ox * stride_x - pad_left;
+ for_each(0 <= ky < kernel_y, 0 <= kx < kernel_x) {
+ index_t y = iy + ky;
+ index_t x = ix + kx;
+ // Only values from the input tensor are used to calculate the
+ // average, padding does not count
+ if (0 <= y < IH and 0 <= x < IW) {
+ count++;
+ acc_t value = sign_extend<acc_t>(tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c]));
+ value = apply_sub_s<acc_t>(value, sign_extend<acc_t>(input_zp));
+ acc = apply_add_s<acc_t>(acc, value);
+ }
+ }
+ if (is_float(in_out_t)) {
+ output_val = acc / static_cast<in_out_t>(count);
+ } else {
+ scale_t scale = reciprocal_scale(count);
+ acc = apply_scale_32(acc, scale.multiplier, scale.shift, false);
+ acc = apply_add_s<acc_t>(acc, sign_extend<acc_t>(output_zp));
+ acc = apply_clip_s<acc_t>(acc, minimum_s<in_out_t>, maximum_s<in_out_t>);
+ output_val = static_cast<in_out_t>(acc);
+ }
+ tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], output_val);
+}
diff --git a/pseudocode/operators/BITWISE_AND.tosac b/pseudocode/operators/BITWISE_AND.tosac
new file mode 100644
index 0000000..5efe3ee
--- /dev/null
+++ b/pseudocode/operators/BITWISE_AND.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = value1 & value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/BITWISE_NOT.tosac b/pseudocode/operators/BITWISE_NOT.tosac
new file mode 100644
index 0000000..1831e75
--- /dev/null
+++ b/pseudocode/operators/BITWISE_NOT.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ in_out_t result = ~value1;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/BITWISE_OR.tosac b/pseudocode/operators/BITWISE_OR.tosac
new file mode 100644
index 0000000..b2b7fc6
--- /dev/null
+++ b/pseudocode/operators/BITWISE_OR.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = value1 | value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/BITWISE_XOR.tosac b/pseudocode/operators/BITWISE_XOR.tosac
new file mode 100644
index 0000000..98f0d2c
--- /dev/null
+++ b/pseudocode/operators/BITWISE_XOR.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = value1 ^ value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/CAST.tosac b/pseudocode/operators/CAST.tosac
new file mode 100644
index 0000000..f6306b3
--- /dev/null
+++ b/pseudocode/operators/CAST.tosac
@@ -0,0 +1,27 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_t in = tensor_read<in_t>(input, shape, index);
+ out_t out;
+ if (out_t == bool_t) {
+ out = (in != 0) ? true : false;
+ } else if (in_t == bool_t) {
+ out = (in) ? 1 : 0;
+ } else if (out_t == fp16_t || out_t == bf16_t || out_t == fp32_t) {
+ out = round_to_nearest_float(in);
+ } else if (in_t == fp16_t || in_t == bf16_t || in_t == fp32_t) {
+ out = apply_clip<out_t>(round_to_nearest_int(in), minimum<out_t>, maximum<out_t>);
+ } else if (sizeof(out_t) >= sizeof(in_t)) {
+ out = sign_extend<out_t>(in);
+ } else {
+ out = truncate(in);
+ }
+ tensor_write<out_t>(output, shape, index, out)
+}
diff --git a/pseudocode/operators/CEIL.tosac b/pseudocode/operators/CEIL.tosac
new file mode 100644
index 0000000..890eb8a
--- /dev/null
+++ b/pseudocode/operators/CEIL.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ in_out_t result = apply_ceil<in_out_t>(value1);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/CLAMP.tosac b/pseudocode/operators/CLAMP.tosac
new file mode 100644
index 0000000..7a26d50
--- /dev/null
+++ b/pseudocode/operators/CLAMP.tosac
@@ -0,0 +1,15 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(max_val < min_val);
+for_each(index in shape) {
+ in_out_t value = tensor_read<in_out_t>(input, shape, index);
+ value = apply_clip<in_out_t>(value, min_val, max_val);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/CLZ.tosac b/pseudocode/operators/CLZ.tosac
new file mode 100644
index 0000000..a3a7180
--- /dev/null
+++ b/pseudocode/operators/CLZ.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ in_out_t result = count_leading_zeros(value1);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/CONCAT.tosac b/pseudocode/operators/CONCAT.tosac
new file mode 100644
index 0000000..f9329af
--- /dev/null
+++ b/pseudocode/operators/CONCAT.tosac
@@ -0,0 +1,32 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= max(1,rank(shapes1[0])));
+ERROR_IF(shape[axis] != sum(shape_dim(shapes1[k], axis) for all k))
+ERROR_IF(in_out_t == shape_t && rank(shape) > 1);
+// The following checks ensure all inputs are compatible for concatenation
+for_each(input_shape in shapes1) {
+ ERROR_IF(rank(input_shape) != rank(shapes1[0]));
+ for_each(index in input_shape) {
+ ERROR_IF(index != axis && input_shape[index] != shapes1[0][index]);
+ }
+}
+for_each(index1 in shape) {
+ dim_t index2 = index1;
+ for (tensor t = 0; t < length(input1); t++) {
+ // Continue to concatenate along axis from each tensor
+ // For each output location, we are looking for the
+ // appropriate input tensor
+ if (index2[axis] >= 0 && index2[axis] < shape_dim(shapes1[t], axis)) {
+ in_out_t value = tensor_read<in_out_t>(input1[t], shapes1[t], index2);
+ tensor_write<in_out_t>(output, shape, index1, value);
+ }
+ index2[axis] = index2[axis] - shape_dim(shapes1[t], axis);
+ }
+}
diff --git a/pseudocode/operators/COND_IF.tosac b/pseudocode/operators/COND_IF.tosac
new file mode 100644
index 0000000..584cc83
--- /dev/null
+++ b/pseudocode/operators/COND_IF.tosac
@@ -0,0 +1,23 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(tosa_nesting_depth >= MAX_NESTING);
+ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(then_graph));
+ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(else_graph));
+ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(then_graph));
+ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(else_graph));
+ERROR_IF(tensor_size(shape) != 1);
+
+tosa_nesting_depth++;
+if (condition[0]) {
+ tosa_execute_graph(then_graph, input_list, output_list);
+} else {
+ tosa_execute_graph(else_graph, input_list, output_list);
+}
+tosa_nesting_depth--;
diff --git a/pseudocode/operators/CONV2D.tosac b/pseudocode/operators/CONV2D.tosac
new file mode 100644
index 0000000..12620e9
--- /dev/null
+++ b/pseudocode/operators/CONV2D.tosac
@@ -0,0 +1,40 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
+ERROR_IF(weight_t != int8_t && weight_zp != 0);
+ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(dilation_y < 1 || dilation_x < 1);
+ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1);
+ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1);
+ERROR_IF(BC != OC && BC != 1);
+
+for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) {
+ out_t acc = 0;
+ index_t iy = oy * stride_y - pad_top;
+ index_t ix = ox * stride_x - pad_left;
+ for_each(0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) {
+ index_t y = iy + ky * dilation_y;
+ index_t x = ix + kx * dilation_x;
+ if (0 <= y < IH && 0 <= x < IW) {
+ out_t value = static_cast<out_t>(tensor_read<in_t>(input,
+ [N,IH,IW,IC],
+ [n,y,x,ic]));
+ out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight,
+ [OC,KH,KW,IC],
+ [oc,ky,kx,ic]));
+ value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
+ weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
+ acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
+ }
+ }
+ acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]);
+ tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc);
+}
diff --git a/pseudocode/operators/CONV3D.tosac b/pseudocode/operators/CONV3D.tosac
new file mode 100644
index 0000000..a0f9130
--- /dev/null
+++ b/pseudocode/operators/CONV3D.tosac
@@ -0,0 +1,43 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
+ERROR_IF(weight_t != i8_t && weight_zp != 0);
+ERROR_IF(pad_d0 < 0 || pad_d1 < 0 || pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+ERROR_IF(stride_d < 1 || stride_y < 1 || stride_x < 1);
+ERROR_IF(dilation_d < 1 || dilation_y < 1 || dilation_x < 1);
+ERROR_IF(OD != idiv_check(ID - 1 + pad_d0 + pad_d1 - (KD - 1) * dilation_d, stride_d) + 1);
+ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1);
+ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1);
+ERROR_IF(BC != OC && BC != 1);
+
+for_each(0 <= n < N, 0 <= od < OD, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) {
+ out_t acc = 0;
+ index_t id = od * stride_d - pad_d0;
+ index_t iy = oy * stride_y - pad_top;
+ index_t ix = ox * stride_x - pad_left;
+ for_each(0 <= kd < KD, 0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) {
+ index_t d = id + kd * dilation_d;
+ index_t y = iy + ky * dilation_y;
+ index_t x = ix + kx * dilation_x;
+ if (0 <= x < IW && 0 <= y < IH && 0 <= d < ID) {
+ out_t value = static_cast<out_t>(tensor_read<in_t>(input,
+ [N,ID,IH,IW,IC],
+ [n,d,y,x,ic]));
+ out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight,
+ [OC,KD,KH,KW,IC],
+ [oc,kd,ky,kx,ic]));
+ value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
+ weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
+ acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
+ }
+ }
+ acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]);
+ tensor_write<out_t>(output, [N,OD,OH,OW,OC], [n,od,oy,ox,oc], acc);
+}
diff --git a/pseudocode/operators/DEPTHWISE_CONV2D.tosac b/pseudocode/operators/DEPTHWISE_CONV2D.tosac
new file mode 100644
index 0000000..c7a0f0f
--- /dev/null
+++ b/pseudocode/operators/DEPTHWISE_CONV2D.tosac
@@ -0,0 +1,40 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
+ERROR_IF(weight_t != i8_t && weight_zp != 0);
+ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(dilation_y < 1 || dilation_x < 1);
+ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1);
+ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1);
+ERROR_IF(BC != C*M && BC != 1);
+
+for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C, 0 <= m < M) {
+ out_t acc = 0;
+ index_t iy = oy * stride_y - pad_top;
+ index_t ix = ox * stride_x - pad_left;
+ for_each(0 <= ky < KH, 0 <= kx < KW) {
+ index_t y = iy + ky * dilation_y;
+ index_t x = ix + kx * dilation_x;
+ if (0 <= y < IH && 0 <= x < IW) {
+ out_t value = static_cast<out_t>(tensor_read<in_t>(input,
+ [N,IH,IW,C],
+ [n,y,x,c]));
+ out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight,
+ [KH,KW,C,M],
+ [ky,kx,c,m]));
+ value = apply_sub_s<out_t>(value, static_cast<out_t>input_zp);
+ weight = apply_sub_s<out_t>(weight, static_cast<out_t>weight_zp);
+ acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
+ }
+ }
+ acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : (c * M) + m]);
+ tensor_write<out_t>(output, [N,OH,OW,C * M], [n,oy,ox,c * M + m], acc);
+}
diff --git a/pseudocode/operators/DIM.tosac b/pseudocode/operators/DIM.tosac
new file mode 100644
index 0000000..7131173
--- /dev/null
+++ b/pseudocode/operators/DIM.tosac
@@ -0,0 +1,11 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis >= rank(shape));
+tensor_write<shape_t>(output, [], [], shape_dim(shape, axis));
diff --git a/pseudocode/operators/EQUAL.tosac b/pseudocode/operators/EQUAL.tosac
new file mode 100644
index 0000000..67e0b64
--- /dev/null
+++ b/pseudocode/operators/EQUAL.tosac
@@ -0,0 +1,22 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_t value1 = tensor_read<in_t>(input1, shape1, index1);
+ in_t value2 = tensor_read<in_t>(input2, shape2, index2);
+ out_t result;
+ if (isNaN(value1) || isNaN(value2))
+ result = False;
+ else
+ result = (value1 == value2) ? True : False;
+ tensor_write<out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/EXP.tosac b/pseudocode/operators/EXP.tosac
new file mode 100644
index 0000000..4ef74a0
--- /dev/null
+++ b/pseudocode/operators/EXP.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ in_out_t result = apply_exp<in_out_t>(value1);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/FFT2D.tosac b/pseudocode/operators/FFT2D.tosac
new file mode 100644
index 0000000..a958aa4
--- /dev/null
+++ b/pseudocode/operators/FFT2D.tosac
@@ -0,0 +1,31 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(!power_of_two(H));
+ERROR_IF(!power_of_two(W));
+
+float sign_val = 1.0;
+
+if (inverse) {
+ sign_val = -1.0;
+}
+
+for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W) {
+ in_out_t sum_real = 0.0;
+ in_out_t sum_imag = 0.0;
+ for_each(0 <= iy < H, 0 <= ix < W) {
+ in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]);
+ in_out_t val_imag = tensor_read<in_out_t>(input_imag, [N,H,W], [n,iy,ix]);
+ float_t a = sign_val * 2 * pi() * ((iy * oy) / H + (ix * ox) / W);
+ sum_real += val_real * cos(a) + val_imag * sin(a);
+ sum_imag += -val_real * sin(a) + val_imag * cos(a);
+ }
+ tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real);
+ tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag);
+}
diff --git a/pseudocode/operators/FLOOR.tosac b/pseudocode/operators/FLOOR.tosac
new file mode 100644
index 0000000..fc893b6
--- /dev/null
+++ b/pseudocode/operators/FLOOR.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ in_out_t result = apply_floor<in_out_t>(value1);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/FULLY_CONNECTED.tosac b/pseudocode/operators/FULLY_CONNECTED.tosac
new file mode 100644
index 0000000..fbacccc
--- /dev/null
+++ b/pseudocode/operators/FULLY_CONNECTED.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t
+ERROR_IF(weight_t != i8_t && weight_zp != 0);
+ERROR_IF(BC != OC && BC != 1);
+
+for_each(0 <= n < N, 0 <= oc < OC) {
+ out_t acc = 0;
+ for_each(0 <= ic < IC) {
+ out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IC], [n,ic]));
+ out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,IC], [oc,ic]));
+ value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
+ weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
+ acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
+ }
+ acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]);
+ tensor_write<out_t>(output, [N,OC], [n,oc], acc);
+}
diff --git a/pseudocode/operators/GATHER.tosac b/pseudocode/operators/GATHER.tosac
new file mode 100644
index 0000000..82c159d
--- /dev/null
+++ b/pseudocode/operators/GATHER.tosac
@@ -0,0 +1,15 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) {
+ index_t k = tensor_read<index_t>(indices, [N,W], [n,w]);
+ REQUIRE(0 <= k && k < K);
+ in_out_t value = tensor_read<in_out_t>(values, [N,K,C], [n,k,c]);
+ tensor_write<in_out_t>(output, [N,W,C], [n,w,c], value);
+}
diff --git a/pseudocode/operators/GREATER.tosac b/pseudocode/operators/GREATER.tosac
new file mode 100644
index 0000000..af8b4c9
--- /dev/null
+++ b/pseudocode/operators/GREATER.tosac
@@ -0,0 +1,22 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_t value1 = tensor_read<in_t>(input1, shape1, index1);
+ in_t value2 = tensor_read<in_t>(input2, shape2, index2);
+ out_t result;
+ if (isNaN(value1) || isNaN(value2))
+ result = False;
+ else
+ result = (value1 > value2) ? True : False;
+ tensor_write<out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/GREATER_EQUAL.tosac b/pseudocode/operators/GREATER_EQUAL.tosac
new file mode 100644
index 0000000..e45990d
--- /dev/null
+++ b/pseudocode/operators/GREATER_EQUAL.tosac
@@ -0,0 +1,22 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_t value1 = tensor_read<in_t>(input1, shape1, index1);
+ in_t value2 = tensor_read<in_t>(input2, shape2, index2);
+ out_t result;
+ if (isNaN(value1) || isNaN(value2))
+ result = False;
+ else
+ result = (value1 >= value2) ? True : False;
+ tensor_write<out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/INTDIV.tosac b/pseudocode/operators/INTDIV.tosac
new file mode 100644
index 0000000..b6d46cc
--- /dev/null
+++ b/pseudocode/operators/INTDIV.tosac
@@ -0,0 +1,31 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+if (in_out_t == shape_t) {
+ ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
+ shape_t value1 = tensor_read<shape_t>(input1, [], []);
+ shape_t value2 = tensor_read<shape_t>(input2, [], []);
+ REQUIRE(value2 != 0);
+ shape_t result = value1 / value2;
+ tensor_write<shape_t>(output, [], [], result);
+} else {
+ ERROR_IF(shape != broadcast_shape(shape1, shape2));
+ for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ REQUIRE(value2 != 0);
+ // This catches the case where we divide minimum<in_out_t> by -1
+ // which is not representable in two's complement
+ REQUIRE(static_cast<int64_t>(value1) / static_cast<int64_t>(value2) <= maximum_s<in_out_t>);
+ in_out_t result = apply_intdiv_s<in_out_t>(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+ }
+}
diff --git a/pseudocode/operators/LOG.tosac b/pseudocode/operators/LOG.tosac
new file mode 100644
index 0000000..2c56105
--- /dev/null
+++ b/pseudocode/operators/LOG.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ in_out_t result = apply_log<in_out_t>(value1);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/LOGICAL_AND.tosac b/pseudocode/operators/LOGICAL_AND.tosac
new file mode 100644
index 0000000..ff03804
--- /dev/null
+++ b/pseudocode/operators/LOGICAL_AND.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = value1 && value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac b/pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac
new file mode 100644
index 0000000..4057163
--- /dev/null
+++ b/pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac
@@ -0,0 +1,19 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ REQUIRE(0 <= value2 && value2 <= 31);
+ in_out_t result = value1 << value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/LOGICAL_NOT.tosac b/pseudocode/operators/LOGICAL_NOT.tosac
new file mode 100644
index 0000000..b07e668
--- /dev/null
+++ b/pseudocode/operators/LOGICAL_NOT.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index);
+ in_out_t result = !value1;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/LOGICAL_OR.tosac b/pseudocode/operators/LOGICAL_OR.tosac
new file mode 100644
index 0000000..4e3aa9e
--- /dev/null
+++ b/pseudocode/operators/LOGICAL_OR.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = value1 || value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac b/pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac
new file mode 100644
index 0000000..5e80211
--- /dev/null
+++ b/pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac
@@ -0,0 +1,20 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ REQUIRE(0 <= static_cast<int32_t>(value2) && static_cast<int32_t>(value2) <= 31);
+ // Logical shifts happen as unsigned types internally
+ in_out_t result = apply_logical_rshift<in_out_t>(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/LOGICAL_XOR.tosac b/pseudocode/operators/LOGICAL_XOR.tosac
new file mode 100644
index 0000000..fbb485b
--- /dev/null
+++ b/pseudocode/operators/LOGICAL_XOR.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = value1 != value2;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/MATMUL.tosac b/pseudocode/operators/MATMUL.tosac
new file mode 100644
index 0000000..3afda4d
--- /dev/null
+++ b/pseudocode/operators/MATMUL.tosac
@@ -0,0 +1,21 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_t != i8_t && (A_zp != 0 || B_zp != 0)); // Zero point only for int8_t
+for_each(0 <= n < N, 0 <= h < H, 0 <= w < W) {
+ out_t acc = 0;
+ for_each(0 <= c < C) {
+ out_t value1 = static_cast<out_t>(tensor_read<in_t>(A, [N,H,C], [n,h,c]));
+ out_t value2 = static_cast<out_t>(tensor_read<in_t>(B, [N,C,W], [n,c,w]));
+ value1 = apply_sub_s<out_t>(value1, static_cast<out_t>(A_zp));
+ value2 = apply_sub_s<out_t>(value2, static_cast<out_t>(B_zp));
+ acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value1 * value2));
+ }
+ tensor_write<out_t>(output, [N,H,W], [n,h,w], acc);
+}
diff --git a/pseudocode/operators/MAXIMUM.tosac b/pseudocode/operators/MAXIMUM.tosac
new file mode 100644
index 0000000..0f4f3b7
--- /dev/null
+++ b/pseudocode/operators/MAXIMUM.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = apply_max_s<in_out_t>(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/MAX_POOL2D.tosac b/pseudocode/operators/MAX_POOL2D.tosac
new file mode 100644
index 0000000..a33b80a
--- /dev/null
+++ b/pseudocode/operators/MAX_POOL2D.tosac
@@ -0,0 +1,33 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(kernel_y < 1 || kernel_x < 1); // kernel size must be >= 1
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+// Padding must be less than kernel size, otherwise no
+// input values will be used.
+ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x);
+ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y);
+ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1);
+ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1);
+
+for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W, 0 <= c < C ) {
+ in_out_t acc = minimum_value<in_out_t>;
+ index_t iy = oy * stride_y - pad_top;
+ index_t ix = ox * stride_x - pad_left;
+ for_each( 0 <= ky < kernel_y, 0 <= kx < kernel_x ) {
+ index_t y = iy + ky;
+ index_t x = ix + kx;
+ if (y >= 0 && y < IH && x >= 0 && x < IW) {
+ in_out_t value = tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c]);
+ acc = apply_max_s<in_out_t>(acc, value);
+ }
+ }
+ tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc);
+}
diff --git a/pseudocode/operators/MINIMUM.tosac b/pseudocode/operators/MINIMUM.tosac
new file mode 100644
index 0000000..fa47b03
--- /dev/null
+++ b/pseudocode/operators/MINIMUM.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = apply_min_s(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/MUL.tosac b/pseudocode/operators/MUL.tosac
new file mode 100644
index 0000000..078525e
--- /dev/null
+++ b/pseudocode/operators/MUL.tosac
@@ -0,0 +1,37 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+if (in_out_t == shape_t) {
+ ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
+ shape_t value1 = tensor_read<shape_t>(input1, [], []);
+ shape_t value2 = tensor_read<shape_t>(input2, [], []);
+ shape_t result = value1 * value2;
+ tensor_write<shape_t>(output, [], [], result);
+} else {
+ REQUIRE(0 <= shift && shift <= 63);
+ REQUIRE(in_t == int32_t || shift == 0);
+ ERROR_IF(shape != broadcast_shape(shape1, shape2));
+ for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_t value1 = tensor_read<in_t>(input1, shape1, index1);
+ in_t value2 = tensor_read<in_t>(input2, shape2, index2);
+ out_t result;
+ if (in_t == i32_t && shift > 0) {
+ int64_t product = sign_extend<int64_t>(value1) * sign_extend<int64_t>(value2);
+ int64_t round = static_cast<int64_t>(1) << (shift - 1);
+ product = (product + round) >> shift;
+ REQUIRE(product >= minimum_s<i32_t> && product <= maximum_s<i32_t>)
+ result = product;
+ } else {
+ result = apply_mul_s(value1, value2); // low 32-bits of result for i32_t
+ }
+ tensor_write<out_t>(output, shape, index, result);
+ }
+}
diff --git a/pseudocode/operators/NEGATE.tosac b/pseudocode/operators/NEGATE.tosac
new file mode 100644
index 0000000..8c235f1
--- /dev/null
+++ b/pseudocode/operators/NEGATE.tosac
@@ -0,0 +1,22 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_out_t != i8_t && input1_zp != 0) // Zero point only for int8_t
+ERROR_IF(in_out_t != i8_t && output_zp != 0) // Zero point only for int8_t
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape, index);
+ acc_t value = apply_sub_s<acc_t>(sign_extend<acc_t>(value1),
+ sign_extend<acc_t>(input1_zp));
+ value = apply_sub_s<acc_t>(0, value);
+ value = apply_add_s<acc_t>(value, sign_extend<acc_t>(output_zp));
+ in_out_t result = truncate<in_out_t>(apply_clip_s<acc_t>(value,
+ minimum_s<in_out_t>,
+ maximum_s<in_out_t>));
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/PAD.tosac b/pseudocode/operators/PAD.tosac
new file mode 100644
index 0000000..4adf114
--- /dev/null
+++ b/pseudocode/operators/PAD.tosac
@@ -0,0 +1,27 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+// Check output shape matches the padded input shape
+ERROR_IF(rank(shape) != rank(shape1));
+for (i = 0; i < rank(shape); i++) {
+ ERROR_IF(padding[i,0] < 0 || padding[i,1] < 0);
+ ERROR_IF(shape[i] != padding[i, 0] + shape1[i] + padding[i, 1]);
+}
+for_each(index in shape) {
+ dim_t index1 = index;
+ bool_t is_pad = false;
+ for(i = 0; i < rank(shape); i++) {
+ index1[i] = index1[i] - padding[i,0];
+ if (index1[i] < 0 || index[i] >= length(shape[i])) {
+ is_pad = true;
+ }
+ }
+ in_out_t value = is_pad ? pad_const : tensor_read<in_out_t>(input1, shape1, index1);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/POW.tosac b/pseudocode/operators/POW.tosac
new file mode 100644
index 0000000..6ecc8e6
--- /dev/null
+++ b/pseudocode/operators/POW.tosac
@@ -0,0 +1,18 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(shape1, shape2));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = apply_pow<in_out_t>(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/RECIPROCAL.tosac b/pseudocode/operators/RECIPROCAL.tosac
new file mode 100644
index 0000000..9bc086b
--- /dev/null
+++ b/pseudocode/operators/RECIPROCAL.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index);
+ in_out_t result = 1.0 / value1;
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/REDUCE_ALL.tosac b/pseudocode/operators/REDUCE_ALL.tosac
new file mode 100644
index 0000000..7438513
--- /dev/null
+++ b/pseudocode/operators/REDUCE_ALL.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+ERROR_IF(shape[axis] != 1);
+left_shape = (axis > 1) ? shape[0:axis-1] : [];
+right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ in_out_t acc = true;
+ for (i = 0; i < shape1[axis]; i++) {
+ index = flatten(left_index, [i], right_index);
+ in_out_t value = tensor_read<in_out_t>(input, shape1, index);
+ acc = acc && value;
+ }
+ out_index = flatten(left_index, [0], right_index);
+ tensor_write<in_out_t>(output, shape, out_index, acc);
+ }
+}
diff --git a/pseudocode/operators/REDUCE_ANY.tosac b/pseudocode/operators/REDUCE_ANY.tosac
new file mode 100644
index 0000000..3303fac
--- /dev/null
+++ b/pseudocode/operators/REDUCE_ANY.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+ERROR_IF(shape[axis] != 1);
+left_shape = (axis > 1) ? shape[0:axis-1] : [];
+right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ in_out_t acc = false;
+ for (i = 0; i < shape1[axis]; i++) {
+ index = flatten(left_index, [i], right_index);
+ in_out_t value = tensor_read<in_out_t>(input, shape1, index);
+ acc = acc || value;
+ }
+ out_index = flatten(left_index, [0], right_index);
+ tensor_write<in_out_t>(output, shape, out_index, acc);
+ }
+}
diff --git a/pseudocode/operators/REDUCE_MAX.tosac b/pseudocode/operators/REDUCE_MAX.tosac
new file mode 100644
index 0000000..57f291a
--- /dev/null
+++ b/pseudocode/operators/REDUCE_MAX.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+ERROR_IF(shape[axis] != 1);
+left_shape = (axis > 1) ? shape[0:axis-1] : [];
+right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ in_out_t acc = minimum<in_out_t>;
+ for (i = 0; i < shape1[axis]; i++) {
+ index = flatten(left_index, [i], right_index);
+ in_out_t value = tensor_read<in_out_t>(input, shape1, index);
+ acc = apply_max_s<in_out_t>(acc, value);
+ }
+ out_index = flatten(left_index, [0], right_index);
+ tensor_write<in_out_t>(output, shape, out_index, acc);
+ }
+}
diff --git a/pseudocode/operators/REDUCE_MIN.tosac b/pseudocode/operators/REDUCE_MIN.tosac
new file mode 100644
index 0000000..5eaa702
--- /dev/null
+++ b/pseudocode/operators/REDUCE_MIN.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+ERROR_IF(shape[axis] != 1);
+left_shape = (axis > 1) ? shape[0:axis-1] : [];
+right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ in_out_t acc = maximum<in_out_t>;
+ for (i = 0; i < shape1[axis]; i++) {
+ index = flatten(left_index, [i], right_index);
+ in_out_t value = tensor_read<in_out_t>(input, shape1, index);
+ acc = apply_min_s<in_out_t>(acc, value);
+ }
+ out_index = flatten(left_index, [0], right_index);
+ tensor_write<in_out_t>(output, shape, out_index, out);
+ }
+}
diff --git a/pseudocode/operators/REDUCE_PRODUCT.tosac b/pseudocode/operators/REDUCE_PRODUCT.tosac
new file mode 100644
index 0000000..3c867d7
--- /dev/null
+++ b/pseudocode/operators/REDUCE_PRODUCT.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+ERROR_IF(shape[axis] != 1);
+left_shape = (axis > 1) ? shape[0:axis-1] : [];
+right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ in_out_t acc = 1.0;
+ for (i = 0; i < shape1[axis]; i++) {
+ index = flatten(left_index, [i], right_index);
+ in_out_t value = tensor_read<in_out_t>(input, shape1, index);
+ acc = apply_mul_s<in_out_t>(acc, value);
+ }
+ out_index = flatten(left_index, [0], right_index);
+ tensor_write<in_out_t>(output, shape, out_index, acc);
+ }
+}
diff --git a/pseudocode/operators/REDUCE_SUM.tosac b/pseudocode/operators/REDUCE_SUM.tosac
new file mode 100644
index 0000000..2f22ada
--- /dev/null
+++ b/pseudocode/operators/REDUCE_SUM.tosac
@@ -0,0 +1,26 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape1));
+ERROR_IF(shape[axis] != 1);
+left_shape = (axis > 1) ? shape[0:axis-1] : [];
+right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : [];
+for_each(left_index in left_shape) {
+ for_each(right_index in right_shape) {
+ acc_t acc = 0;
+ for (i = 0; i < shape1[axis]; i++) {
+ index = flatten(left_index, [i], right_index);
+ acc_t value = tensor_read<in_out_t>(input, shape1, index);
+ acc = apply_add_s<acc_t>(acc, value);
+ }
+ out_index = flatten(left_index, [0], right_index);
+ in_out_t result = static_cast<in_out_t>(acc);
+ tensor_write<in_out_t>(output, shape, out_index, result);
+ }
+}
diff --git a/pseudocode/operators/RESCALE.tosac b/pseudocode/operators/RESCALE.tosac
new file mode 100644
index 0000000..86a939b
--- /dev/null
+++ b/pseudocode/operators/RESCALE.tosac
@@ -0,0 +1,58 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ // uint16 values can have zero_point 0 or 32768
+ // int8/uint8 can have zero point within their valid range
+ // No other types can have zero point != 0
+ ERROR_IF(in_t != i8_t &&
+ (in_t != i16_t || input_unsigned == False) && input_zp != 0);
+ ERROR_IF(out_t != i8_t &&
+ (out_t != i16_t || output_unsigned == False) && output_zp != 0);
+ ERROR_IF(in_t == i16_t && input_unsigned == True && input_zp != 0 && input_zp != 32768);
+ ERROR_IF(out_t == i16_t && output_unsigned == True && output_zp != 0 && output_zp != 32768);
+ ERROR_IF(scale32 && in_t == i48_t);
+ ERROR_IF(!scale32 && double_round);
+ ERROR_IF(in_t == i16_t && out_t == i32_t && input_unsigned);
+ ERROR_IF(in_t == i32_t && out_t == i16_t && output_unsigned);
+
+ in_t in_value = tensor_read<in_t>(input, shape, index);
+
+ int48_t value, extended_in_zp;
+ if (input_unsigned) {
+ value = zero_extend<int48_t>(in_value);
+ extended_in_zp = zero_extend<int48_t>(input_zp);
+ }
+ else {
+ value = sign_extend<int48_t>(value);
+ extended_in_zp = sign_extend<int48_t>(input_zp);
+ }
+
+ value = value - extended_in_zp;
+ int c = (per_channel) ? index[rank(input) - 1] : 0;
+ int32_t result = (scale32) ?
+ apply_scale_32(value, multiplier[c], shift[c], double_round) :
+ apply_scale_16(value, multiplier[c], shift[c]);
+
+ if (output_unsigned) {
+ int32_t extended_out_zp = zero_extend<int32_t>(output_zp);
+ result = apply_add_s<int32_t>(result, extended_out_zp);
+ out_t out = static_cast<out_t>(apply_clip<int32_t>(result,
+ minimum_u<out_t>,
+ maximum_u<out_t>));
+ }
+ else {
+ int32_t extended_out_zp = sign_extend<int32_t>(output_zp);
+ result = apply_add_s<int32_t>(result, extended_out_zp);
+ out_t out = static_cast<out_t>(apply_clip<int32_t>(result,
+ minimum_s<out_t>,
+ maximum_s<out_t>));
+ }
+ tensor_write<out_t>(output, shape, index, out);
+}
diff --git a/pseudocode/operators/RESHAPE.tosac b/pseudocode/operators/RESHAPE.tosac
new file mode 100644
index 0000000..41439c3
--- /dev/null
+++ b/pseudocode/operators/RESHAPE.tosac
@@ -0,0 +1,21 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(tensor_size(shape1) != tensor_size(shape));
+
+for_each(index in shape) {
+ // Calculate flattened index for the output location (index)
+ size_t offset = tensor_index_to_offset(shape, index);
+ // Now convert to the location in the input
+ dim_t tmp_index = tensor_offset_to_index(shape1, offset);
+
+ // Now read/write the value
+ in_out_t val = tensor_read<in_out_t>(input1, shape1, tmp_index);
+ tensor_write<in_out_t>(output, shape, index, val);
+}
diff --git a/pseudocode/operators/RESIZE.tosac b/pseudocode/operators/RESIZE.tosac
new file mode 100644
index 0000000..fd66530
--- /dev/null
+++ b/pseudocode/operators/RESIZE.tosac
@@ -0,0 +1,74 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+// Ensure the image size is supported by GPU APIs and that for integer
+// implementations, position * stride does not overflow int32_t.
+ERROR_IF(max(OH,OW,IH,IW) >= 16384);
+ERROR_IF(scale_y_n <= 0 || scale_y_d <= 0 || scale_x_n <= 0 || scale_x_d <= 0);
+// if in_t=int8_t ensure that an int32_t accumulator can be used
+ERROR_IF(scale_y_n > (1 << 11) || scale_x_n > (1 << 11));
+// set a consistent lower limit of 1/16 downscale to simplify implementations
+ERROR_IF(scale_y_d >= 16 * scale_y_n || scale_x_d >= 16 * scale_x_n);
+ERROR_IF(offset_y < -scale_y_n || offset_y >= 16 * scale_y_n);
+ERROR_IF(offset_x < -scale_x_n || offset_x >= 16 * scale_x_n);
+ERROR_IF(border_y < -16 * scale_y_n || border_y >= scale_y_n);
+ERROR_IF(border_x < -16 * scale_x_n || border_x >= scale_x_n);
+ERROR_IF(OH != idiv_check((IH - 1) * scale_y_n - offset_y + border_y, scale_y_d) + 1);
+ERROR_IF(OW != idiv_check((IW - 1) * scale_x_n - offset_x + border_x, scale_x_d) + 1);
+for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C) {
+ out_t acc;
+ resize_t dx, dy;
+ resize_t unit_x, unit_y;
+
+ unit_x = (is_floating_point(resize_t)) ? 1.0 : scale_x_n;
+ unit_y = (is_floating_point(resize_t)) ? 1.0 : scale_y_n;
+
+ int32_t y = oy * scale_y_d + offset_y;
+ int32_t x = ox * scale_x_d + offset_x;
+ int16_t iy = idiv_floor(y, scale_y_n);
+ int16_t ix = idiv_floor(x, scale_x_n);
+ int16_t ry = y - iy * scale_y_n; // (y % scale_y_n)
+ int16_t rx = x - ix * scale_x_n; // (x % scale_x_n)
+
+ if (is_floating_point(resize_t)) {
+ dy = static_cast<resize_t>(ry) / static_cast<resize_t>(scale_y_n);
+ dx = static_cast<resize_t>(rx) / static_cast<resize_t>(scale_x_n);
+ } else {
+ dy = ry;
+ dx = rx;
+ }
+ // Note that -1 <= iy < IH and -1 <= ix < IW
+ int16_t iy0 = apply_max_s(iy, 0);
+ int16_t iy1 = apply_min_s(iy + 1, IH - 1);
+ int16_t ix0 = apply_max_s(ix, 0);
+ int16_t ix1 = apply_min_s(ix + 1, IW - 1);
+ if (mode==BILINEAR) {
+ using in_s_t = make_signed(in_t); // Use signed calculations for i8/i16
+ in_s_t v00 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix0,c]));
+ in_s_t v01 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix1,c]));
+ in_s_t v10 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix0,c]));
+ in_s_t v11 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix1,c]));
+ acc = v00 * (unit_y - dy) * (unit_x - dx);
+ acc += v01 * (unit_y - dy) * dx;
+ acc += v10 * dy * (unit_x - dx);
+ acc += v11 * dy * dx;
+ tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc);
+ } else if (mode==NEAREST) {
+ int32_t iy, ix;
+ if (is_floating_point(resize_t)) {
+ iy = (dy >= 0.5) ? iy1 : iy0;
+ ix = (dx >= 0.5) ? ix1 : ix0;
+ } else {
+ iy = (2 * dy >= scale_y_n) ? iy1 : iy0;
+ ix = (2 * dx >= scale_x_n) ? ix1 : ix0;
+ }
+ in_t v = tensor_read<in_t>(input, [N,IH,IW,C], [n,iy,ix,c]);
+ tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], v);
+ }
+}
diff --git a/pseudocode/operators/REVERSE.tosac b/pseudocode/operators/REVERSE.tosac
new file mode 100644
index 0000000..63830d2
--- /dev/null
+++ b/pseudocode/operators/REVERSE.tosac
@@ -0,0 +1,16 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(axis < 0 || axis >= rank(shape));
+for_each(index in shape) {
+ dim_t tmp_index = index;
+ tmp_index[axis] = shape[axis] - 1 - index[axis];
+ in_out_t value = tensor_read<in_out_t>(input, shape, tmp_index);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/RFFT2D.tosac b/pseudocode/operators/RFFT2D.tosac
new file mode 100644
index 0000000..f664826
--- /dev/null
+++ b/pseudocode/operators/RFFT2D.tosac
@@ -0,0 +1,24 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(!power_of_two(H));
+ERROR_IF(!power_of_two(W));
+
+for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W/2 + 1) {
+ in_out_t sum_real = 0.0;
+ in_out_t sum_imag = 0.0;
+ for_each(0 <= iy < H, 0 <= ix < W) {
+ in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]);
+ float_t a = 2 * pi() * ((iy * oy) / H + (ix * ox) / W);
+ sum_real += val_real * cos(a);
+ sum_imag += -val_real * sin(a);
+ }
+ tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real);
+ tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag);
+}
diff --git a/pseudocode/operators/RSQRT.tosac b/pseudocode/operators/RSQRT.tosac
new file mode 100644
index 0000000..15d884a
--- /dev/null
+++ b/pseudocode/operators/RSQRT.tosac
@@ -0,0 +1,20 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index);
+ in_out_t result;
+ if (value1 < 0) {
+ result = NaN;
+ }
+ else {
+ result = 1.0 / apply_sqrt<in_out_t>(value1);
+ }
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/SCATTER.tosac b/pseudocode/operators/SCATTER.tosac
new file mode 100644
index 0000000..5027816
--- /dev/null
+++ b/pseudocode/operators/SCATTER.tosac
@@ -0,0 +1,31 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+
+// The following array is used to check compliance that an output position
+// is modified at most once.
+bool_t output_modified[N,K,C];
+
+// Copy the values_in tensor to the values_out tensor.
+// Values not written by the scatter operation are unchanged in the output.
+for_each(0 <= n < N, 0 <= k < K, 0 <= c < C) {
+ in_out_t value = tensor_read<in_out_t>(values_in, [N,K,C], [n,k,c]);
+ tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value);
+ output_modified[n,k,c]=false;
+}
+
+// Now perform the SCATTER operation, modifying the positions from the indices tensor
+for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) {
+ index_t k = tensor_read<index_t>(indices, [N,W], [n,w]);
+ REQUIRE(0 <= k && k < K);
+ REQUIRE(output_modified[n,k,c] == false);
+ in_out_t value = tensor_read<in_out_t>(input, [N,W,C], [n,w,c]);
+ tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value);
+ output_modified[n,k,c] = true;
+}
diff --git a/pseudocode/operators/SELECT.tosac b/pseudocode/operators/SELECT.tosac
new file mode 100644
index 0000000..fe8d760
--- /dev/null
+++ b/pseudocode/operators/SELECT.tosac
@@ -0,0 +1,25 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(shape != broadcast_shape(broadcast_shape(shape1, shape2), shape3));
+for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ dim_t index3 = apply_broadcast(shape, shape3, index);
+ bool_t value1 = tensor_read<bool_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t value3 = tensor_read<in_out_t>(input3, shape3, index3);
+ in_out_t result;
+ if (value1) {
+ result = value2;
+ } else {
+ result = value3;
+ }
+ tensor_write<in_out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/SIGMOID.tosac b/pseudocode/operators/SIGMOID.tosac
new file mode 100644
index 0000000..048523e
--- /dev/null
+++ b/pseudocode/operators/SIGMOID.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input, shape, index);
+ value = sigmoid<in_out_t>(value1);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/SLICE.tosac b/pseudocode/operators/SLICE.tosac
new file mode 100644
index 0000000..0ae0214
--- /dev/null
+++ b/pseudocode/operators/SLICE.tosac
@@ -0,0 +1,28 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(rank(shape1) != length(start) || rank(shape1) != length(size));
+ERROR_IF(rank(shape1) != rank(shape));
+// Sanity check the given coordinates, ensure start and end are
+// within tensor bounds
+for_each(index in rank(shape1)) {
+ ERROR_IF(start[index] < 0);
+ ERROR_IF(size[index] <= 0); //Output must be positive size
+ ERROR_IF(start[index] + size[index] > shape1[index]);
+ ERROR_IF(shape[index] != size[index]);
+}
+
+for_each(index in shape) {
+ dim_t tmp_index = index;
+ for(i = 0; i < rank(shape); i++) {
+ tmp_index[i] = index[i] + start[i];
+ }
+ in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/SUB.tosac b/pseudocode/operators/SUB.tosac
new file mode 100644
index 0000000..ac88b76
--- /dev/null
+++ b/pseudocode/operators/SUB.tosac
@@ -0,0 +1,26 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+if (in_out_t == shape_t) {
+ ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0);
+ shape_t value1 = tensor_read<shape_t>(input1, [], []);
+ shape_t value2 = tensor_read<shape_t>(input2, [], []);
+ shape_t result = apply_sub<shape_t>(value1, value2);
+ tensor_write<shape_t>(output, [], [], result);
+} else {
+ ERROR_IF(shape != broadcast_shape(shape1, shape2));
+ for_each(index in shape) {
+ dim_t index1 = apply_broadcast(shape, shape1, index);
+ dim_t index2 = apply_broadcast(shape, shape2, index);
+ in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1);
+ in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2);
+ in_out_t result = apply_sub_s<in_out_t>(value1, value2);
+ tensor_write<in_out_t>(output, shape, index, result);
+ }
+}
diff --git a/pseudocode/operators/TABLE.tosac b/pseudocode/operators/TABLE.tosac
new file mode 100644
index 0000000..602d49b
--- /dev/null
+++ b/pseudocode/operators/TABLE.tosac
@@ -0,0 +1,21 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+REQUIRE(length(table) == TABLE_SIZE);
+for_each(index in shape) {
+ in_t value = tensor_read<in_t>(input, shape, index);
+ out_t result;
+ if (in_t == i8_t) {
+ // value is a signed int, convert to a 0 based index
+ result = table[static_cast<int16_t>(value) + 128];
+ } else {
+ result = apply_lookup_s(static_cast<int16_t>(table), static_cast<int16_t>(value));
+ }
+ tensor_write<out_t>(output, shape, index, result);
+}
diff --git a/pseudocode/operators/TANH.tosac b/pseudocode/operators/TANH.tosac
new file mode 100644
index 0000000..1b5edba
--- /dev/null
+++ b/pseudocode/operators/TANH.tosac
@@ -0,0 +1,14 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+for_each(index in shape) {
+ in_out_t value1 = tensor_read<in_out_t>(input, shape, index);
+ value = tanh<in_out_t>(value1);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/TILE.tosac b/pseudocode/operators/TILE.tosac
new file mode 100644
index 0000000..d053b8f
--- /dev/null
+++ b/pseudocode/operators/TILE.tosac
@@ -0,0 +1,20 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(rank(shape1) != rank(shape));
+
+for_each(index in shape) {
+ dim_t tmp_index = index;
+ for(i = 0; i < rank(shape); i++) {
+ ERROR_IF(shape1[i] * multiples[i] != shape[i]);
+ tmp_index[i] = index[i] % shape1[i];
+ }
+ in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/TRANSPOSE.tosac b/pseudocode/operators/TRANSPOSE.tosac
new file mode 100644
index 0000000..a8f0815
--- /dev/null
+++ b/pseudocode/operators/TRANSPOSE.tosac
@@ -0,0 +1,35 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(rank(shape1) != rank(shape));
+ERROR_IF(tensor_size(shape1) != tensor_size(shape));
+
+for_each(index in perms) {
+ // Ensure each perms value is a valid value
+ ERROR_IF(index >= rank(shape1));
+ ERROR_IF(index < 0);
+ // Ensure ranks aren't repeated
+ ERROR_IF(indexes_used[index] == true);
+ indexes_used[index] = true;
+}
+
+// Ensure that the output shapes have the properly
+// permuted shapes
+for(i = 0; i < rank(shape); i++) {
+ ERROR_IF(shape1[perms[i]] != shape[i])
+}
+
+for_each(index in shape) {
+ dim_t tmp_index = index;
+ for(i = 0; i < rank(shape); i++) {
+ tmp_index[perms[i]] = index[i]
+ }
+ in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index);
+ tensor_write<in_out_t>(output, shape, index, value);
+}
diff --git a/pseudocode/operators/TRANSPOSE_CONV2D.tosac b/pseudocode/operators/TRANSPOSE_CONV2D.tosac
new file mode 100644
index 0000000..3aa0e23
--- /dev/null
+++ b/pseudocode/operators/TRANSPOSE_CONV2D.tosac
@@ -0,0 +1,35 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only allowed for int8_t
+ERROR_IF(weight_t != i8_t && weight_zp != 0);
+ERROR_IF(out_pad_top <= -KH || out_pad_bottom <= -KH);
+ERROR_IF(out_pad_left <= -KW || out_pad_right <= -KW);
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(OH != (IH - 1) * stride_y + out_pad_top + out_pad_bottom + KH);
+ERROR_IF(OW != (IW - 1) * stride_x + out_pad_left + out_pad_right + KW);
+ERROR_IF(BC != OC && BC != 1);
+
+for_each(index in [N, OH, OW, OC]) {
+ tensor_write<out_t>(output, [N,OH,OW,OC], index, bias[(BC == 1) ? 0 : index[3]])
+}
+for_each(0 <= n < N, 0 <= iy < IH, 0 <= ix < IW, 0 <= oc < OC,
+ 0 <= ic < IC, 0 <= ky < KH, 0 <= kx < KW) {
+ index_t oy = iy * stride_y + out_pad_top + ky;
+ index_t ox = ix * stride_x + out_pad_left + kx;
+ if (oy >= 0 && oy < OH && ox >= 0 && ox < OW) {
+ out_t acc = static_cast<out_t>(tensor_read<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc]));
+ out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IH,IW,IC], [n,iy,ix,ic]));
+ out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,KH,KW,IC], [oc,ky,kx,ic]));
+ value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp));
+ weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp));
+ acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight));
+ tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc);
+ }
+}
diff --git a/pseudocode/operators/VARIABLE.tosac b/pseudocode/operators/VARIABLE.tosac
new file mode 100644
index 0000000..4d2fce7
--- /dev/null
+++ b/pseudocode/operators/VARIABLE.tosac
@@ -0,0 +1,33 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+
+tensor_t var_tensor = variable_tensor_lookup(uid);
+
+// Invocation for the first time
+if (var_tensor == NULL) {
+ // Allocate the persistent mutable memory for the variable tensor
+ tensor_t var_tensor = variable_tensor_allocate<var_t>(var_shape, uid);
+
+ if (initial_value != NULL) {
+ REQUIRE(var_t == in_t);
+ REQUIRE(var_shape == shape);
+ for_each (index in shape) {
+ // Copy data from initial_value to var_tensor
+ in_t value = tensor_read<in_t>(initial_value, shape, index);
+ tensor_write<in_t>(var_tensor.data, var_shape, index, value);
+ }
+ var_tensor.is_written = true;
+ }
+} else { // Variable tensor has already been declared
+ // It's invalid to declare the second variable with the same uid in a single graph execution,
+ REQUIRE(!var_tensor.seen);
+}
+
+var_tensor.seen = true;
diff --git a/pseudocode/operators/VARIABLE_READ.tosac b/pseudocode/operators/VARIABLE_READ.tosac
new file mode 100644
index 0000000..c2e0e38
--- /dev/null
+++ b/pseudocode/operators/VARIABLE_READ.tosac
@@ -0,0 +1,26 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+
+tensor_t variable_tensor = variable_tensor_lookup(uid);
+// Check this variable tensor has been decalred
+REQUIRE(variable_tensor != NULL);
+// Check this variable tensor has been written
+REQUIRE(variable_tensor.is_written);
+// Output tensor's shape and variable_tensor's shape have to match
+REQUIRE(variable_tensor.shape == shape);
+// Output tensor's shape and variable_tensor's type have to match
+REQUIRE(variable_tensor.type == out_t);
+
+for_each (index in shape) {
+ // Read data from pseudo-buffer resource to the output
+ out_t value = tensor_read<tensor_t>(variable_tensor.data, variable_tensor.shape, index);
+ tensor_write<out_t>(input1, shape, index, value);
+}
+
diff --git a/pseudocode/operators/VARIABLE_WRITE.tosac b/pseudocode/operators/VARIABLE_WRITE.tosac
new file mode 100644
index 0000000..83c5525
--- /dev/null
+++ b/pseudocode/operators/VARIABLE_WRITE.tosac
@@ -0,0 +1,29 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+
+tensor_t. variable_tensor = variable_tensor_lookup(uid);
+// Check this variable tensor has been declared
+REQUIRE(variable_tensor);
+// The tensor has to be seen before to be written to
+// The seen variable is cleared before each graph execution and set in declaration
+REQUIRE(variable_tensor.seen);
+// Input tensor's shape and variable_tensor's shape have to match
+REQUIRE(variable_tensor.shape == shape);
+// Input tensor's shape and variable_tensor's type have to match
+REQUIRE(variable_tensor.type == in_t);
+
+for_each (index in shape) {
+ // Write data from the input to the pseudo-buffer resource
+ in_t value = tensor_read<in_t>(input1, shape, index);
+ tensor_write<tensor_t>(variable_tensor.data, variable_tensor.shape, index, value);
+}
+
+variable_tensor.is_written = true;
+
diff --git a/pseudocode/operators/WHILE_LOOP.tosac b/pseudocode/operators/WHILE_LOOP.tosac
new file mode 100644
index 0000000..dae96d5
--- /dev/null
+++ b/pseudocode/operators/WHILE_LOOP.tosac
@@ -0,0 +1,33 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+ERROR_IF(tosa_nesting_depth >= MAX_NESTING);
+ERROR_IF(tensor_list_shape(input_list) != tosa_list_shape(output_list));
+ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(cond_graph));
+ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(body_graph));
+ERROR_IF(tensor_list_shape(input_list) != tosa_output_shape(body_graph));
+// Condition graph output must be a single element tensor with a single bool value
+ERROR_IF(tensor_size(tosa_output_shape(cond_graph)) != 1);
+ERROR_IF(tosa_output_type(cond_graph) != bool_t);
+
+// The iteration number 'i' is included to give unique names to variables
+// in each iteration of the loop and is not required by implementations
+int32_t i=0; // iteration number
+tensor_list_t list[]; // array of tensor lists indexed by iteration
+bool_t *condition[]; // array of condition tensors indexed by iteration
+list[i] = input_list; // copy input data as list[0]
+tosa_nesting_depth++;
+tosa_execute_graph(cond_graph, list[i], [ condition[i] ]); // initial condition
+while (condition[i][0]) {
+ tosa_execute_graph(body_graph, list[i], list[i+1]);
+ i = i+1;
+ tosa_execute_graph(cond_graph, list[i], [ condition[i] ]);
+}
+tosa_nesting_depth--;
+output_list = list[i];
diff --git a/pseudocode/operators/tables/ERF.tosac b/pseudocode/operators/tables/ERF.tosac
new file mode 100644
index 0000000..0ff29ff
--- /dev/null
+++ b/pseudocode/operators/tables/ERF.tosac
@@ -0,0 +1,16 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+int16_t erf_reference(int16_t x) { // input x range is -256 to + 256 inclusive
+ F64 v = (double)x / (double)64;
+ v = erf(v);
+ return round_to_nearest_int(32768.0 * v);
+}
+
+generate_lookup_table(&erf_table, &erf_reference);
diff --git a/pseudocode/operators/tables/SIGMOID.tosac b/pseudocode/operators/tables/SIGMOID.tosac
new file mode 100644
index 0000000..dad1e1d
--- /dev/null
+++ b/pseudocode/operators/tables/SIGMOID.tosac
@@ -0,0 +1,16 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+int16_t sigmoid_reference(int16_t x) { // input x range is -256 to + 256 inclusive
+ fp64_t v = (fp64_t)x / (fp64_t)16;
+ v = 1.0/(1.0 + exp(-v));
+ return round_to_nearest_int(32768.0 * v);
+}
+
+generate_lookup_table(&sigmoid_table, &sigmoid_reference);
diff --git a/pseudocode/operators/tables/TANH.tosac b/pseudocode/operators/tables/TANH.tosac
new file mode 100644
index 0000000..b45f121
--- /dev/null
+++ b/pseudocode/operators/tables/TANH.tosac
@@ -0,0 +1,17 @@
+//
+// This confidential and proprietary software may be used only as
+// authorised by a licensing agreement from ARM Limited
+// (C) COPYRIGHT 2020-2024 ARM Limited
+// ALL RIGHTS RESERVED
+// The entire notice above must be reproduced on all authorised
+// copies and copies may only be made to the extent permitted
+// by a licensing agreement from ARM Limited.
+
+int16_t tanh_reference(int16_t x) { // input x range is -256 to +256 inclusive
+ fp64_t v = (fp64_t)x/(fp64_t)32;
+ v = exp(-2.0*v);
+ v = (1.0-v)/(1.0+v);
+ return round_to_nearest_int(32768.0 * v);
+}
+
+generate_lookup_table(&tanh_table, &tanh_reference);