12 Verilog语法_仿真文件设计

发布时间 2024-01-07 12:37:09作者: 米联客(milianke)

软件版本:无

操作系统:WIN10 64bit

硬件平台:适用所有系列FPGA

登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!

1概述

本小节讲解Verilog语法的仿真文件设计,需要掌握testbench的建立方法。

2仿真文件设计

当完成verilog工程设计后,首先需要进行就是仿真文件的设计,仿真文件又称为testbench,测试激励。

Testbench文件内容往往包含以下几种:

  1. 信号变量声明。
  2. 时钟与复位信号的生成。
  3. 被测试模块接口信号的波形激励生成。
  4. 被测试模块的例化。
  5. 被测试模块接口数据的校验模块。
  6. 打印仿真信息。

例:

module top(

    input   wire                i_clk,   //输入时钟

    input   wire                i_rst_n,     //输入复位

    output  wire                o_gray_hsyn,     //输出hs信号

    output  wire                o_gray_vsyn,     //输出vs信号

    output  wire    [7:0]       o_gray_data,     //输出data数据信号

    output  wire                o_gray_de //输出de像素有效信号

);

wire                i_hsyn;

wire                i_vsyn;

wire [7:0]          i_r;

wire [7:0]          i_g;

wire [7:0]          i_b;    

wire                i_de;  

 

sim_image_tpg u_tpg(

    .i_clk          (i_clk          ),  

    .o_hsyn         (i_hsyn         ),

    .o_vsyn         (i_vsyn         ),

    .o_en           (i_de           ),

    .o_r            (i_r            ),

    .o_g            (i_g            ),

    .o_b            (i_b            )  

);

 

gray2binary_algorithm u_algorithm

(

    .i_clk          (i_clk          ),      

    .i_hsyn         (i_hsyn         ),      

    .i_vsyn         (i_vsyn         ),      

    .i_r            (i_r            ),

    .i_g            (i_g            ),

    .i_b            (i_b            ),  

    .i_de           (i_de           ),      

    .o_gray_hsyn    (o_gray_hsyn    ),  

    .o_gray_vsyn    (o_gray_vsyn    ),  

    .o_gray_data    (o_gray_data    ),  

    .o_gray_de      (o_gray_de      )

);

endmodule

module gray2binary_algorithm

(

    input   wire                i_clk,      

    input   wire                i_hsyn,    

    input   wire                i_vsyn,    

    input   wire [7:0]          i_r,

    input   wire [7:0]          i_g,//don't care

    input   wire [7:0]          i_b,//don't care

    input   wire                i_de,      

    output  wire                o_gray_hsyn,    

    output  wire                o_gray_vsyn,    

    output  wire [7:0]          o_gray_data,    

    output  wire                o_gray_de

);

 

//Binarization

assign o_gray_data  = i_r >= 75 ? 255 : 0;

 

assign o_gray_hsyn  = i_hsyn;

assign o_gray_vsyn  = i_vsyn;

assign o_gray_de    = i_de;

 

endmodule

// `define VIDEO_1920_1080

// `define VIDEO_1680_1050

// `define VIDEO_1280_1024

`define VIDEO_1280_720 //选择分辨率

// `define VIDEO_1024_768

// `define VIDEO_800_600

// `define VIDEO_640_480

module sim_image_tpg(

    input   wire        i_clk,  

    output  reg         o_hsyn,

    output  reg         o_vsyn,

    output  reg         o_en,

    output  reg [7:0]   o_r,

    output  reg [7:0]   o_g,

    output  reg [7:0]   o_b

);

 

//1920x1080 148.5Mhz

`ifdef  VIDEO_1920_1080

parameter  H_ACTIVE         = 1920;// 行数据有效时间

parameter  H_FRONT_PORCH    = 88;  // 行消隐前肩时间

parameter  H_SYNC_TIME      = 44;  // 行同步信号时间

parameter  H_BACK_PORCH     = 148; // 行消隐后肩时间

                                     

parameter  V_ACTIVE         = 1080;// 列数据有效时间

parameter  V_FRONT_PORCH    = 4;   // 列消隐前肩时间

parameter  V_SYNC_TIME      = 5;   // 列同步信号时间

parameter  V_BACK_PORCH     = 36;  // 列消隐后肩时间

`endif

 

//1680x1050 119Mhz

`ifdef  VIDEO_1680_1050

parameter  H_ACTIVE         = 1680;// 行数据有效时间

parameter  H_FRONT_PORCH    = 48;  // 行消隐前肩时间

parameter  H_SYNC_TIME      = 32;  // 行同步信号时间

parameter  H_BACK_PORCH     = 80;  // 行消隐后肩时间

                                     

parameter  V_ACTIVE         = 1050;// 列数据有效时间

parameter  V_FRONT_PORCH    = 3;   // 列消隐前肩时间

parameter  V_SYNC_TIME      = 6;   // 列同步信号时间

parameter  V_BACK_PORCH     = 21;  // 列消隐后肩时间

`endif

 

//1280x1024 108Mhz

`ifdef  VIDEO_1280_1024

parameter  H_ACTIVE         = 1280;// 行数据有效时间

parameter  H_FRONT_PORCH    = 48;  // 行消隐前肩时间

parameter  H_SYNC_TIME      = 112; // 行同步信号时间

parameter  H_BACK_PORCH     = 248; // 行消隐后肩时间

                                     

parameter  V_ACTIVE         = 1024;// 列数据有效时间

parameter  V_FRONT_PORCH    = 1;   // 列消隐前肩时间

parameter  V_SYNC_TIME      = 3;   // 列同步信号时间

parameter  V_BACK_PORCH     = 38;  // 列消隐后肩时间

`endif

 

//1280X720 74.25MHZ

`ifdef  VIDEO_1280_720

parameter  H_ACTIVE         = 1280;// 行数据有效时间

parameter  H_FRONT_PORCH    = 110; // 行消隐前肩时间

parameter  H_SYNC_TIME      = 40;  // 行同步信号时间

parameter  H_BACK_PORCH     = 220; // 行消隐后肩时间    

                                     

parameter  V_ACTIVE         = 720; // 列数据有效时间

parameter  V_FRONT_PORCH    = 5;   // 列消隐前肩时间

parameter  V_SYNC_TIME      = 5;   // 列同步信号时间

parameter  V_BACK_PORCH     = 20;  // 列消隐后肩时间

`endif

 

//1024x768 65Mhz

`ifdef  VIDEO_1024_768

parameter  H_ACTIVE         = 1024;// 行数据有效时间

parameter  H_FRONT_PORCH    = 24;  // 行消隐前肩时间

parameter  H_SYNC_TIME      = 136; // 行同步信号时间

parameter  H_BACK_PORCH     = 160; // 行消隐后肩时间  

                                     

parameter  V_ACTIVE         = 768; // 列数据有效时间

parameter  V_FRONT_PORCH    = 3;   // 列消隐前肩时间

parameter  V_SYNC_TIME      = 6;   // 列同步信号时间

parameter  V_BACK_PORCH     = 29;  // 列消隐后肩时间

 

`endif

 

//800x600 40Mhz

`ifdef  VIDEO_800_600

parameter  H_ACTIVE         = 800;// 行数据有效时间

parameter  H_FRONT_PORCH    = 40 ;// 行消隐前肩时间

parameter  H_SYNC_TIME      = 128;// 行同步信号时间

parameter  H_BACK_PORCH     = 88 ;// 行消隐后肩时间    

                                     

parameter  V_ACTIVE         = 600;// 列数据有效时间

parameter  V_FRONT_PORCH    = 1  ;// 列消隐前肩时间  

parameter  V_SYNC_TIME      = 4  ;// 列同步信号时间  

parameter  V_BACK_PORCH     = 23 ;// 列消隐后肩时间

 

`endif

 

//640x480 25.175Mhz

`ifdef  VIDEO_640_480

parameter H_ACTIVE          = 640; // 行数据有效时间

parameter H_FRONT_PORCH     = 16 ; // 行消隐前肩时间

parameter H_SYNC_TIME       = 96 ; // 行同步信号时间

parameter H_BACK_PORCH      = 48 ; // 行消隐后肩时间

                                   

parameter V_ACTIVE          = 480; // 列数据有效时间

parameter V_FRONT_PORCH     = 10 ; // 列消隐前肩时间

parameter V_SYNC_TIME       = 2  ; // 列同步信号时间

parameter V_BACK_PORCH      = 33 ; // 列消隐后肩时间

`endif

 

parameter  H_TOTAL_TIME     = H_ACTIVE + H_FRONT_PORCH + H_SYNC_TIME + H_BACK_PORCH; // 计算参数H_TOTAL_TIME的值

parameter  V_TOTAL_TIME     = V_ACTIVE + V_FRONT_PORCH + V_SYNC_TIME + V_BACK_PORCH; // 计算参数 V_TOTAL_TIME 的值

 

reg         en;

reg [12:0]  h_syn_cnt = 'd0;

reg [12:0]  v_syn_cnt = 'd0;

reg [ 7:0]  image [0 : H_ACTIVE*V_ACTIVE-1];

reg [31:0]  image_cnt = 'd0;

 

// 行扫描计数器

always@(posedge i_clk)

begin

    if(h_syn_cnt == H_TOTAL_TIME-1)

        h_syn_cnt <= 0;

    else

        h_syn_cnt <= h_syn_cnt + 1;

end

 

always@(posedge i_clk) // 列扫描计数器

begin

    if(h_syn_cnt == H_TOTAL_TIME-1) //h_syn_cnt计数达到设定值 H_TOTAL_TIME进入执行语句

    begin

        if(v_syn_cnt == V_TOTAL_TIME-1) //v_syn_cnt判断,目标值 V_TOTAL_TIM

            v_syn_cnt <= 0; //达到目标,数值清零

        else

            v_syn_cnt <= v_syn_cnt + 1; //否则v_syn_cnt+1

    end

end

 

always@(posedge i_clk) // 行同步控制

begin

    if(h_syn_cnt < H_SYNC_TIME) // h_syn_cnt未达到H_SYNC_TIME计数值

        o_hsyn <= 0; // o_hsyn信号控制置0

    else

        o_hsyn <= 1; // o_hsyn信号控制置1

End

 

always@(posedge i_clk) // 场同步控制

begin

    if(v_syn_cnt < V_SYNC_TIME) // v_syn_cnt未达到V_SYNC_TIME计数值

        o_vsyn <= 0; // o_vsyn信号控制置0

    else

        o_vsyn <= 1; // o_vsyn信号控制置1

end

 

always@(posedge i_clk) // 坐标使能.

begin

    if(v_syn_cnt >= V_SYNC_TIME + V_BACK_PORCH && v_syn_cnt < V_SYNC_TIME + V_BACK_PORCH + V_ACTIVE)

    begin

        if(h_syn_cnt >= H_SYNC_TIME + H_BACK_PORCH && h_syn_cnt < H_SYNC_TIME + H_BACK_PORCH + H_ACTIVE)

            en <= 1;

        else

            en <= 0;

    end

    else

        en <= 0;

end

 

//读取txt文件到image数组中

initial begin

    $readmemh("../matlab_src/image_720_1280_1.txt", image);

end

 

always@(posedge i_clk)

begin

    if(en)

    begin

        o_r         <= image[image_cnt]; // image中存储的图像数据依次写入o_r

        image_cnt   <= image_cnt + 1; // 输出一个像素数据image_cnt+1,输入下一个

    end

    else if(image_cnt == H_ACTIVE*V_ACTIVE) // image_cnt的值增加到H_ACTIVE*V_ACTIVE时为一帧图像

    begin

        o_r         <= 8'h00; //o_r数据清零

        image_cnt   <= 'd0; // image_cnt计数清零

    end

    else

    begin

        o_r         <= 8'h00;

        image_cnt   <= image_cnt;

    end

end

 

always@(posedge i_clk)

begin

    o_en <= en;

end

endmodule

 

module top_tb;

 

reg             clk;

reg             rst_n;

 

integer         image_txt;

 

reg [31:0]      pixel_cnt;

wire[7:0]       data;

wire            de;

 

top u_top

(

    .i_clk               (clk                ),

    .i_rst_n             (rst_n              ),

    .o_gray_data         (data               ),

    .o_gray_de           (de                 )

);

 

always #(1) clk = ~clk;

 

initial

begin

    clk   = 1;

    rst_n = 0;  

    #100

    rst_n = 1;

     

end

 

initial

begin

    image_txt = $fopen("../matlab_src/image_720_1280_3_out.txt"); //打开存有图像数据的.txt文件

end

 

always@(posedge clk or negedge rst_n) //敏感列表

begin

    if(!rst_n)

    begin

        pixel_cnt <= 0; //pixel_cnt计数清零

    end

    else if(de) //像素有效信号拉高时,pixel_cnt+1

    begin

        pixel_cnt = pixel_cnt + 1;

        $fwrite(image_txt,"%h\n",data);

    end

end

 

always@(posedge clk )

begin

    if(pixel_cnt == 720*1280)

    begin

        $display("*******************************************************************************");        

        $display("*** Success:image_720_1280_3_out.txt is output complete! %t", $realtime, "ps***");

        $display("*******************************************************************************");

            $fclose(image_txt);

        $stop;

    end

end

endmodule

2.1信号声明

testbench 模块声明时,一般不需要声明端口。因为激励信号一般都在 testbench 模块内部,没有外部信号。

声明的变量应该能全部对应被测试模块的端口。当然,变量不一定要与被测试模块端口名字一样。但是被测试模块输入端对应的变量应该声明为 reg 型,如 时钟,复位等,输出端对应的变量应该声明为 wire 型,如 data,de。

2.2时钟生成

生成时钟的方式有很多种,举例以下这种生成方式可以借鉴。

例:

always #(1) clk = ~clk; //时钟翻转,前面的时钟周期可以根据情况自行调整

initial

begin

    clk   = 1; //时钟赋初值

    rst_n = 0;   //复位赋初值

    #100

    rst_n = 1; //复位赋完成,恢复高电平

     

end

需要注意的是,利用取反方法产生时钟时,一定要给 clk 寄存器赋初值。

2.3复位生成

复位逻辑比较简单,一般赋初值为 0,再经过一段小延迟后,复位为 1 即可。

例:

initial

begin

    clk   = 1;

    rst_n = 0;  

    #100

    rst_n = 1;

end

2.4激励部分

激励部分该产生怎样的输入信号,是根据被测模块的需要来设计的。本例中采用读取已有的图像数据,按照时序要求读到程序中,生成激励波形。

initial begin

    $readmemh("../matlab_src/image_720_1280_1.txt", image);

end

2.5模块例化

利用 testbench 开始声明的信号变量,对被测试模块进行例化连接。

top u_top

(

    .i_clk                   (clk                ),

    .i_rst_n                 (rst_n              ),

    .o_gray_data             (data               ),

    .o_gray_de               (de                 )

);

2.6打印信息

always@(posedge clk )

begin

    if(pixel_cnt == 720*1280)

    begin

        $display("*******************************************************************************");        

        $display("*** Success:image_720_1280_3_out.txt is output complete! %t", $realtime, "ps***");

        $display("*******************************************************************************");

            $fclose(image_txt);

        $stop;

    end

end

 

通过打印关键仿真信息,能让我们自动化操作仿真过程。