KLayout PCell 介绍

一个”能改参数的圆”

假设要在版图里放五个半径不同的圆环:5、10、15、20、25 μm。最直接的做法是每个半径画一个单元,存成五份 GDS;改一次半径,就再画一份。用图形界面复制、改尺寸、再存一份,文件一多,版图库就变得又臃肿又难维护。

KLayout 给出的答案叫 PCell(parameterized cell,参数化单元):它不包含静态版图,而是由一段代码按照一组参数动态生成 [S1]。同样是圆环,PCell 的做法是”一份代码 + radius/width 参数”——参数变了,几何就跟着变。图 1 对比了这两种方式。

静态单元与 PCell 的对比

图 1:左边是静态单元——每个半径都是一份独立的 GDS 单元;右边是同一个 PCell 用不同 radius 参数动态生成。示意图为本平台自绘。

PCell 是什么

PCell 并不是一个新的文件格式,而是版图工具层的一种”生成器”机制。KLayout 从 0.22 版本开始支持 PCell [S1]。一个 PCell 由三部分组成:

  • 描述文本:在单元树里显示的人类可读名称;
  • 参数声明:半径、宽度、图层等参数的类型与默认值;
  • 生产回调(produce):拿到参数后真正往单元里写几何的代码 [S1]。

使用方式和普通单元几乎一样:从库中选择 PCell,填写参数,然后像放普通单元一样放置、旋转、镜像、阵列 [S1][S2]。区别在于,普通单元的内容是”画好存好”的,PCell 的内容是”每次按参数算出来”的。

表 1:静态单元、生成脚本与 PCell 三种做法对比(作者整理,基于 KLayout 官方机制描述 S1、S2)。

做法 改参数的成本 GDS 里的表现 适合场景
静态单元 每次手动重画或复制一份 每个版本都有一份几何 少量固定图形
生成脚本 改脚本参数重跑一遍 每次重新生成整批几何 一次性批量生成
PCell 改参数即时更新 几何快照 + 参数元信息 可交互复用的器件库

表 1 里”几何快照 + 参数元信息”是关键,我们下一节展开。

GDS 里没有 PCell:快照与元信息

很多刚接触 PCell 的人会以为”GDS 里能存 PCell”。严格说,GDSII 格式本身不支持 PCell 这种参数化概念 [S6][S9]。那 KLayout 是怎么保存 PCell 的?

KLayout 的做法是”快照 + 元信息”:写入 GDS 时,先用当前参数把几何算出来,把几何快照作为普通图形写进文件,再附加一份元信息,记录”这是哪个库的哪个 PCell、参数是什么”(GDS 中放在一个专用顶层单元里,OASIS 中放在 cell properties 里)[S6]。PCell 的代码本身不会写入文件——文件里保存的是几何快照和参数值 [S6]。

这个设计带来两个直接后果:

  1. 任何不认 PCell 的工具打开这个 GDS,看到的都是合法、完整的静态几何,不会报错——只是不能”改参数”而已 [S6];
  2. 用 KLayout 读回时,如果机器上装了对应的 PCell 库,它会重新连接成 PCell,参数面板继续可用;如果没装库,就退化为普通静态单元 [S6]。

我们用 KLayout 0.30.10 的 Python 包实测验证了后半句:一个写入了 PCell 的 GDS 文件,在未注册任何库的全新 Layout 中读回,pcell_names() 为空,单元作为静态单元存在 [S11]。图 2 把这个机制画了出来。也就是说,PCell 是”版图工具的功能”,不是”文件格式的功能”。

PCell 机制流程

图 2:参数传给 PCell 代码动态生成几何;写入 GDS 时保存几何快照与参数元信息;读回时是否有对应库决定了能否恢复为 PCell。示意图为本平台自绘。

写一个最小 PCell:先看官方骨架

KLayout 提供 PCellDeclarationHelper 简化 PCell 声明,Ruby 与 Python 的写法几乎一致,Python 里的模块名是 pya [S5][S4]。下面是最小例子——一个由参数 p 决定大小的矩形:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pya

class MyPCell(pya.PCellDeclarationHelper):
def __init__(self):
super().__init__()
self.param("p", self.TypeInt, "The parameter", default=1)
self.param("l", self.TypeLayer, "The layer", default=pya.LayerInfo(1, 0))

def display_text_impl(self):
return f"We have p={self.p}"

def produce_impl(self):
self.cell.shapes(self.l_layer).insert(
pya.Box(0, 0, self.p * 100, self.p * 200)
)

逐行看:

  • param("p", self.TypeInt, ...) 声明一个整数参数 p,同时自动生成 self.pself.set_p(...) 访问器;类型为图层(TypeLayer)的参数还会额外生成 self.l_layer,在 produce_impl 里直接得到图层索引 [S4];
  • display_text_impl 返回单元树里显示的文字;
  • produce_impl 是核心:用参数往 self.cell 里写几何 [S4]。

在 KLayout 0.30.10 的 Python 环境里实测:注册这个 PCell 后,用 layout.create_cell("MyPCell", {"p": 3, "l": pya.LayerInfo(2, 0)}) 实例化,写入 GDS,再读回——文件是合法的,2/0 层上有一个 300×600(DBU)的矩形。整个”注册 → 实例化 → 写 GDS → 读回”链路是通的 [S11]。

一个更实用的例子:环形 PCell

矩形只是骨架,光电子版图里更常见的是环、波导这类器件。下面的 RingPCell 把半径、波导宽度、图层和离散点数作为参数,用外圆减内圆生成环形:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import math
import pya

class RingPCell(pya.PCellDeclarationHelper):
def __init__(self):
super().__init__()
self.param("radius", self.TypeDouble, "Ring radius (um)", default=5.0)
self.param("width", self.TypeDouble, "Waveguide width (um)", default=0.5)
self.param("layer", self.TypeLayer, "Layer", default=pya.LayerInfo(1, 0))
self.param("npoints", self.TypeInt, "Polygon points", default=64)

def display_text_impl(self):
return f"Ring r={self.radius} w={self.width}"

def coerce_parameters_impl(self):
if self.radius <= 0:
self.radius = 5.0
if self.width <= 0 or self.width >= 2 * self.radius:
self.width = 0.5
if self.npoints < 8:
self.npoints = 64

def produce_impl(self):
dbu = self.layout.dbu
r_out = round((self.radius + self.width / 2) / dbu)
r_in = round((self.radius - self.width / 2) / dbu)

def points(r):
return [
pya.Point(
round(r * math.cos(2 * math.pi * i / self.npoints)),
round(r * math.sin(2 * math.pi * i / self.npoints)),
)
for i in range(self.npoints)
]

ring = pya.Region(pya.Polygon(points(r_out))) - pya.Region(pya.Polygon(points(r_in)))
ring.insert_into(self.layout, self.cell.cell_index(), self.layer_layer)

这里多了一个之前没见过的回调:coerce_parameters_impl。它的作用是”参数修正”——在界面按下 Apply 或实例化之前,把参数整理到一致、合法的状态(比如半径必须为正、环宽不能超过直径)[S4]。没有它,用户把 radius 填成负数,produce 就会生成怪形状甚至报错。

用同一份代码生成半径 5、10、15 μm 的三个环,写进 GDS 再渲染出来,就是图 3:

环形 PCell 实际生成效果

图 3:同一个 RingPCell 用不同 radius 参数生成的三个环(波导宽度 0.5 μm,64 点离散)。图片基于本文代码实际生成的 GDS 渲染,本平台自绘。

为了示例简洁,这里只生成环形本身,没有画总线波导;加总线波导只是再多插几个多边形的事。

注意一点:圆仍然是用 64 个点离散成多边形的——GDS 里没有真正的曲线。这和平台之前那篇《GDS里的圆弧为什么总是多边形》讨论的是同一个底层约束;PCell 解决的是”参数化复用”,而不是”曲线存储”。

guiding shapes:PCell 的”幽灵形状”

PCell 还有一个很特别的机制:guiding shapes(引导形状)。它是一类”幽灵形状”——不产生真实版图,但作为 PCell 实例的一部分存在,可以被正常编辑 [S1]。最常见的用法是手柄:一个点状 guiding shape,在移动模式下拖动它,就相当于修改了某个参数 [S1]。

