-
如何从零开始构建一个可用的
UVM
验证平台<
/p>
前面大体说明了
< br>uvm
的结构,类型关系以及启动过程。
现在最关心的大
概也就是
uvm
是如何把一串激励发送给被
测对象的了。
对于传统的基于
verilog
的验证平台,
基本套路
就是各种
module
中的
task
相互调
用,
最终把激励施加到顶
层模块例化的被测对象中,但是具体步
骤各个团队又有挺大
差异。基于
UVM
的平台,本质其实也是这样,但是它把各
种调用关系进行了更严格的限定,
所以所有使用
uvm
的团队,
实现的验证平台都更加相似。下面以之前的
apb/spi
接
口为
例具体讲解一下。这里把之前的图再贴过来一下。之前已经
说过有关
sequencer,driver,monitor,env
等的大体功能,
这些
可以被认为是验证平台的硬体,除
此之外,还有一些在这之
间流动的软体需要近一步说明一下。
u
vm_sequence_item
就
是这种软体最基本的构造
单元。比如可以定义
apb
端的
seq
uence_item,
其中包括读写信息,数据地址这些成员。
class apb_transfer extendsuvm_sequence_item;
rand bit[31:0]
addr;
randapb_direction_enum
direction;
rand bit[31:0]
data;
rand intunsigned
delay = 0;
constraint c_direction { direction
inside {APB_READ,
APB_WRITE }; }
constraint c_delay { delay
//
这里需要将其中的变量注册一下,这些后边具体讲
`uvm_object_utils_begin(apb_transfer)
`uvm_field_int(addr, UVM_DEFAULT)
`uvm_field_enum(apb_direction_enum,
direction,
UVM_DEFAULT)
`uvm_field_int(data, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name
='apb_transfer');
(name);
endfunction
//
对其他方法没有进行特别的设定
endclass : apb_transfer
比
uvm_sequence_item
稍微高一层
极的信息单元是
uvm_sequence
,这个可以认为是
一连串的
uvm_sequence_item
,可以表达一个
完整的操作,下面是读
fifo
的一个例子。
< br>class read_rx_fifo_seq extends
uvm_sequence#(apb_pkg::apb_transfer); <
/p>
//
此类从
uvm_sequence<
/p>
派生得到,
uvm_sequence
是
一个
参数化的类,这里类型赋值为
apb_transfer<
/p>
,它被定义在
apb_pkg
这个包中<
/p>
functionnew(string
name='read_rx_fifo_seq');
(name);
endfunction
//
Registersequence with a sequencer
`uvm_object_utils(read_rx_fifo_seq)
rand bit[31:0]
read_addr;
rand intunsigned del = 0;
rand intunsigned num_of_rd;
constraintnum_of_rd_ct { (num_of_rd
constraintdel_ct { (del
constraintaddr_ct
{(read_addr[1:0] == 0); }
//
一个
sequence
最重要的行为就是
body
,里边定义这个信
息序列都发送什么东西
virtual taskbody();
`uvm_info(get_type_name(),
$$sformatf('Starting
Reads...',num_of_rd), UVM_LOW)
response_queue_error_report_disabled =
1;
for
(int i = 0; i
read_addr =`RX_FIFO_REG;
//rx fifo address
`uvm_do_with(req,
{ ==
read_addr;
ion ==
APB_READ;
== del; } )
end
endtask
//uvm_do_with
这个宏负责把各个最基本的
sequence_item
加上约束发送出去。
endclass : read_rx_fifo_seq
如
何把这个
sequence
发送出去
呢
?这就需要在
testcase
里边把这个
sequence
通过
sequencer
发出去。
class read_rx_fifo_test extends
uvm_test;
`uvm_component_utils(read_rx_fifo_test)
spi_apb_env
spi_apb_env_0;//test
中例化
env
function
new(string name ='read_rx_fifo_test',
uvm_component parent=null);
(name,parent);
endfunction :
new
virtual function void
build_phase(uvm_phasephase);
_phase(phase);
spi_apb_env_0 =
spi_apb_env::type_id::create('spi_apb_env_0',this)
;
endfunction :
build_phasevirtual
taskmain_phase(uvm_phase phase);
_phase(phase);
//
执行基类的任务
read_rx_fifo_seq_inst
=read_
rx_fifo_seq::type_id::create('read_rx_fifo_seq_ins
t',t
his);
//
产生一个
seq
rea
d_rx_fifo_seq_(spi_apb_env__agent_
b_sqr
);
//
将
seq
< br>通过
sqr
发送
endtaskendclass : read_rx_fifo_testsequ
encer
得到了这
个序列,就要把它交给
driver
,然后
driver
把其中的信息元
素放置到与
dut
连接
的接口上。这样就将需要的激励施加给
了被测对象。
class
apb_master_driver extends uvm_driver
#(apb_transfer);
//
连接
driver
与
dut
的虚接口
.
virtual apb_if
vif;
function new (string name,
uvm_componentparent);
(name, parent);
endfunction : new
//
在外部定义以下任务
extern virtual function
voidbuild_phase(uvm_phase
phase);
extern virtual
function voidconnect_phase(uvm_phase
phase);
extern virtual task
run_phase(uvm_phasephase);
extern virtual protected
taskget_and_drive();
extern virtual protected task
drive_transfer(apb_transfer
trans);
extern virtual
protected taskdrive_address_phase
(apb_transfer trans);
extern virtual protected
task
drive_data_phase(apb_transfer
trans);endclass :
apb_master_driverfunction void
< br>apb_master_driver::connect_phase(uvm_phasephase );
t_phase(phase);
if (!uvm_config_db#(virtual
apb_if)::get(this,'', 'vif', vif))
`uvm_error('NOVIF',{'virtual interface
must be set
for:',get_full_name(),'.vif'})
< br>//
将虚接口通过配置从顶层取出来,并赋值给
vif
endfunction : connect_phasetask
apb_master_driver::run_phase(uvm_phase
phase);
get_and_drive();
endtask :
run_phase//
在执行时一直在从
sqr
取
item
并且赋
值给<
/p>
vif
task
apb_master_driver::get_and_drive();
while (1) begin
fork