diff --git a/litex/soc/cores/dma.py b/litex/soc/cores/dma.py new file mode 100644 index 000000000..b9902a9aa --- /dev/null +++ b/litex/soc/cores/dma.py @@ -0,0 +1,201 @@ +# This file is Copyright (c) 2020 Florent Kermarrec +# 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) + )