12 Commits

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

View File

@@ -9,7 +9,7 @@ jobs:
strategy: strategy:
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"] python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

108
README.md
View File

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

View File

@@ -43,8 +43,8 @@ AxiStreamBus, AxiStreamTransaction, AxiStreamSource, AxiStreamSink, AxiStreamMon
class EthMacFrame: class EthMacFrame:
def __init__(self, data=None, tx_complete=None): def __init__(self, data=b'', tx_complete=None):
self.data = bytearray() self.data = b''
self.sim_time_start = None self.sim_time_start = None
self.sim_time_sfd = None self.sim_time_sfd = None
self.sim_time_end = None self.sim_time_end = None
@@ -53,7 +53,7 @@ class EthMacFrame:
self.tx_complete = None self.tx_complete = None
if type(data) is EthMacFrame: if type(data) is EthMacFrame:
self.data = bytearray(data.data) self.data = bytes(data.data)
self.sim_time_start = data.sim_time_start self.sim_time_start = data.sim_time_start
self.sim_time_sfd = data.sim_time_sfd self.sim_time_sfd = data.sim_time_sfd
self.sim_time_end = data.sim_time_end self.sim_time_end = data.sim_time_end
@@ -61,7 +61,7 @@ class EthMacFrame:
self.ptp_tag = data.ptp_tag self.ptp_tag = data.ptp_tag
self.tx_complete = data.tx_complete self.tx_complete = data.tx_complete
else: else:
self.data = bytearray(data) self.data = bytes(data)
if tx_complete is not None: if tx_complete is not None:
self.tx_complete = tx_complete self.tx_complete = tx_complete
@@ -133,7 +133,10 @@ class EthMacTx(Reset):
self.ptp_ts_valid = ptp_ts_valid self.ptp_ts_valid = ptp_ts_valid
self.ifg = ifg self.ifg = ifg
self.speed = speed self.speed = speed
if bus._name:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
else:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}")
self.log.info("Ethernet MAC TX model") self.log.info("Ethernet MAC TX model")
self.log.info("cocotbext-eth version %s", __version__) self.log.info("cocotbext-eth version %s", __version__)
@@ -274,7 +277,9 @@ class EthMacTx(Reset):
# wait for data # wait for data
cycle = await self.stream.recv() cycle = await self.stream.recv()
frame = EthMacFrame(bytearray()) frame = EthMacFrame()
data = bytearray()
frame.sim_time_start = get_sim_time() frame.sim_time_start = get_sim_time()
# wait for preamble time # wait for preamble time
@@ -293,13 +298,14 @@ class EthMacTx(Reset):
for offset in range(self.byte_lanes): for offset in range(self.byte_lanes):
if not hasattr(self.bus, "tkeep") or (cycle.tkeep.integer >> offset) & 1: if not hasattr(self.bus, "tkeep") or (cycle.tkeep.integer >> offset) & 1:
frame.data.append((cycle.tdata.integer >> (offset * self.byte_size)) & self.byte_mask) data.append((cycle.tdata.integer >> (offset * self.byte_size)) & self.byte_mask)
byte_count += 1 byte_count += 1
# wait for serialization time # wait for serialization time
await Timer(self.time_scale*byte_count*8//self.speed, 'step') await Timer(self.time_scale*byte_count*8//self.speed, 'step')
if cycle.tlast.integer: if cycle.tlast.integer:
frame.data = bytes(data)
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
self.log.info("RX frame: %s", frame) self.log.info("RX frame: %s", frame)
@@ -346,7 +352,10 @@ class EthMacRx(Reset):
self.ptp_time = ptp_time self.ptp_time = ptp_time
self.ifg = ifg self.ifg = ifg
self.speed = speed self.speed = speed
if bus._name:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
else:
self.log = logging.getLogger(f"cocotb.{bus._entity._name}")
self.log.info("Ethernet MAC RX model") self.log.info("Ethernet MAC RX model")
self.log.info("cocotbext-eth version %s", __version__) self.log.info("cocotbext-eth version %s", __version__)

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ project_urls =
Source Code = https://github.com/alexforencich/cocotbext-eth Source Code = https://github.com/alexforencich/cocotbext-eth
download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master
long_description = file: README.md long_description = file: README.md
long-description-content-type = text/markdown long_description_content_type = text/markdown
platforms = any platforms = any
classifiers = classifiers =
Development Status :: 3 - Alpha Development Status :: 3 - Alpha
@@ -58,6 +58,7 @@ python =
3.8: py38 3.8: py38
3.9: py39 3.9: py39
3.10: py310 3.10: py310
3.11: py311
[testenv] [testenv]
setenv = setenv =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ THE SOFTWARE.
import logging import logging
import os import os
from decimal import Decimal
import cocotb_test.simulator import cocotb_test.simulator
@@ -46,8 +47,8 @@ class TB:
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start()) cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
self.ptp_clock = PtpClock( self.ptp_clock = PtpClock(
ts_96=dut.ts_96, ts_tod=dut.ts_tod,
ts_64=dut.ts_64, ts_rel=dut.ts_rel,
ts_step=dut.ts_step, ts_step=dut.ts_step,
pps=dut.pps, pps=dut.pps,
clock=dut.clk, clock=dut.clk,
@@ -66,6 +67,14 @@ class TB:
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
def get_ts_tod_ns(self):
ts = self.dut.ts_tod.value.integer
return Decimal(ts >> 48).scaleb(9) + (Decimal(ts & 0xffffffffffff) / Decimal(2**16))
def get_ts_rel_ns(self):
ts = self.dut.ts_rel.value.integer
return Decimal(ts) / Decimal(2**16)
@cocotb.test() @cocotb.test()
async def run_default_rate(dut): async def run_default_rate(dut):
@@ -75,31 +84,32 @@ async def run_default_rate(dut):
await tb.reset() await tb.reset()
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
await ClockCycles(dut.clk, 10000) await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -112,40 +122,41 @@ async def run_load_timestamps(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.set_ts_96(12345678) tb.ptp_clock.set_ts_tod_ns(12345678)
tb.ptp_clock.set_ts_64(12345678) tb.ptp_clock.set_ts_rel_ns(12345678)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
assert dut.ts_96.value.integer == 12345678+((tb.ptp_clock.period_ns << 16) + tb.ptp_clock.period_fns) assert dut.ts_tod.value.integer == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
assert dut.ts_64.value.integer == 12345678+((tb.ptp_clock.period_ns << 16) + tb.ptp_clock.period_fns) assert dut.ts_rel.value.integer == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
assert dut.ts_step.value.integer == 1 assert dut.ts_step.value.integer == 1
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
await ClockCycles(dut.clk, 2000) await ClockCycles(dut.clk, 2000)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -158,15 +169,15 @@ async def run_seconds_increment(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.set_ts_96(999990000*2**16) tb.ptp_clock.set_ts_tod_ns(999990000)
tb.ptp_clock.set_ts_64(999990000*2**16) tb.ptp_clock.set_ts_rel_ns(999990000)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
saw_pps = False saw_pps = False
@@ -175,30 +186,31 @@ async def run_seconds_increment(dut):
if dut.pps.value.integer: if dut.pps.value.integer:
saw_pps = True saw_pps = True
assert dut.ts_96.value.integer >> 48 == 1 assert dut.ts_tod.value.integer >> 48 == 1
assert dut.ts_96.value.integer & 0xffffffffffff < 10*2**16 assert dut.ts_tod.value.integer & 0xffffffffffff < 10*2**16
assert saw_pps assert saw_pps
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -211,35 +223,35 @@ async def run_frequency_adjustment(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.period_ns = 0x6 tb.ptp_clock.set_period(0x6, 0x66240000)
tb.ptp_clock.period_fns = 0x6624
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
await ClockCycles(dut.clk, 10000) await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta * 6.4/(6+(0x6624+2/5)/2**16) tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6624+2/5)/2**16) tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
@@ -252,36 +264,35 @@ async def run_drift_adjustment(dut):
await tb.reset() await tb.reset()
tb.ptp_clock.drift_ns = 0 tb.ptp_clock.set_drift(20000, 5)
tb.ptp_clock.drift_fns = 20
tb.ptp_clock.drift_rate = 5
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
start_time = get_sim_time('sec') start_time = Decimal(get_sim_time('fs')).scaleb(-6)
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) start_ts_tod = tb.get_ts_tod_ns()
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9 start_ts_rel = tb.get_ts_rel_ns()
await ClockCycles(dut.clk, 10000) await ClockCycles(dut.clk, 10000)
stop_time = get_sim_time('sec') stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9) stop_ts_tod = tb.get_ts_tod_ns()
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9 stop_ts_rel = tb.get_ts_rel_ns()
time_delta = stop_time-start_time time_delta = stop_time-start_time
ts_96_delta = stop_ts_96-start_ts_96 ts_tod_delta = stop_ts_tod-start_ts_tod
ts_64_delta = stop_ts_64-start_ts_64 ts_rel_delta = stop_ts_rel-start_ts_rel
ts_96_diff = time_delta - ts_96_delta * 6.4/(6+(0x6666+20/5)/2**16) tb.log.info("sim time delta : %s ns", time_delta)
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6666+20/5)/2**16) tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
tb.log.info("sim time delta : %g s", time_delta) ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("96 bit ts delta : %g s", ts_96_delta) ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
assert abs(ts_96_diff) < 1e-12 tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
assert abs(ts_64_diff) < 1e-12 tb.log.info("rel ts diff : %s ns", ts_rel_diff)
assert abs(ts_tod_diff) < 1e-3
assert abs(ts_rel_diff) < 1e-3
await RisingEdge(dut.clk) await RisingEdge(dut.clk)
await RisingEdge(dut.clk) await RisingEdge(dut.clk)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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