IVGCVSW-7345 Add Pooling2d support to TOSA Reference Backend

Signed-off-by: Cathal Corbett <cathal.corbett@arm.com>
Change-Id: I73a47e513fe2d064ef233b121a68ef2edf0396dc
diff --git a/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp
index a1a8bac..f335218 100644
--- a/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp
+++ b/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp
@@ -105,4 +105,4 @@
     EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network), inputTensorData, expectedOutputData, backends);
 }
 
-} // anonymous namespaceS
\ No newline at end of file
+} // anonymous namespace
diff --git a/src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp
new file mode 100644
index 0000000..addd27c
--- /dev/null
+++ b/src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp
@@ -0,0 +1,172 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <armnn/INetwork.hpp>
+#include <armnn/Types.hpp>
+
+#include <CommonTestUtils.hpp>
+#include <ResolveType.hpp>
+
+#include <doctest/doctest.h>
+
+namespace
+{
+
+using namespace armnn;
+
+template<typename armnn::DataType DataType>
+armnn::INetworkPtr CreatePooling2dNetwork(const armnn::TensorShape& inputShape,
+                                          const armnn::TensorShape& outputShape,
+                                          PaddingMethod padMethod = PaddingMethod::Exclude,
+                                          PoolingAlgorithm poolAlg = PoolingAlgorithm::Max,
+                                          const float qScale = 1.0f,
+                                          const int32_t qOffset = 0)
+{
+    INetworkPtr network(INetwork::Create());
+
+    TensorInfo inputTensorInfo(inputShape, DataType, qScale, qOffset, true);
+    TensorInfo outputTensorInfo(outputShape, DataType, qScale, qOffset, true);
+
+    Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = poolAlg;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 3;
+    descriptor.m_StrideX = descriptor.m_StrideY = 1;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = padMethod;
+    descriptor.m_DataLayout = DataLayout::NHWC;
+
+    IConnectableLayer* pool = network->AddPooling2dLayer(descriptor, "pool");
+    IConnectableLayer* input = network->AddInputLayer(0, "input");
+    IConnectableLayer* output = network->AddOutputLayer(0, "output");
+
+    Connect(input, pool, inputTensorInfo, 0, 0);
+    Connect(pool, output, outputTensorInfo, 0, 0);
+
+    return network;
+}
+
+template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+void MaxPool2dEndToEnd(const std::vector<armnn::BackendId>& backends,
+                       PaddingMethod padMethod = PaddingMethod::Exclude)
+{
+    const TensorShape& inputShape = { 1, 3, 3, 1 };
+    const TensorShape& outputShape = { 1, 3, 3, 1 };
+
+    INetworkPtr network = CreatePooling2dNetwork<ArmnnType>(inputShape, outputShape, padMethod);
+
+    CHECK(network);
+
+    std::vector<T> inputData{ 1, 2, 3,
+                              4, 5, 6,
+                              7, 8, 9 };
+    std::vector<T> expectedOutput{ 5, 6, 6,
+                                   8, 9, 9,
+                                   8, 9, 9 };
+
+    std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
+    std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput } };
+
+    EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network), inputTensorData, expectedOutputData, backends);
+}
+
+template<armnn::DataType ArmnnType>
+void MaxPool2dEndToEndFloat16(const std::vector<armnn::BackendId>& backends)
+{
+    using namespace half_float::literal;
+    using Half = half_float::half;
+
+    const TensorShape& inputShape = { 1, 3, 3, 1 };
+    const TensorShape& outputShape = { 1, 3, 3, 1 };
+
+    INetworkPtr network = CreatePooling2dNetwork<ArmnnType>(inputShape, outputShape);
+    CHECK(network);
+
+    std::vector<Half> inputData{ 1._h, 2._h, 3._h,
+                                 4._h, 5._h, 6._h,
+                                 7._h, 8._h, 9._h };
+    std::vector<Half> expectedOutput{ 5._h, 6._h, 6._h,
+                                      8._h, 9._h, 9._h,
+                                      8._h, 9._h, 9._h };
+
+    std::map<int, std::vector<Half>> inputTensorData = { { 0, inputData } };
+    std::map<int, std::vector<Half>> expectedOutputData = { { 0, expectedOutput } };
+
+    EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network), inputTensorData, expectedOutputData, backends);
+}
+
+template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+void AvgPool2dEndToEnd(const std::vector<armnn::BackendId>& backends,
+                       PaddingMethod padMethod = PaddingMethod::Exclude)
+{
+    const TensorShape& inputShape =  { 1, 3, 3, 1 };
+    const TensorShape& outputShape =  { 1, 3, 3, 1 };
+
+    INetworkPtr network = CreatePooling2dNetwork<ArmnnType>(
+        inputShape, outputShape, padMethod, PoolingAlgorithm::Average);
+    CHECK(network);
+
+    std::vector<T> inputData{ 1, 2, 3,
+                              4, 5, 6,
+                              7, 8, 9 };
+    std::vector<T> expectedOutput;
+    if (padMethod == PaddingMethod::Exclude)
+    {
+        expectedOutput  = { 3  , 3.5 , 4 ,
+                            4.5, 5  , 5.5,
+                            6  , 6.5, 7  };
+    }
+    else
+    {
+        expectedOutput  = { 1.33333, 2.33333, 1.77778,
+                            3      , 5      , 3.66667,
+                            2.66667, 4.33333, 3.11111 };
+    }
+
+    std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
+    std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput } };
+
+    EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network),
+                                                inputTensorData,
+                                                expectedOutputData,
+                                                backends,
+                                                0.00001f);
+}
+
+template<armnn::DataType ArmnnType>
+void AvgPool2dEndToEndFloat16(const std::vector<armnn::BackendId>& backends,
+                              PaddingMethod padMethod = PaddingMethod::IgnoreValue)
+{
+    using namespace half_float::literal;
+    using Half = half_float::half;
+
+    const TensorShape& inputShape =  { 1, 3, 3, 1 };
+    const TensorShape& outputShape =  { 1, 3, 3, 1 };
+
+    INetworkPtr network = CreatePooling2dNetwork<ArmnnType>(
+        inputShape, outputShape, padMethod, PoolingAlgorithm::Average);
+    CHECK(network);
+
+    std::vector<Half> inputData{ 1._h, 2._h, 3._h,
+                                 4._h, 5._h, 6._h,
+                                 7._h, 8._h, 9._h };
+    std::vector<Half> expectedOutput{ 1.33333_h, 2.33333_h, 1.77778_h,
+                                      3._h     , 5._h     , 3.66667_h,
+                                      2.66667_h, 4.33333_h, 3.11111_h };
+
+    std::map<int, std::vector<Half>> inputTensorData = { { 0, inputData } };
+    std::map<int, std::vector<Half>> expectedOutputData = { { 0, expectedOutput } };
+
+    EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network),
+                                                inputTensorData,
+                                                expectedOutputData,
+                                                backends,
+                                                0.00001f);
+}
+
+} // anonymous namespace
diff --git a/src/backends/tosaCommon/TosaLayerSupportRules.hpp b/src/backends/tosaCommon/TosaLayerSupportRules.hpp
index 2a2b08d..792908c 100644
--- a/src/backends/tosaCommon/TosaLayerSupportRules.hpp
+++ b/src/backends/tosaCommon/TosaLayerSupportRules.hpp
@@ -38,3 +38,29 @@
         m_Res = (tensor->GetShape().size() <= MaxNumOfTensorDimensions) || (!tensor->GetShape().empty());
     }
 };
+
+struct TosaAssertSize : public Rule
+{
+    template<typename Container>
+    explicit TosaAssertSize(const Container& c1, const Container& c2)
+    {
+        m_Res = (c1.size() == c2.size());
+    }
+};
+
+struct TosaContainerContains : public Rule
+{
+    explicit TosaContainerContains(std::tuple<DType, DType>& check, const std::vector<std::tuple<DType, DType>>& c)
+    {
+        for (auto item: c)
+        {
+            if (std::get<0>(check) == std::get<0>(item)
+                && std::get<1>(check) == std::get<1>(item))
+            {
+                m_Res = true;
+                return;
+            }
+        }
+        m_Res = false;
+    }
+};
diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp
index 71d2012..a37eaf2 100644
--- a/src/backends/tosaCommon/TosaMappings.cpp
+++ b/src/backends/tosaCommon/TosaMappings.cpp
@@ -23,10 +23,18 @@
     }
 }
 
+TosaSerializationBasicBlock* CreateEmptyTosaSerializationBasicBlock()
+{
+    // empty basic block when no tosa mapping implemented/exists
+    TosaSerializationOperator* op =
+        new TosaSerializationOperator(Op_UNKNOWN, Attribute_NONE, nullptr, {}, {});
+    return new TosaSerializationBasicBlock("", {op}, {}, {}, {});
+}
+
 TosaSerializationBasicBlock* GetTosaMapping(const LayerType type,
                                             const std::vector<const TensorInfo*>& inputs,
                                             const std::vector<const TensorInfo*>& outputs,
-                                            const BaseDescriptor& /*descriptor*/,
+                                            const BaseDescriptor& descriptor,
                                             bool isMain = false)
 {
     switch (type)
@@ -35,11 +43,30 @@
         {
             return ConvertAdditionToTosaOperator(inputs, outputs, isMain);
         }
+        case LayerType::Pooling2d:
+        {
+            auto poolDesc = PolymorphicDowncast<const Pooling2dDescriptor*>(&descriptor);
+
+            bool avgPoolIgnoreValue =
+                (poolDesc->m_PoolType == PoolingAlgorithm::Average) &&
+                (poolDesc->m_PaddingMethod == PaddingMethod::IgnoreValue);
+
+            if (poolDesc->m_PoolType == PoolingAlgorithm::L2)
+            {
+                return CreateEmptyTosaSerializationBasicBlock();
+            }
+            else if (avgPoolIgnoreValue)
+            {
+                return ConvertAvgPool2DIgnoreValueToTosaOperator(inputs, outputs, isMain, poolDesc);
+            }
+            else
+            {
+                return ConvertPooling2DToTosaOperator(inputs, outputs, isMain, poolDesc);
+            }
+        }
         default:
         {
-            // empty basic block when no tosa mapping implemented/exists
-            TosaSerializationOperator* op = new TosaSerializationOperator(Op_UNKNOWN, Attribute_NONE, nullptr, {}, {});
-            return new TosaSerializationBasicBlock("", {op}, {}, {}, {});
+            return CreateEmptyTosaSerializationBasicBlock();
         }
     }
 }
diff --git a/src/backends/tosaCommon/TosaMappings.hpp b/src/backends/tosaCommon/TosaMappings.hpp
index 7f137ca..8df2493 100644
--- a/src/backends/tosaCommon/TosaMappings.hpp
+++ b/src/backends/tosaCommon/TosaMappings.hpp
@@ -8,7 +8,7 @@
 #include <Layer.hpp>
 
 #include <tosa_serialization_handler.h>
-#include "operatorMappings/AdditionOperator.hpp"
+#include "operatorMappings/TosaCommonOperators.hpp"
 
 using namespace armnn;
 using namespace tosa;
diff --git a/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp
new file mode 100644
index 0000000..b3d2687
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp
@@ -0,0 +1,107 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "Pooling2DOperator.hpp"
+
+TosaSerializationBasicBlock* ConvertAvgPool2DIgnoreValueToTosaOperator(const std::vector<const TensorInfo*>& inputs,
+                                                                       const std::vector<const TensorInfo*>& outputs,
+                                                                       bool isMain,
+                                                                       const Pooling2dDescriptor* poolDescriptor)
+{
+
+    // A helper function with static global variables ensures uniqueness
+    // for dynamically generating input, output and block names
+    std::string padInputName   = std::string("Op_PAD_input0_")  + GetUniqueTosaMappingID();
+    std::string padOutputName  = std::string("Op_PAD_intermediate0_")  + GetUniqueTosaMappingID();
+    std::string poolOutputName = std::string("Op_AVG_POOL2D_output0_") + GetUniqueTosaMappingID();
+    std::string blockName      = std::string("Op_AVG_POOL2D_block_")   + GetUniqueTosaMappingID();
+
+    // If it's the first block, overwrite block name with main.
+    if (isMain)
+    {
+        blockName = std::string("main");
+    }
+
+    std::vector<int> paddings;
+    if (poolDescriptor->m_DataLayout == DataLayout::NHWC)
+    {
+        paddings = {0,
+                    0,
+                    static_cast<int>(poolDescriptor->m_PadTop),
+                    static_cast<int>(poolDescriptor->m_PadBottom),
+                    static_cast<int>(poolDescriptor->m_PadLeft),
+                    static_cast<int>(poolDescriptor->m_PadRight),
+                    0,
+                    0
+        };
+    }
+    else
+    {
+        paddings = {0,
+                    0,
+                    0,
+                    0,
+                    static_cast<int>(poolDescriptor->m_PadTop),
+                    static_cast<int>(poolDescriptor->m_PadBottom),
+                    static_cast<int>(poolDescriptor->m_PadLeft),
+                    static_cast<int>(poolDescriptor->m_PadRight)
+        };
+    }
+
+    TosaPadAttribute padAttribute(paddings, 0, 0.0f);
+    TosaSerializationOperator* opPad = new TosaSerializationOperator(Op_PAD,
+                                                                     Attribute_PadAttribute,
+                                                                     &padAttribute,
+                                                                     {padInputName},
+                                                                     {padOutputName});
+
+    std::vector<int> pad    = {0, 0, 0, 0};
+    std::vector<int> kernel = {static_cast<int>(poolDescriptor->m_PoolHeight),
+                               static_cast<int>(poolDescriptor->m_PoolWidth)};
+    std::vector<int> stride = {static_cast<int>(poolDescriptor->m_StrideY),
+                               static_cast<int>(poolDescriptor->m_StrideX)};
+    TosaPoolAttribute poolAttribute(pad, kernel, stride, 0, 0, ArmNNToDType(inputs[0]->GetDataType()));
+
+    TosaSerializationOperator* opPool = new TosaSerializationOperator(Op_AVG_POOL2D,
+                                                                      Attribute_PoolAttribute,
+                                                                      &poolAttribute,
+                                                                      {padOutputName},
+                                                                      {poolOutputName});
+
+    std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
+    DType inputDType = ArmNNToDType(inputs[0]->GetDataType());
+
+    std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[0]->GetShape());
+    DType outputDType = ArmNNToDType(outputs[0]->GetDataType());
+
+    std::vector<int32_t> intermediateShape;
+    if (poolDescriptor->m_DataLayout == DataLayout::NHWC)
+    {
+        intermediateShape = {inputShape[0],
+                             inputShape[1] + paddings[2] + paddings[3],
+                             inputShape[2] + paddings[4] + paddings[5],
+                             inputShape[3]};
+    }
+    else
+    {
+        intermediateShape = {inputShape[0],
+                             inputShape[1],
+                             inputShape[2] + paddings[4] + paddings[5],
+                             inputShape[3] + paddings[6] + paddings[7]};
+    }
+
+    TosaSerializationTensor* inputTensor  = new TosaSerializationTensor(padInputName, inputShape, inputDType, {});
+    TosaSerializationTensor* intermediateTensor  = new TosaSerializationTensor(
+        padOutputName, intermediateShape, inputDType, {});
+    TosaSerializationTensor* outputTensor = new TosaSerializationTensor(poolOutputName, outputShape, outputDType, {});
+
+    // operatorInputNames/operatorOutputNames ends up being the same as
+    // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
+    return new TosaSerializationBasicBlock(blockName, // name
+                                           {opPad, opPool}, // operators
+                                           {inputTensor, intermediateTensor, outputTensor}, // tensors
+                                           {padInputName}, // inputs
+                                           {poolOutputName}); // outputs
+}
\ No newline at end of file
diff --git a/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp
new file mode 100644
index 0000000..63ae190
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp
@@ -0,0 +1,19 @@
+//
+// 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* ConvertAvgPool2DIgnoreValueToTosaOperator(const std::vector<const TensorInfo*>& inputs,
+                                                                       const std::vector<const TensorInfo*>& outputs,
+                                                                       bool isMain,
+                                                                       const Pooling2dDescriptor* poolDescriptor);
diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
index 9fc33e9..7fdc922 100644
--- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
+++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
@@ -6,6 +6,10 @@
 list(APPEND armnnTosaBackendOperators_sources
         AdditionOperator.hpp
         AdditionOperator.cpp
+        AvgPool2DIgnoreValueOperator.hpp
+        AvgPool2DIgnoreValueOperator.cpp
+        Pooling2DOperator.hpp
+        Pooling2DOperator.cpp
         TosaOperatorUtils.hpp
     )
 
diff --git a/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp
new file mode 100644
index 0000000..cd707ed
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "Pooling2DOperator.hpp"
+
+TosaSerializationBasicBlock* ConvertPooling2DToTosaOperator(const std::vector<const TensorInfo*>& inputs,
+                                                            const std::vector<const TensorInfo*>& outputs,
+                                                            bool isMain,
+                                                            const Pooling2dDescriptor* poolDescriptor)
+{
+    std::string poolType = (poolDescriptor->m_PoolType == PoolingAlgorithm::Max) ? "Op_MAX" : "Op_AVG";
+    Op opcode = (poolDescriptor->m_PoolType == PoolingAlgorithm::Max) ? Op_MAX_POOL2D : Op_AVG_POOL2D;
+
+    // A helper function with static global variables ensures uniqueness
+    // for dynamically generating input, output and block names
+    std::string input0Name = poolType + std::string("_POOL2D_input0_")  + GetUniqueTosaMappingID();
+    std::string outputName = poolType + std::string("_POOL2D_output0_") + GetUniqueTosaMappingID();
+    std::string blockName  = poolType + std::string("_POOL2D_block_")   + GetUniqueTosaMappingID();
+
+    // If it's the first block, overwrite block name with main.
+    if (isMain)
+    {
+        blockName = std::string("main");
+    }
+
+    std::vector<int> pad = {static_cast<int>(poolDescriptor->m_PadTop),
+                            static_cast<int>(poolDescriptor->m_PadBottom),
+                            static_cast<int>(poolDescriptor->m_PadLeft),
+                            static_cast<int>(poolDescriptor->m_PadRight)};
+    std::vector<int> kernel = {static_cast<int>(poolDescriptor->m_PoolHeight),
+                               static_cast<int>(poolDescriptor->m_PoolWidth)};
+    std::vector<int> stride = {static_cast<int>(poolDescriptor->m_StrideY),
+                               static_cast<int>(poolDescriptor->m_StrideX)};
+    TosaPoolAttribute attribute(pad, kernel, stride, 0, 0, ArmNNToDType(inputs[0]->GetDataType()));
+
+    TosaSerializationOperator* op = new TosaSerializationOperator(opcode,
+                                                                  Attribute_PoolAttribute,
+                                                                  &attribute,
+                                                                  {input0Name},
+                                                                  {outputName});
+
+    std::vector<int32_t> inputShape0 = GetTosaTensorShape(inputs[0]->GetShape());
+    DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType());
+
+    std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape());
+    DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
+
+    TosaSerializationTensor* inputTensor0  = new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {});
+    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, outputTensor0}, // tensors
+                                           {input0Name}, // inputs
+                                           {outputName}); // outputs
+}
\ No newline at end of file
diff --git a/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp
new file mode 100644
index 0000000..22d2a3a
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp
@@ -0,0 +1,19 @@
+//
+// 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* ConvertPooling2DToTosaOperator(const std::vector<const TensorInfo*>& inputs,
+                                                            const std::vector<const TensorInfo*>& outputs,
+                                                            bool isMain,
+                                                            const Pooling2dDescriptor* poolDescriptor);
diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
new file mode 100644
index 0000000..00b5f0f
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
@@ -0,0 +1,10 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "AdditionOperator.hpp"
+#include "AvgPool2DIgnoreValueOperator.hpp"
+#include "Pooling2DOperator.hpp"
\ No newline at end of file
diff --git a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
index f580a53..f51b210 100644
--- a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
+++ b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
@@ -59,3 +59,150 @@
 {
     return std::to_string(++uniqueTosaMappingID);
 }
+
+// Function to return Tosa Op as string.
+inline std::string TosaOpToString(Op tosaOp)
+{
+    switch (tosaOp)
+    {
+        case Op_ADD:
+            return "Op_ADD";
+        case Op_AVG_POOL2D:
+            return "Op_AVG_POOL2D";
+        case Op_MAX_POOL2D:
+            return "Op_MAX_POOL2D";
+        case Op_PAD:
+            return "Op_PAD";
+        case Op_UNKNOWN:
+            return "Op_UNKNOWN";
+        case Op_ARGMAX:
+            return "Op_ARGMAX";
+        case Op_CONV2D:
+            return "Op_CONV2D";
+        case Op_CONV3D:
+            return "Op_CONV3D";
+        case Op_DEPTHWISE_CONV2D:
+            return "Op_DEPTHWISE_CONV2D";
+        case Op_FULLY_CONNECTED:
+            return "Op_FULLY_CONNECTED";
+        case Op_MATMUL:
+            return "Op_MATMUL";
+        case Op_TRANSPOSE_CONV2D:
+            return "Op_TRANSPOSE_CONV2D";
+        case Op_CLAMP:
+            return "Op_CLAMP";
+        case Op_RESERVED:
+            return "Op_RESERVED";
+        case Op_SIGMOID:
+            return "Op_SIGMOID";
+        case Op_TANH:
+            return "Op_TANH";
+        case Op_ARITHMETIC_RIGHT_SHIFT:
+            return "Op_ARITHMETIC_RIGHT_SHIFT";
+        case Op_BITWISE_AND:
+            return "Op_BITWISE_AND";
+        case Op_BITWISE_OR:
+            return "Op_BITWISE_OR";
+        case Op_BITWISE_XOR:
+            return "Op_BITWISE_XOR";
+        case Op_INTDIV:
+            return "Op_INTDIV";
+        case Op_LOGICAL_AND:
+            return "Op_LOGICAL_AND";
+        case Op_LOGICAL_LEFT_SHIFT:
+            return "Op_LOGICAL_LEFT_SHIFT";
+        case Op_LOGICAL_RIGHT_SHIFT:
+            return "Op_LOGICAL_RIGHT_SHIFT";
+        case Op_LOGICAL_OR:
+            return "Op_LOGICAL_OR";
+        case Op_LOGICAL_XOR:
+            return "Op_LOGICAL_XOR";
+        case Op_MAXIMUM:
+            return "Op_MAXIMUM";
+        case Op_MINIMUM:
+            return "Op_MINIMUM";
+        case Op_MUL:
+            return "Op_MUL";
+        case Op_POW:
+            return "Op_POW";
+        case Op_SUB:
+            return "Op_SUB";
+        case Op_TABLE:
+            return "Op_TABLE";
+        case Op_ABS:
+            return "Op_ABS";
+        case Op_BITWISE_NOT:
+            return "Op_BITWISE_NOT";
+        case Op_CEIL:
+            return "Op_CEIL";
+        case Op_CLZ:
+            return "Op_CLZ";
+        case Op_EXP:
+            return "Op_EXP";
+        case Op_FLOOR:
+            return "Op_FLOOR";
+        case Op_LOG:
+            return "Op_LOG";
+        case Op_LOGICAL_NOT:
+            return "Op_LOGICAL_NOT";
+        case Op_NEGATE:
+            return "Op_NEGATE";
+        case Op_RECIPROCAL:
+            return "Op_RECIPROCAL";
+        case Op_RSQRT:
+            return "Op_RSQRT";
+        case Op_SELECT:
+            return "Op_SELECT";
+        case Op_EQUAL:
+            return "Op_EQUAL";
+        case Op_GREATER:
+            return "Op_GREATER";
+        case Op_GREATER_EQUAL:
+            return "Op_GREATER_EQUAL";
+        case Op_REDUCE_ANY:
+            return "Op_REDUCE_ANY";
+        case Op_REDUCE_ALL:
+            return "Op_REDUCE_ALL";
+        case Op_REDUCE_MAX:
+            return "Op_REDUCE_MAX";
+        case Op_REDUCE_MIN:
+            return "Op_REDUCE_MIN";
+        case Op_REDUCE_PRODUCT:
+            return "Op_REDUCE_PRODUCT";
+        case Op_REDUCE_SUM:
+            return "Op_REDUCE_SUM";
+        case Op_CONCAT:
+            return "Op_CONCAT";
+        case Op_RESHAPE:
+            return "Op_RESHAPE";
+        case Op_REVERSE:
+            return "Op_REVERSE";
+        case Op_SLICE:
+            return "Op_SLICE";
+        case Op_TILE:
+            return "Op_TILE";
+        case Op_TRANSPOSE:
+            return "Op_TRANSPOSE";
+        case Op_GATHER:
+            return "Op_GATHER";
+        case Op_SCATTER:
+            return "Op_SCATTER";
+        case Op_RESIZE:
+            return "Op_RESIZE";
+        case Op_CAST:
+            return "Op_CAST";
+        case Op_RESCALE:
+            return "Op_RESCALE";
+        case Op_CONST:
+            return "Op_CONST";
+        case Op_IDENTITY:
+            return "Op_IDENTITY";
+        case Op_CUSTOM:
+            return "Op_CUSTOM";
+        case Op_COND_IF:
+            return "Op_COND_IF";
+        case Op_WHILE_LOOP:
+            return "Op_WHILE_LOOP";
+    }
+    return "";
+}
diff --git a/src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp b/src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp
new file mode 100644
index 0000000..8869b3a
--- /dev/null
+++ b/src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp
@@ -0,0 +1,125 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "TosaTestUtils.hpp"
+
+using namespace armnn;
+using namespace tosa;
+
+void VerifyAvgPool2DIgnoreValue(TosaSerializationBasicBlock* basicBlock,
+                                std::vector<std::vector<int32_t>> inputShape,
+                                std::vector<std::vector<int32_t>> outputShape,
+                                std::vector<std::vector<int32_t>> intermediateShape,
+                                const BaseDescriptor& descriptor,
+                                DType dataType = DType_FP32)
+{
+    uint32_t numInputs = static_cast<uint32_t>(inputShape.size());
+    uint32_t numOutputs = static_cast<uint32_t>(outputShape.size());
+    std::string operatorString0 = TosaOpToString(Op_PAD);
+    std::string operatorString1 = TosaOpToString(Op_AVG_POOL2D);
+
+    std::string blockStr = operatorString1 + "_block_";
+    CHECK(basicBlock->GetName().find(blockStr)  != std::string::npos);
+    CHECK(basicBlock->GetInputs().size() == numInputs);
+    CHECK(basicBlock->GetOutputs().size() == numOutputs);
+    CHECK(basicBlock->GetOperators().size() == 2);
+    CHECK(basicBlock->GetTensors().size() == 3);
+
+    //
+    // Verify padding operator first.
+    //
+
+    TosaSerializationOperator* padOp = basicBlock->GetOperators().at(0);
+    uint32_t padOpOutputs = 1;
+    CHECK(padOp->GetInputTensorNames().size() == numInputs);
+    CHECK(padOp->GetOutputTensorNames().size() == padOpOutputs);
+
+    for (uint32_t i = 0; i < numInputs; i++)
+    {
+        std::basic_string<char> blockInputName = basicBlock->GetInputs()[i];
+        std::basic_string<char> operatorInputName  = padOp->GetInputTensorNames()[i];
+
+        std::string opStr = operatorString0 + "_input" + std::to_string(i) + "_";
+
+        CHECK(blockInputName == operatorInputName);
+        CHECK(basicBlock->GetTensorByName(blockInputName));
+        CHECK(blockInputName.find(opStr)  != std::string::npos);
+
+        TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorInputName);
+        CHECK(tensor->GetDtype() == dataType);
+        CHECK(tensor->GetData().size() == 0);
+        CHECK(tensor->GetShape() == inputShape[static_cast<unsigned long int>(i)]);
+    }
+
+    for (uint32_t i = 0; i < padOpOutputs; i++)
+    {
+        std::basic_string<char> operatorOutputName  = padOp->GetOutputTensorNames()[i];
+        std::string opStr = operatorString0 + "_intermediate" + std::to_string(i) + "_";
+
+        CHECK(basicBlock->GetTensorByName(operatorOutputName));
+        CHECK(operatorOutputName.find(opStr)  != std::string::npos);
+
+        TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorOutputName);
+        CHECK(tensor->GetDtype() == dataType);
+        CHECK(tensor->GetData().size() == 0);
+        CHECK(tensor->GetShape() == intermediateShape[static_cast<unsigned long int>(i)]);
+    }
+
+    CHECK(padOp->GetAttributeType() == Attribute_PadAttribute);
+    CHECK(padOp->GetOp() == Op_PAD);
+
+    VerifyTosaAttributeFromDescriptor(descriptor,
+                                      padOp->GetAttribute(),
+                                      LayerType::Pooling2d);
+
+    //
+    // Verify average pool operator second.
+    //
+
+    TosaSerializationOperator* poolOp = basicBlock->GetOperators().at(1);
+    uint32_t poolOpInputs = 1;
+    CHECK(poolOp->GetInputTensorNames().size() == poolOpInputs);
+    CHECK(poolOp->GetOutputTensorNames().size() == numOutputs);
+
+    for (uint32_t i = 0; i < poolOpInputs; i++)
+    {
+        std::basic_string<char> operatorInputName  = poolOp->GetInputTensorNames()[i];
+        std::string opStr = operatorString0 + "_intermediate" + std::to_string(i) + "_";
+
+        CHECK(basicBlock->GetTensorByName(operatorInputName));
+        CHECK(operatorInputName.find(opStr)  != std::string::npos);
+
+        TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorInputName);
+        CHECK(tensor->GetDtype() == dataType);
+        CHECK(tensor->GetData().size() == 0);
+        CHECK(tensor->GetShape() == intermediateShape[static_cast<unsigned long int>(i)]);
+    }
+
+    for (uint32_t i = 0; i < numOutputs; i++)
+    {
+        std::basic_string<char> blockOutputName = basicBlock->GetOutputs()[i];
+        std::basic_string<char> operatorOutputName  = poolOp->GetOutputTensorNames()[i];
+
+        std::string opStr = operatorString1 + "_output" + std::to_string(i) + "_";
+
+        CHECK(blockOutputName == operatorOutputName);
+        CHECK(basicBlock->GetTensorByName(blockOutputName));
+        CHECK(blockOutputName.find(opStr)  != std::string::npos);
+
+        TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorOutputName);
+        CHECK(tensor->GetDtype() == dataType);
+        CHECK(tensor->GetData().size() == 0);
+        CHECK(tensor->GetShape() == outputShape[static_cast<unsigned long int>(i)]);
+    }
+
+    CHECK(poolOp->GetAttributeType() == Attribute_PoolAttribute);
+    CHECK(poolOp->GetOp() == Op_AVG_POOL2D);
+
+    VerifyTosaAttributeFromDescriptor(descriptor,
+                                      poolOp->GetAttribute(),
+                                      LayerType::Pooling2d,
+                                      1);
+
+}
\ No newline at end of file
diff --git a/src/backends/tosaCommon/test/CMakeLists.txt b/src/backends/tosaCommon/test/CMakeLists.txt
index 56cc66e..34c4b0c 100644
--- a/src/backends/tosaCommon/test/CMakeLists.txt
+++ b/src/backends/tosaCommon/test/CMakeLists.txt
@@ -4,7 +4,8 @@
 #
 
 list(APPEND armnnTosaBackendUnitTests_sources
-        TosaOperatorMappingTests.cpp
+        OneToManyMappingTests.cpp
+        OneToOneMappingTests.cpp
     )
 
 add_library(armnnTosaBackendUnitTests OBJECT ${armnnTosaBackendUnitTests_sources})
