| AUTO_BUILD_ARGS = [] | |
| #!/usr/bin/env python3 | |
| """ | |
| Cocotb testbench for Apollo SPI controller module. | |
| Tests basic reset, frequency change, and PTT functionality. | |
| """ | |
| import cocotb | |
| from cocotb.clock import Clock | |
| from cocotb.triggers import RisingEdge, FallingEdge, Timer, ClockCycles | |
| from pathlib import Path | |
| import sys | |
| import os | |
| # Add cocotb to path if needed | |
| sys.path.append(str(Path(__file__).parent)) | |
| async def test_reset_sequence(dut): | |
| """Test that reset properly initializes the module.""" | |
| clock = dut.clock | |
| reset = dut.reset | |
| # Start clock | |
| cocotb.start_soon(Clock(clock, 33, unit="ns").start()) | |
| # Apply reset | |
| reset.value = 1 | |
| await ClockCycles(clock, 2) | |
| # Check reset state | |
| assert dut.ApolloReset.value == 1, "ApolloReset should be high during reset" | |
| assert dut.ApolloEnable.value == 1, "ApolloEnable should be high during reset" | |
| assert dut.SPI_SCK.value == 1, "SPI_SCK should be high during reset" | |
| # Release reset | |
| reset.value = 0 | |
| await ClockCycles(clock, 200) # Wait for reset sequence | |
| # Check post-reset state | |
| assert dut.ApolloReset.value == 1, "ApolloReset should be high after reset" | |
| assert dut.state.value == 3, f"State should be WAIT(3) after reset, got {dut.state.value}" | |
| # Check SPI clock is idle | |
| await ClockCycles(clock, 10) | |
| assert dut.SPI_SCK.value == 0, "SPI_SCK should be idle (0) in WAIT state" | |
| async def test_frequency_change(dut): | |
| """Test that frequency changes trigger SPI transmission.""" | |
| clock = dut.clock | |
| reset = dut.reset | |
| # Start clock | |
| cocotb.start_soon(Clock(clock, 33, unit="ns").start()) | |
| # Apply and release reset | |
| reset.value = 1 | |
| await ClockCycles(clock, 2) | |
| reset.value = 0 | |
| await ClockCycles(clock, 200) | |
| # Initial state check | |
| assert dut.state.value == 3, f"Should be in WAIT state, got {dut.state.value}" | |
| # Change frequency | |
| dut.frequency.value = 0x12345678 | |
| await ClockCycles(clock, 5) | |
| # Check that state transition occurs | |
| assert dut.state.value == 6, f"Should transition to SET_FREQUENCY(6), got {dut.state.value}" | |
| assert dut.ApolloEnable.value == 0, "ApolloEnable should be low during transmission" | |
| # Wait for message transmission | |
| await ClockCycles(clock, 50) | |
| # Check that SPI clock is toggling | |
| sck_initial = dut.SPI_SCK.value | |
| await ClockCycles(clock, 2) | |
| sck_next = dut.SPI_SCK.value | |
| assert sck_initial != sck_next, "SPI_SCK should be toggling during transmission" | |
| async def test_ptt_control(dut): | |
| """Test PTT enable/disable functionality.""" | |
| clock = dut.clock | |
| reset = dut.reset | |
| # Start clock | |
| cocotb.start_soon(Clock(clock, 33, unit="ns").start()) | |
| # Apply and release reset | |
| reset.value = 1 | |
| await ClockCycles(clock, 2) | |
| reset.value = 0 | |
| await ClockCycles(clock, 200) | |
| # Enable PTT | |
| dut.PTT.value = 1 | |
| await ClockCycles(clock, 10) | |
| # Check state transition | |
| assert dut.state.value == 12, f"Should transition to ENABLE_PTT(12), got {dut.state.value}" | |
| assert dut.ApolloEnable.value == 0, "ApolloEnable should be low during PTT enable" | |
| # Wait for transmission to complete | |
| await ClockCycles(clock, 100) | |
| # Disable PTT | |
| dut.PTT.value = 0 | |
| await ClockCycles(clock, 10) | |
| # Check state transition for disable | |
| assert dut.state.value == 4, f"Should transition to SIMPLE_MESSAGE(4) for PTT disable, got {dut.state.value}" | |
| async def test_filter_select_alex_mode(dut): | |
| """Test that FilterSelect high puts module in Alex mode.""" | |
| clock = dut.clock | |
| reset = dut.reset | |
| # Start clock | |
| cocotb.start_soon(Clock(clock, 33, unit="ns").start()) | |
| # Apply and release reset | |
| reset.value = 1 | |
| await ClockCycles(clock, 2) | |
| reset.value = 0 | |
| await ClockCycles(clock, 200) | |
| # Set FilterSelect high (Alex mode) | |
| dut.FilterSelect.value = 1 | |
| await ClockCycles(clock, 10) | |
| # Check state transition to Alex mode | |
| assert dut.state.value == 31, f"Should be in Alex(31) mode, got {dut.state.value}" | |
| # Try to change frequency - should not trigger transmission in Alex mode | |
| original_state = dut.state.value | |
| dut.frequency.value = 0x87654321 | |
| await ClockCycles(clock, 10) | |
| assert dut.state.value == original_state, "State should not change in Alex mode" | |
| async def test_status_available_signal(dut): | |
| """Test that statusAvailable signal behaves correctly.""" | |
| clock = dut.clock | |
| reset = dut.reset | |
| # Start clock | |
| cocotb.start_soon(Clock(clock, 33, unit="ns").start()) | |
| # Apply and release reset | |
| reset.value = 1 | |
| await ClockCycles(clock, 2) | |
| reset.value = 0 | |
| await ClockCycles(clock, 200) | |
| # Initially statusAvailable should be 0 | |
| assert dut.statusAvailable.value == 0, "statusAvailable should be 0 initially" | |
| # Check that we can write to status register (simulate status reception) | |
| # Note: In real operation, this would be set by the READ_STATUS state | |
| # We'll just verify the signal exists and can be monitored | |
| if hasattr(dut, 'status'): | |
| # status is 88 bits wide | |
| assert dut.status.value == 0, "status should be 0 initially" | |
| if __name__ == "__main__": | |
| from pathlib import Path | |
| from cocotb_tools.runner import get_runner | |
| project_root = Path(__file__).resolve().parents[1] | |
| sources = [ | |
| project_root / "reference" / "top.v" | |
| ] | |
| runner = get_runner("icarus") | |
| runner.build( | |
| sources=[str(path) for path in sources], | |
| hdl_toplevel='Apollo', | |
| build_args=AUTO_BUILD_ARGS, | |
| always=True, | |
| ) | |
| runner.test( | |
| hdl_toplevel='Apollo', | |
| test_module="tb", | |
| waves=False, | |
| ) | |
Xet Storage Details
- Size:
- 5.86 kB
- Xet hash:
- f44ed572655182333788c849eac1690977c439f93f3417e45424dd89817df897
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.