| #!/usr/bin/env python3 | |
| # Copyright lowRISC contributors. | |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| r"""Simple Tool for FPGA SPI experiments | |
| """ | |
| import argparse | |
| import logging as log | |
| import os | |
| import subprocess | |
| import sys | |
| import time | |
| import pkg_resources # part of setuptools | |
| from pyftdi.spi import SpiController | |
| def show_and_exit(clitool, packages): | |
| util_path = os.path.dirname(os.path.realpath(clitool)) | |
| os.chdir(util_path) | |
| ver = subprocess.run( | |
| ["git", "describe", "--always", "--dirty", "--broken"], | |
| stdout=subprocess.PIPE).stdout.strip().decode('ascii') | |
| if (ver == ''): | |
| ver = 'not found (not in Git repository?)' | |
| sys.stderr.write(clitool + " Git version " + ver + '\n') | |
| for p in packages: | |
| sys.stderr.write(p + ' ' + pkg_resources.require(p)[0].version + '\n') | |
| exit(0) | |
| USAGE = """ | |
| spitest [options] text [text ...] | |
| """ | |
| def main(): | |
| done_stdin = False | |
| parser = argparse.ArgumentParser( | |
| prog="spitest", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| usage=USAGE, | |
| description=__doc__) | |
| parser.add_argument( | |
| '--version', action='store_true', help='Show version and exit') | |
| parser.add_argument( | |
| '-v', | |
| '--verbose', | |
| action='store_true', | |
| help='Verbose output during processing') | |
| parser.add_argument( | |
| '-f', | |
| '--flippy', | |
| action='store_true', | |
| help='Flip the SPI/JTAG control GPIO 10 times and exit') | |
| parser.add_argument( | |
| '-l', | |
| '--length', | |
| type=int, | |
| action='store', | |
| help='Construct and send a message of specified length') | |
| parser.add_argument( | |
| '-j', | |
| '--jtag', | |
| action='store_true', | |
| help='Set SPI/JTAG control to JTAG and exit') | |
| parser.add_argument( | |
| 'message', | |
| nargs='*', | |
| metavar='input', | |
| default='1234', | |
| help='message to send in 4 byte chunks') | |
| args = parser.parse_args() | |
| if args.version: | |
| show_and_exit(__file__, ["pyftdi"]) | |
| if (args.verbose): | |
| log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) | |
| else: | |
| log.basicConfig(format="%(levelname)s: %(message)s") | |
| # Instanciate a SPI controller | |
| spi = SpiController(cs_count=1) | |
| # interfaces start from 1 here, so this is Channel A (called 0 in jtag) | |
| spi.configure('ftdi://ftdi:2232h/1') | |
| # Get a port to a SPI device w/ /CS on A*BUS3 and SPI mode 0 @ 1MHz | |
| device = spi.get_port(cs=0, freq=1E6, mode=0) | |
| # Get GPIO port to manage extra pins | |
| # BUS4 = JTAG TRST_N, BUS5 = JTAG SRST_N, BUS6 = JTAG_SPIN | |
| # Note: something makes FTDI default to BUS6 low, selected that for SPI | |
| # otherwise SRST being default low holds the chip in reset | |
| # pyftdi Set Direction also forces the output to zero | |
| # so initially make SRST an input w/pullup in FPGA in case SPI/JTAG was | |
| # initially JTAG | |
| gpio = spi.get_gpio() | |
| gpio.set_direction(0x40, 0x40) | |
| time.sleep(1) | |
| gpio.set_direction(0x70, 0x70) | |
| if args.jtag: | |
| gpio.write(0x70) | |
| return | |
| gpio.write(0x30) | |
| if args.flippy: | |
| for i in range(10): | |
| print("Select SPI") | |
| gpio.write(0x30) | |
| time.sleep(2) | |
| print("Select JTAG") | |
| gpio.write(0x70) | |
| time.sleep(2) | |
| return | |
| print("Select SPI") | |
| gpio.write(0x30) | |
| # Synchronous exchange with the remote SPI device | |
| if args.length: | |
| s = '' | |
| for i in range(args.length): | |
| s += hex(i & 15)[-1] | |
| else: | |
| s = '' | |
| for m in args.message: | |
| s += m + ' ' | |
| s = s[:-1] # remove extra space put on end | |
| # pad to ensure multiple of 4 bytes | |
| filled = len(s) % 4 | |
| if filled: | |
| s += '....' [filled:] | |
| while len(s): | |
| write_buf = bytes(s[:4], encoding='utf8') | |
| read_buf = device.exchange(write_buf, duplex=True) | |
| print("Got " + str(read_buf)) | |
| s = s[4:] | |
| if __name__ == '__main__': | |
| main() |