diff --git a/.travis.yml b/.travis.yml index eb38437c..cf2e565d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ # http://docs.travis-ci.com/user/workers/container-based-infrastructure/ -sudo: false +sudo: required +dist: trusty language: python python: @@ -7,20 +8,7 @@ python: - "pypy" - "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 - packages: - - iverilog - # - ghdl + - "3.6" install: - pip install . @@ -30,16 +18,8 @@ env: - CI_TARGET=iverilog - CI_TARGET=ghdl -# matrix: -# allow_failures: -# - python: "3.4" -# env: CI_TARGET=iverilog -# - python: "3.4" -# env: CI_TARGET=ghdl -# - python: "3.5" -# env: CI_TARGET=iverilog -# - python: "3.5" -# env: CI_TARGET=ghdl +before_script: + - ./scripts/ci_deps.sh script: ./scripts/ci.sh diff --git a/README.md b/README.md index addf9baa..10dd4137 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ MyHDL 1.0dev ============ -[![Join the chat at https://gitter.im/jandecaluwe/myhdl](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jandecaluwe/myhdl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join the chat at https://gitter.im/jandecaluwe/myhdl](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/myhdl/myhdl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Documentation Status](https://readthedocs.org/projects/myhdl/badge/?version=stable)](http://docs.myhdl.org/en/stable/manual) [![Documentation Status](https://readthedocs.org/projects/myhdl/badge/?version=latest)](http://docs.myhdl.org/en/latest/manual) -[![Build Status](https://travis-ci.org/jandecaluwe/myhdl.svg?branch=master)](https://travis-ci.org/jandecaluwe/myhdl) +[![Build Status](https://travis-ci.org/myhdl/myhdl.svg?branch=master)](https://travis-ci.org/myhdl/myhdl) What is MyHDL? -------------- diff --git a/myhdl/_Cosimulation.py b/myhdl/_Cosimulation.py index 67875954..dcceb2e1 100644 --- a/myhdl/_Cosimulation.py +++ b/myhdl/_Cosimulation.py @@ -34,7 +34,6 @@ _MAXLINE = 4096 class _error: pass -_error.MultipleCosim = "Only a single cosimulator allowed" _error.DuplicateSigNames = "Duplicate signal name in myhdl vpi call" _error.SigNotFound = "Signal not found in Cosimulation arguments" _error.TimeZero = "myhdl vpi call when not at time 0" @@ -49,11 +48,6 @@ class Cosimulation(object): def __init__(self, exe="", **kwargs): """ Construct a cosimulation object. """ - - if _simulator._cosim: - raise CosimulationError(_error.MultipleCosim) - _simulator._cosim = 1 - rt, wt = os.pipe() rf, wf = os.pipe() @@ -194,7 +188,3 @@ class Cosimulation(object): while 1: yield sigs self._hasChange = 1 - - def __del__(self): - """ Clear flag when this object destroyed - to suite unittest. """ - _simulator._cosim = 0 diff --git a/myhdl/_Simulation.py b/myhdl/_Simulation.py index 6dd8955f..df5fb340 100644 --- a/myhdl/_Simulation.py +++ b/myhdl/_Simulation.py @@ -83,23 +83,21 @@ class Simulation(object): """ _simulator._time = 0 arglist = _flatten(*args) - self._waiters, self._cosim = _makeWaiters(arglist) + self._waiters, self._cosims = _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: - _simulator._cosim = 0 - os.close(cosim._rt) - os.close(cosim._wf) - cosim._child.wait() + cosims = self._cosims + if cosims: + for cosim in cosims: + os.close(cosim._rt) + os.close(cosim._wf) + cosim._child.wait() if _simulator._tracing: _simulator._tracing = 0 _simulator._tf.close() @@ -131,7 +129,7 @@ class Simulation(object): stop.hasRun = 1 maxTime = _simulator._time + duration schedule((maxTime, stop)) - cosim = self._cosim + cosims = self._cosims t = _simulator._time actives = {} tracing = _simulator._tracing @@ -155,10 +153,20 @@ class Simulation(object): except StopIteration: continue - if cosim: - cosim._get() - if _siglist or cosim._hasChange: - cosim._put(t) + if cosims: + any_cosim_changes = False + for cosim in cosims: + any_cosim_changes = \ + any_cosim_changes or cosim._hasChange + for cosim in cosims: + cosim._get() + if _siglist or any_cosim_changes: + # It should be safe to _put a cosim with no changes + # because _put with the same values should be + # idempotent. We need to _put them all here because + # otherwise we can desync _get/_put. + for cosim in cosims: + cosim._put(t) continue elif _siglist: continue @@ -181,8 +189,9 @@ class Simulation(object): t = _simulator._time = _futureEvents[0][0] if tracing: print("#%s" % t, file=tracefile) - if cosim: - cosim._put(t) + if cosims: + for cosim in cosims: + cosim._put(t) while _futureEvents: newt, event = _futureEvents[0] if newt == t: @@ -225,17 +234,15 @@ class Simulation(object): def _makeWaiters(arglist): waiters = [] ids = set() - cosim = None + cosims = [] for arg in arglist: if isinstance(arg, GeneratorType): waiters.append(_inferWaiter(arg)) elif isinstance(arg, _Instantiator): waiters.append(arg.waiter) elif isinstance(arg, Cosimulation): - if cosim is not None: - raise SimulationError(_error.MultipleCosim) - cosim = arg - waiters.append(_SignalTupleWaiter(cosim._waiter())) + cosims.append(arg) + waiters.append(_SignalTupleWaiter(arg._waiter())) elif isinstance(arg, _Waiter): waiters.append(arg) elif arg == True: @@ -249,4 +256,4 @@ def _makeWaiters(arglist): for sig in _signals: if hasattr(sig, '_waiter'): waiters.append(sig._waiter) - return waiters, cosim + return waiters, cosims diff --git a/myhdl/_simulator.py b/myhdl/_simulator.py index ba3b93f4..79e69efb 100644 --- a/myhdl/_simulator.py +++ b/myhdl/_simulator.py @@ -30,7 +30,6 @@ _blocks = [] _siglist = [] _futureEvents = [] _time = 0 -_cosim = 0 _tracing = 0 _tf = None diff --git a/myhdl/conversion/_toVHDL.py b/myhdl/conversion/_toVHDL.py index 5bdbc245..9f285d62 100644 --- a/myhdl/conversion/_toVHDL.py +++ b/myhdl/conversion/_toVHDL.py @@ -466,14 +466,19 @@ def _writeSigDecls(f, intf, siglist, memlist): val_str = "" else: sig_vhdl_objs = [inferVhdlObj(each) for each in m.mem] - + if all([each._init == m.mem[0]._init for each in m.mem]): - val_str = ( - ' := (others => %dX"%s")' % - (sig_vhdl_objs[0].size, str(m.mem[0]._init))) + if isinstance(m.mem[0]._init, bool): + val_str = ( + ' := (others => \'%s\')' % str(int(m.mem[0]._init))) + + else: + val_str = ( + ' := (others => %dX"%s")' % + (sig_vhdl_objs[0].size, str(m.mem[0]._init))) else: _val_str = ',\n '.join( - ['%dX"%s"' % (obj.size, str(each._init)) for + ['%dX"%s"' % (obj.size, str(each._init)) for obj, each in zip(sig_vhdl_objs, m.mem)]) val_str = ' := (\n ' + _val_str + ')' @@ -644,7 +649,10 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): return s def BitRepr(self, item, var): - return '"%s"' % bin(item, len(var)) + if isinstance(var._val, bool): + return '\'%s\'' % bin(item, len(var)) + else: + return '"%s"' % bin(item, len(var)) def inferCast(self, vhd, ori): pre, suf = "", "" diff --git a/myhdl/conversion/_toVerilog.py b/myhdl/conversion/_toVerilog.py index 2d84d24e..ff5a78cb 100644 --- a/myhdl/conversion/_toVerilog.py +++ b/myhdl/conversion/_toVerilog.py @@ -368,7 +368,7 @@ def _writeSigDecls(f, intf, siglist, memlist): if m._driven: k = m._driven - if toVerilog.initial_values: + if toVerilog.initial_values and not k == 'wire': if all([each._init == m.mem[0]._init for each in m.mem]): initialize_block_name = ('INITIALIZE_' + m.name).upper() diff --git a/myhdl/test/bugs/test_issue_169.py b/myhdl/test/bugs/test_issue_169.py new file mode 100644 index 00000000..c7cced96 --- /dev/null +++ b/myhdl/test/bugs/test_issue_169.py @@ -0,0 +1,60 @@ +from myhdl import Signal, block, delay, instance + +import pytest + + +class Test1: + def __init__(self): + self.clock = Signal(bool(0)) + + @block + def test(self): + + @instance + def func(): + i = 0 + while i <= 100: + yield delay(10) + self.clock.next = not self.clock + i = i + 1 + + return func + + +class Test2: + def __init__(self): + self.clock = Signal(bool(1)) + + @block + def test(self): + + @instance + def func(): + i = 0 + while i <= 100: + yield delay(10) + self.clock.next = not self.clock + i = i + 1 + + return func + + +@block +def test_bench(): + inst1 = Test1() + inst2 = Test2() + + # Two instances are created + ins1 = inst1.test() + ins2 = inst2.test() + + return ins1, ins2 + +@pytest.mark.xfail +def test_issue_169(): + test_inst = test_bench() + assert test_inst.verify_convert() == True + +if __name__ == '__main__': + test_issue_169() + diff --git a/myhdl/test/conversion/general/test_case.py b/myhdl/test/conversion/general/test_case.py index 15779c34..89a28405 100644 --- a/myhdl/test/conversion/general/test_case.py +++ b/myhdl/test/conversion/general/test_case.py @@ -79,6 +79,39 @@ def bench_case(map_case, N): return stimulus, inst +@block +def bool_bench_case(map_case): + + a = Signal(False) + z = Signal(intbv(0)[2:]) + + inst = map_case(z, a) + + @instance + def stimulus(): + for i in range(2): + a.next = i + yield delay(10) + print(z) + + return stimulus, inst + +@block +def length1_bench_case(map_case): + + a = Signal(intbv(0)[1:]) + z = Signal(intbv(0)[2:]) + + inst = map_case(z, a) + + @instance + def stimulus(): + for i in range(2): + a.next = i + yield delay(10) + print(z) + + return stimulus, inst def test_case4(): assert bench_case(map_case4, 4).verify_convert() == 0 @@ -91,3 +124,15 @@ def test_case3(): def test_case4_full(): assert bench_case(map_case4_full, 4).verify_convert() == 0 + +def test_case2_bool(): + assert bool_bench_case(map_case3).verify_convert() == 0 + +def test_case3_bool(): + assert bool_bench_case(map_case3).verify_convert() == 0 + +def test_case2_single_bit(): + assert length1_bench_case(map_case3).verify_convert() == 0 + +def test_case3_single_bit(): + assert length1_bench_case(map_case3).verify_convert() == 0 diff --git a/myhdl/test/conversion/general/test_initial_values.py b/myhdl/test/conversion/general/test_initial_values.py index eff3e3a0..6fcdf826 100644 --- a/myhdl/test/conversion/general/test_initial_values.py +++ b/myhdl/test/conversion/general/test_initial_values.py @@ -47,6 +47,24 @@ def initial_value_enum_bench(initial_val, **kwargs): return state_walker, clkgen +@block +def bool_writer(signal, clk): + + @always(clk.posedge) + def writer(): + print(int(signal)) + + return writer + +@block +def int_writer(signal, clk): + + @always(clk.posedge) + def writer(): + print(signal) + + return writer + @block def initial_value_bench(initial_val, **kwargs): @@ -107,9 +125,12 @@ def initial_value_bench(initial_val, **kwargs): else: assert output_signal == update_val - @always(clk.posedge) - def output_writer(): - print(output_signal) + if isinstance(initial_val, bool): + output_writer = bool_writer(output_signal, clk) + + else: + output_writer = int_writer(output_signal, clk) + return clkgen, output_driver, drive_and_check, output_writer @@ -146,25 +167,31 @@ end process INITIAL_VALUE_BENCH_OUTPUT_WRITER; ''' return list_writer +@block +def bool_list_writer(output_signal_list, clk): + + signal_list_length = len(output_signal_list) + + @always(clk.posedge) + def list_writer(): + for i in range(signal_list_length): + print(int(output_signal_list[i])) + + return list_writer @block -def initial_value_list_bench(initial_vals, **kwargs): +def initial_value_bool_list_bench(initial_vals, **kwargs): clk = Signal(bool(0)) input_signal_list = [Signal(initial_val) for initial_val in initial_vals] - if len(initial_vals[0]) == 1: - output_signal_list = [ - Signal(intbv(not initial_val, min=0, max=2)) for - initial_val in initial_vals] - update_val = int(not initial_vals[0]) - else: - output_signal_list = [ - Signal(intbv(0, min=initial_val.min, max=initial_val.max)) for - initial_val in initial_vals] - update_val = 0 + output_signal_list = [ + Signal(not initial_val) for initial_val in initial_vals] - expected_output = [each_input._init for each_input in input_signal_list] + update_val = int(not initial_vals[0]) + + expected_output = [ + bool(each_input._init) for each_input in input_signal_list] N = 10 first = [True] @@ -201,10 +228,81 @@ def initial_value_list_bench(initial_vals, **kwargs): for i in range(signal_list_length): assert output_signal_list[i] == update_val - output_writer = canonical_list_writer(output_signal_list, clk) + output_writer = bool_list_writer(output_signal_list, clk) return clkgen, output_driver, drive_and_check, output_writer +@block +def assign_output(input_signal, output_signal): + @always_comb + def assignment(): + output_signal.next = input_signal + + return assignment + +@block +def initial_value_list_bench(initial_vals, **kwargs): + clk = Signal(bool(0)) + + input_signal_list = [Signal(initial_val) for initial_val in initial_vals] + + if len(initial_vals[0]) == 1: + + output_signal_list = [ + Signal(intbv(not initial_val, min=0, max=2)) for + initial_val in initial_vals] + update_val = int(not initial_vals[0]) + else: + output_signal_list = [ + Signal(intbv(0, min=initial_val.min, max=initial_val.max)) for + initial_val in initial_vals] + update_val = 0 + + expected_output = [each_input._init for each_input in input_signal_list] + + N = 10 + first = [True] + + signal_list_length = len(initial_vals) + + @instance + def clkgen(): + + clk.next = 0 + for n in range(N): + yield delay(10) + clk.next = not clk + + raise StopSimulation() + + # We assign each of the output drivers independently. + # This forces the output to be a wire (where appropriate) so we can + # check this type is handled properly too. + output_drivers = [] + for input_signal, output_signal in zip( + input_signal_list, output_signal_list): + + output_drivers.append(assign_output(input_signal, output_signal)) + + @always(clk.posedge) + def drive_and_check(): + + for i in range(signal_list_length): + input_signal_list[i].next = update_val + + if __debug__: + if first[0]: + for i in range(signal_list_length): + assert output_signal_list[i] == expected_output[i] + first[0] = False + else: + for i in range(signal_list_length): + assert output_signal_list[i] == update_val + + output_writer = canonical_list_writer(output_signal_list, clk) + + return clkgen, output_drivers, drive_and_check, output_writer + def runner(initial_val, tb=initial_value_bench, **kwargs): pre_toVerilog_initial_values = toVerilog.initial_values pre_toVHDL_initial_values = toVHDL.initial_values @@ -242,6 +340,12 @@ def test_signed(): runner(initial_val) +def test_bool(): + '''The correct initial value should be used for bool type signal. + ''' + initial_val = bool(randrange(0, 2)) + runner(initial_val) + def test_modbv(): '''The correct initial value should be used for modbv type signal. ''' @@ -344,12 +448,12 @@ def test_long_signals_list(): def test_bool_signals_list(): '''The correct initial value should be used for a boolean type signal lists ''' - initial_vals = [intbv(0, min=0, max=2) for each in range(10)] + initial_vals = [False for each in range(10)] - runner(initial_vals, tb=initial_value_list_bench) + runner(initial_vals, tb=initial_value_bool_list_bench) - initial_vals = [intbv(0, min=0, max=2)] * 10 - runner(initial_vals, tb=initial_value_list_bench) + initial_vals = [False] * 10 + runner(initial_vals, tb=initial_value_bool_list_bench) def test_init_used(): diff --git a/myhdl/test/core/test_Cosimulation.py b/myhdl/test/core/test_Cosimulation.py index 035b7eba..25e6d9b8 100644 --- a/myhdl/test/core/test_Cosimulation.py +++ b/myhdl/test/core/test_Cosimulation.py @@ -79,21 +79,6 @@ class TestCosimulation: with raises_kind(CosimulationError, _error.OSError): Cosimulation('bla -x 45') - def testNotUnique(self): - cosim1 = Cosimulation(exe + "cosimNotUnique", **allSigs) - with raises_kind(CosimulationError, _error.MultipleCosim): - Cosimulation(exe + "cosimNotUnique", **allSigs) - - @staticmethod - def cosimNotUnique(): - wt, rf = wtrf() - os.write(wt, b"TO 00 a 1") - os.read(rf, MAXLINE) - os.write(wt, b"FROM 00 d 1") - os.read(rf, MAXLINE) - os.write(wt, b"START") - os.read(rf, MAXLINE) - def testFromSignals(self): cosim = Cosimulation(exe + "cosimFromSignals", **allSigs) assert cosim._fromSignames == fromSignames diff --git a/scripts/ci.sh b/scripts/ci.sh index 93b3942c..a7cdfa14 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -18,6 +18,7 @@ run_test() { } foundError=0 + echo -e "Running $CI_TARGET tests\n" CI_TARGET=${CI_TARGET:-core} diff --git a/scripts/ci_deps.sh b/scripts/ci_deps.sh new file mode 100755 index 00000000..2b6446af --- /dev/null +++ b/scripts/ci_deps.sh @@ -0,0 +1,9 @@ +if [ "$CI_TARGET" == "iverilog" ]; then + sudo apt-get -qq update + sudo apt-get install iverilog +elif [ "$CI_TARGET" == "ghdl" ]; then + url=$(curl -s https://api.github.com/repos/tgingold/ghdl/releases/latest | jq -r ".assets[] | select (.name | test (\"ubuntu1_amd64\"))| .browser_download_url") + curl -Lo ghdl.deb $url + sudo dpkg -i ghdl.deb + sudo apt-get install -f +fi diff --git a/scripts/install_ghdl.sh b/scripts/install_ghdl.sh deleted file mode 100755 index ef655d42..00000000 --- a/scripts/install_ghdl.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/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