Hi
I would like to apologise in advance for the lengthy post but I wanted to explain my situation and my approach so that people could make an informed answer.
I am looking to run a series of experiments using the scheduler to atleast microsecond timing resolution and hopefully better. Each of the stages is looking to run for a duration of 5 to 500 ms, therefore the timing of each experiment is very important. I have a stand alone script which initialises the 16 TTL channels, 2 DDS boards (8 channels in total) and the analog input and output channels. Therefore there is no time wasted on initialisation in each of the stages, as each of these stages are subclasses of the initialisation script. There are in total 31 stages, and I have included the ability to comment out any particular stage from the sequence and therefore there is no definite start or end stage. For the purposes of understanding the RTIO underflow errors I have been receiving, I have taken a simplified example of only 4 stages where each stage only contains the 4 DDS AD9910 channels. I have attached an example below. You can see that the stage uses get_arguments to obtain the arguments I set in the scheduler script, please find this attached as well. Within the RUN section of the stage please note that I have commented out self.core.reset and in artiq_master I got the error stating that there
ERROR:worker(2480,test_1.py):root:Terminating with exception (RTIOUnderflow: RTIO underflow at 49997647331512 mu, channel 29, slack -2974447520 mu)
Core Device Traceback (most recent call last):
File "C:\Users\Strontieum\Documents\artiq\repository/test_1.py", line 45, in artiq_worker_test_1.ad9910_test1.run(..., ...) (RA=+0xadc)
this corresponds to an error in channel 29 which is the SPI master in the AD9910 DDS board. However I do not understand why there is such a large negative slack. When using core.reset this negative slack has decreased to -104896 mu slack at the point where I begin setting the frequencies and amplitudes. I understand the errors occur at this point because there are no delays for the previous operation of setting the attenuation (which I has measured to 1.6 us) however could you please explain why there is this huge negative slack when the core device is not reset.
I have another question on fixing the sequence errors for the urukul operations, should I place a 1.6us delay after each zero duration operation e.g setting attenuation or setting frequency, or is it better to place a delay before these operations. What is the difference between the two approaches?
Finally I want each stage to run for a set amount of time therefore I have set one of the arguments set from the scheduler as the time duration value. Within each stage I have calculated the time taken in the run stage and subtracted this from the time duration value then calculated the remaining delay which is placed in the self.core.wait_until_mu(now_mu()) function.
I have used this function to ensure the kernel waits untill all the events have been execueted, before exiting the experiment. Could someone please advise me on whether this is the right approach. The timing for each run stage including core.reset has been measured using the RTIO wall clock, again is this the right approach?
In the end I want to place all the TTL, DDS, analog, frequency amplitude modulation functions within the run section. Is this the correct approach or should I call them through separate scripts with defined class e.g script to perform frequency modulation. I have been stuck on this for a month and would really appreciate expert/experienced advise on the right approach.
@lriesebos @sb10q @drewrisinger @connorgoham @jbqubit
Stage script
`
from artiq.experiment import *
#all the experiments have to be a subclass of the base_experiment
import init_1
class ad9910_test1(init_1.init_1):
"""ad9910_test"""
def build(self):
super().build()
# self.setattr_device("core")
# self.setattr_device("urukul1_ch2")
self._freq0 = self.get_argument("freq0", NumberValue(ndecimals=2, unit="MHz", step=1))
self._amp0 = self.get_argument("amp0", NumberValue(ndecimals=2, step=0.01))
self._atten0 = self.get_argument("atten0", NumberValue(ndecimals=2, step=0.01))
self._t_pulse0 = self.get_argument("t_pulse0", NumberValue(ndecimals=2, unit = "s", step=0.01))
self._freq1 = self.get_argument("freq1", NumberValue(ndecimals=2, unit="MHz", step=1))
self._amp1 = self.get_argument("amp1", NumberValue(ndecimals=2, step=0.01))
self._atten1 = self.get_argument("atten1", NumberValue(ndecimals=2, step=0.01))
self._freq2 = self.get_argument("freq2", NumberValue(ndecimals=2, unit="MHz", step=1))
self._amp2 = self.get_argument("amp2", NumberValue(ndecimals=2, step=0.01))
self._atten2 = self.get_argument("atten2", NumberValue(ndecimals=2, step=0.01))
self._freq3 = self.get_argument("freq3", NumberValue(ndecimals=2, unit="MHz", step=1))
self._amp3 = self.get_argument("amp3", NumberValue(ndecimals=2, step=0.01))
self._atten3 = self.get_argument("atten3", NumberValue(ndecimals=2, step=0.01))
@kernel
def run(self):
t0 = self.core.get_rtio_counter_mu()
# first = now_mu()
self.core.reset()
# first = now_mu()
#t0 = self.core.get_rtio_counter_mu()
#delay(200*us)
# print(now_mu()-self.core.get_rtio_counter_mu()
#t0 = self.core.get_rtio_counter_mu()
# self.DDS_ad9910_channel2.cpld.init()
# self.DDS_ad9910_channel2.init()
# delay(100* us)
# delay(100* ms)
#first = now_mu()
self.DDS_ad9910_channel0.set_att(self._atten0)
# print(now_mu()-first, "1")
delay(1600 * ns)
# first = now_mu()
self.DDS_ad9910_channel1.set_att(self._atten1)
# print(now_mu()-first, "2")
delay(1600 * ns)
# first = now_mu()
self.DDS_ad9910_channel2.set_att(self._atten2)
# print(now_mu()-first, "3")
delay(1600 * ns)
# first = now_mu()
self.DDS_ad9910_channel3.set_att(self._atten3)
# print(now_mu()-first, "4")
delay(1600 * ns)
#delay(1* ms)
self.DDS_ad9910_channel0.set(self._freq0 * MHz, amplitude = self._amp0)
delay(1256 * ns)
self.DDS_ad9910_channel1.set(self._freq1 * MHz, amplitude = self._amp1)
delay(1256 * ns)
self.DDS_ad9910_channel2.set(self._freq2 * MHz, amplitude = self._amp2)
delay(1256 * ns)
self.DDS_ad9910_channel3.set(self._freq3 * MHz, amplitude = self._amp3)
delay(1256 * ns)
with parallel:
self.DDS_ad9910_channel0.sw.on()
self.DDS_ad9910_channel1.sw.on()
self.DDS_ad9910_channel2.sw.on()
self.DDS_ad9910_channel3.sw.on()
t1 = self.core.get_rtio_counter_mu()
delay((self._t_pulse0 * s) - (self.core.mu_to_seconds(t1-t0)) )
# delay(self._t_pulse0 * s)
self.core.wait_until_mu(now_mu())
`
Scheduler script
`
import init_1
#create a scheduler
class Scheduling_experiments2(init_1.init_1):
def build(self):
super().build()
self.setattr_device("core")
self.setattr_device("scheduler")
def run(self):
self.core.reset()
expid_1 = {
"file": "test_1.py",
"class_name": "ad9910_test1",
"arguments": {"freq0":10, "amp0":1, "atten0":0, "t_pulse0":30, "freq1":50, "amp1":1, "atten1":0,"freq2":40, "amp2":1, "atten2":0, "freq3":30, "amp3":1, "atten3":0},
"log_level": self.scheduler.expid["log_level"],
"repo_rev": self.scheduler.expid["repo_rev"],
}
expid_2 = {
"file": "test_2.py",
"class_name": "ad9910_test2",
"arguments": {"freq0":20, "amp0":1, "atten0":0, "t_pulse0":20, "freq1":100, "amp1":1, "atten1":0,"freq2":40, "amp2":1, "atten2":0, "freq3":30, "amp3":1, "atten3":0},
"log_level": self.scheduler.expid["log_level"],
"repo_rev": self.scheduler.expid["repo_rev"],
}
expid_3 = {
"file": "test_3.py",
"class_name": "ad9910_test3",
"arguments": {"freq0":30, "amp0":1, "atten0":0, "t_pulse0":20, "freq1":20, "amp1":1, "atten1":0,"freq2":40, "amp2":1, "atten2":0, "freq3":30, "amp3":1, "atten3":0},
"log_level": self.scheduler.expid["log_level"],
"repo_rev": self.scheduler.expid["repo_rev"],
}
expid_4 = {
"file": "test_4.py",
"class_name": "ad9910_test4",
"arguments": {"freq0":40, "amp0":1, "atten0":0, "t_pulse0":20, "freq1":100, "amp1":1, "atten1":0,"freq2":40, "amp2":1, "atten2":0, "freq3":30, "amp3":1, "atten3":0},
"log_level": self.scheduler.expid["log_level"],
"repo_rev": self.scheduler.expid["repo_rev"],
}
for x in range(1):
self.scheduler.submit("main", expid_1)
self.scheduler.submit("main", expid_2)
self.scheduler.submit("main", expid_3)
self.scheduler.submit("main", expid_4)
`