369 lines
9.0 KiB
Python
369 lines
9.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright 2020 SkyWater PDK Authors
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import abc
|
|
import os
|
|
import operator
|
|
|
|
from dataclasses import dataclass
|
|
from dataclasses_json import dataclass_json
|
|
|
|
|
|
def parse_size(s):
|
|
"""
|
|
|
|
>>> parse_size('_1')
|
|
CellSizeNumeric(units=1)
|
|
|
|
>>> parse_size('a2111o_1')
|
|
CellSizeNumeric(units=1)
|
|
|
|
>>> parse_size('sky130_fd_sc_ms__sdfrtp_1.v')
|
|
CellSizeNumeric(units=1)
|
|
|
|
>>> parse_size('libraries/sky130_fd_sc_ms/v0.0.1/cells/sdfrtp/sky130_fd_sc_ms__sdfrtp_1.v')
|
|
CellSizeNumeric(units=1)
|
|
|
|
>>> parse_size('libraries/sky130_fd_sc_ms/v0.0.1/cells/sdfrtp/sky130_fd_sc_ms__sdfrtp_1.bb.blackbox.v')
|
|
CellSizeNumeric(units=1)
|
|
|
|
>>> parse_size('libraries/sky130_fd_sc_ms/v0.0.1/cells/sdfrtp/sky130_fd_sc_ms__sdfrtp.v')
|
|
>>> parse_size('sky130_fd_sc_ms__sdfrtp.v')
|
|
>>> parse_size('_blah')
|
|
"""
|
|
dirname, s = os.path.split(s)
|
|
if '.' in s:
|
|
s = s.split('.', 1)[0]
|
|
if s.count('_') > 0:
|
|
s = '_' + (s.rsplit('_', 1)[-1])
|
|
if not s or s == '_':
|
|
return None
|
|
try:
|
|
return CellSize.from_suffix(s)
|
|
except InvalidSuffixError as e:
|
|
return None
|
|
|
|
|
|
class InvalidSuffixError(ValueError):
|
|
def __init__(self, s):
|
|
ValueError.__init__(self, "Invalid suffix: {}".format(s.strip()))
|
|
|
|
|
|
class CellSize(abc.ABC):
|
|
"""Drive strength variants of a given cell.
|
|
|
|
See Also
|
|
--------
|
|
skywater_pdk.base.Cell
|
|
skywater_pdk.sizes.CellSizeNumeric
|
|
skywater_pdk.sizes.CellSizeLowPower
|
|
skywater_pdk.sizes.CellSizeMinimum
|
|
|
|
Examples
|
|
--------
|
|
>>> d1 = CellSize.from_suffix("_1")
|
|
>>> d2 = CellSize.from_suffix("_lp")
|
|
>>> d3 = CellSize.from_suffix("_m")
|
|
>>> d4 = CellSize.from_suffix("_2")
|
|
>>> CellSize.from_suffix("_abc")
|
|
Traceback (most recent call last):
|
|
...
|
|
InvalidSuffixError: Invalid suffix: _abc
|
|
>>> l = [d1, d2, d3, d4]
|
|
>>> l
|
|
[CellSizeNumeric(units=1), CellSizeLowPower(lp_variant=0), CellSizeMinimum(), CellSizeNumeric(units=2)]
|
|
>>> l.sort()
|
|
>>> l
|
|
[CellSizeNumeric(units=1), CellSizeNumeric(units=2), CellSizeLowPower(lp_variant=0), CellSizeMinimum()]
|
|
"""
|
|
|
|
@abc.abstractmethod
|
|
def describe(self):
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def suffix(self):
|
|
raise NotImplementedError
|
|
|
|
@classmethod
|
|
def from_suffix(cls, s):
|
|
errors = []
|
|
for subcls in cls.__subclasses__():
|
|
try:
|
|
return subcls.from_suffix(s)
|
|
except (ValueError, AssertionError) as e:
|
|
errors.append((subcls.__name__, e))
|
|
assert errors, ("Unknown error!?", s)
|
|
msg = [s, '']
|
|
for cls_name, e in errors:
|
|
if isinstance(e, ValueError):
|
|
continue
|
|
msg.append("{} failed with: {}".format(cls_name, e))
|
|
raise InvalidSuffixError("\n".join(msg))
|
|
|
|
def __str__(self):
|
|
return "with size {}".format(self.describe())
|
|
|
|
def _cmp(self, op, o):
|
|
if not isinstance(o, CellSize):
|
|
return False
|
|
return op(self.suffix, o.suffix)
|
|
|
|
# Comparison operators
|
|
def __lt__(self, o):
|
|
return self._cmp(operator.lt, o)
|
|
|
|
def __le__(self, o):
|
|
return self._cmp(operator.le, o)
|
|
|
|
def __eq__(self, o):
|
|
return self._cmp(operator.eq, o)
|
|
|
|
def __ne__(self, o):
|
|
return self._cmp(operator.ne, o)
|
|
|
|
def __ge__(self, o):
|
|
return self._cmp(operator.ge, o)
|
|
|
|
def __gt__(self, o):
|
|
return self._cmp(operator.gt, o)
|
|
|
|
|
|
@dataclass_json
|
|
@dataclass(frozen=True)
|
|
class CellSizeNumeric(CellSize):
|
|
"""
|
|
|
|
See Also
|
|
--------
|
|
skywater_pdk.base.Cell
|
|
skywater_pdk.sizes.CellSize
|
|
skywater_pdk.sizes.CellSizeLowPower
|
|
skywater_pdk.sizes.CellSizeMinimum
|
|
|
|
Examples
|
|
--------
|
|
>>> s1 = CellSizeNumeric.from_suffix("_1")
|
|
>>> s2 = CellSizeNumeric.from_suffix("_2")
|
|
>>> s3 = CellSizeNumeric.from_suffix("_3")
|
|
>>> CellSizeNumeric.from_suffix("_-1")
|
|
Traceback (most recent call last):
|
|
...
|
|
InvalidSuffixError: Invalid suffix: _-1
|
|
>>> s1
|
|
CellSizeNumeric(units=1)
|
|
>>> s2
|
|
CellSizeNumeric(units=2)
|
|
>>> s3
|
|
CellSizeNumeric(units=3)
|
|
>>> str(s1)
|
|
'with size of 1 units'
|
|
>>> str(s2)
|
|
'with size of 2 units'
|
|
>>> str(s3)
|
|
'with size of 3 units (invalid?)'
|
|
>>> s1.describe()
|
|
'of 1 units'
|
|
>>> s2.describe()
|
|
'of 2 units'
|
|
>>> s3.describe()
|
|
'of 3 units (invalid?)'
|
|
>>> s1.suffix
|
|
'_1'
|
|
>>> s2.suffix
|
|
'_2'
|
|
>>> s3.suffix
|
|
'_3'
|
|
"""
|
|
units: int
|
|
|
|
VALID_UNIT_VALUES = (0, 1, 2, 4, 8, 6, 12, 14, 16, 20, 32)
|
|
|
|
def describe(self):
|
|
suffix = ""
|
|
if self.units not in self.VALID_UNIT_VALUES:
|
|
suffix = " (invalid?)"
|
|
|
|
return "of {} units{}".format(self.units, suffix)
|
|
|
|
@property
|
|
def suffix(self):
|
|
return "_{}".format(self.units)
|
|
|
|
@classmethod
|
|
def from_suffix(cls, s):
|
|
if not s.startswith("_"):
|
|
raise InvalidSuffixError(s)
|
|
i = int(s[1:])
|
|
if i < 0:
|
|
raise InvalidSuffixError(s)
|
|
return cls(i)
|
|
|
|
|
|
@dataclass_json
|
|
@dataclass(frozen=True)
|
|
class CellSizeLowPower(CellSize):
|
|
"""
|
|
|
|
See Also
|
|
--------
|
|
skywater_pdk.base.Cell
|
|
skywater_pdk.sizes.CellSize
|
|
skywater_pdk.sizes.CellSizeNumeric
|
|
skywater_pdk.sizes.CellSizeMinimum
|
|
|
|
Examples
|
|
--------
|
|
>>> lp = CellSizeLowPower.from_suffix("_lp")
|
|
>>> lp2 = CellSizeLowPower.from_suffix("_lp2")
|
|
>>> lp3 = CellSizeLowPower.from_suffix("_lp3")
|
|
>>> CellSizeLowPower.from_suffix("_ld")
|
|
Traceback (most recent call last):
|
|
...
|
|
InvalidSuffixError: Invalid suffix: _ld
|
|
>>> lp
|
|
CellSizeLowPower(lp_variant=0)
|
|
>>> lp2
|
|
CellSizeLowPower(lp_variant=1)
|
|
>>> lp3
|
|
CellSizeLowPower(lp_variant=2)
|
|
>>> str(lp)
|
|
'with size for low power'
|
|
>>> str(lp2)
|
|
'with size for low power (alternative)'
|
|
>>> str(lp3)
|
|
'with size for low power (extra alternative 0)'
|
|
>>> lp.describe()
|
|
'for low power'
|
|
>>> lp2.describe()
|
|
'for low power (alternative)'
|
|
>>> lp3.describe()
|
|
'for low power (extra alternative 0)'
|
|
>>> lp.suffix
|
|
'_lp'
|
|
>>> lp2.suffix
|
|
'_lp2'
|
|
>>> lp3.suffix
|
|
'_lp3'
|
|
"""
|
|
lp_variant: int = 0
|
|
|
|
def describe(self):
|
|
if self.lp_variant == 0:
|
|
suffix = ""
|
|
elif self.lp_variant == 1:
|
|
suffix = " (alternative)"
|
|
else:
|
|
assert self.lp_variant >= 2, self.lp_variant
|
|
suffix = " (extra alternative {})".format(self.lp_variant-2)
|
|
return "for low power"+suffix
|
|
|
|
@property
|
|
def suffix(self):
|
|
if self.lp_variant == 0:
|
|
return "_lp"
|
|
else:
|
|
assert self.lp_variant > 0, self.lp_variant
|
|
return "_lp{}".format(self.lp_variant+1)
|
|
|
|
@classmethod
|
|
def from_suffix(cls, s):
|
|
if not s.startswith("_lp"):
|
|
raise InvalidSuffixError(s)
|
|
if s == "_lp":
|
|
return cls()
|
|
elif s == "_lp2":
|
|
return cls(1)
|
|
else:
|
|
try:
|
|
i = int(s[3:])
|
|
except ValueError as e:
|
|
raise InvalidSuffixError(s)
|
|
assert i > 2, (s, i)
|
|
return cls(i-1)
|
|
|
|
|
|
class CellSizeMinimum(CellSize):
|
|
"""
|
|
|
|
See Also
|
|
--------
|
|
skywater_pdk.base.Cell
|
|
skywater_pdk.sizes.CellSize
|
|
skywater_pdk.sizes.CellSizeNumeric
|
|
skywater_pdk.sizes.CellSizeLowPower
|
|
|
|
|
|
Examples
|
|
--------
|
|
>>> m = CellSizeMinimum.from_suffix("_m")
|
|
>>> CellSizeMinimum.from_suffix("_m2")
|
|
Traceback (most recent call last):
|
|
...
|
|
InvalidSuffixError: Invalid suffix: _m2
|
|
>>> m
|
|
CellSizeMinimum()
|
|
>>> str(m)
|
|
'with size minimum'
|
|
>>> m.describe()
|
|
'minimum'
|
|
>>> m.suffix
|
|
'_m'
|
|
|
|
>>> m1 = CellSizeMinimum()
|
|
>>> m2 = CellSizeMinimum()
|
|
>>> assert m1 is m2
|
|
"""
|
|
_object = None
|
|
def __new__(cls):
|
|
if cls._object is None:
|
|
cls._object = object.__new__(cls)
|
|
return cls._object
|
|
|
|
def __repr__(self):
|
|
return "CellSizeMinimum()"
|
|
|
|
def describe(self):
|
|
return "minimum"
|
|
|
|
@property
|
|
def suffix(self):
|
|
return "_m"
|
|
|
|
@classmethod
|
|
def from_suffix(cls, s):
|
|
if s != "_m":
|
|
raise InvalidSuffixError(s)
|
|
return cls()
|
|
|
|
def __hash__(self):
|
|
return id(self)
|
|
|
|
def to_dict(self):
|
|
return {'minimum': None}
|
|
|
|
|
|
CellSizeMinimum._object = CellSizeMinimum()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
doctest.testmod()
|