Files
STM32F4-Base/tools/arch_diagram/gen_partition_svg.py
T
wandering1 a6cdebde62 文档体系重组与 D-7 一致性门禁落地,清理芯片手册孤儿图片
- D-6 目录规范化:docs 根目录散落文档归入 UI设计/子系统设计/架构设计/审查记录/
  环境与部署/网络 等子目录,去掉目录名空格,重命名 嵌入式C语言代码规范_V1.0.md
- D-7 三档一致性门禁:新增 scripts/check_doc_links.py(链接存在性)、
  scripts/gen_task_table.py(FreeRTOS 任务表数值)、scripts/check_mem_numbers.py
  (内存/Flash 体积),均支持 --check/--write/--diff,pre-commit 钩子同步注册
- D-5 文档索引:新建 docs/README.md,新建 docs/文档规范.md 约束目录命名与链接写法
- D-1 芯片手册瘦身:删除 1069 张无引用孤儿图片(77.06 MB),
  docs/芯片手册 120 MB → 41 MB,已引用图片零断链
- 同步 C 源码注释中的文档路径引用(D-6 重组后),构建 0 错误 0 警告
2026-09-26 00:51:53 +08:00

139 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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('<rect x="0" y="0" width="%d" height="%d" fill="#ffffff"/>' % (W, total_h))
# 标题 + 副标题
add('<text x="%d" y="40" font-size="%d" font-weight="700" fill="#0f172a">%s</text>'
% (PAD, TITLE_FONT, esc(TITLE)))
add('<text x="%d" y="64" font-size="%d" fill="#64748b">%s</text>'
% (PAD, SUB_FONT, esc(SUB)))
# 偏移标尺(每个分区边界一个刻度 + 偏移标注)
ruler_y = BAR_Y - RULER_DY
add('<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#cbd5e1" stroke-width="1"/>'
% (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('<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#94a3b8" stroke-width="1.5"/>'
% (bx, ruler_y - 4, bx, ruler_y + 4))
lab = labels[i] if i < len(labels) else "?"
add('<text x="%d" y="%d" font-size="%d" fill="#94a3b8" text-anchor="middle">%s</text>'
% (bx, ruler_y - 8, RULER_FONT, esc(lab)))
# 分区盒(名在上、尺寸在下)
for (f, bx, bw) in segs:
add('<rect x="%d" y="%d" width="%d" height="%d" fill="%s" stroke="%s" stroke-width="1.5"/>'
% (bx, BAR_Y, bw, BAR_H, f["bg"], f["color"]))
cx = bx + bw / 2.0
add('<text x="%.1f" y="%d" font-size="%d" font-weight="700" fill="#0f172a" '
'text-anchor="middle">%s</text>'
% (cx, BAR_Y + NAME_DY, NAME_FONT, esc(f["name"])))
add('<text x="%.1f" y="%d" font-size="%d" fill="%s" text-anchor="middle">%s</text>'
% (cx, BAR_Y + SIZE_DY, SIZE_FONT, f["color"], esc(f["label"])))
# 脚注
add('<text x="%d" y="%d" font-size="%d" fill="#94a3b8">%s</text>'
% (PAD, total_h - 16, FOOT_FONT, esc(FOOT)))
svg = ('<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" '
'viewBox="0 0 %d %d" font-family="%s">\n%s\n</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()