10 Commits

Author SHA1 Message Date
Alex Forencich
6a35c31b4b Release v0.1.18 2021-11-07 12:40:42 -08:00
Alex Forencich
73fe54705f Remove deprecated assignments 2021-11-07 01:21:39 -08:00
Alex Forencich
448451e274 Add BASE-R related mappings 2021-10-15 02:00:03 -07:00
Alex Forencich
21c2c05c57 Normalize names 2021-10-14 19:13:51 -07:00
Alex Forencich
ab84a3b100 Add cocotb framework classifier 2021-08-31 14:43:03 -07:00
Alex Forencich
0f3060b9ba Bump to dev version 2021-08-31 01:04:20 -07:00
Alex Forencich
2ee51890f5 Release v0.1.16 2021-08-31 00:24:16 -07:00
Alex Forencich
fbdf4149b3 Update readme 2021-08-31 00:23:36 -07:00
Alex Forencich
03156ff759 Support PTP timestamp tags in MAC model 2021-08-31 00:23:07 -07:00
Alex Forencich
8956de42b5 Bump to dev version 2021-04-12 18:43:42 -07:00
12 changed files with 134 additions and 77 deletions

View File

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

View File

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

View File

@@ -49,6 +49,7 @@ class EthMacFrame:
self.sim_time_sfd = None
self.sim_time_end = None
self.ptp_timestamp = None
self.ptp_tag = None
self.tx_complete = None
if type(data) is EthMacFrame:
@@ -57,6 +58,7 @@ class EthMacFrame:
self.sim_time_sfd = data.sim_time_sfd
self.sim_time_end = data.sim_time_end
self.ptp_timestamp = data.ptp_timestamp
self.ptp_tag = data.ptp_tag
self.tx_complete = data.tx_complete
else:
self.data = bytearray(data)
@@ -104,7 +106,8 @@ class EthMacFrame:
f"sim_time_start={self.sim_time_start!r}, "
f"sim_time_sfd={self.sim_time_sfd!r}, "
f"sim_time_end={self.sim_time_end!r}, "
f"ptp_timestamp={self.ptp_timestamp!r})"
f"ptp_timestamp={self.ptp_timestamp!r}, "
f"ptp_tag={self.ptp_tag!r})"
)
def __len__(self):
@@ -118,7 +121,7 @@ class EthMacFrame:
class EthMacTx(Reset):
def __init__(self, bus, clock, reset=None, ptp_time=None, ptp_ts=None, ptp_ts_valid=None,
def __init__(self, bus, clock, reset=None, ptp_time=None, ptp_ts=None, ptp_ts_tag=None, ptp_ts_valid=None,
reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
self.bus = bus
@@ -126,6 +129,7 @@ class EthMacTx(Reset):
self.reset = reset
self.ptp_time = ptp_time
self.ptp_ts = ptp_ts
self.ptp_ts_tag = ptp_ts_tag
self.ptp_ts_valid = ptp_ts_valid
self.ifg = ifg
self.speed = speed
@@ -189,6 +193,8 @@ class EthMacTx(Reset):
if self.ptp_ts:
self.ptp_ts.setimmediatevalue(0)
if self.ptp_ts_tag:
self.ptp_ts_tag.setimmediatevalue(0)
if self.ptp_ts_valid:
self.ptp_ts_valid.setimmediatevalue(0)
@@ -247,7 +253,7 @@ class EthMacTx(Reset):
self._run_ts_cr = None
if self.ptp_ts_valid:
self.ptp_ts_valid <= 0
self.ptp_ts_valid.value = 0
self.active = False
@@ -278,7 +284,8 @@ class EthMacTx(Reset):
if self.ptp_time:
frame.ptp_timestamp = self.ptp_time.value.integer
self.ts_queue.put_nowait(frame.ptp_timestamp)
frame.ptp_tag = cycle.tuser.integer >> 1
self.ts_queue.put_nowait((frame.ptp_timestamp, frame.ptp_tag))
# process frame data
while True:
@@ -317,12 +324,14 @@ class EthMacTx(Reset):
async def _run_ts(self):
while True:
await RisingEdge(self.clock)
self.ptp_ts_valid <= 0
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):
@@ -532,13 +541,13 @@ class EthMacRx(Reset):
class EthMac:
def __init__(self, tx_bus=None, tx_clk=None, tx_rst=None, tx_ptp_time=None, tx_ptp_ts=None, tx_ptp_ts_valid=None,
rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=None,
def __init__(self, tx_bus=None, tx_clk=None, tx_rst=None, tx_ptp_time=None, tx_ptp_ts=None, tx_ptp_ts_tag=None,
tx_ptp_ts_valid=None, rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=None,
reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tx = EthMacTx(tx_bus, tx_clk, tx_rst, tx_ptp_time, tx_ptp_ts, tx_ptp_ts_valid,
self.tx = EthMacTx(tx_bus, tx_clk, tx_rst, tx_ptp_time, tx_ptp_ts, tx_ptp_ts_tag, tx_ptp_ts_valid,
reset_active_level=reset_active_level, ifg=ifg, speed=speed)
self.rx = EthMacRx(rx_bus, rx_clk, rx_rst, rx_ptp_time,
reset_active_level=reset_active_level, ifg=ifg, speed=speed)

View File

@@ -239,10 +239,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)
@@ -308,10 +308,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,10 +321,10 @@ 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()
@@ -536,8 +536,8 @@ class GmiiPhy:
while True:
await t
self.rx_clk <= 1
self.tx_clk <= 1
self.rx_clk.value = 1
self.tx_clk.value = 1
await t
self.rx_clk <= 0
self.tx_clk <= 0
self.rx_clk.value = 0
self.tx_clk.value = 0

View File

@@ -140,10 +140,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)
@@ -202,10 +202,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,10 +215,10 @@ 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()
@@ -410,8 +410,8 @@ class MiiPhy:
while True:
await t
self.tx_clk <= 1
self.rx_clk <= 1
self.tx_clk.value = 1
self.rx_clk.value = 1
await t
self.tx_clk <= 0
self.rx_clk <= 0
self.tx_clk.value = 0
self.rx_clk.value = 0

View File

@@ -195,13 +195,13 @@ class PtpClock(Reset):
self.ts_64_fns = 0
self.drift_cnt = 0
if self.ts_96 is not None:
self.ts_96 <= 0
self.ts_96.value = 0
if self.ts_64 is not None:
self.ts_64 <= 0
self.ts_64.value = 0
if self.ts_step is not None:
self.ts_step <= 0
self.ts_step.value = 0
if self.pps is not None:
self.pps <= 0
self.pps.value = 0
else:
self.log.info("Reset de-asserted")
if self._run_cr is None:
@@ -212,11 +212,11 @@ class PtpClock(Reset):
await RisingEdge(self.clock)
if self.ts_step is not None:
self.ts_step <= self.ts_updated
self.ts_step.value = self.ts_updated
self.ts_updated = False
if self.pps is not None:
self.pps <= 0
self.pps.value = 0
# increment 96 bit timestamp
if self.ts_96 is not None or self.pps is not None:
@@ -229,13 +229,13 @@ class PtpClock(Reset):
self.ts_96_s += 1
t -= (1000000000 << 16)
if self.pps is not None:
self.pps <= 1
self.pps.value = 1
self.ts_96_fns = t & 0xffff
self.ts_96_ns = t >> 16
if self.ts_96 is not None:
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns)
self.ts_96.value = (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns)
# increment 64 bit timestamp
if self.ts_64 is not None:
@@ -247,7 +247,7 @@ class PtpClock(Reset):
self.ts_64_fns = t & 0xffff
self.ts_64_ns = t >> 16
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns
self.ts_64.value = (self.ts_64_ns << 16) | self.ts_64_fns
if self.drift_rate:
if self.drift_cnt > 0:
@@ -286,7 +286,7 @@ class PtpClockSimTime:
if self.ts_64 is not None:
self.ts_64.setimmediatevalue(0)
if self.pps is not None:
self.pps <= 0
self.pps.value = 0
self._run_cr = cocotb.fork(self._run())
@@ -321,12 +321,12 @@ class PtpClockSimTime:
self.ts_96_fns = self.ts_64_fns
if self.ts_96 is not None:
self.ts_96 <= (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
self.ts_96.value = (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
if self.ts_64 is not None:
self.ts_64 <= (self.ts_64_ns << 16) | self.ts_64_fns
self.ts_64.value = (self.ts_64_ns << 16) | self.ts_64_fns
if self.pps is not None:
self.pps <= int(self.last_ts_96_s != self.ts_96_s)
self.pps.value = int(self.last_ts_96_s != self.ts_96_s)
self.last_ts_96_s = self.ts_96_s

View File

@@ -140,8 +140,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)
@@ -170,8 +170,8 @@ class RgmiiSource(Reset):
await RisingEdge(self.clock)
# 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:
if ifg_cnt > 0:
@@ -235,8 +235,8 @@ class RgmiiSource(Reset):
await FallingEdge(self.clock)
# send low nibble after falling edge, leading in to rising edge
self.data <= d & 0x0F
self.ctrl <= en
self.data.value = d & 0x0F
self.ctrl.value = en
class RgmiiSink(Reset):
@@ -448,6 +448,6 @@ class RgmiiPhy:
while True:
await t
self.rx_clk <= 1
self.rx_clk.value = 1
await t
self.rx_clk <= 0
self.rx_clk.value = 0

View File

@@ -1 +1 @@
__version__ = "0.1.14"
__version__ = "0.1.18"

View File

@@ -250,8 +250,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)
@@ -351,11 +351,11 @@ 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()

View File

@@ -17,9 +17,10 @@ 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]

View File

@@ -54,6 +54,7 @@ class TB:
tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
tx_ptp_time=dut.tx_ptp_time,
tx_ptp_ts=dut.tx_ptp_ts,
tx_ptp_ts_tag=dut.tx_ptp_ts_tag,
tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
rx_clk=dut.rx_clk,
rx_rst=dut.rx_rst,

View File

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