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