Bug Fixes
This commit is contained in:
parent
ba1b0ddc22
commit
e69c549750
8 changed files with 1143 additions and 175 deletions
|
|
@ -4,4 +4,4 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serialport = "4.6.0"
|
||||
tempfile = "3.3.0"
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
289
src/component.rs
289
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<String, Arc<Pin>>;
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>>;
|
||||
fn update(&mut self) -> Result<(), String>;
|
||||
|
||||
use crate::pin::Pin;
|
||||
|
||||
pub trait Component {
|
||||
fn name(&self) -> String;
|
||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>>;
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, 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<String, Arc<Pin>>,
|
||||
pub running: Arc<Mutex<bool>>,
|
||||
name: String,
|
||||
pins: HashMap<String, Arc<Mutex<Pin>>>,
|
||||
running: AtomicBool,
|
||||
}
|
||||
|
||||
impl BaseComponent {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
pub fn new(name: String, pins: HashMap<String, Arc<Mutex<Pin>>>) -> 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<Pin> {
|
||||
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<String, Arc<Pin>> {
|
||||
&self.pins
|
||||
}
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||
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));
|
||||
}
|
||||
});
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.running.load(Ordering::SeqCst)
|
||||
}
|
||||
fn stop(&mut self) {
|
||||
*self.running.lock().unwrap() = false;
|
||||
|
||||
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<String, Arc<Mutex<Pin>>> {
|
||||
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<Mutex<Pin>>) {
|
||||
self.pins.insert(name, pin);
|
||||
}
|
||||
|
||||
pub fn remove_pin(&mut self, name: &str) -> Option<Arc<Mutex<Pin>>> {
|
||||
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<String, Arc<Mutex<Pin>>> {
|
||||
&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<String, Arc<Mutex<Pin>>> {
|
||||
&self.pins
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, 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.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<String, Arc<Mutex<Pin>>> {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -29,19 +29,19 @@ GenericRam<SIZE, DATA_WIDTH, ADDR_WIDTH>
|
|||
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -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<const SIZE: usize, const DATA_WIDTH: usize> {
|
||||
pub(crate) base: crate::component::BaseComponent,
|
||||
memory: HashMap<u64, u64>, // Generic memory storage
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
pub struct GenericRom {
|
||||
pub(crate) base: BaseComponent,
|
||||
memory: Vec<u8>,
|
||||
address_width: usize,
|
||||
data_width: usize,
|
||||
address_range: std::ops::Range<u64>,
|
||||
pub latched_address: ()
|
||||
last_address: u32,
|
||||
access_time: Duration,
|
||||
last_access: Instant,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize, const DATA_WIDTH: usize> GenericRom<SIZE, DATA_WIDTH> {
|
||||
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<u8>, 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<Vec<u8>, _> = 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<P: AsRef<Path>>(&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<const SIZE: usize, const DATA_WIDTH: usize> BusDevice for GenericRom<SIZE, DATA_WIDTH> {
|
||||
impl Component for GenericRom {
|
||||
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) {
|
||||
// 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<Arc<Pin>>) {
|
||||
// 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 get_address_range(&self) -> Option<std::ops::Range<u64>> {
|
||||
Some(self.address_range.clone())
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &str {
|
||||
todo!()
|
||||
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);
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -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<Pin>, destination: &Arc<Pin>) -> Result<(), String> {
|
||||
let mut source_connections = source.connections.lock().unwrap();
|
||||
source_connections.push(destination.clone());
|
||||
|
||||
let mut dest_connections = destination.connections.lock().unwrap();
|
||||
dest_connections.push(source.clone());
|
||||
|
||||
Ok(())
|
||||
/// Manages electrical connections between pins
|
||||
pub struct ConnectionManager {
|
||||
connections: HashMap<String, Vec<String>>, // pin_name -> connected_pin_names
|
||||
}
|
||||
|
||||
impl ConnectionManager {
|
||||
pub fn new() -> Self {
|
||||
ConnectionManager {
|
||||
connections: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect two pins bidirectionally
|
||||
pub fn connect_pins(&mut self, pin1: Arc<Mutex<Pin>>, pin2: Arc<Mutex<Pin>>) -> 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<Mutex<Pin>>]) -> 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<Mutex<Pin>>, pin2: &Arc<Mutex<Pin>>) -> 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<String>> {
|
||||
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<Mutex<Pin>>) -> 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<String> = 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<Vec<String>> {
|
||||
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<Mutex<Pin>>, pin2: Arc<Mutex<Pin>>) -> Result<(), String> {
|
||||
let mut manager = ConnectionManager::new();
|
||||
manager.connect_pins(pin1, pin2)
|
||||
}
|
||||
|
||||
/// Helper function for bus connections
|
||||
pub fn connect_bus(pins: &[Arc<Mutex<Pin>>]) -> 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);
|
||||
}
|
||||
}
|
||||
373
src/pin.rs
373
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<bool> {
|
||||
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<RwLock<PinValue>>,
|
||||
pub output_enable: Arc<Mutex<bool>>,
|
||||
pub connections: Arc<Mutex<Vec<Arc<Pin>>>>,
|
||||
name: String,
|
||||
drivers: HashMap<String, (PinValue, DriveStrength)>,
|
||||
settled_value: PinValue,
|
||||
last_update: Instant,
|
||||
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 {
|
||||
// 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<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 {
|
||||
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<String, (PinValue, DriveStrength)> {
|
||||
&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<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();
|
||||
}
|
||||
}
|
||||
|
||||
// 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<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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue