1. 程式人生 > 其它 >FPGA-串列埠接收模組

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被正確接收,實現了串列埠接收模組序列轉並行的結果。