最典型的例子是 Basic 库的 ROUND_PATH(圆角路径):它用一条路径作为输入形状,路径本身不输出,真正输出的是一条在拐角处按给定半径平滑过渡的新路径 [S1][S10]。改路径、拖拐点,圆角半径仍由数值参数控制。图形化的”画路径 → 拖拐点 → 得圆角波导”,就是 guiding shapes 的日常用法 [S2]。

从 Basic 库到 PDK:PCell 的生态

KLayout 自带 Basic 库,提供一批开箱即用的 PCell:TEXT、CIRCLE、DONUT(带孔的圆)、ELLIPSE、PIE、ARC、ROUND_PATH、ROUND_POLYGON、STROKED_BOX、STROKED_POLYGON [S10],见表 2。而且支持把已有图形直接”Convert To PCells”——画一个多边形,选中后转成 ROUND_POLYGON,就能继续用圆角半径参数调整它 [S10][S3]。

表 2:Basic 库提供的主要 PCell(S10)。

PCell 说明
CIRCLE / ELLIPSE 圆 / 椭圆,可选手柄定义尺寸,默认 64 个插值点
DONUT 带孔的圆(环),内、外半径两个手柄
PIE / ARC 圆环的一段(扇形 / 弧)
ROUND_PATH 按半径平滑路径拐角的圆角路径
ROUND_POLYGON 圆角多边形
STROKED_BOX / STROKED_POLYGON 多边形或矩形的”描边”,可加圆角

在真实项目里,PCell 更多以 PDK(Process Design Kit,工艺设计套件) 的形式出现:工艺厂把自己的器件(波导、环、耦合器等)写成一套 PCell 库,设计者直接调用,改参数得到符合工艺规则的器件。硅光子领域有两个典型的开源例子:

  • SiEPIC-Tools:一个基于 Python 的 KLayout 插件,为硅光子版图提供设计、验证与电路仿真能力,支持 GUI 与脚本两种方式,并配合 SiEPIC-EBeam-PDK 等 PDK 使用 [S8];
  • gdsfactory:用 @gf.cell 装饰器把普通 Python 函数变成参数化单元,官方文档直接说”cells 就是 PCell”,返回的 Component 可以注册进 PDK 统一调用 [S7]。

对写 PDK 的人来说,PCell 意味着器件库可以”一份代码、到处复用”;对用 PDK 的人来说,PCell 意味着”从库里拖器件、填参数”,而不是手动画每个器件。

局限与工程经验

PCell 不是万能的,几个工程上必须知道的限制:

  • 代码不随文件走。GDS 里只有快照和参数,没有 PCell 代码 [S6]。把文件发给同事或送去流片,对方必须装了对应的库(或宏),否则只能看到静态几何、无法改参数。PDK 的分发本质上是在分发”代码”。
  • 库会变。KLayout 的库单元在导入版图时保存一份副本,同时保留对库的引用;库内容更新后,重新加载版图会用新版本替换旧单元 [S2]。这对设计来说是”自动升级”,但也意味着库升级可能改变已有器件的几何——版本管理要跟上。
  • 不适合复杂生成器。KLayout 作者的建议是:PCell 适合器件和小组件,不适合内存生成器这类计算量很大的生成器;PCell 代码只在需要时执行,但依然要保持轻量 [S6]。
  • tape-out 前转静态。下游工具、工厂不一定装有你的 PCell 库。KLayout 提供”Convert Cell To Static”把 PCell 变成普通单元;代价是转换后失去参数控制,不能再调 [S3]。是否在交付前转静态、转多少层,是每个项目要定下的策略。

结论

PCell 是版图工具的”参数化生成”机制:一份代码加一组参数,动态生成几何,实例化与普通单元无异。它不改变 GDS 格式——文件里存的是几何快照和参数元信息,代码留在库里;正因为如此,”能改参数”依赖库,跨工具传递时会退化为静态几何。

理解这一点,再回头看 KLayout 里的 Basic 库、SiEPIC-Tools 和 gdsfactory,会清楚很多:它们本质上是同一件事的不同规模——把”重复画图”变成”一个函数 + 一组参数”。

参考资料


KLayout PCell 介绍
https://time-frame.cloud/2026/08/07/2026-08-07-klayout-pcell/
作者
Time Frame
发布于
2026年8月7日
许可协议