目录
参考书:UVM实战(张强)
1.项目介绍
DUT:待测设计算是一个非常简单的RTL,主要功能为实现输入信号的打拍输出,可以理解为实现打拍功能,通过valid信号控制打拍后的数据是否给到输出。
这个项目是做的第一个项目,主要是通过这个项目去理解UVM中的各个组件,phase机制以及对白皮书中除寄存器模型外的各种机制进行实验并加进一步加深理解。在此项目的基础上,做更难的项目时就可以更加得心应手。
项目文件:
项目架构:借用一下apb_watchdog中的图
2.组件代码实现与功能介绍
interface 接口文件
interface my_if(input clk,input rstn);
logic [31:0] tx_d;//输入
logic [31:0] rx_d;//输出
logic [31:0] addr_i;
logic [31:0] addr_o;
logic valid_i;
logic valid_o;
clocking cb @(posedge clk);
output tx_d;
output valid_i;
output addr_i;
input rx_d;
input valid_o;
input addr_o;
endclocking
endinterface
clocking块中的input是指DUT对测试平台的输入,output是指测试平台对DUT的输入。所以如果在测试平台中需要监测interface中的某些信号,只能监测类型为input的信号,而output类型的信号更多是跟随DUT进行变化的。
tb_top
`include "uvm_macros.svh"
import uvm_pkg::*;
module tb_top;
reg clk;
reg rstn;
if if0(clk,rstn);
test u_test( //interface与待测DUT之间的连接
.clk(clk),
.rstn(rstn),
.tx_d(if0.tx_d),
.addr_i(if0.addr_i),
.valid_i(if0.valid_i),
.rx_d(if0.rx_d),
.addr_o(if0.addr_o),
.valid_o(if0.valid_o)
);
initial begin//复位信号定义
rstn=1;
#50;
rstn=0;
#50;
rstn=1;
end
initial begin//时钟频率定义
clk=0;
forever #20 clk<=~clk;
end
initial begin
$fsdbDumpfile("test.fsdb");//波形文件
$fsdbDumpvars(0,tb_top);//生成层次
end
initial begin
run_test();//必须要有的UVM_TESTNAME相关的
end
initial begin
uvm_config_db#(virtual if)::set(null,"test_top.u_env.*","if0",if0);//派发接口
end
// initial begin
// static real clk_half_period=100.0;
// if(uvm_config_db#(real)::get(uvm_root::get(),"uvm_test_top","clk_half_period",clk_half_period))
// `uvm_info("tb_top",$sformatf("clk half period is %0f",clk_half_period),UVM_LOW)
// end
endmodule
transaction transaction是一个抽象的概念。一般来说,物理协议中的数据交换都是以帧或者包为单位的,通常在一帧或者一个包中要定义好各项参数,每个包的大小不一样。很少会有协议是以bit或者byte为单位来进行数据交换的。以以太网为例,每个包的大小至少是64byte。这个包中要包括源地址、目的地址、包的类型、整个包的CRC校验数据等。transaction就是用于模拟这种实际情况,一笔transaction就是一个包。在不同的验证平台中,会有不同的transaction。(来源: 白皮书)
在这个项目中,采用数组方式发送数据,因此要求scoreboard中数据比对方式为数组比对。一般来说,除了定义每帧中的各个参数之外,还需要对比对数据进行注册,例如,如果要比对的是data和addr,那么应该对这些参数进行注册,即field automation机制。只有经过field automation机制注册的参数才能调用compare和copy等函数进行数据的处理,否则就要自定义compare函数,往往效果不好。
代码 field automation可以把参数都加上,不用的添加注释即可。
class transaction extends uvm_sequence_item;
rand bit [31:0] data;//数据打包
rand bit [31:0] addr;
rand bit [31:0] pload[];//数组
//`uvm_object_utils(transaction)
`uvm_object_utils_begin(transaction)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_array_int(pload,UVM_ALL_ON)
// `uvm_field_int(data,UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="my_transaction");
super.new(name);
endfunction
endclass
driver 组件driver实现的功能为将transaction转换成interface级别,使transaction中随机出的、自己定义的数据能够驱动进入DUT,通过检查结果确定DUT相关功能是否完备。
//typedef class A;//callback相关
class driver extends uvm_driver#(transaction);
//uvm_analysis_port #(transaction) ap;
virtual if if0;
int delay ; // when rstn become to high , begin to send data after the number clocks
int pre_num;
// `uvm_component_utils(driver)
`uvm_register_cb(mydriver,A);
`uvm_component_utils_begin(driver)
`uvm_field_int(pre_num,UVM_ALL_ON)
`uvm_component_utils_end
function new(string name="driver",uvm_component parent=null);
super.new(name,parent);
//ap=new("ap",this);
ini_delay=10;
pre_num=3;
// `uvm_info("my_driver","new is called",UVM_LOW);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("driver","build_phase enter...",UVM_LOW);
`uvm_info("driver",$sformatf("pre_num value is %0d",pre_num),UVM_LOW);
// set_report_max_quit_count(5);
if(!uvm_config_db#(virtual if)::get(this,"","if0",if0))
`uvm_fatal("driver",{"virtual interface must be set for ",get_full_name()," if0!!!"})
uvm_config_db#(int)::get(this,"","ini_delay",ini_delay);
`uvm_info("driver","build_phase exiting...",UVM_LOW)
//uvm_config_db test
// void'(uvm_config_db#(int)::get(uvm_root::get(),"uvm_test_top.u_my_env.scb","pre_num",pre_num));
// $display("uvm_config_db test driver pre_num value is %0d",pre_num);
// uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.scb","pre_num",100);
endfunction
/** uvm_phase order test */
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("driver","connect_phase enter...",UVM_LOW)
`uvm_info("driver","connect_phase exiting...",UVM_LOW)
endfunction
task main_phase(uvm_phase phase);
super.main_phase(phase);
// phase.raise_objection(this);
`uvm_info("driver","main_phase enter...",UVM_LOW)
// $display("driver main phase begin time %0t",$time);
if0.cb.tx_d <=0;
if0.cb.valid_i <=0;
if0.cb.addr_i <=0;
@(posedge if0.rstn);
repeat(ini_delay) @(posedge if0.clk);
while(1)begin
@(posedge if0.clk);
seq_item_port.get_next_item(req);
$display("---------driver--------");//对数据通路的理解
req.print; //yes
`uvm_do_callbacks(my_driver, A, pre_tran(this,req))
if(req==null);
else begin
if(req.delay==0);
else repeat(req.delay) @(posedge if0.clk);
// ap.write(req);
foreach(req.pload[i]) begin
if0.cb.addr_i<= req.addr+i*4;
if0.cb.tx_d <= req.pload[i];
if0.cb.valid_i <= 1'b1;
$display("@%0t:driver if0.cb.tx_d is %0h",$time,if0.tx_d);
@(posedge if0.clk);
end
//callback
// `uvm_info("driver","Enter callback driver_pkg...",UVM_LOW)
// `uvm_do_callbacks(driver,A,driver_pkg(if0,req));
// `uvm_info("driver","Enter callback driver_pkg...",UVM_LOW)
if0.cb.valid_i <=0;
seq_item_port.item_done();
end
end
// phase.drop_objection(this);
$display("driver main phase end time %0t",$time);
`uvm_info("driver","main_phase exiting...",UVM_LOW)
endtask
task post_main_phase(uvm_phase phase);
super.post_main_phase(phase);
`uvm_info("driver","post_main_phase enter...",UVM_LOW)
// $display("driver post_main_phase begin time %0t",$time);
`uvm_do_callbacks(driver, A, pre_tran(this,req))
#1000;
$display("driver post_main_phase end time %0t",$time);
endtask
//
// task run_phase(uvm_phase phase);
// super.run_phase(phase);
// `uvm_info("driver","run_phase enter...",UVM_LOW)
// #100;
// `uvm_info("driver","run_phase exiting...",UVM_LOW)
// endtask
endclass
//class A extends uvm_callback;
// virtual task pre_tran(driver drv, ref my_transaction tr);
// endtask
//endclass
//
//typedef uvm_callbacks#(driver, A) A_pool;
callback相关的代码甭管。main_phase中,首先将相应数据赋值0,然后等待复位信号拉起,将transaction中相应数据给到接口,进而驱动进入DUT内部。
sequencer
class sequencer extends uvm_sequencer #(transaction);
uvm_analysis_port #(transaction) ap;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("sequencer","build_phase enter...",UVM_LOW)
ap=new("ap",this);
`uvm_info("sequencer","build_phase exiting...",UVM_LOW)
endfunction
`uvm_component_utils(sequencer)
/** uvm_phase order test */
endclass
sequencer的实现非常简单, 只要声明一下就可以,这里根据之前的框图,sequencer根据seq_item_port/export与driver进行通信,并通过ap端口向scoreboard发送正确数据。
monitor
class monitor extends uvm_monitor;
virtual if if0;
uvm_analysis_port#(transaction) ap;
`uvm_component_utils(monitor)
function new(string name="monitor",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("monitor","build_phase enter...",UVM_LOW)
ap=new("ap",this);
if(!uvm_config_db#(virtual if)::get(this,"","if0",if0))
`uvm_fatal("monitor","virtual interface must be set for if0!!!")
`uvm_info("monitor","build_phase exiting...",UVM_LOW)
//nonlinear line
//uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.u_my_env.agt.drv","pre_num",100);
endfunction
/** uvm_phase order test */
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("monitor","connect_phase enter...",UVM_LOW)
`uvm_info("monitor","connect_phase exiting...",UVM_LOW)
endfunction
task main_phase(uvm_phase phase);
transaction tr;
`uvm_info("monitor","main_phase enter...",UVM_LOW)
$display("monitor main phase begin time %0t",$time);
while(1)begin
@(posedge if0.clk);
// $display("monitor after repeat time is %0t",$time);
if(if0.valid_o==1'b1) begin
tr=new("tr");
tr.data=if0.rx_d;
tr.addr=if0.addr_o;
$display("@%0t:if0.rx_d %0h",$time,if0.rx_d);
tr.print;//理解数据通路,检查monitor处数据,可删除
ap.write(tr);
end
end
`uvm_info("monitor","main_phase exiting",UVM_LOW)
$display("monitor main phase end time %0t",$time);
endtask
endclass
这里,需要等valid_o为1时才进行interface级别到transaction级别的转换,因为RTL代码说明只有valid_o为1时,打拍后的输入数据才能给到rx_d,否则就直接将输入数据给到输出数据。到这里,整体架构就已经非常清晰了,driver将sequencer通过seq_item_port传输过来的transaction拆成interface级别,因为在top层中,已经实现了interface和DUT的连接,所以这里通过driver直接将相应数据驱动进入DUT,然后由monitor监测DUT的输出,将输出的interface级转为transaction级别,给到scoreboard进行比对,如整体框架图所示,monitor收集到的关于DUT的输出是需要进行比对的数据。如果将整体架构比作一个人的话,transaction级才是其中的血液,不是interface级。
agent
class agent extends uvm_agent;
sequencer sqr;
driver drv;
monitor mon;
//uvm_analysis_port #(transaction) drv_ap;
uvm_analysis_port #(transaction) sqr_ap;
uvm_analysis_port #(transaction) mon_ap;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
`uvm_component_utils(agent)
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("agent","build_phase enter...",UVM_LOW)
if(is_active==UVM_ACTIVE) begin
sqr=sequencer::type_id::create("sqr",this);
drv=driver::type_id::create("drv",this);
end
mon = monitor::type_id::create("mon",this);
`uvm_info("agent","build_phase exiting...",UVM_LOW)
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("agent","connect_phase enter...",UVM_LOW)
if(is_active==UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
//drv_ap=drv.ap;
sqr_ap=sqr.ap;
mon_ap=mon.ap;
`uvm_info("agent","connect_phase exiting...",UVM_LOW)
end
endfunction
endclass
agent中包含driver,monitor,sequencer,并进行这三个组件的例化工作。其中定义了sqr_ap和mon_ap,原因是组件之间的连接关系最好不要跨层次。mon中的数据需要给到scoreboard,如果直接连接就相当于跨了层次,agent和scoreboard是同一等级,所以先进行mon_ap与monitor中ap的连接,然后由mon_ap去连接scoreboard的相应端口,保证规范性。
scoreboard
class scoreboard extends uvm_scoreboard;
uvm_blocking_get_port #(transaction) exp_port;
uvm_blocking_get_port #(transaction) act_port;
transaction queue[$];
transaction dut_final;
int start;
int pre_num;
`uvm_component_utils(scoreboard)
function new (string name="scoreboard",uvm_component parent);
super.new(name,parent);
start=0;
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
/** uvm_phase order test */
`uvm_info("scoreboard","build_phase enter...",UVM_LOW)
exp_port = new("exp_port",this);
act_port = new("act_port",this);
//nonlinear line
//uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.u_my_env.agt.drv","pre_num",100);
// uvm_config_db#(int)::set(null,"uvm_test_top.u_my_env.agt.drv","pre_num",90);
//$display("$$$$$$$$$$ nonlinear line test pre_num is %0d",pre_num);
`uvm_info("scoreboard","build_phase exiting...",UVM_LOW)
endfunction
/** uvm_phase order test */
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("scoreboard","connect_phase enter...",UVM_LOW)
`uvm_info("scoreboard","connect_phase exiting...",UVM_LOW)
endfunction
task main_phase(uvm_phase phase);
transaction tr_golden,tr_dut,tr_tmp;
bit result;
super.main_phase(phase);
fork
while(1)begin
exp_port.get(tr_golden); // golden port
// $display("tr_golden is");
// tr_golden.print;
queue.push_back(tr_golden);
$display("tr_golden is");
tr_golden.print;
$display("queue is");
queue[$].print;
end
while(1) begin
act_port.get(tr_dut);
// $display("tr_dut print at %0t",$time);
// tr_dut.print; //data not in `uvm_object_utils_begin `uvm_object_utils_end, so print nothing
if(queue.size>0 || start!==0) begin
if(start==0)tr_tmp=queue.pop_front();
if(start!==tr_tmp.pload.size) begin
if(start==0)begin
dut_final=new;
dut_final.pload=new[tr_tmp.pload.size];
dut_final.pload[start]=tr_dut.data;
dut_final.addr=tr_dut.addr;
end
else begin
dut_final.pload[start]=tr_dut.data;
end
start++;
end
//
$display("tr_dut_final print");
dut_final.print;
if(start==tr_tmp.pload.size) begin
start=0;
//result = tr_dut.compare(tr_tmp);
result = dut_final.compare(tr_tmp);
if(result) begin
`uvm_info("my_scoreboard","compare match",UVM_LOW)
$display("print tr_tmp dut_final");
tr_tmp.print;
dut_final.print;
end
else begin
`uvm_error("scoreboard",$sformatf("compare mismatched at time %0t",$time))
$display("DUT is");tr_dut.print;
$display("DUT is");dut_final.print;
$display("GOLDEN is");tr_tmp.print;
end
end
end
else begin
`uvm_error("scoreboard",$sformatf("received dut, but golden is null at time %0t",$time))
end
end
join
endtask
function void check_phase(uvm_phase phase);
transaction tr;
if(queue.size>0) `uvm_error("scoreboard",$sformatf("the golden queue not empty , still have %0d transaction",exp_queue.size))
while(queue.size>0) begin
tr=queue.pop_back;
`uvm_error("scoreboard","no compared golden")
tr.print;
end
endfunction
endclass
scoreboard的实现当时属实困惑了,尤其是涉及到数组的数据重新打包整合。要理解scoreboard的代码实现方式,必须理解TLM1.0通信中的阻塞端口和非阻塞端口。以DUT端口为例,act_port声明为blocking_get类型的阻塞端口,阻塞端口意味着如果此端口没有get到来自通信端口的transaction,那么就会阻塞在这里,不会执行接下来的代码。而一旦执行了(exp_queue.size>0 || start!==0) 这段代码,就意味着blocking_get口一定是收到了monitor发送过来的transaction,这时如果exp_queue=0,则可以直接打印错误信息receive DUT,but golden is null。这意味着收到了来自monitor的信息,但是没有收到来自sequencer的数据,说明sequence->sequencer->FIFO->scoreboard这条数据通路发生了错误,错误原因有可能出现在各个通信节点,我之前的错误是env中关于接口与接口的连接出现了问题,这里可能会发生的错误对于刚接触的来说是多种多样的。start为数据整合打包的索引,数组之间的比对是不能够错位的.
env
/** TLM test */
//`include "A.sv"
//`include "B.sv"
//`include "B_no_export.sv"
//`include "A_export.sv"
//`include "C_export.sv"
//`include "A_get.sv"
//`include "B_get.sv"
//`include "A_transport.sv"
//`include "B_transport.sv"
//`include "A_nonblocking.sv"
//`include "B_nonblocking.sv"
//`include "A_analysis.sv"
//`include "B_analysis.sv"
//`include "C_analysis.sv"
class my_env extends uvm_env;
agent agt;
scoreboard scb;
int delay;
/** TLM test */
// A A_inst;
// B B_inst;
// B_no_export Bne;
// A_export Aex;
// C_export C_inst;
// A_get A_g;
// B_get B_g;
// A_transport A_trans;
// B_transport B_trans;
// A_nonblocking A_non;
// B_nonblocking B_non;
// A_analysis A_ana;
// B_analysis B_ana;
// C_analysis C_ana;
//uvm_tlm_analysis_fifo #(transaction) drv_scb_fifo;
uvm_tlm_analysis_fifo #(transaction) sqr_scb_fifo;
uvm_tlm_analysis_fifo #(transaction) mon_scb_fifo;
`uvm_component_utils(env)
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("environment","build_phase enter...",UVM_LOW)
//drv_scb_fifo=new("drv_scb_fifo",this);
sqr_scb_fifo=new("sqr_scb_fifo",this);
mon_scb_fifo=new("mon_scb_fifo",this);
agt = agent::type_id::create("agt",this);
scb = scoreboard::type_id::create("scb",this);
agt.is_active=UVM_ACTIVE;
`uvm_info("environment","build_phase exiting...",UVM_LOW)
//uvm_config_db test
//uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.u_my_env.agt.drv","pre_num",100);//or replace uvm_root::get to null
// a middle value
uvm_config_db#(int)::get(this,"","a",delay);
$display("@%0t: delay value is %0d",$time,delay);
assert(delay==11) $display("@%0t:delay is 10",$time);
else $error("delay!=11");
$display("Enter TLM test field....");
// A_inst = A::type_id::create("A_inst",this);
// C_inst = C_export::type_id::create("C_inst",this);
// B_inst = B::type_id::create("B_inst",this);
// Bne = B_no_export::type_id::create("Bne",this);
// Aex = A_export ::type_id::create("Aex",this);
// A_g = A_get::type_id::create("A_g",this);
// B_g = B_get::type_id::create("B_g",this);
// A_trans = A_transport::type_id::create("A_trans",this);
// B_trans = B_transport::type_id::create("B_trans",this);
// A_non = A_nonblocking::type_id::create("A_non",this);
// B_non = B_nonblocking::type_id::create("B_non",this);
// A_ana = A_analysis::type_id::create("A_ana",this);
// B_ana = B_analysis::type_id::create("B_ana",this);
// C_ana = C_analysis::type_id::create("C_ana",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("environment","connect_phase enter...",UVM_LOW)
//agt.drv_ap.connect(drv_scb_fifo.analysis_export);
agt.sqr_ap.connect(sqr_scb_fifo.analysis_export);
agt.mon_ap.connect(mon_scb_fifo.analysis_export);
scb.exp_port.connect(sqr_scb_fifo.blocking_get_export);
scb.act_port.connect(mon_scb_fifo.blocking_get_export);
// $display("TLM connect begin...A_port connect C_export");
// A_inst.A_port.connect(B_inst.B_export);
// A_inst.A_port.connect(Bne.B_imp);
// A_inst.A_port.connect(C_inst.C_export);
// Aex.A_export.connect(Bne.B_imp);
// A_g.A_port.connect(B_g.B_export);
// A_trans.A_transport.connect(B_trans.B_imp);
// A_non.A_port.connect(B_non.B_imp);
// A_ana.A_ap.connect(B_ana.B_imp);
// A_ana.A_ap.connect(C_ana.C_imp);
// `uvm_info("environment","connect_phase exitiing...",UVM_LOW)
endfunction
task main_phase(uvm_phase phase);
super.main_phase(phase);
`uvm_info("environment","main phase enter...",UVM_LOW)
#1000;
`uvm_info("environment","main phase exiting...",UVM_LOW)
endtask
//function void report_phase(uvm_phase phase);
// bit check_error;
// uvm_report_server svr;
//
// svr = uvm_report_server::get_server();
//
// if(svr.get_severity_count(UVM_FATAL) + svr.get_severity_count(UVM_ERROR) != 0) begin
// check_error = 1;
// `uvm_info(get_type_name(), "SIMULATION FAIL", UVM_NONE)
// end
// if(check_error == 0) begin
// `uvm_info(get_type_name, " Simulation PASSED! ", UVM_NONE)
// end else begin
// `uvm_info(get_type_name, " Simulation FAILED! ", UVM_NONE)
// end
// $display("\n\n");
//
//endfunction : report_phase
endclass
env中主要实现对FIFO的连接工作,mon_ap与FIFO的连接,FIFO与scoreboard的连接。sqr_ap与FIFO的连接,FIFO与scoreboard的连接。并例化agent和scoreboard,如框架图所示。
sequence
class sequence extends uvm_sequence #(transaction);
transaction m_trans;
transaction d_trans;
`uvm_object_utils(sequence)
`uvm_declare_p_sequencer(sequencer)
function new(string name="my_sequence");
super.new(name);
endfunction
//reload
virtual function void test();
$display("********my_sequence virtual type********");
endfunction
function void test_00();
$display("my_sequence not virtual type test_00");
endfunction
//end
virtual task pre_body();
//time test
`uvm_info("pre_body","task pre_body enter...",UVM_LOW)
$display("begin_time is %0t",$time);
#10;
$display("end_time is %0t",$time);
// uvm_phase phase;
// super.pre_body();
//`ifdef UVM_12
// phase=get_starting_phase();
//`else
// phase=starting_phase;
//`endif
// if(phase!=null) begin
// phase.phase_done.set_drain_time(this,1000);
// phase.raise_objection(this);
// end
endtask
// virtual task body();
virtual task body();
`uvm_info("my_sequence","body is called",UVM_LOW)
repeat(1)begin
// `uvm_do(m_trans)
`uvm_create(m_trans)
m_trans.randomize();
`uvm_send(m_trans)
p_sequencer.ap.write(m_trans);
// `uvm_create(m_trans)
// m_trans.randomize() with {m_trans.addr==8;
// m_trans.pload.size==3;
// foreach(m_trans.pload[i])m_trans.pload[i]==i+10;
// };
// p_sequencer.ap.write(m_trans);
// `uvm_send(m_trans)
// `uvm_do_with(m_trans,{m_trans.addr==8;
// m_trans.pload.size==3;
// foreach(m_trans.pload[i])m_trans.pload[i]==i+10;
// }
// )
end
endtask
virtual task post_body();
// uvm_phase phase;
// super.post_body();
//`ifdef UVM_12
// phase=get_starting_phase();
//`else
// phase=starting_phase;
//`endif
// if(phase!=null) begin
// phase.drop_objection(this);
// end
endtask
endclass
endclass
sequence中采用p_sequencer调用sequencer中的ap端口进行transaction的发送。
test
import uvm_pkg::*;
import env_pkg::*;
`include "sequence.sv"
//`include "normal_sequence.sv"
class my_callback extends A;
virtual task pre_tran(my_driver drv, ref my_transaction tr);
`uvm_info("my_callback","this is pre_tran task",UVM_LOW)
endtask
function new(string name="my_callback");
super.new(name);
endfunction
`uvm_object_utils(my_callback)
endclass
class base_test extends uvm_test;
env u_env;
sequence seq0;
// normal_sequence nseq;
// sequence1 seq1;
`uvm_component_utils(base_test)
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
// function void print_test(sequence seq0);
// seq0.test();
// seq0.test_00();
// endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
/** uvm_phase order test */
`uvm_info("test","build_phase enter...",UVM_LOW)
uvm_default_printer.knobs.begin_elements=10;
uvm_default_printer.knobs.end_elements=10;
//uvm_config_db test
uvm_config_db#(int)::set(null,"uvm_test_top.u_my_env.agt.drv","pre_num",90);
uvm_config_db#(real)::set(this,"","clk_half_period",200.0);
// seq0 = new("seq0");
u_env=env::type_id::create("u_env",this);
// //factory.set_type_override_by_type(sequence::get_type(),normal_sequence::get_type());
seq0=sequence::type_id::create("seq0");
// nseq = normal_sequence::type_id::create("nseq",this);
// print_test(seq0);
// print_test(nseq);
//factory.set_type_override_by_type(normal_sequence::get_type(),adnormal_sequence::get_type());
//factory.set_type_override_by_type(sequence::get_type(),adnormal_sequence::get_type());
seq0=sequence::type_id::create("seq0");
// $display("method no.2 uvm_sequence_base start");
// uvm_config_db#(uvm_sequence_base)::set(this,"u_env.agt.sqr.main_phase","default_sequence",seq0);
// $display("method no.3");
// uvm_config_db#(uvm_object_wrapper)::set(this,
// "u_env.agt.sqr.main_phase",
// "default_sequence",
// sequence::type_id::get());
//uvm test
/** uvm_error count 5 quit */
// set_report_max_quit_count(5);
`uvm_info("test","build_phase exiting...",UVM_LOW)
endfunction
/** uvm_phase order test */
virtual function void connect_phase(uvm_phase phase);
/** callback */
my_callback my_cb;
super.connect_phase(phase);
`uvm_info("test","connect_phase enter...",UVM_LOW)
// set_report_max_quit_count(5);
u_my_env.set_report_severity_action(UVM_WARNING, UVM_DISPLAY| UVM_STOP);
my_cb = my_callback::type_id::create("my_cb");
A_pool::add(u_my_env.agt.drv, my_cb);
uvm_top.print_topology();
check_config_usage();
print_config(0);
// seq0.print_override_info("my_sequence");
`uvm_info("test","connect_phase exiting...",UVM_LOW)
endfunction
task main_phase(uvm_phase phase);
`uvm_info("test","main_phase enter...",UVM_LOW)
$display("test main_phase begin time %0t",$time);
set_report_max_quit_count(5);
phase.raise_objection(this);
// seq0=sequence::type_id::create("seq0");
// seq1=sequence1::type_id::create("seq1");
/** first */
seq0.start(u_my_env.agt.sqr);
repeat(25)@(posedge tb_top.u_test.clk);
// seq1.start(u_env.agt.sqr);
phase.phase_done.set_drain_time(this,200);
// #200;
`uvm_info("test","main_phase exiting...",UVM_LOW)
$display("test main phase end time %0t",$time);
phase.drop_objection(this);
endtask
// task run_phase(uvm_phase phase);
// super.run_phase(phase);
// phase.raise_objection(this);
// `uvm_info("test","run_phase enter...",UVM_LOW)
// #10000;
// `uvm_info("test","run_phase exiting...",UVM_LOW)
// phase.drop_objection(this);
// endtask
// task post_shutdown_phase(uvm_phase phase);
// super.post_shutdown_phase(phase);
// phase.raise_objection(this);
//
// `uvm_info("test","post_shutdown_phase enter...",UVM_LOW)
// #1000;
// phase.drop_objection(this);
// `uvm_info("test","post_shutdown_phase exiting...",UVM_LOW)
// endtask
// task shutdown_phase(uvm_phase phase);
// super.shutdown_phase(phase);
// phase.raise_objection(this);
//
// `uvm_info("test","shutdown_phase enter...",UVM_LOW)
// #100000;
// phase.drop_objection(this);
// `uvm_info("test","shutdown_phase exiting...",UVM_LOW)
// endtask
//
// virtual function void extract_phase(uvm_phase phase);
// super.extract_phase(phase);
// phase.raise_objection(this);
// `uvm_info("test","extract phase enter...",UVM_LOW)
// phase.drop_objection(this);
// endfunction
function void report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
super.report_phase(phase);
server=uvm_report_server::get_server();
err_num=server.get_severity_count(UVM_ERROR);
if(err_num!=0) $display("base_test FAILED");
else $display("base_test PASSED");
endfunction
endclass
主要在test中控制系统整体运行时间。
env_pkg
package env_pkg;
import uvm_pkg::*;
`include "transaction.sv"
`include "monitor.sv"
`include "sequencer.sv"
`include "driver.sv"
`include "agent.sv"
`include "scoreboard.sv"
`include "env.sv"
`include "callbacks.sv"
endpackage
对于文件包一般有两个写法,其一是将env组件待运行文件按照例化顺序打包执行;其二是与白皮书的编写方式类似,将所有待执行文件包括test,sequence等按照例化顺序在tb_top中依次排列,平台开始运行后,所有tb_top中的`include都会被执行,这时只要使用+incdir+将`include文件的搜索路径写清楚就可以了。
3.运行结果与讨论
因为RTL非常简单,所以不必提取验证点,比对输入输出数据就行。
这里选择将待执行文件打包。base_test为随机出一个transaction,transaction中添加约束限制数组随机数组的数量,并在各个节点处增加打印信息。Makefile脚本如下,vcs -后面接的参数一搜就有,涉及到文件路径省略掉了。
TEST_NAME=base_test
compile :
vcs -省略(有文件路径) -top tb_top ./if.sv ./test.v ./env_pkg.sv ./tb_top.sv ./test/*.sv
sim:
./simv +UVM_TESTNAME=${TEST_NAME} -l simv.log
test中运行结果。运行结果显示,driver处添加的打印信息率先将随机得到的transaction打印出来,数组中共有8笔数据,并打印transaction拆分成interface级后的数据。紧接着tr_golden被打印,这是因为golden数据的传输中间不经过任何逻辑,因此到达scoreboard的时间一定是快于DUT数据达到scoreboard的时间。并且打印了存储golden数据的队列,没有问题。而后monitor开始不断的拿到DUT的输出数据。transaction不能print出数据是因为data并没有加入field autommation中。从打印结果可以清晰的观察到dut_final不停的将DUT输出的数据加入进来,然后两者比对通过。
---------driver--------
---------------------------------------------------------------------------------------
Name Type Size Value
---------------------------------------------------------------------------------------
m_trans transaction - @852
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h5dd378b6
begin_time time 64 500000
depth int 32 'd2
parent sequence (name) string 4 seq0
parent sequence (full name) string 34 uvm_test_top.u_env.agt.sqr.seq0
sequencer string 29 uvm_test_top.u_env.agt.sqr
---------------------------------------------------------------------------------------
UVM_INFO ./test/base_test.sv(8) @ 500000: reporter [my_callback] this is pre_tran task
UVM_INFO driver.sv(75) @ 500000: uvm_test_top.u_env.agt.drv [driver] Enter callback driver_pkg...
callback driver_pkg
driver if0.cb.tx_d is 0
callback driver_pkg
driver if0.cb.tx_d is 282a5b45
callback driver_pkg
driver if0.cb.tx_d is a91a7e04
callback driver_pkg
driver if0.cb.tx_d is 8c366963
callback driver_pkg
driver if0.cb.tx_d is 48290a3f
callback driver_pkg
driver if0.cb.tx_d is 5ff1bded
callback driver_pkg
driver if0.cb.tx_d is a39adc79
callback driver_pkg
driver if0.cb.tx_d is 42a33ffa
UVM_INFO my_driver.sv(77) @ 820000: uvm_test_top.u_env.agt.drv [driver] Enter callback driver_pkg...
tr_golden is
---------------------------------------------------------------------------------------
Name Type Size Value
---------------------------------------------------------------------------------------
m_trans transaction - @852
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h5dd378b6
begin_time time 64 500000
end_time time 64 820000
depth int 32 'd2
parent sequence (name) string 4 seq0
parent sequence (full name) string 34 uvm_test_top.u_env.agt.sqr.seq0
sequencer string 29 uvm_test_top.u_env.agt.sqr
---------------------------------------------------------------------------------------
queue is
---------------------------------------------------------------------------------------
Name Type Size Value
---------------------------------------------------------------------------------------
m_trans transaction - @852
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h5dd378b6
begin_time time 64 500000
end_time time 64 820000
depth int 32 'd2
parent sequence (name) string 4 seq0
parent sequence (full name) string 34 uvm_test_top.u_env.agt.sqr.seq0
sequencer string 29 uvm_test_top.u_env.agt.sqr
---------------------------------------------------------------------------------------
@860000:if0.rx_d 282a5b45
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @857
addr integral 32 'h2f0
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'h0
[2] integral 32 'h0
[3] integral 32 'h0
[4] integral 32 'h0
[5] integral 32 'h0
[6] integral 32 'h0
[7] integral 32 'h0
------------------------------------------------
@900000:if0.rx_d a91a7e04
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @865
addr integral 32 'h2f4
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h0
[3] integral 32 'h0
[4] integral 32 'h0
[5] integral 32 'h0
[6] integral 32 'h0
[7] integral 32 'h0
------------------------------------------------
@940000:if0.rx_d 8c366963
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @869
addr integral 32 'h2f8
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h0
[4] integral 32 'h0
[5] integral 32 'h0
[6] integral 32 'h0
[7] integral 32 'h0
------------------------------------------------
@980000:if0.rx_d 48290a3f
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @873
addr integral 32 'h2fc
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h0
[5] integral 32 'h0
[6] integral 32 'h0
[7] integral 32 'h0
------------------------------------------------
UVM_INFO my_env.sv(112) @ 1000000: uvm_test_top.u_env [environment] main phase exiting...
@1020000:if0.rx_d 5ff1bded
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @877
addr integral 32 'h300
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'h0
[6] integral 32 'h0
[7] integral 32 'h0
------------------------------------------------
@1060000:if0.rx_d a39adc79
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @881
addr integral 32 'h304
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h0
[7] integral 32 'h0
------------------------------------------------
@1100000:if0.rx_d 42a33ffa
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @885
addr integral 32 'h308
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h0
------------------------------------------------
@1140000:if0.rx_d 5dd378b6
------------------------------------
Name Type Size Value
------------------------------------
tr transaction - @889
addr integral 32 'h30c
pload da(integral) 0 -
------------------------------------
dut_final print
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h5dd378b6
------------------------------------------------
UVM_INFO scoreboard.sv(79) @ 1140000: uvm_test_top.u_env.scb [scoreboard] compare match
print tr_tmp dut_final
---------------------------------------------------------------------------------------
Name Type Size Value
---------------------------------------------------------------------------------------
m_trans transaction - @852
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h5dd378b6
begin_time time 64 500000
end_time time 64 820000
depth int 32 'd2
parent sequence (name) string 4 seq0
parent sequence (full name) string 34 uvm_test_top.u_env.agt.sqr.seq0
sequencer string 29 uvm_test_top.u_env.agt.sqr
---------------------------------------------------------------------------------------
------------------------------------------------
Name Type Size Value
------------------------------------------------
transaction transaction - @861
addr integral 32 'h2f0
pload da(integral) 8 -
[0] integral 32 'h282a5b45
[1] integral 32 'ha91a7e04
[2] integral 32 'h8c366963
[3] integral 32 'h48290a3f
[4] integral 32 'h5ff1bded
[5] integral 32 'ha39adc79
[6] integral 32 'h42a33ffa
[7] integral 32 'h5dd378b6
------------------------------------------------
UVM_INFO ./test/base_test.sv(123) @ 1820000: uvm_test_top [test] main_phase exiting...
test main phase end time 1820000
UVM_INFO agent.sv(44) @ 2000000: uvm_test_top.u_env.agt [agent] main phase exiting...
UVM_INFO driver.sv(89) @ 2200000: uvm_test_top.u_env.agt.drv [driver] post_main_phase enter...
UVM_INFO ./test/base_test.sv(8) @ 2200000: reporter [my_callback] this is pre_tran task
base_test PASSED
--- UVM Report Summary ---
Quit count : 0 of 5
** Report counts by severity
UVM_INFO : 46
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0