blob: 0afddfe2b0944ee2b1ec68e4cdb20ba989897150 [file] [log] [blame]
#!/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):
return TransistorSize.from_suffix(s)
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 "transistors 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)
'transistors with size of 1 units'
>>> str(s2)
'transistors with size of 2 units'
>>> str(s3)
'transistors 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)
'transistors with size for low power'
>>> str(lp2)
'transistors with size for low power (alternative)'
>>> str(lp3)
'transistors 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)
'transistors 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()