| # 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 Any, cast |
| |
| import pya as _pya |
| # Disable type checking |
| pya = cast(Any, _pya) |
| |
| from pdkmaster.technology import geometry as _geo |
| 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, sram as _ram |
| |
| |
| __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 the SRAM |
| sram_cell = _ram.ConnectedSRAM(lib=lib) |
| sram_inst = ckt.instantiate(sram_cell, name="sramtop") |
| layouter.place(sram_inst, origin=_geo.origin) |
| |
| # 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) |