CSR8675的DSP学习笔记——离线调试与仿真

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wzz4420381/article/details/76167105

为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板【淘宝链接:思度科技CSR开发板】

技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)

—————————–正文分割线———————————

1. 引言

CSR8675的DSP之于我,是一片美丽而充满诱惑的原始森林,等待被探索和征服。多少次有冲动深入其中揭开他的秘密,却因手头有要专注的项目,只能让demo板静静地躺在角落。最近主攻项目接近尾声,决定立即开始DSP的学习。

在学习CSR8675的DSP之前,需要准备合适的学习工具和学习资料。学习资料无疑是官网下载的开发文档和示例工程,但学习工具只有一块demo板。demo板需要外部设备提供信号输入,也需要外部设备检测信号输出,不是理想的试验和验证环境。因此实现一种不依赖外部设备的调试与仿真方法成了提高学习效率、降低学习门槛的关键。

经过一周的学习和实践,终于搭建了一套可以在纯软件环境下设计和验证DSP的程序和算法的工具集,并摸索出了调试与仿真的方法。

2. 工具集

系统环境:windows 7
安装软件:matlab 2015a, CSR ADK4.2

CSR ADK4.2安装完成后,需要用到其中的几个组件:

  • xIDE
  • kalsim
  • matlab kalsim api interface

2.1. xIDE

QTIL xIDE软件开发环境可用于:

  • 阅读
  • 编译
  • 调试
  • 运行
  • 重配置
  • 修改

2.2. Kalsim

Kalsim是一种运行在PC主机上的指令集仿真器,用来仿真kalimba DSP的运行。

Kalsim的特性如下:

扫描二维码关注公众号,回复: 3878435 查看本文章
  • 完整的精确到位的指令集仿真。
  • 与真实硬件完全相同的运行调试接口,与标准Kalimba调试工具(xIDE, Matlab, Python)完整兼容。
  • Kalimba外围设备仿真,包括MMU端口,flash文件系统,中断控制和定时器。
  • 高性能Kalimba I/O主控。
  • 执行再现。包括包括运行时的中断和流,即时是调试工具正在运行。
  • 检测未映射的存储访问。
  • 支持CSR8670(Kalimba 架构3)和CSR8675(Kalimba 架构5)。
  • 指令追踪。

2.3. matlab kalsim api interface

一个用来集成Kalimba DSP和Matlab的工具集。这个工具集可以通过调试传输接口连接Kalimba DSP,也可以通过Kalimba工具链生成的ELF或KLO文件。

这个工具集可以完成多种调试动作,例如查询处理器的状态,读写存储器内的变量,生成栈跟踪等。

3. 离线调试

3.1. 主要用途

当DSP工程运行发生异常或不符合预期,且不能通过log trace手段获取运行信息时,需要进入调试模式以定位问题的根源。

3.2. 进入调试

以C:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba工程为例。

  1. 用xIDE打开工程my_first_dsp_app_kalimba.xiw,配置工程属性。
    这里写图片描述

  2. 编译工程,生成my_first_dsp_app_kalimba.kap文件。
    这里写图片描述

  3. 进入C:\ADK_CSR867x.WIN4.2\tools\kalsim路径,shift+右键,选择“在此处打开命令窗口”,输入下列命令:

    kalsim_csr8675 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba.kap -d

    输入命令后,Kalsim加载了kap文件并进入调试模式。
    这里写图片描述

  4. 回到xIDE工程界面,选择debug->transport->kalsim localhost:31400,xIDE将使用kalsim localhost: 31400端口作为调试接口。
    这里写图片描述

    此时Kalsim界面提示如下信息:
    这里写图片描述

    在xIDE界面选择 debug->attach,进入调试模式。
    这里写图片描述

3.3. 常见问题

  1. 调试过程中,如果程序执行异常,PC指针会跳到地址0并停止运行。此时需要先按“q”键让Kalsim退出调试模式并重新加载kap,否则DSP RAM中的值不会是芯片正常复位时的值,影响程序正常运行。

4. 离线仿真

4.1. 主要用途

当需要验证DSP的工程内的算法和DSP的性能时,需要给定DSP的输入,并记录和观察DSP的输出。

