Add negative testing to message_handler

Also restructure the scatter file to not be constrained of the
artificial 512k size limit of the APP_IMAGE region.

Add missing sections in DDR for both scatter file and linker script.

Change-Id: I3d9bc8aeae1b1c11ab994276be64a2850cc23f8e
diff --git a/applications/message_handler/test/main.cpp b/applications/message_handler/test/main.cpp
index 1c5e108..6a4d26d 100644
--- a/applications/message_handler/test/main.cpp
+++ b/applications/message_handler/test/main.cpp
@@ -58,17 +58,10 @@
         }                                                                               \
     } while (0)
 
-// Nr. of tasks to process inferences with, reserves driver & runs inference (Normally 1 per NPU, but not a must)
-#if defined(ETHOSU) && defined(ETHOSU_NPU_COUNT) && ETHOSU_NPU_COUNT > 0
-constexpr size_t NUM_PARALLEL_TASKS = ETHOSU_NPU_COUNT;
-#else
-constexpr size_t NUM_PARALLEL_TASKS = 1;
-#endif
-
 // TensorArena static initialisation
 constexpr size_t arenaSize = TENSOR_ARENA_SIZE;
 
-__attribute__((section(".bss.tensor_arena"), aligned(16))) uint8_t tensorArena[NUM_PARALLEL_TASKS][arenaSize];
+__attribute__((section(".bss.tensor_arena"), aligned(16))) uint8_t tensorArena[arenaSize];
 
 // Message queue from remote host
 __attribute__((section("ethosu_core_in_queue"))) MessageQueue::Queue<1000> inputMessageQueue;
@@ -89,7 +82,7 @@
     TaskParams() :
         messageNotify(xSemaphoreCreateBinary()),
         inferenceInputQueue(std::make_shared<Queue<ethosu_core_inference_req>>()),
-        inferenceOutputQueue(xQueueCreate(10, sizeof(ethosu_core_inference_rsp))),
+        inferenceOutputQueue(xQueueCreate(5, sizeof(ethosu_core_inference_rsp))),
         networks(std::make_shared<WithIndexedNetworks>()) {}
 
     SemaphoreHandle_t messageNotify;
@@ -101,21 +94,16 @@
     std::shared_ptr<Networks> networks;
 };
 
-struct InferenceTaskParams {
-    TaskParams *taskParams;
-    uint8_t *arena;
-};
-
 void inferenceTask(void *pvParameters) {
     printf("Starting inference task\n");
-    InferenceTaskParams *params = reinterpret_cast<InferenceTaskParams *>(pvParameters);
+    TaskParams *params = reinterpret_cast<TaskParams *>(pvParameters);
 
-    InferenceHandler process(params->arena,
+    InferenceHandler process(tensorArena,
                              arenaSize,
-                             params->taskParams->inferenceInputQueue,
-                             params->taskParams->inferenceOutputQueue,
-                             params->taskParams->messageNotify,
-                             params->taskParams->networks);
+                             params->inferenceInputQueue,
+                             params->inferenceOutputQueue,
+                             params->messageNotify,
+                             params->networks);
 
     process.run();
 }
@@ -134,6 +122,97 @@
     process.run();
 }
 
