IVGCVSW-5736 and IVGCVSW-5743 'NonConstWeights: Update front-end and TfLiteDelegate support for FullyConnected Operator'

* Added front-end support for non-const weights for FULLY_CONNECTED operator
* Added FULLY_CONNECTED end-to-end test
* Updated FULLY_CONNECTED operator support in TfLite Arm NN Delegate for non-const weights
* Updated the version numbers

Signed-off-by: Sadik Armagan <sadik.armagan@arm.com>
Change-Id: Iffa5b9aa9297aca4c02d923cce4636c88ac21faa
diff --git a/delegate/include/Version.hpp b/delegate/include/Version.hpp
index 8b831f9..99ee2ad 100644
--- a/delegate/include/Version.hpp
+++ b/delegate/include/Version.hpp
@@ -14,7 +14,7 @@
 
 // ArmNN Delegate version components
 #define DELEGATE_MAJOR_VERSION 24
-#define DELEGATE_MINOR_VERSION 0
+#define DELEGATE_MINOR_VERSION 1
 #define DELEGATE_PATCH_VERSION 0
 
 /// DELEGATE_VERSION: "X.Y.Z"
diff --git a/delegate/src/FullyConnected.hpp b/delegate/src/FullyConnected.hpp
index 0a82286..2b45c48 100644
--- a/delegate/src/FullyConnected.hpp
+++ b/delegate/src/FullyConnected.hpp
@@ -35,62 +35,27 @@
 
     const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
     const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
-    if(!IsValid(&tfLiteTensors[tfLiteNode->inputs->data[0]]))
+    if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
     {
-        TF_LITE_MAYBE_KERNEL_LOG(
-            tfLiteContext,
-            "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
-            operatorCode, nodeIndex);
         return kTfLiteError;
     }
-    if (IsDynamicTensor(tfLiteInputTensor))
-    {
-        TF_LITE_MAYBE_KERNEL_LOG(
-            tfLiteContext,
-            "TfLiteArmnnDelegate: Dynamic input tensors are not supported in node #%d: ",
-            nodeIndex);
-        return kTfLiteError;
-    }
+
     const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
-    if(!IsValid(&tfLiteOutputTensor))
+    if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
     {
-        TF_LITE_MAYBE_KERNEL_LOG(
-            tfLiteContext,
-            "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ",
-            operatorCode, nodeIndex);
-        return kTfLiteError;
-    }
-    if (IsDynamicTensor(tfLiteOutputTensor))
-    {
-        TF_LITE_MAYBE_KERNEL_LOG(
-            tfLiteContext,
-            "TfLiteArmnnDelegate: Dynamic output tensors are not supported in node #%d: ",
-            nodeIndex);
         return kTfLiteError;
     }
 
     const TfLiteTensor& tfLiteWeightsTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
-    if(!IsValid(&tfLiteWeightsTensor))
+    if (!IsValid(tfLiteContext, tfLiteWeightsTensor, operatorCode, nodeIndex))
     {
-        TF_LITE_MAYBE_KERNEL_LOG(
-            tfLiteContext,
-            "TfLiteArmnnDelegate: Invalid weights tensor in operator #%d node #%d: ",
-            operatorCode, nodeIndex);
-        return kTfLiteError;
-    }
-    if (IsDynamicTensor(tfLiteWeightsTensor))
-    {
-        TF_LITE_MAYBE_KERNEL_LOG(
-            tfLiteContext,
-            "TfLiteArmnnDelegate: Dynamic weight tensors are not supported in node #%d: ",
-            nodeIndex);
         return kTfLiteError;
     }
 
     const armnn::TensorInfo& inputTensorInfo   = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
+    armnn::TensorInfo weightsTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteWeightsTensor);
     const armnn::TensorInfo& outputTensorInfo  = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor);
 
-    armnn::TensorInfo weightsTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteWeightsTensor);
     // Fully Connected Layer accepts two dimensional weights input
     int32_t weightsDimension = static_cast<int32_t>(weightsTensorInfo.GetNumDimensions());
     if (weightsDimension != 2)
@@ -102,24 +67,23 @@
         return kTfLiteError;
     }
 
+    bool isConstantWeights = tflite::IsConstantTensor(&tfLiteWeightsTensor);
+
     armnn::TensorInfo biasTensorInfo;
     if (biasEnabled)
     {
         const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
-        if(!IsValid(&tfLiteBiasTensor))
+        if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
         {
-            TF_LITE_MAYBE_KERNEL_LOG(
-                tfLiteContext,
-                "TfLiteArmnnDelegate: Invalid bias tensor in operator #%d node #%d: ",
-                operatorCode, nodeIndex);
             return kTfLiteError;
         }
-        if (IsDynamicTensor(tfLiteBiasTensor))
+        if ((isConstantWeights && !tflite::IsConstantTensor(&tfLiteBiasTensor))
+            || (!isConstantWeights && tflite::IsConstantTensor(&tfLiteBiasTensor)))
         {
             TF_LITE_MAYBE_KERNEL_LOG(
                 tfLiteContext,
-                "TfLiteArmnnDelegate: Dynamic bias tensors are not supported in node #%d: ",
-                nodeIndex);
+                "TfLiteArmnnDelegate: Weights and bias are not compatible"
+                " in operator #%d node #%d: ", operatorCode, nodeIndex);
             return kTfLiteError;
         }
         biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
@@ -130,7 +94,6 @@
     }
 
     armnn::TensorInfo reshapedTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
-
     if (inputTensorInfo.GetNumDimensions() > 2)
     {
         // Calculate reshape to flatten to 2D [batch_size, input_size]
@@ -153,6 +116,7 @@
     armnn::FullyConnectedDescriptor descriptor;
     descriptor.m_TransposeWeightMatrix = true;
     descriptor.m_BiasEnabled           = biasEnabled;
+    descriptor.m_ConstantWeights       = isConstantWeights;
 
     bool isSupported = false;
     auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
@@ -175,27 +139,28 @@
         return isSupported ? kTfLiteOk : kTfLiteError;
     }
 
-    auto weightsTensor = CreateConstTensor(&tfLiteWeightsTensor,
-                                           weightsTensorInfo,
-                                           armnn::Optional<armnn::PermutationVector&>());
+    armnn::Optional<armnn::ConstTensor> optionalWeights = armnn::EmptyOptional();
+    armnn::Optional<armnn::ConstTensor> optionalBiases = armnn::EmptyOptional();
+    if(descriptor.m_ConstantWeights)
+    {
+        auto weightsTensor = CreateConstTensor(&tfLiteWeightsTensor,
+                                               weightsTensorInfo,
+                                               armnn::Optional<armnn::PermutationVector&>());
+        optionalWeights = armnn::Optional<armnn::ConstTensor>(weightsTensor);
 
-    armnn::IConnectableLayer* layer = nullptr;
-    if (biasEnabled)
-    {
-        const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
-        auto biasTensor = CreateConstTensor(&tfLiteBiasTensor,
-                                            biasTensorInfo,
-                                            armnn::Optional<armnn::PermutationVector&>());
-        layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor,
-                                                               weightsTensor,
-                                                               armnn::Optional<armnn::ConstTensor>(biasTensor));
+        if (biasEnabled)
+        {
+            const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
+            auto biasTensor = CreateConstTensor(&tfLiteBiasTensor,
+                                                biasTensorInfo,
+                                                armnn::Optional<armnn::PermutationVector&>());
+            optionalBiases = armnn::Optional<armnn::ConstTensor>(biasTensor);
+        }
     }
-    else
-    {
-        layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor,
-                                                               weightsTensor,
-                                                               armnn::EmptyOptional());
-    }
+
+    armnn::IConnectableLayer* layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor,
+                                                                                     optionalWeights,
+                                                                                     optionalBiases);
     ARMNN_ASSERT(layer != nullptr);
 
     armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
@@ -215,6 +180,14 @@
         // Connect
         delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[0]]->Connect(reshapeLayer->GetInputSlot(0));
         reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
+        if (!descriptor.m_ConstantWeights)
+        {
+            delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[1]]->Connect(layer->GetInputSlot(1));
+            if (biasEnabled)
+            {
+                delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[2]]->Connect(layer->GetInputSlot(2));
+            }
+        }
         delegateData.m_OutputSlotForNode[tfLiteNode->outputs->data[0]] = &outputSlot;
     }
 
diff --git a/delegate/src/test/FullyConnectedTest.cpp b/delegate/src/test/FullyConnectedTest.cpp
index 018f7f5..3bea250 100644
--- a/delegate/src/test/FullyConnectedTest.cpp
+++ b/delegate/src/test/FullyConnectedTest.cpp
@@ -8,7 +8,7 @@
 namespace
 {
 
-void FullyConnectedFp32Test(std::vector<armnn::BackendId>& backends)
+void FullyConnectedFp32Test(std::vector<armnn::BackendId>& backends, bool constantWeights = true)
 {
     std::vector<int32_t> inputTensorShape   { 1, 4, 1, 1 };
     std::vector<int32_t> weightsTensorShape { 1, 4 };
@@ -30,10 +30,11 @@
                               outputTensorShape,
                               inputValues,
                               expectedOutputValues,
-                              weightsData);
+                              weightsData,
+                              constantWeights);
 }
 
-void FullyConnectedActicationTest(std::vector<armnn::BackendId>& backends)
+void FullyConnectedActicationTest(std::vector<armnn::BackendId>& backends, bool constantWeights = true)
 {
     std::vector<int32_t> inputTensorShape   { 1, 4, 1, 1 };
     std::vector<int32_t> weightsTensorShape { 1, 4 };
@@ -55,10 +56,11 @@
                               outputTensorShape,
                               inputValues,
                               expectedOutputValues,
-                              weightsData);
+                              weightsData,
+                              constantWeights);
 }
 
-void FullyConnectedInt8Test(std::vector<armnn::BackendId>& backends)
+void FullyConnectedInt8Test(std::vector<armnn::BackendId>& backends, bool constantWeights = true)
 {
     std::vector<int32_t> inputTensorShape   { 1, 4, 2, 1 };
     std::vector<int32_t> weightsTensorShape { 1, 4 };
@@ -82,7 +84,8 @@
                                 outputTensorShape,
                                 inputValues,
                                 expectedOutputValues,
-                                weightsData);
+                                weightsData,
+                                constantWeights);
 }
 
 TEST_SUITE("FullyConnected_GpuAccTests")
@@ -152,6 +155,24 @@
     FullyConnectedActicationTest(backends);
 }
 
+TEST_CASE ("FullyConnected_Weights_As_Inputs_FP32_CpuRef_Test")
+{
+    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+    FullyConnectedFp32Test(backends, false);
+}
+
+TEST_CASE ("FullyConnected_Weights_As_Inputs_Int8_CpuRef_Test")
+{
+    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+    FullyConnectedInt8Test(backends, false);
+}
+
+TEST_CASE ("FullyConnected_Weights_As_Inputs_Activation_CpuRef_Test")
+{
+    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+    FullyConnectedActicationTest(backends, false);
+}
+
 } // End of TEST_SUITE("FullyConnected_CpuRefTests")
 
 } // anonymous namespace
\ No newline at end of file
diff --git a/delegate/src/test/FullyConnectedTestHelper.hpp b/delegate/src/test/FullyConnectedTestHelper.hpp
index 1b6ca94..37062c3 100644
--- a/delegate/src/test/FullyConnectedTestHelper.hpp
+++ b/delegate/src/test/FullyConnectedTestHelper.hpp
@@ -5,6 +5,8 @@
 
 #pragma once
 
+#include "TestUtils.hpp"
+
 #include <armnn_delegate.hpp>
 
 #include <flatbuffers/flatbuffers.h>
@@ -25,8 +27,9 @@
                                                   const std::vector <int32_t>& inputTensorShape,
                                                   const std::vector <int32_t>& weightsTensorShape,
                                                   const std::vector <int32_t>& biasTensorShape,
-                                                  const std::vector <int32_t>& outputTensorShape,
-                                                  const std::vector <T>& weightsData,
+                                                  std::vector <int32_t>& outputTensorShape,
+                                                  std::vector <T>& weightsData,
+                                                  bool constantWeights = true,
                                                   float quantScale = 1.0f,
                                                   int quantOffset  = 0,
                                                   float outputQuantScale = 2.0f,
@@ -36,26 +39,38 @@
     flatbuffers::FlatBufferBuilder flatBufferBuilder;
     std::array<flatbuffers::Offset<tflite::Buffer>, 3> buffers;
     buffers[0] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
-    buffers[1] = CreateBuffer(flatBufferBuilder,
-                     flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(weightsData.data()),
-                                                    sizeof(T) * weightsData.size()));
 
     auto biasTensorType = ::tflite::TensorType_FLOAT32;
     if (tensorType == ::tflite::TensorType_INT8)
     {
         biasTensorType = ::tflite::TensorType_INT32;
-        std::vector<int32_t> biasData = { 10 };
-        buffers[2] = CreateBuffer(flatBufferBuilder,
-                                  flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
-                                                                 sizeof(int32_t) * biasData.size()));
+    }
+    if (constantWeights)
+    {
+        buffers[1] = CreateBuffer(flatBufferBuilder,
+                     flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(weightsData.data()),
+                                                    sizeof(T) * weightsData.size()));
 
+        if (tensorType == ::tflite::TensorType_INT8)
+        {
+            std::vector<int32_t> biasData = { 10 };
+            buffers[2] = CreateBuffer(flatBufferBuilder,
+                                      flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
+                                                                     sizeof(int32_t) * biasData.size()));
+
+        }
+        else
+        {
+            std::vector<float> biasData = { 10 };
+            buffers[2] = CreateBuffer(flatBufferBuilder,
+                                      flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
+                                                                     sizeof(float) * biasData.size()));
+        }
     }
     else
     {
-        std::vector<float> biasData = { 10 };
-        buffers[2] = CreateBuffer(flatBufferBuilder,
-                                  flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
-                                                                 sizeof(float) * biasData.size()));
+        buffers[1] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
+        buffers[2] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
     }
 
     auto quantizationParameters =
