rusty_emu/src/pin.rs
Jan-Peter von Hunnius eff70aa889 Bug fixes in pin handling
Addition of ActiveBus for measurement and HW manipulation
2025-09-22 18:49:17 +02:00

369 lines
No EOL
10 KiB
Rust

use std::cmp::Ordering;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PinValue {
Low,
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<bool> {
match self {
PinValue::Low => Some(false),
PinValue::High => Some(true),
PinValue::HighZ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DriveStrength {
HighImpedance = 0,
Weak = 1,
Standard = 2,
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<Ordering> {
Some(self.cmp(other))
}
}
pub struct Pin {
name: String,
drivers: HashMap<String, (PinValue, DriveStrength)>,
settled_value: PinValue,
last_update: Instant,
settlement_time: Duration,
connected_pins: Vec<Arc<Mutex<Pin>>>,
}
impl Pin {
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<String>, 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<String>, 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 {
// 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
}
pub fn read_immediate(&self) -> PinValue {
self.settled_value
}
pub fn get_drivers(&self) -> &HashMap<String, (PinValue, DriveStrength)> {
&self.drivers
}
pub fn is_settled(&self) -> bool {
self.last_update.elapsed() >= self.settlement_time
}
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<Mutex<Pin>>) {
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<Mutex<Pin>>) {
self.connected_pins.retain(|p| !Arc::ptr_eq(p, other_pin));
}
pub fn get_connected_pins(&self) -> &Vec<Arc<Mutex<Pin>>> {
&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();
}
}
}
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<PinValue> = 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<Mutex<Pin>>) {
self.connected_pins.retain(|p| !Arc::ptr_eq(p, other_pin));
}
pub fn is_connected_to(&self, other_pin: &Arc<Mutex<Pin>>) -> 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);
}
}