From: <ge...@op...> - 2025-08-11 08:36:44
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9077 -- gerrit commit 6a6f5dbecbded921a5b42c0cbb4e6b57c3073df4 Author: Antonio Borneo <bor...@gm...> Date: Tue Aug 5 12:06:22 2025 +0200 target: cortex-m: add support for armv8m caches Cores like Cortex-M7, Cortex-M55 and Cortex-M85 can have either D-Cache and/or I-Cache. Using SW breakpoints in RAM requires handling these caches. Detect the presence of cache at examine. Detect cache state (enable/disable) at debug entry. Take care of caches synchronization through the PoC (usually the SRAM) while setting and removing SW breakpoints. Add command 'cache_info' to check cache presence and size. Change-Id: Ice637c215fe3042c8fff57edefbab1b86515ef4b Signed-off-by: Antonio Borneo <bor...@gm...> diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 1a76864180..0b5a85704c 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -75,6 +75,7 @@ ARMV6_SRC = \ ARMV7_SRC = \ %D%/armv7m.c \ + %D%/armv7m_cache.c \ %D%/armv7m_trace.c \ %D%/cortex_m.c \ %D%/armv7a.c \ @@ -183,6 +184,7 @@ ARC_SRC = \ %D%/armv4_5_cache.h \ %D%/armv7a.h \ %D%/armv7m.h \ + %D%/armv7m_cache.h \ %D%/armv7m_trace.h \ %D%/armv8.h \ %D%/armv8_dpm.h \ diff --git a/src/target/armv7m.h b/src/target/armv7m.h index 86c45f7f26..942e22584d 100644 --- a/src/target/armv7m.h +++ b/src/target/armv7m.h @@ -15,6 +15,7 @@ #define OPENOCD_TARGET_ARMV7M_H #include "arm.h" +#include "armv7m_cache.h" #include "armv7m_trace.h" struct adiv5_ap; @@ -239,6 +240,8 @@ struct armv7m_common { /* hla_target uses a high level adapter that does not support all functions */ bool is_hla_target; + struct armv7m_cache_common armv7m_cache; + struct armv7m_trace_config trace_config; /* Direct processor core register read and writes */ diff --git a/src/target/armv7m_cache.c b/src/target/armv7m_cache.c new file mode 100644 index 0000000000..c46211b21a --- /dev/null +++ b/src/target/armv7m_cache.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2025 by STMicroelectronics + * Copyright (C) 2025 by Antonio Borneo <bor...@gm...> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdint.h> + +#include <helper/align.h> +#include <helper/bitfield.h> +#include <helper/bits.h> +#include <helper/command.h> +#include <helper/log.h> +#include <helper/types.h> +#include <target/armv7m_cache.h> +#include <target/cortex_m.h> + +static int get_cache_info(struct target *target, unsigned int cl, + unsigned int ind, uint32_t *ccsidr) +{ + uint32_t csselr = FIELD_PREP(CSSELR_LEVEL_MASK, cl) + | FIELD_PREP(CSSELR_IND_MASK, ind); + + target_write_u32(target, CSSELR, csselr); + + return target_read_u32(target, CCSIDR, ccsidr); +} + +static int get_d_ucache_info(struct target *target, unsigned int cl, + uint32_t *ccsidr) +{ + return get_cache_info(target, cl, CSSELR_IND_DATA_OR_UNIFIED_CACHE, ccsidr); +} + +static int get_icache_info(struct target *target, unsigned int cl, + uint32_t *ccsidr) +{ + return get_cache_info(target, cl, CSSELR_IND_INSTRUCTION_CACHE, ccsidr); +} + +static struct armv7m_cachesize decode_ccsidr(uint32_t ccsidr) +{ + struct armv7m_cachesize size; + + size.linelen = 16 << FIELD_GET(CCSIDR_LINESIZE_MASK, ccsidr); + size.associativity = FIELD_GET(CCSIDR_ASSOCIATIVITY_MASK, ccsidr) + 1; + size.nsets = FIELD_GET(CCSIDR_NUMSETS_MASK, ccsidr) + 1; + size.cachesize = size.linelen * size.associativity * size.nsets / 1024; + + /* compute info for set way operation on cache */ + size.index_shift = FIELD_GET(CCSIDR_LINESIZE_MASK, ccsidr) + 2; + size.index = FIELD_GET(CCSIDR_NUMSETS_MASK, ccsidr); + size.way = FIELD_GET(CCSIDR_ASSOCIATIVITY_MASK, ccsidr); + + unsigned int i = 0; + while (((size.way << i) & 0x80000000) == 0) + i++; + size.way_shift = i; + + return size; +} + +int armv7m_identify_cache(struct target *target) +{ + struct armv7m_common *armv7m = target_to_armv7m(target); + struct armv7m_cache_common *cache = &armv7m->armv7m_cache; + + uint32_t clidr; + int retval = target_read_u32(target, CLIDR, &clidr); + if (retval != ERROR_OK) + return retval; + + uint32_t ctr; + retval = target_read_u32(target, CTR, &ctr); + if (retval != ERROR_OK) + return retval; + + if (clidr == 0) { + LOG_TARGET_DEBUG(target, "No cache detected"); + return ERROR_OK; + } + + if (FIELD_GET(CTR_FORMAT_MASK, ctr) != CTR_FORMAT_PROVIDED) { + LOG_ERROR("Wrong value in CTR register"); + return ERROR_FAIL; + } + + cache->iminline = 4UL << FIELD_GET(CTR_IMINLINE_MASK, ctr); + cache->dminline = 4UL << FIELD_GET(CTR_DMINLINE_MASK, ctr); + LOG_TARGET_DEBUG(target, + "ctr=0x%" PRIx32 " ctr.iminline=%" PRIu32 " ctr.dminline=%" PRIu32, + ctr, cache->iminline, cache->dminline); + + cache->loc = FIELD_GET(CLIDR_LOC_MASK, clidr); + LOG_TARGET_DEBUG(target, + "clidr=0x%" PRIx32 " Number of cache levels to PoC=%" PRIu32, + clidr, cache->loc); + + /* retrieve selected cache for later restore */ + uint32_t csselr; + retval = target_read_u32(target, CSSELR, &csselr); + if (retval != ERROR_OK) + return retval; + + /* retrieve all available inner caches */ + for (unsigned int cl = 0; cl < cache->loc; cl++) { + unsigned int ctype = FIELD_GET(CLIDR_CTYPE_MASK(cl + 1), clidr); + + /* skip reserved values */ + if (ctype > CLIDR_CTYPE_UNIFIED_CACHE) + continue; + + /* separate d or unified d/i cache at this level ? */ + if (ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE)) { + /* retrieve d-cache info */ + uint32_t ccsidr; + retval = get_d_ucache_info(target, cl, &ccsidr); + if (retval != ERROR_OK) + goto restore_csselr; + + cache->arch[cl].d_u_size = decode_ccsidr(ccsidr); + + LOG_TARGET_DEBUG(target, + "data/unified cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32, + cache->arch[cl].d_u_size.index, + cache->arch[cl].d_u_size.index_shift, + cache->arch[cl].d_u_size.way, + cache->arch[cl].d_u_size.way_shift); + + LOG_TARGET_DEBUG(target, + "cacheline %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways", + cache->arch[cl].d_u_size.linelen, + cache->arch[cl].d_u_size.cachesize, + cache->arch[cl].d_u_size.associativity); + } + + if (ctype & CLIDR_CTYPE_I_CACHE) { + /* retrieve i-cache info */ + uint32_t ccsidr; + retval = get_icache_info(target, cl, &ccsidr); + if (retval != ERROR_OK) + goto restore_csselr; + + cache->arch[cl].i_size = decode_ccsidr(ccsidr); + + LOG_TARGET_DEBUG(target, + "instruction cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32, + cache->arch[cl].i_size.index, + cache->arch[cl].i_size.index_shift, + cache->arch[cl].i_size.way, + cache->arch[cl].i_size.way_shift); + + LOG_TARGET_DEBUG(target, + "cacheline %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways", + cache->arch[cl].i_size.linelen, + cache->arch[cl].i_size.cachesize, + cache->arch[cl].i_size.associativity); + } + + cache->arch[cl].ctype = ctype; + } + +restore_csselr: + /* restore selected cache */ + target_write_u32(target, CSSELR, csselr); + + if (retval == ERROR_OK) + cache->info_valid = true; + + return retval; +} + +int armv7m_d_cache_flush(struct target *target, uint32_t address, + unsigned int length) +{ + struct armv7m_common *armv7m = target_to_armv7m(target); + struct armv7m_cache_common *cache = &armv7m->armv7m_cache; + + if (!cache->info_valid) + return ERROR_OK; + + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!armv7m->armv7m_cache.d_u_cache_enabled) + return ERROR_OK; + + uint32_t linelen = cache->dminline; + uint32_t addr_line = ALIGN_DOWN(address, linelen); + uint32_t addr_end = address + length; + + while (addr_line < addr_end) { + target_write_u32(target, DCCIMVAC, addr_line); + addr_line += linelen; + keep_alive(); + } + + return ERROR_OK; +} + +int armv7m_i_cache_inval(struct target *target, uint32_t address, + unsigned int length) +{ + struct armv7m_common *armv7m = target_to_armv7m(target); + struct armv7m_cache_common *cache = &armv7m->armv7m_cache; + + if (!cache->info_valid) + return ERROR_OK; + + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!armv7m->armv7m_cache.i_cache_enabled) + return ERROR_OK; + + uint32_t linelen = cache->iminline; + uint32_t addr_line = ALIGN_DOWN(address, linelen); + uint32_t addr_end = address + length; + + while (addr_line < addr_end) { + target_write_u32(target, ICIMVAU, addr_line); + addr_line += linelen; + keep_alive(); + } + + return ERROR_OK; +} + + +int armv7m_handle_cache_info_command(struct command_invocation *cmd, + struct target *target) +{ + struct armv7m_common *armv7m = target_to_armv7m(target); + struct armv7m_cache_common *cache = &armv7m->armv7m_cache; + + if (!target_was_examined(target)) { + command_print(cmd, "Target not examined yet"); + return ERROR_FAIL; + } + + if (!cache->info_valid) { + command_print(cmd, "No cache detected"); + return ERROR_OK; + } + + for (unsigned int cl = 0; cl < cache->loc; cl++) { + struct armv7m_arch_cache *arch = &cache->arch[cl]; + + if (arch->ctype & CLIDR_CTYPE_I_CACHE) + command_print(cmd, + "L%d I-Cache: linelen %" PRIu32 ", associativity %" PRIu32 + ", nsets %" PRIu32 ", cachesize %" PRIu32 " KBytes", + cl + 1, + arch->i_size.linelen, + arch->i_size.associativity, + arch->i_size.nsets, + arch->i_size.cachesize); + + if (arch->ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE)) + command_print(cmd, + "L%d %c-Cache: linelen %" PRIu32 ", associativity %" PRIu32 + ", nsets %" PRIu32 ", cachesize %" PRIu32 " KBytes", + cl + 1, + (arch->ctype & CLIDR_CTYPE_D_CACHE) ? 'D' : 'U', + arch->d_u_size.linelen, + arch->d_u_size.associativity, + arch->d_u_size.nsets, + arch->d_u_size.cachesize); + } + + return ERROR_OK; +} diff --git a/src/target/armv7m_cache.h b/src/target/armv7m_cache.h new file mode 100644 index 0000000000..19cb7c82c4 --- /dev/null +++ b/src/target/armv7m_cache.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Copyright (C) 2025 by STMicroelectronics + * Copyright (C) 2025 by Antonio Borneo <bor...@gm...> + */ + +#ifndef OPENOCD_TARGET_ARMV7M_CACHE_H +#define OPENOCD_TARGET_ARMV7M_CACHE_H + +#include <stdbool.h> +#include <stdint.h> + +#include <helper/types.h> + +struct target; + +struct armv7m_cachesize { + /* cache dimensioning */ + uint32_t linelen; + uint32_t associativity; + uint32_t nsets; + uint32_t cachesize; + /* info for set way operation on cache */ + uint32_t index; + uint32_t index_shift; + uint32_t way; + uint32_t way_shift; +}; + +/* information about one architecture cache at any level */ +struct armv7m_arch_cache { + unsigned int ctype; /* cache type, CLIDR encoding */ + struct armv7m_cachesize d_u_size; /* data cache */ + struct armv7m_cachesize i_size; /* instruction cache */ +}; + +/* common cache information */ +struct armv7m_cache_common { + bool info_valid; + unsigned int loc; /* level of coherency */ + uint32_t dminline; /* minimum d-cache linelen */ + uint32_t iminline; /* minimum i-cache linelen */ + struct armv7m_arch_cache arch[6]; /* cache info, L1 - L7 */ + bool i_cache_enabled; + bool d_u_cache_enabled; +}; + +int armv7m_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, + unsigned int length); +int armv7m_handle_cache_info_command(struct command_invocation *cmd, + struct target *target); + +#endif /* OPENOCD_TARGET_ARMV7M_CACHE_H */ diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index a90610b088..b635072942 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -21,6 +21,7 @@ #include "jtag/interface.h" #include "breakpoints.h" #include "cortex_m.h" +#include "armv7m_cache.h" #include "target_request.h" #include "target_type.h" #include "arm_adi_v5.h" @@ -30,6 +31,7 @@ #include "arm_semihosting.h" #include "smp.h" #include <helper/nvp.h> +#include <helper/string_choices.h> #include <helper/time_support.h> #include <rtt/rtt.h> @@ -926,6 +928,20 @@ static int cortex_m_debug_entry(struct target *target) secure_state ? "Secure" : "Non-Secure", target_state_name(target)); + if (armv7m->armv7m_cache.info_valid) { + uint32_t ccr; + retval = target_read_u32(target, CCR, &ccr); + if (retval != ERROR_OK) + return retval; + + armv7m->armv7m_cache.d_u_cache_enabled = ccr & CCR_DC_MASK; + armv7m->armv7m_cache.i_cache_enabled = ccr & CCR_IC_MASK; + + LOG_TARGET_DEBUG(target, "D-Cache %s, I-Cache %s", + str_enabled_disabled(armv7m->armv7m_cache.d_u_cache_enabled), + str_enabled_disabled(armv7m->armv7m_cache.i_cache_enabled)); + } + /* Errata 3092511 workaround * Cortex-M7 can halt in an incorrect address when breakpoint * and exception occurs simultaneously */ @@ -1938,12 +1954,25 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint breakpoint->orig_instr); if (retval != ERROR_OK) return retval; + /* make sure data cache is cleaned & invalidated down to PoC */ + retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length); + if (retval != ERROR_OK) + return retval; + retval = target_write_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, code); if (retval != ERROR_OK) return retval; + /* update i-cache at breakpoint location */ + retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length); + if (retval != ERROR_OK) + return retval; + retval = armv7m_i_cache_inval(target, breakpoint->address, breakpoint->length); + if (retval != ERROR_OK) + return retval; + breakpoint->is_set = true; } @@ -1986,12 +2015,25 @@ int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoi target_write_u32(target, comparator_list[fp_num].fpcr_address, comparator_list[fp_num].fpcr_value); } else { + /* make sure data cache is cleaned & invalidated down to PoC */ + retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length); + if (retval != ERROR_OK) + return retval; + /* restore original instruction (kept in target endianness) */ retval = target_write_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, breakpoint->orig_instr); if (retval != ERROR_OK) return retval; + + /* update i-cache at breakpoint location */ + retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length); + if (retval != ERROR_OK) + return retval; + retval = armv7m_i_cache_inval(target, breakpoint->address, breakpoint->length); + if (retval != ERROR_OK) + return retval; } breakpoint->is_set = false; @@ -2906,6 +2948,12 @@ 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); + + retval = armv7m_identify_cache(target); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot detect cache"); + return retval; + } } return ERROR_OK; @@ -3235,6 +3283,13 @@ COMMAND_HANDLER(handle_cortex_m_reset_config_command) return ERROR_OK; } +COMMAND_HANDLER(handle_cortex_m_cache_info_command) +{ + struct target *target = get_current_target(CMD_CTX); + + return armv7m_handle_cache_info_command(CMD, target); +} + static const struct command_registration cortex_m_exec_command_handlers[] = { { .name = "maskisr", @@ -3257,6 +3312,13 @@ static const struct command_registration cortex_m_exec_command_handlers[] = { .help = "configure software reset handling", .usage = "['sysresetreq'|'vectreset']", }, + { + .name = "cache_info", + .handler = handle_cortex_m_cache_info_command, + .mode = COMMAND_EXEC, + .help = "display information about target caches", + .usage = "", + }, { .chain = smp_command_handlers, }, diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h index 82b2c1ecde..6b8b410a16 100644 --- a/src/target/cortex_m.h +++ b/src/target/cortex_m.h @@ -15,6 +15,7 @@ #define OPENOCD_TARGET_CORTEX_M_H #include "armv7m.h" +#include "helper/bitfield.h" #include "helper/bits.h" #define CORTEX_M_COMMON_MAGIC 0x1A451A45U @@ -114,6 +115,45 @@ struct cortex_m_part_info { #define FPU_FPCAR 0xE000EF38 #define FPU_FPDSCR 0xE000EF3C +/* Cache */ +#define CCR 0xE000ED14 +#define CLIDR 0xE000ED78 +#define CTR 0xE000ED7C +#define CCSIDR 0xE000ED80 +#define CSSELR 0xE000ED84 +#define ICIMVAU 0xE000EF58 +#define DCCIMVAC 0xE000EF70 + +#define CCR_IC_MASK BIT(17) +#define CCR_DC_MASK BIT(16) + +#define CLIDR_ICB_MASK GENMASK(31, 30) +#define CLIDR_LOUU_MASK GENMASK(29, 27) +#define CLIDR_LOC_MASK GENMASK(26, 24) +#define CLIDR_LOUIS_MASK GENMASK(23, 21) +#define CLIDR_CTYPE_MASK(i) (GENMASK(2, 0) << (3 * (i) - 3)) + +#define CLIDR_CTYPE_I_CACHE BIT(0) +#define CLIDR_CTYPE_D_CACHE BIT(1) +#define CLIDR_CTYPE_UNIFIED_CACHE BIT(2) + +#define CTR_FORMAT_MASK GENMASK(31, 29) +#define CTR_CWG_MASK GENMASK(27, 24) +#define CTR_ERG_MASK GENMASK(23, 20) +#define CTR_DMINLINE_MASK GENMASK(19, 16) +#define CTR_IMINLINE_MASK GENMASK(3, 0) + +#define CTR_FORMAT_PROVIDED 0x04 + +#define CCSIDR_NUMSETS_MASK GENMASK(27, 13) +#define CCSIDR_ASSOCIATIVITY_MASK GENMASK(12, 3) +#define CCSIDR_LINESIZE_MASK GENMASK(2, 0) + +#define CSSELR_LEVEL_MASK GENMASK(3, 1) +#define CSSELR_IND_MASK BIT(0) +#define CSSELR_IND_DATA_OR_UNIFIED_CACHE 0 +#define CSSELR_IND_INSTRUCTION_CACHE 1 + #define TPIU_SSPSR 0xE0040000 #define TPIU_CSPSR 0xE0040004 #define TPIU_ACPR 0xE0040010 -- |