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