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