IVGCVSW-3320 Add reference workload support for TransposeConvolution2dLayer

Signed-off-by: Aron Virginas-Tar <Aron.Virginas-Tar@arm.com>
Change-Id: Icc64f8148c9d8a0d14d772e6e4e7865e70585cd9
diff --git a/src/backends/backendsCommon/test/CommonTestUtils.cpp b/src/backends/backendsCommon/test/CommonTestUtils.cpp
index 950b939..80512e2 100644
--- a/src/backends/backendsCommon/test/CommonTestUtils.cpp
+++ b/src/backends/backendsCommon/test/CommonTestUtils.cpp
@@ -50,3 +50,21 @@
 
     return backendObjPtr;
 }
+
+armnn::TensorShape MakeTensorShape(unsigned int batches,
+                                   unsigned int channels,
+                                   unsigned int height,
+                                   unsigned int width,
+                                   armnn::DataLayout layout)
+{
+    using namespace armnn;
+    switch (layout)
+    {
+        case DataLayout::NCHW:
+            return TensorShape{ batches, channels, height, width };
+        case DataLayout::NHWC:
+            return TensorShape{ batches, height, width, channels };
+        default:
+            throw InvalidArgumentException(std::string("Unsupported data layout: ") + GetDataLayoutName(layout));
+    }
+}
diff --git a/src/backends/backendsCommon/test/CommonTestUtils.hpp b/src/backends/backendsCommon/test/CommonTestUtils.hpp
index 03c9755..58bd6b1 100644
--- a/src/backends/backendsCommon/test/CommonTestUtils.hpp
+++ b/src/backends/backendsCommon/test/CommonTestUtils.hpp
@@ -68,3 +68,9 @@
                                                             armnn::SubgraphView::Layers&& layers);
 
 armnn::IBackendInternalUniquePtr CreateBackendObject(const armnn::BackendId& backendId);
+
+armnn::TensorShape MakeTensorShape(unsigned int batches,
+                                   unsigned int channels,
+                                   unsigned int height,
+                                   unsigned int width,
+                                   armnn::DataLayout layout);
\ No newline at end of file
diff --git a/src/backends/backendsCommon/test/LayerTests.cpp b/src/backends/backendsCommon/test/LayerTests.cpp
index a625097..ca39438 100644
--- a/src/backends/backendsCommon/test/LayerTests.cpp
+++ b/src/backends/backendsCommon/test/LayerTests.cpp
@@ -45,6 +45,7 @@
 #include "DebugTestImpl.hpp"
 #include "DequantizeTestImpl.hpp"
 #include "QuantizeTestImpl.hpp"
+#include "TransposeConvolution2dTestImpl.hpp"
 
 // 3-channel 16x8 image used as common input data for a number of Conv2d tests.
 static std::vector<float> ConvInput3x8x16({
@@ -9643,3 +9644,409 @@
 {
     return QuantizeClampTest<armnn::DataType::QuantisedSymm16>(workloadFactory, memoryManager);
 }
+
+//
+// TransposeConvolution2d
+//
+
+// Simple biased
+LayerTestResult<float, 4> SimpleTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<float, 4> SimpleTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<uint8_t, 4> SimpleTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<uint8_t, 4> SimpleTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<int16_t, 4> SimpleTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<int16_t, 4> SimpleTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+// Simple unbiased
+LayerTestResult<float, 4> UnbiasedSimpleTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<float, 4> UnbiasedSimpleTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<uint8_t, 4> UnbiasedSimpleTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<uint8_t, 4> UnbiasedSimpleTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<int16_t, 4> UnbiasedSimpleTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<int16_t, 4> UnbiasedSimpleTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return SimpleTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+// Padded biased
+LayerTestResult<float, 4> PaddedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<float, 4> PaddedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<uint8_t, 4> PaddedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<uint8_t, 4> PaddedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<int16_t, 4> PaddedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<int16_t, 4> PaddedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+// Padded unbiased
+LayerTestResult<float, 4> UnbiasedPaddedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<float, 4> UnbiasedPaddedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<uint8_t, 4> UnbiasedPaddedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<uint8_t, 4> UnbiasedPaddedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<int16_t, 4> UnbiasedPaddedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<int16_t, 4> UnbiasedPaddedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return PaddedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+// Strided biased
+LayerTestResult<float, 4> StridedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<float, 4> StridedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<uint8_t, 4> StridedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<uint8_t, 4> StridedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<int16_t, 4> StridedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<int16_t, 4> StridedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        true,
+        armnn::DataLayout::NHWC);
+}
+
+// Strided unbiased
+LayerTestResult<float, 4> UnbiasedStridedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<float, 4> UnbiasedStridedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<uint8_t, 4> UnbiasedStridedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<uint8_t, 4> UnbiasedStridedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedAsymm8, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
+
+LayerTestResult<int16_t, 4> UnbiasedStridedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NCHW);
+}
+
+LayerTestResult<int16_t, 4> UnbiasedStridedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return StridedTransposeConvolution2dTestImpl<armnn::DataType::QuantisedSymm16, armnn::DataType::Signed32>(
+        workloadFactory,
+        memoryManager,
+        false,
+        armnn::DataLayout::NHWC);
+}
\ No newline at end of file
diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp
index 10bc00f..b225e4d 100644
--- a/src/backends/backendsCommon/test/LayerTests.hpp
+++ b/src/backends/backendsCommon/test/LayerTests.hpp
@@ -3760,3 +3760,157 @@
 PreluTest<armnn::DataType::QuantisedSymm16>(
         armnn::IWorkloadFactory& workloadFactory,
         const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+//
+// TransposeConvolution2d
+//
+
+// Simple biased
+LayerTestResult<float, 4> SimpleTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> SimpleTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> SimpleTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> SimpleTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> SimpleTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> SimpleTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+// Simple unbiased
+LayerTestResult<float, 4> UnbiasedSimpleTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> UnbiasedSimpleTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> UnbiasedSimpleTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> UnbiasedSimpleTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> UnbiasedSimpleTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> UnbiasedSimpleTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+// Padded biased
+LayerTestResult<float, 4> PaddedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> PaddedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> PaddedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> PaddedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> PaddedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> PaddedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+// Padded unbiased
+LayerTestResult<float, 4> UnbiasedPaddedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> UnbiasedPaddedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> UnbiasedPaddedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> UnbiasedPaddedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> UnbiasedPaddedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> UnbiasedPaddedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+// Strided biased
+LayerTestResult<float, 4> StridedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> StridedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> StridedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> StridedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> StridedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> StridedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+// Strided unbiased
+LayerTestResult<float, 4> UnbiasedStridedTransposeConvolution2dFloatNchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> UnbiasedStridedTransposeConvolution2dFloatNhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> UnbiasedStridedTransposeConvolution2dUint8NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<uint8_t, 4> UnbiasedStridedTransposeConvolution2dUint8NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> UnbiasedStridedTransposeConvolution2dInt16NchwTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<int16_t, 4> UnbiasedStridedTransposeConvolution2dInt16NhwcTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
\ No newline at end of file
diff --git a/src/backends/backendsCommon/test/TransposeConvolution2dTestImpl.hpp b/src/backends/backendsCommon/test/TransposeConvolution2dTestImpl.hpp
new file mode 100644
index 0000000..3bbd5d6
--- /dev/null
+++ b/src/backends/backendsCommon/test/TransposeConvolution2dTestImpl.hpp
@@ -0,0 +1,498 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include "QuantizeHelper.hpp"
+
+#include <armnn/ArmNN.hpp>
+
+#include <ResolveType.hpp>
+
+#include <backendsCommon/CpuTensorHandle.hpp>
+#include <backendsCommon/test/CommonTestUtils.hpp>
+#include <backendsCommon/test/TensorCopyUtils.hpp>
+#include <backendsCommon/test/WorkloadTestUtils.hpp>
+
+#include <reference/RefWorkloadFactory.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace
+{
+
+template<typename T>
+using TensorData = std::pair<armnn::TensorInfo, std::vector<T>>;
+
+template<typename T>
+void VerifyInputTensorData(const TensorData<T>& data, const std::string& tensorName)
+{
+    if (data.first.GetNumElements() > data.second.size())
+    {
+        throw armnn::InvalidArgumentException("Size of data too small for " + tensorName + ": expected " +
+            std::to_string(data.first.GetNumElements()) + "but got " + std::to_string(data.second.size()));
+    }
+}
+
+template<typename T, typename BT>
+void TransposeConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
+                                    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+                                    const armnn::TransposeConvolution2dDescriptor& descriptor,
+                                    const TensorData<T>& input,
+                                    TensorData<T>& output,
+                                    const TensorData<T>& weights,
+                                    const armnn::Optional<TensorData<BT>>& biases)
+{
+    using namespace armnn;
+
+    VerifyInputTensorData(input, "input");
+    VerifyInputTensorData(weights, "biases");
+
+    if (descriptor.m_BiasEnabled)
+    {
+        if (!biases.has_value())
+        {
+            throw InvalidArgumentException("Bias enabled but no bias data provided");
+        }
+        VerifyInputTensorData(biases.value(), "biases");
+    }
+
+    // set up weights
+    ScopedCpuTensorHandle weightsTensor(weights.first);
+
+    TransposeConvolution2dQueueDescriptor queueDescriptor;
+    queueDescriptor.m_Parameters = descriptor;
+    queueDescriptor.m_Weight     = &weightsTensor;
+
+    AllocateAndCopyDataToITensorHandle(&weightsTensor, weights.second.data());
+
+    std::unique_ptr<ScopedCpuTensorHandle> biasesTensor;
+    if (descriptor.m_BiasEnabled)
+    {
+        // set up biases
+        biasesTensor = std::make_unique<ScopedCpuTensorHandle>(biases.value().first);
+        queueDescriptor.m_Bias = biasesTensor.get();
+
+        AllocateAndCopyDataToITensorHandle(biasesTensor.get(), biases.value().second.data());
+    }
+
+    // set up input and output handles
+    std::unique_ptr<ITensorHandle> inputHandle  = workloadFactory.CreateTensorHandle(input.first);
+    std::unique_ptr<ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(output.first);
+
+    // set up workload
+    armnn::WorkloadInfo workloadInfo;
+    AddInputToWorkload(queueDescriptor, workloadInfo, input.first, inputHandle.get());
+    AddOutputToWorkload(queueDescriptor, workloadInfo, output.first, outputHandle.get());
+
+    std::unique_ptr<armnn::IWorkload> workload =
+            workloadFactory.CreateTransposeConvolution2d(queueDescriptor, workloadInfo);
+
+    inputHandle->Allocate();
+    outputHandle->Allocate();
+
+    CopyDataToITensorHandle(inputHandle.get(), input.second.data());
+
+    ExecuteWorkload(*workload, nullptr);
+
+    // copy output
+    output.second = std::vector<T>(output.first.GetNumElements(), 0.0f);
+    CopyDataFromITensorHandle(output.second.data(), outputHandle.get());
+}
+
+template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 4> TransposeConvolution2dTestImpl(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+    const armnn::TransposeConvolution2dDescriptor& descriptor,
+    armnn::TensorInfo& inputInfo,
+    const std::vector<float>& inputData,
+    armnn::TensorInfo& outputInfo,
+    const std::vector<float>& expectedOutputData,
+    armnn::TensorInfo& weightsInfo,
+    const std::vector<float>& weightsData,
+    armnn::TensorInfo& biasesInfo,
+    const std::vector<float>& biasesData)
+{
+    using namespace armnn;
+
+    // set up quantization parameters
+    if (armnn::IsQuantizedType<T>())
+    {
+        constexpr float   qScale  = 0.25f;
+        constexpr int32_t qOffset = 50;
+
+        inputInfo.SetQuantizationScale(qScale);
+        inputInfo.SetQuantizationOffset(qOffset);
+
+        outputInfo.SetQuantizationScale(qScale);
+        outputInfo.SetQuantizationOffset(qOffset);
+
+        weightsInfo.SetQuantizationScale(qScale);
+        weightsInfo.SetQuantizationOffset(qOffset);
+
+        biasesInfo.SetQuantizationScale(qScale * qScale);
+        biasesInfo.SetQuantizationOffset(0);
+    }
+
+    // set up input
+    TensorData<T> input =
+    {
+        inputInfo,
+        QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), inputData)
+    };
+
+    // set up weights
+    TensorData<T> weights =
+    {
+        weightsInfo,
+        QuantizedVector<T>(weightsInfo.GetQuantizationScale(), weightsInfo.GetQuantizationOffset(), weightsData)
+    };
+
+    // set up biases
+    using BT = armnn::ResolveType<ArmnnBType>;
+    Optional<TensorData<BT>> optionalBiases;
+    if (descriptor.m_BiasEnabled)
+    {
+        TensorData<BT> biases =
+        {
+            biasesInfo,
+            QuantizedVector<BT>(biasesInfo.GetQuantizationScale(), biasesInfo.GetQuantizationOffset(), biasesData)
+        };
+
+        optionalBiases = Optional<TensorData<BT>>(biases);
+    }
+
+    // set up output
+    TensorData<T> output = { outputInfo, {} };
+
+    // execute test
+    TransposeConvolution2dTestImpl(workloadFactory,
+                                   memoryManager,
+                                   descriptor,
+                                   input,
+                                   output,
+                                   weights,
+                                   optionalBiases);
+
+    // construct result object
+    LayerTestResult<T, 4> testResult(outputInfo);
+    testResult.output         = MakeTensor<T, 4>(outputInfo, output.second);
+    testResult.outputExpected = MakeTensor<T, 4>(outputInfo,
+                                                 QuantizedVector<T>(outputInfo.GetQuantizationScale(),
+                                                                    outputInfo.GetQuantizationOffset(),
+                                                                    expectedOutputData));
+
+    return testResult;
+}
+
+} // anonymous namespace
+
+template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 4> SimpleTransposeConvolution2dTestImpl(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+    bool biasEnabled,
+    const armnn::DataLayout layout)
+{
+    using namespace armnn;
+
+    constexpr unsigned int batches  = 1u;
+    constexpr unsigned int channels = 1u;
+
+    constexpr unsigned int wInput = 3u;
+    constexpr unsigned int hInput = wInput;
+
+    constexpr unsigned int wOutput = 5u;
+    constexpr unsigned int hOutput = wOutput;
+
+    constexpr unsigned int wWeights = 3u;
+    constexpr unsigned int hWeights = wWeights;
+
+    TensorShape inputShape   = MakeTensorShape(batches, channels, hInput, wInput, layout);
+    TensorShape outputShape  = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
+    TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
+
+    TensorInfo inputInfo(inputShape, ArmnnType);
+    TensorInfo outputInfo(outputShape, ArmnnType);
+    TensorInfo weightsInfo(weightsShape, ArmnnType);
+    TensorInfo biasesInfo({ channels }, ArmnnBType);
+
+    std::vector<float> inputData =
+    {
+       1.f, 1.f, 1.f,
+       1.f, 1.f, 1.f,
+       1.f, 1.f, 1.f
+    };
+
+    std::vector<float> weightsData =
+    {
+        1.f, 2.f, 3.f,
+        4.f, 5.f, 6.f,
+        7.f, 8.f, 9.f
+    };
+
+    std::vector<float> biasesData = { 1.f };
+
+    std::vector<float> expectedOutputData =
+    {
+         1.f,  3.f,  6.f,  5.f,  3.f,
+         5.f, 12.f, 21.f, 16.f,  9.f,
+        12.f, 27.f, 45.f, 33.f, 18.f,
+        11.f, 24.f, 39.f, 28.f, 15.f,
+         7.f, 15.f, 24.f, 17.f,  9.f
+    };
+
+    if (biasEnabled)
+    {
+        // apply bias to expected output data
+        std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
+                       [&](float f) -> float { return f + biasesData[0]; });
+    }
+
+    TransposeConvolution2dDescriptor descriptor;
+    descriptor.m_StrideX     = 1;
+    descriptor.m_StrideY     = 1;
+    descriptor.m_BiasEnabled = biasEnabled;
+    descriptor.m_DataLayout  = layout;
+
+    // swizzle data if needed
+    if (layout == armnn::DataLayout::NHWC)
+    {
+        constexpr size_t dataTypeSize = sizeof(float);
+        const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
+
+        std::vector<float> tmp(inputData.size());
+        armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
+        inputData = tmp;
+
+        tmp.resize(weightsData.size());
+        armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
+        weightsData = tmp;
+
+        tmp.resize(expectedOutputData.size());
+        armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
+        expectedOutputData = tmp;
+    }
+
+    return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
+                                                                 memoryManager,
+                                                                 descriptor,
+                                                                 inputInfo,
+                                                                 inputData,
+                                                                 outputInfo,
+                                                                 expectedOutputData,
+                                                                 weightsInfo,
+                                                                 weightsData,
+                                                                 biasesInfo,
+                                                                 biasesData);
+}
+
+template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 4> PaddedTransposeConvolution2dTestImpl(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+    bool biasEnabled,
+    const armnn::DataLayout layout)
+{
+    using namespace armnn;
+
+    constexpr unsigned int batches  = 1u;
+    constexpr unsigned int channels = 1u;
+
+    constexpr unsigned int wInput = 4u;
+    constexpr unsigned int hInput = wInput;
+
+    constexpr unsigned int wOutput = 2u;
+    constexpr unsigned int hOutput = wOutput;
+
+    constexpr unsigned int wWeights = 3u;
+    constexpr unsigned int hWeights = wWeights;
+
+    TensorShape inputShape   = MakeTensorShape(batches, channels, hInput, wInput, layout);
+    TensorShape outputShape  = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
+    TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
+
+    TensorInfo inputInfo(inputShape, ArmnnType);
+    TensorInfo outputInfo(outputShape, ArmnnType);
+    TensorInfo weightsInfo(weightsShape, ArmnnType);
+    TensorInfo biasesInfo({ channels }, ArmnnBType);
+
+    std::vector<float> inputData =
+    {
+       1.f, 3.f, 2.f, 1.f,
+       1.f, 3.f, 3.f, 1.f,
+       2.f, 1.f, 1.f, 3.f,
+       3.f, 2.f, 3.f, 3.f
+    };
+
+    std::vector<float> weightsData =
+    {
+        1.f, 2.f, 3.f,
+        0.f, 1.f, 0.f,
+        2.f, 1.f, 2.f
+    };
+
+    std::vector<float> biasesData = { 1.f };
+
+    std::vector<float> expectedOutputData =
+    {
+         21.f, 21.f,
+         28.f, 27.f
+    };
+
+    if (biasEnabled)
+    {
+        // apply bias to expected output data
+        std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
+                       [&](float f) -> float { return f + biasesData[0]; });
+    }
+
+    TransposeConvolution2dDescriptor descriptor;
+    descriptor.m_PadLeft     = 2;
+    descriptor.m_PadRight    = 2;
+    descriptor.m_PadTop      = 2;
+    descriptor.m_PadBottom   = 2;
+    descriptor.m_StrideX     = 1;
+    descriptor.m_StrideY     = 1;
+    descriptor.m_BiasEnabled = biasEnabled;
+    descriptor.m_DataLayout  = layout;
+
+    // swizzle data if needed
+    if (layout == armnn::DataLayout::NHWC)
+    {
+        constexpr size_t dataTypeSize = sizeof(float);
+        const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
+
+        std::vector<float> tmp(inputData.size());
+        armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
+        inputData = tmp;
+
+        tmp.resize(weightsData.size());
+        armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
+        weightsData = tmp;
+
+        tmp.resize(expectedOutputData.size());
+        armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
+        expectedOutputData = tmp;
+    }
+
+    return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
+                                                                 memoryManager,
+                                                                 descriptor,
+                                                                 inputInfo,
+                                                                 inputData,
+                                                                 outputInfo,
+                                                                 expectedOutputData,
+                                                                 weightsInfo,
+                                                                 weightsData,
+                                                                 biasesInfo,
+                                                                 biasesData);
+}
+
+ template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
+ LayerTestResult<T, 4> StridedTransposeConvolution2dTestImpl(
+     armnn::IWorkloadFactory& workloadFactory,
+     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+     bool biasEnabled,
+     const armnn::DataLayout layout)
+{
+    using namespace armnn;
+
+    constexpr unsigned int batches  = 1u;
+    constexpr unsigned int channels = 1u;
+
+    constexpr unsigned int wInput = 3u;
+    constexpr unsigned int hInput = wInput;
+
+    constexpr unsigned int wOutput = 7u;
+    constexpr unsigned int hOutput = wOutput;
+
+    constexpr unsigned int wWeights = 3u;
+    constexpr unsigned int hWeights = wWeights;
+
+    TensorShape inputShape   = MakeTensorShape(batches, channels, hInput, wInput, layout);
+    TensorShape outputShape  = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
+    TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
+
+    TensorInfo inputInfo(inputShape, ArmnnType);
+    TensorInfo outputInfo(outputShape, ArmnnType);
+    TensorInfo weightsInfo(weightsShape, ArmnnType);
+    TensorInfo biasesInfo({ channels }, ArmnnBType);
+
+    std::vector<float> inputData =
+    {
+        1.f, 1.f, 1.f,
+        1.f, 1.f, 1.f,
+        1.f, 1.f, 1.f
+    };
+
+    std::vector<float> weightsData =
+    {
+        1.f, 2.f, 3.f,
+        4.f, 5.f, 6.f,
+        7.f, 8.f, 9.f
+    };
+
+    std::vector<float> biasesData = { 1.f };
+
+    std::vector<float> expectedOutputData =
+    {
+        1.f,  2.f,  4.f,  2.f,  4.f,  2.f,  3.f,
+        4.f,  5.f, 10.f,  5.f, 10.f,  5.f,  6.f,
+        8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
+        4.f,  5.f, 10.f,  5.f, 10.f,  5.f,  6.f,
+        8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
+        4.f,  5.f, 10.f,  5.f, 10.f,  5.f,  6.f,
+        7.f,  8.f, 16.f,  8.f, 16.f,  8.f,  9.f
+    };
+
+    if (biasEnabled)
+    {
+        // apply bias to expected output data
+        std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
+                    [&](float f) -> float { return f + biasesData[0]; });
+    }
+
+    TransposeConvolution2dDescriptor descriptor;
+    descriptor.m_StrideX     = 2;
+    descriptor.m_StrideY     = 2;
+    descriptor.m_BiasEnabled = biasEnabled;
+    descriptor.m_DataLayout  = layout;
+
+    // swizzle data if needed
+    if (layout == armnn::DataLayout::NHWC)
+    {
+        constexpr size_t dataTypeSize = sizeof(float);
+        const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
+
+        std::vector<float> tmp(inputData.size());
+        armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
+        inputData = tmp;
+
+        tmp.resize(weightsData.size());
+        armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
+        weightsData = tmp;
+
+        tmp.resize(expectedOutputData.size());
+        armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
+        expectedOutputData = tmp;
+    }
+
+    return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
+                                                                memoryManager,
+                                                                descriptor,
+                                                                inputInfo,
+                                                                inputData,
+                                                                outputInfo,
+                                                                expectedOutputData,
+                                                                weightsInfo,
+                                                                weightsData,
+                                                                biasesInfo,
+                                                                biasesData);
+}
\ No newline at end of file
diff --git a/src/backends/reference/CMakeLists.txt b/src/backends/reference/CMakeLists.txt
index 82880cf..ff16f18 100644
--- a/src/backends/reference/CMakeLists.txt
+++ b/src/backends/reference/CMakeLists.txt
@@ -11,7 +11,6 @@
     RefLayerSupport.hpp
     RefWorkloadFactory.cpp
     RefWorkloadFactory.hpp
-
 )
 
 add_library(armnnRefBackend OBJECT ${armnnRefBackend_sources})
diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp
index 035a28b..5ede8b3 100644
--- a/src/backends/reference/RefWorkloadFactory.cpp
+++ b/src/backends/reference/RefWorkloadFactory.cpp
@@ -464,4 +464,11 @@
     return std::make_unique<RefPreluWorkload>(descriptor, info);
 }
 
+std::unique_ptr<IWorkload> RefWorkloadFactory::CreateTransposeConvolution2d(
+    const TransposeConvolution2dQueueDescriptor& descriptor,
+    const WorkloadInfo& info) const
+{
+    return std::make_unique<RefTransposeConvolution2dWorkload>(descriptor, info);
+}
+
 } // namespace armnn
diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp
index ed513e1..44cb079 100644
--- a/src/backends/reference/RefWorkloadFactory.hpp
+++ b/src/backends/reference/RefWorkloadFactory.hpp
@@ -193,6 +193,9 @@
     std::unique_ptr<IWorkload> CreatePrelu(const PreluQueueDescriptor& descriptor,
                                            const WorkloadInfo& info) const override;
 
+    std::unique_ptr<IWorkload> CreateTransposeConvolution2d(const TransposeConvolution2dQueueDescriptor& descriptor,
+                                                            const WorkloadInfo& info) const override;
+
 private:
 
     template <typename F32Workload, typename U8Workload, typename QueueDescriptorType>
diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk
index 849d87c..6fb17b5 100644
--- a/src/backends/reference/backend.mk
+++ b/src/backends/reference/backend.mk
@@ -60,6 +60,7 @@
         workloads/RefSpaceToDepthWorkload.cpp \
         workloads/RefStridedSliceWorkload.cpp \
         workloads/RefSplitterWorkload.cpp \
+        workloads/RefTransposeConvolution2dWorkload.cpp \
         workloads/ResizeBilinear.cpp \
         workloads/Rsqrt.cpp \
         workloads/SpaceToBatchNd.cpp \
@@ -67,7 +68,8 @@
         workloads/StridedSlice.cpp \
         workloads/StringMapping.cpp \
         workloads/Softmax.cpp \
-        workloads/Splitter.cpp
+        workloads/Splitter.cpp \
+        workloads/TransposeConvolution2d.cpp
 
 # BACKEND_TEST_SOURCES contains the list of files to be included
 # in the Android unit test build (armnn-tests) and it is picked
diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp
index b997a14..3556df1 100644
--- a/src/backends/reference/test/RefLayerTests.cpp
+++ b/src/backends/reference/test/RefLayerTests.cpp
@@ -10,6 +10,7 @@
 
 #include <reference/RefWorkloadFactory.hpp>
 #include <backendsCommon/test/DetectionPostProcessLayerTestImpl.hpp>
+#include <backendsCommon/test/TransposeConvolution2dTestImpl.hpp>
 #include <backendsCommon/test/LayerTests.hpp>
 
 #include <boost/test/unit_test.hpp>
@@ -966,4 +967,47 @@
 ARMNN_AUTO_TEST_CASE(PreluUint8,   PreluTest<armnn::DataType::QuantisedAsymm8>)
 ARMNN_AUTO_TEST_CASE(PreluInt16,   PreluTest<armnn::DataType::QuantisedSymm16>)
 
+// TransposeConvolution2d
+ARMNN_AUTO_TEST_CASE(SimpleTransposeConvolution2dFloatNchw, SimpleTransposeConvolution2dFloatNchwTest)
+ARMNN_AUTO_TEST_CASE(SimpleTransposeConvolution2dFloatNhwc, SimpleTransposeConvolution2dFloatNhwcTest)
+ARMNN_AUTO_TEST_CASE(SimpleTransposeConvolution2dUint8Nchw, SimpleTransposeConvolution2dUint8NchwTest)
+ARMNN_AUTO_TEST_CASE(SimpleTransposeConvolution2dUint8Nhwc, SimpleTransposeConvolution2dUint8NhwcTest)
+ARMNN_AUTO_TEST_CASE(SimpleTransposeConvolution2dInt16Nchw, SimpleTransposeConvolution2dInt16NchwTest)
+ARMNN_AUTO_TEST_CASE(SimpleTransposeConvolution2dInt16Nhwc, SimpleTransposeConvolution2dInt16NhwcTest)
+
+ARMNN_AUTO_TEST_CASE(UnbiasedSimpleTransposeConvolution2dFloatNchw, UnbiasedSimpleTransposeConvolution2dFloatNchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedSimpleTransposeConvolution2dFloatNhwc, UnbiasedSimpleTransposeConvolution2dFloatNhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedSimpleTransposeConvolution2dUint8Nchw, UnbiasedSimpleTransposeConvolution2dUint8NchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedSimpleTransposeConvolution2dUint8Nhwc, UnbiasedSimpleTransposeConvolution2dUint8NhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedSimpleTransposeConvolution2dInt16Nchw, UnbiasedSimpleTransposeConvolution2dInt16NchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedSimpleTransposeConvolution2dInt16Nhwc, UnbiasedSimpleTransposeConvolution2dInt16NhwcTest)
+
+ARMNN_AUTO_TEST_CASE(PaddedTransposeConvolution2dFloatNchw, PaddedTransposeConvolution2dFloatNchwTest)
+ARMNN_AUTO_TEST_CASE(PaddedTransposeConvolution2dFloatNhwc, PaddedTransposeConvolution2dFloatNhwcTest)
+ARMNN_AUTO_TEST_CASE(PaddedTransposeConvolution2dUint8Nchw, PaddedTransposeConvolution2dUint8NchwTest)
+ARMNN_AUTO_TEST_CASE(PaddedTransposeConvolution2dUint8Nhwc, PaddedTransposeConvolution2dUint8NhwcTest)
+ARMNN_AUTO_TEST_CASE(PaddedTransposeConvolution2dInt16Nchw, PaddedTransposeConvolution2dInt16NchwTest)
+ARMNN_AUTO_TEST_CASE(PaddedTransposeConvolution2dInt16Nhwc, PaddedTransposeConvolution2dInt16NhwcTest)
+
+ARMNN_AUTO_TEST_CASE(UnbiasedPaddedTransposeConvolution2dFloatNchw, UnbiasedPaddedTransposeConvolution2dFloatNchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedPaddedTransposeConvolution2dFloatNhwc, UnbiasedPaddedTransposeConvolution2dFloatNhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedPaddedTransposeConvolution2dUint8Nchw, UnbiasedPaddedTransposeConvolution2dUint8NchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedPaddedTransposeConvolution2dUint8Nhwc, UnbiasedPaddedTransposeConvolution2dUint8NhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedPaddedTransposeConvolution2dInt16Nchw, UnbiasedPaddedTransposeConvolution2dInt16NchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedPaddedTransposeConvolution2dInt16Nhwc, UnbiasedPaddedTransposeConvolution2dInt16NhwcTest)
+
+ARMNN_AUTO_TEST_CASE(StridedTransposeConvolution2dFloatNhwc, StridedTransposeConvolution2dFloatNhwcTest)
+ARMNN_AUTO_TEST_CASE(StridedTransposeConvolution2dFloatNchw, StridedTransposeConvolution2dFloatNchwTest)
+ARMNN_AUTO_TEST_CASE(StridedTransposeConvolution2dUint8Nhwc, StridedTransposeConvolution2dUint8NhwcTest)
+ARMNN_AUTO_TEST_CASE(StridedTransposeConvolution2dUint8Nchw, StridedTransposeConvolution2dUint8NchwTest)
+ARMNN_AUTO_TEST_CASE(StridedTransposeConvolution2dInt16Nhwc, StridedTransposeConvolution2dInt16NhwcTest)
+ARMNN_AUTO_TEST_CASE(StridedTransposeConvolution2dInt16Nchw, StridedTransposeConvolution2dInt16NchwTest)
+
+ARMNN_AUTO_TEST_CASE(UnbiasedStridedTransposeConvolution2dFloatNhwc, UnbiasedStridedTransposeConvolution2dFloatNhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedStridedTransposeConvolution2dFloatNchw, UnbiasedStridedTransposeConvolution2dFloatNchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedStridedTransposeConvolution2dUint8Nhwc, UnbiasedStridedTransposeConvolution2dUint8NhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedStridedTransposeConvolution2dUint8Nchw, UnbiasedStridedTransposeConvolution2dUint8NchwTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedStridedTransposeConvolution2dInt16Nhwc, UnbiasedStridedTransposeConvolution2dInt16NhwcTest)
+ARMNN_AUTO_TEST_CASE(UnbiasedStridedTransposeConvolution2dInt16Nchw, UnbiasedStridedTransposeConvolution2dInt16NchwTest)
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt
index daa0043..9be245b 100644
--- a/src/backends/reference/workloads/CMakeLists.txt
+++ b/src/backends/reference/workloads/CMakeLists.txt
@@ -29,6 +29,8 @@
     Gather.hpp
     LstmUtils.hpp
     Maximum.hpp
