35 Commits

Author SHA1 Message Date
Alex Forencich
3b75a9a1a7 Release v0.1.22
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-11-07 11:56:35 -08:00
Alex Forencich
7d8d214b57 Improve PTP model resolution by using Decimal types and wider internal fns accumulators
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-11-07 01:28:25 -08:00
Alex Forencich
c44f928bea Rename PTP clock timestamp signals to tod and rel
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-11-07 01:28:04 -08:00
Alex Forencich
357dd26aae Use num/denom for PTP clock drift
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-11-07 00:41:02 -08:00
Alex Forencich
6131079494 Rename long-description-content-type in setup.cfg
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-11-07 00:13:56 -08:00
Alex Forencich
9079ca34f2 Parametrize EthMac test
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-05-26 17:18:16 -07:00
Alex Forencich
08fd179dac Fix logging when using from_entity
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-05-26 17:02:27 -07:00
Alex Forencich
1fa122e4ee Add Python 3.11 to regression tests
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-05-26 16:48:24 -07:00
Alex Forencich
4b13e61dc8 Remove recursively-expanded macros for module parameters in makefiles
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-05-26 16:47:53 -07:00
Alex Forencich
72f7290488 Use bytes instead of bytearrays
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-02 23:56:01 -08:00
Alex Forencich
6d1afe0904 Rework parameter handling in makefiles
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-29 19:54:10 -08:00
Alex Forencich
d61362e79b Bump to dev version
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-25 18:58:18 -08:00
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
35 changed files with 900 additions and 545 deletions

View File

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

108
README.md
View File

@@ -585,15 +585,15 @@ Methods:
### PTP clock ### PTP clock
The `PtpClock` class implements a PTP hardware clock that produces IEEE 1588 format 96 and 64 bit PTP timestamps. The `PtpClock` class implements a PTP hardware clock that produces IEEE 1588 format 96-bit time-of-day and 64-bit relative PTP timestamps.
To use this module, import it and connect it to the DUT: To use this module, import it and connect it to the DUT:
from cocotbext.eth import PtpClock from cocotbext.eth import PtpClock
ptp_clock = PtpClock( ptp_clock = PtpClock(
ts_96=dut.ts_96, ts_tod=dut.ts_tod,
ts_64=dut.ts_64, ts_rel=dut.ts_rel,
ts_step=dut.ts_step, ts_step=dut.ts_step,
pps=dut.pps, pps=dut.pps,
clock=dut.clk, clock=dut.clk,
@@ -603,17 +603,21 @@ To use this module, import it and connect it to the DUT:
Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge. Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge.
Internally, the `PtpClock` module uses 32-bit fractional ns fields for higher frequency resolution. Only the upper 16 bits are returned in the timestamps, but the full fns value can be accessed with the _ts_tod_fns_ and _ts_rel_fns_ attributes.
All APIs that handle fractional values use the `Decimal` type for maximum precision, as the combination of timestamp range and resolution is usually too much for normal floating point numbers to handle without significant loss of precision.
#### Signals #### Signals
* `ts_96`: 96-bit timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns) * `ts_tod`: 96-bit time-of-day timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
* `ts_64`: 64-bit timestamp (48 bit ns, 16 bit fractional ns) * `ts_rel`: 64-bit relative timestamp (48 bit ns, 16 bit fractional ns)
* `ts_step`: step output, pulsed when non-monotonic step occurs * `ts_step`: step output, pulsed when non-monotonic step occurs
* `pps`: pulse-per-second output, pulsed when ts_96 seconds field increments * `pps`: pulse-per-second output, pulsed when ts_tod seconds field increments
#### Constructor parameters: #### Constructor parameters:
* _ts_96_: 96-bit timestamp signal (optional) * _ts_tod_: 96-bit time-of-day timestamp signal (optional)
* _ts_64_: 64-bit timestamp signal (optional) * _ts_rel_: 64-bit relative timestamp signal (optional)
* _ts_step_: timestamp step signal (optional) * _ts_step_: timestamp step signal (optional)
* _pps_: pulse-per-second signal (optional) * _pps_: pulse-per-second signal (optional)
* _clock_: clock * _clock_: clock
@@ -623,74 +627,84 @@ Once the clock is instantiated, it will generate a continuous stream of monotoni
#### Attributes: #### Attributes:
* _ts_96_s_: current 96-bit timestamp seconds field * _ts_tod_s_: current 96-bit ToD timestamp seconds field
* _ts_96_ns_: current 96-bit timestamp ns field * _ts_tod_ns_: current 96-bit ToD timestamp ns field
* _ts_96_fns_: current 96-bit timestamp fractional ns field * _ts_tod_fns_: current 96-bit ToD timestamp fractional ns field
* _ts_64_ns_: current 64-bit timestamp ns field * _ts_rel_ns_: current 64-bit relative timestamp ns field
* _ts_64_fns_: current 64-bit timestamp fractional ns field * _ts_rel_fns_: current 64-bit relative timestamp fractional ns field
#### Methods #### Methods
* `set_period(ns, fns)`: set clock period from separate fields * `set_period(ns, fns)`: set clock period from separate fields
* `set_drift(ns, fns, rate)`: set clock drift from separate fields * `set_drift(num, denom)`: set clock drift from separate fields
* `set_period_ns(t)`: set clock period in ns (float) * `set_period_ns(t)`: set clock period and drift in ns (Decimal)
* `get_period_ns()`: return current clock period in ns (float) * `get_period_ns()`: return current clock period in ns (Decimal)
* `set_ts_96(ts_s, ts_ns=None, ts_fns=None)`: set 96-bit timestamp from integer or from separate fields * `set_ts_tod(ts_s, ts_ns, ts_fns)`: set 96-bit ToD timestamp from separate fields
* `set_ts_96_ns(t)`: set 96-bit timestamp from ns (float) * `set_ts_tod_96(ts)`: set 96-bit ToD timestamp from integer
* `set_ts_96_s(t)`: set 96-bit timestamp from seconds (float) * `set_ts_tod_ns(t)`: set 96-bit ToD timestamp from ns (Decimal)
* `get_ts_96()`: return current 96-bit timestamp as an integer * `set_ts_tod_s(t)`: set 96-bit ToD timestamp from seconds (Decimal)
* `get_ts_96_ns()`: return current 96-bit timestamp in ns (float) * `set_ts_tod_sim_time()`: set 96-bit ToD timestamp from sim time
* `get_ts_96_s()`: return current 96-bit timestamp in seconds (float) * `get_ts_tod()`: return current 96-bit ToD timestamp as separate fields
* `set_ts_64(ts_ns, ts_fns=None)`: set 64-bit timestamp from integer or from separate fields * `get_ts_tod_96()`: return current 96-bit ToD timestamp as an integer
* `set_ts_64_ns(t)`: set 64-bit timestamp from ns (float) * `get_ts_tod_ns()`: return current 96-bit ToD timestamp in ns (Decimal)
* `set_ts_64_s(t)`: set 64-bit timestamp from seconds (float) * `get_ts_tod_s()`: return current 96-bit ToD timestamp in seconds (Decimal)
* `get_ts_64()`: return current 64-bit timestamp as an integer * `set_ts_rel(ts_ns, ts_fns)`: set 64-bit relative timestamp from separate fields
* `get_ts_64_ns()`: return current 64-bit timestamp in ns (float) * `set_ts_rel_64(ts)`: set 64-bit relative timestamp from integer
* `get_ts_64_s()`: return current 64-bit timestamp in seconds (float) * `set_ts_rel_ns(t)`: set 64-bit relative timestamp from ns (Decimal)
* `set_ts_rel_s(t)`: set 64-bit relative timestamp from seconds (Decimal)
* `set_ts_rel_sim_time()`: set 64-bit relative timestamp from sim time
* `get_ts_rel()`: return current 64-bit relative timestamp as separate fields
* `get_ts_rel_64()`: return current 64-bit relative timestamp as an integer
* `get_ts_rel_ns()`: return current 64-bit relative timestamp in ns (Decimal)
* `get_ts_rel_s()`: return current 64-bit relative timestamp in seconds (Decimal)
### PTP clock (sim time) ### PTP clock (sim time)
The `PtpClockSimTime` class implements a PTP hardware clock that produces IEEE 1588 format 96 and 64 bit PTP timestamps, derived from the current simulation time. This module can be used in place of `PtpClock` so that captured PTP timestamps can be easily compared to captured simulation time. The `PtpClockSimTime` class implements a PTP hardware clock that produces IEEE 1588 format 96-bit time-of-day and 64-bit relative PTP timestamps, derived from the current simulation time. This module can be used in place of `PtpClock` so that captured PTP timestamps can be easily compared to captured simulation time.
To use this module, import it and connect it to the DUT: To use this module, import it and connect it to the DUT:
from cocotbext.eth import PtpClockSimTime from cocotbext.eth import PtpClockSimTime
ptp_clock = PtpClockSimTime( ptp_clock = PtpClockSimTime(
ts_96=dut.ts_96, ts_tod=dut.ts_tod,
ts_64=dut.ts_64, ts_rel=dut.ts_rel,
pps=dut.pps, pps=dut.pps,
clock=dut.clk clock=dut.clk
) )
Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge. Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge.
All APIs that handle fractional values use the `Decimal` type for maximum precision, as the combination of timestamp range and resolution is usually too much for normal floating point numbers to handle without significant loss of precision.
#### Signals #### Signals
* `ts_96`: 96-bit timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns) * `ts_tod`: 96-bit time-of-day timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
* `ts_64`: 64-bit timestamp (48 bit ns, 16 bit fractional ns) * `ts_rel`: 64-bit relative timestamp (48 bit ns, 16 bit fractional ns)
* `pps`: pulse-per-second output, pulsed when ts_96 seconds field increments * `pps`: pulse-per-second output, pulsed when ts_tod seconds field increments
#### Constructor parameters: #### Constructor parameters:
* _ts_96_: 96-bit timestamp signal (optional) * _ts_tod_: 96-bit time-of-day timestamp signal (optional)
* _ts_64_: 64-bit timestamp signal (optional) * _ts_rel_: 64-bit relative timestamp signal (optional)
* _pps_: pulse-per-second signal (optional) * _pps_: pulse-per-second signal (optional)
* _clock_: clock * _clock_: clock
#### Attributes: #### Attributes:
* _ts_96_s_: current 96-bit timestamp seconds field * _ts_tod_s_: current 96-bit ToD timestamp seconds field
* _ts_96_ns_: current 96-bit timestamp ns field * _ts_tod_ns_: current 96-bit ToD timestamp ns field
* _ts_96_fns_: current 96-bit timestamp fractional ns field * _ts_tod_fns_: current 96-bit ToD timestamp fractional ns field
* _ts_64_ns_: current 64-bit timestamp ns field * _ts_rel_ns_: current 64-bit relative timestamp ns field
* _ts_64_fns_: current 64-bit timestamp fractional ns field * _ts_rel_fns_: current 64-bit relative timestamp fractional ns field
#### Methods #### Methods
* `get_ts_96()`: return current 96-bit timestamp as an integer * `get_ts_tod()`: return current 96-bit ToD timestamp as separate fields
* `get_ts_96_ns()`: return current 96-bit timestamp in ns (float) * `get_ts_tod_96()`: return current 96-bit ToD timestamp as an integer
* `get_ts_96_s()`: return current 96-bit timestamp in seconds (float) * `get_ts_tod_ns()`: return current 96-bit ToD timestamp in ns (Decimal)
* `get_ts_64()`: return current 64-bit timestamp as an integer * `get_ts_tod_s()`: return current 96-bit ToD timestamp in seconds (Decimal)
* `get_ts_64_ns()`: return current 64-bit timestamp in ns (float) * `get_ts_rel()`: return current 64-bit relative timestamp as separate fields
* `get_ts_64_s()`: return current 64-bit timestamp in seconds (float) * `get_ts_rel_96()`: return current 64-bit relative timestamp as an integer
* `get_ts_rel_ns()`: return current 64-bit relative timestamp in ns (Decimal)
* `get_ts_rel_s()`: return current 64-bit relative timestamp in seconds (Decimal)

