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:
Jean-Paul Chaput 2020-08-11 14:49:07 +02:00
parent 38375fd9ae
commit 51e3639687
13 changed files with 394 additions and 115 deletions

View File

@ -27,3 +27,6 @@ Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) ) Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2') Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian') Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )

View File

@ -27,3 +27,6 @@ Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) ) Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2') Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian') Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )

View File

@ -27,3 +27,6 @@ Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) ) Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) )
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2') Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian') Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )

View File

@ -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.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.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.termSatReservedLocal" ); p.setInt ( 8 )
p = Cfg.getParamInt ( "katana.termSatThreshold" ); p.setInt ( 9 ) p = Cfg.getParamInt ( "katana.termSatThreshold" ); p.setInt ( 9 )
p = Cfg.getParamInt ( "katana.eventsLimit" ); p.setInt ( 4000002 ) p = Cfg.getParamInt ( "katana.eventsLimit" ); p.setInt ( 4000002 )

View File

@ -28,3 +28,6 @@ Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(600) )
Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2') Cfg.getParamString( 'clockTree.buffer' ).setString( 'buf_x2')
Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian') Cfg.getParamString( 'clockTree.placerEngine' ).setString( 'Etesian')
Cfg.getParamInt ( 'block.spareSide' ).setInt ( 10 ) Cfg.getParamInt ( 'block.spareSide' ).setInt ( 10 )
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )

View File

@ -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.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.vTracksReservedLocal" ); p.setInt ( 3 ); p.setMin(0); p.setMax(20)
p = Cfg.getParamInt ( "katana.termSatReservedLocal" ); p.setInt ( 8 ) 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.termSatThreshold" ); p.setInt ( 9 )
p = Cfg.getParamInt ( "katana.eventsLimit" ); p.setInt ( 4000002 ) p = Cfg.getParamInt ( "katana.eventsLimit" ); p.setInt ( 4000002 )
p = Cfg.getParamInt ( "katana.ripupCost" ); p.setInt ( 3 ); p.setMin(0) 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.globalRipupLimit" ); p.setInt ( 5 ); p.setMin(1)
p = Cfg.getParamInt ( "katana.longGlobalRipupLimit" ); 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.getParamString( 'chip.padCoreSide' ); p.setString( 'South' )
p = Cfg.getParamInt ( "block.spareSide" ); p.setInt ( l(2000) )
tech = DataBase.getDB().getTechnology() tech = DataBase.getDB().getTechnology()
af = AllianceFramework.get() 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 = RoutingGauge.create( 'msxlib' )
rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL1') # metal. rg.addLayerGauge( RoutingLayerGauge.create( tech.getLayer('METAL1') # metal.
@ -237,6 +315,13 @@ af.addRoutingGauge( rg )
af.setRoutingGauge( 'msxlib' ) af.setRoutingGauge( 'msxlib' )
# Gauge for standard cells. # 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' cg = CellGauge.create( 'msxlib'
, 'metal2' # pin layer name. , 'metal2' # pin layer name.
, l( 10.0) # pitch. , l( 10.0) # pitch.

View File

@ -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.hSpacing" ).setInt ( l( 12) )
Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) ) Cfg.getParamInt ( "chip.block.rails.vSpacing" ).setInt ( l( 12) )
Cfg.getParamInt ( 'clockTree.minimumSide' ).setInt ( l(1200) ) 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( 'clockTree.placerEngine' ).setString( 'Etesian')
Cfg.getParamString( 'spares.buffer' ).setString( 'buf_x8')
Cfg.getParamInt ( 'spares.maxSinks' ).setInt ( 31 )

View File

