UVM手把手教程系列(一)UVM验证平台基础知识介绍

描述

UVM验证平台介绍

先抛开UVM,回想一下我们在平时写完程序后,是不是肯定需要灌一个激励给DUT,然后再从DUT获取结果,并跟一个参考模块进行对比,检查结果是否正确。就像下面这个图:

UVM

在UVM中,引入了sequence机制,这个机制的最大作用就是将test case和testbench分离开来。对一个项目而言,testbench是相对稳定的框架,而针对各个module要有不同的测试内容,所以具体的test case 的差异非常大。在UVM中, test和sequence类总是成对出现,实现了testbench和具体的test case的结合。test类中可以针对具体的测试内容对testbench做一些差异化配置,在sequence类中则是实现test case的具体细节。

因此,在UVM中,使用sequence来产生transaction,再由sequencer传给driver,再作为激励传给DUT。也就是说driver只负责驱动transaction,而不负责产生transaction。

UVM

同时UVM中将sequencer、driver和监测激励的monitor封装到一个in_agent中,将监测DUT输出的monitor封装到out_agent中,以提高代码的可重用性,模型如下:

UVM

我们再对上面这些模块做一些更详细的解释,暂时看不懂没关系,先有个概念就行,后面的东西学的多了,这些概念自然也就懂了。

driver:向sequencer申请sequence_item(数据包transaction),并将包里的信息按照总线协议规定驱动到DUT的端口上;

sequencer:负责发送数据,组织管理sequence,driver申请数据时,它就把sequence生成的sequence_item发给driver;(需要注意sequence和sequencer的区别)

scoreboard:比较reference model和monitor分别发送来的数据,根据比较结果判断DUT是否正确工作;

monitor:从DUT接收数据,并将其转成transaction级别的sequence_item,发送给scoreboard,供其比较;

reference model:模仿DUT,完成与DUT相同的功能,为scoreboard提供判断标准;

agent:将sequencer、driver和monitor封装在一起(UVM_ACTIVE/UVM_PASSIVE、两种模式),agent模块的使用提高了代码的可重用性;

env:将平台上的component组件封装在一起,并配置各个组件间的通信端口,实现一个环境多个用例。运行不同用例时,在其中实例化env即可;

sequence:(不属于验证平台的任一部分)产生激励内容(transaction)。通过sequence中的body任务创建(随机化)事务,并发送给sequencer。

UVM中的类

component与object是UVM中两大最基本的概念,也是初学者最容易混淆的两个概念。uvm_object是UVM中最基本的类,读者能想到的几乎所有的类都继承自uvm_object,包括uvm_component。

uvm_component有两大特性是uvm_object所没有的:

通过在new的时候指定parent参数来形成一种树形的组织结构

phase的自动执行特点

从图中可以看出,从uvm_object派生出了两个分支,所有的UVM树的结点都是由uvm_component组成的,只有基于 uvm_component派生的类才可能成为UVM树的结点;最左边分支的类或者直接派生自uvm_object的类,是不可能以结点的形式出 现在UVM树上的。

UVM通过uvm_component来实现树形结构。所有的UVM树的结点本质上都是一个uvm_component。每个uvm_component都有 一个特点:它们在new的时候,需要指定一个类型为uvm_component、名字是parent的变量:

 

function new(string name, uvm_component parent);

 

构成环境的组件都从uvm_component类继承而来,这是因为它们都从uvm_component类继承了phase机制,都会经历各个phase阶段,关于phase的内容,我们后面介绍。

UVM

uvm_driver

所有的driver都要派生自uvm_driver。driver的功能主要就是向sequencer索要sequence_item(transaction),并且将 sequence_item里的信息驱动到DUT的端口上,这相当于完成了从transaction级别到DUT能够接受的端口级别信息的转换。

uvm_monitor

