Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,22 @@ Whether to preserve existing flash content for ranges of sectors that will be er
with new data.
</td></tr>

<tr><td>load.pre_reset</td>
<td>str</td>
<td><i>No default</i></td>
<td>
Specify the type of reset to perform before programming. The value must be one of
'off', 'default', 'hardware', 'system', 'core', 'n_srst', 'sysresetreq', 'vectreset' or 'emulated'.
</td></tr>

<tr><td>load.post_reset</td>
<td>str</td>
<td><i>No default</i></td>
<td>
Specify the type of reset to perform after programming. The value must be one of
'off', 'default', 'hardware', 'system', 'core', 'n_srst', 'sysresetreq', 'vectreset' or 'emulated'.
</td></tr>

<tr><td>logging</td>
<td>str, dict</td>
<td><i>No default</i></td>
Expand Down
10 changes: 8 additions & 2 deletions pyocd/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class OptionInfo(NamedTuple):
"target memory."),
OptionInfo('chip_erase', str, "sector",
"Whether to perform a chip erase or sector erases when programming flash. The value must be"
" one of \"auto\", \"sector\", or \"chip\"."),
" one of 'auto', 'sector', or 'chip'."),
OptionInfo('cmsis_dap.prefer_v1', bool, False,
"If a device provides both CMSIS-DAP v1 and v2 interfaces, use the v1 interface in preference of v2. "
"Normal behaviour is to prefer the v2 interface. This option is primarily intended for testing."),
Expand Down Expand Up @@ -89,6 +89,12 @@ class OptionInfo(NamedTuple):
OptionInfo('keep_unwritten', bool, False,
"Whether to preserve existing flash content for ranges of sectors that will be erased but not "
"written with new data. Default is False."),
OptionInfo('load.pre_reset', str, None,
"Specify the type of reset to perform before programming. The value must be one of"
" 'off', 'default', 'hardware', 'system', 'core', 'n_srst', 'sysresetreq', 'vectreset' or 'emulated'."),
OptionInfo('load.post_reset', str, None,
"Specify the type of reset to perform after programming. The value must be one of"
" 'off', 'default', 'hardware', 'system', 'core', 'n_srst', 'sysresetreq', 'vectreset' or 'emulated'."),
OptionInfo('logging', (str, dict), None,
"Logging configuration dictionary, or path to YAML file containing logging configuration."),
OptionInfo('no_config', bool, False,
Expand Down Expand Up @@ -171,7 +177,7 @@ class OptionInfo(NamedTuple):
OptionInfo('rtos.name', str, None,
"Name of the RTOS plugin to use. If not set, all RTOS plugins are given a chance to load."),
OptionInfo('semihost_console_type', str, 'telnet',
"If set to \"telnet\" then the semihosting telnet server will be started, otherwise "
"If set to 'telnet' then the semihosting telnet server will be started, otherwise "
"semihosting will print to the console."),
OptionInfo('semihost_use_syscalls', bool, False,
"Whether to use GDB syscalls for semihosting file access operations."),
Expand Down
42 changes: 24 additions & 18 deletions pyocd/coresight/cortex_m.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,24 +873,22 @@ def _get_actual_reset_type(self, reset_type: Optional[Target.ResetType]) -> Targ

# Select the actual reset type to use, based on priority:
# 1. reset_type parameter
# 2. cbuild-run user option
# 3. session option
# 2. session option
# 3. cbuild-run user option (modifies default_reset_type)
# 4. core default_reset_type property

if reset_type is None:
# No explicit reset_type parameter provided, check user options.
if self.session.options.is_set('cbuild_run'):
option_reset_type = self.session.options.get('reset_type')
if option_reset_type == 'default':
_reset_type = self.default_reset_type
elif self.session.options.get('reset_type') != 'default':
option_reset_type = self.session.options.get('reset_type')
else:
try:
_reset_type = cmdline.convert_reset_type(option_reset_type)
except ValueError:
LOG.warning("invalid reset type '%s' specified in user options; falling back to 'default'",
option_reset_type)
_reset_type = self.default_reset_type
else:
_reset_type = self.default_reset_type

reset_type = _reset_type

Expand Down Expand Up @@ -980,22 +978,28 @@ def _post_reset_core_accessibility_test(self) -> None:
else:
LOG.debug("Core %d did not come out of reset within timeout", self.core_number)

def reset_hook(self, reset_type: Target.ResetType) -> Optional[bool]:
def reset_hook(self, reset_type: Target.ResetType) -> None:
# Map our reset type to a reset sequence name.
result = self.call_delegate('will_reset', core=self, reset_type=reset_type)

if not result and (self.debug_sequence_delegate is not None):
# Check if the reset type is DEFAULT, SYSTEM, CORE, or HARDWARE.
# These map to standard debug sequence names. Other reset types
# are handled directly by _perform_reset().
if reset_type in (Target.ResetType.DEFAULT,
Target.ResetType.HARDWARE,
Target.ResetType.SYSTEM,
Target.ResetType.CORE):
STANDARD_RESET_SEQUENCES = {
'ResetHardware': Target.ResetType.HARDWARE,
'ResetSystem': Target.ResetType.SYSTEM,
'ResetProcessor': Target.ResetType.CORE,
}
if reset_type in (Target.ResetType.DEFAULT, *STANDARD_RESET_SEQUENCES.values()):
# Check if a custom reset sequence is defined.
if reset_type == Target.ResetType.DEFAULT:
# Use the core's default reset sequence.
reset_sequence_name = self.debug_sequence_delegate.default_reset_sequence(self.node_name)
# See if the default reset sequence name maps to a standard reset type.
if reset_sequence_name in STANDARD_RESET_SEQUENCES:
# Update reset_type so we can use it later in _perform_reset().
reset_type = STANDARD_RESET_SEQUENCES[reset_sequence_name]
elif reset_type == Target.ResetType.HARDWARE:
reset_sequence_name = 'ResetHardware'
elif reset_type == Target.ResetType.SYSTEM:
Expand All @@ -1008,13 +1012,18 @@ def reset_hook(self, reset_type: Target.ResetType) -> Optional[bool]:
LOG.debug("running '%s' debug sequence, core %d", reset_sequence_name, self.core_number)
self.debug_sequence_delegate.run_sequence(reset_sequence_name, pname=self.node_name)
result = True
elif reset_sequence_name not in ('ResetSystem', 'ResetProcessor', 'ResetHardware'):
elif reset_sequence_name not in STANDARD_RESET_SEQUENCES:
# Custom reset sequence was specified but not found. Warn the user, but don't fall back
# to a different reset method.
LOG.error("skipping missing '%s' debug sequence, core %d", reset_sequence_name, self.core_number)
result = True

return result
if not result:
# No delegate or debug sequence handled the reset, so do it ourselves.
LOG.debug("no delegate or debug sequence handled reset; performing %s reset, core %d",
reset_type.name, self.core_number)

self._perform_reset(reset_type)

def _inner_reset(self, reset_type: Optional[Target.ResetType], is_halting: bool) -> None:
"""@brief Internal routine for resetting the core.
Expand All @@ -1029,10 +1038,7 @@ def _inner_reset(self, reset_type: Optional[Target.ResetType], is_halting: bool)

self._run_token += 1

# Give the delegate a chance to overide reset. If the delegate returns True, then it
# handled the reset on its own.
if not self.reset_hook(reset_type):
self._perform_reset(reset_type)
self.reset_hook(reset_type)

# Unless this is a halting reset, make sure the core is not halted. Some DFP debug sequences
# (or user scripts) can leave the core halted after a reset.
Expand Down
40 changes: 28 additions & 12 deletions pyocd/subcommands/load_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from ..core.target import Target
from ..flash.file_programmer import FileProgrammer
from ..utility.cmdline import (
convert_reset_type,
convert_session_options,
int_base_0,
)
Expand Down Expand Up @@ -111,16 +112,24 @@ def invoke(self) -> int:

# Get a list of all secondary cores.
secondary_cores = [c for c in session.target.cores.values() if c != session.target.primary_core]
try:
# Set reset catch for all secondary cores.
for core in secondary_cores:
core.set_reset_catch()
# Reset and halt the primary core.
session.target.reset_and_halt()
finally:
# Clear reset catch for all secondary cores.
for core in secondary_cores:
core.clear_reset_catch()
pre_reset = session.options.get('load.pre_reset')
if pre_reset != "off":
try:
reset_type = convert_reset_type(pre_reset) if pre_reset else None
except ValueError:
LOG.error("Invalid pre-reset option: %s", pre_reset)
return 1

try:
# Set reset catch for all secondary cores.
for core in secondary_cores:
core.set_reset_catch(reset_type)
# Reset and halt the primary core.
session.target.reset_and_halt(reset_type)
finally:
# Clear reset catch for all secondary cores.
for core in secondary_cores:
core.clear_reset_catch(reset_type)

if not self._args.file and self._args.cbuild_run:
# Populate file list from cbuild-run output if not provided explicitly
Expand Down Expand Up @@ -164,7 +173,14 @@ def invoke(self) -> int:
file_format=file_format)

# Reset the target after programming unless --no-reset was specified.
if not self._args.no_reset:
session.target.reset(Target.ResetType.NSRST)
post_reset = session.options.get('load.post_reset')
if not self._args.no_reset and post_reset != 'off':
try:
reset_type = convert_reset_type(post_reset) if post_reset else None
except ValueError:
LOG.error("Invalid post-reset option: %s", post_reset)
return 1

session.target.reset(reset_type)

return 0
Loading