开始是直接对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父类
phase
函数phase(不消耗仿真时间):build_phase
任务phase:mian_phase
- class A;
- ……
- endclass
-
- A a_inst;
- a_inst=new(); //new()通知仿真器创建一个A的实例,之后会开辟空间,返回空间指针
每个phase中,uvm会检查是否有objection被提起(raise_objection),若有,那么等待这个objection被撤销(drop_objection)后停止仿真,若无则马上结束当前phase。raise_objection必须在main_phase中第一个消耗仿真时间的语句前。
避免绝对路径方法
定义了接口后,在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)