1 /***************************************************************************
2 * ESP Xtensa SMP target API for OpenOCD *
3 * Copyright (C) 2020 Espressif Systems Ltd. Co *
4 * Author: Alexey Gerenkov <alexey@espressif.com> *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 ***************************************************************************/
25 #include <target/target.h>
26 #include <target/target_type.h>
27 #include <target/smp.h>
28 #include "esp_xtensa_smp.h"
31 Multiprocessor stuff common:
33 The ESP Xtensa chip can have several cores in it, which can run in SMP-mode if an
34 SMP-capable OS is running. The hardware has a few features which makes
35 SMP debugging much easier.
37 First of all, there's something called a 'break network', consisting of a
38 BreakIn input and a BreakOut output on each CPU. The idea is that as soon
39 as a CPU goes into debug mode for whatever reason, it'll signal that using
40 its DebugOut pin. This signal is connected to the other CPU's DebugIn
41 input, causing this CPU also to go into debugging mode. To resume execution
42 when using only this break network, we will need to manually resume both
45 An alternative to this is the XOCDMode output and the RunStall (or DebugStall)
46 input. When these are cross-connected, a CPU that goes into debug mode will
47 halt execution entirely on the other CPU. Execution on the other CPU can be
48 resumed by either the first CPU going out of debug mode, or the second CPU
49 going into debug mode: the stall is temporarily lifted as long as the stalled
52 A third, separate, signal is CrossTrigger. This is connected in the same way
53 as the breakIn/breakOut network, but is for the TRAX (trace memory) feature;
54 it does not affect OCD in any way.
60 The ESP Xtensa chip has several Xtensa cores inside, but represent themself to the OCD
61 as one chip that works in multithreading mode under FreeRTOS OS.
62 The core that initiate the stop condition will be defined as an active cpu.
63 When one core stops, then other core will be stopped automatically by smpbreak.
64 The core that initiates stop condition will be defined as an active core, and
65 registers of this core will be transferred.
68 #define ESP_XTENSA_SMP_EXAMINE_OTHER_CORES 5
70 static int esp_xtensa_smp_update_halt_gdb(struct target
*target
, bool *need_resume
);
72 static inline struct esp_xtensa_smp_common
*target_to_esp_xtensa_smp(struct target
*target
)
74 return container_of(target
->arch_info
, struct esp_xtensa_smp_common
, esp_xtensa
);
77 int esp_xtensa_smp_assert_reset(struct target
*target
)
82 int esp_xtensa_smp_deassert_reset(struct target
*target
)
84 LOG_TARGET_DEBUG(target
, "begin");
86 int ret
= xtensa_deassert_reset(target
);
89 /* in SMP mode when chip was running single-core app the other core can be left un-examined,
90 because examination is done before SOC reset. But after SOC reset it is functional and should be handled.
91 So try to examine un-examined core just after SOC reset */
92 if (target
->smp
&& !target_was_examined(target
))
93 ret
= xtensa_examine(target
);
97 int esp_xtensa_smp_soft_reset_halt(struct target
*target
)
100 struct target_list
*head
;
101 struct esp_xtensa_smp_common
*esp_xtensa_smp
= target_to_esp_xtensa_smp(target
);
103 LOG_TARGET_DEBUG(target
, "begin");
104 /* in SMP mode we need to ensure that at first we reset SOC on PRO-CPU
105 and then call xtensa_assert_reset() for all cores */
106 if (target
->smp
&& target
->coreid
!= 0)
108 /* Reset the SoC first */
109 if (esp_xtensa_smp
->chip_ops
->reset
) {
110 res
= esp_xtensa_smp
->chip_ops
->reset(target
);
115 return xtensa_assert_reset(target
);
117 foreach_smp_target(head
, target
->smp_targets
) {
118 res
= xtensa_assert_reset(head
->target
);
125 static struct target
*get_halted_esp_xtensa_smp(struct target
*target
, int32_t coreid
)
127 struct target_list
*head
;
130 foreach_smp_target(head
, target
->smp_targets
) {
132 if ((curr
->coreid
== coreid
) && (curr
->state
== TARGET_HALTED
))
139 int esp_xtensa_smp_poll(struct target
*target
)
141 enum target_state old_state
= target
->state
;
142 struct esp_xtensa_smp_common
*esp_xtensa_smp
= target_to_esp_xtensa_smp(target
);
143 struct target_list
*head
;
145 bool other_core_resume_req
= false;
147 if (target
->state
== TARGET_HALTED
&& target
->smp
&& target
->gdb_service
&& !target
->gdb_service
->target
) {
148 target
->gdb_service
->target
= get_halted_esp_xtensa_smp(target
, target
->gdb_service
->core
[1]);
149 LOG_INFO("Switch GDB target to '%s'", target_name(target
->gdb_service
->target
));
150 if (esp_xtensa_smp
->chip_ops
->on_halt
)
151 esp_xtensa_smp
->chip_ops
->on_halt(target
);
152 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
156 int ret
= esp_xtensa_poll(target
);
161 if (target
->state
== TARGET_RESET
) {
162 esp_xtensa_smp
->examine_other_cores
= ESP_XTENSA_SMP_EXAMINE_OTHER_CORES
;
163 } else if (esp_xtensa_smp
->examine_other_cores
> 0 &&
164 (target
->state
== TARGET_RUNNING
|| target
->state
== TARGET_HALTED
)) {
165 LOG_TARGET_DEBUG(target
, "Check for unexamined cores after reset");
166 bool all_examined
= true;
167 foreach_smp_target(head
, target
->smp_targets
) {
171 if (!target_was_examined(curr
)) {
172 if (target_examine_one(curr
) != ERROR_OK
) {
173 LOG_DEBUG("Failed to examine!");
174 all_examined
= false;
179 esp_xtensa_smp
->examine_other_cores
= 0;
181 esp_xtensa_smp
->examine_other_cores
--;
185 if (old_state
!= TARGET_HALTED
&& target
->state
== TARGET_HALTED
) {
187 ret
= esp_xtensa_smp_update_halt_gdb(target
, &other_core_resume_req
);
191 /* Call any event callbacks that are applicable */
192 if (old_state
== TARGET_DEBUG_RUNNING
) {
193 target_call_event_callbacks(target
, TARGET_EVENT_DEBUG_HALTED
);
195 /* check whether any core polled by esp_xtensa_smp_update_halt_gdb() requested resume */
196 if (target
->smp
&& other_core_resume_req
) {
197 /* Resume xtensa_resume will handle BREAK instruction. */
198 ret
= target_resume(target
, 1, 0, 1, 0);
199 if (ret
!= ERROR_OK
) {
200 LOG_ERROR("Failed to resume target");
205 if (esp_xtensa_smp
->chip_ops
->on_halt
)
206 esp_xtensa_smp
->chip_ops
->on_halt(target
);
207 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
214 static int esp_xtensa_smp_update_halt_gdb(struct target
*target
, bool *need_resume
)
216 struct esp_xtensa_smp_common
*esp_xtensa_smp
;
217 struct target
*gdb_target
= NULL
;
218 struct target_list
*head
;
222 *need_resume
= false;
224 if (target
->gdb_service
&& target
->gdb_service
->target
)
225 LOG_DEBUG("GDB target '%s'", target_name(target
->gdb_service
->target
));
227 if (target
->gdb_service
&& target
->gdb_service
->core
[0] == -1) {
228 target
->gdb_service
->target
= target
;
229 target
->gdb_service
->core
[0] = target
->coreid
;
230 LOG_INFO("Set GDB target to '%s'", target_name(target
));
233 if (target
->gdb_service
)
234 gdb_target
= target
->gdb_service
->target
;
236 /* due to smpbreak config other cores can also go to HALTED state */
237 foreach_smp_target(head
, target
->smp_targets
) {
239 LOG_DEBUG("Check target '%s'", target_name(curr
));
240 /* skip calling context */
243 if (!target_was_examined(curr
)) {
244 curr
->state
= TARGET_HALTED
;
247 /* skip targets that were already halted */
248 if (curr
->state
== TARGET_HALTED
)
250 /* Skip gdb_target; it alerts GDB so has to be polled as last one */
251 if (curr
== gdb_target
)
253 LOG_DEBUG("Poll target '%s'", target_name(curr
));
255 esp_xtensa_smp
= target_to_esp_xtensa_smp(curr
);
256 /* avoid auto-resume after syscall, it will be done later */
257 esp_xtensa_smp
->other_core_does_resume
= true;
258 /* avoid recursion in esp_xtensa_smp_poll() */
260 if (esp_xtensa_smp
->chip_ops
->poll
)
261 ret
= esp_xtensa_smp
->chip_ops
->poll(curr
);
263 ret
= esp_xtensa_smp_poll(curr
);
267 esp_xtensa_smp
->other_core_does_resume
= false;
270 /* after all targets were updated, poll the gdb serving target */
271 if (gdb_target
&& gdb_target
!= target
) {
272 esp_xtensa_smp
= target_to_esp_xtensa_smp(gdb_target
);
273 if (esp_xtensa_smp
->chip_ops
->poll
)
274 ret
= esp_xtensa_smp
->chip_ops
->poll(gdb_target
);
276 ret
= esp_xtensa_smp_poll(gdb_target
);
284 static inline int esp_xtensa_smp_smpbreak_disable(struct target
*target
, uint32_t *smp_break
)
286 int res
= xtensa_smpbreak_get(target
, smp_break
);
289 return xtensa_smpbreak_set(target
, 0);
292 static inline int esp_xtensa_smp_smpbreak_restore(struct target
*target
, uint32_t smp_break
)
294 return xtensa_smpbreak_set(target
, smp_break
);
297 static int esp_xtensa_smp_resume_cores(struct target
*target
,
298 int handle_breakpoints
,
301 struct target_list
*head
;
304 LOG_TARGET_DEBUG(target
, "begin");
306 foreach_smp_target(head
, target
->smp_targets
) {
308 /* in single-core mode disabled core cannot be examined, but need to be resumed too*/
309 if ((curr
!= target
) && (curr
->state
!= TARGET_RUNNING
) && target_was_examined(curr
)) {
310 /* resume current address, not in SMP mode */
312 int res
= esp_xtensa_smp_resume(curr
, 1, 0, handle_breakpoints
, debug_execution
);
321 int esp_xtensa_smp_resume(struct target
*target
,
323 target_addr_t address
,
324 int handle_breakpoints
,
330 xtensa_smpbreak_get(target
, &smp_break
);
331 LOG_TARGET_DEBUG(target
, "smp_break=0x%" PRIx32
, smp_break
);
333 /* dummy resume for smp toggle in order to reduce gdb impact */
334 if ((target
->smp
) && (target
->gdb_service
) && (target
->gdb_service
->core
[1] != -1)) {
335 /* simulate a start and halt of target */
336 target
->gdb_service
->target
= NULL
;
337 target
->gdb_service
->core
[0] = target
->gdb_service
->core
[1];
338 /* fake resume at next poll we play the target core[1], see poll*/
339 LOG_TARGET_DEBUG(target
, "Fake resume");
340 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
344 /* xtensa_prepare_resume() can step over breakpoint/watchpoint and generate signals on BreakInOut circuit for
345 * other cores. So disconnect this core from BreakInOut circuit and do xtensa_prepare_resume(). */
346 res
= esp_xtensa_smp_smpbreak_disable(target
, &smp_break
);
349 res
= xtensa_prepare_resume(target
, current
, address
, handle_breakpoints
, debug_execution
);
350 /* restore configured BreakInOut signals config */
351 int ret
= esp_xtensa_smp_smpbreak_restore(target
, smp_break
);
354 if (res
!= ERROR_OK
) {
355 LOG_TARGET_ERROR(target
, "Failed to prepare for resume!");
360 if (target
->gdb_service
)
361 target
->gdb_service
->core
[0] = -1;
362 res
= esp_xtensa_smp_resume_cores(target
, handle_breakpoints
, debug_execution
);
367 res
= xtensa_do_resume(target
);
368 if (res
!= ERROR_OK
) {
369 LOG_TARGET_ERROR(target
, "Failed to resume!");
373 target
->debug_reason
= DBG_REASON_NOTHALTED
;
374 if (!debug_execution
)
375 target
->state
= TARGET_RUNNING
;
377 target
->state
= TARGET_DEBUG_RUNNING
;
379 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
383 int esp_xtensa_smp_step(struct target
*target
,
385 target_addr_t address
,
386 int handle_breakpoints
)
389 uint32_t smp_break
= 0;
390 struct esp_xtensa_smp_common
*esp_xtensa_smp
= target_to_esp_xtensa_smp(target
);
393 res
= esp_xtensa_smp_smpbreak_disable(target
, &smp_break
);
397 res
= xtensa_step(target
, current
, address
, handle_breakpoints
);
399 if (res
== ERROR_OK
) {
400 if (esp_xtensa_smp
->chip_ops
->on_halt
)
401 esp_xtensa_smp
->chip_ops
->on_halt(target
);
402 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
406 int ret
= esp_xtensa_smp_smpbreak_restore(target
, smp_break
);
414 int esp_xtensa_smp_watchpoint_add(struct target
*target
, struct watchpoint
*watchpoint
)
416 int res
= xtensa_watchpoint_add(target
, watchpoint
);
423 struct target_list
*head
;
424 foreach_smp_target(head
, target
->smp_targets
) {
425 struct target
*curr
= head
->target
;
426 if (curr
== target
|| !target_was_examined(curr
))
428 /* Need to use high level API here because every target for core contains list of watchpoints.
429 * GDB works with active core only, so we need to duplicate every watchpoint on other cores,
430 * otherwise watchpoint_free() on active core can fail if WP has been initially added on another core. */
432 res
= watchpoint_add(curr
, watchpoint
->address
, watchpoint
->length
,
433 watchpoint
->rw
, watchpoint
->value
, watchpoint
->mask
);
441 int esp_xtensa_smp_watchpoint_remove(struct target
*target
, struct watchpoint
*watchpoint
)
443 int res
= xtensa_watchpoint_remove(target
, watchpoint
);
450 struct target_list
*head
;
451 foreach_smp_target(head
, target
->smp_targets
) {
452 struct target
*curr
= head
->target
;
455 /* see big comment in esp_xtensa_smp_watchpoint_add() */
457 watchpoint_remove(curr
, watchpoint
->address
);
463 int esp_xtensa_smp_init_arch_info(struct target
*target
,
464 struct esp_xtensa_smp_common
*esp_xtensa_smp
,
465 const struct xtensa_config
*xtensa_cfg
,
466 struct xtensa_debug_module_config
*dm_cfg
,
467 const struct esp_xtensa_smp_chip_ops
*chip_ops
)
469 int ret
= esp_xtensa_init_arch_info(target
, &esp_xtensa_smp
->esp_xtensa
, xtensa_cfg
, dm_cfg
);
472 esp_xtensa_smp
->chip_ops
= chip_ops
;
473 esp_xtensa_smp
->examine_other_cores
= ESP_XTENSA_SMP_EXAMINE_OTHER_CORES
;
477 int esp_xtensa_smp_target_init(struct command_context
*cmd_ctx
, struct target
*target
)
479 return esp_xtensa_target_init(cmd_ctx
, target
);
482 COMMAND_HANDLER(esp_xtensa_smp_cmd_permissive_mode
)
484 struct target
*target
= get_current_target(CMD_CTX
);
485 if (target
->smp
&& CMD_ARGC
> 0) {
486 struct target_list
*head
;
488 foreach_smp_target(head
, target
->smp_targets
) {
490 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_permissive_mode_do
,
491 target_to_xtensa(curr
));
497 return CALL_COMMAND_HANDLER(xtensa_cmd_permissive_mode_do
,
498 target_to_xtensa(target
));
501 COMMAND_HANDLER(esp_xtensa_smp_cmd_smpbreak
)
503 struct target
*target
= get_current_target(CMD_CTX
);
504 if (target
->smp
&& CMD_ARGC
> 0) {
505 struct target_list
*head
;
507 foreach_smp_target(head
, target
->smp_targets
) {
509 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do
, curr
);
515 return CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do
, target
);
518 COMMAND_HANDLER(esp_xtensa_smp_cmd_mask_interrupts
)
520 struct target
*target
= get_current_target(CMD_CTX
);
521 if (target
->smp
&& CMD_ARGC
> 0) {
522 struct target_list
*head
;
524 foreach_smp_target(head
, target
->smp_targets
) {
526 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do
,
527 target_to_xtensa(curr
));
533 return CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do
,
534 target_to_xtensa(target
));
537 COMMAND_HANDLER(esp_xtensa_smp_cmd_perfmon_enable
)
539 struct target
*target
= get_current_target(CMD_CTX
);
540 if (target
->smp
&& CMD_ARGC
> 0) {
541 struct target_list
*head
;
543 foreach_smp_target(head
, target
->smp_targets
) {
545 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do
,
546 target_to_xtensa(curr
));
552 return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do
,
553 target_to_xtensa(target
));
556 COMMAND_HANDLER(esp_xtensa_smp_cmd_perfmon_dump
)
558 struct target
*target
= get_current_target(CMD_CTX
);
560 struct target_list
*head
;
562 foreach_smp_target(head
, target
->smp_targets
) {
564 LOG_INFO("CPU%d:", curr
->coreid
);
565 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do
,
566 target_to_xtensa(curr
));
572 return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do
,
573 target_to_xtensa(target
));
576 COMMAND_HANDLER(esp_xtensa_smp_cmd_tracestart
)
578 struct target
*target
= get_current_target(CMD_CTX
);
580 struct target_list
*head
;
582 foreach_smp_target(head
, target
->smp_targets
) {
584 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do
,
585 target_to_xtensa(curr
));
591 return CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do
,
592 target_to_xtensa(target
));
595 COMMAND_HANDLER(esp_xtensa_smp_cmd_tracestop
)
597 struct target
*target
= get_current_target(CMD_CTX
);
599 struct target_list
*head
;
601 foreach_smp_target(head
, target
->smp_targets
) {
603 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do
,
604 target_to_xtensa(curr
));
610 return CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do
,
611 target_to_xtensa(target
));
614 COMMAND_HANDLER(esp_xtensa_smp_cmd_tracedump
)
616 struct target
*target
= get_current_target(CMD_CTX
);
618 struct target_list
*head
;
620 int32_t cores_max_id
= 0;
621 /* assume that core IDs are assigned to SMP targets sequentially: 0,1,2... */
622 foreach_smp_target(head
, target
->smp_targets
) {
624 if (cores_max_id
< curr
->coreid
)
625 cores_max_id
= curr
->coreid
;
627 if (CMD_ARGC
< ((uint32_t)cores_max_id
+ 1)) {
629 "Need %d filenames to dump to as output!",
633 foreach_smp_target(head
, target
->smp_targets
) {
635 int ret
= CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do
,
636 target_to_xtensa(curr
), CMD_ARGV
[curr
->coreid
]);
642 return CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do
,
643 target_to_xtensa(target
), CMD_ARGV
[0]);
646 const struct command_registration esp_xtensa_smp_xtensa_command_handlers
[] = {
648 .name
= "set_permissive",
649 .handler
= esp_xtensa_smp_cmd_permissive_mode
,
651 .help
= "When set to 1, enable Xtensa permissive mode (less client-side checks)",
656 .handler
= esp_xtensa_smp_cmd_mask_interrupts
,
658 .help
= "mask Xtensa interrupts at step",
659 .usage
= "['on'|'off']",
663 .handler
= esp_xtensa_smp_cmd_smpbreak
,
665 .help
= "Set the way the CPU chains OCD breaks",
667 "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]",
670 .name
= "perfmon_enable",
671 .handler
= esp_xtensa_smp_cmd_perfmon_enable
,
672 .mode
= COMMAND_EXEC
,
673 .help
= "Enable and start performance counter",
674 .usage
= "<counter_id> <select> [mask] [kernelcnt] [tracelevel]",
677 .name
= "perfmon_dump",
678 .handler
= esp_xtensa_smp_cmd_perfmon_dump
,
679 .mode
= COMMAND_EXEC
,
681 "Dump performance counter value. If no argument specified, dumps all counters.",
682 .usage
= "[counter_id]",
685 .name
= "tracestart",
686 .handler
= esp_xtensa_smp_cmd_tracestart
,
687 .mode
= COMMAND_EXEC
,
689 "Tracing: Set up and start a trace. Optionally set stop trigger address and amount of data captured after.",
690 .usage
= "[pc <pcval>/[maskbitcount]] [after <n> [ins|words]]",
694 .handler
= esp_xtensa_smp_cmd_tracestop
,
695 .mode
= COMMAND_EXEC
,
696 .help
= "Tracing: Stop current trace as started by the tracestart command",
701 .handler
= esp_xtensa_smp_cmd_tracedump
,
702 .mode
= COMMAND_EXEC
,
703 .help
= "Tracing: Dump trace memory to a files. One file per core.",
704 .usage
= "<outfile1> <outfile2>",
706 COMMAND_REGISTRATION_DONE
709 const struct command_registration esp_xtensa_smp_command_handlers
[] = {
713 .chain
= esp_xtensa_smp_xtensa_command_handlers
,
715 COMMAND_REGISTRATION_DONE
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)