blob: 0010b70500e87ddc9e98f6bd4b846075b0378e1d [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"
24#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
49#define NUM_JOB_TASKS 1
50// 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/****************************************************************************
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010061 * InferenceJob
62 ****************************************************************************/
63
Anton Moberg6a7703e2021-03-02 15:14:29 +010064struct ProcessTaskParams {
65 ProcessTaskParams() {}
66 ProcessTaskParams(QueueHandle_t _queue, uint8_t *_tensorArena, size_t _arenaSize) :
67 queueHandle(_queue), tensorArena(_tensorArena), arenaSize(_arenaSize) {}
Anton Moberg1a679c42021-02-10 08:45:39 +010068
Anton Moberg6a7703e2021-03-02 15:14:29 +010069 QueueHandle_t queueHandle;
70 uint8_t *tensorArena;
71 size_t arenaSize;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010072};
73
Anton Moberg6a7703e2021-03-02 15:14:29 +010074// Number of total completed jobs, needed to exit application correctly if NUM_JOB_TASKS > 1
75static int totalCompletedJobs = 0;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010076
Anton Moberg6a7703e2021-03-02 15:14:29 +010077// TensorArena static initialisation
78static const size_t arenaSize = TENSOR_ARENA_SIZE_PER_INFERENCE;
79__attribute__((section(".bss.tensor_arena"), aligned(16)))
80uint8_t inferenceProcessTensorArena[NUM_INFERENCE_TASKS][arenaSize];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +010081
Anton Moberg6a7703e2021-03-02 15:14:29 +010082// Wrapper around InferenceProcess::InferenceJob. Adds responseQueue and status for FreeRTOS multi-tasking purposes.
83struct xInferenceJob : public InferenceJob {
84 QueueHandle_t responseQueue;
85 bool status;
86
87 xInferenceJob() : InferenceJob(), responseQueue(nullptr), status(false) {}
88 xInferenceJob(const string &_name,
89 const DataPtr &_networkModel,
90 const vector<DataPtr> &_input,
91 const vector<DataPtr> &_output,
92 const vector<DataPtr> &_expectedOutput,
93 const size_t _numBytesToPrint,
94 const vector<uint8_t> &_pmuEventConfig,
95 const uint32_t _pmuCycleCounterEnable,
96 QueueHandle_t _queue) :
97 InferenceJob(_name,
98 _networkModel,
99 _input,
100 _output,
101 _expectedOutput,
102 _numBytesToPrint,
103 _pmuEventConfig,
104 _pmuCycleCounterEnable),
105 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) {
116 return xSemaphoreCreateMutex();
117}
118
119void ethosu_mutex_lock(void *mutex) {
120 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
121 xSemaphoreTake(handle, portMAX_DELAY);
122}
123
124void ethosu_mutex_unlock(void *mutex) {
125 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(mutex);
126 xSemaphoreGive(handle);
127}
128
129void *ethosu_semaphore_create(void) {
130 return xSemaphoreCreateBinary();
131}
132
133void ethosu_semaphore_take(void *sem) {
134 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
135 xSemaphoreTake(handle, portMAX_DELAY);
136}
137
138void ethosu_semaphore_give(void *sem) {
139 SemaphoreHandle_t handle = reinterpret_cast<SemaphoreHandle_t>(sem);
140 xSemaphoreGive(handle);
141}
142}
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100143
144/****************************************************************************
145 * Functions
146 ****************************************************************************/
147
Anton Moberg6a7703e2021-03-02 15:14:29 +0100148// inferenceProcessTask - Run jobs from queue with available driver
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100149void inferenceProcessTask(void *pvParameters) {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100150 ProcessTaskParams params = *reinterpret_cast<ProcessTaskParams *>(pvParameters);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100151
Anton Moberg6a7703e2021-03-02 15:14:29 +0100152 class InferenceProcess inferenceProcess(params.tensorArena, params.arenaSize);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100153
Anton Moberg6a7703e2021-03-02 15:14:29 +0100154 for (;;) {
155 xInferenceJob *xJob;
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100156
Anton Moberg6a7703e2021-03-02 15:14:29 +0100157 xQueueReceive(params.queueHandle, &xJob, portMAX_DELAY);
158 bool status = inferenceProcess.runJob(*xJob);
159 xJob->status = status;
160 xQueueSend(xJob->responseQueue, &xJob, portMAX_DELAY);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100161 }
Anton Moberg6a7703e2021-03-02 15:14:29 +0100162 vTaskDelete(nullptr);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100163}
164
Anton Moberg6a7703e2021-03-02 15:14:29 +0100165// inferenceSenderTask - Creates NUM_INFERNECE_JOBS jobs, queues them, and then listens for completion status
166void inferenceSenderTask(void *pvParameters) {
167 int ret = 0;
168
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100169 QueueHandle_t inferenceProcessQueue = reinterpret_cast<QueueHandle_t>(pvParameters);
Anton Moberg6a7703e2021-03-02 15:14:29 +0100170 xInferenceJob jobs[NUM_JOBS_PER_TASK];
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100171
172 // Create queue for response messages
Anton Moberg6a7703e2021-03-02 15:14:29 +0100173 QueueHandle_t senderQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100174
Anton Moberg6a7703e2021-03-02 15:14:29 +0100175 // Create and queue the jobs
176 for (int n = 0; n < NUM_JOBS_PER_TASK; n++) {
177 // Create job
178 xInferenceJob *job = &jobs[n];
179 job->name = string(modelName);
180 job->networkModel = DataPtr(networkModelData, sizeof(networkModelData));
181 job->input.push_back(DataPtr(inputData, sizeof(inputData)));
182 job->expectedOutput.push_back(DataPtr(expectedOutputData, sizeof(expectedOutputData)));
183 job->responseQueue = senderQueue;
184 // Send job
185 printf("Sending inference job: job=%p, name=%s\n", job, job->name.c_str());
186 xQueueSend(inferenceProcessQueue, &job, portMAX_DELAY);
187 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100188
Anton Moberg6a7703e2021-03-02 15:14:29 +0100189 // Listen for completion status
190 do {
191 xInferenceJob *pSendJob;
192 xQueueReceive(senderQueue, &pSendJob, portMAX_DELAY);
193 printf("inferenceSenderTask: received response for job: %s, status = %u\n",
194 pSendJob->name.c_str(),
195 pSendJob->status);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100196
Anton Moberg6a7703e2021-03-02 15:14:29 +0100197 totalCompletedJobs++;
198 ret = (pSendJob->status);
199 if (pSendJob->status != 0) {
200 break;
201 }
202 } while (totalCompletedJobs < NUM_JOBS_PER_TASK * NUM_JOB_TASKS);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100203
Anton Moberg6a7703e2021-03-02 15:14:29 +0100204 vQueueDelete(senderQueue);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100205
Anton Moberg6a7703e2021-03-02 15:14:29 +0100206 printf("FreeRTOS application returning %d.\n", ret);
207 exit(ret);
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100208}
209
Anton Moberg6a7703e2021-03-02 15:14:29 +0100210/****************************************************************************
211 * Application
212 ****************************************************************************/
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100213
Anton Moberg6a7703e2021-03-02 15:14:29 +0100214// Declare variables in global scope to avoid stack since FreeRTOS resets stack when the scheduler is started
215static QueueHandle_t inferenceProcessQueue;
216static ProcessTaskParams taskParams[NUM_INFERENCE_TASKS];
Per Åstrand4d612752021-02-22 09:50:54 +0100217
Anton Moberg6a7703e2021-03-02 15:14:29 +0100218// FreeRTOS application. NOTE: Additional tasks may require increased heap size.
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100219int main() {
Anton Moberg6a7703e2021-03-02 15:14:29 +0100220 BaseType_t ret;
221 inferenceProcessQueue = xQueueCreate(NUM_JOBS_PER_TASK, sizeof(xInferenceJob *));
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100222
Anton Moberg6a7703e2021-03-02 15:14:29 +0100223 // inferenceSender tasks to create and queue the jobs
224 for (int n = 0; n < NUM_JOB_TASKS; n++) {
225 ret = xTaskCreate(inferenceSenderTask, "inferenceSenderTask", 2 * 1024, inferenceProcessQueue, 2, nullptr);
226 if (ret != pdPASS) {
227 printf("FreeRTOS: Failed to create 'inferenceSenderTask%i'\n", n);
228 exit(1);
229 }
230 }
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100231
Anton Moberg6a7703e2021-03-02 15:14:29 +0100232 // Create inferenceProcess tasks to process the queued jobs
233 for (int n = 0; n < NUM_INFERENCE_TASKS; n++) {
234 taskParams[n] = ProcessTaskParams(inferenceProcessQueue, inferenceProcessTensorArena[n], arenaSize);
235 ret = xTaskCreate(inferenceProcessTask, "inferenceProcessTask", 3 * 1024, &taskParams[n], 3, nullptr);
236 if (ret != pdPASS) {
237 printf("FreeRTOS: Failed to create 'inferenceProcessTask%i'\n", n);
238 exit(1);
239 }
240 }
241
242 // Start Scheduler
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100243 vTaskStartScheduler();
244
Anton Moberg6a7703e2021-03-02 15:14:29 +0100245 printf("FreeRTOS application failed to initialise \n");
246 exit(1);
247
Kristofer Jonsson43ce4912020-11-20 09:42:53 +0100248 return 0;
Anton Moberg6a7703e2021-03-02 15:14:29 +0100249}