3、交叉覆盖:交叉覆盖(cross cover)的定义、排除非法组合与权重设置

好,咱们接着聊交叉覆盖。说实话,交叉覆盖这个概念,刚接触UVM的朋友容易把它想复杂了。其实说白了,就是「两个或两个以上的coverpoint组合起来看」。你单独看每个coverpoint可能都覆盖到了,但组合在一起呢?有没有遗漏的场景?这才是交叉覆盖要回答的问题。

3.1 交叉覆盖的定义

交叉覆盖,英文叫 cross cover。它的核心思想很简单:
把多个coverpoint的值域做笛卡尔积,然后统计每个组合是否被触发过。

举个例子。假设我们有一个总线协议,有两个信号:

  • cmd:取值可以是 READ、WRITE、IDLE
  • burst:取值可以是 SINGLE、INCR、WRAP

单独覆盖 cmd 和 burst 没问题。但你想过没有?
READ + WRAP 这种组合是否合法?
IDLE + INCR 会不会出现?
这些组合如果不做交叉覆盖,你根本不知道。

核心定义:交叉覆盖是 SystemVerilog 中通过 cross 关键字,将多个 coverpoint 的 bin 做组合覆盖。每个组合称为一个 cross bin。

我个人习惯,在写大型验证环境时,交叉覆盖是必加的。尤其是当协议有状态机、有不同操作模式时,交叉覆盖能帮你发现「模式A + 操作B」这种边界场景。

3.2 排除非法组合

这里有个坑,我踩过好几次。
你想想看,两个coverpoint的笛卡尔积,组合数是指数级增长的。
如果每个coverpoint有10个bin,两个就是100个组合,三个就是1000个。
但很多组合在实际协议中是非法的,根本不会出现。

如果不排除非法组合,会有什么后果?
嗯,后果就是:你的覆盖率报告里会出现一堆「未覆盖」的bin。
这些bin永远不可能被覆盖到,但它们会拉低你的覆盖率数字。
你花大量时间去分析,结果发现是非法组合——浪费时间。

怎么排除? 用 ignore_bins 或者 illegal_bins。

  • ignore_bins:忽略这些组合,不参与覆盖率统计。
  • illegal_bins:如果出现这些组合,直接报错。用于检查协议违例。

我曾经在一个AHB验证项目中,cmd和burst有32种组合,但协议只允许其中12种。我一开始没加ignore_bins,结果覆盖率卡在75%上不去。排查了两天才发现是非法组合在作祟。后来我加了个自动生成ignore_bins的脚本,一劳永逸。

小技巧:写交叉覆盖前,先列一张「合法组合表」。把协议文档翻出来,一行一行对。别偷懒,这一步省不了。

3.3 权重设置

权重设置,说白了就是「给不同的cross bin分配不同的重要性」。

为什么需要权重?
因为不是所有组合都同等重要。
有些组合是核心场景,必须覆盖到。
有些组合是边界场景,覆盖到更好,没覆盖到也能接受。

SystemVerilog 里,权重通过 weight 关键字来设置。默认权重是1。你可以把关键组合的权重设高,比如2或3,这样它们在总体覆盖率中的占比更大。

举个例子:

covergroup cg_with_weight @(posedge clk);
  cmd_cp : coverpoint cmd {
    bins read  = {READ};
    bins write = {WRITE};
    bins idle  = {IDLE};
  }
  burst_cp : coverpoint burst {
    bins single = {SINGLE};
    bins incr   = {INCR};
    bins wrap   = {WRAP};
  }

  cmd_x_burst : cross cmd_cp, burst_cp {
    // 排除非法组合
    ignore_bins illegal_combos = binsof(cmd_cp) intersect {IDLE}
                                && binsof(burst_cp) intersect {INCR, WRAP};

    // 设置权重:核心组合权重2,其他默认1
    weight = 2;  // 整个cross的权重
    // 也可以针对单个bin设置
    bins core_combos = (binsof(cmd_cp.read) && binsof(burst_cp.single)) 
                     || (binsof(cmd_cp.write) && binsof(burst_cp.incr));
    // 注意:单个bin的权重设置语法较复杂,通常用全局weight
  }
endgroup

注意:权重设置要谨慎。设得太高,会导致覆盖率数字虚高——核心组合覆盖了,但边缘组合全空着,覆盖率数字却很好看。这反而掩盖了问题。

我个人建议,权重只用在两种场景:

  1. 核心功能组合:比如读写操作的正确组合,权重可以设为2。
  2. 异常组合:比如错误响应+重试,权重设为1.5或2,确保它们被覆盖到。

其他常规组合,保持默认权重1就好。别搞得太复杂,否则你自己都算不清覆盖率到底代表什么。

3.4 知识体系图

下面这张图,是我画的一个交叉覆盖核心逻辑图。你看一眼就明白了。

交叉覆盖核心逻辑 Coverpoint A (如 cmd: READ/WRITE/IDLE) Coverpoint B (如 burst: SINGLE/INCR/WRAP) cross 笛卡尔积 合法组合(自动生成bin) 非法组合(ignore_bins) 权重设置(weight) 核心:排除非法组合 + 合理设置权重 = 真实的覆盖率

3.5 实战建议

最后,我总结几条实战经验:

  • 先列合法组合表:别急着写代码。拿张纸,把协议允许的组合列出来。这一步花30分钟,能省你后面3天。
  • ignore_bins 要写全:漏掉一个非法组合,你的覆盖率就多一个永远填不上的坑。
  • 权重别乱设:除非你很清楚某个组合的重要性,否则保持默认。权重设高了,覆盖率数字会骗人。
  • 交叉覆盖别太多:3个coverpoint交叉,组合数就可能上千。我建议最多交叉2~3个,再多就拆成多个小的cross。

避坑指南:我曾经在一个项目里,把5个coverpoint做了交叉,结果生成了上万个bin。仿真跑了一整天,覆盖率才到30%。后来我拆成3个小的cross,每个只交叉2~3个coverpoint,覆盖率很快就上去了。记住:交叉覆盖不是越多越好,够用就行。

好了,交叉覆盖就聊到这儿。记住三个关键词:笛卡尔积、排除非法、权重设置。把这三点吃透了,交叉覆盖这块你就拿下了。

可可精品资料库