Hi ARTIQ community,
I’ve recently been trying to implement a dynamical decoupling sequence using a TTL channel and the phaserboard (upconverter varient). The sequence looks like this:

The green (TTL) initialization and readout are typically about 1 us and a few hundred nanoseconds respectively. The RF pulses are generally in the 10’s of nanoseconds (50-100) if we disregard the minimum pulse width from the phaser board. Tau is swept variable, but generally starts in the low 10’s of nanoseconds as well. Ultimately the pulse sequence is very fast.
To achieve longer experiments without underflow, I’ve implemented it all with DMA, where a single pulse sequence is recorded for all values of tau, and are then repeated however times we specify:
@kernel
def record(self, name, index):
with self.core_dma.record(name):
# self.core.break_realtime() DO NOT BREAK REALTIME IN DMA
delay(self.post_rf)
cursor = now_mu()
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(self.rf_amplitude, phase=0.)
delay(self.rf_pi2_time)
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(0.0, phase=0.)
# delay(self.readout_padding)
for iteration in range(self.N_iterations): #rf sequence for every iteration
delay(self.tau_list[index])
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(self.rf_amplitude, phase=0.)
delay(self.rf_pi_time)
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(0.0, phase=0.)
# delay(self.readout_padding)
delay(self.tau_list[index])
delay(self.tau_list[index])
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(self.rf_amplitude, phase=0.)
delay(self.rf_pi_time)
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(0.0, phase=0.)
# delay(self.readout_padding)
delay(self.tau_list[index])
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(self.rf_amplitude, phase=0.)
delay(self.rf_pi2_time)
self.phaser0.channel[1].oscillator[0].set_amplitude_phase(0.0, phase=0.)
delay(self.readout_padding)
# green laser sequence
at_mu(cursor)
delay(self.ttl_start) #ttl/phaser delay compensation
cursor = now_mu()
self.ttl6.pulse(self.green_init_duration)
delay(self.rf_pi2_time)
delay(self.all_N_pulse_repetition_length[index])
delay(self.rf_pi2_time)
delay(self.readout_padding)
cursor = now_mu() # updates to right where readout happens
self.ttl6.on()
delay(self.counting_duration)
self.ttl6.off()
at_mu(cursor)
delay(self.ttl_delay) #Photon Counting/Green delay compensation
self.ttl0_counter.set_config(count_rising=True, count_falling=False, send_count_event=False,
reset_to_zero=False)
delay(self.counting_duration)
self.ttl0_counter.set_config(count_rising=False, count_falling=False, send_count_event=False,
reset_to_zero=False)
delay(self.post_rf)
Naming is a little off here but self.post_rf is just delay padding on either end of the experiment.
In the context of the run statement in the experiment:
@kernel
def run(self):
self.turn_off_laser()
self.phaser_init()
self.phaser0.channel[1].set_duc_frequency((self.freq_resonant - self.freq_center) * MHz) # set frequency (offset from center)
self.phaser0.duc_stb()
print("list of all times:", self.tau_list, "data size:", self.data_size)
self.set_dataset("times", self.tau_list, broadcast=False)
self.set_dataset("on", np.full(self.data_size, np.nan), broadcast=False)
self.core.reset()
self.core.break_realtime()
for i in range(self.data_size):
print(i)
self.core.break_realtime()
handle = self.core_dma.get_handle(self.DMA_names[i])
delay(500*us)
self.ttl0_counter.set_config(count_rising=False, count_falling=False, send_count_event=False,
reset_to_zero=True)
for j in range(self.n_cycles):
self.core_dma.playback_handle(handle)
self.ttl0_counter.set_config(count_rising=False, count_falling=False, send_count_event=True,
reset_to_zero=False)
self.mutate_dataset("on", i, self.ttl0_counter.fetch_count())
self.core.break_realtime()
self.ttl6.off()
self.delete_all() #delete DMA recordings
self.core.break_realtime()
self.phaser0.set_cfg(dac_txena=0)
print(self.data_size)
print("experiment done")
The main issue I have is that there seems to be a hard cutoff for the N_iterations (N in figure) we specify in the code. Typically, I deal with underflow by adding delay between each sequence repetition that pads the slack loss during the experiment. However, I find that at a certain N_iterations value (62), no matter how much delay I pad the experiment by, it will always underflow.
ARTIQ will always say that the underflow occurs within the handle playback operation:
root:Terminating with exception (RTIOUnderflow: RTIO underflow at 2852735500885580 mu, channel 23)
Core Device Traceback:
Traceback (most recent call first):
File "ksupport/lib.rs", line 416, column 18, in (Rust function)
<unknown>
^
File "C:\Users\DQP\repos\dqpcontrol\artiq-master\repository\dds_enrique_DMA.py", line 75, in artiq_worker_dds_enrique_DMA.DDS_DMA.run(..., ...) (RA=+0xc80)
self.core_dma.playback_handle(handle)
File "<artiq>\coredevice\dma.py", line 114, in ?? (RA=+0x155c)
dma_playback(now_mu(), ptr)
artiq.coredevice.exceptions.RTIOUnderflow(0): RTIO underflow at 2852735500885580 mu, channel 23
End of Core Device Traceback
Traceback (most recent call last):
File "C:\ProgramData\anaconda3\envs\artiq\lib\site-packages\artiq\master\worker_impl.py", line 343, in main
exp_inst.run()
File "C:\ProgramData\anaconda3\envs\artiq\lib\site-packages\artiq\language\core.py", line 54, in run_on_core
return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs)
File "C:\ProgramData\anaconda3\envs\artiq\lib\site-packages\artiq\coredevice\core.py", line 140, in run
self._run_compiled(kernel_library, embedding_map, symbolizer, demangler)
File "C:\ProgramData\anaconda3\envs\artiq\lib\site-packages\artiq\coredevice\core.py", line 130, in run_compiled
self.comm.serve(embedding_map, symbolizer, demangler)
File "C:\ProgramData\anaconda3\envs\artiq\lib\site-packages\artiq\coredevice\comm_kernel.py", line 716, in serve
self.serve_exception(embedding_map, symbolizer, demangler)
File "C:\ProgramData\anaconda3\envs\artiq\lib\site-packages\artiq\coredevice\comm_kernel.py", line 698, in _serve_exception
raise python_exn
artiq.coredevice.exceptions.RTIOUnderflow: RTIO underflow at 2852735500885580 mu, channel 23
Analysis with gtkWave after exporting the vcd shows that the experiment seems to underflow at the first TTL pulse. (this is at large time value due to other experiments also getting in the VCD)

My assumption is that this is tied to the DMA playback in some capacity, but can’t seem to point to anything specific that I could change in my code to address it.
I've tried adding delays in many spots to compensate for the slack, but none have worked. Some things I have tried:
- Delays at various stages before playback_handle is called
- Increasing delay at start/end of sequence within the DMA
- Increasing starting Tau and pi_pulse time (100’s ns)
- removing print statements
Hopefully I’ve provided a good amount of information, but if you want to help and need any more additional information, I will be happy to provide it. Thanks!