如何从零开始构建一个可用的UVM验证平台.docx

上传人:b****7 文档编号:10003618 上传时间:2023-02-07 格式:DOCX 页数:8 大小:17.67KB
下载 相关 举报
如何从零开始构建一个可用的UVM验证平台.docx_第1页
第1页 / 共8页
如何从零开始构建一个可用的UVM验证平台.docx_第2页
第2页 / 共8页
如何从零开始构建一个可用的UVM验证平台.docx_第3页
第3页 / 共8页
如何从零开始构建一个可用的UVM验证平台.docx_第4页
第4页 / 共8页
如何从零开始构建一个可用的UVM验证平台.docx_第5页
第5页 / 共8页
点击查看更多>>
下载资源
资源描述

如何从零开始构建一个可用的UVM验证平台.docx

《如何从零开始构建一个可用的UVM验证平台.docx》由会员分享,可在线阅读,更多相关《如何从零开始构建一个可用的UVM验证平台.docx(8页珍藏版)》请在冰豆网上搜索。

如何从零开始构建一个可用的UVM验证平台.docx

如何从零开始构建一个可用的UVM验证平台

如何从零开始构建一个可用的UVM验证平台

前面大体说明了uvm的结构,类型关系以及启动过程。

现在最关心的大概也就是uvm是如何把一串激励发送给被测对象的了。

对于传统的基于verilog的验证平台,基本套路就是各种module中的task相互调用,最终把激励施加到顶层模块例化的被测对象中,但是具体步骤各个团队又有挺大差异。

基于UVM的平台,本质其实也是这样,但是它把各种调用关系进行了更严格的限定,所以所有使用uvm的团队,实现的验证平台都更加相似。

下面以之前的apb/spi接口为例具体讲解一下。

这里把之前的图再贴过来一下。

之前已经说过有关sequencer,driver,monitor,env等的大体功能,这些可以被认为是验证平台的硬体,除此之外,还有一些在这之间流动的软体需要近一步说明一下。

uvm_sequence_item就是这种软体最基本的构造单元。

比如可以定义apb端的sequence_item,其中包括读写信息,数据地址这些成员。

classapb_transferextendsuvm_sequence_item;randbit[31:

0]addr;

randapb_direction_enumdirection;

randbit[31:

0]data;

randintunsigneddelay=0;

constraintc_direction{directioninside{APB_READ,APB_WRITE};}

constraintc_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_endfunctionnew(stringname='apb_transfer');

super.new(name);

endfunction

//对其他方法没有进行特别的设定

endclass:

apb_transfer比uvm_sequence_item稍微高一层极的信息单元是uvm_sequence,这个可以认为是一连串的uvm_sequence_item,可以表达一个完整的操作,下面是读fifo的一个例子。

classread_rx_fifo_seqextendsuvm_sequence#(apb_pkg:

:

apb_transfer);

//此类从uvm_sequence派生得到,uvm_sequence是一个参数化的类,这里类型赋值为apb_transfer,它被定义在apb_pkg这个包中

functionnew(stringname='read_rx_fifo_seq');

super.new(name);

endfunction//Registersequencewithasequencer

`uvm_object_utils(read_rx_fifo_seq)randbit[31:

0]read_addr;

randintunsigneddel=0;

randintunsignednum_of_rd;

constraintnum_of_rd_ct{(num_of_rd

constraintdel_ct{(del

constraintaddr_ct{(read_addr[1:

0]==0);}

//一个sequence最重要的行为就是body,里边定义这个信息序列都发送什么东西

virtualtaskbody();

`uvm_info(get_type_name(),$sformatf('StartingReads...',num_of_rd),UVM_LOW)

