2.3.4.2 UART發送模塊
發送模塊的時序及相關介紹前面都已經寫的很清楚了,就是將波特率模塊分開,這樣方便後期維護及修改,同時也爲了下一節接收模塊的設計做統一處理。
整個模塊要實現的結構如下:
圖2 47 發送模塊(tx_module.v)整體結構圖
首先,有一個頂層模塊(tx_module.v),這個模塊會包含(例化)成下面的兩個子模塊(bps_module.v和tx_control_module.v)。
bps_module.v波特率產生模塊,簡單說就是分頻模塊。
tx_control_module.v發送控制模塊,就是利用狀態機(簡化狀態機)進行發送開始位、數據位和停止位。
表2 11 發送模塊(tx_module.v)模塊間信號定義
表2 12 發送模塊(tx_module.v)頂層模塊信號定義
從根本上說,tx_module.v 是實現“定時發送”的功能。假設我配置的波特率爲9600 bps,那麼每隔0.000104166666666667s bps_module.v 這個模塊就會產生一個高脈衝給tx_control_module.v,該控制模塊會按這個節拍將數據一位一位的數據發送出去。假設配 置的波特率爲115200 bps,那麼每隔0.000086805555555555555555555555555556s tx_bps_module.v 這個模塊就會產生一個高脈衝給tx_control_module.v,該控制模塊會按這個節拍將數據一位一位的數據發送出去。一幀數據有 11 位,那麼 bps_module.v 需要產生 12 次定時。
首先看下bps_module.v模塊,波特率產生模塊設計和之前任意波形發生器一樣。bps_module.v 是作爲“定時”的功能。當 TX_En_Sig/En_Sig 拉低的時候,它是處於隨眠的狀態。一旦 TX_En_Sig/En_Sig 拉高電平,那麼 bps_module.v 就開始計數。然後定時產生一個高脈衝經 BPS_CLK 給 tx_control_module.v。整體結構如下:
圖2 48 波特率產生模塊(bps_module)整體結構圖
表2 13 波特率產生模塊(bps_module)引腳說明
按照1.5.5任意分頻器設計進行bps_module模塊設計。
第一步,相位計數器-32位K步計數器,如代碼2 19所示。
代碼2 19 相位計數器-32位K步計數器
1. //------------------------------------------------------
2. //RTL1: Precise fractional frequency for uart bps clock
3. reg [31:0] cnt;
4. always@(posedge CLOCK or negedge RST_n)
5. begin
6. if(!RST_n)
7. cnt <= 0;
8. else if(En_Sig)
9. cnt <= cnt + BPS_CNT;
10. end
11.
12. //------------------------------------------------------
代碼2 19中,在全局時鐘CLOCK驅動下,進行K步計數,cnt爲0~232-1的地址。BPS_CNT是一個16倍波特率(16*115200)的宏定義的分頻參數,方便例化或者維護。
第二步,合成頻率方波的生成。
在第一步完成了0~232-1尋址後,需要對地址進行比較、劃分,得到一個方波信號。如代碼2 20所示。
代碼2 20 合成頻率方波的生成
1. //RTL2: Equal division of the Frequency division clock
2. reg cnt_equal;
3. always@(posedge CLOCK or negedge RST_n)
4. begin
5. if(!RST_n)
6. cnt_equal <= 0;
7. else if(En_Sig)
8. begin
9. if(cnt < 32'h7FFF_FFFF)
10. cnt_equal <= 0;
11. else
12. cnt_equal <= 1;
13. end
14. end
15.
16. //------------------------------------------------------
代碼2 20中,第9行,32‘h7FFF_FFFF爲232-1的中點,因此它可以作爲一種“門限電壓”來實現合成頻率的方波輸出。
第四步,分頻時鐘使能信號的生成
主要利用“邊沿檢測技術”生成使能時鐘信號,如代碼2 21所示。
代碼2 21 分頻時鐘使能信號的生成
1. //RTL3: Generate enable clock for clock
2. reg cnt_equal_r;
3. always@(posedge CLOCK or negedge RST_n)
4. begin
5. if(!RST_n)
6. cnt_equal_r <= 0;
7. else if(En_Sig)
8. cnt_equal_r <= cnt_equal;
9. end
10.
11. assign BPS_CLKen = (~cnt_equal_r & cnt_equal) ? 1'b1 : 1'b0;
12. assign BPS_CLK = cnt_equal_r;
通過邊沿檢測技術,捕獲了合成時鐘的上升沿作爲時鐘使能信號,同時輸出cut_equel_r信號作爲分頻時鐘BPS_CLK。
完整的代碼如下:
代碼2 22 波特率產生模塊(bps_module)完整代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-03 01:32:40
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-05-06 21:22:18
7. //# Description:
8. //# @Modification History: 2019-05-06 21:22:18
9. //# Date By Version Change Description:
10. //# ========================================================================= #
11. //# 2019-05-06 21:22:18
12. //# ========================================================================= #
13. //# | | #
14. //# | OpenFPGA | #
15. //****************************************************************************//
16. /***********************************************************************
17. fc : Refrence clock = 50MHz = 50*10^6
18. fo : Output clock
19. K : Counter Step
20. fo = fc*[K/(2^32)]
21. 100MHz K = fo*(2^32)/fc = fo*(2^32)/(100*10^6) = 42.94967296 * fo
22. 50MHz K = fo*(2^32)/fc = fo*(2^32)/(50*10^6) = 85.89934592 * fo
23. ***********************************************************************/
24. `timescale 1ns/1ps
25. module bps_module
26. #(
27. //BPS_CNT = 85.89934592 * fo for 50Mhz
28. // parameter BPS_CNT = 32'd21990233 //256000bps 21990233
29. // parameter BPS_CNT = 32'd10995116 //128000bps 10995116
30. // parameter BPS_CNT = 32'd9895605 //115200bps 9895605
31. parameter BPS_CNT = 32'd824634//9600bps 824634
32. //BPS_CNT = 42.949 67296 * fo for 100Mhz
33. // parameter BPS_CNT = 32'd175921860 //256000bps
34. // parameter BPS_CNT = 32'd87960930 //128000bps
35. // parameter BPS_CNT = 32'd79164837 //115200bps
36. // parameter BPS_CNT = 32'd412317 //9600bps
37. )
38. (
39. //global clock
40. input CLOCK,
41. input RST_n,
42.
43. //user interface
44. input En_Sig,
45. output BPS_CLK,
46. output BPS_CLKen
47. );
48.
49.
50. /********************************/
51.
52. //------------------------------------------------------
53. //RTL1: Precise fractional frequency for uart bps clock
54. reg [31:0] cnt;
55. always@(posedge CLOCK or negedge RST_n)
56. begin
57. if(!RST_n)
58. cnt <= 0;
59. else if(En_Sig)
60. cnt <= cnt + BPS_CNT;
61. end
62.
63. //------------------------------------------------------
64. //RTL2: Equal division of the Frequency division clock
65. reg cnt_equal;
66. always@(posedge CLOCK or negedge RST_n)
67. begin
68. if(!RST_n)
69. cnt_equal <= 0;
70. else if(En_Sig)
71. begin
72. if(cnt < 32'h7FFF_FFFF)
73. cnt_equal <= 0;
74. else
75. cnt_equal <= 1;
76. end
77. end
78.
79. //------------------------------------------------------
80. //RTL3: Generate enable clock for clock
81. reg cnt_equal_r;
82. always@(posedge CLOCK or negedge RST_n)
83. begin
84. if(!RST_n)
85. cnt_equal_r <= 0;
86. else if(En_Sig)
87. cnt_equal_r <= cnt_equal;
88. end
89.
90. assign BPS_CLKen = (~cnt_equal_r & cnt_equal) ? 1'b1 : 1'b0;
91. assign BPS_CLK = cnt_equal_r;
92.
93. endmodule
波特率產生後會將時鐘信號送給tx_control_module模塊,模塊的整體結構如下:
圖2 49 發送控制模塊(tx_control_module)整體結構圖
表2 14 發送控制模塊(tx_control_module)引腳說明
tx_control_module.v 控制模塊是最爲中心的一部分,當 TX_En_Sig 拉高電平,同時間bps_module.v 也會開始計數。tx_control_module.v 將 TX_Data 的值,按bps_module.v 產生的定時,有節奏的往 TXD 發送。當一幀數據發送完畢後,就反饋一個 TX_Done_Sig 的高脈衝。
代碼2 23 發送控制模塊(tx_control_module)完整代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-04 00:47:08
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-05-05 22:30:21
7. //# Description:
8. //# @Modification History: 2019-05-05 22:30:21
9. //# Date By Version Change Description:
10. //# ========================================================================= #
11. //# 2019-05-05 22:30:21
12. //# ========================================================================= #
13. //# | | #
14. //# | OpenFPGA | #
15. //****************************************************************************//
16. /**************************************************************************
17. ..IDLE...Start...............UART DATA........................End...IDLE...
18. ________ ______________
19. |____< D0 >< D1 >< D2 >< D3 >< D4 >< D5 >< D6 >< D7 >
20. Bit0 Bit1 Bit2 Bit3 Bit4 Bit5 Bit6 Bit7 Bit8 Bit9
21. **************************************************************************/
22. module tx_control_module
23. (
24. CLOCK, RST_n,
25. TX_En_Sig,
26. TX_Data,
27. BPS_CLK,
28. TX_Done_Sig,
29. TXD
30.
31. );
32.
33. input CLOCK;
34. input RST_n;
35.
36. input TX_En_Sig;
37. input [7:0]TX_Data;
38. input BPS_CLK;
39.
40. output TX_Done_Sig;
41. output TXD;
42.
43. /********************************************************/
44.
45. reg [3:0]i;
46. reg rTX;
47. reg isDone;
48.
49. always @ ( posedge CLOCK or negedge RST_n )
50. if( !RST_n )
51. begin
52. i <= 4'd0;
53. rTX <= 1'b1;
54. isDone <= 1'b0;
55. end
56. else if( TX_En_Sig )
57. case ( i )
58.
59. 4'd0 :
60. if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b1; end
61. 4'd1 :
62. if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b0; end
63.
64. 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9 :
65. if( BPS_CLK ) begin i <= i + 1'b1; rTX <= TX_Data[ i - 2 ]; end
66.
67. 4'd10 :
68. if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b1; end
69.
70. 4'd11 :
71. if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b1; end
72.
73. 4'd12 :
74. if( BPS_CLK ) begin i <= i + 1'b1; isDone <= 1'b1; end
75.
76. 4'd13 :
77. begin i <= 4'd0; isDone <= 1'b0; end
78.
79. //default:i <= 4'd0;
80.
81. endcase
82.
83. /********************************************************/
84.
85. assign TXD = rTX;
86. assign TX_Done_Sig = isDone;
87.
88. /*********************************************************/
89.
90. endmodule
代碼2 23中的設計的核心思想是狀態機,狀態機部分在1.6節已經介紹過了,只不過在寫代碼的時候將狀態機精簡。
當 TX_En_Sig 被拉高(36行)該控制模塊就會開始工作了(同時間bps_module.v 也會開始計數)。每當bps_module.v 產生一個定時的高脈衝 BPS_CLKen, tx_control_module.v 都會發送一位數據。
第 0 位數據是起始位,所以 rTX 寄存器被賦值與邏輯 0(40 行)。接下來的八位都是數據位, tx_control_module.v 按 TX_Data 的值從最低位到最高位,將數據賦值給 rTX 寄存器(43 行)。最後兩位數據則是校驗位和停止位,如果沒有什麼特別需求,就隨便填上邏輯 1 就行了(41~45 行)。最後產生一個 TX_Done_Sig 的高脈衝(51~55 行)。在 62 行 TX_Done_Sig 的輸出是被 isDone 這個標誌寄存器驅動着,然而 TXD的輸出是由 rTX 這個寄存器驅動。
接下來是將上兩個模塊進行例化,完成整個發送模塊的設計。電路的整體結構如圖2 49所示。頂層模塊只需要按照整體結構圖進行“連線”就可以了。代碼如下:
代碼2 24 發送頂層模塊(tx_module.v)完整代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-03 02:18:37
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-05-06 22:13:07
7. //# Description:
8. //# @Modification History: 2019-05-06 22:13:07
9. //# Date By Version Change Description:
10. //# ========================================================================= #
11. //# 2019-05-06 22:13:07
12. //# ========================================================================= #
13. //# | | #
14. //# | OpenFPGA | #
15. //****************************************************************************//
16. module tx_module
17. (
18. CLOCK, RST_n,
19. TX_Data,
20. TX_En_Sig,
21. TX_Done_Sig,
22. TXD
23. );
24.
25. input CLOCK;
26. input RST_n;
27. input [7:0]TX_Data;
28. input TX_En_Sig;
29. output TX_Done_Sig;
30. output TXD;
31.
32. /********************************/
33.
34. wire BPS_CLOCK;
35. wire BPS_CLOCKen;
36.
37. bps_module //BPS
38. #(
39. //BPS_CNT = 85.89934592 * fo for 50Mhz
40. // .BPS_CNT(32'd21990233) //256000bps
41. // .BPS_CNT(32'd10995116) //128000bps
42. // .BPS_CNT(32'd9895605) //115200bps
43. .BPS_CNT(32'd824634) //9600bps * 16 824634
44. //BPS_CNT = 42.949 67296 * fo for 100Mhz
45. // .BPS_CNT(32'd10995116) //256000bps 10995116
46. // .BPS_CNT(32'd5497558) //128000bps 5497558
47. // .BPS_CNT(32'd4947802) //115200bps 4947802
48. // .BPS_CNT(32'd412317) //9600bps 412317
49. )
50. U1_bps_module
51. (
52. .CLOCK( CLOCK ),
53. .RST_n( RST_n ),
54. .En_Sig( TX_En_Sig ), // input - from U2
55. .BPS_CLK( BPS_CLOCK ), // output - to X
56. .BPS_CLKen( BPS_CLOCKen ) // output - to U2
57. );
58. /*********************************/
59.
60. tx_control_module U2_tx_control_module
61. (
62. .CLOCK( CLOCK ),
63. .RST_n( RST_n ),
64. .TX_En_Sig( TX_En_Sig ), // input - from top
65. .TX_Data( TX_Data ), // input - from top
66. .BPS_CLK( BPS_CLOCKen ), // input - from U2
67. .TX_Done_Sig( TX_Done_Sig ), // output - to top
68. .TXD( TXD ) // output - to top
69. );
70. /***********************************/
71.
72. endmodule
完成後的RTL如下:
圖2 50 UART發送模塊RTL
和圖2 49整體設計一樣。
上述代碼的ModelSim仿真和2.3.3節仿真代碼一樣,結果如下:
圖2 51 ModelSim仿真
爲了測試代碼的實用性及使用方法,編寫代碼2 25。
代碼2 25 UART發送實例代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-03 02:18:37
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-05-06 21:38:04
7. //# Description:
8. //# @Modification History: 2019-05-06 21:38:04
9. //# Date By Version Change Description:
10. //# ========================================================================= #
11. //# 2019-05-06 21:38:04
12. //# ========================================================================= #
13. //# | | #
14. //# | OpenFPGA | #
15. //****************************************************************************//
16. module tx_module_demo
17. (
18. CLOCK, RST_n,
19. TXD
20. );
21.
22. input CLOCK;
23. input RST_n;
24. output TXD;
25.
26. /***************************/
27. reg [7:0]rData;
28. reg isTX;
29. wire DoneU1;
30. /****************************/
31.
32.
33.
34. tx_module dut
35. (
36. .CLOCK( CLOCK),
37. .RST_n( RST_n ),
38. .TX_Data( rData ),
39. .TX_En_Sig( isTX ),
40. .TX_Done_Sig( DoneU1 ),
41. .TXD( TXD )
42. );
43.
44. /*******************************/
45.
46. reg [3:0]i;
47.
48.
49. /**************************/
50.
51. parameter T1S = 26'd49_999_999;
52.
53. /***************************
54.
55. reg [25:0]Count_Sec;
56.
57. always @ ( posedge CLOCK or negedge RST_n )
58. if( !RST_n )
59. Count_Sec <= 26'd0;
60. else if( Count_Sec == T1S )
61. Count_Sec <= 26'd0;
62. else
63. Count_Sec <= Count_Sec + 1'b1;
64.
65. /********************************
66.
67.
68. always @ ( posedge CLOCK or negedge RST_n )
69. if( !RST_n )
70. begin
71. isTX <= 1'b0;
72. rData <= 8'h00;
73. end
74. else if( DoneU1 )
75. begin
76. isTX <= 1'b0;
77. rData <= 8'hAB;
78. end
79. else if( Count_Sec == T1S )
80. isTX <= 1'b1;
81. /******************************/
82.
83.
84. /******************************/
85.
86. always @ ( posedge CLOCK or negedge RST_n )
87. if( !RST_n )
88. begin
89. i <= 4'd0;
90. rData <= 8'd0;
91. isTX <= 1'b0;
92. end
93. else
94. case( i )
95.
96. 0:
97. if( DoneU1 ) begin isTX <= 1'b0; i <= i + 1'b1; end
98. else begin isTX <= 1'b1; rData <= 8'hAB; end
99.
100. 1:
101. if( DoneU1 ) begin isTX <= 1'b0; i <= i + 1'b1; end
102. else begin isTX <= 1'b1; rData <= 8'hCD; end
103.
104. 2:
105. if( DoneU1 ) begin isTX <= 1'b0; i <= i + 1'b1; end
106. else begin isTX <= 1'b1; rData <= 8'hEF; end
107.
108. 3: // Stop
109. i <= i;
110.
111. endcase
112. /******************************/
113.
114. endmodule