9 Commits

Author SHA1 Message Date
Alex Forencich
2ee51890f5 Release v0.1.16 2021-08-31 00:24:16 -07:00
Alex Forencich
fbdf4149b3 Update readme 2021-08-31 00:23:36 -07:00
Alex Forencich
03156ff759 Support PTP timestamp tags in MAC model 2021-08-31 00:23:07 -07:00
Alex Forencich
8956de42b5 Bump to dev version 2021-04-12 18:43:42 -07:00
Alex Forencich
de18e62024 Release v0.1.14 2021-04-12 18:18:26 -07:00
Alex Forencich
14738c1dae Print model configuration 2021-04-12 18:18:01 -07:00
Alex Forencich
3d43812c7b Rename byte_width to byte_lanes 2021-04-12 15:17:02 -07:00
Alex Forencich
008d903bb9 Send data without using pop 2021-04-12 15:10:52 -07:00
Alex Forencich
0bd66da868 Bump to dev version 2021-04-01 15:29:03 -07:00
10 changed files with 127 additions and 67 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_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
tx_ptp_time=dut.tx_ptp_time, tx_ptp_time=dut.tx_ptp_time,
tx_ptp_ts=dut.tx_ptp_ts, 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, tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
rx_clk=dut.rx_clk, rx_clk=dut.rx_clk,
rx_rst=dut.rx_rst, 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() 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 #### Signals
* `tdata`: payload data, must be a multiple of 8 bits * `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) * `tready`: indicates sink is ready for data (tx only)
* `tlast`: marks the last cycle of a frame * `tlast`: marks the last cycle of a frame
* `tkeep`: qualifies data byte, data bus width must be evenly divisible by `tkeep` signal width * `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 * `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` * `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`: captured transmit PTP timestamp
* `ptp_ts_tag`: captured transmit PTP timestamp tag
* `ptp_ts_valid`: qualifies captured transmit PTP timestamp * `ptp_ts_valid`: qualifies captured transmit PTP timestamp
#### Constructor parameters (`EthMacRx` and `EthMacTx`): #### Constructor parameters (`EthMacRx` and `EthMacTx`):
@@ -510,7 +514,8 @@ To receive data, call `recv()` or `recv_nowait()`. Optionally call `wait()` to
* _clock_: clock signal * _clock_: clock signal
* _reset_: reset signal (optional) * _reset_: reset signal (optional)
* _ptp_time_: PTP time input from PHC (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) * _ptp_ts_valid_: PTP timestamp valid (optional) (tx)
* _reset_active_level_: reset active level (optional, default `True`) * _reset_active_level_: reset active level (optional, default `True`)
* _ifg_: IFG size in byte times (optional, default `12`) * _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_clk_: transmit clock
* _tx_rst_: transmit reset (optional) * _tx_rst_: transmit reset (optional)
* _tx_ptp_time_: transmit PTP time input from PHC (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) * _tx_ptp_ts_valid_: transmit PTP timestamp valid (optional)
* _rx_bus_: `AxiStreamBus` object containing receive AXI stream interface signals * _rx_bus_: `AxiStreamBus` object containing receive AXI stream interface signals
* _rx_clk_: receive clock * _rx_clk_: receive clock
@@ -565,6 +571,7 @@ Attributes:
* `sim_time_start`: simulation time of first transfer cycle of frame. * `sim_time_start`: simulation time of first transfer cycle of frame.
* `sim_time_sfd`: simulation time at which the SFD was transferred. * `sim_time_sfd`: simulation time at which the SFD was transferred.
* `sim_time_end`: simulation time of last transfer cycle of frame. * `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 * `ptp_timestamp`: captured value of `ptp_time` at frame SFD
* `tx_complete`: event or callable triggered when frame is transmitted. * `tx_complete`: event or callable triggered when frame is transmitted.

View File

@@ -49,6 +49,7 @@ class EthMacFrame:
self.sim_time_sfd = None self.sim_time_sfd = None
self.sim_time_end = None self.sim_time_end = None
self.ptp_timestamp = None self.ptp_timestamp = None
self.ptp_tag = None
self.tx_complete = None self.tx_complete = None
if type(data) is EthMacFrame: if type(data) is EthMacFrame:
@@ -57,6 +58,7 @@ class EthMacFrame:
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
self.ptp_timestamp = data.ptp_timestamp self.ptp_timestamp = data.ptp_timestamp
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 = bytearray(data)
@@ -104,7 +106,8 @@ class EthMacFrame:
f"sim_time_start={self.sim_time_start!r}, " f"sim_time_start={self.sim_time_start!r}, "
f"sim_time_sfd={self.sim_time_sfd!r}, " f"sim_time_sfd={self.sim_time_sfd!r}, "
f"sim_time_end={self.sim_time_end!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): def __len__(self):
@@ -118,7 +121,7 @@ class EthMacFrame:
class EthMacTx(Reset): 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): reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
self.bus = bus self.bus = bus
@@ -126,6 +129,7 @@ class EthMacTx(Reset):
self.reset = reset self.reset = reset
self.ptp_time = ptp_time self.ptp_time = ptp_time
self.ptp_ts = ptp_ts self.ptp_ts = ptp_ts
self.ptp_ts_tag = ptp_ts_tag
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
@@ -172,6 +176,10 @@ class EthMacTx(Reset):
self.log.info(" tuser width: %d bits", len(self.bus.tuser)) self.log.info(" tuser width: %d bits", len(self.bus.tuser))
else: else:
self.log.info(" tuser: not present") self.log.info(" tuser: not present")
if self.ptp_time:
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
else:
self.log.info(" ptp_time: not present")
if self.bus.tready is None: if self.bus.tready is None:
raise ValueError("tready is required") raise ValueError("tready is required")
@@ -185,6 +193,8 @@ class EthMacTx(Reset):
if self.ptp_ts: if self.ptp_ts:
self.ptp_ts.setimmediatevalue(0) self.ptp_ts.setimmediatevalue(0)
if self.ptp_ts_tag:
self.ptp_ts_tag.setimmediatevalue(0)
if self.ptp_ts_valid: if self.ptp_ts_valid:
self.ptp_ts_valid.setimmediatevalue(0) self.ptp_ts_valid.setimmediatevalue(0)
@@ -274,7 +284,8 @@ class EthMacTx(Reset):
if self.ptp_time: if self.ptp_time:
frame.ptp_timestamp = self.ptp_time.value.integer 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 # process frame data
while True: while True:
@@ -316,8 +327,10 @@ class EthMacTx(Reset):
self.ptp_ts_valid <= 0 self.ptp_ts_valid <= 0
if not self.ts_queue.empty(): if not self.ts_queue.empty():
ts = self.ts_queue.get_nowait() ts, tag = self.ts_queue.get_nowait()
self.ptp_ts <= ts self.ptp_ts <= ts
if self.ptp_ts_tag is not None:
self.ptp_ts_tag <= tag
self.ptp_ts_valid <= 1 self.ptp_ts_valid <= 1
@@ -378,6 +391,10 @@ class EthMacRx(Reset):
self.log.info(" tuser width: %d bits", len(self.bus.tuser)) self.log.info(" tuser width: %d bits", len(self.bus.tuser))
else: else:
self.log.info(" tuser: not present") self.log.info(" tuser: not present")
if self.ptp_time:
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
else:
self.log.info(" ptp_time: not present")
if self.byte_size != 8: if self.byte_size != 8:
raise ValueError("Byte size must be 8") raise ValueError("Byte size must be 8")
@@ -462,6 +479,7 @@ class EthMacRx(Reset):
async def _run(self): async def _run(self):
frame = None frame = None
frame_offset = 0
tuser = 0 tuser = 0
self.active = False self.active = False
@@ -477,6 +495,7 @@ class EthMacRx(Reset):
frame.sim_time_sfd = None frame.sim_time_sfd = None
frame.sim_time_end = None frame.sim_time_end = None
self.log.info("TX frame: %s", frame) self.log.info("TX frame: %s", frame)
frame_offset = 0
# wait for preamble time # wait for preamble time
await Timer(self.time_scale*8*8//self.speed, 'step') await Timer(self.time_scale*8*8//self.speed, 'step')
@@ -499,11 +518,12 @@ class EthMacRx(Reset):
cycle.tuser = tuser cycle.tuser = tuser
for offset in range(self.byte_lanes): for offset in range(self.byte_lanes):
cycle.tdata |= (frame.data.pop(0) & self.byte_mask) << (offset * self.byte_size) cycle.tdata |= (frame.data[frame_offset] & self.byte_mask) << (offset * self.byte_size)
cycle.tkeep |= 1 << offset cycle.tkeep |= 1 << offset
byte_count += 1 byte_count += 1
frame_offset += 1
if len(frame.data) == 0: if frame_offset >= len(frame.data):
cycle.tlast = 1 cycle.tlast = 1
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
frame.handle_tx_complete() frame.handle_tx_complete()
@@ -521,13 +541,13 @@ class EthMacRx(Reset):
class EthMac: 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, 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,
rx_bus=None, rx_clk=None, rx_rst=None, rx_ptp_time=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): reset_active_level=True, ifg=12, speed=1000e6, *args, **kwargs):
super().__init__(*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) reset_active_level=reset_active_level, ifg=ifg, speed=speed)
self.rx = EthMacRx(rx_bus, rx_clk, rx_rst, rx_ptp_time, self.rx = EthMacRx(rx_bus, rx_clk, rx_rst, rx_ptp_time,
reset_active_level=reset_active_level, ifg=ifg, speed=speed) reset_active_level=reset_active_level, ifg=ifg, speed=speed)

View File

@@ -258,6 +258,9 @@ class GmiiSource(Reset):
async def _run(self): async def _run(self):
frame = None frame = None
frame_offset = 0
frame_data = None
frame_error = None
ifg_cnt = 0 ifg_cnt = 0
self.active = False self.active = False
@@ -286,28 +289,32 @@ class GmiiSource(Reset):
self.mii_mode = bool(self.mii_select.value.integer) self.mii_mode = bool(self.mii_select.value.integer)
if self.mii_mode: if self.mii_mode:
mii_data = [] # convert to MII
mii_error = [] frame_data = []
frame_error = []
for b, e in zip(frame.data, frame.error): for b, e in zip(frame.data, frame.error):
mii_data.append(b & 0x0F) frame_data.append(b & 0x0F)
mii_data.append(b >> 4) frame_data.append(b >> 4)
mii_error.append(e) frame_error.append(e)
mii_error.append(e) frame_error.append(e)
frame.data = mii_data else:
frame.error = mii_error frame_data = frame.data
frame_error = frame.error
self.active = True self.active = True
frame_offset = 0
if frame is not None: if frame is not None:
d = frame.data.pop(0) d = frame_data[frame_offset]
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD): if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD):
frame.sim_time_sfd = get_sim_time() frame.sim_time_sfd = get_sim_time()
self.data <= d self.data <= d
if self.er is not None: if self.er is not None:
self.er <= frame.error.pop(0) self.er <= frame_error[frame_offset]
self.dv <= 1 self.dv <= 1
frame_offset += 1
if not frame.data: if frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1) ifg_cnt = max(self.ifg, 1)
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
frame.handle_tx_complete() frame.handle_tx_complete()

View File

@@ -159,6 +159,9 @@ class MiiSource(Reset):
async def _run(self): async def _run(self):
frame = None frame = None
frame_offset = 0
frame_data = None
frame_error = None
ifg_cnt = 0 ifg_cnt = 0
self.active = False self.active = False
@@ -183,28 +186,29 @@ class MiiSource(Reset):
self.log.info("TX frame: %s", frame) self.log.info("TX frame: %s", frame)
frame.normalize() frame.normalize()
mii_data = [] # convert to MII
mii_error = [] frame_data = []
frame_error = []
for b, e in zip(frame.data, frame.error): for b, e in zip(frame.data, frame.error):
mii_data.append(b & 0x0F) frame_data.append(b & 0x0F)
mii_data.append(b >> 4) frame_data.append(b >> 4)
mii_error.append(e) frame_error.append(e)
mii_error.append(e) frame_error.append(e)
frame.data = mii_data
frame.error = mii_error
self.active = True self.active = True
frame_offset = 0
if frame is not None: if frame is not None:
d = frame.data.pop(0) d = frame_data[frame_offset]
if frame.sim_time_sfd is None and d == 0xD: if frame.sim_time_sfd is None and d == 0xD:
frame.sim_time_sfd = get_sim_time() frame.sim_time_sfd = get_sim_time()
self.data <= d self.data <= d
if self.er is not None: if self.er is not None:
self.er <= frame.error.pop(0) self.er <= frame_error[frame_offset]
self.dv <= 1 self.dv <= 1
frame_offset += 1
if not frame.data: if frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1) ifg_cnt = max(self.ifg, 1)
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
frame.handle_tx_complete() frame.handle_tx_complete()

View File

@@ -157,6 +157,9 @@ class RgmiiSource(Reset):
async def _run(self): async def _run(self):
frame = None frame = None
frame_offset = 0
frame_data = None
frame_error = None
ifg_cnt = 0 ifg_cnt = 0
self.active = False self.active = False
d = 0 d = 0
@@ -192,27 +195,31 @@ class RgmiiSource(Reset):
self.mii_mode = bool(self.mii_select.value.integer) self.mii_mode = bool(self.mii_select.value.integer)
if self.mii_mode: if self.mii_mode:
mii_data = [] # convert to MII
mii_error = [] frame_data = []
frame_error = []
for b, e in zip(frame.data, frame.error): for b, e in zip(frame.data, frame.error):
mii_data.append((b & 0x0F)*0x11) frame_data.append((b & 0x0F)*0x11)
mii_data.append((b >> 4)*0x11) frame_data.append((b >> 4)*0x11)
mii_error.append(e) frame_error.append(e)
mii_error.append(e) frame_error.append(e)
frame.data = mii_data else:
frame.error = mii_error frame_data = frame.data
frame_error = frame.error
self.active = True self.active = True
frame_offset = 0
if frame is not None: if frame is not None:
d = frame.data.pop(0) d = frame_data[frame_offset]
er = frame.error.pop(0) er = frame_error[frame_offset]
en = 1 en = 1
frame_offset += 1
if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD, 0xDD): if frame.sim_time_sfd is None and d in (EthPre.SFD, 0xD, 0xDD):
frame.sim_time_sfd = get_sim_time() frame.sim_time_sfd = get_sim_time()
if not frame.data: if frame_offset >= len(frame_data):
ifg_cnt = max(self.ifg, 1) ifg_cnt = max(self.ifg, 1)
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
frame.handle_tx_complete() frame.handle_tx_complete()

View File

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

View File

@@ -170,14 +170,19 @@ class XgmiiSource(Reset):
self.queue_occupancy_limit_frames = -1 self.queue_occupancy_limit_frames = -1
self.width = len(self.data) self.width = len(self.data)
self.byte_width = len(self.ctrl) self.byte_size = 8
self.byte_lanes = len(self.ctrl)
assert self.width == self.byte_width * 8 assert self.width == self.byte_lanes * self.byte_size
self.log.info("XGMII source model configuration")
self.log.info(" Byte size: %d bits", self.byte_size)
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
self.idle_d = 0 self.idle_d = 0
self.idle_c = 0 self.idle_c = 0
for k in range(self.byte_width): for k in range(self.byte_lanes):
self.idle_d |= XgmiiCtrl.IDLE << k*8 self.idle_d |= XgmiiCtrl.IDLE << k*8
self.idle_c |= 1 << k self.idle_c |= 1 << k
@@ -262,6 +267,7 @@ class XgmiiSource(Reset):
async def _run(self): async def _run(self):
frame = None frame = None
frame_offset = 0
ifg_cnt = 0 ifg_cnt = 0
deficit_idle_cnt = 0 deficit_idle_cnt = 0
self.active = False self.active = False
@@ -270,9 +276,9 @@ class XgmiiSource(Reset):
await RisingEdge(self.clock) await RisingEdge(self.clock)
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
if ifg_cnt + deficit_idle_cnt > self.byte_width-1 or (not self.enable_dic and ifg_cnt > 4): if ifg_cnt + deficit_idle_cnt > self.byte_lanes-1 or (not self.enable_dic and ifg_cnt > 4):
# in IFG # in IFG
ifg_cnt = ifg_cnt - self.byte_width ifg_cnt = ifg_cnt - self.byte_lanes
if ifg_cnt < 0: if ifg_cnt < 0:
if self.enable_dic: if self.enable_dic:
deficit_idle_cnt = max(deficit_idle_cnt+ifg_cnt, 0) deficit_idle_cnt = max(deficit_idle_cnt+ifg_cnt, 0)
@@ -306,7 +312,7 @@ class XgmiiSource(Reset):
else: else:
min_ifg = 0 min_ifg = 0
if self.byte_width > 4 and (ifg_cnt > min_ifg or self.force_offset_start): if self.byte_lanes > 4 and (ifg_cnt > min_ifg or self.force_offset_start):
ifg_cnt = ifg_cnt-4 ifg_cnt = ifg_cnt-4
frame.start_lane = 4 frame.start_lane = 4
frame.data = bytearray([XgmiiCtrl.IDLE]*4)+frame.data frame.data = bytearray([XgmiiCtrl.IDLE]*4)+frame.data
@@ -316,6 +322,7 @@ class XgmiiSource(Reset):
deficit_idle_cnt = max(deficit_idle_cnt+ifg_cnt, 0) deficit_idle_cnt = max(deficit_idle_cnt+ifg_cnt, 0)
ifg_cnt = 0 ifg_cnt = 0
self.active = True self.active = True
frame_offset = 0
else: else:
# clear counters # clear counters
deficit_idle_cnt = 0 deficit_idle_cnt = 0
@@ -325,16 +332,17 @@ class XgmiiSource(Reset):
d_val = 0 d_val = 0
c_val = 0 c_val = 0
for k in range(self.byte_width): for k in range(self.byte_lanes):
if frame is not None: if frame is not None:
d = frame.data.pop(0) d = frame.data[frame_offset]
if frame.sim_time_sfd is None and d == EthPre.SFD: if frame.sim_time_sfd is None and d == EthPre.SFD:
frame.sim_time_sfd = get_sim_time() frame.sim_time_sfd = get_sim_time()
d_val |= d << k*8 d_val |= d << k*8
c_val |= frame.ctrl.pop(0) << k c_val |= frame.ctrl[frame_offset] << k
frame_offset += 1
if not frame.data: if frame_offset >= len(frame.data):
ifg_cnt = max(self.ifg - (self.byte_width-k), 0) ifg_cnt = max(self.ifg - (self.byte_lanes-k), 0)
frame.sim_time_end = get_sim_time() frame.sim_time_end = get_sim_time()
frame.handle_tx_complete() frame.handle_tx_complete()
frame = None frame = None
@@ -377,9 +385,14 @@ class XgmiiSink(Reset):
self.queue_occupancy_frames = 0 self.queue_occupancy_frames = 0
self.width = len(self.data) self.width = len(self.data)
self.byte_width = len(self.ctrl) self.byte_size = 8
self.byte_lanes = len(self.ctrl)
assert self.width == self.byte_width * 8 assert self.width == self.byte_lanes * self.byte_size
self.log.info("XGMII sink model configuration")
self.log.info(" Byte size: %d bits", self.byte_size)
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
self._run_cr = None self._run_cr = None
@@ -447,7 +460,7 @@ class XgmiiSink(Reset):
await RisingEdge(self.clock) await RisingEdge(self.clock)
if self.enable is None or self.enable.value: if self.enable is None or self.enable.value:
for offset in range(self.byte_width): for offset in range(self.byte_lanes):
d_val = (self.data.value.integer >> (offset*8)) & 0xff d_val = (self.data.value.integer >> (offset*8)) & 0xff
c_val = (self.ctrl.value.integer >> offset) & 1 c_val = (self.ctrl.value.integer >> offset) & 1

View File

@@ -54,6 +54,7 @@ class TB:
tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"), tx_bus=AxiStreamBus.from_prefix(dut, "tx_axis"),
tx_ptp_time=dut.tx_ptp_time, tx_ptp_time=dut.tx_ptp_time,
tx_ptp_ts=dut.tx_ptp_ts, 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, tx_ptp_ts_valid=dut.tx_ptp_ts_valid,
rx_clk=dut.rx_clk, rx_clk=dut.rx_clk,
rx_rst=dut.rx_rst, rx_rst=dut.rx_rst,

View File

@@ -36,11 +36,12 @@ module test_eth_mac
inout wire [63:0] tx_axis_tdata, inout wire [63:0] tx_axis_tdata,
inout wire [7:0] tx_axis_tkeep, inout wire [7:0] tx_axis_tkeep,
inout wire tx_axis_tlast, 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_tvalid,
inout wire tx_axis_tready, inout wire tx_axis_tready,
inout wire [95:0] tx_ptp_time, inout wire [95:0] tx_ptp_time,
inout wire [95:0] tx_ptp_ts, inout wire [95:0] tx_ptp_ts,
inout wire [15: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,

View File

@@ -123,7 +123,7 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
tb = TB(dut) tb = TB(dut)
byte_width = tb.source.width // 8 byte_lanes = tb.source.byte_lanes
tb.source.ifg = ifg tb.source.ifg = ifg
tb.source.enable_dic = enable_dic tb.source.enable_dic = enable_dic
@@ -164,23 +164,23 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
for test_data in test_frames: for test_data in test_frames:
if ifg == 0: if ifg == 0:
lane = 0 lane = 0
if force_offset_start and byte_width > 4: if force_offset_start and byte_lanes > 4:
lane = 4 lane = 4
start_lane_ref.append(lane) start_lane_ref.append(lane)
lane = (lane + len(test_data)+4+ifg) % byte_width lane = (lane + len(test_data)+4+ifg) % byte_lanes
if enable_dic: if enable_dic:
offset = lane % 4 offset = lane % 4
if deficit_idle_count+offset >= 4: if deficit_idle_count+offset >= 4:
offset += 4 offset += 4
lane = (lane - offset) % byte_width lane = (lane - offset) % byte_lanes
deficit_idle_count = (deficit_idle_count + offset) % 4 deficit_idle_count = (deficit_idle_count + offset) % 4
else: else:
offset = lane % 4 offset = lane % 4
if offset > 0: if offset > 0:
offset += 4 offset += 4
lane = (lane - offset) % byte_width lane = (lane - offset) % byte_lanes
tb.log.info("start_lane_ref: %s", start_lane_ref) tb.log.info("start_lane_ref: %s", start_lane_ref)