Timing Closure

Setup and hold, slack and the critical path, maximum clock frequency, pipelining, metastability and its MTBF, and clock domain crossing.

1. What “fast enough” means

In Chapter 13 the timing summary reported a number called WNS, and we said it must be positive. This chapter explains where that number comes from and what to do when it is negative. The idea is simple: a synchronous circuit works only if every signal launched by one clock edge has arrived and settled at its destination flip-flop before the next edge samples it — and has not changed too early either.

The timing analyser cuts the design into timing paths. Each path starts at a point where data is launched and ends where it is captured:

  • register → register: from a flip-flop's clock pin, through its output, the logic and routing, to the D input of another flip-flop. This is the vast majority of paths.
  • input → register: from a top-level input pin to the first flip-flop. Timing it needs to know when the outside world changes the input (set_input_delay).
  • register → output: from the last flip-flop to an output pin (set_output_delay).

For buttons, switches and LEDs the input/output timing does not matter — a human cannot press a button in 8 ns — so in this course those paths are left unconstrained and handled with synchronisers. We focus on register-to-register paths.

launch FFDQcombinationallogic (LUTs, carry)t_logic + t_routecapture FFDQt_cqt_su, t_hin→regreg→outclk pinBUFGclock delay t_Lclock delay t_Cskew = t_C − t_L
A register-to-register timing path. The clock reaches the launch and capture flip-flops through a clock tree with slightly different delays; the difference is the clock skew.

2. The setup condition

Take the clock edge at time 0 at the clock pin. Through the clock tree it reaches the launch flip-flop at\(t_L\) and the capture flip-flop at \(t_C\). The clock skew is\(t_\text{skew} = t_C - t_L\). Three delays make up the data path:

  • \(t_\text{cq}\) — clock-to-Q: after its clock edge, the launch flip-flop needs this long to show the new value at Q.
  • \(t_\text{logic}\) — the delay through the LUTs and carry chains the signal passes (each one is a logic level).
  • \(t_\text{route}\) — the delay of the programmable wires between them. On an FPGA this is often as large as the logic delay, or larger.

The capture flip-flop needs its D input stable for a setup time \(t_\text{su}\)before its clock edge. The data launched at edge 0 must be captured by the next edge, which reaches the capture flip-flop at \(T + t_C\). So:

\[ \underbrace{t_L + t_\text{cq} + t_\text{logic} + t_\text{route}}_{\text{data arrival}} \;\le\; \underbrace{T + t_C - t_\text{su}}_{\text{data required}} \quad\Longleftrightarrow\quad t_\text{cq} + t_\text{logic} + t_\text{route} + t_\text{su} \;\le\; T + t_\text{skew} \]

The setup slack is how much room is left: required time minus arrival time. The real tools also subtract a clock uncertainty \(t_\text{unc}\) for jitter, and they use the slowest (maximum) delays of the slow process corner for the data path.

\[ \text{slack}_\text{setup} = \big(T + t_\text{skew} - t_\text{su} - t_\text{unc}\big) - \big(t_\text{cq} + t_\text{logic} + t_\text{route}\big)_\text{max} \;\ge\; 0 \]

Setting the slack to zero and solving for \(T\) gives the shortest period this path tolerates, and hence its maximum clock frequency:

\[ f_\text{max} = \frac{1}{T_\text{min}} = \frac{1}{t_\text{cq} + t_\text{logic} + t_\text{route} + t_\text{su} + t_\text{unc} - t_\text{skew}} \]

A worked example with illustrative numbers: \(t_\text{cq} = 0.5\) ns, three logic levels totalling 0.9 ns, 2.4 ns of routing, \(t_\text{su} = 0.1\) ns, skew −0.1 ns, no uncertainty. Arrival is 3.8 ns; required is \(8 - 0.1 - 0.1 = 7.8\) ns; slack is +4.0 ns, and \(f_\text{max} = 1/4.0\ \text{ns} = 250\ \text{MHz}\).

3. The hold condition

Setup says the data must not be too late. Hold says it must not be too early. A flip-flop needs its input to stay stable for a hold time \(t_h\) after its clock edge. The danger is the same clock edge: at edge 0 the launch flip-flop starts sending a new value while the capture flip-flop is still sampling the old one. If the path is very short and the capture clock arrives late, the new value can overwrite the old one inside the hold window.

\[ t_L + t_\text{cq,min} + t_\text{comb,min} \;\ge\; t_C + t_h \quad\Longleftrightarrow\quad \text{slack}_\text{hold} = \big(t_\text{cq} + t_\text{comb}\big)_\text{min} - \big(t_h + t_\text{skew}\big) \;\ge\; 0 \]

Here \(t_\text{comb} = t_\text{logic} + t_\text{route}\), using the fastest (minimum) delays. Two observations follow directly from the two inequalities:

  • The period \(T\) does not appear in the hold condition. A hold violation cannot be fixed by slowing the clock.
  • Positive skew (capture clock late) helps setup but hurts hold, by exactly the same amount. That is why FPGAs distribute clocks on dedicated low-skew networks, and why you must never clock logic from ordinary signals (Chapter 14).
launch clk0Tcapture clkt_CT + t_Ct_ht_sudata at Dmay be changingnew value stableold valuehold slackearliest change: t_L + min delayssetup slacklatest arrival
Setup and hold seen at the capture flip-flop. The data launched at edge 0 must change only after the hold window of the capture edge at t_C (hold slack) and must be settled before the setup window of the next capture edge at T + t_C (setup slack).
Key idea: setup is about the next edge, hold about the same edge
Setup compares the slowest arrival with the next capture edge, so it depends on the clock period. Hold compares the fastest arrival with the same capture edge, so it does not. Vivado's router fixes almost all hold violations by itself, by adding wire delay to short paths; your job is mostly setup.

4. Slack, WNS, TNS and the critical path

The analyser computes the slack of every path, for both setup and hold. The path with the smallest setup slack is the critical path: it alone determines \(f_\text{max}\). The summary numbers you met in Chapter 13 are aggregates over all endpoints:

\[ \text{WNS} = \min_\text{paths} \text{slack}_\text{setup}, \qquad \text{TNS} = \sum_\text{endpoints} \min\big(0,\ \text{worst slack at that endpoint}\big), \qquad \text{WHS} = \min_\text{paths} \text{slack}_\text{hold} \]

WNS tells you how far you are from closing timing; TNS tells you how widespread the problem is. A WNS of −0.3 ns with TNS −0.3 ns is one bad path; the same WNS with TNS −250 ns is a design-wide problem. Here is the shape of one path in a setup timing report (abridged; the numbers are illustrative but consistent):

report_timing (abridged, illustrative)
Slack (MET) :             1.234ns  (required time - arrival time)
  Source:                 acc_reg[3]/C   (rising edge-triggered cell FDRE clocked by sys_clk)
  Destination:            sum_reg[17]/D  (rising edge-triggered cell FDRE clocked by sys_clk)
  Path Group:             sys_clk
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            8.000ns  (sys_clk rise@8.000ns - sys_clk rise@0.000ns)
  Data Path Delay:        6.650ns  (logic 3.410ns (51.3%)  route 3.240ns (48.7%))
  Logic Levels:           9  (CARRY4=6 LUT2=1 LUT4=2)
  Clock Path Skew:        -0.045ns
  Clock Uncertainty:      0.035ns
  ...
  required time           7.884
  arrival time           -6.650
  -----------------------------
  slack                   1.234
LineWhat it tells you
Source / Destinationthe launch and capture flip-flops: find them in your HDL
Requirementthe time allowed, normally one period (8 ns)
Data Path Delayt_cq + logic + routing; the logic/route split hints at the fix
Logic Levelshow many LUTs/carry blocks in series: the main lever you control
Clock Path Skewt_skew; small on a global clock, large means a clocking problem
required − arrivalT + skew − t_su − uncertainty, minus the data path delay

Check the arithmetic: required \(= 8.000 - 0.045 - 0.035 - t_\text{su}\) with\(t_\text{su} \approx 0.036\) ns gives 7.884 ns, and \(7.884 - 6.650 = 1.234\) ns. If routing dominates the delay, the cells are far apart (high fanout, congestion, a resource in a fixed column); if logic dominates, there are too many levels between registers.

5. Pipelining: the main tool for setup

When a path has too much logic for one clock period, cut it into pieces with registers in between. Each piece then has a full period to itself. This is pipelining. The price is latency: a result appears more cycles after its inputs. The gain is throughput: a new input can still enter every cycle, at a higher clock rate.

Our example computes a four-element dot product of 8-bit unsigned numbers,\(y = a_0b_0 + a_1b_1 + a_2b_2 + a_3b_3\), packed into two 32-bit inputs. The result needs 18 bits: each product is at most \(255^2 = 65\,025\), and four of them sum to at most\(260\,100 < 2^{18}\). The first version registers the inputs and the output, and puts four multipliers and three adders between them — one long register-to-register path.

dot4 — one long path (latency 2)
module dot4 (
    input  wire        clk,
    input  wire [31:0] a,        // four 8-bit unsigned lanes a[7:0] .. a[31:24]
    input  wire [31:0] b,
    output reg  [17:0] y = 0     // a0*b0 + a1*b1 + a2*b2 + a3*b3, latency 2
);
    reg [31:0] a_r = 0, b_r = 0;
    always @(posedge clk) begin
        a_r <= a;
        b_r <= b;
        // One long register-to-register path: 4 multipliers and 3 adders
        y <= a_r[ 7: 0] * b_r[ 7: 0] + a_r[15: 8] * b_r[15: 8]
           + a_r[23:16] * b_r[23:16] + a_r[31:24] * b_r[31:24];
    end
endmodule

The pipelined version computes exactly the same thing, but stores the four products, then the two pair sums, then the total, each in its own register. The longest path is now one multiplier or one adder.

dot4_pipe — the same sum in four stages (latency 4)
module dot4_pipe (
    input  wire        clk,
    input  wire [31:0] a,        // four 8-bit unsigned lanes
    input  wire [31:0] b,
    output reg  [17:0] y = 0     // same sum of products, latency 4
);
    reg [31:0] a_r = 0, b_r = 0;                     // stage 1: inputs
    reg [15:0] p0 = 0, p1 = 0, p2 = 0, p3 = 0;       // stage 2: products
    reg [16:0] s01 = 0, s23 = 0;                     // stage 3: pair sums
    always @(posedge clk) begin
        a_r <= a;
        b_r <= b;
        p0  <= a_r[ 7: 0] * b_r[ 7: 0];
        p1  <= a_r[15: 8] * b_r[15: 8];
        p2  <= a_r[23:16] * b_r[23:16];
        p3  <= a_r[31:24] * b_r[31:24];
        s01 <= p0 + p1;
        s23 <= p2 + p3;
        y   <= s01 + s23;                            // stage 4: output
    end
endmodule
a_r, b_rp0..p3s01, s23y××××+++stage 2stage 3stage 4stage 1in dot4 the registers p0..p3 and s01, s23 do not exist: × and + form one path
Where the registers sit. dot4 has one combinational path from a_r/b_r to y; dot4_pipe cuts it after the multipliers and after the first adders. Each cut adds one cycle of latency.

The testbench drives both designs with the same pseudo-random inputs from a 32-bit LFSR, keeps a short history of the correct results, and checks that y of dot4 equals the result from two edges ago and y of dot4_pipe the result from four edges ago — for 196 consecutive cycles. Both languages print the same line.

Testbench: same results, two extra cycles of latency
`timescale 1ns/1ps
module dot4_tb;
    reg         clk = 0;
    reg  [31:0] a = 0, b = 0;
    wire [17:0] y2, y4;
    reg  [31:0] lfsr = 32'hACE1_2468;
    reg  [17:0] hist [0:4];            // hist[k]: correct result for inputs k edges ago
    integer i, k, errors = 0, checks = 0;

    dot4      u_flat (.clk(clk), .a(a), .b(b), .y(y2));
    dot4_pipe u_pipe (.clk(clk), .a(a), .b(b), .y(y4));

    always #4 clk = ~clk;

    // Reference model of the arithmetic
    function [17:0] ref_dot(input [31:0] x, input [31:0] z);
        ref_dot = x[7:0] * z[7:0] + x[15:8] * z[15:8]
                + x[23:16] * z[23:16] + x[31:24] * z[31:24];
    endfunction

    function [31:0] step(input [31:0] s);   // 32-bit Galois LFSR
        step = {s[30:0], 1'b0} ^ (s[31] ? 32'h04C1_1DB7 : 32'h0);
    endfunction

    initial begin
        for (k = 0; k <= 4; k = k + 1) hist[k] = 0;
        for (i = 0; i < 200; i = i + 1) begin
            @(negedge clk);
            // one clock edge has passed: age the history by one
            for (k = 4; k > 0; k = k - 1) hist[k] = hist[k-1];
            // y2 must equal the result for inputs applied 2 edges ago, y4 for 4
            if (i >= 4) begin
                checks = checks + 1;
                if (y2 !== hist[2] || y4 !== hist[4]) errors = errors + 1;
            end
            // apply new random inputs
            lfsr = step(lfsr); a = lfsr;
            lfsr = step(lfsr); b = lfsr;
            hist[0] = ref_dot(a, b);
        end
        if (errors == 0)
            $display("PASS: %0d checks, flat latency 2, pipelined latency 4, last y = %0d", checks, y4);
        else
            $display("FAIL: %0d errors in %0d checks", errors, checks);
        $finish;
    end
endmodule
Run it — both print: PASS: 196 checks, flat latency 2, pipelined latency 4, last y = 42024
iverilog -g2012 -o tb dot4.v dot4_pipe.v dot4_tb.v && vvp tb
ghdl -a --std=08 dot4.vhd dot4_pipe.vhd dot4_tb.vhd
ghdl -e --std=08 dot4_tb && ghdl -r --std=08 dot4_tb

On the Zynq the multipliers map to DSP48E1 slices, which have optional internal registers before and after the multiplier. Synthesis can absorb the pipeline registers you write into those built-in registers, so pipelining a multiplier often costs no extra fabric flip-flops at all — and a DSP slice reaches its rated speed only when those registers are used.

Common mistake: pipelining one branch only
If one signal goes through an extra register and another one it is combined with does not, the two arrive from different input samples and the result is silently wrong. Every path from the inputs to a given adder must cross the same number of registers. Count stages along every branch — or let a testbench like the one above do it for you.

6. Other ways to close timing

  • Retiming. Moving existing registers across logic so that stages become more balanced, without changing the latency. If stage 1 has 7 ns of logic and stage 2 has 2 ns, moving part of the logic from 1 to 2 helps. Vivado can do some of this automatically (a synthesis retiming option), but writing balanced stages yourself is more predictable.
  • Fewer logic levels. A 6-input LUT implements any function of six inputs in one level, so restructuring often helps: a wide comparison as a tree instead of a chain, a case instead of a long if/elsif priority chain when priority is not needed, one-hot state encoding (Chapter 12) instead of binary.
  • Less routing delay. A signal that drives hundreds of loads (a high fanout enable or reset) is slow because it must reach all of them. Registering it and duplicating the register helps; so does avoiding resets on datapath registers that do not need them.
  • Timing exceptions, with great care. set_false_path tells the analyser not to time a path at all; set_multicycle_path allows a path several periods (for example when a register is only enabled every fourth cycle). Both are promises you make. If the promise is false, the tool happily reports success and the hardware fails. Use them only for paths you can justify in writing, and prefer fixing the design.
On the board: measuring the effect of pipelining in Vivado

dot4 has 83 input/output bits — far more than the board's switches, buttons and LEDs. Build it out of context (as a block that will later sit inside a larger design), which needs no pin constraints. Run this once with dot4 and once with dot4_pipe:

ooc.tcl — vivado -mode batch -source ooc.tcl
set top dot4            ;# then: dot4_pipe
read_verilog $top.v
synth_design -top $top -part xc7z020clg400-1 -mode out_of_context
create_clock -name clk -period 4.000 [get_ports clk]   ;# ask for 250 MHz
opt_design
place_design
route_design
report_timing_summary -file $top.timing.rpt
report_utilization    -file $top.util.rpt
  1. Compare WNS for the two designs. Then shorten the period step by step until each one fails; estimate each \(f_\text{max}\) as \(1/(T - \text{WNS})\).
  2. Open the utilisation reports: how many DSP48E1 and flip-flops does each version use? Were your pipeline registers absorbed into the DSPs?
  3. In dot4.timing.rpt, find the critical path: how many logic levels, and what share is routing?

7. Metastability and its MTBF

Everything so far assumed the data meets setup and hold. A signal from a button, or from logic on another clock, has no fixed relation to our clock, so sooner or later it will change inside the setup/hold window. The flip-flop is then metastable: its internal latch is balanced between 0 and 1, like a ball on top of a hill, and its output may sit at an invalid level or oscillate for a while before falling one way. Which way, and when, is random. The probability that it is still undecided after a time\(t\) decays exponentially, \(\propto e^{-t/\tau}\), where \(\tau\) is a time constant of the flip-flop, typically tens of picoseconds in modern silicon.

A synchroniser gives the flip-flop time. In a two-flip-flop synchroniser the first stage samples the asynchronous signal; the second samples the first one a full period later. The design fails only if the first stage is still unresolved after the available resolution time\(t_r\). The standard model of the mean time between such failures is

\[ \text{MTBF} = \frac{e^{\,t_r/\tau}}{T_0 \, f_\text{clk} \, f_\text{data}}, \qquad t_r \approx (N-1)\big(T - t_\text{cq} - t_\text{su} - t_\text{route}\big) + \text{slack of the path that uses the result} \]

\(f_\text{clk}\) is the sampling clock, \(f_\text{data}\) the rate at which the asynchronous input changes, \(T_0\) a constant related to the width of the dangerous window, and\(N\) the number of flip-flops in the chain. The denominator counts how often you risk a metastable event; the numerator says how unlikely each one is to last long enough to matter. Because\(t_r\) sits in an exponent, every extra stage multiplies the MTBF by a gigantic factor.

The values of \(\tau\) and \(T_0\) below are illustrative, chosen to be of a plausible order of magnitude; real values depend on the silicon and the conditions. The shape of the result is what matters: one flip-flop fails in a fraction of a millisecond, two are vastly more than enough at 125 MHz, and at high clock rates the margin shrinks quickly.

Python
script.py58 lines

Click Run to execute the Python code

Code will be executed with Python 3 on the server

Key idea: you cannot eliminate metastability, only make it absurdly rare
No circuit can sample an asynchronous signal with zero failure probability. A synchroniser trades latency (one clock period per stage) for an exponential improvement in MTBF. Two stages at 125 MHz put the expected failure far beyond the lifetime of the hardware; that is why every asynchronous input in this course goes through one.

8. Crossing clock domains

A design with two unrelated clocks — say a 125 MHz system clock and a clock recovered from a video input — has two clock domains. A signal going from one to the other is a clock domain crossing (CDC), and to the receiving domain it is just as asynchronous as a button. Three cases, three techniques:

What crossesTechniqueWhy
a slow single-bit level2-FF synchroniserthe level lasts many destination cycles
a single-cycle pulsetoggle + 2-FF + edge detecta short pulse could fall between edges
a counter / pointerGray code + 2-FF per bitonly one bit changes per step
a multi-bit data wordhandshake or async FIFO (Ch. 17)bits may resolve on different cycles

The 2-FF synchroniser is the building block. The ASYNC_REG attribute tells Vivado that these flip-flops form a synchroniser: they are kept (not merged or optimised), placed in the same slice to minimise the wire between them, and recognised as such by the CDC reports.

sync_2ff — two flip-flops for one asynchronous bit
module sync_2ff (
    input  wire clk,       // destination clock
    input  wire d,         // asynchronous level (from another clock domain or a pin)
    output wire q          // synchronised copy, 2 clk cycles later
);
    // ASYNC_REG tells Vivado: keep these two flip-flops, place them side by
    // side, and treat the first one as a synchroniser stage.
    (* ASYNC_REG = "TRUE" *) reg s0 = 1'b0;
    (* ASYNC_REG = "TRUE" *) reg s1 = 1'b0;
    always @(posedge clk) begin
        s0 <= d;           // may go metastable
        s1 <= s0;          // has a full period for s0 to settle
    end
    assign q = s1;
endmodule

A one-cycle pulse in the source domain cannot go through a 2-FF synchroniser directly: if the destination clock is slower, its edges may miss the pulse entirely. The toggle pulse synchroniser turns each pulse into a change of level of a toggle flip-flop. A level stays put until the next pulse, so it cannot be missed; the destination synchronises it and detects each change with an XOR, producing exactly one destination-clock pulse per source pulse. The condition is that source pulses are spaced by at least a few destination cycles — otherwise two toggles could cancel before they are seen. When the source needs to know that the pulse arrived (or must send faster), a handshake sends an acknowledge back through a second synchroniser.

pulse_sync — one pulse in, one pulse out, across unrelated clocks
module pulse_sync (
    input  wire clk_a,     // source clock
    input  wire pulse_a,   // one-cycle pulse in the clk_a domain
    input  wire clk_b,     // destination clock, unrelated to clk_a
    output wire pulse_b    // one-cycle pulse in the clk_b domain
);
    // Source side: turn each pulse into a change of level
    reg toggle_a = 1'b0;
    always @(posedge clk_a)
        if (pulse_a) toggle_a <= ~toggle_a;

    // Destination side: 2-FF synchroniser plus one more FF to see the change
    (* ASYNC_REG = "TRUE" *) reg s0 = 1'b0;
    (* ASYNC_REG = "TRUE" *) reg s1 = 1'b0;
    reg s2 = 1'b0;
    always @(posedge clk_b) begin
        s0 <= toggle_a;
        s1 <= s0;
        s2 <= s1;
    end
    assign pulse_b = s1 ^ s2;   // high for one clk_b cycle per level change
endmodule

The testbench runs two unrelated clocks: 8 ns and 10.3 ns, with an odd phase offset so that the relative phase of their edges drifts through every value during the run. It checks that the level copy follows within three destination cycles, sends 25 single pulses spaced 6 to 10 source cycles apart, and checks that exactly 25 one-cycle pulses arrive. Both simulators print PASS: level ok, 25 pulses sent, 25 received at 2025.65 ns. (A simulator never goes metastable — it resolves every sample cleanly — so this checks the logic, not the MTBF.)

Testbench with two unrelated clocks
`timescale 1ns/1ps
module cdc_tb;
    // Two unrelated clocks: 125 MHz and about 97 MHz with an odd phase offset
    reg clk_a = 0, clk_b = 0;
    always #4 clk_a = ~clk_a;                    // 8 ns
    initial begin
        #1.7;
        forever #5.15 clk_b = ~clk_b;            // 10.3 ns
    end

    reg  lvl_a = 0, pulse_a = 0;
    wire lvl_b, pulse_b;
    integer i, sent = 0, received = 0, width_err = 0, lvl_err = 0;
    reg prev_b = 0;

    sync_2ff   u_lvl (.clk(clk_b), .d(lvl_a), .q(lvl_b));
    pulse_sync u_pls (.clk_a(clk_a), .pulse_a(pulse_a), .clk_b(clk_b), .pulse_b(pulse_b));

    // Count pulses in the destination domain; no pulse may last 2 cycles
    always @(posedge clk_b) begin
        if (pulse_b) received = received + 1;
        if (pulse_b && prev_b) width_err = width_err + 1;
        prev_b = pulse_b;
    end

    initial begin
        // 1. Level crossing: after 3 clk_b edges the copy must match
        for (i = 0; i < 6; i = i + 1) begin
            @(posedge clk_a) lvl_a <= ~lvl_a;
            repeat (3) @(posedge clk_b);
            #1 if (lvl_b !== lvl_a) lvl_err = lvl_err + 1;
        end
        // 2. Pulse crossing: 25 single-cycle pulses, 6 to 10 clk_a cycles apart
        for (i = 0; i < 25; i = i + 1) begin
            @(posedge clk_a) pulse_a <= 1'b1;
            @(posedge clk_a) pulse_a <= 1'b0;
            sent = sent + 1;
            repeat (5 + i % 5) @(posedge clk_a);
        end
        repeat (5) @(posedge clk_b);
        if (lvl_err == 0 && received == sent && width_err == 0)
            $display("PASS: level ok, %0d pulses sent, %0d received", sent, received);
        else
            $display("FAIL: lvl_err=%0d sent=%0d received=%0d width_err=%0d",
                     lvl_err, sent, received, width_err);
        $finish;
    end
