UVM入門
15 Jan 2020 2751字 10分 次 Digital IC Verification打赏作者 CC BY 4.0 (除特别声明或转载文章外)
1 前言
在基於SystemVerilog的验证方法学中,目前市面上有三种:
- VMM(Verification Methodology Manual),这是Synopsys在2006年推出的,在初期是闭源的。当OVM出现后,面对OVM的激烈竞争,VMM开源了。VMM中集成了寄存器解决方案RAL。
- OVM(Open Verification Methodology),这是Cadence和Mentor在2008年推出的,从一开始就开源的。它引进了factory机制,功能非常强大,但是它里面没有寄存器解决方案,这是它最大的短板。针对这一情况,Cadence推出了RGM,补上了这一短板。只是很遗憾的是,RGM并没有成为OVM的一部分,要想使用RGM,需要额外下载。现在OVM已经停止更新,完全被UVM代替。
- UVM(Universal Verification Methodology),其正式版是在2011年2月由Accellera推出的,得到了Synopsys、Mentor和Cadence的支持。UVM几乎完全继承了OVM,又采纳了Synopsys在VMM中的寄存器解决方案RAL。同时,UVM还吸收了VMM中的一些优秀的实现方法。可以说,UVM继承了VMM和OVM的优点,克服了各自的缺点,代表了验证方法学的发展方向。
2 搭建一个简单的验证平台
2.1 验证平台的组成
验证用于找出DUT中的bug,这个过程通常是把DUT放入一个验证平台中来实现的。一个验证平台要实现如下基本功能:
- 验证平台要模拟DUT的各种真实使用情况,这意味着要给DUT施加各种激励,有正常的激励,也有异常的激励。激励的功能是由drive实现的。
- 验证平台要能够根据DUT的输出来判断DUT的行为是否与预期相符合,完成这个功能的是记分板(scoreboard,也被称为shecker)。既然是判断,那么牵扯到两个方面:一是判断什么,需要把什么拿来判断,这里很明显是DUT的输出;二是判断的标准是什么。
- 验证平台要收集DUT的输出并把他们传递给scoreboard,完成这个功能的是monitor。
- 验证平台要能够给出预期结果。在记分板中提到了判断的标准,判断的标准通常就是预期。假设DUT是一个加法器,那么当在它的加数和被加数中分别输入1,即输入1+1时,期待DUT输出2。当DUT在计算1+1的结果时,验证平台也必须相应完成同样的过程,也计算一次1+1。在验证平台中,完成这个过程的是参考模型(reference model)。
一个简单的验证平台框图如下图:
在UVM中,引入了agent和sequence概念,因此UVM中验证平台的典型框图如下图:
2.2 只有driver的验证平台
driver是验证平台最基本的组件,是整个验证平台数据流的源泉。
假设有以下DUT定义:
module dut(
input clk,
input rst_n,
input [7:0] rxd,
input rx_dv,
output reg [7:0] txd,
output reg tx_en
);
always_ff@(posedge clk,negedge rst_n) begin
if(!rst_n)begin
txd <= '0;
tx_en <= '0;
end
else begin
txd <= rxd;
tx_en <= rx_dv;
end
end
endmodule
这个DUT的功能非常简单,通过rxd接收数据,再通过txd发送出去。
UVM是一个库,在这个库中,几乎所有的东西都是使用类(class)来实现的。driver、monitor、reference model、scoreboard等组成部分都是类。类是像SystemVerilog这些面向对象编程语言中最伟大的发明之一,是面向对象的精髓所在。类有函数(function),另外还可以有任务(task),通过这些函数和任务可以完成driver的输出激励功能,完成monitor的监测功能,完成参考模型的计算功能,完成scoreboard的比较功能。类中可以有成员变量,这些成员变量可以控制类的行为,如控制driver的行为等。当要实现一个功能时,首先应该想到的是从UVM的某个类派生出一个新的类,在这个新的类中实现所期望的功能。所以,使用UVM的第一条原则是:**验证平台中所有的组件应该派生自UVM中的类。
UVM验证平台中的driver应该派生自uvm_driver,一个简单的driver如下例:
class my_driver extends uvm_driver;
function new(string name = "my_driver",uvm_compoent parent = null);
super.new(name,parent);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver:main_phase(uvm_phase phase);
top_tp.rxd <= '0;
top_tp.rx_dv <= '0;
while(!top_tb.td)
@(posedge top_tb.clk);
for(int i = 0; i < 256; i++) begin
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0,255);
top_tb.rx_dv <= '1;
`uvm_info("my_driver","data is drived",UVM_LOW)
end
@(posedge top_tb.clk);
top_tb.rx_dv <= '0;
endtask
这个driver的功能非常简单,只是像rxd上发送256个随机数据,并将rx_dv信号置为高电平。当数据发送完毕后,将rx_dv信号置为低电平。在这个driver中,有两点应该引起注意:
- 所有派生自uvm_driver的类的new函数有两个参数,一个是string
告辞