IVGCVSW-3398 Add LSTM normalization parameters to HAL 1.2

* Adding LSTM processing function in HAL 1.2 with normalization parameters
* Refactoring LSTM tests

!armnn:1608

Signed-off-by: Ferran Balaguer <ferran.balaguer@arm.com>
Change-Id: I0e00f14ef078a333e9f2f23d6278a5d92a3001d6
diff --git a/1.2/ArmnnDriverImpl.cpp b/1.2/ArmnnDriverImpl.cpp
index 82eacde..8a444e5 100644
--- a/1.2/ArmnnDriverImpl.cpp
+++ b/1.2/ArmnnDriverImpl.cpp
@@ -49,7 +49,7 @@
                             ErrorStatus errorStatus,
                             const sp<V1_2::IPreparedModel>& preparedModelPtr)
 {
-    Return<void> returned = callback->notify(errorStatus, preparedModelPtr);
+    Return<void> returned = callback->notify_1_2(errorStatus, preparedModelPtr);
     // This check is required, if the callback fails and it isn't checked it will bring down the service
     if (!returned.isOk())
     {
diff --git a/1.2/HalPolicy.cpp b/1.2/HalPolicy.cpp
index 9cad29f..8dbfd89 100644
--- a/1.2/HalPolicy.cpp
+++ b/1.2/HalPolicy.cpp
@@ -36,7 +36,6 @@
         case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
         case V1_0::OperationType::LOGISTIC:
         case V1_0::OperationType::LSH_PROJECTION:
-        case V1_0::OperationType::LSTM:
         case V1_0::OperationType::MUL:
         case V1_0::OperationType::RESHAPE:
         case V1_0::OperationType::RNN:
@@ -164,6 +163,8 @@
             return ConvertSpaceToDepth(operation, model, data);
         case V1_2::OperationType::TANH:
             return ConvertTanH(operation, model, data);
+        case V1_2::OperationType::LSTM:
+            return ConvertLstm(operation, model, data);
         default:
             return Fail("%s: Operation type %s not supported in ArmnnDriver",
                         __func__, toString(operation.type).c_str());
@@ -1008,5 +1009,431 @@
     return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
 }
 
+bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
+{
+    // Inputs:
+    // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
+    //      “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
+    LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
+    if (!input.IsValid())
+    {
+        return Fail("%s: Could not read input 0: input", __func__);
+    }
+    // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
+    LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
+    if (!outputStateIn.IsValid())
+    {
+        return Fail("%s: Could not read input 18: outputStateIn", __func__);
+    }
+    // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
+    LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
+    if (!cellStateIn.IsValid())
+    {
+        return Fail("%s: Could not read input 19: cellStateIn", __func__);
+    }
+
+    // Get the mandatory input tensors:
+    // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size].
+    const ConstTensorPin inputToForgetWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
+    // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    // [num_units, input_size].
+    const ConstTensorPin inputToCellWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
+    // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size].
+    const ConstTensorPin inputToOutputWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
+    // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    const ConstTensorPin recurrentToForgetWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
+    // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    const ConstTensorPin recurrentToCellWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
+    // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    const ConstTensorPin recurrentToOutputWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
+    // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin forgetGateBiasPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
+    // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin cellBiasPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
+    // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin outputGateBiasPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
+
+    if (!inputToForgetWeightsPin.IsValid() ||
+        !inputToCellWeightsPin.IsValid() ||
+        !inputToOutputWeightsPin.IsValid() ||
+        !recurrentToForgetWeightsPin.IsValid() ||
+        !recurrentToCellWeightsPin.IsValid() ||
+        !recurrentToOutputWeightsPin.IsValid() ||
+        !forgetGateBiasPin.IsValid() ||
+        !cellBiasPin.IsValid() ||
+        !outputGateBiasPin.IsValid())
+    {
+        return Fail("%s: Operation has invalid tensor inputs", __func__);
+    }
+
+    // Get the optional input tensors:
+    // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size], where “num_units” corresponds to the number of cell units.
+    const ConstTensorPin inputToInputWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      1,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
+    //     “num_units”), or the second dimension of the “projection_weights”, if defined.
+    const ConstTensorPin recurrentToInputWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      5,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin cellToInputWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      9,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin cellToForgetWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      10,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin cellToOutputWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      11,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    const ConstTensorPin inputGateBiasPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      12,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [output_size, num_units].
+    const ConstTensorPin projectionWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      16,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
+    const ConstTensorPin projectionBiasPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      17,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
+        (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
+        (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
+        (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
+        (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
+        (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
+        (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
+        (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
+    {
+        return Fail("%s: Operation has invalid tensor inputs", __func__);
+    }
+
+    // Get the mandatory input scalars (actually 1-D tensors of size 1):
+    // 20: The activation function: A value indicating the activation function:
+    //     0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
+    // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
+    //     If set to 0.0 then clipping is disabled.
+    // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
+    //     [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+    ActivationFn activation;
+    float cellClip;
+    float projClip;
+    if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
+        !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
+        !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
+    {
+        return Fail("%s: Operation has invalid scalar inputs", __func__);
+    }
+
+    // Get the normalization tensors
+    // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at input gate.
+    const ConstTensorPin inputLayerNormWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      23,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at forget gate.
+    const ConstTensorPin forgetLayerNormWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      24,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at cell gate.
+    const ConstTensorPin cellLayerNormWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      25,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at output gate.
+    const ConstTensorPin outputLayerNormWeightsPin =
+            ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
+                                                                      26,
+                                                                      model,
+                                                                      data,
+                                                                      g_DontPermute,
+                                                                      nullptr,
+                                                                      true);
+
+    // Outputs:
+    // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
+    // with CIFG, or [batch_size, num_units * 3] without CIFG.
+    const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
+    if (!scratchBuffer)
+    {
+        return Fail("%s: Could not read output 0: scratchBuffer", __func__);
+    }
+    // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
+    const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
+    if (!outputStateOut)
+    {
+        return Fail("%s: Could not read output 1: outputStateOut", __func__);
+    }
+    // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
+    const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
+    if (!cellStateOut)
+    {
+        return Fail("%s: Could not read output 2: cellStateOut", __func__);
+    }
+    // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
+    //     effectively the same as the current “output state (out)” value.
+    const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
+    if (!output)
+    {
+        return Fail("%s: Could not read output 3: output", __func__);
+    }
+
+    // set the params structure for the AddLstmLayer call
+    armnn::LstmInputParams params;
+    params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
+    params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
+    params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
+    params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
+    params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
+    params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
+    params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
+    params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
+    params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
+    params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
+    params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
+    params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
+    params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
+    params.m_CellBias = cellBiasPin.GetConstTensorPtr();
+    params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
+    params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
+    params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
+    params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
+    params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
+    params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
+    params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
+
+    // set the layer descriptor
+    armnn::LstmDescriptor desc;
+    desc.m_ActivationFunc = activation;
+    desc.m_ClippingThresCell = cellClip;
+    desc.m_ClippingThresProj = projClip;
+    desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
+                          params.m_RecurrentToInputWeights == nullptr ||
+                          params.m_InputGateBias == nullptr);
+    desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
+                              params.m_CellToOutputWeights != nullptr);
+    desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
+    desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
+                               params.m_ForgetLayerNormWeights != nullptr ||
+                               params.m_CellLayerNormWeights != nullptr ||
+                               params.m_OutputLayerNormWeights != nullptr);
+
+    // validate the optional input groups
+    if (desc.m_CifgEnabled &&
+        (params.m_InputToInputWeights != nullptr ||
+         params.m_RecurrentToInputWeights != nullptr ||
+         params.m_InputGateBias != nullptr))
+    {
+        return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
+                    " and input gate bias must be provided", __func__);
+    }
+
+    if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
+    {
+        return Fail("%s: projection bias should not be provided without projection weights", __func__);
+    }
+
+    if (desc.m_PeepholeEnabled &&
+        (params.m_CellToForgetWeights == nullptr ||
+         params.m_CellToOutputWeights == nullptr ||
+         (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
+    {
+        return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
+                    " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
+    }
+
+    if (desc.m_LayerNormEnabled &&
+        (params.m_ForgetLayerNormWeights == nullptr ||
+         params.m_CellLayerNormWeights == nullptr ||
+         params.m_OutputLayerNormWeights == nullptr ||
+         (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
+    {
+        return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
+                    " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
+    }
+
+    // Check if the layer is supported
+    // Inputs
+    const armnn::TensorInfo& inputInfo         = input.GetTensorInfo();
+    const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
+    const armnn::TensorInfo& cellStateInInfo   = cellStateIn.GetTensorInfo();
+
+    // Outputs
+    const armnn::TensorInfo& scratchBufferInfo  = GetTensorInfoForOperand(*scratchBuffer);
+    const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
+    const armnn::TensorInfo& cellStateOutInfo   = GetTensorInfoForOperand(*cellStateOut);
+    const armnn::TensorInfo& outputInfo         = GetTensorInfoForOperand(*output);
+
+    // Basic parameters
+    armnn::LstmInputParamsInfo paramsInfo;
+    paramsInfo.m_InputToForgetWeights     = &(params.m_InputToForgetWeights->GetInfo());
+    paramsInfo.m_InputToCellWeights       = &(params.m_InputToCellWeights->GetInfo());
+    paramsInfo.m_InputToOutputWeights     = &(params.m_InputToOutputWeights->GetInfo());
+    paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
+    paramsInfo.m_RecurrentToCellWeights   = &(params.m_RecurrentToCellWeights->GetInfo());
+    paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
+    paramsInfo.m_ForgetGateBias           = &(params.m_ForgetGateBias->GetInfo());
+    paramsInfo.m_CellBias                 = &(params.m_CellBias->GetInfo());
+    paramsInfo.m_OutputGateBias           = &(params.m_OutputGateBias->GetInfo());
+
+    // Optional parameters
+    if(!desc.m_CifgEnabled)
+    {
+        paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
+        paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
+        if (params.m_CellToInputWeights != nullptr)
+        {
+            paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
+        }
+        paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
+    }
+
+    if(desc.m_ProjectionEnabled)
+    {
+        paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
+        if (params.m_ProjectionBias != nullptr)
+        {
+            paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
+        }
+    }
+
+    if(desc.m_PeepholeEnabled)
+    {
+        paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
+        paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
+    }
+
+    if (desc.m_LayerNormEnabled)
+    {
+        if(!desc.m_CifgEnabled)
+        {
+            paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
+        }
+        paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
+        paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
+        paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
+    }
+
+    bool isSupported = false;
+    FORWARD_LAYER_SUPPORT_FUNC(__func__,
+                               IsLstmSupported,
+                               data.m_Backends,
+                               isSupported,
+                               inputInfo,
+                               outputStateInInfo,
+                               cellStateInInfo,
+                               scratchBufferInfo,
+                               outputStateOutInfo,
+                               cellStateOutInfo,
+                               outputInfo,
+                               desc,
+                               paramsInfo);
+    if (!isSupported)
+    {
+        return false;
+    }
+
+    // Add the layer
+    armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
+
+    input.Connect(layer->GetInputSlot(0));
+    outputStateIn.Connect(layer->GetInputSlot(1));
+    cellStateIn.Connect(layer->GetInputSlot(2));
+
+    return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
+            SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
+            SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
+            SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
+}
+
 } // namespace hal_1_2
 } // namespace armnn_driver
diff --git a/1.2/HalPolicy.hpp b/1.2/HalPolicy.hpp
index f689613..7468313 100644
--- a/1.2/HalPolicy.hpp
+++ b/1.2/HalPolicy.hpp
@@ -67,6 +67,8 @@
     static bool ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data);
 
     static bool ConvertTanH(const Operation& operation, const Model& model, ConversionData& data);
+
+    static bool ConvertLstm(const Operation& operation, const Model& model, ConversionData& data);
 };
 
 } // namespace hal_1_2
diff --git a/test/1.0/Lstm.cpp b/test/1.0/Lstm.cpp
new file mode 100644
index 0000000..5f0a209
--- /dev/null
+++ b/test/1.0/Lstm.cpp
@@ -0,0 +1,34 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "../Lstm.hpp"
+
+#include <boost/test/data/test_case.hpp>
+
+BOOST_AUTO_TEST_SUITE(LstmTests)
+
+using namespace armnn_driver;
+
+BOOST_DATA_TEST_CASE(LstmNoCifgNoPeepholeNoProjectionTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgNoPeepholeNoProjection<hal_1_0::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionTest, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeNoProjection<hal_1_0::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmNoCifgPeepholeProjectionTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgPeepholeProjection<hal_1_0::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionBatch2Test, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeNoProjectionBatch2<hal_1_0::HalPolicy>(sample);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/1.1/Lstm.cpp b/test/1.1/Lstm.cpp
new file mode 100644
index 0000000..703597e
--- /dev/null
+++ b/test/1.1/Lstm.cpp
@@ -0,0 +1,34 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "../Lstm.hpp"
+
+#include <boost/test/data/test_case.hpp>
+
+BOOST_AUTO_TEST_SUITE(LstmTests)
+
+using namespace armnn_driver;
+
+BOOST_DATA_TEST_CASE(LstmNoCifgNoPeepholeNoProjectionTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgNoPeepholeNoProjection<hal_1_1::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionTest, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeNoProjection<hal_1_1::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmNoCifgPeepholeProjectionTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgPeepholeProjection<hal_1_1::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionBatch2Test, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeNoProjectionBatch2<hal_1_1::HalPolicy>(sample);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/1.2/Lstm.cpp b/test/1.2/Lstm.cpp
new file mode 100644
index 0000000..a76b761
--- /dev/null
+++ b/test/1.2/Lstm.cpp
@@ -0,0 +1,44 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "../Lstm.hpp"
+
+#include <boost/test/data/test_case.hpp>
+
+BOOST_AUTO_TEST_SUITE(LstmTests)
+
+using namespace armnn_driver;
+
+BOOST_DATA_TEST_CASE(LstmNoCifgNoPeepholeNoProjectionTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgNoPeepholeNoProjection<hal_1_2::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionTest, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeNoProjection<hal_1_2::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmNoCifgPeepholeProjectionTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgPeepholeProjection<hal_1_2::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionBatch2Test, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeNoProjectionBatch2<hal_1_2::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmNoCifgPeepholeProjectionNoClippingLayerNormTest, COMPUTE_DEVICES)
+{
+    LstmNoCifgPeepholeProjectionNoClippingLayerNorm<hal_1_2::HalPolicy>(sample);
+}
+
+BOOST_DATA_TEST_CASE(LstmCifgPeepholeProjectionNoClippingLayerNormTest, COMPUTE_DEVICES)
+{
+    LstmCifgPeepholeProjectionNoClippingLayerNorm<hal_1_2::HalPolicy>(sample);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/Android.mk b/test/Android.mk
index a078e0a..2f0e03e 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -57,6 +57,7 @@
 LOCAL_SRC_FILES := \
         1.0/Convolution2D.cpp \
         1.0/FullyConnectedReshape.cpp \
+        1.0/Lstm.cpp \
         Tests.cpp \
         UtilsTests.cpp \
         Concurrent.cpp \
@@ -64,7 +65,6 @@
         GenericLayerTests.cpp \
         DriverTestHelpers.cpp \
         SystemProperties.cpp \
-        Lstm.cpp \
         Concat.cpp \
         TestTensor.cpp
 
@@ -160,6 +160,8 @@
         1.1/Convolution2D.cpp \
         1.1/Mean.cpp \
         1.1/Transpose.cpp \
+        1.0/Lstm.cpp \
+        1.1/Lstm.cpp \
         Tests.cpp \
         UtilsTests.cpp \
         Concurrent.cpp \
@@ -167,7 +169,6 @@
         GenericLayerTests.cpp \
         DriverTestHelpers.cpp \
         SystemProperties.cpp \
-        Lstm.cpp \
         Concat.cpp \
         TestTensor.cpp
 
@@ -257,6 +258,9 @@
         1.1/Transpose.cpp \
         1.2/Dilation.cpp \
         1.2/Capabilities.cpp \
+        1.0/Lstm.cpp \
+        1.1/Lstm.cpp \
+        1.2/Lstm.cpp \
         Tests.cpp \
         UtilsTests.cpp \
         Concurrent.cpp \
@@ -264,7 +268,6 @@
         GenericLayerTests.cpp \
         DriverTestHelpers.cpp \
         SystemProperties.cpp \
-        Lstm.cpp \
         Concat.cpp \
         TestTensor.cpp
 
diff --git a/test/DriverTestHelpers.cpp b/test/DriverTestHelpers.cpp
index 675757f..4c26174 100644
--- a/test/DriverTestHelpers.cpp
+++ b/test/DriverTestHelpers.cpp
@@ -61,6 +61,26 @@
     return Void();
 }
 
+#ifdef ARMNN_ANDROID_NN_V1_2
+
+Return<void> PreparedModelCallback_1_2::notify(ErrorStatus status,
+                                               const android::sp<V1_0::IPreparedModel>& preparedModel)
+{
+    m_ErrorStatus = status;
+    m_PreparedModel = preparedModel;
+    return Void();
+}
+
+Return<void> PreparedModelCallback_1_2::notify_1_2(ErrorStatus status,
+                                                   const android::sp<V1_2::IPreparedModel>& preparedModel)
+{
+    m_ErrorStatus = status;
+    m_PreparedModel_1_2 = preparedModel;
+    return Void();
+}
+
+#endif
+
 // lifted from common/Utils.cpp
 hidl_memory allocateSharedMemory(int64_t size)
 {
@@ -147,6 +167,32 @@
 
 #endif
 
+#ifdef ARMNN_ANDROID_NN_V1_2
+
+android::sp<V1_2::IPreparedModel> PrepareModelWithStatus_1_2(const armnn_driver::hal_1_2::HalPolicy::Model& model,
+                                                             armnn_driver::ArmnnDriver& driver,
+                                                             ErrorStatus& prepareStatus,
+                                                             ErrorStatus expectedStatus)
+{
+    android::sp<PreparedModelCallback_1_2> cb(new PreparedModelCallback_1_2());
+
+    android::hardware::hidl_vec<android::hardware::hidl_handle> emptyHandle1;
+    android::hardware::hidl_vec<android::hardware::hidl_handle> emptyHandle2;
+    armnn_driver::ArmnnDriver::HidlToken emptyToken;
+
+    driver.prepareModel_1_2(model, V1_1::ExecutionPreference::LOW_POWER, emptyHandle1, emptyHandle2, emptyToken, cb);
+
+    prepareStatus = cb->GetErrorStatus();
+    BOOST_TEST(prepareStatus == expectedStatus);
+    if (expectedStatus == ErrorStatus::NONE)
+    {
+        BOOST_TEST((cb->GetPreparedModel_1_2() != nullptr));
+    }
+    return cb->GetPreparedModel_1_2();
+}
+
+#endif
+
 ErrorStatus Execute(android::sp<V1_0::IPreparedModel> preparedModel,
                     const Request& request,
                     ErrorStatus expectedStatus)
diff --git a/test/DriverTestHelpers.hpp b/test/DriverTestHelpers.hpp
index 980b3a7..c6f3f1f 100644
--- a/test/DriverTestHelpers.hpp
+++ b/test/DriverTestHelpers.hpp
@@ -67,6 +67,36 @@
     android::sp<V1_0::IPreparedModel>  m_PreparedModel;
 };
 
+#ifdef ARMNN_ANDROID_NN_V1_2
+
+class PreparedModelCallback_1_2 : public V1_2::IPreparedModelCallback
+{
+public:
+    PreparedModelCallback_1_2()
+            : m_ErrorStatus(ErrorStatus::NONE)
+            , m_PreparedModel()
+            , m_PreparedModel_1_2()
+    { }
+    ~PreparedModelCallback_1_2() override { }
+
+    Return<void> notify(ErrorStatus status, const android::sp<V1_0::IPreparedModel>& preparedModel) override;
+
+    Return<void> notify_1_2(ErrorStatus status, const android::sp<V1_2::IPreparedModel>& preparedModel) override;
+
+    ErrorStatus GetErrorStatus() { return m_ErrorStatus; }
+
+    android::sp<V1_0::IPreparedModel> GetPreparedModel() { return m_PreparedModel; }
+
+    android::sp<V1_2::IPreparedModel> GetPreparedModel_1_2() { return m_PreparedModel_1_2; }
+
+private:
+    ErrorStatus                        m_ErrorStatus;
+    android::sp<V1_0::IPreparedModel>  m_PreparedModel;
+    android::sp<V1_2::IPreparedModel>  m_PreparedModel_1_2;
+};
+
+#endif
+
 hidl_memory allocateSharedMemory(int64_t size);
 
 android::sp<IMemory> AddPoolAndGetData(uint32_t size, Request& request);
@@ -259,6 +289,24 @@
     return PrepareModelWithStatus(model, driver, prepareStatus);
 }
 
+#ifdef ARMNN_ANDROID_NN_V1_2
+
+android::sp<V1_2::IPreparedModel> PrepareModelWithStatus_1_2(const armnn_driver::hal_1_2::HalPolicy::Model& model,
+                                                            armnn_driver::ArmnnDriver& driver,
+                                                            ErrorStatus& prepareStatus,
+                                                            ErrorStatus expectedStatus = ErrorStatus::NONE);
+
+template<typename HalModel>
+android::sp<V1_2::IPreparedModel> PrepareModel_1_2(const HalModel& model,
+                                                   armnn_driver::ArmnnDriver& driver)
+{
+    ErrorStatus prepareStatus = ErrorStatus::NONE;
+    return PrepareModelWithStatus_1_2(model, driver, prepareStatus);
+}
+
+#endif
+
+
 ErrorStatus Execute(android::sp<V1_0::IPreparedModel> preparedModel,
                     const Request& request,
                     ErrorStatus expectedStatus = ErrorStatus::NONE);
diff --git a/test/Lstm.cpp b/test/Lstm.hpp
similarity index 65%
rename from test/Lstm.cpp
rename to test/Lstm.hpp
index 579524c..3d9bf77 100644
--- a/test/Lstm.cpp
+++ b/test/Lstm.hpp
@@ -2,24 +2,16 @@
 // Copyright © 2017 Arm Ltd. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
-#include "DriverTestHelpers.hpp"
-#include "OperationsUtils.h"
 
-#include "../1.0/HalPolicy.hpp"
+#pragma once
+
+#include "DriverTestHelpers.hpp"
 
 #include <boost/array.hpp>
-#include <boost/test/unit_test.hpp>
-#include <boost/test/data/test_case.hpp>
 #include <boost/math/special_functions/relative_difference.hpp>
-#include <log/log.h>
-
-#include <cmath>
-
-BOOST_AUTO_TEST_SUITE(LstmTests)
 
 using ArmnnDriver   = armnn_driver::ArmnnDriver;
 using DriverOptions = armnn_driver::DriverOptions;
-using HalPolicy     = armnn_driver::hal_1_0::HalPolicy;
 
 using namespace driverTestHelpers;
 using namespace android::hardware;
@@ -71,10 +63,43 @@
     }
     return OperandLifeTime::CONSTANT_COPY;
 }
+
+template<typename HalModel>
+void ExecuteModel(const HalModel& model, armnn_driver::ArmnnDriver& driver, const Request& request)
+{
+    android::sp<V1_0::IPreparedModel> preparedModel = PrepareModel(model, driver);
+    if (preparedModel.get() != nullptr)
+    {
+        Execute(preparedModel, request);
+    }
+}
+
+#ifdef ARMNN_ANDROID_NN_V1_2
+
+template<>
+void ExecuteModel<armnn_driver::hal_1_2::HalPolicy::Model>(const armnn_driver::hal_1_2::HalPolicy::Model& model,
+                                                           armnn_driver::ArmnnDriver& driver,
+                                                           const Request& request)
+{
+    android::sp<V1_2::IPreparedModel> preparedModel = PrepareModel_1_2(model, driver);
+    if (preparedModel.get() != nullptr)
+    {
+        Execute(preparedModel, request);
+    }
+}
+
+#endif
+
 } // anonymous namespace
 
-// Add our own tests here since we fail the lstm tests which Google supplies (because of non-const weights)
+#ifndef ARMCOMPUTECL_ENABLED
+static const boost::array<armnn::Compute, 1> COMPUTE_DEVICES = {{ armnn::Compute::CpuRef }};
+#else
+static const boost::array<armnn::Compute, 2> COMPUTE_DEVICES = {{ armnn::Compute::CpuRef, armnn::Compute::GpuAcc }};
+#endif
 
+// Add our own tests here since we fail the lstm tests which Google supplies (because of non-const weights)
+template <typename HalPolicy>
 void LstmTestImpl(const hidl_vec<uint32_t>&   inputDimensions,
                   const std::vector<float>&   inputValue,
                   const hidl_vec<uint32_t>&   inputToInputWeightsDimensions,
@@ -121,6 +146,14 @@
                   const std::vector<float>&   cellClippingThresholdValue,
                   const hidl_vec<uint32_t>&   projectionClippingThresholdDimensions,
                   const std::vector<float>&   projectionClippingThresholdValue,
+                  const hidl_vec<uint32_t>&   inputLayerNormWeightsDimensions,
+                  const std::vector<float>&   inputLayerNormWeightsValue,
+                  const hidl_vec<uint32_t>&   forgetLayerNormWeightsDimensions,
+                  const std::vector<float>&   forgetLayerNormWeightsValue,
+                  const hidl_vec<uint32_t>&   cellLayerNormWeightsDimensions,
+                  const std::vector<float>&   cellLayerNormWeightsValue,
+                  const hidl_vec<uint32_t>&   outputLayerNormWeightsDimensions,
+                  const std::vector<float>&   outputLayerNormWeightsValue,
                   const hidl_vec<uint32_t>&   scratchBufferDimensions,
                   const std::vector<float>&   scratchBufferValue,
                   const hidl_vec<uint32_t>&   outputStateOutDimensions,
@@ -132,7 +165,8 @@
                   armnn::Compute              compute)
 {
     auto driver = std::make_unique<ArmnnDriver>(DriverOptions(compute));
-    HalPolicy::Model model = {};
+    using Model = typename HalPolicy::Model;
+    Model model = {};
 
     // Inputs:
     // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
@@ -241,6 +275,47 @@
                                 projectionClippingThresholdValue,
                                 HalPolicy::OperandType::FLOAT32);
 
+    bool normalizationEnabled = false;
+
+    // If any of the tensors have a value all normalization tensors are set
+    if (!inputLayerNormWeightsValue.empty()  ||
+        !forgetLayerNormWeightsValue.empty() ||
+        !cellLayerNormWeightsValue.empty()   ||
+        !outputLayerNormWeightsValue.empty())
+    {
+        // Normalization:
+        // 23:The input layer normalization weights. A 1-D tensor of shape [num_units].
+        //    Used to rescale normalized inputs to activation at input gate.
+        AddTensorOperand<HalPolicy>(model,
+                                    inputLayerNormWeightsDimensions,
+                                    inputLayerNormWeightsValue,
+                                    HalPolicy::OperandType::TENSOR_FLOAT32,
+                                    CreateNoValueLifeTime(inputLayerNormWeightsDimensions));
+        // 24:The forget layer normalization weights. A 1-D tensor of shape [num_units].
+        //    Used to rescale normalized inputs to activation at forget gate.
+        AddTensorOperand<HalPolicy>(model,
+                                    forgetLayerNormWeightsDimensions,
+                                    forgetLayerNormWeightsValue,
+                                    HalPolicy::OperandType::TENSOR_FLOAT32,
+                                    CreateNoValueLifeTime(forgetLayerNormWeightsDimensions));
+        // 25:The cell layer normalization weights. A 1-D tensor of shape [num_units].
+        //    Used to rescale normalized inputs to activation at cell gate.
+        AddTensorOperand<HalPolicy>(model,
+                                    cellLayerNormWeightsDimensions,
+                                    cellLayerNormWeightsValue,
+                                    HalPolicy::OperandType::TENSOR_FLOAT32,
+                                    CreateNoValueLifeTime(cellLayerNormWeightsDimensions));
+        // 26:The output layer normalization weights. A 1-D tensor of shape [num_units].
+        //    Used to rescale normalized inputs to activation at output gate.
+        AddTensorOperand<HalPolicy>(model,
+                                    outputLayerNormWeightsDimensions,
+                                    outputLayerNormWeightsValue,
+                                    HalPolicy::OperandType::TENSOR_FLOAT32,
+                                    CreateNoValueLifeTime(outputLayerNormWeightsDimensions));
+
+        normalizationEnabled = true;
+    }
+
     // Outputs:
     //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
     //     CIFG, or [batch_size, num_units * 3] without CIFG.
@@ -256,9 +331,19 @@
     // make the lstm operation
     model.operations.resize(1);
     model.operations[0].type = HalPolicy::OperationType::LSTM;
-    model.operations[0].inputs =
-        hidl_vec<uint32_t> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22};
-    model.operations[0].outputs = hidl_vec<uint32_t> {23, 24, 25, 26};
+
+    if (normalizationEnabled)
+    {
+        model.operations[0].inputs = hidl_vec<uint32_t> { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,
+                                                         14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26};
+        model.operations[0].outputs = hidl_vec<uint32_t> {27, 28, 29, 30};
+    }
+    else
+    {
+        model.operations[0].inputs = hidl_vec<uint32_t> { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
+                                                         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22};
+        model.operations[0].outputs = hidl_vec<uint32_t> {23, 24, 25, 26};
+    }
 
     // define the input values
     hidl_vec<RequestArgument> inputArguments;
@@ -296,11 +381,7 @@
     float* outputData = static_cast<float*>(static_cast<void*>(outputMemory->getPointer()));
 
     // make the prepared model and run the execution
-    android::sp<V1_0::IPreparedModel> preparedModel = PrepareModel(model, *driver);
-    if (preparedModel.get() != nullptr)
-    {
-        Execute(preparedModel, request);
-    }
+    ExecuteModel(model, *driver, request);
 
     // check the results
     for (size_t i = 0; i < outputStateOutValue.size(); ++i)
@@ -320,6 +401,7 @@
     }
 }
 
