Compare commits
No commits in common. "master" and "klayout" have entirely different histories.
@ -10,7 +10,6 @@ has deprived the man of both a schematic and a better connectivity tool.
|
|||||||
|
|
||||||
- [Source repository](https://mpxd.net/code/jan/snarled)
|
- [Source repository](https://mpxd.net/code/jan/snarled)
|
||||||
- [PyPI](https://pypi.org/project/snarled)
|
- [PyPI](https://pypi.org/project/snarled)
|
||||||
- [Github mirror](https://github.com/anewusername/snarled)
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -42,10 +42,10 @@ classifiers = [
|
|||||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.10"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"klayout~=0.29",
|
"klayout~=0.28",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -54,40 +54,3 @@ path = "snarled/__init__.py"
|
|||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
snarled = "snarled.main:main"
|
snarled = "snarled.main:main"
|
||||||
|
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
exclude = [
|
|
||||||
".git",
|
|
||||||
"dist",
|
|
||||||
]
|
|
||||||
line-length = 145
|
|
||||||
indent-width = 4
|
|
||||||
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
||||||
lint.select = [
|
|
||||||
"NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG",
|
|
||||||
"C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT",
|
|
||||||
"ARG", "PL", "R", "TRY",
|
|
||||||
"G010", "G101", "G201", "G202",
|
|
||||||
"Q002", "Q003", "Q004",
|
|
||||||
]
|
|
||||||
lint.ignore = [
|
|
||||||
#"ANN001", # No annotation
|
|
||||||
"ANN002", # *args
|
|
||||||
"ANN003", # **kwargs
|
|
||||||
"ANN401", # Any
|
|
||||||
"ANN101", # self: Self
|
|
||||||
"SIM108", # single-line if / else assignment
|
|
||||||
"RET504", # x=y+z; return x
|
|
||||||
"PIE790", # unnecessary pass
|
|
||||||
"ISC003", # non-implicit string concatenation
|
|
||||||
"C408", # dict(x=y) instead of {'x': y}
|
|
||||||
"PLR09", # Too many xxx
|
|
||||||
"PLR2004", # magic number
|
|
||||||
"PLC0414", # import x as x
|
|
||||||
"TRY003", # Long exception message
|
|
||||||
"PTH123", # open()
|
|
||||||
"UP015", # open(..., 'rt')
|
|
||||||
"PLW2901", # overwriting loop var
|
|
||||||
]
|
|
||||||
|
|
||||||
|
@ -12,10 +12,7 @@ has deprived the man of a schematic and a better connectivity tool.
|
|||||||
The main functionality is in `trace`.
|
The main functionality is in `trace`.
|
||||||
`__main__.py` details the command-line interface.
|
`__main__.py` details the command-line interface.
|
||||||
"""
|
"""
|
||||||
from .trace import (
|
from .trace import trace_layout, TraceAnalysis
|
||||||
trace_layout as trace_layout,
|
|
||||||
TraceAnalysis as TraceAnalysis,
|
|
||||||
)
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
__version__ = '1.0'
|
__version__ = '1.0'
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
from collections.abc import Sequence, Iterable
|
from typing import Sequence, Iterable
|
||||||
import logging
|
import logging
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from klayout import db
|
from klayout import db
|
||||||
from .types import lnum_t, layer_t
|
from .types import lnum_t, layer_t
|
||||||
from .utils import SnarledError
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -166,8 +165,8 @@ def trace_layout(
|
|||||||
# Merge labels from a separate layout if asked
|
# Merge labels from a separate layout if asked
|
||||||
if lfile_path:
|
if lfile_path:
|
||||||
if not lfile_map:
|
if not lfile_map:
|
||||||
raise SnarledError('Asked to load labels from a separate file, but no '
|
raise Exception('Asked to load labels from a separate file, but no '
|
||||||
+ 'label layers were specified in lfile_map')
|
'label layers were specified in lfile_map')
|
||||||
|
|
||||||
if lfile_layer_map is None:
|
if lfile_layer_map is None:
|
||||||
lfile_layer_map = layer_map
|
lfile_layer_map = layer_map
|
||||||
@ -200,7 +199,7 @@ def trace_layout(
|
|||||||
|
|
||||||
# Create l2n text layers
|
# Create l2n text layers
|
||||||
layer2texts = {}
|
layer2texts = {}
|
||||||
for layer in labels_map:
|
for layer in labels_map.keys():
|
||||||
if isinstance(layer, str):
|
if isinstance(layer, str):
|
||||||
layer = layer_map[layer]
|
layer = layer_map[layer]
|
||||||
klayer = layout.layer(*layer)
|
klayer = layout.layer(*layer)
|
||||||
@ -248,7 +247,7 @@ def trace_layout(
|
|||||||
#
|
#
|
||||||
# Return merged nets
|
# Return merged nets
|
||||||
#
|
#
|
||||||
top_circuits = [cc for cc, _ in zip(nl.each_circuit_top_down(), range(nl.top_circuit_count()), strict=False)]
|
top_circuits = [cc for cc, _ in zip(nl.each_circuit_top_down(), range(nl.top_circuit_count()))]
|
||||||
|
|
||||||
# Nets with more than one label get their labels joined with a comma
|
# Nets with more than one label get their labels joined with a comma
|
||||||
nets = [
|
nets = [
|
||||||
@ -276,8 +275,9 @@ def _get_topcell(
|
|||||||
"""
|
"""
|
||||||
if name is None:
|
if name is None:
|
||||||
return layout.top_cell()
|
return layout.top_cell()
|
||||||
ind = layout.cell_by_name(name)
|
else:
|
||||||
return layout.cell(ind)
|
ind = layout.cell_by_name(name)
|
||||||
|
return layout.cell(ind)
|
||||||
|
|
||||||
|
|
||||||
def _write_net_layout(
|
def _write_net_layout(
|
||||||
|
@ -5,10 +5,6 @@ from .types import layer_t
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SnarledError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def strip_underscored_label(string: str) -> str:
|
def strip_underscored_label(string: str) -> str:
|
||||||
"""
|
"""
|
||||||
If the label ends in an underscore followed by an integer, strip
|
If the label ends in an underscore followed by an integer, strip
|
||||||
@ -54,18 +50,18 @@ def read_layermap(path: str) -> dict[str, tuple[int, int]]:
|
|||||||
|
|
||||||
for cc in '*-()':
|
for cc in '*-()':
|
||||||
if cc in line:
|
if cc in line:
|
||||||
raise SnarledError(f'Failed to read layermap on line {nn} due to special character "{cc}"')
|
raise Exception(f'Failed to read layermap on line {nn} due to special character "{cc}"')
|
||||||
|
|
||||||
for cc in ':/':
|
for cc in ':/':
|
||||||
if cc not in line:
|
if cc not in line:
|
||||||
raise SnarledError(f'Failed to read layermap on line {nn}; missing "{cc}"')
|
raise Exception(f'Failed to read layermap on line {nn}; missing "{cc}"')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
layer_part, name = line.split(':')
|
layer_part, name = line.split(':')
|
||||||
layer_nums = str2lnum(layer_part)
|
layer_nums = str2lnum(layer_part)
|
||||||
except Exception:
|
except Exception as err:
|
||||||
logger.exception(f'Layer map read failed on line {nn}')
|
logger.error(f'Layer map read failed on line {nn}')
|
||||||
raise
|
raise err
|
||||||
|
|
||||||
layer_map[name.strip()] = layer_nums
|
layer_map[name.strip()] = layer_nums
|
||||||
|
|
||||||
@ -105,7 +101,7 @@ def read_connectivity(path: str) -> list[tuple[layer_t, layer_t | None, layer_t]
|
|||||||
parts = line.split(',')
|
parts = line.split(',')
|
||||||
|
|
||||||
if len(parts) not in (2, 3):
|
if len(parts) not in (2, 3):
|
||||||
raise SnarledError(f'Too many commas in connectivity spec on line {nn}')
|
raise Exception(f'Too many commas in connectivity spec on line {nn}')
|
||||||
|
|
||||||
layers = []
|
layers = []
|
||||||
for part in parts:
|
for part in parts:
|
||||||
@ -113,13 +109,13 @@ def read_connectivity(path: str) -> list[tuple[layer_t, layer_t | None, layer_t]
|
|||||||
if '/' in part:
|
if '/' in part:
|
||||||
try:
|
try:
|
||||||
layer = str2lnum(part)
|
layer = str2lnum(part)
|
||||||
except Exception:
|
except Exception as err:
|
||||||
logger.exception(f'Connectivity spec read failed on line {nn}')
|
logger.error(f'Connectivity spec read failed on line {nn}')
|
||||||
raise
|
raise err
|
||||||
else:
|
else:
|
||||||
layer = part.strip()
|
layer = part.strip()
|
||||||
if not layer:
|
if not layer:
|
||||||
raise SnarledError(f'Empty layer in connectivity spec on line {nn}')
|
raise Exception(f'Empty layer in connectivity spec on line {nn}')
|
||||||
layers.append(layer)
|
layers.append(layer)
|
||||||
|
|
||||||
if len(layers) == 2:
|
if len(layers) == 2:
|
||||||
@ -160,7 +156,7 @@ def read_remap(path: str) -> dict[layer_t, layer_t]:
|
|||||||
parts = line.split(':')
|
parts = line.split(':')
|
||||||
|
|
||||||
if len(parts) != 2:
|
if len(parts) != 2:
|
||||||
raise SnarledError(f'Too many commas in layer remap spec on line {nn}')
|
raise Exception(f'Too many commas in layer remap spec on line {nn}')
|
||||||
|
|
||||||
layers = []
|
layers = []
|
||||||
for part in parts:
|
for part in parts:
|
||||||
@ -168,13 +164,13 @@ def read_remap(path: str) -> dict[layer_t, layer_t]:
|
|||||||
if '/' in part:
|
if '/' in part:
|
||||||
try:
|
try:
|
||||||
layer = str2lnum(part)
|
layer = str2lnum(part)
|
||||||
except Exception:
|
except Exception as err:
|
||||||
logger.exception(f'Layer remap spec read failed on line {nn}')
|
logger.error(f'Layer remap spec read failed on line {nn}')
|
||||||
raise
|
raise err
|
||||||
else:
|
else:
|
||||||
layer = part.strip()
|
layer = part.strip()
|
||||||
if not layer:
|
if not layer:
|
||||||
raise SnarledError(f'Empty layer in layer remap spec on line {nn}')
|
raise Exception(f'Empty layer in layer remap spec on line {nn}')
|
||||||
layers.append(layer)
|
layers.append(layer)
|
||||||
|
|
||||||
remap[layers[0]] = layers[1]
|
remap[layers[0]] = layers[1]
|
||||||
|
Loading…
Reference in New Issue
Block a user