blob: d5799449b272cdde7f0f77bba083de5d294c8845 [file] [log] [blame]
Kristofer Jonsson43ce4912020-11-20 09:42:53 +01001/*
Kristofer Jonsson5410db12022-01-27 17:39:06 +01002 * Copyright (c) 2019-2022 Arm Limited. All rights reserved.
Kristofer Jonsson43ce4912020-11-20 09:42:53 +01003 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19/****************************************************************************
20 * Includes
21 ****************************************************************************/
22
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010023#include "FreeRTOS.h"
Lior Dekel3e128622021-09-23 18:57:12 +030024#include "portmacro.h"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010025#include "queue.h"
Anton Moberg6a7703e2021-03-02 15:14:29 +010026#include "semphr.h"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010027#include "task.h"
28
Anton Moberg6a7703e2021-03-02 15:14:29 +010029#include <inttypes.h>
30#include <stdio.h>
31#include <vector>
32
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010033#include "inference_process.hpp"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010034
Anton Moberg6a7703e2021-03-02 15:14:29 +010035// Model data (Defined & changable by modifiying compile definition in CMakeLists.txt)
36#include "input.h"
37#include "model.h"
38#include "output.h"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010039
40using namespace std;
41using namespace InferenceProcess;
42
43/****************************************************************************
Anton Moberg6a7703e2021-03-02 15:14:29 +010044 * Defines
45 ****************************************************************************/
46
47// Nr. of tasks to process inferences with. Task reserves driver & runs inference (Normally 1 per NPU, but not a must)
48#define NUM_INFERENCE_TASKS 1
49// Nr. of tasks to create jobs and recieve responses
Lior Dekeledaf2f82021-08-10 17:06:16 +030050#define NUM_JOB_TASKS 2
Anton Moberg6a7703e2021-03-02 15:14:29 +010051// Nr. of jobs to create per job task
52#define NUM_JOBS_PER_TASK 1
53
54// Tensor arena size
55#ifdef TENSOR_ARENA_SIZE // If defined in model.h
56#define TENSOR_ARENA_SIZE_PER_INFERENCE TENSOR_ARENA_SIZE
57#else // If not defined, use maximum available
58#define TENSOR_ARENA_SIZE_PER_INFERENCE 2000000 / NUM_INFERENCE_TASKS
59#endif
60
61/****************************************************************************
Davide Grohmannbbbd9162022-05-09 15:15:20 +020062 * Override new operators to call in FreeRTOS allocator
63 ****************************************************************************/
64
65void *operator new(size_t size) {
66 return pvPortMalloc(size);
67}
68
69void *operator new[](size_t size) {
70 return pvPortMalloc(size);
71}
72
73void operator delete(void *ptr) {
74 vPortFree(ptr);
75}
76
77void operator delete(void *ptr, std::size_t) {
78 vPortFree(ptr);
79}
80
81void operator delete[](void *ptr) {
82 vPortFree(ptr);
83}
84
85void operator delete[](void *ptr, std::size_t) {
86 vPortFree(ptr);
87}
88
89/****************************************************************************
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010090 * InferenceJob
91 ****************************************************************************/
92
Anton Moberg6a7703e2021-03-02 15:14:29 +010093struct ProcessTaskParams {
Lior Dekeledaf2f82021-08-10 17:06:16 +030094 ProcessTaskParams() : queueHandle(nullptr), tensorArena(nullptr), arenaSize(0) {}
Anton Moberg6a7703e2021-03-02 15:14:29 +010095 ProcessTaskParams(QueueHandle_t _queue, uint8_t *_tensorArena, size_t _arenaSize) :
96 queueHandle(_queue), tensorArena(_tensorArena), arenaSize(_arenaSize) {}
Anton Moberg1a679c42021-02-10 08:45:39 +010097
Anton Moberg6a7703e2021-03-02 15:14:29 +010098 QueueHandle_t queueHandle;
99 uint8_t *tensorArena;
100 size_t arenaSize;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100101};
102
Lior Dekeledaf2f82021-08-10 17:06:16 +0300103namespace {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100104// Number of total completed jobs, needed to exit application correctly if NUM_JOB_TASKS > 1
Lior Dekeledaf2f82021-08-10 17:06:16 +0300105int totalCompletedJobs = 0;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100106
Anton Moberg6a7703e2021-03-02 15:14:29 +0100107// TensorArena static initialisation
Lior Dekeledaf2f82021-08-10 17:06:16 +0300108const size_t arenaSize = TENSOR_ARENA_SIZE_PER_INFERENCE;
109
110// Declare below variables in global scope to avoid stack since FreeRTOS resets stack when the scheduler is started
111QueueHandle_t inferenceProcessQueue;
112ProcessTaskParams taskParams[NUM_INFERENCE_TASKS];
113} // namespace
114
Anton Moberg6a7703e2021-03-02 15:14:29 +0100115__attribute__((section(".bss.tensor_arena"), aligned(16)))
116uint8_t inferenceProcessTensorArena[NUM_INFERENCE_TASKS][arenaSize];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100117
Anton Moberg6a7703e2021-03-02 15:14:29 +0100118// Wrapper around InferenceProcess::InferenceJob. Adds responseQueue and status for FreeRTOS multi-tasking purposes.
119struct xInferenceJob : public InferenceJob {
120 QueueHandle_t responseQueue;
121 bool status;
122
123 xInferenceJob() : InferenceJob(), responseQueue(nullptr), status(false) {}
124 xInferenceJob(const string &_name,
125 const DataPtr &_networkModel,
126 const vector<DataPtr> &_input,
127 const vector<DataPtr> &_output,
128 const vector<DataPtr> &_expectedOutput,
129 const size_t _numBytesToPrint,
Kristofer Jonsson5410db12022-01-27 17:39:06 +0100130 void *_userArg,
Anton Moberg6a7703e2021-03-02 15:14:29 +0100131 QueueHandle_t _queue) :
Kristofer Jonsson5410db12022-01-27 17:39:06 +0100132 InferenceJob(_name, _networkModel, _input, _output, _expectedOutput, _numBytesToPrint, _userArg),
Anton Moberg6a7703e2021-03-02 15:14:29 +0100133 responseQueue(_queue), status(false) {}
134};
135
136/****************************************************************************
137 * Mutex & Semaphore
138 * Overrides weak-linked symbols in ethosu_driver.c to implement thread handling
139 ****************************************************************************/
140
141extern "C" {
142
143void *ethosu_mutex_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300144 SemaphoreHandle_t sem = xSemaphoreCreateMutex();
145 if (sem == NULL) {
146 printf("Error: Failed to create mutex.\n");
147 }
148 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100149}
150
Ledion Daja60c57372022-04-05 15:04:11 +0200151int ethosu_mutex_lock(void *mutex) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100152 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300153 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
154 printf("Error: Failed to lock mutex.\n");
Ledion Daja60c57372022-04-05 15:04:11 +0200155 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300156 }
Ledion Daja60c57372022-04-05 15:04:11 +0200157 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100158}
159
Ledion Daja60c57372022-04-05 15:04:11 +0200160int ethosu_mutex_unlock(void *mutex) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100161 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300162 if (xSemaphoreGive(handle) != pdTRUE) {
163 printf("Error: Failed to unlock mutex.\n");
Ledion Daja60c57372022-04-05 15:04:11 +0200164 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300165 }
Ledion Daja60c57372022-04-05 15:04:11 +0200166 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100167}
168
169void *ethosu_semaphore_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300170 SemaphoreHandle_t sem = xSemaphoreCreateBinary();
171 if (sem == NULL) {
172 printf("Error: Failed to create semaphore.\n");
173 }
174 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100175}
176
Ledion Daja60c57372022-04-05 15:04:11 +0200177int ethosu_semaphore_take(void *sem) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100178 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300179 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
180 printf("Error: Failed to take semaphore.\n");
Ledion Daja60c57372022-04-05 15:04:11 +0200181 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300182 }
Ledion Daja60c57372022-04-05 15:04:11 +0200183 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100184}
185
Ledion Daja60c57372022-04-05 15:04:11 +0200186int ethosu_semaphore_give(void *sem) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100187 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300188
189 if (xPortIsInsideInterrupt()) {
Ledion Daja60c57372022-04-05 15:04:11 +0200190 if (xSemaphoreGiveFromISR(handle, NULL) != pdTRUE) {
191 printf("Error: Failed to give semaphore from ISR.\n");
192 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300193 }
194 } else {
Ledion Daja60c57372022-04-05 15:04:11 +0200195 /* A FreeRTOS binary semaphore is fundamentally a queue that can only hold one item. If the queue is full,
196 * xSemaphoreGive will return a pdFALSE value. Ignoring the return value in here, as a semaphore give failure
197 * does not affect the application correctness. */
198 if (xSemaphoreGive(handle) != pdTRUE) {
199 // do nothing
Lior Dekel3e128622021-09-23 18:57:12 +0300200 }
201 }
Ledion Daja60c57372022-04-05 15:04:11 +0200202 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100203}
204}
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100205
206/****************************************************************************
207 * Functions
208 ****************************************************************************/
209
Anton Moberg6a7703e2021-03-02 15:14:29 +0100210// inferenceProcessTask - Run jobs from queue with available driver
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100211void inferenceProcessTask(void *pvParameters) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100212 ProcessTaskParams params = *reinterpret_cast<ProcessTaskParams *>(pvParameters);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100213
Anton Moberg6a7703e2021-03-02 15:14:29 +0100214 class InferenceProcess inferenceProcess(params.tensorArena, params.arenaSize);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100215
Anton Moberg6a7703e2021-03-02 15:14:29 +0100216 for (;;) {
217 xInferenceJob *xJob;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100218
Lior Dekel3e128622021-09-23 18:57:12 +0300219 if (xQueueReceive(params.queueHandle, &xJob, portMAX_DELAY) != pdPASS) {
220 printf("Error: inferenceProcessTask failed in receive from Q.\n");
221 exit(1);
222 }
223
Anton Moberg6a7703e2021-03-02 15:14:29 +0100224 bool status = inferenceProcess.runJob(*xJob);
225 xJob->status = status;
Lior Dekel3e128622021-09-23 18:57:12 +0300226 if (xQueueSend(xJob->responseQueue, &xJob, portMAX_DELAY) != pdPASS) {
227 printf("Error: inferenceProcessTask failed in send to Q.\n");
228 exit(1);
229 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100230 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100231 vTaskDelete(nullptr);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100232}
233
Anton Moberg6a7703e2021-03-02 15:14:29 +0100234// inferenceSenderTask - Creates NUM_INFERNECE_JOBS jobs, queues them, and then listens for completion status
235void inferenceSenderTask(void *pvParameters) {
236 int ret = 0;
237
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100238 QueueHandle_t _inferenceProcessQueue = reinterpret_cast<QueueHandle_t>(pvParameters);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100239 xInferenceJob jobs[NUM_JOBS_PER_TASK];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100240
241 // Create queue for response messages
Anton Moberg6a7703e2021-03-02 15:14:29 +0100242 QueueHandle_t senderQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100243
Anton Moberg6a7703e2021-03-02 15:14:29 +0100244 // Create and queue the jobs
245 for (int n = 0; n < NUM_JOBS_PER_TASK; n++) {
246 // Create job
247 xInferenceJob *job = &jobs[n];
248 job->name = string(modelName);
249 job->networkModel = DataPtr(networkModelData, sizeof(networkModelData));
250 job->input.push_back(DataPtr(inputData, sizeof(inputData)));
251 job->expectedOutput.push_back(DataPtr(expectedOutputData, sizeof(expectedOutputData)));
252 job->responseQueue = senderQueue;
253 // Send job
Lior Dekel3e128622021-09-23 18:57:12 +0300254 printf("inferenceSenderTask: Sending inference job: job=%p, name=%s\n", job, job->name.c_str());
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100255 if (xQueueSend(_inferenceProcessQueue, &job, portMAX_DELAY) != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300256 printf("Error: inferenceSenderTask failed in send to Q.\n");
257 exit(1);
258 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100259 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100260
Anton Moberg6a7703e2021-03-02 15:14:29 +0100261 // Listen for completion status
262 do {
263 xInferenceJob *pSendJob;
Lior Dekel3e128622021-09-23 18:57:12 +0300264 if (xQueueReceive(senderQueue, &pSendJob, portMAX_DELAY) != pdPASS) {
265 printf("Error: inferenceSenderTask failed in receive from Q.\n");
266 exit(1);
267 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100268 printf("inferenceSenderTask: received response for job: %s, status = %u\n",
269 pSendJob->name.c_str(),
270 pSendJob->status);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100271
Anton Moberg6a7703e2021-03-02 15:14:29 +0100272 totalCompletedJobs++;
273 ret = (pSendJob->status);
274 if (pSendJob->status != 0) {
275 break;
276 }
277 } while (totalCompletedJobs < NUM_JOBS_PER_TASK * NUM_JOB_TASKS);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100278
Anton Moberg6a7703e2021-03-02 15:14:29 +0100279 vQueueDelete(senderQueue);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100280
Anton Moberg6a7703e2021-03-02 15:14:29 +0100281 printf("FreeRTOS application returning %d.\n", ret);
282 exit(ret);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100283}
284
Anton Moberg6a7703e2021-03-02 15:14:29 +0100285/****************************************************************************
286 * Application
287 ****************************************************************************/
Anton Moberg6a7703e2021-03-02 15:14:29 +0100288// FreeRTOS application. NOTE: Additional tasks may require increased heap size.
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100289int main() {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100290 BaseType_t ret;
291 inferenceProcessQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100292
Anton Moberg6a7703e2021-03-02 15:14:29 +0100293 // inferenceSender tasks to create and queue the jobs
294 for (int n = 0; n < NUM_JOB_TASKS; n++) {
295 ret = xTaskCreate(inferenceSenderTask, "inferenceSenderTask", 2 * 1024, inferenceProcessQueue, 2, nullptr);
296 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300297 printf("Error: Failed to create 'inferenceSenderTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100298 exit(1);
299 }
300 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100301
Anton Moberg6a7703e2021-03-02 15:14:29 +0100302 // Create inferenceProcess tasks to process the queued jobs
303 for (int n = 0; n < NUM_INFERENCE_TASKS; n++) {
304 taskParams[n] = ProcessTaskParams(inferenceProcessQueue, inferenceProcessTensorArena[n], arenaSize);
Kristofer Jonsson99f19422021-07-01 22:15:02 +0200305 ret = xTaskCreate(inferenceProcessTask, "inferenceProcessTask", 8 * 1024, &taskParams[n], 3, nullptr);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100306 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300307 printf("Error: Failed to create 'inferenceProcessTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100308 exit(1);
309 }
310 }
311
312 // Start Scheduler
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100313 vTaskStartScheduler();
314
Lior Dekel3e128622021-09-23 18:57:12 +0300315 printf("Error: FreeRTOS application failed to initialise.\n");
Anton Moberg6a7703e2021-03-02 15:14:29 +0100316 exit(1);
317
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100318 return 0;
Lior Dekeledaf2f82021-08-10 17:06:16 +0300319}