特权同学

Testbench仿真串口自收发通信

0
阅读(9689)

以前仿真用的都是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

Baidu
map