class BaseHardware(object): """Hardware base class. Subclass this class for actual hardware. See, e.g. :class:`HiSPARCII`. """ description = "BaseHardware" _device = None _buffer = None def __init__(self, device_id, device_index=0): self.device_id = device_id self.device_index = device_index self.open() self._buffer = bytearray() def __del__(self): self.close() def open(self): """Open the hardware device.""" self._device = FtdiChip(self.description) def close(self): """Close the hardware device.""" if self._device and not self._device.closed: self._device.close() def flush_device(self): """Flush device output buffers. To completely clear out outdated measurements when changing parameters, call this method. All data received after this method was called is really newly measured. """ self._device.flush() del self._buffer[:] def send_message(self, msg): """Send a message to the hardware device.""" logger.debug("Sending message: %s", msg) self._device.write(msg.encode()) def read_into_buffer(self): """Read data from device and place it in the read buffer. Call this method to empty the device and host usb buffer. All data is read into the class instance's data buffer. It is not necessary to call this method in your program, unless you need to make sure the usb buffers won't fill up while running long tasks. If you just want to read messages from the device, use the appropriate methods. This method is called by those methods. """ data = self._device.read(READ_SIZE) self._buffer.extend(data) def read_message(self): """Read a message from the hardware device. Call this method to communicate with the device. This method should call :meth:`read_into_buffer` and should run the return value through a MessageFactory class. """ self.read_into_buffer() raise NotImplementedError()
class BaseHardware(object): """Hardware base class. Subclass this class for actual hardware. See, e.g. :class:`HiSPARCII`. """ description = "BaseHardware" _device = None _buffer = None def __init__(self): self.open() self._buffer = bytearray() def __del__(self): self.close() def open(self): """Open the hardware device.""" self._device = FtdiChip(self.description) def close(self): """Close the hardware device.""" if self._device and not self._device.closed: self._device.close() def flush_device(self): """Flush device output buffers. To completely clear out outdated measurements when changing parameters, call this method. All data received after this method was called is really newly measured. """ self._device.flush() del self._buffer[:] def send_message(self, msg): """Send a message to the hardware device.""" logger.debug("Sending message: %s", msg) self._device.write(msg.encode()) def read_into_buffer(self): """Read data from device and place it in the read buffer. Call this method to empty the device and host usb buffer. All data is read into the class instance's data buffer. It is not necessary to call this method in your program, unless you need to make sure the usb buffers won't fill up while running long tasks. If you just want to read messages from the device, use the appropriate methods. This method is called by those methods. """ data = self._device.read(READ_SIZE) self._buffer.extend(data) def read_message(self): """Read a message from the hardware device. Call this method to communicate with the device. This method should call :meth:`read_into_buffer` and should run the return value through a MessageFactory class. """ self.read_into_buffer() raise NotImplementedError()
def _burn_firmware(self): """Burn the firmware to the device's FPGA. This was a pain to implement. Understanding this code entails going back and forth between the FTDI and Altera Cyclone manuals, as well as the HiSPARC schematics. The Cyclone has 16 data bits (page 5 in the manual) of which several are general purpose I/O (GPIO). The connections are up to the hardware designers. Connections FTDI <-> Cyclone (HiSPARC III hardware): bit 8: nCONFIG bit 9: CONF_DONE bit 10: nSTATUS Configuration of the FPGA is described on page 9-9 of the Cyclone manual. First, pull nCONFIG low (for at least 500 ns). nSTATUS and CONF_DONE are also pulled low by the device. Then, pull nCONFIG high and the device returns nSTATUS to a high state. If configuration is succesful, CONF_DONE is high. If an error occurs, nSTATUS is pulled low and CONF_DONE remains low. On page 9-13 in the Cyclone manual, the connections between the serial configuration device (the FTDI chip) and the Cyclone are described, along with the data connection and clock settings (page 9-14). References: Altera Cyclone III Device Handbook -- Volume 1 FTDI Application Note AN_108 -- Command Processor for MPSSE and MCU Host Bus Emulation Modes Basic information on using MPSSE mode (useful for understanding the bitmode, clock settings and writing the data): FTDI Application Note AN_135 -- FTDI MPSSE Basics """ # open device's first interface (MPSSE) device = FtdiChip(self.description, interface_select=1) # Select MPSSE mode (0x02) # Direction is not used here, it doesn't seem to work. # We'll set the direction explicitly later. device._device.ftdi_fn.ftdi_set_bitmode(0, 0x02) # Set clock frequency to 30 MHz (0x0000) device.write(bytearray([TCK_DIVISOR, 0, 0])) # Disable divide clock frequency by 5 device.write(bytearray([DISABLE_CLK_DIV5])) # bits 0 and 1 are output that is, bits TCK/SK and TDI/DO, clock and # data device.write(bytearray([SET_BITS_LOW, 0, 0b11])) # pull nCONFIG (low byte bit 0) low device.write(bytearray([SET_BITS_HIGH, 0, 1])) # pull nCONFIG (low byte bit 0) high device.write(bytearray([SET_BITS_HIGH, 1, 1])) # write firmware to device firmware = pkg_resources.resource_string(__name__, "firmware.rbf") for idx in range(0, len(firmware), FPGA_BUFFER_SIZE): xbuf = firmware[idx:idx + FPGA_BUFFER_SIZE] LENGTH = len(xbuf) - 1 LENGTH_L = LENGTH & 0xff LENGTH_H = LENGTH >> 8 & 0xff device.write(bytearray([WRITE_BYTES_PVE_LSB, LENGTH_L, LENGTH_H])) device.write(xbuf) # read device status device.write(bytearray([GET_BITS_HIGH])) time.sleep(.01) data_bits = ord(device.read(1)) # CONF_DONE is bit 8 (high byte, bit 1) conf_done = data_bits & 0b10 if not conf_done: raise HardwareError("Error loading firmware.") device.close()