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
13 from ..hdl
.ast
import DUID
14 from ..hdl
.dsl
import Module
15 from ..hdl
.cd
import ClockDomain
18 __all__
= ["Simulator", "Delay", "Tick", "Passive", "DeadlineError"]
21 class DeadlineError(Exception):
26 __slots__
= ("curr", "curr_dirty", "next", "next_dirty")
31 self
.curr_dirty
= bitarray()
32 self
.next_dirty
= bitarray()
36 self
.curr
.append(value
)
37 self
.next
.append(value
)
38 self
.curr_dirty
.append(True)
39 self
.next_dirty
.append(False)
42 def set(self
, slot
, value
):
43 if self
.next
[slot
] != value
:
44 self
.next_dirty
[slot
] = True
45 self
.next
[slot
] = value
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
56 def flush_curr_dirty(self
):
59 slot
= self
.curr_dirty
.index(True)
62 self
.curr_dirty
[slot
] = False
65 def iter_next_dirty(self
):
69 slot
= self
.next_dirty
.index(True, start
)
76 normalize
= Const
.normalize
79 class _ValueCompiler(ValueVisitor
):
80 def on_AnyConst(self
, value
):
81 raise NotImplementedError # :nocov:
83 def on_AnySeq(self
, value
):
84 raise NotImplementedError # :nocov:
86 def on_Sample(self
, value
):
87 raise NotImplementedError # :nocov:
89 def on_Initial(self
, value
):
90 raise NotImplementedError # :nocov:
92 def on_Record(self
, value
):
93 return self(Cat(value
.fields
.values()))
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
102 def on_Const(self
, value
):
103 return lambda state
: value
.value
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
]
117 raise ValueError # :nocov:
119 def on_ClockSignal(self
, value
):
120 raise NotImplementedError # :nocov:
122 def on_ResetSignal(self
, value
):
123 raise NotImplementedError # :nocov:
125 def on_Operator(self
, value
):
126 shape
= value
.shape()
127 if len(value
.operands
) == 1:
128 arg
, = map(self
, value
.operands
)
130 return lambda state
: normalize(~
arg(state
), shape
)
132 return lambda state
: normalize(-arg(state
), shape
)
134 return lambda state
: normalize(bool(arg(state
)), shape
)
135 elif len(value
.operands
) == 2:
136 lhs
, rhs
= map(self
, value
.operands
)
138 return lambda state
: normalize(lhs(state
) + rhs(state
), shape
)
140 return lambda state
: normalize(lhs(state
) - rhs(state
), shape
)
142 return lambda state
: normalize(lhs(state
) * rhs(state
), shape
)
144 return lambda state
: normalize(lhs(state
) & rhs(state
), shape
)
146 return lambda state
: normalize(lhs(state
) |
rhs(state
), shape
)
148 return lambda state
: normalize(lhs(state
) ^
rhs(state
), shape
)
151 return lhs
<< rhs
if rhs
>= 0 else lhs
>> -rhs
152 return lambda state
: normalize(sshl(lhs(state
), rhs(state
)), shape
)
155 return lhs
>> rhs
if rhs
>= 0 else lhs
<< -rhs
156 return lambda state
: normalize(sshr(lhs(state
), rhs(state
)), shape
)
158 return lambda state
: normalize(lhs(state
) == rhs(state
), shape
)
160 return lambda state
: normalize(lhs(state
) != rhs(state
), shape
)
162 return lambda state
: normalize(lhs(state
) < rhs(state
), shape
)
164 return lambda state
: normalize(lhs(state
) <= rhs(state
), shape
)
166 return lambda state
: normalize(lhs(state
) > rhs(state
), shape
)
168 return lambda state
: normalize(lhs(state
) >= rhs(state
), shape
)
169 elif len(value
.operands
) == 3:
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:
175 def on_Slice(self
, value
):
176 shape
= value
.shape()
177 arg
= self(value
.value
)
179 mask
= (1 << (value
.end
- value
.start
)) - 1
180 return lambda state
: normalize((arg(state
) >> shift
) & mask
, shape
)
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
)
190 def on_Cat(self
, value
):
191 shape
= value
.shape()
194 for opnd
in value
.parts
:
195 parts
.append((offset
, (1 << len(opnd
)) - 1, self(opnd
)))
199 for offset
, mask
, opnd
in parts
:
200 result |
= (opnd(state
) & mask
) << offset
201 return normalize(result
, shape
)
204 def on_Repl(self
, value
):
205 shape
= value
.shape()
206 offset
= len(value
.value
)
207 mask
= (1 << len(value
.value
)) - 1
209 opnd
= self(value
.value
)
212 for _
in range(count
):
214 result |
= opnd(state
)
215 return normalize(result
, shape
)
218 def on_ArrayProxy(self
, value
):
219 shape
= value
.shape()
220 elems
= list(map(self
, value
.elems
))
221 index
= self(value
.index
)
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
)
230 class _LHSValueCompiler(_ValueCompiler
):
231 def __init__(self
, signal_slots
, rhs_compiler
):
232 self
.signal_slots
= signal_slots
233 self
.rhs_compiler
= rhs_compiler
235 def on_Const(self
, value
):
236 raise TypeError # :nocov:
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
))
245 def on_ClockSignal(self
, value
):
246 raise NotImplementedError # :nocov:
248 def on_ResetSignal(self
, value
):
249 raise NotImplementedError # :nocov:
251 def on_Operator(self
, value
):
252 raise TypeError # :nocov:
254 def on_Slice(self
, value
):
255 lhs_r
= self
.rhs_compiler(value
.value
)
256 lhs_l
= self(value
.value
)
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
)
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
)
280 def on_Cat(self
, value
):
283 for opnd
in value
.parts
:
284 parts
.append((offset
, (1 << len(opnd
)) - 1, self(opnd
)))
286 def eval(state
, rhs
):
287 for offset
, mask
, opnd
in parts
:
288 opnd(state
, (rhs
>> offset
) & mask
)
291 def on_Repl(self
, value
):
292 raise TypeError # :nocov:
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
)
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
)
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
)
317 lhs(state
, normalize(rhs(state
), shape
))
320 def on_Assert(self
, stmt
):
321 raise NotImplementedError("Asserts not yet implemented for Simulator backend.") # :nocov:
323 def on_Assume(self
, stmt
):
326 def on_Switch(self
, stmt
):
327 test
= self
.rrhs_compiler(stmt
.test
)
329 for values
, stmts
in stmt
.cases
.items():
331 check
= lambda test
: True
333 check
= lambda test
: False
334 def make_check(mask
, value
, prev_check
):
335 return lambda test
: prev_check(test
) or test
& mask
== 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
)
341 mask
= "1" * len(value
)
343 value
= int(value
, 2)
344 check
= make_check(mask
, value
, check
)
345 cases
.append((check
, self
.on_statements(stmts
)))
347 test_value
= test(state
)
348 for check
, body
in cases
:
349 if check(test_value
):
354 def on_statements(self
, stmts
):
355 stmts
= [self
.on_statement(stmt
) for stmt
in stmts
]
362 class _SimulatorPlatform
:
363 def get_reset_sync(self
, reset_sync
):
365 cd
= ClockDomain("_reset_sync_{}".format(DUID().duid
), async_reset
=True)
367 for i
, o
in zip((0, *reset_sync
._regs
), reset_sync
._regs
):
368 m
.d
[cd
.name
] += o
.eq(i
)
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])
378 def __init__(self
, fragment
, vcd_file
=None, gtkw_file
=None, traces
=()):
379 self
._fragment
= Fragment
.get(fragment
, platform
=_SimulatorPlatform())
381 self
._signal
_slots
= SignalDict() # Signal -> int/slot
382 self
._slot
_signals
= list() # int/slot -> Signal
384 self
._domains
= dict() # str/domain -> ClockDomain
385 self
._domain
_triggers
= list() # int/slot -> str/domain
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}
393 self
._started
= False
396 self
._epsilon
= 1e-10
397 self
._fastest
_clock
= self
._epsilon
398 self
._all
_clocks
= set() # {str/domain}
399 self
._state
= _State()
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
408 self
._funclets
= list() # int/slot -> set(lambda)
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
417 self
._run
_called
= False
420 def _check_process(process
):
421 if inspect
.isgeneratorfunction(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"
429 def _name_process(self
, process
):
430 if process
in self
._process
_loc
:
431 return self
._process
_loc
[process
]
433 frame
= process
.gi_frame
434 return "{}:{}".format(inspect
.getfile(frame
), inspect
.getlineno(frame
))
436 def add_process(self
, process
):
437 process
= self
._check
_process
(process
)
438 self
._processes
.add(process
)
440 def add_sync_process(self
, process
, domain
="sync"):
441 process
= self
._check
_process
(process
)
449 self
._process
_loc
[sync_process
] = self
._name
_process
(process
)
450 cmd
= process
.send(result
)
451 except StopIteration:
453 sync_process
= sync_process()
454 self
.add_process(sync_process
)
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"
463 half_period
= period
/ 2
466 clk
= self
._domains
[domain
].clk
472 yield Delay(half_period
)
474 yield Delay(half_period
)
475 self
.add_process(clk_process
)
476 self
._all
_clocks
.add(domain
)
480 self
._vcd
_writer
= VCDWriter(self
._vcd
_file
, timescale
="100 ps",
481 comment
="Generated by nMigen")
483 root_fragment
= self
._fragment
.prepare()
484 self
._domains
= root_fragment
.domains
487 def add_fragment(fragment
, scope
=()):
488 hierarchy
[fragment
] = scope
489 for index
, (subfragment
, name
) in enumerate(fragment
.subfragments
):
491 add_fragment(subfragment
, (*scope
, "U{}".format(index
)))
493 add_fragment(subfragment
, (*scope
, name
))
494 add_fragment(root_fragment
, scope
=("top",))
496 def add_signal(signal
):
497 if signal
not in self
._signals
:
498 self
._signals
.add(signal
)
500 signal_slot
= self
._state
.add(normalize(signal
.reset
, signal
.shape()))
501 self
._signal
_slots
[signal
] = signal_slot
502 self
._slot
_signals
.append(signal
)
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)
512 self
._funclets
.append(set())
514 self
._domain
_triggers
.append(None)
516 self
._vcd
_signals
.append(set())
517 self
._vcd
_names
.append(None)
519 return self
._signal
_slots
[signal
]
521 def add_domain_signal(signal
, domain
):
522 signal_slot
= add_signal(signal
)
523 self
._domain
_triggers
[signal_slot
] = domain
525 for fragment
, fragment_scope
in hierarchy
.items():
526 for signal
in fragment
.iter_signals():
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
)
534 for fragment
, fragment_scope
in hierarchy
.items():
535 for signal
in fragment
.iter_signals():
536 if not self
._vcd
_writer
:
539 signal_slot
= self
._signal
_slots
[signal
]
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
)
546 var_name
= signal
.name
551 var_init
= signal
.decoder(signal
.reset
).expandtabs().replace(" ", "_")
554 var_size
= signal
.nbits
555 var_init
= signal
.reset
561 var_name_suffix
= var_name
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
,))
572 suffix
= (suffix
or 0) + 1
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
581 self
._comb
_signals |
= signals_bits
583 self
._sync
_signals |
= signals_bits
584 self
._domain
_signals
[domain
] |
= signals_bits
587 for domain
, signals
in fragment
.drivers
.items():
590 for signal
in signals
:
591 reset_stmts
.append(signal
.eq(signal
.reset
))
592 hold_stmts
.append(signal
.eq(signal
))
595 statements
+= reset_stmts
597 if self
._domains
[domain
].async_reset
:
598 statements
.append(Switch(self
._domains
[domain
].rst
,
599 {0: hold_stmts
, 1: reset_stmts
}))
601 statements
+= hold_stmts
602 statements
+= fragment
.statements
604 compiler
= _StatementCompiler(self
._signal
_slots
)
605 funclet
= compiler(statements
)
607 def add_funclet(signal
, funclet
):
608 if signal
in self
._signal
_slots
:
609 self
._funclets
[self
._signal
_slots
[signal
]].add(funclet
)
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
)
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
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
631 for signal_slot
in self
._state
.flush_curr_dirty():
632 funclets
.update(self
._funclets
[signal_slot
])
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
:
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
)
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
])
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
]
657 var_value
= signal
.decoder(new
).expandtabs().replace(" ", "_")
660 vcd_timestamp
= (self
._timestamp
+ self
._delta
) / self
._epsilon
661 self
._vcd
_writer
.change(vcd_signal
, vcd_timestamp
, var_value
)
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
)
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.
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()
681 domain
= curr_domains
.pop()
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
)
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
)
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
)
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.
707 def _run_process(self
, process
):
709 cmd
= process
.send(None)
711 if type(cmd
) is Delay
:
712 if cmd
.interval
is None:
713 interval
= self
._epsilon
715 interval
= cmd
.interval
716 self
._wait
_deadline
[process
] = self
._timestamp
+ interval
717 self
._suspended
.add(process
)
720 elif type(cmd
) is Tick
:
721 self
._wait
_tick
[process
] = cmd
.domain
722 self
._suspended
.add(process
)
725 elif type(cmd
) is Passive
:
726 self
._passive
.add(process
)
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 "
740 .format(self
._name
_process
(process
), signal
))
742 if type(cmd
.lhs
) is Signal
and type(cmd
.rhs
) is Const
:
744 self
._state
.set(self
._signal
_slots
[cmd
.lhs
],
745 normalize(cmd
.rhs
.value
, cmd
.lhs
.shape()))
747 compiler
= _StatementCompiler(self
._signal
_slots
)
748 funclet
= compiler(cmd
)
752 for signal
in lhs_signals
:
753 self
._commit
_signal
(self
._signal
_slots
[signal
], domains
)
754 self
._commit
_sync
_signals
(domains
)
756 elif type(cmd
) is Signal
:
758 cmd
= process
.send(self
._state
.curr
[self
._signal
_slots
[cmd
]])
761 elif isinstance(cmd
, Value
):
762 compiler
= _RHSValueCompiler(self
._signal
_slots
)
763 funclet
= compiler(cmd
)
764 cmd
= process
.send(funclet(self
._state
))
768 raise TypeError("Received unsupported command '{!r}' from process '{}'"
769 .format(cmd
, self
._name
_process
(process
)))
771 cmd
= process
.send(None)
773 except StopIteration:
774 self
._processes
.remove(process
)
775 self
._passive
.discard(process
)
777 except Exception as e
:
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?")
792 while self
._state
.curr_dirty
.any():
793 self
._update
_dirty
_signals
()
794 self
._commit
_comb
_signals
(domains
)
795 self
._commit
_sync
_signals
(domains
)
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
)
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
815 self
._run
_process
(process
)
818 # No processes, or all processes are passive. Nothing to do!
822 self
._run
_called
= True
827 def run_until(self
, deadline
, run_passive
=False):
828 self
._run
_called
= True
830 while self
._timestamp
< deadline
:
831 if not self
.step(run_passive
):
836 def __exit__(self
, *args
):
837 if not self
._run
_called
:
838 warnings
.warn("Simulation created, but not run", UserWarning)
841 vcd_timestamp
= (self
._timestamp
+ self
._delta
) / self
._epsilon
842 self
._vcd
_writer
.close(vcd_timestamp
)
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())
851 gtkw_save
.treeopen("top")
852 gtkw_save
.zoom_markers(math
.log(self
._epsilon
/ self
._fastest
_clock
) - 14)
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)
861 gtkw_save
.trace(self
._vcd
_names
[signal_slot
] + suffix
, **kwargs
)
863 for domain
, cd
in self
._domains
.items():
864 with gtkw_save
.group("d.{}".format(domain
)):
865 if cd
.rst
is not None:
869 for signal
in self
._traces
:
873 self
._vcd
_file
.close()
875 self
._gtkw
_file
.close()