当前位置:   article > 正文

基于FPGA的UART实现(学习笔记)_fpga uart代码

fpga uart代码

异步串口通信(UART):通信中发送端与接收端的时钟不一致。

同步串口通信(USART):通信中发送端与接收端的时钟一致。

UART:速率最高2Mbps(基本出现到达极限),2Mbps已经不稳定,1Mbps接收1w个byte误码率为0。

从板上出来的时钟不建议直接使用,建议使用PLL/MMCM来输出稳定时钟。

首先,UART的目标是实现一个稳定的波特率,同时尽可能的降低误码率。uart时序网上很多,这边不进行描述。其中分频,复位等时序就不占用空间了。

tx部分:

tx发送时采用AXI4协议中握手信号,以自身输出的ready信号与上级输入的vaild信号进行握手进行数据传输。此时会产生一个tx时钟的握手信号,进行开始握手。那么接下来可以考虑ready的输出。在握手后,立刻拉低ready,进行数据输出。数据输出完后,拉高ready,ready分有校验位拉高和无校验位拉高。

对于发送数据,采用计数器开始计数。一般情况下以ready拉低后进行计数,结束条件与上述ready类似。

在ready拉低期间,不断右移数据,将最低位数据不断通过tx发送出去。

  1. module uart_tx#(
  2. parameter P_SYSTEM_CLK = 50_000_000 , //输入时钟
  3. parameter P_UART_BUADRATE = 9600 , //波特率
  4. parameter P_UART_DATA_WIDTH = 8 , //数据宽度
  5. parameter P_UART_STOP_WIDTH = 1 , //1 or 2
  6. parameter P_UART_CHECK = 0 //None = 0 ,Event = 1, Odd = 2
  7. )
  8. (
  9. input wire i_clk ,
  10. input wire i_rst ,
  11. output wire o_uart_tx ,
  12. input wire [P_UART_DATA_WIDTH - 1 : 0] i_user_tx_data , //用户发送的数据
  13. input wire i_user_tx_data_vaild,
  14. output wire o_user_tx_data_ready
  15. );
  16. /***************function**************/
  17. /***************parameter*************/
  18. /***************port******************/
  19. /***************mechine***************/
  20. /***************reg*******************/
  21. reg ro_uart_tx ;
  22. reg ro_user_tx_data_ready ;
  23. reg [15:0] r_cnt ; //计数器位宽高于16 bits时,组合逻辑过高,谨慎使用
  24. reg [P_UART_DATA_WIDTH - 1 : 0] r_tx_data ;
  25. reg r_tx_check ;
  26. /***************wire******************/
  27. wire w_tx_active ;
  28. /***************component*************/
  29. /***************assign****************/
  30. assign o_uart_tx = ro_uart_tx ;
  31. assign o_user_tx_data_ready = ro_user_tx_data_ready ;
  32. assign w_tx_active = i_user_tx_data_vaild & o_user_tx_data_ready ;
  33. /***************always****************/
  34. always @(posedge i_clk or negedge i_rst) begin
  35. if (i_rst) begin
  36. ro_user_tx_data_ready <= 'd1;
  37. end else if (w_tx_active) begin
  38. ro_user_tx_data_ready <= 'd0;
  39. end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 3 && P_UART_CHECK == 0 ) begin
  40. ro_user_tx_data_ready <= 'd1;
  41. end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK > 0 ) begin
  42. ro_user_tx_data_ready <= 'd1;
  43. end else begin
  44. ro_user_tx_data_ready <= ro_user_tx_data_ready;
  45. end
  46. end
  47. always @(posedge i_clk or negedge i_rst) begin
  48. if (i_rst) begin
  49. r_cnt <= 'd0;
  50. end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 3 && P_UART_CHECK == 0) begin //2 表示 开始位 校验位
  51. r_cnt <= 'd0;
  52. end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK > 0) begin
  53. r_cnt <= 'd0;
  54. end else if (!ro_user_tx_data_ready) begin
  55. r_cnt <= r_cnt + 1'd1;
  56. end else begin
  57. r_cnt <= r_cnt;
  58. end
  59. end
  60. always @(posedge i_clk or negedge i_rst) begin
  61. if (i_rst) begin
  62. r_tx_data <= 'd0;
  63. end else if (w_tx_active) begin
  64. r_tx_data <= i_user_tx_data;
  65. end else if (!ro_user_tx_data_ready) begin
  66. r_tx_data <= r_tx_data >> 1;
  67. end else begin
  68. r_tx_data <= r_tx_data;
  69. end
  70. end
  71. always @(posedge i_clk or negedge i_rst) begin
  72. if (i_rst) begin
  73. ro_uart_tx <= 'd1;
  74. end else if (w_tx_active) begin
  75. ro_uart_tx <= 'd0;
  76. end else if ((r_cnt == 3 + P_UART_DATA_WIDTH - 3) && P_UART_CHECK > 0) begin //开启校验位
  77. ro_uart_tx <= P_UART_CHECK == 1 ? ~r_tx_check : r_tx_check; //判断是奇还是偶
  78. end else if ((r_cnt == 3 + P_UART_DATA_WIDTH - 3) && P_UART_CHECK == 0) begin //未开启校验,直接发送停止
  79. ro_uart_tx <= 'd1;
  80. end else if ((r_cnt >= 3 + P_UART_DATA_WIDTH - 2) && P_UART_CHECK > 0) begin //开启了校验,发送完校验,直接发送停止
  81. ro_uart_tx <= 'd1;
  82. end else if (!ro_user_tx_data_ready) begin
  83. ro_uart_tx <= r_tx_data[0];
  84. end else begin
  85. ro_uart_tx <= 'd1;
  86. end
  87. end
  88. always @(posedge i_clk or negedge i_rst) begin
  89. if (i_rst)begin
  90. r_tx_check <= 'd0;
  91. end else if (r_cnt == 3 + P_UART_DATA_WIDTH - 3) begin
  92. r_tx_check <= 'd0;
  93. end else begin
  94. r_tx_check <= r_tx_check ^ r_tx_data[0];
  95. end
  96. end
  97. endmodule

