3. LEF/DEF设计数据读入与合法性检查

LEF/DEF设计数据读入与合法性检查 学习卡片

任务说明

本案例编写一个 Python 脚本,读入 OpenROAD 流程所需的 LEF(工艺/单元库)与 DEF(设计)文件,检查三类关键定义是否完整:

  • 单元(MACRO):DEF 中实例化的每个单元,是否都能在 LEF 中找到对应 MACRO 定义。
  • 引脚(PIN):DEF 中每个实例的每个连接引脚,是否都能在对应 MACRO 的 PIN 定义中找到。
  • 布线层(LAYER):DEF 中布线/走线引用的层,是否都在 LEF 的 LAYER 段中定义。

脚本输出一份检查报告,列出缺失项与统计结果,并以退出码区分是否通过。

环境与依赖

  • Python 3.8+,仅使用标准库(re、sys、argparse)。
  • 输入:一个或多个 LEF 文件、一个 DEF 文件。LEF/DEF 为文本格式,脚本按段关键字解析。

实现思路

LEF 与 DEF 都是分段的文本格式,用行首关键字识别段。解析时维护“当前段”状态:进入 MACRO 段收集单元名与其中的 PIN 名;进入 LAYER 段收集层名。DEF 中 COMPONENTS 段每行给出实例名与单元名,PINS 段给出顶层引脚及其层,SPECIALNETS/NETS 段中的 ROUTED/NEW 行给出布线层。收集完成后做集合比对。

LEF 文件 MACRO / PIN / LAYER DEF 文件 COMPONENTS / PINS / NETS 解析器 按段收集定义 单元完整性检查 引脚完整性检查 布线层完整性检查

完整代码

import re
import sys
import argparse

def parse_lef(paths):
    """返回 (macros, macro_pins, layers)。
    macros: {macro_name: set(pin_name)}
    layers: set(layer_name)
    """
    macros = {}
    layers = set()
    cur_macro = None
    cur_pin = None
    for path in paths:
        with open(path, "r", errors="ignore") as f:
            for raw in f:
                line = raw.strip()
                if not line or line.startswith("#"):
                    continue
                if line.startswith("MACRO "):
                    cur_macro = line.split()[1]
                    macros.setdefault(cur_macro, set())
                    cur_pin = None
                elif line == "END" and cur_macro and cur_pin:
                    cur_pin = None
                elif line.startswith("PIN ") and cur_macro:
                    cur_pin = line.split()[1]
                    macros[cur_macro].add(cur_pin)
                elif line.startswith("LAYER "):
                    layers.add(line.split()[1])
                elif line.startswith("END ") and cur_macro:
                    cur_macro = None
    return macros, layers

def parse_def(path):
    """返回 (instances, top_pins, routed_layers)。
    instances: {inst_name: cell_name}
    top_pins: {pin_name: set(layer)}
    routed_layers: set(layer)
    """
    instances = {}
    top_pins = {}
    routed_layers = set()
    section = None
    cur_pin = None
    with open(path, "r", errors="ignore") as f:
        for raw in f:
            line = raw.strip()
            if not line or line.startswith("#"):
                continue
            if line.startswith("COMPONENTS"):
                section = "COMPONENTS"
                continue
            if line.startswith("PINS"):
                section = "PINS"
                continue
            if line.startswith("SPECIALNETS") or line.startswith("NETS"):
                section = "NETS"
                continue
            if line.startswith("END ") and section:
                section = None
                cur_pin = None
                continue

            if section == "COMPONENTS":
                m = re.match(r"-\s+(\S+)\s+(\S+)", line)
                if m:
                    instances[m.group(1)] = m.group(2)
            elif section == "PINS":
                m = re.match(r"-\s+(\S+)", line)
                if m:
                    cur_pin = m.group(1)
                    top_pins.setdefault(cur_pin, set())
                m = re.search(r"LAYER\s+(\S+)", line)
                if m and cur_pin:
                    top_pins[cur_pin].add(m.group(1))
            elif section == "NETS":
                m = re.search(r"(?:ROUTED|NEW)\s+(\S+)", line)
                if m:
                    routed_layers.add(m.group(1))
    return instances, top_pins, routed_layers

