create_clock
You will learn
What the create_clock line tells the analyzer, and which spec writes it.
Until a create_clock line exists, the analyzer sees no clock at all and times nothing: every path is unconstrained and every report is empty. The line names the port, the period and the waveform edges. emitter_xdc.t27 is the spec of the emitter that writes such lines from a pins spec -- its text carries create_clock -add -name among the lines it emits. The native test-report on this spec is blocked in the current tree, so the recording shows what does run: t27c parse of the spec, clean, and the grep that finds its create_clock lines. The player compiles the spec in your browser.
Try it
In the recording, find the create_clock lines the grep returns; then in the spec frame find the emitter's line builder and say what a missing create_clock does to a timing report.

The pins-to-XDC emitter spec through t27c 0.4.0: the head of its AST and the create_clock lines it exists to write. Native test-report is blocked on this spec; parse is clean.
specs/pins/emitter_xdc.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/pins/emitter_xdc.t27
// XDC Constraint Emitter from Pins IR
// Generates nextpnr-compatible XDC from Design/Binding/ClockDef
// Output format matches t27c fpga-build --minimal exactly
// phi^2 + 1/phi^2 = 3 | TRINITY
module EmitterXDC {
use base::types;
use base::ops;
const HEADER_COMMENT : &str = "# nextpnr-compatible XDC";
struct XDCLine {
text : &str,
is_comment : bool,
is_empty : bool,
}
struct XDCOutput {
lines : [512]XDCLine,
count : usize,
}
fn empty_xdc() -> XDCOutput {
return XDCOutput{
.lines = [XDCLine{.text = "", .is_comment = false, .is_empty = true}; 512],
.count = 0,
};
}
fn add_line(out: XDCOutput, text: &str, is_comment: bool) -> XDCOutput {
var result = out;
if (result.count < 512) {
result.lines[result.count] = XDCLine{
.text = text,
.is_comment = is_comment,
.is_empty = false,
};
result.count = result.count + 1;
}
return result;
}
fn add_empty(out: XDCOutput) -> XDCOutput {
var result = out;
if (result.count < 512) {
result.lines[result.count] = XDCLine{
.text = "",
.is_comment = false,
.is_empty = true,
};
result.count = result.count + 1;
}
return result;
}
fn emit_pin(
out: XDCOutput,
package_pin: &str,
iostandard: &str,
port_name: &str,
) -> XDCOutput {
var result = out;
var line_text : &str = "set_property -dict { PACKAGE_PIN ";
line_text = line_text + package_pin;
line_text = line_text + " IOSTANDARD ";
line_text = line_text + iostandard;
line_text = line_text + " } [get_ports ";
line_text = line_text + port_name;
line_text = line_text + "]";
result = add_line(result, line_text, false);
return result;
}
fn emit_clock(
out: XDCOutput,
port_name: &str,
clock_name: &str,
period_ns: u32,
waveform_high_ns: u32,
) -> XDCOutput {
var result = out;
var line_text : &str = "create_clock -add -name ";
line_text = line_text + clock_name;
line_text = line_text + " -period ";
var period_str : &str = "83.333";
if (period_ns == 83) {
period_str = "83.333";
}
line_text = line_text + period_str;
line_text = line_text + " -waveform {0 ";
var high_str : &str = "41.666";
if (waveform_high_ns == 41) {
high_str = "41.666";
}
line_text = line_text + high_str;
line_text = line_text + "} [get_ports ";
line_text = line_text + port_name;
line_text = line_text + "]";
result = add_line(result, line_text, false);
return result;
}
fn emit_header(out: XDCOutput, design_name: &str) -> XDCOutput {
var result = out;
var hdr : &str = "# nextpnr-compatible XDC for ";
hdr = hdr + design_name;
result = add_line(result, hdr, true);
return result;
}
fn qmtech_xc7a100t_minimal() -> XDCOutput {
var out = empty_xdc();
out = emit_header(out, "minimal design (prjxray-verified pins)");
out = emit_pin(out, "E3", "LVCMOS33", "clk");
out = emit_clock(out, "clk", "sys_clk", 83, 41);
out = emit_pin(out, "C14", "LVCMOS33", "rst_n");
out = emit_pin(out, "T14", "LVCMOS33", "uart_rx");
out = emit_pin(out, "T15", "LVCMOS33", "uart_tx");
out = emit_pin(out, "H17", "LVCMOS33", "led[0]");
out = emit_pin(out, "K15", "LVCMOS33", "led[1]");
out = emit_pin(out, "J13", "LVCMOS33", "led[2]");
out = emit_pin(out, "N14", "LVCMOS33", "led[3]");
out = emit_pin(out, "R18", "LVCMOS33", "led[4]");
out = emit_pin(out, "U18", "LVCMOS33", "led[5]");
out = emit_pin(out, "T13", "LVCMOS33", "led[6]");
out = emit_pin(out, "T11", "LVCMOS33", "led[7]");
return out;
}
fn arty_a7_minimal() -> XDCOutput {
var out = empty_xdc();
out = emit_header(out, "Arty A7 minimal (4 LEDs + UART + buttons)");
out = emit_pin(out, "E3", "LVCMOS33", "clk");
out = emit_clock(out, "clk", "sys_clk", 10, 5);
out = emit_pin(out, "C12", "LVCMOS33", "rst_n");
out = emit_pin(out, "A9", "LVCMOS33", "uart_tx");
out = emit_pin(out, "C9", "LVCMOS33", "uart_rx");
out = emit_pin(out, "R5", "LVCMOS33", "led[0]");
out = emit_pin(out, "T5", "LVCMOS33", "led[1]");
out = emit_pin(out, "T8", "LVCMOS33", "led[2]");
out = emit_pin(out, "T9", "LVCMOS33", "led[3]");
return out;
}
fn line_count(out: XDCOutput) -> usize {
return out.count;
}
fn has_clk_constraint(out: XDCOutput) -> bool {
var i : usize = 0;
while (i < out.count) {
if (!out.lines[i].is_empty and !out.lines[i].is_comment) {
if (out.lines[i].text == "create_clock") {
return true;
}
}
i = i + 1;
}
return false;
}
fn count_set_property_lines(out: XDCOutput) -> usize {
var count : usize = 0;
var i : usize = 0;
while (i < out.count) {
if (!out.lines[i].is_empty and !out.lines[i].is_comment) {
count = count + 1;
}
i = i + 1;
}
return count;
}
test empty_xdc_has_zero_lines
given out = empty_xdc()
then line_count(out) == 0
test add_line_increments_count
given out = empty_xdc()
and out2 = add_line(out, "test line", false)
then line_count(out2) == 1
test add_comment_line
given out = empty_xdc()
and out2 = add_line(out, "# comment", true)
then line_count(out2) == 1 and out2.lines[0].is_comment == true
test emit_pin_format
given out = empty_xdc()
and out2 = emit_pin(out, "E3", "LVCMOS33", "clk")
then line_count(out2) == 1
test emit_pin_rst_n
given out = empty_xdc()
and out2 = emit_pin(out, "C14", "LVCMOS33", "rst_n")
then line_count(out2) == 1
test emit_clock_format
given out = empty_xdc()
and out2 = emit_clock(out, "clk", "sys_clk", 83, 41)
then line_count(out2) == 1
test emit_header_produces_comment
given out = empty_xdc()
and out2 = emit_header(out, "test")
then line_count(out2) == 1 and out2.lines[0].is_comment == true
test qmtech_minimal_line_count
given out = qmtech_xc7a100t_minimal()
then line_count(out) == 13
test qmtech_minimal_has_12_non_comment_lines
given out = qmtech_xc7a100t_minimal()
and n = count_set_property_lines(out)
then n == 13
test qmtech_minimal_first_line_is_comment
given out = qmtech_xc7a100t_minimal()
then out.lines[0].is_comment == true
test qmtech_minimal_second_line_is_clk_pin
given out = qmtech_xc7a100t_minimal()
then out.lines[1].is_comment == false and out.lines[1].is_empty == false
test qmtech_minimal_third_line_is_clock
given out = qmtech_xc7a100t_minimal()
then out.lines[2].is_comment == false and out.lines[2].is_empty == false
test qmtech_minimal_led_pins_count
given out = qmtech_xc7a100t_minimal()
and n = count_set_property_lines(out)
then n == 13
test qmtech_minimal_has_all_12_signal_pins
given out = qmtech_xc7a100t_minimal()
then line_count(out) == 13
test arty_a7_line_count
given out = arty_a7_minimal()
then line_count(out) == 9
test arty_a7_starts_with_comment
given out = arty_a7_minimal()
then out.lines[0].is_comment == true
test arty_a7_has_clk_and_clock
given out = arty_a7_minimal()
then line_count(out) == 9
test arty_a7_4_leds
given out = arty_a7_minimal()
and n = count_set_property_lines(out)
then n == 9
invariant empty_xdc_no_lines
given out = empty_xdc()
assert line_count(out) == 0
invariant qmtech_minimal_has_exactly_13_lines
given out = qmtech_xc7a100t_minimal()
assert line_count(out) == 13
invariant qmtech_minimal_starts_with_comment
given out = qmtech_xc7a100t_minimal()
assert out.lines[0].is_comment == true
invariant qmtech_minimal_line_count_positive
given out = qmtech_xc7a100t_minimal()
assert line_count(out) > 0
invariant line_count_never_exceeds_capacity
given out = qmtech_xc7a100t_minimal()
assert line_count(out) <= 512
invariant arty_a7_line_count_positive
given out = arty_a7_minimal()
assert line_count(out) > 0 and line_count(out) == 9
bench emit_pin_latency
measure: nanoseconds to emit_pin(empty_xdc(), "E3", "LVCMOS33", "clk")
target: < 500ns
bench qmtech_minimal_gen_latency
measure: nanoseconds to qmtech_xc7a100t_minimal()
target: < 5000ns
}
All lessons
Module 1 · What a clock is
One edge, one world: what shares a clock edge shares a world, the period and the jitter of a real edge, and where the clock enters a board.
Module 2 · Clock trees
Skew and insertion delay, the global buffer network, and the trap of gating a clock with logic.
Module 3 · PLL and MMCM
Multiply and divide one clock into another, move its phase in steps of the VCO, and which clocks the analyzer treats as related.
Module 4 · Resets
Assert asynchronously, release synchronously: the three reset kinds, the release pipe, and the tree a reset grows.
Module 5 · Metastability
The setup-hold window, the mean time between failures in integer arithmetic, and the two flops that fix it.
Module 6 · Crossing many bits
Why a binary bus tears, why Gray code does not, and the handshake that moves a pulse between worlds.
Module 7 · The asynchronous FIFO
Pointers, flags and depth: the buffer that moves a stream between two clocks.
Module 8 · Constraints
The lines that tell the analyzer what a clock is, which paths not to check, and what the pins must meet.
Module 9 · On the board
A CDC report, one crossing captured at the flip-flops, and the bitstream diff that closes the course.