+template <typename HalPolicy>
 void LstmNoCifgNoPeepholeNoProjection(armnn::Compute compute)
 {
     // This replicates android/frameworks/ml/nn/runtime/test/generated/vts_models/lstm.model.cpp
@@ -443,6 +525,24 @@
     hidl_vec<uint32_t> projectionClippingThresholdDimensions{};
     std::vector<float> projectionClippingThresholdValue{0.0f};
 
+    // Normalization:
+    // 23:The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at input gate.
+    hidl_vec<uint32_t> inputLayerNormWeightsDimensions{0};
+    std::vector<float> inputLayerNormWeightsValue;
+    // 24:The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at forget gate.
+    hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{0};
+    std::vector<float> forgetLayerNormWeightsValue;
+    // 25:The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at cell gate.
+    hidl_vec<uint32_t> cellLayerNormWeightsDimensions{0};
+    std::vector<float> cellLayerNormWeightsValue;
+    // 26:The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at output gate.
+    hidl_vec<uint32_t> outputLayerNormWeightsDimensions{0};
+    std::vector<float> outputLayerNormWeightsValue;
+
     // Outputs:
     //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
     //     CIFG, or [batch_size, num_units * 3] without CIFG.
@@ -463,36 +563,41 @@
     hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
     std::vector<float> outputValue {-0.02973187f, 0.1229473f, 0.20885126f, -0.15358765f};
 
-    LstmTestImpl(inputDimensions,                       inputValue,
-                 inputToInputWeightsDimensions,         inputToInputWeightsValue,
-                 inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
-                 inputToCellWeightsDimensions,          inputToCellWeightsValue,
-                 inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
-                 recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
-                 recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
-                 recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
-                 recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
-                 cellToInputWeightsDimensions,          cellToInputWeightsValue,
-                 cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
-                 cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
-                 inputGateBiasDimensions,               inputGateBiasValue,
-                 forgetGateBiasDimensions,              forgetGateBiasValue,
-                 cellBiasDimensions,                    cellBiasValue,
-                 outputGateBiasDimensions,              outputGateBiasValue,
-                 projectionWeightsDimensions,           projectionWeightsValue,
-                 projectionBiasDimensions,              projectionBiasValue,
-                 outputStateInDimensions,               outputStateInValue,
-                 cellStateInDimensions,                 cellStateInValue,
-                 activationFunctionDimensions,          activationFunctionValue,
-                 cellClippingThresholdDimensions,       cellClippingThresholdValue,
-                 projectionClippingThresholdDimensions, projectionClippingThresholdValue,
-                 scratchBufferDimensions,               scratchBufferValue,
-                 outputStateOutDimensions,              outputStateOutValue,
-                 cellStateOutDimensions,                cellStateOutValue,
-                 outputDimensions,                      outputValue,
-                 compute);
+    LstmTestImpl<HalPolicy>(inputDimensions,                       inputValue,
+                            inputToInputWeightsDimensions,         inputToInputWeightsValue,
+                            inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
+                            inputToCellWeightsDimensions,          inputToCellWeightsValue,
+                            inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
+                            recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
+                            recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
+                            recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
+                            recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
+                            cellToInputWeightsDimensions,          cellToInputWeightsValue,
+                            cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
+                            cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
+                            inputGateBiasDimensions,               inputGateBiasValue,
+                            forgetGateBiasDimensions,              forgetGateBiasValue,
+                            cellBiasDimensions,                    cellBiasValue,
+                            outputGateBiasDimensions,              outputGateBiasValue,
+                            projectionWeightsDimensions,           projectionWeightsValue,
+                            projectionBiasDimensions,              projectionBiasValue,
+                            outputStateInDimensions,               outputStateInValue,
+                            cellStateInDimensions,                 cellStateInValue,
+                            activationFunctionDimensions,          activationFunctionValue,
+                            cellClippingThresholdDimensions,       cellClippingThresholdValue,
+                            projectionClippingThresholdDimensions, projectionClippingThresholdValue,
+                            inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
+                            forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
+                            cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
+                            outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
+                            scratchBufferDimensions,               scratchBufferValue,
+                            outputStateOutDimensions,              outputStateOutValue,
+                            cellStateOutDimensions,                cellStateOutValue,
+                            outputDimensions,                      outputValue,
+                            compute);
 }
 
+template <typename HalPolicy>
 void LstmCifgPeepholeNoProjection(armnn::Compute compute)
 {
     // This replicates android/frameworks/ml/nn/runtime/test/generated/vts_models/lstm2.model.cpp
@@ -610,6 +715,24 @@
     hidl_vec<uint32_t> projectionClippingThresholdDimensions{};
     std::vector<float> projectionClippingThresholdValue{0.0f};
 
+    // Normalization:
+    // 23:The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at input gate.
+    hidl_vec<uint32_t> inputLayerNormWeightsDimensions{0};
+    std::vector<float> inputLayerNormWeightsValue;
+    // 24:The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at forget gate.
+    hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{0};
+    std::vector<float> forgetLayerNormWeightsValue;
+    // 25:The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at cell gate.
+    hidl_vec<uint32_t> cellLayerNormWeightsDimensions{0};
+    std::vector<float> cellLayerNormWeightsValue;
+    // 26:The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at output gate.
+    hidl_vec<uint32_t> outputLayerNormWeightsDimensions{0};
+    std::vector<float> outputLayerNormWeightsValue;
+
     // Outputs:
     //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
     //     CIFG, or [batch_size, num_units * 3] without CIFG.
@@ -630,36 +753,41 @@
     hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
     std::vector<float> outputValue{-0.36444446f, -0.00352185f, 0.12886585f, -0.05163646f};
 
-    LstmTestImpl(inputDimensions,                       inputValue,
-                 inputToInputWeightsDimensions,         inputToInputWeightsValue,
-                 inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
-                 inputToCellWeightsDimensions,          inputToCellWeightsValue,
-                 inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
-                 recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
-                 recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
-                 recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
-                 recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
-                 cellToInputWeightsDimensions,          cellToInputWeightsValue,
-                 cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
-                 cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
-                 inputGateBiasDimensions,               inputGateBiasValue,
-                 forgetGateBiasDimensions,              forgetGateBiasValue,
-                 cellBiasDimensions,                    cellBiasValue,
-                 outputGateBiasDimensions,              outputGateBiasValue,
-                 projectionWeightsDimensions,           projectionWeightsValue,
-                 projectionBiasDimensions,              projectionBiasValue,
-                 outputStateInDimensions,               outputStateInValue,
-                 cellStateInDimensions,                 cellStateInValue,
-                 activationFunctionDimensions,          activationFunctionValue,
-                 cellClippingThresholdDimensions,       cellClippingThresholdValue,
-                 projectionClippingThresholdDimensions, projectionClippingThresholdValue,
-                 scratchBufferDimensions,               scratchBufferValue,
-                 outputStateOutDimensions,              outputStateOutValue,
-                 cellStateOutDimensions,                cellStateOutValue,
-                 outputDimensions,                      outputValue,
-                 compute);
+    LstmTestImpl<HalPolicy>(inputDimensions,                       inputValue,
+                            inputToInputWeightsDimensions,         inputToInputWeightsValue,
+                            inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
+                            inputToCellWeightsDimensions,          inputToCellWeightsValue,
+                            inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
+                            recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
+                            recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
+                            recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
+                            recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
+                            cellToInputWeightsDimensions,          cellToInputWeightsValue,
+                            cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
+                            cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
+                            inputGateBiasDimensions,               inputGateBiasValue,
+                            forgetGateBiasDimensions,              forgetGateBiasValue,
+                            cellBiasDimensions,                    cellBiasValue,
+                            outputGateBiasDimensions,              outputGateBiasValue,
+                            projectionWeightsDimensions,           projectionWeightsValue,
+                            projectionBiasDimensions,              projectionBiasValue,
+                            outputStateInDimensions,               outputStateInValue,
+                            cellStateInDimensions,                 cellStateInValue,
+                            activationFunctionDimensions,          activationFunctionValue,
+                            cellClippingThresholdDimensions,       cellClippingThresholdValue,
+                            projectionClippingThresholdDimensions, projectionClippingThresholdValue,
+                            inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
+                            forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
+                            cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
+                            outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
+                            scratchBufferDimensions,               scratchBufferValue,
+                            outputStateOutDimensions,              outputStateOutValue,
+                            cellStateOutDimensions,                cellStateOutValue,
+                            outputDimensions,                      outputValue,
+                            compute);
 }
 
