communication.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. """
  2. Communication part of the board package
  3. """
  4. import subprocess
  5. import re
  6. import logging as log
  7. import time
  8. from errors import *
  9. from ...kcgwidget import error
  10. class PCI(object):
  11. """
  12. The Interface to the pci command
  13. """
  14. def __init__(self):
  15. pass
  16. def _safe_call(self, cmd):
  17. """
  18. Actually call the pci command
  19. :param cmd: the command line to execute as list
  20. :return: the output of the command
  21. """
  22. log.debug(cmd)
  23. try:
  24. # if '-r' in cmd:
  25. return subprocess.check_output(cmd)
  26. # else:
  27. # subprocess.Popen(cmd, shell=False)
  28. except subprocess.CalledProcessError as e:
  29. raise BoardError(e.output)
  30. except OSError as e:
  31. if str(e) == "[Errno 2] No such file or directory":
  32. error(0x003, "Pci command not found. Exiting.")
  33. raise InterfaceNotFoundError("pci command was not found in your searchpath")
  34. else:
  35. raise BoardError('{}: {}'.format(' '.join(cmd), str(e)))
  36. def _format(self, output):
  37. """
  38. Format output values (remove unnecessary parts of the output)
  39. :param output: the unformatted output
  40. :return: the formatted output
  41. """
  42. output = output.split("\n")
  43. lines = []
  44. for line in output:
  45. if line and line != "\n":
  46. lines.append(line.split(": ")[1])
  47. formatted = []
  48. for line in lines:
  49. if line and line != "\n":
  50. formatted += re.findall(r'[\da-fA-F]{8}', line)
  51. return formatted
  52. def read(self, board_id, amount=1, reg=None, dma=None, destination=None, decimal=False, timeout=None):
  53. """
  54. Read from boards
  55. :param board_id: id of the board to write to (mandatory)
  56. :param amount: number of 32byte blocks to read (default is 1) [1]
  57. :param reg: register to read from [1]
  58. :param dma: the dma to read from [1]
  59. :param destination: the destination to write the retrieved data to ('memory' to return the data)
  60. :param decimal: whether to return the result as decimal number (default is False)
  61. :param timeout: the timeout for the read (only useful when reading data from dma)
  62. :return:
  63. [1]: If neither reg nor dma are given reg will default to 0x9000 and data from registers will be read
  64. If reg is given, data from registers will be read
  65. If dma is given, data from dma will be read and amount is ignored
  66. If both reg and dma are given, an error will be raised
  67. """
  68. # either reg or dma have to be None
  69. assert reg is None or dma is None, "read was called with reg and dma arguments."
  70. if not reg and not dma:
  71. reg = '0x9000'
  72. source = reg if reg else dma
  73. cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-r', source]
  74. dst = '/dev/stdout' if destination == 'memory' else destination
  75. if reg:
  76. cmd_extend = ["-s%i" % amount]
  77. else:
  78. cmd_extend = ['--multipacket', '-o', dst]
  79. if timeout:
  80. cmd_extend.extend(['--timeout', str(timeout)])
  81. log.vinfo('Write data to {}'.format(dst))
  82. cmd.extend(cmd_extend)
  83. output = self._safe_call(cmd)
  84. if reg:
  85. formatted = self._format(output)
  86. if decimal:
  87. if dst:
  88. with open(dst, 'r+') as f: # to be consistent with use of destination
  89. f.write(str([int(x, 16) for x in formatted]))
  90. return
  91. else:
  92. return [int(x, 16) for x in formatted]
  93. else:
  94. if dst:
  95. with open(dst, 'r+') as f: # to be consistent with use of destination
  96. f.write(str(formatted))
  97. return
  98. return formatted
  99. else:
  100. if dst == '/dev/stdout':
  101. return output.split('Writting')[0]
  102. def write(self, board_id, value, reg='0x9040', hex_mask='FFFFFFFF'):
  103. """
  104. Write to boards
  105. :param board_id: id of the board to write to (mandatory)
  106. :param value: value to write (mandatory)
  107. :param reg: register to write to (optional, default is '0x9040')
  108. :param hex_mask: hex mask to apply to value before writing (optional)
  109. :return:
  110. """
  111. assert len(hex_mask) <= 8, "Hex Mask has more than 32 bit."
  112. if hex_mask != 'FFFFFFFF':
  113. prev_val = self.read(board_id, 1, reg)[0]
  114. prev_bits = '{0:032b}'.format(int(prev_val, 16))
  115. mask_bits = '{0:032b}'.format(int(hex_mask, 16))
  116. value_bits = '{0:032b}'.format(int(value, 16))
  117. new_bits = list(prev_bits)
  118. for i, bit in enumerate(mask_bits):
  119. if bit == '1':
  120. new_bits[i] = value_bits[i]
  121. value = hex(int("".join(new_bits), 2))
  122. cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-w', reg, value]
  123. self._safe_call(cmd)
  124. log.debug('Written %s to register %s' % (value, reg))
  125. def read_data_to_file(self, board_id, filename, dma='dma0', timeout=None):
  126. """
  127. Read data from board and write to file
  128. :param board_id: the board to read from
  129. :param filename: the filename to write to
  130. :param dma: the dma to use?
  131. :param timeout: if not None: the timeout for the underlying pci command
  132. :return:
  133. """
  134. return self.read(board_id, dma=dma, destination=filename, timeout=timeout)
  135. def read_data_to_variable(self, board_id, dma='dma0', timeout=None):
  136. """
  137. Read data and return it.
  138. :param board_id: the board to read from
  139. :param dma: the dma to use?
  140. :param timeout: if not None: the timeout for the underlying pci command
  141. :return: string with data read from board
  142. """
  143. return self.read(board_id, dma=dma, timeout=timeout, destination='memory')
  144. def start_dma(self, board_id, dma='dma0r'):
  145. """
  146. Start dma engine.
  147. :param board_id: the board to start the dma engine for
  148. :param dma: the dma to use
  149. :return:
  150. """
  151. log.vinfo('Start DMA')
  152. cmd = ['pci', '-d', available_boards.get_device_file(board_id), '--start-dma', dma]
  153. self._safe_call(cmd)
  154. time.sleep(0.05)
  155. def stop_dma(self, board_id, dma='dma0r'):
  156. """
  157. Stop dma engine.
  158. :param board_id: the board to stop the dma engine for
  159. :param dma: the dma to use
  160. :return:
  161. """
  162. log.vinfo('Stop DMA')
  163. cmd = ['pci', '-d', available_boards.get_device_file(board_id), '--stop-dma', dma]
  164. self._safe_call(cmd)
  165. time.sleep(0.05)
  166. def info(self, board_id=None, dev_file=None):
  167. """
  168. Get Device info (output of pci -i)
  169. :return: Information string returned by pci -i
  170. """
  171. assert board_id != dev_file, "info got both board_id and dev_file or got none of both"
  172. if board_id is not None:
  173. cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-i']
  174. else:
  175. cmd = ['pci', '-d', dev_file, '-i']
  176. return self._safe_call(cmd)
  177. pci = PCI()
  178. from boards_connected import available_boards # this import has to be here as boards_connected imports pci