特权同学

Testbench仿真串口自收发通信

0
阅读(9834)

以前仿真用的都是ISE自带的Test Bench Waveform。图形化界面,只要点点添加把信号拉高或者拉低进行激励添加,可谓方便快捷。但是所有的仿真激励都用TBW里做恐怕在大一些的项目模块仿真时就会遇到困难了,之前的IIC收发通信用TBW里添加仿真激励就显得有些为难了,所以在对RTL级的verilog设计有了一定程度的掌握后,下一步就该加强自己编写verilog testbench的能力了。

    今天就以一个简单的串口自收发通信开始吧,这个RTL级代码在前面的日志里。

Verilog test fixture:

 

`timescale 1ns / 1ps

 

module testbench;

 

// DATE:     Sat Aug 11 10:03:33 2007

// TITLE:   

// MODULE:   my_uart_top

// DESIGN:   my_uart_top

// FILENAME: my_uart_top

// PROJECT:  test

// VERSION:  Version

 

// Inputs

    reg clk;

    reg rst_n;

    reg rs232_rx;

 

// Outputs

    wire rs232_tx;

 

// Bidirs

 

// Instantiate the UUT

    my_uart_top d (

        .clk(clk),

        .rst_n(rst_n),

        .rs232_rx(rs232_rx),

        .rs232_tx(rs232_tx)

        );

 

// Initialize Inputs

 

    parameter  RESET_TIME = 1000;

   initial begin

      rst_n = 1'b0;  // RESET pulse

       # RESET_TIME rst_n = 1'b1;  //after 1us,RESET over

    end

 

    parameter CLOCK_20NS = 20;

    initial begin

       clk = 1'b0;

       forever

           # (CLOCK_20NS/2) clk = ~clk;    // 20ns CLOCK

    end

 

    parameter BPS_9600 = 104140,

                  SEND_DATA = 8'b1010_0011;

    integer i;

    initial begin

      rs232_rx = 1'b1;   //RESET rs232_rx

       # BPS_9600 rs232_rx = 1'b0;// transmit START bit

 

       for (i=0;i<8;i=i+1)      //transmit DATA byte

           # BPS_9600 rs232_rx = SEND_DATA[i];

 

       # BPS_9600 rs232_rx = 1'b0;// transmit STOP bit

       # BPS_9600 rs232_rx = 1'b1;// bus IDLE

    end

//

    reg[7:0] tran_data;  // receive transmit data byte

    always @ (negedge rs232_tx) begin

       # (BPS_9600/2)

 

       for (i=0;i<8;i=i+1)

           # BPS_9600 tran_data[i] = rs232_tx;    //receive data byte

 

       # BPS_9600

       # (BPS_9600/2);

    end

   

endmodule

 

仿真后的波形如下:(明显比单纯用TBW来得方便)

 

rs232_rx信号为仿真PC发送数据到FPGA里,上图可以看到rs232_rx线的变化。

 

rs232_tx为FPGA发送数据给PC,数据正是PC发送过来的原数据,tran_data是模拟PC接收到来自FPGA的数据,可以看到最终PC接收FPGA发送回来的数据和PC发送给FPGA的数据是一致的。自此,这个仿真说明了源代码设计是正确的。

 

前面的testbench只是比较简单的,因为就做了一次的数据收发。然而在实际中要验证一个设计的正确与否,往往应该是要经受住无数次的考验才算满足要求。基于此,下面的testbench的任务就是从0开始一直发到255一共就是256此验证。这么多此的测试光看波形可不成,尤其定位错误的时候非累死你不可,所以就应该采用$display这个命令,在modulsim的命令窗口里观察就可以了:

 

    上面这个截图会将写入的数据和读出的数据实时的显示出来,而且监视着当前是否错误。这样的仿真验证就比较容易差错了。

    Testbench代码如下:

module vtf_uart_top_v;

 

// DATE:     Sat Aug 11 10:03:33 2007

// TITLE:   

// MODULE:   my_uart_top

// DESIGN:   my_uart_top

// FILENAME: my_uart_top

// PROJECT:  test

// VERSION:  Version

 

// Inputs

    reg clk;

    reg rst_n;

    reg rs232_rx;

 

// Outputs

    wire rs232_tx;

 

// Bidirs

 

// Instantiate the UUT

    my_uart_top d (

        .clk(clk),

        .rst_n(rst_n),

        .rs232_rx(rs232_rx),

        .rs232_tx(rs232_tx)

        );

 

// Initialize Inputs

 

    parameter  RESET_TIME = 1000;

   initial begin

      rst_n = 1'b0;  // RESET pulse

       # RESET_TIME rst_n = 1'b1;  //after 1us,RESET over

    end

 

    parameter CLOCK_20NS = 20;

    initial begin

       clk = 1'b0;

       forever

           # (CLOCK_20NS/2) clk = ~clk;    // 20ns CLOCK

    end

 

    parameter BPS_9600 = 104140;

    integer i;

    reg[7:0] test_data;

    initial begin

       rs232_rx = 1'b1;

       test_data = 0;

       repeat (256) begin

           transmit_data(test_data);

           # (BPS_9600*15);  //delay 1 unit transmit time          

           test_data = test_data+1;

       end

    end

 

    task transmit_data;

       input[7:0] SEND_DATA;

       begin

          rs232_rx = 1'b1;   //RESET rs232_rx

           # BPS_9600 rs232_rx = 1'b0;// transmit START bit

           $display ("write data: %d\n",SEND_DATA);

           for (i=0;i<8;i=i+1)      //transmit DATA byte

              # BPS_9600 rs232_rx = SEND_DATA[i];

   

           # BPS_9600 rs232_rx = 1'b0;// transmit STOP bit

           # BPS_9600 rs232_rx = 1'b1;// bus IDLE

       end

    endtask

 

//

    reg[7:0] tran_data;  // receive transmit data byte

    integer error_counter;   // error times per 256 times transmit

 

    initial begin

       error_counter = 0;

    end

 

    always @ (negedge rs232_tx) begin

       # (BPS_9600/2)

 

       for (i=0;i<8;i=i+1)

           # BPS_9600 tran_data[i] = rs232_tx;    //receive data byte

 

       # BPS_9600

       $display ("read data: %d\n",tran_data);

       if(tran_data != test_data) error_counter = error_counter + 1;

       # (BPS_9600/2);

       $display ("%d errors occur current transmit\n",error_counter);

    end

   

endmodule