sim: VCD output support

This commit is contained in:
Sebastien Bourdeauducq 2015-09-21 21:20:31 +08:00
parent 34ce6b077f
commit a67b4baa0c
9 changed files with 115 additions and 14 deletions

View file

@ -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")

View file

@ -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")

View file

@ -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")

View file

@ -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)

View file

@ -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))

1
migen/sim/__init__.py Normal file
View file

@ -0,0 +1 @@
from migen.sim.core import Simulator, run_simulation

View file

@ -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()

76
migen/sim/vcd.py Normal file
View file

@ -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

View file

@ -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)