| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2020 The SkyWater PDK Authors. |
| # |
| # Use of this source code is governed by the Apache 2.0 |
| # license that can be found in the LICENSE file or at |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # 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') |
| TransistorSizeNumeric(units=1) |
| |
| >>> parse_size('sky130_fd_sc_ms__sdfrtp_1.v') |
| TransistorSizeNumeric(units=1) |
| |
| >>> parse_size('libraries/sky130_fd_sc_ms/v0.0.1/cells/sdfrtp/sky130_fd_sc_ms__sdfrtp_1.v') |
| TransistorSizeNumeric(units=1) |
| |
| >>> parse_size('libraries/sky130_fd_sc_ms/v0.0.1/cells/sdfrtp/sky130_fd_sc_ms__sdfrtp_1.bb.blackbox.v') |
| TransistorSizeNumeric(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('_') > 1: |
| s = '_' + (s.rsplit('_', 1)[-1]) |
| if not s or s == '_': |
| return None |
| try: |
| return TransistorSize.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 TransistorSize(abc.ABC): |
| """Drive strength variants of a given cell. |
| |
| >>> d1 = TransistorSize.from_suffix("_1") |
| >>> d2 = TransistorSize.from_suffix("_lp") |
| >>> d3 = TransistorSize.from_suffix("_m") |
| >>> d4 = TransistorSize.from_suffix("_2") |
| >>> TransistorSize.from_suffix("_abc") |
| Traceback (most recent call last): |
| ... |
| InvalidSuffixError: Invalid suffix: _abc |
| >>> l = [d1, d2, d3, d4] |
| >>> l |
| [TransistorSizeNumeric(units=1), TransistorSizeLowPower(lp_variant=0), TransistorSizeMinimum(), TransistorSizeNumeric(units=2)] |
| >>> l.sort() |
| >>> l |
| [TransistorSizeNumeric(units=1), TransistorSizeNumeric(units=2), TransistorSizeLowPower(lp_variant=0), TransistorSizeMinimum()] |
| """ |
| |
| @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, TransistorSize): |
| 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 TransistorSizeNumeric(TransistorSize): |
| """ |
| >>> s1 = TransistorSizeNumeric.from_suffix("_1") |
| >>> s2 = TransistorSizeNumeric.from_suffix("_2") |
| >>> s3 = TransistorSizeNumeric.from_suffix("_3") |
| >>> TransistorSizeNumeric.from_suffix("_-1") |
| Traceback (most recent call last): |
| ... |
| InvalidSuffixError: Invalid suffix: _-1 |
| >>> s1 |
| TransistorSizeNumeric(units=1) |
| >>> s2 |
| TransistorSizeNumeric(units=2) |
| >>> s3 |
| TransistorSizeNumeric(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 TransistorSizeLowPower(TransistorSize): |
| """ |
| >>> lp = TransistorSizeLowPower.from_suffix("_lp") |
| >>> lp2 = TransistorSizeLowPower.from_suffix("_lp2") |
| >>> lp3 = TransistorSizeLowPower.from_suffix("_lp3") |
| >>> TransistorSizeLowPower.from_suffix("_ld") |
| Traceback (most recent call last): |
| ... |
| InvalidSuffixError: Invalid suffix: _ld |
| >>> lp |
| TransistorSizeLowPower(lp_variant=0) |
| >>> lp2 |
| TransistorSizeLowPower(lp_variant=1) |
| >>> lp3 |
| TransistorSizeLowPower(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 TransistorSizeMinimum(TransistorSize): |
| """ |
| >>> m = TransistorSizeMinimum.from_suffix("_m") |
| >>> TransistorSizeMinimum.from_suffix("_m2") |
| Traceback (most recent call last): |
| ... |
| InvalidSuffixError: Invalid suffix: _m2 |
| >>> m |
| TransistorSizeMinimum() |
| >>> str(m) |
| 'with size minimum' |
| >>> m.describe() |
| 'minimum' |
| >>> m.suffix |
| '_m' |
| |
| >>> m1 = TransistorSizeMinimum() |
| >>> m2 = TransistorSizeMinimum() |
| >>> 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 "TransistorSizeMinimum()" |
| |
| 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} |
| |
| |
| TransistorSizeMinimum._object = TransistorSizeMinimum() |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| doctest.testmod() |