一、概述
在网上看到一篇单bit跨时钟域的verilog HDL实现代码,为便于以后查看归纳到本文中,重点对其中一些实现技巧做总结。
二、跨时钟域时序图
跨时钟域的时序图说明如下:
1) 将src_clk时钟域的输入脉冲转换为src_clk时钟域的电平信号src_state;
2)对src_data电平信号进行打拍(一般可打2拍)同步到dst_clk时钟域;
3)对dst_clk时钟域的电平信号进行边沿检测,产生dst_clk时钟域的脉冲;
三、verilog HDL实现代码及关键点(见代码注释)
module PULSE_SYNC
(
src_clk , //source clock
src_rst_n , //source clock reset (0: reset)
src_pulse , //source clock pulse in
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 in
input dst_clk ; //destination clock
input dst_rst_n ; //destination clock reset (0:reset)
//OUTPUT DECLARATION
output dst_pulse ; //destination pulse out
//INTER DECLARATION
reg src_state ;
reg state_dly1 ;
reg state_dly2 ;
reg dst_state ;
wire dst_pulse ;
//--========================MODULE SOURCE CODE==========================--
always @(posedge src_clk or negedge src_rst_n)
begin
if(src_rst_n == 1'b0)
src_state <= 1'b0 ;
else if (src_pulse)
src_state <= ~src_state ;//在发送时钟域,在两次输入信号高脉冲之间拉高的方法
end
always @(posedge dst_clk or negedge dst_rst_n)
begin
if(dst_rst_n == 1'b0)
begin
state_dly1 <= 1'b0 ;
state_dly2 <= 1'b0 ;
dst_state <= 1'b0 ;
end
else
begin
state_dly1 <= src_state ;
state_dly2 <= state_dly1;
dst_state <= state_dly2;
end
end
assign dst_pulse = dst_state ^ state_dly2 ;//产生边缘检测的实现方法
endmodule
四、增加了握手机制
分析上面同步器的基本设计原理:
(1)源时钟域脉冲转换为源时钟域电平信号;
(2)对单bit电平信号进行打拍的异步处理;
(3)在目的时钟域中进行脉冲还原。
我们可以发现该同步器的控制传递是单向的,即仅从源时钟域到目的时钟域,目的时钟域并没有状态反馈。假设存在如下应用:源时钟域中的第一个脉冲和第二个脉冲间隔过短,第一个脉冲未完成同步,第二脉冲又将状态清空,导致最终脉冲同步丢失。
要解决以上同步问题,需要引入异步握手机制,保证每个脉冲都同步成功,同步成功后再进行下一个脉冲同步。握手原理如下:
sync_req: 源时钟域同步请求信号,高电平表示当前脉冲需要同步;
sync_ack: 目的时钟域应答信号,高电平表示当前已收到同步请求。
五、实现过程
完整同步过程分为以下4个步骤:
(1) 同步请求产生;当同步器处于空闲(即上一次已同步完成)时,源同步脉冲到达时产生同步请求信号sync_req;
(2) 同步请求信号sync_req同步到目的时钟域,目的时钟域产生脉冲信号并将产生应答信号sync_ack;
(3) 同步应答信号sync_ack同步到源时钟域,源时钟域检测到同步应答信号sync_ack后,清除同步请求信号;
(4) 目的时钟域检测到sync_req撤销后,清除sync_ack应答;源时钟域将到sync_ack清除后,认为一次同步完成,可以同步下一个脉冲。
module HANDSHAKE_PULSE_SYNC
(
src_clk , //source clock
src_rst_n , //source clock reset (0: reset)
src_pulse , //source clock pulse in
src_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 in
input 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)
begin
if(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)
begin
if(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 ;
end
always @(posedge src_clk or negedge src_rst_n)
begin
if(src_rst_n == 1'b0)
begin
ack_state_dly1 <= 1'b0 ;
ack_state_dly2 <= 1'b0 ;
src_sync_ack <= 1'b0 ;
end
else
begin
ack_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)
begin
if(dst_rst_n == 1'b0)
begin
req_state_dly1 <= 1'b0 ;
req_state_dly2 <= 1'b0 ;
dst_req_state <= 1'b0 ;
end
else
begin
req_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)
begin
if(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;
end
endmodule