2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
5 * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include "kernel/yosys.h"
22 #include "kernel/satgen.h"
23 #include "kernel/consteval.h"
26 static uint32_t xorshift32_state
= 123456789;
28 static uint32_t xorshift32(uint32_t limit
) {
29 xorshift32_state
^= xorshift32_state
<< 13;
30 xorshift32_state
^= xorshift32_state
>> 17;
31 xorshift32_state
^= xorshift32_state
<< 5;
32 return xorshift32_state
% limit
;
35 static void create_gold_module(RTLIL::Design
*design
, RTLIL::IdString cell_type
, std::string cell_type_flags
)
37 RTLIL::Module
*module
= design
->addModule("\\gold");
38 RTLIL::Cell
*cell
= module
->addCell("\\UUT", cell_type
);
41 if (cell_type
== "$lut")
43 int width
= 1 + xorshift32(6);
45 wire
= module
->addWire("\\A");
47 wire
->port_input
= true;
48 cell
->setPort("\\A", wire
);
50 wire
= module
->addWire("\\Y");
51 wire
->port_output
= true;
52 cell
->setPort("\\Y", wire
);
54 RTLIL::SigSpec config
;
55 for (int i
= 0; i
< (1 << width
); i
++)
56 config
.append(xorshift32(2) ? RTLIL::S1
: RTLIL::S0
);
58 cell
->setParam("\\LUT", config
.as_const());
61 if (cell_type_flags
.find('A') != std::string::npos
) {
62 wire
= module
->addWire("\\A");
63 wire
->width
= 1 + xorshift32(8);
64 wire
->port_input
= true;
65 cell
->setPort("\\A", wire
);
68 if (cell_type_flags
.find('B') != std::string::npos
) {
69 wire
= module
->addWire("\\B");
70 if (cell_type_flags
.find('h') != std::string::npos
)
71 wire
->width
= 1 + xorshift32(6);
73 wire
->width
= 1 + xorshift32(8);
74 wire
->port_input
= true;
75 cell
->setPort("\\B", wire
);
78 if (cell_type_flags
.find('S') != std::string::npos
&& xorshift32(2)) {
79 if (cell_type_flags
.find('A') != std::string::npos
)
80 cell
->parameters
["\\A_SIGNED"] = true;
81 if (cell_type_flags
.find('B') != std::string::npos
)
82 cell
->parameters
["\\B_SIGNED"] = true;
85 if (cell_type_flags
.find('s') != std::string::npos
) {
86 if (cell_type_flags
.find('A') != std::string::npos
&& xorshift32(2))
87 cell
->parameters
["\\A_SIGNED"] = true;
88 if (cell_type_flags
.find('B') != std::string::npos
&& xorshift32(2))
89 cell
->parameters
["\\B_SIGNED"] = true;
92 if (cell_type_flags
.find('Y') != std::string::npos
) {
93 wire
= module
->addWire("\\Y");
94 wire
->width
= 1 + xorshift32(8);
95 wire
->port_output
= true;
96 cell
->setPort("\\Y", wire
);
99 if (cell_type
== "$alu")
101 wire
= module
->addWire("\\CI");
102 wire
->port_input
= true;
103 cell
->setPort("\\CI", wire
);
105 wire
= module
->addWire("\\BI");
106 wire
->port_input
= true;
107 cell
->setPort("\\BI", wire
);
109 wire
= module
->addWire("\\X");
110 wire
->width
= SIZE(cell
->getPort("\\Y"));
111 wire
->port_output
= true;
112 cell
->setPort("\\X", wire
);
114 wire
= module
->addWire("\\CO");
115 wire
->width
= SIZE(cell
->getPort("\\Y"));
116 wire
->port_output
= true;
117 cell
->setPort("\\CO", wire
);
120 module
->fixup_ports();
121 cell
->fixup_parameters();
125 static void run_eval_test(RTLIL::Design
*design
, bool verbose
, std::string uut_name
, std::ofstream
&vlog_file
)
127 log("Eval testing:%c", verbose
? '\n' : ' ');
129 RTLIL::Module
*gold_mod
= design
->module("\\gold");
130 RTLIL::Module
*gate_mod
= design
->module("\\gate");
131 ConstEval
gold_ce(gold_mod
), gate_ce(gate_mod
);
133 ezDefaultSAT ez1
, ez2
;
134 SigMap
sigmap(gold_mod
);
135 SatGen
satgen1(&ez1
, &sigmap
);
136 SatGen
satgen2(&ez2
, &sigmap
);
137 satgen2
.model_undef
= true;
139 for (auto cell
: gold_mod
->cells()) {
140 satgen1
.importCell(cell
);
141 satgen2
.importCell(cell
);
144 if (vlog_file
.is_open())
146 vlog_file
<< stringf("\nmodule %s;\n", uut_name
.c_str());
148 for (auto port
: gold_mod
->ports
) {
149 RTLIL::Wire
*wire
= gold_mod
->wire(port
);
150 if (wire
->port_input
)
151 vlog_file
<< stringf(" reg [%d:0] %s;\n", SIZE(wire
)-1, log_id(wire
));
153 vlog_file
<< stringf(" wire [%d:0] %s_expr, %s_noexpr;\n", SIZE(wire
)-1, log_id(wire
), log_id(wire
));
156 vlog_file
<< stringf(" %s_expr uut_expr(", uut_name
.c_str());
157 for (int i
= 0; i
< SIZE(gold_mod
->ports
); i
++)
158 vlog_file
<< stringf("%s.%s(%s%s)", i
? ", " : "", log_id(gold_mod
->ports
[i
]), log_id(gold_mod
->ports
[i
]),
159 gold_mod
->wire(gold_mod
->ports
[i
])->port_input
? "" : "_expr");
160 vlog_file
<< stringf(");\n");
162 vlog_file
<< stringf(" %s_expr uut_noexpr(", uut_name
.c_str());
163 for (int i
= 0; i
< SIZE(gold_mod
->ports
); i
++)
164 vlog_file
<< stringf("%s.%s(%s%s)", i
? ", " : "", log_id(gold_mod
->ports
[i
]), log_id(gold_mod
->ports
[i
]),
165 gold_mod
->wire(gold_mod
->ports
[i
])->port_input
? "" : "_noexpr");
166 vlog_file
<< stringf(");\n");
168 vlog_file
<< stringf(" task run;\n");
169 vlog_file
<< stringf(" begin\n");
170 vlog_file
<< stringf(" $display(\"%s\");\n", uut_name
.c_str());
173 for (int i
= 0; i
< 64; i
++)
175 log(verbose
? "\n" : ".");
179 RTLIL::SigSpec in_sig
, in_val
;
180 RTLIL::SigSpec out_sig
, out_val
;
181 std::string vlog_pattern_info
;
183 for (auto port
: gold_mod
->ports
)
185 RTLIL::Wire
*gold_wire
= gold_mod
->wire(port
);
186 RTLIL::Wire
*gate_wire
= gate_mod
->wire(port
);
188 log_assert(gold_wire
!= nullptr);
189 log_assert(gate_wire
!= nullptr);
190 log_assert(gold_wire
->port_input
== gate_wire
->port_input
);
191 log_assert(SIZE(gold_wire
) == SIZE(gate_wire
));
193 if (!gold_wire
->port_input
)
196 RTLIL::Const in_value
;
197 for (int i
= 0; i
< SIZE(gold_wire
); i
++)
198 in_value
.bits
.push_back(xorshift32(2) ? RTLIL::S1
: RTLIL::S0
);
200 if (xorshift32(4) == 0) {
201 int inv_chance
= 1 + xorshift32(8);
202 for (int i
= 0; i
< SIZE(gold_wire
); i
++)
203 if (xorshift32(inv_chance
) == 0)
204 in_value
.bits
[i
] = RTLIL::Sx
;
208 log("%s: %s\n", log_id(gold_wire
), log_signal(in_value
));
210 in_sig
.append(gold_wire
);
211 in_val
.append(in_value
);
213 gold_ce
.set(gold_wire
, in_value
);
214 gate_ce
.set(gate_wire
, in_value
);
216 if (vlog_file
.is_open()) {
217 vlog_file
<< stringf(" %s = 'b%s;\n", log_id(gold_wire
), in_value
.as_string().c_str());
218 if (!vlog_pattern_info
.empty())
219 vlog_pattern_info
+= " ";
220 vlog_pattern_info
+= stringf("%s=%s", log_id(gold_wire
), log_signal(in_value
));
224 if (vlog_file
.is_open())
225 vlog_file
<< stringf(" #1;\n");
227 for (auto port
: gold_mod
->ports
)
229 RTLIL::Wire
*gold_wire
= gold_mod
->wire(port
);
230 RTLIL::Wire
*gate_wire
= gate_mod
->wire(port
);
232 log_assert(gold_wire
!= nullptr);
233 log_assert(gate_wire
!= nullptr);
234 log_assert(gold_wire
->port_output
== gate_wire
->port_output
);
235 log_assert(SIZE(gold_wire
) == SIZE(gate_wire
));
237 if (!gold_wire
->port_output
)
240 RTLIL::SigSpec
gold_outval(gold_wire
);
241 RTLIL::SigSpec
gate_outval(gate_wire
);
243 if (!gold_ce
.eval(gold_outval
))
244 log_error("Failed to eval %s in gold module.\n", log_id(gold_wire
));
246 if (!gate_ce
.eval(gate_outval
))
247 log_error("Failed to eval %s in gate module.\n", log_id(gate_wire
));
249 bool gold_gate_mismatch
= false;
250 for (int i
= 0; i
< SIZE(gold_wire
); i
++) {
251 if (gold_outval
[i
] == RTLIL::Sx
)
253 if (gold_outval
[i
] == gate_outval
[i
])
255 gold_gate_mismatch
= true;
259 if (gold_gate_mismatch
)
260 log_error("Mismatch in output %s: gold:%s != gate:%s\n", log_id(gate_wire
), log_signal(gold_outval
), log_signal(gate_outval
));
263 log("%s: %s\n", log_id(gold_wire
), log_signal(gold_outval
));
265 out_sig
.append(gold_wire
);
266 out_val
.append(gold_outval
);
268 if (vlog_file
.is_open()) {
269 vlog_file
<< stringf(" $display(\"[%s] %s expected: %%b, expr: %%b, noexpr: %%b\", %d'b%s, %s_expr, %s_noexpr);\n",
270 vlog_pattern_info
.c_str(), log_id(gold_wire
), SIZE(gold_outval
), gold_outval
.as_string().c_str(), log_id(gold_wire
), log_id(gold_wire
));
271 vlog_file
<< stringf(" if (%s_expr !== %d'b%s) begin $display(\"ERROR\"); $finish; end\n", log_id(gold_wire
), SIZE(gold_outval
), gold_outval
.as_string().c_str());
272 vlog_file
<< stringf(" if (%s_noexpr !== %d'b%s) begin $display(\"ERROR\"); $finish; end\n", log_id(gold_wire
), SIZE(gold_outval
), gold_outval
.as_string().c_str());
277 log("EVAL: %s\n", out_val
.as_string().c_str());
279 std::vector
<int> sat1_in_sig
= satgen1
.importSigSpec(in_sig
);
280 std::vector
<int> sat1_in_val
= satgen1
.importSigSpec(in_val
);
282 std::vector
<int> sat1_model
= satgen1
.importSigSpec(out_sig
);
283 std::vector
<bool> sat1_model_value
;
285 if (!ez1
.solve(sat1_model
, sat1_model_value
, ez1
.vec_eq(sat1_in_sig
, sat1_in_val
)))
286 log_error("Evaluating sat model 1 (no undef modeling) failed!\n");
290 for (int i
= SIZE(out_sig
)-1; i
>= 0; i
--)
291 log("%c", sat1_model_value
.at(i
) ? '1' : '0');
295 for (int i
= 0; i
< SIZE(out_sig
); i
++) {
296 if (out_val
[i
] != RTLIL::S0
&& out_val
[i
] != RTLIL::S1
)
298 if (out_val
[i
] == RTLIL::S0
&& sat1_model_value
.at(i
) == false)
300 if (out_val
[i
] == RTLIL::S1
&& sat1_model_value
.at(i
) == true)
302 log_error("Mismatch in sat model 1 (no undef modeling) output!\n");
305 std::vector
<int> sat2_in_def_sig
= satgen2
.importDefSigSpec(in_sig
);
306 std::vector
<int> sat2_in_def_val
= satgen2
.importDefSigSpec(in_val
);
308 std::vector
<int> sat2_in_undef_sig
= satgen2
.importUndefSigSpec(in_sig
);
309 std::vector
<int> sat2_in_undef_val
= satgen2
.importUndefSigSpec(in_val
);
311 std::vector
<int> sat2_model_def_sig
= satgen2
.importDefSigSpec(out_sig
);
312 std::vector
<int> sat2_model_undef_sig
= satgen2
.importUndefSigSpec(out_sig
);
314 std::vector
<int> sat2_model
;
315 sat2_model
.insert(sat2_model
.end(), sat2_model_def_sig
.begin(), sat2_model_def_sig
.end());
316 sat2_model
.insert(sat2_model
.end(), sat2_model_undef_sig
.begin(), sat2_model_undef_sig
.end());
318 std::vector
<bool> sat2_model_value
;
320 if (!ez2
.solve(sat2_model
, sat2_model_value
, ez2
.vec_eq(sat2_in_def_sig
, sat2_in_def_val
), ez2
.vec_eq(sat2_in_undef_sig
, sat2_in_undef_val
)))
321 log_error("Evaluating sat model 2 (undef modeling) failed!\n");
325 for (int i
= SIZE(out_sig
)-1; i
>= 0; i
--)
326 log("%c", sat2_model_value
.at(SIZE(out_sig
) + i
) ? 'x' : sat2_model_value
.at(i
) ? '1' : '0');
330 for (int i
= 0; i
< SIZE(out_sig
); i
++) {
331 if (sat2_model_value
.at(SIZE(out_sig
) + i
)) {
332 if (out_val
[i
] != RTLIL::S0
&& out_val
[i
] != RTLIL::S1
)
335 if (out_val
[i
] == RTLIL::S0
&& sat2_model_value
.at(i
) == false)
337 if (out_val
[i
] == RTLIL::S1
&& sat2_model_value
.at(i
) == true)
340 log_error("Mismatch in sat model 2 (undef modeling) output!\n");
344 if (vlog_file
.is_open()) {
345 vlog_file
<< stringf(" end\n");
346 vlog_file
<< stringf(" endtask\n");
347 vlog_file
<< stringf("endmodule\n");
354 struct TestCellPass
: public Pass
{
355 TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { }
358 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
360 log(" test_cell [options] {cell-types}\n");
362 log("Tests the internal implementation of the given cell type (for example '$mux')\n");
363 log("by comparing SAT solver, EVAL and TECHMAP implementations of the cell types..\n");
365 log("Run with 'all' instead of a cell type to run the test on all supported\n");
366 log("cell types.\n");
368 log(" -n {integer}\n");
369 log(" create this number of cell instances and test them (default = 100).\n");
371 log(" -s {positive_integer}\n");
372 log(" use this value as rng seed value (default = unix time).\n");
374 log(" -f {ilang_file}\n");
375 log(" don't generate circuits. instead load the specified ilang file.\n");
377 log(" -map {filename}\n");
378 log(" pass this option to techmap.\n");
381 log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n");
384 log(" print additional debug information to the console\n");
386 log(" -vlog {filename}\n");
387 log(" create a verilog test bench to test simlib and write_verilog\n");
390 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*)
393 std::string techmap_cmd
= "techmap -assert";
394 std::string ilang_file
;
395 xorshift32_state
= 0;
396 std::ofstream vlog_file
;
397 bool verbose
= false;
400 for (argidx
= 1; argidx
< SIZE(args
); argidx
++)
402 if (args
[argidx
] == "-n" && argidx
+1 < SIZE(args
)) {
403 num_iter
= atoi(args
[++argidx
].c_str());
406 if (args
[argidx
] == "-s" && argidx
+1 < SIZE(args
)) {
407 xorshift32_state
= atoi(args
[++argidx
].c_str());
410 if (args
[argidx
] == "-map" && argidx
+1 < SIZE(args
)) {
411 techmap_cmd
+= " -map " + args
[++argidx
];
414 if (args
[argidx
] == "-f" && argidx
+1 < SIZE(args
)) {
415 ilang_file
= args
[++argidx
];
419 if (args
[argidx
] == "-simlib") {
420 techmap_cmd
= "techmap -map +/simlib.v -max_iter 2 -autoproc";
423 if (args
[argidx
] == "-v") {
427 if (args
[argidx
] == "-vlog" && argidx
+1 < SIZE(args
)) {
428 vlog_file
.open(args
[++argidx
], std::ios_base::trunc
);
429 if (!vlog_file
.is_open())
430 log_cmd_error("Failed to open output file `%s'.\n", args
[argidx
].c_str());
436 if (xorshift32_state
== 0)
437 xorshift32_state
= time(NULL
);
439 std::map
<std::string
, std::string
> cell_types
;
440 std::vector
<std::string
> selected_cell_types
;
442 cell_types
["$not"] = "ASY";
443 cell_types
["$pos"] = "ASY";
444 cell_types
["$bu0"] = "ASY";
445 cell_types
["$neg"] = "ASY";
447 cell_types
["$and"] = "ABSY";
448 cell_types
["$or"] = "ABSY";
449 cell_types
["$xor"] = "ABSY";
450 cell_types
["$xnor"] = "ABSY";
452 cell_types
["$reduce_and"] = "ASY";
453 cell_types
["$reduce_or"] = "ASY";
454 cell_types
["$reduce_xor"] = "ASY";
455 cell_types
["$reduce_xnor"] = "ASY";
456 cell_types
["$reduce_bool"] = "ASY";
458 cell_types
["$shl"] = "ABshY";
459 cell_types
["$shr"] = "ABshY";
460 cell_types
["$sshl"] = "ABshY";
461 cell_types
["$sshr"] = "ABshY";
462 cell_types
["$shift"] = "ABshY";
463 cell_types
["$shiftx"] = "ABshY";
465 cell_types
["$lt"] = "ABSY";
466 cell_types
["$le"] = "ABSY";
467 cell_types
["$eq"] = "ABSY";
468 cell_types
["$ne"] = "ABSY";
469 // cell_types["$eqx"] = "ABSY";
470 // cell_types["$nex"] = "ABSY";
471 cell_types
["$ge"] = "ABSY";
472 cell_types
["$gt"] = "ABSY";
474 cell_types
["$add"] = "ABSY";
475 cell_types
["$sub"] = "ABSY";
476 cell_types
["$mul"] = "ABSY";
477 cell_types
["$div"] = "ABSY";
478 cell_types
["$mod"] = "ABSY";
479 // cell_types["$pow"] = "ABsY";
481 cell_types
["$logic_not"] = "ASY";
482 cell_types
["$logic_and"] = "ABSY";
483 cell_types
["$logic_or"] = "ABSY";
485 // cell_types["$mux"] = "A";
486 // cell_types["$pmux"] = "A";
487 // cell_types["$slice"] = "A";
488 // cell_types["$concat"] = "A";
489 // cell_types["$assert"] = "A";
491 cell_types
["$lut"] = "*";
492 cell_types
["$alu"] = "ABSY";
494 for (; argidx
< SIZE(args
); argidx
++)
496 if (args
[argidx
].rfind("-", 0) == 0)
497 log_cmd_error("Unexpected option: %s\n", args
[argidx
].c_str());
499 if (args
[argidx
] == "all") {
500 for (auto &it
: cell_types
)
501 if (std::count(selected_cell_types
.begin(), selected_cell_types
.end(), it
.first
) == 0)
502 selected_cell_types
.push_back(it
.first
);
506 if (cell_types
.count(args
[argidx
]) == 0) {
507 std::string cell_type_list
;
509 for (auto &it
: cell_types
) {
510 if (charcount
> 60) {
511 cell_type_list
+= "\n" + it
.first
;
514 cell_type_list
+= " " + it
.first
;
515 charcount
+= SIZE(it
.first
);
517 log_cmd_error("The cell type `%s' is currently not supported. Try one of these:%s\n",
518 args
[argidx
].c_str(), cell_type_list
.c_str());
521 if (std::count(selected_cell_types
.begin(), selected_cell_types
.end(), args
[argidx
]) == 0)
522 selected_cell_types
.push_back(args
[argidx
]);
525 if (!ilang_file
.empty()) {
526 if (!selected_cell_types
.empty())
527 log_cmd_error("Do not specify any cell types when using -f.\n");
528 selected_cell_types
.push_back("ilang");
531 if (selected_cell_types
.empty())
532 log_cmd_error("No cell type to test specified.\n");
534 std::vector
<std::string
> uut_names
;
536 for (auto cell_type
: selected_cell_types
)
537 for (int i
= 0; i
< num_iter
; i
++)
539 RTLIL::Design
*design
= new RTLIL::Design
;
540 if (cell_type
== "ilang")
541 Frontend::frontend_call(design
, NULL
, std::string(), "ilang " + ilang_file
);
543 create_gold_module(design
, cell_type
, cell_types
.at(cell_type
));
544 Pass::call(design
, stringf("copy gold gate; %s gate; opt gate", techmap_cmd
.c_str()));
545 Pass::call(design
, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter");
547 Pass::call(design
, "dump gate");
548 Pass::call(design
, "dump gold");
549 Pass::call(design
, "sat -verify -enable_undef -prove trigger 0 -show-inputs -show-outputs miter");
550 std::string uut_name
= stringf("uut_%s_%d", cell_type
.substr(1).c_str(), i
);
551 if (vlog_file
.is_open()) {
552 Pass::call(design
, stringf("copy gold %s_expr; select %s_expr", uut_name
.c_str(), uut_name
.c_str()));
553 Backend::backend_call(design
, &vlog_file
, "<test_cell -vlog>", "verilog -selected");
554 Pass::call(design
, stringf("copy gold %s_noexpr; select %s_noexpr", uut_name
.c_str(), uut_name
.c_str()));
555 Backend::backend_call(design
, &vlog_file
, "<test_cell -vlog>", "verilog -selected -noexpr");
556 uut_names
.push_back(uut_name
);
558 run_eval_test(design
, verbose
, uut_name
, vlog_file
);
562 if (vlog_file
.is_open()) {
563 vlog_file
<< "\nmodule testbench;\n";
564 for (auto &uut
: uut_names
)
565 vlog_file
<< stringf(" %s %s ();\n", uut
.c_str(), uut
.c_str());
566 vlog_file
<< " initial begin\n";
567 for (auto &uut
: uut_names
)
568 vlog_file
<< " " << uut
<< ".run;\n";
569 vlog_file
<< " end\n";
570 vlog_file
<< "endmodule\n";