Support for dedicated power plane in Cumulus (vertical stripes).

* New: In cumulus/plugins.chip.powerplane, build the overall power
    grid when there is a dedicated supply layer. Makes vertical
    supply stripes and connect them the *horizontal* power rails
    inside the blocks (could be in *any* layer).
      Stripes positions are determined by the pins createds by
    the pads module.
* New: In cumulus/plugins.chip.chip, use the powerplane builder
    if the RoutingGauge provides a PowerSupply kind.
* New: In cumulus/plugins.block.configuration, add support for
    PowerSupply gauges.
* New: In cumulus/plugins.block.pads, if the gauge provides a
    PowerSupply, create north/south border pins for power & ground
    to direct the corona to make vertical power strips.
      This assume that we are using LibreSOC like I/O pads that
    can be connected straight from everywhere in the corona.
      First and last 2 stripes are "cap end" and narrower.
      Positions and width of the sripes are set through the
    configuration parameters:
    * "chip.supplyRailWidth"
    * "chip.supplyRailPitch"
* Change: In cumulus/plugins.block.spares, now take into account
    the "placeArea" parameter.
* Change: In cumulus/plugins.block.bigvia, now have a per metal layer
    area that *may* be expanded if it is too narrow to put at least
    one cut. Add flags to allow controlled expansion of the metal
    plates.
      As a security, now raise an exception if no cut can be created.
This commit is contained in:
Jean-Paul Chaput 2021-03-02 12:19:18 +01:00
parent 3a68cdf549
commit f4204c52d7
9 changed files with 905 additions and 108 deletions

View File

@ -69,6 +69,7 @@
set ( pyPluginAlphaChip ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/configuration.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/power.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/powerplane.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/corona.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/pads.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/chip/chip.py

View File

@ -38,18 +38,29 @@ class BigVia ( object ):
"""
Draw a large are VIA and manage the matrix of cuts.
"""
AllowTopMetalExpand = 0x0001
AllowBotMetalExpand = 0x0002
AllowHorizontalExpand = 0x0004
AllowVerticalExpand = 0x0008
AllowAllExpand = AllowTopMetalExpand \
| AllowBotMetalExpand \
| AllowHorizontalExpand \
| AllowVerticalExpand
def __init__ ( self, net, depth, x, y, width, height ):
def __init__ ( self, net, depth, x, y, width, height, flags=0 ):
self.flags = flags
self.hasLayout = False
self.net = net
self.bottomDepth = depth
self.topDepth = depth
self.x = x
self.y = y
self.width = width
self.height = height
self.widths = {}
self.heights = {}
self.plates = {}
self.vias = {}
self.widths [depth] = width
self.heights[depth] = height
def __str__ ( self ):
global rg
@ -60,8 +71,22 @@ class BigVia ( object ):
, rg.getRoutingLayer(self.topDepth).getName()
, DbU.getValueString(self.x)
, DbU.getValueString(self.y)
, DbU.getValueString(self.width)
, DbU.getValueString(self.height) )
, DbU.getValueString(self.widths [self.topDepth])
, DbU.getValueString(self.heights[self.topDepth]) )
@property
def height ( self ):
maxHeight = 0
for depth in range(self.bottomDepth,self.topDepth+1):
maxHeight = max( maxHeight, self.heights[depth] )
return maxHeight
@property
def width ( self ):
maxWidth = 0
for depth in range(self.bottomDepth,self.topDepth+1):
maxWidth = max( maxWidth, self.widths[depth] )
return maxWidth
def getNet ( self ): return self.net
@ -75,25 +100,53 @@ class BigVia ( object ):
if self.hasLayout:
print( WarningMessage( 'BigVia.mergeDepth(): Cannot be called *after* BigVia.doLayout()' ))
return
if depth < self.bottomDepth: self.bottomDepth = depth
if depth > self.topDepth: self.topDepth = depth
if depth < self.bottomDepth:
bdepth = depth
while depth < self.bottomDepth:
self.widths [depth] = self.widths [self.topDepth]
self.heights[depth] = self.heights[self.topDepth]
depth += 1
self.bottomDepth = bdepth
if depth > self.topDepth:
tdepth = depth
while depth > self.topDepth:
self.widths [depth] = self.widths [self.topDepth]
self.heights[depth] = self.heights[self.topDepth]
depth -= 1
self.topDepth = tdepth
def doLayout ( self ):
global rg
if rg is None: rg = CRL.AllianceFramework.get().getRoutingGauge()
for depth in range(self.bottomDepth,self.topDepth+1):
minSize = rg.getRoutingLayer( depth ).getMinimalSize()
if self.widths[depth] < minSize and (self.flags & BigVia.AllowHorizontalExpand):
self.widths[depth] = minSize
if self.heights[depth] < minSize and (self.flags & BigVia.AllowVerticalExpand):
self.heights[depth] = minSize
for depth in range(self.bottomDepth,self.topDepth+1):
minArea = rg.getRoutingLayer( depth ).getMinimalArea()
minLength = DbU.fromPhysical( minArea / DbU.toPhysical( self.width, DbU.UnitPowerMicro )
, DbU.UnitPowerMicro )
#minLength = toFoundryGrid( minLength, DbU.SnapModeSuperior )
plateArea = DbU.toPhysical( self.width , DbU.UnitPowerMicro ) \
* DbU.toPhysical( self.height, DbU.UnitPowerMicro )
plateArea = DbU.toPhysical( self.widths [depth], DbU.UnitPowerMicro ) \
* DbU.toPhysical( self.heights[depth], DbU.UnitPowerMicro )
if plateArea < minArea:
print( WarningMessage( 'BigVia::doLayout(): Area too small for {}'.format(self.net) ))
if depth == self.bottomDepth and not (self.flags & BigVia.AllowBotMetalExpand):
print( WarningMessage( 'BigVia::doLayout(): @({},{}) Area too small for {} in layer "{}"' \
.format( DbU.getValueString(self.x)
, DbU.getValueString(self.y)
, rg.getRoutingLayer(depth).getName()
, self.net
) ))
if depth == self.topDepth and not (self.flags & BigVia.AllowTopMetalExpand):
print( WarningMessage( 'BigVia::doLayout(): @({},{}) Area too small for {} in layer "{}"' \
.format( DbU.getValueString(self.x)
, DbU.getValueString(self.y)
, rg.getRoutingLayer(depth).getName()
, self.net
) ))
self.plates[ depth ] = Contact.create( self.net
, rg.getRoutingLayer(depth)
, self.x , self.y
, self.width, self.height
, self.widths[depth], self.heights[depth]
)
if rg.isSymbolic():
for depth in range(self.bottomDepth,self.topDepth):
@ -101,8 +154,8 @@ class BigVia ( object ):
, rg.getContactLayer( depth )
, self.x
, self.y
, self.width - DbU.fromLambda( 1.0 )
, self.height - DbU.fromLambda( 1.0 ) )
, self.widths [depth] - DbU.fromLambda( 1.0 )
, self.heights[depth] - DbU.fromLambda( 1.0 ) )
else:
for depth in range(self.bottomDepth,self.topDepth):
self._doCutMatrix( depth )
@ -129,13 +182,28 @@ class BigVia ( object ):
trace( 550, '\t| botEnclosure[{}]: {}\n'.format(depth,DbU.getValueString(botEnclosure)) )
trace( 550, '\t| enclosure [{}]: {}\n'.format(depth,DbU.getValueString(enclosure)) )
cutArea = self.plates[ depth ].getBoundingBox()
cutArea.inflate( - enclosure - cutSide/2 )
hEnclosure = enclosure + cutSide/2
vEnclosure = hEnclosure
if hEnclosure*2 > cutArea.getWidth():
if self.flags & BigVia.AllowHorizontalExpand:
hEnclosure = cutArea.getWidth()/2
else:
raise ErrorMessage( 1, [ 'BigVia._doCutMatrix(): Cannot create cut of {} in {}.' \
.format( cutLayer.getName(), self )
, 'Width is too small to fit a single VIA cut.'
] )
if vEnclosure*2 > cutArea.getHeight():
if self.flags & BigVia.AllowVerticalExpand:
vEnclosure = cutArea.getHeight()/2
else:
raise ErrorMessage( 1, [ 'BigVia._doCutMatrix(): Cannot create cut of {} in {}.' \
.format( cutLayer.getName(), self )
, 'Height is too small to fit a single VIA cut.'
] )
cutArea.inflate( -hEnclosure, -vEnclosure )
xoffset = (cutArea.getWidth () % (cutSide+cutSpacing)) / 2
yoffset = (cutArea.getHeight() % (cutSide+cutSpacing)) / 2
cutArea.translate( xoffset, yoffset )
#if cutArea.isEmpty():
# raise ErrorMessage( 1, 'BigVia._doCutMatrix(): Cannot create at least a single cut in {}.' \
# .format(self))
self.vias[ depth ] = []
y = cutArea.getYMin()
while y <= cutArea.getYMax():

View File

@ -547,6 +547,7 @@ class Block ( object ):
if self.conf.placeArea:
self.etesian.setPlaceArea( self.conf.placeArea )
self.etesian.place()
self.etesian.flattenPower()
Breakpoint.stop( 100, 'Placement done.' )
self.etesian.clearColoquinte()
@ -559,7 +560,7 @@ class Block ( object ):
Breakpoint.stop( 100, 'Block.route() Before global routing.' )
self.katana.runGlobalRouter ( Katana.Flags.NoFlags )
self.katana.loadGlobalRouting( Anabatic.EngineLoadGrByNet )
Breakpoint.stop( 99, 'Block.route() After global routing.' )
Breakpoint.stop( 100, 'Block.route() After global routing.' )
self.katana.layerAssign ( Anabatic.EngineNoNetLayerAssign )
self.katana.runNegociate ( Katana.Flags.NoFlags )
success = self.katana.isDetailedRoutingSuccess()

View File

@ -18,30 +18,15 @@ import re
import os.path
from operator import itemgetter
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
from Hurricane import DataBase, Breakpoint, DbU, Box, Transformation, \
Path, Layer, Occurrence, Net, \
NetExternalComponents, RoutingPad, Horizontal, \
Vertical, Contact, Pin, Plug, Instance
import CRL
from CRL import RoutingLayerGauge
from helpers import trace, l, u, n
from helpers.utils import classdecorator
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.io import catch
from helpers.io import ErrorMessage, WarningMessage, catch
from helpers.overlay import CfgCache
from plugins import getParameter
from plugins.rsave import rsave
@ -133,7 +118,13 @@ class GaugeConf ( object ):
@property
def routingBb ( self ): return self._routingBb
def getLayerDepth ( self, layer ): return self._routingGauge.getLayerDepth( layer )
def getRoutingLayer ( self, depth ):
return self._routingGauge.getRoutingLayer( depth )
def getLayerDepth ( self, layer ):
if isinstance(layer,str):
layer = DataBase.getDB().getTechnology().getLayer( layer )
return self._routingGauge.getLayerDepth( layer )
def getPitch ( self, layer ): return self._routingGauge.getPitch( layer )
@ -173,6 +164,8 @@ class GaugeConf ( object ):
if self._routingGauge.getLayerGauge(depth).getType() == RoutingLayerGauge.PinOnly:
continue
if self._routingGauge.getLayerGauge(depth).getType() == RoutingLayerGauge.PowerSupply:
continue
if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal:
if self.horizontalDeepDepth < 0:
self.horizontalDeepDepth = depth

View File

@ -129,7 +129,7 @@ class BufferPool ( object ):
def _createBuffers ( self ):
"""Create the matrix of instances buffer."""
trace( 540, ',+', '\tBufferPool.createBuffers()\n' )
trace( 540, ',+', '\tBufferPool.createBuffers() of {}\n'.format(self.quadTree) )
yoffset = 0
if self.quadTree.spares.conf.isCoreBlock:
yoffset = self.quadTree.spares.conf.icore.getTransformation().getTy()
@ -239,9 +239,19 @@ class QuadTree ( object ):
@staticmethod
def create ( spares ):
#area = spares.conf.cell.getAbutmentBox()
#if spares.conf.isCoreBlock:
# area = spares.conf.core.getAbutmentBox()
# spares.conf.icore.getTransformation().applyOn( area )
area = spares.conf.placeArea
if area is None:
area = spares.conf.cell.getAbutmentBox()
if spares.conf.isCoreBlock:
area = spares.conf.placeArea
if area is None:
area = spares.conf.core.getAbutmentBox()
else:
area = Box( area )
spares.conf.icore.getTransformation().applyOn( area )
root = QuadTree( spares, None, area )
root.rpartition()
@ -277,7 +287,13 @@ class QuadTree ( object ):
self.pool._destroyBuffers()
def __str__ ( self ):
if hasattr(self,'pool'):
occupancy, capacity = self.pool.getUse()
rtag = self.rtag
else:
occupancy = 'occ'
capacity = 'cap'
rtag = 'rtag'
s = '<QuadTree [{},{} {},{}] {}/{} "{}">' \
.format( DbU.getValueString(self.area.getXMin())
, DbU.getValueString(self.area.getYMin())
@ -285,7 +301,7 @@ class QuadTree ( object ):
, DbU.getValueString(self.area.getYMax())
, occupancy
, capacity
, self.rtag )
, rtag )
return s
def __eq__ ( self, other ):
@ -821,10 +837,11 @@ class Spares ( object ):
if self.conf.cfg.etesian.latchUpDistance is None:
return
trace( 540, ',+', '\tSpares._addCapTies()\n' )
area = self.conf.cell.getAbutmentBox()
if self.conf.isCoreBlock:
area = self.conf.core.getAbutmentBox()
self.conf.icore.getTransformation().applyOn( area )
area = self.quadTree.area
#area = self.conf.cell.getAbutmentBox()
#if self.conf.isCoreBlock:
# area = self.conf.core.getAbutmentBox()
# self.conf.icore.getTransformation().applyOn( area )
y = area.getYMin()
sliceHeight = self.conf.sliceHeight
tieWidth = self.conf.feedsConf.tieWidth()

View File

@ -13,8 +13,7 @@
# +-----------------------------------------------------------------+
from __future__ import print_function
from __future__ import absolute_import
from __future__ import print_function, absolute_import
import sys
import traceback
import os.path
@ -24,30 +23,16 @@ import cProfile
import pstats
import Cfg
import Hurricane
from Hurricane import DataBase
from Hurricane import DbU
from Hurricane import Point
from Hurricane import Transformation
from Hurricane import Box
from Hurricane import Path
from Hurricane import Occurrence
from Hurricane import UpdateSession
from Hurricane import Breakpoint
from Hurricane import Net
from Hurricane import RoutingPad
from Hurricane import Contact
from Hurricane import Horizontal
from Hurricane import Vertical
from Hurricane import Instance
from Hurricane import HyperNet
from Hurricane import Query
from Hurricane import DataBase, DbU ,Point, Transformation, Box, \
Path, Occurrence, UpdateSession, Breakpoint, \
Net, RoutingPad, Contact, Horizontal, Vertical, \
Instance, HyperNet, Query
import Viewer
import CRL
from CRL import RoutingLayerGauge
import helpers
from helpers import trace
from helpers.io import ErrorMessage
from helpers.io import WarningMessage
from helpers.io import ErrorMessage, WarningMessage
from helpers.overlay import UpdateSession
import Etesian
import Anabatic
@ -58,6 +43,7 @@ import plugins.rsave
from plugins.alpha.block.block import Block
import plugins.alpha.chip.pads
import plugins.alpha.chip.power
import plugins.alpha.chip.powerplane
import plugins.alpha.chip.corona
@ -114,6 +100,13 @@ class Chip ( Block ):
self.conf.icore.setPlacementStatus( Instance.PlacementStatus.FIXED )
def doConnectCore ( self ):
if self.conf.routingGauge.hasPowerSupply():
power = plugins.alpha.chip.powerplane.Builder( self.conf )
power.connectPower()
power.connectClocks()
power.doLayout()
Breakpoint.stop( 101, 'After Query power.' )
else:
power = plugins.alpha.chip.power.Builder( self.conf )
power.connectPower()
power.connectClocks()
@ -140,6 +133,7 @@ class Chip ( Block ):
self.padsCorona.doLayout()
self.validate()
self.doCoronaFloorplan()
self.padsCorona.doPowerLayout()
self.conf.refresh()
super(Chip,self).doPnR()
self.conf.refresh( self.conf.chip )

View File

@ -94,6 +94,8 @@ class ChipConf ( BlockConf ):
# trace( 550, '\tONE LAMBDA = %s\n' % DbU.getValueString(DbU.fromLambda(1.0)) )
self.validated = True
# Block Corona parameters (triggers loading from disk).
self.cfg.chip.supplyRailWidth = None
self.cfg.chip.supplyRailPitch = None
self.cfg.chip.block.rails.count = None
self.cfg.chip.block.rails.hWidth = None
self.cfg.chip.block.rails.vWidth = None

View File

@ -21,14 +21,15 @@ from Hurricane import DbU, Point, Transformation, Interval, Box, \
Path, Occurrence, UpdateSession, Layer, \
BasicLayer, Net, Pin, Contact, Segment, \
Horizontal, Vertical, Diagonal, RoutingPad, \
Instance
Instance, DataBase
import CRL
from CRL import RoutingLayerGauge
from CRL import RoutingGauge, RoutingLayerGauge
import helpers
from helpers import trace, l, u, n, onFGrid
from helpers.io import ErrorMessage, WarningMessage
from helpers.overlay import UpdateSession
import plugins.alpha.chip
from plugins.alpha.block.bigvia import BigVia
plugins.alpha.chip.importConstants( globals() )
@ -810,6 +811,7 @@ class Corona ( object ):
self.padSpacers = []
self.padCorner = []
self.padRails = [] # [ , [net, layer, axis, width] ]
self.powerCount = 0
self.conf.cfg.chip.padCoreSide = None
if self.conf.cfg.chip.padCoreSide.lower() == 'south':
self.padOrient = Transformation.Orientation.MY
@ -827,6 +829,12 @@ class Corona ( object ):
if self.conf.cfg.chip.padCorner is not None:
self.padCorner = self.padLib.getCell( self.conf.cfg.chip.padCorner )
@property
def supplyRailWidth ( self ): return self.conf.cfg.chip.supplyRailWidth
@property
def supplyRailPitch ( self ): return self.conf.cfg.chip.supplyRailPitch
def toGrid ( self, u ): return u - (u % self.conf.ioPadPitch)
def validate ( self ):
@ -946,6 +954,10 @@ class Corona ( object ):
bb = component.getBoundingBox()
padInstance.getTransformation().applyOn( bb )
trace( 550, '\t| External:{} bb:{}\n'.format(component,bb) )
if self.conf.routingGauge.hasPowerSupply():
if chipIntNet.isPower() or chipIntNet.isGround():
trace( 550, '\t| Skipped, pads uses distributed power terminals.\n' )
continue
if self.conf.chipConf.ioPadGauge == 'LibreSOCIO':
if chipIntNet.isPower() or chipIntNet.isGround():
if side.type == North or side.type == South:
@ -1041,6 +1053,7 @@ class Corona ( object ):
if not padNet: continue
padConnected = self._createCoreWire( chipIntNet, padNet, doneInstances[-1], padConnected )
if padConnected == 0:
if not (chipIntNet.isSupply() and self.conf.routingGauge.hasPowerSupply()):
trace( 550, '-' )
raise ErrorMessage( 1, 'PadsCorona._placeInnerCorona(): Chip net "{}" is not connected to a pad.' \
.format(chipIntNet.getName()) )
@ -1088,6 +1101,88 @@ class Corona ( object ):
, self.eastSide .coreWires[-1].chipNet.getName()) ] ))
trace( 550, '-' )
def _supplyToPad ( self, chipNet, coronaNet, coronaAxis, stripeWidth, side ):
trace( 550, ',+', '\tCorona.Builder._supplyToPads()\n' )
supplyLayerDepth = self.conf.routingGauge.getPowerSupplyGauge().getDepth()
supplyLayer = self.conf.routingGauge.getPowerSupplyGauge().getLayer()
chipLayer = self.conf.getRoutingLayer( self.conf.routingGauge.getPowerSupplyGauge().getDepth() - 1 )
coronaAb = self.conf.icorona.getAbutmentBox()
chipAxis = coronaAxis + self.conf.icorona.getTransformation().getTx()
trace( 550, '\tchipLayer={}\n'.format(chipLayer) )
for rail in self.padRails:
net = rail[0]
layer = rail[1]
railAxis = rail[2]
width = rail[3]
if net != chipNet or chipLayer.getMask() != layer.getMask():
continue
if side == North:
trace( 550, '\tcoronaAb={}\n'.format(coronaAb) )
trace( 550, '\tcoronaAxis={}\n'.format(DbU.getValueString(coronaAxis)) )
trace( 550, '\tchipAxis={}\n'.format(DbU.getValueString(chipAxis)) )
trace( 550, '\trailNet={} <-> {}\n'.format(net,chipNet) )
trace( 550, '\trailAxis={}\n'.format(DbU.getValueString(railAxis)) )
Vertical.create( chipNet
, supplyLayer
, chipAxis
, stripeWidth
, coronaAb.getYMax()
, self.conf.chipAb.getYMax() - railAxis
)
via = BigVia( chipNet
, supplyLayerDepth
, chipAxis
, self.conf.chipAb.getYMax() - railAxis
, stripeWidth
, width
, BigVia.AllowAllExpand
)
trace( 550, '\tpower depth: {}\n'.format( self.conf.routingGauge.getPowerSupplyGauge().getDepth() ))
via.mergeDepth( self.conf.routingGauge.getPowerSupplyGauge().getDepth()-1 )
via.doLayout()
pin = Pin.create( coronaNet
, '{}.{}'.format(coronaNet.getName(),self.powerCount)
, Pin.Direction.NORTH
, Pin.PlacementStatus.FIXED
, supplyLayer
, coronaAxis
, self.conf.icorona.getMasterCell().getAbutmentBox().getYMax()
, stripeWidth
, DbU.fromLambda( 1.0 )
)
trace( 550, '\tpin={}\n'.format(pin) )
self.powerCount += 1
elif side == South:
Vertical.create( chipNet
, supplyLayer
, chipAxis
, stripeWidth
, self.conf.chipAb.getYMin() + railAxis
, coronaAb.getYMin()
)
via = BigVia( chipNet
, supplyLayerDepth
, chipAxis
, self.conf.chipAb.getYMin() + railAxis
, stripeWidth
, width
, BigVia.AllowAllExpand
)
via.mergeDepth( supplyLayerDepth-1 )
via.doLayout()
pin = Pin.create( coronaNet
, '{}.{}'.format(coronaNet.getName(),self.powerCount)
, Pin.Direction.SOUTH
, Pin.PlacementStatus.FIXED
, supplyLayer
, coronaAxis
, self.conf.icorona.getMasterCell().getAbutmentBox().getYMin()
, stripeWidth
, DbU.fromLambda( 1.0 )
)
self.powerCount += 1
trace( 550, '-' )
def doLayout ( self ):
if not self.conf.validated: return
with UpdateSession():
@ -1099,3 +1194,85 @@ class Corona ( object ):
self.westSide.doLayout()
self._placeInnerCorona()
self.conf.chip.setRouted( True )
def doPowerLayout ( self ):
if not self.conf.routingGauge.hasPowerSupply(): return
with UpdateSession():
capViaWidth = self.conf.vDeepRG.getPitch()*3
coreAb = self.conf.coreAb
stripesNb = int( (coreAb.getWidth() - 8*capViaWidth + self.supplyRailWidth) \
/ self.supplyRailPitch - 1 )
offset = (coreAb.getWidth() - self.supplyRailPitch*(stripesNb-1)) / 2
powerNet = None
groundNet = None
chipPowerNet = None
chipGroundNet = None
corona = self.conf.corona
for net in corona.getNets():
if net.isPower (): powerNet = net
if net.isGround(): groundNet = net
if powerNet:
if powerNet.isGlobal():
chipPowerNet = self.conf.chip.getNet( powerNet.getName() )
else:
for net in self.conf.chip.getNets():
if net.getName() == powerNet.getName():
chipPowerNet = net
break
if not chipPowerNet:
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No core power net not connected in "{}"' \
.format(self.conf.chip.getName()) )
else:
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No power net found in "{}"' \
.format(corona.getName()) )
if groundNet:
if groundNet.isGlobal():
chipGroundNet = self.conf.chip.getNet( groundNet.getName() )
else:
for net in self.conf.chip.getNets():
if net.getName() == groundNet.getName():
chipGroundNet = net
break
if not chipGroundNet:
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No core power net not connected in "{}"' \
.format(self.conf.chip.getName()) )
else:
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No ground net found in "{}"' \
.format(corona.getName()) )
icore = self.conf.icore
xcore = icore.getTransformation().getTx()
trace( 550, '\ticoreAb={}\n'.format(icore.getAbutmentBox()) )
print( 'capViaWidth={}'.format(DbU.getValueString(capViaWidth)))
stripeSpecs = []
for i in range(stripesNb+4):
if i % 2:
coronaNet = groundNet
chipNet = chipGroundNet
else:
coronaNet = powerNet
chipNet = chipPowerNet
if i < 2:
axis = xcore + 2*i*capViaWidth + capViaWidth/2
width = capViaWidth
elif i >= stripesNb+2:
axis = xcore + coreAb.getWidth() - 2*(i-stripesNb-1)*capViaWidth + capViaWidth/2
width = capViaWidth
else:
axis = xcore + offset + (i-2)*self.supplyRailPitch
width = self.supplyRailWidth
stripeSpecs.append( [ chipNet, coronaNet, axis, width ] )
for chipNet, coronaNet, axis, width in stripeSpecs:
self._supplyToPad( chipNet, coronaNet, axis, width, North )
self._supplyToPad( chipNet, coronaNet, axis, width, South )
#for istripe in range(stripesNb):
# trace( 550, '\tistripe={}\n'.format(istripe) )
# axis = xcore + offset + istripe*self.supplyRailPitch
# if istripe % 2:
# coronaNet = groundNet
# chipNet = chipGroundNet
# else:
# coronaNet = powerNet
# chipNet = chipPowerNet
# self._supplyToPad( chipNet, coronaNet, axis, North )
# self._supplyToPad( chipNet, coronaNet, axis, South )

View File

@ -0,0 +1,544 @@
# This file is part of the Coriolis Software.
# Copyright (c) UPMC 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/chip/powerplane.py" |
# +-----------------------------------------------------------------+
from __future__ import print_function
import sys
from Hurricane import DbU, Point, Transformation, Box, Interval, \
Path, Occurrence, UpdateSession, Net, \
Contact, Horizontal, Vertical, Cell, Query, \
DataBase, Pin, NetExternalComponents
import CRL
import helpers
from helpers import trace
from helpers.io import ErrorMessage, WarningMessage
from helpers.overlay import UpdateSession
import plugins
import plugins.chip
from plugins.alpha.block.bigvia import BigVia
__all__ = [ 'Builder' ]
plugins.chip.importConstants( globals() )
# --------------------------------------------------------------------
# Class : "corona.IntervalSet"
class IntervalSet ( object ):
def __init__ ( self ):
self.chunks = []
return
def merge ( self, min, max ):
toMerge = Interval( min, max )
imerge = len(self.chunks)
length = len(self.chunks)
i = 0
while i < length:
if imerge >= length:
if toMerge.getVMax() < self.chunks[i].getVMin():
self.chunks.insert( i, toMerge )
length += 1
imerge = 0
break
if toMerge.intersect(self.chunks[i]):
imerge = i
self.chunks[imerge].merge( toMerge )
else:
if toMerge.getVMax() >= self.chunks[i].getVMin():
self.chunks[imerge].merge( self.chunks[i] )
del self.chunks[ i ]
length -= 1
continue
else:
break
i += 1
if imerge >= length:
self.chunks.insert( length, toMerge )
def toStr ( self ):
s = ''
for interval in self.chunks:
if len(s): s += ' '
s += '[{}:{}]'.format( DbU.getValueString(interval.getVMin())
, DbU.getValueString(interval.getVMax()) )
return s
# --------------------------------------------------------------------
# Class : "power.Rail"
class Rail ( object ):
@staticmethod
def create ( bb ):
rail = None
if bb.getWidth() >= bb.getHeight():
rail = HorizontalRail( bb )
rail.merge( bb )
else:
rail = VerticalRail( bb )
rail.merge( bb )
return rail
def __init__ ( self, axis, width ):
self.axis = axis
self.width = width
self.intervals = IntervalSet()
def __str__ ( self ):
s = '<{} @{} w={} {}>'.format( self.typeName
, DbU.getValueString(self.axis)
, DbU.getValueString(self.width)
, self.intervals.toStr() )
return s
# --------------------------------------------------------------------
# Class : "power.HorizontalRail"
class HorizontalRail ( Rail ):
def __init__ ( self, bb ):
super(HorizontalRail,self).__init__( bb.getYCenter(), bb.getHeight() )
self.isHorizontal = True
@property
def typeName ( self ): return 'HorizontalRail'
def merge ( self, bb ):
self.intervals.merge( bb.getXMin(), bb.getXMax() )
def doLayout ( self, plane, stripes ):
trace( 550, ',+', '\tHorizontalRail.doLayout() metal={} {}\n' \
.format( plane.metal.getName(), self ))
viaWidth = plane.conf.vDeepRG.getPitch()*5
for stripe in stripes:
trace( 550, ',+', '\t{}\n'.format(stripe) )
stripeBb = stripe.getBoundingBox()
for chunk in self.intervals.chunks:
if chunk.getVMax() <= stripeBb.getXMin(): continue
if chunk.getVMin() >= stripeBb.getXMax(): break
trace( 550, '\t| Chunk=[{} {}]\n'.format( DbU.getValueString(chunk.getVMin())
, DbU.getValueString(chunk.getVMax()) ))
chunkBb = Box( chunk.getVMin()
, self.axis - self.width/2
, chunk.getVMax()
, self.axis + self.width/2 )
overlap = stripeBb.getIntersection( chunkBb )
if overlap.isEmpty(): continue
if overlap.getWidth() > 5*viaWidth:
trace( 550, '\t| Large overlap={}\n'.format(overlap) )
via = BigVia( stripe.getNet()
, plane.getLayerDepth(stripe.getLayer())
, overlap.getXMin() + viaWidth/2
, overlap.getYCenter()
, viaWidth
, overlap.getHeight()
, BigVia.AllowTopMetalExpand|BigVia.AllowVerticalExpand
)
via.mergeDepth( plane.getLayerDepth(plane.getLayer()) )
via.doLayout()
via = BigVia( stripe.getNet()
, plane.getLayerDepth(stripe.getLayer())
, overlap.getXMax() - viaWidth/2
, overlap.getYCenter()
, viaWidth
, overlap.getHeight()
, BigVia.AllowTopMetalExpand|BigVia.AllowVerticalExpand
)
via.mergeDepth( plane.getLayerDepth(plane.getLayer()) )
via.doLayout()
else:
trace( 550, '\t| Narrow overlap={}\n'.format(overlap) )
via = BigVia( stripe.getNet()
, plane.getLayerDepth(stripe.getLayer())
, overlap.getXCenter()
, overlap.getYCenter()
, overlap.getWidth()
, overlap.getHeight()
, BigVia.AllowTopMetalExpand|BigVia.AllowVerticalExpand
)
via.mergeDepth( plane.getLayerDepth(plane.getLayer()) )
via.doLayout()
trace( 550, '-' )
trace( 550, '-' )
return
# --------------------------------------------------------------------
# Class : "power.VerticalRail"
class VerticalRail ( Rail ):
def __init__ ( self, bb ):
super(VerticalRail,self).__init__( bb.getXCenter(), bb.getWidth() )
self.isHorizontal = False
@property
def typeName ( self ): return 'VerticalRail'
def merge ( self, bb ):
self.intervals.merge( bb.getYMin(), bb.getYMax() )
def doLayout ( self, plane, stripes ):
return
# --------------------------------------------------------------------
# Class : "power.Rails"
class Rails ( object ):
def __init__ ( self, net ):
self.net = net
self.axisLut = {}
def merge ( self, bb ):
if self.isHorizontal:
axis = bb.getYCenter()
width = bb.getHeight()
else:
axis = bb.getXCenter()
width = bb.getWidth()
if not self.axisLut.has_key(axis):
self.axisLut[ axis ] = {}
if not self.axisLut[axis].has_key(width):
if self.isHorizontal:
self.axisLut[ axis ][ width ] = HorizontalRail( bb )
else:
self.axisLut[ axis ][ width ] = VerticalRail( bb )
trace( 550, '\tRails.merge() on {} bb={}\n'.format(type(self.axisLut[ axis ][ width ]),bb) )
self.axisLut[ axis ][ width ].merge( bb )
def doLayout ( self, plane, stripes ):
for wrail in self.axisLut.values():
for rail in wrail.values():
rail.doLayout( plane, stripes )
return
# --------------------------------------------------------------------
# Class : "power.HorizontalRails"
class HorizontalRails ( Rails ):
def __init__ ( self, net ):
super(HorizontalRails,self).__init__( net )
self.isHorizontal = True
# --------------------------------------------------------------------
# Class : "power.VerticalRails"
class VerticalRails ( Rails ):
def __init__ ( self, net ):
super(VerticalRails,self).__init__( net )
self.isHorizontal = False
# --------------------------------------------------------------------
# Class : "power.Plane"
class Plane ( object ):
Horizontal = 0001
Vertical = 0002
def __init__ ( self, builder, metal ):
self.builder = builder
self.metal = metal
self.powerHRails = HorizontalRails( self.conf.coronaVdd )
self.powerVRails = VerticalRails ( self.conf.coronaVdd )
self.groundHRails = HorizontalRails( self.conf.coronaVss )
self.groundVRails = VerticalRails ( self.conf.coronaVss )
@property
def conf ( self ): return self.builder.conf
def getLayer ( self ): return self.metal
def getLayerDepth ( self, layer ): return self.conf.getLayerDepth( layer )
def addRail ( self, net, bb ):
if net.isPower():
if bb.getWidth() >= bb.getHeight(): self.powerHRails.merge( bb )
else: self.powerVRails.merge( bb )
elif net.isGround():
if bb.getWidth() >= bb.getHeight(): self.groundHRails.merge( bb )
else: self.groundVRails.merge( bb )
else:
trace( 550, '\nPlane.addRail(): Reject, not a supply net:{}\n'.format(net) )
def doLayout ( self, stripes ):
self.powerHRails .doLayout( self, stripes.powerStripes )
self.groundHRails.doLayout( self, stripes.groundStripes )
# --------------------------------------------------------------------
# Class : "power.Stripe"
class Stripe ( object ):
def __init__ ( self, builder, southPin, northPin ):
self.builder = builder
self.southPin = southPin
self.northPin = northPin
self.stripe = None
@property
def conf ( self ): return self.builder.conf
def __str__ ( self ):
s = '<Stripe "{}" @{}>'.format( self.southPin.getNet().getName()
, DbU.getValueString(self.southPin.getX()) )
return s
def getNet ( self ):
if self.southPin: return self.southPin.getNet()
if self.northPin: return self.northPin.getNet()
return None
def getLayer ( self ):
if self.southPin: return self.southPin.getLayer()
if self.northPin: return self.northPin.getLayer()
return None
def getBoundingBox ( self ):
if self.stripe: return self.stripe.getBoundingBox()
bb = Box()
if self.southPin: bb.merge( self.southPin.getBoundingBox() )
if self.northPin: bb.merge( self.northPin.getBoundingBox() )
return bb
def doLayout ( self ):
if self.southPin is None:
print( ErrorMessage( 1, [ 'Stripes.doLayout(): Power/ground stripe is missing a south Pin.'
, '(north:'.format(self.northPin) ] ))
return
if self.northPin is None:
print( ErrorMessage( 1, [ 'Stripes.doLayout(): Power/ground stripe is missing a north Pin.'
, '(north:'.format(self.southPin) ] ))
return
self.stripe = Vertical.create( self.southPin
, self.northPin
, self.southPin.getLayer()
, self.southPin.getX()
, self.southPin.getWidth() )
# --------------------------------------------------------------------
# Class : "power.Stripes"
class Stripes ( object ):
def __init__ ( self, builder ):
self.builder = builder
self.powers = {}
self.grounds = {}
self.supplyLayer = self.conf.routingGauge.getPowerSupplyGauge().getLayer()
for pin in self.conf.coronaVdd.getPins():
if pin.getLayer() != self.supplyLayer: continue
key = pin.getX()
if pin.getAccessDirection() == Pin.Direction.SOUTH:
if not self.powers.has_key(key): self.powers[ key ] = Stripe( self, pin, None )
else: self.powers[ key ].southPin = pin
elif pin.getAccessDirection() == Pin.Direction.NORTH:
if not self.powers.has_key(key): self.powers[ key ] = Stripe( self, None, pin )
else: self.powers[ key ].northPin = pin
for pin in self.conf.coronaVss.getPins():
if pin.getLayer() != self.supplyLayer: continue
key = pin.getX()
if pin.getAccessDirection() == Pin.Direction.SOUTH:
if not self.grounds.has_key(key): self.grounds[ key ] = Stripe( self, pin, None )
else: self.grounds[ key ].southPin = pin
elif pin.getAccessDirection() == Pin.Direction.NORTH:
if not self.grounds.has_key(key): self.grounds[ key ] = Stripe( self, None, pin )
else: self.grounds[ key ].northPin = pin
@property
def conf ( self ): return self.builder.conf
@property
def powerStripes ( self ): return self.powers.values()
@property
def groundStripes ( self ): return self.grounds.values()
def doLayout ( self ):
for stripe in self.powers .values(): stripe.doLayout()
for stripe in self.grounds.values(): stripe.doLayout()
# --------------------------------------------------------------------
# Class : "power.GoCb"
class GoCb ( object ):
def __init__ ( self, builder ):
self.builder = builder
def __call__ ( self, query, go ):
managed = False
if isinstance(go,Horizontal): managed = True
elif isinstance(go,Vertical): managed = True
if not managed: return
rootNet = None
if go.getNet().getType() == long(Net.Type.POWER): rootNet = self.builder.conf.coronaVdd
if go.getNet().getType() == long(Net.Type.GROUND): rootNet = self.builder.conf.coronaVss
if not rootNet: return
if not NetExternalComponents.isExternal(go): return
if self.builder.activePlane:
layer = self.builder.activePlane.metal
if layer.isSymbolic():
layer = layer.getBasicLayer()
bb = go.getBoundingBox( layer )
query.getPath().getTransformation().applyOn( bb )
trace( 550, '\tGoCb.__call__(): path={} go={}\n'.format(query.getPath(),go) )
self.builder.activePlane.addRail( rootNet, bb )
else:
print( WarningMessage( 'power.GoCb() callback called without an active plane.' ))
return
# --------------------------------------------------------------------
# Class : "power.Builder"
class Builder ( object ):
def __init__ ( self, conf ):
self.conf = conf
self.path = Path()
self.corona = self.conf.icorona.getMasterCell()
self.icoreAb = self.conf.icore.getAbutmentBox()
self.planes = {}
self.stripes = Stripes( self )
self.activePlane = None
for layerGauge in self.conf.routingGauge.getLayerGauges():
self.planes[ layerGauge.getLayer().getName() ] = Plane( self, layerGauge.getLayer() )
def connectPower ( self ):
if not self.conf.coronaVdd or not self.conf.coronaVss:
raise ErrorMessage( 1, 'Cannot build block power terminals as core vdd and/or vss are not known.' )
return
goCb = GoCb( self )
query = Query()
query.setGoCallback( goCb )
query.setCell( self.corona )
query.setArea( self.icoreAb )
query.setFilter( Query.DoComponents|Query.DoTerminalCells )
query.setStopCellFlags( Cell.Flags_AbstractedSupply )
for layerGauge in self.conf.routingGauge.getLayerGauges():
self.activePlane = self.planes[ layerGauge.getLayer().getName() ]
layer = layerGauge.getLayer()
if layer.isSymbolic():
layer = layer.getBasicLayer()
query.setBasicLayer( layer )
trace( 550, ',+', 'query.doQuery() {}'.format(layer) )
query.doQuery()
trace( 550, '-' )
self.activePlane = None
def _connectClock ( self, ck, trackNb ):
trace( 550, '\tpower.Builder._connectClock() {}\n'.format(ck) )
blockCk = None
for plug in self.conf.icore.getPlugs():
if plug.getNet() == ck:
blockCk = plug.getMasterNet()
if not blockCk:
raise ErrorMessage( 1, 'Block "{}" has no net connected to the clock "{}".' \
.format(self.conf.icore.getName(),ck.getName()) )
return
htPlugs = []
for plug in ck.getPlugs():
if plug.getInstance().isTerminalNetlist():
htPlugs.append( plug )
if len(htPlugs) != 1:
message = [ 'Clock "{}" of block "{}" is not organized as a H-Tree ({} plugs).' \
.format( ck.getName()
, self.conf.icore.getName()
, len(htPlugs)) ]
for plug in htPlugs:
message += [ '\n - {} {}'.format(plug,plug.getInstance()) ]
raise ErrorMessage( 1, message )
return
coronaPin = None
for pin in ck.getPins():
coronaPin = pin
break
if not coronaPin:
message = [ 'Clock "{}" of block "{}" is not connected to a corona Pin.' \
.format( ck.getName() , self.conf.icore.getName() ) ]
raise ErrorMessage( 1, message )
with UpdateSession():
coronaAb = self.conf.cellPnR.getAbutmentBox()
bufferRp = self.conf.rpAccessByOccurrence( Occurrence(htPlugs[0], Path()), ck, 0 )
pinRp = self.conf.rpAccessByOccurrence( Occurrence(coronaPin , Path()), ck, 0 )
self.conf.expandMinArea( bufferRp )
if coronaPin.getAccessDirection() == Pin.Direction.NORTH:
isVertical = True
axis = coronaAb.getYMax()
trackNb = -trackNb
elif coronaPin.getAccessDirection() == Pin.Direction.SOUTH:
isVertical = True
axis = coronaAb.getYMin()
elif coronaPin.getAccessDirection() == Pin.Direction.EAST:
isVertical = False
axis = coronaAb.getXMax()
trackNb = -trackNb
elif coronaPin.getAccessDirection() == Pin.Direction.WEST:
isVertical = False
axis = coronaAb.getXMin()
if isVertical:
pitch = self.conf.vRoutingGauge.getPitch()
yaxis = axis + pitch * trackNb
yaxis = self.conf.getNearestHorizontalTrack( yaxis, 0 )
xaxisRp = self.conf.getNearestVerticalTrack( bufferRp.getX(), 0 )
xaxisPin = self.conf.getNearestVerticalTrack( pin.getX(), 0 )
contact1 = self.conf.createContact( ck, xaxisRp , yaxis, 0 )
contact2 = self.conf.createContact( ck, xaxisPin, yaxis, 0 )
self.conf.createVertical ( bufferRp, contact1, xaxisRp , 0 )
self.conf.createHorizontal( contact1, contact2, yaxis , 0 )
self.conf.createVertical ( contact2, pinRp , xaxisPin, 0 )
else:
pitch = self.conf.hRoutingGauge.getPitch()
xaxis = axis + pitch * trackNb
xaxis = self.conf.getNearestVerticalTrack( xaxis, 0 )
yaxisRp = self.conf.getNearestHorizontalTrack( bufferRp.getY(), 0 )
yaxisPin = self.conf.getNearestHorizontalTrack( pin.getY(), 0 )
contact1 = self.conf.createContact( ck, xaxis, yaxisRp , 0 )
contact2 = self.conf.createContact( ck, xaxis, yaxisPin, 0 )
self.conf.createHorizontal( bufferRp, contact1, yaxisRp , 0 )
self.conf.createVertical ( contact1, contact2, xaxis , 0 )
self.conf.createHorizontal( contact2, pinRp , yaxisPin, 0 )
return
def connectClocks ( self ):
if not self.conf.useClockTree:
print( WarningMessage( "Clock tree generation has been disabled ('chip.clockTree':False)." ))
return
if len(self.conf.coronaCks) == 0:
raise ErrorMessage( 1, 'Cannot build clock terminal as no clock is not known.' )
return
for i in range(len(self.conf.coronaCks)):
self._connectClock( self.conf.coronaCks[i], i+1 )
def doLayout ( self ):
with UpdateSession():
self.stripes.doLayout()
for plane in self.planes.values():
plane.doLayout( self.stripes )