diff --git a/bootstrap/CMakeLists.txt b/bootstrap/CMakeLists.txt index 4a4c9f3a..0194a460 100644 --- a/bootstrap/CMakeLists.txt +++ b/bootstrap/CMakeLists.txt @@ -32,3 +32,10 @@ PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) + + install(FILES crlenv.py + DESTINATION bin + RENAME crlenv + PERMISSIONS OWNER_WRITE + OWNER_READ GROUP_READ WORLD_READ + OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) diff --git a/bootstrap/crlenv.py b/bootstrap/crlenv.py new file mode 100755 index 00000000..dabb1239 --- /dev/null +++ b/bootstrap/crlenv.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +from pathlib import Path +import socket +import subprocess +import re +import argparse + + +useDebug = False + + +reCoriolisPattern = re.compile( r".*coriolis.*" ) +reReleaseSharedPattern = re.compile( r".*Release\.Shared.*" ) +reReleaseStaticPattern = re.compile( r".*Release\.Static.*" ) +reDebugSharedPattern = re.compile( r".*Debug\.Shared.*" ) +reDebugStaticPattern = re.compile( r".*Debug\.Static.*" ) + + +def scrubPath ( pathName ): + """ + Remove from the PATH like environment variable ``pathName`` any + previous path item referring to a Coriolis location. + """ + if not pathName in os.environ: return '' + value = os.environ[ pathName ] + elements = value.split( ':' ) + scrubbed = [] + for element in elements: + if element == '': continue + if reCoriolisPattern .match(element) \ + or reReleaseSharedPattern.match(element) \ + or reReleaseStaticPattern.match(element) \ + or reDebugSharedPattern .match(element) \ + or reDebugStaticPattern .match(element): + continue + scrubbed.append( element ) + if len(scrubbed) == 0: return '' + return ':'.join( scrubbed ) + + +def envWriteBack ( pathName, pathValue ): + """ + Add to the environment PATH like variable ``pathName`` the components + given in ``pathValue`` and export it back. To avoid having multiple + Coriolis in the path, it is scrubbed beforehand. + """ + if pathName in os.environ: + scrubbed = scrubPath( pathName ) + if scrubbed != '': + pathValue += ':' + scrubbed + os.environ[ pathName ] = pathValue + return pathValue + + +def setupPaths ( verbose ): + """ + Guess and setup the main variables to use Coriolis: + + * ``PATH``, to find the binaries. + * ``LD_LIBRARY_PATH``, to access the dynamic libraries. + * ``DYLD_LIBRARY_PATH``, same as above under MacOS. + * ``PYTHONPATH``, to access the various Python modules + provided by Coriolis. + """ + global useDebug + + # Setup CORIOLIS_TOP. + osEL9 = re.compile (".*Linux.*el9.*x86_64.*") + osSlsoc7x_64 = re.compile (".*Linux.*el7.*x86_64.*") + osSlsoc6x_64 = re.compile (".*Linux.*el6.*x86_64.*") + osSlsoc6x = re.compile (".*Linux.*(el|slsoc)6.*") + osSLSoC5x_64 = re.compile (".*Linux.*el5.*x86_64.*") + osSLSoC5x = re.compile (".*Linux.*(el5|2.6.23.13.*SoC).*") + osFedora_64 = re.compile (".*Linux.*fc.*x86_64.*") + osFedora = re.compile (".*Linux.*fc.*") + osLinux_64 = re.compile (".*Linux.*x86_64.*") + osLinux = re.compile (".*Linux.*") + osDarwin = re.compile (".*Darwin.*") + osUbuntu1004 = re.compile (".*Linux.*ubuntu.*") + osUbuntu1004_64 = re.compile (".*Linux.*ubuntu.*x86_64.*") + osFreeBSD8x_amd64 = re.compile (".*FreeBSD 8.*amd64.*") + osFreeBSD8x_64 = re.compile (".*FreeBSD 8.*x86_64.*") + osFreeBSD8x = re.compile (".*FreeBSD 8.*") + osCygwinW7_64 = re.compile (".*CYGWIN_NT-6\.1.*x86_64.*") + osCygwinW7 = re.compile (".*CYGWIN_NT-6\.1.*i686.*") + osCygwinW8_64 = re.compile (".*CYGWIN_NT-6\.[2-3].*x86_64.*") + osCygwinW8 = re.compile (".*CYGWIN_NT-6\.[2-3].*i686.*") + osCygwinW10_64 = re.compile (".*CYGWIN_NT-10\.[0-3].*x86_64.*") + osCygwinW10 = re.compile (".*CYGWIN_NT-10\.[0-3].*i686.*") + + uname = subprocess.Popen( ["uname", "-srm"], stdout=subprocess.PIPE ) + lines = uname.stdout.readlines() + line = lines[0].decode( 'ascii' ) + if osSlsoc7x_64 .match(line): osDir = "Linux.el7_64" + elif osEL9 .match(line): osDir = "Linux.el9" + elif osSlsoc6x_64 .match(line): osDir = "Linux.slsoc6x_64" + elif osSlsoc6x .match(line): osDir = "Linux.slsoc6x" + elif osSLSoC5x_64 .match(line): osDir = "Linux.SLSoC5x_64" + elif osSLSoC5x .match(line): osDir = "Linux.SLSoC5x" + elif osFedora_64 .match(line): osDir = "Linux.fc_64" + elif osFedora .match(line): osDir = "Linux.fc" + elif osUbuntu1004 .match(line): osDir = "Linux.Ubuntu1004" + elif osUbuntu1004_64 .match(line): osDir = "Linux.Ubuntu1004_64" + elif osLinux_64 .match(line): osDir = "Linux.x86_64" + elif osLinux .match(line): osDir = "Linux.i386" + elif osFreeBSD8x_64 .match(line): osDir = "FreeBSD.8x.x86_64" + elif osFreeBSD8x_amd64.match(line): osDir = "FreeBSD.8x.amd64" + elif osFreeBSD8x .match(line): osDir = "FreeBSD.8x.i386" + elif osDarwin .match(line): osDir = "Darwin" + elif osCygwinW7_64 .match(line): osDir = "Cygwin.W7_64" + elif osCygwinW7 .match(line): osDir = "Cygwin.W7" + elif osCygwinW8_64 .match(line): osDir = "Cygwin.W8_64" + elif osCygwinW8 .match(line): osDir = "Cygwin.W8" + elif osCygwinW10_64 .match(line): osDir = "Cygwin.W10_64" + elif osCygwinW10 .match(line): osDir = "Cygwin.W10" + else: + uname = subprocess.Popen( ["uname", "-sr"], stdout=subprocess.PIPE ) + osDir = uname.stdout.readlines()[0][:-1] + + print( '[WARNING] environment.setupPaths(): Unrecognized OS: "{}".'.format( line[:-1] )) + print( ' (using: "{}")'.format( osDir )) + osDir = Path( osDir ) + homeDir = Path( os.environ['HOME'] ) + buildType = Path( 'Release.Debug' if useDebug else 'Release.Shared' ) + topDirs = [] + if 'CORIOLIS_TOP' in os.environ: + topDirs += [ Path( os.environ['CORIOLIS_TOP'] ) ] + topDirs += [ homeDir / 'coriolis-2.x' / osDir / buildType / 'install' + , Path( '/soc/coriolis2' ) + , Path( '/usr' ) + ] + if verbose: + print( ' o Self locating Coriolis:' ) + coriolisTop = None + for topDir in topDirs: + if not coriolisTop: + if (topDir / 'bin' / 'cgt').is_file(): + if verbose: + print( ' - {} *'.format(topDir) ) + coriolisTop = topDir + else: + if verbose: + print( ' - {}'.format(topDir) ) + if not coriolisTop: + print( '[ERROR] environment.setupPaths(): Unable to locate Coriolis.' ) + return False + + os.environ[ 'CORIOLIS_TOP' ] = coriolisTop.as_posix() + if coriolisTop == '/usr': sysconfDir = Path( 'etc', 'coriolis2' ) + else: sysconfDir = coriolisTop / 'etc' / 'coriolis2' + + # Setup PATH. + binPath = envWriteBack( 'PATH', (coriolisTop/'bin').as_posix() ) + + # Setup LD_LIBRARY_PATH. + libDirs = [] + for lib in [ Path('lib'), Path('lib64') ]: + libDir = lib + absLibDir = coriolisTop / lib + if absLibDir.is_dir(): + libDirs.append( absLibDir ) + libDir = None + if not len(libDirs): + print( '[ERROR] environment.setupPaths(): Library directory not found.' ) + return False + libraryPath = '' + ldPathName = 'LD_LIBRARY_PATH' + if osDir.as_posix().startswith( 'Darwin' ): + ldPathName = 'DYLD_LIBRARY_PATH' + for libDir in libDirs: + if len(libraryPath): libraryPath = libraryPath + ':' + libraryPath = libraryPath + libDir.as_posix() + libraryPath = envWriteBack( ldPathName, libraryPath ) + + # Setup PYTHONPATH. + v = sys.version_info + sitePackagesDir = None + for pyPackageDir in [ Path('python{}.{}'.format(v.major,v.minor)) / 'site-packages' + , Path('python{}.{}'.format(v.major,v.minor)) / 'dist-packages' + , Path('{}.{}'.format(v.major,v.minor)) / 'site-packages' + , Path('python{}'.format(v.major)) / 'site-packages' + , Path('python{}'.format(v.major)) / 'dist-packages' + , Path('{}'.format(v.major)) / 'site-packages' + ]: + sitePackagesDir = libDirs[-1] / pyPackageDir + if sitePackagesDir.is_dir(): + if verbose: + print( ' - {} *'.format(sitePackagesDir) ) + break + if verbose: + print( ' - {}'.format(sitePackagesDir) ) + sitePackagesDir = None + if sitePackagesDir is None: + print( '[ERROR] environment.setupPaths(): Python {site,dist}-packages directory not found.' ) + return False + pythonPath = '' + for packageDir in [ sitePackagesDir + , sitePackagesDir / 'crlcore' + , sitePackagesDir / 'cumulus' + , sitePackagesDir / 'cumulus/plugins' + , sitePackagesDir / 'status' + , sysconfDir + ]: + sys.path.append( str(packageDir) ) + if len(pythonPath): pythonPath += ':' + pythonPath += str(packageDir) + pythonPath = envWriteBack( 'PYTHONPATH', pythonPath ) + return True + + +def printVariable ( name ): + if not name in os.environ: + print( '{:<16}:'.format( name )) + print( '- variable_not_set' ) + return + values = os.environ[ name ].split( ':' ) + print( '{}:'.format( name )) + for value in values: + print( '- {}'.format( value )) + + +def printEnvironment (): + """ + Display the environment setup, using YAML formatting. + """ + print( '# crlenv.py: Alliance/Coriolis finder, guessed values.' ) + print( '---' ) + for name in ('CORIOLIS_TOP', 'PATH', 'DYLD_LIBRARY_PATH' + , 'LD_LIBRARY_PATH', 'PYTHONPATH'): + printVariable( name ) + + +if __name__ == '__main__': + """ + Run any script in a environmnent set for Coriolis. + + Example: + + .. code:: bash + + ego@home:~> crlenv.py -- doit clean_flow + b2v Run . + cgt Run plain CGT (no loaded design) + clean_flow Clean all generated (targets) files. + gds Run . + pnr Run . + yosys Run . + ego@home:~> crlenv.py -- bash + [ego@home]$ echo $CORIOLIS_TOP + /home/ego/coriolis-2.x/Linux.el9/Release.Shared/install + [ego@home]$ exit + ego@home:~> + """ + parser = argparse.ArgumentParser() + parser.add_argument( '-v', '--verbose', action='store_true', dest='verbose' ) + parser.add_argument( '-d', '--debug' , action='store_true', dest='debug' ) + parser.add_argument( 'command', nargs='*' ) + args = parser.parse_args() + + setupPaths( args.verbose ) + if not len(args.command): + printEnvironment() + sys.exit( 0 ) + state = subprocess.run( args.command ) + sys.exit( state.returncode ) diff --git a/cumulus/src/Alliance.py b/cumulus/src/Alliance.py index 6b0c2b76..adb2b0a6 100644 --- a/cumulus/src/Alliance.py +++ b/cumulus/src/Alliance.py @@ -381,7 +381,7 @@ def staticInitialization (): return -helpers.staticInitialization() +helpers.staticInitialization( quiet=True ) staticInitialization() report = ReportLog( 'alliance' ) diff --git a/cumulus/src/CMakeLists.txt b/cumulus/src/CMakeLists.txt index 5ec7f5a2..75b225ce 100644 --- a/cumulus/src/CMakeLists.txt +++ b/cumulus/src/CMakeLists.txt @@ -1,5 +1,30 @@ # -*- explicit-buffer-name: "CMakeLists.txt" -*- + set ( pyDesignFlow ${CMAKE_CURRENT_SOURCE_DIR}/designflow/task.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/copy.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/vasy.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/boom.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/boog.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/loon.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/genpat.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/asimut.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/flatph.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/cougar.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/s2r.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/lvx.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/druc.py + ${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/blif2vst.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/pnr.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/clean.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/alias.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/technos.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/routecheck.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/pnrcheck.py + ${CMAKE_CURRENT_SOURCE_DIR}/designflow/alliancesynth.py + ) set ( pySources ${CMAKE_CURRENT_SOURCE_DIR}/placeandroute.py ${CMAKE_CURRENT_SOURCE_DIR}/ref.py ${CMAKE_CURRENT_SOURCE_DIR}/Alliance.py @@ -90,6 +115,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/plugins/alpha/harness/pads.py ) + install ( FILES ${pyDesignFlow} DESTINATION ${Python_CORIOLISLIB}/designflow ) install ( FILES ${pySources} DESTINATION ${Python_CORIOLISLIB}/cumulus ) install ( FILES ${pyPlugins} DESTINATION ${Python_CORIOLISLIB}/cumulus/plugins ) install ( FILES ${pyPluginCTS} DESTINATION ${Python_CORIOLISLIB}/cumulus/plugins/cts ) diff --git a/cumulus/src/designflow/__init__.py b/cumulus/src/designflow/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cumulus/src/designflow/alias.py b/cumulus/src/designflow/alias.py new file mode 100644 index 00000000..2d65204f --- /dev/null +++ b/cumulus/src/designflow/alias.py @@ -0,0 +1,36 @@ + +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask + +class BadAliasDepend ( Exception ): pass + + +class Alias ( FlowTask ): + + @staticmethod + def mkRule ( rule, depends=[] ): + return Alias( rule, depends ) + + def __init__ ( self, rule, depends, ): + if len(depends) != 1: + raise BadAliasDepend( 'Alias.__init__(): There must be exactly *one* dependency ({})' \ + .format( depends )) + if not isinstance(depends[0],FlowTask): + raise BadAliasDepend( 'Alias.__init__(): The dependency must be another *rule* ({})' \ + .format( depends[0] )) + super().__init__( rule, [], depends ) + + def __repr__ ( self ): + return ''.format( self.basename, self.depends[0].basename ) + + def doTask ( self ): + return True + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/alliancesynth.py b/cumulus/src/designflow/alliancesynth.py new file mode 100644 index 00000000..84aad560 --- /dev/null +++ b/cumulus/src/designflow/alliancesynth.py @@ -0,0 +1,43 @@ + + +from pathlib import Path +from .vasy import Vasy +from .boom import Boom +from .boog import Boog +from .loon import Loon +from .clean import Clean + + +HasFsm = 0x00000001 + + +def mkRuleSet ( callerGlobals, behFile, flags=0 ): + if not isinstance(behFile,Path): + behFile = Path( behFile ) + + ruleStem = behFile.stem + toolFlags = Vasy.AddPowerSupplies|Boom.LocalOptimization + if behFile.suffix in ('.vhdl', '.vhd', '.vlog', '.v'): + vasyTargets = [ Path(behFile.stem + '.vbe') ] + if flags & HasFsm: + vasyTargets = [ Path(behFile.stem+'_model.vbe') + , Path(behFile.stem+'.vst') ] + ruleVasy = Vasy.mkRule( 'vasy_'+ruleStem, vasyTargets + , behFile + , flags=toolFlags ) + behFile = vasyTargets[0] + + ruleBoom = Boom.mkRule( 'boom_'+ruleStem, behFile.stem + '_boom.vbe' + , behFile.stem + '.vbe' + , flags=toolFlags ) + ruleBoog = Boog.mkRule( 'boog_'+ruleStem, behFile.stem + '_boog.vst' + , ruleBoom + , flags=toolFlags ) + ruleLoon = Loon.mkRule( 'loon_'+ruleStem, behFile.stem + '.vst' + , ruleBoog + , flags=toolFlags ) + + for tag in [ 'Vasy', 'Boom', 'Boog', 'Loon' ]: + rule = 'rule' + tag + if rule in locals(): + callerGlobals[ rule+'_'+ruleStem ] = locals()[ rule ] diff --git a/cumulus/src/designflow/asimut.py b/cumulus/src/designflow/asimut.py new file mode 100644 index 00000000..99fb971d --- /dev/null +++ b/cumulus/src/designflow/asimut.py @@ -0,0 +1,57 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Asimut ( FlowTask ): + + RootIsBehavioral = 0x0001 + UseBdd = 0x0002 + ZeroDelay = 0x0004 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Asimut( rule, targets, depends, flags ) + + 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.command = [ 'asimut' ] + if flags & Asimut.RootIsBehavioral: self.command.append( '-b' ) + if flags & Asimut.UseBdd: self.command.append( '-bdd' ) + if flags & Asimut.ZeroDelay: self.command.append( '-zerodelay' ) + self.command += [ self.vhdlFile.stem, self.patFile.stem, self.simFile.stem ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'MBK_IN_LO' ] = 'vst' + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Asimut.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Asimut.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/blif2vst.py b/cumulus/src/designflow/blif2vst.py new file mode 100644 index 00000000..7c640556 --- /dev/null +++ b/cumulus/src/designflow/blif2vst.py @@ -0,0 +1,85 @@ + +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask + + +class TargetNotVst ( Exception ): pass + + +def renameNMigen( occurrence ): + masterCell = occurrence.getEntity().getMasterCell() + origName = masterCell.getName() + replName = origName.replace( '$$', '_unm' ) + if not masterCell.isTerminalNetlist() and not replName.startswith('cmpt_'): + replName = 'cmpt_' + replName + #for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': + # replName = replName.replace(letter, '{}u'.format(letter)) + if origName != replName: + print( ' - "{}" => "{}"'.format(origName,replName) ) + masterCell.setName( replName ) + + +def renameNMigenUniquify ( topCell ): + for occurrence in topCell.getTerminalNetlistInstanceOccurrences(): + renameNMigen(occurrence) + for occurrence in topCell.getNonTerminalNetlistInstanceOccurrences(): + renameNMigen(occurrence) + return + + +class Blif2Vst ( FlowTask ): + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Blif2Vst( rule, targets, depends, flags ) + + def __init__ ( self, rule, targets, depends, flags ): + super().__init__( rule, targets, depends ) + self.flags = flags + if not self.targets[0].endswith('.vst'): + raise TargetNotVst( 'Blif2Vst.__init__(): First target *must* "{}" be a vst file.' \ + .format( self.targets[0] )) + self.addClean( self.targets ) + + def __repr__ ( self ): + return '' \ + .format( self.design, ','.join(self.file_dep) ) + + @property + def design ( self ): + if len(self.targets): return self.targets[0][:-4] + return None + + def doTask ( self ): + from Hurricane import Cell + import CRL + import Viewer + from helpers.io import ErrorMessage + import plugins.rsave + + print( 'Blif2Vst.doTask() on "{}"'.format( self.design )) + views = CRL.Catalog.State.Logical | self.flags + cell = CRL.Blif.load( self.file_depend() ) + if cell.getName() == 'top': + print( ' o Renaming RTLIL anonymous top cell "top" into "{}".'.format(self.design) ) + cell.setName( self.design ) + renameNMigenUniquify( cell ) + CRL.restoreNetsDirection( cell, Cell.Flags_TerminalNetlist ) + kw = {} + kw['views'] = views + kw['cell' ] = cell + plugins.rsave.scriptMain( **kw ) + + return self.checkTargets( 'Blif2Vst.doTask' ) + + def create_doit_tasks ( self ): + if self.design: doc = 'Run {}.'.format( self ) + else: doc = 'Run plain CGT (no loaded design)' + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : doc + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/boog.py b/cumulus/src/designflow/boog.py new file mode 100644 index 00000000..4bc9a4b8 --- /dev/null +++ b/cumulus/src/designflow/boog.py @@ -0,0 +1,68 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class UnsupportedVHdlSuffix ( Exception ): pass + + +class Boog ( FlowTask ): + + FlagMask = 0x00110000 + XschModeCritical = 0x00010000 + XschModeAll = 0x00020000 + OptimArea = 0x00040000 + OptimAreaMostly = 0x00080000 + OptimBalanced = 0x00100000 + OptimDelaysMostly = 0x00200000 + OptimDelays = 0x00400000 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Boog( rule, targets, depends, flags ) + + 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.command = [ 'boog' ] + if flags & Boog.XschModeCritical: self.command += [ '-x', '0' ] + if flags & Boog.XschModeAll: self.command += [ '-x', '1' ] + if flags & Boog.OptimArea: self.command += [ '-m', '0' ] + if flags & Boog.OptimAreaMostly: self.command += [ '-m', '1' ] + if flags & Boog.OptimBalanced: self.command += [ '-m', '2' ] + 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.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'MBK_OUT_LO' ] = self.outputFile.suffix[1:] + shellEnv.export() + print( ' -> Running "{}" ...'.format( ' '.join(self.command) )) + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Boog.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Boog.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/boom.py b/cumulus/src/designflow/boom.py new file mode 100644 index 00000000..c827817f --- /dev/null +++ b/cumulus/src/designflow/boom.py @@ -0,0 +1,60 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class UnsupportedVHdlSuffix ( Exception ): pass + + +class Boom ( FlowTask ): + + FlagMask = 0x00001100 + Verbose = 0x00000100 + TraceOn = 0x00000200 + ReverseBdd = 0x00000400 + LocalOptimization = 0x00000800 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Boom( rule, targets, depends, flags ) + + 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.command = [ 'boom' ] + if flags & Boom.Verbose: self.command.append( '-V' ) + if flags & Boom.TraceOn: self.command.append( '-T' ) + if flags & Boom.ReverseBdd: self.command.append( '-O' ) + if flags & Boom.LocalOptimization: self.command.append( '-A' ) + self.command += [ self.inputFile.stem, self.outputFile.stem ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv.export() + print( ' -> Running "{}" ...'.format( ' '.join(self.command) )) + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Boom.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Boom.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/clean.py b/cumulus/src/designflow/clean.py new file mode 100644 index 00000000..2f5a5698 --- /dev/null +++ b/cumulus/src/designflow/clean.py @@ -0,0 +1,56 @@ + +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask + + +class MissingTarget ( Exception ): pass + + +class Clean ( FlowTask ): + + @staticmethod + def mkRule ( extrasClean=[] ): + return Clean( extrasClean ) + + def __init__ ( self, extrasClean ): + super().__init__( 'clean_flow', [], [] ) + self.extrasClean = extrasClean + + def __repr__ ( self ): + return '' + + def doTask ( self, doExtrasClean ): + print( ' Removing all target files' ) + print( ' =========================' ) + for fileName in FlowTask.cleanTargets: + filePath = Path( fileName ) + if filePath.is_file(): + print( ' - {:<40} [removed]'.format( filePath.as_posix() )) + filePath.unlink() + else: + print( ' - {}'.format( filePath.as_posix() )) + if doExtrasClean and len(self.extrasClean): + print( ' Removing extra clean files' ) + print( ' ==========================' ) + for fileName in self.extrasClean: + filePath = Path( fileName ) + if filePath.is_file(): + print( ' - {:<40} [removed]'.format( filePath.as_posix() )) + filePath.unlink() + else: + print( ' - {}'.format( filePath.as_posix() )) + return True + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Clean all generated (targets) files.' + , 'params' : [ { 'name' : 'doExtrasClean' + , 'long' : 'extras' + , 'type' : bool + , 'default' : False + } ] + , 'uptodate' : [ False ] + } + diff --git a/cumulus/src/designflow/copy.py b/cumulus/src/designflow/copy.py new file mode 100644 index 00000000..735998a9 --- /dev/null +++ b/cumulus/src/designflow/copy.py @@ -0,0 +1,44 @@ + +import os +import shutil +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Copy ( FlowTask ): + + @staticmethod + def mkRule ( rule, targets, depends=[] ): + return Copy( rule, targets, depends ) + + 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.addClean( self.targets ) + + def __repr__ ( self ): + return ''.format( self.sourceFile.as_posix(), self.targetFile.as_posix() ) + + def doTask ( self ): + from helpers.io import ErrorMessage + try: + shutil.copyfile( self.sourceFile, self.targetFile ) + except Exception as e: + e = ErrorMessage( 1, [ 'Copy.doTask(): shutil.copyfile failed.' + , str(e) ] ) + return TaskFailed( e ) + return self.checkTargets( 'Copy.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : 'copy_' + self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/cougar.py b/cumulus/src/designflow/cougar.py new file mode 100644 index 00000000..77248aa3 --- /dev/null +++ b/cumulus/src/designflow/cougar.py @@ -0,0 +1,65 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Cougar ( FlowTask ): + + Transistor = 0x0001 + Flatten = 0x0002 + Verbose = 0x0004 + Core = 0x0008 + GroundCap = 0x0010 + WireRC = 0x0020 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Cougar( rule, targets, depends, flags ) + + 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.command = [ 'cougar' ] + if flags & Cougar.Transistor: self.command.append( '-t' ) + if flags & Cougar.Flatten: self.command.append( '-f' ) + if flags & Cougar.Verbose: self.command.append( '-v' ) + if flags & Cougar.Core: self.command.append( '-c' ) + if flags & Cougar.GroundCap: self.command.append( '-ac' ) + if flags & Cougar.WireRC: self.command.append( '-ar' ) + self.command += [ self.inputFile.stem, self.outputFile.stem ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'MBK_OUT_LO' ] = self.outputFile.suffix[1:] + shellEnv[ 'MBK_IN_PH' ] = self.inputFile .suffix[1:] + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Cougar.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Cougar.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/dreal.py b/cumulus/src/designflow/dreal.py new file mode 100644 index 00000000..7ff5f4f3 --- /dev/null +++ b/cumulus/src/designflow/dreal.py @@ -0,0 +1,56 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Dreal ( FlowTask ): + + Debug = 0x0001 + Xor = 0x0002 + Install = 0x0004 + Force = 0x0008 + + @staticmethod + def mkRule ( rule, depends=[], flags=0 ): + return Dreal( rule, depends, flags ) + + def __init__ ( self, rule, depends, flags ): + super().__init__( rule, [], depends ) + self.flags = flags + self.layoutFile = Path( 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' ) + if flags & Dreal.Install: self.command.append( '-install' ) + if flags & Dreal.Force: self.command.append( '-force' ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'RDS_IN' ] = self.layoutFile.suffix[1:] + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Dreal.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Dreal.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'file_dep' : self.file_dep + , 'uptodate' : [ False ] + } + diff --git a/cumulus/src/designflow/druc.py b/cumulus/src/designflow/druc.py new file mode 100644 index 00000000..813a80a8 --- /dev/null +++ b/cumulus/src/designflow/druc.py @@ -0,0 +1,53 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Druc ( FlowTask ): + + Verbose = 0x0001 + + @staticmethod + def mkRule ( rule, depends=[], flags=0 ): + return Druc( rule, depends, flags ) + + 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.command = [ 'druc', self.referenceFile.stem ] + if flags & Druc.Verbose: self.command.append( '-v' ) + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Druc.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Druc.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/flatph.py b/cumulus/src/designflow/flatph.py new file mode 100644 index 00000000..b15818fb --- /dev/null +++ b/cumulus/src/designflow/flatph.py @@ -0,0 +1,61 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Flatph ( FlowTask ): + + Transistor = 0x0001 + Catalog = 0x0002 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Flatph( rule, targets, depends, flags ) + + def __init__ ( self, rule, targets, depends, flags ): + 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.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.command.append( self.instFile.stem ) + self.command.append( self.flatFile.stem ) + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'MBK_IN_PH' ] = self.hierFile.suffix[1:] + shellEnv[ 'MBK_IN_PH' ] = self.flatFile.suffix[1:] + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Flatph.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Flatph.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/genpat.py b/cumulus/src/designflow/genpat.py new file mode 100644 index 00000000..2ef91010 --- /dev/null +++ b/cumulus/src/designflow/genpat.py @@ -0,0 +1,48 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Genpat ( FlowTask ): + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Genpat( rule, targets, depends, flags ) + + 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.command = [ 'genpat' ] + self.command += [ self.inputFile.stem ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Genpat.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Genpat.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/graal.py b/cumulus/src/designflow/graal.py new file mode 100644 index 00000000..a60ab99f --- /dev/null +++ b/cumulus/src/designflow/graal.py @@ -0,0 +1,56 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Graal ( FlowTask ): + + Debug = 0x0001 + Xor = 0x0002 + Install = 0x0004 + Force = 0x0008 + + @staticmethod + def mkRule ( rule, depends=[], flags=0 ): + return Graal( rule, depends, flags ) + + def __init__ ( self, rule, depends, flags ): + super().__init__( rule, [], depends ) + self.flags = flags + self.layoutFile = Path( 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' ) + if flags & Graal.Install: self.command.append( '-install' ) + if flags & Graal.Force: self.command.append( '-force' ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'MBK_IN_PH' ] = self.layoutFile.suffix[1:] + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Graal.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Graal.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'file_dep' : self.file_dep + , 'uptodate' : [ False ] + } + diff --git a/cumulus/src/designflow/loon.py b/cumulus/src/designflow/loon.py new file mode 100644 index 00000000..5eb8288c --- /dev/null +++ b/cumulus/src/designflow/loon.py @@ -0,0 +1,70 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class UnsupportedVHdlSuffix ( Exception ): pass + + +class Loon ( FlowTask ): + + FlagMask = 0x11000000 + XschModeCritical = 0x01000000 + XschModeAll = 0x02000000 + OptimArea = 0x04000000 + OptimAreaMostly = 0x08000000 + OptimBalanced = 0x10000000 + OptimDelaysMostly = 0x20000000 + OptimDelays = 0x40000000 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Loon( rule, targets, depends, flags ) + + 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.command = [ 'loon' ] + #print( 'flags=0x{:08x}'.format( flags )) + if flags & Loon.XschModeCritical: self.command += [ '-x', '0' ] + if flags & Loon.XschModeAll: self.command += [ '-x', '1' ] + if flags & Loon.OptimArea: self.command += [ '-m', '0' ] + if flags & Loon.OptimAreaMostly: self.command += [ '-m', '1' ] + if flags & Loon.OptimBalanced: self.command += [ '-m', '2' ] + 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.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'MBK_OUT_LO' ] = self.outputFile.suffix[1:] + shellEnv[ 'MBK_IN_LO' ] = self.inputFile .suffix[1:] + shellEnv.export() + print( ' -> Running "{}" ...'.format( ' '.join(self.command) )) + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Loon.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Loon.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/lvx.py b/cumulus/src/designflow/lvx.py new file mode 100644 index 00000000..a4a8b3ce --- /dev/null +++ b/cumulus/src/designflow/lvx.py @@ -0,0 +1,68 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class Lvx ( FlowTask ): + + MergeSupply = 0x0001 + SaveReorder = 0x0002 + CheckUnassigned = 0x0004 + Flatten = 0x0008 + + @staticmethod + def mkRule ( rule, depends=[], flags=0 ): + return Lvx( rule, depends, flags ) + + def __init__ ( self, rule, depends, flags ): + import CRL + + super().__init__( rule, [], depends ) + self.flags = flags + self.referenceFile = Path( self.file_depend(0) ) + self.checkedFile = Path( self.file_depend(1) ) + self.command = [ 'lvx' + , self.referenceFile.suffix[1:] + , self.checkedFile.suffix[1:] + , self.referenceFile.stem + , self.checkedFile.stem ] + if flags & Lvx.MergeSupply: self.command.append( '-a' ) + if flags & Lvx.SaveReorder: self.command.append( '-o' ) + if flags & Lvx.CheckUnassigned: self.command.append( '-u' ) + if flags & Lvx.Flatten: self.command.append( '-f' ) + + if self.flags & Lvx.SaveReorder: + env = CRL.AllianceFramework.get().getEnvironment() + self.targets = [ self.checkedFile.stem + '.' + env.getOUT_LO() ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Lvx.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Lvx.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/pnr.py b/cumulus/src/designflow/pnr.py new file mode 100644 index 00000000..39063f30 --- /dev/null +++ b/cumulus/src/designflow/pnr.py @@ -0,0 +1,114 @@ + +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class PnR ( FlowTask ): + """ + Rule to call a Coriolis ``scriptMain()`` script. If no script is provided, + just launch an empty graphical interface. + + This class provides the expected ``doit`` function ``create_doit_tasks()`` + so it is recognized as a task. + + The action to be called by ``doit`` is provided by the ``doTask()`` function. + + To create a new instance (task) of this rule, call the static method + ``mkRule()``. + """ + + textMode = True + + @staticmethod + def mkRule ( rule, targets=[], depends=[], script=None ): + """ + Creates a new rule instance (``doit`` task). + + :param rule: The name of the rule (``basename`` for ``doit``). + :param targets: A scalar or a list of either files or ``pathlib.Path`` instances. + :param depends: A scalar or a list of file, ``pathlib.Path`` or other rule + instances. In the later case all the *targets* of the rules are + considered as dependencies. + :param script: A callable, typically a ``scriptMain()`` function. The only + requirement is that it should accept one keyworded argument (``**kw``). + """ + return PnR( rule, targets, depends, script ) + + def __init__ ( self, rule, targets, depends, script ): + super().__init__( rule, targets, depends ) + self.script = script + self.addClean( self.targets ) + + def __repr__ ( self ): + return '' \ + .format( self.design, ','.join(self.file_dep) ) + + @property + def design ( self ): + if len(self.targets): return self.targets[0] + return None + + def _setCgtBanner ( self, banner ): + banner.setName( 'cgt' ) + banner.setPurpose( 'Coriolis Graphical Tool' ) + return banner + + def doTask ( self ): + from helpers.io import ErrorMessage + if self.design: + print( 'PnR.doTask() on "{}"'.format( self.design )) + else: + print( 'PnR.doTask() run in interactive CGT mode.' ) + PnR.textMode = False + import Etesian + import Anabatic + import Katana + import Katabatic + import Kite + import Bora + import Tutorial + import Viewer + import Unicorn + + ShellEnv().export() + if self.script and not callable(self.script): + e = ErrorMessage( 1, 'PnR.doTask(): "script" argument is *not* callable.' ) + return TaskFailed( e ) + if self.script: + self.script( **{} ) + if not PnR.textMode: + # Run in graphic mode. + ha = Viewer.HApplication.create( [] ) + Viewer.Graphics.enable() + + unicorn = Unicorn.UnicornGui.create() + unicorn.setApplicationName ( 'cgt') + unicorn.registerTool ( Etesian.GraphicEtesianEngine.grab() ) + unicorn.registerTool ( Kite.GraphicKiteEngine.grab() ) + unicorn.registerTool ( Katana.GraphicKatanaEngine.grab() ) + unicorn.registerTool ( Bora.GraphicBoraEngine.grab() ) + unicorn.registerTool ( Tutorial.GraphicTutorialEngine.grab() ) + #unicorn.setAnonNetSelectable(False) + unicorn.setLayerVisible ( "grid" , False ); + unicorn.setLayerVisible ( "text.instance" , False ); + unicorn.setLayerVisible ( "text.component", False ); + self._setCgtBanner(unicorn.getBanner()) + unicorn.show() + ha.qtExec() + + return self.checkTargets( 'PnR.doTask' ) + + def create_doit_tasks ( self ): + if self.design: doc = 'Run {}.'.format( self ) + else: doc = 'Run plain CGT (no loaded design)' + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : doc + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/pnrcheck.py b/cumulus/src/designflow/pnrcheck.py new file mode 100644 index 00000000..f90c00d3 --- /dev/null +++ b/cumulus/src/designflow/pnrcheck.py @@ -0,0 +1,62 @@ + +from .copy import Copy +from .yosys import Yosys +from .blif2vst import Blif2Vst +from .cougar import Cougar +from .lvx import Lvx +from .druc import Druc +from .pnr import PnR +from .alias import Alias +from .clean import Clean +PnR.textMode = True + + +UseClockTree = 0x0001 +NoSynthesis = 0x0002 +IsChip = 0x0004 + + +def mkRuleSet ( callerGlobals, vlogDesignName, flags=0, extraRtlDepends=[], extrasClean=[] ): + from doDesign import scriptMain + + vhdlDesignName = vlogDesignName.lower() + if flags & IsChip: + routedName = 'chip' + else: + routedName = vhdlDesignName + if flags & UseClockTree: + routedName = vhdlDesignName + '_cts' + + if not (flags & NoSynthesis): + ruleYosys = Yosys .mkRule( 'yosys', vlogDesignName+'.v' ) + ruleB2V = Blif2Vst.mkRule( 'b2v' , [ vhdlDesignName+'.vst' + , vlogDesignName+'.spi' ] + , [ruleYosys] + , flags=0 ) + rtlDepends = [ ruleB2V ] + pnrTargets = [ routedName+'_r.ap' + , routedName+'_r.vst' + , routedName+'_r.spi' ] + else: + rtlDepends = [ vhdlDesignName+'.vst' ] + pnrTargets = [ routedName+'_r.ap' + , routedName+'_r.vst' + , routedName+'_r.spi' + , routedName+'.vst' + , routedName+'.spi' ] + rtlDepends += extraRtlDepends + rulePnR = PnR .mkRule( 'pnr', pnrTargets, rtlDepends, scriptMain ) + ruleCougar = Cougar.mkRule( 'cougar', routedName+'_r_ext.vst', [rulePnR], flags=Cougar.Verbose ) + ruleLvx = Lvx .mkRule( 'lvx' + , [ rulePnR.file_target(1) + , ruleCougar.file_target(0) ] + , flags=Lvx.Flatten ) + ruleDruc = Druc .mkRule( 'druc' , [rulePnR], flags=0 ) + ruleLayout = Alias .mkRule( 'layout', [rulePnR] ) + ruleCgt = PnR .mkRule( 'cgt' ) + ruleClean = Clean .mkRule( extrasClean ) + + for tag in [ 'Yosys', 'B2V', 'PnR', 'Cougar', 'Lvx', 'Druc', 'Cgt', 'Layout', 'Clean' ]: + rule = 'rule' + tag + if rule in locals(): + callerGlobals[ rule ] = locals()[ rule ] diff --git a/cumulus/src/designflow/routecheck.py b/cumulus/src/designflow/routecheck.py new file mode 100644 index 00000000..a1d32389 --- /dev/null +++ b/cumulus/src/designflow/routecheck.py @@ -0,0 +1,34 @@ + +from .copy import Copy +from .cougar import Cougar +from .lvx import Lvx +from .druc import Druc +from .pnr import PnR +from .alias import Alias +from .clean import Clean +PnR.textMode = True + +def mkRuleSet ( callerGlobals, designName ): + from doDesign import scriptMain + + rulePnR = PnR .mkRule( 'pnr', [ designName+'_r.ap' + , designName+'_r.vst' + , designName+'_r.spi' + ] + , [ designName+'.vst' + , designName+'.ap' + ] + , scriptMain ) + ruleCougar = Cougar.mkRule( 'cougar', designName+'_r_ext.vst', [rulePnR], flags=Cougar.Verbose ) + ruleLvx = Lvx .mkRule( 'lvx' + , [ rulePnR.file_target(1) + , ruleCougar.file_target(0) ] + , flags=Lvx.Flatten ) + ruleDruc = Druc .mkRule( 'druc' , [rulePnR], flags=0 ) + ruleLayout = Alias .mkRule( 'layout', [rulePnR] ) + ruleCgt = PnR .mkRule( 'cgt' ) + ruleClean = Clean .mkRule() + + for tag in [ 'PnR', 'Cougar', 'Lvx', 'Druc', 'Cgt', 'Layout', 'Clean' ]: + rule = 'rule' + tag + callerGlobals[ rule ] = locals()[ rule ] diff --git a/cumulus/src/designflow/s2r.py b/cumulus/src/designflow/s2r.py new file mode 100644 index 00000000..f3ba218e --- /dev/null +++ b/cumulus/src/designflow/s2r.py @@ -0,0 +1,63 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class MissingTarget ( Exception ): pass + + +class S2R ( FlowTask ): + + NoDenotch = 0x0001 + DeleteNames = 0x0002 + DoBlackboxes = 0x0004 + NoReplaceBlackboxes = 0x0008 + Verbose = 0x0010 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return S2R( rule, targets, depends, flags ) + + 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.command = [ 's2r' ] + if flags & S2R.NoDenotch: self.command.append( '-t' ) + if flags & S2R.DeleteNames: self.command.append( '-c' ) + if flags & S2R.DoBlackboxes: self.command.append( '-1' ) + if flags & S2R.NoReplaceBlackboxes: self.command.append( '-r' ) + if flags & S2R.Verbose: self.command.append( '-v' ) + self.command += [ self.inputFile.stem, self.outputFile.stem ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv[ 'RDS_OUT' ] = self.outputFile.suffix[1:] + shellEnv[ 'MBK_IN_PH' ] = self.inputFile .suffix[1:] + shellEnv.export() + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'S2R.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'S2R.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } + diff --git a/cumulus/src/designflow/task.py b/cumulus/src/designflow/task.py new file mode 100644 index 00000000..2c6eceb4 --- /dev/null +++ b/cumulus/src/designflow/task.py @@ -0,0 +1,191 @@ + +import os +from pathlib import Path +from doit.exceptions import TaskFailed + +class BadDependency ( Exception ): pass +class DuplicatedRule ( Exception ): pass + + +class ShellEnv ( object ): + """ + Manage and export to the sub-processes the legacy Alliance variables. + + Environment variables stored at class level, should be set once and + for all at startup: + + * ``ALLIANCE_TOP``, usually identical to ``CORIOLIS_TOP``. + * ``RDS_TECHNO_NAME``. + * ``CHECK_TOOLKIT``, where the ``alliance-check-toolkit`` is installed. + + Mutable environment variables, could be changed in each instance. + Their initial values are extracted from the Coriolis Alliance Framework. + + * ``MBK_CATA_LIB``. + * ``MBK_TARGET_LIB``. + * ``MBK_SCALE_X``. + * ``MBK_IN_LO``. + * ``MBK_OUT_LO``. + * ``MBK_IN_PH``. + * ``MBK_OUT_PH``. + * ``MBK_CATAL_NAME``. + * ``RDS_IN``. + * ``RDS_OUT``. + """ + + ALLIANCE_TOP = None + RDS_TECHNO_NAME = None + CHECK_TOOLKIT = None + + def __init__ ( self ): + self.shellEnv = {} + self.capture() + + def __getitem__ ( self, key ): + return self.shellEnv[ key ] + + def __setitem__ ( self, key, value ): + self.shellEnv[ key ] = str( value ) + + def capture ( self ): + """ + Get the default values of the environment variables from the + Coriolis configuration. + """ + import CRL + + af = CRL.AllianceFramework.get() + env = af.getEnvironment() + if len(af.getAllianceLibraries()) > 1: + cataLib = [ lib.getPath() for lib in af.getAllianceLibraries()[1:] ] + self.shellEnv[ 'MBK_CATA_LIB' ] = ':'.join( cataLib ) + self.shellEnv[ 'MBK_TARGET_LIB' ] = cataLib[0] + self.shellEnv[ 'MBK_SCALE_X' ] = str( env.getSCALE_X() ) + self.shellEnv[ 'MBK_IN_LO' ] = env.getIN_LO() + self.shellEnv[ 'MBK_OUT_LO' ] = env.getOUT_LO() + self.shellEnv[ 'MBK_IN_PH' ] = env.getIN_PH() + self.shellEnv[ 'MBK_OUT_PH' ] = env.getOUT_PH() + self.shellEnv[ 'MBK_CATAL_NAME' ] = env.getCATALOG() + self.shellEnv[ 'RDS_IN' ] = 'gds' + self.shellEnv[ 'RDS_OUT' ] = 'gds' + self.shellEnv[ 'ALLIANCE_TOP' ] = ShellEnv.ALLIANCE_TOP + + def export ( self ): + """ + Write back the variables into the environement for usage by the + sub-processes. + """ + for variable, value in self.shellEnv.items(): + if value is None: continue + os.environ[ variable ] = value + if ShellEnv.RDS_TECHNO_NAME is not None: + os.environ[ 'RDS_TECHNO_NAME' ] = ShellEnv.RDS_TECHNO_NAME + if ShellEnv.CHECK_TOOLKIT is not None: + os.environ[ 'CHECK_TOOLKIT' ] = ShellEnv.CHECK_TOOLKIT + + +class FlowTask ( object ): + """ + Add extra features over a doit task. This class *do not* provides the + ``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. + + 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. + + 3. Perform an early check for homonymous tasks. + + 4. Keep track of all the targets of all the tasks to provide them for + the special ``clean_flow`` task. + """ + + rules = {} + cleanTargets = [] + + def __init__ ( self, basename, targets, depends ): + """ + Promote ``targets`` and ``depends`` arguments to list if needed. + Check for duplicated rules, then register the rule name at class level. + """ + 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 + FlowTask.rules[ self.basename ] = self + + @staticmethod + def hasRule ( name ): + if name in FlowTask.rules: return True + return False + + @property + 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. + """ + 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) ) + return files + + def file_target ( self, tindex=0 ): + """ + Access a target, by default, the first one. + """ + if len(self.targets) > tindex: + return self.targets[ tindex ] + return None + + def file_depend ( self, dindex=0, tindex=0 ): + """ + Access a dependency, by default the first one. It can be either a + string or a pathlib.Path object. If the dependency itself is a + FlowTask, then it's first *target* is returned. The dependency + can be selected by a ``dindex``, and a ``tindex`` in case of a + FlowTask. + """ + if len(self.depends) > dindex: + if isinstance(self.depends[dindex],FlowTask): + return self.depends[ dindex ].file_target( tindex ) + return self.depends[ dindex ] + return None + + def checkTargets ( self, methodName ): + """ + Checks that all the the target files have been generated, stop on + error. This is a helper for derived classes. + """ + from helpers.io import ErrorMessage + for target in self.targets: + path = Path( target ) + if not path.is_file(): + e = ErrorMessage( 1, '{}(): The rule "{}" did *not* generate target "{}".' \ + .format( methodName, self.basename, target )) + return TaskFailed( e ) + return True + + def addClean ( self, targets ): + """ + 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 + + diff --git a/cumulus/src/designflow/technos.py b/cumulus/src/designflow/technos.py new file mode 100644 index 00000000..ec66221a --- /dev/null +++ b/cumulus/src/designflow/technos.py @@ -0,0 +1,308 @@ + +import sys +import os +import socket +from pathlib import Path +from .task import ShellEnv + + +__all__ = [ 'Where', 'setupCMOS', 'setupCMOS45' ] + + +class Where ( object ): + + coriolisTop = None + allianceTop = None + cellsTop = None + checkToolkit = None + + def __init__ ( self, checkToolkit=None ): + if 'CORIOLIS_TOP' in os.environ: Where.coriolisTop = Path( os.environ['CORIOLIS_TOP'] ) + if 'ALLIANCE_TOP' in os.environ: Where.allianceTop = Path( os.environ['ALLIANCE_TOP'] ) + if 'CELLS_TOP' in os.environ: Where.cellsTop = Path( os.environ['CELLS_TOP'] ) + if Where.coriolisTop and not Where.allianceTop: Where.allianceTop = Where.coriolisTop + #print( Where.coriolisTop, Where.allianceTop ) + if not Where.coriolisTop: + print( 'technos.Where.__init__(): Unable to locate Coriolis top.' ) + if checkToolkit is None: + checkToolkit = Path.home() / 'coriolis-2.x' / 'src' / 'alliance-check-toolkit' + else: + if isinstance(checkToolkit,str): + checkToolkit = Path( checkToolkit ) + if not Where.cellsTop: + Where.cellsTop = checkToolkit / 'cells' + Where.checkToolkit = checkToolkit + if not Where.cellsTop and Where.allianceTop: + Where.cellsTop = Where.allianceTop / 'cells' + ShellEnv.ALLIANCE_TOP = Where.allianceTop.as_posix() + + def __repr__ ( self ): + if not Where.coriolisTop: + return '' + return ''.format( Where.coriolisTop.as_posix() ) + + +def setupCMOS (): + import Cfg + import Viewer + import CRL + import symbolic.cmos + from helpers import overlay, l, u, n + from designflow.yosys import Yosys + + Where() + + with overlay.CfgCache(priority=Cfg.Parameter.Priority.UserFile) as cfg: + cfg.misc.catchCore = False + cfg.misc.info = False + cfg.misc.paranoid = False + cfg.misc.bug = False + cfg.misc.logMode = True + cfg.misc.verboseLevel1 = True + cfg.misc.verboseLevel2 = True + cfg.misc.minTraceLevel = 1900 + cfg.misc.maxTraceLevel = 3000 + cfg.katana.eventsLimit = 1000000 + cfg.katana.termSatReservedLocal = 6 + cfg.katana.termSatThreshold = 9 + Viewer.Graphics.setStyle( 'Alliance.Classic [black]' ) + af = CRL.AllianceFramework.get() + env = af.getEnvironment() + env.setCLOCK( '^ck$|m_clock|^clk$' ) + + Yosys.setLiberty( Where.cellsTop / 'sxlib' / 'sxlib.lib' ) + ShellEnv.RDS_TECHNO_NAME = (Where.allianceTop / 'etc' / 'cmos.rds').as_posix() + + path = None + for pathVar in [ 'PATH', 'path' ]: + if pathVar in os.environ: + path = os.environ[ pathVar ] + os.environ[ pathVar ] = path + ':' + (Where.allianceTop / 'bin').as_posix() + break + + +def setupCMOS45 ( useNsxlib=False, checkToolkit=None, cellsTop=None ): + import Cfg + import Viewer + import CRL + import symbolic.cmos45 + from helpers import overlay, l, u, n + from designflow.yosys import Yosys + + Where( checkToolkit ) + if cellsTop is None: + cellsTop = Where.cellsTop + else: + if isinstance(cellsTop,str): + cellsTop = Path( cellsTop ) + + with overlay.CfgCache(priority=Cfg.Parameter.Priority.UserFile) as cfg: + cfg.misc.catchCore = False + cfg.misc.info = False + cfg.misc.paranoid = False + cfg.misc.bug = False + cfg.misc.logMode = True + cfg.misc.verboseLevel1 = True + cfg.misc.verboseLevel2 = True + cfg.misc.minTraceLevel = 1900 + cfg.misc.maxTraceLevel = 3000 + cfg.katana.eventsLimit = 1000000 + cfg.etesian.graphics = 3 + cfg.etesian.spaceMargin = 0.05 + cfg.etesian.aspectRatio = 1.0 + cfg.anabatic.edgeLenght = 24 + cfg.anabatic.edgeWidth = 8 + if useNsxlib: + cfg.anabatic.routingGauge = 'msxlib4' + cfg.anabatic.topRoutingLayer = 'METAL4' + cfg.katana.termSatReservedLocal = 6 + cfg.katana.termSatThreshold = 9 + + Viewer.Graphics.setStyle( 'Alliance.Classic [black]' ) + af = CRL.AllianceFramework.get() + env = af.getEnvironment() + env.setCLOCK( '^ck$|m_clock|^clk$' ) + + sxlib = cellsTop / 'nsxlib' + iolib = cellsTop / 'niolib' + liberty = sxlib / 'nsxlib.lib' + env.addSYSTEM_LIBRARY( library=iolib.as_posix(), mode=CRL.Environment.Prepend ) + env.addSYSTEM_LIBRARY( library=sxlib.as_posix(), mode=CRL.Environment.Prepend ) + if not sxlib.is_dir(): + print( '[ERROR] technos.setupCMOS45(): sxlib directory do *not* exists:' ) + print( ' "{}"'.format(sxlib.as_posix()) ) + + Yosys.setLiberty( liberty ) + ShellEnv.RDS_TECHNO_NAME = (Where.checkToolkit / 'etc' / 'FreePDK45.rds').as_posix() + + path = None + for pathVar in [ 'PATH', 'path' ]: + if pathVar in os.environ: + path = os.environ[ pathVar ] + os.environ[ pathVar ] = path + ':' + (Where.allianceTop / 'bin').as_posix() + break + + +def setupSky130_c4m ( checkToolkit=None, pdkMasterTop=None ): + import Cfg + import Viewer + import CRL + import helpers + from helpers import overlay, l, u, n + from designflow.yosys import Yosys + + if isinstance(pdkMasterTop,str): + pdkMasterTop = Path( pdkMasterTop ) + ndaDirectory = None + if pdkMasterTop: + ndaDirectory = pdkMasterTop / 'libs.tech' / 'coriolis' / 'techno' + elif not ndaDirectory: + hostname = socket.gethostname() + if hostname.startswith('lepka'): + ndaDirectory = Path( '/dsk/l1/jpc/crypted/soc/techno' ) + if not ndaDirectory.is_dir(): + print ('[ERROR] You forgot to mount the NDA encrypted directory, stupid!') + else: + ndaDirectory = Path( '/users/soft/techno/techno' ) + pdkMasterTop = ndaDirectory + helpers.setNdaTopDir( ndaDirectory.as_posix() ) + if not pdkMasterTop.is_dir(): + print( '[ERROR] technos.setupSky130_c4m(): pdkMasterTop directory do *not* exists:' ) + print( ' "{}"'.format(pdkMasterTop.as_posix()) ) + + Where( checkToolkit ) + + from node130.sky130 import techno, StdCellLib #, LibreSOCIO + techno.setup() + StdCellLib.setup() + #LibreSOCIO.setup() + + cellsTop = pdkMasterTop / 'libs.ref' + liberty = cellsTop / 'StdCellLib' / 'liberty' / 'StdCellLib_nom.lib' + + with overlay.CfgCache(priority=Cfg.Parameter.Priority.UserFile) as cfg: + cfg.misc.catchCore = False + cfg.misc.minTraceLevel = 12300 + cfg.misc.maxTraceLevel = 12400 + cfg.misc.info = False + cfg.misc.paranoid = False + cfg.misc.bug = False + cfg.misc.logMode = True + cfg.misc.verboseLevel1 = False + cfg.misc.verboseLevel2 = False + cfg.etesian.graphics = 2 + cfg.anabatic.topRoutingLayer = 'm4' + cfg.katana.eventsLimit = 4000000 + af = CRL.AllianceFramework.get() + lg5 = af.getRoutingGauge( 'StdCellLib' ).getLayerGauge( 5 ) + lg5.setType( CRL.RoutingLayerGauge.PowerSupply ) + env = af.getEnvironment() + env.setCLOCK( '^sys_clk$|^ck|^jtag_tck$' ) + + Yosys.setLiberty( liberty ) + ShellEnv.CHECK_TOOLKIT = Where.checkToolkit.as_posix() + + +def setupFreePDK45_c4m ( checkToolkit=None, pdkMasterTop=None ): + import Cfg + import Viewer + import CRL + import helpers + from helpers import overlay, l, u, n + from designflow.yosys import Yosys + + if isinstance(pdkMasterTop,str): + pdkMasterTop = Path( pdkMasterTop ) + if not pdkMasterTop.is_dir(): + print( '[ERROR] technos.setupFreePDK45_c4m(): pdkMasterTop directory do *not* exists:' ) + print( ' "{}"'.format(pdkMasterTop.as_posix()) ) + sys.path.append( (pdkMasterTop / 'coriolis' / 'techno' / 'etc' / 'coriolis2').resolve().as_posix() ) + + Where( checkToolkit ) + + from NDA.node45.freepdk45_c4m import techno, FlexLib, LibreSOCIO + techno.setup() + FlexLib.setup() + LibreSOCIO.setup() + + liberty = pdkMasterTop / 'views' / 'FreePDK45' / 'FlexLib' / 'liberty' / 'FlexLib_nom.lib' + + with overlay.CfgCache(priority=Cfg.Parameter.Priority.UserFile) as cfg: + cfg.misc.catchCore = False + cfg.misc.minTraceLevel = 12300 + cfg.misc.maxTraceLevel = 12400 + cfg.misc.info = False + cfg.misc.paranoid = False + cfg.misc.bug = False + cfg.misc.logMode = True + cfg.misc.verboseLevel1 = True + cfg.misc.verboseLevel2 = True + cfg.etesian.graphics = 3 + cfg.etesian.spaceMargin = 0.10 + cfg.anabatic.topRoutingLayer = 'metal6' + cfg.katana.eventsLimit = 4000000 + af = CRL.AllianceFramework.get() + lg5 = af.getRoutingGauge('FlexLib').getLayerGauge( 5 ) + lg5.setType( CRL.RoutingLayerGauge.PowerSupply ) + env = af.getEnvironment() + env.setCLOCK( '^sys_clk$|^ck|^jtag_tck$' ) + + Yosys.setLiberty( liberty ) + ShellEnv.CHECK_TOOLKIT = Where.checkToolkit.as_posix() + + +def setupTSMC_c180_c4m ( checkToolkit=None, ndaTop=None ): + import Cfg + import Viewer + import CRL + import helpers + from helpers import overlay, l, u, n + from designflow.yosys import Yosys + + ndaDirectory = None + if ndaTop is not None: + if not isinstance(ndaTop,Path): + ndaDirectory = ndaTop + else: + ndaDirectory = Path( ndaTop ) + if not ndaDirectory: + hostname = socket.gethostname() + if hostname.startswith('lepka'): + ndaDirectory = Path( '/dsk/l1/jpc/crypted/soc/techno' ) + if not ndaDirectory.is_dir(): + print( '[ERROR] You forgot to mount the NDA encrypted directory, stupid!' ) + else: + ndaDirectory = '/users/soft/techno/techno' + helpers.setNdaTopDir( ndaDirectory ) + + Where( checkToolkit ) + + from NDA.node180.tsmc_c018 import techno, FlexLib, LibreSOCIO, LibreSOCMem #, pll + techno.setup() + FlexLib.setup() + LibreSOCIO.setup() + LibreSOCMem.setup() + #pll.setup() + + liberty = ndaDirectory / 'etc' / 'coriolis2' / 'NDA' / 'node180' / 'tsmc_c018' / 'FlexLib.lib' + + with overlay.CfgCache(priority=Cfg.Parameter.Priority.UserFile) as cfg: + cfg.misc.catchCore = False + cfg.misc.minTraceLevel = 10100 + cfg.misc.maxTraceLevel = 10200 + cfg.misc.info = False + cfg.misc.paranoid = False + cfg.misc.bug = False + cfg.misc.logMode = True + cfg.misc.verboseLevel1 = True + cfg.misc.verboseLevel2 = True + cfg.etesian.graphics = 3 + cfg.etesian.uniformDensity = True + cfg.etesian.spaceMargin = 0.04 + cfg.katana.eventsLimit = 4000000 + af = CRL.AllianceFramework.get() + env = af.getEnvironment() + env.setCLOCK( '^clk|^reset' ) + + Yosys.setLiberty( liberty ) + ShellEnv.CHECK_TOOLKIT = Where.checkToolkit.as_posix() diff --git a/cumulus/src/designflow/vasy.py b/cumulus/src/designflow/vasy.py new file mode 100644 index 00000000..6454517d --- /dev/null +++ b/cumulus/src/designflow/vasy.py @@ -0,0 +1,71 @@ + +import os +import subprocess +from pathlib import Path +from doit.exceptions import TaskFailed +from .task import FlowTask, ShellEnv + + +class UnsupportedVHdlSuffix ( Exception ): pass + + +class Vasy ( FlowTask ): + + FlagMask = 0x00000011 + Verbose = 0x00000001 + UseStdLogic = 0x00000002 + AddPowerSupplies = 0x00000004 + + @staticmethod + def mkRule ( rule, targets, depends=[], flags=0 ): + return Vasy( rule, targets, depends, flags ) + + def __init__ ( self, rule, targets, depends, flags ): + super().__init__( rule, targets, depends ) + self.flags = flags + self.vhdlFile = Path( self.file_depend(0) ) + if len(self.targets) > 1: + self.outputFile = Path( self.targets[1].stem+'.vbe' ) + else: + self.outputFile = Path( self.targets[0] ) + self.command = [ 'vasy' ] + if flags & Vasy.Verbose: self.command.append( '-V' ) + if flags & Vasy.UseStdLogic: self.command.append( '-S' ) + if flags & Vasy.AddPowerSupplies: self.command.append( '-p' ) + + if self.outputFile.suffix in ('.v', '.vlog'): + self.command.append( '-v' ) + elif self.outputFile.suffix in ('.vbe', '.vst'): + self.command.append( '-a' ) + elif self.outputFile.suffix ('.vhd',): + self.command.append( '-s' ) + if not (self.vhdlFile.suffix in ('.vbe', '.vst', '.vhd', '.vhdl')): + raise UnsupportedVHdlSuffix( 'Vasy.__init__(): File "{}" has unsupported VHDL suffix (not .vbe, .vst, .vhd, .vhdl).' ) + self.command += [ '-I', self.vhdlFile.suffix[1:] ] + self.command += [ self.vhdlFile.stem, self.outputFile.stem ] + self.addClean( self.targets ) + + def __repr__ ( self ): + return '<{}>'.format( ' '.join(self.command) ) + + def doTask ( self ): + from CRL import AllianceFramework + from helpers.io import ErrorMessage + + shellEnv = ShellEnv() + shellEnv.export() + print( ' -> Running "{}" ...'.format( ' '.join(self.command) )) + state = subprocess.run( self.command ) + if state.returncode: + e = ErrorMessage( 1, 'Vasy.doTask(): UNIX command failed ({}).' \ + .format( state.returncode )) + return TaskFailed( e ) + return self.checkTargets( 'Vasy.doTask' ) + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'targets' : self.targets + , 'file_dep' : self.file_dep + } diff --git a/cumulus/src/designflow/yosys.py b/cumulus/src/designflow/yosys.py new file mode 100644 index 00000000..68ed0c26 --- /dev/null +++ b/cumulus/src/designflow/yosys.py @@ -0,0 +1,118 @@ + +import os.path +from pathlib import Path +from pyosys import libyosys as yosys +from doit.exceptions import TaskFailed +from .task import FlowTask + + +class BadLiberty ( Exception ): pass + + +class Yosys ( FlowTask ): + + _liberty = None + + @staticmethod + def setLiberty ( liberty ): + if isinstance(liberty,Path): pass + elif isinstance(liberty,str): liberty = Path( liberty ) + else: + raise BadLiberty( '[ERROR] Yosys.setLiberty(): Should be or ({})' \ + .format( liberty )) + if not liberty.is_file(): + raise BadLiberty( '[ERROR] Yosys.setLiberty(): File not found "{}"' \ + .format( liberty )) + Yosys._liberty = liberty + + @staticmethod + def mkRule ( rule, depends, top=None, blackboxes=[], flattens=[] ): + return Yosys( rule, depends, top, blackboxes, flattens ) + + def __init__ ( self, rule, depends, top, blackboxes, flattens ): + super().__init__( rule, [], depends ) + self.success = True + self.blackboxes = blackboxes + self.flattens = flattens + self.depends += blackboxes + 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.addClean( self.targets ) + + def __repr__ ( self ): + return '' \ + .format( self.main, self.top, ','.join(self.blackboxes), ','.join(self.flattens) ) + + @property + def liberty ( self ): + return Yosys._liberty + + @property + def main ( self ): + return self.file_depend( 0 ) + + def _run_pass ( self, command ): + if self.success is not True: return + yosys.run_pass( command, self.tool ) + + def _loadDesign ( self, design ): + from helpers.io import ErrorMessage + if self.success is not True: return + if not os.path.isfile(design): + 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 )) + else: + e = ErrorMessage( 1, 'Yosys._loadDesign(): Unsupported input format for "{}".'.format( design )) + self.success = TaskFailed( e ) + return + + def _loadBlackboxes ( self ): + if self.success is not True: return + for blackbox in self.blackboxes: + self._loadDesign( blackbox ) + + def _doFlattens ( self ): + if self.success is not True: return + flattens = ' '.join( self.flattens ) + self._run_pass( 'flatten {}\n'.format( flattens )) + self._run_pass( 'hierarchy -top {}\n'.format( self.top )) + + def doTask ( self ): + from helpers.io import ErrorMessage + if self.liberty is None: + e = ErrorMessage( 1, [ 'Yosys.doTask(): "liberty" has not been set' ] ) + return TaskFailed( e ) + if not self.liberty.is_file(): + e = ErrorMessage( 1, [ 'Yosys.doTask(): File not found "{}"' + , '"{}"'.format( self.liberty.as_posix() ) ] ) + return TaskFailed( e ) + #print( 'Yosys.doTask() on "{}"'.format( self.design )) + self.tool = yosys.Design() + self._loadBlackboxes() + self._loadDesign( self.main ) + self._run_pass( 'hierarchy -check -top {}'.format( self.top )) + self._run_pass( 'synth -top {}'.format( self.top )) + self._doFlattens() + self._run_pass( 'memory' ) + self._run_pass( 'dfflibmap -liberty {}'.format( self.liberty.as_posix() )) + self._run_pass( 'abc -liberty {}'.format( self.liberty.as_posix() )) + self._run_pass( 'clean' ) + self._run_pass( 'write_blif {}'.format( self.targets[0] )) + return self.success + + def create_doit_tasks ( self ): + return { 'basename' : self.basename + , 'actions' : [ self.doTask ] + , 'doc' : 'Run {}.'.format( self ) + , 'file_dep' : self.file_dep + , 'targets' : self.targets + } diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt index 5a63bea3..7b6032e3 100644 --- a/documentation/CMakeLists.txt +++ b/documentation/CMakeLists.txt @@ -54,6 +54,10 @@ content/pages/users-guide/ViewerTools.rst content/pages/users-guide/ScriptsPlugins.rst ) + set ( designFlowRst content/pages/design-flow/DesignFlow_HTML.rst + content/pages/design-flow/DesignFlow.rst + content/pages/design-flow/QuickStart.rst ) + set ( rdsRst content/pages/rds/RDS_HTML.rst content/pages/rds/RDSpage.rst ) @@ -78,6 +82,7 @@ ${usersGuideRst} ${pythonTutorialRst} ${pythonCppRst} + ${designFlowRst} ${rdsRst} ${stratusRst} ) diff --git a/documentation/content/pages/design-flow/DesignFlow.rst b/documentation/content/pages/design-flow/DesignFlow.rst new file mode 100644 index 00000000..6cefa3fa --- /dev/null +++ b/documentation/content/pages/design-flow/DesignFlow.rst @@ -0,0 +1,17 @@ +.. -*- Mode: rst -*- + +.. include:: ../../../etc/definitions.rst + + +======================= +Design Flow Quick Start +======================= + +|pagestylefancy| + + +.. contents:: + +|newpage| + +.. include:: QuickStart.rst diff --git a/documentation/content/pages/design-flow/DesignFlow_HTML.rst b/documentation/content/pages/design-flow/DesignFlow_HTML.rst new file mode 100644 index 00000000..7ff09b21 --- /dev/null +++ b/documentation/content/pages/design-flow/DesignFlow_HTML.rst @@ -0,0 +1,37 @@ +.. -*- Mode: rst -*- + + +=========== +Design Flow +=========== + + +:slug: design-flow +:date: 2022-12-31 01:00 +:Authors: Jean-Paul Chaput +:Contact: +:Version: December 31, 2022 (jpc) +:status: hidden + + +.. role:: raw-html(raw) + :format: html + +.. URLs that changes between the various backends. +.. _Coriolis Tools Documentation: ../documentation.rst +.. _Here: {filename}/pdfs/DesignFlow.pdf +.. _DesignFlow: {filename}/pdfs/DesignFlow.pdf + + +Printable Version of this Document +================================== + +`DesignFlow`_. + + +.. contents:: + :depth: 2 + + +.. include:: ../../../etc/definitions.rst +.. include:: QuickStart.rst diff --git a/documentation/content/pages/design-flow/QuickStart.rst b/documentation/content/pages/design-flow/QuickStart.rst new file mode 100644 index 00000000..2c741d62 --- /dev/null +++ b/documentation/content/pages/design-flow/QuickStart.rst @@ -0,0 +1,312 @@ +.. -*- Mode: rst -*- + + +1. Introduction +=============== + +The goal of the DesignFlow Python tool is to provide a replacement for +Makefiles, especially the complex system that has been developped for +alliance-check-toolkit. It is build upon |DoIt| (DoIt_). + + +1.1 Task vs. Rules +~~~~~~~~~~~~~~~~~~ + +Both as a tribute to |Makefile|, to avoid ambiguties with |DoIt| and to remember +that they are task *generators*, the classes defined to create tasks for the design +flow are called ``rules``. + + + +1.2 A Warning About Determinism +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There is a very important execution difference from a |Makefile|. In a +|Makefile| each rule command is executed in a a separated process, so +information is effectively passed through files which are written then read +from disk. But in |DoIt| we are running inside *one* |Python| process, so while +using Coriolis and the |Hurricane| database, all informations stays *in +memory*. Files are driven, but *not re-read* as the database will use the datas +already present in memory. + +This is not whitout consequences about determism. Let's look at two different +scenarii. + +1. We run straight from the RTL to the layout, using the rule/task sequence: :: + + Yosys => design.blif => blif2vst => design.vst => PnR => design.gds + + In this case, while ``design.vst`` is written on disk, the ``PnR`` stage + will not re-read the ``vst`` file and directly access the data in memory. + +2. Run in two separated steps, first we create the ``vst`` file: :: + + Yosys => design.blif => blif2vst => design.vst + + Then, we perform the ``PnR``: :: + + design.vst => PnR => design.gds + + In this case, as the |DoIt| processess has been restarted between the two + tasks, the ``PnR`` stage *will* read the ``vst`` file. + +The determism in |Coriolis| is ensured through the unique identifiers of the +objects, attributed in creation order. So between thoses two scenarii, the +identifiers will change and so the algorithm results. The differences should +be minor as the identifiers are used as a *last ditch* test to sort between +two objects which cost functions are exactly equal, nevertheless, it will +occur. + +.. note:: |Coriolis| is deterministic, meaning that each scenario will always + give the same result. The difference is truly *between* scenarii. + + +2. Using The Design Flow +======================== + + +2.1 Locating the Various Parts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One of the most tricky part of setting up the design flow is to locate where +the various components are. The script needs to be able to find: + +1. Coriolis, binaries & libraries. This depends widely of your kind of + installation and system. The helper script ``crlenv.py`` supplied + both in |alliance-check-toolkit| and |Coriolis| may help you there. + It looks in all the standard locations (that it is aware of) to try + to find it. + + .. note:: Usually, |Alliance| is installed in the same tree as + |Coriolis|, so it's setup can be deduced from it. + +2. The configurations files for the technology to be used. Here again, + the ``designflow.technos`` module provides you with a set of + pre-defined configurations for open sources technologie shipped + with |Coriolis|. For unsupported ones, you may write your own, + it should perform the whole initialization of the |Coriolis| and + |Hurricane| database. + +3. Optionnaly the |alliance-check-toolkit|. + + +2.2 Basic Example of |dodo| File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example can be found in |alliance-check-toolkit|, under ``benchs/arlet6502/sky130_c4m``. + +.. code:: Python + + from designflow.technos import setupSky130_c4m + + setupSky130_c4m( checkToolkit='../../..' + , pdkMasterTop='../../../pdkmaster/C4M.Sky130' ) + + DOIT_CONFIG = { 'verbosity' : 2 } + + from designflow.pnr import PnR + from designflow.yosys import Yosys + from designflow.blif2vst import Blif2Vst + from designflow.alias import Alias + from designflow.clean import Clean + PnR.textMode = True + + from doDesign import scriptMain + + ruleYosys = Yosys .mkRule( 'yosys', 'Arlet6502.v' ) + ruleB2V = Blif2Vst.mkRule( 'b2v' , [ 'arlet6502.vst' + , 'Arlet6502.spi' ] + , [ruleYosys] + , flags=0 ) + rulePnR = PnR .mkRule( 'pnr' , [ 'arlet6502_cts_r.gds' + , 'arlet6502_cts_r.spi' + , 'arlet6502_cts_r.vst' ] + , [ruleB2V] + , scriptMain ) + ruleCgt = PnR .mkRule( 'cgt' ) + ruleGds = Alias .mkRule( 'gds', [rulePnR] ) + ruleClean = Clean .mkRule() + + +You can run it with: + +.. code:: bash + + ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit list + b2v Run . + cgt Run plain CGT (no loaded design) + clean_flow Clean all generated (targets) files. + gds Run . + pnr Run . + yosys Run . + ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit pnr + ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit clean_flow + + +Let's have a detailed look on the various parts of the script. + +A. **Choosing the technology** Here, we load the predefined configuration for + SkyWater 130nm. We also have to give the location of the + |alliance-check-toolkit|, it may be relative or absolute. + + If you want to use another one, it up to you to configure |Coriolis| at + this point by any means you see fit. + + .. code:: Python + + from designflow.technos import setupSky130_c4m + + setupSky130_c4m( checkToolkit='../../..' + , pdkMasterTop='../../../pdkmaster/C4M.Sky130' ) + + +B. **Loading the various task/rule generators that we will use**, from the + ``designflow`` namespace. The rules are named from the tool they + encapsulate. + + .. code:: Python + + from designflow.pnr import PnR + from designflow.yosys import Yosys + from designflow.blif2vst import Blif2Vst + from designflow.alias import Alias + from designflow.clean import Clean + PnR.textMode = True + + +C. **Creating the rule set.** Each rule generator as a static method ``mkRule()`` + to create a new task. The three first parameters are always: + + 1. The name of the task (the ``basename`` for |DoIt|). + + 2. A target or list of targets, must be files or ``pathlib.Path`` objects. + + 3. A dependency or list of dependencies, they can be files, ``pathlib.Path`` + objects, or other tasks. We can see that the ``Blif2Vst`` rule uses + directly the ``Yosys`` one (the input file will be the *first* target + of the ``Yosys`` rule). + + 4. Any extra parameters. A set of flag for ``Blif2Vst``. The ``PnR`` rule takes + an optional callable argument, *any* callable. In this case we import the + ``scriptMain()`` function from ``doDesign()``. + + There are two more special rules: + + * ``Alias``, to rename a rule. It this case ``gds`` is defined as an alias to + ``PnR`` (because it generate the |gds| file). + + * ``Clean`` to create a rule that will remove all the generated targets. + + .. note:: The clean rule is named ``clean_flow`` because |DoIt| already have + a ``clean`` arguments which would shadow it. + + + .. code:: Python + + PnR.textMode = True + + from doDesign import scriptMain + + ruleYosys = Yosys .mkRule( 'yosys', 'Arlet6502.v' ) + ruleB2V = Blif2Vst.mkRule( 'b2v' , [ 'arlet6502.vst' + , 'Arlet6502.spi' ] + , [ruleYosys] + , flags=0 ) + rulePnR = PnR .mkRule( 'pnr' , [ 'arlet6502_cts_r.gds' + , 'arlet6502_cts_r.spi' + , 'arlet6502_cts_r.vst' ] + , [ruleB2V] + , scriptMain ) + ruleCgt = PnR .mkRule( 'cgt' ) + ruleGds = Alias .mkRule( 'gds', [rulePnR] ) + ruleClean = Clean .mkRule() + + +3. Rules's Catalog +================== + +3.1 Alliance Legacy Tools +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for the |Alliance| legacy tools. They are run through sub-processes. +For more detailed documentation about those tools, refer to their |man| pages. + +#. ``Asimut``, |VHDL| simulator. + +#. ``Boog``, logical synthesys. Map a |VHDL| behavioral description to a standard + cell library (works with ``boom`` & ``loon``). + +#. ``Boom``, behavioral description optimizer (works with ``boog`` & ``loon``). + +#. ``Cougar``, symbolic layout extractor. + +#. ``Dreal``, real layout (|GDS|, |CIF|) editor. + +#. ``Druc``, symbolic layout |DRC|. + +#. ``Flatph``, flatten a layout, fully or in part. + +#. ``Genpat``, pattern generator (for use with ``Asimut``). + +#. ``Graal``, symbolic layout editor. + +#. ``Loon``, netlist optimizer for surface and/or delay (works with ``boom`` & ``boog``). + +#. ``Lvx``, netlist comparator (*Layout* *Versus* *Extracted*). + +#. ``S2R``, symbolic to real translator (to |GDS| or |CIF|). + +#. ``Vasy``, Alliance |VHDL| subset translator towards standard |VHDL| or |Verilog|. + + +3.2 Current Tools +~~~~~~~~~~~~~~~~~ + +#. ``Blif2Vst``, translate a |blif| netlist (|Yosys| output) into the |Alliance| + netlist format |vst|. This is a |Python| script calling |Coriolis| directly + integrated inside the task. + +#. ``PnR``, maybe a bit of a misnomer. This is a caller to function that the user have + to write to perform the P&R as he sees fit for it's particular design. + +#. ``Yosys``, call the |Yosys| logical synthesyser. Provide an off the shelf subset + of functionalities to perform classic use cases. + + +3.3 Utility Rules +~~~~~~~~~~~~~~~~~ + +#. ``Alias``, create a name alias for a rule. + +#. ``Clean``, remove all the generated targets of all the rules. The name of the + rule is ``clean_flow` to not interfer with the |DoIt| clean arguments. + Files not part of any rules targets can be added to be removeds. Then, + to actually remove them, add the ``--extras`` flag to the command line. + + .. code:: bash + + ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit clean_flow --extras + + +#. ``Copy``, copy a file into the current directory. + + +3.4 Rule Sets +~~~~~~~~~~~~~ + +For commonly used sequences of rules, some predefined sets are defined. + +#. ``alliancesynth``, to apply the logical |Alliance| logical synthesis + set of tools. From |VHDL| to optimized |vst|. The set is as follow: :: + + x.vbe => boom => x_boom.vbe => boog => x_boog.vst => loon => x.vst + + An additional rule using ``vasy`` is triggered if the input format + is standard |VHDL|. + +#. ``pnrcheck``, complete flow from |Verilog| to symbolic layout, with + |DRC| and |LVX| checks. Uses |Yosys| for synthesis. + +#. ``routecheck``, perform the routing, the |DRC| and |LVX| check on an + already placed design. Use symbolic layout. + diff --git a/documentation/content/pages/documentation.rst b/documentation/content/pages/documentation.rst index 16fb4563..e43cddbe 100644 --- a/documentation/content/pages/documentation.rst +++ b/documentation/content/pages/documentation.rst @@ -51,6 +51,9 @@ Coriolis Documentation `Coriolis User's Guide <{filename}/pages/users-guide/UsersGuide_HTML.rst>`_ |br| Using the software + `Design Flow Quick Start <{filename}/pages/design-flow/DesignFlow_HTML.rst>`_ |br| + Tool to build your design + `Python Tutorial <{filename}/pages/python-tutorial/PythonTutorial_HTML.rst>`_ |br| A Tutorial to use Coriolis through Python diff --git a/documentation/content/pages/python-cpp-new/Implementation.rst b/documentation/content/pages/python-cpp-new/Implementation.rst index e76319fa..2a91e9a0 100644 --- a/documentation/content/pages/python-cpp-new/Implementation.rst +++ b/documentation/content/pages/python-cpp-new/Implementation.rst @@ -4,7 +4,7 @@ 2. Implementation ================= -We do not try to provides an iterface as sleek as ``pybind11`` that completely +We do not try to provides an interface as sleek as ``pybind11`` that completely hides the Python/C API. Instead we keep mostly visible the classic structure of the Python/C API but we provides templates to automate as much as possible the boring tasks (and code duplication). This way, if we need a very specific diff --git a/documentation/content/pdfs/CheckToolkit.pdf b/documentation/content/pdfs/CheckToolkit.pdf index f57c0cbc..afe45d88 100644 Binary files a/documentation/content/pdfs/CheckToolkit.pdf and b/documentation/content/pdfs/CheckToolkit.pdf differ diff --git a/documentation/content/pdfs/DesignFlow.pdf b/documentation/content/pdfs/DesignFlow.pdf new file mode 100644 index 00000000..e951d2c8 Binary files /dev/null and b/documentation/content/pdfs/DesignFlow.pdf differ diff --git a/documentation/content/pdfs/PythonCpp.pdf b/documentation/content/pdfs/PythonCpp.pdf index bd6c3f1f..04fdf4e8 100644 Binary files a/documentation/content/pdfs/PythonCpp.pdf and b/documentation/content/pdfs/PythonCpp.pdf differ diff --git a/documentation/content/pdfs/PythonTutorial.pdf b/documentation/content/pdfs/PythonTutorial.pdf index 7c3945a0..e0c5aeb8 100644 Binary files a/documentation/content/pdfs/PythonTutorial.pdf and b/documentation/content/pdfs/PythonTutorial.pdf differ diff --git a/documentation/content/pdfs/RDS.pdf b/documentation/content/pdfs/RDS.pdf index a07fe291..929deaab 100644 Binary files a/documentation/content/pdfs/RDS.pdf and b/documentation/content/pdfs/RDS.pdf differ diff --git a/documentation/content/pdfs/Stratus.pdf b/documentation/content/pdfs/Stratus.pdf index a6cbdbac..1a4a52c9 100644 Binary files a/documentation/content/pdfs/Stratus.pdf and b/documentation/content/pdfs/Stratus.pdf differ diff --git a/documentation/content/pdfs/UsersGuide.pdf b/documentation/content/pdfs/UsersGuide.pdf index f4be5a91..981c3634 100644 Binary files a/documentation/content/pdfs/UsersGuide.pdf and b/documentation/content/pdfs/UsersGuide.pdf differ diff --git a/documentation/etc/definitions.rst b/documentation/etc/definitions.rst index 21b9694a..898592b5 100644 --- a/documentation/etc/definitions.rst +++ b/documentation/etc/definitions.rst @@ -60,7 +60,6 @@ .. |Debian| replace:: :sc:`Debian` .. |Ubuntu| replace:: :sc:`Ubuntu` .. |MOSIS| replace:: :sc:`mosis` -.. |Blif| replace:: :sc:`blif` .. |TSMC| replace:: :sc:`tsmc` .. |AMS| replace:: :sc:`ams` .. |XFAB| replace:: :sc:`xfab` @@ -139,6 +138,7 @@ .. |pdf| replace:: :sc:`pdf` .. |UTF-8| replace:: :sc:`utf-8` .. |Python| replace:: :sc:`Python` +.. |DoIt| replace:: :sc:`DoIt` .. |TCL| replace:: :sc:`tcl` .. |Linux| replace:: :sc:`Linux` .. |MacPorts| replace:: :sc:`MacPorts` @@ -163,6 +163,8 @@ .. |flex| replace:: :cb:`flex` .. |struct| replace:: :cb:`struct` .. |Makefile| replace:: :cb:`Makefile` +.. |dodo| replace:: :cb:`dodo` +.. |man| replace:: :cb:`man` .. |KeyUp| replace:: :fboxtt:`Up` @@ -210,6 +212,7 @@ .. _Yosys: http://www.clifford.at/yosys/ .. _GHDL: https://github.com/ghdl/ghdl .. _Si2: http://www.si2.org/ +.. _DoIt: https://pydoit.org/contents.html .. _GPL: http://www.gnu.org/copyleft/gpl.txt .. _LGPL: https://www.gnu.org/licenses/lgpl-3.0.html @@ -226,10 +229,14 @@ .. |layout| replace:: *layout* .. |layouts| replace:: *layouts* .. |VLSI| replace:: :sc:`vlsi` +.. |DRC| replace:: :sc:`drc` +.. |LVX| replace:: :sc:`lvx` +.. |CIF| replace:: :sc:`cif` .. |GDS| replace:: :sc:`gds` .. |GDSII| replace:: :sc:`gdsii` .. |CMOS| replace:: :sc:`cmos` .. |VHDL| replace:: :sc:`vhdl` +.. |Verilog| replace:: :sc:`Verilog` .. |NWELL| replace:: :sc:`nwell` .. |POWER| replace:: :sc:`power` .. |GROUND| replace:: :sc:`ground` @@ -251,5 +258,6 @@ .. |Collections| replace:: *Collections* .. |ap| replace:: :cb:`ap` .. |vst| replace:: :cb:`vst` +.. |blif| replace:: :cb:`blif` .. |kgr| replace:: :cb:`kgr` .. |dot_conf| replace:: :cb:`.conf` diff --git a/documentation/output/archives.html b/documentation/output/archives.html index cfb807f4..70f6048c 100644 --- a/documentation/output/archives.html +++ b/documentation/output/archives.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/authors.html b/documentation/output/authors.html index 048f27f9..c83a2cd9 100644 --- a/documentation/output/authors.html +++ b/documentation/output/authors.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/categories.html b/documentation/output/categories.html index 048f27f9..c83a2cd9 100644 --- a/documentation/output/categories.html +++ b/documentation/output/categories.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/index.html b/documentation/output/index.html index e1a1817d..5281074b 100644 --- a/documentation/output/index.html +++ b/documentation/output/index.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/.html b/documentation/output/pages/.html new file mode 100644 index 00000000..8adec7f6 --- /dev/null +++ b/documentation/output/pages/.html @@ -0,0 +1,559 @@ + + + + + + + + + + + - Coriolis VLSI CAD Tools [offline] + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + --> + + + + + + +
+
+
+
+ LIP6 + Sorbonne Universite + CNRS +

+
+
+
+
+
+ + +
+ + + + +
+ +
+

1. Introduction

+

The goal of the DesignFlow Python tool is to provide a replacement for +Makefiles, especially the complex system that has been developped for +alliance-check-toolkit. It is build upon |DoIt| (DoIt_).

+
+

1.1 Task vs. Rules

+

Both as a tribute to |Makefile|, to avoid ambiguties with |DoIt| and to remember +that they are task generators, the classes defined to create tasks for the design +flow are called rules.

+
+
+

1.2 A Warning About Determinism

+

There is a very important execution difference from a |Makefile|. In a +|Makefile| each rule command is executed in a a separated process, so +information is effectively passed through files which are written then read +from disk. But in |DoIt| we are running inside one |Python| process, so while +using Coriolis and the |Hurricane| database, all informations stays in +memory. Files are driven, but not re-read as the database will use the datas +already present in memory.

+

This is not whitout consequences about determism. Let's look at two different +scenarii.

+
    +
  1. We run straight from the RTL to the layout, using the rule/task sequence:

    +
    +Yosys => design.blif => blif2vst => design.vst => PnR => design.gds
    +
    +

    In this case, while design.vst is written on disk, the PnR stage +will not re-read the vst file and directly access the data in memory.

    +
  2. +
  3. Run in two separated steps, first we create the vst file:

    +
    +Yosys => design.blif => blif2vst => design.vst
    +
    +

    Then, we perform the PnR:

    +
    +design.vst => PnR => design.gds
    +
    +

    In this case, as the |DoIt| processess has been restarted between the two +tasks, the PnR stage will read the vst file.

    +
  4. +
+

The determism in |Coriolis| is ensured through the unique identifiers of the +objects, attributed in creation order. So between thoses two scenarii, the +identifiers will change and so the algorithm results. The differences should +be minor as the identifiers are used as a last ditch test to sort between +two objects which cost functions are exactly equal, nevertheless, it will +occur.

+
+

Note

+

|Coriolis| is deterministic, meaning that each scenario will always +give the same result. The difference is truly between scenarii.

+
+
+
+
+

2. Using The Design Flow

+
+

2.1 Locating the Various Parts

+

One of the most tricky part of setting up the design flow is to locate where +the various components are. The script needs to be able to find:

+
    +
  1. Coriolis, binaries & libraries. This depends widely of your kind of +installation and system. The helper script crlenv.py supplied +both in |alliance-check-toolkit| and |Coriolis| may help you there. +It looks in all the standard locations (that it is aware of) to try +to find it.

    +
    +

    Note

    +

    Usually, |Alliance| is installed in the same tree as +|Coriolis|, so it's setup can be deduced from it.

    +
    +
  2. +
  3. The configurations files for the technology to be used. Here again, +the designflow.technos module provides you with a set of +pre-defined configurations for open sources technologie shipped +with |Coriolis|. For unsupported ones, you may write your own, +it should perform the whole initialization of the |Coriolis| and +|Hurricane| database.

    +
  4. +
  5. Optionnaly the |alliance-check-toolkit|.

    +
  6. +
+
+
+

2.2 Basic Example of |dodo| File

+

This example can be found in |alliance-check-toolkit|, under benchs/arlet6502/sky130_c4m.

+
+from designflow.technos import setupSky130_c4m
+
+setupSky130_c4m( checkToolkit='../../..'
+               , pdkMasterTop='../../../pdkmaster/C4M.Sky130' )
+
+DOIT_CONFIG = { 'verbosity' : 2 }
+
+from designflow.pnr      import PnR
+from designflow.yosys    import Yosys
+from designflow.blif2vst import Blif2Vst
+from designflow.alias    import Alias
+from designflow.clean    import Clean
+PnR.textMode = True
+
+from doDesign import scriptMain
+
+ruleYosys = Yosys   .mkRule( 'yosys', 'Arlet6502.v' )
+ruleB2V   = Blif2Vst.mkRule( 'b2v'  , [ 'arlet6502.vst'
+                                      , 'Arlet6502.spi' ]
+                                    , [ruleYosys]
+                                    , flags=0 )
+rulePnR   = PnR     .mkRule( 'pnr'  , [ 'arlet6502_cts_r.gds'
+                                      , 'arlet6502_cts_r.spi'
+                                      , 'arlet6502_cts_r.vst' ]
+                                    , [ruleB2V]
+                                    , scriptMain )
+ruleCgt   = PnR     .mkRule( 'cgt' )
+ruleGds   = Alias   .mkRule( 'gds', [rulePnR] )
+ruleClean = Clean   .mkRule()
+
+

