| #!/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() |