类的定义与实例化
我们需要定义一个类,并且调用new()实例化对象使用方法和变量。
driver模块
diver模块一般负责接收sequencer发送的transaction(通过使用port封装好的方法),解析transaction对DUT产生激励,并且通过端口发送给moniter模块,这里一般使用interface关键字抽象化我们使用的变量,需要注意要加上virtual关键字实例化虚接口用,并且这里需要get我们的interface数据,与之对应的我们需要在上游agent模块中实例化driver时set我们get的interfac接口。
'include "uvm_macros.svh"
import uvm_pkg::*;
class my_driver extends uvm_driver #(my_transaction)
virtual my_if vif;
uvm_analysis_port #(my_transaction) ap;
'uvm_component_utils(my_driver)
extern function new(string name = "my_driver", uvm_component parent = NULL);
extern virtual funtion void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern task drive_one_pkt(my_transaction)
extern task drive_one_byte(bit [7:0] data)
endclsass;
function my_driver::new(string name, uvm_component parent);
super.new(name,parent);
endfuntion
funtion void my_driver::build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(vitual my_if)::get("this", "", "my_if", vif))
'uvm_fatal("my_driver","error in geting interface");
ap = new("ap", this);
endfuntion
task my_driver::main_phase(uvm_phase phase);
my_transation req;
super.main_phase(phase);
vif.drv_cb.rxd <= 0;
vif.drv_cb.rx_dv <= 1'b0;
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_transation req);
byte unsigned data_q[];
int data_size;
data_size = req.pack_bytes(data_q) / 8;#使用pack_bytes需要req里的uvm_field
repeat(3) @vif.drv_cb;
for(int i = 0; i < data_size; i++) begin
drive_one_byte(data_q[i]);
end
@vif.drv_cb;
vif.drv_cb.rx_dv <= 1'b0;
endtask
task my_driver::drive_one_byte(bit[7:0] data);
@vif.drv_cb;
vif.drv_cb.rxd <= data;
vif.drv_cb.rx_dv <= 1'b1;
endtask;
transaction
模块间通信发送数据一般使用包的形式,需要看具体协议,比如以太网需要源地址、目的地址、数据、校验、包类型。
class my_transation extends uvm_sequence_item;
rand bit [47:0] dmac;
rand bit [47:0] smac;
rand bit [15:0] type;
rand byte pload[];
rand bit [31:0] crc;
constraint cons_pload_size{
pload.size >= 47;
pload.size <= 100;
}
funtion new(string name = "my_transaction");
super.new(name);
endfuntion;
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(dmac,UVM_ALL_ON)
`uvm_field_int(smac,UVM_ALL_ON)
`uvm_field_int(ether_type,UVM_ALL_ON)
`uvm_field_array_int(pload,UVM_ALL_ON)
`uvm_field_int(crc,UVM_ALL_ON)
`uvm_object_utils_end
endclass
monitor
monitor用来监测模块的输出,
class my_monitor extends uvm_monitor
virtual my_if vif;
uvm_analysis_port #(my_transaction) ap;
funtion new(string name = "my_monitor", uvm_component parent = NULL);
super.new(name, parent);
endfunction
funtion void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get("this","","my_if",vif)
uvm_report_fatal("my_monitor", "error in geting interface");
ap = new("ap", this);
endfuntion
extern task main_phase(uvm_phase phase);
extern task recive_one_packet(my_tansaction get_rpt);
extern task get_one_byte(logic valid, logic [7:0] data);
endclass;
task my_monitor::main_phase(uvm_phase phase)
super.main_phase(phase);
my_transaction tr;
while(1) begin
tr = new("my_transacion");
recive_one_packer(tr);
ap.write(tr);
end
endtask
task my_monitor::recive_one_packet(my_transaction get_rpt);
byte unsigned data_q[];
int data_size;
while(1) begin
@(vif.mon_cb);
if(vif.mon_cb.tx_en)break;
end
while(vif.mon_cb.tx_en) begin
data_q.push_back(vif.mon_cb.txd);
@(vif.mon_cb);
end
data_size = data_q.size();
get_rpt.pload = new[data_size];
data_size = get_rpt.unpack_bytes(data_q) / 8;
agent
因为monitor和driver有很多功能上的耦合,所以封装在一起,有agent负责实例化monitor、driver、sequencer,并根据不同状态的monitor实例化不同的monitor。
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
my_monitor mon;
uvm_analysis_port #(my_transaction) ap;
function new(string name = "my_agent", uvm_component parent = NULL)
super.new(name, parent);
endfuntion
funtion void build_phase(uvm_phase phase);
super.build_phase(phase);
if(is_active == UVM_ACTIVE) begin
sqr = my_squencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv",this);
end
mon = my_monitor::type_id::create("mon",this);
endfunction
funtion void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
end;
ap = mon.ap;
endfunction
'uvm_component_utils(my_agent);
endclass
reference model
这个模块用于接收agent中monitor模块传输的数据,并将这个数据传输给scoreboard
class my_model extends uvm_model;
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transacion) ap;
function new(string name = "my_model", uvm_component parent);
super.new(name, parent);
endfuntion
function void build_phase(uvm_phase phase);
super.build_phase(phase);
port = new("port", this);
ap = new("ap", this);
endfuntion
virtual task main_phase(uvm_phase phase);
my_transaction tr;
super.main_phase(phase);
while(1) begin
port.get(tr);
tr.print();
ap.write(tr);
end
endfuntion
'uvm_component_utils(my_model);
endclass
scoreboard
这个模块主要是用来比较来自reference model和DUT的输出是否一致。
class my_scoreboard extends uvm_scoreboard;
my_transaction expect_queue[$];
uvm_blocking_get_port #(my_transaction) exp_port;
uvm_blocking_get_port #(my_transaction) act_port;
'uvm_component_utils(my_scoreboard)
function new(string name = "my_scoreboard", uvm_component parent = NULL);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
exp_port = new("exp_port", this);
act_port = new("act_port", this);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_scoreboard::main_phase(uvm_phase phase);
my_transaction get_expect, get_actual, tmp_tran;
bit result;
super.main(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
temp_tran = expect_queue.pop_front();
result = get_actual.compare(tem_tran);
if(result) begin
'uvm_info("my_socreboard", "compare successfully", UVM_LOW);
end
else begin
'uvm_error("my_scoreboard", "compare falled");
end
else
'uvm_error("my_scoreboard", "received from dut, while expect queue is empty");
end
end
join
endtask
env
这是一个大容器,处于UVM树的最高层,控制着其他资源的实例化,并且通过fifo将reference model与dut输出结果和scoreboard相连。
class my_env extends uvm_env;
my_agent i_agt;
my_agent o_agt;
my_model mdl;
my_socerboard scb;
uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo;
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo;
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfuntion
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
i_agt = new("i_agt", this);
o_agt = new("o_agt", this);
i_agt.is_active = UVM_ACTIVE;
o_agt.is_active = UVM_PASSIVE;
mdl = new("mdl", this);
scb = new("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
virtual function void connect_phase(uvm_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_acb_fifo.blocking_get_export);
endfuntion
endclass
sequence
用来产生数据并传输给sequencer,sequencer收到后传输给driver,这里有两种启动方式,一种是在env的main_phase中启动,一种是在自己的main_phase中启动。
class my_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
function new(string name = "my_sequence");
super.new(name);
endfunction
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(10) begin
'uvm_do(m_trans)
end
#1000;
else
starting_phase.drop_objection(this);
endtask
'uvm_object_utils(my_sequence)
endclass
case
一般在my_test里实例化env,在my_case里使用set使不同的sequence自启动。
class my_test extends uvm_test;
my_env env;
function new(string name = "my_test", uvm_component parent = NULL);
super.new(name, parent);
endfuntion
virtual funtion void build_phase(uvm_phase);
super.build_phase(phase);
env = new("env", this);
endfuncion
endclass
class my_case0 extends base_test;
funtion new(string name = "my_case0", uvm_component parent = NULL);
super.new(name, parent);
endfunction
virtual funtion void 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", case0_sequence::type_id::get());
endfunction
endclass
top
系统仿真时,首先进入top,当运行run_test时根据编译输入的命令实例化具体的case,在case里实例化env,env再实例化整个UVM树形结构。
`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "my_if.sv"
`include "my_transaction.sv"
`include "my_driver.sv"
`include "my_monitor.sv"
`include "my_sequencer.sv"
`include "my_agent.sv"
`include "my_model.sv"
`include "my_scoreboard.sv"
`include "my_env.sv"
`include "base_test.sv"
`include "my_case0.sv"
`include "my_case1.sv"
module top_db;
reg clk;
reg rst_n;
my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data),
.rx_dv(input_if.valid),
.txd(output_if.data),
.tx_en(output_if.valid));
initial begin
clk = 0;
forever begin
#100 clk = ~clk;
end
end
initial begin
run_test();
end
initial begin
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.i_agt.drv", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.i_agt.mon", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.i_agt.mon", "vif", output_if);
end
initial begin
$shm_open("test.shm");
$shm_probe(top_tb,"ACTFM");
#1ms;
$shm_close;
end
endmodule