概述
第三章:SystemVerilog接口和总线功能模型
上一章我们建立的Testbench缺点是缺乏模块化,所有功能都被塞进一个文件和一个SystemVerilog模块中。这使得修改、重用或调试测试台变得困难。
我们迈向UVM的第一步是使用SystemVerilog接口模块化我们的Testbench。我们将看到接口允许我们简单的共享信号,还可以使用它们来创建总线功能模型,从而将与总线相关的协议封装在简单的访问例程中。
本章主要含有以下文件:
tinyalu_pkg.sv
tinyalu_bfm.sv
tester.sv
coverage.sv
scoreboard.sv
top.sv
1、TinyALU BFM(总线功能模型)
tinyalu_bfm封装tinyalu的Testbench中的所有信号,并提供clk、reset_alu(),task和send_op()任务,用于向tinyalu发送操作。我们定义SystemVerilog接口的方式与定义模块的方式相同。我们从interface关键字开始,定义接口中的信号:
interface是sv的一个关键字,级别等同于module,一般接口使用interface进行定义。
BFM是我们迈向模块化的第一步,它处理所有信号激励的问题。
BFM提供两个task:reset_alu()和send_op()。
补充:task and function
(1)task可以添加消耗时间的语句,而function不可以消耗时间 (这一点与verilog相同)。
(2)task可以调用task和function,而function仅能调用function。
还有一点要提醒新手:task和function中是不能使用initial和always的。
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;
//operation_t类型是包tinyalu_pkg中定义的枚举类型
//我们在界面顶部导入了tinyalu_pkg
assign op = op_set;
initial begin
clk = 0;
forever begin
#10;
clk = ~clk;
end
end
task reset_alu();
//reset_alu()task丢弃重置信号,等待两个时钟,然后再次启动。
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);
//send_op()task将操作发送到ALU并返回结果:
op_set = iop;
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);
A = iA;
B = iB;
start = 1'b1;
if (iop == no_op) begin
//NOP操作
@(posedge clk);
#1;
start = 1'b0;
end else begin
do
@(negedge clk);
while (done ==0);
start = 1'b0;
//??为什么
end
end // else: !if(iop == rst_op)
endtask : send_op
endinterface : tinyalu_bfm
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;
endpackage : tinyalu_pkg
send_op()task演示了BFM如何封装与DUT相关的协议:
在这种情况下,task在操作总线上放置一个操作,在操作数总线上放置数据。然后,它根据所请求的操作升高并降低启动信号。封装这种行为有两个好处:
•我们不需要在代码中添加协议级行为;调用此任务的代码比处理信号的代码更简单。
•我们可以在一个地方修改所有协议级别的行为;此处的修复会在整个设计中传播。
现在我们有了TinyALU BFM,我们可以模块化设计的其余部分。
2、创建模块化Testbench
现在我们已经有了BFM,我们可以创建模块来实现我们的三个Testbench操作(tester、scoreboard和coverage),并通过BFM将它们连接到DUT:
(1) top.sv
module top;
tinyalu_bfm
bfm();
tester
tester_i
(bfm);
coverage
coverage_i
(bfm);
scoreboard scoreboard_i(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));
endmodule : top
//将Tsetbench模块与BFM连接
测试台(Testbench)由四部分组成:
•tinyalu_bfm-包含所有信号级行为的SystemVerilog接口
•tester生成测试激励
•coverage提供功能覆盖范围
•scoreboard检查结果
实例化模块时,我们将SystemVerilog接口传递给模块。可以看到DUT实例化引用BFM内的信号。
(2)tester
module tester(tinyalu_bfm bfm);
import tinyalu_pkg::*;
function operation_t get_op();
//function得到随机操作码
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
function byte get_data();
//function得到随机操作数
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
initial begin
//接口定义
byte
unsigned
iA;
byte
unsigned
iB;
operation_t
op_set;
shortint
result;
bfm.reset_alu();
repeat (1000) begin : random_loop
//重复1000次
op_set = get_op();
iA = get_data();
iB = get_data();
bfm.send_op(iA, iB, op_set, result);
//对应BFM中send——op
end : random_loop
$stop;
end // initial begin
endmodule : tester
(3)coverage
module coverage(tinyalu_bfm bfm);
import tinyalu_pkg::*;
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:mul_op] => rst_op);
bins rst_opn[] = (rst_op => [add_op:mul_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:mul_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
op_cov oc;
zeros_or_ones_on_ops c_00_FF;
initial begin : coverage_block
oc = new();
c_00_FF = new();
forever begin
: sampling_block
//采样处与BFM相关联
@(negedge bfm.clk);
A = bfm.A;
B = bfm.B;
op_set = bfm.op_set;
oc.sample();
c_00_FF.sample();
end : sampling_block
end : coverage_block
endmodule : coverage
(4)scoreboar
module scoreboard(tinyalu_bfm bfm);
import tinyalu_pkg::*;
always @(posedge bfm.done) begin
shortint predicted_result;
#1;
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
endmodule : scoreboard
最后
以上就是时尚水池为你收集整理的UVM Primer Ch3 SystemVerilog Interfaces and Bus Functional Models的全部内容,希望文章能够帮你解决UVM Primer Ch3 SystemVerilog Interfaces and Bus Functional Models所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复