UVM是一个方法,是一个平台。UVM-1.2平台提供了:
uvm_test/env/sequence/sequencer/driver/monitor/agent、
base/comp/tlm/seq/ral/
'uvm_fatal/error/warning/info/object_utils/component_utils(extends使用时需要注册)
uvm_port/socket/sequence_item/transaction
这些libraries库
UVM的组成:
component(功能仿真算法等)、communication(通信TLM,port,export、fifo)、transaction(数据定义)
component由uvm_test/env/sequence/sequencer/driver/monitor/agent组成;
communication通信由uvm_port和uvm_socket组成;
transaction的数据定义传输由uvm_sequence_item组成。
以uvm_test为test文件的基类,uvm_driver为命令解析的基类,uvm_monitor为命令采集的基类
uvm_sequence和uvm_sequencer为激励的基类,uvm_agent为协议的基类等,不同的接口用不同agent
interface则是一个SV写法的接口定义
static不能定义virtual虚拟定义的class
引用static的要用双冒号::调用class_name::function,静态解析符
Singleton class 用static定义的属性,没有new(),没有if,没句柄。
Singleton object 只使用一个静态的对象, 有new()、if。
virtual class里面只有virtual function,虚态的属性
CDV覆盖的驱动验证(Coverage-Driven Verificaition)
registry 注册表
覆盖率的途径有line、expression、FSM(状态机)、triggle(触发器)、path(这个为主)
UVM里面的class必须写在program里面,然后在initial调用
更高的覆盖率,更简洁的代码。
测试层、场景层、功能层、命令层、信号层(五个层次)。
component的phase主要由7个机制组成:
build phase/connect phase/run(reset、configure、main、shutdown)phase
机制的意义是什么?
答:多个testcase运行当中有时候需要同步进行,而这些机制的运行,是按照一定顺序进行。
一般在component和transaction里进行拓展编写, sequence_item
class要写在program里
注册一个class,
然后在run_phase里面写操作,
接着用initial调用跑,不能直接跑,
就这样写好了,输出的结果是“Hello UVM”
test-testcase(uvm_test)-environment(uvm_env)-agent(sequence、driver、monitor)-transaction这是流程
在task里写功能层,不可在function写,function是一个函数的行为级
仿真时候用vsim test —+ UVM_TESTNAME = WHAT
但一般的test没这么简单,而是:
在test里面写environment的class,type id::create()构造
test_case调用envrionment env,开始envrionment env调用component
从而形成一个完整的调用过程。
上面提到的build_phase是我们之前讲的component的phase7个主要机制组成的第一个
在build_phase里面写的phase从上到下先执行,再其他的phase从下至上执行。
并行功能性的写在run_phase里面
所有以uvm开头的基类拓展的类需注册
uvm_component_utils(new_test_base)
testbench由以下组成:
test-testcase(uvm_test)-environment(uvm_env)-agent(sequence、driver、monitor)-transaction(处理)写agent- TLM连接
TLM-Transaction Level Modeling 是传输模型,component里,port/export/fifo等串口的传输
工厂factory模式,用override替换新的driver、类似是一个新驱动,全都改变
transaction差不多就是打包了(address=A、data=5、op=write)的传输
下面是transaction的使用过程:
1)sequencer写的items(transaction)传输给driver,(address=A、data=5、op=write),用interface送给DUT
2)DUT传给monitor然后在Scoreboard查看传输情况
testcase-sequencer-driver-DUT-Scoreboard
二值逻辑的初始值是0,四值逻辑的初始值是x。
写transaction的时候,第一不要写错父类,第二一般有rand随机属性,补充:随机可关(mode=0)
使用new_transaction:
先注册一个transaction,来自uvm_sequence_items
'uvm_object_utils_bgein(transaction)
再使用transaction的时候create该句柄
tr=transaction::type_id::create(“tr”,this);
如何改变约束
close the constraint;
tran tr;
tr=new();
tr.randomize with 100; ##使约束值为100
constrain是约束:
constrain rule{
len in {100,200};
}
pack用于打包传输一串数据;upack用于解包
宏定义的方法使用
tr0.copy(tr1);tr0.print();
$cast(tr1,tr0.clone);
tr0.pack(bit_stream);
tr0.unpack(bit_stream);
post_randomize()是指随机函数,自动执行。
问题:突然想对其中的一个data取消print,packed,unpack系列等其他的操作,如何做?
回答:注册注销,注销宏定义,或者重写do_pack函数
uvm_sequence产生随机数基类,用sequencer传送改写的sequence给driver,最后到DUT。
call sequence’start() method调用指定的sequence。
一个agent由agent、sequence、sequence_item、sequencer、driver五部分组成
sequence在task body()函数里面写rand激励,写响应。phase raise开启、drop结束。req的class是传参的句柄
多个sequence的使用:
sequence_item(transaction)产生数据格式,用sequencer将sequence的随机数据格式的对象送给driver,最后到DUT。
上面的sequencer做到了注册的作用,而真正的启动sequence和driver之间的通信,还是需要下面的启动方式。
写完driver写通信,下面是sequencer和driver的通信,用TLM通信。圈圈里面的是sequencer。
-
或者先null,再重新写进去(不推荐)
uvm_config_db #(int)::set(this,“env.agent.seqr”,“port_id”,12); #还可以用来传递参数
phase —变量类型 ----赋值 ------- 路径 -------------端口--------值
seq_phase::set(this.“env.seqr.configure_phase”,“default_sequence”,“seq_CFG::get_type()”)
来配置sequence的phase
configue_db是跨class的配置。一般组合set和get
届时还要写一个agent class;
agent编写connect函数将sequencer和driver连接起来,
先create定义句柄,然后connect两者,
randc所有的随机值实现一遍
提到message,其由none、low、medium、high,full、debug选择输出次数
“%m”显示要执行的层次,用在UVM_HIGH里面:
#()表示传输的参数的类型
component的configuration用来配置数据和string:
uvm_config_db #(type)::set(this,“name”,“if”,dut_test_top.if) ##亦或uvm_resource_db #(type)::set(this,
uvm_resource_db #(type)::set(this,“name”,“if”,dut_test_top.if)
component由7个机制phase组成,build、connect、run(reset、configuration、main、shutdown)
set和get最好都写。
先注册一个transaction(sequence_item),
'uvm_object_utils_begin(transaction)
再virtual task 的body()
的construct里agent的create句柄
tr=transaction::type_id::create(“tr”,this);
set_type_override_by_type(transaction::get_type(),new_trans::get_type());或者
set_inst_override_by_type("*.path",transaction::get_type(),new_trans::get_type());改路径
此时完成了对transaction的修改
当然,driver也可以
实现factory机制(实现test)三步骤:
先注册class(extends),后create::type,
再set_inst_override_by_type(driver::get_type(),driver_2::get_type(),“uvm_test_top.t_env.ag1.drv”);
将driver更替为driver_2
这样就能修改内容,调用的时候改变。
四种class override:
set_inst_override_by_type();
set_type_override_by_type();
set_inst_override_by_name();
set_type_override_by_name();
为什么uvm提供factory机制?
答:重新写一个组件,一般来说,testbench写完后不能改,在需要修改driver、monitor的时候
重写override来替换,实现新的需求;第二个,插入errer_testcase,重改,重写transaction。
哪些class有factory机制?
答:uvm_component :driver、monitor、agent、transaction
如何实现factory机制?
一:注册 uvm_object_utils and uvm_component_utils(transaction type component type);
二:create::type,class需要调用,分配对象;
三:写新的transaction_da_3,用set_type_override_by_type;或者set_inst_overrid e_by_type;完成override的替换(即factory机制)
实现新的transaction_da_3的时候,extend的是原来的transaction;
老driver如下:
新driver如下:
用factory机制替换,
create 和 new()分配一个对象的问题?
用create对象句柄,修改driver、monitor这种。而new()无法使用factory的机制,
config_db机制配置的方法?两种?
config_机制由上层set到底层,配置interface/object/class等传输,如:set string—》filed name
uvm_config_db #(type)::set(this,“name”,“if”,dut_test_top.if) local configuration
uvm_resource_db #(type)::set(this,“name”,“if”,dut_test_top.if) global configuration
什么时候只需要::set,不需要::get?
注册变量之后,可以不需要::set
可以使用uvm_top.print_topology()查看修改后的情况