OpenOCD
armv7m_cache.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /*
4  * Copyright (C) 2025 by STMicroelectronics
5  * Copyright (C) 2025 by Antonio Borneo <borneo.antonio@gmail.com>
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)
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);
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 
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 
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);
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);
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 ?
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 ?
153  cache->has_d_u_cache = true;
154  cache->arch[cl].d_u_size = decode_ccsidr(d_u_ccsidr[cl]);
155 
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 
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 
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 
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 
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 
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 
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)
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 
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 }
#define ALIGN_DOWN(x, a)
Definition: align.h:21
int mem_ap_read_u32(struct adiv5_ap *ap, target_addr_t address, uint32_t *value)
Asynchronous (queued) read of a word from memory or a system register.
Definition: arm_adi_v5.c:245
int mem_ap_write_u32(struct adiv5_ap *ap, target_addr_t address, uint32_t value)
Asynchronous (queued) write of a word to memory or a system register.
Definition: arm_adi_v5.c:297
int mem_ap_read_atomic_u32(struct adiv5_ap *ap, target_addr_t address, uint32_t *value)
Synchronous read of a word from memory or a system register.
Definition: arm_adi_v5.c:274
int mem_ap_write_atomic_u32(struct adiv5_ap *ap, target_addr_t address, uint32_t value)
Synchronous write of a word to memory or a system register.
Definition: arm_adi_v5.c:326
This defines formats and data structures used to talk to ADIv5 entities.
static int dap_run(struct adiv5_dap *dap)
Perform all queued DAP operations, and clear any errors posted in the CTRL_STAT register when they ar...
Definition: arm_adi_v5.h:648
static struct armv7m_common * target_to_armv7m(struct target *target)
Definition: armv7m.h:269
int armv7m_handle_cache_info_command(struct command_invocation *cmd, struct target *target)
Definition: armv7m_cache.c:242
int armv7m_i_cache_inval(struct target *target, uint32_t address, unsigned int length)
Definition: armv7m_cache.c:218
int armv7m_d_cache_flush(struct target *target, uint32_t address, unsigned int length)
Definition: armv7m_cache.c:194
static int get_cache_info(struct adiv5_ap *ap, unsigned int cl, unsigned int ind, uint32_t *ccsidr)
Definition: armv7m_cache.c:24
static int get_i_cache_info(struct adiv5_ap *ap, unsigned int cl, uint32_t *ccsidr)
Definition: armv7m_cache.c:43
int armv7m_identify_cache(struct target *target)
Definition: armv7m_cache.c:71
static struct armv7m_cache_size decode_ccsidr(uint32_t ccsidr)
Definition: armv7m_cache.c:49
static int get_d_u_cache_info(struct adiv5_ap *ap, unsigned int cl, uint32_t *ccsidr)
Definition: armv7m_cache.c:37
#define FIELD_GET(_mask, _value)
FIELD_GET(_mask, _value) - Extract a value from a bitfield @_mask: Bitfield mask @_value: Bitfield va...
Definition: bitfield.h:57
#define FIELD_PREP(_mask, _value)
FIELD_PREP(_mask, _value) - Prepare a value for insertion into a bitfield @_mask: Bitfield mask @_val...
Definition: bitfield.h:44
void command_print(struct command_invocation *cmd, const char *format,...)
Definition: command.c:371
#define CLIDR_CTYPE_UNIFIED_CACHE
Definition: cortex_m.h:138
#define CLIDR_CTYPE_D_CACHE
Definition: cortex_m.h:137
#define CLIDR_LOC_MASK
Definition: cortex_m.h:132
#define CTR_IMINLINE_MASK
Definition: cortex_m.h:144
#define CSSELR_IND_INSTRUCTION_CACHE
Definition: cortex_m.h:155
#define CSSELR_LEVEL_MASK
Definition: cortex_m.h:152
#define CLIDR_CTYPE_I_CACHE
Definition: cortex_m.h:136
#define CCSIDR
Definition: cortex_m.h:122
#define CCSIDR_NUMSETS_MASK
Definition: cortex_m.h:148
#define CSSELR_IND_DATA_OR_UNIFIED_CACHE
Definition: cortex_m.h:154
#define CTR_DMINLINE_MASK
Definition: cortex_m.h:143
#define CLIDR_CTYPE_MASK(i)
Definition: cortex_m.h:134
#define CTR
Definition: cortex_m.h:121
#define CSSELR_IND_MASK
Definition: cortex_m.h:153
#define CLIDR
Definition: cortex_m.h:120
#define CTR_FORMAT_PROVIDED
Definition: cortex_m.h:146
#define DCCIMVAC
Definition: cortex_m.h:125
#define CSSELR
Definition: cortex_m.h:123
#define CCSIDR_LINESIZE_MASK
Definition: cortex_m.h:150
#define ICIMVAU
Definition: cortex_m.h:124
#define CTR_FORMAT_MASK
Definition: cortex_m.h:140
#define CCSIDR_ASSOCIATIVITY_MASK
Definition: cortex_m.h:149
uint32_t size
Size of dw_spi_transaction::buffer.
Definition: dw-spi-helper.h:4
uint32_t address
Starting address. Sector aligned.
Definition: dw-spi-helper.h:0
uint8_t length
Definition: esp_usb_jtag.c:1
void keep_alive(void)
Definition: log.c:427
#define ERROR_FAIL
Definition: log.h:174
#define LOG_TARGET_DEBUG(target, fmt_str,...)
Definition: log.h:150
#define LOG_ERROR(expr ...)
Definition: log.h:133
#define ERROR_OK
Definition: log.h:168
This represents an ARM Debug Interface (v5) Access Port (AP).
Definition: arm_adi_v5.h:250
struct adiv5_dap * dap
DAP this AP belongs to.
Definition: arm_adi_v5.h:254
struct armv7m_cache_size i_size
Definition: armv7m_cache.h:35
unsigned int ctype
Definition: armv7m_cache.h:33
struct armv7m_cache_size d_u_size
Definition: armv7m_cache.h:34
struct armv7m_arch_cache arch[6]
Definition: armv7m_cache.h:46
unsigned int loc
Definition: armv7m_cache.h:43
uint32_t d_min_line_len
Definition: armv7m_cache.h:44
uint32_t i_min_line_len
Definition: armv7m_cache.h:45
uint32_t associativity
Definition: armv7m_cache.h:21
uint32_t cache_size
Definition: armv7m_cache.h:23
uint32_t index_shift
Definition: armv7m_cache.h:26
uint32_t way_shift
Definition: armv7m_cache.h:28
struct adiv5_ap * debug_ap
Definition: armv7m.h:235
struct armv7m_cache_common armv7m_cache
Definition: armv7m.h:243
When run_command is called, a new instance will be created on the stack, filled with the proper value...
Definition: command.h:76
Definition: target.h:119
static bool target_was_examined(const struct target *target)
Definition: target.h:432
uint8_t cmd
Definition: vdebug.c:1