本文為明德揚原創及錄用文章,轉載請注明出處!
案例編號:000900000024
1.1 總體設計
1.1.1 概述
串行接口簡稱串口,也稱串行通訊接口或者串行通信接口,是采用串行通信方式的擴展接口。串行接口是指數據一位一位地順序傳送 ,其特點是通信線路簡單,只要一對傳輸線就可以實現雙向通信(可以直接利用電話線作為傳輸線),從而大大降低了成本,特別適合用于遠距離通信,但傳送速度較慢。一條信息的各位數據被逐位按順序傳送的通信方式稱作串行通信。串行通信的特點是:數據位的傳送,按位順序進行,最少只需要一根傳輸線即可完成;成本低,但傳送速度慢。串行通信的距離可以從幾米到幾千米;根據信息的傳送方向,串行通信可以進一步分為單工、半雙工和全雙工三種。
串口的出現是在 1980 年前后,數據傳輸率是 115kbps~230kbps。串口出現的初期是為了實現連接計算機外設的目的,初期串口一般用來連接鼠標和外置 Modem 以及老式攝像頭和寫字板等設備。
串口也可以應用于兩臺計算機(或設備)之間的互聯及數據傳輸。由于串口不支持熱插拔及傳輸速率較低,部分新主板和大部分便攜電腦已開始取消該接口。串口多用于工業控制和測量設備以及部分通信設備中。
1.1.2 設計目標
本練習要求實現串口回環功能,具體功能要求如下:
1、上位機于 FPGA 之間通過串口進行通信,規定波特率為 9600,數據位為 8bit,無奇偶校
驗位,停止位為 1。
2、 FPGA 內部有一個可保存 128 字節的 FIFO。
3、 FPGA 從上位機接收到數據后,將數據保存到 FIFO 中。
4、當 FIFO 保存的數據超過 60 個數據時,啟動發送數據操作:讀取 FIFO 的數據,將數據返
回給上位機。
5、 在啟動發送數據操作過程中,如果 FIFO 變空,結束發送操作,等待下一次的啟動。
注意:上位機接收到的數據與發送的數據相同,不能多也不能少。
1.1.3 系統結構框圖
系統結構框圖如下所示:
1.1.4 模塊功能
? 串口接收模塊實現功能
1、 將輸入數據進行同步化處理。
2、 解析串口時序,將有效數據進行串并轉換。
? 數據處理模塊實現功能
1、 包含一個 FIFO,用來存儲接收到的數據。
2、 滿足發送條件后,讀出 FIFO 的數據送給下游模塊。
? 串口發送模塊實現功能
1、 將接收到的數據進行并串轉換,發送給上位機。
1.1.5 頂層信號
1.1.6 參考代碼
下面是本工程的頂層代碼:
1. module com_prj(
2. rst_n ,
3. clk ,
4. rx_uart,
5. tx_uart
6. );
7. parameter BPS = 5208;
8.
9. input rst_n ;
10. input clk ;
11. input rx_uart ;
12. output tx_uart ;
13.
14. wire [7:0] uart_in ;
15. wire uart_in_vld ;
16. wire [7:0] uart_out ;
17. wire uart_out_vld;
18. wire rdy ;
19.
20. uart_rx#(.BPS(BPS)) uart_rx(
21. .clk (clk ),
22. .rst_n (rst_n ),
23. .din (rx_uart ),
24. .dout (uart_in ),
25. .dout_vld(uart_in_vld)
26. );
27. uart_tx#(.BPS(BPS)) uart_tx(
28. .clk (clk ),
29. .rst_n (rst_n ),
30. .din (uart_out ),
31. .din_vld (uart_out_vld),
32. .rdy (rdy ),
33. .dout (tx_uart )
34. );
35. data_handle u_data_handle(
36. .clk (clk ),
37. .rst_n (rst_n ),
38. .din (uart_in ),
39. .din_vld (uart_in_vld ),
40. .dout (uart_out ),
41. .dout_vld(uart_out_vld),
42. .rdy (rdy )
43. );
44.
45. endmodule
1.2 串口接收模塊設計
1.2.1 接口信號

1.2.2 設計思路
? UART 異步串行口簡介
數據通信的基本方式可分為并行通信和串行通信兩種:
并行通信:
是指利用多條數據線將一個資料的各位同時傳送。特點是傳輸速度快,適合用于短距
離通信,但要求通信速率較高的應用場合。
串行通信:
是指利用一條傳輸線將資料一位位的順序傳送。特點是通信線路簡單,利用簡單的線
纜就可以實現通信,減低成本,適用于遠距離通信,但傳輸速度慢的應用場合。
在 FPGA 看來,串口只有兩根線,一根線用于接收,一根線用于發送。

? UART 異步串行口的傳輸格式
異步通信以一個字符為傳輸單位,通信中兩個字符間的時間間隔是不固定的,然而在同一個字符
中的兩個相鄰位代碼間的時間間隔是固定的。
通信協議(通信規程):是指通信雙方約定的一些規則。在使用異步串行口傳送一個字符的信息
時,對資料格式有如下規定:規定有空閑位、起始位、數據位、奇偶校驗位、停止位。通訊時序圖如
下:

每一個數據位的寬度等于傳送波特率的倒數。微機異步串行通信中,常用的波特率為 110,150,
300,600,1200,2400,4800,9600 ,19200,38400,115200 等。代表每個碼元傳輸的速
率。在二進制數據傳輸中,波特率和比特率相同都是為每個比特數據傳輸的速率,其倒數為 1bit
數據的寬度,也就是 1bit 數據持續的時間。確定這一時間,就可用 FPGA 構造計數器實現比特
周期的延時,從而實現特定波特率的數據傳輸。
? 開始前,線路處于空閑狀態,送出連續"1"。傳送開始時首先發一個"0"作為起始位,
然后出現在通信線上的是字符的二進制編碼數據。
? 每個字符的數據位長可以約定為 5 位、6 位、7 位或 8 位。
? 后面是奇偶校驗位,顧名思義,檢驗位適用于數據校驗。分為奇校驗和偶校驗。奇校驗需
要保證傳輸數據總共有奇數個邏輯高電平,偶校驗則需要保證傳輸數據有偶數個邏輯高電
平。即"奇偶"指的是數據中(包括該校驗位)1 的個數。例如:傳輸的數據是 01000011,
如果校驗方式是奇校驗,則校驗位為 0 ,若是偶校驗則校驗位是 1.傳輸中校驗位不是必須
項,雙方可以約定不要校驗位,或者使用奇/偶校驗方式。
? 最后是表示停止位的"1"信號,這個停止位可以約定持續 1 位、1.5 位或 2 位的時間寬
度。由于每臺設備有其自己的時鐘,很可能再通信中兩臺設備間出現小小的不同步。因此
停止位不僅僅是表示傳輸的結束,并且提供一個校正時鐘同步的機會,讓從機可以正確的
識別下一輪數據的起始位。停止位的位數越多,不同時鐘同步的容忍程度就越大,但是數
據傳輸速率也越慢。
? 至此一個字符傳送完畢,線路又進入空閑,持續為"1"。經過一段隨機的時間后,下一個
字符開始傳送才又發出起始位。
? 架構設計
上位機發送的數據會按照上圖所示串口的時序圖的順序過來,因此我們需要按照其對應的格式進
行接收。發送 8 位數據 data 前,串口接收數據線會先變 0 并持續一段時間(起始位),然后發送 da
ta[0]、data[1],以此類推直至發送完 data[7],發送每位數據時都會持續一段時間,發送完畢后數據
線會變為 1 并持續一段時間(結束位)。至此,完成數據的發送。可以看出每段有效信號的開始前和
結束后,都會有特殊信號:有效數據開始前會有一段變 0 的信號,用以告知 FPGA 開始傳送數據;
結束后會有一段變 1 的信號,告知 FPGA 此數據傳送結束。
由上面時序分析可知,當我們檢測到數據線從高電平(空閑位)變為低電平(起始位)就表示開
始數據的傳輸了,因此需要進行下降沿的檢測,檢測方法如下:

該檢測方法主要利用 D 觸發器打拍來實現。
din:輸入串口數據。
din_ff0:輸入串口數據經過一級緩存之后的信號,目的是為了將異步信號同步化。
din_ff1:輸入串口數據經過二級緩存之后的信號,目的是為了減少亞穩態造成的影響。
din_ff2:輸入串口數據經過三級緩存之后的信號,目的是為了檢測信號的下降沿。
flag_add:接收狀態指示信號,當在時鐘上升沿檢測到 din_ff1 等于 0 并且 din_ff2 等于 1 的時候
表示檢測到了下降沿,此時將 flag_add 信號拉高,表示進入到接收狀態。
根據串口時序,可以提出兩個計數器的架構,如下圖所示:

