99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
固件镜像处理: 补齐(pad)与拼接(merge) — 纯标准库, GUI 与命令行共用
|
|
================================================================
|
|
pad - BIN 尾部补 pad_byte 到指定对齐(bootloader 的 Y-Modem 按 1K 整包接收)
|
|
merge - Bootloader + APP + 有效标志 拼成一整片 Flash 镜像(初次烧录用)
|
|
|
|
芯片参数表(CHIP_PROFILES)同时供烧录页(target cfg/Flash 大小)与补齐/拼接
|
|
的容量校验使用。
|
|
"""
|
|
|
|
import struct
|
|
|
|
FLASH_BASE_DEFAULT = 0x08000000
|
|
APP_ADDR_DEFAULT = 0x08002000
|
|
FLAG_ADDR_DEFAULT = 0x0800FFFC
|
|
FLAG_VALUE_DEFAULT = 0xEEEEEEEE
|
|
|
|
CHIP_PROFILES = {
|
|
"GD32E230F4": {"target": "target/gd32e23x.cfg", "flash": 16 * 1024, "ram": 4 * 1024},
|
|
"GD32E230F6": {"target": "target/gd32e23x.cfg", "flash": 32 * 1024, "ram": 4 * 1024},
|
|
"GD32E230F8": {"target": "target/gd32e23x.cfg", "flash": 64 * 1024, "ram": 8 * 1024},
|
|
"GD32E230C8": {"target": "target/gd32e23x.cfg", "flash": 64 * 1024, "ram": 8 * 1024},
|
|
}
|
|
|
|
|
|
def parse_int(text):
|
|
"""解析 '0x1C' / '4096' / '1k' / '2m' 形式的数值, 非法返回 None"""
|
|
s = str(text).strip().lower()
|
|
if not s:
|
|
return None
|
|
mult = 1
|
|
if s.endswith("k"):
|
|
mult = 1024
|
|
s = s[:-1]
|
|
elif s.endswith("m"):
|
|
mult = 1024 * 1024
|
|
s = s[:-1]
|
|
try:
|
|
if s.startswith("0x"):
|
|
return int(s, 16) * mult
|
|
return int(s, 10) * mult
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def pad_bin(src_path, dst_path, align, pad_byte=0xFF):
|
|
"""把 BIN 补齐到 align 的整数倍, 返回补齐后大小"""
|
|
with open(src_path, "rb") as f:
|
|
data = f.read()
|
|
if align <= 0:
|
|
raise ValueError("align must be positive")
|
|
remainder = len(data) % align
|
|
if remainder:
|
|
data += bytes([pad_byte & 0xFF]) * (align - remainder)
|
|
with open(dst_path, "wb") as f:
|
|
f.write(data)
|
|
return len(data)
|
|
|
|
|
|
def merge_firmware(bootloader_path, app_path, output_path, app_addr, flag_addr,
|
|
flash_size, flag_value=FLAG_VALUE_DEFAULT):
|
|
"""Bootloader + APP + 有效标志 → 整片 Flash 镜像, 返回尺寸信息 dict"""
|
|
if app_addr < FLASH_BASE_DEFAULT or app_addr >= FLASH_BASE_DEFAULT + flash_size:
|
|
raise ValueError("APP address is outside the flash range")
|
|
if flag_addr < FLASH_BASE_DEFAULT or flag_addr + 4 > FLASH_BASE_DEFAULT + flash_size:
|
|
raise ValueError("flag address is outside the flash range")
|
|
|
|
with open(bootloader_path, "rb") as f:
|
|
bootloader = f.read()
|
|
with open(app_path, "rb") as f:
|
|
app = f.read()
|
|
|
|
app_offset = app_addr - FLASH_BASE_DEFAULT
|
|
flag_offset = flag_addr - FLASH_BASE_DEFAULT
|
|
|
|
if len(bootloader) > app_offset:
|
|
raise ValueError("bootloader (%d bytes) exceeds APP offset (%d bytes)"
|
|
% (len(bootloader), app_offset))
|
|
if app_offset + len(app) > flag_offset:
|
|
raise ValueError("APP end address exceeds the flag address")
|
|
|
|
image = bytearray(b"\xFF" * flash_size)
|
|
image[0:len(bootloader)] = bootloader
|
|
image[app_offset:app_offset + len(app)] = app
|
|
struct.pack_into("<I", image, flag_offset, flag_value & 0xFFFFFFFF)
|
|
|
|
with open(output_path, "wb") as f:
|
|
f.write(image)
|
|
|
|
return {
|
|
"bootloader_size": len(bootloader),
|
|
"app_size": len(app),
|
|
"output_size": len(image),
|
|
"app_addr": app_addr,
|
|
"flag_addr": flag_addr,
|
|
}
|