1 package net.mograsim.logic.core.wires;
3 import static net.mograsim.logic.core.types.Bit.U;
4 import static net.mograsim.logic.core.types.Bit.Z;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.HashSet;
12 import net.mograsim.logic.core.LogicObservable;
13 import net.mograsim.logic.core.LogicObserver;
14 import net.mograsim.logic.core.timeline.Timeline;
15 import net.mograsim.logic.core.types.Bit;
16 import net.mograsim.logic.core.types.BitVector;
17 import net.mograsim.logic.core.types.BitVector.BitVectorMutator;
20 * Represents an array of wires that can store n bits of information.
22 * @author Fabian Stemmler
27 public final String name;
28 private BitVector cachedValues;
29 public final int travelTime;
30 private List<ReadEnd> attached = new ArrayList<>();
31 public final int width;
32 List<ReadWriteEnd> inputs = new ArrayList<>();
34 private Bit[] bitsWithoutFusions;
37 public CoreWire(Timeline timeline, int width, int travelTime)
39 this(timeline, width, travelTime, null);
42 public CoreWire(Timeline timeline, int width, int travelTime, String name)
45 throw new IllegalArgumentException(
46 String.format("Tried to create an array of wires with width %d, but a width of less than 1 makes no sense.", width));
48 this.timeline = timeline;
50 this.travelTime = travelTime;
54 private void initValues()
56 cachedValues = U.toVector(width);
57 bitsWithoutFusions = cachedValues.getBits();
60 private void setNewValues(BitVector newValues)
62 cachedValues = newValues;
66 private void invalidateCachedValuesForAllFusedWires()
68 invalidateCachedValues();
69 if (fusedBits != null)
70 for (FusedBit fusion : fusedBits)
72 fusion.invalidateCachedValuesForAllParticipatingWires();
75 private void invalidateCachedValues()
81 void recalculateValuesWithoutFusions()
83 Bit[] bits = new Bit[width];
88 System.arraycopy(inputs.get(0).getInputValues().getBits(), 0, bits, 0, width);
89 for (int i = 1; i < inputs.size(); i++)
90 Bit.join(bits, inputs.get(i).getInputValues().getBits());
92 bitsWithoutFusions = bits;
93 if (fusedBits == null)
94 setNewValues(BitVector.of(bits));
96 invalidateCachedValuesForAllFusedWires();
99 private void recalculatedCachedValues()
102 if (fusedBits == null)
103 bits = bitsWithoutFusions;
106 bits = new Bit[width];
107 for (int i = 0; i < width; i++)
109 FusedBit fusion = fusedBits[i];
111 bits[i] = bitsWithoutFusions[i];
113 bits[i] = fusion.getValue();
116 cachedValues = BitVector.of(bits);
120 * Forces a Wire to take on specific values. If the new values differ from the old ones, the observers of the Wire will be notified.
121 * WARNING! Use this with care! The preferred way of writing the values is ReadWriteEnd.feedSignals(BitVector)
123 * @param values The values the <code>Wire</code> will have immediately after this method is called
125 public void forceValues(BitVector values)
127 bitsWithoutFusions = values.getBits();
128 invalidateCachedValuesForAllFusedWires();
133 * Returns the least significant bit (LSB)
135 public Bit getValue()
141 * Returns the least significant bit (LSB) of the given index
143 public Bit getValue(int index)
145 return getValues().getLSBit(index);
148 public BitVector getValues(int start, int end)
150 return getValues().subVector(start, end);
153 public BitVector getValues()
155 if (cachedValues == null)
156 recalculatedCachedValues();
161 * Adds an {@link LogicObserver}, who will be notified when the value of the {@link CoreWire} is updated.
163 * @param ob The {@link LogicObserver} to be notified of changes.
164 * @return true if the given {@link LogicObserver} was not already registered, false otherwise
166 * @author Fabian Stemmler
168 boolean attachEnd(ReadEnd end)
170 return attached.add(end);
173 void detachEnd(ReadEnd end)
175 attached.remove(end);
178 private void notifyObservers()
180 attached.forEach(ReadEnd::update);
184 * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link CoreWire}. This {@link ReadWriteEnd} can be written
187 public ReadWriteEnd createReadWriteEnd()
189 return new ReadWriteEnd();
193 * Create a {@link ReadEnd} object, which is tied to this {@link CoreWire}. This {@link ReadEnd} cannot be written to.
195 public ReadEnd createReadOnlyEnd()
197 return new ReadEnd();
200 void registerInput(ReadWriteEnd toRegister)
202 inputs.add(toRegister);
203 recalculateValuesWithoutFusions();
207 * A {@link ReadEnd} feeds a constant signal into the {@link CoreWire} it is tied to. The combination of all inputs determines the
208 * {@link CoreWire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than
209 * Z 0 and 1 turn into X when they are mixed
211 * @author Fabian Stemmler
213 public class ReadEnd implements LogicObservable
215 private List<LogicObserver> observers = new ArrayList<>();
220 CoreWire.this.attachEnd(this);
229 * Included for convenient use on {@link CoreWire}s of width 1.
231 * @return The value of bit 0.
233 * @author Fabian Stemmler
235 public Bit getValue()
237 return CoreWire.this.getValue();
241 * @param index Index of the requested bit.
242 * @return The value of the indexed bit.
244 * @author Fabian Stemmler
246 public Bit getValue(int index)
248 return CoreWire.this.getValue(index);
251 public BitVector getValues()
253 return CoreWire.this.getValues();
257 * @param start Start of the wanted segment. (inclusive)
258 * @param end End of the wanted segment. (exclusive)
259 * @return The values of the segment of {@link Bit}s indexed.
261 * @author Fabian Stemmler
263 public BitVector getValues(int start, int end)
265 return CoreWire.this.getValues(start, end);
269 public String toString()
271 return CoreWire.this.toString();
278 recalculateValuesWithoutFusions();
286 public CoreWire getWire()
288 return CoreWire.this;
292 public void registerObserver(LogicObserver ob)
298 public void deregisterObserver(LogicObserver ob)
300 observers.remove(ob);
303 // void registerCloseObserver(LogicObserver ob)
305 // closeObserver.add(ob);
308 // void deregisterCloseObserver(LogicObserver ob)
310 // closeObserver.remove(ob);
314 public void notifyObservers()
316 observers.forEach(ob -> ob.update(this));
320 public class ReadWriteEnd extends ReadEnd
322 private boolean open;
323 private boolean isWriting;
324 private BitVector inputValues;
335 private void initValues()
337 inputValues = U.toVector(width);
341 * Sets the wires values. This takes up time, as specified by the {@link CoreWire}s travel time.
343 * @param newValues The new values the wires should take on.
345 * @author Fabian Stemmler
347 public void feedSignals(Bit... newValues)
349 feedSignals(BitVector.of(newValues));
352 // TODO what if this is called multiple times at the same simulation time? (happens in component unit tests)
353 public void feedSignals(BitVector newValues)
355 if (newValues.length() != width)
356 throw new IllegalArgumentException(
357 String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), width));
359 throw new IllegalStateException("Attempted to write to closed WireArrayEnd.");
360 timeline.addEvent(e -> setValues(newValues), travelTime);
364 * Sets values of a subarray of wires. This takes up time, as specified by the {@link CoreWire}s travel time.
366 * @param bitVector The new values the wires should take on.
367 * @param startingBit The first index of the subarray of wires.
369 * @author Fabian Stemmler
371 public void feedSignals(int startingBit, BitVector bitVector)
374 throw new IllegalStateException("Attempted to write to closed WireArrayEnd.");
375 timeline.addEvent(e -> setValues(startingBit, bitVector), travelTime);
379 * Sets the values that are being fed into the {@link CoreWire}. The preferred way of setting {@link ReadWriteEnd} values is via
380 * feedValues(...) with a delay.
382 void setValues(int startingBit, BitVector newValues)
384 // index check covered in equals
385 if (!inputValues.equalsWithOffset(newValues, startingBit))
387 Bit[] vals = inputValues.getBits();
388 System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
389 inputValues = BitVector.of(vals);
390 CoreWire.this.recalculateValuesWithoutFusions();
395 * Sets the values that are being fed into the {@link CoreWire}. The preferred way of setting {@link ReadWriteEnd} values is via
396 * feedValues(...) with a delay.
398 void setValues(BitVector newValues)
400 if (inputValues.equals(newValues))
402 inputValues = newValues;
403 CoreWire.this.recalculateValuesWithoutFusions();
407 * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link CoreWire}.Returns the least
408 * significant bit (LSB)
410 public Bit getInputValue()
412 return getInputValue(0);
416 * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link CoreWire} at the indexed {@link Bit}.
417 * Returns the least significant bit (LSB)
420 public Bit getInputValue(int index)
422 return inputValues.getLSBit(index);
426 * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link CoreWire}.
428 public BitVector getInputValues()
433 public BitVector getInputValues(int start, int end)
435 return inputValues.subVector(start, end);
439 * {@link ReadEnd} now feeds Z into the associated {@link CoreWire}.
441 public void clearSignals()
443 feedSignals(Z.toVector(width));
446 public BitVector wireValuesExcludingMe()
448 BitVectorMutator mutator = BitVectorMutator.empty();
449 boolean modified = false;
450 for (ReadWriteEnd wireEnd : inputs)
455 mutator.join(wireEnd.inputValues);
458 mutator.join(BitVector.of(Bit.Z, width));
459 return mutator.toBitVector();
463 public String toString()
465 return inputValues.toString();
475 void setWriting(boolean isWriting)
477 if (this.isWriting != isWriting)
479 this.isWriting = isWriting;
484 CoreWire.this.recalculateValuesWithoutFusions();
495 public String toString()
497 String name = this.name == null ? String.format("0x%08x", hashCode()) : this.name;
498 return String.format("wire %s value: %s inputs: %s", name, getValues(), inputs);
501 public static ReadEnd[] extractEnds(CoreWire[] w)
503 ReadEnd[] inputs = new ReadEnd[w.length];
504 for (int i = 0; i < w.length; i++)
505 inputs[i] = w[i].createReadWriteEnd();
511 * Fuses two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning: The bits are
512 * permanently fused together.
514 * @param a The {@link CoreWire} to be fused with b
515 * @param b The {@link CoreWire} to be fused with a
517 public static void fuse(CoreWire a, CoreWire b)
519 fuse(a, b, 0, 0, a.width);
523 * Fuses the selected bits of two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning:
524 * The bits are permanently fused together.
526 * @param a The {@link CoreWire} to be (partially) fused with b
527 * @param b The {@link CoreWire} to be (partially) fused with a
528 * @param fromA The first bit of {@link CoreWire} a to be fused
529 * @param fromB The first bit of {@link CoreWire} b to be fused
530 * @param width The amount of bits to fuse
532 public static void fuse(CoreWire a, CoreWire b, int fromA, int fromB, int width)
534 // iterate in this direction to be fail-fast (rely on the checks in fuse(Wire, Wire, int, int)
535 for (int i = width - 1; i >= 0; i--)
536 fuse(a, b, fromA + i, fromB + i);
540 * Fuses one bit of two wires together. If this bit changes in one Wire, the other is changed accordingly immediately. Warning: The bits
541 * are permanently fused together.
543 * @param a The {@link CoreWire} to be (partially) fused with b
544 * @param b The {@link CoreWire} to be (partially) fused with a
545 * @param bitA The bit of {@link CoreWire} a to be fused
546 * @param bitB The bit of {@link CoreWire} b to be fused
548 public static void fuse(CoreWire a, CoreWire b, int bitA, int bitB)
551 throw new IllegalArgumentException("No bit " + bitA + " in " + a + " (width " + a.width + ")");
553 throw new IllegalArgumentException("No bit " + bitB + " in " + b + " (width " + b.width + ")");
554 if (a.fusedBits == null)
555 a.fusedBits = new FusedBit[a.width];
556 if (b.fusedBits == null)
557 b.fusedBits = new FusedBit[b.width];
558 FusedBit oldFusionA = a.fusedBits[bitA];
559 FusedBit oldFusionB = b.fusedBits[bitB];
560 if (oldFusionA == null)
561 if (oldFusionB == null)
563 FusedBit fusion = new FusedBit();
564 fusion.addParticipatingWireBit(a, bitA);
565 fusion.addParticipatingWireBit(b, bitB);
567 oldFusionB.addParticipatingWireBit(a, bitA);
568 else if (oldFusionB == null)
569 oldFusionA.addParticipatingWireBit(b, bitB);
571 oldFusionA.mergeOtherIntoThis(oldFusionB);
574 private static class FusedBit
576 private final Set<WireBit> participatingWireBits;
580 this.participatingWireBits = new HashSet<>();
583 public void addParticipatingWireBit(CoreWire w, int bit)
585 addParticipatingWireBit(new WireBit(w, bit));
588 private void addParticipatingWireBit(WireBit wb)
590 wb.wire.fusedBits[wb.bit] = this;
591 participatingWireBits.add(wb);
592 wb.wire.invalidateCachedValuesForAllFusedWires();
595 public void mergeOtherIntoThis(FusedBit other)
597 for (WireBit wb : other.participatingWireBits)
598 addParticipatingWireBit(wb);
601 public void invalidateCachedValuesForAllParticipatingWires()
603 for (WireBit wb : participatingWireBits)
604 wb.wire.invalidateCachedValues();
607 public Bit getValue()
610 for (WireBit wb : participatingWireBits)
611 if (!wb.wire.inputs.isEmpty())
613 Bit bit = wb.wire.bitsWithoutFusions[wb.bit];
614 result = result == null ? bit : result.join(bit);
616 return result == null ? U : result;
620 private static class WireBit
622 public final CoreWire wire;
623 public final int bit;
625 public WireBit(CoreWire wire, int bit)
632 public int hashCode()
634 final int prime = 31;
636 result = prime * result + bit;
637 result = prime * result + ((wire == null) ? 0 : wire.hashCode());
642 public boolean equals(Object obj)
648 if (getClass() != obj.getClass())
650 WireBit other = (WireBit) obj;
651 if (bit != other.bit)
655 if (other.wire != null)
657 } else if (!wire.equals(other.wire))