第三章 随机化基础:$urandom、$random、rand与randc、约束块基础
3.1 为什么需要随机化?
做验证的,谁没被定向测试坑过?
我刚入行那会儿,写测试用例都是手填数据。一个地址写死,一个数据写死。跑一百遍,结果都一样。后来项目复杂度上来了,这种写法根本覆盖不到边界情况。说白了,你永远不知道芯片会在哪个犄角旮旯出问题。
随机化就是干这个的。让工具帮你生成各种合法、非法的输入组合。你只需要定好规则,剩下的交给随机数发生器。
嗯,这里要注意:随机不是乱来。随机化是有约束的随机。我们后面会细说。
3.2 SystemVerilog 里的随机函数
先聊聊最基础的几个随机函数。它们就像工具箱里的螺丝刀,各有各的用法。
$random 和 $urandom
$random 是 Verilog 时代的老将。每次调用返回一个 32 位有符号整数。范围是 -2^31 到 2^31-1。
reg [31:0] data;
data = $random; // 可能得到负数
我个人不太喜欢 $random。为什么?因为它会生成负数。在验证里,负数地址、负数长度,很多时候是非法场景。你还要额外处理符号位,麻烦。
$urandom 就好多了。无符号,32位,0 到 2^32-1。干净利落。
reg [31:0] addr;
addr = $urandom; // 全是正数
我在项目中遇到过一个问题:用 $random 生成数据包长度,结果跑出来一堆负数长度。仿真直接崩了。从那以后,我统一改用 $urandom。
addr = $urandom % 256; // 生成 0~255 的地址
$urandom_range
这个函数更贴心。直接指定上下界。
data = $urandom_range(100, 200); // 生成 100~200 之间的数
注意参数顺序:最大值在前,最小值在后。写反了也不会报错,但结果可能不是你想要的。
3.3 rand 与 randc:类成员的随机化
函数调用虽然方便,但在复杂场景下不够用。我们需要把随机变量封装到类里。
这时候就该 rand 和 randc 出场了。
rand:普通随机
每次随机化时,变量在合法范围内独立取值。可能出现重复值。
class Packet;
rand bit [7:0] length;
rand bit [31:0] data;
endclass
Packet p = new();
p.randomize(); // length 和 data 同时被随机化
每次调用 randomize(),length 和 data 都会重新取值。可能连续两次取到相同的值。
randc:循环随机
randc 保证在所有可能值遍历完之前,不会重复。就像一副扑克牌,抽完一轮才洗牌。
class Command;
randc bit [1:0] opcode; // 0,1,2,3 轮流出现
endclass
我记得有一次验证一个状态机,需要测试所有命令的排列组合。用 randc 就特别合适。它天然保证了覆盖率。
3.4 约束块基础
随机化如果没有约束,那就是瞎蒙。约束块就是给随机化加上规矩。
约束块的写法
class MyData;
rand bit [7:0] addr;
rand bit [7:0] data;
constraint addr_range {
addr inside {[0:100]}; // 地址范围 0~100
}
constraint data_valid {
data != 8'h00; // 数据不能为0
data != 8'hFF; // 数据不能为全1
}
endclass
约束块写在类内部,用 constraint 关键字声明。可以写多个约束块,也可以写在一个块里。我个人习惯按功能拆分,这样好维护。
约束的常用操作符
| 操作符 | 说明 | 示例 |
|---|---|---|
| inside | 值在集合内 | addr inside {[0:100], 200, 255} |
| dist | 概率分布 | len dist {1:=10, [2:5]:=20} |
| if-else | 条件约束 | if (mode == 1) { len == 8; } |
| -> | 蕴含约束 | mode == 1 -> len == 8; |
你想想看,如果没有约束,随机出来的地址可能是 0xFFFFFFF。这在真实芯片里根本不存在。约束就是把这些不可能的情况排除掉。
约束的求解过程
约束求解器会做两件事:
- 检查所有约束是否一致(有没有矛盾)
- 在所有合法解中随机选一个
如果约束写矛盾了,比如同时要求 addr > 100 和 addr < 50,求解器会报错。我曾经在调试一个 bug 时,花了半天才发现是约束冲突。从那以后,我每次写完约束都会先跑一遍空仿真,确认没有解算失败。
3.5 知识体系总览
下面这张图把本章的核心内容串起来了。你可以把它当作一个快速索引。
3.6 避坑指南
讲几个我踩过的坑,你遇到了能少走弯路。
我曾经写过一个约束,要求地址既是偶数又是奇数。求解器直接罢工。检查约束时,记得看看有没有逻辑矛盾。
32位的 randc 变量,遍历一轮需要 40 亿次随机化。仿真跑一天都跑不完。小范围用 randc,大范围用 rand。
声明了 rand 变量,但没调 randomize()。变量值永远是 X 或 0。这种 bug 很难查,因为代码看起来没问题。
3.7 小结
随机化是验证的基石。从简单的 $urandom 到带约束的 rand 变量,每一步都让测试更智能。
记住:随机不是目的,覆盖才是。约束就是帮你把随机引导到正确的方向上。
下一章我们会深入约束的高级用法。到时候再聊。