Added a method to make an external component consistent.

Implement a very simple VHDL parser to ensure consistency
This commit is contained in:
hneemann 2018-03-09 19:46:16 +01:00
parent b3d7ac164c
commit 985fac138b
11 changed files with 405 additions and 13 deletions

View File

@ -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
*/

View File

@ -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<String> 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<String> 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 {
}
}

View File

@ -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;
}
}

View File

@ -82,4 +82,33 @@ public class PortDefinition implements Iterable<Port> {
public Iterator<Port> 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();
}
}

View File

@ -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");

View File

@ -21,6 +21,13 @@ public interface Editor<T> {
*/
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
*

View File

@ -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<Long> {
@ -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<InValue> {
@ -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<Boolean> {
@ -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<Color> {
@ -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<File> {
@ -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<DataField> {
@ -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<Rotation> {
@ -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<E extends Enum> extends LabelEditor<E> {
@ -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<IntFormat> {
@ -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<FormatToExpression> {
@ -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<InverterConfig> {
@ -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;
}
}
}

View File

@ -71,4 +71,8 @@ public class TestCaseDescriptionEditor extends EditorFactory.LabelEditor<TestCas
return panel;
}
@Override
public void setValue(TestCaseDescription value) {
}
}

View File

@ -697,7 +697,11 @@
Der schnelle Simulationslauf kann nur genutzt werden, wenn der Echtzeittakt deaktiviert ist!</string>
<string name="elem_External">Extern</string>
<string name="elem_External_tt">Element zur Anbindung von externen Programmen zur Berechnung der Logik.</string>
<string name="elem_External_tt">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.
</string>
<string name="elem_Diode">Diode</string>
<string name="elem_Diode_tt">Echt bidirektionale Diode.
@ -1060,15 +1064,17 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="key_applicationType_Generic">Generisch</string>
<string name="key_applicationType_GHDL">GHDL</string>
<string name="key_externalInputs">Eingänge</string>
<string name="key_externalInputs_tt">Die Eingänge des externen Prozesses.</string>
<string name="key_externalInputs_tt">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.</string>
<string name="key_externalOutputs">Ausgänge</string>
<string name="key_externalOutputs_tt">Die Ausgänge des externen Prozesses.</string>
<string name="key_externalOutputs_tt">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.</string>
<string name="key_Code">Programmcode</string>
<string name="key_Code_tt">Der Programmcode welcher ausgeführt werden soll.</string>
<string name="key_externalExecutable">Ausfühbare Datei</string>
<string name="key_externalExecutable_tt">Die Datei, die ausgeführt werden soll!</string>
<string name="key_ghdlPath">GHDL</string>
<string name="key_ghdlPath_tt">Pfad der Ausführbaren ghdl-Datei. Nur wichtig, wenn ghdl zur Interpretation von
<string name="key_ghdlPath_tt">Pfad der ausführbaren ghdl-Datei. Nur wichtig, wenn ghdl zur Interpretation von
VHDL-Code verwendet werden soll.</string>
<string name="mod_insertWire">Leitung eingefügt.</string>

View File

@ -693,7 +693,11 @@
Fast forward clocking can only be used if the real-time clock is disabled.</string>
<string name="elem_External">External</string>
<string name="elem_External_tt">Component to execute an external process to calculate the logic function.</string>
<string name="elem_External_tt">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.
</string>
<string name="elem_Diode">Diode</string>
<string name="elem_Diode_tt">Simplified bidirectional diode. It is used to implement a wired AND or a wired OR..
@ -1052,13 +1056,15 @@
<string name="key_applicationType_Generic">Generic</string>
<string name="key_applicationType_GHDL">GHDL</string>
<string name="key_externalInputs">Inputs</string>
<string name="key_externalInputs_tt">The inputs of the external process.</string>
<string name="key_externalInputs_tt">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".</string>
<string name="key_externalOutputs">Outputs</string>
<string name="key_externalOutputs_tt">The outputs of the external process.</string>
<string name="key_externalOutputs_tt">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".</string>
<string name="key_Code">Programcode</string>
<string name="key_Code_tt">The programm code to use with the external process.</string>
<string name="key_externalExecutable">Executeable</string>
<string name="key_externalExecutable_tt">The programm which is to execute.</string>
<string name="key_Code_tt">The programm code to be executed by the external application.</string>
<string name="key_ghdlPath">GHDL</string>
<string name="key_ghdlPath_tt">Path to the executable ghdl file. Only necessary if you want to use ghdl to simulate
components defined with vhdl.</string>

View File

@ -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;
}
}