@@ -155,10 +170,11 @@
                         const std::vector <int32_t>& inputTensorShape,
                         const std::vector <int32_t>& weightsTensorShape,
                         const std::vector <int32_t>& biasTensorShape,
-                        const std::vector <int32_t>& outputTensorShape,
-                        const std::vector <T>& inputValues,
-                        const std::vector <T>& expectedOutputValues,
-                        const std::vector <T>& weightsData,
+                        std::vector <int32_t>& outputTensorShape,
+                        std::vector <T>& inputValues,
+                        std::vector <T>& expectedOutputValues,
+                        std::vector <T>& weightsData,
+                        bool constantWeights = true,
                         float quantScale = 1.0f,
                         int quantOffset  = 0)
 {
@@ -171,10 +187,11 @@
                                                                     biasTensorShape,
                                                                     outputTensorShape,
                                                                     weightsData,
+                                                                    constantWeights,
                                                                     quantScale,
                                                                     quantOffset);
-
     const Model* tfLiteModel = GetModel(modelBuffer.data());
+
     // Create TfLite Interpreters
     std::unique_ptr<Interpreter> armnnDelegateInterpreter;
     CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
@@ -191,25 +208,34 @@
     // Create the ArmNN Delegate
     armnnDelegate::DelegateOptions delegateOptions(backends);
     std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
-                        theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
-                                         armnnDelegate::TfLiteArmnnDelegateDelete);
+    theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
+                     armnnDelegate::TfLiteArmnnDelegateDelete);
     CHECK(theArmnnDelegate != nullptr);
+
     // Modify armnnDelegateInterpreter to use armnnDelegate
     CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk);
 
     // Set input data
-    auto tfLiteDelegateInputId = tfLiteInterpreter->inputs()[0];
-    auto tfLiteDelageInputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateInputId);
-    for (unsigned int i = 0; i < inputValues.size(); ++i)
-    {
-        tfLiteDelageInputData[i] = inputValues[i];
-    }
+    armnnDelegate::FillInput<T>(tfLiteInterpreter, 0, inputValues);
+    armnnDelegate::FillInput<T>(armnnDelegateInterpreter, 0, inputValues);
 
-    auto armnnDelegateInputId = armnnDelegateInterpreter->inputs()[0];
-    auto armnnDelegateInputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateInputId);
-    for (unsigned int i = 0; i < inputValues.size(); ++i)
+    if (!constantWeights)
     {
-        armnnDelegateInputData[i] = inputValues[i];
+        armnnDelegate::FillInput<T>(tfLiteInterpreter, 1, weightsData);
+        armnnDelegate::FillInput<T>(armnnDelegateInterpreter, 1, weightsData);
+
+        if (tensorType == ::tflite::TensorType_INT8)
+        {
+            std::vector <int32_t> biasData = {10};
+            armnnDelegate::FillInput<int32_t>(tfLiteInterpreter, 2, biasData);
+            armnnDelegate::FillInput<int32_t>(armnnDelegateInterpreter, 2, biasData);
+        }
+        else
+        {
+            std::vector<float> biasData = {10};
+            armnnDelegate::FillInput<float>(tfLiteInterpreter, 2, biasData);
+            armnnDelegate::FillInput<float>(armnnDelegateInterpreter, 2, biasData);
+        }
     }
 
     // Run EnqueWorkload
@@ -217,16 +243,11 @@
     CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
 
     // Compare output data
-    auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[0];
-    auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateOutputId);
-    auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0];
-    auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateOutputId);
-    for (size_t i = 0; i < expectedOutputValues.size(); i++)
-    {
-        CHECK(expectedOutputValues[i] == tfLiteDelageOutputData[i]);
-        CHECK(expectedOutputValues[i] == armnnDelegateOutputData[i]);
-        CHECK(tfLiteDelageOutputData[i] == armnnDelegateOutputData[i]);
-    }
+    armnnDelegate::CompareOutputData<T>(tfLiteInterpreter,
+                                        armnnDelegateInterpreter,
+                                        outputTensorShape,
+                                        expectedOutputValues);
+    armnnDelegateInterpreter.reset(nullptr);
 }
 
 } // anonymous namespace
\ No newline at end of file
diff --git a/include/armnn/BackendHelper.hpp b/include/armnn/BackendHelper.hpp
index a562f60..41bb5f9 100644
--- a/include/armnn/BackendHelper.hpp
+++ b/include/armnn/BackendHelper.hpp
@@ -7,6 +7,7 @@
 
 #include <armnn/BackendId.hpp>
 #include <armnn/ILayerSupport.hpp>
+#include <armnn/Types.hpp>
 
 namespace armnn
 {
@@ -19,7 +20,10 @@
 {
 public:
     explicit LayerSupportHandle(std::shared_ptr<ILayerSupport> layerSupport)
-        : m_LayerSupport(std::move(layerSupport)) {};
+        : m_LayerSupport(std::move(layerSupport)), m_BackendId(Compute::Undefined) {};
+
+    explicit LayerSupportHandle(std::shared_ptr<ILayerSupport> layerSupport, const BackendId& backendId)
+        : m_LayerSupport(std::move(layerSupport)), m_BackendId(backendId) {};
 
     bool IsBackendRegistered() const;
 
@@ -422,9 +426,13 @@
 
 private:
     std::shared_ptr<ILayerSupport> m_LayerSupport;
+    const BackendId m_BackendId;
 };
 
 /// Convenience function to retrieve the ILayerSupportHandle for a backend
 LayerSupportHandle GetILayerSupportByBackendId(const armnn::BackendId& backend);
 
+/// Convenience function to check a capability on a backend
+bool IsCapabilitySupported(const armnn::BackendId& backend, armnn::BackendCapability capability);
+
 }
diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp
index 20511ab..278c61f 100644
--- a/include/armnn/Descriptors.hpp
+++ b/include/armnn/Descriptors.hpp
@@ -391,17 +391,25 @@
     FullyConnectedDescriptor()
         : m_BiasEnabled(false)
         , m_TransposeWeightMatrix(false)
+        , m_ConstantWeights(true)
     {}
 
     bool operator ==(const FullyConnectedDescriptor& rhs) const
     {
-        return m_BiasEnabled == rhs.m_BiasEnabled && m_TransposeWeightMatrix == rhs.m_TransposeWeightMatrix;
+        return m_BiasEnabled == rhs.m_BiasEnabled
+               && m_TransposeWeightMatrix == rhs.m_TransposeWeightMatrix
+               && m_ConstantWeights == rhs.m_ConstantWeights;
     }
 
+    /// Get the number of views/inputs.
+    uint32_t GetNumViews() const;
+
     /// Enable/disable bias.
     bool m_BiasEnabled;
     /// Enable/disable transpose weight matrix.
     bool m_TransposeWeightMatrix;
+    /// Enable/disable constant weights and biases.
+    bool m_ConstantWeights;
 };
 
 /// A Convolution2dDescriptor for the Convolution2dLayer.
diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp
index d1d4744..bceb074 100644
--- a/include/armnn/INetwork.hpp
+++ b/include/armnn/INetwork.hpp
@@ -299,6 +299,17 @@
 
     /// Adds a fully connected layer to the network.
     /// @param fullyConnectedDescriptor - Description of the fully connected layer.
+    /// @param weights -Optional Tensor for the weights data.
+    /// @param biases - Optional tensor for the bias data.
+    /// @param name - Optional name for the layer.
+    /// @return - Interface for configuring the layer.
+    IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+                                              const Optional<ConstTensor>& weights,
+                                              const Optional<ConstTensor>& biases,
+                                              const char* name = nullptr);
+
+    /// Adds a fully connected layer to the network.
+    /// @param fullyConnectedDescriptor - Description of the fully connected layer.
     /// @param weights - Tensor for the weights data.
     /// @param biases - Optional tensor for the bias data.
     /// @param name - Optional name for the layer.
diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp
index e1ff46b..576e67e 100644
--- a/include/armnn/Types.hpp
+++ b/include/armnn/Types.hpp
@@ -196,6 +196,16 @@
 using IBackendSharedPtr = std::shared_ptr<IBackend>;
 using IBackendUniquePtr = std::unique_ptr<IBackend, void(*)(IBackend* backend)>;
 
+/// BackendCapability class
+enum class BackendCapability : uint32_t
+{
+    /// Constant weights can be accessed through the descriptors,
+    /// On the other hand, non-const weights can be accessed through inputs.
+    NonConstWeights,
+
+    // add new enum values here
+};
+
 /// Device specific knowledge to be passed to the optimizer.
 class IDeviceSpec
 {
diff --git a/include/armnn/Version.hpp b/include/armnn/Version.hpp
index d8c14ab..2139637 100644
--- a/include/armnn/Version.hpp
+++ b/include/armnn/Version.hpp
@@ -10,7 +10,7 @@
 #define STRINGIFY_MACRO(s) #s
 
 // ArmNN version components
-#define ARMNN_MAJOR_VERSION 24
+#define ARMNN_MAJOR_VERSION 25
 #define ARMNN_MINOR_VERSION 0
 #define ARMNN_PATCH_VERSION 0
 
diff --git a/include/armnn/backends/IBackendInternal.hpp b/include/armnn/backends/IBackendInternal.hpp
index c7ed8ef..8035cff 100644
--- a/include/armnn/backends/IBackendInternal.hpp
+++ b/include/armnn/backends/IBackendInternal.hpp
@@ -164,6 +164,9 @@
 
     /// Returns the version of the Backend API
     static constexpr BackendVersion GetApiVersion() { return BackendVersion(1, 0); }
+
+    /// Returns true if backend support the capability false otherwise
+    virtual bool HasCapability(BackendCapability /*capabilityClass*/) const { return false; }
 };
 
 using IBackendInternalUniquePtr = std::unique_ptr<IBackendInternal>;
diff --git a/include/armnnCaffeParser/Version.hpp b/include/armnnCaffeParser/Version.hpp
index d7135bf..6e7ce5a 100644
--- a/include/armnnCaffeParser/Version.hpp
+++ b/include/armnnCaffeParser/Version.hpp
@@ -14,7 +14,7 @@
 
 // CaffeParser version components
 #define CAFFE_PARSER_MAJOR_VERSION 24
-#define CAFFE_PARSER_MINOR_VERSION 0
+#define CAFFE_PARSER_MINOR_VERSION 1
 #define CAFFE_PARSER_PATCH_VERSION 0
 
 /// CAFFE_PARSER_VERSION: "X.Y.Z"
diff --git a/include/armnnOnnxParser/Version.hpp b/include/armnnOnnxParser/Version.hpp
index e42adf7..d6308b3 100644
--- a/include/armnnOnnxParser/Version.hpp
+++ b/include/armnnOnnxParser/Version.hpp
@@ -14,7 +14,7 @@
 
 // OnnxParser version components
 #define ONNX_PARSER_MAJOR_VERSION 24
-#define ONNX_PARSER_MINOR_VERSION 0
+#define ONNX_PARSER_MINOR_VERSION 1
 #define ONNX_PARSER_PATCH_VERSION 0
 
 /// ONNX_PARSER_VERSION: "X.Y.Z"
diff --git a/include/armnnTfLiteParser/Version.hpp b/include/armnnTfLiteParser/Version.hpp
index 7d239bb..99237f3 100644
--- a/include/armnnTfLiteParser/Version.hpp
+++ b/include/armnnTfLiteParser/Version.hpp
@@ -14,7 +14,7 @@
 
 // TfLiteParser version components
 #define TFLITE_PARSER_MAJOR_VERSION 24
-#define TFLITE_PARSER_MINOR_VERSION 0
+#define TFLITE_PARSER_MINOR_VERSION 1
 #define TFLITE_PARSER_PATCH_VERSION 0
 
 /// TFLITE_PARSER_VERSION: "X.Y.Z"
diff --git a/include/armnnTfParser/Version.hpp b/include/armnnTfParser/Version.hpp
index 6f6aac9..25449f3 100644
--- a/include/armnnTfParser/Version.hpp
+++ b/include/armnnTfParser/Version.hpp
@@ -14,7 +14,7 @@
 
 // tfParser version components
 #define TF_PARSER_MAJOR_VERSION 24
-#define TF_PARSER_MINOR_VERSION 0
+#define TF_PARSER_MINOR_VERSION 1
 #define TF_PARSER_PATCH_VERSION 0
 
 /// TF_PARSER_VERSION: "X.Y.Z"
diff --git a/python/pyarmnn/README.md b/python/pyarmnn/README.md
index 0c4a313..00c0dfa 100644
--- a/python/pyarmnn/README.md
+++ b/python/pyarmnn/README.md
@@ -91,14 +91,14 @@
 ```bash
 $ python setup.py sdist
 ```
-As the result you will get `./dist/pyarmnn-24.0.0.tar.gz` file. As you can see it is platform independent.
+As the result you will get `./dist/pyarmnn-25.0.0.tar.gz` file. As you can see it is platform independent.
 
 ##### 5. Build the binary package
 
 ```bash
 $ python setup.py bdist_wheel
 ```
-As the result you will get something like `./dist/pyarmnn-24.0.0-cp36-cp36m-linux_x86_64.whl` file. As you can see it
+As the result you will get something like `./dist/pyarmnn-25.0.0-cp36-cp36m-linux_x86_64.whl` file. As you can see it
  is platform dependent.
 
 # PyArmNN installation
@@ -107,8 +107,8 @@
 
 Binary package is platform dependent, the name of the package will indicate the platform it was built for, e.g.:
 
-* Linux x86 64bit machine: pyarmnn-24.0.0-cp36-cp36m-*linux_x86_64*.whl
-* Linux Aarch 64 bit machine: pyarmnn-24.0.0-cp36-cp36m-*linux_aarch64*.whl
+* Linux x86 64bit machine: pyarmnn-25.0.0-cp36-cp36m-*linux_x86_64*.whl
+* Linux Aarch 64 bit machine: pyarmnn-25.0.0-cp36-cp36m-*linux_aarch64*.whl
 
 The source package is platform independent but installation involves compilation of Arm NN python extension. You will need to have g++ compatible with C++ 14 standard and a python development library installed on the build machine.
 
