From 2d7a72c57ea4ff6787c745549188c4438fb2d3f1 Mon Sep 17 00:00:00 2001 From: Carter Turn Date: Mon, 5 Feb 2024 10:49:13 -0500 Subject: [PATCH 1/6] Add ability to program prawnblaster via binary block command --- .../PrawnBlaster/blacs_workers.py | 76 ++++++++++++++----- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/labscript_devices/PrawnBlaster/blacs_workers.py b/labscript_devices/PrawnBlaster/blacs_workers.py index 08e5b47d..c2e5d783 100644 --- a/labscript_devices/PrawnBlaster/blacs_workers.py +++ b/labscript_devices/PrawnBlaster/blacs_workers.py @@ -292,27 +292,63 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): # Program instructions for pseudoclock, pulse_program in enumerate(pulse_programs): - for i, instruction in enumerate(pulse_program): - if i == len(self.smart_cache[pseudoclock]): - # Pad the smart cache out to be as long as the program: - self.smart_cache[pseudoclock].append(None) - - # Only program instructions that differ from what's in the smart cache: - if self.smart_cache[pseudoclock][i] != instruction: - self.prawnblaster.write( - b"set %d %d %d %d\r\n" - % ( - pseudoclock, - i, - instruction["half_period"], - instruction["reps"], + # check if it is more efficient to fully refresh + if not fresh and self.smart_cache[pseudoclock] is not None: + # get more convenient handles to smart cache arrays + curr_inst = self.smart_cache[pseudoclock] + + # if arrays aren't of same shape, only compare up to smaller array size + n_curr = len(curr_inst) + n_new = len(pulse_program) + if n_curr > n_new: + # technically don't need to reprogram current elements beyond end of new elements + new_inst = np.sum(curr_inst[:n_new] != pulse_program) + elif n_curr < n_new: + n_diff = n_new - n_curr + val_diffs = np.sum(curr_inst != pulse_program[:n_curr]) + new_inst = val_diffs + n_diff + else: + new_inst = np.sum(curr_inst != inst) + + if new_inst / total_inst > 0.1: + fresh = True + + if fresh or self.smart_cache[pseudoclock] is None: + print('programming from scratch') + self.prawnblaster.write(b"setb %d %d %d\r\n" % (pseudoclock, 0, len(pulse_program))) + serial_buffer = b'' + for i in range(0, len(pulse_program)): + serial_buffer += struct.pack(' Date: Mon, 5 Feb 2024 20:53:22 -0500 Subject: [PATCH 2/6] Fix imports and typos --- labscript_devices/PrawnBlaster/blacs_workers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/labscript_devices/PrawnBlaster/blacs_workers.py b/labscript_devices/PrawnBlaster/blacs_workers.py index c2e5d783..1c0d9960 100644 --- a/labscript_devices/PrawnBlaster/blacs_workers.py +++ b/labscript_devices/PrawnBlaster/blacs_workers.py @@ -13,6 +13,7 @@ import time import labscript_utils.h5_lock import h5py +import numpy as np from blacs.tab_base_classes import Worker from labscript_utils.connections import _ensure_str import labscript_utils.properties as properties @@ -38,6 +39,7 @@ def init(self): global time; import time global re; import re global numpy; import numpy + global struct; import struct global zprocess; import zprocess self.smart_cache = {} self.cached_pll_params = {} @@ -292,6 +294,7 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): # Program instructions for pseudoclock, pulse_program in enumerate(pulse_programs): + total_inst = len(pulse_program) # check if it is more efficient to fully refresh if not fresh and self.smart_cache[pseudoclock] is not None: # get more convenient handles to smart cache arrays @@ -308,7 +311,7 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): val_diffs = np.sum(curr_inst != pulse_program[:n_curr]) new_inst = val_diffs + n_diff else: - new_inst = np.sum(curr_inst != inst) + new_inst = np.sum(curr_inst != pulse_program) if new_inst / total_inst > 0.1: fresh = True From dc50f3c608c3dcf0f4ad30708d3ffe4ac0cdca6d Mon Sep 17 00:00:00 2001 From: Carter Turn Date: Mon, 19 Feb 2024 00:11:04 -0500 Subject: [PATCH 3/6] Add check for PrawnBlaster binary programming support (via version number) --- .../PrawnBlaster/blacs_workers.py | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/labscript_devices/PrawnBlaster/blacs_workers.py b/labscript_devices/PrawnBlaster/blacs_workers.py index 1c0d9960..8d1e6490 100644 --- a/labscript_devices/PrawnBlaster/blacs_workers.py +++ b/labscript_devices/PrawnBlaster/blacs_workers.py @@ -71,6 +71,40 @@ def init(self): self.prawnblaster.write(b"setinpin %d %d\r\n" % (i, in_pin)) assert self.prawnblaster.readline().decode() == "ok\r\n" + # Check if fast serial is available + self.fast_serial = self.version_greater_than((1, 1, 0)) + + def get_version(self): + self.prawnblaster.write(b"version\r\n") + version_str = self.prawnblaster.readline().decode() + assert version_str.startswith("version: ") + version = version_str[9:].strip() + + version_overclock_list = version.split('-') + overclock = False + if len(version_overclock_list) == 2: + if version_overclock_list[1] == 'overclock': + overclock = True + + version_number = version_overclock_list[0].split('.') + assert len(version_number) == 3 + + return (int(version_number[0]), int(version_number[1]), int(version_number[2]), overclock) + + def version_greater_than(self, target_version): + version = self.get_version() + if version[0] > target_version[0]: + return True + if version[0] < target_version[0]: + return False + if version[1] > target_version[1]: + return True + if version[1] < target_version[1]: + return False + if version[2] >= target_version[2]: + return True + return False + def check_status(self): """Checks the operational status of the PrawnBlaster. @@ -296,7 +330,7 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): for pseudoclock, pulse_program in enumerate(pulse_programs): total_inst = len(pulse_program) # check if it is more efficient to fully refresh - if not fresh and self.smart_cache[pseudoclock] is not None: + if not fresh and self.smart_cache[pseudoclock] is not None and self.fast_serial: # get more convenient handles to smart cache arrays curr_inst = self.smart_cache[pseudoclock] @@ -316,8 +350,8 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): if new_inst / total_inst > 0.1: fresh = True - if fresh or self.smart_cache[pseudoclock] is None: - print('programming from scratch') + if (fresh or self.smart_cache[pseudoclock] is None) and self.fast_serial: + print('binary programming') self.prawnblaster.write(b"setb %d %d %d\r\n" % (pseudoclock, 0, len(pulse_program))) serial_buffer = b'' for i in range(0, len(pulse_program)): From 535e87d847ef540796a5567b873342ccfd80217d Mon Sep 17 00:00:00 2001 From: Carter Turn Date: Mon, 19 Feb 2024 00:12:54 -0500 Subject: [PATCH 4/6] Faster method for generating binary programming data stream --- labscript_devices/PrawnBlaster/blacs_workers.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/labscript_devices/PrawnBlaster/blacs_workers.py b/labscript_devices/PrawnBlaster/blacs_workers.py index 8d1e6490..f83e96f2 100644 --- a/labscript_devices/PrawnBlaster/blacs_workers.py +++ b/labscript_devices/PrawnBlaster/blacs_workers.py @@ -353,11 +353,9 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): if (fresh or self.smart_cache[pseudoclock] is None) and self.fast_serial: print('binary programming') self.prawnblaster.write(b"setb %d %d %d\r\n" % (pseudoclock, 0, len(pulse_program))) - serial_buffer = b'' - for i in range(0, len(pulse_program)): - serial_buffer += struct.pack(' Date: Thu, 7 Mar 2024 09:12:50 -0500 Subject: [PATCH 5/6] Cleanup version number acquisition and comparison code. --- .../PrawnBlaster/blacs_workers.py | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/labscript_devices/PrawnBlaster/blacs_workers.py b/labscript_devices/PrawnBlaster/blacs_workers.py index f83e96f2..cd8c17f4 100644 --- a/labscript_devices/PrawnBlaster/blacs_workers.py +++ b/labscript_devices/PrawnBlaster/blacs_workers.py @@ -72,7 +72,8 @@ def init(self): assert self.prawnblaster.readline().decode() == "ok\r\n" # Check if fast serial is available - self.fast_serial = self.version_greater_than((1, 1, 0)) + version, _ = self.get_version() + self.fast_serial = version >= (1, 1, 0) def get_version(self): self.prawnblaster.write(b"version\r\n") @@ -86,24 +87,10 @@ def get_version(self): if version_overclock_list[1] == 'overclock': overclock = True - version_number = version_overclock_list[0].split('.') - assert len(version_number) == 3 - - return (int(version_number[0]), int(version_number[1]), int(version_number[2]), overclock) - - def version_greater_than(self, target_version): - version = self.get_version() - if version[0] > target_version[0]: - return True - if version[0] < target_version[0]: - return False - if version[1] > target_version[1]: - return True - if version[1] < target_version[1]: - return False - if version[2] >= target_version[2]: - return True - return False + version = tuple(int(v) for v in version_overclock_list[0].split('.')) + assert len(version) == 3 + + return version, overclock def check_status(self): """Checks the operational status of the PrawnBlaster. From 08874c9a43f9258f2c68b4e3689e37efcd4efd4b Mon Sep 17 00:00:00 2001 From: Carter Turn Date: Sun, 28 Apr 2024 16:16:36 -0400 Subject: [PATCH 6/6] Add "ready" check for device-side error handling --- labscript_devices/PrawnBlaster/blacs_workers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/labscript_devices/PrawnBlaster/blacs_workers.py b/labscript_devices/PrawnBlaster/blacs_workers.py index cd8c17f4..52733627 100644 --- a/labscript_devices/PrawnBlaster/blacs_workers.py +++ b/labscript_devices/PrawnBlaster/blacs_workers.py @@ -340,6 +340,10 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh): if (fresh or self.smart_cache[pseudoclock] is None) and self.fast_serial: print('binary programming') self.prawnblaster.write(b"setb %d %d %d\r\n" % (pseudoclock, 0, len(pulse_program))) + response = self.prawnblaster.readline().decode() + assert ( + response == "ready\r\n" + ), f"PrawnBlaster said '{response}', expected 'ready'" program_array = np.array([pulse_program['half_period'], pulse_program['reps']], dtype='