pandad: recover from bad bootstub (#29638)

* pandad: recover from bad bootstub

* more

* adjust that

* ugh guess multipanda is still a thing

* reset

---------

Co-authored-by: Comma Device <device@comma.ai>
old-commit-hash: 683b3b5966
beeps
Adeeb Shihadeh 2 years ago committed by GitHub
parent 48d933970b
commit 26c7c08982
  1. 22
      selfdrive/boardd/pandad.py
  2. 48
      selfdrive/boardd/tests/test_pandad.py
  3. 6
      system/hardware/tici/hardware.py

@ -129,6 +129,7 @@ def main() -> NoReturn:
count = 0 count = 0
first_run = True first_run = True
params = Params() params = Params()
no_internal_panda_count = 0
while True: while True:
try: try:
@ -136,6 +137,16 @@ def main() -> NoReturn:
cloudlog.event("pandad.flash_and_connect", count=count) cloudlog.event("pandad.flash_and_connect", count=count)
params.remove("PandaSignatures") params.remove("PandaSignatures")
# Handle missing internal panda
if no_internal_panda_count > 0:
if no_internal_panda_count == 3:
cloudlog.info("No pandas found, putting internal panda into DFU")
HARDWARE.recover_internal_panda()
else:
cloudlog.info("No pandas found, resetting internal panda")
HARDWARE.reset_internal_panda()
time.sleep(3) # wait to come back up
# Flash all Pandas in DFU mode # Flash all Pandas in DFU mode
dfu_serials = PandaDFU.list() dfu_serials = PandaDFU.list()
if len(dfu_serials) > 0: if len(dfu_serials) > 0:
@ -146,10 +157,7 @@ def main() -> NoReturn:
panda_serials = Panda.list() panda_serials = Panda.list()
if len(panda_serials) == 0: if len(panda_serials) == 0:
if first_run: no_internal_panda_count += 1
cloudlog.info("No pandas found, resetting internal panda")
HARDWARE.reset_internal_panda()
time.sleep(2) # wait to come back up
continue continue
cloudlog.info(f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}") cloudlog.info(f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}")
@ -162,10 +170,10 @@ def main() -> NoReturn:
# Ensure internal panda is present if expected # Ensure internal panda is present if expected
internal_pandas = [panda for panda in pandas if panda.is_internal()] internal_pandas = [panda for panda in pandas if panda.is_internal()]
if HARDWARE.has_internal_panda() and len(internal_pandas) == 0: if HARDWARE.has_internal_panda() and len(internal_pandas) == 0:
cloudlog.error("Internal panda is missing, resetting") cloudlog.error("Internal panda is missing, trying again")
HARDWARE.reset_internal_panda() no_internal_panda_count += 1
time.sleep(2) # wait to come back up
continue continue
no_internal_panda_count = 0
# sort pandas to have deterministic order # sort pandas to have deterministic order
pandas.sort(key=cmp_to_key(panda_sort_cmp)) pandas.sort(key=cmp_to_key(panda_sort_cmp))

@ -25,13 +25,17 @@ class TestPandad(unittest.TestCase):
def tearDown(self): def tearDown(self):
managed_processes['pandad'].stop() managed_processes['pandad'].stop()
def _wait_for_boardd(self, timeout=30): def _run_test(self, timeout=30):
managed_processes['pandad'].start()
sm = messaging.SubMaster(['peripheralState']) sm = messaging.SubMaster(['peripheralState'])
for _ in range(timeout*10): for _ in range(timeout*10):
sm.update(100) sm.update(100)
if sm['peripheralState'].pandaType != log.PandaState.PandaType.unknown: if sm['peripheralState'].pandaType != log.PandaState.PandaType.unknown:
break break
managed_processes['pandad'].stop()
if sm['peripheralState'].pandaType == log.PandaState.PandaType.unknown: if sm['peripheralState'].pandaType == log.PandaState.PandaType.unknown:
raise Exception("boardd failed to start") raise Exception("boardd failed to start")
@ -43,7 +47,11 @@ class TestPandad(unittest.TestCase):
HARDWARE.recover_internal_panda() HARDWARE.recover_internal_panda()
assert Panda.wait_for_dfu(None, 10) assert Panda.wait_for_dfu(None, 10)
def _flash_and_test(self, fn, expect_mismatch=False): def _assert_no_panda(self):
assert not Panda.wait_for_dfu(None, 3)
assert not Panda.wait_for_panda(None, 3)
def _flash_bootstub_and_test(self, fn, expect_mismatch=False):
self._go_to_dfu() self._go_to_dfu()
pd = PandaDFU(None) pd = PandaDFU(None)
if fn is None: if fn is None:
@ -61,22 +69,19 @@ class TestPandad(unittest.TestCase):
with Panda() as p: with Panda() as p:
assert p.bootstub assert p.bootstub
managed_processes['pandad'].start() self._run_test(45)
self._wait_for_boardd(45)
@phone_only @phone_only
def test_in_dfu(self): def test_in_dfu(self):
HARDWARE.recover_internal_panda() HARDWARE.recover_internal_panda()
managed_processes['pandad'].start() self._run_test(60)
self._wait_for_boardd(60)
@phone_only @phone_only
def test_in_bootstub(self): def test_in_bootstub(self):
with Panda() as p: with Panda() as p:
p.reset(enter_bootstub=True) p.reset(enter_bootstub=True)
assert p.bootstub assert p.bootstub
managed_processes['pandad'].start() self._run_test()
self._wait_for_boardd()
@phone_only @phone_only
def test_internal_panda_reset(self): def test_internal_panda_reset(self):
@ -84,22 +89,17 @@ class TestPandad(unittest.TestCase):
gpio_set(GPIO.STM_RST_N, 1) gpio_set(GPIO.STM_RST_N, 1)
time.sleep(0.5) time.sleep(0.5)
assert all(not Panda(s).is_internal() for s in Panda.list()) assert all(not Panda(s).is_internal() for s in Panda.list())
self._run_test()
managed_processes['pandad'].start()
self._wait_for_boardd()
assert any(Panda(s).is_internal() for s in Panda.list()) assert any(Panda(s).is_internal() for s in Panda.list())
@phone_only @phone_only
def test_best_case_startup_time(self): def test_best_case_startup_time(self):
# run once so we're setup # run once so we're setup
managed_processes['pandad'].start() self._run_test()
self._wait_for_boardd()
managed_processes['pandad'].stop()
# should be fast this time # should be fast this time
managed_processes['pandad'].start() self._run_test(8)
self._wait_for_boardd(8)
@phone_only @phone_only
def test_protocol_version_check(self): def test_protocol_version_check(self):
@ -108,11 +108,23 @@ class TestPandad(unittest.TestCase):
# flash old fw # flash old fw
fn = os.path.join(HERE, "bootstub.panda_h7_spiv0.bin") fn = os.path.join(HERE, "bootstub.panda_h7_spiv0.bin")
self._flash_and_test(fn, expect_mismatch=True) self._flash_bootstub_and_test(fn, expect_mismatch=True)
@phone_only @phone_only
def test_release_to_devel_bootstub(self): def test_release_to_devel_bootstub(self):
self._flash_and_test(None) self._flash_bootstub_and_test(None)
@phone_only
def test_recover_from_bad_bootstub(self):
self._go_to_dfu()
with PandaDFU(None) as pd:
pd.program_bootstub(b"\x00"*1024)
pd.reset()
HARDWARE.reset_internal_panda()
self._assert_no_panda()
self._run_test(60)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

@ -586,7 +586,7 @@ class Tici(HardwareBase):
gpio_init(GPIO.STM_RST_N, True) gpio_init(GPIO.STM_RST_N, True)
gpio_set(GPIO.STM_RST_N, 1) gpio_set(GPIO.STM_RST_N, 1)
time.sleep(2) time.sleep(1)
gpio_set(GPIO.STM_RST_N, 0) gpio_set(GPIO.STM_RST_N, 0)
def recover_internal_panda(self): def recover_internal_panda(self):
@ -595,9 +595,9 @@ class Tici(HardwareBase):
gpio_set(GPIO.STM_RST_N, 1) gpio_set(GPIO.STM_RST_N, 1)
gpio_set(GPIO.STM_BOOT0, 1) gpio_set(GPIO.STM_BOOT0, 1)
time.sleep(1) time.sleep(0.5)
gpio_set(GPIO.STM_RST_N, 0) gpio_set(GPIO.STM_RST_N, 0)
time.sleep(1) time.sleep(0.5)
gpio_set(GPIO.STM_BOOT0, 0) gpio_set(GPIO.STM_BOOT0, 0)

Loading…
Cancel
Save