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 void xtensa_dm_add_set_ir(struct xtensa_debug_module
*dm
, uint8_t value
)
39 struct scan_field field
;
40 uint8_t t
[4] = { 0, 0, 0, 0 };
42 memset(&field
, 0, sizeof(field
));
43 field
.num_bits
= dm
->tap
->ir_length
;
45 buf_set_u32(t
, 0, field
.num_bits
, value
);
46 jtag_add_ir_scan(dm
->tap
, &field
, TAP_IDLE
);
49 static void xtensa_dm_add_dr_scan(struct xtensa_debug_module
*dm
,
55 struct scan_field field
;
57 memset(&field
, 0, sizeof(field
));
59 field
.out_value
= src
;
60 field
.in_value
= dest
;
61 jtag_add_dr_scan(dm
->tap
, 1, &field
, endstate
);
64 int xtensa_dm_init(struct xtensa_debug_module
*dm
, const struct xtensa_debug_module_config
*cfg
)
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);
74 dm
->pwr_ops
= cfg
->pwr_ops
;
75 dm
->dbg_ops
= cfg
->dbg_ops
;
77 dm
->queue_tdi_idle
= cfg
->queue_tdi_idle
;
78 dm
->queue_tdi_idle_arg
= cfg
->queue_tdi_idle_arg
;
80 dm
->debug_ap
= cfg
->debug_ap
;
81 dm
->debug_apsel
= cfg
->debug_apsel
;
82 dm
->ap_offset
= cfg
->ap_offset
;
86 void xtensa_dm_deinit(struct xtensa_debug_module
*dm
)
89 dap_put_ap(dm
->debug_ap
);
94 int xtensa_dm_poll(struct xtensa_debug_module
*dm
)
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.
101 return (!dm
|| (dm
->dap
&& !dm
->debug_ap
)) ? ERROR_FAIL
: ERROR_OK
;
104 int xtensa_dm_examine(struct xtensa_debug_module
*dm
)
106 struct adiv5_dap
*swjdp
= dm
->dap
;
107 int retval
= ERROR_OK
;
110 LOG_DEBUG("DM examine: DAP AP select %d", dm
->debug_apsel
);
112 dap_put_ap(dm
->debug_ap
);
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");
124 dm
->debug_ap
= dap_get_ap(swjdp
, dm
->debug_apsel
);
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
);
131 /* Leave (only) generic DAP stuff for debugport_init(); */
132 dm
->debug_ap
->memaccess_tck
= 8;
134 retval
= mem_ap_init(dm
->debug_ap
);
135 if (retval
!= ERROR_OK
) {
136 LOG_ERROR("MEM-AP init failed: %d", retval
);
140 /* TODO: how to set autoincrement range? Hard-code it to 1024 bytes for now */
141 dm
->debug_ap
->tar_autoincr_block
= (1 << 10);
147 int xtensa_dm_queue_enable(struct xtensa_debug_module
*dm
)
149 return dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_DCRSET
, OCDDCR_ENABLEOCD
);
152 int xtensa_dm_queue_reg_read(struct xtensa_debug_module
*dm
, enum xtensa_dm_reg reg
, uint8_t *value
)
154 if (reg
>= XDMREG_NUM
) {
155 LOG_ERROR("Invalid DBG reg ID %d!", reg
);
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.
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
, ®data
, NULL
, TAP_IDLE
);
167 xtensa_dm_add_dr_scan(dm
, TAPINS_NARSEL_DATALEN
, dummy
, value
, TAP_IDLE
);
171 int xtensa_dm_queue_reg_write(struct xtensa_debug_module
*dm
, enum xtensa_dm_reg reg
, uint32_t value
)
173 if (reg
>= XDMREG_NUM
) {
174 LOG_ERROR("Invalid DBG reg ID %d!", reg
);
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
, ®data
, NULL
, TAP_IDLE
);
183 xtensa_dm_add_dr_scan(dm
, TAPINS_NARSEL_DATALEN
, valdata
, NULL
, TAP_IDLE
);
187 int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module
*dm
,
188 enum xtensa_dm_pwr_reg reg
,
192 if (reg
>= XDMREG_PWRNUM
) {
193 LOG_ERROR("Invalid PWR reg ID %d!", reg
);
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.
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
);
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
);
214 int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module
*dm
,
215 enum xtensa_dm_pwr_reg reg
,
218 if (reg
>= XDMREG_PWRNUM
) {
219 LOG_ERROR("Invalid PWR reg ID %d!", reg
);
223 uint32_t apbreg
= xdm_pwr_regs
[reg
].apb
+ dm
->ap_offset
;
224 return mem_ap_write_u32(dm
->debug_ap
, apbreg
, data
);
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
);
234 int xtensa_dm_device_id_read(struct xtensa_debug_module
*dm
)
236 uint8_t id_buf
[sizeof(uint32_t)];
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
);
243 dm
->device_id
= buf_get_u32(id_buf
, 0, 32);
247 int xtensa_dm_power_status_read(struct xtensa_debug_module
*dm
, uint32_t clear
)
249 uint8_t stat_buf
[sizeof(uint32_t)] = { 0, 0, 0, 0 };
250 uint8_t stath_buf
[sizeof(uint32_t)] = { 0, 0, 0, 0 };
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);
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
);
262 dm
->power_status
.stat
= buf_get_u32(stat_buf
, 0, 32);
263 dm
->power_status
.stath
= buf_get_u32(stath_buf
, 0, 32);
267 int xtensa_dm_core_status_read(struct xtensa_debug_module
*dm
)
269 uint8_t dsr_buf
[sizeof(uint32_t)];
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
);
277 dm
->core_status
.dsr
= buf_get_u32(dsr_buf
, 0, 32);
281 int xtensa_dm_core_status_clear(struct xtensa_debug_module
*dm
, xtensa_dsr_t bits
)
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
);
288 int xtensa_dm_trace_start(struct xtensa_debug_module
*dm
, struct xtensa_trace_start_config
*cfg
)
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
);
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
);
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(
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
);
316 int xtensa_dm_trace_stop(struct xtensa_debug_module
*dm
, bool pto_enable
)
318 uint8_t traxctl_buf
[sizeof(uint32_t)];
320 struct xtensa_trace_status trace_status
;
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
);
327 traxctl
= buf_get_u32(traxctl_buf
, 0, 32);
330 traxctl
&= ~(TRAXCTRL_PTOWS
| TRAXCTRL_PTOWT
);
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
);
338 /*Check current status of trace hardware */
339 res
= xtensa_dm_trace_status_read(dm
, &trace_status
);
343 if (trace_status
.stat
& TRAXSTAT_TRACT
) {
344 LOG_ERROR("Failed to stop tracing (0x%x)!", trace_status
.stat
);
350 int xtensa_dm_trace_status_read(struct xtensa_debug_module
*dm
, struct xtensa_trace_status
*status
)
352 uint8_t traxstat_buf
[sizeof(uint32_t)];
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);
362 int xtensa_dm_trace_config_read(struct xtensa_debug_module
*dm
, struct xtensa_trace_config
*config
)
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)];
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);
387 int xtensa_dm_trace_data_read(struct xtensa_debug_module
*dm
, uint8_t *dest
, uint32_t size
)
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
);
398 int xtensa_dm_perfmon_enable(struct xtensa_debug_module
*dm
, int counter_id
,
399 const struct xtensa_perfmon_config
*config
)
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);
410 /* enable performance monitor */
411 dm
->dbg_ops
->queue_reg_write(dm
, XDMREG_PMG
, 0x1);
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
);
420 int xtensa_dm_perfmon_dump(struct xtensa_debug_module
*dm
, int counter_id
,
421 struct xtensa_perfmon_result
*out_result
)
423 uint8_t pmstat_buf
[4];
424 uint8_t pmcount_buf
[4];
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);
434 /* TODO: if counter # counter_id+1 has 'select' set to 1, use its value as the
435 * high 32 bits of the counter. */
437 out_result
->overflow
= ((stat
& 1) != 0);
438 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)