1 /*
2  *  yosys -- Yosys Open SYnthesis Suite
3  *
4  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com>
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 
id(std::string internal_id)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 
idx(std::string str)57 static std::string idx(std::string str)
58 {
59 	if (str[0] == '\\')
60 		return str.substr(1);
61 	return str;
62 }
63 
idy(std::string str1,std::string str2=std::string (),std::string str3=std::string ())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 
autotest(std::ostream & f,RTLIL::Design * design,int num_iter,int seed)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 	f << stringf("reg [1023:0] filename;\n\n");
85 
86 	f << stringf("reg [31:0] xorshift128_x = 123456789;\n");
87 	f << stringf("reg [31:0] xorshift128_y = 362436069;\n");
88 	f << stringf("reg [31:0] xorshift128_z = 521288629;\n");
89 	f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", seed ? seed : int(time(nullptr)));
90 	f << stringf("reg [31:0] xorshift128_t;\n\n");
91 	f << stringf("task xorshift128;\n");
92 	f << stringf("begin\n");
93 	f << stringf("\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n");
94 	f << stringf("\txorshift128_x = xorshift128_y;\n");
95 	f << stringf("\txorshift128_y = xorshift128_z;\n");
96 	f << stringf("\txorshift128_z = xorshift128_w;\n");
97 	f << stringf("\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n");
98 	f << stringf("end\n");
99 	f << stringf("endtask\n\n");
100 
101 	for (auto mod : design->modules())
102 	{
103 		std::map<std::string, int> signal_in;
104 		std::map<std::string, std::string> signal_const;
105 		std::map<std::string, int> signal_clk;
106 		std::map<std::string, int> signal_out;
107 
108 		if (mod->get_bool_attribute(ID::gentb_skip))
109 			continue;
110 
111 		int count_ports = 0;
112 		log("Generating test bench for module `%s'.\n", mod->name.c_str());
113 		for (auto wire : mod->wires()) {
114 			if (wire->port_output) {
115 				count_ports++;
116 				signal_out[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
117 				f << stringf("wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
118 			} else if (wire->port_input) {
119 				count_ports++;
120 				bool is_clksignal = wire->get_bool_attribute(ID::gentb_clock);
121 				for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); ++it3)
122 				for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); ++it4) {
123 					if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1)
124 						continue;
125 					RTLIL::SigSpec &signal = (*it4)->signal;
126 					for (auto &c : signal.chunks())
127 						if (c.wire == wire)
128 							is_clksignal = true;
129 				}
130 				if (is_clksignal && wire->attributes.count(ID::gentb_constant) == 0) {
131 					signal_clk[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
132 				} else {
133 					signal_in[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
134 					if (wire->attributes.count(ID::gentb_constant) != 0)
135 						signal_const[idy("sig", mod->name.str(), wire->name.str())] = wire->attributes[ID::gentb_constant].as_string();
136 				}
137 				f << stringf("reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
138 			}
139 		}
140 		f << stringf("%s %s(\n", id(mod->name.str()).c_str(), idy("uut", mod->name.str()).c_str());
141 		for (auto wire : mod->wires()) {
142 			if (wire->port_output || wire->port_input)
143 				f << stringf("\t.%s(%s)%s\n", id(wire->name.str()).c_str(),
144 						idy("sig", mod->name.str(), wire->name.str()).c_str(), --count_ports ? "," : "");
145 		}
146 		f << stringf(");\n\n");
147 
148 		f << stringf("task %s;\n", idy(mod->name.str(), "reset").c_str());
149 		f << stringf("begin\n");
150 		int delay_counter = 0;
151 		for (auto it = signal_in.begin(); it != signal_in.end(); ++it)
152 			f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
153 		for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it)
154 			f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
155 		f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
156 		for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) {
157 			f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
158 			f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
159 		}
160 		delay_counter = 0;
161 		for (auto it = signal_in.begin(); it != signal_in.end(); ++it)
162 			f << stringf("\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2);
163 		f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
164 		for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) {
165 			f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
166 			f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
167 		}
168 		delay_counter = 0;
169 		for (auto it = signal_in.begin(); it != signal_in.end(); ++it) {
170 			if (signal_const.count(it->first) == 0)
171 				continue;
172 			f << stringf("\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str());
173 		}
174 		f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
175 		f << stringf("end\n");
176 		f << stringf("endtask\n\n");
177 
178 		f << stringf("task %s;\n", idy(mod->name.str(), "update_data").c_str());
179 		f << stringf("begin\n");
180 		delay_counter = 0;
181 		for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
182 			if (signal_const.count(it->first) > 0)
183 				continue;
184 			f << stringf("\txorshift128;\n");
185 			f << stringf("\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2);
186 		}
187 		f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
188 		f << stringf("end\n");
189 		f << stringf("endtask\n\n");
190 
191 		f << stringf("task %s;\n", idy(mod->name.str(), "update_clock").c_str());
192 		f << stringf("begin\n");
193 		if (signal_clk.size()) {
194 			f << stringf("\txorshift128;\n");
195 			f << stringf("\t{");
196 			int total_clock_bits = 0;
197 			for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
198 				f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
199 				total_clock_bits += it->second;
200 			}
201 			f << stringf(" } = {");
202 			for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
203 				f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
204 			f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1);
205 		}
206 		f << stringf("end\n");
207 		f << stringf("endtask\n\n");
208 
209 		char shorthand = 'A';
210 		std::vector<std::string> header1;
211 		std::string header2 = "";
212 
213 		f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str());
214 		f << stringf("begin\n");
215 		f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {");
216 		if (signal_in.size())
217 			for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
218 				f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
219 				int len = it->second;
220 				header2 += ", \"";
221 				if (len > 1)
222 					header2 += "/", len--;
223 				while (len > 1)
224 					header2 += "-", len--;
225 				if (len > 0)
226 					header2 += shorthand, len--;
227 				header2 += "\"";
228 				header1.push_back("    " + it->first);
229 				header1.back()[0] = shorthand;
230 				shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
231 			}
232 		else {
233 			f << stringf(" 1'bx");
234 			header2 += ", \"#\"";
235 		}
236 		f << stringf(" }, {");
237 		header2 += ", \" \"";
238 		if (signal_clk.size()) {
239 			for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
240 				f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
241 				int len = it->second;
242 				header2 += ", \"";
243 				if (len > 1)
244 					header2 += "/", len--;
245 				while (len > 1)
246 					header2 += "-", len--;
247 				if (len > 0)
248 					header2 += shorthand, len--;
249 				header2 += "\"";
250 				header1.push_back("    " + it->first);
251 				header1.back()[0] = shorthand;
252 				shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
253 			}
254 		} else {
255 			f << stringf(" 1'bx");
256 			header2 += ", \"#\"";
257 		}
258 		f << stringf(" }, {");
259 		header2 += ", \" \"";
260 		if (signal_out.size()) {
261 			for (auto it = signal_out.begin(); it != signal_out.end(); it++) {
262 				f << stringf("%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str());
263 				int len = it->second;
264 				header2 += ", \"";
265 				if (len > 1)
266 					header2 += "/", len--;
267 				while (len > 1)
268 					header2 += "-", len--;
269 				if (len > 0)
270 					header2 += shorthand, len--;
271 				header2 += "\"";
272 				header1.push_back("    " + it->first);
273 				header1.back()[0] = shorthand;
274 				shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
275 			}
276 		} else {
277 			f << stringf(" 1'bx");
278 			header2 += ", \"#\"";
279 		}
280 		f << stringf(" }, $time, i);\n");
281 		f << stringf("end\n");
282 		f << stringf("endtask\n\n");
283 
284 		f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str());
285 		f << stringf("begin\n");
286 		f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
287 		for (auto &hdr : header1)
288 			f << stringf("\t$fdisplay(file, \"#OUT#   %s\");\n", hdr.c_str());
289 		f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
290 		f << stringf("\t$fdisplay(file, {\"#OUT# \"%s});\n", header2.c_str());
291 		f << stringf("end\n");
292 		f << stringf("endtask\n\n");
293 
294 		f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str());
295 		f << stringf("begin\n");
296 		f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str());
297 		f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str());
298 		f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter);
299 		f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str());
300 		f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_data").c_str());
301 		f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_clock").c_str());
302 		f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "print_status").c_str());
303 		f << stringf("\tend\n");
304 		f << stringf("end\n");
305 		f << stringf("endtask\n\n");
306 	}
307 
308 	f << stringf("initial begin\n");
309 	f << stringf("\tif ($value$plusargs(\"VCD=%%s\", filename)) begin\n");
310 	f << stringf("\t\t$dumpfile(filename);\n");
311 	f << stringf("\t\t$dumpvars(0, testbench);\n");
312 	f << stringf("\tend\n");
313 	f << stringf("\tif ($value$plusargs(\"OUT=%%s\", filename)) begin\n");
314 	f << stringf("\t\tfile = $fopen(filename);\n");
315 	f << stringf("\tend else begin\n");
316 	f << stringf("\t\tfile = $fopen(`outfile);\n");
317 	f << stringf("\tend\n");
318 	for (auto module : design->modules())
319 		if (!module->get_bool_attribute(ID::gentb_skip))
320 			f << stringf("\t%s;\n", idy(module->name.str(), "test").c_str());
321 	f << stringf("\t$fclose(file);\n");
322 	f << stringf("\t$finish;\n");
323 	f << stringf("end\n\n");
324 
325 	f << stringf("endmodule\n");
326 }
327 
328 struct TestAutotbBackend : public Backend {
TestAutotbBackendTestAutotbBackend329 	TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { }
helpTestAutotbBackend330 	void help() override
331 	{
332 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
333 		log("\n");
334 		log("    test_autotb [options] [filename]\n");
335 		log("\n");
336 		log("Automatically create primitive Verilog test benches for all modules in the\n");
337 		log("design. The generated testbenches toggle the input pins of the module in\n");
338 		log("a semi-random manner and dumps the resulting output signals.\n");
339 		log("\n");
340 		log("This can be used to check the synthesis results for simple circuits by\n");
341 		log("comparing the testbench output for the input files and the synthesis results.\n");
342 		log("\n");
343 		log("The backend automatically detects clock signals. Additionally a signal can\n");
344 		log("be forced to be interpreted as clock signal by setting the attribute\n");
345 		log("'gentb_clock' on the signal.\n");
346 		log("\n");
347 		log("The attribute 'gentb_constant' can be used to force a signal to a constant\n");
348 		log("value after initialization. This can e.g. be used to force a reset signal\n");
349 		log("low in order to explore more inner states in a state machine.\n");
350 		log("\n");
351 		log("The attribute 'gentb_skip' can be attached to modules to suppress testbench\n");
352 		log("generation.\n");
353 		log("\n");
354 		log("    -n <int>\n");
355 		log("        number of iterations the test bench should run (default = 1000)\n");
356 		log("\n");
357 		log("    -seed <int>\n");
358 		log("        seed used for pseudo-random number generation (default = 0).\n");
359 		log("        a value of 0 will cause an arbitrary seed to be chosen, based on\n");
360 		log("        the current system time.\n");
361 		log("\n");
362 	}
executeTestAutotbBackend363 	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
364 	{
365 		int num_iter = 1000;
366 		int seed = 0;
367 
368 		log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
369 
370 		int argidx;
371 		for (argidx = 1; argidx < GetSize(args); argidx++)
372 		{
373 			if (args[argidx] == "-n" && argidx+1 < GetSize(args)) {
374 				num_iter = atoi(args[++argidx].c_str());
375 				continue;
376 			}
377 			if (args[argidx] == "-seed" && argidx+1 < GetSize(args)) {
378 				seed = atoi(args[++argidx].c_str());
379 				continue;
380 			}
381 			break;
382 		}
383 
384 		extra_args(f, filename, args, argidx);
385 		autotest(*f, design, num_iter, seed);
386 	}
387 } TestAutotbBackend;
388 
389 PRIVATE_NAMESPACE_END
390 
391