IVGCVSW-7133 Add TosaMappings backbone structure with support for Addition TosaMappings operator.

Signed-off-by: Cathal Corbett <cathal.corbett@arm.com>
Change-Id: Ibea0cf625b3af4ab38e8b985f7a129c983ca9659
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 30e526c..c63d8fc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -512,6 +512,37 @@
     target_link_libraries(armnn PUBLIC ${ARMCOMPUTE_LIBRARIES})
 endif()
 
+# Flatbuffers is not included in armnn if the armnnSerializer is not present
+# Required to target link both flatbuffers and tosa_serialization_lib to armnn
+if (ARMNNTOSAREF)
+    if (FLATBUFFERS_LIBRARY)
+        target_link_libraries(armnn PUBLIC ${FLATBUFFERS_LIBRARY})
+    else()
+        # Use PATH_SUFFIXES to help find separate libs for debug/release on Windows builds
+        find_library(FLATBUFFERS_LIBRARY_DEBUG NAMES flatbuffers
+                HINTS ${FLATBUFFERS_BUILD_DIR}
+                PATH_SUFFIXES "Debug")
+        find_library(FLATBUFFERS_LIBRARY_RELEASE NAMES flatbuffers
+                HINTS ${FLATBUFFERS_BUILD_DIR}
+                PATH_SUFFIXES "Release")
+        target_link_libraries(armnn PUBLIC
+                debug ${FLATBUFFERS_LIBRARY_DEBUG}
+                optimized ${FLATBUFFERS_LIBRARY_RELEASE})
+    endif()
+
+    if (TOSA_SERIALIZATION_LIB)
+        target_link_libraries(armnn PUBLIC -Wl,--whole-archive ${TOSA_SERIALIZATION_LIB} -Wl,--no-whole-archive)
+    else()
+        find_library(TOSA_SERIALIZATION_LIB
+            NAMES libtosa_serialization_lib.a tosa_serialization_lib
+            HINTS ${TOSA_SERIALIZATION_LIB_ROOT}/lib /usr/local/lib /usr/lib)
+
+        message(STATUS "TOSA serialization library set to ${TOSA_SERIALIZATION_LIB}")
+
+        target_link_libraries(armnn PUBLIC -Wl,--whole-archive ${TOSA_SERIALIZATION_LIB} -Wl,--no-whole-archive)
+    endif()
+endif()
+
 if(PROFILING_BACKEND_STREAMLINE AND (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL Android)))
     target_link_libraries(armnn PUBLIC ${CMAKE_THREAD_LIBS_INIT})
 endif()
diff --git a/cmake/GlobalConfig.cmake b/cmake/GlobalConfig.cmake
index d5cdca2..9137fa3 100644
--- a/cmake/GlobalConfig.cmake
+++ b/cmake/GlobalConfig.cmake
@@ -1,5 +1,5 @@
 #
-# Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+# Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
 # Copyright 2020 NXP
 # SPDX-License-Identifier: MIT
 #
@@ -11,6 +11,7 @@
 option(ARMCOMPUTENEON "Build with ARM Compute NEON support" OFF)
 option(ARMCOMPUTECL "Build with ARM Compute OpenCL support" OFF)
 option(ARMNNREF "Build with ArmNN reference support" ON)
+option(ARMNNTOSAREF "Build with Tosa reference support" OFF)
 option(PROFILING_BACKEND_STREAMLINE "Forward the armNN profiling events to DS-5/Streamline as annotations" OFF)
 # options used for heap profiling and leak checking
 option(HEAP_PROFILING "Build with heap profiling enabled" OFF)
@@ -23,6 +24,7 @@
 option(FLATC_DIR "Path to Flatbuffers compiler" OFF)
 option(TF_LITE_GENERATED_PATH "Tensorflow lite generated C++ schema location" OFF)
 option(FLATBUFFERS_ROOT "Location where the flatbuffers 'include' and 'lib' folders to be found" Off)
+option(TOSA_SERIALIZATION_LIB_ROOT "Location where the TOSA serialization library 'include' and 'lib' folders can be found" OFF)
 option(DYNAMIC_BACKEND_PATHS "Colon seperated list of paths where to load the dynamic backends from" "")
 option(SAMPLE_DYNAMIC_BACKEND "Include the sample dynamic backend and its tests in the build" OFF)
 option(BUILD_GATORD_MOCK "Build the Gatord simulator for external profiling testing." ON)
@@ -183,8 +185,8 @@
 if(BUILD_ARMNN_TFLITE_DELEGATE)
     add_definitions(-DARMNN_TFLITE_DELEGATE)
 endif()
