一、概述
1.1、什么是Testbench
Testbench是测试平台,如下图所示。
这个平台的作用就是产生测试激励给待测的设计DUV(Design Under Verification)。灌激励的时候可以写入预期值,检查被灌激励后的DUV的输出是否与预期值一致,从而实现验证RTL设计功能的目的。
1.2、Testbench的六功能以及四要素
六功能
①产生激励:Generate stimulus;
②将激励输入到待测设计:DUV-Design Under Verification);
③产生预期响应:Generate Expectation;
④获取响应:Caputure response;
⑤检查响应的正确性:Check the response for correctness;
⑥根据验证目标评估验证进度:Measure the progress against the overall verification goals。
四要素
①灌激励:产生输入信号给RTL文件;
②做预期:产生预期结果;
③收集响应:收集输出结果,实例化;
④做比较:比较结果。
3、为什么需要Testbench?
保证设计在功能上的正确性需要验证,而testbench就是验证的一种形式。从设计规格到RTL代码阶段,由于RTL的代码是手动设计的,需要我们进行功能验证。而RTL代码到门级网表阶段则是由逻辑中和工具自动完成的。
实现仿真通常是:使用HDL(硬件描述语言)编制的testbench(仿真文件)通过波形或自动比较工具去分析设计的正确性。同时也分析testbench自身的覆盖率和准确性
二、建立Testbench
使用过程语句:initital语句、always语句
initial 和 always 是 2 个基本的过程结构语句,在仿真的一开始即开始相互并行执行。通常来说,被动的检测响应使用 always 语句,而主动的产生激励使用 initial 语句。
initial语句和always语句被赋值的信号类型必须定义为reg类型
而过程连续赋值语句assign被赋值的信号类型必须为wire类型,而且不能出现在过程块中。过程块(initial语句和always语句)通常包含很多语句块(串行语句块begin-end、并行语句块为fork-join、循环语句等)
initial 和 always 的区别是:
①initial 语句只执行一次,而 always 语句不断地重复执行。
②如果希望在 initial 里多次运行一个语句块,可以在 initial 里嵌人循环语句(while、repeat、for 和 forever 等)。
③always语句通常在一些条件发生时完成操作。如下嵌入串行语句块begin-end(并行语句块为fork-join),用always语句描述同步置数、同步清零计数器
module counter1(out, data, load, rst, clk);
output [7:0] out;
input [7:0] data;
input load, rst, clk
reg [7:0] out;
always @(posedg clk) //上升沿触发,执行 always 操作
begin //begin... end 中的语句块顺序执行。
if(!rst) out = 8'b00; //同步清零,低电平有效
else if(load) out = data; //同步置数
else out = out +1;
end
endmodule
④always语句具有可综合性(可综合性就是可以编译成网表文件,也就是可以生成电路。而不可综合性只能在仿真阶段用)
Testbench的书写格式
以全加器full_adder的testbench代码为例
由于gvim的注释只能英文,所以保持了用英文注释哈。
module full_adder_tb;
reg ain, bin, cin; // drive the input port with the reg type
wire sumout, cout; // sample the output port with the wire type
full_adder u0_full_adder(
// task 1. how to create an instance
// module head: verillog-2001 format
/*input wire */ .a_in (ain),
/*input wire */ .b_in (bin),
/*input wire */ .c_in (cin), // carry in
/*output wire */ .sum_out(sumout),
/*output wire */ .c_out (cout) // carry out
);
// behavior of the adder can be synthesizable
// "assign" means connectivity
// assign {c_out, sum_out} = a_in + b_in + c_in;
//task 2. clock and reset generator
parameter CLK_PERIOD = 20;
reg clk, reset_n; // reset_n : active low
initial begin
clk = 0;
forever begin
#(CLK_PERIOD/2) clk = ~clk;
end
end
initial begin
reset_n = 0;
#100
reset_n = 1;
end
//task 3. drive the stimulus and capture the response
//here is a testcase
initial begin
#110 ain = 0;
bin = 0;
cin = 0; // 00
#20 ain = 0;
bin = 1;
cin = 0; // 01
#20 ain = 1;
bin = 0;
cin = 0; // 01
#20 ain = 1;
bin = 1;
cin = 0; // 10
#20 ain = 0;
bin = 0;
cin = 1; // 01
#20 ain = 0;
bin = 1;
cin = 1; // 10
#20 ain = 1;
bin = 0;
cin = 1; // 10
#20 ain = 1;
bin = 1;
cin = 1; // 11
//#20 ain = 1;
// bin = 1;
// cin = 0; // 10
#50 $finish; // here is a system task which can stop the simulation
// $stop
end
//task 4. check the result
always @ (posedge clk) begin
if (!reset_n) begin
$display("%t:%m: resetting ...",$time); // counter 5 clock
end
else begin
$display("%t:%m: resetting finish!", $time); // the 6th clock
end
end
initial begin
#115 if ({cout,sumout} != 2'b00) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b01) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b01) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b01) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b11) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
#20 if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
end
// task 5. dump waveform with the compile option -debug_all
initial begin
//$vcdpluson;
end
endmodule