Merge branch 'master' into newProcessor

# Conflicts:
#	src/main/java/de/neemann/digital/testing/parser/Parser.java
This commit is contained in:
hneemann 2020-11-29 11:10:02 +01:00
commit ee86de0d49
10 changed files with 256 additions and 81 deletions

View File

@ -5,16 +5,11 @@
*/
package de.neemann.digital.testing;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.parser.LineEmitter;
import de.neemann.digital.testing.parser.Parser;
import de.neemann.digital.testing.parser.ParserException;
import de.neemann.digital.testing.parser.VirtualSignal;
import de.neemann.digital.testing.parser.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/**
* The test data.
@ -24,8 +19,7 @@ public class TestCaseDescription {
private transient LineEmitter lines;
private transient ArrayList<String> names;
private transient ArrayList<VirtualSignal> virtualSignals;
private transient DataField program;
private transient HashMap<String, Long> signalInit;
private transient ModelInitializer modelInitializer;
/**
@ -78,8 +72,7 @@ public class TestCaseDescription {
lines = tdp.getLines();
names = tdp.getNames();
virtualSignals = tdp.getVirtualSignals();
program = tdp.getProgram();
signalInit = tdp.getSignalInit();
modelInitializer = tdp.getModelInitializer();
}
/**
@ -110,21 +103,12 @@ public class TestCaseDescription {
}
/**
* @return the program data or null if not available
* @return the model initializer
* @throws TestingDataException TestingDataException
*/
public DataField getProgram() throws TestingDataException {
public ModelInitializer getModelInitializer() throws TestingDataException {
check();
return program;
}
/**
* @return the signal init values
* @throws TestingDataException TestingDataException
*/
public HashMap<String, Long> getSignalInit() throws TestingDataException {
check();
return signalInit;
return modelInitializer;
}
@Override

View File

@ -7,7 +7,6 @@ package de.neemann.digital.testing;
import de.neemann.digital.core.*;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.memory.ProgramMemory;
import de.neemann.digital.core.wiring.Clock;
import de.neemann.digital.data.Value;
import de.neemann.digital.draw.elements.Circuit;
@ -21,8 +20,6 @@ import de.neemann.digital.testing.parser.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* Runs the test and stores the test results created by a single {@link TestCaseDescription} instance.
@ -150,25 +147,7 @@ public class TestExecutor {
if (outputs.size() == 0)
throw new TestingDataException(Lang.get("err_noTestOutputSignalsDefined"));
if (testCase.getProgram() != null) {
List<Node> nodes = model.findNode(n -> n instanceof ProgramMemory && ((ProgramMemory) n).isProgramMemory());
switch (nodes.size()) {
case 0:
throw new TestingDataException(Lang.get("err_noRomFound"));
case 1:
((ProgramMemory) nodes.get(0)).setProgramMemory(testCase.getProgram());
break;
default:
throw new TestingDataException(Lang.get("err_multipleRomsFound"));
}
}
for (Map.Entry<String, Long> r : testCase.getSignalInit().entrySet()) {
Signal.Setter signal = model.getSignalSetter(r.getKey());
if (signal == null)
throw new TestingDataException(Lang.get("err_testSignal_N_notFound", r.getKey()));
signal.set(r.getValue(), 0);
}
testCase.getModelInitializer().init(model);
model.init();
model.addObserver(event -> {

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2020 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.testing.parser;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.Node;
import de.neemann.digital.core.Signal;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.core.memory.ProgramMemory;
import de.neemann.digital.core.memory.RAMInterface;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestingDataException;
import java.util.ArrayList;
import java.util.List;
/**
* Is prepared by the test data parser and then used to initialize the
* model for the test.
*/
public class ModelInitializer {
private final ArrayList<ModelInit> inits;
ModelInitializer() {
this.inits = new ArrayList<>();
}
void initSignal(String name, long value) {
inits.add(new InitSignal(name, value));
}
void initProgramMemory(DataField memory) {
inits.add(new InitProgramMemory(memory));
}
void initMemory(String ramName, int addr, long value) {
inits.add(new InitMemory(ramName, addr, value));
}
/**
* Aplies the init steps to the given model
*
* @param model the model to initialize
* @throws TestingDataException TestingDataException
*/
public void init(Model model) throws TestingDataException {
for (ModelInit mi : inits)
mi.init(model);
}
private interface ModelInit {
void init(Model model) throws TestingDataException;
}
private static final class InitSignal implements ModelInit {
private final String name;
private final long value;
private InitSignal(String name, long value) {
this.name = name;
this.value = value;
}
@Override
public void init(Model model) throws TestingDataException {
Signal.Setter s = model.getSignalSetter(name);
if (s == null)
throw new TestingDataException(Lang.get("err_testSignal_N_notFound", name));
s.set(value, 0);
}
}
private static final class InitProgramMemory implements ModelInit {
private final DataField dataField;
private InitProgramMemory(DataField dataField) {
this.dataField = dataField;
}
@Override
public void init(Model model) throws TestingDataException {
List<Node> nodes = model.findNode(n -> n instanceof ProgramMemory && ((ProgramMemory) n).isProgramMemory());
switch (nodes.size()) {
case 0:
throw new TestingDataException(Lang.get("err_noRomFound"));
case 1:
((ProgramMemory) nodes.get(0)).setProgramMemory(dataField);
break;
default:
throw new TestingDataException(Lang.get("err_multipleRomsFound"));
}
}
}
private static final class InitMemory implements ModelInit {
private final String memoryName;
private final int addr;
private final long value;
private InitMemory(String memoryName, int addr, long value) {
this.memoryName = memoryName;
this.addr = addr;
this.value = value;
}
@Override
public void init(Model model) throws TestingDataException {
List<Node> nodes = model.findNode(n -> n instanceof RAMInterface && ((RAMInterface) n).getLabel().equals(memoryName));
switch (nodes.size()) {
case 0:
throw new TestingDataException(Lang.get("err_noMemoryFound", memoryName));
case 1:
((RAMInterface) nodes.get(0)).getMemory().setData(addr, value);
break;
default:
throw new TestingDataException(Lang.get("err_multipleMemoriesFound", memoryName));
}
}
}
}

View File

@ -5,7 +5,7 @@
*/
package de.neemann.digital.testing.parser;
import de.neemann.digital.core.Bits;
import de.neemann.digital.core.*;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.data.Value;
@ -33,12 +33,11 @@ import java.util.HashMap;
public class Parser {
private final ArrayList<String> names;
private final HashMap<String, Long> signalInitMap;
private final ModelInitializer modelInit;
private final ArrayList<VirtualSignal> virtualSignals;
private final Tokenizer tok;
private final HashMap<String, Function> functions = new HashMap<>();
private LineEmitter emitter;
private DataField program;
/**
* Creates a new instance
@ -51,7 +50,7 @@ public class Parser {
functions.put("ite", new IfThenElse());
names = new ArrayList<>();
virtualSignals = new ArrayList<>();
signalInitMap = new HashMap<>();
modelInit = new ModelInitializer();
tok = new Tokenizer(new BufferedReader(new StringReader(data)));
}
@ -123,11 +122,25 @@ public class Parser {
expect(Tokenizer.Token.NUMBER);
long n = convToLong(tok.getIdent());
expect(Tokenizer.Token.SEMICOLON);
signalInitMap.put(sName, sign * n);
modelInit.initSignal(sName, sign * n);
break;
case MEMORY:
tok.consume();
expect(Tokenizer.Token.IDENT);
final String ramName = tok.getIdent();
expect(Tokenizer.Token.OPEN);
expect(Tokenizer.Token.NUMBER);
long addr = convToLong(tok.getIdent());
expect(Tokenizer.Token.CLOSE);
expect(Tokenizer.Token.EQUAL);
expect(Tokenizer.Token.NUMBER);
long val = convToLong(tok.getIdent());
expect(Tokenizer.Token.SEMICOLON);
modelInit.initMemory(ramName, (int) addr, val);
break;
case PROGRAM:
tok.consume();
program = parseData();
modelInit.initProgramMemory(parseData());
break;
case DECLARE:
tok.consume();
@ -283,17 +296,10 @@ public class Parser {
}
/**
* @return returns the program data or null if not available
* @return the model init actions
*/
public DataField getProgram() {
return program;
}
/**
* @return the signal init map
*/
public HashMap<String, Long> getSignalInit() {
return signalInitMap;
public ModelInitializer getModelInitializer() {
return modelInit;
}
/**

View File

@ -17,7 +17,7 @@ public class Tokenizer {
enum Token {
UNKNOWN, IDENT, AND, OR, XOR, BIN_NOT, OPEN, CLOSE, NUMBER, EOL, EOF, SHIFTLEFT, SHIFTRIGHT, COMMA, EQUAL,
ADD, SUB, MUL, GREATER, GREATEREQUAL, SMALER, SMALEREQUAL, DIV, MOD, END, LOOP, REPEAT, BITS, SEMICOLON,
LET, LOG_NOT, DECLARE, PROGRAM, INIT, WHILE
LET, LOG_NOT, DECLARE, PROGRAM, INIT, MEMORY, WHILE
}
private final static HashMap<String, Token> STATEMENT_MAP = new HashMap<>();
@ -32,6 +32,7 @@ public class Tokenizer {
STATEMENT_MAP.put("declare", Token.DECLARE);
STATEMENT_MAP.put("program", Token.PROGRAM);
STATEMENT_MAP.put("init", Token.INIT);
STATEMENT_MAP.put("memory", Token.MEMORY);
}
private final Reader in;

View File

@ -1158,6 +1158,8 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="err_verilogExporting">Fehler beim Export zu Verilog.</string>
<string name="err_noRomFound">Kein Programmspeicher im Modell gefunden! Ein Programmspeicher muss gewählt werden!</string>
<string name="err_multipleRomsFound">Mehrere Programmspeicher im Modell gefunden! Es darf nur ein Programmspeicher muss gewählt werden!</string>
<string name="err_noMemoryFound">Kein Speicher "{0}" im Modell gefunden!</string>
<string name="err_multipleMemoriesFound">Mehrere Speicher "{0}" im Modell gefunden!</string>
<string name="err_errorLoadingRomData">Fehler beim Laden des Programmspeichers.</string>
<string name="err_parsingSVG">Fehler beim Laden der SVG-Datei.</string>
<string name="err_morePinsDefinedInSVGAsNeeded">Die SVG-Datei enthält Pins, die es in der Schaltung nicht gibt.</string>

View File

@ -1148,6 +1148,8 @@
<string name="err_verilogExporting">Error during export to Verilog.</string>
<string name="err_noRomFound">No program memory found! The program memory needs to be flagged as such.</string>
<string name="err_multipleRomsFound">Multiple program memories found! Only one program memory is allowed.</string>
<string name="err_noMemoryFound">No memory "{0}" found in the model!</string>
<string name="err_multipleMemoriesFound">Multiple memories "{0}" found in the model!</string>
<string name="err_errorLoadingRomData">Error loading the program memory.</string>
<string name="err_parsingSVG">Error while reading the SVG file.</string>
<string name="err_morePinsDefinedInSVGAsNeeded">The SVG file contains pins that do not exist in the circuit.</string>

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2020 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.testing.parser;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Signal;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.core.memory.RAMSinglePort;
import de.neemann.digital.testing.TestingDataException;
import junit.framework.TestCase;
import java.io.IOException;
public class ModelInitializerTest extends TestCase {
public void test_program() throws IOException, ParserException, TestingDataException {
ModelInitializer mi = new Parser("A B Y\n" +
"program(1,2,3,4)\n" +
"1 1 1").parse().getModelInitializer();
Model m = new Model();
RAMSinglePort ram = m.add(new RAMSinglePort(new ElementAttributes().set(Keys.IS_PROGRAM_MEMORY, true)));
mi.init(m);
DataField program = ram.getMemory();
assertNotNull(program);
assertEquals(4, program.trim());
assertEquals(1, program.getDataWord(0));
assertEquals(2, program.getDataWord(1));
assertEquals(3, program.getDataWord(2));
assertEquals(4, program.getDataWord(3));
assertEquals(0, program.getDataWord(4));
}
public void test_signal() throws IOException, ParserException, TestingDataException {
ModelInitializer mi = new Parser("A B Y\n" +
"init s1=5;\n" +
"init s2=-1;\n" +
"1 1 1").parse().getModelInitializer();
Model m = new Model();
ObservableValue s1 = new ObservableValue("s", 8);
ObservableValue s2 = new ObservableValue("s", 8);
m.addSignal(new Signal("s1", s1, (value, highZ) -> s1.setValue(value)));
m.addSignal(new Signal("s2", s2, (value, highZ) -> s2.setValue(value)));
mi.init(m);
assertEquals(5, s1.getValue());
assertEquals(-1, s2.getValueSigned());
}
public void test_ram() throws IOException, ParserException, TestingDataException {
ModelInitializer mi = new Parser("A B Y\n" +
"memory myRam(0)=1;\n" +
"memory myRam(1)=2;\n" +
"1 1 1").parse().getModelInitializer();
Model m = new Model();
RAMSinglePort ram = m.add(new RAMSinglePort(new ElementAttributes().set(Keys.LABEL, "myRam")));
mi.init(m);
DataField ramData = ram.getMemory();
assertNotNull(ram);
assertEquals(2, ramData.trim());
assertEquals(1, ramData.getDataWord(0));
assertEquals(2, ramData.getDataWord(1));
m = new Model();
m.add(new RAMSinglePort(new ElementAttributes().set(Keys.LABEL, "wrongName")));
try {
mi.init(m);
fail();
} catch (TestingDataException e) {
assertTrue(e.getMessage().contains("myRam"));
}
}
}

View File

@ -8,8 +8,13 @@ package de.neemann.digital.testing.parser;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Signal;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.core.memory.RAMSinglePort;
import de.neemann.digital.core.memory.ROM;
import de.neemann.digital.data.Value;
import de.neemann.digital.testing.TestingDataException;
import junit.framework.TestCase;
import java.io.IOException;
@ -193,7 +198,7 @@ public class ParserTest extends TestCase {
assertEquals(1, td.getLines().size());
}
public void test_modelInitState() throws IOException, ParserException {
public void test_modelSetVar() throws IOException, ParserException {
Model model = new Model();
model.addOutput(new Signal("A", new ObservableValue("A", 3).setValue(2)));
model.addOutput(new Signal("B", new ObservableValue("B", 3).setValue(3)));
@ -204,19 +209,4 @@ public class ParserTest extends TestCase {
new LineCollector(parser, context);
assertEquals(5, context.getVar("a"));
}
public void test_program() throws IOException, ParserException {
Parser parser = new Parser("A B Y\n" +
"program(1,2,3,4)\n" +
"1 1 1").parse();
DataField program = parser.getProgram();
assertNotNull(program);
assertEquals(4, program.trim());
assertEquals(1, program.getDataWord(0));
assertEquals(2, program.getDataWord(1));
assertEquals(3, program.getDataWord(2));
assertEquals(4, program.getDataWord(3));
assertEquals(0, program.getDataWord(4));
}
}

View File

@ -13,11 +13,11 @@
<entry>
<string>Testdata</string>
<testData>
<dataString>en Out
<dataString>en Out R
init R=1;
0 1
0 1 1
</dataString>
</testData>
</entry>
@ -100,11 +100,11 @@ init R=1;
<entry>
<string>Testdata</string>
<testData>
<dataString>en Out
<dataString>en Out R
init R=7;
0 7
0 7 7
</dataString>
</testData>
</entry>