只有driver的验证平台
开始是直接对driver进行例化,同时调用main_phase,从而会将数据进行驱动到top中信号。当加入interface后,通过interface将数据驱动到top上。也可以通过interface读取相应的数据。比如monitor中引入接口,监测相应的transaction。
monitor中的interface?
宏
‘uvn_info取代display,可以打印更多东西
uvm_macros.svh包含众多宏定义,需要import
’uvm_component_utils对新定义类应用时,相当于把类注册到uvm内部的一张表中,可以根据类目创建一个类的实例,也拥有实现factory功能的基础。
‘uvm_fatal出现表示出现了问题无法继续
实例化
run_test语句实例化了一个脱离top_tb层次结构的实例,建立了一个新的层次结构
run_test创建的实例是树根,并且名字固定为uvm_test_top
top中实例化 initial begin my_driver drv; drv = new("drv",null); drv.main_phas(null); $finish(); end //利用uvm_component_utils注册后可以实现和上面一样的功能 //此处run_test实例化后的实例名字为uvm_test_top initial begin run_test("my_driver"); //创建实例,调用mian_phase 如果有多个task,会调用别的吗,还是只 end 调用main_phase
uvm父类
- 每一个派生自uvm_component或其派生类的类在其new函数中要指明两个参数:name和parent,另外还要使用uvm_component_utils注册。(比如uvm_driver。)只有uvm_component才能作为数的节点。
- 所有的transaction要派生自uvm_sequence_item,这样才可以使用sequence机制
- uvm_object_utils有生命周期,不想uvm_component_utils一直存在。(比如transaction)
phase
- uvm验证平台的运行主要通过phase管理,以xxx_phase命名,都有一个类型为uvm_phase、名字为phase的参数。driver所做的事几乎都在main_phase中完成,实现一个driver等于实现一个main_phase
- build_phase(仿真时间为0时执行)在new函数之后,mian_phase之前执行。先执行树根的build_phase之后再执行树叶的,当所有build_phase执行完才会执行后面的phase。uvm树最晚在build_phase时段完成,在main_phase中实例化会报错,而在new中实例化不会报错。
- connect_phase在build_phase执行完成后马上执行,执行顺序是从树叶到树根。
函数phase(不消耗仿真时间):build_phase
任务phase:mian_phase
class A; …… endclass A a_inst; a_inst=new(); //new()通知仿真器创建一个A的实例,之后会开辟空间,返回空间指针
引入objection
每个phase中,uvm会检查是否有objection被提起(raise_objection),若有,那么等待这个objection被撤销(drop_objection)后停止仿真,若无则马上结束当前phase。raise_objection必须在main_phase中第一个消耗仿真时间的语句前。
加入virtual interface
避免绝对路径方法
- 使用宏
- 使用interface (如top_tb.clk变为vif.ck)
定义了接口后,在top中实例化后,通过dut实例化就可以将接口作为参数传递进去进行连接。
//错误用法,在类中,不能这样声明句柄? class my_driver extends uvm_drive; my_if drv_if; endclass //正确用法 class my_driver extends uvm_driver; virtual my_if vif; endclass
config_db机制
不在top层时,在build_phase中使用
//set第二个参数表示路径索引 //set和get第三个参数必须一致 //set的第四个参数表示要将哪个interface进行传递,get的第四个参数表示要将得到的interface给哪个成员变量 //使用双冒号是因为函数是静态函数 //uvm_config_db#(virtual my_if)表示一个参数化的类,参数为寄信类型 uvm_config_db#(virtual my_if)::get(this,"","vif",vif) uvm_config_db#(virtual my_if)::set(null,"uvm_test_top","vif",input_if)
为验证平台加入各个组件
加入transaction
加入env
在容器中例化driver、monitor、reference model、scoreboard,调用run_test时,传递的参数不再是my_driver,而是这个容器类,即让UVM自动创建这个容器类(uvm_env)的实例。
class my_env extends uvm_env; my_driver drv; //在env中例化 function new(string name = "my_env" ,uvm_component parent) …… endfuncrion virtual function void build_phase(uvm_phase phase); super.build_phase(phase); //验证平台组件实例化都应该按这种方式实例化,只有使用factory机制注册的类才可以这样实例化 //只有这样实例化的才可以使用factory的重载机制 //create传递的参数会影响set的第二个参数 drv=my_driver::type_id::create("drv",this); //参数为名字和this指针 endfunction 'uvm_component_utils(my_env)
加入monitor
driver把transaction级别数据转变为DUT端口级别,并驱动给DUT,monitor则用来收集DUT的端口数据,并将其转换为transaction交给后续组件如reference model、scoreboard等处理。
monitor也需要virtual my_if
封装成agent
driver和monitor处理的是同一种协议,故将两者封装在一起,称为一个agent。不同agent代表不同协议。
agent有一个属性is_active来选择是否实例化,默认情况为UVM_ACTIVE。
在agent中声明driver、monitor,在其build_phase中进行例化
在env中声明agent,在其build_phase中进行例化,对对相应的is_active模式进行声明
(可以通过config_db机制传递这个参数)
加入reference model
同样声明端口,main_phase中例化数据
数据来源于agent,如何将数据发送到module
uvm_analysis_port :发送
uvm_blocking_get_port :接收
uvm_tlm_fifo :连接两个端口 (大多在env中)暂存作用
(三种方式都是提前声明,在build_phase中利用new进行例化)
uvm_analysis_port #(transaction) ap i_agt.ap.connect(agt_mdl_fifo.analysis_export); //利用connect连接 mdl.port.connect(agt_mdl_fifo.blocking_get_export);
书上例子,在agent和monitor中都声明一个通信句柄,但只有monitor中进行实例化。可以通过在agent的connect_phase中进行赋值ap=mon.ap即可。
加入scoreboard
比较reference model出来的数据和dut出来的数据。由于从dut得到数据有延时,而从reference model得到的结果不需要延时。
exp_port.get(get_actual) //用到接收端口的get方法,参数为transaction句柄 //在reference mode中发送完一笔数据会write到ap(发送句柄)
加入field_automation机制
'uvm_objection_utils_begin(my_transaction) //实现transaction的factory注册 'uvm_field_int(dmac,UVM_ALL_ON) //uvm_field系列宏注册字段 'uvm_field_array_int(pload,UVM_ALL_ON) //这样注册后就可以调用copy、compare、print等函数 'uvm_objection_utils_end //tr.pack_bytes(data_q) 可以将字段变成byte放入data_q,字段按照宏注册顺序 tr.unpack_bytes(data_array) 将byte流转换成tr字段,参数必须为动态数组
在验证平台加入sequencer
sequence机制用于产生激励,driver负责接收激励。
class my_driver extends uvm_driver#(my_transaction) //uvm_driver是参数化的类,如uvm_driver中 //的成员变量req,类型是传递的参数,如transaction req=new("req");
sequence机制
sequence不属于验证平台的一部分,sequencer是uvm_component,sequence是uvm_object。
//第一种transaction例化 driver没有传递transaction类型 task my_monitor::main_phase(uvm_phase phase) my_transaction tr; tr=new("tr"); //第二种例化,传递uvm_driver的参数就是transaction类型 没有手动声明句柄,直接使用 class my_driver extends uvm_driver #(my_transaction) main_phase中 req=new(“req”); //第三种例化,在sequence中 先声明句柄 class my_sequence extends uvm_sequence #(my_transaction) my_transaction m_trans; virtual tsak body(); 'uvm_do(my_trans) //'uvm_do可以创建实例my_trans;将其随机化;最终送到sequencer; endtask //不用sequencer还可以用start_item,finish_item产生transaction
每一个sequence都有一个body任务,当sequence启动后,就会自动执行body中的代码。
sequence–>(仲裁列表)—–>sequencer—>driver
由于数据由sequence到driver,当sequencer做两件事,检查仲裁列表中是否由sequence发送transaction的请求;检测driver中是否有接受transaction的请求。任意一个不满足,都会等另一个条件满足才进行发送。
driver的成员变量seq_item_port和sequencer的成员变量seq_item_export可以建立通道,实现driver向sequencer发送请求。
task my_driver::main_phase(uvm_phase phase) while(1) begin seq_item_port.get_next_item(req); //通过get_next_item向sequence申请新的transaction drive_one_pkt(req); seq_item_port.item_done() end
driver使用get_next_item得到transaction,sequencer自己也保留一份。在下次调用get_next_item前,item_done被调用,可表明driver得到了transaction,会删除保留的,否则会再次发送。另外,uvm_do在driver取走transaction后,会等待driver返回item_done,uvm_do才算执行完,返回执行下一个uvm_do,产生新的transaction。
与get_next_item类似,还有try_next_item,它是非阻塞的,driver会询问sequencer是否有新的transaction,若有则得到该transaction,若无则直接返回。而get_next_transaction会一直等到新的transaction才返回。
启动sequence
常在某个component(如my_sequencer、my_env)的main_phase
task my_env::main_phase(uvm_phase phase); //main_phase中 my_sequencese seq; phase.raise_objection(this); //objection伴随着sequence,通常只在sequence出现的地 方才提起和撤销objection seq=my_sequence::type_id::create("seq"); //创建实例 seq.start(i_agt.sqrt); //参数是一个sequencer指针,且需要指明哪一个sequencer phase.drop_objection;(this); endtask //在sequencer中 seq.start(this); //启动时,参数变化了
default_sequence的使用
启动更多使用的default_squence进行启动,一般在某个component的build_phase中启动
虽然是通过set寄信,但不用get获得信
//env中 uvm_config_db#(uvm_object_wrapper)::set(this, //my_env在uvm_test_top,所以为this "i_agt.sqr.main_phase", //第二个参数为相对第一个参数的相对路径 "defualt_sequence",my_sequence:type:get()) //uvm_config_db在传递virtual interface时,不是类,无法使用指针,故第一个参数设置为null,第二个参数为绝对路径uvm_test_top.xxx //第二个参数还要指定在sequencer的哪个phase启动 //top_tb中 参数为set(null,"uvm_test_top.i_agt.sqr.main_phase",……) //my_agent中 set(this,"sqr.main_phase",……)
由于sequence出现就有objection的提起和撤销,为此可以在sequence中设置
//sequence启动default_sequence时,会对变量名starting_phase进行如下操作 task my_sequencer::main_phase(uvm_phase phase); …… seq.starting_phase=phase; seq.start(this) …… endtask
my_sequence中 …… virtual task body(); if(starting_phase!=null) starting_phase.raise_objection(this); repeat() begin 'uvm_do(m_trans) end #1000 if(starting_phase.drop_objection(this)); endtask
还可以直接在test中设置启动
加上测试用例
加入base_test
树根是基于uvm_test派生的类,此时test是uvm_test_top,所以在basic_test中对env进行句柄声明,在build_phase中实例化。
class base_test extends uvm_test; my_env env; …… function void base_test::build_phase(uvm_phase phase); super.build_phase(phase); env=my_env::type_id::create("env",this); //在basic_test中启动sequence uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", "my_sequence::type_id::get()"); endfunction
除了以上操作,还在basic_test中做如下事情,第一,设置整个验证平台的超时退出时间;第二,通过config_db设置验证平台中某些参数的值
uvm中测试用例的启动
一种激励作为一个测试用例。当有多个sequence时,可以在命令行中指定参数来启动不同测试用例。(因为sequence在其中吗?)
//启动my_case0 initial begin run_test("my_case0"); end //当my_case0运行时,需要修改代码,重新编译才能运行。可以使用不加参数的run_test() initial begin run_test(); end
my_casen my_env my_agent(my_sequencer、my_monitor、my_driver)、my_module、my_scoreboard、my_agent(my_monitor)
uvm_test_top env env i_agt(sqr、drv、mon)、mdl、sco、o_agt(mon)
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215163.html原文链接:https://javaforall.net
