跨时钟域之握手机制
-
全握手机制概述
在不同的时钟域之间进行数据传输时,可以考虑使用握手同步机制。握手同步机制分为半握手和全握手。当从低频时钟域向高频时钟域传输数据时,半握手机制比较适用,这是由于接收端可以更快的完成操作。但是从高频时钟向低频时钟传输数据时,则需要全握手机制。
- 握手同步机制的步骤如下
(1)在高频时钟域下,将一个需要发送的数据准备好,将req信号设置为有效。在低频时钟域下,接收端同步该req信号,同步后的信号命名为req_r0、req_r1;
(2)在低速时钟域下,通过边沿检测的方式来检测信号req_r0、req_r1的上升沿pos_req_r0、pos_req_r1,在信号pos_req_r0的上升沿有效时对数据进行采样,并在信号pos_req_r1有效时生成响应信号ack;
(3)在高速时钟域下,同步该响应信号ack,同步后的信号为 ack_r0、ack_r1,通过边沿检测的方式得到ack信号的上升沿pos_ack_r0,在信号pos_ack_r0为高电平的时候将req信号拉低。
(4)在低速时钟域下,检测到req同步后的信号req_r0为低电平时,拉低响应信号ack。
- 个人理解
1、发送方准备好数据之后,通过req信号通知下接收方;
2、接收方在req信号有效时,接收数据;
3、接收方在接收到数据之后,返还一个ack信号给发送方,告诉它接收方已经接收到数据了;
4、发送方收到响应信号ack有效之后,将req信号拉低,说明数据已经传输结束了;
5、接收方在收到req信号拉低之后,也将ack信号给拉低;
- 程序编写
1 module hand_shake( 2 input clka , // fast clock 3 input clkb , // slow clock 4 5 input rst_n , 6 7 input data_vld , // 持续一个时钟周期 8 input [7:0] data_in , 9 10 output reg [7:0] data_out 11 ); 12 13 //========================================================================\ 14 // =========== Define Parameter and Internal signals =========== 15 //========================================================================/ 16 17 reg data_vld_r0 ; 18 reg data_vld_r1 ; 19 20 wire neg_data_vld ; 21 22 reg req ; 23 reg ack ; 24 25 reg ack_rx_r0 ; 26 reg ack_rx_r1 ; 27 28 wire pos_ack_rx ; 29 30 reg req_r0 ; 31 reg req_r1 ; 32 reg req_r2 ; 33 34 wire pos_req_r0 ; 35 wire pos_req_r1 ; 36 37 //============================================================================= 38 //**************************** Main Code ******************************* 39 //============================================================================= 40 41 always @(posedge clka or negedge rst_n) begin 42 if(!rst_n) begin 43 data_vld_r0 <= 1'b0; 44 data_vld_r1 <= 1'b0; 45 end 46 else begin 47 data_vld_r0 <= data_vld; 48 data_vld_r1 <= data_vld_r0; 49 end 50 end 51 52 assign neg_data_vld = (data_vld_r1 == 1'b1) && (data_vld_r0 == 1'b0); 53 54 always @(posedge clka or negedge rst_n) begin // 将ack信号同步到clka时钟下 55 if(!rst_n) begin 56 ack_rx_r0 <= 1'b0; 57 ack_rx_r1 <= 1'b0; 58 end 59 else begin 60 ack_rx_r0 <= ack; 61 ack_rx_r1 <= ack_rx_r0; 62 end 63 end 64 65 assign pos_ack_rx = (ack_rx_r1 == 1'b0) && (ack_rx_r0 == 1'b1); 66 67 always @(posedge clka or negedge rst_n) begin // 请求信号设计 68 if(!rst_n) 69 req <= 1'b0; 70 else if(neg_data_vld == 1'b1) 71 req <= 1'b1; 72 else if(pos_ack_rx == 1'b1) 73 req <= 1'b0; 74 else 75 req <= req; 76 end 77 78 // ================ 接收数据部分设计 ============================== 79 80 always @(posedge clkb or negedge rst_n) begin // 在时钟域clkb下同步req信号 81 if(!rst_n) begin 82 req_r0 <= 1'b0; 83 req_r1 <= 1'b0; 84 req_r2 <= 1'b0; 85 end 86 else begin 87 req_r0 <= req; 88 req_r1 <= req_r0; 89 req_r2 <= req_r1; 90 end 91 end 92 93 assign pos_req_r0 = (req_r1 == 1'b0) && (req_r0 == 1'b1); 94 assign pos_req_r1 = (req_r2 == 1'b0) && (req_r1 == 1'b1); 95 96 always @(posedge clkb or negedge rst_n) begin // 在同步过来的req信号为高电平时对数据进行采样 97 if(!rst_n) 98 data_out <= 8'd0; 99 else if(pos_req_r0 == 1'b1) 100 data_out <= data_in; 101 else 102 data_out <= data_out; 103 end 104 105 always @(posedge clkb or negedge rst_n) begin // 响应信号ack的设计 106 if(!rst_n) 107 ack <= 1'b0; 108 else if(pos_req_r1 == 1'b1) 109 ack <= 1'b1; 110 else if(req_r0 == 1'b0) 111 ack <= 1'b0; 112 else 113 ack <= ack; 114 end 115 116 endmodule
- 仿真验证
1 `timescale 1ns/1ps 2 3 module handshake_tb; 4 reg clka ; // fast clock 5 reg clkb ; // slow clock 6 7 reg rst_n ; 8 9 reg data_vld ; // 持续一个时钟周期 10 reg [7:0] data_in ; 11 12 wire [7:0] data_out ; 13 14 reg [3:0] cnt ; 15 16 hand_shake hand_shake_inst( 17 .clka ( clka ), 18 .clkb ( clkb ), 19 .rst_n ( rst_n ), 20 .data_vld ( data_vld ), 21 .data_in ( data_in ), 22 .data_out ( data_out ) 23 ); 24 25 initial 26 begin 27 $dumpfile("wave.vcd"); 28 $dumpvars(0,handshake_tb); 29 end 30 31 initial 32 clka = 1'b0; 33 always #4 clka = ~clka; 34 35 initial 36 clkb = 1'b0; 37 always #10 clkb = ~clkb; 38 39 initial 40 begin 41 #1; 42 rst_n = 1'b0; 43 #51; 44 rst_n = 1'b1; 45 end 46 47 always @(posedge clka or negedge rst_n) begin 48 if(!rst_n) 49 cnt <= 4'd0; 50 else 51 cnt <= cnt + 1'b1; 52 end 53 54 always @(posedge clka or negedge rst_n) begin 55 if(!rst_n) 56 data_vld <= 1'b0; 57 else if(&cnt == 1'b1) 58 data_vld <= 1'b1; 59 else 60 data_vld <= 1'b0; 61 end 62 63 always @(posedge clka or negedge rst_n) begin 64 if(!rst_n) 65 data_in <= 8'd0; 66 else if(&cnt == 1'b1) 67 data_in <= data_in + 1'b1; 68 else 69 data_in <= data_in; 70 end 71 72 endmodule


- 参考
[1] https://blog.csdn.net/qq_39814612/article/details/105795252
[2] https://reborn.blog.csdn.net/article/details/112726447?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-14.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-14.control
[3] https://blog.csdn.net/weixin_37603007/article/details/82805966?utm_term=%E6%8F%A1%E6%89%8B%E6%9C%BA%E5%88%B6%E7%9A%84%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-4-82805966&spm=3001.4430

浙公网安备 33010602011771号