@ -173,6 +173,9 @@ class Side ( object ):
if net is None: if net is None:
print( ErrorMessage( 1, [ 'Side.place(IoPin): No net named "{}".'.format(pinName) ] )) print( ErrorMessage( 1, [ 'Side.place(IoPin): No net named "{}".'.format(pinName) ] ))
continue 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)) pinName += '.{}'.format(self.block.state.getIoPinsCounts(net))
pinPos = self.getNextPinPosition( ioPin.flags, upos, ioPin.ustep ) pinPos = self.getNextPinPosition( ioPin.flags, upos, ioPin.ustep )
if pinPos.getX() > self.block.state.xMax or pinPos.getX() < self.block.state.xMin: 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() self.state.cfg.apply()
for side in self.sides.values(): side.setupAb() for side in self.sides.values(): side.setupAb()
def addClockTree ( self, clockNet=None ): def addClockTrees ( self ):
"""Create the trunk of the clock tree (recursive H-Tree).""" """Create the trunk of all the clock trees (recursive H-Tree)."""
if not clockNet: print( ' o Buildding clock tree(s).' )
af = CRL.AllianceFramework.get()
clockNets = []
for net in self.state.cell.getNets(): for net in self.state.cell.getNets():
if af.isCLOCK(net.getName()): 'CLOCK: {}'.format(net)
if net.isClock(): if net.isClock():
clockNet = net trace( 550, '\tBlock.addClockTrees(): Found clock {}.\n'.format(net) )
break clockNets.append( net )
if not clockNet: if not clockNets:
raise ErrorMessage( 3, 'Block.clockTree(): Cell "{}" has no clock net.'.format(self.state.cell.getName()) ) raise ErrorMessage( 3, 'Block.clockTree(): Cell "{}" has no clock net(s).'.format(self.state.cell.getName()) )
with UpdateSession(): 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() self.clockTrees[-1].buildHTree()
return self.clockTrees[-1] trace( 550, '-' )
def splitClocks ( self ): def splitClocks ( self ):
""" """
@ -428,6 +437,10 @@ class Block ( object ):
.format(net.getName() ))) .format(net.getName() )))
def expandIoPins ( self ): 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(): with UpdateSession():
for side in self.sides.values(): for side in self.sides.values():
side.expand() side.expand()
@ -502,7 +515,8 @@ class Block ( object ):
self.checkIoPins() self.checkIoPins()
self.spares.build() self.spares.build()
if editor: editor.fit() 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() self.place()
if self.state.useClockTree: self.splitClocks() if self.state.useClockTree: self.splitClocks()
status = self.route() status = self.route()

View File

