可重用的UVM验证结构

本文转自:http://www.eetop.cn/blog/html/28/1561828-437611.html

引言

用SystemVerilog和UVM写验证平台时,会在模块级和系统级面临的可配置性和可重用性的问题。而从一个模块到系统级验证环境中去重用通用验证组件(Universal Verification Component)是相对比较容易的,但是上述情况不能说成是为把UVC连接到接口线束(harness):这些接口即UVCs需要改变的是从模块级连接主要的输入输出端口到系统级连接DUT内部的一系列分层的探针。这种方式需要重写所有的接口并且也阻碍了对其进行再次复用。

本文将演示如何只对接口连接仅仅只写一次且在模块级和系统级验证环境中使用这些接口。这种顺序并不重要:即系统级验证平台可以在没有完成DUT中的所有模块的情况下被验证人员搭建,DUT和UVM验证平台的各个模块可以很容易的被同时进行。我们并不关心在UVC中使用没使用虚拟接口,反而应该关心的是接口中的总线功能模型(Bus Functional Models)——这一点也被称之为多态接口,使得UVC能够被完全地配置和重用。

    

从接口看它们的可重用性、可配置性以及限制性

在一款芯片的开发过程中,通常来说开发DUT的模块和验证它们是相互独立的。在许多这样的模块被集成到一个完整的芯片之前,通常都将这些模块被分组到更大的模块/子系统中去。这意味着验证平台需要随着DUT不断地开发去进行完善。最新的验证平台经常使用基于验证环境的UVM的类。这个焦点很多都是考虑到可重用性,比如主要从模块级到芯片级进行垂复用,但是也有在模块之间的水平复用。这种不牢靠的连接,一方面是DUT这种静态的模块层级(其通常在验证平台中被实例化)之间的连接,另一方面是动态的基于验证环境的类的连接。

像模块一样,接口是在细化阐述的时候被创建的,因此开发验证平台的验证工程师将会在验证平台中实例化DUT所需要的所有接口,同时将它们连接到DUT上,并且DUT也是要被实例化到验证平台当中去的。虽然SV的接口指的是一种“可综合的信号收集功能”去减轻DUT模块繁重的连接,但是这种方法目前还没有被广泛采用。在验证平台中连接DUT,这种方法会导致的一个同样繁重的任务:即每个信号必须被单独地连接。更糟的是,这些连接不能被垂直复用:即当模块被集成到一个子系统时,它通常希望保持agent和block之间的连接并且将agent切换到passive模式。这些要求即接口被连接到掩埋在子系统内的信号上去。这不仅显得单调乏味,而且可能在后期综合(post-synthesis)时这些信号很难被发现。

从testbench顶层,SV的interface被传递到验证环境当中去。这一点既可能由一个set_vi()access function访问函数通过写一个wrapper去完成;或者由UVM的configuration机制去配置接口并且将接口传递到agent当中去,这一点是因为uvm_config_db的出现,它可以通过直接传递虚拟接口到uvm_config_db中,使得将接口传递到验证环境中去非常方便。但是前面所讲的用于传递接口的方法也存在缺点比如它不利于水平复用,因为虚接口类型包括类型说明,在testbench中给接口提供如此多的参数必须要在编译时让agent知道。典型的代码如下所示:

 

另一个问题是,agent需要知道虚拟接口详细说明。这些信息只能来自agent自己特有的说明、一条定义好的命令,或者来自一个包含了所有配置选项详细信息的包。

    

编写一个可重用的验证框架

接口连接的问题可以通过改变接口被实例化的位置来解决。使用捆绑命令(bind command)将其绑定到模块中,编译器仿佛视它就在模块内部,赋予它访问模块中所有信号的权利。通过改变接口的声明方式使得它使用端口方式而不是内部信号的方式去连接到DUT上,我们可以在bind command中用如下的方式去写端口列表,使它可以连接到DUT中的任何信号上去。Bind command 如下所示:

 

这种bind command命令指导编译器去添加一个类型为bus_if的接口实例化bus_if_inst给samll block。并且将它绑定到类型为small_block上,因此small_block的每一次实例化都将会有接口的一个实例化发生。比如在small_block里的信号bus1_addr等等。另外,也有可能仅仅只绑定接口到一个明确的模块实例上去。

要注意的是,模块的类型说明和接口需要匹配,否则被连接的信号将会出现不同的位宽。在这种情况下,我们只想连接接口到DUT的端口上去,这一点在参考文献[1]中有详细描述,这里不做过多介绍。此外,推荐在参考文献[2]中使用这种方法给在DUT中做探针(probing)使用,其中一个探针接口被绑定到DUT中并且被连接到DUT内部的任何信号上去。 参考文献[2]也展示了如何使用时钟块和modports方法,由于时间有限在这里将其省略,若你对这部分感兴趣可在该文献中详细查询。

应该注意的是在接口中端口的方向不能定义为输出。一个输出端口增加了一个驱动给这个端口信号,在passive模式下接口的重用将被禁止。 这种bind command与一个“真实”的接口实例化具有同样的陷阱,比如当一个连接被拼写错误时的隐式声明信号的方式这种情况下。

通过为agent消除这种不必要的需求,在agent其内部拥有一个虚接口的方式从而有效避免在agent中将类型进行详细说明的问题。这一点可以通过使用总线功能模型(BFM)来完成,其中一个用于包含了功能原型(function prototypes)的BFM的虚基类(virtual base class)将从uvm_component扩展而来。

这个基类将被用于agent当中去并且将在接口中被重载,其中功能原型将会被实现。如果一个wrapper class类被以同样的方式为BFM提供一个绑定函数去使用的话,BFM的绑定可以在正确的UVM phase中完成,并且通过UVM 配置机制使得它可配置化,就如同其他任何的uvm_component被配置一样。在参考文献 [3]中一个叫做get_api_wrapper()的函数也必须在接口实现,绑定wrapper class的一个实例并且返回一个句柄给它。其实现如下图所示:

 

然后,验证平台可以建立这个wrapper类并且通过简单地调用该接口的get_api_wrapper()函数得到它的句柄

使用总线功能模块还具有这样的优点比如它能够加速仿真。为此,运行在一个仿真器上的(计时)DUT,需要严格的从运行在一个模拟器上的(不计时)验证代码中分隔开。它们只能被允许通过事物(transaction)进行通信,这一点可以再参考文献 [4]中找到详细的答案。

下面让我们来看一个使用了简单总线从接口(slave interface)小模块的例子。为了确保要在水平方向和垂直方向上都能复用该模块,我们可以把它和一个控制模块和一个存储器模块联合在一起集成成为一个大的模块。为了保持它的简单特性,在大的模块仍然使用相同的总线从接口,并且它将被传递到控制模块中去。这里采用最重要的地址位去指导事务到达小模块或这存储器模块中去,其最终仿真结果如下图所示。

从这个log文件可以看到有一个被配置成active模式的BFM的agent,并且驱动部分数据到DUT,当事务到达时它将会被被报告在log文件中。对于子系统的结构,我们想去保持这个agent和interface,但要把agent变成passive模式,以便它只能对信号做监视并且报告监视信号活动的行为。我们需要另外一个相同类型的interface和agent,但是与之前不同的使用的是不同的地址和数据位宽去连接到一个大模块中的总线上去。在DUT中有一个刻意不匹配的数据位宽用于去演示在相同的架构内使用不同的类型。

 

结论

使用总线功能模型确实是划分一个验证环境的好方法。如果这个BFM需要是可配置的,那么必须小心仔细的在UVM的正确的phase中绑定它,并且在UVM的hierarchy层次结构中合理的插入BFM。这也保证仿真如果是根据仿真器的要求去写BFM的access函数这种情况。使用bind command命令去实例化接口到DUT中去,这一点允许我们只对一个模块的端口连接操作一次并且可以在子系统级和芯片级去再次重用那些连接连接。之后就都可以在水平方向和垂直方向上复用。

参考文献:

[1] A proven methodology to hierarchically reuse interface connections from the block to the chip level, David Larson

[2] The Missing Link: The Testbench to DUT Connection, David Rich, 2012

[3] Flexible UVM Components: Configuring Bus Functional Models, Gunther Clasen, 2013

[4] UVM Acceleration: Creating Emulation-Ready UVM Testbenches to Boost Block-to-System Verification Productivity, Hans van der Schoot

猜你喜欢

转载自blog.csdn.net/qq_41394155/article/details/83108299