从零开始,搭建一个简单的UVM验证平台(一)

前言:

        这篇系列将从0开始搭建一个UVM验证平台,来帮助一些学习了SV和UVM知识,但对搭建完整的验证环境没有概念的朋友。

UVM前置基础:

1.UVM基础-factory机制、phase机制

2.UVM基础-组件(driver、monitor、agent...)

3.UVM基础-TLM通信机制(一)

4.UVM基础-TLM通信机制(二)

...还在更新

从零搭建一个UVM验证平台:

从零开始,搭建一个简单的UVM验证平台(一)

从零开始,搭建一个简单的UVM验证平台(二)

从零开始,搭建一个简单的UVM验证平台(三)

从零开始,搭建一个简单的UVM验证平台(四)

...还在更新


目录

验证平台的组成

DUT编写(Design Under Test)

Driver搭建

factory机制

objection机制        


验证平台的组成

        ① 验证平台要模拟DUT的各种真实使用情况,这意味着要给DUT施加各种激励,激励的功能是由driver来实现的。

        ② 验证平台要能够根据DUT的输出来判断DUT的行为是否与预期相符合,完成这个功能的是计分板(scoreboard,在SV中被称为checker)。

        ③ 验证平台要收集DUT的输出并把它们传递给scoreboard,完成这个功能的是monitor。

        ④ 验证平台要能够给出预期结果。假设DUT是一个加法器,那么当在它的加数和被加数中分别输入1,即输入1+1时,期望DUT输出2。当DUT在计算1+1的结果时,验证平台也必须相应完成同样的过程,也计算一次1+1。在验证平台中,完成这个过程的是参考模型(reference model)。

        一个简单的验证平台框图如图所示。

DUT编写(Design Under Test)

        driver是验证平台最基本的组件,是整个验证平台数据流的源泉。

假设有一个DUT(design under test)被如下描述:

module dut(
  input             clk           , 
  input             rstn          ,
  input      [7:0]  data_i        ,
  input             data_i_valid  ,
  output reg [7:0]  data_o        ,
  output reg        data_o_valid
);

always @(posedge clk)begin
  if(!rstn)begin
    data_o       <= 8'd0;
    data_o_valid <= 1'b0;
  end
  else begin
    data_o       <= data_i;
    data_o_valid <= data_i_valid;
  end
end

endmodule 

        有如上一个DUT需要被我们验证,上面这个设计的功能非常简单,输入数据data_i和其数据有效信号data_i_valid,打一拍输出。data_o_valid是输出数据有效信号。

Driver搭建

        UVM是一个库,在这个库中所有的东西几乎都是用类(class)实现的。类是System verilog 面向对象编程语言中最伟大的发明之一,是面向对象的精髓。

        类有函数(function)和任务(task),通过这些函数和任务可以完成driver的输出激励功能、完成monitor的监测功能、完成参考模型的计算功能、完成scoreboard的比较功能。

        类的三要素封装、继承和多态,继承是类最总要的特性之一,我们在搭建验证环境时,要保证验证平台中所有的组件应该继承于UVM中的类。

        UVM验证平台中的driver应该派生自uvm_driver,一个简单的driver可以如下所示。

`include "uvm_macros.svh"
import uvm_pkg::*;

class my_driver extends uvm_driver;
  function new(string name = "my_driver", uvm_component 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_tb.data_i       <= 8'd0;
  top_tb.data_i_valid <= 1'b0;
  while(!top_tb.rstn)
    @(posedge top_tb.clk);
  for(int i = 0; i < 256; i = i+1)begin
    @(posedge top_tb.clk)
    top_tb.data_i <= $urandom_range(0,255);
    top_tb.data_i_valid <= 1'b1;
    `uvm_info("my_driver", "data is drived", UVM_LOW) 
  end
  @(posedge top_tb.clk);
  top_tb.data_i_valid <= 1'b0;
