See also

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

Gate Leak Test#

In this tutorial we will demonstrate gate leakage test which can be performed with the QSM module. The gate leakage test aims to measure unwanted current flow between the gate electrode and the underlying channel or quantum dot which occurs due to imperfections in the gate insulation. The tutorial will showcase the gate leakage test on:

  • Single gate leakage test measurement

  • Multiple gate leakage test measurement This test is executed via sourcing voltage from one channel and subsequently measuring the current from the same channel.

Setup#

First, we are going to import the required packages.

[ ]:
from __future__ import annotations

import matplotlib.pyplot as plt
import numpy as np
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

[ ]:
cluster_ip = "10.10.200.42"
cluster_name = "cluster0"

Connect to Cluster#

We now make a connection with the Cluster.

[ ]:
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())

Get connected modules#

[ ]:
# QSM modules
modules = cluster.get_connected_modules(lambda mod: mod.is_qsm_type)
# This uses the module of the correct type with the lowest slot index
module = list(modules.values())[0]

Single Channel Measurement

[ ]:
# As a first example, we demonstrate a single-channel measurement where we use the first channel of the QSM module (O1) both source voltage and measure current.
# If you are running this script directly without connecting any load or device to the module output, ensure that you use two different channels for sourcing and measurement. Note that the QSM has a low output impedance of 2$\Omega$; therefore, you must limit the voltage to ensure the current stays below the 50 mA safety threshold to prevent hardware damage.
channel_name = "io_channel0"  ## change this to test different channels
selected_channel = getattr(module, channel_name)
compliance_current = 10e-9  # A; abort if > compliance

start_voltage = 0.0
stop_voltage = 5.0
num_points = 21
voltages = np.linspace(start_voltage, stop_voltage, num_points)

selected_channel.source_mode("v_source")
selected_channel.slew_rate(0.2)
selected_channel.measure_mode("fine_nanoampere")  # automatic, coarse


voltages = np.linspace(start_voltage, stop_voltage, num_points)
data_v = []
data_i = []


try:
    for v_set in voltages:
        selected_channel.coarse_voltage(v_set)
        current_meas = selected_channel.measure_current()
        data_v.append(v_set)
        data_i.append(current_meas)
        if abs(current_meas) > compliance_current:
            print(f"Too much current: \n(|{current_meas:.3e}| A > {compliance_current:.3e} A)")
            break

except TypeError:
    pass  # Workaround for known issue with dummy module.


print("Ramping down the voltage to zero...")
selected_channel.coarse_voltage(0.0)

if len(data_v) > 0:
    plt.figure(figsize=(8, 5))
    data_i_nA = [x * 1e9 for x in data_i]
    plt.plot(data_v, data_i_nA, "o-", label="Leakage Current")
    plt.title(f"Gate Leak Test (Ch {selected_channel.io_channel_idx})")
    plt.xlabel("Gate Voltage (V)")
    plt.ylabel("Current (nA)")
    plt.grid(True)
    plt.legend()
    plt.tight_layout()
    plt.show()

Multi-Channel Measurement Compared to the single channel measurement, we may extend the measurement on the QSM to conduct multiple gates leakage test by using other channels on the QSM which will be showcased below.

[ ]:
channel_names = [
    "io_channel0",
    "io_channel1",
    "io_channel2",
    "io_channel3",
    "io_channel4",
    "io_channel5",
    "io_channel6",
    "io_channel7",
]

compliance_current = 100e-9  # A; abort specific channel if > compliance

start_voltage = 0.0
stop_voltage = 5.0
num_points = 21
voltages = np.linspace(start_voltage, stop_voltage, num_points)

channels = {}
active_channel_names = []

for name in channel_names:
    ch_obj = getattr(module, name)
    ch_obj = getattr(module, name)

    ch_obj.source_mode("v_source")
    ch_obj.measure_mode("fine_nanoampere")  # automatic, coarse
    ch_obj.slew_rate(10.0)

    channels[name] = {"obj": ch_obj, "v": [], "i": [], "status": "Running"}
    active_channel_names.append(name)

for v_set in voltages:
    if not active_channel_names:
        print("No active channels remaining. Stopping.")
        break

    for name in active_channel_names:
        channels[name]["obj"].coarse_voltage(v_set)

    for name in list(active_channel_names):
        ch_data = channels[name]
        ch_obj = ch_data["obj"]

        try:
            current_meas = ch_obj.measure_current()
            ch_data["v"].append(v_set)
            ch_data["i"].append(current_meas)

            if abs(current_meas) > compliance_current:
                print(f"[{name}] Compliance Exceeded ({current_meas * 1e9:.3f} nA). Ramping to 0V.")

                ch_obj.coarse_voltage(0.0)
                ch_data["status"] = "Compliance Fail"
                active_channel_names.remove(name)

        except TypeError:
            pass  # Workaround for known issue with dummy module.

# Ensure all channels are at 0V
module.reset_io_channel_output()

fig, (ax_pass, ax_fail) = plt.subplots(1, 2, figsize=(14, 6))

ax_pass.set_title("Passed Channels")
ax_pass.set_xlabel("Gate Voltage (V)")
ax_pass.set_ylabel("Current (nA)")
ax_pass.grid(True)

ax_fail.set_title("Failed / Compliance Hit")
ax_fail.set_xlabel("Gate Voltage (V)")
ax_fail.set_ylabel("Current (nA)")
ax_fail.grid(True)

has_passed = False
has_failed = False

for name in channel_names:
    if name in channels:
        data_v = channels[name]["v"]
        data_i = channels[name]["i"]
        status = channels[name]["status"]

        if len(data_v) > 0:
            data_i_nA = np.array(data_i) * 1e9

            if status == "Running":
                ax_pass.plot(data_v, data_i_nA, "o-", label=name)
                has_passed = True
            else:
                ax_fail.plot(data_v, data_i_nA, "x--", label=f"{name} ({status})")
                has_failed = True

if has_passed:
    ax_pass.legend()
if has_failed:
    ax_fail.legend()

plt.tight_layout()
plt.show()