PrawnDO

This labscript device controls the PrawnDO open-source digital output generator based on the Raspberry Pi Pico platform. It is designed to be a companion device to the PrawnBlaster allowing for arbitrary digital output specification (in contrast to the variable pseudoclock generation of the PrawnBlaster).

Initial code development was in this repository.

Specifications

The PrawnDO takes advantage of the specs of the Pico to provide the following:

  • 16 synchronous digital outputs with timing specs equivalent to the PrawnBlaster

    • Timing resolution for an update is 1 clock cycle (10 ns at default 100 MHz clock)

    • Minimum time between updates (on any output) is 5 clock cycles (50 ns with 100 MHz clock)

    • Maximum time between updates (on any output) is 2^32-1 clock cycles (~42.9 s with 100 MHz clock)

    • Updates are internally timed (ie only initial triggering is needed, not for every update)

  • 30,000 instructions (where each instruction can be held between 5 and 2^32-1 clock cycles)

  • Support for external hardware triggers to begin and re-start execution after a wait.

  • Can be referenced to an external LVCMOS clock

  • Internal clock can be set up to 133 MHz (which scales timing specs accordingly)

Installation

In order to turn the standard Pico into a PrawnDO, you need to load the custom firmware available in the Github repo onto the board. The simplest way to do this is by holding the reset button on the board while plugging the USB into a computer. This will bring up a mounted folder that you copy-paste the firmware to. Once copied, the board will reset and be ready to go.

Note that this device communicates using a virtual COM port. The number is assigned by the controlling computer and will need to be determined in order for BLACS to connect to the PrawnDO.

Usage

The pinout for the PrawnDO is as follows:

  • Outputs 0-15: GPIO pins 0-15, respectively.

  • External Trigger input: GPIO 16

  • External Clock input: GPIO 20

Note that signal cables should be connected to the Pico digital grounds for proper operation.

The PrawnDO can provide up to 16 digital outputs, which are accessed via name.outputs. Each channel is specified using with a string of the form 'doD', where 'D' is the GPIO number (i.e. 'do10', is the specification for GPIO 10 of the PrawnDO).

An example connection table that uses the PrawnBlaster and PrawnDO:

from labscript import *

from labscript_devices.PrawnBlaster.labscript_devices import PrawnBlaster
from labscript_devices.PrawnDO.labscript_devices import PrawnDO

PrawnBlaster(name='prawn', com_port='COM6', num_pseudoclocks=1)

PrawnDO(name='prawn_do', com_port='COM5', clock_line=prawn.clocklines[0])

DigitalOut('do0', prawn_do.outputs, 'do0')
DigitalOut('do1', prawn_do.outputs, 'do1')
DigitalOut('do12', prawn_do.outputs, 'do12')

if __name__ == "__main__":

    start()

    stop(1)

Note

The PrawnDO is designed to be directly connected to a Clockline, something not normally done for internally-timed devices in labscript. This is merely for simplicity under the most typical use case of adding standard digital output capability to a PrawnBlaster master pseudoclocking device.

When used in this way, the PrawnDO can share the Clockline with other devices, especially with other PrawnDO boards allowing for significant fan-out. Nominally, the PrawnDO will ignore clock ticks from other devices on the same Clockline, such as a DAQ. However, standard cautions should be taken when sharing a clockline between devices (i.e. don’t overload the physical output driver with too many parallel devices, limit the number of devices doing fast things at nearly the same times, validate critical timings/operations independently).

The PrawnDO can also be triggerd from a standard DigitalOut Trigger. In this case, the clock_line argument is not used, but the standard trigger_device and trigger_connection arguments.

Synchronization

The PrawnDO generates output based on internal timing with external starting triggers in a manner nearly equivalent to the PrawnBlaster. This means that under a typical use case of a PrawnBlaster used with a PrawnDO, the output timings of the devices will drift as their internal clocks drift. Each Pico is specified to have a clock with better than 50 ppm stability, meaning drift could be as bad as 100 ppm between two devices (e.g. 100 microsecond drift after 1 second of run time). In practice, relative drift is often around 5 ppm.

