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()));
//req.clone调用的核心基类的方法,永远返回的是uvm_object类型(父类),但指向的是子类对象,所有转化可以成功
rsp.rsp = 1;
this.rsp_mb.put(rsp);
end
endtask
req是通过uvm的type_id::create创建的,在创建时做了域的自动化的声明,所以可以直接用核心基类里的方法,比如clong。但需要注意的是,req.clong返回的句柄是uvm_object类型(父类),但指向的是子类对象,所以赋值给子类rsp时需要转化,同时转化可以成功。
(PS:调用clong方法会新创建一个对象,复制req到新创建的对象中,并返回新创建对象的句柄)
使用void’( )的原因:避免出现warning。
许多函数都有返回值,比如$cast( ),在进行类型转化的同时会返回1,或者0,表示转化成功或者失败,此时使用void’( )可以把转化值变为空,告诉编译器不用理会返回值;如果不使用void’( ),编译器会有warning
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = fmt_driver::type_id::create("driver", this);
monitor = fmt_monitor::type_id::create("monitor", this);
endfunction
创建组件的时候一定要用type_id::create,因为它不但提供了做override的可能,更重要的是它实现了一个字符串的层次化,字符串的层次化有助于我们配置对象。
继承于uvm_object和uvm_component的类建议都用create创建。但是端口类port继承于uvm_void,无法用type_id::create创建(uvm_void里没有该方法),port在后面的通信章节里会讲
clas 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);
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", ch2_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_gen[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);
// rpt_pkg::logname = {this.m_name, "_check.log"};
// rpt_pkg::clean_log();
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
......
//第三部分,set_interface方法的补充
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
对于接口来说:将上面的代码分为了三个部分,第一部分是对接口的声明,第二部分是在mcdf_base_test环境里得到tb层配置下来的接口,第三部分是将test层的接口传递到下面的各个子组件。
set_interface方法(将test里的接口传递到后续层次的组件中)在connect_phase里使用的原因是,在build_phase里环境还没有完成建立,build_phase是一个自顶向下的建立过程,test以下的组件都还没有建立,接口无法传递,在build_phase里只能先用config_db去得到tb层配置下的接口。在test或者env的build_phase阶段只能得到接口(config_db(# T)::get),不能往下传递接口(set_interface)
initial begin
//do interface configuration from top tb (HW) yo 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);
//If no external configured via +UVM_TESTNAME=my_test, the default test is mcdf_data_consistence_basic_test
run_test("mcdf_data_consistence_basic_test");
......
因为使用set_interface时需要各个组件的句柄,如果将set_interphase放在run_test之前,则当执行set_interface函数时,run_test还没有执行,组件还没有建立;
如果将set_interface放在run_test之后,接口还没有传递,程序开始执行就会报错。所以需要用config_db去配置接口。
打开vcs —— run0
Hierarchy:查看设计的层次 + import导入的package。
object:显示UVM的层次结构
UVM Debug图标
点击UVM Debug图标
Resource:查看uvm_config_db::{set和get}的变量配置
phase(用的非常多):阶段调试,告诉你所处阶段,以及objection的挂起调试
此时处于 run 0 的阶段,即仿真刚刚开始,从图中可以看出,此时组件的创建以及连接已经完成,开始进入run_phase阶段
在run 0 时刻,组件已经建立,可以利用object窗口查看层次结构了,但是需要选择UVM Component选项,选择后可看到此时各组件的层次结构。
如果选择UVM Object选项,会看到uvm_test类里面原有的一些类型,不方便我们了解层次结构,所以不用管。
查看组件的成员变量和方法:例如选中refmod,在member窗口中查看成员变量和方法,此时要取消勾选Base member,否则会出现许多refmod的父类成员和变量,不利于我们观察
通过watch窗口时刻关注某一个类的变化
先搜索需要观察的类,再通过class窗口观察某一个类的层次结构
通过设断点查看run_phase执行顺序,在各个组件的run_phase里设置断点,然后点run(不要点run 0,因为run_phase里有些方法也是不耗时的,run 0之后会直接执行完该方法,那么我们就无法定位到该方法了)
通过设断点可以知道各组件run_phase是并行执行的,0时刻各组件都有相应的task在执行
以下代码均来源于路科验证
package chnl_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
// channel sequence item
class chnl_trans extends uvm_sequence_item;//建议在定义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_filed_array_int(data, UVM_ALL_ON)
`uvm_filed_int(ch_id, UVM_ALL_ON)
`uvm_filed_int(pkt_id, UVM_ALL_ON)
`uvm_filed_int(data_nidles, UVM_ALL_ON)
`uvm_filed_int(pkt_nidles, UVM_ALL_ON)
`uvm_filed_int(rsp, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "chnl_trans");//组件的例化和连接放在对应的build和connect_phase里
super.new(name);
endfunction
//NOTE:: field automation already implemented clone() method
//function chnl_trans clone();
// chnl_trans c = new();
// c.data = this.data;
// c.ch_id = this.ch_id;
// c.data_nidles = this.data_nidles;
// c.pkt_nidles = this.pkt_nidles;
// c.rsp = this.rsp;
// return c;
//endfunction
//NOTE::field automation already implemented sprint() method
//function string sprint();
// string s;
// s = {s, $sformatf("======================================\n")};
// s = {s, $sformatf("chnl_trans object content is as below: \n")};
// foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
// s = {s, $sformatf("ch_id = %0d \n", this.ch_id)};
// s = {s, $sformatf("pkt_id = %0d \n", this.pkt_id)};
// s = {s, $sformatf("data_nidles = %0d \n", this.data_nidles)};
// s = {s, $sformatf("pkt_nidles = %0d \n", this.pkt_nidles)};
// s = {s, $sformatf("rsp = %0d \n", this.rsp)};
// s = {s, $sformatf("======================================\n")};
// return s;
//endfunction
endclass:chnl_trans
//channel_driver
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()));
//req.clone调用的核心基类的方法,永远返回的是uvm_object类型(父类),但指向的是子类对象,所有转化可以成功
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("send data 'h%8x", t.data[i]),UVM_HIGH)
repeat(t.data_nidles) chnl_idle();
end
endtask
task chnl_idle();
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 0;
intf.drv_ck.ch_data <= 0;
endtask
endclass:chnl_driver
//channel generator and to be replaced by sequence + sequencer later
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_filed_int(pkt_id, UVM_ALL_ON)
`uvm_filed_int(ch_id, UVM_ALL_ON)
`uvm_filed_int(data_nidles, UVM_ALL_ON)
`uvm_filed_int(pkt_nidles, UVM_ALL_ON)
`uvm_filed_int(data_size, UVM_ALL_ON)
`uvm_filed_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 send_trans();
chnl_trans req, rsp;
req = chnl_trans::type_id::create("req");//继承于uvm_object和uvm_component类建议都用create创建
//但是端口类port继承于uvm_void,无法用type_id::create创建,需要用new()
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("[RNDFALL] 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()};//用super是避免与当前方法sprint同名
//NOTE::field automation already implemented sprint() method
// s = {s, $sformatf("ntrans = %0d: \n", this.ntrans)};
// s = {s, $sformatf("ch_id = %0d \n", this.ch_id)};
// s = {s, $sformatf("pkt_id = %0d \n", this.pkt_id)};
// s = {s, $sformatf("data_nidles = %0d \n", this.data_nidles)};
// s = {s, $sformatf("pkt_nidles = %0d \n", this.pkt_nidles)};
// s = {s, $sformatf("data_size = %0d: \n", this.data_size)};
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
typedef struct packed{
bit[31:0] data;
bit[1:0] id;
} mon_data_t;
//channel monitor
class chnl_monitor extends uvm_component;
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
// channel agent
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);//调用子类的run时省略,使用自己的方法时不能省略
//NOTE::No more needs to call run manually
//fork
// drive.run();
// monitor.run();
//join
endtask
endclass:chnl_agent
endpackage