4.2. Kalsim仿真

Kalsim仿真可以在给定输入后快速获取输出。比对输入和输出文件后,即可验证DSP的算法和性能。

Kalsim仿真支持多种音频格式的的输入和输出文件,可以是PCM, MP3, AAC等。

Kalsim仿真支持输入和输出文件的脚本控制,方便验证无线传输时的帧传输速率对DSP的影响。

下面结合C:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba工程,给出两种方式的运行步骤和结果。

4.2.1. 无脚本仿真

4.2.1.1. 输入指令

执行章3.2中的步骤1-3。打开命令窗口后,输入如下指令:

kalsim_csr8675 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba.kap -i 0 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\0.wav r=128000 -i 1 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\0.wav r=128000 -o 0 output_a.raw r=128000 -o 1 output_b.raw r=128000

此段指令包含以下几个动作:

  • 加载kap文件
  • 将0.wav文件中的音频流给到DSP的0号输入,设定数据带宽为16Bit*8K=128KBit
  • 将0.wav文件中的音频流给到DSP的1号输入,设定数据带宽为128KBit
  • 将DSP的0号输出记录到output_a.raw文件中,设定数据带宽为128KBit
  • 将DSP的1号输出记录到output_b.raw文件中,设定数据带宽为128KBit

4.2.1.2. 运行结果

输入指令后的运行结果如下:

C:\ADK_CSR867x.WIN4.2\tools\kalsim>kalsim_csr8675 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba.kap -i 0 c:\ADK_CSR867x.WIN4.2\apps\exa mples\my_first_dsp_app\0.wav r=128000 -i 1 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\0.wav r=128000 -o 0 output_a.raw r=128000 -o 1 output_b.raw r=128000
Kalsim (Kalimba DSP simulator)
Copyright Qualcomm Technologies International Ltd. 2008-2016

Release External/2016a (32 bit)
Built: Mar  8 2016
Kalimba Arch 5: CSR8675 build.

>> Press 'q' at any point to quit Kalsim <<

kalsim: Loading
        c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_k
        alimba.kap.
kalsim: opening file to read in
        c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\0.wav.
kalsim: opening file to read in
        c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\0.wav.
kalsim: opening file to write out output_a.raw.
kalsim: opening file to write out output_b.raw.
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
..............
kalsim: Quit requested.
...
.Kalsim finished running the program. Return value (r0): -8142952
This simulation run took 7.13 seconds.
Total Instructions = 14,740,252, Total Cycles = 1,035,992,318
MIPS = 2.1, MCPS = 145.2
Simulated at 1.21x real time

DSP开始仿真后,不会自动暂停,除非按下“q”键。仿真结果如下:
这里写图片描述

上面两张图分别对应output_a.wav和output_b.wav文件,最后一个文件对应0.wav文件。

观察这三个文件,可以看到在0.wav文件播放结束后output_a和output_b文件仍有输出。查看相关手册可知:

After the file has finished streaming, Kalsim continues generating output with the stale data in the MMU buffers until Kalsim is terminated. This is visible in the output because after the 2 seconds of input, the stale 512 samples are repeated continuously.

4.2.2. 有脚本仿真

4.2.2.1. 输入指令

执行章3.2中的步骤1-3。打开命令窗口后,输入如下指令:

kalsim_csr8675 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba.kap --infile 0 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\bi_mute.wav s=input_0.kps --infile 1  c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\bi_mute.wav s=input_1.kps --outfile 0 output_a.raw s=output_0.kps --outfile 1 output_b.raw s=output_1.kps -s 32

此段指令包含以下动作:

  • 加载kap文件
  • 将bi_mute.wav文件中的音频流给到DSP的0号输入,运行脚本input_0.kps
  • 将bi_mute.wav文件中的音频流给到DSP的0号输入,运行脚本input_1.kps
  • 将DSP的0号输出记录到output_a.raw文件中,运行脚本output_0.kps
  • 将DSP的1号输出记录到output_b.raw文件中,运行脚本output_0.kps
  • 当DSP的输入没有收到任何音频流且持续32million个时钟周期后,自动结束仿真

4.2.2.2. 运行结果

C:\ADK_CSR867x.WIN4.2\tools\kalsim>kalsim_csr8675 c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_kalimba.kap --infile 0 c:\ADK_CSR867x.WIN4.2\app
s\examples\my_first_dsp_app\bi_mute.wav s=input_0.kps --infile 1  c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\bi_mute.wav s=input_1.kps --outfile 0 output_a.ra
w s=output_0.kps --outfile 1 output_b.raw s=output_1.kps -s 32
Kalsim (Kalimba DSP simulator)
Copyright Qualcomm Technologies International Ltd. 2008-2016

Release External/2016a (32 bit)
Built: Mar  8 2016
Kalimba Arch 5: CSR8675 build.

>> Press 'q' at any point to quit Kalsim <<

kalsim: Loading
        c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\my_first_dsp_app_k
        alimba.kap.
input_0.kps: First port script
input_0.kps: About to wait 100 us:
input_0.kps: About to start streaming experiment
input_1.kps: First port script
input_1.kps: About to wait 100 us:
input_1.kps: About to start streaming experiment
output_0.kps: First port script
output_0.kps: About to wait 100 us:
output_0.kps: About to start streaming experiment
output_1.kps: First port script
output_1.kps: About to wait 100 us:
output_1.kps: About to start streaming experiment
kalsim: opening file to read in
        c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\bi_mute.wav.
kalsim: opening file to read in
        c:\ADK_CSR867x.WIN4.2\apps\examples\my_first_dsp_app\bi_mute.wav.
kalsim: opening file to write out output_a.raw.
kalsim: opening file to write out output_b.raw.
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................

Kalsim finished running the program. Return value (r0): 3366339
This simulation run took 4.05 seconds.
Total Instructions = 72,016,347, Total Cycles = 2,416,944,275
MIPS = 17.8, MCPS = 596.2
Simulated at 4.97x real time

仿真结果如下:
这里写图片描述

此图形显示的是输入和输出文件的频谱信息。可以看出输出比输入滞后一小段时间,大约是1024个采样周期。

4.2.2.3. 输入脚本

输入脚本input_0.kps内容如下:

echo First port script
echo About to wait 100 us:
//wait 100
// Start putting some data in
// Syntax: stream <amount> <rate in bps> [block size] [extra buffer]
echo About to start streaming experiment
strict_stream 128000 128000 160 40000
wait 100000
strict_stream 128000 128000 160 40000
wait 400000
strict_stream 128000 128000 160 40000
//wait 100000
strict_stream 128000 128000 160 40000
//wait 100000
strict_stream 128000 128000 160 40000
//wait 100000

脚本的含义如下:

  1. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  2. 等待400ms
  3. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  4. 等待100ms
  5. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  6. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  7. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit

4.2.2.4. 输出脚本

输出脚本output_0.kps内容如下:

echo First port script
echo About to wait 100 us:
//wait 100
// Start putting some data in
// Syntax: stream <amount> <rate in bps> [block size] [extra buffer]
echo About to start streaming experiment
strict_stream 128000 128000 160 40000
wait 100000
strict_stream 128000 128000 160 40000
wait 400000
strict_stream 128000 128000 160 40000
//wait 100000
strict_stream 128000 128000 160 40000
//wait 100000
strict_stream 128000 128000 160 40000
//wait 100000

脚本的含义如下:

  1. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  2. 等待100ms
  3. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  4. 等待400ms
  5. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  6. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit
  7. 连续处理1秒数据,传输速率16Bit*8K,每次发送160位,缓冲区40KBit

4.2.3. 常见问题

  1. strict_stream的缓冲区size应适中。否则容易出现数据传输溢出或缺失。
  2. 有关Kalsim命令和脚本命令的详细信息,参见Qualcomm® Kalimba™ DSP Simulator User Guide。

4.3. matlab仿真

4.3.1. 主要用途

将Kalsim集成到matlab的环境中,可以一键生成matlab工程与DSP工程的仿真结果对比。

相同的输入文件,由matlab同时调用matlab算法文件(.m)和Kalsim仿真器内的DSP算法(.asm),两种算法将结果同时返回到matlab。

这里写图片描述

4.3.2. 仿真步骤

  1. 在Kalsim中加载kap文件,输入命令如下:

    kalsim_csr8675 c:\ADK_CSR867x.WIN4.2\apps\examples\fileIO\fileIO.kap -d
  2. 用xIDE打开File_IO_Example工程,进入调试模式。

  3. 打开matlab工程(工程代码包含在File_IO_Example工程中)。打开工程后点击运行,仿真开始。仿真过程中,matlab会将输入文件中的数据分段发送给Kalsim simulator,每段数据处理完后再立即发送给matlab,知道所有数据处理完毕。matlab工程的流程图如下:
    这里写图片描述

matlab工程的代码如下:

%%
%% File IO Test Harness
%% 
%% This script sends and receives data to the DSP for file IO processing. 
%%
   close;
   clear all;

%%  
%% Parameters
%
   input_1 = 'left_input.wav';
   input_2 = 'right_input.wav';

   output_1 = 'left_output.wav';
   output_2 = 'right_oukaltput.wav';
   klo_file = 'fileIO.elf';
   test = kalprobe_start(klo_file);
%% Script Configuration items
   show_progress= 1;    % show the progress in percentage done.

% These strings come from the DSP file IO application. Since the goal is
% to use the same file_io_harness.asm file for all file IO systems, these names
% shouldn't change unless it's something unique like an algorithm that has 3 inputs.
   dsp_transfer_buf_1 = '$M.main.transfer_buf_1'; % used for left input and output
   dsp_transfer_buf_2 = '$M.main.transfer_buf_2'; % used for right input and output
%%  
%% Initialization 
%%

%% Ensure transfer buffers in dsp are the same size.  
   transfer_size_1 = length(kalreadval(dsp_transfer_buf_1));
   transfer_size_2 = length(kalreadval(dsp_transfer_buf_2));
   if(transfer_size_1 ~= transfer_size_2)
       fprintf('DSP transfer buffers must be the same size!/n ');
       break;
   end
   transfer_size = transfer_size_1; % prefer to use this name below.
   clear transfer_size_1 transfer_size_2;

%%  Read input signals
   [x1,fs,nbit] = wavread(input_1);     % left input
   [x2,fs,nbit] = wavread(input_2);     % right input
   LenX1 = length(x1);
   LenX2 = length(x2);
   file_size  = min(LenX1,LenX2);

   clear LenX1 LenX2;
   x1    = x1(1:file_size);
   x2    = x2(1:file_size);

%% Show Progress
 if show_progress
     loop_count = 0;
 end

%%
%%  Transfer / Receive Data From DSP
%%

   num_transfers = file_size/transfer_size;
   num_transfers = round(num_transfers);
   zero_pad_length = num_transfers*transfer_size - file_size;
   x1 = [x1; zeros(zero_pad_length,1)];
   x2 = [x2; zeros(zero_pad_length,1)];

   fprintf('Processing\n');
   for t = 0:(num_transfers-1)

       %% Print Progress
       if show_progress
           if (mod(t,round(num_transfers/20))==0)
               loop_count = loop_count + 1;
               pct_done = round(t*100/num_transfers);
               fprintf('%4d%% Done. ', pct_done);
               %% Reset loop count to add new line after
               %% 5 percentage complete text updates.
               if (loop_count==5) 
                   loop_count = 0;
                   fprintf('\n');
               end
           end
       end

       in_chunk_1 = x1((t*transfer_size)+1:(t+1)*transfer_size);
       in_chunk_2 = x2((t*transfer_size)+1:(t+1)*transfer_size);

       %% Transfer Data to DSP
       if kalprobe_enter('$Probe_AlgEntry')
           kalwriteval(dsp_transfer_buf_1,in_chunk_1,'frac');
           kalwriteval(dsp_transfer_buf_2,in_chunk_2,'frac');
           kalprobe_leave('$Probe_AlgEntry',1);
       end

       pause(0.5);
       kalrunning(1);
       pause(0.5);

       %% Retreive Data From DSP
       if kalprobe_enter('$Probe_AlgExit');
           out_chunk_1 = kalreadval(dsp_transfer_buf_1,'frac');
           out_chunk_2 = kalreadval(dsp_transfer_buf_2,'frac');
           kalprobe_leave('$Probe_AlgExit',1);
       end

       % pause(0.5);
       kalrunning(1);
       pause(0.5);

      %% Output buffering
      y1((t*transfer_size)+1:(t+1)*transfer_size) = out_chunk_1;
      y2((t*transfer_size)+1:(t+1)*transfer_size) = out_chunk_2;

  end

  wavwrite(y1,fs,output_1);
  wavwrite(y2,fs,output_2);

  diff1 = x1(1:length(y1)) - y1';
  diff2 = x2(1:length(y2)) - y2';

  subplot(2,3,1);plot(x1);xlabel('time (seconds)');title('matlab output left');axis([0,2.5*10^4,-0.25,0.25]);
  subplot(2,3,2);plot(y1);xlabel('time (seconds)');title('kalimba output left');axis([0,2.5*10^4,-0.25,0.25]);
  subplot(2,3,3);plot(diff1);xlabel('time (seconds)');title('diff output left');axis([0,2.5*10^4,-0.25,0.25]);
  subplot(2,3,4);plot(x2);xlabel('time (seconds)');title('matlab output right');axis([0,2.5*10^4,-0.25,0.25]);
  subplot(2,3,5);plot(y2);xlabel('time (seconds)');title('kalimba output right');axis([0,2.5*10^4,-0.25,0.25]);
  subplot(2,3,6);plot(diff2);xlabel('time (seconds)');title('diff output right');axis([0,2.5*10^4,-0.25,0.25]);

bypass时的仿真结果如下:
这里写图片描述

可见两者在bypass模式时输出完全一致。

4.4. 两种仿真方式对比

Kalsim仿真速度比matlab仿真快,但Kalsim仿真输出的wav文件不能直接与输入文件作比较,需要借助matlab等其他工具来做进一步处理。matlab仿真的最大优势是使算法开发真正做到了简单高效,且可以使用matlab丰富的分析工具辅助分析。

后续DSP内算法的开发都会尽可能使用matlab平台。

5. 真实硬件运行与仿真的差异

5.1. 存储映射寄存器时序

在Kalsim中存储映射寄存器总是立即生效,但在真实硬件运行时需要数个时钟周期。大多数应用下不会有明显影响。

5.2. MMU行为

在真实硬件中,传输速率和常见的块大小因应用和数据目的地而不同。例如,真实硬件中的端口的音频数据输出速率是一个定值,但一些数据目的地(例如蓝牙传输)以有规律的间隔传输数据。

默认情况下,Kalsim在6400个仿真时钟周期内处理所有MMU缓冲区,尽可能拷贝输入和输出。这可以用来仿真MMU的平均行为。

一些应用需要更灵活的控制,此时需要使用端口脚本。例如,一些音频应用使用MMU缓冲区的填满功能以判定音频文件是否解码完成。

5.3. 中断控制和定时器时序

在监视不同中断源时,速率是不同的。这就是说中断发生的时序与真实硬件是略有不同的。中断控制器在每X个DSP时钟时被仿真一次,X默认值是6400。此值可通过命令修改。80 MIPS Kalimba DSP的中断分辨率的默认值是80us。

5.4. 不支持的存储映射寄存器

Kalsim可以仿真存在于Kalimba DSP内的存储映射寄存器子集。读写不被支持的存储映射寄存器将引起警告。

5.5. VM行为

Kalsim不能仿真BlueCore设备内的VM的行为。取而代之的,它可以仿真一些常见的VM行为,例如收到MESSAGE_KALIMBA_READY后会返回 MESSAGE_GO消息,以及在连接DSP端口后发送消息。

此特性对一些DSP工程影响很大,直接导致一些DSP工程不能离线仿真。例如sbc_decoder工程,他需要在收到MESSAGE_GO消息前配置好解码模式,但由于Kalsim不能发送配置解码模式的消息,使得工程在仿真时程序执行异常。遇到这样的情况,需要用到在线调试。在线调试很简单,在此不专门展开。

6. 总结

离线调试与仿真环境的搭建成功,给后续学习打下了很好的基础。后续的主要工作分两个步骤,一是学习并理解现有的DSP工程,二是在此基础上学会开发具有独特功能的DSP算法。

猜你喜欢

转载自blog.csdn.net/wzz4420381/article/details/76167105