response_queue_error_report_disabled=1;for(inti=0;i

read_addr=`RX_FIFO_REG;//rxfifoaddress

`uvm_do_with(req,

{req.addr==read_addr;

req.direction==APB_READ;

req.delay==del;})

end

endtask

//uvm_do_with这个宏负责把各个最基本的sequence_item加上约束发送出去。

endclass:

read_rx_fifo_seq如何把这个sequence发送出去呢?

这就需要在testcase里边把这个sequence通过sequencer发出去。

classread_rx_fifo_testextendsuvm_test;`uvm_component_utils(read_rx_fifo_test)spi_apb_envspi_apb_env_0;//test中例化envfunctionnew(stringname='read_rx_fifo_test',

uvm_componentparent=null);

super.new(name,parent);

endfunction:

newvirtualfunctionvoidbuild_phase(uvm_phasephase);

super.build_phase(phase);

spi_apb_env_0=spi_apb_env:

:

type_id:

:

create('spi_apb_env_0',this);

endfunction:

build_phasevirtualtaskmain_phase(uvm_phasephase);

super.main_phase(phase);

//执行基类的任务

read_rx_fifo_seq_inst=read_rx_fifo_seq:

:

type_id:

:

create('read_rx_fifo_seq_inst',this);

//产生一个seq

read_rx_fifo_seq_inst.start(spi_apb_env_0.apb_agent_0.apb_sqr);

//将seq通过sqr发送

endtaskendclass:

read_rx_fifo_testsequencer得到了这个序列,就要把它交给driver,然后driver把其中的信息元素放置到与dut连接的接口上。

这样就将需要的激励施加给了被测对象。

classapb_master_driverextendsuvm_driver#(apb_transfer);//连接driver与dut的虚接口.

virtualapb_ifvif;

functionnew(stringname,uvm_componentparent);

super.new(name,parent);

endfunction:

new//在外部定义以下任务

externvirtualfunctionvoidbuild_phase(uvm_phasephase);

externvirtualfunctionvoidconnect_phase(uvm_phasephase);

externvirtualtaskrun_phase(uvm_phasephase);

externvirtualprotectedtaskget_and_drive();

externvirtualprotectedtaskdrive_transfer(apb_transfertrans);

externvirtualprotectedtaskdrive_address_phase(apb_transfertrans);

externvirtualprotectedtaskdrive_data_phase(apb_transfertrans);endclass:

apb_master_driverfunctionvoidapb_master_driver:

:

connect_phase(uvm_phasephase);

super.connect_phase(phase);

if(!

uvm_config_db#(virtualapb_if):

:

get(this,'','vif',vif))

`uvm_error('NOVIF',{'virtualinterfacemustbesetfor:

',get_full_name(),'.vif'})

//将虚接口通过配置从顶层取出来,并赋值给vif

endfunction:

connect_phasetaskapb_master_driver:

:

run_phase(uvm_phasephase);

get_and_drive();

endtask:

run_phase//在执行时一直在从sqr取item并且赋值给vif

taskapb_master_driver:

:

get_and_drive();

while

(1)begin

fork

begin

foreverbegin

@(posedgevif.pclockiff(vif.preset))

seq_item_port.get_next_item(req);

//从sqr取到item

drive_transfer(req);

//发送

seq_item_port.item_done(req);

//告知sqr使其产生下一个item

end

end

join_any

disablefork;

end

endtask:

get_and_drive//具体一个发送过程

taskapb_master_driver:

:

drive_transfer(apb_transfertrans);drive_address_phase(trans);

//发送地址

drive_data_phase(trans);

//发送数据

endtask:

drive_transfertaskapb_master_driver:

:

drive_address_phase(apb_transfertrans);

intslave_indx;

slave_indx=cfg.get_slave_psel_by_addr(trans.addr);

vif.paddr

vif.psel

vif.penable

if(trans.direction==APB_READ)begin

vif.prwd

end

elsebegin

vif.prwd

vif.pwdata

end

@(posedgevif.pclock);

endtask:

drive_address_phasetaskapb_master_driver:

:

drive_data_phase(apb_transfertrans);

vif.penable

@(posedgevif.pclockiffvif.pready);

if(trans.direction==APB_READ)begin

trans.data=vif.prdata;

end

vif.penable

vif.psel

endtask:

drive_data_phase再重复一下这个基本的步骤,在test的main_phase中,由基本的sequence_item组成的sequence被放到sequencer上执行,sequencer将它转交给driver,其后driver通过虚接口将具体信号施加到被测对象上,实现uvm中激励的产生传输与施加过程。

这里就产生了两个问题,一个是sequencer如何将sequence_item转交给driver,另一个是driver如何通过虚接口将具体信号施加到被测对象上。

这里具体解释对于第一个问题,我们在driver中有这么2句

seq_item_port.get_next_item(req);

seq_item_port.item_done(req);

这里的seq_item_port就是driver与sequencer通信的接口。

在uvm_driver中可以看到这个定义为

uvm_seq_item_pull_port#(REQ,RSP)seq_item_port;

这里的REQ,RSP都是对应uvm_driver的sequence_item。

而uvm_seq_item_pull_port是一个uvm专用的tlm口

同样,在uvm_sequencer中有

uvm_seq_item_pull_imp#(REQ,RSP,this_type)seq_item_export;

而在上层的uvm_agent中有virtualfunctionvoidconnect_phase(uvm_phasephase);

drv.seq_item_port.connect(sqr.seq_item_export);

endfunction

这里drv跟sqr分别是例化的uvm_driveruvm_sequencer的名字,可以看到他们两个实质是通过这个tlm接口来传递item的。

下面图大致说明了这个链接建立以及传送数据的过程。

首先在build后的connect时段,会确立drv.seq_item_port与sqr.seq_item_export的链接,也就是检查port与export的一一对应关系,并将与port对应的export映射给m_if,而后每次执行driver中的get_next_item与item_done实际就是执行了sequencer中的get_next_item与item_done。

而这实际上就是往一个队列中不断存数与取数的过程。

详细代码在uvm_sequencer中接下来再看第2个问题,driver通过虚接口驱动信号的过程。

这里是apb接口的定义

interfaceapb_if(inputpclock,inputpreset);parameterPADDR_WIDTH=32;

parameterPWDATA_WIDTH=32;

parameterPRDATA_WIDTH=32;logic[PADDR_WIDTH-1:

0]paddr;

logicprwd;

logic[PWDATA_WIDTH-1:

0]pwdata;

logicpenable;

logic[15:

0]psel;

logic[PRDATA_WIDTH-1:

0]prdata;

logicpslverr;

logicpready;

endinterface:

apb_if下面是顶层TB中的一段regclock;

regreset;apb_ifapb_if_0(clock,reset);

//例化接口

dut_dummydut(.apb_clock(clock),

.apb_reset(reset),

.apb_if(apb_if_0)

);

//例化被测对象,链接前面定义的接口

initialbegin

uvm_config_db#(virtualapb_if):

:

set(null,'*.demo_tb0.apb0*','vif',apb_if_0);

//通过config_db将apb_if_0这个实际的接口,传送到验证平台下层各组件的虚假口中

run_test();

//运行测试

end这里的uvm_config_db#(virtualapb_if):

:

set对应之前driver里边的uvm_config_db#(virtualapb_if):

:

get

也就是在顶层set,底层get,然后通过uvm_config_db这个类似数据库的玩意,实现从顶层module到底层class中接口的链接,从而driver中的信息流进dut里边。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 商务科技

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1