+ethosu_core_network_info_req networkInfoIndexedRequest(uint64_t user_arg, uint32_t index) {
+    ethosu_core_network_info_req req = {user_arg,                   // user_arg
+                                        {                           // network
+                                         ETHOSU_CORE_NETWORK_INDEX, // type
+                                         {{
+                                             index, // index
+                                             0      // ignored padding of union
+                                         }}}};
+    return req;
+}
+
+ethosu_core_network_info_req networkInfoBufferRequest(uint64_t user_arg, unsigned char *ptr, uint32_t ptr_size) {
+    ethosu_core_network_info_req req = {user_arg,                    // user_arg
+                                        {                            // network
+                                         ETHOSU_CORE_NETWORK_BUFFER, // type
+                                         {{
+                                             reinterpret_cast<uint32_t>(ptr), // ptr
+                                             ptr_size                         // size
+                                         }}}};
+    return req;
+}
+
+ethosu_core_network_info_rsp networkInfoResponse(uint64_t user_arg) {
+    ethosu_core_network_info_rsp rsp = {
+        user_arg,               // user_arg
+        "Vela Optimised",       // description
+        1,                      // ifm_count
+        {/* not comparable */}, // ifm_sizes
+        1,                      // ofm_count
+        {/* not comparable */}, // ofm_sizes
+        ETHOSU_CORE_STATUS_OK   // status
+    };
+    return rsp;
+}
+
+ethosu_core_inference_req
+inferenceIndexedRequest(uint64_t user_arg, uint32_t index, uint8_t *data, uint32_t data_size) {
+    ethosu_core_inference_req req = {
+        user_arg, // user_arg
+        1,        // ifm_count
+        {         // ifm:
+         {
+             reinterpret_cast<uint32_t>(&inputData[0]), // ptr
+             sizeof(inputData)                          // size
+         }},
+        1, // ofm_count
+        {  // ofm
+         {
+             reinterpret_cast<uint32_t>(data), // ptr
+             data_size                         // size
+         }},
+        {                           // network
+         ETHOSU_CORE_NETWORK_INDEX, // type
+         {{
+             index, // index
+             0      // ignored padding of union
+         }}},
+        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
+        0                         // pmu_cycle_counter_enable
+    };
+    return req;
+}
+
+ethosu_core_inference_req
+inferenceBufferRequest(uint64_t user_arg, unsigned char *ptr, uint32_t ptr_size, uint8_t *data, uint32_t data_size) {
+    ethosu_core_inference_req req = {
+        user_arg, // user_arg
+        1,        // ifm_count
+        {         // ifm:
+         {
+             reinterpret_cast<uint32_t>(&inputData[0]), // ptr
+             sizeof(inputData)                          // size
+         }},
+        1, // ofm_count
+        {  // ofm
+         {
+             reinterpret_cast<uint32_t>(data), // ptr
+             data_size                         // size
+         }},
+        {                            // network
+         ETHOSU_CORE_NETWORK_BUFFER, // type
+         {{
+             reinterpret_cast<uint32_t>(ptr), // ptr
+             ptr_size                         // size
+         }}},
+        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
+        0                         // pmu_cycle_counter_enable
+    };
+    return req;
+}
+
 void testPing(MessageClient client) {
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_PING));
     TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_PONG));
@@ -210,25 +289,12 @@
 #endif
 }
 
-void testNetworkInfo(MessageClient client) {
+void testNetworkInfoIndex(MessageClient client) {
     const uint64_t fake_user_arg     = 42;
-    ethosu_core_network_info_req req = {fake_user_arg,              // user_arg
-                                        {                           // network
-                                         ETHOSU_CORE_NETWORK_INDEX, // type
-                                         {{
-                                             0, // index
-                                             0  // ignored padding of union
-                                         }}}};
+    const uint32_t network_index     = 0;
+    ethosu_core_network_info_req req = networkInfoIndexedRequest(fake_user_arg, network_index);
     ethosu_core_network_info_rsp rsp;
-    ethosu_core_network_info_rsp expected_rsp = {
-        req.user_arg,           // user_arg
-        "Vela Optimised",       // description
-        1,                      // ifm_count
-        {/* not comparable */}, // ifm_sizes
-        1,                      // ofm_count
-        {/* not comparable */}, // ofm_sizes
-        0                       // status
-    };
+    ethosu_core_network_info_rsp expected_rsp = networkInfoResponse(fake_user_arg);
 
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_REQ, req));
     TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_RSP, rsp));
@@ -240,32 +306,56 @@
     TEST_ASSERT(expected_rsp.status == rsp.status);
 }
 
-void testInferenceRun(MessageClient client) {
+void testNetworkInfoNonExistantIndex(MessageClient client) {
+    const uint64_t fake_user_arg     = 42;
+    const uint32_t network_index     = 1;
+    ethosu_core_network_info_req req = networkInfoIndexedRequest(fake_user_arg, network_index);
+    ethosu_core_network_info_rsp rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_REQ, req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_RSP, rsp));
+
+    TEST_ASSERT(fake_user_arg == rsp.user_arg);
+    TEST_ASSERT(ETHOSU_CORE_STATUS_ERROR == rsp.status);
+}
+
+void testNetworkInfoBuffer(MessageClient client) {
+    const uint64_t fake_user_arg     = 42;
+    uint32_t size                    = sizeof(Model0::networkModelData);
+    unsigned char *ptr               = Model0::networkModelData;
+    ethosu_core_network_info_req req = networkInfoBufferRequest(fake_user_arg, ptr, size);
+    ethosu_core_network_info_rsp rsp;
+    ethosu_core_network_info_rsp expected_rsp = networkInfoResponse(fake_user_arg);
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_REQ, req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_RSP, rsp));
+
+    TEST_ASSERT(expected_rsp.user_arg == rsp.user_arg);
+    TEST_ASSERT(std::strncmp(expected_rsp.desc, rsp.desc, sizeof(rsp.desc)) == 0);
+    TEST_ASSERT(expected_rsp.ifm_count == rsp.ifm_count);
+    TEST_ASSERT(expected_rsp.ofm_count == rsp.ofm_count);
+    TEST_ASSERT(expected_rsp.status == rsp.status);
+}
+
+void testNetworkInfoUnparsableBuffer(MessageClient client) {
+    const uint64_t fake_user_arg     = 42;
+    uint32_t size                    = sizeof(Model0::networkModelData) / 4;
+    unsigned char *ptr               = Model0::networkModelData + size;
+    ethosu_core_network_info_req req = networkInfoBufferRequest(fake_user_arg, ptr, size);
+    ethosu_core_network_info_rsp rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_REQ, req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_NETWORK_INFO_RSP, rsp));
+
+    TEST_ASSERT(42 == rsp.user_arg);
+    TEST_ASSERT(ETHOSU_CORE_STATUS_ERROR == rsp.status);
+}
+
+void testInferenceRunIndex(MessageClient client) {
+    const uint64_t fake_user_arg = 42;
+    const uint32_t network_index = 0;
     uint8_t data[sizeof(expectedOutputData)];
-    const uint64_t fake_user_arg  = 42;
-    ethosu_core_inference_req req = {
-        fake_user_arg, // user_arg
-        1,             // ifm_count
-        {              // ifm:
-         {
-             reinterpret_cast<uint32_t>(&inputData[0]), // ptr
-             sizeof(inputData)                          // size
-         }},
-        1, // ofm_count
-        {  // ofm
-         {
-             reinterpret_cast<uint32_t>(&data[0]), // ptr
-             sizeof(data)                          // size
-         }},
-        {                           // network
-         ETHOSU_CORE_NETWORK_INDEX, // type
-         {{
-             0, // index
-             0  // ignored padding of union
-         }}},
-        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
-        0                         // pmu_cycle_counter_enable
-    };
+    ethosu_core_inference_req req = inferenceIndexedRequest(fake_user_arg, network_index, data, sizeof(data));
     ethosu_core_inference_rsp rsp;
 
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
@@ -279,18 +369,101 @@
     TEST_ASSERT(std::memcmp(rsp.pmu_event_config, req.pmu_event_config, sizeof(req.pmu_event_config)) == 0);
 }
 
+void testInferenceRunNonExistingIndex(MessageClient client) {
+    const uint64_t fake_user_arg = 42;
+    const uint32_t network_index = 1;
+    uint8_t data[sizeof(expectedOutputData)];
+    ethosu_core_inference_req req = inferenceIndexedRequest(fake_user_arg, network_index, data, sizeof(data));
+    ethosu_core_inference_rsp rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, rsp));
+
+    TEST_ASSERT(req.user_arg == rsp.user_arg);
+    TEST_ASSERT(rsp.status == ETHOSU_CORE_STATUS_ERROR);
+}
+
+void testInferenceRunBuffer(MessageClient client) {
+    const uint64_t fake_user_arg = 42;
+    uint32_t network_size        = sizeof(Model0::networkModelData);
+    unsigned char *network_ptr   = Model0::networkModelData;
+    uint8_t data[sizeof(expectedOutputData)];
+    ethosu_core_inference_req req =
+        inferenceBufferRequest(fake_user_arg, network_ptr, network_size, data, sizeof(data));
+    ethosu_core_inference_rsp rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, rsp));
+
+    TEST_ASSERT(req.user_arg == rsp.user_arg);
+    TEST_ASSERT(rsp.ofm_count == 1);
+    TEST_ASSERT(std::memcmp(expectedOutputData, data, sizeof(expectedOutputData)) == 0);
+    TEST_ASSERT(rsp.status == ETHOSU_CORE_STATUS_OK);
+    TEST_ASSERT(rsp.pmu_cycle_counter_enable == req.pmu_cycle_counter_enable);
+    TEST_ASSERT(std::memcmp(rsp.pmu_event_config, req.pmu_event_config, sizeof(req.pmu_event_config)) == 0);
+}
+
+void testInferenceRunUnparsableBuffer(MessageClient client) {
+    const uint64_t fake_user_arg = 42;
+    uint32_t network_size        = sizeof(Model0::networkModelData) / 4;
+    unsigned char *network_ptr   = Model0::networkModelData + network_size;
+    uint8_t data[sizeof(expectedOutputData)];
+    ethosu_core_inference_req req =
+        inferenceBufferRequest(fake_user_arg, network_ptr, network_size, data, sizeof(data));
+    ethosu_core_inference_rsp rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, rsp));
+
+    TEST_ASSERT(req.user_arg == rsp.user_arg);
+    TEST_ASSERT(rsp.status == ETHOSU_CORE_STATUS_ERROR);
+}
+
+void testSequentiallyQueuedInferenceRuns(MessageClient client) {
+    int runs = 5;
+    uint8_t data[runs][sizeof(expectedOutputData)];
+    const uint64_t fake_user_arg = 42;
+    const uint32_t network_index = 0;
+    ethosu_core_inference_req req;
+    ethosu_core_inference_rsp rsp[runs];
+
+    for (int i = 0; i < runs; i++) {
+        vTaskDelay(150);
+
+        req = inferenceIndexedRequest(fake_user_arg + i, network_index, data[i], sizeof(expectedOutputData));
+        TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
+    }
+
+    for (int i = 0; i < runs; i++) {
+        TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, rsp[i]));
+        TEST_ASSERT(uint64_t(fake_user_arg + i) == rsp[i].user_arg);
+        TEST_ASSERT(rsp[i].ofm_count == 1);
+        TEST_ASSERT(std::memcmp(expectedOutputData, data[i], sizeof(expectedOutputData)) == 0);
+        TEST_ASSERT(rsp[i].status == ETHOSU_CORE_STATUS_OK);
+        TEST_ASSERT(rsp[i].pmu_cycle_counter_enable == req.pmu_cycle_counter_enable);
+        TEST_ASSERT(std::memcmp(rsp[i].pmu_event_config, req.pmu_event_config, sizeof(req.pmu_event_config)) == 0);
+    }
+}
+
 void clientTask(void *) {
     printf("Starting client task\n");
 
     MessageClient client(*inputMessageQueue.toQueue(), *outputMessageQueue.toQueue(), mailbox);
 
-    vTaskDelay(10);
+    vTaskDelay(50);
 
     testPing(client);
     testVersion(client);
     testCapabilities(client);
-    testNetworkInfo(client);
-    testInferenceRun(client);
+    testNetworkInfoIndex(client);
+    testNetworkInfoNonExistantIndex(client);
+    testNetworkInfoBuffer(client);
+    testNetworkInfoUnparsableBuffer(client);
+    testInferenceRunIndex(client);
+    testInferenceRunNonExistingIndex(client);
+    testInferenceRunBuffer(client);
+    testInferenceRunUnparsableBuffer(client);
+    testSequentiallyQueuedInferenceRuns(client);
 
     exit(0);
 }
