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