I was since able to investigate a bit further, and it seems the function set_phase_mode()
is the culprit here.
Some example code triggering the behavior is this:
@kernel
def run(self):
SHOTS = 50
is_standby = True
STANDBY_CHUNK_DURATION = 1
while True:
self.core.break_realtime()
if is_standby:
self.krn_update_standby()
delay(STANDBY_CHUNK_DURATION * s)
is_standby = False
else:
self.krn_create_sequence_dma()
sequence_handle = self.core_dma.get_handle("sequence")
for k in range(SHOTS):
self.core.break_realtime()
self.core_dma.playback_handle(sequence_handle)
is_standby = True
This loop just alternates between a "standby mode" and a "sequence mode", where the latter creates a DMA sequence and plays back 50 times.
The DMA sequence is created like that ( I have 12 Urukul channels available in dds_devs[]
):
@kernel
def krn_create_sequence_dma(self):
with self.core_dma.record("sequence"):
with parallel:
self.krn_set_dds_state()
self.krn_set_digital_out_state()
@kernel
def krn_set_dds_state(self):
FRQUENCY_MHZ = 150
PHASE = 0.0
AMPLITUDE = 0.3
DDS_CHANNELS = 12
for i in range(DDS_CHANNELS):
delay(10 * us)
self.dds_devs[i].set(FRQUENCY_MHZ * MHz,
phase=PHASE,
amplitude=AMPLITUDE)
for i in range(DDS_CHANNELS):
delay(10 * us)
self.dds_devs[i].sw.on()
delay(10 * us)
self.dds_devs[i].sw.off()
@kernel
def krn_set_digital_out_state(self):
self.digital_out_devs[0].pulse(5 * ms)
delay(5 * ms)
Now for the "standby mode" (this is, where I'm setting the phase mode):
@kernel
def krn_update_standby(self):
self.core.break_realtime()
self.krn_update_digital_out_standby()
self.krn_update_dds_standby()
return
@kernel
def krn_update_digital_out_standby(self):
self.core.break_realtime()
DIGITAL_OUT_CHANNELS = 19
for i in range(DIGITAL_OUT_CHANNELS):
self.core.break_realtime()
self.digital_out_devs[i+1].pulse(100 *us)
delay(100 * us)
return
@kernel
def krn_update_dds_standby(self):
self.core.break_realtime()
FREQUENCY_MHZ = 150
PHASE = 0.0
AMPLITUDE = 0.5
STANDBY_CHANNELS = 4 # <- critical parameter
for i in range(STANDBY_CHANNELS):
self.core.break_realtime()
self.dds_devs[i].set_phase_mode(3) # <- critical function
delay(1 * ms)
self.dds_devs[i].set(FREQUENCY_MHZ * MHz, phase=PHASE, amplitude=AMPLITUDE)
for i in range(STANDBY_CHANNELS):
self.dds_devs[i].sw.on()
delay(10 * ms)
self.dds_devs[i].sw.off()
return
One would expect, that the DMA sequence is executed 50 times, and there are therefore 50 digital pulses output on DIO channel 0.
But this isn't what's happening:
Seemingly randomly, there are only between 35 and 50 digital pulses created.
If I remove the call to set_phase_mode()
in the non-DMA part, the DMA sequence works as intended.
Those pulses are silently omitted, I only found when actually checking the output.
I couldn't find any errors, exceptions or any other indication, that something was wrong.
Moreover, with set_phase_mode()
in place, the amount of "surviving" digital pulses in dependent on STANDBY_CHANNELS
: If this number is decreased, less pulses are omitted, and if it is increased above 6, no pulses are left at all.
I'm aware of the comment in the documentation, that set_phase_mode()
can be problematic when used in a DMA sequence, but that is not the case here.
To me, this looks like a bug.
But before filing it, I wanted to check with the reads of this forum, if I'm getting something wrong.
Thanks for reading, and sorry for the large amount of example code!
Max