Slight improvements of Wire#fuse(...)
[Mograsim.git] / net.mograsim.logic.core / src / net / mograsim / logic / core / wires / Wire.java
index b8990cc..4063d78 100644 (file)
@@ -4,6 +4,7 @@ import static net.mograsim.logic.core.types.Bit.U;
 import static net.mograsim.logic.core.types.Bit.Z;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import net.mograsim.logic.core.LogicObservable;
@@ -22,12 +23,14 @@ import net.mograsim.logic.core.types.BitVector.BitVectorMutator;
 public class Wire
 {
        public final String name;
-       private BitVector values;
+       private BitVector cachedValues;
        public final int travelTime;
        private List<ReadEnd> attached = new ArrayList<>();
        public final int width;
        List<ReadWriteEnd> inputs = new ArrayList<>();
        Timeline timeline;
+       private Bit[] bitsWithoutFusions;
+       FusedBit[] fusedBits;
 
        public Wire(Timeline timeline, int width, int travelTime)
        {
@@ -48,28 +51,67 @@ public class Wire
 
        private void initValues()
        {
-               values = U.toVector(width);
+               cachedValues = U.toVector(width);
+               bitsWithoutFusions = cachedValues.getBits();
        }
 
        private void setNewValues(BitVector newValues)
        {
-               if (values.equals(newValues))
-                       return;
-               values = newValues;
+               cachedValues = newValues;
                notifyObservers();
        }
 
-       void recalculate()
+       private void invalidateCachedValuesForAllFusedWires()
        {
+               invalidateCachedValues();
+               if (fusedBits != null)
+                       for (FusedBit fusion : fusedBits)
+                               if (fusion != null)
+                                       fusion.invalidateCachedValuesForAllParticipatingWires();
+       }
+
+       private void invalidateCachedValues()
+       {
+               cachedValues = null;
+               notifyObservers();
+       }
+
+       void recalculateValuesWithoutFusions()
+       {
+               Bit[] bits = new Bit[width];
                if (inputs.isEmpty())
-                       setNewValues(U.toVector(width));
+                       Arrays.fill(bits, U);
                else
                {
-                       BitVectorMutator mutator = BitVectorMutator.empty();
-                       for (ReadWriteEnd wireArrayEnd : inputs)
-                               mutator.join(wireArrayEnd.getInputValues());
-                       setNewValues(mutator.toBitVector());
+                       System.arraycopy(inputs.get(0).getInputValues().getBits(), 0, bits, 0, width);
+                       for (int i = 1; i < inputs.size(); i++)
+                               Bit.join(bits, inputs.get(i).getInputValues().getBits());
+               }
+               bitsWithoutFusions = bits;
+               if (fusedBits == null)
+                       setNewValues(BitVector.of(bits));
+               else
+                       invalidateCachedValuesForAllFusedWires();
+       }
+
+       private void recalculatedCachedValues()
+       {
+               Bit[] bits;
+               if (fusedBits == null)
+                       bits = bitsWithoutFusions;
+               else
+               {
+                       bits = new Bit[width];
+                       for (int i = 0; i < width; i++)
+                       {
+                               FusedBit fusion = fusedBits[i];
+                               if (fusion == null)
+                                       bits[i] = bitsWithoutFusions[i];
+                               else
+                                       bits[i] = fusion.getValue();
+                       }
                }
+               cachedValues = BitVector.of(bits);
        }
 
        /**
@@ -93,7 +135,7 @@ public class Wire
         */
        public boolean hasNumericValue()
        {
-               return values.isBinary();
+               return getValues().isBinary();
        }
 
        /**
@@ -107,7 +149,7 @@ public class Wire
        {
                long val = 0;
                long mask = 1;
-               for (Bit bit : values)
+               for (Bit bit : getValues())
                {
                        switch (bit)
                        {
@@ -157,17 +199,19 @@ public class Wire
         */
        public Bit getValue(int index)
        {
-               return values.getLSBit(index);
+               return getValues().getLSBit(index);
        }
 
        public BitVector getValues(int start, int end)
        {
-               return values.subVector(start, end);
+               return getValues().subVector(start, end);
        }
 
        public BitVector getValues()
        {
-               return values;
+               if (cachedValues == null)
+                       recalculatedCachedValues();
+               return cachedValues;
        }
 
        /**
@@ -212,6 +256,7 @@ public class Wire
        void registerInput(ReadWriteEnd toRegister)
        {
                inputs.add(toRegister);
+               recalculateValuesWithoutFusions();
        }
 
        /**
@@ -323,7 +368,7 @@ public class Wire
                {
                        inputs.remove(this);
                        detachEnd(this);
-                       recalculate();
+                       recalculateValuesWithoutFusions();
                }
 
                public int width()
@@ -348,6 +393,16 @@ public class Wire
                        observers.remove(ob);
                }
 
+//             void registerCloseObserver(LogicObserver ob)
+//             {
+//                     closeObserver.add(ob);
+//             }
+//             
+//             void deregisterCloseObserver(LogicObserver ob)
+//             {
+//                     closeObserver.remove(ob);
+//             }
+
                @Override
                public void notifyObservers()
                {
@@ -424,7 +479,7 @@ public class Wire
                                Bit[] vals = inputValues.getBits();
                                System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
                                inputValues = BitVector.of(vals);
-                               Wire.this.recalculate();
+                               Wire.this.recalculateValuesWithoutFusions();
                        }
                }
 
@@ -437,7 +492,7 @@ public class Wire
                        if (inputValues.equals(newValues))
                                return;
                        inputValues = newValues;
-                       Wire.this.recalculate();
+                       Wire.this.recalculateValuesWithoutFusions();
                }
 
                /**
@@ -483,12 +538,16 @@ public class Wire
                public BitVector wireValuesExcludingMe()
                {
                        BitVectorMutator mutator = BitVectorMutator.empty();
+                       boolean modified = false;
                        for (ReadWriteEnd wireEnd : inputs)
                        {
                                if (wireEnd == this)
                                        continue;
+                               modified = true;
                                mutator.join(wireEnd.inputValues);
                        }
+                       if (!modified)
+                               mutator.join(BitVector.of(Bit.Z, width));
                        return mutator.toBitVector();
                }
 
@@ -514,7 +573,7 @@ public class Wire
                                        inputs.add(this);
                                else
                                        inputs.remove(this);
-                               Wire.this.recalculate();
+                               Wire.this.recalculateValuesWithoutFusions();
                        }
                }
 
@@ -528,7 +587,7 @@ public class Wire
        public String toString()
        {
                String name = this.name == null ? String.format("0x%08x", hashCode()) : this.name;
-               return String.format("wire %s value: %s inputs: %s", name, values, inputs);
+               return String.format("wire %s value: %s inputs: %s", name, getValues(), inputs);
        }
 
        public static ReadEnd[] extractEnds(Wire[] w)
@@ -539,7 +598,19 @@ public class Wire
                return inputs;
        }
 
-       // TODO Fix ReadWriteEnd feeding signals to entire Wire (Z) instead of only selected Bits
+       /**
+        * 
+        * Fuses two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning: The bits are
+        * permanently fused together.
+        * 
+        * @param a The {@link Wire} to be fused with b
+        * @param b The {@link Wire} to be fused with a
+        */
+       public static void fuse(Wire a, Wire b)
+       {
+               fuse(a, b, 0, 0, a.width);
+       }
+
        /**
         * Fuses the selected bits of two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning:
         * The bits are permanently fused together.
@@ -552,56 +623,101 @@ public class Wire
         */
        public static void fuse(Wire a, Wire b, int fromA, int fromB, int width)
        {
-               ReadWriteEnd rA = a.createReadWriteEnd(), rB = b.createReadWriteEnd();
-               rA.setWriting(false);
-               rB.setWriting(false);
-               rA.setValues(BitVector.of(Bit.Z, a.width));
-               rB.setValues(BitVector.of(Bit.Z, b.width));
-               Fusion aF = new Fusion(rB, fromA, fromB, width), bF = new Fusion(rA, fromB, fromA, width);
-               rA.registerObserver(aF);
-               rB.registerObserver(bF);
-               aF.update(rA);
-               bF.update(rB);
+               // iterate in this direction to be fail-fast (rely on the checks in fuse(Wire, Wire, int, int)
+               for (int i = width - 1; i >= 0; i--)
+                       fuse(a, b, fromA + i, fromB + i);
        }
 
        /**
+        * Fuses one bit of two wires together. If this bit changes in one Wire, the other is changed accordingly immediately. Warning: The bits
+        * are permanently fused together.
         * 
-        * Fuses two wires together. If the bits change in one Wire, the other is changed accordingly immediately. Warning: The bits are
-        * permanently fused together.
-        * 
-        * @param a The {@link Wire} to be fused with b
-        * @param b The {@link Wire} to be fused with a
+        * @param a    The {@link Wire} to be (partially) fused with b
+        * @param b    The {@link Wire} to be (partially) fused with a
+        * @param bitA The bit of {@link Wire} a to be fused
+        * @param bitB The bit of {@link Wire} b to be fused
         */
-       public static void fuse(Wire a, Wire b)
+       private static void fuse(Wire a, Wire b, int bitA, int bitB)
        {
-               fuse(a, b, 0, 0, a.width);
+               if (bitA >= a.width)
+                       throw new IllegalArgumentException("No bit " + bitA + " in " + a + " (width " + a.width + ")");
+               if (bitB >= b.width)
+                       throw new IllegalArgumentException("No bit " + bitB + " in " + b + " (width " + b.width + ")");
+               if (a.fusedBits == null)
+                       a.fusedBits = new FusedBit[a.width];
+               if (b.fusedBits == null)
+                       b.fusedBits = new FusedBit[b.width];
+               FusedBit oldFusionA = a.fusedBits[bitA];
+               FusedBit oldFusionB = b.fusedBits[bitB];
+               if (oldFusionA == null)
+                       if (oldFusionB == null)
+                       {
+                               FusedBit fusion = new FusedBit();
+                               fusion.addParticipatingWireBit(a, bitA);
+                               fusion.addParticipatingWireBit(b, bitB);
+                       } else
+                               oldFusionB.addParticipatingWireBit(a, bitA);
+               else if (oldFusionB == null)
+                       oldFusionA.addParticipatingWireBit(b, bitB);
+               else
+                       oldFusionA.mergeOtherIntoThis(oldFusionB);
        }
 
-       private static class Fusion implements LogicObserver
+       private static class FusedBit
        {
-               private ReadWriteEnd target;
-               int fromSource, fromTarget, width;
+               private final List<WireBit> participatingWireBits;
 
-               public Fusion(ReadWriteEnd target, int fromSource, int fromTarget, int width)
+               public FusedBit()
                {
-                       this.target = target;
-                       this.fromSource = fromSource;
-                       this.fromTarget = fromTarget;
-                       this.width = width;
+                       this.participatingWireBits = new ArrayList<>();
                }
 
-               @Override
-               public void update(LogicObservable initiator)
+               public void addParticipatingWireBit(Wire w, int bit)
                {
-                       ReadWriteEnd source = (ReadWriteEnd) initiator;
-                       if (source.getWire().inputs.size() - (source.isWriting() ? 1 : 0) == 0)
-                               target.setWriting(false);
-                       else
-                       {
-                               target.setWriting(true);
-                               BitVector targetInput = source.wireValuesExcludingMe().subVector(fromSource, fromSource + width);
-                               target.setValues(fromTarget, targetInput);
-                       }
+                       addParticipatingWireBit(new WireBit(w, bit));
+               }
+
+               private void addParticipatingWireBit(WireBit wb)
+               {
+                       wb.wire.fusedBits[wb.bit] = this;
+                       participatingWireBits.add(wb);
+                       wb.wire.invalidateCachedValuesForAllFusedWires();
+               }
+
+               public void mergeOtherIntoThis(FusedBit other)
+               {
+                       for (WireBit wb : other.participatingWireBits)
+                               addParticipatingWireBit(wb);
+               }
+
+               public void invalidateCachedValuesForAllParticipatingWires()
+               {
+                       for (WireBit wb : participatingWireBits)
+                               wb.wire.invalidateCachedValues();
+               }
+
+               public Bit getValue()
+               {
+                       Bit result = null;
+                       for (WireBit wb : participatingWireBits)
+                               if (!wb.wire.inputs.isEmpty())
+                               {
+                                       Bit bit = wb.wire.bitsWithoutFusions[wb.bit];
+                                       result = result == null ? bit : result.join(bit);
+                               }
+                       return result == null ? U : result;
+               }
+       }
+
+       private static class WireBit
+       {
+               public final Wire wire;
+               public final int bit;
+
+               public WireBit(Wire wire, int bit)
+               {
+                       this.wire = wire;
+                       this.bit = bit;
                }
        }
 }
\ No newline at end of file