diff --git a/liteeth/mac/__init__.py b/liteeth/mac/__init__.py index b660e79..c78e1b3 100644 --- a/liteeth/mac/__init__.py +++ b/liteeth/mac/__init__.py @@ -8,6 +8,7 @@ from liteeth.common import * from liteeth.mac.common import * from liteeth.mac.core import LiteEthMACCore from liteeth.mac.wishbone import LiteEthMACWishboneInterface +from liteeth.mac.dma import LiteEthMACDMAInterface # MAC ---------------------------------------------------------------------------------------------- @@ -79,6 +80,56 @@ class LiteEthMAC(Module, AutoCSR): def get_csrs(self): return self.csrs + +# MAC with DMA ------------------------------------------------------------------------------------- + +class LiteEthMACDMA(Module, AutoCSR): + def __init__(self, phy, + dma_write_port, + dma_read_port, + dma_offset, + with_preamble_crc = True, + nrxslots = 2, + ntxslots = 2, + with_sys_datapath = False): + + dw = phy.dw + + self.with_dma = CSRConstant(True) + self.rx_slots = CSRConstant(nrxslots) + self.tx_slots = CSRConstant(ntxslots) + self.slot_size = CSRConstant(2**bits_for(eth_mtu)) + self.dma_offset = CSRConstant(dma_offset) + + self.csrs = [] + + self.submodules.core = LiteEthMACCore( + phy = phy, + dw = dw, + with_sys_datapath = with_sys_datapath, + with_preamble_crc = with_preamble_crc + ) + + self.submodules.dma = LiteEthMACDMAInterface( + dw = dw, + write_port = dma_write_port, + read_port = dma_read_port, + offset = dma_offset, + nrxslots = nrxslots, + ntxslots = ntxslots, + slot_size = self.slot_size.constant + ) + + self.ev = self.dma.ev + self.csrs = self.core.get_csrs() + self.dma.get_csrs() + + self.comb += self.core.source.connect(self.dma.sink) + self.comb += self.dma.source.connect(self.core.sink) + + def get_csrs(self): + return self.csrs + + # MAC Core Crossbar -------------------------------------------------------------------------------- class LiteEthMACCoreCrossbar(Module): diff --git a/liteeth/mac/dma/__init__.py b/liteeth/mac/dma/__init__.py new file mode 100644 index 0000000..9175968 --- /dev/null +++ b/liteeth/mac/dma/__init__.py @@ -0,0 +1,18 @@ +from migen import * + +from litex.soc.interconnect.csr import * +from litex.soc.interconnect.csr_eventmanager import * + +from .writer import LiteEthMACDMAWriter +from .reader import LiteEthMACDMAReader + + +class LiteEthMACDMAInterface(Module, AutoCSR): + def __init__(self, dw, write_port, read_port, offset, nrxslots, ntxslots, slot_size): + write_offset = offset + read_offset = offset + slot_size * nrxslots + + self.submodules.sram_writer = LiteEthMACDMAWriter(dw, nrxslots, slot_size, write_port, write_offset) + self.submodules.sram_reader = LiteEthMACDMAReader(dw, ntxslots, slot_size, read_port, read_offset) + self.submodules.ev = SharedIRQ(self.sram_writer.ev, self.sram_reader.ev) + self.sink, self.source = self.sram_writer.sink, self.sram_reader.source diff --git a/liteeth/mac/dma/reader.py b/liteeth/mac/dma/reader.py new file mode 100644 index 0000000..f60fd95 --- /dev/null +++ b/liteeth/mac/dma/reader.py @@ -0,0 +1,129 @@ +from migen import * + +from litex.soc.interconnect.csr import * +from litex.soc.interconnect.csr_eventmanager import * + +from liteeth.common import * + +from litedram.frontend.dma import LiteDRAMDMAReader + + +class LiteEthMACDMAReader(Module, AutoCSR): + def __init__(self, dw, nslots, depth, port, offset): + self.source = source = stream.Endpoint(eth_phy_description(dw)) + + dma_dw = port.data_width + length_bits = bits_for(depth - 1) + slot_bits = bits_for(nslots - 1) + + self._start = CSR() + self._ready = CSRStatus() + self._slot = CSRStorage(slot_bits) + self._length = CSRStorage(length_bits) + + self.submodules.ev = EventManager() + self.ev.done = EventSourcePulse() + self.ev.finalize() + + # # # + + # Command FIFO. + _cmd_fifo_layout = [("slot", slot_bits), ("length", length_bits)] + cmd_fifo = stream.SyncFIFO(_cmd_fifo_layout, nslots) + self.submodules += cmd_fifo + self.comb += [ + cmd_fifo.sink.valid.eq(self._start.re & self._start.r), + cmd_fifo.sink.slot.eq(self._slot.storage), + cmd_fifo.sink.length.eq(self._length.storage), + self._ready.status.eq(cmd_fifo.sink.ready), + ] + + length = cmd_fifo.source.length + count = Signal(length_bits, reset_less=True) + + # Encode Length to last_be. + length_lsb = length[:log2_int(dw//8)] if (dw != 8) else 0 + self.comb += If(source.last, + Case(length_lsb, { + 1 : source.last_be.eq(0b00000001), + 2 : source.last_be.eq(0b00000010), + 3 : source.last_be.eq(0b00000100), + 4 : source.last_be.eq(0b00001000), + 5 : source.last_be.eq(0b00010000), + 6 : source.last_be.eq(0b00100000), + 7 : source.last_be.eq(0b01000000), + "default" : source.last_be.eq(2**(dw//8 - 1)), + }) + ) + + # DMA. + start = Signal() + last = Signal() + + self.submodules.dma = dma = LiteDRAMDMAReader(port) + + # Converter. + conv = stream.StrideConverter( + description_from=[("data", dma_dw)], + description_to=[("data", dw)]) + conv = ResetInserter()(conv) + self.submodules += conv + + self.comb += conv.source.connect(source, omit={"last", "last_be"}), + + # FSM. + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + conv.reset.eq(1), + If(cmd_fifo.source.valid, + start.eq(1), + NextValue(count, 0), + NextState("READ") + ) + ) + fsm.act("READ", + dma.source.connect(conv.sink), + source.last.eq(count >= length - 1), + If(source.valid & source.ready, + NextValue(count, count + (dw//8)), + If(source.last, + last.eq(1), + NextState("TERMINATE") + ) + ) + ) + fsm.act("TERMINATE", + dma.source.ready.eq(1), + If(dma.rsv_level == 0, + self.ev.done.trigger.eq(1), + cmd_fifo.source.ready.eq(1), + NextState("IDLE") + ) + ) + + # DMA address. + rd_addr_offset = C(offset // (dma_dw//8)) + rd_addr = Signal(bits_for(depth // (dma_dw//8) - 1), reset_less=True) + + rd_slot = cmd_fifo.source.slot + rd_slot_offset = Signal.like(dma.sink.address) + self.comb += rd_slot_offset.eq(rd_slot * (depth // (dma_dw//8))) + + self.comb += dma.sink.address.eq(rd_addr_offset + rd_slot_offset + rd_addr) + + self.submodules.dma_fsm = dma_fsm = FSM(reset_state="IDLE") + dma_fsm.act("IDLE", + If(start, + NextValue(rd_addr, 0), + NextState("READ") + ) + ) + dma_fsm.act("READ", + dma.sink.valid.eq(1), + If(dma.sink.valid & dma.sink.ready, + NextValue(rd_addr, rd_addr + 1) + ), + If(last, + NextState("IDLE") + ) + ) diff --git a/liteeth/mac/dma/writer.py b/liteeth/mac/dma/writer.py new file mode 100644 index 0000000..c7beeaf --- /dev/null +++ b/liteeth/mac/dma/writer.py @@ -0,0 +1,140 @@ +from migen import * + +from litex.soc.interconnect.csr import * +from litex.soc.interconnect.csr_eventmanager import * + +from liteeth.common import * + +from litedram.frontend.dma import LiteDRAMDMAWriter + + +class LiteEthMACDMAWriter(Module, AutoCSR): + def __init__(self, dw, nslots, depth, port, offset): + self.sink = sink = stream.Endpoint(eth_phy_description(dw)) + + dma_dw = port.data_width + length_bits = bits_for(depth - 1) + slot_bits = bits_for(nslots - 1) + + self._slot = CSRStatus(slot_bits) + self._length = CSRStatus(length_bits) + self._errors = CSRStatus(32) + + self.submodules.ev = EventManager() + self.ev.available = EventSourceLevel() + self.ev.finalize() + + # # # + + errors = self._errors.status + + slot = Signal(slot_bits) + length = Signal(length_bits, reset_less=True) + length_inc = Signal(bits_for(dw//8)) + + # Decode Length increment from from last_be. + self.comb += Case(sink.last_be, { + 0b00000001 : length_inc.eq(1), + 0b00000010 : length_inc.eq(2), + 0b00000100 : length_inc.eq(3), + 0b00001000 : length_inc.eq(4), + 0b00010000 : length_inc.eq(5), + 0b00100000 : length_inc.eq(6), + 0b01000000 : length_inc.eq(7), + "default" : length_inc.eq(dw//8) + }) + + # Status FIFO. + _stat_fifo_layout = [("slot", slot_bits), ("length", length_bits)] + stat_fifo = stream.SyncFIFO(_stat_fifo_layout, nslots) + self.submodules += stat_fifo + + # Converter. + conv = stream.StrideConverter( + description_from=[("data", dw)], + description_to=[("data", dma_dw)]) + conv = ResetInserter()(conv) + self.submodules += conv + + # DMA. + start = Signal() + done = Signal() + + self.submodules.dma = dma = LiteDRAMDMAWriter(port) + + self.comb += conv.source.connect(dma.sink, omit={"address"}), + + # FSM. + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + conv.reset.eq(1), + If(sink.valid, + If(stat_fifo.sink.ready, + start.eq(1), + NextValue(length, 0), + NextState("WRITE") + ).Else( + NextValue(errors, errors + 1), + NextState("DISCARD-REMAINING") + ) + ) + ) + fsm.act("WRITE", + sink.connect(conv.sink, omit={"last_be", "error"}), + If(sink.valid & sink.ready, + NextValue(length, length + length_inc), + If(sink.last, + NextState("TERMINATE") + ).Elif(length >= eth_mtu, + NextState("DISCARD-REMAINING") + ) + ) + ) + fsm.act("DISCARD-REMAINING", + sink.ready.eq(1), + If(sink.valid & sink.last, + NextState("IDLE") + ) + ) + fsm.act("TERMINATE", + If(done, + stat_fifo.sink.valid.eq(1), + NextValue(slot, slot + 1), + NextState("IDLE") + ) + ) + + self.comb += [ + stat_fifo.sink.slot.eq(slot), + stat_fifo.sink.length.eq(length), + stat_fifo.source.ready.eq(self.ev.available.clear), + self.ev.available.trigger.eq(stat_fifo.source.valid), + self._slot.status.eq(stat_fifo.source.slot), + self._length.status.eq(stat_fifo.source.length), + ] + + # DMA address. + wr_addr_offset = C(offset // (dma_dw//8)) + wr_addr = Signal(bits_for(depth // (dma_dw//8) - 1), reset_less=True) + + wr_slot_offset = Signal.like(dma.sink.address) + self.comb += wr_slot_offset.eq(slot * (depth // (dma_dw//8))) + + self.comb += dma.sink.address.eq(wr_addr_offset + wr_slot_offset + wr_addr) + + self.submodules.dma_fsm = dma_fsm = FSM(reset_state="IDLE") + dma_fsm.act("IDLE", + done.eq(1), + If(start, + NextValue(wr_addr, 0), + NextState("WRITE") + ) + ) + dma_fsm.act("WRITE", + If(dma.sink.valid & dma.sink.ready, + NextValue(wr_addr, wr_addr + 1), + If(dma.sink.last, + NextState("IDLE") + ) + ) + )