blob: 0f4009e2a7ee05bdbab8fd83601379e298168b34 [file] [log] [blame]
Kristofer Jonsson43ce4912020-11-20 09:42:53 +01001/*
Anton Moberg6a7703e2021-03-02 15:14:29 +01002 * Copyright (c) 2019-2021 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,
102 const vector<uint8_t> &_pmuEventConfig,
103 const uint32_t _pmuCycleCounterEnable,
104 QueueHandle_t _queue) :
105 InferenceJob(_name,
106 _networkModel,
107 _input,
108 _output,
109 _expectedOutput,
110 _numBytesToPrint,
111 _pmuEventConfig,
112 _pmuCycleCounterEnable),
113 responseQueue(_queue), status(false) {}
114};
115
116/****************************************************************************
117 * Mutex & Semaphore
118 * Overrides weak-linked symbols in ethosu_driver.c to implement thread handling
119 ****************************************************************************/
120
121extern "C" {
122
123void *ethosu_mutex_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300124 SemaphoreHandle_t sem = xSemaphoreCreateMutex();
125 if (sem == NULL) {
126 printf("Error: Failed to create mutex.\n");
127 }
128 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100129}
130
131void ethosu_mutex_lock(void *mutex) {
132 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300133 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
134 printf("Error: Failed to lock mutex.\n");
135 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100136}
137
138void ethosu_mutex_unlock(void *mutex) {
139 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
Lior Dekel3e128622021-09-23 18:57:12 +0300140 if (xSemaphoreGive(handle) != pdTRUE) {
141 printf("Error: Failed to unlock mutex.\n");
142 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100143}
144
145void *ethosu_semaphore_create(void) {
Lior Dekel3e128622021-09-23 18:57:12 +0300146 SemaphoreHandle_t sem = xSemaphoreCreateBinary();
147 if (sem == NULL) {
148 printf("Error: Failed to create semaphore.\n");
149 }
150 return (void *)sem;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100151}
152
153void ethosu_semaphore_take(void *sem) {
154 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300155 if (xSemaphoreTake(handle, portMAX_DELAY) != pdTRUE) {
156 printf("Error: Failed to take semaphore.\n");
157 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100158}
159
160void ethosu_semaphore_give(void *sem) {
161 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
Lior Dekel3e128622021-09-23 18:57:12 +0300162 BaseType_t ret;
163
164 if (xPortIsInsideInterrupt()) {
165 ret = xSemaphoreGiveFromISR(handle, NULL);
166 if (ret != pdTRUE) {
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100167 printf("Error: Failed to give semaphore from ISR. ret - 0x%08lx\n", ret);
Lior Dekel3e128622021-09-23 18:57:12 +0300168 }
169 } else {
170 ret = xSemaphoreGive(handle);
171 if (ret != pdTRUE) {
172 /* The next line is in comment because xSemaphoreGive returns pdFAIL when
173 calling it twice in a row during this application run.
174 This failure doesn't affect the final result of the FreeRTOS application. */
175 /* printf("Error: Failed to give semaphore. ret - 0x%08x\n", ret); */
176 }
177 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100178}
179}
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100180
181/****************************************************************************
182 * Functions
183 ****************************************************************************/
184
Anton Moberg6a7703e2021-03-02 15:14:29 +0100185// inferenceProcessTask - Run jobs from queue with available driver
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100186void inferenceProcessTask(void *pvParameters) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100187 ProcessTaskParams params = *reinterpret_cast<ProcessTaskParams *>(pvParameters);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100188
Anton Moberg6a7703e2021-03-02 15:14:29 +0100189 class InferenceProcess inferenceProcess(params.tensorArena, params.arenaSize);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100190
Anton Moberg6a7703e2021-03-02 15:14:29 +0100191 for (;;) {
192 xInferenceJob *xJob;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100193
Lior Dekel3e128622021-09-23 18:57:12 +0300194 if (xQueueReceive(params.queueHandle, &xJob, portMAX_DELAY) != pdPASS) {
195 printf("Error: inferenceProcessTask failed in receive from Q.\n");
196 exit(1);
197 }
198
Anton Moberg6a7703e2021-03-02 15:14:29 +0100199 bool status = inferenceProcess.runJob(*xJob);
200 xJob->status = status;
Lior Dekel3e128622021-09-23 18:57:12 +0300201 if (xQueueSend(xJob->responseQueue, &xJob, portMAX_DELAY) != pdPASS) {
202 printf("Error: inferenceProcessTask failed in send to Q.\n");
203 exit(1);
204 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100205 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100206 vTaskDelete(nullptr);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100207}
208
Anton Moberg6a7703e2021-03-02 15:14:29 +0100209// inferenceSenderTask - Creates NUM_INFERNECE_JOBS jobs, queues them, and then listens for completion status
210void inferenceSenderTask(void *pvParameters) {
211 int ret = 0;
212
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100213 QueueHandle_t _inferenceProcessQueue = reinterpret_cast<QueueHandle_t>(pvParameters);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100214 xInferenceJob jobs[NUM_JOBS_PER_TASK];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100215
216 // Create queue for response messages
Anton Moberg6a7703e2021-03-02 15:14:29 +0100217 QueueHandle_t senderQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100218
Anton Moberg6a7703e2021-03-02 15:14:29 +0100219 // Create and queue the jobs
220 for (int n = 0; n < NUM_JOBS_PER_TASK; n++) {
221 // Create job
222 xInferenceJob *job = &jobs[n];
223 job->name = string(modelName);
224 job->networkModel = DataPtr(networkModelData, sizeof(networkModelData));
225 job->input.push_back(DataPtr(inputData, sizeof(inputData)));
226 job->expectedOutput.push_back(DataPtr(expectedOutputData, sizeof(expectedOutputData)));
227 job->responseQueue = senderQueue;
228 // Send job
Lior Dekel3e128622021-09-23 18:57:12 +0300229 printf("inferenceSenderTask: Sending inference job: job=%p, name=%s\n", job, job->name.c_str());
Kristofer Jonsson29467e02021-11-26 16:10:43 +0100230 if (xQueueSend(_inferenceProcessQueue, &job, portMAX_DELAY) != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300231 printf("Error: inferenceSenderTask failed in send to Q.\n");
232 exit(1);
233 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100234 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100235
Anton Moberg6a7703e2021-03-02 15:14:29 +0100236 // Listen for completion status
237 do {
238 xInferenceJob *pSendJob;
Lior Dekel3e128622021-09-23 18:57:12 +0300239 if (xQueueReceive(senderQueue, &pSendJob, portMAX_DELAY) != pdPASS) {
240 printf("Error: inferenceSenderTask failed in receive from Q.\n");
241 exit(1);
242 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100243 printf("inferenceSenderTask: received response for job: %s, status = %u\n",
244 pSendJob->name.c_str(),
245 pSendJob->status);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100246
Anton Moberg6a7703e2021-03-02 15:14:29 +0100247 totalCompletedJobs++;
248 ret = (pSendJob->status);
249 if (pSendJob->status != 0) {
250 break;
251 }
252 } while (totalCompletedJobs < NUM_JOBS_PER_TASK * NUM_JOB_TASKS);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100253
Anton Moberg6a7703e2021-03-02 15:14:29 +0100254 vQueueDelete(senderQueue);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100255
Anton Moberg6a7703e2021-03-02 15:14:29 +0100256 printf("FreeRTOS application returning %d.\n", ret);
257 exit(ret);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100258}
259
Anton Moberg6a7703e2021-03-02 15:14:29 +0100260/****************************************************************************
261 * Application
262 ****************************************************************************/
Anton Moberg6a7703e2021-03-02 15:14:29 +0100263// FreeRTOS application. NOTE: Additional tasks may require increased heap size.
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100264int main() {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100265 BaseType_t ret;
266 inferenceProcessQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100267
Anton Moberg6a7703e2021-03-02 15:14:29 +0100268 // inferenceSender tasks to create and queue the jobs
269 for (int n = 0; n < NUM_JOB_TASKS; n++) {
270 ret = xTaskCreate(inferenceSenderTask, "inferenceSenderTask", 2 * 1024, inferenceProcessQueue, 2, nullptr);
271 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300272 printf("Error: Failed to create 'inferenceSenderTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100273 exit(1);
274 }
275 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100276
Anton Moberg6a7703e2021-03-02 15:14:29 +0100277 // Create inferenceProcess tasks to process the queued jobs
278 for (int n = 0; n < NUM_INFERENCE_TASKS; n++) {
279 taskParams[n] = ProcessTaskParams(inferenceProcessQueue, inferenceProcessTensorArena[n], arenaSize);
Kristofer Jonsson99f19422021-07-01 22:15:02 +0200280 ret = xTaskCreate(inferenceProcessTask, "inferenceProcessTask", 8 * 1024, &taskParams[n], 3, nullptr);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100281 if (ret != pdPASS) {
Lior Dekel3e128622021-09-23 18:57:12 +0300282 printf("Error: Failed to create 'inferenceProcessTask%i'\n", n);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100283 exit(1);
284 }
285 }
286
287 // Start Scheduler
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100288 vTaskStartScheduler();
289
Lior Dekel3e128622021-09-23 18:57:12 +0300290 printf("Error: FreeRTOS application failed to initialise.\n");
Anton Moberg6a7703e2021-03-02 15:14:29 +0100291 exit(1);
292
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100293 return 0;
Lior Dekeledaf2f82021-08-10 17:06:16 +0300294}