diff --git a/Cargo.toml b/Cargo.toml index ff937b9..c5d3f7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -serialport = "4.6.0" +tempfile = "3.3.0" \ No newline at end of file diff --git a/src/bus.rs b/src/bus.rs index 45dc429..6b6c6ae 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -33,7 +33,6 @@ impl GenericBus { let pin = base.add_pin( pin_name.to_string(), *initial_value, - false, // Bus doesn't drive pins by default ); bus_pins.insert(pin_name.to_string(), pin); } diff --git a/src/component.rs b/src/component.rs index fbee86b..90b2007 100644 --- a/src/component.rs +++ b/src/component.rs @@ -1,72 +1,255 @@ use std::collections::HashMap; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex, atomic::{AtomicBool, Ordering}}; use std::thread; use std::time::Duration; -use crate::pin::{Pin, PinValue}; -// Component trait -pub trait Component: Send { - fn name(&self) -> &str; - fn pins(&self) -> &HashMap>; - fn get_pin(&self, name: &str) -> Option>; - fn update(&mut self) -> Result<(), String>; + +use crate::pin::Pin; + +pub trait Component { + fn name(&self) -> String; + fn pins(&self) -> &HashMap>>; + fn get_pin(&self, name: &str) -> Result>, String>; + fn update(&mut self); fn run(&mut self); fn stop(&mut self); + + // Helper method to check if component is running + fn is_running(&self) -> bool; } -#[derive(Clone)] + pub struct BaseComponent { - pub name: String, - pub pins: HashMap>, - pub running: Arc>, + name: String, + pins: HashMap>>, + running: AtomicBool, } + impl BaseComponent { - pub fn new(name: String) -> Self { - Self { + pub fn new(name: String, pins: HashMap>>) -> Self { + BaseComponent { name, - pins: HashMap::new(), - running: Arc::new(Mutex::new(false)), + pins, + running: AtomicBool::new(false), } } - pub fn add_pin(&mut self, name: String, initial_value: PinValue, initial_output_enable: bool) -> Arc { - let pin = Arc::new(Pin { - name: name.clone(), - value: Arc::new(RwLock::new(initial_value)), - output_enable: Arc::new(Mutex::new(initial_output_enable)), - connections: Arc::new(Mutex::new(Vec::new())), - }); - self.pins.insert(name, pin.clone()); - pin - } -} -impl Component for BaseComponent { - fn name(&self) -> &str { + pub fn get_name(&self) -> &str { &self.name } - fn pins(&self) -> &HashMap> { + + pub fn is_running(&self) -> bool { + self.running.load(Ordering::SeqCst) + } + + pub fn set_running(&self, running: bool) { + self.running.store(running, Ordering::SeqCst); + } + + pub fn create_pin_map(pin_names: &[&str], component_name: &str) -> HashMap>> { + let mut pins = HashMap::new(); + for pin_name in pin_names { + pins.insert( + pin_name.to_string(), + Arc::new(Mutex::new(Pin::new(format!("{}_{}", component_name, pin_name)))) + ); + } + pins + } + + pub fn add_pin(&mut self, name: String, pin: Arc>) { + self.pins.insert(name, pin); + } + + pub fn remove_pin(&mut self, name: &str) -> Option>> { + self.pins.remove(name) + } + + // Helper method to get a mutable reference to pins (for components that need to modify pins) + pub fn pins_mut(&mut self) -> &mut HashMap>> { + &mut self.pins + } +} + +// Implement Clone manually without AtomicBool +impl Clone for BaseComponent { + fn clone(&self) -> Self { + // Create new pins with the same names but new Pin instances + let mut new_pins = HashMap::new(); + for (name, pin) in &self.pins { + let pin_guard = pin.lock().unwrap(); + new_pins.insert( + name.clone(), + Arc::new(Mutex::new(Pin::new(pin_guard.name().to_string()))) + ); + } + + BaseComponent { + name: self.name.clone(), + pins: new_pins, + running: AtomicBool::new(false), // Always start as not running when cloned + } + } +} + +// Default implementation for Component trait +impl Component for BaseComponent { + fn name(&self) -> String { + self.name.clone() + } + + fn pins(&self) -> &HashMap>> { &self.pins } - fn get_pin(&self, name: &str) -> Option> { - self.pins.get(name).cloned() - } - fn update(&mut self) -> Result<(), String> { - // Base implementation does nothing - Ok(()) - } - fn run(&mut self) { - *self.running.lock().unwrap() = true; - let running = self.running.clone(); - let mut self_clone = self.clone(); - thread::spawn(move || { - while *running.lock().unwrap() { - if let Err(e) = self_clone.update() { - eprintln!("Error in {}: {}", self_clone.name, e); - } - thread::sleep(Duration::from_micros(10)); - } - }); + fn get_pin(&self, name: &str) -> Result>, String> { + self.pins.get(name) + .cloned() + .ok_or_else(|| format!("Pin {} not found on component {}", name, self.name)) } + + fn update(&mut self) { + // Base implementation does nothing - components should override this + // This is where pin state changes would be processed + } + + fn run(&mut self) { + self.set_running(true); + + // Simple run loop - components should override this for their specific behavior + while self.is_running() { + self.update(); + thread::sleep(Duration::from_micros(10)); // Prevent busy waiting + } + } + fn stop(&mut self) { - *self.running.lock().unwrap() = false; + self.set_running(false); + } + + fn is_running(&self) -> bool { + self.running.load(Ordering::SeqCst) + } +} + +// Helper function to create component pins easily +pub fn create_component_pins(component_name: &str, pin_definitions: &[(&str, &str)]) -> HashMap>> { + let mut pins = HashMap::new(); + + for (pin_name, pin_type) in pin_definitions { + let full_pin_name = format!("{}_{}", component_name, pin_name); + let mut pin = Pin::new(full_pin_name); + + // Set initial state based on pin type + match *pin_type { + "input" | "in" => { + // Input pins start as HighZ (waiting for input) + pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::HighZ); + } + "output" | "out" => { + // Output pins start as HighZ (tri-state) + pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::HighZ); + } + "power" => { + // Power pins start high + pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::High); + } + "ground" => { + // Ground pins start low + pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::Low); + } + _ => { + // Default to HighZ + pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::HighZ); + } + } + + pins.insert(pin_name.to_string(), Arc::new(Mutex::new(pin))); + } + + pins +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_base_component_creation() { + let pins = BaseComponent::create_pin_map(&["A0", "A1", "D0", "D1"], "TEST"); + let component = BaseComponent::new("TEST_COMP".to_string(), pins); + + assert_eq!(component.name(), "TEST_COMP"); + assert!(!component.is_running()); + } + + #[test] + fn test_base_component_pin_management() { + let pins = BaseComponent::create_pin_map(&["A0", "D0"], "TEST"); + let mut component = BaseComponent::new("TEST_COMP".to_string(), pins); + + // Test getting existing pin + assert!(component.get_pin("A0").is_ok()); + + // Test getting non-existent pin + assert!(component.get_pin("NON_EXISTENT").is_err()); + + // Test adding new pin + let new_pin = Arc::new(Mutex::new(Pin::new("TEST_NEW".to_string()))); + component.add_pin("NEW_PIN".to_string(), new_pin); + assert!(component.get_pin("NEW_PIN").is_ok()); + + // Test removing pin + let removed = component.remove_pin("A0"); + assert!(removed.is_some()); + assert!(component.get_pin("A0").is_err()); + } + + #[test] + fn test_base_component_lifecycle() { + let pins = BaseComponent::create_pin_map(&["TEST"], "TEST"); + let mut component = BaseComponent::new("TEST_COMP".to_string(), pins); + + assert!(!component.is_running()); + + // Note: In real usage, run() would be called in a separate thread + component.set_running(true); + assert!(component.is_running()); + + component.stop(); + assert!(!component.is_running()); + } + + #[test] + fn test_base_component_clone() { + let pins = BaseComponent::create_pin_map(&["A0", "D0"], "ORIG"); + let original = BaseComponent::new("ORIGINAL".to_string(), pins); + original.set_running(true); + + let cloned = original.clone(); + + // Cloned component should have same name but not running state + assert_eq!(cloned.name(), "ORIGINAL"); + assert!(!cloned.is_running()); // Clone should not be running + + // Pins should exist but be different instances + assert!(cloned.get_pin("A0").is_ok()); + assert!(cloned.get_pin("D0").is_ok()); + } + + #[test] + fn test_create_component_pins() { + let pin_definitions = vec![ + ("A0", "input"), + ("D0", "output"), + ("VCC", "power"), + ("GND", "ground"), + ]; + + let pins = create_component_pins("TEST", &pin_definitions); + + assert_eq!(pins.len(), 4); + assert!(pins.contains_key("A0")); + assert!(pins.contains_key("D0")); + assert!(pins.contains_key("VCC")); + assert!(pins.contains_key("GND")); } } \ No newline at end of file diff --git a/src/components/memory/generic_ram.rs b/src/components/memory/generic_ram.rs index 62eff66..f489b3c 100644 --- a/src/components/memory/generic_ram.rs +++ b/src/components/memory/generic_ram.rs @@ -29,19 +29,19 @@ GenericRam // Add address pins for i in 0..ADDR_WIDTH { - base.add_pin(format!("addr_{}", i), PinValue::Low, false); + base.add_pin(format!("addr_{}", i), PinValue::Low); } // Add data pins for i in 0..DATA_WIDTH { - base.add_pin(format!("data_{}", i), PinValue::HighZ, false); + base.add_pin(format!("data_{}", i), PinValue::HighZ); } // Add control pins - base.add_pin("cs".to_string(), PinValue::High, false); // Chip Select (active low) - base.add_pin("we".to_string(), PinValue::High, false); // Write Enable (active low) - base.add_pin("oe".to_string(), PinValue::High, false); // Output Enable (active low) - base.add_pin("clk".to_string(), PinValue::Low, false); // Clock for synchronization + base.add_pin("cs".to_string(), PinValue::High); // Chip Select (active low) + base.add_pin("we".to_string(), PinValue::High); // Write Enable (active low) + base.add_pin("oe".to_string(), PinValue::High); // Output Enable (active low) + base.add_pin("clk".to_string(), PinValue::Low); // Clock for synchronization // Calculate address mask based on size let address_mask = ((1 << ADDR_WIDTH) - 1) as u64; diff --git a/src/components/memory/generic_rom.rs b/src/components/memory/generic_rom.rs index 977a3d0..1ecef03 100644 --- a/src/components/memory/generic_rom.rs +++ b/src/components/memory/generic_rom.rs @@ -1,118 +1,332 @@ use std::collections::HashMap; -use std::ops::Range; -use std::sync::Arc; -use crate::{Component, Pin, PinValue}; -use crate::bus::BusDevice; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; -pub struct GenericRom { - pub(crate) base: crate::component::BaseComponent, - memory: HashMap, // Generic memory storage +use crate::component::{BaseComponent, Component}; +use crate::pin::{Pin, PinValue}; + +pub struct GenericRom { + pub(crate) base: BaseComponent, + memory: Vec, + address_width: usize, data_width: usize, - address_range: std::ops::Range, - pub latched_address: () + last_address: u32, + access_time: Duration, + last_access: Instant, } -impl GenericRom { - pub fn new(name: String, base_address: u64) -> Self { - let mut base = crate::component::BaseComponent::new(name); +impl GenericRom { + pub fn new(name: String, size: usize, address_width: usize, data_width: usize) -> Self { + let mut pins = HashMap::new(); - // Add data pins based on width - for i in 0..DATA_WIDTH { - base.add_pin(format!("data_{}", i), PinValue::HighZ, false); + // Create address pins (A0, A1, A2, ...) + for i in 0..address_width { + pins.insert(format!("A{}", i), Arc::new(Mutex::new(Pin::new(format!("{}_A{}", name, i))))); } - // Add control pins - base.add_pin("cs".to_string(), PinValue::Low, false); - base.add_pin("oe".to_string(), PinValue::Low, false); + // Create data pins (D0, D1, D2, ...) + for i in 0..data_width { + pins.insert(format!("D{}", i), Arc::new(Mutex::new(Pin::new(format!("{}_D{}", name, i))))); + } - let address_range = base_address..base_address + SIZE as u64; + // Control pins + pins.insert("CS".to_string(), Arc::new(Mutex::new(Pin::new(format!("{}_CS", name))))); + pins.insert("OE".to_string(), Arc::new(Mutex::new(Pin::new(format!("{}_OE", name))))); - Self { - base, - memory: HashMap::new(), - data_width: DATA_WIDTH, - address_range, - latched_address: (), + let memory = vec![0u8; size]; + + GenericRom { + base: BaseComponent::new(name, pins), + memory, + address_width, + data_width, + last_address: 0, + access_time: Duration::from_nanos(100), // 100ns access time + last_access: Instant::now(), } } - pub fn load_data(&mut self, data: &[u64]) { - for (i, &word) in data.iter().enumerate() { - if i < SIZE { - self.memory.insert(self.address_range.start + i as u64, word); + pub fn load_data(&mut self, data: Vec, offset: usize) -> Result<(), String> { + if offset + data.len() > self.memory.len() { + return Err("Data exceeds ROM capacity".to_string()); + } + + self.memory[offset..offset + data.len()].copy_from_slice(&data); + Ok(()) + } + + pub fn load_from_hex(&mut self, hex_data: &str, offset: usize) -> Result<(), String> { + let bytes: Result, _> = hex_data + .split_whitespace() + .map(|s| u8::from_str_radix(s, 16)) + .collect(); + + match bytes { + Ok(data) => self.load_data(data, offset), + Err(_) => Err("Invalid hex data".to_string()), + } + } + pub fn load_from_binary_file>(&mut self, path: P, offset: usize) -> Result<(), String> { + let path_ref = path.as_ref(); + + // Check if file exists + if !path_ref.exists() { + return Err(format!("File not found: {}", path_ref.display())); + } + + // Check if offset is within bounds + if offset >= self.memory.len() { + return Err(format!("Offset {} exceeds ROM size {}", offset, self.memory.len())); + } + + let mut file = File::open(path_ref) + .map_err(|e| format!("Failed to open file: {}", e))?; + + // Get file size + let metadata = file.metadata() + .map_err(|e| format!("Failed to get file metadata: {}", e))?; + let file_size = metadata.len() as usize; + + // Check if file fits in ROM + if offset + file_size > self.memory.len() { + return Err(format!( + "File too large: offset {} + file size {} > ROM size {}", + offset, file_size, self.memory.len() + )); + } + + // Read file into buffer + let mut buffer = vec![0u8; file_size]; + file.read_exact(&mut buffer) + .map_err(|e| format!("Failed to read file: {}", e))?; + + // Copy into ROM memory + self.memory[offset..offset + file_size].copy_from_slice(&buffer); + + Ok(()) + } + + pub fn load_from_binary(&mut self, data: &[u8], offset: usize) -> Result<(), String> { + if offset + data.len() > self.memory.len() { + return Err(format!( + "Data exceeds ROM capacity: offset {} + data length {} > ROM size {}", + offset, data.len(), self.memory.len() + )); + } + + self.memory[offset..offset + data.len()].copy_from_slice(data); + Ok(()) + } + + + fn read_address(&self) -> u32 { + let mut address = 0; + + for i in 0..self.address_width { + if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) { + if let Ok(pin_guard) = pin.lock() { + if pin_guard.read() == PinValue::High { + address |= 1 << i; + } + } } } - } - pub fn is_selected(&self, address: u64) -> bool { - if let Some(cs_pin) = self.base.get_pin("cs") { - cs_pin.read() == PinValue::High && self.address_range.contains(&address) - } else { - false - } - } - fn get_current_address(&self) -> u64 { - // In a real system, this would read address from connected address pins - // For now, return a default address - 0 + + address } - fn output_data(&self, data: u64) { - for i in 0..DATA_WIDTH { - if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { - let bit_value = (data >> i) & 1; - let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; - pin.write(pin_value, true); // Drive the pin + pub(crate) fn is_selected(&self) -> bool { + // Check Chip Select (active low by convention) + if let Ok(cs_pin) = self.base.get_pin("CS") { + if let Ok(cs_guard) = cs_pin.lock() { + if cs_guard.read() == PinValue::High { + return false; // Not selected + } } } + true // Selected when CS is low or not connected } - fn set_data_pins_high_z(&self) { - for i in 0..DATA_WIDTH { - if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) { - pin.write(PinValue::HighZ, false); // High impedance, not driving + fn output_enabled(&self) -> bool { + // Check Output Enable (active low by convention) + if let Ok(oe_pin) = self.base.get_pin("OE") { + if let Ok(oe_guard) = oe_pin.lock() { + if oe_guard.read() == PinValue::High { + return false; // Output disabled + } + } + } + true // Enabled when OE is low or not connected + } + + pub(crate) fn output_data(&self, data: u8) { + // Only drive data pins if selected and output enabled + if !self.is_selected() || !self.output_enabled() { + // Tri-state the outputs + for i in 0..self.data_width { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ); + } + } + } + return; + } + + // Drive data pins according to the data value + for i in 0..self.data_width { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + let bit_value = (data >> i) & 1; + let pin_value = if bit_value == 1 { + PinValue::High + } else { + PinValue::Low + }; + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value); + } } } } } -impl BusDevice for GenericRom { +impl Component for GenericRom { + 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) { - // Check if we're selected and output is enabled - if let (Some(cs_pin), Some(oe_pin)) = (self.base.get_pin("cs"), self.base.get_pin("oe")) { - let is_selected = cs_pin.read() == PinValue::High; - let output_enabled = oe_pin.read() == PinValue::Low; // Active low + // Respect access timing + if self.last_access.elapsed() < self.access_time { + return; + } - if is_selected && output_enabled { - // Read address from address pins (simplified - in real system, address would come from bus) - // For now, we'll use a dummy address - this would be connected to address pins in a real system - let address = self.get_current_address(); + let current_address = self.read_address(); - // Read data from ROM - let data = self.read(address); - - // Output data to data pins - self.output_data(data); + // Only process if address changed or we need to update outputs + if current_address != self.last_address || !self.is_selected() || !self.output_enabled() { + if self.is_selected() && self.output_enabled() { + // Read from memory (handle address bounds) + if (current_address as usize) < self.memory.len() { + let data = self.memory[current_address as usize]; + self.output_data(data); + } else { + // Address out of bounds - tri-state outputs + for i in 0..self.data_width { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ); + } + } + } + } } else { - // Put data pins in high impedance when not selected - self.set_data_pins_high_z(); + // Not selected or output disabled - tri-state outputs + for i in 0..self.data_width { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ); + } + } + } + } + + self.last_address = current_address; + self.last_access = Instant::now(); + } + } + + fn run(&mut self) { + self.base.set_running(true); + + while self.base.is_running() { + self.update(); + thread::sleep(Duration::from_micros(1)); // Small delay to prevent busy waiting + } + } + + fn stop(&mut self) { + self.base.set_running(false); + + // Tri-state all outputs when stopped + for i in 0..self.data_width { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ); + } } } } - fn connect_to_bus(&mut self, bus_pins: Vec>) { - // Connect data pins - for (i, bus_pin) in bus_pins.iter().enumerate() { - if let Some(rom_pin) = self.base.get_pin(&format!("data_{}", i)) { - crate::connection::connect_pins(&rom_pin, bus_pin).unwrap(); - } - } + fn is_running(&self) -> bool { + self.base.is_running() + } +} + +#[cfg(test)] +mod tests { + use std::fs; + use tempfile::NamedTempFile; + use super::*; + + #[test] + fn test_rom_creation() { + let rom = GenericRom::new("TEST_ROM".to_string(), 1024, 10, 8); + assert_eq!(rom.name(), "TEST_ROM"); + assert_eq!(rom.memory.len(), 1024); } - fn get_address_range(&self) -> Option> { - Some(self.address_range.clone()) + #[test] + fn test_rom_file_loading() { + let mut rom = GenericRom::new("TEST_ROM".to_string(), 256, 8, 8); + + // Create a temporary file with test data + let test_data = vec![0x12, 0x34, 0x56, 0x78]; + let temp_file = NamedTempFile::new().unwrap(); + fs::write(temp_file.path(), &test_data).unwrap(); + + assert!(rom.load_from_binary_file(temp_file.path(), 0).is_ok()); + + assert_eq!(rom.memory[0], 0x12); + assert_eq!(rom.memory[1], 0x34); + assert_eq!(rom.memory[2], 0x56); + assert_eq!(rom.memory[3], 0x78); } - fn get_name(&self) -> &str { - todo!() + #[test] + fn test_rom_data_loading() { + let mut rom = GenericRom::new("TEST_ROM".to_string(), 256, 8, 8); + + let test_data = vec![0x12, 0x34, 0x56, 0x78]; + assert!(rom.load_data(test_data.clone(), 0).is_ok()); + + assert_eq!(rom.memory[0], 0x12); + assert_eq!(rom.memory[1], 0x34); + assert_eq!(rom.memory[2], 0x56); + assert_eq!(rom.memory[3], 0x78); + } + + #[test] + fn test_rom_hex_loading() { + let mut rom = GenericRom::new("TEST_ROM".to_string(), 256, 8, 8); + + assert!(rom.load_from_hex("12 34 56 78", 0).is_ok()); + + assert_eq!(rom.memory[0], 0x12); + assert_eq!(rom.memory[1], 0x34); + assert_eq!(rom.memory[2], 0x56); + assert_eq!(rom.memory[3], 0x78); } } \ No newline at end of file diff --git a/src/components/memory/intel_4001.rs b/src/components/memory/intel_4001.rs index 400713c..09a129b 100644 --- a/src/components/memory/intel_4001.rs +++ b/src/components/memory/intel_4001.rs @@ -3,7 +3,7 @@ use crate::{Component, PinValue}; use crate::systems::intel_mcs_4::MCS4Phase; // 4001 ROM: 256 bytes, 4-bit data width -pub type Intel4001 = GenericRom<256, 4>; +pub type Intel4001 = GenericRom; impl Intel4001 { fn update(&mut self) -> Result<(), String> { // Read the current bus phase from the pins @@ -21,7 +21,7 @@ impl Intel4001 { } MCS4Phase::DataRead => { if self.is_selected() { - self.output_data(); + self.output_data(/* u8 */); } } _ => { diff --git a/src/connection.rs b/src/connection.rs index 38c5d05..994df25 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,12 +1,271 @@ -use std::sync::Arc; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; use crate::pin::Pin; -pub fn connect_pins(source: &Arc, destination: &Arc) -> Result<(), String> { - let mut source_connections = source.connections.lock().unwrap(); - source_connections.push(destination.clone()); +/// Manages electrical connections between pins +pub struct ConnectionManager { + connections: HashMap>, // pin_name -> connected_pin_names +} - let mut dest_connections = destination.connections.lock().unwrap(); - dest_connections.push(source.clone()); +impl ConnectionManager { + pub fn new() -> Self { + ConnectionManager { + connections: HashMap::new(), + } + } - Ok(()) + /// Connect two pins bidirectionally + pub fn connect_pins(&mut self, pin1: Arc>, pin2: Arc>) -> Result<(), String> { + let pin1_name = { + let p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?; + p1.name().to_string() + }; + + let pin2_name = { + let p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?; + p2.name().to_string() + }; + + // Connect pin1 to pin2 + { + let mut p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?; + p1.connect_to(pin2.clone()); + } + + // Connect pin2 to pin1 (bidirectional) + { + let mut p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?; + p2.connect_to(pin1.clone()); + } + + // Update connection graph + self.connections.entry(pin1_name.clone()) + .or_insert_with(Vec::new) + .push(pin2_name.clone()); + + self.connections.entry(pin2_name) + .or_insert_with(Vec::new) + .push(pin1_name); + + Ok(()) + } + + /// Connect multiple pins together (bus connection) + pub fn connect_bus(&mut self, pins: &[Arc>]) -> Result<(), String> { + if pins.len() < 2 { + return Err("Need at least 2 pins for bus connection".to_string()); + } + + for i in 0..pins.len() { + for j in i+1..pins.len() { + self.connect_pins(pins[i].clone(), pins[j].clone())?; + } + } + + Ok(()) + } + + /// Disconnect two pins + pub fn disconnect_pins(&mut self, pin1: &Arc>, pin2: &Arc>) -> Result<(), String> { + let pin1_name = { + let p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?; + p1.name().to_string() + }; + + let pin2_name = { + let p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?; + p2.name().to_string() + }; + + // Disconnect pin1 from pin2 + { + let mut p1 = pin1.lock().map_err(|e| format!("Failed to lock pin1: {}", e))?; + p1.disconnect_from_pin(pin2); + } + + // Disconnect pin2 from pin1 + { + let mut p2 = pin2.lock().map_err(|e| format!("Failed to lock pin2: {}", e))?; + p2.disconnect_from_pin(pin1); + } + + // Update connection graph + if let Some(connections) = self.connections.get_mut(&pin1_name) { + connections.retain(|name| name != &pin2_name); + } + + if let Some(connections) = self.connections.get_mut(&pin2_name) { + connections.retain(|name| name != &pin1_name); + } + + Ok(()) + } + + /// Get all pins connected to a given pin + pub fn get_connected_pins(&self, pin_name: &str) -> Option<&Vec> { + self.connections.get(pin_name) + } + + /// Check if two pins are connected + pub fn are_connected(&self, pin1_name: &str, pin2_name: &str) -> bool { + self.connections.get(pin1_name) + .map(|connections| connections.contains(&pin2_name.to_string())) + .unwrap_or(false) + } + + /// Disconnect all pins from a given pin + pub fn disconnect_all(&mut self, pin: &Arc>) -> Result<(), String> { + let pin_name = { + let p = pin.lock().map_err(|e| format!("Failed to lock pin: {}", e))?; + p.name().to_string() + }; + + // Get all connected pin names before disconnecting + let connected_names: Vec = self.connections + .get(&pin_name) + .map(|v| v.clone()) + .unwrap_or_default(); + + // Disconnect from each connected pin + for connected_name in &connected_names { + // We need to find the actual Pin objects to disconnect them + // For now, we'll handle this through the connection graph + if let Some(connections) = self.connections.get_mut(connected_name) { + connections.retain(|name| name != &pin_name); + } + } + + // Clear all connections for this pin + if let Some(connections) = self.connections.get_mut(&pin_name) { + connections.clear(); + } + + // Clear the pin's internal connections + { + let mut p = pin.lock().map_err(|e| format!("Failed to lock pin: {}", e))?; + p.clear_connections(); + } + + Ok(()) + } + + /// Get a list of all connection groups (useful for debugging) + pub fn get_connection_groups(&self) -> Vec> { + use std::collections::{HashSet, VecDeque}; + + let mut visited = HashSet::new(); + let mut groups = Vec::new(); + + for pin_name in self.connections.keys() { + if !visited.contains(pin_name) { + let mut group = Vec::new(); + let mut queue = VecDeque::new(); + queue.push_back(pin_name.clone()); + + while let Some(current) = queue.pop_front() { + if visited.insert(current.clone()) { + group.push(current.clone()); + + if let Some(neighbors) = self.connections.get(¤t) { + for neighbor in neighbors { + if !visited.contains(neighbor) { + queue.push_back(neighbor.clone()); + } + } + } + } + } + + if group.len() > 1 { + groups.push(group); + } + } + } + + groups + } +} + +/// Helper function for quick pin connections +pub fn connect_pins(pin1: Arc>, pin2: Arc>) -> Result<(), String> { + let mut manager = ConnectionManager::new(); + manager.connect_pins(pin1, pin2) +} + +/// Helper function for bus connections +pub fn connect_bus(pins: &[Arc>]) -> Result<(), String> { + let mut manager = ConnectionManager::new(); + manager.connect_bus(pins) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pin_connection() { + let pin1 = Arc::new(Mutex::new(Pin::new("PIN1".to_string()))); + let pin2 = Arc::new(Mutex::new(Pin::new("PIN2".to_string()))); + + assert!(connect_pins(pin1.clone(), pin2.clone()).is_ok()); + + // Verify connection + let p1 = pin1.lock().unwrap(); + let p2 = pin2.lock().unwrap(); + + assert_eq!(p1.get_connection_count(), 1); + assert_eq!(p2.get_connection_count(), 1); + assert!(p1.is_connected_to(&pin2)); + assert!(p2.is_connected_to(&pin1)); + } + + #[test] + fn test_pin_disconnection() { + let pin1 = Arc::new(Mutex::new(Pin::new("PIN1".to_string()))); + let pin2 = Arc::new(Mutex::new(Pin::new("PIN2".to_string()))); + + let mut manager = ConnectionManager::new(); + manager.connect_pins(pin1.clone(), pin2.clone()).unwrap(); + + manager.disconnect_pins(&pin1, &pin2).unwrap(); + + let p1 = pin1.lock().unwrap(); + let p2 = pin2.lock().unwrap(); + + assert_eq!(p1.get_connection_count(), 0); + assert_eq!(p2.get_connection_count(), 0); + assert!(!p1.is_connected_to(&pin2)); + assert!(!p2.is_connected_to(&pin1)); + } + + #[test] + fn test_bus_connection() { + let pins: Vec<_> = (0..4) + .map(|i| Arc::new(Mutex::new(Pin::new(format!("PIN{}", i))))) + .collect(); + + assert!(connect_bus(&pins).is_ok()); + + // Verify all pins are connected to each other + for i in 0..pins.len() { + let pin = pins[i].lock().unwrap(); + assert_eq!(pin.get_connection_count(), pins.len() - 1); + } + } + + #[test] + fn test_disconnect_all() { + let pin1 = Arc::new(Mutex::new(Pin::new("PIN1".to_string()))); + let pin2 = Arc::new(Mutex::new(Pin::new("PIN2".to_string()))); + let pin3 = Arc::new(Mutex::new(Pin::new("PIN3".to_string()))); + + let mut manager = ConnectionManager::new(); + manager.connect_pins(pin1.clone(), pin2.clone()).unwrap(); + manager.connect_pins(pin1.clone(), pin3.clone()).unwrap(); + + manager.disconnect_all(&pin1).unwrap(); + + let p1 = pin1.lock().unwrap(); + assert_eq!(p1.get_connection_count(), 0); + } } \ No newline at end of file diff --git a/src/pin.rs b/src/pin.rs index 7c8bbaf..1fdb8a5 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -1,50 +1,363 @@ -use std::sync::{Arc, Mutex, RwLock}; +use std::cmp::{Ordering, PartialOrd}; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; -// Pin value representation #[derive(Debug, Clone, Copy, PartialEq)] pub enum PinValue { - High, Low, - HighZ, // High impedance (disconnected) + High, + HighZ, // Tri-state +} + +impl PinValue { + pub fn to_str(&self) -> &'static str { + match self { + PinValue::Low => "Low", + PinValue::High => "High", + PinValue::HighZ => "HighZ", + } + } + + pub fn to_char(&self) -> char { + match self { + PinValue::Low => '0', + PinValue::High => '1', + PinValue::HighZ => 'Z', + } + } + + pub fn from_bool(value: bool) -> Self { + if value { + PinValue::High + } else { + PinValue::Low + } + } + + pub fn to_bool(&self) -> Option { + match self { + PinValue::Low => Some(false), + PinValue::High => Some(true), + PinValue::HighZ => None, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum DriveStrength { + HighImpedance = 0, + Weak = 1, + Standard = 2, + Strong = 3, } -#[derive(Clone)] pub struct Pin { - pub name: String, - pub value: Arc>, - pub output_enable: Arc>, - pub connections: Arc>>>, + name: String, + drivers: HashMap, + settled_value: PinValue, + last_update: Instant, + 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 { - // Get the effective value considering all connected pins + pub fn new(name: String) -> Self { + Pin { + name, + drivers: HashMap::new(), + settled_value: PinValue::HighZ, + last_update: Instant::now(), + settlement_time: Duration::from_nanos(10), // 10ns settlement time + connected_pins: Vec::new(), + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn set_driver(&mut self, driver_name: Option, value: PinValue) { + let strength = DriveStrength::Standard; + self.set_driver_with_strength(driver_name, value, strength); + } + + pub fn set_driver_with_strength(&mut self, driver_name: Option, value: PinValue, strength: DriveStrength) { + let driver_id = driver_name.unwrap_or_else(|| "anonymous".to_string()); + + if value == PinValue::HighZ && strength == DriveStrength::HighImpedance { + self.drivers.remove(&driver_id); + } else { + self.drivers.insert(driver_id, (value, strength)); + } + + self.last_update = Instant::now(); + self.recalculate_value(); + } + + pub fn remove_driver(&mut self, driver_name: &str) { + self.drivers.remove(driver_name); + self.last_update = Instant::now(); + self.recalculate_value(); + } + pub fn read(&self) -> PinValue { - let connections = self.connections.lock().unwrap(); + // If we're still within settlement time, return the previous value + if self.last_update.elapsed() < self.settlement_time { + return self.settled_value; + } + self.settled_value + } - let mut found_high_pin = false; + pub fn read_immediate(&self) -> PinValue { + self.settled_value + } - // Check if any connected pin is actively driving low - 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_drivers(&self) -> &HashMap { + &self.drivers + } + pub fn is_settled(&self) -> bool { + self.last_update.elapsed() >= self.settlement_time + } - if *output_enable && *value == PinValue::Low { - return PinValue::Low; - } else if *output_enable && *value == PinValue::High { - found_high_pin = true; + pub fn get_settlement_time(&self) -> Duration { + self.settlement_time + } + + pub fn set_settlement_time(&mut self, time: Duration) { + self.settlement_time = time; + } + + pub fn connect_to(&mut self, other_pin: Arc>) { + if !self.connected_pins.iter().any(|p| Arc::ptr_eq(p, &other_pin)) { + self.connected_pins.push(other_pin); + } + } + + pub fn disconnect_from(&mut self, other_pin: &Arc>) { + self.connected_pins.retain(|p| !Arc::ptr_eq(p, other_pin)); + } + + pub fn get_connected_pins(&self) -> &Vec>> { + &self.connected_pins + } + + pub fn propagate(&self) { + for connected_pin in &self.connected_pins { + if let Ok(mut pin) = connected_pin.lock() { + // Copy our drivers to the connected pin (simulate electrical connection) + let mut new_drivers = self.drivers.clone(); + + // Merge with existing drivers on the connected pin + for (driver, value) in &pin.drivers { + new_drivers.insert(driver.clone(), *value); + } + + pin.drivers = new_drivers; + pin.last_update = Instant::now(); + pin.recalculate_value(); } } - - // If no one is driving, return HighZ - if found_high_pin { - PinValue::Low - } else { - PinValue::HighZ - } } - pub fn write(&self, value: PinValue, output_enable: bool) { - *self.value.write().unwrap() = value; - *self.output_enable.lock().unwrap() = output_enable; + fn recalculate_value(&mut self) { + if self.drivers.is_empty() { + self.settled_value = PinValue::HighZ; + return; + } + + // Find the strongest driver strength manually + let mut max_strength = DriveStrength::HighImpedance; + for (_, strength) in self.drivers.values() { + if *strength > max_strength { + max_strength = *strength; + } + } + if max_strength == DriveStrength::HighImpedance { + self.settled_value = PinValue::HighZ; + return; + } + + // Get all drivers with the strongest strength + let strong_drivers: Vec = self.drivers.values() + .filter(|(_, strength)| *strength == max_strength) + .map(|(value, _)| *value) + .collect(); + + // Resolve conflicts: Low dominates, then High, HighZ is ignored + if strong_drivers.iter().any(|v| *v == PinValue::Low) { + self.settled_value = PinValue::Low; + } else if strong_drivers.iter().any(|v| *v == PinValue::High) { + self.settled_value = PinValue::High; + } else { + self.settled_value = PinValue::HighZ; + } + + // Propagate to connected pins + self.propagate(); + } + pub fn clear_connections(&mut self) { + self.connected_pins.clear(); + } + + pub fn disconnect_from_pin(&mut self, other_pin: &Arc>) { + self.connected_pins.retain(|p| !Arc::ptr_eq(p, other_pin)); + } + + pub fn is_connected_to(&self, other_pin: &Arc>) -> bool { + self.connected_pins.iter().any(|p| Arc::ptr_eq(p, other_pin)) + } + + pub fn get_connection_count(&self) -> usize { + self.connected_pins.len() + } + + pub fn to_string(&self) -> String { + format!("{}: {}", self.name, self.settled_value.to_str()) } } + +impl Default for Pin { + fn default() -> Self { + Pin::new("unnamed".to_string()) + } +} + +// Helper implementations for easier testing and debugging +impl std::fmt::Display for PinValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_str()) + } +} + +impl std::fmt::Display for Pin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.name, self.settled_value)?; + + if !self.drivers.is_empty() { + write!(f, " [drivers: ")?; + for (i, (driver, (value, strength))) in self.drivers.iter().enumerate() { + if i > 0 { write!(f, ", ")?; } + write!(f, "{}={}({})", driver, value.to_char(), *strength as u8)?; + } + write!(f, "]")?; + } + + if !self.is_settled() { + write!(f, " (settling)")?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + use std::sync::Mutex; + + #[test] + fn test_pin_creation() { + let pin = Pin::new("TEST".to_string()); + assert_eq!(pin.name(), "TEST"); + assert_eq!(pin.read(), PinValue::HighZ); + assert!(pin.drivers.is_empty()); + } + + #[test] + fn test_pin_driving() { + let mut pin = Pin::new("TEST".to_string()); + + // Drive high + pin.set_driver(Some("driver1".to_string()), PinValue::High); + assert_eq!(pin.read(), PinValue::High); + + // Drive low - should override + pin.set_driver(Some("driver2".to_string()), PinValue::Low); + assert_eq!(pin.read(), PinValue::Low); + + // Remove driver + pin.remove_driver("driver2"); + assert_eq!(pin.read(), PinValue::High); + } + + #[test] + fn test_pin_strength() { + let mut pin = Pin::new("TEST".to_string()); + + // Weak high + pin.set_driver_with_strength( + Some("weak".to_string()), + PinValue::High, + DriveStrength::Weak + ); + assert_eq!(pin.read(), PinValue::High); + + // Strong low should override weak high + pin.set_driver_with_strength( + Some("strong".to_string()), + PinValue::Low, + DriveStrength::Strong + ); + assert_eq!(pin.read(), PinValue::Low); + } + + #[test] + fn test_pin_connection() { + let pin1 = Arc::new(Mutex::new(Pin::new("PIN1".to_string()))); + let pin2 = Arc::new(Mutex::new(Pin::new("PIN2".to_string()))); + + // Connect pins + { + let mut p1 = pin1.lock().unwrap(); + p1.connect_to(pin2.clone()); + } + + // Drive pin1 + { + let mut p1 = pin1.lock().unwrap(); + p1.set_driver(Some("test".to_string()), PinValue::High); + } + + // Wait for propagation + std::thread::sleep(Duration::from_millis(1)); + + // Check pin2 + let p2 = pin2.lock().unwrap(); + assert_eq!(p2.read(), PinValue::High); + } + + #[test] + fn test_pin_conflict_resolution() { + let mut pin = Pin::new("TEST".to_string()); + + // Multiple drivers with same strength - Low should dominate + pin.set_driver(Some("driver1".to_string()), PinValue::High); + pin.set_driver(Some("driver2".to_string()), PinValue::Low); + pin.set_driver(Some("driver3".to_string()), PinValue::High); + + assert_eq!(pin.read(), PinValue::Low); + + // Remove low driver - should settle to High + pin.remove_driver("driver2"); + assert_eq!(pin.read(), PinValue::High); + } + + #[test] + fn test_pin_tri_state() { + let mut pin = Pin::new("TEST".to_string()); + + // Drive then tri-state + pin.set_driver(Some("driver".to_string()), PinValue::High); + assert_eq!(pin.read(), PinValue::High); + + pin.set_driver(Some("driver".to_string()), PinValue::HighZ); + assert_eq!(pin.read(), PinValue::HighZ); + } +} \ No newline at end of file