Bug fixes in pin handling

Addition of ActiveBus for measurement and HW manipulation
This commit is contained in:
Jan-Peter von Hunnius 2025-09-22 18:49:17 +02:00
parent e69c549750
commit eff70aa889
3 changed files with 359 additions and 114 deletions

View file

@ -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<Arc<Pin>>);
use crate::component::{BaseComponent, Component};
use crate::pin::{Pin, PinValue, DriveStrength};
/// Optional: report address range for debugging/monitoring
fn get_address_range(&self) -> Option<std::ops::Range<u64>> {
None
}
/// Optional: get device name for debugging
fn get_name(&self) -> &str;
}
#[derive(Clone)]
pub struct GenericBus {
base: crate::component::BaseComponent,
connected_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
bus_pins: HashMap<String, Arc<Pin>>, // Named bus lines (like "A0", "D0", "R/W")
base: BaseComponent,
connected_pins: Vec<Arc<Mutex<Pin>>>,
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<Mutex<dyn BusDevice>>) {
// Get all bus pins and connect them to the device
let bus_pins: Vec<Arc<Pin>> = 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<Arc<Pin>> {
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<Mutex<Pin>>) -> 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<Pin>) {
let connections = bus_pin.connections.lock().unwrap();
pub fn disconnect_pin(&mut self, pin: &Arc<Mutex<Pin>>) -> 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<Arc<Mutex<Pin>>> {
&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<PinValue> = 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<Pin>, 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<String, Arc<Pin>> {
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
self.base.pins()
}
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, 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<PinValue>,
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<PinValue>) {
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<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) {
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());
}
}

View file

@ -1,4 +1,3 @@
use std::ops::Range;
use std::sync::Arc;
use crate::{Component, Pin, PinValue};

View file

@ -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<Ordering> {
Some(self.cmp(other))
}
}
pub struct Pin {
name: String,
drivers: HashMap<String, (PinValue, DriveStrength)>,
@ -60,11 +71,6 @@ pub struct Pin {
settlement_time: Duration,
connected_pins: Vec<Arc<Mutex<Pin>>>,
}
impl PartialOrd for DriveStrength {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some((*self as u8).cmp(&(*other as u8)))
}
}
impl Pin {
pub fn new(name: String) -> Self {
Pin {