From 8348742ffefa0642daf2061ff6258c821bafdf3f Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Sat, 25 May 2019 00:12:15 +0200 Subject: [PATCH] WireEnd functionality split into ReadEnd and ReadWriteEnd --- .../mi/logic/components/BasicComponent.java | 70 +- .../era/mi/logic/components/BitDisplay.java | 99 +- era.mi/src/era/mi/logic/components/Clock.java | 119 +-- .../era/mi/logic/components/Component.java | 7 +- .../era/mi/logic/components/Connector.java | 150 +-- era.mi/src/era/mi/logic/components/Demux.java | 13 +- .../era/mi/logic/components/ManualSwitch.java | 143 +-- .../src/era/mi/logic/components/Merger.java | 165 +-- era.mi/src/era/mi/logic/components/Mux.java | 187 ++-- .../src/era/mi/logic/components/Splitter.java | 100 +- .../mi/logic/components/TriStateBuffer.java | 13 +- .../mi/logic/components/gates/AndGate.java | 25 +- .../components/gates/MultiInputGate.java | 17 +- .../mi/logic/components/gates/NotGate.java | 97 +- .../era/mi/logic/components/gates/OrGate.java | 25 +- .../mi/logic/components/gates/XorGate.java | 37 +- .../src/era/mi/logic/tests/ComponentTest.java | 776 +++++++------- .../era/mi/logic/tests/TestBitDisplay.java | 94 +- era.mi/src/era/mi/logic/wires/Wire.java | 951 +++++++++--------- .../src/era/mi/logic/wires/WireObserver.java | 17 +- 20 files changed, 1584 insertions(+), 1521 deletions(-) diff --git a/era.mi/src/era/mi/logic/components/BasicComponent.java b/era.mi/src/era/mi/logic/components/BasicComponent.java index e6952838..f73e0f74 100644 --- a/era.mi/src/era/mi/logic/components/BasicComponent.java +++ b/era.mi/src/era/mi/logic/components/BasicComponent.java @@ -1,35 +1,35 @@ -package era.mi.logic.components; - -import era.mi.logic.Simulation; -import era.mi.logic.types.BitVector; -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.WireObserver; - -/** - * A basic component that recomputes all outputs (with a delay), when it is updated. - * - * @author Fabian Stemmler - */ -public abstract class BasicComponent implements WireObserver, Component -{ - 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(Wire initiator, BitVector oldValues) - { - Simulation.TIMELINE.addEvent(e -> compute(), processTime); - } - - protected abstract void compute(); -} +package era.mi.logic.components; + +import era.mi.logic.Simulation; +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.WireObserver; + +/** + * A basic component that recomputes all outputs (with a delay), when it is updated. + * + * @author Fabian Stemmler + */ +public abstract class BasicComponent implements WireObserver, Component +{ + 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(ReadEnd initiator, BitVector oldValues) + { + Simulation.TIMELINE.addEvent(e -> compute(), processTime); + } + + protected abstract void compute(); +} diff --git a/era.mi/src/era/mi/logic/components/BitDisplay.java b/era.mi/src/era/mi/logic/components/BitDisplay.java index 971eb7a7..28b51654 100644 --- a/era.mi/src/era/mi/logic/components/BitDisplay.java +++ b/era.mi/src/era/mi/logic/components/BitDisplay.java @@ -1,49 +1,50 @@ -package era.mi.logic.components; - -import java.util.List; - -import era.mi.logic.types.Bit; -import era.mi.logic.types.BitVector; -import era.mi.logic.wires.Wire.WireEnd; - -public class BitDisplay extends BasicComponent -{ - private final WireEnd in; - private BitVector displayedValue; - - public BitDisplay(WireEnd in) - { - super(1); - this.in = in; - in.addObserver(this); - compute(); - } - - @Override - protected void compute() - { - displayedValue = in.getValues(); - } - - public BitVector getDisplayedValue() - { - return displayedValue; - } - - public boolean isDisplaying(Bit... values) - { - return displayedValue.equals(BitVector.of(values)); - } - - @Override - public List getAllInputs() - { - return List.of(in); - } - - @Override - public List getAllOutputs() - { - return List.of(); - } -} +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.types.Bit; +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +public class BitDisplay extends BasicComponent +{ + private final ReadEnd in; + private BitVector displayedValue; + + public BitDisplay(ReadEnd in) + { + super(1); + this.in = in; + in.addObserver(this); + compute(); + } + + @Override + protected void compute() + { + displayedValue = in.getValues(); + } + + public BitVector getDisplayedValue() + { + return displayedValue; + } + + public boolean isDisplaying(Bit... values) + { + return displayedValue.equals(BitVector.of(values)); + } + + @Override + public List getAllInputs() + { + return List.of(in); + } + + @Override + public List getAllOutputs() + { + return List.of(); + } +} diff --git a/era.mi/src/era/mi/logic/components/Clock.java b/era.mi/src/era/mi/logic/components/Clock.java index 07419b46..b4528a41 100644 --- a/era.mi/src/era/mi/logic/components/Clock.java +++ b/era.mi/src/era/mi/logic/components/Clock.java @@ -1,59 +1,60 @@ -package era.mi.logic.components; - -import java.util.List; - -import era.mi.logic.Simulation; -import era.mi.logic.timeline.TimelineEvent; -import era.mi.logic.timeline.TimelineEventHandler; -import era.mi.logic.types.Bit; -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; - -public class Clock implements TimelineEventHandler, Component -{ - private boolean toggle = false; - private WireEnd out; - private int delta; - - /** - * - * @param out {@link Wire} the clock's impulses are fed into - * @param delta ticks between rising and falling edge - */ - public Clock(WireEnd out, int delta) - { - this.delta = delta; - this.out = out; - addToTimeline(); - } - - @Override - public void handle(TimelineEvent e) - { - addToTimeline(); - out.feedSignals(toggle ? Bit.ONE : Bit.ZERO); - toggle = !toggle; - } - - public WireEnd getOut() - { - return out; - } - - private void addToTimeline() - { - Simulation.TIMELINE.addEvent(this, delta); - } - - @Override - public List getAllInputs() - { - return List.of(); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.Simulation; +import era.mi.logic.timeline.TimelineEvent; +import era.mi.logic.timeline.TimelineEventHandler; +import era.mi.logic.types.Bit; +import era.mi.logic.wires.Wire; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +public class Clock implements TimelineEventHandler, Component +{ + private boolean toggle = false; + private ReadWriteEnd out; + private int delta; + + /** + * + * @param out {@link Wire} the clock's impulses are fed into + * @param delta ticks between rising and falling edge + */ + public Clock(ReadWriteEnd out, int delta) + { + this.delta = delta; + this.out = out; + addToTimeline(); + } + + @Override + public void handle(TimelineEvent e) + { + addToTimeline(); + out.feedSignals(toggle ? Bit.ONE : Bit.ZERO); + toggle = !toggle; + } + + public ReadWriteEnd getOut() + { + return out; + } + + private void addToTimeline() + { + Simulation.TIMELINE.addEvent(this, delta); + } + + @Override + public List getAllInputs() + { + return List.of(); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/era/mi/logic/components/Component.java b/era.mi/src/era/mi/logic/components/Component.java index 5e994e3e..d749a4bd 100644 --- a/era.mi/src/era/mi/logic/components/Component.java +++ b/era.mi/src/era/mi/logic/components/Component.java @@ -2,7 +2,8 @@ package era.mi.logic.components; import java.util.List; -import era.mi.logic.wires.Wire.WireEnd; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; public interface Component { @@ -11,10 +12,10 @@ public interface Component * Returns immutable list of all inputs to the {@link Component} (including e.g. the select bits to a MUX). Intended for visualization * in the UI. */ - public List getAllInputs(); + public List getAllInputs(); /** * Returns immutable list of all outputs to the {@link Component}. Intended for visualization in the UI. */ - public List getAllOutputs(); + public List getAllOutputs(); } diff --git a/era.mi/src/era/mi/logic/components/Connector.java b/era.mi/src/era/mi/logic/components/Connector.java index 89fb96eb..4f211bfe 100644 --- a/era.mi/src/era/mi/logic/components/Connector.java +++ b/era.mi/src/era/mi/logic/components/Connector.java @@ -1,75 +1,75 @@ -package era.mi.logic.components; - -import java.util.List; - -import era.mi.logic.Simulation; -import era.mi.logic.types.BitVector; -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; -import era.mi.logic.wires.WireObserver; - -public class Connector implements WireObserver, Component -{ - private boolean connected; - private final WireEnd a; - private final WireEnd b; - - public Connector(WireEnd a, WireEnd b) - { - if (a.length() != b.length()) - throw new IllegalArgumentException(String.format("WireArray width does not match: %d, %d", a.length(), b.length())); - this.a = a; - this.b = b; - a.addObserver(this); - b.addObserver(this); - } - - public void connect() - { - connected = true; - update(a.getWire()); - update(b.getWire()); - } - - public void disconnect() - { - connected = false; - a.clearSignals(); - b.clearSignals(); - } - - public void setConnection(boolean connected) - { - if (connected) - connect(); - else - disconnect(); - } - - @Override - public void update(Wire initiator, BitVector oldValues) - { - if (connected) - Simulation.TIMELINE.addEvent(e -> update(initiator), 1); - } - - private void update(Wire initiator) - { - if (initiator == a.getWire()) - b.feedSignals(a.wireValuesExcludingMe()); - else - a.feedSignals(b.wireValuesExcludingMe()); - } - - @Override - public List getAllInputs() - { - return List.of(a, b); - } - - @Override - public List getAllOutputs() - { - return List.of(a, b); - } -} +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.Simulation; +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; +import era.mi.logic.wires.WireObserver; + +public class Connector implements WireObserver, Component +{ + private boolean connected; + private final ReadWriteEnd a; + private final ReadWriteEnd b; + + public Connector(ReadWriteEnd a, ReadWriteEnd b) + { + if (a.length() != b.length()) + throw new IllegalArgumentException(String.format("WireArray width does not match: %d, %d", a.length(), b.length())); + this.a = a; + this.b = b; + a.addObserver(this); + b.addObserver(this); + } + + public void connect() + { + connected = true; + update(a); + update(b); + } + + public void disconnect() + { + connected = false; + a.clearSignals(); + b.clearSignals(); + } + + public void setConnection(boolean connected) + { + if (connected) + connect(); + else + disconnect(); + } + + @Override + public void update(ReadEnd initiator, BitVector oldValues) + { + if (connected) + Simulation.TIMELINE.addEvent(e -> update(initiator), 1); + } + + private void update(ReadEnd initiator) + { + if (initiator == a) + b.feedSignals(a.wireValuesExcludingMe()); + else + a.feedSignals(b.wireValuesExcludingMe()); + } + + @Override + public List getAllInputs() + { + return List.of(a, b); + } + + @Override + public List getAllOutputs() + { + return List.of(a, b); + } +} diff --git a/era.mi/src/era/mi/logic/components/Demux.java b/era.mi/src/era/mi/logic/components/Demux.java index ffc1bbad..0a48cb9e 100644 --- a/era.mi/src/era/mi/logic/components/Demux.java +++ b/era.mi/src/era/mi/logic/components/Demux.java @@ -3,7 +3,8 @@ package era.mi.logic.components; import java.util.List; import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; /** * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the @@ -14,8 +15,8 @@ import era.mi.logic.wires.Wire.WireEnd; */ public class Demux extends BasicComponent { - private final WireEnd select, in; - private final WireEnd[] outputs; + private final ReadEnd select, in; + private final ReadWriteEnd[] outputs; private final int outputSize; private int selected = -1; @@ -26,7 +27,7 @@ public class Demux extends BasicComponent * @param select Indexes the output array to which the input is mapped. Must have enough bits to index all outputs. * @param outputs One of these outputs receives the input signal, depending on the select bits */ - public Demux(int processTime, WireEnd in, WireEnd select, WireEnd... outputs) + public Demux(int processTime, ReadEnd in, ReadEnd select, ReadWriteEnd... outputs) { super(processTime); outputSize = in.length(); @@ -67,13 +68,13 @@ public class Demux extends BasicComponent } @Override - public List getAllInputs() + public List getAllInputs() { return List.of(in, select); } @Override - public List getAllOutputs() + public List getAllOutputs() { return List.of(outputs); } diff --git a/era.mi/src/era/mi/logic/components/ManualSwitch.java b/era.mi/src/era/mi/logic/components/ManualSwitch.java index 0ad4a76c..0b694a44 100644 --- a/era.mi/src/era/mi/logic/components/ManualSwitch.java +++ b/era.mi/src/era/mi/logic/components/ManualSwitch.java @@ -1,71 +1,72 @@ -package era.mi.logic.components; - -import java.util.List; - -import era.mi.logic.types.Bit; -import era.mi.logic.wires.Wire.WireEnd; - -/** - * This class models a simple on/off (ONE/ZERO) switch for user interaction. - * - * @author Christian Femers - * - */ -public class ManualSwitch implements Component -{ - private WireEnd output; - private boolean isOn; - - public ManualSwitch(WireEnd output) - { - if (output.length() != 1) - throw new IllegalArgumentException("Switch output can be only a single wire"); - this.output = output; - } - - public void switchOn() - { - setState(true); - } - - public void switchOff() - { - setState(false); - } - - public void toggle() - { - setState(!isOn); - } - - public void setState(boolean isOn) - { - if (this.isOn == isOn) - return; - this.isOn = isOn; - output.feedSignals(getValue()); - } - - public boolean isOn() - { - return isOn; - } - - public Bit getValue() - { - return isOn ? Bit.ONE : Bit.ZERO; - } - - @Override - public List getAllInputs() - { - return List.of(); - } - - @Override - public List getAllOutputs() - { - return List.of(output); - } - -} +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.types.Bit; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +/** + * This class models a simple on/off (ONE/ZERO) switch for user interaction. + * + * @author Christian Femers + * + */ +public class ManualSwitch implements Component +{ + private ReadWriteEnd output; + private boolean isOn; + + public ManualSwitch(ReadWriteEnd output) + { + if (output.length() != 1) + throw new IllegalArgumentException("Switch output can be only a single wire"); + this.output = output; + } + + public void switchOn() + { + setState(true); + } + + public void switchOff() + { + setState(false); + } + + public void toggle() + { + setState(!isOn); + } + + public void setState(boolean isOn) + { + if (this.isOn == isOn) + return; + this.isOn = isOn; + output.feedSignals(getValue()); + } + + public boolean isOn() + { + return isOn; + } + + public Bit getValue() + { + return isOn ? Bit.ONE : Bit.ZERO; + } + + @Override + public List getAllInputs() + { + return List.of(); + } + + @Override + public List getAllOutputs() + { + return List.of(output); + } + +} diff --git a/era.mi/src/era/mi/logic/components/Merger.java b/era.mi/src/era/mi/logic/components/Merger.java index 34ba217c..2c1ffcb3 100644 --- a/era.mi/src/era/mi/logic/components/Merger.java +++ b/era.mi/src/era/mi/logic/components/Merger.java @@ -1,82 +1,83 @@ -package era.mi.logic.components; - -import java.util.List; - -import era.mi.logic.types.BitVector; -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; -import era.mi.logic.wires.WireObserver; - -public class Merger implements WireObserver, Component -{ - private WireEnd out; - private WireEnd[] inputs; - private int[] beginningIndex; - - /** - * - * @param union The output of merging n {@link Wire}s into one. Must have length = a1.length() + a2.length() + ... + an.length(). - * @param inputs The inputs to be merged into the union - */ - public Merger(WireEnd union, WireEnd... 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 WireEnd getInput(int index) - { - return inputs[index]; - } - - public WireEnd getUnion() - { - return out; - } - - @Override - public void update(Wire initiator, BitVector oldValues) - { - int index = find(initiator); - int beginning = beginningIndex[index]; - out.feedSignals(beginning, inputs[index].getValues()); - } - - private int find(Wire w) - { - for (int i = 0; i < inputs.length; i++) - if (inputs[i].getWire() == w) - return i; - return -1; - } - - public WireEnd[] getInputs() - { - return inputs.clone(); - } - - @Override - public List getAllInputs() - { - return List.of(inputs); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; +import era.mi.logic.wires.WireObserver; + +public class Merger implements WireObserver, Component +{ + private ReadWriteEnd out; + private ReadEnd[] inputs; + private int[] beginningIndex; + + /** + * + * @param union The output of merging n {@link Wire}s into one. Must have length = a1.length() + a2.length() + ... + an.length(). + * @param inputs The inputs to be merged into the union + */ + public Merger(ReadWriteEnd union, ReadEnd... 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 ReadEnd getInput(int index) + { + return inputs[index]; + } + + public ReadEnd getUnion() + { + return out; + } + + @Override + public void update(ReadEnd initiator, BitVector oldValues) + { + int index = find(initiator); + int beginning = beginningIndex[index]; + out.feedSignals(beginning, inputs[index].getValues()); + } + + private int find(ReadEnd r) + { + for (int i = 0; i < inputs.length; i++) + if (inputs[i] == r) + return i; + return -1; + } + + public ReadEnd[] getInputs() + { + return inputs.clone(); + } + + @Override + public List getAllInputs() + { + return List.of(inputs); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/era/mi/logic/components/Mux.java b/era.mi/src/era/mi/logic/components/Mux.java index aea71166..17ad0dd8 100644 --- a/era.mi/src/era/mi/logic/components/Mux.java +++ b/era.mi/src/era/mi/logic/components/Mux.java @@ -1,93 +1,94 @@ -package era.mi.logic.components; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; - -/** - * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the - * output. - * - * @author Fabian Stemmler - * - */ -public class Mux extends BasicComponent -{ - private WireEnd select; - private WireEnd out; - private WireEnd[] inputs; - private final int outputSize; - - /** - * Input {@link Wire}s and out must be of uniform length - * - * @param out Must be of uniform length with all inputs. - * @param select Indexes the input array which is to be mapped to the output. Must have enough bits to index all inputs. - * @param inputs One of these inputs is mapped to the output, depending on the select bits - */ - public Mux(int processTime, WireEnd out, WireEnd select, WireEnd... inputs) - { - super(processTime); - outputSize = out.length(); - - this.inputs = inputs.clone(); - for (int i = 0; i < this.inputs.length; i++) - { - if (inputs[i].length() != outputSize) - throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!"); - inputs[i].addObserver(this); - } - - this.select = select; - select.addObserver(this); - - 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 + ")."); - - this.out = out; - } - - public WireEnd getOut() - { - return out; - } - - public WireEnd getSelect() - { - return select; - } - - @Override - public void compute() - { - int selectValue; - if (!select.hasNumericValue() || (selectValue = (int) select.getUnsignedValue()) >= inputs.length) - { - out.clearSignals(); - return; - } - - WireEnd active = inputs[selectValue]; - out.feedSignals(active.getValues()); - } - - @Override - public List getAllInputs() - { - ArrayList wires = new ArrayList(Arrays.asList(inputs)); - wires.add(select); - return Collections.unmodifiableList(wires); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} +package era.mi.logic.components; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import era.mi.logic.wires.Wire; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +/** + * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the + * output. + * + * @author Fabian Stemmler + * + */ +public class Mux extends BasicComponent +{ + private ReadEnd select; + private ReadWriteEnd out; + private ReadEnd[] inputs; + private final int outputSize; + + /** + * Input {@link Wire}s and out must be of uniform length + * + * @param out Must be of uniform length with all inputs. + * @param select Indexes the input array which is to be mapped to the output. Must have enough bits to index all inputs. + * @param inputs One of these inputs is mapped to the output, depending on the select bits + */ + public Mux(int processTime, ReadWriteEnd out, ReadEnd select, ReadEnd... inputs) + { + super(processTime); + outputSize = out.length(); + + this.inputs = inputs.clone(); + for (int i = 0; i < this.inputs.length; i++) + { + if (inputs[i].length() != outputSize) + throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!"); + inputs[i].addObserver(this); + } + + this.select = select; + select.addObserver(this); + + 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 + ")."); + + this.out = out; + } + + public ReadEnd getOut() + { + return out; + } + + public ReadEnd getSelect() + { + return select; + } + + @Override + public void compute() + { + int selectValue; + if (!select.hasNumericValue() || (selectValue = (int) select.getUnsignedValue()) >= inputs.length) + { + out.clearSignals(); + return; + } + + ReadEnd active = inputs[selectValue]; + out.feedSignals(active.getValues()); + } + + @Override + public List getAllInputs() + { + ArrayList wires = new ArrayList(Arrays.asList(inputs)); + wires.add(select); + return Collections.unmodifiableList(wires); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/era/mi/logic/components/Splitter.java b/era.mi/src/era/mi/logic/components/Splitter.java index 4764c27a..530ad62e 100644 --- a/era.mi/src/era/mi/logic/components/Splitter.java +++ b/era.mi/src/era/mi/logic/components/Splitter.java @@ -1,43 +1,57 @@ -package era.mi.logic.components; - -import era.mi.logic.types.BitVector; -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; -import era.mi.logic.wires.WireObserver; - -public class Splitter implements WireObserver -{ - private WireEnd input; - private WireEnd[] outputs; - - public Splitter(WireEnd input, WireEnd... outputs) - { - this.input = input; - this.outputs = outputs; - input.addObserver(this); - int length = 0; - for (WireEnd 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() - { - BitVector inputBits = input.getValues(); - int startIndex = 0; - for (int i = 0; i < outputs.length; i++) - { - outputs[i].feedSignals(inputBits.subVector(startIndex, startIndex + outputs[i].length())); - startIndex += outputs[i].length(); - } - } - - @Override - public void update(Wire initiator, BitVector oldValues) - { - compute(); - } -} +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; +import era.mi.logic.wires.WireObserver; + +public class Splitter implements WireObserver, Component +{ + private ReadEnd input; + private ReadWriteEnd[] outputs; + + public Splitter(ReadEnd input, ReadWriteEnd... outputs) + { + this.input = input; + this.outputs = outputs; + input.addObserver(this); + int length = 0; + for (ReadEnd 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() + { + BitVector inputBits = input.getValues(); + int startIndex = 0; + for (int i = 0; i < outputs.length; i++) + { + outputs[i].feedSignals(inputBits.subVector(startIndex, startIndex + outputs[i].length())); + startIndex += outputs[i].length(); + } + } + + @Override + public void update(ReadEnd initiator, BitVector oldValues) + { + compute(); + } + + @Override + public List getAllInputs() + { + return List.of(input); + } + + @Override + public List getAllOutputs() + { + return List.of(outputs); + } +} diff --git a/era.mi/src/era/mi/logic/components/TriStateBuffer.java b/era.mi/src/era/mi/logic/components/TriStateBuffer.java index 0ab162f1..77072325 100644 --- a/era.mi/src/era/mi/logic/components/TriStateBuffer.java +++ b/era.mi/src/era/mi/logic/components/TriStateBuffer.java @@ -3,14 +3,15 @@ package era.mi.logic.components; import java.util.List; import era.mi.logic.types.Bit; -import era.mi.logic.wires.Wire.WireEnd; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; public class TriStateBuffer extends BasicComponent { - WireEnd in, enable; - WireEnd out; + ReadEnd in, enable; + ReadWriteEnd out; - public TriStateBuffer(int processTime, WireEnd in, WireEnd out, WireEnd enable) + public TriStateBuffer(int processTime, ReadEnd in, ReadWriteEnd out, ReadEnd enable) { super(processTime); if (in.length() != out.length()) @@ -35,13 +36,13 @@ public class TriStateBuffer extends BasicComponent } @Override - public List getAllInputs() + public List getAllInputs() { return List.of(in, enable); } @Override - public List getAllOutputs() + public List getAllOutputs() { return List.of(out); } diff --git a/era.mi/src/era/mi/logic/components/gates/AndGate.java b/era.mi/src/era/mi/logic/components/gates/AndGate.java index 5da680e4..4d0726e6 100644 --- a/era.mi/src/era/mi/logic/components/gates/AndGate.java +++ b/era.mi/src/era/mi/logic/components/gates/AndGate.java @@ -1,12 +1,13 @@ -package era.mi.logic.components.gates; - -import era.mi.logic.types.BitVector.BitVectorMutator; -import era.mi.logic.wires.Wire.WireEnd; - -public class AndGate extends MultiInputGate -{ - public AndGate(int processTime, WireEnd out, WireEnd... in) - { - super(processTime, BitVectorMutator::and, out, in); - } -} +package era.mi.logic.components.gates; + +import era.mi.logic.types.BitVector.BitVectorMutator; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +public class AndGate extends MultiInputGate +{ + public AndGate(int processTime, ReadWriteEnd out, ReadEnd... in) + { + super(processTime, BitVectorMutator::and, out, in); + } +} diff --git a/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java b/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java index e76661d9..4c985b43 100644 --- a/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java +++ b/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java @@ -5,16 +5,17 @@ import java.util.List; import era.mi.logic.components.BasicComponent; import era.mi.logic.types.BitVector.BitVectorMutator; import era.mi.logic.types.MutationOperation; -import era.mi.logic.wires.Wire.WireEnd; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; public abstract class MultiInputGate extends BasicComponent { - protected WireEnd[] in; - protected WireEnd out; + protected ReadEnd[] in; + protected ReadWriteEnd out; protected final int length; protected MutationOperation op; - protected MultiInputGate(int processTime, MutationOperation op, WireEnd out, WireEnd... in) + protected MultiInputGate(int processTime, MutationOperation op, ReadWriteEnd out, ReadEnd... in) { super(processTime); this.op = op; @@ -22,7 +23,7 @@ public abstract class MultiInputGate extends BasicComponent this.in = in.clone(); if (in.length < 1) throw new IllegalArgumentException(String.format("Cannot create gate with %d wires.", in.length)); - for (WireEnd w : in) + for (ReadEnd w : in) { if (w.length() != length) throw new IllegalArgumentException("All wires connected to the gate must be of uniform length."); @@ -32,13 +33,13 @@ public abstract class MultiInputGate extends BasicComponent } @Override - public List getAllInputs() + public List getAllInputs() { return List.of(in); } @Override - public List getAllOutputs() + public List getAllOutputs() { return List.of(out); } @@ -47,7 +48,7 @@ public abstract class MultiInputGate extends BasicComponent protected void compute() { BitVectorMutator mutator = BitVectorMutator.empty(); - for (WireEnd w : in) + for (ReadEnd w : in) op.apply(mutator, w.getValues()); out.feedSignals(mutator.get()); } diff --git a/era.mi/src/era/mi/logic/components/gates/NotGate.java b/era.mi/src/era/mi/logic/components/gates/NotGate.java index 1c0d8330..ec1f8165 100644 --- a/era.mi/src/era/mi/logic/components/gates/NotGate.java +++ b/era.mi/src/era/mi/logic/components/gates/NotGate.java @@ -1,48 +1,49 @@ -package era.mi.logic.components.gates; - -import java.util.List; - -import era.mi.logic.components.BasicComponent; -import era.mi.logic.wires.Wire.WireEnd; - -public class NotGate extends BasicComponent -{ - private WireEnd in; - private WireEnd out; - - public NotGate(int processTime, WireEnd in, WireEnd out) - { - super(processTime); - this.in = in; - in.addObserver(this); - this.out = out; - } - - @Override - protected void compute() - { - out.feedSignals(in.getValues().not()); - } - - public WireEnd getIn() - { - return in; - } - - public WireEnd getOut() - { - return out; - } - - @Override - public List getAllInputs() - { - return List.of(in); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} +package era.mi.logic.components.gates; + +import java.util.List; + +import era.mi.logic.components.BasicComponent; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +public class NotGate extends BasicComponent +{ + private ReadEnd in; + private ReadWriteEnd out; + + public NotGate(int processTime, ReadEnd in, ReadWriteEnd out) + { + super(processTime); + this.in = in; + in.addObserver(this); + this.out = out; + } + + @Override + protected void compute() + { + out.feedSignals(in.getValues().not()); + } + + public ReadEnd getIn() + { + return in; + } + + public ReadEnd getOut() + { + return out; + } + + @Override + public List getAllInputs() + { + return List.of(in); + } + + @Override + public List getAllOutputs() + { + return List.of(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 index 8c1775fd..52dc9218 100644 --- a/era.mi/src/era/mi/logic/components/gates/OrGate.java +++ b/era.mi/src/era/mi/logic/components/gates/OrGate.java @@ -1,12 +1,13 @@ -package era.mi.logic.components.gates; - -import era.mi.logic.types.BitVector.BitVectorMutator; -import era.mi.logic.wires.Wire.WireEnd; - -public class OrGate extends MultiInputGate -{ - public OrGate(int processTime, WireEnd out, WireEnd... in) - { - super(processTime, BitVectorMutator::or, out, in); - } -} +package era.mi.logic.components.gates; + +import era.mi.logic.types.BitVector.BitVectorMutator; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +public class OrGate extends MultiInputGate +{ + public OrGate(int processTime, ReadWriteEnd out, ReadEnd... in) + { + super(processTime, BitVectorMutator::or, out, in); + } +} diff --git a/era.mi/src/era/mi/logic/components/gates/XorGate.java b/era.mi/src/era/mi/logic/components/gates/XorGate.java index 73a25568..d2414698 100644 --- a/era.mi/src/era/mi/logic/components/gates/XorGate.java +++ b/era.mi/src/era/mi/logic/components/gates/XorGate.java @@ -1,18 +1,19 @@ -package era.mi.logic.components.gates; - -import era.mi.logic.types.BitVector.BitVectorMutator; -import era.mi.logic.wires.Wire.WireEnd; - -/** - * Outputs 1 when the number of 1 inputs is odd. - * - * @author Fabian Stemmler - */ -public class XorGate extends MultiInputGate -{ - public XorGate(int processTime, WireEnd out, WireEnd... in) - { - super(processTime, BitVectorMutator::xor, out, in); - } - -} +package era.mi.logic.components.gates; + +import era.mi.logic.types.BitVector.BitVectorMutator; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +/** + * Outputs 1 when the number of 1 inputs is odd. + * + * @author Fabian Stemmler + */ +public class XorGate extends MultiInputGate +{ + public XorGate(int processTime, ReadWriteEnd out, ReadEnd... in) + { + super(processTime, BitVectorMutator::xor, out, in); + } + +} diff --git a/era.mi/src/era/mi/logic/tests/ComponentTest.java b/era.mi/src/era/mi/logic/tests/ComponentTest.java index 8a9bc5a4..805d2e58 100644 --- a/era.mi/src/era/mi/logic/tests/ComponentTest.java +++ b/era.mi/src/era/mi/logic/tests/ComponentTest.java @@ -1,384 +1,392 @@ -package era.mi.logic.tests; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.function.LongConsumer; - -import org.junit.jupiter.api.Test; - -import era.mi.logic.Simulation; -import era.mi.logic.components.Connector; -import era.mi.logic.components.Demux; -import era.mi.logic.components.Merger; -import era.mi.logic.components.Mux; -import era.mi.logic.components.Splitter; -import era.mi.logic.components.TriStateBuffer; -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.components.gates.XorGate; -import era.mi.logic.types.Bit; -import era.mi.logic.types.BitVector; -import era.mi.logic.wires.Wire; -import era.mi.logic.wires.Wire.WireEnd; - -@SuppressWarnings("unused") -class ComponentTest -{ - - @Test - void circuitExampleTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(1, 1), b = new Wire(1, 1), c = new Wire(1, 10), d = new Wire(2, 1), e = new Wire(1, 1), f = new Wire(1, 1), - g = new Wire(1, 1), h = new Wire(2, 1), i = new Wire(2, 1), j = new Wire(1, 1), k = new Wire(1, 1); - new AndGate(1, f.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd()); - new NotGate(1, f.createReadOnlyEnd(), g.createEnd()); - new Merger(h.createEnd(), c.createReadOnlyEnd(), g.createReadOnlyEnd()); - new Mux(1, i.createEnd(), e.createReadOnlyEnd(), h.createReadOnlyEnd(), d.createReadOnlyEnd()); - new Splitter(i.createReadOnlyEnd(), k.createEnd(), j.createEnd()); - - a.createEnd().feedSignals(Bit.ZERO); - b.createEnd().feedSignals(Bit.ONE); - c.createEnd().feedSignals(Bit.ZERO); - d.createEnd().feedSignals(Bit.ONE, Bit.ONE); - e.createEnd().feedSignals(Bit.ZERO); - - Simulation.TIMELINE.executeAll(); - - assertEquals(Bit.ONE, j.getValue()); - assertEquals(Bit.ZERO, k.getValue()); - } - - @Test - void splitterTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(3, 1), b = new Wire(2, 1), c = new Wire(3, 1), in = new Wire(8, 1); - in.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); - new Splitter(in.createReadOnlyEnd(), a.createEnd(), b.createEnd(), c.createEnd()); - - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(a.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO); - assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO); - assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE); - } - - @Test - void mergerTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(3, 1), b = new Wire(2, 1), c = new Wire(3, 1), out = new Wire(8, 1); - a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO); - b.createEnd().feedSignals(Bit.ONE, Bit.ZERO); - c.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); - - new Merger(out.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd()); - - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); - } - - @Test - void triStateBufferTest() - { - Wire a = new Wire(1, 1), b = new Wire(1, 1), en = new Wire(1, 1), notEn = new Wire(1, 1); - new NotGate(1, en.createReadOnlyEnd(), notEn.createEnd()); - new TriStateBuffer(1, a.createReadOnlyEnd(), b.createEnd(), en.createReadOnlyEnd()); - new TriStateBuffer(1, b.createReadOnlyEnd(), a.createEnd(), notEn.createReadOnlyEnd()); - - WireEnd enI = en.createEnd(), aI = a.createEnd(), bI = b.createEnd(); - enI.feedSignals(Bit.ONE); - aI.feedSignals(Bit.ONE); - bI.feedSignals(Bit.Z); - - Simulation.TIMELINE.executeAll(); - - assertEquals(Bit.ONE, b.getValue()); - - bI.feedSignals(Bit.ZERO); - - Simulation.TIMELINE.executeAll(); - - assertEquals(Bit.X, b.getValue()); - assertEquals(Bit.ONE, a.getValue()); - - aI.clearSignals(); - enI.feedSignals(Bit.ZERO); - - Simulation.TIMELINE.executeAll(); - - assertEquals(Bit.ZERO, a.getValue()); - - } - - @Test - void muxTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(4, 3), b = new Wire(4, 6), c = new Wire(4, 4), select = new Wire(2, 5), out = new Wire(4, 1); - WireEnd selectIn = select.createEnd(); - - selectIn.feedSignals(Bit.ZERO, Bit.ZERO); - a.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); - c.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); - - new Mux(1, out.createEnd(), select.createReadOnlyEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd()); - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(out.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); - selectIn.feedSignals(Bit.ZERO, Bit.ONE); - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); - - selectIn.feedSignals(Bit.ONE, Bit.ONE); - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(out.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); - - } - - @Test - void demuxTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(4, 3), b = new Wire(4, 6), c = new Wire(4, 4), select = new Wire(2, 5), in = new Wire(4, 1); - WireEnd selectIn = select.createEnd(); - - selectIn.feedSignals(Bit.ZERO, Bit.ZERO); - in.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); - - new Demux(1, in.createReadOnlyEnd(), select.createReadOnlyEnd(), a.createEnd(), b.createEnd(), c.createEnd()); - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(a.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); - assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); - assertBitArrayEquals(c.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); - selectIn.feedSignals(Bit.ZERO, Bit.ONE); - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); - assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); - assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); - - selectIn.feedSignals(Bit.ONE, Bit.ONE); - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); - assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); - assertBitArrayEquals(c.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); - - } - - @Test - void andTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(4, 1), b = new Wire(4, 3), c = new Wire(4, 1); - new AndGate(1, c.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd()); - a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO); - b.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); - - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(c.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ZERO); - } - - @Test - void orTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(4, 1), b = new Wire(4, 3), c = new Wire(4, 1); - new OrGate(1, c.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd()); - a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO); - b.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); - - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ONE); - } - - @Test - void xorTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(3, 1), b = new Wire(3, 2), c = new Wire(3, 1), d = new Wire(3, 1); - new XorGate(1, d.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd()); - a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE); - b.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); - c.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); - - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(d.getValues(), Bit.ZERO, Bit.ONE, Bit.ONE); - } - - @Test - void notTest() - { - Simulation.TIMELINE.reset(); - Wire a = new Wire(3, 1), b = new Wire(3, 2); - new NotGate(1, a.createReadOnlyEnd(), b.createEnd()); - a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE); - - Simulation.TIMELINE.executeAll(); - - assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO, Bit.ZERO); - } - - @Test - void rsLatchCircuitTest() - { - Simulation.TIMELINE.reset(); - Wire r = new Wire(1, 1), s = new Wire(1, 1), t1 = new Wire(1, 15), t2 = new Wire(1, 1), q = new Wire(1, 1), nq = new Wire(1, 1); - - new OrGate(1, t2.createEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd()); - new OrGate(1, t1.createEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd()); - new NotGate(1, t2.createReadOnlyEnd(), q.createEnd()); - new NotGate(1, t1.createReadOnlyEnd(), nq.createEnd()); - - WireEnd sIn = s.createEnd(), rIn = r.createEnd(); - - sIn.feedSignals(Bit.ONE); - rIn.feedSignals(Bit.ZERO); - - Simulation.TIMELINE.executeAll(); - - assertEquals(Bit.ONE, q.getValue()); - assertEquals(Bit.ZERO, nq.getValue()); - - sIn.feedSignals(Bit.ZERO); - - Simulation.TIMELINE.executeAll(); - assertEquals(Bit.ONE, q.getValue()); - assertEquals(Bit.ZERO, nq.getValue()); - - rIn.feedSignals(Bit.ONE); - - Simulation.TIMELINE.executeAll(); - - assertEquals(Bit.ZERO, q.getValue()); - assertEquals(Bit.ONE, nq.getValue()); - } - - @Test - void numericValueTest() - { - Simulation.TIMELINE.reset(); - - Wire a = new Wire(4, 1); - a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE); - - Simulation.TIMELINE.executeAll(); - - assertEquals(15, a.getUnsignedValue()); - assertEquals(-1, a.getSignedValue()); - } - - @Test - void multipleInputs() - { - Simulation.TIMELINE.reset(); - Wire w = new Wire(2, 1); - WireEnd wI1 = w.createEnd(), wI2 = w.createEnd(); - wI1.feedSignals(Bit.ONE, Bit.Z); - wI2.feedSignals(Bit.Z, Bit.X); - Simulation.TIMELINE.executeAll(); - assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.X); - - wI2.feedSignals(Bit.ZERO, Bit.Z); - Simulation.TIMELINE.executeAll(); - assertBitArrayEquals(w.getValues(), Bit.X, Bit.Z); - - wI2.feedSignals(Bit.Z, Bit.Z); - Simulation.TIMELINE.executeAll(); - assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z); - - wI2.feedSignals(Bit.ONE, Bit.Z); - w.addObserver((i, oldValues) -> fail("WireArray notified observer, although value did not change.")); - Simulation.TIMELINE.executeAll(); - assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z); - } - - @Test - void wireConnections() - { - // Nur ein Experiment, was über mehrere 'passive' Bausteine hinweg passieren würde - - Simulation.TIMELINE.reset(); - - Wire a = new Wire(1, 2); - Wire b = new Wire(1, 2); - Wire c = new Wire(1, 2); - WireEnd aI = a.createEnd(); - WireEnd bI = b.createEnd(); - WireEnd cI = c.createEnd(); - - TestBitDisplay test = new TestBitDisplay(c.createReadOnlyEnd()); - TestBitDisplay test2 = new TestBitDisplay(a.createReadOnlyEnd()); - LongConsumer print = time -> System.out.format("Time %2d\n a: %s\n b: %s\n c: %s\n", time, a, b, c); - - cI.feedSignals(Bit.ONE); - test.assertAfterSimulationIs(print, Bit.ONE); - - cI.feedSignals(Bit.X); - test.assertAfterSimulationIs(print, Bit.X); - - cI.feedSignals(Bit.X); - cI.feedSignals(Bit.Z); - test.assertAfterSimulationIs(print, Bit.Z); - - new Connector(b.createEnd(), c.createEnd()).connect(); - test.assertAfterSimulationIs(print, Bit.Z); - System.err.println("ONE"); - bI.feedSignals(Bit.ONE); - test.assertAfterSimulationIs(print, Bit.ONE); - System.err.println("ZERO"); - bI.feedSignals(Bit.ZERO); - test.assertAfterSimulationIs(print, Bit.ZERO); - System.err.println("Z"); - bI.feedSignals(Bit.Z); - test.assertAfterSimulationIs(print, Bit.Z); - - new Connector(a.createEnd(), b.createEnd()).connect(); - System.err.println("Z 2"); - aI.feedSignals(Bit.Z); - test.assertAfterSimulationIs(print, Bit.Z); - test2.assertAfterSimulationIs(Bit.Z); - System.err.println("ONE 2"); - aI.feedSignals(Bit.ONE); - test.assertAfterSimulationIs(print, Bit.ONE); - test2.assertAfterSimulationIs(Bit.ONE); - System.err.println("ZERO 2"); - aI.feedSignals(Bit.ZERO); - test.assertAfterSimulationIs(print, Bit.ZERO); - test2.assertAfterSimulationIs(Bit.ZERO); - System.err.println("Z 2 II"); - aI.feedSignals(Bit.Z); - test.assertAfterSimulationIs(print, Bit.Z); - test2.assertAfterSimulationIs(Bit.Z); - - System.err.println("No Conflict yet"); - bI.feedSignals(Bit.ONE); - test.assertAfterSimulationIs(print, Bit.ONE); - test2.assertAfterSimulationIs(Bit.ONE); - aI.feedSignals(Bit.ONE); - test.assertAfterSimulationIs(print, Bit.ONE); - test2.assertAfterSimulationIs(Bit.ONE); - System.err.println("Conflict"); - aI.feedSignals(Bit.ZERO); - test.assertAfterSimulationIs(print, Bit.X); - test2.assertAfterSimulationIs(Bit.X); - aI.feedSignals(Bit.ONE); - test.assertAfterSimulationIs(print, Bit.ONE); - test2.assertAfterSimulationIs(Bit.ONE); - } - - private static void assertBitArrayEquals(BitVector actual, Bit... expected) - { - assertArrayEquals(expected, actual.getBits()); - } -} +package era.mi.logic.tests; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.function.LongConsumer; + +import org.junit.jupiter.api.Test; + +import era.mi.logic.Simulation; +import era.mi.logic.components.Connector; +import era.mi.logic.components.Demux; +import era.mi.logic.components.Merger; +import era.mi.logic.components.Mux; +import era.mi.logic.components.Splitter; +import era.mi.logic.components.TriStateBuffer; +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.components.gates.XorGate; +import era.mi.logic.types.Bit; +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +class ComponentTest +{ + + @Test + void circuitExampleTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(1, 1), b = new Wire(1, 1), c = new Wire(1, 10), d = new Wire(2, 1), e = new Wire(1, 1), f = new Wire(1, 1), + g = new Wire(1, 1), h = new Wire(2, 1), i = new Wire(2, 1), j = new Wire(1, 1), k = new Wire(1, 1); + new AndGate(1, f.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd()); + new NotGate(1, f.createReadOnlyEnd(), g.createEnd()); + new Merger(h.createEnd(), c.createReadOnlyEnd(), g.createReadOnlyEnd()); + new Mux(1, i.createEnd(), e.createReadOnlyEnd(), h.createReadOnlyEnd(), d.createReadOnlyEnd()); + new Splitter(i.createReadOnlyEnd(), k.createEnd(), j.createEnd()); + + a.createEnd().feedSignals(Bit.ZERO); + b.createEnd().feedSignals(Bit.ONE); + c.createEnd().feedSignals(Bit.ZERO); + d.createEnd().feedSignals(Bit.ONE, Bit.ONE); + e.createEnd().feedSignals(Bit.ZERO); + + Simulation.TIMELINE.executeAll(); + + assertEquals(Bit.ONE, j.getValue()); + assertEquals(Bit.ZERO, k.getValue()); + } + + @Test + void splitterTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(3, 1), b = new Wire(2, 1), c = new Wire(3, 1), in = new Wire(8, 1); + in.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + new Splitter(in.createReadOnlyEnd(), a.createEnd(), b.createEnd(), c.createEnd()); + + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(a.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO); + assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO); + assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE); + } + + @Test + void mergerTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(3, 1), b = new Wire(2, 1), c = new Wire(3, 1), out = new Wire(8, 1); + a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO); + b.createEnd().feedSignals(Bit.ONE, Bit.ZERO); + c.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); + + new Merger(out.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd()); + + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + } + + @Test + void triStateBufferTest() + { + Wire a = new Wire(1, 1), b = new Wire(1, 1), en = new Wire(1, 1), notEn = new Wire(1, 1); + new NotGate(1, en.createReadOnlyEnd(), notEn.createEnd()); + new TriStateBuffer(1, a.createReadOnlyEnd(), b.createEnd(), en.createReadOnlyEnd()); + new TriStateBuffer(1, b.createReadOnlyEnd(), a.createEnd(), notEn.createReadOnlyEnd()); + + ReadWriteEnd enI = en.createEnd(), aI = a.createEnd(), bI = b.createEnd(); + enI.feedSignals(Bit.ONE); + aI.feedSignals(Bit.ONE); + bI.feedSignals(Bit.Z); + + Simulation.TIMELINE.executeAll(); + + assertEquals(Bit.ONE, b.getValue()); + + bI.feedSignals(Bit.ZERO); + + Simulation.TIMELINE.executeAll(); + + assertEquals(Bit.X, b.getValue()); + assertEquals(Bit.ONE, a.getValue()); + + aI.clearSignals(); + enI.feedSignals(Bit.ZERO); + + Simulation.TIMELINE.executeAll(); + + assertEquals(Bit.ZERO, a.getValue()); + + } + + @Test + void muxTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(4, 3), b = new Wire(4, 6), c = new Wire(4, 4), select = new Wire(2, 5), out = new Wire(4, 1); + ReadWriteEnd selectIn = select.createEnd(); + + selectIn.feedSignals(Bit.ZERO, Bit.ZERO); + a.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); + c.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + + new Mux(1, out.createEnd(), select.createReadOnlyEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd()); + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(out.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); + selectIn.feedSignals(Bit.ZERO, Bit.ONE); + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + + selectIn.feedSignals(Bit.ONE, Bit.ONE); + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(out.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); + + } + + @Test + void demuxTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(4, 3), b = new Wire(4, 6), c = new Wire(4, 4), select = new Wire(2, 5), in = new Wire(4, 1); + ReadWriteEnd selectIn = select.createEnd(); + + selectIn.feedSignals(Bit.ZERO, Bit.ZERO); + in.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); + + new Demux(1, in.createReadOnlyEnd(), select.createReadOnlyEnd(), a.createEnd(), b.createEnd(), c.createEnd()); + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(a.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); + assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); + assertBitArrayEquals(c.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); + selectIn.feedSignals(Bit.ZERO, Bit.ONE); + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); + assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); + assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO); + + selectIn.feedSignals(Bit.ONE, Bit.ONE); + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); + assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U); + assertBitArrayEquals(c.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z); + + } + + @Test + void andTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(4, 1), b = new Wire(4, 3), c = new Wire(4, 1); + new AndGate(1, c.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd()); + a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO); + b.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(c.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ZERO); + } + + @Test + void orTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(4, 1), b = new Wire(4, 3), c = new Wire(4, 1); + new OrGate(1, c.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd()); + a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO); + b.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE); + + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ONE); + } + + @Test + void xorTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(3, 1), b = new Wire(3, 2), c = new Wire(3, 1), d = new Wire(3, 1); + new XorGate(1, d.createEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd()); + a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE); + b.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); + c.createEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE); + + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(d.getValues(), Bit.ZERO, Bit.ONE, Bit.ONE); + } + + @Test + void notTest() + { + Simulation.TIMELINE.reset(); + Wire a = new Wire(3, 1), b = new Wire(3, 2); + new NotGate(1, a.createReadOnlyEnd(), b.createEnd()); + a.createEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE); + + Simulation.TIMELINE.executeAll(); + + assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO, Bit.ZERO); + } + + @Test + void rsLatchCircuitTest() + { + Simulation.TIMELINE.reset(); + Wire r = new Wire(1, 1), s = new Wire(1, 1), t1 = new Wire(1, 15), t2 = new Wire(1, 1), q = new Wire(1, 1), nq = new Wire(1, 1); + + new OrGate(1, t2.createEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd()); + new OrGate(1, t1.createEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd()); + new NotGate(1, t2.createReadOnlyEnd(), q.createEnd()); + new NotGate(1, t1.createReadOnlyEnd(), nq.createEnd()); + + ReadWriteEnd sIn = s.createEnd(), rIn = r.createEnd(); + + sIn.feedSignals(Bit.ONE); + rIn.feedSignals(Bit.ZERO); + + Simulation.TIMELINE.executeAll(); + + assertEquals(Bit.ONE, q.getValue()); + assertEquals(Bit.ZERO, nq.getValue()); + + sIn.feedSignals(Bit.ZERO); + + Simulation.TIMELINE.executeAll(); + assertEquals(Bit.ONE, q.getValue()); + assertEquals(Bit.ZERO, nq.getValue()); + + rIn.feedSignals(Bit.ONE); + + Simulation.TIMELINE.executeAll(); + + assertEquals(Bit.ZERO, q.getValue()); + assertEquals(Bit.ONE, nq.getValue()); + } + + @Test + void numericValueTest() + { + Simulation.TIMELINE.reset(); + + Wire a = new Wire(4, 1); + a.createEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE); + + Simulation.TIMELINE.executeAll(); + + assertEquals(15, a.getUnsignedValue()); + assertEquals(-1, a.getSignedValue()); + } + + @Test + void multipleInputs() + { + Simulation.TIMELINE.reset(); + Wire w = new Wire(2, 1); + ReadWriteEnd wI1 = w.createEnd(), wI2 = w.createEnd(); + wI1.feedSignals(Bit.ONE, Bit.Z); + wI2.feedSignals(Bit.Z, Bit.X); + Simulation.TIMELINE.executeAll(); + assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.X); + + wI2.feedSignals(Bit.ZERO, Bit.Z); + Simulation.TIMELINE.executeAll(); + assertBitArrayEquals(w.getValues(), Bit.X, Bit.Z); + + wI2.feedSignals(Bit.Z, Bit.Z); + Simulation.TIMELINE.executeAll(); + assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z); + + wI2.feedSignals(Bit.ONE, Bit.Z); + ReadEnd rE = w.createReadOnlyEnd(); + rE.addObserver((i, oldValues) -> fail("WireEnd notified observer, although value did not change.")); + Simulation.TIMELINE.executeAll(); + rE.close(); + wI1.feedSignals(Bit.X, Bit.X); + Simulation.TIMELINE.executeAll(); + wI1.addObserver((i, oldValues) -> fail("WireEnd notified observer, although it was closed.")); + wI1.close(); + assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z); + } + + @Test + void wireConnections() + { + // Nur ein Experiment, was über mehrere 'passive' Bausteine hinweg passieren würde + + Simulation.TIMELINE.reset(); + + Wire a = new Wire(1, 2); + Wire b = new Wire(1, 2); + Wire c = new Wire(1, 2); + ReadWriteEnd aI = a.createEnd(); + ReadWriteEnd bI = b.createEnd(); + ReadWriteEnd cI = c.createEnd(); + + TestBitDisplay test = new TestBitDisplay(c.createReadOnlyEnd()); + TestBitDisplay test2 = new TestBitDisplay(a.createReadOnlyEnd()); + LongConsumer print = time -> System.out.format("Time %2d\n a: %s\n b: %s\n c: %s\n", time, a, b, c); + + cI.feedSignals(Bit.ONE); + test.assertAfterSimulationIs(print, Bit.ONE); + + cI.feedSignals(Bit.X); + test.assertAfterSimulationIs(print, Bit.X); + + cI.feedSignals(Bit.X); + cI.feedSignals(Bit.Z); + test.assertAfterSimulationIs(print, Bit.Z); + + new Connector(b.createEnd(), c.createEnd()).connect(); + test.assertAfterSimulationIs(print, Bit.Z); + System.err.println("ONE"); + bI.feedSignals(Bit.ONE); + test.assertAfterSimulationIs(print, Bit.ONE); + System.err.println("ZERO"); + bI.feedSignals(Bit.ZERO); + test.assertAfterSimulationIs(print, Bit.ZERO); + System.err.println("Z"); + bI.feedSignals(Bit.Z); + test.assertAfterSimulationIs(print, Bit.Z); + + new Connector(a.createEnd(), b.createEnd()).connect(); + System.err.println("Z 2"); + aI.feedSignals(Bit.Z); + test.assertAfterSimulationIs(print, Bit.Z); + test2.assertAfterSimulationIs(Bit.Z); + System.err.println("ONE 2"); + aI.feedSignals(Bit.ONE); + test.assertAfterSimulationIs(print, Bit.ONE); + test2.assertAfterSimulationIs(Bit.ONE); + System.err.println("ZERO 2"); + aI.feedSignals(Bit.ZERO); + test.assertAfterSimulationIs(print, Bit.ZERO); + test2.assertAfterSimulationIs(Bit.ZERO); + System.err.println("Z 2 II"); + aI.feedSignals(Bit.Z); + test.assertAfterSimulationIs(print, Bit.Z); + test2.assertAfterSimulationIs(Bit.Z); + + System.err.println("No Conflict yet"); + bI.feedSignals(Bit.ONE); + test.assertAfterSimulationIs(print, Bit.ONE); + test2.assertAfterSimulationIs(Bit.ONE); + aI.feedSignals(Bit.ONE); + test.assertAfterSimulationIs(print, Bit.ONE); + test2.assertAfterSimulationIs(Bit.ONE); + System.err.println("Conflict"); + aI.feedSignals(Bit.ZERO); + test.assertAfterSimulationIs(print, Bit.X); + test2.assertAfterSimulationIs(Bit.X); + aI.feedSignals(Bit.ONE); + test.assertAfterSimulationIs(print, Bit.ONE); + test2.assertAfterSimulationIs(Bit.ONE); + } + + private static void assertBitArrayEquals(BitVector actual, Bit... expected) + { + assertArrayEquals(expected, actual.getBits()); + } +} diff --git a/era.mi/src/era/mi/logic/tests/TestBitDisplay.java b/era.mi/src/era/mi/logic/tests/TestBitDisplay.java index cb0494e1..b9de0cc2 100644 --- a/era.mi/src/era/mi/logic/tests/TestBitDisplay.java +++ b/era.mi/src/era/mi/logic/tests/TestBitDisplay.java @@ -1,47 +1,47 @@ -package era.mi.logic.tests; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -import java.util.function.LongConsumer; - -import era.mi.logic.Simulation; -import era.mi.logic.components.BitDisplay; -import era.mi.logic.types.Bit; -import era.mi.logic.wires.Wire.WireEnd; - -public final class TestBitDisplay extends BitDisplay -{ - - public TestBitDisplay(WireEnd in) - { - super(in); - } - - public void assertDisplays(Bit... expected) - { - assertArrayEquals(expected, getDisplayedValue().getBits()); - } - - public void assertAfterSimulationIs(Bit... expected) - { - Simulation.TIMELINE.executeAll(); - assertDisplays(expected); - } - - public void assertAfterSimulationIs(LongConsumer r, Bit... expected) - { - while (Simulation.TIMELINE.hasNext()) - { - Simulation.TIMELINE.executeNext(); - r.accept(Simulation.TIMELINE.getSimulationTime()); - } - assertDisplays(expected); - } - - @Override - protected void compute() - { - super.compute(); - System.out.println("update: value is " + getDisplayedValue()); - } -} +package era.mi.logic.tests; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.function.LongConsumer; + +import era.mi.logic.Simulation; +import era.mi.logic.components.BitDisplay; +import era.mi.logic.types.Bit; +import era.mi.logic.wires.Wire.ReadEnd; + +public final class TestBitDisplay extends BitDisplay +{ + + public TestBitDisplay(ReadEnd in) + { + super(in); + } + + public void assertDisplays(Bit... expected) + { + assertArrayEquals(expected, getDisplayedValue().getBits()); + } + + public void assertAfterSimulationIs(Bit... expected) + { + Simulation.TIMELINE.executeAll(); + assertDisplays(expected); + } + + public void assertAfterSimulationIs(LongConsumer r, Bit... expected) + { + while (Simulation.TIMELINE.hasNext()) + { + Simulation.TIMELINE.executeNext(); + r.accept(Simulation.TIMELINE.getSimulationTime()); + } + assertDisplays(expected); + } + + @Override + protected void compute() + { + super.compute(); + System.out.println("update: value is " + getDisplayedValue()); + } +} diff --git a/era.mi/src/era/mi/logic/wires/Wire.java b/era.mi/src/era/mi/logic/wires/Wire.java index a7b96eee..5eaf27ab 100644 --- a/era.mi/src/era/mi/logic/wires/Wire.java +++ b/era.mi/src/era/mi/logic/wires/Wire.java @@ -1,463 +1,490 @@ -package era.mi.logic.wires; - -import static era.mi.logic.types.Bit.*; - -import java.util.ArrayList; -import java.util.List; - -import era.mi.logic.Simulation; -import era.mi.logic.types.Bit; -import era.mi.logic.types.BitVector; -import era.mi.logic.types.BitVector.BitVectorMutator; - -/** - * Represents an array of wires that can store n bits of information. - * - * @author Fabian Stemmler - * - */ -public class Wire -{ - private BitVector values; - public final int travelTime; - private List observers = new ArrayList(); - public final int length; - private List inputs = new ArrayList(); - - public Wire(int length, int travelTime) - { - if (length < 1) - throw new IllegalArgumentException( - String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length)); - this.length = length; - this.travelTime = travelTime; - initValues(); - } - - private void initValues() - { - values = U.toVector(length); - } - - private void recalculateSingleInput() - { - setNewValues(inputs.get(0).getInputValues()); - } - - private void recalculateMultipleInputs() - { - BitVectorMutator mutator = BitVectorMutator.empty(); - for (WireEnd wireArrayEnd : inputs) - mutator.join(wireArrayEnd.getInputValues()); - setNewValues(mutator.get()); - } - - private void setNewValues(BitVector newValues) - { - if (values.equals(newValues)) - return; - BitVector oldValues = values; - values = newValues; - notifyObservers(oldValues); - } - - private void recalculate() - { - switch (inputs.size()) - { - case 0: - return; - case 1: - recalculateSingleInput(); - break; - default: - recalculateMultipleInputs(); - } - } - - /** - * The {@link Wire} 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 {@link Wire} is interpreted as an unsigned integer with n bits. - * - * @return The unsigned value of the {@link Wire}'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 (Bit bit : values) - { - switch (bit) - { - 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 {@link Wire} is interpreted as a signed integer with n bits. - * - * @return The signed value of the {@link Wire}'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; - } - - public Bit getValue() - { - return getValue(0); - } - - public Bit getValue(int index) - { - return values.getBit(index); - } - - public BitVector getValues(int start, int end) - { - return values.subVector(start, end); - } - - public BitVector getValues() - { - return values; - } - - /** - * Adds an {@link WireObserver}, who will be notified when the value of the {@link Wire} is updated. - * - * @param ob The {@link WireObserver} to be notified of changes. - * @return true if the given {@link WireObserver} was not already registered, false otherwise - * - * @author Fabian Stemmler - */ - public boolean addObserver(WireObserver ob) - { - return observers.add(ob); - } - - private void notifyObservers(BitVector oldValues) - { - for (WireObserver o : observers) - o.update(this, oldValues); - } - - /** - * Create and register a {@link WireEnd} object, which is tied to this {@link Wire}. - */ - public WireEnd createEnd() - { - return new WireEnd(false); - } - - /** - * Create a {@link WireEnd} object, which is tied to this {@link Wire}. This {@link WireEnd} cannot written to. - */ - public WireEnd createReadOnlyEnd() - { - return new WireEnd(true); - } - - private void registerInput(WireEnd toRegister) - { - inputs.add(toRegister); - } - - /** - * A {@link WireEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the - * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0 - * and 1 turn into X when they are mixed - * - * @author Fabian Stemmler - */ - public class WireEnd - { - private boolean open; - private BitVector inputValues; - - private WireEnd(boolean readOnly) - { - super(); - open = !readOnly; // TODO: that makes sense, doesn't it? - initValues(); - if (!readOnly) - registerInput(this); - } - - private void initValues() - { - inputValues = U.toVector(length); - } - - /** - * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time. - * - * @param newValues The new values the wires should take on. - * - * @author Fabian Stemmler - */ - public void feedSignals(Bit... newValues) - { - feedSignals(BitVector.of(newValues)); - } - - public void feedSignals(BitVector newValues) - { - if (newValues.length() != length) - throw new IllegalArgumentException( - String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length)); - if (!open) - throw new RuntimeException("Attempted to write to closed WireArrayEnd."); - Simulation.TIMELINE.addEvent(e -> setValues(newValues), travelTime); - } - - /** - * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time. - * - * @param bitVector 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, BitVector bitVector) - { - if (!open) - throw new RuntimeException("Attempted to write to closed WireArrayEnd."); - Simulation.TIMELINE.addEvent(e -> setValues(startingBit, bitVector), travelTime); - } - - private void setValues(int startingBit, BitVector newValues) - { - // index check covered in equals - if (!inputValues.equalsWithOffset(newValues, startingBit)) - { - Bit[] vals = inputValues.getBits(); - System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length()); - inputValues = BitVector.of(vals); - Wire.this.recalculate(); - } - } - - private void setValues(BitVector newValues) - { - if (inputValues.equals(newValues)) - return; - inputValues = newValues; - Wire.this.recalculate(); - } - - /** - * @return The value (of bit 0) the {@link WireEnd} is currently feeding into the associated {@link Wire}. - */ - public Bit getInputValue() - { - return getInputValue(0); - } - - /** - * @return The value which the {@link WireEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}. - */ - public Bit getInputValue(int index) - { - return inputValues.getBit(index); - } - - /** - * @return A copy (safe to modify) of the values the {@link WireEnd} is currently feeding into the associated {@link Wire}. - */ - public BitVector getInputValues() - { - return getInputValues(0, length); - } - - public BitVector getInputValues(int start, int end) - { - return inputValues.subVector(start, end); - } - - /** - * {@link WireEnd} now feeds Z into the associated {@link Wire}. - */ - public void clearSignals() - { - feedSignals(Z.toVector(length)); - } - - public BitVector wireValuesExcludingMe() - { - BitVectorMutator mutator = BitVectorMutator.empty(); - for (WireEnd wireEnd : inputs) - { - if (wireEnd == this) - continue; - mutator.join(wireEnd.inputValues); - } - return mutator.get(); - } - - /** - * Included for convenient use on {@link Wire}s of length 1. - * - * @return The value of bit 0. - * - * @author Fabian Stemmler - */ - public Bit getValue() - { - return Wire.this.getValue(); - } - - /** - * @param index Index of the requested bit. - * @return The value of the indexed bit. - * - * @author Fabian Stemmler - */ - public Bit getValue(int index) - { - return Wire.this.getValue(index); - } - - /** - * @param index Index of the requested bit. - * @return The value of the indexed bit. - * - * @author Fabian Stemmler - */ - public BitVector getValues() - { - return Wire.this.getValues(); - } - - /** - * @param start Start of the wanted segment. (inclusive) - * @param end End of the wanted segment. (exclusive) - * @return The values of the segment of {@link Bit}s indexed. - * - * @author Fabian Stemmler - */ - public BitVector getValues(int start, int end) - { - return Wire.this.getValues(start, end); - } - - /** - * The {@link Wire} 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() - { - return Wire.this.hasNumericValue(); - } - - /** - * The {@link Wire} is interpreted as an unsigned integer with n bits. - * - * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on. - * - * @author Fabian Stemmler - */ - public long getUnsignedValue() - { - return Wire.this.getUnsignedValue(); - } - - /** - * The {@link Wire} is interpreted as a signed integer with n bits. - * - * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on. - * - * @author Fabian Stemmler - */ - public long getSignedValue() - { - return Wire.this.getSignedValue(); - } - - @Override - public String toString() - { - return inputValues.toString(); - // return String.format("%s \nFeeding: %s", WireArray.this.toString(), Arrays.toString(inputValues)); - } - - public void close() - { - inputs.remove(this); - open = false; - } - - public int length() - { - return length; - } - - public boolean addObserver(WireObserver ob) - { - return Wire.this.addObserver(ob); - } - - public Wire getWire() - { - return Wire.this; - } - } - - @Override - public String toString() - { - return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs); - // Arrays.toString(values), inputs.stream().map(i -> Arrays.toString(i.inputValues)).reduce((s1, s2) -> s1 + s2) - } - - public static WireEnd[] extractEnds(Wire[] w) - { - WireEnd[] inputs = new WireEnd[w.length]; - for (int i = 0; i < w.length; i++) - inputs[i] = w[i].createEnd(); - return inputs; - } +package era.mi.logic.wires; + +import static era.mi.logic.types.Bit.U; +import static era.mi.logic.types.Bit.Z; + +import java.util.ArrayList; +import java.util.List; + +import era.mi.logic.Simulation; +import era.mi.logic.types.Bit; +import era.mi.logic.types.BitVector; +import era.mi.logic.types.BitVector.BitVectorMutator; + +/** + * Represents an array of wires that can store n bits of information. + * + * @author Fabian Stemmler + * + */ +public class Wire +{ + private BitVector values; + public final int travelTime; + private List attached = new ArrayList(); + public final int length; + private List inputs = new ArrayList(); + + public Wire(int length, int travelTime) + { + if (length < 1) + throw new IllegalArgumentException( + String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length)); + this.length = length; + this.travelTime = travelTime; + initValues(); + } + + private void initValues() + { + values = U.toVector(length); + } + + private void recalculateSingleInput() + { + setNewValues(inputs.get(0).getInputValues()); + } + + private void recalculateMultipleInputs() + { + BitVectorMutator mutator = BitVectorMutator.empty(); + for (ReadWriteEnd wireArrayEnd : inputs) + mutator.join(wireArrayEnd.getInputValues()); + setNewValues(mutator.get()); + } + + private void setNewValues(BitVector newValues) + { + if (values.equals(newValues)) + return; + BitVector oldValues = values; + values = newValues; + notifyObservers(oldValues); + } + + private void recalculate() + { + switch (inputs.size()) + { + case 0: + return; + case 1: + recalculateSingleInput(); + break; + default: + recalculateMultipleInputs(); + } + } + + /** + * The {@link Wire} 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 {@link Wire} is interpreted as an unsigned integer with n bits. + * + * @return The unsigned value of the {@link Wire}'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 (Bit bit : values) + { + switch (bit) + { + default: + case Z: + case X: + return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0; + case ONE: + val |= mask; + break; + case ZERO: + } + mask = mask << 1; + } + return val; + } + + /** + * The {@link Wire} is interpreted as a signed integer with n bits. + * + * @return The signed value of the {@link Wire}'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; + } + + public Bit getValue() + { + return getValue(0); + } + + public Bit getValue(int index) + { + return values.getBit(index); + } + + public BitVector getValues(int start, int end) + { + return values.subVector(start, end); + } + + public BitVector getValues() + { + return values; + } + + /** + * Adds an {@link WireObserver}, who will be notified when the value of the {@link Wire} is updated. + * + * @param ob The {@link WireObserver} to be notified of changes. + * @return true if the given {@link WireObserver} was not already registered, false otherwise + * + * @author Fabian Stemmler + */ + private void attachEnd(ReadEnd end) + { + attached.add(end); + } + + private void detachEnd(ReadEnd end) + { + attached.remove(end); + } + + private void notifyObservers(BitVector oldValues) + { + for (ReadEnd o : attached) + o.update(oldValues); + } + + /** + * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link Wire}. This {@link ReadWriteEnd} can be written to. + */ + public ReadWriteEnd createEnd() + { + return new ReadWriteEnd(); + } + + /** + * Create a {@link ReadEnd} object, which is tied to this {@link Wire}. This {@link ReadEnd} cannot be written to. + */ + public ReadEnd createReadOnlyEnd() + { + return new ReadEnd(); + } + + private void registerInput(ReadWriteEnd toRegister) + { + inputs.add(toRegister); + } + + /** + * A {@link ReadEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the + * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0 + * and 1 turn into X when they are mixed + * + * @author Fabian Stemmler + */ + public class ReadEnd + { + private List observers = new ArrayList(); + + private ReadEnd() + { + super(); + Wire.this.attachEnd(this); + } + + public void update(BitVector oldValues) + { + for (WireObserver ob : observers) + ob.update(this, oldValues); + } + + /** + * Included for convenient use on {@link Wire}s of length 1. + * + * @return The value of bit 0. + * + * @author Fabian Stemmler + */ + public Bit getValue() + { + return Wire.this.getValue(); + } + + /** + * @param index Index of the requested bit. + * @return The value of the indexed bit. + * + * @author Fabian Stemmler + */ + public Bit getValue(int index) + { + return Wire.this.getValue(index); + } + + /** + * @param index Index of the requested bit. + * @return The value of the indexed bit. + * + * @author Fabian Stemmler + */ + public BitVector getValues() + { + return Wire.this.getValues(); + } + + /** + * @param start Start of the wanted segment. (inclusive) + * @param end End of the wanted segment. (exclusive) + * @return The values of the segment of {@link Bit}s indexed. + * + * @author Fabian Stemmler + */ + public BitVector getValues(int start, int end) + { + return Wire.this.getValues(start, end); + } + + /** + * The {@link Wire} 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() + { + return Wire.this.hasNumericValue(); + } + + /** + * The {@link Wire} is interpreted as an unsigned integer with n bits. + * + * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on. + * + * @author Fabian Stemmler + */ + public long getUnsignedValue() + { + return Wire.this.getUnsignedValue(); + } + + /** + * The {@link Wire} is interpreted as a signed integer with n bits. + * + * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on. + * + * @author Fabian Stemmler + */ + public long getSignedValue() + { + return Wire.this.getSignedValue(); + } + + @Override + public String toString() + { + return Wire.this.toString(); + } + + public void close() + { + inputs.remove(this); + detachEnd(this); + recalculate(); + } + + public int length() + { + return length; + } + + public boolean addObserver(WireObserver ob) + { + return observers.add(ob); + } + + public Wire getWire() + { + return Wire.this; + } + } + + public class ReadWriteEnd extends ReadEnd + { + private boolean open; + private BitVector inputValues; + + private ReadWriteEnd() + { + super(); + open = true; + initValues(); + registerInput(this); + } + + private void initValues() + { + inputValues = U.toVector(length); + } + + /** + * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time. + * + * @param newValues The new values the wires should take on. + * + * @author Fabian Stemmler + */ + public void feedSignals(Bit... newValues) + { + feedSignals(BitVector.of(newValues)); + } + + public void feedSignals(BitVector newValues) + { + if (newValues.length() != length) + throw new IllegalArgumentException( + String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length)); + if (!open) + throw new RuntimeException("Attempted to write to closed WireArrayEnd."); + Simulation.TIMELINE.addEvent(e -> setValues(newValues), travelTime); + } + + /** + * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time. + * + * @param bitVector 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, BitVector bitVector) + { + if (!open) + throw new RuntimeException("Attempted to write to closed WireArrayEnd."); + Simulation.TIMELINE.addEvent(e -> setValues(startingBit, bitVector), travelTime); + } + + private void setValues(int startingBit, BitVector newValues) + { + // index check covered in equals + if (!inputValues.equalsWithOffset(newValues, startingBit)) + { + Bit[] vals = inputValues.getBits(); + System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length()); + inputValues = BitVector.of(vals); + Wire.this.recalculate(); + } + } + + private void setValues(BitVector newValues) + { + if (inputValues.equals(newValues)) + return; + inputValues = newValues; + Wire.this.recalculate(); + } + + /** + * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link Wire}. + */ + public Bit getInputValue() + { + return getInputValue(0); + } + + /** + * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}. + */ + public Bit getInputValue(int index) + { + return inputValues.getBit(index); + } + + /** + * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link Wire}. + */ + public BitVector getInputValues() + { + return getInputValues(0, length); + } + + public BitVector getInputValues(int start, int end) + { + return inputValues.subVector(start, end); + } + + /** + * {@link ReadEnd} now feeds Z into the associated {@link Wire}. + */ + public void clearSignals() + { + feedSignals(Z.toVector(length)); + } + + public BitVector wireValuesExcludingMe() + { + BitVectorMutator mutator = BitVectorMutator.empty(); + for (ReadWriteEnd wireEnd : inputs) + { + if (wireEnd == this) + continue; + mutator.join(wireEnd.inputValues); + } + return mutator.get(); + } + + @Override + public String toString() + { + return inputValues.toString(); + } + } + + @Override + public String toString() + { + return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs); + // Arrays.toString(values), inputs.stream().map(i -> Arrays.toString(i.inputValues)).reduce((s1, s2) -> s1 + s2) + } + + public static ReadEnd[] extractEnds(Wire[] w) + { + ReadEnd[] inputs = new ReadEnd[w.length]; + for (int i = 0; i < w.length; i++) + inputs[i] = w[i].createEnd(); + return inputs; + } } \ No newline at end of file diff --git a/era.mi/src/era/mi/logic/wires/WireObserver.java b/era.mi/src/era/mi/logic/wires/WireObserver.java index 9258e0a0..b282a9dd 100644 --- a/era.mi/src/era/mi/logic/wires/WireObserver.java +++ b/era.mi/src/era/mi/logic/wires/WireObserver.java @@ -1,8 +1,9 @@ -package era.mi.logic.wires; - -import era.mi.logic.types.BitVector; - -public interface WireObserver -{ - public void update(Wire initiator, BitVector oldValues); -} +package era.mi.logic.wires; + +import era.mi.logic.types.BitVector; +import era.mi.logic.wires.Wire.ReadEnd; + +public interface WireObserver +{ + public void update(ReadEnd initiator, BitVector oldValues); +} -- 2.17.1