跳转至

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流程*

  1. 打开NPU设备。
  2. 传入模型文件,得到模型task.
  3. 获取task的输入输出信息。
  4. 拷贝输入数据到模型内存中。
  5. 运行模型,得到输出数据。
  6. 释放模型task.
  7. 关闭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流程*

  1. 打开SNPU
  2. 拷贝输入数据到模型内存中。
  3. 运行模型,得到输出数据。
  4. 关闭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__