In an earlier post, we have seen the usage of a Double Flop Synchronizer. When data has to be sent across from a fast clock domain to a slower domain, Handshake synchronization makes a better choice.
A simple Double Flop Synchronizer has the tendency to miss small pulses of data when the source clock is faster than the destination clock.
Handshake or Feedback synchronization helps to alleviate this problem by utilizing a pulse-stretching method that guarantees the capture of data. The drawback however, is the increased latency to complete synchronization of the data.
Handshake synchronizer also has the advantage of being a closed-loop solution.
An open-loop solution is one which does not provide any acknowledgement upon completion of synchronization. In such a case, the source domain will not know when to place the new data.
Handshake synchronizer returns a 'busy' signal to the source domain to indicate that synchronization is in progress and new data can be placed only after busy signal is de-asserted.
If I use the existing design without the 'busy' signal, then the solution becomes open-loop.
Handshake synchronization procedure involves the following key steps:
Step 1:
Step 3:
After the signal is synchronized, the same must be re-synchronized back to the source domain to act as acknowledgement to de-assert the stretched signal.
This is again achieved using a double-flop synchronizer, this time working at the source clk domain.
Step 4:
In some cases, we may need to generate a pulse of the data, then we can insert a pulse generation circuit at the destination side.
Verilog Code:
module fb_sync (
clkA,
clkB,
resetA,
resetB,
inA,
outB,
outB_pulse,
busy ); input clkA; input clkB; input resetA; input resetB; input inA; output outB; output outB_pulse;
output busy;
reg outB_level;
reg inA_level,next_inA_level ;
wire outB_pulse;
wire busy;
wire ack;
assign outB_pulse = outB & (!outB_level);
assign busy = inA_level || ack;
always @ (posedge clkB or negedge resetB) begin if(~resetB) outB_level <= 1'b0; else outB_level <= outB; end wire outB; double_flop_sync OUTPUT_DFF ( //Instance 1 of double flop synchronizer .clk (clkB), .rst_n (resetB), .async_in (inA_level), .sync_out (outB) ); double_flop_sync ACK_DFF ( //Instance 2 of double flop synchronizer .clk (clkA), .rst_n (resetA), .async_in (outB), .sync_out (ack) ); always @(posedge clkA or negedge resetA) begin if (!resetA) begin inA_level <= 1'b0; end else begin inA_level <= next_inA_level; end end always @ (*) begin if (inA) begin next_inA_level = 1'b1 ; end else begin if (ack) begin next_inA_level = 1'b0 ; end else begin next_inA_level = inA_level; end
end
end endmodule
Testbench:
module fb_sync_tb; reg clkA; reg clkB; reg resetA; reg resetB; reg inA; wire outB; wire outB_pulse;
wire busy; fb_sync DUT ( clkA, clkB, resetA, resetB, inA, outB, outB_pulse,
busy ); always #5 clkA=~clkA; always #10 clkB=~clkB; initial begin clkA=1'b0; clkB=1'b0; resetA=1'b0; resetB=1'b0; inA=1'b0; #50 resetA=1'b1; resetB=1'b1; #45 inA=1'b1; #20 inA=1'b0; end initial #700 $finish; endmodule
Simulation Waveform:
- inA signal arrives at the source clock domain.
- inA signal is stretched to create an inA_level signal.
- outB is obtained after double-flopping inA_level at the destination clock domain.
- outB is now double-flopped back to source clock domain as ack to deassert the inA_level signal.
- outB_pulse is obtained as one pulse of the outB signal.
- busy is obtained as long as the synchronizer is active in its operation.
No comments:
Post a Comment