diff --git a/artiq/coredevice/sawg.py b/artiq/coredevice/sawg.py index f6f90f973..3858527d6 100644 --- a/artiq/coredevice/sawg.py +++ b/artiq/coredevice/sawg.py @@ -1,73 +1,139 @@ -from artiq.language.core import kernel, now_mu -from artiq.coredevice.rtio import rtio_output -from artiq.language.types import TInt32, TFloat +from artiq.language.core import kernel, now_mu, portable +from artiq.coredevice.rtio import rtio_output, rtio_output_list +from artiq.language.types import TInt32, TInt64, TFloat, TList + + +class Spline: + kernel_invariants = {"channel", "core", "scale", "width", + "time_width", "time_scale"} + + def __init__(self, width, time_width, channel, core_device, scale=1.): + self.core = core_device + self.channel = channel + self.width = width + self.scale = (1 << width) / scale + self.time_width = time_width + self.time_scale = (1 << time_width) / core_device.coarse_ref_period + + @portable(flags=["fast-math"]) + def to_mu(self, value: TFloat) -> TInt32: + return int(round(value*self.scale)) + + @portable(flags=["fast-math"]) + def from_mu(self, value: TInt32) -> TFloat: + return value/self.scale + + @portable(flags=["fast-math"]) + def to_mu64(self, value: TFloat) -> TList(TInt32): + v = int(round(value*self.scale), width=64) + return [int(v >> 32, width=32), int(v, width=32)] + + @kernel + def set_mu(self, value: TInt32): + """Set spline value (machine units). + + :param value: Spline value in integer machine units. + """ + rtio_output(now_mu(), self.channel, 0, value) + + @kernel + def set(self, value: TFloat): + """Set spline value. + + :param value: Spline value relative to full-scale. + """ + rtio_output(now_mu(), self.channel, 0, self.to_mu(value)) + + @kernel + def set64(self, value: TFloat): + """Set spline value. + + :param value: Spline value relative to full-scale. + """ + rtio_output_list(now_mu(), self.channel, 0, self.to_mu64(value)) + + @kernel + def set_list_mu(self, value: TList(TInt32)): + """Set spline raw values. + + :param value: Spline packed raw values. + """ + rtio_output_list(now_mu(), self.channel, 0, value) + + @portable(flags=["fast-math"]) + def coeff_to_mu(self, value: TList(TFloat)) -> TList(TInt32): + l = len(value) + w = l*self.width + (l - 1)*l//2*self.time_width + v = [0] * ((w + 31)//32) + j = 0 + for i, vi in enumerate(value): + w = self.width + i*self.time_width + vi = int(round(vi*(self.scale*self.time_scale**i)), width=64) + for k in range(0, w, 16): + wi = (vi >> k) & 0xffff + v[j//2] += wi << (16 * ((j + 1)//2 - j//2)) + j += 1 + v.append(vi) + return v + + @kernel + def set_list(self, value: TList(TFloat)): + """Set spline coefficients. + + :param value: List of floating point spline knot coefficients, + lowest order (constant) coefficient first. + """ + self.set_list_mu(self.coeff_to_mu(value)) class SAWG: """Smart arbitrary waveform generator channel. + The channel is parametrized as: :: + + oscillators = exp(2j*pi*(frequency0*t + phase0))*( + amplitude1*exp(2j*pi*(frequency1*t + phase1)) + + amplitude2*exp(2j*pi*(frequency2*t + phase2)) + + output = (offset + + i_enable*Re(oscillators) + + q_enable*Im(buddy_oscillators)) + + Where: + * offset, amplitude1, amplitude1: in units of full scale + * phase0, phase1, phase2: in units of turns + * frequency0, frequency1, frequency2: in units of Hz :param channel_base: RTIO channel number of the first channel (amplitude). Frequency and Phase are then assumed to be successive channels. """ - kernel_invariants = {"amplitude_scale", "frequency_scale", "phase_scale", - "channel_base", "core"} + kernel_invariants = {"channel_base", "core", + "amplitude1", "frequency1", "phase1", + "amplitude2", "frequency2", "phase2" + "frequency0", "phase0", "offset"} - def __init__(self, dmgr, channel_base, parallelism=4, core_device="core"): + def __init__(self, dmgr, channel_base, parallelism, core_device="core"): self.core = dmgr.get(core_device) self.channel_base = channel_base + width = 16 + time_width = 16 cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain - a_width = 16 - f_width = 32 - p_width = 16 - self.amplitude_scale = (1 << a_width) / 2 / cordic_gain - self.phase_scale = 1 << p_width - self.frequency_scale = ((1 << f_width) * self.core.coarse_ref_period / - parallelism) - - @kernel - def set_amplitude_mu(self, amplitude: TInt32): - """Set DDS amplitude (machine units). - - :param amplitude: DDS amplitude in machine units. - """ - rtio_output(now_mu(), self.channel_base, 0, amplitude) - - @kernel(flags=["fast-math"]) - def set_amplitude(self, amplitude: TFloat): - """Set DDS amplitude. - - :param amplitude: DDS amplitude relative to full-scale. - """ - self.set_amplitude_mu(int(round(amplitude*self.amplitude_scale))) - - @kernel - def set_frequency_mu(self, frequency: TInt32): - """Set DDS frequency (machine units). - - :param frequency: DDS frequency in machine units. - """ - rtio_output(now_mu(), self.channel_base + 1, 0, frequency) - - @kernel(flags=["fast-math"]) - def set_frequency(self, frequency: TFloat): - """Set DDS frequency. - - :param frequency: DDS frequency in Hz. - """ - self.set_frequency_mu(int(round(frequency*self.frequency_scale))) - - @kernel - def set_phase_mu(self, phase: TInt32): - """Set DDS phase (machine units). - - :param phase: DDS phase in machine units. - """ - rtio_output(now_mu(), self.channel_base + 2, 0, phase) - - @kernel(flags=["fast-math"]) - def set_phase(self, phase: TFloat): - """Set DDS phase. - - :param phase: DDS phase relative in turns. - """ - self.set_phase_mu(int(round(phase*self.phase_scale))) + # cfg: channel_base + self.offset = Spline(width, time_width, channel_base + 1, + self.core, 2) + self.amplitude1 = Spline(width, time_width, channel_base + 2, + self.core, 2*cordic_gain**2) + self.frequency1 = Spline(3*width, time_width, channel_base + 3, + self.core, self.core.coarse_ref_period) + self.phase1 = Spline(width, time_width, channel_base + 4, + self.core, 1.) + self.amplitude2 = Spline(width, time_width, channel_base + 5, + self.core, 2*cordic_gain**2) + self.frequency2 = Spline(3*width, time_width, channel_base + 6, + self.core, self.core.coarse_ref_period) + self.phase2 = Spline(width, time_width, channel_base + 7, + self.core, 1.) + self.frequency0 = Spline(2*width, time_width, channel_base + 8, + self.core, + parallelism/self.core.coarse_ref_period) + self.phase0 = Spline(width, time_width, channel_base + 9, + self.core, 1.) diff --git a/artiq/examples/phaser/device_db.pyon b/artiq/examples/phaser/device_db.pyon index 9858fc093..1bd1beb74 100644 --- a/artiq/examples/phaser/device_db.pyon +++ b/artiq/examples/phaser/device_db.pyon @@ -12,7 +12,7 @@ "module": "artiq.coredevice.core", "class": "Core", "arguments": { - "ref_period": 5/6, + "ref_period": 5e-9/6, "external_clock": True } }, @@ -60,18 +60,18 @@ "type": "local", "module": "artiq.coredevice.sawg", "class": "SAWG", - "arguments": {"channel_base": 7, "parallelism": 2} + "arguments": {"channel_base": 14, "parallelism": 2} }, "sawg2": { "type": "local", "module": "artiq.coredevice.sawg", "class": "SAWG", - "arguments": {"channel_base": 10, "parallelism": 2} + "arguments": {"channel_base": 24, "parallelism": 2} }, "sawg3": { "type": "local", "module": "artiq.coredevice.sawg", "class": "SAWG", - "arguments": {"channel_base": 13, "parallelism": 2} + "arguments": {"channel_base": 34, "parallelism": 2} } }