@@ -126,7 +126,7 @@
 ```
 Install PyArmNN from binary by pointing to the wheel file:
 ```bash
-$ pip install /path/to/pyarmnn-24.0.0-cp36-cp36m-linux_aarch64.whl
+$ pip install /path/to/pyarmnn-25.0.0-cp36-cp36m-linux_aarch64.whl
 ```
 
 ## Installing from source package
@@ -143,7 +143,7 @@
 
 Install PyArmNN as follows:
 ```bash
-$ pip install /path/to/pyarmnn-24.0.0.tar.gz
+$ pip install /path/to/pyarmnn-25.0.0.tar.gz
 ```
 
 If PyArmNN installation script fails to find Arm NN libraries it will raise an error like this
@@ -157,7 +157,7 @@
 You can also verify it by running the following and getting output similar to below:
 ```bash
 $ python -c "import pyarmnn as ann;print(ann.GetVersion())"
-'24.0.0'
+'25.0.0'
 ```
 
 # PyArmNN API overview
diff --git a/python/pyarmnn/examples/image_classification/README.md b/python/pyarmnn/examples/image_classification/README.md
index 3d2cbab..27ed4ca 100644
--- a/python/pyarmnn/examples/image_classification/README.md
+++ b/python/pyarmnn/examples/image_classification/README.md
@@ -20,7 +20,7 @@
 You can also verify it by running the following and getting output similar to below:

 ```bash

 $ python -c "import pyarmnn as ann;print(ann.GetVersion())"

-'24.0.0'

+'25.0.0'

 ```

 

 ##### Dependencies

diff --git a/python/pyarmnn/examples/object_detection/README.md b/python/pyarmnn/examples/object_detection/README.md
index 38f0630..267d2e9 100644
--- a/python/pyarmnn/examples/object_detection/README.md
+++ b/python/pyarmnn/examples/object_detection/README.md
@@ -23,7 +23,7 @@
 You can also verify it by running the following and getting output similar to below:
 ```bash
 $ python -c "import pyarmnn as ann;print(ann.GetVersion())"
-'24.0.0'
+'25.0.0'
 ```
 
 ##### Dependencies
diff --git a/python/pyarmnn/examples/speech_recognition/README.md b/python/pyarmnn/examples/speech_recognition/README.md
index 5ccf003..f92bae6 100644
--- a/python/pyarmnn/examples/speech_recognition/README.md
+++ b/python/pyarmnn/examples/speech_recognition/README.md
@@ -18,7 +18,7 @@
 
 ```bash
 $ python -c "import pyarmnn as ann;print(ann.GetVersion())"
-'24.0.0'
+'25.0.0'
 ```
 
 ### Dependencies
diff --git a/python/pyarmnn/src/pyarmnn/_version.py b/python/pyarmnn/src/pyarmnn/_version.py
index 58e3954..f7fc8c9 100644
--- a/python/pyarmnn/src/pyarmnn/_version.py
+++ b/python/pyarmnn/src/pyarmnn/_version.py
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: MIT
 import os
 
-version_info = (24, 0, 0)
+version_info = (25, 0, 0)
 
 __dev_version_env = os.getenv("PYARMNN_DEV_VER", "")
 
@@ -24,7 +24,7 @@
     """Compares expected Arm NN version and Arm NN version used to build the package.
 
     Args:
-        installed_armnn_version (str): Arm NN version used to generate the package (e.g. 24.0.0)
+        installed_armnn_version (str): Arm NN version used to generate the package (e.g. 25.0.0)
         expected_armnn_version (str): Expected Arm NN version
 
     Returns:
diff --git a/python/pyarmnn/test/test_setup.py b/python/pyarmnn/test/test_setup.py
index 19fd5f7..f47addb 100644
--- a/python/pyarmnn/test/test_setup.py
+++ b/python/pyarmnn/test/test_setup.py
@@ -87,15 +87,15 @@
 
 
 def test_armnn_version():
-    check_armnn_version('24.0.0', '24.0.0')
+    check_armnn_version('25.0.0', '25.0.0')
 
 
 def test_incorrect_armnn_version():
     with pytest.raises(AssertionError) as err:
-        check_armnn_version('24.0.0', '24.1.0')
+        check_armnn_version('25.0.0', '25.1.0')
 
-    assert 'Expected ArmNN version is 24.1.0 but installed ArmNN version is 24.0.0' in str(err.value)
+    assert 'Expected ArmNN version is 25.1.0 but installed ArmNN version is 25.0.0' in str(err.value)
 
 
 def test_armnn_version_patch_does_not_matter():
-    check_armnn_version('24.0.0', '24.0.1')
+    check_armnn_version('25.0.0', '25.0.1')
diff --git a/python/pyarmnn/test/test_version.py b/python/pyarmnn/test/test_version.py
index cfd8c88..ddb4abe 100644
--- a/python/pyarmnn/test/test_version.py
+++ b/python/pyarmnn/test/test_version.py
@@ -18,7 +18,7 @@
 
     importlib.reload(v)
 
-    assert "24.0.0.dev1" == v.__version__
+    assert "25.0.0.dev1" == v.__version__
 
     del os.environ["PYARMNN_DEV_VER"]
     del v
@@ -30,7 +30,7 @@
 
     importlib.reload(v)
 
-    assert "24.0.0" == v.__arm_ml_version__
+    assert "25.0.0" == v.__arm_ml_version__
 
     del os.environ["PYARMNN_DEV_VER"]
     del v
diff --git a/samples/ObjectDetection/Readme.md b/samples/ObjectDetection/Readme.md
index 59b4594..bceaa4b 100644
--- a/samples/ObjectDetection/Readme.md
+++ b/samples/ObjectDetection/Readme.md
@@ -168,10 +168,10 @@
 The full list of libs after cross-compilation to copy on your board:
 ```
 libarmnn.so
-libarmnn.so.24
-libarmnn.so.24.0
+libarmnn.so.25
+libarmnn.so.25.0
 libarmnnTfLiteParser.so
-libarmnnTfLiteParser.so.24.0
+libarmnnTfLiteParser.so.24.1
 libavcodec.so
 libavcodec.so.58
 libavcodec.so.58.54.100
diff --git a/src/armnn/BackendHelper.cpp b/src/armnn/BackendHelper.cpp
index 1467366..1c926f4 100644
--- a/src/armnn/BackendHelper.cpp
+++ b/src/armnn/BackendHelper.cpp
@@ -23,7 +23,21 @@
 
     auto factoryFunc = backendRegistry.GetFactory(backend);
     auto backendObject = factoryFunc();
-    return LayerSupportHandle(backendObject->GetLayerSupport());
+    return LayerSupportHandle(backendObject->GetLayerSupport(), backend);
+}
+
+/// Convenience function to check a capability on a backend
+bool IsCapabilitySupported(const armnn::BackendId& backend, armnn::BackendCapability capability)
+{
+    bool hasCapability = false;
+    auto const& backendRegistry = armnn::BackendRegistryInstance();
+    if (backendRegistry.IsBackendRegistered(backend))
+    {
+        auto factoryFunc = backendRegistry.GetFactory(backend);
+        auto backendObject = factoryFunc();
+        hasCapability = backendObject->HasCapability(capability);
+    }
+    return hasCapability;
 }
 
 bool LayerSupportHandle::IsBackendRegistered() const
@@ -293,6 +307,16 @@
                                                    const FullyConnectedDescriptor& descriptor,
                                                    Optional<std::string&> reasonIfUnsupported)
 {
+    if(!descriptor.m_ConstantWeights && !m_BackendId.IsUndefined())
+    {
+        bool result = false;
+        result = IsCapabilitySupported(m_BackendId, BackendCapability::NonConstWeights);
+        if (!result)
+        {
+            return result;
+        }
+    }
+
     return m_LayerSupport->IsFullyConnectedSupported(input,
                                                     output,
                                                     weights,
diff --git a/src/armnn/Descriptors.cpp b/src/armnn/Descriptors.cpp
index 881023e..706992c 100644
--- a/src/armnn/Descriptors.cpp
+++ b/src/armnn/Descriptors.cpp
@@ -425,4 +425,21 @@
 
 }
 
+uint32_t FullyConnectedDescriptor::GetNumViews() const
+{
+    // Return 1 with constant weights, otherwise check if bias is enabled
+    uint32_t numInputs = 1;
+    if (!m_ConstantWeights)
+    {
+        // non-const weights
+        numInputs = 2;
+        if (m_BiasEnabled)
+        {
+            // non-const bias
+            numInputs = 3;
+        }
+    }
+    return numInputs;
+}
+
 }
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 9373a6a..18a4d02 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -171,21 +171,26 @@
     return pNetworkImpl->AddFillLayer(fillDescriptor, name);
 }
 
-
 IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
                                                     const ConstTensor& weights,
                                                     const Optional<ConstTensor>& biases,
                                                     const char* name)
 {
-    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name);
+    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
+                                                armnn::Optional<ConstTensor>(weights),
+                                                biases,
+                                                name);
 }
 
 IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
                                                     const ConstTensor& weights,
                                                     const char* name)
 {
-    Optional<ConstTensor> biases;
-    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name);
+    armnn::Optional<ConstTensor> biases;
+    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
+                                                armnn::Optional<ConstTensor>(weights),
+                                                biases,
+                                                name);
 }
 
 IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
@@ -193,8 +198,18 @@
                                                     const ConstTensor& biases,
                                                     const char* name)
 {
-    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights,
-                                                armnn::Optional<ConstTensor>(biases), name);
+    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
+                                                armnn::Optional<ConstTensor>(weights),
+                                                armnn::Optional<ConstTensor>(biases),
+                                                name);
+}
+
+IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+                                                    const Optional<ConstTensor>& weights,
+                                                    const Optional<ConstTensor>& biases,
+                                                    const char* name)
+{
+    return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name);
 }
 
 IConnectableLayer* INetwork::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
@@ -1709,41 +1724,58 @@
 }
 
 IConnectableLayer* NetworkImpl::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
-                                                       const ConstTensor& weights,
-                                                       const Optional<ConstTensor>& biases,
-                                                       const char* name)
+                                                           const Optional<ConstTensor>& weights,
+                                                           const Optional<ConstTensor>& biases,
+                                                           const char* name)
 {
-    if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
+    if (fullyConnectedDescriptor.m_ConstantWeights && !weights.has_value())
     {
-        throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
+        throw InvalidArgumentException("AddFullyConnectedLayer: weights cannot be empty");
+
+        if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
+        {
+            throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
+        }
     }
 
     const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
 
-    layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
-
-    if (fullyConnectedDescriptor.m_BiasEnabled)
+    if (fullyConnectedDescriptor.m_ConstantWeights)
     {
-        layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
+        layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights.value());
+        if (fullyConnectedDescriptor.m_BiasEnabled)
+        {
+            layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
+        }
     }
 
     return layer;
 }
 
 IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+                                                       const Optional<ConstTensor>& weights,
+                                                       const Optional<ConstTensor>& biases,
+                                                       const char* name)
+{
+    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
+}
+
+IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
                                                    const ConstTensor& weights,
                                                    const Optional<ConstTensor>& biases,
                                                    const char* name)
 {
-    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
+    Optional<ConstTensor> optionalWeights(weights);
+    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name);
 }
 
 IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
                                                    const ConstTensor& weights,
                                                    const char* name)
 {
+    Optional<ConstTensor> optionalWeights(weights);
     Optional<ConstTensor> biases;
-    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
+    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name);
 }
 
 IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
@@ -1751,8 +1783,9 @@
                                                    const ConstTensor& biases,
                                                    const char* name)
 {
+    Optional<ConstTensor> optionalWeights(weights);
     Optional<ConstTensor> optionalBiases(biases);
-    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
+    return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, optionalBiases, name);
 }
 
 IConnectableLayer* NetworkImpl::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp
index 8f16be1..30941ca 100644
--- a/src/armnn/Network.hpp
+++ b/src/armnn/Network.hpp
@@ -104,6 +104,11 @@
                                     const char* name = nullptr);
 
     IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+                                              const Optional<ConstTensor>& weights,
+                                              const Optional<ConstTensor>& biases,
+                                              const char* name = nullptr);
+
+    IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
                                               const ConstTensor& weights,
                                               const Optional<ConstTensor>& biases,
                                               const char* name = nullptr);
@@ -265,7 +270,7 @@
 
 private:
     IConnectableLayer* AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
-                                                  const ConstTensor& weights,
+                                                  const Optional<ConstTensor>& weights,
                                                   const Optional<ConstTensor>& biases,
                                                   const char* name);
 
diff --git a/src/armnn/layers/FullyConnectedLayer.cpp b/src/armnn/layers/FullyConnectedLayer.cpp
index 0e5e594..6d0b57a 100644
--- a/src/armnn/layers/FullyConnectedLayer.cpp
+++ b/src/armnn/layers/FullyConnectedLayer.cpp
@@ -15,24 +15,25 @@
 {
 
 FullyConnectedLayer::FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name)
-    : LayerWithParameters(1, 1, LayerType::FullyConnected, param, name)
+    : LayerWithParameters(param.GetNumViews(), 1, LayerType::FullyConnected, param, name)
 {
 }
 
 std::unique_ptr<IWorkload> FullyConnectedLayer::CreateWorkload(const IWorkloadFactory& factory) const
 {
     // on this level constant data should not be released..
-    ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null.");
-
     FullyConnectedQueueDescriptor descriptor;
-
-    descriptor.m_Weight = m_Weight.get();
-    if (m_Param.m_BiasEnabled)
+    if (m_Param.m_ConstantWeights)
     {
-        ARMNN_ASSERT_MSG(m_Bias != nullptr, "FullyConnectedLayer: Bias data should not be null.");
-        descriptor.m_Bias = m_Bias.get();
-    }
+        ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null.");
+        descriptor.m_Weight = m_Weight.get();
 
+        if (m_Param.m_BiasEnabled)
+        {
+            ARMNN_ASSERT_MSG(m_Bias != nullptr, "FullyConnectedLayer: Bias data should not be null.");
+            descriptor.m_Bias = m_Bias.get();
+        }
+    }
     SetAdditionalInfo(descriptor);
 
     return factory.CreateFullyConnected(descriptor, PrepInfoAndDesc(descriptor));
@@ -41,13 +42,15 @@
 FullyConnectedLayer* FullyConnectedLayer::Clone(Graph& graph) const
 {
     auto layer = CloneBase<FullyConnectedLayer>(graph, m_Param, GetName());
-
-    layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
-    if (layer->m_Param.m_BiasEnabled)
+    if (m_Param.m_ConstantWeights)
     {
-        layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*m_Bias) : nullptr;
-    }
+        layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
 
+        if (layer->m_Param.m_BiasEnabled)
+        {
+            layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*m_Bias) : nullptr;
+        }
+    }
     return std::move(layer);
 }
 
@@ -70,11 +73,20 @@
 
     VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod);
 
-    // check if we m_Weight data is not nullptr
-    ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null.");
+    std::vector<TensorShape> inferredShapes;
+    if (m_Param.m_ConstantWeights)
+    {
+        // check if m_Weight data is not nullptr
+        ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null.");
 
-    auto inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
-                                             m_Weight->GetTensorInfo().GetShape() });
+        inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+                                            m_Weight->GetTensorInfo().GetShape()});
+    }
+    else
+    {
+        inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+                                            GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()});
+    }
 
     ARMNN_ASSERT(inferredShapes.size() == 1);
     ARMNN_ASSERT(inferredShapes[0].GetDimensionality() == Dimensionality::Specified);
@@ -89,27 +101,37 @@
 
 void FullyConnectedLayer::Accept(ILayerVisitor& visitor) const
 {
-    ConstTensor weightsTensor(m_Weight->GetTensorInfo(), m_Weight->Map(true));
+    Optional<ConstTensor> optionalWeightsTensor = EmptyOptional();
     Optional<ConstTensor> optionalBiasTensor = EmptyOptional();
-
-    if (GetParameters().m_BiasEnabled)
+    if(GetParameters().m_ConstantWeights)
     {
-        ConstTensor biasTensor(m_Bias->GetTensorInfo(), m_Bias->GetConstTensor<void>());
-        optionalBiasTensor = Optional<ConstTensor>(biasTensor);
-    }
+        ConstTensor weightsTensor(m_Weight->GetTensorInfo(), m_Weight->GetConstTensor<void>());
+        optionalWeightsTensor = Optional<ConstTensor>(weightsTensor);
 
-    visitor.VisitFullyConnectedLayer(this, GetParameters(), weightsTensor, optionalBiasTensor, GetName());
+        if (GetParameters().m_BiasEnabled)
+        {
+            ConstTensor biasTensor(m_Bias->GetTensorInfo(), m_Bias->GetConstTensor<void>());
+            optionalBiasTensor = Optional<ConstTensor>(biasTensor);
+        }
+    }
+    visitor.VisitFullyConnectedLayer(this,
+                                     GetParameters(),
+                                     optionalWeightsTensor.value(),
+                                     optionalBiasTensor,
+                                     GetName());
 }
 
 void FullyConnectedLayer::ExecuteStrategy(IStrategy& strategy) const
 {
-    std::vector<armnn::ConstTensor> constTensors { {m_Weight->GetTensorInfo(), m_Weight->Map(true)} };
-
-    if (GetParameters().m_BiasEnabled)
+    std::vector <armnn::ConstTensor> constTensors;
+    if(GetParameters().m_ConstantWeights)
     {
-        constTensors.emplace_back(ConstTensor(m_Bias->GetTensorInfo(), m_Bias->Map(true)));
+        constTensors.emplace_back(ConstTensor(m_Weight->GetTensorInfo(), m_Weight->Map(true)));
+        if (GetParameters().m_BiasEnabled)
+        {
+            constTensors.emplace_back(ConstTensor(m_Bias->GetTensorInfo(), m_Bias->Map(true)));
+        }
     }
-
     strategy.ExecuteStrategy(this, GetParameters(), constTensors, GetName());
 }
 
diff --git a/src/armnn/test/OptimizerTests.cpp b/src/armnn/test/OptimizerTests.cpp
index fa860ab..896fdfd 100644
--- a/src/armnn/test/OptimizerTests.cpp
+++ b/src/armnn/test/OptimizerTests.cpp
@@ -10,6 +10,7 @@
 #include <Network.hpp>
 #include <Optimizer.hpp>
 
+#include <armnn/BackendHelper.hpp>
 #include <armnn/BackendRegistry.hpp>
 #include <armnn/INetwork.hpp>
 #include <armnn/LayerVisitorBase.hpp>
@@ -679,6 +680,13 @@
     };
 };
 
+BOOST_AUTO_TEST_CASE(BackendCapabilityTest)
+{
+    BackendId backendId ="MockBackend";
+    // MockBackend does not support the NonConstWeights capability
+    BOOST_CHECK(!armnn::IsCapabilitySupported(backendId, armnn::BackendCapability::NonConstWeights));
+}
+
 BOOST_AUTO_TEST_CASE(BackendHintTest)
 {
     class TestBackendAssignment : public LayerVisitorBase<VisitorNoThrowPolicy>
diff --git a/src/armnn/test/UtilsTests.cpp b/src/armnn/test/UtilsTests.cpp
index 7776a2d..f0198cb 100644
--- a/src/armnn/test/UtilsTests.cpp
+++ b/src/armnn/test/UtilsTests.cpp
@@ -278,6 +278,18 @@
 
     BOOST_CHECK(layerSupportObject.IsBackendRegistered());
 }
+
+BOOST_AUTO_TEST_CASE(IsCapabilitySupported_CpuRef)
+{
+    BOOST_CHECK(armnn::IsCapabilitySupported(armnn::Compute::CpuRef, armnn::BackendCapability::NonConstWeights));
+}
+#endif
+
+#if defined(ARMCOMPUTENEON_ENABLED)
+BOOST_AUTO_TEST_CASE(IsCapabilitySupported_CpuAcc)
+{
+    BOOST_CHECK(!armnn::IsCapabilitySupported(armnn::Compute::CpuAcc, armnn::BackendCapability::NonConstWeights));
+}
 #endif
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp
index c347977..633c272 100644
--- a/src/armnnDeserializer/Deserializer.cpp
+++ b/src/armnnDeserializer/Deserializer.cpp
@@ -1841,7 +1841,6 @@
     CHECK_LAYERS(graph, 0, layerIndex);
     auto inputs = GetInputs(graph, layerIndex);
     CHECK_LOCATION();
-    CHECK_VALID_SIZE(inputs.size(), 1);
 
     auto outputs = GetOutputs(graph, layerIndex);
     CHECK_VALID_SIZE(outputs.size(), 1);
@@ -1853,20 +1852,36 @@
     armnn::FullyConnectedDescriptor fullyConnectedDescriptor;
     fullyConnectedDescriptor.m_BiasEnabled = flatBufferDescriptor->biasEnabled();
     fullyConnectedDescriptor.m_TransposeWeightMatrix = flatBufferDescriptor->transposeWeightsMatrix();
-
-    armnn::ConstTensor weightsTensor = ToConstTensor(flatBufferLayer->weights());
-
-    armnn::IConnectableLayer* layer;
-    armnn::Optional<armnn::ConstTensor> optionalBiases = armnn::EmptyOptional();
-    if (flatBufferDescriptor->biasEnabled())
+    fullyConnectedDescriptor.m_ConstantWeights = flatBufferDescriptor->constantWeights();
+    uint32_t numInputs = 1;
+    if (!fullyConnectedDescriptor.m_ConstantWeights)
     {
-        armnn::ConstTensor biasTensorData = ToConstTensor(flatBufferLayer->biases());
-        optionalBiases = armnn::Optional<armnn::ConstTensor>(biasTensorData);
+        numInputs = 2;
+        if (fullyConnectedDescriptor.m_BiasEnabled)
+        {
+            numInputs = 3;
+        }
     }
-    layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor,
-                                              weightsTensor,
-                                              optionalBiases,
-                                              layerName.c_str());
+    CHECK_VALID_SIZE(inputs.size(), numInputs);
+
+    armnn::Optional <armnn::ConstTensor> optionalWeights = armnn::EmptyOptional();
+    armnn::Optional<armnn::ConstTensor> optionalBiases = armnn::EmptyOptional();
+    if (fullyConnectedDescriptor.m_ConstantWeights)
+    {
+        armnn::ConstTensor weightsTensorData = ToConstTensor(flatBufferLayer->weights());
+        optionalWeights = armnn::Optional<armnn::ConstTensor>(weightsTensorData);
+
+        if (flatBufferDescriptor->biasEnabled())
+        {
+            armnn::ConstTensor biasTensorData = ToConstTensor(flatBufferLayer->biases());
+            optionalBiases = armnn::Optional<armnn::ConstTensor>(biasTensorData);
+        }
+    }
+
+    armnn::IConnectableLayer* layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor,
+                                                                        optionalWeights,
+                                                                        optionalBiases,
+                                                                        layerName.c_str());
 
     armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
     layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index e2b3a3c..88d66f7 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -321,6 +321,7 @@
 table FullyConnectedDescriptor {
     biasEnabled:bool = false;
     transposeWeightsMatrix:bool = false;
+    constantWeights:bool = true;
 }
 
 table GatherLayer {
diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h
index 524ffb0..99ab0dc 100644
--- a/src/armnnSerializer/ArmnnSchema_generated.h
+++ b/src/armnnSerializer/ArmnnSchema_generated.h
@@ -3504,7 +3504,8 @@
   typedef FullyConnectedDescriptorBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
     VT_BIASENABLED = 4,
-    VT_TRANSPOSEWEIGHTSMATRIX = 6
+    VT_TRANSPOSEWEIGHTSMATRIX = 6,
+    VT_CONSTANTWEIGHTS = 8
   };
   bool biasEnabled() const {
     return GetField<uint8_t>(VT_BIASENABLED, 0) != 0;
@@ -3512,10 +3513,14 @@
   bool transposeWeightsMatrix() const {
     return GetField<uint8_t>(VT_TRANSPOSEWEIGHTSMATRIX, 0) != 0;
   }
+  bool constantWeights() const {
+    return GetField<uint8_t>(VT_CONSTANTWEIGHTS, 1) != 0;
+  }
   bool Verify(flatbuffers::Verifier &verifier) const {
     return VerifyTableStart(verifier) &&
            VerifyField<uint8_t>(verifier, VT_BIASENABLED) &&
            VerifyField<uint8_t>(verifier, VT_TRANSPOSEWEIGHTSMATRIX) &&
+           VerifyField<uint8_t>(verifier, VT_CONSTANTWEIGHTS) &&
            verifier.EndTable();
   }
 };
@@ -3530,6 +3535,9 @@
   void add_transposeWeightsMatrix(bool transposeWeightsMatrix) {
     fbb_.AddElement<uint8_t>(FullyConnectedDescriptor::VT_TRANSPOSEWEIGHTSMATRIX, static_cast<uint8_t>(transposeWeightsMatrix), 0);
   }
+  void add_constantWeights(bool constantWeights) {
+    fbb_.AddElement<uint8_t>(FullyConnectedDescriptor::VT_CONSTANTWEIGHTS, static_cast<uint8_t>(constantWeights), 1);
+  }
   explicit FullyConnectedDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
         : fbb_(_fbb) {
     start_ = fbb_.StartTable();
@@ -3545,8 +3553,10 @@
 inline flatbuffers::Offset<FullyConnectedDescriptor> CreateFullyConnectedDescriptor(
     flatbuffers::FlatBufferBuilder &_fbb,
     bool biasEnabled = false,
-    bool transposeWeightsMatrix = false) {
+    bool transposeWeightsMatrix = false,
+    bool constantWeights = true) {
   FullyConnectedDescriptorBuilder builder_(_fbb);
+  builder_.add_constantWeights(constantWeights);
   builder_.add_transposeWeightsMatrix(transposeWeightsMatrix);
   builder_.add_biasEnabled(biasEnabled);
   return builder_.Finish();
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index 0586700..ae9ddf2 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -1118,12 +1118,8 @@
 void SerializerStrategy::SerializeFullyConnectedLayer(const armnn::IConnectableLayer* layer,
                                                       const armnn::FullyConnectedDescriptor& fullyConnectedDescriptor,
                                                       const std::vector<armnn::ConstTensor>& constants,
-                                                      const char* name)
+                                                      const char*)
 {
-    IgnoreUnused(name);
-
-    const armnn::ConstTensor& weights = constants.at(0);
-
     // Create FlatBuffer BaseLayer
     auto flatBufferBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_FullyConnected);
 
@@ -1131,17 +1127,23 @@
     auto flatBufferDescriptor =
         serializer::CreateFullyConnectedDescriptor(m_flatBufferBuilder,
                                                    fullyConnectedDescriptor.m_BiasEnabled,
-                                                   fullyConnectedDescriptor.m_TransposeWeightMatrix);
+                                                   fullyConnectedDescriptor.m_TransposeWeightMatrix,
+                                                   fullyConnectedDescriptor.m_ConstantWeights);
 
     // Create FlatBuffer weights data
-    auto flatBufferWeights = CreateConstTensorInfo(weights);
-
+    flatbuffers::Offset<serializer::ConstTensor> flatBufferWeights;
     // Create FlatBuffer bias data
     flatbuffers::Offset<serializer::ConstTensor> flatBufferBiases;
-    if (fullyConnectedDescriptor.m_BiasEnabled)
+    if (fullyConnectedDescriptor.m_ConstantWeights && !constants.empty())
     {
-        armnn::ConstTensor biases = constants.at(1);
-        flatBufferBiases = CreateConstTensorInfo(biases);
+        armnn::ConstTensor weights = constants.at(0);
+        flatBufferWeights = CreateConstTensorInfo(weights);
+
+        if (fullyConnectedDescriptor.m_BiasEnabled)
+        {
+            armnn::ConstTensor biases = constants.at(1);
+            flatBufferBiases = CreateConstTensorInfo(biases);
+        }
     }
 
     // Create FlatBuffer FullyConnectedLayer
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index f261731..d7c10cb 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -748,6 +748,7 @@
     armnn::FullyConnectedDescriptor descriptor;
     descriptor.m_BiasEnabled = true;
     descriptor.m_TransposeWeightMatrix = false;
+    descriptor.m_ConstantWeights = true;
 
     armnn::INetworkPtr network = armnn::INetwork::Create();
     armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
@@ -773,6 +774,53 @@
     deserializedNetwork->ExecuteStrategy(verifier);
 }
 
+BOOST_AUTO_TEST_CASE(SerializeFullyConnectedWeightsAsInputs)
+{
+    const std::string layerName("fullyConnected_weights_as_inputs");
+    const armnn::TensorInfo inputInfo ({ 2, 5, 1, 1 }, armnn::DataType::Float32);
+    const armnn::TensorInfo outputInfo({ 2, 3 }, armnn::DataType::Float32);
+
+    const armnn::TensorInfo weightsInfo({ 5, 3 }, armnn::DataType::Float32);
+    const armnn::TensorInfo biasesInfo ({ 3 }, armnn::DataType::Float32);
+
+    armnn::Optional<armnn::ConstTensor> weights = armnn::EmptyOptional();
+    armnn::Optional<armnn::ConstTensor> bias = armnn::EmptyOptional();
+
+    armnn::FullyConnectedDescriptor descriptor;
+    descriptor.m_BiasEnabled = true;
+    descriptor.m_TransposeWeightMatrix = false;
+    descriptor.m_ConstantWeights = false;
+
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+    armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
+    armnn::IConnectableLayer* const weightsInputLayer = network->AddInputLayer(1);
+    armnn::IConnectableLayer* const biasInputLayer = network->AddInputLayer(2);
+    armnn::IConnectableLayer* const fullyConnectedLayer =
+        network->AddFullyConnectedLayer(descriptor,
+                                        weights,
+                                        bias,
+                                        layerName.c_str());
+    armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
+
+    inputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(0));
+    weightsInputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(1));
+    biasInputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(2));
+    fullyConnectedLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+
+    inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
+    weightsInputLayer->GetOutputSlot(0).SetTensorInfo(weightsInfo);
+    biasInputLayer->GetOutputSlot(0).SetTensorInfo(biasesInfo);
+    fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network));
+    BOOST_CHECK(deserializedNetwork);
+
+    const std::vector<armnn::ConstTensor> constants {};
+    LayerVerifierBaseWithDescriptorAndConstants<armnn::FullyConnectedDescriptor> verifier(
+        layerName, {inputInfo, weightsInfo, biasesInfo}, {outputInfo}, descriptor, constants);
+    deserializedNetwork->ExecuteStrategy(verifier);
+}
+
 BOOST_AUTO_TEST_CASE(SerializeGather)
 {
     using GatherDescriptor = armnn::GatherDescriptor;
diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp
index 90db57f..2c5303c 100644
--- a/src/backends/backendsCommon/WorkloadData.cpp
+++ b/src/backends/backendsCommon/WorkloadData.cpp
@@ -1022,7 +1022,16 @@
 {
     const std::string descriptorName{"FullyConnectedQueueDescriptor"};
 
-    ValidateNumInputs(workloadInfo,  descriptorName, 1);
+    uint32_t numInputs = 1;
+    if (!m_Parameters.m_ConstantWeights)
+    {
+        numInputs = 2;
+        if (m_Parameters.m_BiasEnabled)
+        {
+            numInputs = 3;
+        }
+    }
+    ValidateNumInputs(workloadInfo, descriptorName, numInputs);
     ValidateNumOutputs(workloadInfo, descriptorName, 1);
 
     const TensorInfo& inputTensorInfo  = workloadInfo.m_InputTensorInfos[0];
@@ -1035,19 +1044,32 @@
         throw InvalidArgumentException(descriptorName + ": Input tensor must have 2 or 4 dimensions.");
     }
 
-    ValidatePointer(m_Weight, descriptorName, "weight");
-
-    const TensorInfo& weightTensorInfo = m_Weight->GetTensorInfo();
+    TensorInfo weightTensorInfo;
+    if (m_Parameters.m_ConstantWeights)
+    {
+        ValidatePointer(m_Weight, descriptorName, "weight");
+        weightTensorInfo = m_Weight->GetTensorInfo();
+    }
+    else
+    {
+        weightTensorInfo  = workloadInfo.m_InputTensorInfos[1];
+    }
     ValidateTensorNumDimensions(weightTensorInfo, descriptorName, 2, "weight");
 
     if (m_Parameters.m_BiasEnabled)
     {
-        ValidatePointer(m_Bias, descriptorName, "bias");
-
+        TensorInfo biasTensorInfo;
+        if (m_Parameters.m_ConstantWeights)
+        {
+            ValidatePointer(m_Bias, descriptorName, "bias");
+            biasTensorInfo = m_Bias->GetTensorInfo();
+        }
+        else
+        {
+            biasTensorInfo  = workloadInfo.m_InputTensorInfos[2];
+        }
         // Validates type and quantization values.
-        const TensorInfo& biasTensorInfo = m_Bias->GetTensorInfo();
         ValidateBiasTensorQuantization(biasTensorInfo, inputTensorInfo, weightTensorInfo, descriptorName);
-
         ValidateTensorDataType(biasTensorInfo, GetBiasDataType(inputTensorInfo.GetDataType()), descriptorName, "bias");
         ValidateTensorNumDimensions(biasTensorInfo, descriptorName, 1, "bias");
     }
diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp
index 19281a8..20d7134 100644
--- a/src/backends/backendsCommon/WorkloadFactory.cpp
+++ b/src/backends/backendsCommon/WorkloadFactory.cpp
@@ -9,6 +9,7 @@
 #include <armnn/Types.hpp>
 #include <armnn/LayerSupport.hpp>
 #include <armnn/ILayerSupport.hpp>
+#include <armnn/BackendHelper.hpp>
 #include <armnn/BackendRegistry.hpp>
 #include <armnn/utility/PolymorphicDowncast.hpp>
 #include <armnn/utility/TransformIterator.hpp>
@@ -63,7 +64,7 @@
 
     auto backendFactory = backendRegistry.GetFactory(backendId);
     auto backendObject = backendFactory();
-    auto layerSupportObject = backendObject->GetLayerSupport(modelOptions);
+    auto layerSupportObject = LayerSupportHandle(backendObject->GetLayerSupport(modelOptions), backendId);
 
     switch(layer.GetType())
     {
@@ -72,7 +73,7 @@
             auto cLayer = PolymorphicDowncast<const ActivationLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsActivationSupported(
+            result = layerSupportObject.IsActivationSupported(
                                            OverrideDataType(input, dataType),
                                            OverrideDataType(output, dataType),
                                            cLayer->GetParameters(),
@@ -84,7 +85,7 @@
             const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsAdditionSupported(
+            result = layerSupportObject.IsAdditionSupported(
                                         OverrideDataType(input0, dataType),
                                         OverrideDataType(input1, dataType),
                                         OverrideDataType(output, dataType),
@@ -98,7 +99,7 @@
 
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsArgMinMaxSupported(
+            result = layerSupportObject.IsArgMinMaxSupported(
                     OverrideDataType(input, dataType),
                     OverrideDataType(output, DataType::Signed32),
                     descriptor,
@@ -114,7 +115,7 @@
             const TensorInfo& var = cLayer->m_Variance->GetTensorInfo();
             const TensorInfo& beta = cLayer->m_Beta->GetTensorInfo();
             const TensorInfo& gamma = cLayer->m_Gamma->GetTensorInfo();
-            result = layerSupportObject->IsBatchNormalizationSupported(
+            result = layerSupportObject.IsBatchNormalizationSupported(
                                                    OverrideDataType(input, dataType),
                                                    OverrideDataType(output, dataType),
                                                    OverrideDataType(mean, dataType),
@@ -131,10 +132,10 @@
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
             auto cLayer = PolymorphicDowncast<const BatchToSpaceNdLayer*>(&layer);
 
-            result = layerSupportObject->IsBatchToSpaceNdSupported(OverrideDataType(input, dataType),
-                                                                   OverrideDataType(output, dataType),
-                                                                   cLayer->GetParameters(),
-                                                                   reason);
+            result = layerSupportObject.IsBatchToSpaceNdSupported(OverrideDataType(input, dataType),
+                                                                  OverrideDataType(output, dataType),
+                                                                  cLayer->GetParameters(),
+                                                                  reason);
             break;
         }
         case LayerType::Comparison:
@@ -145,45 +146,45 @@
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsComparisonSupported(OverrideDataType(input0, dataType),
-                                                               OverrideDataType(input1, dataType),
-                                                               OverrideDataType(output, DataType::Boolean),
-                                                               cLayer->GetParameters(),
-                                                               reason);
+            result = layerSupportObject.IsComparisonSupported(OverrideDataType(input0, dataType),
+                                                              OverrideDataType(input1, dataType),
+                                                              OverrideDataType(output, DataType::Boolean),
+                                                              cLayer->GetParameters(),
+                                                              reason);
             break;
         }
         case LayerType::Constant:
         {
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsConstantSupported(OverrideDataType(output, dataType), reason);
+            result = layerSupportObject.IsConstantSupported(OverrideDataType(output, dataType), reason);
             break;
         }
         case LayerType::ConvertBf16ToFp32:
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsConvertBf16ToFp32Supported(input, output, reason);
+            result = layerSupportObject.IsConvertBf16ToFp32Supported(input, output, reason);
             break;
         }
         case LayerType::ConvertFp16ToFp32:
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsConvertFp16ToFp32Supported(input, output, reason);
+            result = layerSupportObject.IsConvertFp16ToFp32Supported(input, output, reason);
             break;
         }
         case LayerType::ConvertFp32ToBf16:
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsConvertFp32ToBf16Supported(input, output, reason);
+            result = layerSupportObject.IsConvertFp32ToBf16Supported(input, output, reason);
             break;
         }
         case LayerType::ConvertFp32ToFp16:
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsConvertFp32ToFp16Supported(input, output, reason);
+            result = layerSupportObject.IsConvertFp32ToFp16Supported(input, output, reason);
             break;
         }
         case LayerType::Convolution2d:
@@ -205,7 +206,7 @@
                     OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
             }
 
-            result = layerSupportObject->IsConvolution2dSupported(
+            result = layerSupportObject.IsConvolution2dSupported(
                                               input,
                                               output,
                                               descriptor,
@@ -219,7 +220,7 @@
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsDebugSupported(OverrideDataType(input, dataType),
+            result = layerSupportObject.IsDebugSupported(OverrideDataType(input, dataType),
                                                           OverrideDataType(output, dataType),
                                                           reason);
             break;
@@ -231,7 +232,7 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsDepthToSpaceSupported(OverrideDataType(input, dataType),
+            result = layerSupportObject.IsDepthToSpaceSupported(OverrideDataType(input, dataType),
                                                                  OverrideDataType(output, dataType),
                                                                  cLayer->GetParameters(),
                                                                  reason);
@@ -255,7 +256,7 @@
                     OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
             }
 
-            result = layerSupportObject->IsDepthwiseConvolutionSupported(
+            result = layerSupportObject.IsDepthwiseConvolutionSupported(
                                                      input,
                                                      output,
                                                      descriptor,
@@ -269,9 +270,9 @@
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsDequantizeSupported(input,
-                                                               OverrideDataType(output, dataType),
-                                                               reason);
+            result = layerSupportObject.IsDequantizeSupported(input,
+                                                              OverrideDataType(output, dataType),
+                                                              reason);
             break;
         }
         case LayerType::DetectionPostProcess:
@@ -287,15 +288,15 @@
             const TensorInfo& numDetections = layer.GetOutputSlot(3).GetTensorInfo();
 
             const DetectionPostProcessDescriptor& descriptor = cLayer->GetParameters();
-            result = layerSupportObject->IsDetectionPostProcessSupported(boxEncodings,
-                                                                         scores,
-                                                                         anchors,
-                                                                         detectionBoxes,
-                                                                         detectionClasses,
-                                                                         detectionScores,
-                                                                         numDetections,
-                                                                         descriptor,
-                                                                         reason);
+            result = layerSupportObject.IsDetectionPostProcessSupported(boxEncodings,
+                                                                        scores,
+                                                                        anchors,
+                                                                        detectionBoxes,
+                                                                        detectionClasses,
+                                                                        detectionScores,
+                                                                        numDetections,
+                                                                        descriptor,
+                                                                        reason);
             break;
         }
         case LayerType::ElementwiseUnary:
@@ -305,10 +306,10 @@
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsElementwiseUnarySupported(OverrideDataType(input, dataType),
-                                                                     OverrideDataType(output, dataType),
-                                                                     cLayer->GetParameters(),
-                                                                     reason);
+            result = layerSupportObject.IsElementwiseUnarySupported(OverrideDataType(input, dataType),
+                                                                    OverrideDataType(output, dataType),
+                                                                    cLayer->GetParameters(),
+                                                                    reason);
             break;
         }
         case LayerType::Fill:
@@ -318,7 +319,7 @@
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
             const FillDescriptor& descriptor = cLayer->GetParameters();
 
-            result = layerSupportObject->IsFillSupported(
+            result = layerSupportObject.IsFillSupported(
                 OverrideDataType(input, dataType),
                 OverrideDataType(output, dataType),
                 descriptor,
@@ -329,18 +330,18 @@
         {
             auto cLayer = PolymorphicDowncast<const FakeQuantizationLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
-            result = layerSupportObject->IsFakeQuantizationSupported(OverrideDataType(input, dataType),
-                                                                     cLayer->GetParameters(),
-                                                                     reason);
+            result = layerSupportObject.IsFakeQuantizationSupported(OverrideDataType(input, dataType),
+                                                                    cLayer->GetParameters(),
+                                                                    reason);
             break;
         }
         case LayerType::Floor:
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsFloorSupported(OverrideDataType(input, dataType),
-                                                          OverrideDataType(output, dataType),
-                                                          reason);
+            result = layerSupportObject.IsFloorSupported(OverrideDataType(input, dataType),
+                                                         OverrideDataType(output, dataType),
+                                                         reason);
             break;
         }
         case LayerType::FullyConnected:
@@ -348,21 +349,43 @@
             auto cLayer = PolymorphicDowncast<const FullyConnectedLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
+
+            const FullyConnectedDescriptor& descriptor = cLayer->GetParameters();
+            TensorInfo weightsInfo;
+            const TensorInfo* weightsInfoPtr = nullptr;
+
+            if (descriptor.m_ConstantWeights)
+            {
+                ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
+                weightsInfo = OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType);
+            }
+            else
+            {
+                weightsInfo = OverrideDataType(layer.GetInputSlot(1).GetConnection()->GetTensorInfo(), dataType);
+
+            }
+            weightsInfoPtr = &weightsInfo;
 
             TensorInfo biasInfo;
-            const TensorInfo * biasInfoPtr = nullptr;
+            const TensorInfo* biasInfoPtr = nullptr;
             static const TensorInfo dummyBFloat16Bias(TensorShape({1,1,1,1}), DataType::BFloat16);
             static const TensorInfo dummyFloat16Bias(TensorShape({1,1,1,1}), DataType::Float16);
             static const TensorInfo dummyFloat32Bias(TensorShape({1,1,1,1}), DataType::Float32);
             static const TensorInfo dummyQA8Bias(TensorShape({1,1,1,1}), DataType::Signed32);
 
-            const FullyConnectedDescriptor& descriptor = cLayer->GetParameters();
             if (descriptor.m_BiasEnabled)
             {
-                ARMNN_ASSERT(cLayer->m_Bias.get() != nullptr);
-                biasInfo = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
-                biasInfoPtr = &biasInfo;
+                if(descriptor.m_ConstantWeights)
+                {
+                    ARMNN_ASSERT(cLayer->m_Bias.get() != nullptr);
+                    biasInfo = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
+                    biasInfoPtr = &biasInfo;
+                }
+                else
+                {
+                    biasInfo = OverrideDataType(layer.GetInputSlot(2).GetConnection()->GetTensorInfo(), dataType);
+                    biasInfoPtr = &biasInfo;
+                }
             }
             else
             {
@@ -398,11 +421,10 @@
                     }
                 }
             }
-
-            result = layerSupportObject->IsFullyConnectedSupported(
+            result = layerSupportObject.IsFullyConnectedSupported(
                                                OverrideDataType(input, dataType),
                                                OverrideDataType(output, dataType),
-                                               OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType),
+                                               *weightsInfoPtr,
                                                *biasInfoPtr,
                                                descriptor,
                                                reason);
@@ -415,17 +437,17 @@
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
             auto cLayer = PolymorphicDowncast<const GatherLayer*>(&layer);
             const GatherDescriptor& descriptor = cLayer->GetParameters();
-            result = layerSupportObject->IsGatherSupported(OverrideDataType(input0, dataType),
-                                                           input1,
-                                                           OverrideDataType(output, dataType),
-                                                           descriptor,
-                                                           reason);
+            result = layerSupportObject.IsGatherSupported(OverrideDataType(input0, dataType),
+                                                          input1,
+                                                          OverrideDataType(output, dataType),
+                                                          descriptor,
+                                                          reason);
             break;
         }
         case LayerType::Input:
         {
             const TensorInfo& input = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsInputSupported(OverrideDataType(input, dataType), reason);
+            result = layerSupportObject.IsInputSupported(OverrideDataType(input, dataType), reason);
             break;
         }
         case LayerType::InstanceNormalization:
@@ -436,7 +458,7 @@
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsInstanceNormalizationSupported(
+            result = layerSupportObject.IsInstanceNormalizationSupported(
                 OverrideDataType(input, dataType),
                 OverrideDataType(output, dataType),
                 descriptor,
@@ -451,7 +473,7 @@
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsL2NormalizationSupported(
+            result = layerSupportObject.IsL2NormalizationSupported(
                                                 OverrideDataType(input, dataType),
                                                 OverrideDataType(output, dataType),
                                                 descriptor,
@@ -466,11 +488,11 @@
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsLogicalBinarySupported(input0,
-                                                                  input1,
-                                                                  output,
-                                                                  cLayer->GetParameters(),
-                                                                  reason);
+            result = layerSupportObject.IsLogicalBinarySupported(input0,
+                                                                 input1,
+                                                                 output,
+                                                                 cLayer->GetParameters(),
+                                                                 reason);
             break;
         }
         case LayerType::LogSoftmax:
@@ -480,10 +502,10 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsLogSoftmaxSupported(OverrideDataType(input, dataType),
-                                                               OverrideDataType(output, dataType),
-                                                               cLayer->GetParameters(),
-                                                               reason);
+            result = layerSupportObject.IsLogSoftmaxSupported(OverrideDataType(input, dataType),
+                                                              OverrideDataType(output, dataType),
+                                                              cLayer->GetParameters(),
+                                                              reason);
             break;
         }
         case LayerType::Lstm:
@@ -617,7 +639,7 @@
                 paramsInfo.m_OutputLayerNormWeights = &optOutputLayerNormWeights;
             }
 
-            result = layerSupportObject->IsLstmSupported(
+            result = layerSupportObject.IsLstmSupported(
                                      input,
                                      outputStateIn,
                                      cellStateIn,
@@ -636,10 +658,10 @@
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsMaximumSupported(OverrideDataType(input0, dataType),
-                                                            OverrideDataType(input1, dataType),
-                                                            OverrideDataType(output, dataType),
-                                                            reason);
+            result = layerSupportObject.IsMaximumSupported(OverrideDataType(input0, dataType),
+                                                           OverrideDataType(input1, dataType),
+                                                           OverrideDataType(output, dataType),
+                                                           reason);
             break;
         }
         case LayerType::MemCopy:
@@ -647,9 +669,9 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsMemCopySupported(OverrideDataType(input, dataType),
-                                                            OverrideDataType(output, dataType),
-                                                            reason);
+            result = layerSupportObject.IsMemCopySupported(OverrideDataType(input, dataType),
+                                                           OverrideDataType(output, dataType),
+                                                           reason);
             break;
         }
         case LayerType::MemImport:
@@ -657,9 +679,9 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsMemImportSupported(OverrideDataType(input, dataType),
-                                                              OverrideDataType(output, dataType),
-                                                              reason);
+            result = layerSupportObject.IsMemImportSupported(OverrideDataType(input, dataType),
+                                                             OverrideDataType(output, dataType),
+                                                             reason);
             break;
         }
         case LayerType::Merge:
@@ -668,10 +690,10 @@
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsMergeSupported(OverrideDataType(input0, dataType),
-                                                          OverrideDataType(input1, dataType),
-                                                          OverrideDataType(output, dataType),
-                                                          reason);
+            result = layerSupportObject.IsMergeSupported(OverrideDataType(input0, dataType),
+                                                         OverrideDataType(input1, dataType),
+                                                         OverrideDataType(output, dataType),
+                                                         reason);
             break;
         }
         case LayerType::Concat:
@@ -699,7 +721,7 @@
 
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsConcatSupported(inputPtrs, output, cLayer->GetParameters(), reason);
+            result = layerSupportObject.IsConcatSupported(inputPtrs, output, cLayer->GetParameters(), reason);
 
 
             break;
@@ -709,7 +731,7 @@
             const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsMultiplicationSupported(
+            result = layerSupportObject.IsMultiplicationSupported(
                                                OverrideDataType(input0, dataType),
                                                OverrideDataType(input1, dataType),
                                                OverrideDataType(output, dataType),
@@ -721,16 +743,16 @@
             auto cLayer = PolymorphicDowncast<const NormalizationLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsNormalizationSupported(OverrideDataType(input, dataType),
-                                                                  OverrideDataType(output, dataType),
-                                                                  cLayer->GetParameters(),
-                                                                  reason);
+            result = layerSupportObject.IsNormalizationSupported(OverrideDataType(input, dataType),
+                                                                 OverrideDataType(output, dataType),
+                                                                 cLayer->GetParameters(),
+                                                                 reason);
             break;
         }
         case LayerType::Output:
         {
             const TensorInfo& output = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
-            result = layerSupportObject->IsOutputSupported(OverrideDataType(output, dataType), reason);
+            result = layerSupportObject.IsOutputSupported(OverrideDataType(output, dataType), reason);
             break;
         }
         case LayerType::Permute:
@@ -738,10 +760,10 @@
             auto cLayer = PolymorphicDowncast<const PermuteLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsPermuteSupported(OverrideDataType(input, dataType),
-                                                            OverrideDataType(output, dataType),
-                                                            cLayer->GetParameters(),
-                                                            reason);
+            result = layerSupportObject.IsPermuteSupported(OverrideDataType(input, dataType),
+                                                           OverrideDataType(output, dataType),
+                                                           cLayer->GetParameters(),
+                                                           reason);
             break;
         }
         case LayerType::Pad:
@@ -749,7 +771,7 @@
             auto cLayer = PolymorphicDowncast<const PadLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsPadSupported(
+            result = layerSupportObject.IsPadSupported(
                                     OverrideDataType(input, dataType),
                                     OverrideDataType(output, dataType),
                                     cLayer->GetParameters(),
@@ -761,26 +783,26 @@
             auto cLayer = PolymorphicDowncast<const Pooling2dLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsPooling2dSupported(OverrideDataType(input, dataType),
-                                                              OverrideDataType(output, dataType),
-                                                              cLayer->GetParameters(),
-                                                              reason);
+            result = layerSupportObject.IsPooling2dSupported(OverrideDataType(input, dataType),
+                                                             OverrideDataType(output, dataType),
+                                                             cLayer->GetParameters(),
+                                                             reason);
             break;
         }
         case LayerType::PreCompiled:
         {
             auto cLayer = PolymorphicDowncast<const PreCompiledLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
-            result = layerSupportObject->IsPreCompiledSupported(OverrideDataType(input, dataType),
-                                                                cLayer->GetParameters(),
-                                                                reason);
+            result = layerSupportObject.IsPreCompiledSupported(OverrideDataType(input, dataType),
+                                                               cLayer->GetParameters(),
+                                                               reason);
             break;
         }
         case LayerType::Quantize:
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsQuantizeSupported(input, output, reason);
+            result = layerSupportObject.IsQuantizeSupported(input, output, reason);
             break;
         }
         case LayerType::QLstm:
@@ -865,15 +887,15 @@
                         &cLayer->m_LayerNormParameters.m_OutputLayerNormWeights->GetTensorInfo();
             }
 
