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:
David Sidrane
2018-07-10 15:22:17 -07:00
committed by Lorenz Meier
parent 0c0b761c87
commit 05936f2ff7
+75 -6
View File
@@ -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_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):
# Open the port, keep the default timeout short so we can poll quickly.
# On some systems writes can suddenly get stuck without having a
# 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.otp = b''
self.sn = b''
@@ -230,6 +243,11 @@ class uploader(object):
else:
break
# debugging code
def __probe(self, state):
#self.port.setRTS(state)
return
def __send(self, c):
# print("send " + binascii.hexlify(c))
while True:
@@ -266,6 +284,24 @@ class uploader(object):
if c != self.OK:
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
def __sync(self):
# 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.
self.__send(uploader.GET_OTP + t + uploader.EOC)
value = self.__recv(4)
synstart = time.time()
self.__getSync()
dif = time.time() - synstart
#print("%5.5f" %dif)
if (dif > self.maxDtGetSync and dif != 0) :
self.maxDtGetSync = dif
return value
# 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
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='')
self.__send(uploader.CHIP_ERASE +
uploader.EOC)
@@ -371,7 +414,7 @@ class uploader(object):
raise RuntimeError("timed out waiting for erase")
# send a PROG_MULTI command to write a collection of bytes
def __program_multi(self, data):
def __program_multi(self, data, windowMode):
if runningPython3:
length = len(data).to_bytes(1, byteorder='big')
@@ -382,7 +425,17 @@ class uploader(object):
self.__send(length)
self.__send(data)
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
def __verify_multi(self, data):
@@ -420,18 +473,33 @@ class uploader(object):
# upload code
def __program(self, label, fw):
self.__probe(False)
print("\n", end='')
code = fw.image
groups = self.__split_len(code, uploader.PROG_MULTI_MAX)
# Give imedate feedback
self.__drawProgressBar(label, 0, len(groups))
uploadProgress = 0
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)
uploadProgress += 1
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))
# Do any remaining fragment
self.__ackSyncWindow(self.window)
self.window = 0
self.__drawProgressBar(label, 100, 100)
# verify code
@@ -489,6 +557,7 @@ class uploader(object):
# upload the firmware
def upload(self, fw, force=False, boot_delay=None):
# Make sure we are doing the right thing
start = time.time()
if self.board_type != fw.property('board_id'):
msg = "Firmware not suitable for this board (board_type=%u board_id=%u)" % (
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"
"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")
self.__erase("Erase ")
self.__program("Program", fw)
@@ -582,9 +650,10 @@ class uploader(object):
if boot_delay is not None:
self.__set_boot_delay(boot_delay)
print("\nRebooting.\n")
print("\nRebooting.", end='')
self.__reboot()
self.port.close()
print(" Elapsed Time %3.3f\n" % (time.time() - start))
def __next_baud_flightstack(self):
if self.baudrate_flightstack_idx + 1 >= len(self.baudrate_flightstack):