rusty_emu/tests/intel_4001_state_machine_test.rs

371 lines
13 KiB
Rust

use std::thread;
use std::time::Duration;
use rusty_emu::component::Component;
use rusty_emu::components::common::intel_400x::{Intel400xTimingState, TimingState};
use rusty_emu::components::memory::intel_4001::Intel4001;
use rusty_emu::pin::PinValue;
/// Test to verify that Intel 4001 ROM unified state machine progresses correctly
/// This test isolates unified state machine logic to verify proper state transitions
#[test]
fn test_intel_4001_state_machine_progression() {
let mut rom = Intel4001::new_with_access_time("StateMachineTestROM".to_string(), 1); // 1ns for fast testing
// Load test data: address 0x12 should contain 0xAB
let test_data = vec![0x00; 0x12]; // Initialize with zeros
let mut data_with_test = test_data;
data_with_test.push(0xAB); // Add 0xAB at address 0x12
rom.load_rom_data(data_with_test, 0).unwrap();
// Get pin references for simulation
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 phi1_pin = rom.get_pin("PHI1").unwrap();
let phi2_pin = rom.get_pin("PHI2").unwrap();
let reset_pin = rom.get_pin("RESET").unwrap();
// Data pins for address and data verification
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();
// Start component
rom.base.set_running(true);
assert!(rom.is_running());
// Initialize clock pins to Low
{
let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Initialize control pins
// Note: RESET is active LOW, so set it to High to ensure component is not in reset
{
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
let mut reset_guard = reset_pin.lock();
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);
reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Not in reset
}
// Initialize data pins to Low
{
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to initialize clock states
rom.update();
// Verify initial state
assert_eq!(rom.get_timing_state(), TimingState::Idle);
println!("Initial state: {:?}", rom.get_timing_state());
// Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
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
}
// Set up address high nibble (0x1 for address 0x12)
{
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
// 0x1 = 0001 (D0=1, D1=0, D2=0, D3=0)
d0_guard.set_driver(Some("TEST".to_string()), PinValue::High);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Wait for pins to settle
thread::sleep(Duration::from_millis(1));
// Trigger Φ1 rising edge to start memory operation and latch high nibble
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
// Update to process Φ1 rising edge
rom.update();
// Should have started memory operation and transitioned to AddressPhase
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
println!(
"State after Φ1 rising (high nibble): {:?}",
rom.get_timing_state()
);
// Reset Φ1 to Low
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to process Φ1 falling edge
rom.update();
// Set up address low nibble (0x2 for address 0x12)
{
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
// 0x2 = 0010 (D0=0, D1=1, D2=0, D3=0)
d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Wait for pins to settle
thread::sleep(Duration::from_millis(1));
// Trigger Φ1 rising edge again - ROM should stay in AddressPhase
// (address nibbles are latched on Φ1 falling edge, not rising)
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
// Update to process Φ1 rising edge
rom.update();
// Should still be in AddressPhase (latching happens on falling edge)
assert_eq!(
rom.get_timing_state(),
TimingState::AddressPhase,
"Should still be in AddressPhase after Φ1 rising - latching happens on falling edge"
);
println!(
"State after Φ1 rising (before falling): {:?}",
rom.get_timing_state()
);
// Reset Φ1 to Low to trigger falling edge - this is when low nibble is latched
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to process Φ1 falling edge - low nibble is latched here
rom.update();
// Should have latched low nibble and transitioned to WaitLatency or DriveData
// With 1ns access time, it might transition directly to DriveData
let state_after_low_nibble = rom.get_timing_state();
assert!(
state_after_low_nibble == TimingState::WaitLatency
|| state_after_low_nibble == TimingState::DriveData,
"Expected WaitLatency or DriveData after latching both nibbles, got {:?}",
state_after_low_nibble
);
println!(
"State after Φ1 falling (low nibble latched): {:?}",
state_after_low_nibble
);
// Remove test drivers from data pins to allow ROM to drive bus
{
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
d0_guard.remove_driver("TEST");
d1_guard.remove_driver("TEST");
d2_guard.remove_driver("TEST");
d3_guard.remove_driver("TEST");
}
// If still in WaitLatency, trigger Φ2 rising edge to check latency and drive data
if rom.get_timing_state() == TimingState::WaitLatency {
// Trigger Φ2 rising edge
{
let mut phi2_guard = phi2_pin.lock();
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
// Update to process Φ2 rising edge
rom.update();
// Should have transitioned to DriveData
assert_eq!(rom.get_timing_state(), TimingState::DriveData);
println!("State after Φ2 rising: {:?}", rom.get_timing_state());
}
// Wait for pins to settle after ROM drives data
thread::sleep(Duration::from_millis(1));
// Verify data is being driven on the bus
// Data 0xAB has low nibble 0xB = 1011 (D0=1, D1=1, D2=0, D3=1)
// Note: Due to bit ordering, D0 should be High, D1 should be High, D2 should be Low, D3 should be High
assert_eq!(d0_pin.lock().read(), PinValue::High);
assert_eq!(d1_pin.lock().read(), PinValue::High);
assert_eq!(d2_pin.lock().read(), PinValue::Low);
assert_eq!(d3_pin.lock().read(), PinValue::High);
// Reset Φ2 to Low
{
let mut phi2_guard = phi2_pin.lock();
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to process Φ2 falling edge
rom.update();
// Should have returned to Idle
assert_eq!(rom.get_timing_state(), TimingState::Idle);
println!("Final state: {:?}", rom.get_timing_state());
// Verify data bus is tri-stated
assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
}
/// Test to verify that unified state machine doesn't progress without proper control signals
#[test]
fn test_intel_4001_state_machine_no_progression_without_signals() {
let mut rom = Intel4001::new_with_access_time("NoProgressionTestROM".to_string(), 1);
// Get pin references for simulation
let phi1_pin = rom.get_pin("PHI1").unwrap();
let phi2_pin = rom.get_pin("PHI2").unwrap();
// Start component
rom.base.set_running(true);
assert!(rom.is_running());
// Initialize clock pins to Low
{
let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to initialize clock states
rom.update();
// Verify initial state
assert_eq!(rom.get_timing_state(), TimingState::Idle);
// Trigger clock edges without setting up control signals
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
// Update to process Φ1 rising edge
rom.update();
// Should still be in Idle state since control signals are not set
assert_eq!(rom.get_timing_state(), TimingState::Idle);
// Reset Φ1 to Low
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to process Φ1 falling edge
rom.update();
// Trigger Φ2 rising edge
{
let mut phi2_guard = phi2_pin.lock();
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
// Update to process Φ2 rising edge
rom.update();
// Should still be in Idle state
assert_eq!(rom.get_timing_state(), TimingState::Idle);
}
/// Test to verify that reset properly clears unified state machine
#[test]
fn test_intel_4001_state_machine_reset() {
let mut rom = Intel4001::new_with_access_time("ResetTestROM".to_string(), 1);
// Get pin references for simulation
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 phi1_pin = rom.get_pin("PHI1").unwrap();
let reset_pin = rom.get_pin("RESET").unwrap();
// Start component
rom.base.set_running(true);
assert!(rom.is_running());
// Initialize clock pins to Low
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to initialize clock states
rom.update();
// Verify initial state
assert_eq!(rom.get_timing_state(), TimingState::Idle);
// Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
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
}
// Trigger Φ1 rising edge to start memory operation
{
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
// Update to process Φ1 rising edge
rom.update();
// Should have started memory operation and transitioned to AddressPhase
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
// Assert RESET (active LOW - set to Low to trigger reset)
{
let mut reset_guard = reset_pin.lock();
reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Update to process reset
rom.update();
// Should have returned to Idle state
assert_eq!(rom.get_timing_state(), TimingState::Idle);
}