2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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.
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.
20 #include "kernel/yosys.h"
26 PRIVATE_NAMESPACE_BEGIN
28 static std::string
id(std::string internal_id
)
30 const char *str
= internal_id
.c_str();
31 bool do_escape
= false;
36 if ('0' <= *str
&& *str
<= '9')
39 for (int i
= 0; str
[i
]; i
++) {
40 if ('0' <= str
[i
] && str
[i
] <= '9')
42 if ('a' <= str
[i
] && str
[i
] <= 'z')
44 if ('A' <= str
[i
] && str
[i
] <= 'Z')
53 return "\\" + std::string(str
) + " ";
54 return std::string(str
);
57 static std::string
idx(std::string str
)
64 static std::string
idy(std::string str1
, std::string str2
= std::string(), std::string str3
= std::string())
68 str1
+= "_" + idx(str2
);
70 str1
+= "_" + idx(str3
);
74 static void autotest(std::ostream
&f
, RTLIL::Design
*design
, int num_iter
, int seed
)
76 f
<< stringf("`ifndef outfile\n");
77 f
<< stringf("\t`define outfile \"/dev/stdout\"\n");
78 f
<< stringf("`endif\n");
80 f
<< stringf("module testbench;\n\n");
82 f
<< stringf("integer i;\n");
83 f
<< stringf("integer file;\n\n");
84 f
<< stringf("reg [1023:0] filename;\n\n");
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");
101 for (auto mod
: design
->modules())
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
;
108 if (mod
->get_bool_attribute(ID::gentb_skip
))
112 log("Generating test bench for module `%s'.\n", mod
->name
.c_str());
113 for (auto wire
: mod
->wires()) {
114 if (wire
->port_output
) {
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
) {
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
)
125 RTLIL::SigSpec
&signal
= (*it4
)->signal
;
126 for (auto &c
: signal
.chunks())
130 if (is_clksignal
&& wire
->attributes
.count(ID::gentb_constant
) == 0) {
131 signal_clk
[idy("sig", mod
->name
.str(), wire
->name
.str())] = wire
->width
;
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();
137 f
<< stringf("reg [%d:0] %s;\n", wire
->width
-1, idy("sig", mod
->name
.str(), wire
->name
.str()).c_str());
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
? "," : "");
146 f
<< stringf(");\n\n");
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());
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());
169 for (auto it
= signal_in
.begin(); it
!= signal_in
.end(); ++it
) {
170 if (signal_const
.count(it
->first
) == 0)
172 f
<< stringf("\t%s <= #%d 'b%s;\n", it
->first
.c_str(), ++delay_counter
*2, signal_const
[it
->first
].c_str());
174 f
<< stringf("\t#%d;\n", ((2*delay_counter
+99)/100)*100);
175 f
<< stringf("end\n");
176 f
<< stringf("endtask\n\n");
178 f
<< stringf("task %s;\n", idy(mod
->name
.str(), "update_data").c_str());
179 f
<< stringf("begin\n");
181 for (auto it
= signal_in
.begin(); it
!= signal_in
.end(); it
++) {
182 if (signal_const
.count(it
->first
) > 0)
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);
187 f
<< stringf("\t#%d;\n", ((2*delay_counter
+99)/100)*100);
188 f
<< stringf("end\n");
189 f
<< stringf("endtask\n\n");
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");
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
;
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);
206 f
<< stringf("end\n");
207 f
<< stringf("endtask\n\n");
209 char shorthand
= 'A';
210 std::vector
<std::string
> header1
;
211 std::string header2
= "";
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
;
222 header2
+= "/", len
--;
224 header2
+= "-", len
--;
226 header2
+= shorthand
, len
--;
228 header1
.push_back(" " + it
->first
);
229 header1
.back()[0] = shorthand
;
230 shorthand
= shorthand
== 'Z' ? 'A' : shorthand
+1;
233 f
<< stringf(" 1'bx");
234 header2
+= ", \"#\"";
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
;
244 header2
+= "/", len
--;
246 header2
+= "-", len
--;
248 header2
+= shorthand
, len
--;
250 header1
.push_back(" " + it
->first
);
251 header1
.back()[0] = shorthand
;
252 shorthand
= shorthand
== 'Z' ? 'A' : shorthand
+1;
255 f
<< stringf(" 1'bx");
256 header2
+= ", \"#\"";
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
;
266 header2
+= "/", len
--;
268 header2
+= "-", len
--;
270 header2
+= shorthand
, len
--;
272 header1
.push_back(" " + it
->first
);
273 header1
.back()[0] = shorthand
;
274 shorthand
= shorthand
== 'Z' ? 'A' : shorthand
+1;
277 f
<< stringf(" 1'bx");
278 header2
+= ", \"#\"";
280 f
<< stringf(" }, $time, i);\n");
281 f
<< stringf("end\n");
282 f
<< stringf("endtask\n\n");
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");
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");
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");
325 f
<< stringf("endmodule\n");
328 struct TestAutotbBackend
: public Backend
{
329 TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { }
332 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
334 log(" test_autotb [options] [filename]\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");
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");
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");
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");
351 log("The attribute 'gentb_skip' can be attached to modules to suppress testbench\n");
352 log("generation.\n");
355 log(" number of iterations the test bench should run (default = 1000)\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");
363 void execute(std::ostream
*&f
, std::string filename
, std::vector
<std::string
> args
, RTLIL::Design
*design
) override
368 log_header(design
, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
371 for (argidx
= 1; argidx
< GetSize(args
); argidx
++)
373 if (args
[argidx
] == "-n" && argidx
+1 < GetSize(args
)) {
374 num_iter
= atoi(args
[++argidx
].c_str());
377 if (args
[argidx
] == "-seed" && argidx
+1 < GetSize(args
)) {
378 seed
= atoi(args
[++argidx
].c_str());
384 extra_args(f
, filename
, args
, argidx
);
385 autotest(*f
, design
, num_iter
, seed
);
389 PRIVATE_NAMESPACE_END