Source code for pyIPCMI.ToolChain.Altera.Quartus

# EMACS settings: -*-	tab-width: 2; indent-tabs-mode: t; python-indent-offset: 2 -*-
# vim: tabstop=2:shiftwidth=2:noexpandtab
# kate: tab-width 2; replace-tabs off; indent-width 2;
#
# ==============================================================================
# Authors:          Patrick Lehmann
#                   Martin Zabel
#
# Python Class:     Altera Quartus specific classes
#
# License:
# ==============================================================================
# Copyright 2017-2018 Patrick Lehmann - Bötzingen, Germany
# Copyright 2007-2016 Technische Universität Dresden - Germany
#                     Chair of VLSI-Design, Diagnostics and Architecture
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
#
# load dependencies
from collections                import OrderedDict
from enum                       import unique
from subprocess                 import check_output, STDOUT

from lib.Functions              import Init
from pyIPCMI.Base.Exceptions            import PlatformNotSupportedException
from pyIPCMI.Base.Logging               import Severity, LogEntry
from pyIPCMI.Base.Executable            import Executable, CommandLineArgumentList, DryRunException
from pyIPCMI.Base.Executable            import ExecutableArgument, ShortValuedFlagArgument, LongValuedFlagArgument, StringArgument, ShortFlagArgument
from pyIPCMI.Base.Project               import Project as BaseProject, ProjectFile, FileTypes, SettingsFile
from pyIPCMI.ToolChain                  import ToolMixIn, ConfigurationException, ToolConfiguration, EditionDescription, Edition, ToolSelector, OutputFilteredExecutable
from pyIPCMI.ToolChain.Altera           import AlteraException


__api__ = [
	'QuartusException',
	'QuartusEditions',
	'Configuration',
	'Quartus',
	'Map',
	'TclShell',
	'MapFilter',
	'QuartusSession',
	'QuartusProject',
	'QuartusSettings',
	'QuartusProjectFile'
]
__all__ = __api__


