Skip to content

Karthik-Swaminathan98/mcu-function-size-analyser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

mcu-function-size-analyser

Python tooling for dependency-aware function code size analysis in embedded firmware — parses .map files and objdump disassembly to compute the true binary footprint of any function including all transitive dependencies, for both ARM and RISC-V targets.

Developed during Master's thesis at TU Chemnitz / Infineon Technologies (2025).
Used to measure code size across 20+ CMSIS-DSP/NN and NMSIS/Andes-DSP/NN kernels.

Source repos:

Language Targets Thesis


Why dependency-aware code size matters

A common mistake in embedded benchmarking is reporting only a function's own compiled size. In practice, integrating a kernel means paying for every routine it calls — helper functions, math utilities, initialisation code.

For example, arm_cfft_f32 itself is only 308 bytes but its full footprint including all dependencies is 3308 bytes — more than 10× larger. Reporting only 308 bytes would mislead any developer choosing between CMSIS and NMSIS under tight Flash constraints.

These scripts always report both figures — own size and total size with full dependency breakdown.


Scripts

function_size.py — ARM Cortex-M (CMSIS)

Parses ARM .map files and objdump disassembly (using bl, blx, b.w call instructions) to build a call graph and compute total function footprint.

python function_size.py <map_file> <objdump_file> <function_name> [function_name2 ...]

Example:

python function_size.py \
  Release/arm_fft_benchmark.map \
  Release/arm_fft_benchmark.objdump \
  arm_cfft_f32 arm_cfft_q15

Output:

[*] Analyzing file: Release/arm_fft_benchmark.map

[*] Function: arm_cfft_f32
    Own size: 308 bytes
    Total size (with dependencies): 3308 bytes
    [*] Breakdown:
        arm_radix8_butterfly_f32           1404 bytes
        arm_cfft_radix8by4_f32             1176 bytes
        arm_cfft_radix8by2_f32              420 bytes
        arm_cfft_f32                        308 bytes
------------------------------------------------------------

function_size_riscv.py — RISC-V Andes D25F (NMSIS / Andes)

Same analysis for RISC-V targets — parses jal, j, c.j, call instructions from RISC-V objdump disassembly (RVC compressed encoding supported).

python function_size_riscv.py <map_file> <objdump_file> <function_name> [function_name2 ...]

Example:

python function_size_riscv.py \
  output/map.txt \
  output/objdump.txt \
  riscv_dsp_cfft_f32 riscv_dsp_cfft_q15

Output:

[*] Analyzing file: output/map.txt

[*] Function: riscv_dsp_cfft_f32
    Own size: 272 bytes
    Total size (with dependencies): 2184 bytes
    [*] Breakdown:
        riscv_dsp_cfft_rd4_f32             1052 bytes
        riscv_dsp_cfft_rd2_f32              624 bytes
        riscv_dsp_cfft_f32                  272 bytes
        riscv_dsp_bitreversal               236 bytes
------------------------------------------------------------

Makefile integration — automated post-build analysis

Both scripts are designed to run automatically as post-build steps inside the project Makefile — no manual invocation needed after each build.

RISC-V (AndeSight / riscv-32-elf-gcc)

Add this target to your project Makefile:

.PHONY: function-analysis

FUNCTIONS := riscv_dsp_cfft_f32 riscv_dsp_cfft_q15 \
             riscv_dsp_cifft_f32 riscv_dsp_cifft_q15

function-analysis: output/objdump.txt output/map.txt
	@echo "Running function size analysis..."
	python tools/function_size_riscv.py \
	  output/map.txt \
	  output/objdump.txt \
	  $(FUNCTIONS) > tools/code_size_report.txt || true

After each build, run:

make function-analysis

Output is saved to tools/code_size_report.txt.


ARM (ModusToolbox / GCC ARM)

Add this to your ModusToolbox Makefile as a POSTBUILD hook — runs automatically every time the project builds successfully:

TARGET_FUNC := arm_cfft_f32 arm_cfft_q15 arm_fir_f32

POSTBUILD=\
$(MTB_TOOLCHAIN_GCC_ARM__BASE_DIR)/bin/arm-none-eabi-objdump -S \
$(MTB_TOOLS__OUTPUT_CONFIG_DIR)/$(APPNAME).$(MTB_TOOLCHAIN_GCC_ARM__SUFFIX_TARGET) \
> $(MTB_TOOLS__OUTPUT_CONFIG_DIR)/$(APPNAME).objdump && \
python tools/function_size.py \
$(MTB_TOOLS__OUTPUT_CONFIG_DIR)/$(APPNAME).map \
$(MTB_TOOLS__OUTPUT_CONFIG_DIR)/$(APPNAME).objdump \
$(TARGET_FUNC) > tools/code_size_report.txt

The POSTBUILD variable in ModusToolbox executes after a successful build — objdump is generated automatically and the analysis runs immediately, saving results to tools/code_size_report.txt.


How it works

Both scripts follow the same 3-step pipeline:

1. Parse .map file
   └─► Extract function names + sizes from .text.* sections

2. Parse objdump disassembly
   └─► Build call graph: which functions call which
       ARM:    bl / blx / b.w instructions
       RISC-V: jal / j / c.j / call instructions

3. BFS traversal from root function
   └─► Visit all reachable callees
   └─► Sum sizes of all unique visited functions
   └─► Report: own size + total size + breakdown

Key difference between ARM and RISC-V versions

function_size.py (ARM) function_size_riscv.py (RISC-V)
Call instructions parsed bl, blx, b.w, b jal, j, c.j, call
Compressed encoding No (ARM Thumb) Yes (RISC-V RVC)
Map section format .text.arm_* .text.riscv_*

Requirements

Python >= 3.8

No external dependencies — uses only Python standard library (sys, re, os, collections). No pip install needed.


Repository structure

mcu-function-size-analyser/
├── function_size.py           # ARM Cortex-M code size analyser
├── function_size_riscv.py     # RISC-V Andes D25F code size analyser
└── README.md

Adding to your own project

  1. Copy function_size.py (ARM) or function_size_riscv.py (RISC-V) into a tools/ folder in your project
  2. Add the Makefile target shown above
  3. Set your target function names in FUNCTIONS / TARGET_FUNC
  4. Build your project — the report generates automatically

Related repos

Repo Description
ARM-Project ARM Cortex-M4 CMSIS-DSP/NN benchmarks — uses function_size.py
RISCV-Project RISC-V NMSIS-DSP/NN benchmarks — uses function_size_riscv.py
arm-riscv-benchmark-results Full cross-architecture results including code size comparisons

Acknowledgements

Developed as part of a Master's thesis at Technische Universität Chemnitz in collaboration with Infineon Technologies, Dresden (2025).


Author

Karthik Swaminathan — Embedded Firmware Engineer
M.Sc. Embedded Systems · TU Chemnitz
LinkedIn · karthik94870@gmail.com

About

Python tooling for embedded benchmark analysis — objdump call-graph parser, .map code-size extractor, ARM vs RISC-V visualisation

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages