代码编织梦想

UVM学习笔记之一

申明本文是对《UVM实战》的学习笔记,部分内容摘抄书中,版权归作者张强所有
本文是对UVM实战这一书第2章的学习记录。

先描述DUT

假设我们有一个如下的DUT:
规格是:
1.通过rxd接收数据,通过txd发送出去
2.rx_dv是接收数据的随路有效指示,tx_en是发送的的有效指示
3.每个时钟接收数据,下一拍发送数据
4.复位清零

验证这个DUT我们需要做什么

我们先描述一下,一个完成的UVM环境需要什么。
它的结构是如下的。

2.2 只有driver的验证平台

拿到这个激励的第一步,肯定是要给它发激励。给时钟,给复位,给rx_dv,给rx_d。这个DUT很简单,上学的时候我们都写过testbench,理论上我们用testbench就可以实现。但是事实上,面对复杂的DUT,testbench是低效的。


在这个driver的main_phase中(问题2-2-0:什么是phase)完成了所有的激励驱动,给时钟、复位、数据。
uvm_driver派生自uvm_component(问题2-2-1:什么是uvm_componet)。
driver有两个new函数有两个参数,一个是string类型的name,一个是uvm_componet类型的parent。(问题2-2-2:这两参数有什么用?这两参数干嘛用?)
(插入代码2-6)
但是这里调用driver还是有像一个testbench.需要手动new,然后执行,然后结束。UVM是不允许这么复杂的,所以有没有一键完成呢?答案是有的,那就是factory机制,工厂机制在整个UVM中实很强大的,我们在这里先只用其一部分功能,即完成上述操作的一键完成。

被注册后的driver,在run_test中传入字符串即完成了上述new到finish的过程。
objection机制:在每个phase中,UVM会检查是否有objection被提起(raise_objection),如果有,那么等待这个objection被撤销(drop_objection)后停止仿真;如果没有,则马上结束当前phase.
注意:raise_objection语句必须在phase中第一个消耗仿真时间的语句之前。
加入virtual interface
为什么要加入interface。interface是干嘛的?简单的说,它是链接DUT和验证组件的连通器。为什么需要它?直连不可以吗?直连肯定是可以的,但是太笨了,因为DUT的线可能会变,层次也可能不一样,可能环境很多地方都要连,有时需要加。你不可能每次变化都到处去改吧?所以就要引入interface,它就相当于一个联通的module吧,你把dut的线连到端口,在验证环境测就调用这个interface的端口就行了。
为什么要叫virtual :这是因为interface一端连在dut,一端连在验证环境组件的原因。
怎么使用interface:直接在driver里面申明,然后…使用就行了。
怎么连接DUT和driver的interface:也就是说,如何知道你在验证组件driver中申明的interface就是dut连接的interface呢?通过层次引用可以吗?不可以。为什么?因为run_test()传入的是不和top_tb是一个层次的。这个时候我们就要用到通信机制,config_db机制了。在top_tb寄信,然后在driver里面收信,这样两者就联系起来了。
config_db机制
config_db机制中,分为set和get两步操作。即可以理解为“寄信”和“收信”
这里的环境的get是放在了build_phase里面,build_phase是在new函数之后main函数之前执行的。
config_db的参数,set和get都有4个参数,这两个函数要匹配,要寄信和收信匹配则第三个参数必须完全一致。set函数的第四个参数表示要将哪个interface传递给收信人。get的第四个参数表示要将要把得到的interface传递给自己的哪个成员。set函数的第二个参数表示路径索引,即“收信人地址”。
小结一下,上述其实就是写了一个driver,并注册到工厂,利用工厂模式,让driver一键执行。同时,把driver和dut之间需要通信的线连接到interface上,便于管理。然后,利用config_db机制,把两者的interface连接起来了。还提到了phase机制和objection机制。

2.3 加入其它组件,完善验证平台

上面driver的驱动,都是信号级别的,就是说给的驱动都是一次性的,而通常激励是多种的。

加入transaction

transacton可以理解为一个包。我们可以有很多个包,每个包可以不一样。这样我们发激励就灵活了。其实,这里都是基于面向对象的思想,激励是激励,驱动是驱动。
transaction派生自uvm_sequence_item。因此常常说的sequence_item也就是transaction。而uvm_sequence_item的祖先是uvm_object。
uvm_object和uvm_component是有区别的。object是有生命周期的,而component是贯彻全程的。这个也好理解,uvm_driver派生自comoinent,driver肯定是整个仿真全程需要存在的嘛,他是机器。而transaction是数据包,发送完,check完就不需要了,换下一个transaction了,它是物料,是流水线上的产品。机器一直存在,一个物料不是一直存在的,因为做好了一个产品,就换下一个物料了。

加入env

