123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- import re
- import time
- import logging
- import subprocess
- import ConfigParser
- import numpy as np
- log = logging.getLogger(__name__)
- class BoardError(Exception):
- pass
- class ObserverError(Exception):
- pass
- class BoardConfiguration():
- def __init__(self, config_file=None, logger=None):
- self._config = {}
- self._observers = {}
- self.logger = logger
- self._set_defaults()
- if config_file:
- self.load_config(config_file)
- def _set_defaults(self):
- c = self._config
- c['fpga_delay_max'] = 15
- c['fpga_delay'] = 0
- c['fpga_delay_factor'] = 150
-
- c['chip_delay_max'] = 31
- c['chip_1_delay'] = 4
- c['chip_2_delay'] = 4
- c['chip_3_delay'] = 4
- c['chip_4_delay'] = 4
- c['chip_delay_factor'] = 3
- c['th_delay_max'] = 15
- c['th_delay'] = 3
- c['th_delay_factor'] = 150
-
- c['adc_delay_max'] = 15
- c['adc_1_delay'] = 4
- c['adc_2_delay'] = 4
- c['adc_3_delay'] = 4
- c['adc_4_delay'] = 4
- c['adc_delay_factor'] = 150
-
- c['th_to_adc_cycles'] = 7
- c['adc_1_delay_individual'] = -1 # -1 = 'ADC 1 delay is the same as th_to_adc_cycles'
- # 0 .. 16 = 'Use this offset instead'
-
- c['orbits_observe'] = 100
- c['orbits_skip'] = 2
- def load_config(self, filename):
- if filename:
- config = ConfigParser.RawConfigParser()
- if not config.read(str(filename)):
- return
- for key in self._config.keys():
- try:
- self._config[key] = config.getint('Config', key)
- if self.logger:
- self.logger.info("Read '%s' for '%s' from '%s'"%(str(self._config[key]), key, str(filename)))
- except ConfigParser.NoOptionError as e:
- pass
- except ConfigParser.NoSectionError as e:
- pass
- def save_config(self, filename):
- if filename:
- with open(str(filename), 'w') as f:
- cp = ConfigParser.RawConfigParser()
- cp.add_section('Config')
- for key in self._config.keys():
- cp.set('Config', key, self._config[key])
- f.write('#\n'
- '# HEB (Hot Electron Bolometer) Configuration file\n'
- '#\n'
- '# (c) Karlsruhe Institute of Technology, 2014\n'
- '# All rights reserved.\n'
- '#\n'
- '# Applicable Firmware Version(s):\n'
- '#\n\n')
- cp.write(f)
- def get(self, key):
- return self._config.get(key, None)
- def update(self, key, value):
- self._config[key] = value
- self._notify_observers(key, value)
- def get(self, key):
- return self._config.get(key, None)
- def update(self, key, value):
- self._config[key] = value
- self._notify_observers(key, value)
- def observe(self, who, callback, key):
- if key not in self._config.keys():
- raise ObserverError(str("Key '%s' is unknown."%key))
-
- if self._observers.get(key, None) is None:
- self._observers[key] = []
-
- self._observers[key].append([who, callback])
-
- def unobserve(self, who, key=None):
- if key is not None:
- observers = np.array(self._observers.get(key, None))
- if observers is None:
- return
- if who not in observers[:, 0]:
- return
- for i, _obs in enumerate(self._observers[key]):
- if _obs[0] is who:
- del self._observers[key][i]
- if not self._observers[key]:
- del self._observers[key]
- return
-
- for _key in self._observers.keys():
- for i, _obs in enumerate(self._observers[_key]):
- if _obs[0] is who:
- del self._observers[_key][i]
- if not self._observers[_key]:
- del self._observers[_key]
- def _notify_observers(self, key, value):
- observers = self._observers.get(key, None)
- if observers is None:
- return
- for (who, callback) in observers:
- callback(value)
-
- def make_uint(self, value, maximum, name=None):
- if value is None:
- raise ValueError(str("%s Value is invalid (None)" % name))
-
- val = None
- try:
- val = int(value)
- except:
- raise ValueError(str("%s Value is not a valid number" % name))
- if maximum is not None:
- if val > maximum:
- raise ValueError(str("%s Value is too large (>%i)" % (name, maximum)))
-
- if val < 0:
- raise ValueError(str("%s Values below 0 are not allowed" % name))
-
- return val
- def set_fpga_delay(self, value):
- time_factor = self.make_uint(value, self.get('fpga_delay_max'), 'FPGA_Delay')
- reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "0"
- write_pci(reg_value, '0x9060')
- if self.logger:
- self.logger.info("Set FPGA clock delay %i * %i --> %i picoseconds" % (time_factor, self.get('fpga_delay_factor'), time_factor*self.get('fpga_delay_factor')))
- self.update('fpga_delay', value)
- def set_chip_delay(self, adcs, values):
- if not adcs or not values:
- if self.logger:
- self.logger.info("Nothing to do for chip delay.")
- return
- _adcs = []
- for adc in adcs:
- _adcs.append(self.make_uint(adc, 3, 'ADC_'))
-
- _values = []
- for value in values:
- _values.append(self.make_uint(value, self.get('chip_delay_max'), 'ADC_Value'))
- a_v = zip(_adcs, _values)
-
- factors = [None, None, None, None]
- for (adc, value) in a_v:
- factors[adc] = value
-
- reg_value = ''
- mask = ''
- # Chip Delays are stored as 'ADC_4 ADC_3 ADC_2 ADC_1' in the register.
- # Therefore, we need to traverse the factors array in reverse order
- for value in reversed(factors):
- if value is not None:
- reg_value += '{0:02x}'.format(value)
- mask += 'ff'
- else:
- reg_value += '00'
- mask += '00'
-
- write_pci(reg_value, '0x9080', hex_mask=mask)
- if self.logger:
- s = "Setting ADC Delays:"
- for (adc, value) in a_v:
- s += ' ADC_%i Fine Delay: %i,' % (adc, value)
- s = s[:-1] # cut away the last dangling ','
- self.logger.info(s)
- for (adc, value) in a_v:
- s = 'chip_%i_delay'%(adc+1)
- self.update(s, value)
- def set_th_delay(self, value):
- time_factor = self.make_uint(value, self.get('th_delay_max'), 'TH_Delay')
- reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "3"
- write_pci(reg_value, '0x9060')
- if self.logger:
- self.logger.info("Set T/H Signal delay %i * %i --> %i picoseconds" % (time_factor, self.get('th_delay_factor'), time_factor*self.get('th_delay_factor')))
- self.update('th_delay', value)
- def set_adc_delay(self, adc, value):
- if adc is None or value is None:
- if self.logger:
- self.logger.info("Nothing to do for ADC delay.")
- return
- _adc = self.make_uint(adc, 3, 'ADC Number')
- _val = self.make_uint(value, self.get('adc_delay_max'), 'ADC Delay')
- reg_value = "0x000501" + '{0:01x}'.format(_val) + "%i" % (_adc+4)
- write_pci(reg_value, '0x9060')
- if self.logger:
- s = "Setting ADC_%i delay %i * %i --> %i picoseconds" % ((_adc+1), _val, self.get('adc_delay_factor'), _val*self.get('adc_delay_factor'))
- self.logger.info(s)
- adc_s = 'adc_%i_delay'%(_adc+1)
- self.update(adc_s, _val)
- def set_delay(self, n):
- def write_delay(value, channel):
- cmd = '00501' + '%01x' % value + str(channel)
- write_pci(cmd, reg='0x9060')
- time.sleep(0.005)
- write_delay(n, 3)
- self.update('th_delay', n)
- delay = n + self.get('th_to_adc_cycles')
- if delay > self.get('adc_delay_max'):
- delay -= self.get('adc_delay_max') + 1
- write_delay(delay, 5)
- self.update('adc_2_delay', delay)
- write_delay(delay, 6)
- self.update('adc_3_delay', delay)
- write_delay(delay, 7)
- self.update('adc_4_delay', delay)
- #ADC 1 might have an individual delay
- try:
- delay = n + self.make_uint(self.get('adc_1_delay_individual'), 16, 'ADC 1 individual delay')
- except ValueError:
- if self.logger:
- self.logger.info(r"'adc_1_delay_individual' not set or inactive. Using default.")
- if delay > self.get('adc_delay_max'):
- delay -= self.get('adc_delay_max') + 1
- write_delay(delay, 4)
- self.update('adc_1_delay', delay)
- def safe_call(cmd):
- try:
- return subprocess.check_output(cmd)
- except subprocess.CalledProcessError as e:
- raise BoardError(e.output)
- except OSError as e:
- raise BoardError('{}: {}'.format(' '.join(cmd), str(e)))
- def write_pci(value, reg='0x9040', opts=[], hex_mask='FFFFFFFF'):
- if hex_mask != 'FFFFFFFF':
- if len(hex_mask) > 8:
- hex_mask = hex_mask[-8:]
- prev_val = read_pci(1, reg)[0]
- prev_bits = '{0:032b}'.format(int(prev_val,16))
- mask_bits = '{0:032b}'.format(int(hex_mask,16))
- value_bits = '{0:032b}'.format(int(value,16))
- new_bits = list(prev_bits)
-
- for i, bit in enumerate(mask_bits):
- if bit == '1':
- new_bits[i] = value_bits[i]
-
- value = hex(int("".join(new_bits),2))
- cmd = ['pci', '-w', reg, value]
- cmd.extend(opts)
- safe_call(cmd)
- log.debug('Written %str to register %s'%(value, reg))
-
- def read_pci(amount=1, reg='0x9000', opts=[], decimal=False):
- cmd = ['pci', '-r', reg, "-s%i"%amount]
- cmd.extend(opts)
- output = safe_call(cmd).split("\n")
- lines = []
- for line in output:
- if line and line != "\n":
- lines.append(line.split(": ")[1])
- formated = []
- for line in lines:
- if line and line != "\n":
- formated += re.findall(r'[\da-fA-F]{8}', line)
- if decimal:
- return [int(x,16) for x in formated]
- else:
- return formated
-
- def get_dec_from_bits(bits, msb=-1, lsb=-1):
- rtrnValue = 0
-
- if (msb < 0 or lsb < 0):
- rtrnValue = int(bits)
- elif (msb < lsb) or (msb > 31) or (lsb > 31):
- log.info("Bit range for msb and lsb of get_dec_from_bits was wrong. Not truncating")
- rtrnValue = int(bits, 2)
- else:
- chunk = ('{0:032b}'.format(int(bits, 2)))[31-msb:32-lsb]
- rtrnValue = int(chunk, 2)
-
- return rtrnValue
- def start_dma(dma='dma0r'):
- log.info('Start DMA')
- cmd = ['pci', '--start-dma', dma]
- safe_call(cmd)
- time.sleep(0.05)
-
- def stop_dma(dma='dma0r'):
- log.info('Stop DMA')
- cmd = ['pci', '--stop-dma', dma]
- safe_call(cmd)
- time.sleep(0.05)
- def data_reset():
- log.info('Data reset')
- write_pci('000003f5', hex_mask='7')
- time.sleep(0.05)
- write_pci('000003f0', hex_mask='7')
- time.sleep(0.05)
- def flush_dma(dma='dma0'):
- log.info('Flushing DMA Pipeline')
- write_pci('03f0', hex_mask='CF0')
- cmd = ['pci', '-r', dma, '-o', '/dev/null', '--multipacket']
- safe_call(cmd)
- def is_active():
- control = read_pci(1, '0x9040')[0]
- control_bits = '{0:032b}'.format(int(control, 16))
- return control_bits[22:26] == "1111"
- def start_pilot_bunch_emulator():
- log.info('Start pilot bunch emulator')
- write_pci('400003f0', hex_mask='400003F0')
- time.sleep(0.05)
- write_pci('03f0', hex_mask='CF0')
- def start_acquisition():
- log.info('Start acquisition')
- write_pci('1', '4', hex_mask='1')
- time.sleep(0.05)
- write_pci('00bf0', hex_mask='CF0')
- def stop_acquisition():
- log.info('Stop acquisition')
- write_pci('003f0', hex_mask='CF0')
- time.sleep(0.05)
- def enable_transfer():
- log.info('Enable data transfer')
- write_pci('007f0', hex_mask='CF0')
- time.sleep(0.05)
- def write_data(filename, dma='dma0'):
- log.info('Write data to {}'.format(filename))
- cmd = ['pci', '-r', dma, '-o', filename, '--multipacket']
- safe_call(cmd)
- write_pci('003f0', hex_mask='CF0')
- def run_status(script_path):
- cmd = ['bash', script_path]
- output = safe_call(cmd)
- log.info(output)
- def acquire_data(filename, duration=1.0, simulate=False):
- #start_dma() #Dont start dma when it is already active! (Causes data corruption in the driver)
- if simulate:
- start_pilot_bunch_emulator()
- start_acquisition()
- #time.sleep(duraition) #calculate the time instead
- wait_for_revolutions()
- stop_acquisition()
- enable_transfer()
- write_data(filename)
- flush_dma()
- def wait_for_revolutions():
- n = read_pci(1, '0x9020', decimal=True)[0] # Get the ammount of orbits to observe
- spin_time_ns = 368 * n
- wait_time_s = spin_time_ns / 1000.0 / 1000.0 / 1000.0 # Milli, Micro, Nano
- time.sleep(wait_time_s * 1.4) # 40% Safety margin
|