• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

systemverilog中covergroup的用法

SystemVerilog中的covergroup是一种用于功能覆盖的语言结构,它允许用户定义一组覆盖点(coverpoints)和交叉覆盖(cross coverage),以衡量验证的完整性。下面将详细说明covergroup的用法。

SystemVerilog Covergroup用法详解
一、基本概念
Covergroup是SystemVerilog中用于收集覆盖率的构造,它可以在类中定义,也可以在模块或接口中定义。Covergroup包含覆盖点(coverpoint)和交叉覆盖(cross),用于测量设计中的代码功能覆盖率。

二、基本语法
2.1 定义Covergroup
systemverilog
covergroup covergroup_name [([arguments])] [coverage_event];
    // 覆盖点定义
    // 选项设置
endgroup
2.2 简单示例
systemverilog
class my_class;
    logic [3:0] data;
    logic valid;
    
    // 定义covergroup
    covergroup data_cg @(posedge clk);  // 在时钟上升沿采样
        coverpoint data {
            bins low = {[0:7]};
            bins high = {[8:15]};
        }
        coverpoint valid;
    endgroup
    
    // 实例化covergroup
    function new();
        data_cg = new();
    endfunction
endclass
三、覆盖点(Coverpoint)
3.1 基本覆盖点
systemverilog
covergroup cg;
    // 覆盖一个变量
    coverpoint var_name {
        // 仓定义
    }
endgroup
3.2 仓(Bins)定义
systemverilog
covergroup cg;
    coverpoint data {
        // 自动仓:根据变量的取值范围自动创建仓
        // 手动定义仓
        bins zero = {0};
        bins small = {[1:10]};
        bins large = {[11:100]};
        
        // 忽略某些值
        ignore_bins ignore_vals = {7, 8, 9};
        
        // 非法值
        illegal_bins illegal_vals = {101, 102};
    }