env可以理解成环境组件的顶层。run_test()里面刚不是传的是driver嘛。但是事实上,我们的验证组件不止driver。因此需要一个env作为容器,然后把driver、monitor、rm、scoreboard等这些放进去。
type_name::type_id::create
这是factory独特的实例化方式,区别于new。只有在工厂中注册过了,才可以用这种方式实例化,也只有用这种方式实例化了,才能在后面使用factory的重载功能。因此,验证平台中的组件在实例化的时候都应该使用这种方式。这里同样由两个参数,我们回顾问题2-2-2,driver在new的时候同样由两个参数,第一个是名字,第二个是parent。这里其实就是表示他的父节点是啥。我们把driver放在env里面。其实就是相当于rtl中的,把driver例化在env里面,parent参数则表示的这个意思。而这里父节点我理解要区别和区分类派生和继承的子类和父类。
build_phase的执行顺序:这里env里面有build_phase,同时driver里面也有build_phase。那么先执行谁的呢?这里是先执行树根的再执行树叶的,也就是先执行env的再执行树叶的(问题2-3-1:为什么要这个顺序呢?)。这里要区别与父类子类中的执行顺序,可以通过super的位置来控制。

加入monitor

有了env,我们就可以往里面加其他的组件了。
monitor就是监视器,用来收集DUT的数据,收集数据准备给sb去比较。monitor和driver是相对的,driver是给dut数据,monitor是收集dut数据。monitor都应该派生于uvm_monitor,当然他的祖先依然也是component。因为他也是个机器,是个工具,应该存在域整个验证工作过程中。monitor也需要interface,也需要tranction。通常还不止一个interface,输入输出都需要嘛。这里我们为了不凌乱,同时也是基于面向对象的思想。这里引入了agent的概念。

封装成agent

agent是一个收纳,把长得像的东西再打包放到一起。这里既是把DUT的输入这一边的东西打包成一个agent,有输入driver和monitor。把输出端的东西打包成一个agent。这里发现,输出端的agent是没有driver的,因此是不需要实例化driver的。对于是否实例化driver,agent是有提供一个参数可供使用的。is_active。使用枚举值UVM_PASSIVE和UVM_ACTIVE来控制。
把driver和monitor封装成agent后,在env中需要实例化agent,而不需要直接实例化driver和monitor了。
uvm要求uvm树最晚在build_phase时段完成,如果在build_phase后的某个phase实例化一个component,如在main_phase中完成,则会报错。但是可以在new函数中实例化。

加入RM reference model

rm的编写不复杂,但是rm与driver与scorebaoard之间的transaciont传递方式令人感兴趣。实现component之间的transaction级别的通信叫TLM(Transaction Level Modeling)。
要实现通信,需要考虑两点。第一、数据时如何发送的?第二,数据时如何接收的?
如何发送?
在UVM的transaction级别的通信中,数据的发送有很多种,其中一种就是使用uvm_analysis_port。
(插入代码)
uvm_analysis_port是一个参数化的类,其参数就是这个analysis_port需要传递的数据类型,书中例子传递的就是my_transaction.
在monitor中实例化一个uvm_analysis_port。然后在获取transaciton后使用.write()函数写就可以了。
如何接收?
(插入代码)
其中一种是uvm_blocking_get_port.这也是一个参数化的类。在rm中实例化该port,然后使用get()函数则可以从monitor发送的transaction。
如何connect?
到此,实现各个monitor和rm的端口,但是两者之间的通信并没有实现。还需要在env使用fifo将两个端口联系在一起。(为什么需要fifo?因为analysis_port是个非阻塞的)
实例化一个uvm_tlm_analusis_fifo。然后在connect_phase中将两者连接起来。这里用到了connect_phase.
connect_phase是在build_phase执行完后开始执行的,与build_phase不一样的是,它是从树叶到树根的顺序。

加入scoerboard

scoerboard其实就是check,一端数据来自于RM,一端来自于DUT。两者进行比较。
sb中需要用到一个compare函数,该函数是一个在transaction中定义的函数,调用该函数,可实现transaction的比较。即来自rm和dut的transaction的比较。这里又引入了工厂field_automation机制,引入这个机制要解决的问题就是,刚compare函数里面,对transaction内的成员变量比较的问题。一个个的写出来去比,这是UVM不容忍的,况且后面要是加一个岂不是又要来改这里??

加入field_automation机制

field_automation机制其实就是把成员变量进行注册,这样就不需要进行逐字逐段的操作了。使用uvm_object_utils_begin和uvm_object_utils_end来实现。进行完宏注册后,可以直接调用copy、compare、print等函数。同时,在driver和monitor中的collec_one_pkt和drv_one_pkt也可以直接调用pack_byte将tr中的变量一键推入。driver同样unpack_bytes也可以实现。
(问题2-3-2 transaction中有哪些内置函数??)

