Align power lines on QuadTree leaf area centers (X).

* Bug: In cumulus/plugins.block.block.py, always import Python modules
    using the exact same path. Otherwise the module may get imported
    twice and static variables are duplicated, generating a big mess.
      This was causing problem for the LUT in macro.py, and got SRAMs
    blocks encapsulated twice.
* New: In cumulus/plugins.block.block.Block.addPlaceHolder(), create
    a "place holder" instance over a given area to prevent the placer
    from using it. Allow to make space reservation.
* New: In cumulus/plugins.block.configuration.BlockConf, copy the
    toXPitch() and toYSlice() methods from spare in order to share
    them between modules. Still have to remove some other local copies.
* New: In cumulus/plugins.block.spare.QuadTree, keep a list of all
    the X centers of the partitionned areas. For yse by the power lines.
* New: In cumulus/plugins.chip.chip, move doPowerLayout() call from
    doChipFloorplan() to doConnectCore(), this is to delay the call
    until *after* the spare QuadTree has been created and we can
    align the power lines to the centers of the QuadTree.
* New: In cumulus/plugins.chip.pads.Corona.doPowerLayout(), if a
    spare QuadTree has been created, align the power lines on the
    X center of the leaf areas. This is a cheap way to avoid DRC
    errors between the power BigVias and the wires from the various
    clock trees (on METAL5).
* New: In cumulus/plugins.block.macro, add an ad-hoc patch for Staf's
    SRAMs. The blockage areas are slightly too narrow. We enlarge
    them by one pitch.
This commit is contained in:
Jean-Paul Chaput 2021-04-15 23:44:19 +02:00
parent dd28bbba7a
commit 95713ac66b
7 changed files with 198 additions and 50 deletions

View File

@ -24,7 +24,7 @@ from Hurricane import Breakpoint, DbU, Box, Transformation, Point, \
Cell, Instance
import CRL
from CRL import RoutingLayerGauge
from helpers import trace, dots
from helpers import trace, dots, l, u, n
from helpers.io import ErrorMessage, WarningMessage, catch
from helpers.overlay import UpdateSession
import Etesian
@ -32,15 +32,15 @@ import Anabatic
import Katana
import plugins.rsave
from plugins import getParameter
from alpha.macro.macro import Macro
from alpha.block import timing
from alpha.block.spares import Spares
from alpha.block.clocktree import ClockTree
#from alpha.block.hfns1 import BufferTree
#from alpha.block.hfns2 import BufferTree
#from alpha.block.hfns3 import BufferTree
from alpha.block.hfns4 import BufferTree
from alpha.block.configuration import IoPin, BlockConf, GaugeConf
from plugins.alpha.macro.macro import Macro
from plugins.alpha.block import timing
from plugins.alpha.block.spares import Spares
from plugins.alpha.block.clocktree import ClockTree
#from plugins.alpha.block.hfns1 import BufferTree
#from plugins.alpha.block.hfns2 import BufferTree
#from plugins.alpha.block.hfns3 import BufferTree
from plugins.alpha.block.hfns4 import BufferTree
from plugins.alpha.block.configuration import IoPin, BlockConf, GaugeConf
timing.staticInit()
@ -297,17 +297,18 @@ class Block ( object ):
Create a Block object. The only parameter ``conf`` must be a BlockConf
object which contains the complete block configuration.
"""
self.flags = 0
self.conf = conf
self.spares = Spares( self )
self.clockTrees = []
self.hfnTrees = []
self.blockInstances = []
self.sides = { IoPin.WEST : Side( self.conf, IoPin.WEST )
, IoPin.EAST : Side( self.conf, IoPin.EAST )
, IoPin.SOUTH : Side( self.conf, IoPin.SOUTH )
, IoPin.NORTH : Side( self.conf, IoPin.NORTH )
}
self.flags = 0
self.conf = conf
self.spares = Spares( self )
self.clockTrees = []
self.hfnTrees = []
self.blockInstances = []
self.placeHolderCount = 0
self.sides = { IoPin.WEST : Side( self.conf, IoPin.WEST )
, IoPin.EAST : Side( self.conf, IoPin.EAST )
, IoPin.SOUTH : Side( self.conf, IoPin.SOUTH )
, IoPin.NORTH : Side( self.conf, IoPin.NORTH )
}
if not self.conf.cell.getAbutmentBox().isEmpty():
isBuilt = True
for instance in self.conf.cell.getInstances():
@ -604,6 +605,7 @@ class Block ( object ):
coreTransf.applyOn( macroPosition )
xoffset = macroPosition.getX() - pnrAb.getXMin()
xpitch = self.conf.vDeepRG.getPitch()
print( 'X placement pitch: {}'.format(DbU.getValueString(xpitch)) )
print( 'Original X offset: {}'.format(DbU.getValueString(xoffset)) )
if xoffset % xpitch:
xoffset += xpitch - (xoffset % xpitch)
@ -627,6 +629,33 @@ class Block ( object ):
, Transformation.Orientation.ID )
, Instance.PlacementStatus.FIXED )
def addPlaceHolder ( self, area, inCore=False ):
if area.isEmpty():
print( ErrorMessage( 1, 'Block.addPlaceHolder(): Request for an empty place holder area.' ))
return
af = CRL.AllianceFramework.get()
phCellName = 'placeholder_{}'.format(self.placeHolderCount)
placeHolder = Cell.create( af.getLibrary(0), phCellName )
if inCore:
self.conf.icore.getTransformation().applyOn( area )
pitchedArea = Box( self.conf.toXPitch(area.getXMin(),True)
, self.conf.toYSlice(area.getYMin(),True)
, self.conf.toXPitch(area.getXMax())
, self.conf.toYSlice(area.getYMax()) )
placeHolder.setAbutmentBox( Box( u(0.0)
, u(0.0)
, pitchedArea.getWidth()
, pitchedArea.getHeight() ))
print( 'pitchedArea={}'.format(pitchedArea) )
placeHolder.setTerminalNetlist( True )
instance = Instance.create( self.conf.cellPnR, phCellName, placeHolder )
instance.setTransformation( Transformation( pitchedArea.getXMin()
, pitchedArea.getYMin()
, Transformation.Orientation.ID ) )
instance.setPlacementStatus( Instance.PlacementStatus.FIXED )
self.placeHolderCount += 1
return instance
def route ( self ):
routedCell = self.conf.corona if self.conf.isCoreBlock else self.conf.cell
self.katana = Katana.KatanaEngine.create( routedCell )

View File

@ -1239,3 +1239,27 @@ class BlockConf ( GaugeConf ):
self.cell.setName( self.cell.getName()+'_r' )
rsave( self.cell, CRL.Catalog.State.Physical|flags )
return
def toXPitch ( self, x, superior=False ):
"""
Returns the coordinate of the pitch immediately inferior to X.
Compute in the "P&R cell" coordinate system which may be core
or corona.
"""
area = self.cellPnR.getAbutmentBox()
modulo = (x - area.getXMin()) % self.sliceStep
if modulo and superior:
x += self.sliceStep
return x - modulo
def toYSlice ( self, y, superior=False ):
"""
Returns the coordinate of the slice immediately inferior to Y.
Compute in the "P&R cell" coordinate system which may be core
or corona.
"""
area = self.cellPnR.getAbutmentBox()
modulo = (y - area.getYMin()) % self.sliceHeight
if modulo and superior:
y += self.sliceHeight
return y - modulo

View File

@ -308,6 +308,7 @@ class QuadTree ( object ):
self.pool = BufferPool( self )
self.plugs = []
self.rtag = rtag
self.rleafX = [ area.getXCenter() ]
def destroy ( self ):
if self.bl: self.bl.destroy()
@ -481,6 +482,17 @@ class QuadTree ( object ):
for leaf in self.leafs: leaf.rselectBuffer( column, row, flags )
trace( 540, '-' )
def _mergeLeafX ( self, leafX ):
for x in leafX:
if not (x in self.rleafX):
self.rleafX.append( x )
self.rleafX.sort()
def _upMergeLeafX ( self ):
if self.parent:
self.parent._mergeLeafX( self.rleafX )
self.parent._upMergeLeafX()
def partition ( self ):
"""
Build one level of the quad-tree, if the side of the area is bigger than
@ -584,6 +596,8 @@ class QuadTree ( object ):
for leaf in self.leafs:
trace( 540, '\tLeaf rtag:"{}"\n'.format(leaf.rtag) )
leaf.rpartition()
else:
self._upMergeLeafX()
trace( 540, '-' )
if self.isRoot():
self._rsetMaxDepth()
@ -804,6 +818,11 @@ class Spares ( object ):
self.quadTree = None
self.strayBuffers = []
@property
def rleafX ( self ):
if self.quadTree: return self.quadTree.rleafX
return []
def reset ( self ):
self.quadTree.destroy()
for buffer in self.strayBuffers:
@ -908,6 +927,9 @@ class Spares ( object ):
with UpdateSession():
self.quadTree = QuadTree.create( self )
#self._addCapTies()
print( "X Centers of the QuadTree leaf" )
for x in self.quadTree.rleafX:
print( '| {}'.format(DbU.getValueString(x) ))
trace( 540, '-' )
def rshowPoolUse ( self ):

View File

@ -82,7 +82,7 @@ class Chip ( Block ):
self.conf.chip.setAbutmentBox( self.conf.chipAb )
trace( 550, '\tSet chip ab:{}\n'.format(self.conf.chip.getAbutmentBox()) )
trace( 550, '\tUsing core ab:{}\n'.format(self.conf.core.getAbutmentBox()) )
self.padsCorona = plugins.alpha.chip.pads.Corona( self.conf )
self.padsCorona = plugins.alpha.chip.pads.Corona( self )
self.conf.validated = self.padsCorona.validate()
if not self.conf.validated:
return False
@ -112,10 +112,11 @@ class Chip ( Block ):
y = y - (y % self.conf.sliceHeight)
self.conf.icore.setTransformation ( Transformation(x,y,Transformation.Orientation.ID) )
self.conf.icore.setPlacementStatus( Instance.PlacementStatus.FIXED )
self.padsCorona.doPowerLayout()
self.conf.refresh()
def doConnectCore ( self ):
if self.padsCorona:
self.padsCorona.doPowerLayout()
if self.conf.routingGauge.hasPowerSupply():
power = plugins.alpha.chip.powerplane.Builder( self.conf )
power.connectPower()

View File

@ -780,7 +780,7 @@ class CoreWire ( object ):
class Corona ( object ):
def __init__ ( self, conf ):
def __init__ ( self, chip ):
def _cmpPad ( pad1, pad2):
width1 = pad1.getAbutmentBox().getWidth()
@ -797,7 +797,7 @@ class Corona ( object ):
duplicateds.append( [ position, padInstance ] )
return duplicateds
self.conf = conf
self.chip = chip
self.conf.validated = False
self.northPads = _dupPads( self.conf.chipConf.northPads )
self.southPads = _dupPads( self.conf.chipConf.southPads )
@ -838,6 +838,9 @@ class Corona ( object ):
if self.conf.cfg.chip.minPadSpacing is None:
self.conf.cfg.chip.minPadSpacing = 0
@property
def conf ( self ): return self.chip.conf
@property
def supplyRailWidth ( self ): return self.conf.cfg.chip.supplyRailWidth
@ -1212,9 +1215,6 @@ class Corona ( object ):
with UpdateSession():
capViaWidth = self.conf.vDeepRG.getPitch()*3
coreAb = self.conf.coreAb
stripesNb = int( (coreAb.getWidth() - 8*capViaWidth + self.supplyRailWidth) \
/ self.supplyRailPitch - 1 )
offset = (coreAb.getWidth() - self.supplyRailPitch*(stripesNb-1)) / 2
powerNet = None
groundNet = None
chipPowerNet = None
@ -1251,31 +1251,59 @@ class Corona ( object ):
else:
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No ground net found in "{}"' \
.format(corona.getName()) )
icore = self.conf.icore
xcore = icore.getTransformation().getTx()
trace( 550, '\ticoreAb={}\n'.format(icore.getAbutmentBox()) )
print( 'capViaWidth={}'.format(DbU.getValueString(capViaWidth)))
stripeSpecs = []
for i in range(stripesNb+4):
stripesNb = int( (coreAb.getWidth() - 8*capViaWidth + self.supplyRailWidth) \
/ self.supplyRailPitch - 1 )
offset = (coreAb.getWidth() - self.supplyRailPitch*(stripesNb-1)) / 2
stripeSpecs.append( [ xcore + capViaWidth/2 , capViaWidth ] )
stripeSpecs.append( [ xcore + 2*capViaWidth + capViaWidth/2 , capViaWidth ] )
if self.chip.spares:
rleafX = self.chip.spares.rleafX
spacing = (rleafX[1] - rleafX[0]) / 2
stepOffset = 0
step = 1
trace( 550, '\trleafX\n' )
for i in range(len(rleafX)):
trace( 550, '\t| rleafX[{}] = {}\n'.format(i,DbU.getValueString(rleafX[i])))
if spacing < self.supplyRailPitch:
stepOffset = 1
step = 2
spacing = (rleafX[2] - rleafX[0]) / 2
if step == 1:
stripeSpecs.append( [ rleafX[0] - spacing, self.supplyRailWidth ] )
trace( 550, '\tstripe[N/A] @{}\n'.format(DbU.getValueString(stripeSpecs[-1][0])))
else:
stripeSpecs.append( [ rleafX[0], self.supplyRailWidth ] )
trace( 550, '\tstripe[N/A] @{}\n'.format(DbU.getValueString(stripeSpecs[-1][0])))
for i in range( stepOffset, len(rleafX)-stepOffset, step ):
if step == 1:
stripeSpecs.append( [ rleafX[i], self.supplyRailWidth ] )
trace( 550, '\tstripe[{}] @{}\n'.format(i,DbU.getValueString(stripeSpecs[-1][0])))
stripeSpecs.append( [ rleafX[i] + spacing, self.supplyRailWidth ] )
trace( 550, '\tstripe[{}] @{}\n'.format(i,DbU.getValueString(stripeSpecs[-1][0])))
else:
for i in range(stripesNb):
stripeSpecs.append( [ xcore + offset + i*self.supplyRailPitch
, self.supplyRailWidth
] )
stripeSpecs.append( [ xcore + coreAb.getWidth() - 2*capViaWidth + capViaWidth/2 , capViaWidth ] )
stripeSpecs.append( [ xcore + coreAb.getWidth() - capViaWidth/2 , capViaWidth ] )
trace( 550, '\ticoreAb={}\n'.format(icore.getAbutmentBox()) )
trace( 550, '\tcapViaWidth={}\n'.format(DbU.getValueString(capViaWidth)))
for i in range(len(stripeSpecs)):
if i % 2:
coronaNet = groundNet
chipNet = chipGroundNet
else:
coronaNet = powerNet
chipNet = chipPowerNet
if i < 2:
axis = xcore + 2*i*capViaWidth + capViaWidth/2
width = capViaWidth
elif i >= stripesNb+2:
axis = xcore + coreAb.getWidth() - 2*(i-stripesNb-1)*capViaWidth + capViaWidth/2
width = capViaWidth
coronaNet = powerNet
else:
axis = xcore + offset + (i-2)*self.supplyRailPitch
width = self.supplyRailWidth
stripeSpecs.append( [ chipNet, coronaNet, axis, width ] )
for chipNet, coronaNet, axis, width in stripeSpecs:
self._supplyToPad( chipNet, coronaNet, axis, width, North )
self._supplyToPad( chipNet, coronaNet, axis, width, South )
chipNet = chipGroundNet
coronaNet = groundNet
trace( 550, '\tstripe[{}] @{}\n'.format(i,DbU.getValueString(stripeSpecs[i][0])))
self._supplyToPad( chipNet, coronaNet, stripeSpecs[i][0], stripeSpecs[i][1], North )
self._supplyToPad( chipNet, coronaNet, stripeSpecs[i][0], stripeSpecs[i][1], South )
#for istripe in range(stripesNb):
# trace( 550, '\tistripe={}\n'.format(istripe) )
# axis = xcore + offset + istripe*self.supplyRailPitch

View File

@ -448,7 +448,7 @@ class Builder ( object ):
if layer.isSymbolic():
layer = layer.getBasicLayer()
query.setBasicLayer( layer )
trace( 550, ',+', 'query.doQuery() {}'.format(layer) )
trace( 550, ',+', '\tquery.doQuery() {}\n'.format(layer) )
query.doQuery()
trace( 550, '-' )
self.activePlane = None

View File

@ -22,7 +22,7 @@ from Hurricane import Breakpoint, DbU, Box, Transformation, Point, \
Box, Path, Layer, Occurrence, Net, \
NetExternalComponents, RoutingPad, Pad, \
Horizontal, Vertical, Contact, Pin, Plug, \
Cell, Instance
Cell, Instance, Rectilinear
import CRL
from CRL import RoutingLayerGauge
from helpers import trace, dots
@ -40,10 +40,12 @@ class Macro ( object ):
processeds macros so they are modified only once.
"""
trace( 550, '\tStatic init of Macro\n' )
LUT = {}
@staticmethod
def lookup ( macroCell ):
trace( 550, '\tMacro.lookup() on {}\n'.format(macroCell) )
if Macro.LUT.has_key(macroCell): return Macro.LUT[ macroCell ]
return None
@ -68,6 +70,7 @@ class Macro ( object ):
macro = Macro.lookup( macroCell )
if macro is not None:
trace( 550, '\tReusing macro wrapper {}\n'.format(macroCell) )
return macro
return Macro( macroCell, gaugeName, hMargin, vMargin )
@ -112,12 +115,53 @@ class Macro ( object ):
that are half free and half occluded by the block itself may
cause (stupid) deadlock to appear.
"""
trace( 550, '\tMacro.__init__() {}\n'.format(macroCell) )
self.cell = macroCell
Macro.LUT[ self.cell ] = self
af = CRL.AllianceFramework.get()
ab = self.cell.getAbutmentBox()
self.rg = af.getRoutingGauge( gaugeName )
self.rg = af.getRoutingGauge( gaugeName )
gaugeMetal2 = self.rg.getLayerGauge( 1 )
gaugeMetal3 = self.rg.getLayerGauge( 2 )
gaugeMetal4 = self.rg.getLayerGauge( 3 )
blockageMetal2 = gaugeMetal2.getBlockageLayer()
blockageMetal3 = gaugeMetal3.getBlockageLayer()
blockageMetal4 = gaugeMetal4.getBlockageLayer()
minSpacingMetal2 = gaugeMetal2.getLayer().getMinimalSpacing()
minSpacingMetal3 = gaugeMetal3.getLayer().getMinimalSpacing()
minSpacingMetal4 = gaugeMetal4.getLayer().getMinimalSpacing()
if self.cell.getName() == 'SPBlock_512W64B8W':
print( ' o Ad-hoc blockage patch for "{}".'.format(self.cell.getName()) )
for net in self.cell.getNets():
for component in net.getComponents():
if isinstance(component,Rectilinear) and component.getLayer() == blockageMetal2:
bb = component.getBoundingBox()
bb.inflate( minSpacingMetal2/2 )
Horizontal.create( component.getNet()
, blockageMetal2
, bb.getYCenter()
, bb.getHeight()
, bb.getXMin()
, bb.getXMax() )
elif isinstance(component,Rectilinear) and component.getLayer() == blockageMetal3:
bb = component.getBoundingBox()
bb.inflate( 2*minSpacingMetal3, minSpacingMetal3/2 )
Vertical.create( component.getNet()
, blockageMetal3
, bb.getXCenter()
, bb.getWidth()
, bb.getYMin()
, bb.getYMax() )
elif isinstance(component,Rectilinear) and component.getLayer() == blockageMetal4:
bb = component.getBoundingBox()
bb.inflate( minSpacingMetal4/2 )
Horizontal.create( component.getNet()
, blockageMetal4
, bb.getYCenter()
, bb.getHeight()
, bb.getXMin()
, bb.getXMax() )
self.innerAb = ab
sliceHeight = af.getCellGauge( gaugeName ).getSliceHeight()
westPins = []