+template <typename HalPolicy>
 void LstmNoCifgPeepholeProjection(armnn::Compute compute)
 {
     // This replicates android/frameworks/ml/nn/runtime/test/generated/vts_models/lstm3.model.cpp
@@ -1284,6 +1412,24 @@
     hidl_vec<uint32_t> projectionClippingThresholdDimensions{};
     std::vector<float> projectionClippingThresholdValue{0.0f};
 
+    // Normalization:
+    // 23:The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at input gate.
+    hidl_vec<uint32_t> inputLayerNormWeightsDimensions{0};
+    std::vector<float> inputLayerNormWeightsValue;
+    // 24:The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at forget gate.
+    hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{0};
+    std::vector<float> forgetLayerNormWeightsValue;
+    // 25:The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at cell gate.
+    hidl_vec<uint32_t> cellLayerNormWeightsDimensions{0};
+    std::vector<float> cellLayerNormWeightsValue;
+    // 26:The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at output gate.
+    hidl_vec<uint32_t> outputLayerNormWeightsDimensions{0};
+    std::vector<float> outputLayerNormWeightsValue;
+
     // Outputs:
     //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
     //     CIFG, or [batch_size, num_units * 3] without CIFG.
@@ -1326,36 +1472,41 @@
          0.00422612f, -0.0345232f, 0.00223253f, -0.00957321f, 0.0210624f, 0.013331f, 0.0150954f, 0.02168f
     };
 
