2.4 基於FPGA的UART協議實現(四)實用UART傳輸FPGA實現(一)

  上一節設計實現的UART只是簡易的實現,沒考慮諸如抖動(起始位抖動會導致數據位傳輸或接收錯誤)等問題,但是對於理解UART傳輸協議卻很有幫助。在單片機中使用時一般串口都可以進行大量數據的傳輸,這得益於單片機在串口傳輸時會有“緩存”空間用於數據的存儲,下面來看看單片機內部串口結構,如圖2 43所示:
在這裏插入圖片描述

          圖2 43 80C51串口組成示意圖
  所以本節設計串口時會參考單片機內部串口結構,主要是將上一節的UART功能拆解並完善部分功能,其中緩衝部分會在後面章節介紹完FIFO(First Input First Output)後,在將最終版本的UART進行封裝,但是本節的串口設計也可以單獨使用,也具有實用性。

2.3.4.1 電平檢測模塊

  在進行實用型UART設計之前,需要先了解在FPGA中怎麼進行電平檢測。
  在FPGA中最常見的就是利用“阻塞”“非阻塞”賦值語句進行電平檢測,在一些特定場合還需要利用延遲達到精確檢測的目的。
  爲了更好地理解“阻塞”“非阻塞”賦值要點,我們需要對Verilog語言中的阻塞賦值和非阻塞賦值的功能和執行時間上的差別有深入的理解。我們定義下面的兩個關鍵字:
  RHS——方程式右手方向的表達式或變量可分別縮寫成 RHS表達式或RHS變量;
  LHS ——方程式左手方向的表達式或變量可分別縮寫成LHS 表達式或LHS變量。
  IEEE Verilog標準定義了有些語句有確定的執行時間,有些語句沒有確定的執行時間。若有兩條或兩條以上的語句準備在同一時間執行,但由於語句的排列順序不同,卻產生了不同的輸出結果。這就是造成Verilog模塊冒險和競爭的原因。爲了避免產生競爭,理解阻塞和非阻塞賦值在執行時間上的差別是至關重要的。
  

1、阻塞賦值

  阻塞賦值用等號(=)表示。爲什麼稱這種賦值爲阻塞賦值呢?因爲在賦值時先計算RHS部分的值,這是賦值語句不允許任何別的Verilog語言的干擾,直到現行的賦值完成時刻,即把RHS賦值給LHS的時刻,它才允許別的賦值語句的執行。
  一般可綜合的賦值操作在RHS不能設定延時(即使是0延時也不允許)。從理論上講,它與後面的賦值語句只有概念上的先後,而無實質的延遲。若在RHS上加延遲,則在延遲時間會阻止賦值語句的執行,延遲後才進行賦值,這種賦值語句是不可綜合的,在需要綜合的模塊設計中不可使用這種風格的代碼。
所謂阻塞的概念是指在同一個always塊中,其後面的賦值語句從概念上是在前一句賦值語句結束之後再開始賦值的。

2、非阻塞賦值

  非阻塞賦值用小於等於號(<=)表示。爲什麼稱這種賦值爲非阻塞賦值呢?因爲在賦值開始時計算RHS表達式,賦值操作時刻結束時更新LHS。在計算非阻塞賦值的RHS表達式和更新LHS期間,其他的Verilog語句,包括其他的非阻塞賦值語句都可能計算RHS表達式和更新LHS。非阻塞賦值允許其他的Verilog語句同時進行操作。非阻塞賦值可以看作兩個步驟的過程:
  (1)在賦值開始時,計算非阻塞賦值RHS表達式;
  (2)在賦值結束時,更新非阻塞賦值LHS表達式。
  非阻塞賦值操作只能用於對寄存器類型變量進行賦值,因此只能用在“initial”塊和“always”塊等過程塊中,而非阻塞賦值不允許用於連續賦值。

*重點:

1)時序電路建模時,用非阻塞賦值;
2)鎖存器電路建模時,用非阻塞賦值;
3)用always塊建立組合邏輯模型時,用阻塞賦值;
4)在同一個always塊中建立時序和組合邏輯電路時,用非阻塞賦值;
5)在同一個alway塊中,不要即用非阻塞又用阻塞賦值;
6)不要在一個以上的always塊中爲同一個變量賦值;
7)用$strobe系統任務來顯示用非阻塞賦值的變量值;
8)在賦值時不要使用#0延遲。*
9)在描述組合邏輯的always塊中用阻塞賦值,則綜合成組合邏輯的電路結構;
10)在描述時序邏輯的always塊中用非阻塞賦值,則綜合成時序邏輯的電路結構。
  圖2 44中是最基本的門電路設計的上升沿、下降沿捕獲電路。由圖可知,Trigger作爲外部觸發信號的輸入,通過FPGA內部的clk與rst_n全局時鐘信號,同步寄存輸出。上升沿與下降沿的邊沿捕獲信號與當前的輸入信號(Trigger)、上一時刻的寄存信號(Trigger_r)有關,主要關係如表2 9所示。
在這裏插入圖片描述

          表2 9 上升沿、下降沿邊沿檢測條件

在這裏插入圖片描述

  邊沿檢測的實現很好理解,當上一時刻爲低電平,而當前時刻爲高電平時,顯而易見這是外部信號的上升沿;反之,當上一時刻爲高電平,而當前時刻爲低電平時,爲外部信號的下降沿。

  與上述原理圖中使用了2輸入與門,通過反相器的輔助,作爲2個時鐘信號的對比。可以採用逆向思維進行推斷;當pos_edge爲高,即捕獲到上升沿時,2輸入與門輸入了2個“1”,由反相器可知D觸發器寄存輸出後的信號爲0,而當前信號爲1。結果分析與設計的一樣。neg_edge的設計也是如此。

  外部輸入的信號有效時,會保持一段時間的高電平,但FPGA不能通過判斷高電平使能信號去進行邏輯分析。由於在FPGA中不便於處理類似的觸發信號(除非外部輸入信號作爲全局時鐘使用),所以通過邊沿採樣技術實現上升沿時刻的使能信號的捕捉,進而實現外部信號的上升沿觸發功能。

  一級的D觸發器寄存在比較時,前一時刻的信號已經同步到同一時鐘域,而當前時刻直接從外部輸入,與FPGA整體邏輯電路不再同一時鐘域。但希望我們的設計能夠全部通過同步電路設計,以提高系統的可靠性,因此可以採用2級D觸發器作爲信號的寄存,同時比較第一級與第二級D觸發器輸出的信號,來檢測外部輸入信號的上升沿或下降沿。根據原理圖設計的電路如圖2 45所示。
在這裏插入圖片描述

      圖2 45 二級寄存器實現的上升沿、下降沿捕獲電路
  對於電平檢測模塊要實現的電路如下:
在這裏插入圖片描述
              圖2 46 電平檢測模塊建模圖
在這裏插入圖片描述
              代碼2 17 detect_module.v

1.	module detect_module  
2.	(  
3.	CLK, RSTn, Pin_In, H2L_Sig, L2H_Sig  
4.	);  
5.	  
6.	input CLK;  
7.	input RSTn;  
8.	input Pin_In;  
9.	output H2L_Sig;  
10.	output L2H_Sig;  
11.	/**********************************/  
12.	// 開發板使用的晶振爲 50MHz, 50M*0.0001-1=4_999  
13.	parameter T100US = 11'd4999;  
14.	/**********************************/  
15.	reg [10:0]Count1;  
16.	reg isEn;  
17.	always @ ( posedge CLK or negedge RSTn )  
18.	if( !RSTn )  
19.	    begin  
20.	        Count1 <= 11'd0;  
21.	        isEn <= 1'b0;  
22.	    end  
23.	else if( Count1 == T100US )  
24.	        isEn <= 1'b1;  
25.	else  
26.	        Count1 <= Count1 + 1'b1;  
27.	/********************************************/  
28.	reg H2L_F1;  
29.	reg H2L_F2;  
30.	reg L2H_F1;  
31.	reg L2H_F2;  
32.	always @ ( posedge CLK or negedge RSTn )  
33.	if( !RSTn )  
34.	    begin  
35.	        H2L_F1 <= 1'b1;  
36.	        H2L_F2 <= 1'b1;  
37.	        L2H_F1 <= 1'b0;  
38.	        L2H_F2 <= 1'b0;  
39.	    end  
40.	else  
41.	    begin  
42.	        H2L_F1 <= Pin_In;  
43.	        H2L_F2 <= H2L_F1;  
44.	        L2H_F1 <= Pin_In;  
45.	        L2H_F2 <= L2H_F1;  
46.	end  
47.	/***********************************/  
48.	assign H2L_Sig = isEn ? ( H2L_F2 & !H2L_F1 ) : 1'b0;  
49.	assign L2H_Sig = isEn ? ( !L2H_F2 & L2H_F1 ) : 1'b0;  
50.	/***********************************/  
51.	endmodule  

  第 13 行定義了 100us 的常量,而第 17~26 行是用於延遲 100us。因爲電平檢測模塊是非常敏感,在復位的一瞬間,電平容易處於不穩定的狀態,我們需要延遲 100us。 isEn = 1 寄存器是表示 100us 的延遲已經完成( 26 行)。
  第 28~31 行,聲明瞭四個寄存器。 H2L_F1, H2L_F2,是針對檢測電平由高變低。相反的 L2H_F1, L2H_F2,則是針對檢測電平由低變高。在 35~38 行,對各個寄存器初始化了,由於 H2L_Fx 是爲了檢測由高變低的電平,所以初始化爲邏輯 1。 L2H_Fx 是爲了檢測由低變高的電平,初值被設置爲邏輯 0。
                  代碼2 18

1.	//初始化  
2.	H2L_F1 <= 1'b1;  
3.	H2L_F2 <= 1'b1;  
4.	//每一個時間的操作  
5.	H2L_F1 <= Pin_In;  
6.	H2L_F2 <= H2L_F1;  
7.	//每一個時間的布爾運算輸出  
8.	Pin_Out = H2L_F2 & !H2L_F1  

  上面代碼是用來檢測電平由高變低。 H2L_F1 和 H2L_F2 的初值都是邏輯 1。假設第一個時間 Pin_In 爲低電平, H2L_F1 就會被賦值位邏輯 0,而 H2L_F1 則是被賦值爲 H2L_F1上一個時間的值(也就是 H2L_F1 的初值)。
  我們知道在第一個時間, H2L_F1 爲邏輯 0, H2L_F2 位邏輯 1。由於對 H2L_F1 的取反操作, H2L_F1 與 H2L_F2“求與”運算,所以這個表達式的輸出是邏輯 1。
  再假設第二個時間 Pin_In 保持爲低電平, H2L_F1 同樣會被賦值爲邏輯 0, 而 H2L_F2則是被賦值爲 H2L_F1 上一個時間的值,亦即邏輯 0 (第一個時間的值)。再經過布爾表達式的運算, 在第二個時間, H2L_F1 是邏輯 0, H2L_F2 是邏輯 0, 所以輸出的結果是邏輯 0.
                表2 10 邏輯說明
在這裏插入圖片描述
  第 41~56 行,正是執行如上的操作,無論是檢測電平由高變低或者由低變高,思路基本都是一樣。而最後的 48~49 行,是關於“電平檢測”的布爾表達式。但是有一點不同的是,Pin_Out 的輸出,是發生在 100us 之後,因爲 100us 之前被 isEn 寄存器所限制(原因之前已經說了)。換一句話說,電平檢測模塊的有效是發生在 100us 的延遲之後。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章