Merge pull request #59 from lschuermann/dev/mac-hw-timestamp
MAC: implement TX return channel & hardware timestamping
This commit is contained in:
commit
9ac5c592d0
|
@ -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":
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue