diff --git a/src/bus.rs b/src/bus.rs index 6b6c6ae..02db5ef 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,161 +1,401 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use crate::{Component, Pin, PinValue}; -pub trait BusDevice: Send { - /// Called on each simulation cycle to update device state based on pin states - fn update(&mut self); +use std::thread; +use std::time::{Duration, Instant}; - /// Connect this device to bus pins - fn connect_to_bus(&mut self, bus_pins: Vec>); +use crate::component::{BaseComponent, Component}; +use crate::pin::{Pin, PinValue, DriveStrength}; - /// Optional: report address range for debugging/monitoring - fn get_address_range(&self) -> Option> { - None - } - - /// Optional: get device name for debugging - fn get_name(&self) -> &str; -} -#[derive(Clone)] pub struct GenericBus { - base: crate::component::BaseComponent, - connected_devices: Vec>>, - bus_pins: HashMap>, // Named bus lines (like "A0", "D0", "R/W") + base: BaseComponent, + connected_pins: Vec>>, + bus_value: PinValue, + last_update: Instant, + settlement_time: Duration, + is_active: bool, } impl GenericBus { - pub fn new(name: String, pin_definitions: &[(&str, PinValue)]) -> Self { - let mut base = crate::component::BaseComponent::new(name.clone()); - let mut bus_pins = HashMap::new(); + pub fn new(name: String) -> Self { + // Bus typically has bidirectional data pins + let pin_names = vec!["DATA"]; + let pins = BaseComponent::create_pin_map(&pin_names, &name); - // Create bus pins - for (pin_name, initial_value) in pin_definitions { - let pin = base.add_pin( - pin_name.to_string(), - *initial_value, - ); - bus_pins.insert(pin_name.to_string(), pin); - } - - Self { - base, - connected_devices: Vec::new(), - bus_pins, + GenericBus { + base: BaseComponent::new(name, pins), + connected_pins: Vec::new(), + bus_value: PinValue::HighZ, + last_update: Instant::now(), + settlement_time: Duration::from_nanos(10), + is_active: true, } } - pub fn connect_device(&mut self, device: Arc>) { - // Get all bus pins and connect them to the device - let bus_pins: Vec> = self.bus_pins.values().cloned().collect(); - - if let Ok(mut device) = device.lock() { - device.connect_to_bus(bus_pins); - } - - self.connected_devices.push(device); + pub fn with_settlement_time(mut self, time: Duration) -> Self { + self.settlement_time = time; + self } - pub fn get_pin(&self, name: &str) -> Option> { - self.bus_pins.get(name).cloned() - } - - /// The key function: resolve pin conflicts and propagate values - fn resolve_bus_conflicts(&self) { - for (pin_name, bus_pin) in &self.bus_pins { - self.resolve_pin_state(bus_pin); + pub fn connect_pin(&mut self, pin: Arc>) -> Result<(), String> { + if !self.connected_pins.iter().any(|p| Arc::ptr_eq(p, &pin)) { + self.connected_pins.push(pin); + Ok(()) + } else { + Err("Pin already connected to bus".to_string()) } } - fn resolve_pin_state(&self, bus_pin: &Arc) { - let connections = bus_pin.connections.lock().unwrap(); + pub fn disconnect_pin(&mut self, pin: &Arc>) -> Result<(), String> { + let initial_len = self.connected_pins.len(); + self.connected_pins.retain(|p| !Arc::ptr_eq(p, pin)); - // Check if any device is driving the pin low (wired-AND behavior) - let mut any_driving_low = false; - let mut any_driving_high = false; - let mut any_driving = false; + if self.connected_pins.len() < initial_len { + Ok(()) + } else { + Err("Pin not found on bus".to_string()) + } + } - for connected_pin in connections.iter() { - let output_enable = connected_pin.output_enable.lock().unwrap(); - let value = connected_pin.value.read().unwrap(); + pub fn get_connected_pins(&self) -> &Vec>> { + &self.connected_pins + } - if *output_enable { - any_driving = true; - match *value { - PinValue::Low => any_driving_low = true, - PinValue::High => any_driving_high = true, - PinValue::HighZ => {} // High-Z doesn't drive + pub fn set_active(&mut self, active: bool) { + self.is_active = active; + if !active { + // Tri-state the bus when deactivated + self.bus_value = PinValue::HighZ; + self.propagate_bus_value(); + } + } + + pub fn is_active(&self) -> bool { + self.is_active + } + + pub fn get_bus_value(&self) -> PinValue { + self.bus_value + } + + fn read_bus_state(&self) -> PinValue { + if !self.is_active { + return PinValue::HighZ; + } + + if self.connected_pins.is_empty() { + return PinValue::HighZ; + } + + // Collect all active drivers from connected pins + let mut drivers = Vec::new(); + + for pin in &self.connected_pins { + if let Ok(pin_guard) = pin.lock() { + let pin_drivers = pin_guard.get_drivers(); + for (_, (value, strength)) in pin_drivers { + if *strength != DriveStrength::HighImpedance && *value != PinValue::HighZ { + drivers.push((*value, *strength)); + } } } } - // Determine the resolved bus state - let resolved_value = if any_driving_low { - // If any device pulls low, bus is low (wired-AND) + if drivers.is_empty() { + return PinValue::HighZ; + } + + // Find the strongest driver + let max_strength = drivers.iter() + .map(|(_, strength)| *strength) + .max() + .unwrap_or(DriveStrength::HighImpedance); + + if max_strength == DriveStrength::HighImpedance { + return PinValue::HighZ; + } + + // Get values from strongest drivers + let strong_drivers: Vec = drivers.iter() + .filter(|(_, strength)| *strength == max_strength) + .map(|(value, _)| *value) + .collect(); + + // Resolve conflicts: Low dominates + if strong_drivers.iter().any(|v| *v == PinValue::Low) { PinValue::Low - } else if any_driving_high && !any_driving_low { - // If devices are driving high but no one is pulling low + } else if strong_drivers.iter().any(|v| *v == PinValue::High) { PinValue::High - } else if any_driving { - // Mixed driving states (shouldn't happen in proper design) - PinValue::Low // Safe fallback: low wins } else { - // No one is driving - high impedance PinValue::HighZ - }; - - // Update the bus pin to reflect the resolved state - // BUT: the bus itself doesn't drive the pin - it just reflects the resolved state - // In real hardware, the bus pin's state emerges from the connected devices - drop(connections); - - // We need to update the pin's value without "driving" it - // This is tricky because our Pin struct couples value with output enable - // Let's create a helper method to update the resolved value - self.update_pin_resolved_value(bus_pin, resolved_value); + } } - fn update_pin_resolved_value(&self, pin: &Arc, value: PinValue) { - // This is a challenge: we want to set the pin's value without claiming to "drive" it - // We need to extend our Pin struct to support bus resolution - let mut current_value = pin.value.write().unwrap(); - *current_value = value; - // Note: we don't touch output_enable - the bus isn't driving, just reflecting + fn propagate_bus_value(&self) { + if !self.is_active { + return; + } + + for pin in &self.connected_pins { + if let Ok(mut pin_guard) = pin.lock() { + // The bus acts as a driver for connected pins + pin_guard.set_driver_with_strength( + Some(self.base.get_name().to_string()), + self.bus_value, + DriveStrength::Standard + ); + } + } + } + + pub fn simulate_bus_contention(&self) -> Result<(), String> { + // Simulate bus contention detection + let mut high_drivers = 0; + let mut low_drivers = 0; + + for pin in &self.connected_pins { + if let Ok(pin_guard) = pin.lock() { + let value = pin_guard.read(); + match value { + PinValue::High => high_drivers += 1, + PinValue::Low => low_drivers += 1, + PinValue::HighZ => {} + } + } + } + + if high_drivers > 0 && low_drivers > 0 { + // Bus contention detected + Err("Bus contention: multiple drivers conflict".to_string()) + } else { + Ok(()) + } } } impl Component for GenericBus { - fn name(&self) -> &str { + fn name(&self) -> String { self.base.name() } - fn pins(&self) -> &std::collections::HashMap> { + fn pins(&self) -> &HashMap>> { self.base.pins() } - fn get_pin(&self, name: &str) -> Option> { + fn get_pin(&self, name: &str) -> Result>, String> { self.base.get_pin(name) } - fn update(&mut self) -> Result<(), String> { - // Bus update cycle: - // 1. First, let all devices update based on current pin states - for device in &self.connected_devices { - if let Ok(mut device) = device.lock() { - device.update(); - } + fn update(&mut self) { + if !self.is_active { + return; } - // 2. Then resolve bus conflicts and propagate pin states - self.resolve_bus_conflicts(); + // Respect settlement timing + if self.last_update.elapsed() < self.settlement_time { + return; + } - Ok(()) + let new_bus_value = self.read_bus_state(); + + if new_bus_value != self.bus_value { + self.bus_value = new_bus_value; + self.propagate_bus_value(); + self.last_update = Instant::now(); + } + + // Check for bus contention (optional - could be expensive) + // if let Err(e) = self.simulate_bus_contention() { + // eprintln!("Bus contention warning: {}", e); + // } } fn run(&mut self) { - self.base.run(); + self.base.set_running(true); + + while self.is_running() { + self.update(); + thread::sleep(Duration::from_micros(1)); + } + } + + fn stop(&mut self) { + self.base.set_running(false); + self.set_active(false); + } + + fn is_running(&self) -> bool { + self.base.is_running() + } +} + +// Active bus implementation for test equipment +pub struct ActiveBus { + base: GenericBus, + test_pattern: Vec, + pattern_index: usize, + pattern_interval: Duration, + last_pattern_update: Instant, +} + +impl ActiveBus { + pub fn new(name: String) -> Self { + ActiveBus { + base: GenericBus::new(name), + test_pattern: Vec::new(), + pattern_index: 0, + pattern_interval: Duration::from_millis(1), + last_pattern_update: Instant::now(), + } + } + + pub fn set_test_pattern(&mut self, pattern: Vec) { + self.test_pattern = pattern; + self.pattern_index = 0; + } + + pub fn set_pattern_interval(&mut self, interval: Duration) { + self.pattern_interval = interval; + } + + pub fn drive_pattern(&mut self) { + if self.test_pattern.is_empty() { + return; + } + + if self.last_pattern_update.elapsed() >= self.pattern_interval { + if let Some(data_pin) = self.base.get_pin("DATA").ok() { + if let Ok(mut pin_guard) = data_pin.lock() { + let value = self.test_pattern[self.pattern_index]; + pin_guard.set_driver_with_strength( + Some(self.base.name() + "_pattern"), + value, + DriveStrength::Strong + ); + } + } + + self.pattern_index = (self.pattern_index + 1) % self.test_pattern.len(); + self.last_pattern_update = Instant::now(); + } + } +} + +impl Component for ActiveBus { + fn name(&self) -> String { + self.base.name() + } + + fn pins(&self) -> &HashMap>> { + self.base.pins() + } + + fn get_pin(&self, name: &str) -> Result>, String> { + self.base.get_pin(name) + } + + fn update(&mut self) { + self.base.update(); + self.drive_pattern(); + } + + fn run(&mut self) { + self.base.base.set_running(true); + + while self.is_running() { + self.update(); + thread::sleep(Duration::from_micros(1)); + } } fn stop(&mut self) { self.base.stop(); } + + fn is_running(&self) -> bool { + self.base.is_running() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bus_creation() { + let bus = GenericBus::new("TEST_BUS".to_string()); + assert_eq!(bus.name(), "TEST_BUS"); + assert!(!bus.is_running()); + assert!(bus.is_active()); + } + + #[test] + fn test_bus_pin_connection() { + let mut bus = GenericBus::new("TEST_BUS".to_string()); + let pin = Arc::new(Mutex::new(Pin::new("TEST_PIN".to_string()))); + + assert!(bus.connect_pin(pin.clone()).is_ok()); + assert_eq!(bus.get_connected_pins().len(), 1); + + // Try connecting same pin again + assert!(bus.connect_pin(pin.clone()).is_err()); + } + + #[test] + fn test_bus_value_propagation() { + let mut bus = GenericBus::new("TEST_BUS".to_string()); + let pin1 = Arc::new(Mutex::new(Pin::new("PIN1".to_string()))); + let pin2 = Arc::new(Mutex::new(Pin::new("PIN2".to_string()))); + + bus.connect_pin(pin1.clone()).unwrap(); + bus.connect_pin(pin2.clone()).unwrap(); + + // Drive pin1 high + { + let mut pin_guard = pin1.lock().unwrap(); + pin_guard.set_driver(Some("test".to_string()), PinValue::High); + } + + // Update bus + bus.update(); + + // Pin2 should see the high value + let pin2_guard = pin2.lock().unwrap(); + assert_eq!(pin2_guard.read(), PinValue::High); + } + + #[test] + fn test_bus_contention_detection() { + let mut bus = GenericBus::new("TEST_BUS".to_string()); + let pin1 = Arc::new(Mutex::new(Pin::new("PIN1".to_string()))); + let pin2 = Arc::new(Mutex::new(Pin::new("PIN2".to_string()))); + + bus.connect_pin(pin1.clone()).unwrap(); + bus.connect_pin(pin2.clone()).unwrap(); + + // Drive pins to conflicting states + { + let mut pin1_guard = pin1.lock().unwrap(); + pin1_guard.set_driver(Some("driver1".to_string()), PinValue::High); + } + { + let mut pin2_guard = pin2.lock().unwrap(); + pin2_guard.set_driver(Some("driver2".to_string()), PinValue::Low); + } + + // Should detect bus contention + assert!(bus.simulate_bus_contention().is_err()); + } + + #[test] + fn test_active_bus_pattern() { + let mut active_bus = ActiveBus::new("ACTIVE_BUS".to_string()); + let pattern = vec![PinValue::High, PinValue::Low, PinValue::High, PinValue::Low]; + + active_bus.set_test_pattern(pattern); + active_bus.set_pattern_interval(Duration::from_millis(10)); + + // Test that active bus can drive patterns + active_bus.drive_pattern(); + assert!(active_bus.is_running()); + } } \ No newline at end of file diff --git a/src/components/memory/generic_ram.rs b/src/components/memory/generic_ram.rs index f489b3c..adf782a 100644 --- a/src/components/memory/generic_ram.rs +++ b/src/components/memory/generic_ram.rs @@ -1,4 +1,3 @@ -use std::ops::Range; use std::sync::Arc; use crate::{Component, Pin, PinValue}; diff --git a/src/pin.rs b/src/pin.rs index 1fdb8a5..3df0b74 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -1,4 +1,4 @@ -use std::cmp::{Ordering, PartialOrd}; +use std::cmp::Ordering; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -44,7 +44,7 @@ impl PinValue { } } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DriveStrength { HighImpedance = 0, Weak = 1, @@ -52,6 +52,17 @@ pub enum DriveStrength { Strong = 3, } +impl Ord for DriveStrength { + fn cmp(&self, other: &Self) -> Ordering { + (*self as u8).cmp(&(*other as u8)) + } +} + +impl PartialOrd for DriveStrength { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} pub struct Pin { name: String, drivers: HashMap, @@ -60,11 +71,6 @@ pub struct Pin { settlement_time: Duration, connected_pins: Vec>>, } -impl PartialOrd for DriveStrength { - fn partial_cmp(&self, other: &Self) -> Option { - Some((*self as u8).cmp(&(*other as u8))) - } -} impl Pin { pub fn new(name: String) -> Self { Pin {