hdl.ast: implement Initial.
[nmigen.git] / nmigen / back / pysim.py
1 import math
2 import inspect
3 import warnings
4 from contextlib import contextmanager
5 from bitarray import bitarray
6 from vcd import VCDWriter
7 from vcd.gtkw import GTKWSave
8
9 from ..tools import flatten
10 from ..hdl.ast import *
11 from ..hdl.ir import *
12 from ..hdl.xfrm import ValueVisitor, StatementVisitor
13 from ..hdl.ast import DUID
14 from ..hdl.dsl import Module
15 from ..hdl.cd import ClockDomain
16
17
18 __all__ = ["Simulator", "Delay", "Tick", "Passive", "DeadlineError"]
19
20
21 class DeadlineError(Exception):
22 pass
23
24
25 class _State:
26 __slots__ = ("curr", "curr_dirty", "next", "next_dirty")
27
28 def __init__(self):
29 self.curr = []
30 self.next = []
31 self.curr_dirty = bitarray()
32 self.next_dirty = bitarray()
33
34 def add(self, value):
35 slot = len(self.curr)
36 self.curr.append(value)
37 self.next.append(value)
38 self.curr_dirty.append(True)
39 self.next_dirty.append(False)
40 return slot
41
42 def set(self, slot, value):
43 if self.next[slot] != value:
44 self.next_dirty[slot] = True
45 self.next[slot] = value
46
47 def commit(self, slot):
48 old_value = self.curr[slot]
49 new_value = self.next[slot]
50 if old_value != new_value:
51 self.next_dirty[slot] = False
52 self.curr_dirty[slot] = True
53 self.curr[slot] = new_value
54 return old_value, new_value
55
56 def flush_curr_dirty(self):
57 while True:
58 try:
59 slot = self.curr_dirty.index(True)
60 except ValueError:
61 break
62 self.curr_dirty[slot] = False
63 yield slot
64
65 def iter_next_dirty(self):
66 start = 0
67 while True:
68 try:
69 slot = self.next_dirty.index(True, start)
70 start = slot + 1
71 except ValueError:
72 break
73 yield slot
74
75
76 normalize = Const.normalize
77
78
79 class _ValueCompiler(ValueVisitor):
80 def on_AnyConst(self, value):
81 raise NotImplementedError # :nocov:
82
83 def on_AnySeq(self, value):
84 raise NotImplementedError # :nocov:
85
86 def on_Sample(self, value):
87 raise NotImplementedError # :nocov:
88
89 def on_Initial(self, value):
90 raise NotImplementedError # :nocov:
91
92 def on_Record(self, value):
93 return self(Cat(value.fields.values()))
94
95
96 class _RHSValueCompiler(_ValueCompiler):
97 def __init__(self, signal_slots, sensitivity=None, mode="rhs"):
98 self.signal_slots = signal_slots
99 self.sensitivity = sensitivity
100 self.signal_mode = mode
101
102 def on_Const(self, value):
103 return lambda state: value.value
104
105 def on_Signal(self, value):
106 if self.sensitivity is not None:
107 self.sensitivity.add(value)
108 if value not in self.signal_slots:
109 # A signal that is neither driven nor a port always remains at its reset state.
110 return lambda state: value.reset
111 value_slot = self.signal_slots[value]
112 if self.signal_mode == "rhs":
113 return lambda state: state.curr[value_slot]
114 elif self.signal_mode == "lhs":
115 return lambda state: state.next[value_slot]
116 else:
117 raise ValueError # :nocov:
118
119 def on_ClockSignal(self, value):
120 raise NotImplementedError # :nocov:
121
122 def on_ResetSignal(self, value):
123 raise NotImplementedError # :nocov:
124
125 def on_Operator(self, value):
126 shape = value.shape()
127 if len(value.operands) == 1:
128 arg, = map(self, value.operands)
129 if value.op == "~":
130 return lambda state: normalize(~arg(state), shape)
131 if value.op == "-":
132 return lambda state: normalize(-arg(state), shape)
133 if value.op == "b":
134 return lambda state: normalize(bool(arg(state)), shape)
135 elif len(value.operands) == 2:
136 lhs, rhs = map(self, value.operands)
137 if value.op == "+":
138 return lambda state: normalize(lhs(state) + rhs(state), shape)
139 if value.op == "-":
140 return lambda state: normalize(lhs(state) - rhs(state), shape)
141 if value.op == "*":
142 return lambda state: normalize(lhs(state) * rhs(state), shape)
143 if value.op == "&":
144 return lambda state: normalize(lhs(state) & rhs(state), shape)
145 if value.op == "|":
146 return lambda state: normalize(lhs(state) | rhs(state), shape)
147 if value.op == "^":
148 return lambda state: normalize(lhs(state) ^ rhs(state), shape)
149 if value.op == "<<":
150 def sshl(lhs, rhs):
151 return lhs << rhs if rhs >= 0 else lhs >> -rhs
152 return lambda state: normalize(sshl(lhs(state), rhs(state)), shape)
153 if value.op == ">>":
154 def sshr(lhs, rhs):
155 return lhs >> rhs if rhs >= 0 else lhs << -rhs
156 return lambda state: normalize(sshr(lhs(state), rhs(state)), shape)
157 if value.op == "==":
158 return lambda state: normalize(lhs(state) == rhs(state), shape)
159 if value.op == "!=":
160 return lambda state: normalize(lhs(state) != rhs(state), shape)
161 if value.op == "<":
162 return lambda state: normalize(lhs(state) < rhs(state), shape)
163 if value.op == "<=":
164 return lambda state: normalize(lhs(state) <= rhs(state), shape)
165 if value.op == ">":
166 return lambda state: normalize(lhs(state) > rhs(state), shape)
167 if value.op == ">=":
168 return lambda state: normalize(lhs(state) >= rhs(state), shape)
169 elif len(value.operands) == 3:
170 if value.op == "m":
171 sel, val1, val0 = map(self, value.operands)
172 return lambda state: val1(state) if sel(state) else val0(state)
173 raise NotImplementedError("Operator '{}' not implemented".format(value.op)) # :nocov:
174
175 def on_Slice(self, value):
176 shape = value.shape()
177 arg = self(value.value)
178 shift = value.start
179 mask = (1 << (value.end - value.start)) - 1
180 return lambda state: normalize((arg(state) >> shift) & mask, shape)
181
182 def on_Part(self, value):
183 shape = value.shape()
184 arg = self(value.value)
185 shift = self(value.offset)
186 mask = (1 << value.width) - 1
187 stride = value.stride
188 return lambda state: normalize((arg(state) >> shift(state) * stride) & mask, shape)
189
190 def on_Cat(self, value):
191 shape = value.shape()
192 parts = []
193 offset = 0
194 for opnd in value.parts:
195 parts.append((offset, (1 << len(opnd)) - 1, self(opnd)))
196 offset += len(opnd)
197 def eval(state):
198 result = 0
199 for offset, mask, opnd in parts:
200 result |= (opnd(state) & mask) << offset
201 return normalize(result, shape)
202 return eval
203
204 def on_Repl(self, value):
205 shape = value.shape()
206 offset = len(value.value)
207 mask = (1 << len(value.value)) - 1
208 count = value.count
209 opnd = self(value.value)
210 def eval(state):
211 result = 0
212 for _ in range(count):
213 result <<= offset
214 result |= opnd(state)
215 return normalize(result, shape)
216 return eval
217
218 def on_ArrayProxy(self, value):
219 shape = value.shape()
220 elems = list(map(self, value.elems))
221 index = self(value.index)
222 def eval(state):
223 index_value = index(state)
224 if index_value >= len(elems):
225 index_value = len(elems) - 1
226 return normalize(elems[index_value](state), shape)
227 return eval
228
229
230 class _LHSValueCompiler(_ValueCompiler):
231 def __init__(self, signal_slots, rhs_compiler):
232 self.signal_slots = signal_slots
233 self.rhs_compiler = rhs_compiler
234
235 def on_Const(self, value):
236 raise TypeError # :nocov:
237
238 def on_Signal(self, value):
239 shape = value.shape()
240 value_slot = self.signal_slots[value]
241 def eval(state, rhs):
242 state.set(value_slot, normalize(rhs, shape))
243 return eval
244
245 def on_ClockSignal(self, value):
246 raise NotImplementedError # :nocov:
247
248 def on_ResetSignal(self, value):
249 raise NotImplementedError # :nocov:
250
251 def on_Operator(self, value):
252 raise TypeError # :nocov:
253
254 def on_Slice(self, value):
255 lhs_r = self.rhs_compiler(value.value)
256 lhs_l = self(value.value)
257 shift = value.start
258 mask = (1 << (value.end - value.start)) - 1
259 def eval(state, rhs):
260 lhs_value = lhs_r(state)
261 lhs_value &= ~(mask << shift)
262 lhs_value |= (rhs & mask) << shift
263 lhs_l(state, lhs_value)
264 return eval
265
266 def on_Part(self, value):
267 lhs_r = self.rhs_compiler(value.value)
268 lhs_l = self(value.value)
269 shift = self.rhs_compiler(value.offset)
270 mask = (1 << value.width) - 1
271 stride = value.stride
272 def eval(state, rhs):
273 lhs_value = lhs_r(state)
274 shift_value = shift(state) * stride
275 lhs_value &= ~(mask << shift_value)
276 lhs_value |= (rhs & mask) << shift_value
277 lhs_l(state, lhs_value)
278 return eval
279
280 def on_Cat(self, value):
281 parts = []
282 offset = 0
283 for opnd in value.parts:
284 parts.append((offset, (1 << len(opnd)) - 1, self(opnd)))
285 offset += len(opnd)
286 def eval(state, rhs):
287 for offset, mask, opnd in parts:
288 opnd(state, (rhs >> offset) & mask)
289 return eval
290
291 def on_Repl(self, value):
292 raise TypeError # :nocov:
293
294 def on_ArrayProxy(self, value):
295 elems = list(map(self, value.elems))
296 index = self.rhs_compiler(value.index)
297 def eval(state, rhs):
298 index_value = index(state)
299 if index_value >= len(elems):
300 index_value = len(elems) - 1
301 elems[index_value](state, rhs)
302 return eval
303
304
305 class _StatementCompiler(StatementVisitor):
306 def __init__(self, signal_slots):
307 self.sensitivity = SignalSet()
308 self.rrhs_compiler = _RHSValueCompiler(signal_slots, self.sensitivity, mode="rhs")
309 self.lrhs_compiler = _RHSValueCompiler(signal_slots, self.sensitivity, mode="lhs")
310 self.lhs_compiler = _LHSValueCompiler(signal_slots, self.lrhs_compiler)
311
312 def on_Assign(self, stmt):
313 shape = stmt.lhs.shape()
314 lhs = self.lhs_compiler(stmt.lhs)
315 rhs = self.rrhs_compiler(stmt.rhs)
316 def run(state):
317 lhs(state, normalize(rhs(state), shape))
318 return run
319
320 def on_Assert(self, stmt):
321 raise NotImplementedError("Asserts not yet implemented for Simulator backend.") # :nocov:
322
323 def on_Assume(self, stmt):
324 pass # :nocov:
325
326 def on_Switch(self, stmt):
327 test = self.rrhs_compiler(stmt.test)
328 cases = []
329 for values, stmts in stmt.cases.items():
330 if values == ():
331 check = lambda test: True
332 else:
333 check = lambda test: False
334 def make_check(mask, value, prev_check):
335 return lambda test: prev_check(test) or test & mask == value
336 for value in values:
337 if "-" in value:
338 mask = "".join("0" if b == "-" else "1" for b in value)
339 value = "".join("0" if b == "-" else b for b in value)
340 else:
341 mask = "1" * len(value)
342 mask = int(mask, 2)
343 value = int(value, 2)
344 check = make_check(mask, value, check)
345 cases.append((check, self.on_statements(stmts)))
346 def run(state):
347 test_value = test(state)
348 for check, body in cases:
349 if check(test_value):
350 body(state)
351 return
352 return run
353
354 def on_statements(self, stmts):
355 stmts = [self.on_statement(stmt) for stmt in stmts]
356 def run(state):
357 for stmt in stmts:
358 stmt(state)
359 return run
360
361
362 class _SimulatorPlatform:
363 def get_reset_sync(self, reset_sync):
364 m = Module()
365 cd = ClockDomain("_reset_sync_{}".format(DUID().duid), async_reset=True)
366 m.domains += cd
367 for i, o in zip((0, *reset_sync._regs), reset_sync._regs):
368 m.d[cd.name] += o.eq(i)
369 m.d.comb += [
370 ClockSignal(cd.name).eq(ClockSignal(reset_sync.domain)),
371 ResetSignal(cd.name).eq(reset_sync.arst),
372 ResetSignal(reset_sync.domain).eq(reset_sync._regs[-1])
373 ]
374 return m
375
376
377 class Simulator:
378 def __init__(self, fragment, vcd_file=None, gtkw_file=None, traces=()):
379 self._fragment = Fragment.get(fragment, platform=_SimulatorPlatform())
380
381 self._signal_slots = SignalDict() # Signal -> int/slot
382 self._slot_signals = list() # int/slot -> Signal
383
384 self._domains = dict() # str/domain -> ClockDomain
385 self._domain_triggers = list() # int/slot -> str/domain
386
387 self._signals = SignalSet() # {Signal}
388 self._comb_signals = bitarray() # {Signal}
389 self._sync_signals = bitarray() # {Signal}
390 self._user_signals = bitarray() # {Signal}
391 self._domain_signals = dict() # str/domain -> {Signal}
392
393 self._started = False
394 self._timestamp = 0.
395 self._delta = 0.
396 self._epsilon = 1e-10
397 self._fastest_clock = self._epsilon
398 self._all_clocks = set() # {str/domain}
399 self._state = _State()
400
401 self._processes = set() # {process}
402 self._process_loc = dict() # process -> str/loc
403 self._passive = set() # {process}
404 self._suspended = set() # {process}
405 self._wait_deadline = dict() # process -> float/timestamp
406 self._wait_tick = dict() # process -> str/domain
407
408 self._funclets = list() # int/slot -> set(lambda)
409
410 self._vcd_file = vcd_file
411 self._vcd_writer = None
412 self._vcd_signals = list() # int/slot -> set(vcd_signal)
413 self._vcd_names = list() # int/slot -> str/name
414 self._gtkw_file = gtkw_file
415 self._traces = traces
416
417 self._run_called = False
418
419 @staticmethod
420 def _check_process(process):
421 if inspect.isgeneratorfunction(process):
422 process = process()
423 if not inspect.isgenerator(process):
424 raise TypeError("Cannot add a process '{!r}' because it is not a generator or "
425 "a generator function"
426 .format(process))
427 return process
428
429 def _name_process(self, process):
430 if process in self._process_loc:
431 return self._process_loc[process]
432 else:
433 frame = process.gi_frame
434 return "{}:{}".format(inspect.getfile(frame), inspect.getlineno(frame))
435
436 def add_process(self, process):
437 process = self._check_process(process)
438 self._processes.add(process)
439
440 def add_sync_process(self, process, domain="sync"):
441 process = self._check_process(process)
442 def sync_process():
443 try:
444 cmd = None
445 while True:
446 if cmd is None:
447 cmd = Tick(domain)
448 result = yield cmd
449 self._process_loc[sync_process] = self._name_process(process)
450 cmd = process.send(result)
451 except StopIteration:
452 pass
453 sync_process = sync_process()
454 self.add_process(sync_process)
455
456 def add_clock(self, period, phase=None, domain="sync"):
457 if self._fastest_clock == self._epsilon or period < self._fastest_clock:
458 self._fastest_clock = period
459 if domain in self._all_clocks:
460 raise ValueError("Domain '{}' already has a clock driving it"
461 .format(domain))
462
463 half_period = period / 2
464 if phase is None:
465 phase = half_period
466 clk = self._domains[domain].clk
467 def clk_process():
468 yield Passive()
469 yield Delay(phase)
470 while True:
471 yield clk.eq(1)
472 yield Delay(half_period)
473 yield clk.eq(0)
474 yield Delay(half_period)
475 self.add_process(clk_process)
476 self._all_clocks.add(domain)
477
478 def __enter__(self):
479 if self._vcd_file:
480 self._vcd_writer = VCDWriter(self._vcd_file, timescale="100 ps",
481 comment="Generated by nMigen")
482
483 root_fragment = self._fragment.prepare()
484 self._domains = root_fragment.domains
485
486 hierarchy = {}
487 def add_fragment(fragment, scope=()):
488 hierarchy[fragment] = scope
489 for index, (subfragment, name) in enumerate(fragment.subfragments):
490 if name is None:
491 add_fragment(subfragment, (*scope, "U{}".format(index)))
492 else:
493 add_fragment(subfragment, (*scope, name))
494 add_fragment(root_fragment, scope=("top",))
495
496 def add_signal(signal):
497 if signal not in self._signals:
498 self._signals.add(signal)
499
500 signal_slot = self._state.add(normalize(signal.reset, signal.shape()))
501 self._signal_slots[signal] = signal_slot
502 self._slot_signals.append(signal)
503
504 self._comb_signals.append(False)
505 self._sync_signals.append(False)
506 self._user_signals.append(False)
507 for domain in self._domains:
508 if domain not in self._domain_signals:
509 self._domain_signals[domain] = bitarray()
510 self._domain_signals[domain].append(False)
511
512 self._funclets.append(set())
513
514 self._domain_triggers.append(None)
515 if self._vcd_writer:
516 self._vcd_signals.append(set())
517 self._vcd_names.append(None)
518
519 return self._signal_slots[signal]
520
521 def add_domain_signal(signal, domain):
522 signal_slot = add_signal(signal)
523 self._domain_triggers[signal_slot] = domain
524
525 for fragment, fragment_scope in hierarchy.items():
526 for signal in fragment.iter_signals():
527 add_signal(signal)
528
529 for domain, cd in fragment.domains.items():
530 add_domain_signal(cd.clk, domain)
531 if cd.rst is not None:
532 add_domain_signal(cd.rst, domain)
533
534 for fragment, fragment_scope in hierarchy.items():
535 for signal in fragment.iter_signals():
536 if not self._vcd_writer:
537 continue
538
539 signal_slot = self._signal_slots[signal]
540
541 for i, (subfragment, name) in enumerate(fragment.subfragments):
542 if signal in subfragment.ports:
543 var_name = "{}_{}".format(name or "U{}".format(i), signal.name)
544 break
545 else:
546 var_name = signal.name
547
548 if signal.decoder:
549 var_type = "string"
550 var_size = 1
551 var_init = signal.decoder(signal.reset).expandtabs().replace(" ", "_")
552 else:
553 var_type = "wire"
554 var_size = signal.nbits
555 var_init = signal.reset
556
557 suffix = None
558 while True:
559 try:
560 if suffix is None:
561 var_name_suffix = var_name
562 else:
563 var_name_suffix = "{}${}".format(var_name, suffix)
564 self._vcd_signals[signal_slot].add(self._vcd_writer.register_var(
565 scope=".".join(fragment_scope), name=var_name_suffix,
566 var_type=var_type, size=var_size, init=var_init))
567 if self._vcd_names[signal_slot] is None:
568 self._vcd_names[signal_slot] = \
569 ".".join(fragment_scope + (var_name_suffix,))
570 break
571 except KeyError:
572 suffix = (suffix or 0) + 1
573
574 for domain, signals in fragment.drivers.items():
575 signals_bits = bitarray(len(self._signals))
576 signals_bits.setall(False)
577 for signal in signals:
578 signals_bits[self._signal_slots[signal]] = True
579
580 if domain is None:
581 self._comb_signals |= signals_bits
582 else:
583 self._sync_signals |= signals_bits
584 self._domain_signals[domain] |= signals_bits
585
586 statements = []
587 for domain, signals in fragment.drivers.items():
588 reset_stmts = []
589 hold_stmts = []
590 for signal in signals:
591 reset_stmts.append(signal.eq(signal.reset))
592 hold_stmts .append(signal.eq(signal))
593
594 if domain is None:
595 statements += reset_stmts
596 else:
597 if self._domains[domain].async_reset:
598 statements.append(Switch(self._domains[domain].rst,
599 {0: hold_stmts, 1: reset_stmts}))
600 else:
601 statements += hold_stmts
602 statements += fragment.statements
603
604 compiler = _StatementCompiler(self._signal_slots)
605 funclet = compiler(statements)
606
607 def add_funclet(signal, funclet):
608 if signal in self._signal_slots:
609 self._funclets[self._signal_slots[signal]].add(funclet)
610
611 for signal in compiler.sensitivity:
612 add_funclet(signal, funclet)
613 for domain, cd in fragment.domains.items():
614 add_funclet(cd.clk, funclet)
615 if cd.rst is not None:
616 add_funclet(cd.rst, funclet)
617
618 self._user_signals = bitarray(len(self._signals))
619 self._user_signals.setall(True)
620 self._user_signals &= ~self._comb_signals
621 self._user_signals &= ~self._sync_signals
622
623 return self
624
625 def _update_dirty_signals(self):
626 """Perform the statement part of IR processes (aka RTLIL case)."""
627 # First, for all dirty signals, use sensitivity lists to determine the set of fragments
628 # that need their statements to be reevaluated because the signals changed at the previous
629 # delta cycle.
630 funclets = set()
631 for signal_slot in self._state.flush_curr_dirty():
632 funclets.update(self._funclets[signal_slot])
633
634 # Second, compute the values of all signals at the start of the next delta cycle, by
635 # running precompiled statements.
636 for funclet in funclets:
637 funclet(self._state)
638
639 def _commit_signal(self, signal_slot, domains):
640 """Perform the driver part of IR processes (aka RTLIL sync), for individual signals."""
641 # Take the computed value (at the start of this delta cycle) of a signal (that could have
642 # come from an IR process that ran earlier, or modified by a simulator process) and update
643 # the value for this delta cycle.
644 old, new = self._state.commit(signal_slot)
645 if old == new:
646 return
647
648 # If the signal is a clock that triggers synchronous logic, record that fact.
649 if new == 1 and self._domain_triggers[signal_slot] is not None:
650 domains.add(self._domain_triggers[signal_slot])
651
652 if self._vcd_writer:
653 # Finally, dump the new value to the VCD file.
654 for vcd_signal in self._vcd_signals[signal_slot]:
655 signal = self._slot_signals[signal_slot]
656 if signal.decoder:
657 var_value = signal.decoder(new).expandtabs().replace(" ", "_")
658 else:
659 var_value = new
660 vcd_timestamp = (self._timestamp + self._delta) / self._epsilon
661 self._vcd_writer.change(vcd_signal, vcd_timestamp, var_value)
662
663 def _commit_comb_signals(self, domains):
664 """Perform the comb part of IR processes (aka RTLIL always)."""
665 # Take the computed value (at the start of this delta cycle) of every comb signal and
666 # update the value for this delta cycle.
667 for signal_slot in self._state.iter_next_dirty():
668 if self._comb_signals[signal_slot]:
669 self._commit_signal(signal_slot, domains)
670
671 def _commit_sync_signals(self, domains):
672 """Perform the sync part of IR processes (aka RTLIL posedge)."""
673 # At entry, `domains` contains a list of every simultaneously triggered sync update.
674 while domains:
675 # Advance the timeline a bit (purely for observational purposes) and commit all of them
676 # at the same timestamp.
677 self._delta += self._epsilon
678 curr_domains, domains = domains, set()
679
680 while curr_domains:
681 domain = curr_domains.pop()
682
683 # Wake up any simulator processes that wait for a domain tick.
684 for process, wait_domain in list(self._wait_tick.items()):
685 if domain == wait_domain:
686 del self._wait_tick[process]
687 self._suspended.remove(process)
688
689 # Immediately run the process. It is important that this happens here,
690 # and not on the next step, when all the processes will run anyway,
691 # because Tick() simulates an edge triggered process. Like DFFs that latch
692 # a value from the previous clock cycle, simulator processes observe signal
693 # values from the previous clock cycle on a tick, too.
694 self._run_process(process)
695
696 # Take the computed value (at the start of this delta cycle) of every sync signal
697 # in this domain and update the value for this delta cycle. This can trigger more
698 # synchronous logic, so record that.
699 for signal_slot in self._state.iter_next_dirty():
700 if self._domain_signals[domain][signal_slot]:
701 self._commit_signal(signal_slot, domains)
702
703 # Unless handling synchronous logic above has triggered more synchronous logic (which
704 # can happen e.g. if a domain is clocked off a clock divisor in fabric), we're done.
705 # Otherwise, do one more round of updates.
706
707 def _run_process(self, process):
708 try:
709 cmd = process.send(None)
710 while True:
711 if type(cmd) is Delay:
712 if cmd.interval is None:
713 interval = self._epsilon
714 else:
715 interval = cmd.interval
716 self._wait_deadline[process] = self._timestamp + interval
717 self._suspended.add(process)
718 break
719
720 elif type(cmd) is Tick:
721 self._wait_tick[process] = cmd.domain
722 self._suspended.add(process)
723 break
724
725 elif type(cmd) is Passive:
726 self._passive.add(process)
727
728 elif type(cmd) is Assign:
729 lhs_signals = cmd.lhs._lhs_signals()
730 for signal in lhs_signals:
731 if not signal in self._signals:
732 raise ValueError("Process '{}' sent a request to set signal '{!r}', "
733 "which is not a part of simulation"
734 .format(self._name_process(process), signal))
735 signal_slot = self._signal_slots[signal]
736 if self._comb_signals[signal_slot]:
737 raise ValueError("Process '{}' sent a request to set signal '{!r}', "
738 "which is a part of combinatorial assignment in "
739 "simulation"
740 .format(self._name_process(process), signal))
741
742 if type(cmd.lhs) is Signal and type(cmd.rhs) is Const:
743 # Fast path.
744 self._state.set(self._signal_slots[cmd.lhs],
745 normalize(cmd.rhs.value, cmd.lhs.shape()))
746 else:
747 compiler = _StatementCompiler(self._signal_slots)
748 funclet = compiler(cmd)
749 funclet(self._state)
750
751 domains = set()
752 for signal in lhs_signals:
753 self._commit_signal(self._signal_slots[signal], domains)
754 self._commit_sync_signals(domains)
755
756 elif type(cmd) is Signal:
757 # Fast path.
758 cmd = process.send(self._state.curr[self._signal_slots[cmd]])
759 continue
760
761 elif isinstance(cmd, Value):
762 compiler = _RHSValueCompiler(self._signal_slots)
763 funclet = compiler(cmd)
764 cmd = process.send(funclet(self._state))
765 continue
766
767 else:
768 raise TypeError("Received unsupported command '{!r}' from process '{}'"
769 .format(cmd, self._name_process(process)))
770
771 cmd = process.send(None)
772
773 except StopIteration:
774 self._processes.remove(process)
775 self._passive.discard(process)
776
777 except Exception as e:
778 process.throw(e)
779
780 def step(self, run_passive=False):
781 # Are there any delta cycles we should run?
782 if self._state.curr_dirty.any():
783 # We might run some delta cycles, and we have simulator processes waiting on
784 # a deadline. Take care to not exceed the closest deadline.
785 if self._wait_deadline and \
786 (self._timestamp + self._delta) >= min(self._wait_deadline.values()):
787 # Oops, we blew the deadline. We *could* run the processes now, but this is
788 # virtually certainly a logic loop and a design bug, so bail out instead.d
789 raise DeadlineError("Delta cycles exceeded process deadline; combinatorial loop?")
790
791 domains = set()
792 while self._state.curr_dirty.any():
793 self._update_dirty_signals()
794 self._commit_comb_signals(domains)
795 self._commit_sync_signals(domains)
796 return True
797
798 # Are there any processes that haven't had a chance to run yet?
799 if len(self._processes) > len(self._suspended):
800 # Schedule an arbitrary one.
801 process = (self._processes - set(self._suspended)).pop()
802 self._run_process(process)
803 return True
804
805 # All processes are suspended. Are any of them active?
806 if len(self._processes) > len(self._passive) or run_passive:
807 # Are any of them suspended before a deadline?
808 if self._wait_deadline:
809 # Schedule the one with the lowest deadline.
810 process, deadline = min(self._wait_deadline.items(), key=lambda x: x[1])
811 del self._wait_deadline[process]
812 self._suspended.remove(process)
813 self._timestamp = deadline
814 self._delta = 0.
815 self._run_process(process)
816 return True
817
818 # No processes, or all processes are passive. Nothing to do!
819 return False
820
821 def run(self):
822 self._run_called = True
823
824 while self.step():
825 pass
826
827 def run_until(self, deadline, run_passive=False):
828 self._run_called = True
829
830 while self._timestamp < deadline:
831 if not self.step(run_passive):
832 return False
833
834 return True
835
836 def __exit__(self, *args):
837 if not self._run_called:
838 warnings.warn("Simulation created, but not run", UserWarning)
839
840 if self._vcd_writer:
841 vcd_timestamp = (self._timestamp + self._delta) / self._epsilon
842 self._vcd_writer.close(vcd_timestamp)
843
844 if self._vcd_file and self._gtkw_file:
845 gtkw_save = GTKWSave(self._gtkw_file)
846 if hasattr(self._vcd_file, "name"):
847 gtkw_save.dumpfile(self._vcd_file.name)
848 if hasattr(self._vcd_file, "tell"):
849 gtkw_save.dumpfile_size(self._vcd_file.tell())
850
851 gtkw_save.treeopen("top")
852 gtkw_save.zoom_markers(math.log(self._epsilon / self._fastest_clock) - 14)
853
854 def add_trace(signal, **kwargs):
855 signal_slot = self._signal_slots[signal]
856 if self._vcd_names[signal_slot] is not None:
857 if len(signal) > 1 and not signal.decoder:
858 suffix = "[{}:0]".format(len(signal) - 1)
859 else:
860 suffix = ""
861 gtkw_save.trace(self._vcd_names[signal_slot] + suffix, **kwargs)
862
863 for domain, cd in self._domains.items():
864 with gtkw_save.group("d.{}".format(domain)):
865 if cd.rst is not None:
866 add_trace(cd.rst)
867 add_trace(cd.clk)
868
869 for signal in self._traces:
870 add_trace(signal)
871
872 if self._vcd_file:
873 self._vcd_file.close()
874 if self._gtkw_file:
875 self._gtkw_file.close()