first bunch of classes to integrate GHDL

This commit is contained in:
hneemann 2018-03-07 11:43:21 +01:00
parent 298d2890a0
commit b44c92c379
16 changed files with 576 additions and 3 deletions

View File

@ -53,7 +53,20 @@ public class NodeException extends ExceptionWithOrigin {
* @param values the values affected by this exception
*/
public NodeException(String message, Node node, int input, ImmutableList<ObservableValue> values) {
super(message);
this(message, node, input, values, null);
}
/**
* Creates a new instance.
*
* @param message the message
* @param node the nod effected by tis exception
* @param input the affected nodes input
* @param values the values affected by this exception
* @param cause the cause
*/
public NodeException(String message, Node node, int input, ImmutableList<ObservableValue> values, Exception cause) {
super(message, cause);
this.input = input;
this.nodes = new ArrayList<>();
if (node != null)

View File

@ -371,6 +371,9 @@ public class Key<VALUE> {
* A special string key to flag long multi line strings.
*/
public static final class LongString extends Key<String> {
private int rows = 6;
private int columns = 30;
/**
* Creates a new Key
*
@ -379,5 +382,42 @@ public class Key<VALUE> {
public LongString(String key) {
super(key, "");
}
/**
* @return the rows of the editor field
*/
public int getRows() {
return rows;
}
/**
* Sets the rows in the editor
*
* @param rows the number ow rows
* @return this for chained calls
*/
public LongString setRows(int rows) {
this.rows = rows;
return this;
}
/**
* @return the coloums of the editor field
*/
public int getColumns() {
return columns;
}
/**
* Sets the columns in the editor
*
* @param columns the number ow rows
* @return this for chained calls
*/
public LongString setColumns(int columns) {
this.columns = columns;
return this;
}
}
}

View File

@ -8,6 +8,7 @@ package de.neemann.digital.core.element;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.core.arithmetic.BarrelShifterMode;
import de.neemann.digital.core.arithmetic.LeftRightFormat;
import de.neemann.digital.core.extern.ProcessFactory;
import de.neemann.digital.core.io.InValue;
import de.neemann.digital.core.IntFormat;
import de.neemann.digital.core.memory.DataField;
@ -509,4 +510,31 @@ public final class Keys {
public static final Key<ROMManger> ROMMANAGER
= new Key<>("romContent", ROMManger.EMPTY);
/**
* The type of the external process
*/
public static final Key.KeyEnum<ProcessFactory.Type> PROCESS_TYPE
= new Key.KeyEnum<>("processType", ProcessFactory.Type.Generic, ProcessFactory.Type.values());
/**
* The inputs used by the external process
*/
public static final Key<String> EXTERNAL_INPUTS
= new Key<>("externalInputs", "in");
/**
* The outputs used by the external process
*/
public static final Key<String> EXTERNAL_OUTPUTS
= new Key<>("externalOutputs", "out");
/**
* The code to be executed by the external process
*/
public static final Key.LongString EXTERNAL_CODE
= new Key.LongString("Code").setRows(20).setColumns(40);
/**
* The parameters to be used by the external process
*/
public static final Key<String> EXTERNAL_PARAMETERS
= new Key<String>("externalParameters", "");
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2018 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.extern;
import de.neemann.digital.core.*;
import de.neemann.digital.core.element.*;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage;
import javax.swing.*;
import java.io.IOException;
/**
* The external component
*/
public class External extends Node implements Element {
/**
* The external component description
*/
public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(External.class) {
@Override
public PinDescriptions getInputDescription(ElementAttributes elementAttributes) {
return new PortDefinition(elementAttributes.get(Keys.EXTERNAL_INPUTS)).getPinDescriptions(PinDescription.Direction.input);
}
@Override
public PinDescriptions getOutputDescriptions(ElementAttributes elementAttributes) {
return new PortDefinition(elementAttributes.get(Keys.EXTERNAL_OUTPUTS)).getPinDescriptions(PinDescription.Direction.output);
}
}
.addAttribute(Keys.LABEL)
.addAttribute(Keys.EXTERNAL_INPUTS)
.addAttribute(Keys.EXTERNAL_OUTPUTS)
.addAttribute(Keys.EXTERNAL_CODE)
.addAttribute(Keys.PROCESS_TYPE)
.addAttribute(Keys.EXTERNAL_PARAMETERS);
private final ProcessFactory.Type type;
private final PortDefinition ins;
private final PortDefinition outs;
private final ObservableValues outputs;
private final String code;
private final String params;
private ObservableValues inputs;
private ProcessHandler processHandler;
/**
* Creates a new instance
*
* @param attr the elements attributes
*/
public External(ElementAttributes attr) {
super(true);
ins = new PortDefinition(attr.get(Keys.EXTERNAL_INPUTS));
outs = new PortDefinition(attr.get(Keys.EXTERNAL_OUTPUTS));
outputs = outs.createOutputs();
type = attr.get(Keys.PROCESS_TYPE);
code = attr.get(Keys.EXTERNAL_CODE);
params = attr.get(Keys.EXTERNAL_PARAMETERS);
}
@Override
public void setInputs(ObservableValues inputs) throws NodeException {
this.inputs = inputs;
for (int i = 0; i < inputs.size(); i++)
inputs.get(i).checkBits(ins.getPort(i).getBits(), this, i).addObserverToValue(this);
}
@Override
public void readInputs() throws NodeException {
try {
processHandler.writeValues(inputs);
} catch (IOException e) {
throw new NodeException(Lang.get("err_errorWritingDataToProcess"), this, -1, inputs, e);
}
}
@Override
public void writeOutputs() throws NodeException {
try {
processHandler.readValuesTo(outputs);
} catch (IOException e) {
throw new NodeException(Lang.get("err_errorReadingDataToProcess"), this, -1, outputs, e);
}
}
@Override
public ObservableValues getOutputs() {
return outputs;
}
@Override
public void init(Model model) throws NodeException {
try {
processHandler = ProcessFactory.create(type, code, params);
} catch (IOException e) {
throw new NodeException(Lang.get("err_errorCreatingProcess"), this, -1, null, e);
}
model.addObserver(event -> {
if (event.equals(ModelEvent.STOPPED)) {
try {
processHandler.close();
} catch (IOException e) {
SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorClosingExternalProcess")).addCause(e));
}
}
}, ModelEvent.STOPPED);
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018 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.extern;
import de.neemann.digital.core.Bits;
/**
* A port for external access
*/
public class Port {
private final int bits;
private final String name;
/**
* Creates a new port
*
* @param port the port
*/
public Port(String port) {
int p = port.indexOf(':');
if (p < 0) {
name = port;
bits = 1;
} else {
name = port.substring(0, p);
int b = 1;
try {
b = (int) Bits.decode(port.substring(p + 1));
} catch (Bits.NumberFormatException e) {
b = 1;
}
bits = b;
}
}
/**
* @return the number of bits
*/
public int getBits() {
return bits;
}
/**
* @return the name of the port
*/
public String getName() {
return name;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2018 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.extern;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.ObservableValues;
import de.neemann.digital.core.element.PinDescription;
import de.neemann.digital.core.element.PinDescriptions;
import de.neemann.digital.core.element.PinInfo;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* The list of ports used by an external component
*/
public class PortDefinition {
private final ArrayList<Port> ports;
/**
* Creates a ew instance
*
* @param portDescription a comma separated list of port names
*/
public PortDefinition(String portDescription) {
ports = new ArrayList<>();
StringTokenizer st = new StringTokenizer(portDescription, ",");
while (st.hasMoreTokens())
ports.add(new Port(st.nextToken().trim()));
}
/**
* Creates the output values to be used by the model
*
* @return the output values
*/
public ObservableValues createOutputs() {
ObservableValues.Builder builder = new ObservableValues.Builder();
for (Port p : ports)
builder.add(new ObservableValue(p.getName(), p.getBits()));
return builder.build();
}
/**
* Creates the pin descriptions
*
* @param direction the direction to use for the pin descriptions
* @return the pin descriptions
*/
public PinDescriptions getPinDescriptions(PinDescription.Direction direction) {
PinInfo[] infos = new PinInfo[ports.size()];
for (int i = 0; i < infos.length; i++)
infos[i] = new PinInfo(ports.get(i).getName(), "", direction);
return new PinDescriptions(infos);
}
/**
* Getter for a single port
*
* @param i the port number
* @return the port
*/
public Port getPort(int i) {
return ports.get(i);
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018 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.extern;
import de.neemann.digital.lang.Lang;
import java.io.IOException;
/**
* The factory used to create external processes
*/
public final class ProcessFactory {
private ProcessFactory() {
}
/**
* The process types
*/
public enum Type {
/**
* generic executable
*/
Generic,
/**
* ghdl vhdl interpreter
*/
GHDL
}
/**
* Creates a new process
*
* @param type the type of the prosess
* @param code the code to use
* @param params the parameters to use
* @return the created process handler
* @throws IOException IOException
*/
public static ProcessHandler create(Type type, String code, String params) throws IOException {
switch (type) {
default:
throw new IOException(Lang.get("err_processType_N_notFound", type.name()));
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 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.extern;
import de.neemann.digital.core.ObservableValues;
import java.io.Closeable;
import java.io.IOException;
/**
* A process handler implementation
*/
public interface ProcessHandler extends Closeable {
/**
* Transfers the given values to the external process
*
* @param values the values to transfer
* @throws IOException IOException
*/
void writeValues(ObservableValues values) throws IOException;
/**
* Reads values from the externalö process and writes them to the given values
*
* @param values the values to write to
* @throws IOException IOException
*/
void readValuesTo(ObservableValues values) throws IOException;
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018 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.extern.handler;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.ObservableValues;
import de.neemann.digital.core.extern.ProcessHandler;
import java.io.*;
import java.util.StringTokenizer;
/**
* The generic process description
*/
public abstract class StdIOProcess implements ProcessHandler {
private BufferedWriter writer;
private BufferedReader reader;
/**
* Sets the reader and writer
*
* @param reader the reader
* @param writer the writer
*/
public void setReaderWriter(BufferedReader reader, BufferedWriter writer) {
this.reader = reader;
this.writer = writer;
}
/**
* Sets the reader and writer
*
* @param in the input stream
* @param out the output stream
*/
public void setInputOutputStream(InputStream in, OutputStream out) {
setReaderWriter(
new BufferedReader(new InputStreamReader(in)),
new BufferedWriter(new OutputStreamWriter(out)));
}
@Override
public void writeValues(ObservableValues values) throws IOException {
boolean first = true;
for (ObservableValue v : values) {
if (first)
first = false;
else
writer.write(",");
writer.write(v.getName());
writer.write("=");
writer.write(Long.toHexString(v.getValue()));
}
writer.write("\n");
writer.flush();
}
@Override
public void readValuesTo(ObservableValues values) throws IOException {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("digital:")) {
StringTokenizer st = new StringTokenizer(line.substring(8), ",=", true);
for (ObservableValue v : values) {
if (!st.hasMoreTokens())
throw new IOException("not enough values received!");
String name = st.nextToken().trim();
if (name.equals(","))
name = st.nextToken().trim();
if (!name.equals(v.getName()))
throw new IOException("values in wrong order: expected " + v.getName() + " but found " + name);
if (!st.nextToken().equals("="))
throw new IOException("= expected");
final String valStr = st.nextToken();
if (valStr.equals("Z"))
v.setToHighZ();
else
v.setValue(Long.parseLong(valStr, 16));
}
return;
}
}
}
}

View File

@ -0,0 +1,10 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
/**
* The handlers which control the external processes
*/
package de.neemann.digital.core.extern.handler;

View File

@ -0,0 +1,10 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
/**
* Code to use external processes to implement the behavior of a component
*/
package de.neemann.digital.core.extern;

View File

@ -9,6 +9,7 @@ import de.neemann.digital.core.arithmetic.*;
import de.neemann.digital.core.arithmetic.Comparator;
import de.neemann.digital.core.basic.*;
import de.neemann.digital.core.element.*;
import de.neemann.digital.core.extern.External;
import de.neemann.digital.core.flipflops.*;
import de.neemann.digital.core.io.*;
import de.neemann.digital.core.memory.*;
@ -186,7 +187,8 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
.add(PowerSupply.DESCRIPTION)
.add(BusSplitter.DESCRIPTION)
.add(Reset.DESCRIPTION)
.add(Break.DESCRIPTION));
.add(Break.DESCRIPTION)
.add(External.DESCRIPTION));
addExternalJarComponents(jarFile);

View File

@ -13,6 +13,7 @@ import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.arithmetic.BarrelShifterMode;
import de.neemann.digital.core.arithmetic.LeftRightFormat;
import de.neemann.digital.core.element.*;
import de.neemann.digital.core.extern.ProcessFactory;
import de.neemann.digital.core.io.InValue;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.core.memory.ROM;
@ -71,6 +72,7 @@ public final class EditorFactory {
add(FormatToExpression.class, FormatEditor.class);
add(InverterConfig.class, InverterConfigEditor.class);
add(ROMManger.class, ROMManagerEditor.class);
add(ProcessFactory.Type.class, ProcessFactoryTypeEditor.class);
}
private <T> void add(Class<T> clazz, Class<? extends Editor<T>> editor) {
@ -167,7 +169,8 @@ public final class EditorFactory {
public StringEditor(String value, Key<String> key) {
if (key instanceof Key.LongString) {
text = new JTextArea(6, 30);
Key.LongString k = (Key.LongString) key;
text = new JTextArea(k.getRows(), k.getColumns());
compToAdd = new JScrollPane(text);
setLabelAtTop(true);
} else {
@ -538,6 +541,12 @@ public final class EditorFactory {
}
}
private static final class ProcessFactoryTypeEditor extends EnumEditor<ProcessFactory.Type> {
public ProcessFactoryTypeEditor(ProcessFactory.Type value, Key<ProcessFactory.Type> key) {
super(value, key);
}
}
private static class LanguageEditor extends LabelEditor<Language> {
private JComboBox comb;

View File

@ -696,6 +696,8 @@
Es kann dann ein Programm bis zum nächsten BRK-Befehl ausgeführt werden.
Der schnelle Simulationslauf kann nur genutzt werden, wenn der Echtzeittakt deaktiviert ist!</string>
<string name="elem_External">Extern</string>
<string name="elem_External_tt">Element zur Anbindung von externen Programmen zur Berechnung der Logik.</string>
<string name="elem_Diode">Diode</string>
<string name="elem_Diode_tt">Echt bidirektionale Diode.
@ -838,6 +840,11 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="err_couldNotCreateElement_N">Konnte kein Element vom Typ "{0}" erzeugen!</string>
<string name="err_centralDefinedRomsAreNotSupported">Zentral definierte ROM-Inhalte werden nicht unterstützt!</string>
<string name="err_processType_N_notFound">Prozesstyp {0} wurde nicht gefunden!</string>
<string name="err_errorWritingDataToProcess">Es konnten keine Werte an den externen Prozess übergeben werden!</string>
<string name="err_errorReadingDataToProcess">Es konnten keine Werte vom externen Prozess gelesen werden!</string>
<string name="err_errorCreatingProcess">Der externe Prozess konnte nicht gestartet werden!</string>
<string name="key_AddrBits">Adress-Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
<string name="key_AddrBits_tt">Anzahl der Adress-Bits, die verwendet werden.</string>
<string name="key_Bits">Daten-Bits</string><!-- And, NAnd, Or, NOr, XOr, XNOr, Not, LookUpTable, Delay, Out, In, Ground, VDD, Const, PullUp, PullDown, Driver, DriverInvSel, Multiplexer, Demultiplexer, D_FF, D_FF_AS, Register, ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM, GraphicCard, Counter, Add, Sub, Mul, BarrelShifter, Comparator, Neg, BitCount, Switch, Relay, PFET, NFET, FGPFET, FGNFET, TransGate -->
@ -1036,6 +1043,19 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="key_romContent">ROM Inhalte</string>
<string name="key_romContent_tt">Inhalt aller ROM Bausteine</string>
<string name="key_processType">Art des Prozesses</string>
<string name="key_processType_tt">Gibt an, was für ein Prozess gestartet werden soll, um den Code auszuführen.</string>
<string name="key_processType_Generic">Generisch</string>
<string name="key_processType_GHDL">GHDL</string>
<string name="key_externalInputs">Eingänge</string>
<string name="key_externalInputs_tt">Die Eingänge des externen Prozesses.</string>
<string name="key_externalOutputs">Ausgänge</string>
<string name="key_externalOutputs_tt">Die Ausgänge des externen Prozesses.</string>
<string name="key_Code">Programmcode</string>
<string name="key_Code_tt">Der Programmcode welcher ausgeführt werden soll.</string>
<string name="key_externalParameters">Parameter</string>
<string name="key_externalParameters_tt">Die Parameter, die an den externen Prozess übergeben werden sollen.</string>
<string name="mod_insertWire">Leitung eingefügt.</string>
<string name="mod_insertCopied">Aus Zwischenablage eingefügt.</string>
<string name="mod_setKey_N0_in_element_N1">Wert ''{0}'' in Element ''{1}'' verändert.</string>
@ -1336,6 +1356,8 @@ Soll dennoch exportiert werden?</string>
und analysiert werden. Aus nur einer Wahrheitstabelle kann keine Hardwarebeschreibung erzeugt werden.</string>
<string name="msg_noData">keine Daten</string>
<string name="msg_errorClosingExternalProcess">Konnte externen Prozess nicht beenden!</string>
<string name="ok">Ok</string>
<string name="rot_0"></string>
<string name="rot_180">180°</string>

View File

@ -692,6 +692,8 @@
Then you can run a simulated processor until a BRK instruction is reached.
Fast forward clocking can only be used if the real-time clock is disabled.</string>
<string name="elem_External">External</string>
<string name="elem_External_tt">Component to execute an external process to calculate the logic function.</string>
<string name="elem_Diode">Diode</string>
<string name="elem_Diode_tt">Simplified bidirectional diode. It is used to implement a wired AND or a wired OR..
@ -833,6 +835,11 @@ The names of the variables may not be unique.</string>
<string name="err_centralDefinedRomsAreNotSupported">ROM's defined in the settings are not supported!</string>
<string name="err_couldNotCreateElement_N">Could not create a component of type {0}!</string>
<string name="err_processType_N_notFound">Could not found process type {0}!</string>
<string name="err_errorWritingDataToProcess">Could not write values to the external process!</string>
<string name="err_errorReadingDataToProcess">Could not write values from the external process!</string>
<string name="err_errorCreatingProcess">Could not create the external process!</string>
<string name="key_AddrBits">Address Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
<string name="key_AddrBits_tt">Number of address bits used.</string>
<string name="key_Bits">Data Bits</string><!-- And, NAnd, Or, NOr, XOr, XNOr, Not, LookUpTable, Delay, Out, In, Ground, VDD, Const, PullUp, PullDown, Driver, DriverInvSel, Multiplexer, Demultiplexer, D_FF, D_FF_AS, Register, ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM, GraphicCard, Counter, Add, Sub, Mul, BarrelShifter, Comparator, Neg, BitCount, Switch, Relay, PFET, NFET, FGPFET, FGNFET, TransGate -->
@ -1027,6 +1034,19 @@ The names of the variables may not be unique.</string>
<string name="key_romContent">Content of ROM's</string>
<string name="key_romContent_tt">Content of all used ROM's</string>
<string name="key_processType">Process Type</string>
<string name="key_processType_tt">Defines which kind of process to use.</string>
<string name="key_processType_Generic">Generic</string>
<string name="key_processType_GHDL">GHDL</string>
<string name="key_externalInputs">Inputs</string>
<string name="key_externalInputs_tt">The inputs of the external process.</string>
<string name="key_externalOutputs">Outputs</string>
<string name="key_externalOutputs_tt">The outputs of the external process.</string>
<string name="key_Code">Programcode</string>
<string name="key_Code_tt">The programm code to use with the external process.</string>
<string name="key_externalParameters">Parameters</string>
<string name="key_externalParameters_tt">Parameters to be used by the external process.</string>
<string name="mod_insertWire">Inserted wire.</string>
<string name="mod_insertCopied">Insert from clipboard.</string>
<string name="mod_setKey_N0_in_element_N1">Value ''{0}'' in component ''{1}'' modified.</string>
@ -1323,6 +1343,8 @@ Start export anyway?</string>
<string name="win_romDialog">Included ROM's</string>
<string name="msg_noData">no data</string>
<string name="msg_errorClosingExternalProcess">Could not close external process!</string>
<string name="ok">Ok</string>
<string name="rot_0"></string>
<string name="rot_180">180°</string>

View File

@ -8,6 +8,7 @@ package de.neemann.digital.lang;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.basic.FanIn;
import de.neemann.digital.core.element.*;
import de.neemann.digital.core.extern.External;
import de.neemann.digital.core.memory.LookUpTable;
import de.neemann.digital.core.wiring.*;
import de.neemann.digital.draw.elements.PinException;
@ -66,6 +67,7 @@ public class TestElemConsistence extends TestCase {
|| e instanceof PriorityEncoder
|| e instanceof Splitter
|| e instanceof BusSplitter
|| e instanceof External
|| e instanceof LookUpTable);
}