vsp算法移植指南*
- 为了帮助算法工程师更快的部署自己的算法,本文所讲的一些配置都是基于
GX8008C_Wukong_Prime
开发板,简称悟空开发板
,因此手中如何没有此开发板,请联系我们的当地的销售人员。
1. 开发板系统框图:*
2. example固件编译和下载*
2.1 example配置*
可以根据下面配置和需求修改
- example_lib: 一个包含TX和RX处理的算法包
- TX: 一个简单的多通道混音算法,把输入的n路mic数据混音作为输出
- RX: 对RX音频作增益放大
8008c_wukong_v1.4_example_lib_16k_2Amic_1Aref_UAC4ch_SPKtxout.config
- 采样率 16k
- UAC1.0声卡模式
- UAC上行输出4路数据,1ch-TX_OUT,2ch-Amic,1ch-Aref
- AudioOut输出TX_OUT
8008c_wukong_v1.4_example_lib_16k_2Amic_1Aref_UAC6ch_SPKrxout.config
- 采样率 16k
- UAC1.0声卡模式
- UAC上行输出6路数据,1ch-TX_OUT,1ch-RX_OUT,2ch-Amic,1ch-Aref,1ch-RX_IN
- AudioOut输出RX_OUT
8008c_wukong_v1.4_example_lib_16k_4Dmic_1Aref_UAC6ch_SPKtxout.config
- 采样率 16k
- UAC1.0声卡模式
- UAC上行输出6路数据,1ch-TX_OUT,4ch-Dmic,1ch-Aref
- AudioOut输出TX_OUT
8008c_wukong_v1.4_example_lib_16k_4Dmic_1Aref_UAC8ch_SPKrxout.config
- 采样率 16k
- UAC1.0声卡模式
- UAC上行输出8路数据,1ch-TX_OUT,1ch-RX_OUT,4ch-Dmic,1ch-Aref,1ch-RX_IN
- AudioOut输出RX_OUT
8008c_wukong_v1.4_example_lib_48k_UAC2ch_SPKrxout2ch.config
- 采样率 48k
- UAC1.0声卡模式
- UAC上行输出2路数据,2ch-RX_OUT,UAC下行双通道48k
- AudioOut输出RX_OUT
2.2 如何编译:*
- 将
vsp_sdk
下载到本地之后,在vsp_sdk
目录下执行命令编译完成后,生成的固件在$ cp configs/example_lib/8008c_wukong_v1.4_example_lib_16k_2Amic_1Aref_UAC4ch_SPKtxout.config .config $ make menuconfig # 打开menuconfig,保存并退出 $ make clean; $ make
output
目录中,mcu_nor.bin
是mcu
的固件,dsp.fw
是dsp
的固件,vsp.bin
是两个部分合并的固件,
2.3 如何烧录固件 [两种方式]:*
- 烧录
vsp.bin
:$ sudo tools/bootx/bootx -m leo_mini -t u -c "download 0 output/vsp.bin;reboot"
- 烧录
mcu_nor.bin
跟dsp.fw
$ cd tools/bootx $ ./flash_nor_mini.sh
- 下载完成后PC会识别到声卡设备,就可以通过
audacity(录音工具)
进行录音。具体录音操作说明点这里
3. 输入输出通道的配置*
-
执行
make menuconfig
,进入VSP I/O Buffer settings
中Channel settings:
设置通道,根据需求设置对应的mic
和ref
通道,输出通道也是根据需求来配置,如果需要uac
录音必须选择交织
输出,勾选Interlaced OUT Channels
,并按需配置你需要通过uac
录制的路数。Frame settings:
根据需求来配置相应的采样率和帧长Context settings:
dsp
算法处理的对象是context
,需要按需配置Frame Number in a Context
4.如何构建自己的算法包*
vsp_sdk/dsp/vpa
目录下是一个个互相独立的算法包,可以通过 menuconfig
来选择所需要的算法包,本节我们参考 example_lib
来构建属于自己的算法包
- 拷贝
example_lib
算法目录,重命名为需要移植的算法, 例如改为gx_lib
-
修改
gx_lib
目录下的vpa.name
、Makefile
和Kconfig
文件的内容,主要是需要修改部分路径和宏- 将
vpa.name
中的内容替换为config VSP_VPA_GX_LIB bool "GX [Library]"
-
将
Kconfig
中的VSP_VPA_EXAMPLE_LIB
替换为VSP_VPA_GX_LIB
-
将
Makefile
中的CONFIG_VSP_VPA_EXAMPLE_LIB
替换为CONFIG_VSP_VPA_GX_LIB
,将SRC_DIR = vpa/example_lib
替换为SRC_DIR = vpa/gx_lib
- 将
-
构建完算法包后,执行
make menuconfig
,在Voice Process Algorithm select
中选中新增的算法gx_lib
,编译的就是你新增的算法包
5. 算法移植*
5.1 目录介绍*
example_lib
整个目录结构如下,高亮部分是工程师需要重点关注的部分1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
dsp/vpa/example_lib ├── include # 头文件的目录 │ ├── usr_alg.h # 算法的对外头文件 │ └── vsp_algorithm.h ├── Kconfig ├── lib │ └── libusr_alg.a # 算法源码编译生成的库文件 # 会先编译usr_alg内的源码生成库文件在当前目录 # 如果已经是库文件,则直接把库文件放到这里,忽略目录usr_alg ├── Makefile ├── src │ ├── usr_alg # 算法的源码可以存放这里,方便统一维护和裁剪 │ │ ├── Makefile │ │ └── usr_alg.c │ ├── vsp_algorithm.c # 算法集成的代码放这里,方便统一维护 │ └── vsp_process.c └── vpa.name
5.1 算法的初始化*
- 算法需要初始化的代码放在
VspInitialize
接口中,这个接口会在上电后调用一次1 2 3 4 5
XIP_TEXT_ATTR int VspInitialize(VSP_CONTEXT_HEADER *context_header) { VspDoExampleInit(context_header); return 0; }
1 2 3 4 5
IRAM0_TEXT_ATTR int VspDoExampleInit(VSP_CONTEXT_HEADER *context_header) { /* 算法初始化代码 */ return 0; }
5.2 算法的处理*
- 算法处理的入口函数在
vpa_process.c
中的VspProcessActive
,所有的算法都是在这个函数中进行处理,这个函数会在初始化后,根据配置的context的时长回调。比如Frame Number in a Context
是2和Frame settings
是16ms,则VspProcessActive
每32ms回调一次。
5.2.1 TX 处理*
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
5.2.2 RX 处理*
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
6. 算法相关的 helper
接口*
- 所有相关的
helper
接口都在vsp_sdk/dsp/vsp/vsp_helper.c
中。
函数接口 | 使用说明 |
---|---|
int VspProcessInvalidateMicBuffer (VSP_CONTEXT *context) |
根据 context 中对应的 mic_buffer 地址和大小,无效 cache 中的数据 |
int VspProcessWritebackMicBuffer (VSP_CONTEXT *context) |
根据 context 中对应的 mic_buffer 地址和大小,将 cache 中的数据写回 sram |
int VspProcessInvalidateRefBuffer (VSP_CONTEXT *context) |
根据 context 中对应的 ref_buffer 地址和大小,无效 cache 中的数据 |
int VspProcessWritebackRefBuffer (VSP_CONTEXT *context) |
空 |
int VspProcessInvalidateRxBuffer (VSP_CONTEXT *context) |
根据 context 中对应的 rx_buffer 地址和大小,无效 cache 中的数据 |
short * VspProcessGetMicFrame (VSP_CONTEXT *context, unsigned int channel_num, int frame_index) |
功能 :获取当前 context 中某个mic通道 的某一帧 的麦克风数据起始地址channel_num :mic 通道号frame_index :当前context 的第几帧 |
short * VspProcessGetRefFrame (VSP_CONTEXT *context, unsigned int channel_num, int frame_index) |
功能 :获取当前 context 中某个ref通道 的某一帧 的ref数据起始地址channel_num :ref 通道号frame_index :当前context 的第几帧 |
short * VspProcessGetRxFrame (VSP_CONTEXT *context, unsigned int channel_num, int frame_index) |
功能 :获取当前 context 中某个rx通道 的某一帧 的ref数据起始地址channel_num :ref 通道号frame_index :当前context 的第几帧 |
short * VspProcessGetOutFrame (VSP_CONTEXT *context, unsigned int channel_num, unsigned int frame_index) |
功能 :获取当前 context 中某个output通道 的某一帧 的output数据起始地址channel_num :output 通道号frame_index :当前contex t的第几帧 |
VSP_CONTEXT * VspProcessGetContext (const VSP_CONTEXT *context, unsigned int index) |
功能:以当前 context 为起始点,获取第 index 个 context 的地址 |
注意
在 VspProcessActive
前,我们需要调用 VspProcessInvalidateMicBuffer
,VspProcessInvalidateRxBuffer
,VspProcessInvalidateRefBuffer
接口无效当前 dsp
中相关的cache
数据,保正算法拿到的 mic,ref,rx
数据都是实时有效的
注意
在使用VspProcessGetRxFrame
获取到uac下行双通道的数据时,数据是交织存储的
7. 算力查看*
我们也提供了实时查看dsp算力的功能
- 执行
make menuconfig
,进入DSP settings
- 使能
Enable Process Cycle Statistic
和Enable log printing on DSP
,Enable log printing on DSP
分级下的默认就行,然后重新编译固件,连接dsp
串口,就会打印算力,超过百分百就是算力超了,需要优化算法。
提醒:如何统计某个热点函数的算力
我们提供了 unsigned xthal_get_ccount(void) 来获取当前 CCOUNT 寄存器的值,DSP 每走一拍会自动加 1,如果 DSP 的频率是400M,那么这个寄存器每秒就会自动加 400M。因此我们在热点函数前后调用 xthal_get_ccount(),然后相减就是该热点函数所消耗的算力。
8. 内存使用*
8.1 SRAM*
8008/8008C
都拥有1536KB
SRAM,默认代码都跑在SRAM上,1536KB
由MCU和DSP共用,可以配置DSP使用的SRAM内存大小,剩下的内存留给MCU使用。- 执行
make menuconfig
,进入DSP settings
,配置(1300) SRAM size kept for DSP(KB)
如何确定MCU内存
MCU这边不会用到动态内存,只要 make mcu 能编译正常,MCU内存就不会有问题
8.2 DRAM0和IRAM0*
-
DSP上除了SRAM可用,还有64k的
DRAM0
和64k的IRAM0
,默认使用的都是SRAM,如果需要使用这个内存,需要用以下宏#define IRAM0_TEXT_ATTR __attribute__((section(".iram0.text"))) #define DRAM0_BSS_ATTR __attribute__((section(".dram0.bss"))) #define DRAM0_DATA_ATTR __attribute__((section(".dram0.data"))) #define DRAM0_RODATA_ATTR __attribute__((section(".dram0.rodata")))
-
DRAM0
放一些数据,如static short all_data[6*FRAME_LEN] DRAM0_DATA_ATTR;
-
IRAM0
放一些代码,如IRAM0_TEXT_ATTR int VspProcessActive(VSP_CONTEXT *context)
8.3 XIP*
-
在8008c(8008上不支持)上,还可以使用XIP技术,可以把一些执行频率低的代码或者一些只读的数据放到XIP段上面,需要用到以下宏
#define XIP_TEXT_ATTR __attribute__((section(".xip.text"))) #define XIP_RODATA_ATTR __attribute__((section(".xip.rodata")))
-
执行
make menuconfig
,进入DSP settings
,配置[*] Enable XIP
-
XIP
放一些代码,如XIP_TEXT_ATTR int VspInitialize(VSP_CONTEXT_HEADER *context_header)
-
XIP
放一些代码,如short data[3] XIP_RODATA_ATTR = {1, 2, 3};
9. 算法开发相关手册*
- DSP编译工具链安装完成后,打开 Xplorer,点击 Help→PDF Documentation 就可以看到很多关于 HIFI4 的文档。包含了 Hifi4 规格书、指令集使用说明、Xtexsa编译工具链使用说明文档。