diff --git a/artiq/coredevice/ad9914.py b/artiq/coredevice/ad9914.py index e892ba65d..bd2ec4db0 100644 --- a/artiq/coredevice/ad9914.py +++ b/artiq/coredevice/ad9914.py @@ -162,16 +162,16 @@ class AD9914: def set_phase_mode(self, phase_mode): """Sets the phase mode of the DDS channel. Supported phase modes are: - * ``PHASE_MODE_CONTINUOUS``: the phase accumulator is unchanged when + * :const:`PHASE_MODE_CONTINUOUS`: the phase accumulator is unchanged when switching frequencies. The DDS phase is the sum of the phase accumulator and the phase offset. The only discrete jumps in the DDS output phase come from changes to the phase offset. - * ``PHASE_MODE_ABSOLUTE``: the phase accumulator is reset when + * :const:`PHASE_MODE_ABSOLUTE`: the phase accumulator is reset when switching frequencies. Thus, the phase of the DDS at the time of the frequency change is equal to the phase offset. - * ``PHASE_MODE_TRACKING``: when switching frequencies, the phase + * :const:`PHASE_MODE_TRACKING`: when switching frequencies, the phase accumulator is set to the value it would have if the DDS had been running at the specified frequency since the start of the experiment. @@ -193,7 +193,7 @@ class AD9914: :param ftw: frequency to generate. :param pow: adds an offset to the phase. :param phase_mode: if specified, overrides the default phase mode set - by ``set_phase_mode`` for this call. + by :meth:`set_phase_mode` for this call. :param ref_time: reference time used to compute phase. Specifying this makes it easier to have a well-defined phase relationship between DDSes on the same bus that are updated at a similar time. @@ -270,7 +270,7 @@ class AD9914: @kernel def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT, amplitude=1.0): - """Like ``set_mu``, but uses Hz and turns.""" + """Like :meth:`set_mu`, but uses Hz and turns.""" self.set_mu(self.frequency_to_ftw(frequency), self.turns_to_pow(phase), phase_mode, self.amplitude_to_asf(amplitude)) @@ -323,7 +323,7 @@ class AD9914: @kernel def set_x(self, frequency, amplitude=1.0): - """Like ``set_x_mu``, but uses Hz and turns. + """Like :meth:`set_x_mu`, but uses Hz and turns. Note that the precision of ``float`` is less than the precision of the extended frequency tuning word. diff --git a/artiq/coredevice/dma.py b/artiq/coredevice/dma.py index eafe98e6c..261a6bcfe 100644 --- a/artiq/coredevice/dma.py +++ b/artiq/coredevice/dma.py @@ -34,7 +34,7 @@ def dma_playback(timestamp: TInt64, ptr: TInt32) -> TNone: class DMARecordContextManager: - """Context manager returned by ``CoreDMA.record()``. + """Context manager returned by :meth:`CoreDMA.record()`. Upon entering, starts recording a DMA trace. All RTIO operations are redirected to a newly created DMA buffer after this call, and ``now`` diff --git a/artiq/coredevice/spi2.py b/artiq/coredevice/spi2.py index c8749b082..205251084 100644 --- a/artiq/coredevice/spi2.py +++ b/artiq/coredevice/spi2.py @@ -184,7 +184,7 @@ class SPIMaster: experiments and are known. This method is portable and can also be called from e.g. - ``__init__``. + :meth:`__init__`. :param div: SPI clock divider (see: :meth:`set_config_mu`) :param length: SPI transfer length (see: :meth:`set_config_mu`) diff --git a/artiq/coredevice/suservo.py b/artiq/coredevice/suservo.py index 58e15a9d0..850cb98e4 100644 --- a/artiq/coredevice/suservo.py +++ b/artiq/coredevice/suservo.py @@ -253,7 +253,7 @@ class Channel: This method does not advance the timeline. Output RF switch setting takes effect immediately and is independent of any other activity (profile settings, other channels). The RF switch behaves like - ``TTLOut``. RTIO event replacement is supported. IIR updates take place + :class:`artiq.coredevice.ttl.TTLOut`. RTIO event replacement is supported. IIR updates take place once the RF switch has been enabled for the configured delay and the profile setting has been stable. Profile changes take between one and two servo cycles to reach the DDS. diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 76b443eb7..09732fccc 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -2,8 +2,8 @@ Drivers for TTL signals on RTIO. TTL channels (including the clock generator) all support output event -replacement. For example, pulses of "zero" length (e.g. ``on()`` -immediately followed by ``off()``, without a delay) are suppressed. +replacement. For example, pulses of "zero" length (e.g. :meth:`TTLInOut.on` +immediately followed by :meth:`TTLInOut.off`, without a delay) are suppressed. """ import numpy @@ -107,8 +107,8 @@ class TTLInOut: This should be used with bidirectional channels. Note that the channel is in input mode by default. If you need to drive a - signal, you must call ``output``. If the channel is in output mode most of - the time in your setup, it is a good idea to call ``output`` in the + signal, you must call :meth:`output`. If the channel is in output mode most of + the time in your setup, it is a good idea to call :meth:`output` in the startup kernel. There are three input APIs: gating, sampling and watching. When one @@ -301,10 +301,10 @@ class TTLInOut: @kernel def sample_get(self): """Returns the value of a sample previously obtained with - ``sample_input``. + :meth:`sample_input`. Multiple samples may be queued (using multiple calls to - ``sample_input``) into the RTIO FIFOs and subsequently read out using + :meth:`sample_input`) into the RTIO FIFOs and subsequently read out using multiple calls to this function. This function does not interact with the time cursor.""" @@ -324,11 +324,11 @@ class TTLInOut: @kernel def watch_stay_on(self): """Checks that the input is at a high level at the position - of the time cursor and keep checking until ``watch_done`` + of the time cursor and keep checking until :meth:`watch_done` is called. Returns ``True`` if the input is high. A call to this function - must always be followed by an eventual call to ``watch_done`` + must always be followed by an eventual call to :meth:`watch_done` (use e.g. a try/finally construct to ensure this). The time cursor is not modified by this function. @@ -338,7 +338,7 @@ class TTLInOut: @kernel def watch_stay_off(self): - """Like ``watch_stay_on``, but for low levels.""" + """Like :meth:`watch_stay_on`, but for low levels.""" rtio_output(now_mu(), self.channel, 3, 1) # gate rising return rtio_input_data(self.channel) == 0 @@ -419,7 +419,7 @@ class TTLClockGen: @kernel def set(self, frequency): - """Like ``set_mu``, but using Hz.""" + """Like :meth:`set_mu`, but using Hz.""" self.set_mu(self.frequency_to_ftw(frequency)) @kernel diff --git a/artiq/dashboard/moninj.py b/artiq/dashboard/moninj.py index bbd0b9a3e..8502667f3 100644 --- a/artiq/dashboard/moninj.py +++ b/artiq/dashboard/moninj.py @@ -74,6 +74,7 @@ class _TTLWidget(QtWidgets.QFrame): self.cur_level = False self.cur_oe = False self.cur_override = False + self.cur_override_level = False self.refresh_display() def enterEvent(self, event): @@ -106,7 +107,9 @@ class _TTLWidget(QtWidgets.QFrame): self.set_mode(self.channel, "0") def refresh_display(self): - value_s = "1" if self.cur_level else "0" + level = self.cur_override_level if self.cur_override else self.cur_level + value_s = "1" if level else "0" + if self.cur_override: value_s = "" + value_s + "" color = " color=\"red\"" @@ -377,7 +380,7 @@ class _DeviceManager: if override == TTLOverride.en.value: widget.cur_override = bool(value) if override == TTLOverride.level.value: - widget.cur_level = bool(value) + widget.cur_override_level = bool(value) widget.refresh_display() async def core_connector(self): diff --git a/artiq/examples/kasli_basic/device_db_mitll.py b/artiq/examples/kasli_basic/device_db_mitll.py index ea4402455..66a757176 100644 --- a/artiq/examples/kasli_basic/device_db_mitll.py +++ b/artiq/examples/kasli_basic/device_db_mitll.py @@ -139,6 +139,13 @@ for i in range(2): } } +device_db["grabber0"] = { + "type": "local", + "module": "artiq.coredevice.grabber", + "class": "Grabber", + "arguments": {"channel_base": 20} +} + device_db.update( led0={ "type": "local", diff --git a/artiq/examples/kasli_basic/device_db_mitll2.py b/artiq/examples/kasli_basic/device_db_mitll2.py new file mode 100644 index 000000000..dcd003c23 --- /dev/null +++ b/artiq/examples/kasli_basic/device_db_mitll2.py @@ -0,0 +1,225 @@ +core_addr = "kasli-1.lab.m-labs.hk" + +device_db = { + "core": { + "type": "local", + "module": "artiq.coredevice.core", + "class": "Core", + "arguments": {"host": core_addr, "ref_period": 1e-9} + }, + "core_log": { + "type": "controller", + "host": "::1", + "port": 1068, + "command": "aqctl_corelog -p {port} --bind {bind} " + core_addr + }, + "core_cache": { + "type": "local", + "module": "artiq.coredevice.cache", + "class": "CoreCache" + }, + "core_dma": { + "type": "local", + "module": "artiq.coredevice.dma", + "class": "CoreDMA" + }, + + "i2c_switch0": { + "type": "local", + "module": "artiq.coredevice.i2c", + "class": "PCA9548", + "arguments": {"address": 0xe0} + }, + "i2c_switch1": { + "type": "local", + "module": "artiq.coredevice.i2c", + "class": "PCA9548", + "arguments": {"address": 0xe2} + }, +} + +for i in range(8): + device_db["ttl" + str(i)] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLInOut" if i < 4 else "TTLOut", + "arguments": {"channel": i}, + } + +device_db.update( + spi_urukul0={ + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 8} + }, + ttl_urukul0_io_update={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 9} + }, + ttl_urukul0_sw0={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 10} + }, + ttl_urukul0_sw1={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 11} + }, + ttl_urukul0_sw2={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 12} + }, + ttl_urukul0_sw3={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 13} + }, + urukul0_cpld={ + "type": "local", + "module": "artiq.coredevice.urukul", + "class": "CPLD", + "arguments": { + "spi_device": "spi_urukul0", + "io_update_device": "ttl_urukul0_io_update", + "refclk": 125e6, + "clk_sel": 0 + } + } +) + +for i in range(4): + device_db["urukul0_ch" + str(i)] = { + "type": "local", + "module": "artiq.coredevice.ad9910", + "class": "AD9910", + "arguments": { + "pll_n": 32, + "chip_select": 4 + i, + "cpld_device": "urukul0_cpld", + "sw_device": "ttl_urukul0_sw" + str(i) + } + } + +device_db.update( + spi_urukul1={ + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 14} + }, + ttl_urukul1_io_update={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 15} + }, + ttl_urukul1_sw0={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 16} + }, + ttl_urukul1_sw1={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 17} + }, + ttl_urukul1_sw2={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 18} + }, + ttl_urukul1_sw3={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 19} + }, + urukul1_cpld={ + "type": "local", + "module": "artiq.coredevice.urukul", + "class": "CPLD", + "arguments": { + "spi_device": "spi_urukul1", + "io_update_device": "ttl_urukul1_io_update", + "refclk": 125e6, + "clk_sel": 0 + } + } +) + +for i in range(4): + device_db["urukul1_ch" + str(i)] = { + "type": "local", + "module": "artiq.coredevice.ad9910", + "class": "AD9910", + "arguments": { + "pll_n": 32, + "chip_select": 4 + i, + "cpld_device": "urukul1_cpld", + "sw_device": "ttl_urukul1_sw" + str(i) + } + } + +for i in range(2): + device_db["spi_zotino{}".format(i)] = { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 20+3*i+0} + } + device_db["ttl_zotino{}_ldac".format(i)] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 20+3*i+1} + } + device_db["ttl_zotino{}_clr".format(i)] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 20+3*i+2} + } + device_db["zotino{}".format(i)] = { + "type": "local", + "module": "artiq.coredevice.zotino", + "class": "Zotino", + "arguments": { + "spi_device": "spi_zotino{}".format(i), + "ldac_device": "ttl_zotino{}_ldac".format(i), + "clr_device": "ttl_zotino{}_clr".format(i) + } + } + +device_db["grabber0"] = { + "type": "local", + "module": "artiq.coredevice.grabber", + "class": "Grabber", + "arguments": {"channel_base": 26} +} + +device_db.update( + led0={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 28} + }, + led1={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 29} + }, +) diff --git a/artiq/examples/kasli_basic/device_db_tsinghua.py b/artiq/examples/kasli_basic/device_db_tsinghua.py index 9c9cb979c..9c20b8c83 100644 --- a/artiq/examples/kasli_basic/device_db_tsinghua.py +++ b/artiq/examples/kasli_basic/device_db_tsinghua.py @@ -171,6 +171,13 @@ device_db["zotino0"] = { } } +device_db["grabber0"] = { + "type": "local", + "module": "artiq.coredevice.grabber", + "class": "Grabber", + "arguments": {"channel_base": 20} +} + device_db.update( led0={ "type": "local", diff --git a/artiq/examples/kasli_basic/device_db_ustc.py b/artiq/examples/kasli_basic/device_db_ustc.py index 1c2810042..4828b18d0 100644 --- a/artiq/examples/kasli_basic/device_db_ustc.py +++ b/artiq/examples/kasli_basic/device_db_ustc.py @@ -173,6 +173,13 @@ for i in range(4): } } +device_db["grabber0"] = { + "type": "local", + "module": "artiq.coredevice.grabber", + "class": "Grabber", + "arguments": {"channel_base": 36} +} + device_db.update( led0={ "type": "local", diff --git a/artiq/examples/kasli_basic/repository/kasli_tester.py b/artiq/examples/kasli_basic/repository/kasli_tester.py index 685271217..475925db2 100644 --- a/artiq/examples/kasli_basic/repository/kasli_tester.py +++ b/artiq/examples/kasli_basic/repository/kasli_tester.py @@ -156,7 +156,9 @@ class KasliTester(EnvExperiment): def test_ttl_ins(self): print("*** Testing TTL inputs.") - + if not self.ttl_outs: + print("No TTL output channel available to use as stimulus.") + return ttl_out_name, ttl_out_dev = next(iter(self.ttl_outs)) for ttl_in_name, ttl_in_dev in self.ttl_ins: print("Connect {} to {}. Press ENTER when done." @@ -277,23 +279,10 @@ class KasliTester(EnvExperiment): print("Press ENTER when done.") input() - def test_grabbers(self): - rois = [[0, 0, 0, 2, 2], [1, 0, 0, 2048, 2048]] - print("*** Testing Grabber Frame Grabbers.") - print("ROIs: %s".format(rois)) - print("Press ENTER, activate the camera's frame grabber output, " - "and trigger it once. Type 's' to skip the test.") - if input().strip().lower() == "s": - print("skipping...") - return - for card_n, (card_name, card_dev) in enumerate(self.grabbers): - print(card_name) - self.grabber_capture(card_dev, rois) - @kernel def grabber_capture(self, card_dev, rois): self.core.break_realtime() - delay(10*us) + delay(100*us) mask = 0 for i in range(len(rois)): i = rois[i][0] @@ -308,8 +297,21 @@ class KasliTester(EnvExperiment): card_dev.input_mu(n) self.core.break_realtime() card_dev.gate_roi(0) - print("ROI sums:") - print(n) + print("ROI sums: {}".format(n)) + + def test_grabbers(self): + print("*** Testing Grabber Frame Grabbers.") + print("Activate the camera's frame grabber output, type 'g', press " + "ENTER, and trigger the camera.") + print("Just press ENTER to skip the test.") + if input().strip().lower() != "g": + print("skipping...") + return + rois = [[0, 0, 0, 2, 2], [1, 0, 0, 2048, 2048]] + print("ROIs: {}".format(rois)) + for card_n, (card_name, card_dev) in enumerate(self.grabbers): + print(card_name) + self.grabber_capture(card_dev, rois) def run(self): print("****** Kasli system tester ******") diff --git a/artiq/examples/kasli_tester/device_db.py b/artiq/examples/kasli_tester/device_db.py index f6cba7fea..e9779af5d 100644 --- a/artiq/examples/kasli_tester/device_db.py +++ b/artiq/examples/kasli_tester/device_db.py @@ -39,6 +39,7 @@ device_db = { } +# DIO (EEM5) starting at RTIO channel 0 for i in range(8): device_db["ttl" + str(i)] = { "type": "local", @@ -48,6 +49,7 @@ for i in range(8): } +# Urukul (EEM1) starting at RTIO channel 8 device_db.update( spi_urukul0={ "type": "local", @@ -112,6 +114,7 @@ for i in range(4): } +# Sampler (EEM3) starting at RTIO channel 14 device_db["spi_sampler0_adc"] = { "type": "local", "module": "artiq.coredevice.spi2", @@ -142,6 +145,7 @@ device_db["sampler0"] = { } +# Zotino (EEM4) starting at RTIO channel 17 device_db["spi_zotino0"] = { "type": "local", "module": "artiq.coredevice.spi2", @@ -171,18 +175,150 @@ device_db["zotino0"] = { } } + +# Grabber (EEM6) starting at RTIO channel 20 +device_db["grabber0"] = { + "type": "local", + "module": "artiq.coredevice.grabber", + "class": "Grabber", + "arguments": {"channel_base": 20} +} + + +# Urukul (EEM7) starting at RTIO channel 22 +device_db.update( + spi_urukul1={ + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 22} + }, + ttl_urukul1_io_update={ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 23} + }, + urukul1_cpld={ + "type": "local", + "module": "artiq.coredevice.urukul", + "class": "CPLD", + "arguments": { + "spi_device": "spi_urukul1", + "io_update_device": "ttl_urukul1_io_update", + "refclk": 100e6, + "clk_sel": 1 + } + } +) + +for i in range(4): + device_db["urukul1_ch" + str(i)] = { + "type": "local", + "module": "artiq.coredevice.ad9912", + "class": "AD9912", + "arguments": { + "pll_n": 10, + "chip_select": 4 + i, + "cpld_device": "urukul1_cpld" + } + } + + +# DIO (EEM8) starting at RTIO channel 24 +for i in range(8): + device_db["ttl" + str(8 + i)] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 24 + i}, + } + + +# DIO (EEM9) starting at RTIO channel 32 +for i in range(8): + device_db["ttl" + str(16 + i)] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 32 + i}, + } + + +# Sampler (EEM10) starting at RTIO channel 40 +device_db["spi_sampler1_adc"] = { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 40} +} +device_db["spi_sampler1_pgia"] = { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 41} +} +device_db["spi_sampler1_cnv"] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 42}, +} +device_db["sampler1"] = { + "type": "local", + "module": "artiq.coredevice.sampler", + "class": "Sampler", + "arguments": { + "spi_adc_device": "spi_sampler1_adc", + "spi_pgia_device": "spi_sampler1_pgia", + "cnv_device": "spi_sampler1_cnv" + } +} + + +# Zotino (EEM11) starting at RTIO channel 43 +device_db["spi_zotino1"] = { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {"channel": 43} +} +device_db["ttl_zotino1_ldac"] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 44} +} +device_db["ttl_zotino1_clr"] = { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 45} +} +device_db["zotino1"] = { + "type": "local", + "module": "artiq.coredevice.zotino", + "class": "Zotino", + "arguments": { + "spi_device": "spi_zotino1", + "ldac_device": "ttl_zotino1_ldac", + "clr_device": "ttl_zotino1_clr" + } +} + + device_db.update( led0={ "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 20} + "arguments": {"channel": 46} }, led1={ "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 21} + "arguments": {"channel": 47} }, ) diff --git a/artiq/firmware/libboard_artiq/grabber.rs b/artiq/firmware/libboard_artiq/grabber.rs index 2973dc2e9..ba5e2a4db 100644 --- a/artiq/firmware/libboard_artiq/grabber.rs +++ b/artiq/firmware/libboard_artiq/grabber.rs @@ -134,8 +134,12 @@ pub fn tick() { if clock_pattern_ok(g) { let last_xy = get_last_pixels(g); if last_xy != unsafe { INFO[g].frame_size } { + // x capture is on ~LVAL which is after + // the last increment on DVAL + // y capture is on ~FVAL which coincides with the + // last increment on ~LVAL info!("grabber{} frame size: {}x{}", - g, last_xy.0 + 1, last_xy.1 + 1); + g, last_xy.0, last_xy.1 + 1); unsafe { INFO[g].frame_size = last_xy } } State::Watch diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index b3e2bbb94..cbaeb1e26 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -272,9 +272,8 @@ fn startup_ethernet() { smoltcp::phy::EthernetTracer::new(net_device, net_trace_fn) }; - let mut neighbor_map = [None; 8]; let neighbor_cache = - smoltcp::iface::NeighborCache::new(&mut neighbor_map[..]); + smoltcp::iface::NeighborCache::new(alloc::btree_map::BTreeMap::new()); let mut interface = smoltcp::iface::EthernetInterfaceBuilder::new(net_device) .neighbor_cache(neighbor_cache) diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 89efeef68..a631af5a2 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -381,6 +381,19 @@ const SI5324_SETTINGS: si5324::FrequencySettings crystal_ref: true }; +#[cfg(rtio_frequency = "125.0")] +const SI5324_SETTINGS: si5324::FrequencySettings + = si5324::FrequencySettings { + n1_hs : 5, + nc1_ls : 8, + n2_hs : 7, + n2_ls : 360, + n31 : 63, + n32 : 63, + bwsel : 4, + crystal_ref: true +}; + const SIPHASER_PHASE: u16 = 32; #[no_mangle] diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index b8b2754bb..efcb06168 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -344,7 +344,7 @@ def main(): storage_img = args.storage programmer.write_binary(*config["storage"], storage_img) elif action == "firmware": - if variant == "satellite": + if variant.endswith("satellite"): firmware = "satman" else: firmware = "runtime" diff --git a/artiq/gateware/drtio/siphaser.py b/artiq/gateware/drtio/siphaser.py index aa2cbb05f..adbf72749 100644 --- a/artiq/gateware/drtio/siphaser.py +++ b/artiq/gateware/drtio/siphaser.py @@ -4,17 +4,20 @@ from migen.genlib.cdc import MultiReg from misoc.interconnect.csr import * -# This code assumes 125/62.5MHz reference clock and 150MHz RTIO frequency. +# This code assumes 125/62.5MHz reference clock and 125MHz or 150MHz RTIO +# frequency. class SiPhaser7Series(Module, AutoCSR): def __init__(self, si5324_clkin, si5324_clkout_fabric, - ref_clk=None, ref_div2=False): + ref_clk=None, ref_div2=False, rtio_clk_freq=150e6): self.switch_clocks = CSRStorage() self.phase_shift = CSR() self.phase_shift_done = CSRStatus(reset=1) self.sample_result = CSRStatus() - # 125MHz/62.5MHz reference clock to 150MHz. VCO @ 750MHz. + assert rtio_clk_freq in (125e6, 150e6) + + # 125MHz/62.5MHz reference clock to 125MHz/150MHz. VCO @ 750MHz. # Used to provide a startup clock to the transceiver through the Si, # we do not use the crystal reference so that the PFD (f3) frequency # can be high. @@ -31,18 +34,20 @@ class SiPhaser7Series(Module, AutoCSR): o_CLKFBOUT=mmcm_freerun_fb, i_CLKFBIN=mmcm_freerun_fb, - p_CLKOUT0_DIVIDE_F=5.0, o_CLKOUT0=mmcm_freerun_output, + p_CLKOUT0_DIVIDE_F=750e6/rtio_clk_freq, + o_CLKOUT0=mmcm_freerun_output, ) - - # 150MHz to 150MHz with controllable phase shift, VCO @ 1200MHz. - # Inserted between CDR and output to Si, used to correct + + # 125MHz/150MHz to 125MHz/150MHz with controllable phase shift, + # VCO @ 1000MHz/1200MHz. + # Inserted between CDR and output to Si, used to correct # non-determinstic skew of Si5324. mmcm_ps_fb = Signal() mmcm_ps_output = Signal() mmcm_ps_psdone = Signal() self.specials += \ Instance("MMCME2_ADV", - p_CLKIN1_PERIOD=1e9/150e6, + p_CLKIN1_PERIOD=1e9/rtio_clk_freq, i_CLKIN1=ClockSignal("rtio_rx0"), i_RST=ResetSignal("rtio_rx0"), i_CLKINSEL=1, # yes, 1=CLKIN1 0=CLKIN2 @@ -86,7 +91,7 @@ class SiPhaser7Series(Module, AutoCSR): p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="TRUE", i_I=si5324_clkout_fabric.p, i_IB=si5324_clkout_fabric.n, o_O=si5324_clkout_se), - + clkout_sample1 = Signal() # IOB register self.sync.rtio_rx0 += clkout_sample1.eq(si5324_clkout_se) self.specials += MultiReg(clkout_sample1, self.sample_result.status) diff --git a/artiq/gateware/grabber/core.py b/artiq/gateware/grabber/core.py index d9dac3d12..4baaf98c3 100644 --- a/artiq/gateware/grabber/core.py +++ b/artiq/gateware/grabber/core.py @@ -85,16 +85,20 @@ class Parser(Module, AutoCSR): self.sync.cl += [ last_lval.eq(lval), last_fval.eq(fval), - pix.x.eq(pix.x + 1), + If(dval, + pix.x.eq(pix.x + 1), + ), If(~lval, - pix.x.eq(0), - If(last_lval, last_x.eq(pix.x)), - If(last_fval & last_lval, + If(last_lval, + last_x.eq(pix.x), pix.y.eq(pix.y + 1) - ) + ), + pix.x.eq(0) ), If(~fval, - If(last_fval, last_y.eq(pix.y)), + If(last_fval, + last_y.eq(pix.y) + ), pix.y.eq(0) ) ] diff --git a/artiq/gateware/suservo/iir.py b/artiq/gateware/suservo/iir.py index 72501afae..ba00dd97e 100644 --- a/artiq/gateware/suservo/iir.py +++ b/artiq/gateware/suservo/iir.py @@ -104,7 +104,7 @@ class IIR(Module): will be abbreviated W here. It reads 1 << W.channels input channels (typically from an ADC) - and on each iteration processes the data on using a first-order IIR filter. + and on each iteration processes the data using a first-order IIR filter. At the end of the cycle each the output of the filter together with additional data (typically frequency tunning word and phase offset word for a DDS) are presented at the 1 << W.channels outputs of the module. diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 00c2d68b8..8fbaa0ec1 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -154,7 +154,7 @@ class Opticlock(_StandaloneBase): _StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) self.config["SI5324_AS_SYNTHESIZER"] = None - # self.config["SI5324_EXT_REF"] = None + self.config["SI5324_EXT_REF"] = None self.config["RTIO_FREQUENCY"] = "125.0" if hw_rev == "v1.0": # EEM clock fan-out from Si5324, not MMCX @@ -306,6 +306,45 @@ class MITLL(_StandaloneBase): self.rtio_crg.cd_rtio.clk, self.grabber0.deserializer.cd_cl.clk) +class MITLL2(_StandaloneBase): + def __init__(self, hw_rev=None, **kwargs): + if hw_rev is None: + hw_rev = "v1.1" + _StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) + + self.config["SI5324_AS_SYNTHESIZER"] = None + self.config["RTIO_FREQUENCY"] = "125.0" + if hw_rev == "v1.0": + # EEM clock fan-out from Si5324, not MMCX + self.comb += self.platform.request("clk_sel").eq(1) + + self.rtio_channels = [] + self.grabber_csr_group = [] + eem.DIO.add_std(self, 5, + ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 2, 1, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 4, 3, ttl_serdes_7series.Output_8X) + eem.Zotino.add_std(self, 6, ttl_serdes_7series.Output_8X) + eem.Zotino.add_std(self, 7, ttl_serdes_7series.Output_8X) + eem.Grabber.add_std(self, 0) + + for i in (1, 2): + sfp_ctl = self.platform.request("sfp_ctl", i) + phy = ttl_simple.Output(sfp_ctl.led) + self.submodules += phy + self.rtio_channels.append(rtio.Channel.from_phy(phy)) + + self.config["HAS_RTIO_LOG"] = None + self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) + self.rtio_channels.append(rtio.LogChannel()) + + self.add_rtio(self.rtio_channels) + self.config["HAS_GRABBER"] = None + self.add_csr_group("grabber", self.grabber_csr_group) + self.platform.add_false_path_constraints( + self.rtio_crg.cd_rtio.clk, self.grabber0.deserializer.cd_cl.clk) + + class USTC(_StandaloneBase): def __init__(self, hw_rev=None, **kwargs): if hw_rev is None: @@ -573,11 +612,20 @@ class Tester(_StandaloneBase): self.comb += self.platform.request("clk_sel").eq(1) self.rtio_channels = [] + self.grabber_csr_group = [] eem.DIO.add_std(self, 5, ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X) eem.Urukul.add_std(self, 1, 0, ttl_serdes_7series.Output_8X) eem.Sampler.add_std(self, 3, 2, ttl_serdes_7series.Output_8X) eem.Zotino.add_std(self, 4, ttl_serdes_7series.Output_8X) + eem.Grabber.add_std(self, 6) + eem.Urukul.add_std(self, 7, None, ttl_serdes_7series.Output_8X) + eem.DIO.add_std(self, 8, + ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) + eem.DIO.add_std(self, 9, + ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) + eem.Sampler.add_std(self, 10, None, ttl_serdes_7series.Output_8X) + eem.Zotino.add_std(self, 11, ttl_serdes_7series.Output_8X) for i in (1, 2): sfp_ctl = self.platform.request("sfp_ctl", i) @@ -588,9 +636,13 @@ class Tester(_StandaloneBase): self.config["HAS_RTIO_LOG"] = None self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) self.rtio_channels.append(rtio.LogChannel()) - self.add_rtio(self.rtio_channels) + self.config["HAS_GRABBER"] = None + self.add_csr_group("grabber", self.grabber_csr_group) + self.platform.add_false_path_constraints( + self.rtio_crg.cd_rtio.clk, self.grabber0.deserializer.cd_cl.clk) + class _RTIOClockMultiplier(Module, AutoCSR): def __init__(self, rtio_clk_freq): @@ -633,7 +685,7 @@ class _MasterBase(MiniSoC, AMPSoC): } mem_map.update(MiniSoC.mem_map) - def __init__(self, **kwargs): + def __init__(self, rtio_clk_freq=150e6, **kwargs): MiniSoC.__init__(self, cpu_type="or1k", sdram_controller_type="minicon", @@ -645,7 +697,6 @@ class _MasterBase(MiniSoC, AMPSoC): add_identifier(self) platform = self.platform - rtio_clk_freq = 150e6 i2c = self.platform.request("i2c") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) @@ -783,7 +834,7 @@ class _SatelliteBase(BaseSoC): } mem_map.update(BaseSoC.mem_map) - def __init__(self, **kwargs): + def __init__(self, rtio_clk_freq=150e6, **kwargs): BaseSoC.__init__(self, cpu_type="or1k", sdram_controller_type="minicon", @@ -792,7 +843,6 @@ class _SatelliteBase(BaseSoC): add_identifier(self) platform = self.platform - rtio_clk_freq = 150e6 disable_si5324_ibuf = Signal(reset=1) disable_si5324_ibuf.attr.add("no_retiming") @@ -872,7 +922,8 @@ class _SatelliteBase(BaseSoC): self.submodules.siphaser = SiPhaser7Series( si5324_clkin=platform.request("si5324_clkin"), si5324_clkout_fabric=platform.request("si5324_clkout_fabric"), - ref_clk=self.crg.clk125_div2, ref_div2=True) + ref_clk=self.crg.clk125_div2, ref_div2=True, + rtio_clk_freq=rtio_clk_freq) platform.add_false_path_constraints( self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) self.csr_devices.append("siphaser") @@ -950,6 +1001,66 @@ class Satellite(_SatelliteBase): self.add_rtio(self.rtio_channels) +class VLBAIMaster(_MasterBase): + def __init__(self, hw_rev=None, *args, **kwargs): + if hw_rev is None: + hw_rev = "v1.1" + _MasterBase.__init__(self, rtio_clk_freq=125e6, hw_rev=hw_rev, *args, + **kwargs) + + self.rtio_channels = [] + eem.DIO.add_std(self, 0, + ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X) + eem.DIO.add_std(self, 1, + ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) + eem.DIO.add_std(self, 2, + ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) + eem.Sampler.add_std(self, 3, None, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 5, 4, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X) + + for i in (0, 1): + phy = ttl_simple.Output(self.platform.request("user_led", i)) + self.submodules += phy + self.rtio_channels.append(rtio.Channel.from_phy(phy)) + + eem.Zotino.add_std(self, 7, ttl_serdes_7series.Output_8X) + + self.config["HAS_RTIO_LOG"] = None + self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) + self.rtio_channels.append(rtio.LogChannel()) + + self.add_rtio(self.rtio_channels) + + +class VLBAISatellite(_SatelliteBase): + def __init__(self, hw_rev=None, *args, **kwargs): + if hw_rev is None: + hw_rev = "v1.1" + _SatelliteBase.__init__(self, rtio_clk_freq=125e6, hw_rev=hw_rev, + *args, **kwargs) + + self.rtio_channels = [] + eem.DIO.add_std(self, 0, + ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X) + eem.DIO.add_std(self, 1, + ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) + eem.DIO.add_std(self, 2, + ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) + eem.Sampler.add_std(self, 3, None, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 5, 4, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X) + + for i in (0, 1): + phy = ttl_simple.Output(self.platform.request("user_led", i)) + self.submodules += phy + self.rtio_channels.append(rtio.Channel.from_phy(phy)) + + eem.Zotino.add_std(self, 7, ttl_serdes_7series.Output_8X) + + self.add_rtio(self.rtio_channels) + + def main(): parser = argparse.ArgumentParser( description="ARTIQ device binary builder for Kasli systems") @@ -957,8 +1068,8 @@ def main(): soc_kasli_args(parser) parser.set_defaults(output_dir="artiq_kasli") variants = {cls.__name__.lower(): cls for cls in [ - Opticlock, SUServo, SYSU, MITLL, USTC, Tsinghua, WIPM, PTB, HUB, LUH, - Tester, Master, Satellite]} + Opticlock, SUServo, SYSU, MITLL, MITLL2, USTC, Tsinghua, WIPM, PTB, HUB, LUH, + VLBAIMaster, VLBAISatellite, Tester, Master, Satellite]} parser.add_argument("-V", "--variant", default="opticlock", help="variant: {} (default: %(default)s)".format( "/".join(sorted(variants.keys())))) diff --git a/artiq/language/core.py b/artiq/language/core.py index 47c1746f3..e6cd1a498 100644 --- a/artiq/language/core.py +++ b/artiq/language/core.py @@ -28,19 +28,20 @@ def kernel(arg=None, flags={}): This decorator marks an object's method for execution on the core device. - When a decorated method is called from the Python interpreter, the ``core`` + When a decorated method is called from the Python interpreter, the :attr:`core` attribute of the object is retrieved and used as core device driver. The core device driver will typically compile, transfer and run the method (kernel) on the device. When kernels call another method: - - if the method is a kernel for the same core device, is it compiled + + - if the method is a kernel for the same core device, it is compiled and sent in the same binary. Calls between kernels happen entirely on the device. - if the method is a regular Python method (not a kernel), it generates a remote procedure call (RPC) for execution on the host. - The decorator takes an optional parameter that defaults to ``core`` and + The decorator takes an optional parameter that defaults to :attr`core` and specifies the name of the attribute to use as core device driver. This decorator must be present in the global namespace of all modules using diff --git a/artiq/language/environment.py b/artiq/language/environment.py index a7d7ae55f..3bb33647d 100644 --- a/artiq/language/environment.py +++ b/artiq/language/environment.py @@ -230,7 +230,7 @@ class HasEnvironment: instead: when the repository is scanned to build the list of available experiments and when the dataset browser ``artiq_browser`` is used to open or run the analysis stage of an experiment. Do not - rely on being able to operate on devices or arguments in ``build()``. + rely on being able to operate on devices or arguments in :meth:`build`. Datasets are read-only in this method. @@ -277,7 +277,7 @@ class HasEnvironment: def setattr_device(self, key): """Sets a device driver as attribute. The names of the device driver - and of the attribute are the same. + and of the attribute are the same. The key is added to the instance's kernel invariants.""" setattr(self, key, self.get_device(key)) @@ -328,8 +328,10 @@ class HasEnvironment: By default, datasets obtained by this method are archived into the output HDF5 file of the experiment. If an archived dataset is requested more than one time (and therefore its value has potentially changed) or is - modified, a warning is emitted. Archival can be turned off by setting - the ``archive`` argument to ``False``. + modified, a warning is emitted. + + :param archive: Set to ``False`` to prevent archival together with the run's results. + Default is ``True`` """ try: return self.__dataset_mgr.get(key, archive) @@ -355,7 +357,7 @@ class Experiment: """Entry point for pre-computing data necessary for running the experiment. - Doing such computations outside of ``run`` enables more efficient + Doing such computations outside of :meth:`run` enables more efficient scheduling of multiple experiments that need to access the shared hardware during part of their execution. @@ -371,8 +373,8 @@ class Experiment: This method may interact with the hardware. - The experiment may call the scheduler's ``pause`` method while in - ``run``. + The experiment may call the scheduler's :meth:`pause` method while in + :meth:`run`. """ raise NotImplementedError @@ -382,7 +384,7 @@ class Experiment: This method may be overloaded by the user to implement the analysis phase of the experiment, for example fitting curves. - Splitting this phase from ``run`` enables tweaking the analysis + Splitting this phase from :meth:`run` enables tweaking the analysis algorithm on pre-existing data, and CPU-bound analyses to be run overlapped with the next experiment in a pipelined manner. @@ -392,13 +394,14 @@ class Experiment: class EnvExperiment(Experiment, HasEnvironment): - """Base class for top-level experiments that use the ``HasEnvironment`` - environment manager. + """Base class for top-level experiments that use the + :class:`~artiq.language.environment.HasEnvironment` environment manager. Most experiments should derive from this class.""" def prepare(self): - """The default prepare method calls prepare for all children, in the - order of instantiation, if the child has a prepare method.""" + """This default prepare method calls :meth:`~artiq.language.environment.Experiment.prepare` + for all children, in the order of instantiation, if the child has a + :meth:`~artiq.language.environment.Experiment.prepare` method.""" for child in self.children: if hasattr(child, "prepare"): child.prepare() diff --git a/artiq/protocols/asyncio_server.py b/artiq/protocols/asyncio_server.py index e7e300eed..4eef0374b 100644 --- a/artiq/protocols/asyncio_server.py +++ b/artiq/protocols/asyncio_server.py @@ -6,7 +6,8 @@ class AsyncioServer: """Generic TCP server based on asyncio. Users of this class must derive from it and define the - ``_handle_connection_cr`` method and coroutine. + :meth:`~artiq.protocols.asyncio_server.AsyncioServer._handle_connection_cr` + method/coroutine. """ def __init__(self): self._client_tasks = set() @@ -14,10 +15,10 @@ class AsyncioServer: async def start(self, host, port): """Starts the server. - The user must call ``stop`` to free resources properly after this - method completes successfully. + The user must call :meth:`stop` + to free resources properly after this method completes successfully. - This method is a `coroutine`. + This method is a *coroutine*. :param host: Bind address of the server (see ``asyncio.start_server`` from the Python standard library). @@ -48,3 +49,6 @@ class AsyncioServer: task = asyncio.ensure_future(self._handle_connection_cr(reader, writer)) self._client_tasks.add(task) task.add_done_callback(self._client_done) + + async def _handle_connection_cr(self, reader, writer): + raise NotImplementedError diff --git a/artiq/protocols/pc_rpc.py b/artiq/protocols/pc_rpc.py index 410cc8908..adecf25df 100644 --- a/artiq/protocols/pc_rpc.py +++ b/artiq/protocols/pc_rpc.py @@ -1,7 +1,7 @@ """ This module provides a remote procedure call (RPC) mechanism over sockets between conventional computers (PCs) running Python. It strives to be -transparent and uses ``artiq.protocols.pyon`` internally so that e.g. Numpy +transparent and uses :mod:`artiq.protocols.pyon` internally so that e.g. Numpy arrays can be easily used. Note that the server operates on copies of objects provided by the client, @@ -61,18 +61,18 @@ class Client: can be used as if they were local methods. For example, if the server provides method ``foo``, and ``c`` is a local - ``Client`` object, then the method can be called as: :: + :class:`.Client` object, then the method can be called as: :: result = c.foo(param1, param2) - The parameters and the result are automatically transferred with the + The parameters and the result are automatically transferred from the server. Only methods are supported. Attributes must be accessed by providing and using "get" and/or "set" methods on the server side. At object initialization, the connection to the remote server is - automatically attempted. The user must call ``close_rpc`` to + automatically attempted. The user must call :meth:`~artiq.protocols.pc_rpc.Client.close_rpc` to free resources properly after initialization completes successfully. :param host: Identifier of the server. The string can represent a @@ -81,11 +81,11 @@ class Client: :param port: TCP port to use. :param target_name: Target name to select. ``IncompatibleServer`` is raised if the target does not exist. - Use ``AutoTarget`` for automatic selection if the server has only one + Use :class:`.AutoTarget` for automatic selection if the server has only one target. Use ``None`` to skip selecting a target. The list of targets can then - be retrieved using ``get_rpc_id`` and then one can be selected later - using ``select_rpc_target``. + be retrieved using :meth:`~artiq.protocols.pc_rpc.Client.get_rpc_id` + and then one can be selected later using :meth:`~artiq.protocols.pc_rpc.Client.select_rpc_target`. :param timeout: Socket operation timeout. Use ``None`` for blocking (default), ``0`` for non-blocking, and a finite value to raise ``socket.timeout`` if an operation does not complete within the @@ -198,7 +198,7 @@ class AsyncioClient: async def connect_rpc(self, host, port, target_name): """Connects to the server. This cannot be done in __init__ because - this method is a coroutine. See ``Client`` for a description of the + this method is a coroutine. See :class:`artiq.protocols.pc_rpc.Client` for a description of the parameters.""" self.__reader, self.__writer = \ await asyncio.open_connection(host, port, limit=100*1024*1024) @@ -447,7 +447,8 @@ class _PrettyPrintCall: class Server(_AsyncioServer): """This class creates a TCP server that handles requests coming from - ``Client`` objects. + *Client* objects (whether :class:`.Client`, :class:`.BestEffortClient`, + or :class:`.AsyncioClient`). The server is designed using ``asyncio`` so that it can easily support multiple connections without the locking issues that arise in @@ -591,7 +592,7 @@ def simple_server_loop(targets, host, port, description=None): """Runs a server until an exception is raised (e.g. the user hits Ctrl-C) or termination is requested by a client. - See ``Server`` for a description of the parameters. + See :class:`artiq.protocols.pc_rpc.Server` for a description of the parameters. """ loop = asyncio.get_event_loop() try: diff --git a/artiq/protocols/remote_exec.py b/artiq/protocols/remote_exec.py index 0b7f2419c..ae9870c30 100644 --- a/artiq/protocols/remote_exec.py +++ b/artiq/protocols/remote_exec.py @@ -7,10 +7,10 @@ large amounts of data with it, and only exchange higher-level, processed data with the experiment (and over the network). Controllers with support for remote execution contain an additional target -that gives RPC access to instances of ``RemoteExecServer``. One such instance +that gives RPC access to instances of :class:`.RemoteExecServer`. One such instance is created per client (experiment) connection and manages one Python namespace in which the experiment can execute arbitrary code by calling the methods of -``RemoteExecServer``. +:class:`.RemoteExecServer`. The namespaces are initialized with the following global values: diff --git a/artiq/protocols/sync_struct.py b/artiq/protocols/sync_struct.py index 35b72ff75..ca48c5356 100644 --- a/artiq/protocols/sync_struct.py +++ b/artiq/protocols/sync_struct.py @@ -117,10 +117,10 @@ class Subscriber: class Notifier: """Encapsulates a structure whose changes need to be published. - All mutations to the structure must be made through the ``Notifier``. The + All mutations to the structure must be made through the :class:`.Notifier`. The original structure must only be accessed for reads. - In addition to the list methods below, the ``Notifier`` supports the index + In addition to the list methods below, the :class:`.Notifier` supports the index syntax for modification and deletion of elements. Modification of nested structures can be also done using the index syntax, for example: @@ -131,11 +131,11 @@ class Notifier: [[42]] This class does not perform any network I/O and is meant to be used with - e.g. the ``Publisher`` for this purpose. Only one publisher at most can be - associated with a ``Notifier``. + e.g. the :class:`.Publisher` for this purpose. Only one publisher at most can be + associated with a :class:`.Notifier`. :param backing_struct: Structure to encapsulate. For convenience, it - also becomes available as the ``read`` property of the ``Notifier``. + also becomes available as the ``read`` property of the :class:`.Notifier`. """ def __init__(self, backing_struct, root=None, path=[]): self.read = backing_struct @@ -168,7 +168,7 @@ class Notifier: def pop(self, i=-1): """Pop an element from a list. The returned element is not - encapsulated in a ``Notifier`` and its mutations are no longer + encapsulated in a :class:`.Notifier` and its mutations are no longer tracked.""" r = self._backing_struct.pop(i) if self.root.publish is not None: @@ -199,11 +199,11 @@ class Notifier: class Publisher(AsyncioServer): """A network server that publish changes to structures encapsulated in - ``Notifiers``. + a :class:`.Notifier`. :param notifiers: A dictionary containing the notifiers to associate with - the ``Publisher``. The keys of the dictionary are the names of the - notifiers to be used with ``Subscriber``. + the :class:`.Publisher`. The keys of the dictionary are the names of the + notifiers to be used with :class:`.Subscriber`. """ def __init__(self, notifiers): AsyncioServer.__init__(self) @@ -245,7 +245,7 @@ class Publisher(AsyncioServer): finally: self._recipients[notifier_name].remove(queue) except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError): - # subscribers disconnecting are a normal occurence + # subscribers disconnecting are a normal occurrence pass finally: writer.close() diff --git a/doc/manual/core_language_reference.rst b/doc/manual/core_language_reference.rst index 8982f9231..228d33590 100644 --- a/doc/manual/core_language_reference.rst +++ b/doc/manual/core_language_reference.rst @@ -1,7 +1,7 @@ Core language reference ======================= -The most commonly used features from the ARTIQ language modules and from the core device modules are bundled together in ``artiq.experiment`` and can be imported with ``from artiq.experiment import *``. +The most commonly used features from the ARTIQ language modules and from the core device modules are bundled together in :mod:`artiq.experiment` and can be imported with ``from artiq.experiment import *``. :mod:`artiq.language.core` module --------------------------------- diff --git a/doc/manual/developing.rst b/doc/manual/developing.rst index 39eaa4602..5d1edc998 100644 --- a/doc/manual/developing.rst +++ b/doc/manual/developing.rst @@ -95,9 +95,9 @@ and the ARTIQ kernels. * Install LLVM and Clang: :: $ cd ~/artiq-dev - $ git clone -b artiq-4.0 https://github.com/m-labs/llvm-or1k + $ git clone -b artiq-6.0 https://github.com/m-labs/llvm-or1k $ cd llvm-or1k - $ git clone -b artiq-4.0 https://github.com/m-labs/clang-or1k tools/clang + $ git clone -b artiq-6.0 https://github.com/m-labs/clang-or1k tools/clang $ mkdir build $ cd build @@ -108,7 +108,7 @@ and the ARTIQ kernels. * Install Rust: :: $ cd ~/artiq-dev - $ git clone -b artiq-1.23.0 https://github.com/m-labs/rust + $ git clone -b artiq-1.28.0 https://github.com/m-labs/rust $ cd rust $ git submodule update --init --recursive $ mkdir build @@ -118,18 +118,18 @@ and the ARTIQ kernels. $ sudo chown $USER.$USER /usr/local/rust-or1k $ make install - $ libs="core std_unicode alloc" - $ rustc="/usr/local/rust-or1k/bin/rustc --target or1k-unknown-none -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s -g --crate-type rlib -L ." $ destdir="/usr/local/rust-or1k/lib/rustlib/or1k-unknown-none/lib/" - $ mkdir ../build-or1k - $ cd ../build-or1k - $ for lib in ${libs}; do ${rustc} --crate-name ${lib} ../src/lib${lib}/lib.rs; done - $ ${rustc} --crate-name libc ../src/liblibc_mini/lib.rs - $ ${rustc} --crate-name unwind ../src/libunwind/lib.rs - $ ${rustc} -Cpanic=abort --crate-name panic_abort ../src/libpanic_abort/lib.rs - $ ${rustc} -Cpanic=unwind --crate-name panic_unwind ../src/libpanic_unwind/lib.rs --cfg llvm_libunwind + $ rustc="rustc --out-dir ${destdir} -L ${destdir} --target or1k-unknown-none -g -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s --crate-type rlib" $ mkdir -p ${destdir} - $ cp *.rlib ${destdir} + $ ${rustc} --crate-name core src/libcore/lib.rs + $ ${rustc} --crate-name compiler_builtins src/libcompiler_builtins/src/lib.rs --cfg $ 'feature="compiler-builtins"' --cfg 'feature="mem"' + $ ${rustc} --crate-name std_unicode src/libstd_unicode/lib.rs + $ ${rustc} --crate-name alloc src/liballoc/lib.rs + $ ${rustc} --crate-name libc src/liblibc_mini/lib.rs + $ ${rustc} --crate-name unwind src/libunwind/lib.rs + $ ${rustc} -Cpanic=abort --crate-name panic_abort src/libpanic_abort/lib.rs + $ ${rustc} -Cpanic=unwind --crate-name panic_unwind src/libpanic_unwind/lib.rs \ + --cfg llvm_libunwind .. note:: Compilation of LLVM can take more than 30 min on some machines. Compilation of Rust can take more than two hours. diff --git a/doc/manual/developing_a_ndsp.rst b/doc/manual/developing_a_ndsp.rst index a48b7aa2b..4989aded6 100644 --- a/doc/manual/developing_a_ndsp.rst +++ b/doc/manual/developing_a_ndsp.rst @@ -1,5 +1,5 @@ -Developing a network device support package -=========================================== +Developing a Network Device Support Package (NDSP) +================================================== Most ARTIQ devices are interfaced through "controllers" that expose RPC interfaces to the network (based on :class:`artiq.protocols.pc_rpc`). The master never does direct I/O to the devices, but issues RPCs to the controllers when needed. As opposed to running everything on the master, this architecture has those main advantages: diff --git a/doc/manual/environment.rst b/doc/manual/environment.rst index 924c54509..5a2df7a89 100644 --- a/doc/manual/environment.rst +++ b/doc/manual/environment.rst @@ -10,7 +10,7 @@ The device database The device database contains information about the devices available in a ARTIQ installation, what drivers to use, what controllers to use and on what machine, and where the devices are connected. -The master (or ``artiq_run``) instantiates the device drivers (and the RPC clients in the case of controllers) for the experiments based on the contents of the device database. +The master (or :mod:`~artiq.frontend.artiq_run`) instantiates the device drivers (and the RPC clients in the case of controllers) for the experiments based on the contents of the device database. The device database is stored in the memory of the master and is generated by a Python script typically called ``device_db.py``. That script must define a global variable ``device_db`` with the contents of the database. The device database is a Python dictionary whose keys are the device names, and values can have several types. diff --git a/doc/manual/getting_started_core.rst b/doc/manual/getting_started_core.rst index eca62fd03..a5d77cd38 100644 --- a/doc/manual/getting_started_core.rst +++ b/doc/manual/getting_started_core.rst @@ -21,7 +21,7 @@ As a very first step, we will turn on a LED on the core device. Create a file `` self.core.reset() self.led.on() -The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.environment.EnvExperiment`. Among other features, ``EnvExperiment`` calls our ``build`` method and provides the ``setattr_device`` method that interfaces to the device database to create the appropriate device drivers and make those drivers accessible as ``self.core`` and ``self.led``. The ``@kernel`` decorator tells the system that the ``run`` method must be compiled for and executed on the core device (instead of being interpreted and executed as regular Python code on the host). The decorator uses ``self.core`` internally, which is why we request the core device using ``setattr_device`` like any other. +The central part of our code is our ``LED`` class, which derives from :class:`artiq.language.environment.EnvExperiment`. Among other features, :class:`~artiq.language.environment.EnvExperiment` calls our :meth:`~artiq.language.environment.Experiment.build` method and provides the :meth:`~artiq.language.environment.HasEnvironment.setattr_device` method that interfaces to the device database to create the appropriate device drivers and make those drivers accessible as ``self.core`` and ``self.led``. The :func:`~artiq.language.core.kernel` decorator (``@kernel``) tells the system that the :meth:`~artiq.language.environment.Experiment.run` method must be compiled for and executed on the core device (instead of being interpreted and executed as regular Python code on the host). The decorator uses ``self.core`` internally, which is why we request the core device using :meth:`~artiq.language.environment.HasEnvironment.setattr_device` like any other. Copy the file ``device_db.py`` (containing the device database) from the ``examples/master`` folder of ARTIQ into the same directory as ``led.py`` (alternatively, you can use the ``--device-db`` option of ``artiq_run``). You will probably want to set the IP address of the core device in ``device_db.py`` so that the computer can connect to it (it is the ``host`` parameter of the ``comm`` entry). See :ref:`device-db` for more information. The example device database is designed for the ``nist_clock`` hardware adapter on the KC705; see :ref:`board-ports` for RTIO channel assignments if you need to adapt the device database to a different hardware platform. @@ -69,18 +69,18 @@ You can then turn the LED off and on by entering 0 or 1 at the prompt that appea $ artiq_run led.py Enter desired LED state: 0 -What happens is the ARTIQ compiler notices that the ``input_led_state`` function does not have a ``@kernel`` decorator and thus must be executed on the host. When the core device calls it, it sends a request to the host to execute it. The host displays the prompt, collects user input, and sends the result back to the core device, which sets the LED state accordingly. +What happens is the ARTIQ compiler notices that the :meth:`input_led_state` function does not have a ``@kernel`` decorator (:func:`~artiq.language.core.kernel`) and thus must be executed on the host. When the core device calls it, it sends a request to the host to execute it. The host displays the prompt, collects user input, and sends the result back to the core device, which sets the LED state accordingly. RPC functions must always return a value of the same type. When they return a value that is not ``None``, the compiler should be informed in advance of the type of the value, which is what the ``-> TBool`` annotation is for. -Without the ``break_realtime()`` call, the RTIO events emitted by ``self.led.on()`` or ``self.led.off()`` would be scheduled at a fixed and very short delay after entering ``run()``. -These events would fail because the RPC to ``input_led_state()`` can take an arbitrary amount of time and therefore the deadline for submission of RTIO events would have long passed when ``self.led.on()`` or ``self.led.off()`` are called. -The ``break_realtime()`` call is necessary to waive the real-time requirements of the LED state change. +Without the :meth:`~artiq.coredevice.core.Core.break_realtime` call, the RTIO events emitted by :func:`self.led.on()` or :func:`self.led.off()` would be scheduled at a fixed and very short delay after entering :meth:`~artiq.language.environment.Experiment.run()`. +These events would fail because the RPC to :meth:`input_led_state()` can take an arbitrary amount of time and therefore the deadline for submission of RTIO events would have long passed when :func:`self.led.on()` or :func:`self.led.off()` are called. +The :meth:`~artiq.coredevice.core.Core.break_realtime` call is necessary to waive the real-time requirements of the LED state change. It advances the timeline far enough to ensure that events can meet the submission deadline. -Real-time I/O (RTIO) --------------------- +Real-time Input/Output (RTIO) +----------------------------- The point of running code on the core device is the ability to meet demanding real-time constraints. In particular, the core device can respond to an incoming stimulus or the result of a measurement with a low and predictable latency. We will see how to use inputs later; first, we must familiarize ourselves with how time is managed in kernels. @@ -102,11 +102,11 @@ Create a new file ``rtio.py`` containing the following: :: delay(2*us) self.ttl0.pulse(2*us) -In its ``build()`` method, the experiment obtains the core device and a TTL device called ``ttl0`` as defined in the device database. +In its :meth:`~artiq.language.environment.Experiment.build` method, the experiment obtains the core device and a TTL device called ``ttl0`` as defined in the device database. In ARTIQ, TTL is used roughly synonymous with "a single generic digital signal" and does not refer to a specific signaling standard or voltage/current levels. -When ``run()``, the experiment first ensures that ``ttl0`` is in output mode and actively driving the device it is connected to. -Bidirectional TTL channels (i.e. ``TTLInOut``) are in input (high impedance) mode by default, output-only TTL channels (``TTLOut``) are always in output mode. +When :meth:`~artiq.language.environment.Experiment.run`, the experiment first ensures that ``ttl0`` is in output mode and actively driving the device it is connected to. +Bidirectional TTL channels (i.e. :class:`~artiq.devices.ttl.TTLInOut`) are in input (high impedance) mode by default, output-only TTL channels (:class:`~artiq.devices.ttl.TTLOut`) are always in output mode. There are no input-only TTL channels. The experiment then drives one million 2 µs long pulses separated by 2 µs each. @@ -118,7 +118,7 @@ Any asymmetry in the overhead would manifest itself in a distorted and variable Instead, inside the core device, output timing is generated by the gateware and the CPU only programs switching commands with certain timestamps that the CPU computes. -This guarantees precise timing as long as the CPU can keep generating timestamps that are increasing fast enough. In case it fails to do that (and attempts to program an event with a timestamp smaller than the current RTIO clock timestamp), a :class:`artiq.coredevice.exceptions.RTIOUnderflow` exception is raised. The kernel causing it may catch it (using a regular ``try... except...`` construct), or it will be propagated to the host. +This guarantees precise timing as long as the CPU can keep generating timestamps that are increasing fast enough. In case it fails to do that (and attempts to program an event with a timestamp smaller than the current RTIO clock timestamp), a :exc:`~artiq.coredevice.exceptions.RTIOUnderflow` exception is raised. The kernel causing it may catch it (using a regular ``try... except...`` construct), or it will be propagated to the host. Try reducing the period of the generated waveform until the CPU cannot keep up with the generation of switching events and the underflow exception is raised. Then try catching it: :: @@ -161,7 +161,7 @@ ARTIQ can implement ``with parallel`` blocks without having to resort to any of It simply remembers the position on the timeline when entering the ``parallel`` block and then seeks back to that position after submitting the events generated by each statement. In other words, the statements in the ``parallel`` block are actually executed sequentially, only the RTIO events generated by them are scheduled to be executed in parallel. Note that if a statement takes a lot of CPU time to execute (this different from the events scheduled by a statement taking a long time), it may cause a subsequent statement to miss the deadline for timely submission of its events. -This then causes a ``RTIOUnderflow`` exception to be raised. +This then causes a :exc:`~artiq.coredevice.exceptions.RTIOUnderflow` exception to be raised. Within a parallel block, some statements can be made sequential again using a ``with sequential`` construct. Observe the pulses generated by this code: :: @@ -199,8 +199,8 @@ The core device records the real-time I/O waveforms into a circular buffer. It i Afterwards, the recorded data can be extracted and written to a VCD file using ``artiq_coreanalyzer -w rtio.vcd`` (see: :ref:`core-device-rtio-analyzer-tool`). VCD files can be viewed using third-party tools such as GtkWave. -DMA ---- +Direct Memory Access (DMA) +-------------------------- DMA allows you to store fixed sequences of pulses in system memory, and have the DMA core in the FPGA play them back at high speed. Pulse sequences that are too fast for the CPU (i.e. would cause RTIO underflows) can still be generated using DMA. The only modification of the sequence that the DMA core supports is shifting it in time (so it can be played back at any position of the timeline), everything else is fixed at the time of recording the sequence. diff --git a/doc/manual/make.bat b/doc/manual/make.bat new file mode 100644 index 000000000..5567edd05 --- /dev/null +++ b/doc/manual/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +REM TODO: missing latexpdf and latexpdfja cmds of ./Makefile + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. epub3 to make an epub3 + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + REM echo.^> qcollectiongenerator %BUILDDIR%\qthelp\ARTIQ.qhcp + REM echo.To view the help file: + REM echo.^> assistant -collectionFile %BUILDDIR%\qthelp\ARTIQ.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "epub3" ( + %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/doc/manual/management_system.rst b/doc/manual/management_system.rst index cd771b226..12f5e45d6 100644 --- a/doc/manual/management_system.rst +++ b/doc/manual/management_system.rst @@ -1,7 +1,7 @@ Management system ================= -The management system described below is optional: experiments can be run one by one using ``artiq_run``, and the controllers can run stand-alone (without a controller manager). For their very first steps with ARTIQ or in simple or particular cases, users do not need to deploy the management system. +The management system described below is optional: experiments can be run one by one using :mod:`~artiq.frontend.artiq_run`, and the controllers can run stand-alone (without a controller manager). For their very first steps with ARTIQ or in simple or particular cases, users do not need to deploy the management system. Components ********** @@ -42,13 +42,15 @@ Basics To use hardware resources more efficiently, potentially compute-intensive pre-computation and analysis phases of other experiments is executed in parallel with the body of the current experiment that accesses the hardware. +.. seealso:: These steps are implemented in :class:`~artiq.language.environment.Experiment`. However, user-written experiments should usually derive from (sub-class) :class:`artiq.language.environment.EnvExperiment`. + Experiments are divided into three phases that are programmed by the user: -1. The preparation stage, that pre-fetches and pre-computes any data that necessary to run the experiment. Users may implement this stage by overloading the ``prepare`` method. It is not permitted to access hardware in this stage, as doing so may conflict with other experiments using the same devices. -2. The running stage, that corresponds to the body of the experiment, and typically accesses hardware. Users must implement this stage and overload the ``run`` method. -3. The analysis stage, where raw results collected in the running stage are post-processed and may lead to updates of the parameter database. This stage may be implemented by overloading the ``analyze`` method. +1. The **preparation** stage, that pre-fetches and pre-computes any data that necessary to run the experiment. Users may implement this stage by overloading the :meth:`~artiq.language.environment.Experiment.prepare` method. It is not permitted to access hardware in this stage, as doing so may conflict with other experiments using the same devices. +2. The **running** stage, that corresponds to the body of the experiment, and typically accesses hardware. Users must implement this stage and overload the :meth:`~artiq.language.environment.Experiment.run` method. +3. The **analysis** stage, where raw results collected in the running stage are post-processed and may lead to updates of the parameter database. This stage may be implemented by overloading the :meth:`~artiq.language.environment.Experiment.analyze` method. -.. note:: Only the ``run`` method implementation is mandatory; if the experiment does not fit into the pipelined scheduling model, it can leave one or both of the other methods empty (which is the default). +.. note:: Only the :meth:`~artiq.language.environment.Experiment.run` method implementation is mandatory; if the experiment does not fit into the pipelined scheduling model, it can leave one or both of the other methods empty (which is the default). The three phases of several experiments are then executed in a pipelined manner by the scheduler in the ARTIQ master: experiment A executes its preparation stage, then experiment A executes its running stage while experiment B executes its preparation stage, and so on. @@ -70,11 +72,11 @@ If there are other experiments with higher priority (e.g. a high-priority timed Otherwise, ``pause()`` returns immediately. To check whether ``pause()`` would in fact *not* return immediately, use :meth:`artiq.master.scheduler.Scheduler.check_pause`. -The experiment must place the hardware in a safe state and disconnect from the core device (typically, by using ``self.core.comm.close()``) before calling ``pause``. +The experiment must place the hardware in a safe state and disconnect from the core device (typically, by calling ``self.coredevice.comm.close()`` from the kernel, which is equivalent to :meth:`artiq.coredevice.core.Core.close`) before calling ``pause()``. -Accessing the ``pause`` and ``check_pause`` methods is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using ``get_device`` or ``attr_device``. +Accessing the ``pause()`` and :meth:`~artiq.master.scheduler.Scheduler.check_pause` methods is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using :meth:`~artiq.language.environment.HasEnvironment.get_device` (``self.get_device()``) or :meth:`~artiq.language.environment.HasEnvironment.setattr_device` (``self.setattr_device()``). -``check_pause`` can be called (via RPC) from a kernel, but ``pause`` must not. +:meth:`~artiq.master.scheduler.Scheduler.check_pause` can be called (via RPC) from a kernel, but ``pause()`` must not. Multiple pipelines ------------------