rand/randc/constraint 练习

第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抽扑克牌,抽过的牌不会立刻再抽到,直到全抽完。

使用流程

  1. rand/randc 声明变量。

  2. 调用 obj.randomize() 生成随机值。

  3. 检查返回值: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;

  • 约束:

    1. addr 在 8'h10 ~ 8'h20。

    2. addr != 8'h15(排除15)。

    3. 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;

做三件事:

  1. 正常随机5次。

  2. 关闭 addr 的随机(addr.rand_mode(0)),把addr设为8'hFF,再随机5次,看addr是否不变。

  3. 关闭整个约束(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[]; // 动态数组

  • 约束:

    1. length 在 64 ~ 1500 之间。

    2. payload.size() == length;(数组大小等于length)

    3. dst_mac != src_mac;

    4. 每个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]} 用冒号
相关推荐
Salt & Light22 天前
UVM 字段自动化 Field Automation:让打印、复制、比较自动化
学习·systemverilog·uvm
北方孤寂的灵魂2 个月前
关于AXI协议详解
verilog·systemverilog·sv·数字验证
Salt & Light2 个月前
SV基础之连接设计和测试平台
systemverilog
fei_sun3 个月前
【SystemVerilog】连接设计和测试平台(待补充)
systemverilog
fei_sun3 个月前
【SystemVerilog验证】数据类型(待补充)
数据结构·systemverilog
不会武功的火柴3 个月前
SystemVerilog语法(11)-面向对象编程下篇
面向对象·fpga·systemverilog·ic验证
Ether IC Verifier3 个月前
SystemVerilog 数据类型详解
php·systemverilog·uvm·ic验证
Ether IC Verifier4 个月前
IC 验证工程师新手入门指南
systemverilog·ic验证·dpu
Nick.Q4 个月前
Ubuntu 24.04 从零跑通 OpenTitan:IC 验证工程师实录(Verilator + VCS + Verdi)
linux·ubuntu·systemverilog