rx模块:

 输入数据没有采用打两拍,是在顶层以50M时钟进行超采样。降低了亚稳态

  1. module uart_rx#(
  2. parameter P_SYSTEM_CLK = 50_000_000 , //输入时钟
  3. parameter P_UART_BUADRATE = 9600 , //波特率
  4. parameter P_UART_DATA_WIDTH = 8 , //数据宽度
  5. parameter P_UART_STOP_WIDTH = 1 , //1 or 2
  6. parameter P_UART_CHECK = 0 //None = 0 ,Odd = 1, Event = 2
  7. )
  8. (
  9. input wire i_clk ,
  10. input wire i_rst ,
  11. input wire i_uart_rx ,
  12. output wire [P_UART_DATA_WIDTH - 1 : 0] o_user_rx_data ,
  13. output wire o_user_rx_data_vaild
  14. );
  15. /***************function**************/
  16. /***************parameter*************/
  17. /***************port******************/
  18. /***************mechine***************/
  19. /***************reg*******************/
  20. reg [P_UART_DATA_WIDTH - 1 : 0] ro_user_rx_data ;
  21. reg ro_user_rx_data_vaild ;
  22. reg [ 1:0] ri_uart_rx ;
  23. reg [15:0] r_cnt ;
  24. reg r_rx_check ;
  25. /***************wire******************/
  26. /***************component*************/
  27. /***************assign****************/
  28. assign o_user_rx_data = ro_user_rx_data ;
  29. assign o_user_rx_data_vaild = ro_user_rx_data_vaild ;
  30. /***************always****************/
  31. always @(posedge i_clk or negedge i_rst) begin
  32. if (i_rst)begin
  33. ri_uart_rx <= 2'b11;
  34. end else begin
  35. ri_uart_rx <= {ri_uart_rx[0],i_uart_rx};
  36. end
  37. end
  38. always @(posedge i_clk or negedge i_rst) begin
  39. if (i_rst)begin
  40. r_cnt <= 'd0;
  41. end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK == 0) begin
  42. r_cnt <= 'd0;
  43. end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 1 && P_UART_CHECK > 0) begin
  44. r_cnt <= 'd0;
  45. end else if (i_uart_rx == 1'b0 || r_cnt > 0) begin
  46. r_cnt <= r_cnt + 'd1;
  47. end else begin
  48. r_cnt <= r_cnt;
  49. end
  50. end
  51. always @(posedge i_clk or negedge i_rst) begin
  52. if (i_rst)begin
  53. ro_user_rx_data <= 'd0;
  54. end else if (r_cnt >= 1 && r_cnt <= P_UART_DATA_WIDTH) begin
  55. ro_user_rx_data <= {i_uart_rx,ro_user_rx_data[P_UART_DATA_WIDTH - 1 :1]};
  56. end else begin
  57. ro_user_rx_data <= ro_user_rx_data;
  58. end
  59. end
  60. always @(posedge i_clk or negedge i_rst) begin
  61. if (i_rst)begin
  62. ro_user_rx_data_vaild <= 'd0;
  63. end else if (r_cnt == P_UART_DATA_WIDTH + 0 && P_UART_CHECK == 0) begin
  64. ro_user_rx_data_vaild <= 'd1;
  65. end else if (r_cnt == P_UART_DATA_WIDTH + 1 && P_UART_CHECK == 1 && i_uart_rx == !r_rx_check) begin
  66. ro_user_rx_data_vaild <= 'd1;
  67. end else if (r_cnt == P_UART_DATA_WIDTH + 1 && P_UART_CHECK == 2 && i_uart_rx == r_rx_check) begin
  68. ro_user_rx_data_vaild <= 'd1;
  69. end else begin
  70. ro_user_rx_data_vaild <= 'd0;
  71. end
  72. end
  73. always @(posedge i_clk or negedge i_rst) begin
  74. if (i_rst)begin
  75. r_rx_check <= 'd0;
  76. end else if (r_cnt >= 1 && r_cnt <= P_UART_DATA_WIDTH) begin
  77. r_rx_check <= r_rx_check ^ i_uart_rx;
  78. end else begin
  79. r_rx_check <= 'd0;
  80. end
  81. end

最终采用回环输出。采用fifo进行在中间缓存,以115200波特率发送10000bytes误码率为0.以1Mbps发送误码率为0。黑金Spartan 7 开发板跑的结果如下:

 

 

 

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

闽ICP备14008679号