4. 权重与分布:dist关键字、:=与:/操作符的区别、权重分配实战、常见陷阱

各位同学,咱们今天聊一个非常实用的话题——权重与分布。

做随机约束仿真,说白了就是让验证工具帮我们“掷骰子”。但问题来了:有时候我们希望某些值出现的概率高一些,某些值出现的概率低一些。这时候就需要用到 dist 关键字了。

我个人习惯把 dist 叫做“加权随机”。它不像 inside 那样均匀分布,而是允许你手动控制每个值的“中奖概率”。

4.1 dist 关键字的基本用法

dist 用在约束块里,后面跟一个分布列表。每个分布项由值和权重组成。看个例子:

class Packet;
    rand int addr;

    constraint c_addr {
        addr dist {
            0       := 1,
            [1:10]  := 5,
            [11:20] := 10
        };
    }
endclass

这个约束的意思是:addr 取 0 的概率是 1 份,取 1~10 的概率是 5 份,取 11~20 的概率是 10 份。注意,这里的“份”是相对权重,不是绝对概率。

嗯,这里要注意:dist 只能用于 rand 变量,不能用于 randc。我在项目中遇到过有人把 dist 用在 randc 上,结果仿真器直接报错。你想想看,randc 是循环随机,跟权重分布天生冲突。

4.2 := 与 :/ 操作符的区别

这是新手最容易搞混的地方。我刚开始学的时候也绕了半天。

:= 操作符:表示“每个值分配相同的权重”。

比如 [1:10] := 5,意思是 1 到 10 这 10 个值,每个值都分到 5 份权重。总权重就是 10 × 5 = 50 份。

:/ 操作符:表示“整个范围分配一个总权重,内部均匀分配”。

比如 [1:10] :/ 5,意思是 1 到 10 这个范围总共分到 5 份权重,每个值分到 5/10 = 0.5 份。

看个对比表格就清楚了:

写法 含义 每个值权重 总权重
[1:3] := 6 每个值 6 份 6 18
[1:3] :/ 6 总共 6 份 2 6

说白了,:= 是“按值分配”,:/ 是“按范围分配”。

我个人建议:如果你想让某个范围内的每个值概率差不多,用 :=;如果你只想控制整个范围的总概率,用 :/。我曾经在一个项目里用 :/ 给地址空间分配权重,结果发现地址分布太稀疏了,后来改成 := 才解决问题。

4.3 权重分配实战

光说不练假把式。咱们来一个实际场景:

假设你要验证一个网络包处理器。包的长度有几种情况:

  • 最小包(64字节):偶尔出现
  • 正常包(128~512字节):经常出现
  • 最大包(1518字节):偶尔出现
  • 超长包(1519~2000字节):极少出现

dist 可以这样写:

class Packet;
    rand int pkt_len;

    constraint c_len {
        pkt_len dist {
            64            := 1,
            [128:512]     := 10,
            1518          := 2,
            [1519:2000]   :/ 1
        };
    }
endclass

这里我用了 :/ 处理超长包范围,因为我不想让 1519~2000 这 482 个值每个都占 1 份权重——那样概率就太大了。用 :/ 1 表示整个范围只占 1 份,每个值出现的概率极低。

你想想看,如果不小心写成 [1519:2000] := 1,那超长包的总权重就是 482 份,比正常包还多,仿真结果就完全偏离实际了。

4.4 常见陷阱

做验证这么多年,我在 dist 上踩过的坑还真不少。下面几个是高频雷区:

陷阱一:权重值不能为 0

有些同学想“让某个值永远不出现”,就把权重设为 0。但 dist 不支持 0 权重。正确的做法是用 solve...before 或者干脆不写这个值。

我曾经见过有人写 addr dist { 0 := 0, [1:10] := 1 },结果仿真器报了个奇怪的警告,然后 addr 还是偶尔取 0。嗯,别这么干。

陷阱二:权重值不要太大

理论上权重可以是任意正整数,但太大的权重(比如 1000000)会导致仿真器计算概率时出现精度问题。我一般控制在 1~100 之间。

陷阱三:范围重叠

dist 列表里的范围不能重叠。比如你不能同时写 [1:10] := 5[5:15] := 3。仿真器会报错,因为 5~10 这个区间不知道该听谁的。

小技巧:

如果你需要让某个值出现的概率是另一个值的两倍,直接写权重 2 和 1 就行。不需要算百分比,dist 会自动归一化。

另外,调试时可以用 $urandom_range 配合 dist 做快速验证。我经常写个小 testbench,跑 10000 次随机,统计一下分布是否符合预期。

4.5 总结一下

权重分布这东西,说白了就是让随机更有“人性”。

  • dist 是加权随机,:= 按值分配,:/ 按范围分配
  • 权重值用 1~100 就够了,别搞太大
  • 范围不能重叠,权重不能为 0
  • 实战中多用 :/ 控制大范围的总概率

下一章咱们聊聊约束的优先级和 solve...before,那也是个容易翻车的地方。到时候见。