Hi,
You can do it by adding the covergroup under the register block. So, you will be able to add the cross coverpoint into it as well.
Following is the example implementation code. It is based on a register model class auto-generated by code generator tools, and user create a derived class extends from the register block class (auto-generated register block class). Then use set_type_override_by_type to replace the base class with the derived one. With this, whenever there is a write or read happens to a register, the sample method will be automatically called.
You may find this cookbook very useful to understand the difference between sample and sample_values.
// Your original register model package.
// Normally autogenerated using register model generator such as
// Magillem-MRV, Agnisys, CSRCompiler, Cadence's blueprint etc
package myreg_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
// myreg_reg_c Register Definition
class myreg_reg_c extends uvm_reg;
`uvm_object_utils(myreg_reg_c)
rand uvm_reg_field all;
// This the autogeneated covergroup for each register
covergroup fields_cg ();
option.per_instance = 1;
all_cp : coverpoint all.get()
{bins all[16] = {['h0:'hFFFFFFFF]}; illegal_bins bad = default;}
endgroup : fields_cg
// Constructor
function new(string name = "myreg_reg_c");
super.new(name, 32, build_coverage(UVM_CVR_FIELD_VALS));
if (has_coverage(UVM_CVR_FIELD_VALS))
fields_cg = new();
endfunction : new
// Build
virtual function void build();
// create bitfield
all = uvm_reg_field::type_id::create("all");
// configure
all.configure(
.parent (this),
.size (32),
.lsb_pos (0),
.access ("RW"),
.volatile (0),
.reset (32'b0),
.has_reset (1),
.is_rand (1),
.individually_accessible(0));
endfunction : build
// This is how mostly the autogerated code implement the field's
// coverage sampling.
function void sample_values();
super.sample_values();
if (has_coverage(UVM_CVR_FIELD_VALS))
begin
`uvm_info(get_type_name(), "UVM_CVR_FIELD_VALS enabled", UVM_HIGH)
if (fields_cg != null)
begin
fields_cg.sample();
`uvm_info(get_type_name(), "fields_cg.sample called", UVM_HIGH)
end
end
endfunction : sample_values
endclass : myreg_reg_c
class myblock_ral_c extends uvm_reg_block;
rand myreg_reg_c myreg;
function new(string name = "myblock_ral_c");
super.new(name, build_coverage(UVM_CVR_ADDR_MAP+UVM_CVR_FIELD_VALS));
`uvm_info(get_type_name(), $sformatf("has_coverage=%0b", has_coverage(UVM_CVR_FIELD_VALS)), UVM_NONE)
endfunction: new
virtual function void build();
default_map = create_map("", 0, 4, UVM_BIG_ENDIAN, 0);
myreg = myreg_reg_c::type_id::create("myreg",,get_full_name());
myreg.configure(this, null);
myreg.build();
// Create the address map
default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 1);
default_map.add_reg(myreg, 'h0000, "RW");
endfunction : build
`uvm_object_utils(myblock_ral_c)
virtual function void sample(uvm_reg_addr_t offset, bit is_read, uvm_reg_map map);
super.sample(offset, is_read, map);
`uvm_info(get_type_name(), "sample called", UVM_HIGH)
endfunction : sample
virtual function void sample_values();
super.sample_values();
`uvm_info(get_type_name(), "sample_values called", UVM_HIGH)
endfunction : sample_values
endclass : myblock_ral_c
endpackage : myreg_pkg
module test();
import uvm_pkg::*;
`include "uvm_macros.svh"
import myreg_pkg::*;
// Create your derived class and add the covergroup which has all
// of the coverpoints of your register fields and also add the cross
// definition on the same covergroup
class myblock_ral_ext_c extends myblock_ral_c;
`uvm_object_utils(myblock_ral_ext_c)
// Here you put all of your fields coverpoints and crosses
covergroup fields_cg;
option.per_instance = 1;
myreg_all_cp : coverpoint myreg.all.get()
{bins all[16] = {['h0:'hFFFFFFFF]}; illegal_bins bad = default;}
// mycross_cp : cross ...
endgroup : fields_cg
function new(string name = "myblock_ral_ext_c");
super.new(name);
`uvm_info(get_type_name(), $sformatf("has_coverage=%0b", has_coverage(UVM_CVR_FIELD_VALS)), UVM_NONE)
if (has_coverage(UVM_CVR_FIELD_VALS))
fields_cg = new();
endfunction: new
virtual function void build();
super.build();
endfunction : build
virtual function void sample (
uvm_reg_addr_t offset,
bit is_read,
uvm_reg_map map
);
super.sample(offset, is_read, map);
`uvm_info(get_type_name(), "sample called", UVM_HIGH)
if (has_coverage(UVM_CVR_FIELD_VALS))
begin
`uvm_info(get_type_name(), "UVM_CVR_FIELD_VALS enabled", UVM_HIGH)
if (fields_cg != null)
begin
fields_cg.sample();
`uvm_info(get_type_name(), "fields_cg.sample called", UVM_HIGH)
end
end
endfunction : sample
virtual function void sample_values();
super.sample_values();
`uvm_info(get_type_name(), "sample_values called", UVM_HIGH)
endfunction : sample_values
endclass : myblock_ral_ext_c
// I am reusing the same register adapter's agent the being provided
// under the examples of UVM install package just to make sure this
// example code works.
// Please make sure you add +incdir+$UVM_HOME/examples/simple/registers/common
//
// !! Please don't use this agent, instead use the
// dedicated bus protocol UVC which will be specific for your design.!!
`include "reg_agent.sv"
class mydut;
static task rw(reg_rw rw);
endtask
endclass : mydut
class mytest extends uvm_test;
`uvm_component_utils(mytest)
myblock_ral_c m_ral;
reg_agent#(mydut) m_agent;
uvm_reg_predictor#(reg_rw) m_predict;
function new (string n = "mytest", uvm_component p = null);
super.new(n, p);
//
// ! IMPORTANT !
// Set the type override in the test's constructor.
// Please dont call it under any child component
//
set_type_override_by_type(
myblock_ral_c::get_type(),
myblock_ral_ext_c::get_type());
// Providing mechanism to control UVM register's coverage model
// from plusargs
begin
uvm_reg_cvr_t _cov_models;
_cov_models = UVM_NO_COVERAGE;
if ($test$plusargs("UVM_CVR_ALL")) _cov_models |= UVM_CVR_ALL;
if ($test$plusargs("UVM_CVR_REG_BITS")) _cov_models |= UVM_CVR_REG_BITS;
if ($test$plusargs("UVM_CVR_ADDR_MAP")) _cov_models |= UVM_CVR_ADDR_MAP;
if ($test$plusargs("UVM_CVR_FIELD_VALS")) _cov_models |= UVM_CVR_FIELD_VALS;
if ($test$plusargs("UVM_CVR_ALL")) _cov_models |= UVM_CVR_ALL;
uvm_reg::include_coverage("*", _cov_models);
end
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_ral = myblock_ral_c::type_id::create("m_ral", , get_full_name());
m_ral.build();
m_agent = reg_agent#(mydut)::type_id::create("m_agent", this);
m_predict = uvm_reg_predictor#(reg_rw)::type_id::create("m_predict", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
reg2rw_adapter reg2rw = new("reg2rw");
m_ral.default_map.set_sequencer(m_agent.sqr, reg2rw);
m_ral.lock_model();
m_predict.map = m_ral.default_map;
m_predict.adapter = reg2rw;
m_agent.mon.ap.connect(m_predict.bus_in);
m_ral.default_map.set_auto_predict(0);
endfunction : connect_phase
task run_phase(uvm_phase phase);
uvm_status_e status;
if (uvm_top.get_report_verbosity_level() >= UVM_HIGH)
this.print();
phase.raise_objection(this);
repeat (10)
begin
#10ns;
void'(m_ral.randomize());
m_ral.update(status);
m_ral.sample_values();
if (uvm_top.get_report_verbosity_level() >= UVM_HIGH)
m_ral.print();
end
phase.drop_objection(this);
endtask : run_phase
endclass : mytest
initial $timeformat(-9, 3, " ns", 15);
initial run_test("mytest");
endmodule : test
Hope this helps.
-Baser