endgroup
3.3 条件覆盖
systemverilog
covergroup cg @(posedge clk);
    // 使用iff条件
    coverpoint data iff (valid) {
        bins low = {[0:127]};
        bins high = {[128:255]};
    }
    
    // 使用条件表达式
    coverpoint addr {
        bins low_addr = {[0:'h3FF]} with (data == 0);
        bins high_addr = {['h400:'h7FF]} with (data != 0);
    }
endgroup
四、交叉覆盖(Cross Coverage)
4.1 基本交叉覆盖
systemverilog
covergroup cg;
    coverpoint a;
    coverpoint b;
    
    // 交叉覆盖a和b
    cross a, b {
        // 可以在这里添加仓限制或条件
    }
endgroup
4.2 限制交叉覆盖
systemverilog
covergroup cg;
    coverpoint a {
        bins a_bins[] = {[0:3]};
    }
    coverpoint b {
        bins b_bins[] = {[0:3]};
    }
    
    cross a, b {
        // 只关注某些交叉
        bins a0_b0 = binsof(a) intersect {0} && binsof(b) intersect {0};
        bins a1_b1 = binsof(a) intersect {1} && binsof(b) intersect {1};
        
        // 忽略某些交叉
        ignore_bins ignore = binsof(a) intersect {2} && binsof(b) intersect {2};
        
        // 非法交叉
        illegal_bins illegal = binsof(a) intersect {3} && binsof(b) intersect {3};
    }
endgroup
五、选项(Options)
5.1 Covergroup选项
systemverilog
covergroup cg (int low, int high) @(posedge clk);
    // 覆盖组选项
    option.per_instance = 1;        // 每个实例单独收集覆盖率
    option.comment = "My covergroup";
    option.at_least = 2;            // 每个仓至少命中2次
    option.auto_bin_max = 10;       // 自动仓的最大数量
    option.cross_auto_bin_max = 5;  // 交叉覆盖自动仓的最大数量
    
    coverpoint data {
        option.weight = 2;          // 此覆盖点的权重
        option.goal = 90;           // 覆盖率目标百分比
        bins low = {[low:high]};
    }
endgroup
5.2 覆盖点选项
systemverilog
covergroup cg;
    coverpoint data {
        option.auto_bin_max = 8;
        option.weight = 1;
        option.goal = 80;
        // 其他仓定义...
    }
endgroup
六、在类中使用Covergroup
6.1 类中定义和实例化
systemverilog
class transaction;
    rand logic [7:0] addr;
    rand logic [31:0] data;
    rand logic wr_en;
    
    // 定义covergroup
    covergroup addr_data_cg;
        coverpoint addr {
            bins low = {[0:127]};
            bins high = {[128:255]};
        }
        coverpoint data {
            bins zero = {0};
            bins small = {[1:1000]};
            bins large = {[1001:$]};
        }
        coverpoint wr_en;
        
        // 交叉覆盖
        cross addr, wr_en {
            bins wr_low = binsof(addr.low) && binsof(wr_en) intersect {1};
            bins rd_low = binsof(addr.low) && binsof(wr_en) intersect {0};
        }
    endgroup
    
    // 构造函数中实例化
    function new();
        addr_data_cg = new();
    endfunction
    
    // 采样函数
    function void sample();
        addr_data_cg.sample();
    endfunction
endclass
七、采样触发
7.1 事件触发
systemverilog
covergroup cg @(posedge clk);  // 使用时钟事件
    coverpoint data;
endgroup
7.2 显式采样
systemverilog
covergroup cg;
    coverpoint data;
endgroup

// 在代码中显式采样
cg cg_inst = new();
always @(posedge clk) begin
    cg_inst.sample();
end
八、实际示例
8.1 存储器接口覆盖
systemverilog
class memory_if_monitor;
    logic [31:0] addr;
    logic [31:0] data;
    logic        wr_en;
    logic        rd_en;
    logic        valid;
    logic        ready;
    
    // Covergroup for memory interface
    covergroup mem_cg @(posedge clk iff valid);
        // 地址覆盖点
        cp_addr: coverpoint addr {
            bins mem_range[4] = {[32'h0000_0000:32'h0000_FFFF]};
            bins io_range    = {[32'h1000_0000:32'h1000_0FFF]};
            bins config_range = {[32'h2000_0000:32'h2000_00FF]};
        }
        
        // 数据覆盖点(只关注写数据)
        cp_wr_data: coverpoint data iff (wr_en) {
            bins zero     = {0};
            bins all_ones = {32'hFFFF_FFFF};
            bins positive = {[1:32'h7FFF_FFFF]};
            bins negative = {[32'h8000_0000:32'hFFFF_FFFE]};
        }
        
        // 操作类型覆盖点
        cp_op_type: coverpoint {wr_en, rd_en} {
            bins idle  = {2'b00};
            bins write = {2'b10};
            bins read  = {2'b01};
            illegal_bins both = {2'b11};  // 不能同时读写
        }
        
        // 握手覆盖点
        cp_handshake: coverpoint {valid, ready} {
            bins idle     = {2'b00};
            bins request  = {2'b10};
            bins response = {2'b01};
            bins active   = {2'b11};
        }
        
        // 交叉覆盖:地址范围与操作类型
        cross_addr_op: cross cp_addr, cp_op_type {
            // 忽略无效交叉
            ignore_bins ignore = binsof(cp_op_type.idle);
        }
        
        // 交叉覆盖:操作类型与握手
        cross_op_handshake: cross cp_op_type, cp_handshake {
            // 我们关心写操作时的握手序列
            bins write_request = binsof(cp_op_type.write) && 
                                 binsof(cp_handshake.request);
            bins write_active = binsof(cp_op_type.write) && 
                                binsof(cp_handshake.active);
        }
    endgroup
    
    // 实例化covergroup
    function new();
        mem_cg = new();
    endfunction
endclass
8.2 带参数的Covergroup
systemverilog
class param_cg_example;
    int min_val, max_val;
    
    // 带参数的covergroup
    covergroup range_cg (int min_val, int max_val) with function sample(int val);
        // 覆盖传入的值
        coverpoint val {
            bins low  = {[min_val:min_val+(max_val-min_val)/2]};
            bins high = {[min_val+(max_val-min_val)/2+1:max_val]};
        }
    endgroup
    
    // 实例化
    function new(int min, int max);
        range_cg = new(min, max);
    endfunction
    
    // 采样
    function void sample_val(int val);
        range_cg.sample(val);
    endfunction
endclass
九、覆盖率查询和控制
9.1 查询覆盖率
systemverilog
// 获取覆盖率百分比
real coverage_percentage = cg_inst.get_coverage();

// 获取覆盖组实例的覆盖率
real inst_coverage = cg_inst.get_inst_coverage();

// 获取所有相同类型covergroup的覆盖率
real total_coverage = cg_inst::get_coverage();
9.2 覆盖率控制
systemverilog
// 设置覆盖率目标
cg_inst.set_inst_name("instance_name");
cg_inst.start();  // 开始收集覆盖率
cg_inst.stop();   // 停止收集覆盖率
十、最佳实践
明确覆盖目标:定义covergroup前明确要覆盖的功能点

避免过度覆盖:不要创建不必要的覆盖点,以免影响仿真性能

使用有意义的仓名:便于理解覆盖报告

定期检查覆盖率报告:指导测试用例的编写

结合断言使用:covergroup用于衡量覆盖率,断言用于检查设计正确性

十一、常见问题
性能问题:过多的覆盖点或交叉覆盖会降低仿真速度

内存消耗:大量的覆盖组实例可能消耗大量内存

覆盖率合并:多个实例的覆盖率可以合并,但需要注意选项设置

通过合理使用covergroup,可以有效地衡量验证的完整性,确保设计的所有重要功能都被测试到。

SystemVerilog Covergroup用法详解

一、Covergroup基本概念和语法

1.1 基本定义

Covergroup是SystemVerilog中用于收集功能覆盖率的构造,它可以:

  • 定义覆盖点(coverpoints)
  • 定义交叉覆盖(cross coverage)
  • 在测试过程中自动或手动采样
// 基本语法
covergroup covergroup_name [(arguments)] [coverage_event];
    // 选项(options)
    // 覆盖点(coverpoints)
    // 交叉覆盖(cross coverage)
endgroup

// 或者使用带sample方法的定义
covergroup covergroup_name [(arguments)] with function sample(arguments);
    // 覆盖点定义
endgroup

1.2 简单示例

// 在类中定义covergroup
class packet;
    rand bit [3:0] data;
    rand bit [1:0] mode;
    rand bit       valid;
    
    // 定义covergroup
    covergroup cg @(posedge clk);
        // 覆盖点
        cp_data: coverpoint data {
            bins low  = {[0:7]};
            bins high = {[8:15]};
        }
        cp_mode: coverpoint mode;
        cp_valid: coverpoint valid;
    endgroup
    
    // 构造函数
    function new();
        cg = new();  // 实例化covergroup
    endfunction
endclass

二、覆盖点(Coverpoint)详解

2.1 基本覆盖点

covergroup cg;
    // 自动创建bins(根据数据类型)
    coverpoint data;  // 为4-bit数据自动创建16个bins
    
    // 手动定义bins
    coverpoint addr {
        bins low    = {[0:127]};
        bins middle = {[128:255]};
        bins high   = {[256:511]};
    }
    
    // 指定数量的bins
    coverpoint data {
        option.auto_bin_max = 8;  // 最多8个自动bins
    }
endgroup

2.2 Bins类型和定义

covergroup cg;
    coverpoint data {
        // 1. 值bins(单个值或多个值)
        bins zero = {0};
        bins small = {1, 2, 3};
        bins medium[] = {[4:7]};  // 4个bins: {4}, {5}, {6}, {7}
        bins large = {[8:15]};
        
        // 2. 条件bins
        bins valid_data = data with (data > 0 && data < 10);
        
        // 3. 转换bins(序列)
        bins seq = (0 => 1 => 2);
        bins seq_range = ([0:2] => [3:5]);
        
        // 4. 忽略的bins
        ignore_bins ignore = {8'hFF, 8'h00};
        
        // 5. 非法的bins
        illegal_bins illegal = {8'hFE};
    }
endgroup

2.3 带条件的覆盖点

covergroup cg;
    // 使用iff条件
    coverpoint data iff (valid == 1'b1) {
        bins valid_data = {[1:255]};
    }
    
    // 使用with条件
    coverpoint addr {
        bins low_addr = {[0:'h3FF]} with (data == 0);
        bins high_addr = {['h400:'h7FF]} with (data != 0);
    }
    
    // 带权重的bins
    coverpoint cmd {
        option.weight = 2;  // 这个覆盖点的权重
        bins read = {0} with (weight = 3);  // 这个bin的权重
        bins write = {1};
        bins other = default;
    }
endgroup

三、交叉覆盖(Cross Coverage)

3.1 基本交叉覆盖

covergroup cg;
    coverpoint a {
        bins a_bins[] = {[0:3]};
    }
    coverpoint b {
        bins b_bins[] = {[0:3]};
    }
    
    // 交叉覆盖a和b(产生16个交叉bin)
    cross a, b;
    
    // 命名的交叉覆盖
    cross a_x_b: cross a, b;
endgroup

3.2 交叉覆盖的限制和选择

covergroup cg;
    coverpoint a {
        bins a0 = {0};
        bins a1 = {1};
        bins a2 = {2};
    }
    
    coverpoint b {
        bins b0 = {0};
        bins b1 = {1};
        bins b2 = {2};
    }
    
    cross a, b {
        // 只选择特定的交叉组合
        bins a0_b0 = binsof(a.a0) && binsof(b.b0);
        bins a1_b1 = binsof(a.a1) && binsof(b.b1);
        
        // 忽略某些交叉组合
        ignore_bins ignore = binsof(a) intersect {2} && binsof(b) intersect {2};
        
        // 非法交叉组合
        illegal_bins illegal = binsof(a.a0) && binsof(b.b1);
        
        // 使用binsof函数选择
        bins all_a0 = binsof(a.a0);
        bins all_b1 = binsof(b.b1);
    }
endgroup

3.3 带条件的交叉覆盖

covergroup transaction_cg with function sample(bit[31:0] addr, bit[31:0] data, bit wr);
    coverpoint addr {
        bins mem_range[4] = {[32'h0000_0000:32'h0000_FFFF]};
        bins io_range     = {[32'h1000_0000:32'h1000_0FFF]};
    }
    
    coverpoint data {
        bins zero = {0};
        bins positive = {[1:32'h7FFF_FFFF]};
        bins negative = {[32'h8000_0000:32'hFFFF_FFFF]};
    }
    
    coverpoint wr {
        bins read = {0};
        bins write = {1};
    }
    
    // 条件交叉:只在写操作时交叉地址和数据
    cross addr, data iff (wr == 1) {
        // 自定义交叉bin
        bins mem_write_zero = binsof(addr.mem_range) && binsof(data.zero);
        bins io_write_positive = binsof(addr.io_range) && binsof(data.positive);
    }
    
    // 限制交叉的维度
    cross wr, addr, data {
        // 减少交叉数量
        bins read_trans = binsof(wr.read) && binsof(addr.mem_range) && binsof(data.positive);
        bins write_trans = binsof(wr.write) && binsof(addr.io_range) && binsof(data.zero);
    }
endgroup

四、Covergroup选项

4.1 Covergroup级别选项

covergroup cg (int id) @(posedge clk);
    // 基本选项
    option.per_instance = 1;          // 每个实例单独收集覆盖率
    option.comment = "Covergroup for module XYZ";
    option.name = "my_cg";
    
    // 采样控制
    option.strobe = 1;                // 在时钟周期结束时采样
    option.at_least = 2;              // 每个bin至少命中2次
    option.auto_bin_max = 64;         // 自动bin的最大数量
    option.cross_auto_bin_max = 8;    // 交叉自动bin的最大数量
    
    // 权重和阈值
    option.weight = 5;                // 这个covergroup的权重
    option.goal = 95;                 // 覆盖率目标百分比
    
    // 覆盖点
    coverpoint data {
        option.weight = 2;
        option.goal = 100;
        option.comment = "Data coverage";
    }
    
    // 交叉覆盖选项
    cross data, mode {
        option.weight = 3;
        option.goal = 80;
        option.at_least = 1;
    }
endgroup

4.2 实例特定的选项

covergroup cg with function sample(int data);
    option.per_instance = 1;
    option.get_inst_coverage = 1;  // 使能实例覆盖率查询
    
    coverpoint data {
        bins range[] = {[0:255]};
    }
endgroup

// 创建多个实例
cg cg1 = new();
cg cg2 = new();

// 设置不同的选项
initial begin
    cg1.option.at_least = 1;
    cg2.option.at_least = 3;
    cg1.option.comment = "Instance for port A";
    cg2.option.comment = "Instance for port B";
end

五、实际应用示例

5.1 AXI总线覆盖组

class axi_coverage;
    // AXI信号
    bit [31:0] awaddr, araddr;
    bit [31:0] wdata, rdata;
    bit [2:0]  awprot, arprot;
    bit [3:0]  wstrb;
    bit [1:0]  bresp, rresp;
    bit        awvalid, awready, wvalid, wready;
    bit        arvalid, arready, rvalid, rready;
    bit        bvalid, bready;
    
    // 写地址通道covergroup
    covergroup cg_write_addr @(posedge clk);
        option.per_instance = 1;
        option.name = "AXI_Write_Address";
        
        cp_awaddr: coverpoint awaddr {
            bins zero       = {32'h0};
            bins aligned_4k = {[32'h1000:32'h1FFF]};  // 4K对齐
            bins unaligned  = {[32'h1001:32'h1003]};  // 非对齐
            bins high_mem   = {[32'h8000_0000:32'hFFFF_FFFF]};
            bins other      = default;
        }
        
        cp_awprot: coverpoint awprot {
            bins normal     = {3'b000};
            bins privileged = {3'b001};
            bins secure     = {3'b010};
            bins cacheable  = {3'b100};
            illegal_bins illegal = {3'b111};  // 保留值
        }
        
        cp_aw_handshake: coverpoint {awvalid, awready} {
            bins idle     = {2'b00};
            bins request  = {2'b10};
            bins response = {2'b01};
            bins active   = {2'b11};
        }
        
        // 交叉覆盖
        cross_aw: cross cp_awaddr, cp_awprot {
            bins normal_access = binsof(cp_awaddr.aligned_4k) && 
                                 binsof(cp_awprot.normal);
            bins secure_high = binsof(cp_awaddr.high_mem) && 
                               binsof(cp_awprot.secure);
        }
    endgroup
    
    // 写数据通道covergroup
    covergroup cg_write_data with function sample(bit[31:0] data, bit[3:0] strb);
        option.name = "AXI_Write_Data";
        
        cp_wdata: coverpoint data {
            bins zero     = {0};
            bins all_ones = {32'hFFFF_FFFF};
            bins byte_pattern = {32'hA5A5_A5A5};
            bins increment = {32'h0000_0001, 32'h0000_0002, 32'h0000_0003};
        }
        
        cp_wstrb: coverpoint strb {
            bins no_write  = {4'b0000};
            bins byte0     = {4'b0001};
            bins byte1     = {4'b0010};
            bins byte2     = {4'b0100};
            bins byte3     = {4'b1000};
            bins half_word = {4'b0011, 4'b1100};
            bins full_word = {4'b1111};
            bins partial   = default;
        }
        
        cp_w_handshake: coverpoint {wvalid, wready} {
            bins idle     = {2'b00};
            bins request  = {2'b10};
            bins response = {2'b01};
            bins active   = {2'b11};
        }
        
        // 数据与字节使能的交叉
        cross_data_strb: cross cp_wdata, cp_wstrb {
            bins zero_with_full = binsof(cp_wdata.zero) && 
                                  binsof(cp_wstrb.full_word);
            bins ones_with_partial = binsof(cp_wdata.all_ones) && 
                                     binsof(cp_wstrb.partial);
        }
    endgroup
    
    // 构造函数
    function new();
        cg_write_addr = new();
        cg_write_data = new();
    endfunction
    
    // 采样函数
    function void sample_write_addr();
        cg_write_addr.sample();
    endfunction
    
    function void sample_write_data();
        cg_write_data.sample(wdata, wstrb);
    endfunction
endclass

5.2 状态机覆盖组

module fsm_coverage;
    // 状态机定义
    typedef enum logic [2:0] {
        IDLE    = 3'b000,
        START   = 3'b001,
        DATA    = 3'b010,
        PARITY  = 3'b011,
        STOP    = 3'b100,
        ERROR   = 3'b101
    } state_t;
    
    state_t current_state, next_state;
    bit [7:0] data_reg;
    bit       parity_error;
    int       data_count;
    
    // 状态转移covergroup
    covergroup cg_fsm_transitions @(posedge clk);
        option.name = "FSM_State_Transitions";
        option.comment = "Cover state transitions and sequences";
        
        cp_current: coverpoint current_state {
            bins states[] = {[IDLE:ERROR]};
            illegal_bins invalid = {3'b110, 3'b111};
        }
        
        cp_next: coverpoint next_state {
            bins states[] = {[IDLE:ERROR]};
        }
        
        // 状态转移序列
        cp_transition: coverpoint {current_state, next_state} {
            // 合法转移
            bins idle_to_start = {IDLE, START};
            bins start_to_data = {START, DATA};
            bins data_to_parity = {DATA, PARITY};
            bins parity_to_stop = {PARITY, STOP};
            bins stop_to_idle = {STOP, IDLE};
            bins any_to_error = {[IDLE:STOP], ERROR};
            
            // 非法转移(如果发生则报错)
            illegal_bins illegal = {
                {START, IDLE},
                {DATA, START},
                {PARITY, DATA},
                {STOP, PARITY},
                {ERROR, STOP}
            };
        }
        
        // 状态驻留时间
        cp_state_duration: coverpoint data_count {
            bins short = {[0:1]};
            bins medium = {[2:5]};
            bins long = {[6:10]};
            bins very_long = {[11:20]};
        }
        
        // 带条件的状态覆盖
        cp_state_with_error: coverpoint current_state {
            bins normal_states = {IDLE, START, DATA, PARITY, STOP} 
                                 with (parity_error == 0);
            bins error_state = {ERROR} with (parity_error == 1);
        }
        
        // 复杂交叉:状态转移与数据计数
        cross_transition_duration: cross cp_transition, cp_state_duration {
            // 只关注数据状态到校验状态的转移
            bins data_to_parity_with_count = 
                binsof(cp_transition.data_to_parity) &&
                binsof(cp_state_duration.medium);
        }
        
        // 序列覆盖
        cp_state_sequence: coverpoint current_state {
            // 检查特定的状态序列
            bins idle_start_data = (IDLE => START => DATA);
            bins full_packet = (IDLE => START => DATA[*1:8] => PARITY => STOP => IDLE);
        }
    endgroup
    
    // 实例化covergroup
    cg_fsm_transitions fsm_cg = new();
    
    // 数据覆盖组
    covergroup cg_data @(posedge clk iff (current_state == DATA));
        option.name = "FSM_Data_Coverage";
        
        cp_data_value: coverpoint data_reg {
            bins zero = {0};
            bins max = {255};
            bins low = {[1:127]};
            bins high = {[128:254]};
            bins ascii_printable = {[32:126]};
            bins control_chars = {[0:31], 127};
        }
        
        cp_parity: coverpoint ^data_reg {  // 奇偶校验位
            bins even = {0};
            bins odd = {1};
        }
        
        // 数据值与奇偶校验的交叉
        cross_data_parity: cross cp_data_value, cp_parity {
            bins zero_even = binsof(cp_data_value.zero) && 
                            binsof(cp_parity.even);
            bins max_odd = binsof(cp_data_value.max) && 
                          binsof(cp_parity.odd);
        }
    endgroup
    
    cg_data data_cg = new();
endmodule

5.3 缓存控制器覆盖组

class cache_controller_coverage;
    // 缓存信号
    bit [31:0] addr;
    bit [63:0] data;
    bit        rd_req, wr_req;
    bit        hit, miss;
    bit [1:0]  cache_state;  // 00:Invalid, 01:Shared, 10:Exclusive, 11:Modified
    bit [2:0]  burst_count;
    
    // 地址空间covergroup
    covergroup cg_address_space with function sample(bit[31:0] address);
        option.per_instance = 1;
        option.name = "Cache_Address_Space";
        
        // 按cache line对齐划分
        cp_addr_aligned: coverpoint address[5:0] {  // cache line内的偏移
            bins aligned_0   = {0};
            bins aligned_8   = {8};
            bins aligned_16  = {16};
            bins aligned_32  = {32};
            bins aligned_48  = {48};
            bins aligned_56  = {56};
            bins unaligned   = default;
        }
        
        cp_addr_range: coverpoint address {
            // 按内存区域划分
            bins dram_range[4] = {[32'h0000_0000:32'h3FFF_FFFF]};
            bins mmio_range    = {[32'h4000_0000:32'h4000_FFFF]};
            bins flash_range   = {[32'h8000_0000:32'h9FFF_FFFF]};
            bins reserved      = {[32'hC000_0000:32'hFFFF_FFFF]};
        }
        
        cp_cache_set: coverpoint address[11:6] {  // cache set index
            option.auto_bin_max = 64;
        }
        
        cp_cache_tag: coverpoint address[31:12] {  // cache tag
            bins zero_tag = {0};
            bins non_zero = {[1:20'hFFFFF]};
        }
    endgroup
    
    // 缓存命中/失效covergroup
    covergroup cg_cache_operation @(posedge clk);
        option.name = "Cache_Operation";
        
        cp_op_type: coverpoint {rd_req, wr_req} {
            bins idle   = {2'b00};
            bins read   = {2'b10};
            bins write  = {2'b01};
            illegal_bins both = {2'b11};
        }
        
        cp_cache_result: coverpoint hit {
            bins hit  = {1};
            bins miss = {0};
        }
        
        cp_cache_state: coverpoint cache_state {
            bins invalid   = {0};
            bins shared    = {1};
            bins exclusive = {2};
            bins modified  = {3};
        }
        
        cp_burst_length: coverpoint burst_count {
            bins single   = {0};
            bins burst_4  = {4};
            bins burst_8  = {8};
            bins burst_16 = {16};
            bins other    = default;
        }
        
        // 操作类型与命中/失效的交叉
        cross_op_hit: cross cp_op_type, cp_cache_result {
            bins read_hit  = binsof(cp_op_type.read) && binsof(cp_cache_result.hit);
            bins read_miss = binsof(cp_op_type.read) && binsof(cp_cache_result.miss);
            bins write_hit = binsof(cp_op_type.write) && binsof(cp_cache_result.hit);
            bins write_miss = binsof(cp_op_type.write) && binsof(cp_cache_result.miss);
        }
        
        // 缓存状态与操作类型的交叉
        cross_state_op: cross cp_cache_state, cp_op_type {
            // 关注从不同状态开始的读写
            bins read_from_shared = binsof(cp_cache_state.shared) && 
                                    binsof(cp_op_type.read);
            bins write_to_exclusive = binsof(cp_cache_state.exclusive) && 
                                     binsof(cp_op_type.write);
            bins write_to_modified = binsof(cp_cache_state.modified) && 
                                    binsof(cp_op_type.write);
        }
        
        // 突发传输与命中/失效的交叉
        cross_burst_hit: cross cp_burst_length, cp_cache_result {
            bins burst4_hit  = binsof(cp_burst_length.burst_4) && 
                               binsof(cp_cache_result.hit);
            bins burst8_miss = binsof(cp_burst_length.burst_8) && 
                               binsof(cp_cache_result.miss);
        }
    endgroup
    
    // 数据模式covergroup
    covergroup cg_data_pattern with function sample(bit[63:0] data, bit wr_op);
        option.name = "Cache_Data_Patterns";
        
        cp_data_pattern: coverpoint data {
            // 常见数据模式
            bins all_zeros      = {64'h0};
            bins all_ones       = {64'hFFFF_FFFF_FFFF_FFFF};
            bins walking_1      = {64'h0000_0000_0000_0001,
                                  64'h0000_0000_0000_0002,
                                  64'h0000_0000_0000_0004,
                                  64'h0000_0000_0000_0008};
            bins walking_0      = {64'hFFFF_FFFF_FFFF_FFFE,
                                  64'hFFFF_FFFF_FFFF_FFFD,
                                  64'hFFFF_FFFF_FFFF_FFFB,
                                  64'hFFFF_FFFF_FFFF_FFF7};
            bins alternating    = {64'hAAAA_AAAA_AAAA_AAAA,
                                  64'h5555_5555_5555_5555};
            bins byte_boundary  = {64'h0000_0000_0000_00FF,
                                  64'h0000_0000_0000_FF00,
                                  64'h0000_0000_00FF_0000};
        }
        
        cp_data_alignment: coverpoint data {
            // 检查数据对齐情况
            bins word_aligned = data with (data[31:0] == 0 || data[63:32] == 0);
            bins dword_aligned = data with (data[31:0] == data[63:32]);
        }
        
        // 数据模式与操作类型的交叉(只在写操作时关注)
        cross_pattern_op: cross cp_data_pattern, cp_data_alignment iff (wr_op) {
            bins zeros_aligned = binsof(cp_data_pattern.all_zeros) && 
                                binsof(cp_data_alignment.word_aligned);
            bins ones_unaligned = binsof(cp_data_pattern.all_ones) && 
                                 !binsof(cp_data_alignment.word_aligned);
        }
    endgroup
    
    // 构造函数
    function new();
        cg_address_space = new();
        cg_cache_operation = new();
        cg_data_pattern = new();
    endfunction
    
    // 采样方法
    function void sample_address(bit[31:0] address);
        cg_address_space.sample(address);
    endfunction
    
    function void sample_operation();
        cg_cache_operation.sample();
    endfunction
    
    function void sample_data(bit[63:0] data, bit wr_op);
        cg_data_pattern.sample(data, wr_op);
    endfunction
    
    // 覆盖率查询方法
    function real get_coverage();
        real total = 0;
        total += cg_address_space.get_coverage() * 0.3;
        total += cg_cache_operation.get_coverage() * 0.4;
        total += cg_data_pattern.get_coverage() * 0.3;
        return total;
    endfunction
endclass

5.4 配置寄存器覆盖组

class config_register_coverage;
    // 配置寄存器
    bit [15:0] config_reg;
    bit        config_valid;
    int        config_write_count;
    
    // 寄存器域定义
    bit [1:0]  mode = config_reg[1:0];
    bit [2:0]  speed = config_reg[4:2];
    bit        enable = config_reg[5];
    bit [7:0]  threshold = config_reg[13:6];
    bit        interrupt = config_reg[14];
    bit        reset = config_reg[15];
    
    // 配置寄存器covergroup
    covergroup cg_config_reg @(posedge clk iff (config_valid));
        option.name = "Configuration_Register";
        option.per_instance = 1;
        option.at_least = 2;  // 每个bin至少命中2次
        
        // 各个域的覆盖点
        cp_mode: coverpoint mode {
            bins mode0 = {0};  // 低速模式
            bins mode1 = {1};  // 中速模式
            bins mode2 = {2};  // 高速模式
            bins mode3 = {3};  // 自动模式
        }
        
        cp_speed: coverpoint speed {
            bins speed0 = {0};  // 1x
            bins speed1 = {1};  // 2x
            bins speed2 = {2};  // 4x
            bins speed3 = {3};  // 8x
            bins speed4 = {4};  // 16x
            bins speed5 = {5, 6, 7};  // 保留
        }
        
        cp_enable: coverpoint enable {
            bins disabled = {0};
            bins enabled = {1};
        }
        
        cp_threshold: coverpoint threshold {
            bins zero = {0};
            bins low = {[1:63]};
            bins medium = {[64:127]};
            bins high = {[128:191]};
            bins max = {[192:255]};
        }
        
        cp_interrupt: coverpoint interrupt {
            bins disabled = {0};
            bins enabled = {1};
        }
        
        cp_reset: coverpoint reset {
            bins normal = {0};
            bins reset_active = {1};
        }
        
        // 域之间的交叉覆盖
        cross_mode_speed: cross cp_mode, cp_speed {
            // 检查模式和速度的组合是否合理
            bins low_speed = binsof(cp_mode.mode0) && 
                            binsof(cp_speed.speed0);
            bins high_speed = binsof(cp_mode.mode2) && 
                             binsof(cp_speed.speed4);
            // 忽略不合理的组合
            ignore_bins invalid = 
                (binsof(cp_mode.mode0) && binsof(cp_speed.speed4)) ||
                (binsof(cp_mode.mode2) && binsof(cp_speed.speed0));
        }
        
        cross_enable_threshold: cross cp_enable, cp_threshold {
            // 使能时关注阈值设置
            bins enabled_low_thresh = binsof(cp_enable.enabled) && 
                                     binsof(cp_threshold.low);
            bins enabled_high_thresh = binsof(cp_enable.enabled) && 
                                      binsof(cp_threshold.high);
        }
        
        // 多域交叉
        cross_mode_speed_enable: cross cp_mode, cp_speed, cp_enable {
            // 重点关注使能状态下的组合
            bins enabled_combinations = 
                binsof(cp_enable.enabled) && 
                (binsof(cp_mode.mode1) || binsof(cp_mode.mode2)) &&
                (binsof(cp_speed.speed1) || binsof(cp_speed.speed2));
        }
        
        // 寄存器值序列覆盖
        cp_reg_sequence: coverpoint config_reg {
            // 检查特定的配置序列
            bins power_on_config = (16'h0000 => 16'h0021 => 16'h0061);
            bins mode_transition = (16'h0061 => 16'h00E1 => 16'h0161);
            bins reset_sequence = (16'h8000 => 16'h0000 => 16'h0061);
        }
        
        // 配置写入次数覆盖
        cp_write_count: coverpoint config_write_count {
            bins first_write = {1};
            bins few_writes = {[2:5]};
            bins many_writes = {[6:20]};
            bins excessive_writes = {[21:100]};
        }
    endgroup
    
    // 非法配置检测covergroup
    covergroup cg_illegal_config @(posedge clk);
        option.name = "Illegal_Config_Detection";
        
        // 非法配置模式
        cp_illegal_combinations: coverpoint config_reg {
            illegal_bins illegal_mode_speed = 
                {16'h0004, 16'h0005, 16'h0006, 16'h0007};  // 模式0+高速
            illegal_bins illegal_threshold_enable = 
                {16'h0020, 16'h0060};  // 使能为0但中断使能
            illegal_bins reserved_bits = config_reg with 
                (config_reg[3] == 1 || config_reg[12] == 1);  // 保留位为1
        }
        
        // 配置验证序列
        cp_config_sequence: coverpoint config_reg {
            // 非法序列
            illegal_bins illegal_sequence[] = (
                16'h8000 => 16'h0061,  // 复位后直接配置
                16'h0061 => 16'h8000,  // 配置后立即复位
                16'h0000 => 16'h8000   // 从0直接跳转到复位
            );
        }
    endgroup
    
    // 构造函数
    function new();
        cg_config_reg = new();
        cg_illegal_config = new();
    endfunction
    
    // 采样方法
    function void sample_config();
        cg_config_reg.sample();
        cg_illegal_config.sample();
    endfunction
    
    // 覆盖率报告
    function void report_coverage();
        real reg_cov = cg_config_reg.get_coverage();
        real illegal_cov = cg_illegal_config.get_coverage();
        
        $display("=== Configuration Register Coverage ===");
        $display("Legal config coverage: %0.2f%%", reg_cov);
        $display("Illegal config coverage: %0.2f%%", illegal_cov);
        $display("Overall coverage: %0.2f%%", (reg_cov + illegal_cov) / 2);
        
        // 详细报告
        if (reg_cov < 90) begin
            $display("WARNING: Legal configuration coverage below 90%%");
            cg_config_reg.get_inst_coverage();  // 获取详细覆盖率
        end
    endfunction
endclass

六、Covergroup高级特性

6.1 动态Covergroup创建

class dynamic_coverage;
    // 动态covergroup数组
    covergroup cg_array[int] with function sample(bit[31:0] data);
        option.per_instance = 1;
        
        coverpoint data {
            bins ranges[] = {[0:255]};
        }
    endgroup
    
    // 动态创建covergroup
    function void create_cg(int id);
        if (!cg_array.exists(id)) begin
            cg_array[id] = new();
            cg_array[id].option.comment = $sformatf("CG for ID %0d", id);
        end
    endfunction
    
    // 动态采样
    function void sample_data(int id, bit[31:0] data);
        if (cg_array.exists(id)) begin
            cg_array[id].sample(data);
        end
    endfunction
    
    // 获取总体覆盖率
    function real get_total_coverage();
        real total = 0;
        int count = 0;
        
        foreach (cg_array[i]) begin
            total += cg_array[i].get_coverage();
            count++;
        end
        
        return (count > 0) ? total / count : 0;
    endfunction
endclass

6.2 覆盖率控制和查询

module coverage_control;
    covergroup cg with function sample(int data);
        option.per_instance = 1;
        option.get_inst_coverage = 1;
        
        coverpoint data {
            bins values[] = {[0:9]};
        }
    endgroup
    
    cg cg_inst = new();
    
    // 覆盖率控制
    initial begin
        // 开始收集覆盖率
        cg_inst.start();
        
        // 运行测试
        #1000;
        
        // 停止收集
        cg_inst.stop();
        
        // 查询覆盖率
        $display("Instance coverage: %0.2f%%", 
                 cg_inst.get_inst_coverage());
        $display("Type coverage: %0.2f%%", 
                 cg::get_coverage());
        
        // 获取详细覆盖率
        cg_inst.get_coverage_details();
        
        // 重置覆盖率
        if (cg_inst.get_inst_coverage() < 80) begin
            $display("Coverage too low, resetting and retrying");
            cg_inst.reset();
            cg_inst.start();
        end
    end
endmodule

七、最佳实践和调试技巧

7.1 调试Covergroup

class coverage_debug;
    covergroup debug_cg with function sample(bit[7:0] data, bit valid);
        option.per_instance = 1;
        option.comment = "Debug coverage group";
        
        // 添加调试选项
        option.debug = 1;  // 启用调试信息
        
        coverpoint data iff (valid) {
            // 输出采样信息
            bins low = {[0:127]} with 
                ($display("Sampled low value: %h at %0t", data, $time));
            bins high = {[128:255]} with 
                ($display("Sampled high value: %h at %0t", data, $time));
        }
        
        // 覆盖率不足时报警
        cross data, valid {
            option.goal = 100;
            bins valid_data = binsof(data) && binsof(valid) intersect {1} with
                (if (coverage < 50) 
                    $warning("Cross coverage low: %0.2f%%", coverage));
        }
    endgroup
    
    // 覆盖率监控线程
    task monitor_coverage();
        real last_cov = 0;
        forever begin
            #1000;
            real current_cov = debug_cg.get_inst_coverage();
            if (current_cov - last_cov < 1.0) begin
                $display("Coverage growth slow: %0.2f%% -> %0.2f%%", 
                         last_cov, current_cov);
                debug_cg.get_coverage_details();  // 打印详细报告
            end
            last_cov = current_cov;
        end
    endtask
endclass

7.2 性能优化

class optimized_coverage;
    // 性能优化的covergroup
    covergroup fast_cg @(posedge clk);
        // 优化选项
        option.per_instance = 0;      // 共享实例减少内存
        option.auto_bin_max = 32;     // 限制自动bin数量
        option.cross_num_print_missing = 1000;  // 减少输出
        
        // 减少覆盖点
        coverpoint important_signal {
            option.weight = 5;        // 增加重要信号的权重
            bins critical[] = {[0:15]};
        }
        
        // 减少交叉维度
        cross important_signal, other_signal {
            option.cross_auto_bin_max = 16;  // 限制交叉bin数量
        }
    endgroup
    
    // 条件采样减少开销
    covergroup conditional_cg with function sample_when_needed();
        option.sample_on = 0;  // 禁用自动采样
        
        coverpoint data {
            // 只在特定条件下采样
            bins values[] = {[0:255]};
        }
    endgroup
    
    function void sample_conditionally(bit sample_en);
        if (sample_en && $time > 1000) begin  // 条件采样
            conditional_cg.sample();
        end
    endfunction
endclass

八、总结

8.1 Covergroup的关键特性:

  1. 覆盖点(Coverpoints):收集变量值的覆盖率
  2. 仓(Bins):定义感兴趣的值或值范围
  3. 交叉覆盖(Cross Coverage):检查多个变量组合的覆盖率
  4. 选项(Options):控制采样行为、权重、目标等
  5. 条件覆盖:使用iff、with等条件

8.2 实际应用建议:

  1. 聚焦关键功能:优先覆盖设计的关键路径和功能
  2. 合理划分bins:避免过多或过少的bins
  3. 管理交叉覆盖:注意交叉爆炸问题,合理选择交叉维度
  4. 分层覆盖:从基本功能到复杂场景逐步覆盖
  5. 定期审查:定期检查覆盖率报告,指导测试开发

8.3 常见陷阱:

  1. 覆盖爆炸:过多的交叉覆盖导致性能问题
  2. 无效覆盖:覆盖了不重要的功能点
  3. 采样时机不当:在信号不稳定时采样
  4. 条件过于严格:导致某些场景永远无法覆盖

Covergroup是验证过程中确保功能覆盖完整性的重要工具,合理使用可以显著提高验证质量。

posted on 2026-01-29 21:51  SOC验证工程师  阅读(6)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3