跳到主要内容

7.7 VDSP 开发指南

注意

S600上包含两个 VDSP 核,以下描述关于 VDSP1的使用只有在 S600上支持!

基础调试指南

CPU 侧开发

镜像加载卸载

VDSP 所有核共用一个 Firmware,默认名字为 vdsp0。第二个 VDSP 核(VDSP1,仅 S600等双核平台)与 VDSP0在功能上一致:同样通过 remoteproc 节点设置 FW 名称、执行加载/卸载,并可通过 versionstate 等节点查看版本与运行状态;区别仅在于 sysfs 节点为 remoteproc_vdsp1(以及启停、日志等路径中的实例号,见下文各小节)。系统在启动时默认不启动 VDSP FW(Firmware),需要用户通过命令的形式手动加载和卸载 FW,命令如下所示:

echo -n <firmware路径> > /sys/module/firmware_class/parameters/path

# 设置VDSP0 FW名称:
echo <firmware名称> > /sys/class/remoteproc/remoteproc_vdsp0/firmware
#VDSP0的FW加载:
echo start > /sys/class/remoteproc/remoteproc_vdsp0/state
#VDSP0的FW卸载:
echo stop > /sys/class/remoteproc/remoteproc_vdsp0/state

# 设置VDSP1 FW名称:
echo <firmware名称> > /sys/class/remoteproc/remoteproc_vdsp1/firmware
#VDSP1的FW加载:
echo start > /sys/class/remoteproc/remoteproc_vdsp1/state
#VDSP1的FW卸载:
echo stop > /sys/class/remoteproc/remoteproc_vdsp1/state

用户可通过以下命令修改 FW 路径(必须是绝对路径):

echo -n <firmware路径> > /sys/module/firmware_class/parameters/path

用户可根据自己命名的 FW 名称在加载之前进行调整:

# VDSP0
echo <firmware名称> > /sys/class/remoteproc/remoteproc_vdsp0/firmware

# VDSP1
echo <firmware名称> > /sys/class/remoteproc/remoteproc_vdsp1/firmware

用户需要修改原始镜像,配置 init.rc,kernel 启动后由 init 进程自动加载 VDSP 镜像。

#首先将编译出的FW镜像(如vdsp0)拷贝到/userdata 下
echo -n <firmware路径> > /sys/module/firmware_class/parameters/path
# VDSP0
echo <firmware名称> > /sys/class/remoteproc/remoteproc_vdsp0/firmware
echo start > /sys/class/remoteproc/remoteproc_vdsp0/state

# VDSP1
echo <firmware名称> > /sys/class/remoteproc/remoteproc_vdsp1/firmware
echo start > /sys/class/remoteproc/remoteproc_vdsp1/state

FW 版本查看

#S100 VDSP0
cat /sys/class/remoteproc/remoteproc_vdsp0/version # for vdsp0
#S600 VDSP1(与 VDSP0 相同,仅节点不同)
cat /sys/class/remoteproc/remoteproc_vdsp1/version # for vdsp1

VDSP 运行状态查看

#running表示已加载,offline表示未加载
#S100 VDSP0
cat /sys/class/remoteproc/remoteproc_vdsp0/state # for vdsp0
#S600 VDSP1(与 VDSP0 相同,仅节点不同)
cat /sys/class/remoteproc/remoteproc_vdsp1/state # for vdsp1

心跳监控

默认处于关闭状态,可通过下列命令打开或关闭;心跳监控、发送周期为100ms,监控到连续7次心跳被丢失就会上报诊断并 reset VDSP。

# 打开心跳监控
echo Y > /sys/module/hobot_remoteproc/parameters/heartbeat_enable
# 关闭心跳监控
echo N > /sys/module/hobot_remoteproc/parameters/heartbeat_enable

通过函数接口形式操作 vdsp

通过 libvdsp.so 动态链接库加载 DSP 程序,实现加载、启动、停止、复位、获取 DSP 状态等功能,API 介绍可参考 VDSP启停控制接口

消息连接和发送

目前用户只可使用系统预设的服务名称,可使用的服务名如下表所示:

VDSP服务名称作用是否必须启动VDSP 侧是否默认启动
DSP0/1dcore0_device_op/dcore1_device_op系统软件内部对 DSP 的调试控制,系统软件已经使用,用户不可再次注册和使用
DSP0/1dcore0_acore_heart/dcore1_acore_heart心跳机制使用,目前未使用,用户可用作其他用途
DSP0/1dcore0_rpmsg_bpu/dcore1_rpmsg_bpuBPU 相关的控制,目前未使用,用户可用作其他用途
DSP0/1dcore0_rpmsg_op/dcore1_rpmsg_op工具链算子相关的控制,目前未使用,用户可用作其他用途

用户可使用的 API 可参考下表:

接口功能头文件相关库
hb_rpmsg_connect_server连接服务rpmsg_lib.hlibrpmsg.so
hb_rpmsg_disconnect_server断开服务rpmsg_lib.hlibrpmsg.so
hb_rpmsg_send发送消息rpmsg_lib.hlibrpmsg.so
hb_rpmsg_recv接收消息rpmsg_lib.hlibrpmsg.so

其中,同一个服务通道,不支持多进程、多线程并发接收或发送。

heap 相关开发

VDSP 提供了动态分配释放 heap 接口,支持配置自定义内存对齐大小,以及支持查看当前 heap 状态,当 VDSP 端需要动态分配 heap 时,可以通过以下接口实现。

接口功能相关头文件
hb_mem_heap_initialize初始化 mem alloctor 接口hb_mem_allocator.h
hb_mem_heap_deinitialize解初始化 mem alloctor 接口hb_mem_allocator.h
hb_mem_heap_alloc分配 heap 空间hb_mem_allocator.h
hb_mem_heap_free释放已分配的 heap 空间hb_mem_allocator.h
hb_mem_heap_get_status获取当前 heap 状态hb_mem_allocator.h

VDSP 侧开发

代码获取步骤如下:

(1)首先获取到发布包,解压后确认有vdsp源码。如果无,联系地瓜相关人员进行获取。
(2)在vdsp路径下可以获取到vdsp源码。

Linux 环境搭建

提示

获取 build 包建立编译环境。其中 build 包的获取请联系地瓜相关人员进行获取。

本文档仅提供 Linux 环境搭建及编译的说明介绍。对于文档中提到的 xplorer 中获取调试文档请联系地瓜相关人员进行获取。

Linux 环境下安装 build 的命令:

tar -zxvf Vision_Q8_linux.tgz \
&& mv RI-2023.11-linux/Vision_Q8/ /opt/xtensa/XtDevTools/install/builds/RI-2023.11-linux/ \
&& rm -rf RI-2023.11-linux

/opt/xtensa/XtDevTools/install/builds/RI-2023.11-linux/Vision_Q8/install --xtensa-tools \
/opt/xtensa/XtDevTools/install/tools/RI-2023.11-linux/XtensaTools/

编译

获取代码后编译步骤如下:

(1)cd vdsp_fw
(2)bash make.sh

静态库生成在 library 目录

library/libvdsp0.a

二进制镜像生成在 samples 目录

samples/{subdir}/vdsp0

调试指南

日志查看

VDSP FW 的日志会通过串口输出。

需要注意的是 VDSP FW 与其他模块共用一个串口,如 BL31、optee,若 VDSP FW 输出日志太多,可能会阻塞这些模块的日志输出,引起 watchdog。 另外 VDSP FW 日志和 kernel 日志都输出到串口中,存在相互干扰的问题,用户可通过降低 kernel 日志等级来防止日志干扰:

echo 0 > /proc/sys/kernel/printk

当没有串口可用的情况下,用户可通过 ssh 登录板子,后台默认会启动 hrut_remoteproc_log 服务:

#VDSP0 默认开机执行的启动命令,日志保存路径:/log/dsp0/message
hrut_remoteproc_log -b /sys/class/remoteproc/remoteproc_vdsp0/log -f /log/dsp0/message -r 2048 -n 200
#VDSP1(与 VDSP0 相同用法,仅 remoteproc 节点与落盘路径换为 dsp1)
hrut_remoteproc_log -b /sys/class/remoteproc/remoteproc_vdsp1/log -f /log/dsp1/message -r 2048 -n 200

同样的,VDSP FW 的日志会写入 Share memory 中,由 CPU 侧 log 服务进程存入文件系统中。因此用户可通过以下路径下的文件查看日志,但是需要注意的是这里的日志并不是实时的。

#VDSP0的日志路径:
/log/dsp0/message
/log/dsp0/archive/
#VDSP1的日志路径:
/log/dsp1/message
/log/dsp1/archive/
#message是临时文件,存满之后会写入到archive/目录下,当该目录下的文件达到一定数量后,会删除时间较早产生的文件

日志打印接口

使用 printf 接口日志会通过串口输出。

推荐使用 DSP_ERR、DSP_WARN、DSP_INFO、DSP_DBG 接口,该接口除了将日志通过串口输出,还会将日志写入 Share memory 中,通过 CPU 侧的 log 服务将日志信息存入文件系统中。

DSP_*接口使用注意事项:

  1. 头文件hb_vdsp_log.h
  2. 接口使用示例。比如进入异常分支需要打印日志时,使用 DSP_ERR 接口,DSP_ERR("Input parameter invalid.\n");

线程状态查看

通过以下命令可在串口中查看 VDSP 侧的线程状态。需要注意的是以下数据的统计和输出可能会影响 VDSP 的性能。

使用方法:首先需要代码中使能#define THREAD_STACK_CHECK (1),其次需要在新启动的线程前使能栈跟踪,如下所示:

(void)hb_enable_stack_track(dev_thread_stack, sizeof(dev_thread_stack)/sizeof(dev_thread_stack[0]));
#S100 VDSP0:
echo on > /sys/devices/virtual/misc/vdsp0/vdsp_ctrl/dspthread
echo off > /sys/devices/virtual/misc/vdsp0/vdsp_ctrl/dspthread
#S600 VDSP1(与 VDSP0 相同,仅 misc 设备节点为 vdsp1):
echo on > /sys/devices/virtual/misc/vdsp1/vdsp_ctrl/dspthread
echo off > /sys/devices/virtual/misc/vdsp1/vdsp_ctrl/dspthread

coredump 查看

和 coredump 相关的系统软件初始化主要有两部分:注册异常和使能看门狗。

hb_wdt_on();
hb_enable_coredump();

目前 xos 能够处理的异常类型如下:

/*  EXCCAUSE register values:  */
/* General Exception causes (Bits 3:0 of EXCCAUSE register) */

