Added TestGCD
[Mograsim.git] / tests / net.mograsim.logic.model.am2900.tests / src / net / mograsim / logic / model / am2900 / TestGCD.java
1 package net.mograsim.logic.model.am2900;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4
5 import java.io.IOException;
6 import java.util.concurrent.ThreadLocalRandom;
7 import java.util.concurrent.atomic.AtomicInteger;
8 import java.util.concurrent.atomic.AtomicReference;
9 import java.util.stream.IntStream;
10
11 import org.junit.jupiter.api.AfterAll;
12 import org.junit.jupiter.api.BeforeAll;
13 import org.junit.jupiter.api.Test;
14 import org.junit.jupiter.api.TestInstance;
15 import org.junit.jupiter.api.TestInstance.Lifecycle;
16 import org.junit.jupiter.params.ParameterizedTest;
17 import org.junit.jupiter.params.provider.MethodSource;
18
19 import net.mograsim.logic.core.types.BitVector;
20 import net.mograsim.logic.model.LogicUIStandaloneGUI;
21 import net.mograsim.logic.model.am2900.machine.Am2900Machine;
22 import net.mograsim.logic.model.am2900.machine.Am2900MainMemoryDefinition;
23 import net.mograsim.logic.model.am2900.machine.Am2900MicroInstructionDefinition;
24 import net.mograsim.logic.model.am2900.machine.Am2900MicroInstructionMemoryDefinition;
25 import net.mograsim.logic.model.am2900.machine.StrictAm2900MachineDefinition;
26 import net.mograsim.logic.model.am2900.machine.registers.am2901.NumberedRegister;
27 import net.mograsim.machine.MainMemory;
28 import net.mograsim.machine.mi.MicroInstruction;
29 import net.mograsim.machine.mi.MicroInstructionDefinition;
30 import net.mograsim.machine.mi.MicroInstructionMemory;
31 import net.mograsim.machine.mi.MicroInstructionMemoryParser;
32 import net.mograsim.machine.mi.StandardMicroInstructionMemory;
33 import net.mograsim.machine.mi.parameters.MicroInstructionParameter;
34 import net.mograsim.machine.mi.parameters.ParameterClassification;
35 import net.mograsim.machine.standard.memory.WordAddressableMemory;
36
37 @TestInstance(Lifecycle.PER_CLASS)
38 public class TestGCD
39 {
40         private static final boolean startGUI = false;
41
42         private Am2900Machine mach;
43         private MicroInstructionMemory mpm;
44         private MainMemory ram;
45         private AtomicInteger machState;
46         private AtomicInteger halfCycleCount;
47         private AtomicReference<MicroInstructionMemory> mpmToSetNextCycle;
48
49         @BeforeAll
50         public void setupMachine() throws IOException
51         {
52                 Am2900Loader.setup();
53                 mach = new StrictAm2900MachineDefinition().createNew();
54                 mpm = MicroInstructionMemoryParser.parseMemory(Am2900MicroInstructionMemoryDefinition.instance,
55                                 TestGCD.class.getResourceAsStream("gcd.mpm"));
56                 ram = new WordAddressableMemory(Am2900MainMemoryDefinition.instance);
57                 mach.getMainMemory().bind(ram);
58
59                 if (startGUI)
60                         startGUI();
61
62                 machState = new AtomicInteger(2);
63                 halfCycleCount = new AtomicInteger();
64                 // needed for avoiding hazard loops in the first muIR
65                 mpmToSetNextCycle = new AtomicReference<>();
66                 mach.getClock().registerObserver(c ->
67                 {
68                         halfCycleCount.addAndGet(1);
69                         synchronized (machState)
70                         {
71                                 long muPC = mach.getActiveMicroInstructionAddress();
72                                 if (muPC == 0)
73                                 {
74                                         machState.set(0);
75                                         machState.notify();
76                                 } else if (muPC == 0x12)
77                                 {
78                                         machState.set(1);
79                                         machState.notify();
80                                 }
81                                 if (!mach.getClock().isOn())
82                                 {
83                                         MicroInstructionMemory mpmToSet = mpmToSetNextCycle.getAndSet(null);
84                                         if (mpmToSet != null)
85                                                 mach.getMicroInstructionMemory().bind(mpmToSet);
86                                 }
87                         }
88                 });
89
90                 mach.reset();
91
92                 Thread execT = new Thread(mach.getTimeline()::executeAll, "Logic executer");
93                 execT.setDaemon(true);
94                 execT.start();
95
96                 System.out.println("Machine initialized");
97
98         }
99
100         private void startGUI()
101         {
102                 new Thread(() -> new LogicUIStandaloneGUI(mach.getModel()).run(), "GUI thread").start();
103         }
104
105         @Test
106         public void testGCDHardcodedCases() throws InterruptedException
107         {
108                 checkGCD(4, 12);
109                 checkGCD(4, 13);
110                 checkGCD(0, 3);
111                 checkGCD(4, 0);
112                 checkGCD(0, 0);
113                 checkGCD(48820, 8480);
114                 checkGCD(21420, 11288);
115                 checkGCD(15862, 21219);
116         }
117
118         @ParameterizedTest
119         @MethodSource("generateRandomInts")
120         public void testGCDRandomCases(int i) throws InterruptedException
121         {
122                 int a = i & 0xFFFF;
123                 int b = (i >>> 16);
124                 checkGCD(a, b);
125         }
126
127         public static IntStream generateRandomInts()
128         {
129                 return ThreadLocalRandom.current().ints(10);
130         }
131
132         private void checkGCD(int euclidA, int euclidB) throws InterruptedException
133         {
134                 int exp = gcd(euclidA, euclidB);
135                 System.out.println("Checking gcd(" + euclidA + ", " + euclidB + ") + (expected " + exp + ")");
136                 int act = executeGCD(euclidA, euclidB);
137                 assertEquals(exp, act);
138         }
139
140         private static int gcd(int a, int b)
141         {
142                 if (b == 0)
143                         return a;
144                 return gcd(b, a % b);
145         }
146
147         private int executeGCD(int euclidA, int euclidB) throws InterruptedException
148         {
149                 ram.setCell(0, BitVector.from(euclidA, 16));
150                 ram.setCell(1, BitVector.from(euclidB, 16));
151
152                 resetMachine();
153
154                 synchronized (machState)
155                 {
156                         while (machState.get() != 1)
157                                 machState.wait();
158                 }
159
160                 BitVector result = mach.getRegister(NumberedRegister.instancesCorrectOrder.get(1));
161                 return result.isBinary() ? (int) result.getUnsignedValueLong() : -1;
162         }
163
164         private void resetMachine() throws InterruptedException
165         {
166                 MicroInstructionDefinition muiDef = Am2900MicroInstructionDefinition.instance;
167                 ParameterClassification[] paramClassifications = muiDef.getParameterClassifications();
168                 MicroInstructionParameter[] defaultParams = muiDef.createDefaultInstruction().getParameters();
169                 defaultParams[19] = paramClassifications[19].parse("JZ");
170                 MicroInstruction jzMI = MicroInstruction.create(defaultParams);
171
172                 MicroInstructionMemory jzMPM = new StandardMicroInstructionMemory(Am2900MicroInstructionMemoryDefinition.instance);
173                 jzMPM.setCell(0x00, jzMI);
174                 jzMPM.setCell(0x13, jzMI);
175                 mpmToSetNextCycle.set(jzMPM);
176
177                 synchronized (machState)
178                 {
179                         while (machState.get() != 0)
180                                 machState.wait();
181                 }
182
183                 mpmToSetNextCycle.set(mpm);
184         }
185
186         @AfterAll
187         public void printStats()
188         {
189                 System.out.println("Machine executed " + halfCycleCount.get() + " cycles in total (including JZs)");
190         }
191 }