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
6 changes: 6 additions & 0 deletions pyocd/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ class OptionInfo(NamedTuple):
"List of telnet input file paths for each core."),
OptionInfo('cbuild_run.telnet_file_out', tuple, None,
"List of telnet output file paths for each core."),
OptionInfo('cbuild_run.rtt_control_block', tuple, None,
"List of RTT control block configurations for each core."),
OptionInfo('cbuild_run.rtt_channel', tuple, None,
"List of RTT channel configurations for each core."),
OptionInfo('cbuild_run.systemview', tuple, None,
"SystemView configuration."),
]

## @brief The runtime dictionary of options.
Expand Down
4 changes: 4 additions & 0 deletions pyocd/core/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ def _get_cbuild_run_config(self, command: Optional[str]) -> Dict[str, Any]:
debugger_options['cbuild_run.telnet_file_in'] = telnet_file.get('in')
debugger_options['cbuild_run.telnet_file_out'] = telnet_file.get('out')

debugger_options['cbuild_run.rtt_control_block'] = self.cbuild_run.rtt_control_block
debugger_options['cbuild_run.rtt_channel'] = self.cbuild_run.rtt_channel
debugger_options['cbuild_run.systemview'] = self.cbuild_run.systemview

# Set reset types for load operations.
debugger_options['load.pre_reset'] = self.cbuild_run.pre_reset
debugger_options['load.post_reset'] = self.cbuild_run.post_reset
Expand Down
166 changes: 166 additions & 0 deletions pyocd/target/pack/cbuild_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def __init__(self, yml_path: str) -> None:
self._system_resources: Optional[Dict[str, list]] = None
self._system_descriptions: Optional[List[dict]] = None
self._required_packs: Dict[str, Optional[Path]] = {}
self._rtt_config_cache: Optional[Tuple] = None

try:
# Convert to Path object early and resolve to absolute path
Expand Down Expand Up @@ -692,6 +693,117 @@ def _resolve_path(file_path: Optional[str], strict: bool = False) -> Optional[st
return {'in': tuple(in_files) if any(in_files) else None,
'out': tuple(out_files) if any(out_files) else None}

@property
def rtt_config(self) -> Optional[Tuple]:
"""@brief Cached RTT configurations."""
if self._rtt_config_cache is None:
self._rtt_config_cache = self._get_rtt_config()
return self._rtt_config_cache

@property
def rtt_control_block(self) -> Optional[Tuple]:
"""@brief RTT control block configurations for each core."""
rtt_config_list = self.rtt_config
if rtt_config_list is None:
return None

control_block_list = []
for config in rtt_config_list:
config = config.get('control-block')
if config is not None:
control_block_list.append({
'address': config.get('address'),
'size': config.get('size'),
'auto-detect': config.get('auto-detect', False)
})
else:
control_block_list.append(None)

return tuple(control_block_list)

@property
def rtt_channel(self) -> Optional[Tuple]:
"""@brief RTT channel configurations for each core."""

SUPPORTED_MODES = { 'stdio', 'telnet', 'systemview'}

rtt_config_list = self.rtt_config
if rtt_config_list is None:
return None

channel_list = []
for idx, config in enumerate(rtt_config_list):
channels = config.get('channel', [])
valid_channel_list = []
for ch_cfg in channels:
ch_num = ch_cfg.get('number', None)
if ch_num is None:
# Warn about missing channel number
LOG.warning("RTT channel configuration for core %d is missing channel number; channel disabled", idx)
continue
if any(ch['number'] == ch_num for ch in valid_channel_list):
LOG.warning("RTT channel %d for core %d is already configured; skipping duplicate", ch_num, idx)
continue
ch_mode = ch_cfg.get('mode', None)
if ch_mode is None:
# Warn about missing channel mode
LOG.warning("RTT channel %d configuration for core %d is missing mode; channel disabled", ch_num, idx)
continue
if ch_mode not in SUPPORTED_MODES:
# Warn about unsupported channel mode
LOG.warning("RTT channel %d configuration for core %d has unsupported mode '%s'; channel disabled",
ch_num, idx, ch_mode)
continue
# STDIO mode
if ch_mode == 'stdio':
valid_channel_list.append({'number': ch_num, 'mode': ch_mode})
# Telnet mode
elif ch_mode == 'telnet':
port = ch_cfg.get('port', None)
if port is None:
LOG.warning("RTT telnet channel %d configuration for core %d is missing port configuration; channel disabled", ch_num, idx)
continue
else:
valid_channel_list.append({'number': ch_num, 'mode': ch_mode, 'port': port})
# SystemView mode
elif ch_mode == 'systemview':
valid_channel_list.append({'number': ch_num, 'mode': ch_mode})

# If no stdio channel was configured and channel 0 is not already taken,
# configure channel 0 as the stdio channel.
if not any(ch['mode'] == 'stdio' for ch in valid_channel_list) and \
not any(int(ch['number']) == 0 for ch in valid_channel_list):
valid_channel_list.append({'number': 0, 'mode': 'stdio'})

valid_channel_list.sort(key=lambda x: int(x['number']))
channel_list.append(valid_channel_list if valid_channel_list else None)

return tuple(channel_list)

@property
def systemview(self) -> Optional[Dict[str, Any]]:
"""@brief SystemView configurations for each core."""
systemview = self.debugger.get('systemview') or []

# Set default values
file = f"{self._cbuild_name}.SVDat"
auto_start = True
auto_stop = True

if systemview:
_file = systemview.get('file', file)
auto_start = systemview.get('auto-start', True)
auto_stop = systemview.get('auto-stop', True)
if _file is not None:
file = str(Path(os.path.expandvars(str(_file))).expanduser().resolve())

systemview_cfg ={
'file': file,
'auto-start': auto_start,
'auto-stop': auto_stop
}
return systemview_cfg

def populate_target(self, target: Optional[str] = None) -> None:
"""@brief Generates and populates the target defined by the .cbuild-run.yml file."""
if target is None:
Expand All @@ -716,6 +828,60 @@ def populate_target(self, target: Optional[str] = None) -> None:
})
TARGET[target] = tgt

def _get_rtt_config(self) -> Optional[Tuple]:
"""@brief RTT configuration from debugger section.

Returns a tuple of RTT configurations, one per core. If no RTT configuration
exists in the debugger section, returns None.
"""
rtt_config_data = self.debugger.get('rtt') or []
if not rtt_config_data:
return None

# Ensure the configuration is a list. If the user provided a single
# dictionary, wrap it in a list to handle it uniformly.
if isinstance(rtt_config_data, dict):
rtt_config_list = [rtt_config_data]
else:
rtt_config_list = rtt_config_data

# Check for a global configuration (no 'pname')
global_config = next((c for c in rtt_config_list if 'pname' not in c), None)

# Create a map of pname to its specific configuration
pname_map = {c['pname']: c for c in rtt_config_list if 'pname' in c}

sorted_processors = self.sorted_processors
is_multicore = len(sorted_processors) > 1
primary_core_index = self.primary_core if self.primary_core is not None else 0

# Warn the user if a global control-block is used on a multicore target
if is_multicore and global_config and ('control-block' in global_config):
LOG.warning("Global RTT 'control-block' configuration is only applied to the primary core for "
"multicore targets. Other global RTT settings are applied to all cores.")

rtt_configs = []
for i, proc_info in enumerate(sorted_processors):
core_config = {}

# Apply global settings
if global_config:
# Default global configuration
core_config.update(deepcopy(global_config))
if is_multicore and i != primary_core_index:
# Remove control-block for non-primary cores in multicore targets
core_config.pop('control-block', None)

# Override with core-specific settings
pname_config = pname_map.get(proc_info.name)
if pname_config:
core_config.update(deepcopy(pname_config))

# Add configuration to the list
rtt_configs.append(core_config)

return tuple(rtt_configs)

def _get_server_port(self, server_type: str) -> Optional[Tuple]:
"""@brief Generic method to get server port assignments from debugger section."""
server_config = self.debugger.get(server_type, [])
Expand Down
Loading