ftdi_swd.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from array import array
  15. from pyftdi.ftdi import Ftdi
  16. import usb.util
  17. class FTDISerialWireDebug(object):
  18. def __init__(self, vid, pid, interface, direction, output_mask, reset_mask, frequency):
  19. self._direction = direction
  20. self._output_mask = output_mask
  21. self._reset_mask = reset_mask
  22. self._ftdi = Ftdi()
  23. try:
  24. self._ftdi.open_mpsse(vid, pid, interface, direction=direction, frequency=frequency,
  25. latency=1)
  26. except:
  27. self._ftdi = None
  28. raise
  29. # get the FTDI FIFO size and increase the chuncksize to match
  30. self._ftdi_fifo_size = min(self._ftdi.fifo_sizes)
  31. self._ftdi.write_data_set_chunksize(self._ftdi_fifo_size)
  32. self._cmd_buffer = array('B')
  33. self._output_enabled = False
  34. self._pending_acks = 0
  35. self._sequence_cmd_buffer = None
  36. def close(self):
  37. if not self._ftdi:
  38. return
  39. self.send_cmds()
  40. self._ftdi.close()
  41. # PyFTDI doesn't do a good job of cleaning up - make sure we release the usb device
  42. usb.util.dispose_resources(self._ftdi.usb_dev)
  43. self._ftdi = None
  44. def _fatal(self, message):
  45. raise Exception('FATAL ERROR: {}'.format(message))
  46. def _queue_cmd(self, write_data):
  47. if len(write_data) > self._ftdi_fifo_size:
  48. raise Exception('Data too big!')
  49. if self._sequence_cmd_buffer is not None:
  50. self._sequence_cmd_buffer.extend(write_data)
  51. else:
  52. if len(self._cmd_buffer) + len(write_data) > self._ftdi_fifo_size:
  53. self.send_cmds()
  54. self._cmd_buffer.extend(write_data)
  55. def _set_output_enabled(self, enabled):
  56. if enabled == self._output_enabled:
  57. return
  58. self._output_enabled = enabled
  59. direction = self._direction & ~(0x00 if enabled else self._output_mask)
  60. self._queue_cmd([Ftdi.SET_BITS_LOW, 0, direction])
  61. def reset(self):
  62. # toggle the reset line
  63. self.reset_lo()
  64. self.reset_hi()
  65. def reset_lo(self):
  66. direction = self._direction & ~(0x00 if self._output_enabled else self._output_mask)
  67. self._queue_cmd([Ftdi.SET_BITS_LOW, 0, direction | self._reset_mask])
  68. self.send_cmds()
  69. def reset_hi(self):
  70. direction = self._direction & ~(0x00 if self._output_enabled else self._output_mask)
  71. self._queue_cmd([Ftdi.SET_BITS_LOW, 0, direction & ~self._reset_mask])
  72. self.send_cmds()
  73. def send_cmds(self):
  74. if self._sequence_cmd_buffer is not None:
  75. self._ftdi.write_data(self._sequence_cmd_buffer)
  76. elif len(self._cmd_buffer) > 0:
  77. self._ftdi.write_data(self._cmd_buffer)
  78. self._cmd_buffer = array('B')
  79. def write_bits_cmd(self, data, num_bits):
  80. if num_bits < 0 or num_bits > 8:
  81. self._fatal('Invalid num_bits')
  82. elif (data & ((1 << num_bits) - 1)) != data:
  83. self._fatal('Invalid data!')
  84. self._set_output_enabled(True)
  85. self._queue_cmd([Ftdi.WRITE_BITS_NVE_LSB, num_bits - 1, data])
  86. def write_bytes_cmd(self, data):
  87. length = len(data) - 1
  88. if length < 0 or length > 0xffff:
  89. self._fatal('Invalid length')
  90. self._set_output_enabled(True)
  91. self._queue_cmd([Ftdi.WRITE_BYTES_NVE_LSB, length & 0xff, length >> 8] + data)
  92. def read_bits_cmd(self, num_bits):
  93. if num_bits < 0 or num_bits > 8:
  94. self._fatal('Invalid num_bits')
  95. self._set_output_enabled(False)
  96. self._queue_cmd([Ftdi.READ_BITS_PVE_LSB, num_bits - 1])
  97. def read_bytes_cmd(self, length):
  98. length -= 1
  99. if length < 0 or length > 0xffff:
  100. self._fatal('Invalid length')
  101. self._set_output_enabled(False)
  102. self._queue_cmd([Ftdi.READ_BYTES_PVE_LSB, length & 0xff, length >> 8])
  103. def get_read_bytes(self, length):
  104. return self._ftdi.read_data_bytes(length)
  105. def get_read_fifo_size(self):
  106. return self._ftdi_fifo_size
  107. def start_sequence(self):
  108. if self._sequence_cmd_buffer is not None:
  109. self._fatal('Attempted to start a sequence while one is in progress')
  110. self.send_cmds()
  111. self._sequence_cmd_buffer = array('B')
  112. def end_sequence(self):
  113. if self._sequence_cmd_buffer is None:
  114. self._fatal('No sequence started')
  115. self._sequence_cmd_buffer = None