+    Mean.cpp
+    Mean.hpp
     Concatenate.hpp
     Concatenate.cpp
     Minimum.hpp
@@ -44,6 +46,8 @@
     RefBatchNormalizationWorkload.hpp
     RefBatchToSpaceNdWorkload.cpp
     RefBatchToSpaceNdWorkload.hpp
+    RefConcatWorkload.cpp
+    RefConcatWorkload.hpp
     RefConstantWorkload.cpp
     RefConstantWorkload.hpp
     RefConvertFp16ToFp32Workload.cpp
@@ -74,8 +78,8 @@
     RefL2NormalizationWorkload.hpp
     RefLstmWorkload.cpp
     RefLstmWorkload.hpp
-    RefConcatWorkload.cpp
-    RefConcatWorkload.hpp
+    RefMeanWorkload.cpp
+    RefMeanWorkload.hpp
     RefNormalizationWorkload.cpp
     RefNormalizationWorkload.hpp
     RefPadWorkload.cpp
@@ -104,6 +108,8 @@
     RefSplitterWorkload.hpp
     RefStridedSliceWorkload.cpp
     RefStridedSliceWorkload.hpp
+    RefTransposeConvolution2dWorkload.cpp
+    RefTransposeConvolution2dWorkload.hpp
     RefWorkloads.hpp
     RefWorkloadUtils.hpp
     ResizeBilinear.cpp
@@ -123,10 +129,8 @@
     StringMapping.cpp
     StringMapping.hpp
     TensorBufferArrayView.hpp
-    Mean.cpp
-    Mean.hpp
-    RefMeanWorkload.cpp
-    RefMeanWorkload.hpp
+    TransposeConvolution2d.cpp
+    TransposeConvolution2d.hpp
 )
 
 add_library(armnnRefBackendWorkloads OBJECT ${armnnRefBackendWorkloads_sources})
