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();