diff --git a/src/backends/tosaCommon/test/OneToManyMappingTests.cpp b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp
new file mode 100644
index 0000000..98fd563
--- /dev/null
+++ b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp
@@ -0,0 +1,84 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "AvgPool2DIgnoreValueChecker.hpp"
+
+using namespace armnn;
+using namespace tosa;
+
+TEST_SUITE("TosaOperatorMappingOneToManyTests")
+{
+TEST_CASE("GetTosaMapping_AvgPool2DIgnoreValueLayer")
+{
+    armnn::Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = armnn::PoolingAlgorithm::Average;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2;
+    descriptor.m_StrideX = descriptor.m_StrideY = 2;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32);
+    armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32);
+
+    std::vector<std::vector<int32_t>> inputShape         = {{ 1, 1, 4, 4 }};
+    std::vector<std::vector<int32_t>> intermediateShape  = {{ 1, 1, 6, 6 }};
+    std::vector<std::vector<int32_t>> outputShape        = {{ 1, 1, 3, 3 }};
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::Pooling2d, {&inputTensorInfo}, {&outputTensorInfo}, descriptor, false);
+    VerifyAvgPool2DIgnoreValue(basicBlock,
+                               inputShape,
+                               outputShape,
+                               intermediateShape,
+                               descriptor);
+}
+
+TEST_CASE("GetTosaMappingFromLayer_AvgPool2DIgnoreValueLayer")
+{
+    IRuntime::CreationOptions options;
+    IRuntimePtr runtime(IRuntime::Create(options));
+
+    // Builds up the structure of the network.
+    INetworkPtr net(INetwork::Create());
+
+    armnn::Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = armnn::PoolingAlgorithm::Average;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2;
+    descriptor.m_StrideX = descriptor.m_StrideY = 2;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
+
+    IConnectableLayer* input0  = net->AddInputLayer(0, "input0");
+    IConnectableLayer* pool    = net->AddPooling2dLayer(descriptor, "pool");
+    IConnectableLayer* output  = net->AddOutputLayer(0, "output");
+
+    input0->GetOutputSlot(0).Connect(pool->GetInputSlot(0));
+    pool->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32);
+    armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32);
+
+    std::vector<std::vector<int32_t>> inputShape         = {{ 1, 1, 4, 4 }};
+    std::vector<std::vector<int32_t>> intermediateShape  = {{ 1, 1, 6, 6 }};
+    std::vector<std::vector<int32_t>> outputShape        = {{ 1, 1, 3, 3 }};
+
+    input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
+    pool->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(pool), false);
+    VerifyAvgPool2DIgnoreValue(basicBlock,
+                              inputShape,
+                              outputShape,
+                              intermediateShape,
+                              descriptor);
+}
+}
\ No newline at end of file
diff --git a/src/backends/tosaCommon/test/OneToOneMappingTests.cpp b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp
new file mode 100644
index 0000000..04d1eb4
--- /dev/null
+++ b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp
@@ -0,0 +1,213 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "TosaTestUtils.hpp"
+
+using namespace armnn;
+using namespace tosa;
+
+TEST_SUITE("TosaOperatorMappingOneToOneTests")
+{
+TEST_CASE("GetTosaMapping_AdditionLayer")
+{
+    TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true);
+
+    std::vector<std::vector<int32_t>> inputShape  = {{ 1, 2, 4, 2 }, { 1, 2, 4, 2 }};
+    std::vector<std::vector<int32_t>> outputShape = {{ 1, 2, 4, 2 }};
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor(), false);
+    AssertTosaOneToOneMappingBasicBlock(
+        basicBlock, inputShape, outputShape, Op_ADD, Attribute_NONE, BaseDescriptor(), LayerType::Addition);
+}
+
+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);
+
+    std::vector<std::vector<int32_t>> inputShape  = {{ 1, 2, 4, 2 }, { 1, 2, 4, 2 }};
+    std::vector<std::vector<int32_t>> outputShape = {{ 1, 2, 4, 2 }};
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(add), false);
+    AssertTosaOneToOneMappingBasicBlock(
+        basicBlock, inputShape, outputShape, Op_ADD, Attribute_NONE, BaseDescriptor(), LayerType::Addition);
+}
+
+TEST_CASE("GetTosaMapping_MaxPool2DLayer")
+{
+    armnn::Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = armnn::PoolingAlgorithm::Max;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2;
+    descriptor.m_StrideX = descriptor.m_StrideY = 2;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude;
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32);
+    armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32);
+
+    std::vector<std::vector<int32_t>> inputShape  = {{ 1, 1, 4, 4 }};
+    std::vector<std::vector<int32_t>> outputShape = {{ 1, 1, 3, 3 }};
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::Pooling2d, {&inputTensorInfo}, {&outputTensorInfo}, descriptor, false);
+    AssertTosaOneToOneMappingBasicBlock(
+        basicBlock, inputShape, outputShape, Op_MAX_POOL2D, Attribute_PoolAttribute, descriptor, LayerType::Pooling2d);
+}
+
+TEST_CASE("GetTosaMappingFromLayer_MaxPool2DLayer")
+{
+    IRuntime::CreationOptions options;
+    IRuntimePtr runtime(IRuntime::Create(options));
+
+    // Builds up the structure of the network.
+    INetworkPtr net(INetwork::Create());
+
+    armnn::Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = armnn::PoolingAlgorithm::Max;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2;
+    descriptor.m_StrideX = descriptor.m_StrideY = 2;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude;
+
+    IConnectableLayer* input0  = net->AddInputLayer(0, "input0");
+    IConnectableLayer* pool    = net->AddPooling2dLayer(descriptor, "pool");
+    IConnectableLayer* output  = net->AddOutputLayer(0, "output");
+
+    input0->GetOutputSlot(0).Connect(pool->GetInputSlot(0));
+    pool->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32);
+    armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32);
+
+    std::vector<std::vector<int32_t>> inputShape  = {{ 1, 1, 4, 4 }};
+    std::vector<std::vector<int32_t>> outputShape = {{ 1, 1, 3, 3 }};
+
+    input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
+    pool->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(pool), false);
+    AssertTosaOneToOneMappingBasicBlock(
+        basicBlock, inputShape, outputShape, Op_MAX_POOL2D, Attribute_PoolAttribute, descriptor, LayerType::Pooling2d);
+}
+
+TEST_CASE("GetTosaMapping_AvgPool2DLayer")
+{
+    armnn::Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = armnn::PoolingAlgorithm::Average;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2;
+    descriptor.m_StrideX = descriptor.m_StrideY = 2;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude;
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32);
+    armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32);
+
+    std::vector<std::vector<int32_t>> inputShape  = {{ 1, 1, 4, 4 }};
+    std::vector<std::vector<int32_t>> outputShape = {{ 1, 1, 3, 3 }};
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::Pooling2d, {&inputTensorInfo}, {&outputTensorInfo}, descriptor, false);
+    AssertTosaOneToOneMappingBasicBlock(basicBlock,
+                                        inputShape,
+                                        outputShape,
+                                        Op_AVG_POOL2D,
+                                        Attribute_PoolAttribute,
+                                        descriptor,
+                                        LayerType::Pooling2d);
+}
+
+TEST_CASE("GetTosaMappingFromLayer_AvgPool2DLayer")
+{
+    IRuntime::CreationOptions options;
+    IRuntimePtr runtime(IRuntime::Create(options));
+
+    // Builds up the structure of the network.
+    INetworkPtr net(INetwork::Create());
+
+    armnn::Pooling2dDescriptor descriptor;
+    descriptor.m_PoolType = armnn::PoolingAlgorithm::Average;
+    descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2;
+    descriptor.m_StrideX = descriptor.m_StrideY = 2;
+    descriptor.m_PadLeft = 1;
+    descriptor.m_PadRight = 1;
+    descriptor.m_PadTop = 1;
+    descriptor.m_PadBottom = 1;
+    descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude;
+
+    IConnectableLayer* input0  = net->AddInputLayer(0, "input0");
+    IConnectableLayer* pool    = net->AddPooling2dLayer(descriptor, "pool");
+    IConnectableLayer* output  = net->AddOutputLayer(0, "output");
+
+    input0->GetOutputSlot(0).Connect(pool->GetInputSlot(0));
+    pool->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32);
+    armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32);
+
+    std::vector<std::vector<int32_t>> inputShape  = {{ 1, 1, 4, 4 }};
+    std::vector<std::vector<int32_t>> outputShape = {{ 1, 1, 3, 3 }};
+
+    input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
+    pool->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(pool), false);
+    AssertTosaOneToOneMappingBasicBlock(basicBlock,
+                                        inputShape,
+                                        outputShape,
+                                        Op_AVG_POOL2D,
+                                        Attribute_PoolAttribute,
+                                        descriptor,
+                                        LayerType::Pooling2d);
+}
+
+TEST_CASE("GetTosaMapping_Unimplemented")
+{
+    TosaSerializationBasicBlock* basicBlock =
+        GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor(), false);
+
+    CHECK(basicBlock->GetName() == "");
+    CHECK(basicBlock->GetTensors().size() == 0);
+    CHECK(basicBlock->GetOperators().size() == 1);
+    CHECK(basicBlock->GetInputs().size() == 0);
+    CHECK(basicBlock->GetOutputs().size() == 0);
+
+    TosaSerializationOperator* op = basicBlock->GetOperators()[0];
+    CHECK(op->GetAttributeType() == Attribute_NONE);
+    CHECK(op->GetOp() == tosa::Op_UNKNOWN);
+    CHECK(op->GetInputTensorNames().size() == 0);
+    CHECK(op->GetOutputTensorNames().size() == 0);
+}
+}
diff --git a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
deleted file mode 100644
index d7cc5d8..0000000
--- a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-//
-// 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_FP32)
-{
-    std::string blockStr = operatorString + "_block_";
-    CHECK(basicBlock->GetName().find(blockStr)  != std::string::npos);
-    CHECK(basicBlock->GetInputs().size() == numInputs);
-    CHECK(basicBlock->GetOutputs().size() == numOutputs);
-    CHECK(basicBlock->GetOperators().size() == 1);
-    CHECK(basicBlock->GetTensors().size() == (numInputs + numOutputs));
-
-    TosaSerializationOperator* op = basicBlock->GetOperators().at(0);
-    CHECK(op->GetInputTensorNames().size() == numInputs);
-    CHECK(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) + "_";
-
-        CHECK(blockInputName == operatorInputName);
-        CHECK(tensorName == operatorInputName);
-        CHECK(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) + "_";
-
-        CHECK(blockOutputName == operatorOutputName);
-        CHECK(tensorName == operatorOutputName);
-        CHECK(blockOutputName.find(opStr)  != std::string::npos);
-    }
-
-    CHECK(op->GetAttributeType() == Attribute_NONE);
-    CHECK(op->GetOp() == tosaOp);
-
-    TosaSerializationTensor* tensor0 = basicBlock->GetTensors()[0];
-    CHECK(tensor0->GetDtype() == dataType);
-    CHECK(tensor0->GetData().size() == 0);
-    CHECK(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(), false);
-    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), false);
-    AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD");
-}
-
-TEST_CASE("GetTosaMapping_Unimplemented")
-{
-    TosaSerializationBasicBlock* basicBlock =
-        GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor(), false);
-
-    CHECK(basicBlock->GetName() == "");
-    CHECK(basicBlock->GetTensors().size() == 0);
-    CHECK(basicBlock->GetOperators().size() == 1);
-    CHECK(basicBlock->GetInputs().size() == 0);
-    CHECK(basicBlock->GetOutputs().size() == 0);
-
-    TosaSerializationOperator* op = basicBlock->GetOperators()[0];
-    CHECK(op->GetAttributeType() == Attribute_NONE);
-    CHECK(op->GetOp() == tosa::Op_UNKNOWN);
-    CHECK(op->GetInputTensorNames().size() == 0);
-    CHECK(op->GetOutputTensorNames().size() == 0);
-}
-}
diff --git a/src/backends/tosaCommon/test/TosaTestUtils.hpp b/src/backends/tosaCommon/test/TosaTestUtils.hpp
new file mode 100644
index 0000000..a362bde
--- /dev/null
+++ b/src/backends/tosaCommon/test/TosaTestUtils.hpp
@@ -0,0 +1,162 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <Layer.hpp>
+
+#include <tosaCommon/TosaMappings.hpp>
+
+#include <doctest/doctest.h>
+
+using namespace armnn;
+using namespace tosa;
+
+inline void VerifyTosaAttributeFromDescriptor(const BaseDescriptor& descriptor,
+                                              const TosaAttributeBase* attribute,
+                                              LayerType type,
+                                              uint32_t mappingOpNumber = 0)
+{
+    switch (type)
+    {
+        case LayerType::Pooling2d:
+        {
+            auto poolDesc = PolymorphicDowncast<const Pooling2dDescriptor*>(&descriptor);
+            std::vector<int> pad = {static_cast<int>(poolDesc->m_PadTop),
+                                    static_cast<int>(poolDesc->m_PadBottom),
+                                    static_cast<int>(poolDesc->m_PadLeft),
+                                    static_cast<int>(poolDesc->m_PadRight)};
+
+            bool avgPoolIgnoreValue =
+                     (poolDesc->m_PoolType == PoolingAlgorithm::Average) &&
+                     (poolDesc->m_PaddingMethod == PaddingMethod::IgnoreValue);
+            if (avgPoolIgnoreValue)
+            {
+                if (mappingOpNumber == 0)
+                {
+                    if (poolDesc->m_DataLayout == DataLayout::NHWC)
+                    {
+                        pad = {0,
+                               0,
+                               static_cast<int>(poolDesc->m_PadTop),
+                               static_cast<int>(poolDesc->m_PadBottom),
+                               static_cast<int>(poolDesc->m_PadLeft),
+                               static_cast<int>(poolDesc->m_PadRight),
+                               0,
+                               0
+                        };
+                    }
+                    else
+                    {
+                        pad = {0,
+                               0,
+                               0,
+                               0,
+                               static_cast<int>(poolDesc->m_PadTop),
+                               static_cast<int>(poolDesc->m_PadBottom),
+                               static_cast<int>(poolDesc->m_PadLeft),
+                               static_cast<int>(poolDesc->m_PadRight)
+                        };
+                    }
+
+                    TosaPadAttribute padAttribute(attribute);
+
+                    CHECK(pad == padAttribute.padding());
+                    CHECK(0.0f == padAttribute.pad_const_fp());
+                    CHECK(0 == padAttribute.pad_const_int());
+
+                    break;
+                }
+                pad = {0, 0, 0, 0};
+            }
+
+            std::vector<int> kernel = {static_cast<int>(poolDesc->m_PoolHeight),
+                                       static_cast<int>(poolDesc->m_PoolWidth)};
+            std::vector<int> stride = {static_cast<int>(poolDesc->m_StrideY),
+                                       static_cast<int>(poolDesc->m_StrideX)};
+            TosaPoolAttribute poolAttribute(attribute);
+            CHECK(pad == poolAttribute.pad());
+            CHECK(kernel == poolAttribute.kernel());
+            CHECK(stride == poolAttribute.stride());
+        }
+        default:
+            break;
+    }
+    return;
+}
+
+inline void AssertTosaOneToOneMappingBasicBlock(TosaSerializationBasicBlock* basicBlock,
+                                                std::vector<std::vector<int32_t>> inputShape,
+                                                std::vector<std::vector<int32_t>> outputShape,
+                                                Op tosaOp,
+                                                Attribute tosaAttribute,
+                                                const BaseDescriptor& descriptor,
+                                                LayerType type,
+                                                DType dataType = DType_FP32)
+{
+    uint32_t numInputs = static_cast<uint32_t>(inputShape.size());
+    uint32_t numOutputs = static_cast<uint32_t>(outputShape.size());
+    std::string operatorString = TosaOpToString(tosaOp);
+
+    std::string blockStr = operatorString + "_block_";
+    CHECK(basicBlock->GetName().find(blockStr)  != std::string::npos);
+    CHECK(basicBlock->GetInputs().size() == numInputs);
+    CHECK(basicBlock->GetOutputs().size() == numOutputs);
+    CHECK(basicBlock->GetOperators().size() == 1);
+    CHECK(basicBlock->GetTensors().size() == (numInputs + numOutputs));
+
+    TosaSerializationOperator* op = basicBlock->GetOperators().at(0);
+    CHECK(op->GetInputTensorNames().size() == numInputs);
+    CHECK(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) + "_";
+
+        CHECK(blockInputName == operatorInputName);
+        CHECK(tensorName == operatorInputName);
+        CHECK(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) + "_";
+
+        CHECK(blockOutputName == operatorOutputName);
+        CHECK(tensorName == operatorOutputName);
+        CHECK(blockOutputName.find(opStr)  != std::string::npos);
+    }
+
+    CHECK(op->GetAttributeType() == tosaAttribute);
+    CHECK(op->GetOp() == tosaOp);
+
+    for (uint32_t i = 0; i < numInputs; i++)
+    {
+        TosaSerializationTensor* tensor = basicBlock->GetTensors()[i];
+        CHECK(tensor->GetDtype() == dataType);
+        CHECK(tensor->GetData().size() == 0);
+        CHECK(tensor->GetShape() == inputShape[static_cast<unsigned long int>(i)]);
+    }
+
+    for (uint32_t i = 0; i < numOutputs; i++)
+    {
+        TosaSerializationTensor* tensor = basicBlock->GetTensors()[i + inputShape.size()];
+        CHECK(tensor->GetDtype() == dataType);
+        CHECK(tensor->GetData().size() == 0);
+        CHECK(tensor->GetShape() == outputShape[static_cast<unsigned long int>(i)]);
+    }
+
+    VerifyTosaAttributeFromDescriptor(descriptor,
+                                      op->GetAttribute(),
+                                      type);
+}
\ No newline at end of file
diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp
index a39bfb6..ce4abbf 100644
--- a/src/backends/tosaReference/TosaRefLayerSupport.cpp
+++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp
@@ -13,24 +13,25 @@
 
 #include <vector>
 #include <array>
