最近一个月在实习公司做回归测试,对公司的UVM平台用的比较熟练,就想着自己做一个DUT,然后搭建一个UVM验证平台。
首先,DUT是一个简单的32位的加法器,代码如下:alu.v
module adder32_cla( input clk , input rst , input enable , input [31:0] a , input [31:0] b , input cin , output [31:0] sum_r , output cout_r ); reg [31:0] sum_r = 32'h00000000 ; reg cout_r = 1'h0 ; always @(posedge clk or negedge rst) begin if (!rst) begin sum_r = 32'h00000000 ; cout_r = 1'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 = 1'b0 ; my_my_if.a = 32'h00000000 ; my_my_if.b = 32'h00000000 ; my_my_if.enable = 1'b1 ; my_my_if.rst = 1'b0 ; #100 my_my_if.rst = 1'b1 ; end initial begin my_my_if.clk <= 1'b0 ; #500 my_my_if.clk <= 1'b0 ; #500 my_my_if.clk <= 1'b1 ; forever #50 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 [31:0] a,b; bit [31:0] 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(10) begin `uvm_do(m_trans) end #10000; 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(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_transaction req); @vif.drv_cb; @vif.drv_cb begin vif.drv_cb.enable<=1'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<=1'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(1) 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 [31:0] a ; logic [31:0] b ; logic enable ; wire cout_r ; wire [31:0] 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(1) 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 [32:0] sum_total; begin sum_total=pkt2.a+pkt2.b+pkt2.cin; pkt.sum_r=sum_total[31:0]; pkt.cout_r=sum_total[32]; 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=0; 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 (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 tmp_tran=expect_queue.pop_front(); result=get_actual.compare(tmp_tran); if(result) begin pre_number=pre_number+1; $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