board.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import re
  2. import time
  3. import logging
  4. import subprocess
  5. import ConfigParser
  6. import numpy as np
  7. log = logging.getLogger(__name__)
  8. class BoardError(Exception):
  9. pass
  10. class ObserverError(Exception):
  11. pass
  12. class BoardConfiguration():
  13. def __init__(self, config_file=None, logger=None):
  14. self._config = {}
  15. self._observers = {}
  16. self.logger = logger
  17. self._set_defaults()
  18. if config_file:
  19. self.load_config(config_file)
  20. def _set_defaults(self):
  21. c = self._config
  22. c['fpga_delay_max'] = 15
  23. c['fpga_delay'] = 0
  24. c['fpga_delay_factor'] = 150
  25. c['chip_delay_max'] = 31
  26. c['chip_1_delay'] = 4
  27. c['chip_2_delay'] = 4
  28. c['chip_3_delay'] = 4
  29. c['chip_4_delay'] = 4
  30. c['chip_delay_factor'] = 3
  31. c['th_delay_max'] = 15
  32. c['th_delay'] = 3
  33. c['th_delay_factor'] = 150
  34. c['adc_delay_max'] = 15
  35. c['adc_1_delay'] = 4
  36. c['adc_2_delay'] = 4
  37. c['adc_3_delay'] = 4
  38. c['adc_4_delay'] = 4
  39. c['adc_delay_factor'] = 150
  40. c['th_to_adc_cycles'] = 7
  41. c['adc_1_delay_individual'] = -1 # -1 = 'ADC 1 delay is the same as th_to_adc_cycles'
  42. # 0 .. 16 = 'Use this offset instead'
  43. c['orbits_observe'] = 100
  44. c['orbits_skip'] = 2
  45. def load_config(self, filename):
  46. if filename:
  47. config = ConfigParser.RawConfigParser()
  48. if not config.read(str(filename)):
  49. return
  50. for key in self._config.keys():
  51. try:
  52. self._config[key] = config.getint('Config', key)
  53. if self.logger:
  54. self.logger.info("Read '%s' for '%s' from '%s'"%(str(self._config[key]), key, str(filename)))
  55. except ConfigParser.NoOptionError as e:
  56. pass
  57. except ConfigParser.NoSectionError as e:
  58. pass
  59. def save_config(self, filename):
  60. if filename:
  61. with open(str(filename), 'w') as f:
  62. cp = ConfigParser.RawConfigParser()
  63. cp.add_section('Config')
  64. for key in self._config.keys():
  65. cp.set('Config', key, self._config[key])
  66. f.write('#\n'
  67. '# HEB (Hot Electron Bolometer) Configuration file\n'
  68. '#\n'
  69. '# (c) Karlsruhe Institute of Technology, 2014\n'
  70. '# All rights reserved.\n'
  71. '#\n'
  72. '# Applicable Firmware Version(s):\n'
  73. '#\n\n')
  74. cp.write(f)
  75. def get(self, key):
  76. return self._config.get(key, None)
  77. def update(self, key, value):
  78. self._config[key] = value
  79. self._notify_observers(key, value)
  80. def get(self, key):
  81. return self._config.get(key, None)
  82. def update(self, key, value):
  83. self._config[key] = value
  84. self._notify_observers(key, value)
  85. def observe(self, who, callback, key):
  86. if key not in self._config.keys():
  87. raise ObserverError(str("Key '%s' is unknown."%key))
  88. if self._observers.get(key, None) is None:
  89. self._observers[key] = []
  90. self._observers[key].append([who, callback])
  91. def unobserve(self, who, key=None):
  92. if key is not None:
  93. observers = np.array(self._observers.get(key, None))
  94. if observers is None:
  95. return
  96. if who not in observers[:, 0]:
  97. return
  98. for i, _obs in enumerate(self._observers[key]):
  99. if _obs[0] is who:
  100. del self._observers[key][i]
  101. if not self._observers[key]:
  102. del self._observers[key]
  103. return
  104. for _key in self._observers.keys():
  105. for i, _obs in enumerate(self._observers[_key]):
  106. if _obs[0] is who:
  107. del self._observers[_key][i]
  108. if not self._observers[_key]:
  109. del self._observers[_key]
  110. def _notify_observers(self, key, value):
  111. observers = self._observers.get(key, None)
  112. if observers is None:
  113. return
  114. for (who, callback) in observers:
  115. callback(value)
  116. def make_uint(self, value, maximum, name=None):
  117. if value is None:
  118. raise ValueError(str("%s Value is invalid (None)" % name))
  119. val = None
  120. try:
  121. val = int(value)
  122. except:
  123. raise ValueError(str("%s Value is not a valid number" % name))
  124. if maximum is not None:
  125. if val > maximum:
  126. raise ValueError(str("%s Value is too large (>%i)" % (name, maximum)))
  127. if val < 0:
  128. raise ValueError(str("%s Values below 0 are not allowed" % name))
  129. return val
  130. def set_fpga_delay(self, value):
  131. time_factor = self.make_uint(value, self.get('fpga_delay_max'), 'FPGA_Delay')
  132. reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "0"
  133. write_pci(reg_value, '0x9060')
  134. if self.logger:
  135. 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')))
  136. self.update('fpga_delay', value)
  137. def set_chip_delay(self, adcs, values):
  138. if not adcs or not values:
  139. if self.logger:
  140. self.logger.info("Nothing to do for chip delay.")
  141. return
  142. _adcs = []
  143. for adc in adcs:
  144. _adcs.append(self.make_uint(adc, 3, 'ADC_'))
  145. _values = []
  146. for value in values:
  147. _values.append(self.make_uint(value, self.get('chip_delay_max'), 'ADC_Value'))
  148. a_v = zip(_adcs, _values)
  149. factors = [None, None, None, None]
  150. for (adc, value) in a_v:
  151. factors[adc] = value
  152. reg_value = ''
  153. mask = ''
  154. # Chip Delays are stored as 'ADC_4 ADC_3 ADC_2 ADC_1' in the register.
  155. # Therefore, we need to traverse the factors array in reverse order
  156. for value in reversed(factors):
  157. if value is not None:
  158. reg_value += '{0:02x}'.format(value)
  159. mask += 'ff'
  160. else:
  161. reg_value += '00'
  162. mask += '00'
  163. write_pci(reg_value, '0x9080', hex_mask=mask)
  164. if self.logger:
  165. s = "Setting ADC Delays:"
  166. for (adc, value) in a_v:
  167. s += ' ADC_%i Fine Delay: %i,' % (adc, value)
  168. s = s[:-1] # cut away the last dangling ','
  169. self.logger.info(s)
  170. for (adc, value) in a_v:
  171. s = 'chip_%i_delay'%(adc+1)
  172. self.update(s, value)
  173. def set_th_delay(self, value):
  174. time_factor = self.make_uint(value, self.get('th_delay_max'), 'TH_Delay')
  175. reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "3"
  176. write_pci(reg_value, '0x9060')
  177. if self.logger:
  178. 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')))
  179. self.update('th_delay', value)
  180. def set_adc_delay(self, adc, value):
  181. if adc is None or value is None:
  182. if self.logger:
  183. self.logger.info("Nothing to do for ADC delay.")
  184. return
  185. _adc = self.make_uint(adc, 3, 'ADC Number')
  186. _val = self.make_uint(value, self.get('adc_delay_max'), 'ADC Delay')
  187. reg_value = "0x000501" + '{0:01x}'.format(_val) + "%i" % (_adc+4)
  188. write_pci(reg_value, '0x9060')
  189. if self.logger:
  190. s = "Setting ADC_%i delay %i * %i --> %i picoseconds" % ((_adc+1), _val, self.get('adc_delay_factor'), _val*self.get('adc_delay_factor'))
  191. self.logger.info(s)
  192. adc_s = 'adc_%i_delay'%(_adc+1)
  193. self.update(adc_s, _val)
  194. def set_delay(self, n):
  195. def write_delay(value, channel):
  196. cmd = '00501' + '%01x' % value + str(channel)
  197. write_pci(cmd, reg='0x9060')
  198. time.sleep(0.005)
  199. write_delay(n, 3)
  200. self.update('th_delay', n)
  201. delay = n + self.get('th_to_adc_cycles')
  202. if delay > self.get('adc_delay_max'):
  203. delay -= self.get('adc_delay_max') + 1
  204. write_delay(delay, 5)
  205. self.update('adc_2_delay', delay)
  206. write_delay(delay, 6)
  207. self.update('adc_3_delay', delay)
  208. write_delay(delay, 7)
  209. self.update('adc_4_delay', delay)
  210. #ADC 1 might have an individual delay
  211. try:
  212. delay = n + self.make_uint(self.get('adc_1_delay_individual'), 16, 'ADC 1 individual delay')
  213. except ValueError:
  214. if self.logger:
  215. self.logger.info(r"'adc_1_delay_individual' not set or inactive. Using default.")
  216. if delay > self.get('adc_delay_max'):
  217. delay -= self.get('adc_delay_max') + 1
  218. write_delay(delay, 4)
  219. self.update('adc_1_delay', delay)
  220. def safe_call(cmd):
  221. try:
  222. return subprocess.check_output(cmd)
  223. except subprocess.CalledProcessError as e:
  224. raise BoardError(e.output)
  225. except OSError as e:
  226. raise BoardError('{}: {}'.format(' '.join(cmd), str(e)))
  227. def write_pci(value, reg='0x9040', opts=[], hex_mask='FFFFFFFF'):
  228. if hex_mask != 'FFFFFFFF':
  229. if len(hex_mask) > 8:
  230. hex_mask = hex_mask[-8:]
  231. prev_val = read_pci(1, reg)[0]
  232. prev_bits = '{0:032b}'.format(int(prev_val,16))
  233. mask_bits = '{0:032b}'.format(int(hex_mask,16))
  234. value_bits = '{0:032b}'.format(int(value,16))
  235. new_bits = list(prev_bits)
  236. for i, bit in enumerate(mask_bits):
  237. if bit == '1':
  238. new_bits[i] = value_bits[i]
  239. value = hex(int("".join(new_bits),2))
  240. cmd = ['pci', '-w', reg, value]
  241. cmd.extend(opts)
  242. safe_call(cmd)
  243. log.debug('Written %str to register %s'%(value, reg))
  244. def read_pci(amount=1, reg='0x9000', opts=[], decimal=False):
  245. cmd = ['pci', '-r', reg, "-s%i"%amount]
  246. cmd.extend(opts)
  247. output = safe_call(cmd).split("\n")
  248. lines = []
  249. for line in output:
  250. if line and line != "\n":
  251. lines.append(line.split(": ")[1])
  252. formated = []
  253. for line in lines:
  254. if line and line != "\n":
  255. formated += re.findall(r'[\da-fA-F]{8}', line)
  256. if decimal:
  257. return [int(x,16) for x in formated]
  258. else:
  259. return formated
  260. def get_dec_from_bits(bits, msb=-1, lsb=-1):
  261. rtrnValue = 0
  262. if (msb < 0 or lsb < 0):
  263. rtrnValue = int(bits)
  264. elif (msb < lsb) or (msb > 31) or (lsb > 31):
  265. log.info("Bit range for msb and lsb of get_dec_from_bits was wrong. Not truncating")
  266. rtrnValue = int(bits, 2)
  267. else:
  268. chunk = ('{0:032b}'.format(int(bits, 2)))[31-msb:32-lsb]
  269. rtrnValue = int(chunk, 2)
  270. return rtrnValue
  271. def start_dma(dma='dma0r'):
  272. log.info('Start DMA')
  273. cmd = ['pci', '--start-dma', dma]
  274. safe_call(cmd)
  275. time.sleep(0.05)
  276. def stop_dma(dma='dma0r'):
  277. log.info('Stop DMA')
  278. cmd = ['pci', '--stop-dma', dma]
  279. safe_call(cmd)
  280. time.sleep(0.05)
  281. def data_reset():
  282. log.info('Data reset')
  283. write_pci('000003f5', hex_mask='7')
  284. time.sleep(0.05)
  285. write_pci('000003f0', hex_mask='7')
  286. time.sleep(0.05)
  287. def flush_dma(dma='dma0'):
  288. log.info('Flushing DMA Pipeline')
  289. write_pci('03f0', hex_mask='CF0')
  290. cmd = ['pci', '-r', dma, '-o', '/dev/null', '--multipacket']
  291. safe_call(cmd)
  292. def is_active():
  293. control = read_pci(1, '0x9040')[0]
  294. control_bits = '{0:032b}'.format(int(control, 16))
  295. return control_bits[22:26] == "1111"
  296. def start_pilot_bunch_emulator():
  297. log.info('Start pilot bunch emulator')
  298. write_pci('400003f0', hex_mask='400003F0')
  299. time.sleep(0.05)
  300. write_pci('03f0', hex_mask='CF0')
  301. def start_acquisition():
  302. log.info('Start acquisition')
  303. write_pci('1', '4', hex_mask='1')
  304. time.sleep(0.05)
  305. write_pci('00bf0', hex_mask='CF0')
  306. def stop_acquisition():
  307. log.info('Stop acquisition')
  308. write_pci('003f0', hex_mask='CF0')
  309. time.sleep(0.05)
  310. def enable_transfer():
  311. log.info('Enable data transfer')
  312. write_pci('007f0', hex_mask='CF0')
  313. time.sleep(0.05)
  314. def write_data(filename, dma='dma0'):
  315. log.info('Write data to {}'.format(filename))
  316. cmd = ['pci', '-r', dma, '-o', filename, '--multipacket']
  317. safe_call(cmd)
  318. write_pci('003f0', hex_mask='CF0')
  319. def run_status(script_path):
  320. cmd = ['bash', script_path]
  321. output = safe_call(cmd)
  322. log.info(output)
  323. def acquire_data(filename, duration=1.0, simulate=False):
  324. #start_dma() #Dont start dma when it is already active! (Causes data corruption in the driver)
  325. if simulate:
  326. start_pilot_bunch_emulator()
  327. start_acquisition()
  328. #time.sleep(duraition) #calculate the time instead
  329. wait_for_revolutions()
  330. stop_acquisition()
  331. enable_transfer()
  332. write_data(filename)
  333. flush_dma()
  334. def wait_for_revolutions():
  335. n = read_pci(1, '0x9020', decimal=True)[0] # Get the ammount of orbits to observe
  336. spin_time_ns = 368 * n
  337. wait_time_s = spin_time_ns / 1000.0 / 1000.0 / 1000.0 # Milli, Micro, Nano
  338. time.sleep(wait_time_s * 1.4) # 40% Safety margin