UVM的终极大作:sequenece

验证平台最复杂的是什么?当然是发激励,各种场景,各种corner。本质上都是要构造激励。因此,发激励这一块,要把数据包,驱动,发送等等这些都要打包出来,而不是柔和在一起。

加入sequencer

sequence机制由两部分组成,sequence和sequencer。er嘛,顾名思义是一个人,那它是干活的,和dirver一样是机器,是comoonent。sequenceer派生自uvm_sequencer,它发送transaction(sequence_item)到driver,driver负责接收,然后发送到dut。
这里其实就是把前面driver中产生激励抽离出来了,driver只能干一件事:那就是把transacion接收了后,写到dut。至于怎么产出,产生怎么样的transaction包,不是他管的。因此,sequencer也要加入agent里面去,因为它拿了原driver的活。同时这里为了内聚,它两都有一个传参,通常是一个transaction。

sequence机制

sequencer是一把枪,sequence是弹夹,item是子弹。sequence是一个容器,但是一个object而不是component。因为,子弹打光了,就可以换弹夹,它也是由生命周期的。

sequence有几个新东西:body、uvm_do、
body
每个sequence都有一个body任务,当一个sequence启动后,会自动执行body里面的代码。
uvm_do
①创建一个transaction的示例my_trans
②将其随机化
③将其发送给sequencer
现在的关系是:
sequence <=-=>sequencer<=-=>driver
sequence 、sequencer、driver之间的连接
driver中有seq_item_port,sequencer中有seq_item_export。他两之间传递的transaction就是他两定义的。他两如何connect呢?他们刚不是放到了一个agent里面了嘛,在connect_phase里面连接就行。似曾相识:刚rm和monirot好像也是这样吧。
连接后,driver可以调用seq_item_port的get_next_item(还有try_next_item是非阻塞的)sequencer申请item,用完后,然后再调用item_done()就高速sequencer,我这个item搞定了。即完成了握手。
然后,sequence如何和sequencer交互呢?刚说了uvm_do的功能,因此sequence想sequencer发送子弹item,应该只需要要指明给那个sequencer就行。这里内置函数start,start的参数就是er的指针。在env里面和在sequencer都行。
这里还提到了之前提到的objection(问题2-4-1:objection到底为啥需要呢?到底为什么需要这个东西,有待了解)。这里解释到:objection一般伴随着sequence,至于它出现的地方才有提起和撤销objection,弹夹里面的子弹用光了,可以结束仿真了。

default_sequence的使用

刚才我们sequence的启动是在env或者sequencer中手动执行start。还可以在env的bulid_phase中使用config_db来实现他两的connect。第三个参数填写sequener,第四个参数填写该seqence。这里似乎不太符合之前说的set第三个参数和get保持一致,第四个参数是收信人地址的说法。这里暂时做遗留问题,这里说是纯粹是UVM的规定。然后,还有刚刚objection的是写在env,即让seq start的地方。这里使用default_sequence的方法,将objection的东西,用starting_phase代替,写在body里面。

2.5 加入base_test

我们一开始的树根是driver,后面为了加入跟多的component,我们加入了env。但是,事实上base_test才是实际中的树根。我们把env加入到base_test中。
run_test()
我们有很多用例,不同的用例,可能产生的tranction不一样,调用的sequence也不一样,因此run_test中传的也不一样,但是每次都要修改run_test来指定用例,肯定也是不可容忍的。因此,run_test()是不传参的,UVM用UVM_TESTNAME来寻找用例

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zyw2018/article/details/125818554

《uvm实战》学习笔记1_上进的蠢猪的博客-爱代码爱编程

验证平台的组成   激励模块:driver计分板:scoreboard收集DUT输出:monitor预期参考模型:Reference model 其他概念:agent、sequence     1、driver模块 class my_driver extends uvm_driver;    function new(string name =

《uvm实战》学习笔记3_上进的蠢猪的博客-爱代码爱编程

UVM中的TLM通信 一、基本概念 1)、put操作:通信的发起者A把一个transaction发送给B。在这个过程中,A称为“发起者”,而B称为“目标”。A具有的端口(用方框表示) 称为PORT,而B的端口(用圆圈表示) 称为EXPORT。这个过程中,数据流是从A流向B的。 2)、get操作:在这个过程中,A依然是“发起者”,B依然是“目标”,A上

uvm实战 学习笔记 第一章 与uvm的第一次接触_沈醉不知的博客-爱代码爱编程_uvm实战

