89fcb80d8e5eb28dd4eaa024dd408b68e4802743
[Mograsim.git] / net.mograsim.logic.core / src / net / mograsim / logic / core / wires / Wire.java
1 package net.mograsim.logic.core.wires;\r
2 \r
3 import static net.mograsim.logic.core.types.Bit.U;\r
4 import static net.mograsim.logic.core.types.Bit.Z;\r
5 \r
6 import java.util.ArrayList;\r
7 import java.util.List;\r
8 \r
9 import net.mograsim.logic.core.LogicObservable;\r
10 import net.mograsim.logic.core.LogicObserver;\r
11 import net.mograsim.logic.core.timeline.Timeline;\r
12 import net.mograsim.logic.core.types.Bit;\r
13 import net.mograsim.logic.core.types.BitVector;\r
14 import net.mograsim.logic.core.types.BitVector.BitVectorMutator;\r
15 \r
16 /**\r
17  * Represents an array of wires that can store n bits of information.\r
18  * \r
19  * @author Fabian Stemmler\r
20  *\r
21  */\r
22 public class Wire\r
23 {\r
24         private BitVector values;\r
25         public final int travelTime;\r
26         private List<ReadEnd> attached = new ArrayList<>();\r
27         public final int length;\r
28         List<ReadWriteEnd> inputs = new ArrayList<>();\r
29         Timeline timeline;\r
30 \r
31         public Wire(Timeline timeline, int length, int travelTime)\r
32         {\r
33                 if (length < 1)\r
34                         throw new IllegalArgumentException(\r
35                                         String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length));\r
36                 this.timeline = timeline;\r
37                 this.length = length;\r
38                 this.travelTime = travelTime;\r
39                 initValues();\r
40         }\r
41 \r
42         private void initValues()\r
43         {\r
44                 values = U.toVector(length);\r
45         }\r
46 \r
47         private void recalculateSingleInput()\r
48         {\r
49                 setNewValues(inputs.get(0).getInputValues());\r
50         }\r
51 \r
52         private void recalculateMultipleInputs()\r
53         {\r
54                 BitVectorMutator mutator = BitVectorMutator.empty();\r
55                 for (ReadWriteEnd wireArrayEnd : inputs)\r
56                         mutator.join(wireArrayEnd.getInputValues());\r
57                 setNewValues(mutator.get());\r
58         }\r
59 \r
60         private void setNewValues(BitVector newValues)\r
61         {\r
62                 if (values.equals(newValues))\r
63                         return;\r
64 //              BitVector oldValues = values;\r
65                 values = newValues;\r
66                 notifyObservers();\r
67         }\r
68 \r
69         void recalculate()\r
70         {\r
71                 switch (inputs.size())\r
72                 {\r
73                 case 0:\r
74                         return;\r
75                 case 1:\r
76                         recalculateSingleInput();\r
77                         break;\r
78                 default:\r
79                         recalculateMultipleInputs();\r
80                 }\r
81         }\r
82 \r
83         /**\r
84          * The {@link Wire} is interpreted as an unsigned integer with n bits.\r
85          * \r
86          * @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
87          *         value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.\r
88          * \r
89          * @author Fabian Stemmler\r
90          */\r
91         public boolean hasNumericValue()\r
92         {\r
93                 for (Bit b : values)\r
94                 {\r
95                         if (b != Bit.ZERO && b != Bit.ONE)\r
96                                 return false;\r
97                 }\r
98                 return true;\r
99         }\r
100 \r
101         /**\r
102          * The {@link Wire} is interpreted as an unsigned integer with n bits.\r
103          * \r
104          * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
105          * \r
106          * @author Fabian Stemmler\r
107          */\r
108         public long getUnsignedValue()\r
109         {\r
110                 long val = 0;\r
111                 long mask = 1;\r
112                 for (Bit bit : values)\r
113                 {\r
114                         switch (bit)\r
115                         {\r
116                         default:\r
117                         case Z:\r
118                         case X:\r
119                                 return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;\r
120                         case ONE:\r
121                                 val |= mask;\r
122                                 break;\r
123                         case ZERO:\r
124                         }\r
125                         mask = mask << 1;\r
126                 }\r
127                 return val;\r
128         }\r
129 \r
130         /**\r
131          * The {@link Wire} is interpreted as a signed integer with n bits.\r
132          * \r
133          * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
134          * \r
135          * @author Fabian Stemmler\r
136          */\r
137         public long getSignedValue()\r
138         {\r
139                 long val = getUnsignedValue();\r
140                 long mask = 1 << (length - 1);\r
141                 if ((mask & val) != 0)\r
142                 {\r
143                         int shifts = 64 - length;\r
144                         return (val << shifts) >> shifts;\r
145                 }\r
146                 return val;\r
147         }\r
148 \r
149         public Bit getValue()\r
150         {\r
151                 return getValue(0);\r
152         }\r
153 \r
154         public Bit getValue(int index)\r
155         {\r
156                 return values.getBit(index);\r
157         }\r
158 \r
159         public BitVector getValues(int start, int end)\r
160         {\r
161                 return values.subVector(start, end);\r
162         }\r
163 \r
164         public BitVector getValues()\r
165         {\r
166                 return values;\r
167         }\r
168 \r
169         /**\r
170          * Adds an {@link LogicObserver}, who will be notified when the value of the {@link Wire} is updated.\r
171          * \r
172          * @param ob The {@link LogicObserver} to be notified of changes.\r
173          * @return true if the given {@link LogicObserver} was not already registered, false otherwise\r
174          * \r
175          * @author Fabian Stemmler\r
176          */\r
177         void attachEnd(ReadEnd end)\r
178         {\r
179                 attached.add(end);\r
180         }\r
181 \r
182         void detachEnd(ReadEnd end)\r
183         {\r
184                 attached.remove(end);\r
185         }\r
186 \r
187         private void notifyObservers()\r
188         {\r
189                 attached.forEach(r -> r.update());\r
190         }\r
191 \r
192         /**\r
193          * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link Wire}. This {@link ReadWriteEnd} can be written to.\r
194          */\r
195         public ReadWriteEnd createReadWriteEnd()\r
196         {\r
197                 return new ReadWriteEnd();\r
198         }\r
199 \r
200         /**\r
201          * Create a {@link ReadEnd} object, which is tied to this {@link Wire}. This {@link ReadEnd} cannot be written to.\r
202          */\r
203         public ReadEnd createReadOnlyEnd()\r
204         {\r
205                 return new ReadEnd();\r
206         }\r
207 \r
208         void registerInput(ReadWriteEnd toRegister)\r
209         {\r
210                 inputs.add(toRegister);\r
211         }\r
212 \r
213         /**\r
214          * A {@link ReadEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the\r
215          * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0\r
216          * and 1 turn into X when they are mixed\r
217          * \r
218          * @author Fabian Stemmler\r
219          */\r
220         public class ReadEnd implements LogicObservable\r
221         {\r
222                 private List<LogicObserver> observers = new ArrayList<>();\r
223 \r
224                 ReadEnd()\r
225                 {\r
226                         super();\r
227                         Wire.this.attachEnd(this);\r
228                 }\r
229 \r
230                 public void update()\r
231                 {\r
232                         notifyObservers();\r
233                 }\r
234 \r
235                 /**\r
236                  * Included for convenient use on {@link Wire}s of length 1.\r
237                  * \r
238                  * @return The value of bit 0.\r
239                  * \r
240                  * @author Fabian Stemmler\r
241                  */\r
242                 public Bit getValue()\r
243                 {\r
244                         return Wire.this.getValue();\r
245                 }\r
246 \r
247                 /**\r
248                  * @param index Index of the requested bit.\r
249                  * @return The value of the indexed bit.\r
250                  * \r
251                  * @author Fabian Stemmler\r
252                  */\r
253                 public Bit getValue(int index)\r
254                 {\r
255                         return Wire.this.getValue(index);\r
256                 }\r
257 \r
258                 /**\r
259                  * @param index Index of the requested bit.\r
260                  * @return The value of the indexed bit.\r
261                  * \r
262                  * @author Fabian Stemmler\r
263                  */\r
264                 public BitVector getValues()\r
265                 {\r
266                         return Wire.this.getValues();\r
267                 }\r
268 \r
269                 /**\r
270                  * @param start Start of the wanted segment. (inclusive)\r
271                  * @param end   End of the wanted segment. (exclusive)\r
272                  * @return The values of the segment of {@link Bit}s indexed.\r
273                  * \r
274                  * @author Fabian Stemmler\r
275                  */\r
276                 public BitVector getValues(int start, int end)\r
277                 {\r
278                         return Wire.this.getValues(start, end);\r
279                 }\r
280 \r
281                 /**\r
282                  * The {@link Wire} is interpreted as an unsigned integer with n bits.\r
283                  * \r
284                  * @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\r
285                  *         same value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.\r
286                  * \r
287                  * @author Fabian Stemmler\r
288                  */\r
289                 public boolean hasNumericValue()\r
290                 {\r
291                         return Wire.this.hasNumericValue();\r
292                 }\r
293 \r
294                 /**\r
295                  * The {@link Wire} is interpreted as an unsigned integer with n bits.\r
296                  * \r
297                  * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
298                  * \r
299                  * @author Fabian Stemmler\r
300                  */\r
301                 public long getUnsignedValue()\r
302                 {\r
303                         return Wire.this.getUnsignedValue();\r
304                 }\r
305 \r
306                 /**\r
307                  * The {@link Wire} is interpreted as a signed integer with n bits.\r
308                  * \r
309                  * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
310                  * \r
311                  * @author Fabian Stemmler\r
312                  */\r
313                 public long getSignedValue()\r
314                 {\r
315                         return Wire.this.getSignedValue();\r
316                 }\r
317 \r
318                 @Override\r
319                 public String toString()\r
320                 {\r
321                         return Wire.this.toString();\r
322                 }\r
323 \r
324                 public void close()\r
325                 {\r
326                         inputs.remove(this);\r
327                         detachEnd(this);\r
328                         recalculate();\r
329                 }\r
330 \r
331                 public int length()\r
332                 {\r
333                         return length;\r
334                 }\r
335 \r
336                 public Wire getWire()\r
337                 {\r
338                         return Wire.this;\r
339                 }\r
340 \r
341                 @Override\r
342                 public void registerObserver(LogicObserver ob)\r
343                 {\r
344                         observers.add(ob);\r
345                 }\r
346 \r
347                 @Override\r
348                 public void deregisterObserver(LogicObserver ob)\r
349                 {\r
350                         observers.remove(ob);\r
351                 }\r
352 \r
353                 @Override\r
354                 public void notifyObservers()\r
355                 {\r
356                         observers.forEach(ob -> ob.update(this));\r
357                 }\r
358         }\r
359 \r
360         public class ReadWriteEnd extends ReadEnd\r
361         {\r
362                 private boolean open;\r
363                 private BitVector inputValues;\r
364 \r
365                 ReadWriteEnd()\r
366                 {\r
367                         super();\r
368                         open = true;\r
369                         initValues();\r
370                         registerInput(this);\r
371                 }\r
372 \r
373                 private void initValues()\r
374                 {\r
375                         inputValues = U.toVector(length);\r
376                 }\r
377 \r
378                 /**\r
379                  * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.\r
380                  * \r
381                  * @param newValues The new values the wires should take on.\r
382                  * \r
383                  * @author Fabian Stemmler\r
384                  */\r
385                 public void feedSignals(Bit... newValues)\r
386                 {\r
387                         feedSignals(BitVector.of(newValues));\r
388                 }\r
389 \r
390                 public void feedSignals(BitVector newValues)\r
391                 {\r
392                         if (newValues.length() != length)\r
393                                 throw new IllegalArgumentException(\r
394                                                 String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length));\r
395                         if (!open)\r
396                                 throw new RuntimeException("Attempted to write to closed WireArrayEnd.");\r
397                         timeline.addEvent(e -> setValues(newValues), travelTime);\r
398                 }\r
399 \r
400                 /**\r
401                  * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.\r
402                  * \r
403                  * @param bitVector   The new values the wires should take on.\r
404                  * @param startingBit The first index of the subarray of wires.\r
405                  * \r
406                  * @author Fabian Stemmler\r
407                  */\r
408                 public void feedSignals(int startingBit, BitVector bitVector)\r
409                 {\r
410                         if (!open)\r
411                                 throw new RuntimeException("Attempted to write to closed WireArrayEnd.");\r
412                         timeline.addEvent(e -> setValues(startingBit, bitVector), travelTime);\r
413                 }\r
414 \r
415                 private void setValues(int startingBit, BitVector newValues)\r
416                 {\r
417                         // index check covered in equals\r
418                         if (!inputValues.equalsWithOffset(newValues, startingBit))\r
419                         {\r
420                                 Bit[] vals = inputValues.getBits();\r
421                                 System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());\r
422                                 inputValues = BitVector.of(vals);\r
423                                 Wire.this.recalculate();\r
424                         }\r
425                 }\r
426 \r
427                 private void setValues(BitVector newValues)\r
428                 {\r
429                         if (inputValues.equals(newValues))\r
430                                 return;\r
431                         inputValues = newValues;\r
432                         Wire.this.recalculate();\r
433                 }\r
434 \r
435                 /**\r
436                  * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link Wire}.\r
437                  */\r
438                 public Bit getInputValue()\r
439                 {\r
440                         return getInputValue(0);\r
441                 }\r
442 \r
443                 /**\r
444                  * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.\r
445                  */\r
446                 public Bit getInputValue(int index)\r
447                 {\r
448                         return inputValues.getBit(index);\r
449                 }\r
450 \r
451                 /**\r
452                  * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link Wire}.\r
453                  */\r
454                 public BitVector getInputValues()\r
455                 {\r
456                         return getInputValues(0, length);\r
457                 }\r
458 \r
459                 public BitVector getInputValues(int start, int end)\r
460                 {\r
461                         return inputValues.subVector(start, end);\r
462                 }\r
463 \r
464                 /**\r
465                  * {@link ReadEnd} now feeds Z into the associated {@link Wire}.\r
466                  */\r
467                 public void clearSignals()\r
468                 {\r
469                         feedSignals(Z.toVector(length));\r
470                 }\r
471 \r
472                 public BitVector wireValuesExcludingMe()\r
473                 {\r
474                         BitVectorMutator mutator = BitVectorMutator.empty();\r
475                         for (ReadWriteEnd wireEnd : inputs)\r
476                         {\r
477                                 if (wireEnd == this)\r
478                                         continue;\r
479                                 mutator.join(wireEnd.inputValues);\r
480                         }\r
481                         return mutator.get();\r
482                 }\r
483 \r
484                 @Override\r
485                 public String toString()\r
486                 {\r
487                         return inputValues.toString();\r
488                 }\r
489         }\r
490 \r
491         @Override\r
492         public String toString()\r
493         {\r
494                 return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs);\r
495         }\r
496 \r
497         public static ReadEnd[] extractEnds(Wire[] w)\r
498         {\r
499                 ReadEnd[] inputs = new ReadEnd[w.length];\r
500                 for (int i = 0; i < w.length; i++)\r
501                         inputs[i] = w[i].createReadWriteEnd();\r
502                 return inputs;\r
503         }\r
504 }