From 205a6877db1faf6393db7a6880502f3d17c50554 Mon Sep 17 00:00:00 2001 From: Jean-Paul Chaput Date: Mon, 31 May 2021 00:02:23 +0200 Subject: [PATCH] More generic H-Tree support to accomodate the LS180 PLL internal clock. The H-Tree support is now allowed for any net, not only the clocks and not only top-level nets. This allow to better management of the LS180 internal clock signal. * New: In Cell::flattenNets(Instance*,set,uint64_t) new overload of the function to allow the user to select nets that will *not* be flattened. This makes the NoClockFlatten flag effectively obsolete, we keep it for backward compatibility. The net names can be of non top level ones. In that case, they must use the name an HyperNet will get (the Occurrence name). For example: "instance1.instance2.deep_net_name". * New: In PyCell, update the wrapper for the new parameter of flattenNets(), new utility function pyListToStringSet() to translate a Python list into a C++ set of names. * New: In EtesianEngine, add support for a list of nets to be excluded from the flattening procedure. Those excluded nets will also be excludeds from the Coloquinte nets *and* HFNS synthesis, as they are likely to be manageds by a H-Tree. * Change: In AnabaticEngine::_loadGrByNet(), now also skip nets that are flagged as manually detailed route. * New: In AnabaticEngine::antennaProtect(), do not try to insert diodes on nets that are already fixed or detaled route. This replace the clock exclusion. * New: In cumulus/plugins.{block,htree,chip}, replace the concept of clock-tree by the more generic H-Tree. That is, we can ask the P&R to create H-Tree on any net of the design, not only the ones matcheds as clock. The net does not even need to be top-level. This is to manage the PLL internal clock generated by the PLL in the LS180 chip. Start to change all reference to "clock" into "H-Tree". * Bug: In cumulus/plugins.chip.powerplanes.Builder._connectHTree(), there was an inversion of the H & V routing gauges to compute the track into which put the H-Tree center to corona edge wiring. This was causing tracks to be used twice, seen in the ao68000 test bench. --- anabatic/src/AnabaticEngine.cpp | 2 + anabatic/src/AntennaProtect.cpp | 4 +- anabatic/src/AutoHorizontal.cpp | 14 +- cumulus/src/CMakeLists.txt | 2 +- cumulus/src/plugins/alpha/block/block.py | 126 +++++++++++++----- .../src/plugins/alpha/block/configuration.py | 5 + .../alpha/block/{clocktree.py => htree.py} | 116 ++++++++-------- cumulus/src/plugins/alpha/chip/chip.py | 2 +- cumulus/src/plugins/alpha/chip/powerplane.py | 65 +++++---- etesian/src/EtesianEngine.cpp | 17 ++- etesian/src/HFNS.cpp | 2 + etesian/src/PyEtesianEngine.cpp | 17 ++- etesian/src/etesian/EtesianEngine.h | 25 ++++ hurricane/src/hurricane/Cell.cpp | 8 ++ hurricane/src/hurricane/Net.cpp | 3 - hurricane/src/hurricane/hurricane/Cell.h | 1 + hurricane/src/isobar/PyCell.cpp | 60 ++++++--- 17 files changed, 299 insertions(+), 170 deletions(-) rename cumulus/src/plugins/alpha/block/{clocktree.py => htree.py} (72%) diff --git a/anabatic/src/AnabaticEngine.cpp b/anabatic/src/AnabaticEngine.cpp index 3b7c3fb1..ec3fbb02 100644 --- a/anabatic/src/AnabaticEngine.cpp +++ b/anabatic/src/AnabaticEngine.cpp @@ -1241,6 +1241,8 @@ namespace Anabatic { //AutoSegment::setShortNetMode( true ); ++shortNets; } + if (NetRoutingExtension::isManualDetailRoute(net)) + continue; if ( NetRoutingExtension::isManualGlobalRoute(net) or NetRoutingExtension::isAutomaticGlobalRoute(net)) { DebugSession::open( net, 145, 150 ); diff --git a/anabatic/src/AntennaProtect.cpp b/anabatic/src/AntennaProtect.cpp index 2368d716..170ed200 100644 --- a/anabatic/src/AntennaProtect.cpp +++ b/anabatic/src/AntennaProtect.cpp @@ -1057,7 +1057,9 @@ namespace Anabatic { uint32_t total = 0; for ( Net* net : getCell()->getNets() ) { if (net->isSupply()) continue; - if (net->isClock ()) continue; + if ( NetRoutingExtension::isManualDetailRoute(net) + or NetRoutingExtension::isFixed(net)) + continue; antennaProtect( net, failed, total ); } cmess2 << Dots::asString ( " - Antenna gate maximum WL" , DbU::getValueString(etesian->getAntennaGateMaxWL()) ) << endl; diff --git a/anabatic/src/AutoHorizontal.cpp b/anabatic/src/AutoHorizontal.cpp index b9345f2a..cfe81655 100644 --- a/anabatic/src/AutoHorizontal.cpp +++ b/anabatic/src/AutoHorizontal.cpp @@ -94,13 +94,13 @@ namespace Anabatic { } } - if (getId() == 1518590) { - cerr << "AutoHorizontal::_postCreate(): " << this << endl; - cerr << "| Source contact:" << source << endl; - cerr << "| Source GCell: " << getGCell() << endl; - cerr << "| Target contact:" << target << endl; - cerr << "| Target GCell: " << target->getGCell() << endl; - } + // if (getId() == 1518590) { + // cerr << "AutoHorizontal::_postCreate(): " << this << endl; + // cerr << "| Source contact:" << source << endl; + // cerr << "| Source GCell: " << getGCell() << endl; + // cerr << "| Target contact:" << target << endl; + // cerr << "| Target GCell: " << target->getGCell() << endl; + // } } diff --git a/cumulus/src/CMakeLists.txt b/cumulus/src/CMakeLists.txt index 344f5556..f8bdc57d 100644 --- a/cumulus/src/CMakeLists.txt +++ b/cumulus/src/CMakeLists.txt @@ -52,7 +52,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/bigvia.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/spares.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/block.py - ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/clocktree.py + ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/htree.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/timing.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/rsmt.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/hfns1.py diff --git a/cumulus/src/plugins/alpha/block/block.py b/cumulus/src/plugins/alpha/block/block.py index b6726e31..3a7d9cc4 100644 --- a/cumulus/src/plugins/alpha/block/block.py +++ b/cumulus/src/plugins/alpha/block/block.py @@ -16,6 +16,7 @@ from __future__ import print_function import sys import os.path +from copy import deepcopy import Cfg from Hurricane import Breakpoint, DbU, Box, Transformation, Point, \ Box, Path, Layer, Occurrence, Net, \ @@ -35,7 +36,7 @@ from plugins import getParameter from plugins.alpha.macro.macro import Macro from plugins.alpha.block import timing from plugins.alpha.block.spares import Spares -from plugins.alpha.block.clocktree import ClockTree +from plugins.alpha.block.htree import HTree #from plugins.alpha.block.hfns1 import BufferTree #from plugins.alpha.block.hfns2 import BufferTree #from plugins.alpha.block.hfns3 import BufferTree @@ -286,6 +287,7 @@ class Block ( object ): """ LUT = {} + FLATTENED = 0x0001 @staticmethod def lookup ( cell ): @@ -300,10 +302,11 @@ class Block ( object ): self.flags = 0 self.conf = conf self.spares = Spares( self ) - self.clockTrees = [] + self.hTrees = [] self.hfnTrees = [] self.blockInstances = [] self.placeHolderCount = 0 + self.excludedNets = deepcopy( self.conf.hTreeNames ) self.sides = { IoPin.WEST : Side( self.conf, IoPin.WEST ) , IoPin.EAST : Side( self.conf, IoPin.EAST ) , IoPin.SOUTH : Side( self.conf, IoPin.SOUTH ) @@ -329,7 +332,6 @@ class Block ( object ): .format(self.conf.cell.getName()) ) Block.LUT[ self.conf.cell ] = self - @staticmethod def _getInstance ( cell, pattern, level=0 ): """ @@ -370,6 +372,35 @@ class Block ( object ): """ return Block._rgetInstance( self.conf.core, path ) + @staticmethod + def _rinstancesToPath ( path, instances ): + instance = path.getTailInstance().getMasterCell().getInstance( instances[0] ) + path = Path( path, instance ) + if len(instances) > 1: + return Block._rinstanceToPath( path, instances[1:] ) + return path + + @staticmethod + def _instancesToPath ( cell, instances ): + instance = cell.getInstance( instances[0] ) + return Block._rinstancesToPath( Path(instance), instances[1:] ) + + def getFlattenedNet ( self, path ): + """ + Find a net in the hierarchy. The path argument is a list pathname of instance + endind by a net name, like "instance1.instance2.net_name". The function returns + a an Occurrence, the instance path and the Net, or None. + """ + for net in self.conf.cellPnR.getNets(): + if net.getName() == path: + return Occurrence( net, Path() ) + elements = path.split('.') + if len(elements) == 1: + return None + path = Block._instancesToPath( self.conf.cellPnR, elements[:-1] ) + net = path.getTailInstance().getMasterCell().getNet( elements[-1] ) + return Occurrence( net, path ) + def setUnexpandPins ( self, sides ): """ Prevent Pins from the selected sides to be stick out of one pitch. @@ -433,43 +464,65 @@ class Block ( object ): else: self.conf.setRoutingBb( self.conf.cell.getAbutmentBox() ) - def addClockTrees ( self ): - """Create the trunk of all the clock trees (recursive H-Tree).""" - print( ' o Building clock tree(s).' ) - af = CRL.AllianceFramework.get() - clockNets = [] - for net in self.conf.cellPnR.getNets(): - if af.isCLOCK(net.getName()): 'CLOCK: {}'.format(net) - if net.isClock(): - trace( 550, '\tBlock.addClockTrees(): Found clock {}.\n'.format(net) ) - clockNets.append( net ) - if not clockNets: - raise ErrorMessage( 3, 'Block.clockTree(): Cell "{}" has no clock net(s).'.format(self.conf.cell.getName()) ) - with UpdateSession(): - for clockNet in clockNets: - print( ' - "{}".'.format(clockNet.getName()) ) - trace( 550, ',+', '\tBlock.addClockTrees(): Build clock tree for {}.\n'.format(clockNet) ) - self.clockTrees.append( ClockTree(self.spares,clockNet,len(self.clockTrees)) ) - self.clockTrees[-1].buildHTree() - trace( 550, '-' ) - Breakpoint.stop( 100, 'Block.addClockTrees() on {} done.'.format(self.conf.cellPnR) ) + def flattenNets ( self ): + if self.flags & Block.FLATTENED: return + if self.conf.isCoreBlock: + self.conf.corona.flattenNets( self.conf.icore, self.conf.hTreeNames, Cell.Flags_NoClockFlatten ) + else: + self.conf.cell.flattenNets( None, self.excludedNets, Cell.Flags_NoClockFlatten ) + self.flags |= Block.FLATTENED - def splitClocks ( self ): + def addHTrees ( self ): + """Create the trunk of all the clock trees (recursive H-Tree).""" + print( ' o Building H-Tree(s).' ) + af = CRL.AllianceFramework.get() + hTreeNets = [] + netOcc = None + self.flattenNets() + for netName in self.conf.hTreeNames: + netOcc = self.getFlattenedNet( netName ) + #if self.conf.isCoreBlock: + # coreNet = self.conf.cell.getNet( netName ) + # if coreNet is not None: + # trace( 550, '\tFound coreNet={}\n'.format(coreNet) ) + # for plug in self.conf.icore.getPlugs(): + # if plug.getMasterNet() == coreNet: + # net = plug.getNet() + # break + #else: + # net = self.conf.cellPnR.getNet( netName ) + if netOcc is None: + print( ErrorMessage( 3, 'Block.addHTrees(): Cell "{}" has no H-Tree net "{}".' \ + .format( self.conf.cellPnR.getName(), netName ))) + continue + trace( 550, '\tBlock.addHTrees(): Found H-Tree {}.\n'.format(netOcc) ) + hTreeNets.append( netOcc ) + self.etesian.exclude( netName ) + with UpdateSession(): + for hTreeNet in hTreeNets: + print( ' - "{}".'.format(hTreeNet.getName()) ) + trace( 550, ',+', '\tBlock.addHTrees(): Build clock tree for {}.\n'.format(hTreeNet) ) + self.hTrees.append( HTree(self.spares,hTreeNet,len(self.hTrees)) ) + self.hTrees[-1].buildHTree() + for net in self.hTrees[-1].subNets: + self.etesian.exclude( net.getName() ) + self.excludedNets.append( net.getName() ) + trace( 550, '-' ) + Breakpoint.stop( 100, 'Block.addHTrees() on {} done.'.format(self.conf.cellPnR) ) + + def splitHTrees ( self ): """ - Break the clock net and attach all it's Pins to the closest leaf + Break the H-Tree root nets and attach all it's Pins to the closest leaf if the H-Tree. """ - for clockTree in self.clockTrees: - clockTree.splitClock() + for hTree in self.hTrees: + hTree.splitNet() def findHfnTrees4 ( self ): """Perform simple HFNS, just break nets regardless of placement.""" print( ' o Building high fanout nets trees.' ) if self.spares: - if self.conf.isCoreBlock: - self.conf.corona.flattenNets( self.conf.icore, Cell.Flags_NoClockFlatten ) - else: - self.conf.cell.flattenNets( None, Cell.Flags_NoClockFlatten ) + self.flattenNets() beginCount = self.conf.bufferConf.count maxSinks = 10 dots( 82 @@ -578,7 +631,7 @@ class Block ( object ): for side in self.sides.values(): side.expand() - def place ( self ): + def initEtesian ( self ): editor = self.conf.editor if self.conf.isCoreBlock: self.etesian = Etesian.EtesianEngine.create( self.conf.corona ) @@ -588,9 +641,11 @@ class Block ( object ): Breakpoint.stop( 100, 'Block.place(), corona loaded.') else: self.etesian = Etesian.EtesianEngine.create( self.conf.cell ) + self.etesian.getCell().flattenNets( None, self.excludedNets, Cell.Flags_NoClockFlatten ) + + def place ( self ): if self.conf.placeArea: self.etesian.setPlaceArea( self.conf.placeArea ) - self.etesian.getCell().flattenNets( None, Cell.Flags_NoClockFlatten ) if self.conf.useHFNS: self.etesian.doHFNS() self.etesian.place() Breakpoint.stop( 100, 'Placement done.' ) @@ -738,6 +793,7 @@ class Block ( object ): blockInstance.block.build() if editor: editor.setCell( self.conf.cellPnR ) self.conf.cfg.apply() + self.initEtesian() iteration = -1 while True: iteration += 1 @@ -748,14 +804,14 @@ class Block ( object ): self.checkIoPins() self.spares.build() #if self.conf.useHFNS: self.findHfnTrees4() - if self.conf.useClockTree: self.addClockTrees() + self.addHTrees() #if self.conf.useHFNS: self.addHfnBuffers() if editor: editor.fit() #Breakpoint.stop( 0, 'Clock tree(s) done.' ) self.place() #if self.conf.useHFNS: self.findHfnTrees() break - if self.conf.useClockTree: self.splitClocks() + self.splitHTrees() self.spares.removeUnusedBuffers() self.etesian.toHurricane() self.etesian.flattenPower() diff --git a/cumulus/src/plugins/alpha/block/configuration.py b/cumulus/src/plugins/alpha/block/configuration.py index f1d6d896..a7604cf5 100644 --- a/cumulus/src/plugins/alpha/block/configuration.py +++ b/cumulus/src/plugins/alpha/block/configuration.py @@ -1109,6 +1109,7 @@ class BlockConf ( GaugeConf ): self.placeArea = None self.deltaAb = [ 0, 0, 0, 0 ] self.useClockTree = False + self.hTreeNames = [ ] self.useHFNS = False self.useSpares = True self.isBuilt = False @@ -1210,6 +1211,10 @@ class BlockConf ( GaugeConf ): trace( 550, '\tNew cloned cell: "{}"\n'.format(masterCell) ) self.cloneds.append( masterCell ) return + + def useHTree ( self, netName ): + if not netName in self.hTreeNames: + self.hTreeNames.append( netName ); def save ( self, flags ): """ diff --git a/cumulus/src/plugins/alpha/block/clocktree.py b/cumulus/src/plugins/alpha/block/htree.py similarity index 72% rename from cumulus/src/plugins/alpha/block/clocktree.py rename to cumulus/src/plugins/alpha/block/htree.py index 3d57d874..35ef89cc 100644 --- a/cumulus/src/plugins/alpha/block/clocktree.py +++ b/cumulus/src/plugins/alpha/block/htree.py @@ -9,7 +9,7 @@ # | Author : Jean-Paul CHAPUT | # | E-mail : Jean-Paul.Chaput@lip6.fr | # | =============================================================== | -# | Python : "./plugins/block/clocktree.py" | +# | Python : "./plugins/block/htree.py" | # +-----------------------------------------------------------------+ @@ -17,29 +17,14 @@ from __future__ import print_function import sys import os.path import Cfg -from Hurricane import Breakpoint -from Hurricane import DbU -from Hurricane import Box -from Hurricane import Transformation -from Hurricane import Box -from Hurricane import Path -from Hurricane import Layer -from Hurricane import Occurrence -from Hurricane import Net -from Hurricane import HyperNet -from Hurricane import RoutingPad -from Hurricane import Horizontal -from Hurricane import Vertical -from Hurricane import Contact -from Hurricane import Pin -from Hurricane import Plug -from Hurricane import Instance +from Hurricane import Breakpoint, DbU, Box, Transformation, Box, \ + Path, Layer, Occurrence, Net, HyperNet, \ + RoutingPad, Horizontal, Vertical, Contact, \ + Pin, Plug, Instance import CRL from CRL import RoutingLayerGauge from helpers import trace -from helpers.io import ErrorMessage -from helpers.io import WarningMessage -from helpers.io import catch +from helpers.io import ErrorMessage, WarningMessage, catch from helpers.overlay import UpdateSession from plugins import getParameter from plugins.alpha import utils @@ -48,33 +33,47 @@ from plugins.alpha.block.spares import Spares # ---------------------------------------------------------------------------- -# Class : "clocktree.ClockTree". +# Class : "htree.HTree". -class ClockTree ( object ): +class HTree ( object ): """ - Build a clock tree on a block. + Build a H-Tree on a net occurrene. """ - def __init__ ( self, spares, clockNet, index ): + def __init__ ( self, spares, treeNetOcc, index ): self.spares = spares - self.clockNet = clockNet - self.clockIndex = index + self.treeNetOcc = treeNetOcc + self.treeIndex = index self.subNets = [] - if not self.clockNet.isClock(): - print( WarningMessage( 'ClockTree.__init__(): Net "{}" is not of CLOCK type.' \ - .format(self.clockNet.getName()) )) + #if not self.treeNetOcc.getEntity().isClock(): + # print( WarningMessage( 'HTree.__init__(): Net "{}" is not of CLOCK type.' \ + # .format(self.treeNet.getEntity().getName()) )) + if treeNetOcc.getPath().isEmpty(): + self.treeNet = self.treeNetOcc.getEntity() + else: + botNet = self.treeNetOcc.getEntity() + botNet.setExternal( True ) + topNetName = self.treeNetOcc.getName() + topNet = Net.create( self.treeNetOcc.getPath().getOwnerCell(), topNetName ) + topNet.setType ( botNet.getType() ) + topNet.setDirection( botNet.getDirection() ) + path = self.treeNetOcc.getPath().getHeadPath() + self.spares.raddTransNet( topNet, path ) + botInstance = self.treeNetOcc.getPath().getTailInstance() + botInstance.getPlug( botNet ).setNet( botInstance.getCell().getNet( topNetName )) + self.treeNet = topNet def destroy ( self ): - trace( 550, ',+', '\tClockTree.destroy() "{}"\n'.format(self.clockNet.getName()) ) + trace( 550, ',+', '\tHTree.destroy() "{}"\n'.format(self.treeNet.getName()) ) with UpdateSession(): - for subNet in self.subNets + [ self.clockNet ]: + for subNet in self.subNets + [ self.treeNet ]: components = [] for comp in subNet.getComponents(): if isinstance(comp,RoutingPad): components.append( comp ) if isinstance(comp,Pin ): components.append( comp ) for comp in components: comp.destroy() - if subNet != self.clockNet: + if subNet != self.treeNet: subNet.destroy() trace( 550, '-' ) @@ -82,7 +81,7 @@ class ClockTree ( object ): if qt.isLeaf(): return False qt.rconnectBuffer() driverNet = qt.bOutputPlug.getNet() - driverNet.setType( Net.Type.CLOCK ) + driverNet.setType( self.treeNet.getType() ) for leaf in qt.leafs: leaf.bInputPlug.setNet( driverNet ) self._rconnectHTree( leaf ) @@ -92,7 +91,7 @@ class ClockTree ( object ): """ Recursively build one HTree branch for all non-terminal nodes of the QuadTree. """ - trace( 550, ',+', '\tClockTree._rrouteHTree() {}\n'.format(qt.bOutputPlug.getNet()) ) + trace( 550, ',+', '\tHTree._rrouteHTree() {}\n'.format(qt.bOutputPlug.getNet()) ) trace( 550, '\tOn: {}\n'.format(qt) ) if qt.isLeaf(): trace( 550, '-' ) @@ -165,13 +164,13 @@ class ClockTree ( object ): gaugeConf.setStackPosition( brContact, rightX, blY ) gaugeConf.createVertical ( rightContact, brContact, rightX, 0 ) if qt.isRoot(): - ckNet = self.clockNet + ckNet = self.treeNet if not self.spares.conf.isCoreBlock: trace( 550, '\tRemoving any previous pin...\n' ) pins = [] for pin in ckNet.getPins(): pins.append( pin ) for pin in pins: - print( WarningMessage('ClockTree._rrouteHTree(): Removing {}.'.format(pin)) ) + print( WarningMessage('HTree._rrouteHTree(): Removing {}.'.format(pin)) ) pin.destroy() layerGauge = gaugeConf.vRoutingGauge rootContact = gaugeConf.rpAccessByPlugName( qt.buffer, bufferConf.input, ckNet, 0 ) @@ -196,41 +195,44 @@ class ClockTree ( object ): def buildHTree ( self ): """ - Create the clock tree netlist in two steps: + Create the tree tree netlist in two steps: 1. Connect the buffers of the spares QuadTree to form a H-Tree. - 2. Detach the all the clock sink point and reconnect them to the + 2. Detach the all the tree sink point and reconnect them to the buffers of the leafs of the QuadTree. """ qt = self.spares.quadTree - qt.bufferTag = self.clockNet.getName() - qt.rselectBuffer( self.clockIndex, self.clockIndex, Spares.CHECK_USED|Spares.MARK_USED) + qt.bufferTag = self.treeNet.getName() + qt.rselectBuffer( self.treeIndex, self.treeIndex, Spares.CHECK_USED|Spares.MARK_USED) with UpdateSession(): self._rconnectHTree( qt ) self._rrouteHTree ( qt ) - def splitClock ( self ): + def splitNet ( self ): """ - Disconnect the registers from the main clock and reconnect them to - the leaf buffers of the clock tree. + Disconnect the sinks from the main tree and reconnect them to + the leaf buffers of the tree tree. """ bufferConf = self.spares.conf.bufferConf quadTree = self.spares.quadTree - quadTree.bufferTag = self.clockNet.getName() - quadTree.rselectBuffer( self.clockIndex, self.clockIndex, 0 ) + quadTree.bufferTag = self.treeNet.getName() + quadTree.rselectBuffer( self.treeIndex, self.treeIndex, 0 ) with UpdateSession(): - coronaPlugs = [] - hyperClock = HyperNet.create( Occurrence(self.clockNet) ) - for plugOccurrence in hyperClock.getTerminalNetlistPlugOccurrences(): - if quadTree.isUnderArea(plugOccurrence): - quadTree.attachToLeaf( plugOccurrence ) + driverPlugs = [] + hyperNet = HyperNet.create( Occurrence(self.treeNet) ) + for plugOcc in hyperNet.getTerminalNetlistPlugOccurrences(): + trace( 550, '\tReattach "{}"\n'.format(plugOcc) ) + plug = plugOcc.getEntity() + if not (plug.getMasterNet().getDirection() & Net.Direction.OUT) \ + and quadTree.isUnderArea(plugOcc): + quadTree.attachToLeaf( plugOcc ) else: - coronaPlugs.append( plugOccurrence ) + driverPlugs.append( plugOcc ) quadTree.rsplitNetlist() if self.spares.conf.isCoreBlock: plug = utils.getPlugByName( quadTree.buffer, bufferConf.input ) - plug.setNet( self.clockNet ) - trace( 550, '\tCore mode, setting only root plug "{}"\n'.format(self.clockNet.getName()) ) - trace( 550, '\tPlug of "{}" (Cell:{})\n'.format(self.clockNet.getName() - ,self.clockNet.getCell()) ) - for plug in self.clockNet.getPlugs(): + plug.setNet( self.treeNet ) + trace( 550, '\tCore mode, setting only root plug "{}"\n'.format(self.treeNet.getName()) ) + trace( 550, '\tPlug of "{}" (Cell:{})\n'.format(self.treeNet.getName() + ,self.treeNet.getCell()) ) + for plug in self.treeNet.getPlugs(): trace( 550, '\t| {}\n'.format(plug) ) diff --git a/cumulus/src/plugins/alpha/chip/chip.py b/cumulus/src/plugins/alpha/chip/chip.py index 6e7d270f..3bb2c033 100644 --- a/cumulus/src/plugins/alpha/chip/chip.py +++ b/cumulus/src/plugins/alpha/chip/chip.py @@ -120,7 +120,7 @@ class Chip ( Block ): if self.conf.routingGauge.hasPowerSupply(): power = plugins.alpha.chip.powerplane.Builder( self.conf ) power.connectPower() - power.connectClocks() + power.connectHTrees( self.hTrees ) power.doLayout() Breakpoint.stop( 101, 'After Query power.' ) else: diff --git a/cumulus/src/plugins/alpha/chip/powerplane.py b/cumulus/src/plugins/alpha/chip/powerplane.py index a50352ff..281d4bd7 100644 --- a/cumulus/src/plugins/alpha/chip/powerplane.py +++ b/cumulus/src/plugins/alpha/chip/powerplane.py @@ -455,23 +455,23 @@ class Builder ( object ): trace( 550, '-' ) self.activePlane = None - def _connectClock ( self, ck, trackNb ): - trace( 550, '\tpower.Builder._connectClock() {}\n'.format(ck) ) - blockCk = None + def _connectHTree ( self, coronaNet, trackNb ): + trace( 550, '\tpower.Builder._connectHTree() {} on track {}\n'.format(coronaNet,trackNb) ) + coreNet = 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()) ) + if plug.getNet() == coronaNet: + coreNet = plug.getMasterNet() + if not coreNet: + raise ErrorMessage( 1, 'Block "{}" has no net connected to the H-Tree "{}".' \ + .format(self.conf.icore.getName(),coronaNet.getName()) ) return htPlugs = [] - for plug in ck.getPlugs(): + for plug in coronaNet.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() + message = [ 'Net "{}" of block "{}" is not organized as a H-Tree ({} plugs).' \ + .format( coronaNet.getName() , self.conf.icore.getName() , len(htPlugs)) ] for plug in htPlugs: @@ -479,17 +479,17 @@ class Builder ( object ): raise ErrorMessage( 1, message ) return coronaPin = None - for pin in ck.getPins(): + for pin in coronaNet.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() ) ] + message = [ 'Net "{}" of block "{}" is not connected to a corona Pin.' \ + .format( coronaNet.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 ) + bufferRp = self.conf.rpAccessByOccurrence( Occurrence(htPlugs[0], Path()), coronaNet, 0 ) + pinRp = self.conf.rpAccessByOccurrence( Occurrence(coronaPin , Path()), coronaNet, 0 ) trace( 550, '\tpinRp={}\n'.format(pinRp) ) self.conf.expandMinArea( bufferRp ) self.conf.expandMinArea( pinRp ) @@ -508,40 +508,39 @@ class Builder ( object ): isVertical = False axis = coronaAb.getXMin() if isVertical: - pitch = self.conf.vRoutingGauge.getPitch() - yaxis = axis + pitch * trackNb + pitch = self.conf.hRoutingGauge.getPitch() + yaxis = axis + 2 * pitch * trackNb + trace( 550, '\tyaxis(request)={}\n'.format(DbU.getValueString(yaxis)) ) 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 ) + contact1 = self.conf.createContact( coronaNet, xaxisRp , yaxis, 0 ) + contact2 = self.conf.createContact( coronaNet, xaxisPin, yaxis, 0 ) self.conf.createVertical ( bufferRp, contact1, xaxisRp , 0 ) self.conf.createHorizontal( contact1, contact2, yaxis , 0 ) self.conf.createVertical ( contact2, pinRp , xaxisPin, 0 ) + trace( 550, '\tyaxis(track)={}\n'.format(DbU.getValueString(yaxis)) ) trace( 550, '\tcontact1={}\n'.format(contact1) ) trace( 550, '\tcontact2={}\n'.format(contact2) ) else: - pitch = self.conf.hRoutingGauge.getPitch() - xaxis = axis + pitch * trackNb + pitch = self.conf.vRoutingGauge.getPitch() + xaxis = axis + 2 * pitch * trackNb + trace( 550, '\txaxis(request)={} vpitch={}\n' \ + .format(DbU.getValueString(xaxis), DbU.getValueString(pitch)) ) 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 ) + contact1 = self.conf.createContact( coronaNet, xaxis, yaxisRp , 0 ) + contact2 = self.conf.createContact( coronaNet, xaxis, yaxisPin, 0 ) self.conf.createHorizontal( bufferRp, contact1, yaxisRp , 0 ) self.conf.createVertical ( contact1, contact2, xaxis , 0 ) self.conf.createHorizontal( contact2, pinRp , yaxisPin, 0 ) + trace( 550, '\txaxis(track)={}\n'.format(DbU.getValueString(xaxis)) ) 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+2 ) + def connectHTrees ( self, hTrees ): + for i in range(len(hTrees)): + self._connectHTree( hTrees[i].treeNet, i+2 ) def doLayout ( self ): with UpdateSession(): diff --git a/etesian/src/EtesianEngine.cpp b/etesian/src/EtesianEngine.cpp index 93522d8c..d271579e 100644 --- a/etesian/src/EtesianEngine.cpp +++ b/etesian/src/EtesianEngine.cpp @@ -323,6 +323,7 @@ namespace Etesian { , _fixedAbWidth (0) , _diodeCount (0) , _bufferCount (0) + , _excludedNets () { } @@ -792,7 +793,7 @@ namespace Etesian { cmess1 << " - Building RoutingPads (transhierarchical)" << endl; //getCell()->flattenNets( Cell::Flags::BuildRings|Cell::Flags::NoClockFlatten ); //getCell()->flattenNets( getBlockInstance(), Cell::Flags::NoClockFlatten ); - getCell()->flattenNets( NULL, Cell::Flags::NoClockFlatten ); + getCell()->flattenNets( NULL, _excludedNets, Cell::Flags::NoClockFlatten ); bool tooManyInstances = false; index_t instanceId = 0; @@ -929,9 +930,10 @@ namespace Etesian { for ( Net* net : getCell()->getNets() ) { const char* excludedType = NULL; - if (net->getType() == Net::Type::POWER ) excludedType = "POWER"; - if (net->getType() == Net::Type::GROUND) excludedType = "GROUND"; - if (net->getType() == Net::Type::CLOCK ) excludedType = "CLOCK"; + if (net->getType() == Net::Type::POWER ) excludedType = "POWER"; + if (net->getType() == Net::Type::GROUND) excludedType = "GROUND"; + if (net->getType() == Net::Type::CLOCK ) excludedType = "CLOCK"; + if (isExcluded(getString(net->getName()))) excludedType = "USER_EXCLUDED"; if (excludedType) { cparanoid << Warning( "%s is not a routable net (%s,excluded)." , getString(net).c_str(), excludedType ) << endl; @@ -952,9 +954,10 @@ namespace Etesian { for ( Net* net : getCell()->getNets() ) { const char* excludedType = NULL; - if (net->getType() == Net::Type::POWER ) excludedType = "POWER"; - if (net->getType() == Net::Type::GROUND) excludedType = "GROUND"; - if (net->getType() == Net::Type::CLOCK ) excludedType = "CLOCK"; + if (net->getType() == Net::Type::POWER ) excludedType = "POWER"; + if (net->getType() == Net::Type::GROUND) excludedType = "GROUND"; + if (net->getType() == Net::Type::CLOCK ) excludedType = "CLOCK"; + if (isExcluded(getString(net->getName()))) excludedType = "USER_EXCLUDED"; if (excludedType) continue; if (af->isBLOCKAGE(net->getName())) continue; diff --git a/etesian/src/HFNS.cpp b/etesian/src/HFNS.cpp index 5c13330b..dee8d27b 100644 --- a/etesian/src/HFNS.cpp +++ b/etesian/src/HFNS.cpp @@ -456,6 +456,8 @@ namespace Etesian { BufferDatas* bufferDatas = getBufferCells().getBiggestBuffer(); vector< tuple > netDatas; for ( Net* net : getCell()->getNets() ) { + if (isExcluded(getString(net->getName()))) continue; + uint32_t rpCount = 0; for ( RoutingPad* rp : net->getRoutingPads() ) { Occurrence rpOcc = rp->getPlugOccurrence(); diff --git a/etesian/src/PyEtesianEngine.cpp b/etesian/src/PyEtesianEngine.cpp index 7d64d34c..737fe7cb 100644 --- a/etesian/src/PyEtesianEngine.cpp +++ b/etesian/src/PyEtesianEngine.cpp @@ -77,13 +77,14 @@ extern "C" { DirectVoidMethod(EtesianEngine,etesian,clearColoquinte) DirectVoidMethod(EtesianEngine,etesian,flattenPower) DirectVoidMethod(EtesianEngine,etesian,toHurricane) - DirectGetUIntAttribute (PyEtesianEngine_doHFNS ,doHFNS ,PyEtesianEngine,EtesianEngine) - DirectSetLongAttribute (PyEtesianEngine_setFixedAbHeight,setFixedAbHeight,PyEtesianEngine,EtesianEngine) - DirectSetLongAttribute (PyEtesianEngine_setFixedAbWidth ,setFixedAbWidth ,PyEtesianEngine,EtesianEngine) - DirectSetDoubleAttribute(PyEtesianEngine_setSpaceMargin ,setSpaceMargin ,PyEtesianEngine,EtesianEngine) - DirectSetDoubleAttribute(PyEtesianEngine_setAspectRatio ,setAspectRatio ,PyEtesianEngine,EtesianEngine) - DirectGetLongAttribute (PyEtesianEngine_getFixedAbHeight,getFixedAbHeight,PyEtesianEngine,EtesianEngine) - DirectGetLongAttribute (PyEtesianEngine_getFixedAbWidth ,getFixedAbWidth ,PyEtesianEngine,EtesianEngine) + DirectGetUIntAttribute (PyEtesianEngine_doHFNS ,doHFNS ,PyEtesianEngine,EtesianEngine) + DirectSetLongAttribute (PyEtesianEngine_setFixedAbHeight,setFixedAbHeight,PyEtesianEngine,EtesianEngine) + DirectSetLongAttribute (PyEtesianEngine_setFixedAbWidth ,setFixedAbWidth ,PyEtesianEngine,EtesianEngine) + DirectSetDoubleAttribute (PyEtesianEngine_setSpaceMargin ,setSpaceMargin ,PyEtesianEngine,EtesianEngine) + DirectSetDoubleAttribute (PyEtesianEngine_setAspectRatio ,setAspectRatio ,PyEtesianEngine,EtesianEngine) + DirectGetLongAttribute (PyEtesianEngine_getFixedAbHeight,getFixedAbHeight,PyEtesianEngine,EtesianEngine) + DirectGetLongAttribute (PyEtesianEngine_getFixedAbWidth ,getFixedAbWidth ,PyEtesianEngine,EtesianEngine) + DirectSetCStringAttribute(PyEtesianEngine_exclude ,exclude ,PyEtesianEngine,EtesianEngine) static PyObject* PyEtesianEngine_get ( PyObject*, PyObject* args ) @@ -236,6 +237,8 @@ extern "C" { , "Returns the Etesian engine attached to the Cell, None if there isn't." } , { "create" , (PyCFunction)PyEtesianEngine_create , METH_VARARGS|METH_STATIC , "Create an Etesian engine on this cell." } + , { "exclude" , (PyCFunction)PyEtesianEngine_exclude , METH_VARARGS + , "Add the specified net to the exclusion list." } , { "getFixedAbHeight" , (PyCFunction)PyEtesianEngine_getFixedAbHeight , METH_NOARGS , "Returns the forced abutment box height." } , { "getFixedAbWidth" , (PyCFunction)PyEtesianEngine_getFixedAbWidth , METH_NOARGS diff --git a/etesian/src/etesian/EtesianEngine.h b/etesian/src/etesian/EtesianEngine.h index 7dd8b414..6e9c1c0f 100644 --- a/etesian/src/etesian/EtesianEngine.h +++ b/etesian/src/etesian/EtesianEngine.h @@ -71,11 +71,13 @@ namespace Etesian { typedef std::tuple > InstanceInfos; typedef std::map InstancesToIds; typedef std::map NetsToIds; + typedef std::set NetNameSet; public: static const Name& staticGetName (); static EtesianEngine* create ( Cell* ); static EtesianEngine* get ( const Cell* ); public: + inline bool isExcluded ( std::string ) const; virtual Configuration* getConfiguration (); virtual const Configuration* getConfiguration () const; virtual const Name& getName () const; @@ -106,6 +108,7 @@ namespace Etesian { inline void setViewer ( Hurricane::CellViewer* ); inline Cell* getBlockCell () const; inline Instance* getBlockInstance () const; + inline const NetNameSet& getExcludedNets () const; inline void setBlock ( Instance* ); inline void setFixedAbHeight ( DbU::Unit ); inline void setFixedAbWidth ( DbU::Unit ); @@ -136,6 +139,7 @@ namespace Etesian { uint32_t doHFNS (); inline void useFeed ( Cell* ); size_t findYSpin (); + inline void exclude ( string netName ); void addFeeds (); void toHurricane (); void flattenPower (); @@ -174,6 +178,7 @@ namespace Etesian { DbU::Unit _fixedAbWidth; uint32_t _diodeCount; uint32_t _bufferCount; + NetNameSet _excludedNets; protected: // Constructors & Destructors. @@ -229,6 +234,8 @@ namespace Etesian { inline uint32_t EtesianEngine::_getNewDiodeId () { return _diodeCount++; } inline const Box& EtesianEngine::getPlaceArea () const { return _placeArea; } inline Area* EtesianEngine::getArea () const { return _area; } + inline const EtesianEngine::NetNameSet& + EtesianEngine::getExcludedNets () const { return _excludedNets; } inline void EtesianEngine::setBlock ( Instance* block ) { @@ -319,6 +326,24 @@ namespace Etesian { } + inline bool EtesianEngine::isExcluded ( string netName ) const + { return (_excludedNets.find(netName) != _excludedNets.end()); } + + + inline void EtesianEngine::exclude ( string netName ) + { + // Net* net = getCell()->getNet( netName ); + // if (not net) { + // std::cerr << Error( "EtesianEngine::exclude(Net*): %s has no net named \"%s\"." + // , getString(getCell()).c_str() + // , netName.c_str() + // ) << std::endl; + // return; + // } + if (isExcluded(netName)) return; + _excludedNets.insert( netName ); + } + // Variables. extern const char* missingEtesian; diff --git a/hurricane/src/hurricane/Cell.cpp b/hurricane/src/hurricane/Cell.cpp index 1e1c2e9c..d4f54eb0 100644 --- a/hurricane/src/hurricane/Cell.cpp +++ b/hurricane/src/hurricane/Cell.cpp @@ -820,6 +820,13 @@ void Cell::flattenNets (uint64_t flags ) void Cell::flattenNets ( const Instance* instance, uint64_t flags ) // **************************************************************** +{ + static set excludeds; + flattenNets( instance, excludeds, flags ); +} + +void Cell::flattenNets ( const Instance* instance, const std::set& excludeds, uint64_t flags ) +// *************************************************************************************************** { cdebug_log(18,1) << "Cell::flattenNets() flags:0x" << hex << flags << endl; @@ -837,6 +844,7 @@ void Cell::flattenNets ( const Instance* instance, uint64_t flags ) if (net->isClock() and (flags & Flags::NoClockFlatten)) continue; if (net->isPower() or net->isGround() or net->isBlockage()) continue; + if (excludeds.find(getString(occurrence.getName())) != excludeds.end()) continue; HyperNet hyperNet ( occurrence ); if ( not occurrence.getPath().isEmpty() ) { diff --git a/hurricane/src/hurricane/Net.cpp b/hurricane/src/hurricane/Net.cpp index 45f612d1..e1e3e033 100644 --- a/hurricane/src/hurricane/Net.cpp +++ b/hurricane/src/hurricane/Net.cpp @@ -728,9 +728,6 @@ void Net::_preDestroy() // ******************* { cdebug_log(18,1) << "entering Net::_preDestroy: " << this << endl; - if (getName() == "pipe_middle_0_rb[0]") - cerr << "entering Net::_preDestroy: " << this << endl; - Inherit::_preDestroy(); cdebug_log(18,0) << "Net::_preDestroy: " << this << " slave Plugs..." << endl; diff --git a/hurricane/src/hurricane/hurricane/Cell.h b/hurricane/src/hurricane/hurricane/Cell.h index 3fa03881..baa07b5d 100644 --- a/hurricane/src/hurricane/hurricane/Cell.h +++ b/hurricane/src/hurricane/hurricane/Cell.h @@ -507,6 +507,7 @@ class Cell : public Entity { public: void setAbstractedSupply(bool state) { _flags.set(Flags::AbstractedSupply,state); }; public: void flattenNets(uint64_t flags=Flags::BuildRings); public: void flattenNets(const Instance* instance, uint64_t flags=Flags::BuildRings); + public: void flattenNets(const Instance* instance, const std::set& excludeds, uint64_t flags=Flags::BuildRings); public: void createRoutingPadRings(uint64_t flags=Flags::BuildRings); public: void setFlags(uint64_t flags) { _flags |= flags; } public: void resetFlags(uint64_t flags) { _flags &= ~flags; } diff --git a/hurricane/src/isobar/PyCell.cpp b/hurricane/src/isobar/PyCell.cpp index b5edf3ff..f93680f9 100644 --- a/hurricane/src/isobar/PyCell.cpp +++ b/hurricane/src/isobar/PyCell.cpp @@ -29,6 +29,24 @@ namespace Isobar { + + bool pyListToStringSet ( PyObject* list, set& v ) + { + if (not PyList_Check(list)) return false; + + int length = PyList_Size( list ); + for ( int i=0 ; i excludeds; + Instance* instance = NULL; + PyObject* arg0 = NULL; + PyObject* arg1 = NULL; + PyObject* arg2 = NULL; HTRY METHOD_HEAD ( "Cell.flattenNets()" ) __cs.init( "Cell.flattenNets" ); - if (not PyArg_ParseTuple(args,"O&O&:Cell.flattenNets" - ,Converter,&arg0 - ,Converter,&arg1 - )) { - PyErr_SetString( ConstructorError, "Cell.flattenNets(): Takes exactly two parameters." ); - return NULL; - } - - if (arg0 == Py_None) { - cell->flattenNets( NULL, PyInt_AsLong(arg1) ); - } else if (__cs.getObjectIds() == ":ent:int") { - cell->flattenNets( PYINSTANCE_O(arg0), PyInt_AsLong(arg1) ); - } else { - string message = "Cell.flattenNets(): Bad type of parameter(s), \"" + __cs.getObjectIds() + "\"."; - PyErr_SetString( ConstructorError, message.c_str() ); + if (not PyArg_ParseTuple(args,"OO|O:Cell.flattenNets", &arg0, &arg1, &arg2 )) { + PyErr_SetString( ConstructorError, "Cell.flattenNets(): Takes between two and three parameters." ); return NULL; } + + if (arg0 != Py_None) { + if (not IsPyInstance(arg0)) { + PyErr_SetString( ConstructorError, "Cell.flattenNets(): First argument must be None or an Instance." ); + return NULL; + } + instance = PYINSTANCE_O( arg0 ); + } + if (arg2) { + pyListToStringSet( arg1, excludeds ); + flags = PyInt_AsLong( arg2 ); + } else { + flags = PyInt_AsLong( arg1 ); + } + + cell->flattenNets( instance, excludeds, flags ); HCATCH Py_RETURN_NONE; }