Skip to content

Commit

Permalink
coresight: cortex-m: 'primary_core' option (#1483)
Browse files Browse the repository at this point in the history
- Add 'primary_core' option.
- SoCTarget selects chosen primary core when it is added.
- Primary core is used in multicore debug mode to determine which core
gets system reset control.
- Update multicore debug docs.
  • Loading branch information
flit authored Nov 26, 2022
1 parent 138efda commit 66c0d0e
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 19 deletions.
24 changes: 11 additions & 13 deletions docs/multicore_debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,27 @@ title: Multicore debug
---

pyOCD supports debugging multicore devices. It does this by serving one gdb server per core, to which
you connect independant gdb instances. This is the most reliable method of debugging multicore
embedded devices using gdb.
independent gdb instances are connected. This is the most reliable method of debugging asymmetric multicore
devices using gdb.

`pyocd gdbserver` automatically creates one `GDBServer` instance per core. The first core is given the
user-specified port number. Additional cores have port numbers incremented from there.
`pyocd gdbserver` automatically creates one gdb server instance per core by default. The primary core is given the user-specified port number. Additional cores have port numbers incremented from there. If a gdb server for only one or a subset of cores is desired, the `--core` command line argument can be used with a list of core numbers.

To prevent reset requests from multiple connected gdb instances causing havoc, secondary cores have
their default reset type set to core-only reset (VECTRESET), which will fall back to an emulated
reset for non-v7-M architectures. This feature can be disabled by setting the
`enable_multicore_debug` session option to false.
By default, the primary core is core number 0. For Arm CoreSight based devices, this will be the core with the lowest associated access port address. Use the `primary_core` session option to change the primary core.

When performing multicore debug where multiple gdb instances are connected simultaneously, it is important to set the `enable_multicore_debug` session option to true. This changes secondary cores to have their default reset type set to core-only reset (`sw_core`). This prevents competing reset requests from the multiple gdb instances causing havoc. On v7-M architecture cores, VECTRESET is used. However, VECTRESET is not supported on other core architecture, so non-v7-M architectures will fall back to an emulated core reset.

To debug a multicore device, run `pyocd gdbserver` as usual. This will connect to the device, detect
the cores, and create the gdb server instances on separate ports. Next, start up two gdb instances
and connect to the two gdb server ports. For instance, on a dual core device if you pass 3333 for
the port, connect to port 3333 for the first core and port 3334 for the second core.
the port (or leave it set to default), connect to port 3333 for the first core and port 3334 for the second core.

On many devices, secondary cores are by default held in reset until released by the primary core.
Because gdb does not have a concept of a core held in reset, pyOCD will report a core held in reset
by telling gdb that there is a single thread with the name "Reset". This is visible if you run the
show threads gdb command, and will appear in the Eclipe Debug view's list of threads. All register
by telling gdb that there is a single thread with the name Reset. This is visible if you run the
show threads gdb command, and will appear in the VSCode or Eclipse Debug view's list of threads. All register
values will be reported as 0 until the core is released from reset.

Usually you want to have the primary core load code for the secondary core, so configure the second
core's gdb to not load any code to the target. This is highly device-specific, though, and may
Usually you want to have the primary core load code and/or configure the reset vector for secondary cores prior to releasing those cores from reset. For this situation, configure the second
core's gdb to not load any code to the target. This usage is highly device-specific, though, and may
depend on whether the secondary core's code is running out of flash or RAM.

14 changes: 12 additions & 2 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ Print tracebacks for exceptions.
<td>False</td>
<td>
Whether to put pyOCD into multicore debug mode. The primary effect is to modify the default software
reset type for secondary cores to use VECTRESET, which will fall back to emulated reset if the
secondary core is not v7-M.
reset type for secondary cores to use VECTRESET, which will fall back to emulated reset for
secondary cores that are not v7-M architecture (VECTRESET is only supported on v7-M). The core that is
considered the primary core is selected by the `primary_core` option.
</td></tr>

<tr><td>fast_program</td>
Expand Down Expand Up @@ -255,6 +256,15 @@ Path or list of paths to CMSIS Device Family Packs. Devices defined in the pack(
list of available targets.
</td></tr>

<tr><td>primary_core</td>
<td>int</td>
<td>0</td>
<td>
Core number for the primary/boot core of an asymmetric multicore target. Becomes the default selected
core in the commander and the `SoCTarget` class. Also the core that will control system reset when
`enable_multicore_debug` is set.
</td></tr>

<tr><td>probeserver.port</td>
<td>int</td>
<td>5555</td>
Expand Down
6 changes: 5 additions & 1 deletion pyocd/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class OptionInfo(NamedTuple):
OptionInfo('debug.traceback', bool, True,
"Print tracebacks for exceptions."),
OptionInfo('enable_multicore_debug', bool, False,
"Whether to put pyOCD into multicore debug mode."),
"Whether to put pyOCD into multicore debug mode. Doing so changes the default software reset type of "
"secondary cores to VECTRESET, or emulated reset if that is not supported (i.e., non-v7-M cores)."),
OptionInfo('fast_program', bool, False,
"Setting this option to True will use CRC checks of existing flash sector contents to "
"determine whether pages need to be programmed."),
Expand Down Expand Up @@ -95,6 +96,9 @@ class OptionInfo(NamedTuple):
OptionInfo('pack', (str, list), None,
"Path or list of paths to CMSIS Device Family Packs. Devices defined in the pack(s) are "
"added to the list of available targets."),
OptionInfo('primary_core', int, 0,
"Core number for the primary/boot core of an asymmetric multicore target. This is the core that "
"will control system reset when 'enable_multicore' is set."),
OptionInfo('probeserver.port', int, 5555,
"TCP port for the debug probe server."),
OptionInfo('project_dir', str, None,
Expand Down
6 changes: 5 additions & 1 deletion pyocd/core/soc_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,13 @@ def add_core(self, core: CoreTarget) -> None:
self.cores[core.core_number] = core
self.add_child(core)

# Select first added core.
# Always first added core to ensure some core is selected.
if self.selected_core is None:
self.selected_core = core.core_number
# Otherwise, when the chosen primary code is added, select it. This assumes that cores are only
# added at init time.
elif core.core_number == self.session.options.get('primary_core'):
self.selected_core = core.core_number

def create_init_sequence(self) -> CallSequence:
# Return an empty call sequence. The subclass must override this.
Expand Down
7 changes: 5 additions & 2 deletions pyocd/coresight/cortex_m.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,12 @@ def __init__(self,
self._default_reset_type = Target.ResetType.SW

# Select default sw reset type based on whether multicore debug is enabled and which core
# this is.
# this is. Even though SW_VECTRESET isn't added (above) to the supported reset types by default,
# and is only supported on v7-M, it's ok to select it here because it will automatically fall
# back to SW_EMULATED in ._get_actual_reset_type().
self._default_software_reset_type = Target.ResetType.SW_SYSRESETREQ \
if (not self.session.options.get('enable_multicore_debug')) or (self.core_number == 0) \
if (not self.session.options.get('enable_multicore_debug')) \
or (self.core_number == self.session.options.get('primary_core')) \
else Target.ResetType.SW_VECTRESET

# Set up breakpoints manager.
Expand Down

0 comments on commit 66c0d0e

Please sign in to comment.