32 Commits

Author SHA1 Message Date
Alex Forencich
2bfe8a0e50 Release v0.1.20
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-25 18:51:37 -08:00
Alex Forencich
9c88b0440e Update package versions
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-25 18:28:32 -08:00
Alex Forencich
37b23c358b Put sources and sinks to sleep when idle
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-24 17:42:05 -08:00
Alex Forencich
dd35d734f9 Put sources and sinks to sleep based on clock enables
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-24 17:41:36 -08:00
Alex Forencich
45ee1193cb Remove deprecated assignments
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-24 17:41:12 -08:00
Alex Forencich
5caafbb9e7 Update package versions
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-24 12:54:08 -08:00
Alex Forencich
2d4450e048 Fix path issue so latest coverage works
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-20 20:58:13 -08:00
Alex Forencich
c5d28182c4 Update github actions versions
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-20 15:38:05 -08:00
Alex Forencich
79991205b5 Fix tox config and lock package versions
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-20 15:37:51 -08:00
Alex Forencich
a22123649c Python 3.6 is EOL; remove from CI tests
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-18 17:53:30 -08:00
Alex Forencich
b5ba332ecc Specify min package versions 2021-12-27 17:17:40 -08:00
Alex Forencich
eb62e43fd1 Specify min tox and venv versions 2021-12-27 17:17:15 -08:00
Alex Forencich
5c4ef258ac Skip missing interpreters 2021-12-27 17:16:20 -08:00
Alex Forencich
6c5845fad3 Test on Python 3.10 2021-12-27 17:15:11 -08:00
Alex Forencich
32f6e449c0 Use start_soon instead of fork 2021-12-08 21:45:58 -08:00
Alex Forencich
2af7852006 Cache clock edge event objects 2021-12-03 19:06:43 -08:00
Alex Forencich
3325568406 Bump to dev version 2021-11-07 13:19:10 -08:00
Alex Forencich
6a35c31b4b Release v0.1.18 2021-11-07 12:40:42 -08:00
Alex Forencich
73fe54705f Remove deprecated assignments 2021-11-07 01:21:39 -08:00
Alex Forencich
448451e274 Add BASE-R related mappings 2021-10-15 02:00:03 -07:00
Alex Forencich
21c2c05c57 Normalize names 2021-10-14 19:13:51 -07:00
Alex Forencich
ab84a3b100 Add cocotb framework classifier 2021-08-31 14:43:03 -07:00
Alex Forencich
0f3060b9ba Bump to dev version 2021-08-31 01:04:20 -07:00
Alex Forencich
2ee51890f5 Release v0.1.16 2021-08-31 00:24:16 -07:00
Alex Forencich
fbdf4149b3 Update readme 2021-08-31 00:23:36 -07:00
Alex Forencich
03156ff759 Support PTP timestamp tags in MAC model 2021-08-31 00:23:07 -07:00
Alex Forencich
8956de42b5 Bump to dev version 2021-04-12 18:43:42 -07:00
Alex Forencich
de18e62024 Release v0.1.14 2021-04-12 18:18:26 -07:00
Alex Forencich
14738c1dae Print model configuration 2021-04-12 18:18:01 -07:00
Alex Forencich
3d43812c7b Rename byte_width to byte_lanes 2021-04-12 15:17:02 -07:00
Alex Forencich
008d903bb9 Send data without using pop 2021-04-12 15:10:52 -07:00
Alex Forencich
0bd66da868 Bump to dev version 2021-04-01 15:29:03 -07:00
23 changed files with 506 additions and 250 deletions

View File

@@ -9,13 +9,13 @@ jobs:
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ["3.7", "3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

View File

@@ -467,6 +467,7 @@ To use these modules, import the one you need and connect it to the DUT:
tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
tx_ptp_time=dut.tx_ptp_time,
tx_ptp_ts=dut.tx_ptp_ts,
tx_ptp_ts_tag=dut.tx_ptp_ts_tag,
tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
rx_clk=dut.rx_clk,
rx_rst=dut.rx_rst,
@@ -492,6 +493,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
data = await mac.tx.recv()
PTP timestamping requires free-running PTP clocks driving the PTP time inputs, synchronous with the corresponding MAC clocks. The values of these fields are then captured when the frame SFD is transferred and returned either on tuser (for received frames) or on a separate streaming interface (for transmitted frames). Additionally, on the transmit path, a tag value from tuser is returned along with the timestamp.
#### Signals
* `tdata`: payload data, must be a multiple of 8 bits
@@ -499,9 +502,10 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
* `tready`: indicates sink is ready for data (tx only)
* `tlast`: marks the last cycle of a frame
* `tkeep`: qualifies data byte, data bus width must be evenly divisible by `tkeep` signal width
* `tuser`: user data, carries frame error mark and captured receive PTP timestamp
* `ptp_time`: PTP time input from PHC, captured into `ptp_timestamp` field coincident with transfer of frame SFD and output on `ptp_ts`
* `tuser`: user data, carries frame error mark and captured receive PTP timestamp (RX) or PTP timestamp tag (TX)
* `ptp_time`: PTP time input from PHC, captured into `ptp_timestamp` field coincident with transfer of frame SFD and output on `ptp_ts` (TX) or `tuser` (RX)
* `ptp_ts`: captured transmit PTP timestamp
* `ptp_ts_tag`: captured transmit PTP timestamp tag
* `ptp_ts_valid`: qualifies captured transmit PTP timestamp
#### Constructor parameters (`EthMacRx` and `EthMacTx`):
@@ -510,7 +514,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
* _clock_: clock signal
* _reset_: reset signal (optional)
* _ptp_time_: PTP time input from PHC (optional)
* _ptp_ts_: PTP timestamp output (optional) (tx)
* _ptp_ts_: PTP timestamp (optional) (tx)
* _ptp_ts_tag_: PTP timestamp tag (optional) (tx)
* _ptp_ts_valid_: PTP timestamp valid (optional) (tx)
* _reset_active_level_: reset active level (optional, default `True`)
* _ifg_: IFG size in byte times (optional, default `12`)
@@ -522,7 +527,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
* _tx_clk_: transmit clock
* _tx_rst_: transmit reset (optional)
* _tx_ptp_time_: transmit PTP time input from PHC (optional)
* _tx_ptp_ts_: transmit PTP timestamp output (optional)
* _tx_ptp_ts_: transmit PTP timestamp (optional)
* _tx_ptp_ts_tag_: transmit PTP timestamp tag (optional)
* _tx_ptp_ts_valid_: transmit PTP timestamp valid (optional)
* _rx_bus_: `AxiStreamBus` object containing receive AXI stream interface signals
* _rx_clk_: receive clock
@@ -565,6 +571,7 @@ Attributes:
* `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.
* `ptp_tag`: PTP timestamp tag for transmitted frames.
* `ptp_timestamp`: captured value of `ptp_time` at frame SFD
* `tx_complete`: event or callable triggered when frame is transmitted.

View File

@@ -42,12 +42,12 @@ class XgmiiCtrl(enum.IntEnum):
TERM = 0xfd
ERROR = 0xfe
SEQ_OS = 0x9c
RES0 = 0x1c
RES1 = 0x3c
RES2 = 0x7c
RES3 = 0xbc
RES4 = 0xdc
RES5 = 0xf7
RES_0 = 0x1c
RES_1 = 0x3c
RES_2 = 0x7c
RES_3 = 0xbc
RES_4 = 0xdc
RES_5 = 0xf7
SIG_OS = 0x5c
@@ -93,3 +93,41 @@ class BaseRBlockType(enum.IntEnum):
TERM_5 = 0xd2 # C7 C6 D4 D3 D2 D1 D0 BT
TERM_6 = 0xe1 # C7 D5 D4 D3 D2 D1 D0 BT
TERM_7 = 0xff # D6 D5 D4 D3 D2 D1 D0 BT
xgmii_ctrl_to_baser_mapping = {
XgmiiCtrl.IDLE: BaseRCtrl.IDLE,
XgmiiCtrl.LPI: BaseRCtrl.LPI,
XgmiiCtrl.ERROR: BaseRCtrl.ERROR,
XgmiiCtrl.RES_0: BaseRCtrl.RES_0,
XgmiiCtrl.RES_1: BaseRCtrl.RES_1,
XgmiiCtrl.RES_2: BaseRCtrl.RES_2,
XgmiiCtrl.RES_3: BaseRCtrl.RES_3,
XgmiiCtrl.RES_4: BaseRCtrl.RES_4,
XgmiiCtrl.RES_5: BaseRCtrl.RES_5,
}
baser_ctrl_to_xgmii_mapping = {
BaseRCtrl.IDLE: XgmiiCtrl.IDLE,
BaseRCtrl.LPI: XgmiiCtrl.LPI,
BaseRCtrl.ERROR: XgmiiCtrl.ERROR,
BaseRCtrl.RES_0: XgmiiCtrl.RES_0,
BaseRCtrl.RES_1: XgmiiCtrl.RES_1,
BaseRCtrl.RES_2: XgmiiCtrl.RES_2,
BaseRCtrl.RES_3: XgmiiCtrl.RES_3,
BaseRCtrl.RES_4: XgmiiCtrl.RES_4,
BaseRCtrl.RES_5: XgmiiCtrl.RES_5,
}
block_type_term_lane_mapping = {
BaseRBlockType.TERM_0: 0,
BaseRBlockType.TERM_1: 1,
BaseRBlockType.TERM_2: 2,
BaseRBlockType.TERM_3: 3,
BaseRBlockType.TERM_4: 4,
BaseRBlockType.TERM_5: 5,
BaseRBlockType.TERM_6: 6,
BaseRBlockType.TERM_7: 7,
}

View File

@@ -49,6 +49,7 @@ class EthMacFrame:
self.sim_time_sfd = None
self.sim_time_end = None
self.ptp_timestamp = None
self.ptp_tag = None
self.tx_complete = None
if type(data) is EthMacFrame:
@@ -57,6 +58,7 @@ class EthMacFrame:
self.sim_time_sfd = data.sim_time_sfd
self.sim_time_end = data.sim_time_end
self.ptp_timestamp = data.ptp_timestamp
self.ptp_tag = data.ptp_tag
self.tx_complete = data.tx_complete
else:
self.data = bytearray(data)
@@ -104,7 +106,8 @@ class EthMacFrame:
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"ptp_timestamp={self.ptp_timestamp!r})"
f"ptp_timestamp={self.ptp_timestamp!r}, "
f"ptp_tag={self.ptp_tag!r})"
)
def __len__(self):
@@ -118,7 +121,7 @@ class EthMacFrame:
class EthMacTx(Reset):
def __init__(self, bus, clock, reset=None, ptp_time=None, ptp_ts=None, ptp_ts_valid=None,
def __init__(self, bus, clock, reset=None, ptp_time=None, ptp_ts=None, ptp_ts_tag=None, ptp_ts_valid=None,
reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
self.bus = bus
@@ -126,6 +129,7 @@ class EthMacTx(Reset):
self.reset = reset
self.ptp_time = ptp_time
self.ptp_ts = ptp_ts
self.ptp_ts_tag = ptp_ts_tag
self.ptp_ts_valid = ptp_ts_valid
self.ifg = ifg
self.speed = speed
@@ -172,6 +176,10 @@ class EthMacTx(Reset):
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
else:
self.log.info(" tuser: not present")
if self.ptp_time:
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
else:
self.log.info(" ptp_time: not present")
if self.bus.tready is None:
raise ValueError("tready is required")
@@ -185,6 +193,8 @@ class EthMacTx(Reset):
if self.ptp_ts:
self.ptp_ts.setimmediatevalue(0)
if self.ptp_ts_tag:
self.ptp_ts_tag.setimmediatevalue(0)
if self.ptp_ts_valid:
self.ptp_ts_valid.setimmediatevalue(0)
@@ -243,7 +253,7 @@ class EthMacTx(Reset):
self._run_ts_cr = None
if self.ptp_ts_valid:
self.ptp_ts_valid <= 0
self.ptp_ts_valid.value = 0
self.active = False
@@ -252,9 +262,9 @@ class EthMacTx(Reset):
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
if self._run_ts_cr is None and self.ptp_ts:
self._run_ts_cr = cocotb.fork(self._run_ts())
self._run_ts_cr = cocotb.start_soon(self._run_ts())
async def _run(self):
frame = None
@@ -274,7 +284,8 @@ class EthMacTx(Reset):
if self.ptp_time:
frame.ptp_timestamp = self.ptp_time.value.integer
self.ts_queue.put_nowait(frame.ptp_timestamp)
frame.ptp_tag = cycle.tuser.integer >> 1
self.ts_queue.put_nowait((frame.ptp_timestamp, frame.ptp_tag))
# process frame data
while True:
@@ -311,14 +322,18 @@ class EthMacTx(Reset):
await Timer(self.time_scale*self.ifg*8//self.speed, 'step')
async def _run_ts(self):
clock_edge_event = RisingEdge(self.clock)
while True:
await RisingEdge(self.clock)
self.ptp_ts_valid <= 0
await clock_edge_event
self.ptp_ts_valid.value = 0
if not self.ts_queue.empty():
ts = self.ts_queue.get_nowait()
self.ptp_ts <= ts
self.ptp_ts_valid <= 1
ts, tag = self.ts_queue.get_nowait()
self.ptp_ts.value = ts
if self.ptp_ts_tag is not None:
self.ptp_ts_tag.value = tag
self.ptp_ts_valid.value = 1
class EthMacRx(Reset):
@@ -378,6 +393,10 @@ class EthMacRx(Reset):
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
else:
self.log.info(" tuser: not present")
if self.ptp_time:
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
else:
self.log.info(" ptp_time: not present")
if self.byte_size != 8:
raise ValueError("Byte size must be 8")
@@ -458,10 +477,11 @@ class EthMacRx(Reset):
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
frame_offset = 0
tuser = 0
self.active = False
@@ -477,6 +497,7 @@ class EthMacRx(Reset):
frame.sim_time_sfd = None
frame.sim_time_end = None
self.log.info("TX frame: %s", frame)
frame_offset = 0
# wait for preamble time
await Timer(self.time_scale*8*8//self.speed, 'step')
@@ -499,11 +520,12 @@ class EthMacRx(Reset):
cycle.tuser = tuser
for offset in range(self.byte_lanes):
cycle.tdata |= (frame.data.pop(0) & self.byte_mask) << (offset * self.byte_size)
cycle.tdata |= (frame.data[frame_offset] & self.byte_mask) << (offset * self.byte_size)
cycle.tkeep |= 1 << offset
byte_count += 1
frame_offset += 1
if len(frame.data) == 0:
if frame_offset >= len(frame.data):
cycle.tlast = 1
frame.sim_time_end = get_sim_time()
frame.handle_tx_complete()
@@ -521,13 +543,13 @@ class EthMacRx(Reset):
class EthMac:
def __init__(self, tx_bus=None, tx_clk=None, tx_rst=None, tx_ptp_time=None, tx_ptp_ts=None, tx_ptp_ts_valid=None,
rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=None,
def __init__(self, tx_bus=None, tx_clk=None, tx_rst=None, tx_ptp_time=None, tx_ptp_ts=None, tx_ptp_ts_tag=None,
tx_ptp_ts_valid=None, rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=None,
reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tx = EthMacTx(tx_bus, tx_clk, tx_rst, tx_ptp_time, tx_ptp_ts, tx_ptp_ts_valid,
self.tx = EthMacTx(tx_bus, tx_clk, tx_rst, tx_ptp_time, tx_ptp_ts, tx_ptp_ts_tag, tx_ptp_ts_valid,
reset_active_level=reset_active_level, ifg=ifg, speed=speed)
self.rx = EthMacRx(rx_bus, rx_clk, rx_rst, rx_ptp_time,
reset_active_level=reset_active_level, ifg=ifg, speed=speed)

View File

@@ -157,6 +157,7 @@ class GmiiSource(Reset):
self.current_frame = None
self.idle_event = Event()
self.idle_event.set()
self.active_event = Event()
self.ifg = 12
self.mii_mode = False
@@ -189,6 +190,7 @@ class GmiiSource(Reset):
frame = GmiiFrame(frame)
await self.queue.put(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -198,6 +200,7 @@ class GmiiSource(Reset):
frame = GmiiFrame(frame)
self.queue.put_nowait(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -225,6 +228,7 @@ class GmiiSource(Reset):
frame.handle_tx_complete()
self.dequeue_event.set()
self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
@@ -239,10 +243,10 @@ class GmiiSource(Reset):
self._run_cr = None
self.active = False
self.data <= 0
self.data.value = 0
if self.er is not None:
self.er <= 0
self.dv <= 0
self.er.value = 0
self.dv.value = 0
if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -251,18 +255,28 @@ class GmiiSource(Reset):
if self.queue.empty():
self.idle_event.set()
self.active_event.clear()
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
frame_offset = 0
frame_data = None
frame_error = None
ifg_cnt = 0
self.active = False
clock_edge_event = RisingEdge(self.clock)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.enable is None or self.enable.value:
if ifg_cnt > 0:
@@ -286,40 +300,51 @@ class GmiiSource(Reset):
self.mii_mode = bool(self.mii_select.value.integer)
if self.mii_mode:
mii_data = []
mii_error = []
# convert to MII
frame_data = []
frame_error = []
for b, e in zip(frame.data, frame.error):
mii_data.append(b & 0x0F)
mii_data.append(b >> 4)
mii_error.append(e)
mii_error.append(e)
frame.data = mii_data
frame.error = mii_error
frame_data.append(b & 0x0F)
frame_data.append(b >> 4)
frame_error.append(e)
frame_error.append(e)
else:
frame_data = frame.data
frame_error = frame.error
self.active = True
frame_offset = 0
if frame is not None:
d = frame.data.pop(0)
d = frame_data[frame_offset]
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD):
frame.sim_time_sfd = get_sim_time()
self.data <= d
self.data.value = d
if self.er is not None:
self.er <= frame.error.pop(0)
self.dv <= 1
self.er.value = frame_error[frame_offset]
self.dv.value = 1
frame_offset += 1
if not frame.data:
if frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1)
frame.sim_time_end = get_sim_time()
frame.handle_tx_complete()
frame = None
self.current_frame = None
else:
self.data <= 0
self.data.value = 0
if self.er is not None:
self.er <= 0
self.dv <= 0
self.er.value = 0
self.dv.value = 0
self.active = False
if ifg_cnt == 0 and self.queue.empty():
self.idle_event.set()
self.active_event.clear()
await self.active_event.wait()
elif self.enable is not None and not self.enable.value:
await enable_event
class GmiiSink(Reset):
@@ -415,14 +440,22 @@ class GmiiSink(Reset):
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
self.active = False
clock_edge_event = RisingEdge(self.clock)
active_event = RisingEdge(self.dv)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.enable is None or self.enable.value:
d_val = self.data.value.integer
@@ -481,6 +514,12 @@ class GmiiSink(Reset):
frame.data.append(d_val)
frame.error.append(er_val)
if not dv_val:
await active_event
elif self.enable is not None and not self.enable.value:
await enable_event
class GmiiPhy:
def __init__(self, txd, tx_er, tx_en, tx_clk, gtx_clk, rxd, rx_er, rx_dv, rx_clk,
@@ -510,12 +549,12 @@ class GmiiPhy:
self._clock_cr.kill()
if self.speed == 1000e6:
self._clock_cr = cocotb.fork(self._run_clocks(8*1e9/self.speed))
self._clock_cr = cocotb.start_soon(self._run_clocks(8*1e9/self.speed))
self.tx.mii_mode = False
self.rx.mii_mode = False
self.tx.clock = self.gtx_clk
else:
self._clock_cr = cocotb.fork(self._run_clocks(4*1e9/self.speed))
self._clock_cr = cocotb.start_soon(self._run_clocks(4*1e9/self.speed))
self.tx.mii_mode = True
self.rx.mii_mode = True
self.tx.clock = self.tx_clk
@@ -529,8 +568,8 @@ class GmiiPhy:
while True:
await t
self.rx_clk <= 1
self.tx_clk <= 1
self.rx_clk.value = 1
self.tx_clk.value = 1
await t
self.rx_clk <= 0
self.tx_clk <= 0
self.rx_clk.value = 0
self.tx_clk.value = 0

View File

@@ -59,6 +59,7 @@ class MiiSource(Reset):
self.current_frame = None
self.idle_event = Event()
self.idle_event.set()
self.active_event = Event()
self.ifg = 12
@@ -90,6 +91,7 @@ class MiiSource(Reset):
frame = GmiiFrame(frame)
await self.queue.put(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -99,6 +101,7 @@ class MiiSource(Reset):
frame = GmiiFrame(frame)
self.queue.put_nowait(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -126,6 +129,7 @@ class MiiSource(Reset):
frame.handle_tx_complete()
self.dequeue_event.set()
self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
@@ -140,10 +144,10 @@ class MiiSource(Reset):
self._run_cr = None
self.active = False
self.data <= 0
self.data.value = 0
if self.er is not None:
self.er <= 0
self.dv <= 0
self.er.value = 0
self.dv.value = 0
if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -152,18 +156,28 @@ class MiiSource(Reset):
if self.queue.empty():
self.idle_event.set()
self.active_event.clear()
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
frame_offset = 0
frame_data = None
frame_error = None
ifg_cnt = 0
self.active = False
clock_edge_event = RisingEdge(self.clock)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.enable is None or self.enable.value:
if ifg_cnt > 0:
@@ -183,40 +197,48 @@ class MiiSource(Reset):
self.log.info("TX frame: %s", frame)
frame.normalize()
mii_data = []
mii_error = []
# convert to MII
frame_data = []
frame_error = []
for b, e in zip(frame.data, frame.error):
mii_data.append(b & 0x0F)
mii_data.append(b >> 4)
mii_error.append(e)
mii_error.append(e)
frame.data = mii_data
frame.error = mii_error
frame_data.append(b & 0x0F)
frame_data.append(b >> 4)
frame_error.append(e)
frame_error.append(e)
self.active = True
frame_offset = 0
if frame is not None:
d = frame.data.pop(0)
d = frame_data[frame_offset]
if frame.sim_time_sfd is None and d == 0xD:
frame.sim_time_sfd = get_sim_time()
self.data <= d
self.data.value = d
if self.er is not None:
self.er <= frame.error.pop(0)
self.dv <= 1
self.er.value = frame_error[frame_offset]
self.dv.value = 1
frame_offset += 1
if not frame.data:
if frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1)
frame.sim_time_end = get_sim_time()
frame.handle_tx_complete()
frame = None
self.current_frame = None
else:
self.data <= 0
self.data.value = 0
if self.er is not None:
self.er <= 0
self.dv <= 0
self.er.value = 0
self.dv.value = 0
self.active = False
if ifg_cnt == 0 and self.queue.empty():
self.idle_event.set()
self.active_event.clear()
await self.active_event.wait()
elif self.enable is not None and not self.enable.value:
await enable_event
class MiiSink(Reset):
@@ -309,14 +331,22 @@ class MiiSink(Reset):
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
self.active = False
clock_edge_event = RisingEdge(self.clock)
active_event = RisingEdge(self.dv)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.enable is None or self.enable.value:
d_val = self.data.value.integer
@@ -370,6 +400,12 @@ class MiiSink(Reset):
frame.data.append(d_val)
frame.error.append(er_val)
if not dv_val:
await active_event
elif self.enable is not None and not self.enable.value:
await enable_event
class MiiPhy:
def __init__(self, txd, tx_er, tx_en, tx_clk, rxd, rx_er, rx_dv, rx_clk, reset=None,
@@ -398,7 +434,7 @@ class MiiPhy:
if self._clock_cr is not None:
self._clock_cr.kill()
self._clock_cr = cocotb.fork(self._run_clocks(4*1e9/self.speed))
self._clock_cr = cocotb.start_soon(self._run_clocks(4*1e9/self.speed))
async def _run_clocks(self, period):
half_period = get_sim_steps(period / 2.0, 'ns')
@@ -406,8 +442,8 @@ class MiiPhy:
while True:
await t
self.tx_clk <= 1
self.rx_clk <= 1
self.tx_clk.value = 1
self.rx_clk.value = 1
await t
self.tx_clk <= 0
self.rx_clk <= 0
self.tx_clk.value = 0
self.rx_clk.value = 0

View File

@@ -195,28 +195,30 @@ class PtpClock(Reset):
self.ts_64_fns = 0
self.drift_cnt = 0
if self.ts_96 is not None:
self.ts_96 <= 0
self.ts_96.value = 0
if self.ts_64 is not None:
self.ts_64 <= 0
self.ts_64.value = 0
if self.ts_step is not None:
self.ts_step <= 0
self.ts_step.value = 0
if self.pps is not None:
self.pps <= 0
self.pps.value = 0
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
clock_edge_event = RisingEdge(self.clock)
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.ts_step is not None:
self.ts_step <= self.ts_updated
self.ts_step.value = self.ts_updated
self.ts_updated = False
if self.pps is not None:
self.pps <= 0
self.pps.value = 0
# increment 96 bit timestamp
if self.ts_96 is not None or self.pps is not None:
@@ -229,13 +231,13 @@ class PtpClock(Reset):
self.ts_96_s += 1
t -= (1000000000 << 16)
if self.pps is not None:
self.pps <= 1
self.pps.value = 1
self.ts_96_fns = t & 0xffff
self.ts_96_ns = t >> 16
if self.ts_96 is not None:
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns)
self.ts_96.value = (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns)
# increment 64 bit timestamp
if self.ts_64 is not None:
@@ -247,7 +249,7 @@ class PtpClock(Reset):
self.ts_64_fns = t & 0xffff
self.ts_64_ns = t >> 16
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns
self.ts_64.value = (self.ts_64_ns << 16) | self.ts_64_fns
if self.drift_rate:
if self.drift_cnt > 0:
@@ -286,9 +288,9 @@ class PtpClockSimTime:
if self.ts_64 is not None:
self.ts_64.setimmediatevalue(0)
if self.pps is not None:
self.pps <= 0
self.pps.value = 0
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
def get_ts_96(self):
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
@@ -309,8 +311,10 @@ class PtpClockSimTime:
return self.get_ts_64()*1e-9
async def _run(self):
clock_edge_event = RisingEdge(self.clock)
while True:
await RisingEdge(self.clock)
await clock_edge_event
self.ts_64_fns, self.ts_64_ns = math.modf(get_sim_time('ns'))
@@ -321,12 +325,12 @@ class PtpClockSimTime:
self.ts_96_fns = self.ts_64_fns
if self.ts_96 is not None:
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
self.ts_96.value = (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
if self.ts_64 is not None:
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns
self.ts_64.value = (self.ts_64_ns << 16) | self.ts_64_fns
if self.pps is not None:
self.pps <= int(self.last_ts_96_s != self.ts_96_s)
self.pps.value = int(self.last_ts_96_s != self.ts_96_s)
self.last_ts_96_s = self.ts_96_s

View File

@@ -33,7 +33,7 @@ class Reset:
self._reset_state = True
if reset_signal is not None:
cocotb.fork(self._run_reset(reset_signal, bool(active_level)))
cocotb.start_soon(self._run_reset(reset_signal, bool(active_level)))
self._update_reset()

View File

@@ -61,6 +61,7 @@ class RgmiiSource(Reset):
self.current_frame = None
self.idle_event = Event()
self.idle_event.set()
self.active_event = Event()
self.ifg = 12
self.mii_mode = False
@@ -90,6 +91,7 @@ class RgmiiSource(Reset):
frame = GmiiFrame(frame)
await self.queue.put(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -99,6 +101,7 @@ class RgmiiSource(Reset):
frame = GmiiFrame(frame)
self.queue.put_nowait(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -126,6 +129,7 @@ class RgmiiSource(Reset):
frame.handle_tx_complete()
self.dequeue_event.set()
self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
@@ -140,8 +144,8 @@ class RgmiiSource(Reset):
self._run_cr = None
self.active = False
self.data <= 0
self.ctrl <= 0
self.data.value = 0
self.ctrl.value = 0
if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -150,30 +154,51 @@ class RgmiiSource(Reset):
if self.queue.empty():
self.idle_event.set()
self.active_event.clear()
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
frame_offset = 0
frame_data = None
frame_error = None
ifg_cnt = 0
in_ifg = False
self.active = False
d = 0
er = 0
en = 0
clock_rising_edge_event = RisingEdge(self.clock)
clock_falling_edge_event = FallingEdge(self.clock)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_falling_edge_event
# send low nibble after falling edge, leading in to rising edge
self.data.value = d & 0x0F
self.ctrl.value = en
await clock_rising_edge_event
# send high nibble after rising edge, leading in to falling edge
self.data <= d >> 4
self.ctrl <= en ^ er
self.data.value = d >> 4
self.ctrl.value = en ^ er
if self.enable is None or self.enable.value:
in_ifg = False
if ifg_cnt > 0:
# in IFG
ifg_cnt -= 1
in_ifg = True
elif frame is None and not self.queue.empty():
# send frame
@@ -192,28 +217,33 @@ class RgmiiSource(Reset):
self.mii_mode = bool(self.mii_select.value.integer)
if self.mii_mode:
mii_data = []
mii_error = []
# convert to MII
frame_data = []
frame_error = []
for b, e in zip(frame.data, frame.error):
mii_data.append((b & 0x0F)*0x11)
mii_data.append((b >> 4)*0x11)
mii_error.append(e)
mii_error.append(e)
frame.data = mii_data
frame.error = mii_error
frame_data.append((b & 0x0F)*0x11)
frame_data.append((b >> 4)*0x11)
frame_error.append(e)
frame_error.append(e)
else:
frame_data = frame.data
frame_error = frame.error
self.active = True
frame_offset = 0
if frame is not None:
d = frame.data.pop(0)
er = frame.error.pop(0)
d = frame_data[frame_offset]
er = frame_error[frame_offset]
en = 1
frame_offset += 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 frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1)
in_ifg = True
frame.sim_time_end = get_sim_time()
frame.handle_tx_complete()
frame = None
@@ -223,13 +253,14 @@ class RgmiiSource(Reset):
er = 0
en = 0
self.active = False
if not in_ifg and self.queue.empty():
self.idle_event.set()
self.active_event.clear()
await self.active_event.wait()
await FallingEdge(self.clock)
# send low nibble after falling edge, leading in to rising edge
self.data <= d & 0x0F
self.ctrl <= en
elif self.enable is not None and not self.enable.value:
await enable_event
class RgmiiSink(Reset):
@@ -323,7 +354,7 @@ class RgmiiSink(Reset):
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
@@ -332,21 +363,30 @@ class RgmiiSink(Reset):
dv_val = 0
er_val = 0
clock_rising_edge_event = RisingEdge(self.clock)
clock_falling_edge_event = FallingEdge(self.clock)
active_event = RisingEdge(self.ctrl)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_rising_edge_event
if self.enable is None or self.enable.value:
# capture low nibble on rising edge
d_val = self.data.value.integer
dv_val = self.ctrl.value.integer
await FallingEdge(self.clock)
await clock_falling_edge_event
# capture high nibble on falling edge
d_val |= self.data.value.integer << 4
er_val = dv_val ^ self.ctrl.value.integer
if self.enable is None or self.enable.value:
if frame is None:
if dv_val:
# start of frame
@@ -399,6 +439,12 @@ class RgmiiSink(Reset):
frame.data.append(d_val)
frame.error.append(er_val)
if not dv_val:
await active_event
elif self.enable is not None and not self.enable.value:
await enable_event
class RgmiiPhy:
def __init__(self, txd, tx_ctl, tx_clk, rxd, rx_ctl, rx_clk, reset=None,
@@ -427,11 +473,11 @@ class RgmiiPhy:
self._clock_cr.kill()
if self.speed == 1000e6:
self._clock_cr = cocotb.fork(self._run_clock(8*1e9/self.speed))
self._clock_cr = cocotb.start_soon(self._run_clock(8*1e9/self.speed))
self.tx.mii_mode = False
self.rx.mii_mode = False
else:
self._clock_cr = cocotb.fork(self._run_clock(4*1e9/self.speed))
self._clock_cr = cocotb.start_soon(self._run_clock(4*1e9/self.speed))
self.tx.mii_mode = True
self.rx.mii_mode = True
@@ -441,6 +487,6 @@ class RgmiiPhy:
while True:
await t
self.rx_clk <= 1
self.rx_clk.value = 1
await t
self.rx_clk <= 0
self.rx_clk.value = 0

View File

@@ -1 +1 @@
__version__ = "0.1.12"
__version__ = "0.1.20"

View File

@@ -28,7 +28,7 @@ import zlib
import cocotb
from cocotb.queue import Queue, QueueFull
from cocotb.triggers import RisingEdge, Timer, First, Event
from cocotb.triggers import Edge, RisingEdge, Timer, First, Event
from cocotb.utils import get_sim_time
from .version import __version__
@@ -158,6 +158,7 @@ class XgmiiSource(Reset):
self.current_frame = None
self.idle_event = Event()
self.idle_event.set()
self.active_event = Event()
self.enable_dic = True
self.ifg = 12
@@ -170,14 +171,19 @@ class XgmiiSource(Reset):
self.queue_occupancy_limit_frames = -1
self.width = len(self.data)
self.byte_width = len(self.ctrl)
self.byte_size = 8
self.byte_lanes = len(self.ctrl)
assert self.width == self.byte_width * 8
assert self.width == self.byte_lanes * self.byte_size
self.log.info("XGMII source model configuration")
self.log.info(" Byte size: %d bits", self.byte_size)
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
self.idle_d = 0
self.idle_c = 0
for k in range(self.byte_width):
for k in range(self.byte_lanes):
self.idle_d |= XgmiiCtrl.IDLE << k*8
self.idle_c |= 1 << k
@@ -195,6 +201,7 @@ class XgmiiSource(Reset):
frame = XgmiiFrame(frame)
await self.queue.put(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -204,6 +211,7 @@ class XgmiiSource(Reset):
frame = XgmiiFrame(frame)
self.queue.put_nowait(frame)
self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1
@@ -231,6 +239,7 @@ class XgmiiSource(Reset):
frame.handle_tx_complete()
self.dequeue_event.set()
self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
@@ -245,8 +254,8 @@ class XgmiiSource(Reset):
self._run_cr = None
self.active = False
self.data <= 0
self.ctrl <= 0
self.data.value = 0
self.ctrl.value = 0
if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -255,24 +264,32 @@ class XgmiiSource(Reset):
if self.queue.empty():
self.idle_event.set()
self.active_event.clear()
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
frame_offset = 0
ifg_cnt = 0
deficit_idle_cnt = 0
self.active = False
clock_edge_event = RisingEdge(self.clock)
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.enable is None or self.enable.value:
if ifg_cnt + deficit_idle_cnt > self.byte_width-1 or (not self.enable_dic and ifg_cnt > 4):
if ifg_cnt + deficit_idle_cnt > self.byte_lanes-1 or (not self.enable_dic and ifg_cnt > 4):
# in IFG
ifg_cnt = ifg_cnt - self.byte_width
ifg_cnt = ifg_cnt - self.byte_lanes
if ifg_cnt < 0:
if self.enable_dic:
deficit_idle_cnt = max(deficit_idle_cnt+ifg_cnt, 0)
@@ -306,7 +323,7 @@ class XgmiiSource(Reset):
else:
min_ifg = 0
if self.byte_width > 4 and (ifg_cnt > min_ifg or self.force_offset_start):
if self.byte_lanes > 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
@@ -316,6 +333,7 @@ class XgmiiSource(Reset):
deficit_idle_cnt = max(deficit_idle_cnt+ifg_cnt, 0)
ifg_cnt = 0
self.active = True
frame_offset = 0
else:
# clear counters
deficit_idle_cnt = 0
@@ -325,16 +343,17 @@ class XgmiiSource(Reset):
d_val = 0
c_val = 0
for k in range(self.byte_width):
for k in range(self.byte_lanes):
if frame is not None:
d = frame.data.pop(0)
d = frame.data[frame_offset]
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[frame_offset] << k
frame_offset += 1
if not frame.data:
ifg_cnt = max(self.ifg - (self.byte_width-k), 0)
if frame_offset >= len(frame.data):
ifg_cnt = max(self.ifg - (self.byte_lanes-k), 0)
frame.sim_time_end = get_sim_time()
frame.handle_tx_complete()
frame = None
@@ -343,13 +362,20 @@ class XgmiiSource(Reset):
d_val |= XgmiiCtrl.IDLE << k*8
c_val |= 1 << k
self.data <= d_val
self.ctrl <= c_val
self.data.value = d_val
self.ctrl.value = c_val
else:
self.data <= self.idle_d
self.ctrl <= self.idle_c
self.data.value = self.idle_d
self.ctrl.value = self.idle_c
self.active = False
if ifg_cnt == 0 and self.queue.empty():
self.idle_event.set()
self.active_event.clear()
await self.active_event.wait()
elif self.enable is not None and not self.enable.value:
await enable_event
class XgmiiSink(Reset):
@@ -377,9 +403,14 @@ class XgmiiSink(Reset):
self.queue_occupancy_frames = 0
self.width = len(self.data)
self.byte_width = len(self.ctrl)
self.byte_size = 8
self.byte_lanes = len(self.ctrl)
assert self.width == self.byte_width * 8
assert self.width == self.byte_lanes * self.byte_size
self.log.info("XGMII sink model configuration")
self.log.info(" Byte size: %d bits", self.byte_size)
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
self._run_cr = None
@@ -437,19 +468,32 @@ class XgmiiSink(Reset):
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
self._run_cr = cocotb.fork(self._run())
self._run_cr = cocotb.start_soon(self._run())
async def _run(self):
frame = None
self.active = False
clock_edge_event = RisingEdge(self.clock)
active_event = First(Edge(self.data), Edge(self.ctrl))
enable_event = None
if self.enable is not None:
enable_event = RisingEdge(self.enable)
idle_d = sum([XgmiiCtrl.IDLE << n*8 for n in range(self.byte_lanes)])
idle_c = 2**self.byte_lanes-1
while True:
await RisingEdge(self.clock)
await clock_edge_event
if self.enable is None or self.enable.value:
for offset in range(self.byte_width):
d_val = (self.data.value.integer >> (offset*8)) & 0xff
c_val = (self.ctrl.value.integer >> offset) & 1
data_val = self.data.value.integer
ctrl_val = self.ctrl.value.integer
for offset in range(self.byte_lanes):
d_val = (data_val >> (offset*8)) & 0xff
c_val = (ctrl_val >> offset) & 1
if frame is None:
if c_val and d_val == XgmiiCtrl.START:
@@ -482,3 +526,9 @@ class XgmiiSink(Reset):
frame.data.append(d_val)
frame.ctrl.append(c_val)
if data_val == idle_d and ctrl_val == idle_c:
await active_event
elif self.enable is not None and not self.enable.value:
await enable_event

View File

@@ -17,17 +17,18 @@ long-description-content-type = text/markdown
platforms = any
classifiers =
Development Status :: 3 - Alpha
Programming Language :: Python :: 3
Framework :: cocotb
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Programming Language :: Python :: 3
Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
[options]
packages = find_namespace:
python_requires = >=3.6
install_requires =
cocotb
cocotbext-axi
cocotb >= 1.6.0
cocotbext-axi >= 0.1.16
[options.extras_require]
test =
@@ -46,31 +47,39 @@ addopts =
# tox configuration
[tox:tox]
envlist = py36, py37, py38, py39
envlist = py37, py38, py39, py310
skip_missing_interpreters = true
minversion = 3.18.0
requires = virtualenv >= 16.1
[gh-actions]
python =
3.6: py36
3.7: py37
3.8: py38
3.9: py39
3.10: py310
[testenv]
setenv =
COVERAGE=1
usedevelop = True
deps =
pytest
pytest-xdist
cocotb-test
coverage
pytest-cov
pytest == 7.2.1
pytest-xdist == 3.1.0
cocotb == 1.7.2
cocotb-bus == 0.2.1
cocotb-test == 0.2.4
cocotbext-axi == 0.1.20
coverage == 7.0.5
pytest-cov == 4.0.0
commands =
pytest --cov=cocotbext --cov=tests --cov-branch -n auto
pytest --cov=cocotbext --cov=tests --cov-branch {posargs:-n auto --verbose}
bash -c 'find . -type f -name "\.coverage" | xargs coverage combine --append'
coverage report
whitelist_externals =
allowlist_externals =
bash
# combine if paths are different

View File

@@ -45,8 +45,8 @@ class TB:
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.fork(Clock(dut.tx_clk, 6.4, units="ns").start())
cocotb.fork(Clock(dut.rx_clk, 6.4, units="ns").start())
cocotb.start_soon(Clock(dut.tx_clk, 6.4, units="ns").start())
cocotb.start_soon(Clock(dut.rx_clk, 6.4, units="ns").start())
self.mac = EthMac(
tx_clk=dut.tx_clk,
@@ -54,6 +54,7 @@ class TB:
tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
tx_ptp_time=dut.tx_ptp_time,
tx_ptp_ts=dut.tx_ptp_ts,
tx_ptp_ts_tag=dut.tx_ptp_ts_tag,
tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
rx_clk=dut.rx_clk,
rx_rst=dut.rx_rst,
@@ -80,12 +81,12 @@ class TB:
self.dut.rx_rst.setimmediatevalue(0)
await RisingEdge(self.dut.tx_clk)
await RisingEdge(self.dut.tx_clk)
self.dut.tx_rst <= 1
self.dut.rx_rst <= 1
self.dut.tx_rst.value = 1
self.dut.rx_rst.value = 1
await RisingEdge(self.dut.tx_clk)
await RisingEdge(self.dut.tx_clk)
self.dut.tx_rst <= 0
self.dut.rx_rst <= 0
self.dut.tx_rst.value = 0
self.dut.rx_rst.value = 0
await RisingEdge(self.dut.tx_clk)
await RisingEdge(self.dut.tx_clk)

View File

@@ -36,11 +36,12 @@ module test_eth_mac
inout wire [63:0] tx_axis_tdata,
inout wire [7:0] tx_axis_tkeep,
inout wire tx_axis_tlast,
inout wire tx_axis_tuser,
inout wire [16:0] tx_axis_tuser,
inout wire tx_axis_tvalid,
inout wire tx_axis_tready,
inout wire [95:0] tx_ptp_time,
inout wire [95:0] tx_ptp_ts,
inout wire [15:0] tx_ptp_ts_tag,
inout wire tx_ptp_ts_valid,
inout wire rx_clk,

View File

@@ -47,7 +47,7 @@ class TB:
self._enable_generator = None
self._enable_cr = None
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
self.source = GmiiSource(dut.gmii_d, dut.gmii_er, dut.gmii_en,
dut.clk, dut.rst, dut.gmii_clk_en, dut.gmii_mii_sel)
@@ -61,10 +61,10 @@ class TB:
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 1
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 0
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
@@ -76,15 +76,17 @@ class TB:
self._enable_generator = generator
if self._enable_generator is not None:
self._enable_cr = cocotb.fork(self._run_enable())
self._enable_cr = cocotb.start_soon(self._run_enable())
def clear_enable_generator(self):
self.set_enable_generator(None)
async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator:
self.dut.gmii_clk_en <= val
await RisingEdge(self.dut.clk)
self.dut.gmii_clk_en.value = val
await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
@@ -92,7 +94,7 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_
tb = TB(dut)
tb.source.ifg = ifg
tb.dut.gmii_mii_sel <= mii_sel
tb.dut.gmii_mii_sel.value = mii_sel
if enable_gen is not None:
tb.set_enable_generator(enable_gen())

View File

@@ -44,7 +44,7 @@ class TB:
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.fork(Clock(dut.phy_gtx_clk, 8, units="ns").start())
cocotb.start_soon(Clock(dut.phy_gtx_clk, 8, units="ns").start())
self.gmii_phy = GmiiPhy(dut.phy_txd, dut.phy_tx_er, dut.phy_tx_en, dut.phy_tx_clk, dut.phy_gtx_clk,
dut.phy_rxd, dut.phy_rx_er, dut.phy_rx_dv, dut.phy_rx_clk, dut.phy_rst, speed=speed)
@@ -64,10 +64,10 @@ class TB:
self.dut.phy_rst.setimmediatevalue(0)
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)
self.dut.phy_rst <= 1
self.dut.phy_rst.value = 1
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)
self.dut.phy_rst <= 0
self.dut.phy_rst.value = 0
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)

View File

@@ -47,7 +47,7 @@ class TB:
self._enable_generator = None
self._enable_cr = None
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
self.source = MiiSource(dut.mii_d, dut.mii_er, dut.mii_en,
dut.clk, dut.rst, dut.mii_clk_en)
@@ -60,10 +60,10 @@ class TB:
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 1
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 0
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
@@ -75,15 +75,17 @@ class TB:
self._enable_generator = generator
if self._enable_generator is not None:
self._enable_cr = cocotb.fork(self._run_enable())
self._enable_cr = cocotb.start_soon(self._run_enable())
def clear_enable_generator(self):
self.set_enable_generator(None)
async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator:
self.dut.mii_clk_en <= val
await RisingEdge(self.dut.clk)
self.dut.mii_clk_en.value = val
await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None):

View File

@@ -55,10 +55,10 @@ class TB:
self.dut.phy_rst.setimmediatevalue(0)
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)
self.dut.phy_rst <= 1
self.dut.phy_rst.value = 1
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)
self.dut.phy_rst <= 0
self.dut.phy_rst.value = 0
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)

View File

@@ -30,7 +30,7 @@ import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.triggers import RisingEdge, ClockCycles
from cocotb.utils import get_sim_time
from cocotbext.eth import PtpClock
@@ -43,7 +43,7 @@ class TB:
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.fork(Clock(dut.clk, 6.4, units="ns").start())
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
self.ptp_clock = PtpClock(
ts_96=dut.ts_96,
@@ -59,10 +59,10 @@ class TB:
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 1
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 0
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
@@ -79,8 +79,7 @@ async def run_default_rate(dut):
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
for k in range(10000):
await RisingEdge(dut.clk)
await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec')
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
@@ -126,8 +125,7 @@ async def run_load_timestamps(dut):
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
for k in range(2000):
await RisingEdge(dut.clk)
await ClockCycles(dut.clk, 2000)
stop_time = get_sim_time('sec')
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
@@ -221,8 +219,7 @@ async def run_frequency_adjustment(dut):
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
for k in range(10000):
await RisingEdge(dut.clk)
await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec')
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
@@ -264,8 +261,7 @@ async def run_drift_adjustment(dut):
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
for k in range(10000):
await RisingEdge(dut.clk)
await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec')
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)

View File

@@ -30,7 +30,7 @@ import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.triggers import RisingEdge, ClockCycles
from cocotb.utils import get_sim_time
from cocotbext.eth import PtpClockSimTime
@@ -43,7 +43,7 @@ class TB:
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.fork(Clock(dut.clk, 6.4, units="ns").start())
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
self.ptp_clock = PtpClockSimTime(
ts_96=dut.ts_96,
@@ -66,8 +66,7 @@ async def run_test(dut):
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
for k in range(10000):
await RisingEdge(dut.clk)
await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec')
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)

View File

@@ -47,7 +47,7 @@ class TB:
self._enable_generator = None
self._enable_cr = None
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
self.source = RgmiiSource(dut.rgmii_d, dut.rgmii_ctl, dut.clk, dut.rst, dut.rgmii_clk_en, dut.rgmii_mii_sel)
self.sink = RgmiiSink(dut.rgmii_d, dut.rgmii_ctl, dut.clk, dut.rst, dut.rgmii_clk_en, dut.rgmii_mii_sel)
@@ -59,10 +59,10 @@ class TB:
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 1
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 0
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
@@ -74,15 +74,17 @@ class TB:
self._enable_generator = generator
if self._enable_generator is not None:
self._enable_cr = cocotb.fork(self._run_enable())
self._enable_cr = cocotb.start_soon(self._run_enable())
def clear_enable_generator(self):
self.set_enable_generator(None)
async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator:
self.dut.rgmii_clk_en <= val
await RisingEdge(self.dut.clk)
self.dut.rgmii_clk_en.value = val
await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
@@ -90,7 +92,7 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_
tb = TB(dut)
tb.source.ifg = ifg
tb.dut.rgmii_mii_sel <= mii_sel
tb.dut.rgmii_mii_sel.value = mii_sel
if enable_gen is not None:
tb.set_enable_generator(enable_gen())

View File

@@ -45,11 +45,11 @@ class TB:
self.log.setLevel(logging.DEBUG)
if speed == 1000e6:
cocotb.fork(Clock(dut.phy_tx_clk, 8, units="ns").start())
cocotb.start_soon(Clock(dut.phy_tx_clk, 8, units="ns").start())
elif speed == 100e6:
cocotb.fork(Clock(dut.phy_tx_clk, 40, units="ns").start())
cocotb.start_soon(Clock(dut.phy_tx_clk, 40, units="ns").start())
elif speed == 10e6:
cocotb.fork(Clock(dut.phy_tx_clk, 400, units="ns").start())
cocotb.start_soon(Clock(dut.phy_tx_clk, 400, units="ns").start())
self.rgmii_phy = RgmiiPhy(dut.phy_txd, dut.phy_tx_ctl, dut.phy_tx_clk,
dut.phy_rxd, dut.phy_rx_ctl, dut.phy_rx_clk, dut.phy_rst, speed=speed)
@@ -68,10 +68,10 @@ class TB:
self.dut.phy_rst.setimmediatevalue(0)
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)
self.dut.phy_rst <= 1
self.dut.phy_rst.value = 1
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)
self.dut.phy_rst <= 0
self.dut.phy_rst.value = 0
await RisingEdge(self.dut.phy_tx_clk)
await RisingEdge(self.dut.phy_tx_clk)

View File

@@ -48,7 +48,7 @@ class TB:
self._enable_generator = None
self._enable_cr = None
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
self.source = XgmiiSource(dut.xgmii_d, dut.xgmii_c, dut.clk, dut.rst, dut.xgmii_clk_en)
self.sink = XgmiiSink(dut.xgmii_d, dut.xgmii_c, dut.clk, dut.rst, dut.xgmii_clk_en)
@@ -59,10 +59,10 @@ class TB:
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 1
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst <= 0
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
@@ -74,15 +74,17 @@ class TB:
self._enable_generator = generator
if self._enable_generator is not None:
self._enable_cr = cocotb.fork(self._run_enable())
self._enable_cr = cocotb.start_soon(self._run_enable())
def clear_enable_generator(self):
self.set_enable_generator(None)
async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator:
self.dut.xgmii_clk_en <= val
await RisingEdge(self.dut.clk)
self.dut.xgmii_clk_en.value = val
await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_dic=True,
@@ -123,7 +125,7 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
tb = TB(dut)
byte_width = tb.source.width // 8
byte_lanes = tb.source.byte_lanes
tb.source.ifg = ifg
tb.source.enable_dic = enable_dic
@@ -164,23 +166,23 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
for test_data in test_frames:
if ifg == 0:
lane = 0
if force_offset_start and byte_width > 4:
if force_offset_start and byte_lanes > 4:
lane = 4
start_lane_ref.append(lane)
lane = (lane + len(test_data)+4+ifg) % byte_width
lane = (lane + len(test_data)+4+ifg) % byte_lanes
if enable_dic:
offset = lane % 4
if deficit_idle_count+offset >= 4:
offset += 4
lane = (lane - offset) % byte_width
lane = (lane - offset) % byte_lanes
deficit_idle_count = (deficit_idle_count + offset) % 4
else:
offset = lane % 4
if offset > 0:
offset += 4
lane = (lane - offset) % byte_width
lane = (lane - offset) % byte_lanes
tb.log.info("start_lane_ref: %s", start_lane_ref)