基于简单DUT的UVM验证平台的搭建(一)

最近一个月在实习公司做回归测试,对公司的UVM平台用的比较熟练,就想着自己做一个DUT,然后搭建一个UVM验证平台。

首先,DUT是一个简单的32位的加法器,代码如下:alu.v

module adder32_cla(
  input         clk     ,
  input         rst     ,
  input         enable  ,
  input  [31:0] a       ,
  input  [31:0] b       ,
  input         cin     ,
  output [31:0] sum_r   ,
  output        cout_r  
);

  reg [31:0] sum_r  = 32'h00000000 ;
  reg        cout_r = 1'h0  ;

always @(posedge clk or negedge rst) 
  begin
    if (!rst)
      begin
        sum_r  = 32'h00000000  ;
        cout_r =  1'h0          ;
      end
    else if(enable)
      begin 
        {cout_r,sum_r} <= a + b + cin;              
      end
    else
      begin               
        sum_r  <= sum_r  ;
        cout_r <= cout_r ;
      end                                   
  end    
  
endmodule
View Code

UVM验证组件:

1、top.sv

`timescale 1ns/1ns

`include "pkg.sv"
`include "alu.v"

module top();

import uvm_pkg::*;
`include "uvm_macros.svh"    
my_if my_my_if();
     
adder32_cla inst1 
(
  .clk    (my_my_if.clk    ),
  .rst    (my_my_if.rst    ),
  .enable (my_my_if.enable ),
  .a      (my_my_if.a      ),
  .b      (my_my_if.b      ),
  .cin    (my_my_if.cin    ),
  .sum_r  (my_my_if.sum_r  ),
  .cout_r (my_my_if.cout_r )
);
initial  
  begin
         my_my_if.cin    = 1'b0         ;
         my_my_if.a      = 32'h00000000 ;
         my_my_if.b      = 32'h00000000 ;
         my_my_if.enable = 1'b1         ;
         my_my_if.rst    = 1'b0         ;
    #100 my_my_if.rst    = 1'b1         ;
  end
initial
  begin
         my_my_if.clk   <= 1'b0        ;
        
    #500 my_my_if.clk   <= 1'b0        ;
    #500 my_my_if.clk   <= 1'b1        ;
forever 
    #50  my_my_if.clk   = ~my_my_if.clk;
end
initial
  begin
    uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.i_agt.drv","my_if",my_my_if);
    uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.o_agt.mon","my_if",my_my_if);                         
    run_test();
  end    

endmodule 
View Code

top.sv主要的作用是实例化DUT,和输入输出的interface,并且定义了时钟频率,传递了接口一连接TB,和run_test();用于启动UVM phase。

2、my_test.sv

`ifndef MY_TEST__SV
`define MY_TEST__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_env.sv"
class my_test extends uvm_test;
  my_env env;
  extern function new(string name="my_test",uvm_component parent=null);
  extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_test)
endclass

function my_test::new (string name="my_test",uvm_component parent=null);
  super.new(name,parent);
endfunction
function void my_test::build_phase(uvm_phase phase);
   super.build_phase(phase);
   env = my_env::type_id::create("env");
endfunction

`endif
View Code

my_test.sv派生于uvm_test,属于基类。在项目中,主要采用基类构建框架,继承类实现具体动作的方式来增加灵活性。

3、my_env.sv

`ifndef MY_ENV__SV
`define MY_ENV__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_agent.sv"
`include "my_scoreboard.sv"
`include "my_model.sv"

class my_env extends uvm_env;
  my_agent       i_agt ;              //my_driver functional
  my_agent       o_agt ;              //my_monitor functional
  my_model       mdl   ;
  my_scoreboard  scb   ;
  
  uvm_tlm_analysis_fifo  #(my_transaction) agt_scb_fifo;   //my_monitor
  uvm_tlm_analysis_fifo  #(my_transaction) agt_mdl_fifo;   //my_driver
  uvm_tlm_analysis_fifo  #(my_transaction) mdl_scb_fifo;   //my_model
  extern function new (string name,uvm_component parent);
  extern virtual function void build_phase(uvm_phase phase);
  extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(my_env);
endclass

function my_env::new (string name,uvm_component parent);
super.new(name,parent);
endfunction

function void my_env::build_phase(uvm_phase phase);
super.build_phase(phase);
  i_agt = my_agent::type_id::create("i_agt",this);
  o_agt = my_agent::type_id::create("o_agt",this);
  i_agt.is_active = UVM_ACTIVE;
  o_agt.is_active = UVM_PASSIVE;
  mdl = my_model::type_id::create("mdl",this);
  scb = my_scoreboard::type_id::create("scb",this);
  agt_scb_fifo = new("agt_scb_fifo",this);
  agt_mdl_fifo = new("agt_mdl_fifo",this);
  mdl_scb_fifo = new(" mdl_scb_fifo",this);
endfunction

function void my_env::connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  i_agt.ap.connect(agt_mdl_fifo.analysis_export);
  mdl.port.connect(agt_mdl_fifo.blocking_get_export);
  mdl.ap.connect(mdl_scb_fifo.analysis_export);
  scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
  o_agt.ap.connect(agt_scb_fifo.analysis_export);
  scb.act_port.connect(agt_scb_fifo.blocking_get_export);

endfunction
`endif
View Code

