1 /*
2 * Copyright 2019-2022 Arm Limited and/or its affiliates <open-source-office@arm.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "inference_process.hpp"
8
9 #include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
10 #include <tensorflow/lite/micro/cortex_m_generic/debug_log_callback.h>
11 #include <tensorflow/lite/micro/micro_log.h>
12 #include <tensorflow/lite/micro/micro_interpreter.h>
13 #include <tensorflow/lite/micro/micro_profiler.h>
14 #include <tensorflow/lite/schema/schema_generated.h>
15
16 #include <cmsis_compiler.h>
17 #include <inttypes.h>
18 #include <zephyr/kernel.h>
19
20 using namespace std;
21
22 namespace
23 {
copyOutput(const TfLiteTensor & src,InferenceProcess::DataPtr & dst)24 bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst)
25 {
26 if (dst.data == nullptr) {
27 return false;
28 }
29
30 if (src.bytes > dst.size) {
31 printk("Tensor size mismatch (bytes): actual=%d, expected%d.\n", src.bytes,
32 dst.size);
33 return true;
34 }
35
36 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
37 dst.size = src.bytes;
38
39 return false;
40 }
41
42 } /* namespace */
43
44 namespace InferenceProcess
45 {
DataPtr(void * _data,size_t _size)46 DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size)
47 {
48 }
49
invalidate()50 void DataPtr::invalidate()
51 {
52 #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
53 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
54 #endif
55 }
56
clean()57 void DataPtr::clean()
58 {
59 #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
60 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
61 #endif
62 }
63
InferenceJob()64 InferenceJob::InferenceJob()
65 {
66 }
67
InferenceJob(const string & _name,const DataPtr & _networkModel,const vector<DataPtr> & _input,const vector<DataPtr> & _output,const vector<DataPtr> & _expectedOutput)68 InferenceJob::InferenceJob(const string &_name, const DataPtr &_networkModel,
69 const vector<DataPtr> &_input, const vector<DataPtr> &_output,
70 const vector<DataPtr> &_expectedOutput)
71 : name(_name), networkModel(_networkModel), input(_input), output(_output),
72 expectedOutput(_expectedOutput)
73 {
74 }
75
invalidate()76 void InferenceJob::invalidate()
77 {
78 networkModel.invalidate();
79
80 for (auto &it : input) {
81 it.invalidate();
82 }
83
84 for (auto &it : output) {
85 it.invalidate();
86 }
87
88 for (auto &it : expectedOutput) {
89 it.invalidate();
90 }
91 }
92
clean()93 void InferenceJob::clean()
94 {
95 networkModel.clean();
96
97 for (auto &it : input) {
98 it.clean();
99 }
100
101 for (auto &it : output) {
102 it.clean();
103 }
104
105 for (auto &it : expectedOutput) {
106 it.clean();
107 }
108 }
109
runJob(InferenceJob & job)110 bool InferenceProcess::runJob(InferenceJob &job)
111 {
112 /* Get model handle and verify that the version is correct */
113 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
114 if (model->version() != TFLITE_SCHEMA_VERSION) {
115 printk("Model schema version unsupported: version=%" PRIu32 ", supported=%d.\n",
116 model->version(), TFLITE_SCHEMA_VERSION);
117 return true;
118 }
119
120 /* Create the TFL micro interpreter */
121 tflite::MicroMutableOpResolver <1> resolver;
122 resolver.AddEthosU();
123
124 tflite::MicroInterpreter interpreter(model, resolver, tensorArena, tensorArenaSize);
125
126 /* Allocate tensors */
127 TfLiteStatus allocate_status = interpreter.AllocateTensors();
128 if (allocate_status != kTfLiteOk) {
129 printk("Failed to allocate tensors for inference. job=%p\n", &job);
130 return true;
131 }
132
133 if (job.input.size() != interpreter.inputs_size()) {
134 printk("Number of job and network inputs do not match. input=%zu, network=%zu\n",
135 job.input.size(), interpreter.inputs_size());
136 return true;
137 }
138
139 /* Copy input data */
140 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
141 const DataPtr &input = job.input[i];
142 const TfLiteTensor *tensor = interpreter.input(i);
143
144 if (input.size != tensor->bytes) {
145 printk("Input tensor size mismatch. index=%zu, input=%zu, network=%u\n", i,
146 input.size, tensor->bytes);
147 return true;
148 }
149
150 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size,
151 tensor->data.uint8);
152 }
153
154 /* Run the inference */
155 TfLiteStatus invoke_status = interpreter.Invoke();
156 if (invoke_status != kTfLiteOk) {
157 printk("Invoke failed for inference. job=%s\n", job.name.c_str());
158 return true;
159 }
160
161 /* Copy output data */
162 if (job.output.size() > 0) {
163 if (interpreter.outputs_size() != job.output.size()) {
164 printk("Number of job and network outputs do not match. job=%zu, network=%u\n",
165 job.output.size(), interpreter.outputs_size());
166 return true;
167 }
168
169 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
170 if (copyOutput(*interpreter.output(i), job.output[i])) {
171 return true;
172 }
173 }
174 }
175
176 if (job.expectedOutput.size() > 0) {
177 if (job.expectedOutput.size() != interpreter.outputs_size()) {
178 printk("Number of job and network expected outputs do not match. job=%zu, network=%zu\n",
179 job.expectedOutput.size(), interpreter.outputs_size());
180 return true;
181 }
182
183 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
184 const DataPtr &expected = job.expectedOutput[i];
185 const TfLiteTensor *output = interpreter.output(i);
186
187 if (expected.size != output->bytes) {
188 printk("Expected output tensor size mismatch. index=%u, expected=%zu, network=%zu\n",
189 i, expected.size, output->bytes);
190 return true;
191 }
192
193 for (unsigned int j = 0; j < output->bytes; ++j) {
194 if (output->data.uint8[j] !=
195 static_cast<uint8_t *>(expected.data)[j]) {
196 printk("Expected output tensor data mismatch. index=%u, offset=%u, expected=%02x, network=%02x\n",
197 i, j, static_cast<uint8_t *>(expected.data)[j],
198 output->data.uint8[j]);
199 return true;
200 }
201 }
202 }
203 }
204
205 return false;
206 }
207
208 } /* namespace InferenceProcess */
209