MLBEDSW-5162 MLCE: Vela [3.1.0] falling to run with yolov4_int8.tflite

* fix indices for tflite mapping of EXP operator
* fix indices for tflite mapping of Transpose operator
* ensure read offset after slice is aligned to 16 bytes for NHCWB16 or force linear format
* add unit test to ensure mapping of indices is consistent across TFLite, TOSA and NNG

Signed-off-by: James Ward <james.ward@arm.com>
Change-Id: I17b6e44bc06853325d5eea62a558418ee1ebefe8
diff --git a/ethosu/vela/graph_optimiser_util.py b/ethosu/vela/graph_optimiser_util.py
index 73fbf6c..3e15f12 100644
--- a/ethosu/vela/graph_optimiser_util.py
+++ b/ethosu/vela/graph_optimiser_util.py
@@ -49,15 +49,18 @@
 
 def _avoid_nhcwb16_for_split(tens):
     # If read offset is not a multiple of 16 in the C-dimension, NHCWB16 need to be avoided in the input
+
+    # Return True if NHCWB16 needs to be avoided
+    def offset_not_aligned(read_offset):
+        return read_offset is not None and (read_offset.depth % 16) != 0
+
     for cons_op in tens.consumer_list:
         if cons_op.ifm == tens:
-            read_offset = cons_op.read_offsets[0]
-        elif cons_op.type.is_binary_elementwise_op() and cons_op.ifm2 == tens:
-            read_offset = cons_op.read_offsets[1]
-        else:
-            assert False
-        if read_offset is not None and (read_offset[-1] % 16) != 0:
-            return True
+            if offset_not_aligned(cons_op.read_offsets[0]):
+                return True
+        if cons_op.ifm2 is not None and cons_op.ifm2 == tens:
+            if offset_not_aligned(cons_op.read_offsets[1]):
+                return True
     return False
 
 
diff --git a/ethosu/vela/operation.py b/ethosu/vela/operation.py
index 1e733d5..1707d31 100644
--- a/ethosu/vela/operation.py
+++ b/ethosu/vela/operation.py
@@ -285,7 +285,7 @@
     Tanh = OperatorInfo(indices=NNG_IFM_INDICES)
     Tile = OperatorInfo()
     TopKV2 = OperatorInfo()
-    Transpose = OperatorInfo(indices=NNG_IFM_INDICES)
+    Transpose = OperatorInfo(indices=NNG_IFM_IFM2_INDICES)
     UnidirectionalSequenceLstm = OperatorInfo(block_type=NpuBlockType.VectorProduct, indices=NNG_IFM_WEIGHTS_INDICES)
     UnidirectionalSequenceRnn = OperatorInfo(block_type=NpuBlockType.VectorProduct, indices=NNG_IFM_WEIGHTS_INDICES)
     Unique = OperatorInfo()
diff --git a/ethosu/vela/test/test_nng_mapping.py b/ethosu/vela/test/test_nng_mapping.py
new file mode 100644
index 0000000..08d77fe
--- /dev/null
+++ b/ethosu/vela/test/test_nng_mapping.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2021 Arm Limited or its affiliates. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Unit tests for the mapping of TFLite or TOSA to NNG
+import pytest
+
+from ethosu.vela.tflite_mapping import builtin_operator_map
+from ethosu.vela.tosa_mapping import tosa_operator_map
+
+
+class TestNNGMapping:
+    """Ensure the mappings from TFLite to NNG are consistent."""
+
+    @pytest.mark.parametrize(
+        "operator_map",
+        ((builtin_operator_map), (tosa_operator_map)),
+        ids=("test_tflite_indices_match_nng", "test_tosa_indices_match_nng"),
+    )
+    def test_op_indices_match(self, operator_map):
+        """Ensure TFLite/TOSA indices and NNG indices are consistent for each operator."""
+        for map_op in operator_map.values():
+            op_type = map_op[0]
+            map_op_indices = map_op[-1]  # TFLite/TOSA indices in last element of tuple
+
+            nng_indices = op_type.info.indices
+
+            for idx in range(3):
+                assert len(map_op_indices[idx]) == len(nng_indices[idx])
diff --git a/ethosu/vela/tflite_mapping.py b/ethosu/vela/tflite_mapping.py
index d52e074..396ed5e 100644
--- a/ethosu/vela/tflite_mapping.py
+++ b/ethosu/vela/tflite_mapping.py
@@ -650,7 +650,7 @@
         OptionsSerializer("SpaceToBatchNDOptions"),
         TFLITE_NO_INDICES,
     ),
-    BuiltinOperator.TRANSPOSE: (Op.Transpose, OptionsSerializer("TransposeOptions"), TFLITE_NO_INDICES),
+    BuiltinOperator.TRANSPOSE: (Op.Transpose, OptionsSerializer("TransposeOptions"), TFLITE_IFM_IFM2_INDICES),
     BuiltinOperator.MEAN: (Op.Mean, OptionsSerializer("ReducerOptions", ("keep_dims",)), TFLITE_IFM_INDICES),
     BuiltinOperator.SUB: (
         Op.Sub,
@@ -685,7 +685,7 @@
         ),
         TFLITE_IFM_WEIGHTS_INDICES,
     ),
-    BuiltinOperator.EXP: (Op.Exp, OptionsSerializer("ExpOptions"), TFLITE_IFM_INDICES),
+    BuiltinOperator.EXP: (Op.Exp, OptionsSerializer("ExpOptions"), TFLITE_NO_INDICES),
     BuiltinOperator.TOPK_V2: (Op.TopKV2, OptionsSerializer("TopKV2Options"), TFLITE_NO_INDICES),
     BuiltinOperator.SPLIT: (Op.Split, OptionsSerializer("SplitOptions", ("num_splits",)), TFLITE_SPLIT_IFM_INDICES),
     BuiltinOperator.LOG_SOFTMAX: (Op.LogSoftmax, OptionsSerializer("LogSoftmaxOptions"), TFLITE_NO_INDICES),