blob: 6176c6ec2518975e96c7ebbeb25b6da17a48cea8 [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
class InvalidSuffixError(ValueError):
def __init__(self, s):
ValueError.__init__(self, "Invalid suffix: {}".format(s.strip()))
class DriveStrength(abc.ABC):
"""Drive strength variants of a given cell.
>>> d1 = DriveStrength.from_suffix("_1")
>>> d2 = DriveStrength.from_suffix("_lp")
>>> d3 = DriveStrength.from_suffix("_m")
>>> d4 = DriveStrength.from_suffix("_2")
>>> DriveStrength.from_suffix("_abc")
Traceback (most recent call last):
...
InvalidSuffixError: Invalid suffix: _abc
>>> l = [d1, d2, d3, d4]
>>> l
[DriveStrengthNumeric(units=1), DriveStrengthLowPower(variant=0), DriveStrengthMinimum(), DriveStrengthNumeric(units=2)]
>>> l.sort()
>>> l
[DriveStrengthNumeric(units=1), DriveStrengthNumeric(units=2), DriveStrengthLowPower(variant=0), DriveStrengthMinimum()]
"""
@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 "drive strength {}".format(self.describe())
def _cmp(self, op, o):
if not isinstance(o, DriveStrength):
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(frozen=True)
class DriveStrengthNumeric(DriveStrength):
"""
>>> s1 = DriveStrengthNumeric.from_suffix("_1")
>>> s2 = DriveStrengthNumeric.from_suffix("_2")
>>> s3 = DriveStrengthNumeric.from_suffix("_3")
>>> DriveStrengthNumeric.from_suffix("_0")
Traceback (most recent call last):
...
InvalidSuffixError: Invalid suffix: _0
>>> s1
DriveStrengthNumeric(units=1)
>>> s2
DriveStrengthNumeric(units=2)
>>> s3
DriveStrengthNumeric(units=3)
>>> str(s1)
'drive strength 1 units'
>>> str(s2)
'drive strength 2 units'
>>> str(s3)
'drive strength 3 units (invalid?)'
>>> s1.describe()
'1 units'
>>> s2.describe()
'2 units'
>>> s3.describe()
'3 units (invalid?)'
>>> s1.suffix
'_1'
>>> s2.suffix
'_2'
>>> s3.suffix
'_3'
"""
units: int
def describe(self):
suffix = ""
if self.units not in (1, 2, 4):
suffix = " (invalid?)"
return "{} 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(frozen=True)
class DriveStrengthLowPower(DriveStrength):
"""
>>> lp = DriveStrengthLowPower.from_suffix("_lp")
>>> lp2 = DriveStrengthLowPower.from_suffix("_lp2")
>>> lp3 = DriveStrengthLowPower.from_suffix("_lp3")
>>> DriveStrengthLowPower.from_suffix("_ld")
Traceback (most recent call last):
...
InvalidSuffixError: Invalid suffix: _ld
>>> lp
DriveStrengthLowPower(variant=0)
>>> lp2
DriveStrengthLowPower(variant=1)
>>> lp3
DriveStrengthLowPower(variant=2)
>>> str(lp)
'drive strength Low Power'
>>> str(lp2)
'drive strength Low Power (alternative)'
>>> str(lp3)
'drive strength Low Power (extra alternative 0)'
>>> lp.describe()
'Low Power'
>>> lp2.describe()
'Low Power (alternative)'
>>> lp3.describe()
'Low Power (extra alternative 0)'
>>> lp.suffix
'_lp'
>>> lp2.suffix
'_lp2'
>>> lp3.suffix
'_lp3'
"""
variant: int = 0
def describe(self):
if self.variant == 0:
suffix = ""
elif self.variant == 1:
suffix = " (alternative)"
else:
assert self.variant >= 2, self.variant
suffix = " (extra alternative {})".format(self.variant-2)
return "Low Power"+suffix
@property
def suffix(self):
if self.variant == 0:
return "_lp"
else:
assert self.variant > 0, self.variant
return "_lp{}".format(self.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 DriveStrengthMinimum(DriveStrength):
"""
>>> m = DriveStrengthMinimum.from_suffix("_m")
>>> DriveStrengthMinimum.from_suffix("_m2")
Traceback (most recent call last):
...
InvalidSuffixError: Invalid suffix: _m2
>>> m
DriveStrengthMinimum()
>>> str(m)
'drive strength minimum'
>>> m.describe()
'minimum'
>>> m.suffix
'_m'
"""
def __repr__(self):
return "DriveStrengthMinimum()"
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()
if __name__ == "__main__":
import doctest
doctest.testmod()