blob: 1988cfd1271cfb417b959104ab202cfbb6eeb98e [file] [log] [blame]
#!/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()