diff --git a/cocotbext/eth/gmii.py b/cocotbext/eth/gmii.py index dfc362d..c2b330c 100644 --- a/cocotbext/eth/gmii.py +++ b/cocotbext/eth/gmii.py @@ -33,6 +33,7 @@ from cocotb.utils import get_sim_time from .version import __version__ from .constants import EthPre, ETH_PREAMBLE +from .reset import Reset class GmiiFrame: @@ -114,7 +115,7 @@ class GmiiFrame: return bytes(self.data) -class GmiiSource: +class GmiiSource(Reset): def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -145,8 +146,6 @@ class GmiiSource: self.width = 8 self.byte_width = 1 - self.reset = reset - assert len(self.data) == 8 self.data.setimmediatevalue(0) if self.er is not None: @@ -155,7 +154,9 @@ class GmiiSource: assert len(self.dv) == 1 self.dv.setimmediatevalue(0) - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def send(self, frame): self.send_nowait(frame) @@ -179,6 +180,23 @@ class GmiiSource: while not self.idle(): await RisingEdge(self.clock) + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + self.data <= 0 + if self.er is not None: + self.er <= 0 + self.dv <= 0 + async def _run(self): frame = None ifg_cnt = 0 @@ -187,16 +205,6 @@ class GmiiSource: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - ifg_cnt = 0 - self.active = False - self.data <= 0 - if self.er is not None: - self.er <= 0 - self.dv <= 0 - continue - if self.enable is None or self.enable.value: if ifg_cnt > 0: # in IFG @@ -243,7 +251,7 @@ class GmiiSource: self.active = False -class GmiiSink: +class GmiiSink(Reset): def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -274,15 +282,15 @@ class GmiiSink: self.width = 8 self.byte_width = 1 - self.reset = reset - assert len(self.data) == 8 if self.er is not None: assert len(self.er) == 1 if self.dv is not None: assert len(self.dv) == 1 - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def recv(self, compact=True): while self.empty(): @@ -316,6 +324,19 @@ class GmiiSink: else: await self.sync.wait() + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + async def _run(self): frame = None self.active = False @@ -323,11 +344,6 @@ class GmiiSink: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - self.active = False - continue - if self.enable is None or self.enable.value: d_val = self.data.value.integer dv_val = self.dv.value.integer diff --git a/cocotbext/eth/mii.py b/cocotbext/eth/mii.py index 055c50d..f62302f 100644 --- a/cocotbext/eth/mii.py +++ b/cocotbext/eth/mii.py @@ -32,9 +32,10 @@ from cocotb.utils import get_sim_time, get_sim_steps from .version import __version__ from .gmii import GmiiFrame from .constants import EthPre +from .reset import Reset -class MiiSource: +class MiiSource(Reset): def __init__(self, data, er, dv, clock, reset=None, enable=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -63,8 +64,6 @@ class MiiSource: self.width = 4 self.byte_width = 1 - self.reset = reset - assert len(self.data) == 4 self.data.setimmediatevalue(0) if self.er is not None: @@ -73,7 +72,9 @@ class MiiSource: assert len(self.dv) == 1 self.dv.setimmediatevalue(0) - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def send(self, frame): self.send_nowait(frame) @@ -97,6 +98,23 @@ class MiiSource: while not self.idle(): await RisingEdge(self.clock) + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + self.data <= 0 + if self.er is not None: + self.er <= 0 + self.dv <= 0 + async def _run(self): frame = None ifg_cnt = 0 @@ -105,16 +123,6 @@ class MiiSource: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - ifg_cnt = 0 - self.active = False - self.data <= 0 - if self.er is not None: - self.er <= 0 - self.dv <= 0 - continue - if self.enable is None or self.enable.value: if ifg_cnt > 0: # in IFG @@ -157,7 +165,7 @@ class MiiSource: self.active = False -class MiiSink: +class MiiSink(Reset): def __init__(self, data, er, dv, clock, reset=None, enable=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -185,15 +193,15 @@ class MiiSink: self.width = 4 self.byte_width = 1 - self.reset = reset - assert len(self.data) == 4 if self.er is not None: assert len(self.er) == 1 if self.dv is not None: assert len(self.dv) == 1 - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def recv(self, compact=True): while self.empty(): @@ -227,6 +235,19 @@ class MiiSink: else: await self.sync.wait() + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + async def _run(self): frame = None self.active = False @@ -234,11 +255,6 @@ class MiiSink: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - self.active = False - continue - if self.enable is None or self.enable.value: d_val = self.data.value.integer dv_val = self.dv.value.integer diff --git a/cocotbext/eth/ptp.py b/cocotbext/eth/ptp.py index f90894d..97dff81 100644 --- a/cocotbext/eth/ptp.py +++ b/cocotbext/eth/ptp.py @@ -30,9 +30,10 @@ import cocotb from cocotb.triggers import RisingEdge from .version import __version__ +from .reset import Reset -class PtpClock: +class PtpClock(Reset): def __init__( self, @@ -87,7 +88,9 @@ class PtpClock: if self.pps is not None: self.pps.setimmediatevalue(0) - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) def set_period(self, ns, fns): self.period_ns = int(ns) @@ -176,27 +179,36 @@ class PtpClock: def get_ts_64_s(self): return self.get_ts_64()*1e-9 + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.ts_96_s = 0 + self.ts_96_ns = 0 + self.ts_96_fns = 0 + self.ts_64_ns = 0 + self.ts_64_fns = 0 + self.drift_cnt = 0 + if self.ts_96 is not None: + self.ts_96 <= 0 + if self.ts_64 is not None: + self.ts_64 <= 0 + if self.ts_step is not None: + self.ts_step <= 0 + if self.pps is not None: + self.pps <= 0 + async def _run(self): while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - self.ts_96_s = 0 - self.ts_96_ns = 0 - self.ts_96_fns = 0 - self.ts_64_ns = 0 - self.ts_64_fns = 0 - self.drift_cnt = 0 - if self.ts_96 is not None: - self.ts_96 <= 0 - if self.ts_64 is not None: - self.ts_64 <= 0 - if self.ts_step is not None: - self.ts_step <= 0 - if self.pps is not None: - self.pps <= 0 - continue - if self.ts_step is not None: self.ts_step <= self.ts_updated self.ts_updated = False diff --git a/cocotbext/eth/reset.py b/cocotbext/eth/reset.py new file mode 100644 index 0000000..34eac68 --- /dev/null +++ b/cocotbext/eth/reset.py @@ -0,0 +1,66 @@ +""" + +Copyright (c) 2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import cocotb +from cocotb.triggers import RisingEdge, FallingEdge + + +class Reset: + def _init_reset(self, reset_signal=None, active_high=True): + self._local_reset = False + self._ext_reset = False + self._reset_state = True + + if reset_signal is not None: + cocotb.fork(self._run_reset(reset_signal, active_high)) + + self._update_reset() + + def assert_reset(self, val=None): + if val is None: + self.assert_reset(True) + self.assert_reset(False) + else: + self._local_reset = val + self._update_reset() + + def _update_reset(self): + new_state = self._local_reset or self._ext_reset + if self._reset_state != new_state: + self._reset_state = new_state + self._handle_reset(new_state) + + def _handle_reset(self, state): + pass + + async def _run_reset(self, reset_signal, active_high): + while True: + if bool(reset_signal.value): + await FallingEdge(reset_signal) + self._ext_reset = not active_high + self._update_reset() + else: + await RisingEdge(reset_signal) + self._ext_reset = active_high + self._update_reset() diff --git a/cocotbext/eth/rgmii.py b/cocotbext/eth/rgmii.py index cd33b92..8c816dd 100644 --- a/cocotbext/eth/rgmii.py +++ b/cocotbext/eth/rgmii.py @@ -32,9 +32,10 @@ from cocotb.utils import get_sim_time, get_sim_steps from .version import __version__ from .gmii import GmiiFrame from .constants import EthPre +from .reset import Reset -class RgmiiSource: +class RgmiiSource(Reset): def __init__(self, data, ctrl, clock, reset=None, enable=None, mii_select=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -64,14 +65,14 @@ class RgmiiSource: self.width = 8 self.byte_width = 1 - self.reset = reset - assert len(self.data) == 4 self.data.setimmediatevalue(0) assert len(self.ctrl) == 1 self.ctrl.setimmediatevalue(0) - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def send(self, frame): self.send_nowait(frame) @@ -95,6 +96,21 @@ class RgmiiSource: while not self.idle(): await RisingEdge(self.clock) + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + self.data <= 0 + self.ctrl <= 0 + async def _run(self): frame = None ifg_cnt = 0 @@ -106,14 +122,6 @@ class RgmiiSource: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - ifg_cnt = 0 - self.active = False - self.data <= 0 - self.ctrl <= 0 - continue - if not self.mii_mode: # send high nibble after rising edge, leading in to falling edge self.data <= d >> 4 @@ -169,7 +177,7 @@ class RgmiiSource: self.ctrl <= en -class RgmiiSink: +class RgmiiSink(Reset): def __init__(self, data, ctrl, clock, reset=None, enable=None, mii_select=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -199,12 +207,12 @@ class RgmiiSink: self.width = 8 self.byte_width = 1 - self.reset = reset - assert len(self.data) == 4 assert len(self.ctrl) == 1 - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def recv(self, compact=True): while self.empty(): @@ -238,6 +246,19 @@ class RgmiiSink: else: await self.sync.wait() + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + async def _run(self): frame = None self.active = False @@ -254,11 +275,6 @@ class RgmiiSink: await FallingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - self.active = False - continue - # capture high nibble on falling edge d_val |= self.data.value.integer << 4 er_val = dv_val ^ self.ctrl.value.integer diff --git a/cocotbext/eth/xgmii.py b/cocotbext/eth/xgmii.py index ec4dd9a..0ee01e8 100644 --- a/cocotbext/eth/xgmii.py +++ b/cocotbext/eth/xgmii.py @@ -33,6 +33,7 @@ from cocotb.utils import get_sim_time from .version import __version__ from .constants import EthPre, ETH_PREAMBLE, XgmiiCtrl +from .reset import Reset class XgmiiFrame: @@ -117,7 +118,7 @@ class XgmiiFrame: return bytes(self.data) -class XgmiiSource: +class XgmiiSource(Reset): def __init__(self, data, ctrl, clock, reset=None, enable=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -147,8 +148,6 @@ class XgmiiSource: self.width = len(self.data) self.byte_width = len(self.ctrl) - self.reset = reset - assert self.width == self.byte_width * 8 self.idle_d = 0 @@ -161,7 +160,9 @@ class XgmiiSource: self.data.setimmediatevalue(0) self.ctrl.setimmediatevalue(0) - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def send(self, frame): self.send_nowait(frame) @@ -185,6 +186,21 @@ class XgmiiSource: while not self.idle(): await RisingEdge(self.clock) + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + self.data <= 0 + self.ctrl <= 0 + async def _run(self): frame = None ifg_cnt = 0 @@ -194,15 +210,6 @@ class XgmiiSource: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - ifg_cnt = 0 - deficit_idle_cnt = 0 - self.active = False - self.data <= 0 - self.ctrl <= 0 - continue - 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): # in IFG @@ -272,7 +279,7 @@ class XgmiiSource: self.active = False -class XgmiiSink: +class XgmiiSink(Reset): def __init__(self, data, ctrl, clock, reset=None, enable=None, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") @@ -299,11 +306,11 @@ class XgmiiSink: self.width = len(self.data) self.byte_width = len(self.ctrl) - self.reset = reset - assert self.width == self.byte_width * 8 - cocotb.fork(self._run()) + self._run_cr = None + + self._init_reset(reset) async def recv(self, compact=True): while self.empty(): @@ -337,6 +344,19 @@ class XgmiiSink: else: await self.sync.wait() + def _handle_reset(self, state): + if state: + self.log.info("Reset asserted") + if self._run_cr is not None: + self._run_cr.kill() + self._run_cr = None + else: + self.log.info("Reset de-asserted") + if self._run_cr is None: + self._run_cr = cocotb.fork(self._run()) + + self.active = False + async def _run(self): frame = None self.active = False @@ -344,11 +364,6 @@ class XgmiiSink: while True: await RisingEdge(self.clock) - if self.reset is not None and self.reset.value: - frame = None - self.active = False - continue - if self.enable is None or self.enable.value: for offset in range(self.byte_width): d_val = (self.data.value.integer >> (offset*8)) & 0xff diff --git a/tests/ptp_clock/test_ptp_clock.py b/tests/ptp_clock/test_ptp_clock.py index 7fbefe2..19bedd0 100644 --- a/tests/ptp_clock/test_ptp_clock.py +++ b/tests/ptp_clock/test_ptp_clock.py @@ -116,7 +116,6 @@ async def run_load_timestamps(dut): tb.ptp_clock.set_ts_96(12345678) tb.ptp_clock.set_ts_64(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)