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()
Exemple #2
0
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()
Exemple #4
0
    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()