Add classes for the drive strengths.
diff --git a/scripts/python-skywater-pdk/skywater_pdk/drives.py b/scripts/python-skywater-pdk/skywater_pdk/drives.py
new file mode 100644
index 0000000..6176c6e
--- /dev/null
+++ b/scripts/python-skywater-pdk/skywater_pdk/drives.py
@@ -0,0 +1,262 @@
+#!/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()