Skip to content

Commit 91b35c8

Browse files
committed
Hopefully fixed the timing bug where PrawnBlaster pulses were twice as long as they should have been. Also fixed an incorrect max_instructions value
1 parent 909d296 commit 91b35c8

File tree

3 files changed

+34
-24
lines changed

3 files changed

+34
-24
lines changed

labscript_devices/PrawnBlaster/blacs_workers.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,13 @@ def check_status(self):
7070
if response != "wait not yet available\r\n":
7171
# Parse the response from the PrawnBlaster
7272
wait_remaining = int(response)
73-
clock_resolution = self.device_properties["clock_resolution"]
73+
# Divide by two since the clock_resolution is for clock pulses, which
74+
# have twice the clock_resolution of waits
75+
# Technically, waits also only have a resolution of `clock_resolution`
76+
# but the PrawnBlaster firmware accepts them in half of that so that
77+
# they are easily converted to seconds via the clock frequency.
78+
# Maybe this was a mistake, but it's done now.
79+
clock_resolution = self.device_properties["clock_resolution"] / 2
7480
input_response_time = self.device_properties["input_response_time"]
7581
timeout_length = round(
7682
self.wait_table[self.current_wait]["timeout"] / clock_resolution
@@ -219,7 +225,7 @@ def transition_to_buffered(self, device_name, h5file, initial_values, fresh):
219225
if self.smart_cache[pseudoclock][i] != instruction:
220226
self.prawnblaster.write(
221227
b"set %d %d %d %d\r\n"
222-
% (pseudoclock, i, instruction["period"], instruction["reps"])
228+
% (pseudoclock, i, instruction["half_period"], instruction["reps"])
223229
)
224230
response = self.prawnblaster.readline().decode()
225231
assert (

labscript_devices/PrawnBlaster/labscript_devices.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,19 @@ def generate_code(self, *args, **kwargs):
103103

104104
class PrawnBlaster(PseudoclockDevice):
105105
description = "PrawnBlaster"
106-
clock_limit = 1 / 50e-9
107-
clock_resolution = 10e-9
106+
clock_limit = 1 / 100e-9
107+
clock_resolution = 20e-9
108108
# There appears to be ~50ns buffer on input and then we know there is 80ns between
109109
# trigger detection and first output pulse
110110
input_response_time = 50e-9
111111
trigger_delay = input_response_time + 80e-9
112112
# Overestimate that covers indefinite waits (which labscript does not yet support)
113-
trigger_minimum_duration = 160e-9
113+
trigger_minimum_duration = 160e-9
114114
# There are 4 ASM instructions between end of pulse and being ready to detect
115115
# a retrigger
116116
wait_delay = 40e-9
117117
allowed_children = [_PrawnBlasterPseudoclock, _PrawnBlasterDummyPseudoclock]
118-
max_instructions = 60000
118+
max_instructions = 30000
119119

120120
@set_passed_properties(
121121
property_names={
@@ -160,8 +160,8 @@ def __init__(
160160
# Update the specs based on the number of pseudoclocks
161161
self.max_instructions = self.max_instructions // num_pseudoclocks
162162
# Update the specs based on the clock frequency
163-
if self.clock_resolution != 1 / clock_frequency:
164-
factor = (1 / clock_frequency) / self.clock_resolution
163+
if self.clock_resolution != 2 / clock_frequency:
164+
factor = (2 / clock_frequency) / self.clock_resolution
165165
self.clock_limit *= factor
166166
self.clock_resolution *= factor
167167
self.input_response_time *= factor
@@ -258,7 +258,9 @@ def clocklines(self):
258258
def add_device(self, device):
259259
if len(self.child_devices) < (
260260
self.num_pseudoclocks + self.use_wait_monitor
261-
) and isinstance(device, (_PrawnBlasterPseudoclock, _PrawnBlasterDummyPseudoclock)):
261+
) and isinstance(
262+
device, (_PrawnBlasterPseudoclock, _PrawnBlasterDummyPseudoclock)
263+
):
262264
PseudoclockDevice.add_device(self, device)
263265
elif isinstance(device, _PrawnBlasterPseudoclock):
264266
raise LabscriptError(
@@ -282,7 +284,7 @@ def generate_code(self, hdf5_file):
282284
for i, pseudoclock in enumerate(self.pseudoclocks):
283285
current_wait_index = 0
284286

285-
# Compress clock instructions with the same period
287+
# Compress clock instructions with the same half_period
286288
reduced_instructions = []
287289
for instruction in pseudoclock.clock:
288290
if instruction == "WAIT":
@@ -293,10 +295,12 @@ def generate_code(self, hdf5_file):
293295
wait_table[current_wait_index]
294296
][1]
295297
current_wait_index += 1
296-
# The following period and reps indicates a wait instruction
298+
# The following half_period and reps indicates a wait instruction
297299
reduced_instructions.append(
298300
{
299-
"period": round(wait_timeout / self.clock_resolution),
301+
"half_period": round(
302+
wait_timeout / (self.clock_resolution / 2)
303+
),
300304
"reps": 0,
301305
}
302306
)
@@ -306,43 +310,43 @@ def generate_code(self, hdf5_file):
306310
# Two waits in a row are an indefinite wait
307311
reduced_instructions.append(
308312
{
309-
"period": 2 ** 32 - 1,
313+
"half_period": 2 ** 32 - 1,
310314
"reps": 0,
311315
}
312316
)
313317
reduced_instructions.append(
314318
{
315-
"period": 2 ** 32 - 1,
319+
"half_period": 2 ** 32 - 1,
316320
"reps": 0,
317321
}
318322
)
319323

320324
# Normal instruction
321325
reps = instruction["reps"]
322-
# period is in quantised units:
323-
period = int(round(instruction["step"] / self.clock_resolution))
326+
# half_period is in quantised units:
327+
half_period = int(round(instruction["step"] / self.clock_resolution))
324328
if (
325329
# If there is a previous instruction
326330
reduced_instructions
327331
# And it's not a wait
328332
and reduced_instructions[-1]["reps"] != 0
329-
# And the periods match
330-
and reduced_instructions[-1]["period"] == period
333+
# And the half_periods match
334+
and reduced_instructions[-1]["half_period"] == half_period
331335
# And the sum of the previous reps and current reps won't push it over the limit
332336
and (reduced_instructions[-1]["reps"] + reps) < (2 ** 32 - 1)
333337
):
334338
# Combine instructions!
335339
reduced_instructions[-1]["reps"] += reps
336340
else:
337341
# New instruction
338-
reduced_instructions.append({"period": period, "reps": reps})
342+
reduced_instructions.append({"half_period": half_period, "reps": reps})
339343

340344
# Only add this if there is room in the instruction table. The PrawnBlaster
341345
# firmware has extre room at the end for an instruction that is always 0
342346
# and cannot be set over serial!
343347
if len(reduced_instructions) != self.max_instructions:
344-
# The following period and reps indicates a stop instruction:
345-
reduced_instructions.append({"period": 0, "reps": 0})
348+
# The following half_period and reps indicates a stop instruction:
349+
reduced_instructions.append({"half_period": 0, "reps": 0})
346350

347351
# Check we have not exceeded the maximum number of supported instructions
348352
# for this number of speudoclocks
@@ -352,10 +356,10 @@ def generate_code(self, hdf5_file):
352356
)
353357

354358
# Store these instructions to the h5 file:
355-
dtypes = [("period", int), ("reps", int)]
359+
dtypes = [("half_period", int), ("reps", int)]
356360
pulse_program = np.zeros(len(reduced_instructions), dtype=dtypes)
357361
for j, instruction in enumerate(reduced_instructions):
358-
pulse_program[j]["period"] = instruction["period"]
362+
pulse_program[j]["half_period"] = instruction["half_period"]
359363
pulse_program[j]["reps"] = instruction["reps"]
360364
group.create_dataset(
361365
f"PULSE_PROGRAM_{i}", compression=config.compression, data=pulse_program

labscript_devices/PrawnBlaster/runviewer_parsers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def get_traces(self, add_trace, clock=None):
8282
for j in range(1, -1, -1):
8383
time.append(t)
8484
states.append(j)
85-
t += row['period'] * clock_factor
85+
t += row['half_period'] * clock_factor
8686

8787
clock = (np.array(time), np.array(states))
8888

0 commit comments

Comments
 (0)