(3)UVM验证平台搭建之driver

年轻人的第一个UVM验证平台搭建之driver

  • 最初版本
  • 引入factory机制、objection机制和vitual interface之后

最初版本

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
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.rxd <= 8'b0; 
   top_tb.rx_dv <= 1'b0;
   while(!top_tb.rst_n)
      @(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'b1;
      `uvm_info("my_driver", "data is drived", UVM_LOW)
   end
   @(posedge top_tb.clk);
   top_tb.rx_dv <= 1'b0;
endtask
`endif

这个driver向rxd上发送256个随机数据,并将rx_dv信号置为高电平。当数据发送完毕后,将rx_dv信号置为低电平。在这个driver中,有两点应该引起注意:
所有派生自uvm_driver的类的new函数有两个参数,一个是sring类型的name,一个是uvm_component类型的parent。 这两个参数是由uvm_component要求的,而uvm_driver是一个派生自uvm_component的类,所以也会有这两个参数。

而所谓类的实例化指的是通过new创造出A的一个实例。对大部分的类来说,如果只定义而不实例化,是没有任何意义的;对my driver实例化并且最终搭建的验证平台如下:

`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "my_driver.sv"

module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;

dut my_dut(.clk(clk),
           .rst_n(rst_n),
           .rxd(rxd),
           .rx_dv(rx_dv),
           .txd(txd),
           .tx_en(tx_en));

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

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

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

endmodule

引入factory机制、objection机制和vitual interface之后

my_driver的代码:

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
class my_driver extends uvm_driver;

   virtual my_if vif;

   `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

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      `uvm_info("my_driver", "build_phase is called", UVM_LOW);
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
         `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
   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);
   vif.data <= 8'b0; 
   vif.valid <= 1'b0;
   while(!vif.rst_n)
      @(posedge vif.clk);
   for(int i = 0; i < 256; i++)begin
      @(posedge vif.clk);
      vif.data <= $urandom_range(0, 255);
      vif.valid <= 1'b1;
      `uvm_info("my_driver", "data is drived", UVM_LOW);
   end
   @(posedge vif.clk);
   vif.valid <= 1'b0;
   phase.drop_objection(this);
endtask
`endif

factory机制可以自动创建一个类的实例并调用其中的函数(function)和任务(task)。factory机制的实现被集成在了一个宏中:uvm_component utils。这个宏可以将my_driver登记在UVM内部的一张表中,这张表是factory功能实现的基础。只要在定义一个新的类时使用这个宏,就相当于把这个类注册到了这张表中。

所有派生自uvm_component及其派生类的类都应该使用uvm_component utils宏注册。

在UVM验证平台中,只要一个类使用uvm_component_utils注册且此类被实例化了,那么这个类的main_phase就会自动被调用。

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

raise_objection语句必须在main_phase中第一个消耗仿真时间的语句之前。如$display语句是不消耗仿真时间的,这些语句可以放在raise_objection之前,但是类似@(posedge top.clk)等语句是要消耗仿真时间的。所以在其后使用raise_objection是无法起到作用的。

绝对路径的使用大大减弱了验证平台的可移植性。避免绝对路径的一个方法是使用宏:避免绝对路径的另外一种方式是使用interface。

定义了interface后,在top tb中实例化DUT时,就可以直接使用,interface定义如下:

interface my_if(input clk, input rst_n);

   logic [7:0] data;
   logic valid;
endinterface
class my driver extends uvm driver; 
	my_if drv_if; 
endclass

注:上面这样的使用方式是会报语法错误的,因为my_driver是一个类,在类中不能使用上述方式声明一个interface,只有在类似top_tb这样的模块(module)中才可以。在类中使用的是virtual interface

class my driver extends uvm driver; 

	virtual my_if vif;

UVM引进了config_db机制。在config_db机制中,分为set和get两步操作。所谓set操作,可以理解成是“寄信”,而get则相当于是“收信”。在top_tb中执行set操作:

initial begin
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end

引入了build_phase。与main_phase一样,在build phase中主要通过config_db的set和get操作来传递一些数据,以及实例化成员变量等。build_phase是一个函数phase,而main_phase是一个任务phase,build_phase是不消耗仿真时间的。build_phase总是在仿真时间($time函数打印出的时间)为0时执行。

top_tb代码如下:

`timescale 1ns/1ps
`include "uvm_macros.svh"

import uvm_pkg::*;
`include "my_if.sv"
`include "my_driver.sv"

module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;
my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);

dut my_dut(.clk(clk),
           .rst_n(rst_n),
           .rxd(input_if.data),
           .rx_dv(input_if.valid),
           .txd(output_if.data),
           .tx_en(output_if.valid));

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

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

initial begin
   run_test("my_driver");
end

initial begin
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end
endmodule

这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

你可能感兴趣的:(UVM验证方法学,systemverilog,verilog,芯片)