verilog之testbench的写法

这篇具有很好参考价值的文章主要介绍了verilog之testbench的写法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为什么要编写testbench?

编写Testbench的目的是把RTL代码在Modsim中进行仿真验证,通过查看仿真波形和打印信息验证代码逻辑是否正确。

verilog之testbench的写法,fpga开发
testbench在这里做的就是给输入进行激励
具体逻辑为:
1.使用testbench给模块喂入输入信号(一般在testbench中定义为reg型)
2.通过实例化模块(注意实例化模块时带partameter的写法)把模拟输入信号传入功能模块中

testbench的基本模块

module写法

  • 测试模块的命名:tb_<功能模块名> 或 <功能模块名>_tb
    功能模块名为:delay_clap , 则对应测试模块名为:delay_clap_tb
    需要定义模拟的输入/输出信号:

  • 输入/输出信号与功能模块中定义的输入/输出信号保持一致
    输入信号一般定义为 reg 型信号,因为后面需要在always/initial语句块中被赋值
    输出信号一般为 wire型即可

以边沿检测器为例

//原边沿检测器module部分
module delay_clap (
	    input sig,//异步信号
	    input clk1,//异步慢时钟
	    input rstn,//复位信号
	    input clk2,//目标快时钟
	    output sig_rise,//上升沿的信号
	    output sig_down,//下降沿的信号
	    output sig_out);//直接输出的信号

//具体内容

endmodule

//对应的testbench
module delay_clap_tb;
	reg rstn;
	reg clk1;
	reg clk2;
	reg sig;
	
	wire sig_rise;
	wire sig_down;
	wire sig_out;

//具体内容

endmodule

显然:在定义端口时,testbench中对所有输入都定义为reg型,所有输出都定义为wire型。

生成时钟

特定频率的时钟

对于生成特定频率的时钟,一般会在testbench的最开始设定时间单位和时间精度,例如:

`timescale <时间单位>/<时间精度>
`timescale 1ns/1ps

通过规定timescale,来确定生成信号的时间单位,从而确定具体频率

  • 时间单位:时间尺度预编译指令 时间单位 / 时间精度
  • 定义时间单位: `timescale 1ns/1ns 表示时间单位为1ns,时间精度为1ns
  • 时间单位和时间精度由值 1、10、和 100 以及单位 s、ms、us、ns、ps 和 fs 组成
  • 时间单位不能比时间精度小
  • 仿真过程所有与时间相关量的单位(即1单位的时间)
  • 时间精度:决定时间相关量的精度及仿真显示的最小刻度 1ns/1ps 精度为.0.01ns

比如:要产生250MHZ的时钟信号的计算过程如下:
250MHZ的时钟信号一个周期的时长为: 1 / ( 250 × 1 0 6 ) = 4 n s 1/(250\times10^6)=4ns 1/(250×106)=4ns
则说明该时钟生成的条件应该为声明1ns为时间单位,每2个时间单位翻转一次

生成时钟的写法
// Declare a clock period constant.
Parameter ClockPeriod = 10;

// Clock Generation method 1:
initial begin
forever Clock = #(ClockPeriod / 2) ~ Clock;
end

// Clock Generation method 2:
initial begin
always #(ClockPeriod / 2) Clock = ~Clock;
end

也可以不声明参数,直接写数字

//生成250MHZ的时钟写法
`timescale 1ns/1ps
initial clk='b0;
always #2 clk = ~clk;

生成测试激励

只有给设计激励输入,才能得到验证结果。
提供激励的方法有两种:绝对时间激励相对时间激励

  • 激励信号初始化
    用initial 语句进行初始化,该语句中的代码块只执行一次
    根据需要初始化为0/1都可
绝对时间激励

绝对时间激励是指以仿真时刻0为基准,给信号赋值。
使用 #用于指定等待的延迟时间,之后才会执行下一个激励
这里是指等待的延迟时间,而非绝对时间!

initial begin
    rstn = 0;
    sig = 0;
    #200 //在绝对时间200时间单位时给rstn赋值
    rstn = 1;
    #800//
    sig = 1;
    #1100
    sig = 0;
    #1400
    rstn = 0;
    #1600
    rstn = 1;
    #2000
    sig = 1;
    #3000
    sig = 0;
    #20000
    $stop;
end
相对时间激励

相对时间激励给信号一个初始值,直到某一事件发生后才触发激励赋值

输入信号与系统时钟关联
//sig是由clk1控制的信号,在上升沿发生改变
always @(posedge clk1) begin
    sig <= sig;
end
//在RTL代码中定义的输入信号in
module rtl_module(
    input wire in
);
    //
endmodule


