From f4204c52d71b2e11f85dfa1dd65a89a721b3b9d7 Mon Sep 17 00:00:00 2001 From: Jean-Paul Chaput Date: Tue, 2 Mar 2021 12:19:18 +0100 Subject: [PATCH] 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. --- cumulus/src/CMakeLists.txt | 1 + cumulus/src/plugins/alpha/block/bigvia.py | 112 +++- cumulus/src/plugins/alpha/block/block.py | 3 +- .../src/plugins/alpha/block/configuration.py | 35 +- cumulus/src/plugins/alpha/block/spares.py | 35 +- cumulus/src/plugins/alpha/chip/chip.py | 54 +- .../src/plugins/alpha/chip/configuration.py | 2 + cumulus/src/plugins/alpha/chip/pads.py | 227 +++++++- cumulus/src/plugins/alpha/chip/powerplane.py | 544 ++++++++++++++++++ 9 files changed, 905 insertions(+), 108 deletions(-) create mode 100644 cumulus/src/plugins/alpha/chip/powerplane.py diff --git a/cumulus/src/CMakeLists.txt b/cumulus/src/CMakeLists.txt index f3eaf423..280da117 100644 --- a/cumulus/src/CMakeLists.txt +++ b/cumulus/src/CMakeLists.txt @@ -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 diff --git a/cumulus/src/plugins/alpha/block/bigvia.py b/cumulus/src/plugins/alpha/block/bigvia.py index ccba4901..6a47b8b9 100644 --- a/cumulus/src/plugins/alpha/block/bigvia.py +++ b/cumulus/src/plugins/alpha/block/bigvia.py @@ -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.x , self.y + , 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 ) @@ -128,14 +181,29 @@ class BigVia ( object ): trace( 550, '\t| topEnclosure[{}]: {}\n'.format(depth,DbU.getValueString(topEnclosure)) ) 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 ) + cutArea = self.plates[ depth ].getBoundingBox() + 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(): diff --git a/cumulus/src/plugins/alpha/block/block.py b/cumulus/src/plugins/alpha/block/block.py index 0a900bec..68416fff 100644 --- a/cumulus/src/plugins/alpha/block/block.py +++ b/cumulus/src/plugins/alpha/block/block.py @@ -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() diff --git a/cumulus/src/plugins/alpha/block/configuration.py b/cumulus/src/plugins/alpha/block/configuration.py index ab48e18d..185272aa 100644 --- a/cumulus/src/plugins/alpha/block/configuration.py +++ b/cumulus/src/plugins/alpha/block/configuration.py @@ -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 diff --git a/cumulus/src/plugins/alpha/block/spares.py b/cumulus/src/plugins/alpha/block/spares.py index 3a2642b2..9f5c31ff 100644 --- a/cumulus/src/plugins/alpha/block/spares.py +++ b/cumulus/src/plugins/alpha/block/spares.py @@ -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() + #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.core.getAbutmentBox() + 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 ): - occupancy, capacity = self.pool.getUse() + if hasattr(self,'pool'): + occupancy, capacity = self.pool.getUse() + rtag = self.rtag + else: + occupancy = 'occ' + capacity = 'cap' + rtag = 'rtag' s = '' \ .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() diff --git a/cumulus/src/plugins/alpha/chip/chip.py b/cumulus/src/plugins/alpha/chip/chip.py index fea67a35..afd95f31 100644 --- a/cumulus/src/plugins/alpha/chip/chip.py +++ b/cumulus/src/plugins/alpha/chip/chip.py @@ -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,15 +100,22 @@ class Chip ( Block ): self.conf.icore.setPlacementStatus( Instance.PlacementStatus.FIXED ) def doConnectCore ( self ): - power = plugins.alpha.chip.power.Builder( self.conf ) - power.connectPower() - power.connectClocks() - power.doLayout() - self.conf.refresh() - corona = plugins.alpha.chip.corona.Builder( power ) - corona.connectPads( self.padsCorona ) - corona.connectCore() - corona.doLayout() + 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() + power.doLayout() + self.conf.refresh() + corona = plugins.alpha.chip.corona.Builder( power ) + corona.connectPads( self.padsCorona ) + corona.connectCore() + corona.doLayout() self.conf.refresh() def doPnR ( self ): @@ -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 ) diff --git a/cumulus/src/plugins/alpha/chip/configuration.py b/cumulus/src/plugins/alpha/chip/configuration.py index b3938598..13cc7d3c 100644 --- a/cumulus/src/plugins/alpha/chip/configuration.py +++ b/cumulus/src/plugins/alpha/chip/configuration.py @@ -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 diff --git a/cumulus/src/plugins/alpha/chip/pads.py b/cumulus/src/plugins/alpha/chip/pads.py index 47dfded9..b9a46a0f 100644 --- a/cumulus/src/plugins/alpha/chip/pads.py +++ b/cumulus/src/plugins/alpha/chip/pads.py @@ -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() ) @@ -790,26 +791,27 @@ class Corona ( object ): duplicateds.append( [ position, padInstance ] ) return duplicateds - self.conf = conf - self.conf.validated = False - self.northPads = _dupPads( self.conf.chipConf.northPads ) - self.southPads = _dupPads( self.conf.chipConf.southPads ) - self.eastPads = _dupPads( self.conf.chipConf.eastPads ) - self.westPads = _dupPads( self.conf.chipConf.westPads ) - self.northSide = Side( self, North ) - self.southSide = Side( self, South ) - self.eastSide = Side( self, East ) - self.westSide = Side( self, West ) - self.corners = { SouthWest : Corner( self, SouthWest ) - , SouthEast : Corner( self, SouthEast ) - , NorthWest : Corner( self, NorthWest ) - , NorthEast : Corner( self, NorthEast ) - } - self.padLib = None - self.padOrient = Transformation.Orientation.ID - self.padSpacers = [] - self.padCorner = [] - self.padRails = [] # [ , [net, layer, axis, width] ] + self.conf = conf + self.conf.validated = False + self.northPads = _dupPads( self.conf.chipConf.northPads ) + self.southPads = _dupPads( self.conf.chipConf.southPads ) + self.eastPads = _dupPads( self.conf.chipConf.eastPads ) + self.westPads = _dupPads( self.conf.chipConf.westPads ) + self.northSide = Side( self, North ) + self.southSide = Side( self, South ) + self.eastSide = Side( self, East ) + self.westSide = Side( self, West ) + self.corners = { SouthWest : Corner( self, SouthWest ) + , SouthEast : Corner( self, SouthEast ) + , NorthWest : Corner( self, NorthWest ) + , NorthEast : Corner( self, NorthEast ) + } + self.padLib = None + self.padOrient = Transformation.Orientation.ID + 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 @@ -826,6 +828,12 @@ class Corona ( object ): self.padSpacers.sort( _cmpPad ) 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) @@ -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,9 +1053,10 @@ class Corona ( object ): if not padNet: continue padConnected = self._createCoreWire( chipIntNet, padNet, doneInstances[-1], padConnected ) if padConnected == 0: - trace( 550, '-' ) - raise ErrorMessage( 1, 'PadsCorona._placeInnerCorona(): Chip net "{}" is not connected to a pad.' \ - .format(chipIntNet.getName()) ) + 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()) ) self.conf.setupCorona( self.westSide.gap, self.southSide.gap, self.eastSide.gap, self.northSide.gap ) self.coreSymBb = self.conf.getInstanceAb( self.conf.icorona ) self.coreSymBb.inflate( self.conf.toSymbolic( self.westSide.gap /2, Superior ) @@ -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 ) + diff --git a/cumulus/src/plugins/alpha/chip/powerplane.py b/cumulus/src/plugins/alpha/chip/powerplane.py new file mode 100644 index 00000000..49009701 --- /dev/null +++ b/cumulus/src/plugins/alpha/chip/powerplane.py @@ -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 = ''.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 )