1343 字
7 分钟
stc8g makefile和用量分析脚本

makefile#

#!/usr/bin/make -f
# Makefile for SDCC project with automatic library discovery
CC = sdcc
CFLAGS = --model-medium --opt-code-size
# 定义库名称(只需要在这里添加)
LIBRARIES = User System
# 单片机型号
MCU = STC8G1K08A
# 串口设备
SERIAL_PORT = /dev/ttyUSB0
# 生成包含目录路径
INCLUDES = $(foreach lib, $(LIBRARIES), -I $(lib)/Inc)
# 查找所有源文件
SRCS = $(foreach lib, $(LIBRARIES), $(wildcard $(lib)/Src/*.c))
# 获取所有.c文件的基本名称(用于检查重名)
BASENAMES = $(foreach src, $(SRCS), $(notdir $(src)))
# 检查是否有重复的源文件名
DUPLICATES = $(filter $(words $(BASENAMES)),$(words $(sort $(BASENAMES))))
ifneq ($(DUPLICATES),$(words $(BASENAMES)))
$(warning 警告: 发现重复的源文件名!)
$(warning 重复文件: $(foreach base,$(sort $(foreach b,$(BASENAMES),$(if $(filter 2,$(words $(filter $(b),$(BASENAMES)))),$(b)))),$(base)))
endif
# 生成对象文件列表(保持目录结构)
OBJS = $(patsubst %.c,%.rel,$(SRCS))
# 项目名称
TARGET = pwm
# 构建目录
BUILD_DIR = build
# 将对象文件放在build目录中
OBJS_BUILD = $(patsubst %.rel,$(BUILD_DIR)/%.rel,$(OBJS))
# 默认目标:构建并生成报告
all: check_libs $(BUILD_DIR) $(BUILD_DIR)/report
@echo "构建完成: $(BUILD_DIR)/$(TARGET).hex"
# 检查库目录是否存在
check_libs:
@for lib in $(LIBRARIES); do \
if [ ! -d "$$lib/Inc" ] || [ ! -d "$$lib/Src" ]; then \
echo "错误: 库 $$lib 结构不正确!需要 $$lib/Inc 和 $$lib/Src 目录"; \
exit 1; \
fi; \
done
# 创建构建目录
$(BUILD_DIR):
@mkdir -p $(BUILD_DIR)
# 编译规则:所有.c文件 -> build/目录下的.rel文件
$(BUILD_DIR)/%.rel: %.c
@echo "编译: $<"
@mkdir -p $(dir $@)
@$(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ 2>&1 | head -20
# 链接
$(BUILD_DIR)/$(TARGET).ihx: $(OBJS_BUILD)
@echo "链接: $(words $(OBJS_BUILD)) 个对象文件..."
@cd $(BUILD_DIR) && $(CC) $(CFLAGS) $(patsubst $(BUILD_DIR)/%,%,$(OBJS_BUILD)) -o $(TARGET).ihx
# 生成HEX
$(BUILD_DIR)/$(TARGET).hex: $(BUILD_DIR)/$(TARGET).ihx
@echo "生成HEX文件..."
@cd $(BUILD_DIR) && packihx $(TARGET).ihx > $(TARGET).hex
# report: 调用外部脚本解析 map 并打印报告
$(BUILD_DIR)/report: $(BUILD_DIR)/$(TARGET).hex
@echo "生成资源使用报告..."
@sh scripts/map_report.sh $(BUILD_DIR)/$(TARGET).map || true
# 下载到单片机
flash: $(BUILD_DIR)/$(TARGET).hex
@echo "下载到 $(MCU) via $(SERIAL_PORT)..."
stcgal -p $(SERIAL_PORT) -t 22168 -o program_eeprom_split=12288 -a $(BUILD_DIR)/$(TARGET).hex
# 编译但不链接(调试用)
compile: $(OBJS_BUILD)
@echo "编译完成,生成 $(words $(OBJS_BUILD)) 个.rel文件"
# 显示项目信息
info:
@echo "========================================"
@echo "项目: $(TARGET)"
@echo "编译器: $(CC)"
@echo "库列表: $(LIBRARIES)"
@echo "包含目录: $(INCLUDES)"
@echo "源文件 ($(words $(SRCS)) 个):"
@for src in $(sort $(SRCS)); do echo " $$src"; done
@echo "对象文件 ($(words $(OBJS_BUILD)) 个):"
@for obj in $(sort $(OBJS_BUILD)); do echo " $$obj"; done
@echo "构建目录: $(BUILD_DIR)"
@echo "========================================"
# 清理
clean:
@echo "清理构建文件..."
@rm -rf $(BUILD_DIR)
@rm -f *.ihx *.hex *.rel *.lk *.map *.mem *.rst *.asm *.lst *.sym *.cdb
# 帮助信息
help:
@echo "可用命令:"
@echo " make - 构建整个项目"
@echo " make clean - 清理所有生成文件"
@echo " make flash - 下载到单片机"
@echo " make info - 显示项目信息"
@echo " make compile - 只编译不链接"
@echo " make help - 显示此帮助信息"
.PHONY: all clean flash info help check_libs compile tidy

资源分析脚本#

#!/bin/sh
# Enhanced map report for SDCC projects
# Usage: map_report.sh <map-file> [ROM_TOTAL_BYTES] [RAM_TOTAL_BYTES]
# If ROM_TOTAL_BYTES / RAM_TOTAL_BYTES are not provided, script tries to
# - read a companion .mem file (same base name) for ROM total
# - otherwise falls back to defaults: ROM=8k+4k (12288), RAM=1k+256 (1280)
MAPFILE="$1"
ROM_OVERRIDE="$2"
RAM_OVERRIDE="$3"
if [ -z "$MAPFILE" ] || [ ! -f "$MAPFILE" ]; then
echo "map 报告: 未提供 map 文件 或 文件不存在: $MAPFILE"
exit 0
fi
hex2dec() {
[ -z "$1" ] && echo 0 && return
# accept 0x... or bare hex (without 0x)
case "$1" in
0x*|0X*) printf "%d" "$1" ;;
*) printf "%d" "0x$1" ;;
esac
}
# Try to extract code/const/idata/pdata/xdata sizes from map file.
# We look for sections by name and take the "Size" column where possible.
CSEG_START_HEX=$(awk '/^[[:space:]]*CSEG[[:space:]]+[0-9A-Fa-f]/ {print $2; exit}' "$MAPFILE" | sed 's/0x//g')
CSEG_HEX=$(awk '/^[[:space:]]*CSEG[[:space:]]+[0-9A-Fa-f]/ {print $3; exit}' "$MAPFILE" | sed 's/0x//g')
CONST_HEX=$(awk '/^[[:space:]]*CONST[[:space:]]+[0-9A-Fa-f]/ {print $3; exit}' "$MAPFILE" | sed 's/0x//g')
DSEG_HEX=$(awk '/^[[:space:]]*DSEG[[:space:]]+[0-9A-Fa-f]/ {print $3; exit}' "$MAPFILE" | sed 's/0x//g')
PSEG_HEX=$(awk '/^[[:space:]]*PSEG[[:space:]]+[0-9A-Fa-f]/ {print $3; exit}' "$MAPFILE" | sed 's/0x//g')
XSEG_HEX=$(awk '/^[[:space:]]*XSEG[[:space:]]+[0-9A-Fa-f]/ {print $3; exit}' "$MAPFILE" | sed 's/0x//g')
CSEG=$(hex2dec "$CSEG_HEX")
CONST=$(hex2dec "$CONST_HEX")
DSEG=$(hex2dec "$DSEG_HEX")
PSEG=$(hex2dec "$PSEG_HEX")
XSEG=$(hex2dec "$XSEG_HEX")
USED_ROM=$((CSEG + CONST))
USED_RAM=$((DSEG + PSEG + XSEG))
# compute CSEG end for module delta calc
CSEG_START=$(hex2dec "$CSEG_START_HEX")
CSEG_END=$((CSEG_START + CSEG))
# Determine total ROM (flash) size: prefer override arg, then companion .mem file, then default
DEFAULT_ROM=$((8*1024 + 4*1024))
DEFAULT_RAM=$((1*1024 + 256))
ROM_TOTAL=""
if [ -n "$ROM_OVERRIDE" ]; then
ROM_TOTAL="$ROM_OVERRIDE"
else
MEMFILE="${MAPFILE%.map}.mem"
if [ -f "$MEMFILE" ]; then
# Look for line like: ROM/EPROM/FLASH 0x0000 0x1e73 7796 65536
ROM_TOTAL=$(awk '/ROM\/EPROM\/FLASH/ {print $(NF-1); exit}' "$MEMFILE")
fi
fi
if [ -z "$ROM_TOTAL" ]; then
ROM_TOTAL=$DEFAULT_ROM
fi
RAM_TOTAL=""
if [ -n "$RAM_OVERRIDE" ]; then
RAM_TOTAL="$RAM_OVERRIDE"
else
# try to read some RAM hints from .mem (not always present)
MEMFILE="${MAPFILE%.map}.mem"
if [ -f "$MEMFILE" ]; then
# try to find a line like: PAGED EXT. RAM 0x0001 0x00ba 186 256
RAM_TOTAL=$(awk '/PAGED EXT. RAM|EXTERNAL RAM|Internal RAM layout:/ {line=NR} END{ if (line) { for(i=1;i<=NR;i++){} } }' "$MEMFILE")
# above attempt is conservative; we won't rely on it — fallback to default if empty
RAM_TOTAL=""
fi
fi
if [ -z "$RAM_TOTAL" ]; then
RAM_TOTAL=$DEFAULT_RAM
fi
# Print summary with percentages
percent() {
# percent <used> <total>
awk "BEGIN{ if ($2==0) printf \"N/A\"; else printf \"%.1f\", ($1/$2)*100 }"
}
echo "Map 报告: $MAPFILE"
echo "----------------------------------------"
echo "ROM 总量: $ROM_TOTAL bytes"
echo "ROM 已用: $USED_ROM bytes (code=$CSEG const=$CONST)"
echo "ROM 使用率: $(percent $USED_ROM $ROM_TOTAL)%"
echo ""
echo "RAM 总量: $RAM_TOTAL bytes"
echo "RAM 已用: $USED_RAM bytes (idata=$DSEG pdata=$PSEG xdata=$XSEG)"
echo "RAM 使用率: $(percent $USED_RAM $RAM_TOTAL)%"
echo "----------------------------------------"
# Top modules by C: entries (module name after C:)
# We'll convert the hex sizes to decimal using shell helper then aggregate with awk.
TMPFILE="/tmp/map_report.$$"
rm -f "$TMPFILE"
# Build a sorted list of symbol addresses and their module
awk '/^ *C:/ { addr=$2; sym=$3; mod=$NF; if (addr!="" && sym!="") print addr, sym, mod }' "$MAPFILE" | sed 's/\.rel//; s/\.o//; s/\.obj//' | \
while read HEX SYM MOD; do
[ -z "$HEX" -o -z "$SYM" ] && continue
DEC=$(hex2dec "$HEX")
printf "%d %s %s\n" "$DEC" "$SYM" "$MOD" >> "$TMPFILE"
done
if [ -f "$TMPFILE" ]; then
SORTED="/tmp/map_report_sorted.$$"
sort -n "$TMPFILE" > "$SORTED"
# compute deltas between consecutive symbols and attribute delta to the symbol (function)
# SORTED format: <addr> <symbol> <module>
SYMBOL_SIZES="/tmp/map_report_sym_sizes.$$"
awk -v cseg_end="$CSEG_END" '
{ addr[NR]=$1; sym[NR]=$2; mod[NR]=$3 }
END {
for(i=1;i<=NR;i++) {
if (i<NR) delta = addr[i+1]-addr[i]; else delta = cseg_end - addr[i];
if (delta<0) delta=0;
printf "%d %s %s\n", delta, sym[i], mod[i];
}
}' "$SORTED" > "$SYMBOL_SIZES"
echo "Top 20 函数(按占用字节):"
# Print header with tabs, then nicely aligned rows: Rank, Bytes, Function, Module
printf "%-4s\t%8s\t%-30s\t%s\n" "#" "Bytes" "Function" "Module"
sort -nr "$SYMBOL_SIZES" | head -n 20 | awk 'BEGIN{rank=0} {rank++; printf "%-4d\t%8d\t%-30s\t%s\n", rank, $1, $2, $3}'
rm -f "$SYMBOL_SIZES"
rm -f "$TMPFILE" "$SORTED"
fi
exit 0
stc8g makefile和用量分析脚本
https://www.mintlab.top/posts/stc8g编译脚本/stc8g_makefile/
作者
Mint
发布于
2026-01-03
许可协议
CC BY-NC-SA 4.0