本文分享了一種通用的Uart收發Verilog模塊,可實現Uart協議所支持的任意波特率,任意位寬數據(5~8),任意校驗位(無校驗、奇校驗、偶校驗、1校驗、0校驗),任意停止位(1、1.5、2)的數據傳輸。此模塊需要搭配FIFO使用,以消除發送端和接收端波特率不一致導致的累計誤差。此模塊經過多次測試與實際使用驗證,可實現連續10萬+數據無間隔連續發送與接收無錯誤。
一. 什么是UART
UART,Universal Asynchronous Receiver/Transmitter,通用異步收發器,它將并行數據轉換成串行數據進行傳輸。我們通常說的UART有兩種意思,一種是UART協議,是串口通信采用的通用協議;一種是UART串口通信,指的是TTL電平的串口通信。關于UART和串口通信的關系的詳細介紹可參考我的另一篇博客:
串口通信簡介——發展歷史與基本概念_徐曉康的博客
二. UART協議詳解
UART協議由三根線組成,Tx,Rx,Gnd即發送、接收與地,不包含時鐘線,屬于全雙工異步串行通信協議。
UART協議的波特率,Buad,表征數據傳輸的速率,因為UART協議用3.3V/5V表示邏輯1,用0V表示邏輯0,只有兩個有效電平,所以UART協議的波特率與比特率是相等的。這部分屬于數據通信的基本概念,可參考我的另一篇博客:數據通信的基本概念_徐曉康的博客。
需要注意的是, UART協議的數據傳輸雙方需要預先約定好使用相同的波特率,這樣發送端發出的數據才能被接收端正確出來。
UART協議的時序圖:
空閑位:高電平,表示此刻Tx線或Rx線處于空閑狀態,沒有進行數據傳輸。
開始位:一個傳輸時鐘周期的低電平,表示數據傳輸開始。
數據位:UART協議支持一次傳輸5、6、7或8位,每位占用一個傳輸時鐘周期。
校驗位:UART協議共支持五種校驗方式:
無校驗,即NONE,不進行校驗,此時沒有校驗位;
奇校驗,即ODD校驗,指的是如果數據位中1的個數為奇數,奇校驗值為0,否則為1;
偶校驗,即EVEN校驗,指的是如果數據位中1的個數為偶數,偶校驗值為0,否則為1;
1校驗,即MARK校驗,校驗位固定為1;
0校驗,即SPACE校驗,校驗位固定為0。
停止位:停止位表示單次傳輸結束,停止位可占1 / 1.5 / 2個傳輸時鐘周期。
一幀字符與下一幀字符間可間隔任意個空閑位,也可以完全沒有間隔,即停止位后緊跟下一幀的開始位,但這樣的話可能在連續傳輸大量數據時接收數據出錯。因為Uart是無時鐘的,發送端和接收端的波特率必然存在微小偏差,這導致接收端每一位的長度和發送端是不一樣的,所以大量數據的無間隔傳輸會使得位長誤差累加,最終導致接收錯位。
三. uart收發模塊框圖與使用說明
參數:
參數名 | 說明 | 可選值 |
---|---|---|
CLK_FREQ_MHZ | 此模塊的工作時鐘頻率,以MHz為單位 | 任意正數,默認100 |
BAUD | 串口波特率,注意根據板卡uart收發芯片支持的波特率來設置 | 任意正數,默認115200 |
DATA_BITS | 串口一幀包含的數據位的位寬,一般的串口芯片只支持數據位寬5/6/7/8 | 5,6,7,8(默認) |
PARITY | 校驗類型,無校驗(默認),奇校驗,偶校驗,1校驗,0校驗 | “NONE”(默認),"ODD", "EVEN", "MARK", "SPACE" |
STOP_BITS | 停止位位寬,1/1.5/2 | 1(默認),1.5,2 |
信號:
信號分組 | 信號名 | 方向 | 說明 |
---|---|---|---|
與發送FIFO連接的接口 | tx_cclk_fwft_fifo_8wxxd_empty | input | 發送FWFT 8bit任意深度FIFO空接口 |
? | tx_cclk_fwft_fifo_8wxxd_dout[7 : 0] | input | 發送FWFT 8bit任意深度FIFO數據輸出接口 |
? | tx_cclk_fwft_fifo_8wxxd_rd_en | output | 發送FWFT 8bit任意深度FIFO讀取使能接口 |
與接收FIFO連接的接口 | rx_cclk_fwft_fifo_8wxxd_full | input | 接收FWFT 8bit任意深度FIFO滿接口 |
? | rx_cclk_fwft_fifo_8wxxd_din[7 : 0] | output | 接收FWFT 8bit任意深度FIFO數據輸入接口 |
? | rx_cclk_fwft_fifo_8wxxd_wr_en | output | 接收FWFT 8bit任意深度FIFO寫入使能接口 |
接收錯誤指示 | rdata_error | output |
指示接收數據錯誤,高電平有效, 當根據接收數據計算得到的校驗位與實際接收的校驗位不同時, 置高一個時鐘周期 |
物理引腳 | uart_tx | output | uart發送線 |
? | uart_rx | input | uart接收線 |
時鐘與復位 | clk | input | 模塊工作時鐘,應輸入頻率與參數CLK_FREQ_MHZ相等的時鐘 |
? | rstn | input | 同步復位信號,不連接也可正常工作 |
使用說明:
此模塊需要外接一個發送FWFT 8bit任意深度FIFO和一個接收FWFT 8bit任意深度FIFO,FIFO位寬固定為8,即使要發送的數據位寬為5~7,也可以直接寫入FIFO,如設定的Uart數據位寬為5,那么將5bit數據寫入8bit FIFO中,發送模塊也會相應的只取低5位的數據。
當要發送數據時,上層模塊只需往發送FIFO中寫數據即可,此模塊檢測到發送FIFO非空時,就會將FIFO中數據發送出去;
此模塊會將Uart接收到的數據寫入到接收FIFO中,上層模塊需要去接收FIFO中讀數據以拿到Uart接收到的數據。
四. Uart IP框圖與參數設置
可將Uart收發模塊封裝為IP。
五. 頂層模塊代碼
/* ?*?@Author???????:?Xu?Xiaokang ?*?@Email????????:?XudaKang_up@qq.com ?*?@Date?????????:?2022-05-05?1122 ?*?@LastEditors??:?Xu?Xiaokang ?*?@LastEditTime?:?2022-11-09?1124 ?*?@Filename?????: ?*?@Description??: */ /* !?模塊功能:?在uart收發模塊外層再封裝一層FIFO,包含發送FIFO與接收FIFO,以解決波特率誤差導致接收位偏移的問題 *?思路: ??1. */ module?uartRTUseFIFO #( ??parameter?CLK_FREQ_MHZ?=?100, ??parameter?BAUD?????????=?115200,?//?波特率,?9600,?19200,?38400,?57600,?115200,?230400,?460800,?921600 ??parameter?DATA_BITS????=?8,??????//?數據位寬度,?可選5,?6,?7,?8 ??parameter?PARITY???????=?"NONE",?//?校驗?"NONE",?"ODD",?"EVEN",?"MARK",?"SPACE" ??parameter?STOP_BITS????=?1???????//?停止位寬度可選1,?1.5,?2 )( ??//?發送數據?FWFT?FIFO ??input??wire?????????tx_cclk_fwft_fifo_8wxxd_empty, ??input??wire?[7?:?0]?tx_cclk_fwft_fifo_8wxxd_dout, ??output?wire?????????tx_cclk_fwft_fifo_8wxxd_rd_en, ??//?接收數據?FWFT?FIFO ??input??wire?????????rx_cclk_fwft_fifo_8wxxd_full, ??output?wire?[7?:?0]?rx_cclk_fwft_fifo_8wxxd_din, ??output?wire?????????rx_cclk_fwft_fifo_8wxxd_wr_en, ??output?wire?rdata_error,?//?接收錯誤 ??output?wire?uart_tx, ??input??wire?uart_rx, ??input??wire?clk, ??input??wire?rstn ); //++?實例化串口收發模塊?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wire?[DATA_BITS?-?1?:?0]??rdata;???????//?接收到的數據 wire??????????????????????rdata_valid;?//?指示接收數據有效;?高電平有效 wire?[DATA_BITS?-?1?:?0]??tdata;???????//?要發送的數據 wire??????????????????????tdata_valid;?//?指示發送數據有效;?此信號上升沿有效 wire??????????????????????uart_tx_ready;?//?發送準備就緒 uartTx?#( ??.CLK_FREQ_MHZ????(CLK_FREQ_MHZ???), ??.BAUD????????????(BAUD???????????), ??.DATA_BITS???????(DATA_BITS??????), ??.PARITY??????????(PARITY?????????), ??.STOP_BITS???????(STOP_BITS??????) )?uartTx_dut???????( ??.tdata???????????(tdata??????????), ??.tdata_valid?????(tdata_valid????), ??.uart_tx_ready???(uart_tx_ready??), ??.uart_tx?????????(uart_tx????????), ??.clk?????????????(clk????????????), ??.rstn????????????(rstn???????????) ); uartRx?#( ??.CLK_FREQ_MHZ????(CLK_FREQ_MHZ???), ??.BAUD????????????(BAUD???????????), ??.DATA_BITS???????(DATA_BITS??????), ??.PARITY??????????(PARITY?????????), ??.STOP_BITS???????(STOP_BITS??????) )?uartRx_dut???????( ??.rdata???????????(rdata??????????), ??.rdata_valid?????(rdata_valid????), ??.rdata_error?????(rdata_error????), ??.uart_rx?????????(uart_rx????????), ??.clk?????????????(clk????????????), ??.rstn????????????(rstn???????????) ); //--?實例化串口收發模塊?------------------------------------------------------------ //++?發送數據FIFO接口連接?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ assign?tdata?=?tx_cclk_fwft_fifo_8wxxd_dout[DATA_BITS?-?1?:?0]; reg?tx_cclk_fwft_fifo_8wxxd_rd_en_temp; always?@(posedge?clk)?begin ??tx_cclk_fwft_fifo_8wxxd_rd_en_temp?<=?uart_tx_ready?&&?tdata_valid; end assign?tx_cclk_fwft_fifo_8wxxd_rd_en?=?~tx_cclk_fwft_fifo_8wxxd_empty?&&?tx_cclk_fwft_fifo_8wxxd_rd_en_temp; assign?tdata_valid?=?~tx_cclk_fwft_fifo_8wxxd_empty; //--?發送數據FIFO接口連接?------------------------------------------------------------ //++?接收數據FIFO接口連接?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ reg?rdata_valid_r1; always?@(posedge?clk)?begin ??rdata_valid_r1?<=?rdata_valid; end assign?rdata_valid_pedge?=?rdata_valid?&&?~rdata_valid_r1; assign?rx_cclk_fwft_fifo_8wxxd_wr_en?=?~rx_cclk_fwft_fifo_8wxxd_full?&&?rdata_valid_pedge; assign?rx_cclk_fwft_fifo_8wxxd_din?=?rdata; //--?接收數據FIFO接口連接?------------------------------------------------------------ endmodule
六. 回環測試示例
回環測試頂層模塊代碼:
/* ?*?@Author???????:?Xu?Xiaokang ?*?@Email????????:?xuxiaokang_up@qq.com ?*?@Date?????????:?2022-10-31?1645 ?*?@LastEditors??:?Xu?Xiaokang ?*?@LastEditTime?:?2022-11-09?1146 ?*?@Filename?????: ?*?@Description??: */ /* !?模塊功能:?uart收發,實現環路測試,即將接收到的數據發出來 *?思路: ??1. */ module?uartLoopTop ( ??input??logic?uart_rx, ??output?logic?uart_tx, ??input?logic?fpga_input_clk_p, ??input?logic?fpga_input_clk_n, ??input?logic?rstn ); //++?時鐘與復位?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ logic?clk; clk_wiz_0??clk_wiz_0_u0?( ??.clk_in1_p?(fpga_input_clk_p), ??.clk_in1_n?(fpga_input_clk_n), ??.clk_out1??(clk????????) ); //--?時鐘與復位?------------------------------------------------------------ //?++?參數設置?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ localparam?CLK_FREQ_MHZ?=?100; localparam?BAUD?????????=?115200;?//?波特率,?9600,?19200,?38400,?57600,?115200,?230400,?460800,?921600 localparam?DATA_BITS????=?8;??????//?數據位寬度,?可選5,?6,?7,?8 localparam?PARITY???????=?"ODD";??//?校驗?"NONE",?"ODD",?"EVEN",?"MARK",?"SPACE" localparam?STOP_BITS????=?2;??????//?停止位寬度,?可選1,?1.5,?2 //?--?參數設置?------------------------------------------------------------ //++?實例化uart模塊?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ (*?mark_debug?*)?logic?rx_cclk_fwft_fifo_8wxxd_full; (*?mark_debug?*)?logic?[7?:?0]?rx_cclk_fwft_fifo_8wxxd_din; (*?mark_debug?*)?logic?rx_cclk_fwft_fifo_8wxxd_wr_en; (*?mark_debug?*)?logic?rdata_error; (*?mark_debug?*)?logic?tx_cclk_fwft_fifo_8wxxd_empty; (*?mark_debug?*)?logic?[7?:?0]?tx_cclk_fwft_fifo_8wxxd_dout; (*?mark_debug?*)?logic?tx_cclk_fwft_fifo_8wxxd_rd_en; uartRTUseFIFO?#( ??.CLK_FREQ_MHZ?(CLK_FREQ_MHZ), ??.BAUD?????????(BAUD), ??.DATA_BITS????(DATA_BITS), ??.PARITY???????(PARITY), ??.STOP_BITS????(STOP_BITS) )?uartRTUseFIFO_u0?( ??.rx_cclk_fwft_fifo_8wxxd_full??(rx_cclk_fwft_fifo_8wxxd_full?), ??.rx_cclk_fwft_fifo_8wxxd_din???(rx_cclk_fwft_fifo_8wxxd_din??), ??.rx_cclk_fwft_fifo_8wxxd_wr_en?(rx_cclk_fwft_fifo_8wxxd_wr_en), ??.rdata_error???????????????????(rdata_error??????????????????), ??.tx_cclk_fwft_fifo_8wxxd_empty?(tx_cclk_fwft_fifo_8wxxd_empty), ??.tx_cclk_fwft_fifo_8wxxd_dout??(tx_cclk_fwft_fifo_8wxxd_dout?), ??.tx_cclk_fwft_fifo_8wxxd_rd_en?(tx_cclk_fwft_fifo_8wxxd_rd_en), ??.uart_tx???????????????????????(uart_tx??????????????????????), ??.uart_rx???????????????????????(uart_rx??????????????????????), ??.clk???????????????????????????(clk??????????????????????????), ??.rstn??????????????????????????(rstn?????????????????????????) ); //--?實例化uart模塊?------------------------------------------------------------ //++?實例化FWFT?FIFO?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cclk_fwft_fifo_8w1024d?cclk_fwft_fifo_8w1024d_u0?( ??.clk???(clk??),?//?input?wire?clk ??.srst??(~rstn?),?//?input?wire?srst ??.din???(rx_cclk_fwft_fifo_8wxxd_din??),?//?input?wire?[7??:?0]?din ??.wr_en?(rx_cclk_fwft_fifo_8wxxd_wr_en),?//?input?wire?wr_en ??.full??(rx_cclk_fwft_fifo_8wxxd_full?),?//?output?wire?full ??.rd_en?(tx_cclk_fwft_fifo_8wxxd_rd_en),?//?input?wire?rd_en ??.dout??(tx_cclk_fwft_fifo_8wxxd_dout?),?//?output?wire?[7?:?0]?dout ??.empty?(tx_cclk_fwft_fifo_8wxxd_empty)??//?output?wire?empty ); //--?實例化FWFT?FIFO?------------------------------------------------------------ endmodule
測試用的FPGA板卡:明德揚MP5620,板上串口轉USB芯片為SILICON LABS公司的CP2102-GM,其支持的串口協議如下圖所示:
使用的串口上位機工具:正點原子XCOM V2.6。
需要特別注意的是:上位機的波特率與Verilog模塊設置的波特率是有一定差距的,這個差距的原因可能是上位機設置的波特率不準,也可能是因為波特率設置是整數時鐘分頻得到的,無法精確到小數位,所以上位機和Verilog模塊間uart的波特率并不是嚴格一致的,可能是115200與115201的區別,正常情況這么小的差距數據也能被正常識別。但是XCOM在一次性發送多幀時,兩幀之間是沒有任何間隔的,這使得在連續傳輸多幀后,接收數據錯位。
測試界面如下:
在115200波特率,8數據位,ODD校驗,2停止位的條件下,回環測試通過。接著更改參數(需要USB轉串口的芯片支持該組參數),進行其它條件下的試驗,均無問題。
在連續發送10萬個字節數據時,仍能返回正確的數據,這是因為加入了FIFO,波特率誤差被FIFO緩沖消除了,如果不加FIFO且無間隔的發送數據,則連續發送10幾個數據就可能發生接收錯位,使得返回數據與發送數據不一致。
編輯:黃飛
?
評論
查看更多