串行通信:指利用一條數(shù)據(jù)線(xiàn)將資料一位位的順序傳輸。
異步通信:以一個(gè)字符為傳輸單位,通信中兩個(gè)字符間的時(shí)間間隔是不固定的,然而在同一個(gè)字符的兩個(gè)相鄰位代碼間的時(shí)間間隔是固定的。
通信協(xié)議:指通信雙方約定的一些規(guī)則。在使用串口通信的時(shí)候,規(guī)定有:空閑位、起始位、數(shù)據(jù)位、奇偶校驗(yàn)位、停止位。
02串口通信時(shí)序
這個(gè)協(xié)議在 FPGA 內(nèi)部是除 SPI 之外最簡(jiǎn)單的接口吧,其實(shí)就是發(fā)送方與接收方相互認(rèn)定的協(xié)議(暗號(hào)),這種接口數(shù)據(jù)一般是單向傳輸,所以發(fā)送方和接收方通信一般需要兩根數(shù)據(jù)線(xiàn)。
圖1 URAT時(shí)序圖
數(shù)據(jù)線(xiàn)在沒(méi)有數(shù)據(jù)傳輸時(shí)保持高電平,當(dāng)需要傳輸數(shù)據(jù)時(shí),發(fā)送方把數(shù)據(jù)線(xiàn)拉低一段時(shí)間,告訴接收方開(kāi)始傳輸數(shù)據(jù)了。之后把數(shù)據(jù)從低位到高位或者高位到低位(這個(gè)根據(jù)通信雙方的要求確定)依次發(fā)送給對(duì)方(數(shù)據(jù)的位數(shù)雙方應(yīng)該事先確認(rèn)好,通常5~8位數(shù)據(jù))。數(shù)據(jù)發(fā)送完,可能會(huì)發(fā)送一位奇偶校驗(yàn)(這部分在下一節(jié)構(gòu)建完整UART協(xié)議時(shí)細(xì)說(shuō))。最后就是將數(shù)據(jù)線(xiàn)拉高一段時(shí)間表示數(shù)據(jù)傳輸結(jié)束。
在這之間就會(huì)有疑問(wèn),每位數(shù)據(jù)電平持續(xù)時(shí)間到底是多久?
這就引出波特率,通常就是說(shuō)每秒能傳輸多少位數(shù)據(jù),比如波特率為9600bit/s,就是指1秒傳輸9600位數(shù)據(jù)(當(dāng)然這是包含起始位,校驗(yàn)位,停止位在內(nèi)的,所以有效數(shù)據(jù)其實(shí)并沒(méi)有這么多)。當(dāng)使用該波特率時(shí),那每個(gè)電平持續(xù)時(shí)間不就是1/9600秒么。
03串口接收模塊設(shè)計(jì)
首先確定模塊接口信號(hào),肯定有個(gè)串口的輸入信號(hào)uart_rx吧,然后時(shí)鐘信號(hào)clk和復(fù)位信號(hào)rst_n也是不可能少的。接收到數(shù)據(jù)后肯定要輸出吧,所以在加一個(gè)uart_rx,注意該信號(hào)位寬應(yīng)該是可以改變的(因?yàn)榇趨f(xié)議的數(shù)據(jù)位可以改變)。一般還要有一個(gè)信號(hào)用于指示接收到的數(shù)據(jù)什么時(shí)候是有效的,便于后續(xù)模塊使用uart_rx,即uart_tx_vld(為高電平時(shí),表示uart_rx有效)。
信號(hào) | I/O | 位寬 | 含義 |
clk | I | 1 | 系統(tǒng)時(shí)鐘,50MHZ |
rst_n | I | 1 | 系統(tǒng)復(fù)位,低電平有效 |
uart_rx | I | 1 | UART接口輸入信號(hào) |
rx_data | O | 8 | 數(shù)據(jù)輸出信號(hào) |
rx_vld | O | 1 | 數(shù)據(jù)有效指示信號(hào),高電平有效 |
模塊總體思路:有了輸入輸出信號(hào)后,模塊內(nèi)部就是根據(jù)輸入信號(hào)生成輸出信號(hào)而已。通過(guò)觀察圖1時(shí)序知道,每位數(shù)據(jù)傳輸需要使用 1/波特率 的時(shí)間,每次需要傳輸?shù)?“數(shù)據(jù)” 包括起始位,數(shù)據(jù)位,校驗(yàn)位,結(jié)束位。那么以上是不是就對(duì)應(yīng)兩個(gè)計(jì)數(shù)器?所以使用計(jì)數(shù)器data_num來(lái)計(jì)數(shù)一位數(shù)據(jù)傳輸需要的時(shí)間(需要將1/波特率轉(zhuǎn)換為系統(tǒng)時(shí)鐘個(gè)數(shù)作為data_num的結(jié)束條件),使用計(jì)數(shù)器cnt來(lái)計(jì)數(shù)目前傳輸?shù)牡趲孜粩?shù)據(jù)了。整體思路就是如此,大致如下圖,接下來(lái)就是細(xì)節(jié):
圖2 計(jì)數(shù)器架構(gòu)
計(jì)數(shù)器data_num該從什么時(shí)候計(jì)數(shù)?
當(dāng)發(fā)送方發(fā)送起始位時(shí)會(huì)把數(shù)據(jù)線(xiàn)拉低,并且在之后一段時(shí)間內(nèi)發(fā)送起始位,數(shù)據(jù)位等數(shù)據(jù),那么data_num在此期間都要計(jì)數(shù),直到停止位接收完成為止。由此引入一個(gè)標(biāo)志信號(hào)flag,該信號(hào)為高電平時(shí),計(jì)數(shù)器data_num就計(jì)數(shù),當(dāng)計(jì)數(shù)到時(shí)鐘頻率/波特率(1/波特率對(duì)應(yīng)的時(shí)鐘個(gè)數(shù))時(shí)清零。
故計(jì)數(shù)器data_num初始值為0,計(jì)數(shù)條件add_data_num = flag,結(jié)束條件為end_data_num = add_data_num && data_num == BSP_NUM - 1。
flag當(dāng)然就是檢測(cè)到數(shù)據(jù)線(xiàn)下降沿時(shí)拉高,當(dāng)計(jì)數(shù)器cnt計(jì)數(shù)結(jié)束時(shí)拉低,其余時(shí)間保持不變了。
故flag拉高條件:檢測(cè)到uart_rx下降沿,拉低條件為end_cnt。
接下來(lái)就是計(jì)數(shù)器cnt了,cnt表示數(shù)據(jù)線(xiàn)此時(shí)傳輸?shù)氖堑趲孜粩?shù)據(jù)了。當(dāng)計(jì)數(shù)器data_num計(jì)數(shù)結(jié)束時(shí),表示一位數(shù)據(jù)傳輸完成了,此時(shí)cnt就應(yīng)該加一了。當(dāng)計(jì)數(shù)器計(jì)數(shù)到 起始位數(shù)+數(shù)據(jù)位數(shù)+校驗(yàn)位數(shù)+停止位數(shù) 時(shí)表示數(shù)據(jù)傳輸完成了,此時(shí)cnt計(jì)數(shù)結(jié)束并清零,其余時(shí)間保持不變。
故計(jì)數(shù)器cnt初始值為0,計(jì)數(shù)條件add_cnt = end_data_num,計(jì)數(shù)器清零條件end_data_num = add_cnt && cnt == CNT_W - 1。CNT_W = 起始位數(shù)+數(shù)據(jù)位數(shù)+校驗(yàn)位數(shù)+停止位數(shù) 。
接下來(lái)就是接收數(shù)據(jù)并產(chǎn)生輸出信號(hào)了,一般會(huì)在計(jì)數(shù)器data_num計(jì)數(shù)的中部將數(shù)據(jù)線(xiàn)上的數(shù)據(jù)取下來(lái)進(jìn)行保存,此時(shí)的數(shù)據(jù)是比較穩(wěn)定的。由于最終需要輸出的只是數(shù)據(jù)位,本文不考慮校驗(yàn)位,傳輸?shù)?位是起始位,不需要保存。cnt==1時(shí)表示傳輸?shù)?位數(shù)據(jù),需要保存到輸出信號(hào)上的最低位(這是由于串口調(diào)試助手是先發(fā)的最低位,實(shí)際情況要看發(fā)送方先發(fā)高位還是低位)。
flag拉高后,計(jì)數(shù)器data_num進(jìn)行計(jì)數(shù),當(dāng)計(jì)數(shù)完一位數(shù)據(jù)后清零,并且cnt計(jì)數(shù)器進(jìn)行計(jì)數(shù),當(dāng)cnt大于等于1,小于等于8時(shí),表示此時(shí)接收的是數(shù)據(jù)位,將接收到的數(shù)據(jù)保存到rx_data對(duì)應(yīng)位(最好是在data_num為容量的一半時(shí)進(jìn)行保存),當(dāng)cnt計(jì)數(shù)器計(jì)數(shù)完成,表示一組數(shù)據(jù)接收完成,此時(shí)有效指示信號(hào)拉高,并且flag信號(hào)拉低,結(jié)束一組數(shù)據(jù)的接收;所以當(dāng)cnt=1 && data_num == BSP_CNT/2-1時(shí)(BSP_CNT表示波特率對(duì)應(yīng)的時(shí)鐘個(gè)數(shù)),有rx_data[0] <= uart_rx。
經(jīng)過(guò)對(duì)其它位的詳細(xì)分析,最終會(huì)得到這樣的結(jié)果:當(dāng)cnt >=1 && cnt <= DATA_W && data_num == BSP_CNT/2-1 && add_data_num 時(shí)(DATA_W表示每次發(fā)送的數(shù)據(jù)位位數(shù)),rx_data[cnt - 1] <= uart_rx;這樣就產(chǎn)生了輸出數(shù)據(jù)信號(hào)。
之后就是產(chǎn)生輸出有效指示信號(hào),該信號(hào)當(dāng)然是接收完數(shù)據(jù)時(shí)產(chǎn)生的,其實(shí)可以在計(jì)數(shù)器cnt計(jì)數(shù)結(jié)束時(shí)產(chǎn)生。但數(shù)據(jù)在接收完數(shù)據(jù)位后,其實(shí)數(shù)據(jù)就已經(jīng)接收完成了,此時(shí)就可以把輸出有效指示信號(hào)拉高了,這樣后續(xù)模塊就可以提前使用接收到的數(shù)據(jù)。所以當(dāng)cnt == DATA_W && add_data_num && data_num == BSP_NUM/2-1時(shí)將rx_data_vld拉高,其余時(shí)間拉低。
如果想要保證輸出數(shù)據(jù)線(xiàn)上數(shù)據(jù)比較干凈,不出現(xiàn)接收過(guò)程中的無(wú)效數(shù)據(jù),那么可以將rx_data和rx_data_vld在rx_data_vld有效時(shí)才進(jìn)行輸出,其余時(shí)間保持不變。
最后還要注意,數(shù)據(jù)線(xiàn)是其他芯片或者設(shè)備輸入的信號(hào),為了減小亞穩(wěn)態(tài)出現(xiàn)的機(jī)率,一般需要將數(shù)據(jù)線(xiàn)上的信號(hào)通過(guò)寄存器寄存兩個(gè)時(shí)鐘。由于還需要檢測(cè)數(shù)據(jù)線(xiàn)的下降沿,所以還要把該信號(hào)延遲一個(gè)時(shí)鐘,最終將接收到的信號(hào)uart_rx打三拍(前兩拍用于同步處理,最后一拍用于檢測(cè)輸入信號(hào)的下降沿),然后通過(guò)uart_rx_ff1和uart_rx_ff2檢測(cè)出下降沿,把標(biāo)志信號(hào)flag拉高。
整體時(shí)序圖如下:
圖3 整體時(shí)序
時(shí)序圖可能在手機(jī)上沒(méi)法看,所以將上圖各個(gè)部分截圖放在下面:
圖4 準(zhǔn)備傳輸數(shù)據(jù)
當(dāng)計(jì)數(shù)器data_num=BSP_NUM/2-1的時(shí)候,將uart_rx_ff2的數(shù)據(jù)保存到rx_data的第cnt-1位,下圖為最低位,至于為什么是uart_rx_ff2,而不是uart_rx_ff1,通過(guò)下圖可知uart_rx_ff2與計(jì)數(shù)器data_num是對(duì)齊的,所以該信號(hào)會(huì)更準(zhǔn)。由于串口傳輸數(shù)據(jù)還是比較慢的,使用這兩個(gè)信號(hào)對(duì)結(jié)果基本沒(méi)有影響。
圖5 接收最低位數(shù)據(jù)
接收完8位數(shù)據(jù),將輸出使能信號(hào)拉高,rx_data的x表示不確定,因?yàn)閳D4~圖6只能確定接收的最高位和最低位數(shù)據(jù),其余時(shí)序沒(méi)有畫(huà),中間的時(shí)序類(lèi)似,所以省略雙波浪線(xiàn)表示中間的數(shù)據(jù)傳輸省略。
最后接收完停止位后,end_cnt拉高表示接收一次數(shù)據(jù)傳輸完成,將兩個(gè)計(jì)數(shù)器清零,并且將標(biāo)志信號(hào)flag拉低。
圖6 接收完8位數(shù)據(jù)
上述將模塊內(nèi)部信號(hào)講完了,如果要實(shí)現(xiàn)功能完全夠了,但是在調(diào)用模塊時(shí),我們往往不習(xí)慣去改模塊內(nèi)部的參數(shù),這就需要通過(guò)parameter和localparam添加一些參數(shù),來(lái)自動(dòng)設(shè)置計(jì)數(shù)器位寬,計(jì)數(shù)器結(jié)束條件等等。其實(shí)人為需要設(shè)置的就是波特率、數(shù)據(jù)位位數(shù)、校驗(yàn)位數(shù)、停止位數(shù)(起始位是必須的,故不考慮設(shè)置參數(shù)),由于計(jì)算波特率對(duì)應(yīng)是時(shí)鐘個(gè)數(shù)時(shí)還需要知道系統(tǒng)時(shí)鐘頻率,所以增加一個(gè)系統(tǒng)時(shí)鐘頻率參數(shù)。
所以parameter就定義波特率BPS、時(shí)鐘頻率FCLK、數(shù)據(jù)位數(shù)DATA_W、校驗(yàn)位數(shù)CHECK_W 、停止位數(shù)STOP_W 。而localparam需要通過(guò)parameter定義的參數(shù)得到波特率對(duì)應(yīng)的 時(shí)鐘數(shù)BPS_CNT=時(shí)鐘頻率FCLK/波特率BPS ,計(jì)數(shù)器data_num需要計(jì)數(shù)到BPS_CNT,所以需要通過(guò)BPS_CNT計(jì)算出計(jì)數(shù)器data_num的位寬BPS_CNT_W,可以通過(guò)以下函數(shù)實(shí)現(xiàn)。
function integer clogb2(input integer depth);begin if(depth==0) clogb2 = 1; else if(depth!=0) for(clogb2=0;depth>0;clogb2=clogb2+1) depth=depth>>1; end endfunction
接下來(lái)就是cnt計(jì)數(shù)器的結(jié)束條件了,可以由localparam定義CNT_NUM=DATA_W + CHECK_W + STOP_W。在利用上面函數(shù)計(jì)算出該計(jì)數(shù)器的位寬CNT_NUM_W就行了,內(nèi)部信號(hào)根據(jù)這些常量變化即可。
由此設(shè)計(jì)的模塊在例化時(shí),只需要修改parameter的幾個(gè)常量即可,不要對(duì)模塊內(nèi)部代碼做任何處理,這部分操作不會(huì)占用額外資源,在綜合工具對(duì)齊進(jìn)行綜合時(shí)就會(huì)處理,不會(huì)消耗FPGA的除法器之類(lèi)的資源。
根據(jù)以上分析,直接得到以下代碼,基本上不需要仿真調(diào)試。
04 參考代碼
//--############################################################################################### //--# Designer : 發(fā)送一位數(shù)據(jù)所需系統(tǒng)時(shí)鐘數(shù)計(jì)算方式BPS_CNT = 1000_000_000/(Tclk*比特率), //Tclk是系統(tǒng)時(shí)鐘周期,單位ns。 //--############################################################################################### module uart_rx #( parameter FCLK = 50_000_000 ,//系統(tǒng)時(shí)鐘頻率,默認(rèn)50MHZ; parameter BPS = 9600 ,//串口波特率; parameter DATA_W = 8 ,//接收數(shù)據(jù)位數(shù)以及輸出數(shù)據(jù)位寬; parameter CHECK_W = 0 ,//校驗(yàn)位,0代表無(wú)校驗(yàn)位; parameter STOP_W = 1 //1位停止位; )( input clk ,//系統(tǒng)工作時(shí)鐘50MHZ input rst_n ,//系統(tǒng)復(fù)位信號(hào),低電平有效 input uart_rx ,//UART接口輸入信號(hào) output reg [DATA_W-1:0] rx_out ,//數(shù)據(jù)輸出信號(hào) output reg rx_out_vld //數(shù)據(jù)有效指示信號(hào) ); localparam BPS_CNT = FCLK/BPS;//波特率為9600bit/s,當(dāng)波特率為115200bit/s時(shí),DATA_115200==434; localparam BPS_CNT_W = clogb2(BPS_CNT-1);//根據(jù)BPS_CNT調(diào)用函數(shù)自動(dòng)計(jì)算計(jì)數(shù)器data_num位寬; localparam CNT_NUM = DATA_W + CHECK_W + STOP_W;//計(jì)數(shù)器計(jì)數(shù)值; localparam CNT_NUM_W = clogb2(CNT_NUM);//根據(jù)計(jì)數(shù)器cnt的值,利用函數(shù)自動(dòng)計(jì)算此計(jì)數(shù)器的位寬; reg rx_vld ;//表示接收完一組串口發(fā)來(lái)的數(shù)據(jù)了; reg uart_rx_ff0 ; reg uart_rx_ff1 ; reg uart_rx_ff2 ; reg flag ; reg [BPS_CNT_W-1:0] data_num ; reg [CNT_NUM_W-1:0] cnt ; reg [DATA_W-1:0] rx_data ; wire add_data_num ; wire end_data_num ; wire add_cnt ; wire end_cnt ; /******************注釋開(kāi)始****************** 自動(dòng)計(jì)算信號(hào)位寬; ******************注釋結(jié)束******************/ function integer clogb2(input integer depth);begin if(depth==0) clogb2 = 1; else if(depth!=0) for(clogb2=0;depth>0;clogb2=clogb2+1) depth=depth>>1; end endfunction /******************注釋開(kāi)始****************** 接收一位數(shù)據(jù)所用時(shí)間計(jì)數(shù)器data_num,初始值為0,當(dāng)接收到數(shù)據(jù)時(shí)進(jìn)行計(jì)數(shù), 當(dāng)一位數(shù)據(jù)接收完成時(shí)清零; ******************注釋結(jié)束******************/ always@(posedge clk or negedge rst_n)begin if(!rst_n)begin data_num <= {{BPS_CNT_W}{1'b0}}; end else if(add_data_num)begin if(end_data_num) data_num <= {{BPS_CNT_W}{1'b0}}; else data_num <= data_num + {{{BPS_CNT_W-1}{1'b0}},1'b1}; end end assign add_data_num = flag; assign end_data_num = add_data_num && data_num==BPS_CNT-1; //接受一組數(shù)據(jù)所用時(shí)間; always@(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt <= {{CNT_NUM_W}{1'b0}}; end else if(add_cnt)begin if(end_cnt) cnt <= {{CNT_NUM_W}{1'b0}}; else cnt <= cnt + {{{CNT_NUM_W-1}{1'b0}},1'b1}; end end assign add_cnt = end_data_num; assign end_cnt = add_cnt && cnt== CNT_NUM-1; /******************注釋開(kāi)始****************** PC端相對(duì)應(yīng)于FPGA為異步接口,為預(yù)防亞穩(wěn)態(tài)產(chǎn)生,對(duì)接收數(shù)據(jù)進(jìn)行打兩拍處理,由于需要采集信號(hào)下降沿,故打三拍處理; ******************注釋結(jié)束******************/ always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//三個(gè)寄存器組成移位寄存器,初始化為0; {uart_rx_ff2,uart_rx_ff1,uart_rx_ff0} <= 3'd0; end else begin//時(shí)鐘上升沿時(shí),將uart_rx信號(hào)移入移位寄存器,其余位左移一位; {uart_rx_ff2,uart_rx_ff1,uart_rx_ff0} <= {uart_rx_ff1,uart_rx_ff0,uart_rx}; end end always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin flag <= 1'b0; end else if(uart_rx_ff2==1 && uart_rx_ff1==0)begin//取UART_RX信號(hào)下降沿 flag <= 1'b1; end else if(end_cnt)begin//一組數(shù)據(jù)接收完畢; flag <= 1'b0; end end //在中間時(shí)刻對(duì)輸入數(shù)據(jù)進(jìn)行采集,并且將數(shù)據(jù)存入rx_data; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_data <= {{DATA_W}{1'b0}}; end else if(cnt>=1 && cnt<=DATA_W && add_data_num && data_num==BPS_CNT/2-1)begin rx_data[cnt-1] <= uart_rx_ff2; end end //在接收完數(shù)據(jù)后,指示產(chǎn)生rx_data信號(hào)有效; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_vld <= 1'b0; end else begin rx_vld <= (cnt==CNT_NUM-1 && add_data_num && data_num==BPS_CNT/2-1); end end //當(dāng)接收完一組數(shù)據(jù)后,將接收到的數(shù)據(jù)經(jīng)過(guò)一組觸發(fā)器暫存后輸出; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin// rx_out <= 0; end else if(rx_vld)begin rx_out <= rx_data; end end //在接收完數(shù)據(jù)后,拉高一個(gè)時(shí)鐘,指示產(chǎn)生rx_out信號(hào)有效; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_out_vld <= 1'b0; end else begin rx_out_vld <= rx_vld; end end endmodule05 modelism仿真
仿真部分的代碼,通過(guò)一個(gè)任務(wù)task實(shí)現(xiàn)串口數(shù)據(jù)的發(fā)送,由于上述設(shè)計(jì)不支持校驗(yàn)位,所以這個(gè)模塊設(shè)置校驗(yàn)位也是沒(méi)有用的。
發(fā)送數(shù)據(jù)只需要調(diào)用tx();任務(wù)即可,內(nèi)部直接輸入待發(fā)送數(shù)據(jù),數(shù)據(jù)位寬依舊通過(guò)DATA_W設(shè)置,波特率BPS設(shè)置。
參考代碼:
`timescale 1 ns/1 ns module uart_rx_test(); parameter CYCLE = 20;//The unit is ns. The default value is 10ns; parameter RST_TIME = 10;//Reset time: Reset 3 clock widths by default; parameter STOP_TIME = 1000;//Time for simulation running after reset (unit: clock cycle). Simulation stops after 1000 clocks are run by default; // uart_rx Parameters parameter FCLK = 50_000_000;//系統(tǒng)時(shí)鐘頻率; parameter BPS = 9600 ;//串口波特率; parameter BPS_CNT = FCLK/BPS ;//波特率對(duì)應(yīng)時(shí)鐘數(shù),不用手動(dòng)修改該參數(shù); parameter DATA_W = 8 ;//接收數(shù)據(jù)位數(shù)以及輸出數(shù)據(jù)位寬; parameter CHECK_W = 2'b01 ;//校驗(yàn)位,2'b00代表無(wú)校驗(yàn)位,2'b01表示奇校驗(yàn),2'b10表示偶校驗(yàn),2'b11無(wú)效。 parameter STOP_W = 2'b11 ;//停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位; // uart_rx Inputs reg clk ; reg rst_n ; reg uart_tx ; // uart_rx Outputs wire [DATA_W-1:0] rx_out ; wire rx_out_vld ; //例化串口接收模塊; uart_rx #( .FCLK (FCLK ), .BPS (BPS ), .DATA_W (DATA_W ), .CHECK_W (CHECK_W ), .STOP_W (STOP_W )) u_uart_rx ( .clk ( clk ), .rst_n ( rst_n ), .uart_rx ( uart_tx ), .rx_out ( rx_out ), .rx_out_vld ( rx_out_vld ) ); //The local clock is generated at 100 MB; initial begin clk = 0; forever #(CYCLE/2) clk=~clk; end //Generate reset signal; initial begin rst_n = 1; #2; rst_n = 0; #(RST_TIME*CYCLE);//復(fù)位完成; rst_n = 1; end //Input signal din assignment method; initial begin #1;uart_tx = 1; //初始化時(shí)輸入高電平; #(100*CYCLE); //Start assigning values; tx(8'ha5); //以串口形式發(fā)送8'h5a; #(500*CYCLE); //發(fā)送完成后延遲500個(gè)時(shí)鐘; tx(8'h5a); //之后發(fā)送數(shù)據(jù)8'h59; #(500*CYCLE); //發(fā)送完成后延遲500個(gè)時(shí)鐘; $stop; //Stop simulation; end //模擬串口發(fā)送函數(shù),1位起始位,1位停止位,無(wú)校驗(yàn)位,8位數(shù)據(jù),先發(fā)低位; integer i;//用于控制循環(huán)次數(shù); task tx( input [DATA_W-1:0] data //串口待發(fā)送數(shù)據(jù); ); begin @(posedge clk);//延遲一個(gè)時(shí)鐘后發(fā)送起始位; #1; uart_tx = 1'b0; repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個(gè)時(shí)鐘; for(i=0 ; i<8 ; i=i+1)begin #1; uart_tx = data[i]; repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個(gè)時(shí)鐘; end if(CHECK_W == 2'b01)begin #1;uart_tx = ~(^data);//奇校驗(yàn)時(shí),發(fā)送數(shù)據(jù); repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個(gè)時(shí)鐘; end else if(CHECK_W == 2'b10)begin #1;uart_tx = (^data);//偶校驗(yàn)時(shí),發(fā)送數(shù)據(jù); repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個(gè)時(shí)鐘; end @(posedge clk);//延遲一個(gè)時(shí)鐘后發(fā)送停止位; #1; uart_tx = 1'b1; if(STOP_W == 2'b01)//1位停止位; repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個(gè)時(shí)鐘; else if(STOP_W == 2'b10)//2位停止位; repeat(2*BPS_CNT) @(posedge clk);//延遲2*BPS_CNT個(gè)時(shí)鐘; else if(STOP_W == 2'b11)//1.5位停止位; repeat(BPS_CNT*3/2) @(posedge clk);//延遲1.5*BPS_CNT個(gè)時(shí)鐘; end endtask endmodule
仿真運(yùn)行結(jié)果(rx_out先接收到8’ha5,后接收到8’h5a):
圖7仿真結(jié)果
查看細(xì)節(jié):開(kāi)始接收數(shù)據(jù)(起始位)片段:
圖8起始位仿真
接收最低位數(shù)據(jù)仿真如下:
圖9接收第一位數(shù)據(jù)
接收最后一位數(shù)據(jù),并且產(chǎn)生輸出有效指示信號(hào),下一個(gè)時(shí)鐘將數(shù)據(jù)輸出,此時(shí)串口傳輸實(shí)際上并沒(méi)有完成,最后一位數(shù)據(jù)才傳輸一半(data_num計(jì)數(shù)器才2603==5208/2-1),但已經(jīng)接收到完整數(shù)據(jù),所以直接輸出,節(jié)省時(shí)間,但flag信號(hào)依舊位高電平,表示該模塊還處于工作狀態(tài)。
圖10接收完最后一位數(shù)據(jù)
計(jì)數(shù)器data_num計(jì)數(shù)到5208-1并且計(jì)數(shù)器cnt計(jì)數(shù)器到8,表示一次傳輸完成,flag信號(hào)拉低,并且兩個(gè)計(jì)數(shù)器清零,表示完成傳輸,仿真如下:
圖11接收完停止位
06 綜合測(cè)試
這個(gè)工程很久了,之前學(xué)的時(shí)候使用quartus綜合的,綜合效果如下所示:
圖12quartus綜合工程
對(duì)應(yīng)的RTL模塊視圖(由于時(shí)鐘頻率FCLK和波特率BPS參數(shù)設(shè)置會(huì)影響計(jì)數(shù)器cnt和data_num的位寬,所以不同數(shù)據(jù)匯總和出不同的電路,下圖為時(shí)鐘頻率50MHZ,波特率9600的RTL視圖):
圖13RTL視圖
對(duì)系統(tǒng)時(shí)鐘頻率進(jìn)行約束后,最大時(shí)鐘頻率為120.86MHZ,遠(yuǎn)大于實(shí)際的50MHZ,滿(mǎn)足時(shí)序要求;
圖14系統(tǒng)最大工作時(shí)鐘頻率
signal tap II 測(cè)試
將程序下載到FPGA,打開(kāi)串口調(diào)試助手,設(shè)置波特率9600,發(fā)送數(shù)據(jù)0XA5,使用signal tap II抓取數(shù)據(jù)8'hA5。
圖15串口助手發(fā)送數(shù)據(jù)
串口調(diào)試助手發(fā)送數(shù)據(jù)0XB3,使用signal tap II抓取數(shù)據(jù)8'hB3。
圖16signal tap接收串口助手發(fā)送數(shù)據(jù)
串口調(diào)試助手發(fā)送數(shù)據(jù)0X5a,使用signal tap II抓取數(shù)據(jù)8'h5A。
圖17 調(diào)試
07總結(jié)
其實(shí)最主要的就是能夠根據(jù)協(xié)議找到合適的主架構(gòu),然后根據(jù)該架構(gòu)去產(chǎn)生輸出信號(hào)。
本文就利用兩個(gè)計(jì)數(shù)器作為主架構(gòu),根據(jù)計(jì)數(shù)器的狀態(tài)生成輸出信號(hào),切記我們需要的并不是計(jì)數(shù)器,而是計(jì)數(shù)器生成的輸出信號(hào),如果使用parameter要考慮模塊內(nèi)部各種會(huì)改變的數(shù)據(jù)與這些參數(shù)的關(guān)系。
最好不要留需要手動(dòng)修改的數(shù)據(jù),這種數(shù)據(jù)如果忘記修改,會(huì)對(duì)后續(xù)設(shè)計(jì)造成很大影響,浪費(fèi)調(diào)試時(shí)間。
來(lái)源:本文轉(zhuǎn)載自數(shù)字站公眾號(hào)
-
FPGA
+關(guān)注
關(guān)注
1629文章
21736瀏覽量
603319 -
接收模塊
+關(guān)注
關(guān)注
1文章
20瀏覽量
10459 -
串口
+關(guān)注
關(guān)注
14文章
1554瀏覽量
76512 -
uart
+關(guān)注
關(guān)注
22文章
1235瀏覽量
101387 -
串行通信
+關(guān)注
關(guān)注
4文章
572瀏覽量
35398
原文標(biāo)題:基于FPGA的UART串口接收模塊
文章出處:【微信號(hào):FPGA研究院,微信公眾號(hào):FPGA研究院】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論