2015-09-10 23:33:45 -04:00
|
|
|
import operator
|
|
|
|
|
2015-09-12 07:34:07 -04:00
|
|
|
from migen.fhdl.structure import *
|
2015-09-19 02:56:26 -04:00
|
|
|
from migen.fhdl.structure import (_Operator, _Slice, _ArrayProxy,
|
|
|
|
_Assign, _Fragment)
|
2015-09-17 02:39:36 -04:00
|
|
|
from migen.fhdl.bitcontainer import flen
|
2015-09-17 05:24:20 -04:00
|
|
|
from migen.fhdl.tools import list_targets
|
2015-09-19 11:21:46 -04:00
|
|
|
from migen.fhdl.simplify import FullMemoryWE, MemoryToArray
|
2015-09-10 23:33:45 -04:00
|
|
|
|
|
|
|
|
2015-09-12 07:34:07 -04:00
|
|
|
__all__ = ["Simulator"]
|
|
|
|
|
|
|
|
|
2015-09-10 23:33:45 -04:00
|
|
|
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
|
|
|
|
|
2015-09-19 02:56:26 -04:00
|
|
|
def eval(self, node, postcommit=False):
|
2015-09-15 00:38:02 -04:00
|
|
|
if isinstance(node, Constant):
|
|
|
|
return node.value
|
2015-09-10 23:33:45 -04:00
|
|
|
elif isinstance(node, Signal):
|
2015-09-19 02:56:26 -04:00
|
|
|
if postcommit:
|
|
|
|
try:
|
|
|
|
return self.modifications[node]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
2015-09-10 23:33:45 -04:00
|
|
|
try:
|
|
|
|
return self.signal_values[node]
|
|
|
|
except KeyError:
|
2015-09-17 03:20:27 -04:00
|
|
|
return node.reset.value
|
2015-09-10 23:33:45 -04:00
|
|
|
elif isinstance(node, _Operator):
|
2015-09-19 02:56:26 -04:00
|
|
|
operands = [self.eval(o, postcommit) for o in node.operands]
|
2015-09-10 23:33:45 -04:00
|
|
|
if node.op == "-":
|
|
|
|
if len(operands) == 1:
|
|
|
|
return -operands[0]
|
|
|
|
else:
|
|
|
|
return operands[0] - operands[1]
|
2015-09-17 02:39:36 -04:00
|
|
|
elif node.op == "m":
|
|
|
|
return operands[1] if operands[0] else operands[2]
|
2015-09-10 23:33:45 -04:00
|
|
|
else:
|
|
|
|
return str2op[node.op](*operands)
|
2015-09-17 02:39:36 -04:00
|
|
|
elif isinstance(node, _Slice):
|
2015-09-19 02:56:26 -04:00
|
|
|
v = self.eval(node.value, postcommit)
|
2015-09-17 02:39:36 -04:00
|
|
|
idx = range(node.start, node.stop)
|
|
|
|
return sum(((v >> i) & 1) << j for j, i in enumerate(idx))
|
|
|
|
elif isinstance(node, Cat):
|
|
|
|
shift = 0
|
|
|
|
r = 0
|
|
|
|
for element in node.l:
|
|
|
|
nbits = flen(element)
|
|
|
|
# make value always positive
|
2015-09-19 02:56:26 -04:00
|
|
|
r |= (self.eval(element, postcommit) & (2**nbits-1)) << shift
|
2015-09-17 02:39:36 -04:00
|
|
|
shift += nbits
|
|
|
|
return r
|
2015-09-19 02:56:26 -04:00
|
|
|
elif isinstance(node, _ArrayProxy):
|
|
|
|
return self.eval(node.choices[self.eval(node.key, postcommit)],
|
|
|
|
postcommit)
|
2015-09-10 23:33:45 -04:00
|
|
|
else:
|
2015-09-19 11:21:46 -04:00
|
|
|
# TODO: ClockSignal, ResetSignal
|
2015-09-10 23:33:45 -04:00
|
|
|
raise NotImplementedError
|
|
|
|
|
2015-09-19 02:56:26 -04:00
|
|
|
def assign(self, node, value):
|
|
|
|
if isinstance(node, Signal):
|
|
|
|
assert not node.variable
|
|
|
|
value = value & (2**node.nbits - 1)
|
|
|
|
if node.signed and (value & 2**(node.nbits - 1)):
|
|
|
|
value -= 2**node.nbits
|
|
|
|
self.modifications[node] = value
|
|
|
|
elif isinstance(node, Cat):
|
|
|
|
for element in node.l:
|
|
|
|
nbits = flen(element)
|
|
|
|
self.assign(element, value & (2**nbits-1))
|
|
|
|
value >>= nbits
|
2015-09-19 11:21:46 -04:00
|
|
|
elif isinstance(node, _Slice):
|
2015-09-19 02:56:26 -04:00
|
|
|
full_value = self.eval(node, True)
|
|
|
|
# clear bits assigned to by the slice
|
|
|
|
full_value &= ~((2**node.stop-1) - (2**node.start-1))
|
|
|
|
# set them to the new value
|
|
|
|
value &= 2**(node.stop - node.start)-1
|
|
|
|
full_value |= value << node.start
|
|
|
|
self.assign(node, full_value)
|
|
|
|
elif isinstance(node, _ArrayProxy):
|
|
|
|
self.assign(node.choices[self.eval(node.key)], value)
|
|
|
|
else:
|
2015-09-19 11:21:46 -04:00
|
|
|
# TODO: ClockSignal, ResetSignal
|
2015-09-19 02:56:26 -04:00
|
|
|
raise NotImplementedError
|
2015-09-11 00:44:14 -04:00
|
|
|
|
2015-09-10 23:33:45 -04:00
|
|
|
def execute(self, statements):
|
|
|
|
for s in statements:
|
|
|
|
if isinstance(s, _Assign):
|
2015-09-19 02:56:26 -04:00
|
|
|
self.assign(s.l, self.eval(s.r))
|
2015-09-10 23:33:45 -04:00
|
|
|
elif isinstance(s, If):
|
2015-09-11 00:44:14 -04:00
|
|
|
if self.eval(s.cond):
|
2015-09-10 23:33:45 -04:00
|
|
|
self.execute(s.t)
|
|
|
|
else:
|
|
|
|
self.execute(s.f)
|
2015-09-17 05:25:06 -04:00
|
|
|
elif isinstance(s, Case):
|
|
|
|
test = self.eval(s.test)
|
|
|
|
for k, v in s.cases.items():
|
|
|
|
if isinstance(k, Constant) and k.value == test:
|
|
|
|
self.execute(v)
|
|
|
|
return
|
|
|
|
if "default" in s.cases:
|
|
|
|
self.execute(s.cases["default"])
|
2015-09-10 23:33:45 -04:00
|
|
|
else:
|
|
|
|
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]
|
|
|
|
|
2015-09-19 11:21:46 -04:00
|
|
|
FullMemoryWE().transform_fragment(None, self.fragment)
|
|
|
|
MemoryToArray().transform_fragment(None, self.fragment)
|
2015-09-19 02:56:26 -04:00
|
|
|
# TODO: insert_resets on sync
|
|
|
|
# comb signals return to their reset value if nothing assigns them
|
2015-09-17 05:24:20 -04:00
|
|
|
self.fragment.comb[0:0] = [s.eq(s.reset)
|
|
|
|
for s in list_targets(self.fragment.comb)]
|
|
|
|
|
2015-09-10 23:33:45 -04:00
|
|
|
self.time = TimeManager(clocks)
|
|
|
|
self.evaluator = Evaluator()
|
|
|
|
|
2015-09-12 04:27:59 -04:00
|
|
|
def _commit_and_comb_propagate(self):
|
2015-09-17 05:24:20 -04:00
|
|
|
# TODO: optimize
|
2015-09-12 04:27:59 -04:00
|
|
|
modified = self.evaluator.commit()
|
2015-09-10 23:33:45 -04:00
|
|
|
while modified:
|
2015-09-17 05:24:20 -04:00
|
|
|
self.evaluator.execute(self.fragment.comb)
|
2015-09-10 23:33:45 -04:00
|
|
|
modified = self.evaluator.commit()
|
|
|
|
|
2015-09-12 04:01:53 -04:00
|
|
|
def _eval_nested_lists(self, x):
|
|
|
|
if isinstance(x, list):
|
|
|
|
return [self._eval_nested_lists(e) for e in x]
|
|
|
|
elif isinstance(x, Signal):
|
|
|
|
return self.evaluator.eval(x)
|
|
|
|
else:
|
|
|
|
raise ValueError
|
|
|
|
|
2015-09-11 00:44:14 -04:00
|
|
|
def _process_generators(self, cd):
|
2015-09-12 03:12:57 -04:00
|
|
|
exhausted = []
|
|
|
|
for generator in self.generators[cd]:
|
|
|
|
reply = None
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
request = generator.send(reply)
|
|
|
|
if request is None:
|
|
|
|
break # next cycle
|
|
|
|
elif isinstance(request, tuple):
|
|
|
|
self.evaluator.assign(*request)
|
|
|
|
else:
|
2015-09-12 04:01:53 -04:00
|
|
|
reply = self._eval_nested_lists(request)
|
2015-09-12 03:12:57 -04:00
|
|
|
except StopIteration:
|
|
|
|
exhausted.append(generator)
|
|
|
|
break
|
|
|
|
for generator in exhausted:
|
|
|
|
self.generators[cd].remove(generator)
|
2015-09-11 00:44:14 -04:00
|
|
|
|
2015-09-10 23:33:45 -04:00
|
|
|
def _continue_simulation(self):
|
|
|
|
# TODO: passive generators
|
|
|
|
return any(self.generators.values())
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
self.evaluator.execute(self.fragment.comb)
|
2015-09-12 04:27:59 -04:00
|
|
|
self._commit_and_comb_propagate()
|
2015-09-10 23:33:45 -04:00
|
|
|
|
|
|
|
while True:
|
|
|
|
cds = self.time.tick()
|
|
|
|
for cd in cds:
|
2015-09-12 03:12:57 -04:00
|
|
|
if cd in self.fragment.sync:
|
|
|
|
self.evaluator.execute(self.fragment.sync[cd])
|
|
|
|
if cd in self.generators:
|
|
|
|
self._process_generators(cd)
|
2015-09-12 04:27:59 -04:00
|
|
|
self._commit_and_comb_propagate()
|
2015-09-11 00:44:14 -04:00
|
|
|
|
2015-09-10 23:33:45 -04:00
|
|
|
if not self._continue_simulation():
|
|
|
|
break
|