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 Cell, Instance
import CRL import CRL
from CRL import RoutingLayerGauge 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.io import ErrorMessage, WarningMessage, catch
from helpers.overlay import UpdateSession from helpers.overlay import UpdateSession
import Etesian import Etesian
@ -32,15 +32,15 @@ import Anabatic
import Katana import Katana
import plugins.rsave import plugins.rsave
from plugins import getParameter from plugins import getParameter
from alpha.macro.macro import Macro from plugins.alpha.macro.macro import Macro
from alpha.block import timing from plugins.alpha.block import timing
from alpha.block.spares import Spares from plugins.alpha.block.spares import Spares
from alpha.block.clocktree import ClockTree from plugins.alpha.block.clocktree import ClockTree
#from alpha.block.hfns1 import BufferTree #from plugins.alpha.block.hfns1 import BufferTree
#from alpha.block.hfns2 import BufferTree #from plugins.alpha.block.hfns2 import BufferTree
#from alpha.block.hfns3 import BufferTree #from plugins.alpha.block.hfns3 import BufferTree
from alpha.block.hfns4 import BufferTree from plugins.alpha.block.hfns4 import BufferTree
from alpha.block.configuration import IoPin, BlockConf, GaugeConf from plugins.alpha.block.configuration import IoPin, BlockConf, GaugeConf
timing.staticInit() timing.staticInit()
@ -303,6 +303,7 @@ class Block ( object ):
self.clockTrees = [] self.clockTrees = []
self.hfnTrees = [] self.hfnTrees = []
self.blockInstances = [] self.blockInstances = []
self.placeHolderCount = 0
self.sides = { IoPin.WEST : Side( self.conf, IoPin.WEST ) self.sides = { IoPin.WEST : Side( self.conf, IoPin.WEST )
, IoPin.EAST : Side( self.conf, IoPin.EAST ) , IoPin.EAST : Side( self.conf, IoPin.EAST )
, IoPin.SOUTH : Side( self.conf, IoPin.SOUTH ) , IoPin.SOUTH : Side( self.conf, IoPin.SOUTH )
@ -604,6 +605,7 @@ class Block ( object ):
coreTransf.applyOn( macroPosition ) coreTransf.applyOn( macroPosition )
xoffset = macroPosition.getX() - pnrAb.getXMin() xoffset = macroPosition.getX() - pnrAb.getXMin()
xpitch = self.conf.vDeepRG.getPitch() xpitch = self.conf.vDeepRG.getPitch()
print( 'X placement pitch: {}'.format(DbU.getValueString(xpitch)) )
print( 'Original X offset: {}'.format(DbU.getValueString(xoffset)) ) print( 'Original X offset: {}'.format(DbU.getValueString(xoffset)) )
if xoffset % xpitch: if xoffset % xpitch:
xoffset += xpitch - (xoffset % xpitch) xoffset += xpitch - (xoffset % xpitch)
@ -627,6 +629,33 @@ class Block ( object ):
, Transformation.Orientation.ID ) , Transformation.Orientation.ID )
, Instance.PlacementStatus.FIXED ) , 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 ): def route ( self ):
routedCell = self.conf.corona if self.conf.isCoreBlock else self.conf.cell routedCell = self.conf.corona if self.conf.isCoreBlock else self.conf.cell
self.katana = Katana.KatanaEngine.create( routedCell ) self.katana = Katana.KatanaEngine.create( routedCell )

View File

