#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成 docs/images/物理分区布局.svg —— GD5F2GQ5UE 全片分区布局图(纯标准库)。
由 gen_frame_svg.py 复制而来(build() 与数据解耦,通用):协议帧图按"字节"计量
(字段宽 ∝ 字节数),本图按"MB"计量(分区宽 ∝ MB 数),其余约定一致——
分段矩形 + 偏移标尺 + 逐段标注,连续布局无假箭头。
用法(仓库根目录,任意 python3):
python tools/arch_diagram/gen_partition_svg.py
输出:
docs/images/物理分区布局.svg
改分区时:编辑本文件顶部的 FIELDS / TITLE / SUB / FOOT 后重跑。
"""
import html
import os
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
OUT = os.path.join(ROOT, "docs", "images", "物理分区布局.svg")
# ----------------------------- 画布与尺寸参数 -----------------------------
PAD = 40 # 左右留白
PX_PER_MB = 4 # 每 MB 像素宽(分区宽 ∝ MB 数)
BAR_Y = 116 # 分区盒顶 y(标题 / 副标题 / 偏移标尺在其上方)
BAR_H = 74 # 分区盒高
NAME_DY = 30 # 分区名在盒内的 y 偏移
SIZE_DY = 54 # 尺寸标注在盒内的 y 偏移
RULER_DY = 20 # 偏移标尺线在盒顶上方
TITLE_FONT = 20
SUB_FONT = 13
NAME_FONT = 15
SIZE_FONT = 12
RULER_FONT = 12
FOOT_FONT = 12
FONT = "Microsoft YaHei, Segoe UI, PingFang SC, sans-serif"
# ----------------------------- 分区数据(改这里) -----------------------------
# name=分区名 mb=MB 数(None=变长段,按 DATA_UNITS 画) label=尺寸标注 color/bg=配色
DATA_UNITS = 5 # 变长段的"代表宽度"(按 N MB 画,仅示意)
FIELDS = [
dict(name="ftl_fatfs", mb=256, label="256 MB · Block 0~2047", color="#2563eb", bg="#eff6ff"),
]
TITLE = "GD5F2GQ5UE 物理分区布局(256MB)"
SUB = "单分区:全片划给 FTL(无静态分区)· 块 = 128KB(64 页 × 2KB)· 共 2048 块"
FOOT = ("ftl_fatfs:dhara FTL + FatFS 占用全部 2048 块(偏移 0 ~ 256MB)· "
"详见 docs/STM32F4-Base存储架构说明.md §1")
# ----------------------------- 工具函数 -----------------------------
def esc(s):
return html.escape(s, quote=True)
def field_w(f):
return (f["mb"] if f["mb"] is not None else DATA_UNITS) * PX_PER_MB
def offset_labels(fields):
"""按分区 MB 数累计边界偏移;偏移标注统一带 MB 单位。"""
labs, off = ["0"], 0
for f in fields:
off += f["mb"] if f["mb"] is not None else DATA_UNITS
labs.append("%d MB" % off)
return labs
# ----------------------------- 生成 -----------------------------
def build():
out = []
add = out.append
frame_w = sum(field_w(f) for f in FIELDS)
W = frame_w + 2 * PAD
# 预计算各分区 x / 宽
segs, x = [], PAD
for f in FIELDS:
w = field_w(f)
segs.append([f, x, w])
x += w
labels = offset_labels(FIELDS)
total_h = BAR_Y + BAR_H + 56
add('' % (W, total_h))
# 标题 + 副标题
add('%s'
% (PAD, TITLE_FONT, esc(TITLE)))
add('%s'
% (PAD, SUB_FONT, esc(SUB)))
# 偏移标尺(每个分区边界一个刻度 + 偏移标注)
ruler_y = BAR_Y - RULER_DY
add(''
% (PAD, ruler_y, PAD + frame_w, ruler_y))
bounds = [PAD] + [s[1] + s[2] for s in segs]
for i, bx in enumerate(bounds):
add(''
% (bx, ruler_y - 4, bx, ruler_y + 4))
lab = labels[i] if i < len(labels) else "?"
add('%s'
% (bx, ruler_y - 8, RULER_FONT, esc(lab)))
# 分区盒(名在上、尺寸在下)
for (f, bx, bw) in segs:
add(''
% (bx, BAR_Y, bw, BAR_H, f["bg"], f["color"]))
cx = bx + bw / 2.0
add('%s'
% (cx, BAR_Y + NAME_DY, NAME_FONT, esc(f["name"])))
add('%s'
% (cx, BAR_Y + SIZE_DY, SIZE_FONT, f["color"], esc(f["label"])))
# 脚注
add('%s'
% (PAD, total_h - 16, FOOT_FONT, esc(FOOT)))
svg = ('\n'
% (W, total_h, W, total_h, FONT, "\n".join(out)))
return svg, W, total_h
def main():
svg, W, h = build()
with open(OUT, "w", encoding="utf-8", newline="\n") as f:
f.write(svg)
import xml.etree.ElementTree as ET
ET.fromstring(svg)
print("OK %d bytes, canvas %dx%d -> %s" % (len(svg.encode("utf-8")), W, h, OUT))
if __name__ == "__main__":
main()