草庐IT

TestBench基本写法与语法详解

ZHE980121 2023-04-09 原文

一、TestBench简介

一个完整的设计,除了好的功能描述代码,对于程序的仿真验证是必不可少的。学会如何去验证自己所写的程序,即如何调试自己的程序是一件非常重要的事情。而 RTL 逻辑设计中,学会根据硬件逻辑来写测试程序,即Testbench 是尤其重要的。 Verilog 测试平台是一个例化的待测(MUT) 模块,重要的是给它施加激励并观测其输出。逻辑模块与其对应的测试平台共同组成仿真模型,应用这个模型可以测试该模块能否符合自己的设计要求。

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

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

二、完整的 Test bench文件结构

通常,一个完整的测试文件其结构为

`timescale 仿真单位/仿真精度

module Test_bench();//通常无输入无输出

信号或变量声明定义
逻辑设计中输入对应 reg 型
逻辑设计中输出对应 wire 型
使用 initial 或 always 语句产生激励
例化待测试模块
监控和比较输出响应

endmodule

声明仿真的单位和精度






三、时钟激励设计

下面列举出一些常用的封装子程序,这些是常用的写法,在很多应用中都能用到。

/*----------------------------------------------------------------
时钟激励产生方法一: 50%占空比时钟
----------------------------------------------------------------*/
parameter ClockPeriod=10;
initial
	begin
		clk_i=0;
		forever
		#(ClockPeriod/2) clk_i=~clk_i;
	
	end
/*----------------------------------------------------------------
时钟激励产生方法二: 50%占空比时钟
----------------------------------------------------------------*/
initial
	begin
		clk_i=0;
		always #(ClockPeriod/2) clk_i=~clk_i;
	end
/*----------------------------------------------------------------
时钟激励产生方法四:产生固定数量的时钟脉冲
----------------------------------------------------------------*/
initial
	begin
		clk_i=0;
		repeat(6)
		#(ClockPeriod/2) clk_i=~clk_i;
	end
/*----------------------------------------------------------------
时钟激励产生方法五:产生非占空比为 50%的时钟
----------------------------------------------------------------*/
initial
	begin
		clk_i=0;
		forever
				begin
					#((ClockPeriod/2)-2) clk_i=0;
					#((ClockPeriod/2)+2) clk_i=1;
				end
	end

四、复位信号设计

/*----------------------------------------------------------------
复位信号产生方法一:异步复位
----------------------------------------------------------------*/
initial
	begin
		rst_n_i=1;
		#100;
		rst_n_i=0;
		#100;
		rst_n_i=1;
	end
/*----------------------------------------------------------------
复位信号产生方法二:同步复位
----------------------------------------------------------------*/
initial
	begin
		rst_n_i=1;
		@(negedge clk_i)
		rst_n_i=0;
		#100; //固定时间复位
		repeat(10) @(negedge clk_i); //固定周期数复位
		@(negedge clk_i)
		rst_n_i=1;
	end
/*----------------------------------------------------------------
复位信号产生方法三:复位任务封装
----------------------------------------------------------------*/
task reset;
	input [31:0] reset_time; //复位时间可调,输入复位时间
	RST_ING=0; //复位方式可调,低电平或高电平
		begin
			rst_n=RST_ING; //复位中
			#reset_time; //复位时间
			rst_n_i=~RST_ING; //撤销复位,复位结束
		end
endtask

五、双向信号设计


/*----------------------------------------------------------------
双向信号描述一: inout 在 testbench 中定义为 wire 型变量
----------------------------------------------------------------*/

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

//inout bir_port;
wire bir_port;
reg bir_port_reg;
reg bi_port_oe;
assign bi_port=bi_port_oe ? bir_port_reg : 1'bz;

/*----------------------------------------------------------------
双向信号描述二:强制 force
----------------------------------------------------------------*/

//当双向端口作为输出口时,不需要对其进行初始化,而只需开通三态门
//当双向端口作为输入时,只需要对其初始化并关闭三态门,初始化赋值需
//使用 wire 型数据,通过 force 命令来对双向端口进行输入赋值
//assign dinout=(!en) din :16'hz; 完成双向赋值

initial
	begin
		force dinout=20;
		#200
		force dinout=dinout-1;
	end
	

六、 特殊信号设计


/*----------------------------------------------------------------
特殊激励信号产生描述一:输入信号任务封装
----------------------------------------------------------------*/

task i_data;
input [7:0] dut_data;
begin
	@(posedge data_en); send_data=0;
	@(posedge data_en); send_data=dut_data[0];
	@(posedge data_en); send_data=dut_data[1];
	@(posedge data_en); send_data=dut_data[2];
	@(posedge data_en); send_data=dut_data[3];
	@(posedge data_en); send_data=dut_data[4];
	@(posedge data_en); send_data=dut_data[5];
	@(posedge data_en); send_data=dut_data[6];
	@(posedge data_en); send_data=dut_data[7];
	@(posedge data_en); send_data=1;
	#100;
end
endtask

//调用方法: i_data(8'hXX);
/*----------------------------------------------------------------
特殊激励信号产生描述二:多输入信号任务封装
----------------------------------------------------------------*/

task more_input;
	input [7:0] a;
	input [7:0] b;
	input [31:0] times;
	output [8:0] c;
		begin
			repeat(times) //等待 times 个时钟上升沿
			@(posedge clk_i)
			c=a+b; //时钟上升沿 a, b 相加
		end
endtask

//调用方法: more_input(x,y,t,z); //按声明顺序
/*----------------------------------------------------------------
特殊激励信号产生描述三:输入信号产生,一次 SRAM 写信号产生
----------------------------------------------------------------*/

initial
	begin
		cs_n=1; //片选无效
		wr_n=1; //写使能无效
		rd_n=1; //读使能无效
		addr=8'hxx; //地址无效
		data=8'hzz; //数据无效
		#100;
		cs_n=0; //片选有效
		wr_n=0; //写使能有效
		addr=8'hF1; //写入地址
		data=8'h2C; //写入数据
		#100;
		cs_n=1;
		wr_n=1;
		#10;
		addr=8'hxx;
		data=8'hzz;
	end

/*----------------------------------------------------------------
Testbench 中@与 wait
----------------------------------------------------------------*/

//@使用沿触发
//wait 语句都是使用电平触发
initial
	begin
		start=1'b1;
		wait(en=1'b1);
		#10;
		start=1'b0;
	end

七、repeat ,wait函数

//==========================================
//==    repeat重复执行
//==========================================
initial begin
    start = 1;
    repeat(5) @(posedge clk)    //等待5个时钟上升沿
    start = 0;
end

initial begin
	repeat(10)begin
		...//执行10次
	end 
end

//===========================================
//==    wait为电平触发
//==========================================
initial begin
    start = 1;
    wait(en);                     //等待en==1
    start = 0;
end

八、随机数的产生


$random         //产生随机数
$random % n     //产生范围 {-n,n} 的随机数
{$random} % n   //产生范围 { 0,n} 的随机数

九、文本输入输出


reg [a:0] data_mem [0:b];  //定义位宽为(a+1)深度为(b+1)的存储器
$readmemb/$readmemh("<读入文件名>",<存储器名>);
$readmemb/$readmemh("<读入文件名>",<存储器名>,<起始地址>);
$readmemb/$readmemh("<读入文件名>",<存储器名>,<起始地址>,<结束地址>);

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

$readmemh
/*------------------------------------------------------------------------*\
  读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数
  数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字.
\*------------------------------------------------------------------------*/
//==========================================================================
//==    输出txt文件
//==========================================================================
integer fp_write;                                  //定义
	initial  begin
		begin
			fp_write = $fopen("output.txt");  //打开输出文件
			begin
				$fwrite(fp_write, "\n%h", output_data); //写入数据16进制
				#(`clk_period);
			end
		end
		$fclose(fp_write);   //关闭文件,不可少
