Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b75a9a1a7 | ||
|
|
7d8d214b57 | ||
|
|
c44f928bea | ||
|
|
357dd26aae | ||
|
|
6131079494 | ||
|
|
9079ca34f2 | ||
|
|
08fd179dac | ||
|
|
1fa122e4ee | ||
|
|
4b13e61dc8 | ||
|
|
72f7290488 | ||
|
|
6d1afe0904 | ||
|
|
d61362e79b | ||
|
|
2bfe8a0e50 | ||
|
|
9c88b0440e | ||
|
|
37b23c358b | ||
|
|
dd35d734f9 | ||
|
|
45ee1193cb | ||
|
|
5caafbb9e7 | ||
|
|
2d4450e048 | ||
|
|
c5d28182c4 | ||
|
|
79991205b5 | ||
|
|
a22123649c | ||
|
|
b5ba332ecc | ||
|
|
eb62e43fd1 | ||
|
|
5c4ef258ac | ||
|
|
6c5845fad3 | ||
|
|
32f6e449c0 | ||
|
|
2af7852006 | ||
|
|
3325568406 | ||
|
|
6a35c31b4b | ||
|
|
73fe54705f | ||
|
|
448451e274 | ||
|
|
21c2c05c57 | ||
|
|
ab84a3b100 | ||
|
|
0f3060b9ba | ||
|
|
2ee51890f5 | ||
|
|
fbdf4149b3 | ||
|
|
03156ff759 | ||
|
|
8956de42b5 |
6
.github/workflows/regression-tests.yml
vendored
6
.github/workflows/regression-tests.yml
vendored
@@ -9,13 +9,13 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
|
||||
123
README.md
123
README.md
@@ -467,6 +467,7 @@ To use these modules, import the one you need and connect it to the DUT:
|
||||
tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
|
||||
tx_ptp_time=dut.tx_ptp_time,
|
||||
tx_ptp_ts=dut.tx_ptp_ts,
|
||||
tx_ptp_ts_tag=dut.tx_ptp_ts_tag,
|
||||
tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
|
||||
rx_clk=dut.rx_clk,
|
||||
rx_rst=dut.rx_rst,
|
||||
@@ -492,6 +493,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
|
||||
|
||||
data = await mac.tx.recv()
|
||||
|
||||
PTP timestamping requires free-running PTP clocks driving the PTP time inputs, synchronous with the corresponding MAC clocks. The values of these fields are then captured when the frame SFD is transferred and returned either on tuser (for received frames) or on a separate streaming interface (for transmitted frames). Additionally, on the transmit path, a tag value from tuser is returned along with the timestamp.
|
||||
|
||||
#### Signals
|
||||
|
||||
* `tdata`: payload data, must be a multiple of 8 bits
|
||||
@@ -499,9 +502,10 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
|
||||
* `tready`: indicates sink is ready for data (tx only)
|
||||
* `tlast`: marks the last cycle of a frame
|
||||
* `tkeep`: qualifies data byte, data bus width must be evenly divisible by `tkeep` signal width
|
||||
* `tuser`: user data, carries frame error mark and captured receive PTP timestamp
|
||||
* `ptp_time`: PTP time input from PHC, captured into `ptp_timestamp` field coincident with transfer of frame SFD and output on `ptp_ts`
|
||||
* `tuser`: user data, carries frame error mark and captured receive PTP timestamp (RX) or PTP timestamp tag (TX)
|
||||
* `ptp_time`: PTP time input from PHC, captured into `ptp_timestamp` field coincident with transfer of frame SFD and output on `ptp_ts` (TX) or `tuser` (RX)
|
||||
* `ptp_ts`: captured transmit PTP timestamp
|
||||
* `ptp_ts_tag`: captured transmit PTP timestamp tag
|
||||
* `ptp_ts_valid`: qualifies captured transmit PTP timestamp
|
||||
|
||||
#### Constructor parameters (`EthMacRx` and `EthMacTx`):
|
||||
@@ -510,7 +514,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
|
||||
* _clock_: clock signal
|
||||
* _reset_: reset signal (optional)
|
||||
* _ptp_time_: PTP time input from PHC (optional)
|
||||
* _ptp_ts_: PTP timestamp output (optional) (tx)
|
||||
* _ptp_ts_: PTP timestamp (optional) (tx)
|
||||
* _ptp_ts_tag_: PTP timestamp tag (optional) (tx)
|
||||
* _ptp_ts_valid_: PTP timestamp valid (optional) (tx)
|
||||
* _reset_active_level_: reset active level (optional, default `True`)
|
||||
* _ifg_: IFG size in byte times (optional, default `12`)
|
||||
@@ -522,7 +527,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
|
||||
* _tx_clk_: transmit clock
|
||||
* _tx_rst_: transmit reset (optional)
|
||||
* _tx_ptp_time_: transmit PTP time input from PHC (optional)
|
||||
* _tx_ptp_ts_: transmit PTP timestamp output (optional)
|
||||
* _tx_ptp_ts_: transmit PTP timestamp (optional)
|
||||
* _tx_ptp_ts_tag_: transmit PTP timestamp tag (optional)
|
||||
* _tx_ptp_ts_valid_: transmit PTP timestamp valid (optional)
|
||||
* _rx_bus_: `AxiStreamBus` object containing receive AXI stream interface signals
|
||||
* _rx_clk_: receive clock
|
||||
@@ -565,6 +571,7 @@ Attributes:
|
||||
* `sim_time_start`: simulation time of first transfer cycle of frame.
|
||||
* `sim_time_sfd`: simulation time at which the SFD was transferred.
|
||||
* `sim_time_end`: simulation time of last transfer cycle of frame.
|
||||
* `ptp_tag`: PTP timestamp tag for transmitted frames.
|
||||
* `ptp_timestamp`: captured value of `ptp_time` at frame SFD
|
||||
* `tx_complete`: event or callable triggered when frame is transmitted.
|
||||
|
||||
@@ -578,15 +585,15 @@ Methods:
|
||||
|
||||
### 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:
|
||||
|
||||
from cocotbext.eth import PtpClock
|
||||
|
||||
ptp_clock = PtpClock(
|
||||
ts_96=dut.ts_96,
|
||||
ts_64=dut.ts_64,
|
||||
ts_tod=dut.ts_tod,
|
||||
ts_rel=dut.ts_rel,
|
||||
ts_step=dut.ts_step,
|
||||
pps=dut.pps,
|
||||
clock=dut.clk,
|
||||
@@ -596,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.
|
||||
|
||||
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
|
||||
|
||||
* `ts_96`: 96-bit timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
|
||||
* `ts_64`: 64-bit timestamp (48 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_rel`: 64-bit relative timestamp (48 bit ns, 16 bit fractional ns)
|
||||
* `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:
|
||||
|
||||
* _ts_96_: 96-bit timestamp signal (optional)
|
||||
* _ts_64_: 64-bit timestamp signal (optional)
|
||||
* _ts_tod_: 96-bit time-of-day timestamp signal (optional)
|
||||
* _ts_rel_: 64-bit relative timestamp signal (optional)
|
||||
* _ts_step_: timestamp step signal (optional)
|
||||
* _pps_: pulse-per-second signal (optional)
|
||||
* _clock_: clock
|
||||
@@ -616,74 +627,84 @@ Once the clock is instantiated, it will generate a continuous stream of monotoni
|
||||
|
||||
#### Attributes:
|
||||
|
||||
* _ts_96_s_: current 96-bit timestamp seconds field
|
||||
* _ts_96_ns_: current 96-bit timestamp ns field
|
||||
* _ts_96_fns_: current 96-bit timestamp fractional ns field
|
||||
* _ts_64_ns_: current 64-bit timestamp ns field
|
||||
* _ts_64_fns_: current 64-bit timestamp fractional ns field
|
||||
* _ts_tod_s_: current 96-bit ToD timestamp seconds field
|
||||
* _ts_tod_ns_: current 96-bit ToD timestamp ns field
|
||||
* _ts_tod_fns_: current 96-bit ToD timestamp fractional ns field
|
||||
* _ts_rel_ns_: current 64-bit relative timestamp ns field
|
||||
* _ts_rel_fns_: current 64-bit relative timestamp fractional ns field
|
||||
|
||||
#### Methods
|
||||
|
||||
* `set_period(ns, fns)`: set clock period from separate fields
|
||||
* `set_drift(ns, fns, rate)`: set clock drift from separate fields
|
||||
* `set_period_ns(t)`: set clock period in ns (float)
|
||||
* `get_period_ns()`: return current clock period in ns (float)
|
||||
* `set_ts_96(ts_s, ts_ns=None, ts_fns=None)`: set 96-bit timestamp from integer or from separate fields
|
||||
* `set_ts_96_ns(t)`: set 96-bit timestamp from ns (float)
|
||||
* `set_ts_96_s(t)`: set 96-bit timestamp from seconds (float)
|
||||
* `get_ts_96()`: return current 96-bit timestamp as an integer
|
||||
* `get_ts_96_ns()`: return current 96-bit timestamp in ns (float)
|
||||
* `get_ts_96_s()`: return current 96-bit timestamp in seconds (float)
|
||||
* `set_ts_64(ts_ns, ts_fns=None)`: set 64-bit timestamp from integer or from separate fields
|
||||
* `set_ts_64_ns(t)`: set 64-bit timestamp from ns (float)
|
||||
* `set_ts_64_s(t)`: set 64-bit timestamp from seconds (float)
|
||||
* `get_ts_64()`: return current 64-bit timestamp as an integer
|
||||
* `get_ts_64_ns()`: return current 64-bit timestamp in ns (float)
|
||||
* `get_ts_64_s()`: return current 64-bit timestamp in seconds (float)
|
||||
* `set_drift(num, denom)`: set clock drift from separate fields
|
||||
* `set_period_ns(t)`: set clock period and drift in ns (Decimal)
|
||||
* `get_period_ns()`: return current clock period in ns (Decimal)
|
||||
* `set_ts_tod(ts_s, ts_ns, ts_fns)`: set 96-bit ToD timestamp from separate fields
|
||||
* `set_ts_tod_96(ts)`: set 96-bit ToD timestamp from integer
|
||||
* `set_ts_tod_ns(t)`: set 96-bit ToD timestamp from ns (Decimal)
|
||||
* `set_ts_tod_s(t)`: set 96-bit ToD timestamp from seconds (Decimal)
|
||||
* `set_ts_tod_sim_time()`: set 96-bit ToD timestamp from sim time
|
||||
* `get_ts_tod()`: return current 96-bit ToD timestamp as separate fields
|
||||
* `get_ts_tod_96()`: return current 96-bit ToD timestamp as an integer
|
||||
* `get_ts_tod_ns()`: return current 96-bit ToD timestamp in ns (Decimal)
|
||||
* `get_ts_tod_s()`: return current 96-bit ToD timestamp in seconds (Decimal)
|
||||
* `set_ts_rel(ts_ns, ts_fns)`: set 64-bit relative timestamp from separate fields
|
||||
* `set_ts_rel_64(ts)`: set 64-bit relative timestamp from integer
|
||||
* `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)
|
||||
|
||||
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:
|
||||
|
||||
from cocotbext.eth import PtpClockSimTime
|
||||
|
||||
ptp_clock = PtpClockSimTime(
|
||||
ts_96=dut.ts_96,
|
||||
ts_64=dut.ts_64,
|
||||
ts_tod=dut.ts_tod,
|
||||
ts_rel=dut.ts_rel,
|
||||
pps=dut.pps,
|
||||
clock=dut.clk
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
* `ts_96`: 96-bit timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
|
||||
* `ts_64`: 64-bit timestamp (48 bit ns, 16 bit fractional ns)
|
||||
* `pps`: pulse-per-second output, pulsed when ts_96 seconds field increments
|
||||
* `ts_tod`: 96-bit time-of-day timestamp (48 bit seconds, 32 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_tod seconds field increments
|
||||
|
||||
#### Constructor parameters:
|
||||
|
||||
* _ts_96_: 96-bit timestamp signal (optional)
|
||||
* _ts_64_: 64-bit timestamp signal (optional)
|
||||
* _ts_tod_: 96-bit time-of-day timestamp signal (optional)
|
||||
* _ts_rel_: 64-bit relative timestamp signal (optional)
|
||||
* _pps_: pulse-per-second signal (optional)
|
||||
* _clock_: clock
|
||||
|
||||
#### Attributes:
|
||||
|
||||
* _ts_96_s_: current 96-bit timestamp seconds field
|
||||
* _ts_96_ns_: current 96-bit timestamp ns field
|
||||
* _ts_96_fns_: current 96-bit timestamp fractional ns field
|
||||
* _ts_64_ns_: current 64-bit timestamp ns field
|
||||
* _ts_64_fns_: current 64-bit timestamp fractional ns field
|
||||
* _ts_tod_s_: current 96-bit ToD timestamp seconds field
|
||||
* _ts_tod_ns_: current 96-bit ToD timestamp ns field
|
||||
* _ts_tod_fns_: current 96-bit ToD timestamp fractional ns field
|
||||
* _ts_rel_ns_: current 64-bit relative timestamp ns field
|
||||
* _ts_rel_fns_: current 64-bit relative timestamp fractional ns field
|
||||
|
||||
#### Methods
|
||||
|
||||
* `get_ts_96()`: return current 96-bit timestamp as an integer
|
||||
* `get_ts_96_ns()`: return current 96-bit timestamp in ns (float)
|
||||
* `get_ts_96_s()`: return current 96-bit timestamp in seconds (float)
|
||||
* `get_ts_64()`: return current 64-bit timestamp as an integer
|
||||
* `get_ts_64_ns()`: return current 64-bit timestamp in ns (float)
|
||||
* `get_ts_64_s()`: return current 64-bit timestamp in seconds (float)
|
||||
* `get_ts_tod()`: return current 96-bit ToD timestamp as separate fields
|
||||
* `get_ts_tod_96()`: return current 96-bit ToD timestamp as an integer
|
||||
* `get_ts_tod_ns()`: return current 96-bit ToD timestamp in ns (Decimal)
|
||||
* `get_ts_tod_s()`: return current 96-bit ToD timestamp in seconds (Decimal)
|
||||
* `get_ts_rel()`: return current 64-bit relative timestamp as separate fields
|
||||
* `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)
|
||||
|
||||
@@ -42,12 +42,12 @@ class XgmiiCtrl(enum.IntEnum):
|
||||
TERM = 0xfd
|
||||
ERROR = 0xfe
|
||||
SEQ_OS = 0x9c
|
||||
RES0 = 0x1c
|
||||
RES1 = 0x3c
|
||||
RES2 = 0x7c
|
||||
RES3 = 0xbc
|
||||
RES4 = 0xdc
|
||||
RES5 = 0xf7
|
||||
RES_0 = 0x1c
|
||||
RES_1 = 0x3c
|
||||
RES_2 = 0x7c
|
||||
RES_3 = 0xbc
|
||||
RES_4 = 0xdc
|
||||
RES_5 = 0xf7
|
||||
SIG_OS = 0x5c
|
||||
|
||||
|
||||
@@ -93,3 +93,41 @@ class BaseRBlockType(enum.IntEnum):
|
||||
TERM_5 = 0xd2 # C7 C6 D4 D3 D2 D1 D0 BT
|
||||
TERM_6 = 0xe1 # C7 D5 D4 D3 D2 D1 D0 BT
|
||||
TERM_7 = 0xff # D6 D5 D4 D3 D2 D1 D0 BT
|
||||
|
||||
|
||||
xgmii_ctrl_to_baser_mapping = {
|
||||
XgmiiCtrl.IDLE: BaseRCtrl.IDLE,
|
||||
XgmiiCtrl.LPI: BaseRCtrl.LPI,
|
||||
XgmiiCtrl.ERROR: BaseRCtrl.ERROR,
|
||||
XgmiiCtrl.RES_0: BaseRCtrl.RES_0,
|
||||
XgmiiCtrl.RES_1: BaseRCtrl.RES_1,
|
||||
XgmiiCtrl.RES_2: BaseRCtrl.RES_2,
|
||||
XgmiiCtrl.RES_3: BaseRCtrl.RES_3,
|
||||
XgmiiCtrl.RES_4: BaseRCtrl.RES_4,
|
||||
XgmiiCtrl.RES_5: BaseRCtrl.RES_5,
|
||||
}
|
||||
|
||||
|
||||
baser_ctrl_to_xgmii_mapping = {
|
||||
BaseRCtrl.IDLE: XgmiiCtrl.IDLE,
|
||||
BaseRCtrl.LPI: XgmiiCtrl.LPI,
|
||||
BaseRCtrl.ERROR: XgmiiCtrl.ERROR,
|
||||
BaseRCtrl.RES_0: XgmiiCtrl.RES_0,
|
||||
BaseRCtrl.RES_1: XgmiiCtrl.RES_1,
|
||||
BaseRCtrl.RES_2: XgmiiCtrl.RES_2,
|
||||
BaseRCtrl.RES_3: XgmiiCtrl.RES_3,
|
||||
BaseRCtrl.RES_4: XgmiiCtrl.RES_4,
|
||||
BaseRCtrl.RES_5: XgmiiCtrl.RES_5,
|
||||
}
|
||||
|
||||
|
||||
block_type_term_lane_mapping = {
|
||||
BaseRBlockType.TERM_0: 0,
|
||||
BaseRBlockType.TERM_1: 1,
|
||||
BaseRBlockType.TERM_2: 2,
|
||||
BaseRBlockType.TERM_3: 3,
|
||||
BaseRBlockType.TERM_4: 4,
|
||||
BaseRBlockType.TERM_5: 5,
|
||||
BaseRBlockType.TERM_6: 6,
|
||||
BaseRBlockType.TERM_7: 7,
|
||||
}
|
||||
|
||||
@@ -43,23 +43,25 @@ AxiStreamBus, AxiStreamTransaction, AxiStreamSource, AxiStreamSink, AxiStreamMon
|
||||
|
||||
|
||||
class EthMacFrame:
|
||||
def __init__(self, data=None, tx_complete=None):
|
||||
self.data = bytearray()
|
||||
def __init__(self, data=b'', tx_complete=None):
|
||||
self.data = b''
|
||||
self.sim_time_start = None
|
||||
self.sim_time_sfd = None
|
||||
self.sim_time_end = None
|
||||
self.ptp_timestamp = None
|
||||
self.ptp_tag = None
|
||||
self.tx_complete = None
|
||||
|
||||
if type(data) is EthMacFrame:
|
||||
self.data = bytearray(data.data)
|
||||
self.data = bytes(data.data)
|
||||
self.sim_time_start = data.sim_time_start
|
||||
self.sim_time_sfd = data.sim_time_sfd
|
||||
self.sim_time_end = data.sim_time_end
|
||||
self.ptp_timestamp = data.ptp_timestamp
|
||||
self.ptp_tag = data.ptp_tag
|
||||
self.tx_complete = data.tx_complete
|
||||
else:
|
||||
self.data = bytearray(data)
|
||||
self.data = bytes(data)
|
||||
|
||||
if tx_complete is not None:
|
||||
self.tx_complete = tx_complete
|
||||
@@ -104,7 +106,8 @@ class EthMacFrame:
|
||||
f"sim_time_start={self.sim_time_start!r}, "
|
||||
f"sim_time_sfd={self.sim_time_sfd!r}, "
|
||||
f"sim_time_end={self.sim_time_end!r}, "
|
||||
f"ptp_timestamp={self.ptp_timestamp!r})"
|
||||
f"ptp_timestamp={self.ptp_timestamp!r}, "
|
||||
f"ptp_tag={self.ptp_tag!r})"
|
||||
)
|
||||
|
||||
def __len__(self):
|
||||
@@ -118,7 +121,7 @@ class EthMacFrame:
|
||||
|
||||
|
||||
class EthMacTx(Reset):
|
||||
def __init__(self, bus, clock, reset=None, ptp_time=None, ptp_ts=None, ptp_ts_valid=None,
|
||||
def __init__(self, bus, clock, reset=None, ptp_time=None, ptp_ts=None, ptp_ts_tag=None, ptp_ts_valid=None,
|
||||
reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
|
||||
|
||||
self.bus = bus
|
||||
@@ -126,10 +129,14 @@ class EthMacTx(Reset):
|
||||
self.reset = reset
|
||||
self.ptp_time = ptp_time
|
||||
self.ptp_ts = ptp_ts
|
||||
self.ptp_ts_tag = ptp_ts_tag
|
||||
self.ptp_ts_valid = ptp_ts_valid
|
||||
self.ifg = ifg
|
||||
self.speed = speed
|
||||
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
|
||||
if 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("cocotbext-eth version %s", __version__)
|
||||
@@ -189,6 +196,8 @@ class EthMacTx(Reset):
|
||||
|
||||
if self.ptp_ts:
|
||||
self.ptp_ts.setimmediatevalue(0)
|
||||
if self.ptp_ts_tag:
|
||||
self.ptp_ts_tag.setimmediatevalue(0)
|
||||
if self.ptp_ts_valid:
|
||||
self.ptp_ts_valid.setimmediatevalue(0)
|
||||
|
||||
@@ -247,7 +256,7 @@ class EthMacTx(Reset):
|
||||
self._run_ts_cr = None
|
||||
|
||||
if self.ptp_ts_valid:
|
||||
self.ptp_ts_valid <= 0
|
||||
self.ptp_ts_valid.value = 0
|
||||
|
||||
self.active = False
|
||||
|
||||
@@ -256,9 +265,9 @@ class EthMacTx(Reset):
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
if self._run_ts_cr is None and self.ptp_ts:
|
||||
self._run_ts_cr = cocotb.fork(self._run_ts())
|
||||
self._run_ts_cr = cocotb.start_soon(self._run_ts())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -268,7 +277,9 @@ class EthMacTx(Reset):
|
||||
# wait for data
|
||||
cycle = await self.stream.recv()
|
||||
|
||||
frame = EthMacFrame(bytearray())
|
||||
frame = EthMacFrame()
|
||||
data = bytearray()
|
||||
|
||||
frame.sim_time_start = get_sim_time()
|
||||
|
||||
# wait for preamble time
|
||||
@@ -278,7 +289,8 @@ class EthMacTx(Reset):
|
||||
|
||||
if self.ptp_time:
|
||||
frame.ptp_timestamp = self.ptp_time.value.integer
|
||||
self.ts_queue.put_nowait(frame.ptp_timestamp)
|
||||
frame.ptp_tag = cycle.tuser.integer >> 1
|
||||
self.ts_queue.put_nowait((frame.ptp_timestamp, frame.ptp_tag))
|
||||
|
||||
# process frame data
|
||||
while True:
|
||||
@@ -286,13 +298,14 @@ class EthMacTx(Reset):
|
||||
|
||||
for offset in range(self.byte_lanes):
|
||||
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
|
||||
|
||||
# wait for serialization time
|
||||
await Timer(self.time_scale*byte_count*8//self.speed, 'step')
|
||||
|
||||
if cycle.tlast.integer:
|
||||
frame.data = bytes(data)
|
||||
frame.sim_time_end = get_sim_time()
|
||||
self.log.info("RX frame: %s", frame)
|
||||
|
||||
@@ -315,14 +328,18 @@ class EthMacTx(Reset):
|
||||
await Timer(self.time_scale*self.ifg*8//self.speed, 'step')
|
||||
|
||||
async def _run_ts(self):
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
self.ptp_ts_valid <= 0
|
||||
await clock_edge_event
|
||||
self.ptp_ts_valid.value = 0
|
||||
|
||||
if not self.ts_queue.empty():
|
||||
ts = self.ts_queue.get_nowait()
|
||||
self.ptp_ts <= ts
|
||||
self.ptp_ts_valid <= 1
|
||||
ts, tag = self.ts_queue.get_nowait()
|
||||
self.ptp_ts.value = ts
|
||||
if self.ptp_ts_tag is not None:
|
||||
self.ptp_ts_tag.value = tag
|
||||
self.ptp_ts_valid.value = 1
|
||||
|
||||
|
||||
class EthMacRx(Reset):
|
||||
@@ -335,7 +352,10 @@ class EthMacRx(Reset):
|
||||
self.ptp_time = ptp_time
|
||||
self.ifg = ifg
|
||||
self.speed = speed
|
||||
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
|
||||
if 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("cocotbext-eth version %s", __version__)
|
||||
@@ -466,7 +486,7 @@ class EthMacRx(Reset):
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -532,13 +552,13 @@ class EthMacRx(Reset):
|
||||
|
||||
|
||||
class EthMac:
|
||||
def __init__(self, tx_bus=None, tx_clk=None, tx_rst=None, tx_ptp_time=None, tx_ptp_ts=None, tx_ptp_ts_valid=None,
|
||||
rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=None,
|
||||
def __init__(self, tx_bus=None, tx_clk=None, tx_rst=None, tx_ptp_time=None, tx_ptp_ts=None, tx_ptp_ts_tag=None,
|
||||
tx_ptp_ts_valid=None, rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=None,
|
||||
reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.tx = EthMacTx(tx_bus, tx_clk, tx_rst, tx_ptp_time, tx_ptp_ts, tx_ptp_ts_valid,
|
||||
self.tx = EthMacTx(tx_bus, tx_clk, tx_rst, tx_ptp_time, tx_ptp_ts, tx_ptp_ts_tag, tx_ptp_ts_valid,
|
||||
reset_active_level=reset_active_level, ifg=ifg, speed=speed)
|
||||
self.rx = EthMacRx(rx_bus, rx_clk, rx_rst, rx_ptp_time,
|
||||
reset_active_level=reset_active_level, ifg=ifg, speed=speed)
|
||||
|
||||
@@ -157,6 +157,7 @@ class GmiiSource(Reset):
|
||||
self.current_frame = None
|
||||
self.idle_event = Event()
|
||||
self.idle_event.set()
|
||||
self.active_event = Event()
|
||||
|
||||
self.ifg = 12
|
||||
self.mii_mode = False
|
||||
@@ -189,6 +190,7 @@ class GmiiSource(Reset):
|
||||
frame = GmiiFrame(frame)
|
||||
await self.queue.put(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -198,6 +200,7 @@ class GmiiSource(Reset):
|
||||
frame = GmiiFrame(frame)
|
||||
self.queue.put_nowait(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -225,6 +228,7 @@ class GmiiSource(Reset):
|
||||
frame.handle_tx_complete()
|
||||
self.dequeue_event.set()
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
@@ -239,10 +243,10 @@ class GmiiSource(Reset):
|
||||
self._run_cr = None
|
||||
|
||||
self.active = False
|
||||
self.data <= 0
|
||||
self.data.value = 0
|
||||
if self.er is not None:
|
||||
self.er <= 0
|
||||
self.dv <= 0
|
||||
self.er.value = 0
|
||||
self.dv.value = 0
|
||||
|
||||
if self.current_frame:
|
||||
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
|
||||
@@ -251,10 +255,11 @@ class GmiiSource(Reset):
|
||||
|
||||
if self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -264,8 +269,14 @@ class GmiiSource(Reset):
|
||||
ifg_cnt = 0
|
||||
self.active = False
|
||||
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
if ifg_cnt > 0:
|
||||
@@ -308,10 +319,10 @@ class GmiiSource(Reset):
|
||||
d = frame_data[frame_offset]
|
||||
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD):
|
||||
frame.sim_time_sfd = get_sim_time()
|
||||
self.data <= d
|
||||
self.data.value = d
|
||||
if self.er is not None:
|
||||
self.er <= frame_error[frame_offset]
|
||||
self.dv <= 1
|
||||
self.er.value = frame_error[frame_offset]
|
||||
self.dv.value = 1
|
||||
frame_offset += 1
|
||||
|
||||
if frame_offset >= len(frame_data):
|
||||
@@ -321,12 +332,19 @@ class GmiiSource(Reset):
|
||||
frame = None
|
||||
self.current_frame = None
|
||||
else:
|
||||
self.data <= 0
|
||||
self.data.value = 0
|
||||
if self.er is not None:
|
||||
self.er <= 0
|
||||
self.dv <= 0
|
||||
self.er.value = 0
|
||||
self.dv.value = 0
|
||||
self.active = False
|
||||
self.idle_event.set()
|
||||
|
||||
if ifg_cnt == 0 and self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
await self.active_event.wait()
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class GmiiSink(Reset):
|
||||
@@ -422,14 +440,22 @@ class GmiiSink(Reset):
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
self.active = False
|
||||
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
active_event = RisingEdge(self.dv)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
d_val = self.data.value.integer
|
||||
@@ -488,6 +514,12 @@ class GmiiSink(Reset):
|
||||
frame.data.append(d_val)
|
||||
frame.error.append(er_val)
|
||||
|
||||
if not dv_val:
|
||||
await active_event
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class GmiiPhy:
|
||||
def __init__(self, txd, tx_er, tx_en, tx_clk, gtx_clk, rxd, rx_er, rx_dv, rx_clk,
|
||||
@@ -517,12 +549,12 @@ class GmiiPhy:
|
||||
self._clock_cr.kill()
|
||||
|
||||
if self.speed == 1000e6:
|
||||
self._clock_cr = cocotb.fork(self._run_clocks(8*1e9/self.speed))
|
||||
self._clock_cr = cocotb.start_soon(self._run_clocks(8*1e9/self.speed))
|
||||
self.tx.mii_mode = False
|
||||
self.rx.mii_mode = False
|
||||
self.tx.clock = self.gtx_clk
|
||||
else:
|
||||
self._clock_cr = cocotb.fork(self._run_clocks(4*1e9/self.speed))
|
||||
self._clock_cr = cocotb.start_soon(self._run_clocks(4*1e9/self.speed))
|
||||
self.tx.mii_mode = True
|
||||
self.rx.mii_mode = True
|
||||
self.tx.clock = self.tx_clk
|
||||
@@ -536,8 +568,8 @@ class GmiiPhy:
|
||||
|
||||
while True:
|
||||
await t
|
||||
self.rx_clk <= 1
|
||||
self.tx_clk <= 1
|
||||
self.rx_clk.value = 1
|
||||
self.tx_clk.value = 1
|
||||
await t
|
||||
self.rx_clk <= 0
|
||||
self.tx_clk <= 0
|
||||
self.rx_clk.value = 0
|
||||
self.tx_clk.value = 0
|
||||
|
||||
@@ -59,6 +59,7 @@ class MiiSource(Reset):
|
||||
self.current_frame = None
|
||||
self.idle_event = Event()
|
||||
self.idle_event.set()
|
||||
self.active_event = Event()
|
||||
|
||||
self.ifg = 12
|
||||
|
||||
@@ -90,6 +91,7 @@ class MiiSource(Reset):
|
||||
frame = GmiiFrame(frame)
|
||||
await self.queue.put(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -99,6 +101,7 @@ class MiiSource(Reset):
|
||||
frame = GmiiFrame(frame)
|
||||
self.queue.put_nowait(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -126,6 +129,7 @@ class MiiSource(Reset):
|
||||
frame.handle_tx_complete()
|
||||
self.dequeue_event.set()
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
@@ -140,10 +144,10 @@ class MiiSource(Reset):
|
||||
self._run_cr = None
|
||||
|
||||
self.active = False
|
||||
self.data <= 0
|
||||
self.data.value = 0
|
||||
if self.er is not None:
|
||||
self.er <= 0
|
||||
self.dv <= 0
|
||||
self.er.value = 0
|
||||
self.dv.value = 0
|
||||
|
||||
if self.current_frame:
|
||||
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
|
||||
@@ -152,10 +156,11 @@ class MiiSource(Reset):
|
||||
|
||||
if self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -165,8 +170,14 @@ class MiiSource(Reset):
|
||||
ifg_cnt = 0
|
||||
self.active = False
|
||||
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
if ifg_cnt > 0:
|
||||
@@ -202,10 +213,10 @@ class MiiSource(Reset):
|
||||
d = frame_data[frame_offset]
|
||||
if frame.sim_time_sfd is None and d == 0xD:
|
||||
frame.sim_time_sfd = get_sim_time()
|
||||
self.data <= d
|
||||
self.data.value = d
|
||||
if self.er is not None:
|
||||
self.er <= frame_error[frame_offset]
|
||||
self.dv <= 1
|
||||
self.er.value = frame_error[frame_offset]
|
||||
self.dv.value = 1
|
||||
frame_offset += 1
|
||||
|
||||
if frame_offset >= len(frame_data):
|
||||
@@ -215,12 +226,19 @@ class MiiSource(Reset):
|
||||
frame = None
|
||||
self.current_frame = None
|
||||
else:
|
||||
self.data <= 0
|
||||
self.data.value = 0
|
||||
if self.er is not None:
|
||||
self.er <= 0
|
||||
self.dv <= 0
|
||||
self.er.value = 0
|
||||
self.dv.value = 0
|
||||
self.active = False
|
||||
self.idle_event.set()
|
||||
|
||||
if ifg_cnt == 0 and self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
await self.active_event.wait()
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class MiiSink(Reset):
|
||||
@@ -313,14 +331,22 @@ class MiiSink(Reset):
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
self.active = False
|
||||
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
active_event = RisingEdge(self.dv)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
d_val = self.data.value.integer
|
||||
@@ -374,6 +400,12 @@ class MiiSink(Reset):
|
||||
frame.data.append(d_val)
|
||||
frame.error.append(er_val)
|
||||
|
||||
if not dv_val:
|
||||
await active_event
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class MiiPhy:
|
||||
def __init__(self, txd, tx_er, tx_en, tx_clk, rxd, rx_er, rx_dv, rx_clk, reset=None,
|
||||
@@ -402,7 +434,7 @@ class MiiPhy:
|
||||
if self._clock_cr is not None:
|
||||
self._clock_cr.kill()
|
||||
|
||||
self._clock_cr = cocotb.fork(self._run_clocks(4*1e9/self.speed))
|
||||
self._clock_cr = cocotb.start_soon(self._run_clocks(4*1e9/self.speed))
|
||||
|
||||
async def _run_clocks(self, period):
|
||||
half_period = get_sim_steps(period / 2.0, 'ns')
|
||||
@@ -410,8 +442,8 @@ class MiiPhy:
|
||||
|
||||
while True:
|
||||
await t
|
||||
self.tx_clk <= 1
|
||||
self.rx_clk <= 1
|
||||
self.tx_clk.value = 1
|
||||
self.rx_clk.value = 1
|
||||
await t
|
||||
self.tx_clk <= 0
|
||||
self.rx_clk <= 0
|
||||
self.tx_clk.value = 0
|
||||
self.rx_clk.value = 0
|
||||
|
||||
@@ -23,7 +23,7 @@ THE SOFTWARE.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
from decimal import Decimal, Context
|
||||
from fractions import Fraction
|
||||
|
||||
import cocotb
|
||||
@@ -38,8 +38,8 @@ class PtpClock(Reset):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ts_96=None,
|
||||
ts_64=None,
|
||||
ts_tod=None,
|
||||
ts_rel=None,
|
||||
ts_step=None,
|
||||
pps=None,
|
||||
clock=None,
|
||||
@@ -49,20 +49,13 @@ class PtpClock(Reset):
|
||||
*args, **kwargs):
|
||||
|
||||
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
|
||||
self.ts_96 = ts_96
|
||||
self.ts_64 = ts_64
|
||||
self.ts_tod = ts_tod
|
||||
self.ts_rel = ts_rel
|
||||
self.ts_step = ts_step
|
||||
self.pps = pps
|
||||
self.clock = clock
|
||||
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("cocotbext-eth version %s", __version__)
|
||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
||||
@@ -70,21 +63,28 @@ class PtpClock(Reset):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.ts_96_s = 0
|
||||
self.ts_96_ns = 0
|
||||
self.ts_96_fns = 0
|
||||
self.ctx = Context(prec=60)
|
||||
|
||||
self.ts_64_ns = 0
|
||||
self.ts_64_fns = 0
|
||||
self.period_ns = 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.drift_cnt = 0
|
||||
|
||||
if self.ts_96 is not None:
|
||||
self.ts_96.setimmediatevalue(0)
|
||||
if self.ts_64 is not None:
|
||||
self.ts_64.setimmediatevalue(0)
|
||||
if self.ts_tod is not None:
|
||||
self.ts_tod.setimmediatevalue(0)
|
||||
if self.ts_rel is not None:
|
||||
self.ts_rel.setimmediatevalue(0)
|
||||
if self.ts_step is not None:
|
||||
self.ts_step.setimmediatevalue(0)
|
||||
if self.pps is not None:
|
||||
@@ -96,90 +96,104 @@ class PtpClock(Reset):
|
||||
|
||||
def set_period(self, ns, fns):
|
||||
self.period_ns = int(ns)
|
||||
self.period_fns = int(fns) & 0xffff
|
||||
self.period_fns = int(fns) & 0xffffffff
|
||||
|
||||
def set_drift(self, ns, fns, rate):
|
||||
self.drift_ns = int(ns)
|
||||
self.drift_fns = int(fns) & 0xffff
|
||||
self.drift_rate = int(rate)
|
||||
def set_drift(self, num, denom):
|
||||
self.drift_num = int(num)
|
||||
self.drift_denom = int(denom)
|
||||
|
||||
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)
|
||||
frac = Fraction(drift).limit_denominator(2**16)
|
||||
drift = frac.numerator
|
||||
rate = frac.denominator
|
||||
self.period_ns = period >> 16
|
||||
self.period_fns = period & 0xffff
|
||||
self.drift_ns = drift >> 16
|
||||
self.drift_fns = drift & 0xffff
|
||||
self.drift_rate = rate
|
||||
frac = Fraction(drift).limit_denominator(2**16-1)
|
||||
self.set_period(period >> 32, period & 0xffffffff)
|
||||
self.set_drift(frac.numerator, frac.denominator)
|
||||
|
||||
self.log.info("Set period: %s ns", t)
|
||||
self.log.info("Period: 0x%x ns 0x%08x fns", self.period_ns, self.period_fns)
|
||||
self.log.info("Drift: 0x%04x / 0x%04x fns", self.drift_num, self.drift_denom)
|
||||
|
||||
def get_period_ns(self):
|
||||
p = ((self.period_ns << 16) | self.period_fns) / 2**16
|
||||
if self.drift_rate:
|
||||
return p + ((self.drift_ns << 16) | self.drift_fns) / self.drift_rate / 2**16
|
||||
return p
|
||||
p = Decimal((self.period_ns << 32) | self.period_fns)
|
||||
if self.drift_denom:
|
||||
p += Decimal(self.drift_num) / Decimal(self.drift_denom)
|
||||
return p / Decimal(2**32)
|
||||
|
||||
def set_ts_96(self, ts_s, ts_ns=None, ts_fns=None):
|
||||
ts_s = int(ts_s)
|
||||
if ts_fns is not None:
|
||||
# got separate fields
|
||||
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
|
||||
def set_ts_tod(self, ts_s, ts_ns, ts_fns):
|
||||
self.ts_tod_s = int(ts_s)
|
||||
self.ts_tod_ns = int(ts_ns)
|
||||
self.ts_tod_fns = int(ts_fns)
|
||||
self.ts_updated = True
|
||||
|
||||
def set_ts_96_ns(self, t):
|
||||
self.set_ts_96_s(t*1e-9)
|
||||
def set_ts_tod_96(self, ts):
|
||||
ts = int(ts)
|
||||
self.set_ts_tod(ts >> 48, (ts >> 32) & 0x3fffffff, (ts & 0xffff) << 16)
|
||||
|
||||
def set_ts_96_s(self, t):
|
||||
ts_ns, ts_s = math.modf(t)
|
||||
ts_ns *= 1e9
|
||||
ts_fns, ts_ns = math.modf(ts_ns)
|
||||
ts_fns *= 2**16
|
||||
self.set_ts_96(ts_s, ts_ns, ts_fns)
|
||||
def set_ts_tod_ns(self, t):
|
||||
ts_s, ts_ns = self.ctx.divmod(Decimal(t), Decimal(1000000000))
|
||||
ts_s = ts_s.scaleb(-9).to_integral_value()
|
||||
ts_ns, ts_fns = self.ctx.divmod(ts_ns, Decimal(1))
|
||||
ts_ns = ts_ns.to_integral_value()
|
||||
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):
|
||||
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
|
||||
def set_ts_tod_s(self, t):
|
||||
self.set_ts_tod_ns(Decimal(t).scaleb(9, self.ctx))
|
||||
|
||||
def get_ts_96_ns(self):
|
||||
return self.ts_96_s*1e9+self.ts_96_ns+self.ts_96_fns/2**16
|
||||
def set_ts_tod_sim_time(self):
|
||||
self.set_ts_tod_ns(Decimal(get_sim_time('fs')).scaleb(-6))
|
||||
|
||||
def get_ts_96_s(self):
|
||||
return self.get_ts_96_ns()*1e-9
|
||||
def get_ts_tod(self):
|
||||
return (self.ts_tod_s, self.ts_tod_ns, self.ts_tod_fns)
|
||||
|
||||
def set_ts_64(self, ts_ns, ts_fns=None):
|
||||
ts_ns = int(ts_ns)
|
||||
if ts_fns is not None:
|
||||
# got separate fields
|
||||
self.ts_64_ns = ts_ns
|
||||
self.ts_64_fns = int(ts_fns)
|
||||
else:
|
||||
# got timestamp as integer
|
||||
self.ts_64_ns = ts_ns >> 16
|
||||
self.ts_64_fns = ts_ns & 0xffff
|
||||
def get_ts_tod_96(self):
|
||||
ts_s, ts_ns, ts_fns = self.get_ts_tod()
|
||||
return (ts_s << 48) | (ts_ns << 16) | (ts_fns >> 16)
|
||||
|
||||
def get_ts_tod_ns(self):
|
||||
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_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
|
||||
|
||||
def set_ts_64_ns(self, t):
|
||||
self.set_ts_64(t*2**16)
|
||||
def set_ts_rel_64(self, ts):
|
||||
ts = int(ts)
|
||||
self.set_ts_rel(ts >> 16, (ts & 0xffff) << 16)
|
||||
|
||||
def set_ts_64_s(self, t):
|
||||
self.set_ts_64_ns(t*1e9)
|
||||
def set_ts_rel_ns(self, t):
|
||||
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):
|
||||
return (self.ts_64_ns << 16) | self.ts_64_fns
|
||||
def set_ts_rel_s(self, t):
|
||||
self.set_ts_rel_ns(Decimal(t).scaleb(9, self.ctx))
|
||||
|
||||
def get_ts_64_ns(self):
|
||||
return self.get_ts_64()/2**16
|
||||
def set_ts_rel_sim_time(self):
|
||||
self.set_ts_rel_ns(Decimal(get_sim_time('fs')).scaleb(-6))
|
||||
|
||||
def get_ts_64_s(self):
|
||||
return self.get_ts_64()*1e-9
|
||||
def get_ts_rel(self):
|
||||
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):
|
||||
if state:
|
||||
@@ -188,80 +202,85 @@ class PtpClock(Reset):
|
||||
self._run_cr.kill()
|
||||
self._run_cr = None
|
||||
|
||||
self.ts_96_s = 0
|
||||
self.ts_96_ns = 0
|
||||
self.ts_96_fns = 0
|
||||
self.ts_64_ns = 0
|
||||
self.ts_64_fns = 0
|
||||
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.drift_cnt = 0
|
||||
if self.ts_96 is not None:
|
||||
self.ts_96 <= 0
|
||||
if self.ts_64 is not None:
|
||||
self.ts_64 <= 0
|
||||
if self.ts_tod is not None:
|
||||
self.ts_tod.value = 0
|
||||
if self.ts_rel is not None:
|
||||
self.ts_rel.value = 0
|
||||
if self.ts_step is not None:
|
||||
self.ts_step <= 0
|
||||
self.ts_step.value = 0
|
||||
if self.pps is not None:
|
||||
self.pps <= 0
|
||||
self.pps.value = 0
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.ts_step is not None:
|
||||
self.ts_step <= self.ts_updated
|
||||
self.ts_step.value = self.ts_updated
|
||||
self.ts_updated = False
|
||||
|
||||
if self.pps is not None:
|
||||
self.pps <= 0
|
||||
self.pps.value = 0
|
||||
|
||||
# increment 96 bit timestamp
|
||||
if self.ts_96 is not None or self.pps is not None:
|
||||
t = ((self.ts_96_ns << 16) + self.ts_96_fns) + ((self.period_ns << 16) + self.period_fns)
|
||||
# increment tod bit timestamp
|
||||
self.ts_tod_fns += (self.period_ns << 32) + self.period_fns
|
||||
|
||||
if self.drift_rate and self.drift_cnt == 0:
|
||||
t += (self.drift_ns << 16) + self.drift_fns
|
||||
if self.drift_denom and self.drift_cnt == 0:
|
||||
self.ts_tod_fns += self.drift_num
|
||||
|
||||
if t > (1000000000 << 16):
|
||||
self.ts_96_s += 1
|
||||
t -= (1000000000 << 16)
|
||||
if self.pps is not None:
|
||||
self.pps <= 1
|
||||
ns_inc = self.ts_tod_fns >> 32
|
||||
self.ts_tod_fns &= 0xffffffff
|
||||
|
||||
self.ts_96_fns = t & 0xffff
|
||||
self.ts_96_ns = t >> 16
|
||||
self.ts_tod_ns += ns_inc
|
||||
|
||||
if self.ts_96 is not None:
|
||||
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns)
|
||||
if self.ts_tod_ns >= 1000000000:
|
||||
self.ts_tod_s += 1
|
||||
self.ts_tod_ns -= 1000000000
|
||||
if self.pps is not None:
|
||||
self.pps.value = 1
|
||||
|
||||
# increment 64 bit timestamp
|
||||
if self.ts_64 is not None:
|
||||
t = ((self.ts_64_ns << 16) + self.ts_64_fns) + ((self.period_ns << 16) + self.period_fns)
|
||||
if self.ts_tod is not None:
|
||||
self.ts_tod.value = (self.ts_tod_s << 48) | (self.ts_tod_ns << 16) | (self.ts_tod_fns >> 16)
|
||||
|
||||
if self.drift_rate and self.drift_cnt == 0:
|
||||
t += ((self.drift_ns << 16) + self.drift_fns)
|
||||
# increment rel bit timestamp
|
||||
self.ts_rel_fns += (self.period_ns << 32) + self.period_fns
|
||||
|
||||
self.ts_64_fns = t & 0xffff
|
||||
self.ts_64_ns = t >> 16
|
||||
if self.drift_denom and self.drift_cnt == 0:
|
||||
self.ts_rel_fns += self.drift_num
|
||||
|
||||
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns
|
||||
ns_inc = self.ts_rel_fns >> 32
|
||||
self.ts_rel_fns &= 0xffffffff
|
||||
|
||||
if self.drift_rate:
|
||||
self.ts_rel_ns = (self.ts_rel_ns + ns_inc) & 0xffffffffffff
|
||||
|
||||
if self.ts_rel is not None:
|
||||
self.ts_rel.value = (self.ts_rel_ns << 16) | (self.ts_rel_fns >> 16)
|
||||
|
||||
if self.drift_denom:
|
||||
if self.drift_cnt > 0:
|
||||
self.drift_cnt -= 1
|
||||
else:
|
||||
self.drift_cnt = self.drift_rate-1
|
||||
self.drift_cnt = self.drift_denom-1
|
||||
|
||||
|
||||
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.ts_96 = ts_96
|
||||
self.ts_64 = ts_64
|
||||
self.ts_tod = ts_tod
|
||||
self.ts_rel = ts_rel
|
||||
self.pps = pps
|
||||
self.clock = clock
|
||||
|
||||
@@ -272,61 +291,80 @@ class PtpClockSimTime:
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.ts_96_s = 0
|
||||
self.ts_96_ns = 0
|
||||
self.ts_96_fns = 0
|
||||
self.ctx = Context(prec=60)
|
||||
|
||||
self.ts_64_ns = 0
|
||||
self.ts_64_fns = 0
|
||||
self.ts_tod_s = 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.ts_96.setimmediatevalue(0)
|
||||
if self.ts_64 is not None:
|
||||
self.ts_64.setimmediatevalue(0)
|
||||
self.last_ts_tod_s = 0
|
||||
|
||||
if self.ts_tod is not None:
|
||||
self.ts_tod.setimmediatevalue(0)
|
||||
if self.ts_rel is not None:
|
||||
self.ts_rel.setimmediatevalue(0)
|
||||
if self.pps is not None:
|
||||
self.pps <= 0
|
||||
self.pps.value = 0
|
||||
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
def get_ts_96(self):
|
||||
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
|
||||
def get_ts_tod(self):
|
||||
return (self.ts_tod_s, self.ts_tod_ns, self.ts_tod_fns)
|
||||
|
||||
def get_ts_96_ns(self):
|
||||
return self.ts_96_s*1e9+self.ts_96_ns+self.ts_96_fns/2**16
|
||||
def get_ts_tod_96(self):
|
||||
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):
|
||||
return self.get_ts_96_ns()*1e-9
|
||||
def get_ts_tod_ns(self):
|
||||
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):
|
||||
return (self.ts_64_ns << 16) | self.ts_64_fns
|
||||
def get_ts_tod_s(self):
|
||||
return self.get_ts_tod_ns().scaleb(-9, self.ctx)
|
||||
|
||||
def get_ts_64_ns(self):
|
||||
return self.get_ts_64()/2**16
|
||||
def get_ts_rel(self):
|
||||
return (self.ts_rel_ns, self.ts_rel_fns)
|
||||
|
||||
def get_ts_64_s(self):
|
||||
return self.get_ts_64()*1e-9
|
||||
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)
|
||||
|
||||
async def _run(self):
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
self.ts_64_fns, self.ts_64_ns = math.modf(get_sim_time('ns'))
|
||||
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_64_fns = int(self.ts_64_fns*0x10000)
|
||||
self.ts_rel_ns = int(ts_ns.to_integral_value()) & 0xffffffffffff
|
||||
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)
|
||||
self.ts_96_fns = self.ts_64_fns
|
||||
ts_s, ts_ns = self.ctx.divmod(ts_ns, Decimal(1000000000))
|
||||
|
||||
if self.ts_96 is not None:
|
||||
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
|
||||
self.ts_tod_s = int(ts_s.scaleb(-9).to_integral_value())
|
||||
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:
|
||||
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns
|
||||
if self.ts_tod is not None:
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -33,7 +33,7 @@ class Reset:
|
||||
self._reset_state = True
|
||||
|
||||
if reset_signal is not None:
|
||||
cocotb.fork(self._run_reset(reset_signal, bool(active_level)))
|
||||
cocotb.start_soon(self._run_reset(reset_signal, bool(active_level)))
|
||||
|
||||
self._update_reset()
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ class RgmiiSource(Reset):
|
||||
self.current_frame = None
|
||||
self.idle_event = Event()
|
||||
self.idle_event.set()
|
||||
self.active_event = Event()
|
||||
|
||||
self.ifg = 12
|
||||
self.mii_mode = False
|
||||
@@ -90,6 +91,7 @@ class RgmiiSource(Reset):
|
||||
frame = GmiiFrame(frame)
|
||||
await self.queue.put(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -99,6 +101,7 @@ class RgmiiSource(Reset):
|
||||
frame = GmiiFrame(frame)
|
||||
self.queue.put_nowait(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -126,6 +129,7 @@ class RgmiiSource(Reset):
|
||||
frame.handle_tx_complete()
|
||||
self.dequeue_event.set()
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
@@ -140,8 +144,8 @@ class RgmiiSource(Reset):
|
||||
self._run_cr = None
|
||||
|
||||
self.active = False
|
||||
self.data <= 0
|
||||
self.ctrl <= 0
|
||||
self.data.value = 0
|
||||
self.ctrl.value = 0
|
||||
|
||||
if self.current_frame:
|
||||
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
|
||||
@@ -150,10 +154,11 @@ class RgmiiSource(Reset):
|
||||
|
||||
if self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -161,22 +166,39 @@ class RgmiiSource(Reset):
|
||||
frame_data = None
|
||||
frame_error = None
|
||||
ifg_cnt = 0
|
||||
in_ifg = False
|
||||
self.active = False
|
||||
d = 0
|
||||
er = 0
|
||||
en = 0
|
||||
|
||||
clock_rising_edge_event = RisingEdge(self.clock)
|
||||
clock_falling_edge_event = FallingEdge(self.clock)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_falling_edge_event
|
||||
|
||||
# send low nibble after falling edge, leading in to rising edge
|
||||
self.data.value = d & 0x0F
|
||||
self.ctrl.value = en
|
||||
|
||||
await clock_rising_edge_event
|
||||
|
||||
# send high nibble after rising edge, leading in to falling edge
|
||||
self.data <= d >> 4
|
||||
self.ctrl <= en ^ er
|
||||
self.data.value = d >> 4
|
||||
self.ctrl.value = en ^ er
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
in_ifg = False
|
||||
|
||||
if ifg_cnt > 0:
|
||||
# in IFG
|
||||
ifg_cnt -= 1
|
||||
in_ifg = True
|
||||
|
||||
elif frame is None and not self.queue.empty():
|
||||
# send frame
|
||||
@@ -221,6 +243,7 @@ class RgmiiSource(Reset):
|
||||
|
||||
if frame_offset >= len(frame_data):
|
||||
ifg_cnt = max(self.ifg, 1)
|
||||
in_ifg = True
|
||||
frame.sim_time_end = get_sim_time()
|
||||
frame.handle_tx_complete()
|
||||
frame = None
|
||||
@@ -230,13 +253,14 @@ class RgmiiSource(Reset):
|
||||
er = 0
|
||||
en = 0
|
||||
self.active = False
|
||||
self.idle_event.set()
|
||||
|
||||
await FallingEdge(self.clock)
|
||||
if not in_ifg and self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
await self.active_event.wait()
|
||||
|
||||
# send low nibble after falling edge, leading in to rising edge
|
||||
self.data <= d & 0x0F
|
||||
self.ctrl <= en
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class RgmiiSink(Reset):
|
||||
@@ -330,7 +354,7 @@ class RgmiiSink(Reset):
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -339,21 +363,30 @@ class RgmiiSink(Reset):
|
||||
dv_val = 0
|
||||
er_val = 0
|
||||
|
||||
clock_rising_edge_event = RisingEdge(self.clock)
|
||||
clock_falling_edge_event = FallingEdge(self.clock)
|
||||
|
||||
active_event = RisingEdge(self.ctrl)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
|
||||
# capture low nibble on rising edge
|
||||
d_val = self.data.value.integer
|
||||
dv_val = self.ctrl.value.integer
|
||||
|
||||
await FallingEdge(self.clock)
|
||||
|
||||
# capture high nibble on falling edge
|
||||
d_val |= self.data.value.integer << 4
|
||||
er_val = dv_val ^ self.ctrl.value.integer
|
||||
await clock_rising_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
|
||||
# capture low nibble on rising edge
|
||||
d_val = self.data.value.integer
|
||||
dv_val = self.ctrl.value.integer
|
||||
|
||||
await clock_falling_edge_event
|
||||
|
||||
# capture high nibble on falling edge
|
||||
d_val |= self.data.value.integer << 4
|
||||
er_val = dv_val ^ self.ctrl.value.integer
|
||||
|
||||
if frame is None:
|
||||
if dv_val:
|
||||
# start of frame
|
||||
@@ -406,6 +439,12 @@ class RgmiiSink(Reset):
|
||||
frame.data.append(d_val)
|
||||
frame.error.append(er_val)
|
||||
|
||||
if not dv_val:
|
||||
await active_event
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class RgmiiPhy:
|
||||
def __init__(self, txd, tx_ctl, tx_clk, rxd, rx_ctl, rx_clk, reset=None,
|
||||
@@ -434,11 +473,11 @@ class RgmiiPhy:
|
||||
self._clock_cr.kill()
|
||||
|
||||
if self.speed == 1000e6:
|
||||
self._clock_cr = cocotb.fork(self._run_clock(8*1e9/self.speed))
|
||||
self._clock_cr = cocotb.start_soon(self._run_clock(8*1e9/self.speed))
|
||||
self.tx.mii_mode = False
|
||||
self.rx.mii_mode = False
|
||||
else:
|
||||
self._clock_cr = cocotb.fork(self._run_clock(4*1e9/self.speed))
|
||||
self._clock_cr = cocotb.start_soon(self._run_clock(4*1e9/self.speed))
|
||||
self.tx.mii_mode = True
|
||||
self.rx.mii_mode = True
|
||||
|
||||
@@ -448,6 +487,6 @@ class RgmiiPhy:
|
||||
|
||||
while True:
|
||||
await t
|
||||
self.rx_clk <= 1
|
||||
self.rx_clk.value = 1
|
||||
await t
|
||||
self.rx_clk <= 0
|
||||
self.rx_clk.value = 0
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.14"
|
||||
__version__ = "0.1.22"
|
||||
|
||||
@@ -28,7 +28,7 @@ import zlib
|
||||
|
||||
import cocotb
|
||||
from cocotb.queue import Queue, QueueFull
|
||||
from cocotb.triggers import RisingEdge, Timer, First, Event
|
||||
from cocotb.triggers import Edge, RisingEdge, Timer, First, Event
|
||||
from cocotb.utils import get_sim_time
|
||||
|
||||
from .version import __version__
|
||||
@@ -158,6 +158,7 @@ class XgmiiSource(Reset):
|
||||
self.current_frame = None
|
||||
self.idle_event = Event()
|
||||
self.idle_event.set()
|
||||
self.active_event = Event()
|
||||
|
||||
self.enable_dic = True
|
||||
self.ifg = 12
|
||||
@@ -200,6 +201,7 @@ class XgmiiSource(Reset):
|
||||
frame = XgmiiFrame(frame)
|
||||
await self.queue.put(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -209,6 +211,7 @@ class XgmiiSource(Reset):
|
||||
frame = XgmiiFrame(frame)
|
||||
self.queue.put_nowait(frame)
|
||||
self.idle_event.clear()
|
||||
self.active_event.set()
|
||||
self.queue_occupancy_bytes += len(frame)
|
||||
self.queue_occupancy_frames += 1
|
||||
|
||||
@@ -236,6 +239,7 @@ class XgmiiSource(Reset):
|
||||
frame.handle_tx_complete()
|
||||
self.dequeue_event.set()
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
self.queue_occupancy_bytes = 0
|
||||
self.queue_occupancy_frames = 0
|
||||
|
||||
@@ -250,8 +254,8 @@ class XgmiiSource(Reset):
|
||||
self._run_cr = None
|
||||
|
||||
self.active = False
|
||||
self.data <= 0
|
||||
self.ctrl <= 0
|
||||
self.data.value = 0
|
||||
self.ctrl.value = 0
|
||||
|
||||
if self.current_frame:
|
||||
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
|
||||
@@ -260,10 +264,11 @@ class XgmiiSource(Reset):
|
||||
|
||||
if self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
@@ -272,8 +277,14 @@ class XgmiiSource(Reset):
|
||||
deficit_idle_cnt = 0
|
||||
self.active = False
|
||||
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
if ifg_cnt + deficit_idle_cnt > self.byte_lanes-1 or (not self.enable_dic and ifg_cnt > 4):
|
||||
@@ -351,13 +362,20 @@ class XgmiiSource(Reset):
|
||||
d_val |= XgmiiCtrl.IDLE << k*8
|
||||
c_val |= 1 << k
|
||||
|
||||
self.data <= d_val
|
||||
self.ctrl <= c_val
|
||||
self.data.value = d_val
|
||||
self.ctrl.value = c_val
|
||||
else:
|
||||
self.data <= self.idle_d
|
||||
self.ctrl <= self.idle_c
|
||||
self.data.value = self.idle_d
|
||||
self.ctrl.value = self.idle_c
|
||||
self.active = False
|
||||
self.idle_event.set()
|
||||
|
||||
if ifg_cnt == 0 and self.queue.empty():
|
||||
self.idle_event.set()
|
||||
self.active_event.clear()
|
||||
await self.active_event.wait()
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
|
||||
class XgmiiSink(Reset):
|
||||
@@ -450,19 +468,32 @@ class XgmiiSink(Reset):
|
||||
else:
|
||||
self.log.info("Reset de-asserted")
|
||||
if self._run_cr is None:
|
||||
self._run_cr = cocotb.fork(self._run())
|
||||
self._run_cr = cocotb.start_soon(self._run())
|
||||
|
||||
async def _run(self):
|
||||
frame = None
|
||||
self.active = False
|
||||
|
||||
clock_edge_event = RisingEdge(self.clock)
|
||||
|
||||
active_event = First(Edge(self.data), Edge(self.ctrl))
|
||||
|
||||
enable_event = None
|
||||
if self.enable is not None:
|
||||
enable_event = RisingEdge(self.enable)
|
||||
|
||||
idle_d = sum([XgmiiCtrl.IDLE << n*8 for n in range(self.byte_lanes)])
|
||||
idle_c = 2**self.byte_lanes-1
|
||||
|
||||
while True:
|
||||
await RisingEdge(self.clock)
|
||||
await clock_edge_event
|
||||
|
||||
if self.enable is None or self.enable.value:
|
||||
data_val = self.data.value.integer
|
||||
ctrl_val = self.ctrl.value.integer
|
||||
for offset in range(self.byte_lanes):
|
||||
d_val = (self.data.value.integer >> (offset*8)) & 0xff
|
||||
c_val = (self.ctrl.value.integer >> offset) & 1
|
||||
d_val = (data_val >> (offset*8)) & 0xff
|
||||
c_val = (ctrl_val >> offset) & 1
|
||||
|
||||
if frame is None:
|
||||
if c_val and d_val == XgmiiCtrl.START:
|
||||
@@ -495,3 +526,9 @@ class XgmiiSink(Reset):
|
||||
|
||||
frame.data.append(d_val)
|
||||
frame.ctrl.append(c_val)
|
||||
|
||||
if data_val == idle_d and ctrl_val == idle_c:
|
||||
await active_event
|
||||
|
||||
elif self.enable is not None and not self.enable.value:
|
||||
await enable_event
|
||||
|
||||
36
setup.cfg
36
setup.cfg
@@ -13,21 +13,22 @@ project_urls =
|
||||
Source Code = https://github.com/alexforencich/cocotbext-eth
|
||||
download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master
|
||||
long_description = file: README.md
|
||||
long-description-content-type = text/markdown
|
||||
long_description_content_type = text/markdown
|
||||
platforms = any
|
||||
classifiers =
|
||||
Development Status :: 3 - Alpha
|
||||
Programming Language :: Python :: 3
|
||||
Framework :: cocotb
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
||||
|
||||
[options]
|
||||
packages = find_namespace:
|
||||
python_requires = >=3.6
|
||||
install_requires =
|
||||
cocotb
|
||||
cocotbext-axi
|
||||
cocotb >= 1.6.0
|
||||
cocotbext-axi >= 0.1.16
|
||||
|
||||
[options.extras_require]
|
||||
test =
|
||||
@@ -46,31 +47,40 @@ addopts =
|
||||
|
||||
# tox configuration
|
||||
[tox:tox]
|
||||
envlist = py36, py37, py38, py39
|
||||
envlist = py37, py38, py39, py310
|
||||
skip_missing_interpreters = true
|
||||
minversion = 3.18.0
|
||||
requires = virtualenv >= 16.1
|
||||
|
||||
[gh-actions]
|
||||
python =
|
||||
3.6: py36
|
||||
3.7: py37
|
||||
3.8: py38
|
||||
3.9: py39
|
||||
3.10: py310
|
||||
3.11: py311
|
||||
|
||||
[testenv]
|
||||
setenv =
|
||||
COVERAGE=1
|
||||
usedevelop = True
|
||||
|
||||
deps =
|
||||
pytest
|
||||
pytest-xdist
|
||||
cocotb-test
|
||||
coverage
|
||||
pytest-cov
|
||||
pytest == 7.2.1
|
||||
pytest-xdist == 3.1.0
|
||||
cocotb == 1.7.2
|
||||
cocotb-bus == 0.2.1
|
||||
cocotb-test == 0.2.4
|
||||
cocotbext-axi == 0.1.20
|
||||
coverage == 7.0.5
|
||||
pytest-cov == 4.0.0
|
||||
|
||||
commands =
|
||||
pytest --cov=cocotbext --cov=tests --cov-branch -n auto
|
||||
pytest --cov=cocotbext --cov=tests --cov-branch {posargs:-n auto --verbose}
|
||||
bash -c 'find . -type f -name "\.coverage" | xargs coverage combine --append'
|
||||
coverage report
|
||||
|
||||
whitelist_externals =
|
||||
allowlist_externals =
|
||||
bash
|
||||
|
||||
# combine if paths are different
|
||||
|
||||
@@ -31,9 +31,19 @@ TOPLEVEL = $(DUT)
|
||||
MODULE = $(DUT)
|
||||
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)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +51,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -28,6 +28,7 @@ import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
@@ -45,8 +46,20 @@ class TB:
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.tx_clk, 6.4, units="ns").start())
|
||||
cocotb.fork(Clock(dut.rx_clk, 6.4, units="ns").start())
|
||||
if len(dut.tx_axis_tdata) == 8:
|
||||
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(
|
||||
tx_clk=dut.tx_clk,
|
||||
@@ -54,6 +67,7 @@ class TB:
|
||||
tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
|
||||
tx_ptp_time=dut.tx_ptp_time,
|
||||
tx_ptp_ts=dut.tx_ptp_ts,
|
||||
tx_ptp_ts_tag=dut.tx_ptp_ts_tag,
|
||||
tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
|
||||
rx_clk=dut.rx_clk,
|
||||
rx_rst=dut.rx_rst,
|
||||
@@ -63,12 +77,12 @@ class TB:
|
||||
)
|
||||
|
||||
self.tx_ptp = PtpClockSimTime(
|
||||
ts_96=dut.tx_ptp_time,
|
||||
ts_tod=dut.tx_ptp_time,
|
||||
clock=dut.tx_clk
|
||||
)
|
||||
|
||||
self.rx_ptp = PtpClockSimTime(
|
||||
ts_96=dut.rx_ptp_time,
|
||||
ts_tod=dut.rx_ptp_time,
|
||||
clock=dut.rx_clk
|
||||
)
|
||||
|
||||
@@ -80,12 +94,12 @@ class TB:
|
||||
self.dut.rx_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.tx_rst <= 1
|
||||
self.dut.rx_rst <= 1
|
||||
self.dut.tx_rst.value = 1
|
||||
self.dut.rx_rst.value = 1
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.tx_rst <= 0
|
||||
self.dut.rx_rst <= 0
|
||||
self.dut.tx_rst.value = 0
|
||||
self.dut.rx_rst.value = 0
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
@@ -156,12 +170,21 @@ def incrementing_payload(length):
|
||||
|
||||
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]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("speed", [10e9, 1e9])
|
||||
factory.add_option("speed", speed)
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
@@ -170,7 +193,8 @@ if cocotb.SIM_NAME:
|
||||
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"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
@@ -181,6 +205,13 @@ def test_eth_mac(request):
|
||||
|
||||
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()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
|
||||
@@ -29,28 +29,37 @@ THE SOFTWARE.
|
||||
/*
|
||||
* Ethernet MAC model test
|
||||
*/
|
||||
module test_eth_mac
|
||||
module test_eth_mac #
|
||||
(
|
||||
inout wire tx_clk,
|
||||
inout wire tx_rst,
|
||||
inout wire [63:0] tx_axis_tdata,
|
||||
inout wire [7:0] tx_axis_tkeep,
|
||||
inout wire tx_axis_tlast,
|
||||
inout wire tx_axis_tuser,
|
||||
inout wire tx_axis_tvalid,
|
||||
inout wire tx_axis_tready,
|
||||
inout wire [95:0] tx_ptp_time,
|
||||
inout wire [95:0] tx_ptp_ts,
|
||||
inout wire tx_ptp_ts_valid,
|
||||
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_rst,
|
||||
inout wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata,
|
||||
inout wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep,
|
||||
inout wire tx_axis_tlast,
|
||||
inout wire [AXIS_TX_USER_WIDTH-1:0] tx_axis_tuser,
|
||||
inout wire tx_axis_tvalid,
|
||||
inout wire tx_axis_tready,
|
||||
inout wire [PTP_TS_WIDTH-1:0] tx_ptp_time,
|
||||
inout wire [PTP_TS_WIDTH-1:0] tx_ptp_ts,
|
||||
inout wire [PTP_TAG_WIDTH-1:0] tx_ptp_ts_tag,
|
||||
inout wire tx_ptp_ts_valid,
|
||||
|
||||
inout wire rx_clk,
|
||||
inout wire rx_rst,
|
||||
inout wire [63:0] rx_axis_tdata,
|
||||
inout wire [7:0] rx_axis_tkeep,
|
||||
inout wire rx_axis_tlast,
|
||||
inout wire [96:0] rx_axis_tuser,
|
||||
inout wire rx_axis_tvalid,
|
||||
inout wire [95:0] rx_ptp_time
|
||||
inout wire rx_clk,
|
||||
inout wire rx_rst,
|
||||
inout wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata,
|
||||
inout wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep,
|
||||
inout wire rx_axis_tlast,
|
||||
inout wire [AXIS_RX_USER_WIDTH-1:0] rx_axis_tuser,
|
||||
inout wire rx_axis_tvalid,
|
||||
inout wire [PTP_TS_WIDTH-1:0] rx_ptp_time
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -47,7 +47,7 @@ class TB:
|
||||
self._enable_generator = None
|
||||
self._enable_cr = None
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
|
||||
|
||||
self.source = GmiiSource(dut.gmii_d, dut.gmii_er, dut.gmii_en,
|
||||
dut.clk, dut.rst, dut.gmii_clk_en, dut.gmii_mii_sel)
|
||||
@@ -61,10 +61,10 @@ class TB:
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
@@ -76,15 +76,17 @@ class TB:
|
||||
self._enable_generator = generator
|
||||
|
||||
if self._enable_generator is not None:
|
||||
self._enable_cr = cocotb.fork(self._run_enable())
|
||||
self._enable_cr = cocotb.start_soon(self._run_enable())
|
||||
|
||||
def clear_enable_generator(self):
|
||||
self.set_enable_generator(None)
|
||||
|
||||
async def _run_enable(self):
|
||||
clock_edge_event = RisingEdge(self.dut.clk)
|
||||
|
||||
for val in self._enable_generator:
|
||||
self.dut.gmii_clk_en <= val
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.gmii_clk_en.value = val
|
||||
await clock_edge_event
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
@@ -92,7 +94,7 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_
|
||||
tb = TB(dut)
|
||||
|
||||
tb.source.ifg = ifg
|
||||
tb.dut.gmii_mii_sel <= mii_sel
|
||||
tb.dut.gmii_mii_sel.value = mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator(enable_gen())
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -44,7 +44,7 @@ class TB:
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.phy_gtx_clk, 8, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.phy_gtx_clk, 8, units="ns").start())
|
||||
|
||||
self.gmii_phy = GmiiPhy(dut.phy_txd, dut.phy_tx_er, dut.phy_tx_en, dut.phy_tx_clk, dut.phy_gtx_clk,
|
||||
dut.phy_rxd, dut.phy_rx_er, dut.phy_rx_dv, dut.phy_rx_clk, dut.phy_rst, speed=speed)
|
||||
@@ -64,10 +64,10 @@ class TB:
|
||||
self.dut.phy_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
self.dut.phy_rst <= 1
|
||||
self.dut.phy_rst.value = 1
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
self.dut.phy_rst <= 0
|
||||
self.dut.phy_rst.value = 0
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -47,7 +47,7 @@ class TB:
|
||||
self._enable_generator = None
|
||||
self._enable_cr = None
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
|
||||
|
||||
self.source = MiiSource(dut.mii_d, dut.mii_er, dut.mii_en,
|
||||
dut.clk, dut.rst, dut.mii_clk_en)
|
||||
@@ -60,10 +60,10 @@ class TB:
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
@@ -75,15 +75,17 @@ class TB:
|
||||
self._enable_generator = generator
|
||||
|
||||
if self._enable_generator is not None:
|
||||
self._enable_cr = cocotb.fork(self._run_enable())
|
||||
self._enable_cr = cocotb.start_soon(self._run_enable())
|
||||
|
||||
def clear_enable_generator(self):
|
||||
self.set_enable_generator(None)
|
||||
|
||||
async def _run_enable(self):
|
||||
clock_edge_event = RisingEdge(self.dut.clk)
|
||||
|
||||
for val in self._enable_generator:
|
||||
self.dut.mii_clk_en <= val
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.mii_clk_en.value = val
|
||||
await clock_edge_event
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None):
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -55,10 +55,10 @@ class TB:
|
||||
self.dut.phy_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
self.dut.phy_rst <= 1
|
||||
self.dut.phy_rst.value = 1
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
self.dut.phy_rst <= 0
|
||||
self.dut.phy_rst.value = 0
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -25,12 +25,13 @@ THE SOFTWARE.
|
||||
|
||||
import logging
|
||||
import os
|
||||
from decimal import Decimal
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.triggers import RisingEdge, ClockCycles
|
||||
from cocotb.utils import get_sim_time
|
||||
|
||||
from cocotbext.eth import PtpClock
|
||||
@@ -43,11 +44,11 @@ class TB:
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 6.4, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
|
||||
|
||||
self.ptp_clock = PtpClock(
|
||||
ts_96=dut.ts_96,
|
||||
ts_64=dut.ts_64,
|
||||
ts_tod=dut.ts_tod,
|
||||
ts_rel=dut.ts_rel,
|
||||
ts_step=dut.ts_step,
|
||||
pps=dut.pps,
|
||||
clock=dut.clk,
|
||||
@@ -59,13 +60,21 @@ class TB:
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
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()
|
||||
async def run_default_rate(dut):
|
||||
@@ -75,32 +84,32 @@ async def run_default_rate(dut):
|
||||
await tb.reset()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
start_time = get_sim_time('sec')
|
||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
start_ts_tod = tb.get_ts_tod_ns()
|
||||
start_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
for k in range(10000):
|
||||
await RisingEdge(dut.clk)
|
||||
await ClockCycles(dut.clk, 10000)
|
||||
|
||||
stop_time = get_sim_time('sec')
|
||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
stop_ts_tod = tb.get_ts_tod_ns()
|
||||
stop_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
time_delta = stop_time-start_time
|
||||
ts_96_delta = stop_ts_96-start_ts_96
|
||||
ts_64_delta = stop_ts_64-start_ts_64
|
||||
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||
|
||||
ts_96_diff = time_delta - ts_96_delta
|
||||
ts_64_diff = time_delta - ts_64_delta
|
||||
tb.log.info("sim time delta : %s ns", time_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)
|
||||
tb.log.info("96 bit ts delta : %g s", ts_96_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)
|
||||
ts_tod_diff = time_delta - ts_tod_delta
|
||||
ts_rel_diff = time_delta - ts_rel_delta
|
||||
|
||||
assert abs(ts_96_diff) < 1e-12
|
||||
assert abs(ts_64_diff) < 1e-12
|
||||
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||
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)
|
||||
@@ -113,41 +122,41 @@ async def run_load_timestamps(dut):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.ptp_clock.set_ts_96(12345678)
|
||||
tb.ptp_clock.set_ts_64(12345678)
|
||||
tb.ptp_clock.set_ts_tod_ns(12345678)
|
||||
tb.ptp_clock.set_ts_rel_ns(12345678)
|
||||
|
||||
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_64.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_rel.value.integer == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
|
||||
assert dut.ts_step.value.integer == 1
|
||||
|
||||
start_time = get_sim_time('sec')
|
||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
start_ts_tod = tb.get_ts_tod_ns()
|
||||
start_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
for k in range(2000):
|
||||
await RisingEdge(dut.clk)
|
||||
await ClockCycles(dut.clk, 2000)
|
||||
|
||||
stop_time = get_sim_time('sec')
|
||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
stop_ts_tod = tb.get_ts_tod_ns()
|
||||
stop_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
time_delta = stop_time-start_time
|
||||
ts_96_delta = stop_ts_96-start_ts_96
|
||||
ts_64_delta = stop_ts_64-start_ts_64
|
||||
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||
|
||||
ts_96_diff = time_delta - ts_96_delta
|
||||
ts_64_diff = time_delta - ts_64_delta
|
||||
tb.log.info("sim time delta : %s ns", time_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)
|
||||
tb.log.info("96 bit ts delta : %g s", ts_96_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)
|
||||
ts_tod_diff = time_delta - ts_tod_delta
|
||||
ts_rel_diff = time_delta - ts_rel_delta
|
||||
|
||||
assert abs(ts_96_diff) < 1e-12
|
||||
assert abs(ts_64_diff) < 1e-12
|
||||
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||
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)
|
||||
@@ -160,15 +169,15 @@ async def run_seconds_increment(dut):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.ptp_clock.set_ts_96(999990000*2**16)
|
||||
tb.ptp_clock.set_ts_64(999990000*2**16)
|
||||
tb.ptp_clock.set_ts_tod_ns(999990000)
|
||||
tb.ptp_clock.set_ts_rel_ns(999990000)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
start_time = get_sim_time('sec')
|
||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
start_ts_tod = tb.get_ts_tod_ns()
|
||||
start_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
saw_pps = False
|
||||
|
||||
@@ -177,30 +186,31 @@ async def run_seconds_increment(dut):
|
||||
|
||||
if dut.pps.value.integer:
|
||||
saw_pps = True
|
||||
assert dut.ts_96.value.integer >> 48 == 1
|
||||
assert dut.ts_96.value.integer & 0xffffffffffff < 10*2**16
|
||||
assert dut.ts_tod.value.integer >> 48 == 1
|
||||
assert dut.ts_tod.value.integer & 0xffffffffffff < 10*2**16
|
||||
|
||||
assert saw_pps
|
||||
|
||||
stop_time = get_sim_time('sec')
|
||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
stop_ts_tod = tb.get_ts_tod_ns()
|
||||
stop_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
time_delta = stop_time-start_time
|
||||
ts_96_delta = stop_ts_96-start_ts_96
|
||||
ts_64_delta = stop_ts_64-start_ts_64
|
||||
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||
|
||||
ts_96_diff = time_delta - ts_96_delta
|
||||
ts_64_diff = time_delta - ts_64_delta
|
||||
tb.log.info("sim time delta : %s ns", time_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)
|
||||
tb.log.info("96 bit ts delta : %g s", ts_96_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)
|
||||
ts_tod_diff = time_delta - ts_tod_delta
|
||||
ts_rel_diff = time_delta - ts_rel_delta
|
||||
|
||||
assert abs(ts_96_diff) < 1e-12
|
||||
assert abs(ts_64_diff) < 1e-12
|
||||
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||
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)
|
||||
@@ -213,36 +223,35 @@ async def run_frequency_adjustment(dut):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.ptp_clock.period_ns = 0x6
|
||||
tb.ptp_clock.period_fns = 0x6624
|
||||
tb.ptp_clock.set_period(0x6, 0x66240000)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
start_time = get_sim_time('sec')
|
||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
start_ts_tod = tb.get_ts_tod_ns()
|
||||
start_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
for k in range(10000):
|
||||
await RisingEdge(dut.clk)
|
||||
await ClockCycles(dut.clk, 10000)
|
||||
|
||||
stop_time = get_sim_time('sec')
|
||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
stop_ts_tod = tb.get_ts_tod_ns()
|
||||
stop_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
time_delta = stop_time-start_time
|
||||
ts_96_delta = stop_ts_96-start_ts_96
|
||||
ts_64_delta = stop_ts_64-start_ts_64
|
||||
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||
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)
|
||||
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6624+2/5)/2**16)
|
||||
tb.log.info("sim time delta : %s ns", time_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)
|
||||
tb.log.info("96 bit ts delta : %g s", ts_96_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)
|
||||
ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||
ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||
|
||||
assert abs(ts_96_diff) < 1e-12
|
||||
assert abs(ts_64_diff) < 1e-12
|
||||
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||
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)
|
||||
@@ -255,37 +264,35 @@ async def run_drift_adjustment(dut):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.ptp_clock.drift_ns = 0
|
||||
tb.ptp_clock.drift_fns = 20
|
||||
tb.ptp_clock.drift_rate = 5
|
||||
tb.ptp_clock.set_drift(20000, 5)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
start_time = get_sim_time('sec')
|
||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
start_ts_tod = tb.get_ts_tod_ns()
|
||||
start_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
for k in range(10000):
|
||||
await RisingEdge(dut.clk)
|
||||
await ClockCycles(dut.clk, 10000)
|
||||
|
||||
stop_time = get_sim_time('sec')
|
||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
stop_ts_tod = tb.get_ts_tod_ns()
|
||||
stop_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
time_delta = stop_time-start_time
|
||||
ts_96_delta = stop_ts_96-start_ts_96
|
||||
ts_64_delta = stop_ts_64-start_ts_64
|
||||
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||
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)
|
||||
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6666+20/5)/2**16)
|
||||
tb.log.info("sim time delta : %s ns", time_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)
|
||||
tb.log.info("96 bit ts delta : %g s", ts_96_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)
|
||||
ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||
ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||
|
||||
assert abs(ts_96_diff) < 1e-12
|
||||
assert abs(ts_64_diff) < 1e-12
|
||||
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||
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)
|
||||
|
||||
@@ -34,8 +34,8 @@ module test_ptp_clock
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
inout wire [95:0] ts_96,
|
||||
inout wire [63:0] ts_64,
|
||||
inout wire [95:0] ts_tod,
|
||||
inout wire [63:0] ts_rel,
|
||||
inout wire ts_step,
|
||||
inout wire pps
|
||||
);
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -25,12 +25,13 @@ THE SOFTWARE.
|
||||
|
||||
import logging
|
||||
import os
|
||||
from decimal import Decimal
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.triggers import RisingEdge, ClockCycles
|
||||
from cocotb.utils import get_sim_time
|
||||
|
||||
from cocotbext.eth import PtpClockSimTime
|
||||
@@ -43,15 +44,23 @@ class TB:
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 6.4, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
|
||||
|
||||
self.ptp_clock = PtpClockSimTime(
|
||||
ts_96=dut.ts_96,
|
||||
ts_64=dut.ts_64,
|
||||
ts_tod=dut.ts_tod,
|
||||
ts_rel=dut.ts_rel,
|
||||
pps=dut.pps,
|
||||
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()
|
||||
async def run_test(dut):
|
||||
@@ -62,32 +71,32 @@ async def run_test(dut):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
start_time = get_sim_time('sec')
|
||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
start_ts_tod = tb.get_ts_tod_ns()
|
||||
start_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
for k in range(10000):
|
||||
await RisingEdge(dut.clk)
|
||||
await ClockCycles(dut.clk, 10000)
|
||||
|
||||
stop_time = get_sim_time('sec')
|
||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
||||
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||
stop_ts_tod = tb.get_ts_tod_ns()
|
||||
stop_ts_rel = tb.get_ts_rel_ns()
|
||||
|
||||
time_delta = stop_time-start_time
|
||||
ts_96_delta = stop_ts_96-start_ts_96
|
||||
ts_64_delta = stop_ts_64-start_ts_64
|
||||
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||
|
||||
ts_96_diff = time_delta - ts_96_delta
|
||||
ts_64_diff = time_delta - ts_64_delta
|
||||
tb.log.info("sim time delta : %s ns", time_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)
|
||||
tb.log.info("96 bit ts delta : %g s", ts_96_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)
|
||||
ts_tod_diff = time_delta - ts_tod_delta
|
||||
ts_rel_diff = time_delta - ts_rel_delta
|
||||
|
||||
assert abs(ts_96_diff) < 1e-12
|
||||
assert abs(ts_64_diff) < 1e-12
|
||||
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||
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)
|
||||
|
||||
@@ -33,8 +33,8 @@ module test_ptp_clock_sim_time
|
||||
(
|
||||
input wire clk,
|
||||
|
||||
inout wire [95:0] ts_96,
|
||||
inout wire [63:0] ts_64,
|
||||
inout wire [95:0] ts_tod,
|
||||
inout wire [63:0] ts_rel,
|
||||
inout wire pps
|
||||
);
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -47,7 +47,7 @@ class TB:
|
||||
self._enable_generator = None
|
||||
self._enable_cr = None
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
|
||||
|
||||
self.source = RgmiiSource(dut.rgmii_d, dut.rgmii_ctl, dut.clk, dut.rst, dut.rgmii_clk_en, dut.rgmii_mii_sel)
|
||||
self.sink = RgmiiSink(dut.rgmii_d, dut.rgmii_ctl, dut.clk, dut.rst, dut.rgmii_clk_en, dut.rgmii_mii_sel)
|
||||
@@ -59,10 +59,10 @@ class TB:
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
@@ -74,15 +74,17 @@ class TB:
|
||||
self._enable_generator = generator
|
||||
|
||||
if self._enable_generator is not None:
|
||||
self._enable_cr = cocotb.fork(self._run_enable())
|
||||
self._enable_cr = cocotb.start_soon(self._run_enable())
|
||||
|
||||
def clear_enable_generator(self):
|
||||
self.set_enable_generator(None)
|
||||
|
||||
async def _run_enable(self):
|
||||
clock_edge_event = RisingEdge(self.dut.clk)
|
||||
|
||||
for val in self._enable_generator:
|
||||
self.dut.rgmii_clk_en <= val
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rgmii_clk_en.value = val
|
||||
await clock_edge_event
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
@@ -90,7 +92,7 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_
|
||||
tb = TB(dut)
|
||||
|
||||
tb.source.ifg = ifg
|
||||
tb.dut.rgmii_mii_sel <= mii_sel
|
||||
tb.dut.rgmii_mii_sel.value = mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator(enable_gen())
|
||||
|
||||
@@ -34,6 +34,8 @@ VERILOG_SOURCES += $(DUT).v
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
@@ -41,6 +43,8 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
|
||||
@@ -45,11 +45,11 @@ class TB:
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
if speed == 1000e6:
|
||||
cocotb.fork(Clock(dut.phy_tx_clk, 8, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.phy_tx_clk, 8, units="ns").start())
|
||||
elif speed == 100e6:
|
||||
cocotb.fork(Clock(dut.phy_tx_clk, 40, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.phy_tx_clk, 40, units="ns").start())
|
||||
elif speed == 10e6:
|
||||
cocotb.fork(Clock(dut.phy_tx_clk, 400, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.phy_tx_clk, 400, units="ns").start())
|
||||
|
||||
self.rgmii_phy = RgmiiPhy(dut.phy_txd, dut.phy_tx_ctl, dut.phy_tx_clk,
|
||||
dut.phy_rxd, dut.phy_rx_ctl, dut.phy_rx_clk, dut.phy_rst, speed=speed)
|
||||
@@ -68,10 +68,10 @@ class TB:
|
||||
self.dut.phy_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
self.dut.phy_rst <= 1
|
||||
self.dut.phy_rst.value = 1
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
self.dut.phy_rst <= 0
|
||||
self.dut.phy_rst.value = 0
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
await RisingEdge(self.dut.phy_tx_clk)
|
||||
|
||||
|
||||
@@ -32,14 +32,13 @@ MODULE = $(DUT)
|
||||
VERILOG_SOURCES += $(DUT).v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 64
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_DATA_WIDTH := 64
|
||||
export PARAM_CTRL_WIDTH := $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
@@ -48,8 +47,7 @@ ifeq ($(SIM), icarus)
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
|
||||
@@ -48,7 +48,7 @@ class TB:
|
||||
self._enable_generator = None
|
||||
self._enable_cr = None
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 2, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
|
||||
|
||||
self.source = XgmiiSource(dut.xgmii_d, dut.xgmii_c, dut.clk, dut.rst, dut.xgmii_clk_en)
|
||||
self.sink = XgmiiSink(dut.xgmii_d, dut.xgmii_c, dut.clk, dut.rst, dut.xgmii_clk_en)
|
||||
@@ -59,10 +59,10 @@ class TB:
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
@@ -74,15 +74,17 @@ class TB:
|
||||
self._enable_generator = generator
|
||||
|
||||
if self._enable_generator is not None:
|
||||
self._enable_cr = cocotb.fork(self._run_enable())
|
||||
self._enable_cr = cocotb.start_soon(self._run_enable())
|
||||
|
||||
def clear_enable_generator(self):
|
||||
self.set_enable_generator(None)
|
||||
|
||||
async def _run_enable(self):
|
||||
clock_edge_event = RisingEdge(self.dut.clk)
|
||||
|
||||
for val in self._enable_generator:
|
||||
self.dut.xgmii_clk_en <= val
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.xgmii_clk_en.value = val
|
||||
await clock_edge_event
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_dic=True,
|
||||
|
||||
Reference in New Issue
Block a user