@ -1239,3 +1239,27 @@ class BlockConf ( GaugeConf ):
self.cell.setName( self.cell.getName()+'_r' ) self.cell.setName( self.cell.getName()+'_r' )
rsave( self.cell, CRL.Catalog.State.Physical|flags ) rsave( self.cell, CRL.Catalog.State.Physical|flags )
return 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.pool = BufferPool( self )
self.plugs = [] self.plugs = []
self.rtag = rtag self.rtag = rtag
self.rleafX = [ area.getXCenter() ]
def destroy ( self ): def destroy ( self ):
if self.bl: self.bl.destroy() if self.bl: self.bl.destroy()
@ -481,6 +482,17 @@ class QuadTree ( object ):
for leaf in self.leafs: leaf.rselectBuffer( column, row, flags ) for leaf in self.leafs: leaf.rselectBuffer( column, row, flags )
trace( 540, '-' ) 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 ): def partition ( self ):
""" """
Build one level of the quad-tree, if the side of the area is bigger than 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: for leaf in self.leafs:
trace( 540, '\tLeaf rtag:"{}"\n'.format(leaf.rtag) ) trace( 540, '\tLeaf rtag:"{}"\n'.format(leaf.rtag) )
leaf.rpartition() leaf.rpartition()
else:
self._upMergeLeafX()
trace( 540, '-' ) trace( 540, '-' )
if self.isRoot(): if self.isRoot():
self._rsetMaxDepth() self._rsetMaxDepth()
@ -804,6 +818,11 @@ class Spares ( object ):
self.quadTree = None self.quadTree = None
self.strayBuffers = [] self.strayBuffers = []
@property
def rleafX ( self ):
if self.quadTree: return self.quadTree.rleafX
return []
def reset ( self ): def reset ( self ):
self.quadTree.destroy() self.quadTree.destroy()
for buffer in self.strayBuffers: for buffer in self.strayBuffers:
@ -908,6 +927,9 @@ class Spares ( object ):
with UpdateSession(): with UpdateSession():
self.quadTree = QuadTree.create( self ) self.quadTree = QuadTree.create( self )
#self._addCapTies() #self._addCapTies()
print( "X Centers of the QuadTree leaf" )
for x in self.quadTree.rleafX:
print( '| {}'.format(DbU.getValueString(x) ))
trace( 540, '-' ) trace( 540, '-' )
def rshowPoolUse ( self ): def rshowPoolUse ( self ):

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ from Hurricane import Breakpoint, DbU, Box, Transformation, Point, \
Box, Path, Layer, Occurrence, Net, \ Box, Path, Layer, Occurrence, Net, \
NetExternalComponents, RoutingPad, Pad, \ NetExternalComponents, RoutingPad, Pad, \
Horizontal, Vertical, Contact, Pin, Plug, \ Horizontal, Vertical, Contact, Pin, Plug, \
Cell, Instance Cell, Instance, Rectilinear
import CRL import CRL
from CRL import RoutingLayerGauge from CRL import RoutingLayerGauge
from helpers import trace, dots from helpers import trace, dots
@ -40,10 +40,12 @@ class Macro ( object ):
processeds macros so they are modified only once. processeds macros so they are modified only once.
""" """
trace( 550, '\tStatic init of Macro\n' )
LUT = {} LUT = {}
@staticmethod @staticmethod
def lookup ( macroCell ): def lookup ( macroCell ):
trace( 550, '\tMacro.lookup() on {}\n'.format(macroCell) )
if Macro.LUT.has_key(macroCell): return Macro.LUT[ macroCell ] if Macro.LUT.has_key(macroCell): return Macro.LUT[ macroCell ]
return None return None
@ -68,6 +70,7 @@ class Macro ( object ):
macro = Macro.lookup( macroCell ) macro = Macro.lookup( macroCell )
if macro is not None: if macro is not None:
trace( 550, '\tReusing macro wrapper {}\n'.format(macroCell) )
return macro return macro
return Macro( macroCell, gaugeName, hMargin, vMargin ) 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 that are half free and half occluded by the block itself may
cause (stupid) deadlock to appear. cause (stupid) deadlock to appear.
""" """
trace( 550, '\tMacro.__init__() {}\n'.format(macroCell) )
self.cell = macroCell self.cell = macroCell
Macro.LUT[ self.cell ] = self Macro.LUT[ self.cell ] = self
af = CRL.AllianceFramework.get() af = CRL.AllianceFramework.get()
ab = self.cell.getAbutmentBox() 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 self.innerAb = ab
sliceHeight = af.getCellGauge( gaugeName ).getSliceHeight() sliceHeight = af.getCellGauge( gaugeName ).getSliceHeight()
westPins = [] westPins = []