end


十、打印信息

$monitor      //仿真打印输出,打印出仿真过程中的变量,使其终端显示
/*------------------------------------------------------------------------*\
  $monitor($time,,,"clk=%d reset=%d out=%d",clk,reset,out);
\*------------------------------------------------------------------------*/

$display      //终端打印字符串,显示仿真结果等
/*------------------------------------------------------------------------*\
  $display(” Simulation start ! ");
  $display(” At time %t,input is %b%b%b,output is %b",$time,a,b,en,z);
\*------------------------------------------------------------------------*/

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

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

$realtime     //实行实时模拟时间

十、testbench总体代码结构

`timescale 1ns/1ps  	//时间精度
`define clk_perilod 20	//时钟周期可变

module test_file_tb;

//==================<端口>==================================================
reg                         clk                 ; //时钟,50Mhz
reg                         rst_n               ; //复位,低电平有效
reg  [XX:0]                 in                  ; //
wire [XX:0]                 out                 ; //

//--------------------------------------------------------------------------
//--    模块例化
//--------------------------------------------------------------------------
my_design u_my_design
(
    .clk                    (clk                ),
    .rst_n                  (rst_n              ),
    .in                     (in                 ),
    .out                    (out                )
);

//----------------------------------------------------------------------
//--    时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
    clk = 0;
    forever
        #(`Clock/2) clk = ~clk;
end

initial begin
    rst_n = 0; #(`Clock*20+1);
    rst_n = 1;
end

//----------------------------------------------------------------------
//--    设计输入信号
//----------------------------------------------------------------------
initial begin
    in = 0;
    #(`Clock*20+2); //初始化完成

    $stop;
end

endmodule在这里插入代码片

有关TestBench基本写法与语法详解的更多相关文章

  1. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  2. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  3. ruby - 覆盖相似的方法,更短的语法 - 2

    在Ruby类中,我重写了三个方法,并且在每个方法中,我基本上做同样的事情:classExampleClassdefconfirmation_required?is_allowed&&superenddefpostpone_email_change?is_allowed&&superenddefreconfirmation_required?is_allowed&&superendend有更简洁的语法吗?如何缩短代码? 最佳答案 如何使用别名?classExampleClassdefconfirmation_required?is_a

  4. ruby 语法糖 : dealing with nils - 2

    可能已经问过了,但我找不到它。这里有2个常见的情况(对我来说,在编程Rails时......)用ruby​​编写是令人沮丧的:"astring".match(/abc(.+)abc/)[1]在这种情况下,我得到一个错误,因为字符串不匹配,因此在nil上调用[]运算符。我想找到的是比以下内容更好的替代方法:temp="astring".match(/abc(.+)abc/);temp.nil??nil:temp[1]简而言之,如果不匹配,则简单地返回nil而不会出错第二种情况是这样的:var=something.very.long.and.tedious.to.writevar=some

  5. ruby - Ruby 语法糖有 "rules"吗? - 2

    我正在学习Ruby的基础知识(刚刚开始),我遇到了Hash.[]method.它被引入a=["foo",1,"bar",2]=>["foo",1,"bar",2]Hash[*a]=>{"foo"=>1,"bar"=>2}稍加思索,我发现Hash[*a]等同于Hash.[](*a)或Hash.[]*一个。我的问题是为什么会这样。是什么让您将*a放在方括号内,是否有某种规则可以在何时何地使用“it”?编辑:我的措辞似乎造成了一些困惑。我不是在问数组扩展。我明白了。我的问题基本上是:如果[]是方法名称,为什么可以将参数放在括号内?这看起来几乎——但不完全是——就像说如果你有一个方法Foo.d

  6. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  7. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  8. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  9. ruby -::在 Ruby 语法中是什么意思? - 2

    这个问题在这里已经有了答案:WhatisRuby'sdouble-colon`::`?(12个答案)关闭8年前。什么是::?@song||=::TwelveDaysSong.new

  10. ruby-on-rails - 使用 HTTParty 的非常基本的 Rails 4.1 API 调用 - 2

    Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"

随机推荐