从原理图到 PCB 制造数据:设计交付的 4 个核对关口
器件库、原理图、PCB 版图与制造输出之间,如何让变更有迹可循?本文从数据链、设计审查和发布包校验三个层面拆解交付流程,并提供一段可运行的 Python 文件检查脚本。
1. PCB 交付的难点,往往在阶段之间
一块板的交付通常不止一张 PCB 图。设计过程会涉及器件及封装数据、原理图网络、板层与布局、布线规则,以及后续的生产资料。器件替换或原理图调整后,工程师要确认受影响的数据是否已经复核,并确认交付文件来自哪一版设计。
这类问题不是"文件能否导出"这么简单。还需要区分三件事:
- 设计数据正确性:原理图网络、器件封装、板上连接和约束是否通过工程审查。
- 制造数据完整性:需要的铜层、阻焊、丝印、板框和钻孔数据是否齐全,格式是否符合板厂要求。
- 交付包一致性 :实际发出的文件是否来自已确认的版次,传递后是否被替换或损坏。

2. 按数据链设置检查关口
2.1 元器件库:先确认"器件是谁"
对关键器件确认型号、引脚定义、原理图符号与 PCB 封装之间的映射。更换器件时,除了核对料号,也要确认封装尺寸、焊盘定义和库数据版本。外形相似不代表封装或引脚兼容。
2.2 原理图:确认网络关系,而不只看图面
检查原理图变更是否影响引脚连接、网络名称或层级关系。需要时从设计数据中生成或核对 netlist,并把受影响的网络列入 PCB 复核范围。图面看起来没有变化,不足以证明网络关系没有变化。
2.3 PCB:审查板级实现与约束
复核板框、层叠、过孔、器件位置、布线和适用的设计规则。重点网、差分对、间距和特殊区域应按照项目约束检查。自动或交互式布线可以参与设计过程,但布线完成不等于所有规则、信号完整性或制造约束都已满足。

2.4 生产资料:区分制造与装配交付
PCB 制板常见输出包括 Gerber 层数据和 Excellon/Drill 钻孔数据;不同板厂也可能要求不同命名、层映射或其他交换格式。装配环节还可能要求 BOM、坐标文件或装配图,这些不是每个制板订单都需要的同一套资料。按制造方清单逐项核对,并保留确认版次。

3. 给交付目录增加一次轻量校验
EDA 中的电气检查、设计规则检查(DRC)和制造方评审,负责判断设计是否满足对应要求。文件发布包还可以增加一层简单的自动检查:确认目标版次、必需文件是否存在、文件是否为空,以及已记录的 SHA-256 是否匹配。
下面的脚本使用 Python 标准库,不解析 Gerber 或 Drill 的语义,也不判断电气正确性。它只适合做交付目录的完整性检查,不能替代 EDA 检查或工程评审。
3.1 目录示例
text
release-R07/
├── release-manifest.json
└── files/
├── top_copper.gbr
├── bottom_copper.gbr
└── plated_drill.drl
release-manifest.json 示例:
json
{
"design_revision": "R07",
"files": [
{"path": "files/top_copper.gbr"},
{"path": "files/bottom_copper.gbr"},
{"path": "files/plated_drill.drl"}
]
}
当团队已有发布流程时,可在文件项中增加 sha256 字段,用于核对传递前后文件是否一致。哈希应在最终导出并完成审核后生成;不要复用旧版文件的哈希。
3.2 检查脚本
将下列代码保存为 verify_release_bundle.py:
python
#!/usr/bin/env python3
"""Check release revision, required files, non-empty files, and optional hashes."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import sys
from pathlib import Path
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def check_bundle(manifest_path: Path, expected_revision: str) -> list[str]:
try:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
return [f"cannot read manifest: {exc}"]
if not isinstance(manifest, dict):
return ["manifest root must be a JSON object"]
errors: list[str] = []
revision = manifest.get("design_revision")
if not isinstance(revision, str) or not revision.strip():
errors.append("manifest is missing a non-empty design_revision")
elif revision != expected_revision:
errors.append(
f"revision mismatch: manifest={revision!r}, expected={expected_revision!r}"
)
files = manifest.get("files")
if not isinstance(files, list) or not files:
return errors + ["manifest.files must be a non-empty array"]
root = manifest_path.parent.resolve()
seen: set[str] = set()
for item in files:
if not isinstance(item, dict):
errors.append("each files entry must be an object")
continue
relative = item.get("path")
if not isinstance(relative, str) or not relative.strip():
errors.append("file entry is missing a non-empty path")
continue
candidate = (root / relative).resolve()
try:
candidate.relative_to(root)
except ValueError:
errors.append(f"path escapes release directory: {relative}")
continue
normalized = candidate.as_posix().casefold()
if normalized in seen:
errors.append(f"duplicate path in manifest: {relative}")
continue
seen.add(normalized)
if not candidate.is_file():
errors.append(f"missing file: {relative}")
continue
if candidate.stat().st_size == 0:
errors.append(f"empty file: {relative}")
continue
expected_hash = item.get("sha256")
if expected_hash is not None:
if not isinstance(expected_hash, str) or not re.fullmatch(
r"[0-9a-fA-F]{64}", expected_hash
):
errors.append(f"invalid SHA-256: {relative}")
continue
actual_hash = sha256_file(candidate)
if actual_hash.casefold() != expected_hash.casefold():
errors.append(f"SHA-256 mismatch: {relative}")
return errors
def main() -> int:
parser = argparse.ArgumentParser(
description="Check release revision, required files, non-empty files, and optional SHA-256 hashes."
)
parser.add_argument("manifest", type=Path, help="Path to release-manifest.json")
parser.add_argument("--revision", required=True, help="Expected design revision")
args = parser.parse_args()
errors = check_bundle(args.manifest, args.revision)
if errors:
for error in errors:
print(f"FAIL: {error}", file=sys.stderr)
return 1
print(f"PASS: {args.manifest} revision={args.revision}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
3.3 运行与结果
在交付目录执行:
bash
python verify_release_bundle.py release-manifest.json --revision R07
正常时返回 PASS 和版次;缺少文件、空文件、SHA-256 不匹配或版次不一致时返回 FAIL,进程退出码为 1。应把 --revision 与项目审批或发布记录中的目标版次绑定,而不是从待检查文件名自动推断。
注意:manifest 是发布记录,不是 EDA 设计数据库。即使脚本返回 PASS,也只证明"清单所列文件存在且与清单记录一致",不能证明 Gerber 图层映射正确、网络无误、DRC 通过或板厂已认可文件。
4. 如何评估设计工具是否适合团队
Delta Design 官方资料介绍了元器件库、电路原理图、PCB 设计、规则与布线,以及设计和生产资料输出等环节;RightPCB、TopoR 和 DeltaCAM 等模块资料分别说明了板级设计/布线及生产文件查看、编辑或验证能力。具体格式、模块与授权应以当前产品资料和实际配置为准。
选型演示时,建议带一个经过脱敏的真实项目,现场验证几件事:器件库到符号/封装的对应关系如何维护;原理图变更怎样进入 PCB 复核;规则和布线问题如何定位;Gerber/Drill 等文件如何生成、查看与交付。这样比只看功能清单更容易判断是否贴合现有流程。
适用范围:工业控制与自动化、仪器仪表、通信设备、嵌入式电子等有电路设计和 PCB 交付需求的研发团队。
产品信息:Delta Design 产品页。该链接由运营方提供,具体功能与授权请以产品页和销售确认信息为准。
