-
Notifications
You must be signed in to change notification settings - Fork 22
/
python_simulator.py
386 lines (315 loc) · 12.6 KB
/
python_simulator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
import sys
from abc import abstractmethod
if sys.version_info < (3, 4):
import abc
ABC = abc.ABCMeta('ABC', (object,), {})
else:
from abc import ABC
from collections import namedtuple
from itertools import product
from .simulator import CircuitSimulator, ExecutionState
from ..transforms import flatten, setup_clocks
from ..circuit import *
from ..scope import *
from ..bit import VCC, GND, BitType, _BitType
from ..array import ArrayType
from ..bits import SIntType, BitsType
from ..bit_vector import BitVector
from ..bitutils import seq2int
from ..clock import ClockType
__all__ = ['PythonSimulator', 'testvectors']
ExecutionOrder = namedtuple('ExecutionOrder', ['stateful', 'combinational'])
class PythonSimulatorException(Exception):
pass
class SimPrimitive:
def __init__(self, primitive, value_store):
if primitive.simulate is None:
raise ValueError("Cannot simulate {} of type {} because it does not have a Python simulate method".format(primitive, type(primitive)))
self.primitive = primitive
self.inputs = []
self.outputs = []
self.value_store = value_store
self.state_store = {}
for bit in self.primitive.interface.ports.values():
if not isinstance(bit, ArrayType):
bit = [bit]
for b in bit:
if b.isinput():
self.inputs.append(b)
else:
self.outputs.append(b)
def stateful(self):
return self.primitive.stateful
def inputs_satisfied(self):
for i in self.inputs:
if not self.value_store.value_initialized(i):
return False
return True
def initialize_outputs(self):
# Initializes all outputs to False, should perhaps
# initialize based on starting inputs?
for o in self.outputs:
self.value_store.set_value(o, False)
def simulate(self):
self.primitive.simulate(self.value_store, self.state_store)
class WatchPoint:
idx = 0
def __init__(self, bit, scope, simulator, value):
self.bit = bit
self.scope = scope
self.simulator = simulator
self.value = value
self.old_val = self.simulator.get_value(self.bit, self.scope)
WatchPoint.idx += 1
self.idx = WatchPoint.idx
def was_triggered(self):
new_val = self.simulator.get_value(self.bit, self.scope)
triggered = new_val != self.old_val
if self.value:
if self.value != new_val:
triggered = False
self.old_val = new_val
return triggered
class ValueStore:
def __init__(self):
self.value_map = {}
def value_initialized(self, bit):
if isinstance(bit, ArrayType):
for b in bit:
if not self.value_initialized(b):
return False
return True
if bit.isinput():
bit = bit.value()
if bit.const():
return True
return bit in self.value_map
def get_value(self, bit):
if isinstance(bit, ArrayType):
return [self.get_value(b) for b in bit]
if bit.isinput():
bit = bit.value()
if bit.const():
return True if bit is VCC else False
return self.value_map[bit]
def set_value(self, bit, newval):
if isinstance(bit, ArrayType):
if isinstance(newval, BitVector):
newval = newval.as_bool_list()
elif isinstance(newval, BitsType):
if not newval.const():
raise ValueError("Calling set_value with a BitsType only works with a constant")
newval = newval.bits()
for b,v in zip(bit, newval):
self.set_value(b, v)
return
if isinstance(newval, BitVector):
assert len(newval) == 1
newval = newval.as_bool_list()[0]
if isinstance(newval, int) and newval in {0, 1}:
newval = bool(newval)
assert isinstance(newval, bool), "Can only set boolean values"
assert bit.isoutput()
self.value_map[bit] = newval
class PythonSimulator(CircuitSimulator):
def __setup_primitives(self):
wrapped = []
for primitive in self.circuit.instances:
wrapped.append(SimPrimitive(primitive, self.value_store))
return wrapped
def initialize(self, bit):
if isinstance(bit, ArrayType):
for b in bit:
self.initialize(b)
else:
if bit.isoutput():
self.circuit_inputs.append(bit)
self.value_store.set_value(bit, False)
else:
self.circuit_outputs.append(bit)
def __setup_circuit(self, clock):
if clock is not None:
clock = self.txfm.get_new_bit(clock, self.default_scope)
self.clock = clock
self.circuit_inputs = []
self.circuit_outputs = []
for name, bit in self.circuit.interface.ports.items():
self.initialize(bit)
def __outputs_initialized(self):
for bit in self.circuit_outputs:
assert bit.isinput()
if not self.value_store.value_initialized(bit):
return False
return True
def __sort_state_primitives(self, state_primitives):
"""
State primitives should be sorted in reversed topological order.
This ensures that the simulation order of the stateful elements
is correct.
Intuition:
If the output value of a state element `x` feeds into the input of
another state element `y`,
`y` should perform it's simulation before `x` because it will use
the value of the signal on the previous clock cycle.
"""
sorted_state_primitives = []
for primitive_1 in state_primitives:
inserted = False
for index, primitive_2 in enumerate(sorted_state_primitives):
for output in primitive_1.inputs:
if any(output.value() is x for x in primitive_2.inputs):
sorted_state_primitives.insert(index, primitive_1)
inserted = True
break
if inserted:
break
if not inserted:
sorted_state_primitives.append(primitive_1)
sorted_state_primitives.reverse()
return sorted_state_primitives
def __get_ordered_primitives(self, unordered_primitives):
state_primitives = []
after_state = []
state_primitives = []
for primitive in unordered_primitives:
if primitive.stateful():
primitive.initialize_outputs()
state_primitives.append(primitive)
sorted_state_primitives = self.__sort_state_primitives(state_primitives)
unordered_primitives[:] = [u for u in unordered_primitives if not u.stateful()]
combinational = []
while len(unordered_primitives) > 0:
found = False
for primitive in unordered_primitives:
if primitive.inputs_satisfied():
primitive.initialize_outputs()
combinational.append(primitive)
unordered_primitives.remove(primitive)
found = True
break
assert found, "Some circuits have unsatisfied inputs"
return ExecutionOrder(stateful=sorted_state_primitives, combinational=combinational)
def __step(self):
if self.clock is None:
raise PythonSimulatorException("Cannot step a simulated circuit "
"without a clock, did you pass a clock during "
"initialization?")
cur_clock_val = self.value_store.get_value(self.clock)
self.value_store.set_value(self.clock, not cur_clock_val)
def __init__(self, main_circuit, clock=None):
if isinstance(main_circuit, CircuitType):
raise ValueError("PythonSimulator must be called with a Circuit definition, not an instance")
if clock is not None and not isinstance(clock, ClockType):
raise ValueError("clock must be a ClockType or None")
setup_clocks(main_circuit)
self.main_circuit = main_circuit
self.txfm = flatten(main_circuit)
self.circuit = self.txfm.circuit
self.value_store = ValueStore()
self.default_scope = Scope()
self.__setup_circuit(clock)
self.watchpoints = []
primitives = self.__setup_primitives()
self.execution_order = self.__get_ordered_primitives(primitives)
assert self.__outputs_initialized(), "All circuit outputs not initialized."
def get_capabilities(self):
return []
def get_value(self, bit, scope=None):
if scope is None:
scope = self.default_scope
newbit = self.txfm.get_new_bit(bit, scope)
if newbit is None:
return None
try:
return self.value_store.get_value(newbit)
except KeyError:
return None
def set_value(self, bit, newval, scope=None):
if scope is None:
scope = self.default_scope
newbit = self.txfm.get_new_bit(bit, scope)
if not self.is_circuit_input(newbit):
message = "Only setting main's inputs is supported (Trying to set: {})".format(bit)
raise PythonSimulatorException(message)
else:
self.value_store.set_value(newbit, newval)
def is_circuit_input(self, value):
"""
Checks if `value` is in `self.circuit_inputs`.
If `value` is an `ArrayType`, it recursively checks the elements
"""
if isinstance(value, _BitType):
return any(value is x for x in self.circuit_inputs)
elif isinstance(value, ArrayType):
return all(self.is_circuit_input(elem) for elem in value)
else:
raise NotImplementedError(type(value))
def advance(self, n=1):
cycles = 0
for i in range(0, n):
self.__step()
state = self.evaluate()
if not state.clock:
cycles += 1
if state.triggered_points:
return ExecutionState(triggered_points=state.triggered_points, clock=state.clock, cycles=cycles)
return ExecutionState(triggered_points=[], clock=self.get_clock_value(), cycles=cycles)
def evaluate(self, no_update=False):
for primitive in self.execution_order.stateful:
primitive.simulate()
for primitive in self.execution_order.combinational:
primitive.simulate()
triggered = []
for watch in self.watchpoints:
if watch.was_triggered():
triggered.append(watch)
return ExecutionState(triggered_points=triggered, clock=self.get_clock_value(), cycles=0)
def get_clock_value(self):
"""
Looks up the value of `self.clock` in `self.value_store`
Returns None if self.clock is None (circuit doesn't have a clock)
"""
if self.clock is not None:
return self.value_store.get_value(self.clock)
return None
def rewind(self, halfcycles):
raise PythonSimulatorException("Reversing not currently supported")
def cont(self):
cycles = 0
while True:
self.__step()
state = self.evaluate()
if not state.clock:
cycles += 1
if state.triggered_points:
return ExecutionState(triggered_points=state.triggered_points, clock=state.clock, cycles=cycles)
def add_watchpoint(self, bit, scope, value=None):
self.watchpoints.append(WatchPoint(bit, scope, self, value))
return self.watchpoints[-1].idx
def delete_watchpoint(self, num):
for w in self.watchpoints:
if w.idx == num:
del w
return True
return False
def __call__(self, *largs):
circuit = self.main_circuit
j = 0
for name, port in circuit.interface.ports.items():
if port.isoutput():
n = 1
if isinstance(port, ArrayType):
n = type(port).N
val = BitVector(largs[j], num_bits=n)
self.set_value(getattr(circuit, name), val)
j += 1
self.evaluate()
outs = []
for name, port in circuit.interface.ports.items():
if port.isinput():
val = self.get_value(getattr(circuit, name))
val = int(val) if isinstance(val, bool) else seq2int(val)
outs.append(val)
if len(outs) == 1:
return outs[0]
return tuple(outs)