aboutsummaryrefslogtreecommitdiffstats
path: root/passes/tests
diff options
context:
space:
mode:
Diffstat (limited to 'passes/tests')
-rw-r--r--passes/tests/Makefile.inc5
-rw-r--r--passes/tests/test_abcloop.cc285
-rw-r--r--passes/tests/test_autotb.cc353
-rw-r--r--passes/tests/test_cell.cc745
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;
+