5 from contextlib
import contextmanager
7 from vcd
import VCDWriter
8 from vcd
.gtkw
import GTKWSave
10 from .._utils
import deprecated
11 from ..hdl
.ast
import *
12 from ..hdl
.cd
import *
13 from ..hdl
.ir
import *
14 from ..hdl
.xfrm
import ValueVisitor
, StatementVisitor
, LHSGroupFilter
21 class Settle(Command
):
27 def __init__(self
, interval
=None):
28 self
.interval
= None if interval
is None else float(interval
)
31 if self
.interval
is None:
34 return "(delay {:.3}us)".format(self
.interval
* 1e6
)
38 def __init__(self
, domain
="sync"):
39 if not isinstance(domain
, (str, ClockDomain
)):
40 raise TypeError("Domain must be a string or a ClockDomain instance, not {!r}"
42 assert domain
!= "comb"
46 return "(tick {})".format(self
.domain
)
49 class Passive(Command
):
54 class Active(Command
):
59 class _WaveformWriter
:
60 def update(self
, timestamp
, signal
, value
):
61 raise NotImplementedError # :nocov:
63 def close(self
, timestamp
):
64 raise NotImplementedError # :nocov:
67 class _VCDWaveformWriter(_WaveformWriter
):
69 def timestamp_to_vcd(timestamp
):
70 return timestamp
* (10 ** 10) # 1/(100 ps)
73 def decode_to_vcd(signal
, value
):
74 return signal
.decoder(value
).expandtabs().replace(" ", "_")
76 def __init__(self
, signal_names
, *, vcd_file
, gtkw_file
=None, traces
=()):
77 if isinstance(vcd_file
, str):
78 vcd_file
= open(vcd_file
, "wt")
79 if isinstance(gtkw_file
, str):
80 gtkw_file
= open(gtkw_file
, "wt")
82 self
.vcd_vars
= SignalDict()
83 self
.vcd_file
= vcd_file
84 self
.vcd_writer
= vcd_file
and VCDWriter(self
.vcd_file
,
85 timescale
="100 ps", comment
="Generated by nMigen")
87 self
.gtkw_names
= SignalDict()
88 self
.gtkw_file
= gtkw_file
89 self
.gtkw_save
= gtkw_file
and GTKWSave(self
.gtkw_file
)
93 trace_names
= SignalDict()
95 if trace
not in signal_names
:
96 trace_names
[trace
] = {("top", trace
.name
)}
97 self
.traces
.append(trace
)
99 if self
.vcd_writer
is None:
102 for signal
, names
in itertools
.chain(signal_names
.items(), trace_names
.items()):
106 var_init
= self
.decode_to_vcd(signal
, signal
.reset
)
109 var_size
= signal
.width
110 var_init
= signal
.reset
112 for (*var_scope
, var_name
) in names
:
117 var_name_suffix
= var_name
119 var_name_suffix
= "{}${}".format(var_name
, suffix
)
120 vcd_var
= self
.vcd_writer
.register_var(
121 scope
=var_scope
, name
=var_name_suffix
,
122 var_type
=var_type
, size
=var_size
, init
=var_init
)
125 suffix
= (suffix
or 0) + 1
127 if signal
not in self
.vcd_vars
:
128 self
.vcd_vars
[signal
] = set()
129 self
.vcd_vars
[signal
].add(vcd_var
)
131 if signal
not in self
.gtkw_names
:
132 self
.gtkw_names
[signal
] = (*var_scope
, var_name_suffix
)
134 def update(self
, timestamp
, signal
, value
):
135 if signal
not in self
.vcd_vars
:
138 vcd_timestamp
= self
.timestamp_to_vcd(timestamp
)
140 var_value
= self
.decode_to_vcd(signal
, value
)
143 for vcd_var
in self
.vcd_vars
[signal
]:
144 self
.vcd_writer
.change(vcd_var
, vcd_timestamp
, var_value
)
146 def close(self
, timestamp
):
147 if self
.vcd_writer
is not None:
148 self
.vcd_writer
.close(self
.timestamp_to_vcd(timestamp
))
150 if self
.gtkw_save
is not None:
151 self
.gtkw_save
.dumpfile(self
.vcd_file
.name
)
152 self
.gtkw_save
.dumpfile_size(self
.vcd_file
.tell())
154 self
.gtkw_save
.treeopen("top")
155 for signal
in self
.traces
:
156 if len(signal
) > 1 and not signal
.decoder
:
157 suffix
= "[{}:0]".format(len(signal
) - 1)
160 self
.gtkw_save
.trace(".".join(self
.gtkw_names
[signal
]) + suffix
)
162 if self
.vcd_file
is not None:
163 self
.vcd_file
.close()
164 if self
.gtkw_file
is not None:
165 self
.gtkw_file
.close()
169 __slots__
= ("runnable", "passive")
172 raise NotImplementedError # :nocov:
175 raise NotImplementedError # :nocov:
179 raise NotImplementedError # :nocov:
183 __slots__
= ("signal", "curr", "next", "waiters", "pending")
185 def __init__(self
, signal
, pending
):
187 self
.pending
= pending
188 self
.waiters
= dict()
192 self
.curr
= self
.next
= self
.signal
.reset
194 def set(self
, value
):
195 if self
.next
== value
:
198 self
.pending
.add(self
)
200 def wait(self
, task
, *, trigger
=None):
201 assert task
not in self
.waiters
202 self
.waiters
[task
] = trigger
205 if self
.curr
== self
.next
:
207 self
.curr
= self
.next
212 for process
, trigger
in self
.waiters
.items():
213 if trigger
is None or trigger
== self
.curr
:
214 process
.runnable
= awoken_any
= True
218 class _SimulatorState
:
220 self
.signals
= SignalDict()
224 self
.deadlines
= dict()
226 self
.waveform_writer
= None
229 for signal_state
in self
.signals
.values():
234 self
.deadlines
.clear()
236 def for_signal(self
, signal
):
238 return self
.signals
[signal
]
240 signal_state
= _SignalState(signal
, self
.pending
)
241 self
.signals
[signal
] = signal_state
246 for signal_state
in self
.pending
:
247 if signal_state
.commit():
248 if signal_state
.wakeup():
250 if self
.waveform_writer
is not None:
251 self
.waveform_writer
.update(self
.timestamp
,
252 signal_state
.signal
, signal_state
.curr
)
256 nearest_processes
= set()
257 nearest_deadline
= None
258 for process
, deadline
in self
.deadlines
.items():
260 if nearest_deadline
is not None:
261 nearest_processes
.clear()
262 nearest_processes
.add(process
)
263 nearest_deadline
= self
.timestamp
265 elif nearest_deadline
is None or deadline
<= nearest_deadline
:
266 assert deadline
>= self
.timestamp
267 if nearest_deadline
is not None and deadline
< nearest_deadline
:
268 nearest_processes
.clear()
269 nearest_processes
.add(process
)
270 nearest_deadline
= deadline
272 if not nearest_processes
:
275 for process
in nearest_processes
:
276 process
.runnable
= True
277 del self
.deadlines
[process
]
278 self
.timestamp
= nearest_deadline
282 def start_waveform(self
, waveform_writer
):
283 if self
.timestamp
!= 0.0:
284 raise ValueError("Cannot start writing waveforms after advancing simulation time")
285 if self
.waveform_writer
is not None:
286 raise ValueError("Already writing waveforms to {!r}"
287 .format(self
.waveform_writer
))
288 self
.waveform_writer
= waveform_writer
290 def finish_waveform(self
):
291 if self
.waveform_writer
is None:
293 self
.waveform_writer
.close(self
.timestamp
)
294 self
.waveform_writer
= None
298 __slots__
= ("state", "indexes", "slots")
300 def __init__(self
, state
):
302 self
.indexes
= SignalDict()
305 def get_signal(self
, signal
):
307 return self
.indexes
[signal
]
309 index
= len(self
.slots
)
310 self
.slots
.append(self
.state
.for_signal(signal
))
311 self
.indexes
[signal
] = index
314 def get_in_signal(self
, signal
, *, trigger
=None):
315 index
= self
.get_signal(signal
)
316 self
.slots
[index
].waiters
[self
] = trigger
319 def get_out_signal(self
, signal
):
320 return self
.get_signal(signal
)
329 def append(self
, code
):
330 self
._buffer
.append(" " * self
._level
)
331 self
._buffer
.append(code
)
332 self
._buffer
.append("\n")
340 def flush(self
, indent
=""):
341 code
= "".join(self
._buffer
)
345 def gen_var(self
, prefix
):
346 name
= f
"{prefix}_{self._suffix}"
350 def def_var(self
, prefix
, value
):
351 name
= self
.gen_var(prefix
)
352 self
.append(f
"{name} = {value}")
357 def __init__(self
, context
, emitter
):
358 self
.context
= context
359 self
.emitter
= emitter
362 class _ValueCompiler(ValueVisitor
, _Compiler
):
364 "sign": lambda value
, sign
: value | sign
if value
& sign
else value
,
365 "zdiv": lambda lhs
, rhs
: 0 if rhs
== 0 else lhs
// rhs
,
366 "zmod": lambda lhs
, rhs
: 0 if rhs
== 0 else lhs
% rhs
,
369 def on_ClockSignal(self
, value
):
370 raise NotImplementedError # :nocov:
372 def on_ResetSignal(self
, value
):
373 raise NotImplementedError # :nocov:
375 def on_Record(self
, value
):
376 return self(Cat(value
.fields
.values()))
378 def on_AnyConst(self
, value
):
379 raise NotImplementedError # :nocov:
381 def on_AnySeq(self
, value
):
382 raise NotImplementedError # :nocov:
384 def on_Sample(self
, value
):
385 raise NotImplementedError # :nocov:
387 def on_Initial(self
, value
):
388 raise NotImplementedError # :nocov:
391 class _RHSValueCompiler(_ValueCompiler
):
392 def __init__(self
, context
, emitter
, *, mode
, inputs
=None):
393 super().__init
__(context
, emitter
)
394 assert mode
in ("curr", "next")
396 # If not None, `inputs` gets populated with RHS signals.
399 def on_Const(self
, value
):
400 return f
"{value.value}"
402 def on_Signal(self
, value
):
403 if self
.inputs
is not None:
404 self
.inputs
.add(value
)
406 if self
.mode
== "curr":
407 return f
"slots[{self.context.get_signal(value)}].{self.mode}"
409 return f
"next_{self.context.get_signal(value)}"
411 def on_Operator(self
, value
):
413 value_mask
= (1 << len(value
)) - 1
414 return f
"({self(value)} & {value_mask})"
417 if value
.shape().signed
:
418 return f
"sign({mask(value)}, {-1 << (len(value) - 1)})"
422 if len(value
.operands
) == 1:
423 arg
, = value
.operands
424 if value
.operator
== "~":
425 return f
"(~{self(arg)})"
426 if value
.operator
== "-":
427 return f
"(-{self(arg)})"
428 if value
.operator
== "b":
429 return f
"bool({mask(arg)})"
430 if value
.operator
== "r|":
431 return f
"({mask(arg)} != 0)"
432 if value
.operator
== "r&":
433 return f
"({mask(arg)} == {(1 << len(arg)) - 1})"
434 if value
.operator
== "r^":
435 # Believe it or not, this is the fastest way to compute a sideways XOR in Python.
436 return f
"(format({mask(arg)}, 'b').count('1') % 2)"
437 if value
.operator
in ("u", "s"):
438 # These operators don't change the bit pattern, only its interpretation.
440 elif len(value
.operands
) == 2:
441 lhs
, rhs
= value
.operands
442 lhs_mask
= (1 << len(lhs
)) - 1
443 rhs_mask
= (1 << len(rhs
)) - 1
444 if value
.operator
== "+":
445 return f
"({sign(lhs)} + {sign(rhs)})"
446 if value
.operator
== "-":
447 return f
"({sign(lhs)} - {sign(rhs)})"
448 if value
.operator
== "*":
449 return f
"({sign(lhs)} * {sign(rhs)})"
450 if value
.operator
== "//":
451 return f
"zdiv({sign(lhs)}, {sign(rhs)})"
452 if value
.operator
== "%":
453 return f
"zmod({sign(lhs)}, {sign(rhs)})"
454 if value
.operator
== "&":
455 return f
"({self(lhs)} & {self(rhs)})"
456 if value
.operator
== "|":
457 return f
"({self(lhs)} | {self(rhs)})"
458 if value
.operator
== "^":
459 return f
"({self(lhs)} ^ {self(rhs)})"
460 if value
.operator
== "<<":
461 return f
"({sign(lhs)} << {sign(rhs)})"
462 if value
.operator
== ">>":
463 return f
"({sign(lhs)} >> {sign(rhs)})"
464 if value
.operator
== "==":
465 return f
"({sign(lhs)} == {sign(rhs)})"
466 if value
.operator
== "!=":
467 return f
"({sign(lhs)} != {sign(rhs)})"
468 if value
.operator
== "<":
469 return f
"({sign(lhs)} < {sign(rhs)})"
470 if value
.operator
== "<=":
471 return f
"({sign(lhs)} <= {sign(rhs)})"
472 if value
.operator
== ">":
473 return f
"({sign(lhs)} > {sign(rhs)})"
474 if value
.operator
== ">=":
475 return f
"({sign(lhs)} >= {sign(rhs)})"
476 elif len(value
.operands
) == 3:
477 if value
.operator
== "m":
478 sel
, val1
, val0
= value
.operands
479 return f
"({self(val1)} if {self(sel)} else {self(val0)})"
480 raise NotImplementedError("Operator '{}' not implemented".format(value
.operator
)) # :nocov:
482 def on_Slice(self
, value
):
483 return f
"(({self(value.value)} >> {value.start}) & {(1 << len(value)) - 1})"
485 def on_Part(self
, value
):
486 offset_mask
= (1 << len(value
.offset
)) - 1
487 offset
= f
"(({self(value.offset)} & {offset_mask}) * {value.stride})"
488 return f
"({self(value.value)} >> {offset} & " \
489 f
"{(1 << value.width) - 1})"
491 def on_Cat(self
, value
):
494 for part
in value
.parts
:
495 part_mask
= (1 << len(part
)) - 1
496 gen_parts
.append(f
"(({self(part)} & {part_mask}) << {offset})")
499 return f
"({' | '.join(gen_parts)})"
502 def on_Repl(self
, value
):
503 part_mask
= (1 << len(value
.value
)) - 1
504 gen_part
= self
.emitter
.def_var("repl", f
"{self(value.value)} & {part_mask}")
507 for _
in range(value
.count
):
508 gen_parts
.append(f
"({gen_part} << {offset})")
509 offset
+= len(value
.value
)
511 return f
"({' | '.join(gen_parts)})"
514 def on_ArrayProxy(self
, value
):
515 index_mask
= (1 << len(value
.index
)) - 1
516 gen_index
= self
.emitter
.def_var("rhs_index", f
"{self(value.index)} & {index_mask}")
517 gen_value
= self
.emitter
.gen_var("rhs_proxy")
520 for index
, elem
in enumerate(value
.elems
):
522 self
.emitter
.append(f
"if {gen_index} == {index}:")
524 self
.emitter
.append(f
"elif {gen_index} == {index}:")
525 with self
.emitter
.indent():
526 self
.emitter
.append(f
"{gen_value} = {self(elem)}")
527 self
.emitter
.append(f
"else:")
528 with self
.emitter
.indent():
529 self
.emitter
.append(f
"{gen_value} = {self(value.elems[-1])}")
535 def compile(cls
, context
, value
, *, mode
, inputs
=None):
537 compiler
= cls(context
, emitter
, mode
=mode
, inputs
=inputs
)
538 emitter
.append(f
"result = {compiler(value)}")
539 return emitter
.flush()
542 class _LHSValueCompiler(_ValueCompiler
):
543 def __init__(self
, context
, emitter
, *, rhs
, outputs
=None):
544 super().__init
__(context
, emitter
)
545 # `rrhs` is used to translate rvalues that are syntactically a part of an lvalue, e.g.
546 # the offset of a Part.
548 # `lrhs` is used to translate the read part of a read-modify-write cycle during partial
549 # update of an lvalue.
550 self
.lrhs
= _RHSValueCompiler(context
, emitter
, mode
="next", inputs
=None)
551 # If not None, `outputs` gets populated with signals on LHS.
552 self
.outputs
= outputs
554 def on_Const(self
, value
):
555 raise TypeError # :nocov:
557 def on_Signal(self
, value
):
558 if self
.outputs
is not None:
559 self
.outputs
.add(value
)
562 value_mask
= (1 << len(value
)) - 1
563 if value
.shape().signed
:
564 value_sign
= f
"sign({arg} & {value_mask}, {-1 << (len(value) - 1)})"
566 value_sign
= f
"{arg} & {value_mask}"
567 self
.emitter
.append(f
"next_{self.context.get_out_signal(value)} = {value_sign}")
570 def on_Operator(self
, value
):
571 raise TypeError # :nocov:
573 def on_Slice(self
, value
):
575 width_mask
= (1 << (value
.stop
- value
.start
)) - 1
576 self(value
.value
)(f
"({self.lrhs(value.value)} & " \
577 f
"{~(width_mask << value.start)} | " \
578 f
"(({arg} & {width_mask}) << {value.start}))")
581 def on_Part(self
, value
):
583 width_mask
= (1 << value
.width
) - 1
584 offset_mask
= (1 << len(value
.offset
)) - 1
585 offset
= f
"(({self.rrhs(value.offset)} & {offset_mask}) * {value.stride})"
586 self(value
.value
)(f
"({self.lrhs(value.value)} & " \
587 f
"~({width_mask} << {offset}) | " \
588 f
"(({arg} & {width_mask}) << {offset}))")
591 def on_Cat(self
, value
):
593 gen_arg
= self
.emitter
.def_var("cat", arg
)
596 for part
in value
.parts
:
597 part_mask
= (1 << len(part
)) - 1
598 self(part
)(f
"(({gen_arg} >> {offset}) & {part_mask})")
602 def on_Repl(self
, value
):
603 raise TypeError # :nocov:
605 def on_ArrayProxy(self
, value
):
607 index_mask
= (1 << len(value
.index
)) - 1
608 gen_index
= self
.emitter
.def_var("index", f
"{self.rrhs(value.index)} & {index_mask}")
611 for index
, elem
in enumerate(value
.elems
):
613 self
.emitter
.append(f
"if {gen_index} == {index}:")
615 self
.emitter
.append(f
"elif {gen_index} == {index}:")
616 with self
.emitter
.indent():
618 self
.emitter
.append(f
"else:")
619 with self
.emitter
.indent():
620 self(value
.elems
[-1])(arg
)
622 self
.emitter
.append(f
"pass")
626 def compile(cls
, context
, stmt
, *, inputs
=None, outputs
=None):
628 compiler
= cls(context
, emitter
, inputs
=inputs
, outputs
=outputs
)
630 return emitter
.flush()
633 class _StatementCompiler(StatementVisitor
, _Compiler
):
634 def __init__(self
, context
, emitter
, *, inputs
=None, outputs
=None):
635 super().__init
__(context
, emitter
)
636 self
.rhs
= _RHSValueCompiler(context
, emitter
, mode
="curr", inputs
=inputs
)
637 self
.lhs
= _LHSValueCompiler(context
, emitter
, rhs
=self
.rhs
, outputs
=outputs
)
639 def on_statements(self
, stmts
):
643 self
.emitter
.append("pass")
645 def on_Assign(self
, stmt
):
646 return self
.lhs(stmt
.lhs
)(self
.rhs(stmt
.rhs
))
648 def on_Switch(self
, stmt
):
649 gen_test
= self
.emitter
.def_var("test",
650 f
"{self.rhs(stmt.test)} & {(1 << len(stmt.test)) - 1}")
651 for index
, (patterns
, stmts
) in enumerate(stmt
.cases
.items()):
654 gen_checks
.append(f
"True")
656 for pattern
in patterns
:
658 mask
= int("".join("0" if b
== "-" else "1" for b
in pattern
), 2)
659 value
= int("".join("0" if b
== "-" else b
for b
in pattern
), 2)
660 gen_checks
.append(f
"({gen_test} & {mask}) == {value}")
662 value
= int(pattern
, 2)
663 gen_checks
.append(f
"{gen_test} == {value}")
665 self
.emitter
.append(f
"if {' or '.join(gen_checks)}:")
667 self
.emitter
.append(f
"elif {' or '.join(gen_checks)}:")
668 with self
.emitter
.indent():
671 def on_Assert(self
, stmt
):
672 raise NotImplementedError # :nocov:
674 def on_Assume(self
, stmt
):
675 raise NotImplementedError # :nocov:
677 def on_Cover(self
, stmt
):
678 raise NotImplementedError # :nocov:
681 def compile(cls
, context
, stmt
, *, inputs
=None, outputs
=None):
682 output_indexes
= [context
.get_signal(signal
) for signal
in stmt
._lhs
_signals
()]
684 for signal_index
in output_indexes
:
685 emitter
.append(f
"next_{signal_index} = slots[{signal_index}].next")
686 compiler
= cls(context
, emitter
, inputs
=inputs
, outputs
=outputs
)
688 for signal_index
in output_indexes
:
689 emitter
.append(f
"slots[{signal_index}].set(next_{signal_index})")
690 return emitter
.flush()
693 class _CompiledProcess(_Process
):
694 __slots__
= ("context", "comb", "name", "run")
696 def __init__(self
, state
, *, comb
, name
):
697 self
.context
= _EvalContext(state
)
700 self
.run
= None # set by _FragmentCompiler
704 self
.runnable
= self
.comb
708 class _FragmentCompiler
:
709 def __init__(self
, state
, signal_names
):
711 self
.signal_names
= signal_names
713 def __call__(self
, fragment
, *, hierarchy
=("top",)):
716 def add_signal_name(signal
):
717 hierarchical_signal_name
= (*hierarchy
, signal
.name
)
718 if signal
not in self
.signal_names
:
719 self
.signal_names
[signal
] = {hierarchical_signal_name}
721 self
.signal_names
[signal
].add(hierarchical_signal_name
)
723 for domain_name
, domain_signals
in fragment
.drivers
.items():
724 domain_stmts
= LHSGroupFilter(domain_signals
)(fragment
.statements
)
725 domain_process
= _CompiledProcess(self
.state
, comb
=domain_name
is None,
726 name
=".".join((*hierarchy
, "<{}>".format(domain_name
or "comb"))))
729 emitter
.append(f
"def run():")
732 if domain_name
is None:
733 for signal
in domain_signals
:
734 signal_index
= domain_process
.context
.get_signal(signal
)
735 emitter
.append(f
"next_{signal_index} = {signal.reset}")
738 _StatementCompiler(domain_process
.context
, emitter
, inputs
=inputs
)(domain_stmts
)
741 self
.state
.for_signal(input).wait(domain_process
)
744 domain
= fragment
.domains
[domain_name
]
745 add_signal_name(domain
.clk
)
746 if domain
.rst
is not None:
747 add_signal_name(domain
.rst
)
749 clk_trigger
= 1 if domain
.clk_edge
== "pos" else 0
750 self
.state
.for_signal(domain
.clk
).wait(domain_process
, trigger
=clk_trigger
)
751 if domain
.rst
is not None and domain
.async_reset
:
753 self
.state
.for_signal(domain
.rst
).wait(domain_process
, trigger
=rst_trigger
)
756 clk_index
= domain_process
.context
.get_signal(domain
.clk
)
757 gen_asserts
.append(f
"slots[{clk_index}].curr == {clk_trigger}")
758 if domain
.rst
is not None and domain
.async_reset
:
759 rst_index
= domain_process
.context
.get_signal(domain
.rst
)
760 gen_asserts
.append(f
"slots[{rst_index}].curr == {rst_trigger}")
761 emitter
.append(f
"assert {' or '.join(gen_asserts)}")
763 for signal
in domain_signals
:
764 signal_index
= domain_process
.context
.get_signal(signal
)
765 emitter
.append(f
"next_{signal_index} = slots[{signal_index}].next")
767 _StatementCompiler(domain_process
.context
, emitter
)(domain_stmts
)
769 for signal
in domain_signals
:
770 signal_index
= domain_process
.context
.get_signal(signal
)
771 emitter
.append(f
"slots[{signal_index}].set(next_{signal_index})")
773 # There shouldn't be any exceptions raised by the generated code, but if there are
774 # (almost certainly due to a bug in the code generator), use this environment variable
775 # to make backtraces useful.
776 code
= emitter
.flush()
777 if os
.getenv("NMIGEN_pysim_dump"):
778 file = tempfile
.NamedTemporaryFile("w", prefix
="nmigen_pysim_", delete
=False)
782 filename
= "<string>"
784 exec_locals
= {"slots": domain_process
.context
.slots
, **_ValueCompiler
.helpers
}
785 exec(compile(code
, filename
, "exec"), exec_locals
)
786 domain_process
.run
= exec_locals
["run"]
788 processes
.add(domain_process
)
790 for used_signal
in domain_process
.context
.indexes
:
791 add_signal_name(used_signal
)
793 for subfragment_index
, (subfragment
, subfragment_name
) in enumerate(fragment
.subfragments
):
794 if subfragment_name
is None:
795 subfragment_name
= "U${}".format(subfragment_index
)
796 processes
.update(self(subfragment
, hierarchy
=(*hierarchy
, subfragment_name
)))
801 class _CoroutineProcess(_Process
):
802 def __init__(self
, state
, domains
, constructor
, *, default_cmd
=None):
804 self
.domains
= domains
805 self
.constructor
= constructor
806 self
.default_cmd
= default_cmd
812 self
.coroutine
= self
.constructor()
813 self
.eval_context
= _EvalContext(self
.state
)
815 "slots": self
.eval_context
.slots
,
817 **_ValueCompiler
.helpers
819 self
.waits_on
= set()
823 coroutine
= self
.coroutine
824 while coroutine
.gi_yieldfrom
is not None:
825 coroutine
= coroutine
.gi_yieldfrom
826 if inspect
.isgenerator(coroutine
):
827 frame
= coroutine
.gi_frame
828 if inspect
.iscoroutine(coroutine
):
829 frame
= coroutine
.cr_frame
830 return "{}:{}".format(inspect
.getfile(frame
), inspect
.getlineno(frame
))
832 def get_in_signal(self
, signal
, *, trigger
=None):
833 signal_state
= self
.state
.for_signal(signal
)
834 assert self
not in signal_state
.waiters
835 signal_state
.waiters
[self
] = trigger
836 self
.waits_on
.add(signal_state
)
840 if self
.coroutine
is None:
844 for signal_state
in self
.waits_on
:
845 del signal_state
.waiters
[self
]
846 self
.waits_on
.clear()
851 command
= self
.coroutine
.send(response
)
853 command
= self
.default_cmd
856 if isinstance(command
, Value
):
857 exec(_RHSValueCompiler
.compile(self
.eval_context
, command
, mode
="curr"),
859 response
= Const
.normalize(self
.exec_locals
["result"], command
.shape())
861 elif isinstance(command
, Statement
):
862 exec(_StatementCompiler
.compile(self
.eval_context
, command
),
865 elif type(command
) is Tick
:
866 domain
= command
.domain
867 if isinstance(domain
, ClockDomain
):
869 elif domain
in self
.domains
:
870 domain
= self
.domains
[domain
]
872 raise NameError("Received command {!r} that refers to a nonexistent "
873 "domain {!r} from process {!r}"
874 .format(command
, command
.domain
, self
.name
))
875 self
.get_in_signal(domain
.clk
, trigger
=1 if domain
.clk_edge
== "pos" else 0)
876 if domain
.rst
is not None and domain
.async_reset
:
877 self
.get_in_signal(domain
.rst
, trigger
=1)
880 elif type(command
) is Settle
:
881 self
.state
.deadlines
[self
] = None
884 elif type(command
) is Delay
:
885 if command
.interval
is None:
886 self
.state
.deadlines
[self
] = None
888 self
.state
.deadlines
[self
] = self
.state
.timestamp
+ command
.interval
891 elif type(command
) is Passive
:
894 elif type(command
) is Active
:
897 elif command
is None: # only possible if self.default_cmd is None
898 raise TypeError("Received default command from process {!r} that was added "
899 "with add_process(); did you mean to add this process with "
900 "add_sync_process() instead?"
904 raise TypeError("Received unsupported command {!r} from process {!r}"
905 .format(command
, self
.name
))
907 except StopIteration:
909 self
.coroutine
= None
912 except Exception as exn
:
913 self
.coroutine
.throw(exn
)
916 class _WaveformContextManager
:
917 def __init__(self
, state
, waveform_writer
):
919 self
._waveform
_writer
= waveform_writer
923 self
._state
.start_waveform(self
._waveform
_writer
)
925 self
._waveform
_writer
.close(0)
928 def __exit__(self
, *args
):
929 self
._state
.finish_waveform()
933 def __init__(self
, fragment
, **kwargs
):
934 self
._state
= _SimulatorState()
935 self
._signal
_names
= SignalDict()
936 self
._fragment
= Fragment
.get(fragment
, platform
=None).prepare()
937 self
._processes
= _FragmentCompiler(self
._state
, self
._signal
_names
)(self
._fragment
)
939 # TODO(nmigen-0.3): remove
940 self
._state
.start_waveform(_VCDWaveformWriter(self
._signal
_names
, **kwargs
))
941 self
._clocked
= set()
943 def _check_process(self
, process
):
944 if not (inspect
.isgeneratorfunction(process
) or inspect
.iscoroutinefunction(process
)):
945 if inspect
.isgenerator(process
) or inspect
.iscoroutine(process
):
946 warnings
.warn("instead of generators, use generator functions as processes; "
947 "this allows the simulator to be repeatedly reset",
948 DeprecationWarning, stacklevel
=3)
953 raise TypeError("Cannot add a process {!r} because it is not a generator function"
957 def _add_coroutine_process(self
, process
, *, default_cmd
):
958 self
._processes
.add(_CoroutineProcess(self
._state
, self
._fragment
.domains
, process
,
959 default_cmd
=default_cmd
))
961 def add_process(self
, process
):
962 process
= self
._check
_process
(process
)
964 # Only start a bench process after comb settling, so that the reset values are correct.
967 self
._add
_coroutine
_process
(wrapper
, default_cmd
=None)
969 def add_sync_process(self
, process
, *, domain
="sync"):
970 process
= self
._check
_process
(process
)
972 # Only start a sync process after the first clock edge (or reset edge, if the domain
973 # uses an asynchronous reset). This matches the behavior of synchronous FFs.
976 return self
._add
_coroutine
_process
(wrapper
, default_cmd
=Tick(domain
))
978 def add_clock(self
, period
, *, phase
=None, domain
="sync", if_exists
=False):
979 """Add a clock process.
981 Adds a process that drives the clock signal of ``domain`` at a 50% duty cycle.
986 Clock period. The process will toggle the ``domain`` clock signal every ``period / 2``
988 phase : None or float
989 Clock phase. The process will wait ``phase`` seconds before the first clock transition.
990 If not specified, defaults to ``period / 2``.
991 domain : str or ClockDomain
992 Driven clock domain. If specified as a string, the domain with that name is looked up
993 in the root fragment of the simulation.
995 If ``False`` (the default), raise an error if the driven domain is specified as
996 a string and the root fragment does not have such a domain. If ``True``, do nothing
999 if isinstance(domain
, ClockDomain
):
1001 elif domain
in self
._fragment
.domains
:
1002 domain
= self
._fragment
.domains
[domain
]
1006 raise ValueError("Domain {!r} is not present in simulation"
1008 if domain
in self
._clocked
:
1009 raise ValueError("Domain {!r} already has a clock driving it"
1010 .format(domain
.name
))
1012 half_period
= period
/ 2
1014 # By default, delay the first edge by half period. This causes any synchronous activity
1015 # to happen at a non-zero time, distinguishing it from the reset values in the waveform
1021 # Behave correctly if the process is added after the clock signal is manipulated, or if
1022 # its reset state is high.
1023 initial
= (yield domain
.clk
)
1025 domain
.clk
.eq(~initial
),
1027 domain
.clk
.eq(initial
),
1031 yield from iter(steps
)
1032 self
._add
_coroutine
_process
(clk_process
, default_cmd
=None)
1033 self
._clocked
.add(domain
)
1036 """Reset the simulation.
1038 Assign the reset value to every signal in the simulation, and restart every user process.
1041 for process
in self
._processes
:
1045 """Perform a delta cycle.
1047 Performs the two phases of a delta cycle:
1048 1. run and suspend every non-waiting process once, queueing signal changes;
1049 2. commit every queued signal change, waking up any waiting process.
1051 for process
in self
._processes
:
1052 if process
.runnable
:
1053 process
.runnable
= False
1056 return self
._state
.commit()
1059 """Settle the simulation.
1061 Run every process and commit changes until a fixed point is reached. If there is
1062 an unstable combinatorial loop, this function will never return.
1064 while self
._delta
():
1068 """Step the simulation.
1070 Run every process and commit changes until a fixed point is reached, then advance time
1071 to the closest deadline (if any). If there is an unstable combinatorial loop,
1072 this function will never return.
1074 Returns ``True`` if there are any active processes, ``False`` otherwise.
1077 self
._state
.advance()
1078 return any(not process
.passive
for process
in self
._processes
)
1081 """Run the simulation while any processes are active.
1083 Processes added with :meth:`add_process` and :meth:`add_sync_process` are initially active,
1084 and may change their status using the ``yield Passive()`` and ``yield Active()`` commands.
1085 Processes compiled from HDL and added with :meth:`add_clock` are always passive.
1090 def run_until(self
, deadline
, *, run_passive
=False):
1091 """Run the simulation until it advances to ``deadline``.
1093 If ``run_passive`` is ``False``, the simulation also stops when there are no active
1094 processes, similar to :meth:`run`. Otherwise, the simulation will stop only after it
1095 advances to or past ``deadline``.
1097 If the simulation stops advancing, this function will never return.
1099 assert self
._state
.timestamp
<= deadline
1100 while (self
.step() or run_passive
) and self
._state
.timestamp
< deadline
:
1103 def write_vcd(self
, vcd_file
, gtkw_file
=None, *, traces
=()):
1104 """Write waveforms to a Value Change Dump file, optionally populating a GTKWave save file.
1106 This method returns a context manager. It can be used as: ::
1108 sim = Simulator(frag)
1110 with sim.write_vcd("dump.vcd", "dump.gtkw"):
1115 vcd_file : str or file-like object
1116 Verilog Value Change Dump file or filename.
1117 gtkw_file : str or file-like object
1118 GTKWave save file or filename.
1119 traces : iterable of Signal
1120 Signals to display traces for.
1122 waveform_writer
= _VCDWaveformWriter(self
._signal
_names
,
1123 vcd_file
=vcd_file
, gtkw_file
=gtkw_file
, traces
=traces
)
1124 return _WaveformContextManager(self
._state
, waveform_writer
)
1126 # TODO(nmigen-0.3): remove
1127 @deprecated("instead of `with Simulator(fragment, ...) as sim:`, use "
1128 "`sim = Simulator(fragment); with sim.write_vcd(...):`")
1129 def __enter__(self
): # :nocov:
1132 # TODO(nmigen-0.3): remove
1133 def __exit__(self
, *args
): # :nocov:
1134 self
._state
.finish_waveform()