快时钟域到慢时钟域分两种情况:
1、允许采样丢失:直接采用同步器即可。
2、不允许采样丢失:原理是保证快时钟域的信号宽度满足一定的条件,使得慢时钟域有足够的时间采样到。
对于情况2有两种方法解决:①信号展宽+边沿检测②握手,且①比②要优先被选择。因为握手资源消耗较大,一般不用。
方法一:
脉冲信号展宽+边沿检测,脉冲信号转换成电平信号再进行边沿检测
- 电路图:
- 代码:(verilog是描述电路的语言,所以要心中有电路,代码就好写了)
module pulse_detect(
input clk_fast ,
input clk_slow ,
input rst_n ,
input data_in ,
output dataout
);
reg data_in_fast;
reg [2:0] data_slow;
//将脉冲信号在快时钟域展平为电平信号。即展宽脉冲信号。在两次脉冲信号之间为电平信号。
always@(posedge clk_fast or negedge rst_n)begin
if(!rst_n)
data_in_fast<= 0;
else
data_in_fast<= data_in ? (~data_in_fast) : data_in_fast;
end
//将展宽的脉冲信号在慢时钟域打两拍,并检测边沿。
always@(posedge clk_slow or negedge rst_n)begin
if(!rst)
data_slow <= 3'b0;
else
data_slow <= {data_slow[1:0],data_in_reg};
end
assign dataout = data_slow[2] ^ data_slow[1];
endmodule
- 波形
方法二
握手+边沿:
- 电路图:
- 代码1:
module pulse_detect(
input clk_fast ,
input clk_slow ,
input rst_n ,
input data_in ,
output dataout
);
//握手方式
reg fast_req;//fast时钟域的请求信号
reg slow_ack;//slow时钟域的应答信号
reg [2:0] slow_req;//slow时钟域的请求信号
reg [2:0] fast_ack;//fast时钟域的应答信号
//fast时钟域
//将slow时钟域的应答信号打三拍,送到fast时钟域
always@(posedge clk_fast or negedge rst_n)begin
if(!rst_n)
fast_ack <= 3'd0;
else
fast_ack <= {fast_ack[1:0],slow_ack};
end
//生成请求信号fast_req
always@(posedge clk_fast or negedge rst_n)begin
if(!rst_n)
fast_req <= 0;
else if(data_in)
fast_req <= 1'b1;
else
//快时钟域中没有输入数据时
//如果慢时钟域应答了,01x,则快时钟域此时不请求,否则,快时钟域的请求信号维持上一时钟的状态
fast_req <= ((fast_ack[1]) & (~fast_ack[2])) ? 1'b0 : fast_req;
end
//slow时钟域
//将fast时钟域的请求信号打三拍,送到slow时钟域
always@(posedge clk_slow or negedge rst_n)begin
if(!rst_n)
slow_req <= 0;
else
slow_req <= {slow_req[1:0],fast_req};
end
//生成应答信号slow_ack
always@(posedge clk_slow or negedge rst_n)begin
if(!rst_n)
slow_ack <= 0;
else if(slow_req[1] & (~slow_req[2]))
//如果快时钟域的请求信号由0变为1,01x,即发出请求信号,则慢时钟域进行应答
slow_ack <= 1'b1;
else
//如果slow请求信号10x,即慢时钟域请求信号无效时,慢时钟域不应答
//否则慢时钟域应答信号不变
slow_ack <= (slow_req[2]&(~slow_req[1])) ? 1'b0 : slow_ack;
end
//当慢时钟域01x,发出请求信号时,输出为1.
assign dataout = (~slow_req[2]) & (slow_req[1]);
endmodule
- 代码2:
module Sync_Pulse (
input src_clk,
input dst_clk,
input rst_n,
input src_pulse,
output dst_pulse
);
reg req_state_dly1, req_state_dly2,dst_req_state,src_sync_req;
reg ack_state_dly1,src_sync_ack;
wire dst_sync_ack;
always @ (posedge src_clk or negedge rst_n) begin
if (rst_n == 1'b0)
src_sync_req <= 1'b0;
else if (src_pulse)
src_sync_req <= 1'b1;
else if (src_sync_ack)
src_sync_req <= 1'b0;
else;
end
always @ (posedge dst_clk or negedge rst_n) begin
if (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
assign dst_sync_ack = req_state_dly2;
always @ (posedge src_clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
ack_state_dly1 <= 1'b0;
src_sync_ack <= 1'b0;
end
else begin
ack_state_dly1 <= dst_sync_ack;
src_sync_ack <= ack_state_dly1;
end
end
assign dst_pulse = dst_req_state & (~req_state_dly2);
endmodule
对上述代码增加同步失败的指示信号
module handshake_pulse_sync
(
input src_clk , //source clock
input src_rst_n, //source clock reset (0: reset)
input src_pulse , //source clock pulse in
output src_sync_fail , //source clock sync state: 1 clock pulse if sync fail.
input dst_clk , //destination clock
input dst_rst_n , //destination clock reset (0:reset)
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, ack_state_dly2 ;
reg req_state_dly1, req_state_dly2 ;
reg dst_req_state ;
reg 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
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
电路分析:
将src时钟域的脉冲信号打一拍之后,在dst时钟域内打三拍进行同步,其中第二拍的结果作为dst时钟域的应答信号,第二拍和第三拍的结果做边沿检测,以保证在dst时钟域输出接收到的脉冲信号。
其中第二拍的应答信号,在src时钟域经过两级同步作为src的应答信号,当输入脉冲信号时,src的请求信号有效。当src的应答信号有效时,请求信号无效。
缺点:上述操作无法检测到脉冲连续输入时产生的错误,即无法检测同步失败。如:在当前脉冲信号同步过程中又发出了新的脉冲信号的情况。
快时钟域至少展宽脉冲宽度到大于慢时钟域的周期长度。