diff --git a/.travis.yml b/.travis.yml index 35fbacdb..914ebe81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,13 +9,19 @@ python: - "3.4" - "3.5" +# binary install as per travis instructions +# used to install latest version of ghdl +before_script: + - ./scripts/install_ghdl.sh + - export PATH=$PATH:$PWD/ghdl-0.33/bin/ + addons: apt: - sources: - - pgavin-ghdl + # sources: + # - pgavin-ghdl packages: - iverilog - - ghdl + # - ghdl install: - pip install . diff --git a/doc/source/manual/reference.rst b/doc/source/manual/reference.rst index 38ce3afa..53ef0163 100644 --- a/doc/source/manual/reference.rst +++ b/doc/source/manual/reference.rst @@ -41,6 +41,14 @@ A :class:`Simulation` object has the following method: Run the simulation forever (by default) or for a specified duration. +.. method:: Simulation.quit() + + Quit the simulation after it has run for a specified duration. The method should + be called (the simulation instance must be quit) before another simulation + instance is created. The method is called by default when the simulation is run + forever. + + .. _ref-simsupport: Simulation support functions @@ -90,6 +98,11 @@ Waveform tracing This attribute is used to set the directory to which VCD files are written. By default, the current working directory is used. + .. attribute:: filename + + This attribute is used to set the filename to which VCD files are written. By + default, the name attribbute is used. + .. attribute:: timescale This attribute is used to set the timescale corresponding to unit steps, diff --git a/example/cookbook/dff/dff.py b/example/cookbook/dff/dff.py index ecd18d71..e69fcd45 100644 --- a/example/cookbook/dff/dff.py +++ b/example/cookbook/dff/dff.py @@ -33,6 +33,7 @@ def simulate(timesteps): tb = traceSignals(test_dff) sim = Simulation(tb) sim.run(timesteps) + sim.quit() simulate(2000) diff --git a/example/cookbook/dffa/dffa.py b/example/cookbook/dffa/dffa.py index ac6e66bb..be0da586 100644 --- a/example/cookbook/dffa/dffa.py +++ b/example/cookbook/dffa/dffa.py @@ -44,6 +44,7 @@ def simulate(timesteps): tb = traceSignals(test_dffa) sim = Simulation(tb) sim.run(timesteps) + sim.quit() simulate(20000) diff --git a/example/cookbook/latch/latch.py b/example/cookbook/latch/latch.py index 9ab1b3b6..7439357d 100644 --- a/example/cookbook/latch/latch.py +++ b/example/cookbook/latch/latch.py @@ -33,6 +33,7 @@ def simulate(timesteps): tb = traceSignals(test_latch) sim = Simulation(tb) sim.run(timesteps) + sim.quit() simulate(20000) diff --git a/myhdl/_Simulation.py b/myhdl/_Simulation.py index 80ec07c1..07014b8d 100644 --- a/myhdl/_Simulation.py +++ b/myhdl/_Simulation.py @@ -45,6 +45,7 @@ class _error: _error.ArgType = "Inappriopriate argument type" _error.MultipleCosim = "Only a single cosimulator argument allowed" _error.DuplicatedArg = "Duplicated argument" +_error.MultipleSim = "Only a single Simulation instance is allowed" class Simulation(object): @@ -54,6 +55,7 @@ class Simulation(object): run -- run a simulation for some duration """ + _no_of_instances = 0 def __init__(self, *args): """ Construct a simulation object. @@ -65,13 +67,16 @@ class Simulation(object): _simulator._time = 0 arglist = _flatten(*args) self._waiters, self._cosim = _makeWaiters(arglist) + if Simulation._no_of_instances > 0: + raise SimulationError(_error.MultipleSim) + Simulation._no_of_instances += 1 if not self._cosim and _simulator._cosim: warn("Cosimulation not registered as Simulation argument") self._finished = False del _futureEvents[:] del _siglist[:] - - + + def _finalize(self): cosim = self._cosim if cosim: @@ -85,9 +90,12 @@ class Simulation(object): # clean up for potential new run with same signals for s in _signals: s._clear() + Simulation._no_of_instances = 0 self._finished = True - - + + def quit(self): + self._finalize() + def runc(self, duration=0, quiet=0): simrunc.run(sim=self, duration=duration, quiet=quiet) @@ -201,7 +209,7 @@ class Simulation(object): self._finalize() # now reraise the exepction raise - + def _makeWaiters(arglist): waiters = [] @@ -231,4 +239,4 @@ def _makeWaiters(arglist): if hasattr(sig, '_waiter'): waiters.append(sig._waiter) return waiters, cosim - + diff --git a/myhdl/_traceSignals.py b/myhdl/_traceSignals.py index abd8dd5a..df4cc4ec 100644 --- a/myhdl/_traceSignals.py +++ b/myhdl/_traceSignals.py @@ -50,6 +50,7 @@ class _TraceSignalsClass(object): __slot__ = ("name", "directory", + "filename", "timescale", "tracelists" ) @@ -57,6 +58,7 @@ class _TraceSignalsClass(object): def __init__(self): self.name = None self.directory = None + self.filename = None self.timescale = "1ns" self.tracelists = True @@ -89,8 +91,13 @@ class _TraceSignalsClass(object): else: directory = self.directory + if self.filename is None: + filename = name + else: + filename = str(self.filename) + h = _HierExtr(name, dut, *args, **kwargs) - vcdpath = os.path.join(directory, name + ".vcd") + vcdpath = os.path.join(directory, filename + ".vcd") if path.exists(vcdpath): backup = vcdpath + '.' + str(path.getmtime(vcdpath)) shutil.copyfile(vcdpath, backup) diff --git a/myhdl/conversion/_analyze.py b/myhdl/conversion/_analyze.py index c8d94957..744db3a0 100644 --- a/myhdl/conversion/_analyze.py +++ b/myhdl/conversion/_analyze.py @@ -578,10 +578,14 @@ class _AnalyzeVisitor(ast.NodeVisitor, _ConversionMixin): node.obj = int(0) # XXX elif f is bool: node.obj = bool() - elif f in _flatten(integer_types, ord): + elif f in _flatten(integer_types): node.obj = int(-1) ## elif f in (posedge , negedge): ## node.obj = _EdgeDetector() + elif f is ord: + node.obj = int(-1) + if not (isinstance(node.args[0], ast.Str) and (len(node.args[0].s) == 1)): + self.raiseError(node, _error.NotSupported, "ord: expect string argument with length 1") elif f is delay: node.obj = delay(0) ### suprize: identity comparison on unbound methods doesn't work in python 2.5?? @@ -915,6 +919,8 @@ class _AnalyzeVisitor(ast.NodeVisitor, _ConversionMixin): s = s[m.end():] continue self.raiseError(node, _error.UnsupportedFormatString, "%s" % s) + elif isinstance(n, ast.Str): + f.append(n.s) else: f.append(defaultConvSpec) a.append(n) diff --git a/myhdl/conversion/_toVHDL.py b/myhdl/conversion/_toVHDL.py index dca792e1..dcb9c4a1 100644 --- a/myhdl/conversion/_toVHDL.py +++ b/myhdl/conversion/_toVHDL.py @@ -602,9 +602,9 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): pre, suf = "", "(0)" else: pre, suf = "stdl(", ")" - elif isinstance(vhd, vhd_string): - if isinstance(ori, vhd_enum): - pre, suf = "%s'image(" % ori._type._name, ")" + # elif isinstance(vhd, vhd_string): + # if isinstance(ori, vhd_enum): + # pre, suf = "%s'image(" % ori._type._name, ")" return pre, suf @@ -654,10 +654,6 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.shiftOp(node) elif isinstance(node.op, (ast.BitAnd, ast.BitOr, ast.BitXor)): self.BitOp(node) - elif isinstance(node.op, ast.Mod) and (self.context == _context.PRINT): - self.visit(node.left) - self.write(", ") - self.visit(node.right) else: self.BinOp(node) @@ -953,11 +949,10 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): return elif f is ord: opening, closing = '', '' - if isinstance(node.args[0], ast.Str): - if len(node.args[0].s) > 1: - self.raiseError(node, _error.UnsupportedType, "Strings with length > 1" ) - else: - node.args[0].s = ord(node.args[0].s) + v = ord(node.args[0].s) + node.args[0].s = v + self.write(v) + return elif f in integer_types: opening, closing = '', '' pre, suf = self.inferCast(node.vhd, node.vhdOri) @@ -1361,15 +1356,9 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): a.vhd = vhd_boolean() elif isinstance(a.vhdOri, vhd_enum): a.vhd = vhd_string() - self.write("write(L, ") - self.context = _context.PRINT + self.write("write(L, to_string(") self.visit(a) - self.context = None - if s.justified == 'LEFT': - self.write(", justified=>LEFT") - if s.width: - self.write(", field=>%s" % s.width) - self.write(")") + self.write("))") self.write(';') self.writeline() self.write("writeline(output, L);") diff --git a/myhdl/conversion/_toVerilog.py b/myhdl/conversion/_toVerilog.py index be213ea7..b533054a 100644 --- a/myhdl/conversion/_toVerilog.py +++ b/myhdl/conversion/_toVerilog.py @@ -151,7 +151,7 @@ class _ToVerilogConvertor(object): genlist = _analyzeGens(arglist, h.absnames) siglist, memlist = _analyzeSigs(h.hierarchy) _annotateTypes(genlist) - + intf = _analyzeTopFunc(func, *args, **kwargs) intf.name = name doc = _makeDoc(inspect.getdoc(func)) @@ -750,11 +750,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): return elif f is ord: opening, closing = '', '' - if isinstance(node.args[0], ast.Str): - if len(node.args[0].s) > 1: - self.raiseError(node, _error.UnsupportedType, "Strings with length > 1") - else: - node.args[0].s = str(ord(node.args[0].s)) + node.args[0].s = str(ord(node.args[0].s)) elif f in integer_types: opening, closing = '', '' # convert number argument to integer diff --git a/myhdl/conversion/_verify.py b/myhdl/conversion/_verify.py index 34e4192f..caac3098 100644 --- a/myhdl/conversion/_verify.py +++ b/myhdl/conversion/_verify.py @@ -22,7 +22,7 @@ _simulators = {} sim = namedtuple('sim', 'name hdl analyze elaborate simulate skiplines skipchars ignore') -def registerSimulator(name=None, hdl=None, analyze=None, elaborate=None, simulate=None, +def registerSimulator(name=None, hdl=None, analyze=None, elaborate=None, simulate=None, skiplines=None, skipchars=None, ignore=None): if not isinstance(name, str) or (name.strip() == ""): raise ValueError("Invalid simulator name") @@ -41,8 +41,8 @@ def registerSimulator(name=None, hdl=None, analyze=None, elaborate=None, simulat registerSimulator( name="ghdl", hdl="VHDL", - analyze="ghdl -a --workdir=work pck_myhdl_%(version)s.vhd %(topname)s.vhd", - elaborate="ghdl -e --workdir=work -o %(unitname)s %(topname)s", + analyze="ghdl -a --std=08 --workdir=work pck_myhdl_%(version)s.vhd %(topname)s.vhd", + elaborate="ghdl -e --std=08 --workdir=work %(unitname)s", simulate="ghdl -r --workdir=work %(unitname)s" ) @@ -96,7 +96,7 @@ class _VerificationClass(object): __slots__ = ("simulator", "_analyzeOnly") def __init__(self, analyzeOnly=False): - self.simulator = None + self.simulator = None self._analyzeOnly = analyzeOnly @@ -178,7 +178,7 @@ class _VerificationClass(object): if ret != 0: print("Elaboration failed", file=sys.stderr) return ret - + g = tempfile.TemporaryFile(mode='w+t') #print(simulate) ret = subprocess.call(simulate, stdout=g, shell=True) diff --git a/myhdl/test/bugs/test_issue_104.py b/myhdl/test/bugs/test_issue_104.py new file mode 100644 index 00000000..9734d29b --- /dev/null +++ b/myhdl/test/bugs/test_issue_104.py @@ -0,0 +1,43 @@ +from __future__ import print_function +import pytest +from myhdl import Simulation, delay, SimulationError, instance, now +from myhdl._Simulation import _error +from helpers import raises_kind + +def test(): + @instance + def tbstim(): + yield delay(10) + print("{0:<8d} ".format(now())) + yield delay(1000) + print("{0:<8d} ".format(now())) + for _ in range(10): + yield delay(1000) + + return tbstim + + +def issue_104_quit_method(): + sim = Simulation(test()) + sim.run(1000) + sim.run(500) + sim.quit() + return sim._finished + +def issue_104_multiple_instance(): + sim1 = Simulation(test()) + sim1.run(1000) + # sim1 is "puased" + + # try and create a second, third, forth simulation instance + for ii in range(4): + with raises_kind(SimulationError, _error.MultipleSim): + another_sim = Simulation(test()) + # generating more sims should have failed + sim1.run(1000) + sim1.quit() + +def test_issue_104(): + + assert issue_104_quit_method() == True + issue_104_multiple_instance() diff --git a/myhdl/test/core/test_traceSignals.py b/myhdl/test/core/test_traceSignals.py index 7f79c00a..1fea552a 100644 --- a/myhdl/test/core/test_traceSignals.py +++ b/myhdl/test/core/test_traceSignals.py @@ -155,12 +155,16 @@ class TestTraceSigs: assert not path.exists(psub) def testTristateTrace(self, vcd_dir): - Simulation(topTristate()).run(100, quiet=QUIET) + sim = Simulation(topTristate()) + sim.run(100, quiet=QUIET) + sim.quit() def testBackupOutputFile(self, vcd_dir): p = "%s.vcd" % fun.__name__ dut = traceSignals(fun) - Simulation(dut).run(1000, quiet=QUIET) + sim = Simulation(dut) + sim.run(1000, quiet=QUIET) + sim.quit() _simulator._tf.close() _simulator._tracing = 0 size = path.getsize(p) diff --git a/scripts/install_ghdl.sh b/scripts/install_ghdl.sh new file mode 100755 index 00000000..ef655d42 --- /dev/null +++ b/scripts/install_ghdl.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -evx +wget https://sourceforge.net/projects/ghdl-updates/files/Builds/ghdl-0.33/ghdl-0.33-x86_64-linux.tgz -O /tmp/ghdl.tar.gz +mkdir ghdl-0.33 +tar -C ghdl-0.33 -xvf /tmp/ghdl.tar.gz