Beispiel #1
0
 def test_gpio_loopback(self):
     """Check I/O.
     """
     if self.skip_loopback:
         raise SkipTest('Skip loopback test on multiport device')
     gpio = GpioAsyncController()
     direction = 0xFF & ~((1 << 4) - 1)  # 4 Out, 4 In
     gpio.configure(self.url, direction=direction, frequency=800000)
     for out in range(16):
         # print(f'Write {out:04b} -> {out << 4:08b}')
         gpio.write(out << 4)
         fback = gpio.read()
         lsbs = fback & ~direction
         msbs = fback >> 4
         # check inputs match outputs
         self.assertEqual(lsbs, out)
         # check level of outputs match the ones written
         self.assertEqual(msbs, out)
     outs = list([(out & 0xf) << 4 for out in range(1000)])
     gpio.write(outs)
     gpio.ftdi.read_data(512)
     for _ in range(len(outs)):
         _ = gpio.read(14)
     last = outs[-1] >> 4
     for _ in range(10):
         fbacks = gpio.read(1000)
         for fback in fbacks:
             lsbs = fback & ~direction
             msbs = fback >> 4
             # check inputs match last output
             self.assertEqual(lsbs, last)
             # check level of output match the last written
             self.assertEqual(msbs, last)
     gpio.close()
Beispiel #2
0
 def test_gpio_peek(self):
     """Check I/O.
     """
     gpio_in, gpio_out = GpioAsyncController(), GpioAsyncController()
     gpio_in.configure(self.urls[0], direction=0x00, frequency=1e6)
     gpio_out.configure(self.urls[1], direction=0xFF, frequency=1e6)
     for out in range(256):
         gpio_out.write(out)
         outv = gpio_out.read()
         inv = gpio_in.read()
         # check inputs match outputs
         self.assertEqual(inv, out)
         # check level of outputs match the ones written
         self.assertEqual(outv, out)
     gpio_in.close()
     gpio_out.close()
Beispiel #3
0
 def test_gpio_initial(self):
     """Check initial values.
     """
     if self.skip_loopback:
         raise SkipTest('Skip loopback test on multiport device')
     direction = 0xFF & ~((1 << 4) - 1)  # 4 Out, 4 In
     vftdi = self.loader.get_virtual_ftdi(1, 1)
     vport = vftdi.get_port(1)
     gpio = GpioAsyncController()
     for initial in (0xaf, 0xf0, 0x13, 0x00):
         gpio.configure(self.url,
                        direction=direction,
                        frequency=1e6,
                        initial=initial)
         expect = (initial & 0xF0) | (initial >> 4)
         self.assertEqual(vport.gpio, expect)
         gpio.close()
Beispiel #4
0
 def test_gpio_values(self):
     """Simple test to demonstrate bit-banging.
     """
     if self.skip_loopback:
         raise SkipTest('Skip loopback test on multiport device')
     direction = 0xFF & ~((1 << 4) - 1)  # 4 Out, 4 In
     gpio = GpioAsyncController()
     gpio.configure(self.url,
                    direction=direction,
                    frequency=1e6,
                    initial=0x0)
     port = gpio.get_gpio()  # useless, for API duck typing
     # legacy API: peek mode, 1 byte
     ingress = port.read()
     self.assertIsInstance(ingress, int)
     # peek mode always gives a single byte output
     ingress = port.read(peek=True)
     self.assertIsInstance(ingress, int)
     # stream mode always gives a bytes buffer
     port.write([0xaa for _ in range(256)])
     ingress = port.read(100, peek=False, noflush=False)
     self.assertIsInstance(ingress, bytes)
     if not VirtLoader:
         # the virtual task may sometimes not be triggered soon enough
         self.assertGreater(len(ingress), 2)
     # direct mode is not available with multi-byte mode
     self.assertRaises(ValueError, port.read, 3, True)
     ingress = port.read(3)
     self.assertIsInstance(ingress, bytes)
     if not VirtLoader:
         # the virtual task may sometimes not be triggered soon enough
         self.assertGreater(len(ingress), 0)
     self.assertLessEqual(len(ingress), 3)
     port.write(0x00)
     port.write(0xFF)
     # only 8 bit values are accepted
     self.assertRaises(ValueError, port.write, 0x100)
     port.write([0x00, 0xFF, 0x00])
     port.write(bytes([0x00, 0xFF, 0x00]))
     # only 8 bit values are accepted
     self.assertRaises(ValueError, port.write, [0x00, 0x100, 0x00])
     # check direction API
     port.set_direction(0xFF, 0xFF & ~direction)
     gpio.close()
Beispiel #5
0
	def transmitAsync(self, halfPeriod=0.005):
		gpio = GpioAsyncController()
		gpio.configure(self.device, self.directionArray[0])
		for i in range(len(self.txArray)):
			if (self.directionArray[i] != self.directionArray[i-1]):
				time.sleep(halfPeriod)
				gpio.close()
				gpio = GpioAsyncController()
				gpio.configure(self.device, self.directionArray[i])
				time.sleep(halfPeriod)
				#print("new direction: "+hex(self.directionArray[i]))
			# read the state of the port
			self.rxArray.append( gpio.read() )
			#print("read: "+hex(self.rxArray[-1]))
			# write to the outputs (0 is OK to write to inputs)
			gpio.write(self.txArray[i])
			#print("write: "+hex(self.txArray[i]))
			# wait half a clock cycle
			time.sleep(halfPeriod)
		gpio.close()
Beispiel #6
0
 def test_gpio_baudate(self):
     # this test requires an external device (logic analyser or scope) to
     # check the bitbang read and bitbang write signal (BB_RD, BB_WR) and
     # mesure their frequency. The EEPROM should be configured to enable
     # those signal on some of the CBUS pins, for example.
     gpio = GpioAsyncController()
     direction = 0xFF & ~((1 << 4) - 1)  # 4 Out, 4 In
     gpio.configure(self.url, direction=direction)
     buf = bytes([0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00])
     freqs = [50e3, 200e3, 1e6, 3e6]
     if gpio.ftdi.is_H_series:
         freqs.extend([6e6, 10e6, 12e6])
     gpio.read(128)
     for freq in freqs:
         # set the bitbang refresh rate
         gpio.set_frequency(freq)
         self.assertEqual(gpio.frequency, freq)
         # be sure to leave enough time to purge buffers (HW FIFO) or
         # the frequency changes occur on the current buffer...
         gpio.write(buf)
         gpio.read(128)
         sleep(0.01)
     gpio.close()
Beispiel #7
0
    def test_gpio_freeze(self):
        """Simple test to demonstrate freeze on close.

           For now, it requires a logic analyzer to verify the output,
           this is not automatically validated by SW
        """
        direction = 0xFF & ~((1 << 4) - 1)  # 4 Out, 4 In
        gpio = GpioAsyncController()
        gpio.configure(self.url,
                       direction=direction,
                       frequency=1e3,
                       initial=0x0)
        port = gpio.get_gpio()
        # emit a sequence as a visual marker on b3,b2,b1,b0
        port.write([x << 4 for x in range(16)])
        sleep(0.01)
        # write 0b0110 to the port
        port.write(0x6 << 4)
        sleep(0.001)
        # close w/o freeze: all the outputs should be reset (usually 0b1111)
        # it might need pull up (or pull down) to observe the change as
        # output are not high-Z.
        gpio.close()
        sleep(0.01)
        gpio.configure(self.url,
                       direction=direction,
                       frequency=1e3,
                       initial=0x0)
        port = gpio.get_gpio()
        # emit a sequence as a visual marker with on b3 and b1
        port.write([(x << 4) & 0x90 for x in range(16)])
        sleep(0.01)
        # write 0b0110 to the port
        port.write(0x6 << 4)
        sleep(0.01)
        # close w/ freeze: outputs should not be reset (usually 0b0110)
        gpio.close(True)
Beispiel #8
0
 def test_gpio_stream(self):
     """Check I/O streaming
     """
     if VirtLoader:
         # this would require to synchronize virtual clock between all ports
         # which is not supported by the virtual framework
         raise SkipTest('Skip gpio stream with virtual device')
     gpio_in, gpio_out = GpioAsyncController(), GpioAsyncController()
     gpio_in.configure(self.urls[0], direction=0x00, frequency=1e4)
     gpio_out.configure(self.urls[1], direction=0xFF, frequency=1e4)
     outs = bytes(range(256))
     gpio_out.write(outs)
     # read @ same speed (and same clock source, so no jitter), flushing
     # the byffer which has been filled since the port has been opened
     ins = gpio_in.read(len(outs))
     qout = deque(outs)
     ifirst = ins[0]
     # the inout stream should be a copy of the output stream, minus a
     # couple of missing samples that did not get captured while output
     # was streaming but read command has not been yet received.
     while qout:
         if qout[0] == ifirst:
             break
         qout.popleft()
     # offset is the count of missed bytes
     offset = len(ins) - len(qout)
     self.assertGreater(offset, 0)  # no more output than input
     self.assertLess(offset, 16)  # seems to be in the 6..12 range
     # print('Offset', offset)
     # check that the remaining sequence match
     for sout, sin in zip(qout, ins):
         #print(f'{sout:08b} --> {sin:08b}')
         # check inputs match outputs
         self.assertEqual(sout, sin)
     gpio_in.close()
     gpio_out.close()
Beispiel #9
0
class FtdiDevice:
    """FTDI FT2232H SPI master to access FPGA Control/Status Registers (CSR)
    plus some GPIO control.

    SPI parameters:
      - FTDI channel B:
        * BDBUS0 - SCLK
        * BDBUS1 - MOSI
        * BDBUS2 - MISO
        * BDBUS3 - CSn
      - slave;
      - mode 0 only;
      - most significant bit transmitted first;
      - byte order from high to low;
      - SCK frequency must at least 8 lower than system frequency.

    There are 2 types of SPI transactions:
      - incremental burst -- address increments internally after every data (array)
      - fixed burst -- one adreess, multiple data (FIFO)
    Transaction is done with 8-bit address and 16 bit data words.
    Transaction format:
      - control word (3 bytes):
          * bit 23 -- write (1) or read (0)
          * bit 22 -- burst incremental (1) or fixed (0)
          * bit 21 .. 8 -- 14 bit length (0 - 1 data word, 1 - 2 data words, etc)
          * bits 7 .. 0 -- 8 bit address
      - data word (2 bytes) 1 .. N:
          * bits 15 .. 0 -- data to be written or readen

    GPIO parameters:
      - ADBUS7 - output - active low reset for FPGA configuration (ICE_RESET)
      - BDBUS7 - output - active high reset for FPGA logic (ICE_RESET_FT)
    """

    GPIO_RESET_LOGIC_POS = 7
    GPIO_RESET_CONFIG_POS = 7

    def __init__(self, ftdi_url, spi_freq=1E6):
        """Configure the FTDI interface.

        Keyword arguments:
         ftdi_url -- device url, which can be obtained by Ftdi.show_devices()
         freq -- SPI frequency up to 8E6 (for FPGA running on 64 MHz)
        """
        # Configure SPI master
        self._spi_ctrl = SpiController()
        self._spi_ctrl.configure(ftdi_url + '2')  # second port - channel B
        self._spi_port = self._spi_ctrl.get_port(cs=0, freq=spi_freq, mode=0)
        # Configure FPGA logic reset (ICE_RESET_FT)
        self._spi_gpio = self._spi_ctrl.get_gpio()
        self._spi_gpio.set_direction(1 << self.GPIO_RESET_LOGIC_POS,
                                     1 << self.GPIO_RESET_LOGIC_POS)
        self._spi_gpio.write(0)
        # Configure FPGA configuration reset (ICE_RESET)
        self._gpio_ctrl = GpioAsyncController()
        self._gpio_ctrl.configure(
            ftdi_url + '1',  # first port - channel A
            direction=(1 << self.GPIO_RESET_CONFIG_POS),
            frequency=1e6,
            initial=(1 << self.GPIO_RESET_CONFIG_POS))
        self._gpio_ctrl.write(1 << self.GPIO_RESET_CONFIG_POS)

    def _int_to_bytes(self, i, length=2):
        """Convert integer to bytes"""
        return int.to_bytes(i, length=length, byteorder='big', signed=False)

    def _words_to_bytes(self, words_list):
        """Convert list with 16 bit words to bytes"""
        bytes_str_list = [self._int_to_bytes(w, length=2) for w in words_list]
        return b''.join(bytes_str_list)  # concatenate all strings

    def _bytes_to_words(self, bytes_str):
        """Convert bytes string to list with 16 bit words"""
        return [
            int.from_bytes(bytes_str[b * 2:b * 2 + 2],
                           byteorder='big',
                           signed=False) for b in range(len(bytes_str) // 2)
        ]

    def _prepare_ctrl_word(self, addr, len, burst, wr):
        """Prepare control word for exchange.

           Keyword arguments:
             addr -- 8 bit address
             len -- number of 16 bit data words to write/read (2^14 max)
             burst -- 'fixed' address the same for every data, 'incr' - address + 1 for every next data word
             wr -- 1 for write operation, 0 - for read
        """
        ctrl_word = 0
        ctrl_word |= (wr << 23)
        ctrl_word |= ((burst == 'incr') << 22)
        ctrl_word |= (((len - 1) & 0x3FFF) << 8)
        ctrl_word |= ((addr & 0xFF) << 0)
        return ctrl_word

    def spi_read(self, addr, len=1, burst='fixed'):
        """Read data from address via SPI.

           Keyword arguments:
             addr -- 8 bit address
             len -- number of 16 bit data words to write/read (2^14 max)
             burst -- 'fixed' address the same for every data, 'incr' - address + 1 for every next data word
           Return:
             list of size 'len' with 16 bit data words
        """
        ctrl_word = self._prepare_ctrl_word(addr, len, burst, wr=0)
        rbytes = self._spi_port.exchange(self._int_to_bytes(ctrl_word, 3),
                                         len * 2)
        return self._bytes_to_words(rbytes)

    def spi_write(self, addr, data, burst='fixed'):
        """Write data to address via SPI.

           Keyword arguments:
             addr -- 8 bit address
             data -- list with 16 bit data words to write (list length 2^14 max)
             burst -- 'fixed' address the same for every data, 'incr' - address + 1 for every next data word
         """
        ctrl_word = self._prepare_ctrl_word(addr, len(data), burst, wr=1)
        wbytes = self._int_to_bytes(ctrl_word, 3) + self._words_to_bytes(data)
        self._spi_port.exchange(wbytes)

    def reset_logic_on(self):
        """Activate reset pin ICE_RESET_FT"""
        self._spi_gpio.write((1 << self.GPIO_RESET_LOGIC_POS)
                             | self._spi_gpio.read())

    def reset_logic_off(self):
        """Deactivate reset pin ICE_RESET_FT"""
        self._spi_gpio.write(~(1 << self.GPIO_RESET_LOGIC_POS)
                             & self._spi_gpio.read())

    def reset_config_on(self):
        """Activate reset pin ICE_RESET"""
        self._gpio_ctrl.write(~(1 << self.GPIO_RESET_CONFIG_POS)
                              & self._gpio_ctrl.read())

    def reset_config_off(self):
        """Deactivate reset pin ICE_RESET"""
        self._gpio_ctrl.write((1 << self.GPIO_RESET_LOGIC_POS)
                              | self._gpio_ctrl.read())

    def close_connection(self):
        """Close FTDI interface"""
        self._spi_ctrl.terminate()
        self._gpio_ctrl.close()
Beispiel #10
0
from pyftdi.ftdi import Ftdi
from pyftdi.gpio import (GpioAsyncController, GpioSyncController,
                         GpioMpsseController)

gpio1 = GpioAsyncController()
gpio2 = GpioAsyncController()
gpio1.configure('ftdi:///1', direction=0x95)
gpio2.configure('ftdi:///2', direction=0xbb)
# later, reconfigure BD2 as input and BD7 as output
# gpio.set_direction(0x84, 0x80)

pins1 = gpio1.read()
pins1 &= ~gpio1.direction
pins2 = gpio2.read()
pins2 &= ~gpio2.direction

print("pins1:", hex(pins1))
print("pins2:", hex(pins2))

gpio1.close()
gpio2.close()