文章目录
1.uvm 代码实现
1.1 chnl.pkg.sv
chnl_tans类
相比于SV验证模块代码,成员变量没有发生什么变化,通过使用UVM做了类的注册以及域的自动化的声明,可以使用UVM核心基类的克隆、打印、比较等一些常见方法,进而替换了原来的clone()方法。
class chnl_trans extends uvm_sequence_item;
rand bit[31:0] data[];
rand int ch_id;
rand int pkt_id;
rand int data_nidles;
rand int pkt_nidles;
bit rsp;
constraint cstr{
soft data.size inside {[4:32]};
foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id<<24) + (this.pkt_id<<8) + i;
soft ch_id == 0;
soft pkt_id == 0;
soft data_nidles inside {[0:2]};
soft pkt_nidles inside {[1:10]};
};
`uvm_object_utils_begin(chnl_trans)
`uvm_field_array_int(data, UVM_ALL_ON)
`uvm_field_int(ch_id, UVM_ALL_ON)
`uvm_field_int(pkt_id, UVM_ALL_ON)
`uvm_field_int(data_nidles, UVM_ALL_ON)
`uvm_field_int(pkt_nidles, UVM_ALL_ON)
`uvm_field_int(rsp, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "chnl_trans");
super.new(name);
endfunction
endclass: chnl_trans
chnl_driver类
- 继承于
uvm_driver是一个参数类,所以得uvm_driver #(chnl_trans)。而SV中的run()转换成了UVM中run_phase(uvm_phase phase) do_driver()方法里面void'($cast(rsp, req.clone())),是因为UVM核心基类的克隆方法返回的是uvm_object类型,所以需要把父类的句柄转换为子类的句柄,而req.clone()这个父类句柄指向的是一个子类的对象,所以能转换成功chnl_write()方法中把SV中的$display替换成了UVM中的'uvm_info()
class chnl_driver extends uvm_driver #(chnl_trans);
local virtual chnl_intf intf;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
`uvm_component_utils(chnl_driver)
function new (string name = "chnl_driver", uvm_component parent);
super.new(name, parent);
endfunction
function void set_interface(virtual chnl_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run_phase(uvm_phase phase);
fork
this.do_drive();
this.do_reset();
join
endtask
task do_reset();
forever begin
@(negedge intf.rstn);
intf.ch_valid <= 0;
intf.ch_data <= 0;
end
endtask
task do_drive();
chnl_trans req, rsp;
@(posedge intf.rstn);
forever begin
this.req_mb.get(req);
this.chnl_write(req);
void'($cast(rsp, req.clone()));
rsp.rsp = 1;
this.rsp_mb.put(rsp);
end
endtask
task chnl_write(input chnl_trans t);
foreach(t.data[i]) begin
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 1;
intf.drv_ck.ch_data <= t.data[i];
@(negedge intf.clk);
wait(intf.ch_ready === 'b1);
`uvm_info(get_type_name(), $sformatf("sent data 'h%8x", t.data[i]), UVM_HIGH)
repeat(t.data_nidles) chnl_idle();
end
repeat(t.pkt_nidles) chnl_idle();
endtask
task chnl_idle();
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 0;
intf.drv_ck.ch_data <= 0;
endtask
endclass: chnl_driver
chnl_generator类
相比于SV使用构建函数new()来创建对象, send_trans()方法中使用req = chnl_trans::type_id::create("req")来创建对象
class chnl_generator extends uvm_component;
rand int pkt_id = 0;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
constraint cstr{
soft ch_id == -1;
soft pkt_id == 0;
soft data_size == -1;
soft data_nidles == -1;
soft pkt_nidles == -1;
soft ntrans == 10;
}
`uvm_component_utils_begin(chnl_generator)
`uvm_field_int(pkt_id, UVM_ALL_ON)
`uvm_field_int(ch_id, UVM_ALL_ON)
`uvm_field_int(data_nidles, UVM_ALL_ON)
`uvm_field_int(pkt_nidles, UVM_ALL_ON)
`uvm_field_int(data_size, UVM_ALL_ON)
`uvm_field_int(ntrans, UVM_ALL_ON)
`uvm_component_utils_end
function new (string name = "chnl_generator", uvm_component parent);
super.new(name, parent);
this.req_mb = new();
this.rsp_mb = new();
endfunction
task start();
repeat(ntrans) send_trans();
endtask
task send_trans();
;
assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
local::pkt_id >= 0 -> pkt_id == local::pkt_id;
local::data_nidles >= 0 -> data_nidles == local::data_nidles;
local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
local::data_size >0 -> data.size() == local::data_size;
})
else $fatal("[RNDFAIL] channel packet randomization failure!");
this.pkt_id++;
`uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
this.req_mb.put(req);
this.rsp_mb.get(rsp);
`uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
function string sprint();
string s;
s = {s, $sformatf("=======================================\n")};
s = {s, $sformatf("chnl_generator object content is as below: \n")};
s = {s, super.sprint()};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
function void post_randomize();
string s;
s = {"AFTER RANDOMIZATION \n", this.sprint()};
`uvm_info(get_type_name(), s, UVM_HIGH)
endfunction
endclass: chnl_generator
chnl_monitor类
将SV中的run()替换成了UVM中的run_phase(uvm_phase phase)
class chnl_monitor extends uvm_monitor;
local virtual chnl_intf intf;
mailbox #(mon_data_t) mon_mb;
`uvm_component_utils(chnl_monitor)
function new(string name="chnl_monitor", uvm_component parent);
super.new(name, parent);
endfunction
function void set_interface(virtual chnl_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run_phase(uvm_phase phase);
this.mon_trans();
endtask
task mon_trans();
mon_data_t m;
forever begin
@(posedge intf.clk iff (intf.mon_ck.ch_valid==='b1 && intf.mon_ck.ch_ready==='b1));
m.data = intf.mon_ck.ch_data;
mon_mb.put(m);
`uvm_info(get_type_name(), $sformatf("monitored channel data 'h%8x", m.data), UVM_HIGH)
end
endtask
endclass: chnl_monitor
chnl_agent类
- SV验证结构中例化和连接都发生在构建函数
new()里面,而UVM中例化是在build_phase()方法中,并且通过create()来例化创建对象。 - SV验证结构中
run()需要调用子一级的run()方法,而在UVM中不需要手动去调用子一级的run_phase(),因为run_phase是按照层次来执行的,是由uvm_root来安排的,会自动调用,所以这里run_phase没有内容
class chnl_agent extends uvm_agent;
chnl_driver driver;
chnl_monitor monitor;
local virtual chnl_intf vif;
`uvm_component_utils(chnl_agent)
function new(string name = "chnl_agent", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = chnl_driver::type_id::create("driver", this);
monitor = chnl_monitor::type_id::create("monitor", this);
endfunction
function void set_interface(virtual chnl_intf vif);
this.vif = vif;
driver.set_interface(vif);
monitor.set_interface(vif);
endfunction
task run_phase(uvm_phase phase);
// NOTE:: No more needed to call run manually
// fork
// driver.run();
// monitor.run();
// join
endtask
endclass: chnl_agent
reg_pk、garb_pkg、fmt_pkg变化与chnl_pkg相似
1.2 mcdf_pkg
加载各个组件包
import chnl_pkg::*;
import reg_pkg::*;
import arb_pkg::*;
import fmt_pkg::*;
mcdf参考模型
class mcdf_refmod extends uvm_component;
local virtual mcdf_intf intf;
mcdf_reg_t regs[3];
mailbox #(reg_trans) reg_mb;
mailbox #(mon_data_t) in_mbs[3];
mailbox #(fmt_trans) out_mbs[3];
`uvm_component_utils(mcdf_refmod)
function new (string name = "mcdf_refmod", uvm_component parent);
super.new(name, parent);
foreach(this.out_mbs[i]) this.out_mbs[i] = new();
endfunction
task run_phase(uvm_phase phase);
fork
do_reset();
this.do_reg_update();
do_packet(0);
do_packet(1);
do_packet(2);
join
endtask
task do_reg_update();
reg_trans t;
forever begin
this.reg_mb.get(t);
if(t.addr[7:4] == 0 && t.cmd == `WRITE) begin
this.regs[t.addr[3:2]].en = t.data[0];
this.regs[t.addr[3:2]].prio = t.data[2:1];
this.regs[t.addr[3:2]].len = t.data[5:3];
end
else if(t.addr[7:4] == 1 && t.cmd == `READ) begin
this.regs[t.addr[3:2]].avail = t.data[7:0];
end
end
endtask
task do_packet(int id);
fmt_trans ot;
mon_data_t it;
forever begin
this.in_mbs[id].peek(it);
ot = new();
ot.length = 4 << (this.get_field_value(id, RW_LEN) & 'b11);
ot.data = new[ot.length];
ot.ch_id = id;
foreach(ot.data[m]) begin
this.in_mbs[id].get(it);
ot.data[m] = it.data;
end
this.out_mbs[id].put(ot);
end
endtask
function int get_field_value(int id, mcdf_field_t f);
case(f)
RW_LEN: return regs[id].len;
RW_PRIO: return regs[id].prio;
RW_EN: return regs[id].en;
RD_AVAIL: return regs[id].avail;
endcase
endfunction
task do_reset();
forever begin
@(negedge intf.rstn);
foreach(regs[i]) begin
regs[i].len = 'h0;
regs[i].prio = 'h3;
regs[i].en = 'h1;
regs[i].avail = 'h20;
end
end
endtask
function void set_interface(virtual mcdf_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
endclass: mcdf_refmod
MCDF checker (scoreboard)
class mcdf_checker extends uvm_scoreboard;
local int err_count;
local int total_count;
local int chnl_count[3];
local virtual chnl_intf chnl_vifs[3];
local virtual arb_intf arb_vif;
local virtual mcdf_intf mcdf_vif;
local mcdf_refmod refmod;
mailbox #(mon_data_t) chnl_mbs[3];
mailbox #(fmt_trans) fmt_mb;
mailbox #(reg_trans) reg_mb;
mailbox #(fmt_trans) exp_mbs[3];
`uvm_component_utils(mcdf_checker)
function new (string name = "mcdf_checker", uvm_component parent);
super.new(name, parent);
this.err_count = 0;
this.total_count = 0;
foreach(this.chnl_count[i]) this.chnl_count[i] = 0;
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
foreach(this.chnl_mbs[i]) this.chnl_mbs[i] = new();
this.fmt_mb = new();
this.reg_mb = new();
this.refmod = mcdf_refmod::type_id::create("refmod", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
foreach(this.refmod.in_mbs[i]) begin
this.refmod.in_mbs[i] = this.chnl_mbs[i];
this.exp_mbs[i] = this.refmod.out_mbs[i];
end
this.refmod.reg_mb = this.reg_mb;
endfunction
function void set_interface(virtual mcdf_intf mcdf_vif, virtual chnl_intf chnl_vifs[3], virtual arb_intf arb_vif);
if(mcdf_vif == null)
$error("mcdf interface handle is NULL, please check if target interface has been intantiated");
else begin
this.mcdf_vif = mcdf_vif;
this.refmod.set_interface(mcdf_vif);
end
if(chnl_vifs[0] == null || chnl_vifs[1] == null || chnl_vifs[2] == null)
$error("chnl interface handle is NULL, please check if target interface has been intantiated");
else begin
this.chnl_vifs = chnl_vifs;
end
if(arb_vif == null)
$error("arb interface handle is NULL, please check if target interface has been intantiated");
else begin
this.arb_vif = arb_vif;
end
endfunction
task run_phase(uvm_phase phase);
fork
this.do_channel_disable_check(0);
this.do_channel_disable_check(1);
this.do_channel_disable_check(2);
this.do_arbiter_priority_check();
this.do_data_compare();
this.refmod.run();
join
endtask
task do_data_compare();
fmt_trans expt, mont;
bit cmp;
forever begin
this.fmt_mb.get(mont);
this.exp_mbs[mont.ch_id].get(expt);
cmp = mont.compare(expt);
this.total_count++;
this.chnl_count[mont.ch_id]++;
if(cmp == 0) begin
this.err_count++;
`uvm_error("[CMPERR]", $sformatf("%0dth times comparing but failed! MCDF monitored output packet is different with reference model output", this.total_count))
end
else begin
`uvm_info("[CMPSUC]",$sformatf("%0dth times comparing and succeeded! MCDF monitored output packet is the same with reference model output", this.total_count), UVM_LOW)
end
end
endtask
task do_channel_disable_check(int id);
forever begin
@(posedge this.mcdf_vif.clk iff (this.mcdf_vif.rstn && this.mcdf_vif.mon_ck.chnl_en[id]===0));
if(this.chnl_vifs[id].mon_ck.ch_valid===1 && this.chnl_vifs[id].mon_ck.ch_ready===1)
`uvm_error("[CHKERR]", "ERROR! when channel disabled, ready signal raised when valid high")
end
endtask
task do_arbiter_priority_check();
int id;
forever begin
@(posedge this.arb_vif.clk iff (this.arb_vif.rstn && this.arb_vif.mon_ck.f2a_id_req===1));
id = this.get_slave_id_with_prio();
if(id >= 0) begin
@(posedge this.arb_vif.clk);
if(this.arb_vif.mon_ck.a2s_acks[id] !== 1)
`uvm_error("[CHKERR]", $sformatf("ERROR! arbiter received f2a_id_req===1 and channel[%0d] raising request with high priority, but is not granted by arbiter", id))
end
end
endtask
function int get_slave_id_with_prio();
int id=-1;
int prio=999;
foreach(this.arb_vif.mon_ck.slv_prios[i]) begin
if(this.arb_vif.mon_ck.slv_prios[i] < prio && this.arb_vif.mon_ck.slv_reqs[i]===1) begin
id = i;
prio = this.arb_vif.mon_ck.slv_prios[i];
end
end
return id;
endfunction
function void report_phase(uvm_phase phase);
string s;
super.report_phase(phase);
s = "\n---------------------------------------------------------------\n";
s = {s, "CHECKER SUMMARY \n"};
s = {s, $sformatf("total comparison count: %0d \n", this.total_count)};
foreach(this.chnl_count[i]) s = {s, $sformatf(" channel[%0d] comparison count: %0d \n", i, this.chnl_count[i])};
s = {s, $sformatf("total error count: %0d \n", this.err_count)};
foreach(this.chnl_mbs[i]) begin
if(this.chnl_mbs[i].num() != 0)
s = {s, $sformatf("WARNING:: chnl_mbs[%0d] is not empty! size = %0d \n", i, this.chnl_mbs[i].num())};
end
if(this.fmt_mb.num() != 0)
s = {s, $sformatf("WARNING:: fmt_mb is not empty! size = %0d \n", this.fmt_mb.num())};
s = {s, "---------------------------------------------------------------\n"};
`uvm_info(get_type_name(), s, UVM_LOW)
endfunction
endclass: mcdf_checker
mcdf 覆盖模型
class mcdf_coverage extends uvm_component;
local virtual chnl_intf chnl_vifs[3];
local virtual arb_intf arb_vif;
local virtual mcdf_intf mcdf_vif;
local virtual reg_intf reg_vif;
local virtual fmt_intf fmt_vif;
local int delay_req_to_grant;
`uvm_component_utils(mcdf_coverage)
covergroup cg_mcdf_reg_write_read;
addr: coverpoint reg_vif.mon_ck.cmd_addr {
type_option.weight = 0;
bins slv0_rw_addr = {`SLV0_RW_ADDR};
bins slv1_rw_addr = {`SLV1_RW_ADDR};
bins slv2_rw_addr = {`SLV2_RW_ADDR};
bins slv0_r_addr = {`SLV0_R_ADDR };
bins slv1_r_addr = {`SLV1_R_ADDR };
bins slv2_r_addr = {`SLV2_R_ADDR };
}
cmd: coverpoint reg_vif.mon_ck.cmd {
type_option.weight = 0;
bins write = {`WRITE};
bins read = {`READ};
bins idle = {`IDLE};
}
cmdXaddr: cross cmd, addr {
bins slv0_rw_addr = binsof(addr.slv0_rw_addr);
bins slv1_rw_addr = binsof(addr.slv1_rw_addr);
bins slv2_rw_addr = binsof(addr.slv2_rw_addr);
bins slv0_r_addr = binsof(addr.slv0_r_addr );
bins slv1_r_addr = binsof(addr.slv1_r_addr );
bins slv2_r_addr = binsof(addr.slv2_r_addr );
bins write = binsof(cmd.write);
bins read = binsof(cmd.read );
bins idle = binsof(cmd.idle );
bins write_slv0_rw_addr = binsof(cmd.write) && binsof(addr.slv0_rw_addr);
bins write_slv1_rw_addr = binsof(cmd.write) && binsof(addr.slv1_rw_addr);
bins write_slv2_rw_addr = binsof(cmd.write) && binsof(addr.slv2_rw_addr);
bins read_slv0_rw_addr = binsof(cmd.read) && binsof(addr.slv0_rw_addr);
bins read_slv1_rw_addr = binsof(cmd.read) && binsof(addr.slv1_rw_addr);
bins read_slv2_rw_addr = binsof(cmd.read) && binsof(addr.slv2_rw_addr);
bins read_slv0_r_addr = binsof(cmd.read) && binsof(addr.slv0_r_addr);
bins read_slv1_r_addr = binsof(cmd.read) && binsof(addr.slv1_r_addr);
bins read_slv2_r_addr = binsof(cmd.read) && binsof(addr.slv2_r_addr);
}
endgroup
covergroup cg_mcdf_reg_illegal_access;
addr: coverpoint reg_vif.mon_ck.cmd_addr {
type_option.weight = 0;
bins legal_rw = {`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
bins legal_r = {`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
bins illegal = {[8'h20:$], 8'hC, 8'h1C};
}
cmd: coverpoint reg_vif.mon_ck.cmd {
type_option.weight = 0;
bins write = {`WRITE};
bins read = {`READ};
}
wdata: coverpoint reg_vif.mon_ck.cmd_data_m2s {
type_option.weight = 0;
bins legal = {[0:'h3F]};
bins illegal = {['h40:$]};
}
rdata: coverpoint reg_vif.mon_ck.cmd_data_s2m {
type_option.weight = 0;
bins legal = {[0:'hFF]};
illegal_bins illegal = default;
}
cmdXaddrXdata: cross cmd, addr, wdata, rdata {
bins addr_legal_rw = binsof(addr.legal_rw);
bins addr_legal_r = binsof(addr.legal_r);
bins addr_illegal = binsof(addr.illegal);
bins cmd_write = binsof(cmd.write);
bins cmd_read = binsof(cmd.read);
bins wdata_legal = binsof(wdata.legal);
bins wdata_illegal = binsof(wdata.illegal);
bins rdata_legal = binsof(rdata.legal);
bins write_illegal_addr = binsof(cmd.write) && binsof(addr.illegal);
bins read_illegal_addr = binsof(cmd.read) && binsof(addr.illegal);
bins write_illegal_rw_data = binsof(cmd.write) && binsof(addr.legal_rw) && binsof(wdata.illegal);
bins write_illegal_r_data = binsof(cmd.write) && binsof(addr.legal_r) && binsof(wdata.illegal);
}
endgroup
covergroup cg_channel_disable;
ch0_en: coverpoint mcdf_vif.mon_ck.chnl_en[0] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch1_en: coverpoint mcdf_vif.mon_ck.chnl_en[1] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch2_en: coverpoint mcdf_vif.mon_ck.chnl_en[2] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch0_vld: coverpoint chnl_vifs[0].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
ch1_vld: coverpoint chnl_vifs[1].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
ch2_vld: coverpoint chnl_vifs[2].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
chenXchvld: cross ch0_en, ch1_en, ch2_en, ch0_vld, ch1_vld, ch2_vld {
bins ch0_en = binsof(ch0_en.en);
bins ch0_dis = binsof(ch0_en.dis);
bins ch1_en = binsof(ch1_en.en);
bins ch1_dis = binsof(ch1_en.dis);
bins ch2_en = binsof(ch2_en.en);
bins ch2_dis = binsof(ch2_en.dis);
bins ch0_hi = binsof(ch0_vld.hi);
bins ch0_lo = binsof(ch0_vld.lo);
bins ch1_hi = binsof(ch1_vld.hi);
bins ch1_lo = binsof(ch1_vld.lo);
bins ch2_hi = binsof(ch2_vld.hi);
bins ch2_lo = binsof(ch2_vld.lo);
bins ch0_en_vld = binsof(ch0_en.en) && binsof(ch0_vld.hi);
bins ch0_dis_vld = binsof(ch0_en.dis) && binsof(ch0_vld.hi);
bins ch1_en_vld = binsof(ch1_en.en) && binsof(ch1_vld.hi);
bins ch1_dis_vld = binsof(ch1_en.dis) && binsof(ch1_vld.hi);
bins ch2_en_vld = binsof(ch2_en.en) && binsof(ch2_vld.hi);
bins ch2_dis_vld = binsof(ch2_en.dis) && binsof(ch2_vld.hi);
}
endgroup
covergroup cg_arbiter_priority;
ch0_prio: coverpoint arb_vif.mon_ck.slv_prios[0] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
ch1_prio: coverpoint arb_vif.mon_ck.slv_prios[1] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
ch2_prio: coverpoint arb_vif.mon_ck.slv_prios[2] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
endgroup
covergroup cg_formatter_length;
id: coverpoint fmt_vif.mon_ck.fmt_chid {
bins ch0 = {0};
bins ch1 = {1};
bins ch2 = {2};
illegal_bins illegal = default;
}
length: coverpoint fmt_vif.mon_ck.fmt_length {
bins len4 = {4};
bins len8 = {8};
bins len16 = {16};
bins len32 = {32};
illegal_bins illegal = default;
}
endgroup
covergroup cg_formatter_grant();
delay_req_to_grant: coverpoint this.delay_req_to_grant {
bins delay1 = {1};
bins delay2 = {2};
bins delay3_or_more = {[3:10]};
illegal_bins illegal = {0};
}
endgroup
function new (string name = "mcdf_coverage", uvm_component parent);
super.new(name, parent);
this.cg_mcdf_reg_write_read = new();
this.cg_mcdf_reg_illegal_access = new();
this.cg_channel_disable = new();
this.cg_arbiter_priority = new();
this.cg_formatter_length = new();
this.cg_formatter_grant = new();
endfunction
task run_phase(uvm_phase phase);
fork
this.do_reg_sample();
this.do_channel_sample();
this.do_arbiter_sample();
this.do_formater_sample();
join
endtask
task do_reg_sample();
forever begin
@(posedge reg_vif.clk iff reg_vif.rstn);
this.cg_mcdf_reg_write_read.sample();
this.cg_mcdf_reg_illegal_access.sample();
end
endtask
task do_channel_sample();
forever begin
@(posedge mcdf_vif.clk iff mcdf_vif.rstn);
if(chnl_vifs[0].mon_ck.ch_valid===1
|| chnl_vifs[1].mon_ck.ch_valid===1
|| chnl_vifs[2].mon_ck.ch_valid===1)
this.cg_channel_disable.sample();
end
endtask
task do_arbiter_sample();
forever begin
@(posedge arb_vif.clk iff arb_vif.rstn);
if(arb_vif.slv_reqs[0]!==0 || arb_vif.slv_reqs[1]!==0 || arb_vif.slv_reqs[2]!==0)
this.cg_arbiter_priority.sample();
end
endtask
task do_formater_sample();
fork
forever begin
@(posedge fmt_vif.clk iff fmt_vif.rstn);
if(fmt_vif.mon_ck.fmt_req === 1)
this.cg_formatter_length.sample();
end
forever begin
@(posedge fmt_vif.mon_ck.fmt_req);
this.delay_req_to_grant = 0;
forever begin
if(fmt_vif.fmt_grant === 1) begin
this.cg_formatter_grant.sample();
break;
end
else begin
@(posedge fmt_vif.clk);
this.delay_req_to_grant++;
end
end
end
join
endtask
function void report_phase(uvm_phase phase);
string s;
super.report_phase(phase);
s = "\n---------------------------------------------------------------\n";
s = {s, "COVERAGE SUMMARY \n"};
s = {s, $sformatf("total coverage: %.1f \n", $get_coverage())};
s = {s, $sformatf(" cg_mcdf_reg_write_read coverage: %.1f \n", this.cg_mcdf_reg_write_read.get_coverage())};
s = {s, $sformatf(" cg_mcdf_reg_illegal_access coverage: %.1f \n", this.cg_mcdf_reg_illegal_access.get_coverage())};
s = {s, $sformatf(" cg_channel_disable_test coverage: %.1f \n", this.cg_channel_disable.get_coverage())};
s = {s, $sformatf(" cg_arbiter_priority_test coverage: %.1f \n", this.cg_arbiter_priority.get_coverage())};
s = {s, $sformatf(" cg_formatter_length_test coverage: %.1f \n", this.cg_formatter_length.get_coverage())};
s = {s, $sformatf(" cg_formatter_grant_test coverage: %.1f \n", this.cg_formatter_grant.get_coverage())};
s = {s, "---------------------------------------------------------------\n"};
`uvm_info(get_type_name(), s, UVM_LOW)
endfunction
virtual function void set_interface(virtual chnl_intf ch_vifs[3]
,virtual reg_intf reg_vif
,virtual arb_intf arb_vif
,virtual fmt_intf fmt_vif
,virtual mcdf_intf mcdf_vif
);
this.chnl_vifs = ch_vifs;
this.arb_vif = arb_vif;
this.reg_vif = reg_vif;
this.fmt_vif = fmt_vif;
this.mcdf_vif = mcdf_vif;
if(chnl_vifs[0] == null || chnl_vifs[1] == null || chnl_vifs[2] == null)
$error("chnl interface handle is NULL, please check if target interface has been intantiated");
if(arb_vif == null)
$error("arb interface handle is NULL, please check if target interface has been intantiated");
if(reg_vif == null)
$error("reg interface handle is NULL, please check if target interface has been intantiated");
if(fmt_vif == null)
$error("fmt interface handle is NULL, please check if target interface has been intantiated");
if(mcdf_vif == null)
$error("mcdf interface handle is NULL, please check if target interface has been intantiated");
endfunction
endclass: mcdf_coverage
mcdf顶层环境
class mcdf_env extends uvm_env;
chnl_agent chnl_agts[3];
reg_agent reg_agt;
fmt_agent fmt_agt;
mcdf_checker chker;
mcdf_coverage cvrg;
`uvm_component_utils(mcdf_env)
function new (string name = "mcdf_env", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
this.chker = mcdf_checker::type_id::create("chker", this);
foreach(chnl_agts[i]) begin
this.chnl_agts[i] = chnl_agent::type_id::create($sformatf("chnl_agts[%0d]",i), this);
end
this.reg_agt = reg_agent::type_id::create("reg_agt", this);
this.fmt_agt = fmt_agent::type_id::create("fmt_agt", this);
this.cvrg = mcdf_coverage::type_id::create("cvrg", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
foreach(chnl_agts[i]) begin
this.chnl_agts[i].monitor.mon_mb = this.chker.chnl_mbs[i];
end
this.reg_agt.monitor.mon_mb = this.chker.reg_mb;
this.fmt_agt.monitor.mon_mb = this.chker.fmt_mb;
endfunction
task run_phase(uvm_phase phase);
endtask
function void report_phase(uvm_phase phase);
super.report_phase(phase);
endfunction
endclass: mcdf_env
mcdf base test
class mcdf_base_test extends uvm_test;
chnl_generator chnl_gens[3];
reg_generator reg_gen;
fmt_generator fmt_gen;
mcdf_env env;
local int timeout = 10; // 10 * ms
virtual chnl_intf ch0_vif ;
virtual chnl_intf ch1_vif ;
virtual chnl_intf ch2_vif ;
virtual reg_intf reg_vif ;
virtual arb_intf arb_vif ;
virtual fmt_intf fmt_vif ;
virtual mcdf_intf mcdf_vif;
`uvm_component_utils(mcdf_base_test)
function new(string name = "mcdf_base_test", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// get virtual interface from top TB
if(!uvm_config_db#(virtual chnl_intf)::get(this,"","ch0_vif", ch0_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
if(!uvm_config_db#(virtual chnl_intf)::get(this,"","ch1_vif", ch1_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
if(!uvm_config_db#(virtual chnl_intf)::get(this,"","ch2_vif", ch2_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
if(!uvm_config_db#(virtual reg_intf)::get(this,"","reg_vif", reg_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
if(!uvm_config_db#(virtual arb_intf)::get(this,"","arb_vif", arb_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
if(!uvm_config_db#(virtual fmt_intf)::get(this,"","fmt_vif", fmt_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
if(!uvm_config_db#(virtual mcdf_intf)::get(this,"","mcdf_vif", mcdf_vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
this.env = mcdf_env::type_id::create("env", this);
foreach(this.chnl_gens[i]) begin
this.chnl_gens[i] = chnl_generator::type_id::create($sformatf("chnl_gens[%0d]",i), this);
end
this.reg_gen = reg_generator::type_id::create("reg_gen", this);
this.fmt_gen = fmt_generator::type_id::create("fmt_gen", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// After get virtual interface from config_db, and then set them to
// child components
this.set_interface(ch0_vif, ch1_vif, ch2_vif, reg_vif, arb_vif, fmt_vif, mcdf_vif);
foreach(this.chnl_gens[i]) begin
this.env.chnl_agts[i].driver.req_mb = this.chnl_gens[i].req_mb;
this.env.chnl_agts[i].driver.rsp_mb = this.chnl_gens[i].rsp_mb;
end
this.env.reg_agt.driver.req_mb = this.reg_gen.req_mb;
this.env.reg_agt.driver.rsp_mb = this.reg_gen.rsp_mb;
this.env.fmt_agt.driver.req_mb = this.fmt_gen.req_mb;
this.env.fmt_agt.driver.rsp_mb = this.fmt_gen.rsp_mb;
endfunction
task run_phase(uvm_phase phase);
// NOTE:: raise objection to prevent simulation stopping
phase.raise_objection(this);
`uvm_info(get_type_name(), "=====================STARTED=====================", UVM_LOW)
this.do_reg();
this.do_formatter();
fork
this.do_data();
this.do_watchdog();
join_any
`uvm_info(get_type_name(), "=====================FINISHED=====================", UVM_LOW)
phase.drop_objection(this);
endtask
// do register configuration
virtual task do_reg();
endtask
// do external formatter down stream slave configuration
virtual task do_formatter();
endtask
// do data transition from 3 channel slaves
virtual task do_data();
endtask
// timeout watchdog to avoid simulation pending
virtual task do_watchdog();
`uvm_info(get_type_name(), "=====================WATCHDOG GUARDING=====================", UVM_LOW)
#(this.timeout * 1ms);
`uvm_info(get_type_name(), "=====================WATCHDOG BARKING=====================", UVM_LOW)
endtask
// do simulation summary
function void report_phase(uvm_phase phase);
super.report_phase(phase);
endfunction
virtual function void set_interface(virtual chnl_intf ch0_vif
,virtual chnl_intf ch1_vif
,virtual chnl_intf ch2_vif
,virtual reg_intf reg_vif
,virtual arb_intf arb_vif
,virtual fmt_intf fmt_vif
,virtual mcdf_intf mcdf_vif
);
this.env.chnl_agts[0].set_interface(ch0_vif);
this.env.chnl_agts[1].set_interface(ch1_vif);
this.env.chnl_agts[2].set_interface(ch2_vif);
this.env.reg_agt.set_interface(reg_vif);
this.env.fmt_agt.set_interface(fmt_vif);
this.env.chker.set_interface(mcdf_vif, '{ch0_vif, ch1_vif, ch2_vif}, arb_vif);
this.env.cvrg.set_interface('{ch0_vif, ch1_vif, ch2_vif}, reg_vif, arb_vif, fmt_vif, mcdf_vif);
endfunction
virtual function bit diff_value(int val1, int val2, string id = "value_compare");
if(val1 != val2) begin
`uvm_error("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2))
return 0;
end
else begin
`uvm_info("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2), UVM_LOW)
return 1;
end
endfunction
virtual task idle_reg();
void'(reg_gen.randomize() with {cmd == `IDLE; addr == 0; data == 0;});
reg_gen.start();
endtask
virtual task write_reg(bit[7:0] addr, bit[31:0] data);
void'(reg_gen.randomize() with {cmd == `WRITE; addr == local::addr; data == local::data;});
reg_gen.start();
endtask
virtual task read_reg(bit[7:0] addr, output bit[31:0] data);
void'(reg_gen.randomize() with {cmd == `READ; addr == local::addr;});
reg_gen.start();
data = reg_gen.data;
endtask
endclass: mcdf_base_test
mcdf 数据一致性测试
class mcdf_data_consistence_basic_test extends mcdf_base_test;
`uvm_component_utils(mcdf_data_consistence_basic_test)
function new(string name = "mcdf_data_consistence_basic_test", uvm_component parent);
super.new(name, parent);
endfunction
task do_reg();
bit[31:0] wr_val, rd_val;
// slv0 with len=8, prio=0, en=1
wr_val = (1<<3)+(0<<1)+1;
this.write_reg(`SLV0_RW_ADDR, wr_val);
this.read_reg(`SLV0_RW_ADDR, rd_val);
void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
// slv1 with len=16, prio=1, en=1
wr_val = (2<<3)+(1<<1)+1;
this.write_reg(`SLV1_RW_ADDR, wr_val);
this.read_reg(`SLV1_RW_ADDR, rd_val);
void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));
// slv2 with len=32, prio=2, en=1
wr_val = (3<<3)+(2<<1)+1;
this.write_reg(`SLV2_RW_ADDR, wr_val);
this.read_reg(`SLV2_RW_ADDR, rd_val);
void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));
// send IDLE command
this.idle_reg();
endtask
task do_formatter();
void'(fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
fmt_gen.start();
endtask
task do_data();
void'(chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });
void'(chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;});
void'(chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;});
fork
chnl_gens[0].start();
chnl_gens[1].start();
chnl_gens[2].start();
join
#10us; // wait until all data haven been transfered through MCDF
endtask
endclass: mcdf_data_consistence_basic_test
mcdf full random test
class mcdf_full_random_test extends mcdf_base_test;
`uvm_component_utils(mcdf_full_random_test)
function new(string name = "mcdf_full_random_test", uvm_component parent);
super.new(name, parent);
endfunction
task do_reg();
bit[31:0] wr_val, rd_val;
// slv0 with len={4,8,16,32}, prio={[0:3]}, en={[0:1]}
wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
this.write_reg(`SLV0_RW_ADDR, wr_val);
this.read_reg(`SLV0_RW_ADDR, rd_val);
void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
// slv0 with len={4,8,16,32}, prio={[0:3]}, en={[0:1]}
wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
this.write_reg(`SLV1_RW_ADDR, wr_val);
this.read_reg(`SLV1_RW_ADDR, rd_val);
void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));
// slv0 with len={4,8,16,32}, prio={[0:3]}, en={[0:1]}
wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
this.write_reg(`SLV2_RW_ADDR, wr_val);
this.read_reg(`SLV2_RW_ADDR, rd_val);
void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));
// send IDLE command
this.idle_reg();
endtask
task do_formatter();
void'(fmt_gen.randomize() with {fifo inside {SHORT_FIFO, ULTRA_FIFO}; bandwidth inside {LOW_WIDTH, ULTRA_WIDTH};});
fmt_gen.start();
endtask
task do_data();
void'(chnl_gens[0].randomize() with {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};});
void'(chnl_gens[1].randomize() with {ntrans inside {[400:600]}; ch_id==1; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};});
void'(chnl_gens[2].randomize() with {ntrans inside {[400:600]}; ch_id==2; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};});
fork
chnl_gens[0].start();
chnl_gens[1].start();
chnl_gens[2].start();
join
#10us; // wait until all data haven been transfered through MCDF
endtask
endclass: mcdf_full_random_test
2.测试的开始和结束
通过uvm_config_db完成了各个接口从 TB(硬件一侧)到验证环境mcdf_env(软件一侧)的传递。实现了以往SV函数的剥离,即UVM不需要深入到目标组件一侧,调用其set_interface()即可完成传递。这种传递方式有赖于config_db的数据存储和层次传递特性。而在mcdf_env中,暂时保留了mcdf_env的set_interface()以及各个子组件的set_interface()函数。仅修改了TB与mcdf_env之间的接口传递,其实可以移除所有的set_interface()函数,完全使用uvm_config_db的set和get方法,从而使得mcdf_env与其各个子组件之间也实现“层次剥离”,这样也就进一步促进了组件之间的独立性。
module tb;
logic clk;
logic rstn;
mcdf dut(
.clk_i (clk )
,.rstn_i (rstn )
,.cmd_i (reg_if.cmd )
,.cmd_addr_i (reg_if.cmd_addr )
,.cmd_data_i (reg_if.cmd_data_m2s)
,.cmd_data_o (reg_if.cmd_data_s2m)
,.ch0_data_i (chnl0_if.ch_data )
,.ch0_vld_i (chnl0_if.ch_valid )
,.ch0_ready_o (chnl0_if.ch_ready )
,.ch1_data_i (chnl1_if.ch_data )
,.ch1_vld_i (chnl1_if.ch_valid )
,.ch1_ready_o (chnl1_if.ch_ready )
,.ch2_data_i (chnl2_if.ch_data )
,.ch2_vld_i (chnl2_if.ch_valid )
,.ch2_ready_o (chnl2_if.ch_ready )
,.fmt_grant_i (fmt_if.fmt_grant )
,.fmt_chid_o (fmt_if.fmt_chid )
,.fmt_req_o (fmt_if.fmt_req )
,.fmt_length_o(fmt_if.fmt_length )
,.fmt_data_o (fmt_if.fmt_data )
,.fmt_start_o (fmt_if.fmt_start )
,.fmt_end_o (fmt_if.fmt_end )
);
// clock generation
initial begin
clk <= 0;
forever begin
#5 clk <= !clk;
end
end
// reset trigger
initial begin
#10 rstn <= 0;
repeat(10) @(posedge clk);
rstn <= 1;
end
import uvm_pkg::*;
`include "uvm_macros.svh"
import chnl_pkg::*;
import reg_pkg::*;
import arb_pkg::*;
import fmt_pkg::*;
import mcdf_pkg::*;
reg_intf reg_if(.*);
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);
arb_intf arb_if(.*);
fmt_intf fmt_if(.*);
mcdf_intf mcdf_if(.*);
// mcdf interface monitoring MCDF ports and signals
assign mcdf_if.chnl_en[0] = tb.dut.ctrl_regs_inst.slv0_en_o;
assign mcdf_if.chnl_en[1] = tb.dut.ctrl_regs_inst.slv1_en_o;
assign mcdf_if.chnl_en[2] = tb.dut.ctrl_regs_inst.slv2_en_o;
// arbiter interface monitoring arbiter ports
assign arb_if.slv_prios[0] = tb.dut.arbiter_inst.slv0_prio_i;
assign arb_if.slv_prios[1] = tb.dut.arbiter_inst.slv1_prio_i;
assign arb_if.slv_prios[2] = tb.dut.arbiter_inst.slv2_prio_i;
assign arb_if.slv_reqs[0] = tb.dut.arbiter_inst.slv0_req_i;
assign arb_if.slv_reqs[1] = tb.dut.arbiter_inst.slv1_req_i;
assign arb_if.slv_reqs[2] = tb.dut.arbiter_inst.slv2_req_i;
assign arb_if.a2s_acks[0] = tb.dut.arbiter_inst.a2s0_ack_o;
assign arb_if.a2s_acks[1] = tb.dut.arbiter_inst.a2s1_ack_o;
assign arb_if.a2s_acks[2] = tb.dut.arbiter_inst.a2s2_ack_o;
assign arb_if.f2a_id_req = tb.dut.arbiter_inst.f2a_id_req_i;
initial begin
// do interface configuration from top tb (HW) to verification env (SW)
uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch0_vif", chnl0_if);
uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch1_vif", chnl1_if);
uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch2_vif", chnl2_if);
uvm_config_db#(virtual reg_intf)::set(uvm_root::get(), "uvm_test_top", "reg_vif", reg_if);
uvm_config_db#(virtual arb_intf)::set(uvm_root::get(), "uvm_test_top", "arb_vif", arb_if);
uvm_config_db#(virtual fmt_intf)::set(uvm_root::get(), "uvm_test_top", "fmt_vif", fmt_if);
uvm_config_db#(virtual mcdf_intf)::set(uvm_root::get(), "uvm_test_top", "mcdf_vif", mcdf_if);
run_test("mcdf_data_consistence_basic_test");
end
endmodule
通过调用run_test()函数即完成了test的选择、例化和开始测试。可以在代码中指定UVM test,或者通过+UVM_TESTNAME=mytest在仿真选项中灵活传递test名。在run_test()执行中,它会初始化objection机制,即查看objection有没有挂起的地方,因此在test或者generator中必须至少有一处地方使用phase.raise_objection()来挂起仿真,避免仿真退出,而在仿真需要结束时,使用phase.drop_objection()来允许仿真可以退出。同时run_test()可以创建uvm_test组件,及其以下的各层组件群,并且可以调用phase控制方法,按照所有phase顺序执行。在UVM中,将对象的例化放置在build_phase中,而将对象的连接放置在connect_phase中
参考:https://blog.csdn.net/qq_39794062/article/details/114143240