Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
facd770568 | ||
|
|
faad752b9a | ||
|
|
5855644670 | ||
|
|
a81d43216e | ||
|
|
50a60b649e | ||
|
|
bf19679007 | ||
|
|
6835e921f8 | ||
|
|
1b180a1d64 | ||
|
|
63b60341ed | ||
|
|
f92bbaaa70 | ||
|
|
a73c0b734e | ||
|
|
aa97848450 | ||
|
|
1bd01ae879 | ||
|
|
cfbc80c0cb | ||
|
|
1d5688778a | ||
|
|
71d7c7e9d2 | ||
|
|
30bc6f68a1 | ||
|
|
16eaea6967 |
53
README.md
53
README.md
@@ -3,6 +3,7 @@
|
|||||||
[](https://github.com/alexforencich/cocotbext-eth/actions/)
|
[](https://github.com/alexforencich/cocotbext-eth/actions/)
|
||||||
[](https://codecov.io/gh/alexforencich/cocotbext-eth)
|
[](https://codecov.io/gh/alexforencich/cocotbext-eth)
|
||||||
[](https://pypi.org/project/cocotbext-eth)
|
[](https://pypi.org/project/cocotbext-eth)
|
||||||
|
[](https://pepy.tech/project/cocotbext-eth)
|
||||||
|
|
||||||
GitHub repository: https://github.com/alexforencich/cocotbext-eth
|
GitHub repository: https://github.com/alexforencich/cocotbext-eth
|
||||||
|
|
||||||
@@ -46,6 +47,13 @@ To send data into a design with a `GmiiSource`, call `send()` or `send_nowait()`
|
|||||||
# wait for operation to complete (optional)
|
# wait for operation to complete (optional)
|
||||||
await gmii_source.wait()
|
await gmii_source.wait()
|
||||||
|
|
||||||
|
It is also possible to wait for the transmission of a specific frame to complete by passing an event in the tx_complete field of the `GmiiFrame` object, and then awaiting the event. The frame, with simulation time fields set, will be returned in the event data. Example:
|
||||||
|
|
||||||
|
frame = GmiiFrame.from_payload(b'test data', tx_complete=Event())
|
||||||
|
await gmii_source.send(frame)
|
||||||
|
await frame.tx_complete.wait()
|
||||||
|
print(frame.tx_complete.data.sim_time_sfd)
|
||||||
|
|
||||||
To receive data with a `GmiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
To receive data with a `GmiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
||||||
|
|
||||||
data = await gmii_sink.recv()
|
data = await gmii_sink.recv()
|
||||||
@@ -77,6 +85,7 @@ The `GmiiPhy` class provides a model of a GMII PHY chip. It wraps instances of
|
|||||||
* _reset_: reset signal (optional)
|
* _reset_: reset signal (optional)
|
||||||
* _enable_: clock enable (optional)
|
* _enable_: clock enable (optional)
|
||||||
* _mii_select_: MII mode select (optional)
|
* _mii_select_: MII mode select (optional)
|
||||||
|
* _reset_active_level_: reset active level (optional, default `True`)
|
||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
@@ -93,6 +102,7 @@ The `GmiiPhy` class provides a model of a GMII PHY chip. It wraps instances of
|
|||||||
* `count()`: returns the number of items in the queue (all)
|
* `count()`: returns the number of items in the queue (all)
|
||||||
* `empty()`: returns _True_ if the queue is empty (all)
|
* `empty()`: returns _True_ if the queue is empty (all)
|
||||||
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
||||||
|
* `clear()`: drop all data in queue (all)
|
||||||
* `wait()`: wait for idle (source)
|
* `wait()`: wait for idle (source)
|
||||||
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
||||||
|
|
||||||
@@ -118,7 +128,11 @@ Attributes:
|
|||||||
|
|
||||||
* `data`: bytearray
|
* `data`: bytearray
|
||||||
* `error`: error field, optional; list, each entry qualifies the corresponding entry in `data`.
|
* `error`: error field, optional; list, each entry qualifies the corresponding entry in `data`.
|
||||||
* `rx_sim_time`: simulation time when packet was received by sink.
|
* `sim_time_start`: simulation time of first transfer cycle of frame.
|
||||||
|
* `sim_time_sfd`: simulation time at which the SFD was transferred.
|
||||||
|
* `sim_time_end`: simulation time of last transfer cycle of frame.
|
||||||
|
* `start_lane`: byte lane in which the start control character was transferred.
|
||||||
|
* `tx_complete`: event or callable triggered when frame is transmitted.
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
|
|
||||||
@@ -151,6 +165,13 @@ To send data into a design with an `MiiSource`, call `send()` or `send_nowait()`
|
|||||||
# wait for operation to complete (optional)
|
# wait for operation to complete (optional)
|
||||||
await mii_source.wait()
|
await mii_source.wait()
|
||||||
|
|
||||||
|
It is also possible to wait for the transmission of a specific frame to complete by passing an event in the tx_complete field of the `GmiiFrame` object, and then awaiting the event. The frame, with simulation time fields set, will be returned in the event data. Example:
|
||||||
|
|
||||||
|
frame = GmiiFrame.from_payload(b'test data', tx_complete=Event())
|
||||||
|
await mii_source.send(frame)
|
||||||
|
await frame.tx_complete.wait()
|
||||||
|
print(frame.tx_complete.data.sim_time_sfd)
|
||||||
|
|
||||||
To receive data with an `MiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
To receive data with an `MiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
||||||
|
|
||||||
data = await mii_sink.recv()
|
data = await mii_sink.recv()
|
||||||
@@ -181,6 +202,7 @@ The `MiiPhy` class provides a model of an MII PHY chip. It wraps instances of `
|
|||||||
* _clock_: clock signal
|
* _clock_: clock signal
|
||||||
* _reset_: reset signal (optional)
|
* _reset_: reset signal (optional)
|
||||||
* _enable_: clock enable (optional)
|
* _enable_: clock enable (optional)
|
||||||
|
* _reset_active_level_: reset active level (optional, default `True`)
|
||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
@@ -196,6 +218,7 @@ The `MiiPhy` class provides a model of an MII PHY chip. It wraps instances of `
|
|||||||
* `count()`: returns the number of items in the queue (all)
|
* `count()`: returns the number of items in the queue (all)
|
||||||
* `empty()`: returns _True_ if the queue is empty (all)
|
* `empty()`: returns _True_ if the queue is empty (all)
|
||||||
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
||||||
|
* `clear()`: drop all data in queue (all)
|
||||||
* `wait()`: wait for idle (source)
|
* `wait()`: wait for idle (source)
|
||||||
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
||||||
|
|
||||||
@@ -232,6 +255,13 @@ To send data into a design with an `RgmiiSource`, call `send()` or `send_nowait(
|
|||||||
# wait for operation to complete (optional)
|
# wait for operation to complete (optional)
|
||||||
await rgmii_source.wait()
|
await rgmii_source.wait()
|
||||||
|
|
||||||
|
It is also possible to wait for the transmission of a specific frame to complete by passing an event in the tx_complete field of the `GmiiFrame` object, and then awaiting the event. The frame, with simulation time fields set, will be returned in the event data. Example:
|
||||||
|
|
||||||
|
frame = GmiiFrame.from_payload(b'test data', tx_complete=Event())
|
||||||
|
await rgmii_source.send(frame)
|
||||||
|
await frame.tx_complete.wait()
|
||||||
|
print(frame.tx_complete.data.sim_time_sfd)
|
||||||
|
|
||||||
To receive data with an `RgmiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
To receive data with an `RgmiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
||||||
|
|
||||||
data = await rgmii_sink.recv()
|
data = await rgmii_sink.recv()
|
||||||
@@ -261,6 +291,7 @@ The `RgmiiPhy` class provides a model of an RGMII PHY chip. It wraps instances
|
|||||||
* _reset_: reset signal (optional)
|
* _reset_: reset signal (optional)
|
||||||
* _enable_: clock enable (optional)
|
* _enable_: clock enable (optional)
|
||||||
* _mii_select_: MII mode select (optional)
|
* _mii_select_: MII mode select (optional)
|
||||||
|
* _reset_active_level_: reset active level (optional, default `True`)
|
||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
@@ -277,6 +308,7 @@ The `RgmiiPhy` class provides a model of an RGMII PHY chip. It wraps instances
|
|||||||
* `count()`: returns the number of items in the queue (all)
|
* `count()`: returns the number of items in the queue (all)
|
||||||
* `empty()`: returns _True_ if the queue is empty (all)
|
* `empty()`: returns _True_ if the queue is empty (all)
|
||||||
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
||||||
|
* `clear()`: drop all data in queue (all)
|
||||||
* `wait()`: wait for idle (source)
|
* `wait()`: wait for idle (source)
|
||||||
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
||||||
|
|
||||||
@@ -311,6 +343,13 @@ To send data into a design with an `XgmiiSource`, call `send()` or `send_nowait(
|
|||||||
# wait for operation to complete (optional)
|
# wait for operation to complete (optional)
|
||||||
await xgmii_source.wait()
|
await xgmii_source.wait()
|
||||||
|
|
||||||
|
It is also possible to wait for the transmission of a specific frame to complete by passing an event in the tx_complete field of the `XgmiiFrame` object, and then awaiting the event. The frame, with simulation time fields set, will be returned in the event data. Example:
|
||||||
|
|
||||||
|
frame = XgmiiFrame.from_payload(b'test data', tx_complete=Event())
|
||||||
|
await xgmii_source.send(frame)
|
||||||
|
await frame.tx_complete.wait()
|
||||||
|
print(frame.tx_complete.data.sim_time_sfd)
|
||||||
|
|
||||||
To receive data with an `XgmiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
To receive data with an `XgmiiSink`, call `recv()` or `recv_nowait()`. Optionally call `wait()` to wait for new receive data.
|
||||||
|
|
||||||
data = await xgmii_sink.recv()
|
data = await xgmii_sink.recv()
|
||||||
@@ -327,6 +366,7 @@ To receive data with an `XgmiiSink`, call `recv()` or `recv_nowait()`. Optional
|
|||||||
* _clock_: clock signal
|
* _clock_: clock signal
|
||||||
* _reset_: reset signal (optional)
|
* _reset_: reset signal (optional)
|
||||||
* _enable_: clock enable (optional)
|
* _enable_: clock enable (optional)
|
||||||
|
* _reset_active_level_: reset active level (optional, default `True`)
|
||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
@@ -342,6 +382,7 @@ To receive data with an `XgmiiSink`, call `recv()` or `recv_nowait()`. Optional
|
|||||||
* `count()`: returns the number of items in the queue (all)
|
* `count()`: returns the number of items in the queue (all)
|
||||||
* `empty()`: returns _True_ if the queue is empty (all)
|
* `empty()`: returns _True_ if the queue is empty (all)
|
||||||
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
* `idle()`: returns _True_ if no transfer is in progress (all) or if the queue is not empty (source)
|
||||||
|
* `clear()`: drop all data in queue (all)
|
||||||
* `wait()`: wait for idle (source)
|
* `wait()`: wait for idle (source)
|
||||||
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
||||||
|
|
||||||
@@ -379,8 +420,11 @@ Attributes:
|
|||||||
|
|
||||||
* `data`: bytearray
|
* `data`: bytearray
|
||||||
* `ctrl`: control field, optional; list, each entry qualifies the corresponding entry in `data` as an XGMII control character.
|
* `ctrl`: control field, optional; list, each entry qualifies the corresponding entry in `data` as an XGMII control character.
|
||||||
* `rx_sim_time`: simulation time when packet was received by sink.
|
* `sim_time_start`: simulation time of first transfer cycle of frame.
|
||||||
* `rx_start_lane`: byte lane that the frame start control character was received in.
|
* `sim_time_sfd`: simulation time at which the SFD was transferred.
|
||||||
|
* `sim_time_end`: simulation time of last transfer cycle of frame.
|
||||||
|
* `start_lane`: byte lane in which the start control character was transferred.
|
||||||
|
* `tx_complete`: event or callable triggered when frame is transmitted.
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
|
|
||||||
@@ -429,7 +473,8 @@ Once the clock is instantiated, it will generate a continuous stream of monotoni
|
|||||||
* _pps_: pulse-per-second signal (optional)
|
* _pps_: pulse-per-second signal (optional)
|
||||||
* _clock_: clock
|
* _clock_: clock
|
||||||
* _reset_: reset (optional)
|
* _reset_: reset (optional)
|
||||||
* _period_ns_: clock period (nanoseconds)
|
* _reset_active_level_: reset active level (optional, default `True`)
|
||||||
|
* _period_ns_: clock period (nanoseconds, default `6.4`)
|
||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ THE SOFTWARE.
|
|||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
import zlib
|
import zlib
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
|
from cocotb.queue import Queue
|
||||||
from cocotb.triggers import RisingEdge, Timer, First, Event
|
from cocotb.triggers import RisingEdge, Timer, First, Event
|
||||||
from cocotb.utils import get_sim_time, get_sim_steps
|
from cocotb.utils import get_sim_time, get_sim_steps
|
||||||
|
|
||||||
@@ -37,32 +37,41 @@ from .reset import Reset
|
|||||||
|
|
||||||
|
|
||||||
class GmiiFrame:
|
class GmiiFrame:
|
||||||
def __init__(self, data=None, error=None):
|
def __init__(self, data=None, error=None, tx_complete=None):
|
||||||
self.data = bytearray()
|
self.data = bytearray()
|
||||||
self.error = None
|
self.error = None
|
||||||
self.rx_sim_time = None
|
self.sim_time_start = None
|
||||||
|
self.sim_time_sfd = None
|
||||||
|
self.sim_time_end = None
|
||||||
|
self.tx_complete = None
|
||||||
|
|
||||||
if type(data) is GmiiFrame:
|
if type(data) is GmiiFrame:
|
||||||
self.data = bytearray(data.data)
|
self.data = bytearray(data.data)
|
||||||
self.error = data.error
|
self.error = data.error
|
||||||
self.rx_sim_time = data.rx_sim_time
|
self.sim_time_start = data.sim_time_start
|
||||||
|
self.sim_time_sfd = data.sim_time_sfd
|
||||||
|
self.sim_time_end = data.sim_time_end
|
||||||
|
self.tx_complete = data.tx_complete
|
||||||
else:
|
else:
|
||||||
self.data = bytearray(data)
|
self.data = bytearray(data)
|
||||||
self.error = error
|
self.error = error
|
||||||
|
|
||||||
|
if tx_complete is not None:
|
||||||
|
self.tx_complete = tx_complete
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_payload(cls, payload, min_len=60):
|
def from_payload(cls, payload, min_len=60, tx_complete=None):
|
||||||
payload = bytearray(payload)
|
payload = bytearray(payload)
|
||||||
if len(payload) < min_len:
|
if len(payload) < min_len:
|
||||||
payload.extend(bytearray(min_len-len(payload)))
|
payload.extend(bytearray(min_len-len(payload)))
|
||||||
payload.extend(struct.pack('<L', zlib.crc32(payload)))
|
payload.extend(struct.pack('<L', zlib.crc32(payload)))
|
||||||
return cls.from_raw_payload(payload)
|
return cls.from_raw_payload(payload, tx_complete=tx_complete)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_raw_payload(cls, payload):
|
def from_raw_payload(cls, payload, tx_complete=None):
|
||||||
data = bytearray(ETH_PREAMBLE)
|
data = bytearray(ETH_PREAMBLE)
|
||||||
data.extend(payload)
|
data.extend(payload)
|
||||||
return cls(data)
|
return cls(data, tx_complete=tx_complete)
|
||||||
|
|
||||||
def get_preamble_len(self):
|
def get_preamble_len(self):
|
||||||
return self.data.index(EthPre.SFD)+1
|
return self.data.index(EthPre.SFD)+1
|
||||||
@@ -94,6 +103,12 @@ class GmiiFrame:
|
|||||||
if not any(self.error):
|
if not any(self.error):
|
||||||
self.error = None
|
self.error = None
|
||||||
|
|
||||||
|
def handle_tx_complete(self):
|
||||||
|
if isinstance(self.tx_complete, Event):
|
||||||
|
self.tx_complete.set(self)
|
||||||
|
elif callable(self.tx_complete):
|
||||||
|
self.tx_complete(self)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if type(other) is GmiiFrame:
|
if type(other) is GmiiFrame:
|
||||||
return self.data == other.data
|
return self.data == other.data
|
||||||
@@ -102,7 +117,9 @@ class GmiiFrame:
|
|||||||
return (
|
return (
|
||||||
f"{type(self).__name__}(data={self.data!r}, "
|
f"{type(self).__name__}(data={self.data!r}, "
|
||||||
f"error={self.error!r}, "
|
f"error={self.error!r}, "
|
||||||
f"rx_sim_time={self.rx_sim_time!r})"
|
f"sim_time_start={self.sim_time_start!r}, "
|
||||||
|
f"sim_time_sfd={self.sim_time_sfd!r}, "
|
||||||
|
f"sim_time_end={self.sim_time_end!r})"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
@@ -117,7 +134,7 @@ class GmiiFrame:
|
|||||||
|
|
||||||
class GmiiSource(Reset):
|
class GmiiSource(Reset):
|
||||||
|
|
||||||
def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, *args, **kwargs):
|
def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, reset_active_level=True, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.er = er
|
self.er = er
|
||||||
@@ -135,7 +152,9 @@ class GmiiSource(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
|
self.idle_event = Event()
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
self.ifg = 12
|
self.ifg = 12
|
||||||
self.mii_mode = False
|
self.mii_mode = False
|
||||||
@@ -156,29 +175,40 @@ class GmiiSource(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def send(self, frame):
|
async def send(self, frame):
|
||||||
self.send_nowait(frame)
|
frame = GmiiFrame(frame)
|
||||||
|
await self.queue.put(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
|
self.queue_occupancy_bytes += len(frame)
|
||||||
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
def send_nowait(self, frame):
|
def send_nowait(self, frame):
|
||||||
frame = GmiiFrame(frame)
|
frame = GmiiFrame(frame)
|
||||||
|
self.queue.put_nowait(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
self.queue.append(frame)
|
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return self.empty() and not self.active
|
return self.empty() and not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.idle_event.set()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self):
|
async def wait(self):
|
||||||
while not self.idle():
|
await self.idle_event.wait()
|
||||||
await RisingEdge(self.clock)
|
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -210,11 +240,14 @@ class GmiiSource(Reset):
|
|||||||
# in IFG
|
# in IFG
|
||||||
ifg_cnt -= 1
|
ifg_cnt -= 1
|
||||||
|
|
||||||
elif frame is None and self.queue:
|
elif frame is None and not self.queue.empty():
|
||||||
# send frame
|
# send frame
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
|
frame.sim_time_start = get_sim_time()
|
||||||
|
frame.sim_time_sfd = None
|
||||||
|
frame.sim_time_end = None
|
||||||
self.log.info("TX frame: %s", frame)
|
self.log.info("TX frame: %s", frame)
|
||||||
frame.normalize()
|
frame.normalize()
|
||||||
|
|
||||||
@@ -235,13 +268,18 @@ class GmiiSource(Reset):
|
|||||||
self.active = True
|
self.active = True
|
||||||
|
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
self.data <= frame.data.pop(0)
|
d = frame.data.pop(0)
|
||||||
|
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD):
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
self.data <= d
|
||||||
if self.er is not None:
|
if self.er is not None:
|
||||||
self.er <= frame.error.pop(0)
|
self.er <= frame.error.pop(0)
|
||||||
self.dv <= 1
|
self.dv <= 1
|
||||||
|
|
||||||
if not frame.data:
|
if not frame.data:
|
||||||
ifg_cnt = max(self.ifg, 1)
|
ifg_cnt = max(self.ifg, 1)
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
|
frame.handle_tx_complete()
|
||||||
frame = None
|
frame = None
|
||||||
else:
|
else:
|
||||||
self.data <= 0
|
self.data <= 0
|
||||||
@@ -249,11 +287,12 @@ class GmiiSource(Reset):
|
|||||||
self.er <= 0
|
self.er <= 0
|
||||||
self.dv <= 0
|
self.dv <= 0
|
||||||
self.active = False
|
self.active = False
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
|
|
||||||
class GmiiSink(Reset):
|
class GmiiSink(Reset):
|
||||||
|
|
||||||
def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, *args, **kwargs):
|
def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, reset_active_level=True, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.er = er
|
self.er = er
|
||||||
@@ -271,8 +310,8 @@ class GmiiSink(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
self.sync = Event()
|
self.active_event = Event()
|
||||||
|
|
||||||
self.mii_mode = False
|
self.mii_mode = False
|
||||||
|
|
||||||
@@ -290,39 +329,49 @@ class GmiiSink(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def recv(self, compact=True):
|
async def recv(self, compact=True):
|
||||||
while self.empty():
|
frame = await self.queue.get()
|
||||||
self.sync.clear()
|
if self.queue.empty():
|
||||||
await self.sync.wait()
|
self.active_event.clear()
|
||||||
return self.recv_nowait(compact)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
|
self.queue_occupancy_frames -= 1
|
||||||
|
return frame
|
||||||
|
|
||||||
def recv_nowait(self, compact=True):
|
def recv_nowait(self, compact=True):
|
||||||
if self.queue:
|
if not self.queue.empty():
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
|
if self.queue.empty():
|
||||||
|
self.active_event.clear()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
return frame
|
return frame
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return not self.active
|
return not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.active_event.clear()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self, timeout=0, timeout_unit=None):
|
async def wait(self, timeout=0, timeout_unit=None):
|
||||||
if not self.empty():
|
if not self.empty():
|
||||||
return
|
return
|
||||||
self.sync.clear()
|
|
||||||
if timeout:
|
if timeout:
|
||||||
await First(self.sync.wait(), Timer(timeout, timeout_unit))
|
await First(self.active_event.wait(), Timer(timeout, timeout_unit))
|
||||||
else:
|
else:
|
||||||
await self.sync.wait()
|
await self.active_event.wait()
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -353,7 +402,7 @@ class GmiiSink(Reset):
|
|||||||
if dv_val:
|
if dv_val:
|
||||||
# start of frame
|
# start of frame
|
||||||
frame = GmiiFrame(bytearray(), [])
|
frame = GmiiFrame(bytearray(), [])
|
||||||
frame.rx_sim_time = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
else:
|
else:
|
||||||
if not dv_val:
|
if not dv_val:
|
||||||
# end of frame
|
# end of frame
|
||||||
@@ -383,24 +432,28 @@ class GmiiSink(Reset):
|
|||||||
frame.error = error
|
frame.error = error
|
||||||
|
|
||||||
frame.compact()
|
frame.compact()
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
self.queue.append(frame)
|
self.queue.put_nowait(frame)
|
||||||
self.sync.set()
|
self.active_event.set()
|
||||||
|
|
||||||
frame = None
|
frame = None
|
||||||
|
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
|
if frame.sim_time_sfd is None and d_val in (EthPre.SFD, 0xD):
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
frame.data.append(d_val)
|
frame.data.append(d_val)
|
||||||
frame.error.append(er_val)
|
frame.error.append(er_val)
|
||||||
|
|
||||||
|
|
||||||
class GmiiPhy:
|
class GmiiPhy:
|
||||||
def __init__(self, txd, tx_er, tx_en, tx_clk, gtx_clk, rxd, rx_er, rx_dv, rx_clk,
|
def __init__(self, txd, tx_er, tx_en, tx_clk, gtx_clk, rxd, rx_er, rx_dv, rx_clk,
|
||||||
reset=None, speed=1000e6, *args, **kwargs):
|
reset=None, reset_active_level=True, speed=1000e6, *args, **kwargs):
|
||||||
|
|
||||||
self.gtx_clk = gtx_clk
|
self.gtx_clk = gtx_clk
|
||||||
self.tx_clk = tx_clk
|
self.tx_clk = tx_clk
|
||||||
@@ -408,8 +461,8 @@ class GmiiPhy:
|
|||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.tx = GmiiSink(txd, tx_er, tx_en, tx_clk, reset)
|
self.tx = GmiiSink(txd, tx_er, tx_en, tx_clk, reset, reset_active_level=reset_active_level)
|
||||||
self.rx = GmiiSource(rxd, rx_er, rx_dv, rx_clk, reset)
|
self.rx = GmiiSource(rxd, rx_er, rx_dv, rx_clk, reset_active_level=reset_active_level)
|
||||||
|
|
||||||
self.rx_clk.setimmediatevalue(0)
|
self.rx_clk.setimmediatevalue(0)
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
|
from cocotb.queue import Queue
|
||||||
from cocotb.triggers import RisingEdge, Timer, First, Event
|
from cocotb.triggers import RisingEdge, Timer, First, Event
|
||||||
from cocotb.utils import get_sim_time, get_sim_steps
|
from cocotb.utils import get_sim_time, get_sim_steps
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ from .reset import Reset
|
|||||||
|
|
||||||
class MiiSource(Reset):
|
class MiiSource(Reset):
|
||||||
|
|
||||||
def __init__(self, data, er, dv, clock, reset=None, enable=None, *args, **kwargs):
|
def __init__(self, data, er, dv, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.er = er
|
self.er = er
|
||||||
@@ -54,7 +54,9 @@ class MiiSource(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
|
self.idle_event = Event()
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
self.ifg = 12
|
self.ifg = 12
|
||||||
|
|
||||||
@@ -74,29 +76,40 @@ class MiiSource(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def send(self, frame):
|
async def send(self, frame):
|
||||||
self.send_nowait(frame)
|
frame = GmiiFrame(frame)
|
||||||
|
await self.queue.put(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
|
self.queue_occupancy_bytes += len(frame)
|
||||||
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
def send_nowait(self, frame):
|
def send_nowait(self, frame):
|
||||||
frame = GmiiFrame(frame)
|
frame = GmiiFrame(frame)
|
||||||
|
self.queue.put_nowait(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
self.queue.append(frame)
|
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return self.empty() and not self.active
|
return self.empty() and not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.idle_event.set()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self):
|
async def wait(self):
|
||||||
while not self.idle():
|
await self.idle_event.wait()
|
||||||
await RisingEdge(self.clock)
|
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -128,11 +141,14 @@ class MiiSource(Reset):
|
|||||||
# in IFG
|
# in IFG
|
||||||
ifg_cnt -= 1
|
ifg_cnt -= 1
|
||||||
|
|
||||||
elif frame is None and self.queue:
|
elif frame is None and not self.queue.empty():
|
||||||
# send frame
|
# send frame
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
|
frame.sim_time_start = get_sim_time()
|
||||||
|
frame.sim_time_sfd = None
|
||||||
|
frame.sim_time_end = None
|
||||||
self.log.info("TX frame: %s", frame)
|
self.log.info("TX frame: %s", frame)
|
||||||
frame.normalize()
|
frame.normalize()
|
||||||
|
|
||||||
@@ -149,13 +165,18 @@ class MiiSource(Reset):
|
|||||||
self.active = True
|
self.active = True
|
||||||
|
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
self.data <= frame.data.pop(0)
|
d = frame.data.pop(0)
|
||||||
|
if frame.sim_time_sfd is None and d == 0xD:
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
self.data <= d
|
||||||
if self.er is not None:
|
if self.er is not None:
|
||||||
self.er <= frame.error.pop(0)
|
self.er <= frame.error.pop(0)
|
||||||
self.dv <= 1
|
self.dv <= 1
|
||||||
|
|
||||||
if not frame.data:
|
if not frame.data:
|
||||||
ifg_cnt = max(self.ifg, 1)
|
ifg_cnt = max(self.ifg, 1)
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
|
frame.handle_tx_complete()
|
||||||
frame = None
|
frame = None
|
||||||
else:
|
else:
|
||||||
self.data <= 0
|
self.data <= 0
|
||||||
@@ -163,11 +184,12 @@ class MiiSource(Reset):
|
|||||||
self.er <= 0
|
self.er <= 0
|
||||||
self.dv <= 0
|
self.dv <= 0
|
||||||
self.active = False
|
self.active = False
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
|
|
||||||
class MiiSink(Reset):
|
class MiiSink(Reset):
|
||||||
|
|
||||||
def __init__(self, data, er, dv, clock, reset=None, enable=None, *args, **kwargs):
|
def __init__(self, data, er, dv, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.er = er
|
self.er = er
|
||||||
@@ -184,8 +206,8 @@ class MiiSink(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
self.sync = Event()
|
self.active_event = Event()
|
||||||
|
|
||||||
self.queue_occupancy_bytes = 0
|
self.queue_occupancy_bytes = 0
|
||||||
self.queue_occupancy_frames = 0
|
self.queue_occupancy_frames = 0
|
||||||
@@ -201,39 +223,49 @@ class MiiSink(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def recv(self, compact=True):
|
async def recv(self, compact=True):
|
||||||
while self.empty():
|
frame = await self.queue.get()
|
||||||
self.sync.clear()
|
if self.queue.empty():
|
||||||
await self.sync.wait()
|
self.active_event.clear()
|
||||||
return self.recv_nowait(compact)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
|
self.queue_occupancy_frames -= 1
|
||||||
|
return frame
|
||||||
|
|
||||||
def recv_nowait(self, compact=True):
|
def recv_nowait(self, compact=True):
|
||||||
if self.queue:
|
if not self.queue.empty():
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
|
if self.queue.empty():
|
||||||
|
self.active_event.clear()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
return frame
|
return frame
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return not self.active
|
return not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.active_event.clear()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self, timeout=0, timeout_unit=None):
|
async def wait(self, timeout=0, timeout_unit=None):
|
||||||
if not self.empty():
|
if not self.empty():
|
||||||
return
|
return
|
||||||
self.sync.clear()
|
|
||||||
if timeout:
|
if timeout:
|
||||||
await First(self.sync.wait(), Timer(timeout, timeout_unit))
|
await First(self.active_event.wait(), Timer(timeout, timeout_unit))
|
||||||
else:
|
else:
|
||||||
await self.sync.wait()
|
await self.active_event.wait()
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -264,7 +296,7 @@ class MiiSink(Reset):
|
|||||||
if dv_val:
|
if dv_val:
|
||||||
# start of frame
|
# start of frame
|
||||||
frame = GmiiFrame(bytearray(), [])
|
frame = GmiiFrame(bytearray(), [])
|
||||||
frame.rx_sim_time = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
else:
|
else:
|
||||||
if not dv_val:
|
if not dv_val:
|
||||||
# end of frame
|
# end of frame
|
||||||
@@ -289,30 +321,36 @@ class MiiSink(Reset):
|
|||||||
frame.error = error
|
frame.error = error
|
||||||
|
|
||||||
frame.compact()
|
frame.compact()
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
self.queue.append(frame)
|
self.queue.put_nowait(frame)
|
||||||
self.sync.set()
|
self.active_event.set()
|
||||||
|
|
||||||
frame = None
|
frame = None
|
||||||
|
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
|
if frame.sim_time_sfd is None and d_val == 0xD:
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
frame.data.append(d_val)
|
frame.data.append(d_val)
|
||||||
frame.error.append(er_val)
|
frame.error.append(er_val)
|
||||||
|
|
||||||
|
|
||||||
class MiiPhy:
|
class MiiPhy:
|
||||||
def __init__(self, txd, tx_er, tx_en, tx_clk, rxd, rx_er, rx_dv, rx_clk, reset=None, speed=100e6, *args, **kwargs):
|
def __init__(self, txd, tx_er, tx_en, tx_clk, rxd, rx_er, rx_dv, rx_clk, reset=None,
|
||||||
|
reset_active_level=True, speed=100e6, *args, **kwargs):
|
||||||
|
|
||||||
self.tx_clk = tx_clk
|
self.tx_clk = tx_clk
|
||||||
self.rx_clk = rx_clk
|
self.rx_clk = rx_clk
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.tx = MiiSink(txd, tx_er, tx_en, tx_clk, reset)
|
self.tx = MiiSink(txd, tx_er, tx_en, tx_clk, reset, reset_active_level=reset_active_level)
|
||||||
self.rx = MiiSource(rxd, rx_er, rx_dv, rx_clk, reset)
|
self.rx = MiiSource(rxd, rx_er, rx_dv, rx_clk, reset, reset_active_level=reset_active_level)
|
||||||
|
|
||||||
self.tx_clk.setimmediatevalue(0)
|
self.tx_clk.setimmediatevalue(0)
|
||||||
self.rx_clk.setimmediatevalue(0)
|
self.rx_clk.setimmediatevalue(0)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class PtpClock(Reset):
|
|||||||
pps=None,
|
pps=None,
|
||||||
clock=None,
|
clock=None,
|
||||||
reset=None,
|
reset=None,
|
||||||
|
reset_active_level=True,
|
||||||
period_ns=6.4,
|
period_ns=6.4,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ class PtpClock(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
def set_period(self, ns, fns):
|
def set_period(self, ns, fns):
|
||||||
self.period_ns = int(ns)
|
self.period_ns = int(ns)
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ from cocotb.triggers import RisingEdge, FallingEdge
|
|||||||
|
|
||||||
|
|
||||||
class Reset:
|
class Reset:
|
||||||
def _init_reset(self, reset_signal=None, active_high=True):
|
def _init_reset(self, reset_signal=None, active_level=True):
|
||||||
self._local_reset = False
|
self._local_reset = False
|
||||||
self._ext_reset = False
|
self._ext_reset = False
|
||||||
self._reset_state = True
|
self._reset_state = True
|
||||||
|
|
||||||
if reset_signal is not None:
|
if reset_signal is not None:
|
||||||
cocotb.fork(self._run_reset(reset_signal, active_high))
|
cocotb.fork(self._run_reset(reset_signal, bool(active_level)))
|
||||||
|
|
||||||
self._update_reset()
|
self._update_reset()
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class Reset:
|
|||||||
self.assert_reset(True)
|
self.assert_reset(True)
|
||||||
self.assert_reset(False)
|
self.assert_reset(False)
|
||||||
else:
|
else:
|
||||||
self._local_reset = val
|
self._local_reset = bool(val)
|
||||||
self._update_reset()
|
self._update_reset()
|
||||||
|
|
||||||
def _update_reset(self):
|
def _update_reset(self):
|
||||||
@@ -54,13 +54,13 @@ class Reset:
|
|||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def _run_reset(self, reset_signal, active_high):
|
async def _run_reset(self, reset_signal, active_level):
|
||||||
while True:
|
while True:
|
||||||
if bool(reset_signal.value):
|
if bool(reset_signal.value):
|
||||||
await FallingEdge(reset_signal)
|
await FallingEdge(reset_signal)
|
||||||
self._ext_reset = not active_high
|
self._ext_reset = not active_level
|
||||||
self._update_reset()
|
self._update_reset()
|
||||||
else:
|
else:
|
||||||
await RisingEdge(reset_signal)
|
await RisingEdge(reset_signal)
|
||||||
self._ext_reset = active_high
|
self._ext_reset = active_level
|
||||||
self._update_reset()
|
self._update_reset()
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
|
from cocotb.queue import Queue
|
||||||
from cocotb.triggers import RisingEdge, FallingEdge, Timer, First, Event
|
from cocotb.triggers import RisingEdge, FallingEdge, Timer, First, Event
|
||||||
from cocotb.utils import get_sim_time, get_sim_steps
|
from cocotb.utils import get_sim_time, get_sim_steps
|
||||||
|
|
||||||
@@ -37,7 +37,9 @@ from .reset import Reset
|
|||||||
|
|
||||||
class RgmiiSource(Reset):
|
class RgmiiSource(Reset):
|
||||||
|
|
||||||
def __init__(self, data, ctrl, clock, reset=None, enable=None, mii_select=None, *args, **kwargs):
|
def __init__(self, data, ctrl, clock, reset=None, enable=None, mii_select=None,
|
||||||
|
reset_active_level=True, *args, **kwargs):
|
||||||
|
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.ctrl = ctrl
|
self.ctrl = ctrl
|
||||||
@@ -54,7 +56,9 @@ class RgmiiSource(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
|
self.idle_event = Event()
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
self.ifg = 12
|
self.ifg = 12
|
||||||
self.mii_mode = False
|
self.mii_mode = False
|
||||||
@@ -72,29 +76,40 @@ class RgmiiSource(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def send(self, frame):
|
async def send(self, frame):
|
||||||
self.send_nowait(frame)
|
frame = GmiiFrame(frame)
|
||||||
|
await self.queue.put(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
|
self.queue_occupancy_bytes += len(frame)
|
||||||
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
def send_nowait(self, frame):
|
def send_nowait(self, frame):
|
||||||
frame = GmiiFrame(frame)
|
frame = GmiiFrame(frame)
|
||||||
|
self.queue.put_nowait(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
self.queue.append(frame)
|
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return self.empty() and not self.active
|
return self.empty() and not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.idle_event.set()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self):
|
async def wait(self):
|
||||||
while not self.idle():
|
await self.idle_event.wait()
|
||||||
await RisingEdge(self.clock)
|
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -122,7 +137,6 @@ class RgmiiSource(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await RisingEdge(self.clock)
|
await RisingEdge(self.clock)
|
||||||
|
|
||||||
if not self.mii_mode:
|
|
||||||
# send high nibble after rising edge, leading in to falling edge
|
# send high nibble after rising edge, leading in to falling edge
|
||||||
self.data <= d >> 4
|
self.data <= d >> 4
|
||||||
self.ctrl <= en ^ er
|
self.ctrl <= en ^ er
|
||||||
@@ -132,11 +146,14 @@ class RgmiiSource(Reset):
|
|||||||
# in IFG
|
# in IFG
|
||||||
ifg_cnt -= 1
|
ifg_cnt -= 1
|
||||||
|
|
||||||
elif frame is None and self.queue:
|
elif frame is None and not self.queue.empty():
|
||||||
# send frame
|
# send frame
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
|
frame.sim_time_start = get_sim_time()
|
||||||
|
frame.sim_time_sfd = None
|
||||||
|
frame.sim_time_end = None
|
||||||
self.log.info("TX frame: %s", frame)
|
self.log.info("TX frame: %s", frame)
|
||||||
frame.normalize()
|
frame.normalize()
|
||||||
|
|
||||||
@@ -147,8 +164,8 @@ class RgmiiSource(Reset):
|
|||||||
mii_data = []
|
mii_data = []
|
||||||
mii_error = []
|
mii_error = []
|
||||||
for b, e in zip(frame.data, frame.error):
|
for b, e in zip(frame.data, frame.error):
|
||||||
mii_data.append(b & 0x0F)
|
mii_data.append((b & 0x0F)*0x11)
|
||||||
mii_data.append(b >> 4)
|
mii_data.append((b >> 4)*0x11)
|
||||||
mii_error.append(e)
|
mii_error.append(e)
|
||||||
mii_error.append(e)
|
mii_error.append(e)
|
||||||
frame.data = mii_data
|
frame.data = mii_data
|
||||||
@@ -161,14 +178,20 @@ class RgmiiSource(Reset):
|
|||||||
er = frame.error.pop(0)
|
er = frame.error.pop(0)
|
||||||
en = 1
|
en = 1
|
||||||
|
|
||||||
|
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD, 0xDD):
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
if not frame.data:
|
if not frame.data:
|
||||||
ifg_cnt = max(self.ifg, 1)
|
ifg_cnt = max(self.ifg, 1)
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
|
frame.handle_tx_complete()
|
||||||
frame = None
|
frame = None
|
||||||
else:
|
else:
|
||||||
d = 0
|
d = 0
|
||||||
er = 0
|
er = 0
|
||||||
en = 0
|
en = 0
|
||||||
self.active = False
|
self.active = False
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
await FallingEdge(self.clock)
|
await FallingEdge(self.clock)
|
||||||
|
|
||||||
@@ -179,7 +202,9 @@ class RgmiiSource(Reset):
|
|||||||
|
|
||||||
class RgmiiSink(Reset):
|
class RgmiiSink(Reset):
|
||||||
|
|
||||||
def __init__(self, data, ctrl, clock, reset=None, enable=None, mii_select=None, *args, **kwargs):
|
def __init__(self, data, ctrl, clock, reset=None, enable=None, mii_select=None,
|
||||||
|
reset_active_level=True, *args, **kwargs):
|
||||||
|
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.ctrl = ctrl
|
self.ctrl = ctrl
|
||||||
@@ -196,8 +221,8 @@ class RgmiiSink(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
self.sync = Event()
|
self.active_event = Event()
|
||||||
|
|
||||||
self.mii_mode = False
|
self.mii_mode = False
|
||||||
|
|
||||||
@@ -212,39 +237,49 @@ class RgmiiSink(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def recv(self, compact=True):
|
async def recv(self, compact=True):
|
||||||
while self.empty():
|
frame = await self.queue.get()
|
||||||
self.sync.clear()
|
if self.queue.empty():
|
||||||
await self.sync.wait()
|
self.active_event.clear()
|
||||||
return self.recv_nowait(compact)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
|
self.queue_occupancy_frames -= 1
|
||||||
|
return frame
|
||||||
|
|
||||||
def recv_nowait(self, compact=True):
|
def recv_nowait(self, compact=True):
|
||||||
if self.queue:
|
if not self.queue.empty():
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
|
if self.queue.empty():
|
||||||
|
self.active_event.clear()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
return frame
|
return frame
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return not self.active
|
return not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.active_event.clear()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self, timeout=0, timeout_unit=None):
|
async def wait(self, timeout=0, timeout_unit=None):
|
||||||
if not self.empty():
|
if not self.empty():
|
||||||
return
|
return
|
||||||
self.sync.clear()
|
|
||||||
if timeout:
|
if timeout:
|
||||||
await First(self.sync.wait(), Timer(timeout, timeout_unit))
|
await First(self.active_event.wait(), Timer(timeout, timeout_unit))
|
||||||
else:
|
else:
|
||||||
await self.sync.wait()
|
await self.active_event.wait()
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -285,7 +320,7 @@ class RgmiiSink(Reset):
|
|||||||
if dv_val:
|
if dv_val:
|
||||||
# start of frame
|
# start of frame
|
||||||
frame = GmiiFrame(bytearray(), [])
|
frame = GmiiFrame(bytearray(), [])
|
||||||
frame.rx_sim_time = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
else:
|
else:
|
||||||
if not dv_val:
|
if not dv_val:
|
||||||
# end of frame
|
# end of frame
|
||||||
@@ -315,30 +350,36 @@ class RgmiiSink(Reset):
|
|||||||
frame.error = error
|
frame.error = error
|
||||||
|
|
||||||
frame.compact()
|
frame.compact()
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
self.queue.append(frame)
|
self.queue.put_nowait(frame)
|
||||||
self.sync.set()
|
self.active_event.set()
|
||||||
|
|
||||||
frame = None
|
frame = None
|
||||||
|
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
|
if frame.sim_time_sfd is None and d_val in (EthPre.SFD, 0xD, 0xDD):
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
frame.data.append(d_val)
|
frame.data.append(d_val)
|
||||||
frame.error.append(er_val)
|
frame.error.append(er_val)
|
||||||
|
|
||||||
|
|
||||||
class RgmiiPhy:
|
class RgmiiPhy:
|
||||||
def __init__(self, txd, tx_ctl, tx_clk, rxd, rx_ctl, rx_clk, reset=None, speed=1000e6, *args, **kwargs):
|
def __init__(self, txd, tx_ctl, tx_clk, rxd, rx_ctl, rx_clk, reset=None,
|
||||||
|
reset_active_level=True, speed=1000e6, *args, **kwargs):
|
||||||
|
|
||||||
self.tx_clk = tx_clk
|
self.tx_clk = tx_clk
|
||||||
self.rx_clk = rx_clk
|
self.rx_clk = rx_clk
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.tx = RgmiiSink(txd, tx_ctl, tx_clk, reset)
|
self.tx = RgmiiSink(txd, tx_ctl, tx_clk, reset, reset_active_level=reset_active_level)
|
||||||
self.rx = RgmiiSource(rxd, rx_ctl, rx_clk, reset)
|
self.rx = RgmiiSource(rxd, rx_ctl, rx_clk, reset, reset_active_level=reset_active_level)
|
||||||
|
|
||||||
self.rx_clk.setimmediatevalue(0)
|
self.rx_clk.setimmediatevalue(0)
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.1.4"
|
__version__ = "0.1.8"
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ THE SOFTWARE.
|
|||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
import zlib
|
import zlib
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
|
from cocotb.queue import Queue
|
||||||
from cocotb.triggers import RisingEdge, Timer, First, Event
|
from cocotb.triggers import RisingEdge, Timer, First, Event
|
||||||
from cocotb.utils import get_sim_time
|
from cocotb.utils import get_sim_time
|
||||||
|
|
||||||
@@ -37,34 +37,43 @@ from .reset import Reset
|
|||||||
|
|
||||||
|
|
||||||
class XgmiiFrame:
|
class XgmiiFrame:
|
||||||
def __init__(self, data=None, ctrl=None):
|
def __init__(self, data=None, ctrl=None, tx_complete=None):
|
||||||
self.data = bytearray()
|
self.data = bytearray()
|
||||||
self.ctrl = None
|
self.ctrl = None
|
||||||
self.rx_sim_time = None
|
self.sim_time_start = None
|
||||||
self.rx_start_lane = None
|
self.sim_time_sfd = None
|
||||||
|
self.sim_time_end = None
|
||||||
|
self.start_lane = None
|
||||||
|
self.tx_complete = None
|
||||||
|
|
||||||
if type(data) is XgmiiFrame:
|
if type(data) is XgmiiFrame:
|
||||||
self.data = bytearray(data.data)
|
self.data = bytearray(data.data)
|
||||||
self.ctrl = data.ctrl
|
self.ctrl = data.ctrl
|
||||||
self.rx_sim_time = data.rx_sim_time
|
self.sim_time_start = data.sim_time_start
|
||||||
self.rx_start_lane = data.rx_start_lane
|
self.sim_time_sfd = data.sim_time_sfd
|
||||||
|
self.sim_time_end = data.sim_time_end
|
||||||
|
self.start_lane = data.start_lane
|
||||||
|
self.tx_complete = data.tx_complete
|
||||||
else:
|
else:
|
||||||
self.data = bytearray(data)
|
self.data = bytearray(data)
|
||||||
self.ctrl = ctrl
|
self.ctrl = ctrl
|
||||||
|
|
||||||
|
if tx_complete is not None:
|
||||||
|
self.tx_complete = tx_complete
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_payload(cls, payload, min_len=60):
|
def from_payload(cls, payload, min_len=60, tx_complete=None):
|
||||||
payload = bytearray(payload)
|
payload = bytearray(payload)
|
||||||
if len(payload) < min_len:
|
if len(payload) < min_len:
|
||||||
payload.extend(bytearray(min_len-len(payload)))
|
payload.extend(bytearray(min_len-len(payload)))
|
||||||
payload.extend(struct.pack('<L', zlib.crc32(payload)))
|
payload.extend(struct.pack('<L', zlib.crc32(payload)))
|
||||||
return cls.from_raw_payload(payload)
|
return cls.from_raw_payload(payload, tx_complete=tx_complete)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_raw_payload(cls, payload):
|
def from_raw_payload(cls, payload, tx_complete=None):
|
||||||
data = bytearray(ETH_PREAMBLE)
|
data = bytearray(ETH_PREAMBLE)
|
||||||
data.extend(payload)
|
data.extend(payload)
|
||||||
return cls(data)
|
return cls(data, tx_complete=tx_complete)
|
||||||
|
|
||||||
def get_preamble_len(self):
|
def get_preamble_len(self):
|
||||||
return self.data.index(EthPre.SFD)+1
|
return self.data.index(EthPre.SFD)+1
|
||||||
@@ -96,6 +105,12 @@ class XgmiiFrame:
|
|||||||
if not any(self.ctrl):
|
if not any(self.ctrl):
|
||||||
self.ctrl = None
|
self.ctrl = None
|
||||||
|
|
||||||
|
def handle_tx_complete(self):
|
||||||
|
if isinstance(self.tx_complete, Event):
|
||||||
|
self.tx_complete.set(self)
|
||||||
|
elif callable(self.tx_complete):
|
||||||
|
self.tx_complete(self)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if type(other) is XgmiiFrame:
|
if type(other) is XgmiiFrame:
|
||||||
return self.data == other.data
|
return self.data == other.data
|
||||||
@@ -104,8 +119,10 @@ class XgmiiFrame:
|
|||||||
return (
|
return (
|
||||||
f"{type(self).__name__}(data={self.data!r}, "
|
f"{type(self).__name__}(data={self.data!r}, "
|
||||||
f"ctrl={self.ctrl!r}, "
|
f"ctrl={self.ctrl!r}, "
|
||||||
f"rx_sim_time={self.rx_sim_time!r}, "
|
f"sim_time_start={self.sim_time_start!r}, "
|
||||||
f"rx_start_lane={self.rx_start_lane!r})"
|
f"sim_time_sfd={self.sim_time_sfd!r}, "
|
||||||
|
f"sim_time_end={self.sim_time_end!r}, "
|
||||||
|
f"start_lane={self.start_lane!r})"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
@@ -120,7 +137,7 @@ class XgmiiFrame:
|
|||||||
|
|
||||||
class XgmiiSource(Reset):
|
class XgmiiSource(Reset):
|
||||||
|
|
||||||
def __init__(self, data, ctrl, clock, reset=None, enable=None, *args, **kwargs):
|
def __init__(self, data, ctrl, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.ctrl = ctrl
|
self.ctrl = ctrl
|
||||||
@@ -136,7 +153,9 @@ class XgmiiSource(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
|
self.idle_event = Event()
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
self.enable_dic = True
|
self.enable_dic = True
|
||||||
self.ifg = 12
|
self.ifg = 12
|
||||||
@@ -162,29 +181,40 @@ class XgmiiSource(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def send(self, frame):
|
async def send(self, frame):
|
||||||
self.send_nowait(frame)
|
frame = XgmiiFrame(frame)
|
||||||
|
await self.queue.put(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
|
self.queue_occupancy_bytes += len(frame)
|
||||||
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
def send_nowait(self, frame):
|
def send_nowait(self, frame):
|
||||||
frame = XgmiiFrame(frame)
|
frame = XgmiiFrame(frame)
|
||||||
|
self.queue.put_nowait(frame)
|
||||||
|
self.idle_event.clear()
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
self.queue.append(frame)
|
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return self.empty() and not self.active
|
return self.empty() and not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.idle_event.set()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self):
|
async def wait(self):
|
||||||
while not self.idle():
|
await self.idle_event.wait()
|
||||||
await RisingEdge(self.clock)
|
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -221,13 +251,17 @@ class XgmiiSource(Reset):
|
|||||||
|
|
||||||
elif frame is None:
|
elif frame is None:
|
||||||
# idle
|
# idle
|
||||||
if self.queue:
|
if not self.queue.empty():
|
||||||
# send frame
|
# send frame
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
|
frame.sim_time_start = get_sim_time()
|
||||||
|
frame.sim_time_sfd = None
|
||||||
|
frame.sim_time_end = None
|
||||||
self.log.info("TX frame: %s", frame)
|
self.log.info("TX frame: %s", frame)
|
||||||
frame.normalize()
|
frame.normalize()
|
||||||
|
frame.start_lane = 0
|
||||||
assert frame.data[0] == EthPre.PRE
|
assert frame.data[0] == EthPre.PRE
|
||||||
assert frame.ctrl[0] == 0
|
assert frame.ctrl[0] == 0
|
||||||
frame.data[0] = XgmiiCtrl.START
|
frame.data[0] = XgmiiCtrl.START
|
||||||
@@ -243,6 +277,7 @@ class XgmiiSource(Reset):
|
|||||||
|
|
||||||
if self.byte_width > 4 and (ifg_cnt > min_ifg or self.force_offset_start):
|
if self.byte_width > 4 and (ifg_cnt > min_ifg or self.force_offset_start):
|
||||||
ifg_cnt = ifg_cnt-4
|
ifg_cnt = ifg_cnt-4
|
||||||
|
frame.start_lane = 4
|
||||||
frame.data = bytearray([XgmiiCtrl.IDLE]*4)+frame.data
|
frame.data = bytearray([XgmiiCtrl.IDLE]*4)+frame.data
|
||||||
frame.ctrl = [1]*4+frame.ctrl
|
frame.ctrl = [1]*4+frame.ctrl
|
||||||
|
|
||||||
@@ -261,11 +296,16 @@ class XgmiiSource(Reset):
|
|||||||
|
|
||||||
for k in range(self.byte_width):
|
for k in range(self.byte_width):
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
d_val |= frame.data.pop(0) << k*8
|
d = frame.data.pop(0)
|
||||||
|
if frame.sim_time_sfd is None and d == EthPre.SFD:
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
d_val |= d << k*8
|
||||||
c_val |= frame.ctrl.pop(0) << k
|
c_val |= frame.ctrl.pop(0) << k
|
||||||
|
|
||||||
if not frame.data:
|
if not frame.data:
|
||||||
ifg_cnt = max(self.ifg - (self.byte_width-k), 0)
|
ifg_cnt = max(self.ifg - (self.byte_width-k), 0)
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
|
frame.handle_tx_complete()
|
||||||
frame = None
|
frame = None
|
||||||
else:
|
else:
|
||||||
d_val |= XgmiiCtrl.IDLE << k*8
|
d_val |= XgmiiCtrl.IDLE << k*8
|
||||||
@@ -277,11 +317,12 @@ class XgmiiSource(Reset):
|
|||||||
self.data <= self.idle_d
|
self.data <= self.idle_d
|
||||||
self.ctrl <= self.idle_c
|
self.ctrl <= self.idle_c
|
||||||
self.active = False
|
self.active = False
|
||||||
|
self.idle_event.set()
|
||||||
|
|
||||||
|
|
||||||
class XgmiiSink(Reset):
|
class XgmiiSink(Reset):
|
||||||
|
|
||||||
def __init__(self, data, ctrl, clock, reset=None, enable=None, *args, **kwargs):
|
def __init__(self, data, ctrl, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.{data._path}")
|
self.log = logging.getLogger(f"cocotb.{data._path}")
|
||||||
self.data = data
|
self.data = data
|
||||||
self.ctrl = ctrl
|
self.ctrl = ctrl
|
||||||
@@ -297,8 +338,8 @@ class XgmiiSink(Reset):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
self.queue = deque()
|
self.queue = Queue()
|
||||||
self.sync = Event()
|
self.active_event = Event()
|
||||||
|
|
||||||
self.queue_occupancy_bytes = 0
|
self.queue_occupancy_bytes = 0
|
||||||
self.queue_occupancy_frames = 0
|
self.queue_occupancy_frames = 0
|
||||||
@@ -310,39 +351,49 @@ class XgmiiSink(Reset):
|
|||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self._init_reset(reset)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
async def recv(self, compact=True):
|
async def recv(self, compact=True):
|
||||||
while self.empty():
|
frame = await self.queue.get()
|
||||||
self.sync.clear()
|
if self.queue.empty():
|
||||||
await self.sync.wait()
|
self.active_event.clear()
|
||||||
return self.recv_nowait(compact)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
|
self.queue_occupancy_frames -= 1
|
||||||
|
return frame
|
||||||
|
|
||||||
def recv_nowait(self, compact=True):
|
def recv_nowait(self, compact=True):
|
||||||
if self.queue:
|
if not self.queue.empty():
|
||||||
frame = self.queue.popleft()
|
frame = self.queue.get_nowait()
|
||||||
|
if self.queue.empty():
|
||||||
|
self.active_event.clear()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
return frame
|
return frame
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.queue)
|
return self.queue.qsize()
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return not self.queue
|
return self.queue.empty()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
return not self.active
|
return not self.active
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
self.queue.get_nowait()
|
||||||
|
self.active_event.clear()
|
||||||
|
self.queue_occupancy_bytes = 0
|
||||||
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
async def wait(self, timeout=0, timeout_unit=None):
|
async def wait(self, timeout=0, timeout_unit=None):
|
||||||
if not self.empty():
|
if not self.empty():
|
||||||
return
|
return
|
||||||
self.sync.clear()
|
|
||||||
if timeout:
|
if timeout:
|
||||||
await First(self.sync.wait(), Timer(timeout, timeout_unit))
|
await First(self.active_event.wait(), Timer(timeout, timeout_unit))
|
||||||
else:
|
else:
|
||||||
await self.sync.wait()
|
await self.active_event.wait()
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
@@ -373,8 +424,8 @@ class XgmiiSink(Reset):
|
|||||||
if c_val and d_val == XgmiiCtrl.START:
|
if c_val and d_val == XgmiiCtrl.START:
|
||||||
# start
|
# start
|
||||||
frame = XgmiiFrame(bytearray([EthPre.PRE]), [0])
|
frame = XgmiiFrame(bytearray([EthPre.PRE]), [0])
|
||||||
frame.rx_sim_time = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
frame.rx_start_lane = offset
|
frame.start_lane = offset
|
||||||
else:
|
else:
|
||||||
if c_val:
|
if c_val:
|
||||||
# got a control character; terminate frame reception
|
# got a control character; terminate frame reception
|
||||||
@@ -384,15 +435,19 @@ class XgmiiSink(Reset):
|
|||||||
frame.ctrl.append(c_val)
|
frame.ctrl.append(c_val)
|
||||||
|
|
||||||
frame.compact()
|
frame.compact()
|
||||||
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
self.queue.append(frame)
|
self.queue.put_nowait(frame)
|
||||||
self.sync.set()
|
self.active_event.set()
|
||||||
|
|
||||||
frame = None
|
frame = None
|
||||||
else:
|
else:
|
||||||
|
if frame.sim_time_sfd is None and d_val == EthPre.SFD:
|
||||||
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
frame.data.append(d_val)
|
frame.data.append(d_val)
|
||||||
frame.ctrl.append(c_val)
|
frame.ctrl.append(c_val)
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ deps =
|
|||||||
cocotb-test
|
cocotb-test
|
||||||
coverage
|
coverage
|
||||||
pytest-cov
|
pytest-cov
|
||||||
git+https://github.com/cocotb/cocotb.git@e892a3ea48#egg=cocotb
|
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
pytest --cov=cocotbext --cov=tests --cov-branch -n auto
|
pytest --cov=cocotbext --cov=tests --cov-branch -n auto
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_gmii(request):
|
def test_gmii(request):
|
||||||
@@ -160,8 +159,8 @@ def test_gmii(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -154,7 +154,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_gmii_phy(request):
|
def test_gmii_phy(request):
|
||||||
@@ -170,8 +169,8 @@ def test_gmii_phy(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_mii(request):
|
def test_mii(request):
|
||||||
@@ -157,8 +156,8 @@ def test_mii(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_mii_phy(request):
|
def test_mii_phy(request):
|
||||||
@@ -161,8 +160,8 @@ def test_mii_phy(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -294,7 +294,6 @@ async def run_drift_adjustment(dut):
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_ptp_clock(request):
|
def test_ptp_clock(request):
|
||||||
@@ -310,8 +309,8 @@ def test_ptp_clock(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_rgmii(request):
|
def test_rgmii(request):
|
||||||
@@ -157,8 +156,8 @@ def test_rgmii(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
|||||||
MODULE = $(DUT)
|
MODULE = $(DUT)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_rgmii_phy(request):
|
def test_rgmii_phy(request):
|
||||||
@@ -174,8 +173,8 @@ def test_rgmii_phy(request):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ VERILOG_SOURCES += $(DUT).v
|
|||||||
export PARAM_DATA_WIDTH ?= 64
|
export PARAM_DATA_WIDTH ?= 64
|
||||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||||
|
|
||||||
SIM_BUILD ?= sim_build_$(MODULE)-$(PARAM_DATA_WIDTH)
|
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
@@ -58,6 +56,8 @@ else ifeq ($(SIM), verilator)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
iverilog_dump.v:
|
||||||
echo 'module iverilog_dump();' > $@
|
echo 'module iverilog_dump();' > $@
|
||||||
echo 'initial begin' >> $@
|
echo 'initial begin' >> $@
|
||||||
@@ -67,9 +67,5 @@ iverilog_dump.v:
|
|||||||
echo 'endmodule' >> $@
|
echo 'endmodule' >> $@
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@rm -rf sim_build_*
|
|
||||||
@rm -rf iverilog_dump.v
|
@rm -rf iverilog_dump.v
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
|
|||||||
assert rx_frame.check_fcs()
|
assert rx_frame.check_fcs()
|
||||||
assert rx_frame.ctrl is None
|
assert rx_frame.ctrl is None
|
||||||
|
|
||||||
start_lane.append(rx_frame.rx_start_lane)
|
start_lane.append(rx_frame.start_lane)
|
||||||
|
|
||||||
tb.log.info("length: %d", length)
|
tb.log.info("length: %d", length)
|
||||||
tb.log.info("start_lane: %s", start_lane)
|
tb.log.info("start_lane: %s", start_lane)
|
||||||
@@ -229,7 +229,6 @@ if cocotb.SIM_NAME:
|
|||||||
# cocotb-test
|
# cocotb-test
|
||||||
|
|
||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("data_width", [32, 64])
|
@pytest.mark.parametrize("data_width", [32, 64])
|
||||||
@@ -249,8 +248,8 @@ def test_xgmii(request, data_width):
|
|||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir,
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
request.node.name.replace('[', '-').replace(']', ''))
|
||||||
|
|
||||||
cocotb_test.simulator.run(
|
cocotb_test.simulator.run(
|
||||||
python_search=[tests_dir],
|
python_search=[tests_dir],
|
||||||
|
|||||||
Reference in New Issue
Block a user