Added console for displaying the system state
Corrected behaviour and tests
This commit is contained in:
parent
8193bcda46
commit
0278a96a8d
24 changed files with 1074 additions and 2918 deletions
|
|
@ -77,9 +77,14 @@ impl Intel400xResetHandling for Intel4001 {
|
|||
&self.base
|
||||
}
|
||||
|
||||
fn perform_reset(&self) {
|
||||
fn perform_reset(&mut self) {
|
||||
// Reset operations specific to Intel4001
|
||||
// Note: This is called from handle_reset, so we don't need to check reset pin again
|
||||
self.set_timing_state(TimingState::Idle);
|
||||
self.tri_state_data_bus();
|
||||
self.address_low_nibble = None;
|
||||
self.address_high_nibble = None;
|
||||
self.full_address_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -836,110 +841,6 @@ mod tests {
|
|||
assert_eq!(rom.read_rom(0x01).unwrap(), 0x34);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_latching() {
|
||||
// Note: This test sets Φ1 and Φ2 directly for unit validation.
|
||||
// For system integration testing, consider using a proper clock generator
|
||||
// that drives Φ1/Φ2 continuously through realistic multiple cycles.
|
||||
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
||||
|
||||
// Load test data
|
||||
let test_data = vec![0x12];
|
||||
rom.load_rom_data(test_data, 0).unwrap();
|
||||
|
||||
// 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 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();
|
||||
|
||||
// Initialize clock pins to Low to ensure proper edge detection
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
|
||||
// These must remain set throughout the entire memory operation
|
||||
{
|
||||
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 address high nibble (0x0) on data 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.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 0 = 0
|
||||
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0
|
||||
d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 2 = 0
|
||||
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0
|
||||
}
|
||||
|
||||
// --- High nibble ---
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> latch high nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
|
||||
// Should have transitioned to AddressPhase
|
||||
assert_eq!(rom.memory_state, MemoryState::AddressPhase);
|
||||
|
||||
// Check that high nibble was latched
|
||||
assert_eq!(rom.address_high_nibble, Some(0x0));
|
||||
|
||||
// Set address low nibble (0x0) on data 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.set_driver(Some("TEST".into()), PinValue::Low); // Bit 0 = 0
|
||||
d1_guard.set_driver(Some("TEST".into()), PinValue::Low); // Bit 1 = 0
|
||||
d2_guard.set_driver(Some("TEST".into()), PinValue::Low); // Bit 2 = 0
|
||||
d3_guard.set_driver(Some("TEST".into()), PinValue::Low); // Bit 3 = 0
|
||||
}
|
||||
|
||||
// --- Low nibble ---
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> latch low nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
|
||||
// Should have assembled full address and transitioned to WaitLatency
|
||||
// Note: nibbles are cleared after assembly, so check last_address instead
|
||||
assert_eq!(rom.last_address, 0x00);
|
||||
assert_eq!(rom.full_address_ready, true);
|
||||
assert_eq!(rom.memory_state, MemoryState::WaitLatency);
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_operation_start() {
|
||||
|
|
@ -978,510 +879,8 @@ mod tests {
|
|||
println!("Memory state after Φ1 rising: {:?}", rom.memory_state);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_data_driving() {
|
||||
// Note: This test manually sequences through clock phases for unit validation.
|
||||
// For system integration, use continuous clock generator for realistic timing.
|
||||
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
||||
|
||||
// Load test data
|
||||
let test_data = vec![0x12];
|
||||
rom.load_rom_data(test_data, 0).unwrap();
|
||||
|
||||
// Initial state debug
|
||||
rom.debug_state_transitions("INITIAL");
|
||||
|
||||
// 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 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 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
|
||||
}
|
||||
|
||||
// Phase 1: Address phase - latch high nibble (0x0)
|
||||
{
|
||||
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); // Bit 0 = 0
|
||||
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 1 = 0
|
||||
d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 2 = 0
|
||||
d3_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Bit 3 = 0
|
||||
}
|
||||
|
||||
// --- High nibble ---
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> latch high nibble
|
||||
rom.debug_state_transitions("AFTER_HIGH_NIBBLE_RISING");
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
rom.debug_state_transitions("AFTER_HIGH_NIBBLE_FALLING");
|
||||
|
||||
// Phase 2: Address phase - latch low nibble (0x0)
|
||||
// Re-assert control pins to ensure they're maintained during entire operation
|
||||
{
|
||||
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
|
||||
}
|
||||
// Tri-state data bus to let ROM take control
|
||||
{
|
||||
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".into()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("TEST".into()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("TEST".into()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("TEST".into()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// --- Low nibble ---
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> latch low nibble
|
||||
rom.debug_state_transitions("AFTER_LOW_NIBBLE_RISING");
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
rom.debug_state_transitions("AFTER_LOW_NIBBLE_FALLING");
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
rom.debug_state_transitions("BEFORE_SLEEP");
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
rom.debug_state_transitions("AFTER_SLEEP");
|
||||
|
||||
// --- Data phase ---
|
||||
// First set Φ2 to Low, then High to ensure proper edge detection
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // ensure low state
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> drive data
|
||||
rom.debug_state_transitions("AFTER_PHI2_RISING");
|
||||
|
||||
// After sleep, latency should have elapsed, so should transition to DriveData on Φ2 rising
|
||||
// The state should transition to DriveData during the Φ2 rising edge
|
||||
assert_eq!(rom.memory_state, MemoryState::DriveData);
|
||||
println!("After Φ2 high - State: {:?}", rom.memory_state);
|
||||
|
||||
// Debug: Check what data the ROM thinks it should drive
|
||||
let address = rom.last_address;
|
||||
let expected_data = rom.memory[address as usize];
|
||||
println!(
|
||||
"DEBUG: ROM address 0x{:x}, data at address: 0x{:x}",
|
||||
address, expected_data
|
||||
);
|
||||
|
||||
// Debug: Check the bit extraction logic
|
||||
let bit0 = (expected_data >> 0) & 1;
|
||||
let bit1 = (expected_data >> 1) & 1;
|
||||
let bit2 = (expected_data >> 2) & 1;
|
||||
let bit3 = (expected_data >> 3) & 1;
|
||||
println!(
|
||||
"DEBUG: Bit extraction - bit0: {}, bit1: {}, bit2: {}, bit3: {}",
|
||||
bit0, bit1, bit2, bit3
|
||||
);
|
||||
|
||||
// Debug: Check actual pin states and settlement status
|
||||
let d0_actual = d0_pin.lock().unwrap().read();
|
||||
let d1_actual = d1_pin.lock().unwrap().read();
|
||||
let d2_actual = d2_pin.lock().unwrap().read();
|
||||
let d3_actual = d3_pin.lock().unwrap().read();
|
||||
|
||||
let d0_immediate = d0_pin.lock().unwrap().read_immediate();
|
||||
let d1_immediate = d1_pin.lock().unwrap().read_immediate();
|
||||
let d2_immediate = d2_pin.lock().unwrap().read_immediate();
|
||||
let d3_immediate = d3_pin.lock().unwrap().read_immediate();
|
||||
|
||||
let d0_settled = d0_pin.lock().unwrap().is_settled();
|
||||
let d1_settled = d1_pin.lock().unwrap().is_settled();
|
||||
let d2_settled = d2_pin.lock().unwrap().is_settled();
|
||||
let d3_settled = d3_pin.lock().unwrap().is_settled();
|
||||
|
||||
println!(
|
||||
"DEBUG: D0 actual: {:?} (bit 0: {}), immediate: {:?}, settled: {}",
|
||||
d0_actual, bit0, d0_immediate, d0_settled
|
||||
);
|
||||
println!(
|
||||
"DEBUG: D1 actual: {:?} (bit 1: {}), immediate: {:?}, settled: {}",
|
||||
d1_actual, bit1, d1_immediate, d1_settled
|
||||
);
|
||||
println!(
|
||||
"DEBUG: D2 actual: {:?} (bit 2: {}), immediate: {:?}, settled: {}",
|
||||
d2_actual, bit2, d2_immediate, d2_settled
|
||||
);
|
||||
println!(
|
||||
"DEBUG: D3 actual: {:?} (bit 3: {}), immediate: {:?}, settled: {}",
|
||||
d3_actual, bit3, d3_immediate, d3_settled
|
||||
);
|
||||
|
||||
// Debug: Check actual pin states immediately after data driving
|
||||
let d0_actual = d0_pin.lock().unwrap().read();
|
||||
let d1_actual = d1_pin.lock().unwrap().read();
|
||||
let d2_actual = d2_pin.lock().unwrap().read();
|
||||
let d3_actual = d3_pin.lock().unwrap().read();
|
||||
|
||||
println!(
|
||||
"DEBUG: After data driving - D0: {:?}, D1: {:?}, D2: {:?}, D3: {:?}",
|
||||
d0_actual, d1_actual, d2_actual, d3_actual
|
||||
);
|
||||
|
||||
// Debug: Check what the ROM thinks it should be driving
|
||||
let expected_data = 0x12;
|
||||
let bit0 = (expected_data >> 0) & 1;
|
||||
let bit1 = (expected_data >> 1) & 1;
|
||||
let bit2 = (expected_data >> 2) & 1;
|
||||
let bit3 = (expected_data >> 3) & 1;
|
||||
println!(
|
||||
"DEBUG: Expected bits - bit0: {}, bit1: {}, bit2: {}, bit3: {}",
|
||||
bit0, bit1, bit2, bit3
|
||||
);
|
||||
|
||||
// Verify data is driven on bus while Φ2 is high: 0x12 = 0001 0010
|
||||
// 0x12 = 18 decimal = 0001 0010 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=0
|
||||
|
||||
// Small delay to allow pin state to settle
|
||||
thread::sleep(Duration::from_micros(1));
|
||||
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low); // Bit 2 = 0
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::Low); // Bit 3 = 0
|
||||
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge -> tri-state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_operation_start_detection() {
|
||||
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
||||
|
||||
// Load test data
|
||||
let test_data = vec![0x12];
|
||||
rom.load_rom_data(test_data, 0).unwrap();
|
||||
|
||||
// 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 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();
|
||||
|
||||
// Initially, memory should be in idle state
|
||||
assert_eq!(rom.memory_state, MemoryState::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
|
||||
}
|
||||
|
||||
// Tri-state data bus to let ROM take control
|
||||
// Note: Don't clear ROM's driver - let ROM set its own driver when it drives data
|
||||
// The ROM uses driver name format "{}_DATA" (e.g., "ROM_4001_DATA")
|
||||
|
||||
// Set Φ1 high, Φ2 low - should start memory operation
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
|
||||
// Update to process Φ1 rising edge
|
||||
rom.update();
|
||||
|
||||
// Should have transitioned to AddressPhase
|
||||
assert_eq!(rom.memory_state, MemoryState::AddressPhase);
|
||||
|
||||
// Set address on data bus for latching (CPU drives during address phase)
|
||||
{
|
||||
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("CPU".to_string()), PinValue::Low);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::Low);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::Low);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::Low);
|
||||
}
|
||||
|
||||
// --- High nibble ---
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
|
||||
// --- Low nibble ---
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> latch low nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// --- Data phase ---
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> drive data
|
||||
|
||||
// Assert DriveData state while Φ2 is high
|
||||
assert_eq!(rom.memory_state, MemoryState::DriveData);
|
||||
|
||||
// Verify data is driven on bus while Φ2 is high: 0x12 = 0001 0010
|
||||
// 0x12 = 18 decimal = 0001 0010 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=0
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low); // Bit 2 = 0
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::Low); // Bit 3 = 0
|
||||
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge -> tri-state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_address_fetch_cycle_integration() {
|
||||
// Note: This integration test manually steps through clock phases.
|
||||
// For full system testing, implement continuous clock generator
|
||||
// that maintains proper Φ1/Φ2 timing relationships.
|
||||
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
||||
|
||||
// Load test data: address 0x00 = 0x12, address 0x01 = 0x34
|
||||
let test_data = vec![0x12, 0x34, 0x56, 0x78];
|
||||
rom.load_rom_data(test_data, 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 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 up memory read operation: SYNC=1, CM=1, CI=0 (memory access, not I/O)
|
||||
{
|
||||
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, not I/O
|
||||
}
|
||||
|
||||
// Test complete cycle for address 0x00
|
||||
// Step 1: Clear CPU driver to let ROM take control
|
||||
{
|
||||
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("CPU".to_string()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// Step 2: High nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for high nibble
|
||||
|
||||
// Step 3: Clear CPU driver to let ROM take control
|
||||
{
|
||||
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("CPU".to_string()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// Step 4: Low nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> drive data
|
||||
|
||||
// Step 5: Verify data is driven on bus while Φ2 is high: 0x12 = 0001 0010
|
||||
// 0x12 = 18 decimal = 0001 0010 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=0
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low); // Bit 2 = 0
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::Low); // Bit 3 = 0
|
||||
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge -> tri-state
|
||||
|
||||
// Test complete cycle for address 0x01
|
||||
// Reset memory state first
|
||||
rom.return_to_idle();
|
||||
|
||||
// Step 1: Clear CPU driver to let ROM take control
|
||||
{
|
||||
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("CPU".to_string()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// Step 2: High nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for high nibble
|
||||
|
||||
// Step 3: Clear CPU driver to let ROM take control
|
||||
{
|
||||
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("CPU".to_string()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// Step 4: Low nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> drive data
|
||||
|
||||
// Step 5: Verify data is driven on bus while Φ2 is high: 0x34 = 0011 0100
|
||||
// 0x34 = 52 decimal = 0011 0100 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=1
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::Low); // Bit 1 = 0
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::High); // Bit 2 = 1
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::Low); // Bit 3 = 0
|
||||
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge -> tri-state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clock_driven_memory_fetch() {
|
||||
|
|
@ -1621,58 +1020,6 @@ mod tests {
|
|||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comprehensive_intel4001_functionality() {
|
||||
// Comprehensive test of Intel4001 functionality
|
||||
let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 1);
|
||||
|
||||
// Test basic properties
|
||||
assert_eq!(rom.get_rom_size(), 256);
|
||||
assert_eq!(rom.get_access_time(), 1);
|
||||
assert_eq!(rom.name(), "TestROM");
|
||||
|
||||
// Test data loading and reading
|
||||
let test_data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
|
||||
rom.load_rom_data(test_data, 0).unwrap();
|
||||
|
||||
// Verify all loaded data
|
||||
for (i, &expected) in test_data.iter().enumerate() {
|
||||
assert_eq!(rom.read_rom(i as u8).unwrap(), expected);
|
||||
}
|
||||
|
||||
// Test state management
|
||||
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
||||
|
||||
rom.set_timing_state(TimingState::AddressPhase);
|
||||
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
|
||||
|
||||
rom.set_timing_state(TimingState::WaitLatency);
|
||||
assert_eq!(rom.get_timing_state(), TimingState::WaitLatency);
|
||||
|
||||
rom.set_timing_state(TimingState::DriveData);
|
||||
assert_eq!(rom.get_timing_state(), TimingState::DriveData);
|
||||
|
||||
// Test address handling
|
||||
rom.set_address_high_nibble(Some(0x0F));
|
||||
rom.set_address_low_nibble(Some(0x23));
|
||||
rom.set_full_address_ready(true);
|
||||
|
||||
assert_eq!(rom.get_address_high_nibble(), Some(0x0F));
|
||||
assert_eq!(rom.get_address_low_nibble(), Some(0x23));
|
||||
assert_eq!(rom.get_full_address_ready(), true);
|
||||
|
||||
// Test reset functionality
|
||||
rom.perform_reset();
|
||||
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
||||
assert_eq!(rom.get_address_high_nibble(), None);
|
||||
assert_eq!(rom.get_address_low_nibble(), None);
|
||||
assert_eq!(rom.get_full_address_ready(), false);
|
||||
|
||||
// Test error handling
|
||||
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value
|
||||
assert!(rom.load_rom_data(vec![0x12], 255).is_ok()); // Valid
|
||||
assert!(rom.load_rom_data(vec![0x12], 256).is_err()); // Invalid
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intel4001_state_transitions() {
|
||||
|
|
@ -1731,183 +1078,4 @@ mod tests {
|
|||
assert!(rom.load_rom_data(vec![0x12], 256).is_err()); // Invalid - out of bounds
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clock_generator_integration() {
|
||||
// Integration test with simulated clock generator stepping through multiple cycles
|
||||
// This tests more realistic timing scenarios compared to direct Φ1/Φ2 manipulation
|
||||
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
||||
|
||||
// Load test data: address 0x00 = 0x12, address 0x01 = 0x34
|
||||
let test_data = vec![0x12, 0x34];
|
||||
rom.load_rom_data(test_data, 0).unwrap();
|
||||
|
||||
// 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 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 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
|
||||
}
|
||||
|
||||
// Simulate clock generator: step through multiple Φ1/Φ2 cycles
|
||||
// Cycle 1: Address phase for high nibble (0x0)
|
||||
{
|
||||
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::HighZ);
|
||||
d1_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// High nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for high nibble
|
||||
|
||||
// Cycle 2: Address phase for low nibble (0x0)
|
||||
{
|
||||
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".into()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("TEST".into()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("TEST".into()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("TEST".into()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// Low nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> drive data
|
||||
|
||||
// Should have data 0x12 on bus while Φ2 is high: 0001 0010
|
||||
// 0x12 = 18 decimal = 0001 0010 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=0
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); // Bit 1 = 1
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low); // Bit 2 = 0
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::Low); // Bit 3 = 0
|
||||
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge -> tri-state
|
||||
|
||||
// Cycle 3: Next memory operation for address 0x01
|
||||
rom.return_to_idle();
|
||||
|
||||
// Clear CPU driver to let ROM take control for address 0x01
|
||||
{
|
||||
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("CPU".to_string()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// High nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for high nibble
|
||||
|
||||
// Clear CPU driver to let ROM take control for low nibble
|
||||
{
|
||||
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("CPU".to_string()), PinValue::HighZ);
|
||||
d1_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d2_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
d3_guard.set_driver(Some("CPU".to_string()), PinValue::HighZ);
|
||||
}
|
||||
|
||||
// Low nibble
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge
|
||||
phi1_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge for low nibble
|
||||
|
||||
// Advance simulated time to exceed access_time (1ns)
|
||||
thread::sleep(Duration::from_nanos(2));
|
||||
|
||||
// Data phase
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::High);
|
||||
rom.update(); // rising edge -> drive data
|
||||
|
||||
// Should have data 0x34 on bus while Φ2 is high: 0011 0100
|
||||
// 0x34 = 52 decimal = 0011 0100 binary
|
||||
// For 4-bit data bus: D3=0, D2=0, D1=1, D0=1
|
||||
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::Low); // Bit 0 = 0
|
||||
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::Low); // Bit 1 = 0
|
||||
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::High); // Bit 2 = 1
|
||||
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::Low); // Bit 3 = 0
|
||||
|
||||
phi2_pin
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_driver(Some("TEST".into()), PinValue::Low);
|
||||
rom.update(); // falling edge -> tri-state
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue