diff --git a/src/main/java/de/neemann/digital/core/element/Keys.java b/src/main/java/de/neemann/digital/core/element/Keys.java
index fbc564266..085a3cbb1 100644
--- a/src/main/java/de/neemann/digital/core/element/Keys.java
+++ b/src/main/java/de/neemann/digital/core/element/Keys.java
@@ -531,10 +531,5 @@ public final class Keys {
*/
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.KeyFile EXTERNAL_EXECUTABLE
- = new Key.KeyFile("externalExecutable", new File(""));
}
diff --git a/src/main/java/de/neemann/digital/core/extern/External.java b/src/main/java/de/neemann/digital/core/extern/External.java
index af31d1cb5..60a9c97fd 100644
--- a/src/main/java/de/neemann/digital/core/extern/External.java
+++ b/src/main/java/de/neemann/digital/core/extern/External.java
@@ -11,7 +11,6 @@ import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage;
import javax.swing.*;
-import java.io.File;
import java.io.IOException;
/**
@@ -38,15 +37,13 @@ public class External extends Node implements Element {
.addAttribute(Keys.EXTERNAL_INPUTS)
.addAttribute(Keys.EXTERNAL_OUTPUTS)
.addAttribute(Keys.EXTERNAL_CODE)
- .addAttribute(Keys.PROCESS_TYPE)
- .addAttribute(Keys.EXTERNAL_EXECUTABLE);
+ .addAttribute(Keys.PROCESS_TYPE);
private final ProcessFactory.Type type;
private final PortDefinition ins;
private final PortDefinition outs;
private final ObservableValues outputs;
private final String code;
- private final File executable;
private final String label;
private ObservableValues inputs;
private ProcessHandler processHandler;
@@ -64,7 +61,6 @@ public class External extends Node implements Element {
label = attr.getCleanLabel();
type = attr.get(Keys.PROCESS_TYPE);
code = attr.get(Keys.EXTERNAL_CODE);
- executable = attr.get(Keys.EXTERNAL_EXECUTABLE);
}
@Override
@@ -101,7 +97,7 @@ public class External extends Node implements Element {
@Override
public void init(Model model) throws NodeException {
try {
- processHandler = ProcessFactory.create(type, label, code, ins, outs, executable);
+ processHandler = ProcessFactory.create(type, label, code, ins, outs);
} catch (IOException e) {
throw new NodeException(Lang.get("err_errorCreatingProcess"), this, -1, null, e);
}
diff --git a/src/main/java/de/neemann/digital/core/extern/ProcessFactory.java b/src/main/java/de/neemann/digital/core/extern/ProcessFactory.java
index 3dc1b8b62..113bf97d4 100644
--- a/src/main/java/de/neemann/digital/core/extern/ProcessFactory.java
+++ b/src/main/java/de/neemann/digital/core/extern/ProcessFactory.java
@@ -9,7 +9,6 @@ import de.neemann.digital.core.extern.handler.GHDLProcess;
import de.neemann.digital.core.extern.handler.Generic;
import de.neemann.digital.lang.Lang;
-import java.io.File;
import java.io.IOException;
/**
@@ -43,16 +42,15 @@ public final class ProcessFactory {
* @param code the code to use
* @param inputs the inputs to use
* @param outputs the outputs to use
- * @param executable the parameters to use
* @return the created process handler
* @throws IOException IOException
*/
- public static ProcessHandler create(Type type, String label, String code, PortDefinition inputs, PortDefinition outputs, File executable) throws IOException {
+ public static ProcessHandler create(Type type, String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException {
switch (type) {
case Generic:
- return new Generic(executable);
+ return new Generic(code);
case GHDL:
- return new GHDLProcess(executable, label, code, inputs, outputs);
+ return new GHDLProcess(label, code, inputs, outputs);
default:
throw new IOException(Lang.get("err_processType_N_notFound", type.name()));
}
diff --git a/src/main/java/de/neemann/digital/core/extern/handler/GHDLProcess.java b/src/main/java/de/neemann/digital/core/extern/handler/GHDLProcess.java
index 3d41159be..39d21bf9c 100644
--- a/src/main/java/de/neemann/digital/core/extern/handler/GHDLProcess.java
+++ b/src/main/java/de/neemann/digital/core/extern/handler/GHDLProcess.java
@@ -11,20 +11,28 @@ import java.io.File;
import java.io.IOException;
/**
- * Can use GHDL as VHDL simulator
+ * Can use GHDL as VHDL simulator.
*/
public class GHDLProcess extends VHDLProcess {
+
/**
* Creates a new instance
*
- * @param executable the executable
* @param label the label
* @param code the code
* @param inputs th inputs to use
* @param outputs the outputs to use
* @throws IOException IOException
*/
- public GHDLProcess(File executable, String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException {
+ public GHDLProcess(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException {
super(label, code, inputs, outputs);
+
+ String ghdl = "ghdl";
+
+ File file = getVHDLFile();
+ ProcessStarter.start(file.getParentFile(), ghdl, "-a", "--ieee=synopsys", file.getName());
+ ProcessStarter.start(file.getParentFile(), ghdl, "-e", "--ieee=synopsys", "stdIOInterface");
+ ProcessBuilder pb = new ProcessBuilder(ghdl, "-r", "--ieee=synopsys", "stdIOInterface").redirectErrorStream(true).directory(file.getParentFile());
+ setProcess(pb.start());
}
}
diff --git a/src/main/java/de/neemann/digital/core/extern/handler/Generic.java b/src/main/java/de/neemann/digital/core/extern/handler/Generic.java
index 6ca91f191..a2b5c6fb8 100644
--- a/src/main/java/de/neemann/digital/core/extern/handler/Generic.java
+++ b/src/main/java/de/neemann/digital/core/extern/handler/Generic.java
@@ -5,7 +5,6 @@
*/
package de.neemann.digital.core.extern.handler;
-import java.io.File;
import java.io.IOException;
/**
@@ -18,8 +17,9 @@ public class Generic extends StdIOProcess {
* @param file the name of the application to start
* @throws IOException IOException
*/
- public Generic(File file) throws IOException {
- ProcessBuilder pb = new ProcessBuilder(file.getPath());
+ public Generic(String file) throws IOException {
+ String[] args = file.split(" ");
+ ProcessBuilder pb = new ProcessBuilder(args).redirectErrorStream(true);
setProcess(pb.start());
}
}
diff --git a/src/main/java/de/neemann/digital/core/extern/handler/ProcessStarter.java b/src/main/java/de/neemann/digital/core/extern/handler/ProcessStarter.java
new file mode 100644
index 000000000..8bff7718a
--- /dev/null
+++ b/src/main/java/de/neemann/digital/core/extern/handler/ProcessStarter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.lang.Lang;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Helper to start and wait for a process.
+ */
+public final class ProcessStarter {
+
+ private ProcessStarter() {
+ }
+
+ /**
+ * Helper to start a process.
+ * If result value is not null an exception is thrown.
+ *
+ * @param dir the folder in which the process is started
+ * @param args the argument
+ * @return the console output
+ * @throws IOException IOException
+ */
+ public static String start(File dir, String... args) throws IOException {
+ ProcessBuilder pb = new ProcessBuilder(args).redirectErrorStream(true).directory(dir);
+ Process p;
+ try {
+ p = pb.start();
+ } catch (IOException e) {
+ throw new IOException(Lang.get("err_couldNotStartProcess_N", Arrays.toString(args)));
+ }
+ ReaderThread rt = new ReaderThread(p.getInputStream());
+ rt.start();
+ try {
+ int exitValue = p.waitFor();
+ rt.join();
+
+ String output = rt.toString();
+
+ if (exitValue != 0)
+ throw new IOException(Lang.get("err_exitValueNotNull_N_O", exitValue, output));
+
+ return output;
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private static final class ReaderThread extends Thread {
+ private final ByteArrayOutputStream baos;
+ private final InputStream in;
+
+ private ReaderThread(InputStream in) {
+ this.in = in;
+ baos = new ByteArrayOutputStream();
+ }
+
+ @Override
+ public void run() {
+ try {
+ try {
+ byte[] buffer = new byte[4096];
+ int len;
+ while ((len = in.read(buffer)) > 0)
+ baos.write(buffer, 0, len);
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ // do nothing, simply end the thread
+ }
+ }
+
+ @Override
+ public String toString() {
+ return baos.toString();
+ }
+ }
+}
diff --git a/src/main/java/de/neemann/digital/core/extern/handler/StdIOProcess.java b/src/main/java/de/neemann/digital/core/extern/handler/StdIOProcess.java
index 9b2495333..3ae2cf0cc 100644
--- a/src/main/java/de/neemann/digital/core/extern/handler/StdIOProcess.java
+++ b/src/main/java/de/neemann/digital/core/extern/handler/StdIOProcess.java
@@ -11,15 +11,18 @@ import de.neemann.digital.core.extern.ProcessHandler;
import de.neemann.digital.lang.Lang;
import java.io.*;
+import java.util.LinkedList;
/**
- * The generic process description
+ * Communicates with an external process by sending values and receiving results via the stdio.
*/
public class StdIOProcess implements ProcessHandler {
private static final String PREFIX = "Digital:";
+ private static final int MAX_CONSOLE_LINES = 30;
private Process process;
private BufferedWriter writer;
private Thread thread;
+ private LinkedList consoleOut;
private final Object lock = new Object();
private String dataFound;
@@ -56,11 +59,14 @@ public class StdIOProcess implements ProcessHandler {
*/
private void setReaderWriter(BufferedReader reader, BufferedWriter writer) {
this.writer = writer;
-
+ consoleOut = new LinkedList<>();
thread = new Thread(() -> {
try {
String line;
while ((line = reader.readLine()) != null) {
+ consoleOut.add(line);
+ while (consoleOut.size() > MAX_CONSOLE_LINES)
+ consoleOut.removeFirst();
if (line.startsWith(PREFIX)) {
synchronized (lock) {
while (dataFound != null)
@@ -161,8 +167,12 @@ public class StdIOProcess implements ProcessHandler {
}
v.set(value, highZ);
}
- } else
- throw new IOException(Lang.get("err_processTerminatedUnexpected"));
+ } else {
+ StringBuilder sb = new StringBuilder();
+ for (String s : consoleOut)
+ sb.append(s).append("\n");
+ throw new IOException(Lang.get("err_processTerminatedUnexpected_O", sb.toString()));
+ }
}
@Override
diff --git a/src/main/java/de/neemann/digital/core/extern/handler/VHDLProcess.java b/src/main/java/de/neemann/digital/core/extern/handler/VHDLProcess.java
index 85b9984dc..222f805eb 100644
--- a/src/main/java/de/neemann/digital/core/extern/handler/VHDLProcess.java
+++ b/src/main/java/de/neemann/digital/core/extern/handler/VHDLProcess.java
@@ -9,12 +9,15 @@ import de.neemann.digital.core.extern.Port;
import de.neemann.digital.core.extern.PortDefinition;
import java.io.*;
+import java.nio.file.Files;
/**
- * Creates a VHDL process
+ * Creates a VHDL file which is able to cummunicate via stdio.
+ * The given code is used as VHDL code.
*/
public class VHDLProcess extends StdIOProcess {
private final File file;
+ private final File dir;
private static class InstanceHolder {
private static final String TEMPLATE = loadTemplate();
@@ -49,14 +52,16 @@ public class VHDLProcess extends StdIOProcess {
public VHDLProcess(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException {
String t = InstanceHolder.TEMPLATE;
t = t.replace("%name%", label);
- t = t.replace("%incount%", Integer.toString(inputs.getBits()));
- t = t.replace("%outcount%", Integer.toString(outputs.getBits()));
+ t = t.replace("%incount%", Integer.toString(inputs.getBits() - 1));
+ t = t.replace("%outcount%", Integer.toString(outputs.getBits() - 1));
t = t.replace("%ports%", createPorts(inputs, outputs));
t = t.replace("%signals%", createSignals(inputs, outputs));
t = t.replace("%map%", createMap(inputs, outputs));
t = t.replace("%inOutMapping%", createInOutMapping(inputs, outputs));
- file = File.createTempFile(label, ".vhdl");
+ dir = Files.createTempDirectory("digital_vhdl_").toFile();
+
+ file = new File(dir, label + ".vhdl");
try (Writer w = new FileWriter(file)) {
w.write(code);
w.write("\n\n\n");
@@ -67,7 +72,7 @@ public class VHDLProcess extends StdIOProcess {
/**
* @return the created vhdl file
*/
- public File getFile() {
+ public File getVHDLFile() {
return file;
}
@@ -145,6 +150,20 @@ public class VHDLProcess extends StdIOProcess {
@Override
public void close() throws IOException {
super.close();
- //if (file != null) file.delete();
+ if (dir != null)
+ removeFolder(dir);
+ }
+
+ private static void removeFolder(File dir) {
+ File[] list = dir.listFiles();
+ if (list != null) {
+ for (File f : list) {
+ if (f.isDirectory())
+ removeFolder(f);
+ else
+ f.delete();
+ }
+ }
+ dir.delete();
}
}
diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml
index c1f1d11f8..5c85758f1 100644
--- a/src/main/resources/lang/lang_de.xml
+++ b/src/main/resources/lang/lang_de.xml
@@ -846,9 +846,13 @@ Sind evtl. die Namen der Variablen nicht eindeutig?
Der externe Prozess konnte nicht gestartet werden!
Zeitüberschreitung beim Lesen von Daten!
Es wurden nicht genug Daten empfangen!
- Der empfangene Text enthielt ein ungültiges Zeichen: {0}!
- Der Prozess wurde unerwartet beendet!
+ Der Prozess sendete ein ungültiges Zeichen: {0}!
+ Der Prozess wurde unerwartet beendet!
+ {0}
Der Prozess konnte nicht beendet werden!
+ Prozess konnte nicht gestartet werden: {0}
+ Rückgabewert war nicht 0 sondern {0}:
+ {1}
Adress-Bits
Anzahl der Adress-Bits, die verwendet werden.
diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml
index 3ddda8e55..a2cacd0b5 100644
--- a/src/main/resources/lang/lang_en.xml
+++ b/src/main/resources/lang/lang_en.xml
@@ -842,8 +842,13 @@ The names of the variables may not be unique.
Timeout reading data from external process!
Not enough data received!
The received text contains an invalid character: {0}!
- The process has terminated unexpected!
+ The process has terminated unexpected!
+ {0}
Could not terminate the process!
+ Could not start process: {0}
+ Result value was not 0 but {0}:
+ {1}
+
Address Bits
Number of address bits used.
diff --git a/src/main/resources/templates/VHDLStdIOTemplate.templ b/src/main/resources/templates/VHDLStdIOTemplate.templ
index 975f9a893..9ab9ec263 100644
--- a/src/main/resources/templates/VHDLStdIOTemplate.templ
+++ b/src/main/resources/templates/VHDLStdIOTemplate.templ
@@ -27,10 +27,10 @@ architecture top_sim_a of stdIOInterface is
variable result : string (1 to slv'length);
variable r : integer;
begin
- r := 1;
+ r := slv'length;
for i in slv'range loop
result(r) := chr(slv(i));
- r := r + 1;
+ r := r - 1;
end loop;
return result;
end str;