diff --git a/cumulus/src/CMakeLists.txt b/cumulus/src/CMakeLists.txt index 2e86ea52..139fdaeb 100644 --- a/cumulus/src/CMakeLists.txt +++ b/cumulus/src/CMakeLists.txt @@ -16,8 +16,11 @@ ${CMAKE_CURRENT_SOURCE_DIR}/designflow/graal.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/dreal.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/yosys.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/yosysnp.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/blif2vst.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/pnr.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/klayout.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/command.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/clean.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/alias.py ${CMAKE_CURRENT_SOURCE_DIR}/designflow/technos.py diff --git a/cumulus/src/designflow/alias.py b/cumulus/src/designflow/alias.py index 2d65204f..b19fcacd 100644 --- a/cumulus/src/designflow/alias.py +++ b/cumulus/src/designflow/alias.py @@ -7,12 +7,16 @@ class BadAliasDepend ( Exception ): pass class Alias ( FlowTask ): + """ + Create an alias for a rule. Takes only two arguments, the aliased name + of the rule and the original rule itself. + """ @staticmethod def mkRule ( rule, depends=[] ): return Alias( rule, depends ) - def __init__ ( self, rule, depends, ): + def __init__ ( self, rule, depends ): if len(depends) != 1: raise BadAliasDepend( 'Alias.__init__(): There must be exactly *one* dependency ({})' \ .format( depends )) diff --git a/cumulus/src/designflow/asimut.py b/cumulus/src/designflow/asimut.py index bed7f6df..44c11818 100644 --- a/cumulus/src/designflow/asimut.py +++ b/cumulus/src/designflow/asimut.py @@ -21,9 +21,9 @@ class Asimut ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) - self.vhdlFile = Path( self.file_depend(0) ) - self.patFile = Path( self.file_depend(1) ) - self.simFile = Path( self.targets[0] ) + self.vhdlFile = self.file_depend(0) + self.patFile = self.file_depend(1) + self.simFile = self.targets[0] self.command = [ 'asimut' ] if flags & Asimut.RootIsBehavioral: self.command.append( '-b' ) if flags & Asimut.UseBdd: self.command.append( '-bdd' ) diff --git a/cumulus/src/designflow/blif2vst.py b/cumulus/src/designflow/blif2vst.py index 3a9d9137..7ee5b711 100644 --- a/cumulus/src/designflow/blif2vst.py +++ b/cumulus/src/designflow/blif2vst.py @@ -37,18 +37,20 @@ class Blif2Vst ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - if not self.targets[0].endswith('.vst'): + if not self.targets[0].suffix == '.vst': raise TargetNotVst( 'Blif2Vst.__init__(): First target *must* "{}" be a vst file.' \ .format( self.targets[0] )) self.addClean( self.targets ) def __repr__ ( self ): + for d in self.file_dep: + print( d ) return '' \ - .format( self.design, ','.join(self.file_dep) ) + .format( self.design, ','.join([f.as_posix() for f in self.file_dep]) ) @property def design ( self ): - if len(self.targets): return self.targets[0][:-4] + if len(self.targets): return self.targets[0].stem return None def doTask ( self ): @@ -60,7 +62,7 @@ class Blif2Vst ( FlowTask ): print( 'Blif2Vst.doTask() on "{}"'.format( self.design )) views = CRL.Catalog.State.Logical | self.flags - cell = CRL.Blif.load( self.file_depend() ) + cell = CRL.Blif.load( self.file_depend().as_posix() ) if cell.getName() == 'top': print( ' o Renaming RTLIL anonymous top cell "top" into "{}".'.format(self.design) ) cell.setName( self.design ) diff --git a/cumulus/src/designflow/boog.py b/cumulus/src/designflow/boog.py index 565fab90..8e477c3d 100644 --- a/cumulus/src/designflow/boog.py +++ b/cumulus/src/designflow/boog.py @@ -27,8 +27,8 @@ class Boog ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - self.inputFile = Path( self.file_depend(0) ) - self.outputFile = Path( self.targets[0] ) + self.inputFile = self.file_depend(0) + self.outputFile = self.targets[0] self.command = [ 'boog' ] if flags & Boog.XschModeCritical: self.command += [ '-x', '0' ] if flags & Boog.XschModeAll: self.command += [ '-x', '1' ] @@ -38,7 +38,7 @@ class Boog ( FlowTask ): if flags & Boog.OptimDelaysMostly: self.command += [ '-m', '3' ] if flags & Boog.OptimDelays: self.command += [ '-m', '4' ] self.command += [ self.inputFile.stem, self.outputFile.stem ] - self.targets.append( self.outputFile.stem + '.xsc' ) + self.targets.append( self.outputFile.with_suffix('.xsc') ) self.addClean( self.targets ) def __repr__ ( self ): diff --git a/cumulus/src/designflow/boom.py b/cumulus/src/designflow/boom.py index e883f99e..a90c27fa 100644 --- a/cumulus/src/designflow/boom.py +++ b/cumulus/src/designflow/boom.py @@ -24,8 +24,8 @@ class Boom ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - self.inputFile = Path( self.file_depend(0) ) - self.outputFile = Path( self.targets[0] ) + self.inputFile = self.file_depend(0) + self.outputFile = self.targets[0] self.command = [ 'boom' ] if flags & Boom.Verbose: self.command.append( '-V' ) if flags & Boom.TraceOn: self.command.append( '-T' ) diff --git a/cumulus/src/designflow/clean.py b/cumulus/src/designflow/clean.py index 2f5a5698..e8bdca4d 100644 --- a/cumulus/src/designflow/clean.py +++ b/cumulus/src/designflow/clean.py @@ -10,12 +10,13 @@ class MissingTarget ( Exception ): pass class Clean ( FlowTask ): @staticmethod - def mkRule ( extrasClean=[] ): - return Clean( extrasClean ) + def mkRule ( extrasClean=[], extrasGlobs=[] ): + return Clean( extrasClean, extrasGlobs ) - def __init__ ( self, extrasClean ): + def __init__ ( self, extrasClean, extrasGlobs ): super().__init__( 'clean_flow', [], [] ) - self.extrasClean = extrasClean + self.extrasClean = FlowTask._normFileList( extrasClean ) + self.extrasGlobs = extrasGlobs def __repr__ ( self ): return '' @@ -23,8 +24,7 @@ class Clean ( FlowTask ): def doTask ( self, doExtrasClean ): print( ' Removing all target files' ) print( ' =========================' ) - for fileName in FlowTask.cleanTargets: - filePath = Path( fileName ) + for filePath in FlowTask.cleanTargets: if filePath.is_file(): print( ' - {:<40} [removed]'.format( filePath.as_posix() )) filePath.unlink() @@ -33,13 +33,20 @@ class Clean ( FlowTask ): if doExtrasClean and len(self.extrasClean): print( ' Removing extra clean files' ) print( ' ==========================' ) - for fileName in self.extrasClean: - filePath = Path( fileName ) + for filePath in self.extrasClean: if filePath.is_file(): print( ' - {:<40} [removed]'.format( filePath.as_posix() )) filePath.unlink() else: print( ' - {}'.format( filePath.as_posix() )) + if doExtrasClean and len(self.extrasGlobs): + print( ' Removing extra clean files (by glob)' ) + print( ' ====================================' ) + for directory, glob in self.extrasGlobs: + for filePath in Path(directory).glob(glob): + if filePath.is_file(): + print( ' - {:<40} [removed]'.format( filePath.as_posix() )) + filePath.unlink() return True def create_doit_tasks ( self ): diff --git a/cumulus/src/designflow/command.py b/cumulus/src/designflow/command.py new file mode 100644 index 00000000..6f3aab20 --- /dev/null +++ b/cumulus/src/designflow/command.py @@ -0,0 +1,41 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Command ( FlowTask ): + + @staticmethod + def mkRule ( rule, targets=[], depends=[], command=None ): + return Command( rule, targets, depends, command ) + + def __init__ ( self, rule, targets, depends, command ): + super().__init__( rule, targets, depends ) + self.command = command + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from ..helpers.io import ErrorMessage + + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Command.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Command.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/copy.py b/cumulus/src/designflow/copy.py index d0457134..564eb2e4 100644 --- a/cumulus/src/designflow/copy.py +++ b/cumulus/src/designflow/copy.py @@ -17,8 +17,8 @@ class Copy ( FlowTask ): def __init__ ( self, rule, targets, depends ): super().__init__( rule, targets, depends ) - self.sourceFile = Path( self.file_depend(0) ) - self.targetFile = Path( self.targets[0] ) + self.sourceFile = self.file_depend(0) + self.targetFile = self.targets[0] self.addClean( self.targets ) def __repr__ ( self ): diff --git a/cumulus/src/designflow/cougar.py b/cumulus/src/designflow/cougar.py index 913d0f9f..e185d9ee 100644 --- a/cumulus/src/designflow/cougar.py +++ b/cumulus/src/designflow/cougar.py @@ -25,8 +25,8 @@ class Cougar ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - self.inputFile = Path( self.file_depend(0) ) - self.outputFile = Path( self.targets[0] ) + self.inputFile = self.file_depend(0) + self.outputFile = self.targets[0] self.command = [ 'cougar' ] if flags & Cougar.Transistor: self.command.append( '-t' ) if flags & Cougar.Flatten: self.command.append( '-f' ) diff --git a/cumulus/src/designflow/dreal.py b/cumulus/src/designflow/dreal.py index 7ff5f4f3..2245c582 100644 --- a/cumulus/src/designflow/dreal.py +++ b/cumulus/src/designflow/dreal.py @@ -23,7 +23,7 @@ class Dreal ( FlowTask ): def __init__ ( self, rule, depends, flags ): super().__init__( rule, [], depends ) self.flags = flags - self.layoutFile = Path( self.file_depend(0) ) + self.layoutFile = self.file_depend(0) self.command = [ 'dreal', '-l', self.layoutFile.stem ] if flags & Dreal.Debug: self.command.append( '-debug' ) if flags & Dreal.Xor: self.command.append( '-xor' ) diff --git a/cumulus/src/designflow/druc.py b/cumulus/src/designflow/druc.py index de73cd90..87715c74 100644 --- a/cumulus/src/designflow/druc.py +++ b/cumulus/src/designflow/druc.py @@ -20,10 +20,10 @@ class Druc ( FlowTask ): def __init__ ( self, rule, depends, flags ): super().__init__( rule, [], depends ) self.flags = flags - self.referenceFile = Path( self.file_depend(0) ) - self.targets = [ self.referenceFile.stem + '.drc' - , self.referenceFile.stem + '_drc.gds' - , self.referenceFile.stem + '_rng.gds' ] + self.referenceFile = self.file_depend(0) + self.targets = [ self.referenceFile.with_suffix('.drc') + , Path(self.referenceFile.stem + '_drc.gds') + , Path(self.referenceFile.stem + '_rng.gds') ] self.command = [ 'druc', self.referenceFile.stem ] if flags & Druc.Verbose: self.command.append( '-v' ) self.addClean( self.targets ) diff --git a/cumulus/src/designflow/flatph.py b/cumulus/src/designflow/flatph.py index b15818fb..43832488 100644 --- a/cumulus/src/designflow/flatph.py +++ b/cumulus/src/designflow/flatph.py @@ -22,14 +22,14 @@ class Flatph ( FlowTask ): super().__init__( rule, targets, depends ) self.flags = flags self.instFile = None - self.hierFile = Path( self.file_depend(0) ) - self.flatFile = Path( self.targets[0] ) + self.hierFile = self.file_depend(0) + self.flatFile = self.targets[0] self.command = [ 'flatph' ] if flags & Flatph.Transistor: self.command.append( '-t' ) if flags & Flatph.Catalog: self.command.append( '-r' ) self.command.append( self.hierFile.stem ) if len(self.targets) > 1: - self.instFile = Path( self.targets[1] ) + self.instFile = self.targets[1] self.command.append( self.instFile.stem ) self.command.append( self.flatFile.stem ) self.addClean( self.targets ) diff --git a/cumulus/src/designflow/genpat.py b/cumulus/src/designflow/genpat.py index a8d3545b..97dba66b 100644 --- a/cumulus/src/designflow/genpat.py +++ b/cumulus/src/designflow/genpat.py @@ -17,8 +17,8 @@ class Genpat ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) - self.inputFile = Path( self.file_depend(0) ) - self.outputFile = Path( self.targets[0] ) + self.inputFile = self.file_depend(0) + self.outputFile = self.targets[0] self.command = [ 'genpat' ] self.command += [ self.inputFile.stem ] self.addClean( self.targets ) diff --git a/cumulus/src/designflow/graal.py b/cumulus/src/designflow/graal.py index a60ab99f..6faacf0a 100644 --- a/cumulus/src/designflow/graal.py +++ b/cumulus/src/designflow/graal.py @@ -23,7 +23,7 @@ class Graal ( FlowTask ): def __init__ ( self, rule, depends, flags ): super().__init__( rule, [], depends ) self.flags = flags - self.layoutFile = Path( self.file_depend(0) ) + self.layoutFile = self.file_depend(0) self.command = [ 'graal', '-l', self.layoutFile.stem ] if flags & Graal.Debug: self.command.append( '-debug' ) if flags & Graal.Xor: self.command.append( '-xor' ) diff --git a/cumulus/src/designflow/klayout.py b/cumulus/src/designflow/klayout.py new file mode 100644 index 00000000..1db0ce0a --- /dev/null +++ b/cumulus/src/designflow/klayout.py @@ -0,0 +1,50 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Klayout ( FlowTask ): + + Verbose = 0x0001 + + @staticmethod + def mkRule ( rule, targets=[], depends=[], script=None, variables={}, flags=0 ): + return Klayout( rule, targets, depends, script, variables, flags ) + + def __init__ ( self, rule, targets, depends, script, variables, flags ): + depends.insert( 0, script ) + super().__init__( rule, targets, depends ) + self.flags = flags + self.variable = variables + self.command = [ 'klayout' ] + for name, value in variables.items(): + self.command += [ '-rd', '{}={}'.format(name,value) ] + self.command += [ '-b', '-r', self.file_depend(0).as_posix() ] + #self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from ..helpers.io import ErrorMessage + + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Klayout.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Klayout.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/loon.py b/cumulus/src/designflow/loon.py index 97bd7dc6..38adfdd4 100644 --- a/cumulus/src/designflow/loon.py +++ b/cumulus/src/designflow/loon.py @@ -27,8 +27,8 @@ class Loon ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - self.inputFile = Path( self.file_depend(0) ) - self.outputFile = Path( self.targets[0] ) + self.inputFile = self.file_depend(0) + self.outputFile = self.targets[0] self.command = [ 'loon' ] #print( 'flags=0x{:08x}'.format( flags )) if flags & Loon.XschModeCritical: self.command += [ '-x', '0' ] @@ -39,7 +39,7 @@ class Loon ( FlowTask ): if flags & Loon.OptimDelaysMostly: self.command += [ '-m', '3' ] if flags & Loon.OptimDelays: self.command += [ '-m', '4' ] self.command += [ self.inputFile.stem, self.outputFile.stem ] - self.targets.append( self.outputFile.stem + '.xsc' ) + self.targets.append( self.outputFile.with_suffix('.xsc') ) self.addClean( self.targets ) def __repr__ ( self ): diff --git a/cumulus/src/designflow/lvx.py b/cumulus/src/designflow/lvx.py index 10707e73..91dd2c9c 100644 --- a/cumulus/src/designflow/lvx.py +++ b/cumulus/src/designflow/lvx.py @@ -25,8 +25,8 @@ class Lvx ( FlowTask ): super().__init__( rule, [], depends ) self.flags = flags - self.referenceFile = Path( self.file_depend(0) ) - self.checkedFile = Path( self.file_depend(1) ) + self.referenceFile = self.file_depend(0) + self.checkedFile = self.file_depend(1) self.command = [ 'lvx' , self.referenceFile.suffix[1:] , self.checkedFile.suffix[1:] @@ -39,7 +39,7 @@ class Lvx ( FlowTask ): if self.flags & Lvx.SaveReorder: env = CRL.AllianceFramework.get().getEnvironment() - self.targets = [ self.checkedFile.stem + '.' + env.getOUT_LO() ] + self.targets = [ self.checkedFile.with_suffix('.' + env.getOUT_LO()) ] self.addClean( self.targets ) def __repr__ ( self ): diff --git a/cumulus/src/designflow/pnr.py b/cumulus/src/designflow/pnr.py index 453851ff..4498f1f9 100644 --- a/cumulus/src/designflow/pnr.py +++ b/cumulus/src/designflow/pnr.py @@ -45,7 +45,7 @@ class PnR ( FlowTask ): def __repr__ ( self ): return '' \ - .format( self.design, ','.join(self.file_dep) ) + .format( self.design, ','.join([f.as_posix() for f in self.file_dep]) ) @property def design ( self ): diff --git a/cumulus/src/designflow/s2r.py b/cumulus/src/designflow/s2r.py index b3998072..8c53e1b5 100644 --- a/cumulus/src/designflow/s2r.py +++ b/cumulus/src/designflow/s2r.py @@ -24,8 +24,8 @@ class S2R ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - self.inputFile = Path( self.file_depend(0) ) - self.outputFile = Path( self.targets[0] ) + self.inputFile = self.file_depend(0) + self.outputFile = self.targets[0] self.command = [ 's2r' ] if flags & S2R.NoDenotch: self.command.append( '-t' ) if flags & S2R.DeleteNames: self.command.append( '-c' ) diff --git a/cumulus/src/designflow/task.py b/cumulus/src/designflow/task.py index 5b521b19..f13ea2bf 100644 --- a/cumulus/src/designflow/task.py +++ b/cumulus/src/designflow/task.py @@ -3,8 +3,9 @@ import os from pathlib import Path from doit.exceptions import TaskFailed -class BadDependency ( Exception ): pass -class DuplicatedRule ( Exception ): pass +class BadDependency ( Exception ): pass +class DuplicatedRule ( Exception ): pass +class UnsupportedFileType ( Exception ): pass class ShellEnv ( object ): @@ -90,11 +91,13 @@ class FlowTask ( object ): ``create_doit_tasks()`` method. It alows task to be chained directly between them instead of only through dependency/target files. - 1. Targets management: targets are always file name, stored as strings. + 1. Targets management: targets can be passed as plain files (string) + or pathlib.Path, but are all internally converted into Path. 2. Dependencies management: they can be plain files, pathlib.Path objects or other tasks. In the later case, the dependencies are the *targets* - of said task, which sould be files, as stated on 1. + of said task, which sould be pathlib.Path, as stated on 1. + plain files are converted into pathlib.Path. 3. Perform an early check for homonymous tasks. @@ -105,6 +108,19 @@ class FlowTask ( object ): rules = {} cleanTargets = [] + @staticmethod + def _normFile ( depend ): + if isinstance(depend,FlowTask) or isinstance(depend,Path): return depend + if isinstance(depend,str): return Path(depend) + raise UnsupportedFileType( 'FlowTask._normFile(): Unsupported type for "{}"'.format(depend) ) + + @staticmethod + def _normFileList ( depends ): + if not depends: return []; + if isinstance(depends,list): + return [ FlowTask._normFile(d) for d in depends ] + return [ FlowTask._normFile(depends) ] + def __init__ ( self, basename, targets, depends ): """ Promote ``targets`` and ``depends`` arguments to list if needed. @@ -113,12 +129,8 @@ class FlowTask ( object ): if FlowTask.hasRule(basename): raise DuplicatedRule( 'FlowTask.__init__(): Duplicated rule "{}"'.format(basename) ) self.basename = basename - if depends is None: self.depends = [] - elif not isinstance(depends,list): self.depends = [ depends ] - else: self.depends = depends - if targets is None: self.targets = [] - elif not isinstance(targets,list): self.targets = [ targets ] - else: self.targets = targets + self.depends = FlowTask._normFileList( depends ) + self.targets = FlowTask._normFileList( targets ) FlowTask.rules[ self.basename ] = self @staticmethod @@ -130,19 +142,13 @@ class FlowTask ( object ): def file_dep ( self ): """ Build the list of dependencies to be passed on to doit (file_dep task dict). - Convert back pathlib.Path object to string. If the dependency is another - FlowTask, pass on it's own targets. + If the dependency is another FlowTask, pass on it's own targets. + All files are pathlib.Path. """ files = [] for depend in self.depends: - if isinstance(depend,str): - files += [ depend ] - elif isinstance(depend,Path): - files += [ depend.as_posix() ] - elif isinstance(depend,FlowTask): - files += depend.targets - else: - raise BadDependency( 'FlowTask.file_dep(): Unsupported kind of dependency {}.'.format(depend) ) + if isinstance(depend,FlowTask): files += depend.targets + else: files += [ depend ] return files def file_target ( self, tindex=0 ): @@ -174,8 +180,7 @@ class FlowTask ( object ): """ from ..helpers.io import ErrorMessage for target in self.targets: - path = Path( target ) - if not path.is_file(): + if not target.is_file(): e = ErrorMessage( 1, '{}(): The rule "{}" did *not* generate target "{}".' \ .format( methodName, self.basename, target )) return TaskFailed( e ) @@ -186,6 +191,6 @@ class FlowTask ( object ): Add the targets list to the global list. This is a helper method that has to be explicitely called in derived classes. """ - FlowTask.cleanTargets += targets + FlowTask.cleanTargets += FlowTask._normFileList( targets ) diff --git a/cumulus/src/designflow/vasy.py b/cumulus/src/designflow/vasy.py index e00d178d..b70415d3 100644 --- a/cumulus/src/designflow/vasy.py +++ b/cumulus/src/designflow/vasy.py @@ -23,11 +23,11 @@ class Vasy ( FlowTask ): def __init__ ( self, rule, targets, depends, flags ): super().__init__( rule, targets, depends ) self.flags = flags - self.vhdlFile = Path( self.file_depend(0) ) + self.vhdlFile = self.file_depend(0) if len(self.targets) > 1: - self.outputFile = Path( self.targets[1].stem+'.vbe' ) + self.outputFile = self.targets[1].with_suffix('.vbe') else: - self.outputFile = Path( self.targets[0] ) + self.outputFile = self.targets[0] self.command = [ 'vasy' ] if flags & Vasy.Verbose: self.command.append( '-V' ) if flags & Vasy.UseStdLogic: self.command.append( '-S' ) diff --git a/cumulus/src/designflow/yosys.py b/cumulus/src/designflow/yosys.py index b48b09ca..48de9e2f 100644 --- a/cumulus/src/designflow/yosys.py +++ b/cumulus/src/designflow/yosys.py @@ -38,11 +38,8 @@ class Yosys ( FlowTask ): if top is not None: self.top = top else: - if self.main.find('.') == -1: - self.top = self.main - else: - self.top = '.'.join( self.main.split('.')[0:-1] ) - self.targets = [ self.top + '.blif' ] + self.top = self.main.stem + self.targets = [ Path( self.top + '.blif') ] self.addClean( self.targets ) def __repr__ ( self ): @@ -68,8 +65,8 @@ class Yosys ( FlowTask ): e = ErrorMessage( 1, 'Yosys._loadDesign(): Can\'t find design file "{}".'.format( design )) self.success = TaskFailed( e ) return - if design.endswith('.v' ): self._run_pass( 'read_verilog {}'.format( design )) - elif design.endswith('.il'): self._run_pass( 'read_ilang {}'.format( design )) + if design.suffix == '.v' : self._run_pass( 'read_verilog {}'.format( design.as_posix() )) + elif design.suffix == '.il': self._run_pass( 'read_ilang {}'.format( design.as_posix() )) else: e = ErrorMessage( 1, 'Yosys._loadDesign(): Unsupported input format for "{}".'.format( design )) self.success = TaskFailed( e ) diff --git a/cumulus/src/designflow/yosysnp.py b/cumulus/src/designflow/yosysnp.py new file mode 100644 index 00000000..62f26c5a --- /dev/null +++ b/cumulus/src/designflow/yosysnp.py @@ -0,0 +1,94 @@ + +import os.path +import subprocess +from pathlib import Path +from pyosys import libyosys as yosys +from doit.exceptions import TaskFailed +from .task import FlowTask + + +class BadLiberty ( Exception ): pass + + +class YosysNp ( FlowTask ): + + FlagLog = 0x00000001 + FlagQuiet = 0x00000002 + FlagUseYoWasp = 0x00000004 + _liberty = None + + @staticmethod + def setLiberty ( liberty ): + if isinstance(liberty,Path): pass + elif isinstance(liberty,str): liberty = Path( liberty ) + else: + raise BadLiberty( '[ERROR] YosysNp.setLiberty(): Should be or ({})' \ + .format( liberty )) + if not liberty.is_file(): + raise BadLiberty( '[ERROR] YosysNp.setLiberty(): File not found "{}"' \ + .format( liberty )) + YosysNp._liberty = liberty + + @staticmethod + def mkRule ( rule, targets, depends, script, top=None, flags=0 ): + return YosysNp( rule, targets, depends, script, top, flags ) + + def __init__ ( self, rule, targets, depends, script, top, flags ): + self.log = None + self.success = True + self.script = script + self.flags = flags + targets = FlowTask._normFileList( targets ) + depends = FlowTask._normFileList( depends ) + if top is not None: + self.top = top + else: + self.top = depends[0].stem + if targets == []: + targets.append( self.top + '.blif' ) + if self.flags & YosysNp.FlagLog: + self.log = Path( self.top + '.log' ) + targets.append( self.log ) + targets.append( self.top + '.ys' ) + super().__init__( rule, targets, depends ) + self.addClean( targets ) + + def __repr__ ( self ): + return ''.format( self.main, self.top ) + + @property + def liberty ( self ): + return YosysNp._liberty + + @property + def main ( self ): + return self.file_depend( 0 ) + + def doTask ( self ): + from ..helpers.io import ErrorMessage + if self.liberty is None: + e = ErrorMessage( 1, [ 'YosysNp.doTask(): "liberty" has not been set' ] ) + return TaskFailed( e ) + if not self.liberty.is_file(): + e = ErrorMessage( 1, [ 'YosysNp.doTask(): File not found "{}"' + , '"{}"'.format( self.liberty.as_posix() ) ] ) + return TaskFailed( e ) + ysFile = Path(self.main).stem + '.ys' + with open( ysFile, 'w' ) as ysFd: + ysFd.write( self.script.format( liberty =str(self.liberty) + , cellname=self.main.stem + , top =self.top )) + command = [ 'yowasp-yosys' ] if self.flags & YosysNp.FlagUseYoWasp else [ 'yosys' ] + if self.flags & YosysNp.FlagQuiet: command += [ '-q' ] + if self.flags & YosysNp.FlagLog: command += [ '-l', self.log.as_posix() ] + command += [ '-s', ysFile ] + status = subprocess.call( command ) + return status == 0 + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'file_dep' : self.file_dep + , 'targets' : self.targets + }