# Phase 5: Migrate Clock Components to Virtual Time ## Overview Phase 5 replaces `Instant::now()` and `thread::sleep()` in both clock components with virtual time from `Arc`. This is the first phase that modifies existing components rather than adding new infrastructure. **Files modified:** - [`generic_clock.rs`](src/components/clock/generic_clock.rs) — GenericClock - [`two_phase_clock.rs`](src/components/clock/two_phase_clock.rs) — TwoPhaseClock - [`system_config.rs`](src/system_config.rs) — Clock factory closures (minor) **Files NOT modified** (Phase 7 responsibility): - [`virtual_clock.rs`](src/virtual_clock.rs) — Already complete - [`real_time_governor.rs`](src/real_time_governor.rs) — Already complete - [`component.rs`](src/component.rs) — Already has `update_at_time()` default --- ## Implementation Order **Migrate `GenericClock` first, then `TwoPhaseClock`.** Rationale: 1. `GenericClock` is simpler — single-phase, single CLK output 2. `TwoPhaseClock` has dual outputs (PHI1/PHI2) and inter-phase dependencies 3. Both follow the same pattern, so GenericClock establishes the template 4. `GenericClock` has existing unit tests to validate the migration --- ## Part A: GenericClock Migration ### A1. Current Timing Mechanism [`GenericClock`](src/components/clock/generic_clock.rs:11) uses wall-clock time in 5 locations: | Location | Line | Current Code | Purpose | |----------|------|-------------|---------| | [`last_transition`](src/components/clock/generic_clock.rs:16) field | 16 | `last_transition: Instant` | Track when last phase change occurred | | [`pause_start`](src/components/clock/generic_clock.rs:21) field | 21 | `pause_start: Option` | Track when pause began | | [`new()`](src/components/clock/generic_clock.rs:34) constructor | 34 | `last_transition: Instant::now()` | Initialize transition timer | | [`enable()`](src/components/clock/generic_clock.rs:68) | 68 | `self.last_transition = Instant::now()` | Reset timer on enable | | [`pause()`](src/components/clock/generic_clock.rs:80) | 80 | `self.pause_start = Some(Instant::now())` | Record pause start | | [`resume()`](src/components/clock/generic_clock.rs:88) | 88 | `pause_start.elapsed()` | Calculate pause duration | | [`should_transition()`](src/components/clock/generic_clock.rs:131) | 131 | `self.last_transition.elapsed()` | Check if phase duration elapsed | | [`perform_transition()`](src/components/clock/generic_clock.rs:149) | 149 | `self.last_transition = Instant::now()` | Record transition time | | [`run()`](src/components/clock/generic_clock.rs:205) | 205 | `thread::sleep(sleep_time)` | Busy-wait loop with sleep | ### A2. Required Changes #### A2.1 — Add `VirtualClock` field (optional, gradual integration) Replace `Instant`-based fields with virtual-time equivalents. Add `Option>` for gradual integration — clocks work without a VirtualClock (falling back to wall-clock), but use virtual time when one is injected. ```rust // BEFORE (line 11-22): pub struct GenericClock { base: BaseComponent, frequency: f64, duty_cycle: f64, current_state: PinValue, last_transition: Instant, high_time: Duration, low_time: Duration, enabled: bool, paused: bool, pause_start: Option, } // AFTER: pub struct GenericClock { base: BaseComponent, frequency: f64, duty_cycle: f64, current_state: PinValue, last_transition_vtime: Duration, // Virtual time of last transition high_time: Duration, low_time: Duration, enabled: bool, paused: bool, pause_start_vtime: Option, // Virtual time when pause started virtual_clock: Option>, // Injected in Phase 7 } ``` **Key change:** `last_transition: Instant` → `last_transition_vtime: Duration` (virtual nanoseconds since sim start). `pause_start: Option` → `pause_start_vtime: Option`. #### A2.2 — Update constructor ```rust // BEFORE (line 29-43): let mut clock = GenericClock { base: BaseComponent::new(name, pins), frequency, duty_cycle: 0.5, current_state: PinValue::Low, last_transition: Instant::now(), high_time: Duration::from_secs_f64(0.5 / frequency), low_time: Duration::from_secs_f64(0.5 / frequency), enabled: true, paused: false, pause_start: None, }; // AFTER: let mut clock = GenericClock { base: BaseComponent::new(name, pins), frequency, duty_cycle: 0.5, current_state: PinValue::Low, last_transition_vtime: Duration::ZERO, // Virtual time 0 high_time: Duration::from_secs_f64(0.5 / frequency), low_time: Duration::from_secs_f64(0.5 / frequency), enabled: true, paused: false, pause_start_vtime: None, virtual_clock: None, // Set later via set_virtual_clock() }; ``` #### A2.3 — Add `set_virtual_clock()` method New method for Phase 7 injection: ```rust /// Inject a VirtualClock for virtual-time-based operation. /// If not set, the clock falls back to wall-clock Instant-based timing. /// This method is called by ConfigurableSystem in Phase 7. pub fn set_virtual_clock(&mut self, clock: Arc) { self.virtual_clock = Some(clock); } ``` #### A2.4 — Update `enable()` method (line 64-69) ```rust // BEFORE: pub fn enable(&mut self) { self.enabled = true; self.current_state = PinValue::Low; self.last_transition = Instant::now(); } // AFTER: pub fn enable(&mut self) { self.enabled = true; self.current_state = PinValue::Low; self.last_transition_vtime = self.current_virtual_time(); } ``` Where `current_virtual_time()` is a new helper: ```rust /// Get current virtual time, or fall back to Duration::ZERO if no VirtualClock. fn current_virtual_time(&self) -> Duration { self.virtual_clock.as_ref().map_or(Duration::ZERO, |vc| vc.now()) } ``` #### A2.5 — Update `pause()` and `resume()` (lines 78-94) ```rust // BEFORE: pub fn pause(&mut self) { self.paused = true; self.pause_start = Some(Instant::now()); } pub fn resume(&mut self) { if let Some(pause_start) = self.pause_start { let pause_duration = pause_start.elapsed(); self.last_transition += pause_duration; } self.paused = false; self.pause_start = None; } // AFTER: pub fn pause(&mut self) { self.paused = true; self.pause_start_vtime = Some(self.current_virtual_time()); } pub fn resume(&mut self) { if let Some(pause_start_vtime) = self.pause_start_vtime { let pause_duration = self.current_virtual_time().saturating_sub(pause_start_vtime); self.last_transition_vtime = self.last_transition_vtime.saturating_add(pause_duration); } self.paused = false; self.pause_start_vtime = None; } ``` #### A2.6 — Update `should_transition()` (line 130-138) ```rust // BEFORE: fn should_transition(&self) -> bool { let elapsed = self.last_transition.elapsed(); match self.current_state { PinValue::High => elapsed >= self.high_time, PinValue::Low => elapsed >= self.low_time, PinValue::HighZ => true, } } // AFTER: fn should_transition(&self, virtual_time: Duration) -> bool { let elapsed = virtual_time.saturating_sub(self.last_transition_vtime); match self.current_state { PinValue::High => elapsed >= self.high_time, PinValue::Low => elapsed >= self.low_time, PinValue::HighZ => true, } } ``` **Note:** `should_transition()` now takes `virtual_time: Duration` as a parameter instead of computing elapsed from `Instant`. #### A2.7 — Update `perform_transition()` (line 140-150) ```rust // BEFORE: fn perform_transition(&mut self) { let new_state = match self.current_state { PinValue::High => PinValue::Low, PinValue::Low => PinValue::High, PinValue::HighZ => PinValue::Low, }; self.current_state = new_state; self.set_clock_output(new_state); self.last_transition = Instant::now(); } // AFTER: fn perform_transition(&mut self, virtual_time: Duration) { let new_state = match self.current_state { PinValue::High => PinValue::Low, PinValue::Low => PinValue::High, PinValue::HighZ => PinValue::Low, }; self.current_state = new_state; self.set_clock_output(new_state); self.last_transition_vtime = virtual_time; } ``` #### A2.8 — Implement `update_at_time()` (new, replaces `update()` for virtual-time path) Add the virtual-time-aware update method. This overrides the default from the `Component` trait: ```rust impl Component for GenericClock { // ... existing methods unchanged ... fn update_at_time(&mut self, virtual_time: Duration) { // Check pause state first - don't update if paused if self.paused { return; } // Read external enable control let external_enable = self.read_enable_pin(); let should_be_enabled = self.enabled && external_enable; if !should_be_enabled { if self.current_state != PinValue::Low { self.set_clock_output(PinValue::Low); self.current_state = PinValue::Low; } return; } // Check if it's time to transition (using virtual time) if self.should_transition(virtual_time) { self.perform_transition(virtual_time); } } // Keep update() for backward compatibility — delegates to update_at_time // using current virtual time (or Duration::ZERO if no VirtualClock) fn update(&mut self) { let vtime = self.current_virtual_time(); self.update_at_time(vtime); } } ``` #### A2.9 — Rewrite `run()` to use `wait_until()` (line 190-210) This is the most significant change. Replace the `thread::sleep()` busy-loop with `virtual_clock.wait_until()`: ```rust // BEFORE: fn run(&mut self) { self.base.set_running(true); self.enable(); while self.is_running() { self.update(); let sleep_time = if self.frequency > 0.0 { Duration::from_secs_f64(1.0 / self.frequency / 100.0) .max(Duration::from_micros(1)) } else { Duration::from_micros(100) }; thread::sleep(sleep_time); } self.disable(); } // AFTER: fn run(&mut self) { self.base.set_running(true); self.enable(); // If no VirtualClock is injected, fall back to wall-clock run loop let vclock = match self.virtual_clock.as_ref() { Some(vc) => Arc::clone(vc), None => { // Legacy path: use old Instant-based timing return self.run_legacy(); } }; while self.is_running() { // Calculate next transition time let next_transition = self.next_transition_time(); // Block until virtual time reaches next transition match vclock.wait_until(next_transition) { Some(current_vtime) => { if !self.is_running() { break; } self.update_at_time(current_vtime); } None => break, // Simulation stopped } } self.disable(); } ``` Add helper methods: ```rust impl GenericClock { /// Calculate the virtual time of the next phase transition. fn next_transition_time(&self) -> Duration { let phase_duration = match self.current_state { PinValue::High => self.high_time, PinValue::Low => self.low_time, PinValue::HighZ => Duration::from_nanos(1), }; self.last_transition_vtime.saturating_add(phase_duration) } /// Legacy run loop using wall-clock time (backward compatibility). fn run_legacy(&mut self) { while self.is_running() { self.update(); let sleep_time = if self.frequency > 0.0 { Duration::from_secs_f64(1.0 / self.frequency / 100.0) .max(Duration::from_micros(1)) } else { Duration::from_micros(100) }; thread::sleep(sleep_time); } self.disable(); } } ``` #### A2.10 — Add `timing()` override ```rust impl Component for GenericClock { // ... existing methods ... fn timing(&self) -> ComponentTiming { ComponentTiming { name: "Generic Clock", access_time: Duration::ZERO, propagation_delay: Duration::ZERO, setup_time: Duration::ZERO, hold_time: Duration::ZERO, max_clock_frequency: f64::INFINITY, min_update_interval: self.high_time.min(self.low_time), } } } ``` #### A2.11 — Update imports ```rust // BEFORE (line 1-9): use parking_lot::Mutex; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; use crate::component::{BaseComponent, Component}; use crate::pin::{Pin, PinValue}; // AFTER: use parking_lot::Mutex; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use std::thread; use std::time::Duration; use crate::component::{BaseComponent, Component}; use crate::component_timing::ComponentTiming; use crate::pin::{Pin, PinValue}; use crate::virtual_clock::VirtualClock; ``` **Key change:** Remove `Instant` from imports, add `VirtualClock` and `ComponentTiming`. ### A3. GenericClock Test Impact The existing tests in [`generic_clock.rs:238-258`](src/components/clock/generic_clock.rs:238) are: | Test | What it checks | Impact | |------|---------------|--------| | [`test_clock_creation`](src/components/clock/generic_clock.rs:243) | Name and running state | **No impact** — doesn't use `Instant` | | [`test_clock_timing_calculation`](src/components/clock/generic_clock.rs:250) | `frequency_to_duration()` | **No impact** — static calculation | These tests will pass unchanged. No new tests needed for the migration itself — the existing tests validate construction and static calculations. Virtual-time behavior tests should be added in Phase 9. --- ## Part B: TwoPhaseClock Migration ### B1. Current Timing Mechanism [`TwoPhaseClock`](src/components/clock/two_phase_clock.rs:11) uses wall-clock time in 5 locations: | Location | Line | Current Code | Purpose | |----------|------|-------------|---------| | [`last_transition`](src/components/clock/two_phase_clock.rs:15) field | 15 | `last_transition: Instant` | Track when last phase swap occurred | | [`pause_start`](src/components/clock/two_phase_clock.rs:19) field | 19 | `pause_start: Option` | Track when pause began | | [`new()`](src/components/clock/two_phase_clock.rs:38) constructor | 38 | `last_transition: Instant::now()` | Initialize transition timer | | [`enable()`](src/components/clock/two_phase_clock.rs:64) | 64 | `self.last_transition = Instant::now()` | Reset timer on enable | | [`pause()`](src/components/clock/two_phase_clock.rs:80) | 80 | `self.pause_start = Some(Instant::now())` | Record pause start | | [`resume()`](src/components/clock/two_phase_clock.rs:88) | 88 | `pause_start.elapsed()` | Calculate pause duration | | [`should_transition()`](src/components/clock/two_phase_clock.rs:137) | 137 | `self.last_transition.elapsed()` | Check if phase_time elapsed | | [`perform_transition()`](src/components/clock/two_phase_clock.rs:160) | 160 | `self.last_transition = Instant::now()` | Record transition time | | [`run()`](src/components/clock/two_phase_clock.rs:203) | 203 | `thread::sleep(Duration::from_micros(100))` | Fixed 100µs busy-wait | ### B2. Required Changes #### B2.1 — Add `VirtualClock` field (optional, gradual integration) ```rust // BEFORE (line 11-21): pub struct TwoPhaseClock { base: BaseComponent, phi1_state: PinValue, phi2_state: PinValue, last_transition: Instant, phase_time: Duration, enabled: bool, paused: bool, pause_start: Option, frequency: f64, } // AFTER: pub struct TwoPhaseClock { base: BaseComponent, phi1_state: PinValue, phi2_state: PinValue, last_transition_vtime: Duration, // Virtual time of last phase swap phase_time: Duration, enabled: bool, paused: bool, pause_start_vtime: Option, // Virtual time when pause started frequency: f64, virtual_clock: Option>, // Injected in Phase 7 } ``` #### B2.2 — Update constructor (line 24-45) ```rust // BEFORE: TwoPhaseClock { base: BaseComponent::new(name, pins), phi1_state: PinValue::High, phi2_state: PinValue::Low, last_transition: Instant::now(), phase_time, enabled: true, paused: false, pause_start: None, frequency, } // AFTER: TwoPhaseClock { base: BaseComponent::new(name, pins), phi1_state: PinValue::High, phi2_state: PinValue::Low, last_transition_vtime: Duration::ZERO, phase_time, enabled: true, paused: false, pause_start_vtime: None, frequency, virtual_clock: None, } ``` #### B2.3 — Add `set_virtual_clock()` and `current_virtual_time()` helpers ```rust impl TwoPhaseClock { /// Inject a VirtualClock for virtual-time-based operation. pub fn set_virtual_clock(&mut self, clock: Arc) { self.virtual_clock = Some(clock); } /// Get current virtual time, or Duration::ZERO if no VirtualClock. fn current_virtual_time(&self) -> Duration { self.virtual_clock.as_ref().map_or(Duration::ZERO, |vc| vc.now()) } } ``` #### B2.4 — Update `enable()` (line 60-69) ```rust // BEFORE: pub fn enable(&mut self) { self.enabled = true; self.phi1_state = PinValue::High; self.phi2_state = PinValue::Low; self.last_transition = Instant::now(); self.update_outputs(); self.update_outputs(); } // AFTER: pub fn enable(&mut self) { self.enabled = true; self.phi1_state = PinValue::High; self.phi2_state = PinValue::Low; self.last_transition_vtime = self.current_virtual_time(); self.update_outputs(); self.update_outputs(); } ``` #### B2.5 — Update `pause()` and `resume()` (lines 79-95) Same pattern as GenericClock: ```rust pub fn pause(&mut self) { self.paused = true; self.pause_start_vtime = Some(self.current_virtual_time()); } pub fn resume(&mut self) { if let Some(pause_start_vtime) = self.pause_start_vtime { let pause_duration = self.current_virtual_time().saturating_sub(pause_start_vtime); self.last_transition_vtime = self.last_transition_vtime.saturating_add(pause_duration); } self.paused = false; self.pause_start_vtime = None; } ``` #### B2.6 — Update `should_transition()` (line 136-138) ```rust // BEFORE: fn should_transition(&self) -> bool { self.last_transition.elapsed() >= self.phase_time } // AFTER: fn should_transition(&self, virtual_time: Duration) -> bool { let elapsed = virtual_time.saturating_sub(self.last_transition_vtime); elapsed >= self.phase_time } ``` #### B2.7 — Update `perform_transition()` (line 140-161) ```rust // BEFORE: fn perform_transition(&mut self) { match (self.phi1_state, self.phi2_state) { (PinValue::High, PinValue::Low) => { self.phi1_state = PinValue::Low; self.phi2_state = PinValue::High; } (PinValue::Low, PinValue::High) => { self.phi1_state = PinValue::High; self.phi2_state = PinValue::Low; } _ => { self.phi1_state = PinValue::High; self.phi2_state = PinValue::Low; } } self.update_outputs(); self.last_transition = Instant::now(); } // AFTER: fn perform_transition(&mut self, virtual_time: Duration) { match (self.phi1_state, self.phi2_state) { (PinValue::High, PinValue::Low) => { self.phi1_state = PinValue::Low; self.phi2_state = PinValue::High; } (PinValue::Low, PinValue::High) => { self.phi1_state = PinValue::High; self.phi2_state = PinValue::Low; } _ => { self.phi1_state = PinValue::High; self.phi2_state = PinValue::Low; } } self.update_outputs(); self.last_transition_vtime = virtual_time; } ``` #### B2.8 — Implement `update_at_time()` and update `update()` ```rust impl Component for TwoPhaseClock { // ... existing name/pins/get_pin unchanged ... fn update_at_time(&mut self, virtual_time: Duration) { if self.paused { return; } if !self.enabled { return; } self.update_outputs(); if self.should_transition(virtual_time) { self.perform_transition(virtual_time); } } fn update(&mut self) { let vtime = self.current_virtual_time(); self.update_at_time(vtime); } // ... rest unchanged ... } ``` #### B2.9 — Rewrite `run()` to use `wait_until()` (line 195-212) ```rust // BEFORE: fn run(&mut self) { self.base.set_running(true); self.enable(); while self.is_running() { self.update(); thread::sleep(Duration::from_micros(100)); if !self.is_running() { break; } } self.disable(); } // AFTER: fn run(&mut self) { self.base.set_running(true); self.enable(); let vclock = match self.virtual_clock.as_ref() { Some(vc) => Arc::clone(vc), None => return self.run_legacy(), }; while self.is_running() { let next_transition = self.last_transition_vtime + self.phase_time; match vclock.wait_until(next_transition) { Some(current_vtime) => { if !self.is_running() { break; } self.update_at_time(current_vtime); } None => break, } } self.disable(); } ``` Add legacy helper: ```rust impl TwoPhaseClock { /// Legacy run loop using wall-clock time (backward compatibility). fn run_legacy(&mut self) { while self.is_running() { self.update(); thread::sleep(Duration::from_micros(100)); if !self.is_running() { break; } } self.disable(); } } ``` #### B2.10 — Add `timing()` override ```rust fn timing(&self) -> ComponentTiming { ComponentTiming { name: "Two-Phase Clock", access_time: Duration::ZERO, propagation_delay: Duration::ZERO, setup_time: Duration::ZERO, hold_time: Duration::ZERO, max_clock_frequency: f64::INFINITY, min_update_interval: self.phase_time, } } ``` #### B2.11 — Update imports ```rust // BEFORE (line 1-9): use parking_lot::Mutex; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; use crate::component::{BaseComponent, Component}; use crate::pin::{Pin, PinValue}; // AFTER: use parking_lot::Mutex; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use std::thread; use std::time::Duration; use crate::component::{BaseComponent, Component}; use crate::component_timing::ComponentTiming; use crate::pin::{Pin, PinValue}; use crate::virtual_clock::VirtualClock; ``` ### B3. TwoPhaseClock Test Impact No tests exist for `TwoPhaseClock` (no `#[cfg(test)]` module in the file). No test breakage risk. --- ## Part C: System Config Changes ### C1. Factory Closure Updates In [`system_config.rs`](src/system_config.rs:241-277), the factory closures create clocks without a `VirtualClock`. In Phase 5, these remain unchanged — the `virtual_clock` field is `None` by default, and clocks use the legacy `run_legacy()` path. In **Phase 7**, `ConfigurableSystem` will: 1. Create `Arc` 2. After constructing each clock, call `set_virtual_clock(Arc::clone(&virtual_clock))` 3. This requires changing the factory closure signature or adding a post-construction hook For Phase 5, no changes to [`system_config.rs`](src/system_config.rs) are needed. --- ## Part D: Backward Compatibility Analysis ### D1. What stays the same | Aspect | Compatibility | |--------|--------------| | `new(name, frequency)` constructor signature | **Unchanged** — no new required parameters | | `Component` trait impl | **Unchanged** — `update()` still works via delegation | | `enable()` / `disable()` / `pause()` / `resume()` | **Unchanged** — public API identical | | `set_frequency()` / `get_frequency()` | **Unchanged** | | Pin names (CLK, PHI1, PHI2, ENABLE) | **Unchanged** | | `run()` without VirtualClock | **Works** — falls back to `run_legacy()` with `thread::sleep()` | | Existing tests | **Pass** — no Instant dependency in test assertions | ### D2. What changes (internal only) | Change | Visible externally? | |--------|-------------------| | `last_transition: Instant` → `last_transition_vtime: Duration` | No — private field | | `pause_start: Option` → `pause_start_vtime: Option` | No — private field | | `should_transition()` takes `Duration` param | No — private method | | `perform_transition()` takes `Duration` param | No — private method | | New `virtual_clock: Option>` field | No — private, `None` by default | | New `set_virtual_clock()` method | Yes — but opt-in, not required | ### D3. Risk assessment **Risk: LOW.** All changes are internal field replacements. The public API is unchanged. The `Option>` pattern ensures clocks work identically without a VirtualClock injected. --- ## Part E: Execution Checklist These are the implementation steps in order: - [ ] **E1.** Update `GenericClock` imports — remove `Instant`, add `VirtualClock` + `ComponentTiming` - [ ] **E2.** Replace `GenericClock` struct fields: `last_transition: Instant` → `last_transition_vtime: Duration`, `pause_start: Option` → `pause_start_vtime: Option`, add `virtual_clock: Option>` - [ ] **E3.** Update `GenericClock::new()` — initialize `last_transition_vtime: Duration::ZERO`, `virtual_clock: None` - [ ] **E4.** Add `GenericClock::set_virtual_clock()` and `GenericClock::current_virtual_time()` helper methods - [ ] **E5.** Update `GenericClock::enable()` — use `current_virtual_time()` instead of `Instant::now()` - [ ] **E6.** Update `GenericClock::pause()` — use `current_virtual_time()` instead of `Instant::now()` - [ ] **E7.** Update `GenericClock::resume()` — compute pause duration from virtual time deltas - [ ] **E8.** Update `GenericClock::should_transition()` — accept `virtual_time: Duration` parameter - [ ] **E9.** Update `GenericClock::perform_transition()` — accept `virtual_time: Duration`, store in `last_transition_vtime` - [ ] **E10.** Implement `GenericClock::update_at_time()` in `Component` impl - [ ] **E11.** Update `GenericClock::update()` to delegate to `update_at_time(current_virtual_time())` - [ ] **E12.** Add `GenericClock::next_transition_time()` and `GenericClock::run_legacy()` helper methods - [ ] **E13.** Rewrite `GenericClock::run()` — use `wait_until()` when VirtualClock present, else `run_legacy()` - [ ] **E14.** Add `GenericClock::timing()` override in `Component` impl - [ ] **E15.** Verify `GenericClock` existing tests still pass - [ ] **E16.** Update `TwoPhaseClock` imports — remove `Instant`, add `VirtualClock` + `ComponentTiming` - [ ] **E17.** Replace `TwoPhaseClock` struct fields: same pattern as GenericClock - [ ] **E18.** Update `TwoPhaseClock::new()` — initialize virtual time fields - [ ] **E19.** Add `TwoPhaseClock::set_virtual_clock()` and `TwoPhaseClock::current_virtual_time()` helpers - [ ] **E20.** Update `TwoPhaseClock::enable()` — use `current_virtual_time()` - [ ] **E21.** Update `TwoPhaseClock::pause()` and `resume()` — virtual time deltas - [ ] **E22.** Update `TwoPhaseClock::should_transition()` — accept `virtual_time: Duration` - [ ] **E23.** Update `TwoPhaseClock::perform_transition()` — accept `virtual_time: Duration` - [ ] **E24.** Implement `TwoPhaseClock::update_at_time()` in `Component` impl - [ ] **E25.** Update `TwoPhaseClock::update()` to delegate to `update_at_time()` - [ ] **E26.** Add `TwoPhaseClock::run_legacy()` helper - [ ] **E27.** Rewrite `TwoPhaseClock::run()` — use `wait_until()` when VirtualClock present - [ ] **E28.** Add `TwoPhaseClock::timing()` override in `Component` impl - [ ] **E29.** Run `cargo build` to verify compilation - [ ] **E30.** Run `cargo test` to verify all existing tests pass --- ## Flow Diagram: Clock Thread with Virtual Time ```mermaid flowchart TD A[run called] --> B{virtual_clock set?} B -->|No| C[run_legacy - thread::sleep loop] B -->|Yes| D[enable - reset phase state] D --> E[Calculate next_transition_time] E --> F[wait_until next_transition] F --> G{wait_until returned} G -->|None - stopped| H[disable and exit] G -->|Some vtime| I{is_running?} I -->|No| H I -->|Yes| J[update_at_time with vtime] J --> K[Transition phase if needed] K --> E ``` ## Flow Diagram: Phase Transition Logic in Virtual Time ```mermaid flowchart LR subgraph GenericClock G1[last_transition_vtime = 0ns] --> G2{vtime >= high_time?} G2 -->|Yes| G3[High to Low transition] G3 --> G4[last_transition_vtime = vtime] G4 --> G5{vtime >= low_time?} G5 -->|Yes| G6[Low to High transition] G6 --> G4 end subgraph TwoPhaseClock T1[last_transition_vtime = 0ns] --> T2{elapsed >= phase_time?} T2 -->|Yes| T3[PHI1 High+PHI2 Low to PHI1 Low+PHI2 High] T3 --> T4[last_transition_vtime = vtime] T4 --> T5{elapsed >= phase_time?} T5 -->|Yes| T6[PHI1 Low+PHI2 High to PHI1 High+PHI2 Low] T6 --> T4 end ```