To overcome this, either use labscript waits right before time-sensitive operations to resynchronize back to within a single clock cycle (\(\pm10\) ns), or use a common external clock for both devices.

Unless buffering/level protecting circuitry is used, both the PrawnBlaster and the PrawnDO require LVCMOS square-wave clock signals. An example evaluation board with a flexible, multi-channel LVCMOS clock generator is the SI535X-B20QFN-EVB. Note that interrupting the external clock can cause the Pico serial communication to freeze. Recovery requires resetting the board via a power cycle or shorting the RUN pin to ground to re-enable default options including the internal clock.

An example connection table using external clocks with the default frequency of 100 MHz is:

from labscript import *

from labscript_devices.PrawnBlaster.labscript_devices import PrawnBlaster
from labscript_devices.PrawnDO.labscript_devices import PrawnDO

PrawnBlaster(name='prawn', com_port='COM6', num_pseudoclocks=1,
             external_clock_pin=20)

PrawnDO(name='prawn_do', com_port='COM5', clock_line=prawn.clocklines[0],
        external_clock=True)

DigitalOut('do0', prawn_do.outputs, 'do0')
DigitalOut('do1', prawn_do.outputs, 'do1')
DigitalOut('do12', prawn_do.outputs, 'do12')

if __name__ == "__main__":

    start()

    stop(1)

Input/Output Buffers

While the PrawnBlaster and PrawnDO boards can be used as is, it is often a good idea to add unity-gain channel buffers to the inputs and outputs. Using buffers and line drivers from a LVCMOS family with 5V/TTL tolerant inputs can provide compatibility with TTL inputs and drive higher capacitance loads (such a long BNC cables) more reliably. An example that implements these buffers can be found here.

Waits

All waits in the PrawnDO are indefinite waits in the parlance of the PrawnBlaster. This means they will never time out, but must have an external trigger to restart execution. Changing a digital output state concurrently with a wait results in the PrawnDO output holding the updated value during the wait. For example, in the following code snippet, the output of do0 will be low during the wait. For the output of do0 to remain high during the wait, the second instruction (do0.go_low(t)) must be at least 5 clock cycles after the wait start time.

t = 0
do0.go_high(t)
t = 1e-3
wait('my_wait', t)
do0.go_low(t)

Detailed Documentation

class labscript_devices.PrawnDO.labscript_devices.PrawnDO(name, trigger_device=None, trigger_connection=None, clock_line=None, com_port='COM1', clock_frequency=100000000.0, external_clock=False)[source]

Bases: PseudoclockDevice

PrawnDO digital output device.

This labscript device provides general purpose digital outputs using a Raspberry Pi Pico with custom firmware.

It supports two types of connections to a parent device: direct to a Clockline via the clock_line argument or through a Trigger from an IntermediateDevice via the trigger_device and trigger_connection arguments. Only one should be supplied.

Parameters:
  • name (str) – python variable name to assign to the PrawnDO

  • trigger_device (IntermediateDevice, optional) – Device that will send the starting hardware trigger. Used when connecting to an IntermediateDevice via a DigitalOut.

  • trigger_connection (str, optional) – Which output of the trigger_device is connected to the PrawnDO hardware trigger input. Not required when directly connected to a Clockline.

  • clock_line (Clockline, optional) – Used when connected directly to a Clockline. Not required if using a trigger device.

  • com_port (str) – COM port assinged to the PrawnDO by the OS. Takes the form of COMd where d is an integer.

  • clock_frequency (float, optional) – System clock frequency, in Hz. Must be less than 133 MHz. Default is 100e6.

  • external_clock (bool, optional) – Whether to use an external clock. Default is False.

add_device(device)[source]

Adds a child device to this device.

Parameters:

device (Device) – Device to add.

Raises:

LabscriptError – If device is not an allowed child of this device.

allowed_children = [<class 'labscript_devices.PrawnDO.labscript_devices._PrawnDOPseudoclock'>]

Defines types of devices that are allowed to be children of this device.

Type:

list

clock_limit = 10000000.0

Maximum allowable clock rate

clock_resolution = 1e-08

Minimum resolvable unit of time, corresponsd to system clock period.

description = 'PrawnDO device'

Brief description of the device.

generate_code(hdf5_file)[source]

Generate hardware instructions for device and children, then save to h5 file.

Will recursively call generate_code for all children devices.

Parameters:

hdf5_file (h5py.File) – Handle to shot file.

property initial_trigger_time
input_response_time = 5e-08

Time between hardware trigger and output starting.

max_instructions = 30000

Maximum number of instructions. Set by zmq timeout when sending the commands.

minimum_duration = 5e-08

Minimum time between updates on the outputs.

set_initial_trigger_time(*args, **kwargs)[source]

Sets the initial trigger time of the pseudoclock.

If this is the master pseudoclock, time must be 0.

Parameters:

t (float) – Time, in seconds, to trigger this device.

trigger_delay = 5e-08
trigger_minimum_duration = 1.6e-07

Minimum required duration of hardware trigger. A fairly large over-estimate.

wait_delay = 5e-08

Minimum required length of wait before a retrigger can be detected.

class labscript_devices.PrawnDO.labscript_devices._PrawnDOClockline(name, pseudoclock, connection, ramping_allowed=True, **kwargs)[source]

Bases: ClockLine

Dummy clockline for use with PrawnDO

Ensures only a single _PrawnDODirectOutputs is connected to the PrawnDO

Creates a Device.

Parameters:
  • name (str) – python variable name to assign this device to.

  • parent_device (Device) – Parent of this device.

  • connection (str) – Connection on this device that links to parent.

  • call_parents_add_device (bool, optional) – Flag to command device to call its parent device’s add_device when adding a device.

  • added_properties (dict, optional) –

  • gui

  • worker

  • start_order (int, optional) – Priority when starting, sorted with all devices.

  • stop_order (int, optional) – Priority when stopping, sorted with all devices.

  • **kwargs – Other options to pass to parent.

add_device(device)[source]

Adds a child device to this device.

Parameters:

device (Device) – Device to add.

Raises:

LabscriptError – If device is not an allowed child of this device.

class labscript_devices.PrawnDO.labscript_devices._PrawnDOIntermediateDevice(name, parent_device, **kwargs)[source]

Bases: IntermediateDevice

Provides some error checking to ensure parent_device is a ClockLine.

Calls Device.__init__().

Parameters:
  • name (str) – python variable name to assign to device

  • parent_device (ClockLine) – Parent ClockLine device.

allowed_children = [<class 'labscript.labscript.Trigger'>]

Defines types of devices that are allowed to be children of this device.

Type:

list

description = 'PrawnDO Internal Intermediate Device'

Brief description of the device.

class labscript_devices.PrawnDO.labscript_devices._PrawnDOPseudoclock(name, pseudoclock_device, connection, **kwargs)[source]

Bases: Pseudoclock

Dummy pseudoclock for use with PrawnDO.

This pseudoclock ensures only one clockline is attached.

Creates a Pseudoclock.

Parameters:
  • name (str) – python variable name to assign the device instance to.

  • pseudoclock_device (PseudoclockDevice) – Parent pseudoclock device

  • connection (str) – Connection on this device that links to parent

  • **kwargs – Passed to Device().

add_device(device)[source]

Adds a child device to this device.

Parameters:

device (Device) – Device to add.

Raises:

LabscriptError – If device is not an allowed child of this device.

class labscript_devices.PrawnDO.labscript_devices._PrawnDigitalOutputs(name, parent_device, **kwargs)[source]

Bases: IntermediateDevice

Collective output class for the PrawnDO.

This class aggregates the 16 individual digital outputs of the PrawnDO. It is for internal use of the PrawnDO only.

Parameters:
  • name (str) – name to assign

  • parent_device (Device) – Parent device PrawnDO is connected to

add_device(device)[source]

Confirms channel specified is valid before adding

Parameters:

() (device) – Device to attach. Must be a digital output. Allowed connections are a string of the form doXX

allowed_channels = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
allowed_children = [<class 'labscript.labscript.DigitalOut'>]

Defines types of devices that are allowed to be children of this device.

Type:

list

class labscript_devices.PrawnDO.blacs_tabs.PrawnDOTab(notebook, settings, restart=False)[source]

Bases: DeviceTab

get_child_from_connection_table(parent_device_name, port)[source]
initialise_GUI()[source]
initialise_workers()[source]
class labscript_devices.PrawnDO.blacs_workers.PrawnDOInterface(com_port)[source]

Bases: object

_read_full_buffer()[source]

Used to get any extra lines from device after a failed send_command

adm_batch(pulse_program)[source]

Sends pulse program as single binary block using adm command.

Parameters:

pulse_program (numpy.ndarray) – Structured array of program to send. Must have first column as bit sets (<u2) and second as reps (<u4).

close()[source]
get_version()[source]
min_version = (1, 2, 0)

Minimum compatible firmware version tuple

output_state()[source]

Reads the current output state of the PrawnDO

Returns:

Output state of all 16 bits

Return type:

int

Raises:

LabscriptError – If response is not okrn

send_command(command, readlines=False)[source]

Sends the supplied string command and checks for a response.

Automatically applies the correct termination characters.

Parameters:
  • command (str) – Command to send. Termination and encoding is done automatically.

  • readlines (bool, optional) – Use pyserial’s readlines functionality to read multiple response lines. Slower as it relies on timeout to terminate reading.

Returns:

String response from the PrawnDO

Return type:

str

send_command_ok(command)[source]

Sends the supplied string command and confirms ‘ok’ response.

Parameters:

command (str) – String command to send.

Raises:

LabscriptError – If response is not okrn

status()[source]

Reads the status of the PrawnDO

Returns:

tuple containing

  • run-status (int): Run status code

  • clock-status (int): Clock status code

Return type:

(int, int)

Raises:

LabscriptError – If response is not okrn

class labscript_devices.PrawnDO.blacs_workers.PrawnDOWorker(*args, **kwargs)[source]

Bases: Worker

_dict_to_int(d)[source]

Converts dictionary of outputs to an integer mask.

Parameters:

d (dict) – Dictionary of output states

Returns:

Integer mask of the 16 output states.

Return type:

int

_int_to_dict(val)[source]

Converts an integer mask to a dictionary of outputs.

Parameters:

val (int) – 16-bit integer mask to convert

Returns:

Dictonary with output channels as keys and values are boolean states

Return type:

dict

abort_buffered()[source]

Aborts a currently running buffered execution.

Returns:

True is abort was successful.

Return type:

bool

abort_transition_to_buffered()[source]

Aborts transition to buffered.

Calls abort_buffered()

check_remote_values()[source]

Checks the remote state of the PrawnDO.

Called automatically by BLACS.

Returns:

Dictionary of the digital output states.

Return type:

dict

check_status()[source]

Checks operational status of the PrawnDO.

Automatically called by BLACS to update status.

Returns:

Tuple containing:

  • run-status (int): Possible values are:

    • 0 : manual mode

    • 1 : transitioning to buffered execution

    • 2 : buffered execution

    • 3 : abort requested

    • 4 : aborting buffered execution

    • 5 : last buffered execution aborted

    • 6 : transitioning to manual mode

  • clock-status (int): Possible values are:

    • 0 : internal clock

    • 1 : external clock

Return type:

(int, int)

init()[source]
program_manual(front_panel_values)[source]

Change output states in manual mode.

Returns:

Output states after command execution.

Return type:

dict

shutdown()[source]

Closes serial connection to PrawnDO

transition_to_buffered(device_name, h5file, initial_values, fresh)[source]
transition_to_manual()[source]

Transition to manual mode after buffered execution completion.

Returns:

True if transition to manual is successful.

Return type:

bool

class labscript_devices.PrawnDO.runviewer_parsers.PrawnDOParser(path, device)[source]

Bases: object

get_traces(add_trace, clock=None)[source]
class labscript_devices.PrawnDO.runviewer_parsers._PrawnDOIntermediateParser(path, device)[source]

Bases: object

get_traces(add_trace, clock=None)[source]