2022-10-30 20:32:02 -05:00
|
|
|
# SPDX-License-Identifier: AGPL-3.0-Only
|
|
|
|
# Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
|
|
|
|
|
|
|
import cocotb
|
|
|
|
from cocotb.clock import Clock
|
|
|
|
from cocotb.triggers import ClockCycles, RisingEdge, FallingEdge, Timer
|
|
|
|
from cocotb.types import LogicArray
|
|
|
|
|
|
|
|
from .pcs import Code, as_nibbles
|
|
|
|
from .util import alist, ClockEnable, ReverseList
|
|
|
|
|
Add phy_core
This module integrates the PCS with the descrambler, implements the PMA
(which is just the link monitor), and implements loopback and coltest
functions. This is more of the PCS/PMA, but the descrambler is
technically part of the PMD, so it's the "core" instead.
We deviate from the standard in one important way: the link doesn't come
up until the descambler is locked. I think this makes sense, since if
the descrambler isn't locked, then the incoming data will be gibberish.
I suspect this isn't part of the standard because the descrambler
doesn't have a locked output in X3.263, so IEEE would have had to
specify it.
Loopback is actually implemented in the PMD, but it modifies the
behavior in several places. It disables collisions (unless
coltest is enabled). Additionally, we need to force the link up (to
avoid the lengthy stabilization timer), but ensure it is down for at
least once cycle (to ensure the descrambler desynchronizes).
On the test side, we just go through the "happy path," as many of the
edge conditions are tested for in the submodule tests. Several of those
tests are modified so that their helper functions can be reused in this
test. In particular, the rx path is now async so that we can feed it
rx_data.
Signed-off-by: Sean Anderson <seanga2@gmail.com>
2022-11-05 11:14:58 -05:00
|
|
|
async def mii_send_packet(pcs, nibbles, signals=None):
|
|
|
|
if signals is None:
|
|
|
|
signals = {
|
|
|
|
'ce': pcs.ce,
|
|
|
|
'enable': pcs.enable,
|
|
|
|
'err': pcs.err,
|
|
|
|
'data': pcs.data,
|
|
|
|
}
|
|
|
|
|
|
|
|
await FallingEdge(signals['ce'])
|
2022-10-30 20:32:02 -05:00
|
|
|
for nibble in nibbles:
|
Add phy_core
This module integrates the PCS with the descrambler, implements the PMA
(which is just the link monitor), and implements loopback and coltest
functions. This is more of the PCS/PMA, but the descrambler is
technically part of the PMD, so it's the "core" instead.
We deviate from the standard in one important way: the link doesn't come
up until the descambler is locked. I think this makes sense, since if
the descrambler isn't locked, then the incoming data will be gibberish.
I suspect this isn't part of the standard because the descrambler
doesn't have a locked output in X3.263, so IEEE would have had to
specify it.
Loopback is actually implemented in the PMD, but it modifies the
behavior in several places. It disables collisions (unless
coltest is enabled). Additionally, we need to force the link up (to
avoid the lengthy stabilization timer), but ensure it is down for at
least once cycle (to ensure the descrambler desynchronizes).
On the test side, we just go through the "happy path," as many of the
edge conditions are tested for in the submodule tests. Several of those
tests are modified so that their helper functions can be reused in this
test. In particular, the rx path is now async so that we can feed it
rx_data.
Signed-off-by: Sean Anderson <seanga2@gmail.com>
2022-11-05 11:14:58 -05:00
|
|
|
signals['enable'].value = 1
|
|
|
|
signals['err'].value = 0
|
2022-10-30 20:32:02 -05:00
|
|
|
if nibble is None:
|
Add phy_core
This module integrates the PCS with the descrambler, implements the PMA
(which is just the link monitor), and implements loopback and coltest
functions. This is more of the PCS/PMA, but the descrambler is
technically part of the PMD, so it's the "core" instead.
We deviate from the standard in one important way: the link doesn't come
up until the descambler is locked. I think this makes sense, since if
the descrambler isn't locked, then the incoming data will be gibberish.
I suspect this isn't part of the standard because the descrambler
doesn't have a locked output in X3.263, so IEEE would have had to
specify it.
Loopback is actually implemented in the PMD, but it modifies the
behavior in several places. It disables collisions (unless
coltest is enabled). Additionally, we need to force the link up (to
avoid the lengthy stabilization timer), but ensure it is down for at
least once cycle (to ensure the descrambler desynchronizes).
On the test side, we just go through the "happy path," as many of the
edge conditions are tested for in the submodule tests. Several of those
tests are modified so that their helper functions can be reused in this
test. In particular, the rx path is now async so that we can feed it
rx_data.
Signed-off-by: Sean Anderson <seanga2@gmail.com>
2022-11-05 11:14:58 -05:00
|
|
|
signals['err'].value = 1
|
2022-10-30 20:32:02 -05:00
|
|
|
else:
|
Add phy_core
This module integrates the PCS with the descrambler, implements the PMA
(which is just the link monitor), and implements loopback and coltest
functions. This is more of the PCS/PMA, but the descrambler is
technically part of the PMD, so it's the "core" instead.
We deviate from the standard in one important way: the link doesn't come
up until the descambler is locked. I think this makes sense, since if
the descrambler isn't locked, then the incoming data will be gibberish.
I suspect this isn't part of the standard because the descrambler
doesn't have a locked output in X3.263, so IEEE would have had to
specify it.
Loopback is actually implemented in the PMD, but it modifies the
behavior in several places. It disables collisions (unless
coltest is enabled). Additionally, we need to force the link up (to
avoid the lengthy stabilization timer), but ensure it is down for at
least once cycle (to ensure the descrambler desynchronizes).
On the test side, we just go through the "happy path," as many of the
edge conditions are tested for in the submodule tests. Several of those
tests are modified so that their helper functions can be reused in this
test. In particular, the rx path is now async so that we can feed it
rx_data.
Signed-off-by: Sean Anderson <seanga2@gmail.com>
2022-11-05 11:14:58 -05:00
|
|
|
signals['data'].value = nibble
|
|
|
|
await FallingEdge(signals['ce'])
|
2022-10-30 20:32:02 -05:00
|
|
|
|
Add phy_core
This module integrates the PCS with the descrambler, implements the PMA
(which is just the link monitor), and implements loopback and coltest
functions. This is more of the PCS/PMA, but the descrambler is
technically part of the PMD, so it's the "core" instead.
We deviate from the standard in one important way: the link doesn't come
up until the descambler is locked. I think this makes sense, since if
the descrambler isn't locked, then the incoming data will be gibberish.
I suspect this isn't part of the standard because the descrambler
doesn't have a locked output in X3.263, so IEEE would have had to
specify it.
Loopback is actually implemented in the PMD, but it modifies the
behavior in several places. It disables collisions (unless
coltest is enabled). Additionally, we need to force the link up (to
avoid the lengthy stabilization timer), but ensure it is down for at
least once cycle (to ensure the descrambler desynchronizes).
On the test side, we just go through the "happy path," as many of the
edge conditions are tested for in the submodule tests. Several of those
tests are modified so that their helper functions can be reused in this
test. In particular, the rx path is now async so that we can feed it
rx_data.
Signed-off-by: Sean Anderson <seanga2@gmail.com>
2022-11-05 11:14:58 -05:00
|
|
|
signals['enable'].value = 0
|
|
|
|
signals['err'].value = 0
|
|
|
|
signals['data'].value = LogicArray("XXXX")
|
|
|
|
await FallingEdge(signals['ce'])
|
2022-10-30 20:32:02 -05:00
|
|
|
|
|
|
|
class PCSError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class BadSSD(PCSError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class PrematureEnd(PCSError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
async def pcs_recv_bits(pcs, data=None):
|
|
|
|
if data is None:
|
|
|
|
data = pcs.bits
|
|
|
|
|
|
|
|
while True:
|
|
|
|
await RisingEdge(pcs.clk)
|
|
|
|
yield data.value
|
|
|
|
|
|
|
|
async def pcs_recv_packet(pcs, bits=None):
|
|
|
|
if bits is None:
|
|
|
|
bits = pcs_recv_bits(pcs)
|
|
|
|
|
|
|
|
rx_bits = ReverseList([1] * 10)
|
|
|
|
|
|
|
|
async def read_bit():
|
|
|
|
rx_bits.append(await anext(bits))
|
|
|
|
|
|
|
|
async def read_code():
|
|
|
|
for _ in range(5):
|
|
|
|
await read_bit()
|
|
|
|
|
|
|
|
async def bad_ssd():
|
|
|
|
while not all(rx_bits[9:0]):
|
|
|
|
await read_bit()
|
|
|
|
raise BadSSDError()
|
|
|
|
|
|
|
|
while all(rx_bits[9:2]) or rx_bits[0]:
|
|
|
|
await read_bit()
|
|
|
|
|
|
|
|
if Code.decode(rx_bits[9:5]) != Code('I') or \
|
|
|
|
Code.decode(rx_bits[4:0]) != Code('J'):
|
|
|
|
await bad_ssd()
|
|
|
|
|
|
|
|
await read_code()
|
|
|
|
if Code.decode(rx_bits[4:0]) != Code('K'):
|
|
|
|
await bad_ssd()
|
|
|
|
|
|
|
|
yield 0x5
|
|
|
|
await read_code()
|
|
|
|
|
|
|
|
yield 0x5
|
|
|
|
while any(rx_bits[9:0]):
|
|
|
|
await read_code()
|
|
|
|
code = Code.decode(rx_bits[9:5])
|
|
|
|
if code == Code('T') and Code.decode(rx_bits[4:0]) == Code('R'):
|
|
|
|
return
|
|
|
|
yield code.data
|
|
|
|
raise PrematureEndError()
|
|
|
|
|
|
|
|
@cocotb.test(timeout_time=10, timeout_unit='us')
|
|
|
|
async def test_tx(pcs):
|
|
|
|
pcs.enable.value = 0
|
|
|
|
pcs.err.value = 0
|
|
|
|
pcs.data.value = LogicArray("XXXX")
|
|
|
|
pcs.link_status.value = 1
|
|
|
|
await cocotb.start(ClockEnable(pcs.clk, pcs.ce, 5))
|
|
|
|
await Timer(1)
|
|
|
|
await cocotb.start(Clock(pcs.clk, 8, units='ns').start())
|
|
|
|
await FallingEdge(pcs.ce)
|
|
|
|
|
|
|
|
# Test that all bytes can be transmitted
|
|
|
|
packet = list(as_nibbles((0x55, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF)))
|
|
|
|
# And ensure errors are propagated
|
|
|
|
packet.insert(10, None)
|
|
|
|
await cocotb.start(mii_send_packet(pcs, packet))
|
|
|
|
assert packet == await alist(pcs_recv_packet(pcs))
|
|
|
|
|
|
|
|
# Test start errors
|
|
|
|
await cocotb.start(mii_send_packet(pcs, [None]))
|
|
|
|
assert [0x5, 0x5, None] == await alist(pcs_recv_packet(pcs))
|
|
|
|
await cocotb.start(mii_send_packet(pcs, [0x5, None]))
|
|
|
|
assert [0x5, 0x5, None] == await alist(pcs_recv_packet(pcs))
|