| AUTO_BUILD_ARGS = [] | |
| #!/usr/bin/env python3 | |
| """ | |
| cocotb testbench for ADDC module. | |
| Tests the adder with carry functionality including sign conversion and zero detection. | |
| """ | |
| import cocotb | |
| from cocotb.clock import Clock | |
| from cocotb.triggers import RisingEdge, FallingEdge, Timer | |
| from pathlib import Path | |
| import sys | |
| import os | |
| # Add parent directory to path for runner import | |
| sys.path.insert(0, str(Path(__file__).resolve().parent)) | |
| from cocotb_tools.runner import get_runner | |
| async def test_reset_behavior(dut): | |
| """Test that outputs are stable after reset.""" | |
| clock = Clock(dut.clk, 10, unit="ns") | |
| cocotb.start_soon(clock.start()) | |
| # Apply reset | |
| dut.reset.value = 1 | |
| dut.scan_enable.value = 0 | |
| dut.test_mode.value = 0 | |
| # Give some inputs | |
| dut.DQ.value = 0 | |
| dut.SEZ.value = 0 | |
| await RisingEdge(dut.clk) | |
| await RisingEdge(dut.clk) | |
| # Release reset | |
| dut.reset.value = 0 | |
| await RisingEdge(dut.clk) | |
| # Check outputs are defined (not X or Z) | |
| assert dut.PK0.value.is_resolvable, "PK0 should be resolvable after reset" | |
| assert dut.SIGPK.value.is_resolvable, "SIGPK should be resolvable after reset" | |
| async def test_positive_addition(dut): | |
| """Test addition of two positive numbers.""" | |
| clock = Clock(dut.clk, 10, unit="ns") | |
| cocotb.start_soon(clock.start()) | |
| # Initialize | |
| dut.reset.value = 0 | |
| dut.scan_enable.value = 0 | |
| dut.test_mode.value = 0 | |
| await RisingEdge(dut.clk) | |
| # Test case 1: DQ = 100 (positive), SEZ = 50 (positive) | |
| # DQ[15] = 0, SEZ[14] = 0 | |
| dut.DQ.value = 100 # 0x0064 | |
| dut.SEZ.value = 50 # 0x0032 | |
| await RisingEdge(dut.clk) | |
| # DQI = DQ = 100, SEZI = SEZ = 50 | |
| # DQSEZ = 100 + 50 = 150 | |
| # PK0 = DQSEZ[15] = 0 (positive) | |
| # SIGPK = (DQSEZ == 0) = 0 (not zero) | |
| assert dut.PK0.value == 0, f"PK0 should be 0 for positive sum, got {dut.PK0.value}" | |
| assert dut.SIGPK.value == 0, f"SIGPK should be 0 for non-zero sum, got {dut.SIGPK.value}" | |
| async def test_negative_addition(dut): | |
| """Test addition resulting in negative number.""" | |
| clock = Clock(dut.clk, 10, unit="ns") | |
| cocotb.start_soon(clock.start()) | |
| # Initialize | |
| dut.reset.value = 0 | |
| dut.scan_enable.value = 0 | |
| dut.test_mode.value = 0 | |
| await RisingEdge(dut.clk) | |
| # Test case: DQ = -100, SEZ = 50 | |
| # DQ in sign-magnitude: sign=1, magnitude=100 | |
| # DQ = 0x8000 | 100 = 0x8064 | |
| dut.DQ.value = 0x8064 # Sign bit set, magnitude 100 | |
| dut.SEZ.value = 50 # Positive | |
| await RisingEdge(dut.clk) | |
| # DQS = 1 (negative) | |
| # DQI = 65536 - (DQ & 32767) = 65536 - 100 = 65436 (0xFF9C) | |
| # SEZI = SEZ = 50 | |
| # DQSEZ = 65436 + 50 = 65486 (0xFFCE) = -50 in 16-bit 2's complement | |
| # PK0 = DQSEZ[15] = 1 (negative) | |
| # SIGPK = 0 (not zero) | |
| assert dut.PK0.value == 1, f"PK0 should be 1 for negative sum, got {dut.PK0.value}" | |
| assert dut.SIGPK.value == 0, f"SIGPK should be 0 for non-zero sum, got {dut.SIGPK.value}" | |
| async def test_zero_result(dut): | |
| """Test addition resulting in zero.""" | |
| clock = Clock(dut.clk, 10, unit="ns") | |
| cocotb.start_soon(clock.start()) | |
| # Initialize | |
| dut.reset.value = 0 | |
| dut.scan_enable.value = 0 | |
| dut.test_mode.value = 0 | |
| await RisingEdge(dut.clk) | |
| # Test case: DQ = 100, SEZ = -100 | |
| # DQ = 100 (positive) | |
| # SEZ in 15-bit 2's complement: -100 = 32768 - 100 = 32668 (0x7F9C) | |
| dut.DQ.value = 100 | |
| dut.SEZ.value = 32668 # -100 in 15-bit 2's complement | |
| await RisingEdge(dut.clk) | |
| # DQI = 100 | |
| # SEZS = 1 (negative since SEZ[14] = 1) | |
| # SEZI = 32768 + SEZ = 32768 + 32668 = 65436 (0xFF9C) = -100 in 16-bit 2's complement | |
| # DQSEZ = 100 + (-100) = 0 | |
| # PK0 = 0 (MSB of 0 is 0) | |
| # SIGPK = 1 (zero result) | |
| assert dut.SIGPK.value == 1, f"SIGPK should be 1 for zero sum, got {dut.SIGPK.value}" | |
| # Note: PK0 for zero result is 0 since DQSEZ[15] = 0 | |
| async def test_scan_mode_disabled(dut): | |
| """Test that scan outputs are driven when scan is disabled.""" | |
| clock = Clock(dut.clk, 10, unit="ns") | |
| cocotb.start_soon(clock.start()) | |
| # Initialize with scan disabled | |
| dut.reset.value = 0 | |
| dut.scan_enable.value = 0 | |
| dut.test_mode.value = 0 | |
| # Set some scan inputs | |
| dut.scan_in0.value = 1 | |
| dut.scan_in1.value = 1 | |
| dut.scan_in2.value = 1 | |
| dut.scan_in3.value = 1 | |
| dut.scan_in4.value = 1 | |
| await RisingEdge(dut.clk) | |
| # With scan disabled, scan outputs should still be driven (not high-Z) | |
| # Check they are resolvable (not X or Z) | |
| if hasattr(dut, "scan_out0"): | |
| assert dut.scan_out0.value.is_resolvable, "scan_out0 should be driven" | |
| if hasattr(dut, "scan_out1"): | |
| assert dut.scan_out1.value.is_resolvable, "scan_out1 should be driven" | |
| if hasattr(dut, "scan_out2"): | |
| assert dut.scan_out2.value.is_resolvable, "scan_out2 should be driven" | |
| if hasattr(dut, "scan_out3"): | |
| assert dut.scan_out3.value.is_resolvable, "scan_out3 should be driven" | |
| if hasattr(dut, "scan_out4"): | |
| assert dut.scan_out4.value.is_resolvable, "scan_out4 should be driven" | |
| 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='ADDC', | |
| build_args=AUTO_BUILD_ARGS, | |
| always=True, | |
| ) | |
| runner.test( | |
| hdl_toplevel='ADDC', | |
| test_module="tb", | |
| waves=False, | |
| ) | |
Xet Storage Details
- Size:
- 5.73 kB
- Xet hash:
- 106373ed84b69cb552d3d193f71c4ae67879184c961090d216f72e13513c6088
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.