VERDVANA'S BLOG Verdvana

UVM入門


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

        告辞