new simulator: basic execution
[litex.git] / migen / sim.py
1 import operator
2 from collections import defaultdict
3
4 from migen.fhdl.std import *
5 from migen.fhdl.structure import _Operator, _Assign, _Fragment
6 from migen.fhdl.tools import list_inputs
7
8
9 class ClockState:
10 def __init__(self, period, times_before_tick):
11 self.period = period
12 self.times_before_tick = times_before_tick
13
14
15 class TimeManager:
16 def __init__(self, description):
17 self.clocks = dict()
18
19 for k, v in description.items():
20 if not isinstance(v, tuple):
21 v = v, 0
22 self.clocks[k] = ClockState(v[0], v[0] - v[1])
23
24 def tick(self):
25 r = set()
26 dt = min(cs.times_before_tick for cs in self.clocks.values())
27 for k, cs in self.clocks.items():
28 if cs.times_before_tick == dt:
29 r.add(k)
30 cs.times_before_tick -= dt
31 if not cs.times_before_tick:
32 cs.times_before_tick += cs.period
33 return r
34
35
36 str2op = {
37 "~": operator.invert,
38 "+": operator.add,
39 "-": operator.sub,
40 "*": operator.mul,
41
42 ">>>": operator.rshift,
43 "<<<": operator.lshift,
44
45 "&": operator.and_,
46 "^": operator.xor,
47 "|": operator.or_,
48
49 "<": operator.lt,
50 "<=": operator.le,
51 "==": operator.eq,
52 "!=": operator.ne,
53 ">": operator.gt,
54 ">=": operator.ge,
55 }
56
57
58 class Evaluator:
59 def __init__(self):
60 self.signal_values = dict()
61 self.modifications = dict()
62
63 def commit(self):
64 r = set()
65 for k, v in self.modifications.items():
66 if k not in self.signal_values or self.signal_values[k] != v:
67 self.signal_values[k] = v
68 r.add(k)
69 self.modifications.clear()
70 return r
71
72 def _eval(self, node):
73 if isinstance(node, (int, bool)):
74 return node
75 elif isinstance(node, Signal):
76 try:
77 return self.signal_values[node]
78 except KeyError:
79 return node.reset
80 elif isinstance(node, _Operator):
81 operands = [self._eval(o) for o in node.operands]
82 if node.op == "-":
83 if len(operands) == 1:
84 return -operands[0]
85 else:
86 return operands[0] - operands[1]
87 else:
88 return str2op[node.op](*operands)
89 else:
90 # TODO: Cat, Slice, Array, ClockSignal, ResetSignal, Memory
91 raise NotImplementedError
92
93 def execute(self, statements):
94 for s in statements:
95 if isinstance(s, _Assign):
96 value = self._eval(s.r)
97 if isinstance(s.l, Signal):
98 value = value & (2**s.l.nbits - 1)
99 if s.l.signed and (value & 2**(s.l.nbits - 1)):
100 value -= 2**s.l.nbits
101 self.modifications[s.l] = value
102 else:
103 # TODO: Cat, Slice, Array, ClockSignal, ResetSignal, Memory
104 raise NotImplementedError
105 elif isinstance(s, If):
106 if self._eval(s.cond):
107 self.execute(s.t)
108 else:
109 self.execute(s.f)
110 else:
111 # TODO: Case
112 raise NotImplementedError
113
114
115 # TODO: instances via Iverilog/VPI
116 # TODO: VCD output
117 class Simulator:
118 def __init__(self, fragment_or_module, generators, clocks={"sys": 100}):
119 if isinstance(fragment_or_module, _Fragment):
120 self.fragment = fragment_or_module
121 else:
122 self.fragment = fragment_or_module.get_fragment()
123 if not isinstance(generators, dict):
124 generators = {"sys": generators}
125 self.generators = dict()
126 for k, v in generators.items():
127 if isinstance(v, list):
128 self.generators[k] = v
129 else:
130 self.generators[k] = [v]
131
132 # TODO: insert_resets
133 self.time = TimeManager(clocks)
134 self.evaluator = Evaluator()
135
136 self.comb_dependent_statements = defaultdict(list)
137 for statement in self.fragment.comb:
138 for signal in list_inputs(statement):
139 self.comb_dependent_statements[signal].append(statement)
140
141 def _comb_propagate(self, modified):
142 while modified:
143 for signal in modified:
144 self.evaluator.execute(self.comb_dependent_statements[signal])
145 modified = self.evaluator.commit()
146
147 def _continue_simulation(self):
148 # TODO: passive generators
149 return any(self.generators.values())
150
151 def run(self):
152 self.evaluator.execute(self.fragment.comb)
153 self._comb_propagate(self.evaluator.commit())
154
155 while True:
156 print(self.evaluator.signal_values)
157 cds = self.time.tick()
158 for cd in cds:
159 self.evaluator.execute(self.fragment.sync[cd])
160 self._comb_propagate(self.evaluator.commit())
161 if not self._continue_simulation():
162 break