| # SPDX-License-Identifier: GPL-2.0-or-later | |
| # PDKMaster & co. don't put a license requirement on the generated files. | |
| # The generated gds files in this project are released under the LGPL 2.0 or later license. | |
| from pathlib import Path | |
| from typing import Dict, Any, cast | |
| import pya as _pya | |
| # Disable type checking | |
| pya = cast(Any, _pya) | |
| from pdkmaster.design import library as _lib | |
| from pdkmaster.io.klayout import export as _klexp | |
| from c4m.pdk import sky130 | |
| from . import frame as _frm | |
| __all__ = ["gen_gds"] | |
| def _postprocess(*, klay: "pya.Layout"): | |
| # Post process | |
| # - split tapdiff into tap and diff layers | |
| # - remove pad drawing layer to avoid interacting with RDL | |
| nwell_idx = klay.layer(64, 20) | |
| diff_idx = klay.layer(65, 20) | |
| tap_idx = klay.layer(65, 44) | |
| nsdm_idx = klay.layer(93, 44) | |
| psdm_idx = klay.layer(94, 20) | |
| pad_idx = klay.layer(76, 20) | |
| for cell in klay.each_cell(): | |
| nwell = pya.Region(cell.shapes(nwell_idx)) | |
| nsdm = pya.Region(cell.shapes(nsdm_idx)) | |
| psdm = pya.Region(cell.shapes(psdm_idx)) | |
| tap_cover = (nsdm & nwell) + (psdm - nwell) | |
| diff_shapes = cell.shapes(diff_idx) | |
| tap_shapes = cell.shapes(tap_idx) | |
| difftap = pya.Region(cell.shapes(diff_idx)) # Original difftap layer to be split | |
| tap = difftap & tap_cover | |
| diff = difftap - tap | |
| diff_shapes.clear() | |
| diff_shapes.insert(diff) | |
| tap_shapes.insert(tap) | |
| cell.shapes(pad_idx).clear() | |
| def gen_gds(*, name: str, gds_out: Path, gds_empty: Path): | |
| top_name = "user_analog_project_wrapper" | |
| lib = _lib.Library( | |
| name=name, tech=sky130.tech, cktfab=sky130.cktfab, layoutfab=sky130.layoutfab, | |
| ) | |
| # Create user_analog_project_wrapper top cell | |
| top = lib.new_cell(name=top_name) | |
| ckt = top.new_circuit() | |
| layouter = top.new_circuitlayouter() | |
| # Add circuit and | |
| layouter.layout.boundary = _frm.boundary | |
| # Convert to klayout | |
| klay = _klexp.export2db( | |
| obj=lib, add_pin_label=True, gds_layers=sky130.gds_layers, cell_name=None, merge=True, | |
| ) | |
| _postprocess(klay=klay) | |
| # Instantiate the empty cell in top cell | |
| emptylib = pya.Library() | |
| emptylib.register("empty") | |
| emptylayout = emptylib.layout() | |
| emptylayout.read(str(gds_empty)) | |
| emptylibcell_idx = emptylayout.cell("user_analog_project_wrapper_empty").cell_index() | |
| emptycell_idx = klay.add_lib_cell(emptylib, emptylibcell_idx) | |
| ktop = klay.cell(top_name) | |
| ktop.insert(pya.CellInstArray(emptycell_idx, pya.Trans.R0)) | |
| ktech = pya.Technology.technology_by_name("Skywater_S8") | |
| ksaveopts = ktech.save_layout_options.dup() | |
| ksaveopts.write_context_info = False | |
| klay.write(str(gds_out), ksaveopts) |