From 230bb56e1d7cb57385ca89cf893a70006399afb6 Mon Sep 17 00:00:00 2001 From: hneemann Date: Fri, 10 Jun 2016 11:25:13 +0200 Subject: [PATCH] analyzer is able to analyse sequential circuits build with JK-FF and T-FF as far as they are connected directly to a single clock. --- .../digital/analyse/AnalyseException.java | 9 ++ .../digital/analyse/ModelAnalyser.java | 87 +++++++++++++++++++ .../java/de/neemann/digital/core/Model.java | 9 ++ .../de/neemann/digital/core/Observable.java | 9 ++ .../de/neemann/digital/core/basic/Not.java | 6 ++ .../digital/core/flipflops/FlipflopD.java | 30 ++++++- .../digital/core/flipflops/FlipflopJK.java | 28 ++++++ .../digital/core/flipflops/FlipflopT.java | 13 +++ src/main/resources/lang/lang_de.properties | 2 + src/main/resources/lang/lang_en.properties | 2 + .../digital/analyse/ModelAnalyserTest.java | 42 +++++++++ .../digital/integration/ToBreakRunner.java | 20 ++++- 12 files changed, 254 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/neemann/digital/analyse/AnalyseException.java b/src/main/java/de/neemann/digital/analyse/AnalyseException.java index 4970ca7ca..66840e42f 100644 --- a/src/main/java/de/neemann/digital/analyse/AnalyseException.java +++ b/src/main/java/de/neemann/digital/analyse/AnalyseException.java @@ -14,4 +14,13 @@ public class AnalyseException extends Exception { public AnalyseException(String message) { super(message); } + + /** + * Creates a new instance + * + * @param e the cause + */ + public AnalyseException(Exception e) { + super(e); + } } diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java index 9f1d11f15..0098b913e 100644 --- a/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java +++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java @@ -3,7 +3,14 @@ package de.neemann.digital.analyse; import de.neemann.digital.analyse.expression.BitSetter; import de.neemann.digital.analyse.quinemc.BoolTableIntArray; import de.neemann.digital.core.*; +import de.neemann.digital.core.basic.And; +import de.neemann.digital.core.basic.Not; +import de.neemann.digital.core.basic.Or; +import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.flipflops.FlipflopD; +import de.neemann.digital.core.flipflops.FlipflopJK; +import de.neemann.digital.core.flipflops.FlipflopT; +import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.lang.Lang; import java.util.ArrayList; @@ -30,6 +37,14 @@ public class ModelAnalyser { */ public ModelAnalyser(Model model) throws AnalyseException { this.model = model; + + try { + replaceJKFF(); + replaceTFF(); + } catch (NodeException e) { + throw new AnalyseException(e); + } + inputs = checkBinary(model.getInputs()); outputs = checkBinary(model.getOutputs()); @@ -40,6 +55,7 @@ public class ModelAnalyser { int i = 0; List flipflops = model.findNode(FlipflopD.class); for (FlipflopD ff : flipflops) { + checkClock(ff); ff.getDInput().removeObserver(ff); // turn off flipflop String label = ff.getLabel(); if (label.length() == 0) @@ -66,6 +82,18 @@ public class ModelAnalyser { rows = 1 << inputs.size(); } + private void checkClock(Node node) throws AnalyseException { + if (!getClock().hasObserver(node)) + throw new AnalyseException(Lang.get("err_ffNeedsToBeConnectedToClock")); + } + + private ObservableValue getClock() throws AnalyseException { + ArrayList clocks = model.getClocks(); + if (clocks.size() != 1) + throw new AnalyseException(Lang.get("err_aSingleClockNecessary")); + return clocks.get(0).getClockOutput(); + } + private ArrayList checkBinary(ArrayList list) throws AnalyseException { for (Signal s : list) if (s.getValue().getBits() != 1) @@ -111,4 +139,63 @@ public class ModelAnalyser { return tt; } + private void replaceJKFF() throws NodeException, AnalyseException { + List jkList = model.findNode(FlipflopJK.class); + + for (FlipflopJK jk : jkList) { + checkClock(jk); + + // remove JK-ff from model + model.removeNode(jk); + jk.getClockVal().removeObserver(jk); + jk.getjVal().removeObserver(jk); + jk.getkVal().removeObserver(jk); + + // create d ff + ObservableValue q = jk.getOutputs().get(0); + ObservableValue qn = jk.getOutputs().get(1); + FlipflopD d = new FlipflopD(jk.getLabel(), q, qn); + + And a1 = new And(new ElementAttributes()); + a1.setInputs(new ObservableValues(jk.getjVal(), qn)); + And a2 = new And(new ElementAttributes()); + Not nk = new Not(new ElementAttributes()); + nk.setInputs(jk.getkVal().asList()); + a2.setInputs(new ObservableValues(nk.getOutput(), q)); + + Or or = new Or(new ElementAttributes()); + or.setInputs(new ObservableValues(a1.getOutput(), a2.getOutput())); + + d.setInputs(new ObservableValues(or.getOutputs().get(0), jk.getClockVal())); + + model.add(a1); + model.add(a2); + model.add(nk); + model.add(or); + model.add(d); + } + } + + private void replaceTFF() throws NodeException, AnalyseException { + List jkList = model.findNode(FlipflopT.class); + + for (FlipflopT tff : jkList) { + checkClock(tff); + + // remove T-ff from model + model.removeNode(tff); + tff.getClockVal().removeObserver(tff); + + // create d ff + ObservableValue q = tff.getOutputs().get(0); + ObservableValue qn = tff.getOutputs().get(1); + FlipflopD d = new FlipflopD(tff.getLabel(), q, qn); + + d.setInputs(new ObservableValues(qn, getClock())); + + model.add(d); + } + } + + } diff --git a/src/main/java/de/neemann/digital/core/Model.java b/src/main/java/de/neemann/digital/core/Model.java index 8d49583fb..fa120f277 100644 --- a/src/main/java/de/neemann/digital/core/Model.java +++ b/src/main/java/de/neemann/digital/core/Model.java @@ -461,4 +461,13 @@ public class Model implements Iterable { public Iterator iterator() { return nodes.iterator(); } + + /** + * removes a node from this model + * + * @param node the node to remove + */ + public void removeNode(Node node) { + nodes.remove(node); + } } diff --git a/src/main/java/de/neemann/digital/core/Observable.java b/src/main/java/de/neemann/digital/core/Observable.java index b3fb9ef97..3cfb97567 100644 --- a/src/main/java/de/neemann/digital/core/Observable.java +++ b/src/main/java/de/neemann/digital/core/Observable.java @@ -66,4 +66,13 @@ public class Observable { } + /** + * Returns true if the given observer observes this observable + * + * @param observer the observer + * @return true if the given observer observes this observable + */ + public boolean hasObserver(Observer observer) { + return observers.contains(observer); + } } diff --git a/src/main/java/de/neemann/digital/core/basic/Not.java b/src/main/java/de/neemann/digital/core/basic/Not.java index 5c9feab98..68c7e084c 100644 --- a/src/main/java/de/neemann/digital/core/basic/Not.java +++ b/src/main/java/de/neemann/digital/core/basic/Not.java @@ -60,4 +60,10 @@ public class Not extends Node implements Element { return output.asList(); } + /** + * @return the output + */ + public ObservableValue getOutput() { + return output; + } } diff --git a/src/main/java/de/neemann/digital/core/flipflops/FlipflopD.java b/src/main/java/de/neemann/digital/core/flipflops/FlipflopD.java index 72c84d786..260781252 100644 --- a/src/main/java/de/neemann/digital/core/flipflops/FlipflopD.java +++ b/src/main/java/de/neemann/digital/core/flipflops/FlipflopD.java @@ -44,10 +44,29 @@ public class FlipflopD extends Node implements Element { * @param attributes the attributes */ public FlipflopD(ElementAttributes attributes) { + this(attributes, + new ObservableValue("Q", attributes.getBits()), + new ObservableValue("\u00ACQ", attributes.getBits())); + } + + /** + * Creates a new D-FF with the given outputs! + * + * @param label the label + * @param q output + * @param qn inverted output + */ + public FlipflopD(String label, ObservableValue q, ObservableValue qn) { + this(new ElementAttributes().set(Keys.LABEL, label).setBits(q.getBits()), q, qn); + if (qn.getBits() != q.getBits()) + throw new RuntimeException("wrong bit count given!"); + } + + private FlipflopD(ElementAttributes attributes, ObservableValue q, ObservableValue qn) { super(true); bits = attributes.getBits(); - this.q = new ObservableValue("Q", bits); - this.qn = new ObservableValue("\u00ACQ", bits); + this.q = q; + this.qn = qn; isProbe = attributes.get(Keys.VALUE_IS_PROBE); label = attributes.getCleanLabel(); @@ -101,4 +120,11 @@ public class FlipflopD extends Node implements Element { public String getLabel() { return label; } + + /** + * @return the clock value + */ + public ObservableValue getClock() { + return clockVal; + } } diff --git a/src/main/java/de/neemann/digital/core/flipflops/FlipflopJK.java b/src/main/java/de/neemann/digital/core/flipflops/FlipflopJK.java index 1f79059b0..278aa208b 100644 --- a/src/main/java/de/neemann/digital/core/flipflops/FlipflopJK.java +++ b/src/main/java/de/neemann/digital/core/flipflops/FlipflopJK.java @@ -82,6 +82,34 @@ public class FlipflopJK extends Node implements Element { kVal = inputs.get(2).addObserverToValue(this).checkBits(1, this); } + /** + * @return the j value + */ + public ObservableValue getjVal() { + return jVal; + } + + /** + * @return the k value + */ + public ObservableValue getkVal() { + return kVal; + } + + /** + * @return the clock value + */ + public ObservableValue getClockVal() { + return clockVal; + } + + /** + * @return the label + */ + public String getLabel() { + return label; + } + @Override public ObservableValues getOutputs() { return ovs(q, qn); diff --git a/src/main/java/de/neemann/digital/core/flipflops/FlipflopT.java b/src/main/java/de/neemann/digital/core/flipflops/FlipflopT.java index 75d76ec38..0d416ae2c 100644 --- a/src/main/java/de/neemann/digital/core/flipflops/FlipflopT.java +++ b/src/main/java/de/neemann/digital/core/flipflops/FlipflopT.java @@ -80,4 +80,17 @@ public class FlipflopT extends Node implements Element { model.addSignal(new Signal(label, q)); } + /** + * @return the clock value + */ + public ObservableValue getClockVal() { + return clockVal; + } + + /** + * @return the label + */ + public String getLabel() { + return label; + } } diff --git a/src/main/resources/lang/lang_de.properties b/src/main/resources/lang/lang_de.properties index ca47c62e1..9613ca02e 100644 --- a/src/main/resources/lang/lang_de.properties +++ b/src/main/resources/lang/lang_de.properties @@ -202,6 +202,8 @@ err_pinMap_NoNameForPin_N=Kein Name f\u00FCr Pin {0} err_pinMap_input_N_notAllowed=Eingang {0} ist nicht erlaubt! err_pinMap_output_N_notAllowed=Ausgang {0} ist nicht erlaubt! err_cannotAnalyse_N=Element {0} kann nicht analysiert werden. +err_ffNeedsToBeConnectedToClock=Flipflops m\u00FCssen direkt mit dem Takt verbunden sein. +err_aSingleClockNecessary=Es darf nur einen Takt geben. attr_dialogTitle=Eigenschaften msg_errorEditingValue=Fehler bei der Eingabe eines Wertes diff --git a/src/main/resources/lang/lang_en.properties b/src/main/resources/lang/lang_en.properties index c77f390bc..445bc0e59 100644 --- a/src/main/resources/lang/lang_en.properties +++ b/src/main/resources/lang/lang_en.properties @@ -181,6 +181,8 @@ err_pinMap_NoNameForPin_N=No Name for pin {0} err_pinMap_input_N_notAllowed=Input {0} not allowed! err_pinMap_output_N_notAllowed=Output {0} not allowed! err_cannotAnalyse_N=Cannot analyse Node {0} +err_ffNeedsToBeConnectedToClock=Flipflop needs to be connected to the clock. +err_aSingleClockNecessary=A single clock is necessary. attr_dialogTitle=Attributes diff --git a/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java b/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java index 7f098931f..48ede886d 100644 --- a/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java +++ b/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java @@ -28,4 +28,46 @@ public class ModelAnalyserTest extends TestCase { "1\t0\t1\t\n" + "1\t1\t0\t\n", tt.toString()); } + + public void testAnalyzerDFF() throws Exception { + Model model = new ToBreakRunner("dig/analyzeTestDFF.dig").getModel(); + TruthTable tt = new ModelAnalyser(model).analyse(); + check2BitCounter(tt); + } + + public void testAnalyzerJKFF() throws Exception { + Model model = new ToBreakRunner("dig/analyzeTestJKFF.dig", false).getModel(); + TruthTable tt = new ModelAnalyser(model).analyse(); + check2BitCounter(tt); + } + + public void testAnalyzerTFF() throws Exception { + Model model = new ToBreakRunner("dig/analyzeTestTFF.dig", false).getModel(); + TruthTable tt = new ModelAnalyser(model).analyse(); + check2BitCounter(tt); + } + + private void check2BitCounter(TruthTable tt) { + assertEquals(4, tt.getRows()); + assertEquals(4, tt.getCols()); + + // first col is XOr: + assertEquals(0, tt.getValue(0, 2)); + assertEquals(1, tt.getValue(1, 2)); + assertEquals(1, tt.getValue(2, 2)); + assertEquals(0, tt.getValue(3, 2)); + + // second col + assertEquals(1, tt.getValue(0, 3)); + assertEquals(0, tt.getValue(1, 3)); + assertEquals(1, tt.getValue(2, 3)); + assertEquals(0, tt.getValue(3, 3)); + + assertEquals("Q_1n\tQ_0n\tQ_1n+1\tQ_0n+1\t\n" + + "0\t0\t0\t1\t\n" + + "0\t1\t1\t0\t\n" + + "1\t0\t1\t1\t\n" + + "1\t1\t0\t0\t\n", tt.toString()); + } + } \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/integration/ToBreakRunner.java b/src/test/java/de/neemann/digital/integration/ToBreakRunner.java index 21fc1e57d..0fb60794b 100644 --- a/src/test/java/de/neemann/digital/integration/ToBreakRunner.java +++ b/src/test/java/de/neemann/digital/integration/ToBreakRunner.java @@ -39,6 +39,19 @@ public class ToBreakRunner { this(new File(Resources.getRoot(), file)); } + /** + * Creates a new instance + * + * @param file the file to load + * @param doInit if true model is initialized + * @throws IOException + * @throws PinException + * @throws NodeException + */ + public ToBreakRunner(String file, boolean doInit) throws IOException, PinException, NodeException { + this(new File(Resources.getRoot(), file), doInit); + } + /** * Creates a new instance * @@ -48,6 +61,10 @@ public class ToBreakRunner { * @throws NodeException */ public ToBreakRunner(File filename) throws IOException, PinException, NodeException { + this(filename, true); + } + + private ToBreakRunner(File filename, boolean doInit) throws IOException, PinException, NodeException { ElementLibrary library = new ElementLibrary(); ShapeFactory shapeFactory = new ShapeFactory(library); circuit = Circuit.loadCircuit(filename, shapeFactory); @@ -56,7 +73,8 @@ public class ToBreakRunner { ModelDescription md = new ModelDescription(circuit, library); model = md.createModel(false); - model.init(true); + if (doInit) + model.init(true); } /**