# -*- Mode:Python -*- # # This file is part of the Coriolis Software. # Copyright (c) UPMC 2016-2018, 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 import CRL import helpers from helpers import isderived from helpers import trace from helpers.io import ErrorMessage as Error from Analog import Device from Analog import Transistor from Analog import CommonDrain from Analog import CommonGatePair from Analog import CommonSourcePair from Analog import CrossCoupledPair from Analog import DifferentialPair from Analog import LevelShifter from Analog import SimpleCurrentMirror from Analog import LayoutGenerator from Bora import SlicingNode from Bora import HSlicingNode from Bora import VSlicingNode from Bora import DSlicingNode from Bora import RHSlicingNode from Bora import RVSlicingNode import karakaze.Oceane import Anabatic import Katana import Bora NMOS = Transistor.NMOS PMOS = Transistor.PMOS 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 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 = karakaze.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 self.netCache.has_key(netName): 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 netType.has_key('isExternal'): 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 len(dspec) < 12: raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], has %d items instead of 12 .' \ % (count,len(dspec)) , '%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) ]) 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 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) ]) 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 len(dspec) > 2: 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]) ) 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.setPlacementStatus( Instance.PlacementStatus.UNPLACED ) self.__dict__[ dspec[1] ] = instance else: masterCell = dspec[0] instance = Instance.create( self.cell, dspec[1], masterCell, Transformation() ) instance.setPlacementStatus( Instance.PlacementStatus.UNPLACED ) self.__dict__[ dspec[1] ] = instance else: self.checkDSpec( count, dspec ) trace( 110, '\tBuilding \"%s\"\n' % dspec[1] ) 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 not (dspec[7] is None): device.setMint ( dspec[7] ) if dspec[8]: device.setExternalDummy( dspec[8] ) self.generator.setDevice ( device ) self.generator.drawLayout() instance = Instance.create( self.cell, dspec[1], device, Transformation() ) instance.setPlacementStatus( Instance.PlacementStatus.UNPLACED ) self.__dict__[ dspec[1] ] = instance 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 node.has_key('type'): 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 node.has_key('device'): 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 node.has_key('span'): 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 node.has_key('NF'): 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 node.has_key('toleranceRatioH'): raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceRationH\" key/element in root node.' ] + self.showNode(node) ) if not node.has_key('toleranceRatioW'): raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceRationW\" key/element in root node.' ] + self.showNode(node) ) if not node.has_key('toleranceBandH'): raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceBandH\" key/element in root node.' ] + self.showNode(node) ) if not node.has_key('toleranceBandW'): raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceBandW\" key/element in root node.' ] + self.showNode(node) ) if not node.has_key('children'): print Error( 3, [ 'AnalogDesign.doSlicingTree(): Suspicious root node without children.' ] + self.showNode(node) ) if node.has_key('children'): if not isinstance(node['children'],list): raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"children\" value *must* be of type list.' ] + self.showNode(node) ) if node.has_key('symmetries'): 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, span=(0, 0, 0), NF=0 ): node = DSlicingNode.create( name, self.cell, span[0], span[1], span[2], self.rg ) node.setAlignment( align ) if NF != 0: node.setNFing( NF ) 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