See also

A Jupyter notebook version of this tutorial can be downloaded here.

LINQ Routing Configuration#

Every piece of data sent via LINQ-based feedback is tagged with an 8-bit id that controls which sequencers receive it. There are three routing modes, which differ primarily in latency. For a full overview of latencies per data type and routing mode, see the LINQ-based feedback documentation.

IDs 1–15 are reserved for self-cast and require no routing configuration. IDs 16–255 must be explicitly configured using one of the routing commands below before the sequencers are armed:

Command

Description

cluster.clear_router()

Reset all routing to factory defaults.

module.set_local_route(id, sequencers=None)

Intra-cast: route id within the module, optionally to a subset of sequencers.

cluster.set_cmm_route(id, targets)

Multi-cast: route id to specific modules or sequencers via the CMM.

cluster.set_broadcast(id)

Broadcast: route id to every sequencer in the cluster via the CMM.

ID 0 is reserved to disable sharing of a specific data type.

This tutorial demonstrates all three routing modes using Q1 register / immediate transfers, which are supported on every module variant.

Hardware Requirements#

  • A Qblox Cluster with one module containing at least two sequencers.

Setup#

[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]:
# QRC modules
modules = cluster.get_connected_modules(lambda mod: mod.is_qrc_type)
# This uses the module of the correct type with the lowest slot index
module = list(modules.values())[0]

Self-Cast#

IDs 1–15 are reserved for self-cast — no routing configuration is needed. Data sent with these IDs is delivered only to the feedback queue of the originating sequencer.

Experiment Parameters#

[5]:
SELF_CAST_ID = 5  # Any value 1–15
VALUE = 42

Define Q1ASM Program#

[6]:
prog_self_cast = f"""
fb_com_data     {SELF_CAST_ID}, {VALUE}, 4     # Self-cast {VALUE} with ID {SELF_CAST_ID}
wait            60                             # Wait for self-cast latency
fb_pop_data     {SELF_CAST_ID}, R0             # Pop message into R0
stop
"""

Upload and Run#

[7]:
module.sequencer0.sequence(
    {
        "waveforms": {},
        "program": prog_self_cast,
        "acquisitions": {},
        "weights": {},
    }
)

module.arm_sequencer(0)
module.start_sequencer()

print("Sequencer 0 status:", module.get_sequencer_status(0))
Sequencer 0 status: Status: OKAY, State: STOPPED, Exit Code: 0, Info Flags: NONE, Warning Flags: NONE, Error Flags: NONE, Log: []

Verify Result#

[8]:
self_cast_result = module.sequencer0.get_register("R0")

print(f"Sent    : {VALUE}")
print(f"Received: {self_cast_result}")
print(
    "✓ PASS"
    if self_cast_result == VALUE
    else "✗ FAIL — check that the ID is in the self-cast range (1–15)"
)
Sent    : 42
Received: 42
✓ PASS

Intra-Cast#

module.set_local_route(id) routes data with the given id to all sequencers within the module. To restrict delivery to a subset, pass an explicit list of sequencers:

module.set_local_route(id, [module.sequencer1, module.sequencer3])

Experiment Parameters#

[9]:
INTRA_ID = 16
VALUE = 99

Configure Routing#

[10]:
cluster.clear_router()
module.set_local_route(INTRA_ID)  # All sequencers in module receive ID 16

Configure Sequencers#

[11]:
module.sequencer0.sync_en(True)
module.sequencer1.sync_en(True)

Define Q1ASM Programs#

[12]:
prog_intra_sender = f"""
wait_sync       4                              # Synchronize with receiver
fb_com_data     {INTRA_ID}, {VALUE}, 4         # Intra-cast {VALUE} with ID {INTRA_ID}
stop
"""

prog_intra_receiver = f"""
wait_sync       4                              # Synchronize with sender
wait            150                            # Wait for intra-cast latency (~150 ns)
fb_pop_data     {INTRA_ID}, R0                 # Pop message with ID {INTRA_ID} into R0
stop
"""

Upload and Run#

[13]:
module.sequencer0.sequence(
    {
        "waveforms": {},
        "program": prog_intra_sender,
        "acquisitions": {},
        "weights": {},
    }
)
module.sequencer1.sequence(
    {
        "waveforms": {},
        "program": prog_intra_receiver,
        "acquisitions": {},
        "weights": {},
    }
)

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 Result#

[14]:
intra_result = module.sequencer1.get_register("R0")

print(f"Sent    : {VALUE}")
print(f"Received: {intra_result}")
print(
    "✓ PASS"
    if intra_result == VALUE
    else "✗ FAIL — check that set_local_route was called before arming"
)
Sent    : 99
Received: 99
✓ PASS

Multi-Cast and Broadcast#

cluster.set_cmm_route(id, targets) routes data via the CMM to specific modules or sequencers. cluster.set_broadcast(id) delivers to every sequencer in the cluster, including the sender.

Here we configure two IDs simultaneously: ID 32 is targeted to sequencer 1 only (multi-cast), while ID 64 is broadcast to all sequencers. Sequencer 0 sends both in one program, and sequencer 1 pops both.

Experiment Parameters#

[15]:
MULTI_ID = 32
BROADCAST_ID = 64
VALUE_MULTI = 2025
VALUE_BCAST = 12345

Configure Routing#

[16]:
cluster.clear_router()
cluster.set_cmm_route(MULTI_ID, [module.sequencer1])  # ID 32 → sequencer 1 only
cluster.set_broadcast(BROADCAST_ID)  # ID 64 → all sequencers

Configure Sequencers#

[17]:
module.sequencer0.sync_en(True)
module.sequencer1.sync_en(True)

Define Q1ASM Programs#

[18]:
prog_multi_sender = f"""
wait_sync       4
fb_com_data     {MULTI_ID},     {VALUE_MULTI},  4      # Multi-cast to sequencer 1
fb_com_data     {BROADCAST_ID}, {VALUE_BCAST},  4      # Broadcast to all sequencers
stop
"""

prog_multi_receiver = f"""
wait_sync       4
wait            500                                    # Wait conservatively for multi-cast latency
fb_pop_data     {MULTI_ID},     R0                     # Pop multi-cast value into R0
fb_pop_data     {BROADCAST_ID}, R1                     # Pop broadcast value into R1
stop
"""

Upload and Run#

[19]:
module.sequencer0.sequence(
    {
        "waveforms": {},
        "program": prog_multi_sender,
        "acquisitions": {},
        "weights": {},
    }
)
module.sequencer1.sequence(
    {
        "waveforms": {},
        "program": prog_multi_receiver,
        "acquisitions": {},
        "weights": {},
    }
)

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 Result#

[20]:
multi_result = module.sequencer1.get_register("R0")
bcast_result = module.sequencer1.get_register("R1")

print(f"Multi-cast  — sent: {VALUE_MULTI},  received: {multi_result}")
print(f"Broadcast   — sent: {VALUE_BCAST}, received: {bcast_result}")
print(
    "✓ PASS"
    if multi_result == VALUE_MULTI and bcast_result == VALUE_BCAST
    else "✗ FAIL — check CMM route and broadcast configuration"
)
Multi-cast  — sent: 2025,  received: 2025
Broadcast   — sent: 12345, received: 12345
✓ PASS

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.

[21]:
# 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: OKAY, State: STOPPED, Exit Code: 0, Info Flags: FORCED_STOP, Warning Flags: NONE, Error Flags: NONE, Log: []