完成了C语言函数(function
)或Verilog功能模块(module
)的编写,接下来我们需要对其进行测试、仿真等手段来验证函数或模块的正确性。本篇博文介绍为一个给定的Verilog模块编写仿真模块,也就是所谓testbench
的方法。
我们为上一篇博文【0基础学会Verilog】002. Verilog时序逻辑实现C语言函数所述的calc_wire()
以及calc_reg()
两种类型的Verilog模块编写相关testbench。
1. 为C语言的calc()编写测试模块
还是先以C语言函数的测试为例,为了测试calc_v2()
函数模块,我们需要编写一个完整的可执行程序,程序内部调用我们准备测试的calc_v2()
函数。
为方便比对,这里我们将待测函数calc_v2()
代码列在此处。
//使用指针的C语言函数,计算表达式((a+b) - c * 8)的值
void calc_v2 (
int a,
int b,
int c,
int* sum)
{
int tmp = 0;
tmp = (a+b) - c * 8;
*sum = tmp;
}
下面代码我们编写一个console程序,为简单起见只有main()函数。从代码分析可以看出一个测试模块的编写主要分为三个部分:
- 给出待测函数要求的输入参数;
- 调用待测函数并提取运算结果;
- 显示函数计算的结果用于比对和验证。
//C语言函数calc_v2()的测试程序
void main(void){
//1. 给出待测函数要求的输入参数
int aa = 3;
int bb = 4;
int cc = 7;
//2. 调用待测试函数,并将结果保存到整数dd中
int dd = calc(aa,bb,cc);
//3. 显示函数计算的结果用于比对和验证
printf("(%d +%d)-%d*8 = %d",aa,bb,cc,dd);
}
2. 为calc_wire()模块编写Testbench
用Verilog编写的硬件模块设计代码最终是作为FPGA整体设计的一部分,类似于搭积木块的形式完成整体功能。涉及硬件的调试往往代价比较高,无论是时间成本和经济成本都较高。
因此,独立功能的硬件模块代码需要预先充分验证、测试,以保证其功能和性能满足要求,才能集成到系统内部。这也是学习Verilog硬件设计时我们一定要先学会为指定模块编写testbench的原因。
下面的代码完成类似上面C语言的main()
函数的功能,给定相关的输入数据,调用待测模块,得到模块(/函数)的输出加以验证。
验证可以是自动的,即用所谓Golden Data
与模块的输出进行自动比对,直接给出结论。简单的情况下也可以只输出模块的结果,由设计者或测试者自己判断。特别是在调试阶段,对于简单的模块一般采取后者即可。至于自动验证方法,功能强大,但一般比较繁琐,本专栏的主要目标是带领大家学习Verilog的语法,就暂时不涉及完善的测试模块实现,以后另开篇幅介绍验证语言SystemVerilog。
为了方便比对学习,同样,我们也把待测的Verilog模块源码列于此处。
//用组合逻辑实现与calc_v2()函数相同的功能
module calc_wire(
input wire [31:0] a,
input wire [31:0] b,
input wire [31:0] c,
output wire [31:0] sum
);
wire [31:0] tmp;
assign tmp = (a+b) - c * 8;
assign sum = tmp;
endmodule
下面我们开始为calc_wire()
编写仿真模块(testbench
)文章来源:https://www.toymoban.com/news/detail-854032.html
`timescale 1ns/1ps
//timescale代表时间尺度
//时间单位:1ns, 时间精度:1ps
//下面代码中``#``号后面的数字就是时间,单位是ns
//例如,``#200``表示等待200ns才继续下一个操作
//模块名的声明
//注意:仿真的顶层模块是没有输入和输出参数
//比如下面一行的();表示参数为空
module calc_wire_testbench();
//1. 为待测模块要求的每个输入信号(input)
// 声明一个对应的reg型变量
reg [31:0] aa;
reg [31:0] bb;
reg [31:0] cc;
//2. 为待测模块要求的每个输出信号(output)
// 声明一个对应的wire型变量
wire [31:0] sum;
//3. 使用initial块为输入信号变量赋值
initial begin
aa = 3;
bb = 4;
cc = 7;
end
//4. 调用待测试模块(DUT:Design Under Test),
// 将信号aa,bb,cc的值分别传递给模块的输入参数a,b,c
// 并将模块运算结果通过sum提取出来
calc_wire dut(
.a(aa),
.b(bb),
.c(cc),
.sum(sum)
);
//5. 使用initial块显示模块运算的结果,
// 并在等待一段时间后结束仿真
initial begin
#300; //等待 300ns
$display("result of ((%d +%d) - %d * 8) is %d",
aa,bb,cc,sum);
$finish; //结束仿真
end
endmodule
3. 为calc_reg()模块编写Testbench
//时间单位:1ns, 时间精度:1ps
`timescale 1ns/1ps
module calc_reg_testbench();
//1. 时序逻辑需要至少一个时钟信号
// 因此,testbench必须生成一个虚拟的时钟信号
reg clk;
initial begin
clk = 0;
forever begin //死循环
#5; //wait 5ns
clk = ~clk; //invert the clk
end
end
//2. 给出待测模块需要的输入参数
reg [31:0] aa;
reg [31:0] bb;
reg [31:0] cc;
initial begin
aa = 3;
bb = 4;
cc = 7;
end
//3. 调用待测试函数,并将结果保存到整数dd中
wire [31:0] sum;
calc_wire dut(
.clk(clk), //时序逻辑必须输入时钟信号
.a(aa),
.b(bb),
.c(cc),
.sum(sum)
);
//4. 显示函数计算的结果用于比对和验证
initial begin
#300;
$display("(%d +%d) - %d * 8 = %d",aa,bb,cc,sum);
$finish;
end
endmodule
4. 用Vivado自带仿真器进行验证
5. 扩展练习
下面的C语言函数calc_v3()
基于指针实现多输入和多输出的功能,完成以下作业:文章来源地址https://www.toymoban.com/news/detail-854032.html
- 用组合逻辑将下面的C语言函数转换为Verilog语言的wire版硬件模块
calc_v3_wire()
; - 用时序逻辑将下面的C语言函数转换为Verilog语言的reg版硬件模块
calc_v3_reg()
; - 编写用于测试C语言函数
calc_v3()
的C语言程序主函数main()
; - 为wire版Verilog模块
calc_v3_wire()
编写Testbench; - 为reg版Verilog模块
calc_v3_reg()
编写Testbench;
//实现多输入和多输出的功能模块
void calc_v3(
//输入参数
int a, int b, int c,
//输出参数
int* d, int* e)
{
//计算d的值并输出
int dd = 0;
dd = (a+b) - c * 8;
*d = dd;
//计算e的值并输出
int ee = 0;
ee = (a+c) - (b << 2);
*e = ee;
return;
}
到了这里,关于【0基础学会Verilog】003. 为Verilog模块编写测试模块testbench的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!