Feedback#

Qblox’s instruments support feedback using an integrated trigger network of four parallel trigger lines that interconnect all sequencers within the system and allow the user to create low-latency feedback sequences for these sequencers. Additionally, the external trigger input on each instrument is connected to the same trigger network. This allows other instruments to also contribute to any feedback sequence. The feedback works from any sequencer in any readout module to any sequencer in any module. The total feedback time, i.e. the last sample received in the input to the first sample sent out from output, is listed in the table below for the different analog / digital combinations of input and output signals. Two types of digital input can be considered. The TTL input corresponds to performing a thresholded acquisition with a single count threshold, while the CMM trigger corresponds to the use of an external trigger which is mapped to the cluster trigger network. Between two separate modules, there is no difference in latency whether the output is an analog or digital signal (marker outputs). However, in the case of feedback within a single module, there is a speed-up when using a digital signal as output since the trigger can be directly reproduced as a marker output without going through the trigger network.

Feedback

Input

Output

In-to-out latency (ns)

Between 2 modules

Analog

Analog / Digital marker

364

TTL

Analog / Digital marker

340

CMM trigger

Analog / Digital marker

248

Within 1 module

Analog

Analog

364

Analog

Digital marker

168

TTL

Digital marker

132

Note

The trigger network needs to be calibrated. Currently, this calibration is run 40 seconds after the instrument boots. Please refrain from running feedback sequences before that time. The reset() method can be used to rerun calibration if required.

Overview#

The figure below shows the four-line trigger network connecting the external trigger input and all sequencers. Access to this trigger network is address-based. When triggering the network using the external trigger input or any of the sequencers, the resulting trigger is converted into one of fifteen configurable addresses (i.e. 1 to 15). In turn, any sequencer can subscribe to one or multiple addresses and react to those incoming triggers using custom sequencer instructions to modify sequence flow (e.g. set_cond, latch_en, latch_rst) (see section Instructions). Of course, conventional trigger synchronization remains possible as well, as discussed in the Trigger section.

Trigger network

Triggering#

The trigger network operates on a 28 ns timegrid. This timegrid is started/aligned during module synchronization using the wait_sync instruction. Once synchronized, triggers are sent to the nearest timegrid point in the future. In other words, triggers that are not sent on this timegrid get a maximum time penalty of 24 ns. Any trigger that is sent on the trigger network takes 212 ns to propagate to any receiver on the trigger network, and be available for conditional operation. A trigger can be sent on the trigger network once every 252 ns (or 9 timegrid points). The figure below shows an example of the trigger network timegrid.

Trigger network timegrid

Sending triggers on the trigger network can be done from the three following sources:

Thresholded acquisitions#

Any thresholded acquisition result, as discussed in the 6. Thresholding section, can send a trigger on the trigger network. When the acquisition completes, the result is mapped to the trigger network following the next steps.

First, the mapping needs to be enabled using the Sequencer.thresholded_acq_trigger_en() parameter. Next, a trigger address with which the trigger is sent on the network needs to be selected using the Sequencer.thresholded_acq_trigger_address() parameter. Remember that this address needs to be used by the receiving sequencer to handle this trigger. Finally, the polarity of the result needs to be selected using the Sequencer.thresholded_acq_trigger_invert() parameter. If you want to set a trigger when the acquisition result is 1, the parameter must be set to false and vice versa.

TTL triggers#

When acquiring TTL triggers, as discussed in the TTL trigger acquisition section, any trigger detected during the acquisition period can be mapped to the trigger network as well. This effectively means that multiple triggers can be sent on the network during a single acquisition period. However, remember that the trigger network can only be retriggered every 252 ns, so the rate of the TTL triggers on the input must be limited to the maximum trigger network rate.

During a TTL trigger acquisition, all detected triggers are treated like thresholded acquisition results. Mapping the TTL triggers on the trigger network is therefore identical to mapping thresholded acquisition results on the network and uses the same parameters.

External trigger input#

As mentioned at the start of this page, the external trigger input can also be mapped to the trigger network following the next steps. First, the mapping needs to be enabled using the Cluster.ext_trigger_input_trigger_en() parameter. Next, a trigger address with which the trigger is sent on the network needs to be selected using the Cluster.ext_trigger_input_trigger_address() parameter. Once sent on the trigger network, the external trigger behaves identically to any other trigger source and has the same propagation delay and trigger rate.

Note that the external trigger input will be sampled with the instrument’s internal clock. If an external trigger signal arrives in that clock’s setup or hold time, it can cause timing jitter on the execution of any related instruction like wait_trigger. Use the Cluster.ext_trigger_input_delay() parameter to delay the external trigger signal and resolve any time jitter.

Trigger handling#

On the receiving side of the trigger network, every sequencer has the basic capability to handle a trigger address by using the wait_trigger instruction and selecting that address as its first argument. On top of that, each sequencer also has more advanced trigger-handling capabilities. Every sequencer has a separate latch/counter combination for each trigger address on the network, which from here on is referred to as the trigger network address counters. These counters allow the counting of every triggered address on the network in real time. These counters are controlled using the latch_en and latch_rst instructions, where the first enables/disables the counters for all trigger addresses and the latter resets the counters for all addresses back to zero (see section Instructions).

Once a trigger on the trigger network is captured in the counters, the sequencers can operate on the counter values to create a single boolean (true/false) condition on which to execute real-time instructions or simply skip them. Any real-time instruction following the set_cond instruction evaluates this condition and is either executed (when true) or skipped (when false). On a skip, the instruction is replaced by a wait time in nanoseconds as expressed in the else (i.e. fourth) argument of the instruction. This allows the creation of conditional playback scenarios by setting the else argument to the same duration as the skipped instruction. Alternatively, it allows creating repeat until success scenarios by setting the else argument to a short time (e.g. 4 nanoseconds) and quickly flushing instructions from the pipeline.

