3. SPI验证测试用例设计:读写寄存器测试,DMA传输测试,异常时序注入测试,边界条件测试
SPI 验证,说白了就是要把这个协议从里到外翻个底朝天。我做了这么多年验证,发现很多人上来就写驱动,结果一上硬件就抓瞎——时序不对、DMA 卡死、边界没测到。嗯,今天咱们就把这四类测试用例掰开揉碎了讲。
3.1 读写寄存器测试
这是最基础的,但也是最容易翻车的。我见过有人 SPI 寄存器读回来全是 0xFF,还以为是芯片没焊好,结果发现是地址线接反了。
3.1.1 寄存器回读验证
写一个值进去,再读出来,看是不是同一个数。听起来简单?你得考虑:
- 全地址遍历: 每个寄存器地址都要测,别偷懒只测几个
- 数据线完整性: 写 0xAA、0x55 这种交替位模式,检查有没有短路
- 写保护位: 有些寄存器是只读的,写进去应该被忽略
3.1.2 寄存器域位测试
寄存器里经常有多个域位,比如控制寄存器的 bit[3:0] 是分频系数,bit[7] 是使能位。你得单独测每个域位:
// 伪代码示例:域位测试
for (bit_pos = 0; bit_pos < 32; bit_pos++) {
write_reg(addr, 1 << bit_pos);
read_val = read_reg(addr);
if (read_val != (1 << bit_pos)) {
printf("Bit %d 读写失败\n", bit_pos);
}
}
为什么要这么测?我曾经遇到过一个芯片,它的 bit[15] 和 bit[16] 在内部是短路的,写一个另一个也跟着变。要不是做了逐位测试,这 bug 根本抓不到。
3.2 DMA传输测试
DMA 传输是 SPI 性能的关键。但 DMA 一旦出问题,数据流就全乱了。我调试过最头疼的一个问题,就是 DMA 传输到一半突然停掉,查了三天才发现是 DMA 描述符链表的地址没对齐。
3.2.1 单次DMA传输
先测最简单的:一次 DMA 传输 1 字节、4 字节、64 字节、1024 字节。检查:
- 传输完成后 SPI 状态寄存器是否正确
- 接收缓冲区数据是否完整
- DMA 传输完成中断是否触发
3.2.2 循环DMA传输
很多场景需要连续传输,比如音频数据流。循环 DMA 测试要关注:
- 设置 DMA 为循环模式,缓冲区大小 256 字节
- 启动传输,持续 10 秒
- 检查是否有数据丢失或重复
- 检查 DMA 地址是否回绕正确
3.2.3 DMA与CPU并发访问
DMA 在后台跑,CPU 同时读写同一个缓冲区——这就是经典的数据一致性问题。我建议这样测:
| 测试场景 | 操作 | 预期结果 |
|---|---|---|
| CPU读,DMA写 | CPU 每 1ms 读取缓冲区,DMA 持续写入 | CPU 读到完整数据,无撕裂 |
| CPU写,DMA读 | CPU 更新缓冲区头部,DMA 持续发送 | DMA 发送数据不包含半更新状态 |
| Cache一致性 | 使能 CPU Cache,DMA 传输 4KB | Cache 刷新/无效化操作正确 |
3.3 异常时序注入测试
这是验证中最有意思的部分。正常时序谁都会测,但异常时序才是真正考验设计鲁棒性的地方。说白了,就是故意给 SPI 接口喂「坏数据」,看它会不会崩溃。
3.3.1 时钟异常
- 时钟抖动: 在 SPI 时钟上叠加 ±5% 的随机抖动,看数据采样是否稳定
- 时钟毛刺: 在时钟上升沿附近插入一个窄脉冲,检查 SPI 状态机是否误触发
- 时钟暂停: 传输过程中暂停时钟 10μs 再恢复,看能否继续
3.3.2 片选异常
片选信号(CS)是 SPI 的「开关」,它出问题后果很严重:
- CS 提前拉高: 传输 8 个时钟后 CS 拉高,只传输了 1 个字节
- CS 延迟拉高: 传输完成后 CS 延迟 100ns 才拉高
- CS 毛刺: 传输过程中 CS 出现 5ns 的毛刺
- CS 悬空: CS 引脚未驱动,处于高阻态
3.3.3 数据线异常
MOSI 和 MISO 是数据命脉。我习惯这样注入异常:
// 异常注入示例:在传输第5个字节时翻转MISO
for (byte_idx = 0; byte_idx < 10; byte_idx++) {
if (byte_idx == 5) {
// 强制将MISO拉高一个时钟周期
force_miso_high();
delay_one_spi_clock();
release_miso();
}
spi_transfer_byte(tx_data[byte_idx]);
check_rx_data(rx_data[byte_idx]);
}
3.4 边界条件测试
边界条件是最容易漏掉的,但也是 bug 最密集的地方。你想想看,设计者通常只考虑正常情况,边界上的「万一」往往被忽略。
3.4.1 传输长度边界
| 边界类型 | 测试值 | 关注点 |
|---|---|---|
| 最小长度 | 0 字节、1 字节 | 空传输是否触发异常 |
| FIFO 深度边界 | FIFO 深度 -1、FIFO 深度、FIFO 深度 +1 | FIFO 满/空标志是否正确 |
| DMA 描述符边界 | 描述符链表长度 1、2、最大值 | 链表遍历是否正常 |
| 最大长度 | 65535 字节(16位长度字段最大值) | 长度计数器是否溢出 |
3.4.2 时钟频率边界
SPI 通常支持分频系数 2、4、8、16、32、64、128、256。边界测试要覆盖:
- 最高频率: 分频系数 2,看 setup/hold 时间是否满足
- 最低频率: 分频系数 256,看是否有超时问题
- 动态切换: 传输过程中切换分频系数,看状态机是否错乱
3.4.3 模式切换边界
SPI 有 4 种模式(CPOL/CPHA 组合)。很多芯片支持运行时切换模式,但切换瞬间最容易出问题:
- 模式 0 → 模式 1:时钟极性变化,当前传输是否受影响
- 模式 2 → 模式 3:数据采样沿变化,已发出的数据是否有效
- 全模式遍历:连续切换 1000 次,看是否有累积错误
3.5 知识体系总览
下面这张图把 SPI 验证测试的四个维度串起来了。你可以把它当作测试用例设计的检查清单:
3.6 测试用例设计清单
最后,我整理了一份测试用例清单。每次做 SPI 验证时,我都会对着这个清单打勾:
- 寄存器测试(10个用例): 全地址遍历、数据线 0xAA/0x55、域位逐位、写保护、复位默认值、保留位检查、多字节连续读写、地址递增、随机地址、边界地址(0x00 和 0xFF)
- DMA测试(8个用例): 单次 1/4/64/1024 字节、循环 256 字节 10 秒、描述符链表 1/2/最大值、CPU 并发读写、Cache 一致性、DMA 暂停/恢复、DMA 超时、DMA 错误处理
- 异常时序测试(12个用例): 时钟抖动 ±5%、时钟毛刺 2ns、时钟暂停 10μs、CS 提前/延迟/毛刺/悬空、MOSI 毛刺、MISO 毛刺、数据线短路模拟、电源抖动、地弹噪声、多从设备冲突、模式切换毛刺、复位时序异常
- 边界条件测试(8个用例): 0 字节传输、1 字节传输、FIFO 深度 -1/+1、最大长度 65535、最高/最低频率、频率动态切换、模式 0→1→2→3 遍历、连续模式切换 1000 次