跨时钟域信号传输问题之握手同步

举报
李锐博恩 发表于 2021/07/15 02:42:02 2021/07/15
【摘要】 所谓握手,即通信双方使用了专用控制信号进行状态指示,这个控制信号既有发送域给接受域的也有接收域给控制域的,有别于单向控制信号方式。        使用握手协议方式处理跨时钟域数据传输时,只需要对双方的握手信号(req 和 ack)分别使用脉冲检测方法进行同步,在具体实现中,假设req ,ack, data,...

所谓握手,即通信双方使用了专用控制信号进行状态指示,这个控制信号既有发送域给接受域的也有接收域给控制域的,有别于单向控制信号方式。
       使用握手协议方式处理跨时钟域数据传输时,只需要对双方的握手信号(req 和 ack)分别使用脉冲检测方法进行同步,在具体实现中,假设req ,ack, data,总线在初始化时都处于无效状态,发送域先把数据放入总线,随后发送有效的req信号给接收域;接收域在检测到有效的req信号后锁存数据总线,然后会送一个有效的ack信号表示读取完成应答;发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销ack信号,此时完成一次正常握手通信,此后,发送域可以继续开始下一次握手通信,如此循环,该方式能够使接收到的数据稳定可靠,有效的避免了亚稳态的出现,但是控制信号握手检测会消耗通信双方较多的时间。

上段内容参考:跨时钟域处理——专用握手信号

如下图为握手通信示意图:

//2019年8月17日更新

 

发送域的Verilog设计:


  
  1. module handshake_tclk(
  2. input tclk, //发送域的时钟和复位
  3. input resetb_tclk,
  4. input r_ack, //接收到的响应信号
  5. input data_avail, //从其他模块接收到的数据有效信号
  6. input [31:0] transmit_data, //需要发送出去的信号
  7. output t_rdy, //对于发送时钟域,需要输出数据准备好了的信号,以便在接收时钟域接收此信号,用来提示接收信号
  8. output [31:0] t_data //需要发送出去的信号
  9. );
  10. localparam IDLE = 3'b001; //空闲状态,判断数据是否有效,如果有效就输出t_rdy有效,表示数据准备好了
  11. localparam ASSERT_TRDY = 3'b010; //到了这个状态,表明t_rdy已经有效了,这时我们需要判断响应r_ack_tclk是否有效,如果有效则使t_rdy无效,否则保持不变
  12. localparam DEASSERT_TRDY = 3'b100; //到了这个t_rdy无效状态,需要考虑下一次的数据传输了,如果data_avail有效,则下一个状态进入ASSERT_TRDY,且t_rdy有效,否则进入空闲状态
  13. reg t_cur_state, t_nxt_state;
  14. reg t_rdy, t_rdy_nxt;
  15. reg [31:0] t_data, t_data_nxt;
  16. reg r_ack_d1, r_ack_tclk; //对于接收到的响应信号,需要进行时钟域同步,同步到发送时钟域
  17. always@(posedge tclk or negedge resetb_tclk) begin
  18. if(resetb_tclk) begin
  19. t_cur_state <= IDLE;
  20. t_rdy <= 0;
  21. //同步接收域的响应信号ack
  22. r_ack_d1 <= 0;
  23. r_ack_tclk <= 0;
  24. end
  25. else begin
  26. t_cur_state <= t_nxt_state;
  27. t_rdy <= t_rdy_nxt;
  28. r_ack_d1 <= r_ack;
  29. r_ack_tclk <= r_ack_d1;
  30. end
  31. end
  32. always@(*) begin
  33. t_nxt_state = t_cur_state;
  34. t_rdy_nxt = 0;
  35. t_data_nxt = t_data;
  36. case(t_cur_state)
  37. IDLE: begin
  38. if(data_avail) begin
  39. t_nxt_state = ASSERT_TRDY;
  40. t_rdy_nxt = 1'b1;
  41. t_data_nxt = transmit_data;
  42. end
  43. else ;
  44. end
  45. ASSERT_TRDY: begin
  46. if(r_ack_tclk) begin
  47. t_rdy_nxt = 1'b0;
  48. t_nxt_state = DEASSERT_TRDY;
  49. t_data_nxt = 'd0;
  50. end
  51. else begin
  52. t_rdy_nxt = 1'b1;
  53. t_data_nxt = t_data;
  54. end
  55. end
  56. DEASSERT_TRDY: begin
  57. if(!r_ack_tclk) begin
  58. if(data_avail) begin
  59. t_nxt_state = ASSERT_TRDY;
  60. t_rdy_nxt = 1'b1;
  61. t_data_nxt = transmit_data;
  62. end
  63. else t_nxt_state = IDLE;
  64. end
  65. else;
  66. end
  67. default: ;
  68. endcase
  69. end
  70. endmodule

更新于2019年8月18日:

接收域设计代码:


  
  1. module handshake_rclk(
  2. input rclk,
  3. input resetb_rclk,
  4. input t_rdy,
  5. input [31:0] t_data,
  6. output r_ack
  7. );
  8. localparam IDLE = 2'b01;
  9. localparam ASSERT_ACK = 2'b10;
  10. reg r_cur_state, r_nxt_state;
  11. reg r_ack, r_ack_nxt;
  12. reg [31:0] t_data_rclk, t_data_rclk_nxt;
  13. reg t_rdy_d1, t_rdy_rclk;
  14. always@(posedge clk or negedge resetb_rclk) begin
  15. if(~resetb_rclk) begin
  16. r_cur_state <= IDLE;
  17. r_ack <= 0;
  18. t_data_rclk <= 0;
  19. t_rdy_d1 <= 0;
  20. t_rdy_rclk <= 0;
  21. end
  22. else begin
  23. r_cur_state <= r_nxt_state;
  24. r_ack <= r_ack_nxt;
  25. t_data_rclk <= t_data_rclk_nxt;
  26. t_rdy_d1 <= t_rdy;
  27. t_rdy_rclk <= t_rdy_d1;
  28. end
  29. end
  30. always@(*) begin
  31. r_nxt_state = r_cur_state;
  32. r_ack_nxt = 1'b0;
  33. t_data_rclk_nxt = t_data_rclk;
  34. case(r_nxt_state)
  35. IDLE: begin
  36. if(t_rdy_rclk) begin
  37. r_nxt_state = ASSERT_ACK;
  38. r_ack_nxt = 1'b1;
  39. t_data_rclk_nxt = t_data;
  40. end
  41. else;
  42. end
  43. ASSERT_ACK: begin
  44. if(~t_rdy_rclk) begin
  45. r_ack_nxt = 1'b0;
  46. r_nxt_state = IDLE;
  47. end
  48. else r_ack_nxt = 1'b1;
  49. end
  50. endcase
  51. end

 

综合上述两段设计程序,来大致总结下发送域的控制信号t_rdy和接收域的响应信号r_ack之间的关系;

最先是t_rdy有效,当接收域检测到t_rdy信号后,r_ack有效;

让发送域检测到r_ack后,t_rdy无效;

让接收域检测到t_rdy无效后,使r_ack无效,此时,一次完整的握手成功完成。

 

无效素材:


下面的设计是参考互联网上的,不得不说真是良莠不齐,这个设计既然是跨时钟域设计的握手信号,你都没有两套时钟,怎么能叫跨时钟域呢?

下面给出Verilog代码及测试文件:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2019/04/28 21:00:41
  7. // Design Name:
  8. // Module Name: handshack
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module handshack(
  22. input clk, //系统时钟
  23. input rst_n, //低电平复位信号
  24. input req, //请求信号,高电平有效
  25. input [7:0] datain, //输入数据
  26. output ack, //应答信号,高电平有效
  27. output [7:0] dataout //输出数据,主要用于观察是否和输入一致
  28. );
  29. //req上升沿检测
  30. reg reqr1, reqr2, reqr3; //定义reqrx代表延迟x拍
  31. //--------------------------------------------------------
  32. //第一种方法
  33. always @ (posedge clk or negedge rst_n) begin
  34. if(!rst_n) begin
  35. reqr1 <= 1'b0;
  36. reqr2 <= 1'b0;
  37. reqr3 <= 1'b0;
  38. end
  39. else begin
  40. reqr1 <= req;
  41. reqr2 <= reqr1;
  42. reqr3 <= reqr2;
  43. end
  44. end
  45. //pos_req3比pos_req2延后一拍,确保数据被稳定锁存
  46. wire pos_req1, pos_req2, pos_req3;
  47. assign pos_req1 = ( { req, reqr1 } == 2'b10 ) ? 1'b1 : 1'b0;
  48. assign pos_req2 = ( { reqr1, reqr2 } == 2'b10 ) ? 1'b1 : 1'b0;
  49. assign pos_req3 = ( { reqr2, reqr3 } == 2'b10 ) ? 1'b1 : 1'b0;
  50. //------------------------------------------------------------------
  51. // 检测上升沿的第二种方法
  52. // assign pos_req1 = ~reqr1 & req;
  53. // assign pos_req2 = ~reqr2 & reqr1;
  54. // assign pos_req3 = ~reqr3 & reqr2;
  55. //----------------------------------------------------------------
  56. //数据锁存
  57. reg [7:0] dataoutr;
  58. always @ (posedge clk or negedge rst_n) begin
  59. if(!rst_n) begin
  60. dataoutr <= 8'h00;
  61. end
  62. else if(pos_req2) begin
  63. dataoutr <= datain; //检测到req有效后的第二拍,锁存输入数据;
  64. end
  65. end
  66. assign dataout = dataoutr;
  67. //-------------------------------------------------------------------
  68. //产生应答信号
  69. reg ackr;
  70. always @ (posedge clk or negedge rst_n) begin
  71. if(!rst_n) begin
  72. ackr <= 1'b0;
  73. end
  74. else if(pos_req3) begin //锁存输入数据后的一拍,产生应答信号
  75. ackr <= 1'b1;
  76. end
  77. else if(!req) begin
  78. ackr <= 1'b0;
  79. end
  80. end
  81. assign ack = ackr;
  82. endmodule

  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2019/04/28 22:12:00
  7. // Design Name:
  8. // Module Name: handshack_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module handshack_tb(
  22. );
  23. reg clk, rst_n;
  24. reg req;
  25. reg [7:0] datain;
  26. wire ack;
  27. wire [7:0] dataout;
  28. //--------------------------------------------------
  29. //产生时钟
  30. initial clk = 0;
  31. always begin
  32. #1 clk = ~clk;
  33. end
  34. //------------------------------------------------------
  35. //初始化
  36. initial begin
  37. rst_n = 0;
  38. req = 0;
  39. #4
  40. rst_n = 1;
  41. #4
  42. datain = 8'h55;
  43. #2
  44. req = 1;
  45. #10
  46. req = 0;
  47. datain = 8'haa;
  48. #4
  49. req = 1;
  50. #10
  51. req = 0;
  52. end
  53. //------------------------------------------------------
  54. //调用模块
  55. handshack u_handshack(
  56. .clk(clk),
  57. .rst_n(rst_n),
  58. .req(req),
  59. .datain(datain),
  60. .ack(ack),
  61. .dataout(dataout)
  62. );
  63. endmodule

仿真波形:

RTL原理图:

主要观察pos_reqx是由多路选择器构成,但如果将上升沿检测部分用与门方式实现,我们看看RTL图是什么样的:

 

 

 

 

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/89647526

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。