当前位置:   article > 正文

FPGA实现UART通讯(FSM+移位寄存器实现 含校验位)_fgpa uart 实现

fgpa uart 实现

 一、UART通讯协议原理与时序

        串口(UART)协议的发送和接收时序、物理层接口下面两篇博客讲的很详细。

y​​​​​​​​​​​​​​串口(UART)的FPGA实现(含源码工程)_孤独的单刀的博客-CSDN博客_fpga uarthttps://blog.csdn.net/wuzhikaidetb/article/details/114596930UART的fpga实现_alone_l的博客-CSDN博客_uart fpgahttps://blog.csdn.net/alone_l/article/details/124946773        这里我主要参考的是博主孤独的单刀的代码,虽然博主写的系列文章都很不错而且基础理论的讲解也很详细到位,但是博主并没有使用状态机的方式实现,并且使用case来产生uart_txd输出和串转并得到输入的语句会综合出一个很大的多路选择器,而同样的功能可是使用移位寄存器来实现,这就会造成资源不必要的浪费。RTL代码与其综合出的电路的对应如下所示:

1、UART发送模块中


2、UART接收模块中

 

 在本篇博客中笔者使用了状态机+移位寄存器的方式来实现,很大程度上减少了资源的损耗,移位寄存器部分代码和综合结果如下。

1、UART发送模块

 2、UART接收模块

         可以看到,使用移位寄存器的方式来实现UART驱动更加节省资源。下面笔者将分别介绍UART接收端和发送端的RTL实现。

二、UART接口驱动的RTL实现与仿真结果

1、时钟分频产生波特率时钟

RTL代码:

        为了满足波特率的要求,需要将总线电平维持(时钟频率/波特率)个周期才能被接收端正确接收。为什么是(时钟频率/波特率)个系统时钟周期呢?得从波特率定义来看,波特率是指每秒钟发送/接收的bit数,单位为bit数/秒=bps;时钟频率是每秒的时钟周期数,单位为周期数/秒=Hz,因此(时钟频率/波特率)的单位为周期数/bit数也就是一个发送/接收1bit数据所需要的周期数!

        因此我们可以通过产生一个波特率时钟来控制状态机的状态转移,为了实现参数化的设计,使用parameter可以在例化的时候调整调整波特率和时钟频率:

        并定义传输一位所需要的周期数 

为了使占空比接近1/2,这里定义了计数分频跳变沿时计数器的计数数为 

         剩下的就是简单的计数分频逻辑了,CLK_div模块整体代码如下:

  1. module CLK_div #(
  2. parameter BAUD = 115200,
  3. parameter CLK_frq = 100000000
  4. )(
  5. input sys_clk,//系统时钟
  6. input rst_n,//系统复位
  7. output reg baud_clk//波特率时钟
  8. );
  9. parameter DIV_MAX = CLK_frq/BAUD;//传输一位所需周期数
  10. parameter EDGE_NUM =DIV_MAX/2;//跳变周期
  11. reg [8:0]clk_cnt;//分频计数器
  12. //计数器控制逻辑
  13. always @(posedge sys_clk or negedge rst_n) begin
  14. if(!rst_n)begin
  15. clk_cnt <= 10'd0;
  16. baud_clk <= 1'b0;
  17. end
  18. else if(clk_cnt == EDGE_NUM)begin
  19. clk_cnt <= 10'd0;
  20. baud_clk <= ~baud_clk;
  21. end
  22. else begin
  23. clk_cnt <= clk_cnt + 10'd1;
  24. baud_clk <= baud_clk;
  25. end
  26. end
  27. endmodule

2、UART发送端

