FSimX has one user-facing simulation flow.
Today the shipping default is the interpreter; eligible processes execute through a compiled path where a state-hash parity gate accepts them.
The long-term default transition is gated by the fallback count on a reference full chip.
Most simulators that offer a compiled mode make it a mode: a flag the user sets, a separate flow to maintain, a result the user has to trust on faith because it is hard to compare against the interpreter run of the same design. That design pushes a hard question onto the user - is the compiled path correct for my design? - and offers no direct way to answer it. FSimX takes a different position: one user-facing flow, with the compiled path used internally where an equivalence check accepts it. Today the shipping default is the interpreter, and the compiled path handles the eligible processes for which the parity gate agrees with it; when the fallback count on a reference full chip reaches zero, the compiled path becomes the shipping default. This article explains how the engine decides which internal path to use per process, and why the decision is checkable.
The rule: only use the compiled path where it agrees with the interpreter
The governing constraint: the compiled path is used only for processes where its result agrees, checkpoint for checkpoint, with the interpreter run of the same design. Everything else falls back. If compiled and interpreted execution can diverge on a process, that process cannot silently move to the compiled path; if they agree across the parity checkpoints, the move is safe.
The equivalence mechanism is a state hash - not a mathematical proof of equivalence, but a strong differential check. At checkpoints during a run, the engine folds the signal state into a hash (FNV-1a over the checkpoint's signal-state snapshot), and the compiled and interpreted runs of the same design must produce the same hash sequence. A mismatch is a parity-gate failure and blocks the change. The parity gates guarding the compiled tier check four-way hash parity along with named-fallback non-vacuity and a reverted-marking divergence check, so a compiled process is accepted only when it agrees with the interpreter and a deliberate revert of the optimisation is shown to change the hash.
Where compiled already wins, and where it does not yet
The steady-state case already pays off. On the automotive SoC, the compiled tier compiles 8,717 of 10,674 processes and runs them at 1.6 to 2.2 times the interpreter's speed. Where processes carry enough work and compile cleanly, the compiled path is straightforwardly faster, and the parity gate lets the engine use it without asking.
The case where it does not yet win is equally on the record. On the bench_lfsr 500,000-cycle loop, turning on the compiled tier produced no speedup at all:
| bench_lfsr arm | wall (min of 3) | outcome |
|---|---|---|
| interpreter (default) | 25.27 s | PASS, checksum identical |
| compiled, cold cache | 28.20 s | no win |
| compiled, warm cache | 26.17 s | no win |
The engine reported the reason in its own words: nothing compiled (all fell back), interpreter. The clocked block in this testbench carries procedural control the compiler does not yet lower, so every process fell back and the run paid a small overhead for the attempt. That is a roughly four percent regression on this design, published next to the wins because it is the current state of the compiled tier on this workload, not a footnote.
The two levers, and the numbers on them
The gap between "wins on the SoC" and "no win on the loop" is compile coverage and per-process cost, not correctness, and both are measured levers rather than mysteries.
- Fallback elimination. The most recent engine train compiled the families that previously fell back most often: wide multiply, divide, modulo and shift, signed wide comparisons, automatic leaf-function inlining, clocked blocking writes to process locals, and memory-block array access. Each was verified bit-identical against the interpreter, including a randomized 128-bit operand sweep of roughly 500 pairs with forced zero divisors and X-propagation legs. On those families the benchmark fallback count went to zero. Every family that stops falling back is a family the compiled default can now cover.
- Per-process shell cost. The reason the newly compiled families are throughput-neutral on small processes is a fixed per-process cost that only amortizes when a process carries enough work. That cost is the named next optimization target, with profiling counters already in place to measure it. Compile time and partitioning, deciding what to compile and how to group it, are the same lever seen from the build side.
The compiled tier does not yet close the large throughput gap against Verilator (a fully compiled two-state model) on a free-running loop; that gap and its exact multiplier are covered in Blog 03. What this article claims is narrower: on eligible processes the compiled path already outruns the interpreter, the remaining distance is compile coverage and per-process overhead (both measured), and moving a process to the compiled path requires the parity gate to accept it.
The metric that gates the default transition
The threshold is the per-design fallback count on a reference full chip, tracked as a product metric. Until it reaches zero, the shipping default stays the interpreter and the compiled path handles the processes for which the parity gate accepts it. When it reaches zero on the reference, the compiled path becomes the shipping default, and the interpreter continues internally as the correctness oracle the parity gates check against.
A state-hash parity gate decides which internal execution path serves each eligible process. The two remaining engineering costs - the interpreter fallback rate and the per-process compile overhead - are the named next targets.
