465 lines
23 KiB
Python
465 lines
23 KiB
Python
|
|
# This file is part of the Coriolis Software.
|
|
# Copyright (c) SU 2021-2021, 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/macro/macro.py" |
|
|
# +-----------------------------------------------------------------+
|
|
|
|
|
|
from __future__ import print_function
|
|
import sys
|
|
import os.path
|
|
from operator import itemgetter, attrgetter, methodcaller
|
|
import Cfg
|
|
from Hurricane import Breakpoint, DbU, Box, Transformation, Point, \
|
|
Box, Path, Layer, Occurrence, Net, \
|
|
NetExternalComponents, RoutingPad, Pad, \
|
|
Horizontal, Vertical, Contact, Pin, Plug, \
|
|
Cell, Instance, Rectilinear
|
|
import CRL
|
|
from CRL import RoutingLayerGauge
|
|
from helpers import trace, dots, l, u, n
|
|
from helpers.io import ErrorMessage, WarningMessage, catch
|
|
from helpers.overlay import UpdateSession
|
|
from plugins.alpha.block.bigvia import BigVia
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Class : "macro.Macro".
|
|
|
|
class Macro ( object ):
|
|
"""
|
|
Encapsulate the layout of a user-defined block so it can be used
|
|
in the Coriolis P&R flow. Maintain a lookup table of already
|
|
processeds macros so they are modified only once.
|
|
"""
|
|
|
|
trace( 550, '\tStatic init of Macro\n' )
|
|
LUT = {}
|
|
|
|
@staticmethod
|
|
def lookup ( macroCell ):
|
|
trace( 550, '\tMacro.lookup() on {}\n'.format(macroCell) )
|
|
if Macro.LUT.has_key(macroCell): return Macro.LUT[ macroCell ]
|
|
return None
|
|
|
|
@staticmethod
|
|
def place ( topCell, instance, transf, status ):
|
|
macro = Macro.lookup( instance.getMasterCell() )
|
|
ab = instance.getMasterCell().getAbutmentBox()
|
|
if transf.getOrientation() == Transformation.Orientation.ID:
|
|
abShift = Transformation( -ab.getXMin(), -ab.getYMin(), Transformation.Orientation.ID )
|
|
if transf.getOrientation() == Transformation.Orientation.MX:
|
|
abShift = Transformation( ab.getXMin(), -ab.getYMin(), Transformation.Orientation.ID )
|
|
print( 'transf={}'.format(transf) )
|
|
print( 'abShift={}'.format(abShift) )
|
|
abShift.applyOn( transf )
|
|
print( 'transf={}'.format(transf) )
|
|
instance.setTransformation( transf )
|
|
instance.setPlacementStatus( status )
|
|
|
|
@staticmethod
|
|
def wrap ( macroCell, gaugeName, hMargin, vMargin ):
|
|
"""
|
|
Adjust the interface of a macro block cell to better fit a
|
|
given routing gauge. Keep a table of the already processed
|
|
blocks (must be run only once for a block).
|
|
|
|
For an explanation of the parameters, see Macro.__init__().
|
|
"""
|
|
macro = Macro.lookup( macroCell )
|
|
if macro is not None:
|
|
trace( 550, '\tReusing macro wrapper {}\n'.format(macroCell) )
|
|
return macro
|
|
return Macro( macroCell, gaugeName, hMargin, vMargin )
|
|
|
|
def getPPitch ( self, metal ):
|
|
depth = self.rg.getLayerDepth( metal )
|
|
if depth < self.rg.getDepth(): pdepth = depth + 1
|
|
else: pdepth = depth - 1
|
|
return self.rg.getLayerPitch( pdepth )
|
|
|
|
def getWireWidth ( self, metal ):
|
|
return self.rg.getWireWidth( metal )
|
|
|
|
def getLayerDepth ( self, metal ):
|
|
return self.rg.getLayerDepth( metal )
|
|
|
|
def getNearestTrackAxis ( self, metal, axis ):
|
|
lg = self.rg.getLayerGauge( metal )
|
|
if lg.getDirection() == RoutingLayerGauge.Horizontal:
|
|
axisMin = self.outerAb.getYMin()
|
|
axisMax = self.outerAb.getYMax()
|
|
else:
|
|
axisMin = self.outerAb.getXMin()
|
|
axisMax = self.outerAb.getXMax()
|
|
trackIndex = lg.getTrackIndex( axisMin, axisMax, axis, RoutingLayerGauge.Nearest )
|
|
return axisMin + lg.getOffset() + trackIndex * lg.getPitch()
|
|
|
|
def __init__ ( self, macroCell, gaugeName, hMargin, vMargin ):
|
|
"""
|
|
Encapsulate a macro cell.to better fit the routing gauge.
|
|
|
|
:param macroCell: The macro cell to be encapsulated.
|
|
:param gaugeName: The name of the RoutingGauge to use.
|
|
:param hMargin: The number of pitchs the horizontal terminals
|
|
will stick out of the original abutment box.
|
|
:param vMargin: The number of pitchs the vertical terminals
|
|
will stick out of the original abutment box.
|
|
|
|
The abutment box of the block is enlarged so both it's dimensions
|
|
are a multiple of the sliceHeight. It is then enlarged of one more
|
|
sliceHeight on each side. The resulting block will:
|
|
|
|
* Exactly fit the GCell grid.
|
|
* Eternal pins will be isolated in fully empty GCells on the side.
|
|
Due to some quirks in the P&R algorithm, putting them in GCells
|
|
that are half free and half occluded by the block itself may
|
|
cause (stupid) deadlock to appear.
|
|
"""
|
|
trace( 550, '\tMacro.__init__() {}\n'.format(macroCell) )
|
|
self.cell = macroCell
|
|
Macro.LUT[ self.cell ] = self
|
|
|
|
af = CRL.AllianceFramework.get()
|
|
ab = self.cell.getAbutmentBox()
|
|
self.rg = af.getRoutingGauge( gaugeName )
|
|
gaugeMetal2 = self.rg.getLayerGauge( 1 )
|
|
gaugeMetal3 = self.rg.getLayerGauge( 2 )
|
|
gaugeMetal4 = self.rg.getLayerGauge( 3 )
|
|
gaugeMetal5 = self.rg.getLayerGauge( 4 )
|
|
blockageMetal2 = gaugeMetal2.getBlockageLayer()
|
|
blockageMetal3 = gaugeMetal3.getBlockageLayer()
|
|
blockageMetal4 = gaugeMetal4.getBlockageLayer()
|
|
minSpacingMetal2 = gaugeMetal2.getLayer().getMinimalSpacing()
|
|
minSpacingMetal3 = gaugeMetal3.getLayer().getMinimalSpacing()
|
|
minSpacingMetal4 = gaugeMetal4.getLayer().getMinimalSpacing()
|
|
useJumper = False
|
|
xMinAdjust = 0
|
|
yMinAdjust = 0
|
|
xMaxAdjust = 0
|
|
yMaxAdjust = 0
|
|
if self.cell.getName().lower() == 'spblock_512w64b8w':
|
|
print( ' o Ad-hoc patch for "{}".'.format(self.cell.getName()) )
|
|
useJumper = True
|
|
xMinAdjust = 3*self.rg.getPitch( gaugeMetal5.getLayer() )
|
|
pitch = gaugeMetal2.getPitch()
|
|
if xMinAdjust % pitch:
|
|
xMinAdjust += pitch - (xMinAdjust % pitch)
|
|
for net in self.cell.getNets():
|
|
for component in net.getComponents():
|
|
if isinstance(component,Rectilinear) and component.getLayer() == blockageMetal2:
|
|
bb = component.getBoundingBox()
|
|
bb.inflate( minSpacingMetal2 + xMinAdjust
|
|
, minSpacingMetal2 + u(0.19)
|
|
, minSpacingMetal2
|
|
, minSpacingMetal2 )
|
|
Horizontal.create( component.getNet()
|
|
, blockageMetal2
|
|
, bb.getYCenter()
|
|
, bb.getHeight()
|
|
, bb.getXMin()
|
|
, bb.getXMax() )
|
|
Horizontal.create( component.getNet()
|
|
, blockageMetal4
|
|
, bb.getYCenter()
|
|
, bb.getHeight()
|
|
, bb.getXMin()
|
|
, bb.getXMax() )
|
|
elif isinstance(component,Rectilinear) and component.getLayer() == blockageMetal3:
|
|
bb = component.getBoundingBox()
|
|
bb.inflate( 2*minSpacingMetal3, minSpacingMetal3/2 )
|
|
Vertical.create( component.getNet()
|
|
, blockageMetal3
|
|
, bb.getXCenter()
|
|
, bb.getWidth()
|
|
, bb.getYMin()
|
|
, bb.getYMax() )
|
|
elif isinstance(component,Rectilinear) and component.getLayer() == blockageMetal4:
|
|
bb = component.getBoundingBox()
|
|
bb.inflate( minSpacingMetal4 )
|
|
Horizontal.create( component.getNet()
|
|
, blockageMetal4
|
|
, bb.getYCenter()
|
|
, bb.getHeight()
|
|
, bb.getXMin()
|
|
, bb.getXMax() )
|
|
if self.cell.getName().lower() in [ 'pll', 'gds_pll', 'cmpt_pll' ]:
|
|
print( ' o Ad-hoc patch for "{}".'.format(self.cell.getName()) )
|
|
self.innerAb = ab
|
|
sliceHeight = af.getCellGauge( gaugeName ).getSliceHeight()
|
|
westPins = []
|
|
eastPins = []
|
|
northPins = []
|
|
southPins = []
|
|
for net in self.cell.getNets():
|
|
if not net.isExternal() or net.isSupply() or net.isClock():
|
|
continue
|
|
for component in net.getComponents():
|
|
if not NetExternalComponents.isExternal(component):
|
|
continue
|
|
bb = component.getBoundingBox()
|
|
if not ab.isConstrainedBy(bb):
|
|
continue
|
|
if ab.getXMax() == bb.getXMax(): eastPins.append( component )
|
|
elif ab.getXMin() == bb.getXMin(): westPins.append( component )
|
|
elif ab.getYMax() == bb.getYMax(): northPins.append( component )
|
|
elif ab.getYMin() == bb.getYMin(): southPins.append( component )
|
|
if ab.getWidth () % sliceHeight: xMaxAdjust = sliceHeight - (ab.getWidth ()+xMinAdjust) % sliceHeight
|
|
if ab.getHeight() % sliceHeight: yMaxAdjust = sliceHeight - (ab.getHeight()+yMinAdjust) % sliceHeight
|
|
self.innerAb.inflate( xMinAdjust, 0, xMaxAdjust, yMaxAdjust )
|
|
self.outerAb = Box( self.innerAb )
|
|
self.outerAb.inflate( sliceHeight )
|
|
westPins .sort( key=lambda k: k.getBoundingBox().getYCenter() )
|
|
eastPins .sort( key=lambda k: k.getBoundingBox().getYCenter() )
|
|
northPins.sort( key=lambda k: k.getBoundingBox().getXCenter() )
|
|
southPins.sort( key=lambda k: k.getBoundingBox().getXCenter() )
|
|
|
|
with UpdateSession():
|
|
for component in westPins:
|
|
NetExternalComponents.setInternal( component )
|
|
pitch = self.rg.getPitch( component.getLayer() )
|
|
ppitch = self.getPPitch( component.getLayer() )
|
|
wwidth = self.getWireWidth( component.getLayer() )
|
|
bb = component.getBoundingBox()
|
|
yAxis = bb.getYCenter()
|
|
yOngrid = self.getNearestTrackAxis( component.getLayer(), yAxis )
|
|
xMax = bb.getXMin()
|
|
xMin = xMax - hMargin*ppitch
|
|
width = bb.getHeight()
|
|
ppYAxis = yAxis
|
|
ppYOngrid = yOngrid
|
|
if not self.rg.isSymbolic():
|
|
if ppYAxis < ppYOngrid:
|
|
ppYAxis -= width/2
|
|
ppYOngrid += wwidth/2
|
|
else:
|
|
ppYAxis += width/2
|
|
ppYOngrid -= wwidth/2
|
|
if useJumper:
|
|
jpitch = self.rg.getPitch ( gaugeMetal5.getLayer() )
|
|
jwwidth = self.rg.getWireWidth( gaugeMetal5.getLayer() )
|
|
xMin -= 4*jpitch
|
|
bvia1 = BigVia( component.getNet()
|
|
, self.getLayerDepth(component.getLayer())
|
|
, xMax
|
|
, yOngrid
|
|
, wwidth
|
|
, 3*wwidth
|
|
, flags=BigVia.AllowAllExpand )
|
|
bvia1.mergeDepth( gaugeMetal5.getDepth() )
|
|
bvia1.doLayout()
|
|
bvia2 = BigVia( component.getNet()
|
|
, self.getLayerDepth(component.getLayer())
|
|
, xMax - 3*jpitch
|
|
, yOngrid
|
|
, wwidth
|
|
, 3*wwidth
|
|
, flags=BigVia.AllowAllExpand )
|
|
bvia2.mergeDepth( gaugeMetal5.getDepth() )
|
|
bvia2.doLayout()
|
|
Horizontal.create( bvia1.getPlate( gaugeMetal5.getLayer() )
|
|
, bvia2.getPlate( gaugeMetal5.getLayer() )
|
|
, gaugeMetal5.getLayer()
|
|
, yOngrid
|
|
, jwwidth
|
|
)
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, component.getLayer()
|
|
, yOngrid
|
|
, wwidth
|
|
, xMin
|
|
, xMax - 3*jpitch
|
|
)
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, component.getLayer()
|
|
, yOngrid
|
|
, wwidth
|
|
, xMin
|
|
, xMin + ppitch + ppitch/2
|
|
)
|
|
blockageNet = self.cell.getNet( '*' )
|
|
for gauge in [ gaugeMetal3, gaugeMetal3, gaugeMetal4, gaugeMetal5 ]:
|
|
bb = bvia1.getPlate( gauge.getLayer() ).getBoundingBox()
|
|
bb.merge( bvia2.getPlate( gauge.getLayer() ).getBoundingBox() )
|
|
bb.inflate( gauge.getLayer().getMinimalSpacing() )
|
|
Pad.create( blockageNet
|
|
, gauge.getLayer().getBlockageLayer()
|
|
, bb )
|
|
else:
|
|
vertical = Vertical.create( component.getNet()
|
|
, component.getLayer()
|
|
, bb.getXMin()
|
|
, width
|
|
, ppYAxis
|
|
, ppYOngrid
|
|
)
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, component.getLayer()
|
|
, yOngrid
|
|
, wwidth
|
|
, xMin
|
|
, xMax
|
|
)
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, component.getLayer()
|
|
, yOngrid
|
|
, wwidth
|
|
, xMin
|
|
, xMin + ppitch + ppitch/2
|
|
)
|
|
NetExternalComponents.setExternal( horizontal )
|
|
for component in eastPins:
|
|
layer = component.getLayer()
|
|
if layer.getMask() != gaugeMetal2.getLayer().getMask():
|
|
useBigVia = True
|
|
layer = gaugeMetal2.getLayer()
|
|
NetExternalComponents.setInternal( component )
|
|
pitch = self.rg.getPitch( layer )
|
|
ppitch = self.getPPitch( layer )
|
|
wwidth = self.getWireWidth( layer )
|
|
bb = component.getBoundingBox()
|
|
yAxis = bb.getYCenter()
|
|
yOngrid = self.getNearestTrackAxis( layer, yAxis )
|
|
xMin = self.innerAb.getXMax() - xMaxAdjust
|
|
xMax = xMin + xMaxAdjust + hMargin*ppitch
|
|
width = bb.getHeight()
|
|
ppYAxis = yAxis
|
|
ppYOngrid = yOngrid
|
|
if not self.rg.isSymbolic():
|
|
if ppYAxis < ppYOngrid:
|
|
ppYAxis -= width/2
|
|
ppYOngrid += wwidth/2
|
|
else:
|
|
ppYAxis += width/2
|
|
ppYOngrid -= wwidth/2
|
|
vertical = Vertical.create( component.getNet()
|
|
, component.getLayer()
|
|
, bb.getXMax()
|
|
, width
|
|
, ppYAxis
|
|
, ppYOngrid
|
|
)
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, layer
|
|
, yOngrid
|
|
, wwidth
|
|
, xMin
|
|
, xMax
|
|
)
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, layer
|
|
, yOngrid
|
|
, wwidth
|
|
, xMin + xMaxAdjust + ppitch
|
|
, xMax
|
|
)
|
|
NetExternalComponents.setExternal( horizontal )
|
|
for component in southPins:
|
|
NetExternalComponents.setInternal( component )
|
|
pitch = self.rg.getPitch( component.getLayer() )
|
|
ppitch = self.getPPitch( component.getLayer() )
|
|
wwidth = self.getWireWidth( component.getLayer() )
|
|
bb = component.getBoundingBox()
|
|
xAxis = bb.getXCenter()
|
|
xOngrid = self.getNearestTrackAxis( component.getLayer(), xAxis )
|
|
yMax = bb.getYMin()
|
|
yMin = yMax - vMargin*ppitch
|
|
width = bb.getWidth()
|
|
ppXAxis = xAxis
|
|
ppXOngrid = xOngrid
|
|
if not self.rg.isSymbolic():
|
|
if ppXAxis < ppXOngrid:
|
|
ppXAxis -= width/2
|
|
ppXOngrid += wwidth/2
|
|
else:
|
|
ppXAxis += width/2
|
|
ppXOngrid -= wwidth/2
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, component.getLayer()
|
|
, bb.getYMin()
|
|
, width
|
|
, ppXAxis
|
|
, ppXOngrid
|
|
)
|
|
vertical = Vertical.create( component.getNet()
|
|
, component.getLayer()
|
|
, xOngrid
|
|
, wwidth
|
|
, yMin
|
|
, yMax
|
|
)
|
|
vertical = Vertical.create( component.getNet()
|
|
, component.getLayer()
|
|
, xOngrid
|
|
, wwidth
|
|
, yMin
|
|
, yMax - ppitch
|
|
)
|
|
NetExternalComponents.setExternal( vertical )
|
|
for component in northPins:
|
|
layer = component.getLayer()
|
|
if layer.getMask() != gaugeMetal3.getLayer().getMask():
|
|
useBigVia = True
|
|
layer = gaugeMetal3.getLayer()
|
|
NetExternalComponents.setInternal( component )
|
|
pitch = self.rg.getPitch( layer )
|
|
ppitch = self.getPPitch( layer )
|
|
wwidth = self.getWireWidth( layer )
|
|
bb = component.getBoundingBox()
|
|
xAxis = bb.getXCenter()
|
|
xOngrid = self.getNearestTrackAxis( layer, xAxis )
|
|
yMin = self.innerAb.getYMax() - yMaxAdjust
|
|
yMax = yMin + yMaxAdjust + vMargin*ppitch
|
|
width = bb.getWidth()
|
|
ppXAxis = xAxis
|
|
ppXOngrid = xOngrid
|
|
if not self.rg.isSymbolic():
|
|
if ppXAxis < ppXOngrid:
|
|
ppXAxis -= width/2
|
|
ppXOngrid += wwidth/2
|
|
else:
|
|
ppXAxis += width/2
|
|
ppXOngrid -= wwidth/2
|
|
if useBigVia:
|
|
bvia = BigVia( component.getNet()
|
|
, self.getLayerDepth(component.getLayer())
|
|
, xOngrid
|
|
, bb.getYMax() - wwidth
|
|
, wwidth
|
|
, 3*wwidth
|
|
, flags=BigVia.AllowAllExpand )
|
|
bvia.mergeDepth( self.getLayerDepth(layer) )
|
|
bvia.doLayout()
|
|
else:
|
|
horizontal = Horizontal.create( component.getNet()
|
|
, component.getLayer()
|
|
, bb.getYMax()
|
|
, width
|
|
, ppXAxis
|
|
, ppXOngrid
|
|
)
|
|
vertical = Vertical.create( component.getNet()
|
|
, layer
|
|
, xOngrid
|
|
, wwidth
|
|
, yMin
|
|
, yMax
|
|
)
|
|
vertical = Vertical.create( component.getNet()
|
|
, layer
|
|
, xOngrid
|
|
, wwidth
|
|
, yMin + ppitch + yMaxAdjust
|
|
, yMax
|
|
)
|
|
NetExternalComponents.setExternal( vertical )
|
|
self.cell.setAbutmentBox( self.outerAb )
|