def generate_code(self, hdf5_file):
     # Generate the hardware instructions
     self.init_device_group(hdf5_file)
     PseudoclockDevice.generate_code(self, hdf5_file)
     dig_outputs, ignore = self.get_direct_outputs()
     pb_inst = self.convert_to_pb_inst(dig_outputs, [], {}, {}, {})
     self.write_pb_inst_to_h5(pb_inst, hdf5_file) 
示例#2
0
    def add_device(self, device):
        """Adds child devices.

        This is automatically called by the labscript compiler.

        Args:
            device (:class:`_PrawnBlasterPseudoclock` or :class:`_PrawnBlasterDummyPseudoclock`):
                Instance to attach to the device. Only the allowed children can be attached.
        """

        if len(self.child_devices) < (
                self.num_pseudoclocks + self.use_wait_monitor) and isinstance(
                    device,
                    (_PrawnBlasterPseudoclock, _PrawnBlasterDummyPseudoclock)):
            PseudoclockDevice.add_device(self, device)
        elif isinstance(device, _PrawnBlasterPseudoclock):
            raise LabscriptError(
                f"The {self.description} {self.name} automatically creates the correct number of pseudoclocks."
                +
                "Instead of instantiating your own Pseudoclock object, please use the internal"
                + f" ones stored in {self.name}.pseudoclocks")
        else:
            raise LabscriptError(
                f"You have connected {device.name} (class {device.__class__}) to {self.name}, but {self.name} does not support children with that class."
            )
    def __init__(self,
                 name,
                 trigger_device=None,
                 trigger_connection=None,
                 serial='',
                 reference_clock='internal',
                 clock_frequency=100e6,
                 use_wait_monitor=False,
                 trigger_debounce_clock_ticks=10):
        # set device properties based on clock frequency
        self.clock_limit = clock_frequency / 2
        self.clock_resolution = 1 / clock_frequency
        # We'll set this to be 2x the debounce count
        self.trigger_minimum_duration = 2 * trigger_debounce_clock_ticks / clock_frequency
        # Todo: confirm this.
        #       It should only be 5 clock cycles + the debounce_clock_ticks
        #       as I think it takes 3 clock cycles to propagate to the state
        #       machine of the FPGA code (due to the three uses of the non-blocking <= verilog operator
        #       in the debounce code) and then another 2 cycles before the output goes high
        #       (one to move out of the wait for retrigger code and then one because the update of the
        #        output state is non-blocking)\
        #
        self.trigger_delay = (5 +
                              trigger_debounce_clock_ticks) / clock_frequency
        # Todo: confirm this
        #       I believe it is 1 clock cycle
        self.wait_delay = self.clock_resolution

        PseudoclockDevice.__init__(self, name, trigger_device,
                                   trigger_connection)
        self.BLACS_connection = serial

        if trigger_debounce_clock_ticks >= 2**16:
            raise LabscriptError(
                'The %s %s trigger_debounce_clock_ticks parameter must be between 0 and 65535'
                % (self.description, self.name))

        # create Pseudoclock and clockline
        self._pseudoclock = CiceroOpalKellyXEM3001Pseudoclock(
            '%s_pseudoclock' % name, self,
            'clock')  # possibly a better connection name than 'clock'?
        # Create the internal direct output clock_line
        self._clock_line = ClockLine('%s_clock_line' % name, self.pseudoclock,
                                     'Clock Out')

        # Create internal devices for connecting to a wait monitor
        self.__wait_monitor_dummy_pseudoclock = CiceroOpalKellyXEM3001DummyPseudoclock(
            '%s__dummy_wait_pseudoclock' % name, self, '_')
        self.__wait_monitor_dummy_clock_line = CiceroOpalKellyXEM3001DummyClockLine(
            '%s__dummy_wait_clock_line' % name,
            self.__wait_monitor_dummy_pseudoclock, '_')
        self.__wait_monitor_intermediate_device = CiceroOpalKellyXEM3001DummyIntermediateDevice(
            '%s_internal_wait_monitor_outputs' % name,
            self.__wait_monitor_dummy_clock_line)

        if use_wait_monitor:
            WaitMonitor('%s__wait_monitor' % name,
                        self.internal_wait_monitor_outputs, 'internal',
                        self.internal_wait_monitor_outputs, 'internal',
                        self.internal_wait_monitor_outputs, 'internal')
 def generate_code(self, hdf5_file):
     # Generate the hardware instructions
     hdf5_file.create_group('/devices/' + self.name)
     PseudoclockDevice.generate_code(self, hdf5_file)
     dig_outputs = self.direct_outputs.get_all_outputs()
     npg_inst = self.convert_to_npg_inst(dig_outputs)
     self.write_npg_inst_to_h5(npg_inst, hdf5_file)
 def generate_code(self, hdf5_file):
     PseudoclockDevice.generate_code(self, hdf5_file)
     group = hdf5_file['devices'].create_group(self.name)   
     
     # compress clock instructions with the same period: This will
     # halve the number of instructions roughly, since the PineBlaster
     # does not have a 'slow clock':
     reduced_instructions = []
     for instruction in self.pseudoclock.clock:
         if instruction == 'WAIT':
             # The following period and reps indicates a wait instruction
             reduced_instructions.append({'period': 0, 'reps': 1})
             continue
         reps = instruction['reps']
         # period is in quantised units:
         period = int(round(instruction['step']/self.clock_resolution))
         if reduced_instructions and reduced_instructions[-1]['period'] == period:
             reduced_instructions[-1]['reps'] += reps
         else:
             reduced_instructions.append({'period': period, 'reps': reps})
     # The following period and reps indicates a stop instruction:
     reduced_instructions.append({'period': 0, 'reps': 0})
     if len(reduced_instructions) > self.max_instructions:
         raise LabscriptError("%s %s has too many instructions. It has %d and can only support %d"%(self.description, self.name, len(reduced_instructions), self.max_instructions))
     # Store these instructions to the h5 file:
     dtypes = [('period',int),('reps',int)]
     pulse_program = np.zeros(len(reduced_instructions),dtype=dtypes)
     for i, instruction in enumerate(reduced_instructions):
         pulse_program[i]['period'] = instruction['period']
         pulse_program[i]['reps'] = instruction['reps']
     group.create_dataset('PULSE_PROGRAM', compression = config.compression, data=pulse_program)
     # TODO: is this needed, the PulseBlasters don't save it... 
     self.set_property('is_master_pseudoclock', self.is_master_pseudoclock, location='device_properties')
     self.set_property('stop_time', self.stop_time, location='device_properties')
 def add_device(self, device):
     if not self.child_devices and isinstance(device, Pseudoclock):
         PseudoclockDevice.add_device(self, device)
     elif isinstance(device, Pseudoclock):
         raise LabscriptError(
             f'The {self.name} PseudoclockDevice only supports a single Pseudoclock, so it automatically creates one.'
             +
             f'Instead of instantiating your own Pseudoclock object, please use the internal one stored in {self.name}.pseudoclock'
         )
     elif isinstance(device, DigitalOut):
         raise LabscriptError(
             f'You have connected {device.name} directly to {self.name}, which is not allowed. You should instead specify '
             +
             f'the parent_device of {device.name} as {self.name}.direct_outputs'
         )
     elif isinstance(device, ClockLine):
         raise LabscriptError(
             f'You have connected {device.name} directly to {self.name}, which is not allowed. You should instead specify '
             +
             f'the parent_device of {device.name} as {self.name}.pseudoclock'
         )
     else:
         raise LabscriptError(
             f'You have connected {device.name} (class {device.__class__}) to {self.name}, but {self.name} does not support children with that class.'
         )
 def generate_code(self, hdf5_file):
     # Generate the hardware instructions
     self.init_device_group(hdf5_file)
     PseudoclockDevice.generate_code(self, hdf5_file)
     dig_outputs, ignore = self.get_direct_outputs()
     pb_inst = self.convert_to_pb_inst(dig_outputs, [], {}, {}, {})
     self.write_pb_inst_to_h5(pb_inst, hdf5_file)
 def __init__(self, name, trigger_device=None, trigger_connection=None, usbport='COM1'):
     PseudoclockDevice.__init__(self, name, trigger_device, trigger_connection)
     self.BLACS_connection = usbport
     
     # create Pseudoclock and clockline
     self._pseudoclock = PineBlasterPseudoclock('%s_pseudoclock'%name, self, 'clock') # possibly a better connection name than 'clock'?
     # Create the internal direct output clock_line
     self._clock_line = ClockLine('%s_clock_line'%name, self.pseudoclock, 'internal')
示例#9
0
 def add_device(self, device):
     if len(self.child_devices) < 2 and isinstance(device, Pseudoclock):
         PseudoclockDevice.add_device(self, device)            
     elif isinstance(device, Pseudoclock):
         raise LabscriptError('The %s %s automatically creates a Pseudoclock because it only supports one. '%(self.description, self.name) +
                              'Instead of instantiating your own Pseudoclock object, please use the internal' +
                              ' one stored in %s.pseudoclock'%self.name)
     else:
         raise LabscriptError('You have connected %s (class %s) to %s, but %s does not support children with that class.'%(device.name, device.__class__, self.name, self.name))
示例#10
0
 def generate_code(self, hdf5_file):
     PseudoclockDevice.generate_code(self, hdf5_file)
     group = hdf5_file['devices'].create_group(self.name)   
     
     # compress clock instructions with the same period: This will
     # halve the number of instructions roughly, since the PineBlaster
     # does not have a 'slow clock':
     reduced_instructions = []
     current_wait_index = 0
     wait_table = sorted(compiler.wait_table)
     
     if not self.is_master_pseudoclock:
         reduced_instructions.append({'on': 0, 'off': ((self.trigger_edge_type=='rising') << 1) + 1, 'reps': 0})
             
     for instruction in self.pseudoclock.clock:
         if instruction == 'WAIT':
             # The following period and reps indicates a wait instruction
             wait_timeout = compiler.wait_table[wait_table[current_wait_index]][1]
             current_wait_index += 1
             
             # The actual wait instruction.
             # on_counts correspond to teh number of reference clock cycles
             # to wait for external trigger before auto-resuming. 
             # It overcounts by 1 here because the logic on the FPGA is different for the first reference clock cycle 
             # (you cannot resume until the after second reference clock cycle), so we subtract 1 off the on counts
             # so that it times-out after the correct number of samples
             reduced_instructions.append({'on': round(wait_timeout/self.clock_resolution)-1, 'off': ((self.trigger_edge_type=='rising') << 1) + 1, 'reps': 0})
             continue
         reps = instruction['reps']
         # period is in quantised units:
         periods = int(round(instruction['step']/self.clock_resolution))
         # Get the "high" half of the clock period
         on_period = int(periods/2)
         # Use the remainder to calculate the "off period" (allows slightly assymetric clock signals so to minimise timing errors)
         off_period = periods-on_period
         
         if reduced_instructions and reduced_instructions[-1]['on'] == on_period and reduced_instructions[-1]['off'] == off_period:
             reduced_instructions[-1]['reps'] += reps
         else:
             reduced_instructions.append({'on': on_period, 'off': off_period, 'reps': reps})
     
     if len(reduced_instructions) > self.max_instructions:
         raise LabscriptError("%s %s has too many instructions. It has %d and can only support %d"%(self.description, self.name, len(reduced_instructions), self.max_instructions))
         
     # Store these instructions to the h5 file:
     dtypes = [('on_period',np.int64),('off_period',np.int64),('reps',np.int64)]
     pulse_program = np.zeros(len(reduced_instructions),dtype=dtypes)
     for i, instruction in enumerate(reduced_instructions):
         pulse_program[i]['on_period'] = instruction['on']
         pulse_program[i]['off_period'] = instruction['off']
         pulse_program[i]['reps'] = instruction['reps']
     group.create_dataset('PULSE_PROGRAM', compression = config.compression, data=pulse_program)
     
     self.set_property('is_master_pseudoclock', self.is_master_pseudoclock, location='device_properties')
     self.set_property('stop_time', self.stop_time, location='device_properties')
示例#11
0
 def __init__(self,
              name='dummy_pseudoclock',
              BLACS_connection='dummy_connection',
              **kwargs):
     self.BLACS_connection = BLACS_connection
     PseudoclockDevice.__init__(self, name, None, None, **kwargs)
     self.pseudoclock = Pseudoclock(self.name + '_pseudoclock', self,
                                    'pseudoclock')
     self.clockline = ClockLine(name='clockline',
                                pseudoclock=self.pseudoclock,
                                connection='dummy')
示例#12
0
 def __init__(self, name, ip_address, trigger_device=None, trigger_connection=None):
     PseudoclockDevice.__init__(self, name, trigger_device, trigger_connection)
     self.BLACS_connection = ip_address
     
     # create Pseudoclock and clockline
     self._pseudoclock = RFBlasterPseudoclock('%s_pseudoclock'%name, self, 'clock') # possibly a better connection name than 'clock'?
     # Create the internal direct output clock_line
     self._clock_line = ClockLine('%s_clock_line'%name, self.pseudoclock, 'internal')
     # Create the internal intermediate device connected to the above clock line
     # This will have the DDSs of the RFBlaster connected to it
     self._direct_output_device = RFBlasterDirectOutputs('%s_direct_output_device'%name, self._clock_line)
示例#13
0
 def __init__(self, name, ip_address, trigger_device=None, trigger_connection=None):
     PseudoclockDevice.__init__(self, name, trigger_device, trigger_connection)
     self.BLACS_connection = ip_address
     
     # create Pseudoclock and clockline
     self._pseudoclock = RFBlasterPseudoclock('%s_pseudoclock'%name, self, 'clock') # possibly a better connection name than 'clock'?
     # Create the internal direct output clock_line
     self._clock_line = ClockLine('%s_clock_line'%name, self.pseudoclock, 'internal')
     # Create the internal intermediate device connected to the above clock line
     # This will have the DDSs of the RFBlaster connected to it
     self._direct_output_device = RFBlasterDirectOutputs('%s_direct_output_device'%name, self._clock_line)
示例#14
0
 def add_device(self, device):
     if not self.child_devices and isinstance(device, Pseudoclock):
         PseudoclockDevice.add_device(self, device)
     elif isinstance(device, Pseudoclock):
         raise LabscriptError('The %s %s automatically creates a Pseudoclock because it only supports one. '%(self.description, self.name) +
                              'Instead of instantiating your own Pseudoclock object, please use the internal' +
                              ' one stored in %s.pseudoclock'%self.name)
     elif isinstance(device, DDS):
         #TODO: Defensive programming: device.name may not exist!
         raise LabscriptError('You have connected %s directly to %s, which is not allowed. You should instead specify the parent_device of %s as %s.direct_outputs'%(device.name, self.name, device.name, self.name))
     else:
         raise LabscriptError('You have connected %s (class %s) to %s, but %s does not support children with that class.'%(device.name, device.__class__, self.name, self.name))
示例#15
0
 def add_device(self, device):
     if not self.child_devices and isinstance(device, Pseudoclock):
         PseudoclockDevice.add_device(self, device)
     elif isinstance(device, Pseudoclock):
         raise LabscriptError('The %s %s automatically creates a Pseudoclock because it only supports one. '%(self.description, self.name) +
                              'Instead of instantiating your own Pseudoclock object, please use the internal' +
                              ' one stored in %s.pseudoclock'%self.name)
     elif isinstance(device, DDS):
         #TODO: Defensive programming: device.name may not exist!
         raise LabscriptError('You have connected %s directly to %s, which is not allowed. You should instead specify the parent_device of %s as %s.direct_outputs'%(device.name, self.name, device.name, self.name))
     else:
         raise LabscriptError('You have connected %s (class %s) to %s, but %s does not support children with that class.'%(device.name, device.__class__, self.name, self.name))
示例#16
0
 def __init__(self,
              name='dummy_pseudoclock',
              BLACS_connection='dummy_connection',
              **kwargs):
     self.BLACS_connection = BLACS_connection
     PseudoclockDevice.__init__(self, name, None, None, **kwargs)
     self._pseudoclock = _DummyPseudoclock(
         name=f'{name}_pseudoclock',
         pseudoclock_device=self,
         connection='pseudoclock',
     )
     self._clock_line = ClockLine(
         name=f'{name}_clock_line',
         pseudoclock=self.pseudoclock,
         connection='internal',
     )
 def __init__(self,
              name='narwhal_pulsegen',
              usbport='autodetect',
              **kwargs):
     self.BLACS_connection = usbport
     PseudoclockDevice.__init__(self, name, None, None, **kwargs)
     # Create the internal pseudoclock
     self._pseudoclock = NarwhalPulseGenPseudoclock(
         name=f'{name}_pseudoclock',
         pseudoclock_device=self,
         connection='pseudoclock',
     )
     # Create the internal direct output clock_line
     self._direct_output_clock_line = ClockLine(
         name=f'{name}_direct_output_clock_line',
         pseudoclock=self.pseudoclock,
         connection='internal',
         ramping_allowed=False,
     )
     # Create the internal intermediate device connected to the above clock line
     # This will have the direct DigitalOuts of the NarwhalPulseGen connected to it
     self._direct_output_device = NarwhalPulseGenDirectOutputs(
         name=f'{name}_direct_output_device',
         parent_device=self._direct_output_clock_line)
 def generate_code(self, hdf5_file):
     PseudoclockDevice.generate_code(self, hdf5_file)
     group = self.init_device_group(hdf5_file)
     self.set_property('stop_time',
                       self.stop_time,
                       location='device_properties')
示例#19
0
    def generate_code(self, hdf5_file):
        from rfblaster import caspr
        import rfblaster.rfjuice
        rfjuice_folder = os.path.dirname(rfblaster.rfjuice.__file__)
        
        import rfblaster.rfjuice.const as c
        from rfblaster.rfjuice.cython.make_diff_table import make_diff_table
        from rfblaster.rfjuice.cython.compile import compileD
        # from rfblaster.rfjuice.compile import compileD
        import tempfile
        from subprocess import Popen, PIPE
        
        # Generate clock and save raw instructions to the h5 file:
        PseudoclockDevice.generate_code(self, hdf5_file)
        dtypes = [('time',float),('amp0',float),('freq0',float),('phase0',float),('amp1',float),('freq1',float),('phase1',float)]
        
        times = self.pseudoclock.times[self._clock_line]
        
        data = np.zeros(len(times),dtype=dtypes)
        data['time'] = times
        for dds in self.direct_outputs.child_devices:
            prefix, connection = dds.connection.split()
            data['freq%s'%connection] = dds.frequency.raw_output
            data['amp%s'%connection] = dds.amplitude.raw_output
            data['phase%s'%connection] = dds.phase.raw_output
        group = hdf5_file['devices'].create_group(self.name)
        group.create_dataset('TABLE_DATA',compression=config.compression, data=data)
        
        # Quantise the data and save it to the h5 file:
        quantised_dtypes = [('time',np.int64),
                            ('amp0',np.int32), ('freq0',np.int32), ('phase0',np.int32),
                            ('amp1',np.int32), ('freq1',np.int32), ('phase1',np.int32)]
        quantised_data = np.zeros(len(times),dtype=quantised_dtypes)
        quantised_data['time'] = np.array(c.tT*1e6*data['time']+0.5)
        for dds in range(2):
            # TODO: bounds checking
            # Adding 0.5 to each so that casting to integer rounds:
            quantised_data['freq%d'%dds] = np.array(c.fF*1e-6*data['freq%d'%dds] + 0.5)
            quantised_data['amp%d'%dds]  = np.array((2**c.bitsA - 1)*data['amp%d'%dds] + 0.5)
            quantised_data['phase%d'%dds] = np.array(c.pP*data['phase%d'%dds] + 0.5)
        group.create_dataset('QUANTISED_DATA',compression=config.compression, data=quantised_data)
        # Generate some assembly code and compile it to machine code:
        assembly_group = group.create_group('ASSEMBLY_CODE')
        binary_group = group.create_group('BINARY_CODE')
        diff_group = group.create_group('DIFF_TABLES')
        # When should the RFBlaster wait for a trigger?
        quantised_trigger_times = np.array([c.tT*1e6*t + 0.5 for t in self.trigger_times], dtype=np.int64)
        for dds in range(2):
            abs_table = np.zeros((len(times), 4),dtype=np.int64)
            abs_table[:,0] = quantised_data['time']
            abs_table[:,1] = quantised_data['amp%d'%dds]
            abs_table[:,2] = quantised_data['freq%d'%dds]
            abs_table[:,3] = quantised_data['phase%d'%dds]
            
            # split up the table into chunks delimited by trigger times:
            abs_tables = []
            for i, t in enumerate(quantised_trigger_times):
                subtable = abs_table[abs_table[:,0] >= t]
                try:
                    next_trigger_time = quantised_trigger_times[i+1]
                except IndexError:
                    # No next trigger time
                    pass
                else:
                    subtable = subtable[subtable[:,0] < next_trigger_time]
                subtable[:,0] -= t
                abs_tables.append(subtable)

            # convert to diff tables:
            diff_tables = [make_diff_table(tab) for tab in abs_tables]
            # Create temporary files, get their paths, and close them:
            with tempfile.NamedTemporaryFile(delete=False) as f:
                temp_assembly_filepath = f.name
            with tempfile.NamedTemporaryFile(delete=False) as f:
                temp_binary_filepath = f.name
                
            try:
                # Compile to assembly:
                with open(temp_assembly_filepath,'w') as assembly_file:
                    for i, dtab in enumerate(diff_tables):
                        compileD(dtab, assembly_file, init=(i == 0),
                                 jump_to_start=(i == 0),
                                 jump_from_end=False,
                                 close_end=(i == len(diff_tables) - 1),
                                 local_loop_pre = str(i),
                                 set_defaults = (i==0))
                # Save the assembly to the h5 file:
                with open(temp_assembly_filepath,) as assembly_file:
                    assembly_code = assembly_file.read()
                    assembly_group.create_dataset('DDS%d'%dds, data=assembly_code)
                    for i, diff_table in enumerate(diff_tables):
                        diff_group.create_dataset('DDS%d_difftable%d'%(dds,i), compression=config.compression, data=diff_table)
                # compile to binary:
                compilation = Popen([caspr,temp_assembly_filepath,temp_binary_filepath],
                                     stdout=PIPE, stderr=PIPE, cwd=rfjuice_folder,startupinfo=startupinfo)
                stdout, stderr = compilation.communicate()
                if compilation.returncode:
                    print stdout
                    raise LabscriptError('RFBlaster compilation exited with code %d\n\n'%compilation.returncode + 
                                         'Stdout was:\n %s\n'%stdout + 'Stderr was:\n%s\n'%stderr)
                # Save the binary to the h5 file:
                with open(temp_binary_filepath,'rb') as binary_file:
                    binary_data = binary_file.read()
                # has to be numpy.string_ (string_ in this namespace,
                # imported from pylab) as python strings get stored
                # as h5py as 'variable length' strings, which 'cannot
                # contain embedded nulls'. Presumably our binary data
                # must contain nulls sometimes. So this crashes if we
                # don't convert to a numpy 'fixes length' string:
                binary_group.create_dataset('DDS%d'%dds, data=np.string_(binary_data))
            finally:
                # Delete the temporary files:
                os.remove(temp_assembly_filepath)
                os.remove(temp_binary_filepath)
示例#20
0
    def generate_code(self, hdf5_file):
        from rfblaster import caspr
        import rfblaster.rfjuice
        rfjuice_folder = os.path.dirname(rfblaster.rfjuice.__file__)
        
        import rfblaster.rfjuice.const as c
        from rfblaster.rfjuice.cython.make_diff_table import make_diff_table
        from rfblaster.rfjuice.cython.compile import compileD
        # from rfblaster.rfjuice.compile import compileD
        import tempfile
        from subprocess import Popen, PIPE
        
        # Generate clock and save raw instructions to the h5 file:
        PseudoclockDevice.generate_code(self, hdf5_file)
        dtypes = [('time',float),('amp0',float),('freq0',float),('phase0',float),('amp1',float),('freq1',float),('phase1',float)]

        times = self.pseudoclock.times[self._clock_line]
        
        data = np.zeros(len(times),dtype=dtypes)
        data['time'] = times
        for dds in self.direct_outputs.child_devices:
            prefix, connection = dds.connection.split()
            data['freq%s'%connection] = dds.frequency.raw_output
            data['amp%s'%connection] = dds.amplitude.raw_output
            data['phase%s'%connection] = dds.phase.raw_output
        group = hdf5_file['devices'].create_group(self.name)
        group.create_dataset('TABLE_DATA',compression=config.compression, data=data)
        
        # Quantise the data and save it to the h5 file:
        quantised_dtypes = [('time',np.int64),
                            ('amp0',np.int32), ('freq0',np.int32), ('phase0',np.int32),
                            ('amp1',np.int32), ('freq1',np.int32), ('phase1',np.int32)]

        quantised_data = np.zeros(len(times),dtype=quantised_dtypes)
        quantised_data['time'] = np.array(c.tT*1e6*data['time']+0.5)
        for dds in range(2):
            # TODO: bounds checking
            # Adding 0.5 to each so that casting to integer rounds:
            quantised_data['freq%d'%dds] = np.array(c.fF*1e-6*data['freq%d'%dds] + 0.5)
            quantised_data['amp%d'%dds]  = np.array((2**c.bitsA - 1)*data['amp%d'%dds] + 0.5)
            quantised_data['phase%d'%dds] = np.array(c.pP*data['phase%d'%dds] + 0.5)
        group.create_dataset('QUANTISED_DATA',compression=config.compression, data=quantised_data)
        # Generate some assembly code and compile it to machine code:
        assembly_group = group.create_group('ASSEMBLY_CODE')
        binary_group = group.create_group('BINARY_CODE')
        diff_group = group.create_group('DIFF_TABLES')
        # When should the RFBlaster wait for a trigger?
        quantised_trigger_times = np.array([c.tT*1e6*t + 0.5 for t in self.trigger_times], dtype=np.int64)
        for dds in range(2):
            abs_table = np.zeros((len(times), 4),dtype=np.int64)
            abs_table[:,0] = quantised_data['time']
            abs_table[:,1] = quantised_data['amp%d'%dds]
            abs_table[:,2] = quantised_data['freq%d'%dds]
            abs_table[:,3] = quantised_data['phase%d'%dds]
            
            # split up the table into chunks delimited by trigger times:
            abs_tables = []
            for i, t in enumerate(quantised_trigger_times):
                subtable = abs_table[abs_table[:,0] >= t]
                try:
                    next_trigger_time = quantised_trigger_times[i+1]
                except IndexError:
                    # No next trigger time
                    pass
                else:
                    subtable = subtable[subtable[:,0] < next_trigger_time]
                subtable[:,0] -= t
                abs_tables.append(subtable)

            # convert to diff tables:
            diff_tables = [make_diff_table(tab) for tab in abs_tables]
            # Create temporary files, get their paths, and close them:
            with tempfile.NamedTemporaryFile(delete=False) as f:
                temp_assembly_filepath = f.name
            with tempfile.NamedTemporaryFile(delete=False) as f:
                temp_binary_filepath = f.name
                
            try:
                # Compile to assembly:
                with open(temp_assembly_filepath,'w') as assembly_file:
                    for i, dtab in enumerate(diff_tables):
                        compileD(dtab, assembly_file, init=(i == 0),
                                 jump_to_start=(i == 0),
                                 jump_from_end=False,
                                 close_end=(i == len(diff_tables) - 1),
                                 local_loop_pre = str(i),
                                 set_defaults = (i==0))
                # Save the assembly to the h5 file:
                with open(temp_assembly_filepath,) as assembly_file:
                    assembly_code = assembly_file.read()
                    assembly_group.create_dataset('DDS%d'%dds, data=assembly_code)
                    for i, diff_table in enumerate(diff_tables):
                        diff_group.create_dataset('DDS%d_difftable%d'%(dds,i), compression=config.compression, data=diff_table)
                # compile to binary:
                compilation = Popen([caspr,temp_assembly_filepath,temp_binary_filepath],
                                     stdout=PIPE, stderr=PIPE, cwd=rfjuice_folder,startupinfo=startupinfo)
                stdout, stderr = compilation.communicate()
                if compilation.returncode:
                    print(stdout)
                    raise LabscriptError('RFBlaster compilation exited with code %d\n\n'%compilation.returncode +
                                         'Stdout was:\n %s\n'%stdout + 'Stderr was:\n%s\n'%stderr)
                # Save the binary to the h5 file:
                with open(temp_binary_filepath,'rb') as binary_file:
                    binary_data = binary_file.read()
                # has to be numpy.string_ (string_ in this namespace,
                # imported from pylab) as python strings get stored
                # as h5py as 'variable length' strings, which 'cannot
                # contain embedded nulls'. Presumably our binary data
                # must contain nulls sometimes. So this crashes if we
                # don't convert to a numpy 'fixes length' string:
                binary_group.create_dataset('DDS%d'%dds, data=np.string_(binary_data))
            finally:
                # Delete the temporary files:
                os.remove(temp_assembly_filepath)
                os.remove(temp_binary_filepath)
示例#21
0
    def generate_code(self, hdf5_file):
        """Generates the hardware instructions for the pseudoclocks.

        This is automatically called by the labscript compiler.

        Args:
            hdf5_file (:class:`h5py.File`): h5py file object for shot 
        """

        PseudoclockDevice.generate_code(self, hdf5_file)
        group = self.init_device_group(hdf5_file)

        current_wait_index = 0
        wait_table = sorted(compiler.wait_table)

        # For each pseudoclock
        for i, pseudoclock in enumerate(self.pseudoclocks):
            current_wait_index = 0

            # Compress clock instructions with the same half_period
            reduced_instructions = []
            for instruction in pseudoclock.clock:
                if instruction == "WAIT":
                    # If we're using the internal wait monitor, set the timeout
                    if self.use_wait_monitor:
                        # Get the wait timeout value
                        wait_timeout = compiler.wait_table[
                            wait_table[current_wait_index]][1]
                        current_wait_index += 1
                        # The following half_period and reps indicates a wait instruction
                        reduced_instructions.append({
                            "half_period":
                            round(wait_timeout / (self.clock_resolution / 2)),
                            "reps":
                            0,
                        })
                        continue
                    # Else, set an indefinite wait and wait for a trigger from something else.
                    else:
                        # Two waits in a row are an indefinite wait
                        reduced_instructions.append({
                            "half_period": 2**32 - 1,
                            "reps": 0,
                        })
                        reduced_instructions.append({
                            "half_period": 2**32 - 1,
                            "reps": 0,
                        })

                # Normal instruction
                reps = instruction["reps"]
                # half_period is in quantised units:
                half_period = int(
                    round(instruction["step"] / self.clock_resolution))
                if (
                        # If there is a previous instruction
                        reduced_instructions
                        # And it's not a wait
                        and reduced_instructions[-1]["reps"] != 0
                        # And the half_periods match
                        and
                        reduced_instructions[-1]["half_period"] == half_period
                        # And the sum of the previous reps and current reps won't push it over the limit
                        and (reduced_instructions[-1]["reps"] + reps) <
                    (2**32 - 1)):
                    # Combine instructions!
                    reduced_instructions[-1]["reps"] += reps
                else:
                    # New instruction
                    reduced_instructions.append({
                        "half_period": half_period,
                        "reps": reps
                    })

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

            # Check we have not exceeded the maximum number of supported instructions
            # for this number of speudoclocks
            if len(reduced_instructions) > self.max_instructions:
                raise LabscriptError(
                    f"{self.description} {self.name}.clocklines[{i}] has too many instructions. It has {len(reduced_instructions)} and can only support {self.max_instructions}"
                )

            # Store these instructions to the h5 file:
            dtypes = [("half_period", int), ("reps", int)]
            pulse_program = np.zeros(len(reduced_instructions), dtype=dtypes)
            for j, instruction in enumerate(reduced_instructions):
                pulse_program[j]["half_period"] = instruction["half_period"]
                pulse_program[j]["reps"] = instruction["reps"]
            group.create_dataset(f"PULSE_PROGRAM_{i}",
                                 compression=config.compression,
                                 data=pulse_program)

        # This is needed so the BLACS worker knows whether or not to be a wait monitor
        self.set_property(
            "is_master_pseudoclock",
            self.is_master_pseudoclock,
            location="device_properties",
        )
        self.set_property("stop_time",
                          self.stop_time,
                          location="device_properties")
示例#22
0
    def __init__(
        self,
        name,
        trigger_device=None,
        trigger_connection=None,
        com_port="COM1",
        num_pseudoclocks=1,
        out_pins=None,
        in_pins=None,
        clock_frequency=100e6,
        external_clock_pin=None,
        use_wait_monitor=True,
    ):
        """PrawnBlaster Pseudoclock labscript device.

        This labscript device creates Pseudoclocks based on the PrawnBlaster,
        a Raspberry Pi Pico with custom firmware.

        Args:
            name (str): python variable name to assign to the PrawnBlaster
            com_port (str): COM port assigned to the PrawnBlaster by the OS. Takes 
                the form of `'COMd'`, where `d` is an integer.
            num_pseudoclocks (int): Number of pseudoclocks to create. Ranges from 1-4.
            trigger_device (:class:`~labscript.IntermediateDevice`, optional): Device
                that will send the hardware start trigger when using the PrawnBlaster
                as a secondary Pseudoclock.
            trigger_connection (str, optional): Which output of the `trigger_device`
                is connected to the PrawnBlaster hardware trigger input.
            out_pins (list, optional): What outpins to use for the pseudoclock outputs.
                Must have length of at least `num_pseudoclocks`. Defaults to `[9,11,13,15]`
            in_pins (list, optional): What inpins to use for the pseudoclock hardware
                triggering. Must have length of at least `num_pseudoclocks`. 
                Defaults to `[0,0,0,0]`
            clock_frequency (float, optional): Frequency of clock. Standard range
                accepts up to 133 MHz. An experimental overclocked firmware is 
                available that allows higher frequencies.
            external_clock_pin (int, optional): If not `None` (the default), 
                the PrawnBlaster uses an external clock on the provided pin. Valid
                options are `20` and `22`. The external frequency must be defined
                using `clock_frequency`.
            use_wait_monitor (bool, optional): Configure the PrawnBlaster to
                perform its own wait monitoring.

        """

        # Check number of pseudoclocks is within range
        if num_pseudoclocks < 1 or num_pseudoclocks > 4:
            raise LabscriptError(
                f"The PrawnBlaster {name} only supports between 1 and 4 pseudoclocks"
            )

        # Update the specs based on the number of pseudoclocks
        self.max_instructions = self.max_instructions // num_pseudoclocks
        # Update the specs based on the clock frequency
        if self.clock_resolution != 2 / clock_frequency:
            factor = (2 / clock_frequency) / self.clock_resolution
            self.clock_limit *= factor
            self.clock_resolution *= factor
            self.input_response_time *= factor
            self.trigger_delay *= factor
            self.trigger_minimum_duration *= factor
            self.wait_delay *= factor

        # Instantiate the base class
        PseudoclockDevice.__init__(self, name, trigger_device,
                                   trigger_connection)
        self.num_pseudoclocks = num_pseudoclocks
        # Wait monitor can only be used if this is the master pseudoclock
        self.use_wait_monitor = use_wait_monitor and self.is_master_pseudoclock

        # Set the BLACS connections
        self.BLACS_connection = com_port

        # Check in/out pins
        if out_pins is None:
            out_pins = [9, 11, 13, 15]
        if in_pins is None:
            in_pins = [0, 0, 0, 0]
        if len(out_pins) < num_pseudoclocks:
            raise LabscriptError(
                f"The PrawnBlaster {self.name} is configured with {num_pseudoclocks} but only has pin numbers specified for {len(out_pins)}."
            )
        else:
            self.out_pins = out_pins[:num_pseudoclocks]
        if len(in_pins) < num_pseudoclocks:
            raise LabscriptError(
                f"The PrawnBlaster {self.name} is configured with {num_pseudoclocks} but only has pin numbers specified for {len(in_pins)}."
            )
        else:
            self.in_pins = in_pins[:num_pseudoclocks]

        self._pseudoclocks = []
        self._clocklines = []
        for i in range(num_pseudoclocks):
            self._pseudoclocks.append(
                _PrawnBlasterPseudoclock(
                    i,
                    name=f"{name}_pseudoclock_{i}",
                    pseudoclock_device=self,
                    connection=f"pseudoclock {i}",
                ))
            self._clocklines.append(
                ClockLine(
                    name=f"{name}_clock_line_{i}",
                    pseudoclock=self._pseudoclocks[i],
                    connection=f"GPIO {self.out_pins[i]}",
                ))

        if self.use_wait_monitor:
            # Create internal devices for connecting to a wait monitor
            self.__wait_monitor_dummy_pseudoclock = _PrawnBlasterDummyPseudoclock(
                "%s__dummy_wait_pseudoclock" % name, self, "_")
            self.__wait_monitor_dummy_clock_line = _PrawnBlasterDummyClockLine(
                "%s__dummy_wait_clock_line" % name,
                self.__wait_monitor_dummy_pseudoclock,
                "_",
            )
            self.__wait_monitor_intermediate_device = (
                _PrawnBlasterDummyIntermediateDevice(
                    "%s_internal_wait_monitor_outputs" % name,
                    self.__wait_monitor_dummy_clock_line,
                ))

            # Create the wait monitor
            WaitMonitor(
                "%s__wait_monitor" % name,
                self.internal_wait_monitor_outputs,
                "internal",
                self.internal_wait_monitor_outputs,
                "internal",
                self.internal_wait_monitor_outputs,
                "internal",
            )
 def __init__(self,
              name='dummy_pseudoclock',
              BLACS_connection='dummy_connection',
              **kwargs):
     self.BLACS_connection = BLACS_connection
     PseudoclockDevice.__init__(self, name, None, None, **kwargs)