diff --git a/src/main/java/de/neemann/digital/testing/TestExecutor.java b/src/main/java/de/neemann/digital/testing/TestExecutor.java index 611bf859d..15352b432 100644 --- a/src/main/java/de/neemann/digital/testing/TestExecutor.java +++ b/src/main/java/de/neemann/digital/testing/TestExecutor.java @@ -110,7 +110,7 @@ public class TestExecutor { model.init(); - lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), new Context()); + lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), new Context().setModel(model)); return this; } diff --git a/src/main/java/de/neemann/digital/testing/parser/Context.java b/src/main/java/de/neemann/digital/testing/parser/Context.java index f69185267..36fcbf150 100644 --- a/src/main/java/de/neemann/digital/testing/parser/Context.java +++ b/src/main/java/de/neemann/digital/testing/parser/Context.java @@ -5,6 +5,8 @@ */ package de.neemann.digital.testing.parser; +import de.neemann.digital.core.Model; +import de.neemann.digital.core.Signal; import de.neemann.digital.lang.Lang; import java.util.HashMap; @@ -16,6 +18,7 @@ import java.util.Map; public class Context { private final Context parent; private HashMap map; + private Model model; /** * Creates an empty context @@ -42,8 +45,14 @@ public class Context { */ public long getVar(String name) throws ParserException { if (map == null || !map.containsKey(name)) { - if (parent == null) + if (parent == null) { + if (model != null) { + for (Signal s : model.getSignals()) + if (s.getName().equals(name)) + return s.getValue().getValue(); + } throw new ParserException(Lang.get("err_variable_N0_notFound", name)); + } return parent.getVar(name); } else return map.get(name); @@ -83,4 +92,15 @@ public class Context { sb.append(e.getKey()).append("=").append(e.getValue()); } } + + /** + * Sets the model where tis context is used with. + * + * @param model the model + * @return this for chained calls + */ + public Context setModel(Model model) { + this.model = model; + return this; + } } diff --git a/src/main/java/de/neemann/digital/testing/parser/LineEmitterWhile.java b/src/main/java/de/neemann/digital/testing/parser/LineEmitterWhile.java new file mode 100644 index 000000000..cb2c32b1c --- /dev/null +++ b/src/main/java/de/neemann/digital/testing/parser/LineEmitterWhile.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Line emitter which implements the while loop + */ +public class LineEmitterWhile implements LineEmitter { + private final Expression condition; + private final LineEmitter inner; + + /** + * Creates a new instance + * + * @param condition the condition + * @param inner the inner LineEmitter + */ + public LineEmitterWhile(Expression condition, LineEmitter inner) { + this.condition = condition; + this.inner = inner; + } + + @Override + public void emitLines(LineListener listener, Context context) throws ParserException { + while (condition.value(context) != 0) + inner.emitLines(listener, context); + } +} 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 e46c6a785..3dd3897d2 100644 --- a/src/main/java/de/neemann/digital/testing/parser/Parser.java +++ b/src/main/java/de/neemann/digital/testing/parser/Parser.java @@ -33,8 +33,8 @@ public class Parser { private final ArrayList names; private final Tokenizer tok; + private final HashMap functions = new HashMap<>(); private LineEmitter emitter; - private HashMap functions = new HashMap<>(); /** * Creates a new instance @@ -111,7 +111,7 @@ public class Parser { case LET: tok.consume(); expect(Tokenizer.Token.IDENT); - final String varName=tok.getIdent(); + final String varName = tok.getIdent(); expect(Tokenizer.Token.EQUAL); final Expression intValue = parseExpression(); expect(Tokenizer.Token.SEMICOLON); @@ -134,6 +134,13 @@ public class Parser { expect(Tokenizer.Token.CLOSE); list.add(new LineEmitterRepeat(var, count, parseRows(Tokenizer.Token.LOOP))); break; + case WHILE: + tok.consume(); + expect(Tokenizer.Token.OPEN); + final Expression condition = parseExpression(); + expect(Tokenizer.Token.CLOSE); + list.add(new LineEmitterWhile(condition, parseRows(Tokenizer.Token.WHILE))); + break; default: throw newUnexpectedToken(t); } @@ -283,6 +290,7 @@ public class Parser { } return ac; } + private Expression parseGreaterEqual() throws IOException, ParserException { Expression ac = parseEquals(); while (isToken(Tokenizer.Token.GREATEREQUAL)) { @@ -440,7 +448,7 @@ public class Parser { return (c) -> ~notExp.value(c); case LOG_NOT: Expression boolNotExp = parseIdent(); - return (c) -> boolNotExp.value(c)==0?1:0; + return (c) -> boolNotExp.value(c) == 0 ? 1 : 0; case OPEN: Expression exp = parseExpression(); expect(Tokenizer.Token.CLOSE); 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 858a77d6f..dde77f8c0 100644 --- a/src/main/java/de/neemann/digital/testing/parser/Tokenizer.java +++ b/src/main/java/de/neemann/digital/testing/parser/Tokenizer.java @@ -16,7 +16,8 @@ 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 + ADD, SUB, MUL, GREATER, GREATEREQUAL, SMALER, SMALEREQUAL, DIV, MOD, END, LOOP, REPEAT, BITS, SEMICOLON, + LET, LOG_NOT, WHILE } private static HashMap statementMap = new HashMap<>(); @@ -27,6 +28,7 @@ public class Tokenizer { statementMap.put("repeat", Token.REPEAT); statementMap.put("bits", Token.BITS); statementMap.put("let", Token.LET); + statementMap.put("while", Token.WHILE); } private final Reader in; diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 091625450..419d4f6dd 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -2029,6 +2029,23 @@ loop(a,16) end loop +

Unter Umständen kann es notwendig sein, auf den Startzustand der Schaltung +reagieren zu können. Daher können die Signale, die in der Schaltung bereitgestellt werden, +innerhalb des Testfalls verwendet werden. Soll zum Beispiel ein Zähler, welcher in +einem undefinierten Zustand startet, getestet werden, kann dieser in einen definierten +Zustand getaktet werden:

+ +
C Q_3 Q_2 Q_1 Q_0
+
+# clock counter to 1111
+while(!(Q_3 & Q_2 & Q_1 & Q_0))
+C x x x x
+end while
+
+# start the test execution
+repeat(16) C bits(4,n)
+
+

Unter Umständen ist die Erzeugung von Zufallszahlen in Testfällen hilfreich. Diese können mit der Funktion 'random([n])' erzeugt werden. Die erzeugte Zahl ist größer gleich Null und kleiner als [n]. Betrachtet man einen 16-Bit Multiplikator als Beispiel, diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 3fe7aa9e2..c147946ca 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -1982,6 +1982,22 @@ loop(a,16) end loop +

Under certain circumstances it may be necessary to be able to react to the initial +state of the circuit. Therefore the signals provided in the circuit can be used within +the test case. For example, if a counter that starts in an undefined state is to be +tested, it can be clocked to a defined state:

+ +
C Q_3 Q_2 Q_1 Q_0
+
+# clock counter to 1111
+while(!(Q_3 & Q_2 & Q_1 & Q_0))
+C x x x x
+end while
+
+# start the test execution
+repeat(16) C bits(4,n)
+
+

It may be helpful to generate random numbers in test cases. These can be created with the function 'random([n])'. The generated number is greater than or equal to zero and less than [n]. Considering a 16-bit multiplier as an example, diff --git a/src/test/java/de/neemann/digital/integration/TestExamples.java b/src/test/java/de/neemann/digital/integration/TestExamples.java index 0867298b8..baf210383 100644 --- a/src/test/java/de/neemann/digital/integration/TestExamples.java +++ b/src/test/java/de/neemann/digital/integration/TestExamples.java @@ -43,8 +43,8 @@ public class TestExamples extends TestCase { */ public void testTestExamples() throws Exception { File examples = new File(Resources.getRoot(), "/dig/test"); - assertEquals(189, new FileScanner(this::check).scan(examples)); - assertEquals(177, testCasesInFiles); + assertEquals(191, new FileScanner(this::check).scan(examples)); + assertEquals(179, testCasesInFiles); } /** diff --git a/src/test/java/de/neemann/digital/testing/parser/LineCollector.java b/src/test/java/de/neemann/digital/testing/parser/LineCollector.java index 547b6e6b0..2dc2a9fb3 100644 --- a/src/test/java/de/neemann/digital/testing/parser/LineCollector.java +++ b/src/test/java/de/neemann/digital/testing/parser/LineCollector.java @@ -10,6 +10,7 @@ import de.neemann.digital.data.Value; import java.util.ArrayList; /** + * */ public class LineCollector implements LineListener { private final ArrayList names; @@ -22,8 +23,12 @@ public class LineCollector implements LineListener { } public LineCollector(Parser parser) throws ParserException { + this(parser, new Context()); + } + + public LineCollector(Parser parser, Context context) throws ParserException { this.list = new ArrayList<>(); - parser.getLines().emitLines(this, new Context()); + parser.getLines().emitLines(this, context); names = parser.getNames(); } 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 97dcec364..6406269c2 100644 --- a/src/test/java/de/neemann/digital/testing/parser/ParserTest.java +++ b/src/test/java/de/neemann/digital/testing/parser/ParserTest.java @@ -5,6 +5,9 @@ */ 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.data.Value; import de.neemann.digital.testing.TestingDataException; import junit.framework.TestCase; @@ -191,4 +194,16 @@ public class ParserTest extends TestCase { assertEquals(1, td.getLines().size()); } + public void test_modelInitState() throws IOException, ParserException { + Model model = new Model(); + model.addSignal(new Signal("A", new ObservableValue("A", 3).setValue(2))); + model.addSignal(new Signal("B", new ObservableValue("B", 3).setValue(3))); + Parser parser = new Parser("A B Y\n" + + "let a=A+B;\n" + + "1 1 1\n#test").parse(); + Context context = new Context().setModel(model); + LineCollector td = new LineCollector(parser, context); + assertEquals(5, context.getVar("a")); + } + } diff --git a/src/test/resources/dig/test/TestCaseInitialState.dig b/src/test/resources/dig/test/TestCaseInitialState.dig new file mode 100644 index 000000000..9f4982285 --- /dev/null +++ b/src/test/resources/dig/test/TestCaseInitialState.dig @@ -0,0 +1,242 @@ + + + 1 + + + + T_FF + + + withEnable + false + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + Clock + + + Label + C + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + Out + + + rotation + + + + Label + Q_0 + + + + + + Out + + + rotation + + + + Label + Q_1 + + + + + + Out + + + rotation + + + + Label + Q_2 + + + + + + Out + + + rotation + + + + Label + Q_3 + + + + + + Out + + + rotation + + + + Label + Q_4 + + + + + + Testcase + + + Testdata + + C Q_4 Q_3 Q_2 Q_1 Q_0 + +# get the current model state and store it in n0 +let n0=(Q_4<<4)|(Q_3<<3)|(Q_2<<2)|(Q_1<<1)|Q_0; + +# start the test execution +repeat(32) C bits(5,n+n0+1) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/test/TestCaseInitialState2.dig b/src/test/resources/dig/test/TestCaseInitialState2.dig new file mode 100644 index 000000000..8031c0a8f --- /dev/null +++ b/src/test/resources/dig/test/TestCaseInitialState2.dig @@ -0,0 +1,245 @@ + + + 1 + + + + T_FF + + + withEnable + false + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + Clock + + + Label + C + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + T_FF + + + withEnable + false + + + inverterConfig + + C + + + + + + + Out + + + rotation + + + + Label + Q_0 + + + + + + Out + + + rotation + + + + Label + Q_1 + + + + + + Out + + + rotation + + + + Label + Q_2 + + + + + + Out + + + rotation + + + + Label + Q_3 + + + + + + Out + + + rotation + + + + Label + Q_4 + + + + + + Testcase + + + Testdata + + C Q_4 Q_3 Q_2 Q_1 Q_0 + +# clock counter to 11111 +while(!(Q_4&Q_3&Q_2&Q_1&Q_0)) +C x x x x x +end while + + +# start the test execution +repeat(32) C bits(5,n) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file