最近一個月在實習公司做回歸測試,對公司的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