diff --git a/soc/artiqlib/rtio/__init__.py b/soc/artiqlib/rtio/__init__.py index 1f1b4f691..9c20b7171 100644 --- a/soc/artiqlib/rtio/__init__.py +++ b/soc/artiqlib/rtio/__init__.py @@ -1,111 +1,2 @@ -from migen.fhdl.std import * -from migen.bank.description import * -from migen.genlib.fifo import SyncFIFOBuffered -from migen.genlib.cdc import MultiReg - -from types import SimpleNamespace - from artiqlib.rtio import phy - -class RTIOBankO(Module): - def __init__(self, channels, counter_width, fine_ts_width, fifo_depth): - self.sel = Signal(max=len(channels)) - self.timestamp = Signal(counter_width+fine_ts_width) - self.value = Signal() - self.writable = Signal() - self.we = Signal() - self.underflow = Signal() - self.level = Signal(bits_for(fifo_depth)) - - ### - - counter = Signal(counter_width) - self.sync += [ - counter.eq(counter + 1), - If(self.we & self.writable, - If(self.timestamp[fine_ts_width:] < counter + 2, self.underflow.eq(1)) - ) - ] - - fifos = [] - for n, channel in enumerate(channels): - fifo = SyncFIFOBuffered([ - ("timestamp", counter_width+fine_ts_width), ("value", 2)], - fifo_depth) - self.submodules += fifo - fifos.append(fifo) - - # FIFO write - self.comb += [ - fifo.din.timestamp.eq(self.timestamp), - fifo.din.value.eq(self.value), - fifo.we.eq(self.we & (self.sel == n)) - ] - - # FIFO read - self.comb += [ - channel.hit.eq(fifo.readable & - (fifo.dout.timestamp[fine_ts_width:] == counter)), - channel.value.eq(fifo.dout.value), - fifo.re.eq(channel.hit) - ] - if fine_ts_width: - self.comb += channel.fine_ts.eq(fifo.dout.timestamp[:fine_ts_width]) - - selfifo = Array(fifos)[self.sel] - self.comb += self.writable.eq(selfifo.writable), self.level.eq(selfifo.level) - -class RTIO(Module, AutoCSR): - def __init__(self, phy, counter_width=32, ofifo_depth=8, ififo_depth=8): - # Extract info from PHY - if hasattr(phy.interface[0], "o_fine_ts"): - fine_ts_width = flen(channels[0].o_fine_ts) - else: - fine_ts_width = 0 - oes = [padif.oe for padif in phy.interface if hasattr(padif, "oe")] - - # Submodules - self.submodules.bank_o = InsertReset(RTIOBankO( - [SimpleNamespace(hit=padif.o_set_value, - value=padif.o_value, - fine_ts=getattr(padif, "o_fine_ts", None)) - for padif in phy.interface], - counter_width, fine_ts_width, ofifo_depth)) - - # CSRs - self._r_reset = CSRStorage(reset=1) - self._r_chan_sel = CSRStorage(flen(self.bank_o.sel)) - - self._r_oe = CSR() - - self._r_o_timestamp = CSRStorage(counter_width+fine_ts_width) - self._r_o_value = CSRStorage() - self._r_o_writable = CSRStatus() - self._r_o_we = CSR() - self._r_o_underflow = CSRStatus() - self._r_o_level = CSRStatus(bits_for(ofifo_depth)) - - # OE - oes = [] - for n, padif in enumerate(phy.interface): - if hasattr(padif, "oe"): - self.sync += \ - If(self._r_oe.re & (self._r_chan_sel.storage == n), - padif.oe.eq(self._r_oe.r) - ) - oes.append(padif.oe) - else: - oes.append(1) - self.comb += self._r_oe.w.eq(Array(oes)[self._r_chan_sel.storage]) - - # Output/Gate - self.comb += [ - self.bank_o.reset.eq(self._r_reset.storage), - self.bank_o.sel.eq(self._r_chan_sel.storage), - self.bank_o.timestamp.eq(self._r_o_timestamp.storage), - self.bank_o.value.eq(self._r_o_value.storage), - self._r_o_writable.status.eq(self.bank_o.writable), - self.bank_o.we.eq(self._r_o_we.re), - self._r_o_underflow.status.eq(self.bank_o.underflow), - self._r_o_level.status.eq(self.bank_o.level) - ] +from artiqlib.rtio.core import RTIO diff --git a/soc/artiqlib/rtio/core.py b/soc/artiqlib/rtio/core.py new file mode 100644 index 000000000..79ab4b77d --- /dev/null +++ b/soc/artiqlib/rtio/core.py @@ -0,0 +1,183 @@ +from migen.fhdl.std import * +from migen.bank.description import * +from migen.genlib.fifo import SyncFIFOBuffered +from migen.genlib.cdc import MultiReg + +from artiqlib.rtio.rbus import get_fine_ts_width + +class _RTIOBankO(Module): + def __init__(self, rbus, counter_width, fine_ts_width, fifo_depth, counter_init): + self.sel = Signal(max=len(rbus)) + self.timestamp = Signal(counter_width+fine_ts_width) + self.value = Signal(2) + self.writable = Signal() + self.we = Signal() + self.underflow = Signal() + self.level = Signal(bits_for(fifo_depth)) + + ### + + counter = Signal(counter_width, reset=counter_init) + self.sync += [ + counter.eq(counter + 1), + If(self.we & self.writable, + If(self.timestamp[fine_ts_width:] < counter + 2, self.underflow.eq(1)) + ) + ] + + fifos = [] + for n, chif in enumerate(rbus): + fifo = SyncFIFOBuffered([ + ("timestamp", counter_width+fine_ts_width), ("value", 2)], + fifo_depth) + self.submodules += fifo + fifos.append(fifo) + + # FIFO write + self.comb += [ + fifo.din.timestamp.eq(self.timestamp), + fifo.din.value.eq(self.value), + fifo.we.eq(self.we & (self.sel == n)) + ] + + # FIFO read + self.comb += [ + chif.o_stb.eq(fifo.readable & + (fifo.dout.timestamp[fine_ts_width:] == counter)), + chif.o_value.eq(fifo.dout.value), + fifo.re.eq(chif.o_stb) + ] + if fine_ts_width: + self.comb += chif.o_fine_ts.eq(fifo.dout.timestamp[:fine_ts_width]) + + selfifo = Array(fifos)[self.sel] + self.comb += self.writable.eq(selfifo.writable), self.level.eq(selfifo.level) + +class _RTIOBankI(Module): + def __init__(self, rbus, counter_width, fine_ts_width, fifo_depth): + self.sel = Signal(max=len(rbus)) + self.timestamp = Signal(counter_width+fine_ts_width) + self.value = Signal() + self.readable = Signal() + self.re = Signal() + self.overflow = Signal() + + ### + + counter = Signal(counter_width) + self.sync += counter.eq(counter + 1) + + timestamps = [] + values = [] + readables = [] + overflows = [] + for n, chif in enumerate(rbus): + if hasattr(chif, "oe"): + sensitivity = Signal(2) + self.sync += If(~chif.oe & chif.o_stb, + sensitivity.eq(chif.o_value)) + + fifo = SyncFIFOBuffered([ + ("timestamp", counter_width+fine_ts_width), ("value", 1)], + fifo_depth) + self.submodules += fifo + + # FIFO write + if fine_ts_width: + full_ts = Cat(chif.i_fine_ts, counter) + else: + full_ts = counter + self.comb += [ + fifo.din.timestamp.eq(full_ts), + fifo.din.value.eq(chif.i_value), + fifo.we.eq(~chif.oe & chif.i_stb & + ((chif.i_value & sensitivity[0]) | (~chif.i_value & sensitivity[1]))) + ] + + # FIFO read + timestamps.append(fifo.dout.timestamp) + values.append(fifo.dout.value) + readables.append(fifo.readable) + self.comb += fifo.re.eq(self.re & (self.sel == n)) + + overflow = Signal() + self.sync += If(fifo.we & ~fifo.writable, overflow.eq(1)) + overflows.append(overflow) + else: + timestamps.append(0) + values.append(0) + readables.append(0) + overflows.append(0) + + self.comb += [ + self.timestamp.eq(Array(timestamps)[self.sel]), + self.value.eq(Array(values)[self.sel]), + self.readable.eq(Array(readables)[self.sel]), + self.overflow.eq(Array(overflows)[self.sel]) + ] + +class RTIO(Module, AutoCSR): + def __init__(self, phy, counter_width=32, ofifo_depth=8, ififo_depth=8): + fine_ts_width = get_fine_ts_width(phy.rbus) + + # Submodules + self.submodules.bank_o = InsertReset(_RTIOBankO(phy.rbus, + counter_width, fine_ts_width, ofifo_depth, + phy.loopback_latency)) + self.submodules.bank_i = InsertReset(_RTIOBankI(phy.rbus, + counter_width, fine_ts_width, ofifo_depth)) + + # CSRs + self._r_reset = CSRStorage(reset=1) + self._r_chan_sel = CSRStorage(flen(self.bank_o.sel)) + + self._r_oe = CSR() + + self._r_o_timestamp = CSRStorage(counter_width+fine_ts_width) + self._r_o_value = CSRStorage(2) + self._r_o_writable = CSRStatus() + self._r_o_we = CSR() + self._r_o_underflow = CSRStatus() + self._r_o_level = CSRStatus(bits_for(ofifo_depth)) + + self._r_i_timestamp = CSRStatus(counter_width+fine_ts_width) + self._r_i_value = CSRStatus() + self._r_i_readable = CSRStatus() + self._r_i_re = CSR() + self._r_i_overflow = CSRStatus() + + # OE + oes = [] + for n, chif in enumerate(phy.rbus): + if hasattr(chif, "oe"): + self.sync += \ + If(self._r_oe.re & (self._r_chan_sel.storage == n), + chif.oe.eq(self._r_oe.r) + ) + oes.append(chif.oe) + else: + oes.append(1) + self.comb += self._r_oe.w.eq(Array(oes)[self._r_chan_sel.storage]) + + # Output/Gate + self.comb += [ + self.bank_o.reset.eq(self._r_reset.storage), + self.bank_o.sel.eq(self._r_chan_sel.storage), + self.bank_o.timestamp.eq(self._r_o_timestamp.storage), + self.bank_o.value.eq(self._r_o_value.storage), + self._r_o_writable.status.eq(self.bank_o.writable), + self.bank_o.we.eq(self._r_o_we.re), + self._r_o_underflow.status.eq(self.bank_o.underflow), + self._r_o_level.status.eq(self.bank_o.level) + ] + + # Input + self.comb += [ + self.bank_i.reset.eq(self._r_reset.storage), + self.bank_i.sel.eq(self._r_chan_sel.storage), + self._r_i_timestamp.status.eq(self.bank_i.timestamp), + self._r_i_value.status.eq(self.bank_i.value), + self._r_i_readable.status.eq(self.bank_i.readable), + self.bank_i.re.eq(self._r_i_re.re), + self._r_i_overflow.status.eq(self.bank_i.overflow) + ] diff --git a/soc/artiqlib/rtio/phy.py b/soc/artiqlib/rtio/phy.py index 4e7f1de78..1cdfa96ba 100644 --- a/soc/artiqlib/rtio/phy.py +++ b/soc/artiqlib/rtio/phy.py @@ -1,50 +1,29 @@ from migen.fhdl.std import * from migen.genlib.cdc import MultiReg -from migen.genlib.record import Record -class PHYBase(Module): - def __init__(self, fine_ts_bits, pads, output_only_pads): - self.interface = [] - - for pad in pads: - layout = [ - ("o_set_value", 1), - ("o_value", 1) - ] - if fine_ts_bits: - layout.append(("o_fine_ts", fine_ts_bits)) - if pad not in output_only_pads: - layout += [ - ("oe", 1), - ("i_detect", 1), - ("i_value", 1) - ] - if fine_ts_bits: - layout.append(("i_fine_ts", fine_ts_bits)) - self.interface.append(Record(layout)) +from artiqlib.rtio.rbus import create_rbus -class SimplePHY(PHYBase): +class SimplePHY(Module): def __init__(self, pads, output_only_pads=set()): - PHYBase.__init__(self, 0, pads, output_only_pads) + self.rbus = create_rbus(0, pads, output_only_pads) + self.loopback_latency = 3 - for pad, padif in zip(pads, self.interface): - o_pad_d1 = Signal() + ### + + for pad, chif in zip(pads, self.rbus): o_pad = Signal() - self.sync += [ - If(padif.o_set_value, o_pad_d1.eq(padif.o_value)), - o_pad.eq(o_pad_d1) - ] + self.sync += If(chif.o_stb, o_pad.eq(chif.o_value)) if pad in output_only_pads: self.comb += pad.eq(o_pad) else: ts = TSTriple() i_pad = Signal() - self.sync += ts.oe.eq(padif.oe) + self.sync += ts.oe.eq(chif.oe) self.comb += ts.o.eq(o_pad) self.specials += MultiReg(ts.i, i_pad), \ ts.get_tristate(pad) i_pad_d = Signal() self.sync += i_pad_d.eq(i_pad) - self.comb += padif.i_detect.eq(i_pad ^ i_pad_d), \ - padif.i_value.eq(i_pad) + self.comb += chif.i_stb.eq(i_pad ^ i_pad_d), \ + chif.i_value.eq(i_pad) diff --git a/soc/artiqlib/rtio/rbus.py b/soc/artiqlib/rtio/rbus.py new file mode 100644 index 000000000..47e739b7e --- /dev/null +++ b/soc/artiqlib/rtio/rbus.py @@ -0,0 +1,28 @@ +from migen.fhdl.std import * +from migen.genlib.record import Record + +def create_rbus(fine_ts_bits, pads, output_only_pads): + rbus = [] + for pad in pads: + layout = [ + ("o_stb", 1), + ("o_value", 2) + ] + if fine_ts_bits: + layout.append(("o_fine_ts", fine_ts_bits)) + if pad not in output_only_pads: + layout += [ + ("oe", 1), + ("i_stb", 1), + ("i_value", 1) + ] + if fine_ts_bits: + layout.append(("i_fine_ts", fine_ts_bits)) + rbus.append(Record(layout)) + return rbus + +def get_fine_ts_width(rbus): + if hasattr(rbus[0], "o_fine_ts"): + return flen(rbus[0].o_fine_ts) + else: + return 0