Thanks for the feedback!
Could you elaborate what your mental model of the chip is at the different code steps and what should happen vs does in fact happen? I have a hard time diff-ing mine and yours 😉
Also the line numbers aren't available immediately.
Sorry about that. I think that we understood the issue. Here's the incorrect code again, slightly refactored, for reference:
from artiq.experiment import *
from artiq.coredevice import ad9910
# Urukul v1.3 => use different boards because
# PROFILE[2:0] is shared between channels
self.dds0 = self.get_device("urukul0_ch0")
self.dds1 = self.get_device("urukul2_ch0")
self.trg = self.get_device("ttl4")
self.ftw = self.dds0.frequency_to_ftw(123.2 * MHz)
for dds in [self.dds0, self.dds1]:
Changing to the following works:
A plausible explanation goes as: the phase accumulator only runs if there's a non-0 FTW loaded in the DDS core (otherwise, the SYSCLK-clocked flip-flop just shifts 0s all the time), which is not the case on profile 1 in the incorrect code above. The situation is therefore equivalent to having a phase accumulator reset upon the change to profile 0. The fix is effective only if one sets the same FTW for both profile 0 and profile 1, which relates to your comment about tracking profile-specific virtual oscillators. Does this make sense?
One thing to keep in mind: Are we certain that the PROFILE pins meet S/H w.r.t. SYNC_CLK. Unlike IO_UPDATE they go through the CPLD and are not delay-tuned.
Good point. Originally, the idea was that it shouldn't matter since there're no new values being loaded from the I/O registers, but this is indeed wrong since this also triggers loading new values into the DDS core. Is that correct?