{"record":{"id":"c15858a8de705318","repo":"commaai/openpilot","slug":"spi-flash-wip-timeout","errorCode":null,"errorMessage":"SPI flash WIP timeout","messagePattern":"SPI flash WIP timeout","errorType":"console","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":213,"sourceCode":"    for _ in range(4):\n      self.reg_write(0xC8AD, 0)\n\n  def write_enable(self):\n    for reg, value in ((0xC8AD, 0), (0xC8AA, 0x06), (0xC8AC, 0x04), (0xC8A3, 0), (0xC8A4, 0), (0xC8A9, 1)):\n      self.reg_write(reg, value)\n    self.wait_controller()\n\n  def status(self):\n    self.transaction(0x05, length=1, addr_len=0x04)\n    return self.reg_read(0x7000)[0]\n\n  def wait_write_done(self, timeout=10.0):\n    deadline = time.monotonic() + timeout\n    while time.monotonic() < deadline:\n      if not self.status() & 1:\n        return\n      time.sleep(0.005)\n    raise TimeoutError(\"SPI flash WIP timeout\")\n\n  def init(self):\n    self.reg_write(0xCC33, 0x04)\n    self.reg_write(0xCA81, self.reg_read(0xCA81)[0] | 1)\n    self.reg_write(0xC805, 0x02)\n    self.reg_write(0xC8A6, 0x04)\n    for _ in range(5):\n      self.write_enable()\n      self.write_buffer(bytes(4))\n      self.transaction(0x01, length=1, addr_len=0x04, mode=1)\n      time.sleep(0.01)\n      if not self.status() & 0x1C:\n        return\n    raise RuntimeError(\"could not clear SPI block protection\")\n\n  def read(self, addr, length):\n    out = bytearray()\n    while len(out) < length:","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L195-L231","documentation":"TimeoutError raised by Flash.wait_write_done() (default 10.0s): after a page program (0x02), sector erase (0x20), or write-status transaction, it polls the flash status register (command 0x05) and the Write-In-Progress bit (bit 0) never cleared within 10 seconds. Real SPI NOR erase/program takes milliseconds to a few hundred ms; 10s of continuous WIP means the flash is not acking writes or the status read is returning garbage.","triggerScenarios":"wait_write_done() after program()/erase_sector()/init()'s write-status attempt. Happens when the device disconnected so reg_read returns stale data, when the SPI controller needs re-init (init() not called after reconnect), or when writes are silently rejected and the poll reads a corrupt status.","commonSituations":"Recovering after connect() without flash.init() so the controller is in the wrong mode; flash chip with persistent block protection (see error 65); marginal USB link causing corrupt status reads; end-of-life flash chip that programs/erases extremely slowly.","solutions":["Ensure the reconnect path always re-runs flash.init() before any erase/program (the tool's reconnect() does this - use it, not bare connect())","Wrap erase/program in with_retries so a WIP timeout triggers reconnection and a fresh attempt","Check the status register for block-protection bits (status() & 0x1C) and clear them via init()'s write-status loop","If deterministic on one unit, suspect the SPI flash chip hardware and replace the enclosure"],"exampleFix":"# before\nflash.erase_sector(addr); flash.wait_write_done()\n\n# after: retry with reconnect on stall\nwith_retries(flash, f'sector 0x{addr:05x}', lambda: flash.erase_sector(addr))","handlingStrategy":"retry","validationCode":"def flash_ready(flash) -> bool:\n    try:\n        return (flash.status() & 0x1C) == 0  # protection clear implies writes can complete\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    flash.wait_write_done()\nexcept TimeoutError as e:\n    if 'WIP' in str(e):\n        reconnect(flash)  # re-inits controller\n        retry_write()","preventionTips":["Always call init() after reconnect() before erase/program","Confirm the status register shows no block protection before writing","Power-cycle the enclosure between failed sessions instead of hammering retries"],"tags":["spi","timeout","flash-write","hardware","flashing"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}