09 - Appendix: Tips, Limits, and Troubleshooting
Objective: This reference notebook covers common pitfalls, hardware limits, debugging techniques, and advanced parameters for tProc v2 programs. Use this as a reference when things don’t work as expected.
1. Setup
[1]:
# Standard imports
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import logging
from qick import *
from qick.asm_v2 import AveragerProgramV2, QickSweep1D, AsmV2
# Setup logging (set to DEBUG for detailed output)
logging.basicConfig(level=logging.INFO, format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s')
logging.getLogger("qick_processor").setLevel(logging.WARNING)
# Connect to the board (adjust the path to your firmware)
BITSTREAM_PATH = 'qick_216.bit'#'/path/to/your/firmware.bit' # <--- CHANGE THIS
soc = QickSoc(BITSTREAM_PATH)
soccfg = soc
# Define hardware channels
GEN_CH = 0
RO_CH = 0
2. tProc Hardware Limits
Understanding the tProc’s limitations will help you design programs that fit within hardware constraints.
[2]:
# Display tProc configuration from firmware
print("=== tProc Configuration ===")
if hasattr(soccfg, 'tproc'):
print(f"Program Memory: {soccfg.tproc['pmem_size']} words")
print(f"Data Memory: {soccfg.tproc['dmem_size']} words")
print(f"Waveform Memory: {soccfg.tproc['wmem_size']} words")
print(f"Core Clock: {soccfg.tproc['f_core']} MHz")
print(f"Timing Clock: {soccfg.tproc['f_time']} MHz")
print(f"tProc Version: {soccfg.tproc['version']}")
print(f"Revision: {soccfg.tproc['revision']}")
print(f"Has LFSR: {bool(soccfg.tproc['has_lfsr'])}")
print(f"Has Divider: {bool(soccfg.tproc['has_divider'])}")
print(f"Has Arith: {bool(soccfg.tproc['has_arith'])}")
else:
print("tProc info not available in soccfg")
=== tProc Configuration ===
Program Memory: 4096 words
Data Memory: 16384 words
Waveform Memory: 1024 words
Core Clock: 215.04 MHz
Timing Clock: 430.08 MHz
tProc Version: 2.0
Revision: 26
Has LFSR: True
Has Divider: True
Has Arith: True
2.1 Program Memory Limit (4096 words)
Each tProc instruction occupies 72 bits (9 bytes). Complex programs with many loops and sweeps can approach this limit.
How to check program size:
[3]:
# Create a simple program to check its size
class SizeTestProgram(AveragerProgramV2):
def _initialize(self, cfg):
self.declare_gen(ch=cfg['gen_ch'], nqz=1)
self.declare_readout(ch=cfg['ro_ch'], length=cfg['ro_len'])
self.add_pulse(ch=cfg['gen_ch'], name="test", style="const",
freq=100, ro_ch=cfg['ro_ch'], length=0.1, phase=0, gain=1.0)
self.add_readoutconfig(ch=cfg['ro_ch'], name="ro", freq=100, gen_ch=cfg['gen_ch'])
self.send_readoutconfig(ch=cfg['ro_ch'], name="ro", t=0)
def _body(self, cfg):
self.pulse(ch=cfg['gen_ch'], name="test", t=0)
self.trigger(ros=[cfg['ro_ch']], pins=[0], t=0.25)
config = {'gen_ch': GEN_CH, 'ro_ch': RO_CH, 'ro_len': 0.5}
prog = SizeTestProgram(soccfg, reps=1, final_delay=0.5, cfg=config)
prog.compile()
print(f"Program size: {len(prog.binprog['pmem'])} instructions")
print(f"Memory usage: {len(prog.binprog['pmem']) / soccfg.tproc['pmem_size'] * 100:.1f}%")
Program size: 20 instructions
Memory usage: 0.5%
2.2 Label Limit (2048 instruction offset)
Jump instructions use 11 bits for address literals, limiting forward jumps to 2048 instructions. This affects loops and conditionals.
Workarounds:
Keep your program compact
Use
reps_innermost=Truefor large sweeps (loops are nested differently)Break large programs into multiple smaller ones
2.3 Time Limits (32-bit signed integers)
Timing parameters use 32-bit signed integers. The maximum time depends on the timing clock frequency.
[4]:
timing_clk_MHz = soccfg.tproc['f_time'] # e.g., 491.52 MHz
max_time_us = (2**31 - 1) / timing_clk_MHz
print(f"Maximum delay: {max_time_us:.0f} µs ({max_time_us/1e6:.1f} seconds)")
Maximum delay: 4993219 µs (5.0 seconds)
For most experiments, this is not a limiting factor.
3. Common Errors and Solutions
3.1 Pulse Collision Warnings
Warning: Pulse {name} overlaps with previous pulse
Cause: A pulse is scheduled before the previous pulse has finished.
Solution: Use delay_auto() to ensure proper spacing.
# Wrong:
self.pulse(ch=gen_ch, name="pulse1", t=0)
self.pulse(ch=gen_ch, name="pulse2", t=0.3) # May overlap
# Correct:
self.pulse(ch=gen_ch, name="pulse1", t=0)
self.delay_auto(gap=0.1) # Wait for pulse1 to finish + 0.1 µs
self.pulse(ch=gen_ch, name="pulse2", t=0)
3.2 Readout Latency Mismatch
Symptom: Feedback readout values don’t match buffered readout values.
Cause: read_wait is too short; the tProc reads stale data.
Solution: Increase read_wait until values match (see Notebook 04).
3.3 Sweep Value Quantization
Symptom: Swept values don’t match the requested range exactly.
Cause: Hardware parameters have limited precision (e.g., gain is 15-bit, phase is 32-bit).
Solution: Use get_pulse_param() to retrieve actual values for accurate analysis.
[5]:
# Example: Check quantization effects
requested_gains = np.linspace(0, 1, 10)
# In a real program, you would do:
# actual_gains = prog.get_pulse_param('pulse', 'gain', as_array=True)
# Simulate quantization (15-bit gain)
max_gain_int = 32766
actual_gains = np.round(requested_gains * max_gain_int) / max_gain_int
print("Requested vs Actual Gains:")
for req, act in zip(requested_gains[:5], actual_gains[:5]):
print(f" {req:.4f} -> {act:.4f}")
Requested vs Actual Gains:
0.0000 -> 0.0000
0.1111 -> 0.1111
0.2222 -> 0.2222
0.3333 -> 0.3333
0.4444 -> 0.4445
3.4 Buffer Overflow
Symptom: Data appears truncated or corrupted.
Cause: More data was captured than the buffer can hold.
Solution: Increase buffer size or reduce acquisition length.
[6]:
# Estimate required buffer size
reps = 100
sweep_steps = 50
readout_samples = 256 # decimated samples
total_samples = reps * sweep_steps * readout_samples
required_transfers = np.ceil(total_samples / 256)
print(f"Required transfers: {required_transfers}")
Required transfers: 5000.0
4. Debugging Techniques
4.1 Enable Detailed Logging
Set logging level to DEBUG to see detailed assembler output.
[7]:
# Enable debug logging
logging.getLogger("qick.tprocv2_assembler").setLevel(logging.DEBUG)
# Run a simple program to see debug output
class DebugProgram(AveragerProgramV2):
def _initialize(self, cfg):
self.declare_gen(ch=cfg['gen_ch'], nqz=1)
self.declare_readout(ch=cfg['ro_ch'], length=cfg['ro_len'])
self.add_pulse(ch=cfg['gen_ch'], name="test", style="const",
freq=100, ro_ch=cfg['ro_ch'], length=0.1, phase=0, gain=1.0)
self.add_readoutconfig(ch=cfg['ro_ch'], name="ro", freq=100, gen_ch=cfg['gen_ch'])
self.send_readoutconfig(ch=cfg['ro_ch'], name="ro", t=0)
def _body(self, cfg):
self.pulse(ch=cfg['gen_ch'], name="test", t=0)
self.trigger(ros=[cfg['ro_ch']], pins=[0], t=0.25)
config = {'gen_ch': GEN_CH, 'ro_ch': RO_CH, 'ro_len': 0.5}
prog = DebugProgram(soccfg, reps=1, final_delay=0.5, cfg=config)
# Reset logging to INFO after debugging
logging.getLogger("qick.tprocv2_assembler").setLevel(logging.WARNING)
DEBUG [tprocv2_assembler.py:1017] LIST2BIN: ##### LIST 2 BIN
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'NOP', 'P_ADDR': 0}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'REG_WR', 'DST': 's12', 'SRC': 'imm', 'LIT': '#0', 'P_ADDR': 1, 'LINE': 1}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'WPORT_WR', 'DST': '4', 'SRC': 'wmem', 'ADDR': '&1', 'TIME': '@0', 'P_ADDR': 2, 'LINE': 2}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TIME', 'C_OP': 'inc_ref', 'LIT': '#430', 'P_ADDR': 3, 'LINE': 3}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'REG_WR', 'DST': 'r0', 'SRC': 'imm', 'LIT': '#0', 'P_ADDR': 4, 'LINE': 4}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'WPORT_WR', 'DST': '0', 'SRC': 'wmem', 'ADDR': '&0', 'TIME': '@0', 'P_ADDR': 5, 'LINE': 6}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TRIG', 'SRC': 'set', 'DST': '0', 'TIME': '@108', 'P_ADDR': 6, 'LINE': 7}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TRIG', 'SRC': 'set', 'DST': '10', 'TIME': '@108', 'P_ADDR': 7, 'LINE': 8}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TRIG', 'SRC': 'clr', 'DST': '0', 'TIME': '@118', 'P_ADDR': 8, 'LINE': 9}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TRIG', 'SRC': 'clr', 'DST': '10', 'TIME': '@118', 'P_ADDR': 9, 'LINE': 10}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'REG_WR', 'DST': 's15', 'SRC': 'label', 'LABEL': 'SKIP', 'P_ADDR': 10, 'LINE': 11, 'ADDR': '&12'}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'WAIT', 'ADDR': 's15', 'C_OP': 'time', 'TIME': '@323', 'P_ADDR': 11, 'LINE': 12}
DEBUG [tprocv2_assembler.py:1083] COMMAND_TRANSLATION: Command Wait add one more instruction 12
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TIME', 'C_OP': 'inc_ref', 'LIT': '#538', 'P_ADDR': 13, 'LINE': 13}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'REG_WR', 'DST': 's12', 'SRC': 'op', 'OP': 's12 + #1', 'P_ADDR': 14, 'LINE': 14}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'REG_WR', 'DST': 's15', 'SRC': 'label', 'LABEL': 'reps', 'P_ADDR': 15, 'LINE': 15, 'ADDR': '&5'}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'TEST', 'OP': 'r0 - #0', 'P_ADDR': 16, 'LINE': 16}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'JUMP', 'ADDR': 's15', 'IF': 'NZ', 'WR': 'r0 op', 'OP': 'r0 + #1', 'P_ADDR': 17, 'LINE': 17}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'REG_WR', 'DST': 's15', 'SRC': 'label', 'LABEL': 'NEXT', 'P_ADDR': 18, 'LINE': 18, 'ADDR': '&19'}
DEBUG [tprocv2_assembler.py:1029] list2bin: translating {'CMD': 'JUMP', 'ADDR': 's15', 'P_ADDR': 19, 'LINE': 19}
4.2 Inspect Program Structure
Use print(prog) to see the compiled program structure.
[8]:
print(prog)
macros:
WriteReg(dst='s_core_w1', src=0)
ConfigReadout(ch=0, name='ro', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c569e0>}, t_regs={'t': 0})
Delay(t=1.0, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c56320>}, t_regs={'t': 430})
OpenLoop(n=1, name='reps')
Pulse(ch=0, name='test', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c54910>}, t_regs={'t': 0})
Trigger(ros=[0], tts=[], pins=[0], t=0.25, width=0.023251488095238096, ddr4=False, mr=False, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c563b0>, 'width': <qick.asm_v2.QickParam object at 0xffff66c55e70>}, t_regs={'t': 108, 'width': 10}, outdict=defaultdict(<class 'int'>, {}), trigset={0, 10})
Wait(t=0, auto=True, gens=False, ros=True, tag=None, no_warn=True, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c562f0>}, t_regs={'t': 323})
Delay(t=0.5, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c55120>}, t_regs={'t': 538})
IncReg(dst='s_core_w1', src=1)
CloseLoop()
End()
registers:
reps: QickRegisterV2(addr=0, init=None)
pulses:
test: QickPulse(waveforms=[Waveform(name='test_w0', freq=QickRawParam(par='freq', start=44810940, spans={}, quantize=10, steps={}), phase=QickRawParam(par='phase', start=0, spans={}, quantize=1, steps={}), env=0, gain=QickRawParam(par='gain', start=32766, spans={}, quantize=1, steps={}), length=QickRawParam(par='length', start=60, spans={}, quantize=1, steps={}), conf=9)])
ro: QickPulse(waveforms=[Waveform(name='ro_w0', freq=QickRawParam(par='freq', start=-174762666, spans={}, quantize=-39, steps={}), phase=0, env=0, gain=0, length=3, conf=264)])
expanded ASM:
NOP
REG_WR s12 imm #0
WPORT_WR p4 wmem [&1] @0
TIME #430 inc_ref
REG_WR r0 imm #0
reps:
WPORT_WR p0 wmem [&0] @0
TRIG p0 set @108
TRIG p10 set @108
TRIG p0 clr @118
TRIG p10 clr @118
REG_WR s15 label SKIP
WAIT [s15] @323 time
TIME #538 inc_ref
REG_WR s12 op -op(s12 + #1)
REG_WR s15 label reps
TEST -op(r0 - #0)
JUMP [s15] -if(NZ) -wr(r0 op) -op(r0 + #1)
REG_WR s15 label NEXT
JUMP [s15]
4.3 Check Specific Macros
Use get_macro(tag) to inspect individual instructions.
[9]:
# Create a program with tags to inspect macros
class TaggedProgram(AveragerProgramV2):
def _initialize(self, cfg):
self.declare_gen(ch=cfg['gen_ch'], nqz=1)
self.declare_readout(ch=cfg['ro_ch'], length=cfg['ro_len'])
self.add_pulse(ch=cfg['gen_ch'], name="test", style="const",
freq=100, ro_ch=cfg['ro_ch'], length=0.1, phase=0, gain=1.0)
self.add_readoutconfig(ch=cfg['ro_ch'], name="ro", freq=100, gen_ch=cfg['gen_ch'])
self.send_readoutconfig(ch=cfg['ro_ch'], name="ro", t=0)
def _body(self, cfg):
self.pulse(ch=cfg['gen_ch'], name="test", t=0)
self.delay_auto(0.1) # sin keyword 'gap', sin 'tag'
self.trigger(ros=[cfg['ro_ch']], pins=[0], t=0.25)
config = {'gen_ch': GEN_CH, 'ro_ch': RO_CH, 'ro_len': 0.5}
prog_tagged = TaggedProgram(soccfg, reps=1, final_delay=0.5, cfg=config)
print("Program created with", len(prog_tagged.macro_list), "macros")
print(prog_tagged) # inspect the full structure
Program created with 12 macros
macros:
WriteReg(dst='s_core_w1', src=0)
ConfigReadout(ch=0, name='ro', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9a980>}, t_regs={'t': 0})
Delay(t=1.0, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9aa10>}, t_regs={'t': 430})
OpenLoop(n=1, name='reps')
Pulse(ch=0, name='test', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ab30>}, t_regs={'t': 0})
Delay(t=0.1, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9aad0>}, t_regs={'t': 86})
Trigger(ros=[0], tts=[], pins=[0], t=0.25, width=0.023251488095238096, ddr4=False, mr=False, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9a9b0>, 'width': <qick.asm_v2.QickParam object at 0xffff66c9aa40>}, t_regs={'t': 108, 'width': 10}, outdict=defaultdict(<class 'int'>, {}), trigset={0, 10})
Wait(t=0, auto=True, gens=False, ros=True, tag=None, no_warn=True, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ad10>}, t_regs={'t': 323})
Delay(t=0.5, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ab60>}, t_regs={'t': 538})
IncReg(dst='s_core_w1', src=1)
CloseLoop()
End()
registers:
reps: QickRegisterV2(addr=0, init=None)
pulses:
test: QickPulse(waveforms=[Waveform(name='test_w0', freq=QickRawParam(par='freq', start=44810940, spans={}, quantize=10, steps={}), phase=QickRawParam(par='phase', start=0, spans={}, quantize=1, steps={}), env=0, gain=QickRawParam(par='gain', start=32766, spans={}, quantize=1, steps={}), length=QickRawParam(par='length', start=60, spans={}, quantize=1, steps={}), conf=9)])
ro: QickPulse(waveforms=[Waveform(name='ro_w0', freq=QickRawParam(par='freq', start=-174762666, spans={}, quantize=-39, steps={}), phase=0, env=0, gain=0, length=3, conf=264)])
expanded ASM:
NOP
REG_WR s12 imm #0
WPORT_WR p4 wmem [&1] @0
TIME #430 inc_ref
REG_WR r0 imm #0
reps:
WPORT_WR p0 wmem [&0] @0
TIME #86 inc_ref
TRIG p0 set @108
TRIG p10 set @108
TRIG p0 clr @118
TRIG p10 clr @118
REG_WR s15 label SKIP
WAIT [s15] @323 time
TIME #538 inc_ref
REG_WR s12 op -op(s12 + #1)
REG_WR s15 label reps
TEST -op(r0 - #0)
JUMP [s15] -if(NZ) -wr(r0 op) -op(r0 + #1)
REG_WR s15 label NEXT
JUMP [s15]
[10]:
# Inspect acros from program
print("All macros:")
for i, macro in enumerate(prog_tagged.macro_list):
print(f" [{i}] {type(macro).__name__}: {macro}")
# Buscar por tipo en lugar de por tag
def get_macros_by_type(prog, macro_type_name):
return [(i, m) for i, m in enumerate(prog.macro_list)
if type(m).__name__ == macro_type_name]
print("\nPulse macros:", get_macros_by_type(prog_tagged, 'Pulse'))
print("Delay macros:", get_macros_by_type(prog_tagged, 'Delay'))
print("Trigger macros:", get_macros_by_type(prog_tagged, 'Trigger'))
All macros:
[0] WriteReg: WriteReg(dst='s_core_w1', src=0)
[1] ConfigReadout: ConfigReadout(ch=0, name='ro', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9a980>}, t_regs={'t': 0})
[2] Delay: Delay(t=1.0, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9aa10>}, t_regs={'t': 430})
[3] OpenLoop: OpenLoop(n=1, name='reps')
[4] Pulse: Pulse(ch=0, name='test', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ab30>}, t_regs={'t': 0})
[5] Delay: Delay(t=0.1, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9aad0>}, t_regs={'t': 86})
[6] Trigger: Trigger(ros=[0], tts=[], pins=[0], t=0.25, width=0.023251488095238096, ddr4=False, mr=False, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9a9b0>, 'width': <qick.asm_v2.QickParam object at 0xffff66c9aa40>}, t_regs={'t': 108, 'width': 10}, outdict=defaultdict(<class 'int'>, {}), trigset={0, 10})
[7] Wait: Wait(t=0, auto=True, gens=False, ros=True, tag=None, no_warn=True, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ad10>}, t_regs={'t': 323})
[8] Delay: Delay(t=0.5, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ab60>}, t_regs={'t': 538})
[9] IncReg: IncReg(dst='s_core_w1', src=1)
[10] CloseLoop: CloseLoop()
[11] End: End()
Pulse macros: [(4, Pulse(ch=0, name='test', t=0, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ab30>}, t_regs={'t': 0}))]
Delay macros: [(2, Delay(t=1.0, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9aa10>}, t_regs={'t': 430})), (5, Delay(t=0.1, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9aad0>}, t_regs={'t': 86})), (8, Delay(t=0.5, auto=True, gens=True, ros=True, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9ab60>}, t_regs={'t': 538}))]
Trigger macros: [(6, Trigger(ros=[0], tts=[], pins=[0], t=0.25, width=0.023251488095238096, ddr4=False, mr=False, tag=None, t_params={'t': <qick.asm_v2.QickParam object at 0xffff66c9a9b0>, 'width': <qick.asm_v2.QickParam object at 0xffff66c9aa40>}, t_regs={'t': 108, 'width': 10}, outdict=defaultdict(<class 'int'>, {}), trigset={0, 10}))]
4.4 Verify Pulse Parameters
Use get_pulse_param() to check actual parameters after quantization.
[11]:
# In a sweep program, you can check all swept values
class SweepCheckProgram(AveragerProgramV2):
def _initialize(self, cfg):
self.declare_gen(ch=cfg['gen_ch'], nqz=1)
self.declare_readout(ch=cfg['ro_ch'], length=cfg['ro_len'])
self.add_loop("sweep", cfg['steps'])
self.add_pulse(ch=cfg['gen_ch'], name="test", style="const",
freq=cfg['freq'], ro_ch=cfg['ro_ch'], length=0.1,
phase=QickSweep1D("sweep", -180, 180),
gain=QickSweep1D("sweep", 0, 1))
self.add_readoutconfig(ch=cfg['ro_ch'], name="ro", freq=cfg['freq'], gen_ch=cfg['gen_ch'])
self.send_readoutconfig(ch=cfg['ro_ch'], name="ro", t=0)
def _body(self, cfg):
self.pulse(ch=cfg['gen_ch'], name="test", t=0)
self.trigger(ros=[cfg['ro_ch']], pins=[0], t=0.25)
config_check = {'gen_ch': GEN_CH, 'ro_ch': RO_CH, 'freq': 100, 'steps': 5, 'ro_len': 0.5}
prog_check = SweepCheckProgram(soccfg, reps=1, final_delay=0.5, cfg=config_check)
phases = prog_check.get_pulse_param('test', 'phase', as_array=True)
gains = prog_check.get_pulse_param('test', 'gain', as_array=True)
print(f"Phase values: {phases}")
print(f"Gain values: {gains}")
Phase values: [-180. -90. 0. 90. 180.]
Gain values: [0. 0.24998474 0.49996948 0.74995422 0.99993896]
5. Advanced Parameters
5.1 The reps_innermost Parameter
By default, the reps loop (experiment repetitions) is the outermost loop. This reduces sensitivity to slow drifts.
[12]:
class LoopOrderProgram(AveragerProgramV2):
def _initialize(self, cfg):
self.declare_gen(ch=cfg['gen_ch'], nqz=1)
self.declare_readout(ch=cfg['ro_ch'], length=cfg['ro_len'])
self.add_loop("sweep", 3)
self.add_pulse(ch=cfg['gen_ch'], name="test", style="const",
freq=100, ro_ch=cfg['ro_ch'], length=0.1, phase=0,
gain=QickSweep1D("sweep", 0, 1))
self.add_readoutconfig(ch=cfg['ro_ch'], name="ro", freq=100, gen_ch=cfg['gen_ch'])
self.send_readoutconfig(ch=cfg['ro_ch'], name="ro", t=0)
def _body(self, cfg):
self.pulse(ch=cfg['gen_ch'], name="test", t=0)
self.trigger(ros=[cfg['ro_ch']], pins=[0], t=0.25)
config_loop = {'gen_ch': GEN_CH, 'ro_ch': RO_CH, 'ro_len': 0.5}
# Default: reps outermost
prog_default = LoopOrderProgram(soccfg, reps=4, final_delay=0.5, cfg=config_loop, reps_innermost=False)
# Alternative: reps innermost
prog_innermost = LoopOrderProgram(soccfg, reps=4, final_delay=0.5, cfg=config_loop, reps_innermost=True)
print("Default (reps outermost): Sweep completes all steps before next repetition")
print("Alternative (reps innermost): All repetitions at each sweep step before moving to next step")
Default (reps outermost): Sweep completes all steps before next repetition
Alternative (reps innermost): All repetitions at each sweep step before moving to next step
5.2 The final_delay and initial_delay Parameters
These parameters add automatic delays at the beginning and end of each shot.
[13]:
# initial_delay: added after _initialize() completes
# final_delay: added at the end of each shot before the next repetition
prog_with_delays = LoopOrderProgram(soccfg, reps=2, final_delay=10.0,
cfg=config_loop, initial_delay=5.0)
print("Program with 5 µs initial delay and 10 µs final delay per shot")
Program with 5 µs initial delay and 10 µs final delay per shot
6. Quick Reference: Parameter Quantization
Parameter |
Units |
Resolution |
Range |
|---|---|---|---|
Frequency |
MHz |
Depends on DDS bit-width |
±DDS_range |
Phase |
degrees |
360/2^32 ≈ 8.4e-8° |
0-360° (wraps) |
Gain |
linear |
1/32766 ≈ 3e-5 |
0 to 1 |
Time/Duration |
µs |
1/timing_clock (≈2 ns) |
±2^31 ticks |
Length (const pulse) |
µs |
Same as time |
>0 |
Length (flat-top flat) |
µs |
Same as time |
>0 |
7. Troubleshooting Checklist
When an experiment doesn’t work as expected, check these items in order:
Hardware Connection - Is the board connected? Does
soccfgshow correct configuration?Firmware Version - Is the firmware compatible with your QICK version?
Channel Configuration - Are the generator and readout channels correct?
Pulse Parameters - Are frequency, gain, and length within valid ranges?
Timeline - Are pulses colliding? Use
delay_auto()for spacing.Readout Latency - Is
read_waitproperly tuned for feedback?Buffer Arm - Are buffers armed before running? Do they have enough capacity?
Program Size - Is the program exceeding tProc memory limits?
Logging - Set logging to DEBUG and look for warnings or errors.
Data Retrieval - Are you using the correct time axis for your buffer type?
8. Summary
This reference notebook covers:
Hardware limits of the tProc (program memory, labels, time ranges)
Common errors and their solutions
Debugging techniques (logging, program inspection, parameter verification)
Advanced parameters (
reps_innermost,final_delay,initial_delay)Parameter quantization reference table
Troubleshooting checklist
Next Steps:
Proceed to
`10_Multi_Board_Synchronization.ipynb<./10_Multi_Board_Synchronization.ipynb>`__ to learn about how to connect and work with multiple boards inter-connected.Review the other notebooks in this series for detailed examples
Refer to the QICK Documentation for complete API reference
Join the QICK community for support