class Controller(object):
    def __init__(self,
                 dev=None,
                 vendor_id=0x0403,
                 product_id=0x8531,
                 status=None):
        super(Controller, self).__init__()

        self.vendor = vendor_id
        self.product = product_id
        self.s = status

    def upload(self, filepath):
        """
        Write a binary file to the the SPI Prom

        Args:
            filepath (String): Path to FPGA Binary (bin) image

        Returns (boolean):
            True: Successfully programmed
            False: Failed to program

        Raises:
            IOError:
                Failed to open binary file
        """
        binf = ""

        f = open(filepath, "r")
        binf = f.read()
        f.close()
        #Allow the users to handle File Errors

        #Open the SPI Flash Device
        manager = serial_flash_manager.SerialFlashManager(
            self.vendor, self.product, 2)
        flash = manager.get_flash_device()

        #Print out the device was found
        if self.s: self.s.Info("Found: %s" % str(flash))

        #Erase the flash
        if self.s:
            self.s.Info(
                "Erasing the SPI Flash device, this can take a minute or two..."
            )
        flash.bulk_erase()
        #Write the binary file
        if self.s: self.s.Info("Flash erased, writing binary image to PROM")
        flash.write(0x00, binf)

        #Verify the data was read
        binf_rb = flash.read(0x00, len(binf))
        binf_str = binf_rb.tostring()

        del flash
        del manager

        if binf_str != binf:
            raise NysaError(
                "Image Verification Failed!, data written is not the same as data read"
            )

    def program(self):
        """
        Send a program signal to the board, the FPGA will attempt to read the
        binary image file from the SPI prom. If successful the 'done' LED will
        illuminate

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing
        """
        bbc = BitBangController(self.vendor, self.product, 2)
        if self.s: self.s.Important("Set signals to input")
        bbc.set_pins_to_input()
        bbc.set_pins_to_output()
        bbc.program_high()
        time.sleep(.5)
        bbc.program_low()
        time.sleep(.2)
        bbc.program_high()
        bbc.pins_on()
        bbc.set_pins_to_input()

    def read_bin_file(self, filepath):
        """
        Read the binary image from the SPI Flash

        Args:
            filepath (String): Path to the filepath where the SPI image will
                be written to

        Returns:
            Nothing

        Raises:
            IOError:
                Problem openning file to write to
        """
        manager = serial_flash_manager.SerialFlashManager(
            self.vendor, self.product, 2)
        flash = manager.get_flash_device()

        #Don't know how long the binary file is so we need to read the entire
        #Image

        binf_rb = flash.read(0x00, len(flash))
        f = open(filepath, "w")
        binf_rb.tofile(f)
        f.close()

    def reset(self):
        """
        Send a reset signal to the board, this is the same as pressing the
        'reset' button

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing
        """
        bbc = BitBangController(self.vendor, self.product, 2)
        bbc.set_soft_reset_to_output()
        bbc.soft_reset_high()
        time.sleep(.2)
        bbc.soft_reset_low()
        time.sleep(.2)
        bbc.soft_reset_high()
        bbc.pins_on()
        bbc.set_pins_to_input()

    def set_sync_fifo_mode(self):
        """
        Change the mode of the FIFO to a synchronous FIFO

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing

        """
        fifo = FifoController(self.vendor, self.product)
        fifo.set_sync_fifo()

    def set_debug_mode(self):
        """
        Change the mode of the FIFO to a asynchronous FIFO

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing
        """
        fifo = FifoController(self.vendor, self.product)
        fifo.set_async_fifo()

    def open_dev(self):
        """_open_dev

        Open an FTDI Communication Channel

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Exception
        """
        self.dev = Ftdi()
        frequency = 30.0E6
        latency = 4
        #Ftdi.add_type(self.vendor, self.product, 0x700, "ft2232h")
        self.dev.open(self.vendor, self.product, 0)

        #Drain the input buffer
        self.dev.purge_buffers()

        #Reset
        #Enable MPSSE Mode
        self.dev.set_bitmode(0x00, Ftdi.BITMODE_SYNCFF)

        #Configure Clock
        frequency = self.dev._set_frequency(frequency)

        #Set Latency Timer
        self.dev.set_latency_timer(latency)

        #Set Chunk Size
        self.dev.write_data_set_chunksize(0x10000)
        self.dev.read_data_set_chunksize(0x10000)

        #Set the hardware flow control
        self.dev.set_flowctrl('hw')
        self.dev.purge_buffers()

    def ioctl(self, name, arg=None):
        raise AssertionError("%s not implemented" %
                             sys._getframe().f_code.co_name)

    def list_ioctl(self):
        raise AssertionError("%s not implemented" %
                             sys._getframe().f_code.co_name)
Exemplo n.º 2
0
class _Artemis(Nysa):
    """
    Artemis

    Concrete Class that implemented Artemis specific communication functions
    """
    def __init__(self,
                 idVendor=0x0403,
                 idProduct=0x8531,
                 sernum=None,
                 status=False):
        Nysa.__init__(self, status)
        self.vendor = idVendor
        self.product = idProduct
        self.sernum = sernum

        self.dev = None
        #Run a full garbage collection so any previous references to Artemis will be removed
        gc.collect()
        self.lock = threading.Lock()

        self.dev = Ftdi()
        self._open_dev()
        self.name = "Artemis"
        self.interrupts = 0x00
        self.events = []
        for i in range(INTERRUPT_COUNT):
            e = threading.Event()
            e.set()
            self.events.append(e)

        self.hwq = Queue.Queue(10)
        self.hrq = Queue.Queue(10)

        self.d = ArtemisData()

        self.worker = WorkerThread(self.dev, self.hwq, self.hrq, self.d,
                                   self.lock, self.interrupt_update_callback)
        #Is there a way to indicate closing
        self.worker.setDaemon(True)
        self.worker.start()

        try:
            #XXX: Hack to fix a strange bug where FTDI
            #XXX: won't recognize Artemis until a read and reset occurs
            #self.ping()
            pass

        except NysaCommError:
            pass

        self.reset()
        '''
        #status = True
        self.reader_thread = ReaderThread(self.dev, self.interrupt_update_callback, self.lock, status = status)
        self.reader_thread.setName("Reader Thread")
        #XXX: Need to find a better way to shut this down
        self.reader_thread.setDaemon(True)
        self.reader_thread.start()
        '''

    def __del__(self):
        #if self.s: self.s.Debug( "Close reader thread")
        #self.lock.aquire()
        #if (self.reader_thread is not None) and self.reader_thread.isAlive():
        #    self.reader_thread.stop()
        #    self.s.Debug( "Waiting to join")
        #    self.reader_thread.join()
        #self.lock.release()
        #self.s = True
        #if self.s: self.s.Debug( "Reader thread joined")
        #self.dev.close()
        pass

    def _open_dev(self):
        """_open_dev

        Open an FTDI Communication Channel

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Exception
        """
        #This frequency should go up to 60MHz
        frequency = 30.0E6
        #Latency can go down to 2 but there is a small chance there will be a
        #crash
        latency = 2
        #Ftdi.add_type(self.vendor, self.product, 0x700, "ft2232h")
        self.dev.open(self.vendor, self.product, 0, serial=self.sernum)

        #Drain the input buffer
        self.dev.purge_buffers()

        #Reset
        #Configure Clock
        #frequency = self.dev._set_frequency(frequency)

        #Set Latency Timer
        self.dev.set_latency_timer(latency)

        #Set Chunk Size (Maximum Chunk size)
        self.dev.write_data_set_chunksize(0x10000)
        self.dev.read_data_set_chunksize(0x10000)

        #Set the hardware flow control
        self.dev.set_flowctrl('hw')
        self.dev.purge_buffers()
        #Enable MPSSE Mode
        self.dev.set_bitmode(0x00, Ftdi.BITMODE_SYNCFF)

    def ipc_comm_response(self, name):
        try:
            resp = self.hrq.get(block=True, timeout=ARTEMIS_QUEUE_TIMEOUT)
            if resp == ARTEMIS_RESP_OK:
                #print "%s got an OK response!" % current_thread().name
                return self.d.data
            else:
                raise NysaCommError("Artemis response error %s: %d" %
                                    (name, resp))
        except Queue.Empty:
            raise NysaCommError("Artemis error %s: timeout: %d" %
                                (name, ARTEMIS_QUEUE_TIMEOUT))

    def read(self, address, length=1, disable_auto_inc=False):
        """read

        read data from Artemis

        Command Format

        ID 02 NN NN NN OO AA AA AA
           ID: ID Byte (0xCD)
           02: Read Command (12 for memory read)
           NN: Size of Read (3 Bytes)
           OO: Offset (for peripheral, part of address for mem)
           AA: Address (3 bytes for peripheral,
               (4 bytes including offset for mem)

        Args:
            address (long): Address of the register/memory to read
            length (int): Number of 32-bit words to read
            disable_auto_inc (bool): if true, auto increment feature will be
                disabled

        Returns:
            (Byte Array): A byte array containing the raw data returned from
            Artemis

        Raises:
            NysaCommError
        """
        with self.lock:
            self.d.data = Array('B', [0xCD, 0x02])
            if address >= ARTEMIS_MEMORY_OFFSET:
                address -= ARTEMIS_MEMORY_OFFSET
                self.d.data[1] = self.d.data[1] | 0x10

            if disable_auto_inc:
                self.d.data[1] = self.d.data[1] | 0x20

            fmt_string = "%06X" % length
            self.d.data.fromstring(fmt_string.decode('hex'))

            #Add the address
            addr_string = "%08X" % (address & 0xFFFFFFFF)
            self.d.data.fromstring(addr_string.decode('hex'))
            self.d.length = length
            self.hwq.put(ARTEMIS_READ)
            return self.ipc_comm_response("read")

    def write(self, address, data, disable_auto_inc=False):
        """write

        Write data to a Nysa image

        Command Format

        ID 01 NN NN NN OO AA AA AA DD DD DD DD
           ID: ID Byte (0xCD)
           01: Write Command (11 for Memory Write)
           NN: Size of Write (3 Bytes)
           OO: Offset (for peripheral, part of address for mem)
           AA: Address (3 bytes for peripheral,
             #(4 bytes including offset for mem)
           DD: Data (4 bytes)

        Args:
            address (long): Address of the register/memory to write to
            memory_device (boolean):
                True: Memory device
                False: Peripheral device
            data (array of bytes): Array of raw bytes to send to the devcie
            disable_auto_inc (boolean): Default False
                Set to true if only writing to one memory address (FIFO Mode)

        Returns: Nothing

        Raises:
            NysaCommError
        """
        with self.lock:
            length = len(data) / 4
            #Create an Array with the identification byte and code for writing
            self.d.data = Array('B', [0xCD, 0x01])
            if address >= ARTEMIS_MEMORY_OFFSET:
                address -= ARTEMIS_MEMORY_OFFSET
                self.d.data[1] = self.d.data[1] | 0x10
            if disable_auto_inc:
                self.d.data[1] = self.d.data[1] | 0x20

            #Append the length into the first 24 bits
            fmt_string = "%06X" % length
            self.d.data.fromstring(fmt_string.decode('hex'))
            addr_string = "%08X" % (address & 0xFFFFFFFF)
            self.d.data.fromstring(addr_string.decode('hex'))
            self.d.data.extend(data)
            self.hwq.put(ARTEMIS_WRITE)
            self.ipc_comm_response("write")

    def ping(self):
        """ping

        Command Format

        ID 00 00 00 00 00 00 00 00
            ID: ID Byte (0xCD)
            00: Ping Command
            00 00 00 00 00 00 00: Zeros

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            NysaCommError
        """
        with self.lock:
            self.hwq.put(ARTEMIS_PING)
            self.ipc_comm_response("ping")

    def reset(self):
        """ reset

        Software reset the Nysa FPGA Master, this may not actually reset the entire
        FPGA image

        ID 03 00 00 00
            ID: ID Byte (0xCD)
            00: Reset Command
            00 00 00: Zeros

        Args:
            Nothing

        Return:
            Nothing

        Raises:
            NysaCommError: Failue in communication
        """
        with self.lock:
            self.d.data = (self.vendor, self.product)
            self.hwq.put(ARTEMIS_RESET)
            self.ipc_comm_response("reset")

    def is_programmed(self):
        """
        Check if the FPGA is programmed

        Args:
            Nothing

        Return (Boolean):
            True: FPGA is programmed
            False: FPGA is not programmed

        Raises:
            NysaCommError: Failue in communication
        """
        with self.lock:
            self.d.data = (self.vendor, self.product)
            self.hwq.put(ARTEMIS_IS_PROGRAMMED)
            self.dev.purge_buffers()
            return self.ipc_comm_response("is programmed")

    def dump_core(self):
        """ dump_core

        Returns the state of the wishbone master priorto a reset, this is usefu for
        statusging a crash

        Command Format

        ID 0F 00 00 00 00 00 00 00
            ID: ID Byte (0xCD)
            0F: Dump Core Command
            00 00 00 00 00 00 00 00 00 00 00: Zeros

        Args:
            Nothing

        Returns:
            (Array of 32-bit Values) to be parsed by the core_analyzer utility

        Raises:
            NysaCommError: A failure in communication is detected
        """
        with self.lock:
            self.hwq.put(ARTEMIS_DUMP_CORE)
            return self.ipc_comm_response("dump core")

    def register_interrupt_callback(self, index, callback):
        """ register_interrupt

        Setup the thread to call the callback when an interrupt is detected

        Args:
            index (Integer): bit position of the device
                if the device is 1, then set index = 1
            callback: a function to call when an interrupt is detected

        Returns:
            Nothing

        Raises:
            Nothing
        """
        self.worker.register_interrupt_cb(index, callback)

    def unregister_interrupt_callback(self, index, callback=None):
        """ unregister_interrupt_callback

        Removes an interrupt callback from the reader thread list

        Args:
            index (Integer): bit position of the associated device
                EX: if the device that will receive callbacks is 1, index = 1
            callback: a function to remove from the callback list

        Returns:
            Nothing

        Raises:
            Nothing (This function fails quietly if ther callback is not found)
        """
        self.worker.unregister_interrupt_cb(index, callback)

    def wait_for_interrupts(self, wait_time=1, dev_id=None):
        """ wait_for_interrupts

        listen for interrupts for the user specified amount of time

        The Nysa image will send a small packet of info to the host when a slave
        needs to send information to the host

        Response Format
        DC 01 00 00 00 II II II II
            DC: Inverted CD is the start of a response
            01: Interrupt ID
            00 00 00 00 00 00 00 00: Zeros, reserved for future use
            II II II II: 32-bit interrupts


        Args:
            wait_time (Integer): the amount of time in seconds to wait for an
                interrupt
            dev_id (Integer): Optional device id, if set the function will look
                if an interrupt has already been declared for that function, if
                so then return immediately otherwise setup a callback for this


        Returns (boolean):
            True: Interrupts were detected
            Falses: Interrupts were not detected

        Raises:
            NysaCommError: A failure in communication is detected
        """

        if dev_id is None:
            dev_id = 0

        e = self.events[dev_id]
        #print "Checking events!"

        with self.lock:
            #Check if we have interrupts
            if (self.interrupts & (1 << dev_id)) > 0:
                #There are already existing interrupts, we're done
                return True
            #if we don't have interrupts clear the associated event
            #Clear the event, the interrupt handler will unblock this
            e.clear()

        #Now wait for interrupts
        if e.wait(wait_time):
            #Received an interrupt
            return True
        #Timed out while waiting for interrupts
        e.set()
        return False

    def interrupt_update_callback(self, interrupts):
        #print "Entered interrupt update callback"
        self.interrupts = interrupts
        for i in range(INTERRUPT_COUNT):
            if i == 0:
                if not self.events[i].is_set():
                    #self.s.Debug( "interrupt!")
                    self.events[i].set()

            elif (self.interrupts & (1 << i)) > 0:
                if not self.events[i].is_set():
                    self.events[i].set()

    def get_board_name(self):
        return "Artemis"

    def upload(self, filepath):
        artemis_utils.upload(self.vendor, self.product, self.sernum, filepath,
                             self.s)

    def program(self):
        artemis_utils.program(self.vendor, self.product, self.sernum, self.s)

    def ioctl(self, name, arg=None):
        raise AssertionError(
            "%s not implemented" % sys._getframe().f_code.co_name, self.s)

    def list_ioctl(self):
        raise AssertionError(
            "%s not implemented" % sys._getframe().f_code.co_name, self.s)

    def get_sdb_base_address(self):
        return 0x00000000
class Controller(object):

    def __init__(self, dev = None, vendor_id = 0x0403, product_id = 0x8530, status = None):
        super (Controller, self).__init__()

        self.vendor = vendor_id
        self.product = product_id
        self.s = status

    def upload(self, filepath):
        """
        Write a binary file to the the SPI Prom

        Args:
            filepath (String): Path to FPGA Binary (bin) image

        Returns (boolean):
            True: Successfully programmed
            False: Failed to program

        Raises:
            IOError:
                Failed to open binary file
        """
        binf = ""

        f = open(filepath, "r")
        binf = f.read()
        f.close()
        #Allow the users to handle File Errors

        #Open the SPI Flash Device
        manager = serial_flash_manager.SerialFlashManager(self.vendor, self.product, 2)
        flash = manager.get_flash_device()

        #Print out the device was found
        if self.s: self.s.Info("Found: %s" % str(flash))

        #Erase the flash
        if self.s: self.s.Info("Erasing the SPI Flash device, this can take a minute or two...")
        flash.bulk_erase()
        #Write the binary file
        if self.s: self.s.Info("Flash erased, writing binary image to PROM")
        flash.write(0x00, binf)

        #Verify the data was read
        binf_rb = flash.read(0x00, len(binf))
        binf_str = binf_rb.tostring()

        del flash
        del manager

        if binf_str != binf:
            raise NysaError("Image Verification Failed!, data written is not the same as data read")

    def program(self):
        """
        Send a program signal to the board, the FPGA will attempt to read the
        binary image file from the SPI prom. If successful the 'done' LED will
        illuminate

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing
        """
        bbc = BitBangController(self.vendor, self.product, 2)
        if self.s: self.s.Important("Set signals to input")
        bbc.set_pins_to_input()
        bbc.set_pins_to_output()
        bbc.program_high()
        time.sleep(.5)
        bbc.program_low()
        time.sleep(.2)
        bbc.program_high()
        bbc.pins_on()
        bbc.set_pins_to_input()

    def read_bin_file(self, filepath):
        """
        Read the binary image from the SPI Flash

        Args:
            filepath (String): Path to the filepath where the SPI image will
                be written to

        Returns:
            Nothing

        Raises:
            IOError:
                Problem openning file to write to
        """
        manager = serial_flash_manager.SerialFlashManager(self.vendor, self.product, 2)
        flash = manager.get_flash_device()

        #Don't know how long the binary file is so we need to read the entire
        #Image

        binf_rb = flash.read(0x00, len(flash))
        f = open(filepath, "w")
        binf_rb.tofile(f)
        f.close()

    def reset(self):
        """
        Send a reset signal to the board, this is the same as pressing the
        'reset' button

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing
        """
        bbc = BitBangController(self.vendor, self.product, 2)
        bbc.set_soft_reset_to_output()
        bbc.soft_reset_high()
        time.sleep(.2)
        bbc.soft_reset_low()
        time.sleep(.2)
        bbc.soft_reset_high()
        bbc.pins_on()
        bbc.set_pins_to_input()


    def set_sync_fifo_mode(self):
        """
        Change the mode of the FIFO to a synchronous FIFO

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing

        """
        fifo = FifoController(self.vendor, self.product)
        fifo.set_sync_fifo()

    def set_debug_mode(self):
        """
        Change the mode of the FIFO to a asynchronous FIFO

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Nothing
        """
        fifo = FifoController(self.vendor, self.product)
        fifo.set_async_fifo()


    def open_dev(self):
        """_open_dev

        Open an FTDI Communication Channel

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Exception
        """
        self.dev = Ftdi()
        frequency = 30.0E6
        latency  = 4
        #Ftdi.add_type(self.vendor, self.product, 0x700, "ft2232h")
        self.dev.open(self.vendor, self.product, 0)

        #Drain the input buffer
        self.dev.purge_buffers()

        #Reset
        #Enable MPSSE Mode
        self.dev.set_bitmode(0x00, Ftdi.BITMODE_SYNCFF)


        #Configure Clock
        frequency = self.dev._set_frequency(frequency)

        #Set Latency Timer
        self.dev.set_latency_timer(latency)

        #Set Chunk Size
        self.dev.write_data_set_chunksize(0x10000)
        self.dev.read_data_set_chunksize(0x10000)

        #Set the hardware flow control
        self.dev.set_flowctrl('hw')
        self.dev.purge_buffers()


    def ioctl(self, name, arg = None):
        raise AssertionError("%s not implemented" % sys._getframe().f_code.co_name)

    def list_ioctl(self):
        raise AssertionError("%s not implemented" % sys._getframe().f_code.co_name)
