first test to implement a async realtime executer

This commit is contained in:
hneemann 2018-04-05 23:03:06 +02:00
parent d48dd576cf
commit 89cf567dde
5 changed files with 202 additions and 2 deletions

View File

@ -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<Node> {
@ -71,6 +73,7 @@ public class Model implements Iterable<Node> {
private WindowPosManager windowPosManager;
private HashSet<Node> oscillatingNodes;
private boolean isInvalidSignal = false;
private AsyncSeq asyncInfos;
private final ArrayList<ModelStateObserver> observers;
private ArrayList<ModelStateObserver> observersStep;
@ -715,4 +718,21 @@ public class Model implements Iterable<Node> {
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;
}
}

View File

@ -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;
}
}

View File

@ -190,6 +190,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
.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)

View File

@ -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);
}
}
}

View File

@ -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());
}
}