tinyalu_pkg.sv
在搭建基于对象的验证平台的时候,我们把所有的类定义和共享资源都放在SystemVerilog 包里。用了package就可以在多个module中共享类和变量定义了。当你引入package时,你可以访问package中所有的定义和声明的数据
package tinyalu_pkg; 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; `include "coverage.svh" `include "tester.svh" `include "scoreboard.svh" `include "testbench.svh" endpackage : tinyalu_pkg
tinyalu_bfm.sv
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; initial begin clk = 0; forever begin #10; clk = ~clk; end end 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, output shortint alu_result); if (iop == rst_op) begin @(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); start = 1'b0; end alu_result = result; end // else: !if(iop == rst_op) endtask : send_op endinterface : tinyalu_bfm
tester类
tb_classes/tester.svh
class tester; //注意此处用的是类的定义,原来是module virtual tinyalu_bfm bfm; //定义了操作BFM的变量,这个变量在构造函数里赋值 function new (virtual tinyalu_bfm b); bfm = b; endfunction : new protected 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 protected 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 task execute(); //注意此处是execute方法,原来是initial块 byte unsigned iA; byte unsigned iB; shortint unsigned result; operation_t op_set; bfm.reset_alu(); op_set = rst_op; iA = get_data(); iB = get_data(); bfm.send_op(iA, iB, op_set, result); op_set = mul_op; bfm.send_op(iA, iB, op_set, result); bfm.send_op(iA, iB, op_set, result); op_set = rst_op; bfm.send_op(iA, iB, op_set, result); repeat (10) begin : random_loop op_set = get_op(); iA = get_data(); iB = get_data(); bfm.send_op(iA, iB, op_set, result ); $display("%2h %6s %2h = %4h",iA, op_set.name(), iB, result); end : random_loop $stop; endtask : execute endclass : tester
tb_classes/scoreboard.svh
class scoreboard; virtual tinyalu_bfm bfm; function new (virtual tinyalu_bfm b); bfm = b; endfunction : new task execute(); shortint predicted_result; forever begin : self_checker //注意此处用forever, wait语句代替原来的always @(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 : execute endclass : scoreboard
tb_classes/coverage.svh
class 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 (virtual tinyalu_bfm b); op_cov = new(); zeros_or_ones_on_ops = new(); bfm = b; endfunction : new task execute(); 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 : execute endclass : coverage
tb_classes/testbench.svhclass testbench;
virtual tinyalu_bfm bfm;
//在OOP 里这个相当于module里的端口列表
//回顾基于module的代码,我们知道SystemVerilog接口是在验证平台的module间传递信号的独立的编译单元。tester,scoreboard和coverage module通过模块的端口列表得到BFM的拷贝
//对象可以通过SystemVerilog接口的句柄来用同样的方式访问信号。virtual声明指示编译器这个变量会得到一个接口的句柄。virtual关键字指示了编译时接口句柄是不在bfm变量里
//的,后面有人要仿真这些信号的时候会把句柄放进变量里
tester tester_h; //我们声明了三个变量tester_h,coverage_h和scoreboard_h来存放三个验证平台对象
coverage coverage_h;
scoreboard scoreboard_h;
//有很多方式可以把接口的句柄存入bfm变量里。这个例子里,我们用new()方法来做。
function new (virtual tinyalu_bfm b);
bfm = b;
endfunction : new
task execute(); //execute()方法例化了三个验证平台对象,每个都传入了bfm拷贝
tester_h = new(bfm);
coverage_h = new(bfm);
scoreboard_h = new(bfm);
fork
tester_h.execute();
coverage_h.execute();
scoreboard_h.execute();
join_none
endtask : execute
endclass : testbench
top.sv
module top; import tinyalu_pkg::*; //我们的package叫tinyalu_pkg。里面定义了所有的类,我们通过在module中引入package来访问所有的定义 `include "tinyalu_macros.svh" 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)); tinyalu_bfm bfm(); //例化接口 testbench testbench_h; //上面的代码跟基于module的代码有相似之处,除了我们把激励,自检,功能覆盖模块换成了testbench类。我们现在有存放testbench对象的testbench_h变量了 initial begin testbench_h = new(bfm); testbench_h.execute(); end endmodule : top