基于简单DUT的UVM验证平台的搭建(一)

时间:2023-03-10 07:23:56
基于简单DUT的UVM验证平台的搭建(一)

最近一个月在实习公司做回归测试,对公司的UVM平台用的比较熟练,就想着自己做一个DUT,然后搭建一个UVM验证平台。

首先,DUT是一个简单的32位的加法器,代码如下:alu.v

module adder32_cla(
input clk ,
input rst ,
input enable ,
input [:] a ,
input [:] b ,
input cin ,
output [:] sum_r ,
output cout_r
); reg [:] sum_r = 'h00000000 ;
reg cout_r = 'h0 ; always @(posedge clk or negedge rst)
begin
if (!rst)
begin
sum_r = 'h00000000 ;
cout_r = 'h0 ;
end
else if(enable)
begin
{cout_r,sum_r} <= a + b + cin;
end
else
begin
sum_r <= sum_r ;
cout_r <= cout_r ;
end
end endmodule

UVM验证组件:

1、top.sv

`timescale 1ns/1ns

`include "pkg.sv"
`include "alu.v" module top(); import uvm_pkg::*;
`include "uvm_macros.svh"
my_if my_my_if(); adder32_cla inst1
(
.clk (my_my_if.clk ),
.rst (my_my_if.rst ),
.enable (my_my_if.enable ),
.a (my_my_if.a ),
.b (my_my_if.b ),
.cin (my_my_if.cin ),
.sum_r (my_my_if.sum_r ),
.cout_r (my_my_if.cout_r )
);
initial
begin
my_my_if.cin = 'b0 ;
my_my_if.a = 'h00000000 ;
my_my_if.b = 'h00000000 ;
my_my_if.enable = 'b1 ;
my_my_if.rst = 'b0 ;
# my_my_if.rst = 'b1 ;
end
initial
begin
my_my_if.clk <= 'b0 ; # my_my_if.clk <= 'b0 ;
# my_my_if.clk <= 'b1 ;
forever
# my_my_if.clk = ~my_my_if.clk;
end
initial
begin
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.i_agt.drv","my_if",my_my_if);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.o_agt.mon","my_if",my_my_if);
run_test();
end endmodule

top.sv主要的作用是实例化DUT,和输入输出的interface,并且定义了时钟频率,传递了接口一连接TB,和run_test();用于启动UVM phase。

2、my_test.sv

`ifndef MY_TEST__SV
`define MY_TEST__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_env.sv"
class my_test extends uvm_test;
my_env env;
extern function new(string name="my_test",uvm_component parent=null);
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_test)
endclass function my_test::new (string name="my_test",uvm_component parent=null);
super.new(name,parent);
endfunction
function void my_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env");
endfunction `endif

my_test.sv派生于uvm_test,属于基类。在项目中,主要采用基类构建框架,继承类实现具体动作的方式来增加灵活性。

3、my_env.sv

`ifndef MY_ENV__SV
`define MY_ENV__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_agent.sv"
`include "my_scoreboard.sv"
`include "my_model.sv" class my_env extends uvm_env;
my_agent i_agt ; //my_driver functional
my_agent o_agt ; //my_monitor functional
my_model mdl ;
my_scoreboard scb ; uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo; //my_monitor
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo; //my_driver
uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo; //my_model
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(my_env);
endclass function my_env::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_env::build_phase(uvm_phase phase);
super.build_phase(phase);
i_agt = my_agent::type_id::create("i_agt",this);
o_agt = my_agent::type_id::create("o_agt",this);
i_agt.is_active = UVM_ACTIVE;
o_agt.is_active = UVM_PASSIVE;
mdl = my_model::type_id::create("mdl",this);
scb = my_scoreboard::type_id::create("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 function void my_env::connect_phase(uvm_phase 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_scb_fifo.blocking_get_export); endfunction
`endif

my_env.sv的主要是agent scoreboard reference_model模块的实例化,和三个tlm_analysis_fifo,然后build_phase中type_id::create()模块,配置i/oagent,scoreboard model和new三个FIFO,之后connect_phase中,connect analysis_export和blocking_get_export。

4、my_transaction.sv

要传送的transaction

import uvm_pkg::*;
`include "uvm_macros.svh"
`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV
class my_transaction extends uvm_sequence_item;
rand bit cin;
rand bit [:] a,b;
bit [:] sum_r;
bit cout_r; extern function new (string name="my_transaction");
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(cin,UVM_ALL_ON)
`uvm_field_int(a,UVM_ALL_ON)
`uvm_field_int(b,UVM_ALL_ON)
`uvm_field_int(sum_r,UVM_ALL_ON)
`uvm_field_int(cout_r,UVM_ALL_ON)
`uvm_object_utils_end endclass function my_transaction::new (string name="my_transaction");
super.new(name);
endfunction
`endif

