diff --git a/crlcore/python/helpers/analogtechno.py b/crlcore/python/helpers/analogtechno.py index a7f0207f..b8c4f811 100644 --- a/crlcore/python/helpers/analogtechno.py +++ b/crlcore/python/helpers/analogtechno.py @@ -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 = '' 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 + diff --git a/crlcore/src/ccore/alliance/ap/ApParser.cpp b/crlcore/src/ccore/alliance/ap/ApParser.cpp index fb06f4bc..1462bf24 100644 --- a/crlcore/src/ccore/alliance/ap/ApParser.cpp +++ b/crlcore/src/ccore/alliance/ap/ApParser.cpp @@ -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(); diff --git a/crlcore/src/ccore/lefdef/DefImport.cpp b/crlcore/src/ccore/lefdef/DefImport.cpp index 917091f3..6af3a9a2 100644 --- a/crlcore/src/ccore/lefdef/DefImport.cpp +++ b/crlcore/src/ccore/lefdef/DefImport.cpp @@ -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; + } } diff --git a/crlcore/src/pyCRL/PyDefImport.cpp b/crlcore/src/pyCRL/PyDefImport.cpp index b724023a..67535398 100644 --- a/crlcore/src/pyCRL/PyDefImport.cpp +++ b/crlcore/src/pyCRL/PyDefImport.cpp @@ -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; diff --git a/hurricane/src/hurricane/Technology.cpp b/hurricane/src/hurricane/Technology.cpp index 60486a62..9d185a0f 100644 --- a/hurricane/src/hurricane/Technology.cpp +++ b/hurricane/src/hurricane/Technology.cpp @@ -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(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(getLayer( name )); } + + RegularLayer* Technology::getRegularLayer ( const Name& name ) const - { - Layer* layer = getLayer(name); - return (layer and dynamic_cast(layer)) ? (RegularLayer*)layer : NULL; - } + { return dynamic_cast(getLayer( name )); } ViaLayer* Technology::getViaLayer ( const Name& name ) const - { - Layer* layer = getLayer(name); - return (layer and dynamic_cast(layer)) ? (ViaLayer*)layer : NULL; - } + { return dynamic_cast(getLayer( name )); } BasicLayers Technology::getBasicLayers () const @@ -199,19 +216,6 @@ namespace Hurricane { { return SubTypeCollection(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 ); } diff --git a/hurricane/src/hurricane/hurricane/Technology.h b/hurricane/src/hurricane/hurricane/Technology.h index ee946ad8..ba1a19d9 100644 --- a/hurricane/src/hurricane/hurricane/Technology.h +++ b/hurricane/src/hurricane/hurricane/Technology.h @@ -79,6 +79,7 @@ namespace Hurricane { typedef std::set PhysicalRules; typedef std::map OneLayerRules; typedef std::map TwoLayersRules; + typedef std::map 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; } diff --git a/hurricane/src/isobar/PyTechnology.cpp b/hurricane/src/isobar/PyTechnology.cpp index a754ccc3..994d26a2 100644 --- a/hurricane/src/isobar/PyTechnology.cpp +++ b/hurricane/src/isobar/PyTechnology.cpp @@ -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 diff --git a/oroshi/python/dtr.py b/oroshi/python/dtr.py index bafa81c0..df425b39 100644 --- a/oroshi/python/dtr.py +++ b/oroshi/python/dtr.py @@ -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() diff --git a/oroshi/python/stack.py b/oroshi/python/stack.py index 22df423b..efea3c22 100644 --- a/oroshi/python/stack.py +++ b/oroshi/python/stack.py @@ -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 )