Testbench编写技巧
一、基本架构(常用模板)
1 `timescale 1ns/1ps //时间精度 2 `define Clock 20 //时钟周期 3 4 module my_design_tb; 5 6 //==================<端口>================================================== 7 reg clk ; //时钟,50Mhz 8 reg rst_n ; //复位,低电平有效 9 reg [XX:0] in ; // 10 wire [XX:0] out ; // 11 12 //-------------------------------------------------------------------------- 13 //-- 模块例化 14 //-------------------------------------------------------------------------- 15 my_design u_my_design 16 ( 17 .clk (clk ), 18 .rst_n (rst_n ), 19 .in (in ), 20 .out (out ) 21 ); 22 23 //---------------------------------------------------------------------- 24 //-- 时钟信号和复位信号 25 //---------------------------------------------------------------------- 26 initial begin 27 clk = 0; 28 forever 29 #(`Clock/2) clk = ~clk; 30 end 31 32 initial begin 33 rst_n = 0; #(`Clock*20+1); 34 rst_n = 1; 35 end 36 37 //---------------------------------------------------------------------- 38 //-- 设计输入信号 39 //---------------------------------------------------------------------- 40 initial begin 41 in = 0; 42 #(`Clock*20+2); //初始化完成 43 44 $stop; 45 end 46 47 48 49 endmodule
二、时钟激励设计
`timescale 1ns/1ps //时间精度 `define Clock 20 //时钟周期 //========================================================================== //== 方法一,50%占空比 //========================================================================== initial begin clk = 0; forever #(`Clock/2) clk = ~clk; end //========================================================================== //== 方法二,50%占空比 //========================================================================== initial begin clk = 0; always #(`Clock/2) clk = ~clk; end //========================================================================== //== 方法三,产生固定输入的时钟脉冲 //========================================================================== initial begin clk = 0; repeat(6) #(`Clock/2) clk = ~clk; end //========================================================================== //== 方法四,非50%占空比 //========================================================================== initial begin clk = 0; forever begin #((`Clock/2)-2) clk = 0; #((`Clock/2)+2) clk = 1; end end
三、复位信号设计
`timescale 1ns/1ps //时间精度 `define Clock 20 //时钟周期 //========================================================================== //== 方法一,异步复位 //========================================================================== initial begin rst_n = 0; #(`Clock*20+1); rst_n = 1; end //========================================================================== //== 方法二,同步复位 //========================================================================== initial begin rst_n = 0; #(`Clock*20); rst_n = 1; end
四、task常用方法
//========================================================================== //== 输入信号任务封装 //========================================================================== 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) @(posedge clk) //等待 times 个时钟上升沿 c=a+b; end endtask //调用方法:more_input(x,y,t,z); //按声明顺序
五、@和wait
//========================================================================== //== @为边沿触发 //========================================================================== initial begin start = 1; repeat(5) @(posedge clk) //等待5个时钟上升沿 start = 0; end //========================================================================== //== wait为电平触发 //========================================================================== initial begin start = 1; wait(en); //等待en==1 start = 0; end
六、常用仿真控制语句
$random //产生随机数 $random % n //产生范围 {-n,n} 的随机数 {$random} % n //产生范围 { 0,n} 的随机数 $stop //停止运行仿真,Modelsim 中可继续仿真 $finish //结束运行仿真,Modelsim 中不可继续仿真 $stop(n) //带参数系统任务,根据参数 0、1、2 不同,输出仿真信息 $finish(n) //带参数系统任务,根据参数 0、1、2 不同,输出仿真信息 /*------------------------------------------------------------------------*\ 0:不输出任何信息 1:输出当前仿真时刻和位置 2:输出当前仿真时刻、位置和仿真过程中用到的 memory 以及 CPU 时间的统计 \*------------------------------------------------------------------------*/
七、仿真终端显示描述
$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 //实行实时模拟时间
八、文本输入
$readmemb/$readmemh("<数据文件名>",<存储器名>); $readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>); $readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>); $readmemb /*------------------------------------------------------------------------*\ 读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数 数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。 \*------------------------------------------------------------------------*/ $readmemh /*------------------------------------------------------------------------*\ 读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数 数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字. \*------------------------------------------------------------------------*/
例如:
/*------------------------------------------------------------------------*\ //mem.dat 文件内容 @001 AB CD @003 A1 \*------------------------------------------------------------------------*/ reg [7:0] memory[7:0] ;//声明 8 个 8 位存储单元 integer i ; initial begin $readmemh("mem.dat",memory);//读取系统文件到存储器中的给定地址 for(i=0;i<4;i=i+1) $display("Memory[%d]=%h",i,memory[i]); end /*------------------------------------------------------------------------*\ //仿真输出为 Memory[0] = xx; Memory[1] = AB; Memory[2] = CD; Memory[3] = A1; \*------------------------------------------------------------------------*/
九、简单的仿真案例
1.设计文件
1 module add 2 //==================<端口>================================================== 3 ( 4 input wire [ 5:0] a , 5 input wire [5:0] b , // 输入信号b 6 input wire [5:0] c , // 输入信号a 7 input wire [5:0] d , // 输入信号b 8 output wire [7:0] e // 求和输出信号 9 ); 10 11 //==================<设计>================================================== 12 assign e = a + b + c + d; 13 14 15 endmodule
2.仿真文件
1 `timescale 1ns/1ps //时间精度 2 3 module add_tb; 4 5 //==================<端口>================================================== 6 //输入输出--------------------------------------- 7 reg [5:0] a ; 8 reg [5:0] b ; 9 reg [5:0] c ; 10 reg [5:0] d ; 11 wire [7:0] e ; 12 //中间变量--------------------------------------- 13 reg [5:0] i ; 14 15 //-------------------------------------------------------------------------- 16 //-- 模块例化 17 //-------------------------------------------------------------------------- 18 add u_add 19 ( 20 .a (a ), 21 .b (b ), 22 .c (c ), 23 .d (d ), 24 .e (e ) 25 ); 26 27 //---------------------------------------------------------------------- 28 //-- 设计输入信号 29 //---------------------------------------------------------------------- 30 initial begin 31 a = 0; 32 b = 0; 33 c = 0; 34 d = 0; 35 for(i=1;i<31;i=i+1) begin 36 #10; 37 a = i; 38 b = i; 39 c = i; 40 d = i; 41 end 42 end 43 44 initial begin 45 $monitor($time,,"%d + %d + %d + %d ={%d}",a,b,c,d,e); // 信号打印输出 46 #500; 47 $stop; 48 end 49 50 51 endmodule
3.Modelsim仿真
参考资料:
[1]米联客FPGA教程
[2]吴厚航. 深入浅出玩转FPGA[M]. 北京航空航天大学出版社, 2013.