对于小型设计来说,最好的测试方式便是使用TestBench和HDL仿真器来验证其正确性。一般TestBench需要包含这些部分:实例化待测试设计、使用测试向量激励设计、将结果输出到终端或波形窗口便于可视化观察、比较实际结果和预期结果。下面是一个标准的HDL验证流程:
TestBench可以用VHDL或Verilog、SystemVerilog编写,本文以Verilog HDL为例。FPGA设计必须采用Verilog中可综合的部分子集,但TestBench没有限制,任何行为级语法都可以使用。本文将先介绍TestBench中基本的组成部分。
使用系统时钟的设计在TestBench中必须要生成时钟信号,该功能实现起来也非常简单,示例代码如下:
parameter ClockPeriod = 10;
//方法1
initial begin
forever clock = #(ClockPeriod/2) ~ Clock;
end
//方法2
initial begin
always #(ClockPeriod/2) Clock = ~Clock;
end
只有给设计激励数据,才能得到验证结果。提供激励的方法有两种,绝对时间激励以仿真时刻0为基准,给信号赋值,示例如下:
initial begin
reset = 1;
load = 0;
count = 0;
#100 reset = 0;
#20 load = 1;
#20 count = 1;
end
‘#’用于指定等待的延迟时间,之后才会执行下一个激励。相对时间激励给信号一个初始值,直到某一事件发生后才触发激励赋值,示例如下:
always @ (posedge clk)
tb_cnt <= tb_cnt + 1;
initial begin
if (tb_cnt <= 5) begin
reset = 1;
load = 0;
count = 0;
end
else begin
reset = 0;
load = 1;
count = 1;
end
end
根据需要,可以同时使用两种方法。每一个initial块、always块之间都是并行工作的关系,但在initial块内部是顺序地处理事件。因此复杂的激励序列应该分散到多个initial或always块中,以提高代码可读性和可维护性。
Verilog中可以使用$display和$monitor系统任务来显示仿真结果,示例代码如下:
initial begin
$timeformat(-9, 1, "ns", 12);
$display(" Time clk rst ld sftRg data sel");
$monitor("%t %b %b %b %b %b %b", $realtime,
clock, reset, load, shiftreg, data, sel);
end
$display会将双引号之间的文本输出到终端窗口。$monitor的输出为事件驱动型,如上例中$realtime变量用于触发信号列表的显示,%t表示$realtime以时间格式输出,%b表示其余值以二进制格式输出。其余还有%d、%h、%o等与惯例相同。
下面是一个简单的移位寄存器Verilog设计示例:
module shift_reg (clock, reset, load, sel, data, shiftreg);
input clock;
input reset;
input load;
input [1:0] sel;
input [4:0] data;
output [4:0] shiftreg;
reg [4:0] shiftreg;
always @ (posedge clock)
begin
if (reset)
shiftreg = 0;
else if (load)
shiftreg = data;
else
case (sel)
2'b00 : shiftreg = shiftreg;
2'b01 : shiftreg = shiftreg << 1;
2'b10 : shiftreg = shiftreg >> 1;
default : shiftreg = shiftreg;
endcase
end
endmodule
下面给出上述设计的TestBench示例:
module testbench; // 申明TestBench名称
reg clock;
reg load;
reg reset; // 申明信号
wire [4:0] shiftreg;
reg [4:0] data;
reg [1:0] sel;
// 申明移位寄存器设计单元
shift_reg dut(.clock (clock),
.load (load),
.reset (reset),
.shiftreg (shiftreg),
.data (data),
.sel (sel));
initial begin // 建立时钟
clock = 0;
forever #50 clock = ~clock;
end
initial begin // 提供激励
reset = 1;
data = 5'b00000;
load = 0;
sel = 2'b00;
#200
reset = 0;
load = 1;
#200
data = 5'b00001;
#100
sel = 2'b01;
load = 0;
#200
sel = 2'b10;
#1000 $stop;
end
initial begin // 打印结果到终端
$timeformat(-9,1,"ns",12);
$display(" Time Clk Rst Ld SftRg Data Sel");
$monitor("%t %b %b %b %b %b %b", $realtime,
clock, reset, load, shiftreg, data, sel);
end
endmodule
TestBench中包括实例化设计、建立时钟、提供激励、终端显示几个部分。每个initial块之间都从0时刻开始并行执行。$stop用来指示仿真器停止TestBench仿真(建议每个TestBench中都有至少一个$stop)。$monitor会在终端以ASCII格式打印监测结果。
下面给出一些编写TestBench的基本设计规则: