第2章一个简单的UVM验证平台
2.3 为验证平台加入各个组件
2.3.1 加入transaction
引入transaction目的:
- 更规范地传递信息, 更方便地引入transaction级的随机激励 。(一般来说,物理协议中的数据交换都是以帧或者包为单位的,而transaction就类似于这里包的概念)
my_transaction派生自uvm_sequence_item,而uvm_sequence_item是uvm_object的派生类。
派生自uvm_object的类 vs 派生自uvm_component的类
主要流程如下:
- 在main_phase中,先使用randomize将tr随机化,之后通过drive_one_pkt任务将tr的内容驱动到DUT的端口上。在drive_one_pkt中,先将tr中所有的数据 压入队列data_q中 ,之后再 将data_q中所有的数据弹出并驱动 。将tr中的数据压入队列data_q中的过程相当于打包成一个byte流的过程。
2.3.2 加入env
引入uvm_env的原因:
- 解决多个并列关系模块(driver、 monitor、 reference model和scoreboard等)实例化问题 。
问题:
- 在top_tb中例化这几个模块不行:run_test在top_tb结构层次之外建立一个新的结构层次,模块间传值需要通过config_db机制
- 在run_test中例化这几个模块不行:run_test函数本身限制,只能实例化一个模块。
- 在driver中例化其他模块不行:会让这些模块具备父子关系,打破了他们之间的并列关系。
解决:
- 引入env机制,提供一个容器类,该容器类中包含了多个并列关系的模块,然后用run_test来实例化这个容器类 。
在UVM的树形结构中, build_phase的执行遵照从树根到树叶的顺序。
2.3.3 加入monitor
引入monitor原因:
monitor与driver的比较
关于monitor使用的4点注意:
- 所有的monitor类应该派生自uvm_monitor
- 与driver类似,在my_monitor中也需要有一个virtual my_if
- 使用uvm_component_utils宏注册
- monitor需要时刻收集数据,所以在main_phase中要使用**while(1)**循环
2.3.4 封装成agent
引入agent的原因:
- driver和monitor二者 处理的是同一种协议 ,代码高度相似,所以将两者封装在一起。 不同的agent就代表了不同的协议 。
is_active是uvm_agent内置的一个成员变量,通过顶层传值,控制driver是否进行例化,且is_active的值默认为UVM_ACTIVE 。
- UVM_PASSIVE:例化monitor而不需要例化driver(输出端口无需驱动)
- UVM_ACTIVE:例化monitor,也需要例化driver(输入端口需要驱动)
例化动作可以在build_phase函数中完成,也可以在new函数中完成,但强烈建议仅在build_phase中完成实例化。
2.3.5 加入reference model
引入原因:
这里reference model对应的模块名为 my_model。my_model从i_agt得到my_transaction,并把my_transaction传递给my_scoreboard。在UVM中,通常使用 TLM( Transaction Level Modeling) 实现component之间transaction级别的通信。my_transaction在my_model中的传递方式大致分为三部分:
- 在my_monitor中使用uvm_analysis_port类例化一个用于发送transaction级数据的端口,并通过my_agent中uvm_analysis_port的引用变量往my_env传递端口
- 在my_model中使用uvm_blocking_get_port类例化一个用于接收transaction级数据的端口
- 在my_env中使用uvm_tlm_analysis_fifo类例化一个fifo。引入 connect_phase ,将fifo的analysis_export端口连接到i_agt.ap,fifo的blocking_get_export端口连接到mdl.port。(mdl为my_model类的例化对象)
- 使用fifo的原因: analysis_port是非阻塞性质的 , ap.write函数调用完成后马上返回,不会等待数据被接收。
2.3.6 加入scoreboard
引入scoreboard作用:
- 比较reference model和o_agt的monitor的结果。
多进程的使用:
- 在my_scoreboard中使用uvm_blocking_get_port新建两个port:exp_port、act_port,并在main_phase中,通过fork建立起两个进程,一个进程处理exp_port的数据(ref),当收到数据后,把数据放入expect_queue中;另外一个进程处理act_port的数据(dut),当收集到这些数据后,从expect_queue中弹出之前从exp_port收到的数据,并调用my_transaction的my_compare函数。
- 由于DUT处理数据需要延时,而reference model是基于高级语言的处理,一般 不需要延时 ,因此 可以保证exp_port的数据在act_port的数据之前到来 。
2.3.7 加入field_automation机制
引入field_automation机制的原因:
- 自动实现my_transaction中print、copy、compare这样的比较常见的结构体操作,简化my_transaction的实现。同时默认的pack_bytes(tr中的各个字段转换成byte流)和unpack_bytes(byte流转换成tr中的各个字段)也简化了driver、monitor的实现。
使用uvm_object_utils_begin和uvm_object_utils_end来实现my_transaction的factory注册,在这两个宏中间,使用uvm_field宏注册所有字段。
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(dmac, UVM_ALL_ON)
`uvm_field_int(smac, UVM_ALL_ON)
`uvm_field_int(ether_type, UVM_ALL_ON)
`uvm_field_array_int(pload, UVM_ALL_ON)
`uvm_field_int(crc, UVM_ALL_ON)
`uvm_object_utils_end