rules.py
1 # SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-or-later OR CERN-OHL-S-2.0+ OR Apache-2.0 2 # This file was named ruleprims.py as rule.py seemed to be able to confuse pylance 3 # from vscode. 4 from itertools import product 5 from typing import Iterable 6 7 from ...typing import MultiT, cast_MultiT, OptMultiT, cast_OptMultiT 8 from .. import ( 9 property_ as _prp, rule as _rle, mask as _msk, technology_ as _tch, 10 ) 11 12 from ._core import _Primitive, _MaskPrimitive 13 from ._derived import _Intersect 14 15 16 __all__ = [ 17 "RulePrimitiveT", 18 "MinWidth", "Spacing", "Enclosure", "NoOverlap", 19 ] 20 21 22 class _RulePrimitive(_Primitive): 23 """Subclasses of _RulePrimitive represent extra design rules without further 24 physical representation of a Primitive. They thus don't have a layout etc. 25 It's a base class that needs to be subclassed. 26 """ 27 pass 28 RulePrimitiveT = _RulePrimitive 29 30 31 class MinWidth(_RulePrimitive): 32 """A _RulePrimitive to be able to add extra width rule for a derived 33 primitive. 34 35 Arguments: 36 prim: the primitive on which to add minimum width rule. 37 Typically this should be a derived layer; e.g. minimum width 38 higher for higher voltage diffusion than low voltage diffusion. 39 min_width: the minimum width 40 """ 41 def __init__(self, *, 42 prim: _MaskPrimitive, min_width: float, 43 ): 44 name = f"MinWidth({prim.name},{min_width:.6})" 45 super().__init__(name=name) 46 self.prim = prim 47 self.min_width = min_width 48 49 def _generate_rules(self, *, 50 tech: _tch.Technology, 51 ) -> Iterable[_rle.RuleT]: 52 yield self.prim.mask.width >= self.min_width 53 54 @property 55 def designmasks(self): 56 yield from super().designmasks 57 yield from self.prim.designmasks 58 59 60 class Spacing(_RulePrimitive): 61 """A _RulePrimitive that allows to define extra minimum space requirement 62 that is not derived from the parameters from any of the primitives in the 63 a technology 64 65 Arguments: 66 primitives1: first set of primitives 67 If primitives2 is not provided this set has to contain more than 68 one primitive and the minimum space requirement is for the 69 combined shape of joining all shapes in all the layers in this 70 set. 71 primitives2: optinal second set of primitives 72 If this set is provided the minimum space specification is for 73 each shape on a layer in primitives1 to each shape on a layer 74 in primitives2. 75 min_space: the minimum space specifcation 76 """ 77 def __init__(self, *, 78 primitives1: MultiT[_MaskPrimitive], 79 primitives2: OptMultiT[_MaskPrimitive]=None, 80 min_space: float, 81 ): 82 primitives1 = cast_MultiT(primitives1) 83 primitives2 = cast_OptMultiT(primitives2) 84 85 if primitives2 is not None: 86 name = "Spacing({},{:.6})".format( 87 ",".join( 88 ( 89 prims[0].name if len(prims) == 1 90 else "({})".format(",".join(prim.name for prim in prims)) 91 ) for prims in (primitives1, primitives2) 92 ), 93 min_space, 94 ) 95 else: 96 s_prim1 = ( 97 primitives1[0].name if len(primitives1) == 1 98 else "({})".format(",".join(prim.name for prim in primitives1)) 99 ) 100 name = f"Spacing({s_prim1},None,{min_space:.6})" 101 super().__init__(name=name) 102 self.primitives1 = primitives1 103 self.primitives2 = primitives2 104 self.min_space = min_space 105 106 def _generate_rules(self, *, 107 tech: _tch.Technology, 108 ) -> Iterable[_rle.RuleT]: 109 yield from super()._generate_rules(tech=tech) 110 111 if self.primitives2 is None: 112 joined = _msk.Join(prim1.mask for prim1 in self.primitives1) 113 yield joined.space >= self.min_space 114 else: 115 yield from ( 116 _msk.Spacing(prim1.mask, prim2.mask) >= self.min_space 117 for prim1, prim2 in product(self.primitives1, self.primitives2) 118 ) 119 120 @property 121 def designmasks(self): 122 yield from super().designmasks 123 if self.primitives2 is not None: 124 for prim in (*self.primitives1, *self.primitives2): 125 yield from prim.designmasks 126 else: 127 for prim in self.primitives1: 128 yield from prim.designmasks 129 130 def __repr__(self): 131 return self.name 132 133 134 class Enclosure(_RulePrimitive): 135 """A _RulePrimitive that allows to define extra minimum enclosure 136 requirement that is not derived from the parameters from any of the 137 primitives in a technology. 138 139 Argumnets: 140 prim: the base primitive 141 by: the enclosing primitive 142 min_enclosure: the minimum `Enclosure` of `prim` by `by` 143 """ 144 def __init__(self, *, 145 prim: _MaskPrimitive, by: _MaskPrimitive, min_enclosure: _prp.Enclosure, 146 ): 147 name = f"Enclosure(prim={prim!r},by={by!r},min_enclosure={min_enclosure!r})" 148 super().__init__(name=name) 149 150 self.prim = prim 151 self.by = by 152 self.min_enclosure = min_enclosure 153 154 def _generate_rules(self, *, 155 tech: _tch.Technology, 156 ) -> Iterable[_rle.RuleT]: 157 yield from super()._generate_rules(tech=tech) 158 159 yield self.prim.mask.enclosed_by(self.by.mask) >= self.min_enclosure 160 161 @property 162 def designmasks(self) -> Iterable[_msk.DesignMask]: 163 yield from super().designmasks 164 yield from self.prim.designmasks 165 yield from self.by.designmasks 166 167 def __repr__(self) -> str: 168 return self.name 169 170 171 class NoOverlap(_RulePrimitive): 172 """A _RulePrimitive that allows to define extra no overlap 173 requirement that is not derived from the parameters from any of the 174 primitives in a technology. 175 176 Argumnets: 177 prim1, prim2: the two primitives where none of the shape may 178 have a overlapping part. 179 """ 180 def __init__(self, *, prim1: _MaskPrimitive, prim2: _MaskPrimitive): 181 name = f"NoOverlap(prim1={prim1!r},prim2={prim2!r})" 182 super().__init__(name=name) 183 184 self.prim1 = prim1 185 self.prim2 = prim2 186 187 def _generate_rules(self, *, 188 tech: _tch.Technology, 189 ) -> Iterable[_rle.RuleT]: 190 yield from super()._generate_rules(tech=tech) 191 192 intersect = _Intersect(prims=(self.prim1, self.prim2)) 193 yield intersect.mask.area == 0.0 194 195 @property 196 def designmasks(self) -> Iterable[_msk.DesignMask]: 197 yield from super().designmasks 198 yield from self.prim1.designmasks 199 yield from self.prim2.designmasks 200 201 def __repr__(self) -> str: 202 return self.name