View File

@@ -42,12 +42,12 @@ class XgmiiCtrl(enum.IntEnum):
TERM = 0xfd TERM = 0xfd
ERROR = 0xfe ERROR = 0xfe
SEQ_OS = 0x9c SEQ_OS = 0x9c
RES0 = 0x1c RES_0 = 0x1c
RES1 = 0x3c RES_1 = 0x3c
RES2 = 0x7c RES_2 = 0x7c
RES3 = 0xbc RES_3 = 0xbc
RES4 = 0xdc RES_4 = 0xdc
RES5 = 0xf7 RES_5 = 0xf7
SIG_OS = 0x5c SIG_OS = 0x5c
@@ -93,3 +93,41 @@ class BaseRBlockType(enum.IntEnum):
TERM_5 = 0xd2 # C7 C6 D4 D3 D2 D1 D0 BT TERM_5 = 0xd2 # C7 C6 D4 D3 D2 D1 D0 BT
TERM_6 = 0xe1 # C7 D5 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 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

@@ -43,8 +43,8 @@ AxiStreamBus, AxiStreamTransaction, AxiStreamSource, AxiStreamSink, AxiStreamMon
class EthMacFrame: class EthMacFrame:
def __init__(self, data=None, tx_complete=None): def __init__(self, data=b'', tx_complete=None):
self.data = bytearray() self.data = b''
self.sim_time_start = None self.sim_time_start = None
self.sim_time_sfd = None self.sim_time_sfd = None
self.sim_time_end = None self.sim_time_end = None
@@ -53,7 +53,7 @@ class EthMacFrame:
self.tx_complete = None self.tx_complete = None
if type(data) is EthMacFrame: if type(data) is EthMacFrame:
self.data = bytearray(data.data) self.data = bytes(data.data)
self.sim_time_start = data.sim_time_start self.sim_time_start = data.sim_time_start
self.sim_time_sfd = data.sim_time_sfd self.sim_time_sfd = data.sim_time_sfd
self.sim_time_end = data.sim_time_end self.sim_time_end = data.sim_time_end
@@ -61,7 +61,7 @@ class EthMacFrame:
self.ptp_tag = data.ptp_tag self.ptp_tag = data.ptp_tag
self.tx_complete = data.tx_complete self.tx_complete = data.tx_complete
else: else:
self.data = bytearray(data) self.data = bytes(data)
if tx_complete is not None: if tx_complete is not None:
self.tx_complete = tx_complete self.tx_complete = tx_complete
@@ -133,7 +133,10 @@ class EthMacTx(Reset):
self.ptp_ts_valid = ptp_ts_valid self.ptp_ts_valid = ptp_ts_valid
self.ifg = ifg self.ifg = ifg
self.speed = speed self.speed = speed
if bus._name:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
else:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}")
self.log.info("Ethernet MAC TX model") self.log.info("Ethernet MAC TX model")
self.log.info("cocotbext-eth version %s", __version__) self.log.info("cocotbext-eth version %s", __version__)
@@ -253,7 +256,7 @@ class EthMacTx(Reset):
self._run_ts_cr = None self._run_ts_cr = None
if self.ptp_ts_valid: if self.ptp_ts_valid:
self.ptp_ts_valid <= 0 self.ptp_ts_valid.value = 0
self.active = False self.active = False
@@ -262,9 +265,9 @@ class EthMacTx(Reset):
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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: 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): async def _run(self):
frame = None frame = None
@@ -274,7 +277,9 @@ class EthMacTx(Reset):
# wait for data # wait for data
cycle = await self.stream.recv() cycle = await self.stream.recv()
frame = EthMacFrame(bytearray()) frame = EthMacFrame()
data = bytearray()
frame.sim_time_start = get_sim_time() frame.sim_time_start = get_sim_time()
# wait for preamble time # wait for preamble time
@@ -293,13 +298,14 @@ class EthMacTx(Reset):
for offset in range(self.byte_lanes): for offset in range(self.byte_lanes):
if not hasattr(self.bus, "tkeep") or (cycle.tkeep.integer >> offset) & 1: if not hasattr(self.bus, "tkeep") or (cycle.tkeep.integer >> offset) & 1:
frame.data.append((cycle.tdata.integer >> (offset * self.byte_size)) & self.byte_mask) data.append((cycle.tdata.integer >> (offset * self.byte_size)) & self.byte_mask)
byte_count += 1 byte_count += 1
# wait for serialization time # wait for serialization time
await Timer(self.time_scale*byte_count*8//self.speed, 'step') await Timer(self.time_scale*byte_count*8//self.speed, 'step')
if cycle.tlast.integer: if cycle.tlast.integer:
frame.data = bytes(data)
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
self.log.info("RX frame: %s", frame) self.log.info("RX frame: %s", frame)
@@ -322,16 +328,18 @@ class EthMacTx(Reset):
await Timer(self.time_scale*self.ifg*8//self.speed, 'step') await Timer(self.time_scale*self.ifg*8//self.speed, 'step')
async def _run_ts(self): async def _run_ts(self):
clock_edge_event = RisingEdge(self.clock)
while True: while True:
await RisingEdge(self.clock) await clock_edge_event
self.ptp_ts_valid <= 0 self.ptp_ts_valid.value = 0
if not self.ts_queue.empty(): if not self.ts_queue.empty():
ts, tag = self.ts_queue.get_nowait() ts, tag = self.ts_queue.get_nowait()
self.ptp_ts <= ts self.ptp_ts.value = ts
if self.ptp_ts_tag is not None: if self.ptp_ts_tag is not None:
self.ptp_ts_tag <= tag self.ptp_ts_tag.value = tag
self.ptp_ts_valid <= 1 self.ptp_ts_valid.value = 1
class EthMacRx(Reset): class EthMacRx(Reset):
@@ -344,7 +352,10 @@ class EthMacRx(Reset):
self.ptp_time = ptp_time self.ptp_time = ptp_time
self.ifg = ifg self.ifg = ifg
self.speed = speed self.speed = speed
if bus._name:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
else:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}")
self.log.info("Ethernet MAC RX model") self.log.info("Ethernet MAC RX model")
self.log.info("cocotbext-eth version %s", __version__) self.log.info("cocotbext-eth version %s", __version__)
@@ -475,7 +486,7 @@ class EthMacRx(Reset):
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None

View File

@@ -157,6 +157,7 @@ class GmiiSource(Reset):
self.current_frame = None self.current_frame = None
self.idle_event = Event() self.idle_event = Event()
self.idle_event.set() self.idle_event.set()
self.active_event = Event()
self.ifg = 12 self.ifg = 12
self.mii_mode = False self.mii_mode = False
@@ -189,6 +190,7 @@ class GmiiSource(Reset):
frame = GmiiFrame(frame) frame = GmiiFrame(frame)
await self.queue.put(frame) await self.queue.put(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -198,6 +200,7 @@ class GmiiSource(Reset):
frame = GmiiFrame(frame) frame = GmiiFrame(frame)
self.queue.put_nowait(frame) self.queue.put_nowait(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -225,6 +228,7 @@ class GmiiSource(Reset):
frame.handle_tx_complete() frame.handle_tx_complete()
self.dequeue_event.set() self.dequeue_event.set()
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0 self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0 self.queue_occupancy_frames = 0
@@ -239,10 +243,10 @@ class GmiiSource(Reset):
self._run_cr = None self._run_cr = None
self.active = False self.active = False
self.data <= 0 self.data.value = 0
if self.er is not None: if self.er is not None:
self.er <= 0 self.er.value = 0
self.dv <= 0 self.dv.value = 0
if self.current_frame: if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame) self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -251,10 +255,11 @@ class GmiiSource(Reset):
if self.queue.empty(): if self.queue.empty():
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
@@ -264,8 +269,14 @@ class GmiiSource(Reset):
ifg_cnt = 0 ifg_cnt = 0
self.active = False 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: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
if ifg_cnt > 0: if ifg_cnt > 0:
@@ -308,10 +319,10 @@ class GmiiSource(Reset):
d = frame_data[frame_offset] d = frame_data[frame_offset]
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD): if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD):
frame.sim_time_sfd = get_sim_time() frame.sim_time_sfd = get_sim_time()
self.data <= d self.data.value = d
if self.er is not None: if self.er is not None:
self.er <= frame_error[frame_offset] self.er.value = frame_error[frame_offset]
self.dv <= 1 self.dv.value = 1
frame_offset += 1 frame_offset += 1
if frame_offset >= len(frame_data): if frame_offset >= len(frame_data):
@@ -321,12 +332,19 @@ class GmiiSource(Reset):
frame = None frame = None
self.current_frame = None self.current_frame = None
else: else:
self.data <= 0 self.data.value = 0
if self.er is not None: if self.er is not None:
self.er <= 0 self.er.value = 0
self.dv <= 0 self.dv.value = 0
self.active = False self.active = False
if ifg_cnt == 0 and self.queue.empty():
self.idle_event.set() 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): class GmiiSink(Reset):
@@ -422,14 +440,22 @@ class GmiiSink(Reset):
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
self.active = False 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: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
d_val = self.data.value.integer d_val = self.data.value.integer
@@ -488,6 +514,12 @@ class GmiiSink(Reset):
frame.data.append(d_val) frame.data.append(d_val)
frame.error.append(er_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: 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,
@@ -517,12 +549,12 @@ class GmiiPhy:
self._clock_cr.kill() self._clock_cr.kill()
if self.speed == 1000e6: 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.tx.mii_mode = False
self.rx.mii_mode = False self.rx.mii_mode = False
self.tx.clock = self.gtx_clk self.tx.clock = self.gtx_clk
else: 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.tx.mii_mode = True
self.rx.mii_mode = True self.rx.mii_mode = True
self.tx.clock = self.tx_clk self.tx.clock = self.tx_clk
@@ -536,8 +568,8 @@ class GmiiPhy:
while True: while True:
await t await t
self.rx_clk <= 1 self.rx_clk.value = 1
self.tx_clk <= 1 self.tx_clk.value = 1
await t await t
self.rx_clk <= 0 self.rx_clk.value = 0
self.tx_clk <= 0 self.tx_clk.value = 0

View File

@@ -59,6 +59,7 @@ class MiiSource(Reset):
self.current_frame = None self.current_frame = None
self.idle_event = Event() self.idle_event = Event()
self.idle_event.set() self.idle_event.set()
self.active_event = Event()
self.ifg = 12 self.ifg = 12
@@ -90,6 +91,7 @@ class MiiSource(Reset):
frame = GmiiFrame(frame) frame = GmiiFrame(frame)
await self.queue.put(frame) await self.queue.put(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -99,6 +101,7 @@ class MiiSource(Reset):
frame = GmiiFrame(frame) frame = GmiiFrame(frame)
self.queue.put_nowait(frame) self.queue.put_nowait(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -126,6 +129,7 @@ class MiiSource(Reset):
frame.handle_tx_complete() frame.handle_tx_complete()
self.dequeue_event.set() self.dequeue_event.set()
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0 self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0 self.queue_occupancy_frames = 0
@@ -140,10 +144,10 @@ class MiiSource(Reset):
self._run_cr = None self._run_cr = None
self.active = False self.active = False
self.data <= 0 self.data.value = 0
if self.er is not None: if self.er is not None:
self.er <= 0 self.er.value = 0
self.dv <= 0 self.dv.value = 0
if self.current_frame: if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame) self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -152,10 +156,11 @@ class MiiSource(Reset):
if self.queue.empty(): if self.queue.empty():
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
@@ -165,8 +170,14 @@ class MiiSource(Reset):
ifg_cnt = 0 ifg_cnt = 0
self.active = False 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: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
if ifg_cnt > 0: if ifg_cnt > 0:
@@ -202,10 +213,10 @@ class MiiSource(Reset):
d = frame_data[frame_offset] d = frame_data[frame_offset]
if frame.sim_time_sfd is None and d == 0xD: if frame.sim_time_sfd is None and d == 0xD:
frame.sim_time_sfd = get_sim_time() frame.sim_time_sfd = get_sim_time()
self.data <= d self.data.value = d
if self.er is not None: if self.er is not None:
self.er <= frame_error[frame_offset] self.er.value = frame_error[frame_offset]
self.dv <= 1 self.dv.value = 1
frame_offset += 1 frame_offset += 1
if frame_offset >= len(frame_data): if frame_offset >= len(frame_data):
@@ -215,12 +226,19 @@ class MiiSource(Reset):
frame = None frame = None
self.current_frame = None self.current_frame = None
else: else:
self.data <= 0 self.data.value = 0
if self.er is not None: if self.er is not None:
self.er <= 0 self.er.value = 0
self.dv <= 0 self.dv.value = 0
self.active = False self.active = False
if ifg_cnt == 0 and self.queue.empty():
self.idle_event.set() 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): class MiiSink(Reset):
@@ -313,14 +331,22 @@ class MiiSink(Reset):
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
self.active = False 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: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
d_val = self.data.value.integer d_val = self.data.value.integer
@@ -374,6 +400,12 @@ class MiiSink(Reset):
frame.data.append(d_val) frame.data.append(d_val)
frame.error.append(er_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: class MiiPhy:
def __init__(self, txd, tx_er, tx_en, tx_clk, rxd, rx_er, rx_dv, rx_clk, reset=None, def __init__(self, txd, tx_er, tx_en, tx_clk, rxd, rx_er, rx_dv, rx_clk, reset=None,
@@ -402,7 +434,7 @@ class MiiPhy:
if self._clock_cr is not None: if self._clock_cr is not None:
self._clock_cr.kill() 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): async def _run_clocks(self, period):
half_period = get_sim_steps(period / 2.0, 'ns') half_period = get_sim_steps(period / 2.0, 'ns')
@@ -410,8 +442,8 @@ class MiiPhy:
while True: while True:
await t await t
self.tx_clk <= 1 self.tx_clk.value = 1
self.rx_clk <= 1 self.rx_clk.value = 1
await t await t
self.tx_clk <= 0 self.tx_clk.value = 0
self.rx_clk <= 0 self.rx_clk.value = 0

View File

@@ -23,7 +23,7 @@ THE SOFTWARE.
""" """
import logging import logging
import math from decimal import Decimal, Context
from fractions import Fraction from fractions import Fraction
import cocotb import cocotb
@@ -38,8 +38,8 @@ class PtpClock(Reset):
def __init__( def __init__(
self, self,
ts_96=None, ts_tod=None,
ts_64=None, ts_rel=None,
ts_step=None, ts_step=None,
pps=None, pps=None,
clock=None, clock=None,
@@ -49,20 +49,13 @@ class PtpClock(Reset):
*args, **kwargs): *args, **kwargs):
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}") self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
self.ts_96 = ts_96 self.ts_tod = ts_tod
self.ts_64 = ts_64 self.ts_rel = ts_rel
self.ts_step = ts_step self.ts_step = ts_step
self.pps = pps self.pps = pps
self.clock = clock self.clock = clock
self.reset = reset self.reset = reset
self.period_ns = 0
self.period_fns = 0
self.drift_ns = 0
self.drift_fns = 0
self.drift_rate = 0
self.set_period_ns(period_ns)
self.log.info("PTP clock") self.log.info("PTP clock")
self.log.info("cocotbext-eth version %s", __version__) self.log.info("cocotbext-eth version %s", __version__)
self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("Copyright (c) 2020 Alex Forencich")
@@ -70,21 +63,28 @@ class PtpClock(Reset):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.ts_96_s = 0 self.ctx = Context(prec=60)
self.ts_96_ns = 0
self.ts_96_fns = 0
self.ts_64_ns = 0 self.period_ns = 0
self.ts_64_fns = 0 self.period_fns = 0
self.drift_num = 0
self.drift_denom = 0
self.drift_cnt = 0
self.set_period_ns(period_ns)
self.ts_tod_s = 0
self.ts_tod_ns = 0
self.ts_tod_fns = 0
self.ts_rel_ns = 0
self.ts_rel_fns = 0
self.ts_updated = False self.ts_updated = False
self.drift_cnt = 0 if self.ts_tod is not None:
self.ts_tod.setimmediatevalue(0)
if self.ts_96 is not None: if self.ts_rel is not None:
self.ts_96.setimmediatevalue(0) self.ts_rel.setimmediatevalue(0)
if self.ts_64 is not None:
self.ts_64.setimmediatevalue(0)
if self.ts_step is not None: if self.ts_step is not None:
self.ts_step.setimmediatevalue(0) self.ts_step.setimmediatevalue(0)
if self.pps is not None: if self.pps is not None:
@@ -96,90 +96,104 @@ class PtpClock(Reset):
def set_period(self, ns, fns): def set_period(self, ns, fns):
self.period_ns = int(ns) self.period_ns = int(ns)
self.period_fns = int(fns) & 0xffff self.period_fns = int(fns) & 0xffffffff
def set_drift(self, ns, fns, rate): def set_drift(self, num, denom):
self.drift_ns = int(ns) self.drift_num = int(num)
self.drift_fns = int(fns) & 0xffff self.drift_denom = int(denom)
self.drift_rate = int(rate)
def set_period_ns(self, t): def set_period_ns(self, t):
drift, period = math.modf(t*2**16) t = Decimal(t)
period, drift = self.ctx.divmod(Decimal(t) * Decimal(2**32), Decimal(1))
period = int(period) period = int(period)
frac = Fraction(drift).limit_denominator(2**16) frac = Fraction(drift).limit_denominator(2**16-1)
drift = frac.numerator self.set_period(period >> 32, period & 0xffffffff)
rate = frac.denominator self.set_drift(frac.numerator, frac.denominator)
self.period_ns = period >> 16
self.period_fns = period & 0xffff self.log.info("Set period: %s ns", t)
self.drift_ns = drift >> 16 self.log.info("Period: 0x%x ns 0x%08x fns", self.period_ns, self.period_fns)
self.drift_fns = drift & 0xffff self.log.info("Drift: 0x%04x / 0x%04x fns", self.drift_num, self.drift_denom)
self.drift_rate = rate
def get_period_ns(self): def get_period_ns(self):
p = ((self.period_ns << 16) | self.period_fns) / 2**16 p = Decimal((self.period_ns << 32) | self.period_fns)
if self.drift_rate: if self.drift_denom:
return p + ((self.drift_ns << 16) | self.drift_fns) / self.drift_rate / 2**16 p += Decimal(self.drift_num) / Decimal(self.drift_denom)
return p return p / Decimal(2**32)
def set_ts_96(self, ts_s, ts_ns=None, ts_fns=None): def set_ts_tod(self, ts_s, ts_ns, ts_fns):
ts_s = int(ts_s) self.ts_tod_s = int(ts_s)
if ts_fns is not None: self.ts_tod_ns = int(ts_ns)
# got separate fields self.ts_tod_fns = int(ts_fns)
self.ts_96_s = ts_s
self.ts_96_ns = int(ts_ns)
self.ts_96_fns = int(ts_fns)
else:
# got timestamp as integer
self.ts_96_s = ts_s >> 48
self.ts_96_ns = (ts_s >> 16) & 0x3fffffff
self.ts_96_fns = ts_s & 0xffff
self.ts_updated = True self.ts_updated = True
def set_ts_96_ns(self, t): def set_ts_tod_96(self, ts):
self.set_ts_96_s(t*1e-9) ts = int(ts)
self.set_ts_tod(ts >> 48, (ts >> 32) & 0x3fffffff, (ts & 0xffff) << 16)
def set_ts_96_s(self, t): def set_ts_tod_ns(self, t):
ts_ns, ts_s = math.modf(t) ts_s, ts_ns = self.ctx.divmod(Decimal(t), Decimal(1000000000))
ts_ns *= 1e9 ts_s = ts_s.scaleb(-9).to_integral_value()
ts_fns, ts_ns = math.modf(ts_ns) ts_ns, ts_fns = self.ctx.divmod(ts_ns, Decimal(1))
ts_fns *= 2**16 ts_ns = ts_ns.to_integral_value()
self.set_ts_96(ts_s, ts_ns, ts_fns) ts_fns = (ts_fns * Decimal(2**32)).to_integral_value()
self.set_ts_tod(ts_s, ts_ns, ts_fns)
def get_ts_96(self): def set_ts_tod_s(self, t):
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns self.set_ts_tod_ns(Decimal(t).scaleb(9, self.ctx))
def get_ts_96_ns(self): def set_ts_tod_sim_time(self):
return self.ts_96_s*1e9+self.ts_96_ns+self.ts_96_fns/2**16 self.set_ts_tod_ns(Decimal(get_sim_time('fs')).scaleb(-6))
def get_ts_96_s(self): def get_ts_tod(self):
return self.get_ts_96_ns()*1e-9 return (self.ts_tod_s, self.ts_tod_ns, self.ts_tod_fns)
def set_ts_64(self, ts_ns, ts_fns=None): def get_ts_tod_96(self):
ts_ns = int(ts_ns) ts_s, ts_ns, ts_fns = self.get_ts_tod()
if ts_fns is not None: return (ts_s << 48) | (ts_ns << 16) | (ts_fns >> 16)
# got separate fields
self.ts_64_ns = ts_ns def get_ts_tod_ns(self):
self.ts_64_fns = int(ts_fns) ts_s, ts_ns, ts_fns = self.get_ts_tod()
else: ns = Decimal(ts_fns) / Decimal(2**32)
# got timestamp as integer ns = self.ctx.add(ns, Decimal(ts_ns))
self.ts_64_ns = ts_ns >> 16 return self.ctx.add(ns, Decimal(ts_s).scaleb(9))
self.ts_64_fns = ts_ns & 0xffff
def get_ts_tod_s(self):
return self.get_ts_tod_ns().scaleb(-9, self.ctx)
def set_ts_rel(self, ts_ns, ts_fns):
self.ts_rel_ns = int(ts_ns)
self.ts_rel_fns = int(ts_fns)
self.ts_updated = True self.ts_updated = True
def set_ts_64_ns(self, t): def set_ts_rel_64(self, ts):
self.set_ts_64(t*2**16) ts = int(ts)
self.set_ts_rel(ts >> 16, (ts & 0xffff) << 16)
def set_ts_64_s(self, t): def set_ts_rel_ns(self, t):
self.set_ts_64_ns(t*1e9) ts_ns, ts_fns = self.ctx.divmod(Decimal(t), Decimal(1))
ts_ns = ts_ns.to_integral_value()
ts_fns = (ts_fns * Decimal(2**32)).to_integral_value()
self.set_ts_rel(ts_ns, ts_fns)
def get_ts_64(self): def set_ts_rel_s(self, t):
return (self.ts_64_ns << 16) | self.ts_64_fns self.set_ts_rel_ns(Decimal(t).scaleb(9, self.ctx))
def get_ts_64_ns(self): def set_ts_rel_sim_time(self):
return self.get_ts_64()/2**16 self.set_ts_rel_ns(Decimal(get_sim_time('fs')).scaleb(-6))
def get_ts_64_s(self): def get_ts_rel(self):
return self.get_ts_64()*1e-9 return (self.ts_rel_ns, self.ts_rel_fns)
def get_ts_rel_64(self):
ts_ns, ts_fns = self.get_ts_rel()
return (ts_ns << 16) | (ts_fns >> 16)
def get_ts_rel_ns(self):
ts_ns, ts_fns = self.get_ts_rel()
return self.ctx.add(Decimal(ts_fns) / Decimal(2**32), Decimal(ts_ns))
def get_ts_rel_s(self):
return self.get_ts_rel_ns().scaleb(-9, self.ctx)
def _handle_reset(self, state): def _handle_reset(self, state):
if state: if state:
@@ -188,80 +202,85 @@ class PtpClock(Reset):
self._run_cr.kill() self._run_cr.kill()
self._run_cr = None self._run_cr = None
self.ts_96_s = 0 self.ts_tod_s = 0
self.ts_96_ns = 0 self.ts_tod_ns = 0
self.ts_96_fns = 0 self.ts_tod_fns = 0
self.ts_64_ns = 0 self.ts_rel_ns = 0
self.ts_64_fns = 0 self.ts_rel_fns = 0
self.drift_cnt = 0 self.drift_cnt = 0
if self.ts_96 is not None: if self.ts_tod is not None:
self.ts_96 <= 0 self.ts_tod.value = 0
if self.ts_64 is not None: if self.ts_rel is not None:
self.ts_64 <= 0 self.ts_rel.value = 0
if self.ts_step is not None: if self.ts_step is not None:
self.ts_step <= 0 self.ts_step.value = 0
if self.pps is not None: if self.pps is not None:
self.pps <= 0 self.pps.value = 0
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
clock_edge_event = RisingEdge(self.clock)
while True: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.ts_step is not None: if self.ts_step is not None:
self.ts_step <= self.ts_updated self.ts_step.value = self.ts_updated
self.ts_updated = False self.ts_updated = False
if self.pps is not None: if self.pps is not None:
self.pps <= 0 self.pps.value = 0
# increment 96 bit timestamp # increment tod bit timestamp
if self.ts_96 is not None or self.pps is not None: self.ts_tod_fns += (self.period_ns << 32) + self.period_fns
t = ((self.ts_96_ns << 16) + self.ts_96_fns) + ((self.period_ns << 16) + self.period_fns)
if self.drift_rate and self.drift_cnt == 0: if self.drift_denom and self.drift_cnt == 0:
t += (self.drift_ns << 16) + self.drift_fns self.ts_tod_fns += self.drift_num
if t > (1000000000 << 16): ns_inc = self.ts_tod_fns >> 32
self.ts_96_s += 1 self.ts_tod_fns &= 0xffffffff
t -= (1000000000 << 16)
self.ts_tod_ns += ns_inc
if self.ts_tod_ns >= 1000000000:
self.ts_tod_s += 1
self.ts_tod_ns -= 1000000000
if self.pps is not None: if self.pps is not None:
self.pps <= 1 self.pps.value = 1
self.ts_96_fns = t & 0xffff if self.ts_tod is not None:
self.ts_96_ns = t >> 16 self.ts_tod.value = (self.ts_tod_s << 48) | (self.ts_tod_ns << 16) | (self.ts_tod_fns >> 16)
if self.ts_96 is not None: # increment rel bit timestamp
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns) self.ts_rel_fns += (self.period_ns << 32) + self.period_fns
# increment 64 bit timestamp if self.drift_denom and self.drift_cnt == 0:
if self.ts_64 is not None: self.ts_rel_fns += self.drift_num
t = ((self.ts_64_ns << 16) + self.ts_64_fns) + ((self.period_ns << 16) + self.period_fns)
if self.drift_rate and self.drift_cnt == 0: ns_inc = self.ts_rel_fns >> 32
t += ((self.drift_ns << 16) + self.drift_fns) self.ts_rel_fns &= 0xffffffff
self.ts_64_fns = t & 0xffff self.ts_rel_ns = (self.ts_rel_ns + ns_inc) & 0xffffffffffff
self.ts_64_ns = t >> 16
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns if self.ts_rel is not None:
self.ts_rel.value = (self.ts_rel_ns << 16) | (self.ts_rel_fns >> 16)
if self.drift_rate: if self.drift_denom:
if self.drift_cnt > 0: if self.drift_cnt > 0:
self.drift_cnt -= 1 self.drift_cnt -= 1
else: else:
self.drift_cnt = self.drift_rate-1 self.drift_cnt = self.drift_denom-1
class PtpClockSimTime: class PtpClockSimTime:
def __init__(self, ts_96=None, ts_64=None, pps=None, clock=None, *args, **kwargs): def __init__(self, ts_tod=None, ts_rel=None, pps=None, clock=None, *args, **kwargs):
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}") self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
self.ts_96 = ts_96 self.ts_tod = ts_tod
self.ts_64 = ts_64 self.ts_rel = ts_rel
self.pps = pps self.pps = pps
self.clock = clock self.clock = clock
@@ -272,61 +291,80 @@ class PtpClockSimTime:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.ts_96_s = 0 self.ctx = Context(prec=60)
self.ts_96_ns = 0
self.ts_96_fns = 0
self.ts_64_ns = 0 self.ts_tod_s = 0
self.ts_64_fns = 0 self.ts_tod_ns = 0
self.ts_tod_fns = 0
self.last_ts_96_s = 0 self.ts_rel_ns = 0
self.ts_rel_fns = 0
if self.ts_96 is not None: self.last_ts_tod_s = 0
self.ts_96.setimmediatevalue(0)
if self.ts_64 is not None: if self.ts_tod is not None:
self.ts_64.setimmediatevalue(0) self.ts_tod.setimmediatevalue(0)
if self.ts_rel is not None:
self.ts_rel.setimmediatevalue(0)
if self.pps is not None: 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): def get_ts_tod(self):
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns return (self.ts_tod_s, self.ts_tod_ns, self.ts_tod_fns)
def get_ts_96_ns(self): def get_ts_tod_96(self):
return self.ts_96_s*1e9+self.ts_96_ns+self.ts_96_fns/2**16 ts_s, ts_ns, ts_fns = self.get_ts_tod()
return (ts_s << 48) | (ts_ns << 16) | (ts_fns >> 16)
def get_ts_96_s(self): def get_ts_tod_ns(self):
return self.get_ts_96_ns()*1e-9 ts_s, ts_ns, ts_fns = self.get_ts_tod()
ns = Decimal(ts_fns) / Decimal(2**32)
ns = self.ctx.add(ns, Decimal(ts_ns))
return self.ctx.add(ns, Decimal(ts_s).scaleb(9))
def get_ts_64(self): def get_ts_tod_s(self):
return (self.ts_64_ns << 16) | self.ts_64_fns return self.get_ts_tod_ns().scaleb(-9, self.ctx)
def get_ts_64_ns(self): def get_ts_rel(self):
return self.get_ts_64()/2**16 return (self.ts_rel_ns, self.ts_rel_fns)
def get_ts_64_s(self): def get_ts_rel_64(self):
return self.get_ts_64()*1e-9 ts_ns, ts_fns = self.get_ts_rel()
return (ts_ns << 16) | (ts_fns >> 16)
def get_ts_rel_ns(self):
ts_ns, ts_fns = self.get_ts_rel()
return self.ctx.add(Decimal(ts_fns) / Decimal(2**32), Decimal(ts_ns))
def get_ts_rel_s(self):
return self.get_ts_rel_ns().scaleb(-9, self.ctx)
async def _run(self): async def _run(self):
clock_edge_event = RisingEdge(self.clock)
while True: while True:
await RisingEdge(self.clock) await clock_edge_event
self.ts_64_fns, self.ts_64_ns = math.modf(get_sim_time('ns')) ts_ns, ts_fns = self.ctx.divmod(Decimal(get_sim_time('fs')).scaleb(-6), Decimal(1))
self.ts_64_ns = int(self.ts_64_ns) self.ts_rel_ns = int(ts_ns.to_integral_value()) & 0xffffffffffff
self.ts_64_fns = int(self.ts_64_fns*0x10000) self.ts_rel_fns = int((ts_fns * Decimal(2**16)).to_integral_value())
self.ts_96_s, self.ts_96_ns = divmod(self.ts_64_ns, 1000000000) ts_s, ts_ns = self.ctx.divmod(ts_ns, Decimal(1000000000))
self.ts_96_fns = self.ts_64_fns
if self.ts_96 is not None: self.ts_tod_s = int(ts_s.scaleb(-9).to_integral_value())
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns self.ts_tod_ns = int(ts_ns.to_integral_value())
self.ts_tod_fns = self.ts_rel_fns
if self.ts_64 is not None: if self.ts_tod is not None:
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns self.ts_tod.value = (self.ts_tod_s << 48) | (self.ts_tod_ns << 16) | self.ts_tod_fns
if self.ts_rel is not None:
self.ts_rel.value = (self.ts_rel_ns << 16) | self.ts_rel_fns
if self.pps is not None: 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_tod_s != self.ts_tod_s)
self.last_ts_96_s = self.ts_96_s self.last_ts_tod_s = self.ts_tod_s

View File

@@ -33,7 +33,7 @@ class Reset:
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, bool(active_level))) cocotb.start_soon(self._run_reset(reset_signal, bool(active_level)))
self._update_reset() self._update_reset()

View File

@@ -61,6 +61,7 @@ class RgmiiSource(Reset):
self.current_frame = None self.current_frame = None
self.idle_event = Event() self.idle_event = Event()
self.idle_event.set() self.idle_event.set()
self.active_event = Event()
self.ifg = 12 self.ifg = 12
self.mii_mode = False self.mii_mode = False
@@ -90,6 +91,7 @@ class RgmiiSource(Reset):
frame = GmiiFrame(frame) frame = GmiiFrame(frame)
await self.queue.put(frame) await self.queue.put(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -99,6 +101,7 @@ class RgmiiSource(Reset):
frame = GmiiFrame(frame) frame = GmiiFrame(frame)
self.queue.put_nowait(frame) self.queue.put_nowait(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -126,6 +129,7 @@ class RgmiiSource(Reset):
frame.handle_tx_complete() frame.handle_tx_complete()
self.dequeue_event.set() self.dequeue_event.set()
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0 self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0 self.queue_occupancy_frames = 0
@@ -140,8 +144,8 @@ class RgmiiSource(Reset):
self._run_cr = None self._run_cr = None
self.active = False self.active = False
self.data <= 0 self.data.value = 0
self.ctrl <= 0 self.ctrl.value = 0
if self.current_frame: if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame) self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -150,10 +154,11 @@ class RgmiiSource(Reset):
if self.queue.empty(): if self.queue.empty():
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
@@ -161,22 +166,39 @@ class RgmiiSource(Reset):
frame_data = None frame_data = None
frame_error = None frame_error = None
ifg_cnt = 0 ifg_cnt = 0
in_ifg = False
self.active = False self.active = False
d = 0 d = 0
er = 0 er = 0
en = 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: 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 # send high nibble after rising edge, leading in to falling edge
self.data <= d >> 4 self.data.value = d >> 4
self.ctrl <= en ^ er self.ctrl.value = en ^ er
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
in_ifg = False
if ifg_cnt > 0: if ifg_cnt > 0:
# in IFG # in IFG
ifg_cnt -= 1 ifg_cnt -= 1
in_ifg = True
elif frame is None and not self.queue.empty(): elif frame is None and not self.queue.empty():
# send frame # send frame
@@ -221,6 +243,7 @@ class RgmiiSource(Reset):
if frame_offset >= len(frame_data): if frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1) ifg_cnt = max(self.ifg, 1)
in_ifg = True
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
frame.handle_tx_complete() frame.handle_tx_complete()
frame = None frame = None
@@ -230,13 +253,14 @@ class RgmiiSource(Reset):
er = 0 er = 0
en = 0 en = 0
self.active = False self.active = False
if not in_ifg and self.queue.empty():
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
await self.active_event.wait()
await FallingEdge(self.clock) elif self.enable is not None and not self.enable.value:
await enable_event
# send low nibble after falling edge, leading in to rising edge
self.data <= d & 0x0F
self.ctrl <= en
class RgmiiSink(Reset): class RgmiiSink(Reset):
@@ -330,7 +354,7 @@ class RgmiiSink(Reset):
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
@@ -339,21 +363,30 @@ class RgmiiSink(Reset):
dv_val = 0 dv_val = 0
er_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: 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 # capture low nibble on rising edge
d_val = self.data.value.integer d_val = self.data.value.integer
dv_val = self.ctrl.value.integer dv_val = self.ctrl.value.integer
await FallingEdge(self.clock) await clock_falling_edge_event
# capture high nibble on falling edge # capture high nibble on falling edge
d_val |= self.data.value.integer << 4 d_val |= self.data.value.integer << 4
er_val = dv_val ^ self.ctrl.value.integer er_val = dv_val ^ self.ctrl.value.integer
if self.enable is None or self.enable.value:
if frame is None: if frame is None:
if dv_val: if dv_val:
# start of frame # start of frame
@@ -406,6 +439,12 @@ class RgmiiSink(Reset):
frame.data.append(d_val) frame.data.append(d_val)
frame.error.append(er_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: class RgmiiPhy:
def __init__(self, txd, tx_ctl, tx_clk, rxd, rx_ctl, rx_clk, reset=None, def __init__(self, txd, tx_ctl, tx_clk, rxd, rx_ctl, rx_clk, reset=None,
@@ -434,11 +473,11 @@ class RgmiiPhy:
self._clock_cr.kill() self._clock_cr.kill()
if self.speed == 1000e6: 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.tx.mii_mode = False
self.rx.mii_mode = False self.rx.mii_mode = False
else: 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.tx.mii_mode = True
self.rx.mii_mode = True self.rx.mii_mode = True
@@ -448,6 +487,6 @@ class RgmiiPhy:
while True: while True:
await t await t
self.rx_clk <= 1 self.rx_clk.value = 1
await t await t
self.rx_clk <= 0 self.rx_clk.value = 0

View File

@@ -1 +1 @@
__version__ = "0.1.16" __version__ = "0.1.22"

View File

@@ -28,7 +28,7 @@ import zlib
import cocotb import cocotb
from cocotb.queue import Queue, QueueFull 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 cocotb.utils import get_sim_time
from .version import __version__ from .version import __version__
@@ -158,6 +158,7 @@ class XgmiiSource(Reset):
self.current_frame = None self.current_frame = None
self.idle_event = Event() self.idle_event = Event()
self.idle_event.set() self.idle_event.set()
self.active_event = Event()
self.enable_dic = True self.enable_dic = True
self.ifg = 12 self.ifg = 12
@@ -200,6 +201,7 @@ class XgmiiSource(Reset):
frame = XgmiiFrame(frame) frame = XgmiiFrame(frame)
await self.queue.put(frame) await self.queue.put(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -209,6 +211,7 @@ class XgmiiSource(Reset):
frame = XgmiiFrame(frame) frame = XgmiiFrame(frame)
self.queue.put_nowait(frame) self.queue.put_nowait(frame)
self.idle_event.clear() self.idle_event.clear()
self.active_event.set()
self.queue_occupancy_bytes += len(frame) self.queue_occupancy_bytes += len(frame)
self.queue_occupancy_frames += 1 self.queue_occupancy_frames += 1
@@ -236,6 +239,7 @@ class XgmiiSource(Reset):
frame.handle_tx_complete() frame.handle_tx_complete()
self.dequeue_event.set() self.dequeue_event.set()
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
self.queue_occupancy_bytes = 0 self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0 self.queue_occupancy_frames = 0
@@ -250,8 +254,8 @@ class XgmiiSource(Reset):
self._run_cr = None self._run_cr = None
self.active = False self.active = False
self.data <= 0 self.data.value = 0
self.ctrl <= 0 self.ctrl.value = 0
if self.current_frame: if self.current_frame:
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame) self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
@@ -260,10 +264,11 @@ class XgmiiSource(Reset):
if self.queue.empty(): if self.queue.empty():
self.idle_event.set() self.idle_event.set()
self.active_event.clear()
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
@@ -272,8 +277,14 @@ class XgmiiSource(Reset):
deficit_idle_cnt = 0 deficit_idle_cnt = 0
self.active = False 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: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
if ifg_cnt + deficit_idle_cnt > self.byte_lanes-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):
@@ -351,13 +362,20 @@ class XgmiiSource(Reset):
d_val |= XgmiiCtrl.IDLE << k*8 d_val |= XgmiiCtrl.IDLE << k*8
c_val |= 1 << k c_val |= 1 << k
self.data <= d_val self.data.value = d_val
self.ctrl <= c_val self.ctrl.value = c_val
else: else:
self.data <= self.idle_d self.data.value = self.idle_d
self.ctrl <= self.idle_c self.ctrl.value = self.idle_c
self.active = False self.active = False
if ifg_cnt == 0 and self.queue.empty():
self.idle_event.set() 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): class XgmiiSink(Reset):
@@ -450,19 +468,32 @@ class XgmiiSink(Reset):
else: else:
self.log.info("Reset de-asserted") self.log.info("Reset de-asserted")
if self._run_cr is None: 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): async def _run(self):
frame = None frame = None
self.active = False 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: while True:
await RisingEdge(self.clock) await clock_edge_event
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
data_val = self.data.value.integer
ctrl_val = self.ctrl.value.integer
for offset in range(self.byte_lanes): for offset in range(self.byte_lanes):
d_val = (self.data.value.integer >> (offset*8)) & 0xff d_val = (data_val >> (offset*8)) & 0xff
c_val = (self.ctrl.value.integer >> offset) & 1 c_val = (ctrl_val >> offset) & 1
if frame is None: if frame is None:
if c_val and d_val == XgmiiCtrl.START: if c_val and d_val == XgmiiCtrl.START:
@@ -495,3 +526,9 @@ class XgmiiSink(Reset):
frame.data.append(d_val) frame.data.append(d_val)
frame.ctrl.append(c_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

@@ -13,21 +13,22 @@ project_urls =
Source Code = https://github.com/alexforencich/cocotbext-eth Source Code = https://github.com/alexforencich/cocotbext-eth
download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master
long_description = file: README.md long_description = file: README.md
long-description-content-type = text/markdown long_description_content_type = text/markdown
platforms = any platforms = any
classifiers = classifiers =
Development Status :: 3 - Alpha Development Status :: 3 - Alpha
Programming Language :: Python :: 3 Framework :: cocotb
License :: OSI Approved :: MIT License License :: OSI Approved :: MIT License
Operating System :: OS Independent Operating System :: OS Independent
Programming Language :: Python :: 3
Topic :: Scientific/Engineering :: Electronic Design Automation (EDA) Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
[options] [options]
packages = find_namespace: packages = find_namespace:
python_requires = >=3.6 python_requires = >=3.6
install_requires = install_requires =
cocotb cocotb >= 1.6.0
cocotbext-axi cocotbext-axi >= 0.1.16
[options.extras_require] [options.extras_require]
test = test =
@@ -46,31 +47,40 @@ addopts =
# tox configuration # tox configuration
[tox:tox] [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] [gh-actions]
python = python =
3.6: py36
3.7: py37 3.7: py37
3.8: py38 3.8: py38
3.9: py39 3.9: py39
3.10: py310
3.11: py311
[testenv] [testenv]
setenv = setenv =
COVERAGE=1 COVERAGE=1
usedevelop = True
deps = deps =
pytest pytest == 7.2.1
pytest-xdist pytest-xdist == 3.1.0
cocotb-test cocotb == 1.7.2
coverage cocotb-bus == 0.2.1
pytest-cov cocotb-test == 0.2.4
cocotbext-axi == 0.1.20
coverage == 7.0.5
pytest-cov == 4.0.0
commands = 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' bash -c 'find . -type f -name "\.coverage" | xargs coverage combine --append'
coverage report
whitelist_externals = allowlist_externals =
bash bash
# combine if paths are different # combine if paths are different

View File

@@ -31,9 +31,19 @@ TOPLEVEL = $(DUT)
MODULE = $(DUT) MODULE = $(DUT)
VERILOG_SOURCES += $(DUT).v VERILOG_SOURCES += $(DUT).v
# module parameters
export PARAM_PTP_TS_WIDTH := 96
export PARAM_PTP_TAG_WIDTH := 16
export PARAM_AXIS_DATA_WIDTH := 64
export PARAM_AXIS_KEEP_WIDTH := $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
export PARAM_AXIS_TX_USER_WIDTH := $(shell expr $(PARAM_PTP_TAG_WIDTH) + 1 )
export PARAM_AXIS_RX_USER_WIDTH := $(shell expr $(PARAM_PTP_TS_WIDTH) + 1 )
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +51,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -28,6 +28,7 @@ import logging
import os import os
import cocotb_test.simulator import cocotb_test.simulator
import pytest
import cocotb import cocotb
from cocotb.clock import Clock from cocotb.clock import Clock
@@ -45,8 +46,20 @@ class TB:
self.log = logging.getLogger("cocotb.tb") self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG) self.log.setLevel(logging.DEBUG)
cocotb.fork(Clock(dut.tx_clk, 6.4, units="ns").start()) if len(dut.tx_axis_tdata) == 8:
cocotb.fork(Clock(dut.rx_clk, 6.4, units="ns").start()) clk_period = 8
elif len(dut.tx_axis_tdata) == 32:
clk_period = 3.102
elif len(dut.tx_axis_tdata) == 64:
if speed == 25e9:
clk_period = 2.56
else:
clk_period = 6.206
elif len(dut.tx_axis_tdata) == 512:
clk_period = 3.102
cocotb.start_soon(Clock(dut.tx_clk, clk_period, units="ns").start())
cocotb.start_soon(Clock(dut.rx_clk, clk_period, units="ns").start())
self.mac = EthMac( self.mac = EthMac(
tx_clk=dut.tx_clk, tx_clk=dut.tx_clk,
@@ -64,12 +77,12 @@ class TB:
) )
self.tx_ptp = PtpClockSimTime( self.tx_ptp = PtpClockSimTime(
ts_96=dut.tx_ptp_time, ts_tod=dut.tx_ptp_time,
clock=dut.tx_clk clock=dut.tx_clk
) )
self.rx_ptp = PtpClockSimTime( self.rx_ptp = PtpClockSimTime(
ts_96=dut.rx_ptp_time, ts_tod=dut.rx_ptp_time,
clock=dut.rx_clk clock=dut.rx_clk
) )
@@ -81,12 +94,12 @@ class TB:
self.dut.rx_rst.setimmediatevalue(0) self.dut.rx_rst.setimmediatevalue(0)
await RisingEdge(self.dut.tx_clk) await RisingEdge(self.dut.tx_clk)
await RisingEdge(self.dut.tx_clk) await RisingEdge(self.dut.tx_clk)
self.dut.tx_rst <= 1 self.dut.tx_rst.value = 1
self.dut.rx_rst <= 1 self.dut.rx_rst.value = 1
await RisingEdge(self.dut.tx_clk) await RisingEdge(self.dut.tx_clk)
await RisingEdge(self.dut.tx_clk) await RisingEdge(self.dut.tx_clk)
self.dut.tx_rst <= 0 self.dut.tx_rst.value = 0
self.dut.rx_rst <= 0 self.dut.rx_rst.value = 0
await RisingEdge(self.dut.tx_clk) await RisingEdge(self.dut.tx_clk)
await RisingEdge(self.dut.tx_clk) await RisingEdge(self.dut.tx_clk)
@@ -157,12 +170,21 @@ def incrementing_payload(length):
if cocotb.SIM_NAME: if cocotb.SIM_NAME:
if len(cocotb.top.tx_axis_tdata) == 8:
speed = [100e6, 1e9]
elif len(cocotb.top.tx_axis_tdata) == 32:
speed = [10e9]
elif len(cocotb.top.tx_axis_tdata) == 64:
speed = [10e9, 25e9]
elif len(cocotb.top.tx_axis_tdata) == 512:
speed = [100e9]
for test in [run_test_tx, run_test_rx]: for test in [run_test_tx, run_test_rx]:
factory = TestFactory(test) factory = TestFactory(test)
factory.add_option("payload_lengths", [size_list]) factory.add_option("payload_lengths", [size_list])
factory.add_option("payload_data", [incrementing_payload]) factory.add_option("payload_data", [incrementing_payload])
factory.add_option("speed", [10e9, 1e9]) factory.add_option("speed", speed)
factory.generate_tests() factory.generate_tests()
@@ -171,7 +193,8 @@ if cocotb.SIM_NAME:
tests_dir = os.path.dirname(__file__) tests_dir = os.path.dirname(__file__)
def test_eth_mac(request): @pytest.mark.parametrize("data_width", [8, 32, 64, 512])
def test_eth_mac(request, data_width):
dut = "test_eth_mac" dut = "test_eth_mac"
module = os.path.splitext(os.path.basename(__file__))[0] module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut toplevel = dut
@@ -182,6 +205,13 @@ def test_eth_mac(request):
parameters = {} parameters = {}
parameters['PTP_TS_WIDTH'] = 96
parameters['PTP_TAG_WIDTH'] = 16
parameters['AXIS_DATA_WIDTH'] = data_width
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
parameters['AXIS_TX_USER_WIDTH'] = parameters['PTP_TAG_WIDTH']+1
parameters['AXIS_RX_USER_WIDTH'] = parameters['PTP_TS_WIDTH']+1
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", sim_build = os.path.join(tests_dir, "sim_build",

View File

@@ -29,29 +29,37 @@ THE SOFTWARE.
/* /*
* Ethernet MAC model test * Ethernet MAC model test
*/ */
module test_eth_mac module test_eth_mac #
(
parameter PTP_TS_WIDTH = 96,
parameter PTP_TAG_WIDTH = 16,
parameter AXIS_DATA_WIDTH = 64,
parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8),
parameter AXIS_TX_USER_WIDTH = PTP_TAG_WIDTH+1,
parameter AXIS_RX_USER_WIDTH = PTP_TS_WIDTH+1
)
( (
inout wire tx_clk, inout wire tx_clk,
inout wire tx_rst, inout wire tx_rst,
inout wire [63:0] tx_axis_tdata, inout wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata,
inout wire [7:0] tx_axis_tkeep, inout wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep,
inout wire tx_axis_tlast, inout wire tx_axis_tlast,
inout wire [16:0] tx_axis_tuser, inout wire [AXIS_TX_USER_WIDTH-1:0] tx_axis_tuser,
inout wire tx_axis_tvalid, inout wire tx_axis_tvalid,
inout wire tx_axis_tready, inout wire tx_axis_tready,
inout wire [95:0] tx_ptp_time, inout wire [PTP_TS_WIDTH-1:0] tx_ptp_time,
inout wire [95:0] tx_ptp_ts, inout wire [PTP_TS_WIDTH-1:0] tx_ptp_ts,
inout wire [15:0] tx_ptp_ts_tag, inout wire [PTP_TAG_WIDTH-1:0] tx_ptp_ts_tag,
inout wire tx_ptp_ts_valid, inout wire tx_ptp_ts_valid,
inout wire rx_clk, inout wire rx_clk,
inout wire rx_rst, inout wire rx_rst,
inout wire [63:0] rx_axis_tdata, inout wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata,
inout wire [7:0] rx_axis_tkeep, inout wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep,
inout wire rx_axis_tlast, inout wire rx_axis_tlast,
inout wire [96:0] rx_axis_tuser, inout wire [AXIS_RX_USER_WIDTH-1:0] rx_axis_tuser,
inout wire rx_axis_tvalid, inout wire rx_axis_tvalid,
inout wire [95:0] rx_ptp_time inout wire [PTP_TS_WIDTH-1:0] rx_ptp_time
); );
endmodule endmodule

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -47,7 +47,7 @@ class TB:
self._enable_generator = None self._enable_generator = None
self._enable_cr = 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, self.source = GmiiSource(dut.gmii_d, dut.gmii_er, dut.gmii_en,
dut.clk, dut.rst, dut.gmii_clk_en, dut.gmii_mii_sel) dut.clk, dut.rst, dut.gmii_clk_en, dut.gmii_mii_sel)
@@ -61,10 +61,10 @@ class TB:
self.dut.rst.setimmediatevalue(0) self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
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)
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)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
@@ -76,15 +76,17 @@ class TB:
self._enable_generator = generator self._enable_generator = generator
if self._enable_generator is not None: 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): def clear_enable_generator(self):
self.set_enable_generator(None) self.set_enable_generator(None)
async def _run_enable(self): async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator: for val in self._enable_generator:
self.dut.gmii_clk_en <= val self.dut.gmii_clk_en.value = val
await RisingEdge(self.dut.clk) await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False): 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 = TB(dut)
tb.source.ifg = ifg 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: if enable_gen is not None:
tb.set_enable_generator(enable_gen()) tb.set_enable_generator(enable_gen())

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -44,7 +44,7 @@ class TB:
self.log = logging.getLogger("cocotb.tb") self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG) 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, 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) 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) self.dut.phy_rst.setimmediatevalue(0)
await RisingEdge(self.dut.phy_tx_clk) await RisingEdge(self.dut.phy_tx_clk)
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)
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)
await RisingEdge(self.dut.phy_tx_clk) await RisingEdge(self.dut.phy_tx_clk)

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -47,7 +47,7 @@ class TB:
self._enable_generator = None self._enable_generator = None
self._enable_cr = 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, self.source = MiiSource(dut.mii_d, dut.mii_er, dut.mii_en,
dut.clk, dut.rst, dut.mii_clk_en) dut.clk, dut.rst, dut.mii_clk_en)
@@ -60,10 +60,10 @@ class TB:
self.dut.rst.setimmediatevalue(0) self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
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)
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)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
@@ -75,15 +75,17 @@ class TB:
self._enable_generator = generator self._enable_generator = generator
if self._enable_generator is not None: 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): def clear_enable_generator(self):
self.set_enable_generator(None) self.set_enable_generator(None)
async def _run_enable(self): async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator: for val in self._enable_generator:
self.dut.mii_clk_en <= val self.dut.mii_clk_en.value = val
await RisingEdge(self.dut.clk) await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None): async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None):

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -55,10 +55,10 @@ class TB:
self.dut.phy_rst.setimmediatevalue(0) self.dut.phy_rst.setimmediatevalue(0)
await RisingEdge(self.dut.phy_tx_clk) await RisingEdge(self.dut.phy_tx_clk)
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)
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)
await RisingEdge(self.dut.phy_tx_clk) await RisingEdge(self.dut.phy_tx_clk)

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -25,12 +25,13 @@ THE SOFTWARE.
import logging import logging
import os import os
from decimal import Decimal
import cocotb_test.simulator import cocotb_test.simulator
import cocotb import cocotb
from cocotb.clock import Clock from cocotb.clock import Clock
from cocotb.triggers import RisingEdge from cocotb.triggers import RisingEdge, ClockCycles
from cocotb.utils import get_sim_time from cocotb.utils import get_sim_time
from cocotbext.eth import PtpClock from cocotbext.eth import PtpClock
@@ -43,11 +44,11 @@ class TB:
self.log = logging.getLogger("cocotb.tb") self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG) 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( self.ptp_clock = PtpClock(
ts_96=dut.ts_96, ts_tod=dut.ts_tod,
ts_64=dut.ts_64, ts_rel=dut.ts_rel,
ts_step=dut.ts_step, ts_step=dut.ts_step,
pps=dut.pps, pps=dut.pps,
clock=dut.clk, clock=dut.clk,
@@ -59,13 +60,21 @@ class TB:
self.dut.rst.setimmediatevalue(0) self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
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)
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)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
def get_ts_tod_ns(self):
ts = self.dut.ts_tod.value.integer
return Decimal(ts >> 48).scaleb(9) + (Decimal(ts & 0xffffffffffff) / Decimal(2**16))
def get_ts_rel_ns(self):
ts = self.dut.ts_rel.value.integer
return Decimal(ts) / Decimal(2**16)
@cocotb.test() @cocotb.test()
async def run_default_rate(dut): async def run_default_rate(dut):
@@ -75,32 +84,32 @@ async def run_default_rate(dut):
await tb.reset() await tb.reset()
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
for k in range(10000): await ClockCycles(dut.clk, 10000)
await RisingEdge(dut.clk)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -113,41 +122,41 @@ async def run_load_timestamps(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.set_ts_96(12345678) tb.ptp_clock.set_ts_tod_ns(12345678)
tb.ptp_clock.set_ts_64(12345678) tb.ptp_clock.set_ts_rel_ns(12345678)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
assert dut.ts_96.value.integer == 12345678+((tb.ptp_clock.period_ns << 16) + tb.ptp_clock.period_fns) assert dut.ts_tod.value.integer == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
assert dut.ts_64.value.integer == 12345678+((tb.ptp_clock.period_ns << 16) + tb.ptp_clock.period_fns) assert dut.ts_rel.value.integer == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
assert dut.ts_step.value.integer == 1 assert dut.ts_step.value.integer == 1
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
for k in range(2000): await ClockCycles(dut.clk, 2000)
await RisingEdge(dut.clk)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -160,15 +169,15 @@ async def run_seconds_increment(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.set_ts_96(999990000*2**16) tb.ptp_clock.set_ts_tod_ns(999990000)
tb.ptp_clock.set_ts_64(999990000*2**16) tb.ptp_clock.set_ts_rel_ns(999990000)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
saw_pps = False saw_pps = False
@@ -177,30 +186,31 @@ async def run_seconds_increment(dut):
if dut.pps.value.integer: if dut.pps.value.integer:
saw_pps = True saw_pps = True
assert dut.ts_96.value.integer >> 48 == 1 assert dut.ts_tod.value.integer >> 48 == 1
assert dut.ts_96.value.integer & 0xffffffffffff < 10*2**16 assert dut.ts_tod.value.integer & 0xffffffffffff < 10*2**16
assert saw_pps assert saw_pps
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -213,36 +223,35 @@ async def run_frequency_adjustment(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.period_ns = 0x6 tb.ptp_clock.set_period(0x6, 0x66240000)
tb.ptp_clock.period_fns = 0x6624
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
for k in range(10000): await ClockCycles(dut.clk, 10000)
await RisingEdge(dut.clk)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta * 6.4/(6+(0x6624+2/5)/2**16) tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6624+2/5)/2**16) tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -255,37 +264,35 @@ async def run_drift_adjustment(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.drift_ns = 0 tb.ptp_clock.set_drift(20000, 5)
tb.ptp_clock.drift_fns = 20
tb.ptp_clock.drift_rate = 5
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
for k in range(10000): await ClockCycles(dut.clk, 10000)
await RisingEdge(dut.clk)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta * 6.4/(6+(0x6666+20/5)/2**16) tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6666+20/5)/2**16) tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)

