Every timing path is measured against a clock, so the first constraint names the clocks and states each EXAMPLE period. Without one the tool has no target for those paths, as the missing-constraints lesson shows. With one, the period becomes the budget, and everything else in this topic refines it: margins, IO shares, and named exceptions.
Synthesis treats the clocks it is given as ideal: every flop clock pin switches at the same instant, with no skew and no insertion delay. Real skew is handled later, by the clock tree in layout and propagated-clock analysis in signoff.
The design below is a divide-by-2 register: a flop whose output feeds back inverted, toggling on every master rising edge. The script synthesises it and asserts the one sequential cell with reset; the second half is the break-it preview, the feedback tied to a constant, which should leave no sequential cell. Both halves write their netlist, so every count below is read back from those files.
read_verilog <<EOV
module div2(input clk, input rst, output reg q);
always @(posedge clk) begin
if (rst) q <= 1'b0;
else q <= ~q;
end
endmodule
EOV
synth -top div2
tee -o stat_toggle.txt stat
write_json net_toggle.json
select -assert-count 1 t:$_DFF_P_ t:$_SDFF_PP0_ %u
check
design -reset
read_verilog <<EOV
module div2stuck(input clk, output reg q);
always @(posedge clk) q <= 1'b0;
endmodule
EOV
synth -top div2stuck
tee -o stat_stuck.txt stat
write_json net_stuck.json
select -assert-none t:$_DFF_P_ t:$_SDFF_PP0_
check
log PASS: a divider needs its inverted feedback and a stuck driver loses the flopThe period comes from the SDC clock and the toggle from this RTL: Yosys proves the structure, the SDC names it. The netlist picture is a recording; the clock text is illustrative, because the open tools have no SDC reader.
The toggle version: 2 cells, 1 flop. The stuck version optimises to 0 cells and no flop: a flop that always loads a constant is wiring, not state.
Version (recorded) | Cells (recorded) | Flops (recorded) |
|---|---|---|
Toggle | 2 | 1 flop |
Stuck at constant | 0 | no flop |
The toggle netlist holds 2 cells: one inverter for the feedback and one resettable flop for the state, drawn below. The stuck netlist holds 0 cells with no flop: constant in, constant out, so the tool deleted the state and the divider with it.
The constraint below names both clocks for the same divider. The master line states a name and an EXAMPLE period on the clock port. The generated line carries three fields: -source takes the master source pin or port, never the clock name; the created object is the divider flop's Q pin; -divide_by 2 with -name states the relationship.
# not-run: no SDC reader in the open tools
create_clock -name CLK -period 10.0 [get_ports clk]
create_generated_clock -name DIV2 -source [get_ports clk] -divide_by 2 [get_pins div_reg/Q]Two clock models share this text. The ideal model, which synthesis uses, treats both clocks as perfect: zero skew, zero latency. The propagated model, which signoff uses, traces each edge through the real clock network, so skew and insertion delay appear. The synthesis numbers in this topic are all ideal-clock numbers; the next lesson prices what the ideal model leaves out as uncertainty and latency.
Frequency is one divided by the period. The EXAMPLE parameters below are a 10.0 ns master, 100 MHz, and its divide-by-2 child. The program checks both frequencies, both periods, the six-edge toggle trace, and the divided rising edges on every second master edge: five self-checks, so the doubling is proven, not quoted.
import sys
# Divide-by-2 clock arithmetic: EXAMPLE parameters, hand-derived.
PERIOD_NS = 10.0
DIVIDE_BY = 2
freq_mhz = 1000.0 / PERIOD_NS
div_period_ns = PERIOD_NS * DIVIDE_BY
div_freq_mhz = 1000.0 / div_period_ns
# Toggle model: q starts at reset 0, then inverts on every master rising edge.
q = 0
trace = []
for _ in range(6):
q = 1 - q
trace.append(q)
if freq_mhz != 100.0:
print("FAIL: master frequency got " + str(freq_mhz) + " want 100.0")
sys.exit(1)
if div_period_ns != 20.0:
print("FAIL: divided period got " + str(div_period_ns) + " want 20.0")
sys.exit(1)
if div_freq_mhz != 50.0:
print("FAIL: divided frequency got " + str(div_freq_mhz) + " want 50.0")
sys.exit(1)
if trace != [1, 0, 1, 0, 1, 0]:
print("FAIL: toggle trace got " + str(trace))
sys.exit(1)
# Rising edges of the divided clock land on every second master edge.
rising = [i for i in range(1, len(trace)) if trace[i - 1] == 0 and trace[i] == 1]
if rising != [2, 4]:
print("FAIL: divided rising edges got " + str(rising) + " want [2, 4]")
sys.exit(1)
print("PASS: divide-by-2 stretches 10.0 ns to 20.0 ns and 100 MHz to 50 MHz")The trace is the divider working: from reset the output alternates on every master edge, and the divided rising edges sit two master periods apart. That spacing is the divided period of 20.0 ns, which is 50 MHz.
A divider output can feed thousands of flops, which tempts the textbook into using it as the buffering example. Resist that: under the ideal clock model the tool assumes zero skew however many pins the net reaches, so it never buffers the net. Real clock networks are buffered in clock-tree synthesis during layout, long after logic synthesis; the buffering lessons draw their fanout examples from data nets instead.
The second run in the script is the break-it: the same clock, the same output port, but the flop input tied to a constant. The cell count moves from 2 to 0 and the flop count from 1 flop to no flop at all. Without toggling it is not a slow divider; it is no divider, and the tool says so by deleting the state.
100 MHz for the master. The divided clock doubles the period to 20.0 ns and halves the frequency to 50 MHz.
The toggle netlist holds 2 cells with 1 flop: feedback plus state, a real divider. The stuck version has no flop because constant in means constant out. Toggling is the divider.
Cell and flop counts and the netlist picture on this page come from one Yosys 0.70 run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): a divide-by-2 register and a constant-driven variant are synthesised, with write_json netlists and flop-count assertions. Period, frequency, and toggle-trace figures come from the stdlib-only Python program shown, which asserts every figure it prints, and are labelled hand-derived. The SDC listing is illustrative: the open tools have no SDC reader.