|
@@ -0,0 +1,360 @@
|
|
|
+import time
|
|
|
+from ctypes import (cdll, byref, c_int, c_uint8, c_char_p, c_void_p, c_uint16, cast,
|
|
|
+ create_string_buffer, Structure, pointer, POINTER, memmove)
|
|
|
+
|
|
|
+from JTAG import JTAG
|
|
|
+from pylibftdi.device import Device
|
|
|
+from pylibftdi._base import FtdiError
|
|
|
+from pylibftdi.driver import ftdi_device_list
|
|
|
+
|
|
|
+
|
|
|
+class DataLine:
|
|
|
+ """DataLine class supplies the constants that define which data line (DataLine) has which functionality"""
|
|
|
+ TCK = 0x01
|
|
|
+ TMS = 0x02
|
|
|
+ NCE = 0x04
|
|
|
+ NCS = 0x08
|
|
|
+ TDI = 0x10
|
|
|
+ LED = 0x20
|
|
|
+ READ = 0x40
|
|
|
+ SHMODE = 0x80
|
|
|
+ READ_TDO = 0x01
|
|
|
+
|
|
|
+
|
|
|
+# To access libftdi functions in Python use:
|
|
|
+# Device.fdll.ftdi_xxx(1, 2, 3)
|
|
|
+
|
|
|
+# Comment from OpenOCD usb_blaster.c:
|
|
|
+# Actually, the USB-Blaster offers a byte-shift mode to transmit up to 504 data
|
|
|
+# bits (bidirectional) in a single USB packet. A header byte has to be sent as
|
|
|
+# the first byte in a packet with the following meaning:
|
|
|
+#
|
|
|
+# Bit 7 (0x80): Must be set to indicate byte-shift mode.
|
|
|
+# Bit 6 (0x40): If set, the USB-Blaster will also read data, not just write.
|
|
|
+# Bit 5..0: Define the number N of following bytes
|
|
|
+#
|
|
|
+# All N following bytes will then be clocked out serially on TDI. If Bit 6 was
|
|
|
+# set, it will afterwards return N bytes with TDO data read while clocking out
|
|
|
+# the TDI data. LSB of the first byte after the header byte will appear first
|
|
|
+# on TDI.
|
|
|
+#
|
|
|
+#
|
|
|
+# Simple bit banging mode:
|
|
|
+#
|
|
|
+# Bit 7 (0x80): Must be zero (see byte-shift mode above)
|
|
|
+# Bit 6 (0x40): If set, you will receive a byte indicating the state of TDO
|
|
|
+# in return.
|
|
|
+# Bit 5 (0x20): Output Enable/LED.
|
|
|
+# Bit 4 (0x10): TDI Output.
|
|
|
+# Bit 3 (0x08): nCS Output (not used in JTAG mode).
|
|
|
+# Bit 2 (0x04): nCE Output (not used in JTAG mode).
|
|
|
+# Bit 1 (0x02): TMS Output.
|
|
|
+# Bit 0 (0x01): TCK Output.
|
|
|
+#
|
|
|
+# For transmitting a single data bit, you need to write two bytes (one for
|
|
|
+# setting up TDI/TMS/TCK=0, and one to trigger TCK high with same TDI/TMS
|
|
|
+# held). Up to 64 bytes can be combined in a single USB packet.
|
|
|
+# It isn't possible to read a data without transmitting data.
|
|
|
+
|
|
|
+class Status:
|
|
|
+ """Status class supplies the constants that define which state (Status) has which meaning"""
|
|
|
+ nFault = 0x08
|
|
|
+ Select = 0x10
|
|
|
+ PError = 0x20
|
|
|
+ nAck = 0x40
|
|
|
+
|
|
|
+
|
|
|
+class USBBlaster(JTAG, Device):
|
|
|
+ """This class is used to communicate with the USB Blaster over the USB port."""
|
|
|
+
|
|
|
+ def __init__(self, debug=False, vid=0x09FB, pid=0x6001, IRbefore=0, IRafter=0, DRbefore=0, DRafter=0,
|
|
|
+ skipReset=False):
|
|
|
+ # We pass lazy_open=True - meaning, the constructor of Device will not
|
|
|
+ # try to open the device - we will do it ourselves
|
|
|
+ self.debug = debug
|
|
|
+
|
|
|
+ Device.__init__(self, lazy_open=True)
|
|
|
+
|
|
|
+ self.ctx = create_string_buffer(1024)
|
|
|
+ ret_val = self.fdll.ftdi_init(byref(self.ctx))
|
|
|
+ if ret_val != 0:
|
|
|
+ msg = "%s (%d)" % (self.get_error_string(), ret_val)
|
|
|
+ del self.ctx
|
|
|
+ raise FtdiError(msg)
|
|
|
+
|
|
|
+ self.__open_device(vid, pid)
|
|
|
+
|
|
|
+ self.vid = vid
|
|
|
+ self.pid = pid
|
|
|
+
|
|
|
+ # Reset the device
|
|
|
+ self.fdll.ftdi_usb_reset(byref(self.ctx))
|
|
|
+
|
|
|
+ if self.fdll.ftdi_set_latency_timer(byref(self.ctx), 2) < 0:
|
|
|
+ pass #print("Unable to set latency timer")
|
|
|
+
|
|
|
+ latency_timer = c_uint8()
|
|
|
+ if self.fdll.ftdi_get_latency_timer(byref(self.ctx),
|
|
|
+ byref(latency_timer)) < 0:
|
|
|
+ pass #print("Unable to get latency timer")
|
|
|
+
|
|
|
+ self.fdll.ftdi_disable_bitbang(byref(self.ctx))
|
|
|
+ # Init the JTAG
|
|
|
+ self.dataLine = 0x00
|
|
|
+
|
|
|
+ # Call JTAG constructor only after USB connection is set-up (it calls
|
|
|
+ # reset so the device must already be setup
|
|
|
+
|
|
|
+ JTAG.__init__(self, IRbefore, IRafter, DRbefore, DRafter, skipReset)
|
|
|
+
|
|
|
+ def __exit__(self):
|
|
|
+ self.__del__()
|
|
|
+
|
|
|
+ def __del__(self):
|
|
|
+ # self.ctx is cleared in the parent class destructor
|
|
|
+ Device.__del__(self)
|
|
|
+
|
|
|
+ def __open_device(self, vid, pid):
|
|
|
+ # Search for all devices with VID & PID - try to open all of them, if
|
|
|
+ # it is opened correctly first one is our device - if not it means
|
|
|
+ # we have also Altera USB Blaster connected (same VID & PID), but with
|
|
|
+ # original Altera's driver - we want to keep that device for Altera tools
|
|
|
+ devlistptrtype = POINTER(ftdi_device_list)
|
|
|
+ dev_list_ptr = devlistptrtype()
|
|
|
+ ret_val = self.fdll.ftdi_usb_find_all(byref(self.ctx),
|
|
|
+ byref(dev_list_ptr),
|
|
|
+ vid, pid)
|
|
|
+ if ret_val < 0:
|
|
|
+ msg = "%s (%d)" % (self.get_error_string(), ret_val)
|
|
|
+ self.fdll.ftdi_deinit(byref(self.ctx))
|
|
|
+ del self.ctx
|
|
|
+ raise FtdiError(msg)
|
|
|
+ elif ret_val == 0:
|
|
|
+ msg = "No devices found for VID=0x%04x, PID=0x%04x" % (vid, pid)
|
|
|
+ self.fdll.ftdi_deinit(byref(self.ctx))
|
|
|
+ del self.ctx
|
|
|
+ raise FtdiError(msg)
|
|
|
+ else:
|
|
|
+ pass #print("Number of devices found: %d (VID & PID: 0x%04x,0x%04x)" % (
|
|
|
+ #ret_val, vid, pid))
|
|
|
+ manuf = create_string_buffer(128)
|
|
|
+ desc = create_string_buffer(128)
|
|
|
+ serial = create_string_buffer(128)
|
|
|
+
|
|
|
+ while dev_list_ptr:
|
|
|
+ ret_val = self.fdll.ftdi_usb_get_strings(byref(self.ctx),
|
|
|
+ dev_list_ptr.contents.dev,
|
|
|
+ manuf, 127, desc, 127, serial, 127)
|
|
|
+ if ret_val < 0:
|
|
|
+ # step to next in linked-list
|
|
|
+ dev_list_ptr = cast(dev_list_ptr.contents.next,
|
|
|
+ devlistptrtype)
|
|
|
+ continue
|
|
|
+
|
|
|
+ ret_val = self.fdll.ftdi_usb_open_dev(byref(self.ctx),
|
|
|
+ dev_list_ptr.contents.dev)
|
|
|
+ if ret_val == 0:
|
|
|
+ pass #print("Opened device: %s, %s, Serial: %s" % (
|
|
|
+ #manuf.value, desc.value, serial.value))
|
|
|
+ self._opened = True
|
|
|
+ break
|
|
|
+
|
|
|
+ pass #print("Failed to open device (will try others): %s (%d)" % (
|
|
|
+ #self.get_error_string(), ret_val))
|
|
|
+ # step to next in linked-list
|
|
|
+ dev_list_ptr = cast(dev_list_ptr.contents.next,
|
|
|
+ devlistptrtype)
|
|
|
+
|
|
|
+ if not self._opened:
|
|
|
+ msg = "No suitable devices could be opened"
|
|
|
+ self.fdll.ftdi_deinit(byref(self.ctx))
|
|
|
+ del self.ctx
|
|
|
+ raise FtdiError(msg)
|
|
|
+
|
|
|
+ def __wr(self, data, readBack=False):
|
|
|
+ if isinstance(data, list):
|
|
|
+ if readBack:
|
|
|
+ raise FtdiError("__wr() Readback supported only for 1 bit access")
|
|
|
+
|
|
|
+ w_data = (c_uint8 * len(data))(*data)
|
|
|
+ w_len = len(w_data)
|
|
|
+ if self.debug:
|
|
|
+ pass #print("__wr() writing %d bytes, data=[" % (w_len), end='')
|
|
|
+ for i in range(0, w_len):
|
|
|
+ if i != w_len - 1:
|
|
|
+ pass #print("0x%x," % (w_data[i]), end='')
|
|
|
+ else:
|
|
|
+ pass #print("0x%x" % (w_data[i]), end='')
|
|
|
+ pass #print("]")
|
|
|
+ else:
|
|
|
+ w_len = 1
|
|
|
+ if self.debug:
|
|
|
+ pass #print("__wr() writing 1 byte, data= [ 0x%x ]" % (data))
|
|
|
+ if readBack:
|
|
|
+ w_data = c_uint8(data | DataLine.READ)
|
|
|
+ else:
|
|
|
+ w_data = c_uint8(data)
|
|
|
+
|
|
|
+ ret_val = self.fdll.ftdi_write_data(byref(self.ctx),
|
|
|
+ byref(w_data), w_len)
|
|
|
+ if ret_val < 0:
|
|
|
+ msg = "ftdi_write_data(): %s (%d)" % (self.get_error_string(), ret_val)
|
|
|
+ raise FtdiError(msg)
|
|
|
+
|
|
|
+ if readBack:
|
|
|
+ numBytes = 1
|
|
|
+ rd_buf = (c_uint8 * (numBytes & 0x3f))()
|
|
|
+ ret_val = self.fdll.ftdi_read_data(byref(self.ctx), byref(rd_buf), numBytes)
|
|
|
+ if ret_val < 0:
|
|
|
+ msg = "ftdi_read_data(): %s (%d)" % (self.get_error_string(), ret_val)
|
|
|
+ raise FtdiError(msg)
|
|
|
+ return rd_buf[0]
|
|
|
+
|
|
|
+ def __rd(self, numBytes=1):
|
|
|
+ # Usually one doesn't want to use this function (shift-mode) but rather
|
|
|
+ # bitbang mode - so check if you want really __wr(data, readBack=True);
|
|
|
+ wr_dat = [DataLine.SHMODE | DataLine.READ | (numBytes & 0x3F)] + [self.dataLine] * numBytes
|
|
|
+
|
|
|
+ if numBytes > 0x3f:
|
|
|
+ msg = "__rd() number of supported bytes is 0x3f (requsted: %x)" % (numBytes)
|
|
|
+ raise FtdiError(msg)
|
|
|
+
|
|
|
+ self.__wr(wr_dat)
|
|
|
+
|
|
|
+ rd_buf = (c_uint8 * (numBytes & 0x3f))()
|
|
|
+ ret_val = self.fdll.ftdi_read_data(byref(self.ctx),
|
|
|
+ byref(rd_buf), numBytes)
|
|
|
+ if ret_val < 0:
|
|
|
+ msg = "ftdi_read_data(): %s (%d)" % (self.get_error_string(), ret_val)
|
|
|
+ raise FtdiError(msg)
|
|
|
+ elif ret_val == 0:
|
|
|
+ return []
|
|
|
+ elif ret_val == 1:
|
|
|
+ ret_data = rd_buf[0]
|
|
|
+ if self.debug:
|
|
|
+ pass #print("__rd() returned 1 byte, data=[ 0x%x ]" %ret_data)
|
|
|
+ else:
|
|
|
+ ret_data = []
|
|
|
+ for i in range(0, len(rd_buf)):
|
|
|
+ ret_data.append(int(rd_buf[i]))
|
|
|
+ if self.debug:
|
|
|
+ pass #print("__rd() returned %d bytes, data=[" % (len(ret_data)), end='')
|
|
|
+ for i in range(0, len(ret_data)):
|
|
|
+ if i != len(ret_data) - 1:
|
|
|
+ pass #print("0x%x," % (ret_data[i]), end='')
|
|
|
+ else:
|
|
|
+ pass #print("0x%x" % (ret_data[i]), end='')
|
|
|
+ pass #print("]")
|
|
|
+ return ret_data
|
|
|
+
|
|
|
+ def _shiftTAP(self, data):
|
|
|
+ for bit in data:
|
|
|
+ if bit not in "01":
|
|
|
+ continue
|
|
|
+ out = (self.dataLine & ~(DataLine.TDI | DataLine.TMS)) | DataLine.NCE | DataLine.NCS | DataLine.LED
|
|
|
+ if bit == '1':
|
|
|
+ out |= DataLine.TMS
|
|
|
+ self.dataLine = out
|
|
|
+ self.__wr(out)
|
|
|
+ self.__wr(out | DataLine.TCK)
|
|
|
+
|
|
|
+ def _shiftReg(self, data):
|
|
|
+ dataOut = ""
|
|
|
+
|
|
|
+ for idx, bit in enumerate(data[::-1]):
|
|
|
+ if bit not in "01":
|
|
|
+ continue
|
|
|
+ out = (self.dataLine & ~(DataLine.TDI | DataLine.TMS)) | DataLine.NCE | DataLine.NCS | DataLine.LED
|
|
|
+ if bit == '1':
|
|
|
+ out |= DataLine.TDI
|
|
|
+ if idx == len(data) - 1:
|
|
|
+ out |= DataLine.TMS
|
|
|
+ self.dataLine = out
|
|
|
+ tmp = self.__wr(out, readBack=True)
|
|
|
+
|
|
|
+ tmp = self.__wr(out | DataLine.TCK, readBack=True)
|
|
|
+ if tmp & DataLine.READ_TDO:
|
|
|
+ dataOut += "1"
|
|
|
+ else:
|
|
|
+ dataOut += "0"
|
|
|
+
|
|
|
+ return dataOut
|
|
|
+
|
|
|
+ def setBit(self, bit, value):
|
|
|
+ if value:
|
|
|
+ self.dataLine |= bit
|
|
|
+ else:
|
|
|
+ self.dataLine &= ~bit
|
|
|
+ self.__wr(self.dataLine)
|
|
|
+
|
|
|
+ def reset(self):
|
|
|
+ JTAG.reset(self)
|
|
|
+ self.__wr(DataLine.NCE | DataLine.NCS | DataLine.LED)
|
|
|
+
|
|
|
+ def toggleJTAGClk(self, times=1, seconds=None):
|
|
|
+ if type(times) is not int or times < 1:
|
|
|
+ pass #print("Error: times has to be a positive integer. No clock cycles applied.")
|
|
|
+ return
|
|
|
+ if seconds:
|
|
|
+ while seconds:
|
|
|
+ self._shiftTAP("0")
|
|
|
+ time.sleep(1)
|
|
|
+ seconds -= 1
|
|
|
+ else:
|
|
|
+ while times:
|
|
|
+ self._shiftTAP("0")
|
|
|
+ times -= 1
|
|
|
+
|
|
|
+ def scanChain(self):
|
|
|
+ # Very stupid hard-coded scan chain - sequence from OpenOCD
|
|
|
+ r_size = 128
|
|
|
+ r_buf = (c_uint8 * r_size)()
|
|
|
+
|
|
|
+ self.reset()
|
|
|
+
|
|
|
+ # Clear first the buffer (if anything was in)
|
|
|
+ ret_val = self.fdll.ftdi_usb_purge_rx_buffer(byref(self.ctx))
|
|
|
+
|
|
|
+ w_data = [0x2f, 0x2c, 0x2d, 0x2e, 0x2f, 0x2c, 0x2d, 0x2c, 0x2d, 0x2c]
|
|
|
+ self.__wr(w_data)
|
|
|
+
|
|
|
+ # Read 0x30 bytes out
|
|
|
+ r_buf = self.__rd(0x30)
|
|
|
+
|
|
|
+ # No answer, not very good reverse-engineered jtag stream :-)
|
|
|
+ r_size = len(r_buf)
|
|
|
+ dev_ids = []
|
|
|
+ dev_cnts = -1
|
|
|
+ non_zero = 0
|
|
|
+ for i in range(0, r_size):
|
|
|
+ if r_buf[i] == 0xff or r_buf[i] == self.dataLine:
|
|
|
+ break
|
|
|
+
|
|
|
+ if i % 4 == 0:
|
|
|
+ dev_cnts += 1
|
|
|
+ dev_ids.append(0)
|
|
|
+
|
|
|
+ if r_buf[i] != 0:
|
|
|
+ non_zero = 1
|
|
|
+ dev_ids[dev_cnts] |= ((r_buf[i]) << (8 * (i % 4)))
|
|
|
+
|
|
|
+ self.reset()
|
|
|
+
|
|
|
+ # Clear first the buffer (if anything was in)
|
|
|
+ ret_val = self.fdll.ftdi_usb_purge_rx_buffer(byref(self.ctx))
|
|
|
+
|
|
|
+ if non_zero == 1:
|
|
|
+ return dev_ids
|
|
|
+ else:
|
|
|
+ return []
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ jDev = USBBlaster()
|
|
|
+ dev_ids = jDev.scanChain()
|
|
|
+ if len(dev_ids) > 0:
|
|
|
+ pass #print("Devices found:")
|
|
|
+ for i in range(0, len(dev_ids)):
|
|
|
+ pass #print("tap%d ID=0x%08x" % (i, dev_ids[i]))
|
|
|
+ else:
|
|
|
+ pass #print("No taps/devices found!")
|