From cd3f68ba7668f00bc91b4560dc149820a635b926 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 30 Nov 2016 18:43:19 +0800 Subject: [PATCH] rtio: DMA core (untested) --- artiq/gateware/rtio/dma.py | 331 +++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 artiq/gateware/rtio/dma.py diff --git a/artiq/gateware/rtio/dma.py b/artiq/gateware/rtio/dma.py new file mode 100644 index 000000000..31af3d430 --- /dev/null +++ b/artiq/gateware/rtio/dma.py @@ -0,0 +1,331 @@ +from migen import * +from migen.genlib.record import Record, layout_len +from migen.genlib.fsm import FSM +from misoc.interconnect.csr import * +from misoc.interconnect import stream, wishbone + +from artiq.gateware.rtio import cri + + +class WishboneReader(Module): + def __init__(self, bus=None): + if bus is None: + bus = wishbone.Interface + self.bus = bus + + aw = len(bus.adr) + dw = len(bus.dat_w) + self.sink = stream.Endpoint(["address", aw]) + self.source = stream.Endpoint(["data", dw]) + + # # # + + bus_stb = Signal() + data_reg_loaded = Signal() + + self.comb += [ + bus_stb.eq(self.sink.stb & (~data_reg_loaded | self.source.ack)), + bus.cyc.eq(bus_stb), + bus.stb.eq(bus_stb), + bus.adr.eq(self.sink.address), + self.sink.ack.eq(bus.ack), + self.source.stb.eq(data_reg_loaded), + ] + self.sync += [ + If(self.source.ack, data_reg_loaded.eq(0)), + If(bus.ack, + data_reg_loaded.eq(1), + self.source.data.eq(bus.dat_r), + self.source.eop.eq(self.sink.eop) + ) + ] + + +class DMAReader(Module, AutoCSR): + def __init__(self, membus, enable): + aw = len(membus.adr) + data_alignment = log2_int(len(membus.dat_w)//8) + + self.submodules.wb_reader = WishboneReader(membus) + self.source = self.wb_reader.source + + # All numbers in bytes + self.base_address = CSRStorage(aw + data_alignment, + alignment_bits=data_alignment) + self.last_address = CSRStorage(aw + data_alignment, + alignment_bits=data_alignment) + + # # # + + enable_r = Signal() + address = self.wb_reader.sink + self.sync += [ + enable_r.eq(enable), + If(enable & ~enable_r, + address.address.eq(self.base_address.storage), + address.eop.eq(0), + address.stb.eq(1) + ), + If(address.stb & address.ack, + If(address.eop, + address.stb.eq(0) + ).Else( + address.address.eq(address.address + 1), + If(~enable | (address.address == self.last_address.storage), + address.eop.eq(1) + ) + ) + ) + ] + + +class RawSlicer(Module): + def __init__(self, in_size, out_size, granularity): + g = granularity + + self.sink = stream.Endpoint([("data", in_size*g)]) + self.source = Signal(out_size*g) + self.source_stb = Signal() + self.source_consume = Signal(max=out_size+1) + + # # # + + # worst-case buffer space required (when loading): + # + buf_size = out_size - 1 + in_size + 1 + buf = Signal(buf_size*g) + self.comb += self.source.eq(buf[:out_size]) + + level = Signal(max=buf_size+1) + next_level = Signal(max=buf_size+1) + self.sync += level.eq(next_level) + self.comb += next_level.eq(level) + + load_buf = Signal() + shift_buf = Signal() + + self.sync += [ + If(load_buf, Case(level, + # note how the MSBs of the buffer are set to 0 + # (including the EOP marker position) + {i: buf[i*g:].eq(self.sink.data) + for i in range(out_size)})), + If(shift_buf, buf.eq(buf >> self.source_consume*g)) + ] + + fsm = FSM(reset_state="FETCH") + self.submodules += fsm + + fsm.act("FETCH", + self.sink.ack.eq(1), + load_buf.eq(1), + If(self.sink.stb, + If(self.sink.eop, + # insert bits of 0 to mark EOP + next_level.eq(level + in_size + 1) + ).Else( + next_level.eq(level + in_size) + ) + ), + If(next_level >= out_size, NextState("OUTPUT")) + ) + fsm.act("OUTPUT", + self.source_stb.eq(1), + shift_buf.eq(1), + next_level.eq(level - self.source_consume), + If(next_level < out_size, NextState("FETCH")) + ) + + +record_layout = [ + ("length", 8), # of whole record (header+data) + ("channel", 24), + ("timestamp", 64), + ("address", 16), + ("data", 512) # variable length +] + + +class RecordConverter(Module): + def __init__(self, stream_slicer): + self.source = stream.Endpoint(record_layout) + + hdrlen = layout_len(header_layout) - 512 + record_raw = Record(record_layout) + self.comb += [ + record_raw.raw_bits().eq(stream_slicer.source), + + record.channel.eq(record_raw.channel), + record.timestamp.eq(record_raw.timestamp), + record.address.eq(record_raw.address), + Case(record_raw.length, + {hdrlen+i*8: self.cri.o_data.eq(header.data[:]) + for i in range(512//8)}), + + self.source.stb.eq(stream_slicer.source_stb), + self.source.eop.eq(record_raw.length == 0), + If(self.source.ack, + If(record_raw.length == 0, + stream_slicer.source_consume.eq(1) + ).Else( + stream_slicer.source_consume.eq(record_raw.length) + ) + ) + ] + + +class RecordSlicer(Module): + def __init__(self, in_size): + self.submodules.raw_slicer = RawSlicer( + in_size, layout_len(record_layout)//8, 8) + self.submodules.record_converter = RecordConverter(self.raw_slicer) + self.sink = self.raw_slicer.sink + self.source = self.record_converter.source + + +class TimeOffset(Module, AutoCSR): + def __init__(self): + self.time_offset = CSRStorage(64) + self.source = stream.Endpoint(record_layout) + self.sink = stream.Endpoint(record_layout) + + # # # + + pipe_ce = Signal() + self.sync += \ + If(pipe_ce, + self.source.payload.connect(self.sink.payload, + exclude={"timestamp"}), + self.source.payload.timestamp.eq(self.sink.payload.timestamp + + self.time_offset.storage), + self.source.stb.eq(self.sink.stb) + ) + self.comb += [ + self.pipe_ce.eq(self.source.ack | ~self.source.stb), + self.sink.ack.eq(self.pipe_ce) + ] + + +class CRIMaster(Module, AutoCSR): + def __init__(self): + self.arb_req = CSRStorage() + self.arb_gnt = CSRStatus() + + self.error_status = CSRStatus(5) # same encoding as RTIO status + self.error_underflow_reset = CSR() + self.error_sequence_error_reset = CSR() + self.error_collision_reset = CSR() + self.error_busy_reset = CSR() + + self.error_channel = CSRStatus(24) + self.error_timestamp = CSRStatus(64) + self.error_address = CSRStatus(16) + + self.sink = stream.Endpoint(record_layout) + self.cri = cri.Interface() + self.busy = Signal() + + # # # + + self.comb += [ + self.cri.arb_req.eq(self.arb_req.storage), + self.arb_gnt.status.eq(self.cri.arb_gnt) + ] + + error_set = Signal(4) + for i, rcsr in enumerate([self.error_underflow_reset, self.error_sequence_error_reset, + self.error_collision_reset, self.error_busy_reset]): + # bit 0 is RTIO wait and always 0 here + bit = i + 1 + self.sync += [ + If(error_set[i], + self.error_status[bit].eq(1), + self.error_channel.status.eq(self.sink.channel), + self.error_timestamp.status.eq(self.sink.timestamp), + self.error_address.status.eq(self.sink.address) + ), + If(rcsr.re, self.error_status[bit].eq(0)) + ] + + self.comb += [ + self.cri.chan_sel.eq(self.sink.channel), + self.cri.o_timestamp.eq(self.sink.timestamp), + self.cri.o_address.eq(self.sink.address), + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(self.error_status.status == 0, + If(self.sink.stb, NextState("WRITE")) + ).Else( + # discard all data until errors are acked + self.sink.ack.eq(1) + ) + ) + fsm.act("WRITE", + self.busy.eq(1), + self.cri.cmd.eq(cri.commands["write"]), + NextState("CHECK_STATE") + ) + fsm.act("CHECK_STATE", + self.busy.eq(1), + If(~self.cri.o_status, + self.sink.ack.eq(1), + NextState("IDLE") + ), + If(self.cri.o_status[1], NextState("UNDERFLOW")), + If(self.cri.o_status[2], NextState("SEQUENCE_ERROR")), + If(self.cri.o_status[3], NextState("COLLISION")), + If(self.cri.o_status[4], NextState("BUSY")) + ) + for n, name in enumerate(["UNDERFLOW", "SEQUENCE_ERROR", + "COLLISION", "BUSY"]): + fsm.act(name, + self.busy.eq(1), + error_set.eq(1 << n), + self.cri.cmd.eq(cri.commands["o_" + name.lower() + "_reset"]), + self.sink.ack.eq(1), + NextState("IDLE") + ) + + +class DMA(Module): + def __init__(self, membus): + # shutdown procedure: set enable to 0, wait until busy=0 + self.enable = CSRStorage() + self.busy = CSRStatus() + + self.submodules.dma = DMAReader(membus, self.enable.storage) + self.submodules.slicer = RecordSlicer(len(membus.dat_w)) + self.submodules.time_offset = TimeOffset() + self.submodules.cri_master = CRIMaster() + + self.comb += [ + self.dma.source.connect(self.slicer.sink), + self.slicer.source.connect(self.time_offset.sink), + self.time_offset.source.connect(self.cri_master.sink) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(self.enable.storage, NextState("FLOWING")) + ) + fsm.act("FLOWING", + self.busy.status.eq(1), + If(self.cri_master.sink.stb & self.cri_master.sink.ack & self.cri_master.sink.eop, + NextState("WAIT_CRI_MASTER") + ) + ) + fsm.act("WAIT_CRI_MASTER", + self.busy.status.eq(1), + If(~self.cri_master.busy, NextState("IDLE")) + ) + + def get_csrs(self): + return ([self.enable, self.busy] + + self.dma.get_csrs() + self.time_offset.get_csrs() + + self.cri_master.get_csrs())