(1)RTL代码

        为了实现可变参数设计,这里使用parameter可以在例化的时候调整波特率和时钟频率以及校验模式:

 其中校验模式参数定义为:

        输入输出端口如下所示:

         在UART_TX中例化上一节介绍过的时钟分频模块用于控制状态机的状态转移和输出等逻辑。

         由于输入UART发送端的使能信号uart_en为异步输入信号,因此需要两级寄存将异步信号同步化,降低亚稳态的影响:

         本设计使用的校验为奇偶校验,将奇偶校验位产生逻辑分离出来使用寄存器寄存,这样就不用在校验周期的时候等待校验位计算完成再传输而是直接使用:

        本设计采用三段式状态机,其状态编码如下所示,由于状态少,使用独热码编码节省组合逻辑资源:

        状态机的第一段为状态转移同步逻辑:

        此外需要一个发送bit数计数器来计数已经发送了多少个bit用来控制状态机在数据发送完成之后的转移。 当状态为发送数据状态时每发送一个bie,发送bit数计数器将进行递增直到位满溢出。

        状态机的第二段为产生下一个状态组合逻辑,最开始状态机上电复位到IDLE空闲状态,被两级寄存后的使能信号uart_en_d1触发之后进入起始位发送状态START,发送完毕起始位之后进入数据发送状态DATA,当发送bit数计数器为7即3'b111的时候判断校验模式VERIFY_MODE是否为无校验,是则进入终止位发送STOP,否则进入校验位发送状态VERI,若未发送完数据则停留在DATA继续发送数据。

        这里使用按位与运算&data_cnt来替代data_cnt==3'd111是因为相同逻辑功能的情况下,前者综合出三输入与门,后者综合出减法器加一个组合逻辑。相比之下前者更节省逻辑资源。

        状态机处于校验状态VERI后进入终止位发送状态STOP。处于终止位发送状态时判断寄存后的UART发送使能信号uart_en_d1,如果使能则进入START继续发送,如果不使能则回到空闲IDLE。

         状态机的第三段为产生输出的同步逻辑,先使用数据寄存器data_reg在START状态寄存输入的并行数据data,在DATA数据发送状态每发送一位data_reg右移一位(UART先发送低位后发送高位,因此是右移;如果是IIC或SPI则应该左移,因为先发送高位后发送低位)。在校验周期VERI的时候uart_txd发送的是已经运算好的校验位uart_verify_bit。

         完整代码如下:

  1. module UART_TX_VERIFY #(
  2. parameter BAUD = 115200,
  3. parameter CLK_frq = 100000000,
  4. parameter VERIFY_MODE = 0
  5. )(
  6. input sys_clk,//系统时钟
  7. input rst_n,//系统复位
  8. input uart_en,//UART发送使能端 异步信号
  9. input [7:0]data,//即将输出的BYTE
  10. output reg uart_txd//UART发送端
  11. );
  12. //校验参数定义
  13. parameter VERIFY_NONE = 2'b00;//无校验
  14. parameter VERIFY_ODD = 2'b01;//奇校验
  15. parameter VERIFY_EVEN = 2'b11;//偶校验
  16. wire baud_clk;
  17. //产生波特率时钟
  18. CLK_div #(
  19. .BAUD(BAUD),
  20. .CLK_frq(CLK_frq)
  21. )CLK_div_dut(
  22. .sys_clk(sys_clk),
  23. .rst_n(rst_n),
  24. .baud_clk (baud_clk)
  25. );
  26. //uart_en异步信号同步化
  27. reg uart_en_d0,uart_en_d1;
  28. always @(posedge baud_clk or negedge rst_n) begin
  29. if(!rst_n)begin
  30. uart_en_d0 <= 1'b0;
  31. uart_en_d1 <= 1'b0;
  32. end
  33. else begin
  34. uart_en_d0 <= uart_en;
  35. uart_en_d1 <= uart_en_d0;
  36. end
  37. end
  38. //产生校验位
  39. reg uart_verify_bit;
  40. always @(*) begin
  41. case(VERIFY_MODE)//0表示传输成功
  42. VERIFY_ODD: uart_verify_bit = ~(^data);
  43. VERIFY_EVEN:uart_verify_bit = ^data;
  44. default:uart_verify_bit = 1'b1;
  45. endcase
  46. end
  47. //FSM状态编码
  48. localparam IDLE = 5'b00001;
  49. localparam START= 5'b00010;
  50. localparam DATA = 5'b00100;
  51. localparam VERI = 5'b01000;
  52. localparam STOP = 5'b10000;
  53. //FSM状态寄存器
  54. reg [4:0] cstate,nstate;
  55. //发送数据寄存器
  56. reg [7:0] data_reg;
  57. //发送bit计数器
  58. reg [2:0] data_cnt;
  59. always @(posedge baud_clk or negedge rst_n) begin
  60. if(!rst_n)begin
  61. data_cnt <= 3'd0;
  62. end
  63. else if(cstate == DATA)begin
  64. data_cnt <= data_cnt + 3'd1;
  65. end
  66. else begin
  67. data_cnt <= 3'd0;
  68. end
  69. end
  70. //FSM状态转移同步逻辑
  71. always @(posedge baud_clk or negedge rst_n) begin
  72. if(!rst_n)begin
  73. cstate <= IDLE;
  74. end
  75. else begin
  76. cstate <= nstate;
  77. end
  78. end
  79. //FSM产生下一个状态组合逻辑
  80. always @(*) begin
  81. case (cstate)
  82. IDLE,STOP:begin
  83. nstate = uart_en_d1?START:IDLE;
  84. end
  85. START:begin
  86. nstate = DATA;
  87. end
  88. DATA:begin//记满0~7则发送BYTE完毕
  89. nstate = (&data_cnt)?((VERIFY_MODE==VERIFY_NONE)?STOP:VERI):DATA;
  90. end
  91. VERI:begin
  92. nstate = STOP;
  93. end
  94. STOP:begin
  95. nstate = uart_en_d1?START:IDLE;
  96. end
  97. default:nstate = IDLE;
  98. endcase
  99. end
  100. //FSM产生输出逻辑
  101. always @(posedge baud_clk or negedge rst_n) begin
  102. if(!rst_n)begin
  103. data_reg <= 8'h00;
  104. uart_txd <= 1'b1;
  105. end
  106. else begin
  107. case (nstate)
  108. START:begin
  109. data_reg <= data;
  110. uart_txd <= 1'b0;
  111. end
  112. DATA:begin
  113. data_reg <= data_reg>>1;
  114. uart_txd <= data_reg[0];
  115. end
  116. VERI:begin
  117. data_reg <= 8'h00;
  118. uart_txd <= uart_verify_bit;
  119. end
  120. STOP:begin
  121. data_reg <= 8'h00;
  122. uart_txd <= 1'b1;
  123. end
  124. default:begin
  125. data_reg <= 8'h00;
  126. uart_txd <= 1'b1;
  127. end
  128. endcase
  129. end
  130. end
  131. endmodule

(2)仿真结果

testbench代码如下:

  1. module UART_TX_tb;
  2. // Parameters
  3. localparam BAUD = 115200;
  4. localparam CLK_frq = 100000000;
  5. //校验参数定义
  6. parameter VERIFY_NONE = 2'b00;//无校验
  7. parameter VERIFY_ODD = 2'b01;//奇校验
  8. parameter VERIFY_EVEN = 2'b11;//偶校验
  9. // Ports
  10. reg sys_clk;
  11. reg uart_en;
  12. reg rst_n;
  13. reg [7:0] data;
  14. wire uart_txd;
  15. UART_TX_VERIFY#(
  16. .BAUD(BAUD),
  17. .CLK_frq(CLK_frq),
  18. .VERIFY_MODE(VERIFY_EVEN)
  19. )UART_TX_dut(
  20. .sys_clk(sys_clk),
  21. .uart_en(uart_en),
  22. .rst_n(rst_n),
  23. .data(data),
  24. .uart_txd(uart_txd)
  25. );
  26. initial begin
  27. begin
  28. sys_clk = 0;
  29. uart_en = 0;
  30. rst_n = 0;
  31. data = 8'b01011010;
  32. #1300;
  33. uart_en = 1;
  34. rst_n = 1;
  35. // #100;
  36. // uart_en = 0;
  37. // #100000 ;
  38. // uart_en = 1;
  39. // data = 8'b11011101;
  40. #500000 $finish;
  41. end
  42. end
  43. always
  44. #5 sys_clk = ! sys_clk ;
  45. endmodule

        测试在偶校验情况下的UART传送情况,仿真波形图如下,功能正确:

3、UART接收端

(1)RTL代码

        与UART_TX同理,使用参数化设计:

         输入输出端口如下所示:

         例化分频器产生波特率时钟驱动状态机:

         由于UART是串行全双工异步通讯协议,因此发送端UART_TX和接收端UART_RX所使用的系统时钟可以不同(笔者为了方便还是使用了相同频率的时钟,但是实际上是可以不同的),因此接收端输入串行信号是异步信号,需要进行两级寄存同步化,减少亚稳态的影响(注意是减少而不是消除)。

        校验模式和状态机参数编码如下:

         同样需要一个接收bit数计数器来判断接收的bit数来控制状态转移:

         状态机第一段为状态转移同步逻辑:

        状态机第二段为产生下一个状态的组合逻辑,因为数据稳定性的要求,uart_rxd_d1需要在发送周期的中间时刻被采样,处于IDLE的状态机在采样到低电平起始位的时候就进入数据接收状态DATA。同理,在数据接收状态时判断接收bit数计数器是否为3‘b111,如果接收完毕判断校验模式选择进入VERI或者STOP。

         

         状态机第三段是产生输出的逻辑,输出有两个,分别是接收的并行数据uart_rx_data和接收完成标志位。

        串转并逻辑如下,注意是在baud_clk下降沿进行采样,此时数据早已经建立好所以稳定。在DATA数据接收状态的时候通过右移运算来接收数据。

         产生接收到的数据产生校验位逻辑分离出来,降低时延:

        注意这里是接收端接收的数据uart_rx_data进行逻辑运算与两级寄存后的uart_rxd_d1进行逻辑运算来判断是由传输错误。如果传输成功,则uart_rx_done在VERI状态就拉高,反之只有在STOP状态才拉高。

        完整代码如下所示:

  1. module UART_RX #(
  2. parameter BAUD = 115200,//bps
  3. parameter CLK_frq = 100000000,//Hz
  4. parameter VERIFY_MODE = 0//默认不校验
  5. )(
  6. input sys_clk,//系统时钟
  7. input rst_n,//系统复位
  8. input uart_rxd,//UART传输端
  9. output reg uart_rx_done,//接收完成标志
  10. output reg [7:0]uart_rx_data//接收到的数据
  11. );
  12. //校验参数定义
  13. parameter VERIFY_NONE = 2'b00;//无校验
  14. parameter VERIFY_ODD = 2'b01;//奇校验
  15. parameter VERIFY_EVEN = 2'b11;//偶校验
  16. //FSM状态编码
  17. localparam IDLE = 4'b0001;
  18. localparam DATA = 4'b0010;
  19. localparam VERI = 4'b0100;
  20. localparam STOP = 4'b1000;
  21. //FSM状态寄存器
  22. reg [3:0] cstate,nstate;//状态寄存器
  23. reg uart_rxd_d0,uart_rxd_d1;//打两拍同步化
  24. reg [2:0] data_cnt;//接收数据位数寄存器
  25. wire baud_clk;//波特率时钟
  26. wire odd_verify_flag;//奇校验成功标志
  27. wire even_verify_flag;//偶校验成功标志
  28. //产生波特率时钟
  29. CLK_div #(
  30. .BAUD(BAUD),
  31. .CLK_frq(CLK_frq)
  32. )CLK_div(
  33. .sys_clk(sys_clk),
  34. .rst_n(rst_n),
  35. .baud_clk (baud_clk)
  36. );
  37. //uart_rxd异步信号同步化 减少亚稳态影响
  38. always @(posedge sys_clk or negedge rst_n) begin
  39. if(!rst_n)begin
  40. uart_rxd_d0 <= 1'b0;
  41. uart_rxd_d1 <= 1'b0;
  42. end
  43. else begin
  44. uart_rxd_d0 <= uart_rxd;
  45. uart_rxd_d1 <= uart_rxd_d0;
  46. end
  47. end
  48. //接收bit数计数器
  49. always @(posedge baud_clk or negedge rst_n) begin
  50. if(!rst_n)begin
  51. data_cnt <= 3'd0;
  52. end
  53. else if(cstate == DATA)begin
  54. data_cnt <= data_cnt + 3'd1;
  55. end
  56. else begin
  57. data_cnt <= 3'd0;
  58. end
  59. end
  60. //状态转移同步逻辑
  61. always @(posedge baud_clk or negedge rst_n) begin
  62. if(!rst_n)begin
  63. cstate <= IDLE;
  64. end
  65. else begin
  66. cstate <= nstate;
  67. end
  68. end
  69. //产生下一个状态组合逻辑
  70. always @(*) begin
  71. case (cstate)
  72. IDLE: nstate = uart_rxd_d1?IDLE:DATA;
  73. DATA: nstate = (&data_cnt)?((VERIFY_MODE==VERIFY_NONE)?STOP:VERI):DATA;
  74. VERI: nstate = STOP;
  75. STOP: nstate = IDLE;
  76. default:nstate = IDLE;
  77. endcase
  78. end
  79. //产生输出逻辑 下降沿采样数据稳定
  80. always @(negedge baud_clk or negedge rst_n) begin
  81. if(!rst_n)begin
  82. uart_rx_data <= 8'd0;
  83. end
  84. else begin
  85. case (cstate)
  86. DATA:uart_rx_data <= {uart_rxd_d1,uart_rx_data[7:1]};
  87. VERI,STOP:uart_rx_data <= uart_rx_data;
  88. default:uart_rx_data <= 8'd0;
  89. endcase
  90. end
  91. end
  92. //产生校验位
  93. assign odd_verify_flag = (VERIFY_MODE == VERIFY_ODD) && (uart_rxd_d1 == ~(^uart_rx_data))?1'b1:1'b0;
  94. assign even_verify_flag = (VERIFY_MODE == VERIFY_EVEN) && (uart_rxd_d1 == ^uart_rx_data)?1'b1:1'b0;
  95. always @(posedge baud_clk or negedge rst_n) begin
  96. if(!rst_n)begin
  97. uart_rx_done <= 1'b0;
  98. end
  99. else begin
  100. case (nstate)
  101. VERI:uart_rx_done <= (odd_verify_flag||even_verify_flag)?1'b1:1'b0;
  102. STOP:uart_rx_done <= 1'b1;
  103. default:uart_rx_done <= 1'b0;
  104. endcase
  105. end
  106. end
  107. endmodule