def check(lef_paths, def_path):
    macros, layers = parse_lef(lef_paths)
    instances, top_pins, routed_layers = parse_def(def_path)

    missing_cells = []
    missing_pins = []
    for inst, cell in instances.items():
        if cell not in macros:
            missing_cells.append((inst, cell))
            continue
        for pin in macros[cell]:
            pass  # 引脚存在性由 DEF 侧连接决定,见下

    # 检查 DEF 中实例引用的单元是否存在
    for inst, cell in instances.items():
        if cell not in macros:
            missing_cells.append((inst, cell))

    # 检查顶层引脚引用的层
    missing_layers = set()
    for pin, used in top_pins.items():
        for lyr in used:
            if lyr not in layers:
                missing_layers.add(lyr)
    for lyr in routed_layers:
        if lyr not in layers:
            missing_layers.add(lyr)

    return {
        "macro_count": len(macros),
        "layer_count": len(layers),
        "inst_count": len(instances),
        "missing_cells": missing_cells,
        "missing_layers": sorted(missing_layers),
    }

def main():
    ap = argparse.ArgumentParser(description="LEF/DEF 合法性检查")
    ap.add_argument("--lef", nargs="+", required=True, help="一个或多个 LEF 文件")
    ap.add_argument("--def", dest="def_file", required=True, help="DEF 文件")
    args = ap.parse_args()

    r = check(args.lef, args.def_file)

    print("=== LEF/DEF 检查报告 ===")
    print(f"LEF 单元(MACRO)数: {r['macro_count']}")
    print(f"LEF 布线层(LAYER)数: {r['layer_count']}")
    print(f"DEF 实例数: {r['inst_count']}")

    ok = True
    if r["missing_cells"]:
        ok = False
        print(f"\n[FAIL] 缺失单元定义 {len(r['missing_cells'])} 处:")
        for inst, cell in r["missing_cells"]:
            print(f"  实例 {inst} 引用的单元 {cell} 未在 LEF 中定义")
    else:
        print("\n[PASS] 所有实例引用的单元均在 LEF 中定义")

    if r["missing_layers"]:
        ok = False
        print(f"\n[FAIL] 缺失布线层定义 {len(r['missing_layers'])} 个:")
        for lyr in r["missing_layers"]:
            print(f"  层 {lyr} 未在 LEF 中定义")
    else:
        print("[PASS] 所有引用的布线层均在 LEF 中定义")

    print("\n结论:", "通过" if ok else "不通过")
    sys.exit(0 if ok else 1)

if __name__ == "__main__":
    main()

运行方式

python check_lef_def.py --lef tech.lef cells.lef --def design.def

可传入多个 LEF(工艺库 + 单元库),脚本会合并解析。

验证方法与通过条件

  • 运行后查看报告:若打印 [PASS] 且结论为“通过”,退出码为 0,说明单元与布线层定义完整。
  • 若存在缺失,报告会逐条列出缺失的单元(含实例名)或层名,退出码为 1。
  • 可用 echo $?(Linux/macOS)或 echo %ERRORLEVEL%(Windows)确认退出码。
  • 构造一个 DEF 引用 LEF 中不存在的单元,脚本应报 [FAIL] 并列出该实例,用于验证检查逻辑生效。

说明与边界

本脚本按段关键字做轻量文本解析,适用于标准 LEF/DEF 结构;对引脚完整性的检查聚焦于“实例引用的单元是否存在”,因为 DEF 中实例的引脚连接需结合 NETS 段解析,本案例将其作为扩展点。若需更严格的引脚级校验,可在 NETS 段解析每个网络的 (inst pin) 连接并比对 MACRO 的 PIN 集合。