first version of a splitter

This commit is contained in:
hneemann 2016-03-20 15:28:19 +01:00
parent 65a6d42014
commit ed6a2f6a16
11 changed files with 375 additions and 5 deletions

View File

@ -11,6 +11,8 @@ public class AttributeKey<VALUE> {
public static final AttributeKey<Integer> Value = new AttributeKey<>("Value", 1); public static final AttributeKey<Integer> Value = new AttributeKey<>("Value", 1);
public static final AttributeKey<Integer> Default = new AttributeKey<>("Default", 0); public static final AttributeKey<Integer> Default = new AttributeKey<>("Default", 0);
public static final AttributeKey<java.awt.Color> Color = new AttributeKey<>("Color", java.awt.Color.RED); public static final AttributeKey<java.awt.Color> Color = new AttributeKey<>("Color", java.awt.Color.RED);
public static final AttributeKey<String> InputSplit = new AttributeKey<>("Input Splitting", "");
public static final AttributeKey<String> OutputSplit = new AttributeKey<>("Output Splitting", "");
private final String name; private final String name;
private final VALUE def; private final VALUE def;

View File

@ -36,7 +36,7 @@ public class ElementAttributes {
} }
} }
public <VALUE> void set(AttributeKey<VALUE> key, VALUE value) { public <VALUE> ElementAttributes set(AttributeKey<VALUE> key, VALUE value) {
if (value != get(key)) { if (value != get(key)) {
if (value.equals(key.getDefault())) { if (value.equals(key.getDefault())) {
if (attributes != null) if (attributes != null)
@ -48,6 +48,7 @@ public class ElementAttributes {
} }
fireValueChanged(key); fireValueChanged(key);
} }
return this;
} }
private void fireValueChanged(AttributeKey key) { private void fireValueChanged(AttributeKey key) {

View File

@ -0,0 +1,183 @@
package de.neemann.digital.core.wiring;
import de.neemann.digital.core.*;
import de.neemann.digital.core.element.AttributeKey;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.gui.draw.elements.PinException;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* @author hneemann
*/
public class Splitter implements Element {
public static final ElementTypeDescription DESCRIPTION
= new SplitterTypeDescription()
.addAttribute(AttributeKey.InputSplit)
.addAttribute(AttributeKey.OutputSplit)
.setShortName("");
private final ObservableValue[] outputs;
private final Ports inPorts;
private final Ports outPorts;
private ObservableValue[] inputs;
private static class SplitterTypeDescription extends ElementTypeDescription {
public SplitterTypeDescription() {
super(Splitter.class);
}
@Override
public String[] getInputNames(ElementAttributes elementAttributes) {
Ports p = new Ports(elementAttributes.get(AttributeKey.InputSplit));
return p.getNames();
}
}
public Splitter(ElementAttributes attributes) throws PinException {
outPorts = new Ports(attributes.get(AttributeKey.OutputSplit));
outputs = outPorts.getOutputs();
inPorts = new Ports(attributes.get(AttributeKey.InputSplit));
if (inPorts.getBits() != outPorts.getBits())
throw new PinException("splitterPortMismatch");
}
@Override
public void setInputs(ObservableValue... inputs) throws NodeException {
this.inputs = inputs;
for (int i = 0; i < inputs.length; i++) {
if (inPorts.getPort(i).getBits() != inputs[i].getBits())
throw new BitsException("splitterBitsMismatch", inputs[i]);
inputs[i].addObserver(createObserverForInput(i));
}
}
private Observer createObserverForInput(int i) throws NodeException {
Observer observer = outPorts.getSimpleTargetObserver(inPorts.getPort(i), inputs, outputs);
if (observer == null)
throw new NodeException("splitterMismatchError");
return observer;
}
@Override
public ObservableValue[] getOutputs() {
return outputs;
}
@Override
public void registerNodes(Model model) {
}
public static final class Ports {
private final ArrayList<Port> ports;
private int bits;
public Ports(String definition) {
StringTokenizer st = new StringTokenizer(definition, ",", false);
ports = new ArrayList<>();
bits = 0;
while (st.hasMoreTokens()) {
Port p = new Port(Integer.decode(st.nextToken().trim()), bits, ports.size());
ports.add(p);
bits += p.getBits();
}
if (ports.isEmpty()) {
ports.add(new Port(1, 0, 0));
bits = 1;
}
}
public int getBits() {
return bits;
}
public String[] getNames() {
String[] name = new String[ports.size()];
for (int i = 0; i < name.length; i++)
name[i] = ports.get(i).getName();
return name;
}
public ObservableValue[] getOutputs() {
ObservableValue[] outputs = new ObservableValue[ports.size()];
for (int i = 0; i < ports.size(); i++) {
Port p = ports.get(i);
outputs[i] = new ObservableValue(p.getName(), p.getBits());
}
return outputs;
}
public Port getPort(int i) {
return ports.get(i);
}
/**
* Checks if there is a single out target port for the input port
*
* @param inPort
* @param inputs
* @param outputs
*/
public Observer getSimpleTargetObserver(Port inPort, ObservableValue[] inputs, ObservableValue[] outputs) {
int pos = inPort.getPos();
int bits = inPort.getBits();
for (Port outPort : ports) {
if (outPort.getPos() <= pos && pos + bits <= outPort.getPos() + outPort.getBits()) {
final int bitPos = pos - outPort.getPos();
final int mask = ~(((1 << inPort.bits) - 1) << bitPos);
final ObservableValue inValue = inputs[inPort.number];
final ObservableValue outValue = outputs[outPort.number];
return new Observer() {
@Override
public void hasChanged() {
long in = inValue.getValue();
long out = outValue.getValue();
outValue.setValue((out & mask) | (in << bitPos));
}
};
}
}
return null;
}
}
private static final class Port {
private final String name;
private final int bits;
private final int pos;
private final int number;
public Port(int bits, int pos, int number) {
this.bits = bits;
this.pos = pos;
this.number = number;
if (bits == 1)
name = "" + pos;
else
name = "" + pos + "-" + (pos + bits - 1);
}
public int getBits() {
return bits;
}
public int getPos() {
return pos;
}
public String getName() {
return name;
}
}
}

View File

@ -101,7 +101,8 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
shape.drawTo(gr); shape.drawTo(gr);
for (Pin p : shape.getPins()) for (Pin p : shape.getPins())
gr.drawCircle(p.getPos().add(-PIN, -PIN), p.getPos().add(PIN, PIN), p.getDirection() == Pin.Direction.input ? Style.NORMAL : Style.WIRE); gr.drawCircle(p.getPos().add(-PIN, -PIN), p.getPos().add(PIN, PIN)
, p.getDirection() == Pin.Direction.input ? Style.NORMAL : Style.WIRE_OUT);
if (highLight && minMax != null) { if (highLight && minMax != null) {
Vector delta = minMax.getMax().sub(minMax.getMin()); Vector delta = minMax.getMax().sub(minMax.getMin());

View File

@ -10,6 +10,7 @@ public class Style {
public static final Style WIRE = new Style(2, true, Color.BLUE.darker()); public static final Style WIRE = new Style(2, true, Color.BLUE.darker());
public static final Style WIRE_LOW = new Style(3, true, new Color(0, 112, 0)); public static final Style WIRE_LOW = new Style(3, true, new Color(0, 112, 0));
public static final Style WIRE_HIGH = new Style(3, true, new Color(102, 255, 102)); public static final Style WIRE_HIGH = new Style(3, true, new Color(102, 255, 102));
public static final Style WIRE_OUT = new Style(2, true, Color.RED.darker());
public static final Style FILLED = new Style(2, true, Color.BLACK); public static final Style FILLED = new Style(2, true, Color.BLACK);
public static final Style THIN = new Style(1, false, Color.BLACK); public static final Style THIN = new Style(1, false, Color.BLACK);
public static final Style DASH = new Style(1, false, Color.BLACK); public static final Style DASH = new Style(1, false, Color.BLACK);

View File

@ -13,6 +13,7 @@ import de.neemann.digital.core.io.Const;
import de.neemann.digital.core.io.In; import de.neemann.digital.core.io.In;
import de.neemann.digital.core.io.Out; import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.wiring.Delay; import de.neemann.digital.core.wiring.Delay;
import de.neemann.digital.core.wiring.Splitter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -42,6 +43,8 @@ public class PartLibrary implements Iterable<PartLibrary.PartContainer> {
add(Out.LEDDESCRIPTION, "IO"); add(Out.LEDDESCRIPTION, "IO");
add(Out.PROBEDESCRIPTION, "IO"); add(Out.PROBEDESCRIPTION, "IO");
add(Splitter.DESCRIPTION, "Wires");
add(RS_FF.DESCRIPTION, "FlipFlops"); add(RS_FF.DESCRIPTION, "FlipFlops");
add(JK_FF.DESCRIPTION, "FlipFlops"); add(JK_FF.DESCRIPTION, "FlipFlops");
add(D_FF.DESCRIPTION, "FlipFlops"); add(D_FF.DESCRIPTION, "FlipFlops");

View File

@ -26,7 +26,7 @@ public class ModelEntry {
} }
/** /**
* Sets the Inputs of the element contained in thes entry * Sets the Inputs of the element contained in this entry
* *
* @throws PinException * @throws PinException
* @throws NodeException * @throws NodeException

View File

@ -12,6 +12,7 @@ import de.neemann.digital.core.io.Const;
import de.neemann.digital.core.io.In; import de.neemann.digital.core.io.In;
import de.neemann.digital.core.io.Out; import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.wiring.Delay; import de.neemann.digital.core.wiring.Delay;
import de.neemann.digital.core.wiring.Splitter;
import de.neemann.digital.gui.draw.library.PartLibrary; import de.neemann.digital.gui.draw.library.PartLibrary;
import java.util.HashMap; import java.util.HashMap;
@ -53,6 +54,9 @@ public final class ShapeFactory {
map.put(Out.DESCRIPTION.getName(), attr -> new OutputShape(attr.get(AttributeKey.Label))); map.put(Out.DESCRIPTION.getName(), attr -> new OutputShape(attr.get(AttributeKey.Label)));
map.put(Out.LEDDESCRIPTION.getName(), attr -> new LEDShape(attr.get(AttributeKey.Label), attr.get(AttributeKey.Color))); map.put(Out.LEDDESCRIPTION.getName(), attr -> new LEDShape(attr.get(AttributeKey.Label), attr.get(AttributeKey.Color)));
map.put(Out.PROBEDESCRIPTION.getName(), attr -> new ProbeShape(attr.get(AttributeKey.Label))); map.put(Out.PROBEDESCRIPTION.getName(), attr -> new ProbeShape(attr.get(AttributeKey.Label)));
map.put(Splitter.DESCRIPTION.getName(), attr -> new SplitterShape(attr.get(AttributeKey.InputSplit), attr.get(AttributeKey.OutputSplit)));
} }
public PartLibrary setLibrary(PartLibrary library) { public PartLibrary setLibrary(PartLibrary library) {

View File

@ -0,0 +1,62 @@
package de.neemann.digital.gui.draw.shapes;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.wiring.Splitter;
import de.neemann.digital.gui.draw.elements.IOState;
import de.neemann.digital.gui.draw.elements.Pin;
import de.neemann.digital.gui.draw.elements.Pins;
import de.neemann.digital.gui.draw.graphics.*;
import static de.neemann.digital.gui.draw.shapes.GenericShape.SIZE;
/**
* @author hneemann
*/
public class SplitterShape implements Shape {
private final String[] inputs;
private final String[] outputs;
private final int length;
private Pins pins;
public SplitterShape(String inputDef, String outputDef) {
inputs = new Splitter.Ports(inputDef).getNames();
outputs = new Splitter.Ports(outputDef).getNames();
length = (Math.max(inputs.length, outputs.length) - 1) * SIZE + 2;
}
@Override
public Pins getPins() {
if (pins == null) {
pins = new Pins();
for (int i = 0; i < inputs.length; i++)
pins.add(new Pin(new Vector(0, i * SIZE), inputs[i], Pin.Direction.input));
for (int i = 0; i < outputs.length; i++)
pins.add(new Pin(new Vector(SIZE, i * SIZE), outputs[i], Pin.Direction.output));
}
return pins;
}
@Override
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
return null;
}
@Override
public void drawTo(Graphic graphic) {
for (int i = 0; i < inputs.length; i++) {
Vector pos = new Vector(-2, i * SIZE - 2);
graphic.drawText(pos, pos.add(2, 0), inputs[i], Orientation.RIGHTBOTTOM, Style.SHAPE_PIN);
}
for (int i = 0; i < outputs.length; i++) {
Vector pos = new Vector(SIZE + 2, i * SIZE - 2);
graphic.drawText(pos, pos.add(2, 0), outputs[i], Orientation.LEFTBOTTOM, Style.SHAPE_PIN);
}
graphic.drawPolygon(new Polygon(true)
.add(2, 0)
.add(SIZE - 2, 0)
.add(SIZE - 2, length)
.add(2, length), Style.NORMAL);
}
}

View File

@ -3,6 +3,7 @@ package de.neemann.digital;
import de.neemann.digital.core.Model; import de.neemann.digital.core.Model;
import de.neemann.digital.core.NodeException; import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.ObservableValue; import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.element.Element;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -15,13 +16,17 @@ public class TestExecuter {
private ObservableValue[] inputs; private ObservableValue[] inputs;
private ObservableValue[] outputs; private ObservableValue[] outputs;
public TestExecuter() throws NodeException {
this(null);
}
public TestExecuter(Model model) throws NodeException { public TestExecuter(Model model) throws NodeException {
this(model, false); this(model, false);
} }
public TestExecuter(Model model, boolean noise) throws NodeException { public TestExecuter(Model model, boolean noise) throws NodeException {
this.model = model; this.model = model;
model.init(noise); if (model != null)
model.init(noise);
} }
public TestExecuter setInputs(ObservableValue... values) { public TestExecuter setInputs(ObservableValue... values) {
@ -34,11 +39,17 @@ public class TestExecuter {
return this; return this;
} }
public TestExecuter setOutputsOf(Element element) {
outputs = element.getOutputs();
return this;
}
public void check(int... val) throws NodeException { public void check(int... val) throws NodeException {
for (int i = 0; i < inputs.length; i++) { for (int i = 0; i < inputs.length; i++) {
inputs[i].setValue(val[i]); inputs[i].setValue(val[i]);
} }
model.doStep(); if (model != null)
model.doStep();
for (int i = 0; i < outputs.length; i++) { for (int i = 0; i < outputs.length; i++) {
int should = val[i + inputs.length]; int should = val[i + inputs.length];

View File

@ -0,0 +1,102 @@
package de.neemann.digital.core.wiring;
import de.neemann.digital.TestExecuter;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.element.AttributeKey;
import de.neemann.digital.core.element.ElementAttributes;
import junit.framework.TestCase;
/**
* @author hneemann
*/
public class SplitterTest extends TestCase {
public void testBits() throws Exception {
ObservableValue a = new ObservableValue("a", 1);
ObservableValue b = new ObservableValue("b", 1);
ObservableValue c = new ObservableValue("c", 1);
ObservableValue d = new ObservableValue("d", 1);
Splitter splitter = new Splitter(new ElementAttributes()
.set(AttributeKey.InputSplit, "1,1,1,1")
.set(AttributeKey.OutputSplit, "4"));
splitter.setInputs(a, b, c, d);
ObservableValue[] outputs = splitter.getOutputs();
assertEquals(1, outputs.length);
TestExecuter sc = new TestExecuter().setInputs(a, b, c, d).setOutputsOf(splitter);
sc.check(0, 0, 0, 0, 0);
sc.check(1, 0, 0, 0, 1);
sc.check(0, 1, 0, 0, 2);
sc.check(0, 0, 1, 0, 4);
sc.check(0, 0, 0, 1, 8);
sc.check(1, 1, 0, 0, 3);
sc.check(1, 1, 1, 0, 7);
sc.check(1, 1, 1, 1, 15);
sc.check(0, 0, 1, 1, 12);
sc.check(0, 1, 1, 1, 14);
}
public void testMoreBits() throws Exception {
ObservableValue a = new ObservableValue("a", 4);
ObservableValue b = new ObservableValue("b", 4);
ObservableValue c = new ObservableValue("c", 4);
ObservableValue d = new ObservableValue("d", 4);
Splitter splitter = new Splitter(new ElementAttributes()
.set(AttributeKey.InputSplit, "4,4,4,4")
.set(AttributeKey.OutputSplit, "16"));
splitter.setInputs(a, b, c, d);
ObservableValue[] outputs = splitter.getOutputs();
assertEquals(1, outputs.length);
TestExecuter sc = new TestExecuter().setInputs(d, c, b, a).setOutputsOf(splitter);
sc.check(0, 0, 0, 0, 0x0000);
sc.check(0, 0, 0, 1, 0x0001);
sc.check(0, 0, 1, 0, 0x0010);
sc.check(0, 1, 0, 0, 0x0100);
sc.check(1, 0, 0, 0, 0x1000);
sc.check(0, 0, 0, 15, 0x000F);
sc.check(0, 0, 15, 0, 0x00F0);
sc.check(0, 15, 0, 0, 0x0F00);
sc.check(15, 0, 0, 0, 0xF000);
}
public void testMoreBits2() throws Exception {
ObservableValue a = new ObservableValue("a", 4);
ObservableValue b = new ObservableValue("b", 4);
ObservableValue c = new ObservableValue("c", 4);
ObservableValue d = new ObservableValue("d", 4);
Splitter splitter = new Splitter(new ElementAttributes()
.set(AttributeKey.InputSplit, "4,4,4,4")
.set(AttributeKey.OutputSplit, "8,8"));
splitter.setInputs(a, b, c, d);
ObservableValue[] outputs = splitter.getOutputs();
assertEquals(2, outputs.length);
TestExecuter sc = new TestExecuter().setInputs(b, a, d, c).setOutputsOf(splitter);
sc.check(0x0, 0x0, 0x0, 0x0, 0x00, 0x00);
sc.check(0x1, 0x0, 0x0, 0x0, 0x10, 0x00);
sc.check(0x0, 0x1, 0x0, 0x0, 0x01, 0x00);
sc.check(0x0, 0x0, 0x1, 0x0, 0x00, 0x10);
sc.check(0x0, 0x0, 0x0, 0x1, 0x00, 0x01);
sc.check(0xf, 0x0, 0x0, 0x0, 0xf0, 0x00);
sc.check(0x0, 0xf, 0x0, 0x0, 0x0f, 0x00);
sc.check(0x0, 0x0, 0xf, 0x0, 0x00, 0xf0);
sc.check(0x0, 0x0, 0x0, 0xf, 0x00, 0x0f);
sc.check(0x1, 0x1, 0x0, 0x0, 0x11, 0x00);
sc.check(0x0, 0x0, 0x1, 0x1, 0x00, 0x11);
sc.check(0xf, 0xf, 0x0, 0x0, 0xff, 0x00);
sc.check(0x0, 0x0, 0xf, 0xf, 0x00, 0xff);
sc.check(0xf, 0xf, 0xf, 0xf, 0xff, 0xff);
}
}