1 // SPDX-License-Identifier: GPL-2.0-or-later
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 ***************************************************************************/
13 #include <helper/align.h>
14 #include "xtensa_debug_module.h"
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
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
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
;
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
;
37 static enum xtensa_dm_reg
xtensa_dm_regaddr_to_id(uint32_t addr
)
39 enum xtensa_dm_reg id
;
40 uint32_t addr_masked
= (addr
& (XTENSA_DM_APB_ALIGN
- 1));
41 for (id
= XDMREG_TRAXID
; id
< XDMREG_NUM
; id
++)
42 if (xdm_regs
[id
].apb
== addr_masked
)
47 static void xtensa_dm_add_set_ir(struct xtensa_debug_module
*dm
, uint8_t value
)
49 struct scan_field field
;
50 uint8_t t
[4] = { 0, 0, 0, 0 };
52 memset(&field
, 0, sizeof(field
));
53 field
.num_bits
= dm
->tap
->ir_length
;
55 buf_set_u32(t
, 0, field
.num_bits
, value
);
56 jtag_add_ir_scan(dm
->tap
, &field
, TAP_IDLE
);
59 static void xtensa_dm_add_dr_scan(struct xtensa_debug_module
*dm
,
65 struct scan_field field
;
67 memset(&field
, 0, sizeof(field
));
69 field
.out_value
= src
;
70 field
.in_value
= dest
;
71 jtag_add_dr_scan(dm
->tap
, 1, &field
, endstate
);
74 int xtensa_dm_init(struct xtensa_debug_module
*dm
, const struct xtensa_debug_module_config
*cfg
)
78 if (!IS_ALIGNED(cfg
->ap_offset
, XTENSA_DM_APB_ALIGN
)) {
79 LOG_ERROR("Xtensa DM APB offset must be aligned to a %dKB multiple",
80 XTENSA_DM_APB_ALIGN
/ 1024);
84 dm
->pwr_ops
= cfg
->pwr_ops
;
85 dm
->dbg_ops
= cfg
->dbg_ops
;
87 dm
->queue_tdi_idle
= cfg
->queue_tdi_idle
;
88 dm
->queue_tdi_idle_arg
= cfg
->queue_tdi_idle_arg
;
90 dm
->debug_ap
= cfg
->debug_ap
;
91 dm
->debug_apsel
= cfg
->debug_apsel
;
92 dm
->ap_offset
= cfg
->ap_offset
;
96 void xtensa_dm_deinit(struct xtensa_debug_module
*dm
)
99 dap_put_ap(dm
->debug_ap
);
104 int xtensa_dm_poll(struct xtensa_debug_module
*dm
)
106 /* Check if debug_ap is available to prevent segmentation fault.
107 * If the re-examination after an error does not find a MEM-AP
108 * (e.g. the target stopped communicating), debug_ap pointer
109 * can suddenly become NULL.
111 return (!dm
|| (dm
->dap
&& !dm
->debug_ap
)) ? ERROR_FAIL
: ERROR_OK
;
114 int xtensa_dm_examine(struct xtensa_debug_module
*dm
)
116 struct adiv5_dap
*swjdp
= dm
->dap
;
117 int retval
= ERROR_OK
;
120 LOG_DEBUG("DM examine: DAP AP select %d", dm
->debug_apsel
);
122 dap_put_ap(dm
->debug_ap
);
125 if (dm
->debug_apsel
== DP_APSEL_INVALID
) {
126 LOG_DEBUG("DM examine: search for APB-type MEM-AP...");
127 /* TODO: Determine whether AP_TYPE_AXI_AP APs can be supported... */
128 retval
= dap_find_get_ap(swjdp
, AP_TYPE_APB_AP
, &dm
->debug_ap
);
129 if (retval
!= ERROR_OK
) {
130 LOG_ERROR("Could not find MEM-AP to control the core");
134 dm
->debug_ap
= dap_get_ap(swjdp
, dm
->debug_apsel
);
137 /* TODO: Allow a user-specified AP instead of relying on AP_TYPE_APB_AP */
138 dm
->debug_apsel
= dm
->debug_ap
->ap_num
;
139 LOG_DEBUG("DM examine: Setting apsel to %d", dm
->debug_apsel
);
141 /* Leave (only) generic DAP stuff for debugport_init(); */
142 dm
->debug_ap
->memaccess_tck
= 8;
144 retval
= mem_ap_init(dm
->debug_ap
);
145 if (retval
!= ERROR_OK
) {
146 LOG_ERROR("MEM-AP init failed: %d", retval
);
150 /* TODO: how to set autoincrement range? Hard-code it to 1024 bytes for now */
151 dm
->debug_ap
->tar_autoincr_block
= (1 << 10);
157 int xtensa_dm_queue_enable(struct xtensa_debug_module
*dm
)
159 return dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_DCRSET
, OCDDCR_ENABLEOCD
);
162 int xtensa_dm_queue_reg_read(struct xtensa_debug_module
*dm
, enum xtensa_dm_reg reg
, uint8_t *value
)
164 if (reg
>= XDMREG_NUM
) {
165 LOG_ERROR("Invalid DBG reg ID %d!", reg
);
169 /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with
170 * queued reads, but requires an API change to pass value as a 32-bit pointer.
172 return mem_ap_read_buf(dm
->debug_ap
, value
, 4, 1, xdm_regs
[reg
].apb
+ dm
->ap_offset
);
173 uint8_t regdata
= (xdm_regs
[reg
].nar
<< 1) | 0;
174 uint8_t dummy
[4] = { 0, 0, 0, 0 };
175 xtensa_dm_add_set_ir(dm
, TAPINS_NARSEL
);
176 xtensa_dm_add_dr_scan(dm
, TAPINS_NARSEL_ADRLEN
, ®data
, NULL
, TAP_IDLE
);
177 xtensa_dm_add_dr_scan(dm
, TAPINS_NARSEL_DATALEN
, dummy
, value
, TAP_IDLE
);
181 int xtensa_dm_queue_reg_write(struct xtensa_debug_module
*dm
, enum xtensa_dm_reg reg
, uint32_t value
)
183 if (reg
>= XDMREG_NUM
) {
184 LOG_ERROR("Invalid DBG reg ID %d!", reg
);
188 return mem_ap_write_u32(dm
->debug_ap
, xdm_regs
[reg
].apb
+ dm
->ap_offset
, value
);
189 uint8_t regdata
= (xdm_regs
[reg
].nar
<< 1) | 1;
190 uint8_t valdata
[] = { value
, value
>> 8, value
>> 16, value
>> 24 };
191 xtensa_dm_add_set_ir(dm
, TAPINS_NARSEL
);
192 xtensa_dm_add_dr_scan(dm
, TAPINS_NARSEL_ADRLEN
, ®data
, NULL
, TAP_IDLE
);
193 xtensa_dm_add_dr_scan(dm
, TAPINS_NARSEL_DATALEN
, valdata
, NULL
, TAP_IDLE
);
197 int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module
*dm
,
198 enum xtensa_dm_pwr_reg reg
,
202 if (reg
>= XDMREG_PWRNUM
) {
203 LOG_ERROR("Invalid PWR reg ID %d!", reg
);
207 /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with
208 * queued reads, but requires an API change to pass value as a 32-bit pointer.
210 uint32_t apbreg
= xdm_pwr_regs
[reg
].apb
+ dm
->ap_offset
;
211 int retval
= mem_ap_read_buf(dm
->debug_ap
, data
, 4, 1, apbreg
);
212 if (retval
== ERROR_OK
)
213 retval
= mem_ap_write_u32(dm
->debug_ap
, apbreg
, clear
);
216 uint8_t value_clr
= (uint8_t)clear
;
217 uint8_t tap_insn
= (reg
== XDMREG_PWRCTL
) ? TAPINS_PWRCTL
: TAPINS_PWRSTAT
;
218 int tap_insn_sz
= (reg
== XDMREG_PWRCTL
) ? TAPINS_PWRCTL_LEN
: TAPINS_PWRSTAT_LEN
;
219 xtensa_dm_add_set_ir(dm
, tap_insn
);
220 xtensa_dm_add_dr_scan(dm
, tap_insn_sz
, &value_clr
, data
, TAP_IDLE
);
224 int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module
*dm
,
225 enum xtensa_dm_pwr_reg reg
,
228 if (reg
>= XDMREG_PWRNUM
) {
229 LOG_ERROR("Invalid PWR reg ID %d!", reg
);
233 uint32_t apbreg
= xdm_pwr_regs
[reg
].apb
+ dm
->ap_offset
;
234 return mem_ap_write_u32(dm
->debug_ap
, apbreg
, data
);
236 uint8_t tap_insn
= (reg
== XDMREG_PWRCTL
) ? TAPINS_PWRCTL
: TAPINS_PWRSTAT
;
237 int tap_insn_sz
= (reg
== XDMREG_PWRCTL
) ? TAPINS_PWRCTL_LEN
: TAPINS_PWRSTAT_LEN
;
238 uint8_t value
= (uint8_t)data
;
239 xtensa_dm_add_set_ir(dm
, tap_insn
);
240 xtensa_dm_add_dr_scan(dm
, tap_insn_sz
, &value
, NULL
, TAP_IDLE
);
244 int xtensa_dm_device_id_read(struct xtensa_debug_module
*dm
)
246 uint8_t id_buf
[sizeof(uint32_t)];
248 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_OCDID
, id_buf
);
249 xtensa_dm_queue_tdi_idle(dm
);
250 int res
= xtensa_dm_queue_execute(dm
);
253 dm
->device_id
= buf_get_u32(id_buf
, 0, 32);
257 int xtensa_dm_power_status_read(struct xtensa_debug_module
*dm
, uint32_t clear
)
259 uint8_t stat_buf
[sizeof(uint32_t)] = { 0, 0, 0, 0 };
260 uint8_t stath_buf
[sizeof(uint32_t)] = { 0, 0, 0, 0 };
262 /* TODO: JTAG does not work when PWRCTL_JTAGDEBUGUSE is not set.
263 * It is set in xtensa_examine(), need to move reading of XDMREG_OCDID out of this function */
264 /* dm->dbg_ops->queue_reg_read(dm, XDMREG_OCDID, id_buf);
266 dm
->pwr_ops
->queue_reg_read(dm
, XDMREG_PWRSTAT
, stat_buf
, clear
);
267 dm
->pwr_ops
->queue_reg_read(dm
, XDMREG_PWRSTAT
, stath_buf
, clear
);
268 xtensa_dm_queue_tdi_idle(dm
);
269 int res
= xtensa_dm_queue_execute(dm
);
272 dm
->power_status
.stat
= buf_get_u32(stat_buf
, 0, 32);
273 dm
->power_status
.stath
= buf_get_u32(stath_buf
, 0, 32);
277 int xtensa_dm_core_status_read(struct xtensa_debug_module
*dm
)
279 uint8_t dsr_buf
[sizeof(uint32_t)];
281 xtensa_dm_queue_enable(dm
);
282 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_DSR
, dsr_buf
);
283 xtensa_dm_queue_tdi_idle(dm
);
284 int res
= xtensa_dm_queue_execute(dm
);
287 dm
->core_status
.dsr
= buf_get_u32(dsr_buf
, 0, 32);
291 int xtensa_dm_core_status_clear(struct xtensa_debug_module
*dm
, xtensa_dsr_t bits
)
293 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_DSR
, bits
);
294 xtensa_dm_queue_tdi_idle(dm
);
295 return xtensa_dm_queue_execute(dm
);
298 int xtensa_dm_read(struct xtensa_debug_module
*dm
, uint32_t addr
, uint32_t *val
)
300 enum xtensa_dm_reg reg
= xtensa_dm_regaddr_to_id(addr
);
301 uint8_t buf
[sizeof(uint32_t)];
302 if (reg
< XDMREG_NUM
) {
303 xtensa_dm_queue_enable(dm
);
304 dm
->dbg_ops
->queue_reg_read(dm
, reg
, buf
);
305 xtensa_dm_queue_tdi_idle(dm
);
306 int res
= xtensa_dm_queue_execute(dm
);
307 if (res
== ERROR_OK
&& val
)
308 *val
= buf_get_u32(buf
, 0, 32);
314 int xtensa_dm_write(struct xtensa_debug_module
*dm
, uint32_t addr
, uint32_t val
)
316 enum xtensa_dm_reg reg
= xtensa_dm_regaddr_to_id(addr
);
317 if (reg
< XDMREG_NUM
) {
318 xtensa_dm_queue_enable(dm
);
319 dm
->dbg_ops
->queue_reg_write(dm
, reg
, val
);
320 xtensa_dm_queue_tdi_idle(dm
);
321 return xtensa_dm_queue_execute(dm
);
326 int xtensa_dm_trace_start(struct xtensa_debug_module
*dm
, struct xtensa_trace_start_config
*cfg
)
328 /*Turn off trace unit so we can start a new trace. */
329 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_TRAXCTRL
, 0);
330 xtensa_dm_queue_tdi_idle(dm
);
331 int res
= xtensa_dm_queue_execute(dm
);
335 /*Set up parameters */
336 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_TRAXADDR
, 0);
337 if (cfg
->stopmask
!= XTENSA_STOPMASK_DISABLED
) {
338 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_PCMATCHCTRL
,
339 (cfg
->stopmask
<< PCMATCHCTRL_PCML_SHIFT
));
340 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_TRIGGERPC
, cfg
->stoppc
);
342 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_DELAYCNT
, cfg
->after
);
343 /*Options are mostly hardcoded for now. ToDo: make this more configurable. */
344 dm
->dbg_ops
->queue_reg_write(
348 ((cfg
->stopmask
!= XTENSA_STOPMASK_DISABLED
) ? TRAXCTRL_PCMEN
: 0) | TRAXCTRL_TMEN
|
349 (cfg
->after_is_words
? 0 : TRAXCTRL_CNTU
) | (0 << TRAXCTRL_SMPER_SHIFT
) | TRAXCTRL_PTOWS
);
350 xtensa_dm_queue_tdi_idle(dm
);
351 return xtensa_dm_queue_execute(dm
);
354 int xtensa_dm_trace_stop(struct xtensa_debug_module
*dm
, bool pto_enable
)
356 uint8_t traxctl_buf
[sizeof(uint32_t)];
358 struct xtensa_trace_status trace_status
;
360 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_TRAXCTRL
, traxctl_buf
);
361 xtensa_dm_queue_tdi_idle(dm
);
362 int res
= xtensa_dm_queue_execute(dm
);
365 traxctl
= buf_get_u32(traxctl_buf
, 0, 32);
368 traxctl
&= ~(TRAXCTRL_PTOWS
| TRAXCTRL_PTOWT
);
370 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_TRAXCTRL
, traxctl
| TRAXCTRL_TRSTP
);
371 xtensa_dm_queue_tdi_idle(dm
);
372 res
= xtensa_dm_queue_execute(dm
);
376 /*Check current status of trace hardware */
377 res
= xtensa_dm_trace_status_read(dm
, &trace_status
);
381 if (trace_status
.stat
& TRAXSTAT_TRACT
) {
382 LOG_ERROR("Failed to stop tracing (0x%x)!", trace_status
.stat
);
388 int xtensa_dm_trace_status_read(struct xtensa_debug_module
*dm
, struct xtensa_trace_status
*status
)
390 uint8_t traxstat_buf
[sizeof(uint32_t)];
392 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_TRAXSTAT
, traxstat_buf
);
393 xtensa_dm_queue_tdi_idle(dm
);
394 int res
= xtensa_dm_queue_execute(dm
);
395 if (res
== ERROR_OK
&& status
)
396 status
->stat
= buf_get_u32(traxstat_buf
, 0, 32);
400 int xtensa_dm_trace_config_read(struct xtensa_debug_module
*dm
, struct xtensa_trace_config
*config
)
402 uint8_t traxctl_buf
[sizeof(uint32_t)];
403 uint8_t memadrstart_buf
[sizeof(uint32_t)];
404 uint8_t memadrend_buf
[sizeof(uint32_t)];
405 uint8_t adr_buf
[sizeof(uint32_t)];
410 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_TRAXCTRL
, traxctl_buf
);
411 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_MEMADDRSTART
, memadrstart_buf
);
412 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_MEMADDREND
, memadrend_buf
);
413 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_TRAXADDR
, adr_buf
);
414 xtensa_dm_queue_tdi_idle(dm
);
415 int res
= xtensa_dm_queue_execute(dm
);
416 if (res
== ERROR_OK
) {
417 config
->ctrl
= buf_get_u32(traxctl_buf
, 0, 32);
418 config
->memaddr_start
= buf_get_u32(memadrstart_buf
, 0, 32);
419 config
->memaddr_end
= buf_get_u32(memadrend_buf
, 0, 32);
420 config
->addr
= buf_get_u32(adr_buf
, 0, 32);
425 int xtensa_dm_trace_data_read(struct xtensa_debug_module
*dm
, uint8_t *dest
, uint32_t size
)
430 for (unsigned int i
= 0; i
< size
/ 4; i
++)
431 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_TRAXDATA
, &dest
[i
* 4]);
432 xtensa_dm_queue_tdi_idle(dm
);
433 return xtensa_dm_queue_execute(dm
);
436 int xtensa_dm_perfmon_enable(struct xtensa_debug_module
*dm
, int counter_id
,
437 const struct xtensa_perfmon_config
*config
)
442 uint8_t pmstat_buf
[4];
443 uint32_t pmctrl
= ((config
->tracelevel
) << 4) +
444 (config
->select
<< 8) +
445 (config
->mask
<< 16) +
446 (config
->kernelcnt
<< 3);
448 /* enable performance monitor */
449 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_PMG
, 0x1);
451 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_PM0
+ counter_id
, 0);
452 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_PMCTRL0
+ counter_id
, pmctrl
);
453 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_PMSTAT0
+ counter_id
, pmstat_buf
);
454 xtensa_dm_queue_tdi_idle(dm
);
455 return xtensa_dm_queue_execute(dm
);
458 int xtensa_dm_perfmon_dump(struct xtensa_debug_module
*dm
, int counter_id
,
459 struct xtensa_perfmon_result
*out_result
)
461 uint8_t pmstat_buf
[4];
462 uint8_t pmcount_buf
[4];
464 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_PMSTAT0
+ counter_id
, pmstat_buf
);
465 dm
->dbg_ops
->queue_reg_read(dm
, XDMREG_PM0
+ counter_id
, pmcount_buf
);
466 xtensa_dm_queue_tdi_idle(dm
);
467 int res
= xtensa_dm_queue_execute(dm
);
468 if (res
== ERROR_OK
) {
469 uint32_t stat
= buf_get_u32(pmstat_buf
, 0, 32);
470 uint64_t result
= buf_get_u32(pmcount_buf
, 0, 32);
472 /* TODO: if counter # counter_id+1 has 'select' set to 1, use its value as the
473 * high 32 bits of the counter. */
475 out_result
->overflow
= ((stat
& 1) != 0);
476 out_result
->value
= result
;
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)