FPGA逻辑设计回顾(7)多比特信号的CDC处理方式之握手同步
前言
注:本文首发自易百纳技术社区,FPGA逻辑设计回顾(7)多比特信号的CDC处理方式之握手同步;
每种跨时钟域处理的方式都有其适用范围,例如:两级同步器,用于单比特信号处理,且是从慢时钟域到快时钟域:
还有反馈展宽同步方式,用于单比特信号同步,且从慢时钟域到快时钟域:FPGA逻辑设计回顾(4)亚稳态与单比特脉冲信号的CDC处理问题
Mux同步器,用于单向同步的多比特同步:
FPGA逻辑设计回顾(5)多比特信号的CDC处理方式之MUX同步器
异步FIFO用途倒是挺广,但是过于有时杀鸡用牛刀也是多次一举:
FPGA逻辑设计回顾(6)多比特信号的CDC处理方式之异步FIFO
格雷码同步在异步FIFO的内部得以应用,用于读写指针的跨时钟域传输,在上述异步FIFO链接中也有讲到。
本文要介绍的CDC处理方式是握手同步,它适用于数据稳定(不频繁变化)情况下的数据跨时钟域同步。
看见了吧,这就是数字设计的多样性,针对不同的场景可以使用不同的同步方式。
这些方式都掌握是一件好事,一是可以锤炼自己的设计水平,另外,只有熟练掌握才能随机应变。还有,这是找工作的常考知识!
握手同步介绍
在这种同步方案中,无论源时钟和目的时钟之间的时钟周期比如何,都采用请求和确认机制来保证正确的数据采样到目的时钟域。这种技术主要用于不连续变化或非常频繁地变化的数据。
如下图表显示了这种实现方式:
如下图握手同步的时序所示,数据应该在总线上保持稳定,直到从目的端接收到同步确认信号(A2-q),并且它(A2-q)变为低电平。
握手同步的RTL实现
虽然上面给出了实现框图,但是毕竟是框图,图中一个Sender FSM,以及Received FSM,没有给出实现细节,对初学者可能还像是空中楼阁一样,没什么实际的作用。
其实有一个技巧,就是看时序图,可以从时序图中得到握手同步的实现方式。
这也是作为一个设计者的素质,RTL代码,时序图,无缝切换,所谓,做逻辑的,不画时序图算什么?
从图中时序图,我们提炼出输入信号:
- clk_a, clk_b
- rst(这个图中没有,但是很有必要)
- a_en,data_a(这两者作为输入,可以当成一种协议,检测到a_en有效(下降沿)的时候,输入数据就更新,更新数据一直持续到a_en的下一个下降沿)
输出信号:
- b_en,这是对a_en的同步
- data_b_out,这是对b的同步
- ack_a,这是我个人想要加进来的,它是B时钟域的响应信号ack,同步到a时钟域后的信号,通知a时钟域(下降沿通知),可以发送下一个数据了。事实上,a_en就是该信号的下降沿;
好了,有了这些信息,可以进行RTL逻辑设计了:
我给出我的设计代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer: 李锐博恩
// Create Date: 2021/01/16 00:34:50
// Module Name: syn_handshake
//
module syn_handshake( input wire clk_a , input wire clk_b , input wire rst , input wire a_en , //来自于外部的使能信号,脉冲持续一个时钟周期 input wire [3:0] data_a_in , //外部输入信号 output reg [3:0] data_b_out , output wire b_en , output wire ack_a ); //生成a_en的下降沿 reg a_en_d1 ; reg a_en_d2 ; reg a_en_neg ; always@(posedge clk_a or posedge rst) begin if(rst) begin a_en_d1 <= 1'd0; a_en_d2 <= 1'd0; a_en_neg <= 1'd0; end else begin a_en_d1 <= a_en; a_en_d2 <= a_en_d1; // a_en_neg <= ~a_en_d1 && a_en_d2; a_en_neg <= ~a_en && a_en_d1; end end //生成请求信号 reg a_req ; always@(posedge clk_a or posedge rst) begin if(rst) begin a_req <= 1'd0; end else if(a_en_neg) begin a_req <= 1'd1; end else if(a2_q_pos) begin //a_req 拉低条件 a_req <= 1'd0; end else begin a_req <= a_req; end end //请求信号a_req跨时钟域处理 reg b1_q, b2_q; always@(posedge clk_b or posedge rst) begin if(rst) begin b1_q <= 1'd0; b2_q <= 1'd0; end else begin b1_q <= a_req; b2_q <= b1_q; end end //生成时钟域b内的数据使能信号 //b2_q信号的上升沿 //b时钟域对a时钟域的响应信号 reg b2_q_d1; reg b2_q_d2; reg b2_q_d3; wire b_en; reg ack_b; always@(posedge clk_b or posedge rst) begin if(rst) begin b2_q_d1 <= 1'd0; b2_q_d2 <= 1'd0; b2_q_d3 <= 1'd0; ack_b <= 1'd0; end else begin b2_q_d1 <= b2_q; b2_q_d2 <= b2_q_d1; b2_q_d3 <= b2_q_d2; ack_b <= b2_q_d2; end end assign b_en = ~b2_q_d3 && b2_q_d2; //b_en有效,则表示数据有效,可以采样a时钟域的数据了 always@(posedge clk_b or posedge rst) begin if(rst) begin data_b_out <= 4'd0; end else if(b_en) begin data_b_out <= data_a_in; end end //响应信号同步到a时钟域 //a2_q上升沿作为a_req拉低的条件 reg a1_q, a2_q; reg a3_q ; wire a2_q_pos; always@(posedge clk_a or posedge rst) begin if(rst) begin a1_q <= 1'd0; a2_q <= 1'd0; a3_q <= 1'd0; end else begin a1_q <= ack_b; a2_q <= a1_q; a3_q <= a2_q; end end assign a2_q_pos = ~a3_q && a2_q; assign ack_a = a2_q; //此信号作为a_en的反馈信号,a_en取此信号的下降沿
endmodule
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
有的朋友,可能会说了,我看过其他的握手同步的设计方法,为什么代码和你的不一样,或者我看过你的其他篇,握手同步,但是为什么代码和以前的不一样;
这个嘛!不同时期写的嘛,以前我怎么写的,我也忘了。但是我相信是万变不离其中的。
握手同步的行为仿真
有了设计,没有仿真怎么能行,每一个设计都是需要仿真的,要不然怎么证明你的设计是没有问题的。
行为仿真是验证逻辑功能的关键一步!
本逻辑仿真的重点在于设计输入信号,输入信号中的重点在于我刚才说的握手同步是有适用场景的,因此,要设计这个场景的输入信号,这里要设计的是输入数据一定要是稳定的,不要频繁的变化;
如下我设计的仿真逻辑,data_a,只有在a_en有效的时候才更新,a_en有效,那必然是时钟域B同步完成了。这就相当于一个握手的过程完成了,协议达成, 成
交!
这里给出仿真平台:
`timescale 1ns / 1ps
//
// Company:
// Engineer: 李锐博恩
// Create Date: 2021/01/16 00:34:50
// Module Name: syn_handshake_tb
//
module syn_handshake_tb( ); reg clk_a ; reg clk_b ; reg rst ; reg a_en ; //来自于外部的使能信号,脉冲持续一个时钟周期 reg [3:0] data_a_in ; //外部输入信号 wire [3:0] data_b_out ; wire b_en ; wire ack_a ; initial begin clk_a = 1'd0; forever begin #2 clk_a = ~clk_a; end end initial begin clk_b = 1'd0; forever begin #3 clk_b = ~clk_b; end end initial begin rst = 1'd1; #15 @(negedge clk_a); rst = 1'd0; end reg ack_a_d1; always@(posedge clk_a or posedge rst) begin if(rst) begin a_en <= 1'd1; ack_a_d1 <= 1'd0; end else begin ack_a_d1 <= ack_a; a_en <= ~ack_a && ack_a_d1; end end always@(posedge clk_a or posedge rst) begin if(rst) begin data_a_in <= 4'd0; end else if(a_en) begin data_a_in <= $random; end end syn_handshake u_syn_handshake( .clk_a ( clk_a ), .clk_b ( clk_b ), .rst ( rst ), .a_en ( a_en ), .data_a_in ( data_a_in ), .data_b_out ( data_b_out ), .b_en ( b_en ), .ack_a ( ack_a ) ); endmodule
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
一个简单的TB文件!
仿真波形:
下面标出重点:
其他不说,红色方框内的信号表示输入信号已经同步到时钟域B了。
中间信号呢?下面也给出:
建议大家放到自己平台上仿真,然后对着代码以及仿真,分析实现的过程。
最后给出RTL原理图:
可见,我并没有如原理中所说,使用到状态机的方式,方法可能很多,选择适合你的吧。
再给出综合后的原理图,以验证我的设计是可综合的:
不得不说,这可能是我要总结的CDC的最后一种方式了。
也不一定,还有一些方法,说不定我有空了,还会总结几个,但基本就这些了,再见吧,朋友们。
参考资料
Synchronizer techniques for multi-clock domain SoCs & FPGAs
FIFO, handshake synchronizers a challenge for CDC analysis
Get those clock domains in sync
Crossing the abyss:asynchronous signals in a synchronous world
有空多读读这些英文资料,可能会有启发!
文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。
原文链接:reborn.blog.csdn.net/article/details/112726447
- 点赞
- 收藏
- 关注作者
评论(0)