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 theclock_line
argument or through aTrigger
from anIntermediateDevice
via thetrigger_device
andtrigger_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 anIntermediateDevice
via aDigitalOut
.trigger_connection (str, optional) – Which output of the
trigger_device
is connected to the PrawnDO hardware trigger input. Not required when directly connected to aClockline
.clock_line (
Clockline
, optional) – Used when connected directly to aClockline
. Not required if using a trigger device.com_port (str) – COM port assinged to the PrawnDO by the OS. Takes the form of
COMd
whered
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:
- 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.
- 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:
- 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:
- 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)
- class labscript_devices.PrawnDO.blacs_tabs.PrawnDOTab(notebook, settings, restart=False)[source]
Bases:
DeviceTab
- class labscript_devices.PrawnDO.blacs_workers.PrawnDOInterface(com_port)[source]
Bases:
object
- 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).
- 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:
- 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:
- Returns:
String response from the PrawnDO
- Return type:
- class labscript_devices.PrawnDO.blacs_workers.PrawnDOWorker(*args, **kwargs)[source]
Bases:
Worker
- abort_buffered()[source]
Aborts a currently running buffered execution.
- Returns:
True
is abort was successful.- Return type:
- 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:
- 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: