Tuesday, August 9, 2022

Handshake Synchronizer Design and Verilog Code

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:

Consider the source and destination clock domains to be A and B respectively.
Input data is inA. 
The input signal from the source domain (typically faster clock) needs to be stretched so that it can be sampled in the destination domain (slower clock).
This data is stretched using mux M1 and de-asserted upon arrival of acknowledgement using mux 2.
The stretched data is labelled as inA_level.
 

Step 2:
The stretched signal can be passed to the destination domain using a double-flop synchronizer. 
The output at the destination side is labelled as outB.

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.


Step 5:
To complete the handshake with the source side, busy signal must be asserted as long as the synchronizer is performing the synchronization activity. Once completed, it would be de-asserted.
This logic can be added for a closed-loop solution.


With these steps, we have completed the design of the Feedback Synchronizer.
Let us now write the Verilog code.

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:




As seen from the simulation waveform:
  1. inA signal arrives at the source clock domain.
  2. inA signal is stretched to create an inA_level signal.
  3. outB is obtained after double-flopping inA_level at the destination clock domain.
  4. outB is now double-flopped back to source clock domain as ack to deassert the inA_level signal.
  5. outB_pulse is obtained as one pulse of the outB signal.
  6. busy is obtained as long as the synchronizer is active in its operation.

No comments:

Post a Comment