-            result = layerSupportObject->IsQLstmSupported(input,
-                                                          previousOutputIn,
-                                                          previousCellStateIn,
-                                                          outputStateOut,
-                                                          cellStateOut,
-                                                          output,
-                                                          descriptor,
-                                                          paramsInfo,
-                                                          reason);
+            result = layerSupportObject.IsQLstmSupported(input,
+                                                         previousOutputIn,
+                                                         previousCellStateIn,
+                                                         outputStateOut,
+                                                         cellStateOut,
+                                                         output,
+                                                         descriptor,
+                                                         paramsInfo,
+                                                         reason);
             break;
         }
         case LayerType::QuantizedLstm:
@@ -919,13 +941,13 @@
             paramsInfo.m_OutputGateBias           =
                     &cLayer->m_QuantizedLstmParameters.m_OutputGateBias->GetTensorInfo();;
 
-            result = layerSupportObject->IsQuantizedLstmSupported(input,
-                                                                  previousCellStateIn,
-                                                                  previousOutputIn,
-                                                                  cellStateOut,
-                                                                  output,
-                                                                  paramsInfo,
-                                                                  reason);
+            result = layerSupportObject.IsQuantizedLstmSupported(input,
+                                                                 previousCellStateIn,
+                                                                 previousOutputIn,
+                                                                 cellStateOut,
+                                                                 output,
+                                                                 paramsInfo,
+                                                                 reason);
             break;
         }
         case LayerType::Division:
@@ -933,7 +955,7 @@
             const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsDivisionSupported(
+            result = layerSupportObject.IsDivisionSupported(
                                          OverrideDataType(input0, dataType),
                                          OverrideDataType(input1, dataType),
                                          OverrideDataType(output, dataType),
@@ -944,9 +966,9 @@
         {
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsRankSupported(OverrideDataType(input, dataType),
-                                                         OverrideDataType(output, dataType),
-                                                         reason);
+            result = layerSupportObject.IsRankSupported(OverrideDataType(input, dataType),
+                                                        OverrideDataType(output, dataType),
+                                                        reason);
             break;
         }
         case LayerType::Reshape:
@@ -954,10 +976,10 @@
             auto cLayer = PolymorphicDowncast<const ReshapeLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsReshapeSupported(OverrideDataType(input, dataType),
-                                                            OverrideDataType(output, dataType),
-                                                            cLayer->GetParameters(),
-                                                            reason);
+            result = layerSupportObject.IsReshapeSupported(OverrideDataType(input, dataType),
+                                                           OverrideDataType(output, dataType),
+                                                           cLayer->GetParameters(),
+                                                           reason);
             break;
         }
         case LayerType::Resize:
@@ -965,10 +987,10 @@
             auto cLayer = PolymorphicDowncast<const ResizeLayer*>(&layer);
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsResizeSupported(OverrideDataType(input, dataType),
-                                                           OverrideDataType(output, dataType),
-                                                           cLayer->GetParameters(),
-                                                           reason);
+            result = layerSupportObject.IsResizeSupported(OverrideDataType(input, dataType),
+                                                          OverrideDataType(output, dataType),
+                                                          cLayer->GetParameters(),
+                                                          reason);
             break;
         }
         case LayerType::Slice:
@@ -978,10 +1000,10 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsSliceSupported(OverrideDataType(input, dataType),
-                                                          OverrideDataType(output, dataType),
-                                                          cLayer->GetParameters(),
-                                                          reason);
+            result = layerSupportObject.IsSliceSupported(OverrideDataType(input, dataType),
+                                                         OverrideDataType(output, dataType),
+                                                         cLayer->GetParameters(),
+                                                         reason);
             break;
         }
         case LayerType::Softmax:
@@ -989,10 +1011,10 @@
             auto cLayer = PolymorphicDowncast<const SoftmaxLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsSoftmaxSupported(OverrideDataType(input, dataType),
-                                                            OverrideDataType(output, dataType),
-                                                            cLayer->GetParameters(),
-                                                            reason);
+            result = layerSupportObject.IsSoftmaxSupported(OverrideDataType(input, dataType),
+                                                           OverrideDataType(output, dataType),
+                                                           cLayer->GetParameters(),
+                                                           reason);
             break;
         }
         case LayerType::SpaceToBatchNd:
@@ -1000,10 +1022,10 @@
             auto cLayer = PolymorphicDowncast<const SpaceToBatchNdLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsSpaceToBatchNdSupported(OverrideDataType(input, dataType),
-                                                                   OverrideDataType(output, dataType),
-                                                                   cLayer->GetParameters(),
-                                                                   reason);
+            result = layerSupportObject.IsSpaceToBatchNdSupported(OverrideDataType(input, dataType),
+                                                                  OverrideDataType(output, dataType),
+                                                                  cLayer->GetParameters(),
+                                                                  reason);
             break;
         }
         case LayerType::SpaceToDepth:
@@ -1013,10 +1035,10 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsSpaceToDepthSupported(OverrideDataType(input, dataType),
-                                                                 OverrideDataType(output, dataType),
-                                                                 cLayer->GetParameters(),
-                                                                 reason);
+            result = layerSupportObject.IsSpaceToDepthSupported(OverrideDataType(input, dataType),
+                                                                OverrideDataType(output, dataType),
+                                                                cLayer->GetParameters(),
+                                                                reason);
             break;
         }
         case LayerType::Splitter:
@@ -1035,10 +1057,10 @@
 
             const std::vector<std::reference_wrapper<TensorInfo>> outputPtrs(outputs.begin(), outputs.end());
 
-            result = layerSupportObject->IsSplitterSupported(OverrideDataType(input, dataType),
-                                                             outputPtrs,
-                                                             cLayer->GetParameters(),
-                                                             reason);
+            result = layerSupportObject.IsSplitterSupported(OverrideDataType(input, dataType),
+                                                            outputPtrs,
+                                                            cLayer->GetParameters(),
+                                                            reason);
             break;
         }
         case LayerType::Stack:
@@ -1064,7 +1086,7 @@
 
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsStackSupported(inputPtrs, output, cLayer->GetParameters(), reason);
+            result = layerSupportObject.IsStackSupported(inputPtrs, output, cLayer->GetParameters(), reason);
 
             break;
         }
@@ -1103,10 +1125,10 @@
             std::vector<const TensorInfo*> outputPtrs(beginPtrO, endPtrO);
 
 
-            result = layerSupportObject->IsStandInSupported(inputPtrs,
-                                                            outputPtrs,
-                                                            cLayer->GetParameters(),
-                                                            reason);
+            result = layerSupportObject.IsStandInSupported(inputPtrs,
+                                                           outputPtrs,
+                                                           cLayer->GetParameters(),
+                                                           reason);
             break;
         }
         case LayerType::StridedSlice:
@@ -1114,10 +1136,10 @@
             auto cLayer = PolymorphicDowncast<const StridedSliceLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsStridedSliceSupported(OverrideDataType(input, dataType),
-                                                                 OverrideDataType(output, dataType),
-                                                                 cLayer->GetParameters(),
-                                                                 reason);
+            result = layerSupportObject.IsStridedSliceSupported(OverrideDataType(input, dataType),
+                                                                OverrideDataType(output, dataType),
+                                                                cLayer->GetParameters(),
+                                                                reason);
             break;
         }
         case LayerType::Subtraction:
@@ -1125,7 +1147,7 @@
             const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsSubtractionSupported(
+            result = layerSupportObject.IsSubtractionSupported(
                                             OverrideDataType(input0, dataType),
                                             OverrideDataType(input1, dataType),
                                             OverrideDataType(output, dataType),
@@ -1138,11 +1160,11 @@
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output0 = layer.GetOutputSlot(0).GetTensorInfo();
             const TensorInfo& output1 = layer.GetOutputSlot(1).GetTensorInfo();
-            result = layerSupportObject->IsSwitchSupported(OverrideDataType(input0, dataType),
-                                                           OverrideDataType(input1, dataType),
-                                                           OverrideDataType(output0, dataType),
-                                                           OverrideDataType(output1, dataType),
-                                                           reason);
+            result = layerSupportObject.IsSwitchSupported(OverrideDataType(input0, dataType),
+                                                          OverrideDataType(input1, dataType),
+                                                          OverrideDataType(output0, dataType),
+                                                          OverrideDataType(output1, dataType),
+                                                          reason);
             break;
         }
         case LayerType::Mean:
@@ -1150,7 +1172,7 @@
             auto cLayer = PolymorphicDowncast<const MeanLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsMeanSupported(
+            result = layerSupportObject.IsMeanSupported(
                                      OverrideDataType(input, dataType),
                                      OverrideDataType(output, dataType),
                                      cLayer->GetParameters(),
@@ -1162,10 +1184,10 @@
             const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsMinimumSupported(OverrideDataType(input0, dataType),
-                                                            OverrideDataType(input1, dataType),
-                                                            OverrideDataType(output, dataType),
-                                                            reason);
+            result = layerSupportObject.IsMinimumSupported(OverrideDataType(input0, dataType),
+                                                           OverrideDataType(input1, dataType),
+                                                           OverrideDataType(output, dataType),
+                                                           reason);
             break;
         }
         case LayerType::Prelu:
@@ -1173,10 +1195,10 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& alpha  = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsPreluSupported(OverrideDataType(input,  dataType),
-                                                          OverrideDataType(alpha,  dataType),
-                                                          OverrideDataType(output, dataType),
-                                                          reason);
+            result = layerSupportObject.IsPreluSupported(OverrideDataType(input,  dataType),
+                                                         OverrideDataType(alpha,  dataType),
+                                                         OverrideDataType(output, dataType),
+                                                         reason);
             break;
         }
         case LayerType::Transpose:
@@ -1184,10 +1206,10 @@
             auto cLayer = PolymorphicDowncast<const TransposeLayer*>(&layer);
             const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
-            result = layerSupportObject->IsTransposeSupported(OverrideDataType(input, dataType),
-                                                              OverrideDataType(output, dataType),
-                                                              cLayer->GetParameters(),
-                                                              reason);
+            result = layerSupportObject.IsTransposeSupported(OverrideDataType(input, dataType),
+                                                             OverrideDataType(output, dataType),
+                                                             cLayer->GetParameters(),
+                                                             reason);
             break;
         }
         case LayerType::TransposeConvolution2d:
@@ -1211,12 +1233,12 @@
             ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
             const TensorInfo weights = OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType);
 
-            result = layerSupportObject->IsTransposeConvolution2dSupported(input,
-                                                                           output,
-                                                                           descriptor,
-                                                                           weights,
-                                                                           biases,
-                                                                           reason);
+            result = layerSupportObject.IsTransposeConvolution2dSupported(input,
+                                                                          output,
+                                                                          descriptor,
+                                                                          weights,
+                                                                          biases,
+                                                                          reason);
 
             break;
         }
@@ -1226,10 +1248,10 @@
             const TensorInfo& input  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
             const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
 
-            result = layerSupportObject->IsReduceSupported(OverrideDataType(input, dataType),
-                                                           OverrideDataType(output, dataType),
-                                                           cLayer->GetParameters(),
-                                                           reason);
+            result = layerSupportObject.IsReduceSupported(OverrideDataType(input, dataType),
+                                                          OverrideDataType(output, dataType),
+                                                          cLayer->GetParameters(),
+                                                          reason);
             break;
         }
         default:
diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt
index f92e074..d3857b8 100644
--- a/src/backends/backendsCommon/test/CMakeLists.txt
+++ b/src/backends/backendsCommon/test/CMakeLists.txt
@@ -23,6 +23,7 @@
     ElementwiseUnaryEndToEndTestImpl.hpp
     EndToEndTestImpl.hpp
     FillEndToEndTestImpl.hpp
+    FullyConnectedEndToEndTestImpl.hpp
     GatherEndToEndTestImpl.hpp
     InstanceNormalizationEndToEndTestImpl.cpp
     InstanceNormalizationEndToEndTestImpl.hpp
diff --git a/src/backends/backendsCommon/test/CompatibilityTests.cpp b/src/backends/backendsCommon/test/CompatibilityTests.cpp
index b69e112..1c4ff70 100644
--- a/src/backends/backendsCommon/test/CompatibilityTests.cpp
+++ b/src/backends/backendsCommon/test/CompatibilityTests.cpp
@@ -7,6 +7,7 @@
 
 #include <cl/ClBackend.hpp>
 #include <neon/NeonBackend.hpp>
+#include <reference/RefBackend.hpp>
 
 #include <Network.hpp>
 
@@ -115,3 +116,18 @@
 }
 
 BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(BackendCapability)
+
+BOOST_AUTO_TEST_CASE(Backends_Capability_Test)
+{
+    auto neonBackend = std::make_unique<NeonBackend>();
+    auto clBackend   = std::make_unique<ClBackend>();
+    auto refBackend  = std::make_unique<RefBackend>();
+
+    BOOST_CHECK(!neonBackend->HasCapability(armnn::BackendCapability::NonConstWeights));
+    BOOST_CHECK(!clBackend->HasCapability(armnn::BackendCapability::NonConstWeights));
+    BOOST_CHECK(refBackend->HasCapability(armnn::BackendCapability::NonConstWeights));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp
new file mode 100644
index 0000000..5a618c3
--- /dev/null
+++ b/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp
@@ -0,0 +1,97 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include "CommonTestUtils.hpp"
+
+#include <ResolveType.hpp>
+
+#include <armnn/INetwork.hpp>
+
+#include <armnn/utility/NumericCast.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include <vector>
+
+namespace
+{
+
+armnn::INetworkPtr CreateFullyConnectedNetworkNonConstWeights(const armnn::TensorInfo& inputTensorInfo,
+                                                              const armnn::TensorInfo& outputTensorInfo,
+                                                              const armnn::TensorInfo& weightsTensorInfo,
+                                                              armnn::FullyConnectedDescriptor descriptor)
+{
+    armnn::INetworkPtr network(armnn::INetwork::Create());
+
+    armnn::IConnectableLayer* inputLayer  = network->AddInputLayer(0, "Input");
+    armnn::IConnectableLayer* weightsInputLayer   = network->AddInputLayer(1, "Weights_Input");
+    armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor,
+                                                                                    armnn::EmptyOptional(),
+                                                                                    armnn::EmptyOptional(),
+                                                                                    "Fully_Connected");
+    armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
+
+    Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
+    Connect(weightsInputLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
+    Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
+
+    return network;
+}
+
+template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector<armnn::BackendId>& backends)
+{
+    using namespace armnn;
+
+    armnn::TensorInfo inputTensorInfo({ 1, 1, 2, 3 }, ArmnnType);
+    inputTensorInfo.SetQuantizationScale(0.1f);
+    inputTensorInfo.SetQuantizationOffset(63);
+
+    armnn::TensorInfo outputTensorInfo({ 1, 2 }, ArmnnType);
+    outputTensorInfo.SetQuantizationScale(5.f);
+    outputTensorInfo.SetQuantizationOffset(10);
+
+    armnn::TensorInfo weightsTensorInfo({ 2, 6 }, ArmnnType);
+    weightsTensorInfo.SetQuantizationScale(0.2f);
+    weightsTensorInfo.SetQuantizationOffset(93);
+
+    FullyConnectedDescriptor descriptor;
+    descriptor.m_ConstantWeights = false;
+    descriptor.m_BiasEnabled     = false;
+    descriptor.m_TransposeWeightMatrix = true;
+
+    std::vector<T> inputData {
+        -1.2f, 6.1f, -3.5f,
+        18.8f, -5.5f, 2.9f
+    };
+
+    std::vector<T> weightsData {
+        -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f,
+        23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f
+    };
+
+    std::vector<T> floatExpectedOutputData {
+        -107.04f, 110.f
+    };
+    std::vector<T> expectedOutputData = armnnUtils::QuantizedVector<T>(floatExpectedOutputData);
+
+    armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo,
+                                                                            outputTensorInfo,
+                                                                            weightsTensorInfo,
+                                                                            descriptor);
+
+    BOOST_TEST_CHECKPOINT("create a network");
+
+    std::map<int, std::vector<T>> inputTensorData    = {{ 0, inputData }, {1, weightsData}};
+    std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutputData }};
+
+    EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
+                                                inputTensorData,
+                                                expectedOutputTensorData,
+                                                backends,
+                                                1.0f);
+}
+} // anonymous namespace
diff --git a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp
index c9e2e16..9176094 100644
--- a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp
+++ b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp
@@ -67,12 +67,70 @@
     return result;
 }
 
