当前位置:   article > 正文

跨时钟域处理------握手协议verilog(究极保姆级教程)_握手实现verilog

握手实现verilog

1.绪论

         在芯片设计中经常需要跨时钟域传输处理一些数据,为了避免亚稳态带来的问题,所以需要对这些数据进行同步处理。目前常用的同步技术有以下几类,在不同的情况下会有不同的选择。

        1.单比特数据跨时钟域传输常常只用打两拍即可。

        2.当要传输的数据较多时,可以在不同的时钟域之间使用FIFO。

        3.少量数据传输时,在不同的时钟域之间可以使用握手协议。

        本文主要结合个人的理解介绍握手协议,有许多地方都不够严谨,有误之处还请指出。

PS:关于时钟域和亚稳态的知识可以去看下这篇文章:CDC:跨时钟域处理_cdc clock_杰之行的博客-CSDN博客

2.握手协议框图

        这次设计中有两个模块,其中tx模块负责发送数据rx,rx模块在接收tx模块给的值后会返回数据,如下图:

        

        上图中带t的都代表是发送模块中的输入输出,带r的代表为接收模块中的输入输出。下面来说明各个信号的意义(clk和reset信号就不介绍了):

data_in:输入的数据。

valid:数据有效信号,在高电平时代表输入数据有效。

data_out_t:valid信号上升沿到来时输出的data_in的数据(先存在写数据线上)。

data_in_r:rx从写数据线上接收的数据。

req_t:tx输出的请求信号。

req_r:rx接收到的请求信号(还需经过亚稳态处理才能使用)。

ack_r:rx在接收到打拍后的req请求信号后输出的应答信号。

ack_t:   tx接收到的应答信号(同样要处理后才能使用)。

data_out_r:rx返回给tx的数据(先存在读数据线上)。

data_in_t:tx从读数据线上接收到的数据。

3. 握手协议具体流程

具体流程如下:

1.tx模块接收外部输入信号data_in和valid,在valid上升沿到来时输出data_out_t到写数据线上,并且同时发出请求信号req_t告知rx模块我想要跟你传输数据(这个过程就像是tx给rx伸出象征友好的手)

ps:这里使用valid上升沿而不用valid本身为1来判断的原因是为了避免valid信号持续时间过长导致在一个valid期间多次传输数据。写数据线在顶层模块中用wire模拟了。

2.req_t在传递给rx后再经过打两拍处理得到req_dd,此时经过两拍之后数据已经稳定,rx认为tx的数据已经准备好了便接收写数据线上的数据,同时将要返回的数据data_out_r写到读数据线上,并且返回出一个应答信号ack_r给tx,以此告诉tx自己已经接收了数据并发送了返回值,至此已经完成了半握手(这个过程就像是rx伸出手和tx握手)

3.应答信号ack_r传给ack_t后再经过打两拍处理得到ack_dd,此时数据已经稳定,tx认为rx的数据已经准备好了便接收读数据线上的数据,同时拉低请求信号req_t,告知rx自己已经接收完数据。(tx伸出的手收回)

4.rx在检测到req_dd拉低后,知道了tx接收了返回数据,将应答信号ack_r也拉低(rx伸出的手收回)

5.tx检测到ack_dd拉低,本次传输过程结束。等下一个valid上升沿拉发起下一次数据交换。

4.代码编写与仿真验证

接下来是各模块代码和testbench的编写,有大量注释。

4.1 tx模块RTL编写

  1. module fullhs_tx(
  2. input tclk ,
  3. input treset ,
  4. input [31:0] data_in ,
  5. input [31:0] data_in_t ,
  6. input valid ,
  7. input ack_t ,
  8. output reg [31:0] data_out_t ,
  9. output reg req_t
  10. );
  11. reg [31:0] data_in_t_valid ; //从接收模块中返回的数据稳定后的值。
  12. reg ack_d ; //打1拍。
  13. reg ack_dd ; //打2拍。
  14. reg valid_d ; //valid 信号延时1拍。
  15. wire valid_posflag ; //valid上升沿flag信号。
  16. //为了解决当一个valid信号过长时导致在一个valid期间多次传输数据的问题,这里使用valid的上升沿作为判断条件。
  17. //上升沿检测。
  18. always@(posedge tclk or negedge treset) begin
  19. if(!treset) begin
  20. valid_d <= 'b0;
  21. end
  22. else begin
  23. valid_d <= valid;
  24. end
  25. end
  26. assign valid_posflag = valid & (~valid_d);
  27. //刚返还tx的数据并不稳定,在这里认为数据在应答信号ack经过tx的两个D触发器(打两拍)消除亚稳态之后才算稳定有效的值
  28. always@(posedge tclk or negedge treset) begin
  29. if(!treset) begin
  30. ack_d <= 'b0;
  31. ack_dd <= 'b0;
  32. end
  33. else begin
  34. ack_d <= ack_t;
  35. ack_dd <= ack_d;
  36. end
  37. end
  38. always@(posedge tclk or negedge treset) begin
  39. if(!treset) begin
  40. data_in_t_valid <= 'b0;
  41. req_t <= 1'b0;
  42. data_out_t <= 'b0;
  43. end
  44. else if(ack_dd == 1) begin //收到返回的akc信号(打两拍后的)后让req信号置零。
  45. data_in_t_valid <= data_in_t ;
  46. req_t <= 1'b0;
  47. end
  48. else if(valid_posflag == 1) begin //在数据有效信号为1时让data_out_t获取data_in的值,让req信号在valid来时拉起。
  49. req_t <= 1'b1 ;
  50. data_out_t <= data_in;
  51. end
  52. end
  53. endmodule

4.2 rx模块RTL编写

  1. module fullhs_rx(
  2. input rclk ,
  3. input rreset ,
  4. input [31:0] data_in_r ,
  5. input req_r ,
  6. output reg ack_r ,
  7. output reg [31:0] data_out_r //为了方便观察波形,让这里返还的数据与接受数据取反相同。
  8. );
  9. reg [31:0] data_in_r_valid ; //从发送模块中接受的稳定后的数据。
  10. reg req_d ; //req经过一个D触发器。
  11. reg req_dd ; //req经过两个D触发器。
  12. //刚进入rx的数据并不稳定,在这里认为数据在请求信号req经过rx的两个D触发器(打两拍)消除亚稳态之后才算稳定有效的值
  13. always@(posedge rclk or negedge rreset) begin
  14. if(!rreset) begin
  15. req_d <= 'b0;
  16. req_dd <= 'b0;
  17. end
  18. else begin
  19. req_d <= req_r;
  20. req_dd <= req_d;
  21. end
  22. end
  23. //在数据消除亚稳态之后,根据req信号做出相应操作。
  24. always@(posedge rclk or negedge rreset) begin
  25. if(!rreset) begin
  26. data_in_r_valid <= 'b0;
  27. ack_r <= 'b0;
  28. data_out_r <= 'b0;
  29. end
  30. else if(req_dd == 1) begin //在req信号为1时,拉高应答信号ack,接收tx发送信号,返回反还值。
  31. data_in_r_valid <= data_in_r ;
  32. ack_r <= 1'b1;
  33. data_out_r <= ~data_in_r_valid ;
  34. end
  35. else if(req_dd == 0) begin //req信号拉低后,拉低ack信号,结束一次通信。
  36. ack_r <= 1'b0 ;
  37. end
  38. end
  39. endmodule

4.3顶层模块编写

        这个模块的作用是将tx和rx的io相互绑定,定义了数据线,并且给后续testbench提供输入激励的端口。

  1. module fullhs_top(
  2. input tclk,
  3. input rclk,
  4. input [31:0] data_in,
  5. input valid ,
  6. input reset
  7. );
  8. wire [31:0] wrdata;
  9. wire [31:0] rddata;
  10. wire req;
  11. wire ack;
  12. fullhs_tx fullhs_tx_u(
  13. .tclk ( tclk ),
  14. .treset ( reset ),
  15. .data_in_t ( rddata ),
  16. .ack_t ( ack ),
  17. .data_out_t( wrdata ),
  18. . req_t ( req ),
  19. .data_in (data_in ),
  20. .valid (valid )
  21. );
  22. fullhs_rx fullhs_rx_u(
  23. .rclk ( rclk ),
  24. .rreset ( reset ),
  25. .data_in_r ( wrdata ),
  26. .req_r ( req ),
  27. .ack_r ( ack ),
  28. .data_out_r ( rddata )
  29. );
  30. endmodule

4.4 testbench编写

  1. `timescale 1ns / 1ps
  2. module tb_fullhs( );
  3. reg tclk;
  4. reg rclk;
  5. reg [31:0] data_in;
  6. reg valid;
  7. reg reset;
  8. initial begin
  9. tclk = 0;
  10. rclk = 0;
  11. reset = 0;
  12. data_in = 0;
  13. valid = 0;
  14. #200 ;
  15. reset = 1;
  16. #300 ;
  17. data_in = 32'hf0f0f0f0;
  18. valid = 1;
  19. #200 ;
  20. valid = 0;
  21. #300 ;
  22. data_in = 32'hffff0000;
  23. valid = 1;
  24. #200 ;
  25. valid = 0;
  26. # 300;
  27. data_in = 32'hff00ff00;
  28. valid =1;
  29. end
  30. always # 5 tclk = ~tclk;
  31. always # 10 rclk = ~rclk;
  32. fullhs_top fullhs_top_u(
  33. .tclk (tclk ),
  34. .rclk (rclk ),
  35. .data_in(data_in),
  36. .valid(valid),
  37. .reset(reset )
  38. );
  39. endmodule

4.5 仿真波形

4.5.1 慢时钟域到快时钟域

     

4.5.2 快时钟域到慢时钟域

修改testbench中的tclk和rclk

5.参考文献

跨时钟域之全握手_跨时钟 握手_DeamonYang的博客-CSDN博客

这篇文章写得很不错可以看看,但是没有设置valid信号。

        

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

闽ICP备14008679号