# SPDX-License-Identifier: AGPL-3.0-Only # Copyright (C) 2022 Sean Anderson import cocotb from cocotb.clock import Clock from cocotb.triggers import FallingEdge, Timer from cocotb.types import LogicArray def BIT(n): return 1 << n BMCR = 0 BMSR = 1 PHYID1 = 2 PHYID2 = 3 EXTSTATUS = 15 NWCR = 16 PWCR = 17 DCR = 18 FCCR = 19 SECR = 21 VCR = 30 BMCR_RESET = BIT(15) BMCR_LOOPBACK = BIT(14) BMCR_SPEED_LSB = BIT(13) BMCR_PDOWN = BIT(11) BMCR_ISOLATE = BIT(10) BMCR_DUPLEX = BIT(8) BMCR_COLTEST = BIT(7) BMCR_SPEED_MSB = BIT(6) BMSR_100BASEXFD = BIT(14) BMSR_100BASEXHD = BIT(13) BMSR_LSTATUS = BIT(2) BMSR_EXTCAP = BIT(0) VCR_DTEST = BIT(15) VCR_LTEST = BIT(14) @cocotb.test(timeout_time=1, timeout_unit='us') async def test_mdio(regs): regs.cyc.value = 1 regs.stb.value = 0 regs.link_status.value = 1 regs.positive_wraparound.value = 0 regs.negative_wraparound.value = 0 regs.false_carrier.value = 0 regs.symbol_error.value = 0 await Timer(1) await cocotb.start(Clock(regs.clk, 8, units='ns').start()) async def xfer(regad, data=None): await FallingEdge(regs.clk) regs.stb.value = 1 regs.addr.value = regad if data is None: regs.we.value = 0 else: regs.we.value = 1 regs.data_write.value = data await FallingEdge(regs.clk) assert regs.ack.value or regs.err.value regs.stb.value = 0 regs.we.value = LogicArray('X') regs.addr.value = LogicArray('X' * 4) regs.data_write.value = LogicArray('X' * 16) if data is None and regs.ack.value: return regs.data_read.value async def reg_toggle(reg, bit, signal, ro_mask=0): if signal: assert not signal.value await xfer(reg, bit) if signal: assert signal.value assert await xfer(reg) == (ro_mask | bit) await xfer(reg, 0) if signal: assert not signal.value def bmcr_toggle(bit, signal): return reg_toggle(BMCR, bit, signal, ro_mask=BMCR_SPEED_LSB) assert await xfer(BMCR) == (BMCR_SPEED_LSB | BMCR_ISOLATE) await bmcr_toggle(BMCR_LOOPBACK, regs.loopback) await bmcr_toggle(BMCR_PDOWN, regs.pdown) await bmcr_toggle(BMCR_ISOLATE, regs.isolate) await bmcr_toggle(BMCR_DUPLEX, None) await bmcr_toggle(BMCR_COLTEST, regs.coltest) await xfer(BMCR, BMCR_RESET) assert await xfer(BMCR) == (BMCR_SPEED_LSB | BMCR_ISOLATE) await xfer(BMSR, 0xffff) assert await xfer(BMSR) == (BMSR_100BASEXFD | BMSR_100BASEXHD | BMSR_LSTATUS | BMSR_EXTCAP) regs.link_status.value = 0 assert not await xfer(BMSR) & BMSR_LSTATUS regs.link_status.value = 1 assert not await xfer(BMSR) & BMSR_LSTATUS assert await xfer(BMSR) & BMSR_LSTATUS await xfer(PHYID1, 0xffff) assert await xfer(PHYID1) == 0 await xfer(PHYID2, 0xffff) assert await xfer(PHYID2) == 0 # I'm pretty sure this register will never be implemented assert await xfer(EXTSTATUS) is None assert await xfer(EXTSTATUS, 0) is None async def counter_test(reg, signal, edge_triggered=False, active_high=True): signal.value = 1 if active_high else 0 assert await xfer(reg) == 1 await xfer(reg, 0xfffe) if edge_triggered: signal.value = 0 if active_high else 1 await FallingEdge(regs.clk) if edge_triggered: signal.value = 1 if active_high else 0 await FallingEdge(regs.clk) signal.value = 0 if active_high else 1 assert await xfer(reg) == 0x7fff assert await xfer(reg) == 0 await counter_test(NWCR, regs.negative_wraparound) await counter_test(PWCR, regs.positive_wraparound) await xfer(DCR) # Clear DCR from the BMSR testing await counter_test(DCR, regs.link_status, True, False) await counter_test(FCCR, regs.false_carrier) await counter_test(SECR, regs.symbol_error) await reg_toggle(VCR, VCR_DTEST, regs.descrambler_test) await reg_toggle(VCR, VCR_LTEST, regs.link_monitor_test)