IVGCVSW-7165 Implement TosaRefPreCompiledWorkload::Execute()

 * Added FP32 support for TOSA Reference Backend.
 * Added main block creation to OptimizeSubgraphView, this will only
   occur once.

Change-Id: I169dac50b78e2c693da6327962c9f1d3ae3bd712
Signed-off-by: James Conroy <james.conroy@arm.com>
Signed-off-by: Matthew Sloyan <matthew.sloyan@arm.com>
diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp
index 3c14bfd..71d2012 100644
--- a/src/backends/tosaCommon/TosaMappings.cpp
+++ b/src/backends/tosaCommon/TosaMappings.cpp
@@ -26,13 +26,14 @@
 TosaSerializationBasicBlock* GetTosaMapping(const LayerType type,
                                             const std::vector<const TensorInfo*>& inputs,
                                             const std::vector<const TensorInfo*>& outputs,
-                                            const BaseDescriptor& /*descriptor*/)
+                                            const BaseDescriptor& /*descriptor*/,
+                                            bool isMain = false)
 {
     switch (type)
     {
         case LayerType::Addition:
         {
-            return ConvertAdditionToTosaOperator(inputs, outputs);
+            return ConvertAdditionToTosaOperator(inputs, outputs, isMain);
         }
         default:
         {
@@ -43,7 +44,7 @@
     }
 }
 
-TosaSerializationBasicBlock* GetTosaMappingFromLayer(Layer* layer)
+TosaSerializationBasicBlock* GetTosaMappingFromLayer(Layer* layer, bool isMain = false)
 {
     std::vector<const TensorInfo*> inputs;
     for (auto inputSlot : layer->GetInputSlots())
@@ -60,7 +61,8 @@
     TosaSerializationBasicBlock* basicBlock = GetTosaMapping(layer->GetType(),
                                                              inputs,
                                                              outputs,
-                                                             layer->GetParameters());
+                                                             layer->GetParameters(),
+                                                             isMain);
     SetBasicBlockConstantTensorData(layer, basicBlock);
     return basicBlock;
 }
diff --git a/src/backends/tosaCommon/TosaMappings.hpp b/src/backends/tosaCommon/TosaMappings.hpp
index c721bca..7f137ca 100644
--- a/src/backends/tosaCommon/TosaMappings.hpp
+++ b/src/backends/tosaCommon/TosaMappings.hpp
@@ -26,8 +26,9 @@
 TosaSerializationBasicBlock* GetTosaMapping(const LayerType type,
                                             const std::vector<const TensorInfo*>& inputs,
                                             const std::vector<const TensorInfo*>& outputs,
-                                            const BaseDescriptor& /*descriptor*/);
+                                            const BaseDescriptor& /*descriptor*/,
+                                            bool isMain);
 
 // Function called in armnn::OptimizeSubgraphView() when access to armnn::Layer is available
 // and there is an option to set tosa basic block data from constant layer tenors available from the input layer.
-TosaSerializationBasicBlock* GetTosaMappingFromLayer(Layer* layer);
+TosaSerializationBasicBlock* GetTosaMappingFromLayer(Layer* layer, bool isMain);
diff --git a/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp b/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp
index 98ea03a..7967977 100644
--- a/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp
+++ b/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp
@@ -6,7 +6,8 @@
 #include "AdditionOperator.hpp"
 
 TosaSerializationBasicBlock* ConvertAdditionToTosaOperator(const std::vector<const TensorInfo*>& inputs,
-                                                           const std::vector<const TensorInfo*>& outputs)
+                                                           const std::vector<const TensorInfo*>& outputs,
+                                                           bool isMain)
 {
     // A helper function with static global variables ensures uniqueness
     // for dynamically generating input, output and block names
@@ -15,6 +16,12 @@
     std::string outputName = std::string("Op_ADD_output0_") + GetUniqueTosaMappingID();
     std::string blockName  = std::string("Op_ADD_block_")   + GetUniqueTosaMappingID();
 
+    // If it's the first block, overwrite block name with main.
+    if (isMain)
+    {
+        blockName = std::string("main");
+    }
+
     TosaSerializationOperator* op = new TosaSerializationOperator(Op_ADD,
                                                                   Attribute_NONE,
                                                                   nullptr,
diff --git a/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp
index 2a9c479..f467bb6 100644
--- a/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp
+++ b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp
@@ -14,5 +14,6 @@
 using namespace tosa;
 
 TosaSerializationBasicBlock* ConvertAdditionToTosaOperator(const std::vector<const TensorInfo*>& inputs,
-                                                           const std::vector<const TensorInfo*>& outputs);
+                                                           const std::vector<const TensorInfo*>& outputs,
+                                                           bool isMain);
 
diff --git a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
index f4435bd..d7cc5d8 100644
--- a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
+++ b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp
@@ -72,7 +72,7 @@
 {
     TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true);
     TosaSerializationBasicBlock* basicBlock =
-        GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor());
+        GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor(), false);
     AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD");
 }
 
@@ -100,14 +100,14 @@
     add->GetOutputSlot(0).SetTensorInfo(info);
 
     TosaSerializationBasicBlock* basicBlock =
-        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(add));
+        GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(add), false);
     AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD");
 }
 
 TEST_CASE("GetTosaMapping_Unimplemented")
 {
     TosaSerializationBasicBlock* basicBlock =
-        GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor());
+        GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor(), false);
 
     CHECK(basicBlock->GetName() == "");
     CHECK(basicBlock->GetTensors().size() == 0);
diff --git a/src/backends/tosaReference/TosaRefBackend.cpp b/src/backends/tosaReference/TosaRefBackend.cpp
index 688cf93..e3a516a 100644
--- a/src/backends/tosaReference/TosaRefBackend.cpp
+++ b/src/backends/tosaReference/TosaRefBackend.cpp
@@ -85,6 +85,9 @@
     OptimizationViews optimizationViews(modelOptions);
     auto handler = std::make_unique<TosaSerializationHandler>();
 
+    // A main block should only be added once.
+    bool isMain = true;
+
     auto it = subgraph.endIConnectable();
     while (it != subgraph.beginIConnectable())
     {
@@ -97,8 +100,13 @@
             continue;
         }
 
-        tosa::TosaSerializationBasicBlock* mappings = GetTosaMappingFromLayer(&base);
+        tosa::TosaSerializationBasicBlock* mappings = GetTosaMappingFromLayer(&base, isMain);
         handler.get()->GetBlocks().push_back(mappings);
+
+        if(isMain)
+        {
+            isMain = false;
+        }
     }
 
     auto compiledBlob =
diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp
index f5f34a8..c2b0b1b 100644
--- a/src/backends/tosaReference/TosaRefLayerSupport.cpp
+++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp
@@ -113,7 +113,7 @@
             break;
     }
 
-    auto mappings = GetTosaMapping(type, inputInfos, outputInfos, descriptor);
+    auto mappings = GetTosaMapping(type, inputInfos, outputInfos, descriptor, false);
     if (mappings->GetName() == "")
     {
         // There currently isn't a TOSA mapping for this layer, as the default was returned.
diff --git a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp
index c4af4d4..18d2900 100644
--- a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp
+++ b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp
@@ -11,13 +11,91 @@
 TosaRefPreCompiledWorkload::TosaRefPreCompiledWorkload(const PreCompiledQueueDescriptor& descriptor,
                                                        const WorkloadInfo& info)
     : BaseWorkload<PreCompiledQueueDescriptor>(descriptor, info)
+    , m_workloadInfo(info)
 {
-    // Do nothing for now
+    // Check that the workload is holding a pointer to a valid pre-compiled object
+    if (m_Data.m_PreCompiledObject == nullptr)
+    {
+        throw InvalidArgumentException(
+                "TosaRefPreCompiledWorkload requires a valid pre-compiled object (TosaSerializationHandler).");
+    }
 }
 
 void TosaRefPreCompiledWorkload::Execute() const
 {
-    // Do nothing for now
+    uint32_t numInputBuffers  = static_cast<uint32_t>(m_Data.m_Inputs.size());
+    uint32_t numOutputBuffers = static_cast<uint32_t>(m_Data.m_Outputs.size());
+
+    tosa::TosaSerializationHandler* handler = static_cast<tosa::TosaSerializationHandler*>(m_Data.m_PreCompiledObject);
+
+    std::vector<std::string> input_names = handler->GetInputs();
+    std::vector<std::string> output_names = handler->GetOutputs();
+
+    TosaReference::IModelRunner runner;
+    GraphStatus status;
+
+    // Initialise the model runner with the TosaSerializationHandler
+    status = runner.initialize(*handler);
+    if(status != GraphStatus::TOSA_VALID)
+    {
+        throw armnn::Exception("An error has occurred while initialising the TOSA Reference Model.");
+    }
+
+    // Set the inputs
+    for (uint32_t inputSlotIdx = 0; inputSlotIdx < numInputBuffers; ++inputSlotIdx)
+    {
+        DataType dataType = m_workloadInfo.m_InputTensorInfos[inputSlotIdx].GetDataType();
+        switch (dataType)
+        {
+            case DataType::Float32:
+                SetInput<float>(runner, input_names[inputSlotIdx], inputSlotIdx);
+                break;
+            default:
+                throw armnn::Exception("Input data type is unsupported in TOSA Reference Backend.");
+        }
+    }
+
+    // Run the TOSA Reference Model
+    status = runner.run();
+    if(status != GraphStatus::TOSA_VALID)
+    {
+        throw armnn::Exception("An error has occurred while running the TOSA Reference Model.");
+    }
+
+    // Gets the outputs
+    for (uint32_t outputSlotIdx = 0; outputSlotIdx < numOutputBuffers; ++outputSlotIdx)
+    {
+        DataType dataType = m_workloadInfo.m_OutputTensorInfos[outputSlotIdx].GetDataType();
+        switch (dataType)
+        {
+            case DataType::Float32:
+                GetOutput<float>(runner, output_names[outputSlotIdx], outputSlotIdx);
+                break;
+            default:
+                throw armnn::Exception("Output data type is unsupported in TOSA Reference Backend.");
+        }
+    }
+}
+
+template <typename T>
+void TosaRefPreCompiledWorkload::SetInput(TosaReference::IModelRunner& runner,
+                                          std::string inputName,
+                                          uint32_t inputIndex) const
+{
+    std::vector<T> inputData(m_Data.m_Inputs[inputIndex]->GetShape().GetNumElements());
+    m_Data.m_Inputs[inputIndex]->CopyOutTo(inputData.data());
+
+    runner.setInput<T>(inputName, inputData);
+}
+
+template <typename T>
+void TosaRefPreCompiledWorkload::GetOutput(TosaReference::IModelRunner& runner,
+                                           std::string outputName,
+                                           uint32_t outputIndex) const
+{
+    std::vector<T> actualOutputs = runner.getOutput<T>(outputName);
+
+    m_Data.m_Outputs[outputIndex]->CopyInFrom(actualOutputs.data());
 }
 
 bool TosaRefPreCompiledWorkloadValidate(std::string*)
diff --git a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp
index 3097269..2b3a314 100644
--- a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp
+++ b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp
@@ -7,6 +7,9 @@
 
 #include "armnn/backends/Workload.hpp"
 
+#include <graph_status.h>
+#include <model_runner.h>
+
 #include <memory>
 #include <string>
 #include <vector>
@@ -38,6 +41,14 @@
     {
         this->m_Data.m_Outputs[slot] = tensorHandle;
     }
+
+    template <typename T>
+    void SetInput(TosaReference::IModelRunner& runner, std::string inputName, uint32_t inputIndex) const;
+
+    template <typename T>
+    void GetOutput(TosaReference::IModelRunner& runner, std::string outputName, uint32_t outputIndex) const;
+
+    WorkloadInfo m_workloadInfo;
 };
 
 }    //namespace armnn