Easy · Clock Domain Crossing · Sequential Logic
Implement the most important circuit in clock-domain crossing: a multi-flop synchronizer that brings a single-bit level signal from another clock domain into the clk domain.
A flip-flop only produces a clean 0 or 1 if its input is stable for a short window around the clock edge (setup and hold time). A signal coming from another clock domain (or from a button, or an asynchronous chip) can change at any moment, including inside that window. When that happens the flop can become metastable: its output hovers between 0 and 1 for an unpredictable time before settling to one of them. If downstream logic reads that half-way value, different gates may see different values and the design can fail in strange ways.
The probability that a metastable flop is still unresolved after a time t decays exponentially, roughly exp(-t / tau). A synchronizer therefore samples the signal with one flop and gives it a whole clock period to settle before a second flop samples it again. Only the last flop's output is used by the rest of the design. The reliability is measured as MTBF (mean time between failures):
MTBF = exp(t_resolve / tau) / (T0 * f_clk * f_data)
Every extra stage adds one clock period to t_resolve, which multiplies the MTBF by a huge factor. Two stages are the usual choice; very fast clocks or safety-critical designs use three.
Rules that come with it:
can arrive with some bits from the old value and some from the new one. (Later problems cover pulse synchronizers and Gray-code FIFOs for those cases.)
Simulation cannot show metastability. Simulators model a flip-flop as an ideal device, so in this problem (and in the hidden tests) the input d is driven synchronously to clk and the only observable behavior is the latency of the chain. That is exactly what is graded.
module two_flop_sync #(
parameter int STAGES = 2 // number of flip-flops in the chain, STAGES >= 2
) (
input logic clk,
input logic rst, // synchronous, active-high
input logic d, // asynchronous single-bit level input
output logic q // d, synchronized into the clk domain
);
STAGES flip-flops clocked by the rising edge of clk. The firstflop samples d; each next flop samples the previous one; q is the output of the last flop (no logic between the last flop and q).
d sampled at rising edge n appears on q right after risingedge n + STAGES − 1. Put differently: a value driven on d during cycle k is visible on q during cycle k + STAGES. With STAGES = 2, q is d delayed by exactly 2 cycles.
rst is synchronous and active-high. On every rising edge of clk where rst is1, every flop in the chain becomes 0 (so q is 0 in the cycle after that edge, and no value of d seen during reset ever reaches q). rst must have no effect between clock edges.
q stays 0 until a 1 sampled on d after the release hastravelled through the whole chain (latency rule above).
Write your module so that it works for any STAGES >= 2. The hidden tests build it with the default STAGES = 2 and also with other values of STAGES.
d rises in cycle 2 and falls in cycle 6; q follows exactly two cycles later.
| cycle | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| rst | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| d | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| q | x | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 |
Sign up free to solve Two-Flop Synchronizer in your browser.