Merge pull request #59 from lschuermann/dev/mac-hw-timestamp

MAC: implement TX return channel & hardware timestamping
This commit is contained in:
enjoy-digital 2021-04-08 13:04:03 +02:00 committed by GitHub
commit 9ac5c592d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 27 deletions

View File

@ -18,7 +18,8 @@ class LiteEthMAC(Module, AutoCSR):
with_preamble_crc = True, with_preamble_crc = True,
nrxslots = 2, nrxslots = 2,
ntxslots = 2, ntxslots = 2,
hw_mac = None): hw_mac = None,
timestamp_source = None):
assert interface in ["crossbar", "wishbone", "hybrid"] assert interface in ["crossbar", "wishbone", "hybrid"]
self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc) self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc)
self.csrs = [] self.csrs = []
@ -37,7 +38,13 @@ class LiteEthMAC(Module, AutoCSR):
self.rx_slots = CSRConstant(nrxslots) self.rx_slots = CSRConstant(nrxslots)
self.tx_slots = CSRConstant(ntxslots) self.tx_slots = CSRConstant(ntxslots)
self.slot_size = CSRConstant(2**bits_for(eth_mtu)) 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.ev, self.bus = self.interface.sram.ev, self.interface.bus
self.csrs = self.interface.get_csrs() + self.core.get_csrs() self.csrs = self.interface.get_csrs() + self.core.get_csrs()
if interface == "hybrid": if interface == "hybrid":

View File

@ -14,16 +14,22 @@ from litex.soc.interconnect.csr_eventmanager import *
# MAC SRAM Writer ---------------------------------------------------------------------------------- # MAC SRAM Writer ----------------------------------------------------------------------------------
class LiteEthMACSRAMWriter(Module, AutoCSR): 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.sink = sink = stream.Endpoint(eth_phy_description(dw))
self.crc_error = Signal() self.crc_error = Signal()
slotbits = max(log2_int(nslots), 1) slotbits = max(log2_int(nslots), 1)
lengthbits = 32 lengthbits = 32
timestampbits = 0 if timestamp_source is None else value_bits_sign(timestamp_source)[0]
self._slot = CSRStatus(slotbits) self._slot = CSRStatus(slotbits)
self._length = CSRStatus(lengthbits) 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.errors = CSRStatus(32)
self.submodules.ev = EventManager() self.submodules.ev = EventManager()
@ -62,15 +68,27 @@ class LiteEthMACSRAMWriter(Module, AutoCSR):
ongoing = Signal() ongoing = Signal()
# Status FIFO # 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 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 # FSM
self.submodules.fsm = fsm = FSM(reset_state="IDLE") self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE", fsm.act("IDLE",
If(sink.valid, If(sink.valid,
If(fifo.sink.ready, If(fifo.sink.ready,
ongoing.eq(1), ongoing.eq(1),
*([] if timestamp_source is None else
[NextValue(rx_start_timestamp, timestamp_source)]),
NextValue(counter, counter + inc), NextValue(counter, counter + inc),
NextState("WRITE") NextState("WRITE")
).Else( ).Else(
@ -109,12 +127,14 @@ class LiteEthMACSRAMWriter(Module, AutoCSR):
fifo.sink.slot.eq(slot), fifo.sink.slot.eq(slot),
fifo.sink.length.eq(counter) fifo.sink.length.eq(counter)
] ]
fsm.act("TERMINATE", fsm.act("TERMINATE",
NextValue(counter, 0), NextValue(counter, 0),
slot_ce.eq(1), slot_ce.eq(1),
fifo.sink.valid.eq(1), fifo.sink.valid.eq(1),
NextState("IDLE") NextState("IDLE")
) )
self.comb += [ self.comb += [
fifo.source.ready.eq(self.ev.available.clear), fifo.source.ready.eq(self.ev.available.clear),
self.ev.available.trigger.eq(fifo.source.valid), self.ev.available.trigger.eq(fifo.source.valid),
@ -122,6 +142,13 @@ class LiteEthMACSRAMWriter(Module, AutoCSR):
self._length.status.eq(fifo.source.length), 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 # Memory
mems = [None]*nslots mems = [None]*nslots
ports = [None]*nslots ports = [None]*nslots
@ -145,50 +172,83 @@ class LiteEthMACSRAMWriter(Module, AutoCSR):
# MAC SRAM Reader ---------------------------------------------------------------------------------- # MAC SRAM Reader ----------------------------------------------------------------------------------
class LiteEthMACSRAMReader(Module, AutoCSR): class LiteEthMACSRAMReader(Module, AutoCSR):
def __init__(self, dw, depth, nslots=2, endianness="big"): def __init__(self, dw, depth, nslots=2, endianness="big", timestamp_source=None):
self.source = source = stream.Endpoint(eth_phy_description(dw)) self.source = source = stream.Endpoint(eth_phy_description(dw))
slotbits = max(log2_int(nslots), 1) slotbits = max(log2_int(nslots), 1)
lengthbits = bits_for(depth*4) # length in bytes lengthbits = bits_for(depth*4) # length in bytes
self.lengthbits = lengthbits self.lengthbits = lengthbits
# TX packet timestamping, for applications such as IEEE 1588
# Precision Time Protocol
timestampbits = 0 if timestamp_source is None else value_bits_sign(timestamp_source)[0]
# MAC Reader command channel
self._start = CSR() self._start = CSR()
self._ready = CSRStatus() self._ready = CSRStatus()
self._level = CSRStatus(log2_int(nslots) + 1) self._level = CSRStatus(log2_int(nslots) + 1)
self._slot = CSRStorage(slotbits, reset_less=True) self._slot = CSRStorage(slotbits, reset_less=True)
self._length = CSRStorage(lengthbits, reset_less=True) self._length = CSRStorage(lengthbits, reset_less=True)
# MAC Reader return channel
self._res_slot = CSRStatus(slotbits)
if timestamp_source is not None:
self._res_tx_timestamp = CSRStatus(timestampbits)
self.submodules.ev = EventManager() self.submodules.ev = EventManager()
self.ev.done = EventSourcePulse() self.ev.done = EventSourceLevel()
self.ev.finalize() self.ev.finalize()
# # # # # #
# Command FIFO # Command FIFO
fifo = stream.SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) cmd_fifo = stream.SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots)
self.submodules += fifo self.submodules += cmd_fifo
self.comb += [ self.comb += [
fifo.sink.valid.eq(self._start.re), cmd_fifo.sink.valid.eq(self._start.re),
fifo.sink.slot.eq(self._slot.storage), cmd_fifo.sink.slot.eq(self._slot.storage),
fifo.sink.length.eq(self._length.storage), cmd_fifo.sink.length.eq(self._length.storage),
self._ready.status.eq(fifo.sink.ready), self._ready.status.eq(cmd_fifo.sink.ready),
self._level.status.eq(fifo.level) self._level.status.eq(cmd_fifo.level)
] ]
# Result FIFO (return channel)
res_fifo_layout = [("slot", slotbits)]
if timestamp_source is not None:
res_fifo_layout += [("tx_timestamp", timestampbits)]
res_fifo = stream.SyncFIFO(res_fifo_layout, nslots)
self.submodules += res_fifo
self.comb += [
res_fifo.source.ready.eq(self.ev.done.clear),
self.ev.done.trigger.eq(res_fifo.source.valid),
self._res_slot.status.eq(res_fifo.source.slot),
]
if timestamp_source is not None:
self.comb += self._res_tx_timestamp.status.eq(res_fifo.source.tx_timestamp),
# Length computation # Length computation
read_address = Signal(lengthbits) read_address = Signal(lengthbits)
counter = Signal(lengthbits) counter = Signal(lengthbits)
# Store the timestamp of transmission start
if timestamp_source is not None:
tx_start_timestamp = Signal(timestampbits)
# FSM # FSM
self.submodules.fsm = fsm = FSM(reset_state="IDLE") self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE", fsm.act("IDLE",
NextValue(counter, 0), NextValue(counter, 0),
If(fifo.source.valid, If(cmd_fifo.source.valid,
read_address.eq(0), read_address.eq(0),
*([] if timestamp_source is None
else [NextValue(tx_start_timestamp, timestamp_source)]),
NextState("SEND") NextState("SEND")
) )
) )
length_lsb = fifo.source.length[0:2]
length_lsb = cmd_fifo.source.length[0:2]
if endianness == "big": if endianness == "big":
self.comb += If(source.last, self.comb += If(source.last,
Case(length_lsb, { Case(length_lsb, {
@ -207,7 +267,7 @@ class LiteEthMACSRAMReader(Module, AutoCSR):
})) }))
fsm.act("SEND", fsm.act("SEND",
source.valid.eq(1), source.valid.eq(1),
source.last.eq(counter >= (fifo.source.length - 4)), source.last.eq(counter >= (cmd_fifo.source.length - 4)),
read_address.eq(counter), read_address.eq(counter),
If(source.ready, If(source.ready,
read_address.eq(counter + 4), read_address.eq(counter + 4),
@ -218,13 +278,17 @@ class LiteEthMACSRAMReader(Module, AutoCSR):
) )
) )
fsm.act("END", fsm.act("END",
fifo.source.ready.eq(1), res_fifo.sink.valid.eq(1),
self.ev.done.trigger.eq(1), cmd_fifo.source.ready.eq(1),
NextState("IDLE") NextState("IDLE")
) )
self.comb += res_fifo.sink.slot.eq(cmd_fifo.source.slot)
if timestamp_source is not None:
self.comb += res_fifo.sink.tx_timestamp.eq(tx_start_timestamp)
# Memory # Memory
rd_slot = fifo.source.slot rd_slot = cmd_fifo.source.slot
mems = [None]*nslots mems = [None]*nslots
ports = [None]*nslots ports = [None]*nslots
for n in range(nslots): for n in range(nslots):
@ -242,8 +306,8 @@ class LiteEthMACSRAMReader(Module, AutoCSR):
# MAC SRAM ----------------------------------------------------------------------------------------- # MAC SRAM -----------------------------------------------------------------------------------------
class LiteEthMACSRAM(Module, AutoCSR): class LiteEthMACSRAM(Module, AutoCSR):
def __init__(self, dw, depth, nrxslots, ntxslots, endianness): def __init__(self, dw, depth, nrxslots, ntxslots, endianness, timestamp_source=None):
self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots, endianness) self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots, endianness, timestamp_source)
self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots, endianness) self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots, endianness, timestamp_source)
self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev)
self.sink, self.source = self.writer.sink, self.reader.source self.sink, self.source = self.writer.sink, self.reader.source

View File

@ -13,7 +13,7 @@ from litex.soc.interconnect import wishbone
# MAC Wishbone Interface --------------------------------------------------------------------------- # MAC Wishbone Interface ---------------------------------------------------------------------------
class LiteEthMACWishboneInterface(Module, AutoCSR): 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.sink = stream.Endpoint(eth_phy_description(dw))
self.source = stream.Endpoint(eth_phy_description(dw)) self.source = stream.Endpoint(eth_phy_description(dw))
self.bus = wishbone.Interface() self.bus = wishbone.Interface()
@ -22,7 +22,7 @@ class LiteEthMACWishboneInterface(Module, AutoCSR):
# storage in SRAM # storage in SRAM
sram_depth = eth_mtu//(dw//8) 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.comb += [
self.sink.connect(self.sram.sink), self.sink.connect(self.sram.sink),
self.sram.source.connect(self.source) self.sram.source.connect(self.source)