diff --git a/src/main/java/de/neemann/digital/core/Model.java b/src/main/java/de/neemann/digital/core/Model.java index c7220dd98..69ec57870 100644 --- a/src/main/java/de/neemann/digital/core/Model.java +++ b/src/main/java/de/neemann/digital/core/Model.java @@ -6,6 +6,7 @@ package de.neemann.digital.core; import de.neemann.digital.core.io.Button; +import de.neemann.digital.core.wiring.AsyncSeq; import de.neemann.digital.core.wiring.Break; import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.core.wiring.Reset; @@ -41,6 +42,7 @@ import java.util.*; * call of the registerNodes method. These lists are necessary to keep track of all elements which are not a node like * inputs and outputs. All elements which are nodes can be obtained by {@link #findNode(Class, NodeFilter)} or * {@link #findNode(Class)}. + * * @see de.neemann.digital.core.element.Element#registerNodes(Model) */ public class Model implements Iterable { @@ -71,6 +73,7 @@ public class Model implements Iterable { private WindowPosManager windowPosManager; private HashSet oscillatingNodes; private boolean isInvalidSignal = false; + private AsyncSeq asyncInfos; private final ArrayList observers; private ArrayList observersStep; @@ -715,4 +718,21 @@ public class Model implements Iterable { return i.getValue(); return null; } + + + /** + * Sets async execution infos + * + * @param asyncInfos manly the frequency + */ + public void setAsyncInfos(AsyncSeq asyncInfos) { + this.asyncInfos = asyncInfos; + } + + /** + * @return the infos uset for async execution + */ + public AsyncSeq getAsyncInfos() { + return asyncInfos; + } } diff --git a/src/main/java/de/neemann/digital/core/wiring/AsyncSeq.java b/src/main/java/de/neemann/digital/core/wiring/AsyncSeq.java new file mode 100644 index 000000000..65bcd968a --- /dev/null +++ b/src/main/java/de/neemann/digital/core/wiring/AsyncSeq.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Helmut Neemann + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.core.wiring; + +import de.neemann.digital.core.*; +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.core.element.Keys; +import de.neemann.digital.lang.Lang; + +/** + */ +public class AsyncSeq implements Element { + + /** + * the clocks description + */ + public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(AsyncSeq.class) + .addAttribute(Keys.RUN_AT_REAL_TIME) + .addAttribute(Keys.FREQUENCY); + + private final int frequency; + + /** + * Creates a new instance + * + * @param attributes the clocks attributes + */ + public AsyncSeq(ElementAttributes attributes) { + if (attributes.get(Keys.RUN_AT_REAL_TIME)) { + int f = attributes.get(Keys.FREQUENCY); + if (f < 1) f = 1; + frequency = f; + } else + frequency = 0; + } + + @Override + public void setInputs(ObservableValues inputs) throws NodeException { + throw new NodeException(Lang.get("err_noInputsAvailable")); + } + + @Override + public ObservableValues getOutputs() { + return ObservableValues.EMPTY_LIST; + } + + @Override + public void registerNodes(Model model) { + model.setAsyncInfos(this); + } + + /** + * @return the clocks frequency + */ + public int getFrequency() { + return frequency; + } + +} diff --git a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java index 4945adadb..f635f1f8b 100644 --- a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java +++ b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java @@ -190,6 +190,7 @@ public class ElementLibrary implements Iterable .add(TransGate.DESCRIPTION)) .add(new LibraryNode(Lang.get("lib_misc")) .add(TestCaseElement.TESTCASEDESCRIPTION) + .add(AsyncSeq.DESCRIPTION) .add(PowerSupply.DESCRIPTION) .add(BusSplitter.DESCRIPTION) .add(Reset.DESCRIPTION) diff --git a/src/main/java/de/neemann/digital/draw/model/AsyncSequentialClock.java b/src/main/java/de/neemann/digital/draw/model/AsyncSequentialClock.java new file mode 100644 index 000000000..d9f50e22f --- /dev/null +++ b/src/main/java/de/neemann/digital/draw/model/AsyncSequentialClock.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Helmut Neemann + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.draw.model; + +import de.neemann.digital.core.Model; +import de.neemann.digital.core.ModelEvent; +import de.neemann.digital.core.ModelStateObserverTyped; +import de.neemann.digital.core.NodeException; +import de.neemann.digital.core.wiring.AsyncSeq; +import de.neemann.digital.gui.ErrorStopper; +import de.neemann.digital.gui.sync.Sync; +import de.neemann.digital.lang.Lang; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * The real time clock used to clock a circuit in async mode. + */ +public class AsyncSequentialClock implements ModelStateObserverTyped { + private final Model model; + private final ScheduledThreadPoolExecutor executor; + private final ErrorStopper stopper; + private final Sync modelSync; + private final int frequency; + private RealTimeRunner runner; + + /** + * Creates a new real time clock + * + * @param model the model + * @param asyncSeq the infos used to cofigure the clock + * @param executor the executor used to schedule the update + * @param stopper used to stop the model if an error is detected + * @param modelSync used to access a running model + */ + public AsyncSequentialClock(Model model, AsyncSeq asyncSeq, ScheduledThreadPoolExecutor executor, ErrorStopper stopper, Sync modelSync) { + this.model = model; + this.executor = executor; + this.stopper = stopper; + this.modelSync = modelSync; + int f = asyncSeq.getFrequency(); + if (f < 1) f = 1; + this.frequency = f; + } + + @Override + public void handleEvent(ModelEvent event) { + switch (event) { + case STARTED: + int delayMuS = 1000000 / frequency; + if (delayMuS < 50) + delayMuS = 50; + runner = new RealTimeRunner(delayMuS); + break; + case STOPPED: + if (runner != null) + runner.stop(); + break; + } + } + + @Override + public ModelEvent[] getEvents() { + return new ModelEvent[]{ModelEvent.STARTED, ModelEvent.STOPPED}; + } + + /** + * runs with defined rate + */ + private class RealTimeRunner { + + private final ScheduledFuture timer; + + RealTimeRunner(int delay) { + timer = executor.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + try { + modelSync.accessNEx(() -> model.doMicroStep(false)); + } catch (NodeException | RuntimeException e) { + stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e); + timer.cancel(false); + } + } + }, delay, delay, TimeUnit.MICROSECONDS); + } + + public void stop() { + if (timer != null) + timer.cancel(false); + } + } + +} diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index d1b518c6f..4f70de0a0 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -15,6 +15,7 @@ import de.neemann.digital.core.element.Keys; import de.neemann.digital.core.io.Button; import de.neemann.digital.core.io.*; import de.neemann.digital.core.memory.ROM; +import de.neemann.digital.core.wiring.AsyncSeq; import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.draw.elements.*; import de.neemann.digital.draw.gif.GifExporter; @@ -22,6 +23,7 @@ import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.library.CustomElement; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementNotFoundException; +import de.neemann.digital.draw.model.AsyncSequentialClock; import de.neemann.digital.draw.model.ModelCreator; import de.neemann.digital.draw.model.RealTimeClock; import de.neemann.digital.draw.shapes.Drawable; @@ -1188,6 +1190,18 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS if (threadRunnerCount > 1) throw new RuntimeException(Lang.get("err_moreThanOneFastClock")); } + if (!realTimeClockRunning && updateEvent == ModelEvent.MICROSTEP) { + // no real clock + AsyncSeq ai = model.getAsyncInfos(); + if (ai != null && ai.getFrequency() > 0) { + modelSync = new LockSync(); + model.addObserver( + new AsyncSequentialClock(model, ai, timerExecutor, this, modelSync)); + realTimeClockRunning = true; + } + } + + if (modelSync == null) modelSync = NoSync.INST; @@ -1429,10 +1443,12 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS @Override public void hasChanged() { - modelCreator.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted()); + if (!realTimeClockRunning) + modelCreator.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted()); model.fireManualChangeEvent(); circuitComponent.repaintNeeded(); - doStep.setEnabled(model.needsUpdate()); + if (!realTimeClockRunning) + doStep.setEnabled(model.needsUpdate()); } }