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