Skip to content
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
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ Compile: `scons`

Flash bootstub and app: `board/recover.sh` # STM flasher should be connected to debug port, needs openocd

Flash app through CAN bus with panda:
Flash app through CAN bus with (standalone) panda:

`board/flash_base.sh` # base motherboard

`board/flash_knee.sh` # knee motherboard

Flash app through CAN bus with (comma device) panda:

1. compile body firmware on your computer and transfer it to the comma device
2. kill openpilot or just the panda processes
3. run `board/flash_base.sh`
4. restart openpilot via `op start` or restart device
78 changes: 74 additions & 4 deletions board/canloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,59 @@
import time
import argparse
import _thread
from panda import Panda, MCU_TYPE_F4 # pylint: disable=import-error
from panda.tests.pedal.canhandle import CanHandle # pylint: disable=import-error
import struct
from itertools import accumulate

from panda import Panda # pylint: disable=import-error
from opendbc.car.structs import CarParams
from opendbc.car.uds import CanClient, IsoTpMessage, MessageTimeoutError


REQUEST_IN = 0xC0
REQUEST_OUT = 0x40
DEFAULT_ISOTP_TIMEOUT = 2
FLASH_STEP = 0xF8

class MCU_TYPE_F4:
class config:
sector_sizes = [0x4000 for _ in range(4)] + [0x10000] + [0x20000 for _ in range(11)]

class CanHandle:
def __init__(self, can_send, can_recv):
self.client = CanClient(can_send, can_recv, tx_addr=1, rx_addr=2, bus=0)

def transact(self, dat, timeout=DEFAULT_ISOTP_TIMEOUT, expect_disconnect=False):
try:
msg = IsoTpMessage(self.client, timeout=timeout)
msg.send(dat)
if expect_disconnect:
deadline = time.monotonic() + timeout
while not msg.tx_done:
msg.recv(timeout=0)
if not msg.tx_done and time.monotonic() > deadline:
raise MessageTimeoutError("timeout waiting for flow control")
time.sleep(0.01)
return b""
ret, _ = msg.recv()
return ret
except MessageTimeoutError as e:
raise TimeoutError from e

def controlWrite(self, request_type, request, value, index, data, timeout=DEFAULT_ISOTP_TIMEOUT, expect_disconnect=False):
dat = struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, 0)
return self.transact(dat, timeout=timeout, expect_disconnect=expect_disconnect)

def controlRead(self, request_type, request, value, index, length, timeout=DEFAULT_ISOTP_TIMEOUT):
dat = struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length)
return self.transact(dat, timeout=timeout)

def bulkWrite(self, endpoint, data, timeout=DEFAULT_ISOTP_TIMEOUT):
dat = struct.pack("HH", endpoint, len(data)) + data
return self.transact(dat, timeout=timeout)

def bulkRead(self, endpoint, timeout=DEFAULT_ISOTP_TIMEOUT):
dat = struct.pack("HH", endpoint, 0)
return self.transact(dat, timeout=timeout)

def heartbeat_thread(p):
while True:
Expand All @@ -20,6 +70,26 @@ def flush_panda():
if len(p.can_recv()) == 0:
break

def flash_can(handle, code, mcu_type):
assert handle.controlRead(REQUEST_IN, 0xb0, 0, 0, 0xc)[4:8] == b"\xde\xad\xd0\x0d", "flasher not present"

apps_sectors_cumsum = accumulate(mcu_type.config.sector_sizes[1:])
last_sector = next((i + 1 for i, v in enumerate(apps_sectors_cumsum) if v > len(code)), -1)
assert last_sector >= 1, "Binary too small? No sector to erase."
assert last_sector < 7, "Binary too large! Risk of overwriting provisioning chunk."

handle.controlWrite(REQUEST_IN, 0xb1, 0, 0, b'')
for i in range(1, last_sector + 1):
handle.controlWrite(REQUEST_IN, 0xb2, i, 0, b'')

for i in range(0, len(code), FLASH_STEP):
handle.bulkWrite(2, code[i:i + FLASH_STEP])

try:
handle.controlWrite(REQUEST_IN, 0xd8, 0, 0, b'', expect_disconnect=True)
except Exception:
pass

def flasher(p, addr, file):
p.can_send(addr, b"\xce\xfa\xad\xde\x1e\x0b\xb0\x0a", 0)
time.sleep(0.1)
Expand All @@ -29,7 +99,7 @@ def flasher(p, addr, file):
retries = 3 # How many times to retry on timeout error
while(retries+1>0):
try:
Panda.flash_static(CanHandle(p, 0), code, MCU_TYPE_F4)
flash_can(CanHandle(p.can_send, p.can_recv), code, MCU_TYPE_F4)
except TimeoutError:
print("Timeout, trying again...")
retries -= 1
Expand All @@ -51,7 +121,7 @@ def flasher(p, addr, file):

p = Panda()
_thread.start_new_thread(heartbeat_thread, (p,))
p.set_safety_mode(Panda.SAFETY_BODY)
p.set_safety_mode(CarParams.SafetyModel.body)

print("Flashing motherboard")
flasher(p, addr, args.fn)
Expand Down
Loading