testbench tb仿真

2024-06-21 13:18
文章标签 仿真 tb testbench

本文主要是介绍testbench tb仿真,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Verilog功能模块HDL设计完成后,并不代表设计工作的结束,还需要对设计进行进一步的仿真验证。掌握验证的方法,即如何调试自己的程序非常重要。在RTL逻辑设计中,要学会根据硬件逻辑来写测试程序即写Testbench。Verilog测试平台是一个例化的待测(MUT)模块,重要的是给它施加激励并观测其输出。逻辑块与其对应的测试平台共同组成仿真模型,应用这个模型就可以测试该模块能否符合自己的设计要求。

  编写TESTBENCH的目的就是为了测试使用HDL设计的电路,对其进行仿真验证、测试设计电路的功能、性能与设计的 预期是否相符。通常,编写测试文件的过程如下:

  •        产生模拟激励(波形)
  •       将产生的激励加入到被测试模块中并观察其响应;
  •       将输出响应与期望值比较。

1. 完整的TESTBENCH文件结构

 
  1. module Test_bench()//一般简单的测试文件无输入输出

  2. 信号或变量声明定义

  3. 逻辑设计中输入信号在这里对应reg型变量

  4. 逻辑设计中的输出信号在这里对应wire型

  5. 使用initial或always语句块产生激励

  6. 例化猜测是模块UT

  7. 监控和比较输出响应

  8. endmodule

2.时钟激励产生

 下面列举一些常用的生成时钟激励的方法:

方法一: forever

 
  1. //*========================================================

  2. 50%占空比时钟

  3. ==========================================================*//

  4. parameter ClockPeriod = 10 ;

  5. initial

  6. beign

  7. clk_i = 0;

  8. forever

  9. # (ClockPeriod/2) clk_i = ~clk_i ;

  10. end

方法2: always块

 
  1. //=========================================================

  2. 50%时钟占空比

  3. ==============================================================*/

  4. Parameter ClockPeriod = 10 ;

  5. initial

  6. begin

  7. clk_i =0 ;

  8. always #(ClockPeriod/2) clk_i =~clk_i ;

  9. end

方法3:产生固定数量的时钟脉冲

 
  1. parameter CloclPeriod = 10 ;

  2. initial

  3. begin

  4. clk_i = 0 ;

  5. repeat(6)

  6. #(ClockPeriod/2) clk_i =~ clk_i;

  7. end

方法4:产生占空比非 50%的时钟

 
  1. parameter ClockPeriod = 10 ;

  2. initial

  3. begin

  4. clk_i = 0 ;

  5. forever

  6. begin

  7. #((ClockPeriod/2)-2) clk_i = 0 ;

  8. #((ClockPeriod/2)+2) clk_i = 1;

  9. end

  10. end

3.复位信号设计

方法1:异步复位

 
  1. initial

  2. begin

  3. rst_n_i = 1 ;

  4. #100 ;

  5. rst_n_i = 0 ;

  6. #100 ;

  7. rst_n_i = 1;

  8. end

方法2:同步复位

 
  1. initial

  2. begin

  3. rst_n_i = 1;

  4. @(negedge clk_i)

  5. rst_n_i = 0;

  6. #100 ; //这里给的是固定时间复位

  7. repeat(10) @(negedge clk_i) ; //这里可以设置 固定数量的时钟周期

  8. @(negedge clk_i)

  9. rst_n_i = 1;

  10. end

方法3:对复位进行任务封装

 
  1. task reset ;

  2. input[31:0] reset_timer ; //将复位的时间作为输入,达到复位时间可调的目的

  3. RST_ING = 0 ;//复位的方式可调,低电平有效或高电平有效

  4. begin

  5. rst_n = RST_ING ; //复位中

  6. #reset_time //设置的复位时间

  7. rst_n_i = ~ RST_ING ;

  8. end

  9. endtask

4.双向信号设计

双向信号的描述方式并不唯一,常用的方法如下:

描述方式1: inout在testbench中定义为wire型变量

 
  1. //为双向端口设置中间变量inout_reg作为intou的输出寄存,其中inout变量定义为wire型,使用输出使能控制

  2. //传输的方向

  3. //inout bir_port;

  4. wire birport ; //将双向接口变量定义为wire型

  5. reg bir_port_reg ; //定义一个reg型的中间变量,作为双向口的输出寄存

  6. reg bi_port_oe ; //定义输出使能,用于控制传输的方向

  7. assign birport = (bir_port_oe)?bir_port_reg:1'bz;

描述方式2:强制force

当双向端口作为输出端口时,不需要对其进行初始化,而只需开通三态门;当双向接口作为输入时,只需要对其初始化,并关闭三态门,初始化赋值需要使用wire数据,通过force命令来对双向端口进行输入赋值

 
  1. //assign dinout = (!en)din: 16'hz ; 完成双向赋值

  2. initial

  3. begin

  4. for dinout = 20 ;

  5. #200

  6. force dinout = dinout -1 ;

  7. end

5. 特殊信号设计

1.输入信号任务的封装

方便产生激励数据。

 
  1. task i_data ;

  2. input[7:0] dut_data;

  3. begin

  4. @(posedge data_en) ;send_data = 0;

  5. @(posedge data_en) ;send_data = dut_data[0];

  6. @(posedge data_en) ;send_data = dut_data[1];

  7. @(posedge data_en) ;send_data = dut_data[2];

  8. @(posedge data_en) ;send_data = dut_data[3];

  9. @(posedge data_en) ;send_data = dut_data[4];

  10. @(posedge data_en) ;send_data = dut_data[5];

  11. @(posedge data_en) ;send_data = dut_data[6];

  12. @(posedge data_en) ;send_data = dut_data[7];

  13. @(posedge data_en) ;send_data = 1;

  14. #100 ;

  15. end

  16. endtask

  17. //调用该task的方法: i_data(8'hXX) ;

2.多输入信号任务封装

 
  1. task more_input;

  2. input [7:0] a;

  3. input [7:0] b;

  4. input [31:0] times ;

  5. output[8:0] c;

  6. begin

  7. repeat(times) //等待times个时钟上升沿

  8. @(posedge clk_i)

  9. c= a+b ;//时钟上升沿, a和b相加

  10. end

  11. endtask

  12. //调用方法: more_input(x,y,t,z);

3.输入信号产生,一次SRAM写信号产生

 
  1. initial

  2. begin

  3. cs_n = 1 ; //片选无效

  4. wr_n = 1 ; //写使能无效

  5. rd_n =1 ; //读使能无效

  6. addr = 8'hxx; //地址无效

  7. data = 8'hxx; //数据无效

  8. #100 ;

  9. cs_n = 0 ;

  10. wr_n = 0 ;

  11. addr = 8'hF1 ;

  12. data = 8'h2C ;

  13. #100 ;

  14. cs_n = 1;

  15. wr_n = 1 ;

  16. #10 ;

  17. addr = 8'hxx;

  18. data = 8'hxx;

  19. end

Testbench中的 和 wait

//wait都是使用电平触发

 
  1. intial

  2. begin

  3. start = 1'b1 ;

  4. wait(en = 1'b1) ;

  5. #10;

  6. start = 1'b0 ;

  7. end

6.仿真控制语句以及系统任务描述

仿真控制语句以及系统能够任务描述: 

 
  1. $stop //停止运行仿真,modelsim中可以继续仿真

  2. $stop(n) //带参数系统任务,根据参数0,1,或2不同,输出仿真信息

  3. $finish //结束运行仿真,不可继续仿真

  4. $finish(n) //带参数系统任务,根据参数的不同:0,1或2,输出仿真信息

  5. // 0: 不输出任何信息

  6. // 1: 输出当前仿真时刻和位置

  7. // 2:输出房前仿真时刻、位置和仿真过程中用到的memory以及cpu时间的统计

  8. $random //产生随机数

  9. $random%n //产生范围-n到n之间的随机数

  10. {$random}%n //产生范围0到n之间的随机数

仿真终端显示描述

 
  1. $monitor //仿真打印输出,打印出仿真过程中的变量,使其终端显示

  2. /* $monitor($time,,,"clk = %d reset = %d out = %d",clk,reset,out); */

  3. $display //终端打印字符串,显示仿真结果等

  4. /* $display("Simulation start !");

  5. $display("At time %t,input is %b %b %b,output is %b",$time,a,b,en,z);

  6. */

  7. $time //返回64位整型时间

  8. $stime //返回32位整型时间

  9. $realtiime //实行实型模拟时间

文本输入方式:$readmemb /$readmemh

 
  1. //激励具有复杂的数据结构

  2. //verilog提供了读入文本的系统函数

  3. $readmemb/$readmemh("<数据文件名>",<存储器名>);

  4. $readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>);

  5. $readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);

  6. $readmemb:

  7. /*读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数

  8. 数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。*/

  9. $readmemh: /*读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字。*/

  10. /*当地址出现在数据文件中,格式为@hh...h,地址与数字之间不允许空白位置, 可出现多个地址*/

 
  1. module ;

  2. reg [7:0] memory[0:3];//声明8个8位存储单元

  3. integer i;

  4. initial

  5. begin

  6. $readmemh("mem.dat",memory);//读取系统文件到存储器中的给定地址 //显示此时存储器内容

  7. for(i=0;i<4;i=i+1)

  8. $display("Memory[%d]=%h",i,memory[i]);

  9. end

  10. endmodule

mem.dat的文件内容格式:

 
  1. //mem.dat文件内容

  2. @001

  3. AB CD

  4. @003

  5. A1

  6. //仿真输出为

  7. // Memory[0] = xx;

  8. // Memory[1] = AB;

  9. //Memory[2] = CD;

  10. //Memory[3] = A1;

7.加法器的仿真测试文件编写

     上面只例举了常用的 testbench 写法,在工程应用中基本能够满足我们需求,至于其他更为复杂的 testbench写法,大家可参考其他书籍或资料。 

     这里提出以下几点建议供大家参考: 

  •               封装有用且常用的 testbench,testbench 中可以使用 task 或 function 对代码进行封装,下次利用时灵活调用即可;
  •              如果待测试文件中存在双向信号(inout)需要注意,需要一个 reg 变量来表示输入,一个 wire 变量表示输出; 
  •              单个 initial 语句不要太复杂,可分开写成多个 initial 语句,便于阅读和修改;
  •              Testbench 说到底是依赖 PC 软件平台,必须与自身设计的硬件功能相搭配。

下面具体看一段程序:

 
  1. module add(a,b,c,d,e);// 模块接口

  2. input [5:0] a; // 输入信号a

  3. input [5:0] b; // 输入信号b

  4. input [5:0] c; // 输入信号a

  5. input [5:0] d; // 输入信号b

  6. output[7:0] e; // 求和输出信号

  7. wire [6:0]outa1,outa2; // 定义输出网线型

  8. assign e = outa2+outa1; // 把两部分输出结果合并

  9. /*

  10. 通常,我们模块的调用写法如下:

  11. 被调用的模块名字- 自定义的名字- 括号内信号

  12. 这里比如括号内的信号,.ina(ina1)

  13. 这种写法最常用,信号的顺序可以调换

  14. 另外还有一种写法没可以直接这样写

  15. adder u1 (ina1,inb1,outa1);

  16. 这种写法必须确保信号的顺序一致,这种写法几乎没有人采用 */

  17. adder u1 (.ina(a),.inb(b),.outa(outa1)); // 调用adder 模块,自定义名字为u1

  18. adder u2 (.ina(c),.inb(d),.outa(outa2)); // 调用adder 模块,自定义名字为u2

  19. endmodule

 
  1. //adder 子模块

  2. module adder(ina,inb,outa );// 模块接口

  3. input [5:0] ina; // ina-输入信号

  4. input [5:0] inb; // inb-输入信号

  5. output [6:0] outa; // outa-输入信号

  6. assign outa = ina + inb; // 求和

  7. endmodule // 模块结束

仿真文件:

 
  1. `timescale 1ns / 1ps

  2. module add_tb();

  3. reg [5:0] a;

  4. reg [5:0] b;

  5. reg [5:0] c;

  6. reg [5:0] d;

  7. wire[7:0] e;

  8. reg [5:0] i; //中间变量

  9. // 调用被仿真模块模块

  10. add uut (

  11. .a(a),

  12. .b(b),

  13. .c(c),

  14. .d(d),

  15. .e(e));

  16. initial

  17. begin // initial 是仿真用的初始化关键词

  18. a=0 ; // 必须初始化输入信号

  19. b=0 ;

  20. c=0 ;

  21. d=0

  22. for(i=1;i<31;i=i+1)

  23. begin

  24. #10 ; a = i; b = i; c = i; d = i;

  25. end

  26. end

  27. initial

  28. begin

  29. $monitor($time,,,"%d + %d + %d + %d ={%d}",a,b,c,d,e); // 信号打印输出

  30. #500

  31. $finish;

  32. end

  33. endmodule

仿真波形:

终端显示:

这篇关于testbench tb仿真的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081335

相关文章

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

perl的学习记录——仿真regression

1 记录的背景 之前只知道有这个强大语言的存在,但一直侥幸自己应该不会用到它,所以一直没有开始学习。然而人生这么长,怎就确定自己不会用到呢? 这次要搭建一个可以自动跑完所有case并且打印每个case的pass信息到指定的文件中。从而减轻手动跑仿真,手动查看log信息的重复无效低质量的操作。下面简单记录下自己的思路并贴出自己的代码,方便自己以后使用和修正。 2 思路整理 作为一个IC d

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑燃料电池和电解槽虚拟惯量支撑的电力系统优化调度方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python

Matlab simulink建模与仿真 第十章(模型扩展功能库)

参考视频:simulink1.1simulink简介_哔哩哔哩_bilibili 一、模型扩展功能库中的模块概览         注:下面不会对Block Support Table模块进行介绍。 二、基于触发的和基于时间的线性化模块 1、Trigger-Based Linearization基于触发的线性化模块 (1)每次当模块受到触发时,都会调用linmod或者dlinmod函数

AMEsim和Simulink联合仿真生成新的.mexw64液压模型文件

AMEsim和Simulink进行联合仿真非常重要的就是AMEsim经过第四阶段Simulation会在相同文件下面生成一个与AMEsim液压模型相同名字的.mexw64文件,在Simulink进行联合仿真的S-Function需要找的也就是这个文件,只不过输入的时候除了液压模型名字之外,后面有一个短下划线。 简而言之: AMEsim和Simulink联合仿真, 首先是需要AMEsim软

【自动驾驶】控制算法(八)横向控制Ⅱ | Carsim 与 Matlab 联合仿真基本操作

写在前面: 🌟 欢迎光临 清流君 的博客小天地,这里是我分享技术与心得的温馨角落。📝 个人主页:清流君_CSDN博客,期待与您一同探索 移动机器人 领域的无限可能。 🔍 本文系 清流君 原创之作,荣幸在CSDN首发🐒 若您觉得内容有价值,还请评论告知一声,以便更多人受益。 转载请注明出处,尊重原创,从我做起。 👍 点赞、评论、收藏,三连走一波,让我们一起养成好习惯😜 在这里,您将

Matlab/Simulink和AMEsim联合仿真(以PSO-PID算法为例)

目录 安装软件和配置环境变量 Matlab/Simulink和AMEsim联合仿真详细流程 非常重要的一点 Simulink模型和AMEsim模型用S-Function建立连接 从AMEsim软件打开Matlab Matlab里的设置 Matlab的.m文件修改(对于PSO-PID算法) 运行程序 我印象中好像做过Matlab/Simulink和AMEsim联合仿真的分享似的

基于SA模拟退火算法的多车辆TSP问题求解matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述        基于SA模拟退火算法的多车辆TSP问题求解matlab仿真,三个车辆分别搜索其对应的最短路径,仿真后得到路线规划图和SA收敛曲线。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 (完整程序运行后无水印)

modelsim仿真流程

modelsim仿真流程 1、建立工程 project new "../prj" test.mpf 2、添加rtl文件 project addfile "../test.v" verilog 3、建立仿真库 vlib work 4、编译rtl到仿真库中 vlog -sv -sv09compat +define+T133 +incdir+"../rtl" test.v -wo

Matlab simulink建模与仿真 第八章(数学运算库)【下】

参考视频:simulink1.1simulink简介_哔哩哔哩_bilibili 六、圆整函数及最值函数模块 1、Rounding Function圆整函数模块 圆整函数模块的功能是将小数转换为整数,它提供了四种取整方式: ①floor:向下取整,输出距离输入小数在负无穷方向上最近的整数。 ②ceil:向上取整,输出距离输入小数在正无穷方向上最近的整数。 ③round:四舍五入,输出距