线路版图绘制入门

如何利用 IPKISS 设计一个简单的线路版图?

下图是一个简单的光子线路,一个方向耦合器与四个光栅耦合器相连:
具体实现步骤如下:

导入 PDK 和 IPKISS 函数库

1. 首先需要导入工艺信息 PDK,此处我们导入的是 Luceda 基于标准 SOI 工艺开发的通用示例PDK:si_fab
2. 接着导入 IPKISS 的函数库,里面包含了我们要用到的一系列函数,如线路设计函数 i3.Circuit
from si_fab import all as pdk  # 导入工艺信息,简写为pdk
from ipkiss3 import all as i3  # 导入函数库,简写为i3

构建类

构建一个继承自 i3.Circuit 的类 class RoutedDC()
class RoutedDC(i3.Circuit):
线路设计主要分为四部分:
  • 定义线路属性
  • 实例化线路中的器件
  • 器件的摆放与连接
  • 端口命名

定义线路属性

线路的属性包含线路中用到的器件类型、线路的结构参数等。
【器件类型】 本例用到的是方向耦合器 pdk.SiDirectionalCouplerSPower 和光栅耦合器 pdk.FC_TE_1550()
【结构参数】本例中,光栅耦合器在 x 方向和 y 方向上的间距为 spacing_x 和 spacing_y
【default】给线路属性赋默认值
dc = i3.ChildCellProperty(doc="Directional coupler in circuit")
fgc = i3.ChildCellProperty(doc="PCell of the fiber grating coupler")
spacing_x = i3.PositiveNumberProperty(default=200.0, doc="The spacing of fiber grating coupler in x direction")
spacing_y = i3.PositiveNumberProperty(default=100.0, doc="The spacing of fiber grating coupler in y direction")
bend_radius = i3.PositiveNumberProperty(default=20, doc="The bend radius of the routing waveguide")

def _default_dc(self):
    return pdk.SiDirectionalCouplerSPower(power_fraction=0.5, target_wavelength=1.55)

def _default_fgc(self):
    return pdk.FC_TE_1550()

线路中的子器件

线路中的子器件 insts{} 是一个字典集,有 1 个方向耦合器和 4 个光栅耦合器,他们的名称分别为 dcfgc_1fgc_2fgc_3fgc_4
    def _default_insts(self):
        insts = {
            "dc": self.dc,
            "fgc_1": self.fgc,
            "fgc_2": self.fgc,
            "fgc_3": self.fgc,
            "fgc_4": self.fgc,
        }
        return insts

器件的放置与连接

实例化完的器件都放置在默认值位置(如原点),如需将器件放置在指定位置并连接相应的端口,还需要在 specs[] 列表中进行摆放与连接。
    def _default_specs(self):
        specs = [
            i3.Place("dc", (0, 0)),
            i3.PlaceRelative("fgc_1", "dc", (-self.spacing_x / 2, -self.spacing_y / 2)),
            i3.PlaceRelative("fgc_2", "dc", (-self.spacing_x / 2, self.spacing_y / 2)),
            i3.PlaceRelative("fgc_3", "dc", (self.spacing_x / 2, -self.spacing_y / 2), angle=180),
            i3.PlaceRelative("fgc_4", "dc", (self.spacing_x / 2, self.spacing_y / 2), angle=180),
            i3.ConnectManhattan("fgc_1:out", "dc:in1", bend_radius=self.bend_radius),
            i3.ConnectManhattan("fgc_2:out", "dc:in2", bend_radius=self.bend_radius),
            i3.ConnectManhattan("fgc_3:out", "dc:out1", bend_radius=self.bend_radius),
            i3.ConnectManhattan("fgc_4:out", "dc:out2", bend_radius=self.bend_radius)
        ]

        return specs

简化端口命名

最后,需要将线路中还暴露在外的端口重新命名。一是为了简化端口的名称,避免名字过于冗长;二是便于后续的网表提取。exposed_ports{} 也是一个字典集。
    def _default_exposed_ports(self):
        exposed_ports = {
            "fgc_1:vertical_in": "in1",
            "fgc_2:vertical_in": "in2",
            "fgc_3:vertical_in": "out1",
            "fgc_4:vertical_in": "out2",
        }
        return exposed_ports
通过这四步,一个简单的光子线路版图就完成了。

线路实例化及导出 GDSⅡ 版图

如果想要查看线路的版图,还需要对线路进行实例化:
if __name__ == '__main__':
    dc = RoutedDC(bend_radius=20)
    my_circuit = dc.Layout()
    my_circuit.visualize(annotate=True)
    my_circuit.write_gdsii("Rounted_dc.gds")

完整代码

from si_fab import all as pdk
from ipkiss3 import all as i3


class RoutedDC(i3.Circuit):
    _name_prefix = "routed_dc"
    dc = i3.ChildCellProperty(doc="Directional coupler in circuit")
    fgc = i3.ChildCellProperty(doc="PCell of the fiber grating coupler")
    spacing_x = i3.PositiveNumberProperty(default=200.0, doc="The spacing of fiber grating coupler in x direction")
    spacing_y = i3.PositiveNumberProperty(default=100.0, doc="The spacing of fiber grating coupler in y direction")
    bend_radius = i3.PositiveNumberProperty(default=20, doc="The bend radius of the routing waveguide")

    def _default_dc(self):
        return pdk.SiDirectionalCouplerSPower(power_fraction=0.5, target_wavelength=1.55)

    def _default_fgc(self):
        return pdk.FC_TE_1550()

    def _default_insts(self):
        insts = {
            "dc": self.dc,
            "fgc_1": self.fgc,
            "fgc_2": self.fgc,
            "fgc_3": self.fgc,
            "fgc_4": self.fgc,
        }
        return insts

    def _default_specs(self):
        specs = [
            i3.Place("dc", (0, 0)),
            i3.PlaceRelative("fgc_1", "dc", (-self.spacing_x / 2, -self.spacing_y / 2)),
            i3.PlaceRelative("fgc_2", "dc", (-self.spacing_x / 2, self.spacing_y / 2)),
            i3.PlaceRelative("fgc_3", "dc", (self.spacing_x / 2, -self.spacing_y / 2), angle=180),
            i3.PlaceRelative("fgc_4", "dc", (self.spacing_x / 2, self.spacing_y / 2), angle=180),
            i3.ConnectManhattan("fgc_1:out", "dc:in1", bend_radius=self.bend_radius),
            i3.ConnectManhattan("fgc_2:out", "dc:in2", bend_radius=self.bend_radius),
            i3.ConnectManhattan("fgc_3:out", "dc:out1", bend_radius=self.bend_radius),
            i3.ConnectManhattan("fgc_4:out", "dc:out2", bend_radius=self.bend_radius)
        ]

        return specs

    def _default_exposed_ports(self):
        exposed_ports = {
            "fgc_1:vertical_in": "in1",
            "fgc_2:vertical_in": "in2",
            "fgc_3:vertical_in": "out1",
            "fgc_4:vertical_in": "out2",
        }
        return exposed_ports


if __name__ == '__main__':
    dc = RoutedDC(bend_radius=20)
    my_circuit = dc.Layout()
    my_circuit.visualize(annotate=True)
    my_circuit.write_gdsii("Rounted_dc.gds")
2023-08-03
2