You can run it with:

+
+ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit list
+b2v          Run <blif2vst arlet6502 depends=[Arlet6502.blif]>.
+cgt          Run plain CGT (no loaded design)
+clean_flow   Clean all generated (targets) files.
+gds          Run <Alias "gds" for "pnr">.
+pnr          Run <pnr arlet6502_cts_r.gds depends=[arlet6502.vst,Arlet6502.spi]>.
+yosys        Run <yosys Arlet6502.v top=Arlet6502 blackboxes=[] flattens=[]>.
+ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit pnr
+ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit clean_flow
+
+

Let's have a detailed look on the various parts of the script.

+
    +
  1. Choosing the technology Here, we load the predefined configuration for +SkyWater 130nm. We also have to give the location of the +|alliance-check-toolkit|, it may be relative or absolute.

    +

    If you want to use another one, it up to you to configure |Coriolis| at +this point by any means you see fit.

    +
    +from designflow.technos import setupSky130_c4m
    +
    +setupSky130_c4m( checkToolkit='../../..'
    +               , pdkMasterTop='../../../pdkmaster/C4M.Sky130' )
    +
    +
  2. +
  3. Loading the various task/rule generators that we will use, from the +designflow namespace. The rules are named from the tool they +encapsulate.

    +
    +from designflow.pnr      import PnR
    +from designflow.yosys    import Yosys
    +from designflow.blif2vst import Blif2Vst
    +from designflow.alias    import Alias
    +from designflow.clean    import Clean
    +PnR.textMode = True
    +
    +
  4. +
  5. Creating the rule set. Each rule generator as a static method mkRule() +to create a new task. The three first parameters are always:

    +
      +
    1. The name of the task (the basename for |DoIt|).
    2. +
    3. A target or list of targets, must be files or pathlib.Path objects.
    4. +
    5. A dependency or list of dependencies, they can be files, pathlib.Path +objects, or other tasks. We can see that the Blif2Vst rule uses +directly the Yosys one (the input file will be the first target +of the Yosys rule).
    6. +
    7. Any extra parameters. A set of flag for Blif2Vst. The PnR rule takes +an optional callable argument, any callable. In this case we import the +scriptMain() function from doDesign().
    8. +
    +

    There are two more special rules:

    +
      +
    • Alias, to rename a rule. It this case gds is defined as an alias to +PnR (because it generate the |gds| file).

      +
    • +
    • Clean to create a rule that will remove all the generated targets.

      +
      +

      Note

      +

      The clean rule is named clean_flow because |DoIt| already have +a clean arguments which would shadow it.

      +
      +
    • +
    +
    +PnR.textMode = True
    +
    +from doDesign import scriptMain
    +
    +ruleYosys = Yosys   .mkRule( 'yosys', 'Arlet6502.v' )
    +ruleB2V   = Blif2Vst.mkRule( 'b2v'  , [ 'arlet6502.vst'
    +                                      , 'Arlet6502.spi' ]
    +                                    , [ruleYosys]
    +                                    , flags=0 )
    +rulePnR   = PnR     .mkRule( 'pnr'  , [ 'arlet6502_cts_r.gds'
    +                                      , 'arlet6502_cts_r.spi'
    +                                      , 'arlet6502_cts_r.vst' ]
    +                                    , [ruleB2V]
    +                                    , scriptMain )
    +ruleCgt   = PnR     .mkRule( 'cgt' )
    +ruleGds   = Alias   .mkRule( 'gds', [rulePnR] )
    +ruleClean = Clean   .mkRule()
    +
    +
  6. +
