# -*- coding: utf-8 -*- import copy import datetime from Hurricane import DataBase from Hurricane import UpdateSession from Hurricane import DbU from Hurricane import Box from Hurricane import Net from Hurricane import Horizontal from Hurricane import Vertical from Hurricane import Contact from Hurricane import Pad from Hurricane import NetExternalComponents from CRL import AllianceFramework import Constant import helpers from helpers.io import ErrorMessage as Error from helpers import trace from Analog import Device import oroshi #helpers.setTraceLevel( 100 ) def traceMT ( mt ): trace( 100, '+', '\tMeta-Transistor Topological Datas\n' ) trace( 100, '\t +-------------------------------------------------+\n' ) trace( 100, '\t | Gate name | %20s |\n' % mt['gate'] ) trace( 100, '\t +------------+------------------------------------+\n' ) trace( 100, '\t | NF | %10d |\n' % mt['NF'] ) trace( 100, '\t | MIN | %10r |\n' % mt['MIN'] ) trace( 100, '\t +------------+-----------------------+------------+\n' ) trace( 100, '\t | Style | NSint | %10d |\n' % mt['style.NSint'] ) trace( 100, '\t | | DSint | %10d |\n' % mt['style.NDint'] ) trace( 100, '\t | | NSend | %10d |\n' % mt['style.NSend'] ) trace( 100, '\t | | DSend | %10d |\n' % mt['style.NDend'] ) trace( 100, '\t +------------+---------+-------------+------------+\n' ) trace( 100, '\t | Stress | BSIM4 | SAeff | %10.2g |\n' % mt['stress.SAeff_Bsim4' ] ) trace( 100, '\t | | | SBeff | %10.2g |\n' % mt['stress.SBeff_Bsim4' ] ) trace( 100, '\t | | | SAinv | %10.2g |\n' % mt['stress.SAinv_Bsim4' ] ) trace( 100, '\t | | | SBinv | %10.2g |\n' % mt['stress.SBinv_Bsim4' ] ) trace( 100, '\t | | | SBinv | %10.2g |\n' % mt['stress.SBinv_Bsim4' ] ) trace( 100, '\t | | | alpha | %10.2g |\n' % mt['stress.alpha' ] ) trace( 100, '\t | | | alphaInv | %10.2g |\n' % mt['stress.alphaInv' ] ) trace( 100, '\t | | | LODeffect | %10.2g |\n' % mt['stress.LODeffect' ] ) trace( 100, '\t | +---------+-------------+------------+\n' ) trace( 100, '\t | | Crolles | SAeff | %10.2g |\n' % mt['stress.SAeff_Crolles'] ) trace( 100, '\t | | | SBeff | %10.2g |\n' % mt['stress.SBeff_Crolles'] ) trace( 100, '\t | | | SAinv | %10.2g |\n' % mt['stress.SAinv_Crolles'] ) trace( 100, '\t | | | SBinv | %10.2g |\n' % mt['stress.SBinv_Crolles'] ) trace( 100, '\t | | | po2actEff | %10.2g |\n' % mt['stress.po2actEff_Crolles'] ) trace( 100, '\t +------------+---------+-------------+------------+\n' ) trace( 100, '\t | Parasitics | ASeff %10.2g m2 |\n' % mt['parasitics.ASeff'] ) trace( 100, '\t | | PSeff %10.2g m |\n' % mt['parasitics.PSeff'] ) trace( 100, '\t | | ADeff %10.2g m2 |\n' % mt['parasitics.ADeff'] ) trace( 100, '\t | | PDeff %10.2g m |\n' % mt['parasitics.PDeff'] ) trace( 100, '\t +------------+------------------------------------+\n' ) trace( 100, '-' ) return class Wiring ( object ): def __init__ ( self, device, wiring ): chain = wiring.split('.') net = chain[0] side = chain[1] if net == 'B' and device.isBulkConnected(): net = 'S' self.net = device.getNet( net ) self.topTrack = -1 self.botTrack = -1 self.wTrack = 1 if len(chain) >= 3: self.wTrack = int(chain[2]) length = len(side) for i in range( 0, length/2, 2 ): track = -2 if side[i+1] != 'X': track = int( side[i+1] ) if side[i] == 't': self.topTrack = track if side[i] == 'b': self.botTrack = track if side[i] == 'z': pass return def isDrain ( self ): return self.net.getName()[0] == 'D' def isSource ( self ): return self.net.getName()[0] == 'S' def isGate ( self ): return self.net.getName()[0] == 'G' def isBulk ( self ): return self.net.getName()[0] == 'B' def isTop ( self ): if self.topTrack != -1: return Stack.NorthBulk return None def isBot ( self ): if self.botTrack != -1: return Stack.SouthBulk return None def getTop ( self ): return self.topTrack def getBot ( self ): return self.botTrack def setTop ( self, track ): self.topTrack = track def setBot ( self, track ): self.botTrack = track def getWTrack ( self ): return self.wTrack def __str__ ( self ): s = self.net.getName() if not self.isTop() and not self.isBot(): s += 'z' else: if self.isBot(): s += '.b%d' % self.botTrack s += '.%d' % self.wTrack if self.isTop(): s += '.t%d' % self.topTrack s += '.%d' % self.wTrack return s class Bulk ( object ): def __init__ ( self, stack, axis, usource, utarget, flags ): self.stack = stack self.axis = axis self.usource = usource self.utarget = utarget self.flags = flags self.ucontacts = [] self.ucontactsBulk = [] return def addContact ( self, upos, enabled=True ): #if self.ucontacts: # trace( 100, '\taddContact [ ' ) # for pos, en in self.ucontacts: trace( 100, "%s+%s " % (DbU.getValueString(pos),en) ) # trace( 100, '] inserting: %s+%s\n' % (DbU.getValueString(upos),enabled) ) if len(self.ucontacts) == 0 or self.ucontacts[-1][0] < upos: self.ucontacts.append( (upos,enabled) ) else: for i in range(len(self.ucontacts)): if self.ucontacts[i][0] > upos: self.ucontacts.insert( i, (upos,enabled) ) return if self.ucontacts[i][0] == upos: return self.ucontacts.append( (upos,enabled) ) return def _computeContacts ( self, ucontacts, pitch ): #trace( 100, ',+', '\tBulk._computeContacts() @%s\n' % DbU.getValueString(self.axis) ) #trace( 100, '\thorPitch: %s\n' % DbU.getValueString(self.stack.horPitch) ) #trace( 100, '\tbulk pitch: %s\n' % DbU.getValueString(pitch) ) #trace( 100, '\tusource: %s\n' % DbU.getValueString(self.usource) ) #trace( 100, '\tutarget: %s\n' % DbU.getValueString(self.utarget) ) icontact = 0 ucontact = self.usource while ucontact <= self.utarget: #trace( 100, '\tucontact: %s\n' % DbU.getValueString(ucontact) ) if len(ucontacts) == 0: ucontacts.insert( 0, (ucontact,True) ) else: if icontact == 0 and ucontact < ucontacts[0][0]: if ucontacts[0][0] - ucontact >= pitch: ucontacts.insert( 0, (ucontact,True) ) else: while icontact+1 < len(ucontacts) and ucontacts[icontact+1][0] < ucontact: if icontact+1 == len(ucontacts): break icontact += 1 if ucontact - ucontacts[icontact][0] >= pitch: if icontact + 1 >= len(ucontacts) \ or ucontacts[icontact+1][0] - ucontact >= pitch: ucontacts.insert( icontact+1, (ucontact,True) ) icontact += 1 else: ucontact = ucontacts[icontact][0] ucontact += pitch trace( 100, '-' ) return def computeContacts ( self ): #self.ucontactsBulk = copy.deepcopy( self.ucontacts ) pitch = self.stack.minWidth_metal1 + self.stack.minSpacing_metal1 pitch = max( pitch, self.stack.minWidth_cut0 + self.stack.minEnclosure_metal1_cut0*2 + self.stack.minSpacing_metal1 ) if self.stack.isVH: self._computeContacts( self.ucontactsBulk, pitch ) pitch = max( pitch, self.stack.minWidth_cut1 + self.stack.minEnclosure_metal1_cut1*2 + self.stack.minSpacing_metal1 ) pitch = max( pitch, self.stack.minWidth_metal2 + self.stack.minSpacing_metal2 ) pitch = max( pitch, self.stack.minWidth_cut1 + self.stack.minEnclosure_metal2_cut1*2 + self.stack.minSpacing_metal2 ) pitch = max( pitch, self.stack.minWidth_cut2 + self.stack.minEnclosure_metal2_cut2*2 + self.stack.minSpacing_metal2 ) if self.stack.isVH: pitch = max( pitch, self.stack.minWidth_metal3 + self.stack.minSpacing_metal3 ) pitch = max( pitch, self.stack.minWidth_cut2 + self.stack.minEnclosure_metal3_cut2*2 + self.stack.minSpacing_metal3 ) #trace( 100, '\tOriginal ucontacts[]\n' ) #for ucontact, enabled in self.ucontacts: # trace( 100, '\tucontact: %s enabled:%d\n' % (DbU.getValueString(ucontact),enabled) ) #trace( 100, '\n' ) self._computeContacts( self.ucontacts, pitch ) if not self.stack.isVH: self.ucontactsBulk = self.ucontacts return 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' ) bulkNet = self.stack.bulkNet self.computeContacts() #trace( 100, '\tBulk.doLayout() @%s\n' % DbU.getValueString(self.axis) ) if self.flags & (Stack.NorthBulk | Stack.SouthBulk): xsource = self.usource xtarget = self.utarget if not self.stack.hasWestBulk(): xsource = self.ucontacts[ 0][0] if not self.stack.hasEastBulk(): xtarget = self.ucontacts[-1][0] width = self.stack.minWidth_metal1 width = max( width, self.stack.minEnclosure_metal1_cut0*2 + self.stack.minWidth_cut0 ) Horizontal.create( bulkNet , metal1 , self.axis , width , xsource-self.stack.wire1Width/2 , xtarget+self.stack.wire1Width/2 ) width = self.stack.minWidth_cut0 + 2* self.stack.minEnclosure_active_cut0 Horizontal.create( bulkNet , active , self.axis , width , self.usource-width/2 , self.utarget+width/2 ) width += 2* self.stack.minEnclosure_bImplant_active Horizontal.create( bulkNet , self.stack.bImplantLayer , self.axis , width , self.usource-width/2 , self.utarget+width/2 ) for xcontact, enabled in self.ucontactsBulk: if enabled: Contact.create( bulkNet , cut0 , xcontact , self.axis , self.stack.minWidth_cut0 , self.stack.minWidth_cut0 ) cutBb = Box( xcontact, self.axis, xcontact, self.axis ) #metal1EnclBb = Box( cutBb ).inflate( self.stack.minEnclosure_metal1_cut0 + self.stack.minWidth_cut0/2 ) #Pad.create( bulkNet, metal1, metal1EnclBb ) for xcontact, enabled in self.ucontacts: trace( 100, '\tucontact: %s enabled:%d\n' % (DbU.getValueString(xcontact),enabled) ) if enabled: Contact.create( bulkNet , cut1 , xcontact , self.axis , self.stack.minWidth_cut1 , self.stack.minWidth_cut1 ) if self.stack.isVH: Contact.create( bulkNet , cut2 , xcontact , self.axis , self.stack.minWidth_cut2 , self.stack.minWidth_cut2 ) cutBb = Box( xcontact, self.axis, xcontact, self.axis ) metal1EnclBb = Box( cutBb ).inflate( self.stack.minEnclosure_metal1_cut1 + self.stack.minWidth_cut1/2 ) Pad.create( bulkNet, metal1, metal1EnclBb ) metal2EnclBb = Box( cutBb ).inflate( self.stack.minEnclosure_metal2_cut1 + self.stack.minWidth_cut1/2 ) if self.stack.isVH: metal2EnclBb.merge( Box( cutBb ).inflate( self.stack.minEnclosure_metal2_cut2 + self.stack.minWidth_cut2/2 ) ) Pad.create( bulkNet, metal2, metal2EnclBb ) if self.stack.isVH: metal3EnclBb = Box( cutBb ).inflate( self.stack.minEnclosure_metal3_cut2 + self.stack.minWidth_cut2/2 ) Pad.create( bulkNet, metal3, metal3EnclBb ) if self.flags & (Stack.EastBulk | Stack.WestBulk): metal1Width = self.stack.minWidth_metal1 metal1Width = max( metal1Width, self.stack.minWidth_cut0 + 2*self.stack.minEnclosure_metal1_cut0 ) metal1Width = max( metal1Width, self.stack.minWidth_cut1 + 2*self.stack.minEnclosure_metal1_cut1 ) Vertical.create( bulkNet , metal1 , self.axis , metal1Width , self.usource-self.stack.wire1Width/2 , self.utarget+self.stack.wire1Width/2 ) width = self.stack.minWidth_cut0 + 2*self.stack.minEnclosure_active_cut0 Vertical.create( bulkNet , active , self.axis , width , self.usource-width/2 , self.utarget+width/2 ) width += 2*self.stack.minEnclosure_bImplant_active Vertical.create( bulkNet , self.stack.bImplantLayer , self.axis , width , self.usource-width/2 , self.utarget+width/2 ) for icontact in range(len(self.ucontacts)): if (icontact == 0 and self.stack.hasSouthBulk()) \ or (icontact+1 == len(self.ucontacts) and self.stack.hasNorthBulk()): continue Contact.create( bulkNet , cut0 , self.axis , self.ucontacts[icontact][0] , self.stack.minWidth_cut0 , self.stack.minWidth_cut0 ) return ## Draw a Stack of Transistors. # # A Stack of Transistors is a set of transistor put into a regular # band and connected through their sources/drains. All share the # exact same W & L. The way they are connecteds defines what functionnality # the Stack implement. # # The abutment box of the stack is adjusted so that both height and width # are even multiples of the track pitches, so the device can be easily # placed and handled by the mixed router. The extra space needed for # padding is added around the active area. Due to the presence of tracks # at the top and bottom of the stack, the active area will be horizontally # centered but \b not vertically. # # The drawing of the stack is controlled through a set of variables # (attributes) that allows to create it regardless of the technology. # The technology is taken into account in the way those variables are # computed and, obviously, their values. The following schematics details # the main stack drawing variables along with their computations. # # \section secStackLayout Stack Layout # # \subsection secGatePitch Gate pitch # # - \c self.gatePitch : the pitch of transistors gates, inside the stack. # It also applies to dummy transistors. # # \image html gate-pitch-1.png "Gate Pitch" # \image latex gate-pitch-1.pdf "Gate Pitch" width=.9\linewidth # # # \subsection secActiveSideWidth Active Side Width # # - \c self.activeSideWidth : the distance between the axis of the last # transistor gate (on the left or right) and the edge of the active # area (\e not the diffusion area). # # \image html active-side-width-1.png "Active Side Width" # \image latex active-side-width-1.pdf "Active Side Width" width=.9\linewidth # # # \subsection secHTrackDistance H-Track Distance # # - \c self.hTrackDistance : the minimal distance between either the top or # bottom edge of the active area and the \e axis of the first track. # # \image html htrack-distance-1.png "H-Track distance" # \image latex htrack-distance-1.pdf "H-Track distance" width=.9\linewidth # # # \subsection secOverallVariables BoundingBox & Overall Variables # # - \c self.xpitches : the number of vertical track pitches needed to fully # enclose the active area. # - \c self.ypitches : the number of horizontal track pitches needed to fully # enclose the active area. # - \c self.activeOffsetX & \c self.activeOffsetY : the offsets of the active area # from the bottom left corner of the abutment box. # - \c self.diffusionWidth & \c self.diffusionHeight are the minimun dimensions # required to fit the active area. # - \c self.topTracksNb() : the number of tracks at the top of the stack. # - \c self.botTracksNb() : the number of tracks at the bottom of the stack. # # \image html stack-layout-3.png "General Stack Layout" # \image latex stack-layout-3.pdf "General Stack Layout" width=.9\linewidth # # # \section secWiringSpecs Wiring Specifications # # Stack routing is done through vertical \c metal1 wires coming from the # gates and diffusions areas and \c metal2 horizontal wires that can be # either above or below the active area. \c metal2 wires (or track) goes # through the whole stack and are assigned to one net only. A net will # have at least one track above or below and may have both. # # The connections to the diffusions areas and gates of the various # fingers are specified through a list. The stack is made of a regular # alternation of diffusions and gates. The list tells, for each one # starting from the left, to which net and track they are connected. # For a stack of \f$NFs\f$ transistor fingers, the must wiring specification # must contains \f$ 3 + (NFs-1) \times 2\f$ elements. The list is given # through one \e string with each elements separated by one or more # whitespace. The syntax for \e one element is detailed \ref secAtomicWiring. # # Track numbering scheme # # Tracks above (top) the active area and below (bottom) each have their # own numbering. In both case, the count start \e from the active area. # This, the top tracks will be numbered by increasing Y and the bottom # tracks by \e decreasing Y. # # Track/Net assignement # # The track/net assignement is deduced from the atomic wiring specifications. # It also allows to compute the total number of tracks needed above and # below the active area. # # \image html wiring-spec-2.png "Wiring Specification" # \image latex wiring-spec-2.pdf "Wiring Specification" width=.9\linewidth # # \subsection secAtomicWiring Atomic Wiring Specification # # An atomic wiring specification has the same syntax for either diffusions # or gates. It \e must not comprise any whitespaces. it is made of the # following parts: # - The net name to connect to. # - Whether the track is above the active area (\c "t") or below (\c "b"). # The special case (\c "z") means that this element must be left # unconnected (is such case possible?). # - The number of the track. # # \image html wiring-spec-1.png "Atomic Wiring Specification" # \image latex wiring-spec-1.pdf "Atomic Wiring Specification" width=.4\linewidth # # # \section secStackImplDetails Stack Implementation Details # # The \c __setattr__() and \c __getattr__ functions have been redefined # so that the technological values (rules) can be accessed has normal # attributes of the Stack class, in addition to the regular ones. class Stack ( object ): NorthBulk = 0x0004 SouthBulk = 0x0008 EastBulk = 0x0010 WestBulk = 0x0020 RingBulk = NorthBulk|SouthBulk|EastBulk|WestBulk SourceIsolated = 0x0040 SourceShared = 0x0080 SourceMerged = 0x0100 DrainIsolated = 0x0200 DrainShared = 0x0400 DrainMerged = 0x0800 rules = oroshi.getRules() def __setattr__ ( self, attribute, value ): if hasattr(Stack.rules,attribute): print '[ERROR] Stack.%s attribute is read-only (ignored).' % attribute return self.__dict__[attribute] = value return def __getattr__ ( self, attribute ): if attribute.find('_') != -1: if attribute in [ 'minEnclosure_nImplant_active' , 'minEnclosure_pImplant_active' ]: raise Error( 3, 'Stack.__getattr__(): Do not access directly rule "%s".' % attribute ) if self.isNmos(): if attribute == 'minEnclosure_tImplant_active': return getattr(Stack.rules,'minEnclosure_nImplant_active') if attribute == 'minEnclosure_bImplant_active': return getattr(Stack.rules,'minEnclosure_pImplant_active') else: if attribute == 'minEnclosure_tImplant_active': return getattr(Stack.rules,'minEnclosure_pImplant_active') if attribute == 'minEnclosure_bImplant_active': return getattr(Stack.rules,'minEnclosure_nImplant_active') return getattr(Stack.rules,attribute) if self.__dict__.has_key(attribute): return self.__dict__[attribute] return None @staticmethod def _addToTracks ( tracks, trackNb, net ): if trackNb < len(tracks): if tracks[trackNb] and tracks[trackNb] != net: raise Error( 3, 'Stack._addToTracks(): Track %d is already assigned to net "%s" (requested by net "%s")' % ( trackNb, tracks[trackNb].getName(), net.getName() ) ) tracks[ trackNb ] = net else: for i in range( len(tracks), trackNb ): tracks.append( None ) tracks.append( net ) return @staticmethod def _addToWTracks ( tracks, trackNb, net ): if trackNb < len(tracks): if tracks[trackNb] and tracks[trackNb] != net: raise Error( 3, 'Stack._addToTracks(): Track %d is already assigned to net "%s" (requested by net "%s")' % ( trackNb, tracks[trackNb].getName(), net.getName() ) ) tracks[ trackNb ] = net else: for i in range( len(tracks), trackNb ): tracks.append( 0 ) tracks.append( net ) return @staticmethod def toGeomod ( geoFlags, nf ): geomod = 0 # Check for geomod 9 & 10 first, because they require the additionnal # condition that "nf" must be even. They lead to simpler equations in # The BSIM4 model. #if nf%2 == 0: if geoFlags == (Stack.SourceIsolated | Stack.DrainIsolated): geomod = 0 elif geoFlags == (Stack.SourceIsolated | Stack.DrainShared ): geomod = 1 elif geoFlags == Stack.SourceIsolated: geomod = 1 elif geoFlags == (Stack.SourceShared | Stack.DrainIsolated): geomod = 2 elif geoFlags == Stack.DrainIsolated : geomod = 2 elif geoFlags == (Stack.SourceShared | Stack.DrainShared ): geomod = 3 elif geoFlags == Stack.SourceShared: geomod = 3 elif geoFlags == Stack.DrainShared : geomod = 3 elif geoFlags == 0: geomod = 3 elif geoFlags == (Stack.SourceIsolated | Stack.DrainMerged ): geomod = 4 elif geoFlags == (Stack.SourceShared | Stack.DrainMerged ): geomod = 5 elif geoFlags == Stack.DrainMerged : geomod = 5 elif geoFlags == (Stack.SourceMerged | Stack.DrainIsolated): geomod = 6 elif geoFlags == (Stack.SourceMerged | Stack.DrainShared ): geomod = 7 elif geoFlags == Stack.SourceMerged: geomod = 7 elif geoFlags == (Stack.SourceMerged | Stack.DrainMerged ): geomod = 8 # Modes 9 & 10 are not clear to me, but we shouldn't need it ever. if geomod in [0, 4, 6, 8] and nf%2 == 0: print '[WARNING] Stack.toGeomod(): In geomod %i, NF must be odd (%i)' % (geomod,nf) return geomod ## [API] Constructor # # param rules The physical rule set. # \param device The Hurricane AMS device into which the layout will be drawn. # \param NERC Number of contact rows in external (first & last) diffusion connectors. # \param NIRC Number of contact rows in middle diffusion connectors. # param w The \b width of every transistor of the stack (aka \e fingers). # param L The \b length of every transistor. # param NFs The total number of fingers (dummies includeds). # param NDs The number of dummies to put on each side of the stack. def __init__ ( self, device, NERC, NIRC ): self.dimensioned = False self.device = device self.w = oroshi.adjustOnGrid(device.getW() / device.getM()) self.L = oroshi.adjustOnGrid(device.getL()) self.NDs = device.getExternalDummy() # Number of Dummies at each end of the stack. self.NFs = device.getM() * self.metaTnb() + self.NDs*2 # Total number of Fingers (including dummies). self.NERC = NERC self.NIRC = NIRC self.wirings = [] self.topTracks = [] self.botTracks = [] self.flags = 0 self.bulks = [ None, None, None, None ] # 0:North, 1:South, 2:East, 3:West. self.metaTransistors = {} self.topWTracks = [] # widths top tracks self.botWTracks = [] # widths bot tracks if device.isBulkConnected(): self.bulkNet = device.getNet( 'S' ) else: self.bulkNet = device.getNet( 'B' ) bulkType = self.device.getBulkType() if bulkType & 0x0001: self.flags |= Stack.NorthBulk if bulkType & 0x0002: self.flags |= Stack.SouthBulk if bulkType & 0x0004: self.flags |= Stack.EastBulk 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.wellLayer = None else: self.tImplantLayer = DataBase.getDB().getTechnology().getLayer( 'pImplant' ) self.bImplantLayer = DataBase.getDB().getTechnology().getLayer( 'nImplant' ) self.wellLayer = DataBase.getDB().getTechnology().getLayer( 'pWell' ) return def metaTnb ( self ): metaT = 0 for i in self.device.getInstances(): metaT += 1 return metaT ## [API] Set the Stack wiring specification. # # \param wiringSpec A string defining the connections for the # gates and diffusion areas. # # For a comprehensive explanation of the wiring specification, refers # to \ref secWiringSpecs . def setWirings ( self, wiringSpec ): trace( 100, 'SetWirings \n' ) restrictions = {} for net in self.device.getNets(): if net.getName() == 'anonymous': restrictions[net] = Device.AllBlocked else: restrictions[net] = Device.NorthBlocked|Device.SouthBlocked hasTopBulkTrack = False hasBotBulkTrack = False specs = wiringSpec.split() i = 0 topBulkWiring = None botBulkWiring = None trace( 100, specs ) for spec in specs: wiring = Wiring( self.device, spec ) self.wirings.append( wiring ) if wiring.isTop(): #restrictions[wiring.net] = restrictions[wiring.net] & ~Device.NorthBlocked if wiring.net == self.bulkNet: hasTopBulkTrack = True topBulkWiring = wiring continue Stack._addToTracks ( self.topTracks , wiring.topTrack, wiring.net ) Stack._addToWTracks( self.topWTracks, wiring.topTrack, wiring.wTrack ) restrictions[wiring.net] = restrictions[wiring.net] & ~Device.NorthBlocked if wiring.isBot(): #restrictions[wiring.net] = restrictions[wiring.net] & ~Device.SouthBlocked if wiring.net == self.bulkNet: hasBotBulkTrack = True botBulkWiring = wiring continue Stack._addToTracks ( self.botTracks , wiring.botTrack, wiring.net ) Stack._addToWTracks( self.botWTracks, wiring.botTrack, wiring.wTrack ) restrictions[wiring.net] = restrictions[wiring.net] & ~Device.SouthBlocked for net, flags in restrictions.items(): self.device.setRestrictions( net, flags ) if self.flags & Stack.NorthBulk or hasTopBulkTrack: if not self.topTracks or self.topTracks[-1] != self.bulkNet: for net in self.topTracks: if net == self.bulkNet: raise Error( 3, 'Stack.setWirings(): Bulk track (net:"%s") must be topmost.' \ % self.bulkNet.getName() ) index = len(self.topTracks) Stack._addToTracks( self.topTracks , index, self.bulkNet ) if (topBulkWiring != None): Stack._addToWTracks( self.topWTracks, index, topBulkWiring.wTrack ) elif (botBulkWiring != None): Stack._addToWTracks( self.topWTracks, index, botBulkWiring.wTrack ) else: Stack._addToWTracks( self.topWTracks, index, self.device.getParameter('B.w').getValue() ) bulkIndex = len(self.topTracks)-1 for wiring in self.wirings: if wiring.net == self.bulkNet: wiring.setTop( bulkIndex ) Stack._addToTracks ( self.topTracks , len(self.topTracks) , None ) Stack._addToWTracks( self.topWTracks, len(self.topWTracks), self.device.getParameter('B.w').getValue() ) if self.flags & Stack.SouthBulk or hasBotBulkTrack: if not self.botTracks or self.botTracks[-1] != self.bulkNet: for net in self.botTracks: if net == self.bulkNet: raise Error( 3, 'Stack.setWirings(): Bulk track (net:"%s") must be bottommost.' \ % self.bulkNet.getName() ) index = len(self.botTracks) Stack._addToTracks( self.botTracks , index, self.bulkNet ) if (botBulkWiring != None): Stack._addToWTracks( self.botWTracks, index, botBulkWiring.wTrack ) elif (topBulkWiring != None): Stack._addToWTracks( self.botWTracks, index, topBulkWiring.wTrack ) else: Stack._addToWTracks( self.botWTracks, index, self.device.getParameter('B.w').getValue() ) bulkIndex = len(self.botTracks)-1 for wiring in self.wirings: if wiring.net == self.bulkNet: wiring.setBot( bulkIndex ) Stack._addToTracks ( self.botTracks , len(self.botTracks) , None ) Stack._addToWTracks( self.botWTracks, len(self.botWTracks), self.device.getParameter('B.w').getValue() ) trace( 100, '\tbotTracks:%d topTracks:%d\n' % (len(self.topTracks),len(self.botTracks)) ) for i in range(len(self.wirings)): trace( 100, '\t| wirings[%d]: %s\n' % (i,str(self.wirings[i])) ) if self.wirings[i].net == self.bulkNet: if hasBotBulkTrack: self.wirings[i].setBot( len(self.botTracks)-2 ) if hasTopBulkTrack: self.wirings[i].setTop( len(self.topTracks)-2 ) trace( 100, '\t+ wirings[%d]: %s\n' % (i,str(self.wirings[i])) ) for i in range(len(self.wirings)): gateName = self.wirings[i].net.getName() if gateName[0] != 'G': continue leftMost = False rightMost = False if i == 1: leftMost = True if i == len(self.wirings)-1: rightMost = True # MIN means "minimize the number of sources", so according to the # number of transistor fingers, it is almost equivalent to # "drain first". if not self.metaTransistors.has_key(gateName): MIN = 0 if self.wirings[i-1].isDrain(): MIN = 1 self.metaTransistors[gateName] = { 'gate':self.wirings[i].net, 'MIN':MIN, 'NF':1 , 'leftMost':leftMost, 'rightMost':rightMost } else: self.metaTransistors[gateName]['NF'] += 1 self.metaTransistors[gateName]['rightMost'] = rightMost # Compute style parameters for use in geomod equations. for mt in self.metaTransistors.values(): geoFlags = 0 nf = mt['NF'] if nf % 2: mt['style.NDend'] = 1 mt['style.NSend'] = 1 mt['style.NDint'] = nf-1 mt['style.NSint'] = nf-1 if mt['leftMost']: if mt['MIN'] == 1: geoFlags |= Stack.DrainIsolated else: geoFlags |= Stack.SourceIsolated else: if mt['MIN'] == 1: geoFlags |= Stack.DrainShared else: geoFlags |= Stack.SourceShared if mt['leftMost']: if mt['MIN'] == 1: geoFlags |= Stack.SourceIsolated else: geoFlags |= Stack.DrainIsolated else: if mt['MIN'] == 1: geoFlags |= Stack.SourceShared else: geoFlags |= Stack.DrainShared else: if mt['MIN']: mt['style.NDend'] = 2 mt['style.NSend'] = 0 mt['style.NDint'] = (nf/2 - 1)*2 mt['style.NSint'] = nf if mt['leftMost'] or mt['rightMost']: geoFlags |= Stack.DrainIsolated else: geoFlags |= Stack.DrainShared else: mt['style.NDend'] = 0 mt['style.NSend'] = 2 mt['style.NSint'] = (nf/2 - 1)*2 mt['style.NDint'] = nf if mt['leftMost'] or mt['rightMost']: geoFlags |= Stack.SourceIsolated else: geoFlags |= Stack.SourceShared mt['style.geomod'] = Stack.toGeomod( geoFlags, mt['NF'] ) return def isNmos ( self ): return self.device.isNMOS() def isPmos ( self ): return self.device.isPMOS() def hasNorthBulk ( self ): return self.flags & Stack.NorthBulk def hasSouthBulk ( self ): return self.flags & Stack.SouthBulk def hasEastBulk ( self ): return self.flags & Stack.EastBulk def hasWestBulk ( self ): return self.flags & Stack.WestBulk def wiring ( self, i ): return self.wirings[i] def tracksNb ( self ): return len(self.topTracks) + len(self.botTracks) def topTracksNb ( self ): return len(self.topTracks) def botTracksNb ( self ): return len(self.botTracks) def isTopTrack ( self, net ): if net == self.bulkNet: return False trackIndex = 2 if self.flags & Stack.NorthBulk: trackIndex = 3 if trackIndex > len(self.topTracks): return False if self.topTracks[-trackIndex] != net: return False return True def isBotTrack ( self, net ): if net == self.bulkNet: return False trackIndex = 2 if self.flags & Stack.SouthBulk: trackIndex = 3 if trackIndex > len(self.botTracks): return False if self.botTracks[-trackIndex] != net: return False return True ###################################### def tracksNbPitch ( self ): nb = 0 for i in range (self.topTracksNb()-1): nb += self.topWTracks[i] for i in range (self.botTracksNb()-1): nb += self.botWTracks[i] return nb def getBotTrackY ( self, i ): nb = self.horPitch for j in range(self.botTracksNb()-2-i): nb += self.horPitch*self.botWTracks[self.botTracksNb()-2-j] return nb def getTopTrackY ( self, i ): y = (self.ypitches)*self.horPitch + self.getBotTrackY(0) + self.getHorizontalWidth(self.botWTracks[0]) #y = self.activeOffsetY + (self.ypitches-1)*self.horPitch #- self.hTrackDistance for j in range(i): y += self.horPitch*self.topWTracks[j] return y def getLastTopTrackY ( self ): return self.getTopTrackY(self.topTracksNb()-1) def getLastTopWTracks ( self ): return self.topWTracks[self.topTracksNb()-2] def getLastBotWTracks ( self ): return self.botWTracks[self.botTracksNb()-2] def getHorizontalWidth ( self, trackSpan ): return (self.horPitch * (trackSpan - 1)) def getHorizontalAxis ( self, trackSpan ): return self.getHorizontalWidth(trackSpan)/2 def getWiringWidth ( self, wiring, isTopConnect ): if isTopConnect: return self.horPitch * (self.topWTracks[wiring.topTrack] - 1) return self.horPitch * (self.botWTracks[wiring.botTrack] - 1) ###################################### def DMCI ( self ): if not self.dimensioned: self.computeDimensions() return self.sideActiveWidth \ - self.L/2 \ - self.metal1ToGate \ - self.eDiffMetal1Width/2 def DMCG ( self ): if not self.dimensioned: self.computeDimensions() return (self.gatePitch - self.L)/2 def DMCGT ( self ): return 0.0 def DGG ( self ): if not self.dimensioned: self.computeDimensions() return self.gatePitch - self.L def DGI ( self ): if not self.dimensioned: self.computeDimensions() return self.sideActiveWidth - self.L/2 ## [internal] Compute Stack dimensions from the technological rules. # # Internal function. Perform the computation of: # - \c self.metal1Pitch # - \c self.minWidth_metal1 # - \c self.metal2Pitch # - \c self.minWidth_metal2 # - \c self.gatePitch # - \c self.sideActiveWidth # - \c self.hTrackDistance # - \c self.xpitches # - \c self.ypitches # - \c self.activeOffsetX # - \c self.activeOffsetY # - \c self.boundingBox def computeDimensions ( self ): if self.dimensioned: return self.dimensioned = True trace( 100, '\tStack.computeDimensions(): Start time %s.\n' % str(datetime.datetime.now()) ) specsNb = 3 + (self.NFs - 1)*2 if len(self.wirings) != specsNb: raise Error( 3, [ 'Stack._computeDimension(): Wiring inconsistency,' \ ' have %d specs but should be %d for a NFs of %d.' \ % (len(self.wirings), specsNb, self.NFs) ] ) rg = AllianceFramework.get().getRoutingGauge() self.metal2Pitch = rg.getHorizontalPitch() self.metal3Pitch = rg.getVerticalPitch() self.isVH = rg.isVH() foundHor = False foundVer = False for depth in range(rg.getDepth()): rlg = rg.getLayerGauge(depth) if rlg.getType() == Constant.PinOnly: continue if rlg.getDirection() == Constant.Horizontal and not foundHor: self.horPitch = rlg.getPitch() foundHor = True if rlg.getDirection() == Constant.Vertical and not foundVer: self.verPitch = rlg.getPitch() foundVer = True width1 = self.minWidth_cut0 + self.minEnclosure_metal1_cut0*2 width2 = self.minWidth_cut1 + self.minEnclosure_metal1_cut1*2 self.wire1Width = max( self.minWidth_metal1, width1, width2 ) pitch1 = self.minWidth_metal2 + self.minSpacing_metal2 pitch2 = self.minWidth_cut1 + self.minEnclosure_metal2_cut1*2 + self.minSpacing_metal2 self.metal2TechnoPitch = max( pitch1, pitch2 ) if self.metal2Pitch == 0: self.metal2Pitch = self.metal2TechnoPitch if self.metal2Pitch < self.metal2TechnoPitch: raise Error( 3, 'Stack.computeDimensions(): Routing gauge pitch %s is inferior to technology minimum of %s.' % ( DbU.getValueString(self.metal2Pitch) , DbU.getValueString(self.metal2TechnoPitch) ) ) width1 = self.minWidth_cut1 + self.minEnclosure_metal2_cut1*2 if self.isVH: self.wire2Width = self.minWidth_metal2 self.wire3Width = self.minWidth_metal3 else: self.wire2Width = max( self.minWidth_metal2, width1 ) pitch1 = self.minGateSpacing_poly + self.minWidth_cut0 pitch2 = self.minSpacing_cut1 + self.minWidth_cut1 self.gateVia1Pitch = max( pitch1, pitch2 ) pitch1 = self.minWidth_cut0 + self.minSpacing_cut0 pitch2 = self.minWidth_cut1 + self.minSpacing_cut1 self.contactDiffPitch = max( pitch1, pitch2 ) self.contactDiffSide = max( self.minWidth_cut1, self.minWidth_cut0 ) iDiffContactWidth = self.contactDiffSide + (self.NIRC - 1)*self.contactDiffPitch eDiffContactWidth = self.contactDiffSide + (self.NERC - 1)*self.contactDiffPitch overlap = max( self.minEnclosure_metal1_cut0, self.minEnclosure_metal1_cut1 ) gateVia1Side = max( self.minWidth_cut1, self.minWidth_cut0 ) self.iDiffMetal1Width = iDiffContactWidth + overlap*2 self.eDiffMetal1Width = eDiffContactWidth + overlap*2 pitch1 = self.L \ + iDiffContactWidth \ + self.minSpacing_cut0_poly*2 pitch2 = self.minWidth_cut0 \ + self.minEnclosure_poly_cut0*2 \ + self.minGateSpacing_poly pitch3 = self.minSpacing_metal1*2 \ + self.iDiffMetal1Width \ + max( self.L, gateVia1Side+2*overlap ) self.gatePitch = max( pitch1, pitch2, pitch3 ) self.metal1ToGate = (self.gatePitch - self.L - self.iDiffMetal1Width) / 2 self.sideActiveWidth = self.minEnclosure_active_cut0 \ - self.minEnclosure_metal1_cut0 \ + self.eDiffMetal1Width \ + self.metal1ToGate \ + self.L/2 hTrackDistance1 = self.minWidth_cut0/2 + self.minSpacing_cut0_active hTrackDistance2 = self.minWidth_cut0/2 + self.minEnclosure_poly_cut0 + self.minSpacing_poly_active self.hTrackDistance = max( hTrackDistance1, hTrackDistance2 ) vBulkDistance1 = self.minWidth_cut0/2 \ + self.minEnclosure_active_cut0 \ + self.minEnclosure_tImplant_active \ + self.minEnclosure_bImplant_active vBulkDistance2 = self.minWidth_cut0/2 \ + self.minEnclosure_active_cut0 \ + self.minSpacing_nImplant_pImplant self.vBulkDistance = max( vBulkDistance1, vBulkDistance2 ) activeHeight = self.w + 2*self.hTrackDistance self.ypitches = activeHeight / self.horPitch if activeHeight % self.horPitch: self.ypitches += 1 if (self.ypitches + self.tracksNbPitch()) % 2: self.ypitches += 1 diffusionWidth = (self.sideActiveWidth + self.minEnclosure_tImplant_active)*2 + (self.NFs-1)*self.gatePitch deviceMinWidth = diffusionWidth if self.flags & Stack.WestBulk: deviceMinWidth += self.vBulkDistance + self.verPitch if self.flags & Stack.EastBulk: deviceMinWidth += self.vBulkDistance + self.verPitch self.xpitches = deviceMinWidth / self.verPitch if self.xpitches % 2: self.xpitches += 1 else: if diffusionWidth % self.verPitch: self.xpitches += 2 self.activeOffsetY = self.getBotTrackY(0) + self.getHorizontalWidth(self.botWTracks[0]) \ + self.hTrackDistance \ + (self.ypitches*self.horPitch - activeHeight)/2 \ self.bbHeight = self.getLastTopTrackY() self.bbWidth = self.xpitches * self.verPitch diffusionRealWidth = self.bbWidth if self.flags & Stack.WestBulk: diffusionRealWidth -= self.vBulkDistance + self.verPitch if self.flags & Stack.EastBulk: diffusionRealWidth -= self.vBulkDistance + self.verPitch self.activeOffsetX = self.minEnclosure_tImplant_active + (diffusionRealWidth - diffusionWidth)/2 if self.flags & Stack.WestBulk: self.activeOffsetX += self.vBulkDistance + self.verPitch self.boundingBox = Box( 0, 0, self.bbWidth, self.bbHeight ) self.activeBox = Box( self.activeOffsetX , self.activeOffsetY , self.activeOffsetX + (self.NFs - 1) * self.gatePitch + 2*self.sideActiveWidth , self.activeOffsetY + self.w ) westBulkX = self.activeOffsetX eastBulkX = self.activeOffsetX + diffusionWidth - self.minEnclosure_tImplant_active*2 southBulkY = self.boundingBox.getYMin() + self.horPitch northBulkY = self.boundingBox.getYMax() - self.horPitch self.bulkWidth = self.minWidth_cut0 + 2*self.minEnclosure_active_cut0 if self.flags & Stack.EastBulk: eastBulkX = self.boundingBox.getXMax() - self.horPitch self.bulks[2] = Bulk( self, eastBulkX, southBulkY, northBulkY, Stack.EastBulk ) if self.flags & Stack.WestBulk: westBulkX = self.horPitch self.bulks[3] = Bulk( self, westBulkX, southBulkY, northBulkY, Stack.WestBulk ) if self.flags & Stack.NorthBulk: self.bulks[0] = Bulk( self, northBulkY, westBulkX, eastBulkX, Stack.NorthBulk ) if self.flags & Stack.SouthBulk: self.bulks[1] = Bulk( self, southBulkY, westBulkX, eastBulkX, Stack.SouthBulk ) self.DMCI = oroshi.toUnity( self.sideActiveWidth - self.L/2 - self.metal1ToGate - self.eDiffMetal1Width/2 ) self.DMCG = oroshi.toUnity( (self.gatePitch - self.L)/2 ) self.DMCGT = 0 self.DGG = oroshi.toUnity( self.gatePitch - self.L ) self.DGI = oroshi.toUnity( self.sideActiveWidth - self.L/2 ) trace( 100, '+' ) trace( 100, '\t +----------------------------------+\n' ) trace( 100, '\t | Parameters |\n' ) trace( 100, '\t +=====================+============+\n' ) trace( 100, '\t | w (finger) | %10s |\n' % DbU.getValueString(self.w) ) trace( 100, '\t | L | %10s |\n' % DbU.getValueString(self.L) ) trace( 100, '\t | NFs | %10d |\n' % self.NFs ) trace( 100, '\t +=====================+============+\n' ) trace( 100, '\t | Computed |\n' ) trace( 100, '\t +=====================+============+\n' ) trace( 100, '\t | metal2 pitch | %10s |\n' % DbU.getValueString(self.metal2Pitch) ) trace( 100, '\t | metal2 Techno pitch | %10s |\n' % DbU.getValueString(self.metal2TechnoPitch) ) trace( 100, '\t | gatePitch | %10s |\n' % DbU.getValueString(self.gatePitch) ) trace( 100, '\t | sideActiveWidth | %10s |\n' % DbU.getValueString(self.sideActiveWidth) ) trace( 100, '\t | contactDiffPitch | %10s |\n' % DbU.getValueString(self.contactDiffPitch) ) trace( 100, '\t | hTrackDistance | %10s |\n' % DbU.getValueString(self.hTrackDistance) ) trace( 100, '\t | vBulkDistance | %10s |\n' % DbU.getValueString(self.vBulkDistance) ) trace( 100, '\t | activeOffsetX | %10s |\n' % DbU.getValueString(self.activeOffsetX) ) trace( 100, '\t | activeOffsetY | %10s |\n' % DbU.getValueString(self.activeOffsetY) ) trace( 100, '\t | active pitches | %10d |\n' % self.ypitches ) trace( 100, '\t +---------------------+------------+\n' ) trace( 100, '\n' ) trace( 100, '\tStack.computeDimensions(): End time %s.\n' % str(datetime.datetime.now()) ) trace( 100, '-' ) for mt in self.metaTransistors.values(): self.computeStressEffect( mt ) self.computeLayoutParasitics( mt ) traceMT( mt ) return ## [API] Draw the complete layout. # # Draw the commplete layout of the Stack. def doLayout ( self, bbMode ): trace( 100, '+', '\tStack.doLayout().\n' ) self.computeDimensions() if not bbMode: UpdateSession.open() self.drawActive() self.drawWell() xoffset = self.activeOffsetX + self.sideActiveWidth for i in range(self.NFs): self.drawGate( xoffset + i*self.gatePitch, self.wirings[1+2*i] ) for i in range(self.NFs+1): if i == 0: # Leftmost diffusion area. NRC = self.NERC width = self.eDiffMetal1Width axis = self.activeOffsetX \ + self.sideActiveWidth \ - self.L/2 \ - self.metal1ToGate \ - width/2 elif i == self.NFs: # Rightmost diffusion area. NRC = self.NERC width = self.eDiffMetal1Width axis = self.activeOffsetX \ + self.sideActiveWidth \ + self.gatePitch*(self.NFs - 1) \ + self.L/2 \ + self.metal1ToGate \ + width/2 else: # Middle diffusion areas. NRC = self.NIRC width = self.iDiffMetal1Width axis = self.activeOffsetX \ + self.sideActiveWidth \ - self.gatePitch/2 \ + self.gatePitch*i self.drawSourceDrain( axis, self.wirings[2*i], width, NRC ) 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' ) trackNb = 0 if self.topTracks: trackNb = len(self.topTracks) for i in range(trackNb): if not self.topTracks[i]: continue h = Horizontal.create( self.topTracks[i] , metal2 , self.getTopTrackY(i) + self.getHorizontalAxis (self.topWTracks[i]) , self.wire2Width + self.getHorizontalWidth(self.topWTracks[i]) , capSpacing , self.bbWidth - capSpacing ) if self.isVH: h = Horizontal.create( self.topTracks[i] , metal3 , self.getTopTrackY(i) + self.getHorizontalAxis (self.topWTracks[i]) , self.wire3Width + self.getHorizontalWidth(self.topWTracks[i]) , capSpacing , self.bbWidth - capSpacing ) NetExternalComponents.setExternal( h ) trackNb = 0 if self.botTracks: trackNb = len(self.botTracks) for i in range(trackNb): if not self.botTracks[i]: continue h = Horizontal.create( self.botTracks[i] , metal2 , self.getBotTrackY(i) + self.getHorizontalAxis (self.botWTracks[i]) , self.wire2Width + self.getHorizontalWidth(self.botWTracks[i]) , capSpacing , self.bbWidth - capSpacing ) if self.isVH: h = Horizontal.create( self.botTracks[i] , metal3 , self.getBotTrackY(i) + self.getHorizontalAxis (self.botWTracks[i]) , self.wire3Width + self.getHorizontalWidth(self.botWTracks[i]) , capSpacing , self.bbWidth - capSpacing ) NetExternalComponents.setExternal( h ) for bulk in self.bulks: if bulk: bulk.doLayout() UpdateSession.close() trace( 100, '\tStack.doLayout(): End time %s\n' %str(datetime.datetime.now()) ) trace( 100, '-' ) return self.boundingBox def drawActive ( self ): trace( 100, '+', '\tDrawActive().\n' ) activeNet = self.device.getNet( 'active' ) if not activeNet: activeNet = Net.create( self.device, 'active' ) activeNet.setAutomatic( True ) tImplantNet = self.device.getNet( 'nImplant' ) if not tImplantNet: tImplantNet = Net.create( self.device, 'nImplant' ) tImplantNet.setAutomatic( True ) active = DataBase.getDB().getTechnology().getLayer( 'active' ) width = self.w length = (self.NFs - 1) * self.gatePitch + 2*self.sideActiveWidth axis = width / 2 xoffset = self.activeOffsetX yoffset = self.activeOffsetY segment = Horizontal.create( activeNet, active, yoffset+axis, width, xoffset, xoffset+length ) width = width + 2*self.minEnclosure_tImplant_active length = length + 2*self.minEnclosure_tImplant_active axis = width / 2 xoffset = self.activeOffsetX - self.minEnclosure_tImplant_active yoffset = self.activeOffsetY - self.minEnclosure_tImplant_active segment = Horizontal.create( tImplantNet , self.tImplantLayer , yoffset+axis , width , xoffset , xoffset+length ) return def drawWell ( self ): if self.wellLayer: trace( 100, '+', '\tDrawWell().\n' ) Pad.create( self.device.getNet('anonymous'), self.wellLayer, self.boundingBox ) return 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' ) width = self.L if wiring.isTop(): ytarget = self.getTopTrackY( wiring.topTrack ) else: ytarget = self.activeOffsetY + self.minExtension_poly_active + self.w if wiring.isBot(): ysource = self.getBotTrackY( wiring.botTrack ) else: ysource = self.activeOffsetY - self.minExtension_poly_active segment = Vertical.create( wiring.net, gate, axis, width, ysource, ytarget ) contactHeight = self.minWidth_cut0 + 2*self.minEnclosure_poly_cut0 contactWidth = max( contactHeight, self.L ) contactsNb = (contactWidth - 2*self.minEnclosure_poly_cut0) / self.gateVia1Pitch if contactsNb: contactPitch = contactWidth / contactsNb else: contactsNb = 1 contactPitch = self.L gateVia1Overlap = max( self.minEnclosure_metal1_cut0, self.minEnclosure_metal1_cut1 ) gateVia1Side = max( self.minWidth_cut1, self.minWidth_cut0 ) for connector in ( (wiring.isTop(),ytarget,True), (wiring.isBot(),ysource,False) ): if not connector[0]: continue isTopConnect = connector[2] yoffset = connector[1] xcontact = axis - self.L/2 + contactPitch/2 contactBb = Box( axis, yoffset ).inflate( contactWidth/2, contactHeight/2) width = gateVia1Side + 2*gateVia1Overlap + self.getWiringWidth(wiring, isTopConnect) y = yoffset + self.getWiringWidth(wiring, isTopConnect)/2 rowHeight = self.horPitch if isTopConnect: contactBb = Box( axis, yoffset ).inflate( contactWidth/2, contactHeight/2+self.getWiringWidth(wiring, isTopConnect)) Pad.create( wiring.net, gate, contactBb ) # GateExtension Contact Horizontal.create( wiring.net , metal1 , y , width , xcontact - gateVia1Side/2 - gateVia1Overlap , xcontact + (contactsNb-1)*contactPitch + gateVia1Side/2 + gateVia1Overlap )# M1 area cut1Bb = Box() cut2Bb = Box() for i in range(contactsNb): if isTopConnect: rangeWidth = range(self.topWTracks[wiring.topTrack]) else: rangeWidth = range(self.botWTracks[wiring.botTrack]) for j in rangeWidth: contactBb = Box( xcontact, yoffset ).inflate( self.minWidth_cut0/2 ) Pad.create( wiring.net, cut0, contactBb ) contactBb = Box( xcontact, yoffset ).inflate( self.minWidth_cut1/2 ) Pad.create( wiring.net, cut1, contactBb ) cut1Bb.merge( contactBb ) if self.isVH: contactBb = Box( xcontact, yoffset ).inflate( self.minWidth_cut2/2 ) Pad.create( wiring.net, cut2, contactBb ) cut2Bb.merge( contactBb ) yoffset += rowHeight yoffset -= rowHeight*self.topWTracks[wiring.topTrack] if self.isVH: metal2EnclBb = Box( cut1Bb ).inflate( self.minEnclosure_metal2_cut1 ) metal2EnclBb.merge( Box( cut2Bb ).inflate( self.minEnclosure_metal2_cut2 ) ) Pad.create( wiring.net, metal2, metal2EnclBb ) metal3EnclBb = Box( cut2Bb ).inflate( self.minEnclosure_metal3_cut2 ) Pad.create( wiring.net, metal3, metal3EnclBb ) enable = False if wiring.net == self.bulkNet: enable = True if connector[0] & Stack.NorthBulk and self.bulks[0]: self.bulks[0].addContact( xcontact, enable ) if connector[0] & Stack.SouthBulk and self.bulks[1]: self.bulks[1].addContact( xcontact, enable ) xcontact += contactPitch return def drawSourceDrain ( self, axis, wiring, width, cols ): 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' ) rows = max( 1, (self.w - 2*self.minEnclosure_active_cut0) / self.contactDiffPitch ) ypitch = self.w / rows yoffset = self.activeOffsetY + ypitch/2 xpitch = self.contactDiffPitch ypitch2 = self.horPitch xoffset = axis - (self.contactDiffPitch * (cols - 1))/2 if self.w < 2*self.minEnclosure_active_cut0 + self.minWidth_cut0: active = DataBase.getDB().getTechnology().getLayer( 'active' ) box = Box( xoffset, yoffset, xoffset + (cols-1)*xpitch, yoffset ) box.inflate( self.minWidth_cut0 + self.minEnclosure_active_cut0 ) Pad.create( wiring.net, active, box ) box.inflate( self.minEnclosure_tImplant_active ) Pad.create( wiring.net, self.tImplantLayer, box ) for j in range(rows): for i in range(cols): contact = Contact.create( wiring.net , cut0 , xoffset + i*xpitch , yoffset + j*ypitch , self.minWidth_cut0 , self.minWidth_cut0 ) if wiring.isTop(): ytarget = self.getTopTrackY( wiring.topTrack ) for i in range(cols): for j in range(self.topWTracks[wiring.topTrack]): Contact.create( wiring.net , cut1 , xoffset + i*xpitch , ytarget + j*ypitch2 , self.minWidth_cut1 , self.minWidth_cut1 ) Contact.create( wiring.net , cut2 , xoffset + i*xpitch , ytarget + j*ypitch2 , self.minWidth_cut2 , self.minWidth_cut2 ) enable = False if wiring.net == self.bulkNet: enable = True if self.bulks[0]: self.bulks[0].addContact( xoffset + i*xpitch, enable ) if self.isVH: cutBb = Box( xoffset , ytarget , xoffset + xpitch *(cols - 1) , ytarget + ypitch2*(self.topWTracks[wiring.topTrack] - 1) ) metal2EnclBb = Box( cutBb ).inflate( self.minEnclosure_metal2_cut1 + self.minWidth_cut1/2 ) metal2EnclBb.merge( Box( cutBb ).inflate( self.minEnclosure_metal2_cut2 + self.minWidth_cut2/2 ) ) Pad.create( wiring.net, metal2, metal2EnclBb ) metal3EnclBb = Box( cutBb ).inflate( self.minEnclosure_metal3_cut2 + self.minWidth_cut2/2 ) Pad.create( wiring.net, metal3, metal3EnclBb ) ytarget += ypitch2*(self.topWTracks[wiring.topTrack]-1) else: ytarget = yoffset + ypitch*(rows - 1) if wiring.isBot(): ysource = self.getBotTrackY( wiring.botTrack ) for i in range(cols): for j in range(self.botWTracks[wiring.botTrack]): Contact.create( wiring.net , cut1 , xoffset + i*xpitch , ysource + j*ypitch2 , self.minWidth_cut1 , self.minWidth_cut1 ) Contact.create( wiring.net , cut2 , xoffset + i*xpitch , ysource + j*ypitch2 , self.minWidth_cut2 , self.minWidth_cut2 ) if self.bulks[1]: if self.isBotTrack(wiring.net) or wiring.net == self.bulkNet: enable = False if wiring.net == self.bulkNet: enable = True self.bulks[1].addContact( xoffset + i*xpitch, enable ) if self.isVH: cutBb = Box( xoffset , ysource , xoffset + xpitch *(cols - 1) , ysource + ypitch2*(self.botWTracks[wiring.botTrack] - 1) ) metal2EnclBb = Box( cutBb ).inflate( self.minEnclosure_metal2_cut1 + self.minWidth_cut1/2 ) metal2EnclBb.merge( Box( cutBb ).inflate( self.minEnclosure_metal2_cut2 + self.minWidth_cut2/2 ) ) Pad.create( wiring.net, metal2, metal2EnclBb ) metal3EnclBb = Box( cutBb ).inflate( self.minEnclosure_metal3_cut2 + self.minWidth_cut2/2 ) Pad.create( wiring.net, metal3, metal3EnclBb ) else: ysource = yoffset bcontact = Contact.create( wiring.net , metal1 , axis , ysource , width , self.wire1Width ) tcontact = Contact.create( wiring.net , metal1 , axis , ytarget , width , self.wire1Width ) segment = Vertical.create( bcontact, tcontact, metal1, axis, width ) return def computeStressEffect ( self, mt ): # Stress effect computation as specified in S. Youssef thesis, p.59. eastBulkWidth = 0 westBulkWidth = 0 if self.flags & Stack.WestBulk: westBulkWidth = self.bulkWidth if self.flags & Stack.EastBulk: eastBulkWidth = self.bulkWidth L = oroshi.toUnity( self.L ) sumA_Bsim4 = 0.0 sumB_Bsim4 = 0.0 sumA_Crolles = 0.0 sumB_Crolles = 0.0 for i in range(self.NFs): if not self.wirings[i].net != mt['gate']: continue sa = self.DGI + westBulkWidth + i *(L + self.DGG) sb = self.DGI + eastBulkWidth + (self.NFs - i - 1)*(L + self.DGG) sumA_Bsim4 += 1 / (sa + 0.5*self.DGG) sumB_Bsim4 += 1 / (sb + 0.5*self.DGG) sumA_Crolles += 1 / sa sumB_Crolles += 1 / sb SAinv_Bsim4 = sumA_Bsim4 / mt['NF'] SBinv_Bsim4 = sumB_Bsim4 / mt['NF'] SAinv_Crolles = sumA_Crolles / mt['NF'] SBinv_Crolles = sumB_Crolles / mt['NF'] mt['stress.SAinv_Bsim4'] = SAinv_Bsim4 mt['stress.SBinv_Bsim4'] = SBinv_Bsim4 mt['stress.SAeff_Bsim4'] = 1 / SAinv_Bsim4 mt['stress.SBeff_Bsim4'] = 1 / SBinv_Bsim4 mt['stress.LODeffect' ] = 1 / SAinv_Bsim4 + 1 / SBinv_Bsim4 mt['stress.alpha' ] = 2 / ( SAinv_Bsim4 + SBinv_Bsim4 ) mt['stress.alphaInv' ] = 1 / mt['stress.alpha'] mt['stress.SAinv_Crolles' ] = SAinv_Crolles mt['stress.SBinv_Crolles' ] = SBinv_Crolles mt['stress.SAeff_Crolles' ] = 1 / SAinv_Crolles mt['stress.SBeff_Crolles' ] = 1 / SBinv_Crolles mt['stress.po2actEff_Crolles'] = 2 / ( SAinv_Crolles + SBinv_Crolles ) return def computeLayoutParasitics ( self, mt ): #trace( 100, '\tStack.computeLayoutParasitics(): %s\n' % str(mt) ) NSend = mt['style.NSend'] NDend = mt['style.NDend'] NSint = mt['style.NSint'] NDint = mt['style.NDint'] NF = mt['NF'] Weff = oroshi.toUnity( self.w ) DMCI = self.DMCI DMCG = self.DMCG DMCGT = self.DMCGT if mt['style.geomod'] == 0: ASeff = NSend * ( DMCG + DMCI - DMCGT) * Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend * ( 2*(DMCG + DMCI - DMCGT) + Weff) + NSint * 2*(DMCG - DMCGT) ADeff = NDend * ( DMCG + DMCI - DMCGT) * Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * ( 2*(DMCG + DMCI - DMCGT) + Weff) + NDint * 2*(DMCG - DMCGT) elif mt['style.geomod'] == 1: ASeff = NSend * (DMCG + DMCI - DMCGT) * Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend * ( 2*(DMCG + DMCI - DMCGT) + Weff) + NSint *2*(DMCG - DMCGT) ADeff = NDend * (DMCG - DMCGT)*Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * 2*(DMCG - DMCGT) + NDint *2*(DMCG - DMCGT) elif mt['style.geomod'] == 2: ASeff = NSend * (DMCG - DMCGT) * Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend * 2 * (DMCG - DMCGT) + NSint *2*(DMCG - DMCGT) ADeff = NDend * (DMCG + DMCI - DMCGT) * Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * ( 2*(DMCG + DMCI - DMCGT) + Weff) + NDint *2*(DMCG - DMCGT) elif mt['style.geomod'] == 3: ASeff = NSend *(DMCG - DMCGT) * Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend *2*(DMCG - DMCGT) + NSint *2*(DMCG - DMCGT) ADeff = NDend *(DMCG + DMCI - DMCGT) * Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * ( 2*(DMCG + DMCI - DMCGT) + Weff) + NDint *2*(DMCG - DMCGT) elif mt['style.geomod'] == 4: ASeff = NSend * (DMCG + DMCI - DMCGT) * Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend * ( 2*(DMCG + DMCI - DMCGT) * Weff) + NSint *2*(DMCG - DMCGT) ADeff = NDend * (DMDG - DMCGT)*Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * 2*(DMDG - DMCGT) + NDint *2*(DMCG - DMCGT) elif mt['style.geomod'] == 5: ASeff = NSend *(DMCG - DMCGT)*Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend * 2 *(DMCG - DMCGT) + NSint*2*(DMCG - DMCGT) ADeff = NDend *(DMDG - DMCGT) * Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * ( 2*(DMDG - DMCGT) * Weff) + NDint*2*(DMCG - DMCGT) elif mt['style.geomod'] == 5: ASeff = NSend *(DMDG - DMCGT)*Weff + NSint *(DMDG - DMCGT)*Weff PSeff = NSend *2*(DMCG - DMCGT) + NSint*2*(DMCG - DMCGT) ADeff = NDend *(DMCG + DMCI - DMCGT) * Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * ( 2*(DMCG + DMCI - DMCGT) * Weff) + NDint*2*(DMCG - DMCGT) elif mt['style.geomod'] == 6: ASeff = NSend *(DMDG - DMCGT)*Weff + NSint *(DMDG - DMCGT)*Weff PSeff = NSend *2*(DMCG - DMCGT) + NSint*2*(DMCG - DMCGT) ADeff = NDend *(DMCG + DMCI - DMCGT) * Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend * ( 2*(DMCG + DMCI - DMCGT) * Weff) + NDint*2*(DMCG - DMCGT) elif mt['style.geomod'] == 7: ASeff = NSend *(DMDG - DMCGT)*Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend *2*(DMDG - DMCGT) + NSint*2*(DMCG - DMCGT) ADeff = NDend *(DMCG - DMCGT)*Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend *2*(DMCG - DMCGT) + NDint*2*(DMCG - DMCGT) elif mt['style.geomod'] == 8: ASeff = NSend *(DMDG - DMCGT)*Weff + NSint *(DMCG - DMCGT)*Weff PSeff = NSend *2*(DMDG - DMCGT) + NSint*2*(DMCG - DMCGT) ADeff = NDend *(DMDG - DMCGT)*Weff + NDint *(DMCG - DMCGT)*Weff PDeff = NDend *2*(DMDG - DMCGT) + NDint*2*(DMCG - DMCGT) elif mt['style.geomod'] == 9: ASeff = (DMCG + DMCI - DMCGT) * Weff + (NF-1) *(DMCG - DMCGT)*Weff PSeff = (2*(DMCG + DMCI - DMCGT) + Weff) + (NF-1)*2*(DMCG - DMCGT) ADeff = NF *(DMCG - DMCGT)*Weff PDeff = NF* 2*(DMCG - DMCGT) elif mt['style.geomod'] == 10: ASeff = NF *(DMCG - DMCGT)*Weff PSeff = NF* 2*(DMCG - DMCGT) ADeff = (DMCG + DMCI - DMCGT) * Weff + (NF-1) *(DMCG - DMCGT)*Weff PDeff = (2*(DMCG + DMCI - DMCGT) + Weff) + (NF-1)*2*(DMCG - DMCGT) else: raise Error( 3, 'Stack.computeLayoutParasitics(): Unknown GEOMOD %s for meta-transistor \"%s\".' % ( mt['style.geomod'], mt['gate'].getName() ) ) mt['parasitics.ASeff'] = ASeff mt['parasitics.PSeff'] = PSeff mt['parasitics.ADeff'] = ADeff mt['parasitics.PDeff'] = PDeff return