Files

136 lines
4.2 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
合并 bootloader.hex + app.hex 为出厂烧录用的 merged_single.hex。
用法:
python tools/merge_hex.py [--out OUT] [--bl bootloader.hex] [--app app.hex]
默认输入/输出均位于 PY32F040开发框架/Output/ 下。
"""
import argparse
import os
import sys
# 默认相对本脚本: ../Output (脚本位于 框架/tools/)
_DEF_OUT_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"Output")
def parse_hex(path):
"""解析 Intel HEX -> dict[abs_addr] = byte。完整支持 0x04 扩展线性地址记录,
得到 32 位绝对地址。本工程基址 0x08000000 即由 0x04 记录(:020000040800F2)给出。"""
mem = {}
upper = 0 # 0x04 记录的扩展线性地址(左移到高 16 位)
with open(path, "r") as f:
for line in f:
line = line.strip()
if not line.startswith(":"):
continue
n = int(line[1:3], 16)
addr = int(line[3:7], 16)
rec = int(line[7:9], 16)
if rec == 0x00:
data = bytes.fromhex(line[9:9 + n * 2])
base = (upper << 16) | addr
for i, b in enumerate(data):
mem[base + i] = b
elif rec == 0x01: # EOF
break
elif rec == 0x04: # 扩展线性地址
upper = int(line[9:13], 16)
# 0x02/0x03/0x05 等本工程不涉及,忽略
return mem
def _emit_ela(upper):
"""0x04 扩展线性地址记录。"""
body = "02000004%04X" % upper
cs = 0
for i in range(0, len(body), 2):
cs += int(body[i:i + 2], 16)
cs = (0x100 - (cs & 0xFF)) & 0xFF
return ":%s%02X" % (body, cs)
def _emit(buf, addr):
body = "%02X%04X00" % (len(buf), addr) + "".join("%02X" % b for b in buf)
cs = 0
for i in range(0, len(body), 2):
cs += int(body[i:i + 2], 16)
cs = (0x100 - (cs & 0xFF)) & 0xFF
return ":%s%02X" % (body, cs)
def write_hex(path, mem):
"""按 32 位绝对地址写出;每当高 16 位(upper)变化时插入 0x04 扩展记录,
保证 0x08000000 / 0x08004800 等基址被烧录器正确识别。"""
addrs = sorted(mem)
lines = []
buf = []
cur_upper = None
cur = None
next_addr = None
def flush():
if buf:
lines.append(_emit(buf, cur))
buf.clear()
for a in addrs:
up = (a >> 16) & 0xFFFF
off = a & 0xFFFF
if cur_upper is None:
cur_upper = up
lines.append(_emit_ela(up))
cur = off
next_addr = a + 1
elif up != cur_upper:
flush()
cur_upper = up
lines.append(_emit_ela(up))
cur = off
next_addr = a + 1
elif a != next_addr:
flush()
cur = off
next_addr = a + 1
buf.append(mem[a])
next_addr = a + 1
if len(buf) == 16:
flush()
cur = next_addr & 0xFFFF
flush()
lines.append(":00000001FF")
with open(path, "w") as f:
f.write("\n".join(lines) + "\n")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--bl", default=os.path.join(_DEF_OUT_DIR, "bootloader.hex"))
ap.add_argument("--app", default=os.path.join(_DEF_OUT_DIR, "app.hex"))
ap.add_argument("--out", default=os.path.join(_DEF_OUT_DIR, "merged_single.hex"))
args = ap.parse_args()
for p in (args.bl, args.app):
if not os.path.isfile(p):
sys.exit("ERROR: 找不到 %s" % p)
bl = parse_hex(args.bl)
app = parse_hex(args.app)
print("bootloader: %d bytes, range 0x%X-0x%X" % (len(bl), min(bl), max(bl)))
print("app: %d bytes, range 0x%X-0x%X" % (len(app), min(app), max(app)))
overlap = set(bl) & set(app)
if overlap:
sys.exit("ERROR: 地址重叠 %d 处 (例如 0x%X)" % (len(overlap), min(overlap)))
merged = dict(bl)
merged.update(app)
write_hex(args.out, merged)
nlines = sum(1 for _ in open(args.out))
print("merged: %d bytes -> %s (%d 行)" % (len(merged), args.out, nlines))
if __name__ == "__main__":
main()