blob: 81631efcb85c9139181ed45f4107960a6c99ad8e [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/****************************************************************************
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010062 * InferenceJob
63 ****************************************************************************/
64
Anton Moberg6a7703e2021-03-02 15:14:29 +010065struct ProcessTaskParams {
Lior Dekeledaf2f82021-08-10 17:06:16 +030066 ProcessTaskParams() : queueHandle(nullptr), tensorArena(nullptr), arenaSize(0) {}
Anton Moberg6a7703e2021-03-02 15:14:29 +010067 ProcessTaskParams(QueueHandle_t _queue, uint8_t *_tensorArena, size_t _arenaSize) :
68 queueHandle(_queue), tensorArena(_tensorArena), arenaSize(_arenaSize) {}
Anton Moberg1a679c42021-02-10 08:45:39 +010069
Anton Moberg6a7703e2021-03-02 15:14:29 +010070 QueueHandle_t queueHandle;
71 uint8_t *tensorArena;
72 size_t arenaSize;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010073};
74
Lior Dekeledaf2f82021-08-10 17:06:16 +030075namespace {
Anton Moberg6a7703e2021-03-02 15:14:29 +010076// Number of total completed jobs, needed to exit application correctly if NUM_JOB_TASKS > 1
Lior Dekeledaf2f82021-08-10 17:06:16 +030077int totalCompletedJobs = 0;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010078
Anton Moberg6a7703e2021-03-02 15:14:29 +010079// TensorArena static initialisation
Lior Dekeledaf2f82021-08-10 17:06:16 +030080const size_t arenaSize = TENSOR_ARENA_SIZE_PER_INFERENCE;
81
82// Declare below variables in global scope to avoid stack since FreeRTOS resets stack when the scheduler is started
83QueueHandle_t inferenceProcessQueue;
84ProcessTaskParams taskParams[NUM_INFERENCE_TASKS];
85} // namespace
86
Anton Moberg6a7703e2021-03-02 15:14:29 +010087__attribute__((section(".bss.tensor_arena"), aligned(16)))
88uint8_t inferenceProcessTensorArena[NUM_INFERENCE_TASKS][arenaSize];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010089
Anton Moberg6a7703e2021-03-02 15:14:29 +010090// Wrapper around InferenceProcess::InferenceJob. Adds responseQueue and status for FreeRTOS multi-tasking purposes.
91struct xInferenceJob : public InferenceJob {
92 QueueHandle_t responseQueue;
93 bool status;
94
95 xInferenceJob() : InferenceJob(), responseQueue(nullptr), status(false) {}
96 xInferenceJob(const string &_name,
97 const DataPtr &_networkModel,
98 const vector<DataPtr> &_input,
99 const vector<DataPtr> &_output,
100 const vector<DataPtr> &_expectedOutput,
101 const size_t _numBytesToPrint,
Kristofer Jonsson5410db12022-01-27 17:39:06 +0100102 void *_userArg,
Anton Moberg6a7703e2021-03-02 15:14:29 +0100103 QueueHandle_t _queue) :
Kristofer Jonsson5410db12022-01-27 17:39:06 +0100104 InferenceJob(_name, _networkModel, _input, _output, _expectedOutput, _numBytesToPrint, _userArg),
Anton Moberg6a7703e2021-03-02 15:14:29 +0100105 responseQueue(_queue), status(false) {}
106};
107
108/****************************************************************************
109 * Mutex & Semaphore
110 * Overrides weak-linked symbols in ethosu_driver.c to implement thread handling
111 ****************************************************************************/
112
113extern "C" {
114
115void *ethosu_mutex_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300116 SemaphoreHandle_t sem = xSemaphoreCreateMutex();
117 if (sem == NULL) {
118 printf("Error: Failed to create mutex.\n");
119 }
120 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100121}
122
123void ethosu_mutex_lock(void *mutex) {
124 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300125 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
126 printf("Error: Failed to lock mutex.\n");
127 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100128}
129
130void ethosu_mutex_unlock(void *mutex) {
131 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300132 if (xSemaphoreGive(handle) != pdTRUE) {
133 printf("Error: Failed to unlock mutex.\n");
134 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100135}
136
137void *ethosu_semaphore_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300138 SemaphoreHandle_t sem = xSemaphoreCreateBinary();
139 if (sem == NULL) {
140 printf("Error: Failed to create semaphore.\n");
141 }
142 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100143}
144
145void ethosu_semaphore_take(void *sem) {
146 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300147 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
148 printf("Error: Failed to take semaphore.\n");
149 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100150}
151
152void ethosu_semaphore_give(void *sem) {
153 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300154 BaseType_t ret;
155
156 if (xPortIsInsideInterrupt()) {
157 ret = xSemaphoreGiveFromISR(handle, NULL);
158 if (ret != pdTRUE) {
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100159 printf("Error: Failed to give semaphore from ISR. ret - 0x%08lx\n", ret);
Lior Dekel3e128622021-09-23 18:57:12 +0300160 }
161 } else {
162 ret = xSemaphoreGive(handle);
163 if (ret != pdTRUE) {
164 /* The next line is in comment because xSemaphoreGive returns pdFAIL when
165 calling it twice in a row during this application run.
166 This failure doesn't affect the final result of the FreeRTOS application. */
167 /* printf("Error: Failed to give semaphore. ret - 0x%08x\n", ret); */
168 }
169 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100170}
171}
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100172
173/****************************************************************************
174 * Functions
175 ****************************************************************************/
176
Anton Moberg6a7703e2021-03-02 15:14:29 +0100177// inferenceProcessTask - Run jobs from queue with available driver
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100178void inferenceProcessTask(void *pvParameters) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100179 ProcessTaskParams params = *reinterpret_cast<ProcessTaskParams *>(pvParameters);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100180
Anton Moberg6a7703e2021-03-02 15:14:29 +0100181 class InferenceProcess inferenceProcess(params.tensorArena, params.arenaSize);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100182
Anton Moberg6a7703e2021-03-02 15:14:29 +0100183 for (;;) {
184 xInferenceJob *xJob;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100185
Lior Dekel3e128622021-09-23 18:57:12 +0300186 if (xQueueReceive(params.queueHandle, &xJob, portMAX_DELAY) != pdPASS) {
187 printf("Error: inferenceProcessTask failed in receive from Q.\n");
188 exit(1);
189 }
190
Anton Moberg6a7703e2021-03-02 15:14:29 +0100191 bool status = inferenceProcess.runJob(*xJob);
192 xJob->status = status;
Lior Dekel3e128622021-09-23 18:57:12 +0300193 if (xQueueSend(xJob->responseQueue, &xJob, portMAX_DELAY) != pdPASS) {
194 printf("Error: inferenceProcessTask failed in send to Q.\n");
195 exit(1);
196 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100197 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100198 vTaskDelete(nullptr);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100199}
200
Anton Moberg6a7703e2021-03-02 15:14:29 +0100201// inferenceSenderTask - Creates NUM_INFERNECE_JOBS jobs, queues them, and then listens for completion status
202void inferenceSenderTask(void *pvParameters) {
203 int ret = 0;
204
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100205 QueueHandle_t _inferenceProcessQueue = reinterpret_cast<QueueHandle_t>(pvParameters);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100206 xInferenceJob jobs[NUM_JOBS_PER_TASK];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100207
208 // Create queue for response messages
Anton Moberg6a7703e2021-03-02 15:14:29 +0100209 QueueHandle_t senderQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100210
Anton Moberg6a7703e2021-03-02 15:14:29 +0100211 // Create and queue the jobs
212 for (int n = 0; n < NUM_JOBS_PER_TASK; n++) {
213 // Create job
214 xInferenceJob *job = &jobs[n];
215 job->name = string(modelName);
216 job->networkModel = DataPtr(networkModelData, sizeof(networkModelData));
217 job->input.push_back(DataPtr(inputData, sizeof(inputData)));
218 job->expectedOutput.push_back(DataPtr(expectedOutputData, sizeof(expectedOutputData)));
219 job->responseQueue = senderQueue;
220 // Send job
Lior Dekel3e128622021-09-23 18:57:12 +0300221 printf("inferenceSenderTask: Sending inference job: job=%p, name=%s\n", job, job->name.c_str());
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100222 if (xQueueSend(_inferenceProcessQueue, &job, portMAX_DELAY) != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300223 printf("Error: inferenceSenderTask failed in send to Q.\n");
224 exit(1);
225 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100226 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100227
Anton Moberg6a7703e2021-03-02 15:14:29 +0100228 // Listen for completion status
229 do {
230 xInferenceJob *pSendJob;
Lior Dekel3e128622021-09-23 18:57:12 +0300231 if (xQueueReceive(senderQueue, &pSendJob, portMAX_DELAY) != pdPASS) {
232 printf("Error: inferenceSenderTask failed in receive from Q.\n");
233 exit(1);
234 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100235 printf("inferenceSenderTask: received response for job: %s, status = %u\n",
236 pSendJob->name.c_str(),
237 pSendJob->status);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100238
Anton Moberg6a7703e2021-03-02 15:14:29 +0100239 totalCompletedJobs++;
240 ret = (pSendJob->status);
241 if (pSendJob->status != 0) {
242 break;
243 }
244 } while (totalCompletedJobs < NUM_JOBS_PER_TASK * NUM_JOB_TASKS);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100245
Anton Moberg6a7703e2021-03-02 15:14:29 +0100246 vQueueDelete(senderQueue);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100247
Anton Moberg6a7703e2021-03-02 15:14:29 +0100248 printf("FreeRTOS application returning %d.\n", ret);
249 exit(ret);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100250}
251
Anton Moberg6a7703e2021-03-02 15:14:29 +0100252/****************************************************************************
253 * Application
254 ****************************************************************************/
Anton Moberg6a7703e2021-03-02 15:14:29 +0100255// FreeRTOS application. NOTE: Additional tasks may require increased heap size.
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100256int main() {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100257 BaseType_t ret;
258 inferenceProcessQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100259
Anton Moberg6a7703e2021-03-02 15:14:29 +0100260 // inferenceSender tasks to create and queue the jobs
261 for (int n = 0; n < NUM_JOB_TASKS; n++) {
262 ret = xTaskCreate(inferenceSenderTask, "inferenceSenderTask", 2 * 1024, inferenceProcessQueue, 2, nullptr);
263 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300264 printf("Error: Failed to create 'inferenceSenderTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100265 exit(1);
266 }
267 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100268
Anton Moberg6a7703e2021-03-02 15:14:29 +0100269 // Create inferenceProcess tasks to process the queued jobs
270 for (int n = 0; n < NUM_INFERENCE_TASKS; n++) {
271 taskParams[n] = ProcessTaskParams(inferenceProcessQueue, inferenceProcessTensorArena[n], arenaSize);
Kristofer Jonsson99f19422021-07-01 22:15:02 +0200272 ret = xTaskCreate(inferenceProcessTask, "inferenceProcessTask", 8 * 1024, &taskParams[n], 3, nullptr);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100273 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300274 printf("Error: Failed to create 'inferenceProcessTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100275 exit(1);
276 }
277 }
278
279 // Start Scheduler
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100280 vTaskStartScheduler();
281
Lior Dekel3e128622021-09-23 18:57:12 +0300282 printf("Error: FreeRTOS application failed to initialise.\n");
Anton Moberg6a7703e2021-03-02 15:14:29 +0100283 exit(1);
284
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100285 return 0;
Lior Dekeledaf2f82021-08-10 17:06:16 +0300286}