diff --git a/artiq/coredevice/rtio.py b/artiq/coredevice/rtio.py index e28f06e06..ae7a5ee03 100644 --- a/artiq/coredevice/rtio.py +++ b/artiq/coredevice/rtio.py @@ -21,17 +21,17 @@ class LLRTIOOut(AutoDB): @kernel def _set_oe(self): - syscall("rtio_oe", self.channel, True) + syscall("rtio_set_oe", time_to_cycles(now()), self.channel, True) @kernel - def set_value(self, t, value): - """Sets the value of the RTIO channel. + def set_o(self, t, value): + """Sets the output value of the RTIO channel. :param t: timestamp in RTIO cycles (64-bit integer). :param value: value to set at the output. """ - syscall("rtio_set", t, self.channel, value) + syscall("rtio_set_o", t, self.channel, value) @kernel def on(self, t): @@ -40,7 +40,7 @@ class LLRTIOOut(AutoDB): :param t: timestamp in RTIO cycles (64-bit integer). """ - self.set_value(t, 1) + self.set_o(t, 1) @kernel def off(self, t): @@ -49,28 +49,10 @@ class LLRTIOOut(AutoDB): :param t: timestamp in RTIO cycles (64-bit integer). """ - self.set_value(t, 0) + self.set_o(t, 0) -class _RTIOBase(AutoDB): - class DBKeys: - core = Device() - channel = Argument() - - def build(self): - self.previous_timestamp = int64(0) # in RTIO cycles - - @kernel - def _set_oe(self, oe): - syscall("rtio_oe", self.channel, oe) - - @kernel - def _set_value(self, value): - syscall("rtio_set", time_to_cycles(now()), self.channel, value) - self.previous_timestamp = time_to_cycles(now()) - - -class RTIOOut(_RTIOBase): +class RTIOOut(AutoDB): """RTIO output driver. Configures the corresponding RTIO channel as output on the core device and @@ -85,9 +67,22 @@ class RTIOOut(_RTIOBase): :param channel: channel number """ + class DBKeys: + core = Device() + channel = Argument() + def build(self): - _RTIOBase.build(self) - self._set_oe(True) + self.previous_timestamp = int64(0) # in RTIO cycles + self._set_oe() + + @kernel + def _set_oe(self): + syscall("rtio_set_oe", time_to_cycles(now()), self.channel, True) + + @kernel + def _set_o(self, value): + syscall("rtio_set_o", time_to_cycles(now()), self.channel, value) + self.previous_timestamp = time_to_cycles(now()) @kernel def sync(self): @@ -96,24 +91,19 @@ class RTIOOut(_RTIOBase): This function is useful to synchronize CPU-controlled devices (such as the AD9858 DDS bus) with related RTIO controls (such as RF switches at the output of the DDS). - """ while syscall("rtio_get_counter") < self.previous_timestamp: pass @kernel def on(self): - """Sets the output to a logic high state. - - """ - self._set_value(1) + """Sets the output to a logic high state.""" + self._set_o(1) @kernel def off(self): - """Sets the output to a logic low state. - - """ - self._set_value(0) + """Sets the output to a logic low state.""" + self._set_o(0) @kernel def pulse(self, duration): @@ -125,7 +115,7 @@ class RTIOOut(_RTIOBase): self.off() -class RTIOIn(_RTIOBase): +class RTIOIn(AutoDB): """RTIO input driver. Configures the corresponding RTIO channel as input on the core device and @@ -134,29 +124,41 @@ class RTIOIn(_RTIOBase): :param core: core device :param channel: channel number - """ + class DBKeys: + core = Device() + channel = Argument() + def build(self): - _RTIOBase.build(self) - self._set_oe(False) + self.previous_timestamp = int64(0) # in RTIO cycles + self._set_oe() + + @kernel + def _set_oe(self): + syscall("rtio_set_oe", time_to_cycles(now()), self.channel, False) + + @kernel + def _set_sensitivity(self, value): + syscall("rtio_set_sensitivity", time_to_cycles(now()), self.channel, value) + self.previous_timestamp = time_to_cycles(now()) @kernel def gate_rising(self, duration): """Register rising edge events for the specified duration. """ - self._set_value(1) + self._set_sensitivity(1) delay(duration) - self._set_value(0) + self._set_sensitivity(0) @kernel def gate_falling(self, duration): """Register falling edge events for the specified duration. """ - self._set_value(2) + self._set_sensitivity(2) delay(duration) - self._set_value(0) + self._set_sensitivity(0) @kernel def gate_both(self, duration): @@ -164,18 +166,9 @@ class RTIOIn(_RTIOBase): duration. """ - self._set_value(3) + self._set_sensitivity(3) delay(duration) - self._set_value(0) - - @kernel - def pileup_count(self): - """Returns the number of pileup events (a system clock cycle with too - many input transitions) since the last call to this function for this - channel (or since the last RTIO reset). - - """ - return syscall("rtio_pileup_count", self.channel) + self._set_sensitivity(0) @kernel def count(self): diff --git a/artiq/coredevice/runtime.py b/artiq/coredevice/runtime.py index 4c428d643..15cfff05c 100644 --- a/artiq/coredevice/runtime.py +++ b/artiq/coredevice/runtime.py @@ -12,11 +12,11 @@ llvm.initialize_all_targets() llvm.initialize_all_asmprinters() _syscalls = { - "rtio_oe": "ib:n", - "rtio_set": "Iii:n", + "rtio_set_o": "Iii:n", + "rtio_set_oe": "Iib:n", + "rtio_set_sensitivity": "Iii:n", "rtio_get_counter": "n:I", "rtio_get": "iI:I", - "rtio_pileup_count": "i:i", "dds_phase_clear_en": "ib:n", "dds_program": "Iiiiibb:n", } diff --git a/artiq/gateware/rtio/__init__.py b/artiq/gateware/rtio/__init__.py index f170e8354..9b34976c0 100644 --- a/artiq/gateware/rtio/__init__.py +++ b/artiq/gateware/rtio/__init__.py @@ -1,2 +1 @@ -from artiq.gateware.rtio import phy -from artiq.gateware.rtio.core import RTIO +from artiq.gateware.rtio.core import Channel, RTIO diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index a9719f8c8..c7d764920 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -2,12 +2,13 @@ from fractions import Fraction from migen.fhdl.std import * from migen.bank.description import * +from migen.genlib.misc import optree from migen.genlib.record import Record from migen.genlib.cdc import * from migen.genlib.fifo import AsyncFIFO from migen.genlib.resetsync import AsyncResetSynchronizer -from artiq.gateware.rtio.rbus import get_fine_ts_width +from artiq.gateware.rtio import rtlink class _GrayCodeTransfer(Module): @@ -35,26 +36,20 @@ class _GrayCodeTransfer(Module): class _RTIOCounter(Module): - def __init__(self, width, loopback_latency): + def __init__(self, width): self.width = width - # Timestamp counter in RTIO domain for outputs - self.o_value_rio = Signal(width) + # Timestamp counter in RTIO domain + self.value_rio = Signal(width) # Timestamp counter resynchronized to sys domain - # Lags behind o_value_rio, monotonic and glitch-free - self.o_value_sys = Signal(width) - # Timestamp counter in RTIO domain for inputs, - # compensated for PHY loopback latency - self.i_value_rio = Signal(width, reset=2**width-loopback_latency) + # Lags behind value_rio, monotonic and glitch-free + self.value_sys = Signal(width) # # # - self.sync.rio += [ - self.o_value_rio.eq(self.o_value_rio + 1), - self.i_value_rio.eq(self.i_value_rio + 1) - ] + self.sync.rio += self.value_rio.eq(self.value_rio + 1), gt = _GrayCodeTransfer(width) self.submodules += gt - self.comb += gt.i.eq(self.o_value_rio), self.o_value_sys.eq(gt.o) + self.comb += gt.i.eq(self.value_rio), self.value_sys.eq(gt.o) # CHOOSING A GUARD TIME @@ -78,263 +73,235 @@ class _RTIOCounter(Module): # timestamp*Tio > (fifo_depth-1)*Tio + time # We also have (guard time reached): # timestamp*Tio < time + guard_io_cycles*Tio -# [NB: time > counter.o_value_sys*Tio] +# [NB: time > counter.value_sys*Tio] # Thus we must have: # guard_io_cycles > fifo_depth-1 # # We can prevent overflows by choosing instead: # guard_io_cycles < fifo_depth-1 -class _RTIOBankO(Module): - def __init__(self, rbus, counter, fine_ts_width, fifo_depth, guard_io_cycles): - self.sel = Signal(max=len(rbus)) - # timestamp and value must be valid 1 cycle before we - self.timestamp = Signal(counter.width + fine_ts_width) - self.value = Signal(2) +class _OutputManager(Module): + def __init__(self, interface, counter, fifo_depth, guard_io_cycles): + data_width = rtlink.get_data_width(interface) + address_width = rtlink.get_address_width(interface) + fine_ts_width = rtlink.get_fine_ts_width(interface) + + ev_layout = [] + if data_width: + ev_layout.append(("data", data_width)) + if address_width: + ev_layout.append(("address", address_width)) + ev_layout.append(("timestamp", counter.width + fine_ts_width)) + # ev must be valid 1 cycle before we to account for the latency in + # generating replace, sequence_error and nop + self.ev = Record(ev_layout) + self.writable = Signal() self.we = Signal() # maximum throughput 1/2 - self.underflow = Signal() # valid 2 cycles after we - self.underflow_reset = Signal() + + self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() - self.sequence_error_reset = Signal() # # # - signal_underflow = Signal() - signal_sequence_error = Signal() - fifos = [] - ev_layout = [("timestamp", counter.width + fine_ts_width), - ("value", 2)] - for n, chif in enumerate(rbus): - # FIFO - fifo = RenameClockDomains(AsyncFIFO(ev_layout, fifo_depth), - {"write": "rsys", "read": "rio"}) - self.submodules += fifo - fifos.append(fifo) + # FIFO + fifo = RenameClockDomains(AsyncFIFO(ev_layout, fifo_depth), + {"write": "rsys", "read": "rio"}) + self.submodules += fifo - # Buffer - buf_pending = Signal() - buf = Record(ev_layout) - buf_just_written = Signal() + # Buffer + buf_pending = Signal() + buf = Record(ev_layout) + buf_just_written = Signal() - # Special cases - replace = Signal() - sequence_error = Signal() - nop = Signal() - self.sync.rsys += [ - replace.eq(self.timestamp == buf.timestamp[fine_ts_width:]), - sequence_error.eq(self.timestamp < buf.timestamp[fine_ts_width:]), - nop.eq(self.value == buf.value) - ] - self.comb += If(self.we & (self.sel == n) & sequence_error, - signal_sequence_error.eq(1)) - - # Buffer read and FIFO write - self.comb += fifo.din.eq(buf) - in_guard_time = Signal() - self.comb += in_guard_time.eq( - buf.timestamp[fine_ts_width:] < counter.o_value_sys + guard_io_cycles) - self.sync.rsys += If(in_guard_time, buf_pending.eq(0)) - self.comb += \ - If(buf_pending, - If(in_guard_time, - If(buf_just_written, - signal_underflow.eq(1) - ).Else( - fifo.we.eq(1) - ) - ), - If((self.we & (self.sel == n) - & ~replace & ~nop & ~sequence_error), - fifo.we.eq(1) - ) - ) - - # Buffer write - # Must come after read to handle concurrent read+write properly - self.sync.rsys += [ - buf_just_written.eq(0), - If(self.we & (self.sel == n) & ~nop & ~sequence_error, - buf_just_written.eq(1), - buf_pending.eq(1), - buf.timestamp.eq(self.timestamp), - buf.value.eq(self.value) - ) - ] - - # Buffer output of FIFO to improve timing - dout_stb = Signal() - dout_ack = Signal() - dout = Record(ev_layout) - self.sync.rio += \ - If(fifo.re, - dout_stb.eq(1), - dout.eq(fifo.dout) - ).Elif(dout_ack, - dout_stb.eq(0) - ) - self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) - - # FIFO read through buffer - self.comb += [ - dout_ack.eq( - dout.timestamp[fine_ts_width:] == counter.o_value_rio), - chif.o_stb.eq(dout_stb & dout_ack), - chif.o_value.eq(dout.value) - ] - if fine_ts_width: - self.comb += chif.o_fine_ts.eq( - dout.timestamp[:fine_ts_width]) - - self.comb += \ - self.writable.eq(Array(fifo.writable for fifo in fifos)[self.sel]) + # Special cases + replace = Signal() + sequence_error = Signal() + nop = Signal() self.sync.rsys += [ - If(self.underflow_reset, self.underflow.eq(0)), - If(self.sequence_error_reset, self.sequence_error.eq(0)), - If(signal_underflow, self.underflow.eq(1)), - If(signal_sequence_error, self.sequence_error.eq(1)) + replace.eq(self.ev.timestamp == buf.timestamp[fine_ts_width:]), + sequence_error.eq(self.ev.timestamp < buf.timestamp[fine_ts_width:]) ] + if interface.suppress_nop: + self.sync.rsys += nop.eq( + optree("&", + [getattr(self.ev, a) == getattr(buf, a) + for a in ("data", "address") + if hasattr(self.ev, a)], + default=0)) + self.comb += self.sequence_error.eq(self.we & sequence_error) + + # Buffer read and FIFO write + self.comb += fifo.din.eq(buf) + in_guard_time = Signal() + self.comb += in_guard_time.eq( + buf.timestamp[fine_ts_width:] + < counter.value_sys + guard_io_cycles) + self.sync.rsys += If(in_guard_time, buf_pending.eq(0)) + self.comb += \ + If(buf_pending, + If(in_guard_time, + If(buf_just_written, + self.underflow.eq(1) + ).Else( + fifo.we.eq(1) + ) + ), + If(self.we & ~replace & ~nop & ~sequence_error, + fifo.we.eq(1) + ) + ) + + # Buffer write + # Must come after read to handle concurrent read+write properly + self.sync.rsys += [ + buf_just_written.eq(0), + If(self.we & ~nop & ~sequence_error, + buf_just_written.eq(1), + buf_pending.eq(1), + buf.eq(self.ev) + ) + ] + self.comb += self.writable.eq(fifo.writable) + + # Buffer output of FIFO to improve timing + dout_stb = Signal() + dout_ack = Signal() + dout = Record(ev_layout) + self.sync.rio += \ + If(fifo.re, + dout_stb.eq(1), + dout.eq(fifo.dout) + ).Elif(dout_ack, + dout_stb.eq(0) + ) + self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) + + # FIFO read through buffer + # TODO: report error on stb & busy + self.comb += [ + dout_ack.eq( + dout.timestamp[fine_ts_width:] == counter.value_rio), + interface.stb.eq(dout_stb & dout_ack) + ] + if data_width: + self.comb += interface.data.eq(dout.data) + if address_width: + self.comb += interface.address.eq(dout.address) + if fine_ts_width: + self.comb += interface.fine_ts.eq(dout.timestamp[:fine_ts_width]) -class _RTIOBankI(Module): - def __init__(self, rbus, counter, fine_ts_width, fifo_depth): - self.sel = Signal(max=len(rbus)) - self.timestamp = Signal(counter.width + fine_ts_width) - self.value = Signal() +class _InputManager(Module): + def __init__(self, interface, counter, fifo_depth): + data_width = rtlink.get_data_width(interface) + fine_ts_width = rtlink.get_fine_ts_width(interface) + + ev_layout = [] + if data_width: + ev_layout.append(("data", data_width)) + if interface.timestamped: + ev_layout.append(("timestamp", counter.width + fine_ts_width)) + self.ev = Record(ev_layout) + self.readable = Signal() self.re = Signal() - self.overflow = Signal() - self.overflow_reset = Signal() - self.pileup_count = Signal(16) - self.pileup_reset = Signal() + + self.overflow = Signal() # pulsed # # # - timestamps = [] - values = [] - readables = [] - overflows = [] - pileup_counts = [] - ev_layout = [("timestamp", counter.width+fine_ts_width), - ("value", 1)] - for n, chif in enumerate(rbus): - if hasattr(chif, "oe"): - sensitivity = Signal(2) - self.sync.rio += If(~chif.oe & chif.o_stb, - sensitivity.eq(chif.o_value)) + fifo = RenameClockDomains(AsyncFIFO(ev_layout, fifo_depth), + {"read": "rsys", "write": "rio"}) + self.submodules += fifo - fifo = RenameClockDomains(AsyncFIFO(ev_layout, fifo_depth), - {"read": "rsys", "write": "rio"}) - self.submodules += fifo - - # FIFO write - if fine_ts_width: - full_ts = Cat(chif.i_fine_ts, counter.i_value_rio) - else: - full_ts = counter.i_value_rio - 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() - overflow_reset_sync = PulseSynchronizer("rsys", "rio") - self.submodules += overflow_reset_sync - self.comb += overflow_reset_sync.i.eq( - self.overflow_reset & (self.sel == n)) - self.sync.rio += [ - If(overflow_reset_sync.o, overflow.eq(0)), - If(fifo.we & ~fifo.writable, overflow.eq(1)) - ] - overflow_sys = Signal() - self.specials += MultiReg(overflow, overflow_sys, "rsys") - overflows.append(overflow_sys) - - pileup_count = Signal(16) - pileup_count_reset_sync = PulseSynchronizer("rsys", "rio") - self.submodules += pileup_count_reset_sync - self.comb += pileup_count_reset_sync.i.eq( - self.pileup_reset & (self.sel == n)) - self.sync.rio += \ - If(pileup_count_reset_sync.o, - pileup_count.eq(0) - ).Elif(chif.i_pileup, - If(pileup_count != 2**16 - 1, # saturate - pileup_count.eq(pileup_count + 1) - ) - ) - pileup_count_sync = _GrayCodeTransfer(16) - self.submodules += pileup_count_sync - self.comb += pileup_count_sync.i.eq(pileup_count) - pileup_counts.append(pileup_count_sync.o) + # FIFO write + if data_width: + self.comb += fifo.din.data.eq(interface.data) + if interface.timestamped: + if fine_ts_width: + full_ts = Cat(interface.fine_ts, counter.value_rio) else: - timestamps.append(0) - values.append(0) - readables.append(0) - overflows.append(0) - pileup_counts.append(0) + full_ts = counter.value_rio + self.comb += fifo.din.timestamp.eq(full_ts) + self.comb += fifo.we.eq(interface.stb) + # FIFO read 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]), - self.pileup_count.eq(Array(pileup_counts)[self.sel]) + self.ev.eq(fifo.dout), + self.readable.eq(fifo.readable), + fifo.re.eq(self.re) + ] + + overflow_sync = PulseSynchronizer("rio", "rsys") + overflow_ack_sync = PulseSynchronizer("rsys", "rio") + self.submodules += overflow_sync, overflow_ack_sync + overflow_blind = Signal() + self.comb += overflow_sync.i.eq(fifo.we & ~fifo.writable & ~overflow_blind) + self.sync.rio += [ + If(fifo.we & ~fifo.writable, overflow_blind.eq(1)), + If(overflow_ack_sync.o, overflow_blind.eq(0)) + ] + self.comb += [ + overflow_ack_sync.i.eq(overflow_sync.o), + self.overflow.eq(overflow_sync.o) ] -class RTIO(Module, AutoCSR): - def __init__(self, phy, clk_freq, counter_width=63, - ofifo_depth=64, ififo_depth=64, - guard_io_cycles=20): - fine_ts_width = get_fine_ts_width(phy.rbus) +class Channel: + def __init__(self, interface, ofifo_depth=64, ififo_depth=64): + self.interface = interface + self.ofifo_depth = ofifo_depth + self.ififo_depth = ififo_depth - # Submodules - self.submodules.counter = _RTIOCounter( - counter_width, phy.loopback_latency) - self.submodules.bank_o = _RTIOBankO( - phy.rbus, self.counter, fine_ts_width, ofifo_depth, guard_io_cycles) - self.submodules.bank_i = _RTIOBankI( - phy.rbus, self.counter, fine_ts_width, ififo_depth) + +class _CSRs(AutoCSR): + def __init__(self): + self.frequency_i = CSRStatus(32) + self.frequency_fn = CSRStatus(8) + self.frequency_fd = CSRStatus(8) + + +class _KernelCSRs(AutoCSR): + def __init__(self, chan_sel_width, + data_width, address_width, full_ts_width): + self.reset = CSRStorage(reset=1) + self.chan_sel = CSRStorage(chan_sel_width) + + self.o_data = CSRStorage(data_width) + self.o_address = CSRStorage(address_width) + self.o_timestamp = CSRStorage(full_ts_width) + self.o_we = CSR() + self.o_status = CSRStatus(3) + self.o_underflow_reset = CSR() + self.o_sequence_error_reset = CSR() + + self.i_data = CSRStatus(data_width) + self.i_timestamp = CSRStatus(full_ts_width) + self.i_re = CSR() + self.i_status = CSRStatus(2) + self.i_overflow_reset = CSR() + + self.counter = CSRStatus(full_ts_width) + self.counter_update = CSR() + + +class RTIO(Module): + def __init__(self, channels, clk_freq, counter_width=63, + guard_io_cycles=20): + data_width = max(rtlink.get_data_width(c.interface) + for c in channels) + address_width = max(rtlink.get_address_width(c.interface) + for c in channels) + fine_ts_width = max(rtlink.get_fine_ts_width(c.interface) + for c in channels) # CSRs - self._reset = CSRStorage(reset=1) - self._chan_sel = CSRStorage(flen(self.bank_o.sel)) - - self._oe = CSR() - - self._o_timestamp = CSRStorage(counter_width + fine_ts_width) - self._o_value = CSRStorage(2) - self._o_we = CSR() - self._o_status = CSRStatus(3) - self._o_underflow_reset = CSR() - self._o_sequence_error_reset = CSR() - - self._i_timestamp = CSRStatus(counter_width + fine_ts_width) - self._i_value = CSRStatus() - self._i_re = CSR() - self._i_status = CSRStatus(2) - self._i_overflow_reset = CSR() - self._i_pileup_count = CSRStatus(16) - self._i_pileup_reset = CSR() - - self._counter = CSRStatus(counter_width + fine_ts_width) - self._counter_update = CSR() - - self._frequency_i = CSRStatus(32) - self._frequency_fn = CSRStatus(8) - self._frequency_fd = CSRStatus(8) - + self.csrs = _CSRs() + self.kcsrs = _KernelCSRs(bits_for(len(channels)-1), + max(data_width, 1), + max(address_width, 1), + counter_width + fine_ts_width) # Clocking/Reset # Create rsys and rio domains based on sys and rio @@ -343,63 +310,113 @@ class RTIO(Module, AutoCSR): self.clock_domains.cd_rio = ClockDomain() self.comb += [ self.cd_rsys.clk.eq(ClockSignal()), - self.cd_rsys.rst.eq(self._reset.storage) + self.cd_rsys.rst.eq(self.kcsrs.reset.storage) ] self.comb += self.cd_rio.clk.eq(ClockSignal("rtio")) - self.specials += AsyncResetSynchronizer( - self.cd_rio, self._reset.storage) + self.specials += AsyncResetSynchronizer(self.cd_rio, + self.kcsrs.reset.storage) + + # Latency compensation + # TODO + + # Managers + self.submodules.counter = _RTIOCounter(counter_width) + + i_datas, i_timestamps = [], [] + o_statuses, i_statuses = [], [] + sel = self.kcsrs.chan_sel.storage + for n, channel in enumerate(channels): + selected = Signal() + self.comb += selected.eq(sel == n) + + o_manager = _OutputManager(channel.interface.o, self.counter, + channel.ofifo_depth, guard_io_cycles) + self.submodules += o_manager + + if hasattr(o_manager.ev, "data"): + self.comb += o_manager.ev.data.eq( + self.kcsrs.o_data.storage) + if hasattr(o_manager.ev, "address"): + self.comb += o_manager.ev.address.eq( + self.kcsrs.o_address.storage) + ts_shift = (flen(self.kcsrs.o_timestamp.storage) + - flen(o_manager.ev.timestamp)) + self.comb += o_manager.ev.timestamp.eq( + self.kcsrs.o_timestamp.storage[ts_shift:]) + + self.comb += o_manager.we.eq(selected & self.kcsrs.o_we.re) + + underflow = Signal() + sequence_error = Signal() + self.sync.rsys += [ + If(selected & self.kcsrs.o_underflow_reset.re, + underflow.eq(0)), + If(selected & self.kcsrs.o_sequence_error_reset.re, + sequence_error.eq(0)), + If(o_manager.underflow, underflow.eq(1)), + If(o_manager.sequence_error, sequence_error.eq(1)) + ] + o_statuses.append(Cat(~o_manager.writable, + underflow, + sequence_error)) + + if channel.interface.i is not None: + i_manager = _InputManager(channel.interface.i, self.counter, + channel.ififo_depth) + self.submodules += i_manager + + if hasattr(i_manager.ev, "data"): + i_datas.append(i_manager.ev.data) + else: + i_datas.append(0) + if channel.interface.i.timestamped: + ts_shift = (flen(self.kcsrs.i_timestamp.status) + - flen(i_manager.ev.timestamp)) + i_timestamps.append(i_manager.ev.timestamp << ts_shift) + else: + i_timestamps.append(0) + + self.comb += i_manager.re.eq(selected & self.kcsrs.i_re.re) + + overflow = Signal() + self.sync.rsys += [ + If(selected & self.kcsrs.i_overflow_reset.re, + overflow.eq(0)), + If(i_manager.overflow, + overflow.eq(1)) + ] + i_statuses.append(Cat(~i_manager.readable, overflow)) - # OE - oes = [] - for n, chif in enumerate(phy.rbus): - if hasattr(chif, "oe"): - self.sync += \ - If(self._oe.re & (self._chan_sel.storage == n), - chif.oe.eq(self._oe.r) - ) - oes.append(chif.oe) else: - oes.append(1) - self.comb += self._oe.w.eq(Array(oes)[self._chan_sel.storage]) - - # Output/Gate + i_datas.append(0) + i_timestamps.append(0) + i_statuses.append(0) self.comb += [ - self.bank_o.sel.eq(self._chan_sel.storage), - self.bank_o.timestamp.eq(self._o_timestamp.storage), - self.bank_o.value.eq(self._o_value.storage), - self.bank_o.we.eq(self._o_we.re), - self._o_status.status.eq(Cat(~self.bank_o.writable, - self.bank_o.underflow, - self.bank_o.sequence_error)), - self.bank_o.underflow_reset.eq(self._o_underflow_reset.re), - self.bank_o.sequence_error_reset.eq(self._o_sequence_error_reset.re) - ] - - # Input - self.comb += [ - self.bank_i.sel.eq(self._chan_sel.storage), - self._i_timestamp.status.eq(self.bank_i.timestamp), - self._i_value.status.eq(self.bank_i.value), - self.bank_i.re.eq(self._i_re.re), - self._i_status.status.eq(Cat(~self.bank_i.readable, self.bank_i.overflow)), - self.bank_i.overflow_reset.eq(self._i_overflow_reset.re), - self._i_pileup_count.status.eq(self.bank_i.pileup_count), - self.bank_i.pileup_reset.eq(self._i_pileup_reset.re) + self.kcsrs.i_data.status.eq(Array(i_datas)[sel]), + self.kcsrs.i_timestamp.status.eq(Array(i_timestamps)[sel]), + self.kcsrs.o_status.status.eq(Array(o_statuses)[sel]), + self.kcsrs.i_status.status.eq(Array(i_statuses)[sel]) ] # Counter access self.sync += \ - If(self._counter_update.re, - self._counter.status.eq(Cat(Replicate(0, fine_ts_width), - self.counter.o_value_sys)) + If(self.kcsrs.counter_update.re, + self.kcsrs.counter.status.eq(self.counter.value_sys + << fine_ts_width) ) - # Frequency + # Frequency CSRs clk_freq = Fraction(clk_freq).limit_denominator(255) clk_freq_i = int(clk_freq) clk_freq_f = clk_freq - clk_freq_i self.comb += [ - self._frequency_i.status.eq(clk_freq_i), - self._frequency_fn.status.eq(clk_freq_f.numerator), - self._frequency_fd.status.eq(clk_freq_f.denominator) + self.csrs.frequency_i.status.eq(clk_freq_i), + self.csrs.frequency_fn.status.eq(clk_freq_f.numerator), + self.csrs.frequency_fd.status.eq(clk_freq_f.denominator) ] + + def get_csrs(self): + return self.csrs.get_csrs() + + def get_kernel_csrs(self): + return self.kcsrs.get_csrs() diff --git a/artiq/gateware/rtio/phy.py b/artiq/gateware/rtio/phy.py deleted file mode 100644 index d1526adb6..000000000 --- a/artiq/gateware/rtio/phy.py +++ /dev/null @@ -1,30 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.cdc import MultiReg - -from artiq.gateware.rtio.rbus import create_rbus - - -class SimplePHY(Module): - def __init__(self, pads, output_only_pads=set()): - self.rbus = create_rbus(0, pads, output_only_pads) - self.loopback_latency = 3 - - # # # - - for pad, chif in zip(pads, self.rbus): - o_pad = Signal() - self.sync.rio += If(chif.o_stb, o_pad.eq(chif.o_value)) - if hasattr(chif, "oe"): - ts = TSTriple() - i_pad = Signal() - self.sync.rio += ts.oe.eq(chif.oe) - self.comb += ts.o.eq(o_pad) - self.specials += MultiReg(ts.i, i_pad, "rio"), \ - ts.get_tristate(pad) - - i_pad_d = Signal() - self.sync.rio += i_pad_d.eq(i_pad) - self.comb += chif.i_stb.eq(i_pad ^ i_pad_d), \ - chif.i_value.eq(i_pad) - else: - self.comb += pad.eq(o_pad) diff --git a/artiq/gateware/rtio/phy/__init__.py b/artiq/gateware/rtio/phy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artiq/gateware/rtio/phy/ttl_simple.py b/artiq/gateware/rtio/phy/ttl_simple.py new file mode 100644 index 000000000..d1985a012 --- /dev/null +++ b/artiq/gateware/rtio/phy/ttl_simple.py @@ -0,0 +1,46 @@ +from migen.fhdl.std import * +from migen.genlib.cdc import MultiReg + +from artiq.gateware.rtio import rtlink + + +class Output(Module): + def __init__(self, pad): + self.rtlink = rtlink.Interface(rtlink.OInterface(1)) + + # # # + + self.sync.rio += If(self.rtlink.o.stb, pad.eq(self.rtlink.o.data)) + + +class Inout(Module): + def __init__(self, pad): + self.rtlink = rtlink.Interface( + rtlink.OInterface(2, 2), + rtlink.IInterface(1)) + + # # # + + ts = TSTriple() + self.specials += ts.get_tristate(pad) + sensitivity = Signal(2) + + self.sync.rio += If(self.rtlink.o.stb, + Case(self.rtlink.o.address, { + 0: ts.o.eq(self.rtlink.o.data[0]), + 1: ts.oe.eq(self.rtlink.o.data[0]), + 2: sensitivity.eq(self.rtlink.o.data) + }).makedefault() + ) + + i = Signal() + i_d = Signal() + self.specials += MultiReg(ts.i, i, "rio") + self.sync.rio += i_d.eq(i) + self.comb += [ + self.rtlink.i.stb.eq( + (sensitivity[0] & ( i & ~i_d)) | + (sensitivity[1] & (~i & i_d)) + ), + self.rtlink.i.data.eq(i) + ] diff --git a/artiq/gateware/rtio/rbus.py b/artiq/gateware/rtio/rbus.py deleted file mode 100644 index 821b3c2a6..000000000 --- a/artiq/gateware/rtio/rbus.py +++ /dev/null @@ -1,31 +0,0 @@ -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), - ("i_pileup", 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 diff --git a/soc/runtime/Makefile b/soc/runtime/Makefile index a5e0b51e8..0fcf8c7b3 100644 --- a/soc/runtime/Makefile +++ b/soc/runtime/Makefile @@ -54,7 +54,7 @@ ksupport.elf: $(OBJECTS_KSUPPORT) ksupport_data.o: ksupport.bin $(LD) -r -b binary -o $@ $< -service_table.h: $(SERVICE_TABLE_INPUT) +service_table.h: $(SERVICE_TABLE_INPUT) gen_service_table.py @echo " GEN " $@ && ./gen_service_table.py $(SERVICE_TABLE_INPUT) > $@ services.c: service_table.h diff --git a/soc/runtime/gen_service_table.py b/soc/runtime/gen_service_table.py index 5ef506abd..1e1e0aee1 100755 --- a/soc/runtime/gen_service_table.py +++ b/soc/runtime/gen_service_table.py @@ -6,11 +6,11 @@ import sys services = [ ("syscalls", [ ("rpc", "comm_rpc"), - ("rtio_oe", "rtio_oe"), - ("rtio_set", "rtio_set"), + ("rtio_set_o", "rtio_set_o"), + ("rtio_set_oe", "rtio_set_oe"), + ("rtio_set_sensitivity", "rtio_set_sensitivity"), ("rtio_get_counter", "rtio_get_counter"), ("rtio_get", "rtio_get"), - ("rtio_pileup_count", "rtio_pileup_count"), ("dds_phase_clear_en", "dds_phase_clear_en"), ("dds_program", "dds_program"), ]), diff --git a/soc/runtime/rtio.c b/soc/runtime/rtio.c index 441f062ee..9f156b5cf 100644 --- a/soc/runtime/rtio.c +++ b/soc/runtime/rtio.c @@ -18,19 +18,10 @@ void rtio_init(void) rtio_reset_write(0); } -void rtio_oe(int channel, int oe) -{ - rtio_chan_sel_write(channel); - rtio_oe_write(oe); -} - -void rtio_set(long long int timestamp, int channel, int value) +static void write_and_process_status(long long int timestamp, int channel) { int status; - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_value_write(value); rtio_o_we_write(1); status = rtio_o_status_read(); if(status) { @@ -49,6 +40,33 @@ void rtio_set(long long int timestamp, int channel, int value) } } +void rtio_set_o(long long int timestamp, int channel, int value) +{ + rtio_chan_sel_write(channel); + rtio_o_timestamp_write(timestamp); + rtio_o_address_write(0); + rtio_o_data_write(value); + write_and_process_status(timestamp, channel); +} + +void rtio_set_oe(long long int timestamp, int channel, int oe) +{ + rtio_chan_sel_write(channel); + rtio_o_timestamp_write(timestamp); + rtio_o_address_write(1); + rtio_o_data_write(oe); + write_and_process_status(timestamp, channel); +} + +void rtio_set_sensitivity(long long int timestamp, int channel, int sensitivity) +{ + rtio_chan_sel_write(channel); + rtio_o_timestamp_write(timestamp); + rtio_o_address_write(2); + rtio_o_data_write(sensitivity); + write_and_process_status(timestamp, channel); +} + long long int rtio_get_counter(void) { rtio_counter_update_write(1); @@ -81,16 +99,6 @@ long long int rtio_get(int channel, long long int time_limit) return r; } -int rtio_pileup_count(int channel) -{ - int r; - - rtio_chan_sel_write(channel); - r = rtio_i_pileup_count_read(); - rtio_i_pileup_reset_write(1); - return r; -} - void rtio_fud_sync(void) { while(rtio_get_counter() < previous_fud_end_time); @@ -102,14 +110,15 @@ void rtio_fud(long long int fud_time) int status; rtio_chan_sel_write(RTIO_FUD_CHANNEL); + rtio_o_address_write(0); fud_end_time = fud_time + 3*8; previous_fud_end_time = fud_end_time; rtio_o_timestamp_write(fud_time); - rtio_o_value_write(1); + rtio_o_data_write(1); rtio_o_we_write(1); rtio_o_timestamp_write(fud_end_time); - rtio_o_value_write(0); + rtio_o_data_write(0); rtio_o_we_write(1); status = rtio_o_status_read(); if(status) { diff --git a/soc/runtime/rtio.h b/soc/runtime/rtio.h index 072236048..c1182e80b 100644 --- a/soc/runtime/rtio.h +++ b/soc/runtime/rtio.h @@ -2,11 +2,11 @@ #define __RTIO_H void rtio_init(void); -void rtio_oe(int channel, int oe); -void rtio_set(long long int timestamp, int channel, int value); +void rtio_set_o(long long int timestamp, int channel, int value); +void rtio_set_oe(long long int timestamp, int channel, int oe); +void rtio_set_sensitivity(long long int timestamp, int channel, int sensitivity); long long int rtio_get_counter(void); long long int rtio_get(int channel, long long int time_limit); -int rtio_pileup_count(int channel); void rtio_fud_sync(void); void rtio_fud(long long int fud_time); diff --git a/soc/runtime/test_mode.c b/soc/runtime/test_mode.c index 82bbaff08..15a81d830 100644 --- a/soc/runtime/test_mode.c +++ b/soc/runtime/test_mode.c @@ -82,8 +82,8 @@ static void ttlout(char *n, char *value) } rtio_init(); - rtio_oe(n2, 1); - rtio_set(rtio_get_counter() + 8000, n2, value2); + rtio_set_oe(rtio_get_counter() + 8000, n2, 1); + rtio_set_o(rtio_get_counter() + 8000, n2, value2); } static void ddssel(char *n) diff --git a/soc/targets/artiq_kc705.py b/soc/targets/artiq_kc705.py index 319b33c66..40ceed001 100644 --- a/soc/targets/artiq_kc705.py +++ b/soc/targets/artiq_kc705.py @@ -9,6 +9,7 @@ from misoclib.soc import mem_decoder from targets.kc705 import MiniSoC from artiq.gateware import amp, rtio, ad9858, nist_qc1 +from artiq.gateware.rtio.phy import ttl_simple class _RTIOCRG(Module, AutoCSR): @@ -50,24 +51,36 @@ class _Peripherals(MiniSoC): platform.request("user_led", 0), platform.request("user_led", 1))) - fud = Signal() self.comb += [ platform.request("ttl_l_tx_en").eq(1), platform.request("ttl_h_tx_en").eq(1) ] - rtio_ins = [platform.request("pmt") for i in range(2)] - rtio_outs = [platform.request("ttl", i) for i in range(16)] - rtio_outs.append(platform.request("user_led", 2)) - self.add_constant("RTIO_FUD_CHANNEL", len(rtio_ins) + len(rtio_outs)) - rtio_outs.append(fud) + # RTIO channels + rtio_channels = [] + for i in range(2): + phy = ttl_simple.Inout(platform.request("pmt", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink, ififo_depth=512)) + for i in range(16): + phy = ttl_simple.Output(platform.request("ttl", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + phy = ttl_simple.Output(platform.request("user_led", 2)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + fud = Signal() + self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels)) + phy = ttl_simple.Output(fud) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + # RTIO core self.submodules.rtiocrg = _RTIOCRG(platform, self.crg.pll_sys) - self.submodules.rtiophy = rtio.phy.SimplePHY( - rtio_ins + rtio_outs, - output_only_pads=set(rtio_outs)) - self.submodules.rtio = rtio.RTIO(self.rtiophy, - clk_freq=125000000, - ififo_depth=512) + self.submodules.rtio = rtio.RTIO(rtio_channels, + clk_freq=125000000) dds_pads = platform.request("dds") self.submodules.dds = ad9858.AD9858(dds_pads) @@ -85,7 +98,7 @@ class UP(_Peripherals): def __init__(self, *args, **kwargs): _Peripherals.__init__(self, *args, **kwargs) - rtio_csrs = self.rtio.get_csrs() + rtio_csrs = self.rtio.get_csrs() + self.rtio.get_kernel_csrs() self.submodules.rtiowb = wbgen.Bank(rtio_csrs) self.add_wb_slave(mem_decoder(self.mem_map["rtio"]), self.rtiowb.bus) self.add_csr_region("rtio", self.mem_map["rtio"] + 0x80000000, 32, rtio_csrs) @@ -93,6 +106,7 @@ class UP(_Peripherals): self.add_wb_slave(mem_decoder(self.mem_map["dds"]), self.dds.bus) self.add_memory_region("dds", self.mem_map["dds"] + 0x80000000, 64*4) + class AMP(_Peripherals): csr_map = { "kernel_cpu": 14 @@ -112,7 +126,7 @@ class AMP(_Peripherals): self.add_wb_slave(mem_decoder(self.mem_map["mailbox"]), self.mailbox.i1) self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["mailbox"]), self.mailbox.i2) - rtio_csrs = self.rtio.get_csrs() + rtio_csrs = self.rtio.get_kernel_csrs() self.submodules.rtiowb = wbgen.Bank(rtio_csrs) self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["rtio"]), self.rtiowb.bus) self.add_csr_region("rtio", self.mem_map["rtio"] + 0x80000000, 32, rtio_csrs) @@ -120,4 +134,5 @@ class AMP(_Peripherals): self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["dds"]), self.dds.bus) self.add_memory_region("dds", self.mem_map["dds"] + 0x80000000, 64*4) + default_subtarget = AMP diff --git a/soc/targets/artiq_pipistrello.py b/soc/targets/artiq_pipistrello.py index 5fdf507a1..c9b7415c6 100644 --- a/soc/targets/artiq_pipistrello.py +++ b/soc/targets/artiq_pipistrello.py @@ -7,6 +7,7 @@ from misoclib.soc import mem_decoder from targets.pipistrello import BaseSoC from artiq.gateware import amp, rtio, ad9858, nist_qc1 +from artiq.gateware.rtio.phy import ttl_simple class _RTIOCRG(Module, AutoCSR): @@ -66,26 +67,46 @@ class _Peripherals(BaseSoC): platform.request("user_led", 1), )) - fud = Signal() self.comb += [ platform.request("ttl_l_tx_en").eq(1), platform.request("ttl_h_tx_en").eq(1) ] - rtio_ins = [platform.request("pmt", i) for i in range(2)] - rtio_ins += [platform.request("xtrig", 0)] - rtio_outs = [platform.request("ttl", i) for i in range(16)] - rtio_outs += [platform.request("ext_led", 0)] - rtio_outs += [platform.request("user_led", i) for i in range(2, 5)] - self.add_constant("RTIO_FUD_CHANNEL", len(rtio_ins) + len(rtio_outs)) - rtio_outs.append(fud) + # RTIO channels + rtio_channels = [] + for i in range(2): + phy = ttl_simple.Inout(platform.request("pmt", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink, ififo_depth=512)) + + phy = ttl_simple.Inout(platform.request("xtrig", 0)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + for i in range(16): + phy = ttl_simple.Output(platform.request("ttl", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + phy = ttl_simple.Output(platform.request("ext_led", 0)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + for i in range(2, 5): + phy = ttl_simple.Output(platform.request("user_led", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + fud = Signal() + self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels)) + phy = ttl_simple.Output(fud) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + # RTIO core self.submodules.rtiocrg = _RTIOCRG(platform) - self.submodules.rtiophy = rtio.phy.SimplePHY( - rtio_ins + rtio_outs, - output_only_pads=set(rtio_outs)) - self.submodules.rtio = rtio.RTIO(self.rtiophy, - clk_freq=125000000, - ififo_depth=512) + self.submodules.rtio = rtio.RTIO(rtio_channels, + clk_freq=125000000) dds_pads = platform.request("dds") self.submodules.dds = ad9858.AD9858(dds_pads) diff --git a/soc/targets/artiq_ppro.py b/soc/targets/artiq_ppro.py index 867a84796..8052effac 100644 --- a/soc/targets/artiq_ppro.py +++ b/soc/targets/artiq_ppro.py @@ -9,6 +9,7 @@ from misoclib.mem.sdram.core.minicon import MiniconSettings from targets.ppro import BaseSoC from artiq.gateware import rtio, ad9858, nist_qc1 +from artiq.gateware.rtio.phy import ttl_simple class _TestGen(Module): @@ -78,29 +79,39 @@ class UP(BaseSoC): platform.request("user_led", 0), platform.request("ext_led", 0))) - fud = Signal() self.comb += [ platform.request("ttl_l_tx_en").eq(1), platform.request("ttl_h_tx_en").eq(1) ] - rtio_ins = [platform.request("pmt") for i in range(2)] - rtio_outs = [platform.request("ttl", i) for i in range(5)] - self.add_constant("RTIO_FUD_CHANNEL", len(rtio_ins) + len(rtio_outs)) - rtio_outs.append(fud) + # RTIO channels + rtio_channels = [] + for i in range(2): + phy = ttl_simple.Inout(platform.request("pmt", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + for i in range(5): + phy = ttl_simple.Output(platform.request("ttl", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + fud = Signal() + self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels)) + phy = ttl_simple.Output(fud) + self.submodules += phy + rtio_channels.append(rtio.Channel(phy.rtlink)) + + # RTIO core self.submodules.rtiocrg = _RTIOMiniCRG(platform) - self.submodules.rtiophy = rtio.phy.SimplePHY( - rtio_ins + rtio_outs, - output_only_pads=set(rtio_outs)) - self.submodules.rtio = rtio.RTIO(self.rtiophy, + self.submodules.rtio = rtio.RTIO(rtio_channels, clk_freq=125000000, - counter_width=32, - ififo_depth=512) + counter_width=32) - rtio_csrs = self.rtio.get_csrs() + rtio_csrs = self.rtio.get_csrs() + self.rtio.get_kernel_csrs() self.submodules.rtiowb = wbgen.Bank(rtio_csrs) self.add_wb_slave(mem_decoder(self.mem_map["rtio"]), self.rtiowb.bus) - self.add_csr_region("rtio", self.mem_map["rtio"] + 0x80000000, 32, rtio_csrs) + self.add_csr_region("rtio", self.mem_map["rtio"] + 0x80000000, + 32, rtio_csrs) if with_test_gen: self.submodules.test_gen = _TestGen(platform.request("ttl", 8))