Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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://codecov.io/gh/alexforencich/cocotbext-eth)
|
||||
[](https://pypi.org/project/cocotbext-eth)
|
||||
[](https://pepy.tech/project/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)
|
||||
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.
|
||||
|
||||
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)
|
||||
* _enable_: clock enable (optional)
|
||||
* _mii_select_: MII mode select (optional)
|
||||
* _reset_active_level_: reset active level (optional, default `True`)
|
||||
|
||||
#### 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)
|
||||
* `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)
|
||||
* `clear()`: drop all data in queue (all)
|
||||
* `wait()`: wait for idle (source)
|
||||
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
||||
|
||||
@@ -118,7 +128,11 @@ Attributes:
|
||||
|
||||
* `data`: bytearray
|
||||
* `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:
|
||||
|
||||
@@ -151,6 +165,13 @@ To send data into a design with an `MiiSource`, call `send()` or `send_nowait()`
|
||||
# wait for operation to complete (optional)
|
||||
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.
|
||||
|
||||
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
|
||||
* _reset_: reset signal (optional)
|
||||
* _enable_: clock enable (optional)
|
||||
* _reset_active_level_: reset active level (optional, default `True`)
|
||||
|
||||
#### 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)
|
||||
* `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)
|
||||
* `clear()`: drop all data in queue (all)
|
||||
* `wait()`: wait for idle (source)
|
||||
* `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)
|
||||
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.
|
||||
|
||||
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)
|
||||
* _enable_: clock enable (optional)
|
||||
* _mii_select_: MII mode select (optional)
|
||||
* _reset_active_level_: reset active level (optional, default `True`)
|
||||
|
||||
#### 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)
|
||||
* `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)
|
||||
* `clear()`: drop all data in queue (all)
|
||||
* `wait()`: wait for idle (source)
|
||||
* `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)
|
||||
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.
|
||||
|
||||
data = await xgmii_sink.recv()
|
||||
@@ -327,6 +366,7 @@ To receive data with an `XgmiiSink`, call `recv()` or `recv_nowait()`. Optional
|
||||
* _clock_: clock signal
|
||||
* _reset_: reset signal (optional)
|
||||
* _enable_: clock enable (optional)
|
||||
* _reset_active_level_: reset active level (optional, default `True`)
|
||||
|
||||
#### 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)
|
||||
* `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)
|
||||
* `clear()`: drop all data in queue (all)
|
||||
* `wait()`: wait for idle (source)
|
||||
* `wait(timeout=0, timeout_unit='ns')`: wait for frame received (sink)
|
||||
|
||||
@@ -379,8 +420,11 @@ Attributes:
|
||||
|
||||
* `data`: bytearray
|
||||
* `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.
|
||||
* `rx_start_lane`: byte lane that the frame start control character was received in.
|
||||
* `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:
|
||||
|
||||
@@ -429,7 +473,8 @@ Once the clock is instantiated, it will generate a continuous stream of monotoni
|
||||
* _pps_: pulse-per-second signal (optional)
|
||||
* _clock_: clock
|
||||
* _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:
|
||||
|
||||
|
||||
@@ -37,32 +37,41 @@ from .reset import Reset
|
||||
|
||||
|
||||
class GmiiFrame:
|
||||
def __init__(self, data=None, error=None):
|
||||
def __init__(self, data=None, error=None, tx_complete=None):
|
||||
self.data = bytearray()
|
||||
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:
|
||||
self.data = bytearray(data.data)
|
||||
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:
|
||||
self.data = bytearray(data)
|
||||
self.error = error
|
||||
|
||||
if tx_complete is not None:
|
||||
self.tx_complete = tx_complete
|
||||
|
||||
@classmethod
|
||||
def from_payload(cls, payload, min_len=60):
|
||||
def from_payload(cls, payload, min_len=60, tx_complete=None):
|
||||
payload = bytearray(payload)
|
||||
if len(payload) < min_len:
|
||||
payload.extend(bytearray(min_len-len(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
|
||||
def from_raw_payload(cls, payload):
|
||||
def from_raw_payload(cls, payload, tx_complete=None):
|
||||
data = bytearray(ETH_PREAMBLE)
|
||||
data.extend(payload)
|
||||
return cls(data)
|
||||
return cls(data, tx_complete=tx_complete)
|
||||
|
||||
def get_preamble_len(self):
|
||||
return self.data.index(EthPre.SFD)+1
|
||||
@@ -94,6 +103,12 @@ class GmiiFrame:
|
||||
if not any(self.error):
|
||||
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):
|
||||
if type(other) is GmiiFrame:
|
||||
return self.data == other.data
|
||||
@@ -102,7 +117,9 @@ class GmiiFrame:
|
||||
return (
|
||||
f"{type(self).__name__}(data={self.data!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):
|
||||
@@ -117,7 +134,7 @@ class GmiiFrame:
|
||||
|
||||
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.data = data
|
||||
self.er = er
|
||||
@@ -156,7 +173,7 @@ class GmiiSource(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def send(self, frame):
|
||||
self.send_nowait(frame)
|
||||
@@ -176,6 +193,11 @@ class GmiiSource(Reset):
|
||||
def idle(self):
|
||||
return self.empty() and not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self):
|
||||
while not self.idle():
|
||||
await RisingEdge(self.clock)
|
||||
@@ -215,6 +237,9 @@ class GmiiSource(Reset):
|
||||
frame = self.queue.popleft()
|
||||
self.queue_occupancy_bytes -= len(frame)
|
||||
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)
|
||||
frame.normalize()
|
||||
|
||||
@@ -235,13 +260,18 @@ class GmiiSource(Reset):
|
||||
self.active = True
|
||||
|
||||
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:
|
||||
self.er <= frame.error.pop(0)
|
||||
self.dv <= 1
|
||||
|
||||
if not frame.data:
|
||||
ifg_cnt = max(self.ifg, 1)
|
||||
frame.sim_time_end = get_sim_time()
|
||||
frame.handle_tx_complete()
|
||||
frame = None
|
||||
else:
|
||||
self.data <= 0
|
||||
@@ -253,7 +283,7 @@ class GmiiSource(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.data = data
|
||||
self.er = er
|
||||
@@ -290,7 +320,7 @@ class GmiiSink(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def recv(self, compact=True):
|
||||
while self.empty():
|
||||
@@ -315,6 +345,11 @@ class GmiiSink(Reset):
|
||||
def idle(self):
|
||||
return not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self, timeout=0, timeout_unit=None):
|
||||
if not self.empty():
|
||||
return
|
||||
@@ -353,7 +388,7 @@ class GmiiSink(Reset):
|
||||
if dv_val:
|
||||
# start of frame
|
||||
frame = GmiiFrame(bytearray(), [])
|
||||
frame.rx_sim_time = get_sim_time()
|
||||
frame.sim_time_start = get_sim_time()
|
||||
else:
|
||||
if not dv_val:
|
||||
# end of frame
|
||||
@@ -383,6 +418,7 @@ class GmiiSink(Reset):
|
||||
frame.error = error
|
||||
|
||||
frame.compact()
|
||||
frame.sim_time_end = get_sim_time()
|
||||
self.log.info("RX frame: %s", frame)
|
||||
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
@@ -394,13 +430,16 @@ class GmiiSink(Reset):
|
||||
frame = 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.error.append(er_val)
|
||||
|
||||
|
||||
class GmiiPhy:
|
||||
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.tx_clk = tx_clk
|
||||
@@ -408,8 +447,8 @@ class GmiiPhy:
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.tx = GmiiSink(txd, tx_er, tx_en, tx_clk, reset)
|
||||
self.rx = GmiiSource(rxd, rx_er, rx_dv, rx_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_active_level=reset_active_level)
|
||||
|
||||
self.rx_clk.setimmediatevalue(0)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ from .reset import 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.data = data
|
||||
self.er = er
|
||||
@@ -74,7 +74,7 @@ class MiiSource(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def send(self, frame):
|
||||
self.send_nowait(frame)
|
||||
@@ -94,6 +94,11 @@ class MiiSource(Reset):
|
||||
def idle(self):
|
||||
return self.empty() and not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self):
|
||||
while not self.idle():
|
||||
await RisingEdge(self.clock)
|
||||
@@ -133,6 +138,9 @@ class MiiSource(Reset):
|
||||
frame = self.queue.popleft()
|
||||
self.queue_occupancy_bytes -= len(frame)
|
||||
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)
|
||||
frame.normalize()
|
||||
|
||||
@@ -149,13 +157,18 @@ class MiiSource(Reset):
|
||||
self.active = True
|
||||
|
||||
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:
|
||||
self.er <= frame.error.pop(0)
|
||||
self.dv <= 1
|
||||
|
||||
if not frame.data:
|
||||
ifg_cnt = max(self.ifg, 1)
|
||||
frame.sim_time_end = get_sim_time()
|
||||
frame.handle_tx_complete()
|
||||
frame = None
|
||||
else:
|
||||
self.data <= 0
|
||||
@@ -167,7 +180,7 @@ class MiiSource(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.data = data
|
||||
self.er = er
|
||||
@@ -201,7 +214,7 @@ class MiiSink(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def recv(self, compact=True):
|
||||
while self.empty():
|
||||
@@ -226,6 +239,11 @@ class MiiSink(Reset):
|
||||
def idle(self):
|
||||
return not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self, timeout=0, timeout_unit=None):
|
||||
if not self.empty():
|
||||
return
|
||||
@@ -264,7 +282,7 @@ class MiiSink(Reset):
|
||||
if dv_val:
|
||||
# start of frame
|
||||
frame = GmiiFrame(bytearray(), [])
|
||||
frame.rx_sim_time = get_sim_time()
|
||||
frame.sim_time_start = get_sim_time()
|
||||
else:
|
||||
if not dv_val:
|
||||
# end of frame
|
||||
@@ -289,6 +307,7 @@ class MiiSink(Reset):
|
||||
frame.error = error
|
||||
|
||||
frame.compact()
|
||||
frame.sim_time_end = get_sim_time()
|
||||
self.log.info("RX frame: %s", frame)
|
||||
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
@@ -300,19 +319,24 @@ class MiiSink(Reset):
|
||||
frame = 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.error.append(er_val)
|
||||
|
||||
|
||||
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.rx_clk = rx_clk
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.tx = MiiSink(txd, tx_er, tx_en, tx_clk, reset)
|
||||
self.rx = MiiSource(rxd, rx_er, rx_dv, rx_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, reset_active_level=reset_active_level)
|
||||
|
||||
self.tx_clk.setimmediatevalue(0)
|
||||
self.rx_clk.setimmediatevalue(0)
|
||||
|
||||
@@ -43,6 +43,7 @@ class PtpClock(Reset):
|
||||
pps=None,
|
||||
clock=None,
|
||||
reset=None,
|
||||
reset_active_level=True,
|
||||
period_ns=6.4,
|
||||
*args, **kwargs):
|
||||
|
||||
@@ -90,7 +91,7 @@ class PtpClock(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
def set_period(self, ns, fns):
|
||||
self.period_ns = int(ns)
|
||||
|
||||
@@ -27,13 +27,13 @@ from cocotb.triggers import RisingEdge, FallingEdge
|
||||
|
||||
|
||||
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._ext_reset = False
|
||||
self._reset_state = True
|
||||
|
||||
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()
|
||||
|
||||
@@ -42,7 +42,7 @@ class Reset:
|
||||
self.assert_reset(True)
|
||||
self.assert_reset(False)
|
||||
else:
|
||||
self._local_reset = val
|
||||
self._local_reset = bool(val)
|
||||
self._update_reset()
|
||||
|
||||
def _update_reset(self):
|
||||
@@ -54,13 +54,13 @@ class Reset:
|
||||
def _handle_reset(self, state):
|
||||
pass
|
||||
|
||||
async def _run_reset(self, reset_signal, active_high):
|
||||
async def _run_reset(self, reset_signal, active_level):
|
||||
while True:
|
||||
if bool(reset_signal.value):
|
||||
await FallingEdge(reset_signal)
|
||||
self._ext_reset = not active_high
|
||||
self._ext_reset = not active_level
|
||||
self._update_reset()
|
||||
else:
|
||||
await RisingEdge(reset_signal)
|
||||
self._ext_reset = active_high
|
||||
self._ext_reset = active_level
|
||||
self._update_reset()
|
||||
|
||||
@@ -37,7 +37,9 @@ from .reset import 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.data = data
|
||||
self.ctrl = ctrl
|
||||
@@ -72,7 +74,7 @@ class RgmiiSource(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def send(self, frame):
|
||||
self.send_nowait(frame)
|
||||
@@ -92,6 +94,11 @@ class RgmiiSource(Reset):
|
||||
def idle(self):
|
||||
return self.empty() and not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self):
|
||||
while not self.idle():
|
||||
await RisingEdge(self.clock)
|
||||
@@ -122,10 +129,9 @@ class RgmiiSource(Reset):
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
|
||||
if not self.mii_mode:
|
||||
# send high nibble after rising edge, leading in to falling edge
|
||||
self.data <= d >> 4
|
||||
self.ctrl <= en ^ er
|
||||
# send high nibble after rising edge, leading in to falling edge
|
||||
self.data <= d >> 4
|
||||
self.ctrl <= en ^ er
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
if ifg_cnt > 0:
|
||||
@@ -137,6 +143,9 @@ class RgmiiSource(Reset):
|
||||
frame = self.queue.popleft()
|
||||
self.queue_occupancy_bytes -= len(frame)
|
||||
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)
|
||||
frame.normalize()
|
||||
|
||||
@@ -147,8 +156,8 @@ class RgmiiSource(Reset):
|
||||
mii_data = []
|
||||
mii_error = []
|
||||
for b, e in zip(frame.data, frame.error):
|
||||
mii_data.append(b & 0x0F)
|
||||
mii_data.append(b >> 4)
|
||||
mii_data.append((b & 0x0F)*0x11)
|
||||
mii_data.append((b >> 4)*0x11)
|
||||
mii_error.append(e)
|
||||
mii_error.append(e)
|
||||
frame.data = mii_data
|
||||
@@ -161,8 +170,13 @@ class RgmiiSource(Reset):
|
||||
er = frame.error.pop(0)
|
||||
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:
|
||||
ifg_cnt = max(self.ifg, 1)
|
||||
frame.sim_time_end = get_sim_time()
|
||||
frame.handle_tx_complete()
|
||||
frame = None
|
||||
else:
|
||||
d = 0
|
||||
@@ -179,7 +193,9 @@ class RgmiiSource(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.data = data
|
||||
self.ctrl = ctrl
|
||||
@@ -212,7 +228,7 @@ class RgmiiSink(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def recv(self, compact=True):
|
||||
while self.empty():
|
||||
@@ -237,6 +253,11 @@ class RgmiiSink(Reset):
|
||||
def idle(self):
|
||||
return not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self, timeout=0, timeout_unit=None):
|
||||
if not self.empty():
|
||||
return
|
||||
@@ -285,7 +306,7 @@ class RgmiiSink(Reset):
|
||||
if dv_val:
|
||||
# start of frame
|
||||
frame = GmiiFrame(bytearray(), [])
|
||||
frame.rx_sim_time = get_sim_time()
|
||||
frame.sim_time_start = get_sim_time()
|
||||
else:
|
||||
if not dv_val:
|
||||
# end of frame
|
||||
@@ -315,6 +336,7 @@ class RgmiiSink(Reset):
|
||||
frame.error = error
|
||||
|
||||
frame.compact()
|
||||
frame.sim_time_end = get_sim_time()
|
||||
self.log.info("RX frame: %s", frame)
|
||||
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
@@ -326,19 +348,24 @@ class RgmiiSink(Reset):
|
||||
frame = 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.error.append(er_val)
|
||||
|
||||
|
||||
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.rx_clk = rx_clk
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.tx = RgmiiSink(txd, tx_ctl, tx_clk, reset)
|
||||
self.rx = RgmiiSource(rxd, rx_ctl, rx_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, reset_active_level=reset_active_level)
|
||||
|
||||
self.rx_clk.setimmediatevalue(0)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.4"
|
||||
__version__ = "0.1.6"
|
||||
|
||||
@@ -37,34 +37,43 @@ from .reset import Reset
|
||||
|
||||
|
||||
class XgmiiFrame:
|
||||
def __init__(self, data=None, ctrl=None):
|
||||
def __init__(self, data=None, ctrl=None, tx_complete=None):
|
||||
self.data = bytearray()
|
||||
self.ctrl = None
|
||||
self.rx_sim_time = None
|
||||
self.rx_start_lane = None
|
||||
self.sim_time_start = None
|
||||
self.sim_time_sfd = None
|
||||
self.sim_time_end = None
|
||||
self.start_lane = None
|
||||
self.tx_complete = None
|
||||
|
||||
if type(data) is XgmiiFrame:
|
||||
self.data = bytearray(data.data)
|
||||
self.ctrl = data.ctrl
|
||||
self.rx_sim_time = data.rx_sim_time
|
||||
self.rx_start_lane = data.rx_start_lane
|
||||
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.start_lane = data.start_lane
|
||||
self.tx_complete = data.tx_complete
|
||||
else:
|
||||
self.data = bytearray(data)
|
||||
self.ctrl = ctrl
|
||||
|
||||
if tx_complete is not None:
|
||||
self.tx_complete = tx_complete
|
||||
|
||||
@classmethod
|
||||
def from_payload(cls, payload, min_len=60):
|
||||
def from_payload(cls, payload, min_len=60, tx_complete=None):
|
||||
payload = bytearray(payload)
|
||||
if len(payload) < min_len:
|
||||
payload.extend(bytearray(min_len-len(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
|
||||
def from_raw_payload(cls, payload):
|
||||
def from_raw_payload(cls, payload, tx_complete=None):
|
||||
data = bytearray(ETH_PREAMBLE)
|
||||
data.extend(payload)
|
||||
return cls(data)
|
||||
return cls(data, tx_complete=tx_complete)
|
||||
|
||||
def get_preamble_len(self):
|
||||
return self.data.index(EthPre.SFD)+1
|
||||
@@ -96,6 +105,12 @@ class XgmiiFrame:
|
||||
if not any(self.ctrl):
|
||||
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):
|
||||
if type(other) is XgmiiFrame:
|
||||
return self.data == other.data
|
||||
@@ -104,8 +119,10 @@ class XgmiiFrame:
|
||||
return (
|
||||
f"{type(self).__name__}(data={self.data!r}, "
|
||||
f"ctrl={self.ctrl!r}, "
|
||||
f"rx_sim_time={self.rx_sim_time!r}, "
|
||||
f"rx_start_lane={self.rx_start_lane!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}, "
|
||||
f"start_lane={self.start_lane!r})"
|
||||
)
|
||||
|
||||
def __len__(self):
|
||||
@@ -120,7 +137,7 @@ class XgmiiFrame:
|
||||
|
||||
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.data = data
|
||||
self.ctrl = ctrl
|
||||
@@ -162,7 +179,7 @@ class XgmiiSource(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def send(self, frame):
|
||||
self.send_nowait(frame)
|
||||
@@ -182,6 +199,11 @@ class XgmiiSource(Reset):
|
||||
def idle(self):
|
||||
return self.empty() and not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self):
|
||||
while not self.idle():
|
||||
await RisingEdge(self.clock)
|
||||
@@ -226,8 +248,12 @@ class XgmiiSource(Reset):
|
||||
frame = self.queue.popleft()
|
||||
self.queue_occupancy_bytes -= len(frame)
|
||||
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)
|
||||
frame.normalize()
|
||||
frame.start_lane = 0
|
||||
assert frame.data[0] == EthPre.PRE
|
||||
assert frame.ctrl[0] == 0
|
||||
frame.data[0] = XgmiiCtrl.START
|
||||
@@ -243,6 +269,7 @@ class XgmiiSource(Reset):
|
||||
|
||||
if self.byte_width > 4 and (ifg_cnt > min_ifg or self.force_offset_start):
|
||||
ifg_cnt = ifg_cnt-4
|
||||
frame.start_lane = 4
|
||||
frame.data = bytearray([XgmiiCtrl.IDLE]*4)+frame.data
|
||||
frame.ctrl = [1]*4+frame.ctrl
|
||||
|
||||
@@ -261,11 +288,16 @@ class XgmiiSource(Reset):
|
||||
|
||||
for k in range(self.byte_width):
|
||||
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
|
||||
|
||||
if not frame.data:
|
||||
ifg_cnt = max(self.ifg - (self.byte_width-k), 0)
|
||||
frame.sim_time_end = get_sim_time()
|
||||
frame.handle_tx_complete()
|
||||
frame = None
|
||||
else:
|
||||
d_val |= XgmiiCtrl.IDLE << k*8
|
||||
@@ -281,7 +313,7 @@ class XgmiiSource(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.data = data
|
||||
self.ctrl = ctrl
|
||||
@@ -310,7 +342,7 @@ class XgmiiSink(Reset):
|
||||
|
||||
self._run_cr = None
|
||||
|
||||
self._init_reset(reset)
|
||||
self._init_reset(reset, reset_active_level)
|
||||
|
||||
async def recv(self, compact=True):
|
||||
while self.empty():
|
||||
@@ -335,6 +367,11 @@ class XgmiiSink(Reset):
|
||||
def idle(self):
|
||||
return not self.active
|
||||
|
||||
def clear(self):
|
||||
self.queue.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
async def wait(self, timeout=0, timeout_unit=None):
|
||||
if not self.empty():
|
||||
return
|
||||
@@ -373,8 +410,8 @@ class XgmiiSink(Reset):
|
||||
if c_val and d_val == XgmiiCtrl.START:
|
||||
# start
|
||||
frame = XgmiiFrame(bytearray([EthPre.PRE]), [0])
|
||||
frame.rx_sim_time = get_sim_time()
|
||||
frame.rx_start_lane = offset
|
||||
frame.sim_time_start = get_sim_time()
|
||||
frame.start_lane = offset
|
||||
else:
|
||||
if c_val:
|
||||
# got a control character; terminate frame reception
|
||||
@@ -384,6 +421,7 @@ class XgmiiSink(Reset):
|
||||
frame.ctrl.append(c_val)
|
||||
|
||||
frame.compact()
|
||||
frame.sim_time_end = get_sim_time()
|
||||
self.log.info("RX frame: %s", frame)
|
||||
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
@@ -394,5 +432,8 @@ class XgmiiSink(Reset):
|
||||
|
||||
frame = None
|
||||
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.ctrl.append(c_val)
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
|
||||
@@ -144,7 +144,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
|
||||
@@ -154,7 +154,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
|
||||
@@ -141,7 +141,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
|
||||
@@ -145,7 +145,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@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
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
|
||||
@@ -141,7 +141,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -31,8 +31,6 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -48,6 +46,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -57,9 +57,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
|
||||
@@ -158,7 +158,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
@@ -35,8 +35,6 @@ VERILOG_SOURCES += $(DUT).v
|
||||
export PARAM_DATA_WIDTH ?= 64
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)-$(PARAM_DATA_WIDTH)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
@@ -58,6 +56,8 @@ else ifeq ($(SIM), verilator)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
@@ -67,9 +67,5 @@ iverilog_dump.v:
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@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.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("start_lane: %s", start_lane)
|
||||
@@ -229,7 +229,6 @@ if cocotb.SIM_NAME:
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
@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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
|
||||
Reference in New Issue
Block a user