use rusty_emu::component::{BaseComponent, Component}; use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode}; use rusty_emu::pin::{Pin, PinValue}; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::time::Instant; // Mock component for testing trait implementations #[derive(Debug)] struct MockComponent {} impl MockComponent { fn new() -> Self { Self {} } } impl Component for MockComponent { fn name(&self) -> String { "MockComponent".to_string() } fn pins(&self) -> HashMap>> { HashMap::new() } fn get_pin(&self, name: &str) -> Result>, String> { // For testing, we'll return an error since we don't have real pins Err(format!("Mock pin not implemented for {}", name)) } fn update(&mut self) { // No-op for mock } fn run(&mut self) { // No-op for mock } fn stop(&mut self) { // No-op for mock } fn is_running(&self) -> bool { false } } impl Intel400xAddressHandling for MockComponent { fn get_base(&self) -> &BaseComponent { unimplemented!("MockComponent doesn't contain BaseComponent") } } #[cfg(test)] mod tests { use super::*; use std::thread; use std::time::Duration; #[test] fn test_timing_constants() { // Test that timing constants have expected values assert_eq!( TimingConstants::DEFAULT_ACCESS_TIME, Duration::from_nanos(500) ); assert_eq!(TimingConstants::FAST_ACCESS_TIME, Duration::from_nanos(200)); assert_eq!(TimingConstants::ADDRESS_SETUP, Duration::from_nanos(100)); assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200)); } #[test] fn test_assemble_full_address() { // Create a mock trait implementation for testing struct TestAddressHandler {} impl TestAddressHandler { fn new() -> Self { Self {} } } impl Intel400xAddressHandling for TestAddressHandler { fn get_base(&self) -> &BaseComponent { unimplemented!("TestAddressHandler doesn't contain BaseComponent") } } let handler = TestAddressHandler::new(); // Test address assembly with valid nibbles assert_eq!( handler.assemble_full_address(Some(0x0F), Some(0x03)), Some(0xF3) ); assert_eq!( handler.assemble_full_address(Some(0x02), Some(0x04)), Some(0x24) ); assert_eq!( handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x00) ); assert_eq!( handler.assemble_full_address(Some(0x0F), Some(0x0F)), Some(0xFF) ); // Test with missing nibbles assert_eq!(handler.assemble_full_address(None, Some(0x23)), None); assert_eq!(handler.assemble_full_address(Some(0x0F), None), None); assert_eq!(handler.assemble_full_address(None, None), None); } #[test] fn test_timing_state_machine() { // Test state machine properties let idle_state = TimingState::Idle; let address_state = TimingState::AddressPhase; let wait_state = TimingState::WaitLatency; let drive_state = TimingState::DriveData; // Test state queries assert!(idle_state.is_idle()); assert!(!idle_state.is_address_phase()); assert!(!idle_state.is_waiting_latency()); assert!(!idle_state.is_driving_data()); assert!(!address_state.is_idle()); assert!(address_state.is_address_phase()); assert!(!address_state.is_waiting_latency()); assert!(!address_state.is_driving_data()); assert!(!wait_state.is_idle()); assert!(!wait_state.is_address_phase()); assert!(wait_state.is_waiting_latency()); assert!(!wait_state.is_driving_data()); assert!(!drive_state.is_idle()); assert!(!drive_state.is_address_phase()); assert!(!drive_state.is_waiting_latency()); assert!(drive_state.is_driving_data()); } #[test] fn test_memory_state_conversions() { // Test conversions between MemoryState and TimingState let mem_idle = MemoryState::Idle; let timing_idle: TimingState = mem_idle.into(); assert_eq!(timing_idle, TimingState::Idle); let mem_address = MemoryState::AddressPhase; let timing_address: TimingState = mem_address.into(); assert_eq!(timing_address, TimingState::AddressPhase); let mem_wait = MemoryState::WaitLatency; let timing_wait: TimingState = mem_wait.into(); assert_eq!(timing_wait, TimingState::WaitLatency); let mem_drive = MemoryState::DriveData; let timing_drive: TimingState = mem_drive.into(); assert_eq!(timing_drive, TimingState::DriveData); // Test reverse conversions let back_to_mem: MemoryState = timing_idle.into(); assert_eq!(back_to_mem, MemoryState::Idle); } #[test] fn test_ram_state_conversions() { // Test conversions between RamState and TimingState let ram_idle = RamState::Idle; let timing_idle: TimingState = ram_idle.into(); assert_eq!(timing_idle, TimingState::Idle); let ram_read = RamState::ReadData; let timing_drive: TimingState = ram_read.into(); assert_eq!(timing_drive, TimingState::DriveData); let ram_write = RamState::WriteData; let timing_drive: TimingState = ram_write.into(); assert_eq!(timing_drive, TimingState::DriveData); let ram_output = RamState::OutputPort; let timing_drive: TimingState = ram_output.into(); assert_eq!(timing_drive, TimingState::DriveData); } #[test] fn test_utils_create_driver_name() { assert_eq!(create_driver_name("COMP1", "DATA"), "COMP1_DATA"); assert_eq!(create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR"); assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL"); } #[test] fn test_data_bus_bit_operations() { // Test bit manipulation for data bus operations let test_values = vec![ (0x00, [false, false, false, false]), (0x01, [true, false, false, false]), (0x02, [false, true, false, false]), (0x04, [false, false, true, false]), (0x08, [false, false, false, true]), (0x05, [true, false, true, false]), (0x0A, [false, true, false, true]), (0x0F, [true, true, true, true]), ]; for (value, expected_bits) in test_values { // Test bit extraction (reading from bus) for i in 0..4 { let bit_value = (value >> i) & 1; assert_eq!(bit_value == 1, expected_bits[i as usize]); } // Test bit setting (writing to bus) let mut reconstructed = 0; for i in 0..4 { if expected_bits[i as usize] { reconstructed |= 1 << i; } } assert_eq!(value, reconstructed); } } #[test] fn test_address_latching_logic() { // Test the address latching algorithm let mut high_nibble: Option = None; let mut low_nibble: Option = None; let mut full_address_ready = false; let mut address_latch_time: Option = None; let access_time = Duration::from_nanos(100); // Mock address handler for testing the algorithm struct TestAddressHandler {} impl TestAddressHandler { fn new() -> Self { Self {} } } impl Intel400xAddressHandling for TestAddressHandler { fn get_base(&self) -> &BaseComponent { unimplemented!("TestAddressHandler doesn't contain BaseComponent") } } let handler = TestAddressHandler::new(); // First cycle: high nibble handler.handle_address_latching( 0x12, &mut high_nibble, &mut low_nibble, &mut full_address_ready, &mut address_latch_time, access_time, ); assert_eq!(high_nibble, Some(0x12)); assert_eq!(low_nibble, None); assert_eq!(full_address_ready, false); assert_eq!(address_latch_time, None); // Second cycle: low nibble handler.handle_address_latching( 0x34, &mut high_nibble, &mut low_nibble, &mut full_address_ready, &mut address_latch_time, access_time, ); assert_eq!(high_nibble, None); // Should be cleared assert_eq!(low_nibble, None); // Should be cleared assert_eq!(full_address_ready, true); assert!(address_latch_time.is_some()); // Verify the assembled address let assembled = handler.assemble_full_address(Some(0x02), Some(0x04)); assert_eq!(assembled, Some(0x24)); } #[test] fn test_latency_timing_logic() { let access_time = Duration::from_nanos(100); // Test with no latch time (should return false) assert_eq!( Intel400xAddressHandling::handle_latency_wait( &MockComponent::new(), &None, access_time ), false ); thread::sleep(Duration::from_micros(1)); // Test with recent latch time (should return false if not enough time elapsed) let recent_time = Some(Instant::now()); assert_eq!( Intel400xAddressHandling::handle_latency_wait( &MockComponent::new(), &recent_time, access_time ), true ); // Note: Testing with elapsed time would require sleeping or mocking time // This demonstrates the logic structure is correct } #[test] fn test_intel4001_integration_debugging() { println!("=== Intel 4001 Integration Test: Setup ==="); let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 1); // 1ns for fast testing rom.base.set_running(true); // Load test data: simple arithmetic sequence let test_data = vec![ 0x01, 0x02, 0x03, 0x04, 0x05, 0x10, 0x20, 0x30, 0x40, 0x50, 0xFF, ]; rom.load_rom_data(test_data.clone(), 0).unwrap(); // Verify test data loaded for (i, &data) in test_data.iter().enumerate() { assert_eq!(rom.read_rom(i as u8).unwrap(), data); } // Get pin references let sync_pin = rom.get_pin("SYNC").unwrap(); let cm_pin = rom.get_pin("CM").unwrap(); let ci_pin = rom.get_pin("CI").unwrap(); let reset_pin = rom.get_pin("RESET").unwrap(); let phi1_pin = rom.get_pin("PHI1").unwrap(); let phi2_pin = rom.get_pin("PHI2").unwrap(); let d0_pin = rom.get_pin("D0").unwrap(); let d1_pin = rom.get_pin("D1").unwrap(); let d2_pin = rom.get_pin("D2").unwrap(); let d3_pin = rom.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Initially, all data pins should be HighZ assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); // Test reset (RESET is active LOW) println!("=== Testing Reset ==="); { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); assert_eq!(rom.get_io_mode(), IoMode::Input); // Release reset (set HIGH for normal operation) { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } rom.update(); // Test memory read at address 0x00 println!("=== Memory Access Testing: Address 0x00 ==="); test_memory_read( &mut rom, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0x00, 0x01, ); // Test memory read at address 0xFF // Load test data at address 0xFF specifically // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time // The value 0x0F will be read as 0x0F (low nibble only) rom.load_rom_data(vec![0x0F], 0xFF).unwrap(); println!("=== Memory Access Testing: Address 0xFF ==="); test_memory_read( &mut rom, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0xFF, 0x0F, // 4-bit value (low nibble only) ); // Test I/O operations println!("=== I/O Integration Testing ==="); // Note: I/O operations on Intel 4001 require proper clock timing and control signals. // The handle_io_operation() method requires: // - CM (chip_select) = HIGH // - CI (io_select) = HIGH // - Proper clock edges to trigger the operation // For this test, we'll verify the direct I/O port access methods work correctly // since full I/O timing simulation would require more complex test setup. // Test direct I/O port write (using set_io_port API) rom.set_io_mode(IoMode::Output); rom.set_io_port(0, 0x05).unwrap(); assert_eq!(rom.get_io_port(0).unwrap(), 0x05); // Test I/O port read (using get_io_port API) rom.set_io_port(0, 0x0A).unwrap(); assert_eq!(rom.get_io_port(0).unwrap(), 0x0A); // Test that I/O direction is set to Output when we write assert_eq!(rom.get_io_direction(0).unwrap(), IoDirection::Output); // Debugging features println!("=== Debugging Features ==="); rom.debug_state_transitions("Integration Test"); // Pin state snapshots println!( "Pin states: SYNC: {:?}, CM: {:?}, CI: {:?}, PHI1: {:?}, PHI2: {:?}", sync_pin.lock().unwrap().read(), cm_pin.lock().unwrap().read(), ci_pin.lock().unwrap().read(), phi1_pin.lock().unwrap().read(), phi2_pin.lock().unwrap().read() ); println!("Test completed successfully!"); } fn test_memory_read( rom: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, d0_pin: &Arc>, d1_pin: &Arc>, d2_pin: &Arc>, d3_pin: &Arc>, address: u8, expected_data: u8, ) { // Reset to known state first (RESET is active LOW) { let reset_pin = rom.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } rom.update(); { let reset_pin = rom.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } rom.update(); // Set SYNC=1, CM=1, CI=0 for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Start the component rom.base.set_running(true); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // Set high nibble on data bus rom.write_data_bus((address >> 4) & 0x0F); rom.update(); // PHI1 high to low: Complete address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Set low nibble on data bus rom.write_data_bus(address & 0x0F); rom.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Check if we're in WaitLatency or DriveData state // With very small access times, we might transition directly to DriveData let state = rom.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Wait for latency if needed if state == TimingState::WaitLatency { // Get the actual access time from the ROM let access_time_ns = rom.get_access_time(); thread::sleep(Duration::from_nanos(access_time_ns + 10)); // Add small buffer rom.update(); // Update after waiting to check latency } // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = rom.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } rom.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::DriveData); // Wait a bit for data to be driven thread::sleep(Duration::from_nanos(10)); rom.update(); // Additional update to ensure data is driven on the bus // Check data on bus let data = rom.read_data_bus(); assert_eq!(data, expected_data); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Tri-state pins assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); } #[test] fn test_minimum_access_time() { // Test behavior at minimum access time (200ns) with comprehensive edge cases let mut chip = Intel4001::new_with_access_time("MIN_TIME_ROM".to_string(), 200); // 200ns minimum access time chip.base.set_running(true); // Get pin references let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let reset_pin = chip.get_pin("RESET").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); let d0_pin = chip.get_pin("D0").unwrap(); let d1_pin = chip.get_pin("D1").unwrap(); let d2_pin = chip.get_pin("D2").unwrap(); let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test memory read at minimum timing // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time // The full 8-bit read would require M1/M2 cycles (not yet implemented) let address = 0x12; let expected_data = 0x04; // 4-bit value (low nibble only for single-cycle read) // Set up ROM data chip.load_rom_data(vec![expected_data], address).unwrap(); // Test memory read using the helper function test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u8, expected_data, ); // Verify state machine transitions correctly at minimum timing assert_eq!(chip.get_timing_state(), TimingState::Idle); // Test multiple consecutive reads at minimum timing // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time // The expected data must be a 4-bit value (0x00-0x0F) for addr in 0x10..0x15 { // Use 4-bit values that fit on the data bus let expected_data = (addr & 0x0F) as u8; // Low nibble only let test_data = vec![expected_data]; chip.load_rom_data(test_data, addr).unwrap(); test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, addr as u8, expected_data, ); } // Enhanced edge case testing for minimum access time // Test edge case: consecutive reads at minimum access time // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 for address in 0x00..0x10 { // Use addresses 0x00-0x0F where low nibble equals address let expected_data = (address & 0x0F) as u8; // 4-bit value let result1 = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, expected_data, ); assert_eq!( result1, expected_data, "Failed consecutive read 1 at address {} at minimum access time", address ); } // Test edge case: rapid address changes at minimum access time // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 let test_addresses = [0, 255, 128, 1, 254, 2, 253, 3]; for &address in &test_addresses { let expected_data = (address & 0x0F) as u8; // 4-bit value for data bus chip.load_rom_data(vec![expected_data], address).unwrap(); let result = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, expected_data, ); assert_eq!( result, expected_data, "Failed rapid address change test at address {} at minimum access time", address ); } // Test edge case: minimum access time with I/O operations // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 chip.load_rom_data(vec![0x05], 0x00).unwrap(); // Test pattern (4-bit value) chip.load_rom_data(vec![0x0A], 0xFF).unwrap(); // Another test pattern (4-bit value) // Test I/O pin behavior during minimum access time reads let io_pins = ["IO0", "IO1", "IO2", "IO3"]; // Set I/O pins to output mode for pin_name in &io_pins { let _io_pin = chip.get_pin(pin_name).unwrap(); // Note: In the actual implementation, I/O pin mode is controlled through the chip's I/O methods // For this test, we'll verify the I/O functionality through the chip's methods } // Test memory read while I/O operations are active let result = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0x00, 0x05, ); assert_eq!( result, 0x05, "Failed memory read with active I/O at minimum access time" ); // Test edge case: minimum access time with boundary addresses // Note: Intel 4004 has a 4-bit data bus, so expected data must be 0-15 let boundary_addresses = [0, 1, 254, 255]; for &address in &boundary_addresses { let expected_data = (address & 0x0F) as u8; // 4-bit value for data bus chip.load_rom_data(vec![expected_data], address).unwrap(); let result = test_memory_read_edge_case( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, expected_data, ); assert_eq!( result, expected_data, "Failed boundary address test at {} at minimum access time", address ); } // Test state machine transitions at minimum access time // Verify proper state progression through all phases // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time let mut test_chip = Intel4001::new_with_access_time("STATE_TEST_ROM".to_string(), 200); test_chip.base.set_running(true); test_chip.load_rom_data(vec![0x02], 0x10).unwrap(); // 4-bit value (low nibble only) // Get pins for state machine test let test_sync = test_chip.get_pin("SYNC").unwrap(); let test_cm = test_chip.get_pin("CM").unwrap(); let test_ci = test_chip.get_pin("CI").unwrap(); let test_phi1 = test_chip.get_pin("PHI1").unwrap(); let test_phi2 = test_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 test_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Initialize PHI2 to Low to ensure proper edge detection { let mut phi2_guard = test_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); // Manually step through the state machine to verify timing // Set control pins for memory read { let mut sync_guard = test_sync.lock().unwrap(); let mut cm_guard = test_cm.lock().unwrap(); let mut ci_guard = test_ci.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::Idle); // Ensure PHI1 is low initially { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); // Start address phase - first PHI1 cycle for high nibble { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::AddressPhase); // Apply high nibble test_chip.write_data_bus(0x01); // High nibble test_chip.update(); // PHI1 falling edge - latch high nibble { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::AddressPhase); // Still in address phase // Second PHI1 cycle for low nibble { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); // Apply low nibble test_chip.write_data_bus(0x00); // Low nibble test_chip.update(); // PHI1 falling edge - latch low nibble and complete address { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::WaitLatency); // Wait for minimum latency (200ns) thread::sleep(Duration::from_nanos(210)); // Slightly more than minimum // PHI2 high: Data phase { let mut phi2_guard = test_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::DriveData); // Verify data is driven correctly (4-bit value) let data = test_chip.read_data_bus(); assert_eq!(data, 0x02); // Complete cycle { let mut phi2_guard = test_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::Idle); // Test edge case: timing violations at minimum access time // Test what happens when we try to read too quickly let mut fast_chip = Intel4001::new_with_access_time("FAST_VIOLATION_ROM".to_string(), 200); fast_chip.base.set_running(true); fast_chip.load_rom_data(vec![0x09], 0x50).unwrap(); // Use 4-bit value (0x09) let fast_sync = fast_chip.get_pin("SYNC").unwrap(); let fast_cm = fast_chip.get_pin("CM").unwrap(); let fast_ci = fast_chip.get_pin("CI").unwrap(); let fast_phi1 = fast_chip.get_pin("PHI1").unwrap(); let fast_phi2 = fast_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 fast_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); fast_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); fast_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); fast_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); fast_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Initialize PHI2 to Low to ensure proper edge detection { let mut phi2_guard = fast_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Set up for memory read { let mut sync_guard = fast_sync.lock().unwrap(); let mut cm_guard = fast_cm.lock().unwrap(); let mut ci_guard = fast_ci.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = fast_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // First PHI1 cycle - latch high nibble { let mut phi1_guard = fast_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); // Apply high nibble fast_chip.write_data_bus(0x05); // High nibble of address 0x50 fast_chip.update(); // PHI1 falling edge - latch high nibble { let mut phi1_guard = fast_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Second PHI1 cycle - latch low nibble { let mut phi1_guard = fast_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); // Apply low nibble fast_chip.write_data_bus(0x00); // Low nibble of address 0x50 fast_chip.update(); // PHI1 falling edge - latch low nibble and complete address { let mut phi1_guard = fast_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // At this point, the state should be WaitLatency or DriveData let state = fast_chip.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = fast_chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } fast_chip.update(); // Try to read data - PHI2 rising edge triggers data driving if latency elapsed { let mut phi2_guard = fast_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); // Wait for proper timing and then verify data thread::sleep(Duration::from_nanos(210)); fast_chip.update(); // Now data should be valid (4-bit value) let data = fast_chip.read_data_bus(); assert_eq!(data, 0x09); } // Helper function for edge case memory read testing fn test_memory_read_edge_case( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, _d0_pin: &Arc>, _d1_pin: &Arc>, _d2_pin: &Arc>, _d3_pin: &Arc>, address: u16, expected_data: u8, ) -> u8 { // Load the expected data into ROM at the specified address chip.load_rom_data(vec![expected_data], address as usize) .unwrap(); // Reset to known state chip.perform_reset(); chip.update(); // Start the component chip.base.set_running(true); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for the address to be fully latched and state to transition thread::sleep(Duration::from_nanos(10)); chip.update(); // Check if we're in WaitLatency or DriveData state // With very small access times, we might transition directly to DriveData let state = chip.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Wait for latency if needed let current_state = chip.get_timing_state(); if current_state == TimingState::WaitLatency { // Get the actual access time from the ROM let access_time_ns = chip.get_access_time(); thread::sleep(Duration::from_nanos(access_time_ns + 10)); // Add small buffer chip.update(); // Update after waiting to check latency } // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); assert_eq!(chip.get_timing_state(), TimingState::DriveData); // Wait a bit for data to be driven thread::sleep(Duration::from_nanos(10)); chip.update(); // Additional update to ensure data is driven on the bus // Check data on bus let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); assert_eq!(chip.get_timing_state(), TimingState::Idle); data } #[test] fn test_maximum_access_time() { // Test behavior at maximum access time (750ns) let mut chip = Intel4001::new_with_access_time("MAX_TIME_ROM".to_string(), 750); // 750ns maximum access time chip.base.set_running(true); // Get pin references let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let reset_pin = chip.get_pin("RESET").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); let d0_pin = chip.get_pin("D0").unwrap(); let d1_pin = chip.get_pin("D1").unwrap(); let d2_pin = chip.get_pin("D2").unwrap(); let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test memory read at maximum timing // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time let address = 0x23; let expected_data = 0x05; // 4-bit value (low nibble only) // Set up ROM data chip.load_rom_data(vec![expected_data], address).unwrap(); // Test memory read using the helper function test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u8, expected_data, ); // Verify state machine transitions correctly at maximum timing assert_eq!(chip.get_timing_state(), TimingState::Idle); // Test multiple consecutive reads at maximum timing for addr in 0x20..0x25 { let test_data = vec![addr as u8]; chip.load_rom_data(test_data, addr).unwrap(); test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, addr as u8, addr as u8, ); } // Enhanced edge case testing for maximum access time // Test edge case: consecutive reads at maximum access time for address in 0x40..0x50 { chip.load_rom_data(vec![address as u8], address).unwrap(); let result1 = test_memory_read_edge_case_max( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, address as u8, ); assert_eq!( result1, address as u8, "Failed consecutive read 1 at address {} at maximum access time", address ); } // Test edge case: rapid address changes at maximum access time let test_addresses = [0, 255, 128, 1, 254, 2, 253, 3]; for &address in &test_addresses { chip.load_rom_data(vec![address as u8], address).unwrap(); let result = test_memory_read_edge_case_max( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, address as u8, ); assert_eq!( result, address as u8, "Failed rapid address change test at address {} at maximum access time", address ); } // Test edge case: maximum access time with I/O operations chip.load_rom_data(vec![0x33], 0x00).unwrap(); // Test pattern chip.load_rom_data(vec![0xCC], 0xFF).unwrap(); // Another test pattern // Test I/O pin behavior during maximum access time reads let io_pins = ["IO0", "IO1", "IO2", "IO3"]; // Set I/O pins to output mode for pin_name in &io_pins { let _io_pin = chip.get_pin(pin_name).unwrap(); // Note: In the actual implementation, I/O pin mode is controlled through the chip's I/O methods // For this test, we'll verify the I/O functionality through the chip's methods } // Test memory read while I/O operations are active let result = test_memory_read_edge_case_max( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, 0x00, 0x33, ); assert_eq!( result, 0x33, "Failed memory read with active I/O at maximum access time" ); // Test edge case: maximum access time with boundary addresses let boundary_addresses = [0, 1, 254, 255]; for &address in &boundary_addresses { chip.load_rom_data(vec![address as u8], address).unwrap(); let result = test_memory_read_edge_case_max( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u16, address as u8, ); assert_eq!( result, address as u8, "Failed boundary address test at {} at maximum access time", address ); } // Test state machine transitions at maximum access time // Verify proper state progression through all phases let mut test_chip = Intel4001::new_with_access_time("MAX_STATE_TEST_ROM".to_string(), 750); test_chip.base.set_running(true); test_chip.load_rom_data(vec![0x87], 0x20).unwrap(); // Get pins for state machine test let test_sync = test_chip.get_pin("SYNC").unwrap(); let test_cm = test_chip.get_pin("CM").unwrap(); let test_ci = test_chip.get_pin("CI").unwrap(); let test_phi1 = test_chip.get_pin("PHI1").unwrap(); let test_phi2 = test_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 test_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Manually step through the state machine to verify timing // Set control pins for memory read { let mut sync_guard = test_sync.lock().unwrap(); let mut cm_guard = test_cm.lock().unwrap(); let mut ci_guard = test_ci.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::Idle); // Start address phase { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::AddressPhase); // Apply address test_chip.write_data_bus(0x02); // High nibble test_chip.update(); test_chip.write_data_bus(0x00); // Low nibble test_chip.update(); // Complete address phase { let mut phi1_guard = test_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::WaitLatency); // Wait for maximum latency (750ns) thread::sleep(Duration::from_nanos(760)); // Slightly more than maximum // PHI2 high: Data phase { let mut phi2_guard = test_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::DriveData); // Verify data is driven correctly let data = test_chip.read_data_bus(); assert_eq!(data, 0x87); // Complete cycle { let mut phi2_guard = test_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); assert_eq!(test_chip.get_timing_state(), TimingState::Idle); // Test edge case: timing violations at maximum access time // Test what happens when we try to read too quickly even at maximum access time let mut slow_chip = Intel4001::new_with_access_time("SLOW_VIOLATION_ROM".to_string(), 750); slow_chip.base.set_running(true); slow_chip.load_rom_data(vec![0xBB], 0x60).unwrap(); let slow_sync = slow_chip.get_pin("SYNC").unwrap(); let slow_cm = slow_chip.get_pin("CM").unwrap(); let slow_ci = slow_chip.get_pin("CI").unwrap(); let slow_phi1 = slow_chip.get_pin("PHI1").unwrap(); let slow_phi2 = slow_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 slow_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); slow_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); slow_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); slow_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); slow_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Set up for memory read { let mut sync_guard = slow_sync.lock().unwrap(); let mut cm_guard = slow_cm.lock().unwrap(); let mut ci_guard = slow_ci.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } slow_chip.update(); // Start address phase { let mut phi1_guard = slow_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } slow_chip.update(); // Apply address quickly slow_chip.write_data_bus(0x06); // High nibble slow_chip.update(); slow_chip.write_data_bus(0x00); // Low nibble slow_chip.update(); // Complete address phase { let mut phi1_guard = slow_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } slow_chip.update(); // Try to read data immediately without waiting for maximum latency { let mut phi2_guard = slow_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } slow_chip.update(); // At maximum access time, the chip should still be in WaitLatency let state = slow_chip.get_timing_state(); assert!( state == TimingState::WaitLatency || state == TimingState::DriveData, "Expected WaitLatency or DriveData, got {:?}", state ); // Wait for proper timing and then verify data thread::sleep(Duration::from_nanos(760)); slow_chip.update(); // Now data should be valid let data = slow_chip.read_data_bus(); assert_eq!(data, 0xBB); // Test edge case: clock stretching at maximum access time // Test what happens when clock edges are delayed let mut stretch_chip = Intel4001::new_with_access_time("STRETCH_ROM".to_string(), 750); stretch_chip.base.set_running(true); stretch_chip.load_rom_data(vec![0xDD], 0x70).unwrap(); let stretch_sync = stretch_chip.get_pin("SYNC").unwrap(); let stretch_cm = stretch_chip.get_pin("CM").unwrap(); let stretch_ci = stretch_chip.get_pin("CI").unwrap(); let stretch_phi1 = stretch_chip.get_pin("PHI1").unwrap(); let stretch_phi2 = stretch_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 stretch_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); stretch_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); stretch_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); stretch_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); stretch_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Set up for memory read { let mut sync_guard = stretch_sync.lock().unwrap(); let mut cm_guard = stretch_cm.lock().unwrap(); let mut ci_guard = stretch_ci.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } stretch_chip.update(); // Start address phase with stretched clock { let mut phi1_guard = stretch_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } stretch_chip.update(); // Apply address with delays between nibbles stretch_chip.write_data_bus(0x07); // High nibble thread::sleep(Duration::from_nanos(100)); // Delay between nibbles stretch_chip.update(); stretch_chip.write_data_bus(0x00); // Low nibble thread::sleep(Duration::from_nanos(100)); // Delay after low nibble stretch_chip.update(); // Complete address phase with stretched clock { let mut phi1_guard = stretch_phi1.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } stretch_chip.update(); // Wait for maximum latency with additional stretching thread::sleep(Duration::from_nanos(800)); // More than maximum // PHI2 high with stretched clock: Data phase { let mut phi2_guard = stretch_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } stretch_chip.update(); // Verify data is driven correctly even with clock stretching let data = stretch_chip.read_data_bus(); assert_eq!(data, 0xDD); // Complete cycle { let mut phi2_guard = stretch_phi2.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } stretch_chip.update(); assert_eq!(stretch_chip.get_timing_state(), TimingState::Idle); } // Helper function for edge case memory read testing at maximum access time fn test_memory_read_edge_case_max( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, _d0_pin: &Arc>, _d1_pin: &Arc>, _d2_pin: &Arc>, _d3_pin: &Arc>, address: u16, _expected_data: u8, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for maximum access time (750ns) thread::sleep(Duration::from_nanos(760)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for data to be driven thread::sleep(Duration::from_nanos(10)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } #[test] fn test_address_setup_hold_times() { // Test address setup and hold timing requirements with comprehensive edge cases let mut chip = Intel4001::new_with_access_time("SETUP_HOLD_ROM".to_string(), 500); // Standard access time chip.base.set_running(true); // Get pin references let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let reset_pin = chip.get_pin("RESET").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); let d0_pin = chip.get_pin("D0").unwrap(); let d1_pin = chip.get_pin("D1").unwrap(); let d2_pin = chip.get_pin("D2").unwrap(); let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test address setup time (minimum 100ns before PHI2 rising edge) // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time let address = 0x34; let expected_data = 0x06; // 4-bit value (low nibble only) // Set up ROM data chip.load_rom_data(vec![expected_data], address).unwrap(); // Test memory read using the helper function test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u8, expected_data, ); // Test multiple address setup/hold scenarios for test_addr in 0x30..0x35 { let test_data = vec![test_addr as u8 * 2]; chip.load_rom_data(test_data, test_addr).unwrap(); test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, test_addr as u8, test_addr as u8 * 2, ); } // Enhanced edge case testing for address setup and hold times // Test edge case: minimum address setup time (100ns) // Address must be stable for at least 100ns before PHI2 rising edge let setup_test_addresses = [0x10, 0x20, 0x30, 0x40, 0x50]; for &test_addr in &setup_test_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with minimum setup time let result = test_address_setup_time( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 100, ); // 100ns setup time assert_eq!( result, test_addr as u8, "Failed minimum setup time test at address {}", test_addr ); } // Test edge case: address hold time after PHI2 rising edge // Address must remain stable for at least 50ns after PHI2 rising edge let hold_test_addresses = [0x11, 0x21, 0x31, 0x41, 0x51]; for &test_addr in &hold_test_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with minimum hold time let result = test_address_hold_time( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 50, ); // 50ns hold time assert_eq!( result, test_addr as u8, "Failed minimum hold time test at address {}", test_addr ); } // Test edge case: address setup time violations // Test what happens when address changes too close to PHI2 rising edge let mut violation_chip = Intel4001::new_with_access_time("VIOLATION_ROM".to_string(), 500); violation_chip.base.set_running(true); violation_chip.load_rom_data(vec![0x99], 0x60).unwrap(); let violation_sync = violation_chip.get_pin("SYNC").unwrap(); let violation_cm = violation_chip.get_pin("CM").unwrap(); let violation_ci = violation_chip.get_pin("CI").unwrap(); let violation_phi1 = violation_chip.get_pin("PHI1").unwrap(); let violation_phi2 = violation_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 violation_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test setup time violation let result = test_address_setup_time( &mut violation_chip, &violation_sync, &violation_cm, &violation_ci, &violation_phi1, &violation_phi2, 0x60, 50, ); // Only 50ns setup time (violation) // The chip might still work due to implementation tolerances, but this tests the edge case println!("Setup time violation test result: 0x{:02x}", result); // Test edge case: address hold time violations // Test what happens when address changes too soon after PHI2 rising edge let result = test_address_hold_time( &mut violation_chip, &violation_sync, &violation_cm, &violation_ci, &violation_phi1, &violation_phi2, 0x60, 25, ); // Only 25ns hold time (violation) // The chip might still work due to implementation tolerances, but this tests the edge case println!("Hold time violation test result: 0x{:02x}", result); // Test edge case: address stability during both setup and hold let stability_test_addresses = [0x70, 0x71, 0x72, 0x73, 0x74]; for &test_addr in &stability_test_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with both setup and hold times let result = test_address_stability( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 100, 50, ); // 100ns setup, 50ns hold assert_eq!( result, test_addr as u8, "Failed address stability test at address {}", test_addr ); } // Test edge case: address transitions at different phases // Test address changes during different clock phases let phase_test_addresses = [0x80, 0x81, 0x82, 0x83]; for &test_addr in &phase_test_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test address transition during PHI1 high let result1 = test_address_transition_timing( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, "PHI1_HIGH", ); assert_eq!( result1, test_addr as u8, "Failed address transition during PHI1_HIGH at address {}", test_addr ); // Test address transition during PHI1 low let result2 = test_address_transition_timing( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, "PHI1_LOW", ); assert_eq!( result2, test_addr as u8, "Failed address transition during PHI1_LOW at address {}", test_addr ); } // Test edge case: address nibble timing // Test timing between high and low nibble latching let nibble_test_addresses = [0x90, 0x91, 0x92, 0x93]; for &test_addr in &nibble_test_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different delays between nibbles for nibble_delay in [0, 50, 100, 200] { let result = test_nibble_timing( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, nibble_delay, ); assert_eq!( result, test_addr as u8, "Failed nibble timing test with {}ns delay at address {}", nibble_delay, test_addr ); } } // Test edge case: address setup with I/O operations // Test address timing when I/O operations are active chip.load_rom_data(vec![0xA5], 0xA0).unwrap(); chip.set_io_mode(IoMode::Output); chip.set_io_direction(0, IoDirection::Output).unwrap(); chip.set_io_port(0, 0x0F).unwrap(); let result = test_address_setup_with_io( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, 0xA0, 100, ); assert_eq!(result, 0xA5, "Failed address setup with active I/O"); // Test edge case: address setup at different clock frequencies // Test address timing with different clock periods let clock_periods = [100, 200, 500, 1000]; // ns for &period in &clock_periods { chip.load_rom_data(vec![0xB0 + (period / 100) as u8], 0xB0) .unwrap(); let result = test_address_setup_with_clock_period( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, 0xB0, 100, period, ); assert_eq!( result, 0xB0 + (period / 100) as u8, "Failed address setup with clock period {}ns", period ); } } // Helper function for testing address setup time fn test_address_setup_time( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, setup_time_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for specified setup time thread::sleep(Duration::from_nanos(setup_time_ns)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address hold time fn test_address_hold_time( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, hold_time_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for specified hold time thread::sleep(Duration::from_nanos(hold_time_ns)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address stability fn test_address_stability( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, setup_time_ns: u64, hold_time_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for setup time thread::sleep(Duration::from_nanos(setup_time_ns)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Wait for hold time thread::sleep(Duration::from_nanos(hold_time_ns)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address transition timing fn test_address_transition_timing( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, phase: &str, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); match phase { "PHI1_HIGH" => { // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble during PHI1 high chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble during PHI1 high chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); } "PHI1_LOW" => { // Set PHI1 high first { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set PHI1 low - this latches whatever was on the bus { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Set high nibble during PHI1 low - won't be latched yet chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 low to high: Next address phase cycle { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 low to high again { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); } _ => panic!("Invalid phase: {}", phase), } // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing nibble timing fn test_nibble_timing( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, nibble_delay_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Delay between nibbles thread::sleep(Duration::from_nanos(nibble_delay_ns)); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address setup with I/O operations fn test_address_setup_with_io( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, setup_time_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Perform I/O operation during address setup chip.set_io_mode(IoMode::Output); chip.set_io_direction(1, IoDirection::Output).unwrap(); chip.set_io_port(1, 0x0A).unwrap(); chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for specified setup time thread::sleep(Duration::from_nanos(setup_time_ns)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address setup with different clock periods fn test_address_setup_with_clock_period( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, setup_time_ns: u64, clock_period_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble with clock period consideration { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half clock period thread::sleep(Duration::from_nanos(clock_period_ns / 2)); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half clock period thread::sleep(Duration::from_nanos(clock_period_ns / 2)); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for specified setup time thread::sleep(Duration::from_nanos(setup_time_ns)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } #[test] fn test_clock_edge_relationships() { // Test proper timing relationships between clock edges with comprehensive edge cases let mut chip = Intel4001::new_with_access_time("CLOCK_EDGE_ROM".to_string(), 400); // Medium access time chip.base.set_running(true); // Get pin references let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let reset_pin = chip.get_pin("RESET").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); let d0_pin = chip.get_pin("D0").unwrap(); let d1_pin = chip.get_pin("D1").unwrap(); let d2_pin = chip.get_pin("D2").unwrap(); let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test memory read with focus on clock edge relationships // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time let address = 0x45; let expected_data = 0x07; // 4-bit value (low nibble only) // Set up ROM data chip.load_rom_data(vec![expected_data], address).unwrap(); // Test memory read using the helper function test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u8, expected_data, ); // Test multiple clock edge scenarios for test_addr in 0x40..0x45 { let test_data = vec![test_addr as u8 * 3]; chip.load_rom_data(test_data, test_addr).unwrap(); test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, test_addr as u8, test_addr as u8 * 3, ); } // Enhanced edge case testing for clock edge relationships // Test edge case: PHI1 and PHI2 non-overlapping operation // Verify that PHI1 and PHI2 never overlap (both high at the same time) let non_overlap_addresses = [0x10, 0x20, 0x30, 0x40]; for &test_addr in &non_overlap_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); let result = test_phi1_phi2_non_overlapping( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, ); assert_eq!( result, test_addr as u8, "Failed PHI1/PHI2 non-overlapping test at address {}", test_addr ); } // Test edge case: PHI1 leading edge timing // Test address latching on PHI1 rising edge let leading_edge_addresses = [0x11, 0x21, 0x31, 0x41]; for &test_addr in &leading_edge_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); let result = test_phi1_leading_edge( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, ); assert_eq!( result, test_addr as u8, "Failed PHI1 leading edge test at address {}", test_addr ); } // Test edge case: PHI2 trailing edge timing // Test data valid on PHI2 falling edge let trailing_edge_addresses = [0x12, 0x22, 0x32, 0x42]; for &test_addr in &trailing_edge_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); let result = test_phi2_trailing_edge( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, ); assert_eq!( result, test_addr as u8, "Failed PHI2 trailing edge test at address {}", test_addr ); } // Test edge case: clock edge spacing // Test minimum and maximum spacing between clock edges let spacing_addresses = [0x13, 0x23, 0x33, 0x43]; for &test_addr in &spacing_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test minimum spacing let result1 = test_clock_edge_spacing( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 50, ); // 50ns spacing assert_eq!( result1, test_addr as u8, "Failed minimum clock edge spacing test at address {}", test_addr ); // Test maximum spacing let result2 = test_clock_edge_spacing( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 1000, ); // 1000ns spacing assert_eq!( result2, test_addr as u8, "Failed maximum clock edge spacing test at address {}", test_addr ); } // Test edge case: clock edge jitter // Test behavior with clock edge timing variations let jitter_addresses = [0x14, 0x24, 0x34, 0x44]; for &test_addr in &jitter_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with positive jitter let result1 = test_clock_edge_jitter( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 20, ); // 20ns positive jitter assert_eq!( result1, test_addr as u8, "Failed positive jitter test at address {}", test_addr ); // Test with negative jitter let result2 = test_clock_edge_jitter( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, -20, ); // 20ns negative jitter assert_eq!( result2, test_addr as u8, "Failed negative jitter test at address {}", test_addr ); } // Test edge case: clock edge violations // Test what happens when clock edges occur too close together let mut violation_chip = Intel4001::new_with_access_time("VIOLATION_CHIP".to_string(), 400); violation_chip.base.set_running(true); violation_chip.load_rom_data(vec![0x99], 0x50).unwrap(); let violation_sync = violation_chip.get_pin("SYNC").unwrap(); let violation_cm = violation_chip.get_pin("CM").unwrap(); let violation_ci = violation_chip.get_pin("CI").unwrap(); let violation_phi1 = violation_chip.get_pin("PHI1").unwrap(); let violation_phi2 = violation_chip.get_pin("PHI2").unwrap(); // Set settlement times to 0 violation_sync .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_cm .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_ci .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_phi1 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); violation_phi2 .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test clock edge violation let result = test_clock_edge_violation( &mut violation_chip, &violation_sync, &violation_cm, &violation_ci, &violation_phi1, &violation_phi2, 0x50, 10, ); // 10ns spacing (violation) // The chip might still work due to implementation tolerances, but this tests the edge case println!("Clock edge violation test result: 0x{:02x}", result); // Test edge case: clock edge sequences // Test different sequences of clock edges let sequence_addresses = [0x15, 0x25, 0x35, 0x45]; for &test_addr in &sequence_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test normal sequence: PHI1 high -> PHI1 low -> PHI2 high -> PHI2 low let result1 = test_clock_edge_sequence( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, "NORMAL", ); assert_eq!( result1, test_addr as u8, "Failed normal clock edge sequence test at address {}", test_addr ); // Test reversed sequence: PHI2 high -> PHI2 low -> PHI1 high -> PHI1 low let result2 = test_clock_edge_sequence( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, "REVERSED", ); // This might not work correctly, but tests the edge case println!( "Reversed clock edge sequence test result at address {}: 0x{:02x}", test_addr, result2 ); // Test simultaneous sequence: PHI1 and PHI2 high at same time let result3 = test_clock_edge_sequence( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, "SIMULTANEOUS", ); // This should not work correctly, but tests the edge case println!( "Simultaneous clock edge sequence test result at address {}: 0x{:02x}", test_addr, result3 ); } // Test edge case: clock edge duty cycle // Test different duty cycles for PHI1 and PHI2 let duty_cycle_addresses = [0x16, 0x26, 0x36, 0x46]; for &test_addr in &duty_cycle_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test 25% duty cycle let result1 = test_clock_edge_duty_cycle( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 25, ); assert_eq!( result1, test_addr as u8, "Failed 25% duty cycle test at address {}", test_addr ); // Test 50% duty cycle let result2 = test_clock_edge_duty_cycle( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 50, ); assert_eq!( result2, test_addr as u8, "Failed 50% duty cycle test at address {}", test_addr ); // Test 75% duty cycle let result3 = test_clock_edge_duty_cycle( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, 75, ); assert_eq!( result3, test_addr as u8, "Failed 75% duty cycle test at address {}", test_addr ); } // Test edge case: clock edge frequency // Test different clock frequencies let frequency_addresses = [0x17, 0x27, 0x37, 0x47]; let frequencies = [1000, 2000, 5000, 10000]; // Hz (periods: 1ms, 0.5ms, 0.2ms, 0.1ms) for (i, &test_addr) in frequency_addresses.iter().enumerate() { chip.load_rom_data(vec![test_addr as u8], (test_addr as u16).into()) .unwrap(); let result = test_clock_edge_frequency( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, frequencies[i], ); assert_eq!( result, test_addr as u8, "Failed clock frequency test at address {} with frequency {}Hz", test_addr, frequencies[i] ); } } // Helper function for testing PHI1/PHI2 non-overlapping operation fn test_phi1_phi2_non_overlapping( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Verify PHI2 is still low assert_eq!(phi2_pin.lock().unwrap().read(), PinValue::Low); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(410)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Verify PHI1 is still low assert_eq!(phi1_pin.lock().unwrap().read(), PinValue::Low); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing PHI1 leading edge timing fn test_phi1_leading_edge( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Verify state is Idle before PHI1 rising edge assert_eq!(chip.get_timing_state(), TimingState::Idle); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Verify state is AddressPhase after PHI1 rising edge assert_eq!(chip.get_timing_state(), TimingState::AddressPhase); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(410)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing PHI2 trailing edge timing fn test_phi2_trailing_edge( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(410)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Verify state is DriveData after PHI2 rising edge assert_eq!(chip.get_timing_state(), TimingState::DriveData); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Verify state is Idle after PHI2 falling edge assert_eq!(chip.get_timing_state(), TimingState::Idle); data } // Helper function for testing clock edge spacing fn test_clock_edge_spacing( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, spacing_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for specified spacing between PHI1 falling and PHI2 rising thread::sleep(Duration::from_nanos(spacing_ns)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing clock edge jitter fn test_clock_edge_jitter( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, jitter_ns: i64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble with jitter { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Apply jitter (positive or negative) if jitter_ns > 0 { thread::sleep(Duration::from_nanos(jitter_ns as u64)); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble with jitter { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Apply jitter (positive or negative) if jitter_ns > 0 { thread::sleep(Duration::from_nanos(jitter_ns as u64)); } chip.update(); // PHI1 low to high again: Address phase for low nibble with jitter { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Apply jitter (positive or negative) if jitter_ns > 0 { thread::sleep(Duration::from_nanos(jitter_ns as u64)); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address with jitter { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Apply jitter (positive or negative) if jitter_ns > 0 { thread::sleep(Duration::from_nanos(jitter_ns as u64)); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(410)); // Slightly more than access time // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase with jitter { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } // Apply jitter (positive or negative) if jitter_ns > 0 { thread::sleep(Duration::from_nanos(jitter_ns as u64)); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup with jitter { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Apply jitter (positive or negative) if jitter_ns > 0 { thread::sleep(Duration::from_nanos(jitter_ns as u64)); } chip.update(); data } // Helper function for testing clock edge violations fn test_clock_edge_violation( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, spacing_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for very short spacing (violation) thread::sleep(Duration::from_nanos(spacing_ns)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing clock edge sequences fn test_clock_edge_sequence( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, sequence: &str, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); match sequence { "NORMAL" => { // Normal sequence: PHI1 high -> PHI1 low -> PHI1 high -> PHI1 low (two cycles for address) -> PHI2 high -> PHI2 low // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); thread::sleep(Duration::from_nanos(410)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); let data = chip.read_data_bus(); { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); return data; } "REVERSED" => { // Reversed sequence: PHI2 high -> PHI2 low -> PHI1 high -> PHI1 low // This is an invalid sequence for the Intel 4004 { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); thread::sleep(Duration::from_nanos(410)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); return chip.read_data_bus(); } "SIMULTANEOUS" => { // Simultaneous sequence: PHI1 and PHI2 high at same time // This is an invalid sequence for the Intel 4004 (non-overlapping clocks required) { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); thread::sleep(Duration::from_nanos(410)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); return chip.read_data_bus(); } _ => panic!("Invalid sequence: {}", sequence), } } // Helper function for testing clock edge duty cycle fn test_clock_edge_duty_cycle( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, duty_cycle_percent: u8, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Calculate timing based on duty cycle let total_period_ns = 1000; // 1kHz clock let high_time_ns = (total_period_ns * duty_cycle_percent as u64) / 100; // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for high time thread::sleep(Duration::from_nanos(high_time_ns)); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for high time thread::sleep(Duration::from_nanos(high_time_ns)); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(410)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for high time thread::sleep(Duration::from_nanos(high_time_ns)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing clock edge frequency fn test_clock_edge_frequency( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, frequency_hz: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Calculate timing based on frequency let period_ns = 1_000_000_000 / frequency_hz; // Convert Hz to nanoseconds let half_period_ns = period_ns / 2; // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half period thread::sleep(Duration::from_nanos(half_period_ns)); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half period thread::sleep(Duration::from_nanos(half_period_ns)); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(410)); // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half period thread::sleep(Duration::from_nanos(half_period_ns)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing access time tolerance fn test_access_time_tolerance(chip: &mut Intel4001, address: u16, _expected_data: u8) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } chip.update(); // Get pins let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Ensure PHI2 is low initially { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high: Address phase for high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble on data bus chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch high nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // PHI1 low to high again: Address phase for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set low nibble on data bus chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Latch low nibble and complete address { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with tolerance let access_time = chip.get_access_time(); thread::sleep(Duration::from_nanos(access_time + 50)); // Add 50ns tolerance // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing memory read tolerance fn test_memory_read_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, timing_variation_ns: u64, ) -> u8 { // Reset to known state (RESET is active LOW) { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation) } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase with timing variation { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } thread::sleep(Duration::from_nanos(timing_variation_ns)); // Add timing variation chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase with timing variation { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } thread::sleep(Duration::from_nanos(timing_variation_ns)); // Add timing variation chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // PHI2 low to high: Data phase with timing variation { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } thread::sleep(Duration::from_nanos(timing_variation_ns)); // Add timing variation chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup with timing variation { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } thread::sleep(Duration::from_nanos(timing_variation_ns)); // Add timing variation chip.update(); data } // Helper function for testing timing variation tolerance fn test_timing_variation_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, variation_ns: u64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase with variation { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble with variation chip.write_data_bus(((address >> 4) & 0x0F) as u8); thread::sleep(Duration::from_nanos(variation_ns)); chip.update(); // Set low nibble with variation chip.write_data_bus((address & 0x0F) as u8); thread::sleep(Duration::from_nanos(variation_ns)); chip.update(); // PHI1 high to low: Complete address phase with variation { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with variation thread::sleep(Duration::from_nanos(510 + variation_ns)); // PHI2 low to high: Data phase with variation { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup with variation { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing clock period tolerance fn test_clock_period_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, clock_period_ns: u64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase with clock period consideration { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half clock period thread::sleep(Duration::from_nanos(clock_period_ns / 2)); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Wait for half clock period thread::sleep(Duration::from_nanos(clock_period_ns / 2)); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for clock period thread::sleep(Duration::from_nanos(clock_period_ns)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for half clock period thread::sleep(Duration::from_nanos(clock_period_ns / 2)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address setup tolerance fn test_address_setup_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, setup_time_ns: u64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for specified setup time thread::sleep(Duration::from_nanos(setup_time_ns)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing address hold tolerance fn test_address_hold_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, hold_time_ns: u64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time thread::sleep(Duration::from_nanos(510)); // Slightly more than access time // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Wait for specified hold time thread::sleep(Duration::from_nanos(hold_time_ns)); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing voltage and temperature tolerance fn test_voltage_temperature_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, voltage: f64, temperature: f64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate voltage and temperature effects // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on voltage and temperature let voltage_delay = if voltage < 5.0 { 20 } else if voltage > 5.0 { 10 } else { 0 }; let temperature_delay = if temperature < 25.0 { 15 } else if temperature > 25.0 { 25 } else { 0 }; let total_delay = voltage_delay + temperature_delay; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with voltage/temperature compensation thread::sleep(Duration::from_nanos(510 + total_delay)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing voltage tolerance fn test_voltage_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, voltage: f64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate voltage effects // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on voltage let voltage_delay = if voltage < 5.0 { 20 } else if voltage > 5.0 { 10 } else { 0 }; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with voltage compensation thread::sleep(Duration::from_nanos(510 + voltage_delay)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing temperature tolerance fn test_temperature_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, temperature: f64, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate temperature effects // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on temperature let temperature_delay = if temperature < 25.0 { 15 } else if temperature > 25.0 { 25 } else { 0 }; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with temperature compensation thread::sleep(Duration::from_nanos(510 + temperature_delay)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing manufacturing tolerance fn test_manufacturing_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, variation_percent: i32, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate manufacturing variations // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on variation let base_access_time = 500; // ns let variation = (base_access_time * variation_percent as u64) / 100; let adjusted_access_time = base_access_time + variation; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for adjusted access time thread::sleep(Duration::from_nanos(adjusted_access_time + 10)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing manufacturing variation fn test_manufacturing_variation( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, variation_type: &str, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate manufacturing variations // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on variation type let variation_delay = match variation_type { "FAST" => -50, // 50ns faster "SLOW" => 100, // 100ns slower "TYPICAL" => 0, // No variation "MIN" => -100, // 100ns faster (minimum) "MAX" => 200, // 200ns slower (maximum) _ => 0, }; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with variation let base_access_time = 500; // ns let adjusted_access_time = if variation_delay >= 0 { base_access_time + variation_delay as u64 } else { base_access_time - (-variation_delay) as u64 }; thread::sleep(Duration::from_nanos(adjusted_access_time + 10)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing aging tolerance fn test_aging_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, years: u32, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate aging effects // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on years let aging_delay = years * 5; // 5ns per year // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with aging compensation thread::sleep(Duration::from_nanos((510 + aging_delay) as u64)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing aging effect fn test_aging_effect( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, aging_effect: &str, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate aging effects // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on aging effect let aging_delay = match aging_effect { "FRESH" => 0, // No aging "WORN" => 50, // 50ns slower "DEGRADED" => 100, // 100ns slower "FAILING" => 200, // 200ns slower _ => 0, }; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with aging compensation thread::sleep(Duration::from_nanos(510 + aging_delay)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } // Helper function for testing extreme condition tolerance fn test_extreme_condition_tolerance( chip: &mut Intel4001, sync_pin: &Arc>, cm_pin: &Arc>, ci_pin: &Arc>, phi1_pin: &Arc>, phi2_pin: &Arc>, address: u16, _expected_data: u8, voltage: f64, temperature: f64, aging_years: u32, ) -> u8 { // Reset to known state { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); { let reset_pin = chip.get_pin("RESET").unwrap(); let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Simulate extreme condition effects // In a real implementation, this would affect timing parameters // For this test, we'll just add a delay based on conditions let voltage_delay = if voltage < 5.0 { 20 } else if voltage > 5.0 { 10 } else { 0 }; let temperature_delay = if temperature < 25.0 { 15 } else if temperature > 25.0 { 25 } else { 0 }; let aging_delay = aging_years * 5; // 5ns per year let total_delay = voltage_delay + temperature_delay + aging_delay; // Set control pins for memory read { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // PHI1 low to high: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Set high nibble chip.write_data_bus(((address >> 4) & 0x0F) as u8); chip.update(); // Set low nibble chip.write_data_bus((address & 0x0F) as u8); chip.update(); // PHI1 high to low: Complete address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Wait for access time with extreme condition compensation thread::sleep(Duration::from_nanos((510 + total_delay) as u64)); // PHI2 low to high: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); // Read data let data = chip.read_data_bus(); // PHI2 high to low: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); data } #[test] fn test_timing_tolerances() { // Test behavior with timing variations and tolerances with comprehensive edge cases let mut chip = Intel4001::new_with_access_time("TOLERANCE_ROM".to_string(), 500); // Standard access time chip.base.set_running(true); // Get pin references let sync_pin = chip.get_pin("SYNC").unwrap(); let cm_pin = chip.get_pin("CM").unwrap(); let ci_pin = chip.get_pin("CI").unwrap(); let reset_pin = chip.get_pin("RESET").unwrap(); let phi1_pin = chip.get_pin("PHI1").unwrap(); let phi2_pin = chip.get_pin("PHI2").unwrap(); let d0_pin = chip.get_pin("D0").unwrap(); let d1_pin = chip.get_pin("D1").unwrap(); let d2_pin = chip.get_pin("D2").unwrap(); let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Test memory read with various timing tolerances let address = 0x78; let expected_data = 0x0A; // 4-bit value (low nibble only) // Set up ROM data chip.load_rom_data(vec![expected_data], address).unwrap(); // Test memory read using the helper function test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, address as u8, expected_data, ); // Test multiple timing tolerance scenarios for test_addr in 0x78..0x80 { let test_data = vec![test_addr as u8 * 2]; chip.load_rom_data(test_data, test_addr).unwrap(); test_memory_read( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, &d0_pin, &d1_pin, &d2_pin, &d3_pin, test_addr as u8, test_addr as u8 * 2, ); } // Enhanced edge case testing for timing tolerances // Test edge case: access time tolerance variations let tolerance_addresses = [0x10, 0x20, 0x30, 0x40]; for &test_addr in &tolerance_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); let result = test_access_time_tolerance(&mut chip, test_addr as u16, test_addr as u8); assert_eq!( result, test_addr as u8, "Failed access time tolerance test at address {}", test_addr ); } // Test edge case: memory read with timing variations let variation_addresses = [0x11, 0x21, 0x31, 0x41]; for &test_addr in &variation_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different timing variations for variation in [0, 10, 20, 50, 100] { let result = test_memory_read_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, variation, ); assert_eq!( result, test_addr as u8, "Failed memory read tolerance test with {}ns variation at address {}", variation, test_addr ); } } // Test edge case: timing variation tolerance let variation_test_addresses = [0x12, 0x22, 0x32, 0x42]; for &test_addr in &variation_test_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different timing variations for variation in [0, 25, 50, 75, 100] { let result = test_timing_variation_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, variation, ); assert_eq!( result, test_addr as u8, "Failed timing variation tolerance test with {}ns variation at address {}", variation, test_addr ); } } // Test edge case: clock period tolerance let clock_period_addresses = [0x13, 0x23, 0x33, 0x43]; for &test_addr in &clock_period_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different clock periods for period in [100, 200, 500, 1000] { let result = test_clock_period_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, period, ); assert_eq!( result, test_addr as u8, "Failed clock period tolerance test with {}ns period at address {}", period, test_addr ); } } // Test edge case: address setup tolerance let setup_tolerance_addresses = [0x14, 0x24, 0x34, 0x44]; for &test_addr in &setup_tolerance_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different setup times for setup_time in [50, 100, 150, 200] { let result = test_address_setup_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, setup_time, ); assert_eq!( result, test_addr as u8, "Failed address setup tolerance test with {}ns setup time at address {}", setup_time, test_addr ); } } // Test edge case: address hold tolerance let hold_tolerance_addresses = [0x15, 0x25, 0x35, 0x45]; for &test_addr in &hold_tolerance_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different hold times for hold_time in [25, 50, 75, 100] { let result = test_address_hold_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, hold_time, ); assert_eq!( result, test_addr as u8, "Failed address hold tolerance test with {}ns hold time at address {}", hold_time, test_addr ); } } // Test edge case: voltage and temperature tolerance let voltage_temp_addresses = [0x16, 0x26, 0x36, 0x46]; for &test_addr in &voltage_temp_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different voltage and temperature combinations let test_conditions = [ (4.5, 0.0), // Low voltage, low temperature (5.0, 25.0), // Nominal voltage, nominal temperature (5.5, 50.0), // High voltage, high temperature (4.0, -10.0), // Very low voltage, very low temperature (6.0, 70.0), // Very high voltage, very high temperature ]; for (voltage, temperature) in test_conditions { let result = test_voltage_temperature_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, voltage, temperature, ); assert_eq!( result, test_addr as u8, "Failed voltage/temperature tolerance test at address {} with {}V, {}°C", test_addr, voltage, temperature ); } } // Test edge case: voltage tolerance let voltage_addresses = [0x17, 0x27, 0x37, 0x47]; for &test_addr in &voltage_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different voltages let test_voltages = [3.5, 4.0, 4.5, 5.0, 5.5, 6.0]; for voltage in test_voltages { let result = test_voltage_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, voltage, ); assert_eq!( result, test_addr as u8, "Failed voltage tolerance test at address {} with {}V", test_addr, voltage ); } } // Test edge case: temperature tolerance let temperature_addresses = [0x18, 0x28, 0x38, 0x48]; for &test_addr in &temperature_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different temperatures let test_temperatures = [-20.0, 0.0, 25.0, 50.0, 70.0, 85.0]; for temperature in test_temperatures { let result = test_temperature_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, temperature, ); assert_eq!( result, test_addr as u8, "Failed temperature tolerance test at address {} with {}°C", test_addr, temperature ); } } // Test edge case: manufacturing tolerance let manufacturing_addresses = [0x19, 0x29, 0x39, 0x49]; for &test_addr in &manufacturing_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different manufacturing variations let variations = [-20, -10, 0, 10, 20]; // Percentage variation for variation in variations { let result = test_manufacturing_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, variation, ); assert_eq!( result, test_addr as u8, "Failed manufacturing tolerance test at address {} with {}% variation", test_addr, variation ); } } // Test edge case: manufacturing variation let manufacturing_var_addresses = [0x1A, 0x2A, 0x3A, 0x4A]; for &test_addr in &manufacturing_var_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different manufacturing variation types let variation_types = ["FAST", "SLOW", "TYPICAL", "MIN", "MAX"]; for variation_type in variation_types { let result = test_manufacturing_variation( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, variation_type, ); assert_eq!( result, test_addr as u8, "Failed manufacturing variation test at address {} with {} variation", test_addr, variation_type ); } } // Test edge case: aging tolerance let aging_addresses = [0x1B, 0x2B, 0x3B, 0x4B]; for &test_addr in &aging_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different aging years let aging_years = [0, 1, 5, 10, 20, 30]; for years in aging_years { let result = test_aging_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, years, ); assert_eq!( result, test_addr as u8, "Failed aging tolerance test at address {} with {} years", test_addr, years ); } } // Test edge case: aging effect let aging_effect_addresses = [0x1C, 0x2C, 0x3C, 0x4C]; for &test_addr in &aging_effect_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with different aging effects let aging_effects = ["FRESH", "WORN", "DEGRADED", "FAILING"]; for aging_effect in aging_effects { let result = test_aging_effect( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, aging_effect, ); assert_eq!( result, test_addr as u8, "Failed aging effect test at address {} with {} effect", test_addr, aging_effect ); } } // Test edge case: extreme condition tolerance let extreme_addresses = [0x1D, 0x2D, 0x3D, 0x4D]; for &test_addr in &extreme_addresses { chip.load_rom_data(vec![test_addr as u8], test_addr) .unwrap(); // Test with extreme combinations of voltage, temperature, and aging let extreme_conditions = [ (3.5, -20.0, 30), // Low voltage, low temperature, high aging (6.0, 85.0, 30), // High voltage, high temperature, high aging (4.0, 0.0, 20), // Low voltage, low temperature, medium aging (5.5, 70.0, 10), // High voltage, high temperature, low aging ]; for (voltage, temperature, aging_years) in extreme_conditions { let result = test_extreme_condition_tolerance( &mut chip, &sync_pin, &cm_pin, &ci_pin, &phi1_pin, &phi2_pin, test_addr as u16, test_addr as u8, voltage, temperature, aging_years, ); assert_eq!(result, test_addr as u8, "Failed extreme condition tolerance test at address {} with {}V, {}°C, {} years", test_addr, voltage, temperature, aging_years); } } // Test with different access times let mut fast_chip = Intel4001::new_with_access_time("FAST_ROM".to_string(), 200); fast_chip.base.set_running(true); let mut slow_chip = Intel4001::new_with_access_time("SLOW_ROM".to_string(), 750); slow_chip.base.set_running(true); // Test fast chip let fast_address = 0x90; let fast_expected = 0x12; fast_chip .load_rom_data(vec![fast_expected], fast_address) .unwrap(); // Test slow chip let slow_address = 0xA0; let slow_expected = 0x34; slow_chip .load_rom_data(vec![slow_expected], slow_address) .unwrap(); // Both chips should work correctly with their respective access times // Note: We can't directly test the timing differences without more complex // timing simulation, but we can verify that both chips can read data correctly } #[test] fn comprehensive_intel4001_integration_test() { // Setup section println!("=== Comprehensive Intel 4001 Integration Test: Setup ==="); // Create Intel4001 with fast access time let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 1); // 1ns for fast access rom.base.set_running(true); // Load test data: simple arithmetic sequence let test_data = vec![ 0x01, 0x02, 0x03, 0x04, 0x05, 0x10, 0x20, 0x30, 0x40, 0x50, 0xFF, ]; rom.load_rom_data(test_data.clone(), 0).unwrap(); // Verify test data loaded for (i, &data) in test_data.iter().enumerate() { assert_eq!(rom.read_rom(i as u8).unwrap(), data); } // Get pin references let sync_pin = rom.get_pin("SYNC").unwrap(); let cm_pin = rom.get_pin("CM").unwrap(); let ci_pin = rom.get_pin("CI").unwrap(); let reset_pin = rom.get_pin("RESET").unwrap(); let phi1_pin = rom.get_pin("PHI1").unwrap(); let phi2_pin = rom.get_pin("PHI2").unwrap(); let d0_pin = rom.get_pin("D0").unwrap(); let d1_pin = rom.get_pin("D1").unwrap(); let d2_pin = rom.get_pin("D2").unwrap(); let d3_pin = rom.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing sync_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); cm_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); ci_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); phi2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d0_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d1_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d2_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); d3_pin .lock() .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Initially, all data pins should be HighZ assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); // Set initial pin states // Set reset high initially { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); assert_eq!(rom.get_io_mode(), IoMode::Input); // Set reset low { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Memory Access Verification Section println!("=== Memory Access Verification ==="); // Set data bus to 0 to avoid interference for i in 0..4 { let pin = rom.get_pin(&format!("D{}", i)).unwrap(); let mut pin_guard = pin.lock().unwrap(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Ensure state is Idle { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); { let mut reset_guard = reset_pin.lock().unwrap(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Set SYNC=High, CM=High, CI=Low for memory mode. { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Now loop over test addresses for (i, &expected_data) in test_data.iter().enumerate() { let address = i as u8; println!( "Testing address 0x{:02x} with expected data 0x{:02x}", address, expected_data ); // Ensure PHI2 is Low { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Ensure PHI1 is Low for rising edge { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // PHI1 rising: Address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // Write high nibble rom.write_data_bus((address >> 4) & 0x0F); rom.update(); // Write low nibble rom.write_data_bus(address & 0x0F); rom.update(); rom.update(); // Additional update to trigger state transition assert_eq!(rom.get_timing_state(), TimingState::WaitLatency); // Wait for latency (1ns, but since access time is 200ns, need to wait) thread::sleep(Duration::from_nanos(10)); // As in existing test // PHI2 rising: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::DriveData); // Check data on bus let data = rom.read_data_bus(); assert_eq!(data, expected_data); // PHI2 falling: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Tri-state pins assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); } println!("Memory Access Verification completed successfully!"); // Pin-Level Testing Section println!("=== Pin-Level Testing ==="); // Set control pins to Low for initial state testing { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low); cm_guard.set_driver(Some("TEST".to_string()), PinValue::Low); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } // Test each pin's initial state (HighZ for data/IO, Low for control) assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); // I/O pins should also be HighZ initially let io0_pin = rom.get_pin("IO0").unwrap(); let io1_pin = rom.get_pin("IO1").unwrap(); let io2_pin = rom.get_pin("IO2").unwrap(); let io3_pin = rom.get_pin("IO3").unwrap(); assert_eq!(io0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(io1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(io2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(io3_pin.lock().unwrap().read(), PinValue::HighZ); // Control pins should be Low initially assert_eq!(sync_pin.lock().unwrap().read(), PinValue::Low); assert_eq!(cm_pin.lock().unwrap().read(), PinValue::Low); assert_eq!(ci_pin.lock().unwrap().read(), PinValue::Low); assert_eq!(reset_pin.lock().unwrap().read(), PinValue::Low); // After setting to Low assert_eq!(phi1_pin.lock().unwrap().read(), PinValue::Low); assert_eq!(phi2_pin.lock().unwrap().read(), PinValue::Low); // Set drivers for each pin (High, Low) and verify read values // Test D0: Set to High { let mut d0_guard = d0_pin.lock().unwrap(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); } assert_eq!(d0_pin.lock().unwrap().read(), PinValue::High); // Test D0: Set to Low { let mut d0_guard = d0_pin.lock().unwrap(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Test SYNC: Set to High { let mut sync_guard = sync_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); } assert_eq!(sync_pin.lock().unwrap().read(), PinValue::High); // Test SYNC: Set to Low { let mut sync_guard = sync_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } assert_eq!(sync_pin.lock().unwrap().read(), PinValue::Low); // Test HighZ state for data and IO pins // D0 back to HighZ { let mut d0_guard = d0_pin.lock().unwrap(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); // IO0 to HighZ (should already be) assert_eq!(io0_pin.lock().unwrap().read(), PinValue::HighZ); // Simulate clock edges (PHI1/2 rising/falling) and check for state changes // Set up for memory access to trigger state changes { let mut sync_guard = sync_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock().unwrap(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); cm_guard.set_driver(Some("TEST".to_string()), PinValue::High); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access } rom.update(); // Update to process pin changes // PHI1 rising: Should start address phase { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // Latch address nibbles to progress state rom.write_data_bus(0x00); // High nibble rom.update(); // PHI1 falling { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // PHI1 rising again for low nibble { let mut phi1_guard = phi1_pin.lock().unwrap(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.write_data_bus(0x00); // Low nibble rom.update(); assert_eq!(rom.get_timing_state(), TimingState::WaitLatency); // Latency is immediate for testing // PHI2 rising: Data phase { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::DriveData); // PHI2 falling: Cleanup { let mut phi2_guard = phi2_pin.lock().unwrap(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Assert pin values match expectations // After cleanup, data pins should be HighZ assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); println!("Pin-Level Testing completed successfully!"); // I/O Integration Testing Section println!("=== I/O Integration Testing ==="); // Set CI=High for I/O mode { let mut ci_guard = ci_pin.lock().unwrap(); ci_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); // Test each I/O port (0-3): Set mode to Output, write data, verify port value and direction for port in 0..4 { println!("Testing I/O port {}", port); // Set to Output mode rom.set_io_direction(port, IoDirection::Output).unwrap(); rom.set_io_mode(IoMode::Output); rom.update(); // Write data to port let test_value = ((port + 1) * 0x05) as u8; // 0x05, 0x0A, 0x0F, 0x14 rom.set_io_port(port, test_value).unwrap(); rom.update(); // Verify port value assert_eq!(rom.get_io_port(port).unwrap(), test_value); // Verify direction assert_eq!(rom.get_io_direction(port).unwrap(), IoDirection::Output); // Test I/O direction control and data flow // In output mode, data should be driven to I/O pins // Note: In real hardware, output would drive the pin, but in this sim, it's internal // For testing, we check the latch assert_eq!(rom.get_output_latch(), test_value); } // Test Input mode for port in 0..4 { println!("Testing I/O port {} in Input mode", port); rom.set_io_direction(port, IoDirection::Input).unwrap(); rom.set_io_mode(IoMode::Input); rom.update(); // Set port value for input let input_value = ((port + 1) * 0x03) as u8; // 0x03, 0x06, 0x09, 0x0C rom.set_io_port(port, input_value).unwrap(); rom.update(); // Verify port value assert_eq!(rom.get_io_port(port).unwrap(), input_value); // Verify direction assert_eq!(rom.get_io_direction(port).unwrap(), IoDirection::Input); // In input mode, reading should get the value assert_eq!(rom.get_input_latch(), input_value); } // Test RDM/WRM operations via control pins // Simulate WRM (Write ROM, but for I/O) rom.set_io_mode(IoMode::Output); rom.write_data_bus(0x0F); // Data to write rom.update(); // For port 0 rom.write_data_bus(0x00); // Address port 0 rom.update(); // This should set the port assert_eq!(rom.get_io_port(0).unwrap(), 0x0F); // Simulate RDM (Read Data Memory, for I/O) rom.set_io_mode(IoMode::Input); rom.write_data_bus(0x00); // Address port 0 rom.update(); // Should read the value assert_eq!(rom.get_input_latch(), 0x0F); println!("I/O Integration Testing completed successfully!"); // Debugging Features Section println!("=== Debugging Features ==="); // Include comprehensive logging and state inspection rom.debug_state_transitions("Comprehensive Test"); // Provide pin state snapshots at each test phase println!( "Final Pin states: SYNC: {:?}, CM: {:?}, CI: {:?}, PHI1: {:?}, PHI2: {:?}", sync_pin.lock().unwrap().read(), cm_pin.lock().unwrap().read(), ci_pin.lock().unwrap().read(), phi1_pin.lock().unwrap().read(), phi2_pin.lock().unwrap().read() ); println!( "Data pins: D0: {:?}, D1: {:?}, D2: {:?}, D3: {:?}", d0_pin.lock().unwrap().read(), d1_pin.lock().unwrap().read(), d2_pin.lock().unwrap().read(), d3_pin.lock().unwrap().read() ); println!( "I/O pins: IO0: {:?}, IO1: {:?}, IO2: {:?}, IO3: {:?}", io0_pin.lock().unwrap().read(), io1_pin.lock().unwrap().read(), io2_pin.lock().unwrap().read(), io3_pin.lock().unwrap().read() ); // Enable step-through debugging of memory access cycles // (Already done in memory access section) // Include failure analysis and diagnostic information // If any assertion fails, the test will stop with details println!("Debugging Features completed successfully!"); // Test Data Section println!("=== Test Data ==="); // Use a known test program (e.g., simple arithmetic sequence) // Already loaded: vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x10, 0x20, 0x30, 0x40, 0x50, 0xFF] // Include edge case data patterns for thorough testing let edge_cases = vec![ (0x00, "All zeros"), (0xFF, "All ones"), (0x55, "Alternating bits"), (0xAA, "Alternating bits inverted"), (0x0F, "Low nibble ones"), (0xF0, "High nibble ones"), ]; for (data, desc) in edge_cases { println!("Testing edge case: {} (0x{:02x})", desc, data); rom.load_rom_data(vec![data; 256], 0).unwrap(); let read_data = rom.read_rom(0).unwrap(); assert_eq!(read_data, data, "Failed for {}", desc); } // Provide both valid and invalid test scenarios // Valid: addresses 0-255 for addr in 0..10 { // Test first 10 let data = rom.read_rom(addr).unwrap(); assert_eq!(data, 0x00); // Since we loaded all zeros last } // Test boundary address 0xFF assert_eq!(rom.read_rom(255).unwrap(), 0x00); // Since we loaded all zeros println!("Test Data completed successfully!"); println!("Comprehensive Intel 4001 Integration Test completed successfully!"); } }