1278923ba67894b5ca153b555fe34c383238f712
4 from contextlib
import contextmanager
5 from bitarray
import bitarray
6 from vcd
import VCDWriter
7 from vcd
.gtkw
import GTKWSave
9 from ..tools
import flatten
10 from ..hdl
.ast
import *
11 from ..hdl
.ir
import *
12 from ..hdl
.xfrm
import ValueVisitor
, StatementVisitor
15 __all__
= ["Simulator", "Delay", "Tick", "Passive", "DeadlineError"]
18 class DeadlineError(Exception):
23 __slots__
= ("curr", "curr_dirty", "next", "next_dirty")
28 self
.curr_dirty
= bitarray()
29 self
.next_dirty
= bitarray()
33 self
.curr
.append(value
)
34 self
.next
.append(value
)
35 self
.curr_dirty
.append(True)
36 self
.next_dirty
.append(False)
39 def set(self
, slot
, value
):
40 if self
.next
[slot
] != value
:
41 self
.next_dirty
[slot
] = True
42 self
.next
[slot
] = value
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
53 def flush_curr_dirty(self
):
56 slot
= self
.curr_dirty
.index(True)
59 self
.curr_dirty
[slot
] = False
62 def iter_next_dirty(self
):
66 slot
= self
.next_dirty
.index(True, start
)
73 normalize
= Const
.normalize
76 class _ValueCompiler(ValueVisitor
):
77 def on_Record(self
, value
):
78 return self(Cat(value
.fields
.values()))
81 class _RHSValueCompiler(_ValueCompiler
):
82 def __init__(self
, signal_slots
, sensitivity
=None, mode
="rhs"):
83 self
.signal_slots
= signal_slots
84 self
.sensitivity
= sensitivity
85 self
.signal_mode
= mode
87 def on_Const(self
, value
):
88 return lambda state
: value
.value
90 def on_Signal(self
, value
):
91 if self
.sensitivity
is not None:
92 self
.sensitivity
.add(value
)
93 if value
not in self
.signal_slots
:
94 # A signal that is neither driven nor a port always remains at its reset state.
95 return lambda state
: value
.reset
96 value_slot
= self
.signal_slots
[value
]
97 if self
.signal_mode
== "rhs":
98 return lambda state
: state
.curr
[value_slot
]
99 elif self
.signal_mode
== "lhs":
100 return lambda state
: state
.next
[value_slot
]
102 raise ValueError # :nocov:
104 def on_ClockSignal(self
, value
):
105 raise NotImplementedError # :nocov:
107 def on_ResetSignal(self
, value
):
108 raise NotImplementedError # :nocov:
110 def on_Operator(self
, value
):
111 shape
= value
.shape()
112 if len(value
.operands
) == 1:
113 arg
, = map(self
, value
.operands
)
115 return lambda state
: normalize(~
arg(state
), shape
)
117 return lambda state
: normalize(-arg(state
), shape
)
119 return lambda state
: normalize(bool(arg(state
)), shape
)
120 elif len(value
.operands
) == 2:
121 lhs
, rhs
= map(self
, value
.operands
)
123 return lambda state
: normalize(lhs(state
) + rhs(state
), shape
)
125 return lambda state
: normalize(lhs(state
) - rhs(state
), shape
)
127 return lambda state
: normalize(lhs(state
) * rhs(state
), shape
)
129 return lambda state
: normalize(lhs(state
) & rhs(state
), shape
)
131 return lambda state
: normalize(lhs(state
) |
rhs(state
), shape
)
133 return lambda state
: normalize(lhs(state
) ^
rhs(state
), shape
)
136 return lhs
<< rhs
if rhs
>= 0 else lhs
>> -rhs
137 return lambda state
: normalize(sshl(lhs(state
), rhs(state
)), shape
)
140 return lhs
>> rhs
if rhs
>= 0 else lhs
<< -rhs
141 return lambda state
: normalize(sshr(lhs(state
), rhs(state
)), shape
)
143 return lambda state
: normalize(lhs(state
) == rhs(state
), shape
)
145 return lambda state
: normalize(lhs(state
) != rhs(state
), shape
)
147 return lambda state
: normalize(lhs(state
) < rhs(state
), shape
)
149 return lambda state
: normalize(lhs(state
) <= rhs(state
), shape
)
151 return lambda state
: normalize(lhs(state
) > rhs(state
), shape
)
153 return lambda state
: normalize(lhs(state
) >= rhs(state
), shape
)
154 elif len(value
.operands
) == 3:
156 sel
, val1
, val0
= map(self
, value
.operands
)
157 return lambda state
: val1(state
) if sel(state
) else val0(state
)
158 raise NotImplementedError("Operator '{}' not implemented".format(value
.op
)) # :nocov:
160 def on_Slice(self
, value
):
161 shape
= value
.shape()
162 arg
= self(value
.value
)
164 mask
= (1 << (value
.end
- value
.start
)) - 1
165 return lambda state
: normalize((arg(state
) >> shift
) & mask
, shape
)
167 def on_Part(self
, value
):
168 shape
= value
.shape()
169 arg
= self(value
.value
)
170 shift
= self(value
.offset
)
171 mask
= (1 << value
.width
) - 1
172 return lambda state
: normalize((arg(state
) >> shift(state
)) & mask
, shape
)
174 def on_Cat(self
, value
):
175 shape
= value
.shape()
178 for opnd
in value
.parts
:
179 parts
.append((offset
, (1 << len(opnd
)) - 1, self(opnd
)))
183 for offset
, mask
, opnd
in parts
:
184 result |
= (opnd(state
) & mask
) << offset
185 return normalize(result
, shape
)
188 def on_Repl(self
, value
):
189 shape
= value
.shape()
190 offset
= len(value
.value
)
191 mask
= (1 << len(value
.value
)) - 1
193 opnd
= self(value
.value
)
196 for _
in range(count
):
198 result |
= opnd(state
)
199 return normalize(result
, shape
)
202 def on_ArrayProxy(self
, value
):
203 shape
= value
.shape()
204 elems
= list(map(self
, value
.elems
))
205 index
= self(value
.index
)
207 index_value
= index(state
)
208 if index_value
>= len(elems
):
209 index_value
= len(elems
) - 1
210 return normalize(elems
[index_value
](state
), shape
)
214 class _LHSValueCompiler(_ValueCompiler
):
215 def __init__(self
, signal_slots
, rhs_compiler
):
216 self
.signal_slots
= signal_slots
217 self
.rhs_compiler
= rhs_compiler
219 def on_Const(self
, value
):
220 raise TypeError # :nocov:
222 def on_Signal(self
, value
):
223 shape
= value
.shape()
224 value_slot
= self
.signal_slots
[value
]
225 def eval(state
, rhs
):
226 state
.set(value_slot
, normalize(rhs
, shape
))
229 def on_ClockSignal(self
, value
):
230 raise NotImplementedError # :nocov:
232 def on_ResetSignal(self
, value
):
233 raise NotImplementedError # :nocov:
235 def on_Operator(self
, value
):
236 raise TypeError # :nocov:
238 def on_Slice(self
, value
):
239 lhs_r
= self
.rhs_compiler(value
.value
)
240 lhs_l
= self(value
.value
)
242 mask
= (1 << (value
.end
- value
.start
)) - 1
243 def eval(state
, rhs
):
244 lhs_value
= lhs_r(state
)
245 lhs_value
&= ~
(mask
<< shift
)
246 lhs_value |
= (rhs
& mask
) << shift
247 lhs_l(state
, lhs_value
)
250 def on_Part(self
, value
):
251 lhs_r
= self
.rhs_compiler(value
.value
)
252 lhs_l
= self(value
.value
)
253 shift
= self
.rhs_compiler(value
.offset
)
254 mask
= (1 << value
.width
) - 1
255 def eval(state
, rhs
):
256 lhs_value
= lhs_r(state
)
257 shift_value
= shift(state
)
258 lhs_value
&= ~
(mask
<< shift_value
)
259 lhs_value |
= (rhs
& mask
) << shift_value
260 lhs_l(state
, lhs_value
)
263 def on_Cat(self
, value
):
266 for opnd
in value
.parts
:
267 parts
.append((offset
, (1 << len(opnd
)) - 1, self(opnd
)))
269 def eval(state
, rhs
):
270 for offset
, mask
, opnd
in parts
:
271 opnd(state
, (rhs
>> offset
) & mask
)
274 def on_Repl(self
, value
):
275 raise TypeError # :nocov:
277 def on_ArrayProxy(self
, value
):
278 elems
= list(map(self
, value
.elems
))
279 index
= self
.rhs_compiler(value
.index
)
280 def eval(state
, rhs
):
281 index_value
= index(state
)
282 if index_value
>= len(elems
):
283 index_value
= len(elems
) - 1
284 elems
[index_value
](state
, rhs
)
288 class _StatementCompiler(StatementVisitor
):
289 def __init__(self
, signal_slots
):
290 self
.sensitivity
= SignalSet()
291 self
.rrhs_compiler
= _RHSValueCompiler(signal_slots
, self
.sensitivity
, mode
="rhs")
292 self
.lrhs_compiler
= _RHSValueCompiler(signal_slots
, self
.sensitivity
, mode
="lhs")
293 self
.lhs_compiler
= _LHSValueCompiler(signal_slots
, self
.lrhs_compiler
)
295 def on_Assign(self
, stmt
):
296 shape
= stmt
.lhs
.shape()
297 lhs
= self
.lhs_compiler(stmt
.lhs
)
298 rhs
= self
.rrhs_compiler(stmt
.rhs
)
300 lhs(state
, normalize(rhs(state
), shape
))
303 def on_Assert(self
, stmt
):
304 raise NotImplementedError("Asserts not yet implemented for Simulator backend.") # :nocov:
306 def on_Assume(self
, stmt
):
309 def on_Switch(self
, stmt
):
310 test
= self
.rrhs_compiler(stmt
.test
)
312 for value
, stmts
in stmt
.cases
.items():
314 mask
= "".join("0" if b
== "-" else "1" for b
in value
)
315 value
= "".join("0" if b
== "-" else b
for b
in value
)
317 mask
= "1" * len(value
)
319 value
= int(value
, 2)
320 def make_test(mask
, value
):
321 return lambda test
: test
& mask
== value
322 cases
.append((make_test(mask
, value
), self
.on_statements(stmts
)))
324 test_value
= test(state
)
325 for check
, body
in cases
:
326 if check(test_value
):
331 def on_statements(self
, stmts
):
332 stmts
= [self
.on_statement(stmt
) for stmt
in stmts
]
340 def __init__(self
, fragment
, vcd_file
=None, gtkw_file
=None, traces
=()):
341 self
._fragment
= fragment
343 self
._signal
_slots
= SignalDict() # Signal -> int/slot
344 self
._slot
_signals
= list() # int/slot -> Signal
346 self
._domains
= dict() # str/domain -> ClockDomain
347 self
._domain
_triggers
= list() # int/slot -> str/domain
349 self
._signals
= SignalSet() # {Signal}
350 self
._comb
_signals
= bitarray() # {Signal}
351 self
._sync
_signals
= bitarray() # {Signal}
352 self
._user
_signals
= bitarray() # {Signal}
353 self
._domain
_signals
= dict() # str/domain -> {Signal}
355 self
._started
= False
358 self
._epsilon
= 1e-10
359 self
._fastest
_clock
= self
._epsilon
360 self
._state
= _State()
362 self
._processes
= set() # {process}
363 self
._process
_loc
= dict() # process -> str/loc
364 self
._passive
= set() # {process}
365 self
._suspended
= set() # {process}
366 self
._wait
_deadline
= dict() # process -> float/timestamp
367 self
._wait
_tick
= dict() # process -> str/domain
369 self
._funclets
= list() # int/slot -> set(lambda)
371 self
._vcd
_file
= vcd_file
372 self
._vcd
_writer
= None
373 self
._vcd
_signals
= list() # int/slot -> set(vcd_signal)
374 self
._vcd
_names
= list() # int/slot -> str/name
375 self
._gtkw
_file
= gtkw_file
376 self
._traces
= traces
378 self
._run
_called
= False
380 while not isinstance(self
._fragment
, Fragment
):
381 self
._fragment
= self
._fragment
.get_fragment(platform
=None)
384 def _check_process(process
):
385 if inspect
.isgeneratorfunction(process
):
387 if not inspect
.isgenerator(process
):
388 raise TypeError("Cannot add a process '{!r}' because it is not a generator or "
389 "a generator function"
393 def _name_process(self
, process
):
394 if process
in self
._process
_loc
:
395 return self
._process
_loc
[process
]
397 frame
= process
.gi_frame
398 return "{}:{}".format(inspect
.getfile(frame
), inspect
.getlineno(frame
))
400 def add_process(self
, process
):
401 process
= self
._check
_process
(process
)
402 self
._processes
.add(process
)
404 def add_sync_process(self
, process
, domain
="sync"):
405 process
= self
._check
_process
(process
)
410 self
._process
_loc
[sync_process
] = self
._name
_process
(process
)
411 cmd
= process
.send(result
)
415 except StopIteration:
417 sync_process
= sync_process()
418 self
.add_process(sync_process
)
420 def add_clock(self
, period
, phase
=None, domain
="sync"):
421 if self
._fastest
_clock
== self
._epsilon
or period
< self
._fastest
_clock
:
422 self
._fastest
_clock
= period
424 half_period
= period
/ 2
427 clk
= self
._domains
[domain
].clk
433 yield Delay(half_period
)
435 yield Delay(half_period
)
436 self
.add_process(clk_process
)
440 self
._vcd
_writer
= VCDWriter(self
._vcd
_file
, timescale
="100 ps",
441 comment
="Generated by nMigen")
443 root_fragment
= self
._fragment
.prepare()
444 self
._domains
= root_fragment
.domains
447 def add_fragment(fragment
, scope
=()):
448 hierarchy
[fragment
] = scope
449 for index
, (subfragment
, name
) in enumerate(fragment
.subfragments
):
451 add_fragment(subfragment
, (*scope
, "#{}".format(index
)))
453 add_fragment(subfragment
, (*scope
, name
))
454 add_fragment(root_fragment
, scope
=("top",))
456 def add_signal(signal
):
457 if signal
not in self
._signals
:
458 self
._signals
.add(signal
)
460 signal_slot
= self
._state
.add(normalize(signal
.reset
, signal
.shape()))
461 self
._signal
_slots
[signal
] = signal_slot
462 self
._slot
_signals
.append(signal
)
464 self
._comb
_signals
.append(False)
465 self
._sync
_signals
.append(False)
466 self
._user
_signals
.append(False)
467 for domain
in self
._domains
:
468 if domain
not in self
._domain
_signals
:
469 self
._domain
_signals
[domain
] = bitarray()
470 self
._domain
_signals
[domain
].append(False)
472 self
._funclets
.append(set())
474 self
._domain
_triggers
.append(None)
476 self
._vcd
_signals
.append(set())
477 self
._vcd
_names
.append(None)
479 return self
._signal
_slots
[signal
]
481 def add_domain_signal(signal
, domain
):
482 signal_slot
= add_signal(signal
)
483 self
._domain
_triggers
[signal_slot
] = domain
485 for fragment
, fragment_scope
in hierarchy
.items():
486 for signal
in fragment
.iter_signals():
489 for domain
, cd
in fragment
.domains
.items():
490 add_domain_signal(cd
.clk
, domain
)
491 if cd
.rst
is not None:
492 add_domain_signal(cd
.rst
, domain
)
494 for fragment
, fragment_scope
in hierarchy
.items():
495 for signal
in fragment
.iter_signals():
496 if not self
._vcd
_writer
:
499 signal_slot
= self
._signal
_slots
[signal
]
501 for subfragment
, name
in fragment
.subfragments
:
502 if signal
in subfragment
.ports
:
503 var_name
= "{}_{}".format(name
, signal
.name
)
506 var_name
= signal
.name
511 var_init
= signal
.decoder(signal
.reset
).replace(" ", "_")
514 var_size
= signal
.nbits
515 var_init
= signal
.reset
521 var_name_suffix
= var_name
523 var_name_suffix
= "{}${}".format(var_name
, suffix
)
524 self
._vcd
_signals
[signal_slot
].add(self
._vcd
_writer
.register_var(
525 scope
=".".join(fragment_scope
), name
=var_name_suffix
,
526 var_type
=var_type
, size
=var_size
, init
=var_init
))
527 if self
._vcd
_names
[signal_slot
] is None:
528 self
._vcd
_names
[signal_slot
] = \
529 ".".join(fragment_scope
+ (var_name_suffix
,))
532 suffix
= (suffix
or 0) + 1
534 for domain
, signals
in fragment
.drivers
.items():
535 signals_bits
= bitarray(len(self
._signals
))
536 signals_bits
.setall(False)
537 for signal
in signals
:
538 signals_bits
[self
._signal
_slots
[signal
]] = True
541 self
._comb
_signals |
= signals_bits
543 self
._sync
_signals |
= signals_bits
544 self
._domain
_signals
[domain
] |
= signals_bits
547 for signal
in fragment
.iter_comb():
548 statements
.append(signal
.eq(signal
.reset
))
549 for domain
, signal
in fragment
.iter_sync():
550 statements
.append(signal
.eq(signal
))
551 statements
+= fragment
.statements
553 compiler
= _StatementCompiler(self
._signal
_slots
)
554 funclet
= compiler(statements
)
556 def add_funclet(signal
, funclet
):
557 if signal
in self
._signal
_slots
:
558 self
._funclets
[self
._signal
_slots
[signal
]].add(funclet
)
560 for signal
in compiler
.sensitivity
:
561 add_funclet(signal
, funclet
)
562 for domain
, cd
in fragment
.domains
.items():
563 add_funclet(cd
.clk
, funclet
)
564 if cd
.rst
is not None:
565 add_funclet(cd
.rst
, funclet
)
567 self
._user
_signals
= bitarray(len(self
._signals
))
568 self
._user
_signals
.setall(True)
569 self
._user
_signals
&= ~self
._comb
_signals
570 self
._user
_signals
&= ~self
._sync
_signals
574 def _update_dirty_signals(self
):
575 """Perform the statement part of IR processes (aka RTLIL case)."""
576 # First, for all dirty signals, use sensitivity lists to determine the set of fragments
577 # that need their statements to be reevaluated because the signals changed at the previous
580 for signal_slot
in self
._state
.flush_curr_dirty():
581 funclets
.update(self
._funclets
[signal_slot
])
583 # Second, compute the values of all signals at the start of the next delta cycle, by
584 # running precompiled statements.
585 for funclet
in funclets
:
588 def _commit_signal(self
, signal_slot
, domains
):
589 """Perform the driver part of IR processes (aka RTLIL sync), for individual signals."""
590 # Take the computed value (at the start of this delta cycle) of a signal (that could have
591 # come from an IR process that ran earlier, or modified by a simulator process) and update
592 # the value for this delta cycle.
593 old
, new
= self
._state
.commit(signal_slot
)
597 # If the signal is a clock that triggers synchronous logic, record that fact.
598 if new
== 1 and self
._domain
_triggers
[signal_slot
] is not None:
599 domains
.add(self
._domain
_triggers
[signal_slot
])
602 # Finally, dump the new value to the VCD file.
603 for vcd_signal
in self
._vcd
_signals
[signal_slot
]:
604 signal
= self
._slot
_signals
[signal_slot
]
606 var_value
= signal
.decoder(new
).replace(" ", "_")
609 vcd_timestamp
= (self
._timestamp
+ self
._delta
) / self
._epsilon
610 self
._vcd
_writer
.change(vcd_signal
, vcd_timestamp
, var_value
)
612 def _commit_comb_signals(self
, domains
):
613 """Perform the comb part of IR processes (aka RTLIL always)."""
614 # Take the computed value (at the start of this delta cycle) of every comb signal and
615 # update the value for this delta cycle.
616 for signal_slot
in self
._state
.iter_next_dirty():
617 if self
._comb
_signals
[signal_slot
]:
618 self
._commit
_signal
(signal_slot
, domains
)
620 def _commit_sync_signals(self
, domains
):
621 """Perform the sync part of IR processes (aka RTLIL posedge)."""
622 # At entry, `domains` contains a list of every simultaneously triggered sync update.
624 # Advance the timeline a bit (purely for observational purposes) and commit all of them
625 # at the same timestamp.
626 self
._delta
+= self
._epsilon
627 curr_domains
, domains
= domains
, set()
630 domain
= curr_domains
.pop()
632 # Take the computed value (at the start of this delta cycle) of every sync signal
633 # in this domain and update the value for this delta cycle. This can trigger more
634 # synchronous logic, so record that.
635 for signal_slot
in self
._state
.iter_next_dirty():
636 if self
._domain
_signals
[domain
][signal_slot
]:
637 self
._commit
_signal
(signal_slot
, domains
)
639 # Wake up any simulator processes that wait for a domain tick.
640 for process
, wait_domain
in list(self
._wait
_tick
.items()):
641 if domain
== wait_domain
:
642 del self
._wait
_tick
[process
]
643 self
._suspended
.remove(process
)
645 # Immediately run the process. It is important that this happens here,
646 # and not on the next step, when all the processes will run anyway,
647 # because Tick() simulates an edge triggered process. Like DFFs that latch
648 # a value from the previous clock cycle, simulator processes observe signal
649 # values from the previous clock cycle on a tick, too.
650 self
._run
_process
(process
)
652 # Unless handling synchronous logic above has triggered more synchronous logic (which
653 # can happen e.g. if a domain is clocked off a clock divisor in fabric), we're done.
654 # Otherwise, do one more round of updates.
656 def _run_process(self
, process
):
658 cmd
= process
.send(None)
660 if type(cmd
) is Delay
:
661 if cmd
.interval
is None:
662 interval
= self
._epsilon
664 interval
= cmd
.interval
665 self
._wait
_deadline
[process
] = self
._timestamp
+ interval
666 self
._suspended
.add(process
)
669 elif type(cmd
) is Tick
:
670 self
._wait
_tick
[process
] = cmd
.domain
671 self
._suspended
.add(process
)
674 elif type(cmd
) is Passive
:
675 self
._passive
.add(process
)
677 elif type(cmd
) is Assign
:
678 lhs_signals
= cmd
.lhs
._lhs
_signals
()
679 for signal
in lhs_signals
:
680 if not signal
in self
._signals
:
681 raise ValueError("Process '{}' sent a request to set signal '{!r}', "
682 "which is not a part of simulation"
683 .format(self
._name
_process
(process
), signal
))
684 signal_slot
= self
._signal
_slots
[signal
]
685 if self
._comb
_signals
[signal_slot
]:
686 raise ValueError("Process '{}' sent a request to set signal '{!r}', "
687 "which is a part of combinatorial assignment in "
689 .format(self
._name
_process
(process
), signal
))
691 if type(cmd
.lhs
) is Signal
and type(cmd
.rhs
) is Const
:
693 self
._state
.set(self
._signal
_slots
[cmd
.lhs
],
694 normalize(cmd
.rhs
.value
, cmd
.lhs
.shape()))
696 compiler
= _StatementCompiler(self
._signal
_slots
)
697 funclet
= compiler(cmd
)
701 for signal
in lhs_signals
:
702 self
._commit
_signal
(self
._signal
_slots
[signal
], domains
)
703 self
._commit
_sync
_signals
(domains
)
705 elif type(cmd
) is Signal
:
707 cmd
= process
.send(self
._state
.curr
[self
._signal
_slots
[cmd
]])
710 elif isinstance(cmd
, Value
):
711 compiler
= _RHSValueCompiler(self
._signal
_slots
)
712 funclet
= compiler(cmd
)
713 cmd
= process
.send(funclet(self
._state
))
717 raise TypeError("Received unsupported command '{!r}' from process '{}'"
718 .format(cmd
, self
._name
_process
(process
)))
720 cmd
= process
.send(None)
722 except StopIteration:
723 self
._processes
.remove(process
)
724 self
._passive
.discard(process
)
726 except Exception as e
:
729 def step(self
, run_passive
=False):
730 # Are there any delta cycles we should run?
731 if self
._state
.curr_dirty
.any():
732 # We might run some delta cycles, and we have simulator processes waiting on
733 # a deadline. Take care to not exceed the closest deadline.
734 if self
._wait
_deadline
and \
735 (self
._timestamp
+ self
._delta
) >= min(self
._wait
_deadline
.values()):
736 # Oops, we blew the deadline. We *could* run the processes now, but this is
737 # virtually certainly a logic loop and a design bug, so bail out instead.d
738 raise DeadlineError("Delta cycles exceeded process deadline; combinatorial loop?")
741 while self
._state
.curr_dirty
.any():
742 self
._update
_dirty
_signals
()
743 self
._commit
_comb
_signals
(domains
)
744 self
._commit
_sync
_signals
(domains
)
747 # Are there any processes that haven't had a chance to run yet?
748 if len(self
._processes
) > len(self
._suspended
):
749 # Schedule an arbitrary one.
750 process
= (self
._processes
- set(self
._suspended
)).pop()
751 self
._run
_process
(process
)
754 # All processes are suspended. Are any of them active?
755 if len(self
._processes
) > len(self
._passive
) or run_passive
:
756 # Are any of them suspended before a deadline?
757 if self
._wait
_deadline
:
758 # Schedule the one with the lowest deadline.
759 process
, deadline
= min(self
._wait
_deadline
.items(), key
=lambda x
: x
[1])
760 del self
._wait
_deadline
[process
]
761 self
._suspended
.remove(process
)
762 self
._timestamp
= deadline
764 self
._run
_process
(process
)
767 # No processes, or all processes are passive. Nothing to do!
771 self
._run
_called
= True
776 def run_until(self
, deadline
, run_passive
=False):
777 self
._run
_called
= True
779 while self
._timestamp
< deadline
:
780 if not self
.step(run_passive
):
785 def __exit__(self
, *args
):
786 if not self
._run
_called
:
787 warnings
.warn("Simulation created, but not run", UserWarning)
790 vcd_timestamp
= (self
._timestamp
+ self
._delta
) / self
._epsilon
791 self
._vcd
_writer
.close(vcd_timestamp
)
793 if self
._vcd
_file
and self
._gtkw
_file
:
794 gtkw_save
= GTKWSave(self
._gtkw
_file
)
795 if hasattr(self
._vcd
_file
, "name"):
796 gtkw_save
.dumpfile(self
._vcd
_file
.name
)
797 if hasattr(self
._vcd
_file
, "tell"):
798 gtkw_save
.dumpfile_size(self
._vcd
_file
.tell())
800 gtkw_save
.treeopen("top")
801 gtkw_save
.zoom_markers(math
.log(self
._epsilon
/ self
._fastest
_clock
) - 14)
803 def add_trace(signal
, **kwargs
):
804 signal_slot
= self
._signal
_slots
[signal
]
805 if self
._vcd
_names
[signal_slot
] is not None:
807 suffix
= "[{}:0]".format(len(signal
) - 1)
810 gtkw_save
.trace(self
._vcd
_names
[signal_slot
] + suffix
, **kwargs
)
812 for domain
, cd
in self
._domains
.items():
813 with gtkw_save
.group("d.{}".format(domain
)):
814 if cd
.rst
is not None:
818 for signal
in self
._traces
:
822 self
._vcd
_file
.close()
824 self
._gtkw
_file
.close()