2022-10-30 20:32:02 -05:00
|
|
|
# SPDX-License-Identifier: AGPL-3.0-Only
|
|
|
|
# Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
|
|
|
|
|
|
|
import itertools
|
|
|
|
|
|
|
|
import cocotb
|
|
|
|
from cocotb.clock import Clock
|
|
|
|
from cocotb.triggers import RisingEdge, FallingEdge, Timer
|
|
|
|
from cocotb.types import LogicArray
|
|
|
|
|
|
|
|
from .pcs import Code, as_nibbles
|
|
|
|
from .util import alist, send_recovered_bits, timeout, with_valids
|
|
|
|
|
|
|
|
def as_codes(nibbles):
|
|
|
|
for nibble in nibbles:
|
|
|
|
if nibble is None:
|
|
|
|
yield Code('H')
|
|
|
|
else:
|
|
|
|
yield Code.encode[nibble]
|
|
|
|
|
|
|
|
def frame(data):
|
|
|
|
return itertools.chain(
|
|
|
|
(Code('J'), Code('K')),
|
|
|
|
# Chop off the SSD
|
2022-11-05 10:57:54 -05:00
|
|
|
as_codes(itertools.islice(data, 2, None)),
|
2022-10-30 20:32:02 -05:00
|
|
|
(Code('T'), Code('R')),
|
|
|
|
)
|
|
|
|
|
2022-11-05 10:57:54 -05:00
|
|
|
async def mii_recv_packet(pcs, signals=None):
|
|
|
|
if signals is None:
|
|
|
|
signals = {
|
|
|
|
'ce': pcs.ce,
|
|
|
|
'err': pcs.err,
|
|
|
|
'data': pcs.data,
|
|
|
|
'valid': pcs.valid,
|
|
|
|
}
|
|
|
|
|
|
|
|
while not (signals['ce'].value and signals['valid'].value):
|
2022-10-30 20:32:02 -05:00
|
|
|
await RisingEdge(pcs.clk)
|
|
|
|
|
2022-11-05 10:57:54 -05:00
|
|
|
while signals['valid'].value:
|
|
|
|
if signals['ce'].value:
|
|
|
|
if signals['err'].value:
|
2022-10-30 20:32:02 -05:00
|
|
|
yield None
|
|
|
|
else:
|
2022-11-05 10:57:54 -05:00
|
|
|
yield signals['data'].value
|
2022-10-30 20:32:02 -05:00
|
|
|
await RisingEdge(pcs.clk)
|
|
|
|
|
|
|
|
async def pcs_send_codes(pcs, codes, valids):
|
|
|
|
await send_recovered_bits(pcs.clk, pcs.bits, pcs.bits_valid,
|
|
|
|
itertools.chain(*codes), valids)
|
|
|
|
|
|
|
|
@timeout(10, 'us')
|
|
|
|
async def test_rx(pcs, valids):
|
|
|
|
pcs.bits.value = LogicArray('11')
|
|
|
|
pcs.bits_valid.value = 2
|
|
|
|
pcs.link_status.value = 1
|
|
|
|
await Timer(1)
|
|
|
|
await cocotb.start(Clock(pcs.clk, 8, units='ns').start())
|
|
|
|
|
|
|
|
packet = list(as_nibbles((0x55, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF)))
|
|
|
|
# And test errors too
|
|
|
|
packet.insert(10, None)
|
|
|
|
|
|
|
|
await cocotb.start(pcs_send_codes(pcs, itertools.chain(
|
|
|
|
frame(packet),
|
|
|
|
# Bad SSDs
|
|
|
|
(Code('C'), Code('I'), Code('I')),
|
|
|
|
(Code('J'), Code('I'), Code('I')),
|
|
|
|
(Code('J'), Code('H'), Code('I'), Code('I')),
|
|
|
|
# Premature end, plus two clocks since we don't have instant turnaround
|
|
|
|
(Code('J'), Code('K'), Code('I'), Code('I'), (1,1)),
|
|
|
|
# Packet spacing
|
|
|
|
*((*frame([0x55, 0x55]), (1,) * i) for i in range(10))
|
|
|
|
), valids))
|
|
|
|
|
|
|
|
assert packet == await alist(mii_recv_packet(pcs))
|
|
|
|
|
2022-11-05 10:57:54 -05:00
|
|
|
false_carriers = 0
|
2022-10-30 20:32:02 -05:00
|
|
|
for _ in range(3):
|
|
|
|
while not (pcs.rx.value and pcs.err.value and pcs.ce.value):
|
|
|
|
await RisingEdge(pcs.clk)
|
2022-11-05 10:57:54 -05:00
|
|
|
false_carriers += pcs.false_carrier.value
|
2022-10-30 20:32:02 -05:00
|
|
|
assert pcs.data.value == 0xE
|
|
|
|
await FallingEdge(pcs.rx)
|
2022-11-05 10:57:54 -05:00
|
|
|
assert false_carriers == 3
|
2022-10-30 20:32:02 -05:00
|
|
|
|
|
|
|
assert [0x5, 0x5, None] == await alist(mii_recv_packet(pcs))
|
|
|
|
|
|
|
|
# Test packet spacing
|
|
|
|
for _ in range(10):
|
|
|
|
assert [0x5, 0x5] == await alist(mii_recv_packet(pcs))
|
|
|
|
|
|
|
|
with_valids(globals(), test_rx)
|