约束随机验证(constrained-random verification)
想象你要测试一台自动售货机。你可以手写一张清单——投入一美元、按下 B4、期待掉出一块糖——但这样你永远只能检查到自己想得到的那几种情况。真正咬人的缺陷往往是没人设想过的:有人同时塞进三枚硬币、一下按了两个按钮,还在同一秒里要求找零。约束随机验证把这套工作反了过来。你不再逐条手写每个测试,而是描述合法输入的规则——硬币必须是有效面额,选择必须对应真实的货道——然后让工具发射成千上万种全都遵守这些规则的随机组合,去搜寻那些没人会想到要写下来的边角情形。
更确切地说,它是一种激励生成策略:每个测试用例都从受约束界定的空间里随机抽取——这些声明式规则让随机值既合法又有意义。约束求解器会挑选同时满足每一条规则的值:一个落在范围内的地址、一个对该协议而言合法的报文长度、一种只有在另一字段被置位时才允许的突发类型。每次都换一个不同的随机种子,单个测试平台就能在多轮运行中遍历输入空间的一大片区域,到达定向测试永远偶然撞不进去的状态。
问题在于,单凭随机性并不能告诉你实际覆盖到了什么,所以这项技术从不单独使用。你会把它与功能覆盖率搭配,来衡量哪些场景真正被命中——并据此判断何时可以收手——再与断言搭配,在设计行为异常时自动报警;两者合在一起,就把一股随机用例的洪流变成了可衡量、能自检的验证活动。这正是现代 UVM 测试平台底层的引擎,在那里约束随机生成、覆盖率和检查正是大型 RTL 设计接受验证的标准方式。
class bus_txn;
rand bit [31:0] addr;
rand bit [7:0] len;
constraint legal { addr[1:0] == 2'b00; len inside {[1:64]}; } // word-aligned, bounded length
endclass一个事务类,其地址和长度都被随机化,但只在约束设定的合法范围之内——这里地址被强制按字对齐,长度被限制在范围之内。
这里的“随机”其实有点名不副实——这些值是伪随机的,而且完全可复现。用同一个种子重新运行会重放出一模一样的激励,正是这一点让团队能够可靠地复现并调试那些最初由某次随机运行揭露出来的缺陷。