diff --git a/src/backends/reference/workloads/RefTransposeConvolution2dWorkload.cpp b/src/backends/reference/workloads/RefTransposeConvolution2dWorkload.cpp
new file mode 100644
index 0000000..50dafca
--- /dev/null
+++ b/src/backends/reference/workloads/RefTransposeConvolution2dWorkload.cpp
@@ -0,0 +1,67 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "RefTransposeConvolution2dWorkload.hpp"
+
+#include "RefWorkloadUtils.hpp"
+#include "TransposeConvolution2d.hpp"
+
+#include <Profiling.hpp>
+
+namespace armnn
+{
+
+RefTransposeConvolution2dWorkload::RefTransposeConvolution2dWorkload(
+    const TransposeConvolution2dQueueDescriptor& descriptor, const WorkloadInfo& info) :
+    BaseWorkload<TransposeConvolution2dQueueDescriptor>(descriptor, info)
+{
+    // set up weights decoder
+    m_Weights = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Weight));
+    const TensorInfo& weightsInfo = GetTensorInfo(m_Weights.get());
+
+    m_WeightsDecoder = MakeDecoder<float>(weightsInfo, m_Weights.get()->Map(true));
+    m_WeightsShape   = weightsInfo.GetShape();
+
+    // set up biases decoder
+    if (descriptor.m_Parameters.m_BiasEnabled)
+    {
+        m_Biases = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Bias));
+        const TensorInfo& biasesInfo = GetTensorInfo(m_Biases.get());
+        m_BiasesDecoder = MakeDecoder<float>(biasesInfo, m_Biases.get()->Map(true));
+    }
+}
+
+void RefTransposeConvolution2dWorkload::PostAllocationConfigure()
+{
+    // set up input decoder
+    const ITensorHandle* input  = m_Data.m_Inputs[0];
+    const TensorInfo& inputInfo = GetTensorInfo(input);
+
+    m_InputShape   = inputInfo.GetShape();
+    m_InputDecoder = MakeDecoder<float>(inputInfo, input->Map());
+
+    // set up output encoder
+    ITensorHandle* output        = m_Data.m_Outputs[0];
+    const TensorInfo& outputInfo = GetTensorInfo(output);
+
+    m_OutputShape   = outputInfo.GetShape();
+    m_OutputEncoder = MakeEncoder<float>(outputInfo, output->Map());
+}
+
+void RefTransposeConvolution2dWorkload::Execute() const
+{
+    ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefTransposeConvolution2dWorkload_Execute");
+
+    TransposeConvolution2dImpl(m_Data.m_Parameters,
+                               m_InputShape,
+                               *m_InputDecoder,
+                               m_OutputShape,
+                               *m_OutputEncoder,
+                               m_WeightsShape,
+                               *m_WeightsDecoder,
+                               m_BiasesDecoder.get());
+}
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/reference/workloads/RefTransposeConvolution2dWorkload.hpp b/src/backends/reference/workloads/RefTransposeConvolution2dWorkload.hpp
new file mode 100644
index 0000000..9ded8c9
--- /dev/null
+++ b/src/backends/reference/workloads/RefTransposeConvolution2dWorkload.hpp
@@ -0,0 +1,43 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "Decoders.hpp"
+#include "Encoders.hpp"
+
+#include <backendsCommon/CpuTensorHandle.hpp>
+#include <backendsCommon/Workload.hpp>
+
+namespace armnn
+{
+
+class RefTransposeConvolution2dWorkload : public BaseWorkload<TransposeConvolution2dQueueDescriptor>
+{
+public:
+    RefTransposeConvolution2dWorkload(const TransposeConvolution2dQueueDescriptor& descriptor,
+                                      const WorkloadInfo& info);
+    ~RefTransposeConvolution2dWorkload() = default;
+
+    void PostAllocationConfigure() override;
+
+    void Execute() const override;
+
+private:
+    std::unique_ptr<ScopedCpuTensorHandle> m_Weights;
+    std::unique_ptr<ScopedCpuTensorHandle> m_Biases;
+
+    std::unique_ptr<Decoder<float>> m_InputDecoder;
+    std::unique_ptr<Encoder<float>> m_OutputEncoder;
+
+    std::unique_ptr<Decoder<float>> m_WeightsDecoder;
+    std::unique_ptr<Decoder<float>> m_BiasesDecoder;
+
+    TensorShape m_InputShape;
+    TensorShape m_OutputShape;
+    TensorShape m_WeightsShape;
+};
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/reference/workloads/RefWorkloads.hpp b/src/backends/reference/workloads/RefWorkloads.hpp
index 9058281..3a094c8 100644
--- a/src/backends/reference/workloads/RefWorkloads.hpp
+++ b/src/backends/reference/workloads/RefWorkloads.hpp
@@ -47,6 +47,7 @@
 #include "RefSpaceToBatchNdWorkload.hpp"
 #include "RefStridedSliceWorkload.hpp"
 #include "RefSpaceToDepthWorkload.hpp"
+#include "RefTransposeConvolution2dWorkload.hpp"
 #include "RefWorkloadUtils.hpp"
 #include "ResizeBilinear.hpp"
 #include "Softmax.hpp"
diff --git a/src/backends/reference/workloads/TransposeConvolution2d.cpp b/src/backends/reference/workloads/TransposeConvolution2d.cpp
new file mode 100644
index 0000000..db15cef
--- /dev/null
+++ b/src/backends/reference/workloads/TransposeConvolution2d.cpp
@@ -0,0 +1,248 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "TransposeConvolution2d.hpp"
+
+#include <DataLayoutIndexed.hpp>
+
+namespace armnn
+{
+
+using namespace armnnUtils;
+
+struct TensorData
+{
+    TensorShape        shape;
+    std::vector<float> data;
+};
+
+TensorData SetUpStridedInput(const TensorShape& inputShape,
+                             Decoder<float>& inputDecoder,
+                             const TransposeConvolution2dDescriptor& descriptor,
+                             const DataLayoutIndexed& dataLayoutIndexed)
+{
+    const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
+    const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();
+    const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
+
+    const unsigned int batches  = inputShape[0];
+    const unsigned int channels = inputShape[cIndex];
+
+    const unsigned int wInput = inputShape[wIndex];
+    const unsigned int hInput = inputShape[hIndex];
+
+    const unsigned int wStridedInput = 1u + descriptor.m_StrideX * (wInput - 1);
+    const unsigned int hStridedInput = 1u + descriptor.m_StrideY * (hInput - 1);
+
+    TensorData stridedInput;
+    stridedInput.data  = std::vector<float>(batches * channels * wStridedInput * hStridedInput, 0.0f);
+    stridedInput.shape = TensorShape(4);
+
+    stridedInput.shape[0]      = batches;
+    stridedInput.shape[cIndex] = channels;
+    stridedInput.shape[hIndex] = hStridedInput;
+    stridedInput.shape[wIndex] = wStridedInput;
+
+    // expand input data with strides
+    for (unsigned int batchIdx = 0u; batchIdx < batches; ++batchIdx)
+    {
+        for (unsigned int cInput = 0u; cInput < channels; ++cInput)
+        {
+            for (unsigned int yInput = 0u, yStrided = 0u;
+                 yInput < hInput && yStrided < hStridedInput;
+                 ++yInput, yStrided += descriptor.m_StrideY)
+            {
+                for (unsigned int xInput = 0u, xStrided = 0u;
+                     xInput < wInput && xStrided < wStridedInput;
+                     ++xInput, xStrided += descriptor.m_StrideX)
+                {
+                    unsigned int inputIdx =
+                        dataLayoutIndexed.GetIndex(inputShape, batchIdx, cInput, yInput, xInput);
+                    unsigned int stridedInputIdx =
+                        dataLayoutIndexed.GetIndex(stridedInput.shape, batchIdx, cInput, yStrided, xStrided);
+
+                    inputDecoder[inputIdx];
+                    stridedInput.data[stridedInputIdx] = inputDecoder.Get();
+                }
+            }
+        }
+    }
+
+    return stridedInput;
+}
+
+TensorData SetUpEmptyPaddedOutput(const TensorShape& outputShape,
+                                  const TransposeConvolution2dDescriptor& descriptor,
+                                  const DataLayoutIndexed& dataLayoutIndexed)
+{
+    const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
+    const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();
+    const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
+
+    const unsigned int batches  = outputShape[0];
+    const unsigned int channels = outputShape[cIndex];
+
+    const unsigned int wOutput = outputShape[wIndex];
+    const unsigned int hOutput = outputShape[hIndex];
+
+    const unsigned int wPaddedOutput = wOutput + descriptor.m_PadLeft + descriptor.m_PadRight;
+    const unsigned int hPaddedOutput = hOutput + descriptor.m_PadTop  + descriptor.m_PadBottom;
+
+    TensorData paddedOutput;
+    paddedOutput.data  = std::vector<float>(batches * channels * wPaddedOutput * hPaddedOutput, 0.0f);
+    paddedOutput.shape = TensorShape(4);
+
+    paddedOutput.shape[0]      = batches;
+    paddedOutput.shape[cIndex] = channels;
+    paddedOutput.shape[hIndex] = hPaddedOutput;
+    paddedOutput.shape[wIndex] = wPaddedOutput;
+
+    return paddedOutput;
+}
+
+void Deconvolve(const TensorData& stridedInput,
+                TensorData& paddedOutput,
+                const TensorShape& weightsShape,
+                Decoder<float>& weightsDecoder,
+                const DataLayoutIndexed& dataLayoutIndexed)
+{
+    const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
+    const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();
+    const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
+
+    const unsigned int batches  = stridedInput.shape[0];
+    const unsigned int channels = stridedInput.shape[cIndex];
+
+    const unsigned int wKernel = weightsShape[wIndex];
+    const unsigned int hKernel = weightsShape[hIndex];
+
+    const unsigned int wStridedInput = stridedInput.shape[wIndex];
+    const unsigned int hStridedInput = stridedInput.shape[hIndex];
+
+    // loop through all input elements
+    for (unsigned int batchIdx = 0u; batchIdx < batches; ++batchIdx)
+    {
+        for (unsigned int cInput = 0u; cInput < channels; ++cInput)
+        {
+            for (unsigned int yInput = 0u; yInput < hStridedInput; ++yInput)
+            {
+                for (unsigned int xInput = 0u; xInput < wStridedInput; ++xInput)
+                {
+                    // obtain input value
+                    unsigned int inputIdx =
+                        dataLayoutIndexed.GetIndex(stridedInput.shape, batchIdx, cInput, yInput, xInput);
+                    float inputValue = stridedInput.data[inputIdx];
+
+                    // loop through kernel
+                    for (unsigned int yKernel = 0u; yKernel < hKernel; ++yKernel)
+                    {
+                        for (unsigned int xKernel = 0; xKernel < wKernel; ++xKernel)
+                        {
+                            unsigned int kernelIdx =
+                                dataLayoutIndexed.GetIndex(weightsShape, batchIdx, cInput, yKernel, xKernel);
+
+                            weightsDecoder[kernelIdx];
+                            float kernelValue = weightsDecoder.Get();
+
+                            unsigned int xOutput = xInput + xKernel;
+                            unsigned int yOutput = yInput + yKernel;
+
+                            // compute output increment
+                            float outputValue = inputValue * kernelValue;
+
+                            unsigned int outputIdx = dataLayoutIndexed.GetIndex(paddedOutput.shape,
+                                                                                batchIdx,
+                                                                                cInput,
+                                                                                yOutput,
+                                                                                xOutput);
+
+                            // set output value
+                            paddedOutput.data[outputIdx] += outputValue;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+void TransposeConvolution2dImpl(const TransposeConvolution2dDescriptor& descriptor,
+                                const TensorShape& inputShape,
+                                Decoder<float>& inputDecoder,
+                                const TensorShape& outputShape,
+                                Encoder<float>& outputEncoder,
+                                const TensorShape& weightsShape,
+                                Decoder<float>& weightsDecoder,
+                                Decoder<float>* biasesDecoder)
+{
+    if (descriptor.m_BiasEnabled && !biasesDecoder)
+    {
+        throw InvalidArgumentException("Biases enabled but no bias data provided");
+    }
+
+    const DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
+
+    const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
+    const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();
+    const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
+
+    const unsigned int numBatches  = inputShape[0];
+    const unsigned int numChannels = inputShape[cIndex];
+
+    // set up temporary strided input
+    TensorData stridedInput = SetUpStridedInput(inputShape, inputDecoder, descriptor, dataLayoutIndexed);
+
+    // set up temporary (empty) padded output
+    TensorData paddedOutput = SetUpEmptyPaddedOutput(outputShape, descriptor, dataLayoutIndexed);
+
+    // run deconvolution (without biases) on strided input to produce padded output
+    Deconvolve(stridedInput, paddedOutput, weightsShape, weightsDecoder, dataLayoutIndexed);
+
+    const unsigned int wPaddedOutput = paddedOutput.shape[wIndex];
+    const unsigned int hPaddedOutput = paddedOutput.shape[hIndex];
+
+    // remove padding and apply bias (if enabled)
+    for (unsigned int batchIdx = 0u; batchIdx < numBatches; ++batchIdx)
+    {
+        for (unsigned int cOutput = 0u; cOutput < numChannels; ++cOutput)
+        {
+            // update bias decoder iterator
+            if (descriptor.m_BiasEnabled)
+            {
+                (*biasesDecoder)[cOutput];
+            }
+
+            for (unsigned int yPaddedOutput = descriptor.m_PadTop;
+                 yPaddedOutput < (hPaddedOutput - descriptor.m_PadBottom);
+                 ++yPaddedOutput)
+            {
+                for (unsigned int xPaddedOutput = descriptor.m_PadLeft;
+                     xPaddedOutput < (wPaddedOutput - descriptor.m_PadRight);
+                     ++xPaddedOutput)
+                {
+                    unsigned int xOutput = xPaddedOutput - descriptor.m_PadLeft;
+                    unsigned int yOutput = yPaddedOutput - descriptor.m_PadTop;
+
+                    unsigned int outputIdx =
+                        dataLayoutIndexed.GetIndex(outputShape, batchIdx, cOutput, yOutput, xOutput);
+                    unsigned int paddedOutputIdx =
+                        dataLayoutIndexed.GetIndex(paddedOutput.shape, batchIdx, cOutput, yPaddedOutput, xPaddedOutput);
+
+                    // encode (copy) output data
+                    outputEncoder[outputIdx];
+                    outputEncoder.Set(paddedOutput.data[paddedOutputIdx]);
+
+                    // apply bias (if enabled)
+                    if (descriptor.m_BiasEnabled)
+                    {
+                        outputEncoder.Set(outputEncoder.Get() + biasesDecoder->Get());
+                    }
+                }
+            }
+        }
+    }
+}
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/reference/workloads/TransposeConvolution2d.hpp b/src/backends/reference/workloads/TransposeConvolution2d.hpp
new file mode 100644
index 0000000..f20f327
--- /dev/null
+++ b/src/backends/reference/workloads/TransposeConvolution2d.hpp
@@ -0,0 +1,26 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "Decoders.hpp"
+#include "Encoders.hpp"
+
+#include <armnn/Descriptors.hpp>
+#include <armnn/Tensor.hpp>
+
+namespace armnn
+{
+
+void TransposeConvolution2dImpl(const TransposeConvolution2dDescriptor& descriptor,
+                                const TensorShape& inputShape,
+                                Decoder<float>& inputDecoder,
+                                const TensorShape& outputShape,
+                                Encoder<float>& outputEncoder,
+                                const TensorShape& weightsShape,
+                                Decoder<float>& weightsDecoder,
+                                Decoder<float>* biasesDecoder);
+
+} // namespace armnn
\ No newline at end of file