From a67b4baa0c7eae75217751bd9fea2afcf30d4af8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Sep 2015 21:20:31 +0800 Subject: [PATCH] sim: VCD output support --- examples/basic/graycounter.py | 2 +- examples/sim/basic1.py | 2 +- examples/sim/basic2.py | 2 +- examples/sim/fir.py | 2 +- examples/sim/memory.py | 2 +- migen/sim/__init__.py | 1 + migen/{sim.py => sim/core.py} | 40 ++++++++++++++---- migen/sim/vcd.py | 76 +++++++++++++++++++++++++++++++++++ migen/test/support.py | 2 +- 9 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 migen/sim/__init__.py rename migen/{sim.py => sim/core.py} (90%) create mode 100644 migen/sim/vcd.py diff --git a/examples/basic/graycounter.py b/examples/basic/graycounter.py index bf73e789..f4235793 100644 --- a/examples/basic/graycounter.py +++ b/examples/basic/graycounter.py @@ -15,4 +15,4 @@ def tb(dut): if __name__ == "__main__": dut = GrayCounter(3) - Simulator(dut, tb(dut)).run() + run_simulation(dut, tb(dut), vcd_name="graycounter.vcd") diff --git a/examples/sim/basic1.py b/examples/sim/basic1.py index e9426fce..62afd21b 100644 --- a/examples/sim/basic1.py +++ b/examples/sim/basic1.py @@ -26,4 +26,4 @@ def counter_test(dut): if __name__ == "__main__": dut = Counter() - Simulator(dut, counter_test(dut)).run() + run_simulation(dut, counter_test(dut), vcd_name="basic1.vcd") diff --git a/examples/sim/basic2.py b/examples/sim/basic2.py index 51828602..45bc676f 100644 --- a/examples/sim/basic2.py +++ b/examples/sim/basic2.py @@ -34,4 +34,4 @@ def counter_test(dut): if __name__ == "__main__": dut = Counter() - Simulator(dut, counter_test(dut)).run() + run_simulation(dut, counter_test(dut), vcd_name="basic2.vcd") diff --git a/examples/sim/fir.py b/examples/sim/fir.py index a7b98036..67a78bc7 100644 --- a/examples/sim/fir.py +++ b/examples/sim/fir.py @@ -55,7 +55,7 @@ if __name__ == "__main__": for frequency in [0.05, 0.1, 0.25]: dut = FIR(coef) tb = fir_tb(dut, frequency, in_signals, out_signals) - Simulator(dut, tb).run() + run_simulation(dut, tb) # Plot data from the input and output waveforms. plt.plot(in_signals) diff --git a/examples/sim/memory.py b/examples/sim/memory.py index 4d8ca675..51a0c329 100644 --- a/examples/sim/memory.py +++ b/examples/sim/memory.py @@ -23,4 +23,4 @@ def memory_test(dut): if __name__ == "__main__": dut = Mem() - Simulator(dut, memory_test(dut)).run() + run_simulation(dut, memory_test(dut)) diff --git a/migen/sim/__init__.py b/migen/sim/__init__.py new file mode 100644 index 00000000..d99780b8 --- /dev/null +++ b/migen/sim/__init__.py @@ -0,0 +1 @@ +from migen.sim.core import Simulator, run_simulation diff --git a/migen/sim.py b/migen/sim/core.py similarity index 90% rename from migen/sim.py rename to migen/sim/core.py index eee9a41b..1514dfd3 100644 --- a/migen/sim.py +++ b/migen/sim/core.py @@ -5,12 +5,10 @@ from migen.fhdl.structure import (_Value, _Statement, _Operator, _Slice, _ArrayProxy, _Assign, _Fragment) from migen.fhdl.bitcontainer import flen -from migen.fhdl.tools import list_targets +from migen.fhdl.tools import list_signals, list_targets from migen.fhdl.simplify import MemoryToArray from migen.fhdl.specials import _MemoryLocation - - -__all__ = ["Simulator"] +from migen.sim.vcd import VCDWriter, DummyVCDWriter class ClockState: @@ -37,7 +35,7 @@ class TimeManager: cs.times_before_tick -= dt if not cs.times_before_tick: cs.times_before_tick += cs.period - return r + return dt, r str2op = { @@ -175,9 +173,8 @@ class Evaluator: # TODO: instances via Iverilog/VPI -# TODO: VCD output class Simulator: - def __init__(self, fragment_or_module, generators, clocks={"sys": 100}): + def __init__(self, fragment_or_module, generators, clocks={"sys": 10}, vcd_name=None): if isinstance(fragment_or_module, _Fragment): self.fragment = fragment_or_module else: @@ -198,15 +195,36 @@ class Simulator: self.fragment.comb[0:0] = [s.eq(s.reset) for s in list_targets(self.fragment.comb)] + if vcd_name is None: + self.vcd = DummyVCDWriter() + else: + signals = sorted(list_signals(self.fragment), + key=lambda x: x.duid) + self.vcd = VCDWriter(vcd_name, signals) + self.time = TimeManager(clocks) self.evaluator = Evaluator(mta.replacements) + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def close(self): + self.vcd.close() + def _commit_and_comb_propagate(self): # TODO: optimize + all_modified = set() modified = self.evaluator.commit() + all_modified |= modified while modified: self.evaluator.execute(self.fragment.comb) modified = self.evaluator.commit() + all_modified |= modified + for signal in all_modified: + self.vcd.set(signal, self.evaluator.signal_values[signal]) def _evalexec_nested_lists(self, x): if isinstance(x, list): @@ -245,7 +263,8 @@ class Simulator: self._commit_and_comb_propagate() while True: - cds = self.time.tick() + dt, cds = self.time.tick() + self.vcd.delay(dt) for cd in cds: if cd in self.fragment.sync: self.evaluator.execute(self.fragment.sync[cd]) @@ -255,3 +274,8 @@ class Simulator: if not self._continue_simulation(): break + + +def run_simulation(*args, **kwargs): + with Simulator(*args, **kwargs) as s: + s.run() diff --git a/migen/sim/vcd.py b/migen/sim/vcd.py new file mode 100644 index 00000000..3fdb0a05 --- /dev/null +++ b/migen/sim/vcd.py @@ -0,0 +1,76 @@ +from itertools import count + +from migen.fhdl.bitcontainer import flen +from migen.fhdl.namer import build_namespace + + +def vcd_codes(): + codechars = [chr(i) for i in range(33, 127)] + for n in count(): + q, r = divmod(n, len(codechars)) + code = codechars[r] + while q > 0: + q, r = divmod(q, len(codechars)) + code = codechars[r] + code + yield code + + +class VCDWriter: + def __init__(self, filename, signals): + self.fo = open(filename, "w") + self.codes = dict() + self.signal_values = dict() + self.t = 0 + + try: + ns = build_namespace(signals) + codes = vcd_codes() + for signal in signals: + name = ns.get_name(signal) + code = next(codes) + self.codes[signal] = code + self.fo.write("$var wire {len} {code} {name} $end\n" + .format(name=name, code=code, len=flen(signal))) + self.fo.write("$dumpvars\n") + for signal in signals: + value = signal.reset.value + self._write_value(signal, value) + self.signal_values[signal] = value + self.fo.write("$end\n") + self.fo.write("#0\n") + except: + self.close() + raise + + def _write_value(self, signal, value): + l = flen(signal) + if value < 0: + value += 2**l + if l > 1: + fmtstr = "b{:0" + str(l) + "b} {}\n" + else: + fmtstr = "{}{}\n" + self.fo.write(fmtstr.format(value, self.codes[signal])) + + def set(self, signal, value): + if self.signal_values[signal] != value: + self._write_value(signal, value) + self.signal_values[signal] = value + + def delay(self, delay): + self.t += delay + self.fo.write("#{}\n".format(self.t)) + + def close(self): + self.fo.close() + + +class DummyVCDWriter: + def set(self, signal, value): + pass + + def delay(self, delay): + pass + + def close(self): + pass diff --git a/migen/test/support.py b/migen/test/support.py index 66c141bf..c1bb4697 100644 --- a/migen/test/support.py +++ b/migen/test/support.py @@ -10,4 +10,4 @@ class SimCase: verilog.convert(self.tb) def run_with(self, generator): - Simulator(self.tb, generator).run() + run_simulation(self.tb, generator) -- 2.30.2