From eef37df3aaeab98ac8df4ad6447680228d2a9772 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 5 Nov 2025 14:45:29 +0100 Subject: [PATCH] target: cortex-m: defer cache identification on Cortex-M7 under reset On Cortex-M7 only, several registers in System Control Space (SCS) are not accessible when the CPU is under reset, generating a bus error. This causes OpenOCD to fail examining the CPU when the board reset button is pressed or when the flag 'connect_assert_srst' is used on 'reset_config' command. Introduce a deferred identification of the cache and run it during polling and at target halted (just in case of polling disabled). Change-Id: Ia5c582ae95f825c5fb8c2dcfb320142f7ac04a9f Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/9232 Reviewed-by: Tomas Vanek Tested-by: jenkins --- src/target/armv7m_cache.c | 55 ++++++++++++++++++++++++++++++++++++++- src/target/armv7m_cache.h | 2 ++ src/target/cortex_m.c | 28 ++++++++++++++------ 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/target/armv7m_cache.c b/src/target/armv7m_cache.c index cc0c9d140..f07ac142f 100644 --- a/src/target/armv7m_cache.c +++ b/src/target/armv7m_cache.c @@ -68,7 +68,7 @@ static struct armv7m_cache_size decode_ccsidr(uint32_t ccsidr) return size; } -int armv7m_identify_cache(struct target *target) +static int armv7m_identify_cache_internal(struct target *target) { struct armv7m_common *armv7m = target_to_armv7m(target); struct armv7m_cache_common *cache = &armv7m->armv7m_cache; @@ -191,6 +191,54 @@ int armv7m_identify_cache(struct target *target) return ERROR_OK; } +/* + * On Cortex-M7 only, when the CPU is kept in reset, several registers of the + * System Control Space (SCS) are not accessible and return bus error. + * The list of accessible registers is: + * - 0xE000ED00 + * - 0xE000ED30 + * - 0xE000EDF0 ... 0xE000EEFC + * - 0xE000EF40 ... 0xE000EF48 + * - 0xE000EFD0 ... 0xE000EFFC + * This makes impossible detecting the cache during the reset. + * Use a deferred mechanism to detect the cache during polling or when the + * Cortex-M7 halts. + */ +int armv7m_identify_cache(struct target *target) +{ + struct cortex_m_common *cortex_m = target_to_cm(target); + struct armv7m_common *armv7m = target_to_armv7m(target); + struct armv7m_cache_common *cache = &armv7m->armv7m_cache; + + if (cache->info_valid) + return ERROR_OK; + + if (cortex_m->core_info->impl_part == CORTEX_M7_PARTNO + && cortex_m->dcb_dhcsr & S_RESET_ST) { + cache->defer_identification = true; + return ERROR_OK; + } + + return armv7m_identify_cache_internal(target); +} + +int armv7m_deferred_identify_cache(struct target *target) +{ + struct armv7m_common *armv7m = target_to_armv7m(target); + struct armv7m_cache_common *cache = &armv7m->armv7m_cache; + + if (cache->info_valid || !cache->defer_identification) + return ERROR_OK; + + int retval = armv7m_identify_cache_internal(target); + if (retval != ERROR_OK) + return retval; + + cache->defer_identification = false; + + return ERROR_OK; +} + int armv7m_d_cache_flush(struct target *target, uint32_t address, unsigned int length) { @@ -250,6 +298,11 @@ int armv7m_handle_cache_info_command(struct command_invocation *cmd, return ERROR_FAIL; } + if (cache->defer_identification) { + command_print(cmd, "Cache not detected yet"); + return ERROR_OK; + } + if (!cache->info_valid) { command_print(cmd, "No cache detected"); return ERROR_OK; diff --git a/src/target/armv7m_cache.h b/src/target/armv7m_cache.h index 576bff8d6..e6d943209 100644 --- a/src/target/armv7m_cache.h +++ b/src/target/armv7m_cache.h @@ -38,6 +38,7 @@ struct armv7m_arch_cache { // common cache information struct armv7m_cache_common { bool info_valid; + bool defer_identification; bool has_i_cache; bool has_d_u_cache; unsigned int loc; // level of coherency @@ -47,6 +48,7 @@ struct armv7m_cache_common { }; int armv7m_identify_cache(struct target *target); +int armv7m_deferred_identify_cache(struct target *target); int armv7m_d_cache_flush(struct target *target, uint32_t address, unsigned int length); int armv7m_i_cache_inval(struct target *target, uint32_t address, diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index d15575bd7..ea94f1242 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -876,6 +876,10 @@ static int cortex_m_debug_entry(struct target *target) } // read caches state + retval = armv7m_deferred_identify_cache(target); + if (retval != ERROR_OK) + return retval; + uint32_t ccr = 0; if (armv7m->armv7m_cache.info_valid) { retval = mem_ap_read_u32(armv7m->debug_ap, CCR, &ccr); @@ -1018,6 +1022,10 @@ static int cortex_m_poll_one(struct target *target) /* S_RESET_ST was expected (in a reset command). Continue processing * to quickly get out of TARGET_RESET state */ + } else { + retval = armv7m_deferred_identify_cache(target); + if (retval != ERROR_OK) + return retval; } if (target->state == TARGET_RESET) { @@ -2952,6 +2960,18 @@ int cortex_m_examine(struct target *target) if (retval != ERROR_OK) return retval; + /* + * Use a safe value of sticky S_RESET_ST for cache detection, before + * clearing it below. + */ + if (!armv7m->is_hla_target) { + retval = armv7m_identify_cache(target); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot detect cache"); + return retval; + } + } + /* Don't cumulate sticky S_RESET_ST at the very first read of DHCSR * as S_RESET_ST may indicate a reset that happened long time ago * (most probably the power-on reset before OpenOCD was started). @@ -3021,14 +3041,6 @@ int cortex_m_examine(struct target *target) LOG_TARGET_INFO(target, "target has %d breakpoints, %d watchpoints", cortex_m->fp_num_code, cortex_m->dwt_num_comp); - - if (!armv7m->is_hla_target) { - retval = armv7m_identify_cache(target); - if (retval != ERROR_OK) { - LOG_ERROR("Cannot detect cache"); - return retval; - } - } } return ERROR_OK;