1 /*
2  * Copyright (c) 2017 Helmut Neemann
3  * Use of this source code is governed by the GPL v3 license
4  * that can be found in the LICENSE file.
5  */
6 package de.neemann.digital.hdl.vhdl2;
7 
8 import de.neemann.digital.data.Value;
9 import de.neemann.digital.draw.elements.Circuit;
10 import de.neemann.digital.hdl.model2.HDLCircuit;
11 import de.neemann.digital.hdl.model2.HDLException;
12 import de.neemann.digital.hdl.model2.HDLModel;
13 import de.neemann.digital.hdl.model2.HDLPort;
14 import de.neemann.digital.hdl.printer.CodePrinter;
15 import de.neemann.digital.lang.Lang;
16 import de.neemann.digital.testing.TestCaseDescription;
17 import de.neemann.digital.testing.TestingDataException;
18 import de.neemann.digital.testing.parser.Context;
19 import de.neemann.digital.testing.parser.LineListener;
20 import de.neemann.digital.testing.parser.ParserException;
21 import de.neemann.digital.testing.parser.TestRow;
22 
23 import java.io.File;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /**
29  * Creates a test bench for a model.
30  * The needed test data is taken from the test cases in the circuit
31  */
32 public class VHDLTestBenchCreator {
33     private final List<Circuit.TestCase> testCases;
34     private final HDLCircuit main;
35     private final HDLModel.Renaming renaming;
36     private ArrayList<File> testFileWritten;
37 
38     /**
39      * Creates a new instance
40      *
41      * @param circuit the circuit
42      * @param model   the model
43      */
VHDLTestBenchCreator(Circuit circuit, HDLModel model)44     VHDLTestBenchCreator(Circuit circuit, HDLModel model) {
45         this.main = model.getMain();
46         this.renaming = model.getRenaming();
47         testCases = circuit.getTestCases();
48         testFileWritten = new ArrayList<>();
49     }
50 
51     /**
52      * Writes the test benches
53      *
54      * @param file the original vhdl file
55      * @return this for chained calls
56      * @throws IOException  IOException
57      * @throws HDLException HDLException
58      */
write(File file)59     public VHDLTestBenchCreator write(File file) throws IOException, HDLException {
60         String filename = file.getName();
61         int p = filename.indexOf('.');
62         if (p > 0)
63             filename = filename.substring(0, p);
64 
65         VHDLRenaming renaming = new VHDLRenaming();
66         for (Circuit.TestCase tc : testCases) {
67             if (tc.hasGenericCode())
68                 throw new HDLException(Lang.get("err_hdlTestCaseHasGenericCode"));
69 
70             String testName = tc.getLabel();
71             if (testName.length() > 0) {
72                 testName = filename + "_" + renaming.checkName(testName) + "_tb";
73             } else
74                 testName = filename + "_tb";
75 
76             File f = new File(file.getParentFile(), testName + ".vhdl");
77             testFileWritten.add(f);
78             try (CodePrinter out = new CodePrinter(f)) {
79                 try {
80                     writeTestBench(out, testName, tc);
81                 } catch (TestingDataException | ParserException | RuntimeException e) {
82                     throw new HDLException(Lang.get("err_vhdlErrorWritingTestBench"), e);
83                 }
84             }
85         }
86         return this;
87     }
88 
89     /**
90      * @return returns the files which are written
91      */
getTestFileWritten()92     public ArrayList<File> getTestFileWritten() {
93         return testFileWritten;
94     }
95 
writeTestBench(CodePrinter out, String testName, Circuit.TestCase tc)96     private void writeTestBench(CodePrinter out, String testName, Circuit.TestCase tc) throws IOException, TestingDataException, ParserException {
97         out.print("--  A testbench for ").println(testName);
98         out.println("LIBRARY ieee;");
99         out.println("USE ieee.std_logic_1164.all;");
100         out.println("USE ieee.numeric_std.all;");
101         out.println();
102         out.print("entity ").print(testName).println(" is");
103         out.print("end ").print(testName).println(";");
104         out.println();
105         out.print("architecture behav of ").print(testName).println(" is").inc();
106         out.println("component main").inc();
107         VHDLCreator.writePorts(out, main);
108         out.dec().println("end component;");
109         out.println();
110         for (HDLPort p : main.getInputs())
111             out.print("signal ").print(p.getName()).print(" : ").print(VHDLCreator.getType(p.getBits())).println(";");
112         for (HDLPort p : main.getOutputs())
113             out.print("signal ").print(p.getName()).print(" : ").print(VHDLCreator.getType(p.getBits())).println(";");
114 
115         out.print("function to_string ( a: std_logic_vector) return string is\n"
116                 + "    variable b : string (1 to a'length) := (others => NUL);\n"
117                 + "    variable stri : integer := 1; \n"
118                 + "begin\n"
119                 + "    for i in a'range loop\n"
120                 + "        b(stri) := std_logic'image(a((i)))(2);\n"
121                 + "    stri := stri+1;\n"
122                 + "    end loop;\n"
123                 + "    return b;\n"
124                 + "end function;\n");
125         out.dec().println("begin").inc();
126 
127         out.println("main_0 : main port map (").inc();
128         Separator comma = new Separator(out, ",\n");
129         for (HDLPort p : main.getInputs()) {
130             comma.check();
131             out.print(p.getName() + " => " + p.getName());
132         }
133         for (HDLPort p : main.getOutputs()) {
134             comma.check();
135             out.print(p.getName() + " => " + p.getName());
136         }
137         out.println(" );").dec();
138 
139         out.println("process").inc();
140 
141         TestCaseDescription testdata = tc.getTestCaseDescription();
142 
143         ArrayList<HDLPort> dataOrder = new ArrayList<>();
144         out.println("type pattern_type is record").inc();
145         for (String name : testdata.getNames()) {
146             String saveName = renaming.checkName(name);
147             boolean found = false;
148             for (HDLPort p : main.getPorts()) {
149                 if (p.getName().equals(saveName)) {
150                     out.print(p.getName()).print(" : ").print(VHDLCreator.getType(p.getBits())).println(";");
151                     dataOrder.add(p);
152                     found = true;
153                     break;
154                 }
155             }
156             if (!found)
157                 throw new TestingDataException(Lang.get("err_testSignal_N_notFound", name));
158         }
159         out.dec().println("end record;");
160 
161         out.println("type pattern_array is array (natural range <>) of pattern_type;");
162         out.println("constant patterns : pattern_array := (").inc();
163 
164         LineListener parent = new LineListenerVHDL(out, dataOrder);
165         testdata.getLines().emitLines(parent, new Context());
166 
167         out.println(");").dec();
168 
169 
170         String loopVar = "i";
171         int lv = 0;
172         while (loopVarExists(loopVar, main.getPorts()))
173             loopVar = "i" + (lv++);
174 
175         out.dec().println("begin").inc();
176         out.print("for ").print(loopVar).println(" in patterns'range loop").inc();
177 
178         for (HDLPort p : main.getInputs())
179             out.print(p.getName()).print(" <= patterns(").print(loopVar).print(").").print(p.getName()).println(";");
180         out.println("wait for 10 ns;");
181         for (HDLPort p : main.getOutputs()) {
182             out.print("assert std_match(").print(p.getName()).print(", patterns(").print(loopVar).print(").").print(p.getName()).print(")");
183             out.print(" OR (")
184                     .print(p.getName())
185                     .print(" = ")
186                     .print(getSimpleValue(p.getBits(), 'Z'))
187                     .print(" AND patterns(").print(loopVar).print(").").print(p.getName()).print(" = ")
188                     .print(getSimpleValue(p.getBits(), 'Z'))
189                     .print(")").eol();
190             out.inc().print("report \"wrong value for ").print(p.getName()).print(", ").print(loopVar).print("=\" & integer'image(").print(loopVar).println(")")
191                     .print(" & \", expected \"")
192                     .print(" & ").print(convertFunc(p)).print("(patterns(").print(loopVar).print(").").print(p.getName()).print(")")
193                     .print(" & \", found \"")
194                     .print(" & ").print(convertFunc(p)).print("(").print(p.getName()).print(")")
195                     .print(" severity error;").dec();
196         }
197 
198         out.dec().println("end loop;");
199         out.println("wait;");
200         out.dec().println("end process;");
201         out.dec().println("end behav;");
202     }
203 
convertFunc(HDLPort p)204     private String convertFunc(HDLPort p) {
205         if (p.getBits() > 1)
206             return "to_string";
207         return "std_logic'image";
208     }
209 
loopVarExists(String loopVar, ArrayList<HDLPort> ports)210     private boolean loopVarExists(String loopVar, ArrayList<HDLPort> ports) {
211         for (HDLPort p : ports)
212             if (p.getName().equalsIgnoreCase(loopVar))
213                 return true;
214         return false;
215     }
216 
getSimpleValue(int bits, char c)217     private static String getSimpleValue(int bits, char c) {
218         if (bits == 1)
219             return "'" + c + "'";
220 
221         StringBuilder sb = new StringBuilder("\"");
222         for (int i = 0; i < bits; i++)
223             sb.append(c);
224 
225         return sb.append('"').toString();
226     }
227 
228     /*
229     private static void writeCharValue(CodePrinter out, char c, int bits) throws IOException {
230         if (bits > 1) {
231             out.print("\"");
232             for (int i = 0; i < bits; i++)
233                 out.print(c);
234             out.print("\"");
235         } else
236             out.print("'").print(c).print("'");
237     }*/
238 
239     private static final class LineListenerVHDL implements LineListener {
240         private final CodePrinter out;
241         private final ArrayList<HDLPort> dataOrder;
242         private final Separator lineSep;
243         private int line = 0;
244 
LineListenerVHDL(CodePrinter out, ArrayList<HDLPort> dataOrder)245         private LineListenerVHDL(CodePrinter out, ArrayList<HDLPort> dataOrder) {
246             this.out = out;
247             this.dataOrder = dataOrder;
248             lineSep = new Separator(out, "") {
249                 @Override
250                 public void printSeparator(CodePrinter out) throws IOException {
251                     out.print(", -- i=").print((line++)).print("\n");
252                 }
253             };
254         }
255 
256         @Override
add(TestRow testRow)257         public void add(TestRow testRow) {
258             try {
259                 boolean containsClock = false;
260                 for (Value v : testRow.getValues())
261                     if (v.getType() == Value.Type.CLOCK)
262                         containsClock = true;
263                 if (containsClock) {
264                     lineSep.check();
265                     writeValues(testRow.getValues(), true, 0);
266                     lineSep.check();
267                     writeValues(testRow.getValues(), true, 1);
268                 }
269                 lineSep.check();
270                 writeValues(testRow.getValues(), false, 0);
271             } catch (IOException e) {
272                 throw new RuntimeException(e);
273             }
274         }
275 
writeValues(Value[] values, boolean isClock, int clock)276         private void writeValues(Value[] values, boolean isClock, int clock) throws IOException {
277             out.print("(");
278             Separator sep = new Separator(out, ", ");
279             for (int i = 0; i < values.length; i++) {
280                 sep.check();
281                 Value val = values[i];
282                 int bits = dataOrder.get(i).getBits();
283                 switch (val.getType()) {
284                     case NORMAL:
285                         if (isClock && dataOrder.get(i).getDirection() == HDLPort.Direction.IN)
286                             out.print(getSimpleValue(bits, '-'));
287                         else
288                             out.print(VHDLCreator.value(val.getValue(), bits));
289                         break;
290                     case DONTCARE:
291                         out.print(getSimpleValue(bits, '-'));
292                         break;
293                     case HIGHZ:
294                         out.print(getSimpleValue(bits, 'Z'));
295                         break;
296                     case CLOCK:
297                         out.print("'").print(clock).print("'");
298                         break;
299                     default:
300                         throw new RuntimeException(Lang.get("err_vhdlValuesOfType_N_notAllowed", val.getType()));
301                 }
302             }
303             out.print(")");
304         }
305     }
306 }
307