(2)仿真结果

 testbench代码如下,使用UART_TX发送数据,UART_RX接收数据:

  1. module testbench();
  2. // Parameters
  3. localparam BAUD = 115200;
  4. localparam CLK_frq = 100000000;
  5. //校验参数定义
  6. parameter VERIFY_NONE = 2'b00;//无校验
  7. parameter VERIFY_ODD = 2'b01;//奇校验
  8. parameter VERIFY_EVEN = 2'b11;//偶校验
  9. //声明信号线
  10. reg sys_clk;
  11. reg uart_en;
  12. reg rst_n;
  13. reg [7:0]data;
  14. wire uart_bus;
  15. wire uart_rx_done;
  16. wire [7:0] uart_rx_data;
  17. // wire tx_state;
  18. //产生激励
  19. initial begin
  20. sys_clk = 0;
  21. rst_n = 0;
  22. uart_en = 0;
  23. data = 8'h72;
  24. #100;
  25. rst_n = 1;
  26. uart_en = 1;
  27. @(posedge uart_rx_done);
  28. data = 8'h89;
  29. @(posedge uart_rx_done);
  30. data = 8'b10010011;
  31. #100000;
  32. $finish;
  33. end
  34. always #5 sys_clk = ~sys_clk;
  35. //模块实例化
  36. UART_TX_VERIFY#(
  37. .BAUD(BAUD),
  38. .CLK_frq(CLK_frq),
  39. .VERIFY_MODE(VERIFY_EVEN)
  40. )UART_TX_dut(
  41. .sys_clk(sys_clk),
  42. .uart_en(uart_en),
  43. .rst_n(rst_n),
  44. .data(data),
  45. .uart_txd(uart_bus)
  46. );
  47. UART_RX #(
  48. .BAUD(BAUD),
  49. .CLK_frq(CLK_frq),
  50. .VERIFY_MODE(VERIFY_EVEN)
  51. )
  52. UART_RX_dut (
  53. .sys_clk(sys_clk),
  54. .rst_n(rst_n),
  55. .uart_rxd(uart_bus),
  56. .uart_rx_data(uart_rx_data),
  57. .uart_rx_done(uart_rx_done)
  58. );
  59. endmodule

        仿真波形如下所示: 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/825943
推荐阅读
相关标签
  

闽ICP备14008679号