endtask

 

        这个driver的功能就是向data_i发送256个随机数据,同时将输入数据有效信号data_i_valid拉高,当数据发送完毕后,将data_i_valid信号拉低。在这个driver中有两点需要注意:

        ① 所有派生自uvm_driver的类的new函数有两个参数一个是string类型的name一个是uvm_component类型的parent。这两个参数是必不可少的是uvm_component所要求的。每一个派生自uvm_component或其派生的类在其new函数中需要指明两个参数:name和parent,这是uvm_componet类的一大特征。uvm_driver是一个派生自uvm_component的类,所以自然也会有name和parent两个参数。

        ② driver所做的事几乎都在main_phase中完成。UVM由phase来管理验证平台的运行,这些phase统一咦xxxx_phase来命名,且都有一个类型为uvm_phase、名字为phase的参数。main_phase是uvm_driver中预先定义好的一个任务。因此几乎可以简单地认为,实现一个driver等于实现其main_phase。

        上图为一个完整的phase全过程。 

        driver中还出现了uvm_info宏(除此之外还有uvm_error宏和uvm_warning宏),它有三个参数第一个参数是字符串,用于打印的信息归类;第二个参数也是字符串,是具体需要打印的信息;第三个参数是冗余级别,表示这个命令的紧急程度。针对第三点,在验证平台中,某些信息是非常关键的,这样的信息可以设置为UVM_LOW,而有些信息可有可无就可以设置为UVM_HIGH,介于两者之间则是UVM_MEDIUM。UVM默认只显示UVM_MEDIUM或者UVM_LOW的信息。

        uvm_info宏非常强大,它包含了打印信息的物理文件来源、逻辑节点信息(在UVM树种的路径索引)、打印时间、对信息的分类组织及打印的信息。因此在搭建验证平台时应该尽量使用uvm_info宏取代display语句。

        定义了my_driver类后还需要将其实例化。类的定义和类的实例化是存在区别的,类的定义是class-end块,定义一个类,告诉类有哪些功能。而类的实例化是使用new()函数来创建一个实例。

class A;
...
endclass

A a_inst;
a_inst = new();

        仿真器接到new的指令后,就会在内存中划分出一块空间,在划分前,会首先检查是否已经预先定义过这个类,在已经定义过的情况下,按照定义中所指示的“条文”分配空间,并且把这块空间的指针返回给a_inst,之后就可以通过a_inst来查看类中的各个成员变量,调用成员函数/任务等。对于大部分类来说,如果只定义而不实例化,是没有任何意义的;而如果不定义就直接实例化,仿真器就会报错。

        对my_driver实例化并且最终搭建的验证平台如下:        

`timescale 1ns/1ps
`include "uvm_macros.svh" //这是UVM中的一个文件,包含了众多宏定义

import uvm_pkg::*;        //只有导入了这个库,编译器在编译my_driver.sv文件时才会认识其中继承的uvm_driver等类名

`include "my_driver.sv"

module top_tb;

reg clk,rstn;
reg  [7:0] data_i;
reg  data_i_valid;
wire [7:0] data_o;
wire data_o_valid;

dut my_dut(
  .clk            (clk)           ,
  .data_i         (data_i)        ,
  .data_o         (data_o)        ,
  .data_i_valid   (data_i_valid)  ,
  .data_o_valid   (data_o_valid)
);

initial begin
  my_driver drv; // instance
  drv = new("drv", null);
  drv.main_phase(null);
  $finish();
end

initial begin
  clk = 0;
  forever begin
    #100 clk = ~clk;
  end
end

initial begin
  rstn = 1'b0;
  #1000
  rstn = 1'b1;
end

endmodule

仿真结果: 

 在仿真窗口中可以看到,data is drived 被输出了256次。

factory机制

        到这里我们实现了使用driver给DUT送激励的验证操作,但其实用简单的SV也能实现这样的功能,使用UVM的特性需要引入factory机制,实现:自动创建一个类的实例并调用其中的函数(function)和任务(task)。

在我们原来写的生成激励的driver.sv上做些修改:

`include "uvm_macros.svh"
import uvm_pkg::*;

class my_driver extends uvm_driver;
  `uvm_component_utils(my_driver)  //注册
  function new(string name = "my_driver", uvm_component parent = null);
    super.new(name, parent);
    `uvm_info("my_driver", "new is called", UVM_LOW)
  endfunction
  extern virtual task main_phase(uvm_phase phase);
endclass

