mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-30 04:06:33 +08:00
px_uploader.py:Speed Improvments on Serial
The __getSync was costing about 16Ms per call. The commit uses a window based approch allowing the SYNC,<results> to be read all at one time. and delaying for programing based on transport time + 1 Ms; THe improvment at 2Mbps is >4 minutes to ~37 seconds
This commit is contained in:
committed by
Lorenz Meier
parent
0c0b761c87
commit
05936f2ff7
+75
-6
@@ -191,10 +191,23 @@ class uploader(object):
|
|||||||
MAVLINK_REBOOT_ID1 = bytearray(b'\xfe\x21\x72\xff\x00\x4c\x00\x00\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x01\x00\x00\x53\x6b')
|
MAVLINK_REBOOT_ID1 = bytearray(b'\xfe\x21\x72\xff\x00\x4c\x00\x00\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x01\x00\x00\x53\x6b')
|
||||||
MAVLINK_REBOOT_ID0 = bytearray(b'\xfe\x21\x45\xff\x00\x4c\x00\x00\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x00\x00\x00\xcc\x37')
|
MAVLINK_REBOOT_ID0 = bytearray(b'\xfe\x21\x45\xff\x00\x4c\x00\x00\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x00\x00\x00\xcc\x37')
|
||||||
|
|
||||||
|
MAX_FLASH_PRGRAM_TIME = 0.001 # Time on an F7 to send SYNC, RESULT from last data in multi RXed
|
||||||
|
SYNC_DETECT_THRESHOLD = 0.00015
|
||||||
|
|
||||||
def __init__(self, portname, baudrate_bootloader, baudrate_flightstack):
|
def __init__(self, portname, baudrate_bootloader, baudrate_flightstack):
|
||||||
# Open the port, keep the default timeout short so we can poll quickly.
|
# Open the port, keep the default timeout short so we can poll quickly.
|
||||||
# On some systems writes can suddenly get stuck without having a
|
# On some systems writes can suddenly get stuck without having a
|
||||||
# write_timeout > 0 set.
|
# write_timeout > 0 set.
|
||||||
|
# chartime 8n1 * bit rate is us
|
||||||
|
self.chartime = 10 * (1.0 / baudrate_bootloader)
|
||||||
|
|
||||||
|
# we use a window approche to SYNC,<result> gathring
|
||||||
|
self.window = 0
|
||||||
|
self.window_max = 256
|
||||||
|
self.window_per = 2 # Sync,<result>
|
||||||
|
self.maxDtGetSync = -1000.00
|
||||||
|
self.ackWindowedMode = True # assume Windowed mode for non USB
|
||||||
|
|
||||||
self.port = serial.Serial(portname, baudrate_bootloader, timeout=0.5, write_timeout=0.5)
|
self.port = serial.Serial(portname, baudrate_bootloader, timeout=0.5, write_timeout=0.5)
|
||||||
self.otp = b''
|
self.otp = b''
|
||||||
self.sn = b''
|
self.sn = b''
|
||||||
@@ -230,6 +243,11 @@ class uploader(object):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# debugging code
|
||||||
|
def __probe(self, state):
|
||||||
|
#self.port.setRTS(state)
|
||||||
|
return
|
||||||
|
|
||||||
def __send(self, c):
|
def __send(self, c):
|
||||||
# print("send " + binascii.hexlify(c))
|
# print("send " + binascii.hexlify(c))
|
||||||
while True:
|
while True:
|
||||||
@@ -266,6 +284,24 @@ class uploader(object):
|
|||||||
if c != self.OK:
|
if c != self.OK:
|
||||||
raise RuntimeError("unexpected response 0x%x instead of OK" % ord(c))
|
raise RuntimeError("unexpected response 0x%x instead of OK" % ord(c))
|
||||||
|
|
||||||
|
# The control flow for reciving Sync is on the order of 16 Ms per Sync
|
||||||
|
# This will validate all the SYNC,<results> for a window of programing
|
||||||
|
# in about 13.81 Ms for 256 blocks written
|
||||||
|
def __ackSyncWindow(self, count):
|
||||||
|
if (count > 0):
|
||||||
|
data = bytearray(bytes(self.__recv(count)))
|
||||||
|
if (len(data) != count):
|
||||||
|
raise RuntimeError("Ack Window %i not %i " % (len(data), count))
|
||||||
|
for i in range(0,len(data),2):
|
||||||
|
if chr(data[i]) != self.INSYNC:
|
||||||
|
raise RuntimeError("unexpected %s instead of INSYNC" % c)
|
||||||
|
if chr(data[i+1]) == self.INVALID:
|
||||||
|
raise RuntimeError("bootloader reports INVALID OPERATION")
|
||||||
|
if chr(data[i+1]) == self.FAILED:
|
||||||
|
raise RuntimeError("bootloader reports OPERATION FAILED")
|
||||||
|
if chr(data[i+1]) != self.OK:
|
||||||
|
raise RuntimeError("unexpected response 0x%x instead of OK" % ord(c))
|
||||||
|
|
||||||
# attempt to get back into sync with the bootloader
|
# attempt to get back into sync with the bootloader
|
||||||
def __sync(self):
|
def __sync(self):
|
||||||
# send a stream of ignored bytes longer than the longest possible conversation
|
# send a stream of ignored bytes longer than the longest possible conversation
|
||||||
@@ -309,7 +345,12 @@ class uploader(object):
|
|||||||
t = struct.pack("I", param) # int param as 32bit ( 4 byte ) char array.
|
t = struct.pack("I", param) # int param as 32bit ( 4 byte ) char array.
|
||||||
self.__send(uploader.GET_OTP + t + uploader.EOC)
|
self.__send(uploader.GET_OTP + t + uploader.EOC)
|
||||||
value = self.__recv(4)
|
value = self.__recv(4)
|
||||||
|
synstart = time.time()
|
||||||
self.__getSync()
|
self.__getSync()
|
||||||
|
dif = time.time() - synstart
|
||||||
|
#print("%5.5f" %dif)
|
||||||
|
if (dif > self.maxDtGetSync and dif != 0) :
|
||||||
|
self.maxDtGetSync = dif
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# send the GET_SN command and wait for an info parameter
|
# send the GET_SN command and wait for an info parameter
|
||||||
@@ -347,6 +388,8 @@ class uploader(object):
|
|||||||
|
|
||||||
# send the CHIP_ERASE command and wait for the bootloader to become ready
|
# send the CHIP_ERASE command and wait for the bootloader to become ready
|
||||||
def __erase(self, label):
|
def __erase(self, label):
|
||||||
|
self.ackWindowedMode = self.maxDtGetSync >= uploader.SYNC_DETECT_THRESHOLD
|
||||||
|
print("MaxSync:%2.5f Windowed mode:%s" % (self.maxDtGetSync, self.ackWindowedMode))
|
||||||
print("\n", end='')
|
print("\n", end='')
|
||||||
self.__send(uploader.CHIP_ERASE +
|
self.__send(uploader.CHIP_ERASE +
|
||||||
uploader.EOC)
|
uploader.EOC)
|
||||||
@@ -371,7 +414,7 @@ class uploader(object):
|
|||||||
raise RuntimeError("timed out waiting for erase")
|
raise RuntimeError("timed out waiting for erase")
|
||||||
|
|
||||||
# send a PROG_MULTI command to write a collection of bytes
|
# send a PROG_MULTI command to write a collection of bytes
|
||||||
def __program_multi(self, data):
|
def __program_multi(self, data, windowMode):
|
||||||
|
|
||||||
if runningPython3:
|
if runningPython3:
|
||||||
length = len(data).to_bytes(1, byteorder='big')
|
length = len(data).to_bytes(1, byteorder='big')
|
||||||
@@ -382,7 +425,17 @@ class uploader(object):
|
|||||||
self.__send(length)
|
self.__send(length)
|
||||||
self.__send(data)
|
self.__send(data)
|
||||||
self.__send(uploader.EOC)
|
self.__send(uploader.EOC)
|
||||||
self.__getSync()
|
if (not windowMode):
|
||||||
|
self.__getSync()
|
||||||
|
else:
|
||||||
|
# The following is done to have minimum delay on the transmission
|
||||||
|
# of the ne fw. The per block cost of __getSync was about 16 mS per.
|
||||||
|
# Passively wait on Sync and Result using board rates and
|
||||||
|
# N.B. attempts to activly wait on InWating still carried 8 mS of overhead
|
||||||
|
self.__probe(False)
|
||||||
|
self.__probe(True)
|
||||||
|
time.sleep((ord(length) * self.chartime) + uploader.MAX_FLASH_PRGRAM_TIME)
|
||||||
|
self.__probe(False)
|
||||||
|
|
||||||
# verify multiple bytes in flash
|
# verify multiple bytes in flash
|
||||||
def __verify_multi(self, data):
|
def __verify_multi(self, data):
|
||||||
@@ -420,18 +473,33 @@ class uploader(object):
|
|||||||
|
|
||||||
# upload code
|
# upload code
|
||||||
def __program(self, label, fw):
|
def __program(self, label, fw):
|
||||||
|
self.__probe(False)
|
||||||
print("\n", end='')
|
print("\n", end='')
|
||||||
code = fw.image
|
code = fw.image
|
||||||
groups = self.__split_len(code, uploader.PROG_MULTI_MAX)
|
groups = self.__split_len(code, uploader.PROG_MULTI_MAX)
|
||||||
|
# Give imedate feedback
|
||||||
|
self.__drawProgressBar(label, 0, len(groups))
|
||||||
uploadProgress = 0
|
uploadProgress = 0
|
||||||
for bytes in groups:
|
for bytes in groups:
|
||||||
self.__program_multi(bytes)
|
self.__program_multi(bytes, self.ackWindowedMode)
|
||||||
|
# If in Windo moode extend the window size for the __ackSyncWindow
|
||||||
|
if self.ackWindowedMode:
|
||||||
|
self.window += self.window_per
|
||||||
|
|
||||||
# Print upload progress (throttled, so it does not delay upload progress)
|
# Print upload progress (throttled, so it does not delay upload progress)
|
||||||
uploadProgress += 1
|
uploadProgress += 1
|
||||||
if uploadProgress % 256 == 0:
|
if uploadProgress % 256 == 0:
|
||||||
|
self.__probe(True)
|
||||||
|
self.__probe(False)
|
||||||
|
self.__probe(True)
|
||||||
|
self.__ackSyncWindow(self.window)
|
||||||
|
self.__probe(False)
|
||||||
|
self.window = 0
|
||||||
self.__drawProgressBar(label, uploadProgress, len(groups))
|
self.__drawProgressBar(label, uploadProgress, len(groups))
|
||||||
|
|
||||||
|
# Do any remaining fragment
|
||||||
|
self.__ackSyncWindow(self.window)
|
||||||
|
self.window = 0
|
||||||
self.__drawProgressBar(label, 100, 100)
|
self.__drawProgressBar(label, 100, 100)
|
||||||
|
|
||||||
# verify code
|
# verify code
|
||||||
@@ -489,6 +557,7 @@ class uploader(object):
|
|||||||
# upload the firmware
|
# upload the firmware
|
||||||
def upload(self, fw, force=False, boot_delay=None):
|
def upload(self, fw, force=False, boot_delay=None):
|
||||||
# Make sure we are doing the right thing
|
# Make sure we are doing the right thing
|
||||||
|
start = time.time()
|
||||||
if self.board_type != fw.property('board_id'):
|
if self.board_type != fw.property('board_id'):
|
||||||
msg = "Firmware not suitable for this board (board_type=%u board_id=%u)" % (
|
msg = "Firmware not suitable for this board (board_type=%u board_id=%u)" % (
|
||||||
self.board_type, fw.property('board_id'))
|
self.board_type, fw.property('board_id'))
|
||||||
@@ -570,7 +639,6 @@ class uploader(object):
|
|||||||
"If you know you that the board does not have the silicon errata, use\n"
|
"If you know you that the board does not have the silicon errata, use\n"
|
||||||
"this script with --force, or update the bootloader. If you are invoking\n"
|
"this script with --force, or update the bootloader. If you are invoking\n"
|
||||||
"upload using make, you can use force-upload target to force the upload.\n")
|
"upload using make, you can use force-upload target to force the upload.\n")
|
||||||
|
|
||||||
self.__erase("Erase ")
|
self.__erase("Erase ")
|
||||||
self.__program("Program", fw)
|
self.__program("Program", fw)
|
||||||
|
|
||||||
@@ -582,9 +650,10 @@ class uploader(object):
|
|||||||
if boot_delay is not None:
|
if boot_delay is not None:
|
||||||
self.__set_boot_delay(boot_delay)
|
self.__set_boot_delay(boot_delay)
|
||||||
|
|
||||||
print("\nRebooting.\n")
|
print("\nRebooting.", end='')
|
||||||
self.__reboot()
|
self.__reboot()
|
||||||
self.port.close()
|
self.port.close()
|
||||||
|
print(" Elapsed Time %3.3f\n" % (time.time() - start))
|
||||||
|
|
||||||
def __next_baud_flightstack(self):
|
def __next_baud_flightstack(self):
|
||||||
if self.baudrate_flightstack_idx + 1 >= len(self.baudrate_flightstack):
|
if self.baudrate_flightstack_idx + 1 >= len(self.baudrate_flightstack):
|
||||||
|
|||||||
Reference in New Issue
Block a user