4. bin的高级用法:ignore_bins、illegal_bins、wildcard bins、cross bins
好,咱们接着聊bin。上一节我们讲了基础bin的定义方式,说白了就是告诉仿真器「我要盯着哪些值看」。但实际项目中,光盯着看还不够——你得告诉仿真器「哪些值你别管」、「哪些值一出现就报错」,甚至「我要同时看两个信号的关系」。这就是今天要聊的四个高级用法。
4.1 ignore_bins:眼不见为净
先说说 ignore_bins。这玩意儿我太熟了——刚做验证那会儿,我傻乎乎地把所有可能的值都列进bin里,结果覆盖率报告出来一堆「未覆盖」的坑。后来才发现,有些值根本不需要关心。
举个例子,你有个3位的信号,取值范围0~7。但设计规格说了,这个信号永远不会等于5和6。那你何必还盯着它们看?直接忽略掉就好。
covergroup cg_ignore;
coverpoint addr {
bins valid[] = {[0:4], 7}; // 只关心这6个值
ignore_bins unused = {5, 6}; // 5和6直接忽略
}
endgroup
嗯,这里要注意:ignore_bins 的优先级是最高的。就算你同时定义了 bins 和 ignore_bins,只要某个值落在忽略区间里,它就不会被任何bin统计到。我见过有人把同一个值既放在正常bin里又放在ignore里,结果仿真器直接报warning——别这么干,逻辑上说不通。
4.2 illegal_bins:一出现就翻车
如果说 ignore_bins 是「我不想看见你」,那 illegal_bins 就是「你敢出现我就死给你看」。这玩意儿在验证里特别有用——尤其是当你发现某个值理论上永远不该出现时。
我曾经在一个总线协议项目里吃过亏。协议规定地址必须按4字节对齐,但设计里有个bug,偶尔会发出非对齐地址。当时我没用 illegal_bins,结果这个bug在仿真里跑了三天才被发现。后来我学乖了——只要是不该出现的值,直接标成illegal。
covergroup cg_illegal;
coverpoint addr {
illegal_bins bad_addr = {[1:3], [5:7]};
// 地址必须4字节对齐,0、4、8...才是合法的
}
endgroup
一旦某个值落进 illegal_bins,仿真器会立刻报错并停止仿真(默认行为)。你可以通过仿真选项调整这个行为,但我建议别改——既然标了illegal,就该让它停下来。
4.3 wildcard bins:模糊匹配的艺术
接下来是 wildcard bins。这玩意儿说白了就是「我不关心某些位,你帮我模糊匹配」。在SystemVerilog里,wildcard用 ? 表示「任意值」。
举个例子,你有个状态机的状态编码是3位的,但设计里只用了000、001、010、100这四种状态。其他组合(011、101、110、111)都是非法状态。你可以这么写:
covergroup cg_wildcard;
coverpoint state {
wildcard bins legal_states = {3'b000, 3'b001, 3'b010, 3'b100};
wildcard bins illegal_states = {3'b?11, 3'b1?1, 3'b11?};
// ?表示任意值,所以?11匹配011和111
}
endgroup
你想想看,如果没有wildcard,你得把四个非法状态一个一个列出来,多麻烦。用 ? 一个表达式就搞定了。
我个人习惯在地址译码验证里大量使用wildcard。比如某个外设的地址范围是0x1000~0x1FFF,但实际只用了0x1000、0x1100、0x1200这几个地址。用wildcard可以快速匹配「高4位固定,低12位任意」的模式。
? 只能匹配0或1,不能匹配X或Z。如果你需要匹配X或Z,得用 default 或者单独定义。
4.4 cross bins:组合覆盖的利器
最后,也是最强大的——cross bins。这玩意儿能让你同时看两个或多个coverpoint的组合情况。说白了,就是「当A等于某个值时,B等于什么?」
我记得有个项目,验证一个AXI总线的主机。我们需要确保「当写地址落在某个区间时,写数据不能全为0」。这种跨信号的约束,用单个coverpoint根本搞不定,必须用cross。
covergroup cg_cross;
addr: coverpoint waddr {
bins low = {[0:100]};
bins mid = {[101:200]};
bins high = {[201:255]};
}
data: coverpoint wdata {
bins zero = {0};
bins nonzero = {[1:255]};
}
cross addr, data {
// 自动生成3*2=6个交叉bin
// 但我们可以手动定义更精细的bin
bins addr_low_zero = binsof(addr.low) && binsof(data.zero);
bins addr_mid_zero = binsof(addr.mid) && binsof(data.zero);
bins addr_high_zero = binsof(addr.high) && binsof(data.zero);
}
endgroup
这里有个坑——cross bins的数量会爆炸。如果你有两个coverpoint,每个有10个bin,cross之后就是100个bin。三个coverpoint就是1000个。所以,我建议你只在必要的时候用cross,而且尽量控制每个coverpoint的bin数量。
4.5 四种bin的优先级与组合使用
这四种bin可以混着用,但要注意优先级:ignore_bins > illegal_bins > wildcard bins > 普通 bins。也就是说,如果一个值同时被ignore和illegal定义了,ignore会生效,illegal反而不会触发。
我建议你在实际项目中这样组合:
- 先用
ignore_bins排除所有「不关心」的值 - 再用
illegal_bins标记所有「不该出现」的值 - 然后用
wildcard bins或普通bins定义你关心的值 - 最后用
cross bins检查跨信号的关系
这个顺序能帮你快速定位问题。我曾经在一个项目里,先定义了illegal_bins,结果仿真一跑就停——原来有个值被误标了。调整之后,再跑就顺利多了。