View File

@@ -34,8 +34,8 @@ module test_ptp_clock
input wire clk, input wire clk,
input wire rst, input wire rst,
inout wire [95:0] ts_96, inout wire [95:0] ts_tod,
inout wire [63:0] ts_64, inout wire [63:0] ts_rel,
inout wire ts_step, inout wire ts_step,
inout wire pps inout wire pps
); );

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -25,12 +25,13 @@ THE SOFTWARE.
import logging import logging
import os import os
from decimal import Decimal
import cocotb_test.simulator import cocotb_test.simulator
import cocotb import cocotb
from cocotb.clock import Clock from cocotb.clock import Clock
from cocotb.triggers import RisingEdge from cocotb.triggers import RisingEdge, ClockCycles
from cocotb.utils import get_sim_time from cocotb.utils import get_sim_time
from cocotbext.eth import PtpClockSimTime from cocotbext.eth import PtpClockSimTime
@@ -43,15 +44,23 @@ class TB:
self.log = logging.getLogger("cocotb.tb") self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG) 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( self.ptp_clock = PtpClockSimTime(
ts_96=dut.ts_96, ts_tod=dut.ts_tod,
ts_64=dut.ts_64, ts_rel=dut.ts_rel,
pps=dut.pps, pps=dut.pps,
clock=dut.clk clock=dut.clk
) )
def get_ts_tod_ns(self):
ts = self.dut.ts_tod.value.integer
return Decimal(ts >> 48).scaleb(9) + (Decimal(ts & 0xffffffffffff) / Decimal(2**16))
def get_ts_rel_ns(self):
ts = self.dut.ts_rel.value.integer
return Decimal(ts) / Decimal(2**16)
@cocotb.test() @cocotb.test()
async def run_test(dut): async def run_test(dut):
@@ -62,32 +71,32 @@ async def run_test(dut):
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
for k in range(10000): await ClockCycles(dut.clk, 10000)
await RisingEdge(dut.clk)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)

View File

@@ -33,8 +33,8 @@ module test_ptp_clock_sim_time
( (
input wire clk, input wire clk,
inout wire [95:0] ts_96, inout wire [95:0] ts_tod,
inout wire [63:0] ts_64, inout wire [63:0] ts_rel,
inout wire pps inout wire pps
); );

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -47,7 +47,7 @@ class TB:
self._enable_generator = None self._enable_generator = None
self._enable_cr = 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.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) 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) self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
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)
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)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
@@ -74,15 +74,17 @@ class TB:
self._enable_generator = generator self._enable_generator = generator
if self._enable_generator is not None: 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): def clear_enable_generator(self):
self.set_enable_generator(None) self.set_enable_generator(None)
async def _run_enable(self): async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator: for val in self._enable_generator:
self.dut.rgmii_clk_en <= val self.dut.rgmii_clk_en.value = val
await RisingEdge(self.dut.clk) await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False): 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 = TB(dut)
tb.source.ifg = ifg 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: if enable_gen is not None:
tb.set_enable_generator(enable_gen()) tb.set_enable_generator(enable_gen())

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump COMPILE_ARGS += -s iverilog_dump
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst
endif endif

View File

@@ -45,11 +45,11 @@ class TB:
self.log.setLevel(logging.DEBUG) self.log.setLevel(logging.DEBUG)
if speed == 1000e6: 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: 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: 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, 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) 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) self.dut.phy_rst.setimmediatevalue(0)
await RisingEdge(self.dut.phy_tx_clk) await RisingEdge(self.dut.phy_tx_clk)
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)
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)
await RisingEdge(self.dut.phy_tx_clk) await RisingEdge(self.dut.phy_tx_clk)

View File

@@ -32,14 +32,13 @@ MODULE = $(DUT)
VERILOG_SOURCES += $(DUT).v VERILOG_SOURCES += $(DUT).v
# module parameters # module parameters
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 )
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH) COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v VERILOG_SOURCES += iverilog_dump.v
@@ -48,8 +47,7 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator) else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH) COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
ifeq ($(WAVES), 1) ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst COMPILE_ARGS += --trace-fst

View File

@@ -48,7 +48,7 @@ class TB:
self._enable_generator = None self._enable_generator = None
self._enable_cr = 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.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) 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) self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
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)
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)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
@@ -74,15 +74,17 @@ class TB:
self._enable_generator = generator self._enable_generator = generator
if self._enable_generator is not None: 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): def clear_enable_generator(self):
self.set_enable_generator(None) self.set_enable_generator(None)
async def _run_enable(self): async def _run_enable(self):
clock_edge_event = RisingEdge(self.dut.clk)
for val in self._enable_generator: for val in self._enable_generator:
self.dut.xgmii_clk_en <= val self.dut.xgmii_clk_en.value = val
await RisingEdge(self.dut.clk) await clock_edge_event
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_dic=True, async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_dic=True,