03_串口 RS232

1. 理论学习

1.1 串口专业名词介绍

1.1.1 异步通信

UART 和 SPI、IIC 不同的是,它是异步通信接口,异步通信中的接收
方并不知道数据什么时候会到达,所以双方收发端都要有各自的时钟,在数据传输过程中是不需要时钟的,发送方发送的时间间隔可以不均匀,接受方是在数据的起始位和停止位的帮助下实现信息同步的。

1.1.2 同步通信

SPI、IIC 是同步通信接口(后面的章节会做详细介绍),同
步通信中双方使用频率一致的时钟,在数据传输过程中时钟伴随着数据一起传输,发送方和接收方使用的时钟都是由主机提供的。

1.1.3 全双工

全双工,即可以同时进行发送数据和接收数据。

1.1.4 半双工

发送数据和接受数据在同一时刻只能有一个在进行。

1.1.5 UART 通信

UART 通信只有两根信号线,一根是发送数据端口线叫 tx,一根是接收数据端口线叫 rx,对于 PC 来说它的 tx 要和对于 FPGA 来说的 rx 连接,同样 PC 的 rx 要和 FPGA 的 tx 连接,如果是两个 tx 或者两个 rx 连接那数据就不能正常被发送出去和接收到,所以不要弄混,记住 rx 和 tx 都是相对自身主体来讲的。UART 可以实现全双工,即可以同时进行发送数据和接收数据。
在这里插入图片描述

1.2 RS-232 信号线

RS-232 信号线口标准常用于计算机、路由与调制调解器(MODEN,俗称
“猫”)之间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由)和数据通讯设备 DCE(调制调解器)。我们以这种通讯模型讲解它们的信号线连接方式及各个信号线的作用。在旧式的台式计算机中一般会有 RS-232 标准的 COM 口(也称 DB9 接口)
在这里插入图片描述
DB9接口信号线说明
在这里插入图片描述
在这里插入图片描述
“直通型”的串口线连接
在这里插入图片描述

1.3 RS232 通信协议简介

1)RS232 是 UART 的一种,没有时钟线,只有两根数据线,分别是 rx 和 tx,这两根线都是 1bit 位宽的。其中 rx 是接收数据的线,tx 是发送数据的线。
2)rx 位宽为 1bit,PC 机通过串口调试助手往 FPGA 发 8bit 数据时,FPGA 通过串口线rx 一位一位地接收,从最低位到最高位依次接收,最后在 FPGA 里面位拼接成 8 比特数据。(接受数据最后拼接成8比特数据,一位数据)
3)tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照RS232 协议把这一位一位的数据位拼接成 8bit 数据。
4)串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有10bit在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持
高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如图所示为一个最基本的 RS232 帧结构。
在这里插入图片描述

5)波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、115200 等,我们选用 9600 的波特率进行串口章节的讲解。

6)比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为“每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 9600 的波特率,其串口的比特率为:9600Bps 1bit= 9600bps。

7)波特率和比特率的区别
要讨论比特率与波特率的关系,需要先了解码元与比特的关系。就像刚才的例子中提及的公交车、地铁、的士可以搭乘不同数量的出行人员一样,不同码元也可以用不同位数的比特表示。码元所需要的比特位数,由码元支持的状态数量确定。
在这里插入图片描述
在这里插入图片描述
8)计算:若波特率为9600,由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟频率下计数 5208 次。
9)上位机通过串口发 8bit 数据时,会自动在发 8 位有效数据前发一个波特时间的起始位,也会自动在发完 8 位有效数据后发一个停止位。同理,串口助手接收上位机发送的数据前,必须检测到一个波特时间的起始位才能开始接收数据,接收完 8bit 的数据后,再接收一个波特时间的停止位。

2. 亚稳态

2.1 打第一拍

在这里插入图片描述
1)为什么打第一拍,因为传进来的 rx 和时钟信号并不是同步的,要把 rx 信号变成与时钟信号同步。
2)由于在 PC 机中波特率和 rx 信号是同步的,而 rx 信号和 FPGA 的系统时钟 sys_clk 是异步的关系,我们此时要做的是将慢速时钟域(PC 机中的波特率)系统中的 rx 信号同步到快速时钟域(FPGA 中的 sys_clk)系统中。

2.2 连续打拍

1)什么是亚稳态?
因为在串口传输数据时,当你使用示波器把一个矩形脉冲的上升沿或下降沿放大后会发现其上升沿和下降沿并不是瞬间被拉高或拉低的,而是有一个倾斜变化的过程,这在运放中被称为“压摆率”。此时 FPGA 的第一级寄存器的输出端在时钟沿到来之后比较长的一段时间内都处于不确定的状态,在 0 和 1 之间处于振荡状态,而不是等于串口输入的确定的 rx 值。
在这里插入图片描述
在这里插入图片描述2)亚稳态是如何产生?
建立时间Tus,保持时间Th,RS建立时间和保存时间不满足条件,寄存器延迟Tco,决断时间Tmet,信号传入寄存器没有保证他的建立时间和保持时间。
在这里插入图片描述

3)如何解决亚稳态?
用多级寄存器去减少亚稳态对系统的危害。一般打
2拍。
在这里插入图片描述

3. 实验目标

设计并实现基于串口 RS232 的数据收、发模块,使用收、发模块,完成串口数据回环实验。
在这里插入图片描述

4. 硬件资源

MAX3232 为 RS232 收发器芯片。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL”的电平信号,才能实现通讯。
在这里插入图片描述

5. 模块设计

5.1 顶层模块框图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.2 串口数据接收模块

在这里插入图片描述
在这里插入图片描述
为什么还需要输出一个伴随并行数据有效的标志信号,这是因为后级模块或系统在使用该并行数据的时候可能无法知道该时刻采样的数据是不是稳定有效的,而数据有效标志信号的到来就说明数据才该时刻是稳定有效的,起到一个指示作用。当数据有效标志信号为高时,该并行数据就可以被后级模块或系统使用了。
发送 87654321 —> 87654321
在这里插入图片描述
在这里插入图片描述

5.3 串口数据发送模块

在这里插入图片描述
在这里插入图片描述

6. 波形图

6.1 串口数据接收模块

start_nedge:下降沿,开始标志信号,有时候rex_reg信号也会有0,1跳转,在数据位也会有下降延,为了避免这种情况的发生设置一个使能信号work_en,work_en 信号就可以判断出此时出现的 start_nedge 标志信号并不是我们想要的串口帧起始下降沿,从而将其过滤除掉。
在这里插入图片描述
baud_cnt:区分10 bit ,一帧数据中10 bit的数据
bit_flag 提取数据,找到最稳定的状态。
在这里插入图片描述
提取8bit 数据,有效的数据 1-8,
在这里插入图片描述
有效信号结束后,work_en 拉低,计数器也清0
在这里插入图片描述
然后进行数据的拼接操作,最后输出数据并且把信号po_flag拉高。
在这里插入图片描述

6.2 串口数据发送模块

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7. RTL

7.1 uart_rx

`timescale  1ns/1ns




module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {
    
    rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

7.2 uart_tx

`timescale  1ns/1ns




module  uart_tx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
     input   wire            sys_clk     ,   //系统时钟50MHz
     input   wire            sys_rst_n   ,   //全局复位
     input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
     input   wire            pi_flag     ,   //并行数据有效标志信号
 
     output  reg             tx              //串转并后的1bit数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            work_en <= 1'b0;
        else    if(pi_flag == 1'b1)
            work_en <= 1'b1;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            baud_cnt <= 13'b0;
        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
            baud_cnt <= 13'b0;
        else    if(work_en == 1'b1)
            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_flag <= 1'b0;
        else    if(baud_cnt == 13'd1)
            bit_flag <= 1'b1;
        else
            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (work_en == 1'b1))
        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            tx <= 1'b1; //空闲状态时为高电平
        else    if(bit_flag == 1'b1)
            case(bit_cnt)
                0       : tx <= 1'b0;
                1       : tx <= pi_data[0];
                2       : tx <= pi_data[1];
                3       : tx <= pi_data[2];
                4       : tx <= pi_data[3];
                5       : tx <= pi_data[4];
                6       : tx <= pi_data[5];
                7       : tx <= pi_data[6];
                8       : tx <= pi_data[7];
                9       : tx <= 1'b1;
                default : tx <= 1'b1;
            endcase

endmodule

7.3 rs232

`timescale  1ns/1ns




module  rs232
(
    input   wire    sys_clk     ,   //系统时钟50MHz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    rx          ,   //串口接收数据

    output  wire    tx              //串口发送数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ uart_rx_inst ------------------------
uart_rx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_rx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .rx         (rx         ),  //input             rx
            
    .po_data    (po_data    ),  //output    [7:0]   po_data
    .po_flag    (po_flag    )   //output            po_flag
);

//------------------------ uart_tx_inst ------------------------
uart_tx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_tx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .pi_data    (po_data    ),  //input     [7:0]   pi_data
    .pi_flag    (po_flag    ),  //input             pi_flag
                
    .tx         (tx         )   //output            tx
);

endmodule

8. Testbench

8.1 tb_uart_rx

`timescale  1ns/1ns


module  tb_uart_rx();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg             sys_clk;
reg             sys_rst_n;
reg             rx;

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        rx        <= 1'b1;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送8次数据,分别为0~7
initial begin
        #200
        rx_bit(8'd0);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd3);
        rx_bit(8'd4);
        rx_bit(8'd5);
        rx_bit(8'd6);
        rx_bit(8'd7);
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//定义一个名为rx_bit的任务,每次发送的数据有10位
//data的值分别为0~7由j的值传递进来
//任务以task开头,后面紧跟着的是任务名,调用时使用
task rx_bit(
    //传递到任务中的参数,调用任务的时候从外部传进来一个8位的值
        input   [7:0]   data
);
        integer i;      //定义一个常量
//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1
//不可以写成C语言i=i++的形式
        for(i=0; i<10; i=i+1) begin
            case(i)
                0: rx <= 1'b0;
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(5208*20); //每发送1位数据延时5208个时钟周期
        end
endtask         //任务以endtask结束

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------uart_rx_inst------------------------
uart_rx uart_rx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .rx         (rx         ),  //input           rx
                
        .po_data    (po_data    ),  //output  [7:0]   po_data
        .po_flag    (po_flag    )   //output          po_flag
);

endmodule


8.2 tb_uart_tx

`timescale  1ns/1ns
/



module  tb_uart_tx();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg         sys_clk;
reg         sys_rst_n;
reg [7:0]   pi_data;
reg         pi_flag;

//wire  define
wire        tx;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送7次数据,分别为0~7
initial begin
        pi_data <= 8'b0;
        pi_flag <= 1'b0;
        #200
        //发送数据0
        pi_data <= 8'd0;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
//所以需要数据延时(5208*20*10)后再产生下一个数据
        #(5208*20*10);
        //发送数据1
        pi_data <= 8'd1;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据2
        pi_data <= 8'd2;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据3
        pi_data <= 8'd3;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据4
        pi_data <= 8'd4;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据5
        pi_data <= 8'd5;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据6
        pi_data <= 8'd6;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据7
        pi_data <= 8'd7;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------uart_rx_inst------------------------
uart_tx uart_tx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .pi_data    (pi_data    ),  //output  [7:0]   pi_data
        .pi_flag    (pi_flag    ),  //output          pi_flag

        .tx         (tx         )   //input           tx
);

endmodule

8.3 tb_rs232

`timescale  1ns/1ns




module  tb_rs232();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire  define
wire    tx          ;

//reg   define
reg     sys_clk     ;
reg     sys_rst_n   ;
reg     rx          ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    rx        <= 1'b1;
    #20;
    sys_rst_n <= 1'b1;
end

//调用任务rx_byte
initial begin
    #200
    rx_byte();
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//创建任务rx_byte,本次任务调用rx_bit任务,发送8次数据,分别为0~7
task    rx_byte();  //因为不需要外部传递参数,所以括号中没有输入
    integer j;
    for(j=0; j<8; j=j+1)    //调用8次rx_bit任务,每次发送的值从0变化7
        rx_bit(j);
endtask

//创建任务rx_bit,每次发送的数据有10位,data的值分别为0到7由j的值传递进来
task    rx_bit(
    input   [7:0]   data
);
    integer i;
    for(i=0; i<10; i=i+1)   begin
        case(i)
            0: rx <= 1'b0;
            1: rx <= data[0];
            2: rx <= data[1];
            3: rx <= data[2];
            4: rx <= data[3];
            5: rx <= data[4];
            6: rx <= data[5];
            7: rx <= data[6];
            8: rx <= data[7];
            9: rx <= 1'b1;
        endcase
        #(5208*20); //每发送1位数据延时5208个时钟周期
    end
endtask

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ rs232_inst ------------------------
rs232   rs232_inst
(
    .sys_clk    (sys_clk    ),  //input         sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input         sys_rst_n
    .rx         (rx         ),  //input         rx

    .tx         (tx         )   //output        tx
);

endmodule



猜你喜欢

转载自blog.csdn.net/HeElLose/article/details/131404467