From e34119fdba72f826a3cbec126197c063b4c7fd47 Mon Sep 17 00:00:00 2001 From: Tyler Potyondy Date: Thu, 15 Jan 2026 11:40:03 -0800 Subject: [PATCH 1/2] app_tab: use subregions for cortex-m apps >64kB This updates the logic when inspecting the tab of cortex-m apps to use mpu subregions when flashing apps larger than 64kB. This helps prevent wasted space vs rounding to the nearest power of two. --- tockloader/app_tab.py | 33 +++++++++++++++++++++++++++++---- tockloader/tockloader.py | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/tockloader/app_tab.py b/tockloader/app_tab.py index b110dbd..f4ed5c2 100644 --- a/tockloader/app_tab.py +++ b/tockloader/app_tab.py @@ -1,4 +1,5 @@ import logging +import math import struct import textwrap @@ -166,16 +167,40 @@ def set_size_constraint(self, constraint): # Make sure the total app size is a power of two. for tbf in self._get_tbfs(): current_size = tbf.tbfh.get_app_size() + original_app_size = current_size if (current_size & (current_size - 1)) != 0: # This is not a power of two, but should be. count = 0 while current_size != 0: current_size >>= 1 count += 1 - tbf.tbfh.set_app_size(1 << count) - logging.debug( - "Rounding app up to ^2 size ({} bytes)".format(1 << count) - ) + + # Use 64kB as a heuristic for packing. Use the next power + # of two for sizes less than 64kB. Otherwise we use the + # the following to reduce wasted space: + # (X - 1) * (NextPowerOfTwo / 8) < size < X * (NextPowerOfTwo) + if original_app_size < (1024 * 64): + logging.debug( + "Rounding app up to ^2 size ({} bytes)".format(1 << count) + ) + tbf.tbfh.set_app_size(1 << count) + else: + next_power2 = 1 << count + # Arm cortex-m v7 has 8 subregions for each MPU region + # of equal size, so we can enable X subregions of size + # 2^N / 8. + multiple = next_power2 / 8 + number_of_unmasked_subregions = math.ceil( + original_app_size / multiple + ) + new_size = int(number_of_unmasked_subregions * multiple) + logging.debug( + "Rounding app up to (X - 1) * (NextPowerOfTwo / 8) \ + < size < X * (NextPowerOfTwo) ({} bytes)".format( + new_size + ) + ) + tbf.tbfh.set_app_size(new_size) elif type(constraint) is tuple: if constraint[0] == "multiple": diff --git a/tockloader/tockloader.py b/tockloader/tockloader.py index 18a3867..e3b6834 100644 --- a/tockloader/tockloader.py +++ b/tockloader/tockloader.py @@ -1560,7 +1560,7 @@ def is_valid(slices): # We use the order the apps were given to us. pass elif self.app_settings["order"] == "size_descending": - apps.sort(key=lambda app: app.get_size(), reverse=True) + apps.sort(key=lambda app: app.get_size()) elif self.app_settings["order"] == None: # Any order is fine. pass From 8eb9aa489d70564f9f38aea9c6d61f5d1c7ad642 Mon Sep 17 00:00:00 2001 From: Tyler Potyondy Date: Thu, 15 Jan 2026 13:13:23 -0800 Subject: [PATCH 2/2] Ascending app flash order for cortex-m Replaces default cortex-m policy to be ascending instead of descending. --- tockloader/tockloader.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tockloader/tockloader.py b/tockloader/tockloader.py index e3b6834..60a7876 100644 --- a/tockloader/tockloader.py +++ b/tockloader/tockloader.py @@ -76,7 +76,7 @@ class TockLoader: # - `start_address`: The absolute address in flash where apps start and # must be loaded. # - `order`: How apps should be sorted when flashed onto the board. - # Supported values: size_descending, None + # Supported values: size_descending, size_ascending, None # - `size_constraint`: Valid sizes for the entire application. # Supported values: powers_of_two, (multiple, value), # None @@ -94,7 +94,7 @@ class TockLoader: }, "arch": { "cortex-m": { - "order": "size_descending", + "order": "size_ascending", "size_constraint": "powers_of_two", "alignment_constraint": "size", } @@ -1560,6 +1560,8 @@ def is_valid(slices): # We use the order the apps were given to us. pass elif self.app_settings["order"] == "size_descending": + apps.sort(key=lambda app: app.get_size(), reverse=True) + elif self.app_settings["order"] == "size_ascending": apps.sort(key=lambda app: app.get_size()) elif self.app_settings["order"] == None: # Any order is fine.