communication.py 7.1 KB

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