#!/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('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('_') > 1: 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()