diff --git a/src/main/java/de/neemann/digital/testing/TestCaseDescription.java b/src/main/java/de/neemann/digital/testing/TestCaseDescription.java index 5e615db7a..03e89f944 100644 --- a/src/main/java/de/neemann/digital/testing/TestCaseDescription.java +++ b/src/main/java/de/neemann/digital/testing/TestCaseDescription.java @@ -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 names; private transient ArrayList virtualSignals; - private transient DataField program; - private transient HashMap 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 getSignalInit() throws TestingDataException { - check(); - return signalInit; + return modelInitializer; } @Override diff --git a/src/main/java/de/neemann/digital/testing/TestExecutor.java b/src/main/java/de/neemann/digital/testing/TestExecutor.java index 3504dc810..2ed548c56 100644 --- a/src/main/java/de/neemann/digital/testing/TestExecutor.java +++ b/src/main/java/de/neemann/digital/testing/TestExecutor.java @@ -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 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 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 -> { diff --git a/src/main/java/de/neemann/digital/testing/parser/ModelInitializer.java b/src/main/java/de/neemann/digital/testing/parser/ModelInitializer.java new file mode 100644 index 000000000..45ffc9d1b --- /dev/null +++ b/src/main/java/de/neemann/digital/testing/parser/ModelInitializer.java @@ -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 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 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 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)); + } + } + } +} diff --git a/src/main/java/de/neemann/digital/testing/parser/Parser.java b/src/main/java/de/neemann/digital/testing/parser/Parser.java index 847c4f95c..80829ee2e 100644 --- a/src/main/java/de/neemann/digital/testing/parser/Parser.java +++ b/src/main/java/de/neemann/digital/testing/parser/Parser.java @@ -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 names; - private final HashMap signalInitMap; + private final ModelInitializer modelInit; private final ArrayList virtualSignals; private final Tokenizer tok; private final HashMap 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 getSignalInit() { - return signalInitMap; + public ModelInitializer getModelInitializer() { + return modelInit; } /** diff --git a/src/main/java/de/neemann/digital/testing/parser/Tokenizer.java b/src/main/java/de/neemann/digital/testing/parser/Tokenizer.java index 51f0ffed6..b5d54a7b5 100644 --- a/src/main/java/de/neemann/digital/testing/parser/Tokenizer.java +++ b/src/main/java/de/neemann/digital/testing/parser/Tokenizer.java @@ -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 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; diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 1df35cc04..23da11520 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -1158,6 +1158,8 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Fehler beim Export zu Verilog. Kein Programmspeicher im Modell gefunden! Ein Programmspeicher muss gewählt werden! Mehrere Programmspeicher im Modell gefunden! Es darf nur ein Programmspeicher muss gewählt werden! + Kein Speicher "{0}" im Modell gefunden! + Mehrere Speicher "{0}" im Modell gefunden! Fehler beim Laden des Programmspeichers. Fehler beim Laden der SVG-Datei. Die SVG-Datei enthält Pins, die es in der Schaltung nicht gibt. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 831e100f7..ec9ae981f 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -1148,6 +1148,8 @@ Error during export to Verilog. No program memory found! The program memory needs to be flagged as such. Multiple program memories found! Only one program memory is allowed. + No memory "{0}" found in the model! + Multiple memories "{0}" found in the model! Error loading the program memory. Error while reading the SVG file. The SVG file contains pins that do not exist in the circuit. diff --git a/src/test/java/de/neemann/digital/testing/parser/ModelInitializerTest.java b/src/test/java/de/neemann/digital/testing/parser/ModelInitializerTest.java new file mode 100644 index 000000000..d8591a705 --- /dev/null +++ b/src/test/java/de/neemann/digital/testing/parser/ModelInitializerTest.java @@ -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")); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/testing/parser/ParserTest.java b/src/test/java/de/neemann/digital/testing/parser/ParserTest.java index 8086228d6..eae8e8a7f 100644 --- a/src/test/java/de/neemann/digital/testing/parser/ParserTest.java +++ b/src/test/java/de/neemann/digital/testing/parser/ParserTest.java @@ -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)); - } - } diff --git a/src/test/resources/dig/test/programTestSignal.dig b/src/test/resources/dig/test/programTestSignal.dig index b00217520..123a56221 100644 --- a/src/test/resources/dig/test/programTestSignal.dig +++ b/src/test/resources/dig/test/programTestSignal.dig @@ -13,11 +13,11 @@ Testdata - en Out + en Out R init R=1; -0 1 +0 1 1 @@ -100,11 +100,11 @@ init R=1; Testdata - en Out + en Out R init R=7; -0 7 +0 7 7