FPGA-串列埠接收模組
阿新 • • 發佈:2022-04-17
圖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被正確接收,實現了串列埠接收模組序列轉並行的結果。