14 Commits

Author SHA1 Message Date
Alex Forencich
50a60b649e Release v0.1.6 2021-03-06 18:38:18 -08:00
Alex Forencich
bf19679007 Update readme 2021-03-06 18:34:50 -08:00
Alex Forencich
6835e921f8 Add reset_active_level parameters 2021-03-06 18:34:20 -08:00
Alex Forencich
1b180a1d64 Clean up reset implementation 2021-03-06 18:31:40 -08:00
Alex Forencich
63b60341ed Clear sim time fields on start transmit 2021-01-08 16:25:28 -08:00
Alex Forencich
f92bbaaa70 Update readme 2021-01-05 23:16:00 -08:00
Alex Forencich
a73c0b734e Support override of tx_complete 2021-01-04 22:42:53 -08:00
Alex Forencich
aa97848450 Store SFD transfer sim time 2021-01-03 23:28:33 -08:00
Alex Forencich
1bd01ae879 Fix RGMII error indication 2021-01-03 23:26:31 -08:00
Alex Forencich
cfbc80c0cb Improve transfer tracking 2021-01-03 22:55:09 -08:00
Alex Forencich
1d5688778a Add clear method 2021-01-03 12:52:21 -08:00
Alex Forencich
71d7c7e9d2 Remove extraneous code 2020-12-31 03:12:14 -08:00
Alex Forencich
30bc6f68a1 Rework sim_build output directory, fix default makefile target 2020-12-29 14:25:52 -08:00
Alex Forencich
16eaea6967 Bump to dev version v0.1.5 2020-12-27 23:29:34 -08:00
24 changed files with 280 additions and 143 deletions

View File

@@ -3,6 +3,7 @@
[![Build Status](https://github.com/alexforencich/cocotbext-eth/workflows/Regression%20Tests/badge.svg?branch=master)](https://github.com/alexforencich/cocotbext-eth/actions/) [![Build Status](https://github.com/alexforencich/cocotbext-eth/workflows/Regression%20Tests/badge.svg?branch=master)](https://github.com/alexforencich/cocotbext-eth/actions/)
[![codecov](https://codecov.io/gh/alexforencich/cocotbext-eth/branch/master/graph/badge.svg)](https://codecov.io/gh/alexforencich/cocotbext-eth) [![codecov](https://codecov.io/gh/alexforencich/cocotbext-eth/branch/master/graph/badge.svg)](https://codecov.io/gh/alexforencich/cocotbext-eth)
[![PyPI version](https://badge.fury.io/py/cocotbext-eth.svg)](https://pypi.org/project/cocotbext-eth) [![PyPI version](https://badge.fury.io/py/cocotbext-eth.svg)](https://pypi.org/project/cocotbext-eth)
[![Downloads](https://pepy.tech/badge/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:

View File

@@ -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
@@ -156,7 +173,7 @@ 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) self.send_nowait(frame)
@@ -176,6 +193,11 @@ class GmiiSource(Reset):
def idle(self): def idle(self):
return self.empty() and not self.active 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): async def wait(self):
while not self.idle(): while not self.idle():
await RisingEdge(self.clock) await RisingEdge(self.clock)
@@ -215,6 +237,9 @@ class GmiiSource(Reset):
frame = self.queue.popleft() frame = self.queue.popleft()
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 +260,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
@@ -253,7 +283,7 @@ class GmiiSource(Reset):
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
@@ -290,7 +320,7 @@ 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(): while self.empty():
@@ -315,6 +345,11 @@ class GmiiSink(Reset):
def idle(self): def idle(self):
return not self.active 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): async def wait(self, timeout=0, timeout_unit=None):
if not self.empty(): if not self.empty():
return return
@@ -353,7 +388,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,6 +418,7 @@ 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)
@@ -394,13 +430,16 @@ class GmiiSink(Reset):
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 +447,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)

View File

@@ -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
@@ -74,7 +74,7 @@ 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) self.send_nowait(frame)
@@ -94,6 +94,11 @@ class MiiSource(Reset):
def idle(self): def idle(self):
return self.empty() and not self.active 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): async def wait(self):
while not self.idle(): while not self.idle():
await RisingEdge(self.clock) await RisingEdge(self.clock)
@@ -133,6 +138,9 @@ class MiiSource(Reset):
frame = self.queue.popleft() frame = self.queue.popleft()
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 +157,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
@@ -167,7 +180,7 @@ class MiiSource(Reset):
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
@@ -201,7 +214,7 @@ 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(): while self.empty():
@@ -226,6 +239,11 @@ class MiiSink(Reset):
def idle(self): def idle(self):
return not self.active 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): async def wait(self, timeout=0, timeout_unit=None):
if not self.empty(): if not self.empty():
return return
@@ -264,7 +282,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,6 +307,7 @@ 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)
@@ -300,19 +319,24 @@ class MiiSink(Reset):
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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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
@@ -72,7 +74,7 @@ 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) self.send_nowait(frame)
@@ -92,6 +94,11 @@ class RgmiiSource(Reset):
def idle(self): def idle(self):
return self.empty() and not self.active 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): async def wait(self):
while not self.idle(): while not self.idle():
await RisingEdge(self.clock) await RisingEdge(self.clock)
@@ -122,7 +129,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
@@ -137,6 +143,9 @@ class RgmiiSource(Reset):
frame = self.queue.popleft() frame = self.queue.popleft()
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 +156,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,8 +170,13 @@ 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
@@ -179,7 +193,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
@@ -212,7 +228,7 @@ 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(): while self.empty():
@@ -237,6 +253,11 @@ class RgmiiSink(Reset):
def idle(self): def idle(self):
return not self.active 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): async def wait(self, timeout=0, timeout_unit=None):
if not self.empty(): if not self.empty():
return return
@@ -285,7 +306,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,6 +336,7 @@ 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)
@@ -326,19 +348,24 @@ class RgmiiSink(Reset):
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)

View File

@@ -1 +1 @@
__version__ = "0.1.4" __version__ = "0.1.6"

View File

@@ -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
@@ -162,7 +179,7 @@ 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) self.send_nowait(frame)
@@ -182,6 +199,11 @@ class XgmiiSource(Reset):
def idle(self): def idle(self):
return self.empty() and not self.active 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): async def wait(self):
while not self.idle(): while not self.idle():
await RisingEdge(self.clock) await RisingEdge(self.clock)
@@ -226,8 +248,12 @@ class XgmiiSource(Reset):
frame = self.queue.popleft() frame = self.queue.popleft()
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 +269,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 +288,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
@@ -281,7 +313,7 @@ class XgmiiSource(Reset):
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
@@ -310,7 +342,7 @@ 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(): while self.empty():
@@ -335,6 +367,11 @@ class XgmiiSink(Reset):
def idle(self): def idle(self):
return not self.active 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): async def wait(self, timeout=0, timeout_unit=None):
if not self.empty(): if not self.empty():
return return
@@ -373,8 +410,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,6 +421,7 @@ 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)
@@ -394,5 +432,8 @@ class XgmiiSink(Reset):
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)

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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],