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.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 )

View File

@ -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 )

View File

@ -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 )

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.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 )

View File

@ -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 )

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.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.

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.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 )

View File

@ -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:
for net in self.state.cell.getNets():
if net.isClock():
clockNet = net
break
if not clockNet:
raise ErrorMessage( 3, 'Block.clockTree(): Cell "{}" has no clock net.'.format(self.state.cell.getName()) )
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():
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) )
self.clockTrees[-1].buildHTree()
return self.clockTrees[-1]
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()
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()

View File

@ -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 ):
self.spares = spares
self.clockNet = 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()) ))
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 ):
"""

View File

@ -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()
, CRL.Catalog.State.Views )
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

View File

@ -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.plugs = []
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 ):

View File

@ -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();

View File

@ -640,8 +640,8 @@ namespace Etesian {
instanceTransf.applyOn( instanceAb );
// Upper rounded
int_t xsize = (instanceAb.getWidth () + vpitch -1) / vpitch;
int_t ysize = (instanceAb.getHeight() + hpitch -1) / hpitch;
int_t xsize = (instanceAb.getWidth () + vpitch - 1) / vpitch;
int_t ysize = (instanceAb.getHeight() + hpitch - 1) / hpitch;
// Lower rounded
int_t xpos = instanceAb.getXMin() / vpitch;
int_t ypos = instanceAb.getYMin() / hpitch;
@ -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) );