Add support for layers alias names. Bug in _addPhysicalrule().

* New: In Technology, in order to support symbolic technology on top
    of a real technology using non-generic layer names, it comes in
    handy to be able to define layer alias names. Generic *real*
    layer names could be defined as alias over the technology
    specific ones. Then, we can build the symbolic layers upon
    the generic names (so *that* part of the init code can be
    shared between techs).
       Adds Technology::addLayerAlias()
       The semantic of Technology::getLayer() changes a little, it
     return the techno layer associtated to the name *or* the
     aliased name.
* Bug: In Technology::_addPhysicalRule(), in case of a rule redefinition,
    we were using it's name *after* the deletion of the rule object.
    Nasty crash.
      Improve the error message by giving the name of the conflicting
    rule.
* In CRL/helpers.analogtechno, add an addDevice() function to load
    analogic devices descriptors (copied from the old init system).
* In CRL/ApParser, if an exception is catched, tells in which file and
    line it did occur.
* In Oroshi/dtr.Rules, add a translation step to get the rule names
    from the technology. From generic names to actual technology
    layer names.
       Add some documentation.
* In Oroshi/stack.Stack, get the layers names through dtr.Rules to get
    the layers names translated.
This commit is contained in:
Jean-Paul Chaput 2023-01-05 16:58:49 +01:00
parent da56189d2e
commit 2501688dd1
9 changed files with 249 additions and 70 deletions

View File

@ -22,9 +22,10 @@ import Hurricane
from Hurricane import DbU
from Hurricane import DataBase
from Hurricane import Layer
from helpers.io import ErrorMessage
from helpers.io import catch, ErrorMessage, WarningMessage
tech = None
technoFile = '<technoFile has not been set>'
Length = 0x0001
Area = 0x0002
@ -135,8 +136,36 @@ def _loadAnalogTechno ( techno, ruleTable ):
def loadAnalogTechno ( table, fromFile ):
global technoFile
global tech
if not tech:
tech = DataBase.getDB().getTechnology()
technoFile = fromFile
techno = DataBase.getDB().getTechnology()
_loadAnalogTechno( techno, table )
_loadAnalogTechno( tech, table )
return
def addDevice ( **kw ):
global tech
if not tech:
tech = DataBase.getDB().getTechnology()
try:
if 'name' in kw:
devDesc = tech.addDeviceDescriptor( kw['name'] )
if 'spice' in kw: devDesc.setSpiceFilePath( kw['spice'] )
if 'connectors' in kw:
for connector in kw['connectors']:
devDesc.addConnector( connector )
else:
print( WarningMessage( 'common.addDevice(): Missing connectors on device "{}".' \
.format(kw['name'])))
if 'layouts' in kw:
for layout in kw['layouts']:
devDesc.addLayout( layout[0], layout[1] )
else:
print( WarningMessage( 'common.addDevice(): Missing layouts on device "{}".' \
.format( kw['name'] )))
except Exception as e:
catch( e )
return

View File

@ -846,8 +846,10 @@ namespace {
placeNets(_cell);
} catch ( Error& e ) {
if ( e.what() != "[ERROR] ApParser processed" )
if ( e.what() != "[ERROR] ApParser processed" ) {
cerr << e.what() << endl;
cerr << "[ERROR] ApParser(): file " << _cellPath << ", line: " << _lineNumber << "." << endl;
}
}
Go::enableAutoMaterialization();

View File

@ -182,7 +182,10 @@ namespace {
defrSetSNetCbk ( _snetCbk );
defrSetPathCbk ( _pathCbk );
if (DataBase::getDB()->getTechnology()->getName() == "Sky130") _flags |= Sky130;
if (DataBase::getDB()->getTechnology()->getName() == "Sky130") {
cmess1 << " - Enabling SkyWater 130nm harness hacks." << endl;
_flags |= Sky130;
}
}

View File

@ -63,9 +63,10 @@ extern "C" {
HTRY
char* defFile = NULL;
long flags = 0;
if (PyArg_ParseTuple( args, "s:DefImport.load", &defFile )) {
cell = DefImport::load( defFile, 0 );
if (PyArg_ParseTuple( args, "s|l:DefImport.load", &defFile, &flags )) {
cell = DefImport::load( defFile, flags );
} else {
PyErr_SetString ( ConstructorError, "DefImport.load(): Bad type or bad number of parameters." );
return NULL;

View File

@ -137,6 +137,7 @@ namespace Hurricane {
, _name (name)
, _layerMap ()
, _layerMaskMap ()
, _layerAliases ()
, _unitRules ()
, _noLayerRules ()
, _oneLayerRules ()
@ -162,25 +163,41 @@ namespace Hurricane {
}
BasicLayer* Technology::getBasicLayer ( const Name& name ) const
Layer* Technology::getLayer ( const Layer::Mask& mask, bool useSymbolic ) const
{
Layer* layer = getLayer(name);
return (layer and dynamic_cast<BasicLayer*>(layer)) ? (BasicLayer*)layer : NULL;
Layer* layer = NULL;
LayerMaskMap::const_iterator lb = _layerMaskMap.lower_bound( mask );
LayerMaskMap::const_iterator ub = _layerMaskMap.upper_bound( mask );
for ( ; lb != ub ; lb++ ) {
layer = lb->second;
if (not useSymbolic or layer->isSymbolic()) return layer;
}
return layer;
}
Layer* Technology::getLayer ( const Name& name ) const
{
Layer* layer = _layerMap.getElement(name);
if (layer) return layer;
auto ialias = _layerAliases.find( name );
if (ialias != _layerAliases.end()) return ialias->second;
return nullptr;
}
BasicLayer* Technology::getBasicLayer ( const Name& name ) const
{ return dynamic_cast<BasicLayer*>(getLayer( name )); }
RegularLayer* Technology::getRegularLayer ( const Name& name ) const
{
Layer* layer = getLayer(name);
return (layer and dynamic_cast<RegularLayer*>(layer)) ? (RegularLayer*)layer : NULL;
}
{ return dynamic_cast<RegularLayer*>(getLayer( name )); }
ViaLayer* Technology::getViaLayer ( const Name& name ) const
{
Layer* layer = getLayer(name);
return (layer and dynamic_cast<ViaLayer*>(layer)) ? (ViaLayer*)layer : NULL;
}
{ return dynamic_cast<ViaLayer*>(getLayer( name )); }
BasicLayers Technology::getBasicLayers () const
@ -199,19 +216,6 @@ namespace Hurricane {
{ return SubTypeCollection<Layer*, ViaLayer*>(getLayers()); }
Layer* Technology::getLayer ( const Layer::Mask& mask, bool useSymbolic ) const
{
Layer* layer = NULL;
LayerMaskMap::const_iterator lb = _layerMaskMap.lower_bound( mask );
LayerMaskMap::const_iterator ub = _layerMaskMap.upper_bound( mask );
for ( ; lb != ub ; lb++ ) {
layer = lb->second;
if (not useSymbolic or layer->isSymbolic()) return layer;
}
return layer;
}
Layer* Technology::getMetalAbove ( const Layer* layer, bool useSymbolic ) const
{
if (not layer) return NULL;
@ -338,6 +342,25 @@ namespace Hurricane {
}
bool Technology::addLayerAlias ( const Name& reference, const Name& alias )
{
if (getLayer(alias)) {
cerr << Error( "Technology::addAlias(): Alias name \"%s\" already defined."
, getString(alias).c_str() ) << endl;
return false;
}
Layer* referenceLayer = getLayer( reference );
if (not referenceLayer) {
cerr << Error( "Technology::addAlias(): Reference layer \"%s\" does not exists (yet?)."
, getString(reference).c_str() ) << endl;
return false;
}
_layerAliases.insert( make_pair( alias, referenceLayer ));
return true;
}
void Technology::_postCreate()
{
Super::_postCreate();
@ -623,9 +646,10 @@ namespace Hurricane {
PhysicalRules& rules = ilayer->second;
PhysicalRule search ( rule->getName(), "" );
if (rules.find(&search) != rules.end()) {
string ruleName = getString( rule->getName() );
delete rule;
throw Error( "Technology::addPhysicalRule(): Attempt to redefine rule \"%s\" for layer \"%s\"."
, getString(rule->getName()).c_str(), layerStr.c_str() );
, ruleName.c_str(), layerStr.c_str() );
}
rules.insert( rule );
}
@ -655,6 +679,15 @@ namespace Hurricane {
const Layer* layer2 = getLayer(layer2Name);
LayerPair layerPair ( layer1, layer2 );
if (not layer1)
throw Error( "Technology::addPhysicalRule(): Unknown layer1 rule \"%s\" for (\"%s\",\"%s\")."
, ruleNameStr.c_str(), layer1Str.c_str(), layer2Str.c_str()
);
if (not layer2)
throw Error( "Technology::addPhysicalRule(): Unknown layer2 rule \"%s\" for (\"%s\",\"%s\")."
, ruleNameStr.c_str(), layer1Str.c_str(), layer2Str.c_str()
);
PhysicalRule* rule = new PhysicalRule ( ruleName, reference );
TwoLayersRules::iterator ilp = _twoLayersRules.find( layerPair );
if (ilp == _twoLayersRules.end()) {
@ -667,7 +700,9 @@ namespace Hurricane {
PhysicalRule search ( ruleName, "" );
if (rules.find(&search) != rules.end()) {
delete rule;
throw Error( "Technology::addPhysicalRule(): Attempt to redefine rule \"%s\"." , ruleNameStr.c_str() );
throw Error( "Technology::addPhysicalRule(): Attempt to redefine rule \"%s\" for (\"%s\",\"%s\")."
, ruleNameStr.c_str(), layer1Str.c_str(), layer2Str.c_str()
);
}
rules.insert( rule );
}

View File

@ -79,6 +79,7 @@ namespace Hurricane {
typedef std::set<PhysicalRule* , RuleNameCompare> PhysicalRules;
typedef std::map<const Hurricane::Layer* , PhysicalRules> OneLayerRules;
typedef std::map<LayerPair , PhysicalRules> TwoLayersRules;
typedef std::map<const Name , Hurricane::Layer*> LayerAliases;
public:
// Sub-class : LayerMap.
@ -100,11 +101,11 @@ namespace Hurricane {
inline bool isMetal ( const Layer* ) const;
inline DataBase* getDataBase () const;
inline const Name& getName () const;
inline Layer* getLayer ( const Name& ) const;
Layer* getLayer ( const Name& ) const;
BasicLayer* getBasicLayer ( const Name& ) const;
RegularLayer* getRegularLayer ( const Name& ) const;
ViaLayer* getViaLayer ( const Name& ) const;
inline Layers getLayers () const;
Layers getLayers () const;
BasicLayers getBasicLayers () const;
BasicLayers getBasicLayers ( const Layer::Mask& ) const;
RegularLayers getRegularLayers () const;
@ -137,6 +138,7 @@ namespace Hurricane {
void setName ( const Name& );
bool setSymbolicLayer ( const Name& );
bool setSymbolicLayer ( const Layer* );
bool addLayerAlias ( const Name& reference, const Name& alias );
DeviceDescriptor* addDeviceDescriptor ( const Name& );
ModelDescriptor* addModelDescriptor ( const Name& name
, const Name& simul
@ -181,6 +183,7 @@ namespace Hurricane {
LayerMaskMap _layerMaskMap;
Layer::Mask _cutMask;
Layer::Mask _metalMask;
LayerAliases _layerAliases;
DeviceDescriptors _deviceDescriptors;
ModelDescriptors _modelDescriptors;
UnitRules _unitRules;
@ -200,7 +203,6 @@ namespace Hurricane {
inline bool Technology::isMetal ( const Layer* layer ) const { return _metalMask.contains(layer->getMask()); }
inline DataBase* Technology::getDataBase () const { return _dataBase; }
inline const Name& Technology::getName () const { return _name; }
inline Layer* Technology::getLayer ( const Name& name ) const { return _layerMap.getElement(name); }
inline Layers Technology::getLayers () const { return getCollection(&_layerMaskMap); }
inline Technology::ModelDescriptors& Technology::getModelDescriptors () { return _modelDescriptors; }
inline Technology::LayerMap& Technology::_getLayerMap () { return _layerMap; }

View File

@ -190,6 +190,28 @@ extern "C" {
}
static PyObject* PyTechnology_addLayerAlias ( PyTechnology *self, PyObject* args )
{
cdebug_log(20,0) << "Technology.addLayerAlias()" << endl;
METHOD_HEAD("Technology.addLayerAlias()")
bool rvalue = false;
HTRY
char* reference = NULL;
char* alias = NULL;
if (PyArg_ParseTuple(args,"ss:Technology.addLayerAlias", &reference, &alias)) {
rvalue = techno->addLayerAlias( reference, alias );
} else {
PyErr_SetString( ConstructorError, "Hurricane.addLayerAlias(str,str): Bad parameter(s) type." );
return NULL;
}
HCATCH
if (rvalue) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static PyObject* PyTechnology_setSymbolicLayer ( PyTechnology *self, PyObject* args ) {
cdebug_log(20,0) << "Technology.setSymbolicLayer()" << endl;
@ -443,6 +465,8 @@ extern "C" {
, "Returns the collection of all RegularLayers." }
, { "getViaLayers" , (PyCFunction)PyTechnology_getViaLayers , METH_NOARGS
, "Returns the collection of all BasicLayers." }
, { "addLayerAlias" , (PyCFunction)PyTechnology_addLayerAlias , METH_VARARGS
, "Add an alias name to a layer." }
, { "getMetalAbove" , (PyCFunction)PyTechnology_getMetalAbove , METH_VARARGS
, "Returns the metal layer immediatly above this one." }
, { "getMetalBelow" , (PyCFunction)PyTechnology_getMetalBelow , METH_VARARGS

View File

@ -1,10 +1,32 @@
# -*- coding: utf-8 -*-
from Hurricane import DbU
from Hurricane import 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'
@ -91,18 +113,78 @@ class Rules ( object ):
]
def __init__ ( self, dtr ):
"""
Load the rule set from the technology into the Rules object.
.. note:: The ``dtr`` parameter is just aother name for the currently
used Hurricane::Technology.
"""
trace( 100, '\tRules.__init__()\n' )
self.dtr = dtr
self.dtr = dtr
for rule in Rules.ruleSet: self.addAttr(rule)
return
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) )
#print( 'Rules.addAttr(): {} -> {}'.format( attribute, techAttribute ))
value = None
words = attribute.split( '_' )
words = techAttribute.split( '_' )
try:
if len(words) == 1:
if words[0].endswith('Cap' ): value = self.dtr.getUnitRule( words[0] ).getDoubleValue()

View File

@ -19,6 +19,7 @@ from helpers.io import ErrorMessage as Error
from helpers import trace
from Analog import Device
import oroshi
import oroshi.dtr
#helpers.setTraceLevel( 100 )
@ -212,13 +213,13 @@ class Bulk ( object ):
def doLayout ( self ):
active = DataBase.getDB().getTechnology().getLayer( 'active' )
metal1 = DataBase.getDB().getTechnology().getLayer( 'metal1' )
metal2 = DataBase.getDB().getTechnology().getLayer( 'metal2' )
metal3 = DataBase.getDB().getTechnology().getLayer( 'metal3' )
cut0 = DataBase.getDB().getTechnology().getLayer( 'cut0' )
cut1 = DataBase.getDB().getTechnology().getLayer( 'cut1' )
cut2 = DataBase.getDB().getTechnology().getLayer( 'cut2' )
active = oroshi.rules.getRealLayer( 'active' )
metal1 = oroshi.rules.getRealLayer( 'metal1' )
metal2 = oroshi.rules.getRealLayer( 'metal2' )
metal3 = oroshi.rules.getRealLayer( 'metal3' )
cut0 = oroshi.rules.getRealLayer( 'cut0' )
cut1 = oroshi.rules.getRealLayer( 'cut1' )
cut2 = oroshi.rules.getRealLayer( 'cut2' )
bulkNet = self.stack.bulkNet
self.computeContacts()
@ -604,13 +605,13 @@ class Stack ( object ):
if bulkType & 0x0008: self.flags |= Stack.WestBulk
if self.isNmos():
self.tImplantLayer = DataBase.getDB().getTechnology().getLayer( 'nImplant' )
self.bImplantLayer = DataBase.getDB().getTechnology().getLayer( 'pImplant' )
self.tImplantLayer = oroshi.rules.getRealLayer( 'nImplant' )
self.bImplantLayer = oroshi.rules.getRealLayer( 'pImplant' )
self.wellLayer = None
else:
self.tImplantLayer = DataBase.getDB().getTechnology().getLayer( 'pImplant' )
self.bImplantLayer = DataBase.getDB().getTechnology().getLayer( 'nImplant' )
self.wellLayer = DataBase.getDB().getTechnology().getLayer( 'pWell' )
self.tImplantLayer = oroshi.rules.getRealLayer( 'pImplant' )
self.bImplantLayer = oroshi.rules.getRealLayer( 'nImplant' )
self.wellLayer = oroshi.rules.getRealLayer( 'pWell' )
return
@ -1132,8 +1133,8 @@ class Stack ( object ):
capSpacing = self.minSpacing_metal2 + self.minWidth_metal2//2
capSpacing = max( capSpacing, self.minSpacing_metal3 + self.minWidth_metal3//2 )
metal2 = DataBase.getDB().getTechnology().getLayer( 'metal2' )
metal3 = DataBase.getDB().getTechnology().getLayer( 'metal3' )
metal2 = oroshi.rules.getRealLayer( 'metal2' )
metal3 = oroshi.rules.getRealLayer( 'metal3' )
trackNb = 0
if self.topTracks: trackNb = len(self.topTracks)
for i in range(trackNb):
@ -1193,7 +1194,7 @@ class Stack ( object ):
if not tImplantNet: tImplantNet = Net.create( self.device, 'nImplant' )
tImplantNet.setAutomatic( True )
active = DataBase.getDB().getTechnology().getLayer( 'active' )
active = oroshi.rules.getRealLayer( 'active' )
width = self.w
length = (self.NFs - 1) * self.gatePitch + 2*self.sideActiveWidth
axis = width // 2
@ -1225,13 +1226,13 @@ class Stack ( object ):
def drawGate ( self, axis, wiring ):
trace( 100, '\tStack.drawGate(): %s\n' % wiring )
gate = DataBase.getDB().getTechnology().getLayer( 'poly' )
cut0 = DataBase.getDB().getTechnology().getLayer( 'cut0' )
cut1 = DataBase.getDB().getTechnology().getLayer( 'cut1' )
cut2 = DataBase.getDB().getTechnology().getLayer( 'cut2' )
metal1 = DataBase.getDB().getTechnology().getLayer( 'metal1' )
metal2 = DataBase.getDB().getTechnology().getLayer( 'metal2' )
metal3 = DataBase.getDB().getTechnology().getLayer( 'metal3' )
gate = oroshi.rules.getRealLayer( 'poly' )
cut0 = oroshi.rules.getRealLayer( 'cut0' )
cut1 = oroshi.rules.getRealLayer( 'cut1' )
cut2 = oroshi.rules.getRealLayer( 'cut2' )
metal1 = oroshi.rules.getRealLayer( 'metal1' )
metal2 = oroshi.rules.getRealLayer( 'metal2' )
metal3 = oroshi.rules.getRealLayer( 'metal3' )
width = self.L
if wiring.isTop(): ytarget = self.getTopTrackY( wiring.topTrack )
@ -1318,12 +1319,12 @@ class Stack ( object ):
trace( 100, '\tStack.drawSourceDrain(): %s @%s width:%s NRC=%d\n' \
% (wiring, DbU.getValueString(axis), DbU.getValueString(width), cols ) )
metal1 = DataBase.getDB().getTechnology().getLayer( 'metal1' )
metal2 = DataBase.getDB().getTechnology().getLayer( 'metal2' )
metal3 = DataBase.getDB().getTechnology().getLayer( 'metal3' )
cut0 = DataBase.getDB().getTechnology().getLayer( 'cut0' )
cut1 = DataBase.getDB().getTechnology().getLayer( 'cut1' )
cut2 = DataBase.getDB().getTechnology().getLayer( 'cut2' )
metal1 = oroshi.rules.getRealLayer( 'metal1' )
metal2 = oroshi.rules.getRealLayer( 'metal2' )
metal3 = oroshi.rules.getRealLayer( 'metal3' )
cut0 = oroshi.rules.getRealLayer( 'cut0' )
cut1 = oroshi.rules.getRealLayer( 'cut1' )
cut2 = oroshi.rules.getRealLayer( 'cut2' )
rows = max( 1, (self.w - 2*self.minEnclosure_active_cut0) // self.contactDiffPitch )
ypitch = self.w // rows
yoffset = self.activeOffsetY + ypitch//2
@ -1332,7 +1333,7 @@ class Stack ( object ):
xoffset = axis - (self.contactDiffPitch * (cols - 1))//2
if self.w < 2*self.minEnclosure_active_cut0 + self.minWidth_cut0:
active = DataBase.getDB().getTechnology().getLayer( 'active' )
active = oroshi.rules.getRealLayer( 'active' )
box = Box( xoffset, yoffset, xoffset + (cols-1)*xpitch, yoffset )
box.inflate( self.minWidth_cut0 + self.minEnclosure_active_cut0 )