Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Klayout PyCell integration #174

Merged
merged 4 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "ihp-sg13g2/libs.tech/klayout/python/pycell4klayout-api"]
path = ihp-sg13g2/libs.tech/klayout/python/pycell4klayout-api
url = https://github.com/IHP-GmbH/pycell4klayout-api
[submodule "ihp-sg13g2/libs.tech/klayout/python/pypreprocessor"]
path = ihp-sg13g2/libs.tech/klayout/python/pypreprocessor
url = https://github.com/interpreters/pypreprocessor
1 change: 1 addition & 0 deletions ihp-sg13g2/libs.tech/klayout/python/pypreprocessor
Submodule pypreprocessor added at 5e8400
119 changes: 116 additions & 3 deletions ihp-sg13g2/libs.tech/klayout/python/sg13g2_pycell_lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@
from .sg13_tech import *

import pya
import psutil

import os
import io
import sys
import inspect
import re
import importlib
import pathlib
import tempfile

moduleNames = [
'nmos_code',
Expand All @@ -55,22 +60,130 @@
'inductor3_sp_code',
'dantenna_code',
'dpantenna_code'

]


"""
Support for 'conditional compilation' in a C-style manner of PyCell code:

#ifdef name
...some_code...
#else
...some_other_code...
#endif

The #ifdef-block is executed (name is considered as defined) if
1. An environment variable 'name' can be found case-insentive, or
2. The name can be found case-insentive as part of a process name of the process chain beginnig at
the current process upwards through all parent processes.
otherwise the #else-block is executed

The current process chain will be dumped if the environment variable 'IHP_PYCELL_LIB_PRINT_PROCESS_TREE'
is set.

The list of names which are used in an #ifdef-statement and are considered as 'defined' will be dumped
if the environment variable 'IHP_PYCELL_LIB_PRINT_PROCESS_TREE' is set.

"""

class PyCellLib(pya.Library):
def __init__(self):
self.description = "IHP SG13G2 Pcells"

tech = Tech.get('SG13_dev')

processNames = []
parent = None

p = psutil.Process()
with p.oneshot():
processNames.append(p.name().lower())
parent = p.parent()

maxDepth = 10;
while parent is not None and maxDepth > 0:
maxDepth -= 1
with parent.oneshot():
processNames.append(parent.name().lower())
parent = parent.parent()

if os.getenv('IHP_PYCELL_LIB_PRINT_PROCESS_TREE') is not None:
processChain = ''
isFirst = True
for processName in reversed(processNames):
if not isFirst:
processChain += ' <- '
processChain += "'" + processName + "'"
isFirst = False
print(f'Current process chain: {processChain}')

module = importlib.import_module(f"{__name__}.ihp.pypreprocessor")
preProcessor = getattr(module, "preprocessor")

definesSetToPrint = []

for moduleName in moduleNames:
module = importlib.import_module(f"{__name__}.ihp." + moduleName)
defines = []
definesSet = []

modulePath = os.path.join(os.path.dirname(__file__), 'ihp', f"{moduleName}.py")
moduleFile = io.open(modulePath, 'r', encoding=sys.stdin.encoding)

try:
for line in moduleFile:
match = re.match(r'^#ifdef\s+(\w+)', line)
if match:
define = match.group(1)
if define not in defines:
defines.append(define)

finally:
moduleFile.close()

envs = []
for env in os.environ:
envs.append(env.lower())

for define in defines:
locDefine = define.lower()
for processName in processNames:
if processName.find(locDefine) != -1:
definesSet.append(define)
else:
if locDefine in envs:
definesSet.append(define)

for defineSet in definesSet:
definesSetToPrint.append(defineSet)

modulePreProcPath = None

if len(defines) > 0:
modulePreProcPath = os.path.join(tempfile.gettempdir(), f"{moduleName}_pre.py")

pyPreProcessor = preProcessor(modulePath, modulePreProcPath, definesSet, removeMeta=False, resume=True, run=True)
pyPreProcessor.parse()

spec = importlib.util.spec_from_file_location(f"{__name__}.ihp.{moduleName}", modulePreProcPath)
module = importlib.util.module_from_spec(spec)
sys.modules[moduleName] = module

try:
spec.loader.exec_module(module)
except Exception:
sys.exit(1)

os.remove(modulePreProcPath)
else:
module = importlib.import_module(f"{__name__}.ihp." + moduleName)

match = re.fullmatch(r'^(\S+)_code$', moduleName)
if match:
func = getattr(module, f"{match.group(1)}")
self.layout().register_pcell(match.group(1), PCellWrapper(func(), tech))
self.layout().register_pcell(match.group(1), PCellWrapper(func(), tech, modulePreProcPath, modulePath))

if os.getenv('IHP_PYCELL_LIB_PRINT_DEFINES_SET') is not None:
print(f"Current defines set: {definesSetToPrint}")

self.register("SG13_dev")

Expand Down