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