Repository navigation
Timing Clocks and Timers
Reviewed: 2026-11-01 · Source: b7140848 · Release availability: #267/#271 included in September 30 stable.
xemu has several layers of timing. A host operating-system clock, a QEMU clock domain, an emulated device clock, and a guest-visible timer register are related, but they are not the same thing.
This distinction matters for both correctness and performance. Changing host wait precision is different from changing the virtual clock; changing the virtual clock is different from changing an NV2A PLL; and a timer callback is different from an interrupt becoming visible to the guest CPU.
flowchart TD
A[Host clock / OS wait primitive] --> B[QEMU clock domain]
B --> C[QEMUTimerList]
C --> D[QEMUTimer deadline]
D --> E[Timer callback]
E --> F[Emulated device state]
F --> G[Guest-visible counter / pending bit / IRQ]
G --> H[vCPU observes device effect]
The same timer infrastructure can support many device models, but each device decides which clock domain it uses and what its callback means.
util/qemu-timer.c implements the core clock and timer-list infrastructure used by xemu.
Current clock types include:
| Clock | Purpose |
|---|---|
QEMU_CLOCK_REALTIME |
Host real-time progression for operations tied to wall/monotonic host passage rather than guest execution |
QEMU_CLOCK_VIRTUAL |
Virtual machine time used by emulated hardware that should follow the guest machine's virtual execution timeline |
QEMU_CLOCK_HOST |
Host clock domain for host-facing timing behavior |
QEMU_CLOCK_VIRTUAL_RT |
Virtual real-time-oriented clock behavior used by QEMU infrastructure where appropriate |
The exact clock chosen is part of a device's semantics. Replacing one with another can change pause behavior, snapshot behavior, deadline ordering, and how quickly a guest-visible device advances.
The timer system separates the clock from the timers scheduled against it.
| Object | Role |
|---|---|
QEMUClock |
Defines a time domain and current time source |
QEMUTimerList |
Ordered collection of deadlines for one clock |
QEMUTimerListGroup |
Groups timer lists for event-loop deadline calculations |
QEMUTimer |
Callback plus expiration time and owning list |
Common operations include:
-
qemu_clock_get_ns()— read a clock domain. -
timer_new_ns()/timer_new_us()/timer_new_ms()— create timers with convenient units. -
timer_mod()— arm or move a timer to a deadline. -
timer_mod_anticipate()— move a deadline earlier when appropriate without needlessly delaying an existing earlier event. -
timer_del()— remove an armed timer. -
qemu_clock_run_timers()/ timer-list processing — execute callbacks whose deadlines have expired.
A timer list is not just a sorted container. If code inserts a new deadline that is earlier than what the event loop was expecting, the event loop may need to be notified so it recalculates its wait.
Conceptually:
Device computes desired deadline
↓
timer_mod(...)
↓
Timer list ordering changes
↓
Is new earliest deadline sooner than current wait?
├─ no → no urgent host wakeup
└─ yes → notify event loop
This is one reason unnecessary timer re-arming can matter even when the final guest-visible time is unchanged: list mutation, locking, notifier traffic, and host wakeups are work in their own right.
sequenceDiagram
participant D as Device model
participant Q as QEMU timer list
participant E as Event loop
participant C as Timer callback
participant G as Guest-visible state
D->>Q: timer_mod(deadline)
Q->>E: Notify if earliest deadline changed
E->>Q: Run expired timers
Q->>C: Invoke callback
C->>G: Update pending state / IRQ / registers
A callback should generally re-arm itself only if the emulated hardware requires another future event. Repeatedly deleting and re-inserting an unchanged deadline can be semantically correct but unnecessarily expensive.
The Xbox machine also includes PC-compatible timing devices initialized as part of the platform, including PIT and RTC functionality. Those devices are separate from the NV2A's internal PTIMER.
The key rule is: do not infer one guest timer's behavior from another timer merely because both eventually use QEMU's timer APIs.
hw/xbox/nv2a/ptimer.c implements the NV2A timer block visible through GPU registers.
The PTIMER counter is derived from:
-
QEMU_CLOCK_VIRTUALnanoseconds. - NV2A core-clock configuration from PRAMDAC.
- PTIMER numerator/denominator scaling.
At a conceptual level:
QEMU virtual nanoseconds
↓
NV2A core clock frequency
↓
PTIMER numerator / denominator
↓
Guest-visible PTIMER tick value
This means PTIMER is not simply a copy of the host high-resolution timer.
The device exposes state corresponding to items such as:
- Clock numerator.
- Clock denominator.
- Current time low/high registers.
- Alarm value.
- Interrupt pending state.
- Interrupt enable state.
The current timer value is computed from virtual time rather than being maintained by incrementing a host counter for every guest tick.
flowchart TD
A[Guest writes PTIMER alarm/control] --> B[Compute timer state]
B --> C[Convert target PTIMER tick to virtual deadline]
C --> D[Arm / adjust QEMU timer]
D --> E[Virtual deadline expires]
E --> F[PTIMER callback]
F --> G[Update PTIMER pending state]
G --> H[update_irq]
H --> I[Guest eventually receives NV2A interrupt]
The conversion in both directions matters. If integer math rounds a future guest tick down to an earlier host/virtual deadline, a timer can fire too soon. Conversely, overly conservative rounding can delay a guest event.
Because PTIMER derives its rate from NV2A clock configuration, changing the relevant PRAMDAC clock can change the relationship between virtual nanoseconds and PTIMER ticks.
A robust implementation therefore needs to distinguish:
- Guest-visible PTIMER count continuity.
- Current hardware clock ratio.
- The already scheduled host/virtual deadline.
- Whether a clock change requires recomputing that deadline.
This is also a snapshot concern: a restored guest should observe coherent device time even though the host process resumes at a different real-world moment.
On Windows, xemu/QEMU can use higher-resolution host timing behavior when the relevant setting is enabled. This affects how accurately the process can wait or poll for a host deadline.
It does not mean:
higher host timer resolution
=
faster guest PTIMER
The relationship is instead:
Guest/device deadline
↓
QEMU schedules host work for that virtual/host point
↓
OS wait precision determines how accurately the process wakes
Host timing precision can reduce oversleep or jitter but may have CPU/power tradeoffs. It should be evaluated as a scheduling mechanism, not as a virtual CPU overclock.
Virtual-clock semantics are important when emulation pauses. A hardware timer that is defined in virtual-machine time should generally not advance simply because the user left the emulator paused for ten real-world minutes.
This is one reason xemu cannot collapse every timer onto host wall time. The correct choice depends on what the original hardware would expose to the guest.
Timer state has two kinds of information:
| Type | Example | Migration rule |
|---|---|---|
| Guest architectural state | Alarm register, enable bit, pending bit | Must be restored according to device VMState semantics |
| Derived host scheduling state | Position in host timer list, OS wait handle | Reconstruct from restored guest/device state rather than treating host object identity as guest state |
After load, the emulator may need to recompute the next timer deadline from restored registers and the restored virtual-time relationship.
Timer performance problems are often misdiagnosed because the callback itself may be cheap while scheduling behavior is expensive.
Potential cost areas include:
| Area | Possible cost |
|---|---|
| Clock reads | Frequent calls in hot paths |
| Timer-list mutation | Insert/remove/reorder operations and locking |
| Event-loop notification | Waking a thread because an earlier deadline was inserted |
| Callback frequency | Host work repeated more often than guest semantics require |
| IRQ transitions | Cross-subsystem CPU/device work after callback |
| Oversleep / wake jitter | Tail latency when host scheduling misses a desired deadline |
| Busy polling | Lower latency at the cost of host CPU consumption |
The useful metric is therefore not just “number of timers.” Measure list operations, callbacks, IRQ changes, host waits, and resulting guest progress separately.
A QEMU timer is used only where the device model needs a future event. Many register-level models intentionally complete synchronously.
| Device/path | Current timing model | Important implication |
|---|---|---|
| NV2A PTIMER | Derived from virtual time + PRAMDAC/PTIMER scaling; alarm scheduled with QEMU timer | Guest-visible clock/deadline relationship is modeled |
| NVNet PHY autonegotiation |
250 ms QEMU_CLOCK_VIRTUAL deadline |
Coarse link negotiation delay is preserved |
| SMBus transfer | Target callback runs synchronously from START write | Physical SCL/SDA transfer time omitted |
| ACPI GPIO field pin | Static bit toggles on each guest read | Apparent field frequency depends on polling, not video timing |
| MCPX APU frame pacing | Dedicated thread + SDL queued bytes + QEMU_CLOCK_REALTIME
|
Host audio backend affects pacing/waits |
| SMC power request | Register write directly requests reset/shutdown | PIC firmware response delay omitted |
| Video encoders | Register storage | Encoder scan/analog timing is not implemented there |
flowchart TD
A[Guest observes time-dependent behavior] --> B{Model type}
B -->|virtual deadline| C[QEMU virtual clock / timer]
B -->|host pacing| D[realtime clock / host backend]
B -->|synchronous abstraction| E[immediate callback/result]
B -->|polling approximation| F[result changes when read]
This distinction should be recorded in any timing benchmark. A path with no timer callbacks may simply be modeled synchronously; it does not prove the corresponding physical hardware had zero latency.
Reviewed main and the September 30 stable include #267 PTIMER reconciliation and #271 PGRAPH interrupt/control decoupling. The first reconciles timer semantics and queue scheduling; the second prevents guest control/interrupt work from waiting behind renderer compilation/work while holding BQL. This restores observed responsiveness and progress rather than increasing the guest clock rate.
The retained Windows Vulkan Def Jam diagnostic reaches an active match beyond the prior freeze: 10,266 callbacks against 10,267 alarm writes, maximum lateness 2.406 ms and maximum BQL-held MMIO wait 2.826 ms. It exits 0 after about 13.8 minutes; the planned hour, OpenGL and save/load qualification were not completed. Operational evidence and boundaries.
BEFORE: guest control/interrupt observation -> renderer dependency under BQL
AFTER: guest control/interrupt state -> independent current control path
renderer work retains its own ownership and completion rules
The earlier unmerged #81 deadline-state experiment is historical research. Its keep/cancel/arm design, phase-rounding discussion and adverse OpenGL Morrowind observations are not the current implementation story or accepted performance proof. Keep the guest clock, virtual deadline, callback, pending IRQ and vCPU observation distinct when reasoning about either design.
A practical timing investigation should answer these questions in order:
- Which guest-visible clock or timer is involved?
- Which QEMU clock domain drives it?
- Is the observed error in counter conversion, deadline conversion, host waiting, callback execution, or interrupt delivery?
- Does the problem occur while the VM is paused or only while running?
- Does snapshot restore reproduce the same phase/deadline?
- How many actual
timer_mod/timer_del/ callback operations occur? - Is a performance change reducing useful work or merely moving a wait elsewhere?
| Source | Responsibility |
|---|---|
util/qemu-timer.c |
QEMU clocks, timer lists, deadlines, timer execution, host timing integration |
hw/xbox/xbox.c |
Xbox platform timer-device setup such as PIT/RTC |
hw/xbox/nv2a/ptimer.c |
NV2A PTIMER counter, alarm, interrupt state and scheduling |
hw/xbox/nv2a/pramdac.c |
NV2A clock configuration that influences PTIMER rate |
hw/xbox/nv2a/nv2a.c |
NV2A lifecycle, interrupt integration, migration state |
- PR #267 / #271: delivered PTIMER reconciliation and interrupt/control decoupling; stable availability and bounded evidence above.
- PR #81 — NV2A: replace PTIMER host scheduling with explicit deadlines: historical unmerged deadline/queue experiment; original qualification remains separate.
- PR #59: archived earlier PTIMER semantic investigation used as historical input to the newer work.
See Development for current status.
Debugging Lag & Hot Paths describes how timer/main-loop wakeups are correlated with guest progress before changing deadlines or host waiting. Fewer timer calls or more precise host waits are not accepted as a speedup without matched Release evidence.
DSP tooling guide and navigation updated November 2, 2026 for experimental PR #384, source 8f51d237. Capture/replay usage and limits.
The earlier wiki-wide review was published November 1, 2026. Emulator source: b7140848. Reviewed wiki content: 1d2b671. Current status · Release availability · Publication process.
- Home
- Current status
- Stable versus main
- Implemented mechanisms
- Controls and defaults
- Measured results and limits
- Complete change register
- System
- CPU and vCPU
- Memory and DMA
- Graphics / NV2A
- APU and audio
- Timing and timers
- Platform and I/O
- Hardware observations
- Fidelity boundaries
- Methodology
- PGR2 routes
- Debugging lag / hot paths
- Tools and commands
- DSP capture, replay and fuzzing
- HLE readiness and public XISO proof
- Concurrent DSP shadow and OpenAL proof
- DSP HLE operation inventory
- DSP HLE fix examples and testing
- Development
- Upstream shortlist
- Wiki maintenance
- Research / retired work
- Optimization catalog
- Hot-path shortcuts
- Researched experiments
- Unproven proposal directory
- PR lessons
- September26 snapshot
Repository · PRs · Issues