diff --git a/pyocd/target/pack/flm_region_builder.py b/pyocd/target/pack/flm_region_builder.py index 11fb3b30c..bf37c0801 100644 --- a/pyocd/target/pack/flm_region_builder.py +++ b/pyocd/target/pack/flm_region_builder.py @@ -1,6 +1,7 @@ # pyOCD debugger # Copyright (c) 2022-2023 Chris Reed # Copyright (c) 2025 Arm Limited +# Copyright (c) 2026 Evgeny Korolev # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -127,6 +128,17 @@ def _select_flash_ram(self, region: FlashRegion) -> RamRegion: # so just use a mid-range arbitrary size. ram_size = 16 * 1024 + # Clamp the algo RAM to the containing RAM region, in case a DFP declares an + # RAMsize larger than the device's actual RAM. + containing_ram = self._memory_map.get_region_for_address(ram_start) + if (containing_ram is not None) and (containing_ram.type is MemoryType.RAM): + available = containing_ram.length - (ram_start - containing_ram.start) + if ram_size > available: + LOG.warning("Flash algo for region '%s' requests %#x bytes of RAM at %#010x, " + "but containing region '%s' only provides %#x; clamping to fit.", + region.name, ram_size, ram_start, containing_ram.name, available) + ram_size = available + ram_for_algo = RamRegion(start=ram_start, length=ram_size) else: # No RAM addresses were given, so go with the RAM marked default. diff --git a/test/unit/test_pack.py b/test/unit/test_pack.py index d80525af6..f636aff87 100644 --- a/test/unit/test_pack.py +++ b/test/unit/test_pack.py @@ -1,6 +1,7 @@ # pyOCD debugger # Copyright (c) 2019-2020 Arm Limited # Copyright (c) 2021-2023 Chris Reed +# Copyright (c) 2026 Evgeny Korolev # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -356,6 +357,20 @@ def test_ram_select_explicit(self, builder: FlmFlashRegionBuilder, nrf5340appflm assert not flash.has_subregions assert flash.algo + def test_ram_select_explicit_clamped_to_region(self, builder: FlmFlashRegionBuilder, nrf5340appflm): + # _RAMsize (0x18000) exceeds the containing RAM region (0x20000000, length 0x10000), as + # happens with e.g. SiliconLabs' GeckoPlatform_EFR32MG21_DFP. The algo RAM must be clamped + # to the region so the loader (laid out from the top down) isn't placed past physical RAM. + flash = memory_map.FlashRegion(0, length=0x200000, flm=nrf5340appflm, + _RAMstart=0x20000000, _RAMsize=0x18000) + assert builder.finalise_region(flash) + assert flash.algo + instr_len = len(flash.algo['instructions']) * 4 + # Clamped to the region end (0x20010000), not the oversized 0x20018000. + assert flash.algo['load_address'] == (0x20010000 - instr_len) + assert not flash.has_subregions + assert flash.algo + def has_overlapping_regions(memmap): return any((len(memmap.get_intersecting_regions(r.start, r.end)) > 1) for r in memmap.regions)