GDSII 与 OASIS 版图文件格式:从第一性原理到芯片制造
ℹ 范围:从"掩模需要什么信息"出发,推导版图文件的最小数据模型;逐字节拆解 GDSII 与 OASIS;附完整的 SRAM 样例解码数据供读者复现;最后按工序说明 Fab / 掩模厂如何解析版图并完成芯片生产。
说明:本文所有字节级解码示例均附有可复现的解码脚本与完整输出。GDSII / OASIS 读写对照业界开源库 gdstk (github.com/heitzmann/gdstk)。Fab 流程为行业通用流程,具体参数以各厂 PDK 与 Tape-out 规范为准。
1. 第一性原理:版图文件要回答什么问题
1.1 制造端真正需要的输入
晶圆厂不关心电路"是什么",只关心每一道光刻"哪里透光、哪里不透光"。一颗芯片的制造输入,本质是 N 张二值平面图(每张对应一块掩模,N 在几十量级),外加把它们彼此对齐的坐标系。
| 制造端的硬需求 | 由此推出的约束 | 数据模型上的决策 |
|---|---|---|
| 图形必须精确到制造网格 | 浮点误差不可接受;布尔运算必须可精确判定 | 整数坐标 + 全局单位(数据库单位 DBU) |
| 每块掩模独立制作 | 图形必须按工艺层归类 | 每个图形带 层号(及数据类型) |
| 图形是"区域",不是线条 | 需要可做面积/布尔运算的表示 | 多边形(边界表示),线宽型走线可用 Path 简写 |
| SRAM、标准单元大量重复 | 平铺存储体积爆炸 | 层次化:单元定义一次,实例只存变换 |
| 验证需要知道哪根线是哪个网络 | 纯几何不足以做 LVS | 文本标注(Label)与可选属性 |
| 文件要跨工具、跨公司、跨几十年交换 | 格式必须自描述、与平台无关 | 定义明确的字节序列化规范 |
1.2 为什么不直接存位图
按光刻机最大曝光场 26 mm × 33 mm、1 nm 网格估算单层位图:
| 量 | 计算 | 结果 |
|---|---|---|
| 像素数 | 2.6×107 × 3.3×107 | ≈ 8.6×1014 |
| 1 bit/像素 | 8.6×1014 / 8 | ≈ 107 TB / 层 |
| 同一区域用多边形 | 只存边界顶点 | 与图形复杂度成正比,与面积无关 |
结论:几何必须用矢量(多边形)表示;再叠加层次复用,体积才能降到可交换的量级。位图化(栅格化)只发生在流程最末端——掩模写入机内部。
1.3 推导出的最小数据模型
| 概念 | 定义 | GDSII 叫法 | OASIS 叫法 |
|---|---|---|---|
| 库 | 一个文件;持有单位与全部单元 | Library(BGNLIB…ENDLIB) | 文件(START…END) |
| 单元 | 具名的图形容器,可被引用 | Structure(BGNSTR…ENDSTR) | CELL |
| 多边形 | 闭合顶点序列 + 层/类型 | BOUNDARY(BOX) | POLYGON / RECTANGLE / TRAPEZOID / CTRAPEZOID / CIRCLE |
| 路径 | 中心线 + 宽度 + 端头样式 | PATH | PATH |
| 实例 | 对另一单元的引用 + 变换 | SREF(单个)/ AREF(阵列) | PLACEMENT / PLACEMENT_TRANSFORM(+ 任意重复) |
| 标注 | 点 + 字符串 + 层/类型 | TEXT | TEXT |
| 属性 | 附着在元素上的键值 | PROPATTR / PROPVALUE | PROPERTY |
1.4 层次 = 有向无环图(DAG)
图 1-1单元引用 DAG — 箭头标注引用类型,叶子节点为实际多边形
• 边 = 引用(带变换);不允许成环
• 展平 flatten(TOP) = 对每条“根 → 叶”路径,把复合变换作用到叶子多边形上,再取并集
1.5 两种格式的定位
GDSII 与 OASIS 承载同一个数据模型,差别只在序列化方式:
| 维度 | GDSII Stream | OASIS |
|---|---|---|
| 出身 | Calma 公司 GDS II 系统的流格式(1970 年代末),最终版本 Release 6.0(HEADER=600) | SEMI P39(2004),为替代 GDSII 而设计;掩模写入子集为 SEMI P44(OASIS.MASK) |
| 设计哲学 | 定长、自同步、易实现 | 信息论意义上的紧凑:去掉一切可推导的冗余 |
| 代价 | 体积大,40 年前的位宽限制至今仍在 | 解析器是状态机,实现复杂度高一个量级 |
2. 共同的几何语义
2.1 单位与网格
| 概念 | 含义 | 典型值 |
|---|---|---|
| DBU(数据库单位) | 坐标整数的物理含义,所有几何都是 DBU 的整数倍 | 1 nm(先进节点常用 0.5 nm 或更细) |
| 用户单位 | 工具界面显示用的单位 | 1 µm |
| 制造网格 | 工艺允许的最小坐标步长,由 PDK 规定 | ≥ DBU,且为其整数倍 |
GDSII 在 UNITS 记录中同时给出"1 DBU = 多少用户单位"与"1 DBU = 多少米";OASIS 在 START 中给出"每微米多少个网格单位"。单位读错 = 整张图缩放错误,这是 Fab 接收检查的第一项。
2.2 实例变换
实例把子单元坐标 p 映射到父单元坐标 p',顺序固定为:先关于 X 轴镜像 → 再缩放 → 再旋转 → 最后平移。
p' = T + R(theta) * ( M * F(p) )
F(x, y) = (x, -y) if reflected else (x, y) GDSII: STRANS bit 0x8000 OASIS: info bit F
M = magnification GDSII: MAG (real8) OASIS: real
R(theta) = rotation, counter-clockwise, degrees GDSII: ANGLE (real8) OASIS: AA bits (k*90) or real
T = origin GDSII: XY OASIS: x, y (absolute or relative)
Nested: p_top = A1( A2( ... An(p_leaf) ) ) compose along the DAG path
| 变换类型 | 是否保持网格对齐 | 制造风险 |
|---|---|---|
| 平移(整数)、90° 倍数旋转、镜像 | 是 | 无 |
| 任意角度旋转 | 否 | 顶点落在网格外,需吸附(snap),产生锯齿与微小间隙 |
| 非整数放大 | 通常否 | 同上;多数 PDK 禁止 |
2.3 层号与数据类型
(layer, datatype) 二元组是图形的分类键。它不是掩模编号——两者之间隔着一张由 PDK 定义的层映射表(见 §7.4)。
| 字段 | 典型用途(具体编号以 PDK 为准) |
|---|---|
| layer | 工艺层:有源区、阱、多晶、接触孔、各层金属/通孔、标记层 |
| datatype | 同一层内的用途区分:drawing / pin / dummy(fill) / blockage / 多重曝光颜色 / OPC 排除区 |
| texttype | TEXT 专用的分类号,常用于区分端口标注与注释 |
3. GDSII:逐字节拆解
3.1 唯一的结构单元:记录
GDSII 文件就是一串首尾相接的记录,没有文件头、没有索引、没有压缩。单条记录的结构如图 3-1。
图 3-1GDSII 记录结构 — 每条记录 = 4 B 定长头 + 变长载荷
| 字节 0–1 记录长度 L uint16 | 字节 2 记录类型 | 字节 3 数据类型 | 字节 4 … L−1 载荷(L − 4 字节) |
| 2 字节 大端序 | 1 字节 0x00~0xFF | 1 字节 0~6 | 变长,共 L−4 字节;内容由记录类型决定(坐标 / 字符串 / 实数 / 位字段等) |
| 规则 | 后果 |
|---|---|
| 长度字段 16 位 | 单条记录 ≤ 65534 B → 单个 XY 最多 8191 个点 |
| 长度含 4 字节头且为偶数 | 读取器只需"读 4 字节 → 读 L−4 字节"循环即可遍历全文件 |
| 全部大端序 | 与平台无关;小端机器读取时需逐字段字节交换 |
| 文件常补零至 2048 B 的整数倍 | 磁带时代遗留;ENDLIB 之后的 0x00 应忽略 |
3.2 数据类型(字节 3)
| 码 | 类型 | 单元宽度 | 典型记录 |
|---|---|---|---|
| 0 | 无数据 | 0 | BOUNDARY、ENDEL、ENDSTR、ENDLIB |
| 1 | 位数组 | 2 B | STRANS、PRESENTATION、ELFLAGS |
| 2 | 有符号整数 | 2 B | HEADER、BGNLIB、LAYER、DATATYPE、COLROW、PATHTYPE |
| 3 | 有符号整数 | 4 B | XY、WIDTH、BGNEXTN、ENDEXTN |
| 4 | 实数 | 4 B | 规范保留,实际不用 |
| 5 | 实数(Real8) | 8 B | UNITS、MAG、ANGLE |
| 6 | ASCII 串 | 变长 | LIBNAME、STRNAME、SNAME、STRING;奇数长度补 \0 |
3.3 Real8:不是 IEEE 754
GDSII 的 8 字节实数沿用 IBM 大型机的十六进制浮点格式(指数 excess-64),位段如图 3-2。
图 3-2Real8 位段布局 — 不是 IEEE 754,底数 16,指数偏移 64
| bit 63 S 符号 1 位 | bit 62–56 E 指数 7 位 | bit 55 … 0 M 尾数(小数部分) 56 位,无隐含位 |
| 对比项 | Real8(GDSII) | IEEE 754 double |
|---|---|---|
| 底数 | 16 | 2 |
| 指数 | 7 bit,偏移 64 | 11 bit,偏移 1023 |
| 尾数 | 56 bit,无隐含位 | 52 bit + 隐含位 |
value = (−1)S × (M / 256) × 16(E − 64) 规格化:1/16 ≤ M/256 < 1
解码用 C 代码表达(对照 gdstk gdsii.cpp gdsii_real_to_double):
double gdsii_real_to_double(uint64_t real) {
int64_t exponent = ((real & 0x7F00000000000000) >> 54) - 256;
double mantissa = (double)(real & 0x00FFFFFFFFFFFFFF) / 72057594037927936.0;
double result = mantissa * exp2((double)exponent);
return (real & 0x8000000000000000) ? -result : result;
}
| 对比 | GDSII Real8 | IEEE 754 double |
|---|---|---|
| 底数 | 16 | 2 |
| 指数 | 7 bit,偏移 64 | 11 bit,偏移 1023 |
| 尾数 | 56 bit,无隐含位;十六进制规格化下首位可有至多 3 个前导 0,有效精度 53~56 bit | 52 bit + 1 隐含位 |
| 范围 | 约 10−78 ~ 1075 | 约 10−308 ~ 10308 |
3.4 记录类型(字节 2)
| 类别 | 记录(十六进制码) |
|---|---|
| 库级 | HEADER 00 · BGNLIB 01 · LIBNAME 02 · UNITS 03 · ENDLIB 04 · REFLIBS 1F · FONTS 20 · GENERATIONS 22 · ATTRTABLE 23 · FORMAT 36 · MASK 37 · ENDMASKS 38 · LIBDIRSIZE 39 · SRFNAME 3A · LIBSECUR 3B |
| 单元级 | BGNSTR 05 · STRNAME 06 · ENDSTR 07 · STRCLASS 34 |
| 元素起始 | BOUNDARY 08 · PATH 09 · SREF 0A · AREF 0B · TEXT 0C · NODE 15 · BOX 2D |
| 元素结束 | ENDEL 11 |
| 元素属性 | ELFLAGS 26 · PLEX 2F · LAYER 0D · DATATYPE 0E · TEXTTYPE 16 · NODETYPE 2A · BOXTYPE 2E · WIDTH 0F · PATHTYPE 21 · BGNEXTN 30 · ENDEXTN 31 · XY 10 · SNAME 12 · COLROW 13 · STRANS 1A · MAG 1B · ANGLE 1C · PRESENTATION 17 · STRING 19 |
| 用户属性 | PROPATTR 2B · PROPVALUE 2C |
| 废弃/保留 | TEXTNODE 14 · SPACING 18 · UINTEGER 1D · USTRING 1E · STYPTABLE 24 · STRTYPE 25 · ELKEY 27 · LINKTYPE 28 · LINKKEYS 29 · TAPENUM 32 · TAPECODE 33 · RESERVED 35 |
| 厂商扩展 | Raith 电子束:0x5A(MBMS PATH)、0x62(PXXDATA) |
3.5 文件语法
<file> ::= HEADER BGNLIB [LIBDIRSIZE] [SRFNAME] [LIBSECUR] LIBNAME
[REFLIBS] [FONTS] [ATTRTABLE] [GENERATIONS] [<format>]
UNITS { <structure> }* ENDLIB
<structure> ::= BGNSTR STRNAME [STRCLASS] { <element> }* ENDSTR
<element> ::= { <boundary> | <path> | <sref> | <aref> | <text> | <node> | <box> }
{ PROPATTR PROPVALUE }* ENDEL
<boundary> ::= BOUNDARY [ELFLAGS] [PLEX] LAYER DATATYPE XY
<path> ::= PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE] [WIDTH]
[BGNEXTN] [ENDEXTN] XY
<sref> ::= SREF [ELFLAGS] [PLEX] SNAME [STRANS [MAG] [ANGLE]] XY
<aref> ::= AREF [ELFLAGS] [PLEX] SNAME [STRANS [MAG] [ANGLE]] COLROW XY
<text> ::= TEXT [ELFLAGS] [PLEX] LAYER TEXTTYPE [PRESENTATION] [PATHTYPE]
[WIDTH] [STRANS [MAG] [ANGLE]] XY STRING
<node> ::= NODE [ELFLAGS] [PLEX] LAYER NODETYPE XY
<box> ::= BOX [ELFLAGS] [PLEX] LAYER BOXTYPE XY
GDSII 是"开始标记 + 平铺属性 + 结束标记"的扁平语法:没有嵌套长度,层次只靠 BGNSTR/ENDSTR、元素头/ENDEL 两级括号表达。
3.6 各元素的字段语义
| 元素 | XY 内容 | 关键字段 | 备注 |
|---|---|---|---|
| BOUNDARY | n+1 个点,末点重复首点(显式闭合) | LAYER、DATATYPE | n ≥ 3;n+1 ≤ 8191,故顶点 ≤ 8190。超限多边形写出时须切分:gdstk 由 write_gds 的 max_points 参数控制,本项目在 gds_writer.hpp 中取 kGdsMaxPoints = 8190 |
| PATH | 中心线顶点 | WIDTH(负值 = 绝对宽度,不随 MAG 缩放);PATHTYPE | PATHTYPE:0 平头(Flush)、1 圆头(Round)、2 延伸半线宽方头(HalfWidth)、4 自定义延伸(长度由 BGNEXTN/ENDEXTN 给出) |
| SREF | 1 点:实例原点 | SNAME、STRANS、MAG、ANGLE | SNAME 可引用文件后面才定义的单元 |
| AREF | 3 点:P0 原点、P1 列方向终点、P2 行方向终点 | COLROW(列数、行数,各 int16) | 第 (i,j) 个实例原点 = P0 + i·(P1−P0)/cols + j·(P2−P0)/rows |
| TEXT | 1 点:锚点 | TEXTTYPE、PRESENTATION、STRING | 不产生掩模图形;用于 LVS 端口/网络命名 |
| BOX | 5 点 | BOXTYPE | 历史遗留,通常按多边形读取 |
| NODE | 1~50 点 | NODETYPE | 电气节点;现代流程基本不用,OASIS 无对应 |
3.6.1 位字段
| 记录 | 掩码 | 含义 |
|---|---|---|
| STRANS | 0x8000 | 先关于 X 轴镜像 |
| 0x0004 | 绝对放大(不与父级 MAG 复合) | |
| 0x0002 | 绝对角度(不与父级 ANGLE 复合) | |
| PRESENTATION | 0x0030 | 字体号 0~3 |
| 0x000C | 垂直对齐:0 上、1 中、2 下 | |
| 0x0003 | 水平对齐:0 左、1 中、2 右 |
STRANS 的两个"绝对"标志实际很少被工具支持,gdstk 读到时会报 UnsupportedRecord。
3.6.2 库级字段
| 记录 | 载荷 | 说明 |
|---|---|---|
| HEADER | int16 版本号 | 3、4、5、600 等;600 即 Release 6.0 |
| BGNLIB / BGNSTR | 12 × int16 | 两组"年 月 日 时 分 秒":最后修改、最后访问 |
| UNITS | 2 × Real8 | [0] 1 DBU 折合多少用户单位;[1] 1 DBU 折合多少米 |
| REFLIBS / FONTS | 44 字节定长槽 × 2 / × 4 | 引用库名、字体文件名;可选 |
3.7 实例:SRAM.gds 逐字节解剖
样例文件 4096 B,含 1 个单元 "Cell"、45 个 BOUNDARY、分布在 10 个层上(1、2、3、5、6、7、8、40、50、51)。有效内容 3150 B(到 ENDLIB 为止),其后补零至 2048 B 的整数倍。
3.7.1 库头 — 逐记录走读
| 偏移 | 原始字节 | 记录 | 解码 |
|---|---|---|---|
| 0 | 00 06 00 02 00 05 | HEADER | L=6, type=0x00, dtype=2(int16), payload=00 05 → 版本 5 |
| 6 | 00 1C 01 02 00 74 00 07 00 19 00 03 00 2D 00 35 00 74 00 07 00 19 00 03 00 2D 00 35 | BGNLIB | L=28, type=0x01, dtype=2, 12×int16: 修改时间 2016-07-25 03:45:53, 访问时间同上 |
| 34 | 00 0C 02 06 53 52 41 4D 2E 67 64 73 | LIBNAME | L=12, dtype=6(ASCII): "SRAM.gds" |
| 46 | 00 14 03 05 3E 41 89 37 4B C6 A7 F0 39 44 B8 2F A0 9B 5A 54 | UNITS | L=20, dtype=5(real8): [0]=0.001 µm/DBU, [1]=1×10−9 m/DBU → DBU = 1.0 nm |
3.7.2 第一个多边形(8 边形、斜边)— 完整记录序列
| 偏移 | 字节 | 记录 | 字段值 |
|---|---|---|---|
| 66–93 | 00 1C 05 02 … | BGNSTR | 单元时间戳 |
| 94–101 | 00 08 06 06 43 65 6C 6C | STRNAME | "Cell" |
| 102–105 | 00 04 08 00 | BOUNDARY | (无数据记录) |
| 106–111 | 00 06 0D 02 00 01 | LAYER | 1 |
| 112–117 | 00 06 0E 02 00 00 | DATATYPE | 0 |
| 118–193 | 00 4C 10 03 FF FF FE F2 … | XY | L=76, dtype=3(int32), 9 点(8 边形 + 闭合点) |
| 194–197 | 00 04 11 00 | ENDEL | (无数据记录) |
3.7.3 XY 坐标展开(第一个多边形,全部 9 点)
| # | DBU 整数 (x, y) | 物理坐标 (µm) | 说明 |
|---|---|---|---|
| 0 | −270, 40 | −0.270, 0.040 | 起点 |
| 1 | −295, −40 | −0.295, −0.040 | 左下斜边 |
| 2 | −295, −182 | −0.295, −0.182 | ↓竖直 |
| 3 | −345, −182 | −0.345, −0.182 | ←水平 |
| 4 | −345, −40 | −0.345, −0.040 | ↑竖直 |
| 5 | −370, 40 | −0.370, 0.040 | 左上斜边 |
| 6 | −370, 182 | −0.370, 0.182 | ↑竖直 |
| 7 | −270, 182 | −0.270, 0.182 | →水平(回到 x 起点) |
| 8 | −270, 40 | −0.270, 0.040 | 闭合点 = 第 0 点 |
3.7.4 第二个多边形(矩形)— 完整记录序列
| 偏移 | 字节 | 记录 | 字段值 |
|---|---|---|---|
| 198–201 | 00 04 08 00 | BOUNDARY | |
| 202–207 | 00 06 0D 02 00 01 | LAYER | 1 |
| 208–213 | 00 06 0E 02 00 00 | DATATYPE | 0 |
| 214–257 | 00 2C 10 03 FF FF FF 38 … | XY | L=44, 5 点: (−200,−78)→(−50,−78)→(−50,182)→(−200,182)→(−200,−78) |
| 258–261 | 00 04 11 00 | ENDEL | |
| 本元素总计 64 B(BOUNDARY 4 + LAYER 6 + DATATYPE 6 + XY 44 + ENDEL 4) | |||
3.7.5 全文件记录统计
| 记录类型 | 出现次数 |
|---|---|
| HEADER / BGNLIB / LIBNAME / UNITS / ENDLIB | 各 1 |
| BGNSTR / STRNAME / ENDSTR | 各 1 |
| BOUNDARY | 45 |
| LAYER | 45 |
| DATATYPE | 45 |
| XY | 45 |
| ENDEL | 45 |
| 合计 | 233 |
3.7.6 全部 45 个多边形的层分布
| layer | datatype | 数量 | 层用途(典型 PDK 含义) |
|---|---|---|---|
| 1 | 0 | 16 | 有源区 (OD / DIFF) |
| 2 | 0 | 7 | N 阱 (NWELL) |
| 3 | 0 | 2 | P 型注入 (PIMP) |
| 5 | 0 | 1 | 多晶硅 (PO / POLY) |
| 6 | 0 | 5 | 接触孔 (CO / CONT) |
| 7 | 0 | 5 | 第一层金属 (M1 / Metal1) |
| 8 | 0 | 1 | 第一层通孔 (V1 / Via1) |
| 40 | 0 | 4 | N 型注入 (NIMP) |
| 50 | 0 | 1 | 标记 / 识别层 |
| 51 | 0 | 3 | 另一标记层 |
3.8 GDSII 的结构性局限
| 局限 | 根因 | 工程影响 |
|---|---|---|
| 体积大 | 坐标恒 4 B、绝对坐标、闭合点重复、每元素重写层号 | 先进节点 OPC 后数据量常达数百 GB 级 |
| XY ≤ 8191 点 | 16 位记录长度 | 大多边形须切分,增加顶点与边界 |
| 坐标 int32 | ±2.1×109 DBU | DBU 越细,可表达范围越小(0.25 nm DBU 时约 ±0.54 m,仍够用) |
| layer/datatype 原为 0~255 | 早期规范 | 现代工具普遍扩展到 0~32767(int16)甚至无符号 16 位 |
| 无索引 | 纯顺序流 | 读一个单元也要扫全文件;无法并行读 |
| 只有引用能阵列化 | AREF 只能阵列化单元引用 | 重复的多边形(如通孔阵列)只能逐个存或另建单元 |
| 无完整性校验 | 无校验和字段 | 传输损坏需依赖外部 MD5/SHA |
4. OASIS:逐字节拆解
4.1 设计思路:逐项消灭 GDSII 的冗余
| GDSII 冗余 | OASIS 机制 | 典型收益 |
|---|---|---|
| 每条记录 4 B 头 | 1 B 记录号,长度由内容自推导 | 小元素头开销降 75% |
| 整数恒 2/4 B | 变长整数(7 bit/字节) | 小数值 1 B |
| 绝对坐标 | 首点 + 增量;XYRELATIVE 模式下位置也相对上一元素 | 增量远小于绝对值 |
| 曼哈顿多边形存 x、y 两个分量 | 点列表类型 0/1:每点只存一个分量;末点隐含 | 矩形 2 个数 |
| 矩形存 5 点 | RECTANGLE:宽、高(正方形只存一个) | — |
| 每元素重写层号、宽度等 | 模态变量:省略即沿用上一个值 | 同层连续图形省掉层号 |
| 只能阵列化引用 | 任意元素可带重复(12 种模式:矩阵、单行、显式偏移、斜格、散点…) | 通孔阵列 1 条记录 |
| 引用名每次写全 | 名称表 + 整数索引 | 长单元名只存一次 |
| 剩余统计冗余 | CBLOCK:DEFLATE 压缩任意记录段 | 再降一截 |
| 无索引、无校验 | END 中的表偏移、单元偏移属性、CRC32/校验和 | 随机访问、并行读、完整性验证 |
4.2 文件骨架
"%SEMI-OASIS\r\n" magic, 13 bytes
START version="1.0" unit offset-flag [6 x (flag, offset)] if offset-flag == 0
{ PAD | PROPERTY | name-record }*
{ CELL_REF_NUM(index) | CELL(name)
{ XYABSOLUTE | XYRELATIVE | PLACEMENT | PLACEMENT_TRANSFORM | TEXT
| RECTANGLE | POLYGON | PATH | TRAPEZOID* | CTRAPEZOID | CIRCLE
| PROPERTY | LAST_PROPERTY | XELEMENT | XGEOMETRY | CBLOCK | PAD }*
}*
name-record ::= CELLNAME | TEXTSTRING | PROPNAME | PROPSTRING | LAYERNAME | XNAME
(may appear before, between or after cells)
END [6 x (flag, offset)] padding(b-string) validation-scheme [signature 4B]
-> END record is padded to exactly 256 bytes
4.3 基本编码
4.3.1 整数(Varint)
| 类型 | 编码 | 例 |
|---|---|---|
| unsigned-integer | 小端 7 bit 分组,每字节最高位 = "后面还有" | 300 = 0b1_0010_1100 → AC 02 |
| signed-integer | 先把 (|v|<<1) | sign 作为无符号数编码 | −200 → 401 → 91 03;+150 → 300 → AC 02 |
4.3.2 实数(首字节为类型标记)
| 类型 | 含义 | 后续内容 |
|---|---|---|
| 0 / 1 | 正 / 负整数 | unsigned |
| 2 / 3 | 正 / 负倒数 1/n | unsigned n |
| 4 / 5 | 正 / 负分数 n/d | unsigned n, unsigned d |
| 6 | IEEE float32 | 4 B 小端 |
| 7 | IEEE float64 | 8 B 小端 |
写出器应优先用精确的整数/倒数/分数形式;只有无法精确表示时才退化为浮点。本文样例就是反例:1e-6 / 1e-9 在 IEEE 754 双精度下算得 999.9999999999999 而非 1000,gdstk 只能写成 9 字节的 float64,而不是 1 字节的整数(见 §4.11.1)。
4.3.3 字符串
| 类型 | 允许字符 | 用途 |
|---|---|---|
| a-string | 可打印 ASCII + 空格(0x20–0x7E) | 文本标注 |
| b-string | 任意字节 | 二进制属性值、END 填充 |
| n-string | 可打印且无空格(0x21–0x7E),长度 > 0 | 单元名、属性名 |
三者编码相同:unsigned 长度 + 原始字节。
4.3.4 位移量(Delta)
| 类型 | 方向 | 编码 | 用于 |
|---|---|---|---|
| 1-delta | 单轴 | signed-integer | 曼哈顿点列表(type 0/1)、梯形腰部偏移 |
| 2-delta | 4 方向 (E/N/W/S) | unsigned:低 2 bit 方向,其余为长度 | 曼哈顿任意序点列表(type 2) |
| 3-delta | 8 方向 (含 NE/NW/SW/SE) | unsigned:低 3 bit 方向,其余为长度 | 八角点列表(type 3) |
| g-delta 形式 1 | 8 方向 | unsigned:bit0=0,bit1–3 方向,>>4 为长度 | 任意角度点列表(type 4/5)、重复向量 |
| g-delta 形式 2 | 任意方向 | unsigned:bit0=1,bit1 = x 符号,>>2 为 |x|;再跟一个 signed y | 同上 |
4.4 记录类型表
| ID | 记录 | ID | 记录 |
|---|---|---|---|
| 0 | PAD | 17 | PLACEMENT(90° 倍数旋转) |
| 1 | START | 18 | PLACEMENT_TRANSFORM(任意角度/放大) |
| 2 | END | 19 | TEXT |
| 3 / 4 | CELLNAME(隐式 / 显式编号) | 20 | RECTANGLE |
| 5 / 6 | TEXTSTRING | 21 | POLYGON |
| 7 / 8 | PROPNAME | 22 | PATH |
| 9 / 10 | PROPSTRING | 23–25 | TRAPEZOID(AB / A / B) |
| 11 / 12 | LAYERNAME(几何层 / 文本层) | 26 | CTRAPEZOID(26 种预定义) |
| 13 | CELL_REF_NUM(按索引引用单元) | 27 | CIRCLE |
| 14 | CELL_NAME(按名称引用单元) | 28 / 29 | PROPERTY / LAST_PROPERTY |
| 15 | XYABSOLUTE | 30 / 31 | XNAME |
| 16 | XYRELATIVE | 32 / 33 | XELEMENT / XGEOMETRY |
| 34 | CBLOCK(DEFLATE 压缩块) |
4.5 info 字节:每条元素记录的"存在位图"
几何、引用、文本记录紧跟一个 info 字节,每一位表示某个字段本条是否显式给出;未给出的字段取对应模态变量的当前值。
| 记录 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
|---|---|---|---|---|---|---|---|---|
| RECTANGLE | S 正方形 | W 宽 | H 高 | X | Y | R 重复 | D 类型 | L 层 |
| POLYGON | 0 | 0 | P 点列表 | X | Y | R | D | L |
| PATH | E 端头 | W 半宽 | P | X | Y | R | D | L |
| TRAPEZOID | O 竖向 | W | H | X | Y | R | D | L |
| CTRAPEZOID | T 形状码 | W | H | X | Y | R | D | L |
| CIRCLE | 0 | 0 | r 半径 | X | Y | R | D | L |
| TEXT | 0 | C 显式文本 | N 按索引 | X | Y | R | T 文本类型 | L 文本层 |
| PLACEMENT | C 显式单元 | N 按索引 | X | Y | R | AA k×90° | F 镜像 | |
| PLACEMENT_TRANSFORM | C | N | X | Y | R | M 放大 | A 角度 | F |
| PROPERTY | UUUU 值个数(15=另读) | V 沿用 | C 显式名 | N 按索引 | S 标准属性 | |||
字段在字节流中的出现顺序与位顺序不一定相同(例如 POLYGON 的字段顺序为 L → D → P → X → Y → R),以规范为准。
4.6 模态变量:OASIS 解析器的核心状态
| 模态变量 | 被谁读写 |
|---|---|
| xy-mode(绝对/相对) | XYABSOLUTE / XYRELATIVE 设置;影响所有位置字段 |
| placement-x / placement-y | PLACEMENT* |
| geometry-x / geometry-y | 所有几何记录共享 |
| text-x / text-y | TEXT |
| layer / datatype | 所有几何记录共享 |
| textlayer / texttype | TEXT |
| geometry-w / geometry-h | RECTANGLE、TRAPEZOID、CTRAPEZOID |
| polygon-point-list / path-point-list | POLYGON / PATH |
| path-halfwidth / start-extension / end-extension | PATH |
| ctrapezoid-type / circle-radius | CTRAPEZOID / CIRCLE |
| repetition | 所有可重复记录共享(重复类型 0 = 沿用) |
| placement-cell / text-string | PLACEMENT* / TEXT |
| last-property-name / last-value-list | PROPERTY |
| 时机 | 规则 |
|---|---|
| 文件开始、每个 CELL 开始 | xy-mode = 绝对,三组位置 = (0, 0);其余模态变量变为"未定义" |
| 引用未定义的模态变量 | 文件非法,解析器应报错 |
| 相对模式 | 本条位置 = 上一条同类(几何/引用/文本各自独立)元素位置 + 本条增量 |
推论:OASIS 记录不能脱离上下文单独解析——要知道一个多边形在哪一层,必须从它所在 CELL 的开头顺序执行到它。这也是单元成为最小随机访问单位的原因。
4.7 点列表类型
| 类型 | 适用 | 每点编码 | 多边形闭合 |
|---|---|---|---|
| 0 | 曼哈顿,先水平 | 1-delta,水平/垂直交替 | 最后两条边隐含,不写 |
| 1 | 曼哈顿,先垂直 | 1-delta,垂直/水平交替 | 同上 |
| 2 | 曼哈顿,任意顺序 | 2-delta | 最后一条边隐含 |
| 3 | 八角(0°/45°/90°…) | 3-delta | 同上 |
| 4 | 任意角度 | g-delta(相对首点逐点累加) | 同上 |
| 5 | 任意角度 | g-delta,增量为二阶差分 | 同上 |
4.8 重复(Repetition)
任何几何、引用、文本都可携带重复,副本数 = 1(原件)+ 偏移数。计数字段统一存"总数 − 2"。
| 类型 | 形状 | 字段 |
|---|---|---|
| 0 | 沿用上一个 | 无 |
| 1 | 正交矩阵 | cols−2, rows−2, dx, dy |
| 2 / 3 | 单行 / 单列 | n−2, 间距 |
| 4 / 5 | X 不等距 / 带网格倍数 | n−2, [grid], 逐个间距 |
| 6 / 7 | Y 不等距 / 带网格倍数 | 同上 |
| 8 | 斜格矩阵 | n−2, m−2, 两个 g-delta 向量 |
| 9 | 斜向单行 | n−2, 一个 g-delta 向量 |
| 10 / 11 | 任意散点 / 带网格倍数 | n−2, [grid], 逐个 g-delta |
4.9 名称表与前向引用
名称表(CELLNAME、TEXTSTRING、PROPNAME、PROPSTRING、LAYERNAME)可以出现在文件末尾——gdstk 就把所有表写在单元之后。因此读取器要么先用 END 中的表偏移跳过去读表,要么先存索引、读到 END 后统一回填名字。
4.10 CBLOCK 与 END
CBLOCK 存储 raw DEFLATE(RFC 1951,无 zlib header,对应 inflateInit2(…, -15))压缩的普通记录序列;典型写法是一个单元体一个 CBLOCK。CBLOCK 不可嵌套。
END 记录恰好 256 B,包含 6 张名称表的文件偏移、b-string 填充和校验方案(0=无、1=CRC32、2=checksum32)。读取器可直接 seek 到距文件尾 256 B 处读取。
4.11 实例:SRAM.oas 逐字节解剖
由 SRAM.gds 经 gdstk 转换而来(write_oas,DEFLATE 级别 6,config_flags=0:不检测矩形、不写标准属性、不写校验)。文件 698 B。
4.11.1 顶层记录走读
| 偏移 | 原始字节 | 记录 | 解码 |
|---|---|---|---|
| 0–12 | 25 53 45 4D 49 2D 4F 41 53 49 53 0D 0A | magic | "%SEMI-OASIS\r\n" |
| 13 | 01 | START | 记录 ID 1 |
| 14–17 | 03 31 2E 30 | 版本 | "1.0"(a-string:长度 3 + 3 字节内容) |
| 18–26 | 07 FF FF FF FF FF 3F 8F 40 | unit | 类型 7 (float64),值 ≈ 999.9999999999999(为何不是整数 1000 见 §4.3.2) |
| 27 | 01 | offset-flag | 1 = 表偏移在 END 里 |
| 28–29 | 0D 00 | CELL_REF_NUM | 单元名索引 0(对应后面的 CELLNAME 表第 0 项) |
| 30–35 | 22 00 FB 04 90 03 | CBLOCK | comp-type=0(DEFLATE),uncomp=635 B,comp=400 B |
| 36–435 | (400 B) | DEFLATE 数据 | 解压后为 45 个 POLYGON 记录 |
| 436–441 | 03 04 43 65 6C 6C | CELLNAME_IMPLICIT | 索引 0 = "Cell" |
| 442 | 02 | END | 记录 ID 2 |
| 443–445 | 01 B4 03 | CELLNAME 表 | flag=1(strict), offset=436 (0x01B4) |
| 446–455 | 01 00×5 | 其余 5 表 | 均 flag=1,offset=0(表不存在) |
| 456–696 | EF 01+239×00 | 填充 b-string | 长度 239 |
| 697 | 00 | 校验方案 | 0(无校验) |
| END 合计 256 B ✓(偏移 442–697:记录号 1 + CELLNAME 表 3 + 其余 5 表 10 + 填充 241(长度 2 + 内容 239)+ 校验方案 1) | |||
4.11.2 解压后的前两个多边形 — 逐字节解码
多边形 #0(8 顶点斜边,对应 GDS 层 1):
| 字节 | 含义 | 详细解码 |
|---|---|---|
15 | 记录 ID 21 = POLYGON | |
3B | info = 00111011 | bit0=1(L), bit1=1(D), bit3=1(Y), bit4=1(X), bit5=1(P) → layer, datatype, x, y, point-list 均显式 |
01 | layer = 1 | unsigned-integer, 1 字节 |
00 | datatype = 0 | unsigned-integer, 1 字节 |
04 | 点列表类型 = 4 | "arbitrary"(有斜边,用 g-delta) |
07 | 点数 = 7 | unsigned-integer, 1 字节 |
67 | g-delta #1 | 形式 2: 0x67=103, bit0=1→形式2, bit1=1→x为负, x=103>>2=25→−25; y=… |
| 7 个 g-delta 完整解码:(−25,−80), (0,−142), (−50,0), (0,142), (−25,80), (0,142), (100,0) | ||
9D 04 | x 位置 | signed-integer → −270 |
50 | y 位置 | signed-integer → 40 |
| 首点 (x,y)=(−270,40),绝对顶点序列: (−270,40), (−295,−40), (−295,−182), (−345,−182), (−345,−40), (−370,40), (−370,182), (−270,182) → 回到原点 | ||
| 本元素 25 B;同一多边形在 GDSII 中 96 B | ||
多边形 #1(矩形,对应 GDS 层 1):
| 字节 | 含义 | 详细解码 |
|---|---|---|
15 3B 01 00 | POLYGON, info, layer=1, datatype=0 | 同前 |
00 | 点列表类型 = 0 | "manhattan_horizontal"(曼哈顿,先水平,用 1-delta) |
02 | 点数 = 2 | 类型 0:Δx, Δy(第 3、4 点隐含,不写) |
AC 02 | 1-delta Δx = +150 | signed-integer: 300>>1=150 |
88 04 | 1-delta Δy = +260 | signed-integer: 520>>1=260 |
91 03 | x = −200 | signed-integer: 401>>1=200, LSB=1→负 |
9D 01 | y = −78 | signed-integer: 157>>1=78, LSB=1→负 |
| 矩形:(−200,−78) × 150 × 260。本元素 14 B;同一矩形在 GDSII 中 64 B | ||
4.11.3 全部 45 个 OASIS 多边形解码汇总
以下是通过解析脚本解出的全部 45 个多边形的层、类型、点数和字节大小。完整解码数据见 §9.5。
| # | layer | datatype | 点列表类型 | delta 数 | 字节 | 首点 (DBU) |
|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 4 (arbitrary) | 7 | 25 | (−270, 40) |
| 1 | 1 | 0 | 0 (manhattan) | 2 | 14 | (−200, −78) |
| 2 | 1 | 0 | 0 | 2 | 13 | (50, 77) |
| 3 | 1 | 0 | 4 | 7 | 25 | (270, −40) |
| … | (共 45 个;各记录字节数之和 635 B,与 CBLOCK 声明的解压尺寸一致) | |||||
各多边形按层统计与 GDS 完全一致:layer 1—16 个、layer 2—7 个、layer 3—2 个、layer 5—1 个、layer 6—5 个、layer 7—5 个、layer 8—1 个、layer 40—4 个、layer 50—1 个、layer 51—3 个。几何无损,层与坐标完全对应。
5. GDSII 与 OASIS 对比
5.1 结构对比
| 维度 | GDSII | OASIS |
|---|---|---|
| 记录头 | 4 B(长度 + 类型 + 数据类型) | 1 B 记录号 |
| 整数 | 定长 int16/int32,大端 | 变长 7 bit 分组 |
| 实数 | Real8(底 16,excess-64) | 整数/倒数/分数/IEEE float32/64 |
| 坐标 | 绝对,int32 | 增量 + 模态位置;格式本身不限位宽(实现多用 int64) |
| 上下文 | 无状态,每条记录可独立理解 | 有状态,依赖模态变量 |
| 基本几何 | BOUNDARY、PATH、BOX | RECTANGLE、POLYGON、PATH、TRAPEZOID、CTRAPEZOID、CIRCLE |
| 重复 | 仅 AREF | 12 种重复,适用于任意元素 |
| 名字 | 内联 ASCII | 名称表 + 索引 |
| 压缩 | 无 | 内建 CBLOCK(DEFLATE) |
| 索引/随机访问 | 无 | END 表偏移 + S_CELL_OFFSET |
| 完整性 | 无 | CRC32 / checksum32 |
| 多边形顶点上限 | 8190 | 无格式上限 |
5.2 体积实测
| 对象 | GDSII | OASIS | 比例 |
|---|---|---|---|
| 一个矩形 | 64 B | 14 B | 4.6 : 1 |
| 一个 8 顶点斜边多边形 | 96 B | 25 B | 3.8 : 1 |
| 单元体(45 个多边形) | 3080 B | 635 B(未压缩)/ 400 B(DEFLATE) | 4.9 : 1 / 7.7 : 1 |
| 整个文件 | 4096 B(含 946 B 填充) | 698 B(含 256 B END) | 5.9 : 1 |
样例极小,固定开销(END 256 B)占比高。真实全芯片数据中,OASIS 文件通常比 GDSII 小一个数量级左右。
5.3 互转的信息损失
| 转换路径 | 几何 | 丢失/改变 |
|---|---|---|
| GDSII → OASIS | 无损 | BGNLIB/BGNSTR 时间戳、HEADER 版本号无处存放;NODE 丢弃;BOX 变 POLYGON |
| OASIS → GDSII | 通常无损 | CIRCLE 须多边形化(引入近似误差);非引用元素上的重复须展开或另建单元;超 8190 顶点须切分 |
6. 解析器如何工作
6.1 GDSII 读取:两遍式流解析
对应于 gdstk read_gds (library.cpp)
loop:
read 4 bytes -> L(u16 BE), rec, dtype ; require L >= 4
read L-4 bytes payload ; byte-swap by dtype (2/4/8-byte units)
switch rec:
UNITS : factor = db_in_meters / target_unit
BGNSTR : cell = new Cell
STRNAME : cell.name = payload ; library.cells += cell
BOUNDARY/BOX : cur = new Polygon -> cell.polygons
PATH : cur = new Path -> cell.paths
SREF/AREF : cur = new Reference(mag=1) -> cell.refs
TEXT : cur = new Label -> cell.labels
LAYER/DATATYPE/TEXTTYPE/WIDTH/... : set field on cur
XY : polygon -> points * factor (drop repeated closing point at ENDEL)
ref -> origin; if AREF: derive column/row vectors from P1, P2
ENDEL : cur = null
SNAME : cur.target_name = payload # may be FORWARD reference
ENDLIB : build map name -> cell ; resolve every ref.target_name ; done
other : warn "unsupported record", skip
| 要点 | 说明 |
|---|---|
| 第二遍只解析引用 | SNAME 可能指向后面才出现的单元,必须在 ENDLIB 后统一建名字表回填 |
| 坐标立即乘 factor | DBU 整数 → 物理单位;Fab 内部工具通常保持整数不转浮点 |
| 未知记录可跳过 | 长度自描述,跳过不影响后续——这是 GDSII 健壮性的来源 |
| 按层过滤可在读时完成 | ENDEL 时检查 (layer, datatype) 不在白名单即丢弃,大幅省内存 |
6.2 OASIS 读取:状态机 + 延迟名字解析
对应于 gdstk read_oas (library.cpp)
check 14 bytes == "%SEMI-OASIS\r\n" + START(0x01)
version = string ; require "1.0"
unit = real ; factor = 1/unit ; precision = 1e-6 * factor
offset_flag = uint ; if 0: read 12 uints (table offsets in START)
modal = { xy_absolute: true, positions: (0,0), others: undefined }
loop: rec = 1 byte
CELL_REF_NUM/CELL : new cell ; reset positions & xy-mode
XYABSOLUTE/XYRELATIVE : set xy-mode
RECTANGLE/POLYGON/PATH/TRAPEZOID/... :
info = 1 byte
for each field in record order:
if info bit set: read field, update modal variable
use modal variable value # error if still undefined
position = absolute ? value : modal_pos + value
if R bit: read repetition (type 0 = reuse modal repetition)
PROPERTY : attach to the most recent element/cell/name record
CELLNAME/... : append to name tables
CBLOCK : inflate raw DEFLATE -> switch input to decompressed buffer
END : resolve cell/text/property indices via name tables ; resolve refs ; done
| 要点 | 说明 |
|---|---|
| 输入透明切换 | CBLOCK 解压后从内存缓冲取字节,状态机无感知 |
| 属性挂载靠"最近对象"指针 | PROPERTY 没有显式归属字段,挂在最近完成的元素或名称记录上 |
| 大文件并行化 | 先读 END(文件尾 256 B)→ 名称表偏移 → CELLNAME 及其 S_CELL_OFFSET → 各线程从各自偏移解析单元 |
7. Fab 如何解析版图并完成芯片生产
7.1 全流程总览
图 7-1芯片制造全流程 — 从版图文件到封装成品,12 个阶段跨越 4 个团队
完整性校验
单位/网格
布尔运算
划片道/标记
DRC/LVS
密度均衡
SRAF/ILT
ORC/LFD/MRC
碎化/MPC
| 阶段 | 输入 | 输出 | 数据量变化 |
|---|---|---|---|
| [1]–[6] | 层次化 GDS/OAS 设计层 | 层次化掩模层数据 | 小幅增加(Frame、Fill) |
| [7]–[8] | 掩模层目标图形 | OPC 后掩模图形(通常 OASIS) | 顶点数显著膨胀,层次部分被破坏 |
| [9] | OPC 后多边形 | 写入机专用格式 | 再膨胀;按掩模扁平化 |
| [10] | 写入机格式 | 物理掩模 | 数据 → 实物 |
| [11]–[12] | 掩模组 | 晶圆 → 芯片 | — |
7.2 [0] 设计方交付物
| 交付物 | 内容 | Fab 用途 |
|---|---|---|
| 版图文件 | GDSII 或 OASIS,单一顶层单元 | 全部后续处理的输入 |
| Tape-out 表单(TOF) | 工艺/PDK 版本、顶层单元名、芯片尺寸、掩模层清单、光罩类型、是否 MPW | 决定做哪些掩模、用哪套规则 |
| 层映射/层使用说明 | 用到的 (layer, datatype) 列表 | 核对漏层、多层 |
| 签核报告 | DRC、LVS、天线、密度、ERC 结果及豁免(waiver) | Fab 复查的比对基线 |
| 校验和 | MD5 / SHA 值 | 确认传输完整、版本正确 |
7.3 [1]–[2] 数据接收、解析与归一化
| # | 检查项 | 方法 | 不通过的后果 |
|---|---|---|---|
| 1 | 文件完整 | 校验和比对;OASIS 另验 CRC32/checksum | 传输损坏、版本错 |
| 2 | 格式合法 | GDSII:记录长度 ≥ 4、偶数、语法次序;OASIS:magic、版本、模态变量无未定义使用、CBLOCK 可解压、END 恰 256 B | 解析失败或静默错读 |
| 3 | 单位与网格 | UNITS / START 与 PDK 规定一致;所有顶点落在制造网格上 | 整体缩放错误(最严重);off-grid 吸附产生毛刺 |
| 4 | 顶层单元 | DAG 中无父节点的单元唯一,且与 TOF 中名称一致 | 拿错顶层,做出另一颗芯片 |
| 5 | 引用闭合 | 所有引用(SNAME / 单元索引)都能解析;无环 | 缺图形;展开死循环 |
| 6 | 单元名冲突 | 与 Fab 要合并的 IP、Frame 单元不重名 | 合并时被静默替换 |
| 7 | 变换合法 | 只允许 90° 倍数旋转、镜像、MAG = 1(多数 PDK) | off-grid |
| 8 | 多边形合法 | 不自交、非零面积、无重复点、无锐角尖刺 | 布尔运算与 OPC 异常 |
| 9 | 层清单 | 实际出现的 (layer, datatype) ⊆ PDK 层表且与 TOF 一致 | 漏做掩模或混入非法层 |
| 10 | 尺寸 | 顶层包围盒 = 声明的芯片尺寸且 ≤ 曝光场 | 拼版冲突 |
7.4 [3] 层映射:设计层 → 掩模层
设计层是"设计意图",掩模层是"每次曝光的图形"。两者由 PDK 规定的布尔运算与尺寸调整生成。
| 掩模层(示意) | 生成规则(示意) | 说明 |
|---|---|---|
| 有源区 OD | DIFF.drawing OR DIFF.dummy | 设计图形与填充合并 |
| N 阱 NW | NWELL.drawing | 直接映射 |
| 多晶 PO | POLY.drawing OR POLY.dummy | 先进节点另有 PO cut 掩模 |
| N+ 注入 NP | NIMP.drawing, 或由 (OD NOT PIMP) SIZE +x 自动生成 | 注入层常由工艺规则推导 |
| 阈值调节注入 | VT 标记层 AND 栅区域 | 标记层本身不上掩模 |
| M1 颜色 A / B | M1 按 datatype(颜色)拆分 | 多重曝光:一层设计 → 多张掩模 |
| 钝化开口 | PAD.drawing SIZE −y | 尺寸偏置补偿 |
| 每张掩模还需确定 | 含义 |
|---|---|
| Tone / 极性 | 图形在掩模上是遮光(暗)还是透光(亮)——Clear/Dark field |
| 镜像 | 掩模朝向(Right/Wrong reading),由光刻机光路决定 |
| 倍率 | DUV/EUV 扫描机为 4× 缩小,掩模图形为晶圆图形的 4 倍 |
| 掩模类型 | 二元铬掩模、衰减相移(MoSi)、EUV 反射式掩模等 |
7.5 [4] Frame:拼版与辅助结构
| 结构 | 位置 | 作用 |
|---|---|---|
| 密封环(Seal ring) | die 四周 | 阻挡切割裂纹与水汽 |
| 划片道(Scribe line) | die 之间 | 切割通道,承载测试/对准结构 |
| PCM / WAT 测试键 | 划片道 | 测晶体管、电阻、电容等参数 |
| 对准标记 | 划片道 / 版面边缘 | 每层曝光时光刻机对准前层 |
| 套刻标记(Overlay) | 划片道 | 测量层间对准误差 |
| CD 量测结构 | 划片道 | CD-SEM 测线宽 |
| 掩模条码、名称、版本 | 版面边缘 | 掩模追溯 |
| 拼版参数 | 典型值 |
|---|---|
| 晶圆面最大曝光场 | 26 mm × 33 mm(High-NA EUV 为其一半) |
| 掩模面图形区 | 4 倍:104 mm × 132 mm |
| 掩模基板 | 6025:6 英寸 × 6 英寸 × 0.25 英寸石英 |
| MPW(多项目晶圆) | 多个客户 die 共享一个 Frame,按格位排布 |
7.6–7.7 [5]–[6] 签核复查与 Dummy Fill
| 检查 | 输入 | 目的 |
|---|---|---|
| DRC | 完整 Frame 后的数据 | 用 Fab 内部最新规则复跑;检查 die 与 Frame 交界 |
| LVS(可选) | 版图 + 网表 | 确认版图与电路一致;依赖 TEXT 标注确定端口 |
| 天线检查 | 金属/栅面积比 | 防止刻蚀充电击穿栅氧 |
| 密度检查 | 按窗口统计各层覆盖率 | 为 CMP 与刻蚀均匀性做准备 |
Dummy Fill:在空白处按规则插入不连电的填充图形,使每个统计窗口密度落在标准区间内,防止 CMP 凹陷或过刻。填充图形通常放在专门的 datatype,便于 LVS 忽略。
7.8 [7] RET / OPC:让图形"印得出来"
| 技术 | 做法 | 对数据的影响 |
|---|---|---|
| 规则 OPC | 按查表规则加偏置、线端锤头(hammerhead)、角部衬线(serif) | 顶点数小幅增加 |
| 模型 OPC | 边切为小段,按光刻/光刻胶/刻蚀模型迭代移动至仿真轮廓与目标一致 | 顶点数大幅增加;同一单元在不同环境下 OPC 结果不同,层次被打散 |
| SRAF(亚分辨率辅助图形) | 在孤立图形旁加入不成像的细小图形,改善焦深 | 新增大量小多边形 |
| ILT(反演光刻) | 把掩模当作待优化的像素/曲线场,直接求最优解 | 曲线图形,数据量最大 |
| 多重曝光拆分 | LELE:着色拆成多张掩模;SADP/SAQP:芯轴+切割掩模 | 一层 → 多张掩模 |
OPC 后的数据几乎都用 OASIS 交付——这是 OASIS 在产业中真正普及的场景。
7.9–7.10 [8]–[9] OPC 后验证与 MDP
| 步骤 | 内容 |
|---|---|
| ORC / LFD | 多焦距/剂量条件下仿真,生成 PV-band,找桥连、断线等热点;热点回馈 OPC 重做或退回设计修改 |
| MRC | 检查掩模本身的最小线宽/间距、碎小台阶(jog)、细条(sliver) |
| Fracture(碎化) | 多边形切为 VSB 可曝光的矩形/三角/梯形;控制 shot 数(决定写入时间)、避免过窄 sliver |
| MPC / PEC | 掩模工艺修正 + 电子束邻近效应修正 + 雾化/负载效应修正 |
| 格式转换 | 输出写入机格式:VSB11/12(NuFlare)、JEOL52(JEOL)、OASIS.MASK(SEMI P44);多束写入机取多边形/曲线并在机内实时栅格化 |
| Job deck | 描述掩模版面:图形数据位置、阵列方式、条码/标记、tone、镜像、倍率 |
| 数据回读验证 | 把写入格式反渲染,与 OPC 后数据做 XOR 比对,确认无丢失 |
7.11 [10] 掩模制造
| 工序 | 内容 | 与版图数据的关系 |
|---|---|---|
| 空白基板 | 石英+遮光层(铬/MoSi)+光刻胶;EUV 为 Mo/Si 多层反射膜+吸收层 | 由 TOF 中的掩模类型决定 |
| 写入 | 电子束(关键层)或激光(非关键层)按 MDP 数据曝光 | 直接执行碎化后的 shot |
| 显影、刻蚀、去胶 | 把胶图形转到遮光层 | — |
| 量测 | CD、套准(registration)、相位/透过率 | 量测点位来自数据中的量测结构 |
| 缺陷检测 | Die-to-Database:MDP 数据渲染为参考图,与掩模实拍图逐点比对 | 版图数据第二次被"执行" |
| 修补 | 电子束诱导沉积/刻蚀、纳米机械修补 | 按缺陷坐标定位 |
| 可印性验证 | AIMS 空间像测量:模拟光刻机成像判断残留缺陷是否会印到晶圆 | — |
| 贴保护膜 | Pellicle 距掩模面数毫米的透明薄膜,使落尘处于离焦面 | — |
| 出货 | 进入掩模库,按层绑定工艺路线 | 掩模 ID ↔ 层 ↔ 版本 |
7.12 [11] 晶圆制造:单层循环
| 步骤 | 内容 | 版图数据在此的体现 |
|---|---|---|
| 1. 前处理/涂胶 | 清洗、涂底、旋涂光刻胶、前烘 | — |
| 2. 对准 | 扫描机定位前层对准标记 | Frame 中的对准标记 |
| 3. 曝光 | 步进扫描,4× 缩小,逐场曝光整片晶圆 | 掩模 = 该层几何 |
| 4. 后烘/显影 | 形成光刻胶图形 | — |
| 5. 量测 | CD-SEM 测线宽,套刻机台测层间偏差 | 量测结构、套刻标记 |
| 6. 图形转移 | 刻蚀(成形)或离子注入(掺杂) | 胶图形决定作用区域 |
| 7. 去胶/清洗 | — | — |
| 8. 薄膜/平坦化 | 沉积(CVD/PVD/ALD/电镀)、CMP | Dummy Fill 保证 CMP 均匀 |
7.13 掩模层在工艺中的顺序(逻辑工艺示意)
| 段 | 典型掩模 | 形成的结构 |
|---|---|---|
| FEOL | OD(有源区)→ NW/PW(阱)→ VT 注入 → PO(栅)/PO cut → LDD/Halo → NP/PP(源漏)→ SAB(硅化物阻挡) | 晶体管 |
| MEOL | CO / 局部互连 | 器件到第一层金属 |
| BEOL | M1 → V1 → M2 → … → 顶层金属(多重曝光层每层多张掩模) | 互连 |
| 封装 | PAD 开口、RDL、凸点 | 对外连接 |
掩模总数:成熟节点数十张,先进逻辑节点可达 60–80 张以上;工艺步骤数以千计,周期数月。
7.14 [12] 测试、切割与封装
| 工序 | 内容 | 与版图的关系 |
|---|---|---|
| WAT | 探针测划片道上的 PCM 测试键 | 测试键来自 Frame |
| CP(中测) | 探针卡逐 die 测功能,标记坏片 | 探针位置 = PAD 坐标 |
| 减薄、切割 | 沿划片道切开 | 划片道位置来自 Frame |
| 封装 | 贴片、键合/倒装、塑封 | PAD/凸点布局 |
| FT(终测) | 封装后全功能、全温度测试 | — |
7.15 版图文件的每个要素去了哪里
| 文件要素 | 在制造链中的去向 |
|---|---|
| UNITS / START unit | [2] 网格校验;所有后续坐标的物理含义 |
| 顶层单元 | [2] 确定芯片;[4] 放入 Frame |
| 单元与引用(SREF/AREF/PLACEMENT/重复) | [5]–[7] 层次化 DRC/OPC 加速;[9] 按需展平 |
| (layer, datatype) | [3] 经布尔运算映射为掩模层;决定图形落在哪张掩模 |
| 多边形 / 路径 | [7] OPC 目标 → [9] 碎化为 shot → [10] 掩模上的铬/吸收层图形 → [11] 晶圆结构 |
| TEXT 标注 | [5] LVS 端口与网络命名;一般不上掩模 |
| 属性 | 多为工具元数据;Fab 流程通常忽略 |
| 时间戳 / 库名 | 仅追溯用 |
8. 总结
| 问题 | 答案 |
|---|---|
| 版图文件的本质 | 一组按层分类、整数网格上的多边形,加上把它们组织成 DAG 的单元引用 |
| GDSII 的本质 | "4 字节头 + 定长载荷"的无状态记录流;简单、健壮、冗余大 |
| OASIS 的本质 | 同一数据模型的紧凑编码:变长整数、增量坐标、模态变量、通用重复、名称表、块压缩、尾部索引 |
| 选择依据 | 交换与兼容:GDSII;OPC 后、超大规模、需要并行读取:OASIS |
| Fab 对文件的处理 | 校验 → 解析 → 层映射 → 拼版 → 复查 → 填充 → OPC → 验证 → 碎化 → 写掩模 → 逐层光刻 |
| 最致命的错误 | 单位/网格、顶层单元、层映射错误——不会让工具报错,却会让整套掩模报废 |
9. 附录
9.1 gdstk 核心架构
gdstk(github.com/heitzmann/gdstk)是用 C++ 实现、带 Python 绑定的 GDSII/OASIS 读写库。核心类结构见图 9-1,读写调用链见图 9-2。
图 9-1gdstk 核心类结构 — Library 持有 Cell,Cell 持有多种元素
| 元素 | 关键字段 / 说明 |
|---|---|
| Polygon | layer, datatype, point_array (Array<Vec2>) + area(), bounding_box(), filter()… |
| Reference | type(SREF/AREF), cell_name, origin, rotation, mag, x_reflection, columns, rows + get_bounding_box() |
| Label | layer, texttype, origin, text, rotation, mag, presentation, anchor |
| FlexPath | spine(Array<Vec2>), end_type, end_extensions, bend_type, join_type, elements[] = half_width + offset |
| RobustPath | 独立类(不继承 FlexPath):宽度/偏移可为函数,急弯与自交处理更鲁棒;写出时转为多边形或 Path |
图 9-2gdstk 读写数据流 — 四种 I/O 路径的函数调用链
max_points(本项目取 8190)
oasis.cpp(基础编码)
deflate_level:0–9(默认 6),CBLOCK 压缩级别
gdsii_read_record()
read_gds()
gdsii_real_to_double()
gdsii_real_from_double()
read_unsigned/signed/real…
read_oas()
oasis_read_unsigned_integer()
oasis_read_signed_integer()
oasis_read_real()
oasis_read_gdelta()
oasis_read_repetition()
inflate(CBLOCK)
9.2 关键源码位置(github.com/heitzmann/gdstk)
| 主题 | 源文件 |
|---|---|
| GDSII 记录类型枚举、数据类型常量 | include/gdstk/gdsii.hpp |
| Real8 编解码(GDSII 实数 ↔ IEEE double) | src/gdsii.cpp(gdsii_real_to_double / gdsii_real_from_double) |
| GDSII 记录读取器(字节 → 记录) | src/gdsii.cpp(gdsii_read_record) |
| GDSII 完整读写状态机 | src/library.cpp(read_gds / write_gds) |
| OASIS 记录/点列表/重复类型常量 | include/gdstk/oasis.hpp |
| OASIS 基础编码(varint、实数、delta、点列表、重复) | src/oasis.cpp |
| OASIS 完整读写状态机 + CBLOCK inflate | src/library.cpp(read_oas / write_oas) |
| 写出参数(多边形点数上限、DEFLATE 级别、config_flags) | include/gdstk/library.hpp(write_gds 的 max_points;write_oas 的 deflate_level / config_flags) |
| 格式说明文档 | doc/manual/ch05_formats.rst、ch09_internals.rst |
9.3 GDSII 记录级解码脚本
以下 Python 脚本将任意 .gds 文件解析为记录级 JSON。它对每条记录输出偏移、类型名、数据类型和解码后字段,可直接验证本文 §3.7 中所有字节声明。
gds_records.py — GDSII 记录级解码器
"""GDSII 原始记录级解析器 — 逐条记录解码为 JSON。"""
import json, struct
RECORD_NAMES = {
0x00:"HEADER",0x01:"BGNLIB",0x02:"LIBNAME",0x03:"UNITS",0x04:"ENDLIB",
0x05:"BGNSTR",0x06:"STRNAME",0x07:"ENDSTR",0x08:"BOUNDARY",0x09:"PATH",
0x0A:"SREF",0x0B:"AREF",0x0C:"TEXT",0x0D:"LAYER",0x0E:"DATATYPE",
0x0F:"WIDTH",0x10:"XY",0x11:"ENDEL",0x12:"SNAME",0x13:"COLROW",
0x15:"NODE",0x16:"TEXTTYPE",0x17:"PRESENTATION",0x19:"STRING",
0x1A:"STRANS",0x1B:"MAG",0x1C:"ANGLE",0x1F:"REFLIBS",0x20:"FONTS",
0x21:"PATHTYPE",0x22:"GENERATIONS",0x26:"ELFLAGS",0x2B:"PROPATTR",
0x2C:"PROPVALUE",0x2D:"BOX",0x2F:"PLEX",0x30:"BGNEXTN",0x31:"ENDEXTN",
0x36:"FORMAT",
}
def decode_real8(raw):
v=struct.unpack(">Q",raw)[0]
s=-1.0 if v&0x8000000000000000 else 1.0
e=((v>>56)&0x7F)-64
m=v&0x00FFFFFFFFFFFFFF
return 0.0 if m==0 else s*(m/float(1<<56))*(16.0**e)
def parse(path):
blob=open(path,"rb").read()
doc={"header":None,"libname":None,"units":None,"structures":[]}
cur_struct,cur_elem=None,None
pos=0
while pos<len(blob):
if pos+4>len(blob): break
L,rtype,dtype=struct.unpack(">HBB",blob[pos:pos+4])
if L<4: break
payload=blob[pos+4:pos+L]
name=RECORD_NAMES.get(rtype,f"UNKNOWN_0x{rtype:02X}")
data=None
if dtype==0x02: data=[struct.unpack(">h",payload[i:i+2])[0] for i in range(0,len(payload),2)]
elif dtype==0x03: data=[struct.unpack(">i",payload[i:i+4])[0] for i in range(0,len(payload),4)]
elif dtype==0x05: data=[decode_real8(payload[i:i+8]) for i in range(0,len(payload),8)]
elif dtype==0x06: data=payload.decode("ascii",errors="replace").rstrip("\x00")
elif dtype==0x01: data=[struct.unpack(">H",payload[i:i+2])[0] for i in range(0,len(payload),2)]
# dispatch to library/cell/element fields (简化版,完整版见 gdstk)
if name=="HEADER": doc["header"]={"version":data[0]}
elif name=="LIBNAME": doc["libname"]=data
elif name=="UNITS": doc["units"]={"db_in_user":data[0],"db_in_meter":data[1]}
elif name=="BGNSTR": cur_struct={"name":None,"elements":[]}
elif name=="STRNAME": cur_struct["name"]=data
elif name=="ENDSTR": doc["structures"].append(cur_struct); cur_struct=None
elif name in ("BOUNDARY","PATH","SREF","AREF","TEXT","NODE","BOX"):
cur_elem={"type":name}
elif name=="ENDEL": cur_struct["elements"].append(cur_elem); cur_elem=None
elif cur_elem:
if name=="LAYER": cur_elem["layer"]=data[0]
elif name=="DATATYPE": cur_elem["datatype"]=data[0]
elif name=="XY": cur_elem["points"]=[[data[i],data[i+1]] for i in range(0,len(data),2)]
elif name=="SNAME": cur_elem["sname"]=data
elif name=="WIDTH": cur_elem["width"]=data[0]
elif name=="MAG": cur_elem["mag"]=data[0]
pos+=L
return doc
if __name__=="__main__":
import sys
doc=parse(sys.argv[1])
print(json.dumps(doc,ensure_ascii=False,indent=2))
9.4 OASIS CBLOCK 多边形解码脚本
以下 Python 脚本解压 SRAM.oas 的 CBLOCK 并逐条解码全部 POLYGON 记录(含 g-delta 运算)。输出的层、坐标、delta 与每条记录字节数可逐项核对 §4.11。
oas_polygons.py — OASIS CBLOCK 多边形解码器
"""Decode all POLYGON records from an OASIS CBLOCK body."""
import json, zlib, sys
data = open(sys.argv[1], "rb").read()
def uint(buf, p):
v, shift = 0, 0
while True:
b = buf[p]; p += 1; v |= (b & 0x7F) << shift; shift += 7
if not b & 0x80: return v, p
def sint(buf, p):
v, p = uint(buf, p)
return (-(v >> 1) if v & 1 else v >> 1), p
DIRS = {0:(1,0),1:(0,1),2:(-1,0),3:(0,-1),4:(1,1),5:(-1,1),6:(-1,-1),7:(1,-1)}
pos = data.index(b"\x22\x00", 30) + 2 # find CBLOCK
usize, pos = uint(data, pos)
csize, pos = uint(data, pos)
body = zlib.decompress(data[pos:pos+csize], -15)
polygons = []
q = 0
while q < len(body):
start = q
rec = body[q]; q += 1
if rec != 21: break # only POLYGON in this file
info = body[q]; q += 1
entry = {"info": f"{info:08b}"}
if info & 0x01: entry["layer"], q = uint(body, q)
if info & 0x02: entry["datatype"], q = uint(body, q)
if info & 0x20:
ptype = body[q]; q += 1
cnt, q = uint(body, q)
deltas = []
for _ in range(cnt):
if ptype in (0, 1):
d, q = sint(body, q); deltas.append({"val": d})
elif ptype == 4:
v, q0 = uint(body, q); q = q0
if v & 1 == 0:
dx, dy = DIRS[(v>>1)&7]; m = v>>4
deltas.append({"dx":dx*m,"dy":dy*m,"form":1})
else:
xs = -(v>>2) if v&2 else v>>2
y, q = sint(body, q0)
deltas.append({"dx":xs,"dy":y,"form":2})
entry["point_list"] = {"type": ptype, "count": cnt, "deltas": deltas}
if info & 0x10: entry["x"], q = sint(body, q)
if info & 0x08: entry["y"], q = sint(body, q)
entry["bytes"] = q - start
polygons.append(entry)
print(json.dumps({"polygon_count": len(polygons), "polygons": polygons},
ensure_ascii=False, indent=2))
9.5 样例文件解码数据(完整 45 个多边形)
以下是通过 §9.4 脚本从 SRAM.oas 解出的全部 45 个多边形的层、类型、点数和首点坐标。与 GDS 源文件中 45 个 BOUNDARY 的层/坐标完全一致,确认几何无损。
| # | layer | datatype | 点列表类型 | delta 数 | 首点 (x, y) |
|---|---|---|---|---|---|
| 0 | 1 | 0 | 4 (g-delta) | 7 | (−270, 40) |
| 1 | 1 | 0 | 0 (1-delta) | 2 | (−200, −78) |
| 2 | 1 | 0 | 0 | 2 | (50, 77) |
| 3 | 1 | 0 | 4 | 7 | (270, −40) |
| 4 | 1 | 0 | 0 | 2 | (−460, −10) |
| 5 | 1 | 0 | 0 | 2 | (−460, −270) |
| 6 | 1 | 0 | 0 | 2 | (390, −10) |
| 7 | 1 | 0 | 0 | 2 | (−460, 160) |
| 8 | 1 | 0 | 0 | 2 | (−560, −10) |
| 9 | 1 | 0 | 0 | 2 | (−460, −190) |
| 10 | 1 | 0 | 0 | 2 | (440, −110) |
| 11 | 1 | 0 | 0 | 2 | (480, −110) |
| 12 | 1 | 0 | 0 | 2 | (400, −270) |
| 13 | 1 | 0 | 0 | 2 | (450, −270) |
| 14 | 1 | 0 | 0 | 2 | (250, 90) |
| 15 | 1 | 0 | 0 | 2 | (200, −90) |
| 16 | 2 | 0 | 0 | 2 | (−600, 340) |
| 17 | 2 | 0 | 0 | 2 | (−600, 340) |
| 18 | 2 | 0 | 0 | 2 | (−400, 120) |
| 19 | 2 | 0 | 0 | 2 | (−400, 120) |
| 20 | 2 | 0 | 0 | 2 | (−400, 120) |
| 21 | 2 | 0 | 0 | 2 | (−380, 20) |
| 22 | 2 | 0 | 0 | 2 | (−380, 20) |
| 23 | 3 | 0 | 0 | 2 | (−530, 260) |
| 24 | 3 | 0 | 0 | 2 | (−530, 260) |
| 25 | 5 | 0 | 0 | 2 | (−220, −160) |
| 26 | 6 | 0 | 0 | 2 | (−210, −220) |
| 27 | 6 | 0 | 0 | 2 | (−210, 160) |
| 28 | 6 | 0 | 0 | 2 | (−210, 160) |
| 29 | 6 | 0 | 0 | 2 | (−210, 160) |
| 30 | 6 | 0 | 0 | 2 | (−210, 60) |
| 31 | 7 | 0 | 0 | 2 | (−200, −80) |
| 32 | 7 | 0 | 0 | 2 | (50, 80) |
| 33 | 7 | 0 | 0 | 2 | (50, −250) |
| 34 | 7 | 0 | 0 | 2 | (−200, 180) |
| 35 | 7 | 0 | 0 | 2 | (50, 180) |
| 36 | 8 | 0 | 0 | 2 | (−320, 200) |
| 37 | 40 | 0 | 0 | 2 | (−520, 240) |
| 38 | 40 | 0 | 0 | 2 | (−520, 240) |
| 39 | 40 | 0 | 0 | 2 | (−320, 120) |
| 40 | 40 | 0 | 0 | 2 | (−320, 120) |
| 41 | 50 | 0 | 0 | 2 | (−400, −300) |
| 42 | 51 | 0 | 4 | 7 | (200, −470) |
| 43 | 51 | 0 | 0 | 2 | (−200, −470) |
| 44 | 51 | 0 | 0 | 2 | (−300, −260) |
层统计与 GDS 源完全一致(layer 1—16 个、2—7 个、3—2 个、5—1 个、6—5 个、7—5 个、8—1 个、40—4 个、50—1 个、51—3 个),总计 45 个;各记录字节数之和 635 B,与解压尺寸一致。

浙公网安备 33010602011771号