soc/cores: add simple DMA with WishboneDMAReader/WishboneDMAWriter.
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 24 Jun 2020 08:17:40 +0000 (10:17 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 24 Jun 2020 10:22:44 +0000 (12:22 +0200)
litex/soc/cores/dma.py [new file with mode: 0644]

diff --git a/litex/soc/cores/dma.py b/litex/soc/cores/dma.py
new file mode 100644 (file)
index 0000000..b9902a9
--- /dev/null
@@ -0,0 +1,201 @@
+# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+"""Direct Memory Access (DMA) reader and writer modules."""
+
+from migen import *
+
+from litex.gen.common import reverse_bytes
+
+from litex.soc.interconnect.csr import *
+from litex.soc.interconnect import stream
+from litex.soc.interconnect import wishbone
+
+# Helpers ------------------------------------------------------------------------------------------
+
+def format_bytes(s, endianness):
+    return {"big": s, "little": reverse_bytes(s)}[endianness]
+
+# WishboneDMAReader --------------------------------------------------------------------------------
+
+class WishboneDMAReader(Module, AutoCSR):
+    """Read data from Wishbone MMAP memory.
+
+    For every address written to the sink, one word will be produced on the source.
+
+    Parameters
+    ----------
+    bus : bus
+        Wishbone bus of the SoC to read from.
+
+    Attributes
+    ----------
+    sink : Record("address")
+        Sink for MMAP addresses to be read.
+
+    source : Record("data")
+        Source for MMAP word results from reading.
+    """
+    def __init__(self, bus, endianness="little", with_csr=False):
+        assert isinstance(bus, wishbone.Interface)
+        self.bus    = bus
+        self.sink   = sink   = stream.Endpoint([("address", bus.adr_width)])
+        self.source = source = stream.Endpoint([("data",    bus.data_width)])
+
+        # # #
+
+        data = Signal(bus.data_width)
+
+        self.submodules.fsm = fsm = FSM(reset_state="BUS-READ")
+        fsm.act("BUS-READ",
+            bus.stb.eq(sink.valid),
+            bus.cyc.eq(sink.valid),
+            bus.we.eq(0),
+            bus.sel.eq(2**(bus.data_width//8)-1),
+            bus.adr.eq(sink.address),
+            If(bus.stb & bus.ack,
+                NextValue(data, format_bytes(bus.dat_r, endianness)),
+                NextState("SOURCE-WRITE")
+            )
+        )
+        fsm.act("SOURCE-WRITE",
+            source.valid.eq(1),
+            source.data.eq(data),
+            If(source.ready,
+                sink.ready.eq(1),
+                NextState("BUS-READ")
+            )
+        )
+
+        if with_csr:
+            self.add_csr()
+
+    def add_csr(self):
+        self._base   = CSRStorage(32)
+        self._length = CSRStorage(32)
+        self._enable = CSRStorage()
+        self._done   = CSRStatus()
+        self._loop   = CSRStorage()
+        self._offset = CSRStatus(32)
+
+        # # #
+
+        shift   = log2_int(self.bus.data_width//8)
+        base    = Signal(self.bus.adr_width)
+        offset  = Signal(self.bus.adr_width)
+        length  = Signal(self.bus.adr_width)
+        self.comb += base.eq(self._base.storage[shift:])
+        self.comb += length.eq(self._length.storage[shift:])
+
+        self.comb += self._offset.status.eq(offset)
+
+        fsm = FSM(reset_state="IDLE")
+        fsm = ResetInserter()(fsm)
+        self.submodules += fsm
+        self.comb += fsm.reset.eq(~self._enable.storage)
+        fsm.act("IDLE",
+            NextValue(offset, 0),
+            NextState("RUN"),
+        )
+        fsm.act("RUN",
+            self.sink.valid.eq(1),
+            self.sink.address.eq(base + offset),
+            If(self.sink.ready,
+                NextValue(offset, offset + 1),
+                If(offset == (length - 1),
+                    If(self._loop.storage,
+                        NextValue(offset, 0)
+                    ).Else(
+                        NextState("DONE")
+                    )
+                )
+            )
+        )
+        fsm.act("DONE",
+            self._done.status.eq(1)
+        )
+
+# WishboneDMAWriter --------------------------------------------------------------------------------
+
+class WishboneDMAWriter(Module, AutoCSR):
+    """Write data to Wishbone MMAP memory.
+
+    Parameters
+    ----------
+    bus : bus
+        Wishbone bus of the SoC to read from.
+
+    Attributes
+    ----------
+    sink : Record("address", "data")
+        Sink for MMAP addresses/datas to be written.
+    """
+    def __init__(self, bus, endianness="little", with_csr=False):
+        assert isinstance(bus, wishbone.Interface)
+        self.bus  = bus
+        self.sink = sink = stream.Endpoint([("address", bus.adr_width), ("data", bus.data_width)])
+
+        # # #
+
+        data = Signal(bus.data_width)
+
+        self.comb += [
+            bus.stb.eq(sink.valid),
+            bus.cyc.eq(sink.valid),
+            bus.we.eq(1),
+            bus.sel.eq(2**(bus.data_width//8)-1),
+            bus.adr.eq(sink.address),
+            bus.dat_w.eq(format_bytes(sink.data, endianness)),
+            sink.ready.eq(bus.ack),
+        ]
+
+        if with_csr:
+            self.add_csr()
+
+    def add_csr(self):
+        self._sink = self.sink
+        self.sink  = stream.Endpoint([("data", self.bus.data_width)])
+
+        self._base   = CSRStorage(32)
+        self._length = CSRStorage(32)
+        self._enable = CSRStorage()
+        self._done   = CSRStatus()
+        self._loop   = CSRStorage()
+
+        # # #
+
+        shift   = log2_int(self.bus.data_width//8)
+        base    = Signal(self.bus.adr_width)
+        offset  = Signal(self.bus.adr_width)
+        length  = Signal(self.bus.adr_width)
+        self.comb += base.eq(self._base.storage[shift:])
+        self.comb += length.eq(self._length.storage[shift:])
+
+        fsm = FSM(reset_state="IDLE")
+        fsm = ResetInserter()(fsm)
+        self.submodules += fsm
+        self.comb += fsm.reset.eq(~self._enable.storage)
+        fsm.act("IDLE",
+            self.sink.ready.eq(1),
+            NextValue(offset, 0),
+            NextState("RUN"),
+        )
+        fsm.act("RUN",
+            self._sink.valid.eq(self.sink.valid),
+            self._sink.data.eq(self.sink.data),
+            self._sink.address.eq(base + offset),
+            self.sink.ready.eq(self._sink.ready),
+            If(self.sink.valid & self.sink.ready,
+                NextValue(offset, offset + 1),
+                If(offset == (length - 1),
+                    If(self._loop.storage,
+                        NextValue(offset, 0)
+                    ).Else(
+                        NextState("DONE")
+                    )
+                )
+            )
+        )
+        fsm.act("DONE",
+            self._done.status.eq(1)
+        )