文章目录
- 编写测试验证程序
- 波形产生
- 值序列
- 重复模式
- 测试验证程序实例
- 从文本文件中读取向量
- 实例:时序检测器
测试验证程序用于测试和验证设计方法的正确性。Verilog 提供强有力的结构来说明测试验证程序。
编写测试验证程序
测试验证程序有三个主要目的:
- 产生模拟激励(波形);
- 将输入激励加入到测试模块并收集其输出响应;
- 将响应输出与期望值进行比较。
典型的测试验证程序形式如下:
module test_bench;
// 通常测试验证程序没有输入和输出端口
Local_reg_and_net_declarations
Generate_waveforms_using_initial_&_always_statements
Instantiate_module_under_test
Monitor_output_and_compare_with_expected_values
endmodule
测试中,通过在测试验证程序中进行实例化,激励自动加载于测试模块。
波形产生
有两种产生激励值的主要方法:
- 产生波形,并在确定的离散时间间隔加载激励;
- 根据模块状态产生激励,即根据模块的输出响应产生激励。
通常需要两类波形。一类是具有重复模式的波形,例如时钟波形,另一类是一组指定的值确定的波形。
值序列
产生值序列的最佳方法是使用 initial
语句。例如:
initial
begin
Reset = 0;
#100 Reset = 1;
#80 Reset = 0;
#30 Reset = 1;
end
生成的波形如下图所示:
如果使用非阻塞性过程赋值产生如上所示的波形,则写法如下:
initial
begin
Reset <= 0;
Reset <= #100 1;
Reset <= #180 0;
Reset <= #210 1;
end
重复模式
重复模式的生成通过使用如下形式的连续赋值形式加以简化:
assign #(PERIOD/2) Clock = ~ Clock;
但是这种做法并不完全正确。问题在于 Clock
是一个线网(只有线网能够在连续赋值中被赋值),它的初始值是 z,并且,z 等于 x,~ x 等于 x。因此 Clock
的值永远固定为值 x。
下面是一个完整的时钟产生器模块:
module Gen_Clk_A(Clk_A);
output Clk_A;
reg Clk_A;
parameter tPERIOD = 10;
initial
Clk_A = 0;
always #(tPERIOD/2) Clk_A = ~ Clk_A;
测试验证程序实例
下面是 2-4 解码器和它的测试验证程序:
`timescale 1ns/1ns
module Dec2x4(A, B, Enable, Z);
input A, B, Enable;
output[0:3] Z;
wire Abar, Bbar;
not #(1, 2)
V0(Abar, A);
V1(Bbar, B);
nand #(4, 3)
N0(Z[0], Enable, Abar, Bbar);
N0(Z[1], Enable, Abar, B);
N0(Z[2], Enable, A, Bbar);
N0(Z[3], Enable, A, B);
endmodule
module Dec_Test;
reg Da, Db, Dena;
wire[0:3] Dz;
Dec2x4 D1(Da, Db, Dena, Dz);
initial
begin
Dena = 0;
Da = 0;
Db = 0;
#10 Dena = 1;
#10 Da = 1;
#10 Db = 1;
#10 Da = 0;
#10 Db = 0;
#10 $stop;
end
always@(Dena or Da or Db or Dz)
$display("At time %t, input is %b%b%b, output is %b", $time, Da, Db, Dena, Dz);
endmodule
从文本文件中读取向量
可用 $readmemb
系统任务从文本文件中读取向量(可能包含输入激励和输出期望值)。下面为测试 3 位全加器电路的例子。假定文件 "test.vec"
包含如下两个向量:
向量的前三位对应于输入 A,接下来的三位对应于输入 B,再接下来的位是进位,八到十位是期望的求和结果,最后一位是期望进位值的输出结果。下面是全加器模块和相应的测试验证程序:
module Adder1Bit(A, B, Cin, Sum, Cout);
input A, B, Cin;
output Sum, Cout;
assign Sum = (A ^ B) ^ Cin;
assign Cout = (A ^ B) | (A & Cin) | (B & Cin);
endmodule
module Adder3Bit(First, Second, Carry_In, Sum_Out, Carry_Out);
input[0:2] First, Second;
input Carry_In;
output[0:2] Sum_Out;
output Carry_Out;
wire[0:1] Car;
Adder1Bit
A1(First[2], Second[2], Carry_In, Sum_Out[2], Car[1]),
A1(First[1], Second[1], Car[1], Sum_Out[1], Car[0]),
A1(First[0], Second[0], Car[0], Sum_Out[0], Carry_Out);
endmodule
module TestBench;
parameter BITS = 11, WORDS = 2;
reg[1:BITS] Vmem[1:WORDS];
reg[0:2] A, B, Sum_Ex;
reg Cin, Cout_Ex;
integer J;
wire[0:2] Sum;
wire Cout;
Adder3Bit F1(A, B, Cin, Sum, Cout);
initial
begin
$readmemb("test.vec", Vmem);
for(J=1, J<=WORDS; J=J+1)
begin
{A, B, Cin, Sum_Ex, Cout_Ex} = Vmem[J]
#5;
if((Sum !== Sum_Ex) || (Cout !== Cout_Ex))
$display("****Mismatch on vector %b ****", Vmem[J]);
else
$display("No mismatch on vector %b", Vmem[J]);
end
end
endmodule
测试模块中首先定义存储器 Vmem
,字长对应于每个向量的位数,存储器字数对应于文件中的向量数。
实例:时序检测器
检测数据线上连续三个 1 的序列。在时钟的每个下沿检查数据,状态图如下:
module Count3_1s(Data, Clock, Detect3_1s);
input Data, Clock;
output Detect3_1s;
integer Count;
reg Detect3_1s;
initial begin
Count = 0;
Detect3_1s = 0;
end
always@(negedge Clock) begin
if(Data == 1)
Count = Count + 1;
else
Count = 0;
if(Count >= 3)
Detect3_1s = 1;
else:
Detect3_1s = 0;
end
endmodule
module Top;
reg Data, Clock;
wire Detect;
integer Out_File;
Count3_1s F1(Data, Clock, Detect);
initial begin
Clock = 0;
forever
#5 Clock = ~ Clock;
end
initial begin
Data = 0;
#5 Data = 1;
#40 Data = 0;
#10 Data = 1;
#40 Data = 0;
#20 $stop;
end
initial begin
Out_File = $fopen("results.vectors");
$montitor(Out_File, "Clock = %b, Data = %b, Detect = %b", Clock, Data, Detect);
end
endmodule