NPU模型的API部署流程*
GX8010中,CPU和MCU各有一个NPU,CPU中的为主NPU,MCU中的为SNPU,NPU比SNPU的性能更强,功耗也更大。 CPU可以控制NPU或SNPU,MCU只能控制SNPU。 由于CPU和MCU的特点不同,在其上面使用NPU的API也不同。
1.CPU中使用NPU或SNPU*
生成能在CPU上运行的模型文件,需要在编译模型的配置文件中指定: OUTPUT_TYPE: raw
在CPU上内存资源相对丰富,模型指令以文件方式加载,能提高灵活性。
NPU处理器的输入输出数据类型都是float16,float16和float32的转换由API内部完成,上层应用不需关心。
1.1 调用API流程*
- 打开NPU设备。
- 传入模型文件,得到模型task.
- 获取task的输入输出信息。
- 拷贝输入数据到模型内存中。
- 运行模型,得到输出数据。
- 释放模型task.
- 关闭NPU设备。
gxdnn.h
/* GXDNN
* Copyright (C) 1991-2017 NationalChip Co., Ltd
*
* gxdnn.h NPU Task loader and executor
*
*/
#ifndef __GXDNN_H__
#define __GXDNN_H__
#ifdef __cplusplus
extern "C" {
#endif
/*===============================================================================================*/
typedef void* GxDnnDevice;
typedef void* GxDnnTask;
typedef enum {
GXDNN_RESULT_SUCCESS = 0,
GXDNN_RESULT_WRONG_PARAMETER,
GXDNN_RESULT_MEMORY_NOT_ENOUGH,
GXDNN_RESULT_DEVICE_ERROR,
GXDNN_RESULT_FILE_NOT_FOUND,
GXDNN_RESULT_UNSUPPORT,
GXDNN_RESULT_UNKNOWN_ERROR,
GXDNN_RESULT_OVERTIME,
} GxDnnResult;
/*===============================================================================================*/
/**
* @brief Open NPU device
* @param [in] devicePath the path to device
* [out] device a handle to the openned device
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* GXDNN_RESULT_DEVICE_ERROR device error
* @remark if devicePath is "/dev/gxnpu", open npu device
* devicePath is "/dev/gxsnpu", open snpu device
*/
GxDnnResult GxDnnOpenDevice(const char *devicePath,
GxDnnDevice *device);
/*===============================================================================================*/
/**
* @brief Close NPU device
* @param [in] device the handle to openned device
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* GXDNN_RESULT_DEVICE_ERROR device error
* @remark
*/
GxDnnResult GxDnnCloseDevice(GxDnnDevice device);
/*===============================================================================================*/
/**
* @brief Load NPU Task from file (in Linux/MacOSX)
* @param [in] device the device handle
* [in] taskPath the path to NPU task file
* [out] task a handle to the loaded task
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* GXDNN_RESULT_MEMORY_NOT_ENOUGH no enough memory
* GXDNN_RESULT_DEVICE_ERROR device error
* GXDNN_RESULT_FILE_NOT_FOUND file not found
* GXDNN_RESULT_UNSUPPORT unsupport NPU type or npu task version
* @remark
*/
GxDnnResult GxDnnCreateTaskFromFile(GxDnnDevice device,
const char *taskPath,
GxDnnTask *task);
/*===============================================================================================*/
/**
* @brief Load NPU task from memory
* @param [in] device the device handle
* [in] taskBuffer the pointer to NPU task buffer
* [in] bufferSize the buffer size of the NPU task buffer
* [out] task a handle to the loaded task
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* GXDNN_RESULT_MEMORY_NOT_ENOUGH no enough memory
* GXDNN_RESULT_DEVICE_ERROR device error
* GXDNN_RESULT_UNSUPPORT unsupport NPU type or npu task version
* @remark
*/
GxDnnResult GxDnnCreateTaskFromBuffer(GxDnnDevice device,
const unsigned char *taskBuffer,
const int bufferSize,
GxDnnTask *task);
/*===============================================================================================*/
/**
* @brief Release NPU task
* @param [in] task the loaded task handle
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* @remark
*/
GxDnnResult GxDnnReleaseTask(GxDnnTask task);
/*===============================================================================================*/
#define MAX_NAME_SIZE 30
#define MAX_SHAPE_SIZE 10
typedef struct NpuIOInfo {
int direction; /* 0: Input; 1: Output */
char name[MAX_NAME_SIZE]; /* name of the IO */
int shape[MAX_SHAPE_SIZE]; /* the shape of the IO */
unsigned int dimension; /* the dimension of the IO */
void *dataBuffer; /* the data buffer */
int bufferSize; /* the data buffer size */
} GxDnnIOInfo;
/**
* @brief Get the IO Num of the loaded task
* @param [in] task the loaded task
* [out] inputNum Input number
* [out] outputNum Output Number
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* @remark
*/
GxDnnResult GxDnnGetTaskIONum(GxDnnTask task,
int *inputNum,
int *outputNum);
/*===============================================================================================*/
/**
* @brief Get the IO Info of the loaded task
* @param [in] task the loaded task
* [out] inputInfo the input information List
* [in] inputInfoSize the size of the output info list buffer
* [out] outputInfo the output information list
* [in] outputInfoSize the size of the output info list buffer
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_ERR_BAD_PARAMETER wrong parameter
* @remark
*/
GxDnnResult GxDnnGetTaskIOInfo(GxDnnTask task,
GxDnnIOInfo *inputInfo,
int inputInfoSize,
GxDnnIOInfo *outputInfo,
int outputInfoSize);
/*===============================================================================================*/
typedef enum {
GXDNN_EVENT_FINISH,
GXDNN_EVENT_ABORT,
GXDNN_EVENT_FAILURE
} GxDnnEvent;
/**
* @brief The event handler
* @param [in] task the running task
* [in] event the event type
* [in] userData the userData passed by GxDnnRunTask
* @return int 0 break the task
* not 0 continue the task
*/
typedef int (*GxDnnEventHandler)(GxDnnTask task, GxDnnEvent event, void *userData);
/*===============================================================================================*/
/**
* @brief Run task
* @param [in] task the loaded task
* [in] priority the task priority
* [in] eventHandler the event callback (see remark)
* [in] userData a void data will be passed to event handler
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* @remark if eventHandler == NULL, the function will not return until finish or error happens;
* if the task is running, the task will stop first;
*/
GxDnnResult GxDnnRunTask(GxDnnTask task,
int priority,
GxDnnEventHandler eventHandler,
void *userData);
/*===============================================================================================*/
/**
* @brief Stop task
* @param [in] task the loaded task
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* @remark if the task is running, the event handler will be invoked
*/
GxDnnResult GxDnnStopTask(GxDnnTask task);
/*===============================================================================================*/
typedef struct NpuDevUtilInfo {
float ratio;
} GxDnnDevUtilInfo;
/**
* @brief Get device utilization information
* @param [in] GxDnnDevice device
* [out] GxDnnDevUtilInfo info
* @return GxDnnResult GXDNN_RESULT_SUCCESS succeed without error
* GXDNN_RESULT_WRONG_PARAMETER wrong parameter
* GXDNN_RESULT_DEVICE_ERROR device error
*/
GxDnnResult GxDnnGetDeviceUtil(GxDnnDevice device, GxDnnDevUtilInfo *info);
/*===============================================================================================*/
#ifdef __cplusplus
} /* extern C */
#endif
#endif
2.MCU中使用SNPU*
生成能在MCU上运行的模型文件,需要在编译模型的配置文件中指定: OUTPUT_TYPE: c_code
在MCU上内存资源紧缺,生成的模型文件为C文件,能直接参与编译。这样的优点是不需要解析模型文件,缺点是换模型就得重新编译。
另外,由于MCU效率较低,float16和float32的转换不能在MCU上做,必须由DSP或ARM来转。
2.1 调用API流程*
- 打开SNPU
- 拷贝输入数据到模型内存中。
- 运行模型,得到输出数据。
- 关闭SNPU
snpu.h
/* Voice Signal Preprocess
* Copyright (C) 1991-2017 Nationalchip Co., Ltd
* All Rights Reserved!
*
* snpu.h: Device Driver for SNPU
*
*/
#ifndef __SNPU_H__
#define __SNPU_H__
int SnpuInit(void);
int SnpuLoadFirmware(void);
int SnpuDone(void);
#ifdef CONFIG_GX8010NRE
int SnpuFloat32To16(unsigned int *in_data, unsigned short *out_data, int num, int exponent_width);
int SnpuFloat16To32(unsigned short *in_data, unsigned int *out_data, int num, int exponent_width);
#endif
typedef enum {
SNPU_IDLE,
SNPU_BUSY,
SNPU_STALL,
} SNPU_STATE;
typedef int (*SNPU_CALLBACK)(SNPU_STATE state, void *private_data);
typedef struct {
const char *version; // version in model.c
void *ops; // ops_content in model.c
void *data; // cpu_content in model.c
void *input; // input in model.c
void *output; // output in model.c
void *cmd; // npu_content in model.c
void *tmp_mem; // tmp_content in model.c
} SNPU_TASK;
int SnpuRunTask(SNPU_TASK *task, SNPU_CALLBACK callback, void *private_data);
SNPU_STATE SnpuGetState(void);
#endif // __SNPU_H__