class Fei4RunBase(RunBase): '''Basic FEI4 run meta class. Base class for scan- / tune- / analyze-class. ''' __metaclass__ = abc.ABCMeta def __init__(self, conf, run_conf=None): super(Fei4RunBase, self).__init__(conf=conf, run_conf=run_conf) self.err_queue = Queue() self.fifo_readout = None self.register_utils = None self.raw_data_file = None try: client_addr = self._run_conf['send_data'] self.socket = zmq.Context().socket(zmq.PUSH) self.socket.bind(client_addr) logging.info('Send data to %s' % client_addr) except KeyError: self.socket = None @property def working_dir(self): if self.module_id: return os.path.join(self.conf['working_dir'], self.module_id) else: return os.path.join(self.conf['working_dir'], self.run_id) @property def dut(self): return self.conf['dut'] @property def register(self): return self.conf['fe_configuration'] @property def output_filename(self): if self.module_id: return os.path.join( self.working_dir, str(self.run_number) + "_" + self.module_id + "_" + self.run_id) else: return os.path.join(self.working_dir, str(self.run_number) + "_" + self.run_id) @property def module_id(self): if 'module_id' in self.conf and self.conf['module_id']: module_id = self.conf['module_id'] module_id = re.sub(r"[^\w\s+]", '', module_id) return re.sub(r"\s+", '_', module_id).lower() else: return None def _run(self): if 'scan_parameters' in self.run_conf: sp = namedtuple( 'scan_parameters', field_names=zip(*self.run_conf['scan_parameters'])[0]) self.scan_parameters = sp(*zip( *self.run_conf['scan_parameters'])[1]) else: sp = namedtuple_with_defaults('scan_parameters', field_names=[]) self.scan_parameters = sp() logging.info('Scan parameter(s): %s' % (', '.join([ '%s=%s' % (key, value) for (key, value) in self.scan_parameters._asdict().items() ]) if self.scan_parameters else 'None')) try: last_configuration = self._get_configuration() if 'fe_configuration' in self.conf and self.conf[ 'fe_configuration']: if not isinstance(self.conf['fe_configuration'], FEI4Register): if isinstance(self.conf['fe_configuration'], basestring): if os.path.isabs(self.conf['fe_configuration']): fe_configuration = self.conf['fe_configuration'] else: fe_configuration = os.path.join( self.conf['working_dir'], self.conf['fe_configuration']) self._conf['fe_configuration'] = FEI4Register( configuration_file=fe_configuration) elif isinstance( self.conf['fe_configuration'], (int, long)) and self.conf['fe_configuration'] >= 0: self._conf['fe_configuration'] = FEI4Register( configuration_file=self._get_configuration( self.conf['fe_configuration'])) else: self._conf['fe_configuration'] = FEI4Register( configuration_file=self._get_configuration()) else: pass # do nothing, already initialized elif last_configuration: self._conf['fe_configuration'] = FEI4Register( configuration_file=last_configuration) else: if 'chip_address' in self.conf and isinstance( self.conf['chip_address'], (int, long)): chip_address = self.conf['chip_address'] broadcast = False else: chip_address = 0 broadcast = True if 'fe_flavor' in self.conf and self.conf['fe_flavor']: self._conf['fe_configuration'] = FEI4Register( fe_type=self.conf['fe_flavor'], chip_address=chip_address, broadcast=broadcast) else: raise ValueError('No valid configuration found') if not isinstance(self.conf['dut'], Dut): if isinstance(self.conf['dut'], basestring): if os.path.isabs(self.conf['dut']): dut = self.conf['dut'] else: dut = os.path.join(self.conf['working_dir'], self.conf['dut']) self._conf['dut'] = Dut(dut) else: self._conf['dut'] = Dut(self.conf['dut']) module_path = os.path.dirname( os.path.abspath(inspect.getfile(inspect.currentframe()))) if 'dut_configuration' in self.conf and self.conf[ 'dut_configuration']: if isinstance(self.conf['dut_configuration'], basestring): if os.path.isabs(self.conf['dut_configuration']): dut_configuration = self.conf['dut_configuration'] else: dut_configuration = os.path.join( self.conf['working_dir'], self.conf['dut_configuration']) self.dut.init(dut_configuration) else: self.dut.init(self.conf['dut_configuration']) elif self.dut.name == 'usbpix': self.dut.init( os.path.join(module_path, 'dut_configuration_usbpix.yaml')) elif self.dut.name == 'usbpix_gpac': self.dut.init( os.path.join(module_path, 'dut_configuration_usbpix_gpac.yaml')) else: logging.warning('Omit initialization of DUT') if self.dut.name == 'usbpix': self.dut['POWER'].set_voltage('VDDA1', 1.500) self.dut['POWER'].set_voltage('VDDA2', 1.500) self.dut['POWER'].set_voltage('VDDD1', 1.200) self.dut['POWER'].set_voltage('VDDD2', 1.200) self.dut['POWER_SCC']['EN_VD1'] = 1 self.dut['POWER_SCC']['EN_VD2'] = 1 self.dut['POWER_SCC']['EN_VA1'] = 1 self.dut['POWER_SCC']['EN_VA2'] = 1 self.dut['POWER_SCC'].write() # enabling readout self.dut['rx']['CH1'] = 1 self.dut['rx']['CH2'] = 1 self.dut['rx']['CH3'] = 1 self.dut['rx']['CH4'] = 1 self.dut['rx']['TLU'] = 1 self.dut['rx']['TDC'] = 1 self.dut['rx'].write() elif self.dut.name == 'usbpix_gpac': self.dut['V_in'].set_current_limit( 1000, unit='mA') # one for all # enabling LVDS transceivers self.dut['CCPD_Vdd'].set_enable(False) self.dut['CCPD_Vdd'].set_voltage(0.0, unit='V') self.dut['CCPD_Vdd'].set_enable(True) # enabling V_in self.dut['V_in'].set_enable(False) self.dut['V_in'].set_voltage(2.1, unit='V') self.dut['V_in'].set_enable(True) # enabling readout self.dut['rx']['FE'] = 1 self.dut['rx']['TLU'] = 1 self.dut['rx']['TDC'] = 1 self.dut['rx']['CCPD_TDC'] = 0 self.dut['rx'].write() else: logging.warning( 'Unknown DUT name: %s. DUT may not be set up properly' % self.dut.name) else: pass # do nothing, already initialized if not self.fifo_readout: self.fifo_readout = FifoReadout(self.dut) if not self.register_utils: self.register_utils = FEI4RegisterUtils( self.dut, self.register) with open_raw_data_file(filename=self.output_filename, mode='w', title=self.run_id, scan_parameters=self.scan_parameters. _asdict()) as self.raw_data_file: self.save_configuration_dict(self.raw_data_file.h5_file, 'conf', self.conf) self.save_configuration_dict(self.raw_data_file.h5_file, 'run_conf', self.run_conf) self.register_utils.global_reset() self.register_utils.configure_all() if is_fe_ready(self): reset_service_records = False else: reset_service_records = True self.register_utils.reset_bunch_counter() self.register_utils.reset_event_counter() if reset_service_records: # resetting service records must be done once after power up self.register_utils.reset_service_records() with self.register.restored(name=self.run_number): self.configure() self.register.save_configuration( self.raw_data_file.h5_file) self.fifo_readout.reset_rx() self.fifo_readout.reset_sram_fifo() self.fifo_readout.print_readout_status() self.scan() except Exception: self.handle_err(sys.exc_info()) else: try: if self.abort_run.is_set(): raise RunAborted('Omitting data analysis: run was aborted') self.analyze() except AnalysisError as e: logging.error('Analysis of data failed: %s' % e) except Exception: self.handle_err(sys.exc_info()) else: self.register.save_configuration(self.output_filename) finally: self.raw_data_file = None try: self.fifo_readout.print_readout_status() except Exception: pass try: self.dut['USB'].close() # free USB resources except Exception: logging.error('Cannot close USB device') if not self.err_queue.empty(): exc = self.err_queue.get() if isinstance(exc[1], (RxSyncError, EightbTenbError, FifoError, NoDataTimeout, StopTimeout)): raise RunAborted(exc[1]) else: raise exc[0], exc[1], exc[2] def handle_data(self, data): self.raw_data_file.append_item( data, scan_parameters=self.scan_parameters._asdict(), flush=False) send_data(self.socket, data, self.scan_parameters._asdict()) def handle_err(self, exc): self.err_queue.put(exc) self.abort(msg='%s' % exc[1]) def _get_configuration(self, run_number=None): def find_file(run_number): for root, dirs, files in os.walk(self.working_dir): for cfgfile in files: cfg_root, cfg_ext = os.path.splitext(cfgfile) if cfg_root.startswith(''.join([ str(run_number), '_', self.module_id ])) and cfg_ext.endswith(".cfg"): return os.path.join(root, cfgfile) if not run_number: run_numbers = sorted( self._get_run_numbers(status='FINISHED').iterkeys(), reverse=True) for run_number in run_numbers: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: raise ValueError('Found no configuration with run number %s' % run_number) def set_scan_parameters(self, *args, **kwargs): fields = dict(kwargs) for index, field in enumerate(self.scan_parameters._fields): try: value = args[index] except IndexError: break else: if field in fields: raise TypeError( 'Got multiple values for keyword argument %s' % field) fields[field] = value scan_parameters_old = self.scan_parameters._asdict() self.scan_parameters = self.scan_parameters._replace(**fields) scan_parameters_new = self.scan_parameters._asdict() diff = [ name for name in scan_parameters_old.keys() if np.any(scan_parameters_old[name] != scan_parameters_new[name]) ] if diff: logging.info('Changing scan parameter(s): %s' % (', '.join([('%s=%s' % (name, fields[name])) for name in diff]))) @contextmanager def readout(self, *args, **kwargs): self.start_readout(*args, **kwargs) try: yield self.stop_readout() finally: # in case something fails, call this on last resort if self.fifo_readout.is_running: self.fifo_readout.stop(timeout=0.0) def start_readout(self, *args, **kwargs): if args or kwargs: self.set_scan_parameters(*args, **kwargs) self.fifo_readout.start(reset_sram_fifo=False, clear_buffer=True, callback=self.handle_data, errback=self.handle_err) def stop_readout(self): self.fifo_readout.stop() def save_configuration_dict(self, h5_file, configuation_name, configuration, **kwargs): '''Stores any configuration dictionary to HDF5 file. Parameters ---------- h5_file : string, file Filename of the HDF5 configuration file or file object. configuation_name : str Configuration name. Will be used for table name. configuration : dict Configuration dictionary. ''' def save_conf(): try: h5_file.removeNode(h5_file.root.configuration, name=configuation_name) except tb.NodeError: pass try: configuration_group = h5_file.create_group( h5_file.root, "configuration") except tb.NodeError: configuration_group = h5_file.root.configuration self.scan_param_table = h5_file.createTable( configuration_group, name=configuation_name, description=NameValue, title=configuation_name) row_scan_param = self.scan_param_table.row for key, value in dict.iteritems(configuration): row_scan_param['name'] = key row_scan_param['value'] = str(value) row_scan_param.append() self.scan_param_table.flush() if isinstance(h5_file, tb.file.File): save_conf() else: if os.path.splitext(h5_file)[1].strip().lower() != ".h5": h5_file = os.path.splitext(h5_file)[0] + ".h5" with tb.open_file(h5_file, mode="a", title='', **kwargs) as h5_file: save_conf() @abc.abstractmethod def configure(self): '''Implementation of the run configuration. Will be executed before starting the scan routine. ''' pass @abc.abstractmethod def scan(self): '''Implementation of the scan routine. Do you want to write your own scan? Here is the place to begin. ''' pass @abc.abstractmethod def analyze(self): '''Implementation of run data processing. Will be executed after finishing the scan routine. ''' pass
class Fei4RunBase(RunBase): '''Basic FEI4 run meta class. Base class for scan- / tune- / analyze-class. ''' __metaclass__ = abc.ABCMeta def __init__(self, conf, run_conf=None): # adding default run conf parameters valid for all scans if 'send_data' not in self._default_run_conf: self._default_run_conf.update({'send_data': None}) if 'comment' not in self._default_run_conf: self._default_run_conf.update({'comment': ''}) if 'reset_rx_on_error' not in self._default_run_conf: self._default_run_conf.update({'reset_rx_on_error': False}) super(Fei4RunBase, self).__init__(conf=conf, run_conf=run_conf) self.err_queue = Queue() self.fifo_readout = None self.raw_data_file = None @property def working_dir(self): if self.module_id: return os.path.join(self.conf['working_dir'], self.module_id) else: return os.path.join(self.conf['working_dir'], self.run_id) @property def dut(self): return self.conf['dut'] @property def register(self): return self.conf['fe_configuration'] @property def output_filename(self): if self.module_id: return os.path.join(self.working_dir, str(self.run_number) + "_" + self.module_id + "_" + self.run_id) else: return os.path.join(self.working_dir, str(self.run_number) + "_" + self.run_id) @property def module_id(self): if 'module_id' in self.conf and self.conf['module_id']: module_id = self.conf['module_id'] module_id = re.sub(r"[^\w\s+]", '', module_id) return re.sub(r"\s+", '_', module_id).lower() else: return None def init_dut(self): if self.dut.name == 'mio': if self.dut.get_modules('FEI4AdapterCard') and [adapter_card for adapter_card in self.dut.get_modules('FEI4AdapterCard') if adapter_card.name == 'ADAPTER_CARD']: self.dut['ADAPTER_CARD'].set_voltage('VDDA1', 1.5) self.dut['ADAPTER_CARD'].set_voltage('VDDA2', 1.5) self.dut['ADAPTER_CARD'].set_voltage('VDDD1', 1.2) self.dut['ADAPTER_CARD'].set_voltage('VDDD2', 1.2) self.dut['POWER_SCC']['EN_VD1'] = 1 self.dut['POWER_SCC']['EN_VD2'] = 1 self.dut['POWER_SCC']['EN_VA1'] = 1 self.dut['POWER_SCC']['EN_VA2'] = 1 self.dut['POWER_SCC'].write() # enabling readout self.dut['ENABLE_CHANNEL']['CH1'] = 0 self.dut['ENABLE_CHANNEL']['CH2'] = 0 self.dut['ENABLE_CHANNEL']['CH3'] = 0 self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.get_modules('FEI4QuadModuleAdapterCard') and [adapter_card for adapter_card in self.dut.get_modules('FEI4QuadModuleAdapterCard') if adapter_card.name == 'ADAPTER_CARD']: # resetting over current status self.dut['POWER_QUAD']['EN_CH1'] = 0 self.dut['POWER_QUAD']['EN_CH2'] = 0 self.dut['POWER_QUAD']['EN_CH3'] = 0 self.dut['POWER_QUAD']['EN_CH4'] = 0 self.dut['POWER_QUAD'].write() self.dut['ADAPTER_CARD'].set_voltage('CH1', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH2', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH3', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH4', 2.1) self.dut['POWER_QUAD'].write() channel_names = [channel.name for channel in self.dut.get_modules('fei4_rx')] for channel in channel_names: # enabling readout self.dut['ENABLE_CHANNEL'][channel] = 1 self.dut['POWER_QUAD']['EN_' + channel] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() self.dut['POWER_QUAD'].write() else: logging.warning('Unknown adapter card') # do the minimal configuration here self.dut['ENABLE_CHANNEL']['CH1'] = 0 self.dut['ENABLE_CHANNEL']['CH2'] = 0 self.dut['ENABLE_CHANNEL']['CH3'] = 0 self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'mio_gpac': # PWR self.dut['V_in'].set_current_limit(0.1, unit='A') # one for all, max. 1A # V_in self.dut['V_in'].set_voltage(2.1, unit='V') self.dut['V_in'].set_enable(True) if self.dut["V_in"].get_over_current(): self.power_off() raise Exception('V_in overcurrent detected') # Vdd, also enabling LVDS transceivers self.dut['CCPD_Vdd'].set_voltage(1.80, unit='V') self.dut['CCPD_Vdd'].set_enable(True) if self.dut["CCPD_Vdd"].get_over_current(): self.power_off() raise Exception('Vdd overcurrent detected') # Vssa self.dut['CCPD_Vssa'].set_voltage(1.50, unit='V') self.dut['CCPD_Vssa'].set_enable(True) if self.dut["CCPD_Vssa"].get_over_current(): self.power_off() raise Exception('Vssa overcurrent detected') # VGate self.dut['CCPD_VGate'].set_voltage(2.10, unit='V') self.dut['CCPD_VGate'].set_enable(True) if self.dut["CCPD_VGate"].get_over_current(): self.power_off() raise Exception('VGate overcurrent detected') # enabling readout self.dut['ENABLE_CHANNEL']['FE'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL']['CCPD_TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'seabas2': self.dut['ENABLE_CHANNEL']['CH0'] = 1 self.dut['ENABLE_CHANNEL']['CH1'] = 1 self.dut['ENABLE_CHANNEL']['CH2'] = 1 self.dut['ENABLE_CHANNEL']['CH3'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'nexys4': #enable LVDS RX/TX self.dut['I2C'].write(0xe8, [6,0xf0, 0xff]) self.dut['I2C'].write(0xe8, [2,0x0f, 0x00]) self.dut['ENABLE_CHANNEL']['CH1'] = 0 self.dut['ENABLE_CHANNEL']['CH2'] = 0 self.dut['ENABLE_CHANNEL']['CH3'] = 0 self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() else: logging.warning('Omit initialization of DUT %s', self.dut.name) def init_fe(self): if 'fe_configuration' in self.conf: last_configuration = self._get_configuration() # init config, a number <=0 will also do the initialization (run 0 does not exists) if (not self.conf['fe_configuration'] and not last_configuration) or (isinstance(self.conf['fe_configuration'], (int, long)) and self.conf['fe_configuration'] <= 0): if 'chip_address' in self.conf and self.conf['chip_address']: chip_address = self.conf['chip_address'] broadcast = False else: chip_address = 0 broadcast = True if 'fe_flavor' in self.conf and self.conf['fe_flavor']: self._conf['fe_configuration'] = FEI4Register(fe_type=self.conf['fe_flavor'], chip_address=chip_address, broadcast=broadcast) else: raise ValueError('No fe_flavor given') # use existing config elif not self.conf['fe_configuration'] and last_configuration: self._conf['fe_configuration'] = FEI4Register(configuration_file=last_configuration) # path elif isinstance(self.conf['fe_configuration'], basestring): if os.path.isabs(self.conf['fe_configuration']): fe_configuration = self.conf['fe_configuration'] else: fe_configuration = os.path.join(self.conf['working_dir'], self.conf['fe_configuration']) self._conf['fe_configuration'] = FEI4Register(configuration_file=fe_configuration) # run number elif isinstance(self.conf['fe_configuration'], (int, long)) and self.conf['fe_configuration'] > 0: self._conf['fe_configuration'] = FEI4Register(configuration_file=self._get_configuration(self.conf['fe_configuration'])) # assume fe_configuration already initialized elif not isinstance(self.conf['fe_configuration'], FEI4Register): raise ValueError('No valid fe_configuration given') # init register utils self.register_utils = FEI4RegisterUtils(self.dut, self.register) # reset and configuration self.register_utils.global_reset() self.register_utils.configure_all() if is_fe_ready(self): reset_service_records = False else: reset_service_records = True self.register_utils.reset_bunch_counter() self.register_utils.reset_event_counter() if reset_service_records: # resetting service records must be done once after power up self.register_utils.reset_service_records() else: pass # no fe_configuration def pre_run(self): # sending data self.socket_addr = self._run_conf['send_data'] if self.socket_addr: logging.info('Sending data to %s', self.socket_addr) # scan parameters if 'scan_parameters' in self.run_conf: if isinstance(self.run_conf['scan_parameters'], basestring): self.run_conf['scan_parameters'] = ast.literal_eval(self.run_conf['scan_parameters']) sp = namedtuple('scan_parameters', field_names=zip(*self.run_conf['scan_parameters'])[0]) self.scan_parameters = sp(*zip(*self.run_conf['scan_parameters'])[1]) else: sp = namedtuple_with_defaults('scan_parameters', field_names=[]) self.scan_parameters = sp() logging.info('Scan parameter(s): %s', ', '.join(['%s=%s' % (key, value) for (key, value) in self.scan_parameters._asdict().items()]) if self.scan_parameters else 'None') # init DUT if not isinstance(self.conf['dut'], Dut): module_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) if isinstance(self.conf['dut'], basestring): # dirty fix for Windows pathes self.conf['dut'] = os.path.normpath(self.conf['dut'].replace('\\', '/')) # abs path if os.path.isabs(self.conf['dut']): dut = self.conf['dut'] # working dir elif os.path.exists(os.path.join(self.conf['working_dir'], self.conf['dut'])): dut = os.path.join(self.conf['working_dir'], self.conf['dut']) # path of this file elif os.path.exists(os.path.join(module_path, self.conf['dut'])): dut = os.path.join(module_path, self.conf['dut']) else: raise ValueError('%s: dut file not found' % self.conf['dut']) self._conf['dut'] = Dut(dut) else: self._conf['dut'] = Dut(self.conf['dut']) # only initialize when DUT was not initialized before if 'dut_configuration' in self.conf and self.conf['dut_configuration']: if isinstance(self.conf['dut_configuration'], basestring): # dirty fix for Windows pathes self.conf['dut_configuration'] = os.path.normpath(self.conf['dut_configuration'].replace('\\', '/')) # abs path if os.path.isabs(self.conf['dut_configuration']): dut_configuration = self.conf['dut_configuration'] # working dir elif os.path.exists(os.path.join(self.conf['working_dir'], self.conf['dut_configuration'])): dut_configuration = os.path.join(self.conf['working_dir'], self.conf['dut_configuration']) # path of dut file elif os.path.exists(os.path.join(os.path.dirname(self.dut.conf_path), self.conf['dut_configuration'])): dut_configuration = os.path.join(os.path.dirname(self.dut.conf_path), self.conf['dut_configuration']) # path of this file elif os.path.exists(os.path.join(module_path, self.conf['dut_configuration'])): dut_configuration = os.path.join(module_path, self.conf['dut_configuration']) else: raise ValueError('%s: dut_configuration file not found' % self.conf['dut_configuration']) self.dut.init(dut_configuration) else: self.dut.init(self.conf['dut_configuration']) else: self.dut.init() # additional init of the DUT self.init_dut() else: pass # do nothing, already initialized # FIFO readout self.fifo_readout = FifoReadout(self.dut) # initialize the FE self.init_fe() def do_run(self): with open_raw_data_file(filename=self.output_filename, mode='w', title=self.run_id, register=self.register, conf=self.conf, run_conf=self.run_conf, scan_parameters=self.scan_parameters._asdict(), socket_addr=self.socket_addr) as self.raw_data_file: with self.register.restored(name=self.run_number): # configure for scan self.configure() self.raw_data_file.save_register_configuration() # save register configration after configure() since it is most likely changed there self.fifo_readout.reset_rx() self.fifo_readout.reset_sram_fifo() self.fifo_readout.print_readout_status() # scan self.scan() def post_run(self): try: self.fifo_readout.print_readout_status() # no device? except Exception: pass if not self.err_queue.empty(): exc = self.err_queue.get() # well known errors if isinstance(exc[1], (RxSyncError, EightbTenbError, FifoError, NoDataTimeout, StopTimeout)): raise RunAborted(exc[1]) # some other error via handle_err(), print to crash.log else: raise exc[0], exc[1], exc[2] elif self.abort_run.is_set(): raise RunAborted('Read the log') # analyzing data try: self.analyze() # known errors except AnalysisError as e: logging.error('Analysis of data failed: %s', e) raise RunAborted('Read the log') # analyzed data, save config else: self.register.save_configuration(self.output_filename) # other reasons if self.stop_run.is_set(): raise RunStopped('Read the log') def cleanup_run(self): # no execption should be thrown here self.raw_data_file = None try: self.dut.close() # free resources except Exception: logging.error('Cannot close DUT') def handle_data(self, data): self.raw_data_file.append_item(data, scan_parameters=self.scan_parameters._asdict(), flush=False) def handle_err(self, exc): if self.reset_rx_on_error and isinstance(exc[1], (RxSyncError, EightbTenbError)): self.fifo_readout.print_readout_status() self.fifo_readout.reset_rx() else: self.err_queue.put(exc) self.abort(msg='%s' % exc[1]) def _get_configuration(self, run_number=None): def find_file(run_number): for root, _, files in os.walk(self.working_dir): for cfgfile in files: cfg_root, cfg_ext = os.path.splitext(cfgfile) if cfg_root.startswith(''.join([str(run_number), '_', self.module_id])) and cfg_ext.endswith(".cfg"): return os.path.join(root, cfgfile) if not run_number: run_numbers = sorted(self._get_run_numbers(status='FINISHED').iterkeys(), reverse=True) for run_number in run_numbers: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: raise ValueError('Found no configuration with run number %s' % run_number) def set_scan_parameters(self, *args, **kwargs): fields = dict(kwargs) for index, field in enumerate(self.scan_parameters._fields): try: value = args[index] except IndexError: break else: if field in fields: raise TypeError('Got multiple values for keyword argument %s' % field) fields[field] = value scan_parameters_old = self.scan_parameters._asdict() self.scan_parameters = self.scan_parameters._replace(**fields) scan_parameters_new = self.scan_parameters._asdict() diff = [name for name in scan_parameters_old.keys() if np.any(scan_parameters_old[name] != scan_parameters_new[name])] if diff: logging.info('Changing scan parameter(s): %s', ', '.join([('%s=%s' % (name, fields[name])) for name in diff])) @contextmanager def readout(self, *args, **kwargs): timeout = kwargs.pop('timeout', 10.0) self.start_readout(*args, **kwargs) try: yield self.stop_readout(timeout=timeout) finally: # in case something fails, call this on last resort if self.fifo_readout.is_running: self.fifo_readout.stop(timeout=0.0) def start_readout(self, *args, **kwargs): # Pop parameters for fifo_readout.start callback = kwargs.pop('callback', self.handle_data) clear_buffer = kwargs.pop('clear_buffer', False) fill_buffer = kwargs.pop('fill_buffer', False) reset_sram_fifo = kwargs.pop('reset_sram_fifo', False) errback = kwargs.pop('errback', self.handle_err) no_data_timeout = kwargs.pop('no_data_timeout', None) if args or kwargs: self.set_scan_parameters(*args, **kwargs) self.fifo_readout.start(reset_sram_fifo=reset_sram_fifo, fill_buffer=fill_buffer, clear_buffer=clear_buffer, callback=callback, errback=errback, no_data_timeout=no_data_timeout) def stop_readout(self, timeout=10.0): self.fifo_readout.stop(timeout=timeout) @abc.abstractmethod def configure(self): '''Implementation of the run configuration. Will be executed before starting the scan routine. ''' pass @abc.abstractmethod def scan(self): '''Implementation of the scan routine. Do you want to write your own scan? Here is the place to begin. ''' pass @abc.abstractmethod def analyze(self): '''Implementation of run data processing. Will be executed after finishing the scan routine. ''' pass
class Fei4RunBase(RunBase): '''Basic FEI4 run meta class. Base class for scan- / tune- / analyze-class. ''' __metaclass__ = abc.ABCMeta def __init__(self, conf, run_conf=None): # adding default run conf parameters valid for all scans if 'send_data' not in self._default_run_conf: self._default_run_conf.update({'send_data': None}) if 'comment' not in self._default_run_conf: self._default_run_conf.update({'comment': None}) if 'reset_rx_on_error' not in self._default_run_conf: self._default_run_conf.update({'reset_rx_on_error': None}) super(Fei4RunBase, self).__init__(conf=conf, run_conf=run_conf) self.err_queue = Queue() self.fifo_readout = None self.raw_data_file = None @property def working_dir(self): if self.module_id: return os.path.join(self.conf['working_dir'], self.module_id) else: return os.path.join(self.conf['working_dir'], self.run_id) @property def dut(self): return self.conf['dut'] @property def register(self): return self.conf['fe_configuration'] @property def output_filename(self): if self.module_id: return os.path.join( self.working_dir, str(self.run_number) + "_" + self.module_id + "_" + self.run_id) else: return os.path.join(self.working_dir, str(self.run_number) + "_" + self.run_id) @property def module_id(self): if 'module_id' in self.conf and self.conf['module_id']: module_id = self.conf['module_id'] module_id = re.sub(r"[^\w\s+]", '', module_id) return re.sub(r"\s+", '_', module_id).lower() else: return None def init_dut(self): if self.dut.name == 'mio': if isinstance(self.dut['ADAPTER_CARD'], FEI4AdapterCard): self.dut['ADAPTER_CARD'].set_voltage('VDDA1', 1.5) self.dut['ADAPTER_CARD'].set_voltage('VDDA2', 1.5) self.dut['ADAPTER_CARD'].set_voltage('VDDD1', 1.2) self.dut['ADAPTER_CARD'].set_voltage('VDDD2', 1.2) self.dut['POWER_SCC']['EN_VD1'] = 1 self.dut['POWER_SCC']['EN_VD2'] = 1 self.dut['POWER_SCC']['EN_VA1'] = 1 self.dut['POWER_SCC']['EN_VA2'] = 1 self.dut['POWER_SCC'].write() # enabling readout self.dut['ENABLE_CHANNEL']['CH1'] = 0 self.dut['ENABLE_CHANNEL']['CH2'] = 0 self.dut['ENABLE_CHANNEL']['CH3'] = 0 self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL'].write() elif isinstance(self.dut['ADAPTER_CARD'], FEI4QuadModuleAdapterCard): # resetting over current status self.dut['POWER_QUAD']['EN_CH1'] = 0 self.dut['POWER_QUAD']['EN_CH2'] = 0 self.dut['POWER_QUAD']['EN_CH3'] = 0 self.dut['POWER_QUAD']['EN_CH4'] = 0 self.dut['POWER_QUAD'].write() self.dut['ADAPTER_CARD'].set_voltage('CH1', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH2', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH3', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH4', 2.1) self.dut['POWER_QUAD'].write() channel_names = [ channel.name for channel in self.dut.get_modules('fei4_rx') ] for channel in channel_names: # enabling readout self.dut['ENABLE_CHANNEL'][channel] = 1 self.dut['POWER_QUAD']['EN_' + channel] = 1 self.dut['ENABLE_CHANNEL'].write() self.dut['POWER_QUAD'].write() else: raise RuntimeError('Unknown adapter card') self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'mio_gpac': # PWR self.dut['V_in'].set_current_limit( 0.1, unit='A') # one for all, max. 1A # V_in self.dut['V_in'].set_voltage(2.1, unit='V') self.dut['V_in'].set_enable(True) if self.dut["V_in"].get_over_current(): self.power_off() raise Exception('V_in overcurrent detected') # Vdd, also enabling LVDS transceivers self.dut['CCPD_Vdd'].set_voltage(1.80, unit='V') self.dut['CCPD_Vdd'].set_enable(True) if self.dut["CCPD_Vdd"].get_over_current(): self.power_off() raise Exception('Vdd overcurrent detected') # Vssa self.dut['CCPD_Vssa'].set_voltage(1.50, unit='V') self.dut['CCPD_Vssa'].set_enable(True) if self.dut["CCPD_Vssa"].get_over_current(): self.power_off() raise Exception('Vssa overcurrent detected') # VGate self.dut['CCPD_VGate'].set_voltage(2.10, unit='V') self.dut['CCPD_VGate'].set_enable(True) if self.dut["CCPD_VGate"].get_over_current(): self.power_off() raise Exception('VGate overcurrent detected') # enabling readout self.dut['ENABLE_CHANNEL']['FE'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL']['CCPD_TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'seabas': # enabling readout self.dut['ENABLE_CHANNEL']['CH1'] = 0 self.dut['ENABLE_CHANNEL']['CH2'] = 0 self.dut['ENABLE_CHANNEL']['CH3'] = 0 self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL'].write() else: logging.warning('Omit initialization of DUT %s', self.dut.name) def init_fe(self): if 'fe_configuration' in self.conf: last_configuration = self._get_configuration() # init config, a number <=0 will also do the initialization (run 0 does not exists) if (not self.conf['fe_configuration'] and not last_configuration) or (isinstance( self.conf['fe_configuration'], (int, long)) and self.conf['fe_configuration'] <= 0): if 'chip_address' in self.conf and self.conf['chip_address']: chip_address = self.conf['chip_address'] broadcast = False else: chip_address = 0 broadcast = True if 'fe_flavor' in self.conf and self.conf['fe_flavor']: self._conf['fe_configuration'] = FEI4Register( fe_type=self.conf['fe_flavor'], chip_address=chip_address, broadcast=broadcast) else: raise ValueError('No fe_flavor given') # use existing config elif not self.conf['fe_configuration'] and last_configuration: self._conf['fe_configuration'] = FEI4Register( configuration_file=last_configuration) # path elif isinstance(self.conf['fe_configuration'], basestring): if os.path.isabs(self.conf['fe_configuration']): fe_configuration = self.conf['fe_configuration'] else: fe_configuration = os.path.join( self.conf['working_dir'], self.conf['fe_configuration']) self._conf['fe_configuration'] = FEI4Register( configuration_file=fe_configuration) # run number elif isinstance(self.conf['fe_configuration'], (int, long)) and self.conf['fe_configuration'] > 0: self._conf['fe_configuration'] = FEI4Register( configuration_file=self._get_configuration( self.conf['fe_configuration'])) # assume fe_configuration already initialized elif not isinstance(self.conf['fe_configuration'], FEI4Register): raise ValueError('No valid fe_configuration given') # init register utils self.register_utils = FEI4RegisterUtils(self.dut, self.register) # reset and configuration self.register_utils.global_reset() self.register_utils.configure_all() if is_fe_ready(self): reset_service_records = False else: reset_service_records = True self.register_utils.reset_bunch_counter() self.register_utils.reset_event_counter() if reset_service_records: # resetting service records must be done once after power up self.register_utils.reset_service_records() else: pass # no fe_configuration def pre_run(self): # sending data self.socket_addr = self._run_conf['send_data'] if self.socket_addr: logging.info('Send data to %s', self.socket_addr) # scan parameters if 'scan_parameters' in self.run_conf: if isinstance(self.run_conf['scan_parameters'], basestring): self.run_conf['scan_parameters'] = ast.literal_eval( self.run_conf['scan_parameters']) sp = namedtuple( 'scan_parameters', field_names=zip(*self.run_conf['scan_parameters'])[0]) self.scan_parameters = sp(*zip( *self.run_conf['scan_parameters'])[1]) else: sp = namedtuple_with_defaults('scan_parameters', field_names=[]) self.scan_parameters = sp() logging.info( 'Scan parameter(s): %s', ', '.join([ '%s=%s' % (key, value) for (key, value) in self.scan_parameters._asdict().items() ]) if self.scan_parameters else 'None') # init DUT if not isinstance(self.conf['dut'], Dut): module_path = os.path.dirname( os.path.abspath(inspect.getfile(inspect.currentframe()))) if isinstance(self.conf['dut'], basestring): # dirty fix for Windows pathes self.conf['dut'] = os.path.normpath(self.conf['dut'].replace( '\\', '/')) # abs path if os.path.isabs(self.conf['dut']): dut = self.conf['dut'] # working dir elif os.path.exists( os.path.join(self.conf['working_dir'], self.conf['dut'])): dut = os.path.join(self.conf['working_dir'], self.conf['dut']) # path of this file elif os.path.exists(os.path.join(module_path, self.conf['dut'])): dut = os.path.join(module_path, self.conf['dut']) else: raise ValueError('%s: dut file not found' % self.conf['dut']) self._conf['dut'] = Dut(dut) else: self._conf['dut'] = Dut(self.conf['dut']) # only initialize when DUT was not initialized before if 'dut_configuration' in self.conf and self.conf[ 'dut_configuration']: if isinstance(self.conf['dut_configuration'], basestring): # dirty fix for Windows pathes self.conf['dut_configuration'] = os.path.normpath( self.conf['dut_configuration'].replace('\\', '/')) # abs path if os.path.isabs(self.conf['dut_configuration']): dut_configuration = self.conf['dut_configuration'] # working dir elif os.path.exists( os.path.join(self.conf['working_dir'], self.conf['dut_configuration'])): dut_configuration = os.path.join( self.conf['working_dir'], self.conf['dut_configuration']) # path of dut file elif os.path.exists( os.path.join(os.path.dirname(self.dut.conf_path), self.conf['dut_configuration'])): dut_configuration = os.path.join( os.path.dirname(self.dut.conf_path), self.conf['dut_configuration']) # path of this file elif os.path.exists( os.path.join(module_path, self.conf['dut_configuration'])): dut_configuration = os.path.join( module_path, self.conf['dut_configuration']) else: raise ValueError( '%s: dut_configuration file not found' % self.conf['dut_configuration']) self.dut.init(dut_configuration) else: self.dut.init(self.conf['dut_configuration']) else: self.dut.init() # additional init of the DUT self.init_dut() else: pass # do nothing, already initialized # FIFO readout self.fifo_readout = FifoReadout(self.dut) # initialize the FE self.init_fe() def do_run(self): with open_raw_data_file( filename=self.output_filename, mode='w', title=self.run_id, register=self.register, conf=self.conf, run_conf=self.run_conf, scan_parameters=self.scan_parameters._asdict(), socket_addr=self.socket_addr) as self.raw_data_file: with self.register.restored(name=self.run_number): # configure for scan self.configure() self.raw_data_file.save_register_configuration( ) # save register configration after configure() since it is most likely changed there self.fifo_readout.reset_rx() self.fifo_readout.reset_sram_fifo() self.fifo_readout.print_readout_status() # scan self.scan() def post_run(self): try: self.fifo_readout.print_readout_status() # no device? except Exception: pass if not self.err_queue.empty(): exc = self.err_queue.get() # well known errors if isinstance(exc[1], (RxSyncError, EightbTenbError, FifoError, NoDataTimeout, StopTimeout)): raise RunAborted(exc[1]) # some other error via handle_err(), print to crash.log else: raise exc[0], exc[1], exc[2] elif self.abort_run.is_set(): raise RunAborted('Read the log') # analyzing data try: self.analyze() # known errors except AnalysisError as e: logging.error('Analysis of data failed: %s', e) raise RunAborted('Read the log') # analyzed data, save config else: self.register.save_configuration(self.output_filename) # other reasons if self.stop_run.is_set(): raise RunStopped('Read the log') def cleanup_run(self): # no execption should be thrown here self.raw_data_file = None try: self.dut['USB'].close() # free USB resources # no device? except Exception: logging.error('Cannot close USB device') def handle_data(self, data): self.raw_data_file.append_item( data, scan_parameters=self.scan_parameters._asdict(), flush=False) def handle_err(self, exc): if self.reset_rx_on_error and isinstance( exc[1], (RxSyncError, EightbTenbError)): self.fifo_readout.print_readout_status() self.fifo_readout.reset_rx() else: self.err_queue.put(exc) self.abort(msg='%s' % exc[1]) def _get_configuration(self, run_number=None): def find_file(run_number): for root, _, files in os.walk(self.working_dir): for cfgfile in files: cfg_root, cfg_ext = os.path.splitext(cfgfile) if cfg_root.startswith(''.join([ str(run_number), '_', self.module_id ])) and cfg_ext.endswith(".cfg"): return os.path.join(root, cfgfile) if not run_number: run_numbers = sorted( self._get_run_numbers(status='FINISHED').iterkeys(), reverse=True) for run_number in run_numbers: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: raise ValueError('Found no configuration with run number %s' % run_number) def set_scan_parameters(self, *args, **kwargs): fields = dict(kwargs) for index, field in enumerate(self.scan_parameters._fields): try: value = args[index] except IndexError: break else: if field in fields: raise TypeError( 'Got multiple values for keyword argument %s' % field) fields[field] = value scan_parameters_old = self.scan_parameters._asdict() self.scan_parameters = self.scan_parameters._replace(**fields) scan_parameters_new = self.scan_parameters._asdict() diff = [ name for name in scan_parameters_old.keys() if np.any(scan_parameters_old[name] != scan_parameters_new[name]) ] if diff: logging.info( 'Changing scan parameter(s): %s', ', '.join([('%s=%s' % (name, fields[name])) for name in diff])) @contextmanager def readout(self, *args, **kwargs): timeout = kwargs.pop('timeout', 10.0) self.start_readout(*args, **kwargs) try: yield self.stop_readout(timeout=timeout) finally: # in case something fails, call this on last resort if self.fifo_readout.is_running: self.fifo_readout.stop(timeout=0.0) def start_readout(self, *args, **kwargs): # Pop parameters for fifo_readout.start callback = kwargs.pop('callback', self.handle_data) clear_buffer = kwargs.pop('clear_buffer', False) fill_buffer = kwargs.pop('fill_buffer', False) reset_sram_fifo = kwargs.pop('reset_sram_fifo', False) errback = kwargs.pop('errback', self.handle_err) no_data_timeout = kwargs.pop('no_data_timeout', None) if args or kwargs: self.set_scan_parameters(*args, **kwargs) self.fifo_readout.start(reset_sram_fifo=reset_sram_fifo, fill_buffer=fill_buffer, clear_buffer=clear_buffer, callback=callback, errback=errback, no_data_timeout=no_data_timeout) def stop_readout(self, timeout=10.0): self.fifo_readout.stop(timeout=timeout) @abc.abstractmethod def configure(self): '''Implementation of the run configuration. Will be executed before starting the scan routine. ''' pass @abc.abstractmethod def scan(self): '''Implementation of the scan routine. Do you want to write your own scan? Here is the place to begin. ''' pass @abc.abstractmethod def analyze(self): '''Implementation of run data processing. Will be executed after finishing the scan routine. ''' pass
class Fei4RunBase(RunBase): '''Basic FEI4 run meta class. Base class for scan- / tune- / analyze-class. ''' __metaclass__ = abc.ABCMeta def __init__(self, conf, run_conf=None): # default run conf parameters added for all scans if 'comment' not in self._default_run_conf: self._default_run_conf.update({'comment': ''}) if 'reset_rx_on_error' not in self._default_run_conf: self._default_run_conf.update({'reset_rx_on_error': False}) super(Fei4RunBase, self).__init__(conf=conf, run_conf=run_conf) # default conf parameters if 'working_dir' not in conf: conf.update({'working_dir': ''}) # path string, if empty, path of configuration.yaml file will be used if 'zmq_context' not in conf: conf.update({'zmq_context': None}) # ZMQ context if 'send_data' not in conf: conf.update({'send_data': None}) # address string of PUB socket if 'send_error_msg' not in conf: conf.update({'send_error_msg': None}) # bool self.err_queue = Queue() self.fifo_readout = None self.raw_data_file = None @property def working_dir(self): if self.module_id: return os.path.join(self._conf['working_dir'], self.module_id) else: return os.path.join(self._conf['working_dir'], self.run_id) @property def dut(self): return self._conf['dut'] @property def register(self): return self._conf['fe_configuration'] @property def output_filename(self): if self.module_id: return os.path.join(self.working_dir, str(self.run_number) + "_" + self.module_id + "_" + self.run_id) else: return os.path.join(self.working_dir, str(self.run_number) + "_" + self.run_id) @property def module_id(self): if 'module_id' in self._conf and self._conf['module_id']: module_id = str(self._conf['module_id']) module_id = re.sub(r"[^\w\s+]", '', module_id) return re.sub(r"\s+", '_', module_id).lower() else: return None def init_dut(self): if self.dut.name == 'mio': if self.dut.get_modules('FEI4AdapterCard') and [adapter_card for adapter_card in self.dut.get_modules('FEI4AdapterCard') if adapter_card.name == 'ADAPTER_CARD']: self.dut['ADAPTER_CARD'].set_voltage('VDDA1', 1.5) self.dut['ADAPTER_CARD'].set_voltage('VDDA2', 1.5) self.dut['ADAPTER_CARD'].set_voltage('VDDD1', 1.2) self.dut['ADAPTER_CARD'].set_voltage('VDDD2', 1.2) self.dut['POWER_SCC']['EN_VD1'] = 1 self.dut['POWER_SCC']['EN_VD2'] = 1 # also EN_VPLL on old SCAC self.dut['POWER_SCC']['EN_VA1'] = 1 self.dut['POWER_SCC']['EN_VA2'] = 1 self.dut['POWER_SCC'].write() # enabling readout self.dut['ENABLE_CHANNEL']['CH1'] = 0 # RD2Bar on SCAC self.dut['ENABLE_CHANNEL']['CH2'] = 0 # RD1Bar on SCAC self.dut['ENABLE_CHANNEL']['CH3'] = 0 # RABar on SCAC self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.get_modules('FEI4QuadModuleAdapterCard') and [adapter_card for adapter_card in self.dut.get_modules('FEI4QuadModuleAdapterCard') if adapter_card.name == 'ADAPTER_CARD']: # resetting over current status self.dut['POWER_QUAD']['EN_CH1'] = 0 self.dut['POWER_QUAD']['EN_CH2'] = 0 self.dut['POWER_QUAD']['EN_CH3'] = 0 self.dut['POWER_QUAD']['EN_CH4'] = 0 self.dut['POWER_QUAD'].write() self.dut['ADAPTER_CARD'].set_voltage('CH1', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH2', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH3', 2.1) self.dut['ADAPTER_CARD'].set_voltage('CH4', 2.1) self.dut['POWER_QUAD'].write() channel_names = [channel.name for channel in self.dut.get_modules('fei4_rx')] for channel in channel_names: # enabling readout self.dut['ENABLE_CHANNEL'][channel] = 1 self.dut['POWER_QUAD']['EN_' + channel] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() self.dut['POWER_QUAD'].write() else: logging.warning('Unknown adapter card') # do the minimal configuration here self.dut['ENABLE_CHANNEL']['CH1'] = 0 # RD2Bar on SCAC self.dut['ENABLE_CHANNEL']['CH2'] = 0 # RD1Bar on SCAC self.dut['ENABLE_CHANNEL']['CH3'] = 0 # RABar on SCAC self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'mio_gpac': # PWR self.dut['V_in'].set_current_limit(0.1, unit='A') # one for all, max. 1A # V_in self.dut['V_in'].set_voltage(2.1, unit='V') self.dut['V_in'].set_enable(True) if self.dut["V_in"].get_over_current(): self.power_off() raise Exception('V_in overcurrent detected') # Vdd, also enabling LVDS transceivers self.dut['CCPD_Vdd'].set_voltage(1.80, unit='V') self.dut['CCPD_Vdd'].set_enable(True) if self.dut["CCPD_Vdd"].get_over_current(): self.power_off() raise Exception('Vdd overcurrent detected') # Vssa self.dut['CCPD_Vssa'].set_voltage(1.50, unit='V') self.dut['CCPD_Vssa'].set_enable(True) if self.dut["CCPD_Vssa"].get_over_current(): self.power_off() raise Exception('Vssa overcurrent detected') # VGate self.dut['CCPD_VGate'].set_voltage(2.10, unit='V') self.dut['CCPD_VGate'].set_enable(True) if self.dut["CCPD_VGate"].get_over_current(): self.power_off() raise Exception('VGate overcurrent detected') # enabling readout self.dut['ENABLE_CHANNEL']['FE'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL']['CCPD_TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'seabas2': channel_names = [channel.name for channel in self.dut.get_modules('fei4_rx')] for channel in channel_names: # enabling readout self.dut['ENABLE_CHANNEL'][channel] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL'].write() elif self.dut.name == 'lx9': # enable LVDS RX/TX self.dut['I2C'].write(0xe8, [6, 0xf0, 0xff]) self.dut['I2C'].write(0xe8, [2, 0x01, 0x00]) # select channels here elif self.dut.name == 'nexys4': # enable LVDS RX/TX self.dut['I2C'].write(0xe8, [6, 0xf0, 0xff]) self.dut['I2C'].write(0xe8, [2, 0x0f, 0x00]) # select channels here self.dut['ENABLE_CHANNEL']['CH1'] = 0 self.dut['ENABLE_CHANNEL']['CH2'] = 0 self.dut['ENABLE_CHANNEL']['CH3'] = 0 self.dut['ENABLE_CHANNEL']['CH4'] = 1 self.dut['ENABLE_CHANNEL']['TLU'] = 1 self.dut['ENABLE_CHANNEL']['TDC'] = 1 self.dut['ENABLE_CHANNEL'].write() else: logging.warning('Omit initialization of DUT %s', self.dut.name) def init_fe(self): if 'fe_configuration' in self._conf: last_configuration = self._get_configuration() # init config, a number <=0 will also do the initialization (run 0 does not exists) if (not self._conf['fe_configuration'] and not last_configuration) or (isinstance(self._conf['fe_configuration'], (int, long)) and self._conf['fe_configuration'] <= 0): if 'chip_address' in self._conf and self._conf['chip_address']: chip_address = self._conf['chip_address'] broadcast = False else: chip_address = 0 broadcast = True if 'fe_flavor' in self._conf and self._conf['fe_flavor']: self._conf['fe_configuration'] = FEI4Register(fe_type=self._conf['fe_flavor'], chip_address=chip_address, broadcast=broadcast) else: raise ValueError('No fe_flavor given') # use existing config elif not self._conf['fe_configuration'] and last_configuration: self._conf['fe_configuration'] = FEI4Register(configuration_file=last_configuration) # path elif isinstance(self._conf['fe_configuration'], basestring): if os.path.isabs(self._conf['fe_configuration']): fe_configuration = self._conf['fe_configuration'] else: fe_configuration = os.path.join(self._conf['working_dir'], self._conf['fe_configuration']) self._conf['fe_configuration'] = FEI4Register(configuration_file=fe_configuration) # run number elif isinstance(self._conf['fe_configuration'], (int, long)) and self._conf['fe_configuration'] > 0: self._conf['fe_configuration'] = FEI4Register(configuration_file=self._get_configuration(self._conf['fe_configuration'])) # assume fe_configuration already initialized elif not isinstance(self._conf['fe_configuration'], FEI4Register): raise ValueError('No valid fe_configuration given') # init register utils self.register_utils = FEI4RegisterUtils(self.dut, self.register) # reset and configuration self.register_utils.global_reset() self.register_utils.configure_all() if is_fe_ready(self): reset_service_records = False else: reset_service_records = True self.register_utils.reset_bunch_counter() self.register_utils.reset_event_counter() if reset_service_records: # resetting service records must be done once after power up self.register_utils.reset_service_records() else: pass # no fe_configuration def pre_run(self): # clear error queue in case run is executed a second time self.err_queue.queue.clear() # opening ZMQ context and binding socket if self._conf['send_data'] and not self._conf['zmq_context']: logging.info('Creating ZMQ context') self._conf['zmq_context'] = zmq.Context() # contexts are thread safe unlike sockets else: logging.info('Using existing socket') # scan parameters if 'scan_parameters' in self._run_conf: if isinstance(self._run_conf['scan_parameters'], basestring): self._run_conf['scan_parameters'] = ast.literal_eval(self._run_conf['scan_parameters']) sp = namedtuple('scan_parameters', field_names=zip(*self._run_conf['scan_parameters'])[0]) self.scan_parameters = sp(*zip(*self._run_conf['scan_parameters'])[1]) else: sp = namedtuple_with_defaults('scan_parameters', field_names=[]) self.scan_parameters = sp() logging.info('Scan parameter(s): %s', ', '.join(['%s=%s' % (key, value) for (key, value) in self.scan_parameters._asdict().items()]) if self.scan_parameters else 'None') # init DUT if not isinstance(self._conf['dut'], Dut): module_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) if isinstance(self._conf['dut'], basestring): # dirty fix for Windows pathes self._conf['dut'] = os.path.normpath(self._conf['dut'].replace('\\', '/')) # abs path if os.path.isabs(self._conf['dut']): dut = self._conf['dut'] # working dir elif os.path.exists(os.path.join(self._conf['working_dir'], self._conf['dut'])): dut = os.path.join(self._conf['working_dir'], self._conf['dut']) # path of this file elif os.path.exists(os.path.join(module_path, self._conf['dut'])): dut = os.path.join(module_path, self._conf['dut']) else: raise ValueError('dut parameter not a valid path: %s' % self._conf['dut']) else: dut = self._conf['dut'] dut = Dut(dut) # only initialize when DUT was not initialized before if 'dut_configuration' in self._conf and self._conf['dut_configuration']: if isinstance(self._conf['dut_configuration'], basestring): # dirty fix for Windows pathes self._conf['dut_configuration'] = os.path.normpath(self._conf['dut_configuration'].replace('\\', '/')) # abs path if os.path.isabs(self._conf['dut_configuration']): dut_configuration = self._conf['dut_configuration'] # working dir elif os.path.exists(os.path.join(self._conf['working_dir'], self._conf['dut_configuration'])): dut_configuration = os.path.join(self._conf['working_dir'], self._conf['dut_configuration']) # path of dut file elif os.path.exists(os.path.join(os.path.dirname(dut.conf_path), self._conf['dut_configuration'])): dut_configuration = os.path.join(os.path.dirname(dut.conf_path), self._conf['dut_configuration']) # path of this file elif os.path.exists(os.path.join(module_path, self._conf['dut_configuration'])): dut_configuration = os.path.join(module_path, self._conf['dut_configuration']) else: raise ValueError('dut_configuration parameter not a valid path: %s' % self._conf['dut_configuration']) # make dict dut_configuration = RunManager.open_conf(dut_configuration) # change bit file path if 'USB' in dut_configuration and 'bit_file' in dut_configuration['USB'] and dut_configuration['USB']['bit_file']: bit_file = os.path.normpath(dut_configuration['USB']['bit_file'].replace('\\', '/')) # abs path if os.path.isabs(bit_file): pass # working dir elif os.path.exists(os.path.join(self._conf['working_dir'], bit_file)): bit_file = os.path.join(self._conf['working_dir'], bit_file) # path of dut file elif os.path.exists(os.path.join(os.path.dirname(dut.conf_path), bit_file)): bit_file = os.path.join(os.path.dirname(dut.conf_path), bit_file) # path of this file elif os.path.exists(os.path.join(module_path, bit_file)): bit_file = os.path.join(module_path, bit_file) else: raise ValueError('bit_file parameter not a valid path: %s' % bit_file) dut_configuration['USB']['bit_file'] = bit_file else: dut_configuration = self._conf['dut_configuration'] else: dut_configuration = None dut.init(dut_configuration) # assign dut after init in case of exceptions during init self._conf['dut'] = dut # additional init of the DUT self.init_dut() else: pass # do nothing, already initialized # FIFO readout self.fifo_readout = FifoReadout(self.dut) # initialize the FE self.init_fe() def do_run(self): with self.register.restored(name=self.run_number): # configure for scan self.configure() self.fifo_readout.reset_rx() self.fifo_readout.reset_sram_fifo() self.fifo_readout.print_readout_status() with open_raw_data_file(filename=self.output_filename, mode='w', title=self.run_id, register=self.register, conf=self._conf, run_conf=self._run_conf, scan_parameters=self.scan_parameters._asdict(), context=self._conf['zmq_context'], socket_address=self._conf['send_data']) as self.raw_data_file: # scan self.scan() def post_run(self): # print FIFO status try: self.fifo_readout.print_readout_status() except Exception: # no device? pass # analyzing data try: self.analyze() except Exception: # analysis errors self.handle_err(sys.exc_info()) else: # analyzed data, save config self.register.save_configuration(self.output_filename) if not self.err_queue.empty(): exc = self.err_queue.get() # well known errors, do not print traceback if isinstance(exc[1], (RxSyncError, EightbTenbError, FifoError, NoDataTimeout, StopTimeout, AnalysisError)): raise RunAborted(exc[1]) # some other error via handle_err(), print traceback else: raise exc[0], exc[1], exc[2] elif self.abort_run.is_set(): raise RunAborted() elif self.stop_run.is_set(): raise RunStopped() # if ending up here, succcess! def cleanup_run(self): # no execption should be thrown here self.raw_data_file = None # USB interface needs to be closed here, otherwise an USBError may occur # USB interface can be reused at any time after close without another init try: usb_intf = self.dut.get_modules('SiUsb') except AttributeError: pass # not yet initialized else: if usb_intf: import usb.core for board in usb_intf: try: board.close() # free resources of USB except usb.core.USBError: logging.error('Cannot close USB device') except ValueError: pass # no USB interface, Basil <= 2.1.1 except KeyError: pass # no USB interface, Basil > 2.1.1 except TypeError: pass # DUT not yet initialized except AttributeError: pass # USB interface not yet initialized else: pass # logging.error('Closed USB device') def handle_data(self, data): '''Handling of the data. Parameters ---------- data : list, tuple Data tuple of the format (data (np.array), last_time (float), curr_time (float), status (int)) ''' self.raw_data_file.append_item(data, scan_parameters=self.scan_parameters._asdict(), flush=False) def handle_err(self, exc): '''Handling of Exceptions. Parameters ---------- exc : list, tuple Information of the exception of the format (type, value, traceback). Uses the return value of sys.exc_info(). ''' if self.reset_rx_on_error and isinstance(exc[1], (RxSyncError, EightbTenbError)): self.fifo_readout.print_readout_status() self.fifo_readout.reset_rx() else: # print just the first error massage if not self.abort_run.is_set(): self.abort(msg=str(exc[1])) self.err_queue.put(exc) def _get_configuration(self, run_number=None): def find_file(run_number): for root, _, files in os.walk(self.working_dir): for cfgfile in files: cfg_root, cfg_ext = os.path.splitext(cfgfile) if cfg_root.startswith(''.join([str(run_number), '_', self.module_id])) and cfg_ext.endswith(".cfg"): return os.path.join(root, cfgfile) if not run_number: run_numbers = sorted(self._get_run_numbers(status='FINISHED').iterkeys(), reverse=True) for run_number in run_numbers: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: cfg_file = find_file(run_number) if cfg_file: return cfg_file else: raise ValueError('Found no configuration with run number %s' % run_number) def set_scan_parameters(self, *args, **kwargs): fields = dict(kwargs) for index, field in enumerate(self.scan_parameters._fields): try: value = args[index] except IndexError: break else: if field in fields: raise TypeError('Got multiple values for keyword argument %s' % field) fields[field] = value scan_parameters_old = self.scan_parameters._asdict() self.scan_parameters = self.scan_parameters._replace(**fields) scan_parameters_new = self.scan_parameters._asdict() diff = [name for name in scan_parameters_old.keys() if np.any(scan_parameters_old[name] != scan_parameters_new[name])] if diff: logging.info('Changing scan parameter(s): %s', ', '.join([('%s=%s' % (name, fields[name])) for name in diff])) @contextmanager def readout(self, *args, **kwargs): timeout = kwargs.pop('timeout', 10.0) self.start_readout(*args, **kwargs) try: yield self.stop_readout(timeout=timeout) finally: # in case something fails, call this on last resort if self.fifo_readout.is_running: self.fifo_readout.stop(timeout=0.0) def start_readout(self, *args, **kwargs): # Pop parameters for fifo_readout.start callback = kwargs.pop('callback', self.handle_data) clear_buffer = kwargs.pop('clear_buffer', False) fill_buffer = kwargs.pop('fill_buffer', False) reset_sram_fifo = kwargs.pop('reset_sram_fifo', False) errback = kwargs.pop('errback', self.handle_err) no_data_timeout = kwargs.pop('no_data_timeout', None) if args or kwargs: self.set_scan_parameters(*args, **kwargs) self.fifo_readout.start(reset_sram_fifo=reset_sram_fifo, fill_buffer=fill_buffer, clear_buffer=clear_buffer, callback=callback, errback=errback, no_data_timeout=no_data_timeout) def stop_readout(self, timeout=10.0): self.fifo_readout.stop(timeout=timeout) def _cleanup(self): # called in run base after exception handling super(Fei4RunBase, self)._cleanup() if 'send_message' in self._conf and self._run_status in self._conf['send_message']['status']: subject = '{}{} ({})'.format(self._conf['send_message']['subject_prefix'], self._run_status, gethostname()) last_status_message = '{} run {} ({}) in {} (total time: {})'.format(self.run_status, self.run_number, self.__class__.__name__, self.working_dir, str(self._total_run_time)) body = '\n'.join(item for item in [self._last_traceback, last_status_message] if item) try: send_mail(subject=subject, body=body, smtp_server=self._conf['send_message']['smtp_server'], user=self._conf['send_message']['user'], password=self._conf['send_message']['password'], from_addr=self._conf['send_message']['from_addr'], to_addrs=self._conf['send_message']['to_addrs']) except: logging.warning("Failed sending pyBAR status report") @abc.abstractmethod def configure(self): '''Implementation of the run configuration. Will be executed before starting the scan routine. ''' pass @abc.abstractmethod def scan(self): '''Implementation of the scan routine. Do you want to write your own scan? Here is the place to begin. ''' pass @abc.abstractmethod def analyze(self): '''Implementation of run data processing. Will be executed after finishing the scan routine. ''' pass