From 7ce1085b68572be2dc0c43f93dbf54fed27e623a Mon Sep 17 00:00:00 2001 From: Leon Schuermann Date: Wed, 27 Jan 2021 17:43:23 +0100 Subject: [PATCH] liteeth MAC: implement RX hardware packet timestamping This implements optional packet timestamping based on a hardware timestamp source for incoming Ethernet packets, as required by applications such as IEEE 1588 (Precision Time Protocol). When a timestamp source is given as an argument, an additonal CSR is generated containing the packet timestamp. --- liteeth/mac/__init__.py | 11 +++++++++-- liteeth/mac/sram.py | 41 ++++++++++++++++++++++++++++++++++------- liteeth/mac/wishbone.py | 4 ++-- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/liteeth/mac/__init__.py b/liteeth/mac/__init__.py index 55356c9..9e44fb0 100644 --- a/liteeth/mac/__init__.py +++ b/liteeth/mac/__init__.py @@ -18,7 +18,8 @@ class LiteEthMAC(Module, AutoCSR): with_preamble_crc = True, nrxslots = 2, ntxslots = 2, - hw_mac = None): + hw_mac = None, + timestamp_source = None): assert interface in ["crossbar", "wishbone", "hybrid"] self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc) self.csrs = [] @@ -37,7 +38,13 @@ class LiteEthMAC(Module, AutoCSR): self.rx_slots = CSRConstant(nrxslots) self.tx_slots = CSRConstant(ntxslots) self.slot_size = CSRConstant(2**bits_for(eth_mtu)) - self.submodules.interface = FullMemoryWE()(LiteEthMACWishboneInterface(32, nrxslots, ntxslots, endianness)) + self.submodules.interface = FullMemoryWE()(LiteEthMACWishboneInterface( + dw = 32, + nrxslots = nrxslots, + ntxslots = ntxslots, + endianness = endianness, + timestamp_source = timestamp_source, + )) self.ev, self.bus = self.interface.sram.ev, self.interface.bus self.csrs = self.interface.get_csrs() + self.core.get_csrs() if interface == "hybrid": diff --git a/liteeth/mac/sram.py b/liteeth/mac/sram.py index ac075b8..e4030e2 100644 --- a/liteeth/mac/sram.py +++ b/liteeth/mac/sram.py @@ -14,16 +14,22 @@ from litex.soc.interconnect.csr_eventmanager import * # MAC SRAM Writer ---------------------------------------------------------------------------------- class LiteEthMACSRAMWriter(Module, AutoCSR): - def __init__(self, dw, depth, nslots=2, endianness="big"): + def __init__(self, dw, depth, nslots=2, endianness="big", timestamp_source=None): self.sink = sink = stream.Endpoint(eth_phy_description(dw)) self.crc_error = Signal() slotbits = max(log2_int(nslots), 1) lengthbits = 32 + timestampbits = 0 if timestamp_source is None else value_bits_sign(timestamp_source)[0] self._slot = CSRStatus(slotbits) self._length = CSRStatus(lengthbits) + # If a timestamp source is passed in, timestamp all incoming + # packets and provide the value in a CSR + if timestamp_source is not None: + self._rx_timestamp = CSRStatus(timestampbits) + self.errors = CSRStatus(32) self.submodules.ev = EventManager() @@ -62,17 +68,29 @@ class LiteEthMACSRAMWriter(Module, AutoCSR): ongoing = Signal() # Status FIFO - fifo = stream.SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) + fifo_layout = [("slot", slotbits), ("length", lengthbits)] + if timestamp_source is not None: + fifo_layout += [("rx_timestamp", timestampbits)] + + fifo = stream.SyncFIFO(fifo_layout, nslots) self.submodules += fifo + # If we timestamp incoming packets we're going to need another + # signal to hold the timestamp from the start of packet + # reception + if timestamp_source is not None: + rx_start_timestamp = Signal(timestampbits) + # FSM self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", If(sink.valid, If(fifo.sink.ready, - ongoing.eq(1), - NextValue(counter, counter + inc), - NextState("WRITE") + ongoing.eq(1), + *([] if timestamp_source is None else + [NextValue(rx_start_timestamp, timestamp_source)]), + NextValue(counter, counter + inc), + NextState("WRITE") ).Else( NextValue(self.errors.status, self.errors.status + 1), NextState("DISCARD_REMAINING") @@ -109,12 +127,14 @@ class LiteEthMACSRAMWriter(Module, AutoCSR): fifo.sink.slot.eq(slot), fifo.sink.length.eq(counter) ] + fsm.act("TERMINATE", NextValue(counter, 0), slot_ce.eq(1), fifo.sink.valid.eq(1), NextState("IDLE") ) + self.comb += [ fifo.source.ready.eq(self.ev.available.clear), self.ev.available.trigger.eq(fifo.source.valid), @@ -122,6 +142,13 @@ class LiteEthMACSRAMWriter(Module, AutoCSR): self._length.status.eq(fifo.source.length), ] + # RX timestamping to FIFO + if timestamp_source is not None: + self.comb += [ + fifo.sink.rx_timestamp.eq(rx_start_timestamp), + self._rx_timestamp.status.eq(fifo.source.rx_timestamp), + ] + # Memory mems = [None]*nslots ports = [None]*nslots @@ -242,8 +269,8 @@ class LiteEthMACSRAMReader(Module, AutoCSR): # MAC SRAM ----------------------------------------------------------------------------------------- class LiteEthMACSRAM(Module, AutoCSR): - def __init__(self, dw, depth, nrxslots, ntxslots, endianness): - self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots, endianness) + def __init__(self, dw, depth, nrxslots, ntxslots, endianness, timestamp_source=None): + self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots, endianness, timestamp_source) self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots, endianness) self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) self.sink, self.source = self.writer.sink, self.reader.source diff --git a/liteeth/mac/wishbone.py b/liteeth/mac/wishbone.py index f112767..b044980 100644 --- a/liteeth/mac/wishbone.py +++ b/liteeth/mac/wishbone.py @@ -13,7 +13,7 @@ from litex.soc.interconnect import wishbone # MAC Wishbone Interface --------------------------------------------------------------------------- class LiteEthMACWishboneInterface(Module, AutoCSR): - def __init__(self, dw, nrxslots=2, ntxslots=2, endianness="big"): + def __init__(self, dw, nrxslots=2, ntxslots=2, endianness="big", timestamp_source=None): self.sink = stream.Endpoint(eth_phy_description(dw)) self.source = stream.Endpoint(eth_phy_description(dw)) self.bus = wishbone.Interface() @@ -22,7 +22,7 @@ class LiteEthMACWishboneInterface(Module, AutoCSR): # storage in SRAM sram_depth = eth_mtu//(dw//8) - self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots, endianness) + self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots, endianness, timestamp_source) self.comb += [ self.sink.connect(self.sram.sink), self.sram.source.connect(self.source)