FPGA-串口接收模块

图1  串口接收时序图
图2  串口接收时序框图
  串口接收的信号 rx 相对于 FPGA 内部信号来说是一个异步信号,如不进行处理直接将其输入使用,容易出现时序违例导致亚稳态。因此这里就需要先将信号同步到 FPGA 的时钟域内才可以供后续模块使用,常见的同步方法即使用两级触发器,也就是使用触发器对信号打两拍的方式进行与系统时钟进行同步。
  其次,由串口接收时序图可知,数据接收的起始信号是串行数据由空闲的高电平变为低电平, 即电平由高变低的下降沿,这就需要对下降沿进行检测。可以将同步后的信号放入一个两位的寄存器中,当寄存器的高位为1,低位为0时说明检测到下降沿。当检测到下降沿之后,使能信号rx_en拉高,直到rx_done信号到来之后拉低。串口接收模块波特率的设置与串口发送模块类似。串口接收需要对接收到的rx信号高低电平进行判断,为了判断的准确性,在每一位的中间时刻进行采样,为了实现这一目的,将每一位信号又分为两位,例如对应115200波特率时,baudrate_cnt=1000000000/115200/20/2-1。
  如第一位为起始位0,bps_cnt为1时为该位的中间位置,此时进行采样电平。

 

   串口接收模块代码:

module uart_rx(
    input  wire                         clk                        ,
    input  wire                         rstn                       ,
    input  wire        [   2:0]         baudrate_set               ,
    input  wire                         rx                         ,
    output reg         [   7:0]         data                       ,
    output reg                          rx_done                     
    );
//++++++++++++++++++++++++++++++++++++++++++++++++\
//++++++++++++++    Parameters&Signals  ++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++/
reg                    [  11:0]         baudrate_cnt               ;
reg                    [  11:0]         div_cnt                    ;
reg                    [   4:0]         bps_cnt                    ;
reg                    [   1:0]         r_sync                     ;
reg                    [   1:0]         r_rx                       ;
reg                                     rx_en                      ;
reg                                     p_edge                     ;
wire                                    n_edge                     ;
//++++++++++++++++++++++++++++++++++++++++++++++++\
//++++++++++++++    Main code  +++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++/
always @(*)
    begin
        case (baudrate_set)
            3'd0:baudrate_cnt=1000000000/115200/20/2-1;
            3'd1:baudrate_cnt=1000000000/57600/20/2-1;
            3'd2:baudrate_cnt=1000000000/38400/20/2-1;
            3'd3:baudrate_cnt=1000000000/19200/20/2-1;
            3'd4:baudrate_cnt=1000000000/9600/20/2-1;
            default:baudrate_cnt=1000000000/115200/20/2-1;
        endcase
    end
always @(posedge clk or negedge rstn) begin                         //sync
    if(!rstn)
        r_sync<=2'b00;
    else begin
        r_sync[0]<=rx;
        r_sync[1]<=r_sync[0];
    end
end

always @(posedge clk or negedge rstn) begin                         //register
    if(!rstn)
        r_rx<=2'b00;
    else begin
        r_rx[0]<=r_sync[1];
        r_rx[1]<=r_rx[0];
    end
end

assign n_edge=(r_rx==2'b10);

always@(posedge clk or negedge rstn)//rx_en
        if(!rstn)
            rx_en<=0;
        else if(n_edge)
            rx_en<=1;
        else if(rx_done)
            rx_en<=0;

always @(posedge clk or negedge rstn) begin                         //div_cnt
    if(!rstn)
        div_cnt<=0;
    else if(rx_en)begin
        if(div_cnt==baudrate_cnt)
            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(rx_en)begin
    if(div_cnt==baudrate_cnt/2)begin
        if(bps_cnt==20)
            bps_cnt<=0;
        else
            bps_cnt<=bps_cnt+1'b1;
    end
    end
    else
        bps_cnt<=0;
end

reg                    [   2:0]         r_data[7:0]                ;
reg                    [   2:0]         start_bit                  ;
reg                    [   2:0]         stop_bit                   ;
always @(posedge clk or negedge rstn) begin
    if(!rstn)
    begin
        start_bit<=0;
        r_data[0]<=0;
        r_data[1]<=0;
        r_data[2]<=0;
        r_data[3]<=0;
        r_data[4]<=0;
        r_data[5]<=0;
        r_data[6]<=0;
        r_data[7]<=0;
        stop_bit<=0;
    end
    else begin
        case (bps_cnt)
            0:begin
                start_bit<=0;
                r_data[0]<=0;
                r_data[1]<=0;
                r_data[2]<=0;
                r_data[3]<=0;
                r_data[4]<=0;
                r_data[5]<=0;
                r_data[6]<=0;
                r_data[7]<=0;
                stop_bit<=0;
                end
            1:start_bit<=start_bit+r_rx[1];
            3:r_data[0]<=r_data[0]+r_rx[1];
            5:r_data[1]<=r_data[1]+r_rx[1];
            7:r_data[2]<=r_data[2]+r_rx[1];
            9:r_data[3]<=r_data[3]+r_rx[1];
            11:r_data[4]<=r_data[4]+r_rx[1];
            13:r_data[5]<=r_data[5]+r_rx[1];
            15:r_data[6]<=r_data[6]+r_rx[1];
            17:r_data[7]<=r_data[7]+r_rx[1];
            19:stop_bit<=stop_bit+r_rx[1];
        endcase
    end
end

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        data<=0;
    else if (rx_done)begin
        data[0]<=(r_data[0]==1)?1:0;
        data[1]<=(r_data[1]==1)?1:0;
        data[2]<=(r_data[2]==1)?1:0;
        data[3]<=(r_data[3]==1)?1:0;
        data[4]<=(r_data[4]==1)?1:0;
        data[5]<=(r_data[5]==1)?1:0;
        data[6]<=(r_data[6]==1)?1:0;
        data[7]<=(r_data[7]==1)?1:0;
    end
end

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

endmodule

testbench代码:

`timescale 1ns / 1ns
module uart_rx_tb();
reg                                     clk                        ;
reg                                     rstn                       ;
reg                                     rx                         ;
wire                   [   7:0]         data                       ;
wire                                    rx_done                    ;
uart_rx u_uart_rx(
    .clk                               (clk                       ),
    .rstn                              (rstn                      ),
    .baudrate_set                      (3'd0                      ),
    .rx                                (rx                        ),
    .data                              (data                      ),
    .rx_done                           (rx_done                   ) 
);
    initial clk=1;
    always#10 clk=!clk;
    initial begin
        rstn=0;
        #201;
        rstn=1;
        #200;
        uart_tx_byte(8'h0f);
        @(posedge rx_done);
        #50000;
        uart_tx_byte(8'h53);
        @(posedge rx_done);
        #50000;
        uart_tx_byte(8'hf0);
        @(posedge rx_done);
        #50000;
        uart_tx_byte(8'h0f);
        @(posedge rx_done);
        #50000;
        $stop;
    end

    task uart_tx_byte;                                              //name
    input              [   7:0]         tx_data                    ;//parameter
    begin
        rx = 1;
        #20;
        rx = 0;
        #8680;
        rx = tx_data[0];
        #8680;
        rx = tx_data[1];
        #8680;
        rx = tx_data[2];
        #8680;
        rx = tx_data[3];
        #8680;
        rx = tx_data[4];
        #8680;
        rx = tx_data[5];
        #8680;
        rx = tx_data[6];
        #8680;
        rx = tx_data[7];
        #8680;
        rx = 1;
        #8680;
    end
    endtask
endmodule

  在testbench中使用到了task写法,其格式为task+task名字、输入信号,调用时为task名字(输入)。

  仿真波形:

 

   可以看到,0f、53、f0、0f被正确接收,实现了串口接收模块串行转并行的结果。

posted @ 2022-04-17 13:08  Real马锥  阅读(443)  评论(0编辑  收藏  举报