Files
2025-06-13 18:31:56 +08:00

226 lines
7.1 KiB
Python

# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
#
# SPDX-License-Identifier: MIT
# Combine bootloader, partition table and application into a final binary.
from datetime import date
import os, sys
sys.path.append(os.getenv("IDF_PATH") + "/components/partition_table")
import gen_esp32part
OFFSET_BOOTLOADER_DEFAULT = 0x1000
OFFSET_PARTITIONS_DEFAULT = 0x8000
def load_sdkconfig_value(filename, value, default):
value = "CONFIG_" + value + "="
with open(filename, "r") as f:
for line in f:
if line.startswith(value):
return line.split("=", 1)[1]
return
def load_sdkconfig_hex_value(filename, value, default):
value = "CONFIG_" + value + "="
with open(filename, "r") as f:
for line in f:
if line.startswith(value):
return int(line.split("=", 1)[1], 16)
return default
def load_sdkconfig_spiram_value(filename):
value = "SPIRAM_SUPPORT="
with open(filename, "r") as f:
for line in f:
if value in line:
if line.split("=", 1)[1][0] == "y":
return "SPIRAM-"
return ""
def load_sdkconfig_flash_size_value(filename):
value = "CONFIG_ESPTOOLPY_FLASHSIZE="
with open(filename, "r") as f:
for line in f:
if line.startswith(value):
return str(line.split("_")[-1].split("=", 1)[1][1:-2])
return "4MB"
def load_sdkconfig_str_value(filename, value, default):
value = load_sdkconfig_value(filename, value, None)
if value is None:
return default
return value.strip().strip('"')
def load_partition_table(filename):
with open(filename, "rb") as f:
return gen_esp32part.PartitionTable.from_binary(f.read())
# Extract command-line arguments.
arg_sdkconfig = sys.argv[1]
arg_bootloader_bin = sys.argv[2]
arg_partitions_bin = sys.argv[3]
arg_nvs_bin = sys.argv[4]
arg_application_bin = sys.argv[5]
arg_fs_sys_bin = sys.argv[6]
arg_fs_vfs_bin = sys.argv[7]
arg_board_type_flag = sys.argv[8]
arg_lvgl_flag = sys.argv[9]
arg_output_bin = sys.argv[10]
arg_output_uf2 = sys.argv[11]
# Load required sdkconfig values.
idf_target = load_sdkconfig_str_value(arg_sdkconfig, "IDF_TARGET", "").upper()
offset_bootloader = load_sdkconfig_hex_value(
arg_sdkconfig, "BOOTLOADER_OFFSET_IN_FLASH", OFFSET_BOOTLOADER_DEFAULT
)
offset_partitions = load_sdkconfig_hex_value(
arg_sdkconfig, "PARTITION_TABLE_OFFSET", OFFSET_PARTITIONS_DEFAULT
)
# Load the partition table.
partition_table = load_partition_table(arg_partitions_bin)
max_size_bootloader = offset_partitions - offset_bootloader
max_size_partitions = 0
offset_nvs = 0
max_size_nvs = 0
offset_application = 0
max_size_application = 0
offset_fs_sys = 0
max_size_fs_sys = 0
offset_fs_vfs = 0
max_size_fs_vfs = 0
# Inspect the partition table to find offsets and maximum sizes.
for part in partition_table:
if part.name == "nvs":
max_size_partitions = part.offset - offset_partitions
offset_nvs = part.offset
max_size_nvs = part.size
elif part.type == gen_esp32part.APP_TYPE and offset_application == 0:
offset_application = part.offset
max_size_application = part.size
elif part.type == gen_esp32part.DATA_TYPE and part.name == "sys":
offset_fs_sys = part.offset
max_size_fs_sys = part.size
elif part.type == gen_esp32part.DATA_TYPE and part.name == "vfs":
offset_fs_vfs = part.offset
max_size_fs_vfs = part.size
# Define the input files, their location and maximum size.
files_in = [
("bootloader", offset_bootloader, max_size_bootloader, arg_bootloader_bin),
("partitions", offset_partitions, max_size_partitions, arg_partitions_bin),
("nvs", offset_nvs, max_size_nvs, arg_nvs_bin),
("application", offset_application, max_size_application, arg_application_bin),
("fs_sys", offset_fs_sys, max_size_fs_sys, arg_fs_sys_bin),
("fs_vfs", offset_fs_vfs, max_size_fs_vfs, arg_fs_vfs_bin),
]
# Remove files that are not present.
if arg_fs_vfs_bin == "none":
files_in.pop()
file_out = arg_output_bin
file_out_complete = os.path.abspath(arg_output_bin)
# Write output file with combined firmware.
cur_offset = 0
with open(file_out, "wb") as fout:
for name, offset, max_size, file_in in files_in:
if name == "fs_sys" and offset == 0:
continue
assert offset >= cur_offset
fout.write(b"\xff" * (offset - cur_offset))
cur_offset = offset
with open(file_in, "rb") as fin:
data = fin.read()
fout.write(data)
cur_offset += len(data)
print(
"%-12s@0x%06x % 9d (% 8d remaining)"
% (name, offset, len(data), max_size - len(data))
)
if len(data) > max_size:
print(
"ERROR: %s overflows allocated space of %d bytes by %d bytes"
% (name, max_size, len(data) - max_size)
)
sys.exit(1)
print("%-23s%8d (% 8.1f MB)" % ("total", cur_offset, (cur_offset / 1024 / 1024)))
print(
"\r\nWrote 0x%x bytes to file m5stack/%s, ready to flash to offset 0x%x.\r\n\r\n"
"\033[1;32mExample command:\033[0m\r\n"
" \033[1;33m1.\033[0m make BOARD=%s BOARD_TYPE=%s PORT=/dev/ttyUSBx flash\r\n"
" \033[1;33m2.\033[0m esptool.py --chip %s --port /dev/ttyUSBx --baud 1500000 write_flash 0x%x %s"
% (
cur_offset,
file_out,
0x0,
file_out[6:].split("/")[0],
arg_board_type_flag.lower(),
idf_target.lower(),
0x0,
file_out_complete,
)
)
# Generate .uf2 file if the SoC has native USB.
if idf_target in ("ESP32S2", "ESP32S3"):
sys.path.append(os.path.join(os.path.dirname(__file__), "../micropython/tools"))
import uf2conv
families = uf2conv.load_families()
uf2conv.appstartaddr = 0
uf2conv.familyid = families[idf_target]
with open(arg_application_bin, "rb") as fin, open(arg_output_uf2, "wb") as fout:
fout.write(uf2conv.convert_to_uf2(fin.read()))
# uiflow-0973efa-esp32s3-8mb-atoms3-v2.0.0-alpha-2-20230206.bin
today = date.today()
feature_str = ""
if idf_target == "ESP32C3":
if load_sdkconfig_str_value(arg_sdkconfig, "ESP_CONSOLE_USB_SERIAL_JTAG", "").upper() == "Y":
feature_str = "usb-"
else:
feature_str = load_sdkconfig_spiram_value(arg_sdkconfig).lower()
arg_board_type_flag = arg_board_type_flag + "-"
if arg_lvgl_flag == "1":
arg_lvgl_flag = "lvgl-"
else:
arg_lvgl_flag = ""
uiflow_version = ""
with open("./version.txt", "r") as f:
uiflow_version = f.readline() + "-"
release_file_out = "{}-{}-{}{}-{}{}{}{}.bin".format(
file_out_complete.split(".bin")[0],
idf_target.lower(),
feature_str.lower(),
load_sdkconfig_flash_size_value(arg_sdkconfig).lower(),
arg_board_type_flag.lower(),
arg_lvgl_flag,
uiflow_version.lower(),
today.strftime("%Y%m%d"),
)
print(
"\033[1;32mRelease Firmware:\033[0m\r\n \033[1;33m"
+ arg_board_type_flag[:-1].upper()
+ ":\033[0m "
+ release_file_out
)
os.system("cp {} {}".format(file_out, release_file_out))