Sequencer¶
This section will explain how the sequencers of the Pulsar QCM and QRM are controlled. Every sequencer is controlled using the same functions and parameters, which either take the sequencer index as a parameter or indicate which sequencer they operate on based on the index in their name.
Overview¶
The sequencers are split into the sequence processor, AWG and acquisition paths as shown in the figures below. Each sequence processor controls one AWG path and, in case of the Pulsar QRM, one acquisition path. The AWG path and acquisition path are discussed in more detail in section Pulsar. Each sequencer processor is, in turn, split into a classical and real-time pipeline. The classical pipeline is responsible for any classical instructions related to program flow or arithmetic and the real-time pipeline is responsible for real-time instructions that are used to create the experiment timeline.
The sequencers are started and stopped by calling the arm_sequencer()
, start_sequencer()
and
stop_sequencer()
functions. Once started they will execute the sequence described in the next section.
Sequence¶
The sequencers are programmed with a sequence using the sequencer#.sequence()
function parameter. This parameter expects
a sequence in the form of a JSON compatible file that contains the waveform, weight, acquistion and program information. The JSON file is
expected to adhere to the following format:
waveforms: Indicates that the following waveforms are intended for the AWG path.
waveform name: Replace by string containing the waveform name.
data: List of floating point values to express the waveform.
index: Integer index used by the Q1ASM program to refer to the waveform.
weights: Indicates that the following weight functions are intended for the integration units of the acquisition path (only used by the Pulsar QRM).
weight name: Replace by string containing the weight name.
data: List of floating point values to express the weight.
index: Integer index used by the Q1ASM program to refer to the weight.
acquisitions: Indicates that the following acquisitions are available for the acquisition path to refer to (only used by the Pulsar QRM).
acquisition name: Replace by string containing the acquisition name.
num_bins: Number of bins in acquisition.
index: Integer index used by the Q1ASM program to refer to the acquisition.
program: Single string containing the entire sequence processor Q1ASM program.
Example of a sequence JSON file.
{ "waveforms": { "gaussian": { "data": [ 0.0075756774442599355, 0.5812730178734145, 0.5812730178734145, 0.0075756774442599355 ], "index": 0 }, "sine": { "data": [0.0, 1.0, 1.2246467991473532e-16, -1.0], "index": 1 } }, "weights": { "gaussian": { "data": [0.0075756774442599355, 0.5812730178734145, 0.5812730178734145, 0.0075756774442599355], "index": 0 }, "sine": { "data": [0.0, 1.0, 1.2246467991473532e-16, -1.0], "index": 1 } }, "acquisitions": { "binned": { "num_bins": 100000, "index": 0 }, "averaged": { "num_bins": 1, "index": 1 } }, "program": "\nplay 0,1,4 #Play waveforms and wait 4ns.\nacquire 1,0,16380 #Acquire wait for scope mode acquisition to finish.\nstop #Stop.\n" }
Program¶
The sequence programs are written in the custom Q1ASM assembly language described in the following sections. All sequence processor instructions are executed by the classical pipeline and the real-time instructions are also executed by the real-time pipeline. These latter instructions are intended to control the AWG and acquisition paths in a real-time fashion. Once processed by the classical pipeline they are queued in the real-time pipeline awaiting further execution. A total of 32 instructions can be queued and once the queue is full, the classical part will stall on any further real-time instructions.
Once execution of the real-time instructions by the real-time pipeline is started, care must be taken to not cause an underrun of the queue. An underrun will potentially cause undetermined real-time behaviour and desynchronize any synchronized sequencers. Therefore, when this is detected, the sequencer is completely stopped. A likely cause of underruns is a loop with a very short (i.e. < 24ns) real-time run-time, since the jump of a loop takes some cycles to be execute by the classical pipeline.
Finally, be aware that moving data into a register using an instruction takes a cycle to complete. This means that when an instruction reads from a register that the previous instruction has written to, a nop instruction must be placed in between these consecutive instructions for the value to be correctly read.
The state of the sequencers, including any errors, can be queried through get_sequencer_state()
.
Instructions¶
Instructions |
Argument 0 |
Argument 1 |
Argument 2 |
Argument 3 |
Argument 4 |
Description |
---|---|---|---|---|---|---|
Control |
||||||
illegal
|
–
|
–
|
–
|
–
|
–
|
Instruction that should not
be executed. If it is
executed, the sequencer
will stop with the illegal
instruction flag set.
|
stop
|
–
|
–
|
–
|
–
|
–
|
Instruction that stops the
sequencer.
|
nop
|
–
|
–
|
–
|
–
|
–
|
No operation instruction,
that does nothing. It is
used to pass a single cycle
in the classic part of the
sequencer without any
operations.
|
Jumps |
||||||
jmp
|
Immediate,
Register,
Label
|
–
|
–
|
–
|
–
|
Jump to the next
instruction indicated by
argument 0.
|
jge
|
Register
|
Immediate
|
Immediate,
Register,
Label
|
–
|
–
|
If argument 0 is greater
or equal to argument 1,
jump to the instruction
indicated by argument 2.
|
jlt
|
Register
|
Immediate
|
Immediate,
Register,
Label
|
–
|
–
|
If argument 0 is less
than argument 1, jump to
the instruction indicated
by argument 2.
|
loop
|
Register
|
Immediate,
Register,
Label
|
–
|
–
|
–
|
Subtract argument 0 by
one and jump to the
instruction indicated by
argument 1 until
argument 0 reaches zero.
|
Arithmetic |
||||||
move
|
Immediate,
Register
|
Register
|
–
|
–
|
–
|
Argument 0 is moved /
copied to argument 1.
|
not
|
Immediate,
Register
|
Register
|
–
|
–
|
–
|
Bit-wise invert
argument 0
and move the result to
argument 1.
|
add
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Add argument 1 to
argument 0 and move the
result to argument 2.
|
sub
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Subtract argument 1 from
argument 0 and move the
result to argument 2.
|
and
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Bit-wise AND argument 0
and argument 1 and move
the result to argument 2.
|
or
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Bit-wise OR argument 0
and argument 1 and move
the result to argument 2.
|
xor
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Bit-wise XOR argument 0
and argument 1 and move
the result to argument 2.
|
asl
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Bit-wise left-shift
argument 0 by argument 1
number of bits and move
the result to argument 2.
|
asr
|
Register
|
Immediate,
Register
|
Register
|
–
|
–
|
Bit-wise right-shift
argument 0 by argument 1
number of bits and move the
result to argument 2.
|
Parameter operations |
||||||
set_mrk
|
Immediate,
Register
|
–
|
–
|
–
|
–
|
Set marker output channels
to argument 0 (bits 0-3),
where the bit index
corresponds to the channel
index for baseband modules.
For QCM-RF module, bit
indices 0 & 1 correspond to
output enable 1 and 2
respectively; indices 2 & 3
correspond to marker outputs
2 and 1 respectively.
For QRM-RF module, bit
indices 0 & 1 correspond to
input 1 and output 1 switches
respectively; indices 2 & 3
correspond to marker outputs
1 and 2 respectively. The
values are OR´ed by that of
other sequencers. The
parameters are cached and
only updated when the
upd_param, play,
acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
|
set_freq
|
Immediate,
Register
|
–
|
–
|
–
|
–
|
Set the frequency of the NCO
used by the AWG and
acquisition using
argument 0. The frequency
is divided into 4e9 steps
between -500 and 500 MHz and
expressed as an integer
between -2e9 and 2e9. (e.g.
1 MHz=4e6). The frequency
parameter is cached and only
applied when the
upd_param, play,
acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
|
reset_ph
|
–
|
–
|
–
|
–
|
–
|
Reset the absolute phase of
the NCO used by the AWG and
acquisition to 0°. This also
resets any relative phase
offsets that were already
statically or dynamically
set. The reset is cached and
only applied when the
upd_param, play,
acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
|
set_ph
|
Immediate,
Register
|
–
|
–
|
–
|
–
|
Set the relative phase of
the NCO used by the AWG and
acquisition using
argument 0. The phase is
divided into 1e9 steps
between 0° and 360°,
expressed as an integer
between 0 and 1e9 (e.g
45°=125e6). The phase
parameter is cached and only
updated when the
upd_param, play`,
acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
|
set_ph_delta
|
Immediate,
Register
|
–
|
–
|
–
|
–
|
Set an offset on top of the
relative phase of the NCO
used by the AWG and
acquisition. The offset is
applied on top of the phase
set using set_ph. See
set_ph for more details
regarding the argument. The
phase parameter is cached
and only updated when the
upd_param, play,
acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
|
set_awg_gain
|
Immediate,
Register
|
Immediate,
Register
|
–
|
–
|
–
|
Set AWG gain for path 0
using argument 0 and path
1 using argument 1. Both
gain values are divided in
2**sample path width steps.
The parameters are cached
and only updated when the
upd_param, play,
acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
The arguments are either
all set through immediates
or registers.
|
set_awg_offs
|
Immediate,
Register
|
Immediate,
Register
|
–
|
–
|
–
|
Set AWG gain for path 0
using argument 0 and path
1 using argument 1. Both
offset values are divided
in 2**sample path width
steps. The parameters are
cached and only updated
when the upd_param,
play, acquire,
acquired_weighed or
acquired_ttl
instructions are executed.
The arguments are
either all set through
immediates or registers.
|
Conditional |
||||||
set_cond
|
Immediate,
Register
|
Immediate,
Register
|
Immediate,
Register
|
Immediate
|
–
|
Enable/disable
conditionality on all
following real-time
instructions based on
argument 0. The condition
is based on the trigger
network address counters
being thresholded based on
the associated counter
threshold parameters set
through QCoDeS. The
results are masked using
argument 1 (bits 0-14),
where the bit index plus one
corresponds to the trigger
address. This creates a
selection to include in the
final logical operation set
using argument 2. The
logical operation result
(true/false) determines
the condition. If
the condition is true
upon evaluation, the next
real-time instruction is
executed. Else the real-time
path ignores the instruction
and waits for argument 3
number of nanoseconds before
continueing to the next. All
following real-time
instructions are subject
to the same condition, until
either the conditionality is
disabled or updated.
Disabling the conditionality
does not affect the address
counters.
Logical operators are OR,
NOR, AND, NAND, XOR, XNOR,
where a value for
argument 2 of 0 is OR and
5 is XNOR respectively.
|
Real-time IO operations |
||||||
upd_param
|
Immediate
|
–
|
–
|
–
|
–
|
Update the marker, phase,
phase offset, gain and
offset parameters set using
their respective
instructions and then wait
for argument 0 number of
nanoseconds.
|
play
|
Immediate,
Register
|
Immediate,
Register
|
Immediate
|
–
|
–
|
Update the marker, phase,
phase offset, gain and
offset parameters set using
their respective
instructions, start playing
AWG waveforms stored at
indexes argument 0 on
path 0 and argument 1 on
path 1 and finally wait for
argument 2 number of
nanoseconds. The arguments
are either all set through
immediates or registers.
|
acquire
|
Immediate
|
Immediate,
Register
|
Immediate
|
–
|
–
|
Update the marker, phase,
phase offset, gain and
offset parameters set using
their respective
instruction, start the
acquisition referred to
using index argument 0 and
store the bin data in bin
index argument 1, finally
wait for argument 2 number
of nanoseconds. Integration
is executed using a square
weight with a preset length
through the associated
QCoDeS parameter. The
arguments are either all
set through immediates or
registers.
|
acquire_weighed
|
Immediate
|
Immediate,
Register
|
Immediate,
Register
|
Immediate,
Register
|
Immediate
|
Update the marker, phase,
phase offset, gain and
offset parameters set using
their respective
instruction, start the
acquisition referred to
using index argument 0 and
store the bin data in bin
index argument 1, finally
wait for argument 4 number
of nanoseconds. Integration
is executed using weights
stored at indexes
argument 2 for path 0 and
argument 3 for path 1. The
arguments are either all
set through immediates or
registers.
|
acquire_ttl
|
Immediate
|
Immediate,
Register
|
Immediate
|
Immediate
|
–
|
Update the marker, phase,
phase offset, gain and
offset parameters set using
their respective
instruction, start the
TTL trigger acquisition
referred to using index
argument 0 and store the
bin data in bin index
argument 1, enable the
acquisition by writing 1 to
argument 2, finally wait
for argument 3 number of
nanoseconds. The TTL trigger
acquisition has to be
actively disabled afterwards
by writing 0 to
argument 2.
|
Real-time trigger count control |
||||||
latch_en
|
Immediate
Register
|
Immediate
|
–
|
–
|
–
|
Enable/disable all trigger
network address counters
based on argument 0 and
then wait for argument 1
number of nanoseconds. Once
enabled, the trigger network
address counters will count
all triggers on the trigger
network. When disabed,
the counters hold their last
values.
|
latch_rst
|
Immediate
Register
|
–
|
–
|
–
|
Reset all trigger network
address counters back to 0
and then wait for
argument 1 number of
nanoseconds.
|
|
Real-time wait operations |
||||||
wait
|
Immediate,
Register
|
–
|
–
|
–
|
–
|
Wait for argument 0
number of nanoseconds.
|
wait_trigger
|
Immediate,
Register
|
Immediate,
Register
|
–
|
–
|
–
|
Wait for a trigger on the
trigger network at the
address set using
argument 0 and then wait
for argument 1 number of
nanoseconds.
|
wait_sync
|
Immediate,
Register
|
–
|
–
|
–
|
–
|
Wait for SYNQ to complete
on all connected sequencers
over all connected
instruments and then wait
for argument 0 number of
nanoseconds.
|
Note
The duration argument for set_cond, upd_param, play, acquire, acquire_weighed, acquire_ttl, wait, wait_trigger and wait_sync needs to a be multiple of 4ns. This will be reduced to 1ns in the future.
Arguments¶
Arguments |
Format |
Description |
---|---|---|
Immediate |
# |
32-bit decimal value (e.g. |
Register |
R# |
Register address in range 0 to 63 (e.g. pointing to a 32-bit unsigned integer |
Label |
@label |
Label name string (e.g. |
Labels¶
Any instruction can be preceded by a label. This label can be used as a reference to that specific instruction. In other words, it can be used as a goto-point by any instruction that can alter program flow (i.e. jmp, jge, jlt and loop). The label must be followed by a ‘:’ character and a whitespace before the actual referenced instruction.
Example¶
This is a simple example of a Q1ASM program. It enables each marker channel output for 1μs and then stops.
move 1,R0 # Start at marker output channel 0 (move 1 into R0)
nop # Wait a cycle for R0 to be available.
loop: set_mrk R0 # Set marker output channels to R0
upd_param 1000 # Update marker output channels and wait 1μs.
asl R0,1,R0 # Move to next marker output channel (left-shift R0).
nop # Wait a cycle for R0 to be available.
jlt R0,16,@loop # Loop until all 4 marker output channels have been set once.
set_mrk 0 # Reset marker output channels.
upd_param 4 # Update marker output channels.
stop # Stop sequencer.
Waveforms¶
The waveforms are expressed as a list of floating point values in the range of 1.0 to -1.0 with a resolution of one nanosecond per sample. The AWG path uses these waveforms to parametrically generate pulses on its outputs.
Waveform playback is started by the play instructions. Each waveform is paired with an index, which is used by this instruction to refer to the associated waveform. The waveform is then completely played irrespective of further sequence processor instructions, except when the sequence processor issues the playback of another waveform, in which case the waveform will be stopped and the new waveform will start. When waveforms are not played back-to-back, the intermediate time will be filled by samples with a value of zero.
The programmed waveforms can be retrieved using get_waveforms()
.
Weights¶
The weights are expressed as a list of floating point values in the range of 1.0 to -1.0 with a resolution of one nanosecond per sample. The integration units in the acquisition path apply (i.e. multiply) these weights during the integration process when the acquisition path is triggered for weighed integration.
Weighed integration is triggered by the acquire_weighed instruction. Each weight is paired with an index, which is used by this instruction to refer to the associated weight. The weight is then played, like the waveforms discussed in the previous section and determines the length of the integration. The weighed integration process continues irrespective of further sequence processor instructions, except when the sequence processor issues another acquisition using the acquire or acquire_weighed instructions, in which case the integration will be stopped, the result will be stored and a new integration will start.
The programmed weights can be retrieved using get_weights()
.
Acquisitions¶
Acquisitions are started by the acquire, acquire_weighed or acquire_ttl instructions and each have multiple acquisition results. Based on the executed acquisition instruction, the following acquisition results can be expected:
Scope acquisitions¶
Scope acquisitions are triggered by any of the acquisition instructions, if the sequencer is setup to trigger the associated scope acquisition module using the
scope_acq_sequencer_select()
parameter. Only one sequencer can trigger this module at a time. Once configured, a sequencer’s acquisition will trigger the capture
of 16k input samples on both inputs and will store the raw input samples in a temporary buffer, creating a raw input trace of 16 microseconds. Every time an acquisition is started,
this temporary memory is overwritten, so it is vital to move the samples from the temporary buffer to a more lasting location before the start of the next acquisition.
This is done by calling store_scope_acquisition()
, which moves the samples into the specified acquisition in the acquisition list of the sequencer, located in the RAM
of the instrument. Multiple acquisitions can be stored in this list before being retrieved from the instrument by simply calling get_acquisitions()
. Acquisitions are
returned as a dictionary of acquisitions. Scope acquisition data is located under the scope key as lists of floating point values in a range of 1.0 to -1.0 with a resolution of one
nanosecond per sample, as well as an indication if the ADC was out-of-range during the measurement.
Note
Before calling store_scope_acquisition()
, be sure to call get_acquisition_state()
with a timeout to ensures that both the sequencer has finished and that
there is an acquisition ready.
The scope acquisition module also has an averaging function set through the scope_acq_avg_mode_en_path#()
parameters. This enables the automatic
accumulation of acquisitions, where sample N of acquisition M is automatically accumulated to sample N of acquisition M+1. This happens while the acquisition is
still in the temporary buffer, so after the desired number of averaging acquisitions is completed, call store_scope_acquisition()
to store the
accumulated result in the acquisition list. Once retrieved from the instrument, the accumulated samples will automatically be divided by the number of averages to get the actual
averaged acquisition result.
Tip
For debug purposes, the scope acquisition module can also be triggered using a trigger level, where if the input exceeds this level, an acquisition is started. See the
sequencer#.trigger_mode_acq_path#()
and sequencer#.trigger_level_acq_path#()
parameters for more information.
Integrated acquisitions¶
Both the acquire and acquire_weighed instructions trigger the integrators that take the demodulated input data and integrate that over time. The results are
stored as the integrated acquisitions. The acquire instruction uses a programmable integration time set through the sequencer#.integration_length_acq()
parameter. This allows for a long integrated acquisition time of up to 16 milliseconds. The acquire_weighed instruction allows applying weight functions to the integration.
These weight functions are described in the Weights section. The weight functions are applied by multiplying input sample N with
weight sample N before accumulation (i.e. I_in[N] * I_weight[N] and Q_in[N] * Q_weight[N]). The resulting integration time is equal to the weight length in nanoseconds.
The integrated acquisition results are stored in the acquisition and bin indexed by the instruction and can be directly retrieved by calling get_acquisitions()
.
The results are stored in the associated bin index (i.e. list element) under the integration key in the bins key. Multiple integrated acquistion results stored
in the same bin are automatically averaged. The integrated acquisition results are not normalized over their integration time. Upon retrieval, the results need to
be divided by that integration time as set through the parameter or the associated weight length.
Thresholded acquisitions¶
Both the acquire and acquire_weighed instructions also trigger the calculation of the thresholded acquisition result which allows automatic reduction of the
integrated acquisition results into a single binary qubit state. For this calculation, the integrated acquisition results are considered an IQ pair, where the integrated
acquistion result of input path 0 is I and path 1 is Q. Ultimately, the I-value is compared to a configurable threshold to reduce it to the single state. To effectively do this,
the IQ pair, can first be rotated to the most optimal position for thresholding using the sequencer#.thresholded_acq_rotation()
parameter. The rotated result (the
I-value in particular) is compared against the threshold set using the sequencer#.thresholded_acq_threshold()
parameter. The integrated acquisition results
are not normalized over their integration time and so the value of this parameter must be multiplied by that integration time to compensate for that.
The thresholded acquisition results are stored in the acquisition and bin indexed by the instruction and can be directly retrieved by calling get_acquisitions()
.
The results are stored in the associated bin index (i.e. list element) under the thresholded key in the bins key. Multiple thresholded acquistion results stored in the
same bin are automatically averaged.
TTL trigger acquisitions¶
The acquire_ttl instruction is used to detect TTL triggers on one of the instrument’s inputs when it exceeds a configurable threshold.
The input is selected using sequencer#.ttl_acq_input_select()
, while the threshold is set using sequencer#.ttl_acq_threshold()
.
The instruction enables the TTL trigger acquisition path by setting the enable argument to 1 and needs to actively disable the path again by using the same
instruction with its enable argument set to 0. While the TTL trigger acquisition path is enabled, real-time integration and discretization is disabled.
acquire_ttl 0,0,1,1000 #Acquire triggers and wait for 1000ns
acquire_ttl 0,0,0,4 #Stop acquiring triggers and wait for 4ns
stop #Stop sequencer
When a rising edge is detected on the input that exceeds the threshold while the path is enabled, a TTL trigger is detected and its ADC value at which it was detected is stored in the bin specified by the instruction’s bin index argument. No new TTL triggers will be detected until a new rising edge exceeds the configured threshold.
When multiple triggers are detected while the path is enabled, the storage strategy is determined by the sequencer#.ttl_acq_auto_bin_incr_en()
parameter.
When sequencer#.ttl_acq_auto_bin_incr_en()
is enabled, the bin index is automatically incremented everytime a TTL trigger is detected. This gives the ability
to store every ADC value individually and allows you to count the number of TTL triggers by counting the number of valid bins stored at the end of the sequence.
Alternatively, when sequencer#.ttl_acq_auto_bin_incr_en()
is disabled the same bin is reused for every detected TTL trigger. This allows averaging the
ADC values in hardware and allows you to count the number of TTL triggers by looking at the average count of that bin at the end of the sequence.
The resulting acquisition data can be retrieved using get_acquisitions()
. The ADC values at which TTL triggers were detected are stored in the integration
key as lists of floating point values in a range of 1.0 to -1.0. They replace the integration results of the regular acquisitions.
Continuous waveform mode¶
The sequencer also supports a continuous waveform mode of operation, where the waveform playback control of sequence processor is completely bypassed and a single
waveform is just played back on a loop. This mode can be enabled using the sequencer#.cont_mode_en_awg_path#()
parameter and the waveform can be selected
using the sequencer#.cont_mode_waveform_idx_awg_path#()
parameter. The waveforms used in this mode must be a multiple of four samples long (i.e. 4ns).
When in continuous mode, simply program, arm, start and stop the sequencer using the regular control functions and parameters (i.e. sequencer#.sequence()
,
arm_sequencer()
, start_sequencer()
and stop_sequencer()
). However, be aware that the sequencer processor can still
control parts of the AWG path, like phase, gain and offset, while the sequencer operates in this mode. Therefore, we advise to program the sequence processor with a single
stop instruction.