Skip to content

Clocking and Clock Domain Crossing

sy2002 edited this page Jul 7, 2026 · 1 revision

Two things quietly sink more first-time ports than any Verilog error: getting the clocks exactly right, and moving signals safely between clock domains. Both are easy to get "working in simulation" and then watch fail, rarely and unreproducibly, on real hardware. This article takes a VHDL developer who is not a clocking expert through both, from first principles, in MEGA65/MiSTer2MEGA65 (M2M) terms.

It is a concepts article. It teaches you why things are the way they are and what the pieces do, so that the step-by-step porting instructions make sense. Those instructions — the exact clk.vhd you write, the MMCM arithmetic, the XDC constraints — live in The Ultimate MiSTer2MEGA65 Porting Guide (Phase 4 for clocks; Part III §3.B for clocking patterns and §3.F for CDC and constraints), and this article points you there at each step. The MiSTer-side overview of how a core clocks itself is in the MiSTer Quick Reference Guide.

If you take one sentence away: run everything you can on a single clock and use clock enables for the slower rates; wherever two real clocks must meet, never let a signal cross between them without a proper synchronizer. The rest of this page unpacks that sentence.


Part 1 — Clocking

1.1 The two frequencies you always care about

A retro machine has, in practice, two clocks that matter to you:

  • The system (core) clock — the master rhythm the CPU and chips run on. The original hardware derived the CPU clock from a crystal, usually by division; the FPGA core does the same.
  • The pixel clock (also called the dot clock) — the rate at which the video chip emits one pixel. Everything about the picture (resolution, refresh, sync timing) is counted in pixel clocks.

These two are almost always related — the pixel clock is typically a simple multiple or divisor of the system clock — which is exactly why understanding one helps you understand the other. If the idea of a pixel/dot clock is new, two short primers are worth ten minutes: video timing in Verilog and the XFree86 Video Timings HOWTO.

1.2 The single most useful idea: the clock enable

Here is the mental shift that makes FPGA clocking click. When a subsystem needs to run slower than the main clock, the beginner's instinct is to make a slower clock (divide it down and clock the logic with the result). On an FPGA this is almost always the wrong move:

  • Real clocks are a scarce, special resource — they ride dedicated low-skew "global buffer" networks, and there are only a handful of them.
  • A clock you make by dividing in fabric ("gated" or "ripple" clock) travels on ordinary routing, arrives skewed, and turns every boundary between it and the main clock into a hidden clock-domain crossing (Part 2). It also confuses timing analysis.

The right move is the clock enable (CE): keep everything on one fast clock, and generate a signal that is high for exactly one cycle at the slower rate. Logic that should advance at the slow rate is written as "on the fast clock, but only when the enable is high."

-- A pixel clock enable that is 1/4 of the main clock:
signal ce : std_logic_vector(3 downto 0) := "0001";
process (clk_main) begin
  if rising_edge(clk_main) then
    ce <= ce(0) & ce(3 downto 1);   -- rotate a single '1' around
  end if;
end process;
pixel_ce <= ce(0);                  -- high once every four clocks

That one-hot shift register (or a counter that wraps) is the whole trick. If pixel-clock enables are unfamiliar, this clock-enable primer is a good start.

This is not an obscure optimization — it is how M2M feeds video to the framework. Your core outputs video_ce_o, a clock enable at the native pixel rate (and video_ce_ovl_o, the post-scandoubler rate for the on-screen menu overlay), rather than a separate pixel clock. Internalize the CE now and half of the clocking work disappears.

1.3 How MiSTer clocks, and how you reproduce it on the MEGA65

On MiSTer, the framework hands the core a single 50 MHz reference (CLK_50M). The core instantiates a PLL (always named pll) that produces its clk_sys and any other clocks it needs. Video is emitted on CLK_VIDEO — which usually equals clk_sys — with CE_PIXEL picking out the pixel instants. (See the MiSTer Quick Reference Guide for the MiSTer side in full.)

On the MEGA65, the split of responsibilities is the same shape, with different numbers:

  • The board gives you a 100 MHz clock (not 50).
  • The framework owns the QNICE 50 MHz clock, the HyperRAM 100 MHz clock (plus its delayed and reference variants), the 12.288 MHz audio clock, and the reconfigurable HDMI pixel/TMDS clocks. You never generate these.
  • You own your core clock. You write CORE/vhdl/clk.vhd, which turns the 100 MHz board clock into your main_clk with a Xilinx MMCME2_ADV and loops it back to the framework. You also tell the framework the exact frequency you achieved, as CORE_CLK_SPEED in globals.vhd.
flowchart TD
    B["MEGA65 board clock — 100 MHz"]
    B --> C["your clk.vhd (MMCM)"]
    B --> F["framework — clk_m2m"]
    C --> M["main_* — your core clock"]
    C --> V["video_* — video output (often the same clock as main_*)"]
    F --> Q["qnice_* — 50 MHz Shell CPU"]
    F --> H["hr_* — HyperRAM 100 MHz"]
    F --> A["audio_* — 12.288 MHz"]
Loading

The boxes above are the five clock domains you will meet. You produce the two on the left (main_* and, usually the same clock, video_*); the framework produces the three on the right. Every arrow that would cross between two boxes is a clock-domain crossing — the subject of Part 2.

1.4 Hit the frequency to the Hertz — and where to find the number

Why exactness matters. A core clock that is off by a fraction of a percent will look like it runs, then betray you: audio drifts in pitch, video refresh sits slightly wrong, CIA and VIA timers miscount, tape and fast-loaders that depend on precise cycle counts fail, and a disk drive emulated at the wrong ratio to the CPU corrupts data. So the goal is to reproduce the original frequency as closely as the FPGA's clock hardware allows.

Where the number lives. Read the MiSTer core's rtl/pll/pll_*.v file — the Altera PLL configuration states the exact output frequencies. Cross-check by seeing how the clock is used: often the machine's real CPU frequency is a clean division of it.

How close is close enough. Not zero — the original crystal's tolerance. A consumer crystal is ±50 ppm or worse, so any MMCM solution within single-digit ppm is already better than the real machine ever was. What you must not wave through is error in the 0.1% range and up. (The MMCM cannot hit every frequency exactly; you search its legal divider settings for the closest one — the arithmetic is in the porting guide, §2.4.)

Worked example — the Commodore 64 (PAL). MiSTer's C64 runs clk_sys at about 31.527954 MHz. Why that odd number? The PAL C64 CPU runs at 985248 Hz, and 31.527954 / 32 = 985248. The video base clock clk64 is exactly 2 × clk_sys, and the pixel rate is clk_sys / 4. On the MEGA65 side, clk.vhd asks its MMCM for the same target and achieves 31.527778 MHz (a few ppm off — far inside crystal tolerance), and main.vhd produces the pixel clock as a divide-by-four clock enable exactly like §1.2. The core clock and pixel clock stay locked in the 4:1 ratio the VIC-II expects.

1.5 Reading a core's clock plan: the detective method

Whenever you meet a new core, spend fifteen minutes answering: how many clocks are there, what frequency is each, which parts use which, and is any of it switched at runtime? Open the emu module, find the pll instantiation, then search for where each clock name is used. Some worked vignettes — all drawn from real MiSTer cores — show the kinds of things you find:

  • Commodore 64. Three PLL outputs: clk_sys (the machine, §1.4), clk64 (= 2× clk_sys, drives video and SDRAM), and clk48 — which, when you chase it, is used only by the optional Yamaha OPL sound expander. Lesson: tracing a clock to its single use often tells you a whole feature is optional and can wait.

  • Apple II. Two clocks: a video clock and a 14.318 MHz core clock — but the machine module also wants the raw 50 MHz. Is that a real requirement or a MiSTer-framework artifact you can drop? Following it into rtl/ssc/super_serial_card.v shows a /27 divider making a ~1.85 MHz clock for a 6551 ACIA (the serial chip). So the 50 MHz is genuinely needed by the emulated hardware — you must provide an equivalent, not assume it away. Lesson: don't guess whether a clock is real — read where it is used.

  • Arkanoid. One 48 MHz clock, plus a runtime PLL reconfiguration that applies a ~1.4% overclock to bring the game's refresh into spec for a 60 Hz display (an anti-flicker trick). Lesson: for first bring-up, pick the plain configuration and ignore the reconfiguration; add bells and whistles later.

  • Game Boy. A RAM clock and a 33.554432 MHz clk_sys; the CPU runs at clk_sys / 8 = 4.194304 MHz, delivered as ce_cpu/ce_cpu2x clock enables. Lesson: a well-written modern core already hands you its slow rates as enables — reuse them.

