rusty_emu/tests/intel_400x_tests.rs

7087 lines
243 KiB
Rust

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<String, Arc<Mutex<rusty_emu::pin::Pin>>> {
HashMap::new()
}
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<rusty_emu::pin::Pin>>, 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<u8> = None;
let mut low_nibble: Option<u8> = None;
let mut full_address_ready = false;
let mut address_latch_time: Option<Instant> = 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
println!("=== Testing Reset ===");
{
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);
}
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
d0_pin: &Arc<Mutex<Pin>>,
d1_pin: &Arc<Mutex<Pin>>,
d2_pin: &Arc<Mutex<Pin>>,
d3_pin: &Arc<Mutex<Pin>>,
address: u8,
expected_data: u8,
) {
// Reset to known state first
{
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);
}
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::Low);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
_d0_pin: &Arc<Mutex<Pin>>,
_d1_pin: &Arc<Mutex<Pin>>,
_d2_pin: &Arc<Mutex<Pin>>,
_d3_pin: &Arc<Mutex<Pin>>,
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
let address = 0x23;
let expected_data = 0x45;
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
_d0_pin: &Arc<Mutex<Pin>>,
_d1_pin: &Arc<Mutex<Pin>>,
_d2_pin: &Arc<Mutex<Pin>>,
_d3_pin: &Arc<Mutex<Pin>>,
address: u16,
_expected_data: u8,
) -> 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);
}
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)
let address = 0x34;
let expected_data = 0x56;
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
setup_time_ns: u64,
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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
phase: &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();
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
nibble_delay_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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
setup_time_ns: u64,
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);
}
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
let address = 0x45;
let expected_data = 0x67;
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
) -> 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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
) -> 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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
) -> 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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
spacing_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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
jitter_ns: i64,
) -> 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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
spacing_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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
sequence: &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();
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
duty_cycle_percent: u8,
) -> 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);
}
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
frequency_hz: 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);
}
// 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
{
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();
// 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
address: u16,
_expected_data: u8,
timing_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 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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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<Mutex<Pin>>,
cm_pin: &Arc<Mutex<Pin>>,
ci_pin: &Arc<Mutex<Pin>>,
phi1_pin: &Arc<Mutex<Pin>>,
phi2_pin: &Arc<Mutex<Pin>>,
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 = 0x9A;
// 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!");
}
}