QCC51xx学习笔记:KSE简介和跑通官方仿真例程
Posted NiceBT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QCC51xx学习笔记:KSE简介和跑通官方仿真例程相关的知识,希望对你有一定的参考价值。
为了方便大家学习,现与思度科技联合推出QCC300x/CSR867x/QCC30xx/QCC51xx学习板【思度科技蓝牙开发板】。
技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)
——————————正文分割线———————————–
1. 引言
高通以CSR867X系列为基础,开发了下一代高性能蓝牙SOC系列QCC30XX/QCC51XX。新系列整体的硬软件能力都有了显著的提升,包括全新的音频仿真环境KSE。
从开始阅读KSE描述文档,到第一次跑通例程,再到分析定位仿真误差,经历了漫长的三个月。为了让大家少走一些弯路,将过程中遇到的问题和解决方案分享出来(文中例程和文档都可以在思度开发板QCC512X资料库中下载)。
2. KSE简介
官方介绍文档如下文档:
- 80-cg060-1_ab_kymera_introduction.pdf
- 80-cg335-1_aa_kymera_simulation_environment_(kse)_training.pdf
- 80-cg887-1_ab_kymera_simulation_environment_(kse)_user_guide.pdf
详细内容建议读者阅读原版手册,在此只抓几个重点概念简单讲一下。
首先我们需要了解KSE的系统框架,示意图如下:
- KSE: kymera simulation environment,即kymera仿真环境。包含Kalsim和kymera两部分。
- Filesystem:仿真数据输入和输出以文件形式存在,KSE从文件中读取数据,经过处理后再写入文件。文件可以是raw/wav/sbc等各种格式,方便与算法开发工具(如matlab)对接,构建从算法原型开发到嵌入式平台仿真的全链路环境。
- Kalsim:高通kalimba DSP的PC平台软核版本,即芯片模拟器,可以真实模拟kalimba dsp的处理结果,并提供调试接口。
- Kymera:运行在kalimba dsp内的专用于音频处理的操作系统,C语音和汇编语言混合编程。负责进程管理、内存分配、消息队列、音频接口管理等,需要开发者重点关注。
- Python Shell:一套Python语言编写的脚本,可以方便地运行、调试、测试KSE环境,包含uut, kymera, endpoint, capability, stream, graph, kalcmd, acat等工具。Python工具集可以实现仿真过程的高度自动化,同时兼容各种第三方插件,值得投入时间精力重点学习。
对于算法移植工程师来说,KSE中的kymera API、Python工具集API和文件接口是需要重点关注的,kalsim对我们来说是个黑盒,不用过多关注。软件开发需要掌握C语言、汇编语言,熟悉matlab脚本、python脚本和json格式。
3. 跑通官方仿真例程
3.1. 获取仿真结果
高通官方提供了loopback_download_test例程作为最简的算法移植验证环境,其音频链路框图如下:
官方文档给出了各个部分的详细描述,可以参考如下文档:
- 80-ct522-1_ae_kymera_api_reference.pdf
- 80-cf424-1_ac_kymera_utility_api_reference.pdf
- 80-ct243-1_ag_kymera_capability_api_document.pdf
- 80-cf976-1_ac_kymera_capability_library_user_guide.pdf
- 80-cg461-1_aa_kymera_endpoint_library_user_guide.pdf
- 80-cg063-1_aa_debugging_with_acat.pdf
- 80-cg879-1_aa_qualcomm_kymera_voice_assistant_graph_setup_user_guide.pdf
每个模块都有对应的文档,可见高通为了让开发者掌握这套新架构是投入了很多精力。建议多花时间在阅读上述文档,对理解和运用kymera有很大帮助。
跑通此例程的方法在文档“80-cg335-1_aa_kymera_simulation_environment_(kse)_training.pdf”中有详细描述,这里给出仿真的结果示意:
matlab脚本读取仿真的输入输出文件后,以坐标图形式显示,代码如下:
system('run_pass_through.bat > run_log.txt');
figure(1);
fd = fopen('.\\resource\\153_Prompts_176.4_kHz_Music_Detected_8k.raw','r');
data1 = fread(fd, 'int16');
subplot(2,1,1);plot(data1);xlabel('aud in');
fclose(fd);
fd = fopen('.\\tmp\\153_Prompts_176.4_kHz_Music_Detected_8k.raw','r');
data2 = fread(fd, 'int16');
subplot(2,1,2);plot(data2);xlabel('aud out');
fclose(fd);
3.2. 加速仿真
官方例程中需要用MDE开发环境运行KSE,每次仿真都需手动执行连接仿真器等操作。在此给出一种运用windows bat脚本结合python脚本实现一键自动化仿真的方法。
首先需要按照官方文档运行一次MDE的例程loopback_download_test,目的是用MDE模板工程创建基础仿真工程。
然后我们可以抓取MDE部署仿真工程的命令行,以windows bat脚本的方式启动python KSE shell:
cmd.exe /C "D:\\qtil\\ADK_QCC512X_QCC302X_WIN_6.3.2.24\\tools\\python27\\python.exe -m kse.kalsim.kalsim_shell --ks_path D:\\qtil\\ADK_QCC512X_QCC302X_WIN_6.3.2.24\\tools\\kalsim\\kalsim_qcc512x_audio.exe --log_level 20 --ks_firmware E:\\___Product\\0_INP\\TWSearphone\\target\\qcc512x\\loopback_download_test\\audio\\kalimba\\kymera\\output\\stre_rom_kalsim_kalcmd2_release\\build\\debugbin\\kymera_stre_audio --acat_path D:\\qtil\\ADK_QCC512X_QCC302X_WIN_6.3.2.24\\audio\\kalimba\\kymera\\tools\\ACAT\\ACAT --acat_use --acat_bundle E:\\___Product\\0_INP\\TWSearphone\\target\\qcc512x\\loopback_download_test\\audio\\kalimba\\kymera\\output_bundles\\stre_rom_kalsim_kalcmd2_release\\self_test_and_passthrough\\self_test_and_passthrough --ka_path D:\\qtil\\ADK_QCC512X_QCC302X_WIN_6.3.2.24\\tools\\pythontools --platform stre --hydra_ftp_server_directory E:\\___Product\\0_INP\\TWSearphone\\target\\qcc512x\\loopback_download_test\\audio\\kalimba\\kymera\\output\\stre_rom_kalsim_kalcmd2_release\\build\\patchbin --script "script/kalsim/pass_through.py script/kalsim/self_test_and_passthrough.dkcs""
windows bat脚本的最后一行指明了KSE shell启动时自动加载的仿真脚本pass_through.py,其描述了kymera的输入输出关系、内部音频链路、各capability的设置等,代码如下:
import os
import time
import sys
import argparse
if __name__ == '__main__':
from kats.kalsim.stream.stream_base import CALLBACK_EOF
INPUT_FILE_RAW = 'resource/153_Prompts_176.4_kHz_Music_Detected_8k.raw'
OUTPUT_FILE_RAW = 'tmp/153_Prompts_176.4_kHz_Music_Detected_8k.raw'
print('removing output files')
try:
os.remove(OUTPUT_FILE_RAW)
except Exception:
pass
print('creating source stream')
config =
'hydra_type': 'audio_slot',
'hydra_bac_handle': 0,
'hydra_audioslot': 0,
'stream_flow_control_drive': 'kalsim',
'stream_flow_control_block_size': 1,
'stream_format': 16,
'stream_backing': 'file',
'stream_filename': INPUT_FILE_RAW,
'stream_flow_control_rate': 8000,
st_source = stream.get_instance('hydra', 'source', **config)
st_source.create()
st_source.config()
print('creating sink stream')
config =
'hydra_type': 'audio_slot',
'hydra_bac_handle': 0,
'hydra_audioslot': 0,
'stream_flow_control_drive': 'kalsim',
'stream_flow_control_block_size': 1,
'stream_format': 16,
'stream_backing': 'file',
'stream_filename': OUTPUT_FILE_RAW,
'stream_flow_control_rate': 8000,
st_sink = stream.get_instance('hydra', 'sink', **config)
st_sink.create()
st_sink.config()
print('creating endpoints')
ep_source = kymera.stream_if_get_source('pcm', [0, 0])
kymera.stream_if_configure_sid(ep_source, 'pcm_sample_rising_edge', 0)
kymera.stream_if_configure_sid(ep_source, 'pcm_master_clock_rate', 512000)
kymera.stream_if_configure_sid(ep_source, 'pcm_master_mode', 1)
kymera.stream_if_configure_sid(ep_source, 'pcm_sample_format', 1)
kymera.stream_if_configure_sid(ep_source, 'pcm_sync_rate', 8000)
kymera.stream_if_configure_sid(ep_source, 'pcm_slot_count', 4)
ep_sink = kymera.stream_if_get_sink('pcm', [0, 0])
print('creating pass through operator')
op = kymera.opmgr_create_operator(1)
kymera.opmgr_operator_message(op, [10, 1])
kymera.opmgr_operator_message(op, [11, 1])
print('connecting endpoints')
tr1 = kymera.stream_if_connect(ep_source, op | 0xa000)
tr2 = kymera.stream_if_connect(op | 0x2000, ep_sink)
print('start streaming')
st_sink.start()
kymera.opmgr_start_operators([op])
st_source.start()
for _ in range(12500):
if not st_source.check_active():
print('end of file detected')
break
time.sleep(0.005)
else:
raise RuntimeError('timeout streaming the file')
print('stop streaming')
st_source.stop()
st_source.destroy()
st_sink.stop()
st_sink.destroy()
print('release resources')
kymera.opmgr_stop_operators([op])
kymera.stream_if_transform_disconnect([tr1, tr2])
kymera.opmgr_destroy_operators([op])
kymera.stream_if_close_source(ep_source)
kymera.stream_if_close_sink(ep_sink)
exit()
kymera的音频链路可以图形化显示,需要安装python环境下的graphviz插件。安装完插件后,在上述py脚本中的print(‘start streaming’)语句前插入acat.run(),执行脚本后会需要提示输入指令,输入stream.create_graph_img():
生成html格式的graph描述文件,可以在浏览器打开,这里给出音频链路示意图:
4. 验证仿真结果
loopback_download_test例程跑通后,我们需要验证KSE的仿真结果是否符合预期。此例程中的basic_pass_through capability可以看做简单的数据搬运,因此输入输出应该是bit-by-bit对应的。
回顾3.1章中给出的坐标图,肉眼可见的差异是输出相对于输入有很大的延迟。这里我的判断是KSE环境在运行时引入的延迟,即从打开输出文件,到写入仿真数据,这两个时间点之间有仿真环境的延迟。
为了能较方便地去除延迟对仿真结果结果判定的影响,我们改用白噪声作为输入信号,用find函数查询非0点的起始位置,使输入输出按样点对齐。
matlab脚本代码如下:
figure(1);
fd = fopen('.\\resource\\audio_in.raw','r');
data1 = fread(fd, 'int16');
data1 = data1(1:5000);
subplot(3,1,1);plot(data1);xlabel('aud in');
fclose(fd);
fd = fopen('.\\tmp\\audio_out.raw','r');
data2 = fread(fd, 'int16');
offset = find(data2~=0);
data2 = data2(1+offset(1)-1:5000+offset(1)-1);
subplot(3,1,2);plot(data2);xlabel('aud out');
fclose(fd);
data3=data1-data2;
subplot(3,1,3);plot(data3);xlabel('audio in - audio out');
对齐后的输入、输出、误差结果:
结果出乎意料,可以看到输入输出之间是有误差存在的。对误差信号进行频谱估计,可见其频率成分主要集中在低频:
这个问题困扰了我有近一个月,终于在文档“80-cg887-1_ab_kymera_simulation_environment_(kse)_user_guide.pdf”中找到了答案:
3.4.1 Hydra PCM Endpoints
On the data that is filled in their buffer PCM endpoints carry out two operations:
■ DC removal: To remove the DC component from the data on a moving window basis.
■ Rate matching: To adjust the incoming sample rate to the rate that is specified in the endpoint
configuration.
These two operations can be controlled by a configuration key named
audio_disable_endpoint_processing. A value of 0x0000 disables both functionalities and
0x0001 disables DC removal.
原来hydra PCM类型的endpoint本身隐含了DC removal和Rate matching两个功能,即去除信号中的直流成分和采样率转换。对照上文中提到的误差信号的频率成分主要是低频,猜想消除误差的方式应是关闭DC removal功能。
按照文档给出的方法在pass_through.py脚本中的"create endpoint"段插入如下代码:
kymera.stream_if_configure_sid(ep_source, 'audio_disable_endpoint_processing', 1)
ep_sink = kymera.stream_if_get_sink('pcm', [0, 0])
kymera.stream_if_configure_sid(ep_sink, 'audio_disable_endpoint_processing', 1)
修改accmd.py脚本,添加“audio_disable_endpoint_processing”命令:
# available to all interfaces
'audio_channel_mute_enable': 0x0700,
'audio_sample_size': 0x0701,
'audio_disable_endpoint_processing': 0x0710,
重新运行KSE,再用matlab脚本比对结果,误差消除:
至此一个高效并可靠的仿真环境已搭建完毕,可以在此基础上开始算法移植工作。
5. 总结
- kymera能够支持C & 汇编混合编程是很有益的特性。可以先基于通用C代码评估算法误差、执行效率,后续C转汇编以提升算法效率。
- AEC capability的仿真还未成功。很多算法需要AEC Reference作为参考输入。
- 内存管理机制有待探索。
- 没有涉及客制算法模块和调音工具的开发。
以上是关于QCC51xx学习笔记:KSE简介和跑通官方仿真例程的主要内容,如果未能解决你的问题,请参考以下文章