Module 5: Clock Tree Synthesis · 5.5 Clock Gating & CTS

Running CTS on a Gated Design

This closes Module 5. The UART gets one modification, a gated variant of its receive-side baud generator, and goes through CTS for real, ending the module with the exact structure the previous two articles described in the abstract.

The gated variant

The unmodified UART runs u_rx_baud continuously. This variant, uart_top_gated, adds an enable, rx_baud_en, generated by the receiver’s frame-state logic and asserted only while the receiver is mid-frame, from start bit detected to stop bit complete. While the line sits idle the enable is low and the baud counter’s clock stops.

Because Yosys does not infer clock gating cells, the ICG is instantiated directly from the SKY130 library:

// uart_top_gated.sv: receive-side baud generator, now clock-gated
logic rx_baud_en;      // asserted by u_rx only while mid-frame
logic rx_clk_gated;    // ICG output; CTS treats this as a local clock root

sky130_fd_sc_hd__dlclkp_1 u_icg_rx_baud (
    .CLK  (clk),
    .GATE (rx_baud_en),
    .GCLK (rx_clk_gated)
);

baud_tick_gen_16x #(.CLK_FREQ(CLK_FREQ), .BAUD_RATE(BAUD_RATE))
    u_rx_baud (.clk(rx_clk_gated), .rst_n(rst_n), .tick_16x(tick_16x));

rx_baud_en comes from u_rx’s own frame-state register, which is exactly the enable path the previous article flagged as needing its own setup check into the ICG latch.

Running CTS

% run_cts
[INFO]: Building RC values for the design based on the extraction rules...
[INFO]: Detected 1 integrated clock gating cell on net clk: sky130_fd_sc_hd__dlclkp_1 (u_icg_rx_baud)
[INFO]: Treating u_icg_rx_baud/GCLK as a local clock root for downstream sinks.
[INFO]: Root buffer selected: sky130_fd_sc_hd__clkbuf_16
[INFO]: Clustering 58 clock sinks across 2 domains (ungated: 51, gated: 7)...
[INFO]: Inserting buffered clock tree...
[INFO]: Clock tree synthesis complete.

The line that matters is the second. TritonCTS detects the ICG and treats its output, u_icg_rx_baud/GCLK, as a local clock root for the seven flops downstream of it, the receive baud counter’s bits. That is the first-class treatment the previous article argued for, not an afterthought bolted onto the ungated tree.

The resulting tree

uart_top_gated after run_cts: two clock roots
Figure 1: The ungated tree feeding 51 sinks through a level-2 buffer and cluster buffers, and the ICG feeding the 7 gated sinks as a root of its own. The dashed path is the enable's setup check into the ICG latch.

Two clock domains, structurally: 51 sinks fed by the ordinary ungated tree exactly as built in Section 5.3, and 7 sinks, the gated counter’s bits, fed through the ICG as their local root. The ICG sits as a genuine node in the tree, not a leaf and not an annotation. Every skew and latency number for the 7 gated sinks is computed starting from GCLK and carries the ICG’s own insertion delay, exactly as the previous article’s equation required.

Checking the new paths

The two checks the earlier articles predicted, run for real against this variant. First the enable path:

Startpoint: u_rx/frame_state_reg (enable-generating register)
Endpoint:   u_icg_rx_baud (integrated clock gating cell)
Path Group: clk
Path Type:  max

   data required time                                 19.612
   data arrival time                                    0.847
--------------------------------------------------------------
   slack (MET)                                          18.765

It passes with wide margin, unsurprising at 50 MHz with a short enable-generation path, but this is the check that would not exist at all in the ungated design, now confirmed clean rather than assumed clean. Then the cross-domain path:

Startpoint: u_rx/shift_reg[3] (ungated domain)
Endpoint:   u_rx_baud/count_reg[2] (gated domain)
Path Group: clk
Path Type:  min

   data required time                                  0.520
   data arrival time                                    0.541
--------------------------------------------------------------
   slack (MET)                                          0.021

One register in the ungated tree, one downstream of the ICG. It passes with 21 ps of hold margin, because TritonCTS’s ICG-aware latency accounting, the log line treating GCLK as a real local root, gave the check accurate numbers from the start rather than an underestimate a designer would only discover was wrong once it was expensive to fix.

Not every violation on a gated-domain boundary is this clean. These numbers land comfortably because the design and frequency were chosen so the concept lands clearly. A larger design with tighter margins and more gated domains needs this same class of check run far more times, across every boundary and every corner (Module 7), before a tree touching several gated domains can be signed off.

Practice Exercise

Suppose a second gated domain is added: the transmit baud generator, u_tx_baud, gated by a tx_baud_en from u_tx’s frame-state logic, structured identically to the receive-side gating above. List every new check this module’s articles would require that a fully ungated UART would never need.

Solution

For the new u_tx_baud domain, by direct analogy:

  1. A setup check on the tx_baud_en enable path into its own ICG latch, the enable-path check for a second ICG.
  2. Skew and hold checks on any path crossing between the u_tx_baud gated domain and the ungated rest of the design, the cross-domain check for a second boundary.
  3. If any path connects the two gated domains directly, u_tx_baud to u_rx_baud or the reverse, a third check spanning both ICGs’ accumulated insertion delay. Unlikely in this UART, since transmit and receive are largely independent, but worth ruling out explicitly rather than assuming.

None of the three exist in a design with no gating. Each is a direct consequence of an ICG entering the clock network as a first-class element.

The UART now has a complete, timing-clean, gating-aware clock tree. Module 6 picks it up exactly here and takes it through global and detailed routing, turning every remaining net, clock and data alike, into real, DRC-legal metal.

← CTS Across Gated Clock Domains