+template<typename T, typename B>
+LayerTestResult<T, 2> SimpleFullyConnectedTestWeightsAsInputsImpl(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+    const armnn::ITensorHandleFactory& tensorHandleFactory,
+    armnn::TensorInfo inputTensorInfo,
+    armnn::TensorInfo outputTensorInfo,
+    armnn::TensorInfo weightsTensorInfo,
+    armnn::TensorInfo biasesTensorInfo,
+    boost::multi_array<T, 2>& weights,
+    boost::multi_array<B, 1>& bias,
+    boost::multi_array<T, 4>& input,
+    bool biasEnabled,
+    bool transposeWeights)
+{
+    std::unique_ptr<armnn::ITensorHandle> input0Handle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> input1Handle = tensorHandleFactory.CreateTensorHandle(weightsTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo);
+
+    armnn::FullyConnectedQueueDescriptor data;
+    armnn::WorkloadInfo info;
+
+    AddInputToWorkload(data, info, inputTensorInfo, input0Handle.get());
+    AddInputToWorkload(data, info, weightsTensorInfo, input1Handle.get());
+    AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
+    data.m_Parameters.m_BiasEnabled = biasEnabled;
+    data.m_Parameters.m_TransposeWeightMatrix = transposeWeights;
+    data.m_Parameters.m_ConstantWeights = false;
+
+    std::unique_ptr<armnn::ITensorHandle> input2Handle = nullptr;
+    if (biasEnabled)
+    {
+        input2Handle = tensorHandleFactory.CreateTensorHandle(biasesTensorInfo);
+        AddInputToWorkload(data, info, biasesTensorInfo, input2Handle.get());
+    }
+
+    std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateFullyConnected(data, info);
+    LayerTestResult<T, 2> result(outputTensorInfo);
+
+    input0Handle->Allocate();
+    input1Handle->Allocate();
+    outputHandle->Allocate();
+    CopyDataToITensorHandle(input0Handle.get(), &input[0][0][0][0]);
+    CopyDataToITensorHandle(input1Handle.get(), &weights[0][0]);
+    if (biasEnabled)
+    {
+        input2Handle->Allocate();
+        CopyDataToITensorHandle(input2Handle.get(), &bias[0]);
+    }
+
+    ExecuteWorkload(*workload, memoryManager);
+
+    CopyDataFromITensorHandle(&result.output[0][0], outputHandle.get());
+
+    return result;
+}
+
 template<armnn::DataType ArmnnType, typename T>
 LayerTestResult<T, 2> FullyConnectedTest(
         armnn::IWorkloadFactory& workloadFactory,
         const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
         const armnn::ITensorHandleFactory& tensorHandleFactory,
-        bool biasEnabled)
+        bool biasEnabled,
+        bool constantWeights)
 {
     constexpr static unsigned int inputWidth = 3u;
     constexpr static unsigned int inputHeight = 2u;
@@ -116,15 +174,36 @@
 
     auto bias = MakeTensor<int32_t, 1>(biasesDesc, std::vector<int32_t>{9250, 67500});
 
-    result = SimpleFullyConnectedTestImpl<T>(
-            workloadFactory,
-            memoryManager,
-            tensorHandleFactory,
-            inputTensorInfo, outputTensorInfo,
-            weightsDesc, biasesDesc,
-            weights, bias, input,
-            biasEnabled, true
-    );
+    if (constantWeights)
+    {
+        result = SimpleFullyConnectedTestImpl<T>(workloadFactory,
+                                                 memoryManager,
+                                                 tensorHandleFactory,
+                                                 inputTensorInfo,
+                                                 outputTensorInfo,
+                                                 weightsDesc,
+                                                 biasesDesc,
+                                                 weights,
+                                                 bias,
+                                                 input,
+                                                 biasEnabled,
+                                                 true);
+    }
+    else
+    {
+        result = SimpleFullyConnectedTestWeightsAsInputsImpl<T>(workloadFactory,
+                                                 memoryManager,
+                                                 tensorHandleFactory,
+                                                 inputTensorInfo,
+                                                 outputTensorInfo,
+                                                 weightsDesc,
+                                                 biasesDesc,
+                                                 weights,
+                                                 bias,
+                                                 input,
+                                                 biasEnabled,
+                                                 true);
+    }
 
     if (biasEnabled)
     {
@@ -237,14 +316,16 @@
     armnn::IWorkloadFactory& workloadFactory,
     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
     const armnn::ITensorHandleFactory& tensorHandleFactory,
-    bool biasEnabled);
+    bool biasEnabled,
+    bool constWeights);
 
 template LayerTestResult<armnn::ResolveType<armnn::DataType::QSymmS16>, 2>
 FullyConnectedTest<armnn::DataType::QSymmS16>(
     armnn::IWorkloadFactory& workloadFactory,
     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
     const armnn::ITensorHandleFactory& tensorHandleFactory,
-    bool biasEnabled);
+    bool biasEnabled,
+    bool constWeights);
 
 //
 // Implementation functions
diff --git a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp
index c2d53a5..ec921f7 100644
--- a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp
+++ b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp
@@ -17,7 +17,8 @@
     armnn::IWorkloadFactory& workloadFactory,
     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
     const armnn::ITensorHandleFactory& tensorHandleFactory,
-    bool biasEnabled);
+    bool biasEnabled,
+    bool constantWeights);
 
 LayerTestResult<float, 2> FullyConnectedFloat32Test(
     armnn::IWorkloadFactory& workloadFactory,
diff --git a/src/backends/cl/test/ClLayerTests.cpp b/src/backends/cl/test/ClLayerTests.cpp
index 013965c..2c6ebf9 100644
--- a/src/backends/cl/test/ClLayerTests.cpp
+++ b/src/backends/cl/test/ClLayerTests.cpp
@@ -101,8 +101,8 @@
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnected, FullyConnectedFloat32Test, false, false)
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithBias, FullyConnectedFloat32Test, true, false)
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithTranspose, FullyConnectedFloat32Test, false, true)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true, true)
 
 ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLarge, FullyConnectedLargeTest, false)
 ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLargeTransposed, FullyConnectedLargeTest, true)
diff --git a/src/backends/neon/test/NeonLayerTests.cpp b/src/backends/neon/test/NeonLayerTests.cpp
index 8434a67..8cccf6f 100644
--- a/src/backends/neon/test/NeonLayerTests.cpp
+++ b/src/backends/neon/test/NeonLayerTests.cpp
@@ -574,8 +574,8 @@
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithTranspose, FullyConnectedFloat32Test, false, true)
 ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLarge, FullyConnectedLargeTest, false)
 ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLargeTransposed, FullyConnectedLargeTest, true)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true, true)
 
 // Add
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleAdd, AdditionTest)
diff --git a/src/backends/reference/RefBackend.cpp b/src/backends/reference/RefBackend.cpp
index e93b317..53c55ab 100644
--- a/src/backends/reference/RefBackend.cpp
+++ b/src/backends/reference/RefBackend.cpp
@@ -69,6 +69,16 @@
     return layerSupport;
 }
 
+bool RefBackend::HasCapability(BackendCapability capabilityClass) const
+{
+    auto search = backendCapabilities.find(capabilityClass);
+    if (search != backendCapabilities.end())
+    {
+        return true;
+    }
+    return false;
+}
+
 OptimizationViews RefBackend::OptimizeSubgraphView(const SubgraphView& subgraph) const
 {
     OptimizationViews optimizationViews;
diff --git a/src/backends/reference/RefBackend.hpp b/src/backends/reference/RefBackend.hpp
index 92d392d..c92936c 100644
--- a/src/backends/reference/RefBackend.hpp
+++ b/src/backends/reference/RefBackend.hpp
@@ -9,6 +9,10 @@
 namespace armnn
 {
 
+const std::set<armnn::BackendCapability> backendCapabilities {
+    armnn::BackendCapability::NonConstWeights,
+};
+
 class RefBackend : public IBackendInternal
 {
 public:
@@ -39,6 +43,8 @@
     std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override;
 
     void RegisterTensorHandleFactories(class TensorHandleFactoryRegistry& registry) override;
+
+    bool HasCapability(BackendCapability capabilityClass) const override;
 };
 
 } // namespace armnn
diff --git a/src/backends/reference/test/RefEndToEndTests.cpp b/src/backends/reference/test/RefEndToEndTests.cpp
index 4598568..b697481 100644
--- a/src/backends/reference/test/RefEndToEndTests.cpp
+++ b/src/backends/reference/test/RefEndToEndTests.cpp
@@ -15,6 +15,7 @@
 #include <backendsCommon/test/DetectionPostProcessEndToEndTestImpl.hpp>
 #include <backendsCommon/test/ElementwiseUnaryEndToEndTestImpl.hpp>
 #include <backendsCommon/test/FillEndToEndTestImpl.hpp>
+#include <backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp>
 #include <backendsCommon/test/GatherEndToEndTestImpl.hpp>
 #include <backendsCommon/test/InstanceNormalizationEndToEndTestImpl.hpp>
 #include <backendsCommon/test/LogSoftmaxEndToEndTestImpl.hpp>
@@ -599,6 +600,11 @@
     FillEndToEnd<armnn::DataType::Signed32>(defaultBackends);
 }
 
+BOOST_AUTO_TEST_CASE(RefFullyConnectedEndToEndTestInt32)
+{
+    FullyConnectedWithDynamicWeightsEndToEnd<armnn::DataType::Float32>(defaultBackends);
+}
+
 BOOST_AUTO_TEST_CASE(RefGatherFloatTest)
 {
     GatherEndToEnd<armnn::DataType::Float32>(defaultBackends);
diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp
index 161476e..7371692 100644
--- a/src/backends/reference/test/RefLayerTests.cpp
+++ b/src/backends/reference/test/RefLayerTests.cpp
@@ -579,16 +579,22 @@
 
 // Fully Connected
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnected, FullyConnectedFloat32Test, false, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedQSymm16, FullyConnectedTest<DataType::QSymmS16>, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedQSymm16, FullyConnectedTest<DataType::QSymmS16>, false, true)
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithBias, FullyConnectedFloat32Test, true, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedQSymm16, FullyConnectedTest<DataType::QSymmS16>, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedQSymm16, FullyConnectedTest<DataType::QSymmS16>, true, true)
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithTranspose, FullyConnectedFloat32Test, false, true)
 
 ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLarge, FullyConnectedLargeTest, false)
 ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLargeTransposed, FullyConnectedLargeTest, true)
 
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedWeightsAsInputsUint8,
+                              FullyConnectedTest<DataType::QAsymmU8>,
+                              false,
+                              false)
+
 // Splitter
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleSplitterFloat32, SplitterFloat32Test)
 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleSplitterFloat16, SplitterFloat16Test)
diff --git a/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp b/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp
index 9acca21..49e105f 100644
--- a/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp
+++ b/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp
@@ -14,18 +14,21 @@
 {
 RefFullyConnectedWorkload::RefFullyConnectedWorkload(
     const FullyConnectedQueueDescriptor& descriptor, const WorkloadInfo& info)
-        : BaseWorkload<FullyConnectedQueueDescriptor>(descriptor, info),
-          m_Weight(std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Weight)))
+        : BaseWorkload<FullyConnectedQueueDescriptor>(descriptor, info)
 {
-    const TensorInfo& rWeightInfo = m_Weight->GetTensorInfo();
-    m_WeightShape = rWeightInfo.GetShape();
-    m_WeightDecoder = MakeDecoder<float>(rWeightInfo, m_Weight->Map(true));
-
-    if (descriptor.m_Parameters.m_BiasEnabled)
+    if (descriptor.m_Parameters.m_ConstantWeights)
     {
-        m_Bias = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Bias));
-        const TensorInfo& biasInfo = m_Bias->GetTensorInfo();
-        m_BiasDecoder = MakeDecoder<float>(biasInfo, m_Bias->Map(true));
+        m_Weight = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Weight));
+        const TensorInfo& rWeightInfo = m_Weight->GetTensorInfo();
+        m_WeightShape = rWeightInfo.GetShape();
+        m_WeightDecoder = MakeDecoder<float>(rWeightInfo, m_Weight->Map(true));
+
+        if (descriptor.m_Parameters.m_BiasEnabled)
+        {
+            m_Bias = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Bias));
+            const TensorInfo& biasInfo = m_Bias->GetTensorInfo();
+            m_BiasDecoder = MakeDecoder<float>(biasInfo, m_Bias->Map(true));
+        }
     }
 }
 
@@ -36,6 +39,20 @@
     m_InputShape = inputInfo.GetShape();
     m_InputDecoder = MakeDecoder<float>(inputInfo);
 
+    if (!m_Data.m_Parameters.m_ConstantWeights)
+    {
+        const TensorInfo& rWeightInfo = GetTensorInfo(m_Data.m_Inputs[1]);
+        ARMNN_ASSERT(inputInfo.GetNumDimensions() > 1);
+        m_WeightShape = rWeightInfo.GetShape();
+        m_WeightDecoder = MakeDecoder<float>(rWeightInfo);
+
+        if (m_Data.m_Parameters.m_BiasEnabled)
+        {
+            const TensorInfo& biasInfo = GetTensorInfo(m_Data.m_Inputs[2]);
+            m_BiasDecoder = MakeDecoder<float>(biasInfo);
+        }
+    }
+
     const TensorInfo& outputInfo = GetTensorInfo(m_Data.m_Outputs[0]);
     m_OutputShape = outputInfo.GetShape();
     m_OutputEncoder = MakeEncoder<float>(outputInfo);
@@ -52,6 +69,14 @@
     ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefFullyConnectedWorkload_Execute");
 
     m_InputDecoder->Reset(m_Data.m_Inputs[0]->Map());
+    if (!m_Data.m_Parameters.m_ConstantWeights)
+    {
+        m_WeightDecoder->Reset(m_Data.m_Inputs[1]->Map());
+        if (m_Data.m_Parameters.m_BiasEnabled)
+        {
+            m_BiasDecoder->Reset(m_Data.m_Inputs[2]->Map());
+        }
+    }
     m_OutputEncoder->Reset(m_Data.m_Outputs[0]->Map());
 
     FullyConnected(m_InputShape,