From 7c7d57a6f0284452c6f75334fc51043432f8a1c0 Mon Sep 17 00:00:00 2001 From: James Walmsley Date: Fri, 10 Jul 2026 13:56:42 +0100 Subject: [PATCH] common: disarm vector-catch-on-reset on debug detach st-flash --connect-under-reset arms DEMCR.VC_CORERESET (halt-on-reset) via stlink_soft_reset(). That bit lives in the debug power domain and survives NRST, so once armed the core halts at its reset vector on *every* reset -- including NVIC_SystemReset() from firmware and the board's reset button -- which makes a freshly flashed board appear bricked until it is power-cycled. stlink_exit_debug_mode() only wrote DHCSR.DBGKEY on detach and never cleared the vector catch. Worse, that write (and thus any cleanup) was gated behind core_stat != TARGET_RESET, which is precisely the state connect-under-reset leaves the target in -- so the cleanup was skipped exactly when it was needed. Split the two: keep the DBGKEY write gated on core_stat, but always disarm DEMCR.VC_CORERESET (when a target was identified), preserving the other DEMCR bits and clearing the stale DFSR.VCATCH status. The DEMCR read return already guards against a dead debug link; a failed disarm write is logged via WLOG rather than failing the detach. This mirrors the existing AP1 flash-size fallback, which already disarms VC_CORERESET. Signed-off-by: James Walmsley --- src/stlink-lib/common_legacy.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/stlink-lib/common_legacy.c b/src/stlink-lib/common_legacy.c index a1c7e2b1..bb4c760f 100644 --- a/src/stlink-lib/common_legacy.c +++ b/src/stlink-lib/common_legacy.c @@ -455,10 +455,30 @@ int32_t stlink_exit_debug_mode(stlink_t *sl) { return 0; } - if(sl->flash_type != STM32_FLASH_TYPE_UNKNOWN && - sl->core_stat != TARGET_RESET) { - // stop debugging if the target has been identified - stlink_write_debug32(sl, STM32_REG_DHCSR, STM32_REG_DHCSR_DBGKEY); + if(sl->flash_type != STM32_FLASH_TYPE_UNKNOWN) { + if(sl->core_stat != TARGET_RESET) { + // stop debugging if the target has been identified + stlink_write_debug32(sl, STM32_REG_DHCSR, STM32_REG_DHCSR_DBGKEY); + } + + // Disarm vector-catch-on-reset before detaching. --connect-under-reset + // arms DEMCR.VC_CORERESET (halt-on-reset); it lives in the debug power + // domain and survives NRST, so if left armed the core halts on every reset + // -- including NVIC_SystemReset() from firmware -- and the board looks + // bricked until a power cycle. This must run even when core_stat is + // TARGET_RESET, which is exactly the connect-under-reset case that would + // otherwise skip the cleanup. Only the read failing (dead debug link) + // stops us, and we preserve the other DEMCR bits. + uint32_t demcr = 0; + if(!stlink_read_debug32(sl, STM32_REG_CM3_DEMCR, &demcr) && + (demcr & STM32_REG_CM3_DEMCR_VC_CORERESET)) { + if(stlink_write_debug32(sl, STM32_REG_CM3_DEMCR, + demcr & ~STM32_REG_CM3_DEMCR_VC_CORERESET)) { + WLOG("Could not clear DEMCR.VC_CORERESET; target may halt on reset\n"); + } + // clear the stale vector-catch status in DFSR + stlink_write_debug32(sl, STM32_REG_DFSR, STM32_REG_DFSR_VCATCH); + } } return (sl->backend->exit_debug_mode(sl));