class ft2232: def open_ft2232(self, vps, itf, sn): # find the device on USB devices = usbtools.UsbTools.find_all(vps) if sn is not None: # filter based on device serial number devices = [dev for dev in devices if dev[2] == sn] if len(devices) == 0: raise IOError("No such device") self.vid = devices[0][0] self.pid = devices[0][1] self.sn = devices[0][2] self.ftdi = Ftdi() self.freq = self.ftdi.open_mpsse(self.vid, self.pid, itf, serial = self.sn, frequency = _FREQ) self.wrbuf = array.array('B') self.gpio_init() self.state_reset() self.sir_end_state = 'IDLE' self.sdr_end_state = 'IDLE' def __del__(self): if self.ftdi: self.ftdi.close() def flush(self): """flush the write buffer to the ft2232""" if len(self.wrbuf) > 0: self.ftdi.write_data(self.wrbuf) del self.wrbuf[0:] def write(self, buf, flush = False): """queue write data to send to ft2232""" self.wrbuf.extend(buf) if flush: self.flush() def state_x(self, dst): """change the TAP state from self.state to dst""" if self.state == dst: return tms = tms_mpsse(tap.lookup(self.state, dst)) cmd = _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG self.write((cmd, tms[0], tms[1]), True) self.state = dst def state_reset(self): """from *any* state go to the reset state""" self.state = '*' self.state_x('RESET') def shift_data(self, tdi, tdo, end_state): """ write (and possibly read) a bit stream from the JTAGkey tdi - bit buffer of data to be written to the JTAG TDI pin tdo - bit buffer for the data read from the JTAG TDO pin (optional) end_state - leave the TAP state machine in this state """ wr = tdi.get() io_bits = tdi.n - 1 io_bytes = io_bits >> 3 io_bits &= 0x07 last_bit = (wr[io_bytes] << (7 - io_bits)) & 128 if tdo is not None: read_cmd = _MPSSE_DO_READ read_len = io_bytes + 1 if io_bits: read_len += 1 else: read_cmd = 0 read_len = 0 # write out the full bytes if io_bytes: cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_WRITE_NEG num = io_bytes - 1 self.write((cmd, _lsb(num), _msb(num))) self.write(wr[0:io_bytes]) # write out the remaining bits if io_bits: cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_BITMODE | _MPSSE_WRITE_NEG self.write((cmd, io_bits - 1, wr[io_bytes])) # the last bit of output data is bit 7 of the tms value (goes onto tdi) # continue to read to get the last bit of tdo data cmd = read_cmd | _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG tms = tms_mpsse(tap.lookup(self.state, end_state)) self.write((cmd, tms[0], tms[1] | last_bit)) self.state = end_state # if we are only writing, return if tdo is None: self.flush() return # make the ft2232 flush its data back to the PC self.write((Ftdi.SEND_IMMEDIATE,), True) rd = self.ftdi.read_data_bytes(read_len, _READ_RETRIES) if io_bits: # the n partial bits are in the top n bits of the byte # move them down to the bottom rd[-2] >>= (8 - io_bits) # get the last bit from the tms response byte (last byte) last_bit = (rd[-1] >> (7 - tms[0])) & 1 last_bit <<= io_bits # add the last bit if io_bits: # drop the tms response byte del rd[-1] # or it onto the io_bits byte rd[-1] |= last_bit else: # replace the tms response byte rd[-1] = last_bit # copy to the bit buffer tdo.set(tdi.n, rd) def scan_ir(self, tdi, tdo = None): """write (and possibly read) a bit stream through the IR in the JTAG chain""" self.state_x('IRSHIFT') self.shift_data(tdi, tdo, self.sir_end_state) def scan_dr(self, tdi, tdo = None): """write (and possibly read) a bit stream through the DR in the JTAG chain""" self.state_x('DRSHIFT') self.shift_data(tdi, tdo, self.sdr_end_state) def gpio_init(self): """setup the gpio lines""" self.gpio_dir = _TCK | _TDI | _TMS self.gpio_val = 0 self.ftdi.write_data((Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir))) self.ftdi.write_data((Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir))) def gpio_wr(self, gpio, val): """write a gpio pin""" self.gpio_dir |= gpio if val: self.gpio_val |= gpio else: self.gpio_val &= ~gpio if gpio <= _GPIOL3: self.ftdi.write_data((Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir))) else: self.ftdi.write_data((Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir))) def gpio_rd(self, gpio): """read a gpio pin""" if gpio <= _GPIOL3: self.ftdi.write_data((Ftdi.GET_BITS_LOW,)) val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0] else: self.ftdi.write_data((Ftdi.GET_BITS_HIGH,)) val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0] val <<= 8 return (val & gpio) != 0
class ft2232: def open_ft2232(self, vps, itf, sn): # find the device on USB devices = usbtools.UsbTools.find_all(vps) if sn is not None: # filter based on device serial number devices = [dev for dev in devices if dev[2] == sn] if len(devices) == 0: raise IOError("No such device") self.vid = devices[0][0] self.pid = devices[0][1] self.sn = devices[0][2] self.ftdi = Ftdi() self.freq = self.ftdi.open_mpsse(self.vid, self.pid, itf, serial=self.sn, frequency=_FREQ) self.wrbuf = array.array('B') self.gpio_init() self.state_reset() self.sir_end_state = 'IDLE' self.sdr_end_state = 'IDLE' def __del__(self): if self.ftdi: self.ftdi.close() def flush(self): """flush the write buffer to the ft2232""" if len(self.wrbuf) > 0: self.ftdi.write_data(self.wrbuf) del self.wrbuf[0:] def write(self, buf, flush=False): """queue write data to send to ft2232""" self.wrbuf.extend(buf) if flush: self.flush() def state_x(self, dst): """change the TAP state from self.state to dst""" if self.state == dst: return tms = tms_mpsse(tap.lookup(self.state, dst)) cmd = _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG self.write((cmd, tms[0], tms[1]), True) self.state = dst def state_reset(self): """from *any* state go to the reset state""" self.state = '*' self.state_x('RESET') def shift_data(self, tdi, tdo, end_state): """ write (and possibly read) a bit stream from the JTAGkey tdi - bit buffer of data to be written to the JTAG TDI pin tdo - bit buffer for the data read from the JTAG TDO pin (optional) end_state - leave the TAP state machine in this state """ wr = tdi.get() io_bits = tdi.n - 1 io_bytes = io_bits >> 3 io_bits &= 0x07 last_bit = (wr[io_bytes] << (7 - io_bits)) & 128 if tdo is not None: read_cmd = _MPSSE_DO_READ read_len = io_bytes + 1 if io_bits: read_len += 1 else: read_cmd = 0 read_len = 0 # write out the full bytes if io_bytes: cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_WRITE_NEG num = io_bytes - 1 self.write((cmd, _lsb(num), _msb(num))) self.write(wr[0:io_bytes]) # write out the remaining bits if io_bits: cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_BITMODE | _MPSSE_WRITE_NEG self.write((cmd, io_bits - 1, wr[io_bytes])) # the last bit of output data is bit 7 of the tms value (goes onto tdi) # continue to read to get the last bit of tdo data cmd = read_cmd | _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG tms = tms_mpsse(tap.lookup(self.state, end_state)) self.write((cmd, tms[0], tms[1] | last_bit)) self.state = end_state # if we are only writing, return if tdo is None: self.flush() return # make the ft2232 flush its data back to the PC self.write((Ftdi.SEND_IMMEDIATE, ), True) rd = self.ftdi.read_data_bytes(read_len, _READ_RETRIES) if io_bits: # the n partial bits are in the top n bits of the byte # move them down to the bottom rd[-2] >>= (8 - io_bits) # get the last bit from the tms response byte (last byte) last_bit = (rd[-1] >> (7 - tms[0])) & 1 last_bit <<= io_bits # add the last bit if io_bits: # drop the tms response byte del rd[-1] # or it onto the io_bits byte rd[-1] |= last_bit else: # replace the tms response byte rd[-1] = last_bit # copy to the bit buffer tdo.set(tdi.n, rd) def scan_ir(self, tdi, tdo=None): """write (and possibly read) a bit stream through the IR in the JTAG chain""" self.state_x('IRSHIFT') self.shift_data(tdi, tdo, self.sir_end_state) def scan_dr(self, tdi, tdo=None): """write (and possibly read) a bit stream through the DR in the JTAG chain""" self.state_x('DRSHIFT') self.shift_data(tdi, tdo, self.sdr_end_state) def gpio_init(self): """setup the gpio lines""" self.gpio_dir = _TCK | _TDI | _TMS self.gpio_val = 0 self.ftdi.write_data( (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir))) self.ftdi.write_data( (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir))) def gpio_wr(self, gpio, val): """write a gpio pin""" self.gpio_dir |= gpio if val: self.gpio_val |= gpio else: self.gpio_val &= ~gpio if gpio <= _GPIOL3: self.ftdi.write_data( (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir))) else: self.ftdi.write_data( (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir))) def gpio_rd(self, gpio): """read a gpio pin""" if gpio <= _GPIOL3: self.ftdi.write_data((Ftdi.GET_BITS_LOW, )) val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0] else: self.ftdi.write_data((Ftdi.GET_BITS_HIGH, )) val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0] val <<= 8 return (val & gpio) != 0
class jtag_driver: def __init__(self, vendor, product, interface): self.ftdi = Ftdi() try: self.ftdi.open_mpsse(vendor, product, interface) except IOError as e: print "ft2232 jtag driver: %s" % str(e) self.ftdi = None sys.exit(0) self.wrbuf = array.array('B') self.gpio_init() self.tap = tap.tap() self.state_reset() self.sir_end_state = 'IDLE' self.sdr_end_state = 'IDLE' def __del__(self): if self.ftdi: self.ftdi.close() def __str__(self): """return a string describing the device""" return self.ftdi.type def flush(self): """flush the write buffer to the ft2232""" if len(self.wrbuf) > 0: self.ftdi.write_data(self.wrbuf) del self.wrbuf[0:] def write(self, buf, flush=False): """queue write data to send to ft2232""" self.wrbuf.extend(buf) if flush: self.flush() def state_x(self, dst): """change the TAP state from self.state to dst""" bits = self.tap.tms(self.state, dst) if not bits: # no state change assert self.state == dst return tms = tms_mpsse(bits) cmd = _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG self.write((cmd, tms[0], tms[1]), True) self.state = dst def state_reset(self): """from *any* state go to the reset state""" self.state = '*' self.state_x('RESET') def shift_data(self, tdi, tdo, end_state): """ write (and possibly read) a bit stream from the JTAGkey tdi - bit buffer of data to be written to the JTAG TDI pin tdo - bit buffer for the data read from the JTAG TDO pin (optional) end_state - leave the TAP state machine in this state """ wr = tdi.get() io_bits = tdi.n - 1 io_bytes = io_bits >> 3 io_bits &= 0x07 last_bit = (wr[io_bytes] << (7 - io_bits)) & 128 if tdo is not None: read_cmd = _MPSSE_DO_READ read_len = io_bytes + 1 if io_bits: read_len += 1 else: read_cmd = 0 read_len = 0 # write out the full bytes if io_bytes: cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_WRITE_NEG num = io_bytes - 1 self.write((cmd, _lsb(num), _msb(num))) self.write(wr[0:io_bytes]) # write out the remaining bits if io_bits: cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_BITMODE | _MPSSE_WRITE_NEG self.write((cmd, io_bits - 1, wr[io_bytes])) # the last bit of output data is bit 7 of the tms value (goes onto tdi) # continue to read to get the last bit of tdo data cmd = read_cmd | _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG tms = tms_mpsse(self.tap.tms(self.state, end_state)) self.write((cmd, tms[0], tms[1] | last_bit)) self.state = end_state # if we are only writing, return if tdo is None: self.flush() return # make the ft2232 flush its data back to the PC self.write((Ftdi.SEND_IMMEDIATE, ), True) rd = self.ftdi.read_data_bytes(read_len, _READ_RETRIES) if io_bits: # the n partial bits are in the top n bits of the byte # move them down to the bottom rd[-2] >>= (8 - io_bits) # get the last bit from the tms response byte (last byte) last_bit = (rd[-1] >> (7 - tms[0])) & 1 last_bit <<= io_bits # add the last bit if io_bits: # drop the tms response byte del rd[-1] # or it onto the io_bits byte rd[-1] |= last_bit else: # replace the tms response byte rd[-1] = last_bit # copy to the bit buffer tdo.set(tdi.n, rd) def scan_ir(self, tdi, tdo=None): """write (and possibly read) a bit stream through the IR in the JTAG chain""" self.state_x('IRSHIFT') self.shift_data(tdi, tdo, self.sir_end_state) def scan_dr(self, tdi, tdo=None): """write (and possibly read) a bit stream through the DR in the JTAG chain""" self.state_x('DRSHIFT') self.shift_data(tdi, tdo, self.sdr_end_state) def test_reset(self, val): """control the test reset line""" # TODO pass def reset_jtag(self): """reset the TAP of all JTAG devices in the chain""" self.test_reset(True) time.sleep(_TRST_TIME) self.test_reset(False) self.state_reset() def gpio_init(self): """setup the gpio lines""" self.gpio_dir = _TCK | _TDI | _TMS self.gpio_val = 0 self.ftdi.write_data( (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir))) self.ftdi.write_data( (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir))) def gpio_out(self, gpio): if gpio <= _GPIOL3: self.ftdi.write_data( (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir))) else: self.ftdi.write_data( (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir))) def gpio_set(self, gpio): """set a gpio pin""" self.gpio_dir |= gpio self.gpio_val |= gpio self.gpio_out(gpio) def gpio_clr(self, gpio): """clear a gpio pin""" self.gpio_dir |= gpio self.gpio_val &= ~gpio self.gpio_out(gpio) def gpio_rd(self, gpio): """read a gpio pin""" if gpio <= _GPIOL3: self.ftdi.write_data((Ftdi.GET_BITS_LOW, )) val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0] else: self.ftdi.write_data((Ftdi.GET_BITS_HIGH, )) val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0] val <<= 8 return (val & gpio) != 0
class SpiController(object): """SPI master. :param silent_clock: should be set to avoid clocking out SCLK when all /CS signals are released. This clock beat is used to enforce a delay between /CS signal activation. When weird SPI devices are used, SCLK beating may cause trouble. In this case, silent_clock should be set but beware that SCLK line should be fitted with a pull-down resistor, as SCLK is high-Z during this short period of time. :param cs_count: is the number of /CS lines (one per device to drive on the SPI bus) """ SCK_BIT = 0x01 DO_BIT = 0x02 DI_BIT = 0x04 CS_BIT = 0x08 PAYLOAD_MAX_LENGTH = 0x10000 # 16 bits max def __init__(self, silent_clock=False, cs_count=4): self._ftdi = Ftdi() self._cs_bits = ((SpiController.CS_BIT << cs_count) - 1) & \ ~(SpiController.CS_BIT - 1) self._ports = [None] * cs_count self._direction = self._cs_bits | \ SpiController.DO_BIT | \ SpiController.SCK_BIT self._cs_high = Array('B') if silent_clock: # Set SCLK as input to avoid emitting clock beats self._cs_high.extend([Ftdi.SET_BITS_LOW, self._cs_bits, self._direction & ~SpiController.SCK_BIT]) # /CS to SCLK delay, use 8 clock cycles as a HW tempo self._cs_high.extend([Ftdi.WRITE_BITS_TMS_NVE, 8-1, 0xff]) # Restore idle state self._cs_high.extend([Ftdi.SET_BITS_LOW, self._cs_bits, self._direction]) self._immediate = Array('B', [Ftdi.SEND_IMMEDIATE]) self._frequency = 0.0 def configure(self, vendor, product, interface, frequency=6.0E6): """Configure the FTDI interface as a SPI master""" self._frequency = \ self._ftdi.open_mpsse(vendor, product, interface, direction=self._direction, initial=self._cs_bits, # /CS all high frequency=frequency) def terminate(self): """Close the FTDI interface""" if self._ftdi: self._ftdi.close() self._ftdi = None def get_port(self, cs): """Obtain a SPI port to drive a SPI device selected by cs""" if not self._ftdi: raise SpiIOError("FTDI controller not initialized") if cs >= len(self._ports): raise SpiIOError("No such SPI port") if not self._ports[cs]: cs_state = 0xFF & ~((SpiController.CS_BIT<<cs) | SpiController.SCK_BIT | SpiController.DO_BIT) cs_cmd = Array('B', [Ftdi.SET_BITS_LOW, cs_state, self._direction]) self._ports[cs] = SpiPort(self, cs_cmd) self._flush() return self._ports[cs] @property def frequency_max(self): """Returns the maximum SPI clock""" return self._ftdi.frequency_max @property def frequency(self): """Returns the current SPI clock""" return self._frequency def _exchange(self, frequency, cs_cmd, out, readlen): """Perform a half-duplex transaction with the SPI slave""" if not self._ftdi: raise SpiIOError("FTDI controller not initialized") if len(out) > SpiController.PAYLOAD_MAX_LENGTH: raise SpiIOError("Output payload is too large") if readlen > SpiController.PAYLOAD_MAX_LENGTH: raise SpiIOError("Input payload is too large") if self._frequency != frequency: freq = self._ftdi.set_frequency(frequency) # store the requested value, not the actual one (best effort) self._frequency = frequency write_cmd = struct.pack('<BH', Ftdi.WRITE_BYTES_NVE_MSB, len(out)-1) if readlen: read_cmd = struct.pack('<BH', Ftdi.READ_BYTES_NVE_MSB, readlen-1) cmd = Array('B', cs_cmd) cmd.fromstring(write_cmd) cmd.extend(out) cmd.fromstring(read_cmd) cmd.extend(self._immediate) cmd.extend(self._cs_high) self._ftdi.write_data(cmd) # USB read cycle may occur before the FTDI device has actually # sent the data, so try to read more than once if no data is # actually received data = self._ftdi.read_data_bytes(readlen, 4) else: cmd = Array('B', cs_cmd) cmd.fromstring(write_cmd) cmd.extend(out) cmd.extend(self._cs_high) self._ftdi.write_data(cmd) data = Array('B') return data def _flush(self): """Flush the HW FIFOs""" self._ftdi.write_data(self._immediate) self._ftdi.purge_buffers()
class SpiController(object): """SPI master. :param silent_clock: should be set to avoid clocking out SCLK when all /CS signals are released. This clock beat is used to enforce a delay between /CS signal activation. When weird SPI devices are used, SCLK beating may cause trouble. In this case, silent_clock should be set but beware that SCLK line should be fitted with a pull-down resistor, as SCLK is high-Z during this short period of time. :param cs_count: is the number of /CS lines (one per device to drive on the SPI bus) """ SCK_BIT = 0x01 DO_BIT = 0x02 DI_BIT = 0x04 CS_BIT = 0x08 PAYLOAD_MAX_LENGTH = 0x10000 # 16 bits max def __init__(self, silent_clock=False, cs_count=4): self._ftdi = Ftdi() self._cs_bits = ((SpiController.CS_BIT << cs_count) - 1) & \ ~(SpiController.CS_BIT - 1) self._ports = [None] * cs_count self._direction = self._cs_bits | \ SpiController.DO_BIT | \ SpiController.SCK_BIT self._cs_high = Array('B') if silent_clock: # Set SCLK as input to avoid emitting clock beats self._cs_high.extend([ Ftdi.SET_BITS_LOW, self._cs_bits, self._direction & ~SpiController.SCK_BIT ]) # /CS to SCLK delay, use 8 clock cycles as a HW tempo self._cs_high.extend([Ftdi.WRITE_BITS_TMS_NVE, 8 - 1, 0xff]) # Restore idle state self._cs_high.extend( [Ftdi.SET_BITS_LOW, self._cs_bits, self._direction]) self._immediate = Array('B', [Ftdi.SEND_IMMEDIATE]) self._frequency = 0.0 def configure(self, vendor, product, interface, frequency=6.0E6): """Configure the FTDI interface as a SPI master""" self._frequency = \ self._ftdi.open_mpsse(vendor, product, interface, direction=self._direction, initial=self._cs_bits, # /CS all high frequency=frequency) def terminate(self): """Close the FTDI interface""" if self._ftdi: self._ftdi.close() self._ftdi = None def get_port(self, cs): """Obtain a SPI port to drive a SPI device selected by cs""" if not self._ftdi: raise SpiIOError("FTDI controller not initialized") if cs >= len(self._ports): raise SpiIOError("No such SPI port") if not self._ports[cs]: cs_state = 0xFF & ~((SpiController.CS_BIT << cs) | SpiController.SCK_BIT | SpiController.DO_BIT) cs_cmd = Array('B', [Ftdi.SET_BITS_LOW, cs_state, self._direction]) self._ports[cs] = SpiPort(self, cs_cmd) self._flush() return self._ports[cs] @property def frequency_max(self): """Returns the maximum SPI clock""" return self._ftdi.frequency_max @property def frequency(self): """Returns the current SPI clock""" return self._frequency def _exchange(self, frequency, cs_cmd, out, readlen): """Perform a half-duplex transaction with the SPI slave""" if not self._ftdi: raise SpiIOError("FTDI controller not initialized") if len(out) > SpiController.PAYLOAD_MAX_LENGTH: raise SpiIOError("Output payload is too large") if readlen > SpiController.PAYLOAD_MAX_LENGTH: raise SpiIOError("Input payload is too large") if self._frequency != frequency: freq = self._ftdi.set_frequency(frequency) # store the requested value, not the actual one (best effort) self._frequency = frequency write_cmd = struct.pack('<BH', Ftdi.WRITE_BYTES_NVE_MSB, len(out) - 1) if readlen: read_cmd = struct.pack('<BH', Ftdi.READ_BYTES_NVE_MSB, readlen - 1) cmd = Array('B', cs_cmd) cmd.fromstring(write_cmd) cmd.extend(out) cmd.fromstring(read_cmd) cmd.extend(self._immediate) cmd.extend(self._cs_high) self._ftdi.write_data(cmd) # USB read cycle may occur before the FTDI device has actually # sent the data, so try to read more than once if no data is # actually received data = self._ftdi.read_data_bytes(readlen, 4) else: cmd = Array('B', cs_cmd) cmd.fromstring(write_cmd) cmd.extend(out) cmd.extend(self._cs_high) self._ftdi.write_data(cmd) data = Array('B') return data def _flush(self): """Flush the HW FIFOs""" self._ftdi.write_data(self._immediate) self._ftdi.purge_buffers()