diff --git a/artiq/coredevice/sawg.py b/artiq/coredevice/sawg.py index 1010e48db..9bacdf7bb 100644 --- a/artiq/coredevice/sawg.py +++ b/artiq/coredevice/sawg.py @@ -1,4 +1,160 @@ +from artiq.language.types import TInt32, TBool +from artiq.language.core import kernel, now_mu from artiq.coredevice.spline import Spline +from artiq.coredevice.rtio import rtio_output + + +# sawg.Config addresses +_SAWG_DIV = 0 +_SAWG_CLR = 1 +_SAWG_IQ_EN = 2 +# _SAWF_PAD = 3 # reserved +_SAWG_DUC_I_MIN = 4 +_SAWG_DUC_I_MAX = 5 +_SAWG_DUC_Q_MIN = 6 +_SAWG_DUC_Q_MAX = 7 +_SAWG_OUT_MIN = 8 +_SAWG_OUT_MAX = 9 + + +class Config: + """SAWG configuration. + + Exposes the configurable quantities of a single SAWG channel. + + :param channel: RTIO channel number of the channel. + :param core: Core device. + """ + kernel_invariants = {"channel", "core"} + + def __init__(self, channel, core): + self.channel = channel + self.core = core + + @kernel + def set_div(self, div: TInt32, n: TInt32=0): + """Set the spline evolution divider and current counter value. + + The divider and the spline evolution are synchronized across all + spline channels within a SAWG channel. The phase accumulator always + evolves at full speed. + + :param div: Spline evolution divider, such that + ``t_sawg_spline/t_rtio_coarse = div + 1``. Default: ``0``. + :param n: Current value of the counter. Default: ``0``. + """ + rtio_output(now_mu(), self.channel, _SAWG_DIV, div | (n << 16)) + + @kernel + def set_clr(self, clr0: TBool, clr1: TBool, clr2: TBool): + """Set the phase clear mode for the three phase accumulators. + + When the ``clr`` bit for a given phase accumulator is + set, that phase accumulator will be cleared with every phase RTIO + command and the output phase will be exactly the phase RTIO value + ("absolute phase update mode"). + + In turn, when the bit is cleared, the phase RTIO channels only + provide a phase offset to the current value of the phase + accumulator ("relative phase update mode"). + + :param clr0: Auto-clear phase accumulator of the ``phase0``/ + ``frequency0`` DUC. Default: ``True`` + :param clr1: Auto-clear phase accumulator of the ``phase1``/ + ``frequency1`` DDS. Default: ``True`` + :param clr2: Auto-clear phase accumulator of the ``phase2``/ + ``frequency2`` DDS. Default: ``True`` + """ + rtio_output(now_mu(), self.channel, _SAWG_CLR, int(clr1) | + (int(clr2) << 1) | (int(clr0) << 2)) + + @kernel + def set_iq_en(self, i_enable: TBool, q_enable: TBool): + """Enable I/Q data on this DAC channel. + + Every pair of SAWG channels forms a buddy pair. + The ``iq_en`` configuration controls which DDS data is emitted to the + DACs. + + Refer to the documentation of :class:`SAWG` for a mathematical + description of ``i_enable`` and ``q_enable``. + + :param i_enable: Controls adding the in-phase + DUC-DDS data of *this* SAWG channel to *this* DAC channel. + Default: ``1``. + :param q_enable: controls adding the quadrature + DUC-DDS data of this SAWG's *buddy* channel to *this* DAC + channel. Default: ``0``. + """ + rtio_output(now_mu(), self.channel, _SAWG_IQ_EN, int(i_enable) | + (int(q_enable) << 1)) + + @kernel + def set_duc_i_max(self, limit: TInt32): + """Set the DUC I data summing junction upper limit. + + Each of the three summing junctions has a saturating adder with + configurable upper and lower limits. The three summing junctions are: + + * At the in-phase input to the ``phase0``/``frequency0`` fast DUC, + where the in-phase outputs of the two slow DDS (1 and 2) are + added together. + * At the quadrature input to the ``phase0``/``frequency0`` + fast DUC, where the quadrature outputs of the two slow DDS + (1 and 2) are added together. + * Before the DAC, where the following three data streams + are added together: + + * the output of the ``offset`` spline, + * (optionally, depending on ``i_enable``) the in-phase output + of the ``phase0``/``frequency0`` fast DUC, and + * (optionally, depending on ``q_enable``) the quadrature + output of the ``phase0``/``frequency0`` fast DUC of the + buddy channel. + + Refer to the documentation of :class:`SAWG` for a mathematical + description of the summing junctions. + + The default limits are the full range of signed 16 bit data. + + .. seealso:: + * :meth:`set_duc_i_max`: Upper limit of the in-phase input to + the DUC. + * :meth:`set_duc_i_min`: Lower limit of the in-phase input to + the DUC. + * :meth:`set_duc_q_max`: Upper limit of the quadrature input to + the DUC. + * :meth:`set_duc_q_min`: Lower limit of the quadrature input to + the DUC. + * :meth:`set_out_max`: Upper limit of the DAC output. + * :meth:`set_out_min`: Lower limit of the DAC output. + """ + rtio_output(now_mu(), self.channel, _SAWG_DUC_I_MAX, limit) + + @kernel + def set_duc_i_min(self, limit: TInt32): + """.. seealso:: :meth:`set_duc_i_max`""" + rtio_output(now_mu(), self.channel, _SAWG_DUC_I_MIN, limit) + + @kernel + def set_duc_q_max(self, limit: TInt32): + """.. seealso:: :meth:`set_duc_i_max`""" + rtio_output(now_mu(), self.channel, _SAWG_DUC_Q_MAX, limit) + + @kernel + def set_duc_q_min(self, limit: TInt32): + """.. seealso:: :meth:`set_duc_i_max`""" + rtio_output(now_mu(), self.channel, _SAWG_DUC_Q_MIN, limit) + + @kernel + def set_out_max(self, limit: TInt32): + """.. seealso:: :meth:`set_duc_i_max`""" + rtio_output(now_mu(), self.channel, _SAWG_OUT_MAX, limit) + + @kernel + def set_out_min(self, limit: TInt32): + """.. seealso:: :meth:`set_duc_i_max`""" + rtio_output(now_mu(), self.channel, _SAWG_OUT_MIN, limit) class SAWG: @@ -13,8 +169,10 @@ class SAWG: i_enable*Re(oscillators) + q_enable*Im(buddy_oscillators)) - The nine spline interpolators are accessible as attributes: + The configuration channel and the nine spline interpolators are accessible + as attributes: + * :attr:`config`: :class:`Config` * :attr:`offset`, :attr:`amplitude1`, :attr:`amplitude2`: in units of full scale * :attr:`phase0`, :attr:`phase1`, :attr:`phase2`: in units of turns @@ -22,7 +180,8 @@ class SAWG: of Hz :param channel_base: RTIO channel number of the first channel (amplitude). - Frequency and Phase are then assumed to be successive channels. + The configuration channel and frequency/phase/amplitude channels are + then assumed to be successive channels. :param parallelism: Number of output samples per coarse RTIO clock cycle. :param core_device: Name of the core device that this SAWG is on. """ @@ -37,7 +196,7 @@ class SAWG: width = 16 time_width = 16 cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain - # cfg: channel_base + self.config = Config(channel_base, self.core) self.offset = Spline(width, time_width, channel_base + 1, self.core, 2.) self.amplitude1 = Spline(width, time_width, channel_base + 2, diff --git a/artiq/examples/phaser/repository/demo_2tone.py b/artiq/examples/phaser/repository/demo_2tone.py index d1bf43588..da731cc4e 100644 --- a/artiq/examples/phaser/repository/demo_2tone.py +++ b/artiq/examples/phaser/repository/demo_2tone.py @@ -17,6 +17,21 @@ class SAWGTestTwoTone(EnvExperiment): self.core.reset() self.ttl_sma.output() + self.sawg0.config.set_clr(True, True, True) + delay(10*us) + self.sawg0.config.set_duc_i_max(0x7fff) + delay(10*us) + self.sawg0.config.set_duc_i_min(-0x8000) + delay(10*us) + self.sawg0.config.set_duc_q_max(0x7fff) + delay(10*us) + self.sawg0.config.set_duc_q_min(-0x8000) + delay(10*us) + self.sawg0.config.set_out_max(0x7fff) + delay(10*us) + self.sawg0.config.set_out_min(-0x8000) + delay(10*us) + while True: t_up = t_hold = t_down = 800*ns a1 = .3