04 - Reading, math, and writing
Objective: Use the tProc to do some math and/or bitwise operations on two variables that you will pass in through the config dictionary and take out with single_read .
[1]:
# Import the QICK drivers and auxiliary libraries
from qick import *
import time
%pylab inline
Populating the interactive namespace from numpy and matplotlib
[2]:
# Load bitstream with custom overlay
soc = QickSoc()
soccfg = soc
Hardware Configuration
tProc channel 7 : DAC 229 CH3 <-> Readout channel 0 : ADC 224 CH0
[3]:
class LoopbackProgram(AveragerProgram):
def initialize(self):
cfg=self.cfg
# Move value of A into tProc register
self.regwi(0,1,self.cfg["A"])
self.synci(200) # give processor some time to configure pulses
def body(self):
# Do math: add value of B to the register value
self.mathi(0, 1, 1, '+', self.cfg["B"])
# Write the result to address 123
self.memwi(0,1,123)
Here we will add the value of A to the value of B using the tProc. We write the result to tProc memory address 123, and then read out that address value at the end of the program.
[5]:
config={"reps": 1, # fixed
"A": 100,
"B": -50
}
prog =LoopbackProgram(soccfg, config)
# Load the program into the tProc.
prog.config_all(soc)
# Start tProc.
soc.start_tproc()
time.sleep(0.1)
result = soc.tproc.single_read(addr=123)
print("Result = ", result)
Result = 50
Here we show how similar register operations can also be done using the QickRegister object.
The QickRegister object keeps the register page, address, gen_ch/ro_ch, and reg_type information, so unit conversions like freq2reg/us2cycle/deg2reg can be automatically used when we assign values to registers. This makes qick programs easier to write and read especially when a lot of operations on registers that correspond to physical values are involved.
In the program below, we assign values to the frequency register of a generator, all the assignments are done directly using frequency values in MHz, and no explicit reference to the low level register value/page/address is needed.
[6]:
from qick.asm_v1 import QickRegister, QickRegisterManagerMixin
[7]:
class TestRegProgram(QickRegisterManagerMixin, AveragerProgram): # inhered the QickRegisterManagerMixin class
def initialize(self):
cfg = self.cfg
self.declare_gen(ch=cfg["gen_ch"], nqz=1, ro_ch=cfg["ro_ch"]) # this "ro_ch" will be automatically used for freq2reg operations later
# get the frequency register associated with gen_ch
self.gen_freq_reg = self.get_gen_reg(cfg["gen_ch"], "freq")
# declare another frequency type register on the same generator page, initialize it to the integer that corresponds to frequency "f1" in MHz
# the "gen_ch" and "ro_ch" of cfg["gen_ch"] will be automatically used when converting a physical frequency value to integers of this register
self.gen_freq_reg_temp = self.new_gen_reg(cfg["gen_ch"], name="freq_temp", init_val=cfg["f1"], reg_type="freq")
self.synci(200) # give processor some time to configure pulses
def body(self):
cfg = self.cfg
# set the register value to the integer that corresponds to frequency "f0" in MHz
self.gen_freq_reg.set_to(cfg["f0"])
# Write the result to address 123
self.memwi(self.gen_freq_reg.page, self.gen_freq_reg.addr, 123)
# assign the value of register "gen_freq_reg_temp" to register "gen_freq_reg", which should corresponds to "f1"
self.gen_freq_reg.set_to(self.gen_freq_reg_temp)
# Write the result to address 124
self.memwi(self.gen_freq_reg.page, self.gen_freq_reg.addr, 124)
# add 300 MHz to the frequency kept in "gen_freq_reg_temp" and assign it to "gen_freq_reg"
self.gen_freq_reg.set_to(self.gen_freq_reg_temp, "+", 300)
# Write the result to address 125
self.memwi(self.gen_freq_reg.page, self.gen_freq_reg.addr, 125)
# sum the frequencies kept in "gen_freq_reg_temp" and "gen_freq_reg" and assign it to "gen_freq_reg"
self.gen_freq_reg.set_to(self.gen_freq_reg, "+", self.gen_freq_reg_temp)
# Write the result to address 126
self.memwi(self.gen_freq_reg.page, self.gen_freq_reg.addr, 126)
[7]:
config={
"gen_ch": 0,
"ro_ch": 0,
"reps": 1, # fixed
"f0": 1000, #MHz
"f1": 500 #MHz
}
prog =TestRegProgram(soccfg, config)
# Load the program into the tProc.
prog.config_all(soc)
# Start tProc.
soc.start_tproc()
print(prog)
time.sleep(0.1)
# compare the result with what we should get using freq2reg functions
result = soc.tproc.single_read(addr=123)
print(f"Result=", result, ", expected:", prog.freq2reg(config["f0"], config["gen_ch"], config["ro_ch"]))
result = soc.tproc.single_read(addr=124)
print(f"Result=", result, ", expected:",prog.freq2reg(config["f1"], config["gen_ch"], config["ro_ch"]))
result = soc.tproc.single_read(addr=125)
print(f"Result=", result, ", expected:",prog.freq2reg(config["f1"]+300, config["gen_ch"], config["ro_ch"]))
result = soc.tproc.single_read(addr=126)
print(f"Result=", result, ", expected:",prog.freq2reg(config["f1"]+300+config["f1"], config["gen_ch"], config["ro_ch"]))
// Program
regwi 0, $1, 312076190; //'freq_temp' <= 312076190 (500 MHz)
synci 200;
regwi 0, $15, 0;
regwi 0, $14, 0;
LOOP_J: regwi 0, $22, 624152380; //'gen0_freq' <= 624152380 (1000 MHz)
memwi 0, $22, 123;
mathi 0, $22, $1 + 0; // 'gen0_freq' <= 'freq_temp' + 0 (0 MHz)
memwi 0, $22, 124;
mathi 0, $22, $1 + 187245715; // 'gen0_freq' <= 'freq_temp' + 187245715 (300 MHz)
memwi 0, $22, 125;
math 0, $22, $22 + $1; // 'gen0_freq' <= 'gen0_freq' + 'freq_temp'
memwi 0, $22, 126;
mathi 0, $15, $15 + 1;
memwi 0, $15, 1;
loopnz 0, $14, @LOOP_J;
end ;
Result= 624152380 , expected: 624152380
Result= 312076190 , expected: 312076190
Result= 499321905 , expected: 499321905
Result= 811398095 , expected: 811398095
[ ]: