New: Reimplementation of the Cumulus Block plugin (in alpha).
This commit is contained in:
parent
f211cae69a
commit
4d1acc35ab
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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()
|
|
@ -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]
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue