FPGA serial port sending module

------------Restore content start------------

Universal Asynchronous Receiver/Transmitter (UART) is an asynchronous transceiver, which converts parallel data into serial data for transmission when sending data, and converts the received serial data into parallel data when receiving data, which can realize full duplex transmission and reception. It includes RS232, RS449, RS423, RS422, RS485 and other interface standards and bus standards. In other words, UART is the general term of asynchronous serial communication. RS232, RS449, RS423, RS422 and RS485 are interface standards and bus standards corresponding to various asynchronous serial communication ports. They specify the electrical characteristics, transmission rate, connection characteristics and mechanical characteristics of the communication port.

  

Figure 1 # serial port transmission sequence diagram

Figure 2 logic block diagram of serial port transmission

Calculate the corresponding baud rate clock according to the selected baud rate. For example, the baud rate clock cycle corresponding to the baud rate of 115200 is 8680ns, and the clock timing number corresponding to the 50M clock is 433. Baud rate clock cycle timer is div_cnt. The baud rate clock counter is bps_cnt, record the number of baud rate clocks. send_en is the send flag, when send_en, start sending data according to bps_cnt. Since the serial port needs to be 1 before sending the protocol start bit, BPS_ When CNT = 0, uart_tx=1; bps_ When CNT = 1-10, uart_tx is equal to the start bit, data bit and stop bit in turn. In order to ensure that the stop bit maintains a high level of one bit, BPS_ When CNT = 11, uart_tx = 1, so bps_cnt circulates within 0-11, BPS_ Reset immediately when CNT = 11. To make send_ After the en signal appears, it can immediately start sending data and set Div_ Every time CNT is 0, bps_cnt is + 1. After ten bit data transmission is completed, TX_ Do pull up and send at the same time_ EN is pulled down and the flag is sent.

The serial port sending module is used to realize a counter that counts and adds 1 every 10ms. Counting starts from 0 and passes send_en and tx_done signal cooperation, when count_ When 10ms starts counting, send_ The en signal is pulled high until TX_ When the done signal comes, it is pulled down whenever TX_ When the done signal arrives, the timing number is + 1.

Serial port sending module code:

module urat_tx(
    input  wire                         clk                        ,
    input  wire                         rstn                       ,
    input  wire        [   2:0]         baudrate                   ,
    input  wire        [   7:0]         data                       ,
    input  wire                         ena                        ,
    output reg                          tx                         ,
    output reg                          tx_done                     
    );
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++\
//++++++++++++++++  Parameter & Signal  +++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++/
reg                    [  12:0]         baud_clock                 ;
reg                    [  12:0]         div_cnt                    ;
reg                    [   3:0]         bps_cnt                    ;
//reg                                     ena                        ;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++\
//++++++++++++++++  Main code  ++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++/
always @(*) begin                                                   //baudrate choose
        case (baudrate)
            3'd0:baud_clock<=13'd433;
            3'd1:baud_clock<=13'd867;
            3'd2:baud_clock<=13'd1301;
            3'd3:baud_clock<=13'd2603;
            3'd4:baud_clock<=13'd5207;
            default: baud_clock<=13'd433;
        endcase
end

always @(posedge clk or negedge rstn) begin                         //div_cnt
    if(!rstn)
        div_cnt<=0;
    else if(ena)begin
        if(div_cnt==baud_clock)
            div_cnt<=0;
        else
            div_cnt<=div_cnt+1'b1;
    end
    else
        div_cnt<=0;
end

always @(posedge clk or negedge rstn) begin                         //bps_cnt
    if(!rstn)
        bps_cnt<=0;
    else if(ena)begin
        if(div_cnt==0)
        begin
            if(bps_cnt==11)
                bps_cnt<=0;
            else
                bps_cnt<=bps_cnt+1'b1;
        end
    end
        else
            bps_cnt<=0;
end

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        tx<=0;
    else begin
        case (bps_cnt)
            0:tx<=1;
            1:tx<=0;
            2:tx<=data[0];
            3:tx<=data[1];
            4:tx<=data[2];
            5:tx<=data[3];
            6:tx<=data[4];
            7:tx<=data[5];
            8:tx<=data[6];
            9:tx<=data[7];
            10:tx<=1;
            11:tx<=1;
            default:tx<=1;
        endcase
    end
end

always @(posedge clk or negedge rstn) begin                         //tx_done
    if(!rstn)
        tx_done<=0;
    else if((bps_cnt==11)&&(div_cnt==1))
        tx_done<=1;
    else
        tx_done<=0;
end

/*always @(posedge clk or negedge rstn) begin                       //ena
    if(!rstn)
        ena<=0;
    else if(bps_cnt==0)
        ena<=1;
    else if(tx_done)
        ena<=0;
end*/
endmodule

Counter code every 10ms:

module uart_tx_count(
    clk,
    rstn,
    tx
    );
    input                               clk                        ;
    input                               rstn                       ;
    output                              tx                         ;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++\
//++++++++++++++++  Parameter & Signal  +++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++/
reg                    [   7:0]         data                       ;
wire                                    tx_done                    ;
reg                                     ena                        ;
reg                    [  18:0]         count_10ms                 ;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++\
//++++++++++++++++  Main code  ++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++/
urat_tx urat_tx_inst(
    .clk                               (clk                       ),
    .rstn                              (rstn                      ),
    .baudrate                          (3'd0                      ),
    .data                              (data                      ),
    .ena                               (ena                       ),
    .tx                                (tx                        ),
    .tx_done                           (tx_done                   ) 
);

always @(posedge clk or negedge rstn) begin                         //count_10ms
    if(!rstn)
        count_10ms<=0;
    else if(count_10ms==499999)
        count_10ms<=0;
    else
        count_10ms<=count_10ms+1'b1;
end

always @(posedge clk or negedge rstn) begin                         //ena
    if(!rstn)
        ena<=0;
    else if(count_10ms==1)
        ena<=1;
    else if(tx_done)
        ena<=0;
end

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        data<=0;
    else if(tx_done)
        data<=data+1'b1;
end
endmodule

testbench Code:

`timescale 1ns / 1ns
module urat_tx_count_tb();
reg clk;
reg rstn;
wire tx;
uart_tx_count u_uart_tx_count(
    .clk  (clk  ),
    .rstn (rstn ),
    .tx   (tx   )
);
initial clk=1;
always#10 clk=!clk;
initial begin
    rstn=0;
    #201;
    rstn=1;
    #10000000;
    $stop;
end
endmodule

Vivado simulation results:

 

It can be seen that in the 10ms counting cycle, the ena signal is high only when the signal is transmitted, and the transmission end flag signal TX_ When done arrives, pull it down and add 1 to the count value.

Posted by gnawz on Sun, 17 Apr 2022 13:23:55 +0930