diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 4c905ea5a..d675623d8 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -11,4 +11,8 @@ Release notes This requires reflashing the runtime and the flash storage filesystem image or erase and rewrite its entries. * RTIOCollisionError has been renamed to RTIOCollision - \ No newline at end of file +* the new API for DDS batches is: + with self.core_dds.batch: + ... + with core_dds a device of type artiq.coredevice.dds.CoreDDS. + The dds_bus device should not be used anymore. diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index da8c72b1d..9ea9f5c33 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,12 +1,12 @@ from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, - RTIOCollision, RTIOOverflow, + RTIOCollision, RTIOOverflow, RTIOBusy, DDSBatchError, CacheError) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) __all__ = [] __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision", - "RTIOOverflow", "DDSBatchError", "CacheError"] + "RTIOOverflow", "RTIOBusy", "DDSBatchError", "CacheError"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] diff --git a/artiq/coredevice/analyzer.py b/artiq/coredevice/analyzer.py index b2b458bff..f526bb2ec 100644 --- a/artiq/coredevice/analyzer.py +++ b/artiq/coredevice/analyzer.py @@ -40,16 +40,19 @@ def decode_message(data): DecodedDump = namedtuple( - "DecodedDump", "log_channel dds_channel dds_onehot_sel messages") + "DecodedDump", "log_channel dds_onehot_sel messages") def decode_dump(data): - parts = struct.unpack(">IQbbbb", data[:16]) + parts = struct.unpack(">IQbbb", data[:15]) (sent_bytes, total_byte_count, - overflow_occured, log_channel, dds_channel, dds_onehot_sel) = parts + overflow_occured, log_channel, dds_onehot_sel) = parts - if sent_bytes + 16 != len(data): - raise ValueError("analyzer dump has incorrect length") + expected_len = sent_bytes + 15 + if expected_len != len(data): + raise ValueError("analyzer dump has incorrect length " + "(got {}, expected {})".format( + len(data), expected_len)) if overflow_occured: logger.warning("analyzer FIFO overflow occured, " "some messages have been lost") @@ -57,14 +60,12 @@ def decode_dump(data): logger.info("analyzer ring buffer has wrapped %d times", total_byte_count//sent_bytes) - position = 16 + position = 15 messages = [] for _ in range(sent_bytes//32): messages.append(decode_message(data[position:position+32])) position += 32 - return DecodedDump(log_channel, - dds_channel, bool(dds_onehot_sel), - messages) + return DecodedDump(log_channel, bool(dds_onehot_sel), messages) def vcd_codes(): @@ -299,21 +300,31 @@ def get_vcd_log_channels(log_channel, messages): return vcd_log_channels -def get_ref_period(devices): +def get_single_device_argument(devices, module, cls, argument): ref_period = None for desc in devices.values(): if isinstance(desc, dict) and desc["type"] == "local": - if (desc["module"] == "artiq.coredevice.core" - and desc["class"] == "Core"): + if (desc["module"] == module + and desc["class"] == cls): if ref_period is None: - ref_period = desc["arguments"]["ref_period"] + ref_period = desc["arguments"][argument] else: - return None # more than one core device found + return None # more than one device found return ref_period +def get_ref_period(devices): + return get_single_device_argument(devices, "artiq.coredevice.core", + "Core", "ref_period") + + +def get_dds_sysclk(devices): + return get_single_device_argument(devices, "artiq.coredevice.core", + "CoreDDS", "sysclk") + + def create_channel_handlers(vcd_manager, devices, ref_period, - dds_channel, dds_onehot_sel): + dds_sysclk, dds_onehot_sel): channel_handlers = dict() for name, desc in sorted(devices.items(), key=itemgetter(0)): if isinstance(desc, dict) and desc["type"] == "local": @@ -327,19 +338,17 @@ def create_channel_handlers(vcd_manager, devices, ref_period, channel_handlers[channel] = TTLClockGenHandler(vcd_manager, name, ref_period) if (desc["module"] == "artiq.coredevice.dds" and desc["class"] in {"AD9858", "AD9914"}): - sysclk = desc["arguments"]["sysclk"] - dds_channel_ddsbus = desc["arguments"]["channel"] - if dds_channel in channel_handlers: - dds_handler = channel_handlers[dds_channel] + dds_bus_channel = desc["arguments"]["bus_channel"] + dds_channel = desc["arguments"]["channel"] + if dds_bus_channel in channel_handlers: + dds_handler = channel_handlers[dds_bus_channel] if dds_handler.dds_type != desc["class"]: raise ValueError("All DDS channels must have the same type") - if dds_handler.sysclk != sysclk: - raise ValueError("All DDS channels must have the same sysclk") else: dds_handler = DDSHandler(vcd_manager, desc["class"], - dds_onehot_sel, sysclk) - channel_handlers[dds_channel] = dds_handler - dds_handler.add_dds_channel(name, dds_channel_ddsbus) + dds_sysclk, dds_onehot_sel) + channel_handlers[dds_bus_channel] = dds_handler + dds_handler.add_dds_channel(name, dds_channel) return channel_handlers @@ -355,12 +364,16 @@ def decoded_dump_to_vcd(fileobj, devices, dump): else: logger.warning("unable to determine core device ref_period") ref_period = 1e-9 # guess + dds_sysclk = get_dds_sysclk(devices) + if dds_sysclk is None: + logger.warning("unable to determine DDS sysclk") + dds_sysclk = 3e9 # guess messages = sorted(dump.messages, key=get_message_time) channel_handlers = create_channel_handlers( vcd_manager, devices, ref_period, - dump.dds_channel, dump.dds_onehot_sel) + dds_sysclk, dump.dds_onehot_sel) vcd_log_channels = get_vcd_log_channels(dump.log_channel, messages) channel_handlers[dump.log_channel] = LogHandler(vcd_manager, vcd_log_channels) slack = vcd_manager.get_channel("rtio_slack", 64) diff --git a/artiq/coredevice/cache.py b/artiq/coredevice/cache.py new file mode 100644 index 000000000..02d07d5d5 --- /dev/null +++ b/artiq/coredevice/cache.py @@ -0,0 +1,45 @@ +from artiq.language.core import * +from artiq.language.types import * + + +@syscall +def cache_get(key: TStr) -> TList(TInt32): + raise NotImplementedError("syscall not simulated") + +@syscall +def cache_put(key: TStr, value: TList(TInt32)) -> TNone: + raise NotImplementedError("syscall not simulated") + + +class CoreCache: + """Core device cache access""" + def __init__(self, dmgr, core_device="core"): + self.core = dmgr.get(core_device) + + @kernel + def get(self, key): + """Extract a value from the core device cache. + After a value is extracted, it cannot be replaced with another value using + :meth:`put` until all kernel functions finish executing; attempting + to replace it will result in a :class:`artiq.coredevice.exceptions.CacheError`. + + If the cache does not contain any value associated with ``key``, an empty list + is returned. + + The value is not copied, so mutating it will change what's stored in the cache. + + :param str key: cache key + :return: a list of 32-bit integers + """ + return cache_get(key) + + @kernel + def put(self, key, value): + """Put a value into the core device cache. The value will persist until reboot. + + To remove a value from the cache, call :meth:`put` with an empty list. + + :param str key: cache key + :param list value: a list of 32-bit integers + """ + cache_put(key, value) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 32c2501bd..a836f5093 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -22,7 +22,7 @@ def _render_diagnostic(diagnostic, colored): lines = [shorten_path(path) for path in diagnostic.render(colored=colored)] return "\n".join(lines) -colors_supported = (os.name == 'posix') +colors_supported = os.name == "posix" class _DiagnosticEngine(diagnostic.Engine): def render_diagnostic(self, diagnostic): sys.stderr.write(_render_diagnostic(diagnostic, colored=colors_supported) + "\n") @@ -41,13 +41,6 @@ class CompileError(Exception): def rtio_get_counter() -> TInt64: raise NotImplementedError("syscall not simulated") -@syscall -def cache_get(key: TStr) -> TList(TInt32): - raise NotImplementedError("syscall not simulated") - -@syscall -def cache_put(key: TStr, value: TList(TInt32)) -> TNone: - raise NotImplementedError("syscall not simulated") class Core: """Core device driver. @@ -126,31 +119,3 @@ class Core: min_now = rtio_get_counter() + 125000 if now_mu() < min_now: at_mu(min_now) - - @kernel - def get_cache(self, key): - """Extract a value from the core device cache. - After a value is extracted, it cannot be replaced with another value using - :meth:`put_cache` until all kernel functions finish executing; attempting - to replace it will result in a :class:`artiq.coredevice.exceptions.CacheError`. - - If the cache does not contain any value associated with ``key``, an empty list - is returned. - - The value is not copied, so mutating it will change what's stored in the cache. - - :param str key: cache key - :return: a list of 32-bit integers - """ - return cache_get(key) - - @kernel - def put_cache(self, key, value): - """Put a value into the core device cache. The value will persist until reboot. - - To remove a value from the cache, call :meth:`put_cache` with an empty list. - - :param str key: cache key - :param list value: a list of 32-bit integers - """ - cache_put(key, value) diff --git a/artiq/coredevice/dds.py b/artiq/coredevice/dds.py index 99cbc72af..622292476 100644 --- a/artiq/coredevice/dds.py +++ b/artiq/coredevice/dds.py @@ -11,7 +11,12 @@ PHASE_MODE_TRACKING = 2 @syscall -def dds_init(time_mu: TInt64, channel: TInt32) -> TNone: +def dds_init(time_mu: TInt64, bus_channel: TInt32, channel: TInt32) -> TNone: + raise NotImplementedError("syscall not simulated") + +@syscall +def dds_set(time_mu: TInt64, bus_channel: TInt32, channel: TInt32, ftw: TInt32, + pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone: raise NotImplementedError("syscall not simulated") @syscall @@ -22,35 +27,36 @@ def dds_batch_enter(time_mu: TInt64) -> TNone: def dds_batch_exit() -> TNone: raise NotImplementedError("syscall not simulated") -@syscall -def dds_set(time_mu: TInt64, channel: TInt32, ftw: TInt32, - pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone: - raise NotImplementedError("syscall not simulated") - class _BatchContextManager: - def __init__(self, dds_bus): - self.dds_bus = dds_bus + def __init__(self, core_dds): + self.core_dds = core_dds + self.core = self.core_dds.core @kernel def __enter__(self): - self.dds_bus.batch_enter() + self.core_dds.dds_batch_enter() @kernel def __exit__(self, type, value, traceback): - self.dds_bus.batch_exit() + self.core_dds.dds_batch_exit() -class DDSBus: - """Core device Direct Digital Synthesis (DDS) bus batching driver. +class CoreDDS: + """Core device Direct Digital Synthesis (DDS) driver. - Manages batching of DDS commands on a DDS shared bus.""" - def __init__(self, dmgr): - self.core = dmgr.get("core") + Gives access to the DDS functionality of the core device. + + :param sysclk: DDS system frequency. The DDS system clock must be a + phase-locked multiple of the RTIO clock. + """ + def __init__(self, dmgr, sysclk, core_device="core"): + self.core = dmgr.get(core_device) + self.sysclk = sysclk self.batch = _BatchContextManager(self) @kernel - def batch_enter(self): + def dds_batch_enter(self): """Starts a DDS command batch. All DDS commands are buffered after this call, until ``batch_exit`` is called. @@ -59,26 +65,27 @@ class DDSBus: dds_batch_enter(now_mu()) @kernel - def batch_exit(self): + def dds_batch_exit(self): """Ends a DDS command batch. All buffered DDS commands are issued on the bus.""" dds_batch_exit() class _DDSGeneric: - """Core device Direct Digital Synthesis (DDS) driver. + """Core device Direct Digital Synthesis (DDS) channel driver. Controls one DDS channel managed directly by the core device's runtime. This class should not be used directly, instead, use the chip-specific drivers such as ``AD9858`` and ``AD9914``. - :param sysclk: DDS system frequency. + :param bus: name of the DDS bus device that this DDS is connected to. :param channel: channel number of the DDS device to control. """ - def __init__(self, dmgr, sysclk, channel): - self.core = dmgr.get("core") - self.sysclk = sysclk + def __init__(self, dmgr, bus_channel, channel, core_dds_device="core_dds"): + self.core_dds = dmgr.get(core_dds_device) + self.core = self.core_dds.core + self.bus_channel = bus_channel self.channel = channel self.phase_mode = PHASE_MODE_CONTINUOUS @@ -87,14 +94,14 @@ class _DDSGeneric: """Returns the frequency tuning word corresponding to the given frequency. """ - return round(int(2, width=64)**32*frequency/self.sysclk) + return round(int(2, width=64)**32*frequency/self.core_dds.sysclk) @portable def ftw_to_frequency(self, ftw): """Returns the frequency corresponding to the given frequency tuning word. """ - return ftw*self.sysclk/int(2, width=64)**32 + return ftw*self.core_dds.sysclk/int(2, width=64)**32 @portable def turns_to_pow(self, turns): @@ -124,7 +131,7 @@ class _DDSGeneric: """Resets and initializes the DDS channel. The runtime does this for all channels upon core device startup.""" - dds_init(now_mu(), self.channel) + dds_init(now_mu(), self.bus_channel, self.channel) @kernel def set_phase_mode(self, phase_mode): @@ -163,7 +170,8 @@ class _DDSGeneric: """ if phase_mode == _PHASE_MODE_DEFAULT: phase_mode = self.phase_mode - dds_set(now_mu(), self.channel, frequency, phase, phase_mode, amplitude) + dds_set(now_mu(), self.bus_channel, self.channel, + frequency, phase, phase_mode, amplitude) @kernel def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT, diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index aa00ad784..6317edfe6 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -98,6 +98,18 @@ class RTIOCollision(Exception): """ artiq_builtin = True +class RTIOBusy(Exception): + """Raised when at least one output event could not be executed because + the given channel was already busy executing a previous event. + + This exception is raised late: after the error condition occurred. More + specifically it is raised on submitting an event on the same channel after + the execution of the faulty event was attempted. + + The offending event was discarded. + """ + artiq_builtin = True + class RTIOOverflow(Exception): """Raised when at least one event could not be registered into the RTIO input FIFO because it was full (CPU not reading fast enough). diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index e9de36161..55148402a 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -34,8 +34,8 @@ class PCA9548: On the KC705, this chip is used for selecting the I2C buses on the two FMC connectors. HPC=1, LPC=2. """ - def __init__(self, dmgr, busno=0, address=0xe8): - self.core = dmgr.get("core") + def __init__(self, dmgr, busno=0, address=0xe8, core_device="core"): + self.core = dmgr.get(core_device) self.busno = busno self.address = address @@ -77,8 +77,8 @@ class TCA6424A: On the NIST QC2 hardware, this chip is used for switching the directions of TTL buffers.""" - def __init__(self, dmgr, busno=0, address=0x44): - self.core = dmgr.get("core") + def __init__(self, dmgr, busno=0, address=0x44, core_device="core"): + self.core = dmgr.get(core_device) self.busno = busno self.address = address diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 6221c8f53..80909e527 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,5 +1,5 @@ from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, - delay_mu, int) + delay_mu, int, mu_to_seconds) from artiq.language.units import MHz from artiq.coredevice.rtio import rtio_output, rtio_input_data @@ -40,8 +40,8 @@ class SPIMaster: :param channel: RTIO channel number of the SPI bus to control. """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period, self.core) self.channel = channel @@ -58,7 +58,7 @@ class SPIMaster: @portable def frequency_to_div(self, f): - return int(1/(f*self.ref_period)) + 1 + return int(1/(f*mu_to_seconds(self.ref_period_mu))) + 1 @kernel def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 9ac12ca48..ef855d6b2 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -10,8 +10,8 @@ class TTLOut: :param channel: channel number """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.channel = channel # in RTIO cycles @@ -82,8 +82,8 @@ class TTLInOut: :param channel: channel number """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.channel = channel # in RTIO cycles @@ -232,8 +232,8 @@ class TTLClockGen: :param channel: channel number """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.channel = channel # in RTIO cycles diff --git a/artiq/frontend/artiq_coreanalyzer.py b/artiq/frontend/artiq_coreanalyzer.py index 491b276f1..b715eebc0 100755 --- a/artiq/frontend/artiq_coreanalyzer.py +++ b/artiq/frontend/artiq_coreanalyzer.py @@ -48,7 +48,6 @@ def main(): decoded_dump = decode_dump(dump) if args.print_decoded: print("Log channel:", decoded_dump.log_channel) - print("DDS channel:", decoded_dump.dds_channel) print("DDS one-hot:", decoded_dump.dds_onehot_sel) for message in decoded_dump.messages: print(message) diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 8c2d1b237..69babebac 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -54,6 +54,26 @@ class _RTIOCounter(Module): self.comb += gt.i.eq(self.value_rtio), self.value_sys.eq(gt.o) +class _BlindTransfer(Module): + def __init__(self): + self.i = Signal() + self.o = Signal() + + ps = PulseSynchronizer("rio", "rsys") + ps_ack = PulseSynchronizer("rsys", "rio") + self.submodules += ps, ps_ack + blind = Signal() + self.sync.rio += [ + If(self.i, blind.eq(1)), + If(ps_ack.o, blind.eq(0)) + ] + self.comb += [ + ps.i.eq(self.i & ~blind), + ps_ack.i.eq(ps.o), + self.o.eq(ps.o) + ] + + # CHOOSING A GUARD TIME # # The buffer must be transferred to the FIFO soon enough to account for: @@ -95,7 +115,7 @@ class _OutputManager(Module): 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 + # generating replace, sequence_error and collision self.ev = Record(ev_layout) self.writable = Signal() @@ -104,6 +124,7 @@ class _OutputManager(Module): self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() self.collision = Signal() + self.busy = Signal() # pulsed # # # @@ -128,44 +149,29 @@ class _OutputManager(Module): sequence_error = Signal() collision = Signal() any_error = Signal() - nop = Signal() - self.sync.rsys += [ + if interface.enable_replace: # Note: replace may be asserted at the same time as collision # when addresses are different. In that case, it is a collision. - replace.eq(self.ev.timestamp == buf.timestamp), + self.sync.rsys += replace.eq(self.ev.timestamp == buf.timestamp) # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. - sequence_error.eq(self.ev.timestamp[fine_ts_width:] - < buf.timestamp[fine_ts_width:]) - ] - if hasattr(self.ev, "a"): - different_addresses = self.ev.a != buf.a - else: - different_addresses = 0 - if fine_ts_width: - self.sync.rsys += collision.eq( - (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) - & ((self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]) - |different_addresses)) - self.comb += any_error.eq(sequence_error | collision) - if interface.suppress_nop: - # disable NOP at reset: do not suppress a first write with all 0s - nop_en = Signal(reset=0) - addresses_equal = [getattr(self.ev, a) == getattr(buf, a) - for a in ("data", "address") - if hasattr(self.ev, a)] - if addresses_equal: - self.sync.rsys += nop.eq( - nop_en & reduce(and_, addresses_equal)) + self.sync.rsys += sequence_error.eq(self.ev.timestamp[fine_ts_width:] < + buf.timestamp[fine_ts_width:]) + if interface.enable_replace: + if address_width: + different_addresses = self.ev.address != buf.address else: - self.comb.eq(nop.eq(0)) - self.sync.rsys += [ - # buf now contains valid data. enable NOP. - If(self.we & ~any_error, nop_en.eq(1)), - # underflows cancel the write. allow it to be retried. - If(self.underflow, nop_en.eq(0)) - ] + different_addresses = 0 + if fine_ts_width: + self.sync.rsys += collision.eq( + (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) + & ((self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]) + |different_addresses)) + else: + self.sync.rsys += collision.eq( + self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) self.comb += [ + any_error.eq(sequence_error | collision), self.sequence_error.eq(self.we & sequence_error), self.collision.eq(self.we & collision) ] @@ -186,7 +192,7 @@ class _OutputManager(Module): fifo.we.eq(1) ) ), - If(self.we & ~replace & ~nop & ~any_error, + If(self.we & ~replace & ~any_error, fifo.we.eq(1) ) ) @@ -195,7 +201,7 @@ class _OutputManager(Module): # Must come after read to handle concurrent read+write properly self.sync.rsys += [ buf_just_written.eq(0), - If(self.we & ~nop & ~any_error, + If(self.we & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.eq(self.ev) @@ -217,12 +223,19 @@ class _OutputManager(Module): 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_rtio), interface.stb.eq(dout_stb & dout_ack) ] + + busy_transfer = _BlindTransfer() + self.submodules += busy_transfer + self.comb += [ + busy_transfer.i.eq(interface.stb & interface.busy), + self.busy.eq(busy_transfer.o), + ] + if data_width: self.comb += interface.data.eq(dout.data) if address_width: @@ -245,7 +258,7 @@ class _InputManager(Module): self.readable = Signal() self.re = Signal() - + self.overflow = Signal() # pulsed # # # @@ -278,18 +291,11 @@ class _InputManager(Module): 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)) - ] + overflow_transfer = _BlindTransfer() + self.submodules += overflow_transfer self.comb += [ - overflow_ack_sync.i.eq(overflow_sync.o), - self.overflow.eq(overflow_sync.o) + overflow_transfer.i.eq(fifo.we & ~fifo.writable), + self.overflow.eq(overflow_transfer.o), ] @@ -317,8 +323,7 @@ class Channel: class LogChannel: """A degenerate channel used to log messages into the analyzer.""" def __init__(self): - self.interface = rtlink.Interface( - rtlink.OInterface(32, suppress_nop=False)) + self.interface = rtlink.Interface(rtlink.OInterface(32)) self.probes = [] self.overrides = [] @@ -336,10 +341,11 @@ class _KernelCSRs(AutoCSR): self.o_address = CSRStorage(address_width) self.o_timestamp = CSRStorage(full_ts_width) self.o_we = CSR() - self.o_status = CSRStatus(4) + self.o_status = CSRStatus(5) self.o_underflow_reset = CSR() self.o_sequence_error_reset = CSR() self.o_collision_reset = CSR() + self.o_busy_reset = CSR() if data_width: self.i_data = CSRStatus(data_width) @@ -427,6 +433,7 @@ class RTIO(Module): underflow = Signal() sequence_error = Signal() collision = Signal() + busy = Signal() self.sync.rsys += [ If(selected & self.kcsrs.o_underflow_reset.re, underflow.eq(0)), @@ -434,14 +441,18 @@ class RTIO(Module): sequence_error.eq(0)), If(selected & self.kcsrs.o_collision_reset.re, collision.eq(0)), + If(selected & self.kcsrs.o_busy_reset.re, + busy.eq(0)), If(o_manager.underflow, underflow.eq(1)), If(o_manager.sequence_error, sequence_error.eq(1)), - If(o_manager.collision, collision.eq(1)) + If(o_manager.collision, collision.eq(1)), + If(o_manager.busy, busy.eq(1)) ] o_statuses.append(Cat(~o_manager.writable, underflow, sequence_error, - collision)) + collision, + busy)) if channel.interface.i is not None: i_manager = _InputManager(channel.interface.i, self.counter, diff --git a/artiq/gateware/rtio/phy/ttl_simple.py b/artiq/gateware/rtio/phy/ttl_simple.py index 1cc55ac48..a640cde5b 100644 --- a/artiq/gateware/rtio/phy/ttl_simple.py +++ b/artiq/gateware/rtio/phy/ttl_simple.py @@ -79,8 +79,7 @@ class Inout(Module): class ClockGen(Module): def __init__(self, pad, ftw_width=24): - self.rtlink = rtlink.Interface( - rtlink.OInterface(ftw_width, suppress_nop=False)) + self.rtlink = rtlink.Interface(rtlink.OInterface(ftw_width)) # # # diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 1b58525f6..d8fa752c2 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -5,7 +5,7 @@ from artiq.gateware.rtio import rtlink class RT2WB(Module): - def __init__(self, address_width, wb=None): + def __init__(self, address_width, wb=None, rtio_enable_replace=False): if wb is None: wb = wishbone.Interface() self.wb = wb @@ -13,7 +13,7 @@ class RT2WB(Module): rtlink.OInterface( len(wb.dat_w), address_width + 1, - suppress_nop=False), + enable_replace=rtio_enable_replace), rtlink.IInterface( len(wb.dat_r), timestamped=False) diff --git a/artiq/gateware/rtio/rtlink.py b/artiq/gateware/rtio/rtlink.py index c9d281ceb..d52e38ad3 100644 --- a/artiq/gateware/rtio/rtlink.py +++ b/artiq/gateware/rtio/rtlink.py @@ -3,7 +3,7 @@ from migen import * class OInterface: def __init__(self, data_width, address_width=0, - fine_ts_width=0, suppress_nop=True): + fine_ts_width=0, enable_replace=True): self.stb = Signal() self.busy = Signal() @@ -14,14 +14,14 @@ class OInterface: if fine_ts_width: self.fine_ts = Signal(fine_ts_width) - self.suppress_nop = suppress_nop + self.enable_replace = enable_replace @classmethod def like(cls, other): return cls(get_data_width(other), get_address_width(other), get_fine_ts_width(other), - other.suppress_nop) + other.enable_replace) class IInterface: diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 8f766ecce..ae15ec179 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -204,8 +204,9 @@ class NIST_QC1(_NIST_Ions): self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) - self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) - self.config["DDS_CHANNEL_COUNT"] = 8 + self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) + self.config["RTIO_DDS_COUNT"] = 1 + self.config["DDS_CHANNELS_PER_BUS"] = 8 self.config["DDS_AD9858"] = True phy = dds.AD9858(platform.request("dds"), 8) self.submodules += phy @@ -277,8 +278,9 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=128, ififo_depth=128)) - self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) - self.config["DDS_CHANNEL_COUNT"] = 11 + self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) + self.config["RTIO_DDS_COUNT"] = 1 + self.config["DDS_CHANNELS_PER_BUS"] = 11 self.config["DDS_AD9914"] = True self.config["DDS_ONEHOT_SEL"] = True phy = dds.AD9914(platform.request("dds"), 11, onehot=True) @@ -331,8 +333,9 @@ class NIST_QC2(_NIST_Ions): self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) - self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) - self.config["DDS_CHANNEL_COUNT"] = 12 + self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) + self.config["RTIO_DDS_COUNT"] = 1 + self.config["DDS_CHANNELS_PER_BUS"] = 12 self.config["DDS_AD9914"] = True self.config["DDS_ONEHOT_SEL"] = True phy = dds.AD9914(platform.request("dds"), 12, onehot=True) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index 8cd6b22ee..2b2c7de65 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -9,11 +9,10 @@ from fractions import Fraction from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer from migen.genlib.cdc import MultiReg +from migen.build.generic_platform import * from misoc.interconnect.csr import * -from misoc.interconnect import wishbone from misoc.cores import gpio -from misoc.integration.soc_core import mem_decoder from misoc.targets.pipistrello import (BaseSoC, soc_pipistrello_args, soc_pipistrello_argdict) from misoc.integration.builder import builder_args, builder_argdict @@ -24,6 +23,28 @@ from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds, spi from artiq import __version__ as artiq_version +_pmod_spi = [ + ("pmod_spi", 0, + Subsignal("cs_n", Pins("PMOD:0")), + Subsignal("mosi", Pins("PMOD:1")), + Subsignal("miso", Pins("PMOD:2")), + Subsignal("clk", Pins("PMOD:3")), + IOStandard("LVTTL") + ), + ("pmod_extended_spi", 0, + Subsignal("cs_n", Pins("PMOD:0")), + Subsignal("mosi", Pins("PMOD:1")), + Subsignal("miso", Pins("PMOD:2")), + Subsignal("clk", Pins("PMOD:3")), + Subsignal("int", Pins("PMOD:4")), + Subsignal("rst", Pins("PMOD:5")), + Subsignal("d0", Pins("PMOD:6")), + Subsignal("d1", Pins("PMOD:7")), + IOStandard("LVTTL") + ), +] + + class _RTIOCRG(Module, AutoCSR): def __init__(self, platform, clk_freq): self._clock_sel = CSRStorage() @@ -88,7 +109,8 @@ class _RTIOCRG(Module, AutoCSR): # ISE infers correct period constraints for cd_rtio.clk from # the internal clock. The first two TIGs target just the BUFGMUX. - platform.add_platform_command(""" + platform.add_platform_command( + """ NET "sys_clk" TNM_NET = "GRPsys_clk"; NET "{ext_clk}" TNM_NET = "GRPext_clk"; TIMESPEC "TSfix_ise1" = FROM "GRPsys_clk" TO "GRPext_clk" TIG; @@ -97,8 +119,9 @@ TIMESPEC "TSfix_ise2" = FROM "GRPsys_clk" TO "GRPint_clk" TIG; NET "{rtio_clk}" TNM_NET = "GRPrtio_clk"; TIMESPEC "TSfix_ise3" = FROM "GRPrtio_clk" TO "GRPsys_clk" TIG; TIMESPEC "TSfix_ise4" = FROM "GRPsys_clk" TO "GRPrtio_clk" TIG; -""", ext_clk=rtio_external_clk, int_clk=rtio_internal_clk, - rtio_clk=self.cd_rtio.clk) +""", + ext_clk=rtio_external_clk, int_clk=rtio_internal_clk, + rtio_clk=self.cd_rtio.clk) class NIST_QC1(BaseSoC, AMPSoC): @@ -112,9 +135,9 @@ class NIST_QC1(BaseSoC, AMPSoC): } csr_map.update(BaseSoC.csr_map) mem_map = { - "timer_kernel": 0x10000000, # (shadow @0x90000000) - "rtio": 0x20000000, # (shadow @0xa0000000) - "mailbox": 0x70000000 # (shadow @0xf0000000) + "timer_kernel": 0x10000000, # (shadow @0x90000000) + "rtio": 0x20000000, # (shadow @0xa0000000) + "mailbox": 0x70000000 # (shadow @0xf0000000) } mem_map.update(BaseSoC.mem_map) @@ -134,6 +157,7 @@ class NIST_QC1(BaseSoC, AMPSoC): trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd {build_name}.pcf """ platform.add_extension(nist_qc1.papilio_adapter_io) + platform.add_extension(_pmod_spi) self.submodules.leds = gpio.GPIOOut(platform.request("user_led", 4)) @@ -177,13 +201,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=4)) - pmod = self.platform.request("pmod", 0) - - for i in range(4, 8): - phy = ttl_simple.Inout(pmod.d[i]) - self.submodules += phy - rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128, - ofifo_depth=128)) + spi_pins = self.platform.request("pmod_extended_spi", 0) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) @@ -191,19 +209,15 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) - spi_pins = Module() - spi_pins.cs_n = pmod.d[0] - spi_pins.mosi = pmod.d[1] - spi_pins.miso = pmod.d[2] - spi_pins.clk = pmod.d[3] phy = spi.SPIMaster(spi_pins) self.submodules += phy self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( - phy, ofifo_depth=128, ififo_depth=128)) + phy, ofifo_depth=256, ififo_depth=256)) - self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) - self.config["DDS_CHANNEL_COUNT"] = 8 + self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) + self.config["RTIO_DDS_COUNT"] = 1 + self.config["DDS_CHANNELS_PER_BUS"] = 8 self.config["DDS_AD9858"] = True dds_pins = platform.request("dds") self.comb += dds_pins.p.eq(0) @@ -222,8 +236,8 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width self.config["DDS_RTIO_CLK_RATIO"] = 8 >> self.rtio.fine_ts_width self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) - self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, - self.get_native_sdram_if()) + self.submodules.rtio_analyzer = rtio.Analyzer( + self.rtio, self.get_native_sdram_if()) def main(): diff --git a/artiq/gui/applets.py b/artiq/gui/applets.py index b25ac1829..f9c93d9f5 100644 --- a/artiq/gui/applets.py +++ b/artiq/gui/applets.py @@ -85,7 +85,7 @@ class AppletIPCServer(AsyncioParentComm): await asyncio.wait([self.server_task]) -class AppletDock(QDockWidgetCloseDetect): +class _AppletDock(QDockWidgetCloseDetect): def __init__(self, datasets_sub, uid, name, command): QDockWidgetCloseDetect.__init__(self, "Applet: " + name) self.setObjectName("applet" + str(uid)) @@ -137,7 +137,7 @@ class AppletDock(QDockWidgetCloseDetect): def fix_initial_size(self): self.embed_window.resize(self.embed_widget.size()) - async def terminate(self): + async def terminate(self, delete_self=True): if self.starting_stopping: return self.starting_stopping = True @@ -163,8 +163,12 @@ class AppletDock(QDockWidgetCloseDetect): self.starting_stopping = False + if delete_self: + self.setParent(None) + self.deleteLater() + async def restart(self): - await self.terminate() + await self.terminate(False) await self.start() @@ -235,7 +239,7 @@ class AppletsDock(QtWidgets.QDockWidget): self.table.cellChanged.connect(self.cell_changed) def create(self, uid, name, command): - dock = AppletDock(self.datasets_sub, uid, name, command) + dock = _AppletDock(self.datasets_sub, uid, name, command) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) dock.setFloating(True) asyncio.ensure_future(dock.start()) diff --git a/artiq/gui/log.py b/artiq/gui/log.py index 3a33a9d3d..5b5dbbd39 100644 --- a/artiq/gui/log.py +++ b/artiq/gui/log.py @@ -303,6 +303,9 @@ class LogDockManager: return dock def on_dock_closed(self, name): + dock = self.docks[name] + dock.setParent(None) + dock.deleteLater() del self.docks[name] self.update_closable() diff --git a/artiq/gui/moninj.py b/artiq/gui/moninj.py index 43892876a..c9953d62a 100644 --- a/artiq/gui/moninj.py +++ b/artiq/gui/moninj.py @@ -118,13 +118,14 @@ class _TTLWidget(QtWidgets.QFrame): class _DDSWidget(QtWidgets.QFrame): - def __init__(self, channel, sysclk, title): + def __init__(self, bus_channel, channel, sysclk, title): + self.bus_channel = bus_channel self.channel = channel self.sysclk = sysclk QtWidgets.QFrame.__init__(self) - self.setFrameShape(QtWidgets.QFrame.Panel) + self.setFrameShape(QtWidgets.QFrame.Box) self.setFrameShadow(QtWidgets.QFrame.Raised) grid = QtWidgets.QGridLayout() @@ -146,13 +147,14 @@ class _DDSWidget(QtWidgets.QFrame): self.set_value(0) def set_value(self, ftw): - frequency = ftw*self.sysclk/2**32 + frequency = ftw*self.sysclk()/2**32 self._value.setText("{:.7f} MHz" .format(float(frequency)/1e6)) class _DeviceManager: def __init__(self, send_to_device, init): + self.dds_sysclk = 0 self.send_to_device = send_to_device self.ddb = dict() self.ttl_cb = lambda: None @@ -162,6 +164,9 @@ class _DeviceManager: for k, v in init.items(): self[k] = v + def get_dds_sysclk(self): + return self.dds_sysclk + def __setitem__(self, k, v): if k in self.ttl_widgets: del self[k] @@ -181,12 +186,15 @@ class _DeviceManager: self.ttl_widgets[k] = _TTLWidget( channel, self.send_to_device, force_out, title) self.ttl_cb() + if (v["module"] == "artiq.coredevice.dds" + and v["class"] == "CoreDDS"): + self.dds_sysclk = v["arguments"]["sysclk"] if (v["module"] == "artiq.coredevice.dds" and v["class"] in {"AD9858", "AD9914"}): + bus_channel = v["arguments"]["bus_channel"] channel = v["arguments"]["channel"] - sysclk = v["arguments"]["sysclk"] self.dds_widgets[channel] = _DDSWidget( - channel, sysclk, title) + bus_channel, channel, self.get_dds_sysclk, title) self.dds_cb() except KeyError: pass @@ -275,19 +283,23 @@ class MonInj(TaskObject): "is not present yet") return try: - ttl_levels, ttl_oes, ttl_overrides = \ - struct.unpack(">QQQ", data[:8*3]) + hlen = 8*3+4 + (ttl_levels, ttl_oes, ttl_overrides, + dds_rtio_first_channel, dds_channels_per_bus) = \ + struct.unpack(">QQQHH", data[:hlen]) for w in self.dm.ttl_widgets.values(): channel = w.channel w.set_value(ttl_levels & (1 << channel), ttl_oes & (1 << channel), ttl_overrides & (1 << channel)) - dds_data = data[8*3:] + dds_data = data[hlen:] ndds = len(dds_data)//4 ftws = struct.unpack(">" + "I"*ndds, dds_data) for w in self.dm.dds_widgets.values(): + offset = (dds_channels_per_bus*w.bus_channel + + w.channel-dds_rtio_first_channel) try: - ftw = ftws[w.channel] + ftw = ftws[offset] except KeyError: pass else: diff --git a/artiq/monkey_patches.py b/artiq/monkey_patches.py new file mode 100644 index 000000000..43504daaf --- /dev/null +++ b/artiq/monkey_patches.py @@ -0,0 +1,157 @@ +import sys + + +__all__ = [] + + +if sys.version_info[:3] == (3, 5, 1): + import asyncio + import socket + import collections + import itertools + import os + + # See https://github.com/m-labs/artiq/issues/253 + @asyncio.coroutines.coroutine + def create_server(self, protocol_factory, host=None, port=None, + *, + family=socket.AF_UNSPEC, + flags=socket.AI_PASSIVE, + sock=None, + backlog=100, + ssl=None, + reuse_address=None, + reuse_port=None): + """Create a TCP server. + The host parameter can be a string, in that case the TCP server is bound + to host and port. + The host parameter can also be a sequence of strings and in that case + the TCP server is bound to all hosts of the sequence. If a host + appears multiple times (possibly indirectly e.g. when hostnames + resolve to the same IP address), the server is only bound once to that + host. + Return a Server object which can be used to stop the service. + This method is a coroutine. + """ + if isinstance(ssl, bool): + raise TypeError('ssl argument must be an SSLContext or None') + if host is not None or port is not None: + if sock is not None: + raise ValueError( + 'host/port and sock can not be specified at the same time') + + AF_INET6 = getattr(socket, 'AF_INET6', 0) + if reuse_address is None: + reuse_address = os.name == 'posix' and sys.platform != 'cygwin' + sockets = [] + if host == '': + hosts = [None] + elif (isinstance(host, str) or + not isinstance(host, collections.Iterable)): + hosts = [host] + else: + hosts = host + + fs = [self._create_server_getaddrinfo(host, port, family=family, + flags=flags) + for host in hosts] + infos = yield from asyncio.tasks.gather(*fs, loop=self) + infos = set(itertools.chain.from_iterable(infos)) + + completed = False + try: + for res in infos: + af, socktype, proto, canonname, sa = res + try: + sock = socket.socket(af, socktype, proto) + except socket.error: + # Assume it's a bad family/type/protocol combination. + if self._debug: + asyncio.log.logger.warning('create_server() failed to create ' + 'socket.socket(%r, %r, %r)', + af, socktype, proto, exc_info=True) + continue + sockets.append(sock) + if reuse_address: + sock.setsockopt( + socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + if reuse_port: + if not hasattr(socket, 'SO_REUSEPORT'): + raise ValueError( + 'reuse_port not supported by socket module') + else: + sock.setsockopt( + socket.SOL_SOCKET, socket.SO_REUSEPORT, True) + # Disable IPv4/IPv6 dual stack support (enabled by + # default on Linux) which makes a single socket + # listen on both address families. + if af == AF_INET6 and hasattr(socket, 'IPPROTO_IPV6'): + sock.setsockopt(socket.IPPROTO_IPV6, + socket.IPV6_V6ONLY, + True) + try: + sock.bind(sa) + except OSError as err: + raise OSError(err.errno, 'error while attempting ' + 'to bind on address %r: %s' + % (sa, err.strerror.lower())) + completed = True + finally: + if not completed: + for sock in sockets: + sock.close() + else: + if sock is None: + raise ValueError('Neither host/port nor sock were specified') + sockets = [sock] + + server = asyncio.base_events.Server(self, sockets) + for sock in sockets: + sock.listen(backlog) + sock.setblocking(False) + self._start_serving(protocol_factory, sock, ssl, server) + if self._debug: + asyncio.log.logger.info("%r is serving", server) + return server + + asyncio.base_events.BaseEventLoop.create_server = create_server + + + # See https://github.com/m-labs/artiq/issues/247 + def _loop_writing(self, f=None, data=None): + try: + assert f is self._write_fut + self._write_fut = None + self._pending_write = 0 + if f: + f.result() + if data is None: + data = self._buffer + self._buffer = None + if not data: + if self._closing: + self._loop.call_soon(self._call_connection_lost, None) + if self._eof_written: + self._sock.shutdown(socket.SHUT_WR) + # Now that we've reduced the buffer size, tell the + # protocol to resume writing if it was paused. Note that + # we do this last since the callback is called immediately + # and it may add more data to the buffer (even causing the + # protocol to be paused again). + self._maybe_resume_protocol() + else: + self._write_fut = self._loop._proactor.send(self._sock, data) + if not self._write_fut.done(): + assert self._pending_write == 0 + self._pending_write = len(data) + self._write_fut.add_done_callback(self._loop_writing) + self._maybe_pause_protocol() + else: + self._write_fut.add_done_callback(self._loop_writing) + except (ConnectionResetError, ConnectionAbortedError) as exc: + self._force_close(exc) + except OSError as exc: + self._fatal_error(exc, 'Fatal write error on pipe transport') + + from asyncio import proactor_events + proactor_events._ProactorBaseWritePipeTransport._loop_writing = _loop_writing diff --git a/artiq/protocols/asyncio_server.py b/artiq/protocols/asyncio_server.py index e7e300eed..3316a8804 100644 --- a/artiq/protocols/asyncio_server.py +++ b/artiq/protocols/asyncio_server.py @@ -1,6 +1,8 @@ import asyncio from copy import copy +from artiq.monkey_patches import * + class AsyncioServer: """Generic TCP server based on asyncio. diff --git a/artiq/runtime/analyzer.c b/artiq/runtime/analyzer.c index a1b71b428..23f77c98d 100644 --- a/artiq/runtime/analyzer.c +++ b/artiq/runtime/analyzer.c @@ -12,7 +12,6 @@ struct analyzer_header { unsigned long long int total_byte_count; unsigned char overflow_occured; unsigned char log_channel; - unsigned char dds_channel; unsigned char dds_onehot_sel; } __attribute__((packed)); @@ -72,7 +71,6 @@ void analyzer_start(void) analyzer_header.overflow_occured = rtio_analyzer_message_encoder_overflow_read(); analyzer_header.log_channel = CONFIG_RTIO_LOG_CHANNEL; - analyzer_header.dds_channel = CONFIG_RTIO_DDS_CHANNEL; #ifdef CONFIG_DDS_ONEHOT_SEL analyzer_header.dds_onehot_sel = 1; #else diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index d1157cf65..bfe3e1fcf 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -16,12 +16,12 @@ static void rtio_output_blind(int channel, int addr, int data) rtio_o_we_write(1); } -static void dds_write(int addr, int data) +static void dds_write(int bus_channel, int addr, int data) { - rtio_output_blind(CONFIG_RTIO_DDS_CHANNEL, addr, data); + rtio_output_blind(bus_channel, addr, data); } -static int dds_read(int addr) +static int dds_read(int bus_channel, int addr) { int r; @@ -31,7 +31,7 @@ static int dds_read(int addr) #ifdef CONFIG_DDS_AD9914 #define DDS_READ_FLAG 256 #endif - dds_write(addr | DDS_READ_FLAG, 0); + dds_write(bus_channel, addr | DDS_READ_FLAG, 0); while(rtio_i_status_read() & RTIO_I_STATUS_EMPTY); r = rtio_i_data_read(); rtio_i_re_write(1); @@ -75,16 +75,18 @@ void bridge_main(void) struct msg_brg_dds_sel *msg; msg = (struct msg_brg_dds_sel *)umsg; - dds_write(DDS_GPIO, msg->channel << 1); + dds_write(msg->bus_channel, DDS_GPIO, msg->channel << 1); mailbox_acknowledge(); break; } case MESSAGE_TYPE_BRG_DDS_RESET: { unsigned int g; + struct msg_brg_dds_reset *msg; - g = dds_read(DDS_GPIO); - dds_write(DDS_GPIO, g | 1); - dds_write(DDS_GPIO, g); + msg = (struct msg_brg_dds_reset *)umsg; + g = dds_read(msg->bus_channel, DDS_GPIO); + dds_write(msg->bus_channel, DDS_GPIO, g | 1); + dds_write(msg->bus_channel, DDS_GPIO, g); mailbox_acknowledge(); break; @@ -95,7 +97,7 @@ void bridge_main(void) msg = (struct msg_brg_dds_read_request *)umsg; rmsg.type = MESSAGE_TYPE_BRG_DDS_READ_REPLY; - rmsg.data = dds_read(msg->address); + rmsg.data = dds_read(msg->bus_channel, msg->address); mailbox_send_and_wait(&rmsg); break; } @@ -103,14 +105,18 @@ void bridge_main(void) struct msg_brg_dds_write *msg; msg = (struct msg_brg_dds_write *)umsg; - dds_write(msg->address, msg->data); + dds_write(msg->bus_channel, msg->address, msg->data); mailbox_acknowledge(); break; } - case MESSAGE_TYPE_BRG_DDS_FUD: - dds_write(DDS_FUD, 0); + case MESSAGE_TYPE_BRG_DDS_FUD: { + struct msg_brg_dds_fud *msg; + + msg = (struct msg_brg_dds_fud *)umsg; + dds_write(msg->bus_channel, DDS_FUD, 0); mailbox_acknowledge(); break; + } default: mailbox_acknowledge(); break; diff --git a/artiq/runtime/bridge_ctl.c b/artiq/runtime/bridge_ctl.c index 11182efb9..1ebc4b0bc 100644 --- a/artiq/runtime/bridge_ctl.c +++ b/artiq/runtime/bridge_ctl.c @@ -43,30 +43,33 @@ void brg_ttlo(int n, int value) mailbox_send_and_wait(&msg); } -void brg_ddssel(int channel) +void brg_ddssel(int bus_channel, int channel) { struct msg_brg_dds_sel msg; msg.type = MESSAGE_TYPE_BRG_DDS_SEL; + msg.bus_channel = bus_channel; msg.channel = channel; mailbox_send_and_wait(&msg); } -void brg_ddsreset(void) +void brg_ddsreset(int bus_channel) { - struct msg_base msg; + struct msg_brg_dds_reset msg; msg.type = MESSAGE_TYPE_BRG_DDS_RESET; + msg.bus_channel = bus_channel; mailbox_send_and_wait(&msg); } -unsigned int brg_ddsread(unsigned int address) +unsigned int brg_ddsread(int bus_channel, unsigned int address) { struct msg_brg_dds_read_request msg; struct msg_brg_dds_read_reply *rmsg; unsigned int r; msg.type = MESSAGE_TYPE_BRG_DDS_READ_REQUEST; + msg.bus_channel = bus_channel; msg.address = address; mailbox_send(&msg); while(1) { @@ -82,20 +85,22 @@ unsigned int brg_ddsread(unsigned int address) } } -void brg_ddswrite(unsigned int address, unsigned int data) +void brg_ddswrite(int bus_channel, unsigned int address, unsigned int data) { struct msg_brg_dds_write msg; msg.type = MESSAGE_TYPE_BRG_DDS_WRITE; + msg.bus_channel = bus_channel; msg.address = address; msg.data = data; mailbox_send_and_wait(&msg); } -void brg_ddsfud(void) +void brg_ddsfud(int bus_channel) { - struct msg_base msg; + struct msg_brg_dds_fud msg; msg.type = MESSAGE_TYPE_BRG_DDS_FUD; + msg.bus_channel = bus_channel; mailbox_send_and_wait(&msg); } diff --git a/artiq/runtime/bridge_ctl.h b/artiq/runtime/bridge_ctl.h index e6d2b7306..2929ee8f0 100644 --- a/artiq/runtime/bridge_ctl.h +++ b/artiq/runtime/bridge_ctl.h @@ -6,10 +6,10 @@ void brg_start(void); void brg_ttloe(int n, int value); void brg_ttlo(int n, int value); -void brg_ddssel(int channel); -void brg_ddsreset(void); -unsigned int brg_ddsread(unsigned int address); -void brg_ddswrite(unsigned int address, unsigned int data); -void brg_ddsfud(void); +void brg_ddssel(int bus_channel, int channel); +void brg_ddsreset(int bus_channel); +unsigned int brg_ddsread(int bus_channel, unsigned int address); +void brg_ddswrite(int bus_channel, unsigned int address, unsigned int data); +void brg_ddsfud(int bus_channel); #endif /* __BRIDGE_CTL_H */ diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index 3f8e728c1..b9b07bde7 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -26,11 +26,11 @@ #endif #define DDS_WRITE(addr, data) do { \ - rtio_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ + rtio_output(now, bus_channel, addr, data); \ now += DURATION_WRITE; \ } while(0) -void dds_init(long long int timestamp, int channel) +void dds_init(long long int timestamp, int bus_channel, int channel) { long long int now; @@ -82,17 +82,23 @@ void dds_init(long long int timestamp, int channel) /* Compensation to keep phase continuity when switching from absolute or tracking * to continuous phase mode. */ -static unsigned int continuous_phase_comp[CONFIG_DDS_CHANNEL_COUNT]; +static unsigned int continuous_phase_comp[CONFIG_RTIO_DDS_COUNT][CONFIG_DDS_CHANNELS_PER_BUS]; -static void dds_set_one(long long int now, long long int ref_time, unsigned int channel, +static void dds_set_one(long long int now, long long int ref_time, + int bus_channel, int channel, unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude) { unsigned int channel_enc; - if(channel >= CONFIG_DDS_CHANNEL_COUNT) { + if((channel < 0) || (channel >= CONFIG_DDS_CHANNELS_PER_BUS)) { core_log("Attempted to set invalid DDS channel\n"); return; } + if((bus_channel < CONFIG_RTIO_FIRST_DDS_CHANNEL) + || (bus_channel >= (CONFIG_RTIO_FIRST_DDS_CHANNEL+CONFIG_RTIO_DDS_COUNT))) { + core_log("Attempted to use invalid DDS bus\n"); + return; + } #ifdef CONFIG_DDS_ONEHOT_SEL channel_enc = 1 << channel; #else @@ -124,7 +130,7 @@ static void dds_set_one(long long int now, long long int ref_time, unsigned int /* Disable autoclear phase accumulator and enables OSK. */ DDS_WRITE(DDS_CFR1L, 0x0108); #endif - pow += continuous_phase_comp[channel]; + pow += continuous_phase_comp[bus_channel-CONFIG_RTIO_FIRST_DDS_CHANNEL][channel]; } else { long long int fud_time; @@ -140,7 +146,7 @@ static void dds_set_one(long long int now, long long int ref_time, unsigned int pow -= (ref_time - fud_time)*CONFIG_DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH); if(phase_mode == PHASE_MODE_TRACKING) pow += ref_time*CONFIG_DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH); - continuous_phase_comp[channel] = pow; + continuous_phase_comp[bus_channel-CONFIG_RTIO_FIRST_DDS_CHANNEL][channel] = pow; } #ifdef CONFIG_DDS_AD9858 @@ -157,6 +163,7 @@ static void dds_set_one(long long int now, long long int ref_time, unsigned int } struct dds_set_params { + int bus_channel; int channel; unsigned int ftw; unsigned int pow; @@ -189,20 +196,22 @@ void dds_batch_exit(void) now = batch_ref_time - batch_count*(DURATION_PROGRAM + DURATION_WRITE); for(i=0;i= DDS_MAX_BATCH) artiq_raise_from_c("DDSBatchError", "DDS batch error", 0, 0, 0); /* timestamp parameter ignored (determined by batch) */ + batch[batch_count].bus_channel = bus_channel; batch[batch_count].channel = channel; batch[batch_count].ftw = ftw; batch[batch_count].pow = pow; @@ -210,7 +219,8 @@ void dds_set(long long int timestamp, int channel, batch[batch_count].amplitude = amplitude; batch_count++; } else { - dds_set_one(timestamp - DURATION_PROGRAM, timestamp, channel, ftw, pow, phase_mode, - amplitude); + dds_set_one(timestamp - DURATION_PROGRAM, timestamp, + bus_channel, channel, + ftw, pow, phase_mode, amplitude); } } diff --git a/artiq/runtime/dds.h b/artiq/runtime/dds.h index 6d4e5d972..10fa3fffc 100644 --- a/artiq/runtime/dds.h +++ b/artiq/runtime/dds.h @@ -54,10 +54,10 @@ enum { PHASE_MODE_TRACKING = 2 }; -void dds_init(long long int timestamp, int channel); +void dds_init(long long int timestamp, int bus_channel, int channel); void dds_batch_enter(long long int timestamp); void dds_batch_exit(void); -void dds_set(long long int timestamp, int channel, +void dds_set(long long int timestamp, int bus_channel, int channel, unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude); #endif /* __DDS_H */ diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index bd2adc912..49df62319 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -150,23 +150,37 @@ struct msg_brg_ttl_out { struct msg_brg_dds_sel { int type; + int bus_channel; int channel; }; +struct msg_brg_dds_reset { + int type; + int bus_channel; +}; + struct msg_brg_dds_read_request { int type; + int bus_channel; unsigned int address; }; struct msg_brg_dds_read_reply { int type; + int bus_channel; unsigned int data; }; struct msg_brg_dds_write { int type; + int bus_channel; unsigned int address; unsigned int data; }; +struct msg_brg_dds_fud { + int type; + int bus_channel; +}; + #endif /* __MESSAGES_H */ diff --git a/artiq/runtime/moninj.c b/artiq/runtime/moninj.c index aea5bfd69..4a7c14205 100644 --- a/artiq/runtime/moninj.c +++ b/artiq/runtime/moninj.c @@ -36,13 +36,15 @@ struct monitor_reply { long long int ttl_levels; long long int ttl_oes; long long int ttl_overrides; - unsigned int dds_ftws[CONFIG_DDS_CHANNEL_COUNT]; -}; + unsigned short int dds_rtio_first_channel; + unsigned short int dds_channels_per_bus; + unsigned int dds_ftws[CONFIG_RTIO_DDS_COUNT*CONFIG_DDS_CHANNELS_PER_BUS]; +} __attribute__((packed)); static void moninj_monitor(const ip_addr_t *addr, u16_t port) { struct monitor_reply reply; - int i; + int i, j; struct pbuf *reply_p; reply.ttl_levels = 0; @@ -64,11 +66,15 @@ static void moninj_monitor(const ip_addr_t *addr, u16_t port) reply.ttl_overrides |= 1LL << i; } - rtio_moninj_mon_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); - for(i=0;i\n"); + return; + } + + n2 = strtoul(n, &c, 0); + if(*c != 0) { + printf("incorrect bus channel\n"); + return; + } + + bus_channel = n2; +} + + static void ddssel(char *n) { char *c; @@ -123,7 +145,7 @@ static void ddssel(char *n) #ifdef CONFIG_DDS_ONEHOT_SEL n2 = 1 << n2; #endif - brg_ddssel(n2); + brg_ddssel(bus_channel, n2); } static void ddsw(char *addr, char *value) @@ -147,7 +169,7 @@ static void ddsw(char *addr, char *value) return; } - brg_ddswrite(addr2, value2); + brg_ddswrite(bus_channel, addr2, value2); } static void ddsr(char *addr) @@ -167,16 +189,16 @@ static void ddsr(char *addr) } #ifdef CONFIG_DDS_AD9858 - printf("0x%02x\n", brg_ddsread(addr2)); + printf("0x%02x\n", brg_ddsread(bus_channel, addr2)); #endif #ifdef CONFIG_DDS_AD9914 - printf("0x%04x\n", brg_ddsread(addr2)); + printf("0x%04x\n", brg_ddsread(bus_channel, addr2)); #endif } static void ddsfud(void) { - brg_ddsfud(); + brg_ddsfud(bus_channel); } static void ddsftw(char *n, char *ftw) @@ -203,36 +225,36 @@ static void ddsftw(char *n, char *ftw) #ifdef CONFIG_DDS_ONEHOT_SEL n2 = 1 << n2; #endif - brg_ddssel(n2); + brg_ddssel(bus_channel, n2); #ifdef CONFIG_DDS_AD9858 - brg_ddswrite(DDS_FTW0, ftw2 & 0xff); - brg_ddswrite(DDS_FTW1, (ftw2 >> 8) & 0xff); - brg_ddswrite(DDS_FTW2, (ftw2 >> 16) & 0xff); - brg_ddswrite(DDS_FTW3, (ftw2 >> 24) & 0xff); + brg_ddswrite(bus_channel, DDS_FTW0, ftw2 & 0xff); + brg_ddswrite(bus_channel, DDS_FTW1, (ftw2 >> 8) & 0xff); + brg_ddswrite(bus_channel, DDS_FTW2, (ftw2 >> 16) & 0xff); + brg_ddswrite(bus_channel, DDS_FTW3, (ftw2 >> 24) & 0xff); #endif #ifdef CONFIG_DDS_AD9914 - brg_ddswrite(DDS_FTWL, ftw2 & 0xffff); - brg_ddswrite(DDS_FTWH, (ftw2 >> 16) & 0xffff); + brg_ddswrite(bus_channel, DDS_FTWL, ftw2 & 0xffff); + brg_ddswrite(bus_channel, DDS_FTWH, (ftw2 >> 16) & 0xffff); #endif - brg_ddsfud(); + brg_ddsfud(bus_channel); } static void ddsreset(void) { - brg_ddsreset(); + brg_ddsreset(bus_channel); } #ifdef CONFIG_DDS_AD9858 static void ddsinit(void) { - brg_ddsreset(); - brg_ddswrite(DDS_CFR0, 0x78); - brg_ddswrite(DDS_CFR1, 0x00); - brg_ddswrite(DDS_CFR2, 0x00); - brg_ddswrite(DDS_CFR3, 0x00); - brg_ddsfud(); + brg_ddsreset(bus_channel); + brg_ddswrite(bus_channel, DDS_CFR0, 0x78); + brg_ddswrite(bus_channel, DDS_CFR1, 0x00); + brg_ddswrite(bus_channel, DDS_CFR2, 0x00); + brg_ddswrite(bus_channel, DDS_CFR3, 0x00); + brg_ddsfud(bus_channel); } #endif @@ -241,17 +263,17 @@ static void ddsinit(void) { long long int t; - brg_ddsreset(); - brg_ddswrite(DDS_CFR1H, 0x0000); /* Enable cosine output */ - brg_ddswrite(DDS_CFR2L, 0x8900); /* Enable matched latency */ - brg_ddswrite(DDS_CFR2H, 0x0080); /* Enable profile mode */ - brg_ddswrite(DDS_ASF, 0x0fff); /* Set amplitude to maximum */ - brg_ddswrite(DDS_CFR4H, 0x0105); /* Enable DAC calibration */ - brg_ddswrite(DDS_FUD, 0); + brg_ddsreset(bus_channel); + brg_ddswrite(bus_channel, DDS_CFR1H, 0x0000); /* Enable cosine output */ + brg_ddswrite(bus_channel, DDS_CFR2L, 0x8900); /* Enable matched latency */ + brg_ddswrite(bus_channel, DDS_CFR2H, 0x0080); /* Enable profile mode */ + brg_ddswrite(bus_channel, DDS_ASF, 0x0fff); /* Set amplitude to maximum */ + brg_ddswrite(bus_channel, DDS_CFR4H, 0x0105); /* Enable DAC calibration */ + brg_ddswrite(bus_channel, DDS_FUD, 0); t = clock_get_ms(); while(clock_get_ms() < t + 2); - brg_ddswrite(DDS_CFR4H, 0x0005); /* Disable DAC calibration */ - brg_ddsfud(); + brg_ddswrite(bus_channel, DDS_CFR4H, 0x0005); /* Disable DAC calibration */ + brg_ddsfud(bus_channel); } #endif @@ -265,34 +287,34 @@ static void do_ddstest_one(unsigned int i) unsigned int f, g, j; #ifdef CONFIG_DDS_ONEHOT_SEL - brg_ddssel(1 << i); + brg_ddssel(bus_channel, 1 << i); #else - brg_ddssel(i); + brg_ddssel(bus_channel, i); #endif ddsinit(); for(j=0; j<12; j++) { f = v[j]; #ifdef CONFIG_DDS_AD9858 - brg_ddswrite(DDS_FTW0, f & 0xff); - brg_ddswrite(DDS_FTW1, (f >> 8) & 0xff); - brg_ddswrite(DDS_FTW2, (f >> 16) & 0xff); - brg_ddswrite(DDS_FTW3, (f >> 24) & 0xff); + brg_ddswrite(bus_channel, DDS_FTW0, f & 0xff); + brg_ddswrite(bus_channel, DDS_FTW1, (f >> 8) & 0xff); + brg_ddswrite(bus_channel, DDS_FTW2, (f >> 16) & 0xff); + brg_ddswrite(bus_channel, DDS_FTW3, (f >> 24) & 0xff); #endif #ifdef CONFIG_DDS_AD9914 - brg_ddswrite(DDS_FTWL, f & 0xffff); - brg_ddswrite(DDS_FTWH, (f >> 16) & 0xffff); + brg_ddswrite(bus_channel, DDS_FTWL, f & 0xffff); + brg_ddswrite(bus_channel, DDS_FTWH, (f >> 16) & 0xffff); #endif - brg_ddsfud(); + brg_ddsfud(bus_channel); #ifdef CONFIG_DDS_AD9858 - g = brg_ddsread(DDS_FTW0); - g |= brg_ddsread(DDS_FTW1) << 8; - g |= brg_ddsread(DDS_FTW2) << 16; - g |= brg_ddsread(DDS_FTW3) << 24; + g = brg_ddsread(bus_channel, DDS_FTW0); + g |= brg_ddsread(bus_channel, DDS_FTW1) << 8; + g |= brg_ddsread(bus_channel, DDS_FTW2) << 16; + g |= brg_ddsread(bus_channel, DDS_FTW3) << 24; #endif #ifdef CONFIG_DDS_AD9914 - g = brg_ddsread(DDS_FTWL); - g |= brg_ddsread(DDS_FTWH) << 16; + g = brg_ddsread(bus_channel, DDS_FTWL); + g |= brg_ddsread(bus_channel, DDS_FTWH) << 16; #endif if(g != f) printf("readback fail on DDS %d, 0x%08x != 0x%08x\n", i, g, f); @@ -330,7 +352,7 @@ static void ddstest(char *n, char *channel) do_ddstest_one(channel2); } else { for(i=0;i - select RTIO clock source"); puts("ttloe - set TTL output enable"); puts("ttlo - set TTL output value"); + puts("ddsbus - select the DDS bus RTIO channel"); puts("ddssel - select a DDS"); puts("ddsinit - reset, config, FUD DDS"); puts("ddsreset - reset DDS"); @@ -624,6 +647,7 @@ static void do_command(char *c) else if(strcmp(token, "ttloe") == 0) ttloe(get_token(&c), get_token(&c)); else if(strcmp(token, "ttlo") == 0) ttlo(get_token(&c), get_token(&c)); + else if(strcmp(token, "ddsbus") == 0) ddsbus(get_token(&c)); else if(strcmp(token, "ddssel") == 0) ddssel(get_token(&c)); else if(strcmp(token, "ddsw") == 0) ddsw(get_token(&c), get_token(&c)); else if(strcmp(token, "ddsr") == 0) ddsr(get_token(&c)); diff --git a/artiq/test/coredevice/test_cache.py b/artiq/test/coredevice/test_cache.py index a81950e00..717f68b34 100644 --- a/artiq/test/coredevice/test_cache.py +++ b/artiq/test/coredevice/test_cache.py @@ -5,21 +5,22 @@ from artiq.test.hardware_testbench import ExperimentCase class _Cache(EnvExperiment): def build(self): self.setattr_device("core") - self.print = lambda x: print(x) + self.setattr_device("core_cache") @kernel def get(self, key): - return self.core.get_cache(key) + return self.core_cache.get(key) @kernel def put(self, key, value): - self.core.put_cache(key, value) + self.core_cache.put(key, value) @kernel def get_put(self, key, value): self.get(key) self.put(key, value) + class CacheTest(ExperimentCase): def test_get_empty(self): exp = self.create(_Cache) diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index 8f5dcb48c..17caf7450 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -12,6 +12,10 @@ from artiq.test.hardware_testbench import ExperimentCase artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY") +class PulseNotReceived(Exception): + pass + + class RTT(EnvExperiment): def build(self): self.setattr_device("core") @@ -29,7 +33,10 @@ class RTT(EnvExperiment): delay(1*us) t0 = now_mu() self.ttl_inout.pulse(1*us) - self.set_dataset("rtt", mu_to_seconds(self.ttl_inout.timestamp_mu() - t0)) + t1 = self.ttl_inout.timestamp_mu() + if t1 < 0: + raise PulseNotReceived() + self.set_dataset("rtt", mu_to_seconds(t1 - t0)) class Loopback(EnvExperiment): @@ -41,6 +48,7 @@ class Loopback(EnvExperiment): @kernel def run(self): self.loop_in.input() + self.loop_out.off() delay(1*us) with parallel: self.loop_in.gate_rising(2*us) @@ -48,7 +56,10 @@ class Loopback(EnvExperiment): delay(1*us) t0 = now_mu() self.loop_out.pulse(1*us) - self.set_dataset("rtt", mu_to_seconds(self.loop_in.timestamp_mu() - t0)) + t1 = self.loop_in.timestamp_mu() + if t1 < 0: + raise PulseNotReceived() + self.set_dataset("rtt", mu_to_seconds(t1 - t0)) class ClockGeneratorLoopback(EnvExperiment): diff --git a/artiq/test/coredevice/test_spi.py b/artiq/test/coredevice/test_spi.py new file mode 100644 index 000000000..3686f532b --- /dev/null +++ b/artiq/test/coredevice/test_spi.py @@ -0,0 +1,83 @@ +from artiq.experiment import * +from artiq.test.hardware_testbench import ExperimentCase + + +class WrongError(Exception): + pass + + +class Collision(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("spi0") + + @kernel + def run(self): + self.core.break_realtime() + t = now_mu() + try: + self.spi0.set_config_mu() + except RTIOBusy: + raise WrongError() + at_mu(t) + self.spi0.set_config_mu() + + +class Busy(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("spi0") + self.setattr_device("led") + + @kernel + def run(self): + try: + self.core.break_realtime() + self.spi0.set_config_mu() + t = now_mu() + self.spi0.set_config_mu() + at_mu(t + self.spi0.ref_period_mu) + self.spi0.set_config_mu() # causes the error + self.led.on() + self.led.sync() # registers the error + self.core.break_realtime() + except RTIOBusy: + raise WrongError() # we don't expect RTIOBusy so far + self.spi0.set_config_mu() # raises the error + + +class DrainErrors(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("spi0") + self.setattr_device("led") + + @kernel + def run(self): + while True: + try: + self.core.break_realtime() + delay(100*us) + self.spi0.set_config_mu() + self.led.on() + self.led.sync() + self.core.break_realtime() + self.spi0.set_config_mu() + self.led.off() + return + except: + pass + + +class SPITest(ExperimentCase): + def tearDown(self): + self.execute(DrainErrors) + ExperimentCase.tearDown(self) + + def test_collision(self): + with self.assertRaises(RTIOCollision): + self.execute(Collision) + + def test_busy(self): + with self.assertRaises(RTIOBusy): + self.execute(Busy) diff --git a/artiq/test/hardware_testbench.py b/artiq/test/hardware_testbench.py index b8c1de6f8..890f7e0e8 100644 --- a/artiq/test/hardware_testbench.py +++ b/artiq/test/hardware_testbench.py @@ -107,9 +107,10 @@ class ExperimentCase(unittest.TestCase): return exp except KeyError as e: # skip if ddb does not match requirements - raise unittest.SkipTest(*e.args) + raise unittest.SkipTest( + "device_db entry `{}` not found".format(*e.args)) - def execute(self, cls, *args, **kwargs): + def execute(self, cls, **kwargs): expid = { "file": sys.modules[cls.__module__].__file__, "class_name": cls.__name__, @@ -124,3 +125,8 @@ class ExperimentCase(unittest.TestCase): except CompileError as error: # Reduce amount of text on terminal. raise error from None + except Exception as exn: + if hasattr(exn, "artiq_core_exception"): + exn.args = "{}\n{}".format(exn.args[0], + exn.artiq_core_exception), + raise exn diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index 8a947dab6..9489277a7 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -89,6 +89,8 @@ The board has RTIO SPI buses mapped as follows: | 25 | SPI2_CS_N | SPI2_MOSI | SPI2_MISO | SPI2_CLK | +--------------+-------------+-------------+-----------+------------+ +The DDS bus is on channel 26. + NIST QC2 ++++++++ @@ -132,15 +134,7 @@ When plugged to an adapter, the NIST QC1 hardware can be used. The TTL lines are +--------------+------------+--------------+ | 21 | USER_LED_4 | Output | +--------------+------------+--------------+ -| 22 | PMOD_4 | Input+Output | -+--------------+------------+--------------+ -| 23 | PMOD_5 | Input+Output | -+--------------+------------+--------------+ -| 24 | PMOD_6 | Input+Output | -+--------------+------------+--------------+ -| 25 | PMOD_7 | Input+Output | -+--------------+------------+--------------+ -| 26 | TTL15 | Clock | +| 22 | TTL15 | Clock | +--------------+------------+--------------+ The input only limitation on channels 0 and 1 comes from the QC-DAQ adapter. When the adapter is not used (and physically unplugged from the Pipistrello board), the corresponding pins on the Pipistrello can be used as outputs. Do not configure these channels as outputs when the adapter is plugged, as this would cause electrical contention. @@ -153,5 +147,5 @@ Interface Type 2 (SPI) and 2A (expanded SPI): +--------------+--------+--------+--------+--------+ | RTIO channel | CS_N | MOSI | MISO | CLK | +==============+========+========+========+========+ -| 27 | PMOD_0 | PMOD_1 | PMOD_2 | PMOD_3 | +| 23 | PMOD_0 | PMOD_1 | PMOD_2 | PMOD_3 | +--------------+--------+--------+--------+--------+ diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index e174930e4..9895b04f7 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -42,6 +42,12 @@ These drivers are for the core device and the peripherals closely integrated int .. automodule:: artiq.coredevice.i2c :members: +:mod:`artiq.coredevice.cache` module +----------------------------------------- + +.. automodule:: artiq.coredevice.cache + :members: + :mod:`artiq.coredevice.exceptions` module ----------------------------------------- diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index ee475911a..ebc75b1ff 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -1,5 +1,6 @@ # This is an example device database that needs to be adapted to your setup. # The RTIO channel numbers here are for NIST CLOCK on KC705. +# The list of devices here is not exhaustive. { "comm": { @@ -14,6 +15,17 @@ "class": "Core", "arguments": {"ref_period": 1e-9} }, + "core_cache": { + "type": "local", + "module": "artiq.coredevice.cache", + "class": "CoreCache" + }, + "core_dds": { + "type": "local", + "module": "artiq.coredevice.dds", + "class": "CoreDDS", + "arguments": {"sysclk": 3e9} + }, "i2c_switch": { "type": "local", @@ -121,30 +133,24 @@ "arguments": {"spi_device": "spi0", "ldac_device": "ttl0"} }, - "dds_bus": { - "type": "local", - "module": "artiq.coredevice.dds", - "class": "DDSBus", - "arguments": {} - }, "dds0": { "type": "local", "module": "artiq.coredevice.dds", "class": "AD9914", - "arguments": {"sysclk": 3e9, "channel": 0}, + "arguments": {"bus_channel": 26, "channel": 0}, "comment": "Comments work in DDS panel as well" }, "dds1": { "type": "local", "module": "artiq.coredevice.dds", "class": "AD9914", - "arguments": {"sysclk": 3e9, "channel": 1} + "arguments": {"bus_channel": 26, "channel": 1} }, "dds2": { "type": "local", "module": "artiq.coredevice.dds", "class": "AD9914", - "arguments": {"sysclk": 3e9, "channel": 2} + "arguments": {"bus_channel": 26, "channel": 2} }, "qc_q1_0": { @@ -154,25 +160,25 @@ # that it always resolves to a network-visible IP address (see documentation). "host": "::1", "port": 4000, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_0.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_0.bin" }, "qc_q1_1": { "type": "controller", "host": "::1", "port": 4001, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_1.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_1.bin" }, "qc_q1_2": { "type": "controller", "host": "::1", "port": 4002, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_2.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_2.bin" }, "qc_q1_3": { "type": "controller", "host": "::1", "port": 4003, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_3.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_3.bin" }, "electrodes": { "type": "local", diff --git a/examples/master/repository/coredevice_examples/photon_histogram.py b/examples/master/repository/coredevice_examples/photon_histogram.py index 307b3a52e..5a919aa4a 100644 --- a/examples/master/repository/coredevice_examples/photon_histogram.py +++ b/examples/master/repository/coredevice_examples/photon_histogram.py @@ -6,7 +6,7 @@ class PhotonHistogram(EnvExperiment): def build(self): self.setattr_device("core") - self.setattr_device("dds_bus") + self.setattr_device("core_dds") self.setattr_device("bd_dds") self.setattr_device("bd_sw") self.setattr_device("bdd_dds") @@ -22,7 +22,7 @@ class PhotonHistogram(EnvExperiment): @kernel def program_cooling(self): - with self.dds_bus.batch: + with self.core_dds.batch: self.bd_dds.set(200*MHz) self.bdd_dds.set(300*MHz) diff --git a/examples/master/repository/coredevice_examples/simple/dds_test.py b/examples/master/repository/coredevice_examples/simple/dds_test.py index a0b60bf73..5e262211a 100644 --- a/examples/master/repository/coredevice_examples/simple/dds_test.py +++ b/examples/master/repository/coredevice_examples/simple/dds_test.py @@ -6,7 +6,7 @@ class DDSTest(EnvExperiment): def build(self): self.setattr_device("core") - self.setattr_device("dds_bus") + self.setattr_device("core_dds") self.setattr_device("dds0") self.setattr_device("dds1") self.setattr_device("dds2") @@ -17,7 +17,7 @@ class DDSTest(EnvExperiment): @kernel def run(self): - with self.dds_bus.batch: + with self.core_dds.batch: self.dds1.set(120*MHz) self.dds2.set(200*MHz) delay(1*us)