Unified the 400x state machine for address latching
Fixed timing issues
This commit is contained in:
parent
e17d6ad177
commit
633463e370
26 changed files with 11904 additions and 2471 deletions
356
tests/intel_4001_state_machine_test.rs
Normal file
356
tests/intel_4001_state_machine_test.rs
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
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().unwrap();
|
||||
let mut phi2_guard = phi2_pin.lock().unwrap();
|
||||
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
|
||||
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
|
||||
}
|
||||
|
||||
// Initialize control pins
|
||||
{
|
||||
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 reset_guard = reset_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);
|
||||
reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
|
||||
}
|
||||
|
||||
// Initialize data pins to Low
|
||||
{
|
||||
let mut d0_guard = d0_pin.lock().unwrap();
|
||||
let mut d1_guard = d1_pin.lock().unwrap();
|
||||
let mut d2_guard = d2_pin.lock().unwrap();
|
||||
let mut d3_guard = d3_pin.lock().unwrap();
|
||||
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().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
|
||||
}
|
||||
|
||||
// Set up address high nibble (0x1 for address 0x12)
|
||||
{
|
||||
let mut d0_guard = d0_pin.lock().unwrap();
|
||||
let mut d1_guard = d1_pin.lock().unwrap();
|
||||
let mut d2_guard = d2_pin.lock().unwrap();
|
||||
let mut d3_guard = d3_pin.lock().unwrap();
|
||||
// 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().unwrap();
|
||||
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().unwrap();
|
||||
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().unwrap();
|
||||
let mut d1_guard = d1_pin.lock().unwrap();
|
||||
let mut d2_guard = d2_pin.lock().unwrap();
|
||||
let mut d3_guard = d3_pin.lock().unwrap();
|
||||
// 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 to latch low nibble
|
||||
{
|
||||
let mut phi1_guard = phi1_pin.lock().unwrap();
|
||||
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
|
||||
}
|
||||
|
||||
// Update to process Φ1 rising edge
|
||||
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
|
||||
);
|
||||
println!(
|
||||
"State after Φ1 rising (low nibble): {:?}",
|
||||
state_after_low_nibble
|
||||
);
|
||||
|
||||
// Reset Φ1 to Low
|
||||
{
|
||||
let mut phi1_guard = phi1_pin.lock().unwrap();
|
||||
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
|
||||
}
|
||||
|
||||
// Update to process Φ1 falling edge
|
||||
rom.update();
|
||||
|
||||
// Remove test drivers from data pins to allow ROM to drive bus
|
||||
{
|
||||
let mut d0_guard = d0_pin.lock().unwrap();
|
||||
let mut d1_guard = d1_pin.lock().unwrap();
|
||||
let mut d2_guard = d2_pin.lock().unwrap();
|
||||
let mut d3_guard = d3_pin.lock().unwrap();
|
||||
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().unwrap();
|
||||
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().unwrap().read(), PinValue::High);
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High);
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low);
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::High);
|
||||
|
||||
// Reset Φ2 to Low
|
||||
{
|
||||
let mut phi2_guard = phi2_pin.lock().unwrap();
|
||||
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().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 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().unwrap();
|
||||
let mut phi2_guard = phi2_pin.lock().unwrap();
|
||||
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().unwrap();
|
||||
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().unwrap();
|
||||
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().unwrap();
|
||||
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().unwrap();
|
||||
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().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
|
||||
}
|
||||
|
||||
// Trigger Φ1 rising edge to start memory operation
|
||||
{
|
||||
let mut phi1_guard = phi1_pin.lock().unwrap();
|
||||
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
|
||||
{
|
||||
let mut reset_guard = reset_pin.lock().unwrap();
|
||||
reset_guard.set_driver(Some("TEST".to_string()), PinValue::High);
|
||||
}
|
||||
|
||||
// Update to process reset
|
||||
rom.update();
|
||||
|
||||
// Should have returned to Idle state
|
||||
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue