737 lines
40 KiB
Python
737 lines
40 KiB
Python
#!/usr/bin/python
|
|
|
|
import sys
|
|
import numpy
|
|
from math import sqrt, ceil
|
|
from ..Hurricane import *
|
|
from ..CRL import *
|
|
from ..helpers.io import ErrorMessage as Error
|
|
from ..helpers import trace
|
|
from . import getRules
|
|
from .capacitorunit import CapacitorUnit
|
|
from .capacitormatrix import CapacitorStack
|
|
|
|
|
|
## Route a compact or a matrix of capacitors by connecting it to routing tracks.
|
|
# For a fixed instance, only one type of capacitor is supported at a time,
|
|
# either the Poly-Poly type or Metal-Metal in 350 nm AMS CMOS technology.
|
|
#
|
|
# The dummy mode is also supported.
|
|
# The dummyRing mode is not yet supported.
|
|
|
|
def toDbU ( l ): return DbU.fromPhysical( l, DbU.UnitPowerMicro )
|
|
def toPhY ( l ): return DbU.toPhysical ( l, DbU.UnitPowerMicro )
|
|
|
|
def doBreak( level, message ):
|
|
UpdateSession.close()
|
|
Breakpoint.stop( level, message )
|
|
UpdateSession.open()
|
|
|
|
|
|
class RouteCapacitorSingle ( CapacitorUnit ):
|
|
|
|
rules = getRules()
|
|
|
|
## The constructor computes some of the class attributes and initialises others which
|
|
# will be computed later inside some of the class methods.
|
|
# \param device The Hurricane AMS device into which the layout is drawn.
|
|
# \param capacitorInstance Instance of the class CapacitorMatrix.
|
|
# \param capacitor A nested list containing a compact capacitor
|
|
# (in the first element) or elementary capacitors of
|
|
# a matrix of capacitors (each row in a list).
|
|
# \param dummyMode is \c "True" when dummy capacitor is drawn.
|
|
# \param capacitorType Can be MIM or PIP type capacitor.
|
|
# \param abutmentBox The abutment box of the compact or matrix capacitor.
|
|
# \param routingTrackYCenter A nested dictionary containing the ordinates of top and
|
|
# bottom ( including upper and lower) routing tracks.
|
|
# \param xPlateRLayerXCenter A nested dictionary containing
|
|
# \param xPlateRLayer_width A dictionary containing the widths of the top and bottom
|
|
# plates routing layers.
|
|
# \param routingTrack_width The width of a routing track. Is fixed according to
|
|
# technological parameters.
|
|
# \param tracksNumbers A dictionary containing the number of top and bottom tracks.
|
|
# The allowed maximum total number of tracks is two.
|
|
# \param topPlateWSpec Routing specifications for the top plates.
|
|
# \param bottomPlateWSpec Routing specifications for the bottom plates.
|
|
#
|
|
# \remarks An exception is raised if the entered routing specifications are invalid.
|
|
# Invalidity can be due to a wrong total number of tracks or bad wiring
|
|
# specifications of top and bottom tracks. More information about the valid
|
|
# specifications are given in \c function.
|
|
|
|
def __init__ ( self, capacitorInstance, capacitor, dummyMode = False, tracksNumbers = [1,1], topPlateWSpec = [1,0] , bottomPlateWSpec = [0,1] ):
|
|
|
|
if capacitorInstance.matchingMode:
|
|
raise Error( 1, [ 'RouteCapacitorSingle.__init__(): This class is only dedicated to route matrices equivalent to one capacitor.'
|
|
, 'In case of a matched matrix of capacitors, please use <RoutMatchedCapacitor>.' ] )
|
|
|
|
self.device = capacitorInstance.device
|
|
self.capacitorInstance = capacitorInstance
|
|
self.capacitor = capacitor
|
|
self.dummyMode = dummyMode
|
|
self.capacitorType = capacitorInstance.capacitorType
|
|
self.matrixDim = capacitorInstance.matrixDim
|
|
self.abutmentBox = capacitorInstance.abutmentBox
|
|
self.abutmentBox_spacing = self.capacitorInstance.getAbutmentBox_spacing ()
|
|
self.bondingBox = Box()
|
|
self.capacitorType = capacitorInstance.capacitorType
|
|
self.nets = capacitorInstance.nets[0]
|
|
|
|
self.routingTracksXMinXMax = {}
|
|
self.routingTrackYCenter = { "top": {}, "bottom": {} }
|
|
self.xPlateRLayerXCenter = { "top": [], "bottom": [], "bottomBorders" : [] }
|
|
self.xPlateRLayer_width = {}
|
|
self.routingTrack_width = 0
|
|
|
|
if ( self.__isRoutingTracksNumberOK__( tracksNumbers ) ):
|
|
|
|
self.tracksNumbers = { "top" : tracksNumbers[0] ,"bottom" : tracksNumbers[1] }
|
|
possibleRoutingSchemes = self.__setPossibleRoutingSchemes__( tracksNumbers )
|
|
|
|
if dummyMode == True :
|
|
|
|
if self.connectToTopTracksOnly() :
|
|
self.topPlateWSpec = [1,0]
|
|
|
|
elif self.connectToBottomTracksOnly() :
|
|
self.topPlateWSpec = [0,1]
|
|
|
|
else :
|
|
self.topPlateWSpec = [1,1]
|
|
|
|
self.bottomPlateWSpec = self.topPlateWSpec
|
|
|
|
elif ( dummyMode == False and self.__isWiringSpecOK__(topPlateWSpec, bottomPlateWSpec, possibleRoutingSchemes) ) :
|
|
self.topPlateWSpec = topPlateWSpec
|
|
self.bottomPlateWSpec = bottomPlateWSpec
|
|
|
|
else : raise Error(1,'__init__() : Invalid routing specifications in terms of number, format or routing scheme.')
|
|
|
|
else : raise Error(1,' __init__() : One routing track on one or both sides of the capacitor is allowed in dummy mode. Otherwise, routing tracks can be drawn one on each side or two on one side : (top trak number : %s, bottom track number : %s and dummy mode is %s). ' %( tracksNumbers[0], tracksNumbers[1], dummyMode )) #com verify the difference between a simple print and the dicitionary self.trackingNumber
|
|
|
|
return
|
|
|
|
## Using functions calls and the entered parameters, This function draws the routing tracks, then, connects the compact or matrix of capacitors to routing tracks using routing layers and adequate cuts. \c rout function also initialises: cuts layers according to :
|
|
# - the capacitor type (ie., cuts2 if MIMCAP, cut1 if PIPCAP )
|
|
# - routing tracks layers according to the designer specifications.
|
|
|
|
def route ( self, bbMode=False ):
|
|
UpdateSession.open()
|
|
|
|
bondingBox = {}
|
|
self.setRules ()
|
|
self.computeDimensions( bbMode )
|
|
|
|
if bbMode :
|
|
bondingBox = self.computeLayoutDimensionsInbbMode()
|
|
|
|
if not bbMode:
|
|
routingTracksLayer = DataBase.getDB().getTechnology().getLayer("metal2")
|
|
bottomPlateRLayer = CapacitorUnit.getLayers( self )["bottomPlateRLayer"]
|
|
topPlateRLayer = bottomPlateRLayer
|
|
|
|
if self.capacitorType == 'MIMCap':
|
|
topbottomCutLayer = DataBase.getDB().getTechnology().getLayer("cut2")
|
|
elif self.capacitorType == 'PIPCap':
|
|
topbottomCutLayer = DataBase.getDB().getTechnology().getLayer("cut1")
|
|
else:
|
|
raise Error( 1, 'RouteCapacitorSingle.route(): Unsupported capacitor type "{0}".'.format( self.capacitorType ))
|
|
|
|
self.drawRoutingTracks ( routingTracksLayer )
|
|
self.drawPlatesVRLayers( 'topPlate' , topPlateRLayer , self.topPlateWSpec , self.xPlateRLayerXCenter["top" ], self.xPlateRLayer_width["top" ] )
|
|
self.drawPlatesVRLayers( 'bottomPlate' , bottomPlateRLayer , self.bottomPlateWSpec, self.xPlateRLayerXCenter["bottom"], self.xPlateRLayer_width["bottom" ] )
|
|
self.drawCuts ( self.nets[0], 'topPlate' , self.topPlateWSpec , topbottomCutLayer , self.xPlateRLayer_width ["top" ], self.xPlateRLayerXCenter["top" ] )
|
|
self.drawCuts ( self.nets[1], 'bottomPlate', self.bottomPlateWSpec, topbottomCutLayer , self.xPlateRLayer_width ["bottom"], self.xPlateRLayerXCenter["bottom"] )
|
|
|
|
ab = self.device.getAbutmentBox()
|
|
trace( 101, '\tAB before tracks: {0}\n'.format( ab ))
|
|
trace( 101, '\tBottom track numbers: {0}\n'.format( self.tracksNumbers['bottom'] ))
|
|
dyTop = self.tracksNumbers['top' ] * self.hpitch
|
|
dyBot = self.tracksNumbers['bottom'] * self.hpitch
|
|
self.device.setAbutmentBox( ab.inflate( 0, dyBot, 0, dyTop ) )
|
|
trace( 101, '\tAB after tracks: {0}\n'.format( ab ))
|
|
|
|
UpdateSession.close()
|
|
|
|
return bondingBox
|
|
|
|
|
|
|
|
## Builds and returns a dictionary of the needed technological rules to route
|
|
# the capacitor.
|
|
# The significant rules are :
|
|
# - the minimum spacing between the routing tracks according to their metal layer,
|
|
# - the minimum width of a plate, a cut or a routing metal,
|
|
# - the minimum width, height and spacing between the cuts on the routing track.
|
|
# - etc.
|
|
#
|
|
# At the exception of the minimum spacing between routing tracks, every rule has
|
|
# two possible values according to the capacitor type.
|
|
# \remarks An exception is raised if the entered capacitor type is unsupported.
|
|
|
|
def setRules ( self ):
|
|
CapacitorUnit.setRules( self )
|
|
self.minSpacing_routingTrackMetal = RouteCapacitorSingle.rules.minSpacing_metal2
|
|
|
|
if self.capacitorType == 'MIMCap' :
|
|
self.minHeight_routingTrackcut = RouteCapacitorSingle.rules.minWidth_cut2
|
|
self.minSpacing_routingTrackcut = RouteCapacitorSingle.rules.minSpacing_cut2
|
|
self.minWidth_routingTrackcut = RouteCapacitorSingle.rules.minWidth_cut2
|
|
elif self.capacitorType == 'PIPCap':
|
|
self.minHeight_routingTrackcut = RouteCapacitorSingle.rules.minWidth_cut1
|
|
self.minSpacing_routingTrackcut = RouteCapacitorSingle.rules.minSpacing_cut1
|
|
self.minWidth_routingTrackcut = RouteCapacitorSingle.rules.minWidth_cut1
|
|
|
|
return
|
|
|
|
|
|
## Checks if the wiring specifications are compatible with the possible routing schemes.
|
|
# \return \c "True" if all conditions are satisfied.
|
|
# \param topWiringSpec The desired connection of the top plate.
|
|
# \param bottomWiringSpec The desired connection of the top plate.
|
|
# \param possibleRoutingSchemes A list of the possible connections computed according
|
|
# to routing tracks specifications.
|
|
|
|
def __isWiringSpecOK__ ( self, topPlateWiringSpec, bottomPlateWiringSpec, possibleRoutingSchemes ):
|
|
|
|
state = False
|
|
if len(topPlateWiringSpec ) == len( bottomPlateWiringSpec ) == 2 and [ topPlateWiringSpec, bottomPlateWiringSpec ] in possibleRoutingSchemes :
|
|
state = True
|
|
|
|
else : raise Error(1, '__isWiringSpecOK__() : Invalid wiring specifications for top and/or bottom plates, %s and/or %s, respectively. The possible wiring schemes are : %s. ' %(topPlateWiringSpec, bottomPlateWiringSpec, possibleRoutingSchemes) )
|
|
|
|
return state
|
|
|
|
|
|
|
|
## Builds and retuns a list containing the possible routing schemes according to routing tracks specifications. All the possibilities are summarized in Table 1.
|
|
# \param topTrackNumber The specified number of top routing tracks. Two tracks are defined : the lower and upper ones. Considering a maximum number of top and bottom tracks equal to two, \c topTracksNumber takes 0, 1, 2 when \c bottomTrackNumber is equal to 2, 1, 0, respectively.
|
|
# \param bottomTrackNumber The specified number of bottom routing tracks. The same rules and specifications as top tracks apply to bottom tracks.
|
|
# \param possibleRoutingSchemes A list of the possible connections computed according to routing tracks specifications.
|
|
|
|
def __setPossibleRoutingSchemes__( self, tracksNumbers ):
|
|
|
|
[topTrackNumber , bottomTrackNumber] = [tracksNumbers[0] , tracksNumbers[1]]
|
|
possibleRoutingSchemes = None
|
|
|
|
if self.dummyMode == False: possibleRoutingSchemes = [ [[0,1],[1,0]], [[1,0],[0,1]] ]
|
|
|
|
return possibleRoutingSchemes
|
|
|
|
|
|
|
|
## Checks if the tracks numbers are valid. The requirements which must be satifsied are :
|
|
# - A maximum number of total tracks equal to two (ie., the sum of bottom tracks number and top tracks number must be equal to 2).
|
|
# - The specified numbers for top and bottom tracks belong to the set \c {0,1,2}. Therefore, if the sum is equal to two and one or both numbers are not in {0,1,2}, this is considered as invalid.
|
|
# \return \c "True" if all conditions are satisfied.
|
|
# \param topTrackNumber The specified number for top tracks.
|
|
# \param topTrackNumber The specified number for bottom tracks.
|
|
# \throw <object> Wrong values for routing tracks
|
|
def __isRoutingTracksNumberOK__ ( self, tracksNumbers ):
|
|
|
|
[topTrackNumber , bottomTrackNumber] = [tracksNumbers[0] , tracksNumbers[1]]
|
|
state = False
|
|
if self.dummyMode == True:
|
|
if ( bottomTrackNumber == 1 and topTrackNumber == 0 or bottomTrackNumber == 0 and topTrackNumber == 1 or bottomTrackNumber == 1 and topTrackNumber == 1 ) :
|
|
state = True
|
|
|
|
elif self.dummyMode == False:
|
|
if ( bottomTrackNumber == 1 and topTrackNumber == 1 or bottomTrackNumber == 0 and topTrackNumber == 2 or bottomTrackNumber == 2 and topTrackNumber == 0 ) :
|
|
state = True
|
|
|
|
else : raise Error( 1, '__isRoutingTracksNumberOK__() : Wrong values for routing tracks "%s , %s". ' %(bottomTrackNumber, topTrackNumber) ) #com test this
|
|
|
|
return state
|
|
|
|
|
|
|
|
## Computes relevant attributes (layout parameters) by calling two functions to compute properties and dimensions of :
|
|
# - the routing layers of top and bottom plates
|
|
# - the routing tracks
|
|
def computeDimensions( self, bbMode ):
|
|
|
|
self.computeRLayersDimensions( bbMode )
|
|
|
|
routingTracksLayer = DataBase.getDB().getTechnology().getLayer("metal2")
|
|
bottomPlateRLayer = CapacitorUnit.getLayers( self )["bottomPlateRLayer"]
|
|
topPlateRLayer = bottomPlateRLayer
|
|
|
|
ab = self.device.getAbutmentBox()
|
|
#plateSpacing1 = self.minSpacing_botPlate
|
|
#plateSpacing2 = self.hpitch - (self.metal2Width + self.routingTrack_width)/2
|
|
#plateSpacing = max( plateSpacing1, plateSpacing2 ) + self.metal2Width/2
|
|
plateSpacing = self.minSpacing_botPlate + self.metal2Width/2
|
|
trace( 101, '\tAB height before plate inflate: {0}\n'.format( DbU.getValueString(ab.getHeight()) ))
|
|
trace( 101, '\tminSpacing_botPlate: {0}\n'.format( DbU.getValueString(self.minSpacing_botPlate) ))
|
|
trace( 101, '\tplateSpacing: {0}\n'.format( DbU.getValueString(plateSpacing) ))
|
|
ab.inflate( 0, plateSpacing )
|
|
self.device.setAbutmentBox( ab )
|
|
trace( 101, '\tAB height after plate inflate: {0} {1}\n'.format( DbU.getValueString(ab.getHeight()), ab.getHeight() ))
|
|
|
|
height = ab.getHeight()
|
|
heightAdjust = height % (2*self.hpitch)
|
|
if heightAdjust:
|
|
trace( 101, '\tAB height before V-adjust: {0}\n'.format( DbU.getValueString(ab.getHeight()) ))
|
|
heightAdjust = 2*self.hpitch - heightAdjust
|
|
trace( 101, '\tAB height adjust: {0}\n'.format( DbU.getValueString(heightAdjust) ))
|
|
self.device.setAbutmentBox( ab.inflate( 0, heightAdjust/2 ))
|
|
trace( 101, '\tAB after V-adjust: {0}\n'.format( ab ))
|
|
trace( 101, '\tAB height after V-adjust: {0}\n'.format( DbU.getValueString(ab.getHeight()) ))
|
|
|
|
width = ab.getWidth()
|
|
widthAdjust = width % (2*self.vpitch)
|
|
if widthAdjust:
|
|
widthAdjust = 2*self.vpitch - widthAdjust
|
|
self.device.setAbutmentBox( ab.inflate( widthAdjust/2, 0 ))
|
|
|
|
self.abutmentBox = ab
|
|
self.computeHRoutingTrackDimensions()
|
|
|
|
return
|
|
|
|
|
|
|
|
def computeRLayersDimensions ( self, bbMode ):
|
|
self.routingTrack_width = 2 * self.minEnclo_routingTrackMetal_cut \
|
|
+ self.minWidth_routingTrackcut
|
|
|
|
if not bbMode :
|
|
self.xPlateRLayer_width["top"] = self.capacitor.getTopPlateRLayerWidth() if self.capacitorInstance.matrixDim.values() == [1,1] else self.capacitor[0][0].getTopPlateRLayerWidth()
|
|
|
|
if self.capacitorInstance.__isUnitCap__() :
|
|
self.computeRLayersDimensionsCompactCap ()
|
|
else:
|
|
self.computeRLayersDimensionsMatrixCap()
|
|
return
|
|
|
|
|
|
def computeRLayersDimensionsCompactCap ( self ):
|
|
self.bordersRLayerXMin = [ self.capacitor.getBottomPlateLeftCutXMin(), self.capacitor.getBottomPlateRightCutXMin() ]
|
|
self.xPlateRLayer_width ["bottomBorders"] = self.capacitor.getBotPlateRLayerWidth ()
|
|
self.xPlateRLayer_width ["bottom" ] = []
|
|
self.xPlateRLayerXCenter["bottom" ] = []
|
|
self.xPlateRLayerXCenter["top" ].append( self.capacitor.getTopPlateRLayerXCenter () )
|
|
self.xPlateRLayerXCenter["bottomBorders"].append( self.capacitor.getBotPlateLeftRLayerXCenter () )
|
|
self.xPlateRLayerXCenter["bottomBorders"].append( self.capacitor.getBotPlateRightRLayerXCenter () )
|
|
return
|
|
|
|
|
|
def computeRLayersDimensionsMatrixCap( self ):
|
|
if self.capacitorInstance.matrixDim["columns"] > 1 :
|
|
self.xPlateRLayer_width["bottom"] = ( self.capacitor[0][1].getBotPlateLeftRLayerXMax() - self.capacitor[0][0].getBotPlateRightRLayerXMin() )
|
|
self.bordersRLayerXMin = [ self.capacitor[0][0].getBottomPlateLeftCutXMin(), self.capacitor[0][-1].getBottomPlateRightCutXMin() ]
|
|
|
|
elif self.capacitorInstance.matrixDim["columns"] == 1 :
|
|
self.bordersRLayerXMin = [ self.capacitor[0][0].getBottomPlateLeftCutXMin(), self.capacitor[0][0].getBottomPlateRightCutXMin() ]
|
|
|
|
else : raise Error( 1, 'computeRLayersDimensionsMatrixCap() : Negative number of columns in the matrix "%s".' % self.capacitorInstance.matrixDim["columns"] )
|
|
|
|
self.xPlateRLayer_width["bottomBorders"] = self.capacitor[0][0].getBotPlateRLayerWidth()
|
|
|
|
for i in range( 0, self.matrixDim["columns"] ):
|
|
self.xPlateRLayerXCenter["top"].append( self.capacitor[0][i].getTopPlateRLayerXCenter() )
|
|
for i in range( 0,self.matrixDim["columns"] -1 ):
|
|
self.xPlateRLayerXCenter["bottom"].append( self.capacitor[0][i].getBotPlateRightRLayerXMin() + self.xPlateRLayer_width["bottom"]/2 )
|
|
|
|
if self.capacitorInstance.matrixDim["columns"] > 1 :
|
|
self.xPlateRLayerXCenter["bottomBorders"].append( self.capacitor[0][ 0].getBotPlateLeftRLayerXCenter () )
|
|
self.xPlateRLayerXCenter["bottomBorders"].append( self.capacitor[0][-1].getBotPlateRightRLayerXCenter() )
|
|
else :
|
|
self.xPlateRLayerXCenter["bottomBorders"].append( self.capacitor[0][0].getBotPlateLeftRLayerXCenter () )
|
|
self.xPlateRLayerXCenter["bottomBorders"].append( self.capacitor[0][0].getBotPlateRightRLayerXCenter () )
|
|
|
|
|
|
return
|
|
|
|
## Actual function that computes routing tracks dimensions and positions.
|
|
|
|
def computeHRoutingTrackDimensions( self ):
|
|
trace( 101, ',+', '\tcomputeHRoutingTrackDimensions(): ab {0}\n'.format(self.device.getAbutmentBox()) )
|
|
|
|
self.routingTracksXMinXMax = { "XMin" : self.abutmentBox.getXMin()
|
|
, "XMax" : self.abutmentBox.getXMax() }
|
|
|
|
if self.dummyMode:
|
|
if self.connectToTopTracksOnly():
|
|
yTL = self.abutmentBox.getYMax()
|
|
|
|
self.routingTrackYCenter["top"]["lower"] = yTL
|
|
|
|
self.topLowerTrackDict = { "YMin" : yTL - self.routingTrack_width/2
|
|
, "YMax" : yTL + self.routingTrack_width/2 }
|
|
|
|
elif self.connectToBottomTracksOnly():
|
|
yBU = self.abutmentBox.getYMin()
|
|
|
|
self.routingTrackYCenter["bottom"]["upper"] = yBU
|
|
|
|
self.bottomUpperTrackDict = { "YMin" : yBU - self.routingTrack_width/2
|
|
, "YMax" : yBU + self.routingTrack_width/2 }
|
|
else:
|
|
yTL = self.abutmentBox.getYMax()
|
|
yBU = self.abutmentBox.getYMin()
|
|
|
|
self.routingTrackYCenter["top" ]["lower"] = yTL
|
|
self.routingTrackYCenter["bottom"]["upper"] = yBU
|
|
|
|
self.topLowerTrackDict = { "YMin" : yTL - self.routingTrack_width/2
|
|
, "YMax" : yTL + self.routingTrack_width/2 }
|
|
self.bottomUpperTrackDict = { "YMin" : yBU - self.routingTrack_width/2
|
|
, "YMax" : yBU + self.routingTrack_width/2 }
|
|
|
|
else:
|
|
if self.connectToTopTracksOnly() :
|
|
yTL = self.abutmentBox.getYMax()
|
|
yTU = yTL + self.hpitch
|
|
|
|
self.routingTrackYCenter["top"]["lower"] = yTL
|
|
self.routingTrackYCenter["top"]["upper"] = yTU
|
|
|
|
self.topLowerTrackDict = { "YMin" : yTL - self.routingTrack_width/2
|
|
, "YMax" : yTL + self.routingTrack_width/2 }
|
|
self.topUpperTrackDict = { "YMin" : yTU - self.routingTrack_width/2
|
|
, "YMax" : yTU + self.routingTrack_width/2}
|
|
|
|
elif self.connectToBottomTracksOnly():
|
|
yBU = self.abutmentBox.getYMin()
|
|
yBL = yBU - self.hpitch
|
|
|
|
self.routingTrackYCenter["bottom"]["upper"] = yBU
|
|
self.routingTrackYCenter["bottom"]["lower"] = yBL
|
|
|
|
self.bottomUpperTrackDict = { "YMin" : yBU - self.routingTrack_width/2
|
|
, "YMax" : yBU + self.routingTrack_width/2 }
|
|
self.bottomLowerTrackDict = { "YMin" : yBL - self.routingTrack_width/2
|
|
, "YMax" : yBL + self.routingTrack_width/2 }
|
|
|
|
elif self.connectToTopAndBottomTracks():
|
|
yTL = self.abutmentBox.getYMax()
|
|
yBU = self.abutmentBox.getYMin()
|
|
|
|
trace( 101, '\tyBU: {0}\n'.format( DbU.getValueString(yBU) ))
|
|
|
|
self.routingTrackYCenter["top" ]["lower"] = yTL
|
|
self.routingTrackYCenter["bottom"]["upper"] = yBU
|
|
|
|
self.topLowerTrackDict = { "YMin" : yTL - self.routingTrack_width/2
|
|
, "YMax" : yTL + self.routingTrack_width/2 }
|
|
self.bottomUpperTrackDict = { "YMin" : yBU - self.routingTrack_width/2
|
|
, "YMax" : yBU + self.routingTrack_width/2 }
|
|
|
|
else:
|
|
raise Error( 1, [ 'RouteCapacitorSingle.computeHRoutingTrackDimensions(): Wrong number or values for routing tracks.'
|
|
, 'Top : {0}, bottom : {1}.'.format( self.tracksNumbers["top"], self.tracksNumbers["bottom"]) ] )
|
|
|
|
trace( 101, '-' )
|
|
return
|
|
|
|
|
|
def computeLayoutDimensionsInbbMode( self ):
|
|
|
|
bondingBoxDict = {}
|
|
bondingBoxDict["width" ] = self.routingTracksXMinXMax["XMax"] - self.routingTracksXMinXMax["XMin"]
|
|
bondingBoxDict["XMin" ] = self.routingTracksXMinXMax["XMin"]
|
|
|
|
if self.dummyMode == True :
|
|
|
|
if self.connectToTopTracksOnly () :
|
|
bondingBoxDict["height" ] = self.topLowerTrackDict["YMax"] - self.abutmentBox.getYMin()
|
|
bondingBoxDict["YMin" ] = self.abutmentBox.getYMin()
|
|
|
|
elif self.connectoToBottomTracksOnly() :
|
|
bondingBoxDict["height" ] = self.abutmentBox.getYMax() - self.bottomUpperTrackDict["YMin"]
|
|
bondingBoxDict["YMin" ] = self.bottomUpperTrackDict["YMin"]
|
|
|
|
else :
|
|
bondingBoxDict["height" ] = self.topLowerTrackDict["YMax"] - self.abutmentBox.getYMin()
|
|
bondingBoxDict["YMin" ] = self.bottomUpperTrackDict["YMin"]
|
|
|
|
elif self.dummyMode == False:
|
|
|
|
if self.connectToTopTracksOnly () :
|
|
bondingBoxDict["height" ] = self.topUpperTrackDict["YMax"] - self.abutmentBox.getYMin()
|
|
bondingBoxDict["YMin" ] = self.abutmentBox.getYMin()
|
|
|
|
elif self.connectoToBottomTracksOnly() :
|
|
bondingBoxDict["height" ] = self.abutmentBox.getYMax() - self.bottomLowerTrackDict["YMin"]
|
|
bondingBoxDict["YMin" ] = self.bottomLowerTrackDict["YMin"]
|
|
|
|
else:
|
|
bondingBoxDict["height" ] = self.topLowerTrackDict["YMax"] - self.bottomUpperTrackDict["YMin"]
|
|
bondingBoxDict["YMin" ] = self.bottomUpperTrackDict["YMin"]
|
|
|
|
else : raise Error( 1, 'computeLayoutDimensionsInbbMode() : The dummy mode must be either "True" or "False", "%s". ' % self.dummyMode )
|
|
|
|
bondingBoxDict["surface"] = bondingBoxDict["width"]*bondingBoxDict["height"]
|
|
|
|
self.bondingBox = Box( bondingBoxDict["XMin"], bondingBoxDict["YMin"], bondingBoxDict["XMin"] + bondingBoxDict["width"] , bondingBoxDict["YMin"] + bondingBoxDict["height"] )
|
|
|
|
return bondingBoxDict
|
|
|
|
|
|
## Draws routing tracks, above and/or below the capacitor. A maximum total number of two tracks is drawn. In dummy mode, one track is drawn.
|
|
# \param routingTracksLayer Layer of the routing track.
|
|
# \remark All routing tracks, top and bottom (upper and lower), are drawn using the same layer.
|
|
|
|
def drawRoutingTracks( self , routingTracksLayer ):
|
|
|
|
self.drawTopOrBottomRoutingTracks ( "top" , routingTracksLayer )
|
|
self.drawTopOrBottomRoutingTracks ( "bottom", routingTracksLayer )
|
|
|
|
return
|
|
|
|
|
|
def drawTopOrBottomRoutingTracks ( self, tracksPosition, routingTracksLayer ) :
|
|
trace( 101, ',+', '\tRouteCapacitorSingle.drawTopOrBottomRoutingTracks()\n' )
|
|
if tracksPosition in ["top","bottom"] :
|
|
attribut = [ "lower", "upper" ]
|
|
nets = self.__setNetsDistributionHRTs__()
|
|
for i in range( 0, self.tracksNumbers[tracksPosition] ):
|
|
index = i if tracksPosition == "top" else i-1
|
|
netindex = i-1 if tracksPosition == "top" else i
|
|
trace( 101, '\ttrackPos={0}, index={1}, attribute={2}\n'.format( tracksPosition
|
|
, index
|
|
, attribut[index] ))
|
|
routingtrackYCenter = self.routingTrackYCenter[tracksPosition][attribut[index]]
|
|
horizontal = Horizontal.create( nets[netindex]
|
|
, routingTracksLayer
|
|
, routingtrackYCenter
|
|
, self.routingTrack_width
|
|
, self.routingTracksXMinXMax["XMin"]
|
|
, self.routingTracksXMinXMax["XMax"] )
|
|
NetExternalComponents.setExternal( horizontal )
|
|
trace( 101, '\t{0}\n'.format( horizontal ))
|
|
else:
|
|
raise Error( 1, 'RouteCapacitorSingle.drawOneRoutingTrack(): The track position must be either "top" or "bottom" ("{0}").'.format(tracksPosition))
|
|
trace( 101, '-' )
|
|
return
|
|
|
|
|
|
def __setNetsDistributionHRTs__( self ):
|
|
trace( 101, ',+', '\tRouteCapacitorSingle.__setNetsDistributionHRTs__()\n' )
|
|
if self.dummyMode:
|
|
trace( 1010, '\tDummy mode\n' )
|
|
netsDistribution = self.nets
|
|
else:
|
|
if (self.topPlateWSpec, self.bottomPlateWSpec) == ([1,0], [0,1]):
|
|
netsDistribution = self.nets
|
|
else:
|
|
netsDistribution = [ self.nets[1], self.nets[0] ]
|
|
trace( 101, '\tnetsDistribution = [ {0}, {1} ]\n'.format( netsDistribution[0].getName()
|
|
, netsDistribution[1].getName() ))
|
|
trace( 101, '-' )
|
|
return netsDistribution
|
|
|
|
|
|
|
|
## Draws the routing layers connecting top and bottom plates to the associated routing track.
|
|
# \param Plate The capacitor's plate to be routed (ie., top, bottom).
|
|
# \paramx PlateRLayer Routing layer.
|
|
# \param PlateWSpec Connection specifications of the plate.
|
|
# \param xPlateRLayerXCenter Horizontal position of the routing layer.
|
|
# \param xPlateRLayer_width Width of the routing layer.
|
|
# \throw < plate-name > \c Undefined \c plate
|
|
# \throw < specification > \c Invalid \c routing \c specifications
|
|
|
|
def drawPlatesVRLayers( self, Plate, xPlateRLayer, PlateWSpec, xPlateRLayerXCenter, xPlateRLayer_width ):
|
|
|
|
if self.capacitorInstance.__isUnitCap__() :
|
|
[firstElementInCapacitor , lastElementInCapacitor] = [self.capacitor , self.capacitor]
|
|
elif not( self.capacitorInstance.__isUnitCap__() ):
|
|
[firstElementInCapacitor , lastElementInCapacitor] = [self.capacitor[0][0] , self.capacitor[-1][0]]
|
|
|
|
[ doTop, doBottom ] = [1, 0] if Plate == 'topPlate' else [0, 1]
|
|
|
|
if ( Plate == 'topPlate' ) :
|
|
YMinDict = {
|
|
"toTopToUpper" : firstElementInCapacitor.getTopPlateRLayerYMin(),
|
|
"toTopToLower" : firstElementInCapacitor.getTopPlateRLayerYMin(),
|
|
"toBottomToUpper" : lastElementInCapacitor.getTopPlateRLayerYMax(),
|
|
"toBottomToLower" : lastElementInCapacitor.getTopPlateRLayerYMax()
|
|
}
|
|
net = self.nets[0]
|
|
xMetalXCenter = self.xPlateRLayerXCenter["top"]
|
|
|
|
elif ( Plate == 'bottomPlate' ) :
|
|
YMinDict = {
|
|
"toTopToUpper" : firstElementInCapacitor.getBotPlateRLayerYMin(),
|
|
"toTopToLower" : firstElementInCapacitor.getBotPlateRLayerYMin(),
|
|
"toBottomToUpper" : lastElementInCapacitor.getBotPlateRLayerYMax(),
|
|
"toBottomToLower" : lastElementInCapacitor.getBotPlateRLayerYMax()
|
|
}
|
|
net = self.nets[1]
|
|
xMetalXCenter = self.xPlateRLayerXCenter["bottom"]
|
|
|
|
else : raise Error( 1, 'drawPlatesVRLayers() : Undefined plate, "%s".' % Plate )
|
|
|
|
if ( self.connectToTopTracksOnly () and self.connectToUpper( PlateWSpec ) ) :
|
|
[ dySource, dyTarget ] = [ YMinDict["toTopToUpper" ] , self.topUpperTrackDict["YMax"] ]
|
|
|
|
elif ( self.connectToTopTracksOnly () and self.connectToLower( PlateWSpec ) ) or ( self.connectToTopAndBottomTracks () and self.connectToUpper( PlateWSpec ) ) :
|
|
dyTarget = self.topLowerTrackDict ["YMax"]
|
|
dySource = self.bottomUpperTrackDict["YMin"] if self.dummyMode == True and self.connectToTopAndBottomTracks () else YMinDict["toTopToLower" ]
|
|
elif ( self.connectToBottomTracksOnly() and self.connectToUpper( PlateWSpec ) ) or ( self.connectToTopAndBottomTracks () and self.connectToLower( PlateWSpec ) ) :
|
|
[ dySource, dyTarget ] = [ YMinDict["toBottomToUpper"] , self.bottomUpperTrackDict["YMin"] ]
|
|
|
|
elif ( self.connectToBottomTracksOnly() and self.connectToLower( PlateWSpec ) ) :
|
|
[ dySource, dyTarget ] = [ YMinDict["toBottomToLower"] , self.bottomLowerTrackDict["YMin"] ]
|
|
|
|
else : raise Error( 1, 'drawPlatesVRLayers() : Invalid routing specifications "%s".' % PlateWSpec )
|
|
|
|
for i in range( 0, self.matrixDim["columns"] - doBottom ):
|
|
Vertical.create ( net, xPlateRLayer, xPlateRLayerXCenter[i], xPlateRLayer_width, dySource, dyTarget )
|
|
|
|
if doBottom :
|
|
for i in range( 0,2):
|
|
Vertical.create ( self.nets[1], xPlateRLayer , self.xPlateRLayerXCenter["bottomBorders"][i], self.xPlateRLayer_width["bottomBorders"], dySource, dyTarget )
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
## Draws one or multiple cuts between a routing track and a routing layer to connect the capacitor plate to the track. The function supports cuts for top and bottom plates. First, using wiring specifications, he position of the cuts is identified. Second, the maximum nupmber of cuts is computed, then, its enclosure in the routing layer is adjusted. Third, the cuts are iteratively drawn on every intersection between the routing track and the plate's routing layer.
|
|
# \remark Since in the special case of bottom plate, routing layers on matrix borders are thinner than the intermediate ones, two extra steps are excecuted to draw cuts connecting border routing layers to the routing track. The two steps are computing the maximum number of cuts, which is lower than intermediate cuts, and ajusting its enclosure.
|
|
# \throw <Invalid-specifictions > \c Not \c possible \c to \c compute cuts vertical position due to invalid routing specifications
|
|
# \remak The number of cuts is maximized according to the track's width.
|
|
# \param net Net of the Hurricane Device to which the cuts will be connected.
|
|
# \param Plate Capacitor's plate, top or bottom.
|
|
# \param PlateWSpec Wiring specifications of the plate. It is useful to identify the track on which the cuts are to draw.
|
|
# \param layer Cut's layer.
|
|
# \param xPlateRLayer_width Width of the plate's routing layer. It is useful to compute the maximum number of cuts.
|
|
# \param xPlateRLayerXCenter Hrizontal position of the plate's routing layer, which also defines the cut's position.
|
|
|
|
def drawCuts( self, net, Plate, PlateWSpec, layer, xPlateRLayer_width, xPlateRLayerXCenter ):
|
|
|
|
[ doTop, doBottom ] = [1, 0] if Plate == 'topPlate' else [0, 1]
|
|
cutsYCenter = self. __setCutsYCenter__( PlateWSpec )
|
|
|
|
if not( self.capacitorInstance.__isUnitCap__() ) or ( self.capacitorInstance.__isUnitCap__() and doTop ) :
|
|
|
|
#print("xPlateRLayer_width",xPlateRLayer_width)
|
|
cutsNumber = CapacitorUnit.cutMaxNumber( self, xPlateRLayer_width, self.minWidth_routingTrackcut, self.minSpacing_routingTrackcut, self.minEnclo_routingTrackMetal_cut )
|
|
enclosure_RLayer_cut = ( xPlateRLayer_width - cutsNumber*self.minWidth_routingTrackcut - ( cutsNumber - 1 )*self.minSpacing_routingTrackcut ) / 2
|
|
|
|
for i in range( 0, self.matrixDim["columns"] - doBottom ):
|
|
cutXCenter = xPlateRLayerXCenter[i] - xPlateRLayer_width/2 + enclosure_RLayer_cut + self.minWidth_routingTrackcut/2
|
|
CapacitorUnit.cutLine( self, net, layer, cutXCenter, cutsYCenter, self.minWidth_routingTrackcut, self.minHeight_routingTrackcut, self.minSpacing_routingTrackcut, cutsNumber , 'horizontal' )
|
|
if self.dummyMode == True and self.connectToTopAndBottomTracks() :
|
|
CapacitorUnit.cutLine( self, net, layer, cutXCenter, self.routingTrackYCenter["bottom"]["upper"], self.minWidth_routingTrackcut, self.minHeight_routingTrackcut, self.minSpacing_routingTrackcut, cutsNumber , 'horizontal' )
|
|
|
|
if doBottom :
|
|
borderscutsNumber = CapacitorUnit.cutMaxNumber( self, self.xPlateRLayer_width["bottomBorders"], self.minWidth_routingTrackcut, self.minSpacing_routingTrackcut, self.minEnclo_routingTrackMetal_cut )
|
|
enclosure_RLayer_cut = ( self.xPlateRLayer_width["bottomBorders"] - borderscutsNumber*self.minWidth_routingTrackcut - ( borderscutsNumber - 1 )*self.minSpacing_routingTrackcut ) / 2
|
|
|
|
for i in range( 0,2):
|
|
CapacitorUnit.cutLine( self, net, layer, self.xPlateRLayerXCenter["bottomBorders"][i], cutsYCenter, self.minWidth_routingTrackcut, self.minHeight_routingTrackcut, self.minSpacing_routingTrackcut, borderscutsNumber , 'horizontal' )
|
|
if self.dummyMode == True and self.connectToTopAndBottomTracks() :
|
|
CapacitorUnit.cutLine( self, net, layer, self.xPlateRLayerXCenter["bottomBorders"][i], self.routingTrackYCenter["bottom"]["upper"], self.minWidth_routingTrackcut, self.minHeight_routingTrackcut, self.minSpacing_routingTrackcut, borderscutsNumber , 'horizontal' )
|
|
return
|
|
|
|
|
|
|
|
def __setCutsYCenter__( self, PlateWSpec ):
|
|
|
|
if ( self.connectToTopTracksOnly () and self.connectToLower( PlateWSpec ) ) or ( self.connectToTopAndBottomTracks() and self.connectToUpper( PlateWSpec ) ) :
|
|
cutsYCenter = self.routingTrackYCenter["top"]["lower"]
|
|
|
|
elif ( self.connectToTopTracksOnly () and self.connectToUpper( PlateWSpec ) ) :
|
|
cutsYCenter = self.routingTrackYCenter["top"]["upper"]
|
|
|
|
elif ( self.connectToBottomTracksOnly() ) and ( self.connectToUpper( PlateWSpec ) ) or ( self.connectToTopAndBottomTracks() and self.connectToLower( PlateWSpec ) ) :
|
|
cutsYCenter = self.routingTrackYCenter["bottom"]["upper"]
|
|
|
|
elif ( self.connectToBottomTracksOnly() and self.connectToLower( PlateWSpec ) ) :
|
|
cutsYCenter = self.routingTrackYCenter["bottom"]["lower"]
|
|
|
|
else : raise Error( 1, '__setCutsYCenter__() : Not possible to compute cuts vertical position due to invalid routing specifications "%s".' % PlateWSpec )
|
|
|
|
|
|
return cutsYCenter
|
|
|
|
|
|
|
|
## \return \c True if the plate is to be connected to one of the two top tracks.
|
|
# \param WiringSpecChain Wiring specifications of a capacitor's top or bottom plate
|
|
def connectToTopTracksOnly (self) : return True if self.tracksNumbers["top"] == 2 and self.tracksNumbers["bottom"] == 0 and self.dummyMode == False or self.tracksNumbers["top"] == 1 and self.tracksNumbers["bottom"] == 0 and self.dummyMode == True else False
|
|
|
|
|
|
## \return \c True if the plate is to be connected to one of the two bottom tracks.
|
|
# \param WiringSpecChain Wiring specifications of a capacitor's top or bottom plate
|
|
def connectToBottomTracksOnly (self) : return True if self.tracksNumbers["top"] == 0 and self.tracksNumbers["bottom"] == 2 and self.dummyMode == False or self.tracksNumbers["top"] == 0 and self.tracksNumbers["bottom"] == 1 and self.dummyMode == True else False
|
|
|
|
|
|
def connectToTopAndBottomTracks( self ) : return True if self.tracksNumbers["top"] == 1 and self.tracksNumbers["bottom"] == 1 else False
|
|
|
|
|
|
## \return \c True if the plate is to be connected to the upper track of top or bottom tracks.
|
|
# \param WiringSpecChain Wiring specifications of a capacitor's top or bottom plate
|
|
def connectToUpper( self, plateWiringSpec ) : return True if plateWiringSpec [1] == 1 else False
|
|
|
|
|
|
## \return \c True if the plate is to be connected to the lower track of top or bottom tracks.
|
|
# \param WiringSpecChain Wiring specifications of a capacitor's top or bottom plate
|
|
def connectToLower( self, plateWiringSpec ) : return True if plateWiringSpec [0] == 1 else False
|
|
|
|
|
|
|
|
def scriptMain( **kw ):
|
|
|
|
editor = None
|
|
if 'editor' in kw and kw['editor']:
|
|
editor = kw['editor']
|
|
|
|
UpdateSession.open()
|
|
Device = AllianceFramework.get().createCell( 'capacitor' )
|
|
Device.setTerminal( True )
|
|
|
|
bottomPlate_net0 = Net.create( Device, 'b0' )
|
|
bottomPlate_net0.setExternal( True )
|
|
b0 = Device.getNet("b0")
|
|
doBreak( 1, 'Done building bottomPlate')
|
|
|
|
topPlate_net0 = Net.create( Device, 't0' )
|
|
topPlate_net0.setExternal( True )
|
|
t0 = Device.getNet("t0")
|
|
doBreak( 1, 'Done building topPlate')
|
|
|
|
|
|
if editor:
|
|
UpdateSession.close()
|
|
editor.setCell( Device )
|
|
editor.fit()
|
|
UpdateSession.open()
|
|
|
|
nets = [[t0,b0]]
|
|
|
|
## A matrix of unit capacitors (all are active or all are dummy capacitors)
|
|
|
|
# capacitance = [1600]
|
|
# capacitorInstance = CapacitorStack( Device, capacitance, 'MIMCap', [0,0], nets,unitCap = 400)
|
|
# capacitor = capacitorInstance.create()
|
|
#
|
|
# routedCap = RouteCapacitorSingle( capacitorInstance, capacitor, dummyMode = True, tracksNumbers = [1,0] )
|
|
# routedCap = RouteCapacitorSingle( capacitorInstance, capacitor, tracksNumbers = [2,0], topPlateWSpec = [0,1] , bottomPlateWSpec = [1,0] )
|
|
# routedCap = RouteCapacitorSingle( capacitorInstance, capacitor, dummyMode = False , tracksNumbers = [1,1], topPlateWSpec = [0,1] , bottomPlateWSpec = [1,0])
|
|
|
|
## Unit capacitor ( an active capacitor )
|
|
capacitance = [600]
|
|
capacitorInstance = CapacitorStack( Device, capacitance, 'MIMCap', [0,0], nets,unitCap = 600)
|
|
capacitor = capacitorInstance.create()
|
|
routedCap = RouteCapacitorSingle( capacitorInstance, capacitor, topPlateWSpec = [0,1] , bottomPlateWSpec = [1,0] )
|
|
|
|
## Unit capacitor ( a dummy capacitor )
|
|
# capacitance = [600]
|
|
# capacitorInstance = CapacitorStack( Device, capacitance, 'MIMCap', [0,0], nets,unitCap = 600)
|
|
# capacitor = capacitorInstance.create()
|
|
# routedCap = RouteCapacitorSingle( capacitorInstance, capacitor, dummyMode = True, tracksNumbers = [1,0] )
|
|
|
|
|
|
bondingBox = routedCap.route()
|
|
|
|
AllianceFramework.get().saveCell( Device, Catalog.State.Views )
|
|
|
|
return True
|
|
|