Merge pull request #1085 from YosysHQ/eddie/shregmap_improve
[yosys.git] / passes / tests / test_autotb.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/yosys.h"
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <time.h>
24
25 USING_YOSYS_NAMESPACE
26 PRIVATE_NAMESPACE_BEGIN
27
28 static std::string id(std::string internal_id)
29 {
30 const char *str = internal_id.c_str();
31 bool do_escape = false;
32
33 if (*str == '\\')
34 str++;
35
36 if ('0' <= *str && *str <= '9')
37 do_escape = true;
38
39 for (int i = 0; str[i]; i++) {
40 if ('0' <= str[i] && str[i] <= '9')
41 continue;
42 if ('a' <= str[i] && str[i] <= 'z')
43 continue;
44 if ('A' <= str[i] && str[i] <= 'Z')
45 continue;
46 if (str[i] == '_')
47 continue;
48 do_escape = true;
49 break;
50 }
51
52 if (do_escape)
53 return "\\" + std::string(str) + " ";
54 return std::string(str);
55 }
56
57 static std::string idx(std::string str)
58 {
59 if (str[0] == '\\')
60 return str.substr(1);
61 return str;
62 }
63
64 static std::string idy(std::string str1, std::string str2 = std::string(), std::string str3 = std::string())
65 {
66 str1 = idx(str1);
67 if (!str2.empty())
68 str1 += "_" + idx(str2);
69 if (!str3.empty())
70 str1 += "_" + idx(str3);
71 return id(str1);
72 }
73
74 static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int seed)
75 {
76 f << stringf("`ifndef outfile\n");
77 f << stringf("\t`define outfile \"/dev/stdout\"\n");
78 f << stringf("`endif\n");
79
80 f << stringf("module testbench;\n\n");
81
82 f << stringf("integer i;\n");
83 f << stringf("integer file;\n\n");
84
85 f << stringf("reg [31:0] xorshift128_x = 123456789;\n");
86 f << stringf("reg [31:0] xorshift128_y = 362436069;\n");
87 f << stringf("reg [31:0] xorshift128_z = 521288629;\n");
88 f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", seed ? seed : int(time(NULL)));
89 f << stringf("reg [31:0] xorshift128_t;\n\n");
90 f << stringf("task xorshift128;\n");
91 f << stringf("begin\n");
92 f << stringf("\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n");
93 f << stringf("\txorshift128_x = xorshift128_y;\n");
94 f << stringf("\txorshift128_y = xorshift128_z;\n");
95 f << stringf("\txorshift128_z = xorshift128_w;\n");
96 f << stringf("\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n");
97 f << stringf("end\n");
98 f << stringf("endtask\n\n");
99
100 for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it)
101 {
102 std::map<std::string, int> signal_in;
103 std::map<std::string, std::string> signal_const;
104 std::map<std::string, int> signal_clk;
105 std::map<std::string, int> signal_out;
106
107 RTLIL::Module *mod = it->second;
108
109 if (mod->get_bool_attribute("\\gentb_skip"))
110 continue;
111
112 int count_ports = 0;
113 log("Generating test bench for module `%s'.\n", it->first.c_str());
114 for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); ++it2) {
115 RTLIL::Wire *wire = it2->second;
116 if (wire->port_output) {
117 count_ports++;
118 signal_out[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
119 f << stringf("wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
120 } else if (wire->port_input) {
121 count_ports++;
122 bool is_clksignal = wire->get_bool_attribute("\\gentb_clock");
123 for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); ++it3)
124 for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); ++it4) {
125 if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1)
126 continue;
127 RTLIL::SigSpec &signal = (*it4)->signal;
128 for (auto &c : signal.chunks())
129 if (c.wire == wire)
130 is_clksignal = true;
131 }
132 if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) {
133 signal_clk[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
134 } else {
135 signal_in[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
136 if (wire->attributes.count("\\gentb_constant") != 0)
137 signal_const[idy("sig", mod->name.str(), wire->name.str())] = wire->attributes["\\gentb_constant"].as_string();
138 }
139 f << stringf("reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
140 }
141 }
142 f << stringf("%s %s(\n", id(mod->name.str()).c_str(), idy("uut", mod->name.str()).c_str());
143 for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); ++it2) {
144 RTLIL::Wire *wire = it2->second;
145 if (wire->port_output || wire->port_input)
146 f << stringf("\t.%s(%s)%s\n", id(wire->name.str()).c_str(),
147 idy("sig", mod->name.str(), wire->name.str()).c_str(), --count_ports ? "," : "");
148 }
149 f << stringf(");\n\n");
150
151 f << stringf("task %s;\n", idy(mod->name.str(), "reset").c_str());
152 f << stringf("begin\n");
153 int delay_counter = 0;
154 for (auto it = signal_in.begin(); it != signal_in.end(); ++it)
155 f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
156 for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it)
157 f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
158 f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
159 for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) {
160 f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
161 f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
162 }
163 delay_counter = 0;
164 for (auto it = signal_in.begin(); it != signal_in.end(); ++it)
165 f << stringf("\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2);
166 f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
167 for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) {
168 f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
169 f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
170 }
171 delay_counter = 0;
172 for (auto it = signal_in.begin(); it != signal_in.end(); ++it) {
173 if (signal_const.count(it->first) == 0)
174 continue;
175 f << stringf("\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str());
176 }
177 f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
178 f << stringf("end\n");
179 f << stringf("endtask\n\n");
180
181 f << stringf("task %s;\n", idy(mod->name.str(), "update_data").c_str());
182 f << stringf("begin\n");
183 delay_counter = 0;
184 for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
185 if (signal_const.count(it->first) > 0)
186 continue;
187 f << stringf("\txorshift128;\n");
188 f << stringf("\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2);
189 }
190 f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
191 f << stringf("end\n");
192 f << stringf("endtask\n\n");
193
194 f << stringf("task %s;\n", idy(mod->name.str(), "update_clock").c_str());
195 f << stringf("begin\n");
196 if (signal_clk.size()) {
197 f << stringf("\txorshift128;\n");
198 f << stringf("\t{");
199 int total_clock_bits = 0;
200 for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
201 f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
202 total_clock_bits += it->second;
203 }
204 f << stringf(" } = {");
205 for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
206 f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
207 f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1);
208 }
209 f << stringf("end\n");
210 f << stringf("endtask\n\n");
211
212 char shorthand = 'A';
213 std::vector<std::string> header1;
214 std::string header2 = "";
215
216 f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str());
217 f << stringf("begin\n");
218 f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {");
219 if (signal_in.size())
220 for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
221 f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
222 int len = it->second;
223 header2 += ", \"";
224 if (len > 1)
225 header2 += "/", len--;
226 while (len > 1)
227 header2 += "-", len--;
228 if (len > 0)
229 header2 += shorthand, len--;
230 header2 += "\"";
231 header1.push_back(" " + it->first);
232 header1.back()[0] = shorthand;
233 shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
234 }
235 else {
236 f << stringf(" 1'bx");
237 header2 += ", \"#\"";
238 }
239 f << stringf(" }, {");
240 header2 += ", \" \"";
241 if (signal_clk.size()) {
242 for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
243 f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
244 int len = it->second;
245 header2 += ", \"";
246 if (len > 1)
247 header2 += "/", len--;
248 while (len > 1)
249 header2 += "-", len--;
250 if (len > 0)
251 header2 += shorthand, len--;
252 header2 += "\"";
253 header1.push_back(" " + it->first);
254 header1.back()[0] = shorthand;
255 shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
256 }
257 } else {
258 f << stringf(" 1'bx");
259 header2 += ", \"#\"";
260 }
261 f << stringf(" }, {");
262 header2 += ", \" \"";
263 if (signal_out.size()) {
264 for (auto it = signal_out.begin(); it != signal_out.end(); it++) {
265 f << stringf("%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str());
266 int len = it->second;
267 header2 += ", \"";
268 if (len > 1)
269 header2 += "/", len--;
270 while (len > 1)
271 header2 += "-", len--;
272 if (len > 0)
273 header2 += shorthand, len--;
274 header2 += "\"";
275 header1.push_back(" " + it->first);
276 header1.back()[0] = shorthand;
277 shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
278 }
279 } else {
280 f << stringf(" 1'bx");
281 header2 += ", \"#\"";
282 }
283 f << stringf(" }, $time, i);\n");
284 f << stringf("end\n");
285 f << stringf("endtask\n\n");
286
287 f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str());
288 f << stringf("begin\n");
289 f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
290 for (auto &hdr : header1)
291 f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", hdr.c_str());
292 f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
293 f << stringf("\t$fdisplay(file, {\"#OUT# \"%s});\n", header2.c_str());
294 f << stringf("end\n");
295 f << stringf("endtask\n\n");
296
297 f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str());
298 f << stringf("begin\n");
299 f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str());
300 f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str());
301 f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter);
302 f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str());
303 f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_data").c_str());
304 f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_clock").c_str());
305 f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "print_status").c_str());
306 f << stringf("\tend\n");
307 f << stringf("end\n");
308 f << stringf("endtask\n\n");
309 }
310
311 f << stringf("initial begin\n");
312 f << stringf("\t// $dumpfile(\"testbench.vcd\");\n");
313 f << stringf("\t// $dumpvars(0, testbench);\n");
314 f << stringf("\tfile = $fopen(`outfile);\n");
315 for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it)
316 if (!it->second->get_bool_attribute("\\gentb_skip"))
317 f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str());
318 f << stringf("\t$fclose(file);\n");
319 f << stringf("\t$finish;\n");
320 f << stringf("end\n\n");
321
322 f << stringf("endmodule\n");
323 }
324
325 struct TestAutotbBackend : public Backend {
326 TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { }
327 void help() YS_OVERRIDE
328 {
329 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
330 log("\n");
331 log(" test_autotb [options] [filename]\n");
332 log("\n");
333 log("Automatically create primitive Verilog test benches for all modules in the\n");
334 log("design. The generated testbenches toggle the input pins of the module in\n");
335 log("a semi-random manner and dumps the resulting output signals.\n");
336 log("\n");
337 log("This can be used to check the synthesis results for simple circuits by\n");
338 log("comparing the testbench output for the input files and the synthesis results.\n");
339 log("\n");
340 log("The backend automatically detects clock signals. Additionally a signal can\n");
341 log("be forced to be interpreted as clock signal by setting the attribute\n");
342 log("'gentb_clock' on the signal.\n");
343 log("\n");
344 log("The attribute 'gentb_constant' can be used to force a signal to a constant\n");
345 log("value after initialization. This can e.g. be used to force a reset signal\n");
346 log("low in order to explore more inner states in a state machine.\n");
347 log("\n");
348 log(" -n <int>\n");
349 log(" number of iterations the test bench should run (default = 1000)\n");
350 log("\n");
351 }
352 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
353 {
354 int num_iter = 1000;
355 int seed = 0;
356
357 log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
358
359 int argidx;
360 for (argidx = 1; argidx < GetSize(args); argidx++)
361 {
362 if (args[argidx] == "-n" && argidx+1 < GetSize(args)) {
363 num_iter = atoi(args[++argidx].c_str());
364 continue;
365 }
366 if (args[argidx] == "-seed" && argidx+1 < GetSize(args)) {
367 seed = atoi(args[++argidx].c_str());
368 continue;
369 }
370 break;
371 }
372
373 extra_args(f, filename, args, argidx);
374 autotest(*f, design, num_iter, seed);
375 }
376 } TestAutotbBackend;
377
378 PRIVATE_NAMESPACE_END
379