所有的monitor都要派生自uvm_monitor。monitor做的事情与driver相反,driver向DUT的pin上发送数据,而 monitor则是从DUT的pin上接收数据,并且把接收到的数据转换成transaction级别的sequence_item,再把转换后的数据发送给 scoreboard,供其比较。与uvm_component相比,uvm_monitor几乎没有做任何扩充。

uvm_sequencer

所有的sequencer都要派生自uvm_sequencer。sequencer的功能就是组织管理sequence,当driver要求数据时, 它就把sequence生成的sequence_item转发给driver。

uvm_scoreboard

一般的scoreboard都要派生自uvm_scoreboard。scoreboard的功能就是比较reference model和monitor分别发送 来的数据,根据比较结果判断DUT是否正确工作。

reference model

UVM中并没有针对reference model定义一个类。所以通常来说,reference model都是直接派生自 uvm_component。reference model的作用就是模仿DUT,完成与DUT相同的功能。DUT是用Verilog写成的时序电路,而reference model则可以直接使用SystemVerilog高级语言的特性,同时还可以通过DPI等接口调用其他语言来完成与DUT相同的功能。

uvm_agent

所有的agent要派生自uvm_agent。与前面几个比起来,uvm_agent的作用并不是那么明显。它只是把driver和 monitor封装在一起,根据参数值来决定是只实例化monitor还是要同时实例化driver和monitor。agent的使用主要是从可重用性的角 度来考虑的。如果在做验证平台时不考虑可重用性,那么agent其实是可有可无的。

uvm_env

所有的env(environment的缩写)要派生自uvm_env。env将验证平台上用到的固定不变的component都封装在一 起。这样,当要运行不同的测试用例时,只要在测试用例中实例化此env即可。

uvm_test

所有的测试用例要派生自uvm_test或其派生类,不同的测试用例之间差异很大,所以从uvm_test派生出来的类各不 相同。任何一个派生出的测试用例中,都要实例化env,只有这样,当测试用例在运行的时候,才能把数据正常地发给DUT,并正常地接收DUT的数据。

UVM UVM

UVM的树形结构

上一节我们讲了UVM中的类,也可以看到UVM中各个类的继承关系。了解了这些,再来看UVM中的树形结构。UVM中的sequencer、driver、monitor、agent、model、 scoreboard、env等都是树的一个结点。为什么要用树的形式来组织呢?因为作为一个验证平台,树形结构是一种非常合适的管理方式。

UVM的完整树结构如下图所示:

UVM

uvm_top是一个全局变量,它是uvm_root的一个实例,而且也是唯一的一个实例 ,它的实现方式非常巧妙。而uvm_root 派生自uvm_component,所以uvm_top本质上是一个uvm_component,它是树的根。uvm_test_top的parent是uvm_top,而uvm_top的 parent则是null。

uvm_top提供一系列的方法来控制仿真,例如phase机制、objection防止仿真退出机制等。

层次结构相关的函数

UVM提供了一系列的接口函数用于访问UVM树中的结点。这其中最主要的是以下几个:

get_parent 用于得到当前实例的parent,其函数原型为:

 

extern virtual function uvm_component get_parent ();

 

get_child函数,与get_parent不同的是,get_child需要一个string类型的参数name,表示此child实例在实例化时指定的名字。因为一个component只有一个parent,所以get_parent不需要指定参数;而可能有多个child,所以必须指定name参数。

 

extern function uvm_component get_child (string name);

 

get_children函数,为了得到所有的child

 

extern function void get_children(ref uvm_component children[$]);

uvm_component array[$];
my_comp.get_children(array); 
foreach(array[i])
    do_something(array[i]);

 

get_num_children函数,用于返回当前component所拥有的child的数量

 

extern function int get_num_children();

 

除了一次性得到所有的child外,还可以使用get_first_child和get_next_child的组合依次得到所有的child

 

extern function int get_first_child (ref string name); 
extern function int get_next_child (ref string name);

string name; 
uvm_component child; 
if (comp.get_first_child(name))
do begin 
        child=comp.get_child(name); 
        child.print(); 
end while (comp.get_next_child(name));

 




 

审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分