-    LstmTestImpl(inputDimensions,                       inputValue,
-                 inputToInputWeightsDimensions,         inputToInputWeightsValue,
-                 inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
-                 inputToCellWeightsDimensions,          inputToCellWeightsValue,
-                 inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
-                 recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
-                 recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
-                 recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
-                 recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
-                 cellToInputWeightsDimensions,          cellToInputWeightsValue,
-                 cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
-                 cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
-                 inputGateBiasDimensions,               inputGateBiasValue,
-                 forgetGateBiasDimensions,              forgetGateBiasValue,
-                 cellBiasDimensions,                    cellBiasValue,
-                 outputGateBiasDimensions,              outputGateBiasValue,
-                 projectionWeightsDimensions,           projectionWeightsValue,
-                 projectionBiasDimensions,              projectionBiasValue,
-                 outputStateInDimensions,               outputStateInValue,
-                 cellStateInDimensions,                 cellStateInValue,
-                 activationFunctionDimensions,          activationFunctionValue,
-                 cellClippingThresholdDimensions,       cellClippingThresholdValue,
-                 projectionClippingThresholdDimensions, projectionClippingThresholdValue,
-                 scratchBufferDimensions,               scratchBufferValue,
-                 outputStateOutDimensions,              outputStateOutValue,
-                 cellStateOutDimensions,                cellStateOutValue,
-                 outputDimensions,                      outputValue,
-                 compute);
+    LstmTestImpl<HalPolicy>(inputDimensions,                       inputValue,
+                            inputToInputWeightsDimensions,         inputToInputWeightsValue,
+                            inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
+                            inputToCellWeightsDimensions,          inputToCellWeightsValue,
+                            inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
+                            recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
+                            recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
+                            recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
+                            recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
+                            cellToInputWeightsDimensions,          cellToInputWeightsValue,
+                            cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
+                            cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
+                            inputGateBiasDimensions,               inputGateBiasValue,
+                            forgetGateBiasDimensions,              forgetGateBiasValue,
+                            cellBiasDimensions,                    cellBiasValue,
+                            outputGateBiasDimensions,              outputGateBiasValue,
+                            projectionWeightsDimensions,           projectionWeightsValue,
+                            projectionBiasDimensions,              projectionBiasValue,
+                            outputStateInDimensions,               outputStateInValue,
+                            cellStateInDimensions,                 cellStateInValue,
+                            activationFunctionDimensions,          activationFunctionValue,
+                            cellClippingThresholdDimensions,       cellClippingThresholdValue,
+                            projectionClippingThresholdDimensions, projectionClippingThresholdValue,
+                            inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
+                            forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
+                            cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
+                            outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
+                            scratchBufferDimensions,               scratchBufferValue,
+                            outputStateOutDimensions,              outputStateOutValue,
+                            cellStateOutDimensions,                cellStateOutValue,
+                            outputDimensions,                      outputValue,
+                            compute);
 }
 
+template <typename HalPolicy>
 void LstmCifgPeepholeNoProjectionBatch2(armnn::Compute compute)
 {
     // This replicates android/frameworks/ml/nn/runtime/test/generated/vts_models/lstm2.model.cpp
@@ -1474,6 +1625,24 @@
     hidl_vec<uint32_t> projectionClippingThresholdDimensions{};
     std::vector<float> projectionClippingThresholdValue{0.0f};
 
+    // Normalization:
+    // 23:The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at input gate.
+    hidl_vec<uint32_t> inputLayerNormWeightsDimensions{0};
+    std::vector<float> inputLayerNormWeightsValue;
+    // 24:The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at forget gate.
+    hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{0};
+    std::vector<float> forgetLayerNormWeightsValue;
+    // 25:The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at cell gate.
+    hidl_vec<uint32_t> cellLayerNormWeightsDimensions{0};
+    std::vector<float> cellLayerNormWeightsValue;
+    // 26:The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //    Used to rescale normalized inputs to activation at output gate.
+    hidl_vec<uint32_t> outputLayerNormWeightsDimensions{0};
+    std::vector<float> outputLayerNormWeightsValue;
+
     // Outputs:
     //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
     //     CIFG, or [batch_size, num_units * 3] without CIFG.
@@ -1497,59 +1666,434 @@
     std::vector<float> outputValue{-0.36444446f, -0.00352185f, 0.12886585f, -0.05163646f,
                                    -0.42734814f, -0.00478661f, 0.13455015f, -0.03560682f};
 
-    LstmTestImpl(inputDimensions,                       inputValue,
-                 inputToInputWeightsDimensions,         inputToInputWeightsValue,
-                 inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
-                 inputToCellWeightsDimensions,          inputToCellWeightsValue,
-                 inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
-                 recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
-                 recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
-                 recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
-                 recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
-                 cellToInputWeightsDimensions,          cellToInputWeightsValue,
-                 cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
-                 cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
-                 inputGateBiasDimensions,               inputGateBiasValue,
-                 forgetGateBiasDimensions,              forgetGateBiasValue,
-                 cellBiasDimensions,                    cellBiasValue,
-                 outputGateBiasDimensions,              outputGateBiasValue,
-                 projectionWeightsDimensions,           projectionWeightsValue,
-                 projectionBiasDimensions,              projectionBiasValue,
-                 outputStateInDimensions,               outputStateInValue,
-                 cellStateInDimensions,                 cellStateInValue,
-                 activationFunctionDimensions,          activationFunctionValue,
-                 cellClippingThresholdDimensions,       cellClippingThresholdValue,
-                 projectionClippingThresholdDimensions, projectionClippingThresholdValue,
-                 scratchBufferDimensions,               scratchBufferValue,
-                 outputStateOutDimensions,              outputStateOutValue,
-                 cellStateOutDimensions,                cellStateOutValue,
-                 outputDimensions,                      outputValue,
-                 compute);
+    LstmTestImpl<HalPolicy>(inputDimensions,                       inputValue,
+                            inputToInputWeightsDimensions,         inputToInputWeightsValue,
+                            inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
+                            inputToCellWeightsDimensions,          inputToCellWeightsValue,
+                            inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
+                            recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
+                            recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
+                            recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
+                            recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
+                            cellToInputWeightsDimensions,          cellToInputWeightsValue,
+                            cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
+                            cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
+                            inputGateBiasDimensions,               inputGateBiasValue,
+                            forgetGateBiasDimensions,              forgetGateBiasValue,
+                            cellBiasDimensions,                    cellBiasValue,
+                            outputGateBiasDimensions,              outputGateBiasValue,
+                            projectionWeightsDimensions,           projectionWeightsValue,
+                            projectionBiasDimensions,              projectionBiasValue,
+                            outputStateInDimensions,               outputStateInValue,
+                            cellStateInDimensions,                 cellStateInValue,
+                            activationFunctionDimensions,          activationFunctionValue,
+                            cellClippingThresholdDimensions,       cellClippingThresholdValue,
+                            projectionClippingThresholdDimensions, projectionClippingThresholdValue,
+                            inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
+                            forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
+                            cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
+                            outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
+                            scratchBufferDimensions,               scratchBufferValue,
+                            outputStateOutDimensions,              outputStateOutValue,
+                            cellStateOutDimensions,                cellStateOutValue,
+                            outputDimensions,                      outputValue,
+                            compute);
 }
-#ifndef ARMCOMPUTECL_ENABLED
-    static const boost::array<armnn::Compute, 1> COMPUTE_DEVICES = {{ armnn::Compute::CpuRef }};
-#else
-    static const boost::array<armnn::Compute, 2> COMPUTE_DEVICES = {{ armnn::Compute::CpuRef, armnn::Compute::GpuAcc }};
-#endif
 
