How to run locally a c-model
for STM32 target, based on ST Edge AI Core Technology 2.2.0
r2.0
Overview
This article explains how to run locally the generated c-model in a C-based environment, to:
- enhance an end-user validation process with a large data set including the specific pre and post processing functions with the ST.AI inference run-time.
- integrate a ST Edge AI Core validation step in a CI/CD flow w/o STM32 board
Tip
Python-based environment is covered by the “How to use the AiRunner package” article.
%STEDGEAI_CORE_DIR%
indicates the location where the ST Edge AI pack is installed.
C-based environment
This environment is based on the same X86 AI runtime libraries which are used to perform the built-in validation flow. So, the main prerequisite is to have a compatible C/C++ tool chain to be able to link the final application with them. Note that in Windows® development environment, a MingGW-W64 tool chain is available in X-CUBE-AI pack, including the GNU Make utility.
Following source tree is used to illustrate this how-to.
cubeai_lib_XX
directory contains the ST.AI host/x86 C
library files to build the generated c-model files in
model
directory.
<root_project>
|- makefile
|_ main.c /* entry point, */
|_ *.c/*.h /* extra/user test framework files */
|
|_ model
| |_ network.c/.h /* generated c-model files */
| |_ network_data.c/.h
| |_ network_config.h
|
\_ cubeai_lib_XX|_ include /* X-CUBE-AI header files */
|_ lib /* Libraries */
Retrieve the X86/host C library files
By OS, the expected X86 library files are located in:
%STEDGEAI_CORE_DIR%/Utilities/%OS%/targets/common/EmbedNets/tools/inspector/workspace/
All content of the include
folder can be directly
copied in <root_project>/cube_ai_lib_XX/inlude
.
Idem for the content of lib
in
<root_project>/cube_ai_lib_XX/lib
. Note the the
share libraries (*.dll
or .so
) are not
necessary.
Build the test application
AI_LIB_DIR = $(root_project)/cubeai_lib_XX
...CFLAGS += $(root_project)/model
CFLAGS += -std=c99
CFLAGS += -I$(AI_LIB_DIR)/include
LIBS = -L$(AI_LIB_DIR)/lib/static -lruntime -lst_cmsis_nn -lcmsis_nn -lx86_cmsis -lm
Update the test application source tree with a new c-model
The generate
command can be used with the --output/-o
option to
indicate the location of the generated files.
-m <my_model> --target stm32 <my_options> -o %root_project%/model/ $ stedgeai generate
Test code
There is no difference between the [embedded inference
API][X_CUBE_AI_API] used for a STM32 application and for the X86
test application. Setup/init sequence and the calls of the
ai_<network>_XXX()
functions are the same.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "network.h"
#include "network_data.h"
/**
* @brief Statically allocated buffers.
* Buffers can be dynamically allocated using malloc and size information
* given by the report in ai_network_get_report().
*/
[AI_NETWORK_DATA_ACTIVATIONS_SIZE];
ai_u8 activations[AI_NETWORK_IN_1_SIZE_BYTES];
ai_u8 in_data[AI_NETWORK_OUT_1_SIZE_BYTES];
ai_u8 out_data
/* AI buffer IO handlers */
*ai_input;
ai_buffer *ai_output;
ai_buffer
int main(int argc, char const *argv[])
{
= AI_HANDLE_NULL;
ai_handle network ;
ai_error err;
ai_network_report report
/** @brief Initialize network */
const ai_handle acts[] = { activations };
= ai_network_create_and_init(&network, acts, NULL);
err if (err.type != AI_ERROR_NONE) {
("ai init_and_create error\n");
printfreturn -1;
}
/** @brief {optional} for debug/log purpose */
if (ai_network_get_report(network, &report) != true) {
("ai get report error\n");
printfreturn -1;
}
("Model name : %s\n", report.model_name);
printf("Model signature : %s\n", report.model_signature);
printf
= &report.inputs[0];
ai_input = &report.outputs[0];
ai_output ("input[0] : (%d, %d, %d)\n", AI_BUFFER_SHAPE_ELEM(ai_input, AI_SHAPE_HEIGHT),
printf(ai_input, AI_SHAPE_WIDTH),
AI_BUFFER_SHAPE_ELEM(ai_input, AI_SHAPE_CHANNEL));
AI_BUFFER_SHAPE_ELEM("output[0] : (%d, %d, %d)\n", AI_BUFFER_SHAPE_ELEM(ai_output, AI_SHAPE_HEIGHT),
printf(ai_output, AI_SHAPE_WIDTH),
AI_BUFFER_SHAPE_ELEM(ai_output, AI_SHAPE_CHANNEL));
AI_BUFFER_SHAPE_ELEM
/** @brief Fill input buffer with random values */
(1);
srandfor (int i = 0; i < AI_NETWORK_IN_1_SIZE; i++) {
[i] = rand() % 0xFFFF;
in_data}
/** @brief Normalize, convert and/or quantize inputs if necessary... */
/** @brief Perform inference */
;
ai_i32 n_batch
/** @brief Create the AI buffer IO handlers
* @note ai_inuput/ai_output are already initilaized after the
* ai_network_get_report() call. This is just here to illustrate
* the case where get_report() is not called.
*/
= ai_network_inputs_get(network, NULL);
ai_input = ai_network_outputs_get(network, NULL);
ai_output
/** @brief Set input/output buffer addresses */
[0].data = AI_HANDLE_PTR(in_data);
ai_input[0].data = AI_HANDLE_PTR(out_data);
ai_output
/** @brief Perform the inference */
= ai_network_run(network, &ai_input[0], &ai_output[0]);
n_batch if (n_batch != 1) {
= ai_network_get_error(network);
err ("ai run error %d, %d\n", err.type, err.code);
printfreturn -1;
}
/** @brief Post-process the output results/predictions */
("Inference output..\n");
printffor (int i = 0; i < AI_NETWORK_OUT_1_SIZE; i++) {
("%d,", out_data[i]);
printf}
("\n");
printfreturn 0;
}
C++ environment
The generated 'network.c'
and
'network_data.c'
files should be compiled with the C
compiler. network_runtime
static libraries are fully
compiled with a C-compiler. For the C++ application code, the AI
header files should be included as follow:
#include <iostream>
using namespace std;
extern "C" {
#include "network.h"
#include "network_data.h"
}