A false path is a structural path that can never carry a meaningful transition from start to end. set_false_path removes its timing check completely, so it is safe only when the RTL guarantees that. The table lists typical cases, when each is safe, and what the RTL must guarantee.
Case | Why it is false | When it is safe | What must hold in RTL |
|---|---|---|---|
Static configuration register → datapath | Only conditionally exceptable: settled before any use | Only with a checked settling contract (next section); otherwise keep it timed | Writes only while quiescent, checked settling interval before re-enable, implementation delay bound, and stability during operation; otherwise retain timing |
Raw reset port → reset synchroniser flops | Asynchronous assertion into the synchroniser; any exception is scoped to these pins only | Yes, scoped to the synchroniser's asynchronous pins only | Raw port fans out only to the synchroniser's asynchronous pins |
Synchronised reset net → design registers | Timed normally; recovery/removal are checked against the destination clock | Never: keep it timed | No false path on this net; release is synchronised (see the reset section) |
Contradictory-select complete path | A series path would require the same stable select to be both 0 and 1 | Yes, if the select is static in every analysed mode | Prove the complete path cannot be sensitised in each analyzed mode; ordinary selected mux inputs remain timed |
Test-only paths in functional mode | Scan enable is held low | Do not false-path: use case analysis | Mode pins are constants (use case analysis instead) |
# Default: retain timing on configuration paths.
# Only propose an exception after the checked settling contract below AND
# implementation max delay to actual use pins have been approved.
# Disabled draft; replace object names with reviewed, nonempty collections:
# set_false_path -from [get_cells cfg_reg*] -to [get_pins core/u_use/D]
# not-run: SDC is not executable here
# Raw reset fans out only to the reset synchroniser's asynchronous pins.
# Reason: assertion is asynchronous by design; the synchronised reset net keeps its recovery/removal checks against the destination clock.
# If constrained at all, scope the exception to the synchroniser flops; never an unscoped false path from the reset port into the whole design.
set_false_path -from [get_ports rst_n] -to [get_pins {rst_sync_ff*/RST}]
# Behaviour differs between tools on whether a false path also removes recovery/removal checks, so confirm what was actually excepted in the timing report.A configuration register is false-path material only if nothing reads it until it has settled. Illustrative same-clock contract: a configuration write at E0 is followed by a disabled E1; first use is no earlier than E2, two full 10 ns periods later. Every rewrite restarts the wait.
Assume max clock-to-Q 0.35 ns, configuration-to-use delay bounded by 12.00 ns, and setup 0.15 ns, with zero skew/uncertainty only for this arithmetic: 20.00 - 0.35 - 12.00 - 0.15 = 7.50 ns margin. The corresponding 10 ns first-use interval fails by 2.50 ns. Actual skew, uncertainty and all analysis views belong in the implementation bound. Without that evidence, keep ordinary timing.
This source checks the digital interval, catches two deliberate contract violations, and checks a complete contradictory-select path (used in the optional proof at the end of the lesson).
`timescale 1ns/1ps
module tb_exception_contracts;
logic clk = 0, wr = 0, enable = 0;
int age = 2, violations = 0;
logic s, b, c, x, mid, y;
logic y0;
assign mid = s ? x : b;
assign y = s ? c : mid;
always #5 clk = ~clk;
// age is elapsed source periods since the most recent sampled write.
// This controller example owns all writes; an external configuration
// source also needs stability/CDC checks appropriate to that interface.
always @(posedge clk) begin
if (age < 2) age++;
if (wr) age = 0;
if (enable && (wr || age < 2)) violations++;
end
task automatic step(input bit write_cfg, use_cfg);
@(negedge clk); #1; wr = write_cfg; enable = use_cfg;
@(posedge clk); #1;
endtask
initial begin
step(1, 0); // E0 write
step(0, 1); // E1 deliberate too-early use: checker must reject
if (violations != 1) $fatal(1, "missed early use");
step(0, 0);
step(1, 0); // rewrite restarts settling interval
step(0, 0);
step(0, 1); // E2 legal first use
step(0, 1); // configuration holds during operation
if (violations != 1) $fatal(1, "rejected legal settling trace");
step(1, 1); // deliberate write while active
if (violations != 2) $fatal(1, "missed active write");
step(0, 0);
// Exhaust all stable binary s,b,c values; toggle x for each.
for (int k = 0; k < 8; k++) begin
s = k[2]; b = k[1]; c = k[0]; x = 0; #1;
y0 = y;
x = 1; #1;
if (y !== y0 || y !== (s ? c : b))
$fatal(1, "contradictory-select dependency check failed");
end
$display("PASS: directed settling checks and 8 stable-select cases");
$finish;
end
initial begin #1000; $fatal(1, "watchdog"); end
endmoduleCase analysis is the right tool when a pin is constant in a mode. It changes the logic that STA sees instead of removing a check. set_case_analysis 0 [get_ports test_mode] states that test_mode is constantly 0. The tool propagates the constant, so the mux passes only the functional input.
Mode | Case analysis | What is timed |
|---|---|---|
Functional |
| Functional paths only |
Scan shift |
| Shift paths at the test clock |
Scan capture |
| Capture paths |
Some false paths come from logic structure rather than modes. Two muxes in series that share one select are the classic case.
Follow the highlighted wire from x to y and read the condition at each mux.
At the first mux, x reaches mid only when s = 1.
At the second mux, mid reaches y only when s = 0.
One stable select cannot be both, so the complete path x -> mid -> y is false. Paths from b and c remain real and must be timed.
In the checker, mid = s ? x : b and y = s ? c : mid. Substitution gives y = s ? c : b, independent of x. This Boolean derivation covers the complete path under the stable-select assumption; the eight-case finite check is supporting source, not an executed formal or STA result. Mutually exclusive alternatives in one mux are not themselves false paths.
Name the RTL reason in a comment next to the constraint.
Prove it: assertion, formal check or review sign-off.
Confirm it does not cross clock domains without a synchroniser.
Use the narrowest -from/-to that works.
Revisit it whenever the RTL around it changes.
rst_n acceptable, and why is set_false_path -from [get_clocks A] -to [get_clocks B] usually not?A false path from the raw reset port is acceptable only when scoped narrowly to the reset synchroniser's asynchronous pins, while the synchronised reset net keeps its recovery/removal checks against the destination clock; an unscoped false path from the reset port would remove those checks across the whole design. A clock-to-clock false path covers every signal between the domains, including data that needs a synchroniser, so it hides CDC bugs. Behaviour differs between tools on whether a false path also removes recovery/removal checks, so confirm what was actually excepted in the timing report.
Settling-contract bench, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It checks the digital interval, catches two deliberate contract violations and checks a complete contradictory-select path. It does not measure configuration propagation delay or prove a software contract for all traces.
The contradictory-select result is a Boolean derivation. The eight-case finite check supports it and is not an executed formal or STA result. The SDC shown is not run.