-BOOST_DATA_TEST_CASE(LstmNoCifgNoPeepholeNoProjectionTest, COMPUTE_DEVICES)
+template <typename HalPolicy>
+void LstmNoCifgPeepholeProjectionNoClippingLayerNorm(armnn::Compute compute)
 {
-    LstmNoCifgNoPeepholeNoProjection(sample);
+    // This replicates android/frameworks/ml/nn/runtime/test/generated/vts_models/layer_norm_lstm.model.cpp
+    // with values from android/frameworks/ml/nn/runtime/test/generated/examples/layer_norm_lstm.example.cpp
+    // and weights, biases and scalars passed as CONSTANT_COPY tensors (instead of MODEL_INPUT tensors).
+
+    uint32_t batchSize = 2;
+    uint32_t inputSize = 5;
+    uint32_t numUnits = 4;
+    uint32_t outputSize = 3;
+
+    // Inputs:
+    // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
+    //     “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
+    hidl_vec<uint32_t> inputDimensions{batchSize, inputSize};
+    std::vector<float> inputValue{ 0.7f,  0.8f,  0.1f,  0.2f,  0.3f,  // batch 0
+                                   0.3f,  0.2f,  0.9f,  0.8f,  0.1f}; // batch 1
+
+    // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size], where “num_units” corresponds to the number of cell units.
+    hidl_vec<uint32_t> inputToInputWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToInputWeightsValue{ 0.5,  0.6,  0.7, -0.8, -0.9,
+                                                 0.1,  0.2,  0.3, -0.4,  0.5,
+                                                -0.8,  0.7, -0.6,  0.5, -0.4,
+                                                -0.5, -0.4, -0.3, -0.2, -0.1};
+    // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size].
+    hidl_vec<uint32_t> inputToForgetWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToForgetWeightsValue{-0.6, -0.1,  0.3,  0.2,  0.9,
+                                                 -0.5, -0.2, -0.4,  0.3, -0.8,
+                                                 -0.4,  0.3, -0.5, -0.4, -0.6,
+                                                  0.3, -0.4, -0.6, -0.5, -0.5};
+    // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units, input_size].
+    hidl_vec<uint32_t> inputToCellWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToCellWeightsValue{-0.4, -0.3, -0.2, -0.1, -0.5,
+                                                0.5, -0.2, -0.3, -0.2, -0.6,
+                                                0.6, -0.1, -0.4, -0.3, -0.7,
+                                                0.7, -0.9, -0.5,  0.8,  0.6};
+    // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size].
+    hidl_vec<uint32_t> inputToOutputWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToOutputWeightsValue{-0.8, -0.4, -0.2, -0.9, -0.1,
+                                                 -0.7,  0.3, -0.3, -0.8, -0.2,
+                                                  0.6, -0.2,  0.4, -0.7, -0.3,
+                                                 -0.5,  0.1,  0.5, -0.6, -0.4};
+    // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
+    //     “num_units”), or the second dimension of the “projection_weights”, if defined.
+    hidl_vec<uint32_t> recurrentToInputWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToInputWeightsValue{-0.2, -0.3,  0.4,
+                                                     0.1, -0.5,  0.9,
+                                                    -0.2, -0.3, -0.7,
+                                                    0.05, -0.2, -0.6};
+    // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    hidl_vec<uint32_t> recurrentToForgetWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToForgetWeightsValue{-0.5, -0.3, -0.5,
+                                                     -0.2,  0.6,  0.4,
+                                                      0.9,  0.3, -0.1,
+                                                      0.2,  0.5,  0.2};
+    // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    hidl_vec<uint32_t> recurrentToCellWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToCellWeightsValue{-0.3,  0.2,  0.1,
+                                                   -0.3,  0.8,-0.08,
+                                                   -0.2,  0.3,  0.8,
+                                                   -0.6, -0.1,  0.2};
+    // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    hidl_vec<uint32_t> recurrentToOutputWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToOutputWeightsValue{ 0.3, -0.1,  0.1,
+                                                     -0.2, -0.5, -0.7,
+                                                     -0.2, -0.6, -0.1,
+                                                     -0.4, -0.7, -0.2};
+    // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellToInputWeightsDimensions{numUnits};
+    std::vector<float> cellToInputWeightsValue{0.05, 0.1, 0.25, 0.15};
+    // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellToForgetWeightsDimensions{numUnits};
+    std::vector<float> cellToForgetWeightsValue{-0.02, -0.15, -0.25, -0.03};
+    // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellToOutputWeightsDimensions{numUnits};
+    std::vector<float> cellToOutputWeightsValue{0.1, -0.1, -0.5, 0.05};
+    // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> inputGateBiasDimensions{numUnits};
+    std::vector<float> inputGateBiasValue{0.03, 0.15, 0.22, 0.38};
+    // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> forgetGateBiasDimensions{numUnits};
+    std::vector<float> forgetGateBiasValue{0.1, -0.3, -0.2, 0.1};
+    // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellBiasDimensions{numUnits};
+    std::vector<float> cellBiasValue{-0.05, 0.72, 0.25, 0.08};
+    // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> outputGateBiasDimensions{numUnits};
+    std::vector<float> outputGateBiasValue{0.05, -0.01, 0.2, 0.1};
+    // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [output_size, num_units].
+    hidl_vec<uint32_t> projectionWeightsDimensions{numUnits, outputSize};
+    std::vector<float> projectionWeightsValue{-0.1,  0.2, 0.01,
+                                              -0.2,  0.1,  0.5,
+                                               0.3, 0.08, 0.07,
+                                               0.2, -0.4,  0.2};
+    // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
+    hidl_vec<uint32_t> projectionBiasDimensions{outputSize};
+    std::vector<float> projectionBiasValue(outputSize, 0.0f);
+    // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
+    hidl_vec<uint32_t> outputStateInDimensions{batchSize, outputSize};
+    std::vector<float> outputStateInValue(batchSize * outputSize, 0.0f);
+    // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
+    hidl_vec<uint32_t> cellStateInDimensions{batchSize, numUnits};
+    std::vector<float> cellStateInValue(batchSize * numUnits, 0.0f);
+
+    // Constant scalar values (the VTS test adds these as tensors of dim {})
+    // 20: The activation function: A value indicating the activation function:
+    //     0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
+    hidl_vec<uint32_t> activationFunctionDimensions{};
+    std::vector<int32_t> activationFunctionValue{4};
+    // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
+    //     If set to 0.0 then clipping is disabled.
+    hidl_vec<uint32_t> cellClippingThresholdDimensions{};
+    std::vector<float> cellClippingThresholdValue{0.0f};
+    // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
+    //     [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+    hidl_vec<uint32_t> projectionClippingThresholdDimensions{};
+    std::vector<float> projectionClippingThresholdValue{0.0f};
+
+    // Normalization:
+    // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at input gate.
+    hidl_vec<uint32_t> inputLayerNormWeightsDimensions{numUnits};
+    std::vector<float> inputLayerNormWeightsValue{0.1, 0.2, 0.3, 0.5};
+    // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at forget gate.
+    hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{numUnits};
+    std::vector<float> forgetLayerNormWeightsValue{0.2, 0.2, 0.4, 0.3};
+    // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at cell gate.
+    hidl_vec<uint32_t> cellLayerNormWeightsDimensions{numUnits};
+    std::vector<float> cellLayerNormWeightsValue{0.7, 0.2, 0.3, 0.8};
+    // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at output gate.
+    hidl_vec<uint32_t> outputLayerNormWeightsDimensions{numUnits};
+    std::vector<float> outputLayerNormWeightsValue{0.6, 0.2, 0.2, 0.5};
+
+    // Outputs:
+    //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
+    //     CIFG, or [batch_size, num_units * 3] without CIFG.
+    // HOWEVER, by looking at the code, seems that it's the opposite: (cifg ? 3 : 4) * numUnits
+    // Refer to: android/frameworks/ml/nn/common/operations/LSTM.cpp:319
+    //           android/frameworks/ml/nn/common/operations/LSTMTest.cpp:114
+    //           tensorflow/tensorflow/contrib/lite/kernels/lstm.cc:332
+    hidl_vec<uint32_t> scratchBufferDimensions{batchSize, numUnits * 4};
+    std::vector<float> scratchBufferValue(batchSize * numUnits * 4, 0.0f);
+    //  1: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
+    hidl_vec<uint32_t> outputStateOutDimensions{batchSize, outputSize};
+    std::vector<float> outputStateOutValue { 0.02440767f,  0.12802738f, -0.00170918f,
+                                            -0.00692428f,  0.08487406f,  0.06344498f};
+    //  2: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
+    hidl_vec<uint32_t> cellStateOutDimensions{batchSize, numUnits};
+    std::vector<float> cellStateOutValue {-0.45177122f,  0.37691566f,  0.22542511f,  0.23240635f,
+                                          -0.25258583f,  0.33042118f,  0.01730525f,  0.36660123f};
+    //  3: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
+    //     effectively the same as the current “output state (out)” value.
+    hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
+    std::vector<float> outputValue{ 0.02440767f, 0.12802738f, -0.00170918f,
+                                   -0.00692428f, 0.08487406f,  0.06344498f};
+
+   LstmTestImpl<HalPolicy>(inputDimensions,                       inputValue,
+                           inputToInputWeightsDimensions,         inputToInputWeightsValue,
+                           inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
+                           inputToCellWeightsDimensions,          inputToCellWeightsValue,
+                           inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
+                           recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
+                           recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
+                           recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
+                           recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
+                           cellToInputWeightsDimensions,          cellToInputWeightsValue,
+                           cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
+                           cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
+                           inputGateBiasDimensions,               inputGateBiasValue,
+                           forgetGateBiasDimensions,              forgetGateBiasValue,
+                           cellBiasDimensions,                    cellBiasValue,
+                           outputGateBiasDimensions,              outputGateBiasValue,
+                           projectionWeightsDimensions,           projectionWeightsValue,
+                           projectionBiasDimensions,              projectionBiasValue,
+                           outputStateInDimensions,               outputStateInValue,
+                           cellStateInDimensions,                 cellStateInValue,
+                           activationFunctionDimensions,          activationFunctionValue,
+                           cellClippingThresholdDimensions,       cellClippingThresholdValue,
+                           projectionClippingThresholdDimensions, projectionClippingThresholdValue,
+                           inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
+                           forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
+                           cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
+                           outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
+                           scratchBufferDimensions,               scratchBufferValue,
+                           outputStateOutDimensions,              outputStateOutValue,
+                           cellStateOutDimensions,                cellStateOutValue,
+                           outputDimensions,                      outputValue,
+                           compute);
 }
 
-BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionTest, COMPUTE_DEVICES)
+template <typename HalPolicy>
+void LstmCifgPeepholeProjectionNoClippingLayerNorm(armnn::Compute compute)
 {
-    LstmCifgPeepholeNoProjection(sample);
-}
+    // This replicates android/frameworks/ml/nn/runtime/test/generated/vts_models/layer_norm_lstm.model.cpp
+    // with values from android/frameworks/ml/nn/runtime/test/generated/examples/layer_norm_lstm.example.cpp
+    // and weights, biases and scalars passed as CONSTANT_COPY tensors (instead of MODEL_INPUT tensors).
 
-BOOST_DATA_TEST_CASE(LstmNoCifgPeepholeProjectionTest, COMPUTE_DEVICES)
-{
-    LstmNoCifgPeepholeProjection(sample);
-}
+    uint32_t batchSize = 2;
+    uint32_t inputSize = 5;
+    uint32_t numUnits = 4;
+    uint32_t outputSize = 3;
 
-BOOST_DATA_TEST_CASE(LstmCifgPeepholeNoProjectionBatch2Test, COMPUTE_DEVICES)
-{
-    LstmCifgPeepholeNoProjectionBatch2(sample);
-}
+    // Inputs:
+    // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
+    //     “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
+    hidl_vec<uint32_t> inputDimensions{batchSize, inputSize};
+    std::vector<float> inputValue{ 0.7f, 0.8f, 0.1f, 0.2f, 0.3f,  // batch 0
+                                   0.3f, 0.2f, 0.9f, 0.8f, 0.1f}; // batch 1
 
