From a57c10db306b58a764bbae6bb1fb3c1525fec05d Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 17 Nov 2020 21:30:47 -0800 Subject: [PATCH] Add clock enable and MII select to GMII module and tests --- cocotbext/eth/gmii.py | 39 +++++++++++++++++++++++++++++++++++++-- tests/gmii/test_gmii.py | 39 ++++++++++++++++++++++++++++++++++++--- tests/gmii/test_gmii.v | 4 +++- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/cocotbext/eth/gmii.py b/cocotbext/eth/gmii.py index d424136..62347ac 100644 --- a/cocotbext/eth/gmii.py +++ b/cocotbext/eth/gmii.py @@ -93,12 +93,13 @@ class GmiiSource(object): _signals = ["d"] _optional_signals = ["er", "en", "dv"] - def __init__(self, entity, name, clock, reset=None, enable=None, *args, **kwargs): + def __init__(self, entity, name, clock, reset=None, enable=None, mii_select=None, *args, **kwargs): self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) self.entity = entity self.clock = clock self.reset = reset self.enable = enable + self.mii_select = mii_select self.bus = Bus(self.entity, name, self._signals, optional_signals=self._optional_signals, **kwargs) super().__init__(*args, **kwargs) @@ -183,6 +184,18 @@ class GmiiSource(object): self.queue_occupancy_frames -= 1 self.log.info(f"TX frame: {frame}") frame.normalize() + + if self.mii_select is not None and self.mii_select.value: + mii_data = [] + mii_error = [] + for b, e in zip(frame.data, frame.error): + mii_data.append(b & 0x0F) + mii_data.append(b >> 4) + mii_error.append(e) + mii_error.append(e) + frame.data = mii_data + frame.error = mii_error + self.active = True if frame is not None: @@ -207,12 +220,13 @@ class GmiiSink(object): _signals = ["d"] _optional_signals = ["er", "en", "dv"] - def __init__(self, entity, name, clock, reset=None, enable=None, *args, **kwargs): + def __init__(self, entity, name, clock, reset=None, enable=None, mii_select=None, *args, **kwargs): self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) self.entity = entity self.clock = clock self.reset = reset self.enable = enable + self.mii_select = mii_select self.bus = Bus(self.entity, name, self._signals, optional_signals=self._optional_signals, **kwargs) super().__init__(*args, **kwargs) @@ -293,6 +307,27 @@ class GmiiSink(object): if not dv_val: # end of frame + if self.mii_select is not None and self.mii_select.value: + odd = True + sync = False + b = 0 + be = 0 + data = bytearray() + error = [] + for n, e in zip(frame.data, frame.error): + odd = not odd + b = (n & 0x0F) << 4 | b >> 4 + be |= e + if not sync and b == 0xD5: + odd = True + sync = True + if odd: + data.append(b) + error.append(be) + be = 0 + frame.data = data + frame.error = error + frame.compact() self.log.info(f"RX frame: {frame}") diff --git a/tests/gmii/test_gmii.py b/tests/gmii/test_gmii.py index 10fad75..6a8c23d 100644 --- a/tests/gmii/test_gmii.py +++ b/tests/gmii/test_gmii.py @@ -45,10 +45,16 @@ class TB(object): self.log = SimLog(f"cocotb.tb") self.log.setLevel(logging.DEBUG) + self._enable_generator = None + self._enable_cr = None + cocotb.fork(Clock(dut.clk, 2, units="ns").start()) - self.source = GmiiSource(dut, "gmii", dut.clk, dut.rst) - self.sink = GmiiSink(dut, "gmii", dut.clk, dut.rst) + self.source = GmiiSource(dut, "gmii", dut.clk, dut.rst, dut.gmii_clk_en, dut.gmii_mii_sel) + self.sink = GmiiSink(dut, "gmii", dut.clk, dut.rst, dut.gmii_clk_en, dut.gmii_mii_sel) + + dut.gmii_clk_en.setimmediatevalue(1) + dut.gmii_mii_sel.setimmediatevalue(0) async def reset(self): self.dut.rst.setimmediatevalue(0) @@ -61,13 +67,35 @@ class TB(object): await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk) -async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12): + def set_enable_generator(self, generator=None): + if self._enable_cr is not None: + self._enable_cr.kill() + self._enable_cr = None + + self._enable_generator = generator + + if self._enable_generator is not None: + self._enable_cr = cocotb.fork(self._run_enable()) + + def clear_enable_generator(self): + self.set_enable_generator(None) + + async def _run_enable(self): + for val in self._enable_generator: + self.dut.gmii_clk_en <= val + await RisingEdge(self.dut.clk) + +async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False): tb = TB(dut) byte_width = tb.source.width // 8 tb.source.ifg = ifg + tb.dut.gmii_mii_sel <= mii_sel + + if enable_gen is not None: + tb.set_enable_generator(enable_gen()) await tb.reset() @@ -95,12 +123,17 @@ def size_list(): def incrementing_payload(length): return bytearray(itertools.islice(itertools.cycle(range(256)), length)) +def cycle_en(): + return itertools.cycle([0, 0, 0, 1]) + if cocotb.SIM_NAME: factory = TestFactory(run_test) factory.add_option("payload_lengths", [size_list]) factory.add_option("payload_data", [incrementing_payload]) factory.add_option("ifg", [12, 0]) + factory.add_option("enable_gen", [None, cycle_en]) + factory.add_option("mii_sel", [False, True]) factory.generate_tests() diff --git a/tests/gmii/test_gmii.v b/tests/gmii/test_gmii.v index 431a97e..9dbfb81 100644 --- a/tests/gmii/test_gmii.v +++ b/tests/gmii/test_gmii.v @@ -39,7 +39,9 @@ module test_gmii # inout wire [DATA_WIDTH-1:0] gmii_d, inout wire gmii_er, - inout wire gmii_en + inout wire gmii_en, + inout wire gmii_clk_en, + inout wire gmii_mii_sel ); endmodule