yosys-master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
test_autotb.cc
Go to the documentation of this file.
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 
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)
75 {
76  f << stringf("module testbench;\n\n");
77 
78  f << stringf("integer i;\n\n");
79 
80  f << stringf("reg [31:0] xorshift128_x = 123456789;\n");
81  f << stringf("reg [31:0] xorshift128_y = 362436069;\n");
82  f << stringf("reg [31:0] xorshift128_z = 521288629;\n");
83  f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", int(time(NULL)));
84  f << stringf("reg [31:0] xorshift128_t;\n\n");
85  f << stringf("task xorshift128;\n");
86  f << stringf("begin\n");
87  f << stringf("\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n");
88  f << stringf("\txorshift128_x = xorshift128_y;\n");
89  f << stringf("\txorshift128_y = xorshift128_z;\n");
90  f << stringf("\txorshift128_z = xorshift128_w;\n");
91  f << stringf("\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n");
92  f << stringf("end\n");
93  f << stringf("endtask\n\n");
94 
95  for (auto it = design->modules_.begin(); it != design->modules_.end(); it++)
96  {
97  std::map<std::string, int> signal_in;
98  std::map<std::string, std::string> signal_const;
99  std::map<std::string, int> signal_clk;
100  std::map<std::string, int> signal_out;
101 
102  RTLIL::Module *mod = it->second;
103 
104  if (mod->get_bool_attribute("\\gentb_skip"))
105  continue;
106 
107  int count_ports = 0;
108  log("Generating test bench for module `%s'.\n", it->first.c_str());
109  for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); it2++) {
110  RTLIL::Wire *wire = it2->second;
111  if (wire->port_output) {
112  count_ports++;
113  signal_out[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
114  f << stringf("wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
115  } else if (wire->port_input) {
116  count_ports++;
117  bool is_clksignal = wire->get_bool_attribute("\\gentb_clock");
118  for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); it3++)
119  for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); it4++) {
120  if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1)
121  continue;
122  RTLIL::SigSpec &signal = (*it4)->signal;
123  for (auto &c : signal.chunks())
124  if (c.wire == wire)
125  is_clksignal = true;
126  }
127  if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) {
128  signal_clk[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
129  } else {
130  signal_in[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
131  if (wire->attributes.count("\\gentb_constant") != 0)
132  signal_const[idy("sig", mod->name.str(), wire->name.str())] = wire->attributes["\\gentb_constant"].as_string();
133  }
134  f << stringf("reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
135  }
136  }
137  f << stringf("%s %s(\n", id(mod->name.str()).c_str(), idy("uut", mod->name.str()).c_str());
138  for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); it2++) {
139  RTLIL::Wire *wire = it2->second;
140  if (wire->port_output || wire->port_input)
141  f << stringf("\t.%s(%s)%s\n", id(wire->name.str()).c_str(),
142  idy("sig", mod->name.str(), wire->name.str()).c_str(), --count_ports ? "," : "");
143  }
144  f << stringf(");\n\n");
145 
146  f << stringf("task %s;\n", idy(mod->name.str(), "reset").c_str());
147  f << stringf("begin\n");
148  int delay_counter = 0;
149  for (auto it = signal_in.begin(); it != signal_in.end(); it++)
150  f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
151  for (auto it = signal_clk.begin(); it != signal_clk.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#100; %s <= 1;\n", it->first.c_str());
155  f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
156  }
157  delay_counter = 0;
158  for (auto it = signal_in.begin(); it != signal_in.end(); it++)
159  f << stringf("\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2);
160  for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
161  f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
162  f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
163  }
164  delay_counter = 0;
165  for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
166  if (signal_const.count(it->first) == 0)
167  continue;
168  f << stringf("\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str());
169  }
170  f << stringf("end\n");
171  f << stringf("endtask\n\n");
172 
173  f << stringf("task %s;\n", idy(mod->name.str(), "update_data").c_str());
174  f << stringf("begin\n");
175  delay_counter = 0;
176  for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
177  if (signal_const.count(it->first) > 0)
178  continue;
179  f << stringf("\txorshift128;\n");
180  f << stringf("\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2);
181  }
182  f << stringf("end\n");
183  f << stringf("endtask\n\n");
184 
185  f << stringf("task %s;\n", idy(mod->name.str(), "update_clock").c_str());
186  f << stringf("begin\n");
187  if (signal_clk.size()) {
188  f << stringf("\txorshift128;\n");
189  f << stringf("\t{");
190  int total_clock_bits = 0;
191  for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
192  f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
193  total_clock_bits += it->second;
194  }
195  f << stringf(" } = {");
196  for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
197  f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
198  f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits);
199  }
200  f << stringf("end\n");
201  f << stringf("endtask\n\n");
202 
203  char shorthand = 'A';
204  std::vector<std::string> header1;
205  std::string header2 = "";
206 
207  f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str());
208  f << stringf("begin\n");
209  f << stringf("\t$display(\"#OUT# %%b %%b %%b %%t %%d\", {");
210  if (signal_in.size())
211  for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
212  f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
213  int len = it->second;
214  if (len > 1)
215  header2 += "/", len--;
216  while (len > 1)
217  header2 += "-", len--;
218  if (len > 0)
219  header2 += shorthand, len--;
220  header1.push_back(" " + it->first);
221  header1.back()[0] = shorthand++;
222  }
223  else {
224  f << stringf(" 1'bx");
225  header2 += "#";
226  }
227  f << stringf(" }, {");
228  header2 += " ";
229  if (signal_clk.size()) {
230  for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
231  f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
232  int len = it->second;
233  if (len > 1)
234  header2 += "/", len--;
235  while (len > 1)
236  header2 += "-", len--;
237  if (len > 0)
238  header2 += shorthand, len--;
239  header1.push_back(" " + it->first);
240  header1.back()[0] = shorthand++;
241  }
242  } else {
243  f << stringf(" 1'bx");
244  header2 += "#";
245  }
246  f << stringf(" }, {");
247  header2 += " ";
248  if (signal_out.size()) {
249  for (auto it = signal_out.begin(); it != signal_out.end(); it++) {
250  f << stringf("%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str());
251  int len = it->second;
252  if (len > 1)
253  header2 += "/", len--;
254  while (len > 1)
255  header2 += "-", len--;
256  if (len > 0)
257  header2 += shorthand, len--;
258  header1.push_back(" " + it->first);
259  header1.back()[0] = shorthand++;
260  }
261  } else {
262  f << stringf(" 1'bx");
263  header2 += "#";
264  }
265  f << stringf(" }, $time, i);\n");
266  f << stringf("end\n");
267  f << stringf("endtask\n\n");
268 
269  f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str());
270  f << stringf("begin\n");
271  f << stringf("\t$display(\"#OUT#\");\n");
272  for (auto &hdr : header1)
273  f << stringf("\t$display(\"#OUT# %s\");\n", hdr.c_str());
274  f << stringf("\t$display(\"#OUT#\");\n");
275  f << stringf("\t$display(\"#OUT# %s\");\n", header2.c_str());
276  f << stringf("end\n");
277  f << stringf("endtask\n\n");
278 
279  f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str());
280  f << stringf("begin\n");
281  f << stringf("\t$display(\"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str());
282  f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str());
283  f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter);
284  f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str());
285  f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_data").c_str());
286  f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_clock").c_str());
287  f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "print_status").c_str());
288  f << stringf("\tend\n");
289  f << stringf("end\n");
290  f << stringf("endtask\n\n");
291  }
292 
293  f << stringf("initial begin\n");
294  f << stringf("\t// $dumpfile(\"testbench.vcd\");\n");
295  f << stringf("\t// $dumpvars(0, testbench);\n");
296  for (auto it = design->modules_.begin(); it != design->modules_.end(); it++)
297  if (!it->second->get_bool_attribute("\\gentb_skip"))
298  f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str());
299  f << stringf("\t$finish;\n");
300  f << stringf("end\n\n");
301 
302  f << stringf("endmodule\n");
303 }
304 
305 struct TestAutotbBackend : public Backend {
306  TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { }
307  virtual void help()
308  {
309  // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
310  log("\n");
311  log(" test_autotb [options] [filename]\n");
312  log("\n");
313  log("Automatically create primitive verilog test benches for all modules in the\n");
314  log("design. The generated testbenches toggle the input pins of the module in\n");
315  log("a semi-random manner and dumps the resulting output signals.\n");
316  log("\n");
317  log("This can be used to check the synthesis results for simple circuits by\n");
318  log("comparing the testbench output for the input files and the synthesis results.\n");
319  log("\n");
320  log("The backend automatically detects clock signals. Additionally a signal can\n");
321  log("be forced to be interpreted as clock signal by setting the attribute\n");
322  log("'gentb_clock' on the signal.\n");
323  log("\n");
324  log("The attribute 'gentb_constant' can be used to force a signal to a constant\n");
325  log("value after initialization. This can e.g. be used to force a reset signal\n");
326  log("low in order to explore more inner states in a state machine.\n");
327  log("\n");
328  log(" -n <int>\n");
329  log(" number of iterations the test bench shuld run (default = 1000)\n");
330  log("\n");
331  }
332  virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
333  {
334  int num_iter = 1000;
335 
336  log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
337 
338  int argidx;
339  for (argidx = 1; argidx < GetSize(args); argidx++)
340  {
341  if (args[argidx] == "-n" && argidx+1 < GetSize(args)) {
342  num_iter = atoi(args[++argidx].c_str());
343  continue;
344  }
345  break;
346  }
347 
348  extra_args(f, filename, args, argidx);
349  autotest(*f, design, num_iter);
350  }
352 
354 
std::string str() const
Definition: rtlil.h:182
std::string stringf(const char *fmt,...)
Definition: yosys.cc:58
void log_header(const char *format,...)
Definition: log.cc:188
virtual void execute(std::ostream *&f, std::string filename, std::vector< std::string > args, RTLIL::Design *design)
Definition: test_autotb.cc:332
std::map< RTLIL::IdString, RTLIL::Wire * > wires_
Definition: rtlil.h:595
static std::string idx(std::string str)
Definition: test_autotb.cc:57
bool port_input
Definition: rtlil.h:827
int width
Definition: rtlil.h:826
if(!(yy_init))
Definition: ilang_lexer.cc:846
bool port_output
Definition: rtlil.h:827
void extra_args(std::ostream *&f, std::string &filename, std::vector< std::string > args, size_t argidx)
Definition: register.cc:439
#define PRIVATE_NAMESPACE_BEGIN
Definition: yosys.h:97
int GetSize(RTLIL::Wire *wire)
Definition: yosys.cc:334
RTLIL::IdString name
Definition: rtlil.h:599
#define PRIVATE_NAMESPACE_END
Definition: yosys.h:98
RTLIL::IdString name
Definition: rtlil.h:825
virtual void help()
Definition: test_autotb.cc:307
std::map< RTLIL::IdString, RTLIL::Process * > processes
Definition: rtlil.h:602
#define USING_YOSYS_NAMESPACE
Definition: yosys.h:102
std::map< RTLIL::IdString, RTLIL::Module * > modules_
Definition: rtlil.h:507
#define NULL
void log(const char *format,...)
Definition: log.cc:180
static std::string idy(std::string str1, std::string str2=std::string(), std::string str3=std::string())
Definition: test_autotb.cc:64
USING_YOSYS_NAMESPACE static PRIVATE_NAMESPACE_BEGIN std::string id(std::string internal_id)
Definition: test_autotb.cc:28
static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
Definition: test_autotb.cc:74
TestAutotbBackend TestAutotbBackend
const std::vector< RTLIL::SigChunk > & chunks() const
Definition: rtlil.h:1016