CoolDA 设计仿真(五):烟测不是跑命令,而是验证契约
前言
很多工程报告会把 smoke test 写成“跑了几条命令,看到 PASS”。这种写法对没有环境的读者不友好,也讲不出测试设计的价值。
更好的讲法是:smoke test 在验证跨层契约。CoolDA 这种 SoC demo 的 smoke test 应该证明:
- 固件能构建;
- SoC 能仿真;
- shell 能接收脚本输入;
- runtime 能调度任务;
- BSP 能驱动 APB NPU;
- NPU 结果能对齐 reference;
- 仿真器能干净结束。
smoke test 的层次
一个好的 smoke test 不是单点测试,而是链路测试:
1 | |
这里每一层都可能坏:
| 层 | 可能问题 |
|---|---|
| firmware | 链接脚本、启动代码、符号缺失 |
| simulator | RTL/C++ 链接失败,模型接口变动 |
| shell | prompt 不出现,命令解析错 |
| runtime | tile 调度错,device heap 越界 |
| BSP | 寄存器 offset 错,busy/done 轮询错 |
| NPU RTL | 有符号乘法错,C 输出顺序错 |
| UART/script | 输出捕获错,命令注入乱序 |
smoke test 的任务就是用少量但高价值的断言覆盖这些层。
matmul 功能契约
矩阵乘法功能测试应该包含 CPU reference:
1 | |
NPU 路径则走:
1 | |
最后逐项比较:
1 | |
这个测试验证的不只是 4x4 硬件核,还验证 runtime 是否正确使用了硬件核。
runtime 内存契约
即使硬件没有问题,runtime 也可能出错。至少要检查:
malloc(0)应失败;- 超出 device heap 的分配应失败;
- host 指针和 device 指针不能混用;
- memcpy to device 要检查目标范围;
- memcpy to host 要检查来源范围;
- free 后的空间要标记为不用。
可以把 device heap 想成一个小数组:
1 | |
内存检查伪代码:
1 | |
这类检查让 runtime 更像一个真正的加速器运行时,而不只是几层函数封装。
event 契约
如果 runtime 提供 async/event,smoke test 应该验证事件状态:
1 | |
以及错误情况:
1 | |
一个 event poll 的核心契约是:
1 | |
测试不需要读者看终端输出。文章里应该讲清楚:每次 poll 推进一个 tile step,所有 step 完成后结果必须与 CPU reference 一致。
vecadd 为什么也能出现在 smoke 里
如果当前硬件只实现 matmul,而 vecadd 仍由 CPU reference 实现,那 vecadd 测试还有意义吗?
有。它验证的是 runtime API 和 device memory 路径,而不是 RTL kernel。
vecadd job 很简单:
1 | |
reference:
1 | |
它能覆盖:
- device allocation;
- memcpy to device;
- launch API;
- memcpy to host;
- result compare;
- benchmark 框架。
所以文章里要说清楚:vecadd 测的是 runtime 形状,不代表已经有 vecadd RTL accelerator。
simulator 完成契约
自动测试还需要知道什么时候结束。
如果仿真器通过 shell prompt 注入命令,就应该有这样的契约:
1 | |
这比“睡眠一段固定时间再输入下一条”可靠得多。
伪代码:
1 | |
这里验证的是 simulator、UART 捕获、shell prompt 和脚本注入之间的同步。
benchmark 要克制解读
smoke test 里可以包含 benchmark 入口,但不要把它写成真实性能结论。
原因很简单:
- Verilator 时间不是芯片真实时间;
- 4x4 寄存器喂数小核不代表最终 NPU 性能;
- 没有 DMA 时,CPU 参与数据搬运成本很高;
- 裸机 timer 精度可能影响短 benchmark;
- simulator 编译选项也会影响 host 侧耗时。
benchmark 在这个阶段更适合做回归信号:同一实现下,性能数字是否异常漂移。它不是最终性能报告。
小结
CoolDA smoke test 的重点不是“跑命令”,而是验证契约:
1 | |
到这里,CoolDA 系列也形成了闭环:CPU 到 NPU 的系统路径、APB 寄存器契约、BSP/runtime 分层、xOS/Verilator 交互、跨层测试契约。后续要加 DMA、命令队列或中断,也可以沿着这些层逐步扩展,而不是推倒重来。