Skip to content

Add cycle-accurate simulation of sequential networks - #708

Open
marcelwa wants to merge 2 commits into
lsils:masterfrom
marcelwa:sequential-simulation
Open

Add cycle-accurate simulation of sequential networks#708
marcelwa wants to merge 2 commits into
lsils:masterfrom
marcelwa:sequential-simulation

Conversation

@marcelwa

@marcelwa marcelwa commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

simulate runs a network's combinational logic exactly once and has no notion of a register, so simulating a sequential<Ntk> leaves every register output holding whatever node_map default-constructed it to. simulate_sequential runs the network over a number of clock cycles instead, seeding each register from its reset value, evaluating the combinational logic once per cycle, and latching the register inputs into the register outputs for the next cycle; a simulator can optionally provide compute_pi( index, cycle ) to vary the primary inputs per cycle, and simulate_sequential_params::undefined_reset_value fixes a concrete value for registers with no defined reset. It lives in its own header, simulation_sequential.hpp, because it needs networks/sequential.hpp, which pulls in every network implementation.

`simulate` evaluates the combinational logic of a network exactly once and has
no notion of a register. Handed a `sequential<Ntk>` it assigns the constants,
the primary inputs and the gates -- but never the register outputs, which are
neither. They keep whatever `node_map` default-constructed them to, so every
value in their fanout cone is meaningless: constant 0 for a `bool` simulation,
a zero-variable truth table otherwise. Nothing warns about it.

`simulate_sequential` runs the network over a number of clock cycles instead.
Every register starts at its reset value, the combinational logic is evaluated
once per cycle, the primary outputs are recorded, and the register inputs are
latched into the register outputs for the next cycle.

The simulator concept is unchanged, with one addition: a simulator that provides
`compute_pi( index, cycle )` is driven per cycle, so the primary inputs can
change from one to the next. `stimulus_simulator` is such a simulator, holding
one assignment vector per cycle and repeating its last one for the rest of the
run. Everything that works with `simulate` keeps working, holding its assignment
for the whole run -- which is what a design with no primary inputs, an LFSR say,
wants anyway.

A register may declare no reset value at all -- `register_init::dont_care` or
`register_init::unknown`, which is what `register_t` defaults to and what an
AIGER latch with a nondeterministic reset reads back as. Simulation needs a
concrete one, so `simulate_sequential_params::undefined_reset_value` says which.

It lives in its own header rather than in `simulation.hpp` because it needs
`networks/sequential.hpp`, which pulls in every network implementation. Putting
it in `simulation.hpp` would put that cost on every translation unit that
simulates anything.

The result of a run is a `simulate_sequential_result`, which names both axes of
the data instead of returning a bare nested vector: `outputs[cycle][index]` is
what primary output `index` emitted in that cycle, `states[cycle][index]` what
register `index` held while that cycle was evaluated. The state trace is one
entry longer than the output trace, because simulating `n` cycles crosses `n + 1`
state boundaries; `reset_state()` is the one the run started from and
`final_state()` the one it ended in.

Ten test cases: a 4-bit LFSR walking all 15 of its non-zero states and returning
to its seed on the sixteenth, a second seed producing the same sequence one step
ahead (which only holds if the reset values are honoured), a shift register
driven by a per-cycle stimulus, a stimulus shorter than the run, an undefined
reset following the parameter both ways, truth-table simulation, a zero-cycle
run, the fencepost between the output and state traces, and a register read out
one cycle after it was written, where the output of each cycle must equal the
state it started in.
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.08%. Comparing base (0886ebf) to head (84341a5).

Files with missing lines Patch % Lines
...de/mockturtle/algorithms/simulation_sequential.hpp 96.42% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #708      +/-   ##
==========================================
+ Coverage   84.06%   84.08%   +0.02%     
==========================================
  Files         190      191       +1     
  Lines       29515    29571      +56     
==========================================
+ Hits        24812    24866      +54     
- Misses       4703     4705       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant