{"record":{"id":"7e576cee3afa78b3","repo":"commaai/openpilot","slug":"could-not-clear-spi-block-protection","errorCode":null,"errorMessage":"could not clear SPI block protection","messagePattern":"could not clear SPI block protection","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":227,"sourceCode":"    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:\n      n = min(4096, length - len(out))\n      self.transaction(0x03, addr + len(out), max(4096, n))\n      for off in range(0, n, 255):\n        out += self.reg_read(0x7000 + off, min(255, n - off))\n    return bytes(out)\n\n  def erase_sector(self, addr):\n    self.write_enable()\n    self.transaction(0x20, addr)\n    self.wait_write_done()\n\n  def program(self, addr, data):\n    self.write_buffer(data + bytes((-len(data)) % 4))\n    self.write_enable()","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L209-L245","documentation":"RuntimeError raised at the end of Flash.init(): the routine tries 5 times to clear the SPI status-register block-protection bits by issuing write-enable + write-status-register (command 0x01) with a zeroed 4-byte buffer, then reads back status(). If status() & 0x1C (block-protect bits) is still nonzero after 5 attempts, protection could not be cleared and any subsequent erase/program would silently fail.","triggerScenarios":"Flash.init() on a device whose status register stays protected: a write-enable that never latched (WEL dropped), a controller in the wrong mode because regs 0xCC33/0xCA81/0xC805/0xC8A6 were not applied to a live claim, or a hardware-protected or wrong-model flash chip.","commonSituations":"Running init() right after connect() on a half-enumerated device; status reads corrupted by a flaky USB link so the retry loop can never succeed; a flash chip variant with different protection semantics; device needing a power cycle to reset the status register.","solutions":["Power-cycle the enclosure (unplug/replug USB-C, or toggle VBUS) and retry the full connect()+init() sequence - a cold reset usually clears latched protection state","Make sure init() runs on a fresh reconnect (close + claim again) so the controller registers are actually applied","If it fails on every attempt with a stable link, read status() manually and compare the bit pattern against the flash datasheet - bits that never clear indicate a wrong flash part or dead chip","Replace the enclosure if protection genuinely cannot be cleared (hardware fault)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"def protection_cleared(flash) -> bool:\n    try:\n        return not flash.status() & 0x1C\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    flash.init()\nexcept RuntimeError as e:\n    if 'block protection' in str(e):\n        power_cycle_enclosure()\n        flash.connect(); flash.init()  # one cold-retry","preventionTips":["Cold-boot the enclosure before flashing to clear latched status state","Never skip init() after claiming the interface","Track units that repeatedly fail init - likely hardware, replace rather than loop"],"tags":["spi","block-protection","init","hardware","flashing"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}