new simulator: basic execution
This commit is contained in:
parent
49ef182305
commit
10d89d81f4
|
@ -0,0 +1,162 @@
|
|||
import operator
|
||||
from collections import defaultdict
|
||||
|
||||
from migen.fhdl.std import *
|
||||
from migen.fhdl.structure import _Operator, _Assign, _Fragment
|
||||
from migen.fhdl.tools import list_inputs
|
||||
|
||||
|
||||
class ClockState:
|
||||
def __init__(self, period, times_before_tick):
|
||||
self.period = period
|
||||
self.times_before_tick = times_before_tick
|
||||
|
||||
|
||||
class TimeManager:
|
||||
def __init__(self, description):
|
||||
self.clocks = dict()
|
||||
|
||||
for k, v in description.items():
|
||||
if not isinstance(v, tuple):
|
||||
v = v, 0
|
||||
self.clocks[k] = ClockState(v[0], v[0] - v[1])
|
||||
|
||||
def tick(self):
|
||||
r = set()
|
||||
dt = min(cs.times_before_tick for cs in self.clocks.values())
|
||||
for k, cs in self.clocks.items():
|
||||
if cs.times_before_tick == dt:
|
||||
r.add(k)
|
||||
cs.times_before_tick -= dt
|
||||
if not cs.times_before_tick:
|
||||
cs.times_before_tick += cs.period
|
||||
return r
|
||||
|
||||
|
||||
str2op = {
|
||||
"~": operator.invert,
|
||||
"+": operator.add,
|
||||
"-": operator.sub,
|
||||
"*": operator.mul,
|
||||
|
||||
">>>": operator.rshift,
|
||||
"<<<": operator.lshift,
|
||||
|
||||
"&": operator.and_,
|
||||
"^": operator.xor,
|
||||
"|": operator.or_,
|
||||
|
||||
"<": operator.lt,
|
||||
"<=": operator.le,
|
||||
"==": operator.eq,
|
||||
"!=": operator.ne,
|
||||
">": operator.gt,
|
||||
">=": operator.ge,
|
||||
}
|
||||
|
||||
|
||||
class Evaluator:
|
||||
def __init__(self):
|
||||
self.signal_values = dict()
|
||||
self.modifications = dict()
|
||||
|
||||
def commit(self):
|
||||
r = set()
|
||||
for k, v in self.modifications.items():
|
||||
if k not in self.signal_values or self.signal_values[k] != v:
|
||||
self.signal_values[k] = v
|
||||
r.add(k)
|
||||
self.modifications.clear()
|
||||
return r
|
||||
|
||||
def _eval(self, node):
|
||||
if isinstance(node, (int, bool)):
|
||||
return node
|
||||
elif isinstance(node, Signal):
|
||||
try:
|
||||
return self.signal_values[node]
|
||||
except KeyError:
|
||||
return node.reset
|
||||
elif isinstance(node, _Operator):
|
||||
operands = [self._eval(o) for o in node.operands]
|
||||
if node.op == "-":
|
||||
if len(operands) == 1:
|
||||
return -operands[0]
|
||||
else:
|
||||
return operands[0] - operands[1]
|
||||
else:
|
||||
return str2op[node.op](*operands)
|
||||
else:
|
||||
# TODO: Cat, Slice, Array, ClockSignal, ResetSignal, Memory
|
||||
raise NotImplementedError
|
||||
|
||||
def execute(self, statements):
|
||||
for s in statements:
|
||||
if isinstance(s, _Assign):
|
||||
value = self._eval(s.r)
|
||||
if isinstance(s.l, Signal):
|
||||
value = value & (2**s.l.nbits - 1)
|
||||
if s.l.signed and (value & 2**(s.l.nbits - 1)):
|
||||
value -= 2**s.l.nbits
|
||||
self.modifications[s.l] = value
|
||||
else:
|
||||
# TODO: Cat, Slice, Array, ClockSignal, ResetSignal, Memory
|
||||
raise NotImplementedError
|
||||
elif isinstance(s, If):
|
||||
if self._eval(s.cond):
|
||||
self.execute(s.t)
|
||||
else:
|
||||
self.execute(s.f)
|
||||
else:
|
||||
# TODO: Case
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
# TODO: instances via Iverilog/VPI
|
||||
# TODO: VCD output
|
||||
class Simulator:
|
||||
def __init__(self, fragment_or_module, generators, clocks={"sys": 100}):
|
||||
if isinstance(fragment_or_module, _Fragment):
|
||||
self.fragment = fragment_or_module
|
||||
else:
|
||||
self.fragment = fragment_or_module.get_fragment()
|
||||
if not isinstance(generators, dict):
|
||||
generators = {"sys": generators}
|
||||
self.generators = dict()
|
||||
for k, v in generators.items():
|
||||
if isinstance(v, list):
|
||||
self.generators[k] = v
|
||||
else:
|
||||
self.generators[k] = [v]
|
||||
|
||||
# TODO: insert_resets
|
||||
self.time = TimeManager(clocks)
|
||||
self.evaluator = Evaluator()
|
||||
|
||||
self.comb_dependent_statements = defaultdict(list)
|
||||
for statement in self.fragment.comb:
|
||||
for signal in list_inputs(statement):
|
||||
self.comb_dependent_statements[signal].append(statement)
|
||||
|
||||
def _comb_propagate(self, modified):
|
||||
while modified:
|
||||
for signal in modified:
|
||||
self.evaluator.execute(self.comb_dependent_statements[signal])
|
||||
modified = self.evaluator.commit()
|
||||
|
||||
def _continue_simulation(self):
|
||||
# TODO: passive generators
|
||||
return any(self.generators.values())
|
||||
|
||||
def run(self):
|
||||
self.evaluator.execute(self.fragment.comb)
|
||||
self._comb_propagate(self.evaluator.commit())
|
||||
|
||||
while True:
|
||||
print(self.evaluator.signal_values)
|
||||
cds = self.time.tick()
|
||||
for cd in cds:
|
||||
self.evaluator.execute(self.fragment.sync[cd])
|
||||
self._comb_propagate(self.evaluator.commit())
|
||||
if not self._continue_simulation():
|
||||
break
|
Loading…
Reference in New Issue