-
Notifications
You must be signed in to change notification settings - Fork 25
Simulation Engine Internals
This page covers the code-level implementation of wiRedPanda's simulation engine: the four-state logic system, simulation loop phases, topological sorting, wiring data structures, and feedback loop handling.
For a conceptual overview of the simulation model, see the Simulation Guide.
All signals in wiRedPanda use four states, defined in App/Core/Enums.h:
| State | Value | Meaning | Visual Color |
|---|---|---|---|
Unknown |
-1 | Not yet resolved / floating | Gray |
Inactive |
0 | Logic LOW | Dark green (theme-dependent) |
Active |
1 | Logic HIGH | Green (theme-dependent) |
Error |
2 | Bus conflict / indeterminate | Red |
App/Core/StatusOps.h provides logic operations that respect these states with domination rules:
-
AND:
Inactivedominates —AND(0, Unknown) = 0because any false input makes the AND false. -
OR:
Activedominates —OR(1, Unknown) = 1because any true input makes the OR true. -
NOT: Inverts
Active/Inactive; passesUnknownandErrorthrough unchanged. -
XOR: Requires all inputs to be known; any
UnknownyieldsUnknown.
This matters for simulation correctness: a combinational gate can produce a determinate output even when some inputs are unconnected or unknown, which matches real-world digital logic behavior.
Elements choose which input-reading strategy to use:
-
simUpdateInputsAllowUnknown()(permissive) — Used by combinational gates. AllowsUnknown/Errorthrough so domination rules can resolve them. Only fails if a port is truly unconnected with no default. -
simUpdateInputs()(strict) — Used by sequential elements. AnyUnknownorErrorinput causes the element to propagateUnknownto all outputs and bail out. Sequential elements need complete, valid data.
The simulation (App/Simulation/Simulation.cpp) runs on a 1ms QTimer tick. Each tick executes these phases in order:
flowchart TD
timer(["QTimer (1 ms)"])
clocks["0. updateClock()<br/>Advance clock elements (real-time)"]
step1["1. updateOutputs()<br/>Propagate user inputs: buttons, switches"]
step2["2. updateLogic()<br/>Process elements in topological order:<br/>gates (zero-delay), flip-flops (edge)"]
feedback{"Feedback loop?"}
run_logic["Run updateLogic()<br/>on feedback elements"]
settled{"Settled or<br/>iter >= 10?"}
step3["3. updatePort()<br/>Propagate values via connections (out->in)"]
step4["4. Update outputs<br/>LEDs, displays, buzzers redraw"]
wait(["Wait for next tick (1 ms)"])
timer --> clocks --> step1 --> step2 --> feedback
feedback -- Yes --> run_logic --> settled
settled -- No --> run_logic
settled -- Yes --> step3
feedback -- No --> step3
step3 --> step4 --> wait
Before simulation starts, the engine builds a dependency graph from wire connections and computes a topological sort (App/Core/Priorities.h):
- Each element is assigned a priority = length of the longest path from it to any sink (output), plus one.
- Elements with higher priority are evaluated first (sources before sinks).
- This guarantees that when an element's
updateLogic()runs, all its predecessors have already been updated this cycle.
Input -> AND -> OR -> LED
1st 2nd 3rd 4th
This is done during the initialize() phase, which builds the dependency graph from the scene's connections.
During simulation initialization, physical wire connections are converted into a lightweight predecessor map:
struct SimInputConnection {
GraphicElement *sourceElement = nullptr;
int sourceOutputIndex = 0;
};Each element stores a vector of these for its inputs. During updateLogic(), reading an input is just a direct pointer dereference — no graph traversal needed per tick.
Some circuits have feedback (e.g., an SR latch where outputs feed back to inputs). The engine detects these using Tarjan's strongly connected components algorithm:
- Any element in a cycle is marked as a feedback node.
- Instead of a single-pass evaluation, the engine uses iterative settling: it runs
updateLogic()on all elements, checks if any output changed, and repeats — up to 10 iterations or until convergence. - If the circuit does not converge (e.g., a ring oscillator), a warning is emitted via the status bar.
- Simulation Guide — Conceptual overview of the simulation model
-
Element System — How elements implement
updateLogic() - Architecture Overview — Where the simulation engine fits in the overall architecture
Made with ❤️ by the wiRedPanda Community
Licensed under GPL-3.0 | © 2026 GIBIS-UNIFESP