blob: 29254c740565287d14d56bc5eb601a2825f2cb83 [file] [log] [blame]
Kristofer Jonsson641c0912020-08-31 11:34:14 +02001/*
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +01002 * Copyright (c) 2019-2022 Arm Limited. All rights reserved.
Kristofer Jonsson641c0912020-08-31 11:34:14 +02003 *
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#include "tensorflow/lite/micro/all_ops_resolver.h"
Måns Nilsson231e1d92020-11-05 12:19:34 +010020#include "tensorflow/lite/micro/cortex_m_generic/debug_log_callback.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020021#include "tensorflow/lite/micro/micro_error_reporter.h"
22#include "tensorflow/lite/micro/micro_interpreter.h"
Bhavik Patelffe845d2020-11-16 12:13:56 +010023#include "tensorflow/lite/micro/micro_profiler.h"
Jonny Svärd2ebaac72022-05-10 17:29:30 +020024#include "tensorflow/lite/micro/micro_time.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020025#include "tensorflow/lite/schema/schema_generated.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020026
Jens Elofsson955288a2021-04-22 20:57:15 +020027#include "arm_profiler.hpp"
Kristofer Jonsson3bd34232021-08-30 13:55:55 +020028#ifdef LAYER_BY_LAYER_PROFILER
Jens Elofsson701a63b2021-05-23 17:37:07 +020029#include "layer_by_layer_profiler.hpp"
Jens Elofsson955288a2021-04-22 20:57:15 +020030#endif
Jonny Svärd5adf5a62022-02-09 16:42:10 +010031
32#include "crc.hpp"
33
Anton Moberg07cf70b2021-07-07 11:08:17 +020034#include "ethosu_log.h"
Jens Elofsson955288a2021-04-22 20:57:15 +020035
Kristofer Jonsson641c0912020-08-31 11:34:14 +020036#include "inference_process.hpp"
37
Per Åstrandd9afc082020-10-06 13:25:08 +020038#include "cmsis_compiler.h"
39
Per Åstrand91a91732020-09-25 15:04:26 +020040#include <inttypes.h>
41
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020042using namespace std;
43
Kristofer Jonsson641c0912020-08-31 11:34:14 +020044namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020045DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020046
Kristofer Jonsson34e24962020-11-23 16:22:10 +010047void DataPtr::invalidate() {
48#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010049 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
50#endif
51}
52
53void DataPtr::clean() {
54#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010055 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
56#endif
57}
58
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +010059char *DataPtr::begin() const {
60 return static_cast<char *>(data);
61}
62
63char *DataPtr::end() const {
64 return static_cast<char *>(data) + size;
65}
66
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010067InferenceJob::InferenceJob() : numBytesToPrint(0), externalContext(nullptr) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020068
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020069InferenceJob::InferenceJob(const string &_name,
70 const DataPtr &_networkModel,
71 const vector<DataPtr> &_input,
72 const vector<DataPtr> &_output,
73 const vector<DataPtr> &_expectedOutput,
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010074 const size_t _numBytesToPrint,
75 void *_externalContext) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020076 name(_name),
77 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010078 numBytesToPrint(_numBytesToPrint), externalContext(_externalContext) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020079
Kristofer Jonsson34e24962020-11-23 16:22:10 +010080void InferenceJob::invalidate() {
81 networkModel.invalidate();
82
83 for (auto &it : input) {
84 it.invalidate();
85 }
86
87 for (auto &it : output) {
88 it.invalidate();
89 }
90
91 for (auto &it : expectedOutput) {
92 it.invalidate();
93 }
94}
95
96void InferenceJob::clean() {
97 networkModel.clean();
98
99 for (auto &it : input) {
100 it.clean();
101 }
102
103 for (auto &it : output) {
104 it.clean();
105 }
106
107 for (auto &it : expectedOutput) {
108 it.clean();
109 }
110}
111
Kristofer Jonsson40d886e2021-12-15 11:16:26 +0100112InferenceProcess::InferenceProcess(uint8_t *_tensorArena, size_t _tensorArenaSize) :
113 tensorArena(_tensorArena), tensorArenaSize(_tensorArenaSize) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200114
115bool InferenceProcess::runJob(InferenceJob &job) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100116 LOG_INFO("Running inference job: %s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200117
Bhavik Patelffe845d2020-11-16 12:13:56 +0100118 // Register debug log callback for profiling
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100119 RegisterDebugLogCallback(tfluDebugLog);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200120
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200121 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200122 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
123 if (model->version() != TFLITE_SCHEMA_VERSION) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100124 LOG_ERR("Model schema version unsupported: version=%" PRIu32 ", supported=%d.",
Anton Moberg07cf70b2021-07-07 11:08:17 +0200125 model->version(),
126 TFLITE_SCHEMA_VERSION);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200127 return true;
128 }
129
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200130 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200131 tflite::AllOpsResolver resolver;
Jens Elofsson955288a2021-04-22 20:57:15 +0200132 tflite::ArmProfiler profiler;
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100133 tflite::MicroErrorReporter errorReporter;
134 tflite::MicroInterpreter interpreter(
135 model, resolver, tensorArena, tensorArenaSize, &errorReporter, nullptr, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200136
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100137 // Set external context
Davide Grohmann165f00a2022-02-09 14:53:58 +0100138 if (job.externalContext != nullptr) {
139 interpreter.SetMicroExternalContext(job.externalContext);
140 }
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100141
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200142 // Allocate tensors
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100143 TfLiteStatus status = interpreter.AllocateTensors();
144 if (status != kTfLiteOk) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100145 LOG_ERR("Failed to allocate tensors for inference: job=%s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200146 return true;
147 }
148
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100149 // Copy IFM data from job descriptor to TFLu arena
150 if (copyIfm(job, interpreter)) {
151 return true;
152 }
153
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200154 // Get the current cycle counter value
155 uint32_t cpuCyclesBegin = tflite::GetCurrentTimeTicks();
156
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100157 // Run the inference
158 status = interpreter.Invoke();
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200159
160 // Calculate nbr of CPU cycles for the Invoke call
161 job.cpuCycles = tflite::GetCurrentTimeTicks() - cpuCyclesBegin;
162
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100163 if (status != kTfLiteOk) {
164 LOG_ERR("Invoke failed for inference: job=%s", job.name.c_str());
165 return true;
166 }
167
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100168 // Copy output data from TFLu arena to job descriptor
169 if (copyOfm(job, interpreter)) {
170 return true;
171 }
172
173 printJob(job, interpreter);
174
175 // Compare the OFM with the expected reference data
176 if (compareOfm(job, interpreter)) {
177 return true;
178 }
179
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200180 LOG_INFO("\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100181 LOG_INFO("Finished running job: %s", job.name.c_str());
182
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200183 profiler.ReportResults();
184
185 LOG("\n");
186 LOG("Operator(s) total: %" PRIu64 " CPU cycles\n\n", profiler.GetTotalTicks());
187
188 LOG("Inference runtime: %" PRIu64 " CPU cycles total\n\n", job.cpuCycles);
189
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100190 return false;
191}
192
193bool InferenceProcess::copyIfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200194 // Create a filtered list of non empty input tensors
195 vector<TfLiteTensor *> inputTensors;
196 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
197 TfLiteTensor *tensor = interpreter.input(i);
198
199 if (tensor->bytes > 0) {
200 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200201 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200202 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100203
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200204 if (job.input.size() != inputTensors.size()) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100205 LOG_ERR("Number of input buffers does not match number of non empty network tensors: input=%zu, network=%zu",
Anton Moberg07cf70b2021-07-07 11:08:17 +0200206 job.input.size(),
207 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200208 return true;
209 }
210
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100211 // Copy input data from job to TFLu arena
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200212 for (size_t i = 0; i < inputTensors.size(); ++i) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100213 DataPtr &input = job.input[i];
214 TfLiteTensor *tensor = inputTensors[i];
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200215
216 if (input.size != tensor->bytes) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100217 LOG_ERR("Job input size does not match network input size: job=%s, index=%zu, input=%zu, network=%u",
Anton Moberg07cf70b2021-07-07 11:08:17 +0200218 job.name.c_str(),
219 i,
220 input.size,
221 tensor->bytes);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200222 return true;
223 }
224
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100225 copy(input.begin(), input.end(), tensor->data.uint8);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200226 }
227
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100228 return false;
229}
230
231bool InferenceProcess::copyOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
232 // Skip copy if output is empty
233 if (job.output.empty()) {
234 return false;
235 }
236
237 if (interpreter.outputs_size() != job.output.size()) {
238 LOG_ERR("Output size mismatch: job=%zu, network=%u", job.output.size(), interpreter.outputs_size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200239 return true;
240 }
241
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100242 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
243 DataPtr &output = job.output[i];
244 TfLiteTensor *tensor = interpreter.output(i);
Bhavik Patelffe845d2020-11-16 12:13:56 +0100245
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100246 if (tensor->bytes > output.size) {
247 LOG_ERR("Tensor size mismatch: tensor=%d, expected=%d", tensor->bytes, output.size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200248 return true;
249 }
250
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100251 copy(tensor->data.uint8, tensor->data.uint8 + tensor->bytes, output.begin());
252 }
253
254 return false;
255}
256
257bool InferenceProcess::compareOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
258 // Skip verification if expected output is empty
259 if (job.expectedOutput.empty()) {
260 return false;
261 }
262
263 if (job.expectedOutput.size() != interpreter.outputs_size()) {
264 LOG_ERR("Expected number of output tensors mismatch: job=%s, expected=%zu, network=%zu",
265 job.name.c_str(),
266 job.expectedOutput.size(),
267 interpreter.outputs_size());
268 return true;
269 }
270
271 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
272 const DataPtr &expected = job.expectedOutput[i];
273 const TfLiteTensor *output = interpreter.output(i);
274
275 if (expected.size != output->bytes) {
276 LOG_ERR("Expected output tensor size mismatch: job=%s, index=%u, expected=%zu, network=%zu",
277 job.name.c_str(),
278 i,
279 expected.size,
280 output->bytes);
281 return true;
282 }
283
284 const char *exp = expected.begin();
285 for (unsigned int j = 0; j < output->bytes; ++j) {
286 if (output->data.uint8[j] != exp[j]) {
287 LOG_ERR("Expected output tensor data mismatch: job=%s, index=%u, offset=%u, "
288 "expected=%02x, network=%02x\n",
289 job.name.c_str(),
290 i,
291 j,
292 exp[j],
293 output->data.uint8[j]);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200294 return true;
295 }
296 }
297 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200298
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100299 return false;
300}
301
302void InferenceProcess::printJob(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100303 LOG("arena_used_bytes : %zu\n", interpreter.arena_used_bytes());
304
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100305 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
306 // whichever comes first as well as the output shape.
307 LOG("num_of_outputs: %d\n", interpreter.outputs_size());
308 LOG("output_begin\n");
309 LOG("[\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100310
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100311 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100312 printOutputTensor(interpreter.output(i), job.numBytesToPrint);
313
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100314 if (i != interpreter.outputs_size() - 1) {
315 LOG(",\n");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200316 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200317 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100318
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100319 LOG("]\n");
320 LOG("output_end\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100321}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200322
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100323void InferenceProcess::printOutputTensor(TfLiteTensor *output, size_t bytesToPrint) {
324 constexpr auto crc = Crc();
325 const uint32_t crc32 = crc.crc32(output->data.data, output->bytes);
326 const int numBytesToPrint = min(output->bytes, bytesToPrint);
327 int dims_size = output->dims->size;
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200328
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100329 LOG("{\n");
330 LOG("\"dims\": [%d,", dims_size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200331
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100332 for (int i = 0; i < output->dims->size - 1; ++i) {
333 LOG("%d,", output->dims->data[i]);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200334 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200335
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100336 LOG("%d],\n", output->dims->data[dims_size - 1]);
337 LOG("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Henrik Hoglind59096ef2022-03-18 08:58:11 +0100338 LOG("\"data_bytes\": %d,\n", output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200339
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100340 if (numBytesToPrint) {
341 LOG("\"crc32\": \"%08" PRIx32 "\",\n", crc32);
342 LOG("\"data\":\"");
343
344 for (int i = 0; i < numBytesToPrint - 1; ++i) {
345 /*
346 * Workaround an issue when compiling with GCC where by
347 * printing only a '\n' the produced global output is wrong.
348 */
349 if (i % 15 == 0 && i != 0) {
350 LOG("0x%02x,\n", output->data.uint8[i]);
351 } else {
352 LOG("0x%02x,", output->data.uint8[i]);
353 }
354 }
355
356 LOG("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
357 } else {
358 LOG("\"crc32\": \"%08" PRIx32 "\"\n", crc32);
359 }
360
361 LOG("}");
362}
363
364void InferenceProcess::tfluDebugLog(const char *s) {
365 LOG("%s", s);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200366}
367
368} // namespace InferenceProcess