Skip to content
Draft
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
108 changes: 84 additions & 24 deletions bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
# CAN command message IDs.
ERASE_SECTOR_CAN_ID_LOWBITS = 0x4
ERASE_SECTOR_COMPLETE_CAN_ID_LOWBITS = 0x5
PROGRAM_CAN_ID_LOWBITS = 0x6
NAK_ID = 0x6
VERIFY_CAN_ID_LOWBITS = 0x7
APP_VALIDITY_CAN_ID_LOWBITS = 0x8
APP_START_PROGRAM_ID = 0xA # this is BOARD LOW BIT + 0xA -- we have until boot_id_range_start + 0xFFFFFF
FINAL_PROGRAM_ID = 0xFFFFFF

# Verify response options.
# Keep in sync with:
Expand All @@ -32,7 +34,7 @@

# The minimum amount of data the microcontroller can program at a time.
MIN_PROG_SIZE_BYTES = 32

NAK_TIMEOUT = 0.1

class Bootloader:
bus: can.Bus
Expand All @@ -54,7 +56,7 @@ def __init__(
self.board: boards.Board = board
self.timeout: int = timeout
self.ui_callback: Callable = ui_callback

self.dropped_packets = set()
def goto_bootloader(self) -> bool:
"""
Pushes all boards to bootloader mode.
Expand Down Expand Up @@ -182,30 +184,89 @@ def program(self) -> None:
Program the binary into flash. There is no CAN handshake here to reduce
latency during programming. Also, the bootloader will verify the app's code is valid
by computing a checksum.

"""
for i, address in enumerate(
range(self.ih.minaddr(), self.ih.minaddr() + self.size_bytes(), 8)
):
if self.ui_callback and i % 128 == 0:
self.ui_callback("Programming data", self.size_bytes(), i * 8)

data = [self.ih[address + i] for i in range(0, 8)]
self.__transmission_address = self.ih.minaddr()
self.__transmission_id = self.board.boot_id_range_start | APP_START_PROGRAM_ID
self.__index = 0

def _validator(msg: can.Message):
# Ignore all known status messages
print(f"Can ID that come through {msg.arbitration_id}\n")
if msg.arbitration_id == (self.board.boot_id_range_start | NAK_ID):
dropped_packet_address = int.from_bytes(msg.data, "little")
self.__transmission_address = (dropped_packet_address // 8) * 8
self.__index = dropped_packet_address % 8
self.__transmission_id = self.board.boot_id_range_start | APP_START_PROGRAM_ID
return False
else:
return True

while self.__transmission_address < self.ih.minaddr() + self.size_bytes():

if self.ui_callback and self.__index % 128 == 0:
self.ui_callback("Programming data", self.size_bytes(), self.__index * 8)
self.__index += 1

data = [self.ih[self.__transmission_address + i] for i in range(0, 8)]

success = False
while not success:
try:
self.bus.send(
can.Message(
arbitration_id=self.board.boot_id_range_start
| PROGRAM_CAN_ID_LOWBITS,
data=data,
is_extended_id=True,
if(self.__transmission_id <= (self.board.boot_id_range_start | FINAL_PROGRAM_ID)):
while not success:
try:
self.bus.send(
can.Message(
arbitration_id=self.__transmission_id,
data=data,
is_extended_id=True,
)
)
)
success = True
except can.interfaces.vector.exceptions.VectorOperationError:
pass
success = True
except can.interfaces.vector.exceptions.VectorOperationError:
pass

nak = ~self._await_can_msg(_validator, timeout=NAK_TIMEOUT)

if nak == False:
print(f"WE LIT!!!!! PACKET {self.__transmission_address} recieved\n")
self.__transmission_address += 8
elif nak == True:
print(f"PACKET DROPPED, Re-transmission starting at address {self.__transmission_address}\n")
print(f"PACKET DROPPED, Re-transmission starting at index {self.__index}\n")
else:
print("TIMEOUT\n")


# for i, address in enumerate(
# range(self.ih.minaddr(), self.ih.minaddr() + self.size_bytes(), 8)
# ):
# self.__transmission_address = address #use an internal pointer

# if self.ui_callback and i % 128 == 0:
# self.ui_callback("Programming data", self.size_bytes(), i * 8)
# data = [self.ih[address + i] for i in range(0, 8)]

# success = False
# if(self.__transmission_id <= (self.board.boot_id_range_start | FINAL_PROGRAM_ID)):
# while not success:
# try:
# self.bus.send(
# can.Message(
# arbitration_id=self.__transmission_id,
# data=data,
# is_extended_id=True,
# )
# )
# success = True
# except can.interfaces.vector.exceptions.VectorOperationError:
# pass
# else:
# print(f"RAN OUT OF BOARD IDS, current can ID {self.__transmission_id}")


# Empirically, this tiny delay between messages seems to improve reliability.
time.sleep(0.0005)

if self.ui_callback:
self.ui_callback("Programming data", self.size_bytes(), self.size_bytes())

Expand Down Expand Up @@ -251,7 +312,6 @@ def update(self) -> None:
Run the update procedure for this bootloader.

"""

def _intersect(a_min, a_max, b_min, b_max):
"""1-D intersection to check if an app's hex and a flash sector share any addresses."""
return a_max >= b_min and b_max >= a_min
Expand Down Expand Up @@ -368,4 +428,4 @@ def size_bytes(self) -> int:
return int(
math.ceil((self.ih.maxaddr() - self.ih.minaddr()) / MIN_PROG_SIZE_BYTES)
* MIN_PROG_SIZE_BYTES
)
)