Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
7 changes: 7 additions & 0 deletions boards.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Board:
name: str
start_update_can_id: int
update_ack_can_id: int
boot_load_bin_can_id : int
mcu: Microcontroller
path: str

Expand Down Expand Up @@ -97,41 +98,47 @@ class Board:
name="VC",
start_update_can_id=1210,
update_ack_can_id=1211,
boot_load_bin_can_id = 500,
mcu=STM32H733_MCU,
path=os.path.join("firmware", "quadruna", "VC", "quadruna_VC_app_metadata.hex"),
)
quadruna_BMS = Board(
name="BMS",
start_update_can_id=1200,
update_ack_can_id=1201,
boot_load_bin_can_id = 400,
mcu=STM32H733_MCU,
path=os.path.join("firmware", "quadruna", "BMS", "quadruna_BMS_app_metadata.hex"),
)
quadruna_FSM = Board(
name="FSM",
start_update_can_id=1220,
update_ack_can_id=1221,
boot_load_bin_can_id = 600,
mcu=STM32F412_MCU,
path=os.path.join("firmware", "quadruna", "FSM", "quadruna_FSM_app_metadata.hex"),
)
quadruna_RSM = Board(
name="RSM",
start_update_can_id=1230,
update_ack_can_id=1231,
boot_load_bin_can_id = 700,
mcu=STM32F412_MCU,
path=os.path.join("firmware", "quadruna", "RSM", "quadruna_RSM_app_metadata.hex"),
)
quadruna_CRIT = Board(
name="CRIT",
start_update_can_id=1240,
update_ack_can_id=1241,
boot_load_bin_can_id = 900,
mcu=STM32F412_MCU,
path=os.path.join("firmware", "quadruna", "CRIT", "quadruna_CRIT_app_metadata.hex"),
)
h7dev = Board(
name="h7dev",
start_update_can_id=1300,
update_ack_can_id=1301,
boot_load_bin_can_id = 300,
mcu=STM32H733_MCU,
path=os.path.join("firmware", "dev", "h7dev", "h7dev_app_metadata.hex"),
)
Expand Down
45 changes: 39 additions & 6 deletions bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
PROGRAM_CAN_ID = 1001
VERIFY_CAN_ID = 1002

NAK_ID = 0
NAK_TIMEOUT = 0.1

# CAN reply message IDs.
ERASE_SECTOR_COMPLETE_CAN_ID = 1010
APP_VALIDITY_CAN_ID = 1011
Expand All @@ -34,6 +37,10 @@
# The minimum amount of data the microcontroller can program at a time.
MIN_PROG_SIZE_BYTES = 32

ALL_PACKETS_VALID = 0xFFFFFFFFFFFFFFFF
WINDOW_SIZE = 64

BOARD_10HZ_STATUS_ID = [1309, 1209, 1249, 1229, 1239, 1219]

class Bootloader:
def __init__(
Expand All @@ -49,6 +56,8 @@ def __init__(
self.board = board
self.timeout = timeout
self.ui_callback = ui_callback
self.dropped_packets = set()
self.__transmission_address = 0

def start_update(self) -> bool:
"""
Expand Down Expand Up @@ -123,21 +132,46 @@ 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(

# updated TCP inspired can bootloading: sender will send packet with can id's representing their memory address. The reciever will expect a can ID, of the sender can ID and reciever can ID match all is good, if there is a miss match the board will transmit the excepted can ID back.
# if the expected Can ID is a multiple of 4 it will directly be sent back to the user to begin retransmission at that address, if it is not an interval of 4 then retransmision is restarted at the closests (smaller) interval of 4. Note that a nuiassnace that we have to figure out is that
# if we do want to move back to the closest interval of 4 then we need to figure out how to reflash adddresses that were already flashed (ie if its address 247 and the closest interval of 4 is 244) then we will attempt to reflash 244-246 even though they have already been flashed. Now with the
# stm32s to write to flash memory we need to first erase it. Now if that is the case we need to figure out how to erase as the stm32 can only erase sectors at a time which is 16kb.... I am unsure on what happens if we try to flash memory that has not been erased. If it retains its originally value that that would be best and we can jsut do redundant flashing until we
# get to the address that actually needs to be reflashed.

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 == NAK_ID:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space here lol

self.__transmission_address = int.from_bytes(msg.data, "little")
return False
else:
return True

for i, self.__transmission_address in enumerate(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you overwriting the transmission address in the loop? then the NAK would not do anything. if you are overwriting, it would probably be good to instead reset transmission address (in the loop) to the dropped packet address and retransmit starting from there

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[self.__transmission_address + i] for i in range(0, 8)]

data = [self.ih[address + i] for i in range(0, 8)]
self.bus.send(
can.Message(
arbitration_id=PROGRAM_CAN_ID, data=data, is_extended_id=False
arbitration_id= self.__transmission_address, data=data, is_extended_id=False
)
)

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

if nak == False:
print(f"WE LIT!!!!! PACKET {self.__transmission_address} recieved\n")

elif nak == True:
print(f"PACKET DROPPED AT {self.__transmission_address}\n")
else:
print("TIMEOUT\n")

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

Expand Down Expand Up @@ -177,7 +211,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 @@ -295,4 +328,4 @@ def size_bytes(self) -> int:
return int(
math.ceil((self.ih.maxaddr() - self.ih.minaddr()) / MIN_PROG_SIZE_BYTES)
* MIN_PROG_SIZE_BYTES
)
)