223 lines
8.1 KiB
Python
223 lines
8.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from ..Hurricane import DbU, DataBase
|
|
from ..helpers import trace
|
|
from ..helpers.io import ErrorMessage as Error
|
|
|
|
|
|
class Rules ( object ):
|
|
"""
|
|
The Rules object provides an easier access to the design rules stored
|
|
in the Technology databse. Instead of having to perform a function call
|
|
like:
|
|
|
|
.. code:: Python
|
|
|
|
tech = DataBase.getDB().getTechnology()
|
|
value = tech.getPhysicalRule( 'minEnclosure', 'pImplant', 'active' )
|
|
|
|
We can write access the rule as an attribute of the ``rule`` object.
|
|
|
|
.. code:: Python
|
|
|
|
import oroshi
|
|
|
|
value = oroshi.rules.minEnclosure_pImplant_active
|
|
|
|
Only the rules defined in the Rules.ruleSet list will be loaded.
|
|
"""
|
|
|
|
ruleSet = [ 'minSpacing_nWell'
|
|
, 'minWidth_pImplant'
|
|
, 'minSpacing_pImplant'
|
|
, 'minSpacing_rpolyh_pImplant'
|
|
, 'minEnclosure_pImplant_poly2con'
|
|
, 'minEnclosure_nImplant_active'
|
|
, 'minEnclosure_pImplant_active'
|
|
, 'minSpacing_nImplant_pImplant'
|
|
, 'minSpacing_cut0'
|
|
, 'minWidth_cut0'
|
|
, 'minWidth_active'
|
|
, 'minEnclosure_active_cut0'
|
|
, 'transistorMinL'
|
|
, 'transistorMinW'
|
|
, 'minSpacing_poly'
|
|
, 'minGateSpacing_poly'
|
|
, 'minSpacing_poly_active'
|
|
, 'minExtension_active_poly'
|
|
, 'minExtension_poly_active'
|
|
, 'minEnclosure_poly_cut0'
|
|
, 'minSpacing_cut0_poly'
|
|
, 'minWidth_cut0'
|
|
, 'minSpacing_cut0_active'
|
|
, 'minWidth_metal1'
|
|
, 'minSpacing_metal1'
|
|
, 'minEnclosure_metal1_cut0'
|
|
, 'minEnclosure_metal1_cut1'
|
|
, 'minWidth_cut1'
|
|
, 'minSpacing_cut1'
|
|
, 'minWidth_metal2'
|
|
, 'minSpacing_metal2'
|
|
, 'minEnclosure_metal2_cut1'
|
|
, 'minEnclosure_metal2_cut2'
|
|
, 'minWidth_cut2'
|
|
, 'minSpacing_cut2'
|
|
, 'minWidth_cut1'
|
|
, 'minSpacing_cut1'
|
|
, 'minWidth_metal3'
|
|
, 'minSpacing_metal3'
|
|
, 'minSpacingWide1_metal3'
|
|
, 'minEnclosure_metal3_cut2'
|
|
, 'minSpacingOnMetBot_cut2'
|
|
, 'minSpacingOnMetCap_cut2'
|
|
, 'maxWidth_metcap'
|
|
, 'minSpacing_metbot'
|
|
, 'minSpacing_cut1_metcap'
|
|
, 'minSpacing_cut2_metcap'
|
|
, 'minEnclosure_metbot_metcap'
|
|
, 'minEnclosure_metbot_cut1'
|
|
, 'minEnclosure_metbot_cut2'
|
|
, 'minEnclosure_metcap_cut2'
|
|
, 'minWidth_metcap'
|
|
, 'minWidth_metcapdum'
|
|
, 'minWidth_cpoly'
|
|
, 'minWidth_poly2'
|
|
, 'minWidth_rpolyh'
|
|
, 'minWidthHighPrec_rpolyh'
|
|
, 'minSpacing_cpoly'
|
|
, 'minSpacing_poly2'
|
|
, 'minSpacing_rpolyh'
|
|
, 'minSpacing_cut0_cpoly'
|
|
, 'minSpacing_diff_poly2'
|
|
, 'minSpacing_poly_poly2'
|
|
, 'minEnclosure_poly_cpoly'
|
|
, 'minEnclosure_cpoly_cut0'
|
|
, 'minEnclosure_poly2_cut0'
|
|
, 'MIMCap'
|
|
, 'PIPCap'
|
|
, 'MIMPerimeterCap'
|
|
, 'PIPPerimeterCap'
|
|
, 'RPOLYHSheetRes'
|
|
, 'RPOLY2PHSheetRes'
|
|
, 'MET1RPOLYHContRes'
|
|
, 'minWidth_hres'
|
|
, 'minSpacing_hres'
|
|
, 'minEnclosure_hres_poly2'
|
|
, 'minSpacing_hres_poly1'
|
|
, 'minSpacing_hres_poly2'
|
|
, 'minSpacing_hres_active'
|
|
, 'corrFactor90'
|
|
, 'corrFactor135'
|
|
, 'minRpolyhSquares'
|
|
]
|
|
|
|
def __init__ ( self ):
|
|
"""
|
|
Create an empty rule set. The rules must be loaded afterwards by calling
|
|
``Rules.load()``.
|
|
"""
|
|
trace( 100, '\tRules.__init__()\n' )
|
|
self.dtr = None
|
|
|
|
def isLoaded ( self ):
|
|
"""Tells if the rules have already been loaded."""
|
|
return self.dtr is not None
|
|
|
|
def load ( self, dtr ):
|
|
"""
|
|
Load the rule set from the technology into the Rules object.
|
|
|
|
.. note:: The ``dtr`` parameter is just another name for the currently
|
|
used Hurricane::Technology.
|
|
"""
|
|
trace( 100, '\tRules.load()\n' )
|
|
if self.dtr:
|
|
print( Error( 1, 'Rules.load(): Attempt to load rules from DTR a second time (ignored).' ))
|
|
return
|
|
self.dtr = dtr
|
|
for rule in Rules.ruleSet: self.addAttr(rule)
|
|
|
|
def getRealLayer ( self, stdName ):
|
|
"""
|
|
Get a Layer object by it's name. The alias translation from generic
|
|
names is used to return the real technology name.
|
|
|
|
For example:
|
|
|
|
================== ===================
|
|
Generic Layer Name SkyWater 130nm Name
|
|
================== ===================
|
|
nWell nwm
|
|
active difftap
|
|
pImplant psdm
|
|
cut0 licon
|
|
metal1 li
|
|
cut1 via
|
|
metal2 metal1
|
|
================== ===================
|
|
"""
|
|
return self.dtr.getLayer( stdName )
|
|
|
|
def attrTranslate ( self, attribute ):
|
|
"""
|
|
Translate a rule complete name, given in ``attribute``, using the *generic*
|
|
layer names into another string, using the target technology layer names.
|
|
|
|
For example, for SkyWater 130nm: ::
|
|
|
|
minEnclosure_pImplant_active => minSpacing_psdm_difftap
|
|
"""
|
|
words = attribute.split( '_' )
|
|
translateds = [ words[0] ]
|
|
for word in words[1:]:
|
|
realLayer = self.getRealLayer( word )
|
|
if realLayer is None:
|
|
print( Error( 1, 'rules.attrTranslate(): Unable to translate generic layer "{}".' \
|
|
.format( word )))
|
|
realLayerName = word
|
|
else:
|
|
realLayerName = realLayer.getName()
|
|
translateds.append( realLayerName )
|
|
return '_'.join( translateds )
|
|
|
|
def addAttr ( self, attribute ):
|
|
"""
|
|
Add a new attribute into the dictionnary of rule set. The attribute fields,
|
|
separated by '_' are broken down to get the corresponding rule in the
|
|
technology and be set as value of said attribute.
|
|
|
|
The attribute is the concatenation of the rule name and the various layers
|
|
it applies on. For example: ::
|
|
|
|
(minEnclosure, pImplant, active) => 'minEnclosure_pImplant_active'
|
|
"""
|
|
techAttribute = self.attrTranslate( attribute )
|
|
if attribute in self.__dict__: return
|
|
|
|
#print( 'Rules.addAttr(): {} -> {}'.format( attribute, techAttribute ))
|
|
value = None
|
|
words = techAttribute.split( '_' )
|
|
try:
|
|
if len(words) == 1:
|
|
if words[0].endswith('Cap' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()
|
|
elif words[0].endswith('ContRes' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()
|
|
elif words[0].endswith('Res' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()
|
|
elif words[0].endswith('ctor90' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()
|
|
elif words[0].endswith('ctor135' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()
|
|
elif words[0].endswith('quares' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()
|
|
if (value is None) and len(words) < 4:
|
|
rule = self.dtr.getPhysicalRule( *tuple(words) )
|
|
if rule.isDouble():
|
|
value = rule.getDoubleValue()
|
|
#print( 'Accessed value (Unit):{}'.format(value) )
|
|
else:
|
|
value = rule.getValue()
|
|
#print( 'Accessed value (DbU):{}'.format(DbU.getValueString(value)) )
|
|
except Exception as e:
|
|
print( e )
|
|
|
|
if not value is None:
|
|
self.__dict__[attribute] = value
|
|
|
|
return
|