HDAWG

Initialize the tk.HDAWG instrument drivers as in the previous example and setup and connect the device. Its device serial is dev8030 and we connect via ethernet (1gbe) to the host at 10.42.0.226.

[1]:
import numpy as np
import matplotlib.pyplot as plt

import qcodes as qc
import zhinst.qcodes as ziqc

hdawg = ziqc.HDAWG("hdawg1", "dev8138", interface="1gbe", host="10.42.0.226")
Successfully connected to data server at 10.42.0.2268004 api version: 6
Successfully connected to device DEV8138 on interface 1GBE
Connected to: Zurich Instruments HDAWG (serial:dev8138, firmware:66245) in 1.25s
[2]:
print([k for k in hdawg.submodules.keys()])
print([k for k in hdawg.parameters.keys()])
['awgs', 'stats', 'oscs', 'status', 'sines', 'dios', 'system', 'sigouts', 'triggers', 'features', 'cnts']
['IDN', 'clockbase']

AWG cores

The HDAWG features eight AWG Channels grouped in four AWG Cores with two channels each. These four AWG Cores can be accessed through the awgs submodule of the instrument driver. awgs is a Channel List of four HDAWG-specific AWGCore Instrument Channels. The AWGCore handles the basic functionality to control the AWG Core and program its sequencer to play a specific series of waveforms and commands.

[3]:
print([k for k in hdawg.awgs[0].parameters.keys()])
['outputs', 'output1', 'output2', 'gain1', 'gain2', 'modulation_phase_shift', 'modulation_freq']

An AWG Core features a list of Parameters as well as methods that allow for a high-level control of the AWG Core. The Parameters control the channel outputs as well as their modulation settings.

For instance, the channel outputs can be turned on and off like this:

[ ]:
hdawg.awgs[0].output1("on")
hdawg.awgs[0].output2("on")

AWG output modulation

The output modulation for both channel outputs can be turned on with:

[6]:
hdawg.awgs[0].enable_iq_modulation()

This method applies the corresponding settings for IQ Modulation using one of the internal oscillators and two sine generators. The sines are used to modulate the AWG output channels at a 90 degree phase difference. The parameters modulation_freq, modulation_phase_shift and gain1, gain2 correspond to the settings of the oscillator and the sine generators.

[7]:
print(hdawg.awgs[0].modulation_freq.__doc__)
Node: oscs/0/freq
Description: Sets the modulation frequency of the AWG output channels.
Type: Double
Properties: Read, Write
Unit: Hz


Parameter class:

* `name` modulation_freq
* `label` Modulation Frequency
* `unit` Hz
* `vals` <Numbers>

A simple sweep of the modulation frequency could look like this:

[8]:
import time

for f in np.linspace(10e6, 20e6, 21):
    hdawg.awgs[0].modulation_freq(f)
    # do something here
    time.sleep(0.1)

The AWG Core also holds a SequenceProgram that defines the pulse sequence played by the AWG Sequencer. One of the next examples will describe how this sequence program is defined and how you can configure it to play the sequence of waveforms that you want to use.

Programming the AWG core

[9]:
# turn the outputs on
hdawg.awgs[0].output1("on")
hdawg.awgs[0].output2("on")

All the parameters of the sequence program are set using set_sequence_params(...) which lets the user pass keyword arguments. The most important ones are

  • sequence_type: the type of sequence to be programmed. The ‘Simple’ sequence type allows the user to upload their own sample-based waveforms which are played in a well defined sequence.

  • period: the period in seconds in which the single shots of the experiment should be repeated

  • repetitions: the number of repetitions of the sequence

[10]:
hdawg.awgs[0].set_sequence_params(
    sequence_type="Simple",            # let the user uplaod the sample-defined waveforms
    period=20e-6,                      # play the waveform every 20 us
    repetitions=1e3,                   # repeat 1000 times
)

Because we are using the ‘Simple’ sequence type, we can define the waveforms as numpy arrays. The waveform is defined by samples, which means that the timing calculation has to be taken care of by the user! For example the sampling rate of the HDAWG is 2.4 Gs, thus a waveform of 1000 samples correpsonds to a duration of 1000 / 2.4e9 = 416.6 ns.

We first reset the queue to ensure it is empty, then we add our waveform to the queue. The waveform is always defined for both channels of the AWG Core simultaneously.

[11]:
wave1 = np.ones(1000)
wave2 = -np.ones(1000)

hdawg.awgs[0].reset_queue()                   # reset the queue
hdawg.awgs[0].queue_waveform(wave1, wave2)    # add the waveform to the queue

# what is in the queue?
hdawg.awgs[0].waveforms
Current length of queue: 1
[11]:
[<zhinst.toolkit.helpers.waveform.Waveform at 0x2659aca3cf8>]

Next, we need to tell the AWG Core to compile the sequence program and to upload all the waveforms in the queue. The method compile_and_upload_waveforms() takes care of both these jobs for the ‘Simple’ sequence (to ensure the correct sequence program is compiled before all the waveforms are uploaded).

[12]:
hdawg.awgs[0].compile_and_upload_waveforms()  # compile the sequence program and uplaod all waveforms in the queue
Compilation successful
hdawg1-0: Sequencer status: ELF file uploaded
Upload of 1 waveforms took 0.027058 s

Now we can run our seuqence program!

[13]:
hdawg.awgs[0].output1("off")
[14]:
hdawg.awgs[0].compile()
Compilation successful
hdawg1-0: Sequencer status: ELF file uploaded
[15]:
hdawg.awgs[0].run()        # start the AWG Core!
hdawg.awgs[0].wait_done()  # wait until the AWG Core has finished playing the program

This program will output two rectangular pulses of length 1000 samples (416.6 ns) with amplitude 1 on channel 1 and amplitude -1 on channel 2. The waveforms are played every 20 us and are repeated 1000 times.