//在testbench中对输入信号进行模拟的方式为:
`timescale 1 ns/ 1 ps
module tb_rtl_module(); 
reg in; 
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(sys_rst_n == 1'b0)
		in<= 1'b0;
	else
		in<= {$random} % 2;
end 
rtl_module rtl_module_int1 (
    .in(in)
)
endmodule
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

实例化模块

通过实例化模块把模拟输入信号传入功能模块中,即在测试中将原模块实例化,然后将输入信号与输出信号填入对应的端口
这里注意有参数的端口实例化

    hand_pulse_syn #(.pulse_init(pulse_init)) hand_pulse_syn_inst(.clk_fast(clk_fast),
        .clk_slow(clk_slow),
        .rstn(rstn),
        .pulse_fast(pulse_fast),
        .pulse_slow(pulse_slow));

这里pulse_init要记得声明一个parameter和原模块中一致

仿真参数重定义

在实际仿真中,我们没有必要按照实际的计数器值进行仿真,这将给仿真调试带来不便,此时我们只需改仿真参数为较小的数,能方便的看出输入输出的关系即可

参数传递的方式

即在仿真文件的顶层模块中给每个参数传入新的值!如下所示:

rom_ip 
#(
    .CNT_200MS_MAX      (199)    ,
    .CNT_256_MAX        (9)      ,
    .CNT_KEYFILTER_MAX  (9)       ,
    .CUNT_SCAN_MAX      (99)	  , 
    .CNT_SHIFT_MAX      (21)               
)
rom_ip_inst (
    port
);

这种写法要求参数要从最顶层模块传到最底层模块,每一级都需写参数列表,当参数过多时会造成不便!

使用defparam命名

用defparam命令重定义每个子模块中的仿真参数,这样比较直观,且可以对任意子模块的参数进行设置,较为方便。

// 重定义仿真参数的方法
defparam	rom_ip_inst.rom_rader_inst.CNT_200MS_MAX 			= 199;
defparam	rom_ip_inst.rom_rader_inst.CNT_256_MAX   			= 9;
defparam	rom_ip_inst.key1_filter_inst1.COUNTER_MAX   		= 9;
defparam	rom_ip_inst.key1_filter_inst2.COUNTER_MAX   		= 9;
defparam	rom_ip_inst.dynamic_seg_main_inst1.CUNT_SCAN_MAX   	= 99;
defparam	rom_ip_inst.dynamic_seg_main_inst1.CNT_SHIFT_MAX   	= 99;

语法如下:

defparam	顶层模块实例化名.子模块1实例化名.子模块1的子模块实例化名  = 值;

用always语句实现信号在仿真过程中的电平变化

  • always在仿真过程中将被多次执行
  • always #10 in <= {$random} % 8; 表示每隔10个时间单位in的电平变化一次:
  • {$random}%8 表示随机选取[0,7]之间的数
  • in <= {$random} % 8; 在赋值时会自动进行数据类型转换
  • always后面最好只有一条语句

testbench调用RTL代码中寄存器变量的方法

基本语法:实例化的模块名.变量名
如:在RTL代码中定义了变量 state

module rtl_module(
    port
);
// 定义状态寄存器
reg [2:0]state;
 
endmodule
`timescale 1 ns/ 1 ps
module tb_rtl_module(
    port
);
// 获取rtl代码中的变量
wire [2:0] state = rtl_module_int1.state;
 
// 实例化的模块
rtl_module rtl_module_int1 (
    .port(port)
)
endmodule

注意:testbench中接收的变量要定义为wire型!

查看仿真结果

常见的波形文件类型

常见的波形文件类型一般有两种,VCD和fsdb

fsdb文件

如果使用modelsim,一般要安装debussy配合使用,因为modelsim不认识fsdb文件
因此需要安装debussy再生成fsdb文件

vcd文件

目前正在使用
VCD 文件是在对设计进行的仿真过程中,记录各种信号取值变化情况的信息记录文件。 EDA
工具通过读取 VCD 格式的文件,显示图形化的仿真波形,所以,可以把 VCD 文件简单地视为波形记录文件.)下面分别描述它们的用法并举例说明之。
目前使用$dumpfile$dumpvar两个系统函数分别生成vcd文件名称和要记录的信号

$dumpfile 系统任务:为所要创建的 VCD 文件指定文件名。
举例( "//"符号后的内容为注释文字):
initial
$dumpfile ("myfile.dump"); //指定 VCD 文件的名字为 myfile.dump,仿真信息将记录到此文件
$dumpvar 系统任务:指定需要记录到 VCD 文件中的信号,可以指定某一模块层次上的所有信号,也可以单独指定某一
个信号。
典型语法为$dumpvar(level, module_name); 参数 level 为一个整数, 用于指定层次数, 参数 module 则指定要记录的模块。
整句的意思就是,对于指定的模块,包括其下各个层次(层次数由 level 指定)的信号,都需要记录到 VCD 文件中去。


举例:
  initial
    $dumpvar (0, top); //指定层次数为 0,则 top 模块及其下面各层次的所有信号将被记录

  initial
    $dumpvar (1, top); //记录模块实例 top 以下一层的信号
  //层次数为 1,即记录 top 模块这一层次的信号
  //对于 top 模块中调用的更深层次的模块实例,则不记录其信号变化

  initial
    $dumpvar (2, top); //记录模块实例 top 以下两层的信号

  //即 top 模块及其下一层的信号将被记录
  假设模块 top 中包含有子模块 module1,而我们希望记录 top.module1 模块以下两层的信号,则语法举例如下:
  initial
    $dumpvar (2, top.module1); //模块实例 top.module1 及其下一层的信号将被记录
  假设模块 top 包含信号 signal1 和 signal2(注意是变量而不是子模块), 如我们希望只记录这两个信号,则语法举例如下:

  initial
    $dumpvar (0, top.signal1, top.signal2); //虽然指定了层次数,但层次数是不影响单独指定的信号的
  • 对单个的.v文件中的module一般用法:
/*iverilog */
initial
begin            
    $dumpfile("delay_wave.vcd");        //生成的vcd文件名称
    $dumpvars(0, delay_clap_tb);    //tb模块名称
end
/*iverilog */
通过VScode使用iverilog和gtkwave联合仿真
  • 在VScode中新建终端,进入相应文件夹
  • 在命令行输入如下命令
/***************编译*************************/
iverilog -o wave led_demo_tb.v led_demo.v//对源文件和仿真文件进行语法规则检查和编译
//如果调用了多个.v文件
iverilog -o wave -y ./ led_demo.v led_demo_tb.v

/**************生成波形文件**************************/
vvp -n wave -lxt2//生成wave.vcd文件(波形文件)
//如果要改名字,wave可以改成想要的名字

/**************打开波形文件**************************/
gtkwave wave.vcd //wave.vcd是波形文件名称
制作批量文件

通过批处理文件,可以简化编译仿真的执行过程,直接一键执行编译和仿真。新建文本文档,输入以下内容:

echo "开始编译"
iverilog -o wave led_demo.v led_demo_tb.v
echo "编译完成"
vvp -n wave -lxt2
echo "生成波形文件"
cp wave.vcd wave.lxt
echo "打开波形文件"
gtkwave wave.lxt
pause

文件扩展名需要更改,Windows系统保存为.bat格式,然后双击即可运行!

###testbench完整代码举例
原模块:delay_clap.v(边沿检测器)

//边沿检测器
module delay_clap (
    input sig,//异步信号
    input clk1,//异步慢时钟
    input rstn,//复位信号
    input clk2,//目标快时钟
    output sig_rise,//上升沿的信号
    output sig_down,//下降沿的信号
    output sig_out);//直接输出的信号
reg [2:0] sig3_r;//三级缓存,前两级用于同步,后两节用于边沿检测
always @(posedge clk2 or negedge rstn) begin
    if(!rstn)
    begin
        sig3_r <= 3'b000;
    end
    else
    begin
        sig3_r <= {sig3_r[1:0],sig};
    end
end
assign sig_rise = sig3_r[1] && !sig3_r[2];//上升沿检测,第一个时刻是低,第二个时刻是高
assign sig_down = sig3_r[2] && !sig3_r[1];//下降沿检测
assign sig_out = sig3_r[1];//这里为什么是直接输出?==>使用的是电平同步:打两拍在第二拍输出,降低亚稳态概率

endmodule

testbench delay_clap_tb.v

//边沿检测器
module delay_clap_tb;
reg rstn;
reg clk1;
reg clk2;
reg sig;

wire sig_rise;
wire sig_down;
wire sig_out;

delay_clap delay_clap_inst(
    .rstn(rstn),
    .clk1(clk1),
    .clk2(clk2),
    .sig(sig),

    .sig_rise(sig_rise),
    .sig_down(sig_down),
    .sig_out(sig_out)
);

/*iverilog */
initial
begin            
    $dumpfile("delay_wave.vcd");        //生成的vcd文件名称
    $dumpvars(0, delay_clap_tb);    //tb模块名称
end
/*iverilog */

initial clk1 = 0;
always #30 clk1 = ~clk1;
always @(posedge clk1) begin
    sig <= sig;
end


initial clk2 = 0;
always #10 clk2 = ~clk2;

initial begin
    rstn = 0;
    sig = 0;
    #200
    rstn = 1;
    #800
    sig = 1;
    #1100
    sig = 0;
    #1400
    rstn = 0;
    #1600
    rstn = 1;
    #2000
    sig = 1;
    #3000
    sig = 0;
    #20000
    $stop;

end
endmodule

生成波形图
verilog之testbench的写法,fpga开发

部分参考来源: Verilog十大基本功2(testbench的设计 文件读取和写入操作 源代码).文章来源地址https://www.toymoban.com/news/detail-614738.html