@@ -300,7 +473,6 @@
  * scheduler is started.
  */
 TaskParams taskParams;
-InferenceTaskParams infParams[NUM_PARALLEL_TASKS];
 
 } // namespace
 
@@ -320,19 +492,14 @@
         return ret;
     }
 
-    // One inference task for each NPU
-    for (size_t n = 0; n < NUM_PARALLEL_TASKS; n++) {
-        infParams[n].taskParams = &taskParams;
-        infParams[n].arena      = reinterpret_cast<uint8_t *>(&tensorArena[n]);
-        ret                     = xTaskCreate(inferenceTask, "inferenceTask", 8 * 1024, &infParams[n], 3, nullptr);
-        if (ret != pdPASS) {
-            printf("Failed to create 'inferenceTask%d'\n", n);
-            return ret;
-        }
+    ret = xTaskCreate(inferenceTask, "inferenceTask", 8 * 1024, &taskParams, 3, nullptr);
+    if (ret != pdPASS) {
+        printf("Failed to create 'inferenceTask'\n");
+        return ret;
     }
 
     // Task for handling incoming /outgoing messages from the remote host
-    ret = xTaskCreate(clientTask, "clientTask", 512, nullptr, 2, nullptr);
+    ret = xTaskCreate(clientTask, "clientTask", 1024, nullptr, 2, nullptr);
     if (ret != pdPASS) {
         printf("Failed to create 'messageTask'\n");
         return ret;
diff --git a/applications/message_handler/test/message_client.cpp b/applications/message_handler/test/message_client.cpp
index 4209564..39d1392 100644
--- a/applications/message_handler/test/message_client.cpp
+++ b/applications/message_handler/test/message_client.cpp
@@ -34,7 +34,7 @@
 
 bool MessageClient::sendInputMessage(const uint32_t type, const void *src, uint32_t length) {
     if (!input.write(type, src, length)) {
-        printf("ERROR: Msg: Failed to write ping request. No mailbox message sent\n");
+        printf("ERROR: Msg: Failed to write message request. No mailbox message sent\n");
         return false;
     }
 
@@ -44,7 +44,7 @@
 }
 
 bool MessageClient::waitAndReadOutputMessage(const uint32_t expected_type, uint8_t *dst, uint32_t length) {
-    constexpr TickType_t delay    = pdMS_TO_TICKS(2);
+    constexpr TickType_t delay    = pdMS_TO_TICKS(5);
     constexpr TickType_t deadline = pdMS_TO_TICKS(/* 1 minute */ 60 * 1000 * 1000);
     struct ethosu_core_msg msg;
 
@@ -68,7 +68,7 @@
     }
 
     if (msg.type != expected_type) {
-        printf("ERROR: Wrong message type\n");
+        printf("ERROR: Wrong message type. Got %" PRIu32 " expected %" PRIu32 "\n", msg.type, expected_type);
         return false;
     }