diff --git a/changelog.yaml b/changelog.yaml index 9114dadb5..39dbf3b6b 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -1269,6 +1269,9 @@ subsections: - title: Certificate Creation Tool scope: cert-create + - title: Memory Mapping Tool + scope: memmap + deprecated: - cert_create diff --git a/docs/tools/memory-layout-tool.rst b/docs/tools/memory-layout-tool.rst index ce14dab64..ff5188e4b 100644 --- a/docs/tools/memory-layout-tool.rst +++ b/docs/tools/memory-layout-tool.rst @@ -113,6 +113,40 @@ For more detailed help instructions, run: poetry run memory --help +Memory Footprint +~~~~~~~~~~~~~~~~ + +The tool enables users to view static memory consumption. When the options +``-f``, or ``--footprint`` are provided, the script analyses the ELF binaries in +the build path to generate a table (per memory type), showing memory allocation +and usage. This is the default output generated by the tool. + +.. code:: shell + + $ poetry run memory -f + build-path: build/fvp/release + +----------------------------------------------------------------------------+ + | Memory Usage (bytes) [RAM] | + +-----------+------------+------------+------------+------------+------------+ + | Component | Start | Limit | Size | Free | Total | + +-----------+------------+------------+------------+------------+------------+ + | BL1 | 4034000 | 4040000 | 7000 | 5000 | c000 | + | BL2 | 4021000 | 4034000 | d000 | 6000 | 13000 | + | BL2U | 4021000 | 4034000 | a000 | 9000 | 13000 | + | BL31 | 4003000 | 4040000 | 1e000 | 1f000 | 3d000 | + +-----------+------------+------------+------------+------------+------------+ + + +----------------------------------------------------------------------------+ + | Memory Usage (bytes) [ROM] | + +-----------+------------+------------+------------+------------+------------+ + | Component | Start | Limit | Size | Free | Total | + +-----------+------------+------------+------------+------------+------------+ + | BL1 | 0 | 4000000 | 5df0 | 3ffa210 | 4000000 | + +-----------+------------+------------+------------+------------+------------+ + +The script relies on symbols in the symbol table to determine the start, end, +and limit addresses of each bootloader stage. + -------------- *Copyright (c) 2023, Arm Limited. All rights reserved.* diff --git a/tools/memory/memory/buildparser.py b/tools/memory/memory/buildparser.py index 6f467cd6f..0e3beaaa5 100755 --- a/tools/memory/memory/buildparser.py +++ b/tools/memory/memory/buildparser.py @@ -50,6 +50,15 @@ class TfaBuildParser: reverse=True, ) + def get_mem_usage_dict(self) -> dict: + """Returns map of memory usage per memory type for each module.""" + mem_map = {} + for k, v in self._modules.items(): + mod_mem_map = v.get_elf_memory_layout() + if len(mod_mem_map): + mem_map[k] = mod_mem_map + return mem_map + @property def module_names(self): """Returns sorted list of module names.""" diff --git a/tools/memory/memory/elfparser.py b/tools/memory/memory/elfparser.py index 3964e6cb2..b61f32859 100644 --- a/tools/memory/memory/elfparser.py +++ b/tools/memory/memory/elfparser.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: BSD-3-Clause # +import re from typing import BinaryIO from elftools.elf.elffile import ELFFile @@ -28,6 +29,46 @@ class TfaElfParser: for sym in elf.get_section_by_name(".symtab").iter_symbols() } + self._memory_layout = self.get_memory_layout_from_symbols() + @property def symbols(self): return self._symbols.items() + + def get_memory_layout_from_symbols(self, expr=None) -> dict: + """Retrieve information about the memory configuration from the symbol + table. + """ + assert len(self._symbols), "Symbol table is empty!" + + expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)" if not expr else expr + region_symbols = filter(lambda s: re.match(expr, s), self._symbols) + memory_layout = {} + + for symbol in region_symbols: + region, _, attr = tuple(symbol.lower().strip("__").split("_")) + if region not in memory_layout: + memory_layout[region] = {} + + # Retrieve the value of the symbol using the symbol as the key. + memory_layout[region][attr] = self._symbols[symbol] + + return memory_layout + + def get_elf_memory_layout(self): + """Get the total memory consumed by this module from the memory + configuration. + {"rom": {"start": 0x0, "end": 0xFF, "length": ... } + """ + mem_dict = {} + + for mem, attrs in self._memory_layout.items(): + limit = attrs["start"] + attrs["length"] + mem_dict[mem] = { + "start": attrs["start"], + "limit": limit, + "size": attrs["end"] - attrs["start"], + "free": limit - attrs["end"], + "total": attrs["length"], + } + return mem_dict diff --git a/tools/memory/memory/memmap.py b/tools/memory/memory/memmap.py index 705722870..e6b66f9c2 100755 --- a/tools/memory/memory/memmap.py +++ b/tools/memory/memory/memmap.py @@ -35,12 +35,17 @@ from memory.printer import TfaPrettyPrinter help="The target build type.", type=click.Choice(["debug", "release"], case_sensitive=False), ) +@click.option( + "-f", + "--footprint", + is_flag=True, + show_default=True, + help="Generate a high level view of memory usage by memory types.", +) @click.option( "-s", "--symbols", is_flag=True, - show_default=True, - default=True, help="Generate a map of important TF symbols.", ) @click.option("-w", "--width", type=int, envvar="COLUMNS") @@ -54,6 +59,7 @@ def main( root: Path, platform: str, build_type: str, + footprint: bool, symbols: bool, width: int, d: bool, @@ -64,6 +70,9 @@ def main( parser = TfaBuildParser(build_path) printer = TfaPrettyPrinter(columns=width, as_decimal=d) + if footprint or not symbols: + printer.print_footprint(parser.get_mem_usage_dict()) + if symbols: expr = ( r"(.*)(TEXT|BSS|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF" diff --git a/tools/memory/memory/printer.py b/tools/memory/memory/printer.py index 11fd7f021..c6019c211 100755 --- a/tools/memory/memory/printer.py +++ b/tools/memory/memory/printer.py @@ -4,6 +4,8 @@ # SPDX-License-Identifier: BSD-3-Clause # +from prettytable import PrettyTable + class TfaPrettyPrinter: """A class for printing the memory layout of ELF files. @@ -15,6 +17,7 @@ class TfaPrettyPrinter: def __init__(self, columns: int = None, as_decimal: bool = False): self.term_size = columns if columns and columns > 120 else 120 + self._footprint = None self._symbol_map = None self.as_decimal = as_decimal @@ -50,6 +53,37 @@ class TfaPrettyPrinter: return leading + sec_row_l + sec_row + sec_row_r + def print_footprint( + self, app_mem_usage: dict, sort_key: str = None, fields: list = None + ): + assert len(app_mem_usage), "Empty memory layout dictionary!" + if not fields: + fields = ["Component", "Start", "Limit", "Size", "Free", "Total"] + + sort_key = fields[0] if not sort_key else sort_key + + # Iterate through all the memory types, create a table for each + # type, rows represent a single module. + for mem in sorted(set(k for _, v in app_mem_usage.items() for k in v)): + table = PrettyTable( + sortby=sort_key, + title=f"Memory Usage (bytes) [{mem.upper()}]", + field_names=fields, + ) + + for mod, vals in app_mem_usage.items(): + if mem in vals.keys(): + val = vals[mem] + table.add_row( + [ + mod.upper(), + *self.format_args( + *[val[k.lower()] for k in fields[1:]] + ), + ] + ) + print(table, "\n") + def print_symbol_table( self, symbols: list,