Reading a Post-CTS Timing Report
Section 5.3’s CTS run on the UART reported an achieved worst-case skew of 0.111 ns and a status of MET against its own target. The previous article explained why that success does not guarantee every path is safe. Time to rerun timing on the post-CTS UART and see.
Running the check
With the clock now a real, propagated network rather than an ideal assumption:
% report_checks -path_delay min_max -fields {slew cap input_pins}
min_max asks for both the setup (max) and hold (min) checks in one pass, exactly the two the previous article’s equations covered.
The setup report: comfortable, as expected
Startpoint: u_rx/shift_reg[7] (rising edge-triggered flip-flop clocked by clk)
Endpoint: u_rx/rx_data_out_reg[7] (rising edge-triggered flip-flop clocked by clk)
Path Group: clk
Path Type: max
Delay Time Description
--------------------------------------------------------------
0.000 0.000 clock clk (rise edge)
0.298 0.298 clock network delay (propagated)
0.000 0.298 u_rx/shift_reg[7]/CLK (sky130_fd_sc_hd__dfxtp_2)
0.083 0.381 u_rx/shift_reg[7]/Q
0.412 0.793 u_rx/rx_data_out_reg[7]/D (data arrival time)
--------------------------------------------------------------
20.000 20.000 clock clk (rise edge)
0.409 20.409 clock network delay (propagated)
0.000 20.409 u_rx/rx_data_out_reg[7]/CLK
-0.061 20.348 library setup time
data required time 20.348
--------------------------------------------------------------
data required time 20.348
data arrival time 0.793
--------------------------------------------------------------
slack (MET) 19.555
The UART runs at 50 MHz, the same CLK_FREQ its RTL was written with, giving a 20 ns period that is wide open relative to this design’s short paths. Nineteen and a half nanoseconds of setup slack is not a close call. This path was never in danger, and setup at 50 MHz essentially never will be for a design this size. That is expected, and worth confirming rather than assuming, because a healthy setup margin is exactly what lets you concentrate on hold for the rest of this article. Notice too that the clock network delay line now says propagated with a real number, where Module 4’s report said ideal with 0.00.
The hold report: the violation
Startpoint: u_rx_baud/count_reg[0] (rising edge-triggered flip-flop clocked by clk)
Endpoint: u_rx_baud/count_reg[1] (rising edge-triggered flip-flop clocked by clk)
Path Group: clk
Path Type: min
Delay Time Description
--------------------------------------------------------------
0.000 0.000 clock clk (rise edge)
0.298 0.298 clock network delay (propagated)
0.000 0.298 u_rx_baud/count_reg[0]/CLK (sky130_fd_sc_hd__dfxtp_2)
0.095 0.393 u_rx_baud/count_reg[0]/Q
0.070 0.463 u_rx_baud/count_reg[1]/D (data arrival time)
--------------------------------------------------------------
0.000 0.000 clock clk (rise edge)
0.409 0.409 clock network delay (propagated)
0.000 0.409 u_rx_baud/count_reg[1]/CLK
0.060 0.469 library hold time
data required time 0.469
--------------------------------------------------------------
data required time 0.469
data arrival time 0.463
--------------------------------------------------------------
slack (VIOLATED) -0.006
Read it the way the previous article set you up to. u_rx_baud/count_reg[0] and count_reg[1] are two adjacent bits of the receive-side baud counter, exactly the short-path, minimal-logic shape flagged as the classic hold offender. The launching register’s clock network delay is 0.298 ns; the capturing register’s is 0.409 ns. The difference is 0.111 ns, precisely Section 5.3’s achieved worst-case skew, landing on exactly the pair it was always going to land hardest on. Data arrives at 0.463 ns. The hold check needs the old value steady until 0.469 ns. It falls short by six picoseconds. A small violation, but a violation.
This is the clearest illustration in the module of the previous article’s point. TritonCTS met its own skew target, and still handed the design a real hold violation, because the specific pair of registers that skew landed on had almost no combinational cushion to absorb it. A skew number meeting spec design-wide says nothing about whether it is safe for every pair. Only a real report_checks -path_delay min run on the post-CTS design tells you that.
Against the pre-CTS picture
Under Module 4’s ideal clock model, with both latencies at zero, this same pair would show hold slack of 0.095 + 0.070 - 0.060 = 0.105 ns, comfortably positive. Nothing about the RTL or the cells changed between that model and this report. Only the clock model did, exactly as predicted.
| Stage | Clock model | Hold slack on this pair |
|---|---|---|
| Pre-CTS | Ideal, zero latency | +0.105 ns |
| Post-CTS | Propagated, 0.298 ns / 0.409 ns | -0.006 ns (VIOLATED) |
So a CTS status of MET is not a reason to skip straight to routing. MET describes CTS’s own skew objective. A post-CTS min_max run like this one is the step that tells you whether the tree you just built is safe to route on top of.
Practice Exercise
Using only the clock network delay values in the hold report, compute the skew between count_reg[0] and count_reg[1] and confirm it matches Section 5.3’s reported achieved worst-case skew.
Solution
$$0.409,\text{ns} - 0.298,\text{ns} = 0.111,\text{ns}$$
That matches the CTS summary’s “Achieved worst-case skew: 0.111 ns” exactly. This register pair is the one defining the design’s worst-case skew, and it is also the pair now failing hold. The two reports are the same physical fact seen from two angles.
You have found the violation and understand why it exists. The next article fixes it, with a targeted correction to this one path and a rerun report confirming that hold is restored without breaking anything else.