前面提到了简单的双电平锁存器,下面是一些单bit同步电路。
一、慢时钟域向快时钟域
边沿检测同步器
将慢时钟域的脉冲搬移并缩小为快时钟域的脉冲。
既可以检测上升沿,也可以检测下降沿。
如上图,慢时钟下一个有效脉冲的最短周期为慢时钟的一个周期,站在快时钟的角度下,这个慢时钟域的信号会在快时钟域下持续很多个周期。实际上,这个脉冲在慢时钟域只发生了一次,所以如果用快时钟去检查有效脉冲的翻转边沿是最准确的。边沿上升与下降也只有一次。
适用条件:data_width>clk_fast+T_hold,最安全的就是两个同步周期长度。这样才能保证慢时钟域的脉冲足够保持到被快时钟的同步器采样到。
其verilog代码如下:
二、快时钟域向慢时钟域
脉冲同步器
从快时钟域的取出一个单时钟宽度脉冲,在慢时钟域建立新的单时钟宽度脉冲。
图中阴影部分为快时钟域下的翻转电路。
由于在慢时钟域下直接采样快时钟域的信号,很大概率会采样失败。因此,我们采用翻转电路对有效脉冲进行标定。
适用条件:输入脉冲间隔>=2*clk_slow。输入脉冲相隔过近,则慢时钟域的新脉冲也紧密相邻,结果是输出脉冲比一个时钟周期宽;如果太近,则无法检测到每一个脉冲。
其verilog代码如下:
针对此脉冲同步器出现的问题:源时钟域中的第一个脉冲和第二个脉冲间隔过短,第一个脉冲未完成同步,第二脉冲又将状态清空,导致最终脉冲同步丢失。我们引入握手机制进行解决,思路如下:
(1) 同步请求产生;当同步器处于空闲(即上一次已同步完成)时,源同步脉冲到达时产生同步请求信号sync_req;
(2) 同步请求信号sync_req同步到目的时钟域,目的时钟域产生脉冲信号并将产生应答信号sync_ack;
(3) 同步应答信号sync_ack同步到源时钟域,源时钟域检测到同步应答信号sync_ack后,清除同步请求信号;
(4) 目的时钟域检测到sync_req撤销后,清除sync_ack应答;源时钟域将到sync_ack清除后,认为一次同步完成,可以同步下一个脉冲。
verilog代码如下:
module HANDSHAKE_PULSE_SYNC(src_clk , //source clock src_rst_n , //source clock reset (0: reset)src_pulse , //source clock pulse insrc_sync_fail , //source clock sync state: 1 clock pulse if sync fail.dst_clk , //destination clock dst_rst_n , //destination clock reset (0:reset)dst_pulse //destination pulse out);//PARA DECLARATION//INPUT DECLARATION
input src_clk ; //source clock
input src_rst_n ; //source clock reset (0: reset)
input src_pulse ; //source clock pulse ininput dst_clk ; //destination clock
input dst_rst_n ; //destination clock reset (0:reset)//OUTPUT DECLARATION
output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail.
output dst_pulse ; //destination pulse out//INTER DECLARATION
wire dst_pulse ;
wire src_sync_idle ;
reg src_sync_fail ;
reg src_sync_req ;
reg src_sync_ack ;
reg ack_state_dly1 ;
reg ack_state_dly2 ;
reg req_state_dly1 ;
reg req_state_dly2 ;
reg dst_req_state ;
reg dst_sync_ack ;//--========================MODULE SOURCE CODE==========================--//--=========================================--
// DST Clock :
// 1. generate src_sync_fail;
// 2. generate sync req
// 3. sync dst_sync_ack
//--=========================================--
assign src_sync_idle = ~(src_sync_req | src_sync_ack );//report an error if src_pulse when sync busy ;
always @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)src_sync_fail <= 1'b0 ;else if (src_pulse & (~src_sync_idle)) src_sync_fail <= 1'b1 ;else src_sync_fail <= 1'b0 ;
end//set sync req if src_pulse when sync idle ;
always @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)src_sync_req <= 1'b0 ;else if (src_pulse & src_sync_idle) src_sync_req <= 1'b1 ;else if (src_sync_ack)src_sync_req <= 1'b0 ;
endalways @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)beginack_state_dly1 <= 1'b0 ;ack_state_dly2 <= 1'b0 ;src_sync_ack <= 1'b0 ; endelsebeginack_state_dly1 <= dst_sync_ack ;ack_state_dly2 <= ack_state_dly1 ;src_sync_ack <= ack_state_dly2 ; end
end//--=========================================--
// DST Clock :
// 1. sync src sync req
// 2. generate dst pulse
// 3. generate sync ack
//--=========================================--
always @(posedge dst_clk or negedge dst_rst_n)
beginif(dst_rst_n == 1'b0)beginreq_state_dly1 <= 1'b0 ;req_state_dly2 <= 1'b0 ;dst_req_state <= 1'b0 ;endelsebeginreq_state_dly1 <= src_sync_req ;req_state_dly2 <= req_state_dly1 ;dst_req_state <= req_state_dly2 ;end
end//Rising Edge of dst_state generate a dst_pulse;
assign dst_pulse = (~dst_req_state) & req_state_dly2 ; //set sync ack when src_req = 1 , clear it when src_req = 0 ;
always @(posedge dst_clk or negedge dst_rst_n)
beginif(dst_rst_n == 1'b0)dst_sync_ack <= 1'b0;else if (req_state_dly2) dst_sync_ack <= 1'b1;else dst_sync_ack <= 1'b0;
endendmodule