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