該架構由兩個計數器組成:時鐘計數器 cnt 和數據計數器 data_num。
時鐘計數器 cnt:用于計數發送 1bit 數據所需要的時間,加一條件為 flag_add,表示進入接收狀
態時就開始計數;結束條件為數 5208 個,開發板晶振時鐘是 50M,對應周期為 20ns,每位數據的
持續時間為 104166ns/20ns=5208.3 個時鐘周期,近似為 5208 個時鐘周期。
數據計數器 data_num:用于對接收的每一比特數據進行計數,加一條件為 end_cnt,表示接收
到 1bit 的數據就加一;結束條件為數 9 個,一個起始位加上八個數據位,共 9 位,數完就清零。
? 注意事項
1、 串口接收模塊中的數據計數器一定不要把停止位也數上去,否則在接受的數據的時
候會出錯。感興趣的同學可以使用 signaltap 抓取信號進行分析(仿真沒有用)。
2、 由于工程中串口的每 1bit 數據傳輸所需要的時間是近似值,也就是存在誤差,因此
串口接收在采集數據的時候,需要在數據的中間時刻進行采樣,這樣才能保證數據
的正確性。
1.2.3 參考代碼
下面是使用明德揚的計數器模板等寫出來的本模塊代碼。
1. always @ (posedge clk or negedge rst_n) begin
2. if(!rst_n) begin
3. din_ff0 <= 1'b1;
4. din_ff1 <= 1'b1;
5. din_ff2 <= 1'b1;
6. end
7. else begin
8. din_ff0 <= din;
9. din_ff1 <= din_ff0;
10. din_ff2 <= din_ff1;
11. end
12. end
13.
14. always @ (posedge clk or negedge rst_n)begin
15. if(!rst_n) begin
16. flag_add <= 1'b0;
17. end
18. else if(din_ff2 & ~din_ff1) begin
19. flag_add <= 1'b1;
20. end
21. else if(data_num==4'd8&&end_cnt) begin
22. flag_add <= 1'b0;
23. end
24. end
25.
26. always @ (posedge clk or negedge rst_n)begin
27. if(!rst_n)begin
28. cnt <= 0;
29. end
30. else if(add_cnt)begin
31. if(end_cnt)begin
32. cnt <= 0;
33. end
34. else begin
35. cnt <= cnt+1'b1;
36. end
37. end
38. else begin
39. cnt <= 0;
40. end
41. end
42. assign add_cnt = flag_add;
43. assign end_cnt = add_cnt && cnt == BPS-1;
44.
45.
46.
47. always @(posedge clk or negedge rst_n) begin
48. if (rst_n==0) begin
49. data_num <= 0;
50. end
51. else if(add_data_num) begin
52. if(end_data_num)
53. data_num <= 0;
54. else
55. data_num <= data_num+1 ;
56. end
57. end
58. assign add_data_num = end_cnt;
59. assign end_data_num = add_data_num && data_num == 9-1 ;
60.
61. always @ (posedge clk or negedge rst_n)begin
62. if(!rst_n) begin
63. dout <= 8'd0;
64. end
65. else if(add_cnt && cnt==BPS_P-1 && data_num!=0) begin
66. dout<={din,{dout[7:1]}};
67. end
68. else begin
69. dout<=dout;
70. end
71. end
72.
73. always @ (posedge clk or negedge rst_n)begin
74. if(!rst_n) begin
75. dout_vld <= 1'b0;
76. end
77. else if(add_data_num && data_num == 4'd8) begin
78. dout_vld <= 1'b1;
79. end
80. else begin
81. dout_vld <= 1'b0;
82. end
83. end
1.3 數據處理模塊設計
1.3.1 接口信號

1.3.2 設計思路
? FIFO 原理簡介
FIFO(first input first output),即先進先出的數據緩存器,本質上還是 RAM,與普通存儲器的區別:沒有外部讀寫地址線,這樣使用起來非常簡單。特點就是只能順序寫入數據,順序讀出數據,其數據地址由內部讀寫指針自動加一完成,不能像普通存儲器那樣可以由地址線決定讀取或寫入某個指定的地址。
FIFO 根據讀寫時鐘的區別,分為同步 FIFO 和異步 FIFO。同步 FIFO 讀寫共用一個相同的時鐘;
異步 FIFO 讀寫可以使用不同的時鐘。由于異步 FIFO 內部存在同步化電路,因此資源占用要比同步FIFO 大。
再 FPGA 中,FIFO 的使用主要有兩種場合,應用于緩存和跨時鐘域處理。FIFO 本身就是存儲器,自然可以用作數據的緩存,只不過在選擇上需要跟普通的 RAM 區分開。又由于異步 FIFO 的存
在,可以使得其寫側和讀側時鐘不同,因此又可用作跨時鐘域處理。
更多關于 FIFO 的內容,可以關注明德揚 FIFO 專題課,或者是明德揚免費的 FIFO 課程
? 架構設計
按照功能要求,本模塊需要對接收的數據進行存儲,當存夠 60 個的時候開始發送,下面是本模
塊的架構圖。

本模塊大致分為三部分:FIFO 的寫控制電路、FIFO、FIFO 的讀控制電路。
寫控制電路:只要輸入數據有效,就將數據寫進 FIFO,主要信號為寫數據 data 和寫使能 wrreq。
FIFO ip 核:存儲數據。
讀控制電路:當 FIFO 內部有 60 個數據、FIFO 非空并且下游模塊準備好接收數據的時候,就開
始讀。主要信號為輸出數據 q、FIFO 有效數據量指示信號 usedw、空指示信號 empty、讀使能 rdreq。
1.3.3 參考代碼
84. assign data = din ;
85. assign wrreq = din_vld ;
86.
87.
88. my_fifo u_my_fifo (
89. .clock(clk ),
90. .data (data ),
91. .rdreq(rdreq),
92. .wrreq(wrreq),
93. .empty(empty),
94. .q (q ),
95. .usedw(usedw)
96. );
97.
98.
99. always@(*)begin
100. if(rd_flag && empty==1'b0 && rdy)
101. rdreq = 1'b1;
102. else
103. rdreq = 1'b0;
104. end
105.
106. always@(posedge clk or negedge rst_n)begin
107. if(rst_n==1'b0)begin
108. rd_flag <= 1'b0;
109. end
110. else if(rd_flag==1'b0 && usedw>=60) begin
111. rd_flag <= 1'b1;
112. end
113. else if(rd_flag==1'b1 && empty)begin
114. rd_flag <= 1'b0;
115. end
116. end
117.
118. always @(posedge clk or negedge rst_n)begin
119. if(rst_n==1'b0)begin
120. dout <= 0;
121. end
122. else begin
123. dout <= q;
124. end
125. end
126.
127. always @(posedge clk or negedge rst_n)begin
128. if(rst_n==1'b0)begin
129. dout_vld <= 1'b0;
130. end
131. else begin
132. dout_vld <= rdreq;
133. End
134. end
1.4 串口發送模塊設計
1.4.1 接口信號

1.4.2 設計思路
串口發送就是要按照串口的時序,對數據進行并串轉換,在介紹架構之前,先來描述一下本模塊
一些重要的信號的含義:
工作狀態指示信號 tx_flag:初始狀態為 0,表示處于空閑狀態,當檢測到輸入數據有效的時候,
該信號變為 1,表示處于工作狀態,當數據發送完之后,重新拉低,進入空閑狀態,等待下一個數據的輸入。
數據鎖存信號 tx_data_tmp:位寬為 8bit,初始狀態為 0,當模塊處于空閑狀態,并且輸入數據
有效的時候,就接收輸入的數據進行鎖存。由于輸入數據在串口發送的時間內都需要用到,因此為了防止數據發生變化,導致串口發送出現問題,所以引入此信號進行數據的鎖存。
準備好接收指示信號 rdy:當接收到輸入數據,或者模塊處于發送狀態的時候,此信號為 1,表
示不能接收數據,其他情況為 1,表示準備好接收數據了。由于上游模塊數據輸出速率要比串口發送模塊發送的速度快得多,所以需要此信號來控制上游模塊的輸出,當串口發送模塊收到有效數據的時候,需要立刻把此信號拉高,所以需要用組合邏輯產生。
我們可以得到兩個計數器組成的計數器架構,如下圖所示:

該架構由兩個計數器組成:時鐘計數器 cnt 和數據計數器 data_num。
時鐘計數器 cnt:用于計數發送 1bit 數據所需要的時間,加一條件為 tx_flag,表示進入工作狀態
時就開始計數;結束條件為數 5208 個,開發板晶振時鐘是 50M,對應周期為 20ns,每位數據的持
續時間為 104166ns/20ns=5208.3 個時鐘周期,近似為 5208 個時
溫馨提示:明德揚2023推出了全新課程——邏輯設計基本功修煉課,降低學習FPGA門檻的同時,增加了學習的趣味性,并組織了考試贏積分活動
http://www.cqqtmy.cn/ffkc/415.html
(點擊→了解課程詳情?)感興趣請聯系易老師:13112063618(微信同步)