Best practices distilled from those:

  • Always confirm how a clock is used before you reproduce it; you will discover features and quirks (and sometimes that a clock is unnecessary).
  • Clocking is a rabbit hole. For your first vital sign, pick one video standard (say PAL), ignore turbo modes and optional peripherals, and get something on screen.
  • A rate that is an integer or fractional division of the core clock is a clock enable, not a second clock (§1.2). Reserve a genuine second MMCM output for a frequency that truly cannot be an enable (e.g. an unrelated memory PHY).

1.6 Runtime clock switching (PAL/NTSC) and the M2M answer

Some cores retune their PLL at runtime — the C64 nudges its clock between PAL and NTSC; arcade cores overclock for refresh. On the Intel/Altera side this is a PLL-reconfiguration port. On the MEGA65 the recommended architecture avoids retuning the core clock at runtime (the HDMI clock in particular must stay put), and replaces it with either of two techniques:

  1. Two fixed clock plans. Generate both frequencies statically (two MMCM parameter sets) and select between them with a glitch-free BUFGMUX_CTRL. The C64 uses exactly this for its flicker-free pair. (Mechanics: porting guide §3.B.2.)
  2. Pass the exact frequency to the logic and derive rates with accumulators. Anything that is derived from the core clock (a UART baud rate, a drive clock enable, a time-of-day tick) should not use a hard-coded divisor. Instead, feed the exact core frequency in Hz into the core (clk_main_speed_i) and generate the enable with a fractional accumulator:
-- a clock enable at target_hz Hz (target_hz pulses per second), drift-free, self-adjusting
if rising_edge(clk_main) then
  ce <= '0';
  sum := sum + target_hz;              -- e.g. 16_000_000
  if sum >= clk_main_speed then        -- exact core clock in Hz
    sum := sum - clk_main_speed;
    ce  <= '1';
  end if;
end if;

This is drift-free (the remainder is never lost), costs one adder and one comparator, and — crucially — stays correct when the core clock changes later (flicker-free slows the whole system by ~0.25%; NTSC uses a different frequency), because it is parameterized by the actual Hz rather than a baked-in divider.

One subtlety worth flagging because it caused a real bug: decide per enable whether it must track the master clock (a rate that is a fixed ratio to the CPU — keep it a constant divider so the ratio never changes) or hold absolute time (time-of-day, UART baud — use the Hz-parameterized accumulator). Deriving a drive's clock independently of the CPU changed the CPU:drive frequency ratio in the C64 and broke fast-loaders. The full story and code are in the porting guide §2.4 (S53) and §3.B.2.


Part 2 — Clock Domain Crossing (CDC)

2.1 What a clock domain is, and why crossing it is dangerous

A clock domain is simply all the logic driven by one clock. A signal born in domain A (clocked by clock A) and sampled in domain B (clocked by an unrelated clock B) is a clock domain crossing.

The danger has a name: metastability. A flip-flop needs its input to be stable for a tiny window around the clock edge (setup and hold time). When the two clocks are unrelated, sooner or later B's edge will land exactly while the A-born signal is switching. The flip-flop is caught mid-decision and its output hovers at an undefined level — neither 0 nor 1 — for an unpredictable time, before eventually settling to one of them. If downstream logic samples it during that window, it sees garbage.

Two properties make this the classic beginner trap:

  • It is probabilistic and rare — it might happen once a second or once a week, depending on frequencies and luck.
  • It is invisible in simulation unless you deliberately model it. So the design "works," ships, and then fails intermittently on hardware in a way that is maddening to reproduce.

The rule that follows is absolute: you never wire a signal from one clock domain straight into another. Every crossing goes through a synchronizer built for the shape of that signal. For background at your own pace, AnySilicon's CDC overview and Nandland's crossing-clock-domains lesson are both approachable.

2.2 M2M's clock domains, and the naming convention that protects you

M2M is rigorously multi-clock, and it encodes the domain of every signal in its name. This convention is not cosmetic — it is the tool that lets you (and a reviewer) see a crossing at a glance:

Prefix Clock domain
main_* your core clock (whatever clk.vhd produces)
qnice_* the 50 MHz QNICE (Shell CPU) domain
hr_* the 100 MHz HyperRAM domain
video_* the video-output domain (often the same clock as main_*)
audio_* the 12.288 MHz audio domain

The MEGA65_Core port list is grouped by these domains with banner comments. Crossing a prefix boundary without a CDC block is a bug — for example, wiring a qnice_* signal directly into main_* logic will usually pass simulation and then fail rarely on hardware (§2.1). Honor the prefix on every signal you add; it is how the whole codebase stays reviewable.

2.3 The good news: the framework already crosses most of it for you

Before you worry about CDC, know that the framework hands you the common signals already synchronized into your domain. Without writing anything you get, crossed for you:

  • The 256-bit OSM control vector and the 256-bit general-purpose register, the 65-bit RTC, the paddle values, and the control bits (reset, pause, keyboard/joystick enables) — all arrive in main.vhd already in your core clock.
  • The keyboard vector on the way back to QNICE.
  • The audio samples into the audio domain.
  • Resets into every domain, and all virtual-drive traffic in both directions.

So your CDC work is narrow and specific — exactly three cases:

  1. Memories you share with QNICE (§2.5).
  2. Signals you route yourself between your own clocks (§2.4) — e.g. core clock to HyperRAM.
  3. CDC that is hidden inside the MiSTer core (§2.5) — a trap worth its own warning.

2.4 Crossing safely: pick the tool that fits the shape of the signal

The single most important CDC skill is matching the synchronizer to what you are crossing. Get the shape wrong and a "synchronizer" makes things worse, not better.

What you are crossing Use Why
A single-bit level / a slowly-changing flag a two-flip-flop synchronizer — M2M's cdc_stable Two back-to-back flip-flops give any metastable wobble a full cycle to settle before the value is used.
A pulse / event (one cycle high) a toggle handshake — M2M's cdc_pulse A raw pulse 2-FF'd can be missed entirely or stretched. cdc_pulse turns the event into a level toggle that cannot be lost (keep pulses ≥ 4 cycles of the slower clock apart).
A multi-bit bus / word that must stay coherent an asynchronous FIFO (avm_fifo / XPM FIFO) or a handshake — M2M's cdc_slow Never 2-FF each bit of a bus independently.

That last rule is the one beginners violate. If you synchronize a multi-bit value bit-by-bit, each bit resolves metastability on its own schedule, so for a cycle or two the receiver can latch a torn word — some bits from the old value, some from the new — a number that never actually existed. 0111 → 1000 can be read transiently as 1111 or 0000. For a bus you must therefore either move it through an asynchronous FIFO (which is built to carry whole words across unrelated clocks) or gate it with a handshake that only lets the destination sample when every bit is known stable (that is what cdc_slow does — "propagate only when all bits are stable"). M2M ships cdc_stable, cdc_pulse, and cdc_slow in M2M/vhdl/ for exactly these jobs; the streaming/bus workhorse is the async FIFO (avm_fifo, and the XPM FIFOs).

2.5 The MEGA65-specific gotchas

The QNICE falling-edge contract. QNICE reads and writes core memories on the falling edge of its 50 MHz clock. Every dual-port BRAM or register you share with QNICE must set its QNICE-side port to falling-edge (the FALLING_A/FALLING_B generics of M2M's dualport_2clk_ram). This is not a decoration — it makes the QNICE↔core crossing race-free by construction: the core uses the rising edge, QNICE the falling edge, so they never sample each other mid-change. The price is that a QNICE access has only half a clock period (~10 ns) to reach the memory, which leads to the companion rule: only give a QNICE port to memories that actually need one, and keep them small. (A real port learned this the hard way: wiring a QNICE debug port to a 1 MB RAM spread across hundreds of BRAM tiles could not meet the half-period timing and had to be removed.)

Hidden CDC inside the MiSTer core. A MiSTer module can have two clock ports and yet contain no synchronizers — because on MiSTer both ports were usually fed the same clk_sys, so a crossing never actually existed and nobody wrote one. On the MEGA65 you might feed those two ports different clocks and thereby create a real, unprotected crossing that was never there before. (This exact thing happened with the C64's 1541 drive "Dual ROM," whose second ROM port ended up on the QNICE clock; the fix was to add explicit synchronizers.) So: audit every dual-clock primitive in the core for which clocks you actually connect — a two-clock port is a promise you must keep, not a guarantee the core already did.

Core → HyperRAM. If your core uses HyperRAM, it drives the Avalon-MM master in the hr_* (100 MHz) domain. Cross from your core clock into it with an asynchronous FIFO (avm_fifo), usually behind a read cache (avm_cache) — the same building blocks the C64's REU uses. Video is easier: most cores keep video_clk == main_clk, but the framework treats video as its own domain so that a core with a genuinely different video clock still works.

2.6 A correct crossing still needs a constraint

Building the synchronizer right is only half the job. Vivado, by default, does not know your two clocks are unrelated, so it tries to make the crossing meet timing at their worst-case edge alignment — which between two independent MMCM clocks can be an impossible few picoseconds. Left alone, this both reports meaningless failures and makes the router insert huge detours that poison nearby legitimate paths.

The cure is to tell the tool the truth about the path — a set_max_delay ... -datapath_only (for a handshake/FIFO-protected data path) or a false path (for a signal that is genuinely asynchronous). M2M's cdc_stable documents the set_max_delay constraint it needs — and because cdc_pulse and cdc_slow are built on cdc_stable, the framework's single shared constraint (in common.xdc) covers them too; it already applies for its own crossings. You need to add the constraint for your own crossings and shared memories, in CORE/CORE.xdc. The concept to carry away: an unconstrained CDC can still fail even when the logic is perfect, because the tool is optimizing against a requirement that was never real. The exact XDC patterns, the LUTRAM-vs-flip-flop pin-name traps, and a full timing-closure war story are in the porting guide, §3.F.

2.7 Trap checklist

  • Wiring a qnice_* (or hr_*) signal straight into main_* logic — always cross it.
  • Two-flip-flopping a multi-bit bus → torn words. Use a FIFO or a handshake (§2.4).
  • Two-flip-flopping a pulse → lost or stretched events. Use cdc_pulse.
  • Forgetting the falling-edge QNICE port on a shared BRAM (§2.5).
  • Making a slow clock by dividing in fabric instead of using a clock enable (§1.2).
  • Feeding two clock ports of a MiSTer submodule different clocks without adding synchronizers (§2.5).
  • Leaving a real crossing unconstrained in the XDC (§2.6).
  • "Works in simulation, fails intermittently on hardware" — until proven otherwise, suspect a clock-domain crossing.

Where to go next

  • The actual clock work: The Ultimate MiSTer2MEGA65 Porting Guide — Phase 4 (§2.4) writes clk.vhd and the MMCM arithmetic; Part III §3.B is the PLL/clocking pattern catalog (shimming the Altera PLL, dynamic-reconfiguration replacements, derived-rate enables).
  • The actual CDC work: the same guide's §3.F covers what the framework crosses for you, the QNICE-shared-memory contract, the CDC helper entities, and the XDC constraints in full — plus §1.2.6 on the clock-domain naming convention.
  • The MiSTer side: the MiSTer Quick Reference Guide shows how a MiSTer core clocks itself, so you know what you are reproducing.
  • Background at your own pace: the pixel-clock and clock-enable primers linked in §1.1–§1.2, and the CDC overviews in §2.1.

Clone this wiki locally