Added multiple clock support in cumulus/plugins/alpha/block.
* New: Added multiple clock support in H-Tree generation in alpha/block. * New: In CRL/etc/<NODE>/<TECH>/plugins.py, added three new parameters for block plugin config: "block.spareSide" : The size of the minimum side of a buffered area. (quad-tree leaf). "spare.buffer" : The model of the cell buffer to be used. "spare.maxSinks" : max number of sinks on a buffer before issuing a warning (non-blocking). * Bug: In Etesian::BloatCell::getAb(), never apply the bloat profile to fixed cells. In case of buffers from the spare maxtrix it was leading Coloquinte to detect an overlap of fixed cells, which it do not support (rightly so).
This commit is contained in:
parent
38375fd9ae
commit
51e3639687
|
@ -27,3 +27,6 @@ Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
|
|||
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
|
||||
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
|
||||
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
|
||||
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
|
||||
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )
|
||||
|
||||
|
|
|
@ -27,3 +27,6 @@ Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
|
|||
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
|
||||
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
|
||||
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
|
||||
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
|
||||
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )
|
||||
|
||||
|
|
|
@ -27,3 +27,6 @@ Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
|
|||
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
|
||||
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
|
||||
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
|
||||
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
|
||||
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ p.addValue( "Density" , 2 )
|
|||
|
||||
p = Cfg.getParamInt ( "katana.hTracksReservedLocal" ); p.setInt ( 3 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.vTracksReservedLocal" ); p.setInt ( 3 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.hTracksReservedMin" ); p.setInt ( 1 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.vTracksReservedMin" ); p.setInt ( 1 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.termSatReservedLocal" ); p.setInt ( 8 )
|
||||
p = Cfg.getParamInt ( "katana.termSatThreshold" ); p.setInt ( 9 )
|
||||
p = Cfg.getParamInt ( "katana.eventsLimit" ); p.setInt ( 4000002 )
|
||||
|
|
|
@ -28,3 +28,6 @@ Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(600) )
|
|||
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
|
||||
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
|
||||
Cfg.getParamInt ( 'block.spareSide' ).setInt ( 10 )
|
||||
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
|
||||
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ p = Cfg.getParamInt ( "katana.searchHalo" ); p.setInt ( 1 )
|
|||
p = Cfg.getParamInt ( "katana.hTracksReservedLocal" ); p.setInt ( 3 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.vTracksReservedLocal" ); p.setInt ( 3 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.termSatReservedLocal" ); p.setInt ( 8 )
|
||||
p = Cfg.getParamInt ( "katana.hTracksReservedMin" ); p.setInt ( 1 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.vTracksReservedMin" ); p.setInt ( 1 ); p.setMin(0); p.setMax(20)
|
||||
p = Cfg.getParamInt ( "katana.termSatThreshold" ); p.setInt ( 9 )
|
||||
p = Cfg.getParamInt ( "katana.eventsLimit" ); p.setInt ( 4000002 )
|
||||
p = Cfg.getParamInt ( "katana.ripupCost" ); p.setInt ( 3 ); p.setMin(0)
|
||||
|
@ -74,10 +76,86 @@ p = Cfg.getParamInt ( "katana.localRipupLimit" ); p.setInt ( 9 );
|
|||
p = Cfg.getParamInt ( "katana.globalRipupLimit" ); p.setInt ( 5 ); p.setMin(1)
|
||||
p = Cfg.getParamInt ( "katana.longGlobalRipupLimit" ); p.setInt ( 5 ); p.setMin(1)
|
||||
p = Cfg.getParamString( 'chip.padCoreSide' ); p.setString( 'South' )
|
||||
p = Cfg.getParamInt ( "block.spareSide" ); p.setInt ( l(2000) )
|
||||
|
||||
|
||||
tech = DataBase.getDB().getTechnology()
|
||||
af = AllianceFramework.get()
|
||||
rg = RoutingGauge.create( 'msxlib_uniform' )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL1') # metal.
|
||||
, RoutingLayerGauge.Vertical # preferred routing direction.
|
||||
, RoutingLayerGauge.PinOnly # layer usage.
|
||||
, 0 # depth.
|
||||
, 0.0 # density (deprecated).
|
||||
, l(0) # track offset from AB.
|
||||
, l(10) # track pitch.
|
||||
, l(3) # wire width.
|
||||
, l(2) # VIA side (that is VIA12).
|
||||
, l(7) # obstacle dW.
|
||||
) )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL2') # metal.
|
||||
, RoutingLayerGauge.Horizontal # preferred routing direction.
|
||||
, RoutingLayerGauge.Default # layer usage.
|
||||
, 1 # depth.
|
||||
, 0.0 # density (deprecated).
|
||||
, l(0) # track offset from AB.
|
||||
, l(10) # track pitch.
|
||||
, l(3) # wire width.
|
||||
, l(2) # VIA side (that is VIA23).
|
||||
, l(8) # obstacle dW.
|
||||
) )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL3') # metal.
|
||||
, RoutingLayerGauge.Vertical # preferred routing direction.
|
||||
, RoutingLayerGauge.Default # layer usage.
|
||||
, 2 # depth.
|
||||
, 0.0 # density (deprecated).
|
||||
, l(0) # track offset from AB.
|
||||
, l(10) # track pitch.
|
||||
, l(3) # wire width.
|
||||
, l(2) # VIA side (that is VIA34).
|
||||
, l(8) # obstacle dW.
|
||||
) )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL4') # metal.
|
||||
, RoutingLayerGauge.Horizontal # preferred routing direction.
|
||||
, RoutingLayerGauge.Default # layer usage.
|
||||
, 3 # depth.
|
||||
, 0.0 # density (deprecated).
|
||||
, l(0) # track offset from AB.
|
||||
, l(10) # track pitch.
|
||||
, l(3) # wire width.
|
||||
, l(2) # VIA side (that is VIA23).
|
||||
, l(8) # obstacle dW.
|
||||
) )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL5') # metal.
|
||||
, RoutingLayerGauge.Vertical # preferred routing direction.
|
||||
, RoutingLayerGauge.Default # layer usage.
|
||||
, 4 # depth.
|
||||
, 0.0 # density (deprecated).
|
||||
, l(0) # track offset from AB.
|
||||
, l(10) # track pitch.
|
||||
, l(3) # wire width.
|
||||
, l(2) # VIA side (that is VIA23).
|
||||
, l(8) # obstacle dW.
|
||||
) )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL6') # metal.
|
||||
, RoutingLayerGauge.Horizontal # preferred routing direction.
|
||||
, RoutingLayerGauge.Default # layer usage.
|
||||
, 5 # depth.
|
||||
, 0.0 # density (deprecated).
|
||||
, l(0) # track offset from AB.
|
||||
, l(10) # track pitch.
|
||||
, l(3) # wire width.
|
||||
, l(2) # VIA side (that is VIA23).
|
||||
, l(8) # obstacle dW.
|
||||
) )
|
||||
|
||||
af.addRoutingGauge( rg )
|
||||
rg = RoutingGauge.create( 'msxlib' )
|
||||
|
||||
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL1') # metal.
|
||||
|
@ -237,6 +315,13 @@ af.addRoutingGauge( rg )
|
|||
af.setRoutingGauge( 'msxlib' )
|
||||
|
||||
# Gauge for standard cells.
|
||||
cg = CellGauge.create( 'msxlib_uniform'
|
||||
, 'metal2' # pin layer name.
|
||||
, l( 10.0) # pitch.
|
||||
, l(100.0) # cell slice height.
|
||||
, l( 10.0) # cell slice step.
|
||||
)
|
||||
af.addCellGauge( cg )
|
||||
cg = CellGauge.create( 'msxlib'
|
||||
, 'metal2' # pin layer name.
|
||||
, l( 10.0) # pitch.
|
||||
|
|
|
@ -25,5 +25,7 @@ Cfg.getParamInt ( "chip.block.rails.vWidth" ).setInt ( l( 24) )
|
|||
Cfg.getParamInt ( "chip.block.rails.hSpacing" ).setInt ( l( 12) )
|
||||
Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
|
||||
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
|
||||
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
|
||||
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x8')
|
||||
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
|
||||
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
|
||||
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )
|
||||
|
|
|
@ -173,6 +173,9 @@ class Side ( object ):
|
|||
if net is None:
|
||||
print( ErrorMessage( 1, [ 'Side.place(IoPin): No net named "{}".'.format(pinName) ] ))
|
||||
continue
|
||||
if net.isClock() and self.block.state.useClockTree:
|
||||
print( WarningMessage( 'Side.place(IoPin): Skipping clock IoPin "{}".'.format(pinName) ))
|
||||
continue
|
||||
pinName += '.{}'.format(self.block.state.getIoPinsCounts(net))
|
||||
pinPos = self.getNextPinPosition( ioPin.flags, upos, ioPin.ustep )
|
||||
if pinPos.getX() > self.block.state.xMax or pinPos.getX() < self.block.state.xMin:
|
||||
|
@ -371,19 +374,25 @@ class Block ( object ):
|
|||
self.state.cfg.apply()
|
||||
for side in self.sides.values(): side.setupAb()
|
||||
|
||||
def addClockTree ( self, clockNet=None ):
|
||||
"""Create the trunk of the clock tree (recursive H-Tree)."""
|
||||
if not clockNet:
|
||||
def addClockTrees ( self ):
|
||||
"""Create the trunk of all the clock trees (recursive H-Tree)."""
|
||||
print( ' o Buildding clock tree(s).' )
|
||||
af = CRL.AllianceFramework.get()
|
||||
clockNets = []
|
||||
for net in self.state.cell.getNets():
|
||||
if af.isCLOCK(net.getName()): 'CLOCK: {}'.format(net)
|
||||
if net.isClock():
|
||||
clockNet = net
|
||||
break
|
||||
if not clockNet:
|
||||
raise ErrorMessage( 3, 'Block.clockTree(): Cell "{}" has no clock net.'.format(self.state.cell.getName()) )
|
||||
trace( 550, '\tBlock.addClockTrees(): Found clock {}.\n'.format(net) )
|
||||
clockNets.append( net )
|
||||
if not clockNets:
|
||||
raise ErrorMessage( 3, 'Block.clockTree(): Cell "{}" has no clock net(s).'.format(self.state.cell.getName()) )
|
||||
with UpdateSession():
|
||||
self.clockTrees.append( ClockTree(self.spares,clockNet) )
|
||||
for clockNet in clockNets:
|
||||
print( ' - "{}".'.format(clockNet.getName()) )
|
||||
trace( 550, ',+', '\tBlock.addClockTrees(): Build clock tree for {}.\n'.format(clockNet) )
|
||||
self.clockTrees.append( ClockTree(self.spares,clockNet,len(self.clockTrees)) )
|
||||
self.clockTrees[-1].buildHTree()
|
||||
return self.clockTrees[-1]
|
||||
trace( 550, '-' )
|
||||
|
||||
def splitClocks ( self ):
|
||||
"""
|
||||
|
@ -428,6 +437,10 @@ class Block ( object ):
|
|||
.format(net.getName() )))
|
||||
|
||||
def expandIoPins ( self ):
|
||||
"""
|
||||
Stick out the block I/O pins of one pitch on all sides.
|
||||
See setUnexpandPins() to prevent it to happen on a specific side.
|
||||
"""
|
||||
with UpdateSession():
|
||||
for side in self.sides.values():
|
||||
side.expand()
|
||||
|
@ -502,7 +515,8 @@ class Block ( object ):
|
|||
self.checkIoPins()
|
||||
self.spares.build()
|
||||
if editor: editor.fit()
|
||||
if self.state.useClockTree: self.addClockTree()
|
||||
if self.state.useClockTree: self.addClockTrees()
|
||||
#Breakpoint.stop( 0, 'Clock tree(s) done.' )
|
||||
self.place()
|
||||
if self.state.useClockTree: self.splitClocks()
|
||||
status = self.route()
|
||||
|
|
|
@ -44,6 +44,7 @@ from helpers.overlay import UpdateSession
|
|||
from plugins import getParameter
|
||||
from plugins import utils
|
||||
from plugins.alpha.block.configuration import GaugeConf
|
||||
from plugins.alpha.block.spares import Spares
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -54,51 +55,54 @@ class ClockTree ( object ):
|
|||
Build a clock tree on a block.
|
||||
"""
|
||||
|
||||
def __init__ ( self, spares, clockNet ):
|
||||
def __init__ ( self, spares, clockNet, index ):
|
||||
self.spares = spares
|
||||
self.clockNet = clockNet
|
||||
|
||||
self.clockIndex = index
|
||||
if not self.clockNet.isClock():
|
||||
print( WarningMessage( 'ClockTree.__init__(): Net "{}" is not of CLOCK type.' \
|
||||
.format(self.clockNet.getName()) ))
|
||||
|
||||
def _rconnectHTree ( self, quadTree ):
|
||||
if quadTree.isLeaf(): return False
|
||||
driverNet = quadTree.bOutputPlug.getNet()
|
||||
for leaf in quadTree.leafs:
|
||||
def _rconnectHTree ( self, qt ):
|
||||
if qt.isLeaf(): return False
|
||||
qt.rconnectBuffer()
|
||||
driverNet = qt.bOutputPlug.getNet()
|
||||
for leaf in qt.leafs:
|
||||
leaf.bInputPlug.setNet( driverNet )
|
||||
self._rconnectHTree( leaf )
|
||||
return True
|
||||
|
||||
def _rrouteHTree ( self, quadT ):
|
||||
def _rrouteHTree ( self, qt ):
|
||||
"""
|
||||
Recursively build one HTree branch for all non-terminal nodes of the QuadTree.
|
||||
"""
|
||||
trace( 550, ',+', '\tClockTree._rrouteHTree() {}\n'.format(quadT.bOutputPlug.getNet()) )
|
||||
if quadT.isLeaf():
|
||||
trace( 550, ',+', '\tClockTree._rrouteHTree() {}\n'.format(qt.bOutputPlug.getNet()) )
|
||||
trace( 550, '\tOn: {}\n'.format(qt) )
|
||||
if qt.isLeaf():
|
||||
trace( 550, '-' )
|
||||
return False
|
||||
|
||||
gaugeConf = self.spares.state.gaugeConf
|
||||
bufferConf = self.spares.state.bufferConf
|
||||
ckNet = quadT.bOutputPlug.getNet()
|
||||
ckNet = qt.bOutputPlug.getNet()
|
||||
|
||||
leftSourceContact = gaugeConf.rpAccessByPlugName( quadT.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 )
|
||||
rightSourceContact = gaugeConf.rpAccessByPlugName( quadT.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 )
|
||||
blContact = gaugeConf.rpAccessByPlugName( quadT.bl.buffer, bufferConf.input , ckNet )
|
||||
brContact = gaugeConf.rpAccessByPlugName( quadT.br.buffer, bufferConf.input , ckNet )
|
||||
tlContact = gaugeConf.rpAccessByPlugName( quadT.tl.buffer, bufferConf.input , ckNet )
|
||||
trContact = gaugeConf.rpAccessByPlugName( quadT.tr.buffer, bufferConf.input , ckNet )
|
||||
leftSourceContact = gaugeConf.rpAccessByPlugName( qt.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 )
|
||||
rightSourceContact = gaugeConf.rpAccessByPlugName( qt.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 )
|
||||
blContact = gaugeConf.rpAccessByPlugName( qt.bl.buffer, bufferConf.input , ckNet )
|
||||
brContact = gaugeConf.rpAccessByPlugName( qt.br.buffer, bufferConf.input , ckNet )
|
||||
tlContact = gaugeConf.rpAccessByPlugName( qt.tl.buffer, bufferConf.input , ckNet )
|
||||
trContact = gaugeConf.rpAccessByPlugName( qt.tr.buffer, bufferConf.input , ckNet )
|
||||
leftContact = gaugeConf.createContact( ckNet, blContact.getX(), leftSourceContact.getY(), 0 )
|
||||
rightContact = gaugeConf.createContact( ckNet, brContact.getX(), rightSourceContact.getY(), 0 )
|
||||
|
||||
leftSourceX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, leftSourceContact.getX(), 0 )
|
||||
leftSourceY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, leftSourceContact.getY(), 0 )
|
||||
rightSourceX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, rightSourceContact.getX(), 0 )
|
||||
rightSourceY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, rightSourceContact.getY(), 0 )
|
||||
leftX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, leftContact.getX(), 0 )
|
||||
rightX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, rightContact.getX(), 0 )
|
||||
tlY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, tlContact.getY(), 0 )
|
||||
blY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, blContact.getY(), 0 )
|
||||
leftSourceX = gaugeConf.getNearestVerticalTrack ( qt.root.area, leftSourceContact.getX(), 0 )
|
||||
leftSourceY = gaugeConf.getNearestHorizontalTrack( qt.root.area, leftSourceContact.getY(), 0 )
|
||||
rightSourceX = gaugeConf.getNearestVerticalTrack ( qt.root.area, rightSourceContact.getX(), 0 )
|
||||
rightSourceY = gaugeConf.getNearestHorizontalTrack( qt.root.area, rightSourceContact.getY(), 0 )
|
||||
leftX = gaugeConf.getNearestVerticalTrack ( qt.root.area, leftContact.getX(), 0 )
|
||||
rightX = gaugeConf.getNearestVerticalTrack ( qt.root.area, rightContact.getX(), 0 )
|
||||
tlY = gaugeConf.getNearestHorizontalTrack( qt.root.area, tlContact.getY(), 0 )
|
||||
blY = gaugeConf.getNearestHorizontalTrack( qt.root.area, blContact.getY(), 0 )
|
||||
|
||||
gaugeConf.setStackPosition( leftSourceContact, leftSourceX, leftSourceY )
|
||||
gaugeConf.setStackPosition( rightSourceContact, rightSourceX, rightSourceY )
|
||||
|
@ -119,7 +123,33 @@ class ClockTree ( object ):
|
|||
gaugeConf.createVertical ( rightContact , brContact , rightX , 0 )
|
||||
gaugeConf.createVertical ( trContact , rightContact , rightX , 0 )
|
||||
|
||||
for leaf in quadT.leafs:
|
||||
if qt.isRoot():
|
||||
ckNet = self.clockNet
|
||||
trace( 550, '\tRemoving any previous pin...\n' )
|
||||
pins = []
|
||||
for pin in ckNet.getPins(): pins.append( pin )
|
||||
for pin in pins:
|
||||
print( WarningMessage('ClockTree._rrouteHTree(): Removing {}.'.format(pin)) )
|
||||
pin.destroy()
|
||||
|
||||
layerGauge = gaugeConf.vRoutingGauge
|
||||
rootContact = gaugeConf.rpAccessByPlugName( qt.buffer, bufferConf.input, ckNet, 0 )
|
||||
x = gaugeConf.getNearestVerticalTrack ( qt.area, rootContact.getX(), 0 )
|
||||
y = gaugeConf.getNearestHorizontalTrack( qt.area, rootContact.getY(), 0 )
|
||||
rootPin = Pin.create( ckNet
|
||||
, ckNet.getName()+'.0'
|
||||
, Pin.Direction.NORTH
|
||||
, Pin.PlacementStatus.FIXED
|
||||
, layerGauge.getLayer()
|
||||
, x
|
||||
, qt.area.getYMax()
|
||||
, layerGauge.getViaWidth()
|
||||
, layerGauge.getViaWidth()
|
||||
)
|
||||
gaugeConf.setStackPosition( rootContact, x, y )
|
||||
gaugeConf.createVertical( rootContact, rootPin, x, 0 )
|
||||
|
||||
for leaf in qt.leafs:
|
||||
self._rrouteHTree( leaf )
|
||||
trace( 550, '-' )
|
||||
return True
|
||||
|
@ -131,12 +161,12 @@ class ClockTree ( object ):
|
|||
2. Detach the all the clock sink point and reconnect them to the
|
||||
buffers of the leafs of the QuadTree.
|
||||
"""
|
||||
quadTree = self.spares.quadTree
|
||||
quadTree.bufferTag = self.clockNet.getName()
|
||||
quadTree.ruseBuffer()
|
||||
qt = self.spares.quadTree
|
||||
qt.bufferTag = self.clockNet.getName()
|
||||
qt.rselectBuffer( self.clockIndex, self.clockIndex, Spares.CHECK_USED|Spares.MARK_USED)
|
||||
with UpdateSession():
|
||||
self._rconnectHTree( quadTree )
|
||||
self._rrouteHTree ( quadTree )
|
||||
self._rconnectHTree( qt )
|
||||
self._rrouteHTree ( qt )
|
||||
|
||||
def splitClock ( self ):
|
||||
"""
|
||||
|
|
|
@ -356,7 +356,11 @@ class GaugeConf ( object ):
|
|||
segment = component
|
||||
count += 1
|
||||
if count > 1:
|
||||
raise ErrorMessage( 1, 'GaugeConf::_setStackPosition(): There must be exactly one segment connected to %s, not %d.' % (topContact,count) )
|
||||
message = [ 'GaugeConf::_setStackPosition(): There must be exactly one segment connected to contact, not {}.'.format(count)
|
||||
, '+ {}'.format(topContact) ]
|
||||
for component in topContact.getSlaveComponents():
|
||||
message.append( '| {}'.format(component) )
|
||||
raise ErrorMessage( 1, message )
|
||||
|
||||
if count == 1:
|
||||
if isinstance(segment,Horizontal):
|
||||
|
@ -375,14 +379,16 @@ class BufferInterface ( object ):
|
|||
|
||||
def __init__ ( self, framework ):
|
||||
trace( 550, ',+', '\tBufferInterface.__init__()\n' )
|
||||
self.masterCell = framework.getCell( Cfg.getParamString('clockTree.buffer').asString()
|
||||
self.maxSinks = Cfg.getParamInt('spares.maxSinks').asInt()
|
||||
self.masterCell = framework.getCell( Cfg.getParamString('spares.buffer').asString()
|
||||
, CRL.Catalog.State.Views )
|
||||
if not self.masterCell:
|
||||
trace( 550, '-' )
|
||||
raise ErrorMessage( 3, [ 'ClockTree: Buffer cell "{}" not found in library,' \
|
||||
.format(Cfg.getParamString('clockTree.buffer').asString())
|
||||
, ' please check the "clockTree.buffer" configuration parameter in "plugins.conf".' ] )
|
||||
trace( 550, '\tmasterCell :<{}>\n'.format(self.masterCell) )
|
||||
.format(Cfg.getParamString('spares.buffer').asString())
|
||||
, ' please check the "spares.buffer" configuration parameter in "plugins.conf".' ] )
|
||||
trace( 550, '\t| masterCell :{}\n'.format(self.masterCell) )
|
||||
trace( 550, '\t| maximum sinks:{}\n'.format(self.maxSinks) )
|
||||
|
||||
self.count = 0
|
||||
self.input = None
|
||||
|
@ -393,8 +399,8 @@ class BufferInterface ( object ):
|
|||
if net.getDirection() & Net.Direction.IN: self.input = net.getName()
|
||||
elif net.getDirection() & Net.Direction.OUT: self.output = net.getName()
|
||||
|
||||
trace( 550, '\tinput :<{}>\n'.format(self.input ) )
|
||||
trace( 550, '\toutput:<{}>\n'.format(self.output) )
|
||||
trace( 550, '\t| input :"{}"\n'.format(self.input ) )
|
||||
trace( 550, '\t| output :"{}"\n'.format(self.output) )
|
||||
trace( 550, '-' )
|
||||
return
|
||||
|
||||
|
@ -575,9 +581,11 @@ class BlockState ( object ):
|
|||
def __init__ ( self, cell, ioPins=[] ):
|
||||
self.editor = None
|
||||
self.framework = CRL.AllianceFramework.get()
|
||||
self.cfg = CfgCache('')
|
||||
self.cfg = CfgCache('',Cfg.Parameter.Priority.Interactive)
|
||||
self.gaugeConf = GaugeConf()
|
||||
self.bufferConf = BufferInterface( self.framework )
|
||||
self.bColumns = 2
|
||||
self.bRows = 2
|
||||
self.cell = cell
|
||||
self.fixedWidth = None
|
||||
self.fixedHeight = None
|
||||
|
|
|
@ -46,6 +46,116 @@ from plugins.alpha import utils
|
|||
framework = CRL.AllianceFramework.get()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Class : "spares.BufferPool".
|
||||
|
||||
class BufferPool ( object ):
|
||||
"""
|
||||
Manage a matrix of buffer and their allocations. One pool is created
|
||||
at the center of each QuadTree level (both terminal and non-terminal).
|
||||
"""
|
||||
|
||||
def __init__ ( self, quadTree ):
|
||||
self.quadTree = quadTree
|
||||
self.columns = quadTree.spares.state.bColumns
|
||||
self.rows = quadTree.spares.state.bRows
|
||||
self.buffers = []
|
||||
self.selectedIndex = None
|
||||
for i in range(self.rows*self.columns):
|
||||
self.buffers.append( [ 0, None ] )
|
||||
self._createBuffers()
|
||||
|
||||
@property
|
||||
def selected ( self ):
|
||||
"""
|
||||
Returns the selected buffer instance within the pool. Selection is to
|
||||
be made with either select() or selectFree().
|
||||
"""
|
||||
if self.selectedIndex is None:
|
||||
raise ErrorMessage( 3, 'BufferPool.selected: No buffer has been selected yet.' )
|
||||
return self.buffers[ self.selectedIndex ][ 1 ]
|
||||
|
||||
def toIndex ( self, column, row ): return column + row*self.columns
|
||||
|
||||
def fromIndex ( self, index ): return (index%self.columns, index/self.columns)
|
||||
|
||||
def select ( self, column, row, flags=0 ):
|
||||
"""
|
||||
Select a specific buffer in the pool matrix. The ``flags`` arguments are
|
||||
used to specify additional checks & actions. Flags operations are done by
|
||||
the low level function _select(). To actually access the selected buffer,
|
||||
use the selected property.
|
||||
|
||||
:param column: The colum of the buffer (aka X).
|
||||
:param row: The row of the buffer (aka Y).
|
||||
:param flags: A combination of flags, defined in the Spares object.
|
||||
|
||||
Flags can be used to:
|
||||
|
||||
* CHECK_USED, raise an error if the designated buffer is already used.
|
||||
* MARK_USED, tag the designated buffer as USED.
|
||||
"""
|
||||
|
||||
trace( 550, ',+', '\tBufferPool.select() column:{}, row={}, flags={:x}\n' \
|
||||
.format(column,row,flags) )
|
||||
if column >= self.columns:
|
||||
trace( 550, '-' )
|
||||
raise ErrorMessage( 3, 'BufferPool.select(): Column {} is out of range (max:{}).' \
|
||||
.format(column,self.columns) )
|
||||
if row >= self.rows:
|
||||
trace( 550, '-' )
|
||||
raise ErrorMessage( 3, 'BufferPool.select(): Row {} is out of range (max:{}).' \
|
||||
.format(row,self.rows) )
|
||||
self._select( self.toIndex( column, row ), flags )
|
||||
trace( 550, '-' )
|
||||
|
||||
def _select ( self, index, flags ):
|
||||
self.selectedIndex = index
|
||||
selectedBuffer = self.buffers[ self.selectedIndex ]
|
||||
if flags & Spares.CHECK_USED and selectedBuffer[0] & Spares.USED:
|
||||
raise ErrorMessage( 3, 'BufferPool.select(): Buffer a index {} is already used.' \
|
||||
.format(self.selectedIndex) )
|
||||
if flags & Spares.MARK_USED:
|
||||
selectedBuffer[0] |= Spares.USED
|
||||
|
||||
def selectFree ( self ):
|
||||
"""Select the first free buffer available."""
|
||||
for i in range(self.rows*self.columns):
|
||||
if not (self.buffers[i][0] & Spares.USED):
|
||||
self._select( i, Spares.MARK_USED )
|
||||
|
||||
def _createBuffers ( self ):
|
||||
"""Create the matrix of instances buffer."""
|
||||
trace( 550, ',+', '\tBufferPool.createBuffers()\n' )
|
||||
|
||||
state = self.quadTree.spares.state
|
||||
sliceHeight = state.gaugeConf.sliceHeight
|
||||
x = self.quadTree.onXPitch( self.quadTree.area.getXCenter()
|
||||
- (state.bufferConf.width * self.columns)/2 )
|
||||
y = self.quadTree.onYSlice( self.quadTree.area.getYCenter()
|
||||
- (state.bufferConf.height * self.rows)/2 )
|
||||
slice = y / sliceHeight
|
||||
|
||||
trace( 550, '\tSlice height: {}\n'.format(DbU.getValueString(sliceHeight)) )
|
||||
|
||||
for row in range(self.rows):
|
||||
orientation = Transformation.Orientation.ID
|
||||
y = (slice+row) * sliceHeight
|
||||
if (slice+row)%2:
|
||||
orientation = Transformation.Orientation.MY
|
||||
y += sliceHeight
|
||||
for column in range(self.columns):
|
||||
index = self.toIndex(column,row)
|
||||
transf = Transformation( x + column*state.bufferConf.width, y, orientation )
|
||||
instance = state.createBuffer()
|
||||
instance.setTransformation( transf )
|
||||
instance.setPlacementStatus( Instance.PlacementStatus.FIXED )
|
||||
self.buffers[ index ][1] = instance
|
||||
trace( 550, '\tBuffer[{}]: {} @{}\n'.format(index,self.buffers[index],transf) )
|
||||
trace( 550, '-' )
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Class : "spares.QuadTree".
|
||||
|
||||
|
@ -61,7 +171,6 @@ class QuadTree ( object ):
|
|||
def create ( spares ):
|
||||
root = QuadTree( spares, None, spares.state.cell.getAbutmentBox() )
|
||||
root.rpartition()
|
||||
root.rcreateBuffers()
|
||||
return root
|
||||
|
||||
def __init__ ( self, spares, parent, area, rtag='root' ):
|
||||
|
@ -74,16 +183,23 @@ class QuadTree ( object ):
|
|||
self.br = None
|
||||
self.tl = None
|
||||
self.tr = None
|
||||
self.buseds = 0
|
||||
self.bufferTag = 'spare'
|
||||
self.bufferNet = None
|
||||
self.buffers = []
|
||||
self.pool = BufferPool( self )
|
||||
self.plugs = []
|
||||
if self.parent and self.parent.rtag != '':
|
||||
self.rtag = self.parent.rtag + '_' + rtag
|
||||
else:
|
||||
self.rtag = rtag
|
||||
|
||||
def __str__ ( self ):
|
||||
s = '<QuadTree [{},{} {},{}] "{}">'.format( DbU.getValueString(self.area.getXMin())
|
||||
, DbU.getValueString(self.area.getYMin())
|
||||
, DbU.getValueString(self.area.getXMax())
|
||||
, DbU.getValueString(self.area.getYMax())
|
||||
, self.rtag )
|
||||
return s
|
||||
|
||||
@property
|
||||
def leafs ( self ):
|
||||
activeLeafs = []
|
||||
|
@ -101,6 +217,9 @@ class QuadTree ( object ):
|
|||
def isHBipart ( self ): return self.ycut is None
|
||||
def isVBipart ( self ): return self.xcut is None
|
||||
|
||||
def isRoot ( self ):
|
||||
return self.parent is None
|
||||
|
||||
def isLeaf ( self ):
|
||||
for leaf in (self.bl, self.br, self.tl, self.tr):
|
||||
if leaf is not None: return False
|
||||
|
@ -109,7 +228,7 @@ class QuadTree ( object ):
|
|||
@property
|
||||
def buffer ( self ):
|
||||
"""The the currently selected buffer instance in the pool."""
|
||||
return self.buffers[self.buseds]
|
||||
return self.pool.selected
|
||||
|
||||
@property
|
||||
def bInputPlug ( self ):
|
||||
|
@ -121,7 +240,17 @@ class QuadTree ( object ):
|
|||
"""The output Plug of the currently selected buffer in the pool."""
|
||||
return utils.getPlugByName( self.buffer, self.spares.state.bufferConf.output )
|
||||
|
||||
def useBuffer ( self, doLeaf=False ):
|
||||
def onYSlice ( self, y ):
|
||||
"""Returns the coordinate of the slice immediately inferior to Y."""
|
||||
modulo = (y - self.area.getYMin()) % self.spares.state.gaugeConf.sliceHeight
|
||||
return y - modulo
|
||||
|
||||
def onXPitch ( self, x ):
|
||||
"""Returns the coordinate of the pitch immediately inferior to X."""
|
||||
modulo = (x - self.area.getXMin()) % self.spares.state.gaugeConf.sliceStep
|
||||
return x - modulo
|
||||
|
||||
def connectBuffer ( self, doLeaf=False ):
|
||||
"""
|
||||
Create output nets for the currently selected buffer, if they do not
|
||||
already exists. The nets are created in the top level cell, and their
|
||||
|
@ -129,38 +258,49 @@ class QuadTree ( object ):
|
|||
"""
|
||||
if self.isLeaf() and not doLeaf: return
|
||||
|
||||
trace( 550, '\tQuadTree.useBuffer(): rtag:"{}"\n'.format(self.rtag) )
|
||||
trace( 550, '\tQuadTree.connectBuffer(): rtag:"{}"\n'.format(self.rtag) )
|
||||
plug = self.bOutputPlug
|
||||
if not plug.getNet():
|
||||
outputNetBuff = Net.create( self.spares.state.cell,'{}_{}_{}' \
|
||||
.format(self.root.bufferTag,self.buseds,self.rtag) )
|
||||
outputNetBuff = Net.create( self.spares.state.cell,'{}_{}' \
|
||||
.format(self.root.bufferTag,self.rtag) )
|
||||
plug.setNet( outputNetBuff )
|
||||
trace( 550, '\t| {}\n'.format(plug) )
|
||||
trace( 550, '\t| {}\n'.format(outputNetBuff) )
|
||||
|
||||
def ruseBuffer ( self ):
|
||||
"""Recursive call of useBuffer()"""
|
||||
self.useBuffer()
|
||||
def rconnectBuffer ( self ):
|
||||
"""[R]ecursive call of connectBuffer()"""
|
||||
self.connectBuffer()
|
||||
for leaf in self.leafs:
|
||||
leaf.ruseBuffer()
|
||||
leaf.rconnectBuffer()
|
||||
|
||||
def useNextBuffer ( self ):
|
||||
def rselectBuffer ( self, column, row, flags=0 ):
|
||||
"""
|
||||
Reset the quadtree buffering cache in order to buffer the next net.
|
||||
Flush the plugs list and increase the buffer counter (buseds).
|
||||
Setup the buffer to be used for the next buffering operation.
|
||||
For a more detailed explanation of the parameter, please refer
|
||||
to BufferPool.select().
|
||||
"""
|
||||
trace( 550, '+' )
|
||||
if self.plugs:
|
||||
self.plugs = []
|
||||
self.buseds += 1
|
||||
self.pool.select( column, row, flags )
|
||||
if not self.isLeaf():
|
||||
for leaf in self.leafs: leaf.useNextBuffer()
|
||||
for leaf in self.leafs: leaf.rselectBuffer( column, row, flags )
|
||||
trace( 550, '-' )
|
||||
|
||||
def partition ( self ):
|
||||
trace( 550, ',+', '\tQuadTree.partition(): {}\n'.format(self.area) )
|
||||
"""
|
||||
Build one level of the quad-tree, if the side of the area is bigger than
|
||||
twice the "block.spareSide" value.
|
||||
|
||||
Depending on the initial aspect ratio, the first levels *may* not be a
|
||||
quad-tree, but only a vertical or horizontal bi-partition.
|
||||
"""
|
||||
trace( 550, ',+', '\tQuadTree.partition(): {} (spareSide:{})\n' \
|
||||
.format(self.area, DbU.getValueString(self.spares.state.cfg.block.spareSide)) )
|
||||
|
||||
spareSide = self.spares.state.cfg.block.spareSide
|
||||
sliceHeight = self.spares.state.gaugeConf.sliceHeight
|
||||
side = float(spareSide * sliceHeight)
|
||||
side = float(spareSide)
|
||||
aspectRatio = float(self.area.getWidth()) / float(self.area.getHeight())
|
||||
|
||||
if self.area.getHeight() < side*2.0 or self.area.getWidth () < side*2.0:
|
||||
|
@ -243,6 +383,7 @@ class QuadTree ( object ):
|
|||
return True
|
||||
|
||||
def rpartition ( self ):
|
||||
""""[R]ecursively partition the the area."""
|
||||
trace( 550, ',+', '\tQuadTree.rpartition(): {}\n'.format(self.area) )
|
||||
if self.partition():
|
||||
for leaf in self.leafs:
|
||||
|
@ -250,40 +391,6 @@ class QuadTree ( object ):
|
|||
leaf.rpartition()
|
||||
trace( 550, '-' )
|
||||
|
||||
def createBuffers ( self ):
|
||||
trace( 550, ',+', '\tQuadTree.createBuffers()\n' )
|
||||
#if not self.isLeaf(): return
|
||||
|
||||
state = self.spares.state
|
||||
x = self.area.getXCenter() - state.bufferConf.width
|
||||
y = self.area.getYCenter() - state.bufferConf.height
|
||||
slice = y / state.gaugeConf.sliceHeight - 1
|
||||
|
||||
for slice in range(slice,slice+2):
|
||||
orientation = Transformation.Orientation.ID
|
||||
yadjust = 0
|
||||
if slice%2:
|
||||
orientation = Transformation.Orientation.MY
|
||||
yadjust = 2 * state.gaugeConf.sliceHeight
|
||||
for i in range(2):
|
||||
instance = state.createBuffer()
|
||||
instance.setTransformation( Transformation( x + i*state.bufferConf.width
|
||||
, y + yadjust
|
||||
, orientation ) )
|
||||
instance.setPlacementStatus( Instance.PlacementStatus.FIXED )
|
||||
self.buffers.append( instance )
|
||||
trace( 550, '\tBuffer: {}\n'.format(self.buffers[-1]) )
|
||||
trace( 550, '-' )
|
||||
|
||||
def rcreateBuffers ( self ):
|
||||
self.createBuffers()
|
||||
trace( 550, ',+' )
|
||||
if self.bl: self.bl.rcreateBuffers()
|
||||
if self.br: self.br.rcreateBuffers()
|
||||
if self.tl: self.tl.rcreateBuffers()
|
||||
if self.tr: self.tr.rcreateBuffers()
|
||||
trace( 550, '-' )
|
||||
|
||||
def getLeafUnder ( self, position ):
|
||||
"""Find the QuadTree leaf under `position`."""
|
||||
if self.isLeaf(): return self
|
||||
|
@ -300,6 +407,7 @@ class QuadTree ( object ):
|
|||
return self.tr.getLeafUnder(position)
|
||||
|
||||
def attachToLeaf ( self, plugOccurrence ):
|
||||
"""Assign the plug occurrence to the QuadTree leaf it is under."""
|
||||
position = plugOccurrence.getBoundingBox().getCenter()
|
||||
self.getLeafUnder(position).plugs.append( plugOccurrence )
|
||||
|
||||
|
@ -325,7 +433,7 @@ class QuadTree ( object ):
|
|||
if not self.plugs: return
|
||||
|
||||
trace( 550, ',+', '\tQuadTree.spliNetlist()\n' )
|
||||
self.useBuffer( doLeaf=True )
|
||||
self.connectBuffer( doLeaf=True )
|
||||
netBuff = self.bOutputPlug.getNet()
|
||||
trace( 550, '\tBuffer: {}\n'.format(self.buffer) )
|
||||
trace( 550, '\tBuffer output: {}\n'.format(netBuff) )
|
||||
|
@ -338,6 +446,11 @@ class QuadTree ( object ):
|
|||
deepNetBuff = deepPlug.getMasterNet() if deepPlug else netBuff
|
||||
trace( 550, '\t| deepNetBuff: {} {}\n'.format(deepNetBuff,netBuff) )
|
||||
plug.getEntity().setNet( deepNetBuff )
|
||||
|
||||
maxSinks = self.spares.state.bufferConf.maxSinks
|
||||
if len(self.plugs) > maxSinks:
|
||||
print( WarningMessage( 'QuadTree.splitNetlist(): More than {} sink points ({}) on "{}".' \
|
||||
.format(maxSinks,len(self.plugs),netBuff.getName())) )
|
||||
trace( 550, '-' )
|
||||
|
||||
def rsplitNetlist ( self ):
|
||||
|
@ -358,6 +471,10 @@ class Spares ( object ):
|
|||
Excess area is put in the topmost and rightmost pools.
|
||||
"""
|
||||
|
||||
USED = 0x00000001
|
||||
CHECK_USED = 0x00010000
|
||||
MARK_USED = 0x00020000
|
||||
|
||||
def __init__ ( self, block ):
|
||||
self.state = block.state
|
||||
self.quadTree = None
|
||||
|
@ -370,8 +487,13 @@ class Spares ( object ):
|
|||
"""
|
||||
if not self.state.useSpares: return 0.0
|
||||
spareSide = self.state.cfg.block.spareSide
|
||||
if not spareSide:
|
||||
raise ErrorMessage( 3, 'Spares.getSpareSpaceMargin(): "block.spareSide" parameter is zero ({}).' \
|
||||
.format(spareSide) )
|
||||
areaLength = spareSide * spareSide / self.state.gaugeConf.sliceHeight
|
||||
bufferLength = self.state.bufferConf.width * 4
|
||||
bufferLength = self.state.bufferConf.width * self.state.bColumns * self.state.bRows
|
||||
if not areaLength:
|
||||
raise ErrorMessage( 3, 'Spares.getSpareSpaceMargin(): Spare leaf area is zero.' )
|
||||
return float(bufferLength) / float(areaLength)
|
||||
|
||||
def toXGCellGrid ( self, x ):
|
||||
|
|
|
@ -182,6 +182,8 @@ namespace Etesian {
|
|||
, getString(instanceOcc).c_str() ) << endl;
|
||||
return Box();
|
||||
}
|
||||
if (instance->getPlacementStatus() == Instance::PlacementStatus::FIXED)
|
||||
return instance->getMasterCell()->getAbutmentBox();
|
||||
|
||||
DbU::Unit dx = 0;
|
||||
Cell* cell = instance->getMasterCell();
|
||||
|
|
|
@ -670,6 +670,8 @@ namespace Etesian {
|
|||
|coloquinte::YFlippable;
|
||||
} else {
|
||||
instances[instanceId].attributes = 0;
|
||||
//cerr << "FIXED: " << instance << " size:(" << xsize << " " << ysize
|
||||
// << ") pos:(" << xpos << " " << ypos << ")" << endl;
|
||||
}
|
||||
|
||||
_cellsToIds.insert( make_pair(instanceName,instanceId) );
|
||||
|
|
Loading…
Reference in New Issue