blob: 598c609e5b09e9b15bec54b8e0a92a3d486f8944 [file] [log] [blame]
#!/usr/bin/env python3
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
import serial
import io
import click
import os
import os.path as path
from datetime import datetime
@click.argument('infile')
@click.option('-u', '--uart', required=True, help='uart port')
@click.option('-r', '--reset_script', required=False, help='Custom reset script to switch to serial load')
@click.command(help='Load the provided file using serial load protocol')
def load(infile, uart, reset_script):
try:
ser = serial.Serial(port=uart, baudrate=115200, timeout=0.010,
bytesize=8, stopbits=serial.STOPBITS_ONE)
except serial.SerialException:
raise SystemExit("Failed to open serial port")
# drain serial port buffer
try:
while True:
data = ser.read(1)
if len(data) == 0:
break
except serial.SerialException:
raise SystemExit("Failed to open serial port")
try:
f = open(infile, "rb")
except IOError:
raise SystemExit("Failed to open input file")
data = f.read()
crc = 0
for i in range(len(data)):
crc = crc ^ data[i]
# serial load protocol is the following:
# - on reset, board sends 0x2 and listens for 100ms or so
# - host sends 0x1 followed by length of file to transfer
# - board responds with 0x6
# - host sends file
# - board responds with bytewise XOR of file
# - If XOR matches, host sends 0x6 as final ack
# - board boots image after receiving ACK from host
som_detected = False
reset_triggered = False
prev = datetime.now()
reset_delay_us = 250000
if reset_script is None:
print("Please reset board to enter ROM uart recovery")
while True:
elapsed = datetime.now() - prev
if elapsed.seconds >= 15:
raise SystemExit("Failed to receive SOM, aborting")
if not som_detected and not reset_triggered:
if reset_script and elapsed.microseconds >= reset_delay_us:
print("Triggering SWD reset...")
# Run in background
os.system(reset_script + " &")
reset_triggered = True
msg = ser.read(1)
if len(msg) > 0 and msg[0] == 0x2:
print(msg)
som_detected = True
print("Detected serial boot protocol")
msg = bytes([0x1])
msg += bytes([len(data) & 0xff])
msg += bytes([len(data) >> 8])
ser.write(msg)
ser.timeout = 10
msg = ser.read()
if len(msg) == 0:
raise SystemExit("Failed to receive SOH ACK, aborting")
if msg[0] != 0x6:
raise SystemExit("Received invalid response, aborting")
print("Loading application to RAM")
try:
ser.write(data)
except serial.SerialException:
raise SystemExit("Failed to write executable, aborting")
msg = ser.read()
if len(msg) == 0:
raise SystemExit("Failed to receive XOR of bytes")
if crc != msg[0]:
raise SystemExit("Failed CRC check")
msg = bytes([0x6])
try:
ser.write(msg)
except serial.SerialException:
raise SystemExit("Failed to write final ACK, aborting")
print("Successfully loaded RAM, board will now boot executable")
break
else:
continue
@click.group()
def cli():
pass
cli.add_command(load)
if __name__ == '__main__':
cli()