210 lines
6.7 KiB
Plaintext
210 lines
6.7 KiB
Plaintext
|
#!/usr/bin/env python3
|
||
|
# Copyright (c) 2019-2020, XMOS Ltd, All rights reserved
|
||
|
# requires dtparam=spi=on in /boot/config.txt
|
||
|
|
||
|
"""
|
||
|
This script configures the XVF3510 board in boot from SPI slave and load a
|
||
|
binary file. It requires a bin file as input parameter.
|
||
|
"""
|
||
|
|
||
|
import sys
|
||
|
import os
|
||
|
import time
|
||
|
import argparse
|
||
|
import spidev
|
||
|
import RPi.GPIO as GPIO
|
||
|
from smbus import SMBus
|
||
|
from pathlib import Path
|
||
|
|
||
|
if sys.version[0] != '3':
|
||
|
print("Run this script with Python 3")
|
||
|
sys.exit(1)
|
||
|
|
||
|
def bit_reversed_byte(byte_to_reverse):
|
||
|
"""
|
||
|
Function to reverse the bit-order of a byte
|
||
|
|
||
|
Args:
|
||
|
byte_to_reverse: byte to process
|
||
|
|
||
|
Retruns:
|
||
|
byte in reversed order
|
||
|
"""
|
||
|
return int('{:08b}'.format(byte_to_reverse)[::-1], 2)
|
||
|
|
||
|
def set_boot_sel():
|
||
|
"""
|
||
|
Function to set XVF3510 board in SPI slave boot mode
|
||
|
|
||
|
Args:
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
None
|
||
|
"""
|
||
|
|
||
|
bus = SMBus(1)
|
||
|
|
||
|
# reset BOOT_SEL
|
||
|
bus.write_byte_data(0x20, 3, 0xFE)
|
||
|
bus.write_byte_data(0x20, 7, 0xFF)
|
||
|
|
||
|
state = {}
|
||
|
for i in [2, 6]:
|
||
|
state[i] = bus.read_byte_data(0x20, i)
|
||
|
|
||
|
# start reset
|
||
|
data_to_write = 0x00 | (state[2] & 0xDF)
|
||
|
bus.write_byte_data(0x20, 2, data_to_write)
|
||
|
data_to_write = 0x00 | (state[6] & 0xDF)
|
||
|
bus.write_byte_data(0x20, 6, data_to_write)
|
||
|
# set BOOT_SEL high
|
||
|
data_to_write = 0x01
|
||
|
bus.write_byte_data(0x20, 3, data_to_write)
|
||
|
data_to_write = 0xFE
|
||
|
bus.write_byte_data(0x20, 7, data_to_write)
|
||
|
# stop reset
|
||
|
data_to_write = 0x20 | (state[2] & 0xDF)
|
||
|
bus.write_byte_data(0x20, 2, data_to_write)
|
||
|
data_to_write = 0x20 | (state[6] & 0xDF)
|
||
|
bus.write_byte_data(0x20, 6, data_to_write)
|
||
|
|
||
|
|
||
|
|
||
|
def send_image(bin_filename, verbose=False, max_spi_speed_mhz = 5, block_transfer_pause_ms = 1, direct = False, delay = False):
|
||
|
"""
|
||
|
Function to send the given image to the device via SPI slave
|
||
|
|
||
|
Args:
|
||
|
bin_filename: binary file containing the image to boot
|
||
|
verbose: flag to print debug printouts
|
||
|
direct: Use Pi GPIO outputs rather than the XVF3510 Pi HAT
|
||
|
delay: Release BootSel early to delay startup on version 4.0.0 onwards
|
||
|
|
||
|
Returns:
|
||
|
None
|
||
|
"""
|
||
|
|
||
|
if direct:
|
||
|
#setup GPIO
|
||
|
GPIO.setmode(GPIO.BOARD)
|
||
|
GPIO.setwarnings(False)
|
||
|
|
||
|
#boot_sel = 8
|
||
|
#rst_n = 10
|
||
|
'''
|
||
|
Mycroft board update
|
||
|
'''
|
||
|
#GPIO.setmode(GPIO.BCM)
|
||
|
|
||
|
boot_sel = 37 #26 #= 25 # fix these numbers to be Wiring Pi
|
||
|
rst_n = 13 #27 #= 2
|
||
|
GPIO.setup(boot_sel, GPIO.IN) # Normally, the Pi should not drive this
|
||
|
GPIO.setup(rst_n, GPIO.OUT, initial=GPIO.HIGH)
|
||
|
|
||
|
#setup SPI
|
||
|
spi = spidev.SpiDev()
|
||
|
bus_spi = 0
|
||
|
device = 0
|
||
|
spi.open(bus_spi, device)
|
||
|
|
||
|
#SPI Settings
|
||
|
spi.max_speed_hz = int(max_spi_speed_mhz * 1000000)
|
||
|
spi.mode = 0b00 #XMOS supports 00 or 11
|
||
|
|
||
|
spi_block_size = 4096 #Limitation in spidev and xfer2 doesn't work!
|
||
|
|
||
|
if direct:
|
||
|
GPIO.output(rst_n, 0)
|
||
|
GPIO.setup(boot_sel, GPIO.OUT, initial=GPIO.HIGH)
|
||
|
GPIO.output(rst_n, 1)
|
||
|
else:
|
||
|
set_boot_sel()
|
||
|
|
||
|
# Create a table to map byte values to their bit-reversed values
|
||
|
reverse_table = [bit_reversed_byte(byte) for byte in range(256)]
|
||
|
|
||
|
data = []
|
||
|
with open(bin_filename, "rb") as f:
|
||
|
bytes_read = f.read()
|
||
|
data = list(bytes_read)
|
||
|
binary_size = len(data)
|
||
|
block_count = 0
|
||
|
print('Read file "{0}" size: {1} Bytes'.format(args.bin_filename, binary_size))
|
||
|
if binary_size % spi_block_size != 0:
|
||
|
print("Warning - binary file not a multiple of {} - {} remainder".format( \
|
||
|
spi_block_size, binary_size % spi_block_size))
|
||
|
while binary_size > 0:
|
||
|
block = [reverse_table[byte] for byte in data[:spi_block_size]]
|
||
|
del data[:spi_block_size]
|
||
|
binary_size = len(data)
|
||
|
if verbose:
|
||
|
print("Sending {} Bytes in block {} checksum 0x{:X}".format( \
|
||
|
len(block), block_count, sum(block)))
|
||
|
spi.xfer(block)
|
||
|
|
||
|
if block_count == 0:
|
||
|
#Long delay for PLL reboot
|
||
|
time.sleep(0.1)
|
||
|
|
||
|
if delay:
|
||
|
# release boot_sel early to prevent startup
|
||
|
if direct:
|
||
|
GPIO.setup(boot_sel, GPIO.IN)
|
||
|
else:
|
||
|
#bus = smbus.SMBus(1)
|
||
|
bus = SMBus(1)
|
||
|
data_to_write = 0xFE
|
||
|
bus.write_byte_data(0x20, 3, data_to_write)
|
||
|
data_to_write = 0xFF
|
||
|
bus.write_byte_data(0x20, 7, data_to_write)
|
||
|
|
||
|
elif binary_size > 0:
|
||
|
time.sleep(block_transfer_pause_ms / 1000)
|
||
|
block_count += 1
|
||
|
print("Sending complete")
|
||
|
|
||
|
if direct:
|
||
|
GPIO.setup(boot_sel, GPIO.IN) # Once booted, the Pi should not need to drive boot_sel
|
||
|
GPIO.setup(rst_n, GPIO.OUT, initial=GPIO.HIGH) # Once booted, the Pi should not need to drive reset
|
||
|
#GPIO.cleanup()
|
||
|
else:
|
||
|
#bus = smbus.SMBus(1)
|
||
|
bus = SMBus(1)
|
||
|
|
||
|
# reset BOOT_SEL
|
||
|
data_to_write = 0xFE
|
||
|
bus.write_byte_data(0x20, 3, data_to_write)
|
||
|
data_to_write = 0xFF
|
||
|
bus.write_byte_data(0x20, 7, data_to_write)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
start_time = time.time()
|
||
|
parser = argparse.ArgumentParser(description='Load an image via SPI slave from an RPi')
|
||
|
parser.add_argument('bin_filename', help='binary file name')
|
||
|
parser.add_argument('--direct', action='store_true', \
|
||
|
help='Use just direct GPIO outputs rather than using the XVF3510 Development Kit Pi HAT')
|
||
|
parser.add_argument('--delay', action='store_true', \
|
||
|
help='Delay xvf3510 device start. Release the BootSel pin early to prevent the XVF3510 (V4.0.0 onwards) from starting with the default I2S configuration. This gives AP a chance to configure and start the XVF3510 device.')
|
||
|
parser.add_argument('--max-spi-speed-mhz', type=float, default=5, \
|
||
|
help='Max SPI speed in MHz')
|
||
|
parser.add_argument('--block-transfer-pause-ms', type=float, default=1, \
|
||
|
help='pause between SPI transfers in milliseconds, default 1ms')
|
||
|
parser.add_argument('--verbose', action='store_true', \
|
||
|
help='print debug information')
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
if not Path(args.bin_filename).is_file():
|
||
|
print("Error: input file {} not found".format(args.bin_filename))
|
||
|
exit(1)
|
||
|
|
||
|
send_image(args.bin_filename, args.verbose, args.max_spi_speed_mhz, args.block_transfer_pause_ms, args.direct, args.delay)
|
||
|
|
||
|
end_time = time.time()
|
||
|
if args.verbose:
|
||
|
print("Sending image took {:.3} seconds".format(end_time - start_time))
|