SV 约束 inside/dist:集合成员、权重分布与条件约束
更新时间:2026-09-25。本文是「集成电路大类 · 芯片设计 · 数字验证」序列里的一篇长尾实操文。在 受约束随机验证 CRV:rand、constraint、solve before 与 seed 复现 里,我们已经能用
constraint把随机变量框在合法区间内。实际写激励时还有三个高频小动作:把取值限定在一组离散值里(inside)、让某些值出现得更频繁(dist)、按模式开关切换约束(->与if)。这一篇把这三件事的精确语义讲透,更系统的进阶话题见 进阶随机化:dist 权重、蕴含、双向约束、soft 与内联约束。
本文要回答的问题
inside后面能跟哪些东西?取反怎么写才不会踩优先级的坑?dist的:=和:/只差一个斜杠,权重到底差在哪?mode == BIG -> len inside {...}用蕴含写,和用if-else写,解出来的合法空间一样吗?- 哪些写法会让约束静默失效,甚至直接导致随机化失败?
一、inside:集合成员约束
inside 表达「变量必须是某个集合的成员」,集合可以是值的枚举、区间,甚至另一个变量数组:
typedef enum {BYTE, WORD, DWORD} size_e;
rand size_e sz;
rand int addr;
rand int pick;
int allow[] = '{3, 7, 11};
constraint c_size {
sz inside {WORD, DWORD}; // 枚举值:排除 BYTE
addr inside {[32'h0000_0000 : 32'h7FFF_FFFF]}; // 区间
addr inside {[lo : $]}; // $ 表示该类型最大值
pick inside {allow}; // 变量集合:成员随 allow 内容变化
}区间 [a:b] 是闭区间,两端都可取;$ 在区间里代表变量类型的上限,[$:b] 则代表从类型下限开始。变量集合是 inside 最灵活的用法——集合内容不用写死在约束里,测试用例可以通过改数组来改合法空间,不必动约束块本身。
集合里可以混写单值和多个区间,重复列出同一个值不会报错,但在 dist 之外对结果也没有影响;成员判断按值比较,区间是闭区间。对四态变量还有个容易被忽略的细节:inside 比较时把值里的 x、z 位当通配符处理,所以它适合做译码类匹配,但如果你的本意是精确比较,比较前要先确认信号里没有 x/z。
取反只有一种推荐写法:!(x inside {...})。不要凭直觉写 x !inside,SystemVerilog 没有这个运算符;取反的感叹号必须罩住整个 inside 表达式。下面两行对比,第一行是「x 不在集合里」,第二行由于优先级问题含义会偏离意图,写的时候括号不能省:
!(x inside {[16'hF000 : 16'hFFFF]}); // 正确:避开整段高地址
!(x) inside {...} // 错误写法,语义不是你想要的取反后的集合语义上等于类型全集减去列出的成员,当类型范围很大时,这比把剩余合法值逐一罗列省事得多,也少出遗漏。
二、dist::= 每值权重,😕 组内平分
dist 决定的是概率,不是合法性:列在 dist 里的值都合法,只是被抽中的机会不同。权重不需要加起来等于 100,每个值的概率等于它的权重除以总权重。两个运算符的区别只在区间上体现,一句话概括::= 把权重发给区间里的每个值,:/ 把权重发给整个区间再平分。
以 x 取值 0..3 为例,两种写法对比如下:
// 写法 A::= 每个值各拿 50
x dist {0 := 50, [1:3] := 50};
// 写法 B::/ 0 独拿 50,区间 [1:3] 整组拿 50 再三人均分
x dist {0 :/ 50, [1:3] :/ 50};| 取值 | 写法 A 权重(:=) | 写法 A 概率 | 写法 B 权重(😕) | 写法 B 概率 |
|---|---|---|---|---|
| 0 | 50 | 25% | 50 | 50% |
| 1 | 50 | 25% | 50/3 | 16.67% |
| 2 | 50 | 25% | 50/3 | 16.67% |
| 3 | 50 | 25% | 50/3 | 16.67% |
| 合计 | 200 | 100% | 100 | 100% |
写法 A 的区间有 3 个值,每个值都领到完整的 50,总权重 200,四个值等概率;写法 B 的区间作为一个组只领 50,组内三人均分,于是 0 一个值占了一半概率。下图把同一个区间在两种运算符下的权重切分画了出来,这正是一个斜杠带来的全部差别:
读图时盯住区间 [1:3]:上排每个值都是完整的一份 50,四个格子等高;下排区间三个人合起来才是一份 50,0 那一格相当于三个格子的分量。还有一个混搭的实用结论——x dist {0 := 10, [1:3] :/ 30} 里,区间组拿 30 再三人均分,每个值恰好 10,结果四个值又变成等概率。记不清语义时,照这个例子把总权重算一遍即可。
两个补充约定:权重缺省为 1,x dist {0, [1:3]} 里每个值权重都是 1,等价于等概率;权重只接受整型表达式,写 3.5 这样的小数权重是非法的。另外 dist 只是众多约束中的一条,它给出的概率是「在所有硬约束划出的合法空间之内」的条件分布——先有别的约束把某些值排除,剩余值再按权重抽签,所以同一个 dist 配不同的伴随约束,实际分布会不同。
三、-> 蕴含与 if-else:前提不满足时约束去哪了
条件约束有两种写法,行为差别集中在「前提不成立」时:
constraint c_imp {
mode == BIG -> len inside {[100:200]}; // 蕴含
}
constraint c_if {
if (mode == BIG)
len inside {[100:200]};
else
len inside {[1:99]}; // 两个分支都有约束
}蕴含 P -> Q 的语义是:前提 P 成立时后件 Q 必须成立;P 不成立时整条约束关闭,后件完全自由。上例中 mode != BIG 时,len 取 50、取 1000 都可以,求解器对它没有任何限制。而 if-else 给两个分支都配了约束,无论 mode 是什么,len 都被限定在有界范围内。下图对比同一次求解在两条路径上的结果:
图中红色路径就是两者的分水岭:蕴含在前提不成立时让约束彻底消失,而 if-else 的 else 分支仍然兜住取值范围。另一个差异在反向求解:约束是双向的,if-else 下先定 len 再反推 mode 时,每个 len 都能且只能对应一个分支;蕴含下当 len 落在 [100:200] 之外时,求解器只能让前提为假,可一旦 len 在区间内,mode 取什么都不矛盾——合法空间比直觉大。顺带一提,不带 else 的 if 约束与蕴含等价,所以补齐 else 不是风格问题,是合法空间问题。需要让「常规模式下宽松、特殊模式下收紧」时,记得问自己一句:剩下的模式你真的打算放任不管吗?
四、常见坑
| 坑 | 表现 | 处理 |
|---|---|---|
| 空集合 | x inside {} 恒为假,随机化直接失败 | 保证集合非空;用变量数组时先判长度 |
| 区间反向 | 集合由变量拼出,运行时 a > b,[a:b] 为空 | 约束里补 a <= b,或构造后断言 |
| 权重为 0 | 该值永远不会被 dist 抽中,相当于偷偷删合法值 | 想排除值就别列进集合;想「保留但罕见」用极小正权重 |
| 总权重为 0 | 所有项权重都是 0,无值可抽,随机化失败 | 至少保证一个正权重 |
| 权重依赖 rand 变量 | 权重本身参与求解,分布不可预期 | 权重只用非随机变量、参数或 $urandom 之外的确定表达式 |
| 误把 dist 当硬约束 | 边界场景该有的值因为权重小长时间不出 | 分布负责概率,合法性边界另写 inside 约束或定向用例补 |
权重为 0 还有个细节值得单独说:它只影响 dist 自己的抽签——如果另一条硬约束把变量强制到这个值,求解器仍可能给出该值以满足硬约束。所以「权重 0」表达的是「在这条分布里不抽它」,不是「这个值非法」;要表达非法,请把值移出集合或用互补约束。分布与合法性各司其职,验证激励的可控性才不会在回归里悄悄走样,配合 seed 管理复现问题的做法见 SystemVerilog 随机稳定性:RNG 层次、播种与失败用例可复现。
这一篇在知识体系里的位置
constraint、rand与solve before的基础见 受约束随机验证 CRV:rand、constraint、solve before 与 seed 复现;soft 约束、内联约束与双向约束的系统讲解见 进阶随机化:dist 权重、蕴含、双向约束、soft 与内联约束;分布是否被真正覆盖,要用功能覆盖检验,见 代码覆盖率与功能覆盖率:line/cond/toggle/FSM 与 covergroup 收敛;失败用例的复现机制见 SystemVerilog 随机稳定性:RNG 层次、播种与失败用例可复现。
总结
inside 把变量限定在枚举、区间或变量数组构成的集合内,取反统一写 !(x inside {...});dist 只管概率不管合法性,:= 让区间内每个值各拿完整权重、:/ 让整组权重在区间内平分,用「权重除以总权重」即可算出精确概率;-> 蕴含在前提不成立时整条约束失效、后件自由,if-else 两个分支始终兜住取值且支持双向反推,无 else 的 if 与蕴含等价;最后守住空集合、反向区间、权重为 0 和总权重为 0 这四个高频坑,把「分布」和「合法边界」分别表达,随机激励才既可控又可复现。