Hello everybody!
We have two Sinaras in our lab with slightly different versions of controlling PC software and gateware:
Sinara 1: ARTIQ version: 7.8166.dbd5a17, ARTIQ dashboard 7.8166dbd5a17
Sinara 2: ARTIQ version: 7.7597.bdaaf3c1.beta, ARTIQ dashboard 7.635.07bd1e27.beta
I wrote a code which uses DRG and RAM modes of AD9910 to create a linear frequency ramp with a Gaussian envelope of amplitude (I need this code to do Rapid Adiabatic Passage population transfer of a Ca+ ion acting on the laser AOM). I tested this code successfully on the Sinara 1, but on the Sinara 2 with older versions of gateware and software, it doesn't work. By commenting on different parts of the code, I found out that writing a list of ASF amplitudes to the memory of AD9910 creates an issue.
@kernel
def write_to_ram(self, dds):
dds.set_cfr1(ram_enable=0) # Disable RAM through CFR 1 register (has to be disabled during writing)
self.cpld0.io_update.pulse_mu(8) # Update I/O to make changes
self.cpld0.set_profile(0) # Enable RAM profile
dds.set_profile_ram(start=0, end=len(self.aa_ram)-1, step=self.t_step_int, profile=0, nodwell_high=0, mode=RAM_MODE_RAMPUP) # Set profile parametrs
#dds.set(frequency=self.f_low, phase=0.0, phase_mode=PHASE_MODE_TRACKING, ram_destination=RAM_DEST_ASF) #Doesn't work on the Sinara 2
dds.set(frequency=self.f_low, phase=0.0, profile=0)
self.cpld0.io_update.pulse_mu(8) # Update I/O to make changes
delay(100*us) #100*us min
'''The next line creates a problem on the Sinara 2'''
dds.write_ram(self.aa_ram) # Write ASF amplitudes to RAM
cfr1 = 0b11000000000000001100000000000010 # Create binary number #Enable RAM=1, ram_destination=RAM_DEST_ASF, Load LRR@ I/O update=1, Autoclear DRG accumulator=1, SDIO input only=1, see datasheet for details.
self.dds0.write32(ad9910._AD9910_REG_CFR1,cfr1) # Eanble RAM and set DRG behaviour, writing to CFR1 register. It needs I/O update pulse.
The error message doesn't indicate the problem with a code. It seems that the code doesn't compile at all on the Sinara 2.
Here is the whole error message (press Details):
(artiq7) C:\Users\Gruppe Willitsch\Desktop\ARTIQ codes\artiq-master\artiq-work\repository\routines stable>artiq_run DDS_fDRG_aRAM_Aleks_cycle_v3.py
<class 'list'>
Traceback (most recent call last):
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\transforms\inferencer.py", line 51, in _unify
typea.unify(typeb)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\types.py", line 91, in unify
self.find().unify(other)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\types.py", line 134, in unify
self.params[param].unify(other.params[param])
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\types.py", line 138, in unify
raise UnificationError(self, other)
artiq.compiler.types.UnificationError: (artiq.compiler.types.TMono('int', OrderedDict([('width', artiq.compiler.types.TValue(32))])), <class 'numpy.int32'>)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\Scripts\artiq_run-script.py", line 9, in <module>
sys.exit(main())
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\frontend\artiq_run.py", line 225, in main
return run(with_file=True)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\frontend\artiq_run.py", line 211, in run
raise exn
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\frontend\artiq_run.py", line 204, in run
exp_inst.run()
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\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:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\coredevice\core.py", line 129, in run
self.compile(function, args, kwargs, set_result)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\coredevice\core.py", line 105, in compile
stitcher.finalize()
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\embedding.py", line 705, in finalize
inferencer.visit(self.typedtree)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 39, in visit
return [self.visit(elt) for elt in obj]
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 39, in <listcomp>
return [self.visit(elt) for elt in obj]
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 41, in visit
return self._visit_one(obj)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 32, in visit_one
return getattr(self, visit_attr)(node)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\transforms\inferencer.py", line 1680, in visit_FunctionDefT
self.generic_visit(node)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 27, in generic_visit
self.visit(getattr(node, field_name))
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 39, in visit
return [self.visit(elt) for elt in obj]
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 39, in <listcomp>
return [self.visit(elt) for elt in obj]
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 41, in visit
return self.visit_one(obj)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 34, in visit_one
return self.generic_visit(node)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 27, in generic_visit
self.visit(getattr(node, field_name))
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 41, in visit
return self.visit_one(obj)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\pythonparser\algorithm.py", line 32, in _visit_one
return getattr(self, visit_attr)(node)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\transforms\inferencer.py", line 1370, in visit_CallT
actualarg.loc, None)
File "C:\Users\Gruppe Willitsch.conda\envs\artiq7\lib\site-packages\artiq\compiler\transforms\inferencer.py", line 73, in _unify
e.typeb.find() == typea.find() and e.typea.find() == typeb.find():
AttributeError: type object 'numpy.int32' has no attribute 'find'
I appreciate it if somebody knows what is the reason or how to modify the code to make it work on the older version of software and hardware. There are already many working routines on this machine, and an update to the newer version possibly brings more problems.
The whole code is here:
"""
Created on 17.06.23
@author: Aleksandr Shlykov
"""
'''
Frequency and amplitude RAMP for Rapid Adiabatic Passage using DRG and RAM of AD9910 https://www.analog.com/media/en/technical-documentation/data-sheets/AD9910.pdf
There is a cycle for evaluating different point of RAP in time.
To test the program on the scope uncomment low frequencies and list of 5 amplitudes.
'''
from artiq.experiment import *
from artiq.coredevice import ad9910
from artiq.coredevice.ad9910 import (RAM_DEST_FTW, RAM_DEST_ASF, RAM_MODE_RAMPUP, RAM_MODE_CONT_RAMPUP, RAM_MODE_BIDIR_RAMP, RAM_MODE_CONT_BIDIR_RAMP)
import math
import numpy as np
PHASE_MODE_CONTINUOUS = 0
PHASE_MODE_ABSOLUTE = 1
PHASE_MODE_TRACKING = 2
class AD9910_DRG(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("ttl4")
self.setattr_device("urukul0_cpld")
self.setattr_device("urukul0_ch0")
self.cpld0 = self.urukul0_cpld
self.dds0 = self.urukul0_ch0
def prepare(self):
'''Uncomment to test the programm with low frequencies on the scope'''
# self.f_low=1*MHz
# dt=4*us
# df=8*MHz
# T=16*us #Required full passage time lenght
# sweep_rate=df/T #Corresponding sweep rate *Hz/s
'''Real parameters, comment to test with low frequencies'''
f=70*MHz #Transition frequency
df=400*kHz # Required chirp range
T=150*us # Required full passage time length
'''Internal parameter of DDS'''
dt=148*ns # Desireble time step. Min is 4 ns, max 262.14 ms according to datasheet. Will be automatically rounded to the value which could be divided by 4*ns without the rest.
'''Time step and frequency calculations'''
self.t_step_int=int(round(dt*1E9/4)) # Actual time step, in machine units, 1*mu=4*ns
self.t_step_real=self.t_step_int*4*1E-9 # Actual time step, in *s
# sweep_rate=df/T #C orresponding sweep rate *Hz/s
# self.f_step=sweep_rate*self.t_step_real # Actual frequency step*Hz
# self.RAP_steps=int(round(T/self.t_step_real)) # Number of RAP steps
self.RAP_steps=1000 # =N (Max is 1024 if want RAM synchronisation, but 1000 doesn't create rounding problems)
self.steps=10 # Number of point in the RAP sequence #=E #works up to 500 points, then bugs and RTIO underflow (reasonable numbers 10-100)
self.RAP_steps_pp=int(round(self.RAP_steps/self.steps)) # Number of ramp steps per experimental step
self.f_step=df/self.RAP_steps # Actual frequency step from number of RAP steps*Hz
sweep_rate=self.f_step/self.t_step_real # Sweep rate accordingly
self.f_low=f-df/2 # Low frequency bound
self.f_high=[0]*self.steps #Empty list to record frequencies
self.passage_time=[0]*self.steps #Empty list to record times
for i in range(self.steps):
self.f_high[i]=self.f_low+self.f_step*self.RAP_steps_pp*(i+1) #High frequency bound for every experimental step, comment to test with low frequencies
self.passage_time[i]=self.t_step_real*self.RAP_steps_pp*(i+1) #Passage time per experimetal step *s
# print('Frequencies', self.f_high, 'Number of elements', len(self.f_high))
# print('Times', self.passage_time, 'Number of elements', len(self.passage_time))
# print('Number of ramp steps', self.RAP_steps)
# print('Number of experimental points ', self.steps)
# print('Number of ramp steps per experimental point', self.RAP_steps_pp)
# print('Full passage actual time is', self.passage_time[self.steps-1]*1E6, 'us')
# print('Actual time step is', self.t_step_real*1E9, 'ns')
# print('Time step in machine units is', self.t_step_int, 'mu')
# print('Frequency step is', self.f_step, 'Hz')
# print('Sweep rate is', sweep_rate*1E-9, 'kHz/us')
'''Frequency parameters for DRG'''
ramp_rate_neg = np.uint16(self.t_step_int) # set negative slope rate bits [31:16]
ramp_rate_pos = np.uint16(self.t_step_int) # set positive slope rate bits [15:0]
ramp_rate = np.uint32((ramp_rate_neg << 16) | ramp_rate_pos) # combine to 32 bit by shifting the negative register by 16 point to the left and using bitwise operation OR (|)
self.ramp_rate = ramp_rate.astype(np.int32)
self.ramp_low=0 #Dummy
self.ramp_high=[0]*self.steps #Dummy
self.ramp_step=0 #Dummy
'''Amplitudes parameters for RAM'''
self.aa=[0]*self.RAP_steps# Initialize zero array of N lenght
self.aa_ram = [0] * len(self.aa) # Initialize zero array of the self.aa lenght
'''Uncomment to test the programm with low frequencies on the scope'''
# self.aa=[0.1,0.2,0.4,0.6,1.0] #Uncomment ot test with low freqencies
# self.aa = [float(value) for value in self.aa]
'''Real parameters, comment to test with low frequencies'''
sigma=self.RAP_steps/(6*math.sqrt(2)) #Gaussian width
phi=self.RAP_steps/2 #Center gaussian maximum on the resonance
max_value = 0 #Initialise max_value
for n in range(self.RAP_steps):
# self.aa[n]=1.0 #to test on the spectrum analuser
self.aa[n]=1*math.exp(-((n-phi)**2)/(2*sigma**2)) #Gaussian envelope of the amplitudes
max_value = max(max_value, self.aa[n])
for n in range(self.RAP_steps): #Normalise maximum amplitude to 1.0
self.aa[n] /= max_value
#print('Amplitudes', self.aa, 'Number of elements', len(self.aa))
'''Functions'''
@kernel
def init_dds(self, dds):
dds.init()
dds.set_att(1.0)
dds.sw.off()
@kernel
def configure_ftw_asf(self, dds):
self.ramp_step = dds.frequency_to_ftw(self.f_step) #Convert freq. in Hz to FTW
self.ramp_low = dds.frequency_to_ftw(self.f_low) #Convert freq. in Hz to FTW
for i in range(self.steps):
self.ramp_high[i] = dds.frequency_to_ftw(self.f_high[i]) #Convert freq. in Hz to FTW
dds.amplitude_to_ram(self.aa, self.aa_ram) #Convert amplitudes to ASF
@kernel
def configure_drg_mode(self, dds):
dds.write32(ad9910._AD9910_REG_RAMP_RATE, self.ramp_rate) # Set ramp rate (time step) in the 32 bits ramp rate register of the DRG
dds.write64(ad9910._AD9910_REG_RAMP_STEP, -self.ramp_step, 0) # Set ramp step (freqency step) in the 64 bits ramp step register of the DRG
cfr2 = ((1 << 19) | (0 << 20)) # Create binary number 00000000000000000001000000000000 (32 bits, bites #[19.20.21]=100 (enable DRG (CRF[19]=1), DRG destination=FTW, (CFR2[21:20]=01), see datasheet for details)
dds.write32(ad9910._AD9910_REG_CFR2, cfr2) # Enable and setup DRG, writing to CRF2. It needs I/O update pulse.
@kernel
def write_to_ram(self, dds):
dds.set_cfr1(ram_enable=0) # Disable RAM trough CFR 1 register (has to be disabled during writing)
self.cpld0.io_update.pulse_mu(8) # Update I/O to make changes
self.cpld0.set_profile(0) # Enable RAM profile
dds.set_profile_ram(start=0, end=len(self.aa_ram)-1, step=self.t_step_int, profile=0, nodwell_high=0, mode=RAM_MODE_RAMPUP) # Set profile parametrs
#dds.set(frequency=self.f_low, phase=0.0, phase_mode=PHASE_MODE_TRACKING, ram_destination=RAM_DEST_ASF) #Doesn't work on the Sinara 2
dds.set(frequency=self.f_low, phase=0.0, profile=0)
self.cpld0.io_update.pulse_mu(8) # Update I/O to make changes
delay(100*us) #100*us min
'''Thie next line creates a problem on the Sinara 2'''
dds.write_ram(self.aa_ram) # Write ASF amplitudes to RAM
cfr1 = 0b11000000000000001100000000000010 # Create binary number #Enable RAM=1, ram_destination=RAM_DEST_ASF, Load LRR@ I/O update=1, Autoclear DRG accumulator=1, SDIO input only=1, see datasheet for details.
self.dds0.write32(ad9910._AD9910_REG_CFR1,cfr1) # Eanble RAM and set DRG behaviour, writing to CFR1 register. It needs I/O update pulse.
'''Here programm runs'''
@kernel
def run(self):
self.core.reset() #Resets core device
self.ttl4.output() # Sets TTL4 as an output
self.cpld0.init() # Init CPLD
self.init_dds(self.dds0)
self.configure_ftw_asf(self.dds0) # Create machine units
delay(2300*us) #2300*us min for 500 points
self.configure_drg_mode(self.dds0) # Configure constant DRG parameters. It needs I/O update pulse.
delay(50*us) #50*us min
self.write_to_ram(self.dds0) # Write amplitude list to the RAM, enable RAM profile and set DRG behaviour. It needs I/O update pulse.
delay(50*us) #50*us min
'''Experimental cycle starts here'''
for i in range(self.steps):
self.dds0.write64(ad9910._AD9910_REG_RAMP_LIMIT, self.ramp_high[i], self.ramp_low) # Set frequency ramp limits in the 64 bits ramp limit register of the DRG, needs I/O update
self.cpld0.io_update.pulse_mu(8) # Update I/O to start the DDS
with parallel:
self.dds0.sw.on()
self.ttl4.pulse(4*us)
with sequential:
delay(self.passage_time[i]*s)
self.dds0.sw.off()