跳到主要内容

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 错误。

image_2

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

image_3

目前对于 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 使用哪个库?

image_5

请使用 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:

image_6

进到 xtensa-elf/lib 路径下:

image_7

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

image_8

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

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

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

image_9

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

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

image_10

在总线程个数大于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 业务交互逻辑。

vdsp1

ARM 侧开发流程

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

vdsp2

VDSP 侧开发流程

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

vdsp3

本用例流程说明:

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

vdsp4

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