Bug fixes in pin handling
Addition of ActiveBus for measurement and HW manipulation
This commit is contained in:
parent
e69c549750
commit
eff70aa889
3 changed files with 359 additions and 114 deletions
452
src/bus.rs
452
src/bus.rs
|
|
@ -1,161 +1,401 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use crate::{Component, Pin, PinValue};
|
use std::thread;
|
||||||
pub trait BusDevice: Send {
|
use std::time::{Duration, Instant};
|
||||||
/// Called on each simulation cycle to update device state based on pin states
|
|
||||||
fn update(&mut self);
|
|
||||||
|
|
||||||
/// Connect this device to bus pins
|
use crate::component::{BaseComponent, Component};
|
||||||
fn connect_to_bus(&mut self, bus_pins: Vec<Arc<Pin>>);
|
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 {
|
pub struct GenericBus {
|
||||||
base: crate::component::BaseComponent,
|
base: BaseComponent,
|
||||||
connected_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
|
connected_pins: Vec<Arc<Mutex<Pin>>>,
|
||||||
bus_pins: HashMap<String, Arc<Pin>>, // Named bus lines (like "A0", "D0", "R/W")
|
bus_value: PinValue,
|
||||||
|
last_update: Instant,
|
||||||
|
settlement_time: Duration,
|
||||||
|
is_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenericBus {
|
impl GenericBus {
|
||||||
pub fn new(name: String, pin_definitions: &[(&str, PinValue)]) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
let mut base = crate::component::BaseComponent::new(name.clone());
|
// Bus typically has bidirectional data pins
|
||||||
let mut bus_pins = HashMap::new();
|
let pin_names = vec!["DATA"];
|
||||||
|
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||||
|
|
||||||
// Create bus pins
|
GenericBus {
|
||||||
for (pin_name, initial_value) in pin_definitions {
|
base: BaseComponent::new(name, pins),
|
||||||
let pin = base.add_pin(
|
connected_pins: Vec::new(),
|
||||||
pin_name.to_string(),
|
bus_value: PinValue::HighZ,
|
||||||
*initial_value,
|
last_update: Instant::now(),
|
||||||
);
|
settlement_time: Duration::from_nanos(10),
|
||||||
bus_pins.insert(pin_name.to_string(), pin);
|
is_active: true,
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
connected_devices: Vec::new(),
|
|
||||||
bus_pins,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect_device(&mut self, device: Arc<Mutex<dyn BusDevice>>) {
|
pub fn with_settlement_time(mut self, time: Duration) -> Self {
|
||||||
// Get all bus pins and connect them to the device
|
self.settlement_time = time;
|
||||||
let bus_pins: Vec<Arc<Pin>> = self.bus_pins.values().cloned().collect();
|
self
|
||||||
|
|
||||||
if let Ok(mut device) = device.lock() {
|
|
||||||
device.connect_to_bus(bus_pins);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.connected_devices.push(device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
pub fn connect_pin(&mut self, pin: Arc<Mutex<Pin>>) -> Result<(), String> {
|
||||||
self.bus_pins.get(name).cloned()
|
if !self.connected_pins.iter().any(|p| Arc::ptr_eq(p, &pin)) {
|
||||||
}
|
self.connected_pins.push(pin);
|
||||||
|
Ok(())
|
||||||
/// The key function: resolve pin conflicts and propagate values
|
} else {
|
||||||
fn resolve_bus_conflicts(&self) {
|
Err("Pin already connected to bus".to_string())
|
||||||
for (pin_name, bus_pin) in &self.bus_pins {
|
|
||||||
self.resolve_pin_state(bus_pin);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_pin_state(&self, bus_pin: &Arc<Pin>) {
|
pub fn disconnect_pin(&mut self, pin: &Arc<Mutex<Pin>>) -> Result<(), String> {
|
||||||
let connections = bus_pin.connections.lock().unwrap();
|
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)
|
if self.connected_pins.len() < initial_len {
|
||||||
let mut any_driving_low = false;
|
Ok(())
|
||||||
let mut any_driving_high = false;
|
} else {
|
||||||
let mut any_driving = false;
|
Err("Pin not found on bus".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for connected_pin in connections.iter() {
|
pub fn get_connected_pins(&self) -> &Vec<Arc<Mutex<Pin>>> {
|
||||||
let output_enable = connected_pin.output_enable.lock().unwrap();
|
&self.connected_pins
|
||||||
let value = connected_pin.value.read().unwrap();
|
}
|
||||||
|
|
||||||
if *output_enable {
|
pub fn set_active(&mut self, active: bool) {
|
||||||
any_driving = true;
|
self.is_active = active;
|
||||||
match *value {
|
if !active {
|
||||||
PinValue::Low => any_driving_low = true,
|
// Tri-state the bus when deactivated
|
||||||
PinValue::High => any_driving_high = true,
|
self.bus_value = PinValue::HighZ;
|
||||||
PinValue::HighZ => {} // High-Z doesn't drive
|
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
|
if drivers.is_empty() {
|
||||||
let resolved_value = if any_driving_low {
|
return PinValue::HighZ;
|
||||||
// If any device pulls low, bus is low (wired-AND)
|
}
|
||||||
|
|
||||||
|
// 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
|
PinValue::Low
|
||||||
} else if any_driving_high && !any_driving_low {
|
} else if strong_drivers.iter().any(|v| *v == PinValue::High) {
|
||||||
// If devices are driving high but no one is pulling low
|
|
||||||
PinValue::High
|
PinValue::High
|
||||||
} else if any_driving {
|
|
||||||
// Mixed driving states (shouldn't happen in proper design)
|
|
||||||
PinValue::Low // Safe fallback: low wins
|
|
||||||
} else {
|
} else {
|
||||||
// No one is driving - high impedance
|
|
||||||
PinValue::HighZ
|
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) {
|
fn propagate_bus_value(&self) {
|
||||||
// This is a challenge: we want to set the pin's value without claiming to "drive" it
|
if !self.is_active {
|
||||||
// We need to extend our Pin struct to support bus resolution
|
return;
|
||||||
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
|
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 {
|
impl Component for GenericBus {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> String {
|
||||||
self.base.name()
|
self.base.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pins(&self) -> &std::collections::HashMap<String, Arc<Pin>> {
|
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
||||||
self.base.pins()
|
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)
|
self.base.get_pin(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) -> Result<(), String> {
|
fn update(&mut self) {
|
||||||
// Bus update cycle:
|
if !self.is_active {
|
||||||
// 1. First, let all devices update based on current pin states
|
return;
|
||||||
for device in &self.connected_devices {
|
|
||||||
if let Ok(mut device) = device.lock() {
|
|
||||||
device.update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Then resolve bus conflicts and propagate pin states
|
// Respect settlement timing
|
||||||
self.resolve_bus_conflicts();
|
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) {
|
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) {
|
fn stop(&mut self) {
|
||||||
self.base.stop();
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use std::ops::Range;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::{Component, Pin, PinValue};
|
use crate::{Component, Pin, PinValue};
|
||||||
|
|
||||||
|
|
|
||||||
20
src/pin.rs
20
src/pin.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::{Duration, Instant};
|
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 {
|
pub enum DriveStrength {
|
||||||
HighImpedance = 0,
|
HighImpedance = 0,
|
||||||
Weak = 1,
|
Weak = 1,
|
||||||
|
|
@ -52,6 +52,17 @@ pub enum DriveStrength {
|
||||||
Strong = 3,
|
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 {
|
pub struct Pin {
|
||||||
name: String,
|
name: String,
|
||||||
drivers: HashMap<String, (PinValue, DriveStrength)>,
|
drivers: HashMap<String, (PinValue, DriveStrength)>,
|
||||||
|
|
@ -60,11 +71,6 @@ pub struct Pin {
|
||||||
settlement_time: Duration,
|
settlement_time: Duration,
|
||||||
connected_pins: Vec<Arc<Mutex<Pin>>>,
|
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 {
|
impl Pin {
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
Pin {
|
Pin {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue