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 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 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 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)
Ejemplo n.º 5
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')
Ejemplo n.º 6
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)
Ejemplo n.º 7
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")
 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')
Ejemplo n.º 9
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)