PNNA 驱动接口说明
PNNA 驱动程序基于 C 语言编写,支持 ARM 和 DSP 两种硬件平台。驱动对模型加载、硬件绑定、内存管理及推理流程进行了统一抽象,支持 Linux、RT-Thread 及裸机环境。
示例工程结构
examples 目录包含多平台、多模型的完整部署示例:
1examples
2├── yolov5s # ARM Linux 平台部署示例
3│ ├── postprocess # yolov5s 后处理实现
4│ ├── resource # 模型与测试资源
5│ └── yolov5s_demo.c # 完整推理流程示例
6├── resnet18 # ARM Linux 平台部署示例
7│ ├── postprocess
8│ ├── resource
9│ └── resnet18_demo.c
10├── yolov5s_metal_c6x # DSP 裸机(Metal)平台示例
11│ ├── postprocess
12│ ├── resource
13│ └── yolov5s_demo.c
14└── README.md # 示例说明文档
环境要求
操作系统:Linux / RT-Thread / 裸机
目标硬件:ARM Cortex-A / Cortex-M / DSP C6x
编译工具:对应平台的交叉编译工具链
驱动调用方式
PNNA 驱动为应用部署提供了两种方式支持。
方式一
pnna_open();
nn_ctx_t *ctx = create_model(nbg_buffer);
# get_input
buffer_t input = load_image("test.jpg");
# preprocess
buffer_t quant_input = custom_quantize(input, ctx->input_quant);
infer(ctx, &output, &quant_input);
# postprocess
custom_postprocess(&output);
destroy_model(ctx);
pnna_close();
流程图
方式二
方式二对前处理,推理和后处理进行了封装。
pnna_open();
app_ctx_t *model = create_app_ctx(nbg_buffer, preprocess, postprocess);
# get_input
buffer_t input = load_image(image_filename);
app(model, (void **)&output, &input);
free_image_buffer(&input);
destroy_app_ctx(model);
pnna_close();
流程图
接口说明
pnna_open / pnna_close
int pnna_open();
int pnna_close();
'pnna_open': 打开 PNNA 核,并完成硬件初始化
'pnna_close': 关闭 PNNA 核
create_model / destroy_model
nn_ctx_t *create_model(buffer_t *nbg_buffer, int options);
void destroy_model(nn_ctx_t *ctx);
'create_model()': 解析 NBG 文件生成模型上下文(
nn_ctx_t)'destroy_model()': 释放模型上下文资源 。
infer
int infer(nn_ctx_t *ctx, buffer_t *output, buffer_t *input);
调用 PNNA 核执行推理
create_app_ctx
app_ctx_t *create_app_ctx(buffer_t *nbg_buffer,
preprocess_cb model_preprocess,
postprocess_cb model_postprocess);
创建应用推理上下文
内部调用
create_model()加载模型根据模型输入量化信息分配预处理缓存资源
根据输出张量数量创建分配输出缓存资源
将预处理和后处理回调绑定到上下文
destroy_app_ctx
void destroy_app_ctx(app_ctx_t *model);
释放应用推理上下文资源
app
int app(app_ctx_t *model, void **output, buffer_t *input);
按序执行 [preprocess], infer 和 [postprocess]
配置后处理函数
output指向用户自定义结构体输出内存由调用者分配
detection *output = malloc(sizeof(detection) * 10);
app(model, (void **)&output, &input);
必须传入
output的地址(void **),而不是直接传结构体数组output参数仅用于返回结果指针,app()不会在内部申请结构化输出内存
错误示例:
detection output[10]; // 不推荐
app(model, (void **)output, input);
上述写法会导致指针层级不匹配,可能引发未定义行为。
未配置后处理函数
output类型为buffer_t *输出 buffer 由框架内部管理
buffer_t *output = NULL;
app(model, (void **)&output, &input);
初始将
output置为NULL即可app()内部会将网络输出缓冲区指针赋值给output调用者只负责读取该缓冲区,不负责释放(释放规则以模型生命周期为准)
多输入模型支持
int input_count = model->ctx->input_count;
buffer_t *inputs = malloc(sizeof(buffer_t) * input_count);
for (int i = 0; i < input_count; ++i) {
inputs[i] = load_image(image_paths[i]);
}
app(model, output, inputs);
输入 buffer 生命周期由调用者管理
app()不负责释放输入数据
资源管理对照表
资源 |
创建接口 |
释放接口 |
|---|---|---|
硬件设备 |
pnna_open |
pnna_close |
推理应用上下文 |
create_app_ctx |
destroy_app_ctx |
模型上下文 |
create_model |
destroy_model |
作者 @xyy xunyingya@hngwg.com