New: Reimplementation of the Cumulus Block plugin (in alpha).

This commit is contained in:
Jean-Paul Chaput 2020-08-02 18:18:40 +02:00
parent f211cae69a
commit 4d1acc35ab
9 changed files with 1995 additions and 0 deletions

View File

@ -13,10 +13,12 @@
${CMAKE_CURRENT_SOURCE_DIR}/plugins/chiproute.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/conductor.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/matrixplacer.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/block.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/rsave.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/rsaveall.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/s2r.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/aboutwindow.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/stats.py
)
#set ( pyPluginBlock ${CMAKE_CURRENT_SOURCE_DIR}/plugins/block/__init__.py
# ${CMAKE_CURRENT_SOURCE_DIR}/plugins/block/vchannels.py
@ -40,6 +42,16 @@
set ( pyTools ${CMAKE_CURRENT_SOURCE_DIR}/tools/blif2vst.py
${CMAKE_CURRENT_SOURCE_DIR}/tools/yosys.py
)
set ( pyPluginAlpha ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/utils.py
)
set ( pyPluginBlock ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/configuration.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/spares.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/block.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/clocktree.py
)
install ( FILES ${pySources} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus )
install ( FILES ${pyPlugins} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins )
@ -47,4 +59,6 @@
install ( FILES ${pyPluginCTS} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/cts )
install ( FILES ${pyPluginC2C} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/core2chip )
install ( FILES ${pyPluginChip} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/chip )
install ( FILES ${pyPluginAlpha} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/alpha )
install ( FILES ${pyPluginBlock} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/alpha/block )
#install ( PROGRAMS ${pyTools} DESTINATION bin )

View File

View File

