3. 仿真与调试基础:断点设置、单步执行与全速运行
调试,说白了就是跟芯片“对话”。你告诉它停在哪,它告诉你现在是什么状态。我刚开始做DSP开发时,总觉得调试就是点一下“运行”按钮,看结果对不对。后来被一个野指针折磨了三天,才老老实实把断点、单步这些基本功练扎实了。
这一节,咱们就把调试的几把“手术刀”磨锋利。你掌握了这些,联调时至少能省下一半的排查时间。
3.1 断点设置:硬件断点 vs 软件断点
断点就是让程序停下来的“路障”。但DSP里的路障分两种,用错了会出大问题。
3.1.1 硬件断点
硬件断点是用芯片内部的调试寄存器实现的。它不修改你的代码,也不占用程序存储空间。我习惯在以下场景用硬件断点:
- 在ROM或Flash中设断点——这些区域不可写,软件断点根本放不进去。
- 在中断服务函数入口设断点——硬件断点对时序影响最小。
- 调试启动代码——芯片刚上电时,内存还没初始化,软件断点可能失效。
3.1.2 软件断点
软件断点的原理,是在你设断点的地址上,临时插入一条特殊指令(通常是 ESTOP0 或 SWBP)。程序跑到这里就自动停下。
它的好处是“无限量供应”——只要RAM够大,你想设多少个都行。但要注意:
- 不能在只读区域使用(ROM、Flash、OTP)。
- 调试完后要清除,否则下载到目标板时,代码里还残留着断点指令,程序会跑飞。
- 实时性敏感代码慎用,插入指令会改变流水线行为。
3.1.3 两种断点的对比
| 对比项 | 硬件断点 | 软件断点 |
|---|---|---|
| 实现方式 | 调试寄存器 | 插入特殊指令 |
| 数量限制 | 通常2~6个 | 几乎无限 |
| 适用区域 | ROM/Flash/RAM | 仅RAM |
| 对代码影响 | 无 | 修改了指令流 |
| 实时性影响 | 极小 | 有额外延迟 |
| 典型场景 | 中断、启动代码、时序敏感 | 普通算法调试、数据验证 |
3.2 单步执行与全速运行
这两个操作,一个像“慢镜头回放”,一个像“快进到结局”。怎么配合使用,是调试效率的关键。
3.2.1 单步执行
单步就是一次只执行一条指令(或一行C代码)。我常用的单步方式有三种:
- Step Into (F5):进入函数内部。适合看子函数的实现细节。
- Step Over (F6):跳过函数,直接执行到下一行。适合你信任的库函数。
- Step Out (F7):从当前函数跳出,回到调用处。适合你发现走错路了,想快速返回。
嗯,这里要注意:单步执行时,DSP的定时器、外设仍在运行。如果你在调试PWM或ADC相关代码,单步可能导致外设超时或数据溢出。
3.2.2 全速运行
全速运行就是让DSP以正常速度执行代码。它主要用于:
- 验证整体功能是否正常。
- 测试实时性、时序是否满足要求。
- 配合断点,在关键位置停下来检查状态。
全速运行时,你基本失去了对程序的控制权。所以我的建议是:先单步把逻辑调通,再全速跑整体流程。别一上来就全速,出了问题你都不知道从哪查起。
3.3 变量与寄存器观察
断点让程序停下,观察窗口告诉你“停在了什么状态”。这两个配合,才是调试的完整闭环。
3.3.1 变量观察
在CCS或Code Composer Studio里,你可以把变量拖到“Expressions”窗口。我常用的技巧:
- 右键选择“Add to Watch”,实时查看变量值变化。
- 修改变量值——在观察窗口里直接改,测试边界条件。比如把循环计数器改成最大值,看看会不会溢出。
- 观察数组和结构体——展开“+”号,看每个成员的值。
volatile 关键字,强制编译器保留它。但注意,volatile 会影响性能,调试完记得去掉。
3.3.2 寄存器观察
寄存器是DSP的“神经末梢”。我调试时必看的寄存器包括:
- PC (Program Counter):程序当前执行到哪个地址。
- ST0/ST1 (Status Registers):进位、溢出、符号位等标志。
- IER (Interrupt Enable Register):哪些中断被打开了。
- IFR (Interrupt Flag Register):哪些中断正在等待响应。
为什么会看这些?有一次我调试一个串口通信程序,数据总是丢包。单步执行时一切正常,全速就丢。后来打开IFR一看,发现接收中断标志位一直被置位,但中断服务函数根本没进去——原来是中断向量表配置错了。这种问题,不看寄存器根本发现不了。
3.4 Memory Browser 使用技巧
Memory Browser(内存浏览器)是调试器里最强大的工具之一。它让你直接看到DSP的整个地址空间——程序存储器、数据存储器、外设寄存器映射区,一览无余。
3.4.1 基本操作
- 输入地址:直接在地址栏输入十六进制地址,比如
0x80000000,就能跳转到对应位置。 - 选择显示格式:32位、16位、8位,或者浮点数、ASCII字符。我调试音频算法时,经常把数据以浮点数格式显示,直接看波形样本值。
- 修改内存值:双击某个地址,直接写入新值。这比重新编译下载快多了。
3.4.2 高级用法
我常用的几个技巧:
- 对比内存快照:在程序运行前保存一段内存的快照,运行后再保存一次,用对比工具看差异。这能快速发现哪些变量被意外修改了。
- 搜索特定值:比如你怀疑某个数组被写入了0xDEADBEEF,直接在Memory Browser里搜索这个值,几秒钟就能定位到问题代码。
- 查看外设寄存器:把外设基地址输入Memory Browser,就能看到所有寄存器的当前值。比在寄存器窗口里一个个翻快得多。
3.5 本章知识体系
下面这张图,把调试基础的核心逻辑串起来了。你可以把它当作调试时的“路线图”:
这张图把调试的三大块——断点、执行控制、观察工具——串在了一起。你调试时遇到问题,可以顺着这个结构排查:先看断点设对了没有,再看执行方式是否合适,最后用观察工具确认状态。这三步走下来,大部分问题都能定位到。