diff options
Diffstat (limited to 'passes/tests')
-rw-r--r-- | passes/tests/Makefile.inc | 5 | ||||
-rw-r--r-- | passes/tests/test_abcloop.cc | 285 | ||||
-rw-r--r-- | passes/tests/test_autotb.cc | 353 | ||||
-rw-r--r-- | passes/tests/test_cell.cc | 745 |
4 files changed, 1388 insertions, 0 deletions
diff --git a/passes/tests/Makefile.inc b/passes/tests/Makefile.inc new file mode 100644 index 000000000..531943d78 --- /dev/null +++ b/passes/tests/Makefile.inc @@ -0,0 +1,5 @@ + +OBJS += passes/tests/test_autotb.o +OBJS += passes/tests/test_cell.o +OBJS += passes/tests/test_abcloop.o + diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc new file mode 100644 index 000000000..0a0ceb1d1 --- /dev/null +++ b/passes/tests/test_abcloop.cc @@ -0,0 +1,285 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/satgen.h" + +static uint32_t xorshift32_state = 123456789; + +static uint32_t xorshift32(uint32_t limit) { + xorshift32_state ^= xorshift32_state << 13; + xorshift32_state ^= xorshift32_state >> 17; + xorshift32_state ^= xorshift32_state << 5; + return xorshift32_state % limit; +} + +static RTLIL::Wire *getw(std::vector<RTLIL::Wire*> &wires, RTLIL::Wire *w) +{ + while (1) { + int idx = xorshift32(SIZE(wires)); + if (wires[idx] != w && !wires[idx]->port_output) + return wires[idx]; + } +} + +static void test_abcloop() +{ + log("Rng seed value: %u\n", int(xorshift32_state)); + + RTLIL::Design *design = new RTLIL::Design; + RTLIL::Module *module = nullptr; + RTLIL::SigSpec in_sig, out_sig; + + bool truthtab[16][4]; + int create_cycles = 0; + + while (1) + { + module = design->addModule("\\uut"); + create_cycles++; + + in_sig = {}; + out_sig = {}; + + std::vector<RTLIL::Wire*> wires; + + for (int i = 0; i < 4; i++) { + RTLIL::Wire *w = module->addWire(stringf("\\i%d", i)); + w->port_input = true; + wires.push_back(w); + in_sig.append(w); + } + + for (int i = 0; i < 4; i++) { + RTLIL::Wire *w = module->addWire(stringf("\\o%d", i)); + w->port_output = true; + wires.push_back(w); + out_sig.append(w); + } + + for (int i = 0; i < 16; i++) { + RTLIL::Wire *w = module->addWire(stringf("\\t%d", i)); + wires.push_back(w); + } + + for (auto w : wires) + if (!w->port_input) + switch (xorshift32(12)) + { + case 0: + module->addNotGate(w->name.str() + "g", getw(wires, w), w); + break; + case 1: + module->addAndGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w); + break; + case 2: + module->addNandGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w); + break; + case 3: + module->addOrGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w); + break; + case 4: + module->addNorGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w); + break; + case 5: + module->addXorGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w); + break; + case 6: + module->addXnorGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w); + break; + case 7: + module->addMuxGate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), w); + break; + case 8: + module->addAoi3Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), w); + break; + case 9: + module->addOai3Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), w); + break; + case 10: + module->addAoi4Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), getw(wires, w), w); + break; + case 11: + module->addOai4Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), getw(wires, w), w); + break; + } + + module->fixup_ports(); + Pass::call(design, "clean"); + + ezDefaultSAT ez; + SigMap sigmap(module); + SatGen satgen(&ez, &sigmap); + + for (auto c : module->cells()) { + bool ok = satgen.importCell(c); + log_assert(ok); + } + + std::vector<int> in_vec = satgen.importSigSpec(in_sig); + std::vector<int> inverse_in_vec = ez.vec_not(in_vec); + + std::vector<int> out_vec = satgen.importSigSpec(out_sig); + + for (int i = 0; i < 16; i++) + { + std::vector<int> assumptions; + for (int j = 0; j < SIZE(in_vec); j++) + assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j)); + + std::vector<bool> results; + if (!ez.solve(out_vec, results, assumptions)) { + log("No stable solution for input %d found -> recreate module.\n", i); + goto recreate_module; + } + + for (int j = 0; j < 4; j++) + truthtab[i][j] = results[j]; + + assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + + std::vector<bool> results2; + if (ez.solve(out_vec, results2, assumptions)) { + log("Two stable solutions for input %d found -> recreate module.\n", i); + goto recreate_module; + } + } + break; + + recreate_module: + design->remove(module); + } + + log("Found viable UUT after %d cycles:\n", create_cycles); + Pass::call(design, "write_ilang"); + Pass::call(design, "abc"); + + log("\n"); + log("Pre- and post-abc truth table:\n"); + + ezDefaultSAT ez; + SigMap sigmap(module); + SatGen satgen(&ez, &sigmap); + + for (auto c : module->cells()) { + bool ok = satgen.importCell(c); + log_assert(ok); + } + + std::vector<int> in_vec = satgen.importSigSpec(in_sig); + std::vector<int> inverse_in_vec = ez.vec_not(in_vec); + + std::vector<int> out_vec = satgen.importSigSpec(out_sig); + + bool found_error = false; + bool truthtab2[16][4]; + + for (int i = 0; i < 16; i++) + { + std::vector<int> assumptions; + for (int j = 0; j < SIZE(in_vec); j++) + assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j)); + + for (int j = 0; j < 4; j++) + truthtab2[i][j] = truthtab[i][j]; + + std::vector<bool> results; + if (!ez.solve(out_vec, results, assumptions)) { + log("No stable solution for input %d found.\n", i); + found_error = true; + continue; + } + + for (int j = 0; j < 4; j++) + truthtab2[i][j] = results[j]; + + assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + + std::vector<bool> results2; + if (ez.solve(out_vec, results2, assumptions)) { + log("Two stable solutions for input %d found -> recreate module.\n", i); + found_error = true; + } + } + + for (int i = 0; i < 16; i++) { + log("%3d ", i); + for (int j = 0; j < 4; j++) + log("%c", truthtab[i][j] ? '1' : '0'); + log(" "); + for (int j = 0; j < 4; j++) + log("%c", truthtab2[i][j] ? '1' : '0'); + for (int j = 0; j < 4; j++) + if (truthtab[i][j] != truthtab2[i][j]) { + found_error = true; + log(" !"); + break; + } + log("\n"); + } + + log_assert(found_error == false); + log("\n"); +} + +struct TestAbcloopPass : public Pass { + TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" test_abcloop [options]\n"); + log("\n"); + log("Test handling of logic loops in ABC.\n"); + log("\n"); + log(" -n {integer}\n"); + log(" create this number of circuits and test them (default = 100).\n"); + log("\n"); + log(" -s {positive_integer}\n"); + log(" use this value as rng seed value (default = unix time).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design*) + { + int num_iter = 100; + xorshift32_state = 0; + + int argidx; + for (argidx = 1; argidx < SIZE(args); argidx++) + { + if (args[argidx] == "-n" && argidx+1 < SIZE(args)) { + num_iter = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-s" && argidx+1 < SIZE(args)) { + xorshift32_state = atoi(args[++argidx].c_str()); + continue; + } + break; + } + + if (xorshift32_state == 0) + xorshift32_state = time(NULL) & 0x7fffffff; + + for (int i = 0; i < num_iter; i++) + test_abcloop(); + } +} TestAbcloopPass; + diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc new file mode 100644 index 000000000..eed0f75f9 --- /dev/null +++ b/passes/tests/test_autotb.cc @@ -0,0 +1,353 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +PRIVATE_NAMESPACE_BEGIN + +static std::string id(std::string internal_id) +{ + const char *str = internal_id.c_str(); + bool do_escape = false; + + if (*str == '\\') + str++; + + if ('0' <= *str && *str <= '9') + do_escape = true; + + for (int i = 0; str[i]; i++) { + if ('0' <= str[i] && str[i] <= '9') + continue; + if ('a' <= str[i] && str[i] <= 'z') + continue; + if ('A' <= str[i] && str[i] <= 'Z') + continue; + if (str[i] == '_') + continue; + do_escape = true; + break; + } + + if (do_escape) + return "\\" + std::string(str) + " "; + return std::string(str); +} + +static std::string idx(std::string str) +{ + if (str[0] == '\\') + return str.substr(1); + return str; +} + +static std::string idy(std::string str1, std::string str2 = std::string(), std::string str3 = std::string()) +{ + str1 = idx(str1); + if (!str2.empty()) + str1 += "_" + idx(str2); + if (!str3.empty()) + str1 += "_" + idx(str3); + return id(str1); +} + +static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) +{ + f << stringf("module testbench;\n\n"); + + f << stringf("integer i;\n\n"); + + f << stringf("reg [31:0] xorshift128_x = 123456789;\n"); + f << stringf("reg [31:0] xorshift128_y = 362436069;\n"); + f << stringf("reg [31:0] xorshift128_z = 521288629;\n"); + f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", int(time(NULL))); + f << stringf("reg [31:0] xorshift128_t;\n\n"); + f << stringf("task xorshift128;\n"); + f << stringf("begin\n"); + f << stringf("\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n"); + f << stringf("\txorshift128_x = xorshift128_y;\n"); + f << stringf("\txorshift128_y = xorshift128_z;\n"); + f << stringf("\txorshift128_z = xorshift128_w;\n"); + f << stringf("\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n"); + f << stringf("end\n"); + f << stringf("endtask\n\n"); + + for (auto it = design->modules_.begin(); it != design->modules_.end(); it++) + { + std::map<std::string, int> signal_in; + std::map<std::string, std::string> signal_const; + std::map<std::string, int> signal_clk; + std::map<std::string, int> signal_out; + + RTLIL::Module *mod = it->second; + + if (mod->get_bool_attribute("\\gentb_skip")) + continue; + + int count_ports = 0; + log("Generating test bench for module `%s'.\n", it->first.c_str()); + for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); it2++) { + RTLIL::Wire *wire = it2->second; + if (wire->port_output) { + count_ports++; + signal_out[idy("sig", mod->name.str(), wire->name.str())] = wire->width; + f << stringf("wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str()); + } else if (wire->port_input) { + count_ports++; + bool is_clksignal = wire->get_bool_attribute("\\gentb_clock"); + for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); it3++) + for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); it4++) { + if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1) + continue; + RTLIL::SigSpec &signal = (*it4)->signal; + for (auto &c : signal.chunks()) + if (c.wire == wire) + is_clksignal = true; + } + if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) { + signal_clk[idy("sig", mod->name.str(), wire->name.str())] = wire->width; + } else { + signal_in[idy("sig", mod->name.str(), wire->name.str())] = wire->width; + if (wire->attributes.count("\\gentb_constant") != 0) + signal_const[idy("sig", mod->name.str(), wire->name.str())] = wire->attributes["\\gentb_constant"].as_string(); + } + f << stringf("reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str()); + } + } + f << stringf("%s %s(\n", id(mod->name.str()).c_str(), idy("uut", mod->name.str()).c_str()); + for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); it2++) { + RTLIL::Wire *wire = it2->second; + if (wire->port_output || wire->port_input) + f << stringf("\t.%s(%s)%s\n", id(wire->name.str()).c_str(), + idy("sig", mod->name.str(), wire->name.str()).c_str(), --count_ports ? "," : ""); + } + f << stringf(");\n\n"); + + f << stringf("task %s;\n", idy(mod->name.str(), "reset").c_str()); + f << stringf("begin\n"); + int delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) + f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) + f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + f << stringf("\t#100; %s <= 1;\n", it->first.c_str()); + f << stringf("\t#100; %s <= 0;\n", it->first.c_str()); + } + delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) + f << stringf("\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + f << stringf("\t#100; %s <= 1;\n", it->first.c_str()); + f << stringf("\t#100; %s <= 0;\n", it->first.c_str()); + } + delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) { + if (signal_const.count(it->first) == 0) + continue; + f << stringf("\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str()); + } + f << stringf("end\n"); + f << stringf("endtask\n\n"); + + f << stringf("task %s;\n", idy(mod->name.str(), "update_data").c_str()); + f << stringf("begin\n"); + delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) { + if (signal_const.count(it->first) > 0) + continue; + f << stringf("\txorshift128;\n"); + f << stringf("\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2); + } + f << stringf("end\n"); + f << stringf("endtask\n\n"); + + f << stringf("task %s;\n", idy(mod->name.str(), "update_clock").c_str()); + f << stringf("begin\n"); + if (signal_clk.size()) { + f << stringf("\txorshift128;\n"); + f << stringf("\t{"); + int total_clock_bits = 0; + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); + total_clock_bits += it->second; + } + f << stringf(" } = {"); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) + f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); + f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits); + } + f << stringf("end\n"); + f << stringf("endtask\n\n"); + + char shorthand = 'A'; + std::vector<std::string> header1; + std::string header2 = ""; + + f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str()); + f << stringf("begin\n"); + f << stringf("\t$display(\"#OUT# %%b %%b %%b %%t %%d\", {"); + if (signal_in.size()) + for (auto it = signal_in.begin(); it != signal_in.end(); it++) { + f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str()); + int len = it->second; + if (len > 1) + header2 += "/", len--; + while (len > 1) + header2 += "-", len--; + if (len > 0) + header2 += shorthand, len--; + header1.push_back(" " + it->first); + header1.back()[0] = shorthand++; + } + else { + f << stringf(" 1'bx"); + header2 += "#"; + } + f << stringf(" }, {"); + header2 += " "; + if (signal_clk.size()) { + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); + int len = it->second; + if (len > 1) + header2 += "/", len--; + while (len > 1) + header2 += "-", len--; + if (len > 0) + header2 += shorthand, len--; + header1.push_back(" " + it->first); + header1.back()[0] = shorthand++; + } + } else { + f << stringf(" 1'bx"); + header2 += "#"; + } + f << stringf(" }, {"); + header2 += " "; + if (signal_out.size()) { + for (auto it = signal_out.begin(); it != signal_out.end(); it++) { + f << stringf("%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str()); + int len = it->second; + if (len > 1) + header2 += "/", len--; + while (len > 1) + header2 += "-", len--; + if (len > 0) + header2 += shorthand, len--; + header1.push_back(" " + it->first); + header1.back()[0] = shorthand++; + } + } else { + f << stringf(" 1'bx"); + header2 += "#"; + } + f << stringf(" }, $time, i);\n"); + f << stringf("end\n"); + f << stringf("endtask\n\n"); + + f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str()); + f << stringf("begin\n"); + f << stringf("\t$display(\"#OUT#\");\n"); + for (auto &hdr : header1) + f << stringf("\t$display(\"#OUT# %s\");\n", hdr.c_str()); + f << stringf("\t$display(\"#OUT#\");\n"); + f << stringf("\t$display(\"#OUT# %s\");\n", header2.c_str()); + f << stringf("end\n"); + f << stringf("endtask\n\n"); + + f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str()); + f << stringf("begin\n"); + f << stringf("\t$display(\"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); + f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str()); + f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter); + f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str()); + f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_data").c_str()); + f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_clock").c_str()); + f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "print_status").c_str()); + f << stringf("\tend\n"); + f << stringf("end\n"); + f << stringf("endtask\n\n"); + } + + f << stringf("initial begin\n"); + f << stringf("\t// $dumpfile(\"testbench.vcd\");\n"); + f << stringf("\t// $dumpvars(0, testbench);\n"); + for (auto it = design->modules_.begin(); it != design->modules_.end(); it++) + if (!it->second->get_bool_attribute("\\gentb_skip")) + f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str()); + f << stringf("\t$finish;\n"); + f << stringf("end\n\n"); + + f << stringf("endmodule\n"); +} + +struct TestAutotbBackend : public Backend { + TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" test_autotb [options] [filename]\n"); + log("\n"); + log("Automatically create primitive verilog test benches for all modules in the\n"); + log("design. The generated testbenches toggle the input pins of the module in\n"); + log("a semi-random manner and dumps the resulting output signals.\n"); + log("\n"); + log("This can be used to check the synthesis results for simple circuits by\n"); + log("comparing the testbench output for the input files and the synthesis results.\n"); + log("\n"); + log("The backend automatically detects clock signals. Additionally a signal can\n"); + log("be forced to be interpreted as clock signal by setting the attribute\n"); + log("'gentb_clock' on the signal.\n"); + log("\n"); + log("The attribute 'gentb_constant' can be used to force a signal to a constant\n"); + log("value after initialization. This can e.g. be used to force a reset signal\n"); + log("low in order to explore more inner states in a state machine.\n"); + log("\n"); + log(" -n <int>\n"); + log(" number of iterations the test bench shuld run (default = 1000)\n"); + log("\n"); + } + virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + { + int num_iter = 1000; + + log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); + + int argidx; + for (argidx = 1; argidx < SIZE(args); argidx++) + { + if (args[argidx] == "-n" && argidx+1 < SIZE(args)) { + num_iter = atoi(args[++argidx].c_str()); + continue; + } + break; + } + + extra_args(f, filename, args, argidx); + autotest(*f, design, num_iter); + } +} TestAutotbBackend; + +PRIVATE_NAMESPACE_END + diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc new file mode 100644 index 000000000..1fa90b540 --- /dev/null +++ b/passes/tests/test_cell.cc @@ -0,0 +1,745 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/satgen.h" +#include "kernel/consteval.h" +#include "kernel/macc.h" +#include <algorithm> + +static uint32_t xorshift32_state = 123456789; + +static uint32_t xorshift32(uint32_t limit) { + xorshift32_state ^= xorshift32_state << 13; + xorshift32_state ^= xorshift32_state >> 17; + xorshift32_state ^= xorshift32_state << 5; + return xorshift32_state % limit; +} + +static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode) +{ + RTLIL::Module *module = design->addModule("\\gold"); + RTLIL::Cell *cell = module->addCell("\\UUT", cell_type); + RTLIL::Wire *wire; + + if (cell_type == "$fa") + { + int width = 1 + xorshift32(8); + + wire = module->addWire("\\A"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\A", wire); + + wire = module->addWire("\\B"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\B", wire); + + wire = module->addWire("\\C"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\C", wire); + + wire = module->addWire("\\X"); + wire->width = width; + wire->port_output = true; + cell->setPort("\\X", wire); + + wire = module->addWire("\\Y"); + wire->width = width; + wire->port_output = true; + cell->setPort("\\Y", wire); + } + + if (cell_type == "$lcu") + { + int width = 1 + xorshift32(8); + + wire = module->addWire("\\P"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\P", wire); + + wire = module->addWire("\\G"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\G", wire); + + wire = module->addWire("\\CI"); + wire->port_input = true; + cell->setPort("\\CI", wire); + + wire = module->addWire("\\CO"); + wire->width = width; + wire->port_output = true; + cell->setPort("\\CO", wire); + } + + if (cell_type == "$macc") + { + Macc macc; + int width = 1 + xorshift32(8); + int depth = 1 + xorshift32(6); + int mulbits_a = 0, mulbits_b = 0; + + RTLIL::Wire *wire_a = module->addWire("\\A"); + wire_a->width = 0; + wire_a->port_input = true; + + for (int i = 0; i < depth; i++) + { + int size_a = xorshift32(width) + 1; + int size_b = depth > 4 ? 0 : xorshift32(width) + 1; + + if (mulbits_a + size_a*size_b <= 96 && mulbits_b + size_a + size_b <= 16 && xorshift32(2) == 1) { + mulbits_a += size_a * size_b; + mulbits_b += size_a + size_b; + } else + size_b = 0; + + Macc::port_t this_port; + + wire_a->width += size_a; + this_port.in_a = RTLIL::SigSpec(wire_a, wire_a->width - size_a, size_a); + + wire_a->width += size_b; + this_port.in_b = RTLIL::SigSpec(wire_a, wire_a->width - size_b, size_b); + + this_port.is_signed = xorshift32(2) == 1; + this_port.do_subtract = xorshift32(2) == 1; + macc.ports.push_back(this_port); + } + + wire = module->addWire("\\B"); + wire->width = xorshift32(mulbits_a ? xorshift32(4)+1 : xorshift32(16)+1); + wire->port_input = true; + macc.bit_ports = wire; + + wire = module->addWire("\\Y"); + wire->width = width; + wire->port_output = true; + cell->setPort("\\Y", wire); + + macc.to_cell(cell); + } + + if (cell_type == "$lut") + { + int width = 1 + xorshift32(6); + + wire = module->addWire("\\A"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\A", wire); + + wire = module->addWire("\\Y"); + wire->port_output = true; + cell->setPort("\\Y", wire); + + RTLIL::SigSpec config; + for (int i = 0; i < (1 << width); i++) + config.append(xorshift32(2) ? RTLIL::S1 : RTLIL::S0); + + cell->setParam("\\LUT", config.as_const()); + } + + if (cell_type_flags.find('A') != std::string::npos) { + wire = module->addWire("\\A"); + wire->width = 1 + xorshift32(8); + wire->port_input = true; + cell->setPort("\\A", wire); + } + + if (cell_type_flags.find('B') != std::string::npos) { + wire = module->addWire("\\B"); + if (cell_type_flags.find('h') != std::string::npos) + wire->width = 1 + xorshift32(6); + else + wire->width = 1 + xorshift32(8); + wire->port_input = true; + cell->setPort("\\B", wire); + } + + if (cell_type_flags.find('S') != std::string::npos && xorshift32(2)) { + if (cell_type_flags.find('A') != std::string::npos) + cell->parameters["\\A_SIGNED"] = true; + if (cell_type_flags.find('B') != std::string::npos) + cell->parameters["\\B_SIGNED"] = true; + } + + if (cell_type_flags.find('s') != std::string::npos) { + if (cell_type_flags.find('A') != std::string::npos && xorshift32(2)) + cell->parameters["\\A_SIGNED"] = true; + if (cell_type_flags.find('B') != std::string::npos && xorshift32(2)) + cell->parameters["\\B_SIGNED"] = true; + } + + if (cell_type_flags.find('Y') != std::string::npos) { + wire = module->addWire("\\Y"); + wire->width = 1 + xorshift32(8); + wire->port_output = true; + cell->setPort("\\Y", wire); + } + + if (cell_type == "$alu") + { + wire = module->addWire("\\CI"); + wire->port_input = true; + cell->setPort("\\CI", wire); + + wire = module->addWire("\\BI"); + wire->port_input = true; + cell->setPort("\\BI", wire); + + wire = module->addWire("\\X"); + wire->width = SIZE(cell->getPort("\\Y")); + wire->port_output = true; + cell->setPort("\\X", wire); + + wire = module->addWire("\\CO"); + wire->width = SIZE(cell->getPort("\\Y")); + wire->port_output = true; + cell->setPort("\\CO", wire); + } + + if (constmode) + { + auto conn_list = cell->connections(); + for (auto &conn : conn_list) + { + RTLIL::SigSpec sig = conn.second; + + if (SIZE(sig) == 0 || sig[0].wire == nullptr || sig[0].wire->port_output) + continue; + + int n, m; + switch (xorshift32(5)) + { + case 0: + n = xorshift32(SIZE(sig) + 1); + for (int i = 0; i < n; i++) + sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; + break; + case 1: + n = xorshift32(SIZE(sig) + 1); + for (int i = n; i < SIZE(sig); i++) + sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; + break; + case 2: + n = xorshift32(SIZE(sig)); + m = xorshift32(SIZE(sig)); + for (int i = std::min(n, m); i < std::max(n, m); i++) + sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; + break; + } + + cell->setPort(conn.first, sig); + } + } + + module->fixup_ports(); + cell->fixup_parameters(); + cell->check(); +} + +static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::string uut_name, std::ofstream &vlog_file) +{ + log("Eval testing:%c", verbose ? '\n' : ' '); + + RTLIL::Module *gold_mod = design->module("\\gold"); + RTLIL::Module *gate_mod = design->module("\\gate"); + ConstEval gold_ce(gold_mod), gate_ce(gate_mod); + + ezDefaultSAT ez1, ez2; + SigMap sigmap(gold_mod); + SatGen satgen1(&ez1, &sigmap); + SatGen satgen2(&ez2, &sigmap); + satgen2.model_undef = true; + + if (!nosat) + for (auto cell : gold_mod->cells()) { + satgen1.importCell(cell); + satgen2.importCell(cell); + } + + if (vlog_file.is_open()) + { + vlog_file << stringf("\nmodule %s;\n", uut_name.c_str()); + + for (auto port : gold_mod->ports) { + RTLIL::Wire *wire = gold_mod->wire(port); + if (wire->port_input) + vlog_file << stringf(" reg [%d:0] %s;\n", SIZE(wire)-1, log_id(wire)); + else + vlog_file << stringf(" wire [%d:0] %s_expr, %s_noexpr;\n", SIZE(wire)-1, log_id(wire), log_id(wire)); + } + + vlog_file << stringf(" %s_expr uut_expr(", uut_name.c_str()); + for (int i = 0; i < SIZE(gold_mod->ports); i++) + vlog_file << stringf("%s.%s(%s%s)", i ? ", " : "", log_id(gold_mod->ports[i]), log_id(gold_mod->ports[i]), + gold_mod->wire(gold_mod->ports[i])->port_input ? "" : "_expr"); + vlog_file << stringf(");\n"); + + vlog_file << stringf(" %s_expr uut_noexpr(", uut_name.c_str()); + for (int i = 0; i < SIZE(gold_mod->ports); i++) + vlog_file << stringf("%s.%s(%s%s)", i ? ", " : "", log_id(gold_mod->ports[i]), log_id(gold_mod->ports[i]), + gold_mod->wire(gold_mod->ports[i])->port_input ? "" : "_noexpr"); + vlog_file << stringf(");\n"); + + vlog_file << stringf(" task run;\n"); + vlog_file << stringf(" begin\n"); + vlog_file << stringf(" $display(\"%s\");\n", uut_name.c_str()); + } + + for (int i = 0; i < 64; i++) + { + log(verbose ? "\n" : "."); + gold_ce.clear(); + gate_ce.clear(); + + RTLIL::SigSpec in_sig, in_val; + RTLIL::SigSpec out_sig, out_val; + std::string vlog_pattern_info; + + for (auto port : gold_mod->ports) + { + RTLIL::Wire *gold_wire = gold_mod->wire(port); + RTLIL::Wire *gate_wire = gate_mod->wire(port); + + log_assert(gold_wire != nullptr); + log_assert(gate_wire != nullptr); + log_assert(gold_wire->port_input == gate_wire->port_input); + log_assert(SIZE(gold_wire) == SIZE(gate_wire)); + + if (!gold_wire->port_input) + continue; + + RTLIL::Const in_value; + for (int i = 0; i < SIZE(gold_wire); i++) + in_value.bits.push_back(xorshift32(2) ? RTLIL::S1 : RTLIL::S0); + + if (xorshift32(4) == 0) { + int inv_chance = 1 + xorshift32(8); + for (int i = 0; i < SIZE(gold_wire); i++) + if (xorshift32(inv_chance) == 0) + in_value.bits[i] = RTLIL::Sx; + } + + if (verbose) + log("%s: %s\n", log_id(gold_wire), log_signal(in_value)); + + in_sig.append(gold_wire); + in_val.append(in_value); + + gold_ce.set(gold_wire, in_value); + gate_ce.set(gate_wire, in_value); + + if (vlog_file.is_open() && SIZE(in_value) > 0) { + vlog_file << stringf(" %s = 'b%s;\n", log_id(gold_wire), in_value.as_string().c_str()); + if (!vlog_pattern_info.empty()) + vlog_pattern_info += " "; + vlog_pattern_info += stringf("%s=%s", log_id(gold_wire), log_signal(in_value)); + } + } + + if (vlog_file.is_open()) + vlog_file << stringf(" #1;\n"); + + for (auto port : gold_mod->ports) + { + RTLIL::Wire *gold_wire = gold_mod->wire(port); + RTLIL::Wire *gate_wire = gate_mod->wire(port); + + log_assert(gold_wire != nullptr); + log_assert(gate_wire != nullptr); + log_assert(gold_wire->port_output == gate_wire->port_output); + log_assert(SIZE(gold_wire) == SIZE(gate_wire)); + + if (!gold_wire->port_output) + continue; + + RTLIL::SigSpec gold_outval(gold_wire); + RTLIL::SigSpec gate_outval(gate_wire); + + if (!gold_ce.eval(gold_outval)) + log_error("Failed to eval %s in gold module.\n", log_id(gold_wire)); + + if (!gate_ce.eval(gate_outval)) + log_error("Failed to eval %s in gate module.\n", log_id(gate_wire)); + + bool gold_gate_mismatch = false; + for (int i = 0; i < SIZE(gold_wire); i++) { + if (gold_outval[i] == RTLIL::Sx) + continue; + if (gold_outval[i] == gate_outval[i]) + continue; + gold_gate_mismatch = true; + break; + } + + if (gold_gate_mismatch) + log_error("Mismatch in output %s: gold:%s != gate:%s\n", log_id(gate_wire), log_signal(gold_outval), log_signal(gate_outval)); + + if (verbose) + log("%s: %s\n", log_id(gold_wire), log_signal(gold_outval)); + + out_sig.append(gold_wire); + out_val.append(gold_outval); + + if (vlog_file.is_open()) { + vlog_file << stringf(" $display(\"[%s] %s expected: %%b, expr: %%b, noexpr: %%b\", %d'b%s, %s_expr, %s_noexpr);\n", + 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)); + 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()); + 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()); + } + } + + if (verbose) + log("EVAL: %s\n", out_val.as_string().c_str()); + + if (!nosat) + { + std::vector<int> sat1_in_sig = satgen1.importSigSpec(in_sig); + std::vector<int> sat1_in_val = satgen1.importSigSpec(in_val); + + std::vector<int> sat1_model = satgen1.importSigSpec(out_sig); + std::vector<bool> sat1_model_value; + + if (!ez1.solve(sat1_model, sat1_model_value, ez1.vec_eq(sat1_in_sig, sat1_in_val))) + log_error("Evaluating sat model 1 (no undef modeling) failed!\n"); + + if (verbose) { + log("SAT 1: "); + for (int i = SIZE(out_sig)-1; i >= 0; i--) + log("%c", sat1_model_value.at(i) ? '1' : '0'); + log("\n"); + } + + for (int i = 0; i < SIZE(out_sig); i++) { + if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1) + continue; + if (out_val[i] == RTLIL::S0 && sat1_model_value.at(i) == false) + continue; + if (out_val[i] == RTLIL::S1 && sat1_model_value.at(i) == true) + continue; + log_error("Mismatch in sat model 1 (no undef modeling) output!\n"); + } + + std::vector<int> sat2_in_def_sig = satgen2.importDefSigSpec(in_sig); + std::vector<int> sat2_in_def_val = satgen2.importDefSigSpec(in_val); + + std::vector<int> sat2_in_undef_sig = satgen2.importUndefSigSpec(in_sig); + std::vector<int> sat2_in_undef_val = satgen2.importUndefSigSpec(in_val); + + std::vector<int> sat2_model_def_sig = satgen2.importDefSigSpec(out_sig); + std::vector<int> sat2_model_undef_sig = satgen2.importUndefSigSpec(out_sig); + + std::vector<int> sat2_model; + sat2_model.insert(sat2_model.end(), sat2_model_def_sig.begin(), sat2_model_def_sig.end()); + sat2_model.insert(sat2_model.end(), sat2_model_undef_sig.begin(), sat2_model_undef_sig.end()); + + std::vector<bool> sat2_model_value; + + 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))) + log_error("Evaluating sat model 2 (undef modeling) failed!\n"); + + if (verbose) { + log("SAT 2: "); + for (int i = SIZE(out_sig)-1; i >= 0; i--) + log("%c", sat2_model_value.at(SIZE(out_sig) + i) ? 'x' : sat2_model_value.at(i) ? '1' : '0'); + log("\n"); + } + + for (int i = 0; i < SIZE(out_sig); i++) { + if (sat2_model_value.at(SIZE(out_sig) + i)) { + if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1) + continue; + } else { + if (out_val[i] == RTLIL::S0 && sat2_model_value.at(i) == false) + continue; + if (out_val[i] == RTLIL::S1 && sat2_model_value.at(i) == true) + continue; + } + log_error("Mismatch in sat model 2 (undef modeling) output!\n"); + } + } + } + + if (vlog_file.is_open()) { + vlog_file << stringf(" end\n"); + vlog_file << stringf(" endtask\n"); + vlog_file << stringf("endmodule\n"); + } + + if (!verbose) + log(" ok.\n"); +} + +struct TestCellPass : public Pass { + TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" test_cell [options] {cell-types}\n"); + log("\n"); + log("Tests the internal implementation of the given cell type (for example '$add')\n"); + log("by comparing SAT solver, EVAL and TECHMAP implementations of the cell types..\n"); + log("\n"); + log("Run with 'all' instead of a cell type to run the test on all supported\n"); + log("cell types.\n"); + log("\n"); + log(" -n {integer}\n"); + log(" create this number of cell instances and test them (default = 100).\n"); + log("\n"); + log(" -s {positive_integer}\n"); + log(" use this value as rng seed value (default = unix time).\n"); + log("\n"); + log(" -f {ilang_file}\n"); + log(" don't generate circuits. instead load the specified ilang file.\n"); + log("\n"); + log(" -map {filename}\n"); + log(" pass this option to techmap.\n"); + log("\n"); + log(" -simplib\n"); + log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n"); + log("\n"); + log(" -script {script_file}\n"); + log(" instead of calling \"techmap\", call \"script {script_file}\".\n"); + log("\n"); + log(" -const\n"); + log(" set some input bits to random constant values\n"); + log("\n"); + log(" -nosat\n"); + log(" do not check SAT model or run SAT equivalence checking\n"); + log("\n"); + log(" -v\n"); + log(" print additional debug information to the console\n"); + log("\n"); + log(" -vlog {filename}\n"); + log(" create a verilog test bench to test simlib and write_verilog\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design*) + { + int num_iter = 100; + std::string techmap_cmd = "techmap -assert"; + std::string ilang_file; + xorshift32_state = 0; + std::ofstream vlog_file; + bool verbose = false; + bool constmode = false; + bool nosat = false; + + int argidx; + for (argidx = 1; argidx < SIZE(args); argidx++) + { + if (args[argidx] == "-n" && argidx+1 < SIZE(args)) { + num_iter = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-s" && argidx+1 < SIZE(args)) { + xorshift32_state = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-map" && argidx+1 < SIZE(args)) { + techmap_cmd += " -map " + args[++argidx]; + continue; + } + if (args[argidx] == "-f" && argidx+1 < SIZE(args)) { + ilang_file = args[++argidx]; + num_iter = 1; + continue; + } + if (args[argidx] == "-script" && argidx+1 < SIZE(args)) { + techmap_cmd = "script " + args[++argidx]; + continue; + } + if (args[argidx] == "-simlib") { + techmap_cmd = "techmap -map +/simlib.v -max_iter 2 -autoproc"; + continue; + } + if (args[argidx] == "-const") { + constmode = true; + continue; + } + if (args[argidx] == "-nosat") { + nosat = true; + continue; + } + if (args[argidx] == "-v") { + verbose = true; + continue; + } + if (args[argidx] == "-vlog" && argidx+1 < SIZE(args)) { + vlog_file.open(args[++argidx], std::ios_base::trunc); + if (!vlog_file.is_open()) + log_cmd_error("Failed to open output file `%s'.\n", args[argidx].c_str()); + continue; + } + break; + } + + if (xorshift32_state == 0) { + xorshift32_state = time(NULL) & 0x7fffffff; + log("Rng seed value: %d\n", int(xorshift32_state)); + } + + std::map<std::string, std::string> cell_types; + std::vector<std::string> selected_cell_types; + + cell_types["$not"] = "ASY"; + cell_types["$pos"] = "ASY"; + cell_types["$neg"] = "ASY"; + + cell_types["$and"] = "ABSY"; + cell_types["$or"] = "ABSY"; + cell_types["$xor"] = "ABSY"; + cell_types["$xnor"] = "ABSY"; + + cell_types["$reduce_and"] = "ASY"; + cell_types["$reduce_or"] = "ASY"; + cell_types["$reduce_xor"] = "ASY"; + cell_types["$reduce_xnor"] = "ASY"; + cell_types["$reduce_bool"] = "ASY"; + + cell_types["$shl"] = "ABshY"; + cell_types["$shr"] = "ABshY"; + cell_types["$sshl"] = "ABshY"; + cell_types["$sshr"] = "ABshY"; + cell_types["$shift"] = "ABshY"; + cell_types["$shiftx"] = "ABshY"; + + cell_types["$lt"] = "ABSY"; + cell_types["$le"] = "ABSY"; + cell_types["$eq"] = "ABSY"; + cell_types["$ne"] = "ABSY"; + // cell_types["$eqx"] = "ABSY"; + // cell_types["$nex"] = "ABSY"; + cell_types["$ge"] = "ABSY"; + cell_types["$gt"] = "ABSY"; + + cell_types["$add"] = "ABSY"; + cell_types["$sub"] = "ABSY"; + cell_types["$mul"] = "ABSY"; + cell_types["$div"] = "ABSY"; + cell_types["$mod"] = "ABSY"; + // cell_types["$pow"] = "ABsY"; + + cell_types["$logic_not"] = "ASY"; + cell_types["$logic_and"] = "ABSY"; + cell_types["$logic_or"] = "ABSY"; + + // cell_types["$mux"] = "A"; + // cell_types["$pmux"] = "A"; + // cell_types["$slice"] = "A"; + // cell_types["$concat"] = "A"; + // cell_types["$assert"] = "A"; + + cell_types["$lut"] = "*"; + cell_types["$alu"] = "ABSY"; + cell_types["$lcu"] = "*"; + cell_types["$macc"] = "*"; + cell_types["$fa"] = "*"; + + for (; argidx < SIZE(args); argidx++) + { + if (args[argidx].rfind("-", 0) == 0) + log_cmd_error("Unexpected option: %s\n", args[argidx].c_str()); + + if (args[argidx] == "all") { + for (auto &it : cell_types) + if (std::count(selected_cell_types.begin(), selected_cell_types.end(), it.first) == 0) + selected_cell_types.push_back(it.first); + continue; + } + + if (cell_types.count(args[argidx]) == 0) { + std::string cell_type_list; + int charcount = 100; + for (auto &it : cell_types) { + if (charcount > 60) { + cell_type_list += "\n" + it.first; + charcount = 0; + } else + cell_type_list += " " + it.first; + charcount += SIZE(it.first); + } + log_cmd_error("The cell type `%s' is currently not supported. Try one of these:%s\n", + args[argidx].c_str(), cell_type_list.c_str()); + } + + if (std::count(selected_cell_types.begin(), selected_cell_types.end(), args[argidx]) == 0) + selected_cell_types.push_back(args[argidx]); + } + + if (!ilang_file.empty()) { + if (!selected_cell_types.empty()) + log_cmd_error("Do not specify any cell types when using -f.\n"); + selected_cell_types.push_back("ilang"); + } + + if (selected_cell_types.empty()) + log_cmd_error("No cell type to test specified.\n"); + + std::vector<std::string> uut_names; + + for (auto cell_type : selected_cell_types) + for (int i = 0; i < num_iter; i++) + { + RTLIL::Design *design = new RTLIL::Design; + if (cell_type == "ilang") + Frontend::frontend_call(design, NULL, std::string(), "ilang " + ilang_file); + else + create_gold_module(design, cell_type, cell_types.at(cell_type), constmode); + Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..; opt -fast gate", techmap_cmd.c_str())); + if (!nosat) + Pass::call(design, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter"); + if (verbose) + Pass::call(design, "dump gate"); + Pass::call(design, "dump gold"); + if (!nosat) + Pass::call(design, "sat -verify -enable_undef -prove trigger 0 -show-inputs -show-outputs miter"); + std::string uut_name = stringf("uut_%s_%d", cell_type.substr(1).c_str(), i); + if (vlog_file.is_open()) { + Pass::call(design, stringf("copy gold %s_expr; select %s_expr", uut_name.c_str(), uut_name.c_str())); + Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected"); + Pass::call(design, stringf("copy gold %s_noexpr; select %s_noexpr", uut_name.c_str(), uut_name.c_str())); + Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr"); + uut_names.push_back(uut_name); + } + run_eval_test(design, verbose, nosat, uut_name, vlog_file); + delete design; + } + + if (vlog_file.is_open()) { + vlog_file << "\nmodule testbench;\n"; + for (auto &uut : uut_names) + vlog_file << stringf(" %s %s ();\n", uut.c_str(), uut.c_str()); + vlog_file << " initial begin\n"; + for (auto &uut : uut_names) + vlog_file << " " << uut << ".run;\n"; + vlog_file << " end\n"; + vlog_file << "endmodule\n"; + } + } +} TestCellPass; + |