vos/ambiq-hal-sys/ambiq-sparkfun-sdk/tools/apollo3_scripts/uart_wired_update.py

346 lines
14 KiB
Python
Raw Permalink Normal View History

2022-10-24 06:45:43 +00:00
#!/usr/bin/env python3
# UART wired update host for Corvette Bootloader
import argparse
import serial
import sys
import array
import os
import binascii
from am_defines import *
#******************************************************************************
#
# Main function
#
#******************************************************************************
def main():
# Open a serial port, and communicate with Device
#
# We will use a UART timeout value of 12 second. This should be long
# enough that the slave will respond to us, but short enough that a human
# operator should see errors quickly..
# Max flashing time depends on the amount of SRAM available.
# For very large images, the flashing happens page by page.
# However if the image can fit in the free SRAM, it could take a long time
# for the whole image to be flashed at the end.
# The largest image which can be stored depends on the max SRAM.
# Assuming worst case ~100 ms/page of flashing time, and allowing for the
# image to be close to occupying full SRAM (256K) which is 128 pages.
print('Connecting with Corvette over serial port {}...'.format(args.port), flush=True)
with serial.Serial(args.port, args.baud, timeout=12) as ser:
connect_device(ser)
print('Done.')
#******************************************************************************
#
# Communicate with Device
#
# Given a serial port, connects to the target device using the
# UART.
#
#******************************************************************************
def connect_device(ser):
# Send Hello
#generate mutable byte array for the header
hello = bytearray([0x00]*4);
fill_word(hello, 0, ((8 << 16) | AM_SECBOOT_WIRED_MSGTYPE_HELLO))
print('Sending Hello.')
response = send_command(hello, 88, ser)
print("Received response for Hello")
word = word_from_bytes(response, 4)
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_STATUS):
# Received Status
print("Received Status")
print("length = ", hex((word >> 16)))
print("version = ", hex(word_from_bytes(response, 8)))
print("Max Storage = ", hex(word_from_bytes(response, 12)))
print("Status = ", hex(word_from_bytes(response, 16)))
print("State = ", hex(word_from_bytes(response, 20)))
print("AMInfo = ")
for x in range(24, 88, 4):
print(hex(word_from_bytes(response, x)))
abort = args.abort
if (abort != -1):
# Send OTA Desc
print('Sending Abort command.')
abortMsg = bytearray([0x00]*8);
fill_word(abortMsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_ABORT))
fill_word(abortMsg, 4, abort)
send_ackd_command(abortMsg, ser)
otadescaddr = args.otadesc
if (otadescaddr != 0xFFFFFFFF):
# Send OTA Desc
print('Sending OTA Descriptor = ', hex(otadescaddr))
otaDesc = bytearray([0x00]*8);
fill_word(otaDesc, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_OTADESC))
fill_word(otaDesc, 4, otadescaddr)
send_ackd_command(otaDesc, ser)
imageType = args.imagetype
if (args.binfile != ''):
# Read the binary file from the command line.
with open(args.binfile, mode='rb') as binfile:
application = binfile.read()
# Gather the important binary metadata.
totalLen = len(application)
# Send Update command
print('Sending Update Command.')
# It is assumed that maxSize is 256b multiple
maxImageSize = args.split
if ((maxImageSize & (FLASH_PAGE_SIZE - 1)) != 0):
print ("split needs to be multiple of flash page size")
return
# Each Block of image consists of AM_WU_IMAGEHDR_SIZE Bytes Image header and the Image blob
maxUpdateSize = AM_WU_IMAGEHDR_SIZE + maxImageSize
numUpdates = (totalLen + maxUpdateSize - 1) // maxUpdateSize # Integer division
print("number of updates needed = ", numUpdates)
end = totalLen
for numUpdates in range(numUpdates, 0 , -1):
start = (numUpdates-1)*maxUpdateSize
crc = crc32(application[start:end])
applen = end - start
print("Sending block of size ", str(hex(applen)), " from ", str(hex(start)), " to ", str(hex(end)))
end = end - applen
update = bytearray([0x00]*16);
fill_word(update, 0, ((20 << 16) | AM_SECBOOT_WIRED_MSGTYPE_UPDATE))
fill_word(update, 4, applen)
fill_word(update, 8, crc)
# Size = 0 => We're not piggybacking any data to IMAGE command
fill_word(update, 12, 0)
send_ackd_command(update, ser)
# Loop over the bytes in the image, and send them to the target.
resp = 0
# Max chunk size is AM_MAX_UART_MSG_SIZE adjusted for the header for Data message
maxChunkSize = AM_MAX_UART_MSG_SIZE - 12
for x in range(0, applen, maxChunkSize):
# Split the application into chunks of maxChunkSize bytes.
# This is the max chunk size supported by the UART bootloader
if ((x + maxChunkSize) > applen):
chunk = application[start+x:start+applen]
# print(str(hex(start+x)), " to ", str(hex(applen)))
else:
chunk = application[start+x:start+x+maxChunkSize]
# print(str(hex(start+x)), " to ", str(hex(start + x + maxChunkSize)))
chunklen = len(chunk)
# Build a data packet with a "data command" a "length" and the actual
# payload bytes, and send it to the target.
dataMsg = bytearray([0x00]*8);
fill_word(dataMsg, 0, (((chunklen + 12) << 16) | AM_SECBOOT_WIRED_MSGTYPE_DATA))
# seqNo
fill_word(dataMsg, 4, x)
print("Sending Data Packet of length ", chunklen)
send_ackd_command(dataMsg + chunk, ser)
if (args.raw != ''):
# Read the binary file from the command line.
with open(args.raw, mode='rb') as rawfile:
blob = rawfile.read()
# Send Raw command
print('Sending Raw Command.')
ser.write(blob)
if (args.reset != 0):
# Send reset
print('Sending Reset Command.')
resetmsg = bytearray([0x00]*8);
fill_word(resetmsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_RESET))
# options
fill_word(resetmsg, 4, args.reset)
send_ackd_command(resetmsg, ser)
else:
# Received Wrong message
print("Received Unknown Message")
word = word_from_bytes(response, 4)
print("msgType = ", hex(word & 0xFFFF))
print("Length = ", hex(word >> 16))
print([hex(n) for n in response])
print("!!!Wired Upgrade Unsuccessful!!!....Terminating the script")
exit()
#******************************************************************************
#
# Send ACK'd command
#
# Sends a command, and waits for an ACK.
#
#******************************************************************************
def send_ackd_command(command, ser):
for numTries in range(1, 5 , 1):
response = send_command(command, 20, ser)
word = word_from_bytes(response, 4)
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_ACK):
# Received ACK
if (word_from_bytes(response, 12) != AM_SECBOOT_WIRED_ACK_STATUS_SUCCESS):
print("Received NACK")
print("msgType = ", hex(word_from_bytes(response, 8)))
print("error = ", hex(word_from_bytes(response, 12)))
print("seqNo = ", hex(word_from_bytes(response, 16)))
if (numTries < 4):
print("Retry # ", numTries)
else:
print("Exceed number of retries")
else:
break
else:
print("!!!Wired Upgrade Unsuccessful!!!....unexpected respose - Terminating the script")
exit()
if (numTries == 4):
print("!!!Wired Upgrade Unsuccessful!!!....numTries exceeded - Terminating the script")
exit()
return response
#******************************************************************************
#
# Send command
#
# Sends a command, and waits for the response.
#
#******************************************************************************
def send_command(params, response_len, ser):
# Compute crc
crc = crc32(params)
# print([hex(n) for n in int_to_bytes(crc)])
# print([hex(n) for n in params])
# send crc first
ser.write(int_to_bytes(crc))
# Next, send the parameters.
ser.write(params)
response = ''
response = ser.read(response_len)
# Make sure we got the number of bytes we asked for.
if len(response) != response_len:
print('No response for command 0x{:08X}'.format(word_from_bytes(params, 0) & 0xFFFF))
n = len(response)
if (n != 0):
print("received bytes ", len(response))
print([hex(n) for n in response])
raise NoResponseError
return response
#******************************************************************************
#
# Send a command that uses an array of bytes as its parameters.
#
#******************************************************************************
def send_bytewise_command(command, params, response_len, ser):
# Send the command first.
ser.write(int_to_bytes(command))
# Next, send the parameters.
ser.write(params)
response = ''
response = ser.read(response_len)
# Make sure we got the number of bytes we asked for.
if len(response) != response_len:
print('No response for command 0x{:08X}'.format(command))
raise NoResponseError
return response
#******************************************************************************
#
# Errors
#
#******************************************************************************
class BootError(Exception):
pass
class NoAckError(BootError):
pass
class NoResponseError(BootError):
pass
#******************************************************************************
#
# Main program flow
#
#******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(description =
'UART Wired Update Host for Apollo3')
parser.add_argument('port', help = 'Serial COMx Port')
parser.add_argument('-b', dest='baud', default=115200, type=int,
help = 'Baud Rate (default is 115200)')
parser.add_argument('--raw', dest='raw', default='',
help = 'Binary file for raw message')
parser.add_argument('-f', dest='binfile', default='',
help = 'Binary file to program into the target device')
parser.add_argument('-i', dest = 'imagetype', default=AM_SECBOOT_WIRED_IMAGETYPE_INVALID, type=auto_int,
choices = [
(AM_SECBOOT_WIRED_IMAGETYPE_SBL),
(AM_SECBOOT_WIRED_IMAGETYPE_AM3P),
(AM_SECBOOT_WIRED_IMAGETYPE_PATCH),
(AM_SECBOOT_WIRED_IMAGETYPE_MAIN),
(AM_SECBOOT_WIRED_IMAGETYPE_CHILD),
(AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH),
(AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE),
(AM_SECBOOT_WIRED_IMAGETYPE_INFO0),
(AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA),
(AM_SECBOOT_WIRED_IMAGETYPE_INVALID)
],
help = 'ImageType ('
+ str(AM_SECBOOT_WIRED_IMAGETYPE_SBL) + ': SBL, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_AM3P) + ': AM3P, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_PATCH) + ': Patch, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_MAIN) + ': Main, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_CHILD) + ': Child, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH) + ': CustOTA, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE) + ': NonSecure, '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INFO0) + ': Info0 '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA) + ': Info0_NOOTA) '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INVALID) + ': Invalid) '
'- default[Invalid]')
parser.add_argument('-o', dest = 'otadesc', type=auto_int, default=0xFE000,
help = 'OTA Descriptor Page address (hex) - (Default is 0xFE000 - at the end of main flash) - enter 0xFFFFFFFF to instruct SBL to skip OTA')
parser.add_argument('-r', dest = 'reset', default=1, type=auto_int, choices = [0,1,2],
help = 'Should it send reset command after image download? (0 = no reset, 1 = POI, 2 = POR) (default is 1)')
parser.add_argument('-a', dest = 'abort', default=-1, type=int, choices = [0,1,-1],
help = 'Should it send abort command? (0 = abort, 1 = abort and quit, -1 = no abort) (default is -1)')
parser.add_argument('--split', dest='split', type=auto_int, default=hex(MAX_DOWNLOAD_SIZE),
help='Specify the max block size if the image will be downloaded in pieces')
args = parser.parse_args()
main()