[docs]class QuartusException(AlteraException): pass
@unique class AlteraQuartusPrimeEditions(Edition): """Enumeration of all Quartus Prime editions provided by Mentor Graphics itself.""" QuartusII = EditionDescription(Name="Quartus II", Section=None) QuartusIILite = EditionDescription(Name="Quartus II Lite", Section=None)
[docs]@unique class QuartusEditions(Edition): """Enumeration of all Quartus editions provided by Altera itself.""" AlteraQuartus = EditionDescription(Name="Altera Quartus", Section="INSTALL.Altera.Quartus") IntelQuartus = EditionDescription(Name="Intel Quartus", Section="INSTALL.Intel.Quartus")
[docs]class Configuration(ToolConfiguration): _vendor = "Altera" #: The name of the tools vendor. _toolName = "Altera Quartus" #: The name of the tool. _section = "INSTALL.Altera.Quartus" #: The name of the configuration section. Pattern: ``INSTALL.Vendor.ToolName``. _multiVersionSupport = True #: Altera Quartus supports multiple versions installed on the same system. _template = { "Windows": { _section: { "Version": "16.0", "SectionName": ("%{PathWithRoot}#${Version}", None), "InstallationDirectory": ("${${SectionName}:InstallationDirectory}", "${INSTALL.Altera:InstallationDirectory}/${Version}/quartus"), "BinaryDirectory": ("${${SectionName}:BinaryDirectory}", "${InstallationDirectory}/bin64") } }, "Linux": { _section: { "Version": "16.0", "SectionName": ("%{PathWithRoot}#${Version}", None), "InstallationDirectory": ("${${SectionName}:InstallationDirectory}", "${INSTALL.Altera:InstallationDirectory}/${Version}/quartus"), "BinaryDirectory": ("${${SectionName}:BinaryDirectory}", "${InstallationDirectory}/bin") } } } #: The template for the configuration sections represented as nested dictionaries.
[docs] def CheckDependency(self): """Check if general Altera support is configured in pyIPCMI.""" return (len(self._host.Config['INSTALL.Altera']) != 0)
[docs] def ConfigureForAll(self): try: if (not self._AskInstalled("Is Altera Quartus-II or Quartus Prime installed on your system?")): self.ClearSection() else: # Configure Quartus version version = self._ConfigureVersion() if self._multiVersionSupport: self.PrepareVersionedSections() sectionName = self._host.Config[self._section]['SectionName'] self._host.Config[sectionName]['Version'] = version self._ConfigureInstallationDirectory() binPath = self._ConfigureBinaryDirectory() self.LogNormal("Checking Altera Quartus version... (this may take a few seconds)", indent=1) self.__CheckQuartusVersion(binPath, version) self.LogNormal("{DARK_GREEN}Altera Quartus is now configured.{NOCOLOR}".format(**Init.Foreground), indent=1) except ConfigurationException: self.ClearSection() raise
def __CheckQuartusVersion(self, binPath, version): if (self._host.Platform == "Windows"): quartusPath = binPath / "quartus_sh.exe" else: quartusPath = binPath / "quartus_sh" if not quartusPath.exists(): raise ConfigurationException("Executable '{0!s}' not found.".format(quartusPath)) \ from FileNotFoundError(str(quartusPath)) output = check_output([str(quartusPath), "-v"], universal_newlines=True, stderr=STDOUT) if "Version {0}".format(version) not in output: raise ConfigurationException("Quartus version mismatch. Expected version {0}.".format(version))
class Selector(ToolSelector): _toolName = "Quartus" def Select(self): editions = self._GetConfiguredEditions(QuartusEditions) if (len(editions) == 0): self._host.LogWarning("No Quartus installation found.", indent=1) self._host.Config['INSTALL.Quartus'] = OrderedDict() elif (len(editions) == 1): self._host.LogNormal("Default Quartus installation:", indent=1) self._host.LogNormal("Set to {0}".format(editions[0].Name), indent=2) self._host.Config['INSTALL.Quartus']['SectionName'] = editions[0].Section else: self._host.LogNormal("Select Quartus installation:", indent=1) defaultEdition = QuartusEditions.IntelQuartus if defaultEdition not in editions: defaultEdition = editions[0] selectedEdition = self._AskSelection(editions, defaultEdition) self._host.Config['INSTALL.Quartus']['SectionName'] = selectedEdition.Section
[docs]class Quartus(ToolMixIn):
[docs] def GetMap(self): return Map(self)
[docs] def GetTclShell(self): return TclShell(self)
[docs]class Map(OutputFilteredExecutable, ToolMixIn): def __init__(self, toolchain : ToolMixIn): ToolMixIn.__init__( self, toolchain._platform, toolchain._dryrun, toolchain._binaryDirectoryPath, toolchain._version, toolchain._logger) if (self._platform == "Windows") : executablePath = self._binaryDirectoryPath / "quartus_map.exe" elif (self._platform == "Linux") : executablePath = self._binaryDirectoryPath / "quartus_map" else : raise PlatformNotSupportedException(self._platform) super().__init__(self._platform, self._dryrun, executablePath, logger=self._logger) self.Parameters[self.Executable] = executablePath
[docs] class Executable(metaclass=ExecutableArgument) : pass
[docs] class ArgProjectName(metaclass=StringArgument): pass
[docs] class SwitchArgumentFile(metaclass=ShortValuedFlagArgument): _name = "f"
[docs] class SwitchDeviceFamily(metaclass=LongValuedFlagArgument) : _name = "family"
[docs] class SwitchDevicePart(metaclass=LongValuedFlagArgument) : _name = "part"
Parameters = CommandLineArgumentList( Executable, ArgProjectName, SwitchArgumentFile, SwitchDeviceFamily, SwitchDevicePart )
[docs] def Compile(self) : parameterList = self.Parameters.ToArgumentList() self.LogVerbose("command: {0}".format(" ".join(parameterList))) if (self._dryrun): self.LogDryRun("Start process: {0}".format(" ".join(parameterList))) return try: self.StartProcess(parameterList) except Exception as ex: raise QuartusException("Failed to launch quartus_map.") from ex self._hasOutput = False self._hasWarnings = False self._hasErrors = False try: iterator = iter(MapFilter(self.GetReader())) line = next(iterator) self._hasOutput = True self.LogNormal(" quartus_map messages for '{0}'".format(self.Parameters[self.SwitchArgumentFile])) self.LogNormal(" " + ("-" * (78 - self.Logger.BaseIndent*2))) while True: self._hasWarnings |= (line.Severity is Severity.Warning) self._hasErrors |= (line.Severity is Severity.Error) line.IndentBy(self.Logger.BaseIndent + 1) self.Log(line) line = next(iterator) except DryRunException: pass except StopIteration: pass finally: if self._hasOutput: self.LogNormal(" " + ("-" * (78 - self.Logger.BaseIndent*2)))
[docs]class TclShell(Executable, ToolMixIn): def __init__(self, toolchain : ToolMixIn): ToolMixIn.__init__( self, toolchain._platform, toolchain._dryrun, toolchain._binaryDirectoryPath, toolchain._version, toolchain._logger) if (self._platform == "Windows") : executablePath = self._binaryDirectoryPath / "quartus_sh.exe" elif (self._platform == "Linux") : executablePath = self._binaryDirectoryPath / "quartus_sh" else : raise PlatformNotSupportedException(self._platform) super().__init__(self._platform, self._dryrun, executablePath, logger=self._logger) self.Parameters[self.Executable] = executablePath
[docs] class Executable(metaclass=ExecutableArgument): pass
[docs] class SwitchShell(metaclass=ShortFlagArgument): _name = "s"
Parameters = CommandLineArgumentList( Executable, SwitchShell )
[docs]def MapFilter(gen): iterator = iter(gen) for line in iterator: if line.startswith("Error ("): yield LogEntry(line, Severity.Error) elif line.startswith("Info: Command: quartus_map"): break for line in iterator: if line.startswith("Info ("): yield LogEntry(line, Severity.Verbose) elif line.startswith("Error ("): yield LogEntry(line, Severity.Error) elif line.startswith("Warning ("): yield LogEntry(line, Severity.Warning) elif line.startswith(" Info ("): yield LogEntry(line, Severity.Verbose) elif line.startswith(" Info ("): yield LogEntry(line, Severity.Verbose) elif line.startswith("Info:"): yield LogEntry(line, Severity.Info) elif line.startswith(" Info:"): yield LogEntry(line, Severity.Debug) else: yield LogEntry(line, Severity.Normal)
[docs]class QuartusSession: def __init__(self, host): self.TclShell = host.Toolchain.GetTclShell() self.TclShell.Parameters[self.TclShell.SwitchShell] = True self.TclShell.StartProcess() self.TclShell.SendBoundary() self.TclShell.ReadUntilBoundary()
[docs] def exit(self): self.TclShell.Send("exit") self.TclShell.ReadUntilBoundary()
[docs]class QuartusProject(BaseProject): def __init__(self, host, name, projectFile=None): super().__init__(name) self._host = host self._projectFile = projectFile
[docs] def Create(self, session=None): if (session is None): tclShell = self._host.Toolchain.GetTclShell() tclShell.Parameters[tclShell.SwitchShell] = True tclShell.StartProcess() tclShell.SendBoundary() tclShell.ReadUntilBoundary() else: tclShell = session.TclShell tclShell.Send("project_create {0}".format(self._name)) tclShell.SendBoundary() tclShell.ReadUntilBoundary() if (session is None): tclShell.Send("project_close") tclShell.SendBoundary() tclShell.ReadUntilBoundary() tclShell.Send("exit") tclShell.ReadUntilBoundary()
[docs] def Save(self, session): tclShell = session.TclShell tclShell.Send("export_assignments") tclShell.SendBoundary() tclShell.ReadUntilBoundary()
[docs] def Read(self): tclShell = self._host.Toolchain.GetSynthesizer() tclShell.StartProcess(["-s"]) tclShell.SendBoundary() tclShell.ReadUntilBoundary() tclShell.Send("help") tclShell.SendBoundary() tclShell.ReadUntilBoundary() tclShell.Send("exit") tclShell.ReadUntilBoundary()
[docs] def Open(self, session): tclShell = session.TclShell tclShell.Send("project_open {0}".format(self._name)) tclShell.SendBoundary() tclShell.ReadUntilBoundary()
[docs] def Close(self, session): tclShell = session.TclShell tclShell.Send("project_close") tclShell.SendBoundary() tclShell.ReadUntilBoundary() tclShell.Send("exit") tclShell.ReadUntilBoundary()
[docs]class QuartusSettings(SettingsFile): def __init__(self, name, settingsFile=None): super().__init__(name) self._projectFile = settingsFile self._sourceFiles = [] self._globalAssignments = OrderedDict() self._parameters = {} @property def File(self): return self._projectFile @File.setter def File(self, value): if (not isinstance(value, QuartusProjectFile)): raise ValueError("Parameter 'value' is not of type QuartusProjectFile.") self._projectFile = value @property def GlobalAssignments(self): return self._globalAssignments @property def Parameters(self): return self._parameters
[docs] def CopySourceFilesFromProject(self, project): for file in project.Files(fileType=FileTypes.VHDLSourceFile): self._sourceFiles.append(file)
[docs] def Write(self): if (self._projectFile is None): raise QuartusException("No file path for QuartusProject provided.") buffer = "" for key,value in self._globalAssignments.items(): buffer += "set_global_assignment -name {key} {value!s}\n".format(key=key, value=value) buffer += "\n" for key,value in self._parameters.items(): buffer += "set_parameter -name {key} {value}\n".format(key=key, value=value) buffer += "\n" for file in self._sourceFiles: if (not file.Path.exists()): raise QuartusException("Cannot add '{0!s}' to Quartus settings file.". format(file.Path)) from FileNotFoundError(str(file.Path)) buffer += "set_global_assignment -name VHDL_FILE {file} -library {library}\n".\ format(file=file.Path.as_posix(), library=file.LibraryName) with self._projectFile.Path.open('w') as fileHandle: fileHandle.write(buffer)
[docs]class QuartusProjectFile(ProjectFile): def __init__(self, file): super().__init__(file)