Generalized WireObserver to LogicObserver
[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<ReadEnd>();\r
27         public final int length;\r
28         private List<ReadWriteEnd> inputs = new ArrayList<ReadWriteEnd>();\r
29         private 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(oldValues);\r
67         }\r
68 \r
69         private 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         private void attachEnd(ReadEnd end)\r
178         {\r
179                 attached.add(end);\r
180         }\r
181 \r
182         private void detachEnd(ReadEnd end)\r
183         {\r
184                 attached.remove(end);\r
185         }\r
186 \r
187         private void notifyObservers(BitVector oldValues)\r
188         {\r
189                 for (ReadEnd o : attached)\r
190                         o.update(oldValues);\r
191         }\r
192 \r
193         /**\r
194          * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link Wire}. This {@link ReadWriteEnd} can be written to.\r
195          */\r
196         public ReadWriteEnd createReadWriteEnd()\r
197         {\r
198                 return new ReadWriteEnd();\r
199         }\r
200 \r
201         /**\r
202          * Create a {@link ReadEnd} object, which is tied to this {@link Wire}. This {@link ReadEnd} cannot be written to.\r
203          */\r
204         public ReadEnd createReadOnlyEnd()\r
205         {\r
206                 return new ReadEnd();\r
207         }\r
208 \r
209         private void registerInput(ReadWriteEnd toRegister)\r
210         {\r
211                 inputs.add(toRegister);\r
212         }\r
213 \r
214         /**\r
215          * A {@link ReadEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the\r
216          * {@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
217          * and 1 turn into X when they are mixed\r
218          * \r
219          * @author Fabian Stemmler\r
220          */\r
221         public class ReadEnd implements LogicObservable\r
222         {\r
223                 private List<LogicObserver> observers = new ArrayList<LogicObserver>();\r
224 \r
225                 private ReadEnd()\r
226                 {\r
227                         super();\r
228                         Wire.this.attachEnd(this);\r
229                 }\r
230 \r
231                 public void update(BitVector oldValues)\r
232                 {\r
233                         notifyObservers();\r
234                 }\r
235 \r
236                 /**\r
237                  * Included for convenient use on {@link Wire}s of length 1.\r
238                  * \r
239                  * @return The value of bit 0.\r
240                  * \r
241                  * @author Fabian Stemmler\r
242                  */\r
243                 public Bit getValue()\r
244                 {\r
245                         return Wire.this.getValue();\r
246                 }\r
247 \r
248                 /**\r
249                  * @param index Index of the requested bit.\r
250                  * @return The value of the indexed bit.\r
251                  * \r
252                  * @author Fabian Stemmler\r
253                  */\r
254                 public Bit getValue(int index)\r
255                 {\r
256                         return Wire.this.getValue(index);\r
257                 }\r
258 \r
259                 /**\r
260                  * @param index Index of the requested bit.\r
261                  * @return The value of the indexed bit.\r
262                  * \r
263                  * @author Fabian Stemmler\r
264                  */\r
265                 public BitVector getValues()\r
266                 {\r
267                         return Wire.this.getValues();\r
268                 }\r
269 \r
270                 /**\r
271                  * @param start Start of the wanted segment. (inclusive)\r
272                  * @param end   End of the wanted segment. (exclusive)\r
273                  * @return The values of the segment of {@link Bit}s indexed.\r
274                  * \r
275                  * @author Fabian Stemmler\r
276                  */\r
277                 public BitVector getValues(int start, int end)\r
278                 {\r
279                         return Wire.this.getValues(start, end);\r
280                 }\r
281 \r
282                 /**\r
283                  * The {@link Wire} is interpreted as an unsigned integer with n bits.\r
284                  * \r
285                  * @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
286                  *         same value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.\r
287                  * \r
288                  * @author Fabian Stemmler\r
289                  */\r
290                 public boolean hasNumericValue()\r
291                 {\r
292                         return Wire.this.hasNumericValue();\r
293                 }\r
294 \r
295                 /**\r
296                  * The {@link Wire} is interpreted as an unsigned integer with n bits.\r
297                  * \r
298                  * @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
299                  * \r
300                  * @author Fabian Stemmler\r
301                  */\r
302                 public long getUnsignedValue()\r
303                 {\r
304                         return Wire.this.getUnsignedValue();\r
305                 }\r
306 \r
307                 /**\r
308                  * The {@link Wire} is interpreted as a signed integer with n bits.\r
309                  * \r
310                  * @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
311                  * \r
312                  * @author Fabian Stemmler\r
313                  */\r
314                 public long getSignedValue()\r
315                 {\r
316                         return Wire.this.getSignedValue();\r
317                 }\r
318 \r
319                 @Override\r
320                 public String toString()\r
321                 {\r
322                         return Wire.this.toString();\r
323                 }\r
324 \r
325                 public void close()\r
326                 {\r
327                         inputs.remove(this);\r
328                         detachEnd(this);\r
329                         recalculate();\r
330                 }\r
331 \r
332                 public int length()\r
333                 {\r
334                         return length;\r
335                 }\r
336 \r
337                 public Wire getWire()\r
338                 {\r
339                         return Wire.this;\r
340                 }\r
341 \r
342                 @Override\r
343                 public void registerObserver(LogicObserver ob)\r
344                 {\r
345                         observers.add(ob);\r
346                 }\r
347 \r
348                 @Override\r
349                 public void notifyObservers()\r
350                 {\r
351                         for (LogicObserver ob : observers)\r
352                                 ob.update(this);\r
353                 }\r
354         }\r
355 \r
356         public class ReadWriteEnd extends ReadEnd\r
357         {\r
358                 private boolean open;\r
359                 private BitVector inputValues;\r
360 \r
361                 private ReadWriteEnd()\r
362                 {\r
363                         super();\r
364                         open = true;\r
365                         initValues();\r
366                         registerInput(this);\r
367                 }\r
368 \r
369                 private void initValues()\r
370                 {\r
371                         inputValues = U.toVector(length);\r
372                 }\r
373 \r
374                 /**\r
375                  * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.\r
376                  * \r
377                  * @param newValues The new values the wires should take on.\r
378                  * \r
379                  * @author Fabian Stemmler\r
380                  */\r
381                 public void feedSignals(Bit... newValues)\r
382                 {\r
383                         feedSignals(BitVector.of(newValues));\r
384                 }\r
385 \r
386                 public void feedSignals(BitVector newValues)\r
387                 {\r
388                         if (newValues.length() != length)\r
389                                 throw new IllegalArgumentException(\r
390                                                 String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length));\r
391                         if (!open)\r
392                                 throw new RuntimeException("Attempted to write to closed WireArrayEnd.");\r
393                         timeline.addEvent(e -> setValues(newValues), travelTime);\r
394                 }\r
395 \r
396                 /**\r
397                  * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.\r
398                  * \r
399                  * @param bitVector   The new values the wires should take on.\r
400                  * @param startingBit The first index of the subarray of wires.\r
401                  * \r
402                  * @author Fabian Stemmler\r
403                  */\r
404                 public void feedSignals(int startingBit, BitVector bitVector)\r
405                 {\r
406                         if (!open)\r
407                                 throw new RuntimeException("Attempted to write to closed WireArrayEnd.");\r
408                         timeline.addEvent(e -> setValues(startingBit, bitVector), travelTime);\r
409                 }\r
410 \r
411                 private void setValues(int startingBit, BitVector newValues)\r
412                 {\r
413                         // index check covered in equals\r
414                         if (!inputValues.equalsWithOffset(newValues, startingBit))\r
415                         {\r
416                                 Bit[] vals = inputValues.getBits();\r
417                                 System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());\r
418                                 inputValues = BitVector.of(vals);\r
419                                 Wire.this.recalculate();\r
420                         }\r
421                 }\r
422 \r
423                 private void setValues(BitVector newValues)\r
424                 {\r
425                         if (inputValues.equals(newValues))\r
426                                 return;\r
427                         inputValues = newValues;\r
428                         Wire.this.recalculate();\r
429                 }\r
430 \r
431                 /**\r
432                  * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link Wire}.\r
433                  */\r
434                 public Bit getInputValue()\r
435                 {\r
436                         return getInputValue(0);\r
437                 }\r
438 \r
439                 /**\r
440                  * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.\r
441                  */\r
442                 public Bit getInputValue(int index)\r
443                 {\r
444                         return inputValues.getBit(index);\r
445                 }\r
446 \r
447                 /**\r
448                  * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link Wire}.\r
449                  */\r
450                 public BitVector getInputValues()\r
451                 {\r
452                         return getInputValues(0, length);\r
453                 }\r
454 \r
455                 public BitVector getInputValues(int start, int end)\r
456                 {\r
457                         return inputValues.subVector(start, end);\r
458                 }\r
459 \r
460                 /**\r
461                  * {@link ReadEnd} now feeds Z into the associated {@link Wire}.\r
462                  */\r
463                 public void clearSignals()\r
464                 {\r
465                         feedSignals(Z.toVector(length));\r
466                 }\r
467 \r
468                 public BitVector wireValuesExcludingMe()\r
469                 {\r
470                         BitVectorMutator mutator = BitVectorMutator.empty();\r
471                         for (ReadWriteEnd wireEnd : inputs)\r
472                         {\r
473                                 if (wireEnd == this)\r
474                                         continue;\r
475                                 mutator.join(wireEnd.inputValues);\r
476                         }\r
477                         return mutator.get();\r
478                 }\r
479 \r
480                 @Override\r
481                 public String toString()\r
482                 {\r
483                         return inputValues.toString();\r
484                 }\r
485         }\r
486 \r
487         @Override\r
488         public String toString()\r
489         {\r
490                 return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs);\r
491         }\r
492 \r
493         public static ReadEnd[] extractEnds(Wire[] w)\r
494         {\r
495                 ReadEnd[] inputs = new ReadEnd[w.length];\r
496                 for (int i = 0; i < w.length; i++)\r
497                         inputs[i] = w[i].createReadWriteEnd();\r
498                 return inputs;\r
499         }\r
500 }