在现代IC设计流程中,当设计人员根据设计规格说明书完成RTL代码之后,验证人员开始验证这些代码(通常称其为DUT,Design Under Test)。验证工作主要保证从设计规格说明书到RTL转变的正确性,它包括但不限于以下几点:DUT的行为表现是否与设计规格说明书中的要求一致
DUT是否实现了设计规格说明书中的所有功能
DUT对于异常状况的反应是否与设计规格说明书中的一致
DUT是否足够稳健,能够从异常状态恢复到正常的工作模式
图1 验证发现DUT中的bug
随着芯片规模的增大,验证占整个芯片设计流程的比例越来越大,验证的工作量已经占到整个SoC研发周期的70%到80%。因此,提高芯片验证的效率已变得至关重要。快速搭建一个强大、高效、灵活、可扩展性好的验证平台是芯片成功的关键。
1 UVM代表了验证方法学的发展方向
验证是服务于设计的,目前来说,主流的设计语言有两种:Verilog和VHDL。伴随着IC的发展,涌现出了多种验证语言,如Vera、e、SystemC、SystemVerilog等。其中,SystemVerilog刚一推出就受到了热烈欢迎,SystemVerilog是一个Verilog的扩展集,它既完全兼容Verilog,又具有所有面向对象语言的特性:封装、继承和多态,同时,还为验证提供了一些独有的特性,如产生带约束(constraint)的随机激励。在基于SystemVerilog的验证方法学中,主要有以下三种:VMM(Verification Methodology Manual),这是Synopsys在2006年推出的; VMM中集成了寄存器解决方案RAL(Register Abstraction Layer)。当OVM出现后,面对OVM的激烈竞争,VMM已经开源
OVM(Open Verification Methodology),由Cadence和Mentor于2008年推出;从一开始就是开源的。它引进了factory机制,功能非常强大,但是它里面没有寄存器解决方案,这是它最大的短板。针对这一情况,Candence推出了RGM,补上了这一短板。只是很遗憾的是,RGM并没有成为OVM的一部分,要想使用RGM,需要额外下载。现在OVM已经停止更新,完全被UVM代替
UVM(Universal Verification Methodology),其正式版是在2011年2月由Accellera推出的,得到了Synopsys、Cadence和Mentor的一致支持。UVM几乎完全继承了OVM,同时又采纳了Synopsys在VMM中的寄存器解决方案RAL。UVM继承了OVM和VMM的优点,克服了各自的缺点,代表了验证方法学的发展方向
图2 验证方法学的发展
2 UVM验证平台的基本组件
UVM是一个以SystemVerilog类库为主体的验证平台开发框架,验证工程师利用其可重用组件可以构建具有标准化层次结构和接口的功能验证环境。UVM验证方法学有效结合了测试激励随机生成、自测试平台和随机化约束等方法,它采用最佳框架以实现覆盖率驱动的验证,使验证工程师通过采用高级验证技术来降低风险,满足缩短产品上市时间的迫切需求。
UVM是一个库,在这个库中,几乎所有的东西都是使用类(class)来实现的。类是面向对象编程语言中最伟大的发明之一,是面向对象的精髓所在。使用UVM的第一条原则是:验证平台中所有的组件都应该派生自UVM中的类。当要实现一个功能时,首先应该想到的就是从UVM的某个类派生出一个新的类,类中可以有成员变量,也可以有函数和任务,通过成员变量、函数或任务实现所期望的功能。
图3 简单的UVM验证平台
验证用于找出DUT中的bug,这个过程通常是把DUT放入一个验证平台来实现的。上图描述了一个简单的UVM验证平台,下面结合该图简要描述UVM验证平台的基本组件。transaction,派生自uvm_sequence_item,它是一个抽象的概念,用于描述各个组件传递的信息,是各个组件传递信息的基本数据结构。
driver,派生自uvm_driver,用于给DUT施加各种激励,负责驱动transaction,它本身并不产生transaction
monitor, 派生自uvm_monitor,监测DUT的输入输出信号。用于收集DUT的端口数据,并将其转换成transaction交给后续的reference model和scoreboard组件进行处理
sequencer,派生自uvm_sequencer,用于检测仲裁队列中是否有发送transaction的请求,以及检测driver是否申请transaction
in_agent,派生自uvm_agent,它是一个容器,用于实例化driver、monitor和sequencer
out_agent,派生自uvm_agent,它也是一个容器。由于在输出端口中不需要驱动任何信号,只需要监测信号,所以out_agent中只需要实例化monitor
reference model,派生自uvm_component,用于完成和DUT相同的功能,得到预期结果。一般采用高级语言描述,不用可综合,其输出被scoreboard接收,用于和DUT输出相比较
scoreboard,派生自umv_scoreboard,根据DUT的输出来判断DUT的行为是否与预期相符合,实现将DUT的输出和reference model的输出进行比较
sequence,派生自uvm_sequence,用于产生transaction,即产生激励。sequence用于创建transaction的实例,并将其送给sequencer。
在以上各组件中,driver、monitor、sequencer、agent、reference model和scoreboard都是属于派生自uvm_component的类,它们是验证平台中的固定组成部分。transaction是派生自uvm_object的类,它是验证平台中各组件之间传输的基本数据结构,一般来说,物理协议中的数据交换都是以帧或者包为单位。sequence也是派生自uvm_object的类,它定义了transaction的产生方式,包括产生带约束的transaction,它与具体的测试用例相关。
3 UVM中的sequence与测试用例
sequence机制是UVM中最重要的机制之一,它由两大部分组成:sequence和sequencer。sequence与sequencer之有着密切的联系,这点从二者的名字就可以看出来。sequence直接与sequencer打交道,当driver向sequencer索要数据时,sequencer会检查是否有sequence要发送数据,当发现有sequence_item待发送时,会把此sequence_item交给driver。简言之,sequence用于产生transaction;sequencer则用于承载sequencer产生的transaction并转交给driver使用。
图4 UVM中的sequence机制
UVM中引入sequence机制的好处是将激励从driver中剥离出来,有利于测试平台的重用。通过编写不同的sequence,实现不同的测试用例。当一个sequence启动之后,会自动执行task body中的代码,用于创建transaction
sequence中可以产生带约束的transaction,它与测试用例密切相关
sequence中可以嵌套调用其它sequence
当一个验证平台中存在多个sequencer时,通常会采用virtual sequencer来进行调度。virtual sequencer同样派生自uvm_sequencer,它与一个virtual sequence对应
virtual sequence用来统一调度多个sequence,以便与测试用例直接相连,其本身并没有定义新的sequence。virtual sequence中的task body可以采用fork … join等并行语句来控制多个sequence的行为
随着验证的进行,测试用例的数量一直在增加,在增加的过程中,很重要的一点就是保证后增加的测试用例不影响已经建好的测试用例。在UVM中,最常用的方法是在仿真脚本或命令行中通过指定UVM_TEST_NAME来寻找测试用例的名字,创建它的实例并运行。每个UVM测试用例启动后,都会对应一个完整的UVM树结构,每颗UVM树的顶层都是uvm_test_top。
图5 某测试用例建立的UVM树
4 UVM验证平台:案例学习
下图描述了某SoC系统的UVM验证平台总体结构框图。如图所示,该SoC系统实现的是一个8端口的以太网交换功能,其设计代码DUT对外的主要引脚包含1组AXI-Lite接口(用于寄存器配置)和8组AXI-Stream接口(用于以太网收发包)。验证的目的就是DUT实现了预期的交换功能。
图6 某SoC系统UVM验证平台总体结构
在该UVM验证平台中,包含了两个层次:testbench和testcase。testbench中包含了每个testcase的公有部分。为了与DUT的顶层信号对接,在该SoC系统UVM验证平台的testbench中,共设计了8个用于收发包的port_i(i=0,1,…,7)和1个用于配置寄存器的axilite。除此之外,testbench中还包含virtual sequencer和scoreboard。在该UVM验证平台的每个testcase中,都对应两类trasnaction:一类是与AXI-Lite配置寄存器相关的axi_trans,包含地址、数据和操作类型(读/写)三个字段;另一类transaction是与物理层收发包相关的以太网包packet_trans,它包括目的地址、源地址、帧类型、包长度、帧数据、CRC校验等字段。axilite:与DUT的寄存器配置相关,内部包含driver、monitor和sequencer。其中,driver根据AXI-Lite协议完成axi_trans到AXI-Lite读写时序的转换;monitor监测验证平台施加给DUT的axi_trans,并将它们传送给scoreboard;sequencer与配置寄存器的sequence相关联;
port_i(i=0,1,…,7):与每个port的收发包相关,内部包含driver、monitor和sequencer。其中,driver根据AXI-Stream协议完成packet_trans到AXI-Stream读写时序的转换;monitor通过监测DUT的输入输出AXI-Stream接口信号,将它们转换为发送包/接收包的packet_trans,并将它们传送给scoreboard;sequencer与收发包的sequence相关联;
virtual sequencer:用于调度管理axilite和port_i中的sequencer,内部包含多个sequencer实例。在一个测试用例中,virtual sequencer与一个virtual sequence相关联;
scoreboard:根据axilite中monitor收集的配置信息和各个port_i收集的发包信息,计算出预期的收包信息,再与monitor实际收到的收包信息进行自动对比,输出对比结果。注意到在该验证平台中,我们没有单独使用reference model,因为我们已经把reference model的功能内嵌到scoreboard之中。
测试用例的编写与sequence密切相关,每个测试用例对应一个virtual sequence。根据验证平台的结构,virtual sequence与各sequence的调用关系如图7所示,它将不同的sequence加载到对应的sequencer实例之上。图中以cseq_xxx开头的sequence都与寄存器配置相关,而tseq_xxx开头的sequence都与发送包相关。在每个测试用例中,一般来说,在发送包之前需要完成相关寄存器的配置,例如配置路由表。
图7 virtual sequence的调用关系
因此,基于上述UVM验证平台,编写测试用例的基本步骤为:创建测试用例的cseq_xxx,通过对axi_trans的地址、数据、操作类型字段施加约束可完成相关寄存器的配置;
创建测试用例的tseq_xxx,通过对packet_trans的帧类型、包长度等包格式字段施加约束可以发送特定的包;
创建测试用例的virtual sequence,在virtual sequence中进行调度,将sequence加载到具体端口的sequencer实例之上;
创建测试用例的测试类(testclass),在testclass中指定它与新创建的virtual sequence的类名相关联;
在仿真脚本中指定UVM_TEST_NAME的值为新创建的testclass类名。启动仿真,即可对该测试用例进行验证。
基于UVM验证方法学可以快速构建层次化、可扩展性强的验证平台。本文简要介绍了UVM验证平台的基本框架和测试用例的编写步骤,对UVM中的代码细节并未过多提及。后续将会介绍UVM中的寄存器模型,敬请期待。