# -*- coding: utf-8 -*- import copy import datetime from ..Hurricane import DataBase, UpdateSession, DbU, Box, Net, \ Horizontal, Vertical, Contact, Pad, \ NetExternalComponents from ..CRL import AllianceFramework from .. import Constant from ..helpers.io import ErrorMessage as Error from ..helpers import setTraceLevel, trace from ..Analog import Device from . import getRules, rules, dtr, toUnity, adjustOnGrid #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 = rules.getRealLayer( 'active' ) metal1 = rules.getRealLayer( 'metal1' ) metal2 = rules.getRealLayer( 'metal2' ) metal3 = rules.getRealLayer( 'metal3' ) cut0 = rules.getRealLayer( 'cut0' ) cut1 = rules.getRealLayer( 'cut1' ) cut2 = rules.getRealLayer( '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 = getRules() def __setattr__ ( self, attribute, value ): if hasattr(Stack.rules,attribute): print( '[ERROR] Stack.{} attribute is read-only (ignored).'.format(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 attribute in self.__dict__: 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 {}, NF must be odd ({})'.format(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 = adjustOnGrid(device.getW() // device.getM()) self.L = 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 = rules.getRealLayer( 'nImplant' ) self.bImplantLayer = rules.getRealLayer( 'pImplant' ) self.wellLayer = None else: self.tImplantLayer = rules.getRealLayer( 'pImplant' ) self.bImplantLayer = rules.getRealLayer( 'nImplant' ) self.wellLayer = rules.getRealLayer( '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 gateName in self.metaTransistors: 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 = toUnity( self.sideActiveWidth - self.L//2 - self.metal1ToGate - self.eDiffMetal1Width//2 ) self.DMCG = toUnity( (self.gatePitch - self.L)//2 ) self.DMCGT = 0 self.DGG = toUnity( self.gatePitch - self.L ) self.DGI = 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 = rules.getRealLayer( 'metal2' ) metal3 = rules.getRealLayer( '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 = rules.getRealLayer( '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 = rules.getRealLayer( 'poly' ) cut0 = rules.getRealLayer( 'cut0' ) cut1 = rules.getRealLayer( 'cut1' ) cut2 = rules.getRealLayer( 'cut2' ) metal1 = rules.getRealLayer( 'metal1' ) metal2 = rules.getRealLayer( 'metal2' ) metal3 = rules.getRealLayer( '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 = rules.getRealLayer( 'metal1' ) metal2 = rules.getRealLayer( 'metal2' ) metal3 = rules.getRealLayer( 'metal3' ) cut0 = rules.getRealLayer( 'cut0' ) cut1 = rules.getRealLayer( 'cut1' ) cut2 = rules.getRealLayer( '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 = rules.getRealLayer( '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 = 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 = 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