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 * The {@link CoreWire} is interpreted as an unsigned integer with n bits.
135 * @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
136 * value), not <code>Bit.U</code>, <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
138 * @author Fabian Stemmler
140 public boolean hasNumericValue()
142 return getValues().isBinary();
146 * The {@link CoreWire} is interpreted as an unsigned integer with n bits.
148 * @return The unsigned value of the {@link CoreWire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
150 * @author Fabian Stemmler
152 public long getUnsignedValue()
156 for (Bit bit : getValues())
163 return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;
175 * The {@link CoreWire} is interpreted as a signed integer with n bits.
177 * @return The signed value of the {@link CoreWire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
179 * @author Fabian Stemmler
181 public long getSignedValue()
183 long val = getUnsignedValue();
184 long mask = 1 << (width - 1);
185 if ((mask & val) != 0)
187 int shifts = 64 - width;
188 return (val << shifts) >> shifts;
194 * Returns the least significant bit (LSB)
196 public Bit getValue()
202 * Returns the least significant bit (LSB) of the given index
204 public Bit getValue(int index)
206 return getValues().getLSBit(index);
209 public BitVector getValues(int start, int end)
211 return getValues().subVector(start, end);
214 public BitVector getValues()
216 if (cachedValues == null)
217 recalculatedCachedValues();
222 * Adds an {@link LogicObserver}, who will be notified when the value of the {@link CoreWire} is updated.
224 * @param ob The {@link LogicObserver} to be notified of changes.
225 * @return true if the given {@link LogicObserver} was not already registered, false otherwise
227 * @author Fabian Stemmler
229 boolean attachEnd(ReadEnd end)
231 return attached.add(end);
234 void detachEnd(ReadEnd end)
236 attached.remove(end);
239 private void notifyObservers()
241 attached.forEach(ReadEnd::update);
245 * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link CoreWire}. This {@link ReadWriteEnd} can be written
248 public ReadWriteEnd createReadWriteEnd()
250 return new ReadWriteEnd();
254 * Create a {@link ReadEnd} object, which is tied to this {@link CoreWire}. This {@link ReadEnd} cannot be written to.
256 public ReadEnd createReadOnlyEnd()
258 return new ReadEnd();
261 void registerInput(ReadWriteEnd toRegister)
263 inputs.add(toRegister);
264 recalculateValuesWithoutFusions();
268 * A {@link ReadEnd} feeds a constant signal into the {@link CoreWire} it is tied to. The combination of all inputs determines the
269 * {@link CoreWire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than
270 * Z 0 and 1 turn into X when they are mixed
272 * @author Fabian Stemmler
274 public class ReadEnd implements LogicObservable
276 private List<LogicObserver> observers = new ArrayList<>();
281 CoreWire.this.attachEnd(this);
290 * Included for convenient use on {@link CoreWire}s of width 1.
292 * @return The value of bit 0.
294 * @author Fabian Stemmler
296 public Bit getValue()
298 return CoreWire.this.getValue();
302 * @param index Index of the requested bit.
303 * @return The value of the indexed bit.
305 * @author Fabian Stemmler
307 public Bit getValue(int index)
309 return CoreWire.this.getValue(index);
312 public BitVector getValues()
314 return CoreWire.this.getValues();
318 * @param start Start of the wanted segment. (inclusive)
319 * @param end End of the wanted segment. (exclusive)
320 * @return The values of the segment of {@link Bit}s indexed.
322 * @author Fabian Stemmler
324 public BitVector getValues(int start, int end)
326 return CoreWire.this.getValues(start, end);
330 * The {@link CoreWire} is interpreted as an unsigned integer with n bits.
332 * @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
333 * same value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
335 * @author Fabian Stemmler
337 public boolean hasNumericValue()
339 return CoreWire.this.hasNumericValue();
343 * The {@link CoreWire} is interpreted as an unsigned integer with n bits.
345 * @return The unsigned value of the {@link CoreWire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
347 * @author Fabian Stemmler
349 public long getUnsignedValue()
351 return CoreWire.this.getUnsignedValue();
355 * The {@link CoreWire} is interpreted as a signed integer with n bits.
357 * @return The signed value of the {@link CoreWire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
359 * @author Fabian Stemmler
361 public long getSignedValue()
363 return CoreWire.this.getSignedValue();
367 public String toString()
369 return CoreWire.this.toString();
376 recalculateValuesWithoutFusions();
384 public CoreWire getWire()
386 return CoreWire.this;
390 public void registerObserver(LogicObserver ob)
396 public void deregisterObserver(LogicObserver ob)
398 observers.remove(ob);
401 // void registerCloseObserver(LogicObserver ob)
403 // closeObserver.add(ob);
406 // void deregisterCloseObserver(LogicObserver ob)
408 // closeObserver.remove(ob);
412 public void notifyObservers()
414 observers.forEach(ob -> ob.update(this));
418 public class ReadWriteEnd extends ReadEnd
420 private boolean open;
421 private boolean isWriting;
422 private BitVector inputValues;
433 private void initValues()
435 inputValues = U.toVector(width);
439 * Sets the wires values. This takes up time, as specified by the {@link CoreWire}s travel time.
441 * @param newValues The new values the wires should take on.
443 * @author Fabian Stemmler
445 public void feedSignals(Bit... newValues)
447 feedSignals(BitVector.of(newValues));
450 // TODO what if this is called multiple times at the same simulation time? (happens in component unit tests)
451 public void feedSignals(BitVector newValues)
453 if (newValues.length() != width)
454 throw new IllegalArgumentException(
455 String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), width));
457 throw new IllegalStateException("Attempted to write to closed WireArrayEnd.");
458 timeline.addEvent(e -> setValues(newValues), travelTime);
462 * Sets values of a subarray of wires. This takes up time, as specified by the {@link CoreWire}s travel time.
464 * @param bitVector The new values the wires should take on.
465 * @param startingBit The first index of the subarray of wires.
467 * @author Fabian Stemmler
469 public void feedSignals(int startingBit, BitVector bitVector)
472 throw new IllegalStateException("Attempted to write to closed WireArrayEnd.");
473 timeline.addEvent(e -> setValues(startingBit, bitVector), travelTime);
477 * Sets the values that are being fed into the {@link CoreWire}. The preferred way of setting {@link ReadWriteEnd} values is via
478 * feedValues(...) with a delay.
480 void setValues(int startingBit, BitVector newValues)
482 // index check covered in equals
483 if (!inputValues.equalsWithOffset(newValues, startingBit))
485 Bit[] vals = inputValues.getBits();
486 System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
487 inputValues = BitVector.of(vals);
488 CoreWire.this.recalculateValuesWithoutFusions();
493 * Sets the values that are being fed into the {@link CoreWire}. The preferred way of setting {@link ReadWriteEnd} values is via
494 * feedValues(...) with a delay.
496 void setValues(BitVector newValues)
498 if (inputValues.equals(newValues))
500 inputValues = newValues;
501 CoreWire.this.recalculateValuesWithoutFusions();
505 * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link CoreWire}.Returns the least
506 * significant bit (LSB)
508 public Bit getInputValue()
510 return getInputValue(0);
514 * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link CoreWire} at the indexed {@link Bit}.
515 * Returns the least significant bit (LSB)
518 public Bit getInputValue(int index)
520 return inputValues.getLSBit(index);
524 * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link CoreWire}.
526 public BitVector getInputValues()
531 public BitVector getInputValues(int start, int end)
533 return inputValues.subVector(start, end);
537 * {@link ReadEnd} now feeds Z into the associated {@link CoreWire}.
539 public void clearSignals()
541 feedSignals(Z.toVector(width));
544 public BitVector wireValuesExcludingMe()
546 BitVectorMutator mutator = BitVectorMutator.empty();
547 boolean modified = false;
548 for (ReadWriteEnd wireEnd : inputs)
553 mutator.join(wireEnd.inputValues);
556 mutator.join(BitVector.of(Bit.Z, width));
557 return mutator.toBitVector();
561 public String toString()
563 return inputValues.toString();
573 void setWriting(boolean isWriting)
575 if (this.isWriting != isWriting)
577 this.isWriting = isWriting;
582 CoreWire.this.recalculateValuesWithoutFusions();
593 public String toString()
595 String name = this.name == null ? String.format("0x%08x", hashCode()) : this.name;
596 return String.format("wire %s value: %s inputs: %s", name, getValues(), inputs);
599 public static ReadEnd[] extractEnds(CoreWire[] w)
601 ReadEnd[] inputs = new ReadEnd[w.length];
602 for (int i = 0; i < w.length; i++)
603 inputs[i] = w[i].createReadWriteEnd();
609 * Fuses two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning: The bits are
610 * permanently fused together.
612 * @param a The {@link CoreWire} to be fused with b
613 * @param b The {@link CoreWire} to be fused with a
615 public static void fuse(CoreWire a, CoreWire b)
617 fuse(a, b, 0, 0, a.width);
621 * Fuses the selected bits of two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning:
622 * The bits are permanently fused together.
624 * @param a The {@link CoreWire} to be (partially) fused with b
625 * @param b The {@link CoreWire} to be (partially) fused with a
626 * @param fromA The first bit of {@link CoreWire} a to be fused
627 * @param fromB The first bit of {@link CoreWire} b to be fused
628 * @param width The amount of bits to fuse
630 public static void fuse(CoreWire a, CoreWire b, int fromA, int fromB, int width)
632 // iterate in this direction to be fail-fast (rely on the checks in fuse(Wire, Wire, int, int)
633 for (int i = width - 1; i >= 0; i--)
634 fuse(a, b, fromA + i, fromB + i);
638 * Fuses one bit of two wires together. If this bit changes in one Wire, the other is changed accordingly immediately. Warning: The bits
639 * are permanently fused together.
641 * @param a The {@link CoreWire} to be (partially) fused with b
642 * @param b The {@link CoreWire} to be (partially) fused with a
643 * @param bitA The bit of {@link CoreWire} a to be fused
644 * @param bitB The bit of {@link CoreWire} b to be fused
646 public static void fuse(CoreWire a, CoreWire b, int bitA, int bitB)
649 throw new IllegalArgumentException("No bit " + bitA + " in " + a + " (width " + a.width + ")");
651 throw new IllegalArgumentException("No bit " + bitB + " in " + b + " (width " + b.width + ")");
652 if (a.fusedBits == null)
653 a.fusedBits = new FusedBit[a.width];
654 if (b.fusedBits == null)
655 b.fusedBits = new FusedBit[b.width];
656 FusedBit oldFusionA = a.fusedBits[bitA];
657 FusedBit oldFusionB = b.fusedBits[bitB];
658 if (oldFusionA == null)
659 if (oldFusionB == null)
661 FusedBit fusion = new FusedBit();
662 fusion.addParticipatingWireBit(a, bitA);
663 fusion.addParticipatingWireBit(b, bitB);
665 oldFusionB.addParticipatingWireBit(a, bitA);
666 else if (oldFusionB == null)
667 oldFusionA.addParticipatingWireBit(b, bitB);
669 oldFusionA.mergeOtherIntoThis(oldFusionB);
672 private static class FusedBit
674 private final Set<WireBit> participatingWireBits;
678 this.participatingWireBits = new HashSet<>();
681 public void addParticipatingWireBit(CoreWire w, int bit)
683 addParticipatingWireBit(new WireBit(w, bit));
686 private void addParticipatingWireBit(WireBit wb)
688 wb.wire.fusedBits[wb.bit] = this;
689 participatingWireBits.add(wb);
690 wb.wire.invalidateCachedValuesForAllFusedWires();
693 public void mergeOtherIntoThis(FusedBit other)
695 for (WireBit wb : other.participatingWireBits)
696 addParticipatingWireBit(wb);
699 public void invalidateCachedValuesForAllParticipatingWires()
701 for (WireBit wb : participatingWireBits)
702 wb.wire.invalidateCachedValues();
705 public Bit getValue()
708 for (WireBit wb : participatingWireBits)
709 if (!wb.wire.inputs.isEmpty())
711 Bit bit = wb.wire.bitsWithoutFusions[wb.bit];
712 result = result == null ? bit : result.join(bit);
714 return result == null ? U : result;
718 private static class WireBit
720 public final CoreWire wire;
721 public final int bit;
723 public WireBit(CoreWire wire, int bit)
730 public int hashCode()
732 final int prime = 31;
734 result = prime * result + bit;
735 result = prime * result + ((wire == null) ? 0 : wire.hashCode());
740 public boolean equals(Object obj)
746 if (getClass() != obj.getClass())
748 WireBit other = (WireBit) obj;
749 if (bit != other.bit)
753 if (other.wire != null)
755 } else if (!wire.equals(other.wire))