第0部分:先理解"为什么要随机"
比喻:
-
你测一个加法器,只测
1+1=2,那DUT的bug永远发现不了。 -
验证的目标是用最少的测试,覆盖最多的场景。
-
所以要让输入随机 ,比如随机生成1000组
a+b,覆盖各种边界。
SystemVerilog内建了随机化,是UVM transaction的核心能力。
第1部分:rand 和 randc(基础)
知识点
| 关键字 | 含义 | 例子 |
|---|---|---|
rand |
每次随机,可能连续相同 | 随机0~255 |
randc |
循环随机,遍历所有值后才重复 | 4位会0~15各出现一遍 |
比喻:
-
rand像掷骰子,可能连续掷出3。 -
randc像抽扑克牌,抽过的牌不会立刻再抽到,直到全抽完。
使用流程:
-
用
rand/randc声明变量。 -
调用
obj.randomize()生成随机值。 -
检查返回值:1=成功,0=失败。
训练一:rand基础
题目
定义一个类 Packet:
-
rand bit [7:0] addr; -
rand bit [7:0] data;
在tb里创建对象,随机10次,打印每次的addr和data。
你自己写(5分钟)
systemverilog
Copy
Download
// 在这里写
参考答案
systemverilog
Copy
Download
class Packet;
rand bit [7:0] addr;
rand bit [7:0] data;
endclass
module tb;
initial begin
Packet p = new();
repeat (10) begin
if (!p.randomize())
$error("randomize failed!");
$display("addr=%0d data=%0d", p.addr, p.data);
end
end
endmodule
老师点评
-
rand关键字加在变量前。 -
randomize()是内建方法,返回1/0。 -
永远检查返回值!UVM里如果randomize失败,transaction可能全是0,测试就废了。
-
每次randomize都是独立的,和上次无关。
训练二:randc循环随机
题目
定义一个类 Dice:
randc bit [2:0] face;// 0~7
随机20次,观察输出:前8次是不是0~7各出现一次?
参考答案
systemverilog
Copy
Download
class Dice;
randc bit [2:0] face;
endclass
module tb;
initial begin
Dice d = new();
repeat (20) begin
d.randomize();
$display("face=%0d", d.face);
end
end
endmodule
输出示例
text
Copy
Download
face=3
face=0
face=5
face=1
face=7
face=2
face=6
face=4 // 前8次0~7都出现了
face=2 // 第二轮开始
...
老师点评
-
randc保证每个值都出现过一遍,才进入下一轮。 -
UVM应用:比如测试DUT的8个通道,用randc保证每个通道都测到。
-
小白坑 :
randc位数多时(如32位),遍历一遍要很久,慎用。
第2部分:constraint(约束)------ 最核心
知识点
| 写法 | 含义 |
|---|---|
a inside {[1:10]} |
a在1~10之间 |
a inside {1,3,5} |
a是1、3或5 |
a > b |
大小关系 |
a dist {1:=3, 2:=1} |
权重分布,1的概率是2的3倍 |
a -> b |
蕴含:a为真时b必须真 |
(a==1) -> (b==2) |
条件约束 |
a % 4 == 0 |
取模 |
约束块语法:
systemverilog
Copy
Download
constraint 约束名 {
// 条件
}
训练三:范围约束
题目
Packet 类:
-
rand bit [7:0] addr; -
约束:
addr必须在 8'h10 ~ 8'h20 之间。
随机20次,确认所有addr都在范围内。
参考答案
systemverilog
Copy
Download
class Packet;
rand bit [7:0] addr;
constraint c_addr {
addr inside {[8'h10 : 8'h20]};
}
endclass
module tb;
initial begin
Packet p = new();
repeat (20) begin
p.randomize();
$display("addr=8'h%h", p.addr);
if (p.addr < 8'h10 || p.addr > 8'h20)
$error("Out of range!");
end
end
endmodule
老师点评
-
inside {[min:max]}是最常用约束,方括号表示闭区间。 -
UVM应用 :
constraint c { addr inside {[0:255]}; }。 -
也可以写
inside {8'h10, 8'h12, 8'h14}列举。
训练四:多条件约束
题目
Packet 类:
-
rand bit [7:0] addr; -
rand bit [7:0] data; -
约束:
-
addr在 8'h10 ~ 8'h20。 -
addr != 8'h15(排除15)。 -
data > addr。
-
随机10次,确认三个条件都满足。
参考答案
systemverilog
Copy
Download
class Packet;
rand bit [7:0] addr;
rand bit [7:0] data;
constraint c_all {
addr inside {[8'h10 : 8'h20]};
addr != 8'h15;
data > addr;
}
endclass
module tb;
initial begin
Packet p = new();
repeat (10) begin
if (!p.randomize()) begin
$error("randomize failed!");
continue;
end
$display("addr=%h data=%h", p.addr, p.data);
end
end
endmodule
老师点评
-
多个条件写在一个constraint块里,是"与"关系,全部满足。
-
addr != 8'h15排除一个值。 -
data > addr是关系约束,data会随机到大于addr的值。 -
UVM应用 :比如约束
payload.size() == length;。
训练五:inside列举 + 权重dist
题目
Packet 类:
-
rand int mode; -
约束:
mode只取1、2、3,且1的概率是2的2倍,2和3概率相同。
提示:用 dist {1:=2, 2:=1, 3:=1}。
随机100次,统计各值出现次数。
参考答案
systemverilog
Copy
Download
class Packet;
rand int mode;
constraint c_mode {
mode dist {1 := 2, 2 := 1, 3 := 1};
}
endclass
module tb;
initial begin
Packet p = new();
int cnt[4] = '{0,0,0,0}; // 统计用
repeat (100) begin
p.randomize();
cnt[p.mode]++;
end
for (int i = 1; i <= 3; i++)
$display("mode=%0d count=%0d", i, cnt[i]);
end
endmodule
期望输出(近似)
text
Copy
Download
mode=1 count=50
mode=2 count=25
mode=3 count=25
老师点评
-
dist {值 := 权重},权重可以理解为"相对概率"。 -
上面总权重 = 2+1+1 = 4,所以1占2/4=50%,2和3各25%。
-
也可以用
:/表示均分:dist {1:/2, 2:/1, 3:/1}语义不同(按范围均分),初学用:=即可。
训练六:条件约束(蕴含 ->)
题目
Packet 类:
-
rand bit is_write;// 0=读,1=写 -
rand bit [7:0] addr; -
rand bit [7:0] data; -
约束:
-
如果
is_write == 1,则data != 0(写不能写0)。 -
如果
is_write == 0,则data == 0(读时data无效,固定0)。
-
提示:用 (is_write == 1) -> (data != 0);
参考答案
systemverilog
Copy
Download
class Packet;
rand bit is_write;
rand bit [7:0] addr;
rand bit [7:0] data;
constraint c_rw {
(is_write == 1) -> (data != 0);
(is_write == 0) -> (data == 0);
}
endclass
module tb;
initial begin
Packet p = new();
repeat (10) begin
p.randomize();
$display("is_write=%0d addr=%h data=%h", p.is_write, p.addr, p.data);
end
end
endmodule
老师点评
-
A -> B读作"A蕴含B":A为真时,B必须为真;A为假时,B随意。 -
这是协议约束最常用写法!比如:
-
(kind == READ) -> (data == 0); -
(kind == WRITE) -> (data != 0);
-
-
小白坑 :
->不是"如果...就...",而是"若A真,则B必须真",A假时不管B。
第3部分:randomize with(临时约束)
训练七:临时加约束
题目
复用训练三的 Packet(约束 addr在0x10~0x20),但在tb里临时 强制 addr == 8'h18。
提示:p.randomize() with { addr == 8'h18; };
参考答案
systemverilog
Copy
Download
class Packet;
rand bit [7:0] addr;
constraint c_addr {
addr inside {[8'h10 : 8'h20]};
}
endclass
module tb;
initial begin
Packet p = new();
repeat (5) begin
p.randomize() with { addr == 8'h18; };
$display("addr=%h", p.addr); // 每次都是18
end
end
endmodule
老师点评
-
with {...}加临时约束,只在这次randomize里有效。 -
UVM应用 :
uvm_do_with(req, { req.addr == 8'h18; })就是它。 -
临时约束和类里约束同时生效,两者不能矛盾,否则randomize失败。
-
比如类里约束
addr>0x20,with里强制addr==0x18,就会失败。
第4部分:关闭/打开随机化
训练八:rand_mode和constraint_mode
题目
Packet 类:
-
rand bit [7:0] addr; -
rand bit [7:0] data; -
约束:
addr < 8'h20;
做三件事:
-
正常随机5次。
-
关闭
addr的随机(addr.rand_mode(0)),把addr设为8'hFF,再随机5次,看addr是否不变。 -
关闭整个约束(
constraint_mode(0)),随机5次,看addr是否超出0x20。
参考答案
systemverilog
Copy
Download
class Packet;
rand bit [7:0] addr;
rand bit [7:0] data;
constraint c { addr < 8'h20; }
endclass
module tb;
initial begin
Packet p = new();
$display("--- 正常随机 ---");
repeat (5) begin
p.randomize();
$display("addr=%h data=%h", p.addr, p.data);
end
$display("--- addr随机关闭 ---");
p.addr.rand_mode(0);
p.addr = 8'hFF;
repeat (5) begin
p.randomize();
$display("addr=%h data=%h", p.addr, p.data); // addr恒为FF
end
$display("--- 关闭约束 ---");
p.addr.rand_mode(1);
p.constraint_mode(0);
repeat (5) begin
p.randomize();
$display("addr=%h data=%h", p.addr, p.data); // addr可能≥20
end
end
endmodule
老师点评
-
变量.rand_mode(0/1):关闭/打开该变量的随机。 -
对象.constraint_mode(0/1):关闭/打开该对象所有约束。 -
对象.约束名.constraint_mode(0/1):关闭单个约束(注意语法)。 -
UVM应用:错误注入测试时,常常关掉某些约束,强制发异常包。
第5部分:综合大作业(模拟UVM transaction)
题目
写一个 EthPacket 类,模拟以太网包:
-
rand bit [47:0] dst_mac; -
rand bit [47:0] src_mac; -
rand bit [15:0] length;// 载荷长度 -
rand bit [7:0] payload[];// 动态数组 -
约束:
-
length在 64 ~ 1500 之间。 -
payload.size() == length;(数组大小等于length) -
dst_mac != src_mac; -
每个payload元素随机。
-
创建对象,随机5次,打印dst_mac、src_mac、length、payload.size()。
提示 :动态数组要在randomize前先payload = new[length];?不用! SV的约束会自己处理数组大小。直接写 payload.size() == length; 即可。
参考答案
systemverilog
Copy
Download
class EthPacket;
rand bit [47:0] dst_mac;
rand bit [47:0] src_mac;
rand bit [15:0] length;
rand bit [7:0] payload[];
constraint c_eth {
length inside {[64 : 1500]};
payload.size() == length;
dst_mac != src_mac;
}
endclass
module tb;
initial begin
EthPacket p = new();
repeat (5) begin
if (!p.randomize()) begin
$error("randomize failed");
continue;
end
$display("dst=%h src=%h len=%0d payload_size=%0d",
p.dst_mac, p.src_mac, p.length, p.payload.size());
end
end
endmodule
老师点评
-
动态数组随机化 :约束
payload.size() == length会自动分配数组。 -
UVM应用 :这就是UVM transaction的原型!
uvm_sequence_item里的rand字段就是这些。 -
payload.size()和length保持一致,是协议验证的经典约束。 -
小白坑 :动态数组的
size()约束,必须用payload.size() == length,不能直接写payload = new[length](那是在new里,不是约束)。
第6部分:作业检查表
完成以下8项,才算过关:
-
□ 训练一:会写rand类,检查randomize返回值
-
□ 训练二:理解randc循环随机
-
□ 训练三:会写inside范围约束
-
□ 训练四:会写多条件约束
-
□ 训练五:会写dist权重约束
-
□ 训练六:理解
->蕴含约束 -
□ 训练七:会用randomize with
-
□ 训练八:会rand_mode/constraint_mode
-
□ 综合大作业:EthPacket动态数组约束
第7部分:常见坑(背下来)
| 坑 | 后果 | 正确做法 |
|---|---|---|
忘了randomize() |
变量还是默认值0 | 每次都调用 |
| 不检查返回值 | 约束矛盾时静默失败 | if(!p.randomize()) $error |
a = b 写成 a == b |
约束块里要用== |
关系判断用== |
-> 理解错 |
约束不生效 | A真则B真,A假随意 |
| 动态数组忘了size约束 | 数组空,size=0 | arr.size() == len; |
inside写成[1,10] |
语法错 | inside {[1:10]} 用冒号 |