在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 2241|回复: 4

搭建一个人UVM测试平台

[复制链接]
发表于 2019-6-30 15:00:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
用UVM 搭建一个简单的测试环境, 整体环境的框架如下图。首先sequence 产生sequence_tem(也就是数据包),然后发送给sequencer, sequencer 通过seq_item_export 和driver 的seq_item_port 进行数据的传输。
然后driver按照协议要求把数据通过interface 发送给DUT, 同时driver 也会通过uvm_analysis_port把数据发送给scoreboard. scoreboard 会计算出期望的结果。
最后和monitor(下图的receiver)传输过来的结果进行对比,从而判断出DUT 是否功能正确。

                               
登录/注册后可看大图


1,写一个fifo,新建一个fifo.v 的文件,作为我们的dut,内容如下:
//                              -*- Mode: Verilog -*-
// Filename        : fifo.v
// Description     : a simple fifo, test it use uvm
// Author          : zhuzhiqi
// Created On      : Sat Jun 29 10:48:23 2019
// Last Modified By:
// Last Modified On: Sat Jun 29 10:48:23 2019
// Update Count    : 0
// Status          : Unknown, Use with caution!
module fifo (/*AUTOARG*/
   // Outputs
   full, empty, odata,
   // Inputs
   clk, reset_n, ivlid, ordy, idata
   );
   parameter DATA_WIDTH = 8;
   parameter DEPTHBITS  = 3;
   localparam DEPTH = 2**DEPTHBITS;

   input clk;
   input reset_n;
   input ivlid;
   input ordy;
   output full;
   output empty;
   input [DATA_WIDTH-1:0] idata;
   output [DATA_WIDTH-1:0] odata;
   wire resetn = reset_n;
   //wires and regs
   logic [DEPTHBITS:0] wptr,rptr,n_wptr,n_rptr;
   wire                 i_wen;
   wire                 o_ren;
   logic [DEPTH-1:0][DATA_WIDTH-1:0] mem;
   //fifo logic
   assign full = wptr[DEPTHBITS]!=rptr[DEPTHBITS] && (wptr[DEPTHBITS-1:0]==rptr[DEPTHBITS-1:0]);
   assign empty = wptr == rptr;
   assign i_wen = ~full && ivlid;
   assign o_ren = ~empty && ordy;
   always @(posedge clk or negedge resetn) begin
      if (~resetn) begin
        wptr <= `DLY 'd0;
      end
      else if(i_wen) begin
        wptr <= `DLY wptr + 1'd1;
      end
   end
   always @(posedge clk or negedge resetn) begin
      if (~resetn) begin
         rptr <= `DLY 'd0;
      end
      else if(o_ren) begin
         rptr <= `DLY rptr + 1'd1;
      end
   end
   always @(posedge clk) begin
      if (i_wen) begin
        mem[wptr[DEPTHBITS-1:0]] <= `DLY idata;
      end
   end
   assign odata = mem[rptr[DEPTHBITS-1:0]];

endmodule: fifo
// Local Variables:
// verilog-library-directories"./" )
// verilog-auto-inst-param-value:t
// End:


2, 新建fifo_interface.sv 内容如下, 这是dut 和 tb 的接口:
`ifndef GUARD_INTERFACE
`define GUARD_INTERFACE


//////////////////////////////////////////
// Interface declaration for the fifo///
//////////////////////////////////////////

interface fifo_interface(input clk,input reset_n);
   parameter DATA_WIDTH = 8;
   logic  ordy;
   logic  ivlid;
   logic  full;
   logic  empty;
   logic [DATA_WIDTH-1:0] idata;
   logic [DATA_WIDTH-1:0] odata;

    clocking cb@(posedge clk);
       default output `DLY;
       output  ivlid;
           output  ordy;
           input   full;
           input   empty;
           output  idata;
       input   odata;
    endclocking:cb

    modport test(clocking cb,input clk,input reset_n);

        modport dut(input clk,input reset_n, input ivlid, ordy, idata, output full, empty, odata);
endinterface :fifo_interface

`endif

3,下面开始写我们的tb 部分tb.sv, 内容如下:



//                              -*- Mode: Verilog -*-
// Filename        : tb.v
// Description     : uvm testbench for fifo
// Author          : zhuzihqi
// Created On      : Sat Jun 29 11:09:50 2019
// Last Modified By:
// Last Modified On: Sat Jun 29 11:09:50 2019
// Update Count    : 0
// Status          : Unknown, Use with caution!
//3.1.1 inculde UVM package
`include "uvm_macros.svh"
`include "uvm_pkg.sv"
import uvm_pkg::*;
module top (/*AUTOARG*/);
   parameter DATA_WIDTH = 8;
   parameter DEPTHBITS = 4;

//uvm macros and pkg

//3.1.2 声明并例化interface 以及dut  
   logic clk;
   logic reset_n;
   //interface and dut
   fifo_interface i_itf (.clk(clk),.reset_n(reset_n));

   fifo #(/*AUTOINSTPARAM*/
          // Parameters
          .DATA_WIDTH                        (DATA_WIDTH),
          .DEPTHBITS                        (DEPTHBITS))
   i_fifo (/*AUTOINST*/
           // Outputs
           .full                        (i_itf.full),                 // Templated
           .empty                        (i_itf.empty),                 // Templated
           .odata                        (i_itf.odata[DATA_WIDTH-1:0]), // Templated
           // Inputs
           .clk                                (clk),                         // Templated
           .reset_n                        (reset_n),                 // Templated
           .ivlid                        (i_itf.ivlid),                 // Templated
           .ordy                        (i_itf.ordy),                 // Templated
           .idata                        (i_itf.idata[DATA_WIDTH-1:0])); // Templated

//test logic

///clock and rest_n
   initial begin
      clk = 0;
      reset_n = 0;
      #20;
      reset_n = 1;

   end
   always #1 clk = ~clk;
//uvm start
   //==========uvm transaction, the data packets===========
class fifo_data extends uvm_sequence_item;
   rand bit[DATA_WIDTH-1:0] data;
   rand bit dvalid;
        `uvm_object_utils_begin(fifo_data)
                `uvm_field_int(data, UVM_ALL_ON|UVM_NOPACK)
                `uvm_field_int(dvalid, UVM_ALL_ON|UVM_NOPACK)  
        `uvm_object_utils_end
       
endclass
  //============uvm sequence, control the generation of packets============
class my_sequence extends uvm_sequence#(fifo_data);
   `uvm_object_utils(my_sequence)
   function new(string name = "my_sequence");
      super.new(name);
   endfunction

    virtual task pre_body();
        if (starting_phase != null)
            starting_phase.raise_objection(this, {"Running sequence '", get_full_name(), "'"});

    endtask

    virtual task post_body();
        if (starting_phase != null)
            starting_phase.drop_objection(this, {"Completed sequence '", get_full_name(), "'"});
    endtask

    virtual task body();
          while(1) begin
            req = fifo_data::type_id::create("req");
            //start_item(req);
            //assert(req.randomize());   
            //finish_item(req);
            `uvm_do(req); //realize them same function above
                        //there are many other methods to send reqs
                        //for example `uvm_do_with...
          end
    endtask
endclass  

//=========uvm sequencer, control the start of sequence, and pass sequece to driver==========
class my_sequencer extends uvm_sequencer#(fifo_data);
  `uvm_component_utils(my_sequencer)
  function new(string name = "my_sequencer", uvm_component parent = null);  
      super.new(name, parent);
   endfunction
endclass

//====================
//======uvm driver====
//====================
`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV

class my_driver extends uvm_driver#(fifo_data);
  `uvm_component_utils(my_driver) //regist it in factory
   //my_packet tr;
   virtual  fifo_interface itf;
   uvm_analysis_port #(fifo_data) Drvr2Sb_port; //pass the data to score board
   function new(string name = "my_driver", uvm_component parent = null);  
      super.new(name, parent);
          
   endfunction
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual function void connect_phase(uvm_phase phase);
   //main phase have many sub phase, run_phase is one of them.
   //only run_phase can consume time   
   //extern virtual task main_phase(uvm_phase phase);
   extern virtual task run_phase(uvm_phase phase);
   extern task rec_data();
   extern task send_data();
endclass  

function void my_driver:: build_phase(uvm_phase phase);
      super.build_phase(phase);
      Drvr2Sb_port = new("Drvr2Sb", this);          
      `uvm_info("my_driver", "build_phase is called", UVM_LOW);  
           //uvm_config_db#(virtual fifo_interface)::get(this, "", "vitf", itf);
       if(!uvm_config_db#(virtual fifo_interface)::get(null, "my_agent", "itf", itf))  
                        `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfunction
//connect interface
function void my_driver::connect_phase(uvm_phase phase);
     super.connect_phase(phase);
              
endfunction
//task my_driver::main_phase(uvm_phase phase);
task my_driver::send_data();
   itf.ivlid = 'b0;  
   while(!itf.reset_n)  
      @(posedge itf.clk);  
   //receive data from sequencer
   for(int i = 0; i < 20; i++)begin  
      seq_item_port.get_next_item(req);
      @(posedge itf.clk);  
      itf.idata   <= req.data;  
      itf.ivlid   <= req.dvalid;
      //wait data received if data valid
      if(req.dvalid) begin
            Drvr2Sb_port.write(req); //write it to scorboad
                wait (~itf.full);
          end
          `uvm_info("my_driver", "data is drived", UVM_LOW)
      //finish data consume
          seq_item_port.item_done();
   end
endtask
task my_driver::rec_data();
   itf.ordy = 'b0;  
   while(!itf.reset_n)  
      @(posedge itf.clk);
   while(1) begin
      @(posedge itf.clk);
            itf.ordy <= {$random()} % 2;
   end
endtask
task my_driver::run_phase(uvm_phase phase);  
   super.run_phase(phase);
   phase.raise_objection(this);//<================= if no objection UVM will stop this phase imidiately
   fork
     send_data();
         rec_data();
   join_any       
   //wait fifo empty
   wait(itf.empty);   
   phase.drop_objection(this);//<=================, finish the simulation
endtask  
`endif  
//======uvm monitor =========
class my_monitor extends uvm_monitor;
    virtual  fifo_interface itf;
    fifo_data r_data;
        uvm_analysis_port #(fifo_data) fifo_out;

    `uvm_component_utils(my_monitor)

    // This is standard code for all components
    function new (string name = "my_monitor", uvm_component parent = null);
      super.new (name, parent);
    endfunction

        extern virtual function void build_phase(uvm_phase phase);
    extern virtual function void connect_phase(uvm_phase phase);
    extern virtual task run_phase(uvm_phase phase);
endclass
function void my_monitor::build_phase(uvm_phase phase);
    super.build_phase(phase);
        fifo_out = new("monitor2Sb", this);
        //uvm_config_db#(virtual fifo_interface)::get(this, "", "vitf", itf);
        if(!uvm_config_db#(virtual fifo_interface)::get(null, "my_agent", "itf", itf))  
                        `uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
endfunction
function void my_monitor::connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    //assert(uvm_config_db#(virtual  fifo_interface)::get(this, "", "vitf", itf));
endfunction

task my_monitor::run_phase(uvm_phase phase);
   super.run_phase(phase);
   @(itf.reset_n)
    `uvm_info(get_full_name(),"reset done!!",UVM_LOW);
    forever begin
     @(posedge itf.clk iff itf.reset_n &&itf.ordy && (~itf.empty)) begin
         $display("out put data is %h @ %d ns", itf.odata,$time);
                 r_data   = fifo_data::type_id::create("req");
                 r_data.data  = itf.odata;
                 r_data.dvalid = 1;
                 fifo_out.write(r_data);
         end
    end
endtask

//======uvm score board ===========
`ifndef GUARD_SCOREBOARD
`define GUARD_SCOREBOARD

`uvm_analysis_imp_decl(_rcvd_pkt)
`uvm_analysis_imp_decl(_sent_pkt)

class Scoreboard extends uvm_scoreboard;
    `uvm_component_utils(Scoreboard)

    fifo_data exp_que[$];

    uvm_analysis_imp_rcvd_pkt #(fifo_data,Scoreboard) Rcvr2Sb_port;
    uvm_analysis_imp_sent_pkt #(fifo_data,Scoreboard) Drvr2Sb_port;

    function new(string name = "scb",uvm_component parent);
        super.new(name,parent);
        Rcvr2Sb_port = new("Rcvr2Sb", this);
        Drvr2Sb_port = new("Drvr2Sb", this);
    endfunction
    //receive the golden data from driver
    virtual function void write_sent_pkt(input fifo_data pkt);
        exp_que.push_back(pkt);
   endfunction : write_sent_pkt
   //receive golden data from monitor
    virtual function void write_rcvd_pkt(input fifo_data pkt);
        fifo_data exp_pkt;
      //  pkt.print();

        if(exp_que.size())
        begin
           exp_pkt = exp_que.pop_front();
      //     exp_pkt.print();
           if( pkt.compare(exp_pkt))
             uvm_report_info(get_type_name(), $psprintf("Sent packet and received packet matched"), UVM_LOW);
           else
             uvm_report_error(get_type_name(), $psprintf("Sent packet and received packet mismatched"), UVM_LOW);
        end
        else
             uvm_report_error(get_type_name(), $psprintf("No more packets to in the expected queue to compare"), UVM_LOW);
   endfunction : write_rcvd_pkt

   virtual function void report();
        uvm_report_info(get_type_name(),
        $psprintf("Scoreboard Report \n%s", this.sprint()), UVM_LOW);
   endfunction : report


endclass : Scoreboard

`endif
//now all components is ready connect them in env or agent
class my_agent extends uvm_agent;
   //virtual interface fifo_interface itf;
   my_driver    driver;
   my_sequencer sequencer;
   my_monitor   monitor;
   Scoreboard   Sbd;
  `uvm_component_utils(my_agent)
    function new(string name = "my_agent", uvm_component parent);
        super.new(name, parent);
    endfunction
    extern virtual function void build_phase(uvm_phase phase);
    extern virtual function void connect_phase(uvm_phase phase);
endclass

function void my_agent::build_phase(uvm_phase phase);
    super.build_phase(phase);
    sequencer = my_sequencer::type_id::create("sequencer",this);
    driver    = my_driver::type_id::create("driver",this);
    monitor   = my_monitor::type_id::create("monitor",this);
        Sbd       = Scoreboard::type_id::create("scoreboard",this);   

endfunction : build_phase

function void my_agent::connect_phase(uvm_phase phase);
    super.connect_phase(phase);
        //connect the communication port
    driver.seq_item_port.connect(sequencer.seq_item_export);
        driver.Drvr2Sb_port.connect(Sbd.Drvr2Sb_port);
    monitor.fifo_out.connect(Sbd.Rcvr2Sb_port);
endfunction : connect_phase

//uvm env
class my_env extends uvm_env;
  my_agent i_my_agent;
  `uvm_component_utils(my_env)
  function new (string name = "my_env", uvm_component parent = null);
    super.new (name, parent);
  endfunction
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
        i_my_agent = my_agent::type_id::create("i_my_agent",this);
        uvm_config_db#(uvm_object_wrapper)::set(this,"i_my_agent.sequencer.main_phase","default_sequence",my_sequence::type_id::get());
  endfunction
  //virtual function void connect_phase(uvm_phase phase);
endclass
//run unv test
my_env i_env;
initial begin

   i_env = my_env::type_id::create("i_my_agent",null);
   uvm_config_db#(virtual fifo_interface)::set(null, "my_agent", "itf", i_itf);
   run_test();

end
endmodule: top
tb 到这里就写完了

4, 调用仿真工具跑起来看一下结果:
4.1 新建一个sim_fifo.tcl,内容如下:
  #UVM verilog source HOME
set UVM_HOME     D:/modelsim/verilog_src/uvm-1.1d
#UVM DPI Home
set UVM_DPI_HOME D:/modelsim/uvm-1.1d/win64
vlib     work
vlog -sv  +define+DLY=#0.1 +incdir+$UVM_HOME/src -L mtiAvm -L mtiOvm -L mtiUvm -L mtiUPF \
+incdir+./UVM_FIFO ./UVM_FIFO/tb.v ./UVM_FIFO/fifo.v ./UVM_FIFO/interface.sv
vsim -c -voptargs=+acc -sv_lib $UVM_DPI_HOME/uvm_dpi +TESTNAME="fifo_test" -wlf fifo.wlf -t 1ns work.top
log /* -r
run
4.2 新建一个run.bat 内容如下:
vsim -do sim_fifo.tcl
4.3 双击run.bat,观察 运行结果
#Specify +UVM_NO_RELNOTES to turn off this notice)
#
# UVM_INFO @ 0: reporter [RNTST] Running test ...
# UVM_INFO ./UVM_FIFO/tb.v(148) @ 0: i_my_agent.i_my_agent.driver [my_driver] build_phase is called
# UVM_INFO ./UVM_FIFO/tb.v(233) @ 20: i_my_agent.i_my_agent.monitor [i_my_agent.i_my_agent.monitor] reset done!!
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 23: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 25: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 27: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is 49 @                   27 ns
# UVM_INFO @ 27: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 29: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 31: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is 8b @                   31 ns
# UVM_INFO @ 31: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 33: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 35: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 37: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 39: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 41: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is a7 @                   41 ns
# UVM_INFO @ 41: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 43: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is 5f @                   43 ns
# UVM_INFO @ 43: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 45: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 47: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is 59 @                   47 ns
# UVM_INFO @ 47: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 49: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is dc @                   49 ns
# UVM_INFO @ 49: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 51: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 53: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is 2e @                   53 ns
# UVM_INFO @ 53: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 55: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 57: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is da @                   57 ns
# UVM_INFO @ 57: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 59: i_my_agent.i_my_agent.driver [my_driver] data is drived
# UVM_INFO ./UVM_FIFO/tb.v(174) @ 61: i_my_agent.i_my_agent.driver [my_driver] data is drived
# out put data is 7f @                   61 ns
# UVM_INFO @ 61: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# out put data is 52 @                   63 ns
# UVM_INFO @ 63: i_my_agent.i_my_agent.scoreboard [Scoreboard] Sent packet and received packet matched
# UVM_INFO ./UVM_FIFO/tb.v(197) @ 63: i_my_agent.i_my_agent.driver [i_my_agent.i_my_agent.driver] fifo is enpty simulation stop!!
# UVM_INFO D:/modelsim/verilog_src/uvm-1.1d/src/base/uvm_objection.svh(1268) @ 63: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase


5, 更多UVM 的教程可以参考www.testbench.in 这里不仅有详细的教程,还有实际的例子,以及源代码
发表于 2020-1-8 17:50:03 | 显示全部楼层
感谢感谢,收藏,有机会一定多学学uvm
发表于 2020-1-9 11:21:59 | 显示全部楼层
mark,感谢分享
发表于 2020-1-9 21:02:46 | 显示全部楼层
see see
发表于 2020-5-13 16:16:57 | 显示全部楼层
张强有一本讲UVM验证方法学的书,带了一套简易的UVM环境,跟着书进行调试就可以了,也很好用;
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-11-21 20:40 , Processed in 0.020214 second(s), 6 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表