mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
sim: VCD output support
This commit is contained in:
parent
34ce6b077f
commit
a67b4baa0c
9 changed files with 115 additions and 14 deletions
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
1
migen/sim/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from migen.sim.core import Simulator, run_simulation
|
|
@ -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
76
migen/sim/vcd.py
Normal 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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue