MI DSP API


REVISION HISTORY

Revision No.
Description
Date
3.0
  • Initial release
  • 03/18/2021
  • Added procfs introduction
  • 08/25/2021
    3.1
  • Modify the MI_DSP_FW_PACKING_TYPE_e enumeration variable
  • 12/02/2021
    3.2
  • Add Asynchronous Invocation APIs
  • 12/15/2021

    1. 概述

    1.1. 模块说明

    MI DSP 是SStar平台中内嵌的DSP core,用于加速智能视觉开发,其中MI提供了API方便用户快速开发和深度定制,提高产品的竞争力。

    图1‑1 MI DSP开发框架图

    如上图1-1所示,MI DSP interface主要提供user mode application的API接口。从User mode的Application开始,通过调用MI DSP interface,到kernel mode的MI impl层,再到kernel 的driver, 最后通过share memory 跟DSP交互。

    1.2. 关键字说明

    ID: Identity document,表示唯一编码。

    MI: SStar SDK Middle Interface。

    Hex: 十六进制。

    Kernel Mode: 工作在Kernel环境下的代码,代码具有直接操作硬件的权限,比如ko中的函数和线程等。

    User Mode: 工作在User环境下的代码,比如用户的应用程序,系统调用等。

    XRP: Remote Processing,基于Linux操作系统的通信接口,用于与DSP 交互。

    APP: Application,主要指调用MI API的应用程序。

    API: Application Programming Interface,指Sstar SDK提供给应用程序调用的接口。

    IMPL: Implement,MI 在kernel层的实现。

    SHM: Share memory,共享内存。

    IPC: IP Camera,网络摄像机。

    HW: Hardware,硬件。

    2. API 参考

    提供给user application的MI DSP接口如下。

    API名 功能
    系统功能类
    MI_DSP_CreateDev 创建DSP device
    MI_DSP_DestroyDev 销毁DSP device
    MI_DSP_Invoke 执行一次DSP 定制的算法,并得到输出
    MI_DSP_InvokeAsync 提交一次异步DSP运算任务。
    MI_DSP_WaitDone 等待一次异步DSP(MI_DSP_InvokeAsync)计算任务完成。

    2.1. MI_DSP_CreateDev

    • 功能

      创建DSP 设备。

    • 语法

      MI_S32 MI_DSP_CreateDev(MI_DSP_DEV_e eDspDev,MI_DSP_DevAttr_t *pstDspDevAttr,
      CustReadFunc pReadFunc, char *pCtx, MI_U32 fwSize);
      
    • 形参

      参数名称 描述 输入/输出
      eDspDev Device ID, 对应DSP core ID 输入
      pstDspDevAttr Device 设备属性 输入
      pReadFunc 用户自定义读取文件的函数指针 输入
      pCtx Firmware 文件路径 输入
      fwSize Firmware 文件大小 输入
    • 返回值

      • 0:成功。

      • 非0:失败,参照错误码

    • 依赖

      • 头文件:mi_dsp.h

      • 库文件:libmi_dsp.a / libmi_dsp.so

    • 注意

      • 当用户使用的自定义读取文件函数pReadFunc,fwSize需与pCtx路径所指定的实际文件大小相同;

      • 当fwSize为0时,则pReadFunc需设置为NULL;若用户使用自定义读取文件函数pReadFun,此为错误用法,请遵循上面的注意事项。

    • 举例

      MI_S32 s32Ret;
      MI_DSP_DevAttr_t stDevAttr;
      
      stDevAttr. eFwPackingType = E_MI_DSP_FW_PACKING_PLAIN;
      char *pReadCtx ="/mnt/dsp_firmware";
      
      s32Ret = MI_DSP_CreateDev(MI_DSP_DEV_0, &stDevAttr, NULL, pReadCtx, 0);
      
      if(s32Ret != MI_SUCCESS){
          printf("Fail to create DSP device \n");
          return s32Ret;
      }
      

    2.2. MI_DSP_DestroyDev

    • 功能

      销毁DSP 设备。

    • 语法

      MI_S32 MI_DSP_DestroyDev(MI_DSP_DEV_e eDspDev);
      
    • 形参

      参数名称 描述 输入/输出
      eDspDev Device ID, 对应DSP core ID 输入
    • 返回值

      • 0:成功。

      • 非0:失败,参照错误码

    • 依赖

      • 头文件:mi_dsp.h

      • 库文件:libmi_dsp.a / libmi_dsp.so

    • 注意

      • u32Dev必须是之前创建的DSP device。

    2.3. MI_DSP_Invoke

    • 功能

      执行一次DSP的算法。

    • 语法

      MI_S32 MI_DSP_Invoke(MI_DSP_DEV_e eDspDev, MI_DSP_BufVector_t *pstInputBuf, 
      MI_DSP_BufVector_t *pstOutputBuf,  MI_DSP_PARAM_t *pstParam, 
      MI_DSP_PRI_e ePri, MI_U16 u16TimeOutMs);
      
    • 形参

      参数名称 描述 输入/输出
      eDspDev Device ID, 对应DSP core ID 输入
      pstInputBuf 输入DSP的数组结构体指针 输入
      pstOutputBuf DSP输出的数组结构体指针 输入
      pstParam 输入DSP的数组结构体指针 输入/输出
      ePri 此次Invoke的优先级 输入
      u16TimeOutMs 此次Invoke的超时时间 输入
    • 返回值

      • 0:成功。

      • 非0:失败,参照错误码

    • 依赖

      • 头文件:mi_dsp.h

      • 库文件:libmi_dsp.a / libmi_dsp.so

    • 注意

      • MI_DSP_Invoke是阻塞式API,执行一次得到一次结果。

      • pstParam->s32Result返回该次invoke DSP端的处理结果指示。

      • 和MI_DSP_Invoke 不同,pstParam->s32Result不会被赋值,需要用MI_DSP_WaitDone 函数取得结果。

    • 举例

      MI_DSP_BufVector_t stInputBuf, stOutputBuf;
      MI_DSP_PARAM_t stParam;
      MI_S32 retVal = 0;
      
      memset(&stInputBuf, 0, sizeof(MI_DSP_BufVector_t));
      memset(&stOutputBuf, 0, sizeof(MI_DSP_BufVector_t));
      
      /* 1、通过MMA分配InputBuf的空间,并对其进行映射 */
      stInputBuf.astBufInfoVec[0].u32Size = 0x1000;
      MI_SYS_MMA_Alloc(0, NULL, stInputBuf.astBufInfoVec[0].u32Size, &stInputBuf.astBufInfoVec[0].phyAddr);
      MI_SYS_Mmap(stInputBuf.astBufInfoVec[0].phyAddr, stInputBuf.astBufInfoVec[0].u32Size, &virtAddrIn, FALSE);
      stInputBuf.u32BufCount = 1;
      stInputBuf.astBufInfoVec[0].u32Flag = 0x1;
      memset(virtAddrIn, 0, stInputBuf.astBufInfoVec[0].u32Size);
      
      /* 2、通过MMA分配OutputBuf的空间,并对其进行映射 */
      stOutputBuf.astBufInfoVec[0].u32Size = 0x1000;
      MI_SYS_MMA_Alloc(0,NULL,stOutputBuf.astBufInfoVec[0].u32Size,&stOutputBuf.astBufInfoVec[0].phyAddr);
      MI_SYS_Mmap(stOutputBuf.astBufInfoVec[0].phyAddr, stOutputBuf.astBufInfoVec[0].u32Size, &virtAddrOut, FALSE);
      stOutputBuf.u32BufCount = 1;
      stOutputBuf.astBufInfoVec[0].u32Flag = 0x2;
      stParam.u64Data[0] = 0x2222;
      memset(virtAddrOut, 0, stOutputBuf.astBufInfoVec[0].u32Size);
      
      /* 3、对InputBuf进行赋值 */
      for(int i = 0; i < stInputBuf.astBufInfoVec[0].u32Size; i++) {
          char* p = (char*)virtAddrIn;
          p[i] = 0xc2;
      }
      
      /* 4、调用一次dsp core0 的算法 */
      retVal = MI_DSP_Invoke(0, &stInputBuf, &stOutputBuf, &stParam, 0, 65535);
      if (retVal != MI_SUCCESS)
      {
          printf("core 0 invoke err, retVal: 0x%x!\n", retVal);
          goto invoke_err;
      }
      
      /* 5、打印处理结果 */
      for(int i = 0; i < 10; i++) {
          char* p = (char*)virtAddrOut;
          printf("%d \n", p[i]);
      }
      

    2.4. MI_DSP_InvokeAsync

    • 功能

      提交一次异步DSP运算任务。

    • 语法

      MI_S32 MI_DSP_InvokeAsync(MI_DSP_DEV_e eDspDev, MI_DSP_BufVector_t *pstInputBuf, 
      MI_DSP_BufVector_t *pstOutputBuf,  MI_DSP_PARAM_t *pstParam, MI_DSP_PRI_e ePri, MI_U32 *u32fence);
      
    • 形参

      参数名称 描述 输入/输出
      eDspDev Device ID, 对应DSP core ID 输入
      pstInputBuf 输入DSP的数组结构体指针 输入
      pstOutputBuf DSP输出的数组结构体指针 输入
      pstParam 输入DSP的数组结构体指针 输入/输出
      ePri 此次Invoke的优先级 输入
      u32fence 此次Invoke的任务标识序号 输出
    • 返回值

      • 0:成功。

      • 非0:失败,参照错误码

    • 依赖

      • 头文件:mi_dsp.h

      • 库文件:libmi_dsp.a / libmi_dsp.so

    • 注意

      • MI_DSP_InvokeAsync是非阻塞式API,执行一次立即返回,如果返回MI_DSP_ERR_PRIQ_FULL则意味着对应DSP设备处理比较繁忙,任务队列已经满了。需要配合MI_DSP_WaitDone函数等待任务队列有空位,再提交计算任务。

      • 参数u32fence是函数调用返回的标志此次计算任务的ID,使用MI_DSP_WaitDone函数等待这次计算任务完成。

    • 举例

      InputBuf和OutputBuf使用MI_SYS模块已经分配的buffer

      MI_U32 lastfence = 0; int tmp = 5;
      while (tmp)
      {
          for (int i = 0; i < stInputBuf.astBufInfoVec[0].u32Size; i++)
          {
              char* p = (char*)virtAddrIn;
              p[i]    = tmp;
          }
      
          MI_U32 fence = 0;
          while(MI_SUCCESS != MI_DSP_InvokeAsync(0, &stInputBuf, &stOutputBuf, &stParam, 0, &fence))
          {
              printf("dev busy %d \n",tmp);
              //usleep(19);
              if(MI_DSP_WaitDone(0,0, lastfence, NULL, 20000)!=MI_SUCCESS)
              {
                  printf("dev busy %d timeout\n",tmp);
              }
          }
          lastfence = fence;
          //MI_DSP_Invoke(0, &stInputBuf, &stOutputBuf, &stParam, 0, 100);
          // if(tmp == 3)
          //     MI_DSP_ResetDev(0);
          for (int i = 0; i < 10; i++)
          {
              char* p = (char*)virtAddrOut;
              printf("%d \n", p[i]);
          }
          printf("fence:%d\n", fence);
          tmp--;
      }
      

    2.5. MI_DSP_WaitDone

    • 功能

      等待一次异步DSP(MI_DSP_InvokeAsync)计算任务完成。

    • 语法

      MI_S32 MI_DSP_WaitDone(MI_DSP_DEV_e eDspDev, MI_DSP_PRI_e ePri, 
      MI_U32 u32fence, MI_S32* s32Result, MI_U32 u32TimeOutMs);
      
    • 形参

      参数名称 描述 输入/输出
      eDspDev Device ID, 对应DSP core ID 输入
      ePri 对应Async Invoke的优先级 输入
      u32fence 对应Async Invoke 的任务ID 输入
      s32Result 返回最近完成任务的结果 输出
      u32TimeOutMs 等待Async Invoke 任务完成的超时时间 输入
    • 返回值

      • 0:成功。

      • 非0:失败,参照错误码

    • 依赖

      • 头文件:mi_dsp.h

      • 库文件:libmi_dsp.a / libmi_dsp.so

    • 注意

      • 本函数是以DSP设备为单位的阻塞式调用

      • 函数返回值为:MI_SUCCESS或者MI_DSP_ERR_TIMEOUT

      • 本函数接受对应设备优先级[eDspDev][ePri]的u32Fence参数ID,对ID的先后顺序并无要求。如果先等待后面invoke 对应的任务ID 完成,就意味着前面的任务也已经完成,此时调用函数等待之前的任务ID将会立即返回成功。

      • s32Result的值是DSP最新完成的invoke任务的值,不一定是对应u32fence 任务的结果。考虑到u32fence可能是非常老旧的任务ID了。

    • 举例

      InputBuf和OutputBuf使用MI_SYS模块已经分配的buffer

      MI_U32 lastfence = 0; int tmp = 5;
      while (tmp)
      {
          for (int i = 0; i < stInputBuf.astBufInfoVec[0].u32Size; i++)
          {
              char* p = (char*)virtAddrIn;
              p[i]    = tmp;
          }
      
          MI_U32 fence = 0;
          while(MI_SUCCESS != MI_DSP_InvokeAsync(0, &stInputBuf, &stOutputBuf, &stParam, 0, &fence))
          {
              printf("dev busy %d \n",tmp);
              //usleep(19);
              if(MI_DSP_WaitDone(0,0, lastfence, NULL, 20000)!=MI_SUCCESS)
              {
                  printf("dev busy %d timeout\n",tmp);
              }
          }
          lastfence = fence;
          //MI_DSP_Invoke(0, &stInputBuf, &stOutputBuf, &stParam, 0, 100);
          // if(tmp == 3)
          //     MI_DSP_ResetDev(0);
          for (int i = 0; i < 10; i++)
          {
              char* p = (char*)virtAddrOut;
              printf("%d \n", p[i]);
          }
          printf("fence:%d\n", fence);
          tmp--;
      }
      

    3. 数据类型

    相关数据类型、数据结构定义如下。

    数据类型 定义
    MI_DSP_DEV_e 定义设备Device的类型
    MI_DSP_DevAttr_t 定义设备的硬件属性
    CustReadFunc 用于客制化的读取文件方式,譬如加密等
    MI_DSP_BufVector_t 定义多个输入/输出Buffer的结构体
    MI_DSP_BufInfo_t 定义码流信息的Buffer结构体
    MI_DSP_PARAM_t 定义客制化的用户参数,立即数或地址/长度
    MI_DSP_PRI_e Invoke API的优先级
    MI_DSP_FW_PACKING_TYPE_e 定义DSP firmware的打包方式

    3.1. MI_DSP_DEV_e

    • 说明

      定义设备Device的类型。

    • 定义

      typedef enum
      {
          MI_DSP_DEV_0 = 0,  
          MI_DSP_DEV_1,        
          MI_DSP_DEV_2,
          MI_DSP_DEV_3,
          MI_DSP_DEV_BUTT
      } MI_DSP_DEV_e;
      
    • 成员

      成员名称 描述
      MI_DSP_DEV_0 DSP device设备Core 0
      MI_DSP_DEV_1 DSP device设备Core 1
      MI_DSP_DEV_2 DSP device设备Core 2
      MI_DSP_DEV_3 DSP device设备Core 3

    3.2. MI_DSP_DevAttr_t

    • 说明

      定义设备Device的属性。

    • 定义

      typedef struct MI_DSP_DevAttr_s
      {
          MI_DSP_FW_PACKING_TYPE_e  eFwPackingType; 
          MI_U32  u32ResetVectorAddr;
          MI_U16  u16Prid;
          MI_BOOL  bRebootFw;
      } MI_DSP_DevAttr_t;
      
    • 成员

      成员名称 描述
      eFwPackingType Firmware的打包类型
      u32ResetVectorAddr Firmware的reset vector 地址
      u16Prid DSP设备指定的processer id,debug时需设置为0x272
      bRebootFw Invoke 超时是否自动reset firmware
    • 注意事项

      • 当在IDE create Custom LSPs时选定External选项用以自定义address,u32ResetVecAddr需与该address一致,且该值要64对齐;当选定Primary选项,u32ResetVecAddr需设置为0。

      • 当firmware中部分函数存放在iram,则eFwPackingType为E_MI_DSP_FW_PACKING_ROMING,故需要修改编译firmware的LSP——memmap.xmm文件添加ROMING=true与specs文件使用-lhandler-reset-unpack替换原来的-lhandler-reset,具体查看LSP Manual Chapter 3 Placing Applications in ROM;若没有函数存放在iram,则eFwPackingType设置为E_MI_DSP_FW_PACKING_PLAIN。

    3.3. CustReadFunc

    • 说明

      用于客制化的读取文件方式,需注意该函数执行成功时返回值应大于等于0。

    • 定义

      typedef int (* CustReadFunc)(void *dst_buf, int offset, int size, char *ctx)
      
    • 成员

      成员名称 描述
      dst_buf 数据存放地址
      offset 文件指针偏移量
      size 读取文件大小
      ctx 文件路径
    • 注意事项

      • 当用户需要自定义读取文件的时候(如加密),可以设置此函数指针。

      • 该函数所指定的dst空间,是由MI_DSP_CreateDev()函数内部进行分配的,大小为fwSize,用户不需分配。

      • 当用户将该函数设置为NULL时,使用默认的读取firmware函数,默认读取函数不需用户提供。

    3.4. MI_DSP_BufVector_t

    • 说明

      定义多个输入/输出Buffer的结构体。

    • 定义

      typedef struct MI_DSP_BufVector_s
      {
          MI_U32 u32BufCount;
          MI_DSP_BufInfo_t astBufInfoVec[MAX_BUF_CNT]; 
      } MI_DSP_BufVector_t;
      
    • 成员

      成员名称 描述
      u32BufCount BufInfo的个数
      astBufInfoVec BufInfo的数组
    • 注意事项

      • MAX_BUF_CNT推荐值是10,该宏定义在mi_dsp_datatype.h中指定:#define MAX_BUF_CNT 10。

    3.5. MI_DSP_BufInfo_t

    • 说明

      定义码流信息的结构体。

    • 定义

      typedef  struct  MI_DSP_BufInfo_s
      {
          MI_PHY phyAddr;
          MI_U32 u32Size;
          MI_U32 u32Flag;
      } MI_DSP_BufInfo_t;
      
    • 成员

      成员名称 描述
      phyAddr 传给DSP的内存的物理地址
      u32Size 传给DSP的内存的大小
      u32Flag 传给DSP的内存的一些标志信息
    • 注意事项

      • 通过MI_SYS的接口获取连续的物理内存。

    3.6. MI_DSP_PARAM_t

    • 说明

      定义客制化的用户参数,可以是立即数或地址/长度。

    • 定义

      typedef struct MI_DSP_PARAM_s
      {
          MI_U64 u64Data[MAX_PARA_CNT];    
          MI_S32 s32Result;
      } MI_DSP_PARAM_t;
      
    • 成员

      成员名称 描述
      u64Data 自定义数组,用于向DSP的message handler发送invoke参数
      s32Result 调用MI_DSP_Invoke时,存放DSP的message handler的返回结果
    • 注意事项

      • MAX_PARA_CNT推荐值是2,该宏定义在mi_dsp_datatype.h中指定:#define MAX_PARA_CNT 2。

      • MI_DSP_Invoke时,数组可存放定制化的用户参数,若存放的是地址,必须是MI_SYS_MMA_ALLOC申请的buffer的物理地址,便于DSP查看。

      • s32Result仅在MI_DSP_Invoke 调用时才会被赋值。

    3.7. MI_DSP_PRI_e

    • 说明

      定义MI_DSP_Invoke API的优先级,表示dsp所处理msg的队列的优先级。

    • 定义

      typedef enum
      {
          MI_DSP_PRI_0 = 0,  
          MI_DSP_PRI_1,        
          MI_DSP_PRI_2,        
          MI_DSP_PRI_BUTT
      } MI_DSP_PRI_e;
      
    • 成员

      成员名称 描述
      MI_DSP_PRI_0 优先队列级别0,最高
      MI_DSP_PRI_1 优先队列级别1
      MI_DSP_PRI_2 优先队列级别2,最低

    3.8. MI_DSP_FW_PACKING_TYPE_e

    • 说明

      定义firmware的打包类型。

    • 定义

      typedef enum
      {
          E_MI_DSP_FW_PACKING_ROMING = 0,
          E_MI_DSP_FW_PACKING_PLAIN,
          E_MI_DSP_FW_PACKING_BUTT
      } MI_DSP_FW_PACKING_TYPE_e;
      
    • 成员

      成员名称 描述
      E_MI_DSP_FW_PACKING_ROMING Firmware打包为ROMING类型
      E_MI_DSP_FW_PACKING_PLAIN Firmware打包为PLAIN类型
    • 注意事项

      • 当firmware中部分函数存放在iram,则eFwPackingType为E_MI_DSP_FW_PACKING_ROMING,故需要修改编译firmware的LSP——memmap.xmm文件添加ROMING=true与specs文件使用-lhandler-reset-unpack替换原来的-lhandler-reset,具体查看LSP Manual Chapter 3 Placing Applications in ROM;若没有函数存放在iram,则eFwPackingType设置为E_MI_DSP_FW_PACKING_PLAIN。

    4. 错误码

    MI DSP模块 错误码如下表所示。

    错误码 宏定义 描述
    0xA0242080 MI_DSP_ERR_READ_FILE 读取文件错误
    0xA0242081 MI_DSP_ERR_INVALID_DEVID 设备号错误或超出硬件支持范围
    0xA0242082 MI_DSP_ERR_DEV_ATTR Device设备属性参数错误
    0xA0242083 MI_DSP_ERR_RE_CREATE 重复创建已存在的设备
    0xA0242084 MI_DSP_ERR_NULL_PTR 函数参数中有不允许为空的指针
    0xA0242085 MI_DSP_ERR_PRIQ_FULL Priority Queue满
    0xA0242086 MI_DSP_ERR_NOT_READY 系统没有初始化或Firmware没有ready
    0xA0242087 MI_DSP_ERR_TIMEOUT 系统异常导致超时
    0xA0242088 MI_DSP_ERR_INVALID_PARAM 参数超出合法范围
    0xA0242089 MI_DSP_ERR_BAD_ADDRESS 地址非法
    0xA024208A MI_DSP_ERR_FAILED 其他错误

    5. 示例代码

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <mi_dsp.h>
    #include <mi_dsp_datatype.h>
    #include <mi_sys.h>
    
    int main()
    {
        MI_S32 retVal = 0;
        MI_DSP_DevAttr_t stDspDevAttr;
        MI_DSP_BufVector_t stInputBuf, stOutputBuf;
        MI_DSP_PARAM_t stParam;
    
        void* virtAddrIn  = 0;
        void* virtAddrOut = 0;
        stDspDevAttr. eFwPackingType = E_MI_DSP_FW_PACKING_PLAIN;
        stDspDevAttr.u32ResetVecAddr = 0x10040000;
        stDspDevAttr.u16Prid = 0x272;
    
        /* 1、创建dsp core0的设备,并指定firmware路径 */
        retVal = MI_DSP_CreateDev(0, &stDspDevAttr, NULL, "/lib/firmware/tc_invoke.elf", 0);
        if (retVal != MI_SUCCESS)
        {
            printf("core 0 createdev err, retVal: 0x%x!\n", retVal);
            goto create_err;
        }
    
        memset(&stInputBuf, 0, sizeof(MI_DSP_BufVector_t));
        memset(&stOutputBuf, 0, sizeof(MI_DSP_BufVector_t));
    
        /* 2、通过MMA分配InputBuf的空间,并对其进行映射 */
        stInputBuf.astBufInfoVec[0].u32Size = 0x1000;
        MI_SYS_MMA_Alloc(0, NULL, stInputBuf.astBufInfoVec[0].u32Size, &stInputBuf.astBufInfoVec[0].phyAddr);
        MI_SYS_Mmap(stInputBuf.astBufInfoVec[0].phyAddr, stInputBuf.astBufInfoVec[0].u32Size, &virtAddrIn, FALSE);
        stInputBuf.u32BufCount = 1;
        stInputBuf.astBufInfoVec[0].u32Flag = 0x1;
        memset(virtAddrIn, 0, stInputBuf.astBufInfoVec[0].u32Size);
    
        /* 3、通过MMA分配OutputBuf的空间,并对其进行映射 */
        stOutputBuf.astBufInfoVec[0].u32Size = 0x1000;
        MI_SYS_MMA_Alloc(0, NULL, stOutputBuf.astBufInfoVec[0].u32Size, &stOutputBuf.astBufInfoVec[0].phyAddr);
        MI_SYS_Mmap(stOutputBuf.astBufInfoVec[0].phyAddr, stOutputBuf.astBufInfoVec[0].u32Size, &virtAddrOut, FALSE);
        stOutputBuf.u32BufCount = 1;
        stOutputBuf.astBufInfoVec[0].u32Flag = 0x2;
        stParam.u64Data[0] = 0x2222;
        memset(virtAddrOut, 0, stOutputBuf.astBufInfoVec[0].u32Size);
    
        /* 4、对InputBuf进行赋值 */
        for(int i = 0; i < stInputBuf.astBufInfoVec[0].u32Size; i++) {
            char* p = (char*)virtAddrIn;
            p[i] = 0xc2;
        }
    
        /* 5、调用一次dsp core0 的算法 */
        retVal = MI_DSP_Invoke(0, &stInputBuf, &stOutputBuf, &stParam, 0, 65535);
        if (retVal != MI_SUCCESS)
        {
            printf("core 0 invoke err, retVal: 0x%x!\n", retVal);
            goto invoke_err;
        }
    
        /* 6、打印处理结果 */
        for(int i = 0; i < 10; i++) {
            char* p = (char*)virtAddrOut;
            printf("%d \n", p[i]);
        }
    
        /* 7、进行资源释放工作 */
    invoke_err:
        MI_SYS_Munmap(virtAddr, 0x1000);
        MI_SYS_MMA_Free(0, u64PhyAddr);
        MI_DSP_DestroyDev(0);
    
    create_err:
        while(1) {
            sleep(1);
        }
    
        return 0;
    }
    

    6. PROCFS介绍

    6.1. 使用GDB调试firmware

    6.1.1. 概述

    正确配置软件系统,在板端运行MI_DSP APP。

    在debug主机配置Jlink环境,并正确连接JTAG usb debug工具。

    6.1.2. JTAG 接口切换

    在默认板端的SOC 线路中,JTAG连接的是ARM cores。需要在板端的console键入如下命令,将JTAG连接至DSP cores。

    • /customer/riu_w 101e 7b 101;/customer/riu_w 101e 7b 001;将JTAG 切到 DSP cores上 后续 xt-ocd 才能连上。

    • /customer/riu_w 101e 7b 0 将JTAG切回到 ARM cores。

    6.1.3. xt-ocd 工具安装

    通过xplorer XPG下载xt-ocd-14.05-windows64-installer.exe,如果debug主机是在Linux环境,请下载Linux版本并安装。

    运行xt-ocd需要jlink 的库:Jlink_x64.dll(windows),建议复制到xt-ocd安装目录的modules文件夹中。

    6.1.4. xt-ocd 工具运行

    xt-ocd的运行详情请参考cadence官方文档:Xtensa® Debug Guide。

    Xt-ocd 运行需要在MI_DSP createdev 之后进行,在那之前DSP cores还没有被初始化。

    基本的运行方式:在debug主机运行 xt-ocd.exe -c mytopology.xml。 其中mytopology.xml为配置文件。

    成功运行之后如有如下类似log,即能够正确获取 JTAG 的IR bits跟 width。

    D:\Program Files (x86)\Tensilica\Xtensa OCD Daemon 14.05>xt-ocd.exe -c mytopology.xml
    XOCD 14.05 2020-10-12 17:38:02
    (c) 1999-2021 Cadence Design Systems Inc. All rights reserved.
    [Debug Log 2021-03-04 16:54:53]
    Loading module "gdbstub" v2.0.0.12
    Loading module "jlink" v2.0.2.0
    Using JLINK lib v.61210
    Jlink USB Serial Number: 261003840
    Connected to Jlink Device:
    Name:'J-Link V11 compiled Aug 14 2019 16:21:09'
    S/N:261003840
    Firmware: J-Link V11 compiled Aug 14 2019 16:21:09
    Requested/Set TCK: 2000kHz/2000kHz
    Loading module "jtag" v2.0.0.20
    Loading module "xtensa" v2.0.0.48
    Loading module "traxapp" v2.0.0.8
    Loading module "trax" v2.0.1.23
    Starting thread 'GDBStub'
    Starting thread 'TraxApp'
    Opened GDB socket at port 20000
    traxapp: listening on port 11444
    Initialize XDM driver
    Total IR bits : 5
    TAP[0] irwidth = 5
    

    6.1.5. xt-ocd 配置文件

    <configuration>
    <controller id='Controller0' module='jlink' usbser='261008694' speed='10000000'/>
    <!--
        Default Flyswatter2
    <controller id='Controller0' module='ft2232' probe='flyswatter2' speed='10MHz' />
    
    Here are other example controller (probe) lines, that can replace the above one
    when using different JTAG probes (scan controllers):
    
    Cadence Design Systems ML605 Daughterboard - optional usbser is inventory sticker number prefixed with 'ML605-':
    <controller id='Controller0' module='ft2232' probe='ML605' speed='10MHz' usbser='ML605-2147' />
    
    Tin Can Tools Flyswatter2:
    <controller id='Controller0' module='ft2232' probe='flyswatter2' speed='10MHz' />
    
    RVI/DSTREAM: NOTE: Probe settings are in the loaded rvc file
            <controller id='Controller0' module='rvi' />
    
    JLink IP (10MHz JTCK):
    <controller id='Controller0' module='jlink' ipaddr='192.168.1.1'  port='0' speed='10000000'/>
    
    JLink USB (10MHz JTCK):
    <controller id='Controller0' module='jlink' usbser='12345678' speed='10000000'/>
    -->
    <driver id='XtensaDriver0' module='xtensa' step-intr='mask,stepover,setps' />
    <driver id='TraxDriver0'   module='trax' />
    <chain controller='Controller0'>
        <tap id='TAP0' irwidth='5' />
    </chain>
    <system module='jtag'>
        <component id='Component0' tap='TAP0' config='trax' />
    </system>
    <device id='Xtensa0' component='Component0' driver='XtensaDriver0' />
    <device id='Trax0'   component='Component0' driver='TraxDriver0' />
    <application id='GDBStub' module='gdbstub' port='20000'>
        <target device='Xtensa0' />
    </application>
    <application id='TraxApp' module='traxapp' port='11444'>
        <target device='Trax0' />
    </application>
    </configuration>
    

    配置文件中**usbser='261008694'**,项目需要根据实际情况填写jlink的usb S/N号码。可以在DEBUG主机的Jlink App中看到对应的USB SN号码。

    配置文件中的速度可以适当调快。在FPGA环境中,如果的程序比较大/交互信息多,过快会导致沟通失败。在xt-ocd的运行窗口可看到失败异常信息。

    6.1.6. GDB调试

    1. 在本地调试console运行xt-gdb:xt-gdb.exe --xtensa-core=T12_DSP_eva HelloWorld1

    2. 连接HAPS远程环境 target remote [host]:[port]:target remote 172.19.32.104:20000

    3. 关闭自动本地连接:set auto-connect-native-target off

    4. load程序:load

    5. continue运行程序:continue

    附上一份运行log:

    E:\usr\xtensa\XtDevTools\install\tools\RI-2020.5-win32\XtensaTools\bin>xt-gdb.exe  --xtensa-core=T12_DSP_fpga  HelloWorld1
    GNU gdb (GDB) 7.11.1 Xtensa Tools 14.05
    Copyright (C) 2016 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "--host=x86_64-w64-mingw32 --target=xtensa-elf".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from HelloWorld1...done.
    (xt-gdb) target remote 172.19.32.104:20000
    Remote debugging using 172.19.32.104:20000
    0x00000000 in ?? ()
    (xt-gdb) set auto-connect-native-target off
    (xt-gdb) load
    Loading section .DispatchVector.text, size 0x25c lma 0x10000000
    Loading section .DispatchHandler.text, size 0x4 lma 0x1000025c
    Loading section .ResetVector.text, size 0x4 lma 0x10004000
    Loading section .ResetHandler.text, size 0x18c lma 0x10004004
    Loading section .clib.rodata, size 0x618 lma 0x20000000
    Loading section .rtos.rodata, size 0x80 lma 0x20000640
    Loading section .rodata, size 0x218 lma 0x200006c0
    Loading section .text, size 0xa298 lma 0x200008e0
    Loading section .clib.data, size 0x1bc lma 0x2000ab80
    Loading section .data, size 0x233c lma 0x2000ad40
    Start address 0x10004000, load size 54320
    Transfer rate: 26 KB/sec, 1597 bytes/write.
    (xt-gdb) continue
    Continuing.
    main 34
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    main 36 cc
    hello world 0
    
    Program received signal SIGTRAP, Trace/breakpoint trap.
    _exit () at /build/tree/RI-2020.5_kuma/ib/tools/swtools-MSWin32-x86/xtensa-elf/src/xtos/exit.S:88
    88      /build/tree/RI-2020.5_kuma/ib/tools/swtools-MSWin32-x86/xtensa-elf/src/xtos/exit.S: No such file or directory.
    (xt-gdb)
    

    Xt-gdb的调试手册请参考cadence的官方文档:GNU Debugger User's Guide

    6.2. 使用xplorer IDE 调试firmware

    6.2.1. 概述

    除了使用xt-gdb命令调试firmware之外,还可以使用xplorer IDE。

    IDE 调试与gdb 直接调试需要的准备工作一样,都需要执行5.1.章节的5.1.1到5.1.5。

    6.2.2. IDE调试

    使用xplorer 连接dsp,在xplorer 工具栏中配置正在运行的firmware,在debug选项中选择debug configuration。

    如下图所示,新建一个attach to xtensa gdb port 配置,并按图选择配置项目。注意host IP地址请填写haps 主机的ip地址。

    连接上DSP之后,可以看到DSP处于running状态。

    选择pause->||,然后选择pause,此时DSP会stall住,这个时候可以通过IDE 查看系统的各个信息。如:运行栈,内存信息,变量信息,寄存器信息等。