blob: 2dbc3a8fca5a88187cc3e5fecf24fc510a9ff35f [file] [log] [blame]
Kristofer Jonsson43ce4912020-11-20 09:42:53 +01001/*
Jonny Svärd87f6f7e2023-01-16 16:05:36 +01002 * SPDX-FileCopyrightText: Copyright 2019-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
Kristofer Jonsson43ce4912020-11-20 09:42:53 +01003 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the License); you may
6 * not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18/****************************************************************************
19 * Includes
20 ****************************************************************************/
21
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010022#include "FreeRTOS.h"
Lior Dekel3e128622021-09-23 18:57:12 +030023#include "portmacro.h"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010024#include "queue.h"
Anton Moberg6a7703e2021-03-02 15:14:29 +010025#include "semphr.h"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010026#include "task.h"
27
Anton Moberg6a7703e2021-03-02 15:14:29 +010028#include <inttypes.h>
29#include <stdio.h>
30#include <vector>
31
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010032#include "inference_process.hpp"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010033
Anton Moberg6a7703e2021-03-02 15:14:29 +010034// Model data (Defined & changable by modifiying compile definition in CMakeLists.txt)
35#include "input.h"
36#include "model.h"
37#include "output.h"
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010038
39using namespace std;
40using namespace InferenceProcess;
41
42/****************************************************************************
Anton Moberg6a7703e2021-03-02 15:14:29 +010043 * Defines
44 ****************************************************************************/
45
46// Nr. of tasks to process inferences with. Task reserves driver & runs inference (Normally 1 per NPU, but not a must)
47#define NUM_INFERENCE_TASKS 1
48// Nr. of tasks to create jobs and recieve responses
Lior Dekeledaf2f82021-08-10 17:06:16 +030049#define NUM_JOB_TASKS 2
Anton Moberg6a7703e2021-03-02 15:14:29 +010050// Nr. of jobs to create per job task
51#define NUM_JOBS_PER_TASK 1
52
53// Tensor arena size
54#ifdef TENSOR_ARENA_SIZE // If defined in model.h
55#define TENSOR_ARENA_SIZE_PER_INFERENCE TENSOR_ARENA_SIZE
56#else // If not defined, use maximum available
57#define TENSOR_ARENA_SIZE_PER_INFERENCE 2000000 / NUM_INFERENCE_TASKS
58#endif
59
60/****************************************************************************
Davide Grohmannbbbd9162022-05-09 15:15:20 +020061 * Override new operators to call in FreeRTOS allocator
62 ****************************************************************************/
63
64void *operator new(size_t size) {
65 return pvPortMalloc(size);
66}
67
68void *operator new[](size_t size) {
69 return pvPortMalloc(size);
70}
71
72void operator delete(void *ptr) {
73 vPortFree(ptr);
74}
75
76void operator delete(void *ptr, std::size_t) {
77 vPortFree(ptr);
78}
79
80void operator delete[](void *ptr) {
81 vPortFree(ptr);
82}
83
84void operator delete[](void *ptr, std::size_t) {
85 vPortFree(ptr);
86}
87
88/****************************************************************************
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010089 * InferenceJob
90 ****************************************************************************/
91
Anton Moberg6a7703e2021-03-02 15:14:29 +010092struct ProcessTaskParams {
Lior Dekeledaf2f82021-08-10 17:06:16 +030093 ProcessTaskParams() : queueHandle(nullptr), tensorArena(nullptr), arenaSize(0) {}
Anton Moberg6a7703e2021-03-02 15:14:29 +010094 ProcessTaskParams(QueueHandle_t _queue, uint8_t *_tensorArena, size_t _arenaSize) :
95 queueHandle(_queue), tensorArena(_tensorArena), arenaSize(_arenaSize) {}
Anton Moberg1a679c42021-02-10 08:45:39 +010096
Anton Moberg6a7703e2021-03-02 15:14:29 +010097 QueueHandle_t queueHandle;
98 uint8_t *tensorArena;
99 size_t arenaSize;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100100};
101
Lior Dekeledaf2f82021-08-10 17:06:16 +0300102namespace {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100103// Number of total completed jobs, needed to exit application correctly if NUM_JOB_TASKS > 1
Lior Dekeledaf2f82021-08-10 17:06:16 +0300104int totalCompletedJobs = 0;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100105
Anton Moberg6a7703e2021-03-02 15:14:29 +0100106// TensorArena static initialisation
Lior Dekeledaf2f82021-08-10 17:06:16 +0300107const size_t arenaSize = TENSOR_ARENA_SIZE_PER_INFERENCE;
108
109// Declare below variables in global scope to avoid stack since FreeRTOS resets stack when the scheduler is started
110QueueHandle_t inferenceProcessQueue;
111ProcessTaskParams taskParams[NUM_INFERENCE_TASKS];
112} // namespace
113
Anton Moberg6a7703e2021-03-02 15:14:29 +0100114__attribute__((section(".bss.tensor_arena"), aligned(16)))
115uint8_t inferenceProcessTensorArena[NUM_INFERENCE_TASKS][arenaSize];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100116
Anton Moberg6a7703e2021-03-02 15:14:29 +0100117// Wrapper around InferenceProcess::InferenceJob. Adds responseQueue and status for FreeRTOS multi-tasking purposes.
118struct xInferenceJob : public InferenceJob {
119 QueueHandle_t responseQueue;
120 bool status;
121
122 xInferenceJob() : InferenceJob(), responseQueue(nullptr), status(false) {}
123 xInferenceJob(const string &_name,
124 const DataPtr &_networkModel,
125 const vector<DataPtr> &_input,
126 const vector<DataPtr> &_output,
127 const vector<DataPtr> &_expectedOutput,
128 const size_t _numBytesToPrint,
Kristofer Jonsson5410db12022-01-27 17:39:06 +0100129 void *_userArg,
Anton Moberg6a7703e2021-03-02 15:14:29 +0100130 QueueHandle_t _queue) :
Kristofer Jonsson5410db12022-01-27 17:39:06 +0100131 InferenceJob(_name, _networkModel, _input, _output, _expectedOutput, _numBytesToPrint, _userArg),
Anton Moberg6a7703e2021-03-02 15:14:29 +0100132 responseQueue(_queue), status(false) {}
133};
134
135/****************************************************************************
136 * Mutex & Semaphore
137 * Overrides weak-linked symbols in ethosu_driver.c to implement thread handling
138 ****************************************************************************/
139
140extern "C" {
141
142void *ethosu_mutex_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300143 SemaphoreHandle_t sem = xSemaphoreCreateMutex();
144 if (sem == NULL) {
145 printf("Error: Failed to create mutex.\n");
146 }
147 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100148}
149
Ledion Daja60c57372022-04-05 15:04:11 +0200150int ethosu_mutex_lock(void *mutex) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100151 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300152 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
153 printf("Error: Failed to lock mutex.\n");
Ledion Daja60c57372022-04-05 15:04:11 +0200154 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300155 }
Ledion Daja60c57372022-04-05 15:04:11 +0200156 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100157}
158
Ledion Daja60c57372022-04-05 15:04:11 +0200159int ethosu_mutex_unlock(void *mutex) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100160 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300161 if (xSemaphoreGive(handle) != pdTRUE) {
162 printf("Error: Failed to unlock mutex.\n");
Ledion Daja60c57372022-04-05 15:04:11 +0200163 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300164 }
Ledion Daja60c57372022-04-05 15:04:11 +0200165 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100166}
167
168void *ethosu_semaphore_create(void) {
Jonny Svärd87f6f7e2023-01-16 16:05:36 +0100169 SemaphoreHandle_t sem = xSemaphoreCreateCounting(255, 0); // max, initial val
Lior Dekel3e128622021-09-23 18:57:12 +0300170 if (sem == NULL) {
171 printf("Error: Failed to create semaphore.\n");
172 }
173 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100174}
175
Jonny Svärd244a3102023-12-18 18:26:44 +0100176int ethosu_semaphore_take(void *sem, uint64_t timeout) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100177 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Jonny Svärd244a3102023-12-18 18:26:44 +0100178 if (xSemaphoreTake(handle, (TickType_t)timeout) != pdTRUE) {
Ledion Daja60c57372022-04-05 15:04:11 +0200179 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300180 }
Ledion Daja60c57372022-04-05 15:04:11 +0200181 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100182}
183
Ledion Daja60c57372022-04-05 15:04:11 +0200184int ethosu_semaphore_give(void *sem) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100185 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300186
187 if (xPortIsInsideInterrupt()) {
Ledion Daja60c57372022-04-05 15:04:11 +0200188 if (xSemaphoreGiveFromISR(handle, NULL) != pdTRUE) {
189 printf("Error: Failed to give semaphore from ISR.\n");
190 return -1;
Lior Dekel3e128622021-09-23 18:57:12 +0300191 }
192 } else {
Ledion Daja60c57372022-04-05 15:04:11 +0200193 if (xSemaphoreGive(handle) != pdTRUE) {
Jonny Svärd87f6f7e2023-01-16 16:05:36 +0100194 printf("Error: Failed to give semaphore.\n");
Ledion Daja60c57372022-04-05 15:04:11 +0200195 // do nothing
Lior Dekel3e128622021-09-23 18:57:12 +0300196 }
197 }
Ledion Daja60c57372022-04-05 15:04:11 +0200198 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100199}
200}
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100201
202/****************************************************************************
203 * Functions
204 ****************************************************************************/
205
Anton Moberg6a7703e2021-03-02 15:14:29 +0100206// inferenceProcessTask - Run jobs from queue with available driver
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100207void inferenceProcessTask(void *pvParameters) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100208 ProcessTaskParams params = *reinterpret_cast<ProcessTaskParams *>(pvParameters);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100209
Anton Moberg6a7703e2021-03-02 15:14:29 +0100210 class InferenceProcess inferenceProcess(params.tensorArena, params.arenaSize);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100211
Anton Moberg6a7703e2021-03-02 15:14:29 +0100212 for (;;) {
213 xInferenceJob *xJob;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100214
Lior Dekel3e128622021-09-23 18:57:12 +0300215 if (xQueueReceive(params.queueHandle, &xJob, portMAX_DELAY) != pdPASS) {
216 printf("Error: inferenceProcessTask failed in receive from Q.\n");
217 exit(1);
218 }
219
Anton Moberg6a7703e2021-03-02 15:14:29 +0100220 bool status = inferenceProcess.runJob(*xJob);
221 xJob->status = status;
Lior Dekel3e128622021-09-23 18:57:12 +0300222 if (xQueueSend(xJob->responseQueue, &xJob, portMAX_DELAY) != pdPASS) {
223 printf("Error: inferenceProcessTask failed in send to Q.\n");
224 exit(1);
225 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100226 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100227 vTaskDelete(nullptr);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100228}
229
Anton Moberg6a7703e2021-03-02 15:14:29 +0100230// inferenceSenderTask - Creates NUM_INFERNECE_JOBS jobs, queues them, and then listens for completion status
231void inferenceSenderTask(void *pvParameters) {
232 int ret = 0;
233
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100234 QueueHandle_t _inferenceProcessQueue = reinterpret_cast<QueueHandle_t>(pvParameters);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100235 xInferenceJob jobs[NUM_JOBS_PER_TASK];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100236
237 // Create queue for response messages
Anton Moberg6a7703e2021-03-02 15:14:29 +0100238 QueueHandle_t senderQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100239
Anton Moberg6a7703e2021-03-02 15:14:29 +0100240 // Create and queue the jobs
241 for (int n = 0; n < NUM_JOBS_PER_TASK; n++) {
242 // Create job
243 xInferenceJob *job = &jobs[n];
244 job->name = string(modelName);
245 job->networkModel = DataPtr(networkModelData, sizeof(networkModelData));
246 job->input.push_back(DataPtr(inputData, sizeof(inputData)));
247 job->expectedOutput.push_back(DataPtr(expectedOutputData, sizeof(expectedOutputData)));
248 job->responseQueue = senderQueue;
249 // Send job
Lior Dekel3e128622021-09-23 18:57:12 +0300250 printf("inferenceSenderTask: Sending inference job: job=%p, name=%s\n", job, job->name.c_str());
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100251 if (xQueueSend(_inferenceProcessQueue, &job, portMAX_DELAY) != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300252 printf("Error: inferenceSenderTask failed in send to Q.\n");
253 exit(1);
254 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100255 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100256
Anton Moberg6a7703e2021-03-02 15:14:29 +0100257 // Listen for completion status
258 do {
259 xInferenceJob *pSendJob;
Lior Dekel3e128622021-09-23 18:57:12 +0300260 if (xQueueReceive(senderQueue, &pSendJob, portMAX_DELAY) != pdPASS) {
261 printf("Error: inferenceSenderTask failed in receive from Q.\n");
262 exit(1);
263 }
Ledion Dajab0aacb42023-02-17 09:37:58 +0100264 printf("inferenceSenderTask: received response for job: %s, status = %d\n",
Anton Moberg6a7703e2021-03-02 15:14:29 +0100265 pSendJob->name.c_str(),
266 pSendJob->status);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100267
Anton Moberg6a7703e2021-03-02 15:14:29 +0100268 totalCompletedJobs++;
269 ret = (pSendJob->status);
270 if (pSendJob->status != 0) {
271 break;
272 }
273 } while (totalCompletedJobs < NUM_JOBS_PER_TASK * NUM_JOB_TASKS);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100274
Anton Moberg6a7703e2021-03-02 15:14:29 +0100275 vQueueDelete(senderQueue);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100276
Anton Moberg6a7703e2021-03-02 15:14:29 +0100277 printf("FreeRTOS application returning %d.\n", ret);
278 exit(ret);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100279}
280
Anton Moberg6a7703e2021-03-02 15:14:29 +0100281/****************************************************************************
282 * Application
283 ****************************************************************************/
Anton Moberg6a7703e2021-03-02 15:14:29 +0100284// FreeRTOS application. NOTE: Additional tasks may require increased heap size.
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100285int main() {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100286 BaseType_t ret;
287 inferenceProcessQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100288
Anton Moberg6a7703e2021-03-02 15:14:29 +0100289 // inferenceSender tasks to create and queue the jobs
290 for (int n = 0; n < NUM_JOB_TASKS; n++) {
291 ret = xTaskCreate(inferenceSenderTask, "inferenceSenderTask", 2 * 1024, inferenceProcessQueue, 2, nullptr);
292 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300293 printf("Error: Failed to create 'inferenceSenderTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100294 exit(1);
295 }
296 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100297
Anton Moberg6a7703e2021-03-02 15:14:29 +0100298 // Create inferenceProcess tasks to process the queued jobs
299 for (int n = 0; n < NUM_INFERENCE_TASKS; n++) {
300 taskParams[n] = ProcessTaskParams(inferenceProcessQueue, inferenceProcessTensorArena[n], arenaSize);
Kristofer Jonsson99f19422021-07-01 22:15:02 +0200301 ret = xTaskCreate(inferenceProcessTask, "inferenceProcessTask", 8 * 1024, &taskParams[n], 3, nullptr);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100302 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300303 printf("Error: Failed to create 'inferenceProcessTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100304 exit(1);
305 }
306 }
307
308 // Start Scheduler
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100309 vTaskStartScheduler();
310
Lior Dekel3e128622021-09-23 18:57:12 +0300311 printf("Error: FreeRTOS application failed to initialise.\n");
Anton Moberg6a7703e2021-03-02 15:14:29 +0100312 exit(1);
313
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100314 return 0;
Lior Dekeledaf2f82021-08-10 17:06:16 +0300315}