From: Christian Femers Date: Fri, 23 Aug 2019 02:38:40 +0000 (+0200) Subject: Restructured test classes and improved readability X-Git-Url: https://mograsim.net/gitweb/?a=commitdiff_plain;h=75865384f939f597195f7a1a021ad545e2e23ee8;p=Mograsim.git Restructured test classes and improved readability --- diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Test.java deleted file mode 100644 index 7d87f39b..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Test.java +++ /dev/null @@ -1,298 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import static net.mograsim.logic.model.am2900.TestUtil.*; -import static net.mograsim.logic.model.am2900.TestableAm2901.Am2901_Dest.*; -import static net.mograsim.logic.model.am2900.TestableAm2901.Am2901_Func.*; -import static net.mograsim.logic.model.am2900.TestableAm2901.Am2901_Src.*; -import static net.mograsim.logic.model.am2900.TestableAm2901.Register.*; -import static org.junit.jupiter.api.Assertions.*; - -import java.awt.Point; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -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.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -import net.mograsim.logic.model.am2900.TestableAm2901.Register; - -@DisplayName("Am2901 Tests") -@TestMethodOrder(OrderAnnotation.class) -public class Am2901Test -{ - private TestableAm2901 am2901; - - @BeforeEach - void initialize() - { - createAndSetup(); - setInputsToZero(); - } - - void createAndSetup() - { - am2901 = new TestableAm2901Impl(); - am2901.setup(); - } - - void setRegistersToZero() - { - setInputsToZero(); - for (Register r : Register.values()) - { - setRegisterToZero(r); - } - } - - void setRegisterToZero(Register r) - { - am2901.setD("0000"); - am2901.setSrc(DZ); - am2901.setFunc(AND); - setRegOutput(r); - - am2901.assertFullCycleSuccess(); - } - - void setRegOutput(Register r) - { - if (r == Q) - { - am2901.setDest(QREG); - } else - { - am2901.setReg_B(r.toBitString()); - am2901.setDest(RAMF); - } - } - - void setInputsToZero() - { - am2901.setCarryIn("0"); - am2901.setQ_0("0"); - am2901.setQ_3("0"); - am2901.setRAM_0("0"); - am2901.setRAM_3("0"); - am2901.setReg_A("0000"); - am2901.setReg_B("0000"); - am2901.setD("0000"); - am2901.setSrc(AB); - am2901.setFunc(ADD); - am2901.setDest(QREG); -// am2901.setNotOutEnable("0"); TODO - am2901.clockOn(true); - am2901.assertRunSuccess(); - } - - @ParameterizedTest(name = "{0}") - @Order(1) - @DisplayName("Direct / high level access") - @EnumSource(Register.class) - void testDirectAccess(Register r) - { - assertEquals("UUUU", am2901.getDirectly(r)); - - am2901.setDirectly(r, "1011"); - - assertEquals("1011", am2901.getDirectly(r)); - } - - @ParameterizedTest(name = "{0}") - @Order(2) - @DisplayName("Setting each register to 0") - @EnumSource(Register.class) - void testSetToZero(Register r) - { - assertEquals("UUUU", am2901.getDirectly(r)); - - setRegisterToZero(r); - - assertEquals("0000", am2901.getDirectly(r)); - assertEquals("0000", am2901.getY()); - assertEquals("0", am2901.getCarryOut()); - assertEquals("0", am2901.getOverflow()); - assertEquals("0", am2901.getSign()); - assertEquals("1", am2901.getZero()); - } - - @Test - @Order(3) - @DisplayName("Setting all registers to 0") - void testSetAllToZero() - { - setRegistersToZero(); - - assertEquals("0000", am2901.getY()); - assertEquals("0", am2901.getCarryOut()); - assertEquals("0", am2901.getOverflow()); - assertEquals("0", am2901.getSign()); - assertEquals("1", am2901.getZero()); - assertEquals("0", am2901.getQ_0()); - assertEquals("0", am2901.getQ_3()); - assertEquals("0", am2901.getRAM_0()); - assertEquals("0", am2901.getRAM_3()); - - assertAll("register values", Register.stream().map(r -> () -> - { - assertEquals("0000", am2901.getDirectly(r), r.name()); - })); - } - - @Test - @Order(4) - @DisplayName("ADD operation") - void testADD() - { - am2901.setSrc(DA); - am2901.setFunc(ADD); - am2901.setDest(NOP); - am2901.setReg_A(r0.toBitString()); - - assertAll(getAll4BitPairs().map(xy -> () -> - { - am2901.setDirectly(r0, to4bitBin(xy.x)); - am2901.setD(to4bitBin(xy.y)); - - am2901.assertFullCycleSuccess(); - - int res32Bit = xy.x + xy.y; - int res4Bit = res32Bit & 0b1111; - int res32Bit_sgn = signed4ToSigned32(xy.x) + signed4ToSigned32(xy.y); - int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn); - - assertAll("Result of " + xy.x + " + " + xy.y + " = " + res32Bit, - () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), - () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), " F=0"), - () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), " F3"), - () -> assertEquals(to1bitBin(res32Bit > 15), am2901.getCarryOut(), " Cn+4"), - () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), " OVR")); - })); - } - - @Test - @Order(4) - @DisplayName("AND operation") - void testAND() - { - am2901.setSrc(DA); - am2901.setFunc(AND); - am2901.setDest(NOP); - am2901.setReg_A(r0.toBitString()); - - assertAll(getAll4BitPairs().map(xy -> () -> - { - am2901.setDirectly(r0, to4bitBin(xy.x)); - am2901.setD(to4bitBin(xy.y)); - - am2901.assertFullCycleSuccess(); - - int res32Bit = xy.x & xy.y; - - assertAll("Result of " + xy.x + " & " + xy.y + " = " + res32Bit, - () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), - () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), " F=0"), - () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), " F3") -// () -> assertEquals(to1bitBin(res32Bit), am2901.getCarryOut(), " Cn+4"), // TODO -// () -> assertEquals(to1bitBin(res32Bit), am2901.getOverflow(), " OVR") // TODO - ); - })); - } - - @Test - @Order(4) - @DisplayName("OR operation") - void testOR() - { - am2901.setSrc(DA); - am2901.setFunc(OR); - am2901.setDest(NOP); - am2901.setReg_A(r0.toBitString()); - - assertAll(getAll4BitPairs().map(xy -> () -> - { - am2901.setDirectly(r0, to4bitBin(xy.x)); - am2901.setD(to4bitBin(xy.y)); - - am2901.assertFullCycleSuccess(); - - int res32Bit = xy.x | xy.y; - - assertAll("Result of " + xy.x + " | " + xy.y + " = " + res32Bit, - () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), - () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), " F=0"), - () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), " F3") -// () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getCarryOut(), " Cn+4"), // TODO -// () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getOverflow(), " OVR") // TODO - ); - })); - } - - @Test - @Order(4) - @DisplayName("XOR operation") - void testXOR() - { - am2901.setSrc(DA); - am2901.setFunc(EXOR); - am2901.setDest(NOP); - am2901.setReg_A(r0.toBitString()); - - assertAll(getAll4BitPairs().map(xy -> () -> - { - am2901.setDirectly(r0, to4bitBin(xy.x)); - am2901.setD(to4bitBin(xy.y)); - - am2901.assertFullCycleSuccess(); - - int res32Bit = xy.x ^ xy.y; - - assertAll("Result of " + xy.x + " ^ " + xy.y + " = " + res32Bit, - () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), - () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), " F=0"), - () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), " F3")); - })); - } - - @Test - @Order(4) - @DisplayName("SUB operation") - void testSUB() - { - am2901.setSrc(DA); - am2901.setCarryIn("1"); - am2901.setFunc(SUBR); - am2901.setDest(NOP); - am2901.setReg_A(r0.toBitString()); - - assertAll(getAll4BitPairs().map(xy -> () -> - { - am2901.setDirectly(r0, to4bitBin(xy.x)); - am2901.setD(to4bitBin(xy.y)); - - am2901.assertFullCycleSuccess(); - - int res32Bit = xy.x - xy.y; - int res4Bit = res32Bit & 0b1111; - int res32Bit_sgn = signed4ToSigned32(xy.x) - signed4ToSigned32(xy.y); - int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn); - - assertAll("Result of " + xy.x + " - " + xy.y + " = " + res32Bit, - () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), - () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), " F=0"), - () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), " F3"), - () -> assertEquals(to1bitBin(xy.x >= xy.y), am2901.getCarryOut(), " Cn+4"), - () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), " OVR")); - })); - } - - static Stream getAll4BitPairs() - { - return IntStream.range(0, 16).boxed().flatMap(x -> IntStream.range(0, 16).mapToObj(y -> new Point(x, y))); - } -} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Testbench.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Testbench.java deleted file mode 100644 index 731147ce..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Testbench.java +++ /dev/null @@ -1,102 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -import net.mograsim.logic.model.SimpleLogicUIStandalone; -import net.mograsim.logic.model.model.ViewModelModifiable; -import net.mograsim.logic.model.model.components.GUIComponent; -import net.mograsim.logic.model.model.components.atomic.GUIAndGate; -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.atomic.GUINotGate; -import net.mograsim.logic.model.model.components.atomic.TextComponent; -import net.mograsim.logic.model.model.wires.Pin; -import net.mograsim.logic.model.model.wires.WireCrossPoint; -import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator; -import net.mograsim.logic.model.util.ModellingTool; - -public class Am2901Testbench -{ - public static void main(String[] args) - { - SimpleLogicUIStandalone.executeVisualisation(Am2901Testbench::createTestbench); - } - - public static void createTestbench(ViewModelModifiable model) - { - GUIComponent comp = IndirectGUIComponentCreator.createComponent(model, "GUIAm2901"); - ModellingTool tool = ModellingTool.createFor(model); - - comp.moveTo(240, 0); - - GUIManualSwitch enable = new GUIManualSwitch(model, 1); - WireCrossPoint wcp0 = new WireCrossPoint(model, 1); - GUINotGate not1 = new GUINotGate(model, 1); - GUINotGate not2 = new GUINotGate(model, 1); - GUINotGate not3 = new GUINotGate(model, 1); - GUIAndGate and = new GUIAndGate(model, 1); - tool.connect(wcp0, enable, ""); - tool.connect(wcp0, and, "A"); - tool.connect(wcp0, not1, "A"); - tool.connect(not1, not2, "Y", "A"); - tool.connect(not2, not3, "Y", "A"); - tool.connect(not3, and, "Y", "B"); - enable.moveTo(20, -32.5); - wcp0.moveTo(35, -26); - not1.moveTo(50, -20); - not2.moveTo(80, -20); - not3.moveTo(110, -20); - and.moveTo(135, -30); - Pin last = and.getPin("Y"); - - // guess which pins are outputs and which are inputs - // TODO this code exists four times... but it seems too "hacky" to put it in a helper class - List inputPinNames = new ArrayList<>(); - List outputPinNames = new ArrayList<>(); - for (Pin p : comp.getPins().values()) - if (p.getRelX() == 0) - inputPinNames.add(p.name); - else - outputPinNames.add(p.name); - - inputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY))); - outputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY))); - - for (int i = 0; i < inputPinNames.size(); i++) - { - double x = 55 + 70 * (i % 2); - double y = 10 * i; - - WireCrossPoint wcp = new WireCrossPoint(model, 1); - GUIComponent d_ff = IndirectGUIComponentCreator.createComponent(model, "GUIdff"); - GUIManualSwitch sw = new GUIManualSwitch(model, 1); - - tool.connect(last, wcp); - tool.connect(wcp, d_ff, "C"); - tool.connect(sw, d_ff, "", "D"); - tool.connect(d_ff, comp, "Q", inputPinNames.get(i)); - last = wcp.getPin(); - - TextComponent label = new TextComponent(model, inputPinNames.get(i)); - - sw.moveTo(x, y + 7.5); - wcp.moveTo(160, y); - d_ff.moveTo(170, y); - label.moveTo(x - 48, y + 8); - } - - for (int i = 0; i < outputPinNames.size(); i++) - { - double x = 300 + 75 * (i % 2); - double y = 10 * i - 2.5; - GUIBitDisplay bd = new GUIBitDisplay(model, 1); - bd.moveTo(x, y); - tool.connect(bd.getInputPin(), comp, outputPinNames.get(i)); - - TextComponent label = new TextComponent(model, outputPinNames.get(i)); - label.moveTo(x + 25, y); - } - } -} \ No newline at end of file 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 new file mode 100644 index 00000000..9390a019 --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java @@ -0,0 +1,253 @@ +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 + { + 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 index f85ae04a..46a4b92c 100644 --- 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 @@ -1,5 +1,15 @@ 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() @@ -81,4 +91,16 @@ public final class TestUtil } 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/TestableAm2901.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901.java deleted file mode 100644 index d898b399..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901.java +++ /dev/null @@ -1,131 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.Arrays; -import java.util.stream.Stream; - -public interface TestableAm2901 -{ - void setup(); - - Result run(); - - void setDest(Am2901_Dest dest); - - void setFunc(Am2901_Func func); - - void setSrc(Am2901_Src src); - - void setReg_A(String val_4_bit); - - void setReg_B(String val_4_bit); - - void setCarryIn(String val_1_bit); - - void setNotOutEnable(String val_1_bit); - - void setD(String val_4_bit); - - void setQ_0(String val_1_bit); - - void setQ_3(String val_1_bit); - - void setRAM_0(String val_1_bit); - - void setRAM_3(String val_1_bit); - - void clockOn(boolean isClockOn); - - void setDirectly(Register r, String val_4_bit); - - String getQ_0(); - - String getQ_3(); - - String getRAM_0(); - - String getRAM_3(); - - String getNotP(); - - String getNotG(); - - String getCarryOut(); - - String getSign(); - - String getZero(); - - String getOverflow(); - - String getY(); - - String getDirectly(Register r); - - default void assertRunSuccess() - { - assertEquals(Result.SUCCESS, run()); - } - - default void assertFullCycleSuccess() - { - assertRunSuccess(); - clockOn(false); - assertRunSuccess(); - clockOn(true); - assertRunSuccess(); - } - - public enum Result - { - SUCCESS, OUT_OF_TIME, ERROR; - } - - public enum Am2901_Dest - { - QREG, NOP, RAMA, RAMF, RAMQD, RAMD, RAMQU, RAMU; - - public boolean doesShift() - { - return ordinal() >= 4; - } - - public int getShiftDir() - { - return doesShift() ? (ordinal() < 6 ? -1 : 1) : 0; - } - - public int getI7() - { - return this.ordinal() >> 1 & 1; - } - } - - public enum Am2901_Func - { - ADD, SUBR, SUBS, OR, AND, NOTRS, EXOR, EXNOR; - } - - public enum Am2901_Src - { - AQ, AB, ZQ, ZB, ZA, DA, DQ, DZ; - } - - public enum Register - { - r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, rA, rB, rC, rD, rE, rF, Q; - - public String toBitString() - { - if (this.ordinal() > 0xF) - throw new UnsupportedOperationException(); - return TestUtil.to4bitBin(this.ordinal()); - } - - public static Stream stream() - { - return Arrays.stream(values()); - } - } -} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901Impl.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901Impl.java deleted file mode 100644 index e84588d9..00000000 --- a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901Impl.java +++ /dev/null @@ -1,381 +0,0 @@ -package net.mograsim.logic.model.am2900; - -import static org.junit.jupiter.api.Assertions.fail; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -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.core.types.Bit; -import net.mograsim.logic.core.types.BitVector; -import net.mograsim.logic.core.types.BitVector.BitVectorMutator; -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; - -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; - private ManualSwitch D1, D2, D3, D4; - private ManualSwitch A0, A1, A2, A3; - private ManualSwitch B0, B1, B2, B3; - private ManualSwitch IRAMn, IRAMn_3, IQn, IQn_3; - private BitDisplay Y1, Y2, Y3, Y4; - private BitDisplay F_0, Cn_4, OVR, F3; - private BitDisplay ORAMn, ORAMn_3, OQn, OQn_3; - - private Set wireDebugChangeSet; - private boolean debugWires = false; - public int debugEventThreshold = 10_000; - public int debugEventCount = 500; - - private int eventCounter; - - @Override - public Result run() - { - // Normal execution until completion or eventLimit - int eventLimit = debugEventThreshold; - eventCounter = 0; - debugWires = false; - while (eventCounter < eventLimit) - { - timeline.executeNext(); - if (!timeline.hasNext()) - { -// System.out.println("run() took " + eventCounter + " events"); - return Result.SUCCESS; - } - } - // Start debugging if event limit is reached - 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; - } - - @SuppressWarnings("unused") - @Override - public void setup() - { - // Create view model - ViewModelModifiable viewModel = new ViewModelModifiable(); - am2901 = IndirectGUIComponentCreator.createComponent(viewModel, "GUIAm2901"); - // guess which pins are outputs and which are inputs - // TODO this code exists four times... but it seems too "hacky" to put it in a helper class - List inputPinNames = new ArrayList<>(); - List outputPinNames = new ArrayList<>(); - for (Pin p : am2901.getPins().values()) - if (p.getRelX() == 0) - inputPinNames.add(p.name); - else - outputPinNames.add(p.name); - // Get switches - HashMap idSwitchMap = new HashMap<>(); - for (String id : inputPinNames) - { - GUIManualSwitch sw = new GUIManualSwitch(viewModel, 1); - new GUIWire(viewModel, am2901.getPin(id), sw.getOutputPin()); - idSwitchMap.put(id, sw); - } - // Get displays - HashMap idDisplayMap = new HashMap<>(); - for (String id : outputPinNames) - { - GUIBitDisplay bd = new GUIBitDisplay(viewModel, 1); -// bd.addRedrawListener(() -> System.out.println(id + " " + bd.getBitDisplay().getDisplayedValue())); - new GUIWire(viewModel, am2901.getPin(id), bd.getInputPin()); - idDisplayMap.put(id, bd); - } - // Create logic model - LogicModelParameters params = new LogicModelParameters(); - params.gateProcessTime = 50; - params.wireTravelTime = 10; - timeline = ViewLogicModelAdapter.convert(viewModel, params); - // Bind switches/displays to this test class - for (var entry : idSwitchMap.entrySet()) - setField(entry.getKey().replaceAll("\\+|=", "_"), entry.getValue().getManualSwitch()); - for (var entry : idDisplayMap.entrySet()) - setField(entry.getKey().replaceAll("\\+|=", "_"), entry.getValue().getBitDisplay()); - - // 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); - } - // NOTE: This commented out code is only used for serious debugging (performance cost!) -// System.out.println(wiresIncludingSubmodels.size()); -// viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w -> -// { -// if (debugWires) -// { -// wireDebugChangeSet.add(w.toString()); -// } -// })); - timeline.addEventAddedListener(te -> eventCounter++); - } - - @Override - public void setDest(Am2901_Dest dest) - { - var bits = of(dest.ordinal(), 3); - I8.setState(bits.getLSBit(2)); - I7.setState(bits.getLSBit(1)); - I6.setState(bits.getLSBit(0)); - } - - @Override - public void setFunc(Am2901_Func func) - { - var bits = of(func.ordinal(), 3); - I5.setState(bits.getLSBit(2)); - I4.setState(bits.getLSBit(1)); - I3.setState(bits.getLSBit(0)); - } - - @Override - public void setSrc(Am2901_Src src) - { - var bits = of(src.ordinal(), 3); - I2.setState(bits.getLSBit(2)); - I1.setState(bits.getLSBit(1)); - I0.setState(bits.getLSBit(0)); - } - - @Override - public void setReg_A(String val_4_bit) - { - var bits = BitVector.parse(val_4_bit); - A3.setState(bits.getLSBit(3)); - A2.setState(bits.getLSBit(2)); - A1.setState(bits.getLSBit(1)); - A0.setState(bits.getLSBit(0)); - } - - @Override - public void setReg_B(String val_4_bit) - { - var bits = BitVector.parse(val_4_bit); - B3.setState(bits.getLSBit(3)); - B2.setState(bits.getLSBit(2)); - B1.setState(bits.getLSBit(1)); - B0.setState(bits.getLSBit(0)); - } - - @Override - public void setCarryIn(String val_1_bit) - { - Cn.setState(Bit.parse(val_1_bit)); - } - - @Override - public void setNotOutEnable(String val_1_bit) - { - throw new UnsupportedOperationException(); // TODO - } - - @Override - public void setD(String val_4_bit) - { - var bits = BitVector.parse(val_4_bit); - D4.setState(bits.getLSBit(3)); - D3.setState(bits.getLSBit(2)); - D2.setState(bits.getLSBit(1)); - D1.setState(bits.getLSBit(0)); - } - - @Override - public void setQ_0(String val_1_bit) - { - IQn.setState(Bit.parse(val_1_bit)); - } - - @Override - public void setQ_3(String val_1_bit) - { - IQn_3.setState(Bit.parse(val_1_bit)); - } - - @Override - public void setRAM_0(String val_1_bit) - { - IRAMn.setState(Bit.parse(val_1_bit)); - } - - @Override - public void setRAM_3(String val_1_bit) - { - IRAMn_3.setState(Bit.parse(val_1_bit)); - } - - @Override - public void clockOn(boolean isClockOn) - { - C.setState(isClockOn ? Bit.ONE : Bit.ZERO); - } - - @Override - public String getQ_0() - { - return OQn.getDisplayedValue().toString(); - } - - @Override - public String getQ_3() - { - return OQn_3.getDisplayedValue().toString(); - } - - @Override - public String getRAM_0() - { - return ORAMn.getDisplayedValue().toString(); - } - - @Override - public String getRAM_3() - { - return ORAMn_3.getDisplayedValue().toString(); - } - - @Override - public String getNotP() - { - throw new UnsupportedOperationException(); // TODO - } - - @Override - public String getNotG() - { - throw new UnsupportedOperationException(); // TODO - } - - @Override - public String getCarryOut() - { - return Cn_4.getDisplayedValue().toString(); - } - - @Override - public String getSign() - { - return F3.getDisplayedValue().toString(); - } - - @Override - public String getZero() - { - return F_0.getDisplayedValue().toString(); - } - - @Override - public String getOverflow() - { - return OVR.getDisplayedValue().toString(); - } - - @Override - public String getY() - { - var y3 = Y4.getDisplayedValue(); - var y2 = Y3.getDisplayedValue(); - var y1 = Y2.getDisplayedValue(); - var y0 = Y1.getDisplayedValue(); - return y3.concat(y2).concat(y1).concat(y0).toString(); - } - - private void setField(String name, Object value) - { - try - { - Field f = TestableAm2901Impl.class.getDeclaredField(name); - f.setAccessible(true); - f.set(this, Objects.requireNonNull(value)); - } - catch (Exception e) - { - fail(e); - } - } - - private 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(); - } - - @Override - public void setDirectly(Register r, String val_4_bit) - { - am2901.setHighLevelState(regToStateID(r), BitVector.parse(val_4_bit)); - } - - @Override - public String getDirectly(Register r) - { - return ((BitVector) am2901.getHighLevelState(regToStateID(r))).toString(); - } - - private static String regToStateID(Register r) - { - if (r == Register.Q) - return "qreg.q"; - return "regs.c" + r.toBitString() + ".q"; - } -} 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 new file mode 100644 index 00000000..39dc09ba --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java @@ -0,0 +1,31 @@ +package net.mograsim.logic.model.am2900; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public interface TestableCircuit +{ + void setup(); + + Result run(); + + void clockOn(boolean isClockOn); + + default void assertRunSuccess() + { + assertEquals(Result.SUCCESS, run()); + } + + default void assertFullCycleSuccess() + { + assertRunSuccess(); + clockOn(false); + assertRunSuccess(); + clockOn(true); + assertRunSuccess(); + } + + 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 new file mode 100644 index 00000000..57461c62 --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java @@ -0,0 +1,298 @@ +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 org.junit.jupiter.api.Assertions.*; + +import java.awt.Point; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +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.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register; + +@DisplayName("Am2901 Tests") +@TestMethodOrder(OrderAnnotation.class) +public class Am2901Test +{ + private TestableAm2901 am2901; + + @BeforeEach + void initialize() + { + createAndSetup(); + setInputsToZero(); + } + + void createAndSetup() + { + am2901 = new TestableAm2901Impl(); + am2901.setup(); + } + + void setRegistersToZero() + { + setInputsToZero(); + for (Register r : Register.values()) + { + setRegisterToZero(r); + } + } + + void setRegisterToZero(Register r) + { + am2901.setD("0000"); + am2901.setSrc(DZ); + am2901.setFunc(AND); + setRegOutput(r); + + am2901.assertFullCycleSuccess(); + } + + void setRegOutput(Register r) + { + if (r == Q) + { + am2901.setDest(QREG); + } else + { + am2901.setReg_B(r.toBitString()); + am2901.setDest(RAMF); + } + } + + void setInputsToZero() + { + am2901.setCarryIn("0"); + am2901.setQ_0("0"); + am2901.setQ_3("0"); + am2901.setRAM_0("0"); + am2901.setRAM_3("0"); + am2901.setReg_A("0000"); + am2901.setReg_B("0000"); + am2901.setD("0000"); + am2901.setSrc(AB); + am2901.setFunc(ADD); + am2901.setDest(QREG); +// am2901.setNotOutEnable("0"); TODO + am2901.clockOn(true); + am2901.assertRunSuccess(); + } + + @ParameterizedTest(name = "{0}") + @Order(1) + @DisplayName("Direct / high level access") + @EnumSource(Register.class) + void testDirectAccess(Register r) + { + assertEquals("UUUU", am2901.getDirectly(r)); + + am2901.setDirectly(r, "1011"); + + assertEquals("1011", am2901.getDirectly(r)); + } + + @ParameterizedTest(name = "{0}") + @Order(2) + @DisplayName("Setting each register to 0") + @EnumSource(Register.class) + void testSetToZero(Register r) + { + assertEquals("UUUU", am2901.getDirectly(r)); + + setRegisterToZero(r); + + assertEquals("0000", am2901.getDirectly(r)); + assertEquals("0000", am2901.getY()); + assertEquals("0", am2901.getCarryOut()); + assertEquals("0", am2901.getOverflow()); + assertEquals("0", am2901.getSign()); + assertEquals("1", am2901.getZero()); + } + + @Test + @Order(3) + @DisplayName("Setting all registers to 0") + void testSetAllToZero() + { + setRegistersToZero(); + + assertEquals("0000", am2901.getY()); + assertEquals("0", am2901.getCarryOut()); + assertEquals("0", am2901.getOverflow()); + assertEquals("0", am2901.getSign()); + assertEquals("1", am2901.getZero()); + assertEquals("0", am2901.getQ_0()); + assertEquals("0", am2901.getQ_3()); + assertEquals("0", am2901.getRAM_0()); + assertEquals("0", am2901.getRAM_3()); + + assertAll("register values", Register.stream().map(r -> () -> + { + assertEquals("0000", am2901.getDirectly(r), r.name()); + })); + } + + @Test + @Order(4) + @DisplayName("ADD operation") + void testADD() + { + am2901.setSrc(DA); + am2901.setFunc(ADD); + am2901.setDest(NOP); + am2901.setReg_A(r0.toBitString()); + + assertAll(getAll4BitPairs().map(xy -> () -> + { + am2901.setDirectly(r0, to4bitBin(xy.x)); + am2901.setD(to4bitBin(xy.y)); + + am2901.assertFullCycleSuccess(); + + int res32Bit = xy.x + xy.y; + int res4Bit = res32Bit & 0b1111; + int res32Bit_sgn = signed4ToSigned32(xy.x) + signed4ToSigned32(xy.y); + int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn); + + assertAll("Result of " + xy.x + " + " + xy.y + " = " + res32Bit, + () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), + () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), " F=0"), + () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), " F3"), + () -> assertEquals(to1bitBin(res32Bit > 15), am2901.getCarryOut(), " Cn+4"), + () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), " OVR")); + })); + } + + @Test + @Order(4) + @DisplayName("AND operation") + void testAND() + { + am2901.setSrc(DA); + am2901.setFunc(AND); + am2901.setDest(NOP); + am2901.setReg_A(r0.toBitString()); + + assertAll(getAll4BitPairs().map(xy -> () -> + { + am2901.setDirectly(r0, to4bitBin(xy.x)); + am2901.setD(to4bitBin(xy.y)); + + am2901.assertFullCycleSuccess(); + + int res32Bit = xy.x & xy.y; + + assertAll("Result of " + xy.x + " & " + xy.y + " = " + res32Bit, + () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), + () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), " F=0"), + () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), " F3") +// () -> assertEquals(to1bitBin(res32Bit), am2901.getCarryOut(), " Cn+4"), // TODO +// () -> assertEquals(to1bitBin(res32Bit), am2901.getOverflow(), " OVR") // TODO + ); + })); + } + + @Test + @Order(4) + @DisplayName("OR operation") + void testOR() + { + am2901.setSrc(DA); + am2901.setFunc(OR); + am2901.setDest(NOP); + am2901.setReg_A(r0.toBitString()); + + assertAll(getAll4BitPairs().map(xy -> () -> + { + am2901.setDirectly(r0, to4bitBin(xy.x)); + am2901.setD(to4bitBin(xy.y)); + + am2901.assertFullCycleSuccess(); + + int res32Bit = xy.x | xy.y; + + assertAll("Result of " + xy.x + " | " + xy.y + " = " + res32Bit, + () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), + () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), " F=0"), + () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), " F3") +// () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getCarryOut(), " Cn+4"), // TODO +// () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getOverflow(), " OVR") // TODO + ); + })); + } + + @Test + @Order(4) + @DisplayName("XOR operation") + void testXOR() + { + am2901.setSrc(DA); + am2901.setFunc(EXOR); + am2901.setDest(NOP); + am2901.setReg_A(r0.toBitString()); + + assertAll(getAll4BitPairs().map(xy -> () -> + { + am2901.setDirectly(r0, to4bitBin(xy.x)); + am2901.setD(to4bitBin(xy.y)); + + am2901.assertFullCycleSuccess(); + + int res32Bit = xy.x ^ xy.y; + + assertAll("Result of " + xy.x + " ^ " + xy.y + " = " + res32Bit, + () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), + () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), " F=0"), + () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), " F3")); + })); + } + + @Test + @Order(4) + @DisplayName("SUB operation") + void testSUB() + { + am2901.setSrc(DA); + am2901.setCarryIn("1"); + am2901.setFunc(SUBR); + am2901.setDest(NOP); + am2901.setReg_A(r0.toBitString()); + + assertAll(getAll4BitPairs().map(xy -> () -> + { + am2901.setDirectly(r0, to4bitBin(xy.x)); + am2901.setD(to4bitBin(xy.y)); + + am2901.assertFullCycleSuccess(); + + int res32Bit = xy.x - xy.y; + int res4Bit = res32Bit & 0b1111; + int res32Bit_sgn = signed4ToSigned32(xy.x) - signed4ToSigned32(xy.y); + int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn); + + assertAll("Result of " + xy.x + " - " + xy.y + " = " + res32Bit, + () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), " Y"), + () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), " F=0"), + () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), " F3"), + () -> assertEquals(to1bitBin(xy.x >= xy.y), am2901.getCarryOut(), " Cn+4"), + () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), " OVR")); + })); + } + + static Stream getAll4BitPairs() + { + return IntStream.range(0, 16).boxed().flatMap(x -> IntStream.range(0, 16).mapToObj(y -> new Point(x, y))); + } +} diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Testbench.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Testbench.java new file mode 100644 index 00000000..3a7ffa22 --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Testbench.java @@ -0,0 +1,102 @@ +package net.mograsim.logic.model.am2900.am2901; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import net.mograsim.logic.model.SimpleLogicUIStandalone; +import net.mograsim.logic.model.model.ViewModelModifiable; +import net.mograsim.logic.model.model.components.GUIComponent; +import net.mograsim.logic.model.model.components.atomic.GUIAndGate; +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.atomic.GUINotGate; +import net.mograsim.logic.model.model.components.atomic.TextComponent; +import net.mograsim.logic.model.model.wires.Pin; +import net.mograsim.logic.model.model.wires.WireCrossPoint; +import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator; +import net.mograsim.logic.model.util.ModellingTool; + +public class Am2901Testbench +{ + public static void main(String[] args) + { + SimpleLogicUIStandalone.executeVisualisation(Am2901Testbench::createTestbench); + } + + public static void createTestbench(ViewModelModifiable model) + { + GUIComponent comp = IndirectGUIComponentCreator.createComponent(model, "GUIAm2901"); + ModellingTool tool = ModellingTool.createFor(model); + + comp.moveTo(240, 0); + + GUIManualSwitch enable = new GUIManualSwitch(model, 1); + WireCrossPoint wcp0 = new WireCrossPoint(model, 1); + GUINotGate not1 = new GUINotGate(model, 1); + GUINotGate not2 = new GUINotGate(model, 1); + GUINotGate not3 = new GUINotGate(model, 1); + GUIAndGate and = new GUIAndGate(model, 1); + tool.connect(wcp0, enable, ""); + tool.connect(wcp0, and, "A"); + tool.connect(wcp0, not1, "A"); + tool.connect(not1, not2, "Y", "A"); + tool.connect(not2, not3, "Y", "A"); + tool.connect(not3, and, "Y", "B"); + enable.moveTo(20, -32.5); + wcp0.moveTo(35, -26); + not1.moveTo(50, -20); + not2.moveTo(80, -20); + not3.moveTo(110, -20); + and.moveTo(135, -30); + Pin last = and.getPin("Y"); + + // guess which pins are outputs and which are inputs + // TODO this code exists four times... but it seems too "hacky" to put it in a helper class + List inputPinNames = new ArrayList<>(); + List outputPinNames = new ArrayList<>(); + for (Pin p : comp.getPins().values()) + if (p.getRelX() == 0) + inputPinNames.add(p.name); + else + outputPinNames.add(p.name); + + inputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY))); + outputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY))); + + for (int i = 0; i < inputPinNames.size(); i++) + { + double x = 55 + 70 * (i % 2); + double y = 10 * i; + + WireCrossPoint wcp = new WireCrossPoint(model, 1); + GUIComponent d_ff = IndirectGUIComponentCreator.createComponent(model, "GUIdff"); + GUIManualSwitch sw = new GUIManualSwitch(model, 1); + + tool.connect(last, wcp); + tool.connect(wcp, d_ff, "C"); + tool.connect(sw, d_ff, "", "D"); + tool.connect(d_ff, comp, "Q", inputPinNames.get(i)); + last = wcp.getPin(); + + TextComponent label = new TextComponent(model, inputPinNames.get(i)); + + sw.moveTo(x, y + 7.5); + wcp.moveTo(160, y); + d_ff.moveTo(170, y); + label.moveTo(x - 48, y + 8); + } + + for (int i = 0; i < outputPinNames.size(); i++) + { + double x = 300 + 75 * (i % 2); + double y = 10 * i - 2.5; + GUIBitDisplay bd = new GUIBitDisplay(model, 1); + bd.moveTo(x, y); + tool.connect(bd.getInputPin(), comp, outputPinNames.get(i)); + + TextComponent label = new TextComponent(model, outputPinNames.get(i)); + label.moveTo(x + 25, y); + } + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..34c179da --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java @@ -0,0 +1,108 @@ +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; + +public interface TestableAm2901 extends TestableCircuit +{ + + void setDest(Am2901_Dest dest); + + void setFunc(Am2901_Func func); + + void setSrc(Am2901_Src src); + + void setReg_A(String val_4_bit); + + void setReg_B(String val_4_bit); + + void setCarryIn(String val_1_bit); + + void setNotOutEnable(String val_1_bit); + + void setD(String val_4_bit); + + void setQ_0(String val_1_bit); + + void setQ_3(String val_1_bit); + + void setRAM_0(String val_1_bit); + + void setRAM_3(String val_1_bit); + + void setDirectly(Register r, String val_4_bit); + + String getQ_0(); + + String getQ_3(); + + String getRAM_0(); + + String getRAM_3(); + + String getNotP(); + + String getNotG(); + + String getCarryOut(); + + String getSign(); + + String getZero(); + + String getOverflow(); + + String getY(); + + String getDirectly(Register r); + + public enum Am2901_Dest + { + QREG, NOP, RAMA, RAMF, RAMQD, RAMD, RAMQU, RAMU; + + public boolean doesShift() + { + return ordinal() >= 4; + } + + public int getShiftDir() + { + return doesShift() ? (ordinal() < 6 ? -1 : 1) : 0; + } + + public int getI7() + { + return this.ordinal() >> 1 & 1; + } + } + + public enum Am2901_Func + { + ADD, SUBR, SUBS, OR, AND, NOTRS, EXOR, EXNOR; + } + + public enum Am2901_Src + { + AQ, AB, ZQ, ZB, ZA, DA, DQ, DZ; + } + + public enum Register + { + r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, rA, rB, rC, rD, rE, rF, Q; + + public String toBitString() + { + if (this.ordinal() > 0xF) + throw new UnsupportedOperationException(); + return TestUtil.to4bitBin(this.ordinal()); + } + + public static Stream stream() + { + return Arrays.stream(values()); + } + } +} 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 new file mode 100644 index 00000000..34c68c1e --- /dev/null +++ b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java @@ -0,0 +1,229 @@ +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.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; + private ManualSwitch D1, D2, D3, D4; + private ManualSwitch A0, A1, A2, A3; + private ManualSwitch B0, B1, B2, B3; + private ManualSwitch IRAMn, IRAMn_3, IQn, IQn_3; + private BitDisplay Y1, Y2, Y3, Y4; + private BitDisplay F_0, Cn_4, OVR, F3; + private BitDisplay ORAMn, ORAMn_3, OQn, OQn_3; + + private final TestEnvironmentHelper testHelper = new TestEnvironmentHelper(this, "GUIAm2901"); + + @Override + public Result run() + { + return testHelper.run(); + } + + @Override + public void setup() + { + testHelper.setup(DebugState.NO_DEBUG); + } + + @Override + public void setDest(Am2901_Dest dest) + { + var bits = TestUtil.of(dest.ordinal(), 3); + I8.setState(bits.getLSBit(2)); + I7.setState(bits.getLSBit(1)); + I6.setState(bits.getLSBit(0)); + } + + @Override + public void setFunc(Am2901_Func func) + { + var bits = TestUtil.of(func.ordinal(), 3); + I5.setState(bits.getLSBit(2)); + I4.setState(bits.getLSBit(1)); + I3.setState(bits.getLSBit(0)); + } + + @Override + public void setSrc(Am2901_Src src) + { + var bits = TestUtil.of(src.ordinal(), 3); + I2.setState(bits.getLSBit(2)); + I1.setState(bits.getLSBit(1)); + I0.setState(bits.getLSBit(0)); + } + + @Override + public void setReg_A(String val_4_bit) + { + var bits = BitVector.parse(val_4_bit); + A3.setState(bits.getLSBit(3)); + A2.setState(bits.getLSBit(2)); + A1.setState(bits.getLSBit(1)); + A0.setState(bits.getLSBit(0)); + } + + @Override + public void setReg_B(String val_4_bit) + { + var bits = BitVector.parse(val_4_bit); + B3.setState(bits.getLSBit(3)); + B2.setState(bits.getLSBit(2)); + B1.setState(bits.getLSBit(1)); + B0.setState(bits.getLSBit(0)); + } + + @Override + public void setCarryIn(String val_1_bit) + { + Cn.setState(Bit.parse(val_1_bit)); + } + + @Override + public void setNotOutEnable(String val_1_bit) + { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public void setD(String val_4_bit) + { + var bits = BitVector.parse(val_4_bit); + D4.setState(bits.getLSBit(3)); + D3.setState(bits.getLSBit(2)); + D2.setState(bits.getLSBit(1)); + D1.setState(bits.getLSBit(0)); + } + + @Override + public void setQ_0(String val_1_bit) + { + IQn.setState(Bit.parse(val_1_bit)); + } + + @Override + public void setQ_3(String val_1_bit) + { + IQn_3.setState(Bit.parse(val_1_bit)); + } + + @Override + public void setRAM_0(String val_1_bit) + { + IRAMn.setState(Bit.parse(val_1_bit)); + } + + @Override + public void setRAM_3(String val_1_bit) + { + IRAMn_3.setState(Bit.parse(val_1_bit)); + } + + @Override + public void clockOn(boolean isClockOn) + { + C.setState(isClockOn ? Bit.ONE : Bit.ZERO); + } + + @Override + public String getQ_0() + { + return OQn.getDisplayedValue().toString(); + } + + @Override + public String getQ_3() + { + return OQn_3.getDisplayedValue().toString(); + } + + @Override + public String getRAM_0() + { + return ORAMn.getDisplayedValue().toString(); + } + + @Override + public String getRAM_3() + { + return ORAMn_3.getDisplayedValue().toString(); + } + + @Override + public String getNotP() + { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public String getNotG() + { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public String getCarryOut() + { + return Cn_4.getDisplayedValue().toString(); + } + + @Override + public String getSign() + { + return F3.getDisplayedValue().toString(); + } + + @Override + public String getZero() + { + return F_0.getDisplayedValue().toString(); + } + + @Override + public String getOverflow() + { + return OVR.getDisplayedValue().toString(); + } + + @Override + public String getY() + { + var y3 = Y4.getDisplayedValue(); + var y2 = Y3.getDisplayedValue(); + var y1 = Y2.getDisplayedValue(); + var y0 = Y1.getDisplayedValue(); + return y3.concat(y2).concat(y1).concat(y0).toString(); + } + + @Override + public void setDirectly(Register r, String val_4_bit) + { + am2901.setHighLevelState(regToStateID(r), BitVector.parse(val_4_bit)); + } + + @Override + public String getDirectly(Register r) + { + return ((BitVector) am2901.getHighLevelState(regToStateID(r))).toString(); + } + + private static String regToStateID(Register r) + { + if (r == Register.Q) + return "qreg.q"; + return "regs.c" + r.toBitString() + ".q"; + } +}