Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
    • STA Foundations
    • Setup, Hold and Slack
    • Skew and Variation
    • Timing Exceptions
    • Clock Gating
    • RTL for Timing
    • Clock Architecture
      • Clock-Domain Plan
      • Glitch-Free Clock Mux
      • Generated and Virtual Clocks
      • PLL Lock and Reset Coordination
      • Source-Synchronous Interfaces
      • DVFS Transition Protocol
      • Clock Constraint Examples
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Clock Architecture
  1. Topics
  2. RTL Design
  3. Timing & Clocking
  4. Clock Architecture
  5. Glitch-Free Clock Mux

Part 5 · Timing & Clocking · Intermediate

Glitch-Free Clock Mux

Use an integrated clock-mux cell and a handshake that switches only when sources and consumers are safe.

On this page

    Why this matters

    A combinational clock mux cuts the clk_a pulse off after about 2 ns when sel falls, a runt where a full pulse is two slots wide. A real switch needs a dedicated mux cell and a handshake that waits for the new clock to be active.

    After this lesson you can: explain why a combinational clock mux fails, state the requirements for a safe switch, and describe the request, acknowledge and timeout protocol around the mux cell.

    Why a plain mux fails

    A combinational assign clk = sel ? clk_a : clk_b is unsafe. If select changes while source levels differ, the output can create a runt pulse or an extra active edge.

    Read the picture from the sel fall at 27 ns, drawn in slot 5. clk_a has a 10 ns period and clk_b a 20 ns period; each slot is 2.5 ns, and sel picks clk_a while high and clk_b while low.

    • clk_bad follows clk_b the moment sel falls. clk_b is low, so the clk_a pulse that began at 25 ns is cut off after about 2 ns: a runt, one slot wide where a full pulse is two.

    • clk_good lets the clk_a pulse finish (it is two slots wide), then stays low for 5 ns while neither clock is selected.

    • clk_good starts the first clk_b pulse only at the clk_b rising edge, so every pulse it delivers is full width.

    • A zero-delay simulation like this one only shows the logic; real cells add delay, so the approved cell and its timing checks stay the evidence.

    design.sv
    // bad-example: never mux clocks as ordinary combinational data
    assign clk_bad = select_i ? clk_a_i : clk_b_i;

    The latched, mutually exclusive mux

    clk_good in the waveform comes from the circuit below. Each source has its own enable, and each enable is held by a latch that is open only while its own clock is low.

    The glitch-free mux used for clk_good: each source clock is ANDed with a latched enable, and each enable waits for the other to turn off

    Read each row from left to right: the select request, the latch, then the AND with that row's own clock.

    • A latch can only change while its clock is low, so an enable never cuts a high pulse in half. That is why clk_good finishes the clk_a pulse in the waveform.

    • Each row also needs the other enable off. B cannot start until en_a has fallen, so both clocks are never passed together.

    • Between the two, clk_o simply stays low: the 5 ns gap in the waveform.

    • If the old clock stops, its latch never opens and the switch never completes. That is why the controller below needs a timeout.

    Warning

    RTL simulation with ideal zero-delay clocks may make an unsafe mux look clean. The architecture must use an approved clock cell and switching protocol regardless of a pretty waveform.

    The switching protocol

    In the worked tree, firmware cannot drive the mux directly. A clock controller drains core traffic, requests PLL-to-debug switching, waits for the approved mux wrapper to acknowledge the active source, and only then releases the core.

    1. Block new work and drain the domain.

    2. Request the new source through the clock controller.

    3. Wait for glitch-free mux acknowledgement and source stability.

    4. Re-establish generated-clock/PLL status as required.

    5. Release reset or resume traffic under the new clock.

    Requirement

    Reason

    Dedicated mux cell

    Implements mutually exclusive, low-phase source enables

    Stable select protocol

    Select does not chatter during transfer

    Both sources running or documented stop behavior

    Cell can observe safe phases

    Domain quiescent

    No transaction depends on exact switching latency

    Switch acknowledgement

    Controller knows new clock is active

    Reset/CDC review

    Control crosses safely into source domains

    Check yourself Why quiesce the domain if the mux itself is glitch-free?

    The mux protects pulse shape, not transaction latency or timers. A clock pause or stretched interval during switching can violate active protocol assumptions.

    Controller contract (advanced)

    This section is an integration draft for the clock controller and the approved mux wrapper. Read it when you own that controller; the rule above is enough for most RTL work.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // snippet: interface draft, not generic mux RTL. Status is synchronous to aon_clk_i.
    // The wrapper implements the approved cell's CDC and completion protocol.
    clock_mux_wrapper u_clk_mux (
      .ctrl_clk_i(aon_clk_i),
      .ctrl_rst_ni(aon_rst_ni),
      .clk_a_i,
      .clk_b_i,
      .req_i(switch_req),
      .target_b_i(target_b),
      .busy_o(switch_busy),
      .done_o(switch_done),
      .active_valid_o(active_valid),
      .active_b_o(active_b),
      .fault_o(switch_fault),
      .test_enable_i,
      .clk_o(domain_clk)
    );

    AON controller/wrapper contract

    Required behavior

    Accept

    req && !busy at an always-on (AON) edge; controller holds target and request until done or fault; only one outstanding request; wrapper rearms only after observing req low

    Status

    busy from acceptance through completion; done is one AON-cycle pulse; active_b meaningful only with active_valid; controller drops req on completion before another acceptance

    Completion

    done implies active_valid, active_b == requested target, old source disabled, and target edges observed by the approved status mechanism; echoed select is insufficient

    Failure

    fault sticky until explicit recovery; no done on failure; active_valid clears if active source is no longer known healthy

    Reset/test

    Initial source/status and test override behavior belong to the cell contract; no functional requests during test override

    The AON controller owns separate bounded drain and switch timers. Illustrative limits are 32 and 64 AON cycles, chosen here only to specify a finite contract; hardware limits must cover approved cell latency, source frequencies and synchronizer delays. On either timeout or wrapper fault, latch an error and keep work blocked and the domain isolated/reset. A stopped source may prevent completion indefinitely, including when stopped high. Timeout does not force a mux select or make a stopped-high clock safe; fallback requires a documented cell recovery protocol.

    Cycle

    Event

    Signals

    C0

    Block new work; start 32-cycle drain watchdog

    -

    C3

    Quiescent; assert req, target_b=1; start switch watchdog

    req=1, target_b=1

    C4

    Wrapper accepts; hold target_b=1

    busy=1

    C9

    Completion; stop switch watchdog

    done=1, active_valid=1, active_b=1

    C10+

    Qualify clock/reset release, then resume (never on done alone)

    -

    Stopped-source branch

    No done through 64 switch cycles: sticky timeout; keep isolated/reset; a late done cannot authorize resume until explicit recovery

    no done

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Controller-side checker draft. Bind to AON-synchronous status only.
    // pending/age are controller state; age=0 at acceptance, then saturates.
    assert property (@(posedge aon_clk_i) disable iff (!aon_rst_ni)
      switch_pending && !switch_done && !switch_fault |=> $stable(target_b))
      else $fatal(1, "target changed during switch");
    assert property (@(posedge aon_clk_i) disable iff (!aon_rst_ni)
      switch_done |-> switch_pending && active_valid && (active_b == target_b))
      else $fatal(1, "completion lacks matching active source");
    assert property (@(posedge aon_clk_i) disable iff (!aon_rst_ni)
      switch_pending && (switch_age == 63) && !switch_done |=> timeout_q)
      else $fatal(1, "switch watchdog failed");
    assert property (@(posedge aon_clk_i) disable iff (!aon_rst_ni)
      timeout_q || switch_fault |-> block_work && isolate_domain && !resume)
      else $fatal(1, "clock fault escaped containment");

    Warning

    Limits: this interface and assertion fragment are a draft, not an implementation. The wrapper owner must supply real status synchronization and edge monitoring. Verification must exercise both switching directions, no-op requests, reset/test, stopped-low and stopped-high sources, timeout/late acknowledgement and recovery. Cell timing, minimum pulse widths, CDC/RDC and physical clock signoff remain separate requirements.

    Remember

    • A glitch-free mux uses mutually exclusive, low-phase source enables and a stable select.

    • The controller drains the domain, requests the switch, waits for an acknowledgement that the new source is active, then resumes.

    • Use separate bounded timers. A timeout keeps work blocked and the domain isolated or in reset; it does not make a stopped-high clock safe.

    Rule of thumb: never mux clocks as ordinary data, and never trust a clean zero-delay waveform as proof.

    How this was checked

    The waveform is a zero-delay simulation: it shows the logic, not real cell delay.

    The interface contract and assertion fragment are an unverified integration draft, not an implementation or a pulse-shape proof. Verification still has to cover both switching directions, no-op requests, reset and test, stopped-low and stopped-high sources, timeout and late acknowledgement, and recovery. Cell timing, minimum pulse widths, CDC and RDC, and physical clock signoff are separate.

    The 32 and 64 AON-cycle limits are illustrative values chosen to specify a finite contract.

    Keep going

    • Code lab: Clock mux model

    • Interview: run a mock interview on this topic

    Related topics

    • Generated and virtual clocks

    • Clock gating

    Previous

    149 / 350

    Next: Generated and Virtual Clocks

    Up next

    Up nextGenerated and Virtual Clocks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • PLL Lock and Reset Coordination
    • Source-Synchronous Interfaces
    • DVFS Transition Protocol
    clk_aclk_bselclk_badclk_good
    Simulation of the combinational mux (clk_bad) against a latched, mutually exclusive-enable mux (clk_good) when sel falls while clk_a is high and clk_b is low