From 4530aebd46e430f07adbbe673fc3fc9477754209 Mon Sep 17 00:00:00 2001 From: renyangang Date: Thu, 5 Sep 2024 15:38:28 +0800 Subject: [PATCH 1/3] add setting for Verilog external code library path --- pom.xml | 7 +++++ .../de/neemann/digital/core/element/Keys.java | 6 +++++ .../core/extern/ApplicationIVerilog.java | 18 +++++++++++++ .../core/extern/ApplicationVerilogStdIO.java | 26 +++++++++++++++++++ .../java/de/neemann/digital/gui/Settings.java | 1 + src/main/resources/lang/lang_de.xml | 3 +++ src/main/resources/lang/lang_en.xml | 3 +++ src/main/resources/lang/lang_es_ref.xml | 3 +++ src/main/resources/lang/lang_fr_ref.xml | 3 +++ src/main/resources/lang/lang_it_ref.xml | 3 +++ src/main/resources/lang/lang_pt_ref.xml | 3 +++ src/main/resources/lang/lang_zh.xml | 2 ++ src/main/resources/lang/lang_zh_ref.xml | 3 +++ .../extern/ApplicationVerilogStdIOTest.java | 26 +++++++++++++++++++ 14 files changed, 107 insertions(+) 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/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/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("(? 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..13a7af7ed 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -1525,6 +1525,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..5fed436a5 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -1514,6 +1514,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_ref.xml b/src/main/resources/lang/lang_es_ref.xml index 9dff76a1d..cd5fc4e01 100644 --- a/src/main/resources/lang/lang_es_ref.xml +++ b/src/main/resources/lang/lang_es_ref.xml @@ -1221,6 +1221,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_ref.xml b/src/main/resources/lang/lang_fr_ref.xml index 8996f74dd..103fbdf95 100644 --- a/src/main/resources/lang/lang_fr_ref.xml +++ b/src/main/resources/lang/lang_fr_ref.xml @@ -1442,6 +1442,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_ref.xml b/src/main/resources/lang/lang_it_ref.xml index 902022220..be8e8557b 100644 --- a/src/main/resources/lang/lang_it_ref.xml +++ b/src/main/resources/lang/lang_it_ref.xml @@ -1234,6 +1234,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_ref.xml b/src/main/resources/lang/lang_pt_ref.xml index 3ab9a376e..3ad9281fa 100644 --- a/src/main/resources/lang/lang_pt_ref.xml +++ b/src/main/resources/lang/lang_pt_ref.xml @@ -1270,6 +1270,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..9e9eab0e2 100644 --- a/src/main/resources/lang/lang_zh.xml +++ b/src/main/resources/lang/lang_zh.xml @@ -1709,4 +1709,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..7d82b417e 100644 --- a/src/main/resources/lang/lang_zh_ref.xml +++ b/src/main/resources/lang/lang_zh_ref.xml @@ -2163,4 +2163,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(); From 7202b31422bbf751fcb2c6f52e3faf0ac5da9765 Mon Sep 17 00:00:00 2001 From: renyangang Date: Fri, 18 Oct 2024 19:18:21 +0800 Subject: [PATCH 2/3] add socket extern --- .../digital/core/extern/Application.java | 8 ++++- .../core/extern/ApplicationSocket.java | 32 ++++++++++++++++++ .../core/extern/handler/SocketInterface.java | 33 +++++++++++++++++++ src/main/resources/lang/lang_de.xml | 1 + src/main/resources/lang/lang_en.xml | 1 + src/main/resources/lang/lang_es.xml | 1 + src/main/resources/lang/lang_es_ref.xml | 1 + src/main/resources/lang/lang_fr.xml | 1 + src/main/resources/lang/lang_fr_ref.xml | 1 + src/main/resources/lang/lang_it.xml | 1 + src/main/resources/lang/lang_it_ref.xml | 1 + src/main/resources/lang/lang_pt.xml | 1 + src/main/resources/lang/lang_pt_ref.xml | 1 + src/main/resources/lang/lang_zh.xml | 1 + src/main/resources/lang/lang_zh_ref.xml | 1 + 15 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java create mode 100644 src/main/java/de/neemann/digital/core/extern/handler/SocketInterface.java 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/ApplicationSocket.java b/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java new file mode 100644 index 000000000..86a94c3c7 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java @@ -0,0 +1,32 @@ +/* + * 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.extern.handler.ProcessInterface; +import de.neemann.digital.core.extern.handler.SocketInterface; + +import java.io.File; +import java.io.IOException; + +/** + * application with socket tcp/ip comm + */ +public class ApplicationSocket implements Application { + @Override + public ProcessInterface start(String label, String code, PortDefinition inputs, PortDefinition outputs, File root) throws IOException { + return new SocketInterface(); + } + + @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/handler/SocketInterface.java b/src/main/java/de/neemann/digital/core/extern/handler/SocketInterface.java new file mode 100644 index 000000000..4f1292ba1 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/extern/handler/SocketInterface.java @@ -0,0 +1,33 @@ +/* + * 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 java.io.IOException; + +/** + * socket set get + */ +public class SocketInterface implements ProcessInterface { + @Override + public void writeValues(ObservableValues values) throws IOException { + for (ObservableValue v : values) { + v.set(1, 0); + } + } + + @Override + public void readValues(ObservableValues values) throws IOException { + + } + + @Override + public void close() throws IOException { + + } +} diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 13a7af7ed..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. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 5fed436a5..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 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 cd5fc4e01..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 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 103fbdf95..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 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 be8e8557b..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 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 3ad9281fa..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 diff --git a/src/main/resources/lang/lang_zh.xml b/src/main/resources/lang/lang_zh.xml index 9e9eab0e2..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 输入 输出 程序代码 diff --git a/src/main/resources/lang/lang_zh_ref.xml b/src/main/resources/lang/lang_zh_ref.xml index 7d82b417e..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 From c9ddfe12f5cd6a7b90ec2a998e440c5aa87c4293 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 21 Oct 2024 07:38:27 +0800 Subject: [PATCH 3/3] support socket comm with extern app --- .../core/extern/ApplicationSocket.java | 11 +- .../core/extern/handler/SocketInterface.java | 127 +++++++++++++- .../core/extern/handler/TCPClient.java | 156 ++++++++++++++++++ 3 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 src/main/java/de/neemann/digital/core/extern/handler/TCPClient.java diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java b/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java index 86a94c3c7..a65cde4b0 100644 --- a/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java +++ b/src/main/java/de/neemann/digital/core/extern/ApplicationSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Helmut Neemann. + * 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. */ @@ -12,12 +12,16 @@ import java.io.IOException; /** - * application with socket tcp/ip comm + * 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 { - return new SocketInterface(); + String ipPort = "127.0.0.1:8009"; + if (code.length() > 0 && code.indexOf(":") > 0) { + ipPort = code; + } + return new SocketInterface(ipPort, inputs, outputs); } @Override @@ -29,4 +33,5 @@ public boolean checkSupported() { 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/handler/SocketInterface.java b/src/main/java/de/neemann/digital/core/extern/handler/SocketInterface.java index 4f1292ba1..40d36a659 100644 --- a/src/main/java/de/neemann/digital/core/extern/handler/SocketInterface.java +++ b/src/main/java/de/neemann/digital/core/extern/handler/SocketInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Helmut Neemann. + * 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. */ @@ -7,27 +7,144 @@ import de.neemann.digital.core.ObservableValue; import de.neemann.digital.core.ObservableValues; +import de.neemann.digital.core.extern.Port; +import de.neemann.digital.core.extern.PortDefinition; import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.BitSet; /** - * socket set get + * signal comm out from extern app with shared mem map file */ public class SocketInterface implements ProcessInterface { + + private TCPClient tcpClient; + + private int inputBits; + private int outputBits; + private BitSet inSeta; + private BitSet inSetb; + private int inLen; + private BitSet outSet; + private ByteBuffer buf1; + private ByteBuffer buf2; + + + /** + * mem map init + * @param ipPort address and port for extern + * @param inputs signal inputs + * @param outputs signal outputs + */ + public SocketInterface(String ipPort, PortDefinition inputs, PortDefinition outputs) { + String[] net = ipPort.split(":"); + this.tcpClient = new TCPClient(net[0], Integer.parseInt(net[1])); + this.inputBits = 0; + this.outputBits = 0; + for (Port input : inputs) { + this.inputBits += input.getBits(); + this.inLen = this.inputBits; + // match the VPI t_vpi_value type,t_vpi_vecval use aval and bval with int32 + while (this.inLen % 32 != 0) { + this.inLen++; + } + this.inSeta = new BitSet(this.inLen); + this.inSetb = new BitSet(this.inLen); + this.buf1 = ByteBuffer.allocate(this.inLen/8); + this.buf2 = ByteBuffer.allocate(this.inLen/8); + } + for (Port output : outputs) { + this.outputBits += output.getBits(); + } + } + + private void initSocket() throws IOException { + if (!this.tcpClient.getIsRunning()) { + this.tcpClient.start(); + } + } + + private static byte[] concatenateByteArrays(byte[] array1, byte[] array2) { + byte[] result = new byte[array1.length + array2.length]; // 新数组,长度为两个数组的总和 + + System.arraycopy(array1, 0, result, 0, array1.length); // 将array1复制到result + System.arraycopy(array2, 0, result, array1.length, array2.length); // 将array2复制到result + + return result; + } + @Override public void writeValues(ObservableValues values) throws IOException { + this.initSocket(); + int pos = 0; for (ObservableValue v : values) { - v.set(1, 0); + for (int i=0; i 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(); + } +}