主要包括各种内容以及uvm_object_utils,uvm_field_int的注册,参数约束和随机化。

5、my_sequencer.sv

发送transaction的sequencer

`ifndef MY_SEQUENCER__SV
`define MY_SEQUENCER__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
`include "my_sequence.sv" class my_sequencer extends uvm_sequencer #(my_transaction);
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_sequencer)
endclass function my_sequencer::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_sequencer::build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction `endif

负责把transaction类型的数据传送给sequence,里面内容很简单就一个注册和new函数。

6、my_sequence.sv

`ifndef MY_SEQUENCE__SV
`define MY_SEQUENCE__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
class my_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
`uvm_object_utils(my_sequence)
extern function new(string name="my_sequence");
virtual task body();
if(starting_phase!=null)
starting_phase.raise_objection(this);
repeat()
begin
`uvm_do(m_trans)
end
#;
if(starting_phase!=null)
starting_phase.drop_objection(this);
endtask
endclass function my_sequence::new(string name="my_sequence");
super.new(name);
endfunction `endif

sequence,用于产生激励,里面的objection机制用来控制验证平台的打开与关闭,需要在drop_objection之前先raise_objection。task_body中uvm_do(my_trans),在之前和之后要starting_phase.raise_objection(this)和starting_phase.drop_objection(this);

7、my_driver.sv

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv" class my_driver extends uvm_driver #(my_transaction); virtual my_if vif;//to DUT
uvm_analysis_port #(my_transaction) ap;//the data is to reference model `uvm_component_utils(my_driver) extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase); extern virtual task main_phase(uvm_phase phase);
extern virtual task drive_one_pkt(my_transaction req);
endclass function my_driver::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_driver::build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get(this,"","my_if",vif))
`uvm_fatal("my_driver","Error in Getting interface");
ap=new("ap",this); endfunction task my_driver::main_phase(uvm_phase phase);
my_transaction req;
super.main_phase(phase); while()
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_transaction req);
@vif.drv_cb;
@vif.drv_cb
begin
vif.drv_cb.enable<='b1;
vif.drv_cb.cin<=req.cin;
vif.drv_cb.a<=req.a;
vif.drv_cb.b<=req.b;
end
@vif.drv_cb;
@vif.drv_cb vif.drv_cb.enable<='b0; endtask `endif

首先是virtual inf和build_phase中接受接口,否则fatal,然后main_phase中while(1)循环的get_next_item(req),调用发送函数发送req,返回item_done()。

8、my_monitor.sv

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
class my_monitor extends uvm_monitor;
virtual my_if vif;
uvm_analysis_port #(my_transaction) ap; // to scoreboard
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern virtual task receive_one_pkt(ref my_transaction get_pkt);
`uvm_component_utils(my_monitor)
endclass function my_monitor::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_monitor::build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get(this,"","my_if",vif))
`uvm_fatal("my_monitor","Error in Getting interface");
ap=new("ap",this);
endfunction task my_monitor::main_phase(uvm_phase phase);
my_transaction tr;
super.main_phase(phase);
while() begin
tr=new();
receive_one_pkt(tr);
ap.write(tr);
end
endtask task my_monitor::receive_one_pkt(ref my_transaction get_pkt);
@(negedge vif.drv_cb.enable);
get_pkt.cout_r=vif.mon_cb.cout_r;
get_pkt.sum_r=vif.mon_cb.sum_r;
endtask `endif

主要是virtual inf和analysis_port,在build_phase中接受接口,mian_phase中while(1)的调用接受函数和和ap.write(tr)。

9、my_agent.sv

`ifndef MY_AGENT__SV
`define MY_AGENT__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
`include "my_sequence.sv"
`include "my_sequencer.sv"
`include "my_driver.sv"
`include "my_monitor.sv" class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
my_monitor mon;
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
uvm_analysis_port #(my_transaction) ap;
`uvm_component_utils_begin(my_agent)
`uvm_field_object(sqr,UVM_ALL_ON)
`uvm_field_object(drv,UVM_ALL_ON)
`uvm_field_object(mon,UVM_ALL_ON)
`uvm_component_utils_end
endclass function my_agent::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
if(is_active==UVM_ACTIVE )
begin
sqr=my_sequencer::type_id::create("sqr",this);
drv=my_driver::type_id::create("drv",this);
end
else begin
mon=my_monitor::type_id::create("mon",this);
end
endfunction function void my_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase); if(is_active==UVM_ACTIVE )
begin
drv.seq_item_port.connect(sqr.seq_item_export);
this.ap=drv.ap;
end
else begin
this.ap=mon.ap;
end
endfunction `endif

含有driver和monitor,在build_phase中如果UVM_ACTIVE,就例化drv和sqr,但都例化mon,在build_phase中如果UVM_ACTIVE就链接drv和sqr的接口。

10、interface.sv

`ifndef MY_INTERFACE__SV
`define MY_INTERFACE__SV interface my_if;
logic clk,rst ;
logic cin ;
logic [:] a ;
logic [:] b ;
logic enable ;
wire cout_r ;
wire [:] sum_r ; clocking drv_cb @(posedge clk);
output enable,cin,a,b;
endclocking clocking mon_cb @(posedge clk);
input cout_r,sum_r;
endclocking endinterface
`endif

interface里面就是接口,用于连接DUT和driver和monitor。

12、my_model.sv

就是参考模型(reference_model)

`ifndef MY_MODEL__SV
`define MY_MODEL__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
class my_model extends uvm_component;
uvm_blocking_get_port #(my_transaction) port; //from my_driver
uvm_analysis_port #(my_transaction) ap; //to scoreboard
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern virtual task one_pkt(ref my_transaction pkt,ref my_transaction pkt2);
`uvm_component_utils(my_model)
endclass function my_model::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_model::build_phase(uvm_phase phase);
super.build_phase(phase);
port=new("port",this);
ap=new("ap",this);
endfunction task my_model::main_phase(uvm_phase phase);
my_transaction tr,tr2;
super.main_phase(phase);
while() begin
tr2=new();
port.get(tr);
one_pkt(tr2,tr);
ap.write(tr2);
end
endtask task my_model::one_pkt(ref my_transaction pkt,ref my_transaction pkt2);
bit [:] sum_total;
begin
sum_total=pkt2.a+pkt2.b+pkt2.cin;
pkt.sum_r=sum_total[:];
pkt.cout_r=sum_total[];
end
endtask
`endif

需要进的blocking_get_port和出的analysis_port两个,在build_phase中连接他们,在main_phase中则while1的,如果get_port.get()收到,就ana_port.write()这个tr。

13、my_scoreboard.sv

`ifndef MY_SCOREBOARD__SV
`define MY_SCOREBOARD__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
class my_scoreboard extends uvm_scoreboard;
int pre_number=;
my_transaction expect_queue[$];
uvm_blocking_get_port #(my_transaction) exp_port;//from my_reference
uvm_blocking_get_port #(my_transaction) act_port;//from my_monitor
`uvm_component_utils(my_scoreboard)
extern function new (string name,uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
endclass function my_scoreboard::new (string name,uvm_component parent);
super.new(name,parent);
endfunction function void my_scoreboard::build_phase(uvm_phase phase);
super.build_phase(phase);
exp_port=new("exp_port",this);
act_port=new("act_port",this);
endfunction
task my_scoreboard::main_phase(uvm_phase phase);
my_transaction get_expect,get_actual,tmp_tran;
bit result;
super.main_phase(phase);
fork
while ()
begin
exp_port.get(get_expect);
expect_queue.push_back(get_expect);
end
while ()
begin
act_port.get(get_actual);
if(expect_queue.size>)begin
tmp_tran=expect_queue.pop_front();
result=get_actual.compare(tmp_tran);
if(result) begin
pre_number=pre_number+;
$display("compare SUCCESSFULLy:%0d",pre_number);
end
else begin
$display("compare FAILED");
$display("the expect pkt is");
tmp_tran.print();
$display("the actual pkt is");
get_actual.print();
end
end
else begin
$display("ERROR::Received from DUT,while Expect Queue is empty");
get_actual.print();
end
end
join endtask `endif

是两个get_port,在build_phase中new它们,首先对于exp端,while(1)的,只要get一个tr,就要在tr_queue中push进去一个,同时对于act端,while(1)的。只要get一个tr,就要从tr_queue中pop一个出来,进行比较。这个两个是fork join的,互不影响。

14、my_case0.sv

`ifndef MY_CASE0__SV
`define MY_CASE0__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_test.sv" class my_case0 extends my_test;
`uvm_component_utils(my_case0)
extern function new(string name="my_case0",uvm_component parent=null);
extern virtual function void build_phase(uvm_phase phase);
endclass function my_case0::new (string name="my_case0",uvm_component parent=null);
super.new(name,parent);
endfunction function void my_case0::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",my_sequence::type_id::get());
endfunction `endif

my_case0.sv扩展于my_test.sv,在my_case0.sv的build_phase中传递sequence。

至此,本UVM验证平台的各个组件就已经完成了,接下来有时间把Makefile脚本完善好上传,并在装有vcs和Verdi的虚拟机去运行,查看波形和查看覆盖率报告。

目前还差的东西是在top.sv里面加上生成波形的语句。

附录:pkt.v   导入整个验证平台

`ifndef MY_INTERFACE__SV
`define MY_INTERFACE__SV

`include "interface.sv"
`include "my_env.sv"
`include "my_test.sv"
`include "my_case0.sv"

`endif

欢迎讨论:QQ:447574829