diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 6b86bd70396..c81280e11ab 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -299,7 +299,7 @@ $(BUILD)/firmware.elf: $(OBJ) $(STEPECHO) "LINK $@" $(Q)echo $^ > $(BUILD)/firmware.objs $(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group - $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LINKERFILE) $(BUILD) + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) $(BUILD)/firmware.hex: $(BUILD)/firmware.elf $(STEPECHO) "Create $@" diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 0aaf6e3d849..ad4e65b6ddb 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -348,7 +348,7 @@ $(BUILD)/firmware.elf: $(OBJ) $(GENERATED_LD_FILE) $(STEPECHO) "LINK $@" $(Q)echo $(OBJ) > $(BUILD)/firmware.objs $(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group - $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(GENERATED_LD_FILE) $(BUILD) + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) endif $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/litex/Makefile b/ports/litex/Makefile index cc813a49f8e..9792ce301a3 100644 --- a/ports/litex/Makefile +++ b/ports/litex/Makefile @@ -118,7 +118,7 @@ else $(BUILD)/firmware.elf: $(OBJ) $(STEPECHO) "LINK $@" $(Q)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group - $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LD_FILE) $(BUILD) + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) endif $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/mimxrt10xx/Makefile b/ports/mimxrt10xx/Makefile index 74667fd3d64..95fedc70d8e 100644 --- a/ports/mimxrt10xx/Makefile +++ b/ports/mimxrt10xx/Makefile @@ -189,6 +189,7 @@ else $(BUILD)/firmware.elf: $(OBJ) $(LD_FILES) $(STEPECHO) "LINK $@" $(Q)$(CC) -o $@ $(LDFLAGS) $(filter-out %.ld, $^) -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) endif # -R excludes sections from the output files. diff --git a/ports/nordic/Makefile b/ports/nordic/Makefile index ec755fac892..2abee97386c 100755 --- a/ports/nordic/Makefile +++ b/ports/nordic/Makefile @@ -200,7 +200,7 @@ $(BUILD)/firmware.elf: $(OBJ) $(GENERATED_LD_FILE) $(STEPECHO) "LINK $@" $(Q)echo $(OBJ) > $(BUILD)/firmware.objs $(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group - $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(GENERATED_LD_FILE) $(BUILD) + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) endif $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index 1c13d538e51..5512c7c9f47 100755 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -834,6 +834,7 @@ $(BUILD)/firmware.elf: $(OBJ) $(BOARD_LD) link-$(CHIP_VARIANT_LOWER).ld $(Q)echo $(OBJ) > $(BUILD)/firmware.objs $(Q)echo $(PICO_LDFLAGS) > $(BUILD)/firmware.ldflags $(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags $(LINKER_SCRIPTS) -Wl,--print-memory-usage -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) ifneq ($(CORE1_CHECK_ROOTS),) $(STEPECHO) "CHECK core1 flash calls" $(Q)$(PYTHON) $(TOP)/tools/check_core1_flash_calls.py $@ $(CORE1_CHECK_ROOTS) diff --git a/ports/renode/Makefile b/ports/renode/Makefile index c4ce66194e9..b69d8d251ef 100644 --- a/ports/renode/Makefile +++ b/ports/renode/Makefile @@ -88,6 +88,7 @@ $(BUILD)/firmware.elf: $(OBJ) $(BOARD_LD) link.ld $(Q)echo $(OBJ) > $(BUILD)/firmware.objs $(Q)echo $(PICO_LDFLAGS) > $(BUILD)/firmware.ldflags $(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags $(LINKER_SCRIPTS) -Wl,--print-memory-usage -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) endif $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/silabs/Makefile b/ports/silabs/Makefile index 810ab53f1f3..5c0f01e86b1 100644 --- a/ports/silabs/Makefile +++ b/ports/silabs/Makefile @@ -134,7 +134,8 @@ $(SILABS_BUILD)/$(PROJECTNAME).Makefile: | $(HEADER_BUILD)/mpversion.h $(OUTPUT_DIR)/firmware.out: $(SILABS_BUILD)/pin_functions.h $(SILABS_BUILD)/pins.c $(OBJ) $(OBJS) $(LIB_FILES) $(STEPECHO) 'Linking $(OUTPUT_DIR)/firmware.out' $(Q)echo "$(OBJS) $(OBJ)" > $(OUTPUT_DIR)/linker_objs - $(Q)$(CC) $(LD_FLAGS) @$(OUTPUT_DIR)/linker_objs -Wl,--print-memory-usage $(LIBS) -o $(OUTPUT_DIR)/firmware.out + $(Q)$(CC) $(LD_FLAGS) @$(OUTPUT_DIR)/linker_objs -Wl,--print-memory-usage -Wl,-Map=$(OUTPUT_DIR)/firmware.out.map $(LIBS) -o $(OUTPUT_DIR)/firmware.out + $(Q)$(SIZE) $(OUTPUT_DIR)/firmware.out | $(PYTHON) $(TOP)/tools/build_memory_info.py $(OUTPUT_DIR)/firmware.out.map $(BUILD) $(Q)$(OBJCOPY) $(OUTPUT_DIR)/firmware.out -O binary $(OUTPUT_DIR)/firmware.bin flash: $(OUTPUT_DIR)/firmware.bin diff --git a/ports/stm/Makefile b/ports/stm/Makefile index 5ad0c82654c..f7c17ea8d42 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -257,7 +257,7 @@ $(BUILD)/firmware.elf: $(OBJ) $(STEPECHO) "LINK $@" $(Q)echo $^ > $(BUILD)/firmware.objs $(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group - $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LD_FILE) $(BUILD) + $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $@.map $(BUILD) endif $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index fbde791afa9..e21a96e9eb8 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -52,6 +52,7 @@ export BSIM_OUT_PATH := $(CURDIR)/tools/bsim $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) west build -b $(BOARD) -d $(BUILD) $(WEST_SHIELD_ARGS) --sysbuild -- $(WEST_CMAKE_ARGS) + python ../../tools/build_memory_info.py $(BUILD)/zephyr-cp/zephyr/zephyr.map $(BUILD) --image $(BUILD)/zephyr-cp/zephyr/zephyr.bin $(BUILD)/firmware.elf: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $^ $@ diff --git a/tools/build_memory_info.py b/tools/build_memory_info.py index ff5bbfad710..8e24cbc709e 100755 --- a/tools/build_memory_info.py +++ b/tools/build_memory_info.py @@ -5,55 +5,105 @@ # # SPDX-License-Identifier: MIT +import os import re import sys import json -# Handle size constants with K or M suffixes (allowed in .ld but not in Python). -K_PATTERN = re.compile(r"([0-9]+)[kK]") -K_REPLACE = r"(\1*1024)" +# A linker map lists every region with the lengths already resolved: +# Name Origin Length Attributes +# FLASH_FIRMWARE 0x10000000 0x0017f000 xr +MAP_REGION = re.compile(r"^(\w+)\s+0x[0-9a-f]+\s+0x([0-9a-f]+)\s+\S*$", re.MULTILINE) -M_PATTERN = re.compile(r"([0-9]+)[mM]") -M_REPLACE = r"(\1*1024*1024)" +argv = sys.argv[1:] +flash_names = ["FLASH_FIRMWARE", "FLASH"] +if "--region" in argv: + i = argv.index("--region") + flash_names = argv[i + 1].split(",") + del argv[i : i + 2] + +# Ports whose toolchain this makefile cannot reach pass the image instead of piping +# size(1) in; what it occupies in flash is its size on disk. +image = None +if "--image" in argv: + i = argv.index("--image") + image = argv[i + 1] + del argv[i : i + 2] text = 0 data = 0 bss = 0 -# stdin is the linker output. -for line in sys.stdin: - # Uncomment to see linker output. - # print(line) - line = line.strip() - if not line.startswith("text"): - text, data, bss = map(int, line.split()[:3]) +if image is None: + # stdin is the linker output. + for line in sys.stdin: + # Uncomment to see linker output. + # print(line) + line = line.strip() + if not line.startswith("text"): + text, data, bss = map(int, line.split()[:3]) -regions = {} -# This file is the linker script. -with open(sys.argv[1], "r") as f: - for line in f: - line = line.strip() - if line.startswith(("FLASH_FIRMWARE", "RAM")): - regions[line.split()[0]] = line.split("=")[-1] +def regions_from_map(contents): + """Region sizes from the Memory Configuration table of a linker map.""" + start = contents.find("Memory Configuration") + if start < 0: + return None + end = contents.find("Linker script and memory map", start) + table = contents[start : end if end > 0 else len(contents)] + regions = {} + for name, length in MAP_REGION.findall(table): + if name not in ("Name", "Origin", "Length"): + regions[name] = int(length, 16) + return regions or None -for region, space in regions.items(): - if "/*" in space: - space = space.split("/*")[0] - space = K_PATTERN.sub(K_REPLACE, space) - space = M_PATTERN.sub(M_REPLACE, space) - regions[region] = int(eval(space)) -firmware_region = regions["FLASH_FIRMWARE"] -ram_region = regions["RAM"] +# The linker map lists the regions with every size the script computed resolved. +try: + with open(argv[0], "r") as f: + contents = f.read() +except FileNotFoundError: + print() + print(f"No {argv[0]} to read the flash region from.") + print() + sys.exit(0) +regions = regions_from_map(contents) or {} + +firmware_region = None +for name in flash_names: + if name in regions: + firmware_region = regions[name] + break + +if image is not None: + try: + text = os.stat(image).st_size + except FileNotFoundError: + print() + print(f"No {image} to measure.") + print() + sys.exit(0) used_flash = data + text -free_flash = firmware_region - used_flash used_ram = data + bss -free_ram = ram_region - used_ram -with open(f"{sys.argv[2]}/firmware.size.json", "w") as f: +if firmware_region is None: + # Not knowing the size is not worth failing a build over: the tools that read + # firmware.size.json fall back to assuming there is no headroom. + print() + print( + "No {} region in {}. Regions found: {}.".format( + " or ".join(flash_names), argv[0], ", ".join(sorted(regions)) or "none" + ) + ) + print("{} bytes used in flash firmware space.".format(used_flash)) + print() + sys.exit(0) + +free_flash = firmware_region - used_flash + +with open(f"{argv[1]}/firmware.size.json", "w") as f: json.dump({"used_flash": used_flash, "firmware_region": firmware_region}, f) print() @@ -62,16 +112,11 @@ used_flash, free_flash, firmware_region, firmware_region / 1024 ) ) -print( - "{} bytes used, {} bytes free in ram for stack and heap out of {} bytes ({}kB).".format( - used_ram, free_ram, ram_region, ram_region / 1024 +if image is None and "RAM" in regions: + ram_region = regions["RAM"] + print( + "{} bytes used, {} bytes free in ram for stack and heap out of {} bytes ({}kB).".format( + used_ram, ram_region - used_ram, ram_region, ram_region / 1024 + ) ) -) print() - -# Check that we have free flash space. GCC doesn't fail when the text + data -# sections don't fit in FLASH. It only counts data in RAM. -if free_flash < 0: - print("Too little flash!!!") - print() - sys.exit(-1)