Clocks in SDC
Declare clocks, divided clocks and asynchronous clock groups in SDC, read which edges the tool pairs, and tell an ideal clock from a propagated one.
- Lesson
- L2.6
- Time
- 30 min
- Runs on
- OpenROAD
- Check
- 5 questions
The asynchronous FIFO of R6.4 writes on a 10 ns clock and reads on a 7 ns clock. Its first timing run listed the read-data path failing by 0.1605 ns. The tool launched the word at 20 ns and wanted it at the read register by 21 ns: one nanosecond for a path that the FIFO's pointers already keep safe.
mem5 is written on wclk and read through the mux tree into rdata on rclk. The SDC has two create_clock lines and nothing else.mem5 → rdatasetup check, latest arrival3 cells
- Clock latency
- Cell delay
- Setup time
- Launch at 20 ns
The tool picked the third
wclkedge, at 20 ns. - Capture at 21 ns
The next
rclkedge after it is at 21 ns. That pair is the closest the two clocks ever come. - Violated
Clock-to-Q and two mux levels take 1.0606 ns, so the slack is -0.1605 ns.
Read the path as a table
Launch: wclk rise edge at 20.0000 ns, clock latency 0.0000 ns (ideal). Capture: rclk rise edge at 21.0000 ns, latency 0.0000 ns, library setup time −0.0999 ns.
Arrival 21.0606 ns, required 20.9001 ns, slack −0.1605 ns (violated).
| Cell | Arc | Through | Edge | Delay (ns) | Time (ns) | Slew (ns) |
|---|---|---|---|---|---|---|
| 1 | mem5/CLK → mem5/Q | dfxtp_1 | fall | 0.3040 | 20.3040 | 0.0213 |
mem5/Q → rmux_hi/A1 | wire m5 | fall | 0.0000 | 20.3040 | 0.0213 | |
| 2 | rmux_hi/A1 → rmux_hi/X | mux4_1 | fall | 0.4680 | 20.7720 | 0.0797 |
rmux_hi/X → rmux/A1 | wire hi | fall | 0.0000 | 20.7720 | 0.0797 | |
| 3 | rmux/A1 → rmux/X | mux2_1 | fall | 0.2886 | 21.0606 | 0.0500 |
rmux/X → rdata/D | wire rd | fall | 0.0000 | 21.0606 | 0.0500 |
The report
Startpoint: mem5 (rising edge-triggered flip-flop clocked by wclk)
Endpoint: rdata (rising edge-triggered flip-flop clocked by rclk)
Path Group: rclk
Path Type: max
Fanout Cap Slew Delay Time Description
-------------------------------------------------------------------------------------
0.1000 20.0000 20.0000 clock wclk (rise edge)
0.0000 20.0000 clock network delay (ideal)
0.1000 0.0000 20.0000 ^ mem5/CLK (sky130_fd_sc_hd__dfxtp_1)
1 0.0014 0.0213 0.3040 20.3040 v mem5/Q (sky130_fd_sc_hd__dfxtp_1)
m5 (net)
0.0213 0.0000 20.3040 v rmux_hi/A1 (sky130_fd_sc_hd__mux4_1)
1 0.0018 0.0797 0.4680 20.7720 v rmux_hi/X (sky130_fd_sc_hd__mux4_1)
hi (net)
0.0797 0.0000 20.7720 v rmux/A1 (sky130_fd_sc_hd__mux2_1)
1 0.0017 0.0500 0.2886 21.0606 v rmux/X (sky130_fd_sc_hd__mux2_1)
rd (net)
0.0500 0.0000 21.0606 v rdata/D (sky130_fd_sc_hd__dfxtp_1)
21.0606 data arrival time
0.1000 21.0000 21.0000 clock rclk (rise edge)
0.0000 21.0000 clock network delay (ideal)
0.0000 21.0000 clock reconvergence pessimism
21.0000 ^ rdata/CLK (sky130_fd_sc_hd__dfxtp_1)
-0.0999 20.9001 library setup time
20.9001 data required time
-------------------------------------------------------------------------------------
20.9001 data required time
-21.0606 data arrival time
-------------------------------------------------------------------------------------
-0.1605 slack (VIOLATED)OpenSTA 3.1.0 on the sky130 tt library, ideal clocks with a 0.1 ns clock slew, no wires.
The tool timed exactly what the SDC said, and the SDC was wrong about the clocks.
What create_clock declares
create_clock -name wclk -period 10 [get_ports wclk] gives a clock four things. It has a name and a period. It has a waveform: it rises at 0 and falls at half the period, unless -waveform says otherwise. It also has a source, the port or pin where it starts.
From the source, the tool follows the clock through buffers and inverters to every flop clock pin. It stops at a flop. A clock never passes through a CLK→Q arc, so a flop that divides a clock makes a net with no clock on it.
| SDC command | What it sets | Term in the checks of L2.4 |
|---|---|---|
create_clock -period T | name, period, waveform, source | T, and the launch and capture edges |
set_clock_latency L | expected clock delay to the flops (ideal clock) | Llaunch, Lcapture |
set_clock_latency -source L | delay from the clock's origin to the block's port | added to both latencies |
set_clock_transition S | slew at the flop clock pins (ideal clock) | the index of tcq, tsetup and thold tables |
set_clock_uncertainty U | margin for jitter and skew not yet built | Usetup, Uhold |
set_propagated_clock | time the clock through the real buffers | each flop's own L, so skew appears |
The clock commands of OpenSTA 3.1.0 and where each lands in the setup and hold equations of L2.4.
Before clock tree synthesis the clock is ideal. Every flop sees the edge at the latency from set_clock_latency, zero by default, with no skew. After the tree is built, set_propagated_clock makes the tool add up the real buffer delays instead, and the ideal latency is ignored.
A divided clock is a new clock
Lesson R2.3 warned that a flop dividing a clock makes a new clock domain. In div2, flop dv toggles on clk, and flops rb and rc run on its output. With only create_clock on clk, check_setup lists rb/CLK and rc/CLK as unclocked: no clock reaches them, so nothing they capture is timed.
clk reaches dv and ra; the divided net clkd clocks rb and rc.- The clock stops at dv
The tool follows
clkto the CLK pins ofdvandra. It does not crossdv. - Declare the divided clock
create_generated_clock -divide_by 2 -source [get_ports clk]ondv/Qnames a 2 ns clock tied toclk. - Crossings are timed
Now
ra→rbis a path fromclktoclkd, checked with edges of both clocks.
Read the diagram as text
- clk port: create_clock, 1 ns
- dv (dfxtp_1): toggles every edge
- ra (dfxtp_1): on clk
- clkd net: create_generated_clock
- rb, rc (dfxtp_1): on clkd
- clk port → dv (dfxtp_1): clock
- clk port → ra (dfxtp_1): clock
- dv (dfxtp_1) → clkd net: Q
- clkd net → rb, rc (dfxtp_1): clock
- ra (dfxtp_1) → rb, rc (dfxtp_1): data
A generated clock keeps its master, so the tool knows where its edges fall relative to clk.
The options follow the circuit. Use -divide_by for a counter, -multiply_by for a clock multiplier such as a PLL, and -edges when the new waveform is irregular. -source names where the master is measured: its port, or the divider's own clock pin.
The generated clock also carries the divider's delay once the clock is propagated. Its edge reaches rb a clock-to-Q after the clk edge: 0.3425 ns. That makes the hold check from ra to rb fail by 0.0053 ns.
clkd. Its latency is the delay through dv from clk.ra → rbhold check, earliest arrival1 cell
- Clock latency
- Cell delay
- Hold time
- New data from ra
ralaunches onclkwith a clock-to-Q of 0.3119 ns. - The late divided edge
clkdreachesrb0.3425 ns after the sameclkedge. With a hold time of -0.0253 ns,rbneeds its old D until 0.3172 ns. - Hold violated
Slack -0.0053 ns: the data changes before the late edge has finished sampling.
Read the path as a table
Launch: clk rise edge at 0.0000 ns, clock latency 0.0000 ns. Capture: clkd rise edge at 0.0000 ns, latency 0.3425 ns, library hold time −0.0253 ns.
Arrival 0.3119 ns, required 0.3172 ns, slack −0.0053 ns (violated).
| Cell | Arc | Through | Edge | Delay (ns) | Time (ns) | Slew (ns) |
|---|---|---|---|---|---|---|
| 1 | ra/CLK → ra/Q | dfxtp_1 | rise | 0.3119 | 0.3119 | 0.0329 |
ra/Q → rb/D | wire a | rise | 0.0000 | 0.3119 | 0.0329 |
The report
Startpoint: ra (rising edge-triggered flip-flop clocked by clk)
Endpoint: rb (rising edge-triggered flip-flop clocked by clkd)
Path Group: clkd
Path Type: min
Fanout Cap Slew Delay Time Description
-------------------------------------------------------------------------------------
0.0000 0.0000 clock clk (rise edge)
0.0000 0.0000 clock source latency
2 0.0038 0.1000 0.0000 0.0000 ^ clk (in)
clk (net)
0.1000 0.0000 0.0000 ^ ra/CLK (sky130_fd_sc_hd__dfxtp_1)
1 0.0017 0.0329 0.3119 0.3119 ^ ra/Q (sky130_fd_sc_hd__dfxtp_1)
a (net)
0.0329 0.0000 0.3119 ^ rb/D (sky130_fd_sc_hd__dfxtp_1)
0.3119 data arrival time
0.0000 0.0000 clock clkd (rise edge)
0.0000 0.0000 clock source latency
2 0.0038 0.1000 0.0000 0.0000 ^ clk (in)
clk (net)
0.1000 0.0000 0.0000 ^ dv/CLK (sky130_fd_sc_hd__dfxtp_1)
3 0.0061 0.0713 0.3425 0.3425 ^ dv/Q (sky130_fd_sc_hd__dfxtp_1)
clkd (net)
0.0713 0.0000 0.3425 ^ rb/CLK (sky130_fd_sc_hd__dfxtp_1)
0.0000 0.3425 clock reconvergence pessimism
-0.0253 0.3172 library hold time
0.3172 data required time
-------------------------------------------------------------------------------------
0.3172 data required time
-0.3119 data arrival time
-------------------------------------------------------------------------------------
-0.0053 slack (VIOLATED)OpenSTA 3.1.0, propagated clocks with a 0.1 ns slew at the clk port.
The same check with ideal clocks
ra → rbhold check, earliest arrival1 cell
- Clock latency
- Cell delay
- Hold time
Read the path as a table
Launch: clk rise edge at 0.0000 ns, clock latency 0.0000 ns (ideal). Capture: clkd rise edge at 0.0000 ns, latency 0.0000 ns, library hold time −0.0215 ns.
Arrival 0.3119 ns, required −0.0215 ns, slack 0.3334 ns (met).
| Cell | Arc | Through | Edge | Delay (ns) | Time (ns) | Slew (ns) |
|---|---|---|---|---|---|---|
| 1 | ra/CLK → ra/Q | dfxtp_1 | rise | 0.3119 | 0.3119 | 0.0329 |
ra/Q → rb/D | wire a | rise | 0.0000 | 0.3119 | 0.0329 |
The report
Startpoint: ra (rising edge-triggered flip-flop clocked by clk)
Endpoint: rb (rising edge-triggered flip-flop clocked by clkd)
Path Group: clkd
Path Type: min
Fanout Cap Slew Delay Time Description
-------------------------------------------------------------------------------------
0.1000 0.0000 0.0000 clock clk (rise edge)
0.0000 0.0000 clock network delay (ideal)
0.1000 0.0000 0.0000 ^ ra/CLK (sky130_fd_sc_hd__dfxtp_1)
1 0.0017 0.0329 0.3119 0.3119 ^ ra/Q (sky130_fd_sc_hd__dfxtp_1)
a (net)
0.0329 0.0000 0.3119 ^ rb/D (sky130_fd_sc_hd__dfxtp_1)
0.3119 data arrival time
0.1000 0.0000 0.0000 clock clkd (rise edge)
0.0000 0.0000 clock network delay (ideal)
0.0000 0.0000 clock reconvergence pessimism
0.0000 ^ rb/CLK (sky130_fd_sc_hd__dfxtp_1)
-0.0215 -0.0215 library hold time
-0.0215 data required time
-------------------------------------------------------------------------------------
-0.0215 data required time
-0.3119 data arrival time
-------------------------------------------------------------------------------------
0.3334 slack (MET)Ideal clocks with a 0.1 ns clock slew: both edges at 0 ns.
An ideal clock puts both edges at 0 ns, so this hold check passes with 0.3334 ns. Clock tree synthesis must later balance clkd against clk, or this crossing breaks.
Related clocks and the closest edges
Two clocks in one SDC are related unless you say otherwise. The tool lines up both waveforms over their common period, here 70 ns. For each launch edge it takes the next capture edge, and it checks the tightest pair. For 10 ns and 7 ns, that pair is 20 ns and 21 ns.
- Cycle 7
Launch at 0 ns, capture at 7 ns: 7 ns for the path.
- Cycle 14
Launch at 10 ns, capture at 14 ns: 4 ns for the path.
- Cycle 21
Launch at 20 ns, capture at 21 ns: 1 ns for the path.
The tool times a wclk-to-rclk path against the smallest of these gaps.
| Launch (wclk) | Next capture (rclk) | Time for the path |
|---|---|---|
| 0 ns | 7 ns | 7 ns |
| 10 ns | 14 ns | 4 ns |
| 20 ns | 21 ns | 1 ns |
| 30 ns | 35 ns | 5 ns |
| 40 ns | 42 ns | 2 ns |
| 50 ns | 56 ns | 6 ns |
| 60 ns | 63 ns | 3 ns |
Every wclk edge in one 70 ns common period. The pattern then repeats.
For clocks with no fixed phase, that 1 ns is meaningless. The edges drift past each other, so every gap occurs, down to zero. The FIFO handles this with Gray pointers and two-flop synchronizers, and the SDC must tell the tool so.
Asynchronous clock groups
set_clock_groups -asynchronous -group [get_clocks wclk] -group [get_clocks rclk] declares that no path between the groups is timed, in either direction. The crossings leave the report, and the worst path is now inside the read domain.
The worst setup path after the clock groups
rptr0 → rdatasetup check, latest arrival3 cells
- Clock latency
- Cell delay
- Setup time
Read the path as a table
Launch: rclk rise edge at 0.0000 ns, clock latency 0.0000 ns (ideal). Capture: rclk rise edge at 7.0000 ns, latency 0.0000 ns, library setup time −0.0999 ns.
Arrival 1.1768 ns, required 6.9001 ns, slack 5.7233 ns (met).
| Cell | Arc | Through | Edge | Delay (ns) | Time (ns) | Slew (ns) |
|---|---|---|---|---|---|---|
| 1 | rptr0/CLK → rptr0/Q | dfxtp_1 | rise | 0.3559 | 0.3559 | 0.0892 |
rptr0/Q → rmux_hi/S0 | wire r0 | rise | 0.0000 | 0.3559 | 0.0892 | |
| 2 | rmux_hi/S0 → rmux_hi/X | mux4_1 | fall | 0.5323 | 0.8882 | 0.0797 |
rmux_hi/X → rmux/A1 | wire hi | fall | 0.0000 | 0.8882 | 0.0797 | |
| 3 | rmux/A1 → rmux/X | mux2_1 | fall | 0.2886 | 1.1768 | 0.0500 |
rmux/X → rdata/D | wire rd | fall | 0.0000 | 1.1768 | 0.0500 |
The report
Startpoint: rptr0 (rising edge-triggered flip-flop clocked by rclk)
Endpoint: rdata (rising edge-triggered flip-flop clocked by rclk)
Path Group: rclk
Path Type: max
Fanout Cap Slew Delay Time Description
-------------------------------------------------------------------------------------
0.1000 0.0000 0.0000 clock rclk (rise edge)
0.0000 0.0000 clock network delay (ideal)
0.1000 0.0000 0.0000 ^ rptr0/CLK (sky130_fd_sc_hd__dfxtp_1)
2 0.0082 0.0892 0.3559 0.3559 ^ rptr0/Q (sky130_fd_sc_hd__dfxtp_1)
r0 (net)
0.0892 0.0000 0.3559 ^ rmux_hi/S0 (sky130_fd_sc_hd__mux4_1)
1 0.0018 0.0797 0.5323 0.8882 v rmux_hi/X (sky130_fd_sc_hd__mux4_1)
hi (net)
0.0797 0.0000 0.8882 v rmux/A1 (sky130_fd_sc_hd__mux2_1)
1 0.0017 0.0500 0.2886 1.1768 v rmux/X (sky130_fd_sc_hd__mux2_1)
rd (net)
0.0500 0.0000 1.1768 v rdata/D (sky130_fd_sc_hd__dfxtp_1)
1.1768 data arrival time
0.1000 7.0000 7.0000 clock rclk (rise edge)
0.0000 7.0000 clock network delay (ideal)
0.0000 7.0000 clock reconvergence pessimism
7.0000 ^ rdata/CLK (sky130_fd_sc_hd__dfxtp_1)
-0.0999 6.9001 library setup time
6.9001 data required time
-------------------------------------------------------------------------------------
6.9001 data required time
-1.1768 data arrival time
-------------------------------------------------------------------------------------
5.7233 slack (MET)Read pointer to read data, inside rclk: slack 5.7233 ns.
Clock groups come in three kinds. -asynchronous is for clocks from separate sources. -logically_exclusive and -physically_exclusive are for clocks that never run together, such as two clocks behind one multiplexer. For timing, all three cut the paths between the groups.
Clock groups check nothing on those paths, and that includes one property the FIFO needs. Its Gray pointer works only if the pointer bits reach the synchronizers within about one read period of each other. Real flows bound that skew with a maximum delay on the pointer paths.
Dropped by OpenSTA 3.1.0
set_clock_groups -asynchronous \
-group [get_clocks wclk] -group [get_clocks rclk]
set_max_delay 7 -from [get_cells {wptr0 wptr1}] \
-to [get_cells {rq1_0 rq1_1}]
The clock groups win: the pointer paths stay untimed.
Timed
set_max_delay 7 -ignore_clock_latency \
-from [get_cells {wptr0 wptr1}] -to [get_cells {rq1_0 rq1_1}]
set_false_path -from [get_cells mem*] -to [get_cells rdata]
set_false_path -hold -from [get_clocks wclk] -to [get_clocks rclk]
No clock-level exception: the pointer paths get a 7 ns limit.
In OpenSTA 3.1.0, a max delay is dropped on paths that clock groups or any false path already cut, even a false path written between the two clocks. The version that works names the paths instead: the pointer gets its limit, and the memory paths and the cross-clock hold checks are cut one by one. -ignore_clock_latency bounds the data path alone, leaving out both clocks' latencies. Try it below runs it.
Try it
This is the FIFO with the pointer recipe of the last section. Run it. Paths shows wptr0 → rq1_0 in a group called "path delay": required 6.9121 ns, which is the 7 ns limit minus the flop's setup time, and slack 6.6067 ns. The worst slack overall, 5.7233 ns, now sits inside the read domain.
Now replace the three exception lines with the set_clock_groups line and run again. The pointer paths disappear from the report: nothing checks their skew any more. Delete every exception, and the memory path of the opening figure fails again.
Run to draw the timing paths here.
path 1 slack 6.6067 ns path 1 required 6.9121 ns WNS 5.7233 nsRead the netlist: afifo_x.v
module afifo_x (wclk, rclk, wd, wg0, wg1, rp0, rp1, rp2, rdq, wsync0, wsync1);
input wclk;
input rclk;
input wd;
input wg0;
input wg1;
input rp0;
input rp1;
input rp2;
output rdq;
output wsync0;
output wsync1;
wire g0, g1, m0, m1, m2, m3, m4, m5, m6, m7, s0, s1, r0, r1, r2, lo, hi, rd;
sky130_fd_sc_hd__dfxtp_1 wptr0 (.CLK(wclk), .D(wg0), .Q(g0));
sky130_fd_sc_hd__dfxtp_1 wptr1 (.CLK(wclk), .D(wg1), .Q(g1));
sky130_fd_sc_hd__dfxtp_1 mem0 (.CLK(wclk), .D(wd), .Q(m0));
sky130_fd_sc_hd__dfxtp_1 mem1 (.CLK(wclk), .D(wd), .Q(m1));
sky130_fd_sc_hd__dfxtp_1 mem2 (.CLK(wclk), .D(wd), .Q(m2));
sky130_fd_sc_hd__dfxtp_1 mem3 (.CLK(wclk), .D(wd), .Q(m3));
sky130_fd_sc_hd__dfxtp_1 mem4 (.CLK(wclk), .D(wd), .Q(m4));
sky130_fd_sc_hd__dfxtp_1 mem5 (.CLK(wclk), .D(wd), .Q(m5));
sky130_fd_sc_hd__dfxtp_1 mem6 (.CLK(wclk), .D(wd), .Q(m6));
sky130_fd_sc_hd__dfxtp_1 mem7 (.CLK(wclk), .D(wd), .Q(m7));
sky130_fd_sc_hd__dfxtp_1 rq1_0 (.CLK(rclk), .D(g0), .Q(s0));
sky130_fd_sc_hd__dfxtp_1 rq2_0 (.CLK(rclk), .D(s0), .Q(wsync0));
sky130_fd_sc_hd__dfxtp_1 rq1_1 (.CLK(rclk), .D(g1), .Q(s1));
sky130_fd_sc_hd__dfxtp_1 rq2_1 (.CLK(rclk), .D(s1), .Q(wsync1));
sky130_fd_sc_hd__dfxtp_1 rptr0 (.CLK(rclk), .D(rp0), .Q(r0));
sky130_fd_sc_hd__dfxtp_1 rptr1 (.CLK(rclk), .D(rp1), .Q(r1));
sky130_fd_sc_hd__dfxtp_1 rptr2 (.CLK(rclk), .D(rp2), .Q(r2));
sky130_fd_sc_hd__mux4_1 rmux_lo (.A0(m0), .A1(m1), .A2(m2), .A3(m3), .S0(r0), .S1(r1), .X(lo));
sky130_fd_sc_hd__mux4_1 rmux_hi (.A0(m4), .A1(m5), .A2(m6), .A3(m7), .S0(r0), .S1(r1), .X(hi));
sky130_fd_sc_hd__mux2_1 rmux (.A0(lo), .A1(hi), .S(r2), .X(rd));
sky130_fd_sc_hd__dfxtp_1 rdata (.CLK(rclk), .D(rd), .Q(rdq));
endmodule
Check yourself
Remember
- create_clock gives a name, a period, a waveform and a source; the clock stops at every flop.
- A clock divided by a flop needs create_generated_clock, which keeps its relation to the master clock.
- Related clocks are timed against their tightest launch-capture pair over the common period.
- Asynchronous clock groups cut every check between the groups; a max delay on the Gray pointer needs path-level exceptions.
- Ideal clocks use the SDC latency with no skew; set_propagated_clock times the real buffers.