mirror of
https://github.com/Stone-Red-Code/StoneRed.LogicSimulator.git
synced 2026-09-04 00:56:23 +02:00
Add new cycle and event based simulator for better performance and associated unit tests
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using StoneRed.LogicSimulator.Simulation;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Tests;
|
||||
|
||||
[TestClass]
|
||||
public class CycleAdvancedTests : AdvancedCircuitTestsBase
|
||||
{
|
||||
protected override ICircuitSimulator CreateSimulator()
|
||||
{
|
||||
return new CycleCircuitSimulator();
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class EventAdvancedTests : AdvancedCircuitTestsBase
|
||||
{
|
||||
protected override ICircuitSimulator CreateSimulator()
|
||||
{
|
||||
return new EventCircuitSimulator();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AdvancedCircuitTestsBase
|
||||
{
|
||||
protected abstract ICircuitSimulator CreateSimulator();
|
||||
|
||||
[TestMethod]
|
||||
public void TestSRLatch()
|
||||
{
|
||||
// SR Latch using NOR gates
|
||||
// Q = NOR(R, Q')
|
||||
// Q' = NOR(S, Q)
|
||||
ICircuitSimulator sim = CreateSimulator();
|
||||
|
||||
int s = sim.AddGate(GateKind.Source);
|
||||
int r = sim.AddGate(GateKind.Source);
|
||||
|
||||
int orQ = sim.AddGate(GateKind.Or2);
|
||||
int norQ = sim.AddGate(GateKind.Not); // Q
|
||||
|
||||
int orQNot = sim.AddGate(GateKind.Or2);
|
||||
int norQNot = sim.AddGate(GateKind.Not); // Q'
|
||||
|
||||
// Q = NOR(R, Q')
|
||||
sim.ConnectGates(r, orQ, 0);
|
||||
sim.ConnectGates(norQNot, orQ, 1);
|
||||
sim.ConnectGates(orQ, norQ, 0);
|
||||
|
||||
// Q' = NOR(S, Q)
|
||||
sim.ConnectGates(s, orQNot, 0);
|
||||
sim.ConnectGates(norQ, orQNot, 1);
|
||||
sim.ConnectGates(orQNot, norQNot, 0);
|
||||
|
||||
int qSink = sim.AddGate(GateKind.Sink);
|
||||
int qNotSink = sim.AddGate(GateKind.Sink);
|
||||
sim.ConnectGates(norQ, qSink, 0);
|
||||
sim.ConnectGates(norQNot, qNotSink, 0);
|
||||
|
||||
// Reset state (R=1, S=0) -> Q=0, Q'=1
|
||||
sim.SetSource(r, true);
|
||||
sim.SetSource(s, false);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsFalse(sim.GetOutput(qSink), "Q should be 0 after Reset (R=1)");
|
||||
Assert.IsTrue(sim.GetOutput(qNotSink), "Q' should be 1 after Reset (R=1)");
|
||||
|
||||
// Hold (R=0, S=0) -> Q=0
|
||||
sim.SetSource(r, false);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsFalse(sim.GetOutput(qSink), "Q should stay 0");
|
||||
|
||||
// Set state (R=0, S=1) -> Q=1, Q'=0
|
||||
sim.SetSource(s, true);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsTrue(sim.GetOutput(qSink), "Q should be 1 after Set (S=1)");
|
||||
Assert.IsFalse(sim.GetOutput(qNotSink), "Q' should be 0 after Set (S=1)");
|
||||
|
||||
// Hold (R=0, S=0) -> Q=1
|
||||
sim.SetSource(s, false);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsTrue(sim.GetOutput(qSink), "Q should stay 1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFullAdder()
|
||||
{
|
||||
ICircuitSimulator sim = CreateSimulator();
|
||||
|
||||
int a = sim.AddGate(GateKind.Source);
|
||||
int b = sim.AddGate(GateKind.Source);
|
||||
int cin = sim.AddGate(GateKind.Source);
|
||||
|
||||
int[] xorTable = { 0, 1, 1, 0 };
|
||||
int xor1 = sim.AddLutGate(2, xorTable);
|
||||
int xor2 = sim.AddLutGate(2, xorTable);
|
||||
|
||||
int and1 = sim.AddGate(GateKind.And2);
|
||||
int and2 = sim.AddGate(GateKind.And2);
|
||||
int or1 = sim.AddGate(GateKind.Or2);
|
||||
|
||||
sim.ConnectGates(a, xor1, 0);
|
||||
sim.ConnectGates(b, xor1, 1);
|
||||
|
||||
sim.ConnectGates(xor1, xor2, 0);
|
||||
sim.ConnectGates(cin, xor2, 1);
|
||||
|
||||
sim.ConnectGates(a, and1, 0);
|
||||
sim.ConnectGates(b, and1, 1);
|
||||
|
||||
sim.ConnectGates(xor1, and2, 0);
|
||||
sim.ConnectGates(cin, and2, 1);
|
||||
|
||||
sim.ConnectGates(and1, or1, 0);
|
||||
sim.ConnectGates(and2, or1, 1);
|
||||
|
||||
int sumSink = sim.AddGate(GateKind.Sink);
|
||||
int coutSink = sim.AddGate(GateKind.Sink);
|
||||
sim.ConnectGates(xor2, sumSink, 0);
|
||||
sim.ConnectGates(or1, coutSink, 0);
|
||||
|
||||
void Check(bool iA, bool iB, bool iC, bool expectedSum, bool expectedCout)
|
||||
{
|
||||
sim.SetSource(a, iA);
|
||||
sim.SetSource(b, iB);
|
||||
sim.SetSource(cin, iC);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.AreEqual(expectedSum, sim.GetOutput(sumSink), $"Sum failed for {iA},{iB},{iC}");
|
||||
Assert.AreEqual(expectedCout, sim.GetOutput(coutSink), $"Cout failed for {iA},{iB},{iC}");
|
||||
}
|
||||
|
||||
Check(false, false, false, false, false);
|
||||
Check(true, false, false, true, false);
|
||||
Check(false, true, false, true, false);
|
||||
Check(true, true, false, false, true);
|
||||
Check(false, false, true, true, false);
|
||||
Check(true, false, true, false, true);
|
||||
Check(false, true, true, false, true);
|
||||
Check(true, true, true, true, true);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestDLatch()
|
||||
{
|
||||
ICircuitSimulator sim = CreateSimulator();
|
||||
|
||||
int dSource = sim.AddGate(GateKind.Source);
|
||||
int enSource = sim.AddGate(GateKind.Source);
|
||||
|
||||
int notD = sim.AddGate(GateKind.Not);
|
||||
sim.ConnectGates(dSource, notD, 0);
|
||||
|
||||
int sAnd = sim.AddGate(GateKind.And2);
|
||||
sim.ConnectGates(dSource, sAnd, 0);
|
||||
sim.ConnectGates(enSource, sAnd, 1);
|
||||
|
||||
int rAnd = sim.AddGate(GateKind.And2);
|
||||
sim.ConnectGates(notD, rAnd, 0);
|
||||
sim.ConnectGates(enSource, rAnd, 1);
|
||||
|
||||
int orQ = sim.AddGate(GateKind.Or2);
|
||||
int norQ = sim.AddGate(GateKind.Not);
|
||||
|
||||
int orQNot = sim.AddGate(GateKind.Or2);
|
||||
int norQNot = sim.AddGate(GateKind.Not);
|
||||
|
||||
sim.ConnectGates(rAnd, orQ, 0);
|
||||
sim.ConnectGates(norQNot, orQ, 1);
|
||||
sim.ConnectGates(orQ, norQ, 0);
|
||||
|
||||
sim.ConnectGates(sAnd, orQNot, 0);
|
||||
sim.ConnectGates(norQ, orQNot, 1);
|
||||
sim.ConnectGates(orQNot, norQNot, 0);
|
||||
|
||||
int qSink = sim.AddGate(GateKind.Sink);
|
||||
sim.ConnectGates(norQ, qSink, 0);
|
||||
|
||||
// 1. Transparent mode (EN=1) -> Q follows D
|
||||
sim.SetSource(enSource, true);
|
||||
sim.SetSource(dSource, true);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsTrue(sim.GetOutput(qSink), "Q should be 1 when D=1, EN=1");
|
||||
|
||||
sim.SetSource(dSource, false);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsFalse(sim.GetOutput(qSink), "Q should be 0 when D=0, EN=1");
|
||||
|
||||
// 2. Latch mode (EN=0) -> Q stays same
|
||||
sim.SetSource(dSource, true);
|
||||
sim.SetSource(enSource, true);
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsTrue(sim.GetOutput(qSink));
|
||||
|
||||
sim.SetSource(enSource, false); // LATCH
|
||||
_ = sim.RunUntilStable(10000);
|
||||
|
||||
sim.SetSource(dSource, false); // Change D while latched
|
||||
_ = sim.RunUntilStable(10000);
|
||||
Assert.IsTrue(sim.GetOutput(qSink), "Q should stay 1 even if D changes while EN=0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
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}");
|
||||
}
|
||||
|
||||
sim.Reset();
|
||||
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}");
|
||||
}
|
||||
|
||||
sim.Reset();
|
||||
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.Reset();
|
||||
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.Reset();
|
||||
_ = 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.Reset();
|
||||
sim.SetSource(src, true);
|
||||
_ = sim.RunUntilStable();
|
||||
Assert.IsFalse(sim.GetOutput(sink), "Macro NOT(1) should be 0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StoneRed.LogicSimulator.Simulation\StoneRed.LogicSimulator.Simulation.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user