class _Dionysus (Nysa):
    """
    Dionysus

    Concrete Class that implemented Dionysus specific communication functions
    """

    def __init__(self, idVendor = 0x0403, idProduct = 0x8530, sernum = None, status = False):
        Nysa.__init__(self, status)
        self.vendor = idVendor
        self.product = idProduct
        self.sernum = sernum

        self.dev = None
        #Run a full garbage collection so any previous references to Dionysus will be removed
        gc.collect()
        self.lock = threading.Lock()


        self.dev = Ftdi()
        self._open_dev()
        self.name = "Dionysus"
        self.interrupts = 0x00
        self.events = []
        for i in range (INTERRUPT_COUNT):
            e = threading.Event()
            e.set()
            self.events.append(e)

        self.hwq = Queue.Queue(10)
        self.hrq = Queue.Queue(10)

        self.d = DionysusData()

        self.worker = WorkerThread(self.dev,
                                   self.hwq,
                                   self.hrq,
                                   self.d,
                                   self.lock,
                                   self.interrupt_update_callback)
        #Is there a way to indicate closing
        self.worker.setDaemon(True)
        self.worker.start()

        try:
            #XXX: Hack to fix a strange bug where FTDI
            #XXX: won't recognize Dionysus until a read and reset occurs
            self.ping()

        except NysaCommError:
            pass

        self.reset()



        '''
        #status = True
        self.reader_thread = ReaderThread(self.dev, self.interrupt_update_callback, self.lock, status = status)
        self.reader_thread.setName("Reader Thread")
        #XXX: Need to find a better way to shut this down
        self.reader_thread.setDaemon(True)
        self.reader_thread.start()
        '''

    def __del__(self):
        #if self.s: self.s.Debug( "Close reader thread")
        #self.lock.aquire()
        #if (self.reader_thread is not None) and self.reader_thread.isAlive():
        #    self.reader_thread.stop()
        #    self.s.Debug( "Waiting to join")
        #    self.reader_thread.join()
        #self.lock.release()
        #self.s = True
        #if self.s: self.s.Debug( "Reader thread joined")
        #self.dev.close()
        pass

    def _open_dev(self):
        """_open_dev

        Open an FTDI Communication Channel

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            Exception
        """
        #This frequency should go up to 60MHz
        frequency = 30.0E6
        #Latency can go down to 2 but there is a small chance there will be a
        #crash
        latency  = 2
        #Ftdi.add_type(self.vendor, self.product, 0x700, "ft2232h")
        self.dev.open(self.vendor, self.product, 0, serial = self.sernum)

        #Drain the input buffer
        self.dev.purge_buffers()

        #Reset
        #Configure Clock
        #frequency = self.dev._set_frequency(frequency)

        #Set Latency Timer
        self.dev.set_latency_timer(latency)

        #Set Chunk Size (Maximum Chunk size)
        self.dev.write_data_set_chunksize(0x10000)
        self.dev.read_data_set_chunksize(0x10000)

        #Set the hardware flow control
        self.dev.set_flowctrl('hw')
        self.dev.purge_buffers()
        #Enable MPSSE Mode
        self.dev.set_bitmode(0x00, Ftdi.BITMODE_SYNCFF)

    def ipc_comm_response(self, name):
        try:
            resp = self.hrq.get(block = True, timeout = DIONYSUS_QUEUE_TIMEOUT)
            if resp == DIONYSUS_RESP_OK:
                #print "%s got an OK response!" % current_thread().name
                return self.d.data
            else:
                raise NysaCommError("Dionysus response error %s: %d" % (name, resp))
        except Queue.Empty:
            raise NysaCommError("Dionysus error %s: timeout: %d" % (name, DIONYSUS_QUEUE_TIMEOUT))

    def read(self, address, length = 1, disable_auto_inc = False):
        """read

        read data from Dionysus

        Command Format

        ID 02 NN NN NN OO AA AA AA
           ID: ID Byte (0xCD)
           02: Read Command (12 for memory read)
           NN: Size of Read (3 Bytes)
           OO: Offset (for peripheral, part of address for mem)
           AA: Address (3 bytes for peripheral,
               (4 bytes including offset for mem)

        Args:
            address (long): Address of the register/memory to read
            length (int): Number of 32-bit words to read
            disable_auto_inc (bool): if true, auto increment feature will be
                disabled

        Returns:
            (Byte Array): A byte array containing the raw data returned from
            Dionysus

        Raises:
            NysaCommError
        """
        with self.lock:
            self.d.data = Array('B', [0xCD, 0x02])
            if address >= DIONYSUS_MEMORY_OFFSET:
                address -= DIONYSUS_MEMORY_OFFSET
                self.d.data[1] = self.d.data[1] | 0x10

            if disable_auto_inc:
                self.d.data[1] = self.d.data[1] | 0x20

            fmt_string = "%06X" % length
            self.d.data.fromstring(fmt_string.decode('hex'))

            #Add the address
            addr_string = "%08X" % (address & 0xFFFFFFFF)
            self.d.data.fromstring(addr_string.decode('hex'))
            self.d.length = length
            self.hwq.put(DIONYSUS_READ)
            return self.ipc_comm_response("read")

    def write(self, address, data, disable_auto_inc = False):
        """write

        Write data to a Nysa image

        Command Format

        ID 01 NN NN NN OO AA AA AA DD DD DD DD
           ID: ID Byte (0xCD)
           01: Write Command (11 for Memory Write)
           NN: Size of Write (3 Bytes)
           OO: Offset (for peripheral, part of address for mem)
           AA: Address (3 bytes for peripheral,
             #(4 bytes including offset for mem)
           DD: Data (4 bytes)

        Args:
            address (long): Address of the register/memory to write to
            memory_device (boolean):
                True: Memory device
                False: Peripheral device
            data (array of bytes): Array of raw bytes to send to the devcie
            disable_auto_inc (boolean): Default False
                Set to true if only writing to one memory address (FIFO Mode)

        Returns: Nothing

        Raises:
            NysaCommError
        """
        with self.lock:
            length = len(data) / 4
            #Create an Array with the identification byte and code for writing
            self.d.data = Array ('B', [0xCD, 0x01])
            if address >= DIONYSUS_MEMORY_OFFSET:
                address -= DIONYSUS_MEMORY_OFFSET
                self.d.data[1] = self.d.data[1] | 0x10
            if disable_auto_inc:
                self.d.data[1] = self.d.data[1] | 0x20

            #Append the length into the first 24 bits
            fmt_string = "%06X" % length
            self.d.data.fromstring(fmt_string.decode('hex'))
            addr_string = "%08X" % (address & 0xFFFFFFFF)
            self.d.data.fromstring(addr_string.decode('hex'))
            self.d.data.extend(data)
            self.hwq.put(DIONYSUS_WRITE)
            self.ipc_comm_response("write")

    def ping (self):
        """ping

        Command Format

        ID 00 00 00 00 00 00 00 00
            ID: ID Byte (0xCD)
            00: Ping Command
            00 00 00 00 00 00 00: Zeros

        Args:
            Nothing

        Returns:
            Nothing

        Raises:
            NysaCommError
        """
        with self.lock:
            self.hwq.put(DIONYSUS_PING)
            self.ipc_comm_response("ping")

    def reset (self):
        """ reset

        Software reset the Nysa FPGA Master, this may not actually reset the entire
        FPGA image

        ID 03 00 00 00
            ID: ID Byte (0xCD)
            00: Reset Command
            00 00 00: Zeros

        Args:
            Nothing

        Return:
            Nothing

        Raises:
            NysaCommError: Failue in communication
        """
        with self.lock:
            self.d.data = (self.vendor, self.product)
            self.hwq.put(DIONYSUS_RESET)
            self.ipc_comm_response("reset")

    def is_programmed(self):
        """
        Check if the FPGA is programmed

        Args:
            Nothing

        Return (Boolean):
            True: FPGA is programmed
            False: FPGA is not programmed

        Raises:
            NysaCommError: Failue in communication
        """
        with self.lock:
            self.d.data = (self.vendor, self.product)
            self.hwq.put(DIONYSUS_IS_PROGRAMMED)
            return self.ipc_comm_response("is programmed")

    def dump_core(self):
        """ dump_core

        Returns the state of the wishbone master priorto a reset, this is usefu for
        statusging a crash

        Command Format

        ID 0F 00 00 00 00 00 00 00
            ID: ID Byte (0xCD)
            0F: Dump Core Command
            00 00 00 00 00 00 00 00 00 00 00: Zeros

        Args:
            Nothing

        Returns:
            (Array of 32-bit Values) to be parsed by the core_analyzer utility

        Raises:
            NysaCommError: A failure in communication is detected
        """
        with self.lock:
            self.hwq.put(DIONYSUS_DUMP_CORE)
            return self.ipc_comm_response("dump core")

    def register_interrupt_callback(self, index, callback):
        """ register_interrupt

        Setup the thread to call the callback when an interrupt is detected

        Args:
            index (Integer): bit position of the device
                if the device is 1, then set index = 1
            callback: a function to call when an interrupt is detected

        Returns:
            Nothing

        Raises:
            Nothing
        """
        self.worker.register_interrupt_cb(index, callback)

    def unregister_interrupt_callback(self, index, callback = None):
        """ unregister_interrupt_callback

        Removes an interrupt callback from the reader thread list

        Args:
            index (Integer): bit position of the associated device
                EX: if the device that will receive callbacks is 1, index = 1
            callback: a function to remove from the callback list

        Returns:
            Nothing

        Raises:
            Nothing (This function fails quietly if ther callback is not found)
        """
        self.worker.unregister_interrupt_cb(index, callback)

    def wait_for_interrupts(self, wait_time = 1, dev_id = None):
        """ wait_for_interrupts

        listen for interrupts for the user specified amount of time

        The Nysa image will send a small packet of info to the host when a slave
        needs to send information to the host

        Response Format
        DC 01 00 00 00 II II II II
            DC: Inverted CD is the start of a response
            01: Interrupt ID
            00 00 00 00 00 00 00 00: Zeros, reserved for future use
            II II II II: 32-bit interrupts


        Args:
            wait_time (Integer): the amount of time in seconds to wait for an
                interrupt
            dev_id (Integer): Optional device id, if set the function will look
                if an interrupt has already been declared for that function, if
                so then return immediately otherwise setup a callback for this


        Returns (boolean):
            True: Interrupts were detected
            Falses: Interrupts were not detected

        Raises:
            NysaCommError: A failure in communication is detected
        """

        if dev_id is None:
            dev_id = 0

        e = self.events[dev_id]
        #print "Checking events!"

        with self.lock:
            #Check if we have interrupts
            if (self.interrupts & (1 << dev_id)) > 0:
                #There are already existing interrupts, we're done
                return True
            #if we don't have interrupts clear the associated event
            #Clear the event, the interrupt handler will unblock this
            e.clear()

        #Now wait for interrupts
        if e.wait(wait_time):
            #Received an interrupt
            return True
        #Timed out while waiting for interrupts
        e.set()
        return False

    def interrupt_update_callback(self, interrupts):
        #print "Entered interrupt update callback"
        self.interrupts = interrupts
        for i in range (INTERRUPT_COUNT):
            if i == 0:
                if not self.events[i].is_set():
                    #self.s.Debug( "interrupt!")
                    self.events[i].set()

            elif (self.interrupts & (1 << i)) > 0:
                if not self.events[i].is_set():
                    self.events[i].set()

    def get_board_name(self):
        return "Dionysus"

    def upload(self, filepath):
        dionysus_utils.upload(self.vendor, self.product, self.sernum, filepath, self.s)

    def program (self):
        dionysus_utils.program(self.vendor, self.product, self.sernum, self.s)

    def ioctl(self, name, arg = None):
        raise AssertionError("%s not implemented" % sys._getframe().f_code.co_name, self.s)

    def list_ioctl(self):
        raise AssertionError("%s not implemented" % sys._getframe().f_code.co_name, self.s)

    def get_sdb_base_address(self):
        return 0x00000000