mirror of
https://github.com/Stone-Red-Code/StoneRed.LogicSimulator.git
synced 2026-09-04 09:06:29 +02:00
192 lines
6.4 KiB
C#
192 lines
6.4 KiB
C#
using System.Linq.Expressions;
|
|
|
|
namespace StoneRed.LogicSimulator.Simulation;
|
|
|
|
/// <summary>
|
|
/// A synchronous cycle-based circuit simulator that evaluates all gates every cycle.
|
|
/// Provides deterministic timing and predictable evaluation order (gate 0 to N).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This simulator evaluates all gates synchronously each step, making it simpler and more
|
|
/// predictable than <see cref="EventCircuitSimulator"/>. It's ideal for synchronous digital
|
|
/// designs where deterministic timing is important. Less efficient for sparse circuits
|
|
/// but has uniform performance characteristics.
|
|
/// </remarks>
|
|
public sealed class CycleCircuitSimulator : SimulatorBase
|
|
{
|
|
private int[] nextInputMasks = [];
|
|
private Action<int[], int[], int[]> computeOutputs = (_, _, _) => { };
|
|
private int[] previousOutputMasks = [];
|
|
private int[] previousInputMasks = [];
|
|
|
|
/// <summary>
|
|
/// Ensures internal storage arrays are properly sized for the current gate count.
|
|
/// </summary>
|
|
protected override void EnsureStorage()
|
|
{
|
|
base.EnsureStorage();
|
|
if (nextInputMasks.Length != gateKinds.Count)
|
|
{
|
|
nextInputMasks = new int[gateKinds.Count];
|
|
previousOutputMasks = new int[gateKinds.Count];
|
|
previousInputMasks = new int[gateKinds.Count];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the circuit to initial state, clearing all input and output buffers.
|
|
/// </summary>
|
|
public override void Reset()
|
|
{
|
|
base.Reset();
|
|
Array.Clear(nextInputMasks);
|
|
Array.Clear(previousOutputMasks);
|
|
Array.Clear(previousInputMasks);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes one simulation cycle by evaluating all gates synchronously,
|
|
/// then propagating outputs to inputs for the next cycle.
|
|
/// </summary>
|
|
public override void Step()
|
|
{
|
|
EnsureCompiled();
|
|
if (!initialized)
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
(outputMasks, previousOutputMasks) = (previousOutputMasks, outputMasks);
|
|
computeOutputs(inputMasks, outputMasks, sourceStates);
|
|
if (hasAnyWatchers)
|
|
{
|
|
Array.Copy(inputMasks, previousInputMasks, inputMasks.Length);
|
|
}
|
|
PropagateAndSwap();
|
|
if (hasAnyWatchers)
|
|
{
|
|
NotifyAllWatchers(previousInputMasks);
|
|
}
|
|
}
|
|
|
|
private void PropagateAndSwap()
|
|
{
|
|
Array.Clear(nextInputMasks);
|
|
for (int fromGate = 0; fromGate < gateKinds.Count; fromGate++)
|
|
{
|
|
if ((outputMasks[fromGate] & 1) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int start = edgeStart[fromGate];
|
|
int end = edgeStart[fromGate + 1];
|
|
for (int e = start; e < end; e++)
|
|
{
|
|
nextInputMasks[edgeToGate[e]] |= 1 << edgeToInputBit[e];
|
|
}
|
|
}
|
|
(inputMasks, nextInputMasks) = (nextInputMasks, inputMasks);
|
|
}
|
|
|
|
private bool PropagateAndSwapDetectChange()
|
|
{
|
|
Array.Clear(nextInputMasks);
|
|
for (int fromGate = 0; fromGate < gateKinds.Count; fromGate++)
|
|
{
|
|
if ((outputMasks[fromGate] & 1) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int start = edgeStart[fromGate];
|
|
int end = edgeStart[fromGate + 1];
|
|
for (int e = start; e < end; e++)
|
|
{
|
|
nextInputMasks[edgeToGate[e]] |= 1 << edgeToInputBit[e];
|
|
}
|
|
}
|
|
|
|
bool changed = false;
|
|
for (int i = 0; i < inputMasks.Length; i++)
|
|
{
|
|
if (inputMasks[i] != nextInputMasks[i]) { changed = true; break; }
|
|
}
|
|
|
|
(inputMasks, nextInputMasks) = (nextInputMasks, inputMasks);
|
|
return changed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the simulation until inputs stabilize (no changes between cycles) or maxSteps is reached.
|
|
/// </summary>
|
|
/// <param name="maxSteps">Maximum number of cycles to execute.</param>
|
|
/// <param name="steps">Output parameter containing the number of cycles executed.</param>
|
|
/// <returns>True if the circuit stabilized; false if maxSteps was exceeded.</returns>
|
|
public override bool TryRunUntilStable(int maxSteps, out int steps)
|
|
{
|
|
steps = 0;
|
|
if (maxSteps <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
EnsureCompiled();
|
|
if (!initialized)
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
bool changed = true;
|
|
while (changed && steps < maxSteps)
|
|
{
|
|
steps++;
|
|
(outputMasks, previousOutputMasks) = (previousOutputMasks, outputMasks);
|
|
computeOutputs(inputMasks, outputMasks, sourceStates);
|
|
if (hasAnyWatchers)
|
|
{
|
|
Array.Copy(inputMasks, previousInputMasks, inputMasks.Length);
|
|
}
|
|
changed = PropagateAndSwapDetectChange();
|
|
if (hasAnyWatchers)
|
|
{
|
|
NotifyAllWatchers(previousInputMasks);
|
|
}
|
|
}
|
|
return !changed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compiles a single evaluator that computes outputs for all gates in one call.
|
|
/// All gate logic is combined into a single compiled lambda expression.
|
|
/// </summary>
|
|
protected override void CompileEngine()
|
|
{
|
|
ParameterExpression inputsParam = Expression.Parameter(typeof(int[]), "inputs");
|
|
ParameterExpression outputsParam = Expression.Parameter(typeof(int[]), "outputs");
|
|
ParameterExpression sourcesParam = Expression.Parameter(typeof(int[]), "sources");
|
|
ConstantExpression lutDataConst = Expression.Constant(lutData);
|
|
|
|
List<Expression> body = [];
|
|
for (int i = 0; i < gateKinds.Count; i++)
|
|
{
|
|
ConstantExpression indexExpr = Expression.Constant(i);
|
|
BinaryExpression inMask = Expression.ArrayIndex(inputsParam, indexExpr);
|
|
Expression logicExpr = GenerateGateLogic(i, inMask, sourcesParam, indexExpr, lutDataConst);
|
|
|
|
body.Add(Expression.Assign(Expression.ArrayAccess(outputsParam, indexExpr), logicExpr));
|
|
}
|
|
|
|
computeOutputs = Expression.Lambda<Action<int[], int[], int[]>>(Expression.Block(body), inputsParam, outputsParam, sourcesParam).Compile();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new instance of CycleCircuitSimulator for internal use (e.g., LUT computation).
|
|
/// </summary>
|
|
/// <returns>A new CycleCircuitSimulator instance.</returns>
|
|
protected override SimulatorBase CreateInternalSimulator()
|
|
{
|
|
return new CycleCircuitSimulator();
|
|
}
|
|
}
|