using System.Linq.Expressions; namespace StoneRed.LogicSimulator.Simulation; /// /// An event-driven circuit simulator that uses queue-based change propagation. /// Only gates with changed inputs are evaluated, making it efficient for circuits with localized activity. /// /// /// This simulator uses a dirty-marking propagation queue. When a gate's output changes, /// only the gates connected to it are queued for re-evaluation. This is more efficient /// than for sparse circuits where most gates remain stable. /// public sealed class EventCircuitSimulator : SimulatorBase { private Action[] gateEvaluators = []; private readonly Queue activeQueue = new(); private bool[] inQueue = []; /// /// Called when a gate is added to ensure internal arrays are properly sized. /// /// The ID of the newly added gate. protected override void OnGateAdded(int gateId) { if (inQueue.Length != gateKinds.Count) { Array.Resize(ref inQueue, gateKinds.Count); } } /// /// Resets the circuit to initial state and queues all gates for initial evaluation. /// public override void Reset() { base.Reset(); activeQueue.Clear(); Array.Clear(inQueue); for (int i = 0; i < gateKinds.Count; i++) { Enqueue(i); } } /// /// Called when a source gate's value changes. Queues the source for propagation. /// /// The ID of the source gate that changed. protected override void OnSourceChanged(int gateId) { Enqueue(gateId); } private void Enqueue(int gateId) { if (!inQueue[gateId]) { inQueue[gateId] = true; activeQueue.Enqueue(gateId); } } /// /// Executes one simulation step by processing all pending changes in the queue. /// Continues until the queue is empty (all changes have propagated). /// public override void Step() { EnsureCompiled(); if (!initialized) { Reset(); } while (activeQueue.Count > 0) { int gateId = activeQueue.Dequeue(); inQueue[gateId] = false; int oldOutput = outputMasks[gateId]; gateEvaluators[gateId](inputMasks, outputMasks, sourceStates, gateId); if (outputMasks[gateId] != oldOutput) { Propagate(gateId); } } } private void Propagate(int fromGate) { int start = edgeStart[fromGate]; int end = edgeStart[fromGate + 1]; int outVal = outputMasks[fromGate] & 1; for (int e = start; e < end; e++) { int toGate = edgeToGate[e]; int bit = edgeToInputBit[e]; int oldBit = (inputMasks[toGate] >> bit) & 1; if (oldBit != outVal) { inputMasks[toGate] ^= 1 << bit; if (hasAnyWatchers) { NotifyGateWatchers(toGate); } Enqueue(toGate); } } } /// /// Runs the simulation until the queue is empty (circuit is stable) or maxSteps is reached. /// /// Maximum number of steps to execute. /// Output parameter containing the number of steps executed. /// True if the circuit stabilized (queue is empty); false if maxSteps was exceeded. public override bool TryRunUntilStable(int maxSteps, out int steps) { steps = 0; if (maxSteps <= 0) { return false; } EnsureCompiled(); if (!initialized) { Reset(); } while (activeQueue.Count > 0 && steps < maxSteps) { steps++; Step(); } return activeQueue.Count == 0; } /// /// Compiles per-gate evaluators as separate compiled lambda expressions. /// Each gate has its own evaluator function for efficient event-driven execution. /// protected override void CompileEngine() { gateEvaluators = new Action[gateKinds.Count]; ParameterExpression inputsParam = Expression.Parameter(typeof(int[]), "inputs"); ParameterExpression outputsParam = Expression.Parameter(typeof(int[]), "outputs"); ParameterExpression sourcesParam = Expression.Parameter(typeof(int[]), "sources"); ParameterExpression gateIdParam = Expression.Parameter(typeof(int), "gateId"); ConstantExpression lutDataConst = Expression.Constant(lutData); for (int i = 0; i < gateKinds.Count; i++) { BinaryExpression inMask = Expression.ArrayIndex(inputsParam, gateIdParam); Expression logicExpr = GenerateGateLogic(i, inMask, sourcesParam, gateIdParam, lutDataConst); gateEvaluators[i] = Expression.Lambda>( Expression.Assign(Expression.ArrayAccess(outputsParam, gateIdParam), logicExpr), inputsParam, outputsParam, sourcesParam, gateIdParam).Compile(); } } /// /// Creates a new instance of EventCircuitSimulator for internal use (e.g., LUT computation). /// /// A new EventCircuitSimulator instance. protected override SimulatorBase CreateInternalSimulator() { return new EventCircuitSimulator(); } /// /// Ensures internal storage arrays are properly sized for the current gate count. /// protected override void EnsureStorage() { base.EnsureStorage(); if (inQueue.Length != gateKinds.Count) { Array.Resize(ref inQueue, gateKinds.Count); } } }