task my_driver::main_phase(uvm_phase phase);
  `uvm_info("my_driver", "main phase is called", UVM_LOW);
  top_tb.data_i       <= 8'd0;
  top_tb.data_i_valid <= 1'b0;
  while(!top_tb.rstn)
    @(posedge top_tb.clk);
  for(int i = 0; i < 256; i = i+1)begin
    @(posedge top_tb.clk)
    top_tb.data_i <= $urandom_range(0,255);
    top_tb.data_i_valid <= 1'b1;
    `uvm_info("my_driver", "data is drived", UVM_LOW) 
  end
  @(posedge top_tb.clk);
  top_tb.data_i_valid <= 1'b0;
endtask

 

和之前的driver不同的是在class定义的中间加入了注册宏,uvm_component_utils(my_driver)

        引入了工厂机制之后,就可以自动化的运行类中的function和task,非常方便。在top_tb.sv文件中将my_driver的实例化和main_phase的调用替换为run_test("my_driver")

initial begin
    my_driver drv;
    drv = new("drv", null);
    drv.main_phase(null);
    $finish();
end

//将上述initial替换为下面的initial语句块

initial begin
  run_test("my_driver");
end

        之所以会自动运行是因为,在注册了工厂之后,run_test语句会自动创建一个my_driver的实例,并且会自动调用my_driver的main_phase。传递给run_test的是一个字符串,UVM会根据这个字符串创建了其所代表的一个实例。

        注意:所有派生自uvm_component及其派生类的类,注册的时候都应该使用uvm_componet_utils()宏注册。

        但从仿真的结果我们会发现,输出了main phase is called但是没有输出256次data is drived,而main_phase是一个完整的任务,没理由只执行第一句,而后面的代码不执行。这就牵扯到了UVM的objection机制。 

objection机制        

        在上面的代码中,我们并没有使用finish函数来终止验证平台,但验证平台确实是关闭了。在UVM中通过objection机制来控制验证平台的关闭。

        在每个phase中,UVM会检查是否有objection被提起(raise_objection),如果有,那么等待这个objection被撤销(drop_objection)后停止仿真;如果没有,则马上结束当前phase

        我们在前面使用工厂机制自动创建并执行main_phase的实例my_driver中没有提起objection,但是还是执行了my_driver的第一条语句

`uvm_info("my_driver", "main_phase is called", null);

        是不是和我们这里说的“如果没有提起objection,则马上结束当前phase”矛盾呢?其实不是的,这涉及到一个仿真时间片的概念,仿真器首先要进入到my_driver,才能检测本实例是否把objection提起,在进入my_driver的0时刻,宏定义uvm_info就已经执行了,与此同时,仿真器没检测到objection被提起,继而退出,这并不与uvm_info被执行排斥。

        加入objection机制在my_driver的main_phase首尾,在头部raise_objection,在task尾部,drop_objection。

修改my_driver.sv的代码为:

`include "uvm_macros.svh"
import uvm_pkg::*;

class my_driver extends uvm_driver;
  `uvm_component_utils(my_driver)  //注册
  function new(string name = "my_driver", uvm_component parent = null);
    super.new(name, parent);
    `uvm_info("my_driver", "new is called", UVM_LOW)
  endfunction
  extern virtual task main_phase(uvm_phase phase);
endclass

task my_driver::main_phase(uvm_phase phase);
  phase.raise_objection(this);
  `uvm_info("my_driver", "main phase is called", UVM_LOW);
  top_tb.data_i       <= 8'd0;
  top_tb.data_i_valid <= 1'b0;
  while(!top_tb.rstn)
    @(posedge top_tb.clk);
  for(int i = 0; i < 256; i = i+1)begin
    @(posedge top_tb.clk)
    top_tb.data_i <= $urandom_range(0,255);
    top_tb.data_i_valid <= 1'b1;
    `uvm_info("my_driver", "data is drived", UVM_LOW) 
  end
  @(posedge top_tb.clk);
  top_tb.data_i_valid <= 1'b0;
  phase.drop_objection(this);
endtask

在执行drop_objection语句之前,必须先调用raise_objection语句,这两者总是成对出现。

         加入了raise_objection和drop_objection之后我们会发现"data is drived"按照预期输出了256次。

