Karthik-Swaminathan98/mcu-function-size-analyser

GitHub: Karthik-Swaminathan98/mcu-function-size-analyser

Python工具,通过解析map文件和objdump反汇编构建调用图,精确计算MCU函数的真实代码尺寸(含全部依赖),支持ARM Cortex-M和RISC-V架构的嵌入式基准测试与架构对比分析。

Stars: 0 | Forks: 0

# mcu-function-size-analyser 在 **TU Chemnitz / Infineon Technologies** 攻读硕士学位期间开发(2025年)。 用于测量 20 多个 CMSIS-DSP/NN 和 NMSIS/Andes-DSP/NN 内核的代码大小。 **源码仓库:** - ARM 基准测试:[ARM-Project](https://github.com/Karthik-Swaminathan98/ARM-Project) - RISC-V 基准测试:[RISCV-Project](https://github.com/Karthik-Swaminathan98/RISCV-Project) - 完整结果:[arm-riscv-benchmark-results](https://github.com/Karthik-Swaminathan98/arm-riscv-benchmark-results) ![Language](https://img.shields.io/badge/language-Python%203-blue) ![Targets](https://img.shields.io/badge/targets-ARM%20%7C%20RISC--V-informational) ![Thesis](https://img.shields.io/badge/thesis-TU%20Chemnitz%20%2F%20Infineon%202025-green) ## 为什么依赖感知的代码大小很重要 嵌入式基准测试中一个常见的错误是,仅报告函数自身的编译后大小。 实际上,集成一个内核意味着要为其调用的每一个例程付出代价—— 辅助函数、数学工具、初始化代码。 例如,`arm_cfft_f32` 本身只有 **308 字节**,但包含所有依赖项在内的完整内存占用空间 是 **3308 字节**——大了超过 10 倍。 如果仅报告 308 字节,将会误导那些在严格的 Flash 限制下 在 CMSIS 和 NMSIS 之间进行选择的开发者。 这些脚本始终会同时报告这两个数值——自身大小以及包含完整依赖项细分在内的总大小。 ## 脚本 ### `function_size.py` — ARM Cortex-M (CMSIS) 解析 ARM `.map` 文件和 `objdump` 反汇编(使用 `bl`, `blx`, `b.w` 调用指令) 以构建调用图并计算函数总的内存占用。 ``` python function_size.py [function_name2 ...] ``` **示例:** ``` python function_size.py \ Release/arm_fft_benchmark.map \ Release/arm_fft_benchmark.objdump \ arm_cfft_f32 arm_cfft_q15 ``` **输出:** ``` [*] 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) 针对 RISC-V 目标进行相同的分析——从 RISC-V objdump 反汇编中解析 `jal`, `j`, `c.j`, `call` 指令 (支持 RVC 压缩编码)。 ``` python function_size_riscv.py [function_name2 ...] ``` **示例:** ``` python function_size_riscv.py \ output/map.txt \ output/objdump.txt \ riscv_dsp_cfft_f32 riscv_dsp_cfft_q15 ``` **输出:** ``` [*] 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 集成 — 自动化的构建后分析 这两个脚本都被设计为在项目 Makefile 中作为构建后步骤自动运行—— 每次构建后无需手动调用。 ### RISC-V (AndeSight / riscv-32-elf-gcc) 将此目标添加到您的项目 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 ``` 每次构建后,运行: ``` make function-analysis ``` 输出将保存到 `tools/code_size_report.txt`。 ### ARM (ModusToolbox / GCC ARM) 将此添加到您的 ModusToolbox `Makefile` 中作为 `POSTBUILD` 钩子—— 每次项目成功构建时自动运行: ``` 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 ``` ModusToolbox 中的 `POSTBUILD` 变量会在成功构建后执行—— objdump 会自动生成,分析随即运行, 并将结果保存到 `tools/code_size_report.txt`。 ## 工作原理 这两个脚本都遵循相同的 3 步流水线: ``` 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 ``` ### ARM 和 RISC-V 版本之间的主要区别 | | `function_size.py` (ARM) | `function_size_riscv.py` (RISC-V) | |---|---|---| | 解析的调用指令 | `bl`, `blx`, `b.w`, `b` | `jal`, `j`, `c.j`, `call` | | 压缩编码 | 否 (ARM Thumb) | 是 (RISC-V RVC) | | Map 节格式 | `.text.arm_*` | `.text.riscv_*` | ## 环境要求 ``` Python >= 3.8 ``` 无外部依赖项——仅使用 Python 标准库(`sys`, `re`, `os`, `collections`)。无需 `pip install`。 ## 仓库结构 ``` 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 ``` ## 添加到您自己的项目中 1. 将 `function_size.py` (ARM) 或 `function_size_riscv.py` (RISC-V) 复制到您项目中的 `tools/` 文件夹中 2. 添加上面所示的 Makefile 目标 3. 在 `FUNCTIONS` / `TARGET_FUNC` 中设置您的目标函数名称 4. 构建您的项目——报告会自动生成 ## 相关仓库 | 仓库 | 描述 | |---|---| | [ARM-Project](https://github.com/Karthik-Swaminathan98/ARM-Project) | ARM Cortex-M4 CMSIS-DSP/NN 基准测试——使用 `function_size.py` | | [RISCV-Project](https://github.com/Karthik-Swaminathan98/RISCV-Project) | RISC-V NMSIS-DSP/NN 基准测试——使用 `function_size_riscv.py` | | [arm-riscv-benchmark-results](https://github.com/Karthik-Swaminathan98/arm-riscv-benchmark-results) | 包含代码大小比较的完整跨架构结果 | ## 致谢 作为 **Technische Universität Chemnitz** 硕士学位论文的一部分,与德累斯顿的 **Infineon Technologies** 合作开发(2025年)。 ## 作者 **Karthik Swaminathan** — 嵌入式固件工程师 理学硕士 · 嵌入式系统 · TU Chemnitz [LinkedIn](https://linkedin.com/in/karthik-swaminathan98) · karthik94870@gmail.com
标签:ARM, Python, RISC-V, 代码体积分析, 嵌入式系统, 性能分析, 无后门