# Phase 7: System Integration — ConfigurableSystem + VirtualClock ## Overview Phase 7 wires the `VirtualClock` and `RealTimeGovernor` into `ConfigurableSystem`, completing the transition from `thread::sleep()`-based timing to virtual-time-synchronized per-component threads. ## Current Architecture (Pre-Phase 7) ``` ConfigurableSystem::start() → for each component: thread::spawn(|| { loop { if !running → break if paused → sleep(10ms), continue comp.try_lock() → comp.update() adaptive sleep (yield or µs-sleep based on frequency) } comp.stop() }) ``` Problems: - No timing accuracy — OS `nanosleep` has ~100µs granularity - No synchronization between components - No speed control - Components don't use virtual time ## Target Architecture (Post-Phase 7) ``` ConfigurableSystem::start() 1. Create Arc with tick_size derived from clock frequency 2. Inject VirtualClock into all components via set_virtual_clock() 3. Spawn RealTimeGovernor thread → governor.run() calls clock.start() 4. for each component: thread::spawn(|| { comp.run() // Component's own run() handles VirtualClock wait }) 5. Store governor handle for clean shutdown ``` ## Changes Required ### 1. `src/system_config.rs` — `ConfigurableSystem` #### New Fields ```rust pub struct ConfigurableSystem { // ... existing fields ... virtual_clock: Arc, governor_handle: Arc>>>, } ``` #### Modified `new()` Constructor - Derive tick_size from clock frequency in metadata - Create `Arc` with that tick_size - Store it in the struct #### Modified `start()` Method (Inherent) The current `start()` spawns threads with a manual loop that calls `comp.update()`. The new `start()` will: 1. Inject `Arc` into all components via `set_virtual_clock()` (downcast to each concrete type) 2. Spawn `RealTimeGovernor` in its own thread — `governor.run()` calls `clock.start()` internally 3. For each component, spawn a thread that calls `comp.run()` — the component's own `run()` method handles the VirtualClock wait loop (already implemented in Phases 5-6) 4. Store the governor thread handle #### Modified `cleanup()` Method 1. Call `clock.stop()` to unblock all component threads waiting in `wait_until()` / `wait_for_start()` 2. Set `is_running = false` 3. Wait for component threads to finish (join) 4. Wait for governor thread to finish (join) #### Modified `SystemController::stop()` (Trait Impl) - Call `clock.stop()` before existing cleanup - This wakes all component threads, which see `None` from `wait_until()` and exit #### Modified `SystemController::pause()` / `resume()` (Trait Impl) - Delegate to `clock.pause()` / `clock.resume()` instead of (or in addition to) component-level pause - This is more efficient — all threads block on the condvar instead of polling ### 2. `src/main.rs` — No Changes Required The `main.rs` calls `ConfigurableSystem::start()` or `system.run()`, both of which will now use VirtualClock internally. No changes needed. ### 3. Tick Size Derivation For a 750 kHz two-phase clock: - Full cycle = 1/750000 ≈ 1333ns - Each phase = 1333ns / 2 ≈ 666ns - Phase transition = 666ns / 2 = 333ns (quarter cycle) - tick_size = 333ns General formula: `tick_size = 1 / (4 * frequency)` seconds For non-two-phase clocks (generic_clock): - tick_size = 1 / (2 * frequency) seconds (half cycle) We use the conservative (smaller) tick_size: `1 / (4 * frequency)`. ### 4. Backward Compatibility If `set_virtual_clock()` is not called on a component (e.g., a component type that doesn't have the method), the component's `run()` method falls back to `run_legacy()` which uses `thread::sleep()`. This is already implemented in each component's `run()` method from Phases 5-6. The `Component` trait's `run()` method has a default implementation that uses `thread::sleep()`, so any component that hasn't been migrated will still work. ### 5. Thread Lifecycle ``` Component Thread: 1. comp.run() is called 2. If virtual_clock is Some: a. Loop: wait_until(next_time) → update_at_time(vtime) → schedule next b. Exit when wait_until returns None (clock stopped) 3. If virtual_clock is None: a. run_legacy() — original thread::sleep loop Governor Thread: 1. governor.run() is called 2. Calls clock.start() — wakes all component threads in wait_for_start() 3. Loop: advance virtual time based on mode/speed 4. Exit when clock.is_running() == false 5. Drop guard calls clock.stop() ``` ### 6. Downcasting Strategy Since `set_virtual_clock()` is a method on each concrete type (not on the `Component` trait), we need to downcast from `Box` to each concrete type. The order of attempts: 1. `GenericClock` — `comp.as_any_mut().downcast_mut::()` 2. `TwoPhaseClock` — `comp.as_any_mut().downcast_mut::()` 3. `Intel4004` — `comp.as_any_mut().downcast_mut::()` 4. `Intel4001` — `comp.as_any_mut().downcast_mut::()` 5. `Intel4002` — `comp.as_any_mut().downcast_mut::()` 6. `Intel4003` — `comp.as_any_mut().downcast_mut::()` This is the same pattern already used in `pause()`, `resume()`, `set_clock_frequency()`, etc. ## Implementation Steps 1. Add imports for `VirtualClock`, `RealTimeGovernor`, and concrete component types 2. Add `virtual_clock` and `governor_handle` fields to `ConfigurableSystem` 3. Update `ConfigurableSystem::new()` to create `VirtualClock` from frequency 4. Add `inject_virtual_clock()` helper method 5. Update `ConfigurableSystem::start()` (inherent) to: - Inject VirtualClock into all components - Spawn governor thread - Spawn component threads that call `comp.run()` 6. Update `ConfigurableSystem::cleanup()` to stop clock and join governor 7. Update `SystemController::pause()`/`resume()` to use clock.pause()/resume() 8. Build and test ## Risk Assessment - **Low Risk**: Adding new fields and methods to `ConfigurableSystem` - **Medium Risk**: Changing the thread spawn model from external loop to `comp.run()` - Mitigated by the fact that `comp.run()` already works (tested in Phases 5-6) - **Low Risk**: Downcast-based injection — same pattern already used extensively - **Low Risk**: Governor thread — already tested independently ## Testing Strategy 1. `cargo build` — must compile 2. `cargo test` — all existing tests must pass 3. Manual test: `cargo run -- --system basic` — system should start, run, and stop cleanly 4. Manual test: `cargo run -- --gui --system basic` — GUI should work with VirtualClock