TestBench基本写法与语法详解

TestBench基本写法与语法详解

一、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在这里插入代码片

相关推荐

白银中毒:大明帝国的最后77年
365体育投注官网

白银中毒:大明帝国的最后77年

📅 07-15 👁️ 1206