@ -0,0 +1,565 @@
#
# This file is part of the Coriolis Software.
# Copyright (c) SU 2020-2020, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/block/block.py" |
# +-----------------------------------------------------------------+
from __future__ import print_function
import sys
import os.path
import Cfg
from Hurricane import Breakpoint
from Hurricane import DbU
from Hurricane import Box
from Hurricane import Transformation
from Hurricane import Point
from Hurricane import Box
from Hurricane import Path
from Hurricane import Layer
from Hurricane import Occurrence
from Hurricane import Net
from Hurricane import NetExternalComponents
from Hurricane import RoutingPad
from Hurricane import Pad
from Hurricane import Horizontal
from Hurricane import Vertical
from Hurricane import Contact
from Hurricane import Pin
from Hurricane import Plug
from Hurricane import Instance
import CRL
from CRL import RoutingLayerGauge
from helpers import trace
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.io import catch
from helpers.overlay import UpdateSession
import Etesian
import Anabatic
import Katana
import plugins.rsave
from plugins import getParameter
from alpha.block.spares import Spares
from alpha.block.clocktree import ClockTree
from alpha.block.configuration import IoPin
from alpha.block.configuration import BlockState
# ----------------------------------------------------------------------------
# Class : "block.Side".
class Side ( object ):
"""
Collate the informations about all the IoPin on a side of a block
and perform pins creation & placement.
"""
def __init__ ( self, block, side ):
self.block = block
self.side = side
self.pinSpecs = []
self.expandPins = True
self.pins = {}
@property
def pinDirection ( self ):
"""Translate the IoPin side into a Pin direction."""
if self.side & IoPin.WEST: return Pin.Direction.WEST
elif self.side & IoPin.EAST: return Pin.Direction.EAST
elif self.side & IoPin.SOUTH: return Pin.Direction.SOUTH
else: return Pin.Direction.NORTH
def setupAb ( self ):
"""
Initialise the side coordinate from the block abutmeent box.
Needless to say that it must be called *after* the abutment
box has been setup.
"""
if self.side & IoPin.WEST:
self.gauge = self.block.state.gaugeConf.hDeepRG
self.ubegin = self.block.state.yMin
self.uend = self.block.state.yMax
self.sidePos = self.block.state.xMin
elif self.side & IoPin.EAST:
self.gauge = self.block.state.gaugeConf.hDeepRG
self.ubegin = self.block.state.yMin
self.uend = self.block.state.yMax
self.sidePos = self.block.state.xMax
elif self.side & IoPin.SOUTH:
self.gauge = self.block.state.gaugeConf.vDeepRG
self.ubegin = self.block.state.xMin
self.uend = self.block.state.xMax
self.sidePos = self.block.state.yMin
elif self.side & IoPin.NORTH:
self.gauge = self.block.state.gaugeConf.vDeepRG
self.ubegin = self.block.state.xMin
self.uend = self.block.state.xMax
self.sidePos = self.block.state.yMax
def getNextPinPosition ( self, flags, upos, ustep ):
"""
Return the point of the next available Pin position on
the side.
:param flags: Whether to find the next position from the
begin of the side (A_BEGIN) or the end (A_END).
:param upos: Force the position to use, in that cas the
``flags`` argument is ignored. To let the
function choose, must be set to zero.
:param ustep: The distance to space from the last created Pin.
"""
if not upos:
if not ustep: ustep = self.gauge.getPitch()
while True:
if flags & IoPin.A_BEGIN:
self.ubegin += ustep
pinOffset = self.ubegin
if not self.pins.has_key(self.ubegin):
break
else:
self.uend -= ustep
pinOffset = self.uend
if not self.pins.has_key(self.uend):
break
else:
pinOffset = upos
if (self.side & IoPin.WEST) or (self.side & IoPin.EAST):
return Point( self.sidePos, pinOffset )
return Point( pinOffset, self.sidePos )
def append ( self, pin ):
"""
Append a newly created Pin to a map indexed by the position along
the side. Create a list for each entry. If everything goes well
each list will exacly one entry.
This function is still too simple. It will consider two Pin of
different layers as overlapping.
"""
if (self.side & IoPin.WEST) or (self.side & IoPin.EAST):
upos = pin.getY()
else:
upos = pin.getX()
if not self.pins.has_key(upos):
self.pins[upos] = [ pin ]
else:
self.pins[upos].append( pin )
def place ( self, ioPin ):
"""
Performs the actual creation of the Pin from it's ioPin specification.
Should be called prior to any call to ```Etesian.place()```, so the Pin
is taken into account when building the RoutingPads. Returns the number
of *failed* pins, so zero means that all went well... Currently only
check for out of bounds coordinates.
"""
status = 0
if self.side & (IoPin.NORTH | IoPin.SOUTH):
gauge = self.block.state.gaugeConf.vDeepRG
upos = ioPin.upos
for index in ioPin.indexes:
pinName = ioPin.stem.format( index )
net = self.block.state.cell.getNet( pinName )
if net is None:
print( ErrorMessage( 1, [ 'Side.place(IoPin): No net named "{}".'.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:
print( ErrorMessage( 1, [ 'Side.place(IoPin): Pin "{}" is outside north or south abutment box side.' \
.format(pinName)
, '(x:{}, xAB: [{}:{}])' \
.format( DbU.getValueString(pinPos.getX())
, DbU.getValueString(self.block.state.xMin)
, DbU.getValueString(self.block.state.xMax) ) ] ))
status += 1
trace( 550, '\tIoPin.place() N/S @{} "{}" of "{}".\n'.format(pinPos,pinName,net) )
pin = Pin.create( net
, pinName
, self.pinDirection
, Pin.PlacementStatus.FIXED
, gauge.getLayer()
, pinPos.getX()
, pinPos.getY()
, gauge.getWireWidth()
, gauge.getWireWidth() / 2
)
NetExternalComponents.setExternal( pin )
self.append( pin )
self.block.state.incIoPinsCounts( net )
if upos: upos += ioPin.ustep
else:
gauge = self.block.state.gaugeConf.hDeepRG
upos = ioPin.upos
for index in ioPin.indexes:
pinName = ioPin.stem.format(index)
net = self.block.state.cell.getNet( pinName )
if net is None:
print( ErrorMessage( 1, [ 'Side.place(IoPin): No net named "{}".'.format(pinName) ] ))
continue
pinName += '.{}'.format(self.block.state.getIoPinsCounts(net))
pinPos = self.getNextPinPosition( ioPin.flags, upos, ioPin.ustep )
if pinPos.getY() > self.block.state.yMax or pinPos.getY() < self.block.state.yMin:
print( ErrorMessage( 1, [ 'Side.place(IoPin): Pin "{}" is outside east or west abutment box side.' \
.format(pinName)
, '(y:{}, yAB: [{}:{}])' \
.format( DbU.getValueString(pinPos.getY())
, DbU.getValueString(block.state.yMin)
, DbU.getValueString(block.state.yMax)) ] ))
status += 1
trace( 550, '\tIoPin.place() E/W @{} "{}" of "{}".\n'.format(pinPos,pinName,net) )
pin = Pin.create( net
, pinName
, self.pinDirection
, Pin.PlacementStatus.FIXED
, gauge.getLayer()
, pinPos.getX()
, pinPos.getY()
, gauge.getWireWidth() / 2
, gauge.getWireWidth()
)
NetExternalComponents.setExternal( pin )
self.append( pin )
self.block.state.incIoPinsCounts( net )
if upos: upos += ioPin.ustep
return status
def expand ( self ):
"""
After, and only after routing the block, move the I/O pins *outside*
of the abutment box. THey will stick out for one pitch.
"""
if not self.expandPins: return
rg = self.block.state.gaugeConf.routingGauge
for pinsAtPos in self.pins.values():
for pin in pinsAtPos:
for lg in rg.getLayerGauges():
if lg.getLayer().getMask() == pin.getLayer().getMask():
offset = lg.getPitch()
if self.side & IoPin.WEST: pin.setX( pin.getDx()-offset )
elif self.side & IoPin.EAST: pin.setX( pin.getDx()+offset )
elif self.side & IoPin.SOUTH: pin.setY( pin.getDy()-offset )
elif self.side & IoPin.NORTH: pin.setY( pin.getDy()+offset )
def checkOverlaps ( self ):
"""
Check for Pin overlap. For now we are only checking that the are not at the
exact same position. We should check for neighboring overlap (Pin too close
from each other, not only exactly at the position).
"""
if self.side & IoPin.EAST: sideName = 'EAST'
elif self.side & IoPin.WEST: sideName = 'WEST'
elif self.side & IoPin.SOUTH: sideName = 'SOUTH'
elif self.side & IoPin.NORTH: sideName = 'NORTH'
for upos in self.pins.keys():
count = len(self.pins[upos])
if count > 1:
pinNames = self.pins[upos][0].getName()
for pin in self.pins[upos][1:]:
pinNames += ', ' + pin.getName()
print( ErrorMessage( 1, [ 'Side.checkOverlap(): On {} side of block "{}", {} pins ovelaps.' \
.format(sideName,self.block.state.cell.getName(),count)
, '(@{}: {})' \
.format(DbU.getValueString(upos),pinNames) ] ) )
# ----------------------------------------------------------------------------
# Class : "block.Block".
class Block ( object ):
"""
The workhorse for turning a Cell into a hierarchically reusable placed and
routed block. Provide support for:
* Clock tree.
"""
LUT = {}
@staticmethod
def lookup ( cell ):
if Block.LUT.has_key(cell): return Block.LUT[cell]
return None
@staticmethod
def create ( cell, ioPins=[] ):
"""Create a Block and it's configuration object."""
block = Block( BlockState( cell, ioPins ) )
Block.LUT[ cell ] = block
return block
def __init__ ( self, state ):
"""Not to be used directly, please see Block.create()."""
self.flags = 0
self.state = state
self.spares = Spares( self )
self.clockTrees = []
self.blockInstances = []
self.sides = { IoPin.WEST : Side( self, IoPin.WEST )
, IoPin.EAST : Side( self, IoPin.EAST )
, IoPin.SOUTH : Side( self, IoPin.SOUTH )
, IoPin.NORTH : Side( self, IoPin.NORTH )
}
if not self.state.cell.getAbutmentBox().isEmpty():
self.state.cell.setTerminalNetlist( True )
self.state.isBuilt = True
def setUnexpandPins ( self, sides ):
"""
Prevent Pins from the selected sides to be stick out of one pitch.
This may be useful if you plan to route by abutment on a side.
"""
for side in (IoPin.WEST, IoPin.EAST, IoPin.SOUTH, IoPin.NORTH):
if sides & side: self.sides[side].expandPins = False
def setupAb ( self ):
"""
Compute and set the Block's abutment box. Three cases:
1. The AB is already set. assume the user as already set it by itself
so do nothing.
2. The block has *routed* sub-blocks. Then the width and height of the
AB must be specified, and the placement of the sub-blocks, as the
placer cannot guess them. P&R block are placed and any unplaced
block has it's AB & position set to be the same as the one of the
top cell.
3. Otherwise, let the placer (Etesian) compute the size accorging the
various configuration parameters (aspect ratio, space margin, fixed
height or width, ...).
"""
if not self.state.cell.getAbutmentBox().isEmpty(): return
if len(self.blockInstances):
with UpdateSession():
ab = Box( 0, 0, self.state.fixedWidth, self.state.fixedHeight )
self.state.cell.setAbutmentBox( ab )
for occurrence in self.state.cell.getNonTerminalNetlistInstanceOccurrences():
instance = occurrence.getEntity()
subCell = instance.getMasterCell()
subCell.setAbutmentBox( ab )
for occurrence in self.state.cell.getNonTerminalNetlistInstanceOccurrences():
instance = occurrence.getEntity()
instance.setTransformation( Transformation() )
for blockInstance in self.blockInstances:
blockInstance.place()
else:
sysSpaceMargin = self.state.cfg.etesian.spaceMargin
blockSpaceMargin = sysSpaceMargin + self.spares.getSpareSpaceMargin()
self.state.cfg.etesian.spaceMargin = blockSpaceMargin
self.state.cfg.apply()
with UpdateSession():
etesian = Etesian.EtesianEngine.create( self.state.cell )
if self.state.fixedWidth: etesian.setFixedAbWidth ( self.state.fixedWidth )
if self.state.fixedHeight: etesian.setFixedAbHeight( self.state.fixedHeight )
etesian.setDefaultAb()
etesian.destroy()
self.state.cfg.etesian.spaceMargin = sysSpaceMargin
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()) )
with UpdateSession():
self.clockTrees.append( ClockTree(self.spares,clockNet) )
self.clockTrees[-1].buildHTree()
return self.clockTrees[-1]
def splitClocks ( self ):
"""
Break the clock net and attach all it's Pins to the closest leaf
if the H-Tree.
"""
for clockTree in self.clockTrees:
clockTree.splitClock()
def placeIoPins ( self ):
"""
Place the Pins on all the sides. Raise an exception in case of failure.
(mainly due to Pins outside the side range)
"""
faileds = 0
with UpdateSession():
for ioPin in self.state.ioPins:
if ioPin.flags & IoPin.SOUTH: side = self.sides[IoPin.SOUTH]
elif ioPin.flags & IoPin.NORTH: side = self.sides[IoPin.NORTH]
elif ioPin.flags & IoPin.EAST: side = self.sides[IoPin.EAST ]
else: side = self.sides[IoPin.WEST ]
faileds += side.place( ioPin )
if faileds:
raise ErrorMessage( 3, 'Block.placeIoPins(): Cell "{}" has {} badly placed pins.' \
.format(self.state.cell.getName(),faileds) )
def checkIoPins ( self ):
"""
Check for Pin overlap and that all external Net have at least one Pin.
"""
for side in self.sides.values():
side.checkOverlaps()
for net in self.state.cell.getNets():
if not net.isExternal(): continue
if net.isSupply(): continue
hasPins = False
for pin in net.getPins():
hasPins = True
break
if not hasPins:
print( ErrorMessage( 1, 'Block.checkIoPins() External net "{}" has no pin(s).' \
.format(net.getName() )))
def expandIoPins ( self ):
with UpdateSession():
for side in self.sides.values():
side.expand()
def place ( self ):
etesian = Etesian.EtesianEngine.create( self.state.cell )
etesian.place()
etesian.destroy()
def route ( self ):
katana = Katana.KatanaEngine.create( self.state.cell )
#katana.printConfiguration ()
katana.digitalInit ()
#katana.runNegociatePreRouted()
katana.runGlobalRouter ( Katana.Flags.NoFlags )
katana.loadGlobalRouting ( Anabatic.EngineLoadGrByNet )
katana.layerAssign ( Anabatic.EngineNoNetLayerAssign )
katana.runNegociate ( Katana.Flags.NoFlags )
success = katana.isDetailedRoutingSuccess()
#Breakpoint.stop( 0, 'Block.route() done, success:{}.'.format(success) )
katana.finalizeLayout()
katana.destroy()
return success
def addBlockages ( self ):
with UpdateSession():
net = self.state.cell.getNet( 'blockagenet' )
ab = self.state.cell.getAbutmentBox()
rg = self.state.gaugeConf.routingGauge
for lg in rg.getLayerGauges():
if lg.getType() == RoutingLayerGauge.PinOnly: continue
blockage = lg.getBlockageLayer()
if not blockage: continue
if lg.getDirection() == RoutingLayerGauge.Horizontal:
dxBorder = lg.getPitch() + lg.getWireWidth()
Horizontal.create( net
, blockage
, ab.getCenter().getY()
, ab.getHeight()
, ab.getXMin() + dxBorder
, ab.getXMax() - dxBorder
)
else:
dyBorder = lg.getPitch() + lg.getWireWidth()
Vertical.create( net
, blockage
, ab.getCenter().getX()
, ab.getWidth()
, ab.getYMin() + dyBorder
, ab.getYMax() - dyBorder
)
def build ( self ):
"""
Perform all the steps required to build the layout of the block.
The first step is to build all the blockInstance it depends upon,
so they will appear as ``NetListTerminal`` and we can place them
in their parent cell.
"""
for blockInstance in self.blockInstances:
blockInstance.block.editor = self.state.editor
if not blockInstance.block.state.isBuilt:
blockInstance.block.build()
editor = self.state.editor
if editor: editor.setCell( self.state.cell )
self.state.cfg.apply()
self.setupAb()
self.placeIoPins()
self.checkIoPins()
self.spares.build()
if editor: editor.fit()
if self.state.useClockTree: self.addClockTree()
self.place()
if self.state.useClockTree: self.splitClocks()
status = self.route()
self.addBlockages()
self.expandIoPins()
self.state.isBuilt = True
plugins.rsave.rsave( self.state.cell )
return status
def useBlockInstance ( self, instancePathName , transf ):
"""
Make a sub-block known to the top block. Can be trans-hierarchical.
:param instancePathName: A string of instances names, separated by
dots ('.') pinpointing the instance of the
block.
:param transf: The position to place the instance of the
sub-block.
Prior to calling this function, a block *must* have been created for
the master cell of the instance. But it need not to have been placed
and routed.
"""
iNames = instancePathName.split('.')
path = Path()
for iName in iNames:
if path.isEmpty(): parentCell = self.state.cell
else: parentCell = path.getTailInstance().getMasterCell()
instance = parentCell.getInstance( iName )
if not instance:
raise ErrorMessage( 1, [ 'Block.useBlockInstance(): Cell "{}" has no instance "{}".' \
.format(parentCell.getName(),iName)
, '(in path:"{})"' \
.format(instancePathName) ] )
path = Path( path, instance )
tailInstance = path.getTailInstance()
for blockIns in self.blockInstances:
if blockIns.instance == tailInstance:
print( ErrorMessage('Block.useBlockInstance(): In Cell "{}", duplicate call for "{}"' \
.format(self.cell.getName(),tailInstance.getName()) ))
return
blockIns = BlockInstance( tailInstance, transf )
self.blockInstances.append( blockIns )
# ----------------------------------------------------------------------------
# Class : "blockinstance.BlockInstance".
class BlockInstance ( object ):
"""
Light helper class to hold a sub-block information.
"""
def __init__ ( self, instance, transf ):
self.transf = transf
self.instance = instance
self.block = Block.lookup( instance.getMasterCell() )
if not self.block:
raise ErrorMessage( 1, [ 'BlockInstance.__init__(): Instance "{}" of cell "{}" has no Block defined.' \
.format( instance.getName(), instance.getMasterCell().getName() )
] )
return
def place ( self ):
self.instance.setTransformation( self.transf )
self.instance.setPlacementStatus( Instance.PlacementStatus.PLACED )
return

View File

@ -0,0 +1,152 @@
#
# This file is part of the Coriolis Software.
# Copyright (c) SU 2020-2020, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/block/clocktree.py" |
# +-----------------------------------------------------------------+
from __future__ import print_function
import sys
import os.path
import Cfg
from Hurricane import Breakpoint
from Hurricane import DbU
from Hurricane import Box
from Hurricane import Transformation
from Hurricane import Box
from Hurricane import Path
from Hurricane import Layer
from Hurricane import Occurrence
from Hurricane import Net
from Hurricane import HyperNet
from Hurricane import RoutingPad
from Hurricane import Horizontal
from Hurricane import Vertical
from Hurricane import Contact
from Hurricane import Pin
from Hurricane import Plug
from Hurricane import Instance
import CRL
from CRL import RoutingLayerGauge
from helpers import trace
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.io import catch
from helpers.overlay import UpdateSession
from plugins import getParameter
from plugins import utils
from plugins.alpha.block.configuration import GaugeConf
# ----------------------------------------------------------------------------
# Class : "clocktree.ClockTree".
class ClockTree ( object ):
"""
Build a clock tree on a block.
"""
def __init__ ( self, spares, clockNet ):
self.spares = spares
self.clockNet = clockNet
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:
leaf.bInputPlug.setNet( driverNet )
self._rconnectHTree( leaf )
return True
def _rrouteHTree ( self, quadT ):
"""
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, '-' )
return False
gaugeConf = self.spares.state.gaugeConf
bufferConf = self.spares.state.bufferConf
ckNet = quadT.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 )
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 )
gaugeConf.setStackPosition( leftSourceContact, leftSourceX, leftSourceY )
gaugeConf.setStackPosition( rightSourceContact, rightSourceX, rightSourceY )
gaugeConf.setStackPosition( tlContact, leftX, tlY )
gaugeConf.setStackPosition( blContact, leftX, blY )
gaugeConf.setStackPosition( trContact, rightX, tlY )
gaugeConf.setStackPosition( brContact, rightX, blY )
leftContact .setX( leftX )
leftContact .setY( leftSourceY )
rightContact.setX( rightX )
rightContact.setY( rightSourceY )
gaugeConf.createHorizontal( leftContact , leftSourceContact, leftSourceY , 0 )
gaugeConf.createHorizontal( rightSourceContact, rightContact , rightSourceY, 0 )
gaugeConf.createVertical ( leftContact , blContact , leftX , 0 )
gaugeConf.createVertical ( tlContact , leftContact , leftX , 0 )
gaugeConf.createVertical ( rightContact , brContact , rightX , 0 )
gaugeConf.createVertical ( trContact , rightContact , rightX , 0 )
for leaf in quadT.leafs:
self._rrouteHTree( leaf )
trace( 550, '-' )
return True
def buildHTree ( self ):
"""
Create the clock tree netlist in two steps:
1. Connect the buffers of the spares QuadTree to form a H-Tree.
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()
with UpdateSession():
self._rconnectHTree( quadTree )
self._rrouteHTree ( quadTree )
def splitClock ( self ):
"""
Disconnect the registers from the main clock and reconnect them to
the leaf buffers of the clock tree.
"""
quadTree = self.spares.quadTree
quadTree.bufferTag = self.clockNet.getName()
hyperClock = HyperNet.create( Occurrence(self.clockNet) )
for plugOccurrence in hyperClock.getTerminalNetlistPlugOccurrences():
quadTree.attachToLeaf( plugOccurrence )
quadTree.rsplitNetlist()

View File

@ -0,0 +1,630 @@
#
# This file is part of the Coriolis Software.
# Copyright (c) SU 2020-2020, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/block/configuration.py" |
# +-----------------------------------------------------------------+
import sys
import os.path
import Cfg
from Hurricane import Breakpoint
from Hurricane import DbU
from Hurricane import Box
from Hurricane import Transformation
from Hurricane import Box
from Hurricane import Path
from Hurricane import Layer
from Hurricane import Occurrence
from Hurricane import Net
from Hurricane import NetExternalComponents
from Hurricane import RoutingPad
from Hurricane import Horizontal
from Hurricane import Vertical
from Hurricane import Contact
from Hurricane import Pin
from Hurricane import Plug
from Hurricane import Instance
import CRL
from CRL import RoutingLayerGauge
from helpers import trace, l, u, n
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.io import catch
from helpers.overlay import CfgCache
from plugins import getParameter
from plugins.alpha.utils import getPlugByName
# ----------------------------------------------------------------------------
# Class : "configuration.GaugeConf".
class GaugeConf ( object ):
"""
Wrapper around the Cell and I/O pads RoutingGauge, with additional
services.
The Cell routing gauge is read from the configuration parameter
``anabatic.routingGauge``.
"""
HAccess = 0x0001
OffsetRight1 = 0x0002
OffsetTop1 = 0x0004
OffsetBottom1 = 0x0008
DeepDepth = 0x0010
UseContactWidth = 0x0020
ExpandWidth = 0x0040
def __init__ ( self ):
self._cellGauge = None
self._ioPadGauge = None
self._routingGauge = None
self._topLayerDepth = 0
self._plugToRp = { }
self._rpToAccess = { }
self._loadRoutingGauge()
return
@property
def routingGauge ( self ): return self._routingGauge
@property
def sliceHeight ( self ): return self._cellGauge.getSliceHeight()
@property
def sliceStep ( self ): return self._cellGauge.getSliceStep()
@property
def ioPadHeight ( self ): return self._ioPadGauge.getSliceHeight()
@property
def ioPadStep ( self ): return self._ioPadGauge.getSliceStep()
@property
def ioPadPitch ( self ): return self._ioPadGauge.getPitch()
@property
def ioPadGauge ( self ): return self._ioPadGauge
@property
def hRoutingGauge ( self ): return self._routingGauge.getLayerGauge( self.horizontalDepth )
@property
def vRoutingGauge ( self ): return self._routingGauge.getLayerGauge( self.verticalDepth )
@property
def hDeepRG ( self ): return self._routingGauge.getLayerGauge( self.horizontalDeepDepth )
@property
def vDeepRG ( self ): return self._routingGauge.getLayerGauge( self.verticalDeepDepth )
def getPitch ( self, layer ): return self._routingGauge.getPitch( layer )
def _loadRoutingGauge ( self ):
trace( 550, ',+', '\tGaugeConf._loadRoutingGauge()\n' )
gaugeName = Cfg.getParamString('anabatic.routingGauge').asString()
self._cellGauge = CRL.AllianceFramework.get().getCellGauge( gaugeName )
self._routingGauge = CRL.AllianceFramework.get().getRoutingGauge( gaugeName )
if not self._routingGauge:
trace( 500, '-' )
raise ErrorMessage( 1, [ 'RoutingGauge._loadRoutingGauge(): No routing gauge named "{}".'.format(gaugeName)
, 'Please check the "anabatic.routingGauge" configuration parameter." ' ])
topLayer = Cfg.getParamString('anabatic.topRoutingLayer').asString()
self.topLayerDepth = 0
for layerGauge in self._routingGauge.getLayerGauges():
if layerGauge.getLayer().getName() == topLayer:
self.topLayerDepth = layerGauge.getDepth()
break
if not self.topLayerDepth:
print WarningMessage( 'Gauge top layer not defined, using top of gauge ({}).' \
.format(self._routingGauge.getDepth()) )
self._topLayerDepth = self._routingGauge.getDepth() - 1
self.horizontalDepth = -1
self.verticalDepth = -1
self.horizontalDeepDepth = -1
self.verticalDeepDepth = -1
for depth in range(0,self.topLayerDepth+1):
trace( 550, '\tdepth:{} {}\n'.format(depth,self._routingGauge.getLayerGauge(depth) ))
if self._routingGauge.getLayerGauge(depth).getType() == RoutingLayerGauge.PinOnly:
continue
if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal:
if self.horizontalDeepDepth < 0:
self.horizontalDeepDepth = depth
self.horizontalDepth = depth
if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Vertical:
if self.verticalDeepDepth < 0:
self.verticalDeepDepth = depth
self.verticalDepth = depth
trace( 550, '\t| horizontalDepth :{}\n'.format(self.horizontalDepth) )
trace( 550, '\t| verticalDepth :{}\n'.format(self.verticalDepth) )
trace( 550, '\t| horizontalDeepDepth:{}\n'.format(self.horizontalDeepDepth) )
trace( 550, '\t| verticalDeepDepth :{}\n'.format(self.verticalDeepDepth) )
trace( 500, '-' )
return
def _loadIoPadGauge ( self, ioPadGaugeName ):
self._ioPadGauge = CRL.AllianceFramework.get().getCellGauge( ioPadGaugeName )
if not self._ioPadGauge:
print WarningMessage( 'IO pad gauge "%s" not found.' % ioPadGaugeName )
return
def isHorizontal ( self, layer ):
mask = layer.getMask()
for lg in self._routingGauge.getLayerGauges():
if lg.getLayer().getMask() == mask:
if lg.getDirection() == RoutingLayerGauge.Horizontal: return True
return False
print ErrorMessage( 1, 'GaugeConf.isHorizontal(): Layer "%s" is not part of gauge "%s", cannot know preferred direction.' \
% (layer.getName(), self._routingGauge.getName()) )
return False
def isVertical ( self, layer ): return not self.isHorizontal( layer )
def createContact ( self, net, x, y, flags ):
if flags & GaugeConf.DeepDepth: depth = self.horizontalDeepDepth
else: depth = self.horizontalDepth
if self.horizontalDepth > self.verticalDepth: depth -= 1
trace( 550, '\t%s, horizontalDepth:%d, gaugeDepth:%d\n'
% (self._routingGauge,self.horizontalDepth,self._routingGauge.getDepth()))
return Contact.create( net
, self._routingGauge.getContactLayer(depth)
, x, y
, self._routingGauge.getLayerGauge(depth).getViaWidth()
, self._routingGauge.getLayerGauge(depth).getViaWidth()
)
def getNearestHorizontalTrack ( self, bb, y, flags ):
if flags & GaugeConf.DeepDepth: depth = self.horizontalDeepDepth
else: depth = self.horizontalDepth
index = self._routingGauge.getLayerGauge(depth).getTrackIndex( bb.getYMin()
, bb.getYMax()
, y
, RoutingLayerGauge.Nearest )
return self._routingGauge.getLayerGauge(depth).getTrackPosition( bb.getYMin(), index )
def getNearestVerticalTrack ( self, bb, x, flags ):
if flags & GaugeConf.DeepDepth: depth = self.verticalDeepDepth
else: depth = self.verticalDepth
index = self._routingGauge.getLayerGauge(depth).getTrackIndex( bb.getXMin()
, bb.getXMax()
, x
, RoutingLayerGauge.Nearest )
return self._routingGauge.getLayerGauge(depth).getTrackPosition( bb.getXMin(), index )
def createHorizontal ( self, source, target, y, flags ):
if flags & GaugeConf.DeepDepth: depth = self.horizontalDeepDepth
else: depth = self.horizontalDepth
layer = self._routingGauge.getRoutingLayer(depth)
if flags & GaugeConf.UseContactWidth:
width = source.getBoundingBox(layer.getBasicLayer()).getHeight()
else:
width = self._routingGauge.getLayerGauge(depth).getWireWidth()
if flags & GaugeConf.ExpandWidth:
width += DbU.fromLambda( 1.0 )
segment = Horizontal.create( source, target, layer, y, width )
trace( 550, segment )
return segment
def createVertical ( self, source, target, x, flags ):
if flags & GaugeConf.DeepDepth: depth = self.verticalDeepDepth
else: depth = self.verticalDepth
layer = self._routingGauge.getRoutingLayer(depth)
if flags & GaugeConf.UseContactWidth:
width = source.getBoundingBox(layer.getBasicLayer()).getWidth()
else:
width = self._routingGauge.getLayerGauge(depth).getWireWidth()
if flags & GaugeConf.ExpandWidth:
width += DbU.fromLambda( 1.0 )
segment = Vertical.create( source, target, layer, x, width )
trace( 550, segment )
return segment
def rpAccess ( self, rp, flags ):
trace( 550, ',+', '\t_rpAccess() %s\n' % str(rp) )
if self._rpToAccess.has_key(rp):
trace( 550, '-' )
return self._rpToAccess[rp]
if flags & GaugeConf.DeepDepth:
hdepth = self.horizontalDeepDepth
vdepth = self.verticalDeepDepth
else:
hdepth = self.horizontalDepth
vdepth = self.verticalDepth
hpitch = self._routingGauge.getLayerGauge(hdepth).getPitch()
hoffset = self._routingGauge.getLayerGauge(hdepth).getOffset()
contact1 = Contact.create( rp, self._routingGauge.getContactLayer(0), 0, 0 )
midSliceY = contact1.getY() - (contact1.getY() % self._cellGauge.getSliceHeight()) \
+ self._cellGauge.getSliceHeight() / 2
midTrackY = midSliceY - ((midSliceY - hoffset) % hpitch)
dy = midSliceY - contact1.getY()
if flags & GaugeConf.OffsetBottom1: dy += hpitch
if flags & GaugeConf.OffsetTop1: dy -= hpitch
contact1.setDy( dy )
trace( 550, contact1 )
if flags & GaugeConf.HAccess: stopDepth = hdepth
else: stopDepth = vdepth
trace( 550, '\tstopDepth:%d\n' % stopDepth )
for depth in range(1,stopDepth):
xoffset = 0
if flags & GaugeConf.OffsetRight1 and depth == 1:
xoffset = self._routingGauge.getLayerGauge(depth+1).getPitch()
contact2 = Contact.create( rp.getNet()
, self._routingGauge.getContactLayer(depth)
, contact1.getX() + xoffset
, contact1.getY()
, self._routingGauge.getLayerGauge(depth).getViaWidth()
, self._routingGauge.getLayerGauge(depth).getViaWidth()
)
trace( 550, contact2 )
if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal:
segment = Horizontal.create( contact1
, contact2
, self._routingGauge.getRoutingLayer(depth)
, contact1.getY()
, self._routingGauge.getLayerGauge(depth).getWireWidth()
)
trace( 550, segment )
else:
segment = Vertical.create( contact1
, contact2
, self._routingGauge.getRoutingLayer(depth)
, contact1.getX()
, self._routingGauge.getLayerGauge(depth).getWireWidth()
)
trace( 550, segment )
contact1 = contact2
self._rpToAccess[rp] = contact1
trace( 550, '-' )
return contact1
def rpByOccurrence ( self, occurrence, net ):
plug = occurrence.getEntity()
if self._plugToRp.has_key(plug):
rp = self._plugToRp[plug]
else:
rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea )
self._plugToRp[plug] = rp
return rp
def rpAccessByOccurrence ( self, occurrence, net, flags ):
plug = occurrence.getEntity()
if self._plugToRp.has_key(plug):
rp = self._plugToRp[plug]
else:
rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea )
self._plugToRp[plug] = rp
return self.rpAccess( self._rpByOccurrence(occurrence,net), flags )
def rpByPlug ( self, plug, net ):
if self._plugToRp.has_key(plug):
rp = self._plugToRp[plug]
else:
occurrence = Occurrence( plug, Path(net.getCell(),'') )
rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea )
self._plugToRp[plug] = rp
return rp
def rpByPlugName ( self, instance, plugName, net ):
return self.rpByPlug( getPlugByName(instance,plugName), net )
def rpAccessByPlug ( self, plug, net, flags ):
return self.rpAccess( self.rpByPlug(plug,net), flags )
def rpAccessByPlugName ( self, instance, plugName, net, flags=0 ):
return self.rpAccess( self.rpByPlugName(instance,plugName,net), flags )
def setStackPosition ( self, topContact, x, y ):
topContact.setX( x )
topContact.setY( y )
count = 0
for component in topContact.getSlaveComponents():
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) )
if count == 1:
if isinstance(segment,Horizontal):
segment.setY( y )
segment.getOppositeAnchor( topContact ).setY( y )
elif isinstance(segment,Vertical):
segment.setX( x )
segment.getOppositeAnchor( topContact ).setX( x )
return
# ----------------------------------------------------------------------------
# Class : "configuration.BufferInterface".
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 )
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) )
self.count = 0
self.input = None
self.output = None
for net in self.masterCell.getNets():
if not net.isExternal(): continue
if net.isGlobal(): continue
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, '-' )
return
@property
def width ( self ): return self.masterCell.getAbutmentBox().getWidth()
@property
def height ( self ): return self.masterCell.getAbutmentBox().getHeight()
def createBuffer ( self, cell ):
instance = Instance.create( cell, 'spare_buffer_{}'.format(self.count), self.masterCell )
self.count += 1
return instance
# ----------------------------------------------------------------------------
# Class : "configuration.IoPin".
class IoPin ( object ):
"""
Create an I/O Pin on a side of a block for one net or a vector of nets.
"""
SOUTH = 0x0001
NORTH = 0x0002
EAST = 0x0004
WEST = 0x0008
A_BEGIN = 0x0010
A_END = 0x0020
A_MASK = A_BEGIN|A_END
def __init__ ( self, flags, stem, upos, ustep=0, count=1 ):
"""
Create an I/O Pin(s) on the abutment box of a block. Could be for one
net or a vector of net.
:param flags: On which side the pin is to be put (SOUTH, NORTH, EAST or WEST).
:param stem: A string giving the name of the net. In case of a vector, it
must be a string containing exactly one '{}' as placeholder
for the index.
:param upos: Offset from the bottom or left corner of the abutment box side.
:param ustep: Distance between two consecutive Pins of the vector.
:param count: Either an integer to be passed to ```range()``` or directly
a list of indexes.
Examples:
.. code-block:: python
IoPin( IoPin.WEST , 'debug({})', l(50.0), l(100.0), 16 )
IoPin( IoPin.EAST , 'debug(0)' , l(45.0), 0 , 1 )
IoPin( IoPin.SOUTH, 'adrs({})' , l(50.0), l(100.0), [0, 2, 3, 6] )
"""
if flags & IoPin.SOUTH: pass
elif flags & IoPin.NORTH: pass
elif flags & IoPin.EAST: pass
elif flags & IoPin.WEST: pass
else:
raise ErrorMessage( 1, [ 'IoPin.__init__(): Unsupported direction {} for "{}"'.format(flags,stem)
, '(must be NORTH, SOUTH, EAST or WEST)' ] )
self.flags = flags
self.pins = []
self.stem = stem
self.upos = upos
self.ustep = ustep
self.count = count
if self.upos == 0 and not (self.flags & IoPin.A_MASK):
raise ErrorMessage( 1, [ 'IoPin.__init__(): "upos" parameter cannot be zero, corners are forbidden.'
, 'For net "{}"'.format(stem) ] )
if self.count > 1 and (self.ustep == 0) and not (self.flags & IoPin.A_MASK):
raise ErrorMessage( 1, [ 'IoPin.__init__(): "ustep" parameter cannot be zero when "count" more than 1.'
, 'For net "{}"'.format(stem) ] )
@property
def indexes ( self ):
if isinstance(self.count,int):
return range(self.count)
return self.count
# def place ( self, block ):
# """
# Performs the actual creation of the Pin. Should be called prior to any
# call to ```Etesian.place()```, so the Pin is taken into account when building
# the RoutingPads. Returns the number of *failed* pins, so zero means that all
# went well... Currently only check for out of bounds coordinates.
# """
#
# if self.flags & IoPin.SOUTH: side = block.sides[IoPin.SOUTH]
# elif self.flags & IoPin.NORTH: side = block.sides[IoPin.NORTH]
# elif self.flags & IoPin.EAST: side = block.sides[IoPin.EAST ]
# elif self.flags & IoPin.WEST: side = block.sides[IoPin.WEST ]
# status = 0
# if isinstance(self.count,int):
# indexes = range(self.count)
# else:
# indexes = self.count
# if self.flags & (IoPin.NORTH | IoPin.SOUTH):
# gauge = block.state.gaugeConf.vDeepRG
# for index in indexes:
# pinName = self.stem.format(index)
# net = block.state.cell.getNet( pinName )
# if net is None:
# print( ErrorMessage( 1, [ 'IoPin.place(): No net named "{}".'.format(pinName) ] ))
# continue
# pinName += '.{}'.format(block.state.getIoPinsCounts(net))
# pinPos = side.getNextPinPosition( self.flags, self.upos, self.ustep )
# if pinPos.getX() > block.state.xMax or pinPos.getX() < block.state.xMin:
# print( ErrorMessage( 1, [ 'IoPin.place(): Pin "{}" is outside north or south abutment box side.'.format(pinName)
# , '(x:"{}", AB xMax:{})'.format(DbU.getValueString(pinPos.getX()),DbU.getValueString(block.state.xMax)) ] ))
# status += 1
# trace( 550, '\tIoPin.place() N/S @{} "{}" of "{}".\n'.format(pinPos,pinName,net) )
# pin = Pin.create( net
# , pinName
# , side.pinDirection
# , Pin.PlacementStatus.FIXED
# , gauge.getLayer()
# , pinPos.getX()
# , pinPos.getY()
# , gauge.getWireWidth()
# , gauge.getWireWidth() / 2
# )
# NetExternalComponents.setExternal( pin )
# side.append( self.flags, pin )
# block.state.incIoPinsCounts( net )
# if self.upos: self.upos + self.ustep
# else:
# gauge = block.state.gaugeConf.hDeepRG
# for index in indexes:
# pinName = self.stem.format(index)
# net = block.state.cell.getNet( pinName )
# if net is None:
# print( ErrorMessage( 1, [ 'IoPin.place(): No net named "{}".'.format(pinName) ] ))
# continue
# pinName += '.{}'.format(block.state.getIoPinsCounts(net))
# pinPos = side.getNextPinPosition( self.flags, self.upos, self.ustep )
# if pinPos.getY() > block.state.yMax or pinPos.getY() < block.state.yMin:
# print( ErrorMessage( 1, [ 'IoPin.place(): Pin "{}" is outside east or west abutment box side.'.format(pinName)
# , '(y:"{}", AB yMax:{})'.format(DbU.getValueString(pinPos.getY()),DbU.getValueString(block.state.yMax)) ] ))
# status += 1
# trace( 550, '\tIoPin.place() E/W @{} "{}" of "{}".\n'.format(pinPos,pinName,net) )
# pin = Pin.create( net
# , pinName
# , side.pinDirection
# , Pin.PlacementStatus.FIXED
# , gauge.getLayer()
# , pinPos.getX()
# , pinPos.getY()
# , gauge.getWireWidth() / 2
# , gauge.getWireWidth()
# )
# NetExternalComponents.setExternal( pin )
# side.append( self.flags, pin )
# block.state.incIoPinsCounts( net )
# if self.upos: self.upos + self.ustep
# return status
# ----------------------------------------------------------------------------
# Class : "configuration.BlockState".
class BlockState ( object ):
"""
BlockState centralize all the configurations informations related to a
given block.
It contains:
================= ========================================
Attribute Information
================= ========================================
``framework`` The Framework we are using.
``gaugeConf`` The routing Gauge & Cell gauge.
``bufferConf`` The interface of the buffer cell.
``cell`` The block we are working on.
================= ========================================
"""
def __init__ ( self, cell, ioPins=[] ):
self.editor = None
self.framework = CRL.AllianceFramework.get()
self.cfg = CfgCache('')
self.gaugeConf = GaugeConf()
self.bufferConf = BufferInterface( self.framework )
self.cell = cell
self.fixedWidth = None
self.fixedHeight = None
self.deltaAb = [ 0, 0, 0, 0 ]
self.useClockTree = False
self.useSpares = True
self.isBuilt = False
self.ioPins = []
self.ioPinsCounts = {}
for ioPinSpec in ioPins:
self.ioPins.append( IoPin( *ioPinSpec ) )
self.cfg.etesian.aspectRatio = None
self.cfg.etesian.spaceMargin = None
self.cfg.block.spareSide = None
@property
def bufferWidth ( self ): return self.bufferConf.width
@property
def bufferHeight ( self ): return self.bufferConf.height
@property
def xMin ( self ): return self.cell.getAbutmentBox().getXMin()
@property
def yMin ( self ): return self.cell.getAbutmentBox().getYMin()
@property
def xMax ( self ): return self.cell.getAbutmentBox().getXMax()
@property
def yMax ( self ): return self.cell.getAbutmentBox().getYMax()
def setEditor ( self, editor ): self.editor = editor
def createBuffer ( self ):
return self.bufferConf.createBuffer( self.cell )
def setDeltaAb ( self, dx1, dy1, dx2, dy2 ):
self.deltaAb = [ dx1, dy1, dx2, dy2 ]
def incIoPinsCounts ( self, net ):
if not self.ioPinsCounts.has_key(net):
self.ioPinsCounts[net] = 0
self.ioPinsCounts[net] += 1
def getIoPinsCounts ( self, net ):
if not self.ioPinsCounts.has_key(net): return 0
return self.ioPinsCounts[net]

View File

@ -0,0 +1,481 @@
#
# This file is part of the Coriolis Software.
# Copyright (c) SU 2020-2020, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/block/spares.py" |
# +-----------------------------------------------------------------+
import sys
import os.path
import Cfg
from Hurricane import Breakpoint
from Hurricane import DbU
from Hurricane import Box
from Hurricane import Transformation
from Hurricane import Box
from Hurricane import Path
from Hurricane import Layer
from Hurricane import Occurrence
from Hurricane import Net
from Hurricane import RoutingPad
from Hurricane import Horizontal
from Hurricane import Vertical
from Hurricane import Contact
from Hurricane import Pin
from Hurricane import Plug
from Hurricane import Instance
import CRL
from CRL import RoutingLayerGauge
from helpers import trace
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.io import catch
from helpers.overlay import UpdateSession
from plugins import getParameter
from plugins.alpha import utils
framework = CRL.AllianceFramework.get()
# ----------------------------------------------------------------------------
# Class : "spares.QuadTree".
class QuadTree ( object ):
"""
Recursive quad-tree to partition the design area. If the aspect ratio
is not close to one, the first levels are only binary trees, either
horizontals or verticals. We resume quad-tree when the aspect ratio
is comprised between 1/2 (.5) and 2.0.
"""
@staticmethod
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' ):
self.spares = spares
self.area = area
self.xcut = None
self.ycut = None
self.parent = parent
self.bl = None
self.br = None
self.tl = None
self.tr = None
self.buseds = 0
self.bufferTag = 'spare'
self.bufferNet = None
self.buffers = []
self.plugs = []
if self.parent and self.parent.rtag != '':
self.rtag = self.parent.rtag + '_' + rtag
else:
self.rtag = rtag
@property
def leafs ( self ):
activeLeafs = []
if self.bl: activeLeafs.append( self.bl )
if self.br: activeLeafs.append( self.br )
if self.tl: activeLeafs.append( self.tl )
if self.tr: activeLeafs.append( self.tr )
return activeLeafs
@property
def root ( self ):
if self.parent is None: return self
return self.parent.root
def isHBipart ( self ): return self.ycut is None
def isVBipart ( self ): return self.xcut is None
def isLeaf ( self ):
for leaf in (self.bl, self.br, self.tl, self.tr):
if leaf is not None: return False
return True
@property
def buffer ( self ):
"""The the currently selected buffer instance in the pool."""
return self.buffers[self.buseds]
@property
def bInputPlug ( self ):
"""The input Plug of the currently selected buffer in the pool."""
return utils.getPlugByName( self.buffer, self.spares.state.bufferConf.input )
@property
def bOutputPlug ( self ):
"""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 ):
"""
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
names are derived from the `rtag` attribute.
"""
if self.isLeaf() and not doLeaf: return
trace( 550, '\tQuadTree.useBuffer(): 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) )
plug.setNet( outputNetBuff )
trace( 550, '\t| {}\n'.format(plug) )
trace( 550, '\t| {}\n'.format(outputNetBuff) )
def ruseBuffer ( self ):
"""Recursive call of useBuffer()"""
self.useBuffer()
for leaf in self.leafs:
leaf.ruseBuffer()
def useNextBuffer ( self ):
"""
Reset the quadtree buffering cache in order to buffer the next net.
Flush the plugs list and increase the buffer counter (buseds).
"""
if self.plugs:
self.plugs = []
self.buseds += 1
if not self.isLeaf():
for leaf in self.leafs: leaf.useNextBuffer()
def partition ( self ):
trace( 550, ',+', '\tQuadTree.partition(): {}\n'.format(self.area) )
spareSide = self.spares.state.cfg.block.spareSide
sliceHeight = self.spares.state.gaugeConf.sliceHeight
side = float(spareSide * sliceHeight)
aspectRatio = float(self.area.getWidth()) / float(self.area.getHeight())
if self.area.getHeight() < side*2.0 or self.area.getWidth () < side*2.0:
trace( 550, '-' )
return False
if aspectRatio < 0.5:
self.ycut = self.spares.toYGCellGrid( self.area.getYMin() + self.area.getHeight()/2 )
self.bl = QuadTree( self.spares
, self
, Box( self.area.getXMin()
, self.area.getYMin()
, self.area.getXMax()
, self.ycut )
, 'bl' )
self.tl = QuadTree( self.spares
, self
, Box( self.area.getXMin()
, self.ycut
, self.area.getXMax()
, self.area.getYMax() )
, 'tl' )
trace( 550, '\tVertical bi-partition @Y:{}\n'.format(DbU.getValueString(self.ycut)) )
trace( 550, '-' )
return True
elif aspectRatio > 2.0:
self.xcut = self.spares.toXGCellGrid( self.area.getXMin() + self.area.getWidth()/2 )
self.bl = QuadTree( self.spares
, self
, Box( self.area.getXMin()
, self.area.getYMin()
, self.xcut
, self.area.getYMax() )
, 'bl' )
self.br = QuadTree( self.spares
, self
, Box( self.xcut
, self.area.getYMin()
, self.area.getXMax()
, self.area.getYMax() )
, 'br' )
trace( 550, '\tHorizontal bi-partition @X:{}\n'.format(DbU.getValueString(self.xcut)) )
trace( 550, '-' )
return True
self.ycut = self.spares.toYGCellGrid( self.area.getYMin() + self.area.getHeight()/2 )
self.xcut = self.spares.toXGCellGrid( self.area.getXMin() + self.area.getWidth ()/2 )
self.bl = QuadTree( self.spares
, self
, Box( self.area.getXMin()
, self.area.getYMin()
, self.xcut
, self.ycut )
, 'bl' )
self.br = QuadTree( self.spares
, self
, Box( self.xcut
, self.area.getYMin()
, self.area.getXMax()
, self.ycut )
, 'br' )
self.tl = QuadTree( self.spares
, self
, Box( self.area.getXMin()
, self.ycut
, self.xcut
, self.area.getYMax() )
, 'tl' )
self.tr = QuadTree( self.spares
, self
, Box( self.xcut
, self.ycut
, self.area.getXMax()
, self.area.getYMax() )
, 'tr' )
trace( 550, '\tQuadri-partition @X:{} + @Y:{}\n'\
.format(DbU.getValueString(self.xcut),DbU.getValueString(self.ycut)) )
trace( 550, '-' )
return True
def rpartition ( self ):
trace( 550, ',+', '\tQuadTree.rpartition(): {}\n'.format(self.area) )
if self.partition():
for leaf in self.leafs:
trace( 550, '\tLeaf rtag:"{}"\n'.format(leaf.rtag) )
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
if self.isHBipart():
if position.getX() < self.xcut: return self.bl.getLeafUnder(position)
return self.br.getLeafUnder(position)
if self.isVBipart():
if position.getY() < self.ycut: return self.bl.getLeafUnder(position)
return self.tl.getLeafUnder(position)
if position.getX() < self.xcut:
if position.getY() < self.ycut: return self.bl.getLeafUnder(position)
return self.tl.getLeafUnder(position)
if position.getY() < self.ycut: return self.br.getLeafUnder(position)
return self.tr.getLeafUnder(position)
def attachToLeaf ( self, plugOccurrence ):
position = plugOccurrence.getBoundingBox().getCenter()
self.getLeafUnder(position).plugs.append( plugOccurrence )
def splitNetlist ( self ):
"""
Reorganize the netlist by connecting all plugs to the output of the
currently selected buffer of this QuadTree. Use the following steps:
1. Create the output net of the currently selected buffer in the pool.
This net will be at the top level of the hierarchy. The name of
that net is created from the `rtag` and `buseds` attributes.
2. For each plug to connect:
2.a. Bore the buffer output net through all the levels of hierarchy
of the plug. This way, a new net, connected to the buffer output
will be created in the same hierarchical level as the plug.
2.b. Connect the plug to the buffer output net at the same level.
No net/buffer will be used if the plug list is empty.
"""
if not self.plugs: return
trace( 550, ',+', '\tQuadTree.spliNetlist()\n' )
self.useBuffer( doLeaf=True )
netBuff = self.bOutputPlug.getNet()
trace( 550, '\tBuffer: {}\n'.format(self.buffer) )
trace( 550, '\tBuffer output: {}\n'.format(netBuff) )
for plug in self.plugs:
trace( 550, '\t| Leaf: {}\n'.format(plug) )
trace( 550, '\t| netBuff: {}\n'.format(netBuff) )
deepPlug = self.spares.raddTransNet( netBuff, plug.getPath() )
trace( 550, '\t| netBuff: {}\n'.format(netBuff) )
trace( 550, '\t| Deep Plug: {}\n'.format(deepPlug) )
deepNetBuff = deepPlug.getMasterNet() if deepPlug else netBuff
trace( 550, '\t| deepNetBuff: {} {}\n'.format(deepNetBuff,netBuff) )
plug.getEntity().setNet( deepNetBuff )
trace( 550, '-' )
def rsplitNetlist ( self ):
"""Recursive call over splitNetlist()."""
self.splitNetlist()
for leaf in self.leafs:
leaf.rsplitNetlist()
# ----------------------------------------------------------------------------
# Class : "spares.Spares".
class Spares ( object ):
"""
Manages all the spare buffer over a Cell abutment box. Used for clock tree
synthesis (CTS) and high fanout net synthesis (HFS). 4 buffers are created
in each pool. Pools are set in a regular matrix over the whole abutment box.
Excess area is put in the topmost and rightmost pools.
"""
def __init__ ( self, block ):
self.state = block.state
self.quadTree = None
self.cloneds = []
def getSpareSpaceMargin ( self ):
"""
Compute the percentage of margin space to compensate for the 4 spare
buffers.
"""
if not self.state.useSpares: return 0.0
spareSide = self.state.cfg.block.spareSide
areaLength = spareSide * spareSide / self.state.gaugeConf.sliceHeight
bufferLength = self.state.bufferConf.width * 4
return float(bufferLength) / float(areaLength)
def toXGCellGrid ( self, x ):
"""Find the nearest X (inferior) on the Cell gauge grid (sliceStep)."""
dx = x - self.state.xMin
return self.state.xMin + (dx - dx % self.state.gaugeConf.sliceStep)
def toYGCellGrid ( self, y ):
"""Find the nearest Y (inferior) on the Cell gauge grid (sliceHeight)."""
dy = y - self.state.yMin
return self.state.yMin + (dy - dy % self.state.gaugeConf.sliceHeight)
def build ( self ):
if not self.state.useSpares: return
trace( 550, ',+', '\tSpares.build()\n' )
with UpdateSession():
self.quadTree = QuadTree.create( self )
trace( 550, '-' )
def addClonedCell ( self, masterCell ):
if not masterCell in self.cloneds: self.cloneds.append( masterCell )
return
def raddTransNet ( self, topNet, path ):
"""
Add a net through a whole hierarchy of Instance/Cells. The master cells
of the instances may be modified in the process, if so, they are added
to the cloned cell list.
:param topNet: The Net of the topmost cell which should be propagated
down the hierarchy.
:param path: The ordered list of instances into which the top net
must be connected.
:returns: The plug associated to the net in the *last* instance
(at the bottom of the hierarchy).
This is a recursive function. For each instance level:
1. If the `path` is empty, it implies that no instance is involved,
no plug will be created, so return None.
2. Extract the *head* instance of the `path` and try to find an
already existing plug connected to the `topNet`
3. If no plug exists in the instance, then we must add a new external
net into the master cell of the *head instance*. The creation of
that external net will trigger the creation of the plug in all
the instances of it. The new net is created with the same net
name as the `topNet`, and copy it's type. The new plug is then
connected to the `topNet`.
4. If the tail path is empty, we are finished. Returns the current
plug.
5. The tail path is not empty, recursively call itself on the
*plug master net* and the *tail path*.
"""
if path.isEmpty(): return None
tailPath = path.getTailPath()
headInstance = path.getHeadInstance()
headPlug = utils.getPlugByNet(headInstance,topNet)
if not headPlug:
masterCell = headInstance.getMasterCell()
masterNet = Net.create( masterCell, topNet.getName() )
masterNet.setExternal ( True )
masterNet.setType ( topNet.getType() )
masterNet.setDirection( Net.Direction.IN )
headPlug = headInstance.getPlug( masterNet )
if not headPlug:
raise ErrorMessage( 3, 'Plug not created for %s on instance %s of %s' \
% (topNet.getName(),headInstance.getName(),masterCell.getName()) )
headPlug.setNet( topNet )
self.addClonedCell( masterCell )
if tailPath.isEmpty(): return headPlug
return self.raddTransNet( masterNet, tailPath )
def rsave ( self, cell ):
"""
Save the complete cell hierarchy. Saves only the physical view, except
for the ones that has been cloned (their names should end up by "_cts"),
for which logical and physical views are to be saved. They are completely
new cells.
"""
flags = CRL.Catalog.State.Physical
if cell.getName().endswith('_cts'):
flags = flags | CRL.Catalog.State.Logical
framework.saveCell( cell, flags )
for instance in cell.getInstances():
masterCell = instance.getMasterCell()
if not masterCell.isTerminal():
self.rsave( masterCell )
def save ( self, topCell ):
"""
Frontend to Spares.rsave(). Append the "_cts" suffix to the cloned
cells, then call rsave().
"""
for cell in self.cloneds:
cell.setName( cell.getName()+'_cts' )
self.rsave( topCell )
return

View File

@ -0,0 +1,83 @@
#
# This file is part of the Coriolis Software.
# Copyright (c) SU 2020-2020, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/utils.py" |
# +-----------------------------------------------------------------+
from __future__ import print_function
from Hurricane import Breakpoint
from Hurricane import Box
from Hurricane import Vertical
def breakpoint ( editor, level, message ):
"""
Stop Coriolis when the stop level is at least equal to `level`
and display `message`. If there is an `editor`, perform a fit.
"""
if editor:
editor.fit()
editor.refresh()
Breakpoint.stop( level, message )
def getPlugByName ( instance, netName ):
"""
Search in an Instance for a Plug whose *master net* is named `netName`.
"""
masterCell = instance.getMasterCell()
masterNet = masterCell.getNet( netName )
if masterNet:
return instance.getPlug( masterNet )
return None
def getPlugByNet ( instance, net ):
"""
Search in a Net for a Plug belonging to `instance`. Per Hurricane
build rules it is unique.
"""
for plug in net.getPlugs():
if plug.getInstance() == instance:
return plug
return None
def getRpBb ( instance, netName ):
"""
Compute the bounding box of the Vertical external component of net `netName`
of the Instance `instance` with instance transformation applied.
This function is not robust as it looks for the *first* external Vertical
segment it can find in the instance master cell.
"""
bb = Box()
for net in instance.getMasterCell().getNets():
if net.isExternal() and net.getName() == netName:
for component in net.getExternalComponents():
if isinstance(component,Vertical):
bb = component.getBoundingBox()
instance.getTransformation().applyOn( bb )
return bb
def showNet ( cell, netName ):
"""Display all the components of Net `netName`, along with their bounding boxes."""
net = cell.getNet(netName)
if not net:
print( ErrorMessage( 3, 'Cell %s doesn\'t have net %s' % (cell.getName(),netName) ) )
return
print( 'Components of "{}":'.format(netName) )
for component in net.getComponents():
print( '| {} bb:{}'.format(component, component.getBoundingBox()) )
return

View File

@ -0,0 +1,70 @@
#
# This file is part of the Coriolis Software.
# Copyright (c) SU 2020-2020, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/block.py" |
# +-----------------------------------------------------------------+
"""
This script hook the Block plugin inside GCT/Unicorn.
"""
from __future__ import print_function
import sys
import traceback
import helpers
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.overlay import UpdateSession
from helpers import trace
import plugins
from Hurricane import Breakpoint
from Hurricane import DbU
from Hurricane import Box
from Hurricane import Net
from Hurricane import Cell
from Hurricane import Instance
from Hurricane import Transformation
from plugins.alpha.block.block import Block
# --------------------------------------------------------------------
# Plugin hook functions, unicornHook:menus, ScritMain:call
def unicornHook ( **kw ):
kw['beforeAction'] = 'misc.alpha'
plugins.kwUnicornHook( 'misc.alpha.block'
, 'Block P&&R'
, 'Perform block-level placement'
, sys.modules[__name__].__file__
, **kw
)
return
def scriptMain ( **kw ):
"""The mandatory function that Coriolis CGT/Unicorn will look for."""
rvalue = True
try:
helpers.setTraceLevel( 550 )
cell, editor = plugins.kwParseMain( **kw )
block = Block.create( cell )
if editor: block.setEditor( editor )
rvalue = block.build()
except Exception, e:
helpers.io.catch( e )
rvalue = False
sys.stdout.flush()
sys.stderr.flush()
return rvalue