blob: 71a312850ce00ee18900bc4ed46a1d57e5bf4d59 [file] [log] [blame]
Kristofer Jonsson641c0912020-08-31 11:34:14 +02001/*
Måns Nilsson45a8a132022-09-26 08:42:10 +02002 * SPDX-FileCopyrightText: Copyright 2019-2022 Arm Limited and/or its affiliates <open-source-office@arm.com>
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_interpreter.h"
Jonny Svärd2ebaac72022-05-10 17:29:30 +020022#include "tensorflow/lite/micro/micro_time.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020023#include "tensorflow/lite/schema/schema_generated.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020024
Jens Elofsson955288a2021-04-22 20:57:15 +020025#include "arm_profiler.hpp"
Per Åstrandd9afc082020-10-06 13:25:08 +020026#include "cmsis_compiler.h"
Kristofer Jonsson1fed1d52022-11-21 13:39:45 +010027#include "crc.hpp"
28#include "ethosu_log.h"
29#include "inference_process.hpp"
Per Åstrandd9afc082020-10-06 13:25:08 +020030
Per Åstrand91a91732020-09-25 15:04:26 +020031#include <inttypes.h>
32
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020033using namespace std;
34
Fredrik Svedberg39a03242022-06-30 10:57:57 +020035namespace {
36const char *BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
37
38void printBase64(const uint8_t *data, size_t len) {
39 size_t count = len / 3;
40 size_t remainder = len % 3;
41 char buf[] = "====";
42 while (count--) {
43 buf[0] = BASE64[data[0] >> 2];
44 buf[1] = BASE64[(data[0] & 3) << 4 | data[1] >> 4];
45 buf[2] = BASE64[(data[1] & 0xf) << 2 | data[2] >> 6];
46 buf[3] = BASE64[data[2] & 0x3f];
47 LOG("%s", buf);
48 data += 3;
49 }
50 if (remainder) {
51 uint8_t b2 = remainder > 1 ? data[1] : 0;
52 buf[0] = BASE64[data[0] >> 2];
53 buf[1] = BASE64[(data[0] & 3) << 4 | b2 >> 4];
54 buf[2] = remainder > 1 ? BASE64[(b2 & 0xf) << 2] : '=';
55 buf[3] = '=';
56 LOG("%s", buf);
57 }
58}
59
60} // namespace
61
Kristofer Jonsson641c0912020-08-31 11:34:14 +020062namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020063DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020064
Kristofer Jonsson34e24962020-11-23 16:22:10 +010065void DataPtr::invalidate() {
66#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010067 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
68#endif
69}
70
71void DataPtr::clean() {
72#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010073 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
74#endif
75}
76
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +010077char *DataPtr::begin() const {
78 return static_cast<char *>(data);
79}
80
81char *DataPtr::end() const {
82 return static_cast<char *>(data) + size;
83}
84
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010085InferenceJob::InferenceJob() : numBytesToPrint(0), externalContext(nullptr) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020086
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020087InferenceJob::InferenceJob(const string &_name,
88 const DataPtr &_networkModel,
89 const vector<DataPtr> &_input,
90 const vector<DataPtr> &_output,
91 const vector<DataPtr> &_expectedOutput,
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010092 const size_t _numBytesToPrint,
93 void *_externalContext) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020094 name(_name),
95 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010096 numBytesToPrint(_numBytesToPrint), externalContext(_externalContext) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020097
Kristofer Jonsson34e24962020-11-23 16:22:10 +010098void InferenceJob::invalidate() {
99 networkModel.invalidate();
100
101 for (auto &it : input) {
102 it.invalidate();
103 }
104
105 for (auto &it : output) {
106 it.invalidate();
107 }
108
109 for (auto &it : expectedOutput) {
110 it.invalidate();
111 }
112}
113
114void InferenceJob::clean() {
115 networkModel.clean();
116
117 for (auto &it : input) {
118 it.clean();
119 }
120
121 for (auto &it : output) {
122 it.clean();
123 }
124
125 for (auto &it : expectedOutput) {
126 it.clean();
127 }
128}
129
Kristofer Jonsson40d886e2021-12-15 11:16:26 +0100130InferenceProcess::InferenceProcess(uint8_t *_tensorArena, size_t _tensorArenaSize) :
131 tensorArena(_tensorArena), tensorArenaSize(_tensorArenaSize) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200132
133bool InferenceProcess::runJob(InferenceJob &job) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100134 LOG_INFO("Running inference job: %s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200135
Bhavik Patelffe845d2020-11-16 12:13:56 +0100136 // Register debug log callback for profiling
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100137 RegisterDebugLogCallback(tfluDebugLog);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200138
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200139 // Get model handle and verify that the version is correct
Davide Grohmann30b17b92022-06-14 15:17:18 +0200140 const tflite::Model *model = parser.getModel(job.networkModel.data, job.networkModel.size);
141 if (model == nullptr) {
142 LOG_ERR("Invalid model");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200143 return true;
144 }
145
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200146 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200147 tflite::AllOpsResolver resolver;
Jens Elofsson955288a2021-04-22 20:57:15 +0200148 tflite::ArmProfiler profiler;
Måns Nilssoneee7fc62022-10-20 09:33:40 +0200149 tflite::MicroInterpreter interpreter(model, resolver, tensorArena, tensorArenaSize, nullptr, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200150
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100151 // Set external context
Davide Grohmann165f00a2022-02-09 14:53:58 +0100152 if (job.externalContext != nullptr) {
153 interpreter.SetMicroExternalContext(job.externalContext);
154 }
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100155
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200156 // Allocate tensors
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100157 TfLiteStatus status = interpreter.AllocateTensors();
158 if (status != kTfLiteOk) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100159 LOG_ERR("Failed to allocate tensors for inference: job=%s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200160 return true;
161 }
162
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100163 // Copy IFM data from job descriptor to TFLu arena
164 if (copyIfm(job, interpreter)) {
165 return true;
166 }
167
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200168 // Get the current cycle counter value
169 uint32_t cpuCyclesBegin = tflite::GetCurrentTimeTicks();
170
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100171 // Run the inference
172 status = interpreter.Invoke();
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200173
174 // Calculate nbr of CPU cycles for the Invoke call
175 job.cpuCycles = tflite::GetCurrentTimeTicks() - cpuCyclesBegin;
176
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100177 if (status != kTfLiteOk) {
178 LOG_ERR("Invoke failed for inference: job=%s", job.name.c_str());
179 return true;
180 }
181
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100182 // Copy output data from TFLu arena to job descriptor
183 if (copyOfm(job, interpreter)) {
184 return true;
185 }
186
187 printJob(job, interpreter);
188
189 // Compare the OFM with the expected reference data
190 if (compareOfm(job, interpreter)) {
191 return true;
192 }
193
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200194 LOG_INFO("\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100195 LOG_INFO("Finished running job: %s", job.name.c_str());
196
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200197 profiler.ReportResults();
198
199 LOG("\n");
200 LOG("Operator(s) total: %" PRIu64 " CPU cycles\n\n", profiler.GetTotalTicks());
201
202 LOG("Inference runtime: %" PRIu64 " CPU cycles total\n\n", job.cpuCycles);
203
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100204 return false;
205}
206
207bool InferenceProcess::copyIfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200208 // Create a filtered list of non empty input tensors
209 vector<TfLiteTensor *> inputTensors;
210 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
211 TfLiteTensor *tensor = interpreter.input(i);
212
213 if (tensor->bytes > 0) {
214 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200215 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200216 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100217
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200218 if (job.input.size() != inputTensors.size()) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100219 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 +0200220 job.input.size(),
221 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200222 return true;
223 }
224
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100225 // Copy input data from job to TFLu arena
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200226 for (size_t i = 0; i < inputTensors.size(); ++i) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100227 DataPtr &input = job.input[i];
228 TfLiteTensor *tensor = inputTensors[i];
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200229
230 if (input.size != tensor->bytes) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100231 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 +0200232 job.name.c_str(),
233 i,
234 input.size,
235 tensor->bytes);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200236 return true;
237 }
238
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100239 copy(input.begin(), input.end(), tensor->data.uint8);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200240 }
241
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100242 return false;
243}
244
245bool InferenceProcess::copyOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
246 // Skip copy if output is empty
247 if (job.output.empty()) {
248 return false;
249 }
250
251 if (interpreter.outputs_size() != job.output.size()) {
252 LOG_ERR("Output size mismatch: job=%zu, network=%u", job.output.size(), interpreter.outputs_size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200253 return true;
254 }
255
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100256 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
257 DataPtr &output = job.output[i];
258 TfLiteTensor *tensor = interpreter.output(i);
Bhavik Patelffe845d2020-11-16 12:13:56 +0100259
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100260 if (tensor->bytes > output.size) {
261 LOG_ERR("Tensor size mismatch: tensor=%d, expected=%d", tensor->bytes, output.size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200262 return true;
263 }
264
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100265 copy(tensor->data.uint8, tensor->data.uint8 + tensor->bytes, output.begin());
266 }
267
268 return false;
269}
270
271bool InferenceProcess::compareOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
272 // Skip verification if expected output is empty
273 if (job.expectedOutput.empty()) {
274 return false;
275 }
276
277 if (job.expectedOutput.size() != interpreter.outputs_size()) {
278 LOG_ERR("Expected number of output tensors mismatch: job=%s, expected=%zu, network=%zu",
279 job.name.c_str(),
280 job.expectedOutput.size(),
281 interpreter.outputs_size());
282 return true;
283 }
284
285 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
286 const DataPtr &expected = job.expectedOutput[i];
287 const TfLiteTensor *output = interpreter.output(i);
288
289 if (expected.size != output->bytes) {
290 LOG_ERR("Expected output tensor size mismatch: job=%s, index=%u, expected=%zu, network=%zu",
291 job.name.c_str(),
292 i,
293 expected.size,
294 output->bytes);
295 return true;
296 }
297
298 const char *exp = expected.begin();
299 for (unsigned int j = 0; j < output->bytes; ++j) {
300 if (output->data.uint8[j] != exp[j]) {
301 LOG_ERR("Expected output tensor data mismatch: job=%s, index=%u, offset=%u, "
302 "expected=%02x, network=%02x\n",
303 job.name.c_str(),
304 i,
305 j,
306 exp[j],
307 output->data.uint8[j]);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200308 return true;
309 }
310 }
311 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200312
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100313 return false;
314}
315
316void InferenceProcess::printJob(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100317 LOG("arena_used_bytes : %zu\n", interpreter.arena_used_bytes());
318
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100319 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
320 // whichever comes first as well as the output shape.
321 LOG("num_of_outputs: %d\n", interpreter.outputs_size());
322 LOG("output_begin\n");
323 LOG("[\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100324
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100325 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100326 printOutputTensor(interpreter.output(i), job.numBytesToPrint);
327
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100328 if (i != interpreter.outputs_size() - 1) {
329 LOG(",\n");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200330 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200331 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100332
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100333 LOG("]\n");
334 LOG("output_end\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100335}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200336
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100337void InferenceProcess::printOutputTensor(TfLiteTensor *output, size_t bytesToPrint) {
338 constexpr auto crc = Crc();
339 const uint32_t crc32 = crc.crc32(output->data.data, output->bytes);
340 const int numBytesToPrint = min(output->bytes, bytesToPrint);
341 int dims_size = output->dims->size;
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200342
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100343 LOG("{\n");
344 LOG("\"dims\": [%d,", dims_size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200345
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100346 for (int i = 0; i < output->dims->size - 1; ++i) {
347 LOG("%d,", output->dims->data[i]);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200348 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200349
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100350 LOG("%d],\n", output->dims->data[dims_size - 1]);
351 LOG("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Henrik Hoglind59096ef2022-03-18 08:58:11 +0100352 LOG("\"data_bytes\": %d,\n", output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200353
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100354 if (numBytesToPrint) {
355 LOG("\"crc32\": \"%08" PRIx32 "\",\n", crc32);
356 LOG("\"data\":\"");
Fredrik Svedberg39a03242022-06-30 10:57:57 +0200357 printBase64(output->data.uint8, numBytesToPrint);
358 LOG("\"\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100359 } else {
360 LOG("\"crc32\": \"%08" PRIx32 "\"\n", crc32);
361 }
362
363 LOG("}");
364}
365
366void InferenceProcess::tfluDebugLog(const char *s) {
367 LOG("%s", s);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200368}
369
370} // namespace InferenceProcess