另外再提一点,UVM检查是否提起objection,就是在进入main_phase的瞬间,因此,在进入main_phase之后就必须立刻提起objection,而不能经过任何带有延时的语句譬如@(posedge clk)或者 #1,但凡在raise_objection之前带有延时语句,那么这个objection就不会被检测到,进而导致main_phase立即退出。

        至此,我们的一个仅含有driver的验证平台就搭建完了,到此为止我们总共涉及了几个知识点:类的继承与派生、factory工厂机制、phase机制、objection机制。后面我们还会在这基础之上添加virtual interface、添加 transaction、monitor组件、agent组件以及sequence组件等等。

  • 34
    点赞
  • 247
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在搭建UVM验证平台之前,您需要了解以下内容:UVM语法和结构,UVM验证框架,UVM注册机制,UVM构建和配置方法,UVM测试库,UVM系统级验证UVM统计测试,UVM随机测试,以及UVM文件格式。接下来,你需要使用UVM API来构建一个基本验证环境,然后进行测试和验证结果的记录和分析。最后,你需要根据实际需要对验证平台进行调整和优化,以满足你的验证要求。 ### 回答2: 搭建一个UVM(Universal Verification Methodology)验证平台可以帮助我们有效地进行硬件验证。下面是搭建UVM验证平台的一些建议。 首先,一个UVM验证平台需要一个良好的环境设置。这包括设置合适的操作系统和相应的验证工具。验证工具可以是仿真器(如ModelSim、VCS等)和波形查看器(如DVE、Verdi等)等。同时需要安装UVM库,它包含了许多UVM验证环境所需的类和函数。 其次,我们需要定义测试环境和验证组件。测试环境是一个UVM对象,它包含了验证环境中的各个组件,如顶级模块、输入接口、输出接口等。验证组件是属于测试环境的子对象,它们有不同的功能,如驱动器(driver)、监视器(monitor)、代理(agent)等。每个验证组件负责特定的任务,并且彼此之间可以通过消息传递进行交互。 接下来,我们需要编写测试用例和配置文件。测试用例是验证平台的核心,它们用于模拟各种场景和情况,以测试我们设计的硬件是否符合预期的行为。配置文件则用于配置验证环境和测试组件的参数和属性。 然后,我们需要编写测试启动脚本。测试启动脚本用于指定我们要运行的测试用例以及其他必要的设置,如仿真时间、波形文件保存位置等。 最后,我们可以运行仿真来验证我们的设计。在仿真过程中,UVM验证平台将执行我们编写的测试用例,并通过各个验证组件和消息传递机制来收集和分析波形数据。 总之,搭建一个UVM验证平台需要正确安装验证工具和UVM库,定义测试环境和验证组件,编写测试用例和配置文件,以及编写测试启动脚本。这样,我们就可以使用UVM验证平台验证我们的硬件设计。 ### 回答3: 要搭建一个UVM验证平台,需要以下步骤: 1. 首先,确定验证平台的需求和目标,包括验证环境、测试用例、复用性需求和时间限制等。 2. 设计验证环境。选择适合验证目标的硬件平台和工具。设计验证环境包括顶层模块、测试控制器、驱动器、监控器和代理等。 3. 编写测试用例。根据验证目标和需求编写测试用例,包括功能测试、边界测试、性能测试等。测试用例应涵盖各种正常和异常情况。 4. 实现UVM验证组件。根据验证环境设计和测试用例编写UVM验证组件,包括顶层模块的连接、各个组件的功能实现和接口设置等。 5. 集成和运行验证环境。将验证组件连接到验证环境中,并进行集成测试。确保各个组件的正确互连和功能正常。 6. 运行测试用例。在验证平台上运行测试用例,并收集和分析测试结果。根据测试结果进行调试和修复。 7. 持续改进和优化。根据使用过程中发现的问题和需求,不断改进和优化验证平台,提高验证效率和质量。 8. 文档和培训。及时编写和更新验证平台的文档,包括使用手册、API文档等。培训团队成员,使其熟悉验证平台的使用和开发流程。 总之,搭建一个UVM验证平台需要确定需求、设计验证环境、编写测试用例、实现验证组件、集成和运行验证环境,持续改进和优化,并进行文档和培训。这样可以提高验证效率和质量,有效降低验证成本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃葱的酸菜鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值