From 985fac138b42f7f4cc31aa5fccce65de1658a142 Mon Sep 17 00:00:00 2001 From: hneemann Date: Fri, 9 Mar 2018 19:46:16 +0100 Subject: [PATCH] Added a method to make an external component consistent. Implement a very simple VHDL parser to ensure consistency --- .../digital/core/extern/Application.java | 13 ++ .../core/extern/ApplicationVHDLStdIO.java | 95 ++++++++++++++ .../de/neemann/digital/core/extern/Port.java | 20 +++ .../digital/core/extern/PortDefinition.java | 29 ++++ .../gui/components/AttributeDialog.java | 15 +++ .../digital/gui/components/Editor.java | 7 + .../digital/gui/components/EditorFactory.java | 75 ++++++++++- .../testing/TestCaseDescriptionEditor.java | 4 + src/main/resources/lang/lang_de.xml | 18 ++- src/main/resources/lang/lang_en.xml | 18 ++- .../core/extern/ApplicationVHDLStdIOTest.java | 124 ++++++++++++++++++ 11 files changed, 405 insertions(+), 13 deletions(-) create mode 100644 src/test/java/de/neemann/digital/core/extern/ApplicationVHDLStdIOTest.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 213de0a91..ad03ebb34 100644 --- a/src/main/java/de/neemann/digital/core/extern/Application.java +++ b/src/main/java/de/neemann/digital/core/extern/Application.java @@ -5,6 +5,7 @@ */ package de.neemann.digital.core.extern; +import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.extern.handler.ProcessInterface; import java.io.IOException; @@ -59,6 +60,18 @@ public interface Application { */ ProcessInterface start(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException; + /** + * Used to make the component consistent. + * Could extract the label and the input and output configuration from the code or vice versa. + * If this is not supported, nothing is done. + * + * @param attributes the attributed of this component + * @return true if attributes are modified + */ + default boolean ensureConsistency(ElementAttributes attributes) { + return false; + } + /** * @return true if the code check function is supported */ diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationVHDLStdIO.java b/src/main/java/de/neemann/digital/core/extern/ApplicationVHDLStdIO.java index 701e9c211..483d4144e 100644 --- a/src/main/java/de/neemann/digital/core/extern/ApplicationVHDLStdIO.java +++ b/src/main/java/de/neemann/digital/core/extern/ApplicationVHDLStdIO.java @@ -5,8 +5,14 @@ */ package de.neemann.digital.core.extern; +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.core.element.Keys; + import java.io.*; import java.nio.file.Files; +import java.util.ArrayList; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; /** * Base class of applications which are able to interprete VHDL-Code. @@ -151,4 +157,93 @@ public abstract class ApplicationVHDLStdIO implements Application { return "std_logic_vector (" + (bits - 1) + " downto 0)"; } + @Override + public boolean ensureConsistency(ElementAttributes attributes) { + String code = attributes.get(Keys.EXTERNAL_CODE); + StringTokenizer st = new StringTokenizer(code, "(), :;\t\n\r"); + try { + while (st.hasMoreTokens()) { + if (st.nextToken().toLowerCase().equals("entity")) + break; + } + + String label = st.nextToken(); + + while (st.hasMoreTokens()) { + String tok = st.nextToken().toLowerCase(); + if (tok.equals("end")) + return false; + else if (tok.equals("port")) { + PortDefinition in = new PortDefinition(""); + PortDefinition out = new PortDefinition(""); + scanPorts(st, in, out); + if (in.size() > 0 && out.size() > 0) { + attributes.set(Keys.LABEL, label); + attributes.set(Keys.EXTERNAL_INPUTS, in.toString()); + attributes.set(Keys.EXTERNAL_OUTPUTS, out.toString()); + return true; + } else + return false; + } + } + return false; + } catch (NoSuchElementException | ParseException e) { + return false; + } + } + + private void scanPorts(StringTokenizer st, PortDefinition in, PortDefinition out) throws ParseException { + ArrayList vars = new ArrayList<>(); + while (st.hasMoreTokens()) { + String tok = st.nextToken(); + switch (tok.toLowerCase()) { + case "in": + scanPort(st, vars, in); + vars.clear(); + break; + case "out": + scanPort(st, vars, out); + vars.clear(); + break; + case "end": + return; + default: + vars.add(tok); + } + } + } + + private void scanPort(StringTokenizer st, ArrayList vars, PortDefinition port) throws ParseException { + switch (st.nextToken().toLowerCase()) { + case "std_logic": + for (String var : vars) + port.addPort(var, 1); + break; + case "std_logic_vector": + int upper = getNumber(st); + if (!st.nextToken().toLowerCase().equals("downto")) + throw new ParseException(); + int lower = getNumber(st); + + if (lower != 0) + throw new ParseException(); + + for (String var : vars) + port.addPort(var, upper + 1); + break; + default: + throw new ParseException(); + } + } + + private int getNumber(StringTokenizer st) throws ParseException { + try { + return Integer.parseInt(st.nextToken()); + } catch (NumberFormatException e) { + throw new ParseException(); + } + } + + private static final class ParseException extends Exception { + } } diff --git a/src/main/java/de/neemann/digital/core/extern/Port.java b/src/main/java/de/neemann/digital/core/extern/Port.java index 4b880aea6..dab7ab020 100644 --- a/src/main/java/de/neemann/digital/core/extern/Port.java +++ b/src/main/java/de/neemann/digital/core/extern/Port.java @@ -14,6 +14,18 @@ public class Port { private final int bits; private final String name; + + /** + * Creates a new port + * + * @param name the name + * @param bits the number of bits + */ + public Port(String name, int bits) { + this.name = name; + this.bits = bits; + } + /** * Creates a new port * @@ -49,4 +61,12 @@ public class Port { public String getName() { return name; } + + @Override + public String toString() { + if (bits == 1) + return name; + else + return name + ":" + bits; + } } diff --git a/src/main/java/de/neemann/digital/core/extern/PortDefinition.java b/src/main/java/de/neemann/digital/core/extern/PortDefinition.java index f7ff5228a..5d20b38d3 100644 --- a/src/main/java/de/neemann/digital/core/extern/PortDefinition.java +++ b/src/main/java/de/neemann/digital/core/extern/PortDefinition.java @@ -82,4 +82,33 @@ public class PortDefinition implements Iterable { public Iterator iterator() { return ports.iterator(); } + + /** + * Adds a port to this description + * + * @param name the name + * @param bits the number of bits + */ + public void addPort(String name, int bits) { + ports.add(new Port(name, bits)); + } + + /** + * @return the number of ports + */ + public int size() { + return ports.size(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Port p : ports) { + if (sb.length() > 0) + sb.append(","); + sb.append(p.toString()); + } + + return sb.toString(); + } } diff --git a/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java b/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java index 21bdbcc3c..ffae6ee2d 100644 --- a/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/AttributeDialog.java @@ -204,6 +204,16 @@ public class AttributeDialog extends JDialog { e.setTo(modifiedAttributes); } + /** + * update gui fields with attributes + * + * @throws Editor.EditorParseException Editor.EditorParseException + */ + public void updateEditedValues() throws Editor.EditorParseException { + for (EditorHolder e : editors) + e.getFrom(modifiedAttributes); + } + /** * Shows the dialog * @@ -283,6 +293,11 @@ public class AttributeDialog extends JDialog { attr.set(key, value); } + public void getFrom(ElementAttributes attr) throws Editor.EditorParseException { + T value = attr.get(key); + e.setValue(value); + } + void setDependantEditor(Editor editor, boolean inverted) { if (key.getValueClass() != Boolean.class) throw new RuntimeException("key " + key.getName() + " is not a bool key"); diff --git a/src/main/java/de/neemann/digital/gui/components/Editor.java b/src/main/java/de/neemann/digital/gui/components/Editor.java index 2e06c4d99..80766fc4f 100644 --- a/src/main/java/de/neemann/digital/gui/components/Editor.java +++ b/src/main/java/de/neemann/digital/gui/components/Editor.java @@ -21,6 +21,13 @@ public interface Editor { */ T getValue() throws EditorParseException; + /** + * Sets the value to the gui + * + * @param value the value to set + */ + void setValue(T value); + /** * Adds the components of the editor to the panel * diff --git a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java index dda4be82d..7d6c9ef06 100644 --- a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java +++ b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java @@ -199,6 +199,11 @@ public final class EditorFactory { return text.getText().trim(); } + @Override + public void setValue(String value) { + text.setText(value); + } + public JTextComponent getTextComponent() { return text; } @@ -252,6 +257,11 @@ public final class EditorFactory { return value; } + + @Override + public void setValue(Integer value) { + comboBox.setSelectedItem(value); + } } private final static class LongEditor extends LabelEditor { @@ -284,6 +294,11 @@ public final class EditorFactory { } return value; } + + @Override + public void setValue(Long value) { + comboBox.setSelectedItem(value.toString()); + } } private final static class InValueEditor extends LabelEditor { @@ -310,6 +325,11 @@ public final class EditorFactory { throw new EditorParseException(e); } } + + @Override + public void setValue(InValue value) { + comboBox.setSelectedItem(value.toString()); + } } final static class BooleanEditor implements Editor { @@ -339,6 +359,11 @@ public final class EditorFactory { JCheckBox getCheckBox() { return bool; } + + @Override + public void setValue(Boolean value) { + bool.setEnabled(value); + } } private final static class ColorEditor extends LabelEditor { @@ -378,6 +403,12 @@ public final class EditorFactory { public Color getValue() { return color; } + + @Override + public void setValue(Color value) { + this.color = value; + button.setBackground(color); + } } private final static class FileEditor extends LabelEditor { @@ -417,6 +448,11 @@ public final class EditorFactory { public File getValue() { return new File(textField.getText()); } + + @Override + public void setValue(File value) { + textField.setText(value.getPath()); + } } private final static class DataFieldEditor extends LabelEditor { @@ -481,6 +517,11 @@ public final class EditorFactory { public DataField getValue() { return data.getMinimized(); } + + @Override + public void setValue(DataField value) { + this.data = value; + } } private final static class RotationEditor extends LabelEditor { @@ -504,6 +545,11 @@ public final class EditorFactory { public Rotation getValue() { return new Rotation(comb.getSelectedIndex()); } + + @Override + public void setValue(Rotation value) { + comb.setSelectedIndex(value.getRotation()); + } } private static class EnumEditor extends LabelEditor { @@ -530,6 +576,11 @@ public final class EditorFactory { public E getValue() { return values[comboBox.getSelectedIndex()]; } + + @Override + public void setValue(E value) { + comboBox.setSelectedIndex(value.ordinal()); + } } private static final class IntFormatsEditor extends EnumEditor { @@ -573,7 +624,8 @@ public final class EditorFactory { if (app != null) { try { getAttributeDialog().storeEditedValues(); - Application.create(elementAttributes.get(key)); + if (app.ensureConsistency(elementAttributes)) + getAttributeDialog().updateEditedValues(); PortDefinition ins = new PortDefinition(elementAttributes.get(Keys.EXTERNAL_INPUTS)); PortDefinition outs = new PortDefinition(elementAttributes.get(Keys.EXTERNAL_OUTPUTS)); @@ -641,6 +693,11 @@ public final class EditorFactory { public Language getValue() { return (Language) comb.getSelectedItem(); } + + @Override + public void setValue(Language value) { + comb.setSelectedItem(value); + } } private static class FormatEditor extends LabelEditor { @@ -661,6 +718,11 @@ public final class EditorFactory { public FormatToExpression getValue() { return (FormatToExpression) comb.getSelectedItem(); } + + @Override + public void setValue(FormatToExpression value) { + comb.setSelectedItem(value); + } } private static class InverterConfigEditor extends LabelEditor { @@ -712,6 +774,12 @@ public final class EditorFactory { this.elementAttributes = elementAttributes; return button; } + + @Override + public void setValue(InverterConfig value) { + inverterConfig = value; + button.setText(getButtonText()); + } } private final static class InputSelectDialog extends JDialog { @@ -814,5 +882,10 @@ public final class EditorFactory { public ROMManger getValue() { return romManager; } + + @Override + public void setValue(ROMManger value) { + romManager=value; + } } } diff --git a/src/main/java/de/neemann/digital/gui/components/testing/TestCaseDescriptionEditor.java b/src/main/java/de/neemann/digital/gui/components/testing/TestCaseDescriptionEditor.java index e9995459b..dcf144f60 100644 --- a/src/main/java/de/neemann/digital/gui/components/testing/TestCaseDescriptionEditor.java +++ b/src/main/java/de/neemann/digital/gui/components/testing/TestCaseDescriptionEditor.java @@ -71,4 +71,8 @@ public class TestCaseDescriptionEditor extends EditorFactory.LabelEditor Extern - Element zur Anbindung von externen Programmen zur Berechnung der Logik. + Element zur Anbindung von externen Programmen zur Berechnung der Logik. + Wird verwendet, um das Verhalten eines Elements mit einer Hardwarebeschreibungssprache wie VHDL oder + Verilog zu beschreiben. Die eigentliche Simulation des Verhaltens muss mit einem externen Simulator erfolgen. + Zur Zeit wird nur der VHDL-Simulator ghdl unterstützt. + Diode Echt bidirektionale Diode. @@ -1060,15 +1064,17 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Generisch GHDL Eingänge - Die Eingänge des externen Prozesses. + 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. + Die Eingänge eines 8-Bit Addierers könnten also mit "a:8,b:8,c_in" beschrieben werden. Ausgänge - Die Ausgänge des externen Prozesses. + Die Ausgä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. + Die Ausgänge eines 8-Bit Addierers könnten also mit "s:8,c_out" beschrieben werden. Programmcode Der Programmcode welcher ausgeführt werden soll. - Ausfühbare Datei - Die Datei, die ausgeführt werden soll! GHDL - Pfad der Ausführbaren ghdl-Datei. Nur wichtig, wenn ghdl zur Interpretation von + Pfad der ausführbaren ghdl-Datei. Nur wichtig, wenn ghdl zur Interpretation von VHDL-Code verwendet werden soll. Leitung eingefügt. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index d0bfc948c..276240f9c 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -693,7 +693,11 @@ Fast forward clocking can only be used if the real-time clock is disabled. External - Component to execute an external process to calculate the logic function. + Component to execute an external process to calculate the logic function. + Is used to specify the behaviour of a component by VHDL or Verilog. + The actual simulation of the behavior must be done with an external simulator. + At present only the VHDL simulator ghdl is supported. + Diode Simplified bidirectional diode. It is used to implement a wired AND or a wired OR.. @@ -1052,13 +1056,15 @@ Generic GHDL Inputs - The inputs of the external process. + The inputs of the external process. + It is a comma-separated list of signal names. For each signal name, with a colon separated, a number of bits + can be specified. The inputs of an 8-bit adder could thus be described as "a:8,b:8,c_in". Outputs - The outputs of the external process. + The outputs of the external process. + It is a comma-separated list of signal names. For each signal name, with a colon separated, a number of bits + can be specified. The outputs of an 8-bit adder could thus be described as "s:8,c_out". Programcode - The programm code to use with the external process. - Executeable - The programm which is to execute. + The programm code to be executed by the external application. GHDL Path to the executable ghdl file. Only necessary if you want to use ghdl to simulate components defined with vhdl. diff --git a/src/test/java/de/neemann/digital/core/extern/ApplicationVHDLStdIOTest.java b/src/test/java/de/neemann/digital/core/extern/ApplicationVHDLStdIOTest.java new file mode 100644 index 000000000..a305a431e --- /dev/null +++ b/src/test/java/de/neemann/digital/core/extern/ApplicationVHDLStdIOTest.java @@ -0,0 +1,124 @@ +/* + * 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.element.ElementAttributes; +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.core.extern.handler.ProcessInterface; +import junit.framework.TestCase; + +import java.io.IOException; + +public class ApplicationVHDLStdIOTest extends TestCase { + + private class TestApp extends ApplicationVHDLStdIO { + + @Override + public ProcessInterface start(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException { + return null; + } + + @Override + public boolean checkSupported() { + return false; + } + + @Override + public String checkCode(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException { + return null; + } + } + + + public void testExtraction() { + ElementAttributes attr = extractParameters("LIBRARY ieee;\n" + + "USE ieee.std_logic_1164.all;\n" + + "USE ieee.std_logic_unsigned.all;\n" + + "\n" + + "entity add is\n" + + " port (\n" + + " a: in std_logic_vector(3 downto 0);\n" + + " b: in std_logic_vector(3 downto 0);\n" + + " c_i: in std_logic;\n" + + " s: out std_logic_vector(3 downto 0);\n" + + " c_o: out std_logic );\n" + + "end add;\n" + + "\n" + + "architecture add_arch of add is\n" + + "begin\n" + + "end add_arch;", true); + + assertEquals("add", attr.getCleanLabel()); + assertEquals("a:4,b:4,c_i", attr.get(Keys.EXTERNAL_INPUTS)); + assertEquals("s:4,c_o", attr.get(Keys.EXTERNAL_OUTPUTS)); + } + + public void testExtractionUpper() { + ElementAttributes attr = extractParameters("LIBRARY Ieee;\n" + + "USE Ieee.std_logic_1164.all;\n" + + "USE Ieee.std_logic_unsigned.all;\n" + + "\n" + + "Entity Add Is\n" + + " Port (\n" + + " A: In Std_logic_vector(3 Downto 0);\n" + + " B: In Std_logic_vector(3 Downto 0);\n" + + " C_i: In Std_logic;\n" + + " S: Out Std_logic_vector(3 Downto 0);\n" + + " C_o: Out Std_logic );\n" + + "End Add;\n" + + "\n" + + "architecture Add_arch Of Add Is\n" + + "begin\n" + + "end Add_arch;", true); + + assertEquals("Add", attr.getCleanLabel()); + assertEquals("A:4,B:4,C_i", attr.get(Keys.EXTERNAL_INPUTS)); + assertEquals("S:4,C_o", attr.get(Keys.EXTERNAL_OUTPUTS)); + } + + public void testExtractionCompact() { + ElementAttributes attr = extractParameters("library IEEE;\n" + + "use IEEE.std_logic_1164.all;\n" + + "use IEEE.numeric_std.all;\n" + + "\n" + + "\n" + + "entity nBitZaehler is\n" + + "\tport (LoadIn : in std_logic_vector (7 downto 0); load,reset,clk : in std_logic; CountOut : out std_logic_vector (7 downto 0));\n" + + "end nBitZaehler;\n" + + "\n" + + "architecture nBitZaehlerRTL of nBitZaehler is\n" + + "\tsignal ALUOut : unsigned(7 downto 0); -- internal\n" + + "\tsignal ALUIn : unsigned(7 downto 0); -- internal\n" + + "begin\n" + + "\tend process;\n" + + "end nBitZaehlerRTL;", true); + + assertEquals("nBitZaehler", attr.getCleanLabel()); + assertEquals("LoadIn:8,load,reset,clk", attr.get(Keys.EXTERNAL_INPUTS)); + assertEquals("CountOut:8", attr.get(Keys.EXTERNAL_OUTPUTS)); + } + + public void testExtractionFail() { + extractParameters("library IEEE;\n" + + "use IEEE.std_logic_1164.all;\n" + + "use IEEE.numeric_std.all;\n" + + "\n" + + "\n" + + "entity nBitZaehler is\n" + + "\tgeneric(size : natural := 32);\n" + + "\tport (LoadIn : in std_logic_vector ((size-1) downto 0); load,reset,clk : in std_logic; CountOut : out std_logic_vector (size-1 downto 0));\n" + + "end nBitZaehler;\n", false); + } + + + public ElementAttributes extractParameters(String code, boolean workExpected) { + TestApp ta = new TestApp(); + ElementAttributes attr = new ElementAttributes(); + attr.set(Keys.EXTERNAL_CODE, code); + assertEquals(workExpected, ta.ensureConsistency(attr)); + return attr; + } +} \ No newline at end of file