+#include <tuple>
 
 namespace armnn
 {
 
-static bool RunTosaLayerChecks(TosaSerializationOperator* op,
-                               const std::vector<TosaSerializationTensor*>& inputs,
-                               const std::vector<TosaSerializationTensor*>& outputs,
-                               const std::vector<Attribute>& supportedAttributes,
-                               const std::vector<DType>& supportedTypes,
-                               Optional<string&> reasonIfUnsupported)
+static bool RunTosaLayerChecksSingleDataType(TosaSerializationOperator* op,
+                                             const std::vector<TosaSerializationTensor*>& inputs,
+                                             const std::vector<TosaSerializationTensor*>& outputs,
+                                             const std::vector<Attribute>& supportedAttributes,
+                                             const std::vector<DType>& supportedTypes,
+                                             Optional<string&> reasonIfUnsupported)
 {
     bool supported = true;
 
-    std::string opCode = std::to_string(op->GetOp());
+    std::string opString = TosaOpToString(op->GetOp());
 
     // Check Attribute from operator (GetAttribute)
     supported &= CheckSupportRule(TosaOperatorAttributeOfAny(op, supportedAttributes), reasonIfUnsupported,
-                                  std::string("TOSA Reference Operator: " + opCode +
+                                  std::string("TOSA Reference Operator: " + opString +
                                               " has an unsupported attribute.").c_str());
 
     for (auto input : inputs)
@@ -40,14 +41,14 @@
         // Check Dtype from tensor (GetDtype)
         supported &= CheckSupportRule(TosaTypeAnyOf(input, supportedTypes),
                                       reasonIfUnsupported,
-                                      std::string("TOSA Reference Operator: " + opCode + " for input: " +
+                                      std::string("TOSA Reference Operator: " + opString + " for input: " +
                                                   input->GetName() + " has an unsupported data type: " +
                                                   dataTypeCode).c_str());
 
         // Check Shape from tensor (GetShape)
         supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(input),
                                       reasonIfUnsupported,
-                                      std::string("Tosa Reference Operator: " + opCode + " for input: " +
+                                      std::string("Tosa Reference Operator: " + opString + " for input: " +
                                                   input->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str());
     }
 
@@ -58,20 +59,72 @@
         // Check Dtype from tensor (GetDtype)
         supported &= CheckSupportRule(TosaTypeAnyOf(output, supportedTypes),
                                       reasonIfUnsupported,
-                                      std::string("TOSA Reference Operator: " + opCode + " for output: " +
+                                      std::string("TOSA Reference Operator: " + opString + " for output: " +
                                                   output->GetName() + " has an unsupported data type: " +
                                                   dataTypeCode).c_str());
 
         // Check Shape from tensor (GetShape)
         supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(output),
                                       reasonIfUnsupported,
-                                      std::string("Tosa Reference Operator: " + opCode + " for output: " +
+                                      std::string("Tosa Reference Operator: " + opString + " for output: " +
                                                   output->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str());
     }
 
     return supported;
 }
 
+static bool RunTosaLayerChecksInputOutputDataType(TosaSerializationOperator* op,
+                                                  const std::vector<TosaSerializationTensor*>& inputs,
+                                                  const std::vector<TosaSerializationTensor*>& outputs,
+                                                  const std::vector<Attribute>& supportedAttributes,
+                                                  const std::vector<std::tuple<DType,DType>>& supportedMappingTypes,
+                                                  Optional<string&> reasonIfUnsupported)
+{
+    bool supported = true;
+
+    std::string opString = TosaOpToString(op->GetOp());
+
+    // Check Attribute from operator (GetAttribute)
+    supported &= CheckSupportRule(TosaOperatorAttributeOfAny(op, supportedAttributes), reasonIfUnsupported,
+                                  std::string("TOSA Reference Operator: " + opString +
+                                      " has an unsupported attribute.").c_str());
+
+    supported &= CheckSupportRule(TosaAssertSize(inputs, outputs), reasonIfUnsupported,
+                                  std::string("TOSA Reference Operator: " + opString +
+                                      " must have 1-to-1 mapping of inputs-to-outputs.").c_str());
+
+    for (uint32_t i = 0; i < inputs.size(); i++)
+    {
+        auto input = inputs[i];
+        auto output = outputs[i];
+        std::string inputDataTypeCode = std::to_string(input->GetDtype());
+        std::string outputDataTypeCode = std::to_string(output->GetDtype());
+        std::tuple<DType, DType> mappingType(input->GetDtype(), output->GetDtype());
+
+        // Check Dtype from tensor (GetDtype)
+        supported &= CheckSupportRule(TosaContainerContains(mappingType, supportedMappingTypes),
+                                      reasonIfUnsupported,
+                                      std::string("TOSA Reference Operator: " + opString + " for input: " +
+                                          input->GetName() + " and output: " + output->GetName() +
+                                          " has an unsupported input data type: " + inputDataTypeCode +
+                                          " to output data type: " + outputDataTypeCode).c_str());
+
+        // Check Shape from tensor (GetShape)
+        supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(input),
+                                      reasonIfUnsupported,
+                                      std::string("Tosa Reference Operator: " + opString + " for input: " +
+                                          input->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str());
+
+        // Check Shape from tensor (GetShape)
+        supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(output),
+                                      reasonIfUnsupported,
+                                      std::string("Tosa Reference Operator: " + opString + " for output: " +
+                                          output->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str());
+    }
+
+    return supported;
+}
+
 static bool IsTosaLayerSupported(TosaSerializationOperator* op,
                                  const std::vector<TosaSerializationTensor*>& inputs,
                                  const std::vector<TosaSerializationTensor*>& outputs,
@@ -81,8 +134,6 @@
     {
         case tosa::Op_ADD:
         {
-            bool supported = true;
-
             std::vector<Attribute> supportedAttributes =
             {
                 Attribute_NONE
@@ -97,14 +148,84 @@
             };
 
             // Check the attribute, data types and bounds for inputs and outputs.
-            supported = RunTosaLayerChecks(op,
-                                           inputs,
-                                           outputs,
-                                           supportedAttributes,
-                                           supportedTypes,
-                                           reasonIfUnsupported);
+            return RunTosaLayerChecksSingleDataType(op,
+                                                    inputs,
+                                                    outputs,
+                                                    supportedAttributes,
+                                                    supportedTypes,
+                                                    reasonIfUnsupported);
+        }
+        case tosa::Op_AVG_POOL2D:
+        {
+            std::vector<Attribute> supportedAttributes =
+            {
+                Attribute_PoolAttribute
+            };
 
-            return supported;
+            std::vector<std::tuple<DType, DType>> supportedTypesMapping =
+            {
+                std::tuple<DType, DType>(DType_FP16, DType_FP16),
+                std::tuple<DType, DType>(DType_FP16, DType_FP32),
+                std::tuple<DType, DType>(DType_FP32, DType_FP32),
+                std::tuple<DType, DType>(DType_INT8, DType_INT32),
+                std::tuple<DType, DType>(DType_INT16, DType_INT32)
+            };
+
+            // Check the attribute, data types and bounds for inputs and outputs.
+            return RunTosaLayerChecksInputOutputDataType(op,
+                                                         inputs,
+                                                         outputs,
+                                                         supportedAttributes,
+                                                         supportedTypesMapping,
+                                                         reasonIfUnsupported);
+        }
+        case tosa::Op_MAX_POOL2D:
+        {
+            std::vector<Attribute> supportedAttributes =
+            {
+                Attribute_PoolAttribute
+            };
+
+            std::vector<DType> supportedTypes =
+            {
+                DType_FP16,
+                DType_FP32,
+                DType_INT8,
+                DType_INT16
+            };
+
+            // Check the attribute, data types and bounds for inputs and outputs.
+            return RunTosaLayerChecksSingleDataType(op,
+                                                    inputs,
+                                                    outputs,
+                                                    supportedAttributes,
+                                                    supportedTypes,
+                                                    reasonIfUnsupported);
+        }
+        case tosa::Op_PAD:
+        {
+            std::vector<Attribute> supportedAttributes =
+            {
+                Attribute_PadAttribute
+            };
+
+            std::vector<DType> supportedTypes =
+            {
+                DType_FP16,
+                DType_FP32,
+                DType_INT8,
+                DType_INT16,
+                DType_INT32,
+                DType_BOOL
+            };
+
+            // Check the attribute, data types and bounds for inputs and outputs.
+            return RunTosaLayerChecksSingleDataType(op,
+                                                    inputs,
+                                                    outputs,
+                                                    supportedAttributes,
+                                                    supportedTypes,
+                                                    reasonIfUnsupported);
         }
         default:
             SetValueChecked(reasonIfUnsupported, "Operation is currently unsupported by the TOSA Reference Backend.");
@@ -136,6 +257,11 @@
         case LayerType::Input:
         case LayerType::Output:
             return true;
+        case LayerType::Pooling2d:
+            // Setup inputs and outputs
+            inputInfos.push_back(&infos[0]);
+            outputInfos.push_back(&infos[1]);
+            break;
         default:
             break;
     }
diff --git a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
index 54d6db6..fbe1265 100644
--- a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
+++ b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
@@ -6,27 +6,60 @@
 #include "backendsCommon/test/EndToEndTestImpl.hpp"
 
 #include "backendsCommon/test/AdditionEndToEndTestImpl.hpp"
+#include "backendsCommon/test/Pooling2dEndToEndTestImpl.hpp"
 
 #include <doctest/doctest.h>
 
 TEST_SUITE("TosaRefEndToEnd")
 {
-std::vector<armnn::BackendId> tosaDefaultBackends = { "TosaRef" };
+std::vector<BackendId> tosaDefaultBackends = { "TosaRef" };
 
 // Addition
-TEST_CASE("TosaRefEndtoEndTestFloat32")
+TEST_CASE("TosaRefAdditionEndtoEndTestFloat32")
 {
-    AdditionEndToEnd<armnn::DataType::Float32>(tosaDefaultBackends);
+    AdditionEndToEnd<DataType::Float32>(tosaDefaultBackends);
 }
 
-TEST_CASE("TosaRefEndtoEndTestInt32")
+TEST_CASE("TosaRefAdditionEndtoEndTestInt32")
 {
-    AdditionEndToEnd<armnn::DataType::Signed32>(tosaDefaultBackends);
+    AdditionEndToEnd<DataType::Signed32>(tosaDefaultBackends);
 }
 
-TEST_CASE("TosaRefEndtoEndTestFloat16")
+TEST_CASE("TosaRefAdditionEndtoEndTestFloat16")
 {
-    AdditionEndToEndFloat16<armnn::DataType::Float16>(tosaDefaultBackends);
+    AdditionEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
+// Max Pool 2D
+TEST_CASE("TosaRefMaxPool2DEndtoEndTestFloat32")
+{
+    MaxPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefMaxPool2DEndtoEndTestFloat16")
+{
+    MaxPool2dEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefMaxPool2DIgnoreValueEndtoEndTestFloat32")
+{
+    MaxPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends, PaddingMethod::IgnoreValue);
+}
+
+// Average Pool 2D
+TEST_CASE("TosaRefAvgPool2DEndtoEndTestFloat32")
+{
+    AvgPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefAvgPool2DEndtoEndTestFloat16")
+{
+    AvgPool2dEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefAvgPool2DIgnoreValueEndtoEndTestFloat32")
+{
+    AvgPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends, PaddingMethod::IgnoreValue);
 }
 
 }
\ No newline at end of file
diff --git a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
index 47f3138..48eca34 100644
--- a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
+++ b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
@@ -57,9 +57,131 @@
                                                      reasonIfNotSupported);
 
     CHECK(!supported);
-    REQUIRE(reasonIfNotSupported.find("TOSA Reference Operator: 14 for input: Op_ADD_input0_") != std::string::npos);
-    REQUIRE(reasonIfNotSupported.find("TOSA Reference Operator: 14 for input: Op_ADD_input1_") != std::string::npos);
-    REQUIRE(reasonIfNotSupported.find("TOSA Reference Operator: 14 for output: Op_ADD_output0_") != std::string::npos);
+    REQUIRE(reasonIfNotSupported.find(
+        "TOSA Reference Operator: Op_ADD for input: Op_ADD_input0_") != std::string::npos);
+    REQUIRE(reasonIfNotSupported.find(
+        "TOSA Reference Operator: Op_ADD for input: Op_ADD_input1_") != std::string::npos);
+    REQUIRE(reasonIfNotSupported.find(
+        "TOSA Reference Operator: Op_ADD for output: Op_ADD_output0_") != std::string::npos);
+}
+
+TEST_CASE("IsLayerSupportedTosaReferenceMaxPooling2d")
+{
+    armnn::TensorShape inShape = {1,1,3,4};
+    armnn::TensorShape outShape = {1,1,3,4};
+    armnn::TensorInfo in(inShape, armnn::DataType::Float32);
+    armnn::TensorInfo out(outShape, armnn::DataType::Float32);
+
+    armnn::Pooling2dDescriptor desc;
+    armnn::TosaRefLayerSupport supportChecker;
+    std::string reasonIfNotSupported;
+    auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d,
+                                                     {in, out},
+                                                     desc,
+                                                     armnn::EmptyOptional(),
+                                                     armnn::EmptyOptional(),
+                                                     reasonIfNotSupported);
+
+    CHECK(supported);
+}
+
+TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2d_IgnoreValue")
+{
+    armnn::TensorShape inShape = {1,1,3,4};
+    armnn::TensorShape outShape = {1,1,3,4};
+    armnn::TensorInfo in(inShape, armnn::DataType::Float32);
+    armnn::TensorInfo out(outShape, armnn::DataType::Float32);
+
+    armnn::Pooling2dDescriptor desc;
+    desc.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
+    desc.m_PoolType = armnn::PoolingAlgorithm::Average;
+
+    armnn::TosaRefLayerSupport supportChecker;
+    std::string reasonIfNotSupported;
+    auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d,
+                                                     {in, out},
+                                                     desc,
+                                                     armnn::EmptyOptional(),
+                                                     armnn::EmptyOptional(),
+                                                     reasonIfNotSupported);
+
+    CHECK(supported);
+}
+
+TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2d_InputOutputDatatypeDifferent")
+{
+    armnn::TensorShape inShape = {1,1,3,4};
+    armnn::TensorShape outShape = {1,1,3,4};
+    armnn::TensorInfo in(inShape, armnn::DataType::QAsymmS8);
+    armnn::TensorInfo out(outShape, armnn::DataType::Signed32);
+
+    armnn::Pooling2dDescriptor desc;
+    desc.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
+    desc.m_PoolType = armnn::PoolingAlgorithm::Average;
+
+    armnn::TosaRefLayerSupport supportChecker;
+    std::string reasonIfNotSupported;
+    auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d,
+                                                     {in, out},
+                                                     desc,
+                                                     armnn::EmptyOptional(),
+                                                     armnn::EmptyOptional(),
+                                                     reasonIfNotSupported);
+
+    CHECK(supported);
+}
+
+TEST_CASE("IsLayerSupportedTosaReferenceMaxPooling2dUnsupported")
+{
+    armnn::TensorShape inShape = {1,1,3,4};
+    armnn::TensorShape outShape = {1,1,3,4};
+    armnn::TensorInfo in(inShape, armnn::DataType::Signed64);
+    armnn::TensorInfo out(outShape, armnn::DataType::Signed64);
+
+    armnn::Pooling2dDescriptor desc;
+    armnn::TosaRefLayerSupport supportChecker;
+    std::string reasonIfNotSupported;
+    auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d,
+                                                     {in, out},
+                                                     desc,
+                                                     armnn::EmptyOptional(),
+                                                     armnn::EmptyOptional(),
+                                                     reasonIfNotSupported);
+
+    CHECK(!supported);
+    REQUIRE(reasonIfNotSupported.find(
+        "TOSA Reference Operator: Op_MAX_POOL2D for input: Op_MAX_POOL2D_input0_") != std::string::npos);
+    REQUIRE(reasonIfNotSupported.find(
+        "TOSA Reference Operator: Op_MAX_POOL2D for output: Op_MAX_POOL2D_output0_") != std::string::npos);
+}
+
+TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2dUnsupported_InputOutputDatatypeDifferent")
+{
+    armnn::TensorShape inShape = {1,1,3,4};
+    armnn::TensorShape outShape = {1,1,3,4};
+    armnn::TensorInfo in(inShape, armnn::DataType::Float32);
+    armnn::TensorInfo out(outShape, armnn::DataType::Float16);
+
+    armnn::Pooling2dDescriptor desc;
+    desc.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
+    desc.m_PoolType = armnn::PoolingAlgorithm::Average;
+
+    armnn::TosaRefLayerSupport supportChecker;
+    std::string reasonIfNotSupported;
+    auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d,
+                                                     {in, out},
+                                                     desc,
+                                                     armnn::EmptyOptional(),
+                                                     armnn::EmptyOptional(),
+                                                     reasonIfNotSupported);
+
+    CHECK(!supported);
+    REQUIRE(reasonIfNotSupported.find(
+        "TOSA Reference Operator: Op_AVG_POOL2D for input: Op_PAD_intermediate0_") != std::string::npos);
+    REQUIRE(reasonIfNotSupported.find(
+        " and output: Op_AVG_POOL2D_output0_") != std::string::npos);
+    REQUIRE(reasonIfNotSupported.find(
+        " has an unsupported input data type: 8 to output data type: 10") != std::string::npos);
 }
 
 }