The boolean condition itself is based on a comparison between the counters and the Sequencer.trigger1_count_threshold() parameters. Each counter is individually compared (>=) with its associated threshold. The Sequencer.trigger1_threshold_invert() parameter can be used to invert this comparison (<). The results of these static comparisons create a vector of boolean conditions with a true/false indication for each trigger address. Next, the mask (i.e. second) argument of the set_cond instruction is used to select boolean conditions of specific trigger addresses from the vector to operate on. This is how a sequencer subscribes itself to one or multiple trigger addresses. The mask argument is expressed as a decimal value representing a vector of mask bits that in turn each represent a single trigger address (e.g. selecting trigger addresses 1 and 4 requires mask value 2**(1-1)+2**(4-1)=9). There are 15 latches per sequencer so in total there are 90 latches per module. For example, see the figure below where the mask in the above example has been highlighted in red for sequencer 3. In green, a visualization is included of what happens for when the latch_rst instruction is used on sequencer 1.

Trigger network module latch example

Finally, a logical operation, as determined by the operator (i.e. third argument) of the set_cond instruction, is performed on the selected boolean conditions which results in the final boolean condition used by the set_cond instruction. The following figure shows a block diagram of how the boolean condition is determined.

Trigger network conditional

The following table lists the operator argument values and the associated selection and logical operations:

Argument

Select

Operator

Comment

0

AND mask

OR

Return true if any of the selected counters crossed their thresholds

1

AND mask

NOR

Return true if none of the selected counters crossed their threshold

2

OR NOT(mask)

AND

Return true if all of the selected counters crossed their threshold

3

OR NOT(mask)

NAND

Return true if any of the selected counters did not cross their threshold

4

AND mask

XOR

Return true if an odd number of selected counters crossed their threshold

5

AND mask

XNOR

Return true if an even number of selected counters crossed their threshold

Examples#

This section provides a few simplified feedback examples that can easily be adapted to your own needs.

Conditional playback#

In this example, sequencer 0 performs a measurement and sequencer 1 performs a conditional playback based on the measurement result.

Settings:

sequencer0.thresholded_acq_trigger_en(True)
sequencer0.thresholded_acq_trigger_address(12)
sequencer0.thresholded_acq_trigger_invert(False)

sequencer1.trigger12_count_threshold(1)
sequencer1.trigger12_threshold_invert(False)

Sequencer 0 (Readout):

wait_sync 4       #Wait for sequencers to synchronize

play 0,0,4        #Play readout pulse
wait 148          #Wait for readout pulse to return
acquire 0,0,100   #Acquire readout pulse

stop              #Stop program

Sequencer 1 (Playback):

wait_sync 4             #Wait for sequencers to synchronize

set_latch_en 1,4        #Latch any trigger
latch_rst 1000          #Reset the trigger network address counters, then wait on trigger address 12

set_cond 1,2048,0,4     #Play waveform if trigger is seen (condition: OR T12)
play 0,0,20             #""
set_cond 1,2048,1,4     #Play waveform if trigger is NOT seen (condition: NOR T12)
play 1,1,20             #""

stop                    #Stop program

Repeat until success#

In this example, sequencer 0 repeatedly measures the state of the qubit. When the qubit state reaches zero, it flushes the pipeline and continues its schedule.

Settings:

sequencer0.thresholded_acq_trigger_en(True)
sequencer0.thresholded_acq_trigger_address(12)
sequencer0.thresholded_acq_trigger_invert(False)

sequencer0.trigger12_count_threshold(1)
sequencer0.trigger12_threshold_invert(False)

Sequencer 0:

      move 10,R0            #Set loop iterator R0
      wait_sync 4           #Wait for sequencers to synchronize
      set_latch_en 1,4      #Latch any trigger

      play 0,0,4            #Play readout pulse
      wait 148              #Wait for readout pulse to return
      acquire 0,0,100       #Acquire readout pulse
      latch_rst 1000        #Reset the trigger network address counters, then wait on trigger address 12

      set_cond 1,2048,0,4   #Execute if trigger is seen else flush (condition: OR T12)
loop: play 0,0,4            #Play readout pulse
      wait 148              #Wait for readout pulse to return
      acquire 0,0,100       #Acquire readout pulse
      latch_rst 1000        #Reset the trigger network address counters, then wait on trigger address 12
      loop R0,@loop         #Loop

      set_cond 0,0,0,4      #Disable conditionality
      play 1,1,20           #Continue program

      stop                  #Stop program

Trigger counting#

In this example, the trigger network address counters are used to count TTL triggers on the inputs. Sequencer 0 detects the triggers on the input of the device and then counts the resulting triggers on the trigger network. Finally, it plays a waveform if 5 or more triggers are detected.

Settings:

sequencer0.ttl_acq_input_select(0)
sequencer0.ttl_acq_threshold(0.2)
sequencer0.ttl_acq_auto_bin_incr_en(false)

sequencer0.thresholded_acq_trigger_en(True)
sequencer0.thresholded_acq_trigger_address(12)
sequencer0.thresholded_acq_trigger_invert(False)

sequencer0.trigger12_count_threshold(5)
sequencer0.trigger12_threshold_invert(False)

Sequencer 0:

wait_sync 4            #Wait for sequencers to synchronize

set_latch_en 1,4       #Latch any trigger
latch_rst 4            #Reset the trigger network address counters

acquire_ttl 0,0,1,5000 #Acquire input triggers
acquire_ttl 0,0,0,4    #Stop acquiring triggers

set_cond 1,2048,0,20   #Play if 5 triggers have been seen (condition: OR T12)
play 0,0,20            #""

stop                   #Stop program