Skip to content

Commit 04da6e2

Browse files
committed
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]> Reviewed-on: https://review.openocd.org/c/openocd/+/9077 Reviewed-by: Tomas Vanek <[email protected]> Tested-by: jenkins
1 parent 2abf8da commit 04da6e2

File tree

7 files changed

+454
-0
lines changed

7 files changed

+454
-0
lines changed

doc/openocd.texi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11120,6 +11120,10 @@ Enable or disable trace output for all ITM stimulus ports.
1112011120
@subsection Cortex-M specific commands
1112111121
@cindex Cortex-M
1112211122

11123+
@deffn {Command} {cortex_m cache_info}
11124+
Report information about the type and size of the cache, if present.
11125+
@end deffn
11126+
1112311127
@deffn {Command} {cortex_m maskisr} (@option{auto}|@option{on}|@option{off}|@option{steponly})
1112411128
Control masking (disabling) interrupts during target step/resume.
1112511129

src/target/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ ARMV6_SRC = \
7575

7676
ARMV7_SRC = \
7777
%D%/armv7m.c \
78+
%D%/armv7m_cache.c \
7879
%D%/armv7m_trace.c \
7980
%D%/cortex_m.c \
8081
%D%/armv7a.c \
@@ -183,6 +184,7 @@ ARC_SRC = \
183184
%D%/armv4_5_cache.h \
184185
%D%/armv7a.h \
185186
%D%/armv7m.h \
187+
%D%/armv7m_cache.h \
186188
%D%/armv7m_trace.h \
187189
%D%/armv8.h \
188190
%D%/armv8_dpm.h \

src/target/armv7m.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define OPENOCD_TARGET_ARMV7M_H
1616

1717
#include "arm.h"
18+
#include "armv7m_cache.h"
1819
#include "armv7m_trace.h"
1920

2021
struct adiv5_ap;
@@ -239,6 +240,8 @@ struct armv7m_common {
239240
/* hla_target uses a high level adapter that does not support all functions */
240241
bool is_hla_target;
241242

243+
struct armv7m_cache_common armv7m_cache;
244+
242245
struct armv7m_trace_config trace_config;
243246

244247
/* Direct processor core register read and writes */

src/target/armv7m_cache.c

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
/*
4+
* Copyright (C) 2025 by STMicroelectronics
5+
* Copyright (C) 2025 by Antonio Borneo <[email protected]>
6+
*/
7+
8+
#ifdef HAVE_CONFIG_H
9+
#include "config.h"
10+
#endif
11+
12+
#include <stdint.h>
13+
14+
#include <helper/align.h>
15+
#include <helper/bitfield.h>
16+
#include <helper/bits.h>
17+
#include <helper/command.h>
18+
#include <helper/log.h>
19+
#include <helper/types.h>
20+
#include <target/arm_adi_v5.h>
21+
#include <target/armv7m_cache.h>
22+
#include <target/cortex_m.h>
23+
24+
static int get_cache_info(struct adiv5_ap *ap, unsigned int cl,
25+
unsigned int ind, uint32_t *ccsidr)
26+
{
27+
uint32_t csselr = FIELD_PREP(CSSELR_LEVEL_MASK, cl)
28+
| FIELD_PREP(CSSELR_IND_MASK, ind);
29+
30+
int retval = mem_ap_write_u32(ap, CSSELR, csselr);
31+
if (retval != ERROR_OK)
32+
return retval;
33+
34+
return mem_ap_read_u32(ap, CCSIDR, ccsidr);
35+
}
36+
37+
static int get_d_u_cache_info(struct adiv5_ap *ap, unsigned int cl,
38+
uint32_t *ccsidr)
39+
{
40+
return get_cache_info(ap, cl, CSSELR_IND_DATA_OR_UNIFIED_CACHE, ccsidr);
41+
}
42+
43+
static int get_i_cache_info(struct adiv5_ap *ap, unsigned int cl,
44+
uint32_t *ccsidr)
45+
{
46+
return get_cache_info(ap, cl, CSSELR_IND_INSTRUCTION_CACHE, ccsidr);
47+
}
48+
49+
static struct armv7m_cache_size decode_ccsidr(uint32_t ccsidr)
50+
{
51+
struct armv7m_cache_size size;
52+
53+
size.line_len = 16 << FIELD_GET(CCSIDR_LINESIZE_MASK, ccsidr);
54+
size.associativity = FIELD_GET(CCSIDR_ASSOCIATIVITY_MASK, ccsidr) + 1;
55+
size.num_sets = FIELD_GET(CCSIDR_NUMSETS_MASK, ccsidr) + 1;
56+
size.cache_size = size.line_len * size.associativity * size.num_sets / 1024;
57+
58+
// compute info for set way operation on cache
59+
size.index_shift = FIELD_GET(CCSIDR_LINESIZE_MASK, ccsidr) + 2;
60+
size.index = FIELD_GET(CCSIDR_NUMSETS_MASK, ccsidr);
61+
size.way = FIELD_GET(CCSIDR_ASSOCIATIVITY_MASK, ccsidr);
62+
63+
unsigned int i = 0;
64+
while (((size.way << i) & 0x80000000) == 0)
65+
i++;
66+
size.way_shift = i;
67+
68+
return size;
69+
}
70+
71+
int armv7m_identify_cache(struct target *target)
72+
{
73+
struct armv7m_common *armv7m = target_to_armv7m(target);
74+
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
75+
76+
uint32_t clidr;
77+
int retval = mem_ap_read_u32(armv7m->debug_ap, CLIDR, &clidr);
78+
if (retval != ERROR_OK)
79+
return retval;
80+
81+
uint32_t ctr;
82+
retval = mem_ap_read_u32(armv7m->debug_ap, CTR, &ctr);
83+
if (retval != ERROR_OK)
84+
return retval;
85+
86+
// retrieve selected cache for later restore
87+
uint32_t csselr;
88+
retval = mem_ap_read_atomic_u32(armv7m->debug_ap, CSSELR, &csselr);
89+
if (retval != ERROR_OK)
90+
return retval;
91+
92+
if (clidr == 0) {
93+
LOG_TARGET_DEBUG(target, "No cache detected");
94+
return ERROR_OK;
95+
}
96+
97+
if (FIELD_GET(CTR_FORMAT_MASK, ctr) != CTR_FORMAT_PROVIDED) {
98+
LOG_ERROR("Wrong value in CTR register");
99+
return ERROR_FAIL;
100+
}
101+
102+
cache->i_min_line_len = 4UL << FIELD_GET(CTR_IMINLINE_MASK, ctr);
103+
cache->d_min_line_len = 4UL << FIELD_GET(CTR_DMINLINE_MASK, ctr);
104+
LOG_TARGET_DEBUG(target,
105+
"ctr=0x%" PRIx32 " ctr.i_min_line_len=%" PRIu32 " ctr.d_min_line_len=%" PRIu32,
106+
ctr, cache->i_min_line_len, cache->d_min_line_len);
107+
108+
cache->loc = FIELD_GET(CLIDR_LOC_MASK, clidr);
109+
LOG_TARGET_DEBUG(target,
110+
"clidr=0x%" PRIx32 " Number of cache levels to PoC=%" PRIu32,
111+
clidr, cache->loc);
112+
113+
// retrieve all available inner caches
114+
uint32_t d_u_ccsidr[8], i_ccsidr[8];
115+
for (unsigned int cl = 0; cl < cache->loc; cl++) {
116+
unsigned int ctype = FIELD_GET(CLIDR_CTYPE_MASK(cl + 1), clidr);
117+
118+
// skip reserved values
119+
if (ctype > CLIDR_CTYPE_UNIFIED_CACHE)
120+
continue;
121+
122+
cache->arch[cl].ctype = ctype;
123+
124+
// separate d or unified d/i cache at this level ?
125+
if (ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE)) {
126+
// retrieve d-cache info
127+
retval = get_d_u_cache_info(armv7m->debug_ap, cl, &d_u_ccsidr[cl]);
128+
if (retval != ERROR_OK)
129+
break;
130+
}
131+
132+
if (ctype & CLIDR_CTYPE_I_CACHE) {
133+
// retrieve i-cache info
134+
retval = get_i_cache_info(armv7m->debug_ap, cl, &i_ccsidr[cl]);
135+
if (retval != ERROR_OK)
136+
break;
137+
}
138+
}
139+
140+
// restore selected cache
141+
int retval1 = mem_ap_write_atomic_u32(armv7m->debug_ap, CSSELR, csselr);
142+
143+
if (retval != ERROR_OK)
144+
return retval;
145+
if (retval1 != ERROR_OK)
146+
return retval1;
147+
148+
for (unsigned int cl = 0; cl < cache->loc; cl++) {
149+
unsigned int ctype = cache->arch[cl].ctype;
150+
151+
// separate d or unified d/i cache at this level ?
152+
if (ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE)) {
153+
cache->has_d_u_cache = true;
154+
cache->arch[cl].d_u_size = decode_ccsidr(d_u_ccsidr[cl]);
155+
156+
LOG_TARGET_DEBUG(target,
157+
"data/unified cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32,
158+
cache->arch[cl].d_u_size.index,
159+
cache->arch[cl].d_u_size.index_shift,
160+
cache->arch[cl].d_u_size.way,
161+
cache->arch[cl].d_u_size.way_shift);
162+
163+
LOG_TARGET_DEBUG(target,
164+
"cache line %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways",
165+
cache->arch[cl].d_u_size.line_len,
166+
cache->arch[cl].d_u_size.cache_size,
167+
cache->arch[cl].d_u_size.associativity);
168+
}
169+
170+
if (ctype & CLIDR_CTYPE_I_CACHE) {
171+
cache->has_i_cache = true;
172+
cache->arch[cl].i_size = decode_ccsidr(i_ccsidr[cl]);
173+
174+
LOG_TARGET_DEBUG(target,
175+
"instruction cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32,
176+
cache->arch[cl].i_size.index,
177+
cache->arch[cl].i_size.index_shift,
178+
cache->arch[cl].i_size.way,
179+
cache->arch[cl].i_size.way_shift);
180+
181+
LOG_TARGET_DEBUG(target,
182+
"cache line %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways",
183+
cache->arch[cl].i_size.line_len,
184+
cache->arch[cl].i_size.cache_size,
185+
cache->arch[cl].i_size.associativity);
186+
}
187+
}
188+
189+
cache->info_valid = true;
190+
191+
return ERROR_OK;
192+
}
193+
194+
int armv7m_d_cache_flush(struct target *target, uint32_t address,
195+
unsigned int length)
196+
{
197+
struct armv7m_common *armv7m = target_to_armv7m(target);
198+
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
199+
200+
if (!cache->info_valid || !cache->has_d_u_cache)
201+
return ERROR_OK;
202+
203+
uint32_t line_len = cache->d_min_line_len;
204+
uint32_t addr_line = ALIGN_DOWN(address, line_len);
205+
uint32_t addr_end = address + length;
206+
207+
while (addr_line < addr_end) {
208+
int retval = mem_ap_write_u32(armv7m->debug_ap, DCCIMVAC, addr_line);
209+
if (retval != ERROR_OK)
210+
return retval;
211+
addr_line += line_len;
212+
keep_alive();
213+
}
214+
215+
return dap_run(armv7m->debug_ap->dap);
216+
}
217+
218+
int armv7m_i_cache_inval(struct target *target, uint32_t address,
219+
unsigned int length)
220+
{
221+
struct armv7m_common *armv7m = target_to_armv7m(target);
222+
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
223+
224+
if (!cache->info_valid || !cache->has_i_cache)
225+
return ERROR_OK;
226+
227+
uint32_t line_len = cache->i_min_line_len;
228+
uint32_t addr_line = ALIGN_DOWN(address, line_len);
229+
uint32_t addr_end = address + length;
230+
231+
while (addr_line < addr_end) {
232+
int retval = mem_ap_write_u32(armv7m->debug_ap, ICIMVAU, addr_line);
233+
if (retval != ERROR_OK)
234+
return retval;
235+
addr_line += line_len;
236+
keep_alive();
237+
}
238+
239+
return dap_run(armv7m->debug_ap->dap);
240+
}
241+
242+
int armv7m_handle_cache_info_command(struct command_invocation *cmd,
243+
struct target *target)
244+
{
245+
struct armv7m_common *armv7m = target_to_armv7m(target);
246+
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
247+
248+
if (!target_was_examined(target)) {
249+
command_print(cmd, "Target not examined yet");
250+
return ERROR_FAIL;
251+
}
252+
253+
if (!cache->info_valid) {
254+
command_print(cmd, "No cache detected");
255+
return ERROR_OK;
256+
}
257+
258+
for (unsigned int cl = 0; cl < cache->loc; cl++) {
259+
struct armv7m_arch_cache *arch = &cache->arch[cl];
260+
261+
if (arch->ctype & CLIDR_CTYPE_I_CACHE)
262+
command_print(cmd,
263+
"L%d I-Cache: line length %" PRIu32 ", associativity %" PRIu32
264+
", num sets %" PRIu32 ", cache size %" PRIu32 " KBytes",
265+
cl + 1,
266+
arch->i_size.line_len,
267+
arch->i_size.associativity,
268+
arch->i_size.num_sets,
269+
arch->i_size.cache_size);
270+
271+
if (arch->ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE))
272+
command_print(cmd,
273+
"L%d %c-Cache: line length %" PRIu32 ", associativity %" PRIu32
274+
", num sets %" PRIu32 ", cache size %" PRIu32 " KBytes",
275+
cl + 1,
276+
(arch->ctype & CLIDR_CTYPE_D_CACHE) ? 'D' : 'U',
277+
arch->d_u_size.line_len,
278+
arch->d_u_size.associativity,
279+
arch->d_u_size.num_sets,
280+
arch->d_u_size.cache_size);
281+
}
282+
283+
return ERROR_OK;
284+
}

src/target/armv7m_cache.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
3+
/*
4+
* Copyright (C) 2025 by STMicroelectronics
5+
* Copyright (C) 2025 by Antonio Borneo <[email protected]>
6+
*/
7+
8+
#ifndef OPENOCD_TARGET_ARMV7M_CACHE_H
9+
#define OPENOCD_TARGET_ARMV7M_CACHE_H
10+
11+
#include <stdbool.h>
12+
#include <stdint.h>
13+
14+
#include <helper/types.h>
15+
16+
struct target;
17+
18+
struct armv7m_cache_size {
19+
// cache dimensioning
20+
uint32_t line_len;
21+
uint32_t associativity;
22+
uint32_t num_sets;
23+
uint32_t cache_size;
24+
// info for set way operation on cache
25+
uint32_t index;
26+
uint32_t index_shift;
27+
uint32_t way;
28+
uint32_t way_shift;
29+
};
30+
31+
// information about one architecture cache at any level
32+
struct armv7m_arch_cache {
33+
unsigned int ctype; // cache type, CLIDR encoding
34+
struct armv7m_cache_size d_u_size; // data cache
35+
struct armv7m_cache_size i_size; // instruction cache
36+
};
37+
38+
// common cache information
39+
struct armv7m_cache_common {
40+
bool info_valid;
41+
bool has_i_cache;
42+
bool has_d_u_cache;
43+
unsigned int loc; // level of coherency
44+
uint32_t d_min_line_len; // minimum d-cache line_len
45+
uint32_t i_min_line_len; // minimum i-cache line_len
46+
struct armv7m_arch_cache arch[6]; // cache info, L1 - L7
47+
};
48+
49+
int armv7m_identify_cache(struct target *target);
50+
int armv7m_d_cache_flush(struct target *target, uint32_t address,
51+
unsigned int length);
52+
int armv7m_i_cache_inval(struct target *target, uint32_t address,
53+
unsigned int length);
54+
int armv7m_handle_cache_info_command(struct command_invocation *cmd,
55+
struct target *target);
56+
57+
#endif /* OPENOCD_TARGET_ARMV7M_CACHE_H */

0 commit comments

Comments
 (0)