@ -44,6 +44,7 @@ from helpers.overlay import UpdateSession
from plugins import getParameter from plugins import getParameter
from plugins import utils from plugins import utils
from plugins.alpha.block.configuration import GaugeConf 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. Build a clock tree on a block.
""" """
def __init__ ( self, spares, clockNet ): def __init__ ( self, spares, clockNet, index ):
self.spares = spares self.spares = spares
self.clockNet = clockNet self.clockNet = clockNet
self.clockIndex = index
if not self.clockNet.isClock():
print( WarningMessage( 'ClockTree.__init__(): Net "{}" is not of CLOCK type.' \ print( WarningMessage( 'ClockTree.__init__(): Net "{}" is not of CLOCK type.' \
.format(self.clockNet.getName()) )) .format(self.clockNet.getName()) ))
def _rconnectHTree ( self, quadTree ): def _rconnectHTree ( self, qt ):
if quadTree.isLeaf(): return False if qt.isLeaf(): return False
driverNet = quadTree.bOutputPlug.getNet() qt.rconnectBuffer()
for leaf in quadTree.leafs: driverNet = qt.bOutputPlug.getNet()
for leaf in qt.leafs:
leaf.bInputPlug.setNet( driverNet ) leaf.bInputPlug.setNet( driverNet )
self._rconnectHTree( leaf ) self._rconnectHTree( leaf )
return True return True
def _rrouteHTree ( self, quadT ): def _rrouteHTree ( self, qt ):
""" """
Recursively build one HTree branch for all non-terminal nodes of the QuadTree. Recursively build one HTree branch for all non-terminal nodes of the QuadTree.
""" """
trace( 550, ',+', '\tClockTree._rrouteHTree() {}\n'.format(quadT.bOutputPlug.getNet()) ) trace( 550, ',+', '\tClockTree._rrouteHTree() {}\n'.format(qt.bOutputPlug.getNet()) )
if quadT.isLeaf(): trace( 550, '\tOn: {}\n'.format(qt) )
if qt.isLeaf():
trace( 550, '-' ) trace( 550, '-' )
return False return False
gaugeConf = self.spares.state.gaugeConf gaugeConf = self.spares.state.gaugeConf
bufferConf = self.spares.state.bufferConf 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 ) leftSourceContact = gaugeConf.rpAccessByPlugName( qt.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 )
rightSourceContact = gaugeConf.rpAccessByPlugName( quadT.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 ) rightSourceContact = gaugeConf.rpAccessByPlugName( qt.buffer , bufferConf.output, ckNet , GaugeConf.HAccess|GaugeConf.OffsetBottom1 )
blContact = gaugeConf.rpAccessByPlugName( quadT.bl.buffer, bufferConf.input , ckNet ) blContact = gaugeConf.rpAccessByPlugName( qt.bl.buffer, bufferConf.input , ckNet )
brContact = gaugeConf.rpAccessByPlugName( quadT.br.buffer, bufferConf.input , ckNet ) brContact = gaugeConf.rpAccessByPlugName( qt.br.buffer, bufferConf.input , ckNet )
tlContact = gaugeConf.rpAccessByPlugName( quadT.tl.buffer, bufferConf.input , ckNet ) tlContact = gaugeConf.rpAccessByPlugName( qt.tl.buffer, bufferConf.input , ckNet )
trContact = gaugeConf.rpAccessByPlugName( quadT.tr.buffer, bufferConf.input , ckNet ) trContact = gaugeConf.rpAccessByPlugName( qt.tr.buffer, bufferConf.input , ckNet )
leftContact = gaugeConf.createContact( ckNet, blContact.getX(), leftSourceContact.getY(), 0 ) leftContact = gaugeConf.createContact( ckNet, blContact.getX(), leftSourceContact.getY(), 0 )
rightContact = gaugeConf.createContact( ckNet, brContact.getX(), rightSourceContact.getY(), 0 ) rightContact = gaugeConf.createContact( ckNet, brContact.getX(), rightSourceContact.getY(), 0 )
leftSourceX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, leftSourceContact.getX(), 0 ) leftSourceX = gaugeConf.getNearestVerticalTrack ( qt.root.area, leftSourceContact.getX(), 0 )
leftSourceY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, leftSourceContact.getY(), 0 ) leftSourceY = gaugeConf.getNearestHorizontalTrack( qt.root.area, leftSourceContact.getY(), 0 )
rightSourceX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, rightSourceContact.getX(), 0 ) rightSourceX = gaugeConf.getNearestVerticalTrack ( qt.root.area, rightSourceContact.getX(), 0 )
rightSourceY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, rightSourceContact.getY(), 0 ) rightSourceY = gaugeConf.getNearestHorizontalTrack( qt.root.area, rightSourceContact.getY(), 0 )
leftX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, leftContact.getX(), 0 ) leftX = gaugeConf.getNearestVerticalTrack ( qt.root.area, leftContact.getX(), 0 )
rightX = gaugeConf.getNearestVerticalTrack ( quadT.root.area, rightContact.getX(), 0 ) rightX = gaugeConf.getNearestVerticalTrack ( qt.root.area, rightContact.getX(), 0 )
tlY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, tlContact.getY(), 0 ) tlY = gaugeConf.getNearestHorizontalTrack( qt.root.area, tlContact.getY(), 0 )
blY = gaugeConf.getNearestHorizontalTrack( quadT.root.area, blContact.getY(), 0 ) blY = gaugeConf.getNearestHorizontalTrack( qt.root.area, blContact.getY(), 0 )
gaugeConf.setStackPosition( leftSourceContact, leftSourceX, leftSourceY ) gaugeConf.setStackPosition( leftSourceContact, leftSourceX, leftSourceY )
gaugeConf.setStackPosition( rightSourceContact, rightSourceX, rightSourceY ) gaugeConf.setStackPosition( rightSourceContact, rightSourceX, rightSourceY )
@ -119,7 +123,33 @@ class ClockTree ( object ):
gaugeConf.createVertical ( rightContact , brContact , rightX , 0 ) gaugeConf.createVertical ( rightContact , brContact , rightX , 0 )
gaugeConf.createVertical ( trContact , rightContact , 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 ) self._rrouteHTree( leaf )
trace( 550, '-' ) trace( 550, '-' )
return True return True
@ -131,12 +161,12 @@ class ClockTree ( object ):
2. Detach the all the clock sink point and reconnect them to the 2. Detach the all the clock sink point and reconnect them to the
buffers of the leafs of the QuadTree. buffers of the leafs of the QuadTree.
""" """
quadTree = self.spares.quadTree qt = self.spares.quadTree
quadTree.bufferTag = self.clockNet.getName() qt.bufferTag = self.clockNet.getName()
quadTree.ruseBuffer() qt.rselectBuffer( self.clockIndex, self.clockIndex, Spares.CHECK_USED|Spares.MARK_USED)
with UpdateSession(): with UpdateSession():
self._rconnectHTree( quadTree ) self._rconnectHTree( qt )
self._rrouteHTree ( quadTree ) self._rrouteHTree ( qt )
def splitClock ( self ): def splitClock ( self ):
""" """

View File

@ -356,7 +356,11 @@ class GaugeConf ( object ):
segment = component segment = component
count += 1 count += 1
if 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 count == 1:
if isinstance(segment,Horizontal): if isinstance(segment,Horizontal):
@ -375,14 +379,16 @@ class BufferInterface ( object ):
def __init__ ( self, framework ): def __init__ ( self, framework ):
trace( 550, ',+', '\tBufferInterface.__init__()\n' ) 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 ) , CRL.Catalog.State.Views )
if not self.masterCell: if not self.masterCell:
trace( 550, '-' ) trace( 550, '-' )
raise ErrorMessage( 3, [ 'ClockTree: Buffer cell "{}" not found in library,' \ raise ErrorMessage( 3, [ 'ClockTree: Buffer cell "{}" not found in library,' \
.format(Cfg.getParamString('clockTree.buffer').asString()) .format(Cfg.getParamString('spares.buffer').asString())
, ' please check the "clockTree.buffer" configuration parameter in "plugins.conf".' ] ) , ' please check the "spares.buffer" configuration parameter in "plugins.conf".' ] )
trace( 550, '\tmasterCell :<{}>\n'.format(self.masterCell) ) trace( 550, '\t| masterCell :{}\n'.format(self.masterCell) )
trace( 550, '\t| maximum sinks:{}\n'.format(self.maxSinks) )
self.count = 0 self.count = 0
self.input = None self.input = None
@ -393,8 +399,8 @@ class BufferInterface ( object ):
if net.getDirection() & Net.Direction.IN: self.input = net.getName() if net.getDirection() & Net.Direction.IN: self.input = net.getName()
elif net.getDirection() & Net.Direction.OUT: self.output = net.getName() elif net.getDirection() & Net.Direction.OUT: self.output = net.getName()
trace( 550, '\tinput :<{}>\n'.format(self.input ) ) trace( 550, '\t| input :"{}"\n'.format(self.input ) )
trace( 550, '\toutput:<{}>\n'.format(self.output) ) trace( 550, '\t| output :"{}"\n'.format(self.output) )
trace( 550, '-' ) trace( 550, '-' )
return return
@ -575,9 +581,11 @@ class BlockState ( object ):
def __init__ ( self, cell, ioPins=[] ): def __init__ ( self, cell, ioPins=[] ):
self.editor = None self.editor = None
self.framework = CRL.AllianceFramework.get() self.framework = CRL.AllianceFramework.get()
self.cfg = CfgCache('') self.cfg = CfgCache('',Cfg.Parameter.Priority.Interactive)
self.gaugeConf = GaugeConf() self.gaugeConf = GaugeConf()
self.bufferConf = BufferInterface( self.framework ) self.bufferConf = BufferInterface( self.framework )
self.bColumns = 2
self.bRows = 2
self.cell = cell self.cell = cell
self.fixedWidth = None self.fixedWidth = None
self.fixedHeight = None self.fixedHeight = None

View File

