See also
A Jupyter notebook version of this tutorial can be downloaded here.
Sharing Q1 Registers and Immediates#
This tutorial demonstrates the LINQ-based feedback communication path, which enables low-latency communication of registers and immediates between sequencers.
Every message is addressed with a non-zero 8-bit id:
1–15: Self-cast (same sequencer)
16–255: Intra-cast (within a module) or multi-cast (across multiple modules within a cluster), depending on routing configuration.
See the LINQ User Guide and Routing configuration for full details.
Instructions#
Sending data#
Data is sent with the fb_com_data, for more information see the Sharing Data section in the user guide.
Note:
fb_com_datais a real-time command
Receiving data#
There are two ways to receive data. The first pops a message with a specific ID (given as an immediate), while the second pulls whatever is at the top of the FIFO and writes its ID into a register. The difference between these two approaches is explained in detail in the receiving data section.
The fb_pop_data command is used to pop a message with a specific ID. The fb_pull_data command pulls whatever is at the top of the feedback queue.
Note:
fb_pop_dataandfb_pull_dataare Q1 commands
Hardware Requirements#
A Qblox Cluster with at least one module.
Setup#
First, we import the required packages and connect to the instrument.
[1]:
from __future__ import annotations
from qcodes.instrument import find_or_create_instrument
from qblox_instruments import Cluster, ClusterType
Scan For Clusters#
We scan for the available devices connected via ethernet using the Plug & Play functionality of the Qblox Instruments package (see Plug & Play for more info).
!qblox-pnp list
[2]:
cluster_ip = "10.10.200.42"
cluster_name = "cluster0"
Connect to Cluster#
We now make a connection with the Cluster.
[3]:
cluster: Cluster = find_or_create_instrument(
Cluster,
recreate=True,
name=cluster_name,
identifier=cluster_ip,
dummy_cfg=(
{
2: ClusterType.CLUSTER_QCM,
4: ClusterType.CLUSTER_QRM,
6: ClusterType.CLUSTER_QCM_RF,
8: ClusterType.CLUSTER_QRM_RF,
10: ClusterType.CLUSTER_QTM,
12: ClusterType.CLUSTER_QRC,
16: ClusterType.CLUSTER_QSM,
}
if cluster_ip is None
else None
),
)
cluster.reset()
print(cluster.get_system_status())
Status: ERROR, Flags: FEEDBACK_NETWORK_CALIBRATION_FAILED, Slot flags: NONE
Get connected modules#
[4]:
# QTM modules
modules = cluster.get_connected_modules(lambda mod: mod.is_qtm_type)
# This uses the module of the correct type with the lowest slot index
module = list(modules.values())[0]
Intra-cast using fb_pop_data#
IDs 16–255 are reserved for intra-cast or multi-cast routing. With intra-cast, data is delivered to sequencers within the same module, depending on routing. Here we use intra-cast, therefore routing must be configured before the sequence is executed.
Here we use fb_pop_data to retrieve a message by a known ID.
[5]:
ID = 16
value = 420
cluster.clear_router()
module.set_local_route(ID) # Send to all sequencers in this module
Below we use two sequencers, one sender and one receiver. Sequencer 0 (sender): Intra-casts the immediate value 420 with ID 16. Sequencer 1 (receiver): waits 150 ns for data with ID 16 to arrive, then pops it into R0 using fb_pop_data.
[6]:
prog_sender = f"""
wait_sync 4 # Synchronize sequencers
fb_com_data {ID}, {value}, 4 # Intra-cast value {value} with ID {ID}
stop
"""
[7]:
prog_receiver = f"""
wait_sync 4 # Synchronize sequencers
wait 150 # Block until data ID {ID} is in FIFO
fb_pop_data {ID}, R0 # Pop message with ID {ID} into R0
stop
"""
[8]:
module.sequencer0.sync_en(True)
module.sequencer1.sync_en(True)
module.sequencer0.sequence(
{
"waveforms": {},
"program": prog_sender,
"acquisitions": {},
"weights": {},
}
)
module.sequencer1.sequence(
{
"waveforms": {},
"program": prog_receiver,
"acquisitions": {},
"weights": {},
}
)
[9]:
module.arm_sequencer(0)
module.arm_sequencer(1)
module.start_sequencer()
print("Sequencer 0 status:", module.get_sequencer_status(0))
print("Sequencer 1 status:", module.get_sequencer_status(1))
Sequencer 0 status: Status: OKAY, State: STOPPED, Exit Code: 0, Info Flags: NONE, Warning Flags: NONE, Error Flags: NONE, Log: []
Sequencer 1 status: Status: OKAY, State: STOPPED, Exit Code: 0, Info Flags: NONE, Warning Flags: NONE, Error Flags: NONE, Log: []
Verify data#
[10]:
communicated_data = module.sequencer1.get_register("R0")
if communicated_data == value:
print(f"✓ Verification passed: received {communicated_data} == sent {value}.")
else:
print(f"✗ Verification failed: expected {value}, got {communicated_data}.")
✓ Verification passed: received 420 == sent 420.
Intra-cast using fb_pull_data#
This part demonstrates fb_pull_data, which pulls the next message at the top of the FIFO regardless of its ID. The ID is written into a register alongside the value, which is useful when the receiver does not know the ID in advance.
Sequencer 0 (sender): intra-casts value 420 with ID 16.
Sequencer 1 (receiver): pulls the message from the FIFO using fb_pull_data. The data will be stored in R0 and the ID in R1.
[11]:
prog_receiver_pull = """
wait_sync 4 # Synchronize sequencers
wait 150 # Block until message is in FIFO
fb_pull_data R1, R0 # Pull message: ID -> R1, value -> R0
stop
"""
[12]:
module.sequencer0.sequence(
{
"waveforms": {},
"program": prog_sender,
"acquisitions": {},
"weights": {},
}
)
module.sequencer1.sequence(
{
"waveforms": {},
"program": prog_receiver_pull,
"acquisitions": {},
"weights": {},
}
)
[13]:
module.arm_sequencer(0)
module.arm_sequencer(1)
module.start_sequencer()
print("Sequencer 0 status:", module.get_sequencer_status(0))
print("Sequencer 1 status:", module.get_sequencer_status(1))
Sequencer 0 status: Status: OKAY, State: STOPPED, Exit Code: 0, Info Flags: FORCED_STOP, Warning Flags: NONE, Error Flags: NONE, Log: []
Sequencer 1 status: Status: ERROR, State: STOPPED, Exit Code: 0, Info Flags: FORCED_STOP, Warning Flags: NONE, Error Flags: SEQUENCE_PROCESSOR_RT_EXEC_COMMAND_UNDERFLOW, Log: []
Verify data#
[14]:
received_id = module.sequencer1.get_register("R1") # R1 holds the id
received_value = module.sequencer1.get_register("R0") # R0 holds the value
print(f"Received ID: {received_id} (expected {ID}), value: {received_value} (expected {value})")
if received_value == value and received_id == ID:
print("✓ Verification passed.")
else:
print("✗ Verification failed.")
Received ID: 16 (expected 16), value: 420 (expected 420)
✓ Verification passed.
Stop#
Finally, let’s stop the sequencers if they haven’t already and close the instrument connection. One can also display a detailed snapshot containing the instrument parameters before closing the connection by uncommenting the corresponding lines.
[15]:
# Stop all sequencers.
module.stop_sequencer()
# Print status of sequencers 0 and 1 (should now say it is stopped).
print(module.get_sequencer_status(0))
print(module.get_sequencer_status(1))
print()
Status: OKAY, State: STOPPED, Exit Code: 0, Info Flags: FORCED_STOP, Warning Flags: NONE, Error Flags: NONE, Log: []
Status: ERROR, State: STOPPED, Exit Code: 0, Info Flags: FORCED_STOP, Warning Flags: NONE, Error Flags: SEQUENCE_PROCESSOR_RT_EXEC_COMMAND_UNDERFLOW, Log: []