From: Christian Femers Date: Mon, 2 Sep 2019 07:50:42 +0000 (+0200) Subject: Improved Test experience by a lot; added first tests for Am2904, Am2910 X-Git-Url: https://mograsim.net/gitweb/?p=Mograsim.git;a=commitdiff_plain;h=6d28e5c93c9347784950ca66fb6d1b3a14461ece Improved Test experience by a lot; added first tests for Am2904, Am2910 The current version of the tests includes a failureRule that displays the state of the failed test in a plain SWT window that opens automatically. This should make debugging a lot easier. Some restructuring was done, too. --- diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/SwitchWithDisplay.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/SwitchWithDisplay.java deleted file mode 100644 index c5c3ab79..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/SwitchWithDisplay.java +++ /dev/null @@ -1,65 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import net.mograsim.logic.core.components.BitDisplay; -import net.mograsim.logic.core.components.ManualSwitch; -import net.mograsim.logic.core.types.BitVector; -import net.mograsim.logic.model.model.ViewModelModifiable; -import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay; -import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch; -import net.mograsim.logic.model.model.wires.Pin; -import net.mograsim.logic.model.model.wires.WireCrossPoint; -import net.mograsim.logic.model.util.ModellingTool; - -public class SwitchWithDisplay -{ - private final Pin pin; - private final GUIBitDisplay guiBitDisplay; - private final GUIManualSwitch guiManualSwitch; - - public SwitchWithDisplay(ViewModelModifiable model, Pin target) - { - pin = target; - guiBitDisplay = new GUIBitDisplay(model, pin.logicWidth); - guiManualSwitch = new GUIManualSwitch(model, pin.logicWidth); - - ModellingTool tool = ModellingTool.createFor(model); - WireCrossPoint crossPoint = new WireCrossPoint(model, pin.logicWidth); - tool.connect(guiBitDisplay.getInputPin(), crossPoint); - tool.connect(guiManualSwitch.getOutputPin(), crossPoint); - } - - public final BitVector getDisplayedValue() - { - return guiBitDisplay.getBitDisplay().getDisplayedValue(); - } - - public final void setState(BitVector bits) - { - guiManualSwitch.getManualSwitch().setState(bits); - } - - public final Pin getPin() - { - return pin; - } - - public final BitDisplay getBitDisplay() - { - return guiBitDisplay.getBitDisplay(); - } - - public final ManualSwitch getManualSwitch() - { - return guiManualSwitch.getManualSwitch(); - } - - final GUIBitDisplay getGuiBitDisplay() - { - return guiBitDisplay; - } - - final GUIManualSwitch getGuiManualSwitch() - { - return guiManualSwitch; - } -} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java deleted file mode 100644 index 9ac40307..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java +++ /dev/null @@ -1,257 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import static org.junit.jupiter.api.Assertions.fail; - -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Objects; -import java.util.Queue; -import java.util.Set; -import java.util.TreeSet; - -import net.mograsim.logic.core.components.BitDisplay; -import net.mograsim.logic.core.components.ManualSwitch; -import net.mograsim.logic.core.timeline.Timeline; -import net.mograsim.logic.model.am2900.TestableCircuit.Result; -import net.mograsim.logic.model.model.ViewModel; -import net.mograsim.logic.model.model.ViewModelModifiable; -import net.mograsim.logic.model.model.components.GUIComponent; -import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay; -import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch; -import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; -import net.mograsim.logic.model.model.wires.GUIWire; -import net.mograsim.logic.model.model.wires.Pin; -import net.mograsim.logic.model.modeladapter.LogicModelParameters; -import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter; -import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator; -import net.mograsim.logic.model.util.ModellingTool; - -public class TestEnvironmentHelper -{ - private final TestableCircuit testEnvInstance; - private final Class testEnvClass; - private final String modelId; - private Field componentField; - private Field timelineField; - - private GUIComponent component; - private Timeline timeline; - private ViewModelModifiable viewModel; - private ModellingTool modellingTool; - private HashMap idSwitchMap = new HashMap<>(); - private HashMap idDisplayMap = new HashMap<>(); - - private DebugState debug = DebugState.NO_DEBUG; - private Set wireDebugChangeSet; - private boolean debugWires = false; - public int debugEventThreshold = 10_000; - public int debugEventCount = 500; - private int eventCounter; - - public TestEnvironmentHelper(TestableCircuit testEnv, String modelId) - { - this.testEnvInstance = testEnv; - this.modelId = modelId; - this.testEnvClass = testEnvInstance.getClass(); - for (Field f : testEnvClass.getDeclaredFields()) - { - if (GUIComponent.class.isAssignableFrom(f.getType())) - { - componentField = f; - componentField.setAccessible(true); - } else if (Timeline.class.isAssignableFrom(f.getType())) - { - timelineField = f; - timelineField.setAccessible(true); - } - } - if (componentField == null || timelineField == null) - throw new IllegalStateException("No component or timeline field found!"); - } - - public void setup(DebugState debug) - { - this.debug = debug; - // Create view model - viewModel = new ViewModelModifiable(); - modellingTool = ModellingTool.createFor(viewModel); - component = IndirectGUIComponentCreator.createComponent(viewModel, modelId); - setField(componentField, component); - - component.getPins().values().forEach(this::extendModelPin); - - // Create logic model - LogicModelParameters params = new LogicModelParameters(); - params.gateProcessTime = 50; - params.wireTravelTime = 10; - timeline = ViewLogicModelAdapter.convert(viewModel, params); - setField(timelineField, timeline); - - // Bind switches/displays to this test class - component.getPins().values().forEach(this::bindModelPin); - - if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST) - { - setupDebugging(); - } - timeline.addEventAddedListener(te -> eventCounter++); - } - - private void extendModelPin(Pin p) - { - String javaIdentId = idToJavaIdentifier(p.name); - try - { - Field f = testEnvClass.getDeclaredField(javaIdentId); - Class type = f.getType(); - if (ManualSwitch.class.isAssignableFrom(type)) - { - GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth); - modellingTool.connect(p, gms.getOutputPin()); - idSwitchMap.put(p.name, gms); - } else if (BitDisplay.class.isAssignableFrom(type)) - { - GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth); - modellingTool.connect(p, gbd.getInputPin()); - idDisplayMap.put(p.name, gbd); - } else if (SwitchWithDisplay.class.isAssignableFrom(type)) - { - SwitchWithDisplay swd = new SwitchWithDisplay(viewModel, p); - setField(f, swd); - } else - { - fail("unkown field type " + type); - } - } - catch (NoSuchFieldException | SecurityException e) - { - fail(e); - } - } - - private void bindModelPin(Pin p) - { - String javaIdentId = idToJavaIdentifier(p.name); - if (idDisplayMap.containsKey(p.name)) - setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay()); - if (idSwitchMap.containsKey(p.name)) - setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch()); - } - - private void setupDebugging() - { - // Debug code - HashSet wiresIncludingSubmodels = new HashSet<>(); - Queue modelsToIterate = new LinkedList<>(); - modelsToIterate.add(viewModel); - while (modelsToIterate.size() > 0) - { - ViewModel model = modelsToIterate.poll(); - wiresIncludingSubmodels.addAll(model.getWiresByName().values()); - for (GUIComponent comp : model.getComponentsByName().values()) - if (comp instanceof SubmodelComponent) - modelsToIterate.offer(((SubmodelComponent) comp).submodel); - } - System.out.println(wiresIncludingSubmodels.size()); - viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w -> - { - if (debugWires) - { - wireDebugChangeSet.add(w.toString()); - } - })); - } - - public Result run() - { - // Normal execution until completion or eventLimit - int eventLimit = debugEventThreshold; - eventCounter = 0; - debugWires = false; - while (eventCounter < eventLimit) - { - timeline.executeNext(); - if (!timeline.hasNext()) - return Result.SUCCESS; - } - - // Start debugging if event limit is reached (if debug is active) - if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST) - return debugThisRun(); - - return Result.OUT_OF_TIME; - } - - private Result debugThisRun() - { - int eventLimit = debugEventThreshold; - debugWires = true; - wireDebugChangeSet = new TreeSet<>(); - Set oldChangeSet; - // observe wire changes to detect, if we are really stuck in an endless loop - do - { - eventLimit += debugEventCount; - oldChangeSet = wireDebugChangeSet; - wireDebugChangeSet = new TreeSet<>(); - while (eventCounter < eventLimit) - { - timeline.executeNext(); - if (!timeline.hasNext()) - { - // no endless loop, but more events needed than expected - System.out.println("run() took longer than expected: " + eventCounter); - return Result.SUCCESS; - } - } - } while (!oldChangeSet.equals(wireDebugChangeSet)); - // if stuck, abort execution and print wires - System.err.print("Problematic Wire updates:"); - wireDebugChangeSet.forEach(System.out::println); - System.err.println("run() failed: " + eventCounter); - return Result.OUT_OF_TIME; - } - - private static String idToJavaIdentifier(String s) - { - StringBuilder sb = new StringBuilder(s.length()); - char c = s.charAt(0); - sb.append(Character.isJavaIdentifierStart(c) ? c : '_'); - for (int i = 1; i < s.length(); i++) - sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_'); - return sb.toString(); - } - - private void setField(Field f, S value) - { - try - { - f.set(testEnvInstance, Objects.requireNonNull(value)); - } - catch (Exception e) - { - fail(e); - } - } - - private void setField(String name, S value) - { - try - { - Field f = testEnvClass.getDeclaredField(name); - f.setAccessible(true); - f.set(testEnvInstance, Objects.requireNonNull(value)); - } - catch (Exception e) - { - fail(e); - } - } - - public enum DebugState - { - NO_DEBUG, DEBUG_AT_PERFORMANCE_COST; - } -} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java deleted file mode 100644 index 46a4b92c..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java +++ /dev/null @@ -1,106 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import static org.junit.jupiter.api.Assertions.fail; - -import java.lang.reflect.Field; -import java.util.Objects; - -import net.mograsim.logic.core.types.Bit; -import net.mograsim.logic.core.types.BitVector; -import net.mograsim.logic.core.types.BitVector.BitVectorMutator; -import net.mograsim.logic.model.am2900.am2901.TestableAm2901Impl; - -public final class TestUtil -{ - private TestUtil() - { - - } - - /** - * Transforms the last four bits of an int to a string that contains the binary ('1' and '0') representation of the 4 bits - * - * @author Christian Femers - */ - public static String to4bitBin(int x) - { - StringBuilder sb = new StringBuilder(4); - sb.append((x & 0b1000) == 0 ? '0' : '1'); - sb.append((x & 0b0100) == 0 ? '0' : '1'); - sb.append((x & 0b0010) == 0 ? '0' : '1'); - sb.append((x & 0b0001) == 0 ? '0' : '1'); - return sb.toString(); - } - - /** - * Transforms the given boolean to a string that contains the binary ('1' and '0') representation of the bit - * - * @author Christian Femers - */ - public static String to1bitBin(boolean bitIsSet) - { - return bitIsSet ? "1" : "0"; - } - - /** - * Transforms the given int to a string that contains the binary ('1' and '0') representation of the int. "0" is only returned when the - * int is equal to zero. - * - * @author Christian Femers - */ - public static String to1bitBin(int someInt) - { - return someInt != 0 ? "1" : "0"; - } - - /** - * Transforms a 4 bit signed integer (-8 to 7) to a int representing the same number. (Adding leading 1-bits if the 4 bit int is - * negative) - * - * @author Christian Femers - */ - public static int signed4ToSigned32(int signed4bit) - { - if ((signed4bit & 0b1000) > 0) - return signed4bit | 0xFF_FF_FF_F0; - return signed4bit & 0x00_00_00_0F; - } - - /** - * Transforms a 16 bit signed integer (-32768 to 32767 - a short) to a int representing the same number. (Adding leading 1-bits if the - * 16 bit int is negative) - * - * @author Christian Femers - */ - public static int signed16ToSigned32(int signed16bit) - { - return (short) signed16bit; - } - - /** - * Transforms the last n bits of an int to a string that contains the binary ('1' and '0') representation of the n bits - * - * @author Christian Femers - */ - public static String toNbitString(int x, int n) - { - StringBuilder sb = new StringBuilder(n); - for (int i = 0; i < n; i++) - { - sb.append((x >> i) & 1); - } - return sb.reverse().toString(); - } - - public static BitVector of(int value, int length) - { - BitVectorMutator mutator = BitVectorMutator.ofLength(length); - int val = value; - for (int i = length - 1; i >= 0; i--) - { - mutator.setMSBit(i, Bit.lastBitOf(val)); - val >>>= 1; - } - return mutator.toBitVector(); - } -} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java index 4b1a5a6c..d36d80ff 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java @@ -2,6 +2,8 @@ package net.mograsim.logic.model.am2900; import static org.junit.jupiter.api.Assertions.assertEquals; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper; + public interface TestableCircuit { void setup(); @@ -10,6 +12,8 @@ public interface TestableCircuit void clockOn(boolean isClockOn); + TestEnvironmentHelper getTestEnvironmentHelper(); + default void assertRunSuccess() { assertEquals(Result.SUCCESS, run()); @@ -24,6 +28,11 @@ public interface TestableCircuit assertRunSuccess(); } + default void displayState() + { + getTestEnvironmentHelper().displayState(); + } + public enum Result { SUCCESS, OUT_OF_TIME, ERROR; diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java index 57461c62..0d55c750 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java @@ -1,10 +1,10 @@ package net.mograsim.logic.model.am2900.am2901; -import static net.mograsim.logic.model.am2900.TestUtil.*; import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Dest.*; import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Func.*; import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Src.*; import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register.*; +import static net.mograsim.logic.model.am2900.util.TestUtil.*; import static org.junit.jupiter.api.Assertions.*; import java.awt.Point; @@ -17,16 +17,21 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register; +import net.mograsim.logic.model.am2900.util.DisplayStateOnFailure; @DisplayName("Am2901 Tests") @TestMethodOrder(OrderAnnotation.class) public class Am2901Test { - private TestableAm2901 am2901; + private TestableAm2901 am2901 = new TestableAm2901Impl(); + + @RegisterExtension + DisplayStateOnFailure failureRule = new DisplayStateOnFailure(am2901); @BeforeEach void initialize() @@ -37,7 +42,6 @@ public class Am2901Test void createAndSetup() { - am2901 = new TestableAm2901Impl(); am2901.setup(); } diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java index ba915b30..d8899089 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java @@ -3,8 +3,8 @@ package net.mograsim.logic.model.am2900.am2901; import java.util.Arrays; import java.util.stream.Stream; -import net.mograsim.logic.model.am2900.TestUtil; import net.mograsim.logic.model.am2900.TestableCircuit; +import net.mograsim.logic.model.am2900.util.TestUtil; public interface TestableAm2901 extends TestableCircuit { diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java index 34c68c1e..22ee120d 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java @@ -2,18 +2,16 @@ package net.mograsim.logic.model.am2900.am2901; import net.mograsim.logic.core.components.BitDisplay; import net.mograsim.logic.core.components.ManualSwitch; -import net.mograsim.logic.core.timeline.Timeline; import net.mograsim.logic.core.types.Bit; import net.mograsim.logic.core.types.BitVector; -import net.mograsim.logic.model.am2900.TestEnvironmentHelper; -import net.mograsim.logic.model.am2900.TestEnvironmentHelper.DebugState; -import net.mograsim.logic.model.am2900.TestUtil; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper; +import net.mograsim.logic.model.am2900.util.TestUtil; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper.DebugState; import net.mograsim.logic.model.model.components.GUIComponent; public class TestableAm2901Impl implements TestableAm2901 { private GUIComponent am2901; - private Timeline timeline; private ManualSwitch I8, I7, I6, I5, I4, I3, I2, I1, I0; private ManualSwitch C; private ManualSwitch Cn; @@ -226,4 +224,10 @@ public class TestableAm2901Impl implements TestableAm2901 return "qreg.q"; return "regs.c" + r.toBitString() + ".q"; } + + @Override + public TestEnvironmentHelper getTestEnvironmentHelper() + { + return testHelper; + } } diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Test.java new file mode 100644 index 00000000..cc255e05 --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Test.java @@ -0,0 +1,192 @@ +package net.mograsim.logic.model.am2900.am2904; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Am2904_Carry; +import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Am2904_Inst; +import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Am2904_ShiftDir; +import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Register; +import net.mograsim.logic.model.am2900.util.DisplayStateOnFailure; + +@DisplayName("Am2904 Tests") +@TestMethodOrder(OrderAnnotation.class) +public class Am2904Test +{ + private TestableAm2904 am2904 = new TestableAm2904Impl(); + + @RegisterExtension + DisplayStateOnFailure failureRule = new DisplayStateOnFailure(am2904); + + @BeforeEach + void initialize() + { + createAndSetup(); + setStandardInputs(); + } + + void createAndSetup() + { + am2904.setup(); + } + + void setStandardInputs() + { + am2904.set_CEµ("0"); + am2904.set_CEM("0"); + am2904.setI("0000"); + am2904.set_E("0000"); + am2904.set_OEY("1"); + am2904.set_OECT("0"); + am2904.setCarry(Am2904_Carry.CI0); + am2904.setCX("0"); + am2904.setI10(Am2904_ShiftDir.RIGHT); + am2904.setShiftCode("0000"); + am2904.setInstruction(Am2904_Inst.Load_Load_I_Z); + am2904.setSIO0("Z"); + am2904.setSIO3("Z"); + am2904.setQIO0("Z"); + am2904.setQIO3("Z"); + am2904.set_SE("1"); + am2904.setY("ZZZZ"); + am2904.clockOn(true); + am2904.assertRunSuccess(); + } + + @ParameterizedTest(name = "{0}") + @Order(1) + @DisplayName("Direct / high level access") + @EnumSource(Register.class) + void testDirectAccess(Register r) + { + assertEquals("U", am2904.getDirectly(r)); + + am2904.setDirectly(r, "1"); + + assertEquals("1", am2904.getDirectly(r)); + } + + @Test + @Order(2) + void testBasicStateAndOutputs() + { + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getC0()); + assertEquals("0", am2904.getCT()); + assertEquals("Z", am2904.getQIO0()); + assertEquals("Z", am2904.getQIO3()); + assertEquals("Z", am2904.getSIO0()); + assertEquals("Z", am2904.getSIO3()); + assertEquals("ZZZZ", am2904.getY()); + } + + @Test + @Order(3) + void testSimpleLoadTestIZ() + { + am2904.setInstruction(Am2904_Inst.Load_Load_I_Z); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("1000"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + + am2904.setInstruction(Am2904_Inst.Load_Load_I_notZ); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0000"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + } + + @Test + @Order(3) + void testSimpleLoadTestIC() + { + am2904.setInstruction(Am2904_Inst.Load_Load_I_C); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0100"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + + am2904.setInstruction(Am2904_Inst.Load_Load_I_notC); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0000"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + } + + @Test + @Order(3) + void testSimpleLoadTestIN() + { + am2904.setInstruction(Am2904_Inst.Load_Load_I_N); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0010"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + + am2904.setInstruction(Am2904_Inst.Load_Load_I_notN); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0000"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + } + + @Test + @Order(3) + void testSimpleLoadTestIOVR() + { + am2904.setInstruction(Am2904_Inst.Load_Load_I_OVR); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0001"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + + am2904.setInstruction(Am2904_Inst.Load_Load_I_notOVR); + am2904.assertFullCycleSuccess(); + + assertEquals("0", am2904.getCT()); + + am2904.setI("0000"); + am2904.assertFullCycleSuccess(); + + assertEquals("1", am2904.getCT()); + } +} \ No newline at end of file diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904.java index d9c92339..db58d56a 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904.java @@ -19,7 +19,7 @@ public interface TestableAm2904 extends TestableCircuit void setCX(String val_1_bit); - void setY3(String val_1_bit); + void setY(String ovr_n_c_z); void setIZ(String val_1_bit); @@ -29,12 +29,12 @@ public interface TestableAm2904 extends TestableCircuit void setIN(String val_1_bit); - default void setI(String z_c_ovr_n) + default void setI(String z_c_n_ovr) { - setIZ(z_c_ovr_n.substring(0, 1)); - setIC(z_c_ovr_n.substring(1, 2)); - setIOVR(z_c_ovr_n.substring(2, 3)); - setIN(z_c_ovr_n.substring(3, 4)); + setIZ(z_c_n_ovr.substring(0, 1)); + setIC(z_c_n_ovr.substring(1, 2)); + setIN(z_c_n_ovr.substring(2, 3)); + setIOVR(z_c_n_ovr.substring(3, 4)); } void set_CEM(String val_1_bit); @@ -55,12 +55,12 @@ public interface TestableAm2904 extends TestableCircuit void set_EN(String val_1_bit); - default void set_E(String z_c_ovr_n) + default void set_E(String z_c_n_ovr) { - set_EZ(z_c_ovr_n.substring(0, 1)); - set_EC(z_c_ovr_n.substring(1, 2)); - set_EOVR(z_c_ovr_n.substring(2, 3)); - set_EN(z_c_ovr_n.substring(3, 4)); + set_EZ(z_c_n_ovr.substring(0, 1)); + set_EC(z_c_n_ovr.substring(1, 2)); + set_EN(z_c_n_ovr.substring(2, 3)); + set_EOVR(z_c_n_ovr.substring(3, 4)); } void setSIO0(String val_1_bit); @@ -71,11 +71,13 @@ public interface TestableAm2904 extends TestableCircuit void setQIO3(String val_1_bit); + void setDirectly(Register r, String val_1_bit); + String getC0(); String getCT(); - String getY3(); + String getY(); String getSIO0(); @@ -85,6 +87,8 @@ public interface TestableAm2904 extends TestableCircuit String getQIO3(); + String getDirectly(Register r); + enum Am2904_ShiftDir { RIGHT, LEFT; @@ -214,4 +218,9 @@ public interface TestableAm2904 extends TestableCircuit return (code & 0b001_110) == 0b001_000; } } + + enum Register + { + µZ, µC, µN, µOVR, MZ, MC, MN, MOVR; + } } diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904Impl.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904Impl.java index eeaf9c0c..e36ca6d4 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904Impl.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904Impl.java @@ -1,237 +1,277 @@ package net.mograsim.logic.model.am2900.am2904; +import net.mograsim.logic.core.components.BitDisplay; +import net.mograsim.logic.core.components.ManualSwitch; +import net.mograsim.logic.core.types.Bit; +import net.mograsim.logic.core.types.BitVector; +import net.mograsim.logic.model.am2900.util.SwitchWithDisplay; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper.DebugState; +import net.mograsim.logic.model.model.components.GUIComponent; + public class TestableAm2904Impl implements TestableAm2904 { + private GUIComponent am2904; + private ManualSwitch I; + private ManualSwitch C; + private ManualSwitch Cx; + private ManualSwitch IC, IN, IOVR, IZ; + private ManualSwitch _CEM, _CEmu; + private ManualSwitch _EC, _EN, _EOVR, _EZ; + private ManualSwitch _OECT, _OEY; + private ManualSwitch _SE; + private BitDisplay C0; + private BitDisplay CT; + private SwitchWithDisplay SIO0, SIOn, QIO0, QIOn; + private SwitchWithDisplay YC, YN, YOVR, YZ; + + private final TestEnvironmentHelper testHelper = new TestEnvironmentHelper(this, "file:components/am2904/GUIAm2904.json"); + @Override public void setup() { - // TODO Auto-generated method stub - + testHelper.setup(DebugState.NO_DEBUG); } @Override public Result run() { - // TODO Auto-generated method stub - return null; + return testHelper.run(); } @Override public void clockOn(boolean isClockOn) { - // TODO Auto-generated method stub - + if (isClockOn) + C.switchFullOn(); + else + C.switchFullOff(); } @Override public void setInstruction(Am2904_Inst inst) { - // TODO Auto-generated method stub - + var old = I.getValues(); + var newPart = BitVector.from(inst.ordinal(), 6); + I.setState(old.subVector(0, 7).concat(newPart)); } @Override - public void setCarry(Am2904_Carry carry) + public void setShiftCode(String val_4_bit) { - // TODO Auto-generated method stub - + var old = I.getValues(); + var newPart = BitVector.parse(val_4_bit); + I.setState(old.subVector(0, 3).concat(newPart).concat(old.subVector(7))); } @Override - public void setShiftCode(String val_4_bit) + public void setI10(Am2904_ShiftDir dir) { - // TODO Auto-generated method stub - + var old = I.getValues(); + var newPart = BitVector.from(dir.ordinal(), 1); + I.setState(old.subVector(0, 2).concat(newPart).concat(old.subVector(3))); } @Override - public void setI10(Am2904_ShiftDir dir) + public void setCarry(Am2904_Carry carry) { - // TODO Auto-generated method stub - + var old = I.getValues(); + var newPart = BitVector.from(carry.ordinal(), 2); + I.setState(newPart.concat(old.subVector(2))); } @Override public void setCX(String val_1_bit) { - // TODO Auto-generated method stub - + Cx.setState(BitVector.parse(val_1_bit)); } @Override - public void setY3(String val_1_bit) + public void setY(String ovr_n_c_z) { - // TODO Auto-generated method stub - + var bv = BitVector.parse(ovr_n_c_z); + // correct order apparently unknown :/ + YOVR.setState(bv.getLSBit(3).toVector()); + YN.setState(bv.getLSBit(2).toVector()); + YC.setState(bv.getLSBit(1).toVector()); + YZ.setState(bv.getLSBit(0).toVector()); } @Override public void setIZ(String val_1_bit) { - // TODO Auto-generated method stub - + IZ.setState(BitVector.parse(val_1_bit)); } @Override public void setIC(String val_1_bit) { - // TODO Auto-generated method stub - + IC.setState(BitVector.parse(val_1_bit)); } @Override public void setIOVR(String val_1_bit) { - // TODO Auto-generated method stub - + IOVR.setState(BitVector.parse(val_1_bit)); } @Override public void setIN(String val_1_bit) { - // TODO Auto-generated method stub - + IN.setState(BitVector.parse(val_1_bit)); } @Override public void set_CEM(String val_1_bit) { - // TODO Auto-generated method stub - + _CEM.setState(BitVector.parse(val_1_bit)); } @Override public void set_CEµ(String val_1_bit) { - // TODO Auto-generated method stub - + _CEmu.setState(BitVector.parse(val_1_bit)); } @Override public void set_OEY(String val_1_bit) { - // TODO Auto-generated method stub - + _OEY.setState(BitVector.parse(val_1_bit)); } @Override public void set_OECT(String val_1_bit) { - // TODO Auto-generated method stub - + _OECT.setState(BitVector.parse(val_1_bit)); } @Override public void set_SE(String val_1_bit) { - // TODO Auto-generated method stub - + _SE.setState(BitVector.parse(val_1_bit)); } @Override public void set_EZ(String val_1_bit) { - // TODO Auto-generated method stub - + _EZ.setState(BitVector.parse(val_1_bit)); } @Override public void set_EC(String val_1_bit) { - // TODO Auto-generated method stub - + _EC.setState(BitVector.parse(val_1_bit)); } @Override public void set_EOVR(String val_1_bit) { - // TODO Auto-generated method stub - + _EOVR.setState(BitVector.parse(val_1_bit)); } @Override public void set_EN(String val_1_bit) { - // TODO Auto-generated method stub - + _EN.setState(BitVector.parse(val_1_bit)); } @Override public void setSIO0(String val_1_bit) { - // TODO Auto-generated method stub - + SIO0.setState(BitVector.parse(val_1_bit)); } @Override public void setSIO3(String val_1_bit) { - // TODO Auto-generated method stub - + SIOn.setState(BitVector.parse(val_1_bit)); } @Override public void setQIO0(String val_1_bit) { - // TODO Auto-generated method stub - + QIO0.setState(BitVector.parse(val_1_bit)); } @Override public void setQIO3(String val_1_bit) { - // TODO Auto-generated method stub + QIOn.setState(BitVector.parse(val_1_bit)); + } + @Override + public void setDirectly(Register r, String val_1_bit) + { + var bv = (BitVector) am2904.getHighLevelState(regToStateID(r)); + bv = bv.withBitChanged(3 - r.ordinal() % 4, b -> Bit.parse(val_1_bit)); + am2904.setHighLevelState(regToStateID(r), bv); } @Override public String getC0() { - // TODO Auto-generated method stub - return null; + return C0.getDisplayedValue().toString(); } @Override public String getCT() { - // TODO Auto-generated method stub - return null; + return CT.getDisplayedValue().toString(); } @Override - public String getY3() + public String getY() { - // TODO Auto-generated method stub - return null; + // correct order apparently unknown :/ + var y3 = YOVR.getDisplayedValue(); + var y2 = YN.getDisplayedValue(); + var y1 = YC.getDisplayedValue(); + var y0 = YZ.getDisplayedValue(); + return y3.concat(y2).concat(y1).concat(y0).toString(); } @Override public String getSIO0() { - // TODO Auto-generated method stub - return null; + return SIO0.getDisplayedValue().toString(); } @Override public String getSIO3() { - // TODO Auto-generated method stub - return null; + return SIOn.getDisplayedValue().toString(); } @Override public String getQIO0() { - // TODO Auto-generated method stub - return null; + return QIO0.getDisplayedValue().toString(); } @Override public String getQIO3() { - // TODO Auto-generated method stub - return null; + return QIOn.getDisplayedValue().toString(); + } + + @Override + public String getDirectly(Register r) + { + var bv = (BitVector) am2904.getHighLevelState(regToStateID(r)); + return bv.getLSBit(r.ordinal() % 4).getSymbol(); } + private static String regToStateID(Register r) + { + if (r.ordinal() > 3) + return "msr.q"; + return "musr.q"; + } + + @Override + public TestEnvironmentHelper getTestEnvironmentHelper() + { + return testHelper; + } } diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Test.java new file mode 100644 index 00000000..18b2578d --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Test.java @@ -0,0 +1,69 @@ +package net.mograsim.logic.model.am2900.am2910; + +import static net.mograsim.logic.model.am2900.am2910.TestableAm2910.Am2910_Inst.*; +import static net.mograsim.logic.core.types.Bit.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import net.mograsim.logic.core.types.BitVector; +import net.mograsim.logic.model.am2900.am2910.TestableAm2910.Register; +import net.mograsim.logic.model.am2900.util.DisplayStateOnFailure; + +@DisplayName("Am2910 Tests") +@TestMethodOrder(OrderAnnotation.class) +public class Am2910Test +{ + private TestableAm2910 am2910 = new TestableAm2910Impl(); + + @RegisterExtension + DisplayStateOnFailure failureRule = new DisplayStateOnFailure(am2910); + + @BeforeEach + void initialize() + { + createAndSetup(); + setStandardInputs(); + } + + void createAndSetup() + { + am2910.setup(); + } + + void setStandardInputs() + { + am2910.set_CC("0"); + am2910.set_CCEN("0"); + am2910.set_OE("0"); + am2910.set_RLD("1"); + am2910.setCI("1"); + am2910.setD("000000000000"); + am2910.setInstruction(JZ); + am2910.clockOn(true); + am2910.assertRunSuccess(); + } + + @ParameterizedTest(name = "{0}") + @Order(1) + @DisplayName("Direct / high level access") + @EnumSource(Register.class) + void testDirectAccess(Register r) + { + String us = U.toVector(r.size()).toString(); + String three = BitVector.from(3, r.size()).toString(); + + assertEquals(us, am2910.getDirectly(r)); + + am2910.setDirectly(r, three); + + assertEquals(three, am2910.getDirectly(r)); + } +} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Testbench.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Testbench.java index 028de6ea..5bf827de 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Testbench.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Testbench.java @@ -7,7 +7,10 @@ import net.mograsim.logic.model.SimpleLogicUIStandalone; import net.mograsim.logic.model.SimpleLogicUIStandalone.VisualisationObjects; import net.mograsim.logic.model.model.ViewModelModifiable; import net.mograsim.logic.model.model.components.GUIComponent; +import net.mograsim.logic.model.model.components.Orientation; import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay; +import net.mograsim.logic.model.model.components.atomic.GUIClock; +import net.mograsim.logic.model.model.components.atomic.GUIClock.GUIClockParams; import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch; import net.mograsim.logic.model.model.wires.GUIWire; import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator; @@ -23,7 +26,7 @@ public class Am2910Testbench public static void create(ViewModelModifiable model) { GUIComponent am2910 = IndirectGUIComponentCreator.createComponent(model, "file:components/am2910/GUIAm2910.json", "Am2910"); - GUIManualSwitch C = new GUIManualSwitch(model, 1, "C"); + GUIClock C = new GUIClock(model, new GUIClockParams(1000, Orientation.RIGHT)); GUIManualSwitch D = new GUIManualSwitch(model, 12, "D"); GUIManualSwitch _RLD = new GUIManualSwitch(model, 1, "_RLD"); GUIManualSwitch _CC = new GUIManualSwitch(model, 1, "_CC"); diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/TestableAm2910Impl.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/TestableAm2910Impl.java index 8b2cddf4..53fd654d 100644 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/TestableAm2910Impl.java +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/TestableAm2910Impl.java @@ -2,17 +2,15 @@ package net.mograsim.logic.model.am2900.am2910; import net.mograsim.logic.core.components.BitDisplay; import net.mograsim.logic.core.components.ManualSwitch; -import net.mograsim.logic.core.timeline.Timeline; import net.mograsim.logic.core.types.BitVector; -import net.mograsim.logic.model.am2900.TestEnvironmentHelper; -import net.mograsim.logic.model.am2900.TestEnvironmentHelper.DebugState; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper; +import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper.DebugState; import net.mograsim.logic.model.model.components.GUIComponent; public class TestableAm2910Impl implements TestableAm2910 { private GUIComponent am2901; - private Timeline timeline; private ManualSwitch I; private ManualSwitch C; private ManualSwitch CI; @@ -152,4 +150,10 @@ public class TestableAm2910Impl implements TestableAm2910 throw new IllegalArgumentException("unknown: " + r); } } + + @Override + public TestEnvironmentHelper getTestEnvironmentHelper() + { + return testHelper; + } } diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/DisplayStateOnFailure.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/DisplayStateOnFailure.java new file mode 100644 index 00000000..3f68c67e --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/DisplayStateOnFailure.java @@ -0,0 +1,30 @@ +package net.mograsim.logic.model.am2900.util; + +import java.util.Objects; + +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import net.mograsim.logic.model.am2900.TestableCircuit; + +public class DisplayStateOnFailure implements AfterTestExecutionCallback +{ + public static final boolean ACTIVE = true; + + private final TestableCircuit circuitUnderTest; + + public DisplayStateOnFailure(TestableCircuit circuitUnderTest) + { + this.circuitUnderTest = Objects.requireNonNull(circuitUnderTest); + } + + @Override + public void afterTestExecution(ExtensionContext context) throws Exception + { + if (ACTIVE && context.getExecutionException().isPresent()) + { + context.getExecutionException().get().printStackTrace(); + circuitUnderTest.displayState(); + } + } +} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/SwitchWithDisplay.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/SwitchWithDisplay.java new file mode 100644 index 00000000..2591e3a5 --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/SwitchWithDisplay.java @@ -0,0 +1,65 @@ +package net.mograsim.logic.model.am2900.util; + +import net.mograsim.logic.core.components.BitDisplay; +import net.mograsim.logic.core.components.ManualSwitch; +import net.mograsim.logic.core.types.BitVector; +import net.mograsim.logic.model.model.ViewModelModifiable; +import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay; +import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch; +import net.mograsim.logic.model.model.wires.Pin; +import net.mograsim.logic.model.model.wires.WireCrossPoint; +import net.mograsim.logic.model.util.ModellingTool; + +public class SwitchWithDisplay +{ + private final Pin pin; + private final GUIBitDisplay guiBitDisplay; + private final GUIManualSwitch guiManualSwitch; + + public SwitchWithDisplay(ViewModelModifiable model, Pin target) + { + pin = target; + guiBitDisplay = new GUIBitDisplay(model, pin.logicWidth); + guiManualSwitch = new GUIManualSwitch(model, pin.logicWidth); + + ModellingTool tool = ModellingTool.createFor(model); + WireCrossPoint crossPoint = new WireCrossPoint(model, pin.logicWidth); + tool.connect(guiBitDisplay.getInputPin(), crossPoint); + tool.connect(guiManualSwitch.getOutputPin(), crossPoint); + } + + public final BitVector getDisplayedValue() + { + return guiBitDisplay.getBitDisplay().getDisplayedValue(); + } + + public final void setState(BitVector bits) + { + guiManualSwitch.getManualSwitch().setState(bits); + } + + public final Pin getPin() + { + return pin; + } + + public final BitDisplay getBitDisplay() + { + return guiBitDisplay.getBitDisplay(); + } + + public final ManualSwitch getManualSwitch() + { + return guiManualSwitch.getManualSwitch(); + } + + final GUIBitDisplay getGuiBitDisplay() + { + return guiBitDisplay; + } + + final GUIManualSwitch getGuiManualSwitch() + { + return guiManualSwitch; + } +} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestEnvironmentHelper.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestEnvironmentHelper.java new file mode 100644 index 00000000..ae4b28e6 --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestEnvironmentHelper.java @@ -0,0 +1,274 @@ +package net.mograsim.logic.model.am2900.util; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.TreeSet; + +import net.mograsim.logic.core.components.BitDisplay; +import net.mograsim.logic.core.components.ManualSwitch; +import net.mograsim.logic.core.timeline.Timeline; +import net.mograsim.logic.model.LogicUIStandaloneGUI; +import net.mograsim.logic.model.am2900.TestableCircuit; +import net.mograsim.logic.model.am2900.TestableCircuit.Result; +import net.mograsim.logic.model.model.ViewModel; +import net.mograsim.logic.model.model.ViewModelModifiable; +import net.mograsim.logic.model.model.components.GUIComponent; +import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay; +import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch; +import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; +import net.mograsim.logic.model.model.wires.GUIWire; +import net.mograsim.logic.model.model.wires.Pin; +import net.mograsim.logic.model.modeladapter.LogicModelParameters; +import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter; +import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator; +import net.mograsim.logic.model.util.ModellingTool; + +public class TestEnvironmentHelper +{ + private final TestableCircuit testEnvInstance; + private final Class testEnvClass; + private final String modelId; + private Field componentField; + private Optional timelineField = Optional.empty(); + + private GUIComponent component; + private Timeline timeline; + private ViewModelModifiable viewModel; + private ModellingTool modellingTool; + private HashMap idSwitchMap = new HashMap<>(); + private HashMap idDisplayMap = new HashMap<>(); + + private DebugState debug = DebugState.NO_DEBUG; + private Set wireDebugChangeSet; + private boolean debugWires = false; + public int debugEventThreshold = 10_000; + public int debugEventCount = 500; + private int eventCounter; + + public TestEnvironmentHelper(TestableCircuit testEnv, String modelId) + { + this.testEnvInstance = testEnv; + this.modelId = modelId; + this.testEnvClass = testEnvInstance.getClass(); + for (Field f : testEnvClass.getDeclaredFields()) + { + if (GUIComponent.class.isAssignableFrom(f.getType())) + { + componentField = f; + componentField.setAccessible(true); + } else if (Timeline.class.isAssignableFrom(f.getType())) + { + f.setAccessible(true); + timelineField = Optional.of(f); + } + } + if (componentField == null) + throw new IllegalStateException("No component or timeline field found!"); + } + + public void setup(DebugState debug) + { + this.debug = debug; + // Create view model + viewModel = new ViewModelModifiable(); + modellingTool = ModellingTool.createFor(viewModel); + component = IndirectGUIComponentCreator.createComponent(viewModel, modelId); + setField(componentField, component); + + component.getPins().values().forEach(this::extendModelPin); + + // Create logic model + LogicModelParameters params = new LogicModelParameters(); + params.gateProcessTime = 50; + params.wireTravelTime = 10; + timeline = ViewLogicModelAdapter.convert(viewModel, params); + timelineField.ifPresent(f -> setField(f, timeline)); + + // Bind switches/displays to this test class + component.getPins().values().forEach(this::bindModelPin); + + if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST) + { + setupDebugging(); + } + timeline.addEventAddedListener(te -> eventCounter++); + } + + private void extendModelPin(Pin p) + { + String javaIdentId = idToJavaIdentifier(p.name); + try + { + Field f = testEnvClass.getDeclaredField(javaIdentId); + Class type = f.getType(); + if (ManualSwitch.class.isAssignableFrom(type)) + { + GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth); + modellingTool.connect(p, gms.getOutputPin()); + idSwitchMap.put(p.name, gms); + } else if (BitDisplay.class.isAssignableFrom(type)) + { + GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth); + modellingTool.connect(p, gbd.getInputPin()); + idDisplayMap.put(p.name, gbd); + } else if (SwitchWithDisplay.class.isAssignableFrom(type)) + { + SwitchWithDisplay swd = new SwitchWithDisplay(viewModel, p); + setField(f, swd); + } else + { + fail("unkown field type " + type); + } + } + catch (NoSuchFieldException | SecurityException e) + { + fail(e); + } + } + + private void bindModelPin(Pin p) + { + String javaIdentId = idToJavaIdentifier(p.name); + if (idDisplayMap.containsKey(p.name)) + setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay()); + if (idSwitchMap.containsKey(p.name)) + setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch()); + } + + private void setupDebugging() + { + // Debug code + HashSet wiresIncludingSubmodels = new HashSet<>(); + Queue modelsToIterate = new LinkedList<>(); + modelsToIterate.add(viewModel); + while (modelsToIterate.size() > 0) + { + ViewModel model = modelsToIterate.poll(); + wiresIncludingSubmodels.addAll(model.getWiresByName().values()); + for (GUIComponent comp : model.getComponentsByName().values()) + if (comp instanceof SubmodelComponent) + modelsToIterate.offer(((SubmodelComponent) comp).submodel); + } + System.out.println(wiresIncludingSubmodels.size()); + viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w -> + { + if (debugWires) + { + wireDebugChangeSet.add(w.toString()); + } + })); + } + + public Result run() + { + // Normal execution until completion or eventLimit + int eventLimit = debugEventThreshold; + eventCounter = 0; + debugWires = false; + while (eventCounter < eventLimit) + { + timeline.executeNext(); + if (!timeline.hasNext()) + return Result.SUCCESS; + } + + // Start debugging if event limit is reached (if debug is active) + if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST) + return debugThisRun(); + + return Result.OUT_OF_TIME; + } + + private Result debugThisRun() + { + int eventLimit = debugEventThreshold; + debugWires = true; + wireDebugChangeSet = new TreeSet<>(); + Set oldChangeSet; + // observe wire changes to detect, if we are really stuck in an endless loop + do + { + eventLimit += debugEventCount; + oldChangeSet = wireDebugChangeSet; + wireDebugChangeSet = new TreeSet<>(); + while (eventCounter < eventLimit) + { + timeline.executeNext(); + if (!timeline.hasNext()) + { + // no endless loop, but more events needed than expected + System.out.println("run() took longer than expected: " + eventCounter); + return Result.SUCCESS; + } + } + } while (!oldChangeSet.equals(wireDebugChangeSet)); + // if stuck, abort execution and print wires + System.err.print("Problematic Wire updates:"); + wireDebugChangeSet.forEach(System.out::println); + System.err.println("run() failed: " + eventCounter); + return Result.OUT_OF_TIME; + } + + private static String idToJavaIdentifier(String s) + { + StringBuilder sb = new StringBuilder(s.length()); + char c = s.charAt(0); + sb.append(Character.isJavaIdentifierStart(c) ? c : '_'); + for (int i = 1; i < s.length(); i++) + sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_'); + return sb.toString(); + } + + private void setField(Field f, S value) + { + try + { + f.setAccessible(true); + f.set(testEnvInstance, Objects.requireNonNull(value)); + } + catch (Exception e) + { + fail(e); + } + } + + private void setField(String name, S value) + { + try + { + Field f = testEnvClass.getDeclaredField(name); + f.setAccessible(true); + f.set(testEnvInstance, Objects.requireNonNull(value)); + } + catch (Exception e) + { + fail(e); + } + } + + public void displayState() + { + try + { + new LogicUIStandaloneGUI(viewModel).run(); + viewModel.setRedrawHandler(null); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public enum DebugState + { + NO_DEBUG, DEBUG_AT_PERFORMANCE_COST; + } +} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestUtil.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestUtil.java new file mode 100644 index 00000000..5989dfbe --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestUtil.java @@ -0,0 +1,100 @@ +package net.mograsim.logic.model.am2900.util; + +import net.mograsim.logic.core.types.Bit; +import net.mograsim.logic.core.types.BitVector; +import net.mograsim.logic.core.types.BitVector.BitVectorMutator; + +public final class TestUtil +{ + private TestUtil() + { + + } + + /** + * Transforms the last four bits of an int to a string that contains the binary ('1' and '0') representation of the 4 bits + * + * @author Christian Femers + */ + public static String to4bitBin(int x) + { + StringBuilder sb = new StringBuilder(4); + sb.append((x & 0b1000) == 0 ? '0' : '1'); + sb.append((x & 0b0100) == 0 ? '0' : '1'); + sb.append((x & 0b0010) == 0 ? '0' : '1'); + sb.append((x & 0b0001) == 0 ? '0' : '1'); + return sb.toString(); + } + + /** + * Transforms the given boolean to a string that contains the binary ('1' and '0') representation of the bit + * + * @author Christian Femers + */ + public static String to1bitBin(boolean bitIsSet) + { + return bitIsSet ? "1" : "0"; + } + + /** + * Transforms the given int to a string that contains the binary ('1' and '0') representation of the int. "0" is only returned when the + * int is equal to zero. + * + * @author Christian Femers + */ + public static String to1bitBin(int someInt) + { + return someInt != 0 ? "1" : "0"; + } + + /** + * Transforms a 4 bit signed integer (-8 to 7) to a int representing the same number. (Adding leading 1-bits if the 4 bit int is + * negative) + * + * @author Christian Femers + */ + public static int signed4ToSigned32(int signed4bit) + { + if ((signed4bit & 0b1000) > 0) + return signed4bit | 0xFF_FF_FF_F0; + return signed4bit & 0x00_00_00_0F; + } + + /** + * Transforms a 16 bit signed integer (-32768 to 32767 - a short) to a int representing the same number. (Adding leading 1-bits if the + * 16 bit int is negative) + * + * @author Christian Femers + */ + public static int signed16ToSigned32(int signed16bit) + { + return (short) signed16bit; + } + + /** + * Transforms the last n bits of an int to a string that contains the binary ('1' and '0') representation of the n bits + * + * @author Christian Femers + */ + public static String toNbitString(int x, int n) + { + StringBuilder sb = new StringBuilder(n); + for (int i = 0; i < n; i++) + { + sb.append((x >> i) & 1); + } + return sb.reverse().toString(); + } + + public static BitVector of(int value, int length) + { + BitVectorMutator mutator = BitVectorMutator.ofLength(length); + int val = value; + for (int i = length - 1; i >= 0; i--) + { + mutator.setMSBit(i, Bit.lastBitOf(val)); + val >>>= 1; + } + return mutator.toBitVector(); + } +}