target/xtensa: enable DAP/SWD for generic xtensa
[openocd.git] / src / target / xtensa / xtensa_debug_module.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4 * Xtensa Debug Module (XDM) Support for OpenOCD *
5 * Copyright (C) 2020-2022 Cadence Design Systems, Inc. *
6 * Copyright (C) 2019 Espressif Systems Ltd. *
7 ***************************************************************************/
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <helper/align.h>
14 #include "xtensa_debug_module.h"
15
16 #define TAPINS_PWRCTL 0x08
17 #define TAPINS_PWRSTAT 0x09
18 #define TAPINS_NARSEL 0x1C
19 #define TAPINS_IDCODE 0x1E
20 #define TAPINS_BYPASS 0x1F
21
22 #define TAPINS_PWRCTL_LEN 8
23 #define TAPINS_PWRSTAT_LEN 8
24 #define TAPINS_NARSEL_ADRLEN 8
25 #define TAPINS_NARSEL_DATALEN 32
26 #define TAPINS_IDCODE_LEN 32
27 #define TAPINS_BYPASS_LEN 1
28
29 /* Table of power register offsets for APB space */
30 static const struct xtensa_dm_pwr_reg_offsets xdm_pwr_regs[XDMREG_PWRNUM] =
31 XTENSA_DM_PWR_REG_OFFSETS;
32
33 /* Table of debug register offsets for Nexus and APB space */
34 static const struct xtensa_dm_reg_offsets xdm_regs[XDMREG_NUM] =
35 XTENSA_DM_REG_OFFSETS;
36
37 static void xtensa_dm_add_set_ir(struct xtensa_debug_module *dm, uint8_t value)
38 {
39 struct scan_field field;
40 uint8_t t[4] = { 0, 0, 0, 0 };
41
42 memset(&field, 0, sizeof(field));
43 field.num_bits = dm->tap->ir_length;
44 field.out_value = t;
45 buf_set_u32(t, 0, field.num_bits, value);
46 jtag_add_ir_scan(dm->tap, &field, TAP_IDLE);
47 }
48
49 static void xtensa_dm_add_dr_scan(struct xtensa_debug_module *dm,
50 int len,
51 const uint8_t *src,
52 uint8_t *dest,
53 tap_state_t endstate)
54 {
55 struct scan_field field;
56
57 memset(&field, 0, sizeof(field));
58 field.num_bits = len;
59 field.out_value = src;
60 field.in_value = dest;
61 jtag_add_dr_scan(dm->tap, 1, &field, endstate);
62 }
63
64 int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_module_config *cfg)
65 {
66 if (!dm || !cfg)
67 return ERROR_FAIL;
68 if (!IS_ALIGNED(cfg->ap_offset, XTENSA_DM_APB_ALIGN)) {
69 LOG_ERROR("Xtensa DM APB offset must be aligned to a %dKB multiple",
70 XTENSA_DM_APB_ALIGN / 1024);
71 return ERROR_FAIL;
72 }
73
74 dm->pwr_ops = cfg->pwr_ops;
75 dm->dbg_ops = cfg->dbg_ops;
76 dm->tap = cfg->tap;
77 dm->queue_tdi_idle = cfg->queue_tdi_idle;
78 dm->queue_tdi_idle_arg = cfg->queue_tdi_idle_arg;
79 dm->dap = cfg->dap;
80 dm->debug_ap = cfg->debug_ap;
81 dm->debug_apsel = cfg->debug_apsel;
82 dm->ap_offset = cfg->ap_offset;
83 return ERROR_OK;
84 }
85
86 void xtensa_dm_deinit(struct xtensa_debug_module *dm)
87 {
88 if (dm->debug_ap) {
89 dap_put_ap(dm->debug_ap);
90 dm->debug_ap = NULL;
91 }
92 }
93
94 int xtensa_dm_poll(struct xtensa_debug_module *dm)
95 {
96 /* Check if debug_ap is available to prevent segmentation fault.
97 * If the re-examination after an error does not find a MEM-AP
98 * (e.g. the target stopped communicating), debug_ap pointer
99 * can suddenly become NULL.
100 */
101 return (!dm || (dm->dap && !dm->debug_ap)) ? ERROR_FAIL : ERROR_OK;
102 }
103
104 int xtensa_dm_examine(struct xtensa_debug_module *dm)
105 {
106 struct adiv5_dap *swjdp = dm->dap;
107 int retval = ERROR_OK;
108
109 if (swjdp) {
110 LOG_DEBUG("DM examine: DAP AP select %d", dm->debug_apsel);
111 if (dm->debug_ap) {
112 dap_put_ap(dm->debug_ap);
113 dm->debug_ap = NULL;
114 }
115 if (dm->debug_apsel == DP_APSEL_INVALID) {
116 LOG_DEBUG("DM examine: search for APB-type MEM-AP...");
117 /* TODO: Determine whether AP_TYPE_AXI_AP APs can be supported... */
118 retval = dap_find_get_ap(swjdp, AP_TYPE_APB_AP, &dm->debug_ap);
119 if (retval != ERROR_OK) {
120 LOG_ERROR("Could not find MEM-AP to control the core");
121 return retval;
122 }
123 } else {
124 dm->debug_ap = dap_get_ap(swjdp, dm->debug_apsel);
125 }
126
127 /* TODO: Allow a user-specified AP instead of relying on AP_TYPE_APB_AP */
128 dm->debug_apsel = dm->debug_ap->ap_num;
129 LOG_DEBUG("DM examine: Setting apsel to %d", dm->debug_apsel);
130
131 /* Leave (only) generic DAP stuff for debugport_init(); */
132 dm->debug_ap->memaccess_tck = 8;
133
134 retval = mem_ap_init(dm->debug_ap);
135 if (retval != ERROR_OK) {
136 LOG_ERROR("MEM-AP init failed: %d", retval);
137 return retval;
138 }
139
140 /* TODO: how to set autoincrement range? Hard-code it to 1024 bytes for now */
141 dm->debug_ap->tar_autoincr_block = (1 << 10);
142 }
143
144 return retval;
145 }
146
147 int xtensa_dm_queue_enable(struct xtensa_debug_module *dm)
148 {
149 return dm->dbg_ops->queue_reg_write(dm, XDMREG_DCRSET, OCDDCR_ENABLEOCD);
150 }
151
152 int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, enum xtensa_dm_reg reg, uint8_t *value)
153 {
154 if (reg >= XDMREG_NUM) {
155 LOG_ERROR("Invalid DBG reg ID %d!", reg);
156 return ERROR_FAIL;
157 }
158 if (dm->dap)
159 /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with
160 * queued reads, but requires an API change to pass value as a 32-bit pointer.
161 */
162 return mem_ap_read_buf(dm->debug_ap, value, 4, 1, xdm_regs[reg].apb + dm->ap_offset);
163 uint8_t regdata = (xdm_regs[reg].nar << 1) | 0;
164 uint8_t dummy[4] = { 0, 0, 0, 0 };
165 xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
166 xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, &regdata, NULL, TAP_IDLE);
167 xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, dummy, value, TAP_IDLE);
168 return ERROR_OK;
169 }
170
171 int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, enum xtensa_dm_reg reg, uint32_t value)
172 {
173 if (reg >= XDMREG_NUM) {
174 LOG_ERROR("Invalid DBG reg ID %d!", reg);
175 return ERROR_FAIL;
176 }
177 if (dm->dap)
178 return mem_ap_write_u32(dm->debug_ap, xdm_regs[reg].apb + dm->ap_offset, value);
179 uint8_t regdata = (xdm_regs[reg].nar << 1) | 1;
180 uint8_t valdata[] = { value, value >> 8, value >> 16, value >> 24 };
181 xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
182 xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, &regdata, NULL, TAP_IDLE);
183 xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, valdata, NULL, TAP_IDLE);
184 return ERROR_OK;
185 }
186
187 int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm,
188 enum xtensa_dm_pwr_reg reg,
189 uint8_t *data,
190 uint32_t clear)
191 {
192 if (reg >= XDMREG_PWRNUM) {
193 LOG_ERROR("Invalid PWR reg ID %d!", reg);
194 return ERROR_FAIL;
195 }
196 if (dm->dap) {
197 /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with
198 * queued reads, but requires an API change to pass value as a 32-bit pointer.
199 */
200 uint32_t apbreg = xdm_pwr_regs[reg].apb + dm->ap_offset;
201 int retval = mem_ap_read_buf(dm->debug_ap, data, 4, 1, apbreg);
202 if (retval == ERROR_OK)
203 retval = mem_ap_write_u32(dm->debug_ap, apbreg, clear);
204 return retval;
205 }
206 uint8_t value_clr = (uint8_t)clear;
207 uint8_t tap_insn = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL : TAPINS_PWRSTAT;
208 int tap_insn_sz = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL_LEN : TAPINS_PWRSTAT_LEN;
209 xtensa_dm_add_set_ir(dm, tap_insn);
210 xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value_clr, data, TAP_IDLE);
211 return ERROR_OK;
212 }
213
214 int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm,
215 enum xtensa_dm_pwr_reg reg,
216 uint32_t data)
217 {
218 if (reg >= XDMREG_PWRNUM) {
219 LOG_ERROR("Invalid PWR reg ID %d!", reg);
220 return ERROR_FAIL;
221 }
222 if (dm->dap) {
223 uint32_t apbreg = xdm_pwr_regs[reg].apb + dm->ap_offset;
224 return mem_ap_write_u32(dm->debug_ap, apbreg, data);
225 }
226 uint8_t tap_insn = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL : TAPINS_PWRSTAT;
227 int tap_insn_sz = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL_LEN : TAPINS_PWRSTAT_LEN;
228 uint8_t value = (uint8_t)data;
229 xtensa_dm_add_set_ir(dm, tap_insn);
230 xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value, NULL, TAP_IDLE);
231 return ERROR_OK;
232 }
233
234 int xtensa_dm_device_id_read(struct xtensa_debug_module *dm)
235 {
236 uint8_t id_buf[sizeof(uint32_t)];
237
238 dm->dbg_ops->queue_reg_read(dm, XDMREG_OCDID, id_buf);
239 xtensa_dm_queue_tdi_idle(dm);
240 int res = xtensa_dm_queue_execute(dm);
241 if (res != ERROR_OK)
242 return res;
243 dm->device_id = buf_get_u32(id_buf, 0, 32);
244 return ERROR_OK;
245 }
246
247 int xtensa_dm_power_status_read(struct xtensa_debug_module *dm, uint32_t clear)
248 {
249 uint8_t stat_buf[sizeof(uint32_t)];
250 uint8_t stath_buf[sizeof(uint32_t)];
251
252 /* TODO: JTAG does not work when PWRCTL_JTAGDEBUGUSE is not set.
253 * It is set in xtensa_examine(), need to move reading of XDMREG_OCDID out of this function */
254 /* dm->dbg_ops->queue_reg_read(dm, XDMREG_OCDID, id_buf);
255 *Read reset state */
256 dm->pwr_ops->queue_reg_read(dm, XDMREG_PWRSTAT, stat_buf, clear);
257 dm->pwr_ops->queue_reg_read(dm, XDMREG_PWRSTAT, stath_buf, clear);
258 xtensa_dm_queue_tdi_idle(dm);
259 int res = xtensa_dm_queue_execute(dm);
260 if (res != ERROR_OK)
261 return res;
262 dm->power_status.stat = buf_get_u32(stat_buf, 0, 32);
263 dm->power_status.stath = buf_get_u32(stath_buf, 0, 32);
264 return res;
265 }
266
267 int xtensa_dm_core_status_read(struct xtensa_debug_module *dm)
268 {
269 uint8_t dsr_buf[sizeof(uint32_t)];
270
271 xtensa_dm_queue_enable(dm);
272 dm->dbg_ops->queue_reg_read(dm, XDMREG_DSR, dsr_buf);
273 xtensa_dm_queue_tdi_idle(dm);
274 int res = xtensa_dm_queue_execute(dm);
275 if (res != ERROR_OK)
276 return res;
277 dm->core_status.dsr = buf_get_u32(dsr_buf, 0, 32);
278 return res;
279 }
280
281 int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bits)
282 {
283 dm->dbg_ops->queue_reg_write(dm, XDMREG_DSR, bits);
284 xtensa_dm_queue_tdi_idle(dm);
285 return xtensa_dm_queue_execute(dm);
286 }
287
288 int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg)
289 {
290 /*Turn off trace unit so we can start a new trace. */
291 dm->dbg_ops->queue_reg_write(dm, XDMREG_TRAXCTRL, 0);
292 xtensa_dm_queue_tdi_idle(dm);
293 int res = xtensa_dm_queue_execute(dm);
294 if (res != ERROR_OK)
295 return res;
296
297 /*Set up parameters */
298 dm->dbg_ops->queue_reg_write(dm, XDMREG_TRAXADDR, 0);
299 if (cfg->stopmask != XTENSA_STOPMASK_DISABLED) {
300 dm->dbg_ops->queue_reg_write(dm, XDMREG_PCMATCHCTRL,
301 (cfg->stopmask << PCMATCHCTRL_PCML_SHIFT));
302 dm->dbg_ops->queue_reg_write(dm, XDMREG_TRIGGERPC, cfg->stoppc);
303 }
304 dm->dbg_ops->queue_reg_write(dm, XDMREG_DELAYCNT, cfg->after);
305 /*Options are mostly hardcoded for now. ToDo: make this more configurable. */
306 dm->dbg_ops->queue_reg_write(
307 dm,
308 XDMREG_TRAXCTRL,
309 TRAXCTRL_TREN |
310 ((cfg->stopmask != XTENSA_STOPMASK_DISABLED) ? TRAXCTRL_PCMEN : 0) | TRAXCTRL_TMEN |
311 (cfg->after_is_words ? 0 : TRAXCTRL_CNTU) | (0 << TRAXCTRL_SMPER_SHIFT) | TRAXCTRL_PTOWS);
312 xtensa_dm_queue_tdi_idle(dm);
313 return xtensa_dm_queue_execute(dm);
314 }
315
316 int xtensa_dm_trace_stop(struct xtensa_debug_module *dm, bool pto_enable)
317 {
318 uint8_t traxctl_buf[sizeof(uint32_t)];
319 uint32_t traxctl;
320 struct xtensa_trace_status trace_status;
321
322 dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXCTRL, traxctl_buf);
323 xtensa_dm_queue_tdi_idle(dm);
324 int res = xtensa_dm_queue_execute(dm);
325 if (res != ERROR_OK)
326 return res;
327 traxctl = buf_get_u32(traxctl_buf, 0, 32);
328
329 if (!pto_enable)
330 traxctl &= ~(TRAXCTRL_PTOWS | TRAXCTRL_PTOWT);
331
332 dm->dbg_ops->queue_reg_write(dm, XDMREG_TRAXCTRL, traxctl | TRAXCTRL_TRSTP);
333 xtensa_dm_queue_tdi_idle(dm);
334 res = xtensa_dm_queue_execute(dm);
335 if (res != ERROR_OK)
336 return res;
337
338 /*Check current status of trace hardware */
339 res = xtensa_dm_trace_status_read(dm, &trace_status);
340 if (res != ERROR_OK)
341 return res;
342
343 if (trace_status.stat & TRAXSTAT_TRACT) {
344 LOG_ERROR("Failed to stop tracing (0x%x)!", trace_status.stat);
345 return ERROR_FAIL;
346 }
347 return ERROR_OK;
348 }
349
350 int xtensa_dm_trace_status_read(struct xtensa_debug_module *dm, struct xtensa_trace_status *status)
351 {
352 uint8_t traxstat_buf[sizeof(uint32_t)];
353
354 dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXSTAT, traxstat_buf);
355 xtensa_dm_queue_tdi_idle(dm);
356 int res = xtensa_dm_queue_execute(dm);
357 if (res == ERROR_OK && status)
358 status->stat = buf_get_u32(traxstat_buf, 0, 32);
359 return res;
360 }
361
362 int xtensa_dm_trace_config_read(struct xtensa_debug_module *dm, struct xtensa_trace_config *config)
363 {
364 uint8_t traxctl_buf[sizeof(uint32_t)];
365 uint8_t memadrstart_buf[sizeof(uint32_t)];
366 uint8_t memadrend_buf[sizeof(uint32_t)];
367 uint8_t adr_buf[sizeof(uint32_t)];
368
369 if (!config)
370 return ERROR_FAIL;
371
372 dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXCTRL, traxctl_buf);
373 dm->dbg_ops->queue_reg_read(dm, XDMREG_MEMADDRSTART, memadrstart_buf);
374 dm->dbg_ops->queue_reg_read(dm, XDMREG_MEMADDREND, memadrend_buf);
375 dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXADDR, adr_buf);
376 xtensa_dm_queue_tdi_idle(dm);
377 int res = xtensa_dm_queue_execute(dm);
378 if (res == ERROR_OK) {
379 config->ctrl = buf_get_u32(traxctl_buf, 0, 32);
380 config->memaddr_start = buf_get_u32(memadrstart_buf, 0, 32);
381 config->memaddr_end = buf_get_u32(memadrend_buf, 0, 32);
382 config->addr = buf_get_u32(adr_buf, 0, 32);
383 }
384 return res;
385 }
386
387 int xtensa_dm_trace_data_read(struct xtensa_debug_module *dm, uint8_t *dest, uint32_t size)
388 {
389 if (!dest)
390 return ERROR_FAIL;
391
392 for (unsigned int i = 0; i < size / 4; i++)
393 dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXDATA, &dest[i * 4]);
394 xtensa_dm_queue_tdi_idle(dm);
395 return xtensa_dm_queue_execute(dm);
396 }
397
398 int xtensa_dm_perfmon_enable(struct xtensa_debug_module *dm, int counter_id,
399 const struct xtensa_perfmon_config *config)
400 {
401 if (!config)
402 return ERROR_FAIL;
403
404 uint8_t pmstat_buf[4];
405 uint32_t pmctrl = ((config->tracelevel) << 4) +
406 (config->select << 8) +
407 (config->mask << 16) +
408 (config->kernelcnt << 3);
409
410 /* enable performance monitor */
411 dm->dbg_ops->queue_reg_write(dm, XDMREG_PMG, 0x1);
412 /* reset counter */
413 dm->dbg_ops->queue_reg_write(dm, XDMREG_PM0 + counter_id, 0);
414 dm->dbg_ops->queue_reg_write(dm, XDMREG_PMCTRL0 + counter_id, pmctrl);
415 dm->dbg_ops->queue_reg_read(dm, XDMREG_PMSTAT0 + counter_id, pmstat_buf);
416 xtensa_dm_queue_tdi_idle(dm);
417 return xtensa_dm_queue_execute(dm);
418 }
419
420 int xtensa_dm_perfmon_dump(struct xtensa_debug_module *dm, int counter_id,
421 struct xtensa_perfmon_result *out_result)
422 {
423 uint8_t pmstat_buf[4];
424 uint8_t pmcount_buf[4];
425
426 dm->dbg_ops->queue_reg_read(dm, XDMREG_PMSTAT0 + counter_id, pmstat_buf);
427 dm->dbg_ops->queue_reg_read(dm, XDMREG_PM0 + counter_id, pmcount_buf);
428 xtensa_dm_queue_tdi_idle(dm);
429 int res = xtensa_dm_queue_execute(dm);
430 if (res == ERROR_OK) {
431 uint32_t stat = buf_get_u32(pmstat_buf, 0, 32);
432 uint64_t result = buf_get_u32(pmcount_buf, 0, 32);
433
434 /* TODO: if counter # counter_id+1 has 'select' set to 1, use its value as the
435 * high 32 bits of the counter. */
436 if (out_result) {
437 out_result->overflow = ((stat & 1) != 0);
438 out_result->value = result;
439 }
440 }
441
442 return res;
443 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)