一、本章概述
1. 本章学习目标
本章是 SystemVerilog 约束随机测试完整核心章节,全书共 45 张配图分层讲解随机语法、约束、回调、内联随机、软硬约束、数组随机、种子管理、工程架构,承接第五章面向对象类基础,是 UVM 激励生成底层核心。 完整学习目标:
- 区分
rand/randc,掌握静态 / 动态数组、队列的随机修饰规则; - 掌握
randomize()返回逻辑、SV_RAND_CHECK工程校验宏; - 吃透全部约束语法:
inside、蕴含->、双向<->、dist权重、solve before优先级; - 区分硬约束 hard constraint、软约束 soft constraint,掌握内联约束
with; - 学会
randomize with()内联随机、std::randomize()局部无类随机; - 掌握随机回调函数
pre_randomize()/post_randomize()执行时机与工程用法; - 理解约束函数、约束块重载、子类重写父类约束的语法;
- 掌握
rand_mode()/constraint_mode()进阶开关、局部关闭约束; - 数组、动态队列、结构体的约束随机写法,遍历类 randc 数组特性;
- 对象独立 PRNG 随机流、
srandom()种子控制、多线程随机隔离; - Cfg 分层配置类工业标准架构,约束冲突排查、随机失败调试方法。
2. 核心术语对照表
| 术语 | 全称 | 工程作用 |
|---|---|---|
| CRT | Constraint Random Test 约束随机测试 | 自动生成海量合法激励,替代定向用例 |
| rand | 普通随机变量 | 每次随机独立取值,允许重复 |
| randc | 周期随机变量 | 遍历全部取值后循环,保证全覆盖 |
| Hard Constraint | 硬约束 | 强制满足,冲突直接 randomize 失败 |
| Soft Constraint | 软约束 | 优先满足,无解时自动放弃,不会报错 |
| Inline Randomize | 内联随机 | 单次随机临时覆盖约束,不修改类原生代码 |
| PRNG | 伪随机数发生器 | 每个类实例独立随机流,互不干扰 |
| Pre/Post Randomize | 随机前后回调函数 | 随机前初始化、随机后数据修正、打印 |
| std::randomize | 局部随机函数 | 无需定义 class,直接随机局部变量 |
二、随机变量基础
1. rand 基础随机变量
特性:每次randomize()独立生成数值,无遍历逻辑,允许重复;适用于数据包、随机延时、无全覆盖要求信号。
class Packet; rand bit [7:0] data; // 普通随机字节 rand bit [3:0] delay; endclass2. randc 周期随机变量
核心规则:
- 缓存变量全部合法取值,一轮内不重复;
- 全部数值遍历完成后,重置序列重新循环;
- 若取值集合(数组、枚举)动态修改,遍历序列立刻清空重置。 适用场景:指令枚举、通道号、端口选择,保证功能全覆盖。
typedef enum {RD,WR,ERR,IDLE} cmd_e; class Trans; randc cmd_e cmd; // 循环遍历4种指令,不重复 endclass3. 数组 / 动态队列 rand/randc 修饰
- 定长数组:
rand bit [7:0] payload[8];数组每个元素独立随机 - randc 动态数组:
randc bit [2:0] port[$];队列内数值全局遍历 - 静态变量禁止 rand/randc 修饰:静态变量全局共享,随机流冲突
4. rand vs randc 完整对比表
| 类型 | 取值规则 | 动态数组行为 | 适用场景 | 缺陷 |
|---|---|---|---|---|
| rand | 独立随机,可重复 | 元素互不影响 | 数据载荷、延时 | 无法全覆盖场景 |
| randc | 一轮无重复,遍历循环 | 整个队列统一遍历 | 枚举、指令、通道 | 数组修改重置序列 |
三、randomize () 函数与随机校验宏
1. randomize () 返回规则
- 返回 1:全部硬约束满足,随机成功;
- 返回 0:硬约束冲突、无解、无 rand 变量、约束矛盾;
- 禁止在
new()构造函数内调用randomize(),破坏种子初始化。
2. SV_RAND_CHECK 工程校验宏
随机失败自动打印文件名、代码行号,直接终止仿真,快速定位约束冲突。
`define SV_RAND_CHECK(r) do begin if(!(r)) begin $display("%s:%0d 随机化失败",`__FILE__,`__LINE__); $finish(1); end end while(0) // 使用示例 Trans tr = new(); `SV_RAND_CHECK(tr.randomize());3. std::randomize () 局部无类随机
无需创建 class,直接随机函数内局部变量,适合简单临时激励。
bit [15:0] addr; if(!std::randomize(addr) with {addr inside {[0:1024]};}) begin $error("局部随机失败"); end四、约束完整语法体系
1. inside 范围约束
限定变量合法取值,支持连续区间、离散集合、取反排除
constraint c_len { pkt_len inside {[16:1024]}; // 连续区间 port inside {0,1,3,5}; // 离散端口 !(addr inside {[512:1024]});// 取反,排除区间 }2. 条件蕴含约束->/<->
A -> B:A 成立时 B 必须满足;A 不成立无限制<->双向等价:A、B 真假完全同步
constraint c_write { // 写使能为1时,地址最高位必须为1 wr_en -> addr[31]==1'b1; // 双向等价:err信号和cmd==ERR同时生效 err <-> (cmd == ERR); }等价 if 写法:
constraint c_write { if(wr_en) addr[31]==1'b1; }3. dist 权重概率约束
两种分配符,控制不同数值出现概率,用于构造异常测试场景
:=:区间内每个数值分配对应权重:/:整个区间整体分配权重,内部均分
rand bit [2:0] cmd; constraint c_dist { cmd dist { WR := 10, RD := 10, ERR := 2 // 错误指令低概率,边界测试 }; }4. solve...before 求解优先级
仅改变随机组合出现概率,不会新增 / 删除合法解,增加仿真运算开销,谨慎使用。
solve cmd before data; // 优先随机cmd,再求解data5. 并行约束核心特性
约束块内所有表达式并行同时求解,不存在代码上下执行顺序,不能用顺序逻辑书写约束。
五、硬约束 vs 软约束
1. Hard Constraint 硬约束
所有常规约束均为硬约束,求解器必须满足;若存在冲突,randomize()直接返回 0,随机失败。
2. Soft Constraint 软约束soft
语法:soft 表达式;规则:优先满足软约束;若无同时满足软硬约束的解,求解器自动放弃软约束,仅保证硬约束成立,不会随机失败。 典型用途:期望数值、推荐延时、优选地址,不强制卡死。
constraint c_delay { soft delay inside {[1:10]}; // 优先1~10,无解则允许任意值 pkt_len inside {[8:2048]}; // 硬约束,必须遵守 }六、内联随机 with 临时约束
无需修改类内部原生约束,单次随机临时叠加 / 覆盖约束,灵活生成特殊测试用例。
1. randomize () with 基础用法
Packet pkt = new(); // 本次随机强制data=0xff,仅单次生效,不修改类约束 `SV_RAND_CHECK(pkt.randomize() with {data == 8'hff;});2. 内联约束覆盖原有 dist 权重
tr.randomize() with { cmd dist {ERR:=20}; }; // 临时拉高错误指令概率七、随机回调函数 pre_randomize /post_randomize
1. 完整执行时序(随机执行完整流程)
randomize()调用 → pre_randomize () 执行 → 求解约束随机变量 → post_randomize () 执行 → 返回结果
2. pre_randomize()
随机前预处理:清空队列、初始化标记、重置状态
virtual function void pre_randomize(); super.pre_randomize(); payload.delete(); // 随机前清空数据包队列 endfunction3. post_randomize()
随机后数据修正、格式对齐、打印随机信息、计算衍生信号
virtual function void post_randomize(); crc = calc_crc(data); // 根据随机data计算校验值 $display("随机数据包 data=%h",data); endfunction关键特性:子类可重写回调函数,必须调用super.xxx()保留父类逻辑
八、约束控制开关:rand_mode & constraint_mode
1. rand_mode () 变量随机开关
控制单个 rand 变量是否参与随机;关闭后变量值固定,仅校验约束。
obj.var.rand_mode(1):开启(默认)obj.var.rand_mode(0):关闭随机
pkt.data.rand_mode(0); // data不再随机,固定原值 pkt.randomize();2. constraint_mode () 约束块开关
全局 / 单独关闭某一组约束块,快速切换正常 / 边界测试场景
tr.c_err.constraint_mode(0); // 关闭错误指令约束,只生成正常激励九、约束重载与子类重写约束
- 子类可定义同名约束块,直接覆盖父类约束逻辑;
- 父类约束无法删除,仅能重写或通过
constraint_mode(0)关闭;
class ErrTrans extends Trans; constraint c_cmd { cmd == ERR; } // 重写父类cmd约束,只生成错误指令 endclass十、数组、动态队列约束随机
- foreach 循环约束数组每个元素范围;
- 动态队列可约束长度、内部元素取值;
rand bit [7:0] payload[$]; constraint c_pkt { payload.size() inside {[4:16]}; // 队列长度约束 foreach(payload[i]) payload[i] inside {[0:255]}; }十一、独立 PRNG 随机流与种子管理
1. 独立随机流机制
每个 class 实例、并行 fork 线程自带独立 PRNG;A 对象随机操作完全不影响 B 对象,多端口并行激励无干扰。
2. srandom () 手动固定种子
为对象单独指定种子,固定随机序列,100% 复现仿真 bug 激励
Trans tr = new(); tr.srandom(6688); // 固定当前对象随机种子3. 层次化种子工程规范
顶层传入全局种子,所有子对象自动衍生独立种子;固定顶层种子,整套仿真激励完全可复现。
十二、Cfg 分层配置类工业完整架构
承接第六章工程实践章节,UVM config_db 底层原型,分离配置参数与激励生成,平台高复用。
1. 完整分层代码
// 1.配置类:封装所有可随机DUT参数 class EthCfg; rand bit [3:0] port_en; rand bit [47:0] mac[4]; rand uint frame_num; constraint c_mac { foreach(mac[i]) mac[i][41:40]==2'b00; } constraint c_frame { frame_num inside {[1:100]}; } endclass // 2.环境类:管理配置、激励发生器 class Env; EthCfg cfg; EthGen gen[4]; function new(); cfg = new(); endfunction function void gen_cfg(); `SV_RAND_CHECK(cfg.randomize()); endfunction function void build(); foreach(gen[i]) gen[i] = new(); endfunction task run(); foreach(gen[i]) if(cfg.port_en[i]) fork gen[i].run(); join_none endtask endclass // 顶层测试程序 program test; Env env; initial begin env = new(); env.gen_cfg(); env.cfg.port_en = '1; // 手动覆盖配置参数 env.build(); env.run(); end endprogram2. 工程强制规范
所有 Cfg 参数修改必须在build()函数执行前,build 完成后驱动实例已创建,修改失效。
十三、随机化失败调试、约束冲突排查
1. 常见失败原因
- 硬约束互相矛盾,无合法解;
- randc 数组动态修改,遍历序列清空导致无解;
- solve before 优先级过度限制,无匹配组合;
- 约束内变量未加 rand/randc 修饰。
2. 调试方法
- 使用
SV_RAND_CHECK定位报错代码行; - 临时关闭部分约束块(constraint_mode (0))二分定位冲突约束;
- 改用软约束,规避无解场景;
- 打印 pre/post 随机函数,查看变量初始值干扰。
十四、本章全部易错点汇总
- randc 数组、队列修改元素后,遍历序列直接重置;
- 约束块并行求解,不能按上下顺序书写依赖逻辑;
- solve before 仅改变概率,无法增减合法解,仿真速度下降;
- soft 软约束仅在硬约束无解时自动失效,不会触发随机失败;
- 内联 randomize with 仅单次生效,不永久修改类约束;
- pre/post_randomize 重写必须调用 super 父类函数,否则父类逻辑丢失;
- Cfg 配置参数修改必须放在 build 阶段之前;
- 全局共用 PRNG 会造成多通道激励互相干扰,必须使用对象独立种子。