This is an automated email from Gerrit.

"Antonio Borneo <[email protected]>" 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 <[email protected]>
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 <[email protected]>

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 <[email protected]>
+ */
+
+#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 <[email protected]>
+ */
+
+#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

-- 

Reply via email to