WireArrayEnd now created with U and GUITest supports U
[Mograsim.git] / era.mi / src / era / mi / logic / wires / WireArray.java
1 package era.mi.logic.wires;\r
2 \r
3 import java.io.Closeable;\r
4 import java.util.ArrayList;\r
5 import java.util.Arrays;\r
6 import java.util.Iterator;\r
7 import java.util.List;\r
8 \r
9 import era.mi.logic.Bit;\r
10 import era.mi.logic.Simulation;\r
11 import era.mi.logic.Util;\r
12 \r
13 /**\r
14  * Represents an array of wires that can store n bits of information.\r
15  * \r
16  * @author Fabian Stemmler\r
17  *\r
18  */\r
19 public class WireArray\r
20 {\r
21         private Bit[] values;\r
22         public final int travelTime;\r
23         private List<WireArrayObserver> observers = new ArrayList<WireArrayObserver>();\r
24         public final int length;\r
25         private List<WireArrayEnd> inputs = new ArrayList<WireArrayEnd>();\r
26 \r
27         public WireArray(int length, int travelTime)\r
28         {\r
29                 if (length < 1)\r
30                         throw new IllegalArgumentException(\r
31                                         String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length));\r
32                 this.length = length;\r
33                 this.travelTime = travelTime;\r
34                 initValues();\r
35         }\r
36 \r
37         private void initValues()\r
38         {\r
39                 values = Bit.U.makeArray(length);\r
40         }\r
41 \r
42         private void recalculateSingleInput()\r
43         {\r
44                 WireArrayEnd input = inputs.get(0);\r
45                 if (!Arrays.equals(input.getValues(), values))\r
46                 {\r
47                         Bit[] oldValues = values.clone();\r
48                         System.arraycopy(input.getValues(), 0, values, 0, length);\r
49                         notifyObservers(oldValues);\r
50                 }\r
51         }\r
52 \r
53         private void recalculateMultipleInputs()\r
54         {\r
55                 Iterator<WireArrayEnd> it = inputs.iterator();\r
56                 Bit[] newValues = it.next().inputValues.clone();\r
57 \r
58                 while (it.hasNext())\r
59                 {\r
60                         WireArrayEnd input = it.next();\r
61                         Bit[] bits = input.getValues();\r
62                         for (int i = 0; i < length; i++)\r
63                         {\r
64                                 if (Bit.Z.equals(bits[i]) || newValues[i].equals(bits[i]))\r
65                                         continue;\r
66                                 else if (Bit.Z.equals(newValues[i]))\r
67                                         newValues[i] = bits[i];\r
68                                 else\r
69                                         newValues[i] = Bit.X;\r
70                         }\r
71                 }\r
72 \r
73                 if (!Arrays.equals(newValues, values))\r
74                 {\r
75                         Bit[] oldValues = values;\r
76                         values = newValues;\r
77                         notifyObservers(oldValues);\r
78                 }\r
79         }\r
80 \r
81         private void recalculate()\r
82         {\r
83                 switch (inputs.size())\r
84                 {\r
85                 case 0:\r
86                         return;\r
87                 case 1:\r
88                         recalculateSingleInput();\r
89                         break;\r
90                 default:\r
91                         recalculateMultipleInputs();\r
92                 }\r
93         }\r
94 \r
95         /**\r
96          * The WireArray is interpreted as an unsigned integer with n bits.\r
97          * \r
98          * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the same\r
99          *         value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.\r
100          * \r
101          * @author Fabian Stemmler\r
102          */\r
103         public boolean hasNumericValue()\r
104         {\r
105                 for (Bit b : values)\r
106                 {\r
107                         if (b != Bit.ZERO && b != Bit.ONE)\r
108                                 return false;\r
109                 }\r
110                 return true;\r
111         }\r
112 \r
113         /**\r
114          * The WireArray is interpreted as an unsigned integer with n bits.\r
115          * \r
116          * @return The unsigned value of the {@link WireArray}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
117          * \r
118          * @author Fabian Stemmler\r
119          */\r
120         public long getUnsignedValue()\r
121         {\r
122                 long val = 0;\r
123                 long mask = 1;\r
124                 for (int i = 0; i < length; i++)\r
125                 {\r
126                         switch (values[i])\r
127                         {\r
128                         default:\r
129                         case Z:\r
130                         case X:\r
131                                 return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;\r
132                         // Random number?\r
133                         case ONE:\r
134                                 val |= mask;\r
135                                 break;\r
136                         case ZERO:\r
137                         }\r
138                         mask = mask << 1;\r
139                 }\r
140                 return val;\r
141         }\r
142 \r
143         /**\r
144          * The WireArray is interpreted as a signed integer with n bits.\r
145          * \r
146          * @return The signed value of the {@link WireArray}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
147          * \r
148          * @author Fabian Stemmler\r
149          */\r
150         public long getSignedValue()\r
151         {\r
152                 long val = getUnsignedValue();\r
153                 long mask = 1 << (length - 1);\r
154                 if ((mask & val) != 0)\r
155                 {\r
156                         int shifts = 64 - length;\r
157                         return (val << shifts) >> shifts;\r
158                 }\r
159                 return val;\r
160         }\r
161 \r
162         /**\r
163          * Included for convenient use on {@link WireArray}s of length 1.\r
164          * \r
165          * @return The value of bit 0.\r
166          * \r
167          * @author Fabian Stemmler\r
168          */\r
169         public Bit getValue()\r
170         {\r
171                 return getValue(0);\r
172         }\r
173 \r
174         /**\r
175          * \r
176          * @param index Index of the requested bit.\r
177          * @return The value of the indexed bit.\r
178          * \r
179          * @author Fabian Stemmler\r
180          */\r
181         public Bit getValue(int index)\r
182         {\r
183                 return values[index];\r
184         }\r
185 \r
186         public Bit[] getValues(int start, int end)\r
187         {\r
188                 int length = end - start;\r
189                 Bit[] bits = new Bit[length];\r
190                 System.arraycopy(values, start, bits, 0, length);\r
191                 return bits;\r
192         }\r
193 \r
194         /**\r
195          * @return An array of length n containing the values of the n bits in the {@link WireArray}. Can be safely modified.\r
196          * \r
197          * @author Fabian Stemmler\r
198          */\r
199         public Bit[] getValues()\r
200         {\r
201                 return values.clone();\r
202         }\r
203 \r
204         /**\r
205          * Adds an {@link WireArrayObserver}, who will be notified when the value of the {@link WireArray} is updated.\r
206          * \r
207          * @param ob The {@link WireArrayObserver} to be notified of changes.\r
208          * @return true if the given {@link WireArrayObserver} was not already registered, false otherwise\r
209          * \r
210          * @author Fabian Stemmler\r
211          */\r
212         public boolean addObserver(WireArrayObserver ob)\r
213         {\r
214                 return observers.add(ob);\r
215         }\r
216 \r
217         private void notifyObservers(Bit[] oldValues)\r
218         {\r
219                 for (WireArrayObserver o : observers)\r
220                         o.update(this, oldValues);\r
221         }\r
222 \r
223         /**\r
224          * Create and register a {@link WireArrayEnd} object, which is tied to this {@link WireArray}.\r
225          */\r
226         public WireArrayEnd createInput()\r
227         {\r
228                 return new WireArrayEnd(this);\r
229         }\r
230 \r
231         private void registerInput(WireArrayEnd toRegister)\r
232         {\r
233                 inputs.add(toRegister);\r
234         }\r
235 \r
236         /**\r
237          * A {@link WireArrayEnd} feeds a constant signal into the {@link WireArray} it is tied to. The combination of all inputs determines the\r
238          * {@link WireArray}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than\r
239          * Z 0 and 1 turn into X when they are mixed\r
240          * \r
241          * @author Fabian Stemmler\r
242          */\r
243         public class WireArrayEnd implements Closeable\r
244         {\r
245                 public final WireArray owner;\r
246                 private boolean open;\r
247                 private Bit[] inputValues;\r
248 \r
249                 private WireArrayEnd(WireArray owner)\r
250                 {\r
251                         super();\r
252                         this.owner = owner;\r
253                         open = true;\r
254                         initValues();\r
255                         owner.registerInput(this);\r
256                 }\r
257 \r
258                 private void initValues()\r
259                 {\r
260                         inputValues = Bit.U.makeArray(length);\r
261                 }\r
262 \r
263                 /**\r
264                  * Sets the wires values. This takes up time, as specified by the {@link WireArray}s travel time.\r
265                  * \r
266                  * @param newValues The new values the wires should take on.\r
267                  * \r
268                  * @author Fabian Stemmler\r
269                  */\r
270                 public void feedSignals(Bit... newValues)\r
271                 {\r
272                         if (newValues.length == length)\r
273                         {\r
274                                 feedSignals(0, newValues);\r
275                         } else\r
276                                 throw new IllegalArgumentException(\r
277                                                 String.format("Attempted to input %d bits instead of %d bits.", newValues.length, length));\r
278                 }\r
279 \r
280                 /**\r
281                  * Sets values of a subarray of wires. This takes up time, as specified by the {@link WireArray}s travel time.\r
282                  * \r
283                  * @param newValues   The new values the wires should take on.\r
284                  * @param startingBit The first index of the subarray of wires.\r
285                  * \r
286                  * @author Fabian Stemmler\r
287                  */\r
288                 public void feedSignals(int startingBit, Bit... newValues)\r
289                 {\r
290                         if (!open)\r
291                                 throw new RuntimeException("Attempted to write to closed WireArrayEnd.");\r
292                         Simulation.TIMELINE.addEvent((e) -> setValues(startingBit, newValues), travelTime);\r
293                 }\r
294 \r
295                 private void setValues(int startingBit, Bit... newValues)\r
296                 {\r
297                         int exclLastIndex = startingBit + newValues.length;\r
298                         if (length < exclLastIndex)\r
299                                 throw new ArrayIndexOutOfBoundsException(\r
300                                                 String.format("Attempted to input bits from index %d to %d when there are only %d wires.", startingBit,\r
301                                                                 exclLastIndex - 1, length));\r
302                         if (!Arrays.equals(inputValues, startingBit, exclLastIndex, newValues, 0, newValues.length))\r
303                         {\r
304                                 System.arraycopy(newValues, 0, inputValues, startingBit, newValues.length);\r
305                                 owner.recalculate();\r
306                         }\r
307                 }\r
308 \r
309                 /**\r
310                  * Returns a copy (safe to modify) of the values the {@link WireArrayEnd} is currently feeding into the associated\r
311                  * {@link WireArray}.\r
312                  */\r
313                 public Bit[] getValues()\r
314                 {\r
315                         return inputValues.clone();\r
316                 }\r
317 \r
318                 /**\r
319                  * {@link WireArrayEnd} now feeds Z into the associated {@link WireArray}.\r
320                  */\r
321                 public void clearSignals()\r
322                 {\r
323                         feedSignals(Bit.Z.makeArray(length));\r
324                 }\r
325 \r
326                 public Bit[] wireValuesExcludingMe()\r
327                 {\r
328                         Bit[] bits = Bit.Z.makeArray(length);\r
329                         for (WireArrayEnd wai : inputs)\r
330                         {\r
331                                 if (wai == this)\r
332                                         continue;\r
333                                 Util.combineInto(bits, wai.getValues());\r
334                         }\r
335                         return bits;\r
336                 }\r
337 \r
338                 public Bit getWireValue()\r
339                 {\r
340                         return owner.getValue();\r
341                 }\r
342 \r
343                 public Bit[] getWireValues()\r
344                 {\r
345                         return owner.getValues();\r
346                 }\r
347 \r
348                 @Override\r
349                 public String toString()\r
350                 {\r
351                         return Arrays.toString(inputValues);\r
352                 }\r
353 \r
354                 @Override\r
355                 public void close()\r
356                 {\r
357                         inputs.remove(this);\r
358                         open = false;\r
359                 }\r
360         }\r
361 \r
362         @Override\r
363         public String toString()\r
364         {\r
365                 return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), Arrays.toString(values), inputs);\r
366         }\r
367 \r
368         public static WireArrayEnd[] extractInputs(WireArray[] w)\r
369         {\r
370                 WireArrayEnd[] inputs = new WireArrayEnd[w.length];\r
371                 for (int i = 0; i < w.length; i++)\r
372                         inputs[i] = w[i].createInput();\r
373                 return inputs;\r
374         }\r
375 }