From 0ef8d589680b84e5cbb8b04cdfdd099794879118 Mon Sep 17 00:00:00 2001 From: Teo Mahnic Date: Fri, 29 May 2026 13:52:17 +0200 Subject: [PATCH 1/2] target: pack: register FLM fallback for flashinfo regions --- pyocd/target/pack/cbuild_run.py | 34 ++++++++++++++++++++++++++++++++- pyocd/target/pack/cmsis_pack.py | 33 ++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/pyocd/target/pack/cbuild_run.py b/pyocd/target/pack/cbuild_run.py index ea634549d..811b7d38d 100644 --- a/pyocd/target/pack/cbuild_run.py +++ b/pyocd/target/pack/cbuild_run.py @@ -921,6 +921,7 @@ def _add_flashinfo_regions( flash_start: int, flash_end: int, flash_info: Dict[str, Any], + fallback_flm: Optional[PackFlashAlgo] = None, ) -> None: page_size = flash_info.get('page-size') blocks = flash_info.get('blocks') @@ -967,6 +968,8 @@ def _add_flashinfo_regions( parent_attrs = {**flash_attrs, 'start': flash_start, 'length': flash_end - flash_start, 'sector_size': max(br[2] for br in block_ranges), 'page_size': page_size, **fi_attrs} + if fallback_flm is not None: + parent_attrs['_fallback_flm'] = fallback_flm parent_region = MEMORY_TYPE_CLASS_MAP[MemoryType.FLASH](**parent_attrs) for sub_start, sub_end, block_size, block_arg in block_ranges: @@ -1000,7 +1003,11 @@ def _add_flashinfo_regions( } if memory.get('defined', False): + fallback_algos = [] for flash in self.flashinfo + self.programming: + if any(flash is fallback_algo for fallback_algo in fallback_algos): + continue + if 'pname' in memory and 'pname' in flash: if memory['pname'] != flash['pname']: # Skip this algorithm if 'Pname' exists and does not match @@ -1046,7 +1053,32 @@ def _add_flashinfo_regions( # Create appropriate memory region object and store it regions.append(MEMORY_TYPE_CLASS_MAP[memory_type](**flash_attrs)) else: - _add_flashinfo_regions(flash_attrs, flash_start, flash_end, flash) + fallback_flm = None + for programming in self.programming: + if 'pname' in memory and 'pname' in programming and memory['pname'] != programming['pname']: + continue + + algo_size = programming.get('size') + if algo_size is None: + continue + + algo_end = programming['start'] + algo_size + if (flash_start >= algo_end) or (programming['start'] >= flash_end): + continue + + fallback_algos.append(programming) + try: + algorithm_path = self._check_path(Path(programming['algorithm']), required=False) + fallback_flm = PackFlashAlgo(str(algorithm_path)) + except (exceptions.Error, OSError, ValueError) as err: + LOG.debug("Could not load FLM fallback '%s': %s", programming.get('algorithm'), err) + else: + if 'ram-start' in programming: + flash_attrs['_RAMstart'] = programming['ram-start'] + if 'ram-size' in programming: + flash_attrs['_RAMsize'] = programming['ram-size'] + break + _add_flashinfo_regions(flash_attrs, flash_start, flash_end, flash, fallback_flm) # Stop searching for algorithms if one without pname was found if flash_attrs['pname'] is None: break diff --git a/pyocd/target/pack/cmsis_pack.py b/pyocd/target/pack/cmsis_pack.py index 5f47e16ff..dfe49dd59 100644 --- a/pyocd/target/pack/cmsis_pack.py +++ b/pyocd/target/pack/cmsis_pack.py @@ -799,6 +799,25 @@ def _build_memory_regions(self) -> None: algo_element = None # Convert the region to flash if we found a matching algorithm element. + # Check for flashinfo first — debug sequences take precedence over FLM when available. + fi_match = self._find_matching_flashinfo(attrs['start'], attrs['start'] + attrs['length']) + if fi_match is not None: + fi_elem_found, fi_start, fi_end = fi_match + if not self._saw_startup: + attrs['is_boot_memory'] = True + self._saw_startup = True + # If an FLM also covers this region, load it as fallback in case the + # required debug sequences are not available. + fallback_flm = None + if algo_element is not None: + fallback_flm = self._load_flash_algo(algo_element.attrib['name']) + if fallback_flm is not None: + attrs.update(self._get_flash_ram_attributes(algo_element)) + self._processed_algos.add(algo_element) + self._add_flashinfo_regions(attrs, fi_start, fi_end, fi_elem_found, fallback_flm) + continue + + # No flashinfo; use FLM if available. if (algo_element is not None) and self._set_flash_attributes(algo_element, attrs): # Mark this algo as processed. self._processed_algos.add(algo_element) @@ -811,16 +830,7 @@ def _build_memory_regions(self) -> None: attrs['is_boot_memory'] = True self._saw_startup = True - # If no algo, check for a matching flashinfo (the two should not cover the same area). - elif algo_element is None: - fi_match = self._find_matching_flashinfo(attrs['start'], attrs['start'] + attrs['length']) - if fi_match is not None: - fi_elem_found, fi_start, fi_end = fi_match - if not self._saw_startup: - attrs['is_boot_memory'] = True - self._saw_startup = True - self._add_flashinfo_regions(attrs, fi_start, fi_end, fi_elem_found) - continue + # No flashinfo and no algo — fall through to create a non-flash region. # Create the memory region and add to map. region = MEMORY_TYPE_CLASS_MAP[type](**attrs) @@ -969,6 +979,7 @@ def _add_flashinfo_regions( flash_start: int, flash_end: int, fi_elem: Element, + fallback_flm: Optional[PackFlashAlgo] = None, ) -> None: """@brief Create flash region(s) from a flashinfo XML element.""" try: @@ -1019,6 +1030,8 @@ def _add_flashinfo_regions( # Parent region spans the full flash range with the maximum sector size. parent_attrs = {**flash_attrs, 'start': flash_start, 'length': flash_end - flash_start, 'sector_size': max(br[2] for br in block_ranges), 'page_size': page_size, **fi_attrs} + if fallback_flm is not None: + parent_attrs['_fallback_flm'] = fallback_flm parent_region = MEMORY_TYPE_CLASS_MAP[MemoryType.FLASH](**parent_attrs) for sub_start, sub_end, block_size, block_arg in block_ranges: From a4f625ae123cd4c88dc3eff56fc2d61c31499730 Mon Sep 17 00:00:00 2001 From: Teo Mahnic Date: Fri, 5 Jun 2026 12:44:17 +0200 Subject: [PATCH 2/2] coresight: fall back to FLM when flash debug sequences are missing --- pyocd/coresight/coresight_target.py | 18 ++++++++++++++++++ pyocd/flash/flash_dsq.py | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/pyocd/coresight/coresight_target.py b/pyocd/coresight/coresight_target.py index 665025f4e..16d0173bb 100644 --- a/pyocd/coresight/coresight_target.py +++ b/pyocd/coresight/coresight_target.py @@ -23,6 +23,7 @@ from ..core.memory_map import (FlashRegion, MemoryType, RamRegion, DeviceRegion, MemoryMap) from ..core.soc_target import SoCTarget from ..core import exceptions +from ..flash.flash import Flash from . import (dap, discovery) from ..debug.svd.loader import SVDLoader from ..utility.sequencer import CallSequence @@ -302,6 +303,23 @@ def create_flash(self) -> None: # Set the region in the flash instance. obj.region = region + if not getattr(obj, 'has_required_sequences', True): + fallback_flm = region.attributes.get('_fallback_flm') + fallback_msg = " Falling back to FLM." if fallback_flm is not None else "" + LOG.warning("Flash programming debug sequences are not available for region '%s' (address %#010x);" + " check DFP debug description.%s", region.name, region.start, fallback_msg) + + # Fall back to FLM if required flash programming sequences are not available. + if fallback_flm is not None: + for subregion in list(region.submap.regions): + region.submap.remove_region(subregion) + region.flash_class = Flash + region.flm = fallback_flm # Also clears region.algo. + if not flm_builder.finalise_region(region): + continue + obj = region.flash_class(self, region.algo) + obj.region = region + # Store the flash object back into the memory region. region.flash = obj diff --git a/pyocd/flash/flash_dsq.py b/pyocd/flash/flash_dsq.py index eb7aff884..20aa649ec 100644 --- a/pyocd/flash/flash_dsq.py +++ b/pyocd/flash/flash_dsq.py @@ -49,6 +49,9 @@ class FlashDebugSequence(Flash): _SEQ_PROGRAM_PAGE = "FlashProgramPage" _SEQ_CODE_MEM_REMAP = "DebugCodeMemRemap" + ## Sequences required for basic flash programming operations. + _REQUIRED_SEQUENCES = (_SEQ_ERASE_SECTOR, _SEQ_PROGRAM_PAGE) + def __init__(self, target) -> None: super().__init__(target, flash_algo=None) self._delegate: Optional[DebugSequenceDelegate] = target.debug_sequence_delegate @@ -68,6 +71,11 @@ def region(self, flash_region: "FlashRegion") -> None: def is_erase_all_supported(self) -> bool: return self._has_sequence(self._SEQ_ERASE_CHIP) + @property + def has_required_sequences(self) -> bool: + return all(self._has_sequence(name) for name in self._REQUIRED_SEQUENCES) + + def init(self, operation, address: Optional[int] = None, clock: int = 0, reset: bool = False) -> None: # clock and reset arguments are ignored in this implementation