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/chiproute.py
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/conductor.py
|
${CMAKE_CURRENT_SOURCE_DIR}/plugins/conductor.py
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/matrixplacer.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/rsave.py
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/rsaveall.py
|
${CMAKE_CURRENT_SOURCE_DIR}/plugins/rsaveall.py
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/s2r.py
|
${CMAKE_CURRENT_SOURCE_DIR}/plugins/s2r.py
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/aboutwindow.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
|
#set ( pyPluginBlock ${CMAKE_CURRENT_SOURCE_DIR}/plugins/block/__init__.py
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/plugins/block/vchannels.py
|
# ${CMAKE_CURRENT_SOURCE_DIR}/plugins/block/vchannels.py
|
||||||
|
@ -40,6 +42,16 @@
|
||||||
set ( pyTools ${CMAKE_CURRENT_SOURCE_DIR}/tools/blif2vst.py
|
set ( pyTools ${CMAKE_CURRENT_SOURCE_DIR}/tools/blif2vst.py
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/tools/yosys.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 ${pySources} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus )
|
||||||
install ( FILES ${pyPlugins} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins )
|
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 ${pyPluginCTS} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/cts )
|
||||||
install ( FILES ${pyPluginC2C} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/core2chip )
|
install ( FILES ${pyPluginC2C} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/core2chip )
|
||||||
install ( FILES ${pyPluginChip} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/chip )
|
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 )
|
#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