diff --git a/pom.xml b/pom.xml
index 0699d0dd9..ea5a3b461 100644
--- a/pom.xml
+++ b/pom.xml
@@ -356,6 +356,13 @@
2.0.6
test
+
+ com.github.stefanbirkner
+ system-rules
+ 1.19.0
+ test
+
+
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 350f900aa..00b3ba04a 100644
--- a/src/main/java/de/neemann/digital/core/element/Keys.java
+++ b/src/main/java/de/neemann/digital/core/element/Keys.java
@@ -1028,4 +1028,10 @@ private static File getATMISPPath() {
*/
public static final Key SKIP_HDL =
new Key<>("skipHDL", false).setSecondary();
+
+ /**
+ * verilog code lib path, find include and depends
+ */
+ public static final Key SETTINGS_VERILOG_LIB_DIR =
+ new Key.KeyFile("verilogLib", new File("")).setDirectoryOnly(true).setSecondary();
}
diff --git a/src/main/java/de/neemann/digital/core/extern/Application.java b/src/main/java/de/neemann/digital/core/extern/Application.java
index aaec952a7..badf47b6c 100644
--- a/src/main/java/de/neemann/digital/core/extern/Application.java
+++ b/src/main/java/de/neemann/digital/core/extern/Application.java
@@ -65,7 +65,11 @@ enum Type {
/**
* Icarus verilog interpreter
*/
- IVERILOG
+ IVERILOG,
+ /**
+ * extern socket app interpreter
+ */
+ Socket
}
/**
@@ -83,6 +87,8 @@ static Application create(Type type, ElementAttributes attr) {
return new ApplicationGHDL(attr);
case IVERILOG:
return new ApplicationIVerilog(attr);
+ case Socket:
+ return new ApplicationSocket();
default:
return null;
}
diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java b/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java
index 9228cbc38..cdaef6028 100644
--- a/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java
+++ b/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java
@@ -29,6 +29,7 @@ public class ApplicationIVerilog extends ApplicationVerilogStdIO {
private String iverilogFolder;
private String iverilog;
private String vvp;
+ private String verilogLibPath;
/**
* Initialize a new instance
@@ -39,6 +40,21 @@ public ApplicationIVerilog(ElementAttributes attr) {
this.attr = attr;
iverilogFolder = "";
hasIverilog = findIVerilog();
+ verilogLibPath = getVerilogLibPath();
+ }
+
+ /**
+ * verilog code lib path, find includes and depends code
+ * support config with env vars
+ * @return lib path
+ */
+ private String getVerilogLibPath() {
+ String libPath = Settings.getInstance().get(Keys.SETTINGS_VERILOG_LIB_DIR).getAbsolutePath();
+ if (libPath == null || libPath.equals("")) {
+ return "";
+ }
+ libPath = "-y"+ApplicationIVerilog.replaceEnvVars(libPath);
+ return libPath;
}
@Override
@@ -60,6 +76,7 @@ public ProcessInterface start(String label, String code, PortDefinition inputs,
.add("-o")
.add(testOutputName)
.add(attr, Keys.IVERILOG_OPTIONS)
+ .addString(this.verilogLibPath)
.add(file.getName())
.getArray()
);
@@ -106,6 +123,7 @@ public String checkCode(String label, String code, PortDefinition inputs, PortDe
.add("-o")
.add(testOutputName)
.add(attr, Keys.IVERILOG_OPTIONS)
+ .addString(this.verilogLibPath)
.add(file.getName())
.getArray()
);
diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java b/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java
new file mode 100644
index 000000000..a65cde4b0
--- /dev/null
+++ b/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2024 Ron Ren.
+ * 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.extern.handler.ProcessInterface;
+import de.neemann.digital.core.extern.handler.SocketInterface;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * application with shared mem map file comm
+ */
+public class ApplicationSocket implements Application {
+ @Override
+ public ProcessInterface start(String label, String code, PortDefinition inputs, PortDefinition outputs, File root) throws IOException {
+ String ipPort = "127.0.0.1:8009";
+ if (code.length() > 0 && code.indexOf(":") > 0) {
+ ipPort = code;
+ }
+ return new SocketInterface(ipPort, inputs, outputs);
+ }
+
+ @Override
+ public boolean checkSupported() {
+ return true;
+ }
+
+ @Override
+ public String checkCode(String label, String code, PortDefinition inputs, PortDefinition outputs, File root) throws IOException {
+ return null;
+ }
+
+}
diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java b/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java
index e8ac90af5..6a8b97b56 100644
--- a/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java
+++ b/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java
@@ -16,6 +16,8 @@
import java.io.*;
import java.nio.file.Files;
import java.util.NoSuchElementException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
@@ -208,6 +210,30 @@ private void scanPort(VerilogTokenizer st, PortDefinition in, PortDefinition out
}
}
+ /**
+ * replace env string in options
+ * @param input option string
+ * @return replace every ${xx} env vars
+ */
+ public static String replaceEnvVars(String input) {
+ Pattern pattern = Pattern.compile("(? 0) {
+ this.inSeta.set(pos, true);
+ this.inSetb.set(pos, false);
+ } else if (hZ > 0) {
+ this.inSeta.set(pos, false);
+ this.inSetb.set(pos, true);
+ } else {
+ this.inSeta.set(pos, false);
+ this.inSetb.set(pos, false);
+ }
+ pos++;
+ }
+ }
+ this.buf1.clear();
+ this.buf2.clear();
+ this.buf1.put(this.inSeta.toByteArray());
+ this.buf2.put(this.inSetb.toByteArray());
+ while (this.buf1.position() < this.inLen/8) {
+ this.buf1.put((byte) 0);
+ }
+ while (this.buf2.position() < this.inLen/8) {
+ this.buf2.put((byte) 0);
+ }
+ try {
+ this.tcpClient.sendMessage(SocketInterface.concatenateByteArrays(this.buf1.array(), this.buf2.array()));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void readValues(ObservableValues values) throws IOException {
+ this.initSocket();
+ byte[] buf = this.tcpClient.receiveMessage();
+ if (buf == null) {
+ return;
+ }
+ int posAdd = (buf.length*8)/2;
+ this.outSet = BitSet.valueOf(buf);
+ int pos = 0;
+ for (ObservableValue v : values) {
+ long value = 0L;
+ long hZ = 0L;
+ for (int i=0; i sendQueue = new LinkedBlockingQueue();
+ private volatile byte[] recvMsg;
+
+ private volatile boolean isRunning = false;
+
+ /**
+ * status
+ * @return current status
+ */
+ public boolean getIsRunning() {
+ return this.isRunning;
+ }
+
+ /***
+ * construction
+ * @param serverAddress extern app ip address
+ * @param serverPort extern app port
+ */
+ public TCPClient(String serverAddress, int serverPort) {
+ this.serverAddress = serverAddress;
+ this.serverPort = serverPort;
+ }
+
+ /**
+ * start tcpclient
+ * @throws IOException exception
+ */
+ public void start() throws IOException {
+ if (this.socket == null || this.socket.isClosed() || !this.isRunning) {
+ socket = new Socket(serverAddress, serverPort);
+ this.output = new DataOutputStream(socket.getOutputStream());
+ this.input = new DataInputStream(socket.getInputStream());
+ this.isRunning = true;
+ new Thread(new Sender(this)).start();
+ new Thread(new Receiver(this)).start();
+ }
+ }
+
+ /***
+ * send message to extern app
+ * @param bytes send bytes
+ * @throws InterruptedException put msg failed.
+ */
+ public void sendMessage(byte[] bytes) throws InterruptedException {
+ sendQueue.put(bytes);
+ }
+
+ /***
+ * get message from extern app
+ * @return current extern app signals
+ */
+ public byte[] receiveMessage() {
+ return recvMsg;
+ }
+
+ /***
+ * sender
+ */
+ private class Sender implements Runnable {
+ private TCPClient tcpClient;
+
+ /**
+ * construction
+ * @param tcpClient tcpclient instance
+ */
+ Sender(TCPClient tcpClient) {
+ this.tcpClient = tcpClient;
+ }
+ @Override
+ public void run() {
+ try {
+ while (this.tcpClient.isRunning) {
+ byte[] msg = sendQueue.take();
+ this.tcpClient.output.writeInt(msg.length);
+ this.tcpClient.output.write(msg);
+ this.tcpClient.output.flush();
+ }
+ } catch (InterruptedException e) {
+ System.out.println("Sender interrupted.");
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ this.tcpClient.output.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ this.tcpClient.isRunning = false;
+ }
+ }
+ }
+
+ /***
+ * Receiver
+ */
+ private class Receiver implements Runnable {
+ private TCPClient tcpClient;
+ /**
+ * construction
+ * @param tcpClient tcpclient instance
+ */
+ Receiver(TCPClient tcpClient) {
+ this.tcpClient = tcpClient;
+ }
+ @Override
+ public void run() {
+ try {
+ while (this.tcpClient.isRunning) {
+ int msgLen = this.tcpClient.input.readInt();
+ this.tcpClient.recvMsg = new byte[msgLen];
+ this.tcpClient.input.readFully(this.tcpClient.recvMsg);
+ }
+ } catch (IOException e) {
+ System.out.println("Receiver interrupted.");
+ } finally {
+ try {
+ this.tcpClient.input.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ this.tcpClient.isRunning = false;
+ }
+ }
+ }
+
+ /**
+ * close tcp connection
+ * @throws IOException exception
+ */
+ public void close() throws IOException {
+ this.isRunning = false;
+ socket.close();
+ }
+}
diff --git a/src/main/java/de/neemann/digital/gui/Settings.java b/src/main/java/de/neemann/digital/gui/Settings.java
index 25bdf6d09..cf8b96986 100644
--- a/src/main/java/de/neemann/digital/gui/Settings.java
+++ b/src/main/java/de/neemann/digital/gui/Settings.java
@@ -55,6 +55,7 @@ private static List createKeyList() {
intList.add(Keys.SETTINGS_ATMISP);
intList.add(Keys.SETTINGS_GHDL_PATH);
intList.add(Keys.SETTINGS_IVERILOG_PATH);
+ intList.add(Keys.SETTINGS_VERILOG_LIB_DIR);
intList.add(Keys.SETTINGS_TOOLCHAIN_CONFIG);
intList.add(Keys.SETTINGS_FONT_SCALING);
intList.add(Keys.SETTINGS_MAC_MOUSE);
diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml
index 5fe453990..71f812c2d 100644
--- a/src/main/resources/lang/lang_de.xml
+++ b/src/main/resources/lang/lang_de.xml
@@ -1498,6 +1498,7 @@ Sind evtl. die Namen der Variablen nicht eindeutig?
Generisch
GHDL
IVerilog
+ Socket
Eingänge
Die Eingänge des externen Prozesses. Es handelt sich um eine kommaseparierte
Liste mit Signalnamen. Bei jedem Signalnamen kann, mit einem Doppelpunkt getrennt, eine Bitanzahl angegeben werden.
@@ -1525,6 +1526,9 @@ Sind evtl. die Namen der Variablen nicht eindeutig?
Pfad zum Icarus-Verilog-Installationsordner. Nur notwendig, wenn Sie iverilog
verwenden möchten, um mit Verilog definierte Komponenten zu simulieren.
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
Maximalwert
Wird hier eine Null eingetragen, wird der maximal mögliche Wert verwendet (Alle Bits
sind Eins).
diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml
index c595caef4..1384d79ba 100644
--- a/src/main/resources/lang/lang_en.xml
+++ b/src/main/resources/lang/lang_en.xml
@@ -1485,6 +1485,7 @@
Generic
GHDL
IVerilog
+ Socket
Inputs
The inputs of the external process.
It is a comma-separated list of signal names. For each signal name, a number of bits separated by a colon
@@ -1514,6 +1515,9 @@
iverilog to simulate
components defined with Verilog.
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
Maximum Value
If a zero is entered, the maximum possible value is used (all bits are one).
diff --git a/src/main/resources/lang/lang_es.xml b/src/main/resources/lang/lang_es.xml
index 0394b10c7..d9d2aeed3 100644
--- a/src/main/resources/lang/lang_es.xml
+++ b/src/main/resources/lang/lang_es.xml
@@ -1125,6 +1125,7 @@ In the file howTo.md you can find more details about translations.
Genérica
GHDL
iVerilog
+ Socket
Entradas
Entradas del proceso externo.
Es una lista de nombres de señales separadas por comas. Para cada nombre de señal, usando ":", puede indicarse
diff --git a/src/main/resources/lang/lang_es_ref.xml b/src/main/resources/lang/lang_es_ref.xml
index 9dff76a1d..888385963 100644
--- a/src/main/resources/lang/lang_es_ref.xml
+++ b/src/main/resources/lang/lang_es_ref.xml
@@ -1200,6 +1200,7 @@ In the file howTo.md you can find more details about translations.
Generic
GHDL
IVerilog
+ Socket
Inputs
The inputs of the external process.
It is a comma-separated list of signal names. For each signal name, a number of bits separated by a colon
@@ -1221,6 +1222,9 @@ In the file howTo.md you can find more details about translations.
iverilog to simulate
components defined with Verilog.
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
Maximum Value
If a zero is entered, the maximum possible value is used (all bits are one).
Output is High
diff --git a/src/main/resources/lang/lang_fr.xml b/src/main/resources/lang/lang_fr.xml
index 23d049c21..af426262d 100644
--- a/src/main/resources/lang/lang_fr.xml
+++ b/src/main/resources/lang/lang_fr.xml
@@ -1550,6 +1550,7 @@ In the file howTo.md you can find more details about translations.
Générique
GHDL
IVerilog
+ Socket
Entrées
Les entrées du processus externe.
C'est une liste de noms de signaux séparée par des virgules. Pour chaque nom de signal, un nombre de bits
diff --git a/src/main/resources/lang/lang_fr_ref.xml b/src/main/resources/lang/lang_fr_ref.xml
index 8996f74dd..b2fe40f69 100644
--- a/src/main/resources/lang/lang_fr_ref.xml
+++ b/src/main/resources/lang/lang_fr_ref.xml
@@ -1418,6 +1418,7 @@ In the file howTo.md you can find more details about translations.
Generic
GHDL
IVerilog
+ Socket
Inputs
The inputs of the external process.
It is a comma-separated list of signal names. For each signal name, a number of bits separated by a colon
@@ -1442,6 +1443,9 @@ In the file howTo.md you can find more details about translations.
iverilog to simulate
components defined with Verilog.
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
Maximum Value
If a zero is entered, the maximum possible value is used (all bits are one).
Output is High
diff --git a/src/main/resources/lang/lang_it.xml b/src/main/resources/lang/lang_it.xml
index 9f04c0d01..110b1d09d 100644
--- a/src/main/resources/lang/lang_it.xml
+++ b/src/main/resources/lang/lang_it.xml
@@ -1163,6 +1163,7 @@ In the file howTo.md you can find more details about translations.
Applicazione
Definisce quale applicazione utilizzare.
Generica
+ Socket
Ingressi
Gli ingressi del processo esterno.
Questa è una lista di nomi di segnali separati da virgola. Per ogni nome, un numero di bit separato
diff --git a/src/main/resources/lang/lang_it_ref.xml b/src/main/resources/lang/lang_it_ref.xml
index 902022220..3d24c109e 100644
--- a/src/main/resources/lang/lang_it_ref.xml
+++ b/src/main/resources/lang/lang_it_ref.xml
@@ -1212,6 +1212,7 @@ In the file howTo.md you can find more details about translations.
Application
Defines which application to use.
Generic
+ Socket
Inputs
The inputs of the external process.
It is a comma-separated list of signal names. For each signal name, a number of bits separated by a colon
@@ -1234,6 +1235,9 @@ In the file howTo.md you can find more details about translations.
Path to the Icarus Verilog installation folder. Only necessary if you want to use
iverilog to simulate
components defined with Verilog.
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
Maximum Value
If a zero is entered, the maximum possible value is used (all bits are one).
Output is High
diff --git a/src/main/resources/lang/lang_pt.xml b/src/main/resources/lang/lang_pt.xml
index 2b3c99bc8..57796d7f5 100644
--- a/src/main/resources/lang/lang_pt.xml
+++ b/src/main/resources/lang/lang_pt.xml
@@ -1187,6 +1187,7 @@ In the file howTo.md you can find more details about translations.
Genérico
GHDL
IVerilog
+ Socket
Entradas
Entradas do processo externo.
Lista de nomes de sinais separados por vírgulas. Após cada nome de sinal, separado por dois-pontos, um número de bits
diff --git a/src/main/resources/lang/lang_pt_ref.xml b/src/main/resources/lang/lang_pt_ref.xml
index 3ab9a376e..ab2769472 100644
--- a/src/main/resources/lang/lang_pt_ref.xml
+++ b/src/main/resources/lang/lang_pt_ref.xml
@@ -1249,6 +1249,7 @@ In the file howTo.md you can find more details about translations.
Generic
GHDL
IVerilog
+ Socket
Inputs
The inputs of the external process.
It is a comma-separated list of signal names. For each signal name, a number of bits separated by a colon
@@ -1270,6 +1271,9 @@ In the file howTo.md you can find more details about translations.
iverilog to simulate
components defined with Verilog.
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
Maximum Value
If a zero is entered, the maximum possible value is used (all bits are one).
Output is High
diff --git a/src/main/resources/lang/lang_zh.xml b/src/main/resources/lang/lang_zh.xml
index 4c2c5639f..5c357b86f 100644
--- a/src/main/resources/lang/lang_zh.xml
+++ b/src/main/resources/lang/lang_zh.xml
@@ -512,6 +512,7 @@ In the file howTo.md you can find more details about translations.
应用
定义使用哪个应用
通用
+ Socket
输入
输出
程序代码
@@ -1709,4 +1710,6 @@ In the file howTo.md you can find more details about translations.
存储数据
未能载入 ROM 数据
修正: {0}
+ Verilog 代码库路径
+ 用于配置verilog代码的依赖库路径,在运行仿真时会自动从这个路径查找依赖代码
diff --git a/src/main/resources/lang/lang_zh_ref.xml b/src/main/resources/lang/lang_zh_ref.xml
index c91ab22a0..fd5e7f86f 100644
--- a/src/main/resources/lang/lang_zh_ref.xml
+++ b/src/main/resources/lang/lang_zh_ref.xml
@@ -1987,6 +1987,7 @@ In the file howTo.md you can find more details about translations.
If selected the output is low if the component is active.
GHDL
IVerilog
+ Socket
IVerilog Options
Options that are used for all processing steps by IVerilog.
IVerilog
@@ -2163,4 +2164,7 @@ In the file howTo.md you can find more details about translations.
stored data
Could not load ROM data!
Fixtures: {0}
+ Verilog external code library path
+ Used to configure the dependency library path for Verilog code, which will automatically search for dependency code during simulation
+
diff --git a/src/test/java/de/neemann/digital/core/extern/ApplicationVerilogStdIOTest.java b/src/test/java/de/neemann/digital/core/extern/ApplicationVerilogStdIOTest.java
index 3c9b86f4c..0ce50ee34 100644
--- a/src/test/java/de/neemann/digital/core/extern/ApplicationVerilogStdIOTest.java
+++ b/src/test/java/de/neemann/digital/core/extern/ApplicationVerilogStdIOTest.java
@@ -10,11 +10,19 @@
import de.neemann.digital.core.extern.handler.ProcessInterface;
import de.neemann.digital.hdl.hgs.Context;
import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.EnvironmentVariables;
import java.io.File;
+import java.lang.reflect.Field;
+import java.util.Map;
public class ApplicationVerilogStdIOTest extends TestCase {
+
+ @Rule
+ public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
private class TestApp extends ApplicationVerilogStdIO {
@Override
@@ -93,6 +101,24 @@ public void testEnsureConsistencyIndirect2() {
assertEquals("y:5", attr.get(Keys.EXTERNAL_OUTPUTS));
}
+ @Test
+ public void testReplaceEnvVars() {
+ String path = "d:\\verilogcode\\lib";
+ String pathAfterReplace = ApplicationVerilogStdIO.replaceEnvVars(path);
+ assertEquals("d:\\\\verilogcode\\\\lib",pathAfterReplace);
+
+ path = "/home/verilogcode/lib";
+ pathAfterReplace = ApplicationVerilogStdIO.replaceEnvVars(path);
+ assertEquals(path,pathAfterReplace);
+
+// path = "${VERILOG_LIB_PATH}";
+// environmentVariables.set("VERILOG_LIB_PATH","/home/verilogcode/lib");
+// pathAfterReplace = ApplicationVerilogStdIO.replaceEnvVars(path);
+// assertEquals("/home/verilogcode/lib",pathAfterReplace);
+// environmentVariables.clear("VERILOG_LIB_PATH");
+
+ }
+
private ElementAttributes extractParameters(String code) {
TestApp ta = new TestApp();
ElementAttributes attr = new ElementAttributes();