mirror of
https://github.com/Stone-Red-Code/StoneRed.LogicSimulator.git
synced 2026-09-04 00:56:23 +02:00
195 lines
6.2 KiB
C#
195 lines
6.2 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
using StoneRed.LogicSimulator.Simulation;
|
|
|
|
namespace StoneRed.LogicSimulator.Tests;
|
|
|
|
[TestClass]
|
|
public class CycleSimulatorTests : SimulatorTestsBase
|
|
{
|
|
protected override ICircuitSimulator CreateSimulator()
|
|
{
|
|
return new CycleCircuitSimulator();
|
|
}
|
|
}
|
|
|
|
[TestClass]
|
|
public class EventSimulatorTests : SimulatorTestsBase
|
|
{
|
|
protected override ICircuitSimulator CreateSimulator()
|
|
{
|
|
return new EventCircuitSimulator();
|
|
}
|
|
}
|
|
|
|
public abstract class SimulatorTestsBase
|
|
{
|
|
protected abstract ICircuitSimulator CreateSimulator();
|
|
|
|
[TestMethod]
|
|
public void TestAndGate()
|
|
{
|
|
ICircuitSimulator sim = CreateSimulator();
|
|
int s1 = sim.AddGate(GateKind.Source);
|
|
int s2 = sim.AddGate(GateKind.Source);
|
|
int and = sim.AddGate(GateKind.And2);
|
|
int sink = sim.AddGate(GateKind.Sink);
|
|
sim.ConnectGates(s1, and, 0);
|
|
sim.ConnectGates(s2, and, 1);
|
|
sim.ConnectGates(and, sink, 0);
|
|
|
|
void Check(bool i1, bool i2, bool expected)
|
|
{
|
|
sim.SetSource(s1, i1);
|
|
sim.SetSource(s2, i2);
|
|
_ = sim.RunUntilStable();
|
|
Assert.AreEqual(expected, sim.GetOutput(sink), $"AND2 failed for {i1} & {i2}");
|
|
}
|
|
|
|
Check(false, false, false);
|
|
Check(false, true, false);
|
|
Check(true, false, false);
|
|
Check(true, true, true);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestOrGate()
|
|
{
|
|
ICircuitSimulator sim = CreateSimulator();
|
|
int s1 = sim.AddGate(GateKind.Source);
|
|
int s2 = sim.AddGate(GateKind.Source);
|
|
int or = sim.AddGate(GateKind.Or2);
|
|
int sink = sim.AddGate(GateKind.Sink);
|
|
sim.ConnectGates(s1, or, 0);
|
|
sim.ConnectGates(s2, or, 1);
|
|
sim.ConnectGates(or, sink, 0);
|
|
|
|
void Check(bool i1, bool i2, bool expected)
|
|
{
|
|
sim.SetSource(s1, i1);
|
|
sim.SetSource(s2, i2);
|
|
_ = sim.RunUntilStable();
|
|
Assert.AreEqual(expected, sim.GetOutput(sink), $"OR2 failed for {i1} | {i2}");
|
|
}
|
|
|
|
Check(false, false, false);
|
|
Check(false, true, true);
|
|
Check(true, false, true);
|
|
Check(true, true, true);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestNotGate()
|
|
{
|
|
ICircuitSimulator sim = CreateSimulator();
|
|
int s1 = sim.AddGate(GateKind.Source);
|
|
int not = sim.AddGate(GateKind.Not);
|
|
int sink = sim.AddGate(GateKind.Sink);
|
|
sim.ConnectGates(s1, not, 0);
|
|
sim.ConnectGates(not, sink, 0);
|
|
|
|
sim.SetSource(s1, false);
|
|
_ = sim.RunUntilStable();
|
|
Assert.IsTrue(sim.GetOutput(sink), "NOT(0) should be 1");
|
|
|
|
sim.SetSource(s1, true);
|
|
_ = sim.RunUntilStable();
|
|
Assert.IsFalse(sim.GetOutput(sink), "NOT(1) should be 0");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestLutGate()
|
|
{
|
|
ICircuitSimulator sim = CreateSimulator();
|
|
int s1 = sim.AddGate(GateKind.Source);
|
|
int s2 = sim.AddGate(GateKind.Source);
|
|
int s3 = sim.AddGate(GateKind.Source);
|
|
|
|
// Majority function (2 or more high)
|
|
int[] table = { 0, 0, 0, 1, 0, 1, 1, 1 };
|
|
int lut = sim.AddLutGate(3, table);
|
|
int sink = sim.AddGate(GateKind.Sink);
|
|
|
|
sim.ConnectGates(s1, lut, 0);
|
|
sim.ConnectGates(s2, lut, 1);
|
|
sim.ConnectGates(s3, lut, 2);
|
|
sim.ConnectGates(lut, sink, 0);
|
|
|
|
_ = sim.RunUntilStable();
|
|
|
|
sim.SetSource(s1, true); sim.SetSource(s2, true); sim.SetSource(s3, false);
|
|
_ = sim.RunUntilStable();
|
|
Assert.IsTrue(sim.GetOutput(sink), "Majority(1,1,0) should be 1");
|
|
|
|
sim.SetSource(s1, false); sim.SetSource(s2, true); sim.SetSource(s3, false);
|
|
_ = sim.RunUntilStable();
|
|
Assert.IsFalse(sim.GetOutput(sink), "Majority(0,1,0) should be 0");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestMacroCorrectness()
|
|
{
|
|
ICircuitSimulator sim = CreateSimulator();
|
|
CircuitDefinition def = new CircuitDefinition();
|
|
int inPin = def.AddInputPin();
|
|
int not = def.AddGate(GateKind.Not);
|
|
int outPin = def.AddOutputPin();
|
|
def.Connect(inPin, not, 0);
|
|
def.Connect(not, outPin, 0);
|
|
|
|
sim.RegisterMacroGate("NOT", def);
|
|
MacroInstance inst = sim.AddMacroGate("NOT");
|
|
|
|
int src = sim.AddGate(GateKind.Source);
|
|
int sink = sim.AddGate(GateKind.Sink);
|
|
sim.ConnectGates(src, inst.Inputs[0], 0);
|
|
sim.ConnectGates(inst.Outputs[0], sink, 0);
|
|
|
|
sim.SetSource(src, true);
|
|
_ = sim.RunUntilStable();
|
|
Assert.IsFalse(sim.GetOutput(sink), "Macro NOT(1) should be 0");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestWatcherNotification()
|
|
{
|
|
ICircuitSimulator sim = CreateSimulator();
|
|
int source = sim.AddGate(GateKind.Source);
|
|
int not = sim.AddGate(GateKind.Not);
|
|
sim.ConnectGates(source, not, 0);
|
|
|
|
int watcher1Count = 0;
|
|
int watcher2Count = 0;
|
|
|
|
IDisposable sub1 = sim.WatchGate(not, (id, val) => watcher1Count++);
|
|
IDisposable sub2 = sim.WatchGate(not, (id, val) => watcher2Count++);
|
|
|
|
// Initial state
|
|
sim.SetSource(source, false);
|
|
_ = sim.RunUntilStable();
|
|
Assert.AreEqual(0, watcher1Count, "Watcher 1 should not fire when input mask does not change");
|
|
Assert.AreEqual(0, watcher2Count, "Watcher 2 should not fire when input mask does not change");
|
|
|
|
// No change
|
|
sim.SetSource(source, false);
|
|
_ = sim.RunUntilStable();
|
|
Assert.AreEqual(0, watcher1Count, "Watcher 1 should not fire if no change");
|
|
Assert.AreEqual(0, watcher2Count, "Watcher 2 should not fire if no change");
|
|
|
|
// Change
|
|
sim.SetSource(source, true);
|
|
_ = sim.RunUntilStable();
|
|
Assert.AreEqual(1, watcher1Count, "Watcher 1 should fire on input change");
|
|
Assert.AreEqual(1, watcher2Count, "Watcher 2 should fire on input change");
|
|
|
|
// Dispose one watcher
|
|
sub2.Dispose();
|
|
|
|
// Change again
|
|
sim.SetSource(source, false);
|
|
_ = sim.RunUntilStable();
|
|
Assert.AreEqual(2, watcher1Count, "Watcher 1 should fire after sub2 is disposed");
|
|
Assert.AreEqual(1, watcher2Count, "Watcher 2 should NOT fire after being disposed");
|
|
}
|
|
}
|