657 lines
32 KiB
Python
657 lines
32 KiB
Python
# -*- Mode:Python -*-
|
|
#
|
|
# This file is part of the Coriolis Software.
|
|
# Copyright (c) Sorbonne Université 2016-2023, All Rights Reserved
|
|
#
|
|
# +-----------------------------------------------------------------+
|
|
# | C O R I O L I S |
|
|
# | B o r a - A n a l o g S l i c i n g T r e e |
|
|
# | |
|
|
# | Author : Jean-Paul Chaput |
|
|
# | E-mail : Jean-Paul.Chaput@lip6.fr |
|
|
# | =============================================================== |
|
|
# | Python : "./karakaze/AnalogDesign.py" |
|
|
# +-----------------------------------------------------------------+
|
|
|
|
|
|
from ..Hurricane import *
|
|
from ..Hurricane import DataBase
|
|
from .. import CRL
|
|
from ..helpers import isderived, setTraceLevel, trace
|
|
from ..helpers.io import ErrorMessage as Error
|
|
from ..Analog import Device, TransistorFamily, Transistor, \
|
|
CommonDrain, CommonGatePair, CommonSourcePair, \
|
|
CrossCoupledPair, DifferentialPair, LevelShifter, \
|
|
SimpleCurrentMirror, CapacitorFamily, \
|
|
MultiCapacitor, CapacitorFamily, MultiCapacitor, \
|
|
ResistorFamily, Resistor, LayoutGenerator, \
|
|
Matrix
|
|
from ..Bora import ParameterRange, StepParameterRange, \
|
|
MatrixParameterRange, SlicingNode, HSlicingNode, \
|
|
VSlicingNode, DSlicingNode, RHSlicingNode, \
|
|
RVSlicingNode
|
|
from . import oceane
|
|
from .. import Anabatic, Katana, Bora
|
|
|
|
|
|
#setTraceLevel( 100 )
|
|
|
|
|
|
NMOS = Transistor.NMOS
|
|
PMOS = Transistor.PMOS
|
|
PIP = CapacitorFamily.PIP
|
|
MIM = CapacitorFamily.MIM
|
|
MOM = CapacitorFamily.MOM
|
|
LOWRES = ResistorFamily.LOWRES
|
|
HIRES = ResistorFamily.HIRES
|
|
RPOLYH = ResistorFamily.RPOLYH
|
|
RPOLY2PH = ResistorFamily.RPOLY2PH
|
|
Center = SlicingNode.AlignCenter
|
|
Left = SlicingNode.AlignLeft
|
|
Right = SlicingNode.AlignRight
|
|
Top = SlicingNode.AlignTop
|
|
Bottom = SlicingNode.AlignBottom
|
|
Unknown = SlicingNode.AlignBottom
|
|
VNode = 1
|
|
HNode = 2
|
|
DNode = 3
|
|
|
|
|
|
def toDbU ( value ): return DbU.fromPhysical( value, DbU.UnitPowerMicro )
|
|
def toLength ( value ): return float(value) * 1e+6
|
|
|
|
|
|
def readMatrix ( rows ):
|
|
if not isinstance(rows,list):
|
|
print( '[ERROR] readMatrix(): First level is not a list.' )
|
|
sys.exit( 1 )
|
|
rowCount = len(rows)
|
|
for row in range(len(rows)):
|
|
column = rows[row]
|
|
if not isinstance(column,list):
|
|
print( '[ERROR] readMatrix(): Column {} is not a list.'.format(row) )
|
|
sys.exit( 1 )
|
|
if row == 0:
|
|
columnCount = len(column)
|
|
matrix = Matrix( rowCount, columnCount )
|
|
else:
|
|
if columnCount != len(column):
|
|
print( '[ERROR] readMatrix(): Column {} size discrepency (sould be {}).'.format(len(column),columnCount))
|
|
sys.exit( 1 )
|
|
for column in range(len(column)):
|
|
matrix.setValue( row, column, rows[row][column] )
|
|
return matrix
|
|
|
|
|
|
|
|
class AnalogDesign ( object ):
|
|
|
|
def __init__ ( self ):
|
|
self.cellName = None
|
|
self.netCache = {}
|
|
self.rg = None
|
|
self.library = None
|
|
self.cell = None
|
|
self.netCache = {}
|
|
self.slicingTree = None
|
|
self.stack = []
|
|
self.stack2 = []
|
|
self.toleranceRatioH = 0
|
|
self.toleranceRatioW = 0
|
|
self.toleranceBandH = 0
|
|
self.toleranceBandW = 0
|
|
self.parameters = oceane.Parameters()
|
|
return
|
|
|
|
def setCellName ( self, name ):
|
|
self.cellName = name
|
|
return
|
|
|
|
def beginCell ( self, cellName ):
|
|
self.setCellName( cellName )
|
|
UpdateSession.open()
|
|
self.rg = CRL.AllianceFramework.get().getRoutingGauge()
|
|
self.cell = CRL.AllianceFramework.get().createCell( self.cellName )
|
|
self.library = Library.create( DataBase.getDB().getRootLibrary(), 'AnalogRootLibrary' )
|
|
self.generator = LayoutGenerator()
|
|
return
|
|
|
|
def endCell ( self ):
|
|
UpdateSession.close()
|
|
return
|
|
|
|
def checkBeginCell ( self, function ):
|
|
if not self.cell:
|
|
raise Error( 3, [ 'AnalogDesign: \"AnalogDevice.beginCell()\" must be called *before* \"%s\".' \
|
|
% function
|
|
] )
|
|
return
|
|
|
|
def checkConnexion ( self, count, net, connexion ):
|
|
if not isinstance(connexion,tuple):
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.netSpecs\" in \"%s\", connexion [%d] is *not* a tuple.' \
|
|
% (net.getName(),count)
|
|
, '%s' % str(connexion) ] )
|
|
if len(connexion) != 2:
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.devicesSpecs\" in \"%s\", connexion [%d] has %d items instead of 2 .' \
|
|
% (net.getName(),count,len(connexion))
|
|
, '%s' % str(connexion) ] )
|
|
if not isinstance(connexion[0],str):
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.devicesSpecs\" in \"%s\", connexion [%d], field [0] (instance) is *not* a string.' \
|
|
% (net.getName(),count)
|
|
, '%s' % str(connexion) ] )
|
|
if not isinstance(connexion[1],str):
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.devicesSpecs\" in \"%s\", connexion [%d], field [1] (terminal) is *not* a string.' \
|
|
% (net.getName(),count)
|
|
, '%s' % str(connexion) ] )
|
|
return
|
|
|
|
def checkRail( self, net, metal, npitch, cellName, instanceName ):
|
|
#Net verification missing
|
|
if not isinstance(metal,str):
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"metal\" is *not* a string.' ] )
|
|
if not isinstance(npitch,int):
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"NPitch\" is *not* an int.' ] )
|
|
if not isinstance(cellName,str):
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"cellName\" is *not* a string.' ] )
|
|
if not isinstance(instanceName,str):
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"instanceName\" is *not* a string.' ] )
|
|
return
|
|
|
|
def connect ( self, instanceName, masterNetName, net ):
|
|
instance = getattr( self, instanceName )
|
|
masterNet = instance.getMasterCell().getNet( masterNetName )
|
|
instance.getPlug( masterNet ).setNet( net )
|
|
|
|
state = NetRoutingExtension.get(net)
|
|
device = instance.getMasterCell()
|
|
if masterNetName=='B':
|
|
device.getParameter('B.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='G':
|
|
device.getParameter('G.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='G1':
|
|
device.getParameter('G1.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='G2':
|
|
device.getParameter('G2.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='D':
|
|
device.getParameter('D.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='D1':
|
|
device.getParameter('D1.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='D2':
|
|
device.getParameter('D2.w').setValue(int(state.getWPitch()))
|
|
if masterNetName=='S':
|
|
device.getParameter('S.w').setValue(int(state.getWPitch()))
|
|
return
|
|
|
|
def getNet ( self, netName, create=True ):
|
|
net = None
|
|
if netName in self.netCache:
|
|
net = self.netCache[netName]
|
|
elif create:
|
|
net = Net.create( self.cell, netName )
|
|
self.netCache[ netName ] = net
|
|
return net
|
|
|
|
def doNets ( self ):
|
|
self.checkBeginCell( 'AnalogDesign.doNets()' )
|
|
|
|
if not hasattr(self,'netSpecs'):
|
|
raise Error( 3, 'AnalogDesign.doNets(): Mandatory attribute \"self.netSpecs\" has not been defined.' )
|
|
if not isinstance(self.netSpecs,dict):
|
|
raise Error( 3, 'AnalogDesign.doNets(): Attribute \"self.netSpecs\" *must* be a Python dict.' )
|
|
|
|
for netName, netType in self.netTypes.items():
|
|
if not isinstance(netName,str):
|
|
raise Error( 3, 'AnalogDesign.doNets(): Dict key (net name) of \"self.netTypes\" *must* be a string (%s).' % str(netName) )
|
|
net = self.getNet( netName )
|
|
isExternal = False
|
|
if 'isExternal' in netType: isExternal = netType['isExternal']
|
|
|
|
for netName, connexions in self.netSpecs.items():
|
|
if not isinstance(netName,str):
|
|
raise Error( 3, 'AnalogDesign.doNets(): Dict key (net name) of \"self.netSpecs\" *must* be a string (%s).' % str(netName) )
|
|
net = self.getNet( netName )
|
|
state = NetRoutingExtension.create( net, NetRoutingState.AutomaticGlobalRoute|NetRoutingState.Analog )
|
|
count = 1
|
|
for connexion in connexions:
|
|
if isinstance(connexion,tuple):
|
|
self.checkConnexion( count, net, connexion )
|
|
self.connect( connexion[0], connexion[1], net )
|
|
count += 1
|
|
else:
|
|
if isinstance(connexion,dict): state.setWPitch(long(connexion['W']))
|
|
return
|
|
|
|
def checkDSpec ( self, count, dspec ):
|
|
if not isinstance(dspec,list):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], is *not* a list.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isderived(dspec[0],Device):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [0] is *not* a Device class.' % count
|
|
, '%s' % str(dspec) ])
|
|
|
|
specSize = 0
|
|
if isderived(dspec[0],TransistorFamily): specSize = 12
|
|
elif isderived(dspec[0], CapacitorFamily): specSize = 7
|
|
elif isderived(dspec[0], ResistorFamily): specSize = 8
|
|
else:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], has unsupported device type.' \
|
|
% (count)
|
|
, '%s' % str(dspec) ])
|
|
|
|
if len(dspec) < specSize:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], has %d items instead of 12 .' \
|
|
% (count,len(dspec))
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[1],str):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [1] (model name) is *not* a string.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[2],str):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [2] (layout style) is *not* a string.' % count
|
|
, '%s' % str(dspec) ])
|
|
|
|
if isderived(dspec[0],TransistorFamily):
|
|
if dspec[3] not in [NMOS, PMOS]:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [3] (type) must be either NMOS or PMOS.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[4],float):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [4] (WE) is *not* a float.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[5],float):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [5] (LE) is *not* a float.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[6],int):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [6] (M) is *not* an int.' % count
|
|
, '%s' % str(dspec) ])
|
|
if (not dspec[7] is None) and (not isinstance(dspec[7],int)):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [7] (Mint) is neither an int nor None.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[8],int):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [8] (external dummies) is *not* an int.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[9],bool):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [9] (source first) is *not* a boolean.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[10],int):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [10] (bulk) is *not* an int.' % count
|
|
, '%s' % str(dspec) ])
|
|
else:
|
|
if dspec[10] > 0xf:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [10] (bulk) is greater than 0xf.' % count
|
|
, '%s' % str(dspec) ])
|
|
if not isinstance(dspec[11],bool):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [11] (bulk connected) is *not* a boolean.' % count
|
|
, '%s' % str(dspec) ])
|
|
elif isderived(dspec[0], CapacitorFamily):
|
|
if dspec[3] not in [PIP, MIM, MOM]:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [3] (type) must be either PIP, MIM or MOM.' % count
|
|
, '%s' % str(dspec) ])
|
|
if isinstance(dspec[4],float): pass
|
|
elif isinstance(dspec[4],tuple): pass
|
|
else:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [4] (Cs) should either be *one* float or a *list* of floats.' % count
|
|
, '%s' % str(dspec) ])
|
|
elif isderived(dspec[0],ResistorFamily):
|
|
if dspec[3] not in [RPOLYH, RPOLY2PH]:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [3] (type) must be either RPOLYH or RPOLY2PH.' % count
|
|
, '%s' % str(dspec) ])
|
|
if isinstance(dspec[5],float): pass
|
|
else:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [4] (resistance) must be a float.' % count
|
|
, '%s' % str(dspec) ])
|
|
else:
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], spec list do not match any known pattern.' % count
|
|
, '%s' % str(dspec) ])
|
|
return
|
|
|
|
def checkDSpecDigital ( self, count, dspec ):
|
|
# if not isinstance(dspec[0],str):
|
|
# raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [0] (model name) is *not* a string.' % count
|
|
# , '%s' % str(dspec) ])
|
|
if not isinstance(dspec[1],str):
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [1] (model name) is *not* a string.' % count
|
|
, '%s' % str(dspec) ])
|
|
return
|
|
|
|
def readParameters ( self, path ):
|
|
trace( 110, ',+', '\tReading Oceane parameters from \"%s\"\n' % path )
|
|
|
|
if not path: return
|
|
self.parameters.read( path );
|
|
|
|
for dspec in self.devicesSpecs:
|
|
if isinstance(dspec[0],Cell):
|
|
pass
|
|
elif issubclass(dspec[0],TransistorFamily):
|
|
Tname = dspec[1].split('_')[0]
|
|
Tparameters = self.parameters.getTransistor( Tname )
|
|
if not Tparameters:
|
|
raise Error( 3, [ 'AnalogDesign.readParameters(): Missing parameters for \"%s\".' % Tname ] )
|
|
continue
|
|
dspec[4] = toLength( Tparameters.W )
|
|
dspec[5] = toLength( Tparameters.L )
|
|
dspec[6] = Tparameters.M
|
|
trace( 110, '\t- \"%s\" : W:%f L:%f M:%d\n' % (Tname
|
|
,dspec[4]
|
|
,dspec[5]
|
|
,dspec[6]) )
|
|
elif issubclass(dspec[0],CapacitorFamily):
|
|
Cname = dspec[1]
|
|
Cparameters = self.parameters.getCapacitor( Cname )
|
|
if not Cparameters:
|
|
raise Error( 3, [ 'AnalogDesign.readParameters(): Missing parameters for capacity \"%s\".' % Cname ] )
|
|
continue
|
|
dspec[4] = Cparameters.C * 1e+12
|
|
trace( 110, '\t- \"%s\" : C:%fpF\n' % (Cname ,dspec[4]) )
|
|
elif issubclass(dspec[0],ResistorFamily):
|
|
print( WarningMessage( 'Resistor devices are not supported yet by Oceane parser (instance:"{}").'.format(dspec[1]) ))
|
|
else:
|
|
print( WarningMessage( 'Unsupported analog device type {0} (instance:"{1}").'.format(dspec[0],dspec[1]) ))
|
|
trace( 110, '-,' )
|
|
return
|
|
|
|
|
|
def doDevice ( self, count, dspec ):
|
|
self.checkBeginCell( 'AnalogDesign.doDevice()' )
|
|
if len(dspec) == 2:
|
|
self.checkDSpecDigital( count, dspec )
|
|
if isinstance( dspec[0], str ):
|
|
masterCell = CRL.AllianceFramework.get().getCell( dspec[0], CRL.Catalog.State.Views )
|
|
instance = Instance.create( self.cell
|
|
, dspec[1]
|
|
, masterCell
|
|
, Transformation()
|
|
, Instance.PlacementStatus.UNPLACED )
|
|
self.__dict__[ dspec[1] ] = instance
|
|
else:
|
|
masterCell = dspec[0]
|
|
instance = Instance.create( self.cell
|
|
, dspec[1]
|
|
, masterCell
|
|
, Transformation()
|
|
, Instance.PlacementStatus.UNPLACED )
|
|
self.__dict__[ dspec[1] ] = instance
|
|
else:
|
|
self.checkDSpec( count, dspec )
|
|
|
|
trace( 110, '\t==============================================================\n' )
|
|
trace( 110, '\tBuilding \"%s\"\n' % dspec[1] )
|
|
if isderived(dspec[0],TransistorFamily):
|
|
device = dspec[0].create( self.library, dspec[1], dspec[3], dspec[11] )
|
|
device.getParameter( 'Layout Styles' ).setValue( dspec[2] )
|
|
device.getParameter( 'W' ).setValue( toDbU(dspec[4]) )
|
|
device.getParameter( 'L' ).setValue( toDbU(dspec[5]) )
|
|
device.getParameter( 'M' ).setValue( dspec[6] )
|
|
device.setSourceFirst( dspec[9] )
|
|
device.setBulkType ( dspec[10] )
|
|
|
|
if (len(dspec) > 12): device.getParameter( 'NERC' ).setValue(int (dspec[12]))
|
|
if (len(dspec) > 13): device.getParameter( 'NIRC' ).setValue(int (dspec[13]))
|
|
if (len(dspec) > 14):
|
|
for wiringSpec in dspec[14].split(' '):
|
|
fields = wiringSpec.split('.')
|
|
if len(fields) > 1:
|
|
device.getParameter( fields[0]+'.t' ).setValue( fields[1] )
|
|
|
|
if not (dspec[7] is None): device.setMint ( dspec[7] )
|
|
if dspec[8]: device.setExternalDummy( dspec[8] )
|
|
|
|
elif isderived(dspec[0],CapacitorFamily):
|
|
if isinstance(dspec[4],float): capaValues = (dspec[4],)
|
|
elif isinstance(dspec[4],tuple): capaValues = dspec[4]
|
|
else:
|
|
raise ErrorMessage( 1, 'AnalogDesign.doDevice(): Invalid type for capacities values "%s".' \
|
|
% str(dspec[4]) )
|
|
|
|
device = dspec[0].create( self.library, dspec[1], dspec[3], len(capaValues) )
|
|
device.getParameter( 'Layout Styles' ).setValue( dspec[2] )
|
|
device.getParameter( 'matrix' ).setMatrix( dspec[5] )
|
|
device.setDummy( dspec[6] )
|
|
for i in range(len(capaValues)):
|
|
device.getParameter( 'capacities' ).setValue( i, capaValues[i] )
|
|
|
|
elif isderived(dspec[0],ResistorFamily):
|
|
print( dspec )
|
|
device = dspec[0].create( self.library, dspec[1], dspec[3] )
|
|
device.getParameter( 'R' ).setValue( dspec[4] )
|
|
device.getParameter( 'W' ).setValue( toDbU(dspec[5]) )
|
|
device.getParameter( 'L' ).setValue( toDbU(dspec[6]) )
|
|
device.getParameter( 'bends' ).setValue( dspec[7] )
|
|
trace( 100, '\tW:{0}\n'.format(dspec[5]) )
|
|
trace( 100, '\tpW:{0}\n'.format(device.getParameter('W')) )
|
|
trace( 100, '\tbends:{0}\n'.format(dspec[7]) )
|
|
else:
|
|
raise ErrorMessage( 1, 'AnalogDesign.doDevice(): Unknown/unsupported device "%s".' % str(dspec[0]) )
|
|
|
|
self.generator.setDevice ( device )
|
|
self.generator.drawLayout()
|
|
instance = Instance.create( self.cell
|
|
, dspec[1]
|
|
, device
|
|
, Transformation()
|
|
, Instance.PlacementStatus.UNPLACED )
|
|
|
|
self.__dict__[ dspec[1] ] = instance
|
|
trace( 100, '\tAdd Instance:{0}\n'.format(dspec[1]) )
|
|
return
|
|
|
|
def doDevices ( self ):
|
|
trace( 110, ',+', '\tAnalogDesign.doDevices()\n' )
|
|
|
|
if not hasattr(self,'devicesSpecs'):
|
|
raise Error( 3, 'AnalogDesign.doDevices(): Mandatory attribute \"self.devicesSpecs\" has not been defined.' )
|
|
if not isinstance(self.devicesSpecs,list):
|
|
raise Error( 3, 'AnalogDesign.doDevices(): Attribute \"self.devicesSpecs\" *must* be a Python list.' )
|
|
|
|
count = 1
|
|
for dspec in self.devicesSpecs:
|
|
self.doDevice( count, dspec )
|
|
count += 1
|
|
trace( 110, '-,' )
|
|
return
|
|
|
|
def showNode ( self, node ):
|
|
lines = [ '{' ]
|
|
for key, value in node.items():
|
|
if key == 'children':
|
|
lines += [ "%20s { ... }" % "'children':" ]
|
|
else:
|
|
skey = "'%s':" % str(key)
|
|
lines += [ "%20s %s" % (skey,str(value)) ]
|
|
lines += [ '}' ]
|
|
return lines
|
|
|
|
def checkNode ( self, node, isRoot ):
|
|
if not isinstance(node,dict):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Node element is *not* a dict.'
|
|
] + self.showNode(node) )
|
|
if not 'type' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"type\" key/element.'
|
|
] + self.showNode(node) )
|
|
nodeType = node['type']
|
|
if nodeType not in [VNode, HNode, DNode]:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"type\" must be one of VNode, HNode or DNode.'
|
|
] + self.showNode(node) )
|
|
|
|
if nodeType == DNode:
|
|
if not 'device' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"device\" key/element.'
|
|
] + self.showNode(node) )
|
|
if not isinstance(node['device'],str):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"device\" value *must* be of type str.'
|
|
] + self.showNode(node) )
|
|
if not 'span' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"span\" key/element.'
|
|
] + self.showNode(node) )
|
|
if not isinstance(node['span'],tuple) \
|
|
or len(node['span']) != 3 \
|
|
or not isinstance(node['span'][0],float) \
|
|
or not isinstance(node['span'][1],float) \
|
|
or not isinstance(node['span'][2],float):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"span\" value *must* be a tuple of 3 floats.'
|
|
] + self.showNode(node) )
|
|
if not 'NF' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"NF\" key/element.'
|
|
] + self.showNode(node) )
|
|
if not isinstance(node['NF'],int):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"NF\" value *must* be of type int.'
|
|
] + self.showNode(node) )
|
|
else:
|
|
if isRoot:
|
|
if not 'toleranceRatioH' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceRationH\" key/element in root node.'
|
|
] + self.showNode(node) )
|
|
if not 'toleranceRatioW' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceRationW\" key/element in root node.'
|
|
] + self.showNode(node) )
|
|
if not 'toleranceBandH' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceBandH\" key/element in root node.'
|
|
] + self.showNode(node) )
|
|
if not 'toleranceBandW' in node:
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceBandW\" key/element in root node.'
|
|
] + self.showNode(node) )
|
|
if not 'children' in node:
|
|
print( Error( 3, [ 'AnalogDesign.doSlicingTree(): Suspicious root node without children.'
|
|
] + self.showNode(node) ))
|
|
if 'children' in node:
|
|
if not isinstance(node['children'],list):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"children\" value *must* be of type list.' ]
|
|
+ self.showNode(node) )
|
|
|
|
if 'symmetries' in node:
|
|
symmetries = node['symmetries']
|
|
if not isinstance(symmetries,list):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"symmetries\" value *must* be of type list.'
|
|
] + self.showNode(node) )
|
|
for i in range(len(symmetries)):
|
|
if not isinstance(symmetries[i],tuple) \
|
|
or len(symmetries[i]) != 2 \
|
|
or not isinstance(symmetries[i][0],int) \
|
|
or not isinstance(symmetries[i][1],int):
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"symmetries\" entry [%d] *must* be a tuple of 2 int.' % i ]
|
|
+ self.showNode(node) )
|
|
return
|
|
|
|
def beginSlicingTree ( self ):
|
|
trace( 110, ',+', '\tAnalogDesign.beginSlicingTree()\n' )
|
|
return
|
|
|
|
def topNode ( self ): return self.stack[-1][0]
|
|
def topSymmetries ( self ): return self.stack[-1][1]
|
|
def topSymmetriesNet ( self ): return self.stack[-1][2]
|
|
|
|
def setToleranceRatioH ( self, u ): self.toleranceRatioH = toDbU(u)
|
|
def setToleranceRatioW ( self, u ): self.toleranceRatioW = toDbU(u)
|
|
def setToleranceBandH ( self, u ): self.toleranceBandH = toDbU(u)
|
|
def setToleranceBandW ( self, u ): self.toleranceBandW = toDbU(u)
|
|
|
|
def dupTolerances ( self, node ):
|
|
node.setToleranceRatioH( self.toleranceRatioH )
|
|
node.setToleranceRatioW( self.toleranceRatioW )
|
|
node.setToleranceBandH ( self.toleranceBandH )
|
|
node.setToleranceBandW ( self.toleranceBandW )
|
|
return
|
|
|
|
def pushNode ( self, node ):
|
|
trace( 110, ',+', '\tSlicingTree.pushNode() %s ' % str(node) )
|
|
parent = None
|
|
if len(self.stack):
|
|
parent = self.topNode()
|
|
parent.push_back( node )
|
|
trace( 110, '(parent id:%d)\n' % parent.getId() )
|
|
else:
|
|
trace( 110, '(Root)\n' )
|
|
self.slicingTree = node
|
|
node.setCell( self.cell )
|
|
|
|
self.stack.append( (node,[],[]) )
|
|
self.dupTolerances( node )
|
|
node.setRoutingGauge( self.rg )
|
|
#node.cprint()
|
|
return
|
|
|
|
def pushVNode ( self, alignment ):
|
|
self.pushNode( VSlicingNode.create( alignment ) )
|
|
return
|
|
|
|
def pushHNode ( self, alignment ):
|
|
self.pushNode( HSlicingNode.create( alignment ) )
|
|
return
|
|
|
|
def popNode ( self ):
|
|
for childIndex, copyIndex in self.topSymmetries():
|
|
self.topNode().addSymmetry( childIndex, copyIndex )
|
|
for type, net1, net2 in self.topSymmetriesNet():
|
|
if (net2 == None):
|
|
self.topNode().addSymmetryNet( type, net1 )
|
|
else:
|
|
self.topNode().addSymmetryNet( type, net1, net2 )
|
|
|
|
trace( 110, '-,', '\tSlicingTree.popNode() %s\n' % str(self.topNode()) )
|
|
if len(self.stack) == 1:
|
|
trace( 110, '\tAnalogDesign.endSlicingTree()\n' )
|
|
trace( 110, '-,', '\tSlicingTree %s stack size:%d\n' % (self.cell.getName(), len(self.stack)) )
|
|
#self.topNode().setCell( self.cell )
|
|
self.topNode().updateNetConstraints()
|
|
self.topNode().updateGlobalSize()
|
|
del self.stack[-1]
|
|
return
|
|
|
|
def addDevice ( self, name, align, parameter=None, index=0 ):
|
|
node = DSlicingNode.create( name, self.cell, parameter, self.rg )
|
|
node.setAlignment( align )
|
|
if index != 0: node.setBoxSetIndex( index )
|
|
self.topNode().push_back( node )
|
|
trace( 110, '\tSlicingTree.addDevice() %s (parent id:%d)\n' % (str(node),self.topNode().getId()) )
|
|
#node.cprint()
|
|
return
|
|
|
|
def addHRail ( self, net, metal, npitch, cellName, instanceName ):
|
|
self.checkRail( net, metal, npitch, cellName, instanceName )
|
|
node = RHSlicingNode.create( net, DataBase.getDB().getTechnology().getLayer(metal), npitch, cellName, instanceName)
|
|
self.topNode().push_back( node )
|
|
trace( 110, '\tSlicingTree.addHRail() to %s\n' % (str(self.topNode())) )
|
|
#node.cprint()
|
|
return
|
|
|
|
def addVRail ( self, net, metal, npitch, cellName, instanceName ):
|
|
self.checkRail( net, metal, npitch, cellName, instanceName )
|
|
node = RVSlicingNode.create( net, DataBase.getDB().getTechnology().getLayer(metal), npitch, cellName, instanceName)
|
|
self.topNode().push_back( node )
|
|
trace( 110, '\tSlicingTree.addVRail() to %s\n' % (str(self.topNode())) )
|
|
#node.cprint()
|
|
return
|
|
|
|
def addSymmetry ( self, childIndex, copyIndex ):
|
|
self.topSymmetries().append( (childIndex,copyIndex) )
|
|
return
|
|
|
|
def addSymmetryNet ( self, type, net1, net2=None ):
|
|
self.topSymmetriesNet().append( (type, net1, net2) )
|
|
return
|
|
|
|
def endSlicingTree ( self ):
|
|
self.slicingTree.updateGlobalSize()
|
|
#bora = Bora.BoraEngine.get( self.cell )
|
|
#if not bora: bora = Bora.BoraEngine.create( self.cell )
|
|
#bora.updateSlicingTree()
|
|
return
|
|
|
|
def updatePlacement ( self, *args ):
|
|
if self.slicingTree:
|
|
bora = Bora.BoraEngine.get( self.cell )
|
|
if not bora: bora = Bora.BoraEngine.create( self.cell )
|
|
|
|
signatureMatched = True
|
|
if len(args) == 2: bora.updatePlacement( toDbU(args[0]), toDbU(args[1]) )
|
|
elif len(args) == 1: bora.updatePlacement( args[0] )
|
|
else: signatureMatched = False
|
|
|
|
#if signatureMatched:
|
|
# katana = Katana.KatanaEngine.get( self.cell )
|
|
# if katana:
|
|
# katana.loadGlobalRouting( Anabatic.EngineLoadGrByNet )
|
|
# katana.runNegociate( Katana.Flags.PairSymmetrics );
|
|
# #katana.destroy()
|
|
return
|