UVM Primer - UVM Component
基础知识
uvm_component有两大特性,一是通过在new的时候指定parent参数来形成一种树形的组织结构,二是有phase的自动执行特点
要用UVM 干活 , 你需要自如的定义和例化 UVM 组件 . 这里是步骤
- 第 1 步 : 从 uvm_component 类或其子类继承定义你的组件
- 第 2 步 : 用 uvm_component_utils() 宏注册这个类到 Factory
- 第 3 步 : 提供 uvm_component 构造器
- 第 4 步 : 在必要时重写 UVM 的 phase 方法
写UVM Test 的时候 , 我创建了 run_phase() 方法来解释 UVM 会自动调用 run_phase()来启动仿真 . run_phase() 方法是 UVM phase 方法中的一个
所有UVMcomponent都继承了这些phase方法。UVM在所有的component里以设定的顺序调用这些 phase 方法。你可以在你的component里重写这些phase方法,然后UVM会按顺序调用。
UVM按照以下顺序调用 phase 方法
- function void build_phase UVM用这个方法自顶向下建立你的 验证平台 . 你要在这个方法里例化你的 uvm_component , 如果在别的方法里例化的话 , 你会得到 UVM 的致命报错
- function void connect_phase connect phase 把各个模块连接到一起 . 我们会在后面的章节学习连接
- function void end_of_elaboration_phase UVM在所有 component 都就位并连接好以后调用这个方法 , 需要在 UVM 层次设定完之后再对 验证平台 进行调整的话可以用这个 phase.
- task run_phase UVM会在各自的线程调用这个 task. 验证平台 中的 run_phase() 是 同时 运行的 , 所以你不知道那个会最先启动
- function void report_phase 这个phase 会在最后一个 objection 撤销后运行,然后 测试用例就结束了。你可以用来报告结果
tinyalu_pkg.sv
package tinyalu_pkg; import uvm_pkg::*; `include "uvm_macros.svh" typedef enum bit[2:0] {no_op = 3'b000, add_op = 3'b001, and_op = 3'b010, xor_op = 3'b011, mul_op = 3'b100, rst_op = 3'b111} operation_t; virtual tinyalu_bfm bfm_g; `include "coverage.svh" `include "random_tester.svh" `include "add_tester.svh" `include "scoreboard.svh" `include "random_test.svh" `include "add_test.svh" endpackage : tinyalu_pkg
tinyalu_bfm.sv(和UVM test那一节一样)
interface tinyalu_bfm;
import tinyalu_pkg::*;
byte unsigned A;
byte unsigned B;
bit clk;
bit reset_n;
wire [2:0] op;
bit start;
wire done;
wire [15:0] result;
operation_t op_set;
assign op = op_set;
task reset_alu();
reset_n = 1'b0;
@(negedge clk);
@(negedge clk);
reset_n = 1'b1;
start = 1'b0;
endtask : reset_alu
task send_op(input byte iA, input byte iB, input operation_t iop, shortint alu_result);
if (iop == rst_op) begin
@(negedge clk);
op_set = iop;
@(posedge clk);
reset_n = 1'b0;
start = 1'b0;
@(posedge clk);
#1;
reset_n = 1'b1;
end else begin
@(negedge clk);
op_set = iop;
A = iA;
B = iB;
start = 1'b1;
if (iop == no_op) begin
@(posedge clk);
#1;
start = 1'b0;
end else begin
do
@(negedge clk);
while (done == 0);
alu_result = result;
start = 1'b0;
end
end // else: !if(iop == rst_op)
endtask : send_op
initial begin
clk = 0;
forever begin
#10;
clk = ~clk;
end
end
endinterface : tinyalu_bfm
tb_classes
coverage.svh
class coverage extends uvm_component; `uvm_component_utils(coverage) virtual tinyalu_bfm bfm; byte unsigned A; byte unsigned B; operation_t op_set; covergroup op_cov; coverpoint op_set { bins single_cycle[] = {[add_op : xor_op], rst_op,no_op}; bins multi_cycle = {mul_op}; bins opn_rst[] = ([add_op:no_op] => rst_op); bins rst_opn[] = (rst_op => [add_op:no_op]); bins sngl_mul[] = ([add_op:xor_op],no_op => mul_op); bins mul_sngl[] = (mul_op => [add_op:xor_op], no_op); bins twoops[] = ([add_op:no_op] [* 2]); bins manymult = (mul_op [* 3:5]); } endgroup covergroup zeros_or_ones_on_ops; all_ops : coverpoint op_set { ignore_bins null_ops = {rst_op, no_op};} a_leg: coverpoint A { bins zeros = {'h00}; bins others= {['h01:'hFE]}; bins ones = {'hFF}; } b_leg: coverpoint B { bins zeros = {'h00}; bins others= {['h01:'hFE]}; bins ones = {'hFF}; } op_00_FF: cross a_leg, b_leg, all_ops { bins add_00 = binsof (all_ops) intersect {add_op} && (binsof (a_leg.zeros) || binsof (b_leg.zeros)); bins add_FF = binsof (all_ops) intersect {add_op} && (binsof (a_leg.ones) || binsof (b_leg.ones)); bins and_00 = binsof (all_ops) intersect {and_op} && (binsof (a_leg.zeros) || binsof (b_leg.zeros)); bins and_FF = binsof (all_ops) intersect {and_op} && (binsof (a_leg.ones) || binsof (b_leg.ones)); bins xor_00 = binsof (all_ops) intersect {xor_op} && (binsof (a_leg.zeros) || binsof (b_leg.zeros)); bins xor_FF = binsof (all_ops) intersect {xor_op} && (binsof (a_leg.ones) || binsof (b_leg.ones)); bins mul_00 = binsof (all_ops) intersect {mul_op} && (binsof (a_leg.zeros) || binsof (b_leg.zeros)); bins mul_FF = binsof (all_ops) intersect {mul_op} && (binsof (a_leg.ones) || binsof (b_leg.ones)); bins mul_max = binsof (all_ops) intersect {mul_op} && (binsof (a_leg.ones) && binsof (b_leg.ones)); ignore_bins others_only = binsof(a_leg.others) && binsof(b_leg.others); } endgroup function new (string name, uvm_component parent); super.new(name, parent); op_cov = new(); zeros_or_ones_on_ops = new(); endfunction : new function void build_phase(uvm_phase phase); if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm)) $fatal("Failed to get BFM"); endfunction : build_phase task run_phase(uvm_phase phase); forever begin : sampling_block @(negedge bfm.clk); A = bfm.A; B = bfm.B; op_set = bfm.op_set; op_cov.sample(); zeros_or_ones_on_ops.sample(); end : sampling_block endtask : run_phase endclass : coverage
scoreboard.svh
class scoreboard extends uvm_component; `uvm_component_utils(scoreboard); virtual tinyalu_bfm bfm; function new (string name, uvm_component parent); super.new(name, parent); endfunction : new function void build_phase(uvm_phase phase); if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm)) $fatal("Failed to get BFM"); endfunction : build_phase task run_phase(uvm_phase phase); shortint predicted_result; forever begin : self_checker @(posedge bfm.done) case (bfm.op_set) add_op: predicted_result = bfm.A + bfm.B; and_op: predicted_result = bfm.A & bfm.B; xor_op: predicted_result = bfm.A ^ bfm.B; mul_op: predicted_result = bfm.A * bfm.B; endcase // case (op_set) if ((bfm.op_set != no_op) && (bfm.op_set != rst_op)) if (predicted_result != bfm.result) $error ("FAILED: A: %0h B: %0h op: %s result: %0h", bfm.A, bfm.B, bfm.op_set.name(), bfm.result); end : self_checker endtask : run_phase endclass : scoreboard
add_tester.svh
class add_tester extends random_tester; `uvm_component_utils(add_tester) function new (string name, uvm_component parent); super.new(name, parent); endfunction : new function operation_t get_op(); bit [2:0] op_choice; return add_op; endfunction : get_op endclass : add_tester
ramdom_tester.svh
class random_tester extends uvm_component; `uvm_component_utils (random_tester) virtual tinyalu_bfm bfm; function new (string name, uvm_component parent); super.new(name, parent); endfunction : new virtual function operation_t get_op(); bit [2:0] op_choice; op_choice = $random; case (op_choice) 3'b000 : return no_op; 3'b001 : return add_op; 3'b010 : return and_op; 3'b011 : return xor_op; 3'b100 : return mul_op; 3'b101 : return no_op; 3'b110 : return rst_op; 3'b111 : return rst_op; endcase // case (op_choice) endfunction : get_op virtual function byte get_data(); bit [1:0] zero_ones; zero_ones = $random; if (zero_ones == 2'b00) return 8'h00; else if (zero_ones == 2'b11) return 8'hFF; else return $random; endfunction : get_data function void build_phase(uvm_phase phase); if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm)) $fatal("Failed to get BFM"); endfunction : build_phase task run_phase(uvm_phase phase); byte unsigned iA; byte unsigned iB; operation_t op_set; shortint result; phase.raise_objection(this); bfm.reset_alu(); repeat (1000) begin : random_loop op_set = get_op(); iA = get_data(); iB = get_data(); bfm.send_op(iA, iB, op_set, result); end : random_loop #500; phase.drop_objection(this); endtask : run_phase endclass : random_tester
random_test.svh
class random_test extends uvm_test; `uvm_component_utils(random_test); random_tester tester_h; coverage coverage_h; scoreboard scoreboard_h; function void build_phase(uvm_phase phase); //重载了build_phase然后例化了三个验证平台组件 tester_h = new("tester_h", this); coverage_h = new("coverage_h", this); scoreboard_h = new("scoreboard_h", this); endfunction : build_phase function new (string name, uvm_component parent); super.new(name,parent); endfunction : new endclass
add_test.svh
class add_test extends random_test; //add_test继承了random_test `uvm_component_utils(add_test); add_tester tester_h; function new (string name, uvm_component parent); super.new(name,parent); endfunction : new endclass
用UVM启动仿真
top.sv(和UVM test那一节一样)
module top;
import uvm_pkg::*;
`include "uvm_macros.svh"
import tinyalu_pkg::*;
`include "tinyalu_macros.svh"
tinyalu_bfm bfm();
tinyalu DUT (.A(bfm.A), .B(bfm.B), .op(bfm.op),
.clk(bfm.clk), .reset_n(bfm.reset_n),
.start(bfm.start), .done(bfm.done), .result(bfm.result));
initial begin
uvm_config_db #(virtual tinyalu_bfm)::set(null, "*", "bfm", bfm);
run_test(); //run_test()方法从命令行得到一个类名字符串,然后用UVM Factory来创建这个类的测试对象,然后这个测试对象开始运行测试
end
endmodule : top