@ -46,6 +46,116 @@ from plugins.alpha import utils
framework = CRL.AllianceFramework.get() 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". # Class : "spares.QuadTree".
@ -61,7 +171,6 @@ class QuadTree ( object ):
def create ( spares ): def create ( spares ):
root = QuadTree( spares, None, spares.state.cell.getAbutmentBox() ) root = QuadTree( spares, None, spares.state.cell.getAbutmentBox() )
root.rpartition() root.rpartition()
root.rcreateBuffers()
return root return root
def __init__ ( self, spares, parent, area, rtag='root' ): def __init__ ( self, spares, parent, area, rtag='root' ):
@ -74,16 +183,23 @@ class QuadTree ( object ):
self.br = None self.br = None
self.tl = None self.tl = None
self.tr = None self.tr = None
self.buseds = 0
self.bufferTag = 'spare' self.bufferTag = 'spare'
self.bufferNet = None self.bufferNet = None
self.buffers = [] self.pool = BufferPool( self )
self.plugs = [] self.plugs = []
if self.parent and self.parent.rtag != '': if self.parent and self.parent.rtag != '':
self.rtag = self.parent.rtag + '_' + rtag self.rtag = self.parent.rtag + '_' + rtag
else: else:
self.rtag = rtag 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 @property
def leafs ( self ): def leafs ( self ):
activeLeafs = [] activeLeafs = []
@ -101,6 +217,9 @@ class QuadTree ( object ):
def isHBipart ( self ): return self.ycut is None def isHBipart ( self ): return self.ycut is None
def isVBipart ( self ): return self.xcut is None def isVBipart ( self ): return self.xcut is None
def isRoot ( self ):
return self.parent is None
def isLeaf ( self ): def isLeaf ( self ):
for leaf in (self.bl, self.br, self.tl, self.tr): for leaf in (self.bl, self.br, self.tl, self.tr):
if leaf is not None: return False if leaf is not None: return False
@ -109,7 +228,7 @@ class QuadTree ( object ):
@property @property
def buffer ( self ): def buffer ( self ):
"""The the currently selected buffer instance in the pool.""" """The the currently selected buffer instance in the pool."""
return self.buffers[self.buseds] return self.pool.selected
@property @property
def bInputPlug ( self ): def bInputPlug ( self ):
@ -121,7 +240,17 @@ class QuadTree ( object ):
"""The output Plug of the currently selected buffer in the pool.""" """The output Plug of the currently selected buffer in the pool."""
return utils.getPlugByName( self.buffer, self.spares.state.bufferConf.output ) 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 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 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 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 plug = self.bOutputPlug
if not plug.getNet(): if not plug.getNet():
outputNetBuff = Net.create( self.spares.state.cell,'{}_{}_{}' \ outputNetBuff = Net.create( self.spares.state.cell,'{}_{}' \
.format(self.root.bufferTag,self.buseds,self.rtag) ) .format(self.root.bufferTag,self.rtag) )
plug.setNet( outputNetBuff ) plug.setNet( outputNetBuff )
trace( 550, '\t| {}\n'.format(plug) ) trace( 550, '\t| {}\n'.format(plug) )
trace( 550, '\t| {}\n'.format(outputNetBuff) ) trace( 550, '\t| {}\n'.format(outputNetBuff) )
def ruseBuffer ( self ): def rconnectBuffer ( self ):
"""Recursive call of useBuffer()""" """[R]ecursive call of connectBuffer()"""
self.useBuffer() self.connectBuffer()
for leaf in self.leafs: 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. Setup the buffer to be used for the next buffering operation.
Flush the plugs list and increase the buffer counter (buseds). For a more detailed explanation of the parameter, please refer
to BufferPool.select().
""" """
trace( 550, '+' )
if self.plugs: if self.plugs:
self.plugs = [] self.plugs = []
self.buseds += 1 self.pool.select( column, row, flags )
if not self.isLeaf(): 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 ): 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 spareSide = self.spares.state.cfg.block.spareSide
sliceHeight = self.spares.state.gaugeConf.sliceHeight sliceHeight = self.spares.state.gaugeConf.sliceHeight
side = float(spareSide * sliceHeight) side = float(spareSide)
aspectRatio = float(self.area.getWidth()) / float(self.area.getHeight()) aspectRatio = float(self.area.getWidth()) / float(self.area.getHeight())
if self.area.getHeight() < side*2.0 or self.area.getWidth () < side*2.0: if self.area.getHeight() < side*2.0 or self.area.getWidth () < side*2.0:
@ -243,6 +383,7 @@ class QuadTree ( object ):
return True return True
def rpartition ( self ): def rpartition ( self ):
""""[R]ecursively partition the the area."""
trace( 550, ',+', '\tQuadTree.rpartition(): {}\n'.format(self.area) ) trace( 550, ',+', '\tQuadTree.rpartition(): {}\n'.format(self.area) )
if self.partition(): if self.partition():
for leaf in self.leafs: for leaf in self.leafs:
@ -250,40 +391,6 @@ class QuadTree ( object ):
leaf.rpartition() leaf.rpartition()
trace( 550, '-' ) 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 ): def getLeafUnder ( self, position ):
"""Find the QuadTree leaf under `position`.""" """Find the QuadTree leaf under `position`."""
if self.isLeaf(): return self if self.isLeaf(): return self
@ -300,6 +407,7 @@ class QuadTree ( object ):
return self.tr.getLeafUnder(position) return self.tr.getLeafUnder(position)
def attachToLeaf ( self, plugOccurrence ): def attachToLeaf ( self, plugOccurrence ):
"""Assign the plug occurrence to the QuadTree leaf it is under."""
position = plugOccurrence.getBoundingBox().getCenter() position = plugOccurrence.getBoundingBox().getCenter()
self.getLeafUnder(position).plugs.append( plugOccurrence ) self.getLeafUnder(position).plugs.append( plugOccurrence )
@ -325,7 +433,7 @@ class QuadTree ( object ):
if not self.plugs: return if not self.plugs: return
trace( 550, ',+', '\tQuadTree.spliNetlist()\n' ) trace( 550, ',+', '\tQuadTree.spliNetlist()\n' )
self.useBuffer( doLeaf=True ) self.connectBuffer( doLeaf=True )
netBuff = self.bOutputPlug.getNet() netBuff = self.bOutputPlug.getNet()
trace( 550, '\tBuffer: {}\n'.format(self.buffer) ) trace( 550, '\tBuffer: {}\n'.format(self.buffer) )
trace( 550, '\tBuffer output: {}\n'.format(netBuff) ) trace( 550, '\tBuffer output: {}\n'.format(netBuff) )
@ -338,6 +446,11 @@ class QuadTree ( object ):
deepNetBuff = deepPlug.getMasterNet() if deepPlug else netBuff deepNetBuff = deepPlug.getMasterNet() if deepPlug else netBuff
trace( 550, '\t| deepNetBuff: {} {}\n'.format(deepNetBuff,netBuff) ) trace( 550, '\t| deepNetBuff: {} {}\n'.format(deepNetBuff,netBuff) )
plug.getEntity().setNet( deepNetBuff ) 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, '-' ) trace( 550, '-' )
def rsplitNetlist ( self ): def rsplitNetlist ( self ):
@ -358,6 +471,10 @@ class Spares ( object ):
Excess area is put in the topmost and rightmost pools. Excess area is put in the topmost and rightmost pools.
""" """
USED = 0x00000001
CHECK_USED = 0x00010000
MARK_USED = 0x00020000
def __init__ ( self, block ): def __init__ ( self, block ):
self.state = block.state self.state = block.state
self.quadTree = None self.quadTree = None
@ -370,8 +487,13 @@ class Spares ( object ):
""" """
if not self.state.useSpares: return 0.0 if not self.state.useSpares: return 0.0
spareSide = self.state.cfg.block.spareSide 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 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) return float(bufferLength) / float(areaLength)
def toXGCellGrid ( self, x ): def toXGCellGrid ( self, x ):

View File

@ -182,6 +182,8 @@ namespace Etesian {
, getString(instanceOcc).c_str() ) << endl; , getString(instanceOcc).c_str() ) << endl;
return Box(); return Box();
} }
if (instance->getPlacementStatus() == Instance::PlacementStatus::FIXED)
return instance->getMasterCell()->getAbutmentBox();
DbU::Unit dx = 0; DbU::Unit dx = 0;
Cell* cell = instance->getMasterCell(); Cell* cell = instance->getMasterCell();

View File

@ -670,6 +670,8 @@ namespace Etesian {
|coloquinte::YFlippable; |coloquinte::YFlippable;
} else { } else {
instances[instanceId].attributes = 0; instances[instanceId].attributes = 0;
//cerr << "FIXED: " << instance << " size:(" << xsize << " " << ysize
// << ") pos:(" << xpos << " " << ypos << ")" << endl;
} }
_cellsToIds.insert( make_pair(instanceName,instanceId) ); _cellsToIds.insert( make_pair(instanceName,instanceId) );