装载:一个文件,零编译
pip install klayout-klink 后 klink init 会在项目里铺出 example_template/ledit_bridge/:
ledit_bridge.cpp—— 唯一的宏文件。在 L-Edit 里 Tools → Macro → Load Macro… 直接选它(L-Edit 自己现场编译;宏只用老版本 API 子集编写,理论兼容旧版、实测环境 v16.x;klink 不携带任何 Siemens/Tanner 文件,SDK 头文件来自你自己的 L-Edit 安装)。driver.py—— 免依赖冒烟驱动:python driver.py ping看到宏版本、当前设计与能力表即为连通。tcell_workflows.py—— T-Cell 工作流 CLI(下文)。
传输是 %LOCALAPPDATA%\klink\ledit_bridge\ 下的 inbox/outbox JSON 文件对,宏在 UI 线程 200ms 轮询、原子写回;心跳文件 hello.json 提供活性判定。
能力面
| 类别 | 命令 |
|---|---|
| 设计文件 | new_design(没开 tdb 也能自举建库)· open_design · save_design |
| 图层 | ensure_layer(盖 GDS 号;新层自动配色:实心无边框)· set_layer_style · get_layers(含填充色与特殊层标记) |
| 绘制 | create_cell · draw(box/polygon/wire/circle 批量)· place_instance(含 nx×ny 阵列)· clear_cell |
| 全量读取 | get_selection · get_cell(图形+对象级属性树+实例+端口+标签;wire 带端帽/拐角,torus/pie 带精确参数)· list_cells(现在总是带 hidden 布尔字段,与之不一致时再加 hidden_property) |
| 导航 | show_cell · set_cell_hidden · list_windows · close_window · layout_view · save_image |
| 破坏性 | delete_cell · rename_cell · delete_objects · close_design |
| 验证 | run_drc · drc_summary · export_gds |
| 工艺知识 | get_drc_rules —— 整套设计规则表机读导出 |
| T-Cell | get_tcell_params · instance_tcell(程序化传参实例化)· set_tcell_code(把生成器代码写回成原生 T-Cell) |
与 KLayout 互导(MCP 一调用)
配好 klink MCP 后,agent 直接获得 bridge_ledit 域的十八个意图级工具:下面五个覆盖与 KLayout 的双向交换,其余十三个(导航与视图、标记过桥、破坏性命令、DRC 与 GDS 回程)按主题分节说明在后面。
ledit.status—— 发现与分诊:宏活性、设计是否打开、心跳年龄。任何异常先叫它,错误信息会直接写明下一步。0.5.3 新增:宏支持 list_designs/list_cells 时,还会一并报出当前打开的设计和活动设计的 cell 列表(含 T-Cell 标记)——“L-Edit 里现在有什么”一次调用问完;旧版本宏行为不变(能力探测式降级)。ledit.import_selection—— 你在 L-Edit 里选中,agent 导入 KLayout 新 cell:圆保持参数化(CIRCLE PCell)、层连名带号迁移(重名追加不覆盖)、不可转换对象如实列出。ledit.push_cell—— KLayout 平坦 cell 推回 L-Edit(层同样带名建立)。ledit.import_cell_tree—— 把 L-Edit 里一个 cell 连同它整棵层级导入 KLayout:children-first 重建,实例照旧建成实例(不是拍平成形状),层身份同样连名带号迁移;每个目标 cell 都会被重新建出,所以重复导入是幂等的;没有可转换轮廓的形状列进not_convertible,绝不悄悄丢弃。ledit.push_cell_tree—— 反方向:把 KLayout 一个 cell 连同它下面整棵子树推回 L-Edit,同样 children-first、实例重建成实例;默认clear=True,让重复推送保持幂等(L-Edit 的 draw 只会追加);整棵树作为一次批量请求发过去;L-Edit 摆放表达不了的实例(缩放、非正交旋转、错切阵列)会如实列进unsupported_instances,绝不近似凑合。
导航与视图
ledit.show_cell 打开/前置某个 cell 的版图窗口并把它设为当前可见(“在 L-Edit 里打开 X”);ledit.set_cell_hidden 切换 T-Cell 变体常带的 “Hide In Lists” 标记;ledit.list_windows / ledit.close_window 枚举/关闭版图、文本、日志窗口(按 cell 名,或 ledit.list_windows 给出的 index);ledit.layout_view 是视图的单一动词——不带参数读当前视图,rect_um 设视图,home 复位到 cell 的 home view,调用后总是回读实际视图(加 has_window);ledit.save_image 把 cell/区域渲染成 PNG/BMP/JPG——只在用户明确要图片时调用,从不当验证证据,规矩和 KLayout 的 view.screenshot 一样:验证请用 get_cell / status。
把一个五路扇出的 klink tapered 路由 cell ROUTE_05_FANOUT 推入 L-Edit 之后,ledit.show_cell 的真实返回:
{"cell": "ROUTE_05_FANOUT", "window_opened": false, "via": "window", "file": "klink_site_demo"}
ledit.push_cell_tree 把 ROUTE_05_FANOUT 连同它下面的 cell 推入后,ledit.show_cell 打开的真实 L-Edit v16.3 窗口:图层调色板里是按名字建出的 klink 层(含 GDS 号),cell 窗口里是推入的扇出路由。{"cell": "ROUTE_05_FANOUT", "mode": "get", "rect_um": [-51.139, -4.598, 114.139, 52.598], "has_window": true, "file": "klink_site_demo"}
ledit.save_image 对整个 cell 的渲染(L-Edit 自身渲染器,1600×1000):五条 klink tapered 路由、焊盘,以及零宽 Port 三角形——它们显示为细线,而不是填色三角形。标记以零宽线过桥
klink 的 Port / Anchor / Region 标记从 0.5.6 起统一是零面积的纯标记(width_um=0 的路径轮廓)。0.5.7 之前,draw 会自己拒绝 width_um == 0 的 wire;现在这层拒绝交给了 L-Edit 自己(LWire_New),拒绝时错误信息会说明原因。在真实的 L-Edit v16.3 上验证过:零宽线是被接受的,并渲染成 1 像素的轮廓线——klink 的零宽标记由此第一次能完整过桥。ledit.push_cell_tree 的返回里用 zero_width_wires 报出零宽线的总数。
把 ROUTE_05_FANOUT 连同它下面整棵子树(含 Port、CorridorAnchor 等标记 cell)推入 L-Edit 的真实报告:
{"cell_count": 13, "ops": 57, "zero_width_wires": 10, "requests": 1, "unsupported_instances": []}
13 个 cell、57 条绘制操作装进 1 次批量请求,其中 10 条是零宽线——正是 Port 标记的三角轮廓。用 ledit.layout_view 设视图到某个 Port 附近,再 ledit.save_image 截这块区域:
layout_view(rect_um=...) 缩到一个 Port 标记附近再 save_image:两个焊盘、一条路由、以及 Port 三角形——它作为零宽 wire 过桥,读回 width_um: 0、4 个点,L-Edit 把它渲染成一条细线,而不是填色三角形。破坏性命令:显式目标、会拒绝
ledit.delete_cell 按显式名字删除 cell;除非 force=true,否则遇到“是当前可见 cell”“被其他 cell 实例化”“是 T-Cell 生成器”都会拒绝,并点名引用者。真实的拒绝:
delete_cell failed: refusing to delete 'Port$17': it is instanced 1 time(s) by ROUTE_05_FANOUT
ledit.rename_cell 改名,新名已存在则拒绝。ledit.delete_objects 按图层和/或 rect_um 删形状——只删边界框完全落在矩形内的对象,一条只是穿过矩形的路由留着不动;实例不受影响(清整个 cell 用 clear_cell)。真实返回(先 rename 再 delete_objects):
{"old": "DRC_DEMO", "new": "DRC_DEMO_RENAMED"}
{"cell": "DRC_DEMO_RENAMED", "deleted": 1, "by_layer": {"Poly": 1}}
ledit.close_design 按名字关闭一个已打开的设计;有未保存改动时会拒绝,除非 discard=true——这是清理 klink 自建 scratch 设计(new_design)的唯一办法,因为关掉设计的最后一扇窗口不会关闭设计本身:
{"file": "klink_site_demo", "closed": true, "discarded_changes": true}
ledit.push_cell_tree 本身也变得更安全:发送前先逐条校验每个绘制项;一批请求里任何一步失败,会把这次调用里新建的 cell 删掉(rolled_back),并把清空过的既有 cell 报成 clobbered——不留半成品。
DRC 与 GDS 回程
ledit.run_drc 用设计里已加载的规则集跑 L-Edit 自己的 DRC(整个 cell,或 rect_um 限定一块区域),只报错误数量和 status——L-Edit v16.3 的 UPI 不暴露违例几何,要看几何请走 ledit.export_gds 导出后交给 klink 的 KLayout 侧 DRC 工具。ledit.drc_summary 不重新跑,只读上一次结果;首次跑之前 errors 是 null,status 是 "needed"。
一个 DRC 演示:两个相距 0.4 微米的 Poly 方块,设计加载了 82 条规则:
drc_summary(跑之前): {"cell": "DRC_DEMO", "errors": null, "status": "needed"}
run_drc(整个 cell): {"cell": "DRC_DEMO", "errors": 1, "status": "failed", "rules": 82}
run_drc(只框左边方块): {"cell": "DRC_DEMO", "errors": 0, "status": "failed", "rules": 82, "rect_um": [-1, -1, 4, 6]}
run_drc 在整个 cell 上报 errors: 1 / status: failed(82 条规则);只框住左边方块的区域跑报 errors: 0——数量对得上,但没有违例的具体形状或坐标。ledit.export_gds 把整个设计或一个 cell(连同层级)用 LFile_ExportGDSII 写成 GDS 文件——这是 L-Edit 到 KLayout 最便宜的回程:KLayout 那边 layout.file_info / layout.import_file 原样读取,这个方向不需要任何补齐字节。真实导出 + KLayout 侧读回:
export_gds: {"bytes": 4096, "scope": "specified_cell", "cell": "ROUTE_05_FANOUT"}
klayout_file_info: {"top_cells": ["ROUTE_05_FANOUT"], "dbu": 0.001,
"layers": [{"layer":1,"datatype":0}, {"layer":11,"datatype":0},
{"layer":996,"datatype":99}, {"layer":999,"datatype":1},
{"layer":999,"datatype":99}]}
export_gds 写出的 GDS 用 layout.show_file 在 KLayout 里打开:顶层 cell ROUTE_05_FANOUT,5 个图层,和上面 klayout_file_info 的读数一致。T-Cell:双向参数化
T-Cell 的生成器代码存在 cell 属性里,桥可以整段读出——参数名从代码的 DO-NOT-EDIT 段稳定解析。由此打通两个方向,验收标准只有一个:逐字节一致(整数纳米坐标逐箱相等,以 L-Edit 实际生成的几何为真值)。
python tcell_workflows.py read NFET_Generator # 参数与默认值
python tcell_workflows.py variants NFET_Generator --paramsets "[...]" --out ex.json
python tcell_workflows.py writeback MyGen --code gen.cpp --params "[...]"
python tcell_workflows.py verify MyGen --reference ref.py:boxes --paramsets "[...]"
python tcell_workflows.py fit MyGen --paramsets "[...]" --check "[...]" --out fit.json --register MY_DEVICE
python tcell_workflows.py to_pcell MyGen --reference ref.py:boxes --params-spec spec.json --paramsets ps.json --check chk.json --register MY_DEVICE
- T-Cell → KLayout(纯几何路线):
fit(v0.2.2)采样样本并拟合 v3 重复组模型——数量、间距、位置全部是精确整数律,在留出检查点上与 L-Edit 新鲜变体逐字节对决,然后注册为 KLayout PCell(放置同样逐字节验证)。模型表达不了或钉不死的结构一律 REFUSE 并点名箱族——交替/奇偶结构请走下面的to_pcell移植路线。 - T-Cell → KLayout(代码移植路线,
to_pcell):to_pcell是这条移植路线的脚手架化版本——三个阶段全部过关才注册,任何一步不过直接以非零退出码退出并给出指令性文字,绝不留半成品。第 1 步:把你移植好的参考生成器(Python,entry(params) -> {层名: [[x1,y1,x2,y2] 整数纳米盒子]},harvest 原生格式,和verify用的字典结构完全一致)在若干--paramsets上与 L-Edit 新鲜变体逐字节对决;第 2 步:同一个函数原样注册成原生 KLayout PCell(库名klink_custom;机制是新的klink.domains.structdevice.pcell_native,注册走显式的exec.python逃生舱——刻意不开一条能直接接收代码的 plugin RPC;层名按你设计自己的层表映射到 GDS L/D 号,没在层表里的 GDS 号一律拒绝,klink 从不替你发明);第 3 步:验收循环——在每个--check点把注册好的 PCell 真的放进 live KLayout、把画出的盒子收割回来,再和一份新鲜的 L-Edit 变体逐字节对决。这条路线覆盖 v3 拟合器 REFUSE 的那类结构——奇偶交替的手指、M×L 双线性延展、按计数分档的结构——完整的 L/W/M 参数保真度。 - KLayout → T-Cell:agent 生成 UPI 代码 →
writeback写入(同时定义参数表)→ L-Edit 现场编译 → 得到和手写无异的原生参数化 T-Cell。
下面是一次真实运行,针对的是 L-Edit 自带的 NFET_Generator 范例——正是拟合器会因为奇偶/M×L 结构而 REFUSE 的那个器件:
$ python tcell_workflows.py to_pcell NFET_Generator --reference nfet_ref.py:nfet_boxes \
--params-spec spec.json --paramsets ps.json --check chk.json --register NFET_KLINK
step 1/3: byte-exact verify -- nfet_boxes vs L-Edit NFET_Generator at 5 paramset(s) ...
{'L': 2, 'W': 5, 'M': 1}: 10 boxes -> BYTE-EXACT
{'L': 2, 'W': 8, 'M': 2}: 16 boxes -> BYTE-EXACT
{'L': 3, 'W': 12, 'M': 3}: 24 boxes -> BYTE-EXACT
{'L': 2.5, 'W': 9, 'M': 4}: 24 boxes -> BYTE-EXACT
{'L': 2, 'W': 9.5, 'M': 2}: 16 boxes -> BYTE-EXACT
VERDICT: ALL BYTE-EXACT
step 2/3: registering 'NFET_KLINK' in library 'klink_custom' ...
registered KLayout PCell NFET_KLINK (library klink_custom, params ['L', 'W', 'M'])
step 3/3: acceptance loop -- 3 --check point(s), live KLayout placement vs fresh L-Edit variant ...
[1/3] {'L': 4, 'W': 15, 'M': 5} ... BYTE-EXACT (34 boxes)
[2/3] {'L': 2, 'W': 6, 'M': 2} ... BYTE-EXACT (13 boxes)
[3/3] {'L': 3.5, 'W': 10.5, 'M': 3} ... BYTE-EXACT (20 boxes)
SUCCESS
to_pcell 注册出的原生 PCell 现场画出:手指数量跟着 M 走,接触行数按 W 的 floor 律走,金属列在上下轨之间交替勾连——这正是拟合器会 REFUSE 的那种奇偶结构,to_pcell 把它原样搬进 KLayout,逐字节保真。已知边界与排障
| 症状 / 边界 | 含义 / 修法 |
|---|---|
| 心跳停了(hello.json 变旧) | L-Edit 弹了模态对话框(常见于 T-Cell 编译错误)——关掉即恢复 |
| 改了 T-Cell 代码但几何没变 | L-Edit 缓存变体:换一组参数值,或 Tools → Regenerate T-Cells |
| 画进已有 cell 内容翻倍 | draw 是追加式——重新生成请用 clear_cell 或新 cell |
| 两个 L-Edit 同时开桥 | 当前单命名空间,只保留一个装宏的实例 |
| 层的 GDS 号是 -1 | 导出 GDS 会错——桥建层会盖号;写回代码请沿用模板的 need_layer 范式 |
工具报 macro 太旧 / ERR_MACRO_TOO_OLD | 重新装载宏:Tools → klink: Bridge Stop,然后 Tools → Macro → Load Macro…(错误信息本身会打出宏文件的路径) |
run_drc 拿不到违例几何 | 设计如此——L-Edit v16.3 的 UPI 只给数量和 status;要几何就 export_gds 后走 KLayout 侧 DRC 工具 |
| 关掉最后一扇窗口,设计还在 | 符合设计——关窗口不等于关设计,用 ledit.close_design 才真正关闭 |
save_image 只是给人看的 | 验证请用 get_cell / status,同 KLayout 截图的规矩——不要拿图片当验证证据 |
范围说明:参数化的智能(拟合、移植、验证)在 klink/KLayout 侧完成,L-Edit 收到的是原生结果(静态 cell 或真 T-Cell)。桥不是 L-Edit 插件,是随 klink 发行的外部适配层。