搭建一个人UVM测试平台

用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 是否功能正确。搭建一个人UVM测试平台_第1张图片
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_INTERFACEdefine 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__SVdefine 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 endtaskendif
//==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
d i s p l a y ( " o u t p u t d a t a i s display("out put data is %h @ %d ns", itf.odata, display("outputdataistime);
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_SCOREBOARDdefine 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

参考文章:

【1】http://bbs.eetop.cn/thread-861994-1-1.html

你可能感兴趣的:(搭建一个人UVM测试平台)