rusty_emu/src/components/clock/generic_clock.rs

258 lines
7.6 KiB
Rust

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};
pub struct GenericClock {
base: BaseComponent,
frequency: f64, // Frequency in Hz
duty_cycle: f64, // Duty cycle as percentage (0.0 to 1.0)
current_state: PinValue,
last_transition: Instant,
high_time: Duration,
low_time: Duration,
enabled: bool,
paused: bool, // Pause state for system control
pause_start: Option<Instant>, // When pause started, to track pause duration
}
impl GenericClock {
pub fn new(name: String, frequency: f64) -> Self {
let pin_names = vec!["CLK", "ENABLE"];
let pins = BaseComponent::create_pin_map(&pin_names, &name);
let mut clock = GenericClock {
base: BaseComponent::new(name, pins),
frequency,
duty_cycle: 0.5, // 50% duty cycle by default
current_state: PinValue::Low,
last_transition: Instant::now(),
high_time: Duration::from_secs_f64(0.5 / frequency), // Will be recalculated in set_duty_cycle
low_time: Duration::from_secs_f64(0.5 / frequency), // Will be recalculated in set_duty_cycle
enabled: true,
paused: false,
pause_start: None,
};
clock.set_duty_cycle(0.5); // Initialize timing
clock
}
/// Set the clock frequency dynamically
pub fn set_frequency(&mut self, frequency: f64) {
if frequency > 0.0 {
self.frequency = frequency;
self.update_timing();
}
}
/// Get the current clock frequency
pub fn get_frequency(&self) -> f64 {
self.frequency
}
pub fn set_duty_cycle(&mut self, duty_cycle: f64) {
self.duty_cycle = duty_cycle.clamp(0.1, 0.9); // Keep within reasonable bounds
self.update_timing();
}
pub fn enable(&mut self) {
self.enabled = true;
// Start with known state when enabled
self.current_state = PinValue::Low;
self.last_transition = Instant::now();
}
pub fn disable(&mut self) {
self.enabled = false;
// Set output to Low when disabled
self.set_clock_output(PinValue::Low);
}
/// Pause the clock - freeze output state
pub fn pause(&mut self) {
self.paused = true;
self.pause_start = Some(Instant::now());
}
/// Resume the clock from paused state
/// Maintains phase consistency by adjusting last_transition to account for pause duration
pub fn resume(&mut self) {
if let Some(pause_start) = self.pause_start {
// Calculate how long we were paused
let pause_duration = pause_start.elapsed();
// Adjust last_transition forward by the pause duration to maintain phase
self.last_transition += pause_duration;
}
self.paused = false;
self.pause_start = None;
}
/// Check if the clock is paused
pub fn is_paused(&self) -> bool {
self.paused
}
fn frequency_to_duration(frequency: f64) -> Duration {
if frequency <= 0.0 {
Duration::from_secs(0)
} else {
Duration::from_secs_f64(1.0 / frequency)
}
}
fn update_timing(&mut self) {
let period = Self::frequency_to_duration(self.frequency);
self.high_time = Duration::from_secs_f64(period.as_secs_f64() * self.duty_cycle);
self.low_time = Duration::from_secs_f64(period.as_secs_f64() * (1.0 - self.duty_cycle));
}
fn set_clock_output(&self, value: PinValue) {
if let Ok(clock_pin) = self.base.get_pin("CLK") {
let mut pin_guard = clock_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), value);
}
}
fn read_enable_pin(&self) -> bool {
if let Ok(enable_pin) = self.base.get_pin("ENABLE") {
let pin_guard = enable_pin.lock();
return pin_guard.read() == PinValue::High;
}
true // Enabled by default if pin doesn't exist or can't be read
}
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, // Always transition from HighZ
}
}
fn perform_transition(&mut self) {
let new_state = match self.current_state {
PinValue::High => PinValue::Low,
PinValue::Low => PinValue::High,
PinValue::HighZ => PinValue::Low, // Start with Low from HighZ
};
self.current_state = new_state;
self.set_clock_output(new_state);
self.last_transition = Instant::now();
}
}
impl Component for GenericClock {
fn name(&self) -> String {
self.base.name()
}
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
self.base.pins()
}
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
self.base.get_pin(name)
}
fn update(&mut self) {
// 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
if self.should_transition() {
self.perform_transition();
}
}
fn run(&mut self) {
self.base.set_running(true);
self.enable(); // Ensure clock is enabled when running
while self.is_running() {
self.update();
// Sleep for a short time to prevent busy waiting
// Use a fraction of the expected transition time for responsiveness
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);
}
// Clean up when stopping
self.disable();
}
fn stop(&mut self) {
self.base.set_running(false);
self.disable();
}
fn is_running(&self) -> bool {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
// Advanced clock features
impl GenericClock {}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_clock_creation() {
let clock = GenericClock::new("TEST_CLK".to_string(), 1_000_000.0); // 1MHz
assert_eq!(clock.name(), "TEST_CLK");
assert!(!clock.is_running());
}
#[test]
fn test_clock_timing_calculation() {
let _clock = GenericClock::new("TEST_CLK".to_string(), 1.0); // 1Hz
let period = GenericClock::frequency_to_duration(1.0);
assert_eq!(period, Duration::from_secs(1));
let high_freq = GenericClock::frequency_to_duration(1_000_000.0); // 1MHz
assert_eq!(high_freq, Duration::from_micros(1));
}
}