MI DSP API
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
3.0 | 03/18/2021 | |
08/25/2021 | ||
3.1 | 12/02/2021 | |
3.2 | 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调试¶
-
在本地调试console运行xt-gdb:xt-gdb.exe --xtensa-core=T12_DSP_eva HelloWorld1
-
连接HAPS远程环境 target remote [host]:[port]:target remote 172.19.32.104:20000
-
关闭自动本地连接:set auto-connect-native-target off
-
load程序:load
-
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 查看系统的各个信息。如:运行栈,内存信息,变量信息,寄存器信息等。