From c8da4a343d3e38f576606f4f6bcacf825c2191ae Mon Sep 17 00:00:00 2001 From: Niko Savola Date: Fri, 13 Oct 2023 18:16:56 -0700 Subject: [PATCH 1/2] Generate technology if needed for netlist export --- gplugins/common/config.py | 2 ++ gplugins/klayout/get_netlist.py | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/gplugins/common/config.py b/gplugins/common/config.py index 8393351a..a529eb29 100644 --- a/gplugins/common/config.py +++ b/gplugins/common/config.py @@ -22,6 +22,8 @@ class Path: results_tidy3d = home / ".tidy3d" test_data = repo / "test-data" sparameters_repo = test_data / "sp" + klayout = module_path / "klayout" + klayout_tech = klayout / "tech.lyt" extra = repo / "extra" cwd = pathlib.Path.cwd() diff --git a/gplugins/klayout/get_netlist.py b/gplugins/klayout/get_netlist.py index 63a5fa0b..dfc55708 100644 --- a/gplugins/klayout/get_netlist.py +++ b/gplugins/klayout/get_netlist.py @@ -1,7 +1,12 @@ +from pathlib import Path + +import gdsfactory as gf import kfactory as kf import klayout.db as kdb from gdsfactory.typings import PathType +from gplugins.common.config import PATH + def get_l2n( gdspath: PathType, klayout_tech_path: PathType | None = None @@ -20,13 +25,18 @@ def get_l2n( lib.read(filename=str(gdspath)) c = lib[0] - if klayout_tech_path: - technology = kdb.Technology() - technology.load(str(klayout_tech_path)) + tech_dir = PATH.klayout + tech_dir.mkdir(exist_ok=True, parents=True) + if not klayout_tech_path: + gf.get_active_pdk().klayout_technology.write_tech(tech_dir) + klayout_tech_path = Path(tech_dir) / "tech.lyt" + + technology = kdb.Technology() + technology.load(str(klayout_tech_path)) + lib.technology_name = technology.name l2n = kf.kdb.LayoutToNetlist(c.begin_shapes_rec(0)) - l2n.threads = kf.config.n_threads - for l_idx in c.kcl.layer_indexes(): + for l_idx in c.kcl.layer_indices(): l2n.connect(l2n.make_layer(l_idx, f"layer{l_idx}")) l2n.extract_netlist() return l2n @@ -52,8 +62,6 @@ def get_netlist(gdspath: PathType, **kwargs) -> kdb.Netlist: if __name__ == "__main__": from gdsfactory.samples.demo.lvs import pads_correct, pads_shorted - from gplugins.common.config import PATH - c = pads_correct() c = pads_shorted() gdspath = c.write_gds() From 9ba5c1020364642d5fd0fdc9f9c60519bbaa5131 Mon Sep 17 00:00:00 2001 From: Niko Savola Date: Mon, 16 Oct 2023 16:19:04 -0700 Subject: [PATCH 2/2] Specify connections for `LayoutToNetlist` manually instead of using technologies See https://github.com/KLayout/klayout/issues/1223#issuecomment-1349644804 for justification using this approach --- gplugins/klayout/get_netlist.py | 53 ++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/gplugins/klayout/get_netlist.py b/gplugins/klayout/get_netlist.py index dfc55708..b18031b5 100644 --- a/gplugins/klayout/get_netlist.py +++ b/gplugins/klayout/get_netlist.py @@ -1,5 +1,3 @@ -from pathlib import Path - import gdsfactory as gf import kfactory as kf import klayout.db as kdb @@ -22,22 +20,57 @@ def get_l2n( """ lib = kf.kcell.KCLayout(str(gdspath)) - lib.read(filename=str(gdspath)) - c = lib[0] + Tech = kdb.Technology() tech_dir = PATH.klayout tech_dir.mkdir(exist_ok=True, parents=True) if not klayout_tech_path: gf.get_active_pdk().klayout_technology.write_tech(tech_dir) - klayout_tech_path = Path(tech_dir) / "tech.lyt" + klayout_tech_path = tech_dir - technology = kdb.Technology() - technology.load(str(klayout_tech_path)) - lib.technology_name = technology.name + # klayout tech path is now assumed to contain a `tech.lyt`` file to use + technology = Tech.load(str(klayout_tech_path / "tech.lyt")) + + lib.read(filename=str(gdspath)) + c = lib[0] l2n = kf.kdb.LayoutToNetlist(c.begin_shapes_rec(0)) - for l_idx in c.kcl.layer_indices(): - l2n.connect(l2n.make_layer(l_idx, f"layer{l_idx}")) + l2n.threads = kf.config.n_threads + + reversed_layer_map = dict() + for k, v in gf.get_active_pdk().layers.items(): + reversed_layer_map[v] = reversed_layer_map.get(v, set()) | {k} + + # define stack connections through vias + layer_connection_iter = [ + [ + (connection.layer_a(), connection.via_layer(), connection.layer_b()) + for connection in connectivity.each_connection() + ] + for connectivity in technology.component("connectivity").each() + ][0] + correct_layer_names = set(sum(layer_connection_iter, ())) + + # define the layers to be extracted + for l_idx in c.kcl.layer_indexes(): + layer_info = c.kcl.get_info(l_idx) + names = reversed_layer_map[(layer_info.layer, layer_info.datatype)] + try: + same_name_as_in_connections = next(iter(correct_layer_names & names)) + except StopIteration: + same_name_as_in_connections = next(iter(names)) + l2n.connect(l2n.make_layer(l_idx, same_name_as_in_connections)) + + for layer_a, layer_via, layer_b in ( + (l2n.layer_by_name(layer) for layer in layers) + for layers in layer_connection_iter + ): + # Don't try to connect Nones + if all((layer_a, layer_via)): + l2n.connect(layer_a, layer_via) + if all((layer_b, layer_via)): + l2n.connect(layer_via, layer_b) + l2n.extract_netlist() return l2n