Added a clock input to the keyboard component. See #138

This commit is contained in:
hneemann 2018-04-21 11:08:14 +02:00
parent 5627357b12
commit 52c4efb6e2
7 changed files with 185 additions and 67 deletions

View File

@ -3,8 +3,9 @@ Release Notes
HEAD, planned as v0.19
- Added support for asynchronous sequential circuits such as the Muller-pipeline.
Take a look at the new asynchronous examples for illustration.
- Braking changes:
- Breaking changes:
- Added an enable input to the terminal component.
- Added a clock input to the keyboard component.
- Bug fixes
- Fixed a bug in the VHDL export concerning an invalid optimization of a
std_logic_vector access.

View File

@ -35,6 +35,8 @@ import de.neemann.digital.gui.components.modification.Modifications;
import de.neemann.digital.gui.components.modification.ModifyAttribute;
import de.neemann.digital.gui.components.modification.ModifyMeasurementOrdering;
import de.neemann.digital.gui.components.table.TableDialog;
import de.neemann.digital.gui.components.terminal.Keyboard;
import de.neemann.digital.gui.components.terminal.KeyboardDialog;
import de.neemann.digital.gui.components.testing.ValueTableDialog;
import de.neemann.digital.gui.components.tree.LibraryTreeModel;
import de.neemann.digital.gui.components.tree.SelectTree;
@ -1221,6 +1223,8 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
// all repainting is initiated by user actions!
modelCreator.connectToGui(null);
handleKeyboardComponent(updateEvent, modelSync);
doStep.setEnabled(false);
runToBreakAction.setEnabled(!realTimeClockRunning && model.isFastRunModel());
@ -1250,6 +1254,37 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
return false;
}
private void handleKeyboardComponent(ModelEvent updateEvent, Sync modelSync) {
KeyboardDialog.KeyPressedHandler handler = null;
for (Keyboard k : model.findNode(Keyboard.class)) {
if (handler == null)
if (updateEvent == ModelEvent.MICROSTEP)
handler = keyboard -> {
keyboard.hasChanged();
modelCreator.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted());
model.fireManualChangeEvent();
doStep.setEnabled(model.needsUpdate());
circuitComponent.repaintNeeded();
};
else
handler = keyboard -> {
try {
modelSync.accessNEx(() -> {
keyboard.hasChanged();
model.fireManualChangeEvent();
model.doStep();
});
circuitComponent.repaintNeeded();
} catch (NodeException | RuntimeException e) {
showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
}
};
windowPosManager.register("keyboard_" + k.getLabel(), new KeyboardDialog(this, k, handler));
}
}
private void showMeasurementGraph(ModelEvent updateEvent) {
List<String> ordering = circuitComponent.getCircuit().getMeasurementOrdering();
windowPosManager.register("dataSet", GraphDialog.createLiveDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);

View File

@ -14,8 +14,6 @@ import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.element.Keys;
import javax.swing.*;
import static de.neemann.digital.core.element.PinInfo.input;
/**
@ -27,17 +25,20 @@ public class Keyboard extends Node implements Element {
*/
public static final ElementTypeDescription DESCRIPTION
= new ElementTypeDescription(Keyboard.class,
input("sel"))
input("C").setClock(),
input("en"))
.addAttribute(Keys.ROTATE)
.addAttribute(Keys.LABEL);
.addAttribute(Keys.LABEL)
.addAttribute(Keys.INVERTER_CONFIG);
private final String label;
private KeyboardDialog keyboardDialog;
private ObservableValue data;
private ObservableValue select;
private boolean sel;
private boolean lastSel = false;
private int keyData;
private final ObservableValue data;
private final ObservableValue isKeyOut;
private KeyboardInterface keyboardInterface;
private ObservableValue clock;
private ObservableValue enable;
private boolean enableVal;
private boolean lastClock = false;
/**
* Creates a new terminal instance
@ -48,49 +49,83 @@ public class Keyboard extends Node implements Element {
data = new ObservableValue("D", 16)
.setToHighZ()
.setPinDescription(DESCRIPTION);
isKeyOut = new ObservableValue("av", 1)
.setPinDescription(DESCRIPTION);
label = attributes.getCleanLabel();
}
@Override
public void setInputs(ObservableValues inputs) throws NodeException {
select = inputs.get(0).addObserverToValue(this).checkBits(1, this);
clock = inputs.get(0).addObserverToValue(this).checkBits(1, this, 0);
enable = inputs.get(1).addObserverToValue(this).checkBits(1, this, 1);
}
@Override
public ObservableValues getOutputs() {
return data.asList();
return new ObservableValues(data, isKeyOut);
}
@Override
public void readInputs() throws NodeException {
sel = select.getBool();
if (!lastSel && sel) {
KeyboardDialog kbd = getKeyboard();
if (kbd != null)
keyData = kbd.getChar();
else
keyData = 0;
}
lastSel = sel;
enableVal = enable.getBool();
boolean nowClock = clock.getBool();
if (keyboardInterface != null && nowClock && !lastClock && enableVal)
keyboardInterface.removeChar();
lastClock = nowClock;
}
@Override
public void writeOutputs() throws NodeException {
if (sel)
data.setValue(keyData);
else
data.setToHighZ();
if (keyboardInterface != null) {
if (enableVal)
data.setValue(keyboardInterface.getChar());
else
data.setToHighZ();
isKeyOut.setBool(keyboardInterface.isChar());
} else {
if (enableVal)
data.setValue(0);
else
data.setToHighZ();
isKeyOut.setBool(false);
}
}
private KeyboardDialog getKeyboard() {
if (keyboardDialog == null || !keyboardDialog.isVisible()) {
SwingUtilities.invokeLater(() -> {
if (keyboardDialog == null || !keyboardDialog.isVisible()) {
keyboardDialog = new KeyboardDialog(getModel().getWindowPosManager().getMainFrame());
getModel().getWindowPosManager().register("keyboard_" + label, keyboardDialog);
}
});
}
return keyboardDialog;
/**
* Sets the keyboard interface
*
* @param keyboardInterface the keyboard interface
*/
public void setKeyboardDialog(KeyboardInterface keyboardInterface) {
this.keyboardInterface = keyboardInterface;
}
/**
* @return the keyboard label
*/
public String getLabel() {
return label;
}
/**
* The keyboard interface
*/
public interface KeyboardInterface {
/**
* @return a char or 0 if no char available
*/
int getChar();
/**
* @return true if there is a char
*/
boolean isChar();
/**
* removes the first character
*/
void removeChar();
}
}

View File

@ -15,7 +15,7 @@ import java.awt.event.KeyEvent;
/**
* A simple keyboard implementation
*/
public class KeyboardDialog extends JDialog {
public class KeyboardDialog extends JDialog implements Keyboard.KeyboardInterface {
private final JLabel textLabel;
private final Object textLock = new Object();
private String text;
@ -23,13 +23,15 @@ public class KeyboardDialog extends JDialog {
/**
* Create a new Instance
*
* @param owner the owner frame
* @param owner the owner frame
* @param keyboard the keyboard node which has opened this dialog
* @param keyPressedHandler handler called every time a key is typed
*/
public KeyboardDialog(Frame owner) {
super(owner, Lang.get("elem_Keyboard"), false);
public KeyboardDialog(Frame owner, Keyboard keyboard, KeyPressedHandler keyPressedHandler) {
super(owner, Lang.get("elem_Keyboard") + " " + keyboard.getLabel(), false);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
textLabel = new JLabel("Enter Text ");
textLabel = new JLabel(Lang.get("msg_enterText") + " ");
textLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
getContentPane().add(textLabel);
text = "";
@ -43,6 +45,7 @@ public class KeyboardDialog extends JDialog {
text += e.getKeyChar();
t = text;
}
keyPressedHandler.keyPressed(keyboard);
textLabel.setText(t);
}
});
@ -50,24 +53,47 @@ public class KeyboardDialog extends JDialog {
pack();
setLocationRelativeTo(owner);
setVisible(true);
keyboard.setKeyboardDialog(this);
}
/**
* @return the oldest char
*/
@Override
public int getChar() {
synchronized (textLock) {
if (text.length() == 0)
return 0;
else {
int c = text.charAt(0);
String t;
else
return text.charAt(0);
}
}
@Override
public boolean isChar() {
synchronized (textLock) {
return text.length() > 0;
}
}
@Override
public void removeChar() {
synchronized (textLock) {
if (text.length() > 0) {
text = text.substring(1);
t = text;
final String t = text;
SwingUtilities.invokeLater(() -> textLabel.setText(t));
return c;
}
}
}
/**
* The handler called if a key is typed.
*/
public interface KeyPressedHandler {
/**
* Called if a key is typed
*
* @param keyboard the keyboard used
*/
void keyPressed(Keyboard keyboard);
}
}

View File

@ -224,8 +224,13 @@
Dieses Element puffert die Eingaben, welche dann ausgelesen werden können.
Es wird ein eigenes Fenster für die Eingabe geöffnet.
</string>
<string name="elem_Keyboard_pin_sel">Wenn gesetzt, ist der Ausgang D aktiv und ein Tastendruck wird ausgegeben.</string>
<string name="elem_Keyboard_pin_C">Takt. Eine steigende Flanke entfernt das älteste Zeichen aus dem Buffer.</string>
<string name="elem_Keyboard_pin_en">Wenn gesetzt, ist der Ausgang D aktiv und ein Tastendruck wird ausgegeben.
Ist gleichzeitig die freigabe für den Takt.</string>
<string name="elem_Keyboard_pin_D">Die letzte Taste oder 0, wenn keine Taste gedrückt.</string>
<string name="elem_Keyboard_pin_av">Dieser Ausgang zeigt an, dass Zeichen vorhanden sind.
Er kann genutzt werden, um einen Interrupt auszulösen.</string>
<string name="elem_Terminal">Terminal</string>
<string name="elem_Terminal_tt">Ein Terminal, auf dem ASCII-Zeichen ausgegeben werden können.
Öffnet ein eigenes Fenster, um die Ausgabe anzuzeigen.</string>
@ -1429,6 +1434,7 @@ Soll dennoch exportiert werden?</string>
nicht der Fall ist, wird die Fehlermeldung der externen Anwendung angezeigt. Wenn es möglich ist, werden zudem
die Eingangs- und Ausgangsdefinitionen an den aktuellen Code angepasst.</string>
<string name="msg_applicationFileNotFound">Ausführbare Datei "{0}" nicht gefunden!</string>
<string name="msg_enterText">Text eingeben!</string>
<string name="ok">Ok</string>
<string name="rot_0"></string>

View File

@ -223,13 +223,19 @@
<string name="elem_RotEncoder_tt">Rotary knob with rotary encoder. Used to detect rotational movements.</string>
<string name="elem_RotEncoder_pin_A">encoder signal A</string>
<string name="elem_RotEncoder_pin_B">encoder signal B</string>
<string name="elem_Keyboard">Keyboard</string>
<string name="elem_Keyboard_tt">A keyboard that can be used to enter text.
This component buffers the inputs, which can then be read out.
This component buffers the input, which can then be read out.
A separate window is opened for the text input.
</string>
<string name="elem_Keyboard_pin_sel">If high the output D is active and one keystroke is output.</string>
<string name="elem_Keyboard_pin_D">The last pressed key, or zero if no key is typed.</string>
<string name="elem_Keyboard_pin_C">Clock. A rising edge removes the oldest character from the buffer.</string>
<string name="elem_Keyboard_pin_en">If high the output D is active and one character is output.
It also enables the clock input.</string>
<string name="elem_Keyboard_pin_D">The last typed character, or zero if no character is available.</string>
<string name="elem_Keyboard_pin_av">This output indicates that characters are available.
It can be used to trigger an interrupt.</string>
<string name="elem_Terminal">Terminal</string>
<string name="elem_Terminal_tt">You can write ASCII characters to this terminal.
The terminal opens its own window to visualize the output.</string>
@ -1417,6 +1423,7 @@
If this is not the case, the error message of the external application is displayed.
If possible, the input and output definitions are also adapted to the current code.</string>
<string name="msg_applicationFileNotFound">Executable file "{0}" not found!</string>
<string name="msg_enterText">Enter Text!</string>
<string name="ok">Ok</string>
<string name="rot_0"></string>

View File

@ -170,7 +170,7 @@
<visualElement>
<elementName>Terminal</elementName>
<elementAttributes/>
<pos x="420" y="900"/>
<pos x="420" y="920"/>
</visualElement>
<visualElement>
<elementName>VDD</elementName>
@ -575,6 +575,10 @@
<p1 x="780" y="640"/>
<p2 x="800" y="640"/>
</wire>
<wire>
<p1 x="400" y="960"/>
<p2 x="420" y="960"/>
</wire>
<wire>
<p1 x="240" y="260"/>
<p2 x="260" y="260"/>
@ -619,10 +623,6 @@
<p1 x="460" y="580"/>
<p2 x="480" y="580"/>
</wire>
<wire>
<p1 x="380" y="900"/>
<p2 x="420" y="900"/>
</wire>
<wire>
<p1 x="780" y="200"/>
<p2 x="800" y="200"/>
@ -776,7 +776,7 @@
<p2 x="1160" y="660"/>
</wire>
<wire>
<p1 x="400" y="920"/>
<p1 x="380" y="920"/>
<p2 x="420" y="920"/>
</wire>
<wire>
@ -923,6 +923,10 @@
<p1 x="580" y="680"/>
<p2 x="640" y="680"/>
</wire>
<wire>
<p1 x="400" y="940"/>
<p2 x="420" y="940"/>
</wire>
<wire>
<p1 x="780" y="620"/>
<p2 x="800" y="620"/>
@ -959,10 +963,6 @@
<p1 x="660" y="300"/>
<p2 x="680" y="300"/>
</wire>
<wire>
<p1 x="400" y="940"/>
<p2 x="420" y="940"/>
</wire>
<wire>
<p1 x="580" y="560"/>
<p2 x="620" y="560"/>
@ -995,6 +995,10 @@
<p1 x="760" y="880"/>
<p2 x="880" y="880"/>
</wire>
<wire>
<p1 x="400" y="880"/>
<p2 x="420" y="880"/>
</wire>
<wire>
<p1 x="600" y="500"/>
<p2 x="620" y="500"/>
@ -1276,8 +1280,8 @@
<p2 x="400" y="580"/>
</wire>
<wire>
<p1 x="400" y="920"/>
<p2 x="400" y="940"/>
<p1 x="400" y="940"/>
<p2 x="400" y="960"/>
</wire>
<wire>
<p1 x="400" y="580"/>
@ -1305,12 +1309,16 @@
</wire>
<wire>
<p1 x="400" y="860"/>
<p2 x="400" y="920"/>
<p2 x="400" y="880"/>
</wire>
<wire>
<p1 x="400" y="360"/>
<p2 x="400" y="400"/>
</wire>
<wire>
<p1 x="400" y="880"/>
<p2 x="400" y="940"/>
</wire>
<wire>
<p1 x="600" y="100"/>
<p2 x="600" y="220"/>
@ -1653,7 +1661,7 @@
</wire>
<wire>
<p1 x="380" y="820"/>
<p2 x="380" y="900"/>
<p2 x="380" y="920"/>
</wire>
</wires>
<measurementOrdering/>