my_env.sv的主要是agent scoreboard reference_model模块的实例化,和三个tlm_analysis_fifo,然后build_phase中type_id::create()模块,配置i/oagent,scoreboard model和new三个FIFO,之后connect_phase中,connect analysis_export和blocking_get_export。

4、my_transaction.sv

要传送的transaction

import uvm_pkg::*;
`include "uvm_macros.svh"    
`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV
class my_transaction extends uvm_sequence_item;
    rand bit        cin;
    rand bit [31:0] a,b;
    bit      [31:0] sum_r;
    bit             cout_r;
    
extern function new (string name="my_transaction");
    `uvm_object_utils_begin(my_transaction)
      `uvm_field_int(cin,UVM_ALL_ON)
      `uvm_field_int(a,UVM_ALL_ON)
      `uvm_field_int(b,UVM_ALL_ON)
      `uvm_field_int(sum_r,UVM_ALL_ON)
      `uvm_field_int(cout_r,UVM_ALL_ON)
    `uvm_object_utils_end

endclass

function my_transaction::new (string name="my_transaction");
  super.new(name);
endfunction
`endif
View Code

主要包括各种内容以及uvm_object_utils,uvm_field_int的注册,参数约束和随机化。

5、my_sequencer.sv

发送transaction的sequencer

`ifndef MY_SEQUENCER__SV
`define MY_SEQUENCER__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_transaction.sv"
`include "my_sequence.sv"

class my_sequencer extends uvm_sequencer #(my_transaction);
  extern function new (string name,uvm_component parent);
  extern virtual function void build_phase(uvm_phase phase); 
`uvm_component_utils(my_sequencer)
endclass

function my_sequencer::new (string name,uvm_component parent);
  super.new(name,parent);
endfunction

function void my_sequencer::build_phase(uvm_phase phase);
  super.build_phase(phase);
endfunction

`endif
View Code

负责把transaction类型的数据传送给sequence,里面内容很简单就一个注册和new函数。

6、my_sequence.sv

`ifndef MY_SEQUENCE__SV
`define MY_SEQUENCE__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_transaction.sv"
class  my_sequence extends uvm_sequence #(my_transaction); 
  my_transaction m_trans;
  `uvm_object_utils(my_sequence)
   extern function new(string name="my_sequence");
     virtual task body();
       if(starting_phase!=null)
         starting_phase.raise_objection(this);
         repeat(10)
         begin
           `uvm_do(m_trans)
         end
         #10000;
       if(starting_phase!=null)
         starting_phase.drop_objection(this);
     endtask
endclass

function  my_sequence::new(string name="my_sequence");
  super.new(name);
endfunction

`endif
View Code

sequence,用于产生激励,里面的objection机制用来控制验证平台的打开与关闭,需要在drop_objection之前先raise_objection。task_body中uvm_do(my_trans),在之前和之后要starting_phase.raise_objection(this)和starting_phase.drop_objection(this);

7、my_driver.sv

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_transaction.sv"

class my_driver extends uvm_driver #(my_transaction);     
      
     virtual my_if vif;//to DUT
uvm_analysis_port #(my_transaction) ap;//the data is to reference model

`uvm_component_utils(my_driver)


extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);

extern virtual task  main_phase(uvm_phase phase);
extern virtual task  drive_one_pkt(my_transaction req);
endclass

function my_driver::new (string name,uvm_component parent);
super.new(name,parent);
endfunction
  
function  void my_driver::build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get(this,"","my_if",vif))
`uvm_fatal("my_driver","Error in Getting interface");
ap=new("ap",this);

endfunction

task my_driver::main_phase(uvm_phase phase);
 my_transaction req;
super.main_phase(phase);
  
    while(1)
       begin 
       seq_item_port.get_next_item(req);
                     
        drive_one_pkt(req);
         ap.write(req);
      seq_item_port.item_done();
    
    end
endtask

task my_driver::drive_one_pkt(my_transaction req);
@vif.drv_cb;
    @vif.drv_cb
    begin
        vif.drv_cb.enable<=1'b1;
        vif.drv_cb.cin<=req.cin;
        vif.drv_cb.a<=req.a;
        vif.drv_cb.b<=req.b;
    end
        @vif.drv_cb;
    @vif.drv_cb vif.drv_cb.enable<=1'b0;

endtask

`endif
View Code

首先是virtual inf和build_phase中接受接口,否则fatal,然后main_phase中while(1)循环的get_next_item(req),调用发送函数发送req,返回item_done()。

8、my_monitor.sv

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"    
class my_monitor extends uvm_monitor;
  virtual my_if vif;
  uvm_analysis_port #(my_transaction) ap; //  to scoreboard
  extern function new (string name,uvm_component parent);
  extern virtual function void build_phase(uvm_phase phase);
  extern virtual task  main_phase(uvm_phase phase);
  extern virtual task  receive_one_pkt(ref my_transaction get_pkt);
`uvm_component_utils(my_monitor)
endclass

function my_monitor::new (string name,uvm_component parent);
  super.new(name,parent);
endfunction

function  void my_monitor::build_phase(uvm_phase phase);
  super.build_phase(phase);
  if(!uvm_config_db#(virtual my_if)::get(this,"","my_if",vif))
  `uvm_fatal("my_monitor","Error in Getting interface");
  ap=new("ap",this);
endfunction

task my_monitor::main_phase(uvm_phase phase);
  my_transaction tr;
  super.main_phase(phase);
  while(1) begin
    tr=new();
    receive_one_pkt(tr);
    ap.write(tr);
  end
endtask

task my_monitor::receive_one_pkt(ref my_transaction get_pkt);
  @(negedge vif.drv_cb.enable);
    get_pkt.cout_r=vif.mon_cb.cout_r;
    get_pkt.sum_r=vif.mon_cb.sum_r;
endtask
           
`endif
View Code

主要是virtual inf和analysis_port,在build_phase中接受接口,mian_phase中while(1)的调用接受函数和和ap.write(tr)。

9、my_agent.sv

`ifndef MY_AGENT__SV
`define MY_AGENT__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_transaction.sv"
`include "my_sequence.sv"
`include "my_sequencer.sv"
`include "my_driver.sv"
`include "my_monitor.sv"

class my_agent extends uvm_agent;
  my_sequencer sqr;
  my_driver drv;
  my_monitor mon;
  extern function new (string name,uvm_component parent);
  extern virtual function void build_phase(uvm_phase phase);
  extern virtual function void connect_phase(uvm_phase phase);
uvm_analysis_port #(my_transaction) ap;
`uvm_component_utils_begin(my_agent)
  `uvm_field_object(sqr,UVM_ALL_ON)
  `uvm_field_object(drv,UVM_ALL_ON)
  `uvm_field_object(mon,UVM_ALL_ON)
`uvm_component_utils_end
endclass

function my_agent::new (string name,uvm_component parent);
  super.new(name,parent);
endfunction

function void my_agent::build_phase(uvm_phase phase);
  super.build_phase(phase);
  if(is_active==UVM_ACTIVE )
    begin 
      sqr=my_sequencer::type_id::create("sqr",this);
      drv=my_driver::type_id::create("drv",this);
    end
    else begin
      mon=my_monitor::type_id::create("mon",this);
    end
endfunction          
 
function void my_agent::connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  
if(is_active==UVM_ACTIVE )
    begin 
      drv.seq_item_port.connect(sqr.seq_item_export);
      this.ap=drv.ap;
    end
    else begin
      this.ap=mon.ap;
    end
endfunction

`endif
View Code

含有driver和monitor,在build_phase中如果UVM_ACTIVE,就例化drv和sqr,但都例化mon,在build_phase中如果UVM_ACTIVE就链接drv和sqr的接口。

10、interface.sv

`ifndef MY_INTERFACE__SV
`define MY_INTERFACE__SV

interface my_if;
    logic             clk,rst   ;
    logic             cin        ; 
    logic [31:0]   a          ; 
    logic [31:0]   b          ;
    logic             enable   ; 
    wire              cout_r   ;
    wire  [31:0]   sum_r   ;
      
clocking drv_cb @(posedge clk);
    output enable,cin,a,b;
endclocking

clocking mon_cb @(posedge clk);
    input cout_r,sum_r;      
endclocking
      
endinterface
`endif
View Code

interface里面就是接口,用于连接DUT和driver和monitor。

12、my_model.sv

就是参考模型(reference_model)

`ifndef MY_MODEL__SV
`define MY_MODEL__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_transaction.sv"
class my_model extends uvm_component;
  uvm_blocking_get_port #(my_transaction)  port;   //from my_driver
  uvm_analysis_port  #(my_transaction)   ap;       //to scoreboard
  extern function new (string name,uvm_component parent);
  extern virtual function void build_phase(uvm_phase phase);
  extern virtual task  main_phase(uvm_phase phase);
  extern virtual task one_pkt(ref my_transaction pkt,ref my_transaction pkt2);
`uvm_component_utils(my_model)
endclass

function my_model::new (string name,uvm_component parent);
  super.new(name,parent);
endfunction

function void my_model::build_phase(uvm_phase phase);
  super.build_phase(phase);
  port=new("port",this);
  ap=new("ap",this);
endfunction

task my_model::main_phase(uvm_phase phase);
  my_transaction tr,tr2;
  super.main_phase(phase);
  while(1) begin
    tr2=new();
    port.get(tr);
    one_pkt(tr2,tr);
    ap.write(tr2);
  end
endtask

task my_model::one_pkt(ref my_transaction pkt,ref my_transaction pkt2);
  bit [32:0] sum_total;
  begin
    sum_total=pkt2.a+pkt2.b+pkt2.cin;
    pkt.sum_r=sum_total[31:0];
    pkt.cout_r=sum_total[32];
  end
endtask
`endif
View Code

需要进的blocking_get_port和出的analysis_port两个,在build_phase中连接他们,在main_phase中则while1的,如果get_port.get()收到,就ana_port.write()这个tr。

13、my_scoreboard.sv

`ifndef MY_SCOREBOARD__SV
`define MY_SCOREBOARD__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_transaction.sv"
class my_scoreboard extends uvm_scoreboard;
int pre_number=0;
my_transaction expect_queue[$];
uvm_blocking_get_port #(my_transaction) exp_port;//from my_reference
uvm_blocking_get_port #(my_transaction) act_port;//from my_monitor
`uvm_component_utils(my_scoreboard)
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task  main_phase(uvm_phase phase);
endclass

function my_scoreboard::new (string name,uvm_component parent);
  super.new(name,parent);
endfunction

function void my_scoreboard::build_phase(uvm_phase phase);
  super.build_phase(phase);
  exp_port=new("exp_port",this);
  act_port=new("act_port",this);
endfunction
task my_scoreboard::main_phase(uvm_phase phase);
  my_transaction get_expect,get_actual,tmp_tran;
  bit result;
  super.main_phase(phase);
fork
  while (1)
  begin
      exp_port.get(get_expect);
      expect_queue.push_back(get_expect);
  end
  while (1) 
  begin                   
    act_port.get(get_actual);
    if(expect_queue.size>0)begin
      tmp_tran=expect_queue.pop_front();
      result=get_actual.compare(tmp_tran);
      if(result)  begin
        pre_number=pre_number+1;
        $display("compare SUCCESSFULLy:%0d",pre_number);
      end
      else begin
        $display("compare FAILED");
        $display("the expect pkt is");  
        tmp_tran.print();
        $display("the actual pkt is");  
        get_actual.print();
      end
    end
    else  begin
      $display("ERROR::Received from DUT,while Expect Queue is empty");
      get_actual.print();
     end
  end
join

endtask                
                        
`endif
View Code

是两个get_port,在build_phase中new它们,首先对于exp端,while(1)的,只要get一个tr,就要在tr_queue中push进去一个,同时对于act端,while(1)的。只要get一个tr,就要从tr_queue中pop一个出来,进行比较。这个两个是fork join的,互不影响。

14、my_case0.sv

`ifndef MY_CASE0__SV
`define MY_CASE0__SV
import uvm_pkg::*;
`include "uvm_macros.svh"    
`include "my_test.sv"
  
class my_case0 extends  my_test;
  `uvm_component_utils(my_case0) 
  extern function new(string name="my_case0",uvm_component parent=null);
  extern virtual function void build_phase(uvm_phase phase);  
endclass

function my_case0::new (string name="my_case0",uvm_component parent=null);
  super.new(name,parent);
endfunction

function void my_case0::build_phase(uvm_phase phase);
  super.build_phase(phase);
  uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get());
endfunction

`endif
View Code

my_case0.sv扩展于my_test.sv,在my_case0.sv的build_phase中传递sequence。

至此,本UVM验证平台的各个组件就已经完成了,接下来有时间把Makefile脚本完善好上传,并在装有vcs和Verdi的虚拟机去运行,查看波形和查看覆盖率报告。

目前还差的东西是在top.sv里面加上生成波形的语句。

附录:pkt.v   导入整个验证平台

`ifndef MY_INTERFACE__SV
`define MY_INTERFACE__SV

`include "interface.sv"
`include "my_env.sv"
`include "my_test.sv"
`include "my_case0.sv"

`endif

欢迎讨论:QQ:447574829

posted @ 2019-07-31 22:29  Zhangxianhe  阅读(13158)  评论(3编辑  收藏  举报