diff --git a/artiq/coredevice/suservo.py b/artiq/coredevice/suservo.py index 6f288287b..2f55c20ca 100644 --- a/artiq/coredevice/suservo.py +++ b/artiq/coredevice/suservo.py @@ -17,13 +17,13 @@ COEFF_SHIFT = 11 @portable def y_mu_to_full_scale(y): - """Convert Servo Y data from machine units to units of full scale.""" + """Convert servo Y data from machine units to units of full scale.""" return y*(1./(1 << COEFF_WIDTH - 1)) @portable def adc_mu_to_volts(x, gain): - """Convert Servo ADC data from machine units to Volt.""" + """Convert servo ADC data from machine units to Volt.""" val = (x >> 1) & 0xffff mask = 1 << 15 val = -(val & mask) + (val & ~mask) @@ -36,20 +36,21 @@ class SUServo: Sampler-Urukul Servo is a integrated device controlling one 8-channel ADC (Sampler) and two 4-channel DDS (Urukuls) with a DSP engine connecting the ADC data and the DDS output amplitudes to enable - feedback. SUServo can for example be used to implement intensity - stabilization of laser beams with an AOM and a photodetector. + feedback. SU Servo can for example be used to implement intensity + stabilization of laser beams with an amplifier and AOM driven by Urukul + and a photodetector connected to Sampler. - Additionally SUServo supports multiple preconfigured profiles per channel + Additionally SU Servo supports multiple preconfigured profiles per channel and features like automatic integrator hold. Notes: - * See the SUServo variant of the Kasli target for an example of how to + * See the SU Servo variant of the Kasli target for an example of how to connect the gateware and the devices. Sampler and each Urukul need two EEM connections. * Ensure that both Urukuls are AD9910 variants and have the on-board dip switches set to 1100 (first two on, last two off). - * Refer to the Sampler and Urukul documentation and the SUServo + * Refer to the Sampler and Urukul documentation and the SU Servo example device database for runtime configuration of the devices (PLLs, gains, clock routing etc.) @@ -89,9 +90,10 @@ class SUServo: @kernel def init(self): - """Initialize the Servo, Sampler and both Urukuls. + """Initialize the servo, Sampler and both Urukuls. - Leaves the Servo disabled (see :meth:`set_config`), resets all DDS. + Leaves the servo disabled (see :meth:`set_config`), resets and + configures all DDS. Urukul initialization is performed blindly as there is no readback from the DDS or the CPLDs. @@ -120,7 +122,7 @@ class SUServo: @kernel def write(self, addr, value): - """Write to Servo memory. + """Write to servo memory. This method advances the timeline by one coarse RTIO cycle. @@ -132,7 +134,7 @@ class SUServo: @kernel def read(self, addr): - """Read from Servo memory. + """Read from servo memory. This method does not advance the timeline but consumes all slack. @@ -145,14 +147,16 @@ class SUServo: def set_config(self, enable): """Set SU Servo configuration. - This method advances the timeline by one Servo memory access. + This method advances the timeline by one servo memory access. It does not support RTIO event replacement. - :param enable (int): Enable Servo operation. Enabling starts servo - iterations beginning with the ADC sampling stage. This also - provides a mean for synchronization of Servo updates to other RTIO - activity. - Disabling takes up to 2 Servo cycles (~2.2 µs) to clear the + :param enable (int): Enable servo operation. Enabling starts servo + iterations beginning with the ADC sampling stage. The first DDS + update will happen about two servo cycles (~2.3 µs) after enabling + the servo. The delay is deterministic. + This also provides a mean for synchronization of servo updates to + other RTIO activity. + Disabling takes up to two servo cycles (~2.3 µs) to clear the processing pipeline. """ self.write(CONFIG_ADDR, enable) @@ -163,8 +167,8 @@ class SUServo: This method does not advance the timeline but consumes all slack. - The ``done`` bit indicates that a SUServo cycle has completed. - It is pulsed for one RTIO cycle every SUServo cycle and asserted + The ``done`` bit indicates that a SU Servo cycle has completed. + It is pulsed for one RTIO cycle every SU Servo cycle and asserted continuously when the servo is not ``enabled`` and the pipeline has drained (the last DDS update is done). @@ -246,10 +250,13 @@ class Channel: def set(self, en_out, en_iir=0, profile=0): """Operate channel. - This method does not advance the timeline. - Output RF switch setting takes effect immediately. - IIR updates take place once the RF switch has been enabled for the - configured delay and the profile setting has been stable. + 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 + 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. :param en_out: RF switch enable :param en_iir: IIR updates enable @@ -279,7 +286,7 @@ class Channel: def set_dds(self, profile, frequency, offset, phase=0.): """Set profile DDS coefficients. - This method advances the timeline by four Servo memory accesses. + This method advances the timeline by four servo memory accesses. Profile parameter changes are not synchronized. Activate a different profile or stop the servo to ensure synchronous changes. @@ -332,7 +339,7 @@ class Channel: :param b1: 18 bit signed B1 coefficient (old, X1 coefficient, feed forward, proportional gain) :param dly: IIR update suppression time. In units of IIR cycles - (~1.2 µs, 0-255) + (~1.2 µs, 0-255). """ base = (self.servo_channel << 8) | (profile << 3) self.servo.write(base + 3, adc | (dly << 8)) @@ -344,7 +351,7 @@ class Channel: def set_iir(self, profile, adc, gain, corner=0., limit=0., delay=0.): """Set profile IIR coefficients. - This method advances the timeline by four Servo memory accesses. + This method advances the timeline by four servo memory accesses. Profile parameter changes are not synchronized. Activate a different profile or stop the servo to ensure synchronous changes. @@ -374,7 +381,12 @@ class Channel: :param limit: Integrator gain limit (1). When 0 (the default) the integrator gain limit is infinite. Positive. :param delay: Delay (in seconds, 0-300 µs) before allowing IIR updates - after invoking :meth:`set`. + after invoking :meth:`set`. This is rounded to the nearest number + of servo cycles (~1.2 µs). Since the RF switch (:meth:`set`) can be + opened at any time relative to the servo cycle, the first DDS + update that carries updated IIR data will occur approximately + between ``delay + 1 cycle`` and ``delay + 2 cycles`` after + :meth:`set`. """ B_NORM = 1 << COEFF_SHIFT + 1 A_NORM = 1 << COEFF_SHIFT @@ -424,11 +436,14 @@ class Channel: def get_profile_mu(self, profile, data): """Retrieve profile data. - The data is returned in the `data` argument as: - `[ftw >> 16, b1, pow, adc | (delay << 8), offset, a1, ftw, b0]`. + Profile data is returned in the ``data`` argument in machine units + packed as: ``[ftw >> 16, b1, pow, adc | (delay << 8), offset, a1, + ftw & 0xffff, b0]``. + + .. seealso:: The individual fields are described in + :meth:`set_iir_mu` and :meth:`set_dds_mu`. This method advances the timeline by 32 µs and consumes all slack. - Profile data is returned :param profile: Profile number (0-31) :param data: List of 8 integers to write the profile data into @@ -473,11 +488,11 @@ class Channel: The IIR state is also know as the "integrator", or the DDS amplitude scale factor. It is 18 bits wide and unsigned. - This method must not be used when the Servo - could be writing to the same location. Either deactivate the profile, - or deactivate IIR updates, or disable Servo iterations. + This method must not be used when the servo could be writing to the + same location. Either deactivate the profile, or deactivate IIR + updates, or disable servo iterations. - This method advances the timeline by one Servo memory access. + This method advances the timeline by one servo memory access. :param profile: Profile number (0-31) :param y: 17 bit unsigned Y0 @@ -493,11 +508,11 @@ class Channel: The IIR state is also know as the "integrator", or the DDS amplitude scale factor. It is 18 bits wide and unsigned. - This method must not be used when the Servo - could be writing to the same location. Either deactivate the profile, - or deactivate IIR updates, or disable Servo iterations. + This method must not be used when the servo could be writing to the + same location. Either deactivate the profile, or deactivate IIR + updates, or disable servo iterations. - This method advances the timeline by one Servo memory access. + This method advances the timeline by one servo memory access. :param profile: Profile number (0-31) :param y: IIR state in units of full scale