added bus and multiplexer

This commit is contained in:
hneemann 2016-03-12 14:13:15 +01:00
parent 6938aaf45b
commit f63ba56e77
22 changed files with 340 additions and 76 deletions

View File

@ -0,0 +1,23 @@
package de.neemann.digital;
/**
* @author hneemann
*/
public class BurnException extends NodeException {
private final ObservableValue v1;
private final ObservableValue v2;
public BurnException(ObservableValue v1, ObservableValue v2) {
super("burnException");
this.v1 = v1;
this.v2 = v2;
}
public ObservableValue getV1() {
return v1;
}
public ObservableValue getV2() {
return v2;
}
}

View File

@ -0,0 +1,17 @@
package de.neemann.digital;
/**
* @author hneemann
*/
public class HighZException extends NodeException {
private final ObservableValue causedObservable;
public HighZException(ObservableValue causedObservable) {
super("readOfHighZ");
this.causedObservable = causedObservable;
}
public ObservableValue getCausedObservable() {
return causedObservable;
}
}

View File

@ -4,5 +4,5 @@ package de.neemann.digital;
* @author hneemann
*/
public interface Listener {
void needsUpdate() throws NodeException;
void needsUpdate();
}

View File

@ -13,9 +13,9 @@ public abstract class Node implements Listener {
}
@Override
public void needsUpdate() throws NodeException {
public void needsUpdate() {
if (model == null)
throw new NodeException("no model set");
throw new RuntimeException("noModelSet");
if (model.getVersion() != version) {
model.addToUpdateList(this);
@ -25,7 +25,7 @@ public abstract class Node implements Listener {
/**
* Only read the input!
* It is not allowed to write to the outputs!!!
* It is not allowed to write to one of the outputs!!!
*
* @throws NodeException
*/
@ -33,10 +33,12 @@ public abstract class Node implements Listener {
/**
* Only write to the output!
* It is not allowed to read from the inputs!!!
* It is not allowed to read from one of the inputs!!!
*
* @throws NodeException
*/
public abstract void writeOutputs() throws NodeException;
public void checkConsistence() throws NodeException {
}
}

View File

@ -5,18 +5,20 @@ import java.util.ArrayList;
/**
* @author hneemann
*/
public class ObservableValue {
public class ObservableValue extends Value {
private final int bits;
private int value;
private ArrayList<Listener> listeners;
private final ArrayList<Listener> listeners;
public ObservableValue(int bits) {
this.bits = bits;
this(bits, false);
}
public ObservableValue(int bits, boolean highZ) {
super(bits, highZ);
listeners = new ArrayList<>();
}
public void addListener(Listener listener) throws NodeException {
public void addListener(Listener listener) {
listeners.add(listener);
}
@ -24,29 +26,32 @@ public class ObservableValue {
listeners.remove(listener);
}
public void hasChanged() throws NodeException {
public void hasChanged() {
for (Listener l : listeners) {
l.needsUpdate();
}
}
public int getValue() {
return value;
}
public void setValue(int value) throws NodeException {
if (this.value != value) {
this.value = value;
hasChanged();
}
}
public int getBits() {
return bits;
}
public int getValueBits() {
return getValueBits(value);
public int getValue() throws NodeException {
if (highZ)
throw new HighZException(this);
return value;
}
public void setValue(int value) {
if (this.value != value) {
this.value = value;
if (!highZ)
hasChanged();
}
}
public int getValueBits() throws NodeException {
return getValueBits(getValue());
}
public int getValueBits(int value) {
@ -59,10 +64,28 @@ public class ObservableValue {
}
}
public boolean isHighZ() {
return highZ;
}
public void setHighZ(boolean highZ) {
if (this.highZ != highZ) {
this.highZ = highZ;
hasChanged();
}
}
@Override
public void set(int value, boolean highZ) {
setValue(value);
setHighZ(highZ);
}
@Override
public String toString() {
return "ObservableValue{" +
"value=" + value +
"value=" + (highZ ? "??" : value) +
", bits=" + bits +
'}';
}

View File

@ -0,0 +1,28 @@
package de.neemann.digital;
/**
* @author hneemann
*/
public class Value {
protected final int bits;
protected boolean highZ;
protected int value;
public Value(int bits) {
this(bits, false);
}
public Value(int bits, boolean highZ) {
this.bits = bits;
this.highZ = highZ;
}
public void set(int value, boolean highZ) {
this.value = value;
this.highZ = highZ;
}
public void set(Value v) {
set(v.value, v.highZ);
}
}

View File

@ -1,5 +1,6 @@
package de.neemann.digital.basic;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import java.util.ArrayList;
@ -14,7 +15,7 @@ public class And extends Function {
}
@Override
protected int calculate(ArrayList<ObservableValue> inputs) {
protected int calculate(ArrayList<ObservableValue> inputs) throws NodeException {
int f = -1;
for (ObservableValue i : inputs) {
f &= i.getValue();

View File

@ -0,0 +1,37 @@
package de.neemann.digital.basic;
import de.neemann.digital.BitsException;
import de.neemann.digital.Node;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import java.util.ArrayList;
/**
* @author hneemann
*/
public abstract class FanIn extends Node {
protected final ArrayList<ObservableValue> inputs;
protected final ObservableValue output;
public FanIn(int bits) {
inputs = new ArrayList<>();
output = new ObservableValue(bits);
}
public FanIn addInput(ObservableValue value) throws BitsException, NodeException {
output.checkBits(value);
inputs.add(value);
value.addListener(this);
return this;
}
public void removeInput(ObservableValue value) {
inputs.remove(value);
value.removeListener(this);
}
public ObservableValue getOutput() {
return output;
}
}

View File

@ -1,7 +1,5 @@
package de.neemann.digital.basic;
import de.neemann.digital.BitsException;
import de.neemann.digital.Node;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
@ -10,34 +8,14 @@ import java.util.ArrayList;
/**
* @author hneemann
*/
public abstract class Function extends Node {
public abstract class Function extends FanIn {
private final ArrayList<ObservableValue> inputs;
private final ObservableValue output;
private int value;
public Function(int bits) {
output = new ObservableValue(bits);
inputs = new ArrayList<>();
super(bits);
}
public Function addInput(ObservableValue value) throws BitsException, NodeException {
output.checkBits(value);
inputs.add(value);
value.addListener(this);
return this;
}
public void removeInput(ObservableValue value) {
inputs.remove(value);
value.removeListener(this);
}
public ObservableValue getOutput() {
return output;
}
@Override
public void readInputs() throws NodeException {
value = calculate(inputs);
@ -48,5 +26,5 @@ public abstract class Function extends Node {
output.setValue(value);
}
protected abstract int calculate(ArrayList<ObservableValue> inputs);
protected abstract int calculate(ArrayList<ObservableValue> inputs) throws NodeException;
}

View File

@ -1,5 +1,6 @@
package de.neemann.digital.basic;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import java.util.ArrayList;
@ -14,7 +15,7 @@ public class NAnd extends Function {
}
@Override
protected int calculate(ArrayList<ObservableValue> inputs) {
protected int calculate(ArrayList<ObservableValue> inputs) throws NodeException {
int f = -1;
for (ObservableValue i : inputs) {
f &= i.getValue();

View File

@ -1,5 +1,6 @@
package de.neemann.digital.basic;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import java.util.ArrayList;
@ -13,7 +14,7 @@ public class NOr extends Function {
}
@Override
protected int calculate(ArrayList<ObservableValue> inputs) {
protected int calculate(ArrayList<ObservableValue> inputs) throws NodeException {
int f = 0;
for (ObservableValue i : inputs) {
f |= i.getValue();

View File

@ -1,5 +1,6 @@
package de.neemann.digital.basic;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import java.util.ArrayList;
@ -14,7 +15,7 @@ public class Or extends Function {
}
@Override
protected int calculate(ArrayList<ObservableValue> inputs) {
protected int calculate(ArrayList<ObservableValue> inputs) throws NodeException {
int f = 0;
for (ObservableValue i : inputs) {
f |= i.getValue();

View File

@ -0,0 +1,42 @@
package de.neemann.digital.wiring;
import de.neemann.digital.BurnException;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import de.neemann.digital.Value;
import de.neemann.digital.basic.FanIn;
/**
* @author hneemann
*/
public class Bus extends FanIn {
private Value value;
public Bus(int bits) {
super(bits);
value = new Value(bits);
}
@Override
public void readInputs() throws NodeException {
ObservableValue found = null;
for (ObservableValue in : inputs) {
if (!in.isHighZ()) {
if (found != null)
throw new BurnException(in, found);
found = in;
}
}
if (found == null) {
value.set(0, true);
} else {
value.set(found.getValue(), false);
}
}
@Override
public void writeOutputs() throws NodeException {
output.set(value);
}
}

View File

@ -0,0 +1,34 @@
package de.neemann.digital.wiring;
import de.neemann.digital.NodeException;
import de.neemann.digital.ObservableValue;
import de.neemann.digital.basic.FanIn;
/**
* @author hneemann
*/
public class Multiplexer extends FanIn {
private ObservableValue selector;
private int value;
public Multiplexer(int bits, ObservableValue selector) {
super(bits);
this.selector = selector;
selector.addListener(this);
}
@Override
public void readInputs() throws NodeException {
int n = selector.getValueBits();
if (n >= inputs.size())
throw new NodeException("multiplexerSelectsNotPresentInput");
value = inputs.get(n).getValue();
}
@Override
public void writeOutputs() throws NodeException {
output.setValue(value);
}
}

View File

@ -13,8 +13,8 @@ public class FlipFlops extends TestCase {
ObservableValue s = new ObservableValue(1);
Model model = new Model();
Function a1 = model.add(new NOr(1)).addInput(r);
Function a2 = model.add(new NOr(1)).addInput(s);
FanIn a1 = model.add(new NOr(1)).addInput(r);
FanIn a2 = model.add(new NOr(1)).addInput(s);
a1.addInput(a2.getOutput());
a2.addInput(a1.getOutput());
@ -39,8 +39,8 @@ public class FlipFlops extends TestCase {
ObservableValue s = new ObservableValue(1);
Model model = new Model();
Function a1 = model.add(new NAnd(1)).addInput(r);
Function a2 = model.add(new NAnd(1)).addInput(s);
FanIn a1 = model.add(new NAnd(1)).addInput(r);
FanIn a2 = model.add(new NAnd(1)).addInput(s);
a1.addInput(a2.getOutput());
a2.addInput(a1.getOutput());
@ -60,22 +60,22 @@ public class FlipFlops extends TestCase {
ObservableValue c = new ObservableValue(1);
Model model = new Model();
Function a1 = model.add(new And(1)).addInput(j).addInput(c);
Function a2 = model.add(new And(1)).addInput(k).addInput(c);
FanIn a1 = model.add(new And(1)).addInput(j).addInput(c);
FanIn a2 = model.add(new And(1)).addInput(k).addInput(c);
Not not = model.add(new Not(c));
Function nor1 = model.add(new NOr(1)).addInput(a1.getOutput());
Function nor2 = model.add(new NOr(1)).addInput(a2.getOutput());
FanIn nor1 = model.add(new NOr(1)).addInput(a1.getOutput());
FanIn nor2 = model.add(new NOr(1)).addInput(a2.getOutput());
nor1.addInput(nor2.getOutput());
nor2.addInput(nor1.getOutput());
Function a3 = model.add(new And(1)).addInput(nor1.getOutput()).addInput(not.getOutput());
Function a4 = model.add(new And(1)).addInput(nor2.getOutput()).addInput(not.getOutput());
FanIn a3 = model.add(new And(1)).addInput(nor1.getOutput()).addInput(not.getOutput());
FanIn a4 = model.add(new And(1)).addInput(nor2.getOutput()).addInput(not.getOutput());
Function nor3 = model.add(new NOr(1)).addInput(a3.getOutput());
Function nor4 = model.add(new NOr(1)).addInput(a4.getOutput());
FanIn nor3 = model.add(new NOr(1)).addInput(a3.getOutput());
FanIn nor4 = model.add(new NOr(1)).addInput(a4.getOutput());
nor3.addInput(nor4.getOutput());
nor4.addInput(nor3.getOutput());

View File

@ -39,7 +39,7 @@ public class TestExecuter {
for (int i = 0; i < outputs.length; i++) {
int should = val[i + inputs.length];
if (should >= 0)
assertEquals("Value " + i, outputs[i].getValueBits(should), outputs[i].getValueBits());
assertEquals("output " + i, outputs[i].getValueBits(should), outputs[i].getValueBits());
}
}

View File

@ -16,9 +16,9 @@ public class AndTest extends TestCase {
Model model = new Model();
Function and = model.add(new And(1)).addInput(a).addInput(b);
ObservableValue out = model.add(new And(1)).addInput(a).addInput(b).getOutput();
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(and.getOutput());
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(out);
sc.check(0, 0, 0);
sc.check(1, 0, 0);
sc.check(0, 1, 0);

View File

@ -15,9 +15,9 @@ public class NAndTest extends TestCase {
ObservableValue b = new ObservableValue(1);
Model model = new Model();
Function nand = model.add(new NAnd(1)).addInput(a).addInput(b);
ObservableValue out = model.add(new NAnd(1)).addInput(a).addInput(b).getOutput();
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(nand.getOutput());
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(out);
sc.check(0, 0, 1);
sc.check(1, 0, 1);
sc.check(0, 1, 1);

View File

@ -15,9 +15,9 @@ public class NOrTest extends TestCase {
ObservableValue b = new ObservableValue(1);
Model model = new Model();
Function nor = model.add(new NOr(1)).addInput(a).addInput(b);
ObservableValue nor = model.add(new NOr(1)).addInput(a).addInput(b).getOutput();
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(nor.getOutput());
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(nor);
sc.check(0, 0, 1);
sc.check(1, 0, 0);
sc.check(0, 1, 0);

View File

@ -15,9 +15,9 @@ public class OrTest extends TestCase {
ObservableValue b = new ObservableValue(1);
Model model = new Model();
Function and = model.add(new Or(1)).addInput(a).addInput(b);
ObservableValue and = model.add(new Or(1)).addInput(a).addInput(b).getOutput();
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(and.getOutput());
TestExecuter sc = new TestExecuter(model).setInputs(a, b).setOutputs(and);
sc.check(0, 0, 0);
sc.check(1, 0, 1);
sc.check(0, 1, 1);

View File

@ -0,0 +1,46 @@
package de.neemann.digital.wiring;
import de.neemann.digital.BurnException;
import de.neemann.digital.Model;
import de.neemann.digital.ObservableValue;
import de.neemann.digital.TestExecuter;
import junit.framework.TestCase;
/**
* @author hneemann
*/
public class BusTest extends TestCase {
public void testBus() throws Exception {
Model model = new Model();
ObservableValue a = new ObservableValue(1, true);
ObservableValue b = new ObservableValue(1);
ObservableValue out = model.add(new Bus(1)).addInput(a).addInput(b).getOutput();
TestExecuter te = new TestExecuter(model).setInputs(a, b).setOutputs(out);
te.check(0, 0, 0);
te.check(0, 1, 1);
a.setHighZ(false);
b.setHighZ(true);
te.check(0, 1, 0);
te.check(1, 0, 1);
a.setHighZ(false);
b.setHighZ(false);
try {
te.check(0, 0, 0);
assertTrue(false);
} catch (BurnException e) {
assertTrue(true);
}
}
public void testBusHighZ() throws Exception {
Model model = new Model();
ObservableValue a = new ObservableValue(1, true);
ObservableValue b = new ObservableValue(1, true);
ObservableValue out = model.add(new Bus(1)).addInput(a).addInput(b).getOutput();
TestExecuter te = new TestExecuter(model).setInputs(a, b).setOutputs(out);
assertTrue(out.isHighZ());
}
}

View File

@ -0,0 +1,30 @@
package de.neemann.digital.wiring;
import de.neemann.digital.Model;
import de.neemann.digital.ObservableValue;
import de.neemann.digital.TestExecuter;
import junit.framework.TestCase;
/**
* @author hneemann
*/
public class MultiplexerTest extends TestCase {
public void testMux() throws Exception {
Model model = new Model();
ObservableValue a = new ObservableValue(4);
ObservableValue b = new ObservableValue(4);
ObservableValue c = new ObservableValue(4);
ObservableValue d = new ObservableValue(4);
ObservableValue sel = new ObservableValue(2);
ObservableValue out = model.add(new Multiplexer(4, sel)).addInput(a).addInput(b).addInput(c).addInput(d).getOutput();
TestExecuter te = new TestExecuter(model).setInputs(a, b, c, d, sel).setOutputs(out);
te.check(3, 4, 5, 6, 0, 3);
te.check(3, 4, 5, 6, 1, 4);
te.check(3, 4, 5, 6, 2, 5);
te.check(3, 4, 5, 6, 3, 6);
}
}