FPGA设计案例--通用UART收发模块设计

        关于UART通信总线的基本概念相比大家都已熟知,这里就不再赘述了。本文旨在设计一个支持多种波特率、数据位宽以及校验位的通用UART接收和发送模块,接下来直接给出具体的代码。


通用UART收发模块Verilog代码

module Uart_Tx#(parameter   BAUD_RATE = 115200      ,//Baud Rate:9600,19200,38400,57600,115200,230400; default:115200
                            CLK_FREQY = 50_000_000  ,//System clk; default:50MHz
                            STOP_BIT  = 1           ,//Stop Bit Width:1 bit,2 bit; default:1 bit
                            CHECK_TYP = "NONE"      ,//Parity Check:0-NONE,1-ODD,2-EVEN; default:0
                            DATA_WIDE = 8            //Data Wide:4,5,6,7,8 bit; default:8bit
)(
    input                           clk     ,
    input                           rst_n   ,
    
    input   [DATA_WIDE - 1 : 0]     din     ,
    input                           din_vld ,
    output                          ready   ,

    output                          uart_txd    
);

//function declare
    function integer clog2b(input integer data);begin 
        data = data - 1;
        for(clog2b = 0;data > 0;clog2b = clog2b + 1)begin
            data = data >> 1;
        end 
    end 
    endfunction

    localparam  BIT_PERIOD = CLK_FREQY / BAUD_RATE          ,//The number of clock cycles  to Tx one bit
                CHK_TYPE   = (CHECK_TYP == "NONE") ? 0 : 
                             (CHECK_TYP == "ODD" ) ? 1 : 
                             (CHECK_TYP == "EVEN") ? 2 : 0  ,
                CNT0_W     = clog2b(BIT_PERIOD)             ,//Count BIT_PERIOD -  1 clock cycles
                CNT1_W     = clog2b(16)                     ;//DATA_WIDE bit to Tx


    localparam  IDLE  = 6'b00_0001,
                START = 6'b00_0010,//Tx Start bit 
                DATA  = 6'b00_0100,//Tx Data bit
                CHECK = 6'b00_1000,//Tx Check bit
                STOP  = 6'b01_0000,//Tx Stop bit
                DONE  = 6'b10_0000;//Tx Done

//Signal declare
    reg     [5 : 0]             state_c                     ;
    reg     [5 : 0]             state_n                     ;

    wire                        idle2start                  ; 
    wire                        start2data                  ; 
    wire                        data2stop                   ; 
    wire                        data2check                  ; 
    wire                        check2stop                  ; 
    wire                        stop2done                   ; 

    reg     [CNT0_W - 1 : 0]    cnt0                        ;//0 ~ BIT_PERIOD - 1
    wire                        add_cnt0                    ;
    wire                        end_cnt0                    ;
    
    reg     [CNT1_W - 1 : 0]    cnt1                        ;//0 ~ xx - 1
    wire                        add_cnt1                    ;
    wire                        end_cnt1                    ;
    reg     [CNT1_W - 1 : 0]    xx                          ;

    reg     [DATA_WIDE - 1 : 0] tx_data                     ;//Latch din 
    reg                         tx_bit                      ;//Tx bit

//FSM
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    always @(*)begin
        case(state_c)
            IDLE :state_n = idle2start ? START : state_c;
            START:state_n = start2data ? DATA  : state_c;
            DATA :state_n = data2stop  ? STOP  : (data2check ? CHECK : state_c);
            CHECK:state_n = check2stop ? STOP  : state_c;
            STOP :state_n = stop2done  ? DONE  : state_c;            
            DONE :state_n = IDLE;
            default:state_n = IDLE;
        endcase 
    end
    assign idle2start = state_c == IDLE  && (din_vld);
    assign start2data = state_c == START && (end_cnt1);//Start bit(bit0) Tx done
    assign data2stop  = state_c == DATA  && (CHK_TYPE == 0 && end_cnt1); 
    assign data2check = state_c == DATA  && (CHK_TYPE != 0 && end_cnt1);
    assign check2stop = state_c == CHECK && (end_cnt1);    
    assign stop2done  = state_c == STOP  && (end_cnt1);

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            cnt0 <= 0;
        end
        else if(add_cnt0)begin
            if(end_cnt0)
                cnt0 <= 0;
            else
                cnt0 <= cnt0 + 1;
        end
    end
    assign add_cnt0 = state_c != IDLE;
    assign end_cnt0 = add_cnt0 && cnt0 == BIT_PERIOD - 1;  

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            cnt1 <= 0;
        end
        else if(add_cnt1)begin
            if(end_cnt1)
                cnt1 <= 0;
            else
                cnt1 <= cnt1 + 1;
        end
    end
    assign add_cnt1 = end_cnt0;
    assign end_cnt1 = add_cnt1 && cnt1 == xx - 1;   

    always @(*)begin
        if(state_c == START)
            xx = 1;
        else if(state_c == DATA)
            xx = DATA_WIDE;
        else if(state_c == CHECK)
            xx = 1;
        else //if(state_c == STOP)
            xx = STOP_BIT;
    end

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            tx_data <= 'd0;
        end
        else if(din_vld)begin
            tx_data <= din;
        end
    end

//Tx bit
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            tx_bit <= 1'b1;
        end
        else begin
            case(state_c)
                START:tx_bit <= 1'b0;
                DATA :tx_bit <= tx_data[cnt1];
                CHECK:tx_bit <= CHK_TYPE == 2 ? (^tx_data) : (~^tx_data);
                STOP :tx_bit <= 1'b1;
                default:tx_bit <= 1'b1;
            endcase 
        end
    end

//Output
    assign uart_txd = tx_bit;
    assign ready    = state_c == IDLE;

endmodule 
module Uart_Rx#(parameter   BAUD_RATE = 115200      ,//Baud Rate:9600,19200,38400,57600,115200,230400; default:115200
                            CLK_FREQY = 50_000_000  ,//System clk; default:50MHz
                            STOP_BIT  = 1           ,//Stop Bit Width:1 bit,2 bit; default:1 bit
                            CHECK_TYP = "NONE"      ,//Parity Check:0-NONE,1-ODD,2-EVEN; default:0
                            DATA_WIDE = 8            //Data Wide:4,5,6,7,8 bit; default:8bit
)(
    input                           clk         ,
    input                           rst_n       ,

    input                           uart_rxd    ,
    output  [DATA_WIDE - 1 : 0]     rx_dout     ,
    output                          rx_dout_vld     
);

 //function declare
    function integer clog2b(input integer data);begin 
        data = data - 1;
        for(clog2b = 0;data > 0;clog2b = clog2b + 1)begin
            data = data >> 1;
        end 
    end 
    endfunction

    localparam  BIT_PERIOD = CLK_FREQY / BAUD_RATE                                                                  ,//The number of clock cycles  to Rx one bit
                RX_WIDE    = (CHECK_TYP == "NONE") ? (DATA_WIDE + 1 + STOP_BIT) : (DATA_WIDE + 1 + 1 + STOP_BIT)    ,//Start bit + Data bit (+ Check bit) + Stop bit
                CHK_TYPE   = (CHECK_TYP == "NONE") ? 0 : (CHECK_TYP == "ODD" ) ? 1 : (CHECK_TYP == "EVEN") ? 2 : 0  ,
                CNT0_W     = clog2b(BIT_PERIOD / 16)                                                                ,//16 times oversampling
                CNT1_W     = clog2b(16)                                                                             ,//16 
                CNT2_W     = clog2b(RX_WIDE)                                                                        ;//RX_WIDE bit to Rx

    localparam  IDLE  = 6'b00_0001,
                START = 6'b00_0010,//Rx Start bit 
                DATA  = 6'b00_0100,//Rx Data bit
                CHECK = 6'b00_1000,//Rx Check bit
                STOP  = 6'b01_0000,//Rx Stop bit
                DONE  = 6'b10_0000;//Rx Done

//signale declare
    reg     [5 : 0]             state_c                     ;
    reg     [5 : 0]             state_n                     ;

    wire                        idle2start                  ; 
    wire                        start2data                  ; 
    wire                        start2idle                  ; 
    wire                        data2stop                   ; 
    wire                        data2check                  ; 
    wire                        check2stop                  ; 
    wire                        stop2done                   ; 

    reg     [CNT0_W - 1 : 0]    cnt0                        ;//0 ~ BIT_PERIOD / 16 - 1
    wire                        add_cnt0                    ;
    wire                        end_cnt0                    ;
    
    reg     [CNT1_W - 1 : 0]    cnt1                        ;//0 ~ 16 - 1
    wire                        add_cnt1                    ;
    wire                        end_cnt1                    ;

    reg     [CNT2_W - 1 : 0]    cnt2                        ;//0 ~ xx - 1
    wire                        add_cnt2                    ;
    wire                        end_cnt2                    ;
    reg     [CNT2_W - 1 : 0]    xx                          ;

    reg     [3 : 0]             oversample_acc              ;//Oversampling accumulator
    reg     [RX_WIDE - 1 : 0]   rx_data                     ;
    reg                         uart_rxd_sync               ;//Sync uart_rxd input
    reg     [1 : 0]             uart_rxd_reg                ;//Register uart_rxd_sync 2 clk cycle
    wire                        uart_rxd_nedge              ;//uart_rxd_sync negedge 
    reg     [DATA_WIDE - 1 : 0] data                        ;//Output rx_dout register
    reg                         data_vld                    ;
    

//FSM
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    always @(*)begin
        case(state_c)
            IDLE :state_n = idle2start ? START : state_c;
            START:state_n = start2data ? DATA  : (start2idle ? IDLE  : state_c);
            DATA :state_n = data2stop  ? STOP  : (data2check ? CHECK : state_c);
            CHECK:state_n = check2stop ? STOP  : state_c;
            STOP :state_n = stop2done  ? DONE  : state_c;  
            DONE :state_n = IDLE;
            default:state_n = IDLE;
        endcase 
    end
    assign idle2start = state_c == IDLE  && (uart_rxd_nedge);
    assign start2data = state_c == START && (end_cnt2 && oversample_acc[3] == 1'b0);    //Start bit(bit0) is low,contiue
    assign start2idle = state_c == START && (end_cnt2 && oversample_acc[3] == 1'b1);    //Start bit(bit0) is not low
    assign data2stop  = state_c == DATA  && (CHK_TYPE == 0 && end_cnt2); 
    assign data2check = state_c == DATA  && (CHK_TYPE != 0 && end_cnt2); 
    assign check2stop = state_c == CHECK && (end_cnt2);
    assign stop2done  = state_c == STOP  && (end_cnt2);

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            cnt0 <= 0;
        end
        else if(add_cnt0)begin
            if(end_cnt0)
                cnt0 <= 0;
            else
                cnt0 <= cnt0 + 1;
        end
    end
    assign add_cnt0 = state_c != IDLE;
    assign end_cnt0 = add_cnt0 && cnt0 == (BIT_PERIOD >> 4) - 1;  

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            cnt1 <= 0;
        end
        else if(add_cnt1)begin
            if(end_cnt1)
                cnt1 <= 0;
            else
                cnt1 <= cnt1 + 1;
        end
    end
    assign add_cnt1 = end_cnt0;
    assign end_cnt1 = add_cnt1 && cnt1 == 16 - 1;   
    
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            cnt2 <= 0;
        end
        else if(add_cnt2)begin
            if(end_cnt2)
                cnt2 <= 0;
            else
                cnt2 <= cnt2 + 1;
        end
    end
    assign add_cnt2 = end_cnt1;
    assign end_cnt2 = add_cnt2 && cnt2 == xx - 1;   

    always @(*)begin
        if(state_c == START)
            xx = 1;
        else if(state_c == DATA)
            xx = DATA_WIDE;
        else if(state_c == CHECK)
            xx = 1;
        else //if(state_c == STOP)
            xx = STOP_BIT;
    end

//Sync Register
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            uart_rxd_sync <= 1'b1;
            uart_rxd_reg  <= 2'b11;
        end
        else begin
            uart_rxd_sync <= uart_rxd;
            uart_rxd_reg  <= {uart_rxd_reg[0],uart_rxd_sync};
        end
    end
    assign uart_rxd_nedge = ~uart_rxd_reg[0] & uart_rxd_reg[1];


//Over sample 
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            oversample_acc <= 'd0;
        end
        else if(idle2start || end_cnt1)begin //High priority
            oversample_acc <= 'd0;
        end 
        else if(end_cnt0)begin
            case(cnt1)
                3,4,5,6,7,8,9,10,11,12:oversample_acc <= oversample_acc + uart_rxd_reg[1];
                default               :oversample_acc <= oversample_acc;
            endcase
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            rx_data <= 'd0;
        end
        else if(end_cnt1)begin
            rx_data <= {oversample_acc[3],rx_data[RX_WIDE - 1 : 1]};
        end
    end

//Check the result is correct or not
    generate
        case(CHK_TYPE)
            1:begin    //Ji Parity
                always @(posedge clk or negedge rst_n)begin
                    if(~rst_n)begin
                        data_vld <= 1'b0;
                    end
                    else begin
                        data_vld <= state_c == DONE && (^rx_data[1 +: DATA_WIDE + 1]);
                    end
                end
            end 
            2:begin    //Ou Parity    
                always @(posedge clk or negedge rst_n)begin
                    if(~rst_n)begin
                        data_vld <= 1'b0;
                    end
                    else begin
                        data_vld <= state_c == DONE && (~^rx_data[1 +: DATA_WIDE + 1]);
                    end
                end
            end 
            default:begin       //No Parity
                always @(posedge clk or negedge rst_n)begin
                    if(~rst_n)begin
                        data_vld <= 1'b0;
                    end
                    else begin
                        data_vld <= state_c == DONE;
                    end
                end                    
            end
        endcase  

        always @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                data <= 'd0;
            end
            else if(state_c == DONE)begin
                data <= rx_data[1 +: DATA_WIDE];
            end
        end


    endgenerate

//Output
    assign rx_dout     = data    ;
    assign rx_dout_vld = data_vld;

endmodule 

仿真测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值