From 7c664142a56a3143b390720e7a8783f8b912e17e Mon Sep 17 00:00:00 2001 From: Peter Drmota <49479443+pmldrmota@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:44:03 +0000 Subject: [PATCH] Simplified use of the AD9910 RAM feature (#1584) * coredevice: Change Urukul default single-tone profile to 7 This allows using the internal profile control in RAM modulation mode (which always starts to play back at profile 0) without competing for the content of the profile 0 register used in single tone mode. Signed-off-by: Peter Drmota * ad9910/set_mu: comment on caveats when setting register * ad9910: avoid unnecessary write/param Credit: Solution proposed by @pmldrmota in https://github.com/m-labs/artiq/pull/1584#issuecomment-987774353 * revert 1064fdff (`set_mu()` comments) 158a7be7 had addressed this issue. Co-authored-by: occheung --- RELEASE_NOTES.rst | 7 +++++-- artiq/coredevice/ad9910.py | 42 +++++++++++++++++++++++++++----------- artiq/coredevice/urukul.py | 2 +- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 75b1d3347..7c722c936 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -84,8 +84,11 @@ Highlights: - Improved performance for kernel RPC involving list and array. * Coredevice SI to mu conversions now always return valid codes, or raise a ``ValueError``. * Zotino now exposes ``voltage_to_mu()`` -* ``ad9910``: The maximum amplitude scale factor is now ``0x3fff`` (was ``0x3ffe`` - before). +* ``ad9910``: + - The maximum amplitude scale factor is now ``0x3fff`` (was ``0x3ffe`` before). + - The default single-tone profile is now 7 (was 0). + - Added option to ``set_mu()`` that affects the ASF, FTW and POW registers + instead of the single-tone profile register. * Mirny now supports HW revision independent, human readable ``clk_sel`` parameters: "XO", "SMA", and "MMCX". Passing an integer is backwards compatible. * Dashboard: diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py index e93d3202b..f0b8879d0 100644 --- a/artiq/coredevice/ad9910.py +++ b/artiq/coredevice/ad9910.py @@ -236,7 +236,7 @@ class AD9910: """ self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 24, urukul.SPIT_DDS_WR, self.chip_select) - self.bus.write((addr << 24) | (data << 8)) + self.bus.write((addr << 24) | ((data & 0xffff) << 8)) @kernel def write32(self, addr: TInt32, data: TInt32): @@ -516,10 +516,11 @@ class AD9910: self.cpld.io_update.pulse(1 * us) @kernel - def set_mu(self, ftw: TInt32, pow_: TInt32 = 0, asf: TInt32 = 0x3fff, + def set_mu(self, ftw: TInt32 = 0, pow_: TInt32 = 0, asf: TInt32 = 0x3fff, phase_mode: TInt32 = _PHASE_MODE_DEFAULT, - ref_time_mu: TInt64 = int64(-1), profile: TInt32 = 0) -> TInt32: - """Set profile 0 data in machine units. + ref_time_mu: TInt64 = int64(-1), profile: TInt32 = 7, + ram_destination: TInt32 = -1) -> TInt32: + """Set DDS data in machine units. This uses machine units (FTW, POW, ASF). The frequency tuning word width is 32, the phase offset word width is 16, and the amplitude @@ -538,7 +539,13 @@ class AD9910: by :meth:`set_phase_mode` for this call. :param ref_time_mu: Fiducial time used to compute absolute or tracking phase updates. In machine units as obtained by `now_mu()`. - :param profile: Profile number to set (0-7, default: 0). + :param profile: Single tone profile number to set (0-7, default: 7). + Ineffective if `ram_destination` is specified. + :param ram_destination: RAM destination (:const:`RAM_DEST_FTW`, + :const:`RAM_DEST_POW`, :const:`RAM_DEST_ASF`, + :const:`RAM_DEST_POWASF`). If specified, write free DDS parameters + to the ASF/FTW/POW registers instead of to the single tone profile + register (default behaviour, see `profile`). :return: Resulting phase offset word after application of phase tracking offset. When using :const:`PHASE_MODE_CONTINUOUS` in subsequent calls, use this value as the "current" phase. @@ -561,8 +568,17 @@ class AD9910: # is equivalent to an output pipeline latency. dt = int32(now_mu()) - int32(ref_time_mu) pow_ += dt * ftw * self.sysclk_per_mu >> 16 - self.write64(_AD9910_REG_PROFILE0 + profile, - (asf << 16) | (pow_ & 0xffff), ftw) + if ram_destination == -1: + self.write64(_AD9910_REG_PROFILE0 + profile, + (asf << 16) | (pow_ & 0xffff), ftw) + else: + if not ram_destination == RAM_DEST_FTW: + self.set_ftw(ftw) + if not ram_destination == RAM_DEST_POWASF: + if not ram_destination == RAM_DEST_ASF: + self.set_asf(asf) + if not ram_destination == RAM_DEST_POW: + self.set_pow(pow_) delay_mu(int64(self.sync_data.io_update_delay)) self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYN_CCLK at_mu(now_mu() & ~7) # clear fine TSC again @@ -821,10 +837,11 @@ class AD9910: return self.pow_to_turns(self.get_pow()) @kernel - def set(self, frequency: TFloat, phase: TFloat = 0.0, + def set(self, frequency: TFloat = 0.0, phase: TFloat = 0.0, amplitude: TFloat = 1.0, phase_mode: TInt32 = _PHASE_MODE_DEFAULT, - ref_time_mu: TInt64 = int64(-1), profile: TInt32 = 0) -> TFloat: - """Set profile 0 data in SI units. + ref_time_mu: TInt64 = int64(-1), profile: TInt32 = 7, + ram_destination: TInt32 = -1) -> TFloat: + """Set DDS data in SI units. .. seealso:: :meth:`set_mu` @@ -833,13 +850,14 @@ class AD9910: :param amplitude: Amplitude in units of full scale :param phase_mode: Phase mode constant :param ref_time_mu: Fiducial time stamp in machine units - :param profile: Profile to affect + :param profile: Single tone profile to affect. + :param ram_destination: RAM destination. :return: Resulting phase offset in turns """ return self.pow_to_turns(self.set_mu( self.frequency_to_ftw(frequency), self.turns_to_pow(phase), self.amplitude_to_asf(amplitude), phase_mode, ref_time_mu, - profile)) + profile, ram_destination)) @kernel def get(self, profile: TInt32 = 0) -> TTuple([TFloat, TFloat, TFloat]): diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index 7cda7a4ce..92b951036 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -188,7 +188,7 @@ class CPLD: assert sync_div is None sync_div = 0 - self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0, + self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=7, io_update=0, mask_nu=0, clk_sel=clk_sel, sync_sel=sync_sel, rst=0, io_rst=0, clk_div=clk_div)