/* No exception */
#define EXCCAUSE_NONE UINT32_C(0)
/* Instruction usage */
#define EXCCAUSE_INSTRUCTION UINT32_C(1)
/* Addressing usage */
#define EXCCAUSE_ADDRESS UINT32_C(2)
/* External causes */
#define EXCCAUSE_EXTERNAL UINT32_C(3)
/* Debug exception */
#define EXCCAUSE_DEBUG UINT32_C(4)
/* Syscall exception */
#define EXCCAUSE_SYSCALL UINT32_C(5)
/* Hardware failure */
#define EXCCAUSE_HARDWARE UINT32_C(6)
/* Memory management */
#define EXCCAUSE_MEMORY UINT32_C(7)
/* Coprocessor */
#define EXCCAUSE_CP_DISABLED UINT32_C(8)
/* Reserved 9-15 */

不应在 VQ8 上为异常原因 4(调试异常)/5(SYSCALL 异常)/8(协处理器异常)注册异常处理程序,为系统预留使用。 需要注意的是:9~15 为预留类型,也需要略过不注册。

离线调试方法如下: VDSP 发生 coredump 时,Acore 会把 VDSP 所有可能使用的 memory 空间(iram/dram0/dram1/reserved ddr)全部写入指定的文件系统中,路径如下:

#vdsp0 / vdsp1 的落盘目录相同;具体 dump 文件名会带 vdsp0_* 或 vdsp1_* 前缀以区分实例
/log/coredump/

新建 restore.script.sh 脚本,4个 memory dump 文件的路径根据实际项目的存放路径设置,把获得到的 CPU 寄存器复制到该脚本对应处,如下所示:

python import thread_aware_rtos
python thread_aware_rtos.k.rtos_support.dump_analysis_mode = True
b main
run

restore vdsp0_ddr_2024-05-06-02-50-03.hex binary 0xf0000000
restore vdsp0_iram_2024-05-06-02-50-03.hex binary 0x08080000
restore vdsp0_dram0_2024-05-06-02-50-03.hex binary 0x08000000
restore vdsp0_dram1_2024-05-06-02-50-03.hex binary 0x08040000
# VDSP1:调试步骤与 VDSP0 相同,将上述文件替换为实际生成的 vdsp1_ddr_*.hex、vdsp1_iram_*.hex 等

set $ar0 = 0xf00502a8
set $ar1 = 0xf3fdded0
set $ar2 = 0xf3fddd00
set $ar3 = 0x34
set $ar4 = 0x1b
set $ar5 = 0x5d
set $ar6 = 0x53
set $ar7 = 0x58
set $ar8 = 0xf0050192
set $ar9 = 0xf3fddeb0
set $ar10 = 0x4f
set $ar11 = 0xf3fddd00
set $ar12 = 0x0
set $ar13 = 0xf3fddee0
set $ar14 = 0xf3fddce0
set $ar15 = 0xf3fddd1b
set $ar16 = 0xf0065978
set $ar17 = 0xf3fddb60
set $ar18 = 0x4f
set $ar19 = 0xf002bea4
set $ar20 = 0x0
set $ar21 = 0xffffffb1
set $ar22 = 0x4f
set $ar23 = 0xffffffff
set $ar24 = 0x808100f
set $ar25 = 0xf3fddf00
set $ar26 = 0xf3fddf10
set $ar27 = 0x53113
set $ar28 = 0xf0050280
set $ar29 = 0xffffffff
set $ar30 = 0x0
set $ar31 = 0xf3fddf70
set $ps = 0x68
set $wb = 0x40000311
set $pc = 0xf0050057
python thread_aware_rtos.k.rtos_support.XOS_initialized = True

打开 xt-gdb 命令行(xplorer 或者命令行模式均可),按照顺序执行如下操作:

xt-gdb vdsp0 (可执行文件的目标文件)
(xt-gdb) >> source restore.script.sh
(xt-gdb) >> run
ctrl+c //取消运行
(xt-gdb) >> stepi
(xt-gdb) >> info threads
(xt-gdb) >> bt

backtrace 调试信息显示如下:

(xt-gdb) bt
#0 _RMCDump () at /vdsp/bsp_project/coredump/RegDump.S:90
#1 0xf0050192 in Exc_Dump_Regs () at bsp_project/coredump/Exc_Dump_Regs.c:110
#2 0xf00502a8 in dafault_exchandler (frame=0xf3fddf10) at bsp_project/coredump/coredump.c:125
#3 0x0808100f in _GeneralException (cause=..., exccause=...) at ./xos_vectors_xea3_v2.S
#4 0xf00504b8 in hb_platform_init () at bsp_project/driver/devcontrol/devcontrol.c:152
#5 0xf0030306 in main (argc=1, argv=0xf0073704) at main.c:68

Stack usage 查看

Stack usage 的说明建议阅读 Xtensa® XOS Reference Manual Reference Manual:<VDSP 安装路径>/xtensa/XtDevTools/downloads/<version>/docs/xos_rm.pdf。

MPU 配置

目前部署的 MPU 主要两个作用,一是用来限制 VDSP 访问地址的范围,访问超过 MPU 允许的范围会报 coredump 错误,第二个作用是可以配置地址段的属性,详细介绍请参考 Xtensa® System Software Reference Manual:<VDSP 安装路径>/xtensa/XtDevTools/downloads/<version>/docs/xos_rm.pdf。

VDSP 地址映射以及 MPU 保护部分如下图所示,访问 MPU 保护地址范围外会报 coredump 错误。

VDSP地址映射及MPU保护范围示意图

报错 log 如下图所示,错误地址为0x0,表示访问了不允许访问的地址:

VDSP访问非法地址0x0时的coredump报错日志

目前对于 VDSP 地址的属性配置,主要包括三个部分:

(1)对于 ION 空间的属性配置: XTHAL_MEM_WRITEBACK

(2)对于两个共享内存区域的属性配置:XTHAL_MEM_NON_CACHEABLE

(3)对于除(1)(2)外其他段的属性配置:XTHAL_MEM_WRITEBACK

Cadence 文档路径位置

安装 Xplorer 后,可通过如下位置<VDSP 安装路径>/xtensa/XtDevTools/downloads/RI-2023.11/docs 查看已下载的文档路径。

FAQ

VDSP 侧耗时统计方式有哪些?

用户可使用 gettimeofday()直接获取时间,也可通过 XT_RSR_CCOUNT()获取 count 个数来换算时间,前者需要包含#include <sys/time.h>头文件。建议使用后者来统计时间,更精确,且不建议在耗时要求较高的场景下打印日志。

此外用户还可参考 Xtensa® Software Development Toolkit User's Guide:<VDSP 安装路径>/xtensa/XtDevTools/downloads/<version>/docs/sw_dev_toolkit_ug.pdf。

LSP 如何修改?

(1)拷贝一份模板 lsp

(2)编辑 memmap.xmm

(3)重新生成 memmap,执行命令:xt-genldscripts -b custom_lsp/q8-min-rt/

提示

调试使用 xos 工具时,提示找不到工具链,先确认 xos build 环境是否建立。 如果建立,配置环境变量,比如配置临时环境变量export PATH=$PATH:[*]/XtensaTools/bin

CPU 侧 start 加载和 stop 卸载 FW 时提示不成功?

可能是 stop 时服务根本没启动,或者 start 时服务已启动。

VDSP 侧如何增加用户线程?

示例代码如下:

ret = xos_thread_create(&dev_thread_tcb, 0, dev_thread_func, 0, "dev_control", dev_thread_stack, STACK_SIZE_1, TRACE_THREAD_PRIO, 0, 0);

其中 dev_thread_func 表示创建的线程函数,用来实现用户想要在线程函数中处理的功能。dev_thread_stack 表示指向线程栈的首地址(由用户分配)。STACK_SIZE_1表示栈的大小。TRACE_THREAD_PRIO 表示线程优先级,取值范围在0~15,数值越小优先级越高。

VDSP 侧如何获取设备 id?

可以使用 xthal_get_prid()。

VDSP 侧 idma 使用哪个库?

VDSP侧idma可选库:libidma-os和libidma-debug-os

请使用 libidma-os 或者 libidma-debug-os,因为我们使用 xos。

int64_t/uint64_t/float 类型的变量打印出错?

如果 int64_t/uint64_t/float 定义的变量在打印时输出异常的值,而在使用时(如进行大小比较等操作)是正常的。

这是因为 xtensa/xtutil.h 头文件会将 printf、vsnprintf 等函数替换为 xt_printf,xt_vsnprintf,用户需要将 xtutil.h 头文件注释掉,使用原生的 printf、vsnprintf 接口。

危险

标准 C 库的 printf 等函数,无法在中断 handler 中使用,否则会卡死。

VDSP 在运行过程中出现卡住问题

需要排查变量指针的地址是否是对齐:

(1)(int64_t *)类型的变量指针的地址需要八字节对齐

(2)(int32_t *)类型的变量指针的地址需要四字节对齐

(3)(int16_t *)类型的变量指针的地址需要二字节对齐

确认在中断 handler 是否使用了标准 C 库的 printf 等函数,目前中断 handler 不支持使用。

使用 sim 软仿时出现 seg 段溢出问题

需要更改 sim 的 xmm 文件,路径:xtensa/xtensa/XtDevTools/install/builds/RI-2023.11-win32/Vision_Q8/xtensa-elf/lib/sim/memmap.xmm, 将相应的溢出段的值改大,在 xplorer 下打开 cmd:

在Xplorer中打开命令行窗口

进到 xtensa-elf/lib 路径下:

进入xtensa-elf/lib路径的终端操作界面

执行 xt-genldscripts -b sim,如下提示表明成功:

执行xt-genldscripts -b sim命令的成功输出提示

再次编译 vdsp 工程,进行 sim 软仿。

Idma 搬运过程中出现异常停止问题

出现此情况可以查看 idma init 函数中,是否有设置运行时间,置0可以关闭时间限制:

IDMA初始化函数中运行时间限制参数的设置代码

在 windows 环境编译出的镜像提示 dcore0_rpmsg_op server not start

需要手动在 Build Properties 中添加 CONFIG_TEST_CASE:

在Build Properties中手动添加CONFIG_TEST_CASE宏的配置界面

在总线程个数大于32的运行环境下,线程状态查看功能不生效

这是由于线程 dump 功能实现时,默认定义存放线程信息的数组大小为32; 可以通过加大数组大小来支持大于32个线程状态查看。

如下所示将支持最多64线程状态查看功能:

static int32_t cycle_check(void * arg, int32_t unused)
{
const int32_t countMax = 64;

VDSP 退出流程相关的适配说明

系统中依赖 dev_control 线程正常运行,用户通过 enable 宏 CONFIG_PLATFORM_INIT_BUILTIN_THREADS 来控制由 hb_platform_init 函数启动,如果用户没有 enable 就需要创建相应的线程,同时用户线程需遵循下述的流程:

(1)在用户任务循环线程中添加退出判断的逻辑:调用函数 hb_is_thread_stop,返回1表示需要立即退出线程

VDSP log 使用场景限制

(1)当前中断 handler 不支持直接或间接使用标准 C 库的 printf,如果使用会出现 VDSP 挂死

(2)在 hb_platform_init 之前不支持直接或间接使用标准 C 库的 printf,如果使用可能引发低概率 boot 失败的问题

VDSP 编译注意事项

(1)需要定义平台宏,不同平台对应的宏包括:CONFIG_ARCH_HOBOT_SOC_SIGIE、CONFIG_ARCH_HOBOT_SOC_SIGIP、CONFIG_ARCH_HOBOT_SOC_SIGIB

(2)默认支持不同的 VDSP CORE 可加载同一个 FW,同时就不需要定义 CONFIG_VDSP 宏(CONFIG_VDSP0/CONFIG_VDSP1不再被使用)

安全下电和休眠流程

(1)在安全下电和休眠流程的处理流程中,驱动会检查 VDSP Firmware 状态并确保已经进入停止状态后才继续相应的动作;同时建议在 APP 中实现 VDSP 退出流程

VDSP sample

功能概述

本章节介绍 S 系列 SOC 芯片平台的 VDSP 用例,该用例实现了 VDSP 的启停、核间消息的收发和 VDSP 图像处理。

软件架构说明

需要同时实现 ARM 侧和 VDSP 两侧的开发,当前基于 RPMSG IPC 通信机制实现 client/server 业务交互逻辑。

VDSP软件架构:ARM与VDSP通过RPMSG IPC通信的client/server交互示意图

ARM 侧开发流程

ARM 侧用户主要是加载 VDSP Firmware,并连接 VDSP 侧的服务,作为 client 端向 VDSP 侧发送 rpmsg 计算请求。

ARM侧开发流程:加载VDSP固件并作为client端发送rpmsg计算请求

VDSP 侧开发流程

VDSP 侧主要是初始化其运行环境、启动相关的服务(VDSP 侧作为 server 端可启动多个服务,支持一对一的模式), 并能通过 rpmsg 机制接收和回复 client 端的信息,此外还可启动其他线程进行业务开发。

VDSP侧开发流程:初始化运行环境并启动服务接收rpmsg请求

本用例流程说明:

ARM 侧和 VDSP 的交互流程如下所示:

ARM侧与VDSP侧的RPMSG交互流程时序图

ARM 侧:

  1. 启动 VDSP
  2. 连接 rpmsg server(dcore0_rpmsg_op)
  3. 向 VDSP 发送 rpmsg
  4. 接收 VDSP 发送的 rpmsg
  5. 断开 rpmsg server
  6. 关闭 VDSP

VDSP 侧:

  1. 初始化环境
  2. 启动 rpmsg server(dcore0_rpmsg_op)
  3. 接收 ARM 发送的 rpmsg
  4. 根据接收到的 rpmsg 数据进行解析,并执行相应的图像处理函数
  5. 向 ARM 回复 rpmsg,其中包含执行的结果

代码位置与目录结构

代码路径

  1. 代码位置:
  ARM侧:{sdk_dir}/source/hobot-multimedia-samples/debian/app/vdsp_demo/
DSP侧:{sdk_dir}/vdsp_fw/samples/libxi-sample/
  1. 目录结构:
#源码和二进制文件目录结构,不包括编译框架文件(Makefile、Kconfig等)
ARM:
└── src
└── vdsp_sample.c

DSP:
└── main.c

编译

编译说明

编译命令

# ARM侧sample编译可以直接在板端完成。sample在板端的路径如下所示:
/app/vdsp_demo/vdsp_sample
# 编译命令
cd /app/vdsp_demo/vdsp_sample && make

# VDSP编译命令
cd vdsp_fw
export HR_TARGET_PROJECT=S100 (可配置项:S100/S600)
./make.sh

# VDSP侧输出文件:
{sdk_dir}/vdsp_fw/samples/libxi-sample/vdsp0-{build_type}

运行

支持平台

S100/S600

硬件环境搭建

NA

运行参数说明

下面列出 vdsp sample 支持的输入参数,可以通过 --help 获取到所有参数的说明。

参数名用法默认值
dsp_id-d / --dsp_id=<id>,指定 VDSP 核编号(与 hb_vdsp_*dcore<id>_rpmsg_op 等一致)0
dsp_pathname-p / --dsp_pathname=<path>,指定 VDSP Firmware 路径(传给 hb_vdsp_start/app/vdsp_demo/vdsp_sample/res/q8sample
sample-type-t / --sample-type=<0|1>,sample 类型:0 基础(basic)、1 完整链路(full)1
case-c / --case=<name>,用例名(如 xi-sample-flip;代码中还分支 flip_stressflops_stress 等)xi-sample-flip
test_time--test_time=<秒>,压力类用例时长,单位秒(用于 flip_stress / flops_stress 等)5
loading--loading=<0-100>,压力类用例负载百分比80
help-h / --help,打印用法并退出(未知参数也会进入 usage()(无)

运行结果说明

root@ubuntu:/app/vdsp_demo/vdsp_sample# ./vdsp_sample -d 1 -p /app/vdsp_demo/vdsp_sample/res/q8sample -t 0
vdsp_sample_cxt:
vdsp_id:1
case_name:xi-sample-flip
dsp_pathname:/app/vdsp_demo/vdsp_sample/res/q8sample
vdsp_call_params:
cmd:xi-sample-flip
type:0
buf_width:128
buf_height:128
vdsp_buf0:0xfffd0000
vdsp_buf1:0xfffc0000
result: 0

运行结束后会得到上述 log 输出,recv_buf 返回0表示执行正常。

VDSP API 介绍

核间通信 RPMSG 接口

核间通信 RPMSG 头文件和链接库

  • VDSP 侧

    头文件:hb_rpmsg_interface.h

    链接库:无

  • Acore 侧

    头文件:hb_rpmsg_interface.h

    链接库:librpmsg.so

核间通信 RPMSG API 返回值

VDSP 侧

#define RPMSG_ERR_INVALID_ARG               (-1)
#define RPMSG_ERR_PATH_NOT_LINK (-2)
#define RPMSG_ERR_SERVER_NOT_CONNECT (-3)
#define RPMSG_ERR_OUT_OF_RES (-4)
#define RPMSG_ERR_SEND_BUF_OVERSIZE (-5)
#define RPMSG_ERR_NO_MEM (-6)
#define RPMSG_ERR_TIMEOUT (-7)
#define RPMSG_ERR_RECV_BUF_OVERFLOW (-8)
#define RPMSG_ERR_INVALID_SERVER (-9)
#define RPMSG_ERR_CRC_CHECK (-10)

Acore 侧

#define RPMSG_ERR_INVALID_ARG           (-1)
#define RPMSG_ERR_INVALID_SERVER (-2)
#define RPMSG_ERR_OUT_OF_RES (-3)
#define RPMSG_ERR_KER_USR_TRANS (-4)
#define RPMSG_ERR_SEND_BUF_OVERSIZE (-5)
#define RPMSG_ERR_NO_MEM (-6)
#define RPMSG_ERR_TIMEOUT (-7)
#define RPMSG_ERR_SIGNAL_STOP (-8)
#define RPMSG_ERR_RECV_BUF_OVERFLOW (-9)
#define RPMSG_ERR_NOT_START_SERVER (-10)
#define RPMSG_ERR_CRC_CHECK (-11)
#define RPMSG_ERR_DRV_VERSION (-12)
#define RPMSG_ERR_UNKNOWN_ERR (-13)

核间通信 RPMSG(VDSP 侧) API

hb_rpmsg_start_server

【函数声明】

int32_t hb_rpmsg_start_server(const char* server_name, uint32_t flags, rl_ept_rx_cb_t rx_cb, void* rx_cb_data, uint32_t timeout, rpmsg_handle** handle);

【参数描述】

  • [IN] server_name:服务名称
  • [IN] flags:通信特性
  • [IN] rx_cb:接收报文的 callback 函数
  • [IN] rx_cb_data:回调参数
  • [IN] timeout:接收超时时间(单位:ms)
  • [OUT] handle:代表 rpmsg 通信句柄

用户可用的服务名称:

VDSP0

  • dcore0_acore_heart.
  • dcore0_rpmsg_bpu.
  • dcore0_rpmsg_op.

VDSP1

  • dcore1_acore_heart.
  • dcore1_rpmsg_bpu.
  • dcore1_rpmsg_op.
注意

S100上没有 VDSP1,使用时需要注意。

【说明】

Flags 参数的使用(使用位操作符):

  • RPMSG_F_BLOCK 阻塞式传输
  • RPMSG_F_NONBLOCK 非阻塞式传输
  • RPMSG_F_CRC_CHECK 支持 CRC check

【返回值】

【功能描述】

开启 rpmsg 通信服务

【示例代码】

#include <hb_rpmsg_interface.h>
#include <hb_vdsp_log.h>
#include <platform.h>

#ifdef CNFIG_VDSP0
static char test_server_name[] = "dcore0_rpmsg_op";
#elif CNFIG_VDSP1
static char test_server_name[] = "dcore1_rpmsg_op";
#endif
#define CORE_COM_TX_RX_PAYLOAD_SIZE (240)
#define MAX_PAYLAD (CORE_COM_TX_RX_PAYLOAD_SIZE)
static char buffer_rev[MAX_PAYLAD] = {0};
static char temp_hope[MAX_PAYLAD] ="I am test string,hope you can see me";

int32_t mycallback(void *payload, uint32_t payload_len, uint32_t src, void *priv)
{
printf("[vdsp0],mycallback test! payload is 0x%x ,payload_len is %d, \
src is %d\n",payload,payload_len,src);
memcpy((void*)buffer_rev, payload, payload_len);
}

int32_t rpmsg_test()
{
int32_t ret;
rpmsg_handle *handle = NULL;

//recv, block
ret = hb_rpmsg_start_server(test_server_name, RPMSG_F_BLOCK |
RPMSG_F_QUEUE_RECV, mycallback, NULL,0, &handle);
if(ret != 0) {
DSP_ERR("hb_rpmsg_start_server fail!,ret = %d\n", ret);
hb_rpmsg_stop_server(handle);
return -1;
}
DSP_INF("%s server start,(block/recv) way!!\n", test_server_name);
ret = hb_rpmsg_recv(handle, buffer_rev, MAX_PAYLAD);
if (ret < 0) {
DSP_ERR("hb_rpmsg_recv(block/recv) failed, ret = %d\n", ret);
hb_rpmsg_stop_server(handle);
return -1;
}
DSP_INF("hb_rpmsg_recv(block/recv), buffer_rev : %s\n", buffer_rev);
ret = hb_rpmsg_send(handle, buffer_rev, MAX_PAYLAD);
if (ret < 0) {
DSP_ERR("rpmsg_send error [[[%d]]]\n", ret);
hb_rpmsg_stop_server(handle);
return -1;
}
ret = hb_rpmsg_stop_server(handle);
if (ret < 0) {
DSP_ERR("server:%s stop error [[[%d]]]\n", test_server_name, ret);
return -1;
}
DSP_INF("%s server stop(block/recv)!!\n", test_server_name);
return 0;
}
hb_rpmsg_stop_server

【函数声明】

int32_t hb_rpmsg_stop_server(rpmsg_handle* handle);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄

【返回值】

【功能描述】

停止 rpmsg 通信服务

【示例代码】

参考 hb_rpmsg_start_server

hb_rpmsg_send

【函数声明】

int32_t hb_rpmsg_send(const rpmsg_handle* handle, char *buf, uint32_t len);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [IN] buf:要发送的数据地址
  • [IN] len:要发送的数据长度,可选值为: 1 ~ 240

【返回值】

【功能描述】

发送通信服务帧数据

【示例代码】

参考 hb_rpmsg_start_server

hb_rpmsg_recv

【函数声明】

int32_t hb_rpmsg_recv(rpmsg_handle* handle, char* buf, uint32_t len);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [OUT] buf:接收数据的地址
  • [IN] len:接收数据的长度,可选值为: 1 ~ 240

【返回值】

接收通信服务帧数据

【示例代码】

参考 hb_rpmsg_start_server

hb_rpmsg_send_timeout

【函数声明】

int32_t hb_rpmsg_send_timeout(const rpmsg_handle* handle, char* buf, uint32_t len, uint32_t timeout);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [IN] buf:要发送的数据地址
  • [IN] len:要发送的数据长度,可选值为: 1 ~ 240
  • [IN] timeout:发送阻塞时长

【返回值】

【功能描述】

发送通信服务帧数据,带有 timeout 参数

【示例代码】

#include <hb_rpmsg_interface.h>
#include <hb_vdsp_log.h>
#include <platform.h>

#ifdef CNFIG_VDSP0
static char test_server_name[] = "dcore0_rpmsg_op";
#elif CNFIG_VDSP1
static char test_server_name[] = "dcore1_rpmsg_op";
#endif
#define CORE_COM_TX_RX_PAYLOAD_SIZE (240)
#define MAX_PAYLAD (CORE_COM_TX_RX_PAYLOAD_SIZE)
static char buffer_rev[MAX_PAYLAD] = {0};
static char temp_hope[MAX_PAYLAD] ="I am test string,hope you can see me";

int32_t mycallback(void *payload, uint32_t payload_len, uint32_t src, void *priv)
{
printf("[vdsp0],mycallback test! payload is 0x%x ,payload_len is %d, \
src is %d\n",payload,payload_len,src);
memcpy((void*)buffer_rev, payload, payload_len);
}

int32_t rpmsg_test()
{
int32_t ret;
rpmsg_handle *handle = NULL;

//recv, block
ret = hb_rpmsg_start_server(test_server_name, RPMSG_F_BLOCK |
RPMSG_F_QUEUE_RECV, mycallback, NULL,0, &handle);
if(ret != 0) {
DSP_ERR("hb_rpmsg_start_server fail!,ret = %d\n", ret);
hb_rpmsg_stop_server(handle);
return -1;
}
DSP_INF("%s server start,(block/recv) way!!\n", test_server_name);
ret = hb_rpmsg_recv_timeout(handle, buffer_rev, MAX_PAYLAD, 1000u);
if (ret < 0) {
DSP_ERR("hb_rpmsg_recv(block/recv) failed, ret = %d\n", ret);
hb_rpmsg_stop_server(handle);
return -1;
}
DSP_INF("hb_rpmsg_recv(block/recv), buffer_rev : %s\n", buffer_rev);
ret = hb_rpmsg_send_timeout(handle, buffer_rev, MAX_PAYLAD, 1000u);
if (ret < 0) {
DSP_ERR("rpmsg_send error [[[%d]]]\n", ret);
hb_rpmsg_stop_server(handle);
return -1;
}
ret = hb_rpmsg_stop_server(handle);
if (ret < 0) {
DSP_ERR("server:%s stop error [[[%d]]]\n", test_server_name, ret);
return -1;
}
DSP_INF("%s server stop(block/recv)!!\n", test_server_name);
return 0;
}
hb_rpmsg_recv_timeout

【函数声明】

int32_t hb_rpmsg_recv_timeout(rpmsg_handle* handle, char* buf, uint32_t len, uint32_t timeout);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [OUT] buf:接收数据的地址
  • [IN] len:接收数据的长度,可选值为: 1 ~ 240
  • [IN] timeout:接收阻塞时间

【返回值】

【功能描述】

接收通信服务帧数据,带有 timeout 参数

【示例代码】

参考 hb_rpmsg_send_timeout

核间通信 RPMSG(Acore 侧) API

hb_rpmsg_connect_server

【函数声明】

int32_t hb_rpmsg_connect_server(char *server_name, int flags, int timeout, rpmsg_handle **handle);

【参数描述】

  • [IN] server_name:服务名称
  • [IN] flags:通信特性
  • [IN] timeout:阻塞模式下的超时时间(单位:ms)
  • [OUT] handle:返回 rpmsg 通信句柄

【说明】

Flags 参数的使用(使用位操作符):

  • RPMSG_F_BLOCK 阻塞式传输
  • RPMSG_F_NONBLOCK 非阻塞式传输
  • RPMSG_F_CRC_CHECK 支持 CRC check

【返回值】

【功能描述】

连接通讯服务

【示例代码】

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <rpmsg_lib.h>
void usage(char *s)
{
printf("usage: %s server_name timeout(ms)\n", s);
}
int main(int argc, char *argv[])
{
int ret = 0;
struct rpmsg_handle *handle = NULL;
char buffer[240] = {0};
int timeout = 0;
int cnt_max_connect = 10;
uint32_t major, minor, patch;
char *p;

if (argc != 3) {
usage(argv[0]);
return -1;
}
timeout = atoi(argv[2]);
if (timeout < 0) {
printf("invalid timeout: %d\n", timeout);
usage(argv[0]);
return -1;
}
ret = hb_rpmsg_get_version(&major, &minor, &patch);
if (ret < 0) {
printf("rpmsg get version failed\n");
return ret;
} else {
printf("rpmsg verion: %u.%u.%u\n", major, minor, patch);
}
reconnect:
ret = hb_rpmsg_connect_server(argv[1], RPMSG_F_BLOCK, timeout,
&handle);
if (ret < 0) {
--cnt_max_connect;
if ((ret == RPMSG_ERR_NOT_START_SERVER) && (cnt_max_connect)) {
printf("rpmsg_connect_server error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
usleep(100 * 1000);
goto reconnect;
} else {
printf("rpmsg_connect_server error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
return -1;
}
}
while (1) {
ret = hb_rpmsg_send(handle, buffer, 240);
if (ret < 0) {
printf("rpmsg_send error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
hb_rpmsg_disconnect_server(handle);
return -1;
} else
printf("recv log: %s\n", buffer);
ret = hb_rpmsg_recv(handle, buffer, 240);
if (ret < 0) {
printf("rpmsg_recv error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
hb_rpmsg_disconnect_server(handle);
return -1;
}
}
}
return 0;
}
hb_rpmsg_disconnect_server

【函数声明】

int32_t hb_rpmsg_disconnect_server(rpmsg_handle* handle);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄

【返回值】

【功能描述】

断开通信服务

【示例代码】

参考 hb_rpmsg_connect_server

hb_rpmsg_send

【函数声明】

int32_t hb_rpmsg_send(const rpmsg_handle* handle, char *buf, int32_t len);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [IN] buf:要发送的数据地址
  • [IN] len:要发送的数据长度,可选值为: 1 ~ 240

【返回值】

【功能描述】

发送特定通信服务帧数据

【示例代码】

参考 hb_rpmsg_connect_server

hb_rpmsg_recv

【函数声明】

int32_t hb_rpmsg_recv(rpmsg_handle* handle, char* buf, int32_t len);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [OUT] buf:接收数据的地址
  • [IN] len:接收数据的长度,可选值为: 1 ~ 240

【返回值】

【功能描述】

接收通信服务帧数据

hb_rpmsg_send_timeout

【函数声明】

int32_t hb_rpmsg_send_timeout(const rpmsg_handle* handle, char *buf, int32_t len, int32_t timeout);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [IN] buf:要发送的数据地址
  • [IN] len:要发送的数据长度,可选值为: 1 ~ 240
  • [IN] timeout:发送阻塞时长

【返回值】

【功能描述】

发送通信服务帧数据,带有 timeout 参数

【示例代码】

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <rpmsg_lib.h>
void usage(char *s)
{
printf("usage: %s server_name timeout(ms)\n", s);
}
int main(int argc, char *argv[])
{
int ret = 0;
struct rpmsg_handle *handle = NULL;
char buffer[240] = {0};
int timeout = 0;
int cnt_max_connect = 10;
if (argc != 3) {
usage(argv[0]);
return -1;
}
timeout = atoi(argv[2]);
if (timeout < 0) {
printf("invalid timeout: %d\n", timeout);
usage(argv[0]);
return -1;
}
reconnect:
ret = hb_rpmsg_connect_server(argv[1], RPMSG_F_BLOCK, timeout,
&handle);
if (ret < 0) {
--cnt_max_connect;
if ((ret == RPMSG_ERR_NOT_START_SERVER) && (cnt_max_connect)) {
printf("rpmsg_connect_server error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
usleep(100 * 1000);
goto reconnect;
} else {
printf("rpmsg_connect_server error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
return -1;
}
}
while (1) {
ret = hb_rpmsg_send_timeout(handle, buffer, 240, 1000);
if (ret < 0) {
printf("rpmsg_send error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
hb_rpmsg_disconnect_server(handle);
return -1;
} else
printf("recv log: %s\n", buffer);
ret = hb_rpmsg_recv_timeout(handle, buffer, 240, 1000);
if (ret < 0) {
printf("rpmsg_recv error[%d]: %s\n", ret,
hb_rpmsg_error_message(ret));
hb_rpmsg_disconnect_server(handle);
return -1;
}
}
}
return 0;
}
hb_rpmsg_recv_timeout

【函数声明】

int32_t hb_rpmsg_recv_timeout(rpmsg_handle* handle, char* buf, int32_t len, int32_t timeout);

【参数描述】

  • [IN] handle:代表 rpmsg 通信句柄
  • [OUT] buf:接收数据的地址
  • [IN] len:接收数据的长度,可选值为: 1 ~ 240
  • [IN] timeout:接收阻塞时间

【返回值】

【功能描述】

接收通信服务帧数据,带有 timeout 参数

【示例代码】

参考 hb_rpmsg_send_timeout

hb_rpmsg_get_version

【函数声明】

int32_t hb_rpmsg_get_version(uint32_t* major, uint32_t* minor, uint32_t* patch);

【参数描述】

  • [OUT] major:major 版本号
  • [OUT] minor:minor 版本号
  • [OUT] patch:patch

【返回值】

【功能描述】

获取 rpmsg 动态库版本号

【示例代码】

参考 hb_rpmsg_connect_server

hb_rpmsg_error_message

【函数声明】

const char* hb_rpmsg_error_message(int32_t error_code);

【参数描述】

  • [IN] error_code:错误码

【返回值】

  • 成功:错误描述字符串
  • 失败:NULL

【功能描述】

将错误码转为错误描述字符串

【示例代码】

参考 hb_rpmsg_connect_server

核间通信 IPCFHAL 接口

核间通信 IPCFHAL 头文件和链接库

  • VDSP 侧

    头文件:hb_ipcfhal_interface.h ipcf_hal_errno.h

    链接库:无

  • Acore 侧

    头文件:hb_ipcfhal_interface.h ipcf_hal_errno.h

    链接库:libhbipcfhal.so

核间通信 IPCFHAL API 返回值

#define IPCF_HAL_E_OK           0/**< General OK*/
#define IPCF_HAL_E_NOK 1/**< General Not OK*/
#define IPCF_HAL_E_CONFIG_FAIL 2/**< Config fail*/
#define IPCF_HAL_E_WRONG_CONFIGURATION 3/**< Wrong configuration*/
#define IPCF_HAL_E_NULL_POINTER 4/**< A null pointer was passed as an argument*/
#define IPCF_HAL_E_PARAM_INVALID 5/**< A parameter was invalid*/
#define IPCF_HAL_E_LENGTH_TOO_SMALL 6/**< Length too small*/
#define IPCF_HAL_E_INIT_FAILED 7/**< Initialization failed*/
#define IPCF_HAL_E_UNINIT 8/**< Called before initialization*/
#define IPCF_HAL_E_BUFFER_OVERFLOW 9/**< Source address or destination address Buffer overflow*/
#define IPCF_HAL_E_ALLOC_FAIL 10/**< Source alloc fail*/
#define IPCF_HAL_E_TIMEOUT 11/**< Expired the time out*/
#define IPCF_HAL_E_REINIT 12/**< Re initilize*/
#define IPCF_HAL_E_BUSY 13/**< Busy*/
#define IPCF_HAL_E_CHANNEL_INVALID 14/**< Channel is invalid*/

API 返回值为上述宏定义取负值。

核间通信 IPCFHAL(VDSP 侧) API

hb_ipcfhal_init

【函数声明】

int32_t hb_ipcfhal_init(ipcfhal_chan_t *channel);

【参数描述】

  • [IN] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 初始化

【IPCFHAL 初始化 示例代码】

信息

完整代码编译和运行参考/app/vdsp_demo/vdsp_ipcfhal_sample/READTME.md

#define CFG_FILE "config.json"
#define DATA_LEN 1024

static int32_t ipcfhal_thread_func(void *arg, int32_t unused) {
int32_t ret;
char *ch_name = "cpu-vdsp-ins0ch0";
ipcfhal_chan_t channel;
uint8_t rx_data[DATA_LEN] = {0};
uint8_t tx_data[DATA_LEN] = {0};
int32_t timeout = -1;
uint32_t major, minor, patch;

ret = vdsp_server_ready(BOOT_COMPLETE_EVENT_MASK_BIT1 | BOOT_COMPLETE_EVENT_MASK_BIT2);

ret = hb_ipcfhal_get_version(&major, &minor, &patch);
if (ret < 0) {
return ret;
} else {
printf("ipcfhal verion: %u.%u.%u\n", major, minor, patch);
}

ret = hb_ipcfhal_getchan_byjson(ch_name, &channel, CFG_FILE);
if (ret < 0)
return ret;
ret = hb_ipcfhal_init(&channel);
if (ret < 0)
return ret;
ret = hb_ipcfhal_config(&channel);
if (ret < 0)
return ret;

while (hb_is_thread_stop() != 1) {
memset(tx_data, 0x55, DATA_LEN);
ret = hb_ipcfhal_send(tx_data, DATA_LEN, &channel);
if (ret < 0)
continue;

memset((void *)rx_data, 0, DATA_LEN);
ret = hb_ipcfhal_recv(rx_data, DATA_LEN, timeout, &channel);
if (ret < 0)
continue;
for (int i = 0; i < DATA_LEN; i++) {
if (rx_data[i] != 0x55) {
DSP_ERR("recv rx_data[%d] err. 0x%x\n", i, rx_data[i]);
break;
}
}
xos_thread_sleep_msec(2);
}

return ret;
}
hb_ipcfhal_getchan_byjson

【函数声明】

int32_t hb_ipcfhal_getchan_byjson(const char *name, ipcfhal_chan_t *channel, const char *json_file);

【参数描述】

  • [IN] name:ipcfhal channel 名字
  • [IN] json_file:ipcfhal json 配置文件
  • [OUT] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 从 json 配置文件获取 channel

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_config

【函数声明】

int32_t hb_ipcfhal_config(ipcfhal_chan_t *channel);

【参数描述】

  • [IN] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 配置 channel

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_send

【函数声明】

int32_t hb_ipcfhal_send(const uint8_t *data, int16_t length, ipcfhal_chan_t *channel);

【参数描述】

  • [IN] data:发送的数据 buffer
  • [IN] length:发送的 buffer 长度
  • [IN] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 发送消息

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_recv

【函数声明】

int32_t hb_ipcfhal_recv(uint8_t *data, int16_t length, int32_t timeout, ipcfhal_chan_t *channel);

【参数描述】

  • [IN] length:buffer 最大长度
  • [IN] timeout:0 非阻塞, >0 阻塞超时 ms, -1 阻塞.
  • [IN] channel:ipcfhal channel 信息
  • [OUT] data:接收的数据 buffer

【返回值】

【功能描述】

IPCFHAL 接收消息

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_deinit

【函数声明】

int32_t hb_ipcfhal_deinit(ipcfhal_chan_t *channel);

【参数描述】

  • [IN] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 反初始化

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_trans_err

【函数声明】

int32_t hb_ipcfhal_trans_err(int32_t err_code, char **err_str);

【参数描述】

  • [IN] err_code:返回值错误编码
  • [OUT] err_str:转换错误字符串

【返回值】

【功能描述】

转换 IPCFHAL 错误码为错误字符串

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_get_version

【函数声明】

int32_t hb_ipcfhal_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch);

【参数描述】

  • [OUT] major:libhbipcfhal major 版本号
  • [OUT] minor:libhbipcfhal minor 版本号
  • [OUT] patch:libhbipcfhal patch 版本号

【返回值】

【功能描述】

获取 IPCFHAL 库的版本号

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_register_callback

【函数声明】

int32_t hb_ipcfhal_register_callback(uint8_t *user_data, user_cb_t user_cb, ipcfhal_chan_t *channel);

【参数描述】

  • [IN] user_data:用户数据
  • [IN] user_cb:用户回调函数
  • [IN] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 注册回调函数,VDSP 侧接收方式配置为回调方式;参数为 NULL 时,注销回调函数,VDSP 侧接收方式配置为 recv 方式;同一实例多通道使用 callback 时会阻塞执行,不同实例通道间 callback 会并发执行。

【IPCFHAL 注册回调函数 示例代码】

static void test_ipcfhal_callback_func(uint8_t *userdata, int32_t instance, int32_t chan_id,
uint8_t *buf, uint64_t size)
{
printf("userdata[0x%llx] ins[%d] ch[%d] buf[0x%llx] size[%llu]\n",
(uint64_t)userdata, instance, chan_id, (uint64_t)buf, size);
*userdata = 1u;
}

static int32_t hb_libipchal_register_callback_func(void)
{
int32_t ret = 0;
uint8_t user_flag = 0;
uint32_t cnt = 100u;
uint8_t rx_data[1024] = {0};
ipcfhal_chan_t channel;

printf("hb_libipchal_register_callback_func start\n");
(void)hb_ipcfhal_getchan_byjson("cpu-vdsp-ch0", &channel, "ipcf_config.json");
(void)hb_ipcfhal_init(&channel);
(void)hb_ipcfhal_config(&channel);
(void)hb_ipcfhal_register_callback(&user_flag, test_ipcfhal_callback_func, &channel);

while ((user_flag == 0) && (cnt--) ) {
xos_thread_sleep_msec(100);
}

(void)hb_ipcfhal_register_callback(NULL, NULL, &channel);
(void)hb_ipcfhal_deinit(&channel);
printf("hb_libipchal_register_callback_func end\n");

return 0;
}

核间通信 IPCFHAL(Acore 侧) API

hb_ipcfhal_init

【函数声明】

int32_t hb_ipcfhal_init(ipcfhal_chan_t *channel);

【参数描述】

  • [IN] channel:ipcfhal 通道

【返回值】

【功能描述】

IPCFHAL 初始化

【IPCFHAL 初始化 示例代码】

信息

完整代码编译和运行参考/app/vdsp_demo/vdsp_ipcfhal_sample/READTME.md

{
"log_level": 0,
"config_num": 2,
"config_num_max":256,
"config_0": {
"name": "cpu2vdsp_ins22ch0",
"instance": 22,
"channel": 0,
"pkg_size_max": 4096,
"fifo_size": 64000,
"fifo_type": 0,
"ipcf_dev_path":"/dev/ipcdrv",
"ipcf_dev_name":"ipcdrv"
},
"config_1": {
"name": "cpu2vdsp_ins22ch1",
"instance": 22,
"channel": 1,
"pkg_size_max": 4096,
"fifo_size": 64000,
"fifo_type": 0,
"ipcf_dev_path":"/dev/ipcdrv",
"ipcf_dev_name":"ipcdrv"
}
}

/*************************************************************************
* COPYRIGHT NOTICE
* Copyright 2022-2024, D-Robotics Co., Ltd.
* All rights reserved.
*************************************************************************/

/******************************************************************************/
/*----------------------------------Includes----------------------------------*/
/******************************************************************************/
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <logging.h>
#include <hb_vdsp_mgr.h>
#include "hb_ipcf_hal.h"
#include "ipcf_hal_errno.h"
/******************************************************************************/
/*-----------------------------------Macros-----------------------------------*/
/******************************************************************************/
#define SMP_1KB_LEN (1024)/**< sample send and recv data length*/
#define SMP_TIMEOUT_VAL (1000)/**< sample recv timeout value*/
#define SMP_USLEEP_1MS (1000u)/**< sample sleep 1ms*/
#define SMP_USLEEP_10MS (10000u)/**< sample sleep 10ms*/
#define SMP_SLEEP_1S (1u)/**< sample sleep 1s*/
#define SMP_RUN_TIME (10l)/**< sample run time, unit: s*/
#define SMP_100CNT (100u)/**< sample 100 counts*/
#define SMP_CHAN_NUM (1)/**< sample channel number*/
#define SMP_THREAD_NUM (SMP_CHAN_NUM * 2)/**< sample thread number*/
#define SMP_ROLL_POS (4)/**< sample rolling count position*/
#define SMP_CFG_FILE "/app/vdsp_demo/vdsp_ipcfhal_sample/ipcfhal_config.json"

/******************************************************************************/
/*-------------------------Function Prototypes--------------------------------*/
/******************************************************************************/
void *send_pthread(void *argv);
void *recv_pthread(void *argv);

/******************************************************************************/
/*---------------------------- Local Function -------------------------------*/
/******************************************************************************/
/*thread arg*/
struct th_arg_t {
ipcfhal_chan_t ch;/**< ipcfhal channel*/
bool Is_Enable;/**< thread status*/
uint32_t data_len;/**< send data length*/
uint32_t sleep_time;/**< send period*/
bool result;/**< thread running result*/
};

volatile sig_atomic_t g_running = true;

void signal_handler(int sig) {
if (sig == SIGINT)
g_running = false;
}

static void *tx_pthread(void *argv)
{
int32_t ret = 0;
struct th_arg_t *pth_arg = (struct th_arg_t *)argv;
uint8_t tx_data[SMP_1KB_LEN] = {0u};

if (pth_arg == NULL) {
return NULL;
}

while (pth_arg->Is_Enable) {
memset(tx_data, 0x55, SMP_1KB_LEN);
ret = hb_ipcfhal_send(tx_data, pth_arg->data_len, &pth_arg->ch);
if (ret != (int32_t)pth_arg->data_len) {/*return data_len*/
pr_err("Ins[%u]Ch[%u] send failed\n",
pth_arg->ch.instance, pth_arg->ch.id);
pth_arg->result = false;
}
usleep(pth_arg->sleep_time);
}

pr_info("[%s End]Ins[%u]Ch[%u]\n",
__func__, pth_arg->ch.instance, pth_arg->ch.id);

return NULL;
}

static void *rx_pthread(void *argv)
{
int32_t ret = 0;
struct th_arg_t *pth_arg = (struct th_arg_t *)argv;
uint8_t data[SMP_1KB_LEN] = {0u};
int32_t timeout = SMP_TIMEOUT_VAL;

if (pth_arg == NULL) {
return NULL;
}

while (pth_arg->Is_Enable) {
memset(data, 0u, pth_arg->data_len);
ret = hb_ipcfhal_recv(data, SMP_1KB_LEN, timeout, &pth_arg->ch);
if (ret != -IPCF_HAL_E_TIMEOUT && ret < 0) {
pr_err("Ins[%u]Ch[%u] recv failed: %d\n",
pth_arg->ch.instance, pth_arg->ch.id, ret);
pth_arg->Is_Enable = false;
pth_arg->result = false;
break;
}

if (ret >= 0) {
for (int i = 0; i < SMP_1KB_LEN; i++) {
if (data[i] != 0x55) {
pr_err("recv data[%d] err. 0x%x\n", i, data[i]);
break;
}
}
} else if (ret == -IPCF_HAL_E_TIMEOUT) {
pth_arg->result = false;
}

usleep(pth_arg->sleep_time);
}
pr_info("[%s End]Ins[%u]Ch[%u]\n",
__func__, pth_arg->ch.instance, pth_arg->ch.id);

return NULL;
}

/******************************************************************************/
/*---------------------------- Global Function -------------------------------*/
/******************************************************************************/
int main(int argc, char *argv[])
{
const char *json_path = SMP_CFG_FILE;
struct timespec test_start_time, test_cur_time;
int32_t ret = 0;
bool result = true;
pthread_t thread_tid[SMP_THREAD_NUM];
struct th_arg_t thread_arg[SMP_CHAN_NUM];
const char *chan_name[SMP_CHAN_NUM] = {
"cpu2vdsp_ins22ch0"
};
int32_t vdsp_id = 0;
int32_t timeout = 1000;
const char *vdsp_pathname = "/app/vdsp_demo/vdsp_ipcfhal_sample/res/q8sample";

signal(SIGINT, signal_handler);

ret = hb_vdsp_init(vdsp_id);
if (ret) {
pr_err("vdsp init failed, ret %d\n", ret);
return ret;
}

ret = hb_vdsp_start(vdsp_id, timeout, vdsp_pathname);
if (ret) {
pr_err("vdsp start failed, ret %d\n", ret);
return ret;
}

for (int32_t i = 0; i < SMP_CHAN_NUM; i++) {
/*step1: get channel info by json file*/
ret = hb_ipcfhal_getchan_byjson(chan_name[i], &thread_arg[i].ch, json_path);
if (ret < 0) {/* return channel id*/
pr_err("%s not found, ret %d\n", chan_name[i], ret);
hb_vdsp_stop(vdsp_id);
hb_vdsp_deinit(vdsp_id);
return ret;
}

/*step2: init channel*/
ret = hb_ipcfhal_init(&thread_arg[i].ch);
if (ret < 0) {
pr_err("%s init failed, ret %d\n", thread_arg[i].ch.name, ret);
for (int32_t j = 0; j < i; j++) {
hb_ipcfhal_deinit(&thread_arg[j].ch);
}
hb_vdsp_stop(vdsp_id);
hb_vdsp_deinit(vdsp_id);
return ret;
}

/*step3: config channel*/
ret = hb_ipcfhal_config(&thread_arg[i].ch);
if (ret < 0) {
pr_err("%s config failed, ret %d\n", thread_arg[i].ch.name, ret);
for (int32_t j = 0; j <= i; j++) {
hb_ipcfhal_deinit(&thread_arg[j].ch);
}
hb_vdsp_stop(vdsp_id);
hb_vdsp_deinit(vdsp_id);
return ret;
}

/*step4: set thread arg*/
thread_arg[i].sleep_time = SMP_USLEEP_10MS;
thread_arg[i].data_len = SMP_1KB_LEN;
thread_arg[i].Is_Enable = true;
thread_arg[i].result = true;
(void)pthread_create(&thread_tid[i], NULL, rx_pthread, &thread_arg[i]);
(void)pthread_create(&thread_tid[i+1], NULL, tx_pthread, &thread_arg[i]);
}

/*step5: run send and recv thread*/
clock_gettime(CLOCK_MONOTONIC, &test_start_time);
clock_gettime(CLOCK_MONOTONIC, &test_cur_time);
while (g_running) {
for (int32_t i = 0; i < SMP_CHAN_NUM; i++) {
if (thread_arg[i].Is_Enable == false)
break;
}
if (test_cur_time.tv_sec - test_start_time.tv_sec > SMP_RUN_TIME)
break;

sleep(SMP_SLEEP_1S);
clock_gettime(CLOCK_MONOTONIC, &test_cur_time);
}

/*step6: deinit channel*/
for (int32_t i = 0; i < SMP_CHAN_NUM; i++) {
void *ret;
thread_arg[i].Is_Enable = false;//stop thread
usleep(SMP_USLEEP_10MS);
pthread_join(thread_tid[i], &ret);
pthread_join(thread_tid[i+1], &ret);
(void)hb_ipcfhal_deinit(&thread_arg[i].ch);

result = (result && thread_arg[i].result);
}

if (result == false) {
pr_err("[ipc-sample] ipc sample running failed\n");
} else {
pr_info("[ipc-sample] ipc sample running success\n");
}

ret = hb_vdsp_stop(vdsp_id);
if (ret) {
pr_err("vdsp stop failed, ret %d\n", ret);
return ret;
}

ret = hb_vdsp_deinit(vdsp_id);
if (ret) {
pr_err("vdsp deinit failed, ret %d\n", ret);
return ret;
}
return ret;
}
hb_ipcfhal_getchan_byjson

【函数声明】

int32_t hb_ipcfhal_getchan_byjson(const char *name, ipcfhal_chan_t *channel, const char *json_file);

【参数描述】

  • [IN] name:ipcfhal channel 名字
  • [IN] json_file:ipcfhal json 配置文件
  • [OUT] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 从 json 配置文件获取 channel,channel 初始化后,不支持用户修改结构体成员值。

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_config

【函数声明】

int32_t hb_ipcfhal_config(ipcfhal_chan_t *channel);

【参数描述】

  • [IN] channel:ipcfhal channel 信息

【返回值】

【功能描述】

IPCFHAL 配置 channel

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_send

【函数声明】

int32_t hb_ipcfhal_send(const uint8_t *data, uint32_t length, ipcfhal_chan_t *channel);

【参数描述】

  • [IN] data:发送的数据 buffer
  • [IN] length:发送的 buffer 长度
  • [IN] channel:channel 信息

【返回值】

【功能描述】

IPCFHAL 发送消息

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_recv

【函数声明】

int32_t hb_ipcfhal_recv(uint8_t *data, uint32_t length, int32_t timeout, ipcfhal_chan_t *channel);

【参数描述】

  • [IN] length:buffer 最大长度
  • [IN] timeout:0 非阻塞, >0 阻塞超时 ms, -1 阻塞
  • [IN] channel:channel 信息
  • [OUT] data:接收的数据 buffer

【返回值】

【功能描述】

IPCFHAL 接收消息

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_deinit

【函数声明】

int32_t hb_ipcfhal_deinit(ipcfhal_chan_t *channel);

【参数描述】

  • [IN] channel:channel 信息

【返回值】

【功能描述】

IPCFHAL 反初始化

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_trans_err

【函数声明】

int32_t hb_ipcfhal_trans_err(int32_t err_code, char **err_str);

【参数描述】

  • [IN] err_code:返回值错误编码
  • [OUT] err_str:转换错误字符串

【返回值】

【功能描述】

转换 IPCFHAL 错误码为错误字符串

【示例代码】

参考 hb_ipcfhal_init

hb_ipcfhal_get_version

【函数声明】

int32_t hb_ipcfhal_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch);

【参数描述】

  • [OUT] major:libhbipcfhal major 版本号
  • [OUT] minor:libhbipcfhal minor 版本号
  • [OUT] patch:libhbipcfhal patch 版本号

【返回值】

【功能描述】

获取 IPCFHAL 库的版本号

【示例代码】

参考 hb_ipcfhal_init

HEAP 分配接口

HEAP 分配 头文件和链接库

  • VDSP 侧

    头文件:hb_mem_allocator.h

    链接库:无

HEAP 分配 API 返回值

#define HB_MEM_OK                                    0
#define HB_MEM_ERR_INVALID_PARAMS (-16777215)
#define HB_MEM_ERR_REPEAT_INIT (-16777214)
#define HB_MEM_ERR_HEAP_BUSY (-16777213)
#define HB_MEM_ERR_INSUFFICIENT_MEM (-16777212)

HEAP 分配 API

hb_mem_heap_initialize

【函数声明】

int32_t hb_mem_heap_initialize(hb_mem_heap_t heap_id, void *start_vaddr, size_t heap_size, uint32_t align);

【参数描述】

  • [IN] heap_id:有效的 heap 序号,可选值: 0, 1
  • [IN] start_vaddr:heap 起始地址
  • [IN] heap_size:heap 大小
  • [IN] align:heap 空间对齐的大小

【返回值】

【功能描述】

初始化 heap 分配接口

【示例代码】

int32_t ret;
void *start_vaddr[2] = {(void *)heapDRAM0, heapDRAM1};
size_t heap_size[2] = {sizeof(heapDRAM0), sizeof(heapDRAM1)};
ret = hb_mem_heap_initialize(HB_MEMRY_HEAP_DRAM_0, start_vaddr[0],
heap_size[0], 0x10000);
if (ret < 0) {
printf("hb_mem_heap_initialize failed ret = %d\n", ret);
return ret;
}
ret = hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
if (ret < 0) {
printf("hb_mem_heap_deinitialize failed ret = %d\n", ret);
return ret;
}
return 0;
hb_mem_heap_deinitialize

【函数声明】

int32_t hb_mem_heap_deinitialize(hb_mem_heap_t heap_id);

【参数描述】

  • [IN] heap_id:有效的 heap 序号,可选值: 0, 1

【返回值】

【功能描述】

解初始化 heap 分配接口

【示例代码】

int32_t ret;
void *start_vaddr[2] = {(void *)heapDRAM0, heapDRAM1};
size_t heap_size[2] = {sizeof(heapDRAM0), sizeof(heapDRAM1)};
ret = hb_mem_heap_initialize(HB_MEMRY_HEAP_DRAM_0, start_vaddr[0],
heap_size[0], 0x10000);
if (ret < 0) {
printf("hb_mem_heap_initialize failed ret = %d\n", ret);
return ret;
}
ret = hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
if (ret < 0) {
printf("hb_mem_heap_deinitialize failed ret = %d\n", ret);
return ret;
}
return 0;
hb_mem_heap_alloc

【函数声明】

int32_t hb_mem_heap_alloc(hb_mem_heap_t heap_id, size_t req_size, uint32_t align, void ** vaddr);

【参数描述】

  • [IN] heap_id:有效的 heap 序号,可选值: 0, 1
  • [IN] reg_size:分配的字节大小
  • [IN] align:heap 空间对齐的大小
  • [OUT] vaddr:分配 buffer 的起始地址

【返回值】

【功能描述】

heap 分配接口

【示例代码】

int32_t ret;
void *start_vaddr[2] = {(void *)heapDRAM0, heapDRAM1};
size_t heap_size[2] = {sizeof(heapDRAM0), sizeof(heapDRAM1)};
uint32_t malloc_alignment = 64;
void *vaddr = NULL;
ret = hb_mem_heap_initialize(HB_MEMRY_HEAP_DRAM_0, start_vaddr[0],
heap_size[0], 0x10000);
if (ret < 0) {
printf("hb_mem_heap_initialize failed ret = %d\n", ret);
return ret;
}
ret = hb_mem_heap_alloc(HB_MEMRY_HEAP_DRAM_0, heap_size[0],
malloc_alignment, &vaddr);
if (ret < 0) {
printf("hb_mem_heap_alloc failed ret = %d\n", ret);
hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
return ret;
}
ret = hb_mem_heap_free(HB_MEMRY_HEAP_DRAM_0, vaddr);
if (ret < 0) {
printf("hb_mem_heap_free failed ret = %d\n", ret);
hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
return ret;
}
ret = hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
return 0;
hb_mem_heap_free

【函数声明】

int32_t hb_mem_heap_free(hb_mem_heap_t heap_id, void *vaddr);

【参数描述】

  • [IN] heap_id:有效的 heap 序号,可选值: 0, 1
  • [IN] vaddr:已分配 buffer 的起始地址

【返回值】

【功能描述】

释放 heap 已分配的空间

【示例代码】

int32_t ret;
void *start_vaddr[2] = {(void *)heapDRAM0, heapDRAM1};
size_t heap_size[2] = {sizeof(heapDRAM0), sizeof(heapDRAM1)};
uint32_t malloc_alignment = 64;
void *vaddr = NULL;
ret = hb_mem_heap_initialize(HB_MEMRY_HEAP_DRAM_0, start_vaddr[0],
heap_size[0], 0x10000);
if (ret < 0) {
printf("hb_mem_heap_initialize failed ret = %d\n", ret);
return ret;
}
ret = hb_mem_heap_alloc(HB_MEMRY_HEAP_DRAM_0, heap_size[0],
malloc_alignment, &vaddr);
if (ret < 0) {
printf("hb_mem_heap_alloc failed ret = %d\n", ret);
hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
return ret;
}
ret = hb_mem_heap_free(HB_MEMRY_HEAP_DRAM_0, vaddr);
if (ret < 0) {
printf("hb_mem_heap_free failed ret = %d\n", ret);
hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
return ret;
}
ret = hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
if (ret < 0) {
printf("hb_mem_heap_deinitialize failed ret = %d\n", ret);
return ret;
}
return 0;
hb_mem_heap_get_status

【函数声明】

int32_t hb_mem_heap_get_status(hb_mem_heap_t heap_id, hb_mem_heap_status_t *heap_status);

【参数描述】

  • [IN] heap_id:有效的 heap 序号,可选值: 0, 1
  • [OUT] heap_status:heap 状态信息

【返回值】

【功能描述】

获取 heap 分配状态

【示例代码】

int32_t ret;
void *start_vaddr[2] = {(void *)heapDRAM0, heapDRAM1};
size_t heap_size[2] = {sizeof(heapDRAM0), sizeof(heapDRAM1)};
hb_mem_heap_status_t heap_status = {0, };
ret = hb_mem_heap_initialize(HB_MEMRY_HEAP_DRAM_0, start_vaddr[0],
heap_size[0], 0x10000);
if (ret < 0) {
printf("hb_mem_heap_initialize failed ret = %d\n", ret);
return ret;
}
ret = hb_mem_heap_get_status(HB_MEMRY_HEAP_DRAM_0, &heap_status);
if (ret < 0) {
printf("hb_mem_heap_get_status failed ret = %d\n", ret);
hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
return ret;
}
ret = hb_mem_heap_deinitialize(HB_MEMRY_HEAP_DRAM_0);
if (ret < 0) {
printf("hb_mem_heap_deinitialize failed ret = %d\n", ret);
return ret;
}
return 0;

VDSP 启停控制接口

VDSP 启停控制 头文件和链接库

  • Acore 侧

    头文件:hb_vdsp_mgr.h

    链接库:libvdsp.so

VDSP 启停控制 API 返回值

#define HB_VDSP_OK                      (0)
#define HB_VDSP_OPEN_DEV_ERR (-1)
#define HB_VDSP_START_IOCTL_ERR (-2)
#define HB_VDSP_STOP_IOCTL_ERR (-4)
#define HB_VDSP_GET_STATUS_IOCTL_ERR (-5)
#define HB_VDSP_RESET_IOCTL_ERR (-6)
#define HB_VDSP_PARAM_INVALID (-7)
#define HB_VDSP_SET_PATH_ERR (-8)
#define HB_VDSP_SET_NAME_ERR (-9)
#define HB_VDSP_CLOSE_DEV_ERR (-10)
#define HB_VDSP_ERR_IOC_GET_VERSION_INFO (-11)
#define HB_VDSP_ERR_IOC_MEM_ALLOC (-12)
#define HB_VDSP_ERR_IOC_MEM_FREE (-13)
#define HB_VDSP_ERR_IOC_SMMU_MAP (-14)
#define HB_VDSP_ERR_IOC_SMMU_UNMAP (-15)
#define HB_VDSP_ERR_INIT (-16)
#define HB_VDSP_ERR_DEINIT (-17)
#define HB_VDSP_ERR_NOT_INITED (-18)
#define HB_VDSP_ERR_ALLOC_COM_BUF (-19)
#define HB_VDSP_ERR_FREE_MEMBUF (-20)
#define HB_VDSP_ERR_GET_COM_BUF_WITH_VADDR (-21)
#define HB_VDSP_ERR_SMMU_MAP (-22)
#define HB_VDSP_ERR_SMMU_UNMAP (-23)
#define HB_VDSP_ERR_CHECK_VERSION (-24)
#define HB_VDSP_ERR_INSUFFICIENT_MEM (-25)
#define HB_VDSP_ERR_MISMATCH_INTERFACE (-26)
#define HB_VDSP_ERR_RBTREE_CREATE_NODE (-27)
#define HB_VDSP_ERR_RBTREE_INSERT_NODE (-28)
#define HB_VDSP_ERR_RBTREE_SEARCH_NODE (-29)

VDSP 启停控制 API

hb_vdsp_get_version

【函数声明】

int32_t hb_vdsp_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch);

【参数描述】

  • [OUT] major:libvdsp major 版本号
  • [OUT] minor:libvdsp minor 版本号
  • [OUT] patch:libvdsp patch 版本号

【返回值】

【功能描述】

获取 VDSP 启停控制库的版本号

【获取 VDSP 库版本号 示例代码】

#include <hb_vdsp_mgr.h>

int32_t boot_lib_version_test(int32_t dsp_id)
{
int32_t ret = 0;
uint32_t major, minor, patch;

ret = hb_vdsp_get_version(&major, &minor, &patch);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_get_version ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

printf("%s Get version %d.%d.%d.\n", TAG, major, minor, patch);

return ret;
}
hb_vdsp_init

【函数声明】

int32_t hb_vdsp_init(int32_t dsp_id);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1

【返回值】

【功能描述】

初始化 VDSP 启停控制库

【初始化 VDSP 库 示例代码】

#include <hb_vdsp_mgr.h>

int32_t boot_lib_init_test(int32_t dsp_id)
{
int32_t ret = 0;

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

return ret;
}
hb_vdsp_deinit

【函数声明】

int32_t hb_vdsp_deinit(int32_t dsp_id);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1

【返回值】

【功能描述】

释放 VDSP 启停控制库

【示例代码】

参考 hb_vdsp_init

hb_vdsp_start

【函数声明】

int32_t hb_vdsp_start(int32_t dsp_id, int32_t timeout, const char *pathname);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] timeout:0为异步; -1为同步; 大于0为同步 timeout 等待时间,ms 单位
  • [IN] pathname:vdsp 镜像完整路径,包含镜像名字

【返回值】

【功能描述】

启动 VDSP

【同步启动 VDSP 示例代码】

#include <hb_vdsp_mgr.h>

#define VDSP_BOOT_MODE_ASYNC (0)
#define VDSP_BOOT_MODE_SYNC (-1)

int32_t boot_lib_sync_test(int32_t dsp_id, const char* pathname)
{
int32_t ret = 0;
int32_t status = 0;

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_vdsp_start(dsp_id, VDSP_BOOT_MODE_SYNC, pathname);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_start ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_get_status(dsp_id, &status);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_get_status ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_stop(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_stop ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

printf("%s dsp%d boot sync pass !\n", __func__, dsp_id);
return ret;
}

boot_lib_sync_test(0, "/app/vdsp_demo/vdsp_sample/res/q8sample");
boot_lib_sync_test(1, "/app/vdsp_demo/vdsp_sample/res/q8sample");

【异步启动 VDSP 示例代码】

#include <hb_vdsp_mgr.h>
#include <poll.h>

#define VDSP_BOOT_MODE_ASYNC (0)
#define VDSP_BOOT_MODE_SYNC (-1)

int32_t boot_lib_async_test(int32_t dsp_id, const char* pathname)
{
int32_t ret = 0;
struct pollfd pollfds;
int32_t fd;

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_vdsp_start(dsp_id, VDSP_BOOT_MODE_ASYNC, pathname);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_start ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_get_fd(dsp_id, &fd);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_get_fd ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

pollfds.fd = fd;
pollfds.events = POLLIN | POLLRDNORM;

printf("%s dsp%d poll entry pollfds.events=0x%x fd-%d.\n", __func__, dsp_id, pollfds.events, fd);

ret = poll(&pollfds, 1, 600);
printf("%s dsp%d poll return pollfds.revents=0x%x ret-%d.\n", __func__, dsp_id, pollfds.revents, ret);
if (ret <= 0) {
printf("%s dev poll err: %d, %s\n", __func__, errno, strerror(errno));
hb_vdsp_close_fd(fd);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_close_fd(fd);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_close_fd ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_stop(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_stop ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

printf("%s dsp%d boot async pass !\n", __func__, dsp_id);
return ret;
}

boot_lib_async_test(0, "/app/vdsp_demo/vdsp_sample/res/q8sample");
boot_lib_async_test(1, "/app/vdsp_demo/vdsp_sample/res/q8sample");
hb_vdsp_stop

【函数声明】

int32_t hb_vdsp_stop(int32_t dsp_id);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1

【返回值】

【功能描述】

停止 VDSP

【示例代码】

参考 hb_vdsp_start

hb_vdsp_get_status

【函数声明】

int32_t hb_vdsp_get_status(int32_t dsp_id, int32_t *status);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [OUT] status:VDSP 运行状态

【返回值】

【功能描述】

获取 VDSP 运行状态

::: tip 该接口不支持多进程、多线程并发使用。 :::

【示例代码】

参考 hb_vdsp_start

hb_vdsp_reset

【函数声明】

int32_t hb_vdsp_reset(int32_t dsp_id);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1

【返回值】

【功能描述】

复位 VDSP

【示例代码】

#include <hb_vdsp_mgr.h>

#define VDSP_BOOT_MODE_ASYNC (0)
#define VDSP_BOOT_MODE_SYNC (-1)

int32_t boot_lib_reset_test(int32_t dsp_id, const char* pathname)
{
int32_t ret = 0;
int32_t status = RPROC_OFFLINE;

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_vdsp_start(dsp_id, VDSP_BOOT_MODE_SYNC, pathname);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_start ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_get_status(dsp_id, &status);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_get_status ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_reset(dsp_id);
if (ret != HB_VDSP_OK) {
ALOGE("%s dsp%d hb_vdsp_reset ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_stop(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_stop ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

printf("%s dsp%d reset pass !\n", __func__, dsp_id);
return ret;
}

boot_lib_reset_test(0, "/app/vdsp_demo/vdsp_sample/res/q8sample");
boot_lib_reset_test(1, "/app/vdsp_demo/vdsp_sample/res/q8sample");
hb_vdsp_set_path

【函数声明】

int32_t hb_vdsp_set_path(int32_t dsp_id, const char* path);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] path:vdsp 镜像完整路径,不包含镜像名字

【返回值】

【功能描述】

设置 VDSP 镜像路径

【示例代码】

#include <hb_vdsp_mgr.h>

#define VDSP_BOOT_MODE_ASYNC (0)
#define VDSP_BOOT_MODE_SYNC (-1)

int32_t boot_lib_set_pathname_test(int32_t dsp_id, const char* path, const char* name)
{
int32_t ret = 0;
int32_t status = 0;

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_vdsp_set_path(dsp_id, path);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_set_path ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_set_name(dsp_id, name);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_set_name ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_start(dsp_id, VDSP_BOOT_MODE_SYNC, NULL);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_start ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_get_status(dsp_id, &status);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_get_status ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_stop(dsp_id);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_stop(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_stop ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

printf("%s dsp%d set path & name pass !\n", __func__, dsp_id);
return ret;
}

boot_lib_set_pathname_test(0, "/app/testcase/S05_VDSP/testsuite", "q8sample");
boot_lib_set_pathname_test(1, "/app/testcase/S05_VDSP/testsuite", "q8sample");
hb_vdsp_set_name

【函数声明】

int32_t hb_vdsp_set_name(int32_t dsp_id, const char* name);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] name:vdsp 镜像名字

【返回值】

【功能描述】

设置 VDSP 镜像名字

【示例代码】

参考 hb_vdsp_set_path

hb_vdsp_get_fd

【函数声明】

int32_t hb_vdsp_get_fd(int32_t dsp_id, int32_t *retfd);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [OUT] retfd:vdsp 设备 fd 句柄

【返回值】

【功能描述】

打开并返回 VDSP 设备 fd 句柄

【示例代码】

参考 hb_vdsp_start

hb_vdsp_close_fd

【函数声明】

int32_t hb_vdsp_close_fd(int32_t fd);

【参数描述】

  • [IN] fd:vdsp 设备 fd 句柄

【返回值】

【功能描述】

关闭 VDSP 设备

【示例代码】

参考 hb_vdsp_start

hb_vdsp_mem_alloc

【函数声明】

int32_t hb_vdsp_mem_alloc(int32_t dsp_id, uint64_t size, int64_t flags, uint64_t *va, uint64_t *iova);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] size:申请的内存大小
  • [IN] flags:使用 libhbmem 申请内存的 flags
  • [OUT] va:使用 libhbmem 申请内存的虚拟地址
  • [OUT] iova:映射成的 vdsp 设备地址

【返回值】

【功能描述】

通过 libvdsp 申请内存并映射成 vdsp 可访问的设备地址

【申请内存并映射 示例代码】

#include <hb_vdsp_mgr.h>

int32_t boot_lib_mem_alloc_test(int32_t dsp_id)
{
int32_t ret = 0;
uint64_t usize = 64 * 1024;
int64_t hbmem_flags = HB_MEM_USAGE_CPU_READ_OFTEN | HB_MEM_USAGE_CPU_WRITE_OFTEN;
uint64_t p_va=0;
uint64_t p_iova=0;

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_vdsp_mem_alloc(dsp_id, usize, hbmem_flags, &p_va, &p_iova);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_mem_alloc ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_mem_free(dsp_id, p_va);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_mem_free ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

return ret;
}
hb_vdsp_mem_free

【函数声明】

int32_t hb_vdsp_mem_free(int32_t dsp_id, uint64_t va);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] va:使用 libhbmem 申请内存的虚拟地址

【返回值】

【功能描述】

通过 libvdsp 解映射 vdsp 设备地址并释放内存

【示例代码】

参考 hb_vdsp_mem_alloc

hb_vdsp_mmu_map

【函数声明】

int32_t hb_vdsp_mmu_map(int32_t dsp_id, uint64_t va, uint64_t size, uint64_t *iova);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] va:使用 libhbmem 申请内存的虚拟地址
  • [IN] size:待映射的内存大小
  • [OUT] iova:映射成的 vdsp 设备地址

【返回值】

【功能描述】

通过 libvdsp 映射虚拟地址为 vdsp 可访问的设备地址;map 支持对同一个地址进行多次映射,map、unmap 需要成对使用,输入参数 va、size 指定的区间需要确保不超过 buffer 分配的实际大小

【映射内存为设备地址 示例代码】

#include <hb_vdsp_mgr.h>

int32_t boot_lib_mem_map_test(int32_t dsp_id)
{
int32_t ret = 0;
int64_t flags = HB_MEM_USAGE_CPU_READ_OFTEN | HB_MEM_USAGE_CPU_WRITE_OFTEN;
hb_mem_common_buf_t com_buf = {0, };
hb_mem_common_buf_t info = {0, };
uint64_t usize = 64 * 1024;
uint64_t p_iova=0;

ret = hb_mem_module_open();
if (ret != 0) {
printf("%s dsp%d hb_mem_module_open ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

ret = hb_mem_alloc_com_buf(usize, flags, &com_buf);
if (ret != 0) {
printf("%s dsp%d hb_mem_alloc_com_buf ret-%d fail !\n", __func__, dsp_id, ret);
hb_mem_module_close();
return ret;
}
if (com_buf.fd < 0) {
printf("%s dsp%d com_buf.fd %d fail !\n", __func__, dsp_id, com_buf.fd);
hb_mem_module_close();
return ret;
}

ret = hb_mem_get_com_buf(com_buf.fd, &info);
if (ret != 0) {
printf("%s dsp%d hb_mem_get_com_buf ret-%d fail !\n", __func__, dsp_id, ret);
hb_mem_free_buf(com_buf.fd);
hb_mem_module_close();
return ret;
}

ret = hb_vdsp_init(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_init ret-%d fail !\n", __func__, dsp_id, ret);
hb_mem_free_buf(com_buf.fd);
hb_mem_module_close();
return ret;
}

ret = hb_vdsp_mmu_map(dsp_id, (uint64_t)com_buf.virt_addr, usize, &p_iova);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_mmu_map ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
hb_mem_free_buf(com_buf.fd);
hb_mem_module_close();
return ret;
}

ret = hb_vdsp_mmu_unmap(g_i_vdsp_id, (uint64_t)com_buf.virt_addr);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_mmu_unmap ret-%d fail !\n", __func__, dsp_id, ret);
hb_vdsp_deinit(dsp_id);
hb_mem_free_buf(com_buf.fd);
hb_mem_module_close();
return ret;
}

ret = hb_vdsp_deinit(dsp_id);
if (ret != HB_VDSP_OK) {
printf("%s dsp%d hb_vdsp_deinit ret-%d fail !\n", __func__, dsp_id, ret);
hb_mem_free_buf(com_buf.fd);
hb_mem_module_close();
return ret;
}

ret = hb_mem_free_buf(com_buf.fd);
if (ret != 0) {
printf("%s dsp%d hb_mem_free_buf ret-%d fail !\n", __func__, dsp_id, ret);
hb_mem_module_close();
return ret;
}

ret = hb_mem_module_close();
if (ret != 0) {
printf("%s dsp%d hb_mem_module_close ret-%d fail !\n", __func__, dsp_id, ret);
return ret;
}

return ret;
}
hb_vdsp_mmu_unmap

【函数声明】

int32_t hb_vdsp_mmu_unmap(int32_t dsp_id, uint64_t va);

【参数描述】

  • [IN] dsp_id:dsp 编号,取值0、1, 对应 dsp0、dsp1
  • [IN] va:使用 libhbmem 申请内存的虚拟地址

【返回值】

【功能描述】

通过 libvdsp 解映射 vdsp 设备地址

【示例代码】

参考 hb_vdsp_mmu_map