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