现代IC前端设计流程IC流程 IC设计分类 非算法设计 如网络通信协议算法设计 如图形图像处理how 使用C / C++ 建立算法模型(参考模型)设计语言 Verilog(主流) 版本 1995版2001版ps 可验证(initial, task, functio

《uvm实战》学习笔记-爱代码爱编程

《UVM实战》学习笔记 一、factory机制 在uvm平台中,一个利用`uvm_component_utils注册的类,只要被实例化了,就会自动调用它的main_phase。Run_test(“class name”)会根据类名创建一个实例,如果这个类利用工厂机制注册了,则它的main_phase会被自动调用。二、objection机制 在每个ph

UVM实战 卷I学习笔记1——简单的UVM验证平台:只有driver-爱代码爱编程

目录 验证平台的组成最简单的验证平台——只有driverUVM如何搭建driver:加入factory机制加入objection机制加入virtual interface 个人《UVM实战卷I》学习随手笔记,此书作者:张强 验证平台的组成 一个验证平台要实现的基本功能: 模拟DUT的各种真实使用情况——给DUT施加各种激励→driver实

UVM实战 卷I学习笔记4——创建测试用例-爱代码爱编程

目录 1、加入base_test2、UVM中测试用例的启动 1、加入base_test UVM使用的是一种树形结构,书中最初这棵树的树根是my_driver,后来由于要放置其他component,树根变成 了my_env。但在实际应用的UVM验证平台中my_env并不是树根,通常树根是一个基于uvm_test派生的类。先讲述base_test

《UVM实战卷Ⅰ》学习笔记 前言-爱代码爱编程

目录 1.为什么要写这个专栏? 2.学习《UVM实战卷Ⅰ》的建议 3.其他说明 1.为什么要写这个专栏? 1.尝试使用费曼学习法(即输出式学习法)消化知识 2.个人认为《UVM实战卷Ⅰ》这本书很适合UVM初学者使用,但是网络上关于此书的相关学习经验较少,有部分直接把书中内容搬上来,个人觉得意义不大,因此本专栏会针对某些点进行梳理,梳理过程中可能

UVM实战 卷I学习笔记8——UVM验证平台的运行(1)-爱代码爱编程

目录 phase机制task phase与function phase动态运行phasephase的执行顺序UVM树的遍历super.phase的内容 phase机制 task phase与function phase UVM中的phase按照其是否消耗仿真时间($time打印出的时间)的特性,可以分成一类是function phase(如

UVM实战 卷I学习笔记9——UVM中的sequence(1)-爱代码爱编程

目录 sequence基础从driver中剥离激励产生功能*sequence的启动与执行sequence的仲裁机制在同一sequencer上启动多个sequence sequence基础 从driver中剥离激励产生功能 前面例子中,激励最初产生在driver中,后来产生在sequence中。为什么会有这个过程呢?最开始时,driver的m

UVM实战 卷I学习笔记9——UVM中的sequence(6)-爱代码爱编程

目录 在sequence中使用config_db*在sequence中获取参数*在sequence中设置参数*wait_modified的使用response的使用*put_response与get_responseresponse的数量问题*response handler与另类的response*rsp与req类型不同 在sequence中

UVM实战学习笔记(一)APB VIP-爱代码爱编程

学习完UVM入门课程后,进入到UVM实战阶段,遇到的第一个项目是对APB总线master和slave的验证IP开发。 第一个认识是APBmaster的VIP不是对APB master的验证,而是对master的验证语言实现。在之前的sv和uvm部分学习中,对一个模块的验证是从他的外部接口进行输入和输出对比,按照这个想法,对APBmaster的验证应该是构

UVM实战学习笔记(二)SystemVerilog_Assertion—sequence操作定义(1)-爱代码爱编程

Assertion分类: 1、立即断言:非时序的。如同过程块语句。可以在initial、always、task、function中使用,类似于if语句。 [name : ] assert (expression) [pass_statement] [else fail_statement]. 如assert (a == 1)a = a+1 else

UVM 实战(卷1)-第三章学习笔记-爱代码爱编程

1. UVM component 与UVM object的区别: a. UVM component 派生自UVM object , 所以UVM 具备所有UVM object 的特性。但是UVM component又具有两点是UVM object 不具备的(1.使用new()函数通过指定parent 形成树形结构,2. 具有phase运行机制,可自上向下运

uvm实战 卷i学习笔记13——uvm高级应用(1)_interface信号需要发送给tb_top,drive和scroboard,利用configdb实-爱代码爱编程

目录 interfaceinterface实现driver的部分功能可变时钟 interface interface实现driver的部分功能 在之前所有例子中,interface的定义

uvm实战 卷i学习笔记16——dut代码清单-爱代码爱编程

目录 带双路输入输出端口的DUT:带寄存器配置总线的DUT:带计数器的DUT: 带双路输入输出端口的DUT: module dut(clk, rst_n, rxd0, rx_d