Skip to content

Fix svl script #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 106 additions & 82 deletions tools/artemis/artemis_svl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@
# Variable baud rate bootloader for Artemis Apollo3 modules

# Immediately upon reset the Artemis module will search for the timing character
# to auto-detect the baud rate. If a valid baud rate is found the Artemis will
# to auto-detect the baud rate. If a valid baud rate is found the Artemis will
# respond with the bootloader version packet
# If the computer receives a well-formatted version number packet at the desired
# baud rate it will send a command to begin bootloading. The Artemis shall then
# respond with the a command asking for the next frame.
# The host will then send a frame packet. If the CRC is OK the Artemis will write
# baud rate it will send a command to begin bootloading. The Artemis shall then
# respond with the a command asking for the next frame.
# The host will then send a frame packet. If the CRC is OK the Artemis will write
# that to memory and request the next frame. If the CRC fails the Artemis will
# discard that data and send a request to re-send the previous frame.
# This cycle repeats until the Artemis receives a done command in place of the
# requested frame data command.
# The initial baud rate determination must occur within some small timeout. Once
# baud rate detection has completed all additional communication will have a
# The initial baud rate determination must occur within some small timeout. Once
# baud rate detection has completed all additional communication will have a
# universal timeout value. Once the Artemis has begun requesting data it may no
# no longer exit the bootloader. If the host detects a timeout at any point it
# will stop bootloading.
# no longer exit the bootloader. If the host detects a timeout at any point it
# will stop bootloading.

# Notes about PySerial timeout:
# The timeout operates on whole functions - that is to say that a call to
# ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
# The timeout operates on whole functions - that is to say that a call to
# ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
# that the necessary bytes were not found)
# If there are no incoming bytes (on the line or in the buffer) then two calls to
# If there are no incoming bytes (on the line or in the buffer) then two calls to
# ser.read(n) will time out after 2*ser.timeout
# Incoming UART data is buffered behind the scenes, probably by the OS.

Expand All @@ -39,19 +39,23 @@
import sys
import time
import math
import os.path
from sys import exit

SCRIPT_VERSION_MAJOR = "1"
SCRIPT_VERSION_MINOR = "7"

# ***********************************************************************************
#
# Commands
#
# ***********************************************************************************
SVL_CMD_VER = 0x01 # version
SVL_CMD_BL = 0x02 # enter bootload mode
SVL_CMD_NEXT = 0x03 # request next chunk
SVL_CMD_FRAME = 0x04 # indicate app data frame
SVL_CMD_RETRY = 0x05 # request re-send frame
SVL_CMD_DONE = 0x06 # finished - all data sent
SVL_CMD_VER = 0x01 # version
SVL_CMD_BL = 0x02 # enter bootload mode
SVL_CMD_NEXT = 0x03 # request next chunk
SVL_CMD_FRAME = 0x04 # indicate app data frame
SVL_CMD_RETRY = 0x05 # request re-send frame
SVL_CMD_DONE = 0x06 # finished - all data sent

barWidthInCharacters = 50 # Width of progress bar, ie [###### % complete

Expand Down Expand Up @@ -97,7 +101,7 @@

def get_crc16(data):

#Table and code ported from Artemis SVL bootloader
# Table and code ported from Artemis SVL bootloader
crc = 0x0000
data = bytearray(data)
for ch in data:
Expand All @@ -108,30 +112,33 @@ def get_crc16(data):
return crc



# ***********************************************************************************
#
# Wait for a packet
# Wait for a packet
#
# ***********************************************************************************
def wait_for_packet(ser):

packet = {'len':0, 'cmd':0, 'data':0, 'crc':1, 'timeout':1}
packet = {'len': 0, 'cmd': 0, 'data': 0, 'crc': 1, 'timeout': 1}

n = ser.read(2) # get the number of bytes
n = ser.read(2) # get the number of bytes
if(len(n) < 2):
return packet
packet['len'] = int.from_bytes(n, byteorder='big', signed=False) #

packet['len'] = int.from_bytes(n, byteorder='big', signed=False) #
payload = ser.read(packet['len'])

if(len(payload) != packet['len']):
return packet

packet['timeout'] = 0 # all bytes received, so timeout is not true
packet['cmd'] = payload[0] # cmd is the first byte of the payload
packet['data'] = payload[1:packet['len']-2] # the data is the part of the payload that is not cmd or crc
packet['crc'] = get_crc16(payload) # performing the crc on the whole payload should return 0

# all bytes received, so timeout is not true
packet['timeout'] = 0
# cmd is the first byte of the payload
packet['cmd'] = payload[0]
# the data is the part of the payload that is not cmd or crc
packet['data'] = payload[1:packet['len']-2]
# performing the crc on the whole payload should return 0
packet['crc'] = get_crc16(payload)

return packet

Expand All @@ -140,22 +147,20 @@ def wait_for_packet(ser):
# Send a packet
#
# ***********************************************************************************


def send_packet(ser, cmd, data):
data = bytearray(data)
num_bytes = 3 + len(data)
payload = bytearray(cmd.to_bytes(1,'big'))
payload = bytearray(cmd.to_bytes(1, 'big'))
payload.extend(data)
crc = get_crc16(payload)
payload.extend(bytearray(crc.to_bytes(2,'big')))
payload.extend(bytearray(crc.to_bytes(2, 'big')))

ser.write(num_bytes.to_bytes(2,'big'))
ser.write(num_bytes.to_bytes(2, 'big'))
ser.write(bytes(payload))






# ***********************************************************************************
#
# Setup: signal baud rate, get version, and command BL enter
Expand All @@ -165,29 +170,27 @@ def phase_setup(ser):

baud_detect_byte = b'U'

verboseprint('\nphase:\tsetup')
# Handle the serial startup blip
verboseprint('\nPhase:\tSetup')

# Handle the serial startup blip
ser.reset_input_buffer()
verboseprint('\tcleared startup blip')
verboseprint('\tCleared startup blip')

ser.write(baud_detect_byte) # send the baud detection character

packet = wait_for_packet(ser)
if(packet['timeout'] or packet['crc']):
return 1
twopartprint('\t','Got SVL Bootloader Version: ' +
return False # failed to enter bootloader

twopartprint('\t', 'Got SVL Bootloader Version: ' +
str(int.from_bytes(packet['data'], 'big')))
verboseprint('\tSending \'enter bootloader\' command')

send_packet(ser, SVL_CMD_BL, b'')

# Now enter the bootload phase



return True

# Now enter the bootload phase


# ***********************************************************************************
Expand All @@ -203,7 +206,7 @@ def phase_bootload(ser):
resend_max = 4
resend_count = 0

verboseprint('\nphase:\tbootload')
verboseprint('\nPhase:\tBootload')

with open(args.binfile, mode='rb') as binfile:
application = binfile.read()
Expand All @@ -220,36 +223,38 @@ def phase_bootload(ser):
' bytes to send in ' + str(total_frames) + ' frames')

bl_done = False
bl_failed = False
while((not bl_done) and (not bl_failed)):

packet = wait_for_packet(ser) # wait for indication by Artemis
bl_succeeded = True
while((bl_done == False) and (bl_succeeded == True)):

# wait for indication by Artemis
packet = wait_for_packet(ser)
if(packet['timeout'] or packet['crc']):
print('\n\terror receiving packet')
print(packet)
print('\n')
bl_failed = True
verboseprint('\n\tError receiving packet')
verboseprint(packet)
verboseprint('\n')
bl_succeeded = False
bl_done = True

if( packet['cmd'] == SVL_CMD_NEXT ):
if(packet['cmd'] == SVL_CMD_NEXT):
# verboseprint('\tgot frame request')
curr_frame += 1
resend_count = 0
elif( packet['cmd'] == SVL_CMD_RETRY ):
verboseprint('\t\tretrying...')
elif(packet['cmd'] == SVL_CMD_RETRY):
verboseprint('\t\tRetrying...')
resend_count += 1
if( resend_count >= resend_max ):
bl_failed = True
if(resend_count >= resend_max):
bl_succeeded = False
bl_done = True
else:
print('unknown error')
bl_failed = True
print('Timeout or unknown error')
bl_succeeded = False
bl_done = True

if( curr_frame <= total_frames ):
frame_data = application[((curr_frame-1)*frame_size):((curr_frame-1+1)*frame_size)]
if(curr_frame <= total_frames):
frame_data = application[(
(curr_frame-1)*frame_size):((curr_frame-1+1)*frame_size)]
if(args.verbose):
verboseprint('\tsending frame #'+str(curr_frame) +
verboseprint('\tSending frame #'+str(curr_frame) +
', length: '+str(len(frame_data)))
else:
percentComplete = curr_frame * 100 / total_frames
Expand All @@ -267,19 +272,15 @@ def phase_bootload(ser):
send_packet(ser, SVL_CMD_DONE, b'')
bl_done = True

if( bl_failed == False ):
if(bl_succeeded == True):
twopartprint('\n\t', 'Upload complete')
endTime = time.time()
bps = total_len / (endTime - startTime)
verboseprint('\n\tNominal bootload bps: ' + str(round(bps, 2)))
else:
twopartprint('\n\t', 'Upload failed')

return bl_failed




return bl_succeeded


# ***********************************************************************************
Expand All @@ -294,12 +295,12 @@ def phase_serial_port_help():
for dev in devices:
if(dev.device.upper() == args.port.upper()):
print(dev.device + " is currently open. Please close any other terminal programs that may be using " +
dev.device + " and try again.")
dev.device + " and try again.")
exit()

# otherwise, give user a list of possible com ports
print(args.port.upper() +
" not found but we detected the following serial ports:")
" not found but we detected the following serial ports:")
for dev in devices:
if 'CH340' in dev.description:
print(
Expand All @@ -325,23 +326,46 @@ def main():

print('\n\nArtemis SVL Bootloader')

verboseprint("Script version " + SCRIPT_VERSION_MAJOR +
"." + SCRIPT_VERSION_MINOR)

if not os.path.exists(args.binfile):
print("Bin file {} does not exist.".format(args.binfile))
exit()

bl_success = False
entered_bootloader = False

for _ in range(num_tries):

with serial.Serial(args.port, args.baud, timeout=args.timeout) as ser:

t_su = 0.15 # startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
# startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
t_su = 0.15

time.sleep(t_su) # Allow Artemis to come out of reset
phase_setup(ser) # Perform baud rate negotiation

bl_failed = phase_bootload(ser) # Bootload
# Perform baud rate negotiation
entered_bootloader = phase_setup(ser)

if(entered_bootloader == True):
bl_success = phase_bootload(ser)
if(bl_success == True): # Bootload
#print("Bootload complete!")
break
else:
verboseprint("Failed to enter bootload phase")

if( bl_failed == False ):
if(bl_success == True):
break

except:
if(entered_bootloader == False):
print(
"Target failed to enter bootload mode. Verify the right COM port is selected and that your board has the SVL bootloader.")

except serial.SerialException:
phase_serial_port_help()

exit()


Expand All @@ -367,7 +391,7 @@ def main():
action="store_true")

parser.add_argument("-t", "--timeout", default=0.50, help="Communication timeout in seconds (default 0.5)",
type=float)
type=float)

if len(sys.argv) < 2:
print("No port selected. Detected Serial Ports:")
Expand All @@ -390,7 +414,7 @@ def verboseprint(*args):

def twopartprint(verbosestr, printstr):
if args.verbose:
print(verbosestr, end = '')
print(verbosestr, end='')

print(printstr)

Expand Down
Binary file modified tools/artemis/windows/artemis_svl.exe
Binary file not shown.