1313import time
1414import labscript_utils .h5_lock
1515import h5py
16+ import numpy as np
1617from blacs .tab_base_classes import Worker
1718from labscript_utils .connections import _ensure_str
1819import labscript_utils .properties as properties
@@ -38,6 +39,7 @@ def init(self):
3839 global time ; import time
3940 global re ; import re
4041 global numpy ; import numpy
42+ global struct ; import struct
4143 global zprocess ; import zprocess
4244 self .smart_cache = {}
4345 self .cached_pll_params = {}
@@ -69,6 +71,27 @@ def init(self):
6971 self .prawnblaster .write (b"setinpin %d %d\r \n " % (i , in_pin ))
7072 assert self .prawnblaster .readline ().decode () == "ok\r \n "
7173
74+ # Check if fast serial is available
75+ version , _ = self .get_version ()
76+ self .fast_serial = version >= (1 , 1 , 0 )
77+
78+ def get_version (self ):
79+ self .prawnblaster .write (b"version\r \n " )
80+ version_str = self .prawnblaster .readline ().decode ()
81+ assert version_str .startswith ("version: " )
82+ version = version_str [9 :].strip ()
83+
84+ version_overclock_list = version .split ('-' )
85+ overclock = False
86+ if len (version_overclock_list ) == 2 :
87+ if version_overclock_list [1 ] == 'overclock' :
88+ overclock = True
89+
90+ version = tuple (int (v ) for v in version_overclock_list [0 ].split ('.' ))
91+ assert len (version ) == 3
92+
93+ return version , overclock
94+
7295 def check_status (self ):
7396 """Checks the operational status of the PrawnBlaster.
7497
@@ -292,27 +315,66 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh):
292315
293316 # Program instructions
294317 for pseudoclock , pulse_program in enumerate (pulse_programs ):
295- for i , instruction in enumerate (pulse_program ):
296- if i == len (self .smart_cache [pseudoclock ]):
297- # Pad the smart cache out to be as long as the program:
298- self .smart_cache [pseudoclock ].append (None )
299-
300- # Only program instructions that differ from what's in the smart cache:
301- if self .smart_cache [pseudoclock ][i ] != instruction :
302- self .prawnblaster .write (
303- b"set %d %d %d %d\r \n "
304- % (
305- pseudoclock ,
306- i ,
307- instruction ["half_period" ],
308- instruction ["reps" ],
318+ total_inst = len (pulse_program )
319+ # check if it is more efficient to fully refresh
320+ if not fresh and self .smart_cache [pseudoclock ] is not None and self .fast_serial :
321+ # get more convenient handles to smart cache arrays
322+ curr_inst = self .smart_cache [pseudoclock ]
323+
324+ # if arrays aren't of same shape, only compare up to smaller array size
325+ n_curr = len (curr_inst )
326+ n_new = len (pulse_program )
327+ if n_curr > n_new :
328+ # technically don't need to reprogram current elements beyond end of new elements
329+ new_inst = np .sum (curr_inst [:n_new ] != pulse_program )
330+ elif n_curr < n_new :
331+ n_diff = n_new - n_curr
332+ val_diffs = np .sum (curr_inst != pulse_program [:n_curr ])
333+ new_inst = val_diffs + n_diff
334+ else :
335+ new_inst = np .sum (curr_inst != pulse_program )
336+
337+ if new_inst / total_inst > 0.1 :
338+ fresh = True
339+
340+ if (fresh or self .smart_cache [pseudoclock ] is None ) and self .fast_serial :
341+ print ('binary programming' )
342+ self .prawnblaster .write (b"setb %d %d %d\r \n " % (pseudoclock , 0 , len (pulse_program )))
343+ response = self .prawnblaster .readline ().decode ()
344+ assert (
345+ response == "ready\r \n "
346+ ), f"PrawnBlaster said '{ response } ', expected 'ready'"
347+ program_array = np .array ([pulse_program ['half_period' ],
348+ pulse_program ['reps' ]], dtype = '<u4' ).T
349+ self .prawnblaster .write (program_array .tobytes ())
350+ response = self .prawnblaster .readline ().decode ()
351+ assert (
352+ response == "ok\r \n "
353+ ), f"PrawnBlaster said '{ response } ', expected 'ok'"
354+ self .smart_cache [pseudoclock ] = pulse_program
355+ else :
356+ print ('incremental programming' )
357+ for i , instruction in enumerate (pulse_program ):
358+ if i == len (self .smart_cache [pseudoclock ]):
359+ # Pad the smart cache out to be as long as the program:
360+ self .smart_cache [pseudoclock ].append (None )
361+
362+ # Only program instructions that differ from what's in the smart cache:
363+ if self .smart_cache [pseudoclock ][i ] != instruction :
364+ self .prawnblaster .write (
365+ b"set %d %d %d %d\r \n "
366+ % (
367+ pseudoclock ,
368+ i ,
369+ instruction ["half_period" ],
370+ instruction ["reps" ],
371+ )
309372 )
310- )
311- response = self .prawnblaster .readline ().decode ()
312- assert (
313- response == "ok\r \n "
314- ), f"PrawnBlaster said '{ response } ', expected 'ok'"
315- self .smart_cache [pseudoclock ][i ] = instruction
373+ response = self .prawnblaster .readline ().decode ()
374+ assert (
375+ response == "ok\r \n "
376+ ), f"PrawnBlaster said '{ response } ', expected 'ok'"
377+ self .smart_cache [pseudoclock ][i ] = instruction
316378
317379 if not self .is_master_pseudoclock :
318380 # Start the Prawnblaster and have it wait for a hardware trigger
0 commit comments