From fb4a7457a1df5d7c35203e42a273e694e82a3ba5 Mon Sep 17 00:00:00 2001 From: Jean-Paul Chaput Date: Fri, 15 Aug 2014 19:05:27 +0200 Subject: [PATCH] First basic version of ClockTree & Chip plugins. * New: In Cumulus, first versions of the ClockTree and Chip plugins. Clock Tree plugin: - It is strongly advised to use have 4 metal routing layers for the tree to work. Otherwise, problems can arise with the detailed routing (fully obstructed terminals). - H-Tree can only be build (for now) for design with a form factor between 0.5 an 2. - The tree is created at the block top-level and only the leafs are trans-hierarchically created on the instances/models. The new cell with a clock tree, along with all it's sub-models is created with a "_clocked" suffix. - Leaf cells are connected through a simple Minimum Steiner Tree. - Shorts are avoided by a systematic shift of the wires according to their kind. No wire must pre-exist. When used as a sub-module of "chip" the wires cannot be moved. When created on a block, the wires can be loaded in the detailed router as manual global router. Chip Plugin: - Perform the pad placement and corona creation. Replacement at last of the clunky code from Wu Yifei. - Relies on a Python configuration file '_chip.py' with a "chip" dictionnary. --- crlcore/src/crlcore/helpers/__init__.py | 92 ++- cumulus/src/Alliance.py | 894 +++++++++++++++++++-- cumulus/src/CMakeLists.txt | 7 +- cumulus/src/plugins/ChipPlugin.py | 57 +- cumulus/src/plugins/ClockTreePlugin.py | 114 +++ cumulus/src/plugins/__init__.py | 2 + cumulus/src/plugins/chip/BlockCorona.py | 146 ++-- cumulus/src/plugins/chip/BlockPower.py | 80 +- cumulus/src/plugins/chip/Configuration.py | 464 +++++++++-- cumulus/src/plugins/chip/PadsCorona.py | 54 +- cumulus/src/plugins/clocktree/ClockTree.py | 569 +++++++++++++ cumulus/src/plugins/clocktree/RSMT.py | 371 +++++++++ cumulus/src/plugins/clocktree/__init__.py | 0 13 files changed, 2555 insertions(+), 295 deletions(-) create mode 100755 cumulus/src/plugins/ClockTreePlugin.py create mode 100755 cumulus/src/plugins/clocktree/ClockTree.py create mode 100644 cumulus/src/plugins/clocktree/RSMT.py create mode 100644 cumulus/src/plugins/clocktree/__init__.py diff --git a/crlcore/src/crlcore/helpers/__init__.py b/crlcore/src/crlcore/helpers/__init__.py index 4d5f5504..33b3676b 100644 --- a/crlcore/src/crlcore/helpers/__init__.py +++ b/crlcore/src/crlcore/helpers/__init__.py @@ -31,6 +31,8 @@ symbolicTechno = 'cmos' symbolicDir = None realTechno = 'hcmos9' realDir = None +tab = None +_trace = None def stype ( o ): return str(type(o)).split("'")[1] @@ -168,18 +170,100 @@ class Dots ( object ): return -def staticInitialization (): +class Tab ( object ): + + def __init__ ( self, stepSize=2 ): + self._stepSize = stepSize + self._tsize = 0 + return + + def inc ( self, step=1 ): self._tsize += step*self._stepSize + def dec ( self, step=1 ): + if step*self._stepSize < self._tsize: + self._tsize -= step*self._stepSize + else: + self._tsize = 0 + return + + def __str__ ( self ): return ' '*self._tsize + + +class Trace ( object ): + + def __init__ ( self ): + self._tab = Tab() + self._level = 10000 + return + + @property + def level ( self ): return self._level + @level.setter + def level ( self, level ): self._level = level + + def write ( self, level, arg1='', arg2=None ): + if level < self._level: return + + sflags = [ '', '' ] + message = None + if isinstance(arg1,str) and len(arg1) and arg1[0] in '+-,': + sflags = arg1.split( ',' , 1 ) + if len(sflags) < 2: sflags.append('') + message = arg2 + else: + message = arg1 + + for f in sflags[0]: + if f == '+': self._tab.inc() + if f == '-': self._tab.dec() + + if message: + if not isinstance(message,str): + message = '\t'+str(message)+'\n' + + if len(message) and message[0] == '\t': + sys.stderr.write( str(self._tab) ) + sys.stderr.write( message[1:] ) + else: + sys.stderr.write( message ) + sys.stderr.flush() + + for f in sflags[1]: + if f == '+': self._tab.inc() + if f == '-': self._tab.dec() + return + + +def trace ( *args ): + global _trace + _trace.write( *args ) + return + + +def setTraceLevel ( level ): + global _trace + _trace.level = level + return + + +def staticInitialization ( quiet=False ): global sysConfDir global symbolicDir global realDir + global tab + global _trace + + if sysConfDir != None: return + + tab = Tab() + _trace = Trace() reSysConfDir = re.compile(r'.*etc\/coriolis2') - print ' o Locating configuration directory:' + if not quiet: print ' o Locating configuration directory:' for path in sys.path: if reSysConfDir.match(path): sysConfDir = path - print ' - <%s>' % sysConfDir + if not quiet: print ' - <%s>' % sysConfDir break if not sysConfDir: @@ -192,7 +276,7 @@ def staticInitialization (): raise ErrorMessage( 1, [ 'Cannot locate the directoty holding the configuration files.' , 'The path is something ending by <.../etc/coriolis2>.'] ) - print ' - <%s>' % sysConfDir + if not quiet: print ' - <%s>' % sysConfDir symbolicDir = os.path.join( sysConfDir, symbolicTechno ) realDir = os.path.join( sysConfDir, realTechno ) return diff --git a/cumulus/src/Alliance.py b/cumulus/src/Alliance.py index 21c68f98..22dfe545 100644 --- a/cumulus/src/Alliance.py +++ b/cumulus/src/Alliance.py @@ -25,43 +25,125 @@ import inspect import helpers from helpers import ErrorMessage from helpers import WarningMessage +from helpers import Dots # Global display flags ShowCommand = 0x00001000 ShowLog = 0x00002000 -ForceRun = 0x00004000 +ShowDots = 0x00004000 +ForceRun = 0x00008000 commandFlags = ShowCommand | ShowLog -class RoutingLayerGauge ( object ): +class AddMode ( object ): + + Append = 1 + Prepend = 2 + Replace = 3 + + +class Gauge ( object ): + + Horizontal = 1 + Vertical = 2 + PinOnly = 4 + Default = 8 + + +class SearchPath ( object ): + + def __init__ ( self ): + self._pathes = [] + return + + def add ( self, path, mode ): + name = os.path.basename(path) + if mode == AddMode.Append: + self._pathes.append( (name,path) ) + elif mode == AddMode.Prepend: + self._pathes.insert( 0, (name,path) ) + elif mode == AddMode.Replace: + for ipath in range(len(self._pathes)): + if self._pathes[ipath][0] == name: + self._pathes[ipath] = (name, path) + return + self._pathes.append( (name,path) ) + return + + def toUnix ( self ): + if not len(self._pathes): return '' + + s = '' + for ipath in range(len(self._pathes)): + if ipath > 0: s += ':' + s += self._pathes[ipath][1] + return s + + def __str__ ( self ): + return self.toUnix() - Horizontal = 1 - Vertical = 2 - PinOnly = 4 - Default = 8 class Environment ( object ): - DefaultEnv = 0001 - CommandEnv = 0002 - Append = 0004 + DefaultEnv = 0x0001 + CommandEnv = 0x0002 + Append = 0x0004 + + BehavioralExt = 0x0001 + StructuralExt = 0x0002 + PhysicalExt = 0x0004 + ScriptExt = 0x0008 + CSourceExt = 0x0010 + DirectiveExt = 0x0020 + IocExt = 0x0040 + PatExt = 0x0080 + AllExt = 0x00ff + + extTable = { BehavioralExt : [ 'vbe' ] + , StructuralExt : [ 'vst', 'al' ] + , PhysicalExt : [ 'ap' , 'cp' ] + , ScriptExt : [ 'sh' , 'py' ] + , CSourceExt : [ 'c' ] + , IocExt : [ 'ioc' ] + , DirectiveExt : [ 'lax', 'boom', 'dly' ] + , PatExt : [ 'pat' ] + } + @staticmethod - def splitExtension ( name ): - if name.endswith('_vbe') or name.endswith('_vst'): - return name[:-4], name[-3:] + def symbolToFilename ( name, eflags ): + extensions = [] + for iflag in range(0,len(Environment.extTable)): + if eflags & pow(2,iflag): extensions += Environment.extTable[ pow(2,iflag) ] + + for extension in extensions: + if name.endswith('_'+extension): + l = len(extension) + return name[:-l-1], name[-l:] + return None, None + + @staticmethod + def isExt ( ext, eflags ): + extensions = [] + for iflag in range(0,len(Environment.extTable)): + if eflags & pow(2,iflag): extensions += Environment.extTable[ pow(2,iflag) ] + + if ext in extensions: return True + return False def __init__ ( self, flags ): self.mbkEnv = {} if flags & Environment.DefaultEnv: + self.mbkEnv[ 'ALLIANCE_TOP' ] = None + self.mbkEnv[ 'LD_LIBRARY_PATH' ] = None self.mbkEnv[ 'MBK_TARGET_LIB' ] = None self.mbkEnv[ 'MBK_WORK_LIB' ] = '.' - self.mbkEnv[ 'MBK_CATA_LIB' ] = None + self.mbkEnv[ 'MBK_CATA_LIB' ] = SearchPath() self.mbkEnv[ 'MBK_CATAL_NAME' ] = 'CATAL' self.mbkEnv[ 'MBK_OUT_LO' ] = 'vst' self.mbkEnv[ 'MBK_OUT_PH' ] = 'ap' @@ -73,9 +155,11 @@ class Environment ( object ): self.mbkEnv[ 'RDS_TECHNO_NAME' ] = None self.mbkEnv[ 'GRAAL_TECHNO_NAME' ] = None else: + self.mbkEnv[ 'ALLIANCE_TOP' ] = None + self.mbkEnv[ 'LD_LIBRARY_PATH' ] = None self.mbkEnv[ 'MBK_TARGET_LIB' ] = None self.mbkEnv[ 'MBK_WORK_LIB' ] = None - self.mbkEnv[ 'MBK_CATA_LIB' ] = None + self.mbkEnv[ 'MBK_CATA_LIB' ] = SearchPath() self.mbkEnv[ 'MBK_CATAL_NAME' ] = None self.mbkEnv[ 'MBK_OUT_LO' ] = None self.mbkEnv[ 'MBK_OUT_PH' ] = None @@ -102,6 +186,7 @@ class Environment ( object ): ]) key, value = entry + if key == 'ALLIANCE_TOP': self.mbkEnv[ key ] = value if key == 'GRAAL_TECHNO_NAME': self.mbkEnv[ key ] = value if key == 'RDS_TECHNO_NAME': self.mbkEnv[ key ] = value if key == 'CATALOG': self.mbkEnv[ key ] = value @@ -122,13 +207,12 @@ class Environment ( object ): ,str(libraryEntry) ]) libPath, mode = libraryEntry - if self.mbkEnv['MBK_CATA_LIB']: - self.mbkEnv['MBK_CATA_LIB'] = self.mbkEnv['MBK_CATA_LIB'] + ':' + libPath - else: - self.mbkEnv['MBK_CATA_LIB'] = libPath + self.mbkEnv['MBK_CATA_LIB'].add( libPath, mode ) except Exception, e: ErrorMessage.wrapPrint(e,'In %s: at index %d.' % (allianceFile,entryNo)) + + self.mbkEnv[ 'LD_LIBRARY_PATH' ] = self.mbkEnv[ 'ALLIANCE_TOP' ] + '/lib' return @@ -272,7 +356,8 @@ def staticInitialization (): sys.exit( 1 ) try: - print ' - Loading \"%s\".' % helpers.truncPath(confFile) + print ' o Running configuration hook: Alliance.staticInitialization().' + print ' - Loading \"%s\".' % helpers.truncPath(confFile) execfile( confFile, moduleGlobals ) except Exception, e: print '[ERROR] An exception occured while loading the configuration file:' @@ -290,9 +375,13 @@ def staticInitialization (): print ' <%s>' % confFile sys.exit( 1 ) + print + return + helpers.staticInitialization() staticInitialization() + report = ReportLog( 'alliance' ) @@ -305,23 +394,61 @@ class Node ( EnvironmentWrapper ): @staticmethod def getAllNodes (): return Node.__allNodes + @staticmethod + def lookup ( symbol ): + for node in Node.__allNodes: + if node.ruleName == symbol: + return node + return None + + @staticmethod + def allClean (): + for node in Node.__allNodes: node.clean() + return + + @staticmethod + def findSelfSymbols (): + for node in Node.__allNodes: node._findSelfSymbol() + return + def __init__ ( self ): EnvironmentWrapper.__init__( self, env ) - self._targetName = None - self._dependencies = [] - self._childs = [] - self._depth = Node.Unreached - self._depsReacheds = 0 + self._targetName = None + self._extension = None + self._dependencies = [] + self._childs = [] + self._depth = Node.Unreached + self._depsReacheds = 0 + self._active = False + self._frame = inspect.stack()[self.frameDepth] Node.__allNodes.append( self ) return + def _findSelfSymbol ( self ): + callerGlobals = self._frame[0].f_globals + for symbol in callerGlobals.keys(): + if self == callerGlobals[symbol]: + self.setTarget( symbol ) + return + + self.setDefaultTargetName() + #raise ErrorMessage( 1, 'Node cannot find its symbol in parent frame.' ) + return + + def setTarget ( self, name ): + baseName, extension = Environment.symbolToFilename( name, Environment.AllExt ) + if baseName: + self._targetName = baseName + self._extension = extension + else: + raise ErrorMessage( 1, 'Invalid file format of Node object <%s>' % name ) + return + @property def targetName ( self ): return self._targetName - def setTarget ( self, name ): self._targetName = name - def setDefaultTargetName ( self ): - if self._dependencies == None: + if self._dependencies == []: raise ErrorMessage( 1, 'Node.setDefaultTargetName(): node is neither used nor have dependencies.' ) self.setTarget( self.getDependency(0)._targetName+'_'+self.toolName.lower() ) print WarningMessage( 'Node.setDefaultTargetName(): Node is not affected, using: <%s>' % self.targetName ) @@ -358,10 +485,13 @@ class Node ( EnvironmentWrapper ): def allDepsReached ( self ): return self._depsReacheds >= len(self._dependencies) - def checkFile ( self, hardStop=False ): + def checkFile ( self, hardStop=False, errorMessage=[] ): if not os.path.isfile(self.fileName): error = ErrorMessage( 1, 'File <%s> of node <%s> has not been created.' % (self.fileName,self._targetName) ) + if errorMessage: + print + for line in errorMessage: print line if hardStop: raise error else: @@ -370,18 +500,51 @@ class Node ( EnvironmentWrapper ): def isUptodate ( self ): depsMTime = 0 + #print ' Target: %-30s %d' % (self.fileName, self.mtime) for dependency in self.getDependencies(): dependency.checkFile() depsMTime = max( depsMTime, dependency.mtime ) - return depsMTime < self.mtime + #print ' | Dep: %-31s %d' % (dependency.fileName, dependency.mtime) + return depsMTime <= self.mtime + + def setActive ( self ): + self._active = True + for dependency in self.getDependencies(): + dependency.setActive() + + def isActive ( self ): return self._active + + def setMbkEnv ( self ): return + + def clean ( self ): + if not isinstance(self,Source) \ + and not isinstance(self,Probe) \ + and not isinstance(self,Rule): + print 'Clean | %-30s| rm %s' % ( "<%s>"%self.ruleName, self.fileName ) + + report.open() + report.write( 'Clean <%s>: (%s)\n' % (self.ruleName, self.fileName) ) + if os.path.isfile(self.fileName): + report.write( ' rm %s\n' % self.fileName ) + os.unlink( self.fileName ) + return + report.close() @property def name ( self ): return self._targetName @property - def fileName ( self ): - raise ErrorMessage( 1, 'Node.fileName base class attribute must never be called.' ) - return + def extension ( self ): return self._extension + + @property + def fileName ( self ): return self._targetName+'.'+self._extension + + @property + def ruleName ( self ): + if self._extension: + return self._targetName+'_'+self._extension + return self._targetName + @property def mtime ( self ): @@ -391,9 +554,12 @@ class Node ( EnvironmentWrapper ): class Command ( Node ): + commandTypes = [] + def __init__ ( self ): Node.__init__( self ) self._flags = 0 + Command.commandTypes.append( type(self) ) return @staticmethod @@ -404,30 +570,66 @@ class Command ( Node ): s += command[i] return s + @staticmethod + def indent ( command, indent, width=109 ): + lines = [] + lines.append( '|' ) + + for i in range(len(command)): + lines[-1] += ' ' + command[i] + if (len(lines) == 1 and len(lines[0]) >= width-indent) \ + or len(lines[-1]) >= width: + if i < len(command)-1: + lines.append( ' '*indent + '|' + ' '*(len(command[0])+1) ) + + s = lines[0] + for line in lines[1:]: + s += '\n' + line + return s + + #s = '' + #l = '|' + #for i in range(len(command)): + # l += ' ' + command[i] + # if len(l) >= width-indent: + # if len(s): s += '\n' + # s += l + # if i < len(command)-1: + # l = ' '*indent + '|' + ' '*(len(command[0])+1) + # else: + # return s + #if len(s): s += '\n' + #s += l + #return s + + @property + def frameDepth ( self ): return 3 @property def flags ( self ): return self._flags @flags.setter def flags ( self, flags ): self._flags = flags - def _run ( self ): + def _run ( self, checkFile=True ): global commandFlags flags = commandFlags if self._flags: flags = self._flags - command = self.asCommand() + command = self.asCommand() + ruleName = "<%s>" % self.ruleName - if not self.isUptodate() or flags & ForceRun: + if self.isActive() and (not self.isUptodate() or flags & ForceRun): if flags & ShowCommand: - print "Executing: %s" % Command.asString(command) + print "Executing | %-30s%s" % (ruleName,Command.indent(command,42)) child = subprocess.Popen( command , env=self.env.toSystemEnv() , stdout=subprocess.PIPE , stderr=subprocess.PIPE ) + dots = Dots( ' '*42+'| ', 109 ) report.open() - report.write( 'Executing command:\n' ) + report.write( 'Executing command: %s \n' % ruleName ) report.write( ' %s\n' % Command.asString(command) ) while True: @@ -437,19 +639,39 @@ class Command ( Node ): if flags & ShowLog: print "%s" % (line[:-1]) sys.stdout.flush() + elif flags & ShowDots: + dots.dot() report.write( line ) + + errorLines = [] + while True: + line = child.stderr.readline() + if not line: break + + errorLines.append( line[:-1] ) + sys.stderr.flush() + report.write( line ) + report.close() + dots.reset() (pid,status) = os.waitpid(child.pid, 0) status >>= 8 if status != 0: - print ErrorMessage( 1, "%s returned status:%d." % (self.toolName,status) ) + print + for line in errorLines: print line + raise ErrorMessage( 1, "%s returned status:%d." % (self.toolName,status) ) + if checkFile: + self.checkFile( hardStop=True, errorMessage=errorLines ) else: + if self.isActive(): action = 'Up to date' + else: action = 'Inactive' if flags & ShowCommand: - print "Up to date: %s" % Command.asString(command) + print "%-10s| %-30s%s" % (action,ruleName,Command.indent(command,42)) + report.open() - report.write( 'Up to date command:\n' ) + report.write( '%s command:\n' % action ) report.write( ' %s\n' % Command.asString(command) ) report.close() status = 0 @@ -457,37 +679,74 @@ class Command ( Node ): return status +class StampCommand ( Command ): + + def __init__ ( self ): + Command.__init__( self ) + return + + def setTarget ( self, name ): + if not name.startswith('stamp_'): + raise ErrorMessage( 1, 'Target of stamp commands must start with "stamp_" (<%s>)' % name ) + + self._targetName = 'stamp.' + name[6:] + return + + @property + def frameDepth ( self ): return 4 + @property + def fileName ( self ): return self._targetName + + def _run ( self ): + Command._run( self, checkFile=False ) + if self.isActive(): + with file(self.fileName,'a'): os.utime(self.fileName,None) + return + + class Probe ( Command ): def __init__ ( self, script ): Command.__init__( self ) - self._script = script + self.addDependency( script ) return @property - def toolName ( self ): return self._script + def toolName ( self ): return self.getDependency(0).name+'_'+self.getDependency(0).extension def asCommand ( self ): - return [ os.path.join(os.getcwd(),self._script) ] + return [ os.path.join(os.getcwd(),self.getDependency(0).fileName) ] + + def _run ( self ): + Command._run( self, checkFile=False ) + return class Source ( Node ): def __init__ ( self ): Node.__init__( self ) - self._extension = None return @property - def fileName ( self ): return self.name+'.'+self._extension + def frameDepth ( self ): return 2 + + +class Rule ( Node ): + + def __init__ ( self, *dependencies ): + Node.__init__( self ) + for dependency in dependencies: + self.addDependency( dependency ) + return + + @property + def frameDepth ( self ): return 2 + @property + def mtime ( self ): return 0 def setTarget ( self, name ): - baseName, extension = Environment.splitExtension( name ) - if baseName: - Node.setTarget( self, baseName ) - self._extension = extension - else: - raise ErrorMessage( 1, 'Invalid file format of Source object <%s>' % name ) + self._targetName = name return @@ -526,16 +785,14 @@ class Boom ( Command ): def toolName ( self ): return 'Boom' @property - def fileName ( self ): return self.name+'.vbe' + def fileName ( self ): return self.name+'.'+self.extension @property def directives ( self ): return self._directives @directives.setter def directives ( self, directives ): self._directives = directives - dependency = Source( 'boom' ) - dependency.setTarget( self._directives ) - self.addDependency( dependency ) + self.addDependency( self._directives ) @property def level ( self ): return self._level @@ -559,14 +816,29 @@ class Boom ( Command ): @algorithm.setter def algorithm ( self, algorithm ): self._algorithm = algorithm + def setMbkEnv ( self ): + if self.extension != 'vbe': + raise ErrorMessage( 1, 'Boom(): Optimized file format/ext must be , not <%s>.' + % self.extension ) + if self.getDependency(0).extension != 'vbe': + raise ErrorMessage( 1, 'Boom(): Behavioral source file format/ext must be , not <%s>.' + % self.getDependency(0).extension ) + if self._directives and self._directives.extension != 'boom': + raise ErrorMessage( 1, 'Boom(): Directive file format/ext must be , not <%s>.' + % self._directives.extension ) + self.IN_LO = 'vst' + self.OUT_LO = 'vst' + return + def asCommand ( self ): command = [ 'boom' ] + #command = [ './boom.sh' ] if self._flags & Boom.Verbose: command += [ '-V' ] if self._flags & Boom.Trace: command += [ '-T' ] if self._flags & Boom.ReverseOrder: command += [ '-O' ] if self._flags & Boom.LocalOptimization: command += [ '-A' ] - if self._directives: command += [ '-P', self.getDependency(1).fileName ] + if self._directives: command += [ '-P', self.getDependency(1).name ] if self._level: command += [ '-l', str(self._level) ] if self._delayPercent: command += [ '-d', str(self._delayPercent) ] if self._iterations: command += [ '-i', str(self._iterations) ] @@ -595,16 +867,14 @@ class Boog ( Command ): def toolName ( self ): return 'Boog' @property - def fileName ( self ): return self.name+'.vst' + def fileName ( self ): return self.name+'.'+self.extension @property - def laxFile ( self ): return self._laxFile + def laxFile ( self ): return self._laxNode @laxFile.setter - def laxFile ( self, laxFile ): - self._laxFile = laxFile - dependency = Source( 'boog' ) - dependency.setTarget( self._laxFile ) - self.addDependency( dependency ) + def laxFile ( self, laxNode ): + self._laxNode = laxNode + self.addDependency( self._laxNode ) @property def optimMode( self ): return self._optimMode @@ -620,11 +890,26 @@ class Boog ( Command ): @debugFile.setter def debugFile( self, debugFile ): self._debugFile = debugFile + def setMbkEnv ( self ): + if Environment.isExt(self.extension,Environment.StructuralExt): + self.OUT_LO = self.extension + else: + raise ErrorMessage( 1, 'Boog(): Invalid structural (netlist) file format/ext <%s>.' + % self.extension ) + if self.getDependency(0).extension != 'vbe': + raise ErrorMessage( 1, 'Boog(): Behavioral source file format/ext must be , not <%s>.' + % self.getDependency(0).extension ) + if self._laxFile and self._laxFile.extension != 'lax': + raise ErrorMessage( 1, 'Boog(): Lax file format/ext must be , not <%s>.' + % self._directives.extension ) + self.IN_LO = 'vst' + return + def asCommand ( self ): command = [ 'boog' ] if self._optimMode < 5: command += [ '-m', str(self._optimMode) ] if self._xschMode < 2: command += [ '-x', str(self._xschMode) ] - if self._laxFile: command += [ '-l', self.getDependency(1).fileName ] + if self._laxFile: command += [ '-l', self.getDependency(1).name ] if self._debugFile: command += [ '-d', self._debugFile ] command += [ self.getDependency(0).name ] @@ -647,16 +932,14 @@ class Loon ( Command ): def toolName ( self ): return 'Loon' @property - def fileName ( self ): return self.name+'.vst' + def fileName ( self ): return self.name+'.'+self.OUT_LO @property def laxFile ( self ): return self._laxFile @laxFile.setter def laxFile ( self, laxFile ): self._laxFile = laxFile - dependency = Source( 'lax' ) - dependency.setTarget( self._laxFile ) - self.addDependency( dependency ) + self.addDependency( self._laxFile ) @property def optimMode( self ): return self._optimMode @@ -668,6 +951,22 @@ class Loon ( Command ): @xschMode.setter def xschMode ( self, xschMode ): self._xschMode = xschMode + def setMbkEnv ( self ): + if Environment.isExt(self.extension,Environment.StructuralExt): + self.OUT_LO = self.extension + else: + raise ErrorMessage( 1, 'Loon(): Invalid output structural (netlist) file format/ext <%s>.' + % self.extension ) + if Environment.isExt(self.getDependency(0).extension,Environment.StructuralExt): + self.IN_LO = self.getDependency(0).extension + else: + raise ErrorMessage( 1, 'Loon(): Invalid input structural (netlist) file format/ext <%s>.' + % self.getDependency(0).extension ) + if self._laxFile and self._laxFile.extension != 'lax': + raise ErrorMessage( 1, 'Loon(): Lax file format/ext must be , not <%s>.' + % self._directives.extension ) + return + def asCommand ( self ): command = [ 'loon' ] if self._optimMode < 5: command += [ '-m', str(self._optimMode) ] @@ -679,34 +978,424 @@ class Loon ( Command ): return command +class Genlib ( Command ): + + def __init__ ( self, csource ): + Command.__init__( self ) + self.addDependency( csource ) + return + + @property + def toolName ( self ): return 'Genlib' + + @property + def fileName ( self ): return self.name+'.'+self.OUT_LO + + def setMbkEnv ( self ): + if not Environment.isExt(self.extension,Environment.StructuralExt|Environment.PhysicalExt): + raise ErrorMessage( 1, 'Genlib(): Invalid output netlist or layout file format/ext <%s>.' + % self.extension ) + if not Environment.isExt(self.getDependency(0).extension,Environment.CSourceExt): + raise ErrorMessage( 1, 'Genlib(): Input file format/ext must be , not <%s>.' + % self.getDependency(0).extension ) + + if Environment.isExt(self.extension,Environment.StructuralExt): self.OUT_LO = self.extension + if Environment.isExt(self.extension,Environment.PhysicalExt): self.OUT_PH = self.extension + return + + def asCommand ( self ): + command = [ 'genlib', '-v', '-k' ] + command += [ self.getDependency(0).name ] + return command + + +class Genpat ( Command ): + + def __init__ ( self, csource ): + Command.__init__( self ) + self.addDependency( csource ) + return + + @property + def toolName ( self ): return 'Genpat' + + @property + def fileName ( self ): return self.name+'.pat' + + def setMbkEnv ( self ): + if self.extension != 'pat': + raise ErrorMessage( 1, 'Genpat(): Invalid output pattern file format/ext <%s>, must be .' + % self.extension ) + if not Environment.isExt(self.getDependency(0).extension,Environment.CSourceExt): + raise ErrorMessage( 1, 'Genpat(): Input file format/ext must be , not <%s>.' + % self.getDependency(0).extension ) + return + + def asCommand ( self ): + command = [ 'genpat', '-v' ] + command += [ self.getDependency(0).name ] + return command + + +class Asimut ( Command ): + + RootIsBehavioral = 0x00000001 + UseBdd = 0x00000002 + CompileOnly = 0x00000004 + ZeroDelay = 0x00000008 + + def __init__ ( self, model, patterns ): + Command.__init__( self ) + self._backdelay = None + + self.addDependency( patterns ) + self.addDependency( model ) + return + + @property + def toolName ( self ): return 'Asimut' + + @property + def fileName ( self ): return self.name+'.pat' + + @property + def backdelay ( self ): return self._backdelay + @backdelay.setter + def backdelay ( self, backdelay ): + self._backdelay = backdelay + self.addDependency( self._backdelay ) + + def setMbkEnv ( self ): + if self.flags & Asimut.RootIsBehavioral: + if not Environment.isExt(self.getDependency(1).extension,Environment.BehavioralExt): + raise ErrorMessage( 1, 'Asimut(): Invalid input behavioral file format/ext <%s>, must be .' + % self.getDependency(1).extension ) + else: + if Environment.isExt(self.getDependency(1).extension,Environment.StructuralExt): + self.IN_LO = self.getDependency(1).extension + else: + raise ErrorMessage( 1, 'Asimut(): Invalid input structural (netlist) file format/ext <%s>.' + % self.getDependency(1).extension ) + + if self.extension != 'pat': + raise ErrorMessage( 1, 'Asimut(): Invalid output pattern file format/ext <%s>, must be .' + % self.extension ) + if self.getDependency(0).extension != 'pat': + raise ErrorMessage( 1, 'Asimut(): Invalid input pattern file format/ext <%s>, must be .' + % self.getDependency(0).extension ) + return + + def asCommand ( self ): + command = [ 'asimut' ] + if self.flags & Asimut.RootIsBehavioral: command += [ '-b' ] + if self.flags & Asimut.UseBdd: command += [ '-bdd' ] + if self.flags & Asimut.CompileOnly: command += [ '-c' ] + if self.flags & Asimut.ZeroDelay: command += [ '-zd' ] + if self._backdelay: command += [ '-backdelay', self.getDependency(1).name ] + + command += [ self.getDependency(1).name ] + command += [ self.getDependency(0).name ] + command += [ self.name ] + return command + + +class Ocp ( Command ): + + Ring = 0x00000001 + Gnuplot = 0x00000002 + + def __init__ ( self, netlist ): + Command.__init__( self ) + self._partial = None + self._ioc = None + self._margin = -1 + + self.addDependency( netlist ) + return + + @property + def toolName ( self ): return 'Ocp' + + @property + def fileName ( self ): return self.name+'.'+self._extension + + @property + def partial ( self ): return self._partial + @partial.setter + def partial ( self, partial ): + self._partial = partial + self.addDependency( self._partial ) + + @property + def ioc ( self ): return self._ioc + @ioc.setter + def ioc ( self, ioc ): + self._ioc = ioc + self.addDependency( self._ioc ) + + @property + def margin ( self ): return self._margin + @margin.setter + def margin ( self, margin ): + if margin < 0 or margin > 100: + raise ErrorMessage( 1, 'Ocp: Margin must be between 0 and 100% (%d)' % margin ) + self._margin = margin + + def setMbkEnv ( self ): + if Environment.isExt(self.extension,Environment.PhysicalExt): + self.OUT_PH = self.extension + else: + raise ErrorMessage( 1, 'Ocp(): Invalid output layout file format/ext <%s>.' + % self.extension ) + + if Environment.isExt(self.getDependency(0).extension,Environment.StructuralExt): + self.IN_LO = self.getDependency(0).extension + else: + raise ErrorMessage( 1, 'Ocp(): Input file format/ext must be structural, not <%s>.' + % self.getDependency(0).extension ) + + if Environment.isExt(self._partial.extension,Environment.PhysicalExt): + self.IN_PH = self._partial.extension + else: + raise ErrorMessage( 1, 'Ocp(): Invalid input layout file format/ext <%s> for partial placement.' + % self._partial.extension ) + if self._ioc.extension != 'ioc': + raise ErrorMessage( 1, 'Ocp(): Invalid IOC file format/ext <%s>, must be .' + % self._ioc.extension ) + + return + + def asCommand ( self ): + command = [ 'ocp', '-v' ] + if self.flags & Ocp.Ring: command += [ '-ring' ] + if self.flags & Ocp.Gnuplot: command += [ '-gnuplot' ] + if self._margin > 0: command += [ '-margin' , str(self._margin) ] + if self._partial: command += [ '-partial', self._partial.name ] + if self._ioc: command += [ '-ioc' , self._ioc.name ] + + command += [ self.getDependency(0).name ] + command += [ self.name ] + return command + + +class Nero ( Command ): + + ForceGlobalOff = 0x00000001 + ForceGlobalOn = 0x00000002 + Use2Layers = 0x00000004 + Use3Layers = 0x00000008 + Use4Layers = 0x00000010 + Use5Layers = 0x00000020 + Use6Layers = 0x00000040 + + def __init__ ( self, netlist ): + Command.__init__( self ) + self._placement = None + self.addDependency( netlist ) + return + + @property + def toolName ( self ): return 'Nero' + + @property + def fileName ( self ): return self.name+'.'+self._extension + + @property + def placement ( self ): return self._placement + @placement.setter + def placement ( self, placement ): + self._placement = placement + self.addDependency( self._placement ) + + def setMbkEnv ( self ): + if Environment.isExt(self.extension,Environment.PhysicalExt): + self.OUT_PH = self.extension + else: + raise ErrorMessage( 1, 'Nero(): Invalid output layout file format/ext <%s>.' + % self.extension ) + + if Environment.isExt(self.getDependency(0).extension,Environment.StructuralExt): + self.IN_LO = self.getDependency(0).extension + else: + raise ErrorMessage( 1, 'Nero(): Input file format/ext must be structural, not <%s>.' + % self.getDependency(0).extension ) + + if Environment.isExt(self._placement.extension,Environment.PhysicalExt): + self.IN_PH = self._placement.extension + else: + raise ErrorMessage( 1, 'Nero(): Invalid input layout file format/ext <%s> for placement placement.' + % self._placement.extension ) + + return + + def asCommand ( self ): + command = [ 'nero', '-V' ] + if self.flags & Nero.ForceGlobalOff: command += [ '-L' ] + if self.flags & Nero.ForceGlobalOn: command += [ '-G' ] + if self.flags & Nero.Use2Layers: command += [ '-2' ] + if self.flags & Nero.Use3Layers: command += [ '-3' ] + if self.flags & Nero.Use4Layers: command += [ '-4' ] + if self.flags & Nero.Use5Layers: command += [ '-5' ] + if self.flags & Nero.Use6Layers: command += [ '-6' ] + if self._placement: command += [ '-p', self._placement.name ] + + command += [ self.getDependency(0).name ] + command += [ self.name ] + return command + + +class Cougar ( Command ): + + TransistorFlatten = 0x00000001 + CatalogFlatten = 0x00000002 + CapaToGround = 0x00000004 + RCToGround = 0x00000008 + + def __init__ ( self, layout ): + Command.__init__( self ) + self.addDependency( layout ) + return + + @property + def toolName ( self ): return 'Cougar' + + @property + def fileName ( self ): return self.name+'.'+self._extension + + def setMbkEnv ( self ): + if Environment.isExt(self.extension,Environment.StructuralExt): + self.OUT_LO = self.extension + else: + raise ErrorMessage( 1, 'Cougar(): Invalid output netlist file format/ext <%s>.' + % self.extension ) + + if Environment.isExt(self.getDependency(0).extension,Environment.PhysicalExt): + self.IN_PH = self.getDependency(0).extension + else: + raise ErrorMessage( 1, 'Cougar(): Input file format/ext must be physical, not <%s>.' + % self.getDependency(0).extension ) + return + + def asCommand ( self ): + command = [ 'cougar', '-v' ] + if self.flags & Cougar.TransistorFlatten: command += [ '-t' ] + if self.flags & Cougar.CatalogFlatten: command += [ '-f' ] + if self.flags & Cougar.CapaToGround: command += [ '-ac' ] + if self.flags & Cougar.RCToGround: command += [ '-ar' ] + + command += [ self.getDependency(0).name ] + command += [ self.name ] + return command + + +class Lvx ( StampCommand ): + + MergeSupplies = 0x00000001 + OrderConnectors = 0x00000002 + CheckUnassigneds = 0x00000004 + CatalogFlatten = 0x00000008 + + def __init__ ( self, netlist1, netlist2 ): + StampCommand.__init__( self ) + self.addDependency( netlist1 ) + self.addDependency( netlist2 ) + return + + @property + def toolName ( self ): return 'Lvx' + + def setMbkEnv ( self ): + if not Environment.isExt(self.getDependency(0).extension,Environment.StructuralExt): + raise ErrorMessage( 1, 'Lvx(): Input file 1 format/ext must be structural (netlist), not <%s>.' + % self.getDependency(0).extension ) + if not Environment.isExt(self.getDependency(1).extension,Environment.StructuralExt): + raise ErrorMessage( 1, 'Lvx(): Input file 2 format/ext must be structural (netlist), not <%s>.' + % self.getDependency(1).extension ) + return + + def asCommand ( self ): + command = [ 'lvx' ] + command += [ self.getDependency(0).extension ] + command += [ self.getDependency(1).extension ] + command += [ self.getDependency(0).name ] + command += [ self.getDependency(1).name ] + + if self.flags & Lvx.MergeSupplies: command += [ '-a' ] + if self.flags & Lvx.CheckUnassigneds: command += [ '-u' ] + if self.flags & Lvx.CatalogFlatten: command += [ '-f' ] + return command + + +class Druc ( StampCommand ): + + def __init__ ( self, layout ): + StampCommand.__init__( self ) + self.addDependency( layout ) + return + + @property + def toolName ( self ): return 'Druc' + + def setMbkEnv ( self ): + if not Environment.isExt(self.getDependency(0).extension,Environment.PhysicalExt): + raise ErrorMessage( 1, 'Druc(): Input file format/ext must be physical (layout), not <%s>.' + % self.getDependency(0).extension ) + return + + def asCommand ( self ): + command = [ 'druc', '-v' ] + command += [ self.getDependency(0).name ] + return command + + class Tools ( object ): def __init__ ( self ): self._sourceNodes = [] self._toolNodes = [] + self._ruleNodes = [] self._staticOrder = [] return def _findNodes ( self ): - # Three level of stack from the user's script python module. - callerGlobals = inspect.stack()[2][0].f_globals - for symbol in callerGlobals.keys(): - node = callerGlobals[symbol] - if isinstance(node,Boom) \ - or isinstance(node,Boog) \ - or isinstance(node,Loon) \ - or isinstance(node,Probe): - node.setTarget( symbol ) - self._toolNodes.append( node ) - elif isinstance(node,Source): - node.setTarget( symbol ) - self._sourceNodes.append( node ) + Node.findSelfSymbols() for node in Node.getAllNodes(): - if node.targetName: continue - node.setDefaultTargetName() - if not isinstance(node,Source): - self._toolNodes.append( node ) + isCommand = False + for commandType in Command.commandTypes: + if isinstance(node,commandType): + self._toolNodes.append( node ) + isCommand = True + break + + if not isCommand: + if isinstance(node,Source): self._sourceNodes.append( node ) + if isinstance(node,Rule ): self._ruleNodes.append( node ) + + + # Three level of stack from the user's script python module. + #callerGlobals = inspect.stack()[2][0].f_globals + #for symbol in callerGlobals.keys(): + # isCommand = False + # node = callerGlobals[symbol] + # for commandType in Command.commandTypes: + # if isinstance(node,commandType): + # node.setTarget( symbol ) + # self._toolNodes.append( node ) + # isCommand = True + # break + # + # if not isCommand \ + # and ( isinstance(node,Source) + # or isinstance(node,Rule )): + # node.setTarget( symbol ) + # self._sourceNodes.append( node ) + # + #for node in Node.getAllNodes(): + # if node.targetName: continue + # node.setDefaultTargetName() + # if not isinstance(node,Source): + # self._toolNodes.append( node ) return def _sortNodes ( self ): @@ -727,19 +1416,48 @@ class Tools ( object ): for child in node.getChilds(): if child.getDepth() < 0: nodeStack.append(child) child.incDepsReached( node.getDepth()+1 ) + break if not reacheds and nodeStack: raise ErrorMessage( 1, 'Loop in dependency graph.' ) return + def _setMbkEnv ( self ): + for node in self._staticOrder: + node.setMbkEnv() + return + @staticmethod - def run (): - tool = Tools() + def run ( args=[] ): + tool = Tools() tool._findNodes() tool._sortNodes() + tool._setMbkEnv() + + doClean = False + for arg in args: + if arg == 'clean': doClean = True + else: + node = Node.lookup( arg ) + if node: node.setActive() + + if commandFlags & ShowCommand and not (commandFlags & ShowLog): + print "==========+===============================+============================================" + print " ACTION | TARGET/RULE | COMMAND" + print "==========+===============================+============================================" + + if doClean: + Node.allClean() + if commandFlags & ShowCommand and not (commandFlags & ShowLog): + print "----------+-------------------------------+--------------------------------------------" for node in tool._staticOrder: #for dep in node.getDependencies(): # print dep.name # print dep.fileName - if not isinstance(node,Source): node._run() + if not isinstance(node,Source) and not isinstance(node,Rule): + if node._run(): break + + if commandFlags & ShowCommand and not (commandFlags & ShowLog): + print "----------+-------------------------------+--------------------------------------------" + return diff --git a/cumulus/src/CMakeLists.txt b/cumulus/src/CMakeLists.txt index 045a3d48..474068ec 100644 --- a/cumulus/src/CMakeLists.txt +++ b/cumulus/src/CMakeLists.txt @@ -5,8 +5,12 @@ ${CMAKE_CURRENT_SOURCE_DIR}/Alliance.py ) set ( pyPlugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins/__init__.py - ${CMAKE_CURRENT_SOURCE_DIR}/plugins/ClockTree.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/ChipPlugin.py + ${CMAKE_CURRENT_SOURCE_DIR}/plugins/ClockTreePlugin.py + ) + set ( pyPluginCT ${CMAKE_CURRENT_SOURCE_DIR}/plugins/clocktree/__init__.py + ${CMAKE_CURRENT_SOURCE_DIR}/plugins/clocktree/RSMT.py + ${CMAKE_CURRENT_SOURCE_DIR}/plugins/clocktree/ClockTree.py ) set ( pyPluginChip ${CMAKE_CURRENT_SOURCE_DIR}/plugins/chip/__init__.py ${CMAKE_CURRENT_SOURCE_DIR}/plugins/chip/BlockPower.py @@ -17,4 +21,5 @@ install ( FILES ${pySources} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus ) install ( FILES ${pyPlugins} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins ) + install ( FILES ${pyPluginCT} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/clocktree ) install ( FILES ${pyPluginChip} DESTINATION ${PYTHON_SITE_PACKAGES}/cumulus/plugins/chip ) diff --git a/cumulus/src/plugins/ChipPlugin.py b/cumulus/src/plugins/ChipPlugin.py index 3214b1f5..cecd8bfb 100644 --- a/cumulus/src/plugins/ChipPlugin.py +++ b/cumulus/src/plugins/ChipPlugin.py @@ -13,6 +13,7 @@ # | Python : "./plugins/ChipPlugin.py" | # +-----------------------------------------------------------------+ + try: import sys import traceback @@ -41,6 +42,7 @@ try: import Viewer import CRL from CRL import RoutingLayerGauge + import helpers from helpers import ErrorMessage from helpers import WarningMessage import Nimbus @@ -50,6 +52,7 @@ try: import Kite import Unicorn import plugins + import clocktree.ClockTree import chip.Configuration import chip.BlockPower import chip.BlockCorona @@ -71,10 +74,10 @@ except Exception, e: # Plugin working part. -class PlaceCore ( chip.Configuration.Wrapper ): +class PlaceCore ( chip.Configuration.ChipConfWrapper ): - def __init__ ( self, confWrapper ): - chip.Configuration.Wrapper.__init__( self, confWrapper.conf ) + def __init__ ( self, conf ): + chip.Configuration.ChipConfWrapper.__init__( self, conf.gaugeConf, conf.chipConf ) self.validated = False return @@ -113,12 +116,33 @@ class PlaceCore ( chip.Configuration.Wrapper ): def doPlacement ( self ): if not self.validated: return - checkUnplaced = plugins.CheckUnplaced( self.cores[0].getMasterCell(), plugins.NoFlags ) + coreCell = self.cores[0].getMasterCell() + + checkUnplaced = plugins.CheckUnplaced( coreCell, plugins.NoFlags ) if not checkUnplaced.check(): return - - mauka = Mauka.MaukaEngine.create( self.cores[0].getMasterCell() ) - mauka.run() - mauka.destroy() + + ckCore = None + for plug in self.cko.getPlugs(): + if plug.getInstance() == self.cores[0]: + ckCore = plug.getMasterNet() + if not ckCore: + print WarningMessage( 'Core <%s> is not connected to chip clock.' + % self.cores[0].getName() ) + + if self.useClockTree and ckCore: + ht = clocktree.ClockTree.HTree.create( self, coreCell, ckCore, coreCell.getAbutmentBox() ) + ht.addCloned( self.cell ) + mauka = Mauka.MaukaEngine.create( coreCell ) + mauka.run() + mauka.destroy() + ht.connectLeaf() + ht.prune() + ht.route() + ht.save( self.cell ) + else: + mauka = Mauka.MaukaEngine.create( coreCell ) + mauka.run() + mauka.destroy() return @@ -139,6 +163,9 @@ def unicornHook ( **kw ): def ScriptMain ( **kw ): try: + helpers.staticInitialization( quiet=True ) + #helpers.setTraceLevel( 550 ) + cell, editor = plugins.kwParseMain( **kw ) conf = chip.Configuration.loadConfiguration( cell ) @@ -151,10 +178,17 @@ def ScriptMain ( **kw ): placeCore = PlaceCore( conf ) placeCore.validate() placeCore.doFloorplan() + editor.fit() + placeCore.doPlacement() - corePower = chip.BlockPower.Block( Path(conf.cores[0]), conf.vddi, conf.vssi ) - corePower.queryPower() + editor.fit() + + corePower = chip.BlockPower.Block( conf ) + corePower.connectPower() + corePower.connectClock() corePower.doLayout() + editor.fit() + coreCorona = chip.BlockCorona.Corona( corePower ) coreCorona.connectPads( padsCorona ) coreCorona.connectBlock() @@ -167,5 +201,8 @@ def ScriptMain ( **kw ): except Exception, e: print '\n\n', e; errorCode = 1 traceback.print_tb(sys.exc_info()[2]) + + sys.stdout.flush() + sys.stderr.flush() return 0 diff --git a/cumulus/src/plugins/ClockTreePlugin.py b/cumulus/src/plugins/ClockTreePlugin.py new file mode 100755 index 00000000..c79e266c --- /dev/null +++ b/cumulus/src/plugins/ClockTreePlugin.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# +# This file is part of the Coriolis Software. +# Copyright (c) UPMC 2014-2014, 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@asim.lip6.fr | +# | =============================================================== | +# | Python : "./plugins/ClockTreePlugin.py" | +# +-----------------------------------------------------------------+ + +try: + import sys + import traceback + import os.path + import math + import Cfg + import Hurricane + import Viewer + import CRL + from CRL import RoutingLayerGauge + import helpers + from helpers import trace + from helpers import ErrorMessage + import Mauka + import Unicorn + import plugins + import clocktree.ClockTree + import chip.Configuration +except ImportError, e: + module = str(e).split()[-1] + + print '[ERROR] The <%s> python module or symbol cannot be loaded.' % module + print ' Please check the integrity of the package.' + sys.exit(1) +except Exception, e: + print '[ERROR] A strange exception occurred while loading the basic Coriolis/Python' + print ' modules. Something may be wrong at Python/C API level.\n' + print ' %s' % e + sys.exit(2) + + +# -------------------------------------------------------------------- +# Plugin hook functions, unicornHook:menus, ScritMain:call + +def unicornHook ( **kw ): + plugins.kwUnicornHook( 'plugins.clockTree' + , 'ClockTree' + , 'Build a buffered H-Tree for the clock' + , sys.modules[__name__].__file__ + , **kw + ) + return + + +def ScriptMain ( **kw ): + try: + errorCode = 0 + + print ' o Cleaning up any previous run.' + for fileName in os.listdir('.'): + if fileName.endswith('.ap'): + print ' - <%s>' % fileName + os.unlink(fileName) + + cell = None + if kw.has_key('cell') and kw['cell']: + cell = kw['cell'] + + editor = None + if kw.has_key('editor') and kw['editor']: + editor = kw['editor'] + print ' o Editor detected, running in graphic mode.' + if cell == None: cell = editor.getCell() + + if cell == None: + raise ErrorMessage( 3, 'ClockTree: No cell loaded yet.' ) + + framework = CRL.AllianceFramework.get() + cellGauge = framework.getCellGauge() + + if cell.getAbutmentBox().isEmpty(): + spaceMargin = Cfg.getParamPercentage('nimbus.spaceMargin').asPercentage() / 100.0 + 0.02 + aspectRatio = Cfg.getParamPercentage('nimbus.aspectRatio').asPercentage() / 100.0 + clocktree.ClockTree.computeAbutmentBox( cell, spaceMargin, aspectRatio, cellGauge ) + if editor: editor.fit() + + ht = clocktree.ClockTree.HTree.create( chip.Configuration.GaugeConfWrapper(chip.Configuration.GaugeConf()) + , cell, None, cell.getAbutmentBox() ) + if editor: editor.refresh() + mauka = Mauka.MaukaEngine.create( cell ) + mauka.run() + mauka.destroy() + ht.connectLeaf() + ht.prune() + ht.route() + ht.save( cell ) + + #showNet( cell, 'ck_htree_bl_bl_bl' ) + #showNet( cell, 'ck_htree_bl_bl_br' ) + #showNet( cell, 'ck_htree_bl_bl_tl' ) + #showNet( cell, 'ck_htree_bl_bl_tr' ) + + except ErrorMessage, e: + print e; errorCode = e.code + except Exception, e: + print '\n\n', e; errorCode = 1 + traceback.print_tb(sys.exc_info()[2]) + + return 0 diff --git a/cumulus/src/plugins/__init__.py b/cumulus/src/plugins/__init__.py index b76d48e2..07d8e63f 100644 --- a/cumulus/src/plugins/__init__.py +++ b/cumulus/src/plugins/__init__.py @@ -111,6 +111,8 @@ class StackedVia ( object ): self._vias = [] return + def getNet ( self ): return self._net + def mergeDepth ( self, depth ): if self._hasLayout: print WarningMessage( 'StackedVia.mergeDepth(): Cannot be called *after* StackVia.doLayout()' ) diff --git a/cumulus/src/plugins/chip/BlockCorona.py b/cumulus/src/plugins/chip/BlockCorona.py index f4da8421..3c64ef4b 100644 --- a/cumulus/src/plugins/chip/BlockCorona.py +++ b/cumulus/src/plugins/chip/BlockCorona.py @@ -30,6 +30,7 @@ from Hurricane import Horizontal from Hurricane import Vertical import CRL from CRL import RoutingLayerGauge +from helpers import trace from helpers import ErrorMessage from helpers import WarningMessage from plugins import StackedVia @@ -64,7 +65,7 @@ class HorizontalRail ( Rail ): , '(corona:%s)' % str(self.side.innerBb) ] ) #print ' HorizontalRail.connect() net:%s contact:%s' % (self.net.getName(),str(contact)) - if self.net != contact.getNet(): return False + #if self.net != contact.getNet(): return False if self.vias.has_key(contact.getX()): return False keys = self.vias.keys() @@ -87,26 +88,14 @@ class HorizontalRail ( Rail ): , self.side.getLayerDepth(self.side.getHLayer()) , contact.getX() , self.axis - , contact.getWidth() - , self.side.hRailWidth + , contact.getWidth() - DbU.fromLambda(1.0) + , self.side.hRailWidth - DbU.fromLambda(1.0) ) , contact ] #print ' ADD contact @ [%d %d]' % (DbU.toLambda(contact.getX()), DbU.toLambda(self.axis)) self.vias[ contact.getX() ][1].mergeDepth( self.side.getLayerDepth(contact.getLayer()) ) return True - def doLayout ( self ): - for via in self.vias.values(): - via[1].doLayout() - - Horizontal.create( self.side.corner0(self.order) - , self.side.corner1(self.order) - , self.side.getHLayer() - , self.axis - , self.side.hRailWidth - ) - return - def doLayout ( self ): #print 'HorizontalRail[%s] @%d' % (self.order,DbU.toLambda(self.axis)) @@ -114,6 +103,8 @@ class HorizontalRail ( Rail ): , self.side.corner1(self.order) ] for via in self.vias.values(): + if via[1].getNet() != via[2].getNet(): continue + via[1].doLayout() #print ' Connect:', via[2], via[1].getVia( via[2].getLayer() ) Vertical.create( via[1].getVia( via[2].getLayer() ) @@ -149,7 +140,8 @@ class VerticalRail ( Rail ): , self.side.corner1(self.order) ] for via in self.vias.values(): - #print ' Connect: ', via[2] + if via[1].getNet() != via[2].getNet(): continue + via[1].doLayout() Horizontal.create( via[1].getVia( via[2].getLayer() ) , via[2] @@ -178,30 +170,36 @@ class VerticalRail ( Rail ): , 'power pad is likely to be to far off north or south.' , '(corona:%s)' % str(self.side.innerBb) ] ) - if self.net != contact.getNet(): return False + #if self.net != contact.getNet(): return False if self.vias.has_key(contact.getY()): return False + + trace( 550, ',+', '\tVerticalRail.connect() [%s] @%d\n' % (self.order,DbU.toLambda(self.axis)) ) + trace( 550, contact ) keys = self.vias.keys() keys.sort() insertIndex = bisect.bisect_left( keys, contact.getY() ) - #print 'keys:', keys + trace( 550, ',+', '\tkeys:' ) + for key in keys: + trace( 550, ' %d' % DbU.toLambda(key) ) + trace( 550, '\n' ) if len(keys) > 0: if insertIndex < len(keys): insertPosition = keys[ insertIndex ] - #print 'insertIndex:%d' % insertIndex - #print 'Check NEXT contactBb:%s via:%s' \ - # % ( str(contactBb) - # , str(self.vias[insertPosition][2].getBoundingBox()) ) + trace( 550, '\tinsertIndex:%d' % insertIndex ) + trace( 550, '\tCheck NEXT contactBb:%s via:%s\n' \ + % ( contactBb + , self.vias[insertPosition][2].getBoundingBox()) ) if contactBb.getYMax() >= self.vias[insertPosition][2].getBoundingBox().getYMin(): - # print 'Reject %s intersect NEXT' % str(contact) + trace( 550, ',--', '\tReject %s intersect NEXT\n' % contact ) return False if insertIndex > 0: - #print 'check PREVIOUS contactBb:%s via:%s' \ - # % ( str(contactBb) - # , str(self.vias[keys[insertIndex-1]][2].getBoundingBox()) ) + trace( 550, '\tcheck PREVIOUS contactBb:%s via:%s\n' \ + % ( contactBb + , self.vias[keys[insertIndex-1]][2].getBoundingBox()) ) if self.vias[keys[insertIndex-1]][2].getBoundingBox().getYMax() >= contactBb.getYMin(): - # print 'Reject %s intersect PREVIOUS' % str(contact) + trace( 550, ',--', '\tReject %s intersect PREVIOUS\n' % contact ) return False self.vias[ contact.getY() ] = [ contact.getY() @@ -209,11 +207,11 @@ class VerticalRail ( Rail ): , self.side.getLayerDepth(self.side.getVLayer()) , self.axis , contact.getY() - , self.side.vRailWidth - , contact.getHeight() + , self.side.vRailWidth - DbU.fromLambda(1.0) + , contact.getHeight() - DbU.fromLambda(1.0) ) , contact ] - #print 'ADD %s' % str(contact) + trace(550, ',--' '\tADD %s\n' % contact ) self.vias[ contact.getY() ][1].mergeDepth( self.side.getLayerDepth(contact.getLayer()) ) return True @@ -265,12 +263,18 @@ class Side ( object ): return def connectPads ( self, padSide ): - halfRails = len(self._rails)/2 for terminal in padSide._powerContacts: - #print 'Connect pad terminal ', terminal + #print ' Connect to [-%i] @%d' % (0, DbU.toLambda(self.getOuterRail(0).axis)) + self.getOuterRail( 0 ).connect( terminal ) + + halfRails = (len(self._rails)-1)/2 + trace( 550, 'halfRails:%i' % halfRails ) + for terminal in padSide._powerContacts: + trace( 550, ',+', '\tConnect pad terminal %s\n' % terminal ) for i in range(halfRails): - #print ' Connect to [-%i] @%d' % (i, DbU.toLambda(self.getOuterRail(i).axis)) - self.getOuterRail(i).connect( terminal ) + trace( 550, '\tConnect to [-%i] @%d\n' % (i+1, DbU.toLambda(self.getOuterRail(i+1).axis)) ) + self.getOuterRail(i+1).connect( terminal ) + trace( 550, '-' ) return def doLayout ( self ): @@ -364,60 +368,45 @@ class Corona ( object ): if not isinstance(block,chip.BlockPower.Block): raise ErrorMessage( 1, 'Attempt to create a Corona on a non-Block object.' ) - self._framework = CRL.AllianceFramework.get() - self._routingGauge = self._framework.getRoutingGauge() - self._block = block - self._innerBb = self._block.bb + self._block = block + self._innerBb = self._block.bb self._block.path.getTransformation().applyOn( self._innerBb ) - self._railsNb = 4 + self._railsNb = 5 self._hRailWidth = DbU.fromLambda( 12.0 ) self._vRailWidth = DbU.fromLambda( 12.0 ) self._hRailSpace = DbU.fromLambda( 6.0 ) self._vRailSpace = DbU.fromLambda( 6.0 ) - self._topLayerDepth = 0 - self._horizontalDepth = 0 - self._verticalDepth = 0 - self._southSide = SouthSide( self ) - self._northSide = NorthSide( self ) - self._westSide = WestSide ( self ) - self._eastSide = EastSide ( self ) + self._southSide = SouthSide( self ) + self._northSide = NorthSide( self ) + self._westSide = WestSide ( self ) + self._eastSide = EastSide ( self ) - self._guessHvLayers() return - def _guessHvLayers ( self ): - topLayer = Cfg.getParamString('katabatic.topRoutingLayer').asString() - self._topLayerDepth = 0 - for layerGauge in self._routingGauge.getLayerGauges(): - if layerGauge.getLayer().getName() == topLayer: - self._topLayerDepth = layerGauge.getDepth() - break - if not self._topLayerDepth: - print WarningMessage( 'Gauge top layer not defined, using top of gauge (%d).' \ - % self._routingGauge.getDepth() ) - self._topLayerDepth = self._routingGauge.getDepth() - - for depth in range(0,self._topLayerDepth+1): - if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal: - self._horizontalDepth = depth - if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Vertical: - self._verticalDepth = depth - return + @property + def routingGauge ( self ): return self._block.routingGauge + @property + def topLayerDepth ( self ): return self._block.topLayerDepth + @property + def horizontalDepth ( self ): return self._block.horizontalDepth + @property + def verticalDepth ( self ): return self._block.verticalDepth def getLayerDepth ( self, metal ): - return self._routingGauge.getLayerDepth( metal ) + return self.routingGauge.getLayerDepth( metal ) def getRailNet ( self, i ): + if i == self._railsNb-1: return self._block.cko if i % 2: return self._block.vssi return self._block.vddi def getHLayer ( self ): - return self._routingGauge.getLayerGauge( self._horizontalDepth ).getLayer() + return self.routingGauge.getLayerGauge( self.horizontalDepth ).getLayer() def getVLayer ( self ): - return self._routingGauge.getLayerGauge( self._verticalDepth ).getLayer() + return self.routingGauge.getLayerGauge( self.verticalDepth ).getLayer() def connectBlock ( self ): for plane in self._block.planes.values(): @@ -436,15 +425,15 @@ class Corona ( object ): return def doLayout ( self ): - self._corners = { chip.SouthWest : [] + self._corners = { chip.SouthWest : [] , chip.SouthEast : [] - , chip.NorthWest : [] - , chip.NorthEast : [] + , chip.NorthWest : [] + , chip.NorthEast : [] } - contactDepth = self._horizontalDepth - if self._horizontalDepth > self._verticalDepth: - contactDepth = self._verticalDepth + contactDepth = self.horizontalDepth + if self.horizontalDepth > self.verticalDepth: + contactDepth = self.verticalDepth for i in range(self._railsNb): xBL = self._westSide .getRail(i).axis @@ -453,30 +442,31 @@ class Corona ( object ): yTR = self._northSide.getRail(i).axis net = self.getRailNet( i ) + self.routingGauge.getContactLayer(contactDepth) self._corners[chip.SouthWest].append( Contact.create( net - , self._routingGauge.getContactLayer(contactDepth) + , self.routingGauge.getContactLayer(contactDepth) , xBL, yBL , self._hRailWidth , self._vRailWidth ) ) self._corners[chip.NorthWest].append( Contact.create( net - , self._routingGauge.getContactLayer(contactDepth) + , self.routingGauge.getContactLayer(contactDepth) , xBL, yTR , self._hRailWidth , self._vRailWidth ) ) self._corners[chip.SouthEast].append( Contact.create( net - , self._routingGauge.getContactLayer(contactDepth) + , self.routingGauge.getContactLayer(contactDepth) , xTR, yBL , self._hRailWidth , self._vRailWidth ) ) self._corners[chip.NorthEast].append( Contact.create( net - , self._routingGauge.getContactLayer(contactDepth) + , self.routingGauge.getContactLayer(contactDepth) , xTR, yTR , self._hRailWidth , self._vRailWidth diff --git a/cumulus/src/plugins/chip/BlockPower.py b/cumulus/src/plugins/chip/BlockPower.py index 58cb3f83..ec1a246d 100644 --- a/cumulus/src/plugins/chip/BlockPower.py +++ b/cumulus/src/plugins/chip/BlockPower.py @@ -14,6 +14,7 @@ # +-----------------------------------------------------------------+ +import sys from Hurricane import DbU from Hurricane import Point from Hurricane import Transformation @@ -28,11 +29,17 @@ from Hurricane import Horizontal from Hurricane import Vertical from Hurricane import Query import CRL +import helpers +from helpers import trace from helpers import ErrorMessage from helpers import WarningMessage import chip.Configuration +helpers.staticInitialization( quiet=True ) +helpers.trace.level = 550 + + class Side ( object ): def __init__ ( self, block, side, net, metal ): @@ -90,13 +97,16 @@ class Side ( object ): height = 0 y = self.block.bb.getYMax() + minWidth = DbU.fromLambda( 6.0 ) for terminal in self.terminals: if self.side == chip.West or self.side == chip.East: center = Point( x, terminal[0].getCenter() ) height = terminal[0].getSize() + if height < minWidth: height = minWidth elif self.side == chip.North or self.side == chip.South: center = Point( terminal[0].getCenter(), y ) width = terminal[0].getSize() + if width < minWidth: width = minWidth self.block.path.getTransformation().applyOn( center ) @@ -172,14 +182,13 @@ class GoCb ( object ): return -class Block ( object ): +class Block ( chip.Configuration.ChipConfWrapper ): - def __init__ ( self, path, vddi, vssi ): - self.path = path - self.cell = self.path.getTailInstance().getMasterCell() - self.bb = self.cell.getAbutmentBox() - self.vddi = vddi - self.vssi = vssi + def __init__ ( self, conf ): + chip.Configuration.ChipConfWrapper.__init__( self, conf.gaugeConf, conf.chipConf ) + self.path = Path( self.cores[0] ) + self.block = self.path.getTailInstance().getMasterCell() + self.bb = self.block.getAbutmentBox() self.planes = { } self.activePlane = None @@ -190,7 +199,7 @@ class Block ( object ): return - def queryPower ( self ): + def connectPower ( self ): if not self.vddi or not self.vssi: print ErrorMessage( 1, 'Cannot build block power terminals as vddi and/or vss are not known.' ) return @@ -198,7 +207,7 @@ class Block ( object ): goCb = GoCb( self ) query = Query() query.setGoCallback( goCb ) - query.setCell( self.cell ) + query.setCell( self.block ) query.setArea( self.bb ) query.setFilter( Query.DoComponents|Query.DoTerminalCells ) @@ -210,6 +219,59 @@ class Block ( object ): self.activePlane = None return + + def connectClock ( self ): + if not self.cko: + print ErrorMessage( 1, 'Cannot build clock terminal as ck is not known.' ) + return + + blockCk = None + for plug in self.path.getTailInstance().getPlugs(): + if plug.getNet() == self.cko: + blockCk = plug.getMasterNet() + + if not blockCk: + print ErrorMessage( 1, 'Block <%s> has no net connected to the clock <%s>.' + % (self.path.getTailInstance().getName(),self.ck.getName()) ) + return + + plugs = [] + for plug in blockCk.getPlugs(): + if plug.getInstance().getName() == 'ck_htree': + plugs.append( plug ) + + if len(plugs) != 1: + message = 'Block <%s> has not exactly one H-Tree connecteds to the clock <%s>:' \ + % (self.path.getTailInstance().getName(),blockCk.getName()) + for plug in plugs: + message += '\n - %s' % plug + print ErrorMessage( 1, message ) + return + + UpdateSession.open() + bufferRp = self.rpAccessByOccurrence( Occurrence(plugs[0], self.path), self.cko ) + blockAb = self.block.getAbutmentBox() + self.path.getTransformation().applyOn( blockAb ) + layerGauge = self.routingGauge.getLayerGauge(self.verticalDepth) + + contact = Contact.create( self.cko + , self.routingGauge.getRoutingLayer(self.verticalDepth) + , bufferRp.getX() + , blockAb.getYMax() + , layerGauge.getViaWidth() + , layerGauge.getViaWidth() + ) + segment = self.createVertical( bufferRp, contact, bufferRp.getX() ) + + self.activePlane = self.planes[ layerGauge.getLayer().getName() ] + bb = segment.getBoundingBox( self.activePlane.metal.getBasicLayer() ) + self.path.getTransformation().getInvert().applyOn( bb ) + self.activePlane.addTerminal( self.cko, Plane.Vertical, bb ) + + UpdateSession.close() + + return + def doLayout ( self ): UpdateSession.open() for plane in self.planes.values(): diff --git a/cumulus/src/plugins/chip/Configuration.py b/cumulus/src/plugins/chip/Configuration.py index 3fdacba5..2f7879b8 100644 --- a/cumulus/src/plugins/chip/Configuration.py +++ b/cumulus/src/plugins/chip/Configuration.py @@ -19,19 +19,312 @@ import os.path import Cfg from Hurricane import DbU from Hurricane import Box +from Hurricane import Transformation +from Hurricane import Box +from Hurricane import Path +from Hurricane import Occurrence from Hurricane import Net +from Hurricane import RoutingPad +from Hurricane import Contact +from Hurricane import Horizontal +from Hurricane import Vertical +from Hurricane import Plug +from Hurricane import Instance import CRL +from CRL import RoutingLayerGauge +from helpers import trace from helpers import ErrorMessage from helpers import WarningMessage from plugins import getParameter -class Configuration ( object ): +def breakpoint ( editor, level, message ): + if editor: + editor.fit() + editor.refresh() + Breakpoint.stop( level, message ) + return + + +def getPlugByName ( instance, netName ): + masterCell = instance.getMasterCell() + masterNet = masterCell.getNet( netName ) + if masterNet: + return instance.getPlug( masterNet ) + return None + + +def getPlugByNet ( instance, net ): + for plug in net.getPlugs(): + if plug.getInstance() == instance: + return plug + return None + + +def getRpBb ( instance, netName ): + bb = Box() + for net in instance.getMasterCell().getNets(): + if net.isExternal() and net.getName() == netName: + for component in net.getExternalComponents(): + if isinstance(component,Vertical): + bb = component.getBoundingBox() + instance.getTransformation().applyOn( bb ) + return bb + + +def showNet ( cell, netName ): + net = cell.getNet(netName) + if not net: + print ErrorMessage( 3, 'Cell %s doesn\'t have net %s' % (cell.getName(),netName) ) + return + + print 'Components of', netName + for component in net.getComponents(): + print '| ', component, component.getBoundingBox() + return + + +def destroyNetComponents ( net ): + # 1. We cannot iterate over a Hurricane Collection if we are deleting + # some of it's elements at the same time (could be improved as it + # is an intrusive map. + # 2. Lazy programming: as we don't know the destruction order, some + # components can be deleted by (previous) others so we can endup + # on dangling Python proxy which send an exception that we catch. + # 3. Plugs are not destroyed (they cannot as they are part of the + # Instance). They stay connected to the net. + toDestroy = [] + for component in net.getComponents(): + if not isinstance(component,Plug): + toDestroy.append( component ) + for component in toDestroy: + try: + component.destroy() + except: + pass + return + + +# ------------------------------------------------------------------- +# Class : "Configuration.GaugeConf". + +class GaugeConf ( object ): + + HAccess = 0x0001 + OffsetRight1 = 0x0002 + OffsetTop1 = 0x0004 + OffsetBottom1 = 0x0008 + + def __init__ ( self ): + self._routingGauge = None + self._topLayerDepth = 0 + self._plugToRp = { } + + self._loadRoutingGauge() + return + + + def _loadRoutingGauge ( self ): + self._routingGauge = CRL.AllianceFramework.get().getRoutingGauge() + + topLayer = Cfg.getParamString('katabatic.topRoutingLayer').asString() + + self._topLayerDepth = 0 + for layerGauge in self._routingGauge.getLayerGauges(): + if layerGauge.getLayer().getName() == topLayer: + self._topLayerDepth = layerGauge.getDepth() + break + if not self._topLayerDepth: + print WarningMessage( 'Gauge top layer not defined, using top of gauge (%d).' \ + % self._routingGauge.getDepth() ) + self._topLayerDepth = self._routingGauge.getDepth() + + self._horizontalDepth = 0 + self._verticalDepth = 0 + for depth in range(0,self._topLayerDepth+1): + if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal: + self._horizontalDepth = depth + if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Vertical: + self._verticalDepth = depth + return + + def _createContact ( self, net, x, y ): + return Contact.create( net + , self._routingGauge.getContactLayer(self._horizontalDepth) + , x, y + , self._routingGauge.getLayerGauge(self._horizontalDepth).getViaWidth() + , self._routingGauge.getLayerGauge(self._horizontalDepth).getViaWidth() + ) + + def _createHorizontal ( self, source, target, y ): + segment = Horizontal.create( source + , target + , self._routingGauge.getRoutingLayer(self._horizontalDepth) + , y + , self._routingGauge.getLayerGauge(self._horizontalDepth).getWireWidth() + ) + trace( 550, segment ) + return segment + + def _createVertical ( self, source, target, x ): + segment = Vertical.create( source + , target + , self._routingGauge.getRoutingLayer(self._verticalDepth) + , x + , self._routingGauge.getLayerGauge(self._verticalDepth).getWireWidth() + ) + trace( 550, segment ) + return segment + + def _rpAccess ( self, rp, flags ): + trace( 550, ',+', '\t_rpAccess() %s\n' % str(rp) ) + + contact1 = Contact.create( rp, self._routingGauge.getContactLayer(0), 0, 0 ) + + if flags & GaugeConf.OffsetBottom1: + contact1.setDy( self._routingGauge.getLayerGauge(self._horizontalDepth).getPitch() ) + + if flags & GaugeConf.OffsetTop1: + contact1.setDy( - self._routingGauge.getLayerGauge(self._horizontalDepth).getPitch() ) + + trace( 550, contact1 ) + + if flags & GaugeConf.HAccess: stopDepth = self._horizontalDepth + else: stopDepth = self._verticalDepth + + for depth in range(1,stopDepth): + xoffset = 0 + if flags & GaugeConf.OffsetRight1 and depth == 1: + xoffset = self._routingGauge.getLayerGauge(depth+1).getPitch() + contact2 = Contact.create( rp.getNet() + , self._routingGauge.getContactLayer(depth) + , contact1.getX() + xoffset + , contact1.getY() + , self._routingGauge.getLayerGauge(depth).getViaWidth() + , self._routingGauge.getLayerGauge(depth).getViaWidth() + ) + trace( 550, contact2 ) + if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal: + segment = Horizontal.create( contact1 + , contact2 + , self._routingGauge.getRoutingLayer(depth) + , contact1.getY() + , self._routingGauge.getLayerGauge(depth).getWireWidth() + ) + trace( 550, segment ) + else: + segment = Vertical.create( contact1 + , contact2 + , self._routingGauge.getRoutingLayer(depth) + , contact1.getX() + , self._routingGauge.getLayerGauge(depth).getWireWidth() + ) + trace( 550, segment ) + contact1 = contact2 + + trace( 550, '-' ) + return contact1 + + def _rpByOccurrence ( self, occurrence, net ): + plug = occurrence.getEntity() + if self._plugToRp.has_key(plug): + rp = self._plugToRp[plug] + else: + rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea ) + self._plugToRp[plug] = rp + + return rp + + def _rpAccessByOccurrence ( self, occurrence, net, flags ): + plug = occurrence.getEntity() + if self._plugToRp.has_key(plug): + rp = self._plugToRp[plug] + else: + rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea ) + self._plugToRp[plug] = rp + + return self._rpAccess( self._rpByOccurrence(occurrence,net), flags ) + + def _rpByPlug ( self, plug, net ): + if self._plugToRp.has_key(plug): + rp = self._plugToRp[plug] + else: + occurrence = Occurrence( plug, Path(net.getCell(),'') ) + rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea ) + self._plugToRp[plug] = rp + + return rp + + def _rpByPlugName ( self, instance, plugName, net ): + return self._rpByPlug( getPlugByName(instance,plugName), net ) + + def _rpAccessByPlug ( self, plug, net, flags ): + return self._rpAccess( self._rpByPlug(plug,net), flags ) + + def _rpAccessByPlugName ( self, instance, plugName, net, flags=0 ): + return self._rpAccess( self._rpByPlugName(instance,plugName,net), flags ) + + +# ------------------------------------------------------------------- +# Class : "Configuration.GaugeConfWrapper". + +class GaugeConfWrapper ( object ): + + def __init__ ( self, conf ): + #print id(conf), type(conf) + #if not isinstance(conf,GaugeConf): + # raise ErrorMessage( 1, 'Attempt to create a GaugeConfWrapper() from non-GaugeConf object.' ) + self._gaugeConf = conf + return + + @property + def gaugeConf ( self ): return self._gaugeConf + + @property + def routingGauge ( self ): return self._gaugeConf._routingGauge + @property + def topLayerDepth ( self ): return self._gaugeConf._topLayerDepth + @property + def horizontalDepth ( self ): return self._gaugeConf._horizontalDepth + @property + def verticalDepth ( self ): return self._gaugeConf._verticalDepth + + def loadRoutingGauge ( self ): self._gaugeConf._loadRoutingGauge() + + def rpByOccurrence ( self, occurrence, net ): + return self._gaugeConf._rpByOccurrence ( occurrence, net ) + + def rpByPlugName ( self, instance, plugName, net ): + return self._gaugeConf._rpByPlugName ( instance, plugName, net ) + + def rpAccess ( self, rp, flags=0 ): + return self._gaugeConf._rpAccess( rp, flags ) + + def rpAccessByOccurrence ( self, occurrence, net, flags=0 ): + return self.gaugeConf._rpAccessByOccurrence ( occurrence, net, flags ) + + def rpAccessByPlugName ( self, instance, plugName, net, flags=0 ): + return self._gaugeConf._rpAccessByPlugName( instance, plugName, net, flags ) + + def createContact ( self, net, x, y ): + return self._gaugeConf._createContact( net, x, y ) + + def createHorizontal ( self, source, target, y ): + return self._gaugeConf._createHorizontal( source, target, y ) + + def createVertical ( self, source, target, x ): + return self._gaugeConf._createVertical( source, target, x ) + + +# ------------------------------------------------------------------- +# Class : "Configuration.ChipConf". + +class ChipConf ( object ): @staticmethod - def _readChipSize( chipConfig ): - if not chipConfig.has_key('chip.size'): return Box() - chipSize = chipConfig['chip.size'] + def _readChipSize( chipConfigDict ): + if not chipConfigDict.has_key('chip.size'): return Box() + chipSize = chipConfigDict['chip.size'] if not isinstance(chipSize,tuple): print ErrorMessage( 1, 'The Chip size parameter is *not* a tuple.' ) return Box() @@ -42,11 +335,11 @@ class Configuration ( object ): @staticmethod - def _readCoreSize( chipConfig ): - if not chipConfig.has_key('core.size'): + def _readCoreSize( chipConfigDict ): + if not chipConfigDict.has_key('core.size'): print ErrorMessage( 1, 'The Core size parameter is missing.' ) return Box() - coreSize = chipConfig['core.size'] + coreSize = chipConfigDict['core.size'] if not isinstance(coreSize,tuple): print ErrorMessage( 1, 'The Core size parameter is *not* a tuple.' ) return Box() @@ -56,9 +349,18 @@ class Configuration ( object ): return Box( 0, 0, DbU.fromLambda(coreSize[0]), DbU.fromLambda(coreSize[1]) ) - def _readPads ( self, chipConfig, keyword ): - if not chipConfig.has_key(keyword): return [] - padNameList = chipConfig[keyword] + @staticmethod + def _readClockTree( chipConfigDict ): + useClockTree = False + if chipConfigDict.has_key('chip.clockTree'): + if chipConfigDict['chip.clockTree']: + useClockTree = True + return useClockTree + + + def _readPads ( self, chipConfigDict, keyword ): + if not chipConfigDict.has_key(keyword): return [] + padNameList = chipConfigDict[keyword] if not isinstance(padNameList,list): print ErrorMessage( 1, 'The "%s" entry is not a list.' ) return [] @@ -104,13 +406,11 @@ class Configuration ( object ): return - def __init__ ( self, chipConfig, cell ): - if not isinstance(chipConfig,dict): + def __init__ ( self, chipConfigDict, cell ): + if not isinstance(chipConfigDict,dict): raise ErrorMessage( 1, 'The "chip" variable is not a dictionnary.' ) self._validated = True - self._routingGauge = None - self._topLayerDepth = 0 self._cell = cell # Block Corona parameters. self._railsNb = DbU.fromLambda( getParameter('chip','chip.block.rails.count' ).asInt() ) @@ -138,40 +438,23 @@ class Configuration ( object ): self._clockPad = None self._powerPad = None self._cores = [] - self._southPads = self._readPads( chipConfig, 'pads.south' ) - self._northPads = self._readPads( chipConfig, 'pads.north' ) - self._eastPads = self._readPads( chipConfig, 'pads.east' ) - self._westPads = self._readPads( chipConfig, 'pads.west' ) - self._coreSize = Configuration._readCoreSize( chipConfig ) - self._chipSize = Configuration._readChipSize( chipConfig ) + self._southPads = self._readPads( chipConfigDict, 'pads.south' ) + self._northPads = self._readPads( chipConfigDict, 'pads.north' ) + self._eastPads = self._readPads( chipConfigDict, 'pads.east' ) + self._westPads = self._readPads( chipConfigDict, 'pads.west' ) + self._coreSize = ChipConf._readCoreSize( chipConfigDict ) + self._chipSize = ChipConf._readChipSize( chipConfigDict ) self._minCorona = DbU.fromLambda( 100 ) self._padWidth = 0 self._padHeight = 0 + self._useClockTree = ChipConf._readClockTree( chipConfigDict ) - self.loadRoutingGauge() self.checkPads() self.computeChipSize() self.findPowerAndClockNets() return - def loadRoutingGauge ( self ): - self._routingGauge = CRL.AllianceFramework.get().getRoutingGauge() - - topLayer = Cfg.getParamString('katabatic.topRoutingLayer').asString() - - self._topLayerDepth = 0 - for layerGauge in self._routingGauge.getLayerGauges(): - if layerGauge.getLayer().getName() == topLayer: - self._topLayerDepth = layerGauge.getDepth() - break - if not self._topLayerDepth: - print WarningMessage( 'Gauge top layer not defined, using top of gauge (%d).' \ - % self._routingGauge.getDepth() ) - self._topLayerDepth = self._routingGauge.getDepth() - return - - def checkPads ( self ): af = CRL.AllianceFramework.get() cellPads = [] @@ -268,86 +551,88 @@ class Configuration ( object ): return None -class Wrapper ( object ): +# ------------------------------------------------------------------- +# Class : "Configuration.ChipConfWrapper". - def __init__ ( self, conf ): - if not isinstance(conf,Configuration): - raise ErrorMessage('Attempt to create a Wrapper() from non-Configuration object.') - self._conf = conf +class ChipConfWrapper ( GaugeConfWrapper ): + + def __init__ ( self, gaugeConf, chipConf ): + GaugeConfWrapper.__init__( self, gaugeConf ) + + if not isinstance(chipConf,ChipConf): + raise ErrorMessage( 1, 'Attempt to create a ChipConfWrapper() from non-ChipConf object.' ) + self._chipConf = chipConf return - def isValid ( self ): return self._conf._validated + def isValid ( self ): return self._chipConf._validated @property - def conf ( self ): return self._conf + def chipConf ( self ): return self._chipConf @property - def routingGauge ( self ): return self._conf._routingGauge - @property - def topLayerDepth ( self ): return self._conf._topLayerDepth - - @property - def cell ( self ): return self._conf._cell + def cell ( self ): return self._chipConf._cell # Global Net names. @property - def vddeName ( self ): return self._conf._vddeName + def vddeName ( self ): return self._chipConf._vddeName @property - def vsseName ( self ): return self._conf._vsseName + def vsseName ( self ): return self._chipConf._vsseName @property - def vddiName ( self ): return self._conf._vddiName + def vddiName ( self ): return self._chipConf._vddiName @property - def vssiName ( self ): return self._conf._vssiName + def vssiName ( self ): return self._chipConf._vssiName @property - def ckiName ( self ): return self._conf._ckiName + def ckiName ( self ): return self._chipConf._ckiName @property - def ckoName ( self ): return self._conf._ckoName + def ckoName ( self ): return self._chipConf._ckoName @property - def ckName ( self ): return self._conf._ckName + def ckName ( self ): return self._chipConf._ckName # Global Nets. @property - def vdde ( self ): return self._conf._vdde + def vdde ( self ): return self._chipConf._vdde @property - def vsse ( self ): return self._conf._vsse + def vsse ( self ): return self._chipConf._vsse @property - def vddi ( self ): return self._conf._vddi + def vddi ( self ): return self._chipConf._vddi @property - def vssi ( self ): return self._conf._vssi + def vssi ( self ): return self._chipConf._vssi @property - def cki ( self ): return self._conf._cki + def cki ( self ): return self._chipConf._cki @property - def cko ( self ): return self._conf._cko + def cko ( self ): return self._chipConf._cko @property - def ck ( self ): return self._conf._ck + def ck ( self ): return self._chipConf._ck # Various. @property - def clockPad ( self ): return self._conf._clockPad - @property - def powerPad ( self ): return self._conf._powerPad - @property - def cores ( self ): return self._conf._cores - @property - def southPads ( self ): return self._conf._southPads - @property - def northPads ( self ): return self._conf._northPads - @property - def eastPads ( self ): return self._conf._eastPads - @property - def westPads ( self ): return self._conf._westPads - @property - def coreSize ( self ): return self._conf._coreSize + def clockPad ( self ): return self._chipConf._clockPad + @property + def powerPad ( self ): return self._chipConf._powerPad + @property + def cores ( self ): return self._chipConf._cores + @property + def southPads ( self ): return self._chipConf._southPads + @property + def northPads ( self ): return self._chipConf._northPads + @property + def eastPads ( self ): return self._chipConf._eastPads + @property + def westPads ( self ): return self._chipConf._westPads + @property + def coreSize ( self ): return self._chipConf._coreSize @coreSize.setter - def coreSize ( self, ab ): self._conf._coreSize = ab + def coreSize ( self, ab ): self._chipConf._coreSize = ab + @property + def chipSize ( self ): return self._chipConf._chipSize + @property + def minCorona ( self ): return self._chipConf._minCorona + @property + def padWidth ( self ): return self._chipConf._padWidth + @property + def padHeight ( self ): return self._chipConf._padHeight @property - def chipSize ( self ): return self._conf._chipSize - @property - def minCorona ( self ): return self._conf._minCorona - @property - def padWidth ( self ): return self._conf._padWidth - @property - def padHeight ( self ): return self._conf._padHeight + def useClockTree ( self ): return self._chipConf._useClockTree def loadConfiguration ( cell ): @@ -360,4 +645,5 @@ def loadConfiguration ( cell ): raise WarningMessage( 'Module <%s> do not provides the chip variable, skipped.' \ % confFile ) - return Wrapper( Configuration( confModule.__dict__['chip'], cell ) ) + return ChipConfWrapper( GaugeConf() + , ChipConf ( confModule.__dict__['chip'], cell ) ) diff --git a/cumulus/src/plugins/chip/PadsCorona.py b/cumulus/src/plugins/chip/PadsCorona.py index c5d0798c..658f68da 100644 --- a/cumulus/src/plugins/chip/PadsCorona.py +++ b/cumulus/src/plugins/chip/PadsCorona.py @@ -30,6 +30,7 @@ from Hurricane import Vertical from Hurricane import Instance import CRL from CRL import RoutingLayerGauge +from helpers import trace from helpers import ErrorMessage from helpers import WarningMessage import chip.Configuration @@ -108,12 +109,24 @@ class Side ( object ): def _createPowerContacts ( self, pad, net ): if self._type == chip.North or self._type == chip.South: - hvDepth = self._corona.getVDepth() + hvDepth = self._corona.padVDepth elif self._type == chip.East or self._type == chip.West: - hvDepth = self._corona.getHDepth() + hvDepth = self._corona.padHDepth + + trace( 550, ',+', '\t_createPowerContacts() for %s\n' % net.getName() ) masterCell = pad.getMasterCell() - for component in masterCell.getNet(net.getName()).getExternalComponents(): + if net.isGlobal(): components = masterCell.getNet(net.getName()).getExternalComponents() + else: + for plug in net.getPlugs(): + if plug.getInstance() == pad: + trace( 550, '\tFound Plug on %s\n' % pad ) + components = plug.getMasterNet().getExternalComponents() + + connecteds = False + for component in components: + trace( 550, '\t- %s\n' % component ) + if component.getBoundingBox().getYMin() > masterCell.getAbutmentBox().getYMin(): continue if self._corona.routingGauge.getLayerDepth(component.getLayer()) != hvDepth: continue if not isinstance(component,Vertical): continue @@ -128,6 +141,7 @@ class Side ( object ): position = Point( component.getX(), masterCell.getAbutmentBox().getYMin() ) pad.getTransformation().applyOn( position ) + connecteds = True self._powerContacts.append( Contact.create( net , component.getLayer() , position.getX() @@ -135,6 +149,11 @@ class Side ( object ): , width , height ) ) + if not connecteds: + print WarningMessage( 'Cannot find a suitable connector for <%s> on pad <%s>' + % (net.getName(),pad.getName()) ) + + trace( 550, '-' ) return @@ -149,6 +168,7 @@ class Side ( object ): #print 'Power pad:', pad self._createPowerContacts( pad, self._corona.vddi ) self._createPowerContacts( pad, self._corona.vssi ) + self._createPowerContacts( pad, self._corona.cko ) return @@ -226,10 +246,10 @@ class Side ( object ): return -class Corona ( chip.Configuration.Wrapper ): +class Corona ( chip.Configuration.ChipConfWrapper ): - def __init__ ( self, confWrapper ): - chip.Configuration.Wrapper.__init__( self, confWrapper.conf ) + def __init__ ( self, conf ): + chip.Configuration.ChipConfWrapper.__init__( self, conf.gaugeConf, conf.chipConf ) self.validated = False self._northSide = Side( self, chip.North ) self._southSide = Side( self, chip.South ) @@ -243,7 +263,7 @@ class Corona ( chip.Configuration.Wrapper ): self._powerRails = [] # [ , [net, layer, axis, width] ] self._locatePadRails() - self._guessHvLayers() + self._guessPadHvLayers() return @property @@ -254,6 +274,10 @@ class Corona ( chip.Configuration.Wrapper ): def eastSide ( self ): return self._eastSide @property def westSide ( self ): return self._westSide + @property + def padHDepth ( self ): return self._horizontalPadDepth + @property + def padVDepth ( self ): return self._verticalPadDepth def getSwCorner ( self, i ): return self._corners[chip.SouthWest][i] def getSeCorner ( self, i ): return self._corners[chip.SouthEast][i] @@ -263,8 +287,6 @@ class Corona ( chip.Configuration.Wrapper ): def getRailLayer ( self, i ): return self._powerRails[i][1] def getRailAxis ( self, i ): return self._powerRails[i][2] def getRailWidth ( self, i ): return self._powerRails[i][3] - def getHDepth ( self ): return self._horizontalDepth - def getVDepth ( self ): return self._verticalDepth def validate ( self ): @@ -316,7 +338,7 @@ class Corona ( chip.Configuration.Wrapper ): # ) return - def _guessHvLayers ( self ): + def _guessPadHvLayers ( self ): if not self.powerPad: print ErrorMessage( 1, 'There must be at least one pad of model "pvddick_px" to guess the pad power terminals.' ) return False @@ -329,18 +351,18 @@ class Corona ( chip.Configuration.Wrapper ): #print 'available depth:', availableDepths - self._horizontalDepth = 0 - self._verticalDepth = 0 + self._horizontalPadDepth = 0 + self._verticalPadDepth = 0 for depth in range(0,self.topLayerDepth+1): if not depth in availableDepths: continue if self.routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal: - self._horizontalDepth = depth + self._horizontalPadDepth = depth if self.routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Vertical: - self._verticalDepth = depth + self._verticalPadDepth = depth - #print 'h:', self._horizontalDepth - #print 'v:', self._verticalDepth + #print 'h:', self._horizontalPadDepth + #print 'v:', self._verticalPadDepth return diff --git a/cumulus/src/plugins/clocktree/ClockTree.py b/cumulus/src/plugins/clocktree/ClockTree.py new file mode 100755 index 00000000..3e0852be --- /dev/null +++ b/cumulus/src/plugins/clocktree/ClockTree.py @@ -0,0 +1,569 @@ +#!/usr/bin/env python +# +# This file is part of the Coriolis Software. +# Copyright (c) UPMC 2014-2014, 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@asim.lip6.fr | +# | =============================================================== | +# | Python : "./plugins/clocktree/ClockTree.py" | +# +-----------------------------------------------------------------+ + +try: + import sys + import traceback + import os.path + import optparse + import math + import Cfg + import Hurricane + from Hurricane import DbU + 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 Plug + from Hurricane import Instance + from Hurricane import HyperNet + import Viewer + import CRL + from CRL import RoutingLayerGauge + import helpers + from helpers import trace + from helpers import ErrorMessage + import Nimbus + import Metis + import Mauka + import Katabatic + import Kite + import Unicorn + import plugins + from clocktree.RSMT import RSMT + from chip.Configuration import getPlugByNet + from chip.Configuration import getPlugByName + from chip.Configuration import getRpBb + from chip.Configuration import destroyNetComponents + from chip.Configuration import GaugeConfWrapper +except ImportError, e: + module = str(e).split()[-1] + + print '[ERROR] The <%s> python module or symbol cannot be loaded.' % module + print ' Please check the integrity of the package.' + sys.exit(1) +except Exception, e: + print '[ERROR] A strange exception occurred while loading the basic Coriolis/Python' + print ' modules. Something may be wrong at Python/C API level.\n' + print ' %s' % e + sys.exit(2) + + +class HTree ( GaugeConfWrapper ): + + HAccess = 0x0001 + OffsetRight1 = 0x0002 + OffsetTop1 = 0x0004 + OffsetBottom1 = 0x0008 + + @staticmethod + def create ( conf, cell, clockNet, clockBox ): + if clockBox.isEmpty(): raise ErrorMessage( 3, 'ClockTree: The clock area is empty.' ) + + aspectRatio = DbU.toLambda( clockBox.getWidth() ) / DbU.toLambda( clockBox.getHeight() ) + if aspectRatio > 1.5 or aspectRatio < 0.5: + raise ErrorMessage( 3, 'ClockTree: aspect ratio %f is disproportionate, must be between 0.5 and 1.5.' \ + % aspectRatio ) + + ht = HTree( conf, cell, clockNet, clockBox ) + print ' o Creating Clock H-Tree for <%s>.' % cell.getName() + ht.build() + ht.place() + #ht.route() + print ' - H-Tree depth: %d' % ht.getTreeDepth() + trace( 550, '\tusedVTracks: %s\n' % str(ht.usedVTracks) ) + return ht + + def __init__ ( self, conf, cell, clockNet, area ): + print type(conf) + print type(conf.gaugeConf) + sys.stdout.flush() + GaugeConfWrapper.__init__( self, conf.gaugeConf ) + + self.minSide = DbU.fromLambda( Cfg.getParamInt('clockTree.minimumSide').asInt() ) + if self.minSide < DbU.fromLambda(100.0): + raise ErrorMessage( 3, 'ClockTree: clockTree.minimumSide (%g) is less than 100 lambda.' \ + % DbU.toLambda(self.minSide) ) + + self.framework = CRL.AllianceFramework.get() + self.cell = cell + self.area = area + self.childs = [] + self.bufferCell = self.framework.getCell( 'buf_x2', CRL.Catalog.State.Logical ) + self.tieCell = self.framework.getCell( 'tie_x0', CRL.Catalog.State.Views ) + self.cellGauge = self.framework.getCellGauge() + self.topBuffer = Instance.create( self.cell, 'ck_htree', self.bufferCell ) + self.cloneds = [ self.cell ] + self.usedVTracks = [] + self._feedCount = 0 + + self.masterClock = clockNet + if not self.masterClock: + for net in cell.getNets(): + if net.isClock(): + self.masterClock = net + break + if not self.masterClock: + print '[WARNING] Cell %s has no clock net.' % cell.getName() + self._createChildNet( self.topBuffer, 'ck_htree' ) + + return + + def _createChildNet ( self, ibuffer, tag ): + childNet = Net.create( self.cell, tag ) + childNet.setType( Net.Type.CLOCK ) + getPlugByName(ibuffer, 'q').setNet( childNet ) + return + + def feedCounter ( self ): + self._feedCount += 1 + return self._feedCount + + def toXCellGrid ( self, x ): return x - (x % self.cellGauge.getSliceStep ()) + def toYCellGrid ( self, y ): return y - (y % self.cellGauge.getSliceHeight()) + + def rpDistance ( self, rp1, rp2 ): + dx = abs( rp1.getX() - rp2.getX() ) + dy = abs( self.toYCellGrid(rp1.getY()) - self.toYCellGrid(rp2.getY()) ) + return dx+dy + + def placeInstance ( self, instance, x, y ): + xslice = self.toXCellGrid(x) + yslice = self.toYCellGrid(y) + + transformation = Transformation.Orientation.ID + if (yslice / self.cellGauge.getSliceHeight()) % 2 != 0: + transformation = Transformation.Orientation.MY + yslice += self.cellGauge.getSliceHeight() + + instance.setTransformation ( Transformation(xslice, yslice, transformation) ) + instance.setPlacementStatus( Instance.PlacementStatus.FIXED ) + return + + def destroyInstance ( self, instance ): + transformation = instance.getTransformation() + instanceWidth = instance.getMasterCell().getAbutmentBox().getWidth() + tieWidth = self.tieCell.getAbutmentBox().getWidth() + + instance.destroy() + x = transformation.getTx() + for i in range(instanceWidth/tieWidth): + feed = Instance.create( self.cell + , 'htree_feed_%i' % self.feedCounter() + , self.tieCell + , Transformation(x,transformation.getTy(),transformation.getOrientation()) + #, Instance.PlacementStatus.PLACED + ) + x += tieWidth + return + + def getTreeDepth ( self ): + return self.childs[0].getTreeDepth() + + def getLeafBufferUnder ( self, point ): + return self.childs[0].getLeafBufferUnder( point ) + + def addLeaf ( self, point, plugOccurrence ): + self.childs[0].addLeaf( point, plugOccurrence ) + + def build ( self ): + self.childs.append( HTreeNode( self, self.topBuffer, self.area, '', HTreeNode.RootBranch ) ) + return + + def place ( self ): + UpdateSession.open() + center = self.area.getCenter() + self.placeInstance( self.topBuffer, center.getX(), center.getY() ) + self.usedVTracks += [ getRpBb(self.topBuffer, 'i').getCenter().getX() ] + self.childs[0].place() + UpdateSession.close() + return + + def route ( self ): + UpdateSession.open() + self.childs[0].route() + UpdateSession.close() + return + + def prune ( self ): + UpdateSession.open() + self.childs[0].prune() + UpdateSession.close() + return + + def addCloned ( self, masterCell ): + if not masterCell in self.cloneds: self.cloneds.append( masterCell ) + return + + def addDeepPlug ( self, topNet, path ): + if path.isEmpty(): return None + + tailPath = path.getTailPath() + headInstance = path.getHeadInstance() + headPlug = getPlugByNet(headInstance,topNet) + if headPlug: + if tailPath.isEmpty(): return headPlug + return self.addDeepPlug( headPlug.getMasterNet(), tailPath ) + + masterCell = headInstance.getMasterCell() + masterNet = Net.create( masterCell, topNet.getName() ) + masterNet.setExternal ( True ) + masterNet.setType ( Net.Type.CLOCK ) + masterNet.setDirection( Net.Direction.IN ) + headPlug = headInstance.getPlug( masterNet ) + if not headPlug: + raise ErrorMessage( 3, 'Plug not created for %s on instance %s of %s' \ + % (topNet.getName(),headInstance.getName(),masterCell.getName()) ) + headPlug.setNet( topNet ) + self.addCloned( masterCell ) + + if tailPath.isEmpty(): return headPlug + return self.addDeepPlug( masterNet, tailPath ) + + def _RSMTtoLayout( self, mst, net ): + for node in mst.nodes: + if not node.component: + x = node.realX + if node.realX in self.usedVTracks: + x += self.routingGauge.getLayerGauge(self.verticalDepth).getPitch() + # This is a Steiner point. + node.component = self.createContact( net + , x + , node.realY - self.routingGauge.getLayerGauge(self.horizontalDepth).getPitch() ) + trace( 550, '\tCreate node.component: @Y%d (y:%d) %s\n' % (DbU.toLambda(node.realY) + ,DbU.toLambda(node.y) + ,node.component) ) + else: + # This a terminal (graph) point + for edge in node.edges: + flags = HTree.HAccess + if not edge.isHorizontal(): + if node.isSame(edge.source) or edge.isVertical(): + flags = 0 + break + flags |= HTree.OffsetTop1 + if node.realX in self.usedVTracks: + flags |= HTree.OffsetRight1 + node.component = self.rpAccess( node.component, flags ) + + for edge in mst.edges: + sourceContact = edge.source.component + targetContact = edge.target.component + + if edge.isHorizontal(): + self.createHorizontal( sourceContact, targetContact, targetContact.getY() ) + elif edge.isVertical(): + self.createVertical ( sourceContact, targetContact, sourceContact.getX() ) + else: + turn = self.createContact( edge.source.component.getNet() + , sourceContact.getX() + , targetContact.getY() ) + self.createVertical ( sourceContact, turn, sourceContact.getX() ) + self.createHorizontal( turn, targetContact, targetContact.getY() ) + return + + def _connectLeafs ( self, leafBuffer, leafs ): + trace( 550, ',+', '\tBuffer <%s> has %i leafs.\n' % (leafBuffer.getName(),len(leafs)) ) + if len(leafs) == 0: return + + leafCk = getPlugByName(leafBuffer,'q').getNet() + bufferRp = self.rpByPlugName( leafBuffer, 'q', leafCk ) + + rsmt = RSMT( leafCk.getName() ) + rsmt.addNode( bufferRp, bufferRp.getX(), self.toYCellGrid(bufferRp.getY()) ) + for leaf in leafs: + registerRp = self.rpByOccurrence( leaf, leafCk ) + rsmt.addNode( registerRp, registerRp.getX(), self.toYCellGrid(registerRp.getY()) ) + + rsmt.runI1S() + self._RSMTtoLayout( rsmt, leafCk ) + + trace( 550, '-' ) + return + + def connectLeaf ( self ): + UpdateSession.open() + + leafsByBuffer = {} + hyperMasterClock = HyperNet.create( Occurrence(self.masterClock) ) + for plugOccurrence in hyperMasterClock.getLeafPlugOccurrences(): + position = plugOccurrence.getBoundingBox().getCenter() + self.addLeaf( position, plugOccurrence ) + + self.childs[0].connectLeafs() + sys.stdout.flush() + + getPlugByName( self.topBuffer, 'i' ).setNet( self.masterClock ) + UpdateSession.close() + + return + + def _rsave ( self, cell ): + flags = CRL.Catalog.State.Physical + if cell.getName().endswith('_clocked'): + flags = flags | CRL.Catalog.State.Logical + self.framework.saveCell( cell, flags ) + + for instance in cell.getInstances(): + masterCell = instance.getMasterCell() + if not masterCell.isTerminal(): + self._rsave( masterCell ) + + def save ( self, topCell ): + for cell in self.cloneds: + cell.setName( cell.getName()+'_clocked' ) + + self._rsave( topCell ) + return + + +class HTreeNode ( object ): + + RootBranch = 0x0001 + LeftBranch = 0x0002 + RightBranch = 0x0004 + UpBranch = 0x0008 + DownBranch = 0x0010 + + def __init__ ( self, topTree, sourceBuffer, area, prefix, flags ): + self.topTree = topTree + self.childs = [] + self.blLeafs = [] + self.brLeafs = [] + self.tlLeafs = [] + self.trLeafs = [] + self.flags = flags + self.sourceBuffer = sourceBuffer + self.area = area + self.prefix = prefix + + self.blBuffer = Instance.create( self.topTree.cell, 'ck_htree'+self.prefix+'_bl_ins', self.topTree.bufferCell ) + self.brBuffer = Instance.create( self.topTree.cell, 'ck_htree'+self.prefix+'_br_ins', self.topTree.bufferCell ) + self.tlBuffer = Instance.create( self.topTree.cell, 'ck_htree'+self.prefix+'_tl_ins', self.topTree.bufferCell ) + self.trBuffer = Instance.create( self.topTree.cell, 'ck_htree'+self.prefix+'_tr_ins', self.topTree.bufferCell ) + self.ckNet = getPlugByName(self.sourceBuffer, 'q').getNet() + getPlugByName(self.blBuffer, 'i').setNet( self.ckNet ) + getPlugByName(self.brBuffer, 'i').setNet( self.ckNet ) + getPlugByName(self.tlBuffer, 'i').setNet( self.ckNet ) + getPlugByName(self.trBuffer, 'i').setNet( self.ckNet ) + + self.topTree._createChildNet( self.blBuffer, 'ck_htree'+self.prefix+'_bl' ) + self.topTree._createChildNet( self.brBuffer, 'ck_htree'+self.prefix+'_br' ) + self.topTree._createChildNet( self.tlBuffer, 'ck_htree'+self.prefix+'_tl' ) + self.topTree._createChildNet( self.trBuffer, 'ck_htree'+self.prefix+'_tr' ) + + halfWidth = self.area.getHalfWidth () + halfHeight = self.area.getHalfHeight() + if halfWidth >= self.topTree.minSide and halfHeight >= self.topTree.minSide: + # Recursive call. + self.childs.append( HTreeNode( self.topTree, self.blBuffer, self.blArea(), self.prefix+'_bl', 0 ) ) + self.childs.append( HTreeNode( self.topTree, self.brBuffer, self.brArea(), self.prefix+'_br', 0 ) ) + self.childs.append( HTreeNode( self.topTree, self.tlBuffer, self.tlArea(), self.prefix+'_tl', 0 ) ) + self.childs.append( HTreeNode( self.topTree, self.trBuffer, self.trArea(), self.prefix+'_tr', 0 ) ) + + return + + def xmin ( self ): return self.area.getXMin() + def xmax ( self ): return self.area.getXMax() + def ymin ( self ): return self.area.getYMin() + def ymax ( self ): return self.area.getYMax() + def halfWidth ( self ): return self.area.getHalfWidth() + def halfHeight( self ): return self.area.getHalfHeight() + + def blArea ( self ): + return Box( self.xmin() , self.ymin() + , self.xmin()+self.halfWidth(), self.ymin()+self.halfHeight() ) + + def brArea ( self ): + return Box( self.xmin()+self.halfWidth(), self.ymin() + , self.xmax() , self.ymin()+self.halfHeight() ) + + def tlArea ( self ): + return Box( self.xmin() , self.ymin()+self.halfHeight() + , self.xmin()+self.halfWidth(), self.ymax() ) + + def trArea ( self ): + return Box( self.xmin()+self.halfWidth(), self.ymin()+self.halfHeight() + , self.xmax() , self.ymax() ) + + def getTreeDepth ( self ): + if self.childs: return self.childs[0].getTreeDepth()+1 + return 1 + + def hasLeafs ( self ): + hasLeafsFlag = False + if self.childs: + hasLeafsFlag |= self.childs[0].hasLeafs() + hasLeafsFlag |= self.childs[1].hasLeafs() + hasLeafsFlag |= self.childs[2].hasLeafs() + hasLeafsFlag |= self.childs[3].hasLeafs() + return hasLeafsFlag + + hasLeafsFlag |= (len(self.blLeafs) != 0) + hasLeafsFlag |= (len(self.brLeafs) != 0) + hasLeafsFlag |= (len(self.tlLeafs) != 0) + hasLeafsFlag |= (len(self.trLeafs) != 0) + return hasLeafsFlag + + def addLeaf ( self, point, plugOccurrence ): + if self.childs: + if self.blArea().contains(point): self.childs[0].addLeaf( point, plugOccurrence ) + elif self.brArea().contains(point): self.childs[1].addLeaf( point, plugOccurrence ) + elif self.tlArea().contains(point): self.childs[2].addLeaf( point, plugOccurrence ) + else: self.childs[3].addLeaf( point, plugOccurrence ) + return + + leafBuffer = None + if self.blArea().contains(point): + self.blLeafs.append( plugOccurrence ) + leafBuffer = self.blBuffer + elif self.brArea().contains(point): + self.brLeafs.append( plugOccurrence ) + leafBuffer = self.brBuffer + elif self.tlArea().contains(point): + self.tlLeafs.append( plugOccurrence ) + leafBuffer = self.tlBuffer + else: + self.trLeafs.append( plugOccurrence ) + leafBuffer = self.trBuffer + + leafCk = getPlugByName(leafBuffer,'q').getNet() + deepPlug = self.topTree.addDeepPlug( leafCk, plugOccurrence.getPath() ) + plugOccurrence.getEntity().setNet( deepPlug.getMasterNet() ) + + return + + def getLeafBufferUnder ( self, point ): + if self.childs: + if self.blArea().contains(point): return self.childs[0].getLeafBufferUnder(point) + if self.brArea().contains(point): return self.childs[1].getLeafBufferUnder(point) + if self.tlArea().contains(point): return self.childs[2].getLeafBufferUnder(point) + if self.trArea().contains(point): return self.childs[3].getLeafBufferUnder(point) + + if self.blArea().contains(point): return self.blBuffer + if self.brArea().contains(point): return self.brBuffer + if self.tlArea().contains(point): return self.tlBuffer + return self.trBuffer + + def place ( self ): + x = self.area.getXMin() + self.area.getWidth ()/4 + y = self.area.getYMin() + self.area.getHeight()/4 + halfWidth = self.area.getHalfWidth () + halfHeight = self.area.getHalfHeight() + + self.topTree.placeInstance( self.blBuffer, x , y ) + self.topTree.placeInstance( self.brBuffer, x+halfWidth, y ) + self.topTree.placeInstance( self.tlBuffer, x , y+halfHeight ) + self.topTree.placeInstance( self.trBuffer, x+halfWidth, y+halfHeight ) + + self.topTree.usedVTracks += \ + [ self.topTree.rpAccessByPlugName( self.blBuffer, 'i', self.ckNet ).getX() + , self.topTree.rpAccessByPlugName( self.brBuffer, 'i', self.ckNet ).getX() ] + + for child in self.childs: child.place() + return + + def route ( self ): + if not self.hasLeafs(): return + + leftSourceContact = self.topTree.rpAccessByPlugName( self.sourceBuffer, 'q', self.ckNet , HTree.HAccess|HTree.OffsetBottom1 ) + rightSourceContact = self.topTree.rpAccessByPlugName( self.sourceBuffer, 'q', self.ckNet , HTree.HAccess|HTree.OffsetBottom1 ) + blContact = self.topTree.rpAccessByPlugName( self.blBuffer , 'i', self.ckNet ) + brContact = self.topTree.rpAccessByPlugName( self.brBuffer , 'i', self.ckNet ) + tlContact = self.topTree.rpAccessByPlugName( self.tlBuffer , 'i', self.ckNet ) + trContact = self.topTree.rpAccessByPlugName( self.trBuffer , 'i', self.ckNet ) + leftContact = self.topTree.createContact( self.ckNet, blContact.getX(), leftSourceContact.getY() ) + rightContact = self.topTree.createContact( self.ckNet, brContact.getX(), rightSourceContact.getY() ) + self.topTree.createHorizontal( leftContact , leftSourceContact, leftSourceContact.getY() ) + self.topTree.createHorizontal( rightSourceContact, rightContact , rightSourceContact.getY() ) + self.topTree.createVertical ( leftContact , blContact , leftContact.getX() ) + self.topTree.createVertical ( tlContact , leftContact , leftContact.getX() ) + self.topTree.createVertical ( rightContact , brContact , rightContact.getX() ) + self.topTree.createVertical ( trContact , rightContact , rightContact.getX() ) + + for child in self.childs: child.route() + return + + def connectLeafs ( self ): + if not self.hasLeafs(): + trace( 550, '\tHTree %s has no leafs\n' % self.sourceBuffer.getName() ) + + if self.childs: + self.childs[0].connectLeafs() + self.childs[1].connectLeafs() + self.childs[2].connectLeafs() + self.childs[3].connectLeafs() + return + + if len(self.blLeafs): self.topTree._connectLeafs( self.blBuffer, self.blLeafs ) + if len(self.brLeafs): self.topTree._connectLeafs( self.brBuffer, self.brLeafs ) + if len(self.tlLeafs): self.topTree._connectLeafs( self.tlBuffer, self.tlLeafs ) + if len(self.trLeafs): self.topTree._connectLeafs( self.trBuffer, self.trLeafs ) + return + + def prune ( self ): + for child in self.childs: child.prune() + if self.hasLeafs(): return + + destroyNetComponents( self.ckNet ) + + self.topTree.destroyInstance( self.blBuffer ) + self.topTree.destroyInstance( self.brBuffer ) + self.topTree.destroyInstance( self.tlBuffer ) + self.topTree.destroyInstance( self.trBuffer ) + return + + +def computeAbutmentBox ( cell, spaceMargin, aspectRatio, cellGauge ): + sliceHeight = DbU.toLambda( cellGauge.getSliceHeight() ) + + instancesNb = 0 + cellLength = 0 + for occurrence in cell.getLeafInstanceOccurrences(): + instance = occurrence.getEntity() + instancesNb += 1 + cellLength += int( DbU.toLambda(instance.getMasterCell().getAbutmentBox().getWidth()) ) + + # ar = x/y S = x*y = spaceMargin*SH x=S/y ar = S/y^2 + # y = sqrt(S/AR) + gcellLength = float(cellLength)*(1+spaceMargin) / sliceHeight + rows = math.sqrt( gcellLength/aspectRatio ) + if math.trunc(rows) != rows: rows = math.trunc(rows) + 1 + else: rows = math.trunc(rows) + columns = gcellLength / rows + if math.trunc(columns) != columns: columns = math.trunc(columns) + 1 + else: columns = math.trunc(columns) + + print ' o Creating abutment box (margin:%.1f%%, aspect ratio:%.1f%%, g-length:%.1fl)' \ + % (spaceMargin*100.0,aspectRatio*100.0,(cellLength/sliceHeight)) + print ' - GCell grid: [%dx%d]' % (columns,rows) + + UpdateSession.open() + abutmentBox = Box( DbU.fromLambda(0) + , DbU.fromLambda(0) + , DbU.fromLambda(columns*sliceHeight) + , DbU.fromLambda(rows *sliceHeight) + ) + cell.setAbutmentBox( abutmentBox ) + UpdateSession.close() + + return abutmentBox diff --git a/cumulus/src/plugins/clocktree/RSMT.py b/cumulus/src/plugins/clocktree/RSMT.py new file mode 100644 index 00000000..3a41b26e --- /dev/null +++ b/cumulus/src/plugins/clocktree/RSMT.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python +# +# This file is part of the Coriolis Software. +# Copyright (c) UPMC 2014-2014, 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@asim.lip6.fr | +# | =============================================================== | +# | Python : "./plugins/clocktree/RSMT.py" | +# +-----------------------------------------------------------------+ + + +try: + import sys + import Cfg + import Hurricane + from Hurricane import DbU + 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 + import Viewer + import CRL + from CRL import RoutingLayerGauge + import helpers + from helpers import trace + from helpers import ErrorMessage + import plugins +except ImportError, e: + module = str(e).split()[-1] + + print '[ERROR] The <%s> python module or symbol cannot be loaded.' % module + print ' Please check the integrity of the package.' + sys.exit(1) +except Exception, e: + print '[ERROR] A strange exception occurred while loading the basic Coriolis/Python' + print ' modules. Something may be wrong at Python/C API level.\n' + print ' %s' % e + sys.exit(2) + + +def manhattanDistance ( node1, node2 ): + return abs(node1.x - node2.x) + abs(node1.y - node2.y) + + +class Node ( object ): + + GraphPoint = 0x0001 + SteinerPoint = 0x0002 + KeepPoint = 0x0004 + + @staticmethod + def showNodes ( level, nodes ): + for node in nodes: + trace( level, node ) + return + + def __init__ ( self, component, x, y, flags=0 ): + self._component = component + self._edges = [ ] + self._x = x + self._y = y + self._realY = y + self._back = None + self._distance = DbU.fromLambda(1000.0) + self._flags = flags + + if component: self._flags = self._flags|Node.GraphPoint|Node.KeepPoint + else: self._flags = self._flags|Node.SteinerPoint + return + + def __cmp__ ( self, other ): + return self.distance - other.distance + + def __str__ ( self ): + return '' % ( DbU.toLambda(self.x) + , DbU.toLambda(self.y) + , DbU.toLambda(self.realY) + , DbU.toLambda(self.distance) + , str(self.component) + ) + + @property + def component ( self ): return self._component + @property + def x ( self ): return self._x + @property + def y ( self ): return self._y + @property + def realX ( self ): return self._x + @property + def realY ( self ): return self._realY + @property + def back ( self ): return self._back + @property + def distance ( self ): return self._distance + @property + def flags ( self ): return self._flags + @property + def edges ( self ): return self._edges + @property + def degree ( self ): return len(self._edges) + @component.setter + def component ( self, component ): self._component = component + @realY.setter + def realY ( self, y ): self._realY = y + + def setFlags ( self, flags ): self._flags = self._flags | flags + def unsetFlags ( self, flags ): self._flags = self._flags & ~flags + + def addEdge ( self, edge ): self._edges.append( edge ) + def delEdge ( self, edge ): + for i in range(len(self._edges)): + if self._edges[i] == edge: + del self._edges[i] + return + + def isSame ( self, other ): return id(self) == id(other) + + def update ( self, node ): + distance = manhattanDistance( self, node ) + if distance < self.distance: + self._distance = distance + self._back = node + return True + return False + + +class Edge ( object ): + + def __init__ ( self, source, target ): + self._source = source + self._target = target + self._length = manhattanDistance( self._source, self._target ) + self._source.addEdge( self ) + self._target.addEdge( self ) + return + + def __del__ ( self ): + self._source.delEdge( self ) + self._target.delEdge( self ) + return + + def __str__ ( self ): + return '' % ( DbU.toLambda(self.source.x) + , DbU.toLambda(self.source.y) + , DbU.toLambda(self.target.x) + , DbU.toLambda(self.target.y) + , DbU.toLambda(self.length) + ) + + @property + def source ( self ): return self._source + @property + def target ( self ): return self._target + @property + def length ( self ): return self._length + + def isHorizontal ( self ): return self.source.y == self.target.y + def isVertical ( self ): return self.source.x == self.target.x + + +class Graph ( object ): + + def __init__ ( self, name ): + self._nodes = [ ] + self._edges = [ ] + self._length = 0 + self._name = name + return + + def __len__ ( self ): return self._length + + @property + def name ( self ): return self._name + @property + def length ( self ): return self._length + @property + def nodes ( self ): return self._nodes + @property + def edges ( self ): return self._edges + + def addNode ( self, component, x, y ): + self._nodes.append( Node( component, x, y ) ) + return + + def copyNode ( self, node ): + self.addNode( node.component, node.x, node.y ) + self._nodes[-1].realY = node.realY + + def setNodes ( self, nodes ): + self.__init__( self.name ) + for node in nodes: + self.copyNode( node ) + return + + def showNodes ( self, level ): + trace( level, '+,+', '\tGraph Nodes:\n' ) + for node in self._nodes: + trace( level, node ) + trace(level, '--') + return + + def showEdges ( self, level ): + trace( level, '+,+', '\tGraph Edges:\n' ) + for edge in self._edges: + trace( level, edge ) + trace(level, '--') + return + + +class RMST ( Graph ): + + def __init__ ( self, name ): + Graph.__init__( self, name ) + return + + def runPrim ( self ): + self._edges = [ ] + self._length = 0 + + if len(self._nodes) < 2: return + if len(self._nodes) == 2: + self._edges.append( Edge( self._nodes[0], self._nodes[1] ) ) + self._length = self._edges[0].length + return + + trace(500, '+') + + toReach = [ ] + self._nodes[0]._distance = 0 + for node in self._nodes[1:]: + node.update( self._nodes[0] ) + toReach.append( node ) + toReach.sort() + + trace( 500, '+' , '\tPrim (initial stack)\n' ) + trace( 500, '+,+', '\tS %s\n' % self._nodes[0] ) + Node.showNodes( 500, toReach ) + trace( 500, '---' ) + + while len(toReach): + nearest = toReach.pop(0) + self._edges.append( Edge( nearest, nearest.back ) ) + trace( 500, '++,--', '\tAdding %s\n' % self._edges[-1] ) + + for node in toReach: + node.update( nearest ) + toReach.sort() + + trace( 500, '+' , '\tPrim (current stack)\n' ) + trace( 500, '+,+', '\tS %s\n' % self._nodes[0] ) + Node.showNodes( 500, toReach ) + trace( 500, '---' ) + + for edge in self._edges: + self._length += edge.length + + trace( 500, '-' ) + return + + +class RSMT ( Graph ): + + def __init__ ( self, name ): + Graph.__init__( self, name ) + self._hananNodes = [ ] + return + + def _computeHanan ( self ): + xs = [ ] + ys = [ ] + realYs = { } + for node in self._nodes: + if not node.x in xs: xs.append( node.x ) + if not node.y in xs: + ys.append( node.y ) + realYs[ node.y ] = node.component.getY() + xs.sort() + ys.sort() + + trace( 550, '+,+', '\tHanan matrix: %ix%i' % (len(xs),len(ys)) ) + + self._hananNodes = [ ] + for x in xs: + trace( 550, '\n' ) + trace( 550, '\t' ) + for y in ys: + isNode = False + for node in self._nodes: + if node.x == x and node.y == y: isNode = True + if isNode: + trace( 550, ' -:%04.2d,%04.2d' % (DbU.toLambda(x),DbU.toLambda(y)) ) + continue + trace( 550, ' H:%04.2d,%04.2d' % (DbU.toLambda(x),DbU.toLambda(y)) ) + + self._hananNodes.append( Node( None, x, y ) ) + self._hananNodes[-1].realY = realYs[ y ] + trace( 550, ',--', "\n" ) + return + + def runI1S ( self ): + self._edges = [ ] + self._length = 0 + + if len(self._nodes) < 2: return + if len(self._nodes) == 2: + self._edges.append( Edge( self._nodes[0], self._nodes[1] ) ) + self._length = self._edges[0].length + return + + self._computeHanan() + count = 0 + + trace( 550, '++' ) + minMST = RMST( 'MST[%i]' % count ) + minMST.setNodes( self._nodes ) + minMST.runPrim() + trace( 550, '-,+', '\tInitial %s length %d\n' % (minMST.name,DbU.toLambda(len(minMST))) ) + minMST.showEdges( 550 ) + trace( 550, '-' ) + + addedSteiner = True + while addedSteiner: + addedSteiner = False + for steinerNode in self._hananNodes: + count += 1 + trace( 550, '\tTrying with Steiner point H:%d,%d\n' \ + % (DbU.toLambda(steinerNode.x),DbU.toLambda(steinerNode.y)) ) + mst = RMST( 'MST[%i]' % count ) + mst.setNodes( self._nodes ) + mst.copyNode( steinerNode ) + mst.runPrim() + + trace( 550, '\tCurrent %s length %d\n' % (mst.name,DbU.toLambda(len(mst))) ) + mst.showEdges( 550 ) + if len(mst) < len(minMST): + trace( 550, '\tAccept min RST.\n' ) + minMST = mst + addedSteiner = True + + if addedSteiner: + self.copyNode( minMST.nodes[-1] ) + self.nodes[-1].setFlags( Node.KeepPoint ) + + i = 0 + while i < len(self._edges): + if self._nodes[i].flags & Node.SteinerPoint \ + and self._nodes[i].degree < 3: + trace( 550, 'Deleting unused Steiner point H:%d,%d' \ + % (DbU.toLambda(self._nodes[i].x),DbU.toLambda(self._nodes[i].y)) ) + del self._nodes[i] + else: + i += 1 + + self._nodes = minMST.nodes + self._edges = minMST.edges + self._length = minMST.length + trace( 550, '-' ) + return diff --git a/cumulus/src/plugins/clocktree/__init__.py b/cumulus/src/plugins/clocktree/__init__.py new file mode 100644 index 00000000..e69de29b