DUT为一个可读可写的存储器,RTL代码如下:
//------------------------------------------------------------------------
// Memory Model RTL - www.verificationguide.com
//------------------------------------------------------------------------
/*
-----------------
| |
addr ---->| |
| |------> rdata
| Memory Model |
wdata ---->| |
| |
-----------------
^ ^
| |
wr_en rd_en
-------------------------------------------------------------------------- */
module memory
#( parameter ADDR_WIDTH = 2,
parameter DATA_WIDTH = 8 ) (
input clk,
input reset,
//control signals
input [ADDR_WIDTH-1:0] addr,//输入地址
input wr_en,//读写使能
input rd_en,
//data signals
input [DATA_WIDTH-1:0] wdata,
output [DATA_WIDTH-1:0] rdata
);
reg [DATA_WIDTH-1:0] rdata;
//Memory
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];
//Reset
always @(posedge reset)
for(int i=0;i<2**ADDR_WIDTH;i++) mem[i]=8'hFF;
// Write data to Memory
always @(posedge clk)
if (wr_en) mem[addr] <= wdata;
// Read data from memory
always @(posedge clk)
if (rd_en) rdata <= mem[addr];
endmodule
首先定义sequence item
class mem_seq_item extends uvm_sequence_item;
//data and control fields 输入全部加rand
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
//Utility and Field macros,定义变量宏
`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr_en,UVM_ALL_ON)
`uvm_field_int(rd_en,UVM_ALL_ON)
`uvm_field_int(wdata,UVM_ALL_ON)
`uvm_object_utils_end
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
//constaint, to generate any one among write and read 约束
constraint wr_rd_c { wr_en != rd_en; };
endclass
定义sequence,sequence分为write sequence 和 read sequence,如下:
class mem_sequence extends uvm_sequence#(mem_seq_item);
`uvm_sequence_utils(mem_sequence,mem_sequencer)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
virtual task body();
req = mem_seq_item::type_id::create("req");
wait_for_grant();
req.randomize();
send_request(req);
wait_for_item_done();//可以用`uvm_do(req)代替
endtask
endclass
write sequence
class mem_wr_seq extends uvm_sequence#(mem_seq_item);
`uvm_object_utils(mem_wr_seq)
//Constructor
function new(string name = "mem_wr_seq");
super.new(name);
endfunction
virtual task body();
`uvm_do_with(req,{req.wr_en == 1;})
endtask
endclass
read sequence
class mem_rd_seq extends uvm_sequence#(mem_seq_item);
`uvm_object_utils(mem_rd_seq)
//Constructor
function new(string name = "mem_rd_seq");
super.new(name);
endfunction
virtual task body();
`uvm_do_with(req,{req.rd_en == 1;})
endtask
endclass
定义sequencer
class mem_sequencer extends uvm_sequencer#(mem_seq_item);
`uvm_component_utils(mem_sequencer)
//constructor
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
endclass
定义driver
class mem_driver extends uvm_driver #(mem_seq_item);
// Virtual Interface
virtual mem_if vif;//定义虚接口
`uvm_component_utils(mem_driver)
//uvm_analysis_port #(mem_seq_item) Drvr2Sb_port;
// Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))//获取接口
`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
//respond_to_transfer(req);
drive();
seq_item_port.item_done();
end
endtask : run_phase
// drive
virtual task drive();
req.print();
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
`DRIV_IF.addr <= req.addr;
if(req.wr_en) begin
`DRIV_IF.wr_en <= req.wr_en;
`DRIV_IF.wdata <= req.wdata;
//$display("\tADDR = %0h \tWDATA = %0h",req.addr,trans.wdata);
@(posedge vif.DRIVER.clk);
end
if(req.rd_en) begin
`DRIV_IF.rd_en <= req.rd_en;
@(posedge vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
req.rdata = `DRIV_IF.rdata;
// $display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
endtask : drive
endclass : mem_driver
定义monitor
class mem_monitor extends uvm_monitor;
// Virtual Interface
virtual mem_if vif;//定义虚接口
uvm_analysis_port #(mem_seq_item) item_collected_port;//分析端口
// Placeholder to capture transaction information.
mem_seq_item trans_collected;//定义sequence item 句柄
`uvm_component_utils(mem_monitor)
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
trans_collected = new();
item_collected_port = new("item_collected_port", this);//创建端口对象
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
@(posedge vif.MONITOR.clk);
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en);//等待使能变化
trans_collected.addr = vif.monitor_cb.addr;
if(vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en;
trans_collected.wdata = vif.monitor_cb.wdata;
trans_collected.rd_en = 0;
@(posedge vif.MONITOR.clk);
end
if(vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en;
trans_collected.wr_en = 0;
@(posedge vif.MONITOR.clk);
@(posedge vif.MONITOR.clk);
trans_collected.rdata = vif.monitor_cb.rdata;
end
item_collected_port.write(trans_collected);//将收集的sequence item发送出去
end
endtask : run_phase
endclass : mem_monitor
定义agent
class mem_agent extends uvm_agent;
//declaring agent components
mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;
// UVM automation macros for general components
`uvm_component_utils(mem_agent)
// constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(get_is_active() == UVM_ACTIVE) begin
driver = mem_driver::type_id::create("driver", this);
sequencer = mem_sequencer::type_id::create("sequencer", this);
end
monitor = mem_monitor::type_id::create("monitor", this);
endfunction : build_phase
// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
endclass : mem_agent
定义scoreboard
class mem_scoreboard extends uvm_scoreboard;
`uvm_component_utils(mem_scoreboard)
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
item_collected_export = new("item_collected_export", this);
endfunction: build_phase
// write
virtual function void write(mem_seq_item pkt);//定义write方法,用于monitor分析port端口的调用
$display("SCB:: Pkt recived");
pkt.print();
endfunction : write
// run phase
virtual task run_phase(uvm_phase phase);
--- comparision logic ---
endtask : run_phase
endclass : mem_scoreboard
定义environment
class mem_model_env extends uvm_env;
//---------------------------------------
// agent and scoreboard instance
//---------------------------------------
mem_agent mem_agnt;
mem_scoreboard mem_scb;
`uvm_component_utils(mem_model_env)
//---------------------------------------
// constructor
//---------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
//---------------------------------------
// build_phase - crate the components
//---------------------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase);
mem_agnt = mem_agent::type_id::create("mem_agnt", this);
mem_scb = mem_scoreboard::type_id::create("mem_scb", this);
endfunction : build_phase
//---------------------------------------
// connect_phase - connecting monitor and scoreboard port
//---------------------------------------
function void connect_phase(uvm_phase phase);
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);//连接monitor和scoreboard
endfunction : connect_phase
endclass : mem_model_env
定义test
class mem_model_test extends uvm_test;
`uvm_component_utils(mem_model_test)
mem_model_env env;
mem_sequence seq;
function new(string name = "mem_model_test",uvm_component parent=null);
super.new(name,parent);
endfunction : new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = mem_model_env::type_id::create("env", this);
seq = mem_sequence::type_id::create("seq");
endfunction : build_phase
task run_phase(uvm_phase phase);
phase.raise_objection(this);//在test中启动sequence
seq.start(env.mem_agnt.sequencer);
phase.drop_objection(this);
endtask : run_phase
endclass : mem_model_test
定义接口
interface mem_if(input logic clk,reset);
//---------------------------------------
//declaring the signals
//---------------------------------------
logic [1:0] addr;
logic wr_en;
logic rd_en;
logic [7:0] wdata;
logic [7:0] rdata;
//---------------------------------------
//driver clocking block
//---------------------------------------
clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;//对于driver来说,只有读入的数据为input,其余都为output
endclocking
//---------------------------------------
//monitor clocking block
//---------------------------------------
clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;//对于monitor来说,全部的数据都为input
endclocking
//---------------------------------------
//driver modport
//---------------------------------------
modport DRIVER (clocking driver_cb,input clk,reset);//driver接口
//---------------------------------------
//monitor modport
//---------------------------------------
modport MONITOR (clocking monitor_cb,input clk,reset);//monitor接口
endinterface
定义顶层模块
module tbench_top;
//clock and reset signal declaration
bit clk;
bit reset;//时钟和复位信号
//clock generation
always #5 clk = ~clk;//产生时钟
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
//creatinng instance of interface, inorder to connect DUT and testcase
mem_if intf(clk,reset);
//DUT instance, interface signals are connected to the DUT ports
memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);
//enabling the wave dump
initial begin
uvm_config_db#(virtual mem_if)::set(uvm_root::get(),"*","vif",intf);//配置虚接口
$dumpfile("dump.vcd"); $dumpvars;
end
initial begin
run_test();
end
endmodule