+
+
+
+

3. Rules's Catalog

+
+

3.1 Alliance Legacy Tools

+

Support for the |Alliance| legacy tools. They are run through sub-processes. +For more detailed documentation about those tools, refer to their |man| pages.

+
    +
  1. Asimut, |VHDL| simulator.
  2. +
  3. Boog, logical synthesys. Map a |VHDL| behavioral description to a standard +cell library (works with boom & loon).
  4. +
  5. Boom, behavioral description optimizer (works with boog & loon).
  6. +
  7. Cougar, symbolic layout extractor.
  8. +
  9. Dreal, real layout (|GDS|, |CIF|) editor.
  10. +
  11. Druc, symbolic layout |DRC|.
  12. +
  13. Flatph, flatten a layout, fully or in part.
  14. +
  15. Genpat, pattern generator (for use with Asimut).
  16. +
  17. Graal, symbolic layout editor.
  18. +
  19. Loon, netlist optimizer for surface and/or delay (works with boom & boog).
  20. +
  21. Lvx, netlist comparator (Layout Versus Extracted).
  22. +
  23. S2R, symbolic to real translator (to |GDS| or |CIF|).
  24. +
  25. Vasy, Alliance |VHDL| subset translator towards standard |VHDL| or |Verilog|.
  26. +
+
+
+

3.2 Current Tools

+
    +
  1. Blif2Vst, translate a |blif| netlist (|Yosys| output) into the |Alliance| +netlist format |vst|. This is a |Python| script calling |Coriolis| directly +integrated inside the task.
  2. +
  3. PnR, maybe a bit of a misnomer. This is a caller to function that the user have +to write to perform the P&R as he sees fit for it's particular design.
  4. +
  5. Yosys, call the |Yosys| logical synthesyser. Provide an off the shelf subset +of functionalities to perform classic use cases.
  6. +
+
+
+

3.3 Utility Rules

+
    +
  1. Alias, create a name alias for a rule.

    +
  2. +
  3. Clean, remove all the generated targets of all the rules. The name of the +rule is clean_flow` to not interfer with the |DoIt| clean arguments. +Files not part of any rules targets can be added to be removeds. Then, +to actually remove them, add the ``--extras flag to the command line.

    +
    +ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit clean_flow --extras
    +
    +
  4. +
  5. Copy, copy a file into the current directory.

    +
  6. +
+
+
+

3.4 Rule Sets

+
    +
  1. alliancesynth
  2. +
  3. pnrcheck
  4. +
  5. routecheck
  6. +
+
+
+
+

Docutils System Messages

+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 7); backlink

+Undefined substitution referenced: "DoIt".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 15); backlink

+Undefined substitution referenced: "Makefile".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 15); backlink

+Undefined substitution referenced: "DoIt".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 24); backlink

+Undefined substitution referenced: "Makefile".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 24); backlink

+Undefined substitution referenced: "Makefile".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 24); backlink

+Undefined substitution referenced: "DoIt".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 24); backlink

+Undefined substitution referenced: "Python".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 24); backlink

+Undefined substitution referenced: "Hurricane".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 50); backlink

+Undefined substitution referenced: "DoIt".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 53); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 60); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 74); backlink

+Undefined substitution referenced: "alliance-check-toolkit".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 74); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 80); backlink

+Undefined substitution referenced: "Alliance".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 80); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 83); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 83); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 83); backlink

+Undefined substitution referenced: "Hurricane".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 90); backlink

+Undefined substitution referenced: "alliance-check-toolkit".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 94); backlink

+Undefined substitution referenced: "dodo".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 96); backlink

+Undefined substitution referenced: "alliance-check-toolkit".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 148); backlink

+Undefined substitution referenced: "alliance-check-toolkit".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 152); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 180); backlink

+Undefined substitution referenced: "DoIt".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 195); backlink

+Undefined substitution referenced: "gds".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 200); backlink

+Undefined substitution referenced: "DoIt".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 231); backlink

+Undefined substitution referenced: "Alliance".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 231); backlink

+Undefined substitution referenced: "man".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 234); backlink

+Undefined substitution referenced: "VHDL".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 236); backlink

+Undefined substitution referenced: "VHDL".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 243); backlink

+Undefined substitution referenced: "GDS".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 243); backlink

+Undefined substitution referenced: "CIF".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 245); backlink

+Undefined substitution referenced: "DRC".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 257); backlink

+Undefined substitution referenced: "GDS".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 257); backlink

+Undefined substitution referenced: "CIF".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 259); backlink

+Undefined substitution referenced: "VHDL".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 259); backlink

+Undefined substitution referenced: "VHDL".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 259); backlink

+Undefined substitution referenced: "Verilog".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 265); backlink

+Undefined substitution referenced: "blif".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 265); backlink

+Undefined substitution referenced: "Yosys".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 265); backlink

+Undefined substitution referenced: "Alliance".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 265); backlink

+Undefined substitution referenced: "vst".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 265); backlink

+Undefined substitution referenced: "Python".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 265); backlink

+Undefined substitution referenced: "Coriolis".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 272); backlink

+Undefined substitution referenced: "Yosys".
+
+

System Message: ERROR/3 (/dsk/l1/jpc/coriolis-2.x/src/coriolis/documentation/content/pages/design-flow/QuickStart.rst, line 7); backlink

+Unknown target name: "doit".
+
+ +
+ + + + + + + \ No newline at end of file diff --git a/documentation/output/pages/alliance.html b/documentation/output/pages/alliance.html index 39b3fdf4..0e8ca244 100644 --- a/documentation/output/pages/alliance.html +++ b/documentation/output/pages/alliance.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/check-toolkit.html b/documentation/output/pages/check-toolkit.html index 2e4eff63..298a2612 100644 --- a/documentation/output/pages/check-toolkit.html +++ b/documentation/output/pages/check-toolkit.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics @@ -143,6 +145,15 @@ +
+

System Message: ERROR/3 (content/pages/check-toolkit/README.rst, line 4)

+Duplicate substitution definition name: "Verilog".
+
+

System Message: ERROR/3 (content/pages/check-toolkit/README.rst, line 9)

+Duplicate substitution definition name: "DRC".
+
+

System Message: ERROR/3 (content/pages/check-toolkit/README.rst, line 78)

+Duplicate substitution definition name: "blif".

Toolkit Purpose

diff --git a/documentation/output/pages/design-flow.html b/documentation/output/pages/design-flow.html new file mode 100644 index 00000000..664c1c4e --- /dev/null +++ b/documentation/output/pages/design-flow.html @@ -0,0 +1,454 @@ + + + + + + + + + + + Design Flow - Coriolis VLSI CAD Tools [offline] + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + --> + + + + + + +
+
+
+
+ LIP6 + Sorbonne Universite + CNRS +

Design Flow

+
+
+
+
+
+ + +
+ + + + +
+ + + +
+

1. Introduction

+

The goal of the DesignFlow Python tool is to provide a replacement for +Makefiles, especially the complex system that has been developped for +alliance-check-toolkit. It is build upon DoIt (DoIt).

+
+

1.1 Task vs. Rules

+

Both as a tribute to Makefile, to avoid ambiguties with DoIt and to remember +that they are task generators, the classes defined to create tasks for the design +flow are called rules.

+
+
+

1.2 A Warning About Determinism

+

There is a very important execution difference from a Makefile. In a +Makefile each rule command is executed in a a separated process, so +information is effectively passed through files which are written then read +from disk. But in DoIt we are running inside one Python process, so while +using Coriolis and the Hurricane database, all informations stays in +memory. Files are driven, but not re-read as the database will use the datas +already present in memory.

+

This is not whitout consequences about determism. Let's look at two different +scenarii.

+
    +
  1. We run straight from the RTL to the layout, using the rule/task sequence:

    +
    +Yosys => design.blif => blif2vst => design.vst => PnR => design.gds
    +
    +

    In this case, while design.vst is written on disk, the PnR stage +will not re-read the vst file and directly access the data in memory.

    +
  2. +
  3. Run in two separated steps, first we create the vst file:

    +
    +Yosys => design.blif => blif2vst => design.vst
    +
    +

    Then, we perform the PnR:

    +
    +design.vst => PnR => design.gds
    +
    +

    In this case, as the DoIt processess has been restarted between the two +tasks, the PnR stage will read the vst file.

    +
  4. +
+

The determism in Coriolis is ensured through the unique identifiers of the +objects, attributed in creation order. So between thoses two scenarii, the +identifiers will change and so the algorithm results. The differences should +be minor as the identifiers are used as a last ditch test to sort between +two objects which cost functions are exactly equal, nevertheless, it will +occur.

+
+

Note

+

Coriolis is deterministic, meaning that each scenario will always +give the same result. The difference is truly between scenarii.

+
+
+
+
+

2. Using The Design Flow

+
+

2.1 Locating the Various Parts

+

One of the most tricky part of setting up the design flow is to locate where +the various components are. The script needs to be able to find:

+
    +
  1. Coriolis, binaries & libraries. This depends widely of your kind of +installation and system. The helper script crlenv.py supplied +both in alliance-check-toolkit and Coriolis may help you there. +It looks in all the standard locations (that it is aware of) to try +to find it.

    +
    +

    Note

    +

    Usually, Alliance is installed in the same tree as +Coriolis, so it's setup can be deduced from it.

    +
    +
  2. +
  3. The configurations files for the technology to be used. Here again, +the designflow.technos module provides you with a set of +pre-defined configurations for open sources technologie shipped +with Coriolis. For unsupported ones, you may write your own, +it should perform the whole initialization of the Coriolis and +Hurricane database.

    +
  4. +
  5. Optionnaly the alliance-check-toolkit.

    +
  6. +
+
+
+

2.2 Basic Example of dodo File

+

This example can be found in alliance-check-toolkit, under benchs/arlet6502/sky130_c4m.

+
+from designflow.technos import setupSky130_c4m
+
+setupSky130_c4m( checkToolkit='../../..'
+               , pdkMasterTop='../../../pdkmaster/C4M.Sky130' )
+
+DOIT_CONFIG = { 'verbosity' : 2 }
+
+from designflow.pnr      import PnR
+from designflow.yosys    import Yosys
+from designflow.blif2vst import Blif2Vst
+from designflow.alias    import Alias
+from designflow.clean    import Clean
+PnR.textMode = True
+
+from doDesign import scriptMain
+
+ruleYosys = Yosys   .mkRule( 'yosys', 'Arlet6502.v' )
+ruleB2V   = Blif2Vst.mkRule( 'b2v'  , [ 'arlet6502.vst'
+                                      , 'Arlet6502.spi' ]
+                                    , [ruleYosys]
+                                    , flags=0 )
+rulePnR   = PnR     .mkRule( 'pnr'  , [ 'arlet6502_cts_r.gds'
+                                      , 'arlet6502_cts_r.spi'
+                                      , 'arlet6502_cts_r.vst' ]
+                                    , [ruleB2V]
+                                    , scriptMain )
+ruleCgt   = PnR     .mkRule( 'cgt' )
+ruleGds   = Alias   .mkRule( 'gds', [rulePnR] )
+ruleClean = Clean   .mkRule()
+
+

You can run it with:

+
+ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit list
+b2v          Run <blif2vst arlet6502 depends=[Arlet6502.blif]>.
+cgt          Run plain CGT (no loaded design)
+clean_flow   Clean all generated (targets) files.
+gds          Run <Alias "gds" for "pnr">.
+pnr          Run <pnr arlet6502_cts_r.gds depends=[arlet6502.vst,Arlet6502.spi]>.
+yosys        Run <yosys Arlet6502.v top=Arlet6502 blackboxes=[] flattens=[]>.
+ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit pnr
+ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit clean_flow
+
+

Let's have a detailed look on the various parts of the script.

+
    +
  1. Choosing the technology Here, we load the predefined configuration for +SkyWater 130nm. We also have to give the location of the +alliance-check-toolkit, it may be relative or absolute.

    +

    If you want to use another one, it up to you to configure Coriolis at +this point by any means you see fit.

    +
    +from designflow.technos import setupSky130_c4m
    +
    +setupSky130_c4m( checkToolkit='../../..'
    +               , pdkMasterTop='../../../pdkmaster/C4M.Sky130' )
    +
    +
  2. +
  3. Loading the various task/rule generators that we will use, from the +designflow namespace. The rules are named from the tool they +encapsulate.

    +
    +from designflow.pnr      import PnR
    +from designflow.yosys    import Yosys
    +from designflow.blif2vst import Blif2Vst
    +from designflow.alias    import Alias
    +from designflow.clean    import Clean
    +PnR.textMode = True
    +
    +
  4. +
  5. Creating the rule set. Each rule generator as a static method mkRule() +to create a new task. The three first parameters are always:

    +
      +
    1. The name of the task (the basename for DoIt).
    2. +
    3. A target or list of targets, must be files or pathlib.Path objects.
    4. +
    5. A dependency or list of dependencies, they can be files, pathlib.Path +objects, or other tasks. We can see that the Blif2Vst rule uses +directly the Yosys one (the input file will be the first target +of the Yosys rule).
    6. +
    7. Any extra parameters. A set of flag for Blif2Vst. The PnR rule takes +an optional callable argument, any callable. In this case we import the +scriptMain() function from doDesign().
    8. +
    +

    There are two more special rules:

    +
      +
    • Alias, to rename a rule. It this case gds is defined as an alias to +PnR (because it generate the gds file).

      +
    • +
    • Clean to create a rule that will remove all the generated targets.

      +
      +

      Note

      +

      The clean rule is named clean_flow because DoIt already have +a clean arguments which would shadow it.

      +
      +
    • +
    +
    +PnR.textMode = True
    +
    +from doDesign import scriptMain
    +
    +ruleYosys = Yosys   .mkRule( 'yosys', 'Arlet6502.v' )
    +ruleB2V   = Blif2Vst.mkRule( 'b2v'  , [ 'arlet6502.vst'
    +                                      , 'Arlet6502.spi' ]
    +                                    , [ruleYosys]
    +                                    , flags=0 )
    +rulePnR   = PnR     .mkRule( 'pnr'  , [ 'arlet6502_cts_r.gds'
    +                                      , 'arlet6502_cts_r.spi'
    +                                      , 'arlet6502_cts_r.vst' ]
    +                                    , [ruleB2V]
    +                                    , scriptMain )
    +ruleCgt   = PnR     .mkRule( 'cgt' )
    +ruleGds   = Alias   .mkRule( 'gds', [rulePnR] )
    +ruleClean = Clean   .mkRule()
    +
    +
  6. +
+
+
+
+

3. Rules's Catalog

+
+

3.1 Alliance Legacy Tools

+

Support for the Alliance legacy tools. They are run through sub-processes. +For more detailed documentation about those tools, refer to their man pages.

+
    +
  1. Asimut, vhdl simulator.
  2. +
  3. Boog, logical synthesys. Map a vhdl behavioral description to a standard +cell library (works with boom & loon).
  4. +
  5. Boom, behavioral description optimizer (works with boog & loon).
  6. +
  7. Cougar, symbolic layout extractor.
  8. +
  9. Dreal, real layout (gds, cif) editor.
  10. +
  11. Druc, symbolic layout DRC.
  12. +
  13. Flatph, flatten a layout, fully or in part.
  14. +
  15. Genpat, pattern generator (for use with Asimut).
  16. +
  17. Graal, symbolic layout editor.
  18. +
  19. Loon, netlist optimizer for surface and/or delay (works with boom & boog).
  20. +
  21. Lvx, netlist comparator (Layout Versus Extracted).
  22. +
  23. S2R, symbolic to real translator (to gds or cif).
  24. +
  25. Vasy, Alliance vhdl subset translator towards standard vhdl or Verilog.
  26. +
+
+
+

3.2 Current Tools

+
    +
  1. Blif2Vst, translate a blif netlist (Yosys output) into the Alliance +netlist format vst. This is a Python script calling Coriolis directly +integrated inside the task.
  2. +
  3. PnR, maybe a bit of a misnomer. This is a caller to function that the user have +to write to perform the P&R as he sees fit for it's particular design.
  4. +
  5. Yosys, call the Yosys logical synthesyser. Provide an off the shelf subset +of functionalities to perform classic use cases.
  6. +
+
+
+

3.3 Utility Rules

+
    +
  1. Alias, create a name alias for a rule.

    +
  2. +
  3. Clean, remove all the generated targets of all the rules. The name of the +rule is clean_flow` to not interfer with the |DoIt| clean arguments. +Files not part of any rules targets can be added to be removeds. Then, +to actually remove them, add the ``--extras flag to the command line.

    +
    +ego@home:sky130_c4m> ../../../bin/crlenv.py -- doit clean_flow --extras
    +
    +
  4. +
  5. Copy, copy a file into the current directory.

    +
  6. +
+
+
+

3.4 Rule Sets

+
    +
  1. alliancesynth
  2. +
  3. pnrcheck
  4. +
  5. routecheck
  6. +
+
+
+ +
+ + + + + + + \ No newline at end of file diff --git a/documentation/output/pages/documentation.html b/documentation/output/pages/documentation.html index ab15ba17..862bd53a 100644 --- a/documentation/output/pages/documentation.html +++ b/documentation/output/pages/documentation.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics @@ -138,6 +140,8 @@ Regression tests & examples

Coriolis User's Guide
Using the software

+

Design Flow Quick Start
+Tool to build your design

Python Tutorial
A Tutorial to use Coriolis through Python

Python/C++ Tutorial
diff --git a/documentation/output/pages/gitlab.html b/documentation/output/pages/gitlab.html index c8095255..c9499115 100644 --- a/documentation/output/pages/gitlab.html +++ b/documentation/output/pages/gitlab.html @@ -54,6 +54,7 @@ Topics

@@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/homepage.html b/documentation/output/pages/homepage.html index 8badb6e9..8ff6833a 100644 --- a/documentation/output/pages/homepage.html +++ b/documentation/output/pages/homepage.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/python-cpp-new.html b/documentation/output/pages/python-cpp-new.html index f30ac0c9..cfbfb5e3 100644 --- a/documentation/output/pages/python-cpp-new.html +++ b/documentation/output/pages/python-cpp-new.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics @@ -213,7 +215,7 @@ and the double compilation pass.

2. Implementation

-

We do not try to provides an iterface as sleek as pybind11 that completely +

We do not try to provides an interface as sleek as pybind11 that completely hides the Python/C API. Instead we keep mostly visible the classic structure of the Python/C API but we provides templates to automate as much as possible the boring tasks (and code duplication). This way, if we need a very specific diff --git a/documentation/output/pages/python-cpp.html b/documentation/output/pages/python-cpp.html index 643916bb..72cfa6d3 100644 --- a/documentation/output/pages/python-cpp.html +++ b/documentation/output/pages/python-cpp.html @@ -54,6 +54,7 @@ Topics

@@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/python-tutorial.html b/documentation/output/pages/python-tutorial.html index d36d1eef..0fd3cea2 100644 --- a/documentation/output/pages/python-tutorial.html +++ b/documentation/output/pages/python-tutorial.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics @@ -1397,11 +1399,11 @@ for any discrepencies.

-

7.2 Loading a blif file -- Yosys

-

The blif format is generated by the Yosys logic synthetizer. Here again, it is +

7.2 Loading a blif file -- Yosys

+

The blif format is generated by the Yosys logic synthetizer. Here again, it is pretty straightforward: call the static function Blif.load(). If you made your synthesis on a cell library not managed by AllianceFramework, for example -the one of the FreePDK45, you must load it prior to calling the blif loader.

+the one of the FreePDK45, you must load it prior to calling the blif loader.

cell = Blif.load( 'snx' ) # load "snx.blif" in the working directory.
 
diff --git a/documentation/output/pages/rds.html b/documentation/output/pages/rds.html index 440a9151..9a25e85b 100644 --- a/documentation/output/pages/rds.html +++ b/documentation/output/pages/rds.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics @@ -146,6 +148,9 @@ +
+

System Message: ERROR/3 (content/pages/rds/RDSpage.rst, line 23)

+Duplicate substitution definition name: "DRC".

diff --git a/documentation/output/pages/stratus-developpers.html b/documentation/output/pages/stratus-developpers.html index 58b52f7f..a5267470 100644 --- a/documentation/output/pages/stratus-developpers.html +++ b/documentation/output/pages/stratus-developpers.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/stratus-dpgen.html b/documentation/output/pages/stratus-dpgen.html index e9c35bcd..f8e7dd46 100644 --- a/documentation/output/pages/stratus-dpgen.html +++ b/documentation/output/pages/stratus-dpgen.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/stratus-language.html b/documentation/output/pages/stratus-language.html index d806ed45..876c09f1 100644 --- a/documentation/output/pages/stratus-language.html +++ b/documentation/output/pages/stratus-language.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/stratus-patterns.html b/documentation/output/pages/stratus-patterns.html index 4da5c07b..3b278a4a 100644 --- a/documentation/output/pages/stratus-patterns.html +++ b/documentation/output/pages/stratus-patterns.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/symbolic-layout.html b/documentation/output/pages/symbolic-layout.html index ea112cd2..6706e411 100644 --- a/documentation/output/pages/symbolic-layout.html +++ b/documentation/output/pages/symbolic-layout.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pages/users-guide.html b/documentation/output/pages/users-guide.html index 5a16d514..94b96881 100644 --- a/documentation/output/pages/users-guide.html +++ b/documentation/output/pages/users-guide.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/output/pdfs/CheckToolkit.pdf b/documentation/output/pdfs/CheckToolkit.pdf index f57c0cbc..afe45d88 100644 Binary files a/documentation/output/pdfs/CheckToolkit.pdf and b/documentation/output/pdfs/CheckToolkit.pdf differ diff --git a/documentation/output/pdfs/DesignFlow.pdf b/documentation/output/pdfs/DesignFlow.pdf new file mode 100644 index 00000000..e951d2c8 Binary files /dev/null and b/documentation/output/pdfs/DesignFlow.pdf differ diff --git a/documentation/output/pdfs/PythonCpp.pdf b/documentation/output/pdfs/PythonCpp.pdf index bd6c3f1f..04fdf4e8 100644 Binary files a/documentation/output/pdfs/PythonCpp.pdf and b/documentation/output/pdfs/PythonCpp.pdf differ diff --git a/documentation/output/pdfs/PythonTutorial.pdf b/documentation/output/pdfs/PythonTutorial.pdf index 7c3945a0..e0c5aeb8 100644 Binary files a/documentation/output/pdfs/PythonTutorial.pdf and b/documentation/output/pdfs/PythonTutorial.pdf differ diff --git a/documentation/output/pdfs/RDS.pdf b/documentation/output/pdfs/RDS.pdf index a07fe291..929deaab 100644 Binary files a/documentation/output/pdfs/RDS.pdf and b/documentation/output/pdfs/RDS.pdf differ diff --git a/documentation/output/pdfs/Stratus.pdf b/documentation/output/pdfs/Stratus.pdf index a6cbdbac..1a4a52c9 100644 Binary files a/documentation/output/pdfs/Stratus.pdf and b/documentation/output/pdfs/Stratus.pdf differ diff --git a/documentation/output/pdfs/UsersGuide.pdf b/documentation/output/pdfs/UsersGuide.pdf index f4be5a91..981c3634 100644 Binary files a/documentation/output/pdfs/UsersGuide.pdf and b/documentation/output/pdfs/UsersGuide.pdf differ diff --git a/documentation/output/tags.html b/documentation/output/tags.html index 1a30ac73..b6554ac4 100644 --- a/documentation/output/tags.html +++ b/documentation/output/tags.html @@ -54,6 +54,7 @@ Topics @@ -80,6 +81,7 @@ Topics diff --git a/documentation/pelicanconf.py b/documentation/pelicanconf.py index e36aad89..1d40e5bd 100644 --- a/documentation/pelicanconf.py +++ b/documentation/pelicanconf.py @@ -31,6 +31,7 @@ STATIC_PATHS = [ 'pages/users-guide' , 'pages/python-cpp' , 'pages/stratus' , 'pages/check-toolkit' + , 'pages/design-flow' , 'pages/rds' , 'scripts' , 'images'