This commit includes additional non-documentation changes, related to
issues found while documenting it:
- `Simulator.run_until()` no longer accepts a `run_passive=` argument.
Passive no longer exist and in any case defaulting to `False` does not
make a lot of sense from an API perspective.
- `add_clock()`'s `phase=` argument, when specified, no longer has
`period/2` added to it. This wasn't the documented behavior in first
place and it makes no sense to do that.
- `add_clock()` raises a `NameError` if a clock domain does not exist,
instead of `ValueError`.
- `add_clock()` raises a `DriverConflict` if a clock domain is already
being driven by a clock, instead of `ValueError`.
- GTKWave is no longer a part of the installation instructions, and both
Surfer and GTKWave are recommended (in this order).
This feature does not exactly follow the RFC because the RFC as written
is not implementable; the treatment of async resets in `tick()` triggers
had to be changed. In addition, iterating a trigger was made to watch
for missed events, in case the body of the `async for` awaited for too
long.
Co-authored-by: Wanda <wanda-phi@users.noreply.github.com>
This also changes `decoder` a bit: when an enum is used as a decoder,
it is converted to a `Format.Enum` instead. The original enum is still
stored on the `decoder` attribute, so that it can be propagated
on `Signal.like`.
* Given a private name `$\d+` in RTLIL (as they are not named in the IR)
* Not automatically added to VCD files (as they are not named in the IR)
* Cannot be traced to a VCD (as they have no name to put in the file)
* Cannot be used with an unnamed top-level port (as there is no name)
If delta cycles are expanded (i.e. if the `fs_per_delta` argument to
`Simulator.write_vcd` is not zero), then create a string typed variable
for each testbench in the simulation, which reflects the current
command being executed by that testbench. To make all commands visible,
insert a (visual) delta cycle after each executed command, and ensure
that there is a change/crossing point in the waveform display each time
a command is executed, even if several identical ones in a row.
If delta cycles are not expanded, the behavior is unchanged.
This commit adds an option `fs_per_delta=` to `Simulator.write_vcd()`.
Specifying a positive integer value for it causes the simulator to
offset value change times by that many femtoseconds for each delta
cycle after the last timeline advancement.
This option is only suitable for debugging. If the timeline is advanced
by less than the combined duration of expanded delta cycles, an error
similar to the following will be raised:
vcd.writer.VCDPhaseError: Out of order timestamp: 62490
Typically `fs_per_delta=1` is best, since it allows thousands of delta
cycles to be expanded without risking a VCD phase error, but bigger
values can be used for an exaggerated visual effect.
Also, the VCD writer is changed to use 1 fs as the timebase instead of
1 ps. This change is largely invisible to designers, resulting only in
slightly larger VCD files due to longer timestamps.
Since the `fs_per_delta=` option is per VCD writer, it is possible to
simultaneously dump two VCDs, one with and one without delta cycle
expansion:
with sim.write_vcd("sim.vcd"), sim.write_vcd("sim.d.vcd", fs_per_delta=1):
sim.run()
Before this commit, testbenches (generators added with `add_testbench`)
were not only preemptible after any `yield`, but were *guaranteed* to
be preempted by another testbench after *every* yield. This is evil:
if you have any race condition between testbenches, which is common,
this scheduling strategy will maximize the resulting nondeterminism by
interleaving your testbench with every other one as much as possible.
This behavior is an outcome of the way `add_testbench` is implemented,
which is by yielding `Settle()` after every command.
One can observe that:
- `yield value_like` should never preempt;
- `yield assignable.eq()` in `add_process()` should not preempt, since
it only sets a `next` signal state, or appends to `write_queue` of
a memory state, and never wakes up processes;
- `yield assignable.eq()` in `add_testbench()` should only preempt if
changing `assignable` wakes up an RTL process. (It could potentially
also preempt if that wakes up another testbench, but this has no
benefit and requires `sim.set()` from RFC 36 to be awaitable, which
is not desirable.)
After this commit, `PySimEngine._step()` is implemented with two nested
loops instead of one. The outer loop iterates through every testbench
and runs it until an explicit wait point (`Settle()`, `Delay()`, or
`Tick()`), terminating when no testbenches are runnable. The inner loop
is the usual eval/commit loop, running whenever a testbench changes
design state.
`PySimEngine._processes` is a `set`, which doesn't have a deterministic
iteration order. This does not matter for processes, where determinism
is guaranteed by the eval/commit loop, but causes racy testbenches to
pass or fail nondeterministically (in practice depending on the memory
layout of the Python process). While it is best to not have races in
the testbenches, this commit makes `PySimEngine._testbenches` a `list`,
making the outcome of a race deterministic, and enabling a hacky work-
around to make them work: reordering calls to `add_testbench()`.
A potential future improvement is a simulation mode that, instead,
randomizes the scheduling of testbenches, exposing race conditions
early.
This change completes commit 9dc0617e and makes all the tests pass.
It corresponds with the ongoing langauge reference documentation effort.
Fixes#781.
The design decision of using split memory ports in the internal
representation (copied from Yosys) was misguided and caused no end
of misery. Remove any uses of `$memrd`/`$memwr` and lower memories
directly to a combined memory cell, currently the RTLIL one.
Amaranth bitwise negation `~` compiles to Python bitwise negation `~` in
simulation; the same holds for comparison operators such as `==`. Thus
an expression such as `~(a == b)` in simulation will compile to Python
that takes the bitwise negation of the comparison result, which will be
an actual bool.
On 3.12, the result is a `DeprecationWarning` emitted only at simulation
run-time.
When negating in simulation, coerce the value to an int. `mask` is
sufficient as we do no further arithmetic here.
These operators are ignored when they are encountered on LHS, as
the signedness of the assignment target does not matter in Amaranth.
.as_signed() appears on LHS of assigns to signed aggregate fields.