03_Serial port RS232

1. Theoretical study

1.1 Introduction to serial port terminology

1.1.1 Asynchronous communication

The difference between UART and SPI and IIC is that it is an asynchronous communication interface. The receiver in asynchronous communication
does not know when the data will arrive, so both parties must have their own clocks. During the data transmission process, no clock is needed. The time interval of sending by the sender can be uneven, and the receiver realizes information synchronization with the help of the start bit and stop bit of the data.

1.1.2 Synchronous communication

SPI and IIC are synchronous communication interfaces (details will be introduced in later chapters).
In synchronous communication, both parties use clocks with the same frequency. During data transmission, the clock is transmitted along with the data. The clocks used by the sender and receiver are provided by the host.

1.1.3 Full duplex

Full-duplex, that is, data can be sent and received at the same time.

1.1.4 Half-duplex

Only one of sending data and receiving data can be in progress at the same time.

1.1.5 UART communication

There are only two signal lines in UART communication, one is the sending data port line called tx, and the other is the receiving data port line called rx. For the PC, its tx should be connected with the FPGA’s rx. Similarly, the PC’s rx should be connected with the FPGA’s tx. If two tx or two rx are connected, the data cannot be sent and received normally, so don’t get confused, remember that rx and tx are relative to the main body. UART can achieve full duplex, that is, it can send data and receive data at the same time.
insert image description here

1.2 RS-232 signal line

The RS-232 signal line port standard is often used for communication between computers, routers and modems (MODEN, commonly known as
"cat"). In this communication system, the equipment is divided into data terminal equipment DTE (computer, router) and data communication equipment DCE (modem). We use this communication model to explain their signal line connection and the role of each signal line. In the old desktop computers, there is usually a RS-232 standard COM port (also known as the DB9 interface). The
insert image description here
signal line of the DB9 interface indicates
insert image description here
insert image description here
the "straight-through" serial port connection
insert image description here

1.3 Introduction to RS232 Communication Protocol

1) RS232 is a kind of UART, there is no clock line, only two data lines, namely rx and tx, both of which are 1bit wide. Where rx is the line for receiving data and tx is the line for sending data.
2) The rx bit width is 1 bit. When the PC sends 8-bit data to the FPGA through the serial port debugging assistant, the FPGA receives bit by bit through the serial port rx, from the lowest bit to the highest bit, and finally the bits are spliced ​​into 8-bit data in the FPGA. (The received data is finally spliced ​​into 8-bit data, one-bit data)
3) The tx bit width is 1 bit. When the FPGA sends 8-bit data to the PC through the serial port, the FPGA transmits the 8-bit data to the PC one by one through the tx line, and sends them sequentially from the lowest bit to the highest bit. Finally, the host computer splices the bit-by-bit data bits into 8-bit data through the serial port assistant according to the RS232 protocol.
4) The sending and receiving of serial port data is based on the frame structure, that is, sending and receiving data frame by frame. In addition to containing 8 bits of valid data in the middle, each frame must have a start bit at the beginning of each frame, which is fixed to 0; at the end of each frame, there must also be a stop bit, which is fixed to 1, that is, the most basic frame structure (excluding parity, etc.) has 10 bits . In the case of not sending or receiving data, rx and tx are in the idle state . At this time, the rx and tx lines are kept at
high level. If there is a data frame transmission, there will be a start bit first, then 8bit data bits, and then a 1bit stop bit. Then rx and tx continue to enter the idle state, and then wait for the next data transmission. As shown in the figure is a basic RS232 frame structure.
insert image description here

5) Baud rate: In the information transmission channel, the signal unit carrying data information is called a symbol (because the serial port is 1bit for transmission, so the symbol represents a binary number), the number of symbols transmitted through the signal per second is called the transmission rate of the symbol, referred to as the baud rate, commonly used symbol "Baud", and its unit is "baud per second (Bps)". Common baud rates of serial ports are 4800, 9600, 115200, etc. We choose 9600 baud rate to explain the serial port chapter.

6) Bit rate: The amount of information transmitted by the communication channel per second is called the bit transmission rate, referred to as the bit rate, and its unit is "bits per second (bps)". The bit rate can be calculated from the baud rate, the formula is: bit rate = baud rate * binary digits corresponding to a single modulation state. If the baud rate is 9600, the bit rate of the serial port is: 9600Bps 1bit= 9600bps.

7) The difference between baud rate and bit rate
To discuss the relationship between bit rate and baud rate, it is necessary to understand the relationship between decoding element and bit. Just like the buses, subways, and taxis mentioned in the example just now can take different numbers of passengers, different code elements can also be represented by bits of different digits. The number of bits required for a symbol is determined by the number of states supported by the symbol.
insert image description here
insert image description here
8) Calculation: If the baud rate is 9600, the calculated time for the serial port to send or receive 1 bit data is one baud, that is, 1/9600 second. If a 50MHz (period is 20ns) system clock is used to count, the number to be counted is cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 system clock cycles, that is, the interval between each bit of data must be at a clock frequency of 50MHz Down count 5208 times.
9) When the host computer sends 8-bit data through the serial port, it will automatically send a start bit of baud time before sending 8-bit valid data, and will automatically send a stop bit after sending 8-bit valid data. In the same way, before the serial port assistant receives the data sent by the host computer, it must detect the start bit of a baud time to start receiving data. After receiving the 8bit data, it will receive a stop bit of the baud time.

2. Metastable state

2.1 Take the first beat

insert image description here
1) Why take the first beat, because the incoming rx and the clock signal are not synchronized, and the rx signal must be synchronized with the clock signal.
2) Since the baud rate and the rx signal in the PC are synchronous, and the rx signal and the system clock sys_clk of the FPGA are asynchronous, what we need to do at this time is to synchronize the rx signal in the slow clock domain (the baud rate in the PC) system to the fast clock domain (sys_clk in the FPGA) system.

2.2 Continuous beat

1) What is metastable state?
Because when transmitting data on the serial port, when you use an oscilloscope to amplify the rising or falling edge of a rectangular pulse, you will find that its rising and falling edges are not pulled up or pulled down instantaneously, but have a slope change process, which is called "slew rate" in the op amp. At this time, the output terminal of the FPGA's first-level register is in an uncertain state for a long period of time after the clock edge arrives, and is in an oscillating state between 0 and 1, rather than equal to the determined rx value of the serial port input.
insert image description here
insert image description here2) How is the metastable state generated?
The setup time Tus, the hold time Th, the RS setup time and the save time do not meet the conditions, the register delay Tco, the decision time Tmet, and the signal input register does not guarantee its setup time and hold time.
insert image description here

3) How to solve the metastable state?
Use multi-level registers to reduce the harm of metastability to the system. Generally hit
2 beats.
insert image description here

3. Experimental objectives

Design and realize the data receiving and sending module based on serial port RS232, use the receiving and sending module to complete the serial port data loopback experiment.
insert image description here

4. Hardware resources

MAX3232 is an RS232 transceiver chip. Since the signals of the RS-232 level standard cannot be directly recognized by the controller, these signals will be converted into "TTL" level signals that the controller can recognize through a "level conversion chip" to realize communication.
insert image description here

5. Modular design

5.1 Top-level module block diagram

insert image description here
insert image description here
insert image description here

5.2 Serial port data receiving module

insert image description here
insert image description here
Why is it necessary to output a valid flag signal accompanying the parallel data? This is because the subsequent module or system may not know whether the sampled data at that moment is stable and valid when using the parallel data, and the arrival of the data valid flag signal indicates that the data is stable and valid at that moment, which serves as an indicator. When the data valid flag signal is high, the parallel data can be used by the subsequent module or system.
Send 87654321 —> 87654321
insert image description here
insert image description here

5.3 Serial data sending module

insert image description here
insert image description here

6. Waveform diagram

6.1 Serial data receiving module

start_nedge: Falling edge, start flag signal, sometimes the rex_reg signal will also have 0, 1 jump, and there will be a falling delay in the data bit. In order to avoid this situation, set an enable signal work_en, and the work_en signal can judge that the start_nedge flag signal that appears at this time is not the initial falling edge of the serial port frame we want, so that it can be filtered out.
insert image description here
baud_cnt: distinguish between 10 bit and 10 bit data
bit_flag in a frame of data to extract data and find the most stable state.
insert image description here
Extract 8bit data, valid data 1-8,
insert image description here
after the effective signal ends, work_en is pulled low, the counter is also cleared to 0,
insert image description here
and then the data splicing operation is performed, and finally the data is output and the signal po_flag is pulled high.
insert image description here

6.2 Serial data sending module

insert image description here
insert image description here
insert image description here

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



Guess you like

Origin blog.csdn.net/HeElLose/article/details/131404467