|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
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 这里不仅有详细的教程,还有实际的例子,以及源代码
|
|