-BOOST_AUTO_TEST_SUITE_END()
+    // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size], where “num_units” corresponds to the number of cell units.
+    hidl_vec<uint32_t> inputToInputWeightsDimensions{0};
+    std::vector<float> inputToInputWeightsValue;
+    // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size].
+    hidl_vec<uint32_t> inputToForgetWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToForgetWeightsValue{-0.6, -0.1,  0.3,  0.2,  0.9,
+                                                 -0.5, -0.2, -0.4,  0.3, -0.8,
+                                                 -0.4,  0.3, -0.5, -0.4, -0.6,
+                                                  0.3, -0.4, -0.6, -0.5, -0.5};
+    // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units, input_size].
+    hidl_vec<uint32_t> inputToCellWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToCellWeightsValue{-0.4, -0.3, -0.2, -0.1, -0.5,
+                                                0.5, -0.2, -0.3, -0.2, -0.6,
+                                                0.6, -0.1, -0.4, -0.3, -0.7,
+                                                0.7, -0.9, -0.5,  0.8,  0.6};
+    // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, input_size].
+    hidl_vec<uint32_t> inputToOutputWeightsDimensions{numUnits, inputSize};
+    std::vector<float> inputToOutputWeightsValue{-0.8, -0.4, -0.2, -0.9, -0.1,
+                                                 -0.7,  0.3, -0.3, -0.8, -0.2,
+                                                  0.6, -0.2,  0.4, -0.7, -0.3,
+                                                 -0.5,  0.1,  0.5, -0.6, -0.4};
+    // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
+    //     “num_units”), or the second dimension of the “projection_weights”, if defined.
+    hidl_vec<uint32_t> recurrentToInputWeightsDimensions{0};
+    std::vector<float> recurrentToInputWeightsValue;
+    // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    hidl_vec<uint32_t> recurrentToForgetWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToForgetWeightsValue{-0.5, -0.3, -0.5,
+                                                     -0.2,  0.6,  0.4,
+                                                      0.9,  0.3, -0.1,
+                                                      0.2,  0.5,  0.2};
+    // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    hidl_vec<uint32_t> recurrentToCellWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToCellWeightsValue{-0.3,  0.2,  0.1,
+                                                   -0.3,  0.8,-0.08,
+                                                   -0.2,  0.3,  0.8,
+                                                   -0.6, -0.1,  0.2};
+    // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [num_units, output_size].
+    hidl_vec<uint32_t> recurrentToOutputWeightsDimensions{numUnits, outputSize};
+    std::vector<float> recurrentToOutputWeightsValue{  0.3, -0.1,  0.1,
+                                                      -0.2, -0.5, -0.7,
+                                                      -0.2, -0.6, -0.1,
+                                                      -0.4, -0.7, -0.2};
+    // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellToInputWeightsDimensions{0};
+    std::vector<float> cellToInputWeightsValue;
+    // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellToForgetWeightsDimensions{numUnits};
+    std::vector<float> cellToForgetWeightsValue{-0.02, -0.15, -0.25, -0.03};
+    // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellToOutputWeightsDimensions{numUnits};
+    std::vector<float> cellToOutputWeightsValue{0.1, -0.1, -0.5, 0.05};
+    // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> inputGateBiasDimensions{0};
+    std::vector<float> inputGateBiasValue;
+    // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> forgetGateBiasDimensions{numUnits};
+    std::vector<float> forgetGateBiasValue{0.1, -0.3, -0.2, 0.1};
+    // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> cellBiasDimensions{numUnits};
+    std::vector<float> cellBiasValue{-0.05, 0.72, 0.25, 0.08};
+    // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
+    hidl_vec<uint32_t> outputGateBiasDimensions{numUnits};
+    std::vector<float> outputGateBiasValue{0.05, -0.01, 0.2, 0.1};
+    // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
+    //     [output_size, num_units].
+    hidl_vec<uint32_t> projectionWeightsDimensions{numUnits, outputSize};
+    std::vector<float> projectionWeightsValue{-0.1,  0.2, 0.01,
+                                              -0.2,  0.1,  0.5,
+                                               0.3, 0.08, 0.07,
+                                               0.2, -0.4,  0.2};
+    // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
+    hidl_vec<uint32_t> projectionBiasDimensions{outputSize};
+    std::vector<float> projectionBiasValue(outputSize, 0.0f);
+    // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
+    hidl_vec<uint32_t> outputStateInDimensions{batchSize, outputSize};
+    std::vector<float> outputStateInValue(batchSize * outputSize, 0.0f);
+    // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
+    hidl_vec<uint32_t> cellStateInDimensions{batchSize, numUnits};
+    std::vector<float> cellStateInValue(batchSize * numUnits, 0.0f);
+
+    // Constant scalar values (the VTS test adds these as tensors of dim {})
+    // 20: The activation function: A value indicating the activation function:
+    //     0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
+    hidl_vec<uint32_t> activationFunctionDimensions{};
+    std::vector<int32_t> activationFunctionValue{4};
+    // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
+    //     If set to 0.0 then clipping is disabled.
+    hidl_vec<uint32_t> cellClippingThresholdDimensions{};
+    std::vector<float> cellClippingThresholdValue{0.0f};
+    // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
+    //     [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+    hidl_vec<uint32_t> projectionClippingThresholdDimensions{};
+    std::vector<float> projectionClippingThresholdValue{0.0f};
+
+    // Normalization:
+    // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at input gate.
+    hidl_vec<uint32_t> inputLayerNormWeightsDimensions{numUnits};
+    std::vector<float> inputLayerNormWeightsValue{0.1, 0.2, 0.3, 0.5};
+    // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at forget gate.
+    hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{numUnits};
+    std::vector<float> forgetLayerNormWeightsValue{0.2, 0.2, 0.4, 0.3};
+    // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at cell gate.
+    hidl_vec<uint32_t> cellLayerNormWeightsDimensions{numUnits};
+    std::vector<float> cellLayerNormWeightsValue{0.7, 0.2, 0.3, 0.8};
+    // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
+    //     Used to rescale normalized inputs to activation at output gate.
+    hidl_vec<uint32_t> outputLayerNormWeightsDimensions{numUnits};
+    std::vector<float> outputLayerNormWeightsValue{0.6, 0.2, 0.2, 0.5};
+
+    // Outputs:
+    //  0: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
+    //     CIFG, or [batch_size, num_units * 3] without CIFG.
+    // HOWEVER, by looking at the code, seems that it's the opposite: (cifg ? 3 : 4) * numUnits
+    // Refer to: android/frameworks/ml/nn/common/operations/LSTM.cpp:319
+    //           android/frameworks/ml/nn/common/operations/LSTMTest.cpp:114
+    //           tensorflow/tensorflow/contrib/lite/kernels/lstm.cc:332
+    hidl_vec<uint32_t> scratchBufferDimensions{batchSize, numUnits * 3};
+    std::vector<float> scratchBufferValue(batchSize * numUnits * 3, 0.0f);
+    //  1: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
+    hidl_vec<uint32_t> outputStateOutDimensions{batchSize, outputSize};
+    std::vector<float> outputStateOutValue { 0.02129706f,  0.14081624f,  0.01127331f,
+                                            -0.02263505f,  0.09169482f,  0.07691758f};
+    //  2: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
+    hidl_vec<uint32_t> cellStateOutDimensions{batchSize, numUnits};
+    std::vector<float> cellStateOutValue{-0.35102980f,  0.42610350f,  0.21463650f,  0.27716520f,
+                                         -0.18855170f,  0.32522000f,  0.02036650f,  0.48967660f};
+    //  3: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
+    //     effectively the same as the current “output state (out)” value.
+    hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
+    std::vector<float> outputValue{ 0.02129706f,  0.14081624f,  0.01127331f,
+                                   -0.02263505f,  0.09169482f,  0.07691758f};
+
+    LstmTestImpl<HalPolicy>(inputDimensions,                       inputValue,
+                            inputToInputWeightsDimensions,         inputToInputWeightsValue,
+                            inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
+                            inputToCellWeightsDimensions,          inputToCellWeightsValue,
+                            inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
+                            recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
+                            recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
+                            recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
+                            recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
+                            cellToInputWeightsDimensions,          cellToInputWeightsValue,
+                            cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
+                            cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
+                            inputGateBiasDimensions,               inputGateBiasValue,
+                            forgetGateBiasDimensions,              forgetGateBiasValue,
+                            cellBiasDimensions,                    cellBiasValue,
+                            outputGateBiasDimensions,              outputGateBiasValue,
+                            projectionWeightsDimensions,           projectionWeightsValue,
+                            projectionBiasDimensions,              projectionBiasValue,
+                            outputStateInDimensions,               outputStateInValue,
+                            cellStateInDimensions,                 cellStateInValue,
+                            activationFunctionDimensions,          activationFunctionValue,
+                            cellClippingThresholdDimensions,       cellClippingThresholdValue,
+                            projectionClippingThresholdDimensions, projectionClippingThresholdValue,
+                            inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
+                            forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
+                            cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
+                            outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
+                            scratchBufferDimensions,               scratchBufferValue,
+                            outputStateOutDimensions,              outputStateOutValue,
+                            cellStateOutDimensions,                cellStateOutValue,
+                            outputDimensions,                      outputValue,
+                            compute);
+}