Source code for pyIPCMI.Simulator

# 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
#
# Python Sub Module:    TODO:
#
# 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 datetime           import datetime
from enum               import Enum, unique

from flags              import Flags

from pyIPCMI.Base               import IHost
from pyIPCMI.Base.Exceptions    import ExceptionBase, SkipableException
from pyIPCMI.Base.Logging       import LogEntry
from pyIPCMI.Base.Project       import Environment, VHDLVersion
from pyIPCMI.Base.Shared        import Shared, to_time
from pyIPCMI.DataBase.Entity    import WildCard, SimulationResult
from pyIPCMI.DataBase.TestCase  import TestCase, SimulationStatus, TestSuite
from lib.Decorators     import MethodAlias
from lib.Functions      import Init
from lib.SphinxExtensions import DocumentMemberAttribute


__api__ = [
	'SimulatorException',
	'SkipableSimulatorException',
	'pyIPCMISimulationResultNotFoundException',
	'SimulationSteps',
	'SimulationState',
	'SimulationResult',
	'Simulator',
	'pyIPCMISimulationResultFilter'
]
__all__ = __api__


VHDL_TESTBENCH_LIBRARY_NAME = "test"


[docs]class SimulatorException(ExceptionBase): """Base class for all SimulatorException classes. It is raised while running simulation tasks in pyIPCMI. """
[docs]class SkipableSimulatorException(SimulatorException, SkipableException): """``SkipableSimulatorException`` is a :py:exc:`SimulatorException`, which can be skipped. """
[docs]class pyIPCMISimulationResultNotFoundException(SkipableSimulatorException): """This exception is raised if the expected pyIPCMI simulation result string was not found in the simulator's output. """
[docs]@unique class SimulationSteps(Flags): """Simulation step enumeration.""" Prepare = 1 << 0 CleanUpBefore = 1 << 1 CleanUpAfter = 1 << 2 Analyze = 1 << 5 Elaborate = 1 << 6 # Compile = 1 << 7 # Optimize = 1 << 8 Simulate = 1 << 9 ShowWaveform = 1 << 10 ShowCoverage = 1 << 11 ShowReport = 1 << 15 Recompile = 1 << 25 Resimulate = 1 << 26 # Review = 1 << 27 def __and__(self, other): if isinstance(other, bool): return self if other else self.__class__.no_flags else: return super().__and__(other)
[docs]@unique class SimulationState(Enum): """Simulation state enumeration.""" Prepare = 0 Analyze = 1 Elaborate = 2 Optimize = 3 Simulate = 4 View = 5 Coverage = 6
[docs]class Simulator(Shared): """Base class for all Simulator classes.""" ENVIRONMENT = Environment.Simulation VHDL_VERSION = VHDLVersion.VHDL2008 class __Directories__(Shared.__Directories__): PreCompiled = None
[docs] @DocumentMemberAttribute() def __init__(self, host : IHost, dryRun, simulationSteps : SimulationSteps): """Class initializer :type host: object :param host: The hosting instance for this instance. :type dryRun: bool :param dryRun: Enable dry-run mode :type simulationSteps: SimulationSteps :param simulationSteps: A set of simulation step to precess. """ super().__init__(host, dryRun) self._vhdlVersion = None self._vhdlGenerics = None self._toolChain = None self._simulationSteps = simulationSteps self._testSuite = TestSuite() # TODO: This includes not the read ini files phases ... self._state = SimulationState.Prepare self._analyzeTime = None self._elaborationTime = None self._simulationTime = None
# class properties # ============================================================================ @property def TestSuite(self): return self._testSuite
[docs] def _PrepareSimulationEnvironment(self): self.LogNormal("Preparing simulation environment...") self._PrepareEnvironment()
[docs] def _PrepareEnvironment_PurgeDirectory(self): if (SimulationSteps.CleanUpBefore in self._simulationSteps): super()._PrepareEnvironment_PurgeDirectory()
@MethodAlias(Shared._Prepare) def _PrepareSimulator(self): pass
[docs] def RunAll(self, fqnList, *args, **kwargs): """Run a list of testbenches. Expand wildcards to all selected testbenches.""" self._testSuite.StartTimer() self.Logger.BaseIndent = int(len(fqnList) > 1) try: for fqn in fqnList: entity = fqn.Entity if (isinstance(entity, WildCard)): self.Logger.BaseIndent = 1 for testbench in entity.GetVHDLTestbenches(): self.TryRun(testbench, *args, **kwargs) else: testbench = entity.VHDLTestbench self.TryRun(testbench, *args, **kwargs) except KeyboardInterrupt: self.LogError("Received a keyboard interrupt.") finally: self._testSuite.StopTimer() if (SimulationSteps.ShowReport in self._simulationSteps): self.PrintOverallSimulationReport() return self._testSuite.IsAllPassed
[docs] def TryRun(self, testbench, *args, **kwargs): """Try to run a testbench. Skip skipable exceptions by printing the error and its cause.""" __SIMULATION_STATE_TO_TESTCASE_STATUS__ = { SimulationState.Prepare: SimulationStatus.InternalError, SimulationState.Analyze: SimulationStatus.AnalyzeError, SimulationState.Elaborate: SimulationStatus.ElaborationError, # SimulationState.Optimize: SimulationStatus.ElaborationError, SimulationState.Simulate: SimulationStatus.SimulationError } testCase = TestCase(testbench) self._testSuite.AddTestCase(testCase) testCase.StartTimer() try: self.Run(testbench, *args, **kwargs) testCase.UpdateStatus(testbench.Result) except SkipableSimulatorException as ex: testCase.Status = __SIMULATION_STATE_TO_TESTCASE_STATUS__[self._state] self.LogQuiet(" {RED}ERROR:{NOCOLOR} {ExMsg}".format(ExMsg=ex.message, **Init.Foreground)) cause = ex.__cause__ if (cause is not None): self.LogQuiet(" {YELLOW}{ExType}:{NOCOLOR} {ExMsg!s}".format(ExType=cause.__class__.__name__, ExMsg=cause, **Init.Foreground)) cause = cause.__cause__ if (cause is not None): self.LogQuiet(" {YELLOW}{ExType}:{NOCOLOR} {ExMsg!s}".format(ExType=cause.__class__.__name__, ExMsg=cause, **Init.Foreground)) self.LogQuiet(" {RED}[SKIPPED DUE TO ERRORS]{NOCOLOR}".format(**Init.Foreground)) except SimulatorException: testCase.Status = __SIMULATION_STATE_TO_TESTCASE_STATUS__[self._state] raise except ExceptionBase: testCase.Status = SimulationStatus.SystemError raise finally: testCase.StopTimer()
[docs] def Run(self, testbench, board, vhdlVersion, vhdlGenerics=None): """Write the Testbench message line, create a pyIPCMIProject and add the first *.files file to it.""" self.LogQuiet("{CYAN}Testbench: {0!s}{NOCOLOR}".format(testbench.Parent, **Init.Foreground)) self._vhdlVersion = vhdlVersion self._vhdlGenerics = vhdlGenerics # setup all needed paths to execute fuse self._CreatepyIPCMIProject(testbench.ModuleName, board) self._AddFileListFile(testbench.FilesFile) self._prepareTime = self._GetTimeDeltaSinceLastEvent() if self._simulationSteps.CleanUpBefore: pass if self._simulationSteps.Prepare: pass if self._simulationSteps.Analyze: self.LogNormal("Running analysis for every vhdl file...") self._state = SimulationState.Analyze self._RunAnalysis(testbench) self._analyzeTime = self._GetTimeDeltaSinceLastEvent() if self._simulationSteps.Elaborate: self.LogNormal("Running elaboration...") self._state = SimulationState.Elaborate self._RunElaboration(testbench) self._elaborationTime = self._GetTimeDeltaSinceLastEvent() # if self._simulationSteps.Optimize: # pass if self._simulationSteps.Simulate: self.LogNormal("Running simulation...") self._state = SimulationState.Simulate self._RunSimulation(testbench) self._simulationTime = self._GetTimeDeltaSinceLastEvent() if self._simulationSteps.ShowWaveform: self.LogNormal("Executing waveform viewer...") self._state = SimulationState.View self._RunView(testbench) if self._simulationSteps.ShowCoverage: self.LogNormal("Executing coverage analysis...") self._state = SimulationState.View self._RunCoverage(testbench) self._endAt = datetime.now()
[docs] def _RunAnalysis(self, testbench): pass
[docs] def _RunElaboration(self, testbench): pass
[docs] def _RunSimulation(self, testbench): pass
[docs] def _RunView(self, testbench): pass
[docs] def _RunCoverage(self, testbench): pass
[docs] def PrintOverallSimulationReport(self): self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground)) self.LogQuiet("{HEADLINE}{headline: ^80s}{NOCOLOR}".format(headline="Overall Simulation Report", **Init.Foreground)) self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground)) # table header self.LogQuiet("{Name: <24} | {Duration: >5} | {Status: ^11}".format(Name="Name", Duration="Time", Status="Status")) self.LogQuiet("-" * 80) self.PrintSimulationReportLine(self._testSuite, 0, 24) self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground)) self.LogQuiet("Time: {time: >5} Count: {count: <3} Passed: {passed: <3} No Asserts: {noassert: <2} Failed: {failed: <2} Errors: {error: <2}".format( time=to_time(self._testSuite.OverallRunTime), count=self._testSuite.Count, passed=self._testSuite.PassedCount, noassert=self._testSuite.NoAssertsCount, failed=self._testSuite.FailedCount, error=self._testSuite.ErrorCount )) self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground))
__SIMULATION_REPORT_COLOR_TABLE__ = { SimulationStatus.Unknown: "RED", SimulationStatus.DryRun: "YELLOW", SimulationStatus.InternalError: "DARK_RED", SimulationStatus.SystemError: "DARK_RED", SimulationStatus.AnalyzeError: "DARK_RED", SimulationStatus.ElaborationError: "DARK_RED", SimulationStatus.SimulationError: "RED", SimulationStatus.SimulationFailed: "RED", SimulationStatus.SimulationNoAsserts: "YELLOW", SimulationStatus.SimulationSuccess: "GREEN", SimulationStatus.SimulationGUIRun: "YELLOW" } __SIMULATION_REPORT_STATUS_TEXT_TABLE__ = { SimulationStatus.Unknown: "-- ?? --", SimulationStatus.DryRun: "DRY RUN", SimulationStatus.InternalError: "INT. ERROR", SimulationStatus.SystemError: "SYS. ERROR", SimulationStatus.AnalyzeError: "ANA. ERROR", SimulationStatus.ElaborationError: "ELAB. ERROR", SimulationStatus.SimulationError: "SIM. ERROR", SimulationStatus.SimulationFailed: "FAILED", SimulationStatus.SimulationNoAsserts: "NO ASSERTS", SimulationStatus.SimulationSuccess: "PASSED", SimulationStatus.SimulationGUIRun: "GUI RUN" }
[docs] def PrintSimulationReportLine(self, testObject, indent, nameColumnWidth): _indent = " " * indent for group in testObject.Groups.values(): pattern = "{indent}{{groupName: <{nameColumnWidth}}} | | ".format(indent=_indent, nameColumnWidth=nameColumnWidth) self.LogQuiet(pattern.format(groupName=group.Name)) self.PrintSimulationReportLine(group, indent + 1, nameColumnWidth - 2) for testCase in testObject.TestCases.values(): pattern = "{indent}{{testcaseName: <{nameColumnWidth}}} | {{duration: >5}} | {{{color}}}{{status: ^11}}{{NOCOLOR}}".format( indent=_indent, nameColumnWidth=nameColumnWidth, color=self.__SIMULATION_REPORT_COLOR_TABLE__[testCase.Status]) self.LogQuiet(pattern.format(testcaseName=testCase.Name, duration=to_time(testCase.OverallRunTime), status=self.__SIMULATION_REPORT_STATUS_TEXT_TABLE__[testCase.Status], **Init.Foreground))
def PoCSimulationResultFilter(gen, simulationResult): state = 0 for line in gen: if ((state == 0) and (line.Message == "========================================")): state += 1 elif ((state == 1) and (line.Message == "POC TESTBENCH REPORT")): state += 1 yield LogEntry("{COLOR}{line}{NOCOLOR}".format(COLOR=Init.Foreground['DARK_CYAN'], line=line.Message, **Init.Foreground), line.Severity, line.Indent) continue elif ((state == 2) and (line.Message == "========================================")): state += 1 elif ((state == 3) and (line.Message == "========================================")): state += 1 elif ((state == 4) and line.Message.startswith("SIMULATION RESULT = ")): state += 1 if line.Message.endswith("FAILED"): color = Init.Foreground['RED'] simulationResult <<= SimulationResult.Failed elif line.Message.endswith("NO ASSERTS"): color = Init.Foreground['YELLOW'] simulationResult <<= SimulationResult.NoAsserts elif line.Message.endswith("PASSED"): color = Init.Foreground['GREEN'] simulationResult <<= SimulationResult.Passed else: color = Init.Foreground['RED'] simulationResult <<= SimulationResult.Error yield LogEntry("{COLOR}{line}{NOCOLOR}".format(COLOR=color,line=line.Message, **Init.Foreground), line.Severity, line.Indent) continue elif ((state == 5) and (line.Message == "========================================")): state += 1 yield line if (state != 6): raise pyIPCMISimulationResultNotFoundException("No PoC Testbench Report in simulator output found.")