-# Flatbuffers support for TF Lite and Armnn Serializer
-if(BUILD_TF_LITE_PARSER OR BUILD_ARMNN_SERIALIZER)
+# Flatbuffers support for TF Lite, Armnn Serializer or the TOSA backend.
+if(BUILD_TF_LITE_PARSER OR BUILD_ARMNN_SERIALIZER OR ARMNNTOSAREF)
     # verify we have a valid flatbuffers include path
     find_path(FLATBUFFERS_INCLUDE_PATH flatbuffers/flatbuffers.h
               HINTS ${FLATBUFFERS_ROOT}/include /usr/local/include /usr/include)
@@ -334,6 +336,21 @@
     add_definitions(-DARMNNREF_ENABLED)
 endif()
 
+# ArmNN TOSA reference backend
+if(ARMNNTOSAREF)
+    # Locate the includes for the TOSA serialization library.
+    message(STATUS "TOSA serialization library root set to ${TOSA_SERIALIZATION_LIB_ROOT}")
+
+    find_path(TOSA_SERIALIZATION_LIB_INCLUDE tosa_serialization_handler.h HINTS
+            ${TOSA_SERIALIZATION_LIB_ROOT}/include)
+    message(STATUS "TOSA serialization library include directory located at: ${TOSA_SERIALIZATION_LIB_INCLUDE}")
+
+    find_library(TOSA_SERIALIZATION_LIB
+            NAMES tosa_serialization_lib.a tosa_serialization_lib
+            HINTS ${TOSA_SERIALIZATION_LIB_ROOT}/lib /usr/local/lib /usr/lib)
+    message(STATUS "TOSA serialization library set to ${TOSA_SERIALIZATION_LIB}")
+endif()
+
 # This is the root for the dynamic backend tests to search for dynamic
 # backends. By default it will be the project build directory.
 add_definitions(-DDYNAMIC_BACKEND_BUILD_DIR="${PROJECT_BINARY_DIR}")
diff --git a/src/backends/tosaCommon/CMakeLists.txt b/src/backends/tosaCommon/CMakeLists.txt
new file mode 100644
index 0000000..9653859
--- /dev/null
+++ b/src/backends/tosaCommon/CMakeLists.txt
@@ -0,0 +1,25 @@
+#
+# Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+include_directories(SYSTEM ${FLATBUFFERS_INCLUDE_PATH})
+include_directories(SYSTEM ${TOSA_SERIALIZATION_LIB_INCLUDE})
+
+list(APPEND armnnTosaBackend_sources
+        TosaMappings.hpp
+    )
+
+add_subdirectory(operatorMappings)
+
+if(BUILD_UNIT_TESTS)
+    add_subdirectory(test)
+endif()
+
+add_library(armnnTosaBackend OBJECT ${armnnTosaBackend_sources})
+target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn)
+target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils)
+target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/backends)
+target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/profiling)
+target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/profiling/common/include)
+target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/profiling/client/include)
diff --git a/src/backends/tosaCommon/TosaMappings.hpp b/src/backends/tosaCommon/TosaMappings.hpp
new file mode 100644
index 0000000..5728ff3
--- /dev/null
+++ b/src/backends/tosaCommon/TosaMappings.hpp
@@ -0,0 +1,81 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <Layer.hpp>
+
+#include <tosa_serialization_handler.h>
+#include "operatorMappings/AdditionOperator.hpp"
+
+using namespace armnn;
+using namespace tosa;
+
+// From the input armnn::Layer, set the corresponding data field in the
+// tosa::TosaSerializationTensor where constant tensor data exists in the armnn::Layer.
+void SetBasicBlockConstantTensorData(Layer* layer, TosaSerializationBasicBlock* /*basicBlock*/)
+{
+    switch (layer->GetType())
+    {
+        case LayerType::Convolution2d:
+        {
+            // ToDo: using Convolution2d as an example as it has constant tensors for weights and bias.
+            // ToDo: manually set TosaOperator data of basicBlock where constant tensors exist.
+        }
+        default:
+            // If no switch statement for layer, no constant tensors exist in that layer, return
+            return;
+    }
+}
+
+// Populates a tosa::TosaSerializationBasicBlock from constructing
+// tosa::TosaSerializationOperator(s) and tosa::TosaSerializationTensor(s)
+// based on the input armnn::LayerType and associated armnn::TensorInfos and armnn::Descriptor.
+//
+// If an armnn::LayerType does not have a tosa mapping or the mapping is not implemented in ArmNN,
+// an empty tosa::TosaSerializationBasicBlock() is returned with operator tosa::Op_UNKNOWN.
+TosaSerializationBasicBlock* GetTosaMapping(const LayerType type,
+                                            const std::vector<const TensorInfo*>& inputs,
+                                            const std::vector<const TensorInfo*>& outputs,
+                                            const BaseDescriptor& /*descriptor*/)
+{
+    switch (type)
+    {
+        case LayerType::Addition:
+        {
+            return ConvertAdditionToTosaOperator(inputs, outputs);
+        }
+        default:
+        {
+            // empty basic block when no tosa mapping implemented/exists
+            TosaSerializationOperator* op = new TosaSerializationOperator(Op_UNKNOWN, Attribute_NONE, nullptr, {}, {});
+            return new TosaSerializationBasicBlock("", {op}, {}, {}, {});
+        }
+    }
+}
+
+// Function called in armnn::OptimizeSubgraphView() when access to armnn::Layer is available
+// and there is an option to set tosa basic block data from constant layer tenors available from the input layer.
+TosaSerializationBasicBlock* GetTosaMappingFromLayer(Layer* layer)
+{
+    std::vector<const TensorInfo*> inputs;
+    for (auto inputSlot : layer->GetInputSlots())
+    {
+        inputs.push_back(&inputSlot.GetConnection()->GetTensorInfo());
+    }
+
+    std::vector<const TensorInfo*> outputs;
+    for (auto& outputSlot : layer->GetOutputSlots())
+    {
+        outputs.push_back(&outputSlot.GetTensorInfo());
+    }
+
+    TosaSerializationBasicBlock* basicBlock = GetTosaMapping(layer->GetType(),
+                                                             inputs,
+                                                             outputs,
+                                                             layer->GetParameters());
+    SetBasicBlockConstantTensorData(layer, basicBlock);
+    return basicBlock;
+}
diff --git a/src/backends/tosaCommon/common.cmake b/src/backends/tosaCommon/common.cmake
new file mode 100644
index 0000000..f15c063
--- /dev/null
+++ b/src/backends/tosaCommon/common.cmake
@@ -0,0 +1,11 @@
+#
+# Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+if(ARMNNTOSAREF)
+    add_subdirectory(${PROJECT_SOURCE_DIR}/src/backends/tosaCommon)
+    list(APPEND armnnLibraries armnnTosaBackend)
+    list(APPEND armnnLibraries armnnTosaBackendOperators)
+    list(APPEND armnnUnitTestLibraries armnnTosaBackendUnitTests)
+endif()
diff --git a/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp
new file mode 100644
index 0000000..98c01e2
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp
@@ -0,0 +1,52 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <Layer.hpp>
+
+#include <tosa_serialization_handler.h>
+#include "TosaOperatorUtils.hpp"
+
+using namespace armnn;
+using namespace tosa;
+
+TosaSerializationBasicBlock* ConvertAdditionToTosaOperator(const std::vector<const TensorInfo*>& inputs,
+                                                          const std::vector<const TensorInfo*>& outputs)
+{
+    // A helper function with static global variables ensures uniqueness
+    // for dynamically generating input, output and block names
+    std::string input0Name = std::string("Op_ADD_input0_")  + GetUniqueTosaMappingID();
+    std::string input1Name = std::string("Op_ADD_input1_")  + GetUniqueTosaMappingID();
+    std::string outputName = std::string("Op_ADD_output0_") + GetUniqueTosaMappingID();
+    std::string blockName  = std::string("Op_ADD_block_")   + GetUniqueTosaMappingID();
+
+    TosaSerializationOperator* op = new TosaSerializationOperator(Op_ADD,
+                                                                  Attribute_NONE,
+                                                                  nullptr,
+                                                                  {input0Name, input1Name},
+                                                                  {outputName});
+
+    std::vector<int32_t> inputShape0 = GetTosaTensorShape(inputs[0]->GetShape());
+    DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType());
+
+    std::vector<int32_t> inputShape1 = GetTosaTensorShape(inputs[1]->GetShape());
+    DType inputDType1 = ArmNNToDType(inputs[1]->GetDataType());
+
+    std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape());
+    DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
+
+    TosaSerializationTensor* inputTensor0  = new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {});
+    TosaSerializationTensor* inputTensor1  = new TosaSerializationTensor(input1Name, inputShape1, inputDType1, {});
+    TosaSerializationTensor* outputTensor0 = new TosaSerializationTensor(outputName, outputShape0, outputDType0, {});
+
+    // operatorInputNames/operatorOutputNames ends up being the same as
+    // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
+    return new TosaSerializationBasicBlock(blockName, // name
+                                           {op}, // operators
+                                           {inputTensor0, inputTensor1, outputTensor0}, // tensors
+                                           {input0Name, input1Name}, // inputs
+                                           {outputName}); // outputs
+}
diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
new file mode 100644
index 0000000..3965a6a
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
@@ -0,0 +1,14 @@
+#
+# Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+list(APPEND armnnTosaBackendOperators_sources
+        AdditionOperator.hpp
+        TosaOperatorUtils.hpp
+    )
+
+add_library(armnnTosaBackendOperators OBJECT ${armnnTosaBackendOperators_sources})
+target_include_directories(armnnTosaBackendOperators PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn)
+target_include_directories(armnnTosaBackendOperators PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils)
+target_include_directories(armnnTosaBackendOperators PRIVATE ${PROJECT_SOURCE_DIR}/src/backends)
diff --git a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
new file mode 100644
index 0000000..b887721
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
@@ -0,0 +1,60 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/Tensor.hpp>
+#include <armnn/Types.hpp>
+
+#include <tosa_generated.h>
+
+using namespace armnn;
+using namespace tosa;
+
+// Function to return Tosa datatype from input ArmNN datatype.
+DType ArmNNToDType(const DataType& type)
+{
+    switch (type)
+    {
+        case DataType::Float16:
+        case DataType::Float32:
+        case DataType::BFloat16:
+            return DType_FLOAT;
+        case DataType::QAsymmU8:
+            return DType_UINT8;
+        case DataType::QSymmS8:
+        case DataType::QAsymmS8:
+            return DType_INT8;
+        case DataType::QSymmS16:
+            return DType_INT16;
+        case DataType::Signed32:
+            return DType_INT32;
+        case DataType::Signed64:
+            // No signed 64, only DType_INT48.
+            return DType_UNKNOWN;
+        case DataType::Boolean:
+            return DType_BOOL;
+        default:
+            return DType_UNKNOWN;
+    }
+}
+
+// Function to return Tosa tensor shape from input ArmNN tensor shape.
+std::vector<int32_t> GetTosaTensorShape(const TensorShape& shape)
+{
+    std::vector<int32_t> returnShape;
+    for (u_int32_t i = 0; i < shape.GetNumDimensions(); i++)
+    {
+        returnShape.push_back(static_cast<int32_t>(shape[i]));
+    }
+    return returnShape;
+}
+
+// Function to return unique int as a string to ensure uniqueness between all input, output and block names.
+static int uniqueTosaMappingID = 0;
+std::string GetUniqueTosaMappingID()
+{
+    return std::to_string(++uniqueTosaMappingID);
+}
diff --git a/src/backends/tosaCommon/test/CMakeLists.txt b/src/backends/tosaCommon/test/CMakeLists.txt
new file mode 100644
index 0000000..56cc66e
--- /dev/null
+++ b/src/backends/tosaCommon/test/CMakeLists.txt
@@ -0,0 +1,14 @@
+#
+# Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+list(APPEND armnnTosaBackendUnitTests_sources
+        TosaOperatorMappingTests.cpp
+    )
+
+add_library(armnnTosaBackendUnitTests OBJECT ${armnnTosaBackendUnitTests_sources})
+target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn)
+target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils)
+target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnTestUtils)
+target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/backends)
diff --git a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
new file mode 100644
index 0000000..2f5a56d
--- /dev/null
+++ b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
@@ -0,0 +1,124 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <Layer.hpp>
+
+#include <tosaCommon/TosaMappings.hpp>
+
+#include <doctest/doctest.h>
+
+using namespace armnn;
+using namespace tosa;
+
+void AssertTosaOneToOneMappingBasicBlock(TosaSerializationBasicBlock* basicBlock,
+                                         std::vector<int32_t> shape,
+                                         uint32_t numInputs,
+                                         uint32_t numOutputs,
+                                         Op tosaOp,
+                                         std::string operatorString,
+                                         DType dataType = DType_FLOAT)
+{
+    std::string blockStr = operatorString + "_block_";
+    ARMNN_ASSERT(basicBlock->GetName().find(blockStr)  != std::string::npos);
+    ARMNN_ASSERT(basicBlock->GetInputs().size() == numInputs);
+    ARMNN_ASSERT(basicBlock->GetOutputs().size() == numOutputs);
+    ARMNN_ASSERT(basicBlock->GetOperators().size() == 1);
+    ARMNN_ASSERT(basicBlock->GetTensors().size() == (numInputs + numOutputs));
+
+    TosaSerializationOperator* op = basicBlock->GetOperators().at(0);
+    ARMNN_ASSERT(op->GetInputTensorNames().size() == numInputs);
+    ARMNN_ASSERT(op->GetOutputTensorNames().size() == numOutputs);
+
+    for (uint32_t i = 0; i < numInputs; i++)
+    {
+        std::basic_string<char> blockInputName = basicBlock->GetInputs()[i];
+        std::basic_string<char> operatorInputName  = op->GetInputTensorNames()[i];
+        std::basic_string<char> tensorName = basicBlock->GetTensors()[i]->GetName();
+
+        std::string opStr = operatorString + "_input" + std::to_string(i) + "_";
+
+        ARMNN_ASSERT(blockInputName == operatorInputName);
+        ARMNN_ASSERT(tensorName == operatorInputName);
+        ARMNN_ASSERT(blockInputName.find(opStr)  != std::string::npos);
+    }
+
+    for (uint32_t i = 0; i < numOutputs; i++)
+    {
+        std::basic_string<char> blockOutputName = basicBlock->GetOutputs()[i];
+        std::basic_string<char> operatorOutputName  = op->GetOutputTensorNames()[i];
+        std::basic_string<char> tensorName = basicBlock->GetTensors()[numInputs + i]->GetName();
+
+        std::string opStr = operatorString + "_output" + std::to_string(i) + "_";
+
+        ARMNN_ASSERT(blockOutputName == operatorOutputName);
+        ARMNN_ASSERT(tensorName == operatorOutputName);
+        ARMNN_ASSERT(blockOutputName.find(opStr)  != std::string::npos);
+    }
+
+    ARMNN_ASSERT(op->GetAttributeType() == Attribute_NONE);
+    ARMNN_ASSERT(op->GetOp() == tosaOp);
+
+    TosaSerializationTensor* tensor0 = basicBlock->GetTensors()[0];
+    ARMNN_ASSERT(tensor0->GetDtype() == dataType);
+    ARMNN_ASSERT(tensor0->GetData().size() == 0);
+    ARMNN_ASSERT(tensor0->GetShape() == shape);
+}
+
+TEST_SUITE("TosaOperatorMappingOneToOneTests")
+{
+TEST_CASE("GetTosaMapping_AdditionLayer")
+{
+    TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true);
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor());
+    AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD");
+}
+
+TEST_CASE("GetTosaMappingFromLayer_AdditionLayer")
+{
+    IRuntime::CreationOptions options;
+    IRuntimePtr runtime(IRuntime::Create(options));
+
+    // Builds up the structure of the network.
+    INetworkPtr net(INetwork::Create());
+
+    IConnectableLayer* input0 = net->AddInputLayer(0, "input0");
+    IConnectableLayer* input1 = net->AddInputLayer(1, "input1");
+    IConnectableLayer* add    = net->AddAdditionLayer("add");
+    IConnectableLayer* output = net->AddOutputLayer(0, "output");
+
+    input0->GetOutputSlot(0).Connect(add->GetInputSlot(0));
+    input1->GetOutputSlot(0).Connect(add->GetInputSlot(1));
+    add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+    TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true);
+
+    input0->GetOutputSlot(0).SetTensorInfo(info);
+    input1->GetOutputSlot(0).SetTensorInfo(info);
+    add->GetOutputSlot(0).SetTensorInfo(info);
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(add));
+    AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD");
+}
+
+TEST_CASE("GetTosaMapping_Unimplemented")
+{
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor());
+
+    ARMNN_ASSERT(basicBlock->GetName() == "");
+    ARMNN_ASSERT(basicBlock->GetTensors().size() == 0);
+    ARMNN_ASSERT(basicBlock->GetOperators().size() == 1);
+    ARMNN_ASSERT(basicBlock->GetInputs().size() == 0);
+    ARMNN_ASSERT(basicBlock->GetOutputs().size() == 0);
+
+    TosaSerializationOperator* op = basicBlock->GetOperators()[0];
+    ARMNN_ASSERT(op->GetAttributeType() == Attribute_NONE);
+    ARMNN_ASSERT(op->GetOp() == tosa::Op_UNKNOWN);
+    ARMNN_ASSERT(op->GetInputTensorNames().size() == 0);
+    ARMNN_ASSERT(op->GetOutputTensorNames().size() == 0);
+}
+}