到了这里,关于verilog之testbench的写法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • m基于FPGA的桶形移位寄存器verilog实现,包含testbench

    目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1、桶形移位寄存器的基本原理 2.2、桶形移位寄存器的数学模型 2.3、桶形移位寄存器的实现步骤 3.Verilog核心程序 4.完整算法代码文件 本系统进行了两个平台的开发,分别是: Vivado2019.2 Quartusii18.0+ModelSim-Altera 6.6d  Starter Edition 其

    2024年02月04日
    浏览(40)
  • 基于FPGA的二维DCT变换和逆变换verilog实现,包含testbench

    目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 数据导入到matlab显示图像 vivado2019.2 matlab2022a         离散余弦变换(Discrete Cosine Transform,DCT)是一种广泛应用于图像和信号处理领域的变换技术。在图像处理中,DCT常被用于

    2024年02月21日
    浏览(24)
  • m基于FPGA的8PSK调制解调系统verilog实现,包含testbench测试文件

    目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 8PSK调制原理 2.2 基于FPGA的8PSK调制解调器设计和实现 3.Verilog核心程序 4.完整算法代码文件获得 vivado仿真结果如下: 借助matlab看8PSK的星座图:         随着通信技术的不断发展,相位调制技术因其高频谱效率和抗干扰能力而广

    2024年02月05日
    浏览(35)
  • m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench

    目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 PPM调制解调原理 2.2 基于FPGA的PPM系统实现 3.Verilog核心程序 4.完整算法代码文件获得 vivado2019.2仿真结果如下:          基于FPGA的PPM(脉冲位置调制)光学脉位调制解调系统是一个复杂的电子与光电子相结合的通信系统。    

    2024年02月03日
    浏览(37)
  • m基于FPGA的BPSK调制解调通信系统verilog实现,包含testbench,包含载波同步

    目录 1.算法仿真效果 2.算法涉及理论知识概要 3.verilog核心程序 4.完整算法代码文件 vivado2019.2仿真结果如下:        对比没载波同步和有载波同步的仿真效果,我们可以看到,当不存在载波同步时,数据的包络会有一个缓慢的类正弦变换,这是由于存在频偏导致的。而当加

    2024年02月16日
    浏览(34)
  • m基于FPGA的QPSK调制解调通信系统verilog实现,包含testbench,不包含载波同步

    目录 1.算法仿真效果 2.算法涉及理论知识概要 3.Verilog核心程序 4.完整算法代码文件 本系统进行了两个平台的开发,分别是: Vivado2019.2 Quartusii18.0+ModelSim-Altera 6.6d  Starter Edition 其中Vivado2019.2仿真结果如下:  Quartusii18.0+ModelSim-Altera 6.6d  Starter Edition的测试结果如下:        

    2024年02月12日
    浏览(34)
  • 基于FPGA的16QAM调制器verilog实现,包括testbench,并通过MATLAB显示FPGA输出信号的星座图

    目录 1.算法仿真效果 2.verilog核心程序 3.算法涉及理论知识概要 4.完整verilog matlab2022a/vivado2019.2仿真结果如下:  将FPGA仿真的数据导出,然后在matlab中将数据通过噪声之后,可以得到如下的星座图效果。 fpga工程版本信息:       16QAM全称正交幅度调制是英文Quadrature Amplitude

    2024年02月07日
    浏览(31)
  • m基于FPGA的64QAM调制解调通信系统verilog实现,包含testbench,不包含载波同步

    目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1、64QAM调制解调系统的设计 2.1 信号生成 2.2 信号调制 2.3 信号解调 3.Verilog核心程序 4.完整算法代码文件 本系统进行了两个平台的开发,分别是: Vivado2019.2 Quartusii18.0+ModelSim-Altera 6.6d  Starter Edition 其中Vivado2019.2仿真结果如下:

    2024年02月01日
    浏览(38)
  • m基于FPGA的Hamming汉明编译码verilog实现,包含testbench测试文件,不使用IP核

    目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 Hamming编码过程 2.2 Hamming解码与纠错 2.3 FPGA实现 3.Verilog核心程序 4.完整算法代码文件 本系统进行了Vivado2019.2平台的开发,测试结果如下:        在现代数字通信和存储系统中,错误检测和纠正(Error Detection and Correction, EDC)机

    2024年01月23日
    浏览(26)
  • m基于FPGA的FOC控制器verilog实现,包括CLARK,PARK,PID及SVPWM,含testbench

    目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件 Quartus II 12.1(64-Bit) ModelSim-Altera 6.6d Starter Edition 仿真结果如下: 整个系统的结构如下所示: 1、采集到两相电流 2、经过clarke变换后得到两轴正交电流量, 3、经过旋转变换后得到正交的电流量

    2024年02月15日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包