endmodule

Finally, tell the timing analyser about the crossing. Without a constraint, Vivado assumes the two clocks are related and tries to time the path from toggle_a to s0 against the (meaningless) worst-case distance between their edges, producing a spurious failure. Declaring the clocks asynchronous removes those paths from analysis — which is correct only because every crossing goes through a synchroniser:

cdc.xdc
## Two clocks from two pins that have no fixed phase relationship
create_clock -name clk_a -period 8.000  [get_ports clk_a]
create_clock -name clk_b -period 10.300 [get_ports clk_b]

## Tell the timing analyser not to time paths between them.  The
## synchronisers (ASYNC_REG) are what make those crossings safe.
set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b]
Common mistake: a separate synchroniser for each bit of a bus
Eight 2-FF synchronisers on an 8-bit value do not synchronise the value. When several bits change at once, each one may resolve on a different destination cycle, so for one cycle the destination can see a mixture of old and new bits — a number that never existed. Use Gray code when the value steps by one, or a handshake or an asynchronous FIFO for arbitrary data (Chapter 17).

Practice Problems

Progress: 0/5 (0%)
1
⭐ Easy
A register-to-register path has \(t_\text{cq} = 0.45\) ns, logic delay 2.1 ns, routing delay 3.0 ns, \(t_\text{su} = 0.05\) ns and clock skew \(+0.1\) ns (the capture clock arrives later). Ignoring uncertainty, find the setup slack at 125 MHz and the path's \(f_\text{max}\).
2
⭐ Easy
Your design fails hold timing (WHS = −0.08 ns). A colleague suggests halving the clock frequency. Will that help? Explain with the hold inequality.
3
⭐⭐ Medium
A combinational path has 9.6 ns of logic plus routing; each register adds 0.6 ns of overhead (\(t_\text{cq} + t_\text{su}\)). (a) What is \(f_\text{max}\) unpipelined? (b) You split it into three balanced stages. What are the new \(f_\text{max}\), the latency in cycles and in nanoseconds, and the throughput?
4
⭐⭐ Medium
With the illustrative values of the MTBF program (\(\tau = 120\) ps, register overhead 0.6 ns), by what factor does adding a third flip-flop to a synchroniser clocked at 125 MHz multiply the MTBF? Why is the answer so large?
5
⭐⭐⭐ Hard
A designer passes a 4-bit counter from clock domain A to domain B through four independent 2-FF synchronisers. (a) Give a concrete transition that can produce a value in B that the counter never had. (b) Why does sending the counter in Gray code fix it? (c) What does a Gray-coded pointer still not guarantee?

Problem Set Statistics

Total Problems
5
Attempted
0
Completion
0%
Share:XRedditLinkedIn
Rate this chapter: