Simple HFNS (#4), break the net, regardless of placement.
* New: In cumulus/plugins.block.hfns4.py, perform simple HFNS by breaking the net into sub-nets of at most 10 sinks (hard-coded for now). As this method is called *after* the netlist as been virtually flattened, we have to create the RoutingPad at the top level ourselves. Sub-nets are created at the Cell top-level (same approach as for clock synthesis, because there is no smart way to guess where they should be). * New: In cumulus/plugins.block.block.py, perform HFNS (#4) *before* doing placement. To see the real sink count on each net, we must perform the virtual net flattening first (Cell::flattenNets()). * Change: In cumulus/plugins.block.configuration, allow the creation of spare buffer in any cell (instead of only "self.cellPnR"). * Change: In cumulus/plugins.block.spares.Spares.raddTransNet(), Check if intermediate masterNet exists in Cells before trying to blindly re-create it.
This commit is contained in:
parent
b0ae6be652
commit
e3803d28d7
|
@ -58,6 +58,7 @@
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/hfns1.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/hfns2.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/hfns3.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/block/hfns4.py
|
||||
)
|
||||
set ( pyPluginAlphaC2C ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/core2chip/__init__.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/core2chip/core2chip.py
|
||||
|
|
|
@ -21,7 +21,7 @@ from Hurricane import Breakpoint, DbU, Box, Transformation, Point, \
|
|||
Box, Path, Layer, Occurrence, Net, \
|
||||
NetExternalComponents, RoutingPad, Pad, \
|
||||
Horizontal, Vertical, Contact, Pin, Plug, \
|
||||
Instance
|
||||
Cell, Instance
|
||||
import CRL
|
||||
from CRL import RoutingLayerGauge
|
||||
from helpers import trace, dots
|
||||
|
@ -37,7 +37,8 @@ from alpha.block.spares import Spares
|
|||
from alpha.block.clocktree import ClockTree
|
||||
#from alpha.block.hfns1 import BufferTree
|
||||
#from alpha.block.hfns2 import BufferTree
|
||||
from alpha.block.hfns3 import BufferTree
|
||||
#from alpha.block.hfns3 import BufferTree
|
||||
from alpha.block.hfns4 import BufferTree
|
||||
from alpha.block.configuration import IoPin, BlockConf, GaugeConf
|
||||
|
||||
timing.staticInit()
|
||||
|
@ -407,6 +408,44 @@ class Block ( object ):
|
|||
for clockTree in self.clockTrees:
|
||||
clockTree.splitClock()
|
||||
|
||||
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 )
|
||||
beginCount = self.conf.bufferConf.count
|
||||
maxSinks = 10
|
||||
dots( 82
|
||||
, ' - Max sinks for buffer "{}"'.format(self.conf.bufferConf.name)
|
||||
, ' {}'.format(maxSinks) )
|
||||
nets = []
|
||||
block = self.conf.corona if self.conf.isCoreBlock else self.conf.cell
|
||||
for net in block.getNets():
|
||||
sinksCount = 0
|
||||
for rp in net.getRoutingPads(): sinksCount += 1
|
||||
if sinksCount > maxSinks:
|
||||
nets.append( (net,sinksCount) )
|
||||
with UpdateSession():
|
||||
for net,sinksCount in nets:
|
||||
trace( 550, '\tBlock.addHfnTrees4(): Found high fanout net "{}" ({} sinks).\n' \
|
||||
.format(net.getName(),sinksCount) )
|
||||
#if not net.getName().startswith('alu_m_muls_b(1)'): continue
|
||||
#if not net.getName().startswith('abc_75177_new_n12236'): continue
|
||||
sys.stderr.flush()
|
||||
print( ' - "{}", {} sinks.'.format(net.getName(),sinksCount) )
|
||||
sys.stdout.flush()
|
||||
self.hfnTrees.append( BufferTree( self.spares, net ) )
|
||||
self.hfnTrees[-1].buildBTree()
|
||||
Breakpoint.stop( 100, 'block.findHfnTrees4() done.' )
|
||||
else:
|
||||
print( ' (No spares buffers, disabled)' )
|
||||
endCount = self.conf.bufferConf.count
|
||||
dots( 82, ' - Added buffers', ' {}'.format(endCount-beginCount) )
|
||||
return len(self.hfnTrees)
|
||||
|
||||
def findHfnTrees ( self ):
|
||||
"""Create the trunk of all the high fanout nets."""
|
||||
print( ' o Building high fanout nets trees.' )
|
||||
|
@ -486,9 +525,13 @@ class Block ( object ):
|
|||
side.expand()
|
||||
|
||||
def place ( self ):
|
||||
editor = self.conf.editor
|
||||
if self.conf.isCoreBlock:
|
||||
etesian = Etesian.EtesianEngine.create( self.conf.corona )
|
||||
etesian.setBlock( self.conf.icore )
|
||||
if editor:
|
||||
editor.setCell( self.conf.cell )
|
||||
Breakpoint.stop( 100, 'Block.place(), corona loaded.')
|
||||
else:
|
||||
etesian = Etesian.EtesianEngine.create( self.conf.cell )
|
||||
etesian.place()
|
||||
|
@ -566,12 +609,13 @@ class Block ( object ):
|
|||
self.placeIoPins()
|
||||
self.checkIoPins()
|
||||
self.spares.build()
|
||||
if self.conf.useHFNS: self.findHfnTrees4()
|
||||
if self.conf.useClockTree: self.addClockTrees()
|
||||
if self.conf.useHFNS: self.addHfnBuffers()
|
||||
#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()
|
||||
#if self.conf.useHFNS: self.findHfnTrees()
|
||||
break
|
||||
if self.conf.useClockTree: self.splitClocks()
|
||||
if self.conf.isCoreBlock: self.doConnectCore()
|
||||
|
|
|
@ -719,6 +719,10 @@ class BufferConf ( object ):
|
|||
where ``<Nb>`` is an ever incrementing counter (self.count).
|
||||
"""
|
||||
instance = Instance.create( cell, 'spare_buffer_{}'.format(self.count), self.masterCell )
|
||||
trace( 550, '\tBufferConf.createBuffer(): cell={}, instance={}\n' \
|
||||
.format( cell, instance ))
|
||||
trace( 550, '\tplug={}\n'.format( instance.getPlug( self.masterCell.getNet('q') ) ))
|
||||
trace( 550, '\tplug.getCell()={}\n'.format( instance.getPlug( self.masterCell.getNet('q') ).getCell() ))
|
||||
self.count += 1
|
||||
return instance
|
||||
|
||||
|
@ -1155,8 +1159,9 @@ class BlockConf ( GaugeConf ):
|
|||
self.editor.setCell( cell )
|
||||
self.editor.fit()
|
||||
|
||||
def createBuffer ( self ):
|
||||
return self.bufferConf.createBuffer( self.cellPnR )
|
||||
def createBuffer ( self, cell=None ):
|
||||
if cell is None: cell = self.cellPnR
|
||||
return self.bufferConf.createBuffer( cell )
|
||||
|
||||
def createFeed ( self ):
|
||||
return self.feedsConf.createFeed( self.cellPnR )
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
#
|
||||
# This file is part of the Coriolis Software.
|
||||
# Copyright (c) SU 2020-2020, 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/block/hfns.py" |
|
||||
# +-----------------------------------------------------------------+
|
||||
|
||||
"""
|
||||
Manage High Fanout Net Synthesis (HFNS).
|
||||
|
||||
Fourth variant, quck and simple. Just break the net into subnets,
|
||||
so each of them is under the fanout threshold. Basic method, do not
|
||||
take into account the placement or the global wirelength.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import os.path
|
||||
import re
|
||||
from operator import itemgetter, attrgetter, methodcaller
|
||||
import Cfg
|
||||
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, l, u, n
|
||||
from helpers.io import ErrorMessage, WarningMessage, catch
|
||||
from helpers.overlay import UpdateSession
|
||||
from plugins import getParameter, utils
|
||||
from plugins.alpha.block.configuration import GaugeConf
|
||||
from plugins.alpha.block.spares import Spares
|
||||
|
||||
|
||||
af = CRL.AllianceFramework.get()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Class : "hfns.Cluster".
|
||||
|
||||
class Cluster ( object ):
|
||||
"""
|
||||
Cluster of routing pads, with one driver.
|
||||
"""
|
||||
|
||||
def __init__ ( self, bufferTree, depth ):
|
||||
self.bufferTree = bufferTree
|
||||
self.buffer = None
|
||||
self.depth = depth
|
||||
self.anchors = [ ]
|
||||
self.childs = [ ]
|
||||
self.parent = None
|
||||
self.bInputRp = None
|
||||
self.bOutputRp = None
|
||||
trace( 550, '\tCluster.__init__(), depth={}\n'.format( self.depth ))
|
||||
|
||||
@property
|
||||
def conf ( self ):
|
||||
return self.bufferTree.conf
|
||||
|
||||
@property
|
||||
def bInputPlug ( self ):
|
||||
"""The input Plug of the buffer."""
|
||||
return utils.getPlugByName( self.buffer, self.conf.bufferConf.input )
|
||||
|
||||
@property
|
||||
def bOutputPlug ( self ):
|
||||
"""The output Plug of the buffer."""
|
||||
return utils.getPlugByName( self.buffer, self.conf.bufferConf.output )
|
||||
|
||||
@property
|
||||
def size ( self ):
|
||||
return len(self.anchors)
|
||||
|
||||
def isRoot ( self ): return self.parent is None
|
||||
|
||||
def addAnchor ( self, anchor ):
|
||||
"""Add an anchor. Can be RoutingPad (depth==0) or Cluster (depth>0)."""
|
||||
if isinstance(anchor,Cluster) and self.depth == 0:
|
||||
print( WarningMessage( 'Cluster.addAnchor(): Should not add a cluster at leaf level.' ) )
|
||||
elif isinstance(anchor,RoutingPad) and self.depth > 0:
|
||||
print( WarningMessage( 'Cluster.addAnchor(): Should not add a RoutingPad at non-leaf level.' ) )
|
||||
self.anchors.append( anchor )
|
||||
trace( 550, '\tCluster.addAnchor(), size={} anchor={}\n'.format( self.size, anchor ))
|
||||
|
||||
def createBufInputRp ( self, net ):
|
||||
"""Create a RoutingPad for the buffer input Plug (terminal)."""
|
||||
blockNet = self.createTransNet( net, Path(self.conf.icore) )
|
||||
self.bInputPlug.setNet( blockNet )
|
||||
self.bInputRp = RoutingPad.create( net
|
||||
, Occurrence( self.bInputPlug, Path(self.conf.icore) )
|
||||
, RoutingPad.BiggestArea )
|
||||
trace( 550, '\tCluster.createBufInputRp(): rp={}\n'.format(self.bInputRp) )
|
||||
return self.bInputRp
|
||||
|
||||
def createBufOutputRp ( self, net ):
|
||||
"""Create a RoutingPad for the buffer output Plug (terminal)."""
|
||||
blockNet = self.createTransNet( net, Path(self.conf.icore) )
|
||||
self.bOutputPlug.setNet( blockNet )
|
||||
self.bOutputRp = RoutingPad.create( net
|
||||
, Occurrence( self.bOutputPlug, Path(self.conf.icore) )
|
||||
, RoutingPad.BiggestArea )
|
||||
trace( 550, '\tCluster.createBufOutputRp(): rp={}\n'.format(self.bOutputRp) )
|
||||
return self.bOutputRp
|
||||
|
||||
def setRootDriver ( self, net ):
|
||||
"""Connect the top-level buffer input to the original signal."""
|
||||
if not self.isRoot():
|
||||
raise ErrorMessage( 2, 'Cluster.setRootDriver(): Must be called only on the top root cluster.' )
|
||||
self.createBufInputRp( net )
|
||||
|
||||
def createBuffer ( self ):
|
||||
"""Create the cluster's buffer."""
|
||||
self.buffer = self.conf.createBuffer( self.conf.cell )
|
||||
|
||||
def createTransNet ( self, topNet, path ):
|
||||
trace( 550, '\tCluster.createTransNet(): topNet={}, path={}\n'.format(topNet,path) )
|
||||
bottomPlug = self.bufferTree.raddTransNet( topNet, path )
|
||||
bottomNet = bottomPlug.getMasterNet() if bottomPlug else topNet
|
||||
trace( 550, '\tbottomNet: "{}"\n'.format(bottomNet) )
|
||||
return bottomNet
|
||||
|
||||
def splitNet ( self ):
|
||||
"""
|
||||
Perform the actual splitting of the net into subnets.
|
||||
"""
|
||||
driverNet = self.bufferTree.createSubNet()
|
||||
self.createBuffer()
|
||||
self.createBufOutputRp( driverNet )
|
||||
trace( 550, ',+', '\tCluster.splitNet(), size:{} depth:{} driver:{}\n' \
|
||||
.format(self.size,self.depth,driverNet.getName()) )
|
||||
if len(self.anchors) > 30:
|
||||
print( WarningMessage( 'Cluster.splitNet(): Top cluster of "{}" still has {} sinks.' \
|
||||
.format(driverNet.getName(),len(self.mergedAnchors)) ))
|
||||
for anchor in self.anchors:
|
||||
if isinstance(anchor,Cluster):
|
||||
anchor.createBufInputRp( driverNet )
|
||||
else:
|
||||
plug = anchor.getPlugOccurrence()
|
||||
deepPlug = self.bufferTree.raddTransNet( driverNet, plug.getPath() )
|
||||
deepNetBuff = deepPlug.getMasterNet() if deepPlug else driverNet
|
||||
trace( 550, '\tdeepNetBuf: "{}"\n'.format(deepNetBuff) )
|
||||
if isinstance(plug.getEntity(),Pin):
|
||||
print( 'PIN, SKIPPED for {}'.format(deepNetBuff.getName()) )
|
||||
continue
|
||||
plug.getEntity().setNet( deepNetBuff )
|
||||
anchor.destroy()
|
||||
rp = RoutingPad.create( driverNet, plug, RoutingPad.BiggestArea )
|
||||
for component in driverNet.getComponents():
|
||||
trace( 550, '\t| {}\n'.format(component) )
|
||||
trace( 550, ',-' )
|
||||
|
||||
def show ( self ):
|
||||
"""Select the RoutingPad of the cluster in the editor."""
|
||||
editor = self.bufferTree.spares.conf.editor
|
||||
if not editor: return False
|
||||
editor.unselectAll()
|
||||
editor.setCumulativeSelection( True )
|
||||
editor.setShowSelection( True )
|
||||
area = Box( self.area )
|
||||
area.inflate( l(10.0) )
|
||||
editor.reframe( area, False )
|
||||
#editor.select( self.anchor.getOccurrence() )
|
||||
for anchor in self.mergedAnchors:
|
||||
if isinstance(anchor,Cluster):
|
||||
continue
|
||||
else:
|
||||
editor.select( anchor.getOccurrence() )
|
||||
return True
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Class : "hfns.BufferTree".
|
||||
|
||||
class BufferTree ( object ):
|
||||
"""
|
||||
Recursively break down a Net with a huge fanout. Works at pure netlist
|
||||
level, do not take placement or wirelength into account.
|
||||
"""
|
||||
|
||||
patVhdlVector = re.compile( r'(?P<name>.*)\((?P<index>\d+)\)' )
|
||||
|
||||
def __init__ ( self, spares, net ):
|
||||
trace( 550, '\tBufferTree.__init__() on "{}".\n'.format(net.getName()) )
|
||||
self.spares = spares
|
||||
self.net = net
|
||||
self.isDeepNet = True
|
||||
self.clusterDepth = 0
|
||||
self.clusters = [ [] ]
|
||||
self.bufName = self.conf.bufferConf.name
|
||||
self.netCount = 0
|
||||
self.netName = self.net.getName()
|
||||
self.netIndex = None
|
||||
m = BufferTree.patVhdlVector.match( self.net.getName() )
|
||||
if m:
|
||||
self.netName = m.group('name')
|
||||
self.netIndex = m.group('index')
|
||||
|
||||
@property
|
||||
def conf ( self ):
|
||||
return self.spares.conf
|
||||
|
||||
@property
|
||||
def root ( self ):
|
||||
"""The root cluster of the tree (must be unique...)"""
|
||||
if len(self.clusters[-1]) != 1:
|
||||
raise ErrorMessage( 2, 'BufferTree.root: No, or multiple root for "{}".' \
|
||||
.format(self.net.getName()) )
|
||||
return self.clusters[-1][0]
|
||||
|
||||
def createSubNet ( self ):
|
||||
"""
|
||||
Create a new sub-net for a buffer driver. If the signal is a bit
|
||||
from a vector, unvectorize but keep a ``bitX`` tag in it. For example,
|
||||
the third (i.e. index 2) auxiliary signal for ``my_vector(3)`` will give
|
||||
``my_vector_bit3_2``.
|
||||
"""
|
||||
if self.netIndex is None:
|
||||
subNetName = '{}_hfns_{}'.format( self.netName, self.netCount )
|
||||
else:
|
||||
subNetName = '{}_bit{}_hfns_{}'.format( self.netName, self.netIndex, self.netCount )
|
||||
topCell = self.conf.cellPnR
|
||||
net = Net.create( topCell, subNetName )
|
||||
self.netCount += 1
|
||||
return net
|
||||
|
||||
def raddTransNet ( self, net, path ):
|
||||
return self.spares.raddTransNet( net, path )
|
||||
|
||||
def rpartition ( self ):
|
||||
"""
|
||||
Recursively partition the net.
|
||||
"""
|
||||
trace( 550, ',+', '\tBufferTree.rpartition() on "{}" ...\n'.format(self.net.getName()) )
|
||||
self.rpDriver = None
|
||||
pinRp = None
|
||||
for rp in self.net.getRoutingPads():
|
||||
rpOccurrence = rp.getPlugOccurrence()
|
||||
entity = rpOccurrence.getEntity()
|
||||
if rpOccurrence.getPath().isEmpty():
|
||||
self.isDeepNet = False
|
||||
if isinstance(entity,Pin):
|
||||
pinRp = rp
|
||||
continue
|
||||
masterNet = entity.getMasterNet()
|
||||
if masterNet.getDirection() & Net.Direction.DirIn:
|
||||
if len(self.clusters[0]) == 0 or self.clusters[0][-1].size >= 10:
|
||||
self.clusters[0].append( Cluster(self,self.clusterDepth) )
|
||||
self.clusters[0][-1].addAnchor( rp )
|
||||
else:
|
||||
trace( 550, '\tDriver:{}.\n'.format(rp) )
|
||||
self.rpDriver = rp
|
||||
if pinRp:
|
||||
if self.rpDriver is None:
|
||||
trace( 550, '\tDriver (external pin):{}.\n'.format(rp) )
|
||||
self.rpDriver = rp
|
||||
else:
|
||||
self.clusters[0][-1].addAnchor( pinRp )
|
||||
while len(self.clusters[-1]) > 1:
|
||||
for cluster in self.clusters[self.clusterDepth]:
|
||||
cluster.splitNet()
|
||||
if len(self.clusters) < self.clusterDepth+2:
|
||||
self.clusters.append( [] )
|
||||
self.clusters[-1].append( Cluster(self,self.clusterDepth+1) )
|
||||
if self.clusters[-1][-1].size >= 10:
|
||||
self.clusters[-1].append( Cluster(self,self.clusterDepth+1) )
|
||||
self.clusters[-1][-1].addAnchor( cluster )
|
||||
self.clusterDepth += 1
|
||||
self.clusters[-1][-1].splitNet()
|
||||
trace( 550, '-' )
|
||||
|
||||
def _splitNet ( self ):
|
||||
"""
|
||||
Perform the actual splitting of the net into sub-trees. Mostly calls
|
||||
``BufferTree.rpartition()`` then connect the top cluster root to the original
|
||||
signal.
|
||||
"""
|
||||
self.rpartition()
|
||||
if self.isDeepNet:
|
||||
# Must convert from a DeepNet into a real top Net to be saved.
|
||||
topCell = self.conf.corona if self.conf.isCoreBlock else self.conf.cell
|
||||
topNetName = self.net.getName()
|
||||
driverRpOcc = self.rpDriver.getPlugOccurrence()
|
||||
self.net.destroy()
|
||||
self.net = Net.create( topCell, topNetName )
|
||||
deepPlug = self.spares.raddTransNet( self.net, driverRpOcc.getPath() )
|
||||
deepDriverNet = deepPlug.getMasterNet()
|
||||
driverRpOcc.getEntity().setNet( deepDriverNet )
|
||||
rp = RoutingPad.create( self.net, driverRpOcc, RoutingPad.BiggestArea )
|
||||
trace( 550, '\tBufferTree._splitNet(): rp={}\n'.format(rp) )
|
||||
self.root.setRootDriver( self.net )
|
||||
trace( 550, '\tRoot input: {}\n'.format(self.root.bInputPlug) )
|
||||
|
||||
def buildBTree ( self ):
|
||||
self._splitNet()
|
|
@ -928,20 +928,25 @@ class Spares ( object ):
|
|||
*plug master net* and the *tail path*.
|
||||
"""
|
||||
|
||||
trace( 540, '\tSpares.raddTransNet() top:{} path:{}\n'.format(topNet,path) )
|
||||
trace( 540, ',+', '\tSpares.raddTransNet() top:{} path:{}\n'.format(topNet,path) )
|
||||
if path.isEmpty():
|
||||
self.conf.addClonedCell( topNet.getCell() )
|
||||
trace( 540, '-' )
|
||||
return None
|
||||
tailPath = path.getTailPath()
|
||||
headInstance = path.getHeadInstance()
|
||||
headPlug = utils.getPlugByNet(headInstance,topNet)
|
||||
if not headPlug:
|
||||
masterCell = headInstance.getMasterCell()
|
||||
masterNet = masterCell.getNet( topNet.getName() )
|
||||
trace( 540, '\tmasterCell {}\n'.format(masterCell) )
|
||||
if masterNet is None:
|
||||
trace( 540, '\tcreate Plug in {}\n'.format(headInstance) )
|
||||
masterNet = Net.create( masterCell, topNet.getName() )
|
||||
masterNet.setExternal ( True )
|
||||
masterNet.setType ( topNet.getType() )
|
||||
masterNet.setDirection( Net.Direction.IN )
|
||||
trace( 540, '\tmasterNet {}\n'.format(masterNet) )
|
||||
masterNet.setExternal( True )
|
||||
headPlug = headInstance.getPlug( masterNet )
|
||||
if not headPlug:
|
||||
raise ErrorMessage( 3, 'Plug not created for %s on instance %s of %s' \
|
||||
|
@ -952,8 +957,10 @@ class Spares ( object ):
|
|||
else:
|
||||
masterNet = headPlug.getMasterNet()
|
||||
trace( 540, '\ttailPath {}\n'.format(tailPath) )
|
||||
if tailPath.isEmpty(): return headPlug
|
||||
return self.raddTransNet( masterNet, tailPath )
|
||||
if not tailPath.isEmpty():
|
||||
headPlug = self.raddTransNet( masterNet, tailPath )
|
||||
trace( 540, '-' )
|
||||
return headPlug
|
||||
|
||||
def removeUnusedBuffers ( self ):
|
||||
with UpdateSession():
|
||||
|
|
Loading…
Reference in New Issue