Skip to main content

Module scheduler

Module scheduler 

Source
Expand description

The canonical master-clock scheduler — the heart of the emulator.

§Timebase model (ADR 0006)

The tick unit is 187.5 MHz (MASTER_HZ) — the LCM of the VR4300’s 93.75 MHz PClock and the RCP’s 62.5 MHz MClock. That makes every emulated clock domain an integer divisor of one counter:

ComponentRateDivider
VR4300 PClock93.75 MHz2
RCP (MClock / SClock)62.5 MHz3
COP0 Count (half PClock)46.875 MHz4
Serial Interface15.625 MHz12
Cartridge / PIF1.953125 MHz96

No accumulator, no remainder, no drift — drift is unrepresentable rather than merely avoided. Note the hardware derives the CPU from MClock (a 3/2 PLL at DivMode = 0b01), not the reverse; ADR 0001 had the CPU as master, which inverted that and is why the RCP needed a fractional remainder.

Only the VI (VCLK, ~48.68 MHz, off a different crystal) and the AI genuinely are not rational multiples of this tick; those keep a fractional accumulator.

§The one rule

master_ticks is the only counter in the core that is ever incremented. Every other cycle position is derived — see System::cpu_cycles and System::rcp_cycles, which are accessors rather than fields. A second incremented counter agrees with the first only because every call site remembers to step it: an invariant held by construction rather than by derivation, correct until one path forgets. The residue_invariants_never_move test exists to catch exactly that.

A retired-work tally is a different thing and is legitimate (Cpu::retired); it counts work done and nothing schedules against it.

§Lockstep, edge to edge

This is LOCKSTEP, not catch-up: an RCP event (a DP-done IRQ, an SP halt) is visible to the very next CPU step. But nothing iterates 187.5M times a second — the CPU lands on every 2nd tick and the RCP on every 3rd, so the pattern repeats every 6 ticks and System::step_to_next_edge jumps straight to the next tick where something is due. The unit is a time base, not a loop counter.

There are never OS threads in the core; one timeline is the whole reason the determinism contract holds (ADR 0004). Rollback / run-ahead live in the frontend.

See docs/scheduler.md and docs/adr/0006-one-canonical-master-clock.md.

Structs§

System
Owns the run loop and ties the CPU to the Bus on one timeline.

Constants§

COUNT_DIVIDER
Master ticks per COP0 Count increment — Count runs at half PClock (46.875 MHz), per VR4300 User’s Manual §6.3.3 Figure 6-3. Forgetting the halving is a documented source of 2x timing bugs.
CPU_DIVIDER
Master ticks per VR4300 PClock.
CPU_HZ
The VR4300 pipeline clock (PClock), 93.75 MHz. MASTER_HZ / CPU_DIVIDER.
EDGE_PERIOD
Ticks after which the whole edge schedule repeats: lcm(CPU_DIVIDER, RCP_DIVIDER).
MASTER_HZ
The canonical master tick rate: 187.5 MHz, the LCM of the CPU and RCP clocks.
PHASE_PERIOD
The CPU:RCP interleaving period: lcm(CPU_DIVIDER, RCP_DIVIDER) master ticks.
PIF_DIVIDER
Master ticks per cartridge / PIF cycle (1.953125 MHz).
RCP_DIVIDER
Master ticks per RCP (MClock) cycle.
RCP_HZ
The RCP clock (MClock, and the CPU’s SClock bus rate), 62.5 MHz.
SI_DIVIDER
Master ticks per Serial Interface cycle (15.625 MHz).