From: Daniel Kirschten Date: Fri, 10 May 2019 13:01:41 +0000 (+0200) Subject: Merge branch 'wire_array_inputs_update' X-Git-Url: https://mograsim.net/gitweb/?a=commitdiff_plain;h=5f74819aeebd1f133d3513bc2db9f2258a76c4ad;hp=19bc12b3acc2035af6d4c89cb5fc6da9f93639c5;p=Mograsim.git Merge branch 'wire_array_inputs_update' --- diff --git a/era.mi/.classpath b/era.mi/.classpath new file mode 100644 index 00000000..bc8d71b0 --- /dev/null +++ b/era.mi/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/era.mi/.project b/era.mi/.project new file mode 100644 index 00000000..9a89f37b --- /dev/null +++ b/era.mi/.project @@ -0,0 +1,17 @@ + + + era.mi + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/era.mi/.settings/org.eclipse.jdt.core.prefs b/era.mi/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..a54bb93c --- /dev/null +++ b/era.mi/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=10 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=10 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=10 diff --git a/era.mi/bin/era/mi/logic/Bit.class b/era.mi/bin/era/mi/logic/Bit.class new file mode 100644 index 00000000..9bba9f0c Binary files /dev/null and b/era.mi/bin/era/mi/logic/Bit.class differ diff --git a/era.mi/bin/era/mi/logic/Simulation.class b/era.mi/bin/era/mi/logic/Simulation.class new file mode 100644 index 00000000..7cfc0430 Binary files /dev/null and b/era.mi/bin/era/mi/logic/Simulation.class differ diff --git a/era.mi/bin/era/mi/logic/Util$BitOp.class b/era.mi/bin/era/mi/logic/Util$BitOp.class new file mode 100644 index 00000000..6e5fd47a Binary files /dev/null and b/era.mi/bin/era/mi/logic/Util$BitOp.class differ diff --git a/era.mi/bin/era/mi/logic/Util.class b/era.mi/bin/era/mi/logic/Util.class new file mode 100644 index 00000000..09b21b77 Binary files /dev/null and b/era.mi/bin/era/mi/logic/Util.class differ diff --git a/era.mi/bin/era/mi/logic/components/BasicComponent.class b/era.mi/bin/era/mi/logic/components/BasicComponent.class new file mode 100644 index 00000000..8ed18388 Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/BasicComponent.class differ diff --git a/era.mi/bin/era/mi/logic/components/Clock.class b/era.mi/bin/era/mi/logic/components/Clock.class new file mode 100644 index 00000000..70dfad4c Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/Clock.class differ diff --git a/era.mi/bin/era/mi/logic/components/Merger.class b/era.mi/bin/era/mi/logic/components/Merger.class new file mode 100644 index 00000000..e76a7d23 Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/Merger.class differ diff --git a/era.mi/bin/era/mi/logic/components/Merger2.class b/era.mi/bin/era/mi/logic/components/Merger2.class new file mode 100644 index 00000000..08869fa4 Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/Merger2.class differ diff --git a/era.mi/bin/era/mi/logic/components/Mux.class b/era.mi/bin/era/mi/logic/components/Mux.class new file mode 100644 index 00000000..ec4740b3 Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/Mux.class differ diff --git a/era.mi/bin/era/mi/logic/components/Splitter.class b/era.mi/bin/era/mi/logic/components/Splitter.class new file mode 100644 index 00000000..d795f92c Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/Splitter.class differ diff --git a/era.mi/bin/era/mi/logic/components/gates/AndGate.class b/era.mi/bin/era/mi/logic/components/gates/AndGate.class new file mode 100644 index 00000000..d6ea9f59 Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/gates/AndGate.class differ diff --git a/era.mi/bin/era/mi/logic/components/gates/NotGate.class b/era.mi/bin/era/mi/logic/components/gates/NotGate.class new file mode 100644 index 00000000..addad50e Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/gates/NotGate.class differ diff --git a/era.mi/bin/era/mi/logic/components/gates/OrGate.class b/era.mi/bin/era/mi/logic/components/gates/OrGate.class new file mode 100644 index 00000000..ddc06f36 Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/gates/OrGate.class differ diff --git a/era.mi/bin/era/mi/logic/components/gates/XorGate.class b/era.mi/bin/era/mi/logic/components/gates/XorGate.class new file mode 100644 index 00000000..e2c3f8bd Binary files /dev/null and b/era.mi/bin/era/mi/logic/components/gates/XorGate.class differ diff --git a/era.mi/bin/era/mi/logic/tests/ComponentTest.class b/era.mi/bin/era/mi/logic/tests/ComponentTest.class new file mode 100644 index 00000000..081de043 Binary files /dev/null and b/era.mi/bin/era/mi/logic/tests/ComponentTest.class differ diff --git a/era.mi/bin/era/mi/logic/timeline/Timeline$InnerEvent.class b/era.mi/bin/era/mi/logic/timeline/Timeline$InnerEvent.class new file mode 100644 index 00000000..5278183b Binary files /dev/null and b/era.mi/bin/era/mi/logic/timeline/Timeline$InnerEvent.class differ diff --git a/era.mi/bin/era/mi/logic/timeline/Timeline.class b/era.mi/bin/era/mi/logic/timeline/Timeline.class new file mode 100644 index 00000000..52329114 Binary files /dev/null and b/era.mi/bin/era/mi/logic/timeline/Timeline.class differ diff --git a/era.mi/bin/era/mi/logic/timeline/TimelineEvent.class b/era.mi/bin/era/mi/logic/timeline/TimelineEvent.class new file mode 100644 index 00000000..7eac32e0 Binary files /dev/null and b/era.mi/bin/era/mi/logic/timeline/TimelineEvent.class differ diff --git a/era.mi/bin/era/mi/logic/timeline/TimelineEventHandler.class b/era.mi/bin/era/mi/logic/timeline/TimelineEventHandler.class new file mode 100644 index 00000000..7a4a6f78 Binary files /dev/null and b/era.mi/bin/era/mi/logic/timeline/TimelineEventHandler.class differ diff --git a/era.mi/src/era/mi/logic/Bit.java b/era.mi/src/era/mi/logic/Bit.java new file mode 100644 index 00000000..b18c5974 --- /dev/null +++ b/era.mi/src/era/mi/logic/Bit.java @@ -0,0 +1,62 @@ +package era.mi.logic; + + +public enum Bit +{ + ONE, ZERO, Z, X; + + public static Bit and(Bit a, Bit b) + { + return a.and(b); + } + + public Bit and(Bit other) + { + if(equals(Bit.ZERO) || other.equals(Bit.ZERO)) + return Bit.ZERO; + else if(equals(other) && equals(Bit.ONE)) + return Bit.ONE; + else + return Bit.X; + } + + public static Bit or(Bit a, Bit b) + { + return a.or(b); + } + + public Bit or(Bit other) + { + if(equals(Bit.ONE) || other.equals(Bit.ONE)) + return Bit.ONE; + else if(equals(other) && equals(Bit.ZERO)) + return Bit.ZERO; + else + return Bit.X; + } + + public static Bit xor(Bit a, Bit b) + { + return a.xor(b); + } + + public Bit xor(Bit other) + { + //I'm uncertain how this should behave for cases where one value is neither 1 nor 0. + //TODO: Implement xor + return Bit.X; + } + + public Bit not() + { + switch(this) + { + case ONE: + return Bit.ZERO; + case ZERO: + return Bit.ONE; + default: + return Bit.X; + } + } +} diff --git a/era.mi/src/era/mi/logic/Simulation.java b/era.mi/src/era/mi/logic/Simulation.java new file mode 100644 index 00000000..6a63e155 --- /dev/null +++ b/era.mi/src/era/mi/logic/Simulation.java @@ -0,0 +1,12 @@ +package era.mi.logic; + +import era.mi.logic.timeline.Timeline; + +public class Simulation +{ + public final static Timeline TIMELINE = new Timeline(11); + + public static void main(String[] args) + { + } +} \ No newline at end of file diff --git a/era.mi/src/era/mi/logic/Util.java b/era.mi/src/era/mi/logic/Util.java new file mode 100644 index 00000000..6f1b93ff --- /dev/null +++ b/era.mi/src/era/mi/logic/Util.java @@ -0,0 +1,92 @@ +package era.mi.logic; + +import java.util.Arrays; + +public final class Util +{ + + @SuppressWarnings("unchecked") + public static T[] concat(T[]... arrays) + { + if(arrays.length == 0) + throw new IllegalArgumentException("Cannot concatenate 0 arrays."); + + int length = 0; + for(T[] array : arrays) + length += array.length; + + T[] newArray = Arrays.copyOf(arrays[0], length); + int appendIndex = arrays[0].length; + for(int i = 1; i < arrays.length; i++) + { + System.arraycopy(arrays[i], 0, newArray, appendIndex, arrays[i].length); + appendIndex += arrays[i].length; + } + + return newArray; + } + +// @SuppressWarnings("unchecked") +// public static T[][] split(T[] array, int... lengths) +// { +// //TODO: implement array split again; This version contains an illegal cast +// int totalLength = 0; +// for(int length : lengths) +// totalLength += length; +// +// if(totalLength != array.length) +// throw new IllegalArgumentException(); //TODO: add proper error message +// +// Object[][] newArray = new Object[lengths.length][]; +// int splitIndex = 0; +// for(int i = 0; i < lengths.length; i++) +// { +// System.arraycopy(array, splitIndex, newArray, 0, lengths[i]); +// splitIndex += lengths[i]; +// } +// +// return (T[][]) newArray; +// } + + public static Bit[] and(Bit[] a, Bit[] b) + { + return binBitOp(a, b, (bA, bB) -> Bit.and(bA, bB)); + } + + public static Bit[] or(Bit[] a, Bit[] b) + { + return binBitOp(a, b, (bA, bB) -> Bit.or(bA, bB)); + } + + public static Bit[] xor(Bit[] a, Bit[] b) + { + return binBitOp(a, b, (bA, bB) -> Bit.xor(bA, bB)); + } + + private static Bit[] binBitOp(Bit[] a, Bit[] b, BitOp op) + { + if(a.length != b.length) + throw new IllegalArgumentException("Bit Arrays were not of equal length."); + Bit[] out = new Bit[a.length]; + for(int i = 0; i < a.length; i++) + { + out[i] = op.execute(a[i], b[i]); + } + return out; + } + + public static Bit[] not(Bit[] a) + { + Bit[] out = new Bit[a.length]; + for(int i = 0; i < a.length; i++) + { + out[i] = a[i].not(); + } + return out; + } + + interface BitOp + { + Bit execute(Bit a, Bit b); + } +} diff --git a/era.mi/src/era/mi/logic/components/BasicComponent.java b/era.mi/src/era/mi/logic/components/BasicComponent.java new file mode 100644 index 00000000..ddcf4cea --- /dev/null +++ b/era.mi/src/era/mi/logic/components/BasicComponent.java @@ -0,0 +1,29 @@ +package era.mi.logic.components; + +import era.mi.logic.Simulation; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArrayObserver; + +public abstract class BasicComponent implements WireArrayObserver +{ + private int processTime; + + /** + * + * @param processTime Amount of time this component takes to update its outputs. Must be more than 0, otherwise 1 is assumed. + * + * @author Fabian Stemmler + */ + public BasicComponent(int processTime) + { + this.processTime = processTime > 0 ? processTime : 1; + } + + @Override + public void update(WireArray initiator) + { + Simulation.TIMELINE.addEvent((e) -> {compute();}, processTime); + } + + protected abstract void compute(); +} diff --git a/era.mi/src/era/mi/logic/components/Clock.java b/era.mi/src/era/mi/logic/components/Clock.java new file mode 100644 index 00000000..9f2ecca6 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/Clock.java @@ -0,0 +1,32 @@ +package era.mi.logic.components; + +import era.mi.logic.Bit; +import era.mi.logic.Simulation; +import era.mi.logic.timeline.TimelineEvent; +import era.mi.logic.timeline.TimelineEventHandler; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +public class Clock implements TimelineEventHandler +{ + private boolean toggle = false; + private WireArrayInput outI; + + public Clock(WireArray out) + { + this.outI = out.createInput(); + } + + @Override + public void handle(TimelineEvent e) + { + Simulation.TIMELINE.addEvent(this, 50); + outI.feedSignals(new Bit[] { toggle ? Bit.ONE : Bit.ZERO }); + toggle = !toggle; + } + + public WireArray getOut() + { + return outI.owner; + } +} diff --git a/era.mi/src/era/mi/logic/components/Merger.java b/era.mi/src/era/mi/logic/components/Merger.java new file mode 100644 index 00000000..eb910a4f --- /dev/null +++ b/era.mi/src/era/mi/logic/components/Merger.java @@ -0,0 +1,61 @@ +package era.mi.logic.components; + +import era.mi.logic.Util; +import era.mi.logic.Bit; +import era.mi.logic.WireArray; +import era.mi.logic.WireArrayObserver; + +@Deprecated +public class Merger implements WireArrayObserver +{ + private WireArray out; + private WireArray[] inputs; + + //TODO: General problem with this concept; New inputs coming in at the same time override each other + + /** + * + * @param union The output of merging n {@link WireArray}s into one. Must have length = a1.length() + a2.length() + ... + an.length(). + * @param inputs The inputs to be merged into the union + */ + public Merger(WireArray union, WireArray... inputs) + { + this.inputs = inputs; + this.out = union; + + int length = 0; + for(WireArray input : inputs) + { + length += input.length(); + input.addObserver(this); + } + + if(length != union.length()) + throw new IllegalArgumentException("The output of merging n WireArrays into one must have length = a1.length() + a2.length() + ... + an.length()."); + } + + protected void compute() + { + Bit[][] bits = new Bit[inputs.length][]; + for(int i = 0; i < inputs.length; i++) + bits[i] = inputs[i].getValues(); + Bit[] newOut = Util.concat(bits); + out.feedSignals(newOut); + } + + public WireArray getInput(int index) + { + return inputs[index]; + } + + public WireArray getUnion() + { + return out; + } + + @Override + public void update(WireArray initiator) + { + compute(); //No inner delay + } +} diff --git a/era.mi/src/era/mi/logic/components/Merger2.java b/era.mi/src/era/mi/logic/components/Merger2.java new file mode 100644 index 00000000..ca242544 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/Merger2.java @@ -0,0 +1,70 @@ +package era.mi.logic.components; + +import era.mi.logic.WireArray; +import era.mi.logic.WireArrayObserver; + +public class Merger2 implements WireArrayObserver +{ + private WireArray out; + private WireArray[] inputs; + private int[] beginningIndex; + + /** + * + * @param union The output of merging n {@link WireArray}s into one. Must have length = a1.length() + a2.length() + ... + an.length(). + * @param inputs The inputs to be merged into the union + */ + public Merger2(WireArray union, WireArray... inputs) + { + this.inputs = inputs; + this.out = union; + this.beginningIndex = new int[inputs.length]; + + int length = 0; + for(int i = 0; i < inputs.length; i++) + { + beginningIndex[i] = length; + length += inputs[i].length(); + inputs[i].addObserver(this); + } + + if(length != union.length()) + throw new IllegalArgumentException("The output of merging n WireArrays into one must have length = a1.length() + a2.length() + ... + an.length()."); + } + + public WireArray getInput(int index) + { + return inputs[index]; + } + + public WireArray getUnion() + { + return out; + } + + @Override + public void update(WireArray initiator) + { + int index = find(initiator); + int beginning = beginningIndex[index]; + out.feedSignals(beginning, initiator.getValues()); + } + + private int find(WireArray w) + { + for(int i = 0; i < inputs.length; i++) + if(inputs[i] == w) + return i; + return -1; + } + + public WireArray getOut() + { + return out; + } + + public WireArray[] getInputs() + { + return inputs.clone(); + } +} diff --git a/era.mi/src/era/mi/logic/components/Mux.java b/era.mi/src/era/mi/logic/components/Mux.java new file mode 100644 index 00000000..0b19e7c8 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/Mux.java @@ -0,0 +1,77 @@ +package era.mi.logic.components; + +import era.mi.logic.Bit; +import era.mi.logic.wires.WireArray; + +/** + * Models a Multiplexer. A is selected when select bit is 1, B when select bit is 0. Outputs X otherwise. + * @author Fabian + * + */ +public class Mux extends BasicComponent +{ + private WireArray a, b, out; + private WireArray select; + private final int size; + + /** + * {@link WireArray}s a, b and out must be of uniform length, select + * @param a Must be of uniform length with b and out. + * @param b Must be of uniform length with a and out. + * @param select C + * @param out Must be of uniform length with a and b. + */ + public Mux(int processTime, WireArray a, WireArray b, WireArray select, WireArray out) + { + super(processTime); + size = a.length; + if(b.length != out.length || b.length != size) + throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!"); + this.a = a; + a.addObserver(this); + this.b = b; + b.addObserver(this); + this.select = select; + select.addObserver(this); + this.out = out; + } + + @Override + protected void compute() + { + WireArray active = b; + switch(select.getValue()) + { + case ONE: + active = a; + case ZERO: + out.feedSignals(active.getValues()); + break; + default: + Bit[] newValues = new Bit[size]; + for(int i = 0; i < size; i++) + newValues[i] = Bit.X; + out.feedSignals(newValues); + } + } + + public WireArray getA() + { + return a; + } + + public WireArray getB() + { + return b; + } + + public WireArray getOut() + { + return out; + } + + public WireArray getSelect() + { + return select; + } +} diff --git a/era.mi/src/era/mi/logic/components/Mux2.java b/era.mi/src/era/mi/logic/components/Mux2.java new file mode 100644 index 00000000..c0c4ce32 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/Mux2.java @@ -0,0 +1,101 @@ +package era.mi.logic.components; + +import era.mi.logic.Simulation; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; +import era.mi.logic.wires.WireArrayObserver; + +/** + * Models a Multiplexer. A is selected when select bit is 1, B when select bit is 0. Outputs X otherwise. + * @author Fabian Stemmler + * + */ +public class Mux2 implements WireArrayObserver +{ + private WireArray select; + private WireArrayInput outI; + private WireArrayInput[] inputs; + private final int size; + private final int processTime; + private int selected; + + /** + * {@link WireArray}s a, b and out must be of uniform length, select + * @param out Must be of uniform length with a and b. + * @param select Indexes the input array which is to be mapped to the output + * @param inputs One of these inputs is mapped to the output, depending on the select wires + */ + public Mux2(int processTime, WireArray out, WireArray select, WireArray... inputs) + { + this.processTime = processTime; + size = out.length; + + this.inputs = new WireArrayInput[inputs.length]; + for(int i = 0; i < this.inputs.length; i++) + { + if(inputs[i].length != size) + throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!"); + this.inputs[i] = inputs[i].createInput(); + inputs[i].addObserver(this); + } + + this.select = select; + select.addObserver(this); + selected = -1; + + int maxInputs = 1 << select.length; + if(this.inputs.length > maxInputs) + throw new IllegalArgumentException("There are more inputs (" + + this.inputs.length + ") to the MUX than supported by " + + select.length + " select bits (" + maxInputs + ")."); + + outI = out.createInput(); + out.addObserver(this); + } + + public WireArray getOut() + { + return outI.owner; + } + + public WireArray getSelect() + { + return select; + } + + @Override + public void update(WireArray initiator) { + int selectValue; + if(!select.hasNumericValue() || (selectValue = (int) select.getUnsignedValue()) > size) + { + if(initiator == select) + { + Simulation.TIMELINE.addEvent((e) -> { + if(selected != -1) + { + inputs[selected].clearSignals(); + selected = -1; + outI.clearSignals(); + } + }, processTime); + } + return; + } + + WireArrayInput active = inputs[selectValue]; + Simulation.TIMELINE.addEvent((e) -> { + if(initiator == select) + { + if(selected != -1) + inputs[selected].clearSignals(); + selected = selectValue; + active.feedSignals(outI.owner.getValues()); + outI.feedSignals(active.owner.getValues()); + } + else if(initiator == outI.owner) + active.feedSignals(outI.owner.getValues()); + else if(initiator == active.owner) + outI.feedSignals(active.owner.getValues()); + }, processTime); + } +} diff --git a/era.mi/src/era/mi/logic/components/Splitter.java b/era.mi/src/era/mi/logic/components/Splitter.java new file mode 100644 index 00000000..58d48e7c --- /dev/null +++ b/era.mi/src/era/mi/logic/components/Splitter.java @@ -0,0 +1,53 @@ +package era.mi.logic.components; + +import era.mi.logic.Bit; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArrayObserver; + +public class Splitter implements WireArrayObserver +{ + private WireArray input; + private WireArray[] outputs; + + public Splitter(WireArray input, WireArray... outputs) + { + this.input = input; + this.outputs = outputs; + input.addObserver(this); + int length = 0; + for(WireArray out : outputs) + length += out.length; + + if(input.length != length) + throw new IllegalArgumentException("The input of splitting one into n WireArrays must have length = a1.length() + a2.length() + ... + an.length()."); + } + + protected void compute() + { + int startIndex = 0; + Bit[] inputBits = input.getValues(); + for(int i = 0; i < outputs.length; i++) + { + Bit[] outputBits = new Bit[outputs[i].length]; + System.arraycopy(inputBits, startIndex, outputBits, 0, outputs[i].length); + outputs[i].feedSignals(outputBits); + startIndex += outputs[i].length; + } + } + + public WireArray getInput() + { + return input; + } + + public WireArray[] getOutputs() + { + return outputs.clone(); + } + + @Override + public void update(WireArray initiator) + { + compute(); + } +} diff --git a/era.mi/src/era/mi/logic/components/TriState.java b/era.mi/src/era/mi/logic/components/TriState.java new file mode 100644 index 00000000..2bddbf1c --- /dev/null +++ b/era.mi/src/era/mi/logic/components/TriState.java @@ -0,0 +1,33 @@ +package era.mi.logic.components; + +import era.mi.logic.Bit; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +public class TriState extends BasicComponent{ + WireArray in, enable; + WireArrayInput outI; + + public TriState(int processTime, WireArray in, WireArray out, WireArray enable) { + super(processTime); + if(in.length != out.length) + throw new IllegalArgumentException("Tri-state output must have the same amount of bits as the input. Input: " + in.length + " Output: " + out.length); + if(enable.length != 1) + throw new IllegalArgumentException("Tri-state enable must have exactly one bit, not " + enable.length + "."); + this.in = in; + in.addObserver(this); + this.enable = enable; + enable.addObserver(this); + outI = out.createInput(); + } + + @Override + protected void compute() + { + if(enable.getValue() == Bit.ONE) + outI.feedSignals(in.getValues()); + else + outI.clearSignals(); + } + +} diff --git a/era.mi/src/era/mi/logic/components/gates/AndGate.java b/era.mi/src/era/mi/logic/components/gates/AndGate.java new file mode 100644 index 00000000..b3269cbd --- /dev/null +++ b/era.mi/src/era/mi/logic/components/gates/AndGate.java @@ -0,0 +1,43 @@ +package era.mi.logic.components.gates; + +import era.mi.logic.Util; +import era.mi.logic.components.BasicComponent; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +public class AndGate extends BasicComponent +{ + private WireArray a, b, out; + private WireArrayInput outI; + + public AndGate(int processTime, WireArray a, WireArray b, WireArray out) + { + super(processTime); + this.a = a; + a.addObserver(this); + this.b = b; + b.addObserver(this); + this.out = out; + outI = out.createInput(); + } + + protected void compute() + { + outI.feedSignals(Util.and(a.getValues(), b.getValues())); + } + + public WireArray getA() + { + return a; + } + + public WireArray getB() + { + return b; + } + + public WireArray getOut() + { + return out; + } +} diff --git a/era.mi/src/era/mi/logic/components/gates/NotGate.java b/era.mi/src/era/mi/logic/components/gates/NotGate.java new file mode 100644 index 00000000..1aba6b86 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/gates/NotGate.java @@ -0,0 +1,37 @@ +package era.mi.logic.components.gates; + +import era.mi.logic.Util; +import era.mi.logic.components.BasicComponent; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +public class NotGate extends BasicComponent +{ + private WireArray in, out; + private WireArrayInput outI; + + + public NotGate(int processTime, WireArray in, WireArray out) + { + super(processTime); + this.in = in; + in.addObserver(this); + this.out = out; + outI = out.createInput(); + } + + public void compute() + { + outI.feedSignals(Util.not(in.getValues())); + } + + public WireArray getIn() + { + return in; + } + + public WireArray getOut() + { + return out; + } +} diff --git a/era.mi/src/era/mi/logic/components/gates/OrGate.java b/era.mi/src/era/mi/logic/components/gates/OrGate.java new file mode 100644 index 00000000..c1d95a78 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/gates/OrGate.java @@ -0,0 +1,43 @@ +package era.mi.logic.components.gates; + +import era.mi.logic.Util; +import era.mi.logic.components.BasicComponent; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +public class OrGate extends BasicComponent +{ + private WireArray a, b, out; + private WireArrayInput outI; + + public OrGate(int processTime, WireArray a, WireArray b, WireArray out) + { + super(processTime); + this.a = a; + a.addObserver(this); + this.b = b; + b.addObserver(this); + this.out = out; + this.outI = out.createInput(); + } + + protected void compute() + { + outI.feedSignals(Util.or(a.getValues(), b.getValues())); + } + + public WireArray getA() + { + return a; + } + + public WireArray getB() + { + return b; + } + + public WireArray getOut() + { + return out; + } +} diff --git a/era.mi/src/era/mi/logic/components/gates/XorGate.java b/era.mi/src/era/mi/logic/components/gates/XorGate.java new file mode 100644 index 00000000..c7a94550 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/gates/XorGate.java @@ -0,0 +1,42 @@ +package era.mi.logic.components.gates; + +import era.mi.logic.Util; +import era.mi.logic.components.BasicComponent; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +public class XorGate extends BasicComponent +{ + private WireArray a, b, out; + private WireArrayInput outI; + + public XorGate(int processTime, WireArray a, WireArray b, WireArray out) + { + super(processTime); + this.a = a; + a.addObserver(this); + this.b = b; + b.addObserver(this); + this.out = out; + } + + protected void compute() + { + outI.feedSignals(Util.xor(a.getValues(), b.getValues())); + } + + public WireArray getA() + { + return a; + } + + public WireArray getB() + { + return b; + } + + public WireArray getOut() + { + return out; + } +} diff --git a/era.mi/src/era/mi/logic/tests/ComponentTest.java b/era.mi/src/era/mi/logic/tests/ComponentTest.java new file mode 100644 index 00000000..0638cf8a --- /dev/null +++ b/era.mi/src/era/mi/logic/tests/ComponentTest.java @@ -0,0 +1,248 @@ +package era.mi.logic.tests; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import era.mi.logic.Bit; +import era.mi.logic.Simulation; +import era.mi.logic.components.Merger2; +import era.mi.logic.components.Mux; +import era.mi.logic.components.Mux2; +import era.mi.logic.components.Splitter; +import era.mi.logic.components.gates.AndGate; +import era.mi.logic.components.gates.NotGate; +import era.mi.logic.components.gates.OrGate; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +class ComponentTest +{ + +// @Test +// void circuitExampleTest() +// { +// Simulation.TIMELINE.reset(); +// WireArray a = new WireArray(1, 1), b = new WireArray(1, 1), c = new WireArray(1, 10), d = new WireArray(2, 1), e = new WireArray(1, 1), +// f = new WireArray(1, 1), g = new WireArray(1, 1), h = new WireArray(2, 1), i = new WireArray(2, 1), j = new WireArray(1, 1), k = new WireArray(1, 1); +// new AndGate(1, a, b, f); +// new NotGate(1, f, g); +// new Merger2(h, c, g); +// new Mux(1, h, d, e, i); +// new Splitter(i, k, j); +// +// a.createInput().feedSignals(Bit.ZERO); +// b.createInput().feedSignals(Bit.ONE); +// c.createInput().feedSignals(Bit.ZERO); +// d.createInput().feedSignals(Bit.ONE, Bit.ONE); +// e.createInput().feedSignals(Bit.ONE); +// +// while(Simulation.TIMELINE.hasNext()) +// { +// Simulation.TIMELINE.executeNext(); +// } +// +// assertEquals(Simulation.TIMELINE.getSimulationTime(), 14); +// assertEquals(Bit.ONE, j.getValue()); +// assertEquals(Bit.ZERO, k.getValue()); +// } +// +// @Test +// void splitterTest() +// { +// Simulation.TIMELINE.reset(); +// WireArray a = new WireArray(3, 1), b = new WireArray(2, 1), c = new WireArray(3, 1), in = new WireArray(8, 1); +// in.createInput().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO,Bit.ONE, Bit.ZERO, Bit.ONE); +// new Splitter(in, a, b, c); +// +// while(Simulation.TIMELINE.hasNext()) +// { +// Simulation.TIMELINE.executeNext(); +// } +// +// assertTrue(Arrays.equals(a.getValues(), new Bit[] { Bit.ZERO, Bit.ONE, Bit.ZERO })); +// assertTrue(Arrays.equals(b.getValues(), new Bit[] { Bit.ONE, Bit.ZERO })); +// assertTrue(Arrays.equals(c.getValues(), new Bit[] { Bit.ONE, Bit.ZERO, Bit.ONE })); +// } +// +// @Test +// void mergerTest() +// { +// Simulation.TIMELINE.reset(); +// WireArray a = new WireArray(3, 1), b = new WireArray(2, 1), c = new WireArray(3, 1), out = new WireArray(8, 1); +// a.createInput().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO); +// b.createInput().feedSignals(Bit.ONE, Bit.ZERO); +// c.createInput().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); +// +// new Merger2(out, a, b, c); +// +// while(Simulation.TIMELINE.hasNext()) +// { +// Simulation.TIMELINE.executeNext(); +// } +// +// assertTrue(Arrays.equals(out.getValues(), new Bit[] { Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE })); +// } + + @Test + void muxTest() + { + Simulation.TIMELINE.reset(); + WireArray a = new WireArray(1, 3), b = new WireArray(1, 2), select = new WireArray(1, 1), out = new WireArray(1, 1); + WireArrayInput selectIn = select.createInput(); + + selectIn.feedSignals(Bit.ZERO); + a.createInput().feedSignals(Bit.ONE); + b.createInput().feedSignals(Bit.ZERO); + + new Mux2(1, out, select, a, b); + assertEquals(Bit.Z, out.getValue()); + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertEquals(Bit.ONE, out.getValue()); + selectIn.feedSignals(Bit.ONE); + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertEquals(out.getValue(), Bit.ZERO); + } + + @Test + void andTest() + { + Simulation.TIMELINE.reset(); + AndGate gate = new AndGate(1, new WireArray(4, 1), new WireArray(4, 1), new WireArray(4, 1)); + gate.getA().createInput().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO); + gate.getB().createInput().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + + + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + assertTrue(Arrays.equals(gate.getOut().getValues(), new Bit[] { Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ZERO })); + } + + @Test + void orTest() + { + Simulation.TIMELINE.reset(); + OrGate gate = new OrGate(1, new WireArray(4, 1), new WireArray(4, 1), new WireArray(4, 1)); + gate.getA().createInput().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO); + gate.getB().createInput().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertTrue(Arrays.equals(gate.getOut().getValues(), new Bit[] { Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ONE })); + } + + @Test + void rsLatchCircuitTest() + { + Simulation.TIMELINE.reset(); + WireArray r = new WireArray(1, 1), s = new WireArray(1, 1), t1 = new WireArray(1, 15), t2 = new WireArray(1, 1), q = new WireArray(1, 1), + nq = new WireArray(1, 1); + + new OrGate(1, r, nq, t2); + new OrGate(1, s, q, t1); + new NotGate(1, t2, q); + new NotGate(1, t1, nq); + + WireArrayInput sIn = s.createInput(), rIn = r.createInput(); + + sIn.feedSignals(Bit.ONE); + rIn.feedSignals(Bit.ZERO); + + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertEquals(q.getValue(), Bit.ONE); + assertEquals(nq.getValue(), Bit.ZERO); + + sIn.feedSignals(Bit.ZERO); + + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertEquals(q.getValue(), Bit.ONE); + assertEquals(nq.getValue(), Bit.ZERO); + + rIn.feedSignals(Bit.ONE); + + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertEquals(q.getValue(), Bit.ZERO); + assertEquals(nq.getValue(), Bit.ONE); + } + + @Test + void numericValueTest() + { + Simulation.TIMELINE.reset(); + + WireArray a = new WireArray(4, 1); + a.createInput().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE); + + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + + assertEquals(a.getUnsignedValue(), 15); + assertEquals(a.getSignedValue(), -1); + } + + @Test + void multipleInputs() + { + Simulation.TIMELINE.reset(); + WireArray w = new WireArray(2, 1); + WireArrayInput wI1 = w.createInput(), wI2 = w.createInput(); + wI1.feedSignals(Bit.ONE, Bit.Z); + wI2.feedSignals(Bit.Z, Bit.X); + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + assertTrue(Arrays.equals(w.getValues(), new Bit[] { Bit.ONE, Bit.X })); + + wI2.feedSignals(Bit.ZERO, Bit.Z); + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + assertTrue(Arrays.equals(w.getValues(), new Bit[] { Bit.X, Bit.Z })); + + wI2.feedSignals(Bit.Z, Bit.Z); + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + assertTrue(Arrays.equals(w.getValues(), new Bit[] { Bit.ONE, Bit.Z })); + + wI2.feedSignals(Bit.ONE, Bit.Z); + w.addObserver((i) -> fail("WireArray notified observer, although value did not change.")); + while(Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + } + assertTrue(Arrays.equals(w.getValues(), new Bit[] { Bit.ONE, Bit.Z })); + } +} diff --git a/era.mi/src/era/mi/logic/timeline/Timeline.java b/era.mi/src/era/mi/logic/timeline/Timeline.java new file mode 100644 index 00000000..125b69c3 --- /dev/null +++ b/era.mi/src/era/mi/logic/timeline/Timeline.java @@ -0,0 +1,89 @@ +package era.mi.logic.timeline; + +import java.util.PriorityQueue; + +/** + * Orders Events by the time they are due to be executed. Can execute Events individually. + * @author Fabian Stemmler + * + */ +public class Timeline +{ + private PriorityQueue events; + private long currentTime = 0; + + public Timeline(int initCapacity) + { + events = new PriorityQueue(initCapacity, (a, b) -> { + long difference = a.getTiming() - b.getTiming(); + if(difference == 0) + return 0; + return difference < 0 ? -1 : 1; + }); + } + + public boolean hasNext() + { + return !events.isEmpty(); + } + + public void executeNext() + { + InnerEvent first = events.poll(); + currentTime = first.getTiming(); + first.run(); + } + + public long getSimulationTime() + { + return currentTime; + } + + public void reset() + { + events.clear(); + currentTime = 0; + } + + /** + * Adds an Event to the {@link Timeline} + * @param function The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline. + * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time. + */ + public void addEvent(TimelineEventHandler function, int relativeTiming) + { + long timing = currentTime + relativeTiming; + events.add(new InnerEvent(function, new TimelineEvent(timing), timing)); + } + + private class InnerEvent + { + + private final long timing; + private final TimelineEventHandler function; + private final TimelineEvent event; + + /** + * Creates an {@link InnerEvent} + * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs + * @param timing Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed; + */ + InnerEvent(TimelineEventHandler function, TimelineEvent event, long timing) + { + this.function = function; + this.event = event; + this.timing = timing; + } + + public long getTiming() + { + return timing; + } + + public void run() + { + function.handle(event); + } + + } +} \ No newline at end of file diff --git a/era.mi/src/era/mi/logic/timeline/TimelineEvent.java b/era.mi/src/era/mi/logic/timeline/TimelineEvent.java new file mode 100644 index 00000000..6cec9079 --- /dev/null +++ b/era.mi/src/era/mi/logic/timeline/TimelineEvent.java @@ -0,0 +1,17 @@ +package era.mi.logic.timeline; + +public class TimelineEvent +{ + private final long timing; + + TimelineEvent(long timing) + { + super(); + this.timing = timing; + } + + public long getTiming() + { + return timing; + } +} \ No newline at end of file diff --git a/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java b/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java new file mode 100644 index 00000000..59a91c95 --- /dev/null +++ b/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java @@ -0,0 +1,6 @@ +package era.mi.logic.timeline; + +public interface TimelineEventHandler +{ + public void handle(TimelineEvent e); +} \ No newline at end of file diff --git a/era.mi/src/era/mi/logic/wires/WireArray.java b/era.mi/src/era/mi/logic/wires/WireArray.java new file mode 100644 index 00000000..cf960e08 --- /dev/null +++ b/era.mi/src/era/mi/logic/wires/WireArray.java @@ -0,0 +1,295 @@ +package era.mi.logic.wires; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import era.mi.logic.Bit; +import era.mi.logic.Simulation; + +/** + * Represents an array of wires that can store n bits of information. + * @author Fabian Stemmler + * + */ +public class WireArray +{ + private Bit[] values; + public final int travelTime; + private List observers = new ArrayList(); + public final int length; + private List inputs = new ArrayList(); + + public WireArray(int length, int travelTime) + { + if(length < 1) + throw new IllegalArgumentException("Tried to create an array of wires with length " + length + ", but a length of less than 1 makes no sense."); + this.length = length; + this.travelTime = travelTime; + initValues(); + } + + private void initValues() + { + values = new Bit[length]; + for(int i = 0; i < length; i++) + values[i] = Bit.Z; + } + + private void recalculateSingleInput() + { + WireArrayInput input = inputs.get(0); + if(!Arrays.equals(input.getValues(), values)) + { + System.arraycopy(input.getValues(), 0, values, 0, length); + notifyObservers(); + } + } + + private void recalculateMultipleInputs() + { + Iterator it = inputs.iterator(); + Bit[] newValues = it.next().values.clone(); + + while(it.hasNext()) + { + WireArrayInput input = it.next(); + Bit[] bits = input.getValues(); + for(int i = 0; i < length; i++) + { + if(Bit.Z.equals(bits[i]) || newValues[i].equals(bits[i])) + continue; + else if(Bit.Z.equals(newValues[i])) + newValues[i] = bits[i]; + else + newValues[i] = Bit.X; + } + } + + if(!Arrays.equals(newValues, values)) + { + notifyObservers(); + values = newValues; + } + } + + private void recalculate() + { + switch(inputs.size()) + { + case 0: + return; + case 1: + recalculateSingleInput(); + break; + default: + recalculateMultipleInputs(); + } + } + + /** + * The WireArray is interpreted as an unsigned integer with n bits. + * @return true if all bits are either Bit.ONE or Bit.ZERO (they do not all have to have the same value), not Bit.X or Bit.Z. false is returned otherwise. + * + * @author Fabian Stemmler + */ + public boolean hasNumericValue() + { + for(Bit b : values) + { + if(b != Bit.ZERO && b != Bit.ONE) + return false; + } + return true; + } + + /** + * The WireArray is interpreted as an unsigned integer with n bits. + * @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. + * + * @author Fabian Stemmler + */ + public long getUnsignedValue() + { + long val = 0; + long mask = 1; + for(int i = 0; i < length; i++) + { + switch(values[i]) + { + default: + case Z: + case X: + return 0; //TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0; Random number? + case ONE: + val |= mask; + break; + case ZERO: + } + mask = mask << 1; + } + return val; + } + + /** + * The WireArray is interpreted as a signed integer with n bits. + * @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. + * + * @author Fabian Stemmler + */ + public long getSignedValue() + { + long val = getUnsignedValue(); + long mask = 1 << (length - 1); + if((mask & val) != 0) + { + int shifts = 64 - length; + return (val << shifts) >> shifts; + } + return val; + } + + /** + * Included for convenient use on {@link WireArray}s of length 1. + * @return The value of bit 0. + * + * @author Fabian Stemmler + */ + public Bit getValue() + { + return getValue(0); + } + + /** + * + * @param index Index of the requested bit. + * @return The value of the indexed bit. + * + * @author Fabian Stemmler + */ + public Bit getValue(int index) + { + return values[index]; + } + + public Bit[] getValues(int start, int end) + { + int length = end - start; + Bit[] bits = new Bit[length]; + System.arraycopy(values, start, bits, 0, length); + return bits; + } + + + /** + * @return An array of length n containing the values of the n bits in the {@link WireArray}. Can be safely modified. + * + * @author Fabian Stemmler + */ + public Bit[] getValues() + { + return values.clone(); + } + + /** + * Adds an {@link WireArrayObserver}, who will be notified when the value of the {@link WireArray} is updated. + * @param ob The {@link WireArrayObserver} to be notified of changes. + * @return true if the given {@link WireArrayObserver} was not already registered, false otherwise + * + * @author Fabian Stemmler + */ + public boolean addObserver(WireArrayObserver ob) + { + return observers.add(ob); + } + + private void notifyObservers() + { + for(WireArrayObserver o : observers) + o.update(this); + } + + public WireArrayInput createInput() + { + return new WireArrayInput(this); + } + + private void registerInput(WireArrayInput toRegister) + { + inputs.add(toRegister); + } + + public class WireArrayInput { + public final WireArray owner; + private Bit[] values; + + private WireArrayInput(WireArray owner) { + super(); + this.owner = owner; + initValues(); + owner.registerInput(this); + } + + private void initValues() + { + values = new Bit[length]; + for(int i = 0; i < length; i++) + values[i] = Bit.Z; + } + + /** + * Sets the wires values. This takes up time, as specified by the {@link WireArray}s travel time. + * @param newValues The new values the wires should take on. + * + * @author Fabian Stemmler + */ + public void feedSignals(Bit... newValues) + { + if(newValues.length == length) + { + feedSignals(0, newValues); + } + else + throw new IllegalArgumentException("Attempted to input " + newValues.length + " bits instead of " + length + " bits."); + } + + /** + * Sets values of a subarray of wires. This takes up time, as specified by the {@link WireArray}s travel time. + * @param newValues The new values the wires should take on. + * @param startingBit The first index of the subarray of wires. + * + * @author Fabian Stemmler + */ + public void feedSignals(int startingBit, Bit... newValues) + { + Simulation.TIMELINE.addEvent((e) -> setValues(startingBit, newValues), travelTime); + } + + private void setValues(int startingBit, Bit... newValues) + { + int exclLastIndex = startingBit + newValues.length; + if(length < exclLastIndex) + throw new ArrayIndexOutOfBoundsException("Attempted to input bits from index " + startingBit + " to " + + exclLastIndex + " when there are only " + length + "wires."); + if(!Arrays.equals(values, startingBit, exclLastIndex, newValues, 0, newValues.length)) + { + System.arraycopy(newValues, 0, values, startingBit, newValues.length); + owner.recalculate(); + } + } + + public Bit[] getValues() + { + return values.clone(); + } + + public void clearSignals() + { + Bit[] bits = new Bit[length]; + for(int i = 0; i < length; i++) + bits[i] = Bit.Z; + feedSignals(bits); + } + } +} diff --git a/era.mi/src/era/mi/logic/wires/WireArrayObserver.java b/era.mi/src/era/mi/logic/wires/WireArrayObserver.java new file mode 100644 index 00000000..c0766a2b --- /dev/null +++ b/era.mi/src/era/mi/logic/wires/WireArrayObserver.java @@ -0,0 +1,6 @@ +package era.mi.logic.wires; + +public interface WireArrayObserver +{ + public void update(WireArray initiator); +}