diff options
author | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 |
---|---|---|
committer | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 |
commit | d3c67ad9b61f602de1100cd264efd227dcacb417 (patch) | |
tree | 88c462c53bdab128cd1edbded42483772f82612a /passes | |
parent | b783dbe148e6d246ebd107c0913de2989ab5af48 (diff) | |
parent | 13117bb346dd02d2345f716b4403239aebe3d0e2 (diff) | |
download | yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.gz yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.bz2 yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.zip |
Merge branch 'master' of https://github.com/cliffordwolf/yosys into btor
added case for memwr cell that is used in muxes (same cell is used more than one time)
corrected bug for xnor and logic_not
added pmux cell translation
Conflicts:
backends/btor/btor.cc
Diffstat (limited to 'passes')
82 files changed, 10525 insertions, 3491 deletions
diff --git a/passes/abc/abc.cc b/passes/abc/abc.cc index 5aa13572e..f1d56b23a 100644 --- a/passes/abc/abc.cc +++ b/passes/abc/abc.cc @@ -29,65 +29,88 @@ // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558–562, doi:10.1145/368996.369025 // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; retime; balance; dch; map; topo" -#define ABC_COMMAND_CTR "strash; retime; balance; dch; map; topo; buffer; upsize; dnsize; stime" -#define ABC_COMMAND_LUT "strash; retime; balance; dch; if" -#define ABC_COMMAND_DFL "strash; retime; balance; dch; map" +#define ABC_COMMAND_LIB "strash; scorr -v; ifraig -v; retime -v {D}; strash; dch -vf; map -v {D}" +#define ABC_COMMAND_CTR "strash; scorr -v; ifraig -v; retime -v {D}; strash; dch -vf; map -v {D}; buffer -v; upsize -v {D}; dnsize -v {D}; stime -p" +#define ABC_COMMAND_LUT "strash; scorr -v; ifraig -v; retime -v; strash; dch -vf; if -v" +#define ABC_COMMAND_DFL "strash; scorr -v; ifraig -v; retime -v; strash; dch -vf; map -v" + +#define ABC_FAST_COMMAND_LIB "retime -v {D}; map -v {D}" +#define ABC_FAST_COMMAND_CTR "retime -v {D}; map -v {D}; buffer -v; upsize -v {D}; dnsize -v {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "retime -v; if -v" +#define ABC_FAST_COMMAND_DFL "retime -v; map -v" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include <unistd.h> #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <string.h> #include <dirent.h> +#include <cerrno> #include <sstream> +#include <climits> #include "blifparse.h" +enum class gate_type_t { + G_NONE, + G_FF, + G_NOT, + G_AND, + G_NAND, + G_OR, + G_NOR, + G_XOR, + G_XNOR, + G_MUX, + G_AOI3, + G_OAI3, + G_AOI4, + G_OAI4 +}; + +#define G(_name) gate_type_t::G_ ## _name + struct gate_t { int id; - char type; - int in1, in2, in3; + gate_type_t type; + int in1, in2, in3, in4; bool is_port; - RTLIL::SigSpec sig; + RTLIL::SigBit bit; }; static int map_autoidx; static SigMap assign_map; static RTLIL::Module *module; static std::vector<gate_t> signal_list; -static std::map<RTLIL::SigSpec, int> signal_map; +static std::map<RTLIL::SigBit, int> signal_map; static bool clk_polarity; static RTLIL::SigSpec clk_sig; -static int map_signal(RTLIL::SigSpec sig, char gate_type = -1, int in1 = -1, int in2 = -1, int in3 = -1) +static int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1) { - assert(sig.width == 1); - assert(sig.chunks.size() == 1); + assign_map.apply(bit); - assign_map.apply(sig); - - if (signal_map.count(sig) == 0) { + if (signal_map.count(bit) == 0) { gate_t gate; gate.id = signal_list.size(); - gate.type = -1; + gate.type = G(NONE); gate.in1 = -1; gate.in2 = -1; gate.in3 = -1; + gate.in4 = -1; gate.is_port = false; - gate.sig = sig; + gate.bit = bit; signal_list.push_back(gate); - signal_map[sig] = gate.id; + signal_map[bit] = gate.id; } - gate_t &gate = signal_list[signal_map[sig]]; + gate_t &gate = signal_list[signal_map[bit]]; - if (gate_type >= 0) + if (gate_type != G(NONE)) gate.type = gate_type; if (in1 >= 0) gate.in1 = in1; @@ -95,62 +118,64 @@ static int map_signal(RTLIL::SigSpec sig, char gate_type = -1, int in1 = -1, int gate.in2 = in2; if (in3 >= 0) gate.in3 = in3; + if (in4 >= 0) + gate.in4 = in4; return gate.id; } static void mark_port(RTLIL::SigSpec sig) { - assign_map.apply(sig); - sig.expand(); - for (auto &c : sig.chunks) { - if (c.wire != NULL && signal_map.count(c) > 0) - signal_list[signal_map[c]].is_port = true; - } + for (auto &bit : assign_map(sig)) + if (bit.wire != NULL && signal_map.count(bit) > 0) + signal_list[signal_map[bit]].is_port = true; } -static void extract_cell(RTLIL::Cell *cell) +static void extract_cell(RTLIL::Cell *cell, bool keepff) { if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") { if (clk_polarity != (cell->type == "$_DFF_P_")) return; - if (clk_sig != assign_map(cell->connections["\\C"])) + if (clk_sig != assign_map(cell->getPort("\\C"))) return; - RTLIL::SigSpec sig_d = cell->connections["\\D"]; - RTLIL::SigSpec sig_q = cell->connections["\\Q"]; + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); + + if (keepff) + for (auto &c : sig_q.chunks()) + if (c.wire != NULL) + c.wire->attributes["\\keep"] = 1; assign_map.apply(sig_d); assign_map.apply(sig_q); - map_signal(sig_q, 'f', map_signal(sig_d)); + map_signal(sig_q, G(FF), map_signal(sig_d)); - module->cells.erase(cell->name); - delete cell; + module->remove(cell); return; } - if (cell->type == "$_INV_") + if (cell->type == "$_NOT_") { - RTLIL::SigSpec sig_a = cell->connections["\\A"]; - RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); assign_map.apply(sig_a); assign_map.apply(sig_y); - map_signal(sig_y, 'n', map_signal(sig_a)); + map_signal(sig_y, G(NOT), map_signal(sig_a)); - module->cells.erase(cell->name); - delete cell; + module->remove(cell); return; } - if (cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) { - RTLIL::SigSpec sig_a = cell->connections["\\A"]; - RTLIL::SigSpec sig_b = cell->connections["\\B"]; - RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); assign_map.apply(sig_a); assign_map.apply(sig_b); @@ -160,25 +185,30 @@ static void extract_cell(RTLIL::Cell *cell) int mapped_b = map_signal(sig_b); if (cell->type == "$_AND_") - map_signal(sig_y, 'a', mapped_a, mapped_b); + map_signal(sig_y, G(AND), mapped_a, mapped_b); + else if (cell->type == "$_NAND_") + map_signal(sig_y, G(NAND), mapped_a, mapped_b); else if (cell->type == "$_OR_") - map_signal(sig_y, 'o', mapped_a, mapped_b); + map_signal(sig_y, G(OR), mapped_a, mapped_b); + else if (cell->type == "$_NOR_") + map_signal(sig_y, G(NOR), mapped_a, mapped_b); else if (cell->type == "$_XOR_") - map_signal(sig_y, 'x', mapped_a, mapped_b); + map_signal(sig_y, G(XOR), mapped_a, mapped_b); + else if (cell->type == "$_XNOR_") + map_signal(sig_y, G(XNOR), mapped_a, mapped_b); else log_abort(); - module->cells.erase(cell->name); - delete cell; + module->remove(cell); return; } if (cell->type == "$_MUX_") { - RTLIL::SigSpec sig_a = cell->connections["\\A"]; - RTLIL::SigSpec sig_b = cell->connections["\\B"]; - RTLIL::SigSpec sig_s = cell->connections["\\S"]; - RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_s = cell->getPort("\\S"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); assign_map.apply(sig_a); assign_map.apply(sig_b); @@ -189,15 +219,61 @@ static void extract_cell(RTLIL::Cell *cell) int mapped_b = map_signal(sig_b); int mapped_s = map_signal(sig_s); - map_signal(sig_y, 'm', mapped_a, mapped_b, mapped_s); + map_signal(sig_y, G(MUX), mapped_a, mapped_b, mapped_s); + + module->remove(cell); + return; + } + + if (cell->type.in("$_AOI3_", "$_OAI3_")) + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_c = cell->getPort("\\C"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_c); + assign_map.apply(sig_y); + + int mapped_a = map_signal(sig_a); + int mapped_b = map_signal(sig_b); + int mapped_c = map_signal(sig_c); + + map_signal(sig_y, cell->type == "$_AOI3_" ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); + + module->remove(cell); + return; + } + + if (cell->type.in("$_AOI4_", "$_OAI4_")) + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_c = cell->getPort("\\C"); + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_c); + assign_map.apply(sig_d); + assign_map.apply(sig_y); + + int mapped_a = map_signal(sig_a); + int mapped_b = map_signal(sig_b); + int mapped_c = map_signal(sig_c); + int mapped_d = map_signal(sig_d); + + map_signal(sig_y, cell->type == "$_AOI4_" ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); - module->cells.erase(cell->name); - delete cell; + module->remove(cell); return; } } -static std::string remap_name(std::string abc_name) +static std::string remap_name(RTLIL::IdString abc_name) { std::stringstream sstr; sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); @@ -211,8 +287,9 @@ static void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edge log("Dumping loop state graph to slide %d.\n", ++nr); - fprintf(f, "digraph slide%d {\n", nr); - fprintf(f, " rankdir=\"LR\";\n"); + fprintf(f, "digraph \"slide%d\" {\n", nr); + fprintf(f, " label=\"slide%d\";\n", nr); + fprintf(f, " rankdir=\"TD\";\n"); std::set<int> nodes; for (auto &e : edges) { @@ -222,7 +299,7 @@ static void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edge } for (auto n : nodes) - fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].sig), + fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit), n, in_counts[n], workpool.count(n) ? ", shape=box" : ""); for (auto &e : edges) @@ -248,7 +325,7 @@ static void handle_loops() // dot_f = fopen("test.dot", "w"); for (auto &g : signal_list) { - if (g.type == -1 || g.type == 'f') { + if (g.type == G(NONE) || g.type == G(FF)) { workpool.insert(g.id); } else { if (g.in1 >= 0) { @@ -263,6 +340,10 @@ static void handle_loops() edges[g.in3].insert(g.id); in_edges_count[g.id]++; } + if (g.in4 >= 0 && g.in4 != g.in3 && g.in4 != g.in2 && g.in4 != g.in1) { + edges[g.in4].insert(g.id); + in_edges_count[g.id]++; + } } } @@ -273,10 +354,10 @@ static void handle_loops() int id = *workpool.begin(); workpool.erase(id); - // log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].sig)); + // log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].bit)); for (int id2 : edges[id]) { - assert(in_edges_count[id2] > 0); + log_assert(in_edges_count[id2] > 0); if (--in_edges_count[id2] == 0) workpool.insert(id2); } @@ -293,12 +374,12 @@ static void handle_loops() for (auto &edge_it : edges) { int id2 = edge_it.first; - RTLIL::Wire *w1 = signal_list[id1].sig.chunks[0].wire; - RTLIL::Wire *w2 = signal_list[id2].sig.chunks[0].wire; - if (w1 != NULL) - continue; - else if (w2 == NULL) + RTLIL::Wire *w1 = signal_list[id1].bit.wire; + RTLIL::Wire *w2 = signal_list[id2].bit.wire; + if (w1 == NULL) id1 = id2; + else if (w2 == NULL) + continue; else if (w1->name[0] == '$' && w2->name[0] == '\\') id1 = id2; else if (w1->name[0] == '\\' && w2->name[0] == '$') @@ -307,7 +388,7 @@ static void handle_loops() id1 = id2; else if (edges[id1].size() > edges[id2].size()) continue; - else if (w1->name > w2->name) + else if (w2->name.str() < w1->name.str()) id1 = id2; } @@ -316,27 +397,27 @@ static void handle_loops() continue; } - RTLIL::Wire *wire = new RTLIL::Wire; + log_assert(signal_list[id1].bit.wire != NULL); + std::stringstream sstr; - sstr << "$abcloop$" << (RTLIL::autoidx++); - wire->name = sstr.str(); - module->wires[wire->name] = wire; + sstr << "$abcloop$" << (autoidx++); + RTLIL::Wire *wire = module->addWire(sstr.str()); bool first_line = true; for (int id2 : edges[id1]) { if (first_line) log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)), - log_signal(signal_list[id1].sig), log_signal(signal_list[id2].sig)); + log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit)); else log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "", - log_signal(signal_list[id1].sig), log_signal(signal_list[id2].sig)); + log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit)); first_line = false; } int id3 = map_signal(RTLIL::SigSpec(wire)); signal_list[id1].is_port = true; signal_list[id3].is_port = true; - assert(id3 == int(in_edges_count.size())); + log_assert(id3 == int(in_edges_count.size())); in_edges_count.push_back(0); workpool.insert(id3); @@ -347,10 +428,12 @@ static void handle_loops() signal_list[id2].in2 = id3; if (signal_list[id2].in3 == id1) signal_list[id2].in3 = id3; + if (signal_list[id2].in4 == id1) + signal_list[id2].in4 = id3; } edges[id1].swap(edges[id3]); - module->connections.push_back(RTLIL::SigSig(signal_list[id3].sig, signal_list[id1].sig)); + module->connect(RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); } } @@ -359,11 +442,55 @@ static void handle_loops() fclose(dot_f); } +static std::string add_echos_to_abc_cmd(std::string str) +{ + std::string new_str, token; + for (size_t i = 0; i < str.size(); i++) { + token += str[i]; + if (str[i] == ';') { + while (i+1 < str.size() && str[i+1] == ' ') + i++; + if (!new_str.empty()) + new_str += "echo; "; + new_str += "echo + " + token + " " + token + " "; + token.clear(); + } + } + + if (!token.empty()) { + if (!new_str.empty()) + new_str += "echo; echo + " + token + "; "; + new_str += token; + } + + return new_str; +} + +static std::string fold_abc_cmd(std::string str) +{ + std::string token, new_str = " "; + int char_counter = 10; + + for (size_t i = 0; i <= str.size(); i++) { + if (i < str.size()) + token += str[i]; + if (i == str.size() || str[i] == ';') { + if (char_counter + token.size() > 75) + new_str += "\n ", char_counter = 14; + new_str += token, char_counter += token.size(); + token.clear(); + } + } + + return new_str; +} + static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::string liberty_file, std::string constr_file, bool cleanup, int lut_mode, bool dff_mode, std::string clk_str) + std::string liberty_file, std::string constr_file, bool cleanup, int lut_mode, bool dff_mode, std::string clk_str, + bool keepff, std::string delay_target, bool fast_mode) { module = current_module; - map_autoidx = RTLIL::autoidx++; + map_autoidx = autoidx++; signal_map.clear(); signal_list.clear(); @@ -393,33 +520,48 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } else abc_command = stringf("source %s", script_file.c_str()); } else if (lut_mode) - abc_command = ABC_COMMAND_LUT; + abc_command = fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; else if (!liberty_file.empty()) - abc_command = constr_file.empty() ? ABC_COMMAND_LIB : ABC_COMMAND_CTR; + abc_command = constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); else - abc_command = ABC_COMMAND_DFL; + abc_command = fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + + for (size_t pos = abc_command.find("{D}"); pos != std::string::npos; pos = abc_command.find("{D}", pos)) + abc_command = abc_command.substr(0, pos) + delay_target + abc_command.substr(pos+3); + + abc_command = add_echos_to_abc_cmd(abc_command); + + if (abc_command.size() > 128) { + for (size_t i = 0; i+1 < abc_command.size(); i++) + if (abc_command[i] == ';' && abc_command[i+1] == ' ') + abc_command[i+1] = '\n'; + FILE *f = fopen(stringf("%s/abc.script", tempdir_name).c_str(), "wt"); + fprintf(f, "%s\n", abc_command.c_str()); + fclose(f); + abc_command = stringf("source %s/abc.script", tempdir_name); + } if (clk_str.empty()) { if (clk_str[0] == '!') { clk_polarity = false; clk_str = clk_str.substr(1); } - if (module->wires.count(RTLIL::escape_id(clk_str)) != 0) - clk_sig = assign_map(RTLIL::SigSpec(module->wires.at(RTLIL::escape_id(clk_str)), 1)); + if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) + clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); } - if (dff_mode && clk_sig.width == 0) + if (dff_mode && clk_sig.size() == 0) { int best_dff_counter = 0; std::map<std::pair<bool, RTLIL::SigSpec>, int> dff_counters; - for (auto &it : module->cells) + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (cell->type != "$_DFF_N_" && cell->type != "$_DFF_P_") continue; - std::pair<bool, RTLIL::SigSpec> key(cell->type == "$_DFF_P_", assign_map(cell->connections.at("\\C"))); + std::pair<bool, RTLIL::SigSpec> key(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C"))); if (++dff_counters[key] > best_dff_counter) { best_dff_counter = dff_counters[key]; clk_polarity = key.first; @@ -429,30 +571,30 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (dff_mode || !clk_str.empty()) { - if (clk_sig.width == 0) + if (clk_sig.size() == 0) log("No (matching) clock domain found. Not extracting any FF cells.\n"); else log("Found (matching) %s clock domain: %s\n", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig)); } - if (clk_sig.width != 0) + if (clk_sig.size() != 0) mark_port(clk_sig); std::vector<RTLIL::Cell*> cells; - cells.reserve(module->cells.size()); - for (auto &it : module->cells) + cells.reserve(module->cells_.size()); + for (auto &it : module->cells_) if (design->selected(current_module, it.second)) cells.push_back(it.second); for (auto c : cells) - extract_cell(c); + extract_cell(c, keepff); - for (auto &wire_it : module->wires) { + for (auto &wire_it : module->wires_) { if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute("\\keep")) mark_port(RTLIL::SigSpec(wire_it.second)); } - for (auto &cell_it : module->cells) - for (auto &port_it : cell_it.second->connections) + for (auto &cell_it : module->cells_) + for (auto &port_it : cell_it.second->connections()) mark_port(port_it.second); handle_loops(); @@ -468,17 +610,19 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std int count_input = 0; fprintf(f, ".inputs"); for (auto &si : signal_list) { - if (!si.is_port || si.type >= 0) + if (!si.is_port || si.type != G(NONE)) continue; fprintf(f, " n%d", si.id); count_input++; } + if (count_input == 0) + fprintf(f, " dummy_input\n"); fprintf(f, "\n"); int count_output = 0; fprintf(f, ".outputs"); for (auto &si : signal_list) { - if (!si.is_port || si.type < 0) + if (!si.is_port || si.type == G(NONE)) continue; fprintf(f, " n%d", si.id); count_output++; @@ -486,42 +630,70 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std fprintf(f, "\n"); for (auto &si : signal_list) - fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.sig)); + fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit)); for (auto &si : signal_list) { - assert(si.sig.width == 1 && si.sig.chunks.size() == 1); - if (si.sig.chunks[0].wire == NULL) { + if (si.bit.wire == NULL) { fprintf(f, ".names n%d\n", si.id); - if (si.sig.chunks[0].data.bits[0] == RTLIL::State::S1) + if (si.bit == RTLIL::State::S1) fprintf(f, "1\n"); } } int count_gates = 0; for (auto &si : signal_list) { - if (si.type == 'n') { + if (si.type == G(NOT)) { fprintf(f, ".names n%d n%d\n", si.in1, si.id); fprintf(f, "0 1\n"); - } else if (si.type == 'a') { + } else if (si.type == G(AND)) { fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); fprintf(f, "11 1\n"); - } else if (si.type == 'o') { + } else if (si.type == G(NAND)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "0- 1\n"); + fprintf(f, "-0 1\n"); + } else if (si.type == G(OR)) { fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); fprintf(f, "-1 1\n"); fprintf(f, "1- 1\n"); - } else if (si.type == 'x') { + } else if (si.type == G(NOR)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "00 1\n"); + } else if (si.type == G(XOR)) { fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); fprintf(f, "01 1\n"); fprintf(f, "10 1\n"); - } else if (si.type == 'm') { + } else if (si.type == G(XNOR)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "00 1\n"); + fprintf(f, "11 1\n"); + } else if (si.type == G(MUX)) { fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); fprintf(f, "1-0 1\n"); fprintf(f, "-11 1\n"); - } else if (si.type == 'f') { + } else if (si.type == G(AOI3)) { + fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, "-00 1\n"); + fprintf(f, "0-0 1\n"); + } else if (si.type == G(OAI3)) { + fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, "00- 1\n"); + fprintf(f, "--0 1\n"); + } else if (si.type == G(AOI4)) { + fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); + fprintf(f, "-0-0 1\n"); + fprintf(f, "-00- 1\n"); + fprintf(f, "0--0 1\n"); + fprintf(f, "0-0- 1\n"); + } else if (si.type == G(OAI4)) { + fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); + fprintf(f, "00-- 1\n"); + fprintf(f, "--00 1\n"); + } else if (si.type == G(FF)) { fprintf(f, ".latch n%d n%d\n", si.in1, si.id); - } else if (si.type >= 0) + } else if (si.type != G(NONE)) log_abort(); - if (si.type >= 0) + if (si.type != G(NONE)) count_gates++; } @@ -543,11 +715,18 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); fprintf(f, "GATE ONE 1 Y=CONST1;\n"); fprintf(f, "GATE BUF 1 Y=A; PIN * NONINV 1 999 1 0 1 0\n"); - fprintf(f, "GATE INV 1 Y=!A; PIN * INV 1 999 1 0 1 0\n"); + fprintf(f, "GATE NOT 1 Y=!A; PIN * INV 1 999 1 0 1 0\n"); fprintf(f, "GATE AND 1 Y=A*B; PIN * NONINV 1 999 1 0 1 0\n"); + fprintf(f, "GATE NAND 1 Y=!(A*B); PIN * INV 1 999 1 0 1 0\n"); fprintf(f, "GATE OR 1 Y=A+B; PIN * NONINV 1 999 1 0 1 0\n"); + fprintf(f, "GATE NOR 1 Y=!(A+B); PIN * INV 1 999 1 0 1 0\n"); fprintf(f, "GATE XOR 1 Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n"); + fprintf(f, "GATE XNOR 1 Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n"); fprintf(f, "GATE MUX 1 Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n"); + fprintf(f, "GATE AOI3 1 Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n"); + fprintf(f, "GATE OAI3 1 Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n"); + fprintf(f, "GATE AOI4 1 Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n"); + fprintf(f, "GATE OAI4 1 Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n"); fclose(f); free(p); @@ -564,10 +743,10 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std std::string buffer; if (!liberty_file.empty()) { - buffer += stringf("%s -s -c 'read_blif %s/input.blif; read_lib %s; ", + buffer += stringf("%s -s -c 'read_blif %s/input.blif; read_lib -w %s; ", exe_file.c_str(), tempdir_name, liberty_file.c_str()); if (!constr_file.empty()) - buffer += stringf("read_constr %s; ", constr_file.c_str()); + buffer += stringf("read_constr -v %s; ", constr_file.c_str()); buffer += abc_command + "; "; } else if (lut_mode) @@ -578,13 +757,53 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std exe_file.c_str(), tempdir_name, tempdir_name, abc_command.c_str()); buffer += stringf("write_blif %s/output.blif' 2>&1", tempdir_name); + log("%s\n", buffer.c_str()); + errno = ENOMEM; // popen does not set errno if memory allocation fails, therefore set it by hand f = popen(buffer.c_str(), "r"); if (f == NULL) log_error("Opening pipe to `%s' for reading failed: %s\n", buffer.c_str(), strerror(errno)); +#if 0 char logbuf[1024]; while (fgets(logbuf, 1024, f) != NULL) log("ABC: %s", logbuf); +#else + bool got_cr = false; + int escape_seq_state = 0; + std::string linebuf; + char logbuf[1024]; + while (fgets(logbuf, 1024, f) != NULL) + for (char *p = logbuf; *p; p++) { + if (escape_seq_state == 0 && *p == '\033') { + escape_seq_state = 1; + continue; + } + if (escape_seq_state == 1) { + escape_seq_state = *p == '[' ? 2 : 0; + continue; + } + if (escape_seq_state == 2) { + if ((*p < '0' || '9' < *p) && *p != ';') + escape_seq_state = 0; + continue; + } + escape_seq_state = 0; + if (*p == '\r') { + got_cr = true; + continue; + } + if (*p == '\n') { + log("ABC: %s\n", linebuf.c_str()); + got_cr = false, linebuf.clear(); + continue; + } + if (got_cr) + got_cr = false, linebuf.clear(); + linebuf += *p; + } + if (!linebuf.empty()) + log("ABC: %s\n", linebuf.c_str()); +#endif errno = 0; int ret = pclose(f); if (ret < 0) @@ -609,79 +828,84 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std free(p); log_header("Re-integrating ABC results.\n"); - RTLIL::Module *mapped_mod = mapped_design->modules["\\netlist"]; + RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"]; if (mapped_mod == NULL) log_error("ABC output file does not contain a module `netlist'.\n"); - for (auto &it : mapped_mod->wires) { + for (auto &it : mapped_mod->wires_) { RTLIL::Wire *w = it.second; - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = remap_name(w->name); - module->wires[wire->name] = wire; + RTLIL::Wire *wire = module->addWire(remap_name(w->name)); design->select(module, wire); } std::map<std::string, int> cell_stats; if (builtin_lib) { - for (auto &it : mapped_mod->cells) { + for (auto &it : mapped_mod->cells_) { RTLIL::Cell *c = it.second; cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type == "\\ZERO" || c->type == "\\ONE") { RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]); conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1); - module->connections.push_back(conn); + module->connect(conn); continue; } if (c->type == "\\BUF") { RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); - conn.second = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); - module->connections.push_back(conn); + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]); + conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]); + module->connect(conn); continue; } - if (c->type == "\\INV") { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->type = "$_INV_"; - cell->name = remap_name(c->name); - cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); - cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); - module->cells[cell->name] = cell; + if (c->type == "\\NOT") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_NOT_"); + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR") { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->type = "$_" + c->type.substr(1) + "_"; - cell->name = remap_name(c->name); - cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); - cell->connections["\\B"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\B"].chunks[0].wire->name)]); - cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); - module->cells[cell->name] = cell; + if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || c->type == "\\XNOR") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } if (c->type == "\\MUX") { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->type = "$_MUX_"; - cell->name = remap_name(c->name); - cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); - cell->connections["\\B"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\B"].chunks[0].wire->name)]); - cell->connections["\\S"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\S"].chunks[0].wire->name)]); - cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); - module->cells[cell->name] = cell; + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX_"); + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); + continue; + } + if (c->type == "\\AOI3" || c->type == "\\OAI3") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); + continue; + } + if (c->type == "\\AOI4" || c->type == "\\OAI4") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } if (c->type == "\\DFF") { - log_assert(clk_sig.width == 1); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->type = clk_polarity ? "$_DFF_P_" : "$_DFF_N_"; - cell->name = remap_name(c->name); - cell->connections["\\D"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\D"].chunks[0].wire->name)]); - cell->connections["\\Q"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Q"].chunks[0].wire->name)]); - cell->connections["\\C"] = clk_sig; - module->cells[cell->name] = cell; + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); + cell->setPort("\\C", clk_sig); design->select(module, cell); continue; } @@ -690,54 +914,48 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } else { - for (auto &it : mapped_mod->cells) + for (auto &it : mapped_mod->cells_) { RTLIL::Cell *c = it.second; cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type == "\\_const0_" || c->type == "\\_const1_") { RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections.begin()->second.chunks[0].wire->name)]); + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); - module->connections.push_back(conn); + module->connect(conn); continue; } if (c->type == "\\_dff_") { - log_assert(clk_sig.width == 1); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->type = clk_polarity ? "$_DFF_P_" : "$_DFF_N_"; - cell->name = remap_name(c->name); - cell->connections["\\D"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\D"].chunks[0].wire->name)]); - cell->connections["\\Q"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Q"].chunks[0].wire->name)]); - cell->connections["\\C"] = clk_sig; - module->cells[cell->name] = cell; + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); + cell->setPort("\\C", clk_sig); design->select(module, cell); continue; } - RTLIL::Cell *cell = new RTLIL::Cell; - cell->type = c->type; + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); cell->parameters = c->parameters; - cell->name = remap_name(c->name); - for (auto &conn : c->connections) { + for (auto &conn : c->connections()) { RTLIL::SigSpec newsig; - for (auto &c : conn.second.chunks) { + for (auto &c : conn.second.chunks()) { if (c.width == 0) continue; - assert(c.width == 1); - newsig.append(module->wires[remap_name(c.wire->name)]); + log_assert(c.width == 1); + newsig.append(module->wires_[remap_name(c.wire->name)]); } - cell->connections[conn.first] = newsig; + cell->setPort(conn.first, newsig); } - module->cells[cell->name] = cell; design->select(module, cell); } } - for (auto conn : mapped_mod->connections) { + for (auto conn : mapped_mod->connections()) { if (!conn.first.is_fully_const()) - conn.first = RTLIL::SigSpec(module->wires[remap_name(conn.first.chunks[0].wire->name)]); + conn.first = RTLIL::SigSpec(module->wires_[remap_name(conn.first.as_wire()->name)]); if (!conn.second.is_fully_const()) - conn.second = RTLIL::SigSpec(module->wires[remap_name(conn.second.chunks[0].wire->name)]); - module->connections.push_back(conn); + conn.second = RTLIL::SigSpec(module->wires_[remap_name(conn.second.as_wire()->name)]); + module->connect(conn); } for (auto &it : cell_stats) @@ -748,16 +966,16 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std char buffer[100]; snprintf(buffer, 100, "\\n%d", si.id); RTLIL::SigSig conn; - if (si.type >= 0) { - conn.first = si.sig; - conn.second = RTLIL::SigSpec(module->wires[remap_name(buffer)]); + if (si.type != G(NONE)) { + conn.first = si.bit; + conn.second = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); out_wires++; } else { - conn.first = RTLIL::SigSpec(module->wires[remap_name(buffer)]); - conn.second = si.sig; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + conn.second = si.bit; in_wires++; } - module->connections.push_back(conn); + module->connect(conn); } log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); log("ABC RESULTS: input signals: %8d\n", in_wires); @@ -776,7 +994,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std struct dirent **namelist; int n = scandir(tempdir_name, &namelist, 0, alphasort); - assert(n >= 0); + log_assert(n >= 0); for (int i = 0; i < n; i++) { if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) { if (asprintf(&p, "%s/%s", tempdir_name, namelist[i]->d_name) < 0) log_abort(); @@ -820,16 +1038,32 @@ struct AbcPass : public Pass { log(" if no -script parameter is given, the following scripts are used:\n"); log("\n"); log(" for -liberty without -constr:\n"); - log(" %s\n", ABC_COMMAND_LIB); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LIB).c_str()); + log("\n"); + log(" for -liberty with -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); + log("\n"); + log(" for -lut:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); + log("\n"); + log(" otherwise:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).c_str()); + log("\n"); + log(" -fast\n"); + log(" use different default scripts that are slightly faster (at the cost\n"); + log(" of output quality):\n"); + log("\n"); + log(" for -liberty without -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LIB).c_str()); log("\n"); log(" for -liberty with -constr:\n"); - log(" %s\n", ABC_COMMAND_CTR); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); log("\n"); log(" for -lut:\n"); - log(" %s\n", ABC_COMMAND_LUT); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); log("\n"); log(" otherwise:\n"); - log(" %s\n", ABC_COMMAND_DFL); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str()); log("\n"); log(" -liberty <file>\n"); log(" generate netlists for the specified cell library (using the liberty\n"); @@ -838,6 +1072,18 @@ struct AbcPass : public Pass { log(" -constr <file>\n"); log(" pass this file with timing constraints to ABC. use with -liberty.\n"); log("\n"); + log(" a constr file contains two lines:\n"); + log(" set_driving_cell <cell_name>\n"); + log(" set_load <floating_point_number>\n"); + log("\n"); + log(" the set_driving_cell statement defines which cell type is assumed to\n"); + log(" drive the primary inputs and the set_load statement sets the load in\n"); + log(" femtofarads for each primary output.\n"); + log("\n"); + log(" -D <picoseconds>\n"); + log(" set delay target. the string {D} in the default scripts above is\n"); + log(" replaced by this option when used, and an empty string otherwise.\n"); + log("\n"); log(" -lut <width>\n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); @@ -851,6 +1097,10 @@ struct AbcPass : public Pass { log(" with -dff, then it falls back to the automatic dection of clock domain\n"); log(" if the specified clock is not found in a module.)\n"); log("\n"); + log(" -keepff\n"); + log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); + log(" them, for example for equivialence checking.)\n"); + log("\n"); log(" -nocleanup\n"); log(" when this option is used, the temporary files created by this pass\n"); log(" are not removed. this is useful for debugging.\n"); @@ -869,13 +1119,17 @@ struct AbcPass : public Pass { log_header("Executing ABC pass (technology mapping using ABC).\n"); log_push(); - std::string exe_file = rewrite_yosys_exe("yosys-abc"); - std::string script_file, liberty_file, constr_file, clk_str; - bool dff_mode = false, cleanup = true; + std::string exe_file = proc_self_dirname() + "yosys-abc"; + std::string script_file, liberty_file, constr_file, clk_str, delay_target; + bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; int lut_mode = 0; size_t argidx; - char *pwd = get_current_dir_name(); + char pwd [PATH_MAX]; + if (!getcwd(pwd, sizeof(pwd))) { + log_cmd_error("getcwd failed: %s\n", strerror(errno)); + log_abort(); + } for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exe" && argidx+1 < args.size()) { @@ -900,10 +1154,18 @@ struct AbcPass : public Pass { constr_file = std::string(pwd) + "/" + constr_file; continue; } + if (arg == "-D" && argidx+1 < args.size()) { + delay_target = "-D " + args[++argidx]; + continue; + } if (arg == "-lut" && argidx+1 < args.size()) { lut_mode = atoi(args[++argidx].c_str()); continue; } + if (arg == "-fast") { + fast_mode = true; + continue; + } if (arg == "-dff") { dff_mode = true; continue; @@ -912,13 +1174,16 @@ struct AbcPass : public Pass { clk_str = args[++argidx]; continue; } + if (arg == "-keepff") { + keepff = true; + continue; + } if (arg == "-nocleanup") { cleanup = false; continue; } break; } - free(pwd); extra_args(args, argidx, design); if (lut_mode != 0 && !liberty_file.empty()) @@ -926,12 +1191,12 @@ struct AbcPass : public Pass { if (!constr_file.empty() && liberty_file.empty()) log_cmd_error("Got -constr but no -liberty!\n"); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { if (mod_it.second->processes.size() > 0) log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str()); else - abc_module(design, mod_it.second, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, dff_mode, clk_str); + abc_module(design, mod_it.second, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, dff_mode, clk_str, keepff, delay_target, fast_mode); } assign_map.clear(); diff --git a/passes/abc/blifparse.cc b/passes/abc/blifparse.cc index 2d46d1a8e..1fbb5720d 100644 --- a/passes/abc/blifparse.cc +++ b/passes/abc/blifparse.cc @@ -40,7 +40,7 @@ static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, } if (buffer_len == 0 || buffer[buffer_len-1] == '\\') { - if (buffer[buffer_len-1] == '\\') + if (buffer_len > 0 && buffer[buffer_len-1] == '\\') buffer[--buffer_len] = 0; line_count++; if (fgets(buffer+buffer_len, buffer_size-buffer_len, f) == NULL) @@ -58,9 +58,8 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) RTLIL::Const *lutptr = NULL; RTLIL::State lut_default_state = RTLIL::State::Sx; - int port_count = 0; module->name = "\\netlist"; - design->modules[module->name] = module; + design->add(module); size_t buffer_size = 4096; char *buffer = (char*)malloc(buffer_size); @@ -91,6 +90,7 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) continue; if (!strcmp(cmd, ".end")) { + module->fixup_ports(); free(buffer); return design; } @@ -98,14 +98,11 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) { char *p; while ((p = strtok(NULL, " \t\r\n")) != NULL) { - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = stringf("\\%s", p); - wire->port_id = ++port_count; + RTLIL::Wire *wire = module->addWire(stringf("\\%s", p)); if (!strcmp(cmd, ".inputs")) wire->port_input = true; else wire->port_output = true; - module->add(wire); } continue; } @@ -115,49 +112,34 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) char *d = strtok(NULL, " \t\r\n"); char *q = strtok(NULL, " \t\r\n"); - if (module->wires.count(RTLIL::escape_id(d)) == 0) { - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = RTLIL::escape_id(d); - module->add(wire); - } + if (module->wires_.count(RTLIL::escape_id(d)) == 0) + module->addWire(RTLIL::escape_id(d)); - if (module->wires.count(RTLIL::escape_id(q)) == 0) { - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = RTLIL::escape_id(q); - module->add(wire); - } + if (module->wires_.count(RTLIL::escape_id(q)) == 0) + module->addWire(RTLIL::escape_id(q)); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = dff_name; - cell->connections["\\D"] = module->wires.at(RTLIL::escape_id(d)); - cell->connections["\\Q"] = module->wires.at(RTLIL::escape_id(q)); - module->add(cell); + RTLIL::Cell *cell = module->addCell(NEW_ID, dff_name); + cell->setPort("\\D", module->wires_.at(RTLIL::escape_id(d))); + cell->setPort("\\Q", module->wires_.at(RTLIL::escape_id(q))); continue; } if (!strcmp(cmd, ".gate")) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - module->add(cell); - char *p = strtok(NULL, " \t\r\n"); if (p == NULL) goto error; - cell->type = RTLIL::escape_id(p); + + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(p)); while ((p = strtok(NULL, " \t\r\n")) != NULL) { char *q = strchr(p, '='); if (q == NULL || !q[0] || !q[1]) goto error; *(q++) = 0; - if (module->wires.count(RTLIL::escape_id(q)) == 0) { - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = RTLIL::escape_id(q); - module->add(wire); - } - cell->connections[RTLIL::escape_id(p)] = module->wires.at(RTLIL::escape_id(q)); + if (module->wires_.count(RTLIL::escape_id(q)) == 0) + module->addWire(RTLIL::escape_id(q)); + cell->setPort(RTLIL::escape_id(p), module->wires_.at(RTLIL::escape_id(q))); } continue; } @@ -168,19 +150,17 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) RTLIL::SigSpec input_sig, output_sig; while ((p = strtok(NULL, " \t\r\n")) != NULL) { RTLIL::Wire *wire; - if (module->wires.count(stringf("\\%s", p)) > 0) { - wire = module->wires.at(stringf("\\%s", p)); + if (module->wires_.count(stringf("\\%s", p)) > 0) { + wire = module->wires_.at(stringf("\\%s", p)); } else { - wire = new RTLIL::Wire; - wire->name = stringf("\\%s", p); - module->add(wire); + wire = module->addWire(stringf("\\%s", p)); } input_sig.append(wire); } - output_sig = input_sig.extract(input_sig.width-1, 1); - input_sig = input_sig.extract(0, input_sig.width-1); + output_sig = input_sig.extract(input_sig.size()-1, 1); + input_sig = input_sig.extract(0, input_sig.size()-1); - if (input_sig.width == 0) { + if (input_sig.size() == 0) { RTLIL::State state = RTLIL::State::Sa; while (1) { if (!read_next_line(buffer, buffer_size, line_count, f)) @@ -208,23 +188,17 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) finished_parsing_constval: if (state == RTLIL::State::Sa) state = RTLIL::State::S1; - module->connections.push_back(RTLIL::SigSig(output_sig, state)); + module->connect(RTLIL::SigSig(output_sig, state)); goto continue_without_read; } - input_sig.optimize(); - output_sig.optimize(); - - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$lut"; - cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.width); - cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.width); - cell->connections["\\I"] = input_sig; - cell->connections["\\O"] = output_sig; + RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut"); + cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size()); + cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size()); + cell->setPort("\\A", input_sig); + cell->setPort("\\Y", output_sig); lutptr = &cell->parameters.at("\\LUT"); lut_default_state = RTLIL::State::Sx; - module->add(cell); continue; } diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index f01a1c4b5..eba61d1df 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -15,4 +15,10 @@ OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o OBJS += passes/cmds/log.o +OBJS += passes/cmds/tee.o +OBJS += passes/cmds/write_file.o +OBJS += passes/cmds/connwrappers.o +OBJS += passes/cmds/cover.o +OBJS += passes/cmds/trace.o +OBJS += passes/cmds/plugin.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index acee4c46f..e3fde8559 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -28,8 +28,8 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n if (module->count_id(name) != 0) { - if (module->wires.count(name) > 0) - wire = module->wires.at(name); + if (module->wires_.count(name) > 0) + wire = module->wires_.at(name); if (wire != NULL && wire->width != width) wire = NULL; @@ -47,15 +47,12 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n } else { - wire = new RTLIL::Wire; - wire->name = name; - wire->width = width; + wire = module->addWire(name, width); wire->port_input = flag_input; wire->port_output = flag_output; - module->add(wire); if (flag_input || flag_output) { - wire->port_id = module->wires.size(); + wire->port_id = module->wires_.size(); module->fixup_ports(); } @@ -65,20 +62,20 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n if (!flag_global) return; - for (auto &it : module->cells) + for (auto &it : module->cells_) { - if (design->modules.count(it.second->type) == 0) + if (design->modules_.count(it.second->type) == 0) continue; - RTLIL::Module *mod = design->modules.at(it.second->type); + RTLIL::Module *mod = design->modules_.at(it.second->type); if (!design->selected_whole_module(mod->name)) continue; if (mod->get_bool_attribute("\\blackbox")) continue; - if (it.second->connections.count(name) > 0) + if (it.second->hasPort(name)) continue; - it.second->connections[name] = wire; + it.second->setPort(name, wire); log("Added connection %s to cell %s.%s (%s).\n", name.c_str(), module->name.c_str(), it.first.c_str(), it.second->type.c_str()); } } @@ -139,7 +136,7 @@ struct AddPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod : design->modules) + for (auto &mod : design->modules_) { RTLIL::Module *module = mod.second; if (!design->selected_whole_module(module->name)) diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index 7da2b9517..30c80f732 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -27,14 +27,14 @@ static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap & { CellTypes ct(design); - RTLIL::Wire *dummy_wire = module->new_wire(sig.width, NEW_ID); + RTLIL::Wire *dummy_wire = module->addWire(NEW_ID, sig.size()); - for (auto &it : module->cells) - for (auto &port : it.second->connections) + for (auto &it : module->cells_) + for (auto &port : it.second->connections_) if (ct.cell_output(it.second->type, port.first)) sigmap(port.second).replace(sig, dummy_wire, &port.second); - for (auto &conn : module->connections) + for (auto &conn : module->connections_) sigmap(conn.first).replace(sig, dummy_wire, &conn.first); } @@ -75,7 +75,7 @@ struct ConnectPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { RTLIL::Module *module = NULL; - for (auto &it : design->modules) { + for (auto &it : design->modules_) { if (!design->selected(it.second)) continue; if (module != NULL) @@ -123,7 +123,7 @@ struct ConnectPass : public Pass { SigMap sigmap; if (!flag_nomap) - for (auto &it : module->connections) { + for (auto &it : module->connections()) { std::vector<RTLIL::SigBit> lhs = it.first.to_sigbit_vector(); std::vector<RTLIL::SigBit> rhs = it.first.to_sigbit_vector(); for (size_t i = 0; i < lhs.size(); i++) @@ -148,7 +148,7 @@ struct ConnectPass : public Pass { if (!flag_nounset) unset_drivers(design, module, sigmap, sig_lhs); - module->connections.push_back(RTLIL::SigSig(sig_lhs, sig_rhs)); + module->connect(RTLIL::SigSig(sig_lhs, sig_rhs)); } else if (!unset_expr.empty()) @@ -169,14 +169,14 @@ struct ConnectPass : public Pass { if (flag_nounset) log_cmd_error("Cant use -port together with -nounset.\n"); - if (module->cells.count(RTLIL::escape_id(port_cell)) == 0) + if (module->cells_.count(RTLIL::escape_id(port_cell)) == 0) log_cmd_error("Can't find cell %s.\n", port_cell.c_str()); RTLIL::SigSpec sig; if (!RTLIL::SigSpec::parse_sel(sig, design, module, port_expr)) log_cmd_error("Failed to parse port expression `%s'.\n", port_expr.c_str()); - module->cells.at(RTLIL::escape_id(port_cell))->connections[RTLIL::escape_id(port_port)] = sigmap(sig); + module->cells_.at(RTLIL::escape_id(port_cell))->setPort(RTLIL::escape_id(port_port), sigmap(sig)); } else log_cmd_error("Expected -set, -unset, or -port.\n"); diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc new file mode 100644 index 000000000..aac117169 --- /dev/null +++ b/passes/cmds/connwrappers.cc @@ -0,0 +1,205 @@ +/* + * 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/register.h" +#include "kernel/sigtools.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +struct ConnwrappersWorker +{ + struct portdecl_t { + // key: celltype, portname; + std::string widthparam, signparam; + bool is_signed; + }; + + std::set<RTLIL::IdString> decl_celltypes; + std::map<std::pair<RTLIL::IdString, RTLIL::IdString>, portdecl_t> decls; + + void add_port(std::string celltype, std::string portname, std::string widthparam, std::string signparam) + { + std::pair<std::string, std::string> key(RTLIL::escape_id(celltype), RTLIL::escape_id(portname)); + decl_celltypes.insert(key.first); + + if (decls.count(key)) + log_cmd_error("Duplicate port decl: %s %s\n", celltype.c_str(), portname.c_str()); + + portdecl_t decl; + decl.widthparam = RTLIL::escape_id(widthparam); + decl.signparam = RTLIL::escape_id(signparam); + decl.is_signed = false; + decls[key] = decl; + } + + void add_port(std::string celltype, std::string portname, std::string widthparam, bool is_signed) + { + std::pair<std::string, std::string> key(RTLIL::escape_id(celltype), RTLIL::escape_id(portname)); + decl_celltypes.insert(key.first); + + if (decls.count(key)) + log_cmd_error("Duplicate port decl: %s %s\n", celltype.c_str(), portname.c_str()); + + portdecl_t decl; + decl.widthparam = RTLIL::escape_id(widthparam); + decl.is_signed = is_signed; + decls[key] = decl; + } + + void work(RTLIL::Design *design, RTLIL::Module *module) + { + std::map<RTLIL::SigBit, std::pair<bool, RTLIL::SigSpec>> extend_map; + SigMap sigmap(module); + + for (auto &it : module->cells_) + { + RTLIL::Cell *cell = it.second; + + if (!decl_celltypes.count(cell->type)) + continue; + + for (auto &conn : cell->connections()) + { + std::pair<RTLIL::IdString, RTLIL::IdString> key(cell->type, conn.first); + + if (!decls.count(key)) + continue; + + portdecl_t &decl = decls.at(key); + + if (!cell->parameters.count(decl.widthparam)) + continue; + + if (!decl.signparam.empty() && !cell->parameters.count(decl.signparam)) + continue; + + int inner_width = cell->parameters.at(decl.widthparam).as_int(); + int outer_width = conn.second.size(); + bool is_signed = decl.signparam.empty() ? decl.is_signed : cell->parameters.at(decl.signparam).as_bool(); + + if (inner_width >= outer_width) + continue; + + RTLIL::SigSpec sig = sigmap(conn.second); + extend_map[sig.extract(inner_width - 1, 1)] = std::pair<bool, RTLIL::SigSpec>(is_signed, + sig.extract(inner_width, outer_width - inner_width)); + } + } + + for (auto &it : module->cells_) + { + RTLIL::Cell *cell = it.second; + + if (!design->selected(module, cell)) + continue; + + for (auto &conn : cell->connections_) + { + std::vector<RTLIL::SigBit> sigbits = sigmap(conn.second).to_sigbit_vector(); + RTLIL::SigSpec old_sig; + + for (size_t i = 0; i < sigbits.size(); i++) + { + if (!extend_map.count(sigbits[i])) + continue; + + bool is_signed = extend_map.at(sigbits[i]).first; + RTLIL::SigSpec extend_sig = extend_map.at(sigbits[i]).second; + + int extend_width = 0; + RTLIL::SigBit extend_bit = is_signed ? sigbits[i] : RTLIL::SigBit(RTLIL::State::S0); + while (extend_width < extend_sig.size() && i + extend_width + 1 < sigbits.size() && + sigbits[i + extend_width + 1] == extend_bit) extend_width++; + + if (extend_width == 0) + continue; + + if (old_sig.size() == 0) + old_sig = conn.second; + + conn.second.replace(i+1, extend_sig.extract(0, extend_width)); + i += extend_width; + } + + if (old_sig.size()) + log("Connected extended bits of %s.%s:%s: %s -> %s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), + RTLIL::id2cstr(conn.first), log_signal(old_sig), log_signal(conn.second)); + } + } + } +}; + +struct ConnwrappersPass : public Pass { + ConnwrappersPass() : Pass("connwrappers", "replace undef values with defined constants") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" connwrappers [options] [selection]\n"); + log("\n"); + log("Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports\n"); + log("in wrapper cells with a (larger) constant port size. I.e. the upper bits\n"); + log("of the wrapper outut are signed/unsigned bit extended. This command uses this\n"); + log("knowlege to rewire the inputs of the driven cells to match the output of\n"); + log("the driving cell.\n"); + log("\n"); + log(" -signed <cell_type> <port_name> <width_param>\n"); + log(" -unsigned <cell_type> <port_name> <width_param>\n"); + log(" consider the specified signed/unsigned wrapper output\n"); + log("\n"); + log(" -port <cell_type> <port_name> <width_param> <sign_param>\n"); + log(" use the specified parameter to decide if signed or unsigned\n"); + log("\n"); + log("The options -signed, -unsigned, and -port can be specified multiple times.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + ConnwrappersWorker worker; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-signed" && argidx+3 < args.size()) { + worker.add_port(args[argidx+1], args[argidx+2], args[argidx+3], true); + argidx += 3; + continue; + } + if (args[argidx] == "-unsigned" && argidx+3 < args.size()) { + worker.add_port(args[argidx+1], args[argidx+2], args[argidx+3], false); + argidx += 3; + continue; + } + if (args[argidx] == "-port" && argidx+4 < args.size()) { + worker.add_port(args[argidx+1], args[argidx+2], args[argidx+3], args[argidx+4]); + argidx += 4; + continue; + } + break; + } + extra_args(args, argidx, design); + + log_header("Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); + + for (auto &mod_it : design->modules_) + if (design->selected(mod_it.second)) + worker.work(design, mod_it.second); + } +} ConnwrappersPass; + diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index 4b1a8db81..be7758200 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -41,14 +41,15 @@ struct CopyPass : public Pass { std::string src_name = RTLIL::escape_id(args[1]); std::string trg_name = RTLIL::escape_id(args[2]); - if (design->modules.count(src_name) == 0) + if (design->modules_.count(src_name) == 0) log_cmd_error("Can't find source module %s.\n", src_name.c_str()); - if (design->modules.count(trg_name) != 0) + if (design->modules_.count(trg_name) != 0) log_cmd_error("Target module name %s already exists.\n", trg_name.c_str()); - design->modules[trg_name] = design->modules.at(src_name)->clone(); - design->modules[trg_name]->name = trg_name; + RTLIL::Module *new_mod = design->module(src_name)->clone(); + new_mod->name = trg_name; + design->add(new_mod); } } CopyPass; diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc new file mode 100644 index 000000000..ac72ba53a --- /dev/null +++ b/passes/cmds/cover.cc @@ -0,0 +1,144 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2014 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 <sys/types.h> +#include <unistd.h> +#include <fnmatch.h> + +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +struct CoverPass : public Pass { + CoverPass() : Pass("cover", "print code coverage counters") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" cover [options] [pattern]\n"); + log("\n"); + log("Print the code coverage counters collected using the cover() macro in the Yosys\n"); + log("C++ code. This is useful to figure out what parts of Yosys are utilized by a\n"); + log("test bench.\n"); + log("\n"); + log(" -q\n"); + log(" Do not print output to the normal destination (console and/or log file)\n"); + log("\n"); + log(" -o file\n"); + log(" Write output to this file, truncate if exists.\n"); + log("\n"); + log(" -a file\n"); + log(" Write output to this file, append if exists.\n"); + log("\n"); + log(" -d dir\n"); + log(" Write output to a newly created file in the specified directory.\n"); + log("\n"); + log("When one or more pattern (shell wildcards) are specified, then only counters\n"); + log("matching at least one pattern are printed.\n"); + log("\n"); + log("\n"); + log("It is also possible to instruct Yosys to print the coverage counters on program\n"); + log("exit to a file using environment variables:\n"); + log("\n"); + log(" YOSYS_COVER_DIR=\"{dir-name}\" yosys {args}\n"); + log("\n"); + log(" This will create a file (with an auto-generated name) in this\n"); + log(" directory and write the coverage counters to it.\n"); + log("\n"); + log(" YOSYS_COVER_FILE=\"{file-name}\" yosys {args}\n"); + log("\n"); + log(" This will append the coverage counters to the specified file.\n"); + log("\n"); + log("\n"); + log("Hint: Use the following AWK command to consolidate Yosys coverage files:\n"); + log("\n"); + log(" gawk '{ p[$3] = $1; c[$3] += $2; } END { for (i in p)\n"); + log(" printf \"%%-60s %%10d %%s\\n\", p[i], c[i], i; }' {files} | sort -k3\n"); + log("\n"); + log("\n"); + log("Coverage counters are only available in debug builds of Yosys for Linux.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::vector<FILE*> out_files; + std::vector<std::string> patterns; + bool do_log = true; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-q") { + do_log = false; + continue; + } + if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) { + const char *open_mode = args[argidx] == "-a" ? "a+" : "w"; + std::string filename = args[++argidx]; + if (args[argidx-1] == "-d") { + char filename_buffer[4096]; + snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid()); + filename = mkstemps(filename_buffer, 4); + } + FILE *f = fopen(filename.c_str(), open_mode); + if (f == NULL) { + for (auto f : out_files) + fclose(f); + log_cmd_error("Can't create file %s.\n", args[argidx].c_str()); + } + out_files.push_back(f); + continue; + } + break; + } + while (argidx < args.size() && args[argidx].substr(0, 1) != "-") + patterns.push_back(args[argidx++]); + extra_args(args, argidx, design); + + if (do_log) { + log_header("Printing code coverage counters.\n"); + log("\n"); + } + +#ifdef COVER_ACTIVE + for (auto &it : get_coverage_data()) { + if (!patterns.empty()) { + for (auto &p : patterns) + if (!fnmatch(p.c_str(), it.first.c_str(), 0)) + goto pattern_match; + continue; + } + pattern_match: + for (auto f : out_files) + fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str()); + if (do_log) + log("%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str()); + } +#else + for (auto f : out_files) + fclose(f); + + log_cmd_error("Coverage counters are only available in debug builds of Yosys for Linux.\n"); +#endif + + for (auto f : out_files) + fclose(f); + } +} CoverPass; + diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index 1c02752c2..2a91bc9ea 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -21,21 +21,6 @@ #include "kernel/rtlil.h" #include "kernel/log.h" -struct DeleteWireWorker -{ - RTLIL::Module *module; - std::set<std::string> *delete_wires_p; - - void operator()(RTLIL::SigSpec &sig) { - sig.optimize(); - for (auto &c : sig.chunks) - if (c.wire != NULL && delete_wires_p->count(c.wire->name)) { - c.wire = module->new_wire(c.width, NEW_ID); - c.offset = 0; - } - } -}; - struct DeletePass : public Pass { DeletePass() : Pass("delete", "delete objects in the design") { } virtual void help() @@ -79,9 +64,9 @@ struct DeletePass : public Pass { } extra_args(args, argidx, design); - std::vector<std::string> delete_mods; + std::vector<RTLIL::IdString> delete_mods; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (design->selected_whole_module(mod_it.first) && !flag_input && !flag_output) { delete_mods.push_back(mod_it.first); @@ -94,7 +79,7 @@ struct DeletePass : public Pass { RTLIL::Module *module = mod_it.second; if (flag_input || flag_output) { - for (auto &it : module->wires) + for (auto &it : module->wires_) if (design->selected(module, it.second)) { if (flag_input) it.second->port_input = false; @@ -105,62 +90,52 @@ struct DeletePass : public Pass { continue; } - std::set<std::string> delete_wires; - std::set<std::string> delete_cells; - std::set<std::string> delete_procs; - std::set<std::string> delete_mems; + std::set<RTLIL::Wire*> delete_wires; + std::set<RTLIL::Cell*> delete_cells; + std::set<RTLIL::IdString> delete_procs; + std::set<RTLIL::IdString> delete_mems; - for (auto &it : module->wires) + for (auto &it : module->wires_) if (design->selected(module, it.second)) - delete_wires.insert(it.first); + delete_wires.insert(it.second); for (auto &it : module->memories) if (design->selected(module, it.second)) delete_mems.insert(it.first); - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (design->selected(module, it.second)) - delete_cells.insert(it.first); + delete_cells.insert(it.second); if ((it.second->type == "$memrd" || it.second->type == "$memwr") && delete_mems.count(it.second->parameters.at("\\MEMID").decode_string()) != 0) - delete_cells.insert(it.first); + delete_cells.insert(it.second); } for (auto &it : module->processes) if (design->selected(module, it.second)) delete_procs.insert(it.first); - DeleteWireWorker delete_wire_worker; - delete_wire_worker.module = module; - delete_wire_worker.delete_wires_p = &delete_wires; - module->rewrite_sigspecs(delete_wire_worker); - - for (auto &it : delete_wires) { - delete module->wires.at(it); - module->wires.erase(it); - } - for (auto &it : delete_mems) { delete module->memories.at(it); module->memories.erase(it); } - for (auto &it : delete_cells) { - delete module->cells.at(it); - module->cells.erase(it); - } + for (auto &it : delete_cells) + module->remove(it); for (auto &it : delete_procs) { delete module->processes.at(it); module->processes.erase(it); } + module->remove(delete_wires); + module->fixup_ports(); } for (auto &it : delete_mods) { - delete design->modules.at(it); - design->modules.erase(it); + delete design->modules_.at(it); + design->modules_.erase(it); } } } DeletePass; diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 80a6c0731..9f800c31f 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -22,13 +22,20 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +YOSYS_NAMESPACE_BEGIN + +std::map<std::string, RTLIL::Design*> saved_designs; +std::vector<RTLIL::Design*> pushed_designs; + struct DesignPass : public Pass { DesignPass() : Pass("design", "save, restore and reset current design") { } - std::map<std::string, RTLIL::Design*> saved_designs; virtual ~DesignPass() { for (auto &it : saved_designs) delete it.second; saved_designs.clear(); + for (auto &it : pushed_designs) + delete it; + pushed_designs.clear(); } virtual void help() { @@ -49,6 +56,16 @@ struct DesignPass : public Pass { log("Save the current design under the given name and then clear the current design.\n"); log("\n"); log("\n"); + log(" design -push\n"); + log("\n"); + log("Push the current design to the stack and then clear the current design.\n"); + log("\n"); + log("\n"); + log(" design -pop\n"); + log("\n"); + log("Reset the current design and pop the last design from the stack.\n"); + log("\n"); + log("\n"); log(" design -load <name>\n"); log("\n"); log("Reset the current design and load the design previously saved under the given\n"); @@ -70,6 +87,8 @@ struct DesignPass : public Pass { { bool got_mode = false; bool reset_mode = false; + bool push_mode = false; + bool pop_mode = false; RTLIL::Design *copy_from_design = NULL, *copy_to_design = NULL; std::string save_name, load_name, as_name; std::vector<RTLIL::Module*> copy_src_modules; @@ -83,6 +102,16 @@ struct DesignPass : public Pass { reset_mode = true; continue; } + if (!got_mode && args[argidx] == "-push") { + got_mode = true; + push_mode = true; + continue; + } + if (!got_mode && args[argidx] == "-pop") { + got_mode = true; + pop_mode = true; + continue; + } if (!got_mode && args[argidx] == "-save" && argidx+1 < args.size()) { got_mode = true; save_name = args[++argidx]; @@ -138,7 +167,7 @@ struct DesignPass : public Pass { argidx = args.size(); } - for (auto &it : copy_from_design->modules) { + for (auto &it : copy_from_design->modules_) { if (sel.selected_whole_module(it.first)) { copy_src_modules.push_back(it.second); continue; @@ -151,7 +180,10 @@ struct DesignPass : public Pass { extra_args(args, argidx, design, false); if (!got_mode) - cmd_error(args, argidx, "Missing mode argument (-reset, -save, -load, -copy-from, or -copy-to)."); + cmd_error(args, argidx, "Missing mode argument."); + + if (pop_mode && pushed_designs.empty()) + log_cmd_error("No pushed designs.\n"); if (copy_to_design != NULL) { @@ -160,21 +192,22 @@ struct DesignPass : public Pass { for (auto mod : copy_src_modules) { - std::string trg_name = as_name.empty() ? mod->name : RTLIL::escape_id(as_name); + std::string trg_name = as_name.empty() ? mod->name.str() : RTLIL::escape_id(as_name); - if (copy_to_design->modules.count(trg_name)) - delete copy_to_design->modules.at(trg_name); - copy_to_design->modules[trg_name] = mod->clone(); - copy_to_design->modules[trg_name]->name = trg_name; + if (copy_to_design->modules_.count(trg_name)) + delete copy_to_design->modules_.at(trg_name); + copy_to_design->modules_[trg_name] = mod->clone(); + copy_to_design->modules_[trg_name]->name = trg_name; + copy_to_design->modules_[trg_name]->design = copy_to_design; } } - if (!save_name.empty()) + if (!save_name.empty() || push_mode) { RTLIL::Design *design_copy = new RTLIL::Design; - for (auto &it : design->modules) - design_copy->modules[it.first] = it.second->clone(); + for (auto &it : design->modules_) + design_copy->add(it.second->clone()); design_copy->selection_stack = design->selection_stack; design_copy->selection_vars = design->selection_vars; @@ -182,14 +215,18 @@ struct DesignPass : public Pass { if (saved_designs.count(save_name)) delete saved_designs.at(save_name); - saved_designs[save_name] = design_copy; + + if (push_mode) + pushed_designs.push_back(design_copy); + else + saved_designs[save_name] = design_copy; } - if (reset_mode || !load_name.empty()) + if (reset_mode || !load_name.empty() || push_mode || pop_mode) { - for (auto &it : design->modules) + for (auto &it : design->modules_) delete it.second; - design->modules.clear(); + design->modules_.clear(); design->selection_stack.clear(); design->selection_vars.clear(); @@ -198,12 +235,15 @@ struct DesignPass : public Pass { design->selection_stack.push_back(RTLIL::Selection()); } - if (!load_name.empty()) + if (!load_name.empty() || pop_mode) { - RTLIL::Design *saved_design = saved_designs.at(load_name); + RTLIL::Design *saved_design = pop_mode ? pushed_designs.back() : saved_designs.at(load_name); + + if (pop_mode) + pushed_designs.pop_back(); - for (auto &it : saved_design->modules) - design->modules[it.first] = it.second->clone(); + for (auto &it : saved_design->modules_) + design->add(it.second->clone()); design->selection_stack = saved_design->selection_stack; design->selection_vars = saved_design->selection_vars; @@ -211,4 +251,6 @@ struct DesignPass : public Pass { } } } DesignPass; - + +YOSYS_NAMESPACE_END + diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc new file mode 100644 index 000000000..4e8234d16 --- /dev/null +++ b/passes/cmds/plugin.cc @@ -0,0 +1,124 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2014 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" + +#ifdef YOSYS_ENABLE_PLUGINS +# include <dlfcn.h> +#endif + +YOSYS_NAMESPACE_BEGIN + +std::map<std::string, void*> loaded_plugins; +std::map<std::string, std::string> loaded_plugin_aliases; + +void load_plugin(std::string filename, std::vector<std::string> aliases) +{ +#ifdef YOSYS_ENABLE_PLUGINS + if (filename.find('/') == std::string::npos) + filename = "./" + filename; + + if (!loaded_plugins.count(filename)) { + void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL); + if (hdl == NULL) + log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror()); + loaded_plugins[filename] = hdl; + Pass::init_register(); + } + + for (auto &alias : aliases) + loaded_plugin_aliases[alias] = filename; +#else + log_error("This version of yosys is built without plugin support.\n"); +#endif +} + +struct PluginPass : public Pass { + PluginPass() : Pass("plugin", "load and list loaded plugins") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" plugin [options]\n"); + log("\n"); + log("Load and list loaded plugins.\n"); + log("\n"); + log(" -i <plugin_filename>\n"); + log(" Load (install) the specified plugin.\n"); + log("\n"); + log(" -a <alias_name>\n"); + log(" Register the specified alias name for the loaded plugin\n"); + log("\n"); + log(" -l\n"); + log(" List loaded plugins\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::string plugin_filename; + std::vector<std::string> plugin_aliases; + bool list_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if ((args[argidx] == "-i") && argidx+1 < args.size() && plugin_filename.empty()) { + plugin_filename = args[++argidx]; + continue; + } + if ((args[argidx] == "-a") && argidx+1 < args.size()) { + plugin_aliases.push_back(args[++argidx]); + continue; + } + if (args[argidx] == "-l") { + list_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design, false); + + if (!plugin_filename.empty()) + load_plugin(plugin_filename, plugin_aliases); + + if (list_mode) + { + log("\n"); + if (loaded_plugins.empty()) + log("No plugins loaded.\n"); + else + log("Loaded plugins:\n"); + + for (auto &it : loaded_plugins) + log(" %s\n", it.first.c_str()); + + if (!loaded_plugin_aliases.empty()) { + log("\n"); + int max_alias_len = 1; + for (auto &it : loaded_plugin_aliases) + max_alias_len = std::max(max_alias_len, SIZE(it.first)); + for (auto &it : loaded_plugin_aliases) + log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str()); + } + } + } +} PluginPass; + +YOSYS_NAMESPACE_END + diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 519dce452..91de364fe 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -29,23 +29,17 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: if (module->count_id(to_name)) log_cmd_error("There is already an object `%s' in module `%s'.\n", to_name.c_str(), module->name.c_str()); - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.first == from_name) { - RTLIL::Wire *wire = it.second; - log("Renaming wire %s to %s in module %s.\n", wire->name.c_str(), to_name.c_str(), module->name.c_str()); - module->wires.erase(wire->name); - wire->name = to_name; - module->add(wire); + log("Renaming wire %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module)); + module->rename(it.second, to_name); return; } - for (auto &it : module->cells) + for (auto &it : module->cells_) if (it.first == from_name) { - RTLIL::Cell *cell = it.second; - log("Renaming cell %s to %s in module %s.\n", cell->name.c_str(), to_name.c_str(), module->name.c_str()); - module->cells.erase(cell->name); - cell->name = to_name; - module->add(cell); + log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module)); + module->rename(it.second, to_name); return; } @@ -64,10 +58,12 @@ struct RenamePass : public Pass { log("by this command.\n"); log("\n"); log("\n"); - log(" rename -enumerate [selection]\n"); + log(" rename -enumerate [-pattern <pattern>] [selection]\n"); log("\n"); log("Assign short auto-generated names to all selected wires and cells with private\n"); - log("names.\n"); + log("names. The -pattern option can be used to set the pattern for the new names.\n"); + log("The character %% in the pattern is replaced with a integer number. The default\n"); + log("pattern is '_%%_'.\n"); log("\n"); log(" rename -hide [selection]\n"); log("\n"); @@ -77,6 +73,7 @@ struct RenamePass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + std::string pattern_prefix = "_", pattern_suffix = "_"; bool flag_enumerate = false; bool flag_hide = false; bool got_mode = false; @@ -95,6 +92,12 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) { + int pos = args[++argidx].find('%'); + pattern_prefix = args[argidx].substr(0, pos); + pattern_suffix = args[argidx].substr(pos+1); + continue; + } break; } @@ -102,7 +105,7 @@ struct RenamePass : public Pass { { extra_args(args, argidx, design); - for (auto &mod : design->modules) + for (auto &mod : design->modules_) { int counter = 0; @@ -111,22 +114,22 @@ struct RenamePass : public Pass { continue; std::map<RTLIL::IdString, RTLIL::Wire*> new_wires; - for (auto &it : module->wires) { + for (auto &it : module->wires_) { if (it.first[0] == '$' && design->selected(module, it.second)) - do it.second->name = stringf("\\_%d_", counter++); + do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); while (module->count_id(it.second->name) > 0); new_wires[it.second->name] = it.second; } - module->wires.swap(new_wires); + module->wires_.swap(new_wires); std::map<RTLIL::IdString, RTLIL::Cell*> new_cells; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (it.first[0] == '$' && design->selected(module, it.second)) - do it.second->name = stringf("\\_%d_", counter++); + do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); while (module->count_id(it.second->name) > 0); new_cells[it.second->name] = it.second; } - module->cells.swap(new_cells); + module->cells_.swap(new_cells); } } else @@ -134,29 +137,29 @@ struct RenamePass : public Pass { { extra_args(args, argidx, design); - for (auto &mod : design->modules) + for (auto &mod : design->modules_) { RTLIL::Module *module = mod.second; if (!design->selected(module)) continue; std::map<RTLIL::IdString, RTLIL::Wire*> new_wires; - for (auto &it : module->wires) { + for (auto &it : module->wires_) { if (design->selected(module, it.second)) if (it.first[0] == '\\' && it.second->port_id == 0) it.second->name = NEW_ID; new_wires[it.second->name] = it.second; } - module->wires.swap(new_wires); + module->wires_.swap(new_wires); std::map<RTLIL::IdString, RTLIL::Cell*> new_cells; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (design->selected(module, it.second)) if (it.first[0] == '\\') it.second->name = NEW_ID; new_cells[it.second->name] = it.second; } - module->cells.swap(new_cells); + module->cells_.swap(new_cells); } } else @@ -169,19 +172,19 @@ struct RenamePass : public Pass { if (!design->selected_active_module.empty()) { - if (design->modules.count(design->selected_active_module) > 0) - rename_in_module(design->modules.at(design->selected_active_module), from_name, to_name); + if (design->modules_.count(design->selected_active_module) > 0) + rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name); } else { - for (auto &mod : design->modules) { + for (auto &mod : design->modules_) { if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) { to_name = RTLIL::escape_id(to_name); log("Renaming module %s to %s.\n", mod.first.c_str(), to_name.c_str()); RTLIL::Module *module = mod.second; - design->modules.erase(module->name); + design->modules_.erase(module->name); module->name = to_name; - design->modules[module->name] = module; + design->modules_[module->name] = module; goto rename_ok; } } diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index e5f78830e..e09c00123 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -43,25 +43,22 @@ struct ScatterPass : public Pass { CellTypes ct(design); extra_args(args, 1, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; - for (auto &c : mod_it.second->cells) - for (auto &p : c.second->connections) + for (auto &c : mod_it.second->cells_) + for (auto &p : c.second->connections_) { - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = NEW_ID; - wire->width = p.second.width; - mod_it.second->add(wire); + RTLIL::Wire *wire = mod_it.second->addWire(NEW_ID, p.second.size()); if (ct.cell_output(c.second->type, p.first)) { RTLIL::SigSig sigsig(p.second, wire); - mod_it.second->connections.push_back(sigsig); + mod_it.second->connect(sigsig); } else { RTLIL::SigSig sigsig(wire, p.second); - mod_it.second->connections.push_back(sigsig); + mod_it.second->connect(sigsig); } p.second = wire; diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index f8c351a43..5224f5bc9 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -51,7 +51,7 @@ struct SccWorker void run(RTLIL::Cell *cell, int depth, int maxDepth) { - assert(workQueue.count(cell) > 0); + log_assert(workQueue.count(cell) > 0); workQueue.erase(cell); cellLabels[cell] = std::pair<int, int>(labelCounter, labelCounter); @@ -114,11 +114,11 @@ struct SccWorker SigPool selectedSignals; SigSet<RTLIL::Cell*> sigToNextCells; - for (auto &it : module->wires) + for (auto &it : module->wires_) if (design->selected(module, it.second)) selectedSignals.add(sigmap(RTLIL::SigSpec(it.second))); - for (auto &it : module->cells) + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; @@ -132,7 +132,7 @@ struct SccWorker RTLIL::SigSpec inputSignals, outputSignals; - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) { bool isInput = true, isOutput = true; @@ -166,7 +166,7 @@ struct SccWorker while (workQueue.size() > 0) { RTLIL::Cell *cell = *workQueue.begin(); - assert(cellStack.size() == 0); + log_assert(cellStack.size() == 0); cellDepth.clear(); run(cell, 0, maxDepth); } @@ -191,7 +191,7 @@ struct SccWorker nextsig.sort_and_unify(); sig = prevsig.extract(nextsig); - for (auto &chunk : sig.chunks) + for (auto &chunk : sig.chunks()) if (chunk.wire != NULL) sel.selected_members[module->name].insert(chunk.wire->name); } @@ -216,7 +216,7 @@ struct SccPass : public Pass { log("\n"); log(" -all_cell_types\n"); log(" Usually this command only considers internal non-memory cells. With\n"); - log(" this option set, all cells are considered. For unkown cells all ports\n"); + log(" this option set, all cells are considered. For unknown cells all ports\n"); log(" are assumed to be bidirectional 'inout' ports.\n"); log("\n"); log(" -set_attr <name> <value>\n"); @@ -280,7 +280,7 @@ struct SccPass : public Pass { RTLIL::Selection newSelection(false); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { SccWorker worker(design, mod_it.second, allCellTypes, maxDepth); @@ -290,7 +290,7 @@ struct SccPass : public Pass { } if (selectMode) { - assert(origSelectPos >= 0); + log_assert(origSelectPos >= 0); design->selection_stack[origSelectPos] = newSelection; design->selection_stack[origSelectPos].optimize(design); } diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index a1a64f145..4c540ca67 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -23,6 +23,7 @@ #include "kernel/log.h" #include <string.h> #include <fnmatch.h> +#include <errno.h> using RTLIL::id2cstr; @@ -63,6 +64,8 @@ static bool match_attr_val(const RTLIL::Const &value, std::string pattern, char if (match_op == '=') return value == pattern_value; + if (match_op == '!') + return value != pattern_value; if (match_op == '<') return value.as_int() < pattern_value.as_int(); if (match_op == '>') @@ -82,6 +85,8 @@ static bool match_attr_val(const RTLIL::Const &value, std::string pattern, char if (match_op == '=') return value_str == pattern; + if (match_op == '!') + return value_str != pattern; if (match_op == '<') return value_str < pattern; if (match_op == '>') @@ -115,9 +120,11 @@ static bool match_attr(const std::map<RTLIL::IdString, RTLIL::Const> &attributes static bool match_attr(const std::map<RTLIL::IdString, RTLIL::Const> &attributes, std::string match_expr) { - size_t pos = match_expr.find_first_of("<=>"); + size_t pos = match_expr.find_first_of("<!=>"); if (pos != std::string::npos) { + if (match_expr.substr(pos, 2) == "!=") + return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), '!'); if (match_expr.substr(pos, 2) == "<=") return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), '['); if (match_expr.substr(pos, 2) == ">=") @@ -144,7 +151,7 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) RTLIL::Selection new_sel(false); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (lhs.selected_whole_module(mod_it.first)) continue; @@ -154,13 +161,13 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) } RTLIL::Module *mod = mod_it.second; - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (!lhs.selected_member(mod_it.first, it.first)) new_sel.selected_members[mod->name].insert(it.first); for (auto &it : mod->memories) if (!lhs.selected_member(mod_it.first, it.first)) new_sel.selected_members[mod->name].insert(it.first); - for (auto &it : mod->cells) + for (auto &it : mod->cells_) if (!lhs.selected_member(mod_it.first, it.first)) new_sel.selected_members[mod->name].insert(it.first); for (auto &it : mod->processes) @@ -174,13 +181,13 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) { - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (lhs.selected_whole_module(mod_it.first)) { - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) { - if (design->modules.count(cell_it.second->type) == 0) + if (design->modules_.count(cell_it.second->type) == 0) continue; lhs.selected_modules.insert(cell_it.second->type); } @@ -198,7 +205,7 @@ static void select_op_fullmod(RTLIL::Design *design, RTLIL::Selection &lhs) static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs) { - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (lhs.selected_whole_module(mod_it.first)) continue; @@ -208,11 +215,11 @@ static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs) SigMap sigmap(mod_it.second); SigPool selected_bits; - for (auto &it : mod_it.second->wires) + for (auto &it : mod_it.second->wires_) if (lhs.selected_member(mod_it.first, it.first)) selected_bits.add(sigmap(it.second)); - for (auto &it : mod_it.second->wires) + for (auto &it : mod_it.second->wires_) if (!lhs.selected_member(mod_it.first, it.first) && selected_bits.check_any(sigmap(it.second))) lhs.selected_members[mod_it.first].insert(it.first); } @@ -253,7 +260,7 @@ static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const R if (!rhs.full_selection && rhs.selected_modules.size() == 0 && rhs.selected_members.size() == 0) return; lhs.full_selection = false; - for (auto &it : design->modules) + for (auto &it : design->modules_) lhs.selected_modules.insert(it.first); } @@ -264,18 +271,18 @@ static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const R for (auto &it : rhs.selected_members) { - if (design->modules.count(it.first) == 0) + if (design->modules_.count(it.first) == 0) continue; - RTLIL::Module *mod = design->modules[it.first]; + RTLIL::Module *mod = design->modules_[it.first]; if (lhs.selected_modules.count(mod->name) > 0) { - for (auto &it : mod->wires) + for (auto &it : mod->wires_) lhs.selected_members[mod->name].insert(it.first); for (auto &it : mod->memories) lhs.selected_members[mod->name].insert(it.first); - for (auto &it : mod->cells) + for (auto &it : mod->cells_) lhs.selected_members[mod->name].insert(it.first); for (auto &it : mod->processes) lhs.selected_members[mod->name].insert(it.first); @@ -297,7 +304,7 @@ static void select_op_intersect(RTLIL::Design *design, RTLIL::Selection &lhs, co if (lhs.full_selection) { lhs.full_selection = false; - for (auto &it : design->modules) + for (auto &it : design->modules_) lhs.selected_modules.insert(it.first); } @@ -361,7 +368,7 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v { int sel_objects = 0; bool is_input, is_output; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (lhs.selected_whole_module(mod_it.first) || !lhs.selected_module(mod_it.first)) continue; @@ -369,11 +376,11 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v RTLIL::Module *mod = mod_it.second; std::set<RTLIL::Wire*> selected_wires; - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (lhs.selected_member(mod_it.first, it.first) && limits.count(it.first) == 0) selected_wires.insert(it.second); - for (auto &conn : mod->connections) + for (auto &conn : mod->connections()) { std::vector<RTLIL::SigBit> conn_lhs = conn.first.to_sigbit_vector(); std::vector<RTLIL::SigBit> conn_rhs = conn.second.to_sigbit_vector(); @@ -388,8 +395,8 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v } } - for (auto &cell : mod->cells) - for (auto &conn : cell.second->connections) + for (auto &cell : mod->cells_) + for (auto &conn : cell.second->connections()) { char last_mode = '-'; for (auto &rule : rules) { @@ -408,7 +415,7 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v include_match: is_input = mode == 'x' || ct.cell_input(cell.second->type, conn.first); is_output = mode == 'x' || ct.cell_output(cell.second->type, conn.first); - for (auto &chunk : conn.second.chunks) + for (auto &chunk : conn.second.chunks()) if (chunk.wire != NULL) { if (max_objects != 0 && selected_wires.count(chunk.wire) > 0 && lhs.selected_members[mod->name].count(cell.first) == 0) if (mode == 'x' || (mode == 'i' && is_output) || (mode == 'o' && is_input)) @@ -483,7 +490,7 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) for (auto i2 : i1.second) limits.insert(i2); } else - log_cmd_error("Selection %s is not defined!\n", RTLIL::id2cstr(str)); + log_cmd_error("Selection %s is not defined!\n", RTLIL::unescape_id(str).c_str()); } else limits.insert(RTLIL::escape_id(str)); } @@ -540,7 +547,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se return; } - std::vector<std::string> del_list; + std::vector<RTLIL::IdString> del_list; for (auto mod_name : sel.selected_modules) if (mod_name != design->selected_active_module) del_list.push_back(mod_name); @@ -588,6 +595,13 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_op_diff(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]); work_stack.pop_back(); } else + if (arg == "%D") { + if (work_stack.size() < 2) + log_cmd_error("Must have at least two elements on the stack for operator %%d.\n"); + select_op_diff(design, work_stack[work_stack.size()-1], work_stack[work_stack.size()-2]); + work_stack[work_stack.size()-2] = work_stack[work_stack.size()-1]; + work_stack.pop_back(); + } else if (arg == "%i") { if (work_stack.size() < 2) log_cmd_error("Must have at least two elements on the stack for operator %%i.\n"); @@ -599,14 +613,19 @@ static void select_stmt(RTLIL::Design *design, std::string arg) log_cmd_error("Must have at least one element on the stack for operator %%s.\n"); select_op_submod(design, work_stack[work_stack.size()-1]); } else + if (arg == "%c") { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%c.\n"); + work_stack.push_back(work_stack.back()); + } else if (arg == "%m") { if (work_stack.size() < 1) - log_cmd_error("Must have at least one element on the stack for operator %%s.\n"); + log_cmd_error("Must have at least one element on the stack for operator %%m.\n"); select_op_fullmod(design, work_stack[work_stack.size()-1]); } else if (arg == "%a") { if (work_stack.size() < 1) - log_cmd_error("Must have at least one element on the stack for operator %%s.\n"); + log_cmd_error("Must have at least one element on the stack for operator %%a.\n"); select_op_alias(design, work_stack[work_stack.size()-1]); } else if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) { @@ -635,7 +654,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (design->selection_vars.count(set_name) > 0) work_stack.push_back(design->selection_vars[set_name]); else - log_cmd_error("Selection @%s is not defined!\n", RTLIL::id2cstr(set_name)); + log_cmd_error("Selection @%s is not defined!\n", RTLIL::unescape_id(set_name).c_str()); select_filter_active_mod(design, work_stack.back()); return; } @@ -665,7 +684,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) } sel.full_selection = false; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (arg_mod.substr(0, 2) == "A:") { if (!match_attr(mod_it.second->attributes, arg_mod.substr(2))) @@ -681,22 +700,22 @@ static void select_stmt(RTLIL::Design *design, std::string arg) RTLIL::Module *mod = mod_it.second; if (arg_memb.substr(0, 2) == "w:") { - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (match_ids(it.first, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "i:") { - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (it.second->port_input && match_ids(it.first, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "o:") { - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (it.second->port_output && match_ids(it.first, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "x:") { - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if ((it.second->port_input || it.second->port_output) && match_ids(it.first, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else @@ -704,7 +723,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) size_t delim = arg_memb.substr(2).find(':'); if (delim == std::string::npos) { int width = atoi(arg_memb.substr(2).c_str()); - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (it.second->width == width) sel.selected_members[mod->name].insert(it.first); } else { @@ -712,7 +731,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) std::string max_str = arg_memb.substr(2+delim+1); int min_width = min_str.empty() ? 0 : atoi(min_str.c_str()); int max_width = max_str.empty() ? -1 : atoi(max_str.c_str()); - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (min_width <= it.second->width && (it.second->width <= max_width || max_width == -1)) sel.selected_members[mod->name].insert(it.first); } @@ -723,12 +742,12 @@ static void select_stmt(RTLIL::Design *design, std::string arg) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "c:") { - for (auto &it : mod->cells) + for (auto &it : mod->cells_) if (match_ids(it.first, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "t:") { - for (auto &it : mod->cells) + for (auto &it : mod->cells_) if (match_ids(it.second->type, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else @@ -738,13 +757,13 @@ static void select_stmt(RTLIL::Design *design, std::string arg) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "a:") { - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (match_attr(it.second->attributes, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); for (auto &it : mod->memories) if (match_attr(it.second->attributes, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); - for (auto &it : mod->cells) + for (auto &it : mod->cells_) if (match_attr(it.second->attributes, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); for (auto &it : mod->processes) @@ -752,19 +771,19 @@ static void select_stmt(RTLIL::Design *design, std::string arg) sel.selected_members[mod->name].insert(it.first); } else if (arg_memb.substr(0, 2) == "r:") { - for (auto &it : mod->cells) + for (auto &it : mod->cells_) if (match_attr(it.second->parameters, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else { if (arg_memb.substr(0, 2) == "n:") arg_memb = arg_memb.substr(2); - for (auto &it : mod->wires) + for (auto &it : mod->wires_) if (match_ids(it.first, arg_memb)) sel.selected_members[mod->name].insert(it.first); for (auto &it : mod->memories) if (match_ids(it.first, arg_memb)) sel.selected_members[mod->name].insert(it.first); - for (auto &it : mod->cells) + for (auto &it : mod->cells_) if (match_ids(it.first, arg_memb)) sel.selected_members[mod->name].insert(it.first); for (auto &it : mod->processes) @@ -838,6 +857,10 @@ struct SelectPass : public Pass { log(" selection is non-empty. i.e. produce an error if no object matching\n"); log(" the selection is found.\n"); log("\n"); + log(" -assert-count N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains exactly N objects.\n"); + log("\n"); log(" -list\n"); log(" list all objects in the current selection\n"); log("\n"); @@ -951,12 +974,18 @@ struct SelectPass : public Pass { log(" %%d\n"); log(" pop the top set from the stack and subtract it from the new top\n"); log("\n"); + log(" %%D\n"); + log(" like %%d but swap the roles of two top sets on the stack\n"); + log("\n"); + log(" %%c\n"); + log(" create a copy of the top set rom the stack and push it\n"); + log("\n"); log(" %%x[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); log(" expand top set <num1> num times according to the specified rules.\n"); log(" (i.e. select all cells connected to selected wires and select all\n"); log(" wires connected to selected cells) The rules specify which cell\n"); log(" ports to use for this. the syntax for a rule is a '-' for exclusion\n"); - log(" and a '+' for inclusion, followed by an optional comma seperated\n"); + log(" and a '+' for inclusion, followed by an optional comma separated\n"); log(" list of cell types followed by an optional comma separated list of\n"); log(" cell ports in square brackets. a rule can also be just a cell or wire\n"); log(" name that limits the expansion (is included but does not go beyond).\n"); @@ -996,6 +1025,7 @@ struct SelectPass : public Pass { bool got_module = false; bool assert_none = false; bool assert_any = false; + int assert_count = -1; std::string write_file; std::string set_name; std::string sel_str; @@ -1022,6 +1052,10 @@ struct SelectPass : public Pass { assert_any = true; continue; } + if (arg == "-assert-count" && argidx+1 < args.size()) { + assert_count = atoi(args[++argidx].c_str()); + continue; + } if (arg == "-clear") { clear_mode = true; continue; @@ -1044,9 +1078,9 @@ struct SelectPass : public Pass { } if (arg == "-module" && argidx+1 < args.size()) { RTLIL::IdString mod_name = RTLIL::escape_id(args[++argidx]); - if (design->modules.count(mod_name) == 0) + if (design->modules_.count(mod_name) == 0) log_cmd_error("No such module: %s\n", id2cstr(mod_name)); - design->selected_active_module = mod_name; + design->selected_active_module = mod_name.str(); got_module = true; continue; } @@ -1055,7 +1089,7 @@ struct SelectPass : public Pass { continue; } if (arg.size() > 0 && arg[0] == '-') - log_cmd_error("Unkown option %s.\n", arg.c_str()); + log_cmd_error("Unknown option %s.\n", arg.c_str()); select_stmt(design, arg); sel_str += " " + arg; } @@ -1066,14 +1100,14 @@ struct SelectPass : public Pass { if (none_mode && args.size() != 2) log_cmd_error("Option -none can not be combined with any other options.\n"); - if (add_mode + del_mode + assert_none + assert_any > 1) - log_cmd_error("Options -add, -del, -assert-none or -assert-any can not be combined.\n"); + if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) > 1) + log_cmd_error("Options -add, -del, -assert-none, -assert-any or -assert-count can not be combined.\n"); - if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any)) - log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none or -assert-any.\n"); + if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) + log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any or -assert-count.\n"); - if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any)) - log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none or -assert-any.\n"); + if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) + log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any or -assert-count.\n"); if (work_stack.size() == 0 && got_module) { RTLIL::Selection sel; @@ -1086,7 +1120,7 @@ struct SelectPass : public Pass { work_stack.pop_back(); } - assert(design->selection_stack.size() > 0); + log_assert(design->selection_stack.size() > 0); if (clear_mode) { design->selection_stack.back() = RTLIL::Selection(true); @@ -1113,18 +1147,18 @@ struct SelectPass : public Pass { if (work_stack.size() > 0) sel = &work_stack.back(); sel->optimize(design); - for (auto mod_it : design->modules) + for (auto mod_it : design->modules_) { if (sel->selected_whole_module(mod_it.first) && list_mode) log("%s\n", id2cstr(mod_it.first)); if (sel->selected_module(mod_it.first)) { - for (auto &it : mod_it.second->wires) + for (auto &it : mod_it.second->wires_) if (sel->selected_member(mod_it.first, it.first)) LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); for (auto &it : mod_it.second->memories) if (sel->selected_member(mod_it.first, it.first)) LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); - for (auto &it : mod_it.second->cells) + for (auto &it : mod_it.second->cells_) if (sel->selected_member(mod_it.first, it.first)) LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); for (auto &it : mod_it.second->processes) @@ -1176,6 +1210,34 @@ struct SelectPass : public Pass { return; } + if (assert_count >= 0) + { + int total_count = 0; + if (work_stack.size() == 0) + log_cmd_error("No selection to check.\n"); + RTLIL::Selection *sel = &work_stack.back(); + sel->optimize(design); + for (auto mod_it : design->modules_) + if (sel->selected_module(mod_it.first)) { + for (auto &it : mod_it.second->wires_) + if (sel->selected_member(mod_it.first, it.first)) + total_count++; + for (auto &it : mod_it.second->memories) + if (sel->selected_member(mod_it.first, it.first)) + total_count++; + for (auto &it : mod_it.second->cells_) + if (sel->selected_member(mod_it.first, it.first)) + total_count++; + for (auto &it : mod_it.second->processes) + if (sel->selected_member(mod_it.first, it.first)) + total_count++; + } + if (assert_count != total_count) + log_error("Assertation failed: selection contains %d elements instead of the asserted %d:%s\n", + total_count, assert_count, sel_str.c_str()); + return; + } + if (!set_name.empty()) { if (work_stack.size() == 0) @@ -1237,15 +1299,15 @@ struct CdPass : public Pass { std::string modname = RTLIL::escape_id(args[1]); - if (design->modules.count(modname) == 0 && !design->selected_active_module.empty()) { + if (design->modules_.count(modname) == 0 && !design->selected_active_module.empty()) { RTLIL::Module *module = NULL; - if (design->modules.count(design->selected_active_module) > 0) - module = design->modules.at(design->selected_active_module); - if (module != NULL && module->cells.count(modname) > 0) - modname = module->cells.at(modname)->type; + if (design->modules_.count(design->selected_active_module) > 0) + module = design->modules_.at(design->selected_active_module); + if (module != NULL && module->cells_.count(modname) > 0) + modname = module->cells_.at(modname)->type.str(); } - if (design->modules.count(modname) > 0) { + if (design->modules_.count(modname) > 0) { design->selected_active_module = modname; design->selection_stack.back() = RTLIL::Selection(); select_filter_active_mod(design, design->selection_stack.back()); @@ -1253,14 +1315,14 @@ struct CdPass : public Pass { return; } - log_cmd_error("No such module `%s' found!\n", RTLIL::id2cstr(modname)); + log_cmd_error("No such module `%s' found!\n", RTLIL::unescape_id(modname).c_str()); } } CdPass; template<typename T> static int log_matches(const char *title, std::string pattern, T list) { - std::vector<std::string> matches; + std::vector<RTLIL::IdString> matches; for (auto &it : list) if (pattern.empty() || match_ids(it.first, pattern)) @@ -1306,15 +1368,15 @@ struct LsPass : public Pass { if (design->selected_active_module.empty()) { - counter += log_matches("modules", pattern, design->modules); + counter += log_matches("modules", pattern, design->modules_); } else - if (design->modules.count(design->selected_active_module) > 0) + if (design->modules_.count(design->selected_active_module) > 0) { - RTLIL::Module *module = design->modules.at(design->selected_active_module); - counter += log_matches("wires", pattern, module->wires); + RTLIL::Module *module = design->modules_.at(design->selected_active_module); + counter += log_matches("wires", pattern, module->wires_); counter += log_matches("memories", pattern, module->memories); - counter += log_matches("cells", pattern, module->cells); + counter += log_matches("cells", pattern, module->cells_); counter += log_matches("processes", pattern, module->processes); } diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 8d98df719..029c0ec79 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -98,7 +98,7 @@ struct SetattrPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod : design->modules) + for (auto &mod : design->modules_) { RTLIL::Module *module = mod.second; @@ -111,7 +111,7 @@ struct SetattrPass : public Pass { if (!design->selected(module)) continue; - for (auto &it : module->wires) + for (auto &it : module->wires_) if (design->selected(module, it.second)) do_setunset(it.second->attributes, setunset_list); @@ -119,7 +119,7 @@ struct SetattrPass : public Pass { if (design->selected(module, it.second)) do_setunset(it.second->attributes, setunset_list); - for (auto &it : module->cells) + for (auto &it : module->cells_) if (design->selected(module, it.second)) do_setunset(it.second->attributes, setunset_list); @@ -164,14 +164,14 @@ struct SetparamPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod : design->modules) + for (auto &mod : design->modules_) { RTLIL::Module *module = mod.second; if (!design->selected(module)) continue; - for (auto &it : module->cells) + for (auto &it : module->cells_) if (design->selected(module, it.second)) do_setunset(it.second->parameters, setunset_list); } diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index 9d59834c2..c72e64b80 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -23,35 +23,33 @@ #include "kernel/rtlil.h" #include "kernel/log.h" -static int next_bit_mode; -static uint32_t next_bit_state; - -static RTLIL::State next_bit() +struct SetundefWorker { - if (next_bit_mode == 0) - return RTLIL::State::S0; + int next_bit_mode; + uint32_t next_bit_state; - if (next_bit_mode == 1) - return RTLIL::State::S1; + RTLIL::State next_bit() + { + if (next_bit_mode == 0) + return RTLIL::State::S0; - // xorshift32 - next_bit_state ^= next_bit_state << 13; - next_bit_state ^= next_bit_state >> 17; - next_bit_state ^= next_bit_state << 5; - log_assert(next_bit_state != 0); + if (next_bit_mode == 1) + return RTLIL::State::S1; - return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1; -} + // xorshift32 + next_bit_state ^= next_bit_state << 13; + next_bit_state ^= next_bit_state >> 17; + next_bit_state ^= next_bit_state << 5; + log_assert(next_bit_state != 0); + + return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1; + } -struct SetundefWorker -{ void operator()(RTLIL::SigSpec &sig) { - sig.expand(); - for (auto &c : sig.chunks) - if (c.wire == NULL && c.data.bits.at(0) > RTLIL::State::S1) - c.data.bits.at(0) = next_bit(); - sig.optimize(); + for (auto &bit : sig) + if (bit.wire == NULL && bit.data > RTLIL::State::S1) + bit = next_bit(); } }; @@ -83,6 +81,7 @@ struct SetundefPass : public Pass { { bool got_value = false; bool undriven_mode = false; + SetundefWorker worker; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -93,20 +92,20 @@ struct SetundefPass : public Pass { } if (args[argidx] == "-zero") { got_value = true; - next_bit_mode = 0; + worker.next_bit_mode = 0; continue; } if (args[argidx] == "-one") { got_value = true; - next_bit_mode = 1; + worker.next_bit_mode = 1; continue; } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; - next_bit_mode = 2; - next_bit_state = atoi(args[++argidx].c_str()) + 1; + worker.next_bit_mode = 2; + worker.next_bit_state = atoi(args[++argidx].c_str()) + 1; for (int i = 0; i < 10; i++) - next_bit(); + worker.next_bit(); continue; } break; @@ -116,7 +115,7 @@ struct SetundefPass : public Pass { if (!got_value) log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n"); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { RTLIL::Module *module = mod_it.second; if (!design->selected(module)) @@ -130,27 +129,26 @@ struct SetundefPass : public Pass { SigMap sigmap(module); SigPool undriven_signals; - for (auto &it : module->wires) + for (auto &it : module->wires_) if (!it.second->port_input) undriven_signals.add(sigmap(it.second)); CellTypes ct(design); - for (auto &it : module->cells) - for (auto &conn : it.second->connections) + for (auto &it : module->cells_) + for (auto &conn : it.second->connections()) if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) undriven_signals.del(sigmap(conn.second)); RTLIL::SigSpec sig = undriven_signals.export_all(); - for (auto &c : sig.chunks) { + for (auto &c : sig.chunks()) { RTLIL::SigSpec bits; for (int i = 0; i < c.width; i++) - bits.append(next_bit()); - bits.optimize(); - module->connections.push_back(RTLIL::SigSig(c, bits)); + bits.append(worker.next_bit()); + module->connect(RTLIL::SigSig(c, bits)); } } - module->rewrite_sigspecs(SetundefWorker()); + module->rewrite_sigspecs(worker); } } } SetundefPass; diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index fdccb4bcc..2218eded2 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -22,7 +22,10 @@ #include "kernel/log.h" #include <string.h> #include <dirent.h> -#include <readline/readline.h> + +#ifdef YOSYS_ENABLE_READLINE +# include <readline/readline.h> +#endif using RTLIL::id2cstr; @@ -45,6 +48,7 @@ struct ShowWorker RTLIL::Module *module; uint32_t currentColor; bool genWidthLabels; + bool genSignedLabels; bool stretchIO; bool enumerateIds; bool abbreviateIds; @@ -54,7 +58,7 @@ struct ShowWorker const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections; const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections; - uint32_t xorshift32(uint32_t x) { + static uint32_t xorshift32(uint32_t x) { x ^= x << 13; x ^= x >> 17; x ^= x << 5; @@ -78,7 +82,7 @@ struct ShowWorker std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor) { sig.sort_and_unify(); - for (auto &c : sig.chunks) { + for (auto &c : sig.chunks()) { if (c.wire != NULL) for (auto &s : color_selections) if (s.second.selected_members.count(module->name) > 0 && s.second.selected_members.at(module->name).count(c.wire->name) > 0) @@ -87,17 +91,17 @@ struct ShowWorker return defaultColor; } - std::string nextColor(RTLIL::SigSig &conn, std::string defaultColor) + std::string nextColor(const RTLIL::SigSig &conn, std::string defaultColor) { return nextColor(conn.first, nextColor(conn.second, defaultColor)); } - std::string nextColor(RTLIL::SigSpec &sig) + std::string nextColor(const RTLIL::SigSpec &sig) { return nextColor(sig, nextColor()); } - std::string nextColor(RTLIL::SigSig &conn) + std::string nextColor(const RTLIL::SigSig &conn) { return nextColor(conn, nextColor()); } @@ -124,7 +128,7 @@ struct ShowWorker const char *findLabel(std::string member_name) { for (auto &s : label_selections) - if (s.second.selected_member(module->name, RTLIL::escape_id(member_name))) + if (s.second.selected_member(module->name, member_name)) return escape(s.first); return escape(member_name, true); } @@ -171,15 +175,13 @@ struct ShowWorker std::string gen_signode_simple(RTLIL::SigSpec sig, bool range_check = true) { - sig.optimize(); - - if (sig.chunks.size() == 0) { + if (SIZE(sig) == 0) { fprintf(f, "v%d [ label=\"\" ];\n", single_idx_count); return stringf("v%d", single_idx_count++); } - if (sig.chunks.size() == 1) { - RTLIL::SigChunk &c = sig.chunks[0]; + if (sig.is_chunk()) { + const RTLIL::SigChunk &c = sig.as_chunk(); if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) { if (!range_check || c.wire->width == c.width) return stringf("n%d", id2num(c.wire->name)); @@ -199,13 +201,12 @@ struct ShowWorker if (net.empty()) { std::string label_string; - sig.optimize(); - int pos = sig.width-1; + int pos = sig.size()-1; int idx = single_idx_count++; - for (int i = int(sig.chunks.size())-1; i >= 0; i--) { - RTLIL::SigChunk &c = sig.chunks[i]; + for (int i = int(sig.chunks().size())-1; i >= 0; i--) { + const RTLIL::SigChunk &c = sig.chunks().at(i); net = gen_signode_simple(c, false); - assert(!net.empty()); + log_assert(!net.empty()); if (driver) { label_string += stringf("<s%d> %d:%d - %d:%d |", i, pos, pos-c.width+1, c.offset+c.width-1, c.offset); net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); @@ -225,9 +226,9 @@ struct ShowWorker if (!port.empty()) { currentColor = xorshift32(currentColor); if (driver) - code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor(sig).c_str(), widthLabel(sig.width).c_str()); + code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); else - code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", idx, port.c_str(), nextColor(sig).c_str(), widthLabel(sig.width).c_str()); + code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", idx, port.c_str(), nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); } if (node != NULL) *node = stringf("x%d", idx); @@ -239,7 +240,7 @@ struct ShowWorker net_conn_map[net].in.insert(port); else net_conn_map[net].out.insert(port); - net_conn_map[net].bits = sig.width; + net_conn_map[net].bits = sig.size(); net_conn_map[net].color = nextColor(sig, net_conn_map[net].color); } if (node != NULL) @@ -299,16 +300,16 @@ struct ShowWorker dot_id2num_store.clear(); net_conn_map.clear(); - fprintf(f, "digraph \"%s\" {\n", escape(module->name)); + fprintf(f, "digraph \"%s\" {\n", escape(module->name.str())); if (!notitle) - fprintf(f, "label=\"%s\";\n", escape(module->name)); + fprintf(f, "label=\"%s\";\n", escape(module->name.str())); fprintf(f, "rankdir=\"LR\";\n"); fprintf(f, "remincross=true;\n"); std::set<std::string> all_sources, all_sinks; std::map<std::string, std::string> wires_on_demand; - for (auto &it : module->wires) { + for (auto &it : module->wires_) { if (!design->selected_member(module->name, it.first)) continue; const char *shape = "diamond"; @@ -316,14 +317,14 @@ struct ShowWorker shape = "octagon"; if (it.first[0] == '\\') { fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n", - id2num(it.first), shape, findLabel(it.first), + id2num(it.first), shape, findLabel(it.first.str()), nextColor(RTLIL::SigSpec(it.second), "color=\"black\"").c_str()); if (it.second->port_input) all_sources.insert(stringf("n%d", id2num(it.first))); else if (it.second->port_output) all_sinks.insert(stringf("n%d", id2num(it.first))); } else { - wires_on_demand[stringf("n%d", id2num(it.first))] = it.first; + wires_on_demand[stringf("n%d", id2num(it.first))] = it.first.str(); } } @@ -340,38 +341,43 @@ struct ShowWorker fprintf(f, "}\n"); } - for (auto &it : module->cells) + for (auto &it : module->cells_) { if (!design->selected_member(module->name, it.first)) continue; std::vector<RTLIL::IdString> in_ports, out_ports; - for (auto &conn : it.second->connections) { + for (auto &conn : it.second->connections()) { if (!ct.cell_output(it.second->type, conn.first)) in_ports.push_back(conn.first); else out_ports.push_back(conn.first); } + std::sort(in_ports.begin(), in_ports.end(), RTLIL::sort_by_id_str()); + std::sort(out_ports.begin(), out_ports.end(), RTLIL::sort_by_id_str()); + std::string label_string = "{{"; for (auto &p : in_ports) - label_string += stringf("<p%d> %s|", id2num(p), escape(p)); + label_string += stringf("<p%d> %s%s|", id2num(p), escape(p.str()), + genSignedLabels && it.second->hasParam(p.str() + "_SIGNED") && + it.second->getParam(p.str() + "_SIGNED").as_bool() ? "*" : ""); if (label_string[label_string.size()-1] == '|') label_string = label_string.substr(0, label_string.size()-1); - label_string += stringf("}|%s\\n%s|{", findLabel(it.first), escape(it.second->type)); + label_string += stringf("}|%s\\n%s|{", findLabel(it.first.str()), escape(it.second->type.str())); for (auto &p : out_ports) - label_string += stringf("<p%d> %s|", id2num(p), escape(p)); + label_string += stringf("<p%d> %s|", id2num(p), escape(p.str())); if (label_string[label_string.size()-1] == '|') label_string = label_string.substr(0, label_string.size()-1); label_string += "}}"; std::string code; - for (auto &conn : it.second->connections) { + for (auto &conn : it.second->connections()) { code += gen_portbox(stringf("c%d:p%d", id2num(it.first), id2num(conn.first)), conn.second, ct.cell_output(it.second->type, conn.first)); } @@ -383,7 +389,7 @@ struct ShowWorker else #endif fprintf(f, "c%d [ shape=record, label=\"%s\"%s ];\n%s", - id2num(it.first), label_string.c_str(), findColor(it.first), code.c_str()); + id2num(it.first), label_string.c_str(), findColor(it.first.str()), code.c_str()); } for (auto &it : module->processes) @@ -405,7 +411,7 @@ struct ShowWorker code += gen_portbox("", sig, false, &node); fprintf(f, "%s", code.c_str()); net_conn_map[node].out.insert(stringf("p%d", pidx)); - net_conn_map[node].bits = sig.width; + net_conn_map[node].bits = sig.size(); net_conn_map[node].color = nextColor(sig, net_conn_map[node].color); } @@ -414,25 +420,25 @@ struct ShowWorker code += gen_portbox("", sig, true, &node); fprintf(f, "%s", code.c_str()); net_conn_map[node].in.insert(stringf("p%d", pidx)); - net_conn_map[node].bits = sig.width; + net_conn_map[node].bits = sig.size(); net_conn_map[node].color = nextColor(sig, net_conn_map[node].color); } std::string proc_src = RTLIL::unescape_id(proc->name); if (proc->attributes.count("\\src") > 0) proc_src = proc->attributes.at("\\src").decode_string(); - fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\"];\n", pidx, findLabel(proc->name), proc_src.c_str()); + fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\"];\n", pidx, findLabel(proc->name.str()), proc_src.c_str()); } - for (auto &conn : module->connections) + for (auto &conn : module->connections()) { bool found_lhs_wire = false; - for (auto &c : conn.first.chunks) { + for (auto &c : conn.first.chunks()) { if (c.wire == NULL || design->selected_member(module->name, c.wire->name)) found_lhs_wire = true; } bool found_rhs_wire = false; - for (auto &c : conn.second.chunks) { + for (auto &c : conn.second.chunks()) { if (c.wire == NULL || design->selected_member(module->name, c.wire->name)) found_rhs_wire = true; } @@ -446,11 +452,11 @@ struct ShowWorker if (left_node[0] == 'x' && right_node[0] == 'x') { currentColor = xorshift32(currentColor); - fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", left_node.c_str(), right_node.c_str(), nextColor(conn).c_str(), widthLabel(conn.first.width).c_str()); + fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", left_node.c_str(), right_node.c_str(), nextColor(conn).c_str(), widthLabel(conn.first.size()).c_str()); } else { - net_conn_map[right_node].bits = conn.first.width; + net_conn_map[right_node].bits = conn.first.size(); net_conn_map[right_node].color = nextColor(conn, net_conn_map[right_node].color); - net_conn_map[left_node].bits = conn.first.width; + net_conn_map[left_node].bits = conn.first.size(); net_conn_map[left_node].color = nextColor(conn, net_conn_map[left_node].color); if (left_node[0] == 'x') { net_conn_map[right_node].in.insert(left_node); @@ -487,15 +493,15 @@ struct ShowWorker fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.first.c_str(), it2.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); } - fprintf(f, "};\n"); + fprintf(f, "}\n"); } - ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, - bool genWidthLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle, + ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels, + bool genSignedLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle, const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections, const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections) : f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels), - stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds), + genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds), notitle(notitle), color_selections(color_selections), label_selections(label_selections) { ct.setup_internals(); @@ -509,7 +515,7 @@ struct ShowWorker design->optimize(); page_counter = 0; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { module = mod_it.second; if (!design->selected_module(module->name)) @@ -519,7 +525,7 @@ struct ShowWorker log("Skipping blackbox module %s.\n", id2cstr(module->name)); continue; } else - if (module->cells.empty() && module->connections.empty() && module->processes.empty()) { + if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) { log("Skipping empty module %s.\n", id2cstr(module->name)); continue; } else @@ -575,6 +581,10 @@ struct ShowPass : public Pass { log(" -width\n"); log(" annotate busses with a label indicating the width of the bus.\n"); log("\n"); + log(" -signed\n"); + log(" mark ports (A, B) that are declarted as signed (using the [AB]_SIGNED\n"); + log(" cell parameter) with an asterisk next to the port name.\n"); + log("\n"); log(" -stretch\n"); log(" stretch the graph so all inputs are on the left side and all outputs\n"); log(" (including inout ports) are on the right side.\n"); @@ -591,8 +601,8 @@ struct ShowPass : public Pass { log(" -notitle\n"); log(" do not add the module name as graph title to the dot file\n"); log("\n"); - log("When no <format> is specified, SVG is used. When no <format> and <viewer> is\n"); - log("specified, 'yosys-svgviewer' is used to display the schematic.\n"); + log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n"); + log("specified, 'xdot' is used to display the schematic.\n"); log("\n"); log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n"); log("unless another prefix is specified using -prefix <prefix>.\n"); @@ -613,6 +623,7 @@ struct ShowPass : public Pass { std::vector<RTLIL::Design*> libs; uint32_t colorSeed = 0; bool flag_width = false; + bool flag_signed = false; bool flag_stretch = false; bool flag_pause = false; bool flag_enum = false; @@ -655,6 +666,8 @@ struct ShowPass : public Pass { } if (arg == "-colors" && argidx+1 < args.size()) { colorSeed = atoi(args[++argidx].c_str()); + for (int i = 0; i < 100; i++) + colorSeed = ShowWorker::xorshift32(colorSeed); continue; } if (arg == "-format" && argidx+1 < args.size()) { @@ -665,6 +678,10 @@ struct ShowPass : public Pass { flag_width= true; continue; } + if (arg == "-signed") { + flag_signed= true; + continue; + } if (arg == "-stretch") { flag_stretch= true; continue; @@ -693,10 +710,10 @@ struct ShowPass : public Pass { if (format != "ps") { int modcount = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (mod_it.second->get_bool_attribute("\\blackbox")) continue; - if (mod_it.second->cells.empty() && mod_it.second->connections.empty()) + if (mod_it.second->cells_.empty() && mod_it.second->connections().empty()) continue; if (design->selected_module(mod_it.first)) modcount++; @@ -706,13 +723,13 @@ struct ShowPass : public Pass { } for (auto filename : libfiles) { - FILE *f = fopen(filename.c_str(), "rt"); - if (f == NULL) + std::ifstream f; + f.open(filename.c_str()); + if (f.fail()) log_error("Can't open lib file `%s'.\n", filename.c_str()); RTLIL::Design *lib = new RTLIL::Design; - Frontend::frontend_call(lib, f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog"); + Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog"); libs.push_back(lib); - fclose(f); } if (libs.size() > 0) @@ -728,7 +745,7 @@ struct ShowPass : public Pass { delete lib; log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str()); } - ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_stretch, flag_enum, flag_abbeviate, flag_notitle, color_selections, label_selections); + ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbeviate, flag_notitle, color_selections, label_selections); fclose(f); for (auto lib : libs) @@ -737,8 +754,8 @@ struct ShowPass : public Pass { if (worker.page_counter == 0) log_cmd_error("Nothing there to show.\n"); - if (format != "dot") { - std::string cmd = stringf("dot -T%s -o '%s' '%s'", format.empty() ? "svg" : format.c_str(), out_file.c_str(), dot_file.c_str()); + if (format != "dot" && !format.empty()) { + std::string cmd = stringf("dot -T%s -o '%s' '%s'", format.c_str(), out_file.c_str(), dot_file.c_str()); log("Exec: %s\n", cmd.c_str()); if (system(cmd.c_str()) != 0) log_cmd_error("Shell command failed!\n"); @@ -751,13 +768,14 @@ struct ShowPass : public Pass { log_cmd_error("Shell command failed!\n"); } else if (format.empty()) { - std::string cmd = stringf("fuser -s '%s' || '%s' '%s' &", out_file.c_str(), rewrite_yosys_exe("yosys-svgviewer").c_str(), out_file.c_str()); + std::string cmd = stringf("fuser -s '%s' || xdot '%s' < '%s' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); log("Exec: %s\n", cmd.c_str()); if (system(cmd.c_str()) != 0) log_cmd_error("Shell command failed!\n"); } if (flag_pause) { + #ifdef YOSYS_ENABLE_READLINE char *input = NULL; while ((input = readline("Press ENTER to continue (or type 'shell' to open a shell)> ")) != NULL) { if (input[strspn(input, " \t\r\n")] == 0) @@ -768,6 +786,9 @@ struct ShowPass : public Pass { break; } } + #else + log_cmd_error("This version of yosys is built without readline support => 'show -pause' is not available.\n"); + #endif } log_pop(); diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 6d920dbc5..d03aaf3b5 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -33,8 +33,8 @@ struct SpliceWorker bool sel_by_wire; bool sel_any_bit; bool no_outputs; - std::set<std::string> ports; - std::set<std::string> no_ports; + std::set<RTLIL::IdString> ports; + std::set<RTLIL::IdString> no_ports; CellTypes ct; SigMap sigmap; @@ -52,7 +52,7 @@ struct SpliceWorker RTLIL::SigSpec get_sliced_signal(RTLIL::SigSpec sig) { - if (sig.width == 0 || sig.is_fully_const()) + if (sig.size() == 0 || sig.is_fully_const()) return sig; if (sliced_signals_cache.count(sig)) @@ -69,20 +69,16 @@ struct SpliceWorker RTLIL::SigSpec new_sig = sig; - if (sig_a.width != sig.width) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$slice"; + if (sig_a.size() != sig.size()) { + RTLIL::Cell *cell = module->addCell(NEW_ID, "$slice"); cell->parameters["\\OFFSET"] = offset; - cell->parameters["\\A_WIDTH"] = sig_a.width; - cell->parameters["\\Y_WIDTH"] = sig.width; - cell->connections["\\A"] = sig_a; - cell->connections["\\Y"] = module->new_wire(sig.width, NEW_ID); - new_sig = cell->connections["\\Y"]; - module->add(cell); + cell->parameters["\\A_WIDTH"] = sig_a.size(); + cell->parameters["\\Y_WIDTH"] = sig.size(); + cell->setPort("\\A", sig_a); + cell->setPort("\\Y", module->addWire(NEW_ID, sig.size())); + new_sig = cell->getPort("\\Y"); } - new_sig.optimize(); sliced_signals_cache[sig] = new_sig; return new_sig; @@ -90,7 +86,7 @@ struct SpliceWorker RTLIL::SigSpec get_spliced_signal(RTLIL::SigSpec sig) { - if (sig.width == 0 || sig.is_fully_const()) + if (sig.size() == 0 || sig.is_fully_const()) return sig; if (spliced_signals_cache.count(sig)) @@ -131,19 +127,15 @@ struct SpliceWorker RTLIL::SigSpec new_sig = get_sliced_signal(chunks.front()); for (size_t i = 1; i < chunks.size(); i++) { RTLIL::SigSpec sig2 = get_sliced_signal(chunks[i]); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$concat"; - cell->parameters["\\A_WIDTH"] = new_sig.width; - cell->parameters["\\B_WIDTH"] = sig2.width; - cell->connections["\\A"] = new_sig; - cell->connections["\\B"] = sig2; - cell->connections["\\Y"] = module->new_wire(new_sig.width + sig2.width, NEW_ID); - new_sig = cell->connections["\\Y"]; - module->add(cell); + RTLIL::Cell *cell = module->addCell(NEW_ID, "$concat"); + cell->parameters["\\A_WIDTH"] = new_sig.size(); + cell->parameters["\\B_WIDTH"] = sig2.size(); + cell->setPort("\\A", new_sig); + cell->setPort("\\B", sig2); + cell->setPort("\\Y", module->addWire(NEW_ID, new_sig.size() + sig2.size())); + new_sig = cell->getPort("\\Y"); } - new_sig.optimize(); spliced_signals_cache[sig] = new_sig; log(" Created spliced signal: %s -> %s\n", log_signal(sig), log_signal(new_sig)); @@ -157,7 +149,7 @@ struct SpliceWorker driven_bits.push_back(RTLIL::State::Sm); driven_bits.push_back(RTLIL::State::Sm); - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->port_input) { RTLIL::SigSpec sig = sigmap(it.second); driven_chunks.insert(sig); @@ -166,8 +158,8 @@ struct SpliceWorker driven_bits.push_back(RTLIL::State::Sm); } - for (auto &it : module->cells) - for (auto &conn : it.second->connections) + for (auto &it : module->cells_) + for (auto &conn : it.second->connections()) if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) { RTLIL::SigSpec sig = sigmap(conn.second); driven_chunks.insert(sig); @@ -183,14 +175,14 @@ struct SpliceWorker SigPool selected_bits; if (!sel_by_cell) - for (auto &it : module->wires) + for (auto &it : module->wires_) if (design->selected(module, it.second)) selected_bits.add(sigmap(it.second)); - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (!sel_by_wire && !design->selected(module, it.second)) continue; - for (auto &conn : it.second->connections) + for (auto &conn : it.second->connections_) if (ct.cell_input(it.second->type, conn.first)) { if (ports.size() > 0 && !ports.count(conn.first)) continue; @@ -211,7 +203,7 @@ struct SpliceWorker std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires; - for (auto &it : module->wires) + for (auto &it : module->wires_) if (!no_outputs && it.second->port_output) { if (!design->selected(module, it.second)) continue; @@ -232,15 +224,15 @@ struct SpliceWorker for (auto &it : rework_wires) { - module->wires.erase(it.first->name); - RTLIL::Wire *new_port = new RTLIL::Wire(*it.first); - it.first->name = NEW_ID; + RTLIL::IdString orig_name = it.first->name; + module->rename(it.first, NEW_ID); + + RTLIL::Wire *new_port = module->addWire(orig_name, it.first); it.first->port_id = 0; it.first->port_input = false; it.first->port_output = false; - module->add(it.first); - module->add(new_port); - module->connections.push_back(RTLIL::SigSig(new_port, it.second)); + + module->connect(RTLIL::SigSig(new_port, it.second)); } } }; @@ -259,12 +251,12 @@ struct SplicePass : public Pass { log("\n"); log(" -sel_by_cell\n"); log(" only select the cell ports to rewire by the cell. if the selection\n"); - log(" contains a cell, than all cell inputs are rewired, if neccessary.\n"); + log(" contains a cell, than all cell inputs are rewired, if necessary.\n"); log("\n"); log(" -sel_by_wire\n"); log(" only select the cell ports to rewire by the wire. if the selection\n"); log(" contains a wire, than all cell ports driven by this wire are wired,\n"); - log(" if neccessary.\n"); + log(" if necessary.\n"); log("\n"); log(" -sel_any_bit\n"); log(" it is sufficient if the driver of any bit of a cell port is selected.\n"); @@ -291,7 +283,7 @@ struct SplicePass : public Pass { bool sel_by_wire = false; bool sel_any_bit = false; bool no_outputs = false; - std::set<std::string> ports, no_ports; + std::set<RTLIL::IdString> ports, no_ports; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -335,7 +327,7 @@ struct SplicePass : public Pass { log_header("Executing SPLICE pass (creating cells for signal splicing).\n"); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index da9ef43f9..344b03fc2 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -28,33 +28,28 @@ struct SplitnetsWorker void append_wire(RTLIL::Module *module, RTLIL::Wire *wire, int offset, int width, std::string format) { - RTLIL::Wire *new_wire = new RTLIL::Wire; - - new_wire->port_id = wire->port_id; - new_wire->port_input = wire->port_input; - new_wire->port_output = wire->port_output; - new_wire->name = wire->name; - new_wire->width = width; + std::string new_wire_name = wire->name.str(); if (format.size() > 0) - new_wire->name += format.substr(0, 1); + new_wire_name += format.substr(0, 1); if (width > 1) { - new_wire->name += stringf("%d", offset+width-1); + new_wire_name += stringf("%d", offset+width-1); if (format.size() > 2) - new_wire->name += format.substr(2, 1); + new_wire_name += format.substr(2, 1); else - new_wire->name += ":"; + new_wire_name += ":"; } - new_wire->name += stringf("%d", offset); + new_wire_name += stringf("%d", offset); if (format.size() > 1) - new_wire->name += format.substr(1, 1); + new_wire_name += format.substr(1, 1); - while (module->count_id(new_wire->name) > 0) - new_wire->name = new_wire->name + "_"; - module->add(new_wire); + RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width); + new_wire->port_id = wire->port_id; + new_wire->port_input = wire->port_input; + new_wire->port_output = wire->port_output; std::vector<RTLIL::SigBit> sigvec = RTLIL::SigSpec(new_wire).to_sigbit_vector(); splitmap[wire].insert(splitmap[wire].end(), sigvec.begin(), sigvec.end()); @@ -62,11 +57,9 @@ struct SplitnetsWorker void operator()(RTLIL::SigSpec &sig) { - sig.expand(); - for (auto &c : sig.chunks) - if (splitmap.count(c.wire) > 0) - c = splitmap.at(c.wire).at(c.offset); - sig.optimize(); + for (auto &bit : sig) + if (splitmap.count(bit.wire) > 0) + bit = splitmap.at(bit.wire).at(bit.offset); } }; @@ -83,7 +76,7 @@ struct SplitnetsPass : public Pass { log(" -format char1[char2[char3]]\n"); log(" the first char is inserted between the net name and the bit index, the\n"); log(" second char is appended to the netname. e.g. -format () creates net\n"); - log(" names like 'mysignal(42)'. the 3rd character is the range seperation\n"); + log(" names like 'mysignal(42)'. the 3rd character is the range separation\n"); log(" character when creating multi-bit wires. the default is '[]:'.\n"); log("\n"); log(" -ports\n"); @@ -121,7 +114,7 @@ struct SplitnetsPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { RTLIL::Module *module = mod_it.second; if (!design->selected(module)) @@ -135,16 +128,16 @@ struct SplitnetsPass : public Pass { std::map<RTLIL::Wire*, std::set<int>> split_wires_at; - for (auto &c : module->cells) - for (auto &p : c.second->connections) + for (auto &c : module->cells_) + for (auto &p : c.second->connections()) { if (!ct.cell_known(c.second->type)) continue; if (!ct.cell_output(c.second->type, p.first)) continue; - RTLIL::SigSpec sig = p.second.optimized(); - for (auto &chunk : sig.chunks) { + RTLIL::SigSpec sig = p.second; + for (auto &chunk : sig.chunks()) { if (chunk.wire == NULL) continue; if (chunk.wire->port_id == 0 || flag_ports) { @@ -167,9 +160,9 @@ struct SplitnetsPass : public Pass { } else { - for (auto &w : module->wires) { + for (auto &w : module->wires_) { RTLIL::Wire *wire = w.second; - if (wire->width > 1 && (wire->port_id == 0 || flag_ports)) + if (wire->width > 1 && (wire->port_id == 0 || flag_ports) && design->selected(module, w.second)) worker.splitmap[wire] = std::vector<RTLIL::SigBit>(); } @@ -180,10 +173,10 @@ struct SplitnetsPass : public Pass { module->rewrite_sigspecs(worker); - for (auto &it : worker.splitmap) { - module->wires.erase(it.first->name); - delete it.first; - } + std::set<RTLIL::Wire*> delete_wires; + for (auto &it : worker.splitmap) + delete_wires.insert(it.first); + module->remove(delete_wires); module->fixup_ports(); } diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 834770071..19cdaa621 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -63,13 +63,13 @@ namespace #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode) { #define X(_name) _name = 0; STAT_INT_MEMBERS #undef X - for (auto &it : mod->wires) + for (auto &it : mod->wires_) { if (!design->selected(mod, it.second)) continue; @@ -90,11 +90,35 @@ namespace num_memory_bits += it.second->width * it.second->size; } - for (auto &it : mod->cells) { + for (auto &it : mod->cells_) + { if (!design->selected(mod, it.second)) continue; + + RTLIL::IdString cell_type = it.second->type; + + if (width_mode) + { + if (cell_type.in("$not", "$pos", "$neg", + "$logic_not", "$logic_and", "$logic_or", + "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", + "$lut", "$and", "$or", "$xor", "$xnor", + "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$add", "$sub", "$mul", "$div", "$mod", "$pow")) { + int width_a = it.second->hasPort("\\A") ? SIZE(it.second->getPort("\\A")) : 0; + int width_b = it.second->hasPort("\\B") ? SIZE(it.second->getPort("\\B")) : 0; + int width_y = it.second->hasPort("\\Y") ? SIZE(it.second->getPort("\\Y")) : 0; + cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y})); + } + else if (cell_type.in("$mux", "$pmux")) + cell_type = stringf("%s_%d", cell_type.c_str(), SIZE(it.second->getPort("\\Y"))); + else if (cell_type.in("$sr", "$dff", "$dffsr", "$adff", "$dlatch", "$dlatchsr")) + cell_type = stringf("%s_%d", cell_type.c_str(), SIZE(it.second->getPort("\\Q"))); + } + num_cells++; - num_cells_by_type[it.second->type]++; + num_cells_by_type[cell_type]++; } for (auto &it : mod->processes) { @@ -154,28 +178,37 @@ struct StatPass : public Pass { log(" selected and a module has the 'top' attribute set, this module is used\n"); log(" default value for this option.\n"); log("\n"); + log(" -width\n"); + log(" annotate internal cell types with their word width.\n"); + log(" e.g. $add_8 for an 8 bit wide $add cell.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { log_header("Printing statistics.\n"); + bool width_mode = false; RTLIL::Module *top_mod = NULL; std::map<RTLIL::IdString, statdata_t> mod_stat; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-width") { + width_mode = true; + continue; + } if (args[argidx] == "-top" && argidx+1 < args.size()) { - if (design->modules.count(RTLIL::escape_id(args[argidx+1])) == 0) + if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0) log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str()); - top_mod = design->modules.at(RTLIL::escape_id(args[++argidx])); + top_mod = design->modules_.at(RTLIL::escape_id(args[++argidx])); continue; } break; } extra_args(args, argidx, design); - for (auto &it : design->modules) + for (auto &it : design->modules_) { if (!design->selected_module(it.first)) continue; @@ -184,7 +217,7 @@ struct StatPass : public Pass { if (it.second->get_bool_attribute("\\top")) top_mod = it.second; - statdata_t data(design, it.second); + statdata_t data(design, it.second, width_mode); mod_stat[it.first] = data; log("\n"); diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc new file mode 100644 index 000000000..6f80ef72c --- /dev/null +++ b/passes/cmds/tee.cc @@ -0,0 +1,88 @@ +/* + * 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/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +struct TeePass : public Pass { + TeePass() : Pass("tee", "redirect command output to file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" tee [-q] [-o logfile|-a logfile] cmd\n"); + log("\n"); + log("Execute the specified command, optionally writing the commands output to the\n"); + log("specified logfile(s).\n"); + log("\n"); + log(" -q\n"); + log(" Do not print output to the normal destination (console and/or log file)\n"); + log("\n"); + log(" -o logfile\n"); + log(" Write output to this file, truncate if exists.\n"); + log("\n"); + log(" -a logfile\n"); + log(" Write output to this file, append if exists.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::vector<FILE*> backup_log_files, files_to_close; + backup_log_files = log_files; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-q" && files_to_close.empty()) { + log_files.clear(); + continue; + } + if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) { + const char *open_mode = args[argidx] == "-o" ? "w" : "a+"; + FILE *f = fopen(args[++argidx].c_str(), open_mode); + if (f == NULL) { + for (auto cf : files_to_close) + fclose(cf); + log_cmd_error("Can't create file %s.\n", args[argidx].c_str()); + } + log_files.push_back(f); + files_to_close.push_back(f); + continue; + } + break; + } + + try { + std::vector<std::string> new_args(args.begin() + argidx, args.end()); + Pass::call(design, new_args); + } catch (log_cmd_error_expection) { + for (auto cf : files_to_close) + fclose(cf); + log_files = backup_log_files; + throw log_cmd_error_expection(); + } + + for (auto cf : files_to_close) + fclose(cf); + log_files = backup_log_files; + } +} TeePass; + diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc new file mode 100644 index 000000000..09293a86b --- /dev/null +++ b/passes/cmds/trace.cc @@ -0,0 +1,97 @@ +/* + * 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" + +PRIVATE_NAMESPACE_BEGIN + +struct TraceMonitor : public RTLIL::Monitor +{ + virtual void notify_module_add(RTLIL::Module *module) OVERRIDE + { + log("#TRACE# Module add: %s\n", log_id(module)); + } + + virtual void notify_module_del(RTLIL::Module *module) OVERRIDE + { + log("#TRACE# Module delete: %s\n", log_id(module)); + } + + virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) OVERRIDE + { + log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig)); + } + + virtual void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) OVERRIDE + { + log("#TRACE# Connection in module %s: %s = %s\n", log_id(module), log_signal(sigsig.first), log_signal(sigsig.second)); + } + + virtual void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) OVERRIDE + { + log("#TRACE# New connections in module %s:\n", log_id(module)); + for (auto &sigsig : sigsig_vec) + log("## %s = %s\n", log_signal(sigsig.first), log_signal(sigsig.second)); + } + + virtual void notify_blackout(RTLIL::Module *module) OVERRIDE + { + log("#TRACE# Blackout in module %s:\n", log_id(module)); + } +}; + +struct TracePass : public Pass { + TracePass() : Pass("trace", "redirect command output to file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" trace cmd\n"); + log("\n"); + log("Execute the specified command, logging all changes the command performs on\n"); + log("the design in real time.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // .. parse options .. + break; + } + + TraceMonitor monitor; + design->monitors.insert(&monitor); + + try { + std::vector<std::string> new_args(args.begin() + argidx, args.end()); + Pass::call(design, new_args); + } catch (log_cmd_error_expection) { + design->monitors.erase(&monitor); + throw log_cmd_error_expection(); + } + + design->monitors.erase(&monitor); + } +} TracePass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc new file mode 100644 index 000000000..813e215ba --- /dev/null +++ b/passes/cmds/write_file.cc @@ -0,0 +1,76 @@ +/* + * 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" + +struct WriteFileFrontend : public Frontend { + WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_file [options] output_file [input_file]\n"); + log("\n"); + log("Write the text fron the input file to the output file.\n"); + log("\n"); + log(" -a\n"); + log(" Append to output file (instead of overwriting)\n"); + log("\n"); + log("\n"); + log("Inside a script the input file can also can a here-document:\n"); + log("\n"); + log(" write_file hello.txt <<EOT\n"); + log(" Hello World!\n"); + log(" EOT\n"); + log("\n"); + } + virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*) + { + bool append_mode = false; + std::string output_filename; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-a") { + append_mode = true; + continue; + } + break; + } + + if (argidx < args.size() && args[argidx].rfind("-", 0) != 0) + output_filename = args[argidx++]; + else + log_cmd_error("Missing putput filename.\n"); + + extra_args(f, filename, args, argidx); + + FILE *of = fopen(output_filename.c_str(), append_mode ? "a" : "w"); + char buffer[64 * 1024]; + size_t bytes; + + while (0 < (bytes = f->readsome(buffer, sizeof(buffer)))) + fwrite(buffer, bytes, 1, of); + + fclose(of); + } +} WriteFileFrontend; + diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc index 13e90910b..2fae76091 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -127,12 +127,12 @@ struct FsmPass : public Pass { Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt + encoding_opt); Pass::call(design, "fsm_info"); - if (!flag_nomap) - Pass::call(design, "fsm_map"); - if (flag_export) Pass::call(design, "fsm_export"); + if (!flag_nomap) + Pass::call(design, "fsm_map"); + log_pop(); } } FsmPass; diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index a8ec19126..2c846a4cf 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -26,7 +26,7 @@ static RTLIL::Module *module; static SigMap assign_map; -typedef std::pair<RTLIL::Cell*,std::string> sig2driver_entry_t; +typedef std::pair<RTLIL::Cell*, RTLIL::IdString> sig2driver_entry_t; static SigSet<sig2driver_entry_t> sig2driver, sig2user; static std::set<RTLIL::Cell*> muxtree_cells; static SigPool sig_at_port; @@ -50,14 +50,14 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); for (auto &cellport : cellport_list) { - if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y") + if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") return false; - RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]); - RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]); + RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) return false; - for (int i = 0; i < sig_b.width; i += sig_a.width) - if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.width), recursion_monitor)) + for (int i = 0; i < sig_b.size(); i += sig_a.size()) + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) return false; muxtree_cells.insert(cellport.first); } @@ -80,14 +80,14 @@ static bool check_state_users(RTLIL::SigSpec sig) continue; if (cellport.second != "\\A" && cellport.second != "\\B") return false; - if (cell->connections.count("\\A") == 0 || cell->connections.count("\\B") == 0 || cell->connections.count("\\Y") == 0) + if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y")) return false; - for (auto &port_it : cell->connections) + for (auto &port_it : cell->connections()) if (port_it.first != "\\A" && port_it.first != "\\B" && port_it.first != "\\Y") return false; - if (assign_map(cell->connections["\\A"]) == sig && cell->connections["\\B"].is_fully_const()) + if (assign_map(cell->getPort("\\A")) == sig && cell->getPort("\\B").is_fully_const()) continue; - if (assign_map(cell->connections["\\B"]) == sig && cell->connections["\\A"].is_fully_const()) + if (assign_map(cell->getPort("\\B")) == sig && cell->getPort("\\A").is_fully_const()) continue; return false; } @@ -109,8 +109,8 @@ static void detect_fsm(RTLIL::Wire *wire) continue; muxtree_cells.clear(); SigPool recursion_monitor; - RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]); - RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]); + RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); + RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) { log("Found FSM state register %s in module %s.\n", wire->name.c_str(), module->name.c_str()); wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto"); @@ -148,7 +148,7 @@ struct FsmDetectPass : public Pass { ct.setup_stdcells(); ct.setup_stdcells_mem(); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; @@ -159,8 +159,8 @@ struct FsmDetectPass : public Pass { sig2driver.clear(); sig2user.clear(); sig_at_port.clear(); - for (auto &cell_it : module->cells) - for (auto &conn_it : cell_it.second->connections) { + for (auto &cell_it : module->cells_) + for (auto &conn_it : cell_it.second->connections()) { if (ct.cell_output(cell_it.second->type, conn_it.first) || !ct.cell_known(cell_it.second->type)) { RTLIL::SigSpec sig = conn_it.second; assign_map.apply(sig); @@ -173,11 +173,11 @@ struct FsmDetectPass : public Pass { } } - for (auto &wire_it : module->wires) + for (auto &wire_it : module->wires_) if (wire_it.second->port_id != 0) sig_at_port.add(assign_map(RTLIL::SigSpec(wire_it.second))); - for (auto &wire_it : module->wires) + for (auto &wire_it : module->wires_) if (design->selected(module, wire_it.second)) detect_fsm(wire_it.second); } diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index 5756b10c7..d13643911 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -30,50 +30,50 @@ struct FsmExpand RTLIL::Module *module; RTLIL::Cell *fsm_cell; SigMap assign_map; - SigSet<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> sig2driver, sig2user; + SigSet<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> sig2driver, sig2user; CellTypes ct; - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> merged_set; - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> current_set; - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> no_candidate_set; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> merged_set; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> current_set; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> no_candidate_set; bool already_optimized; int limit_transitions; bool is_cell_merge_candidate(RTLIL::Cell *cell) { - if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux") - if (cell->connections.at("\\A").width < 2) + if (cell->type == "$mux" || cell->type == "$pmux") + if (cell->getPort("\\A").size() < 2) return true; RTLIL::SigSpec new_signals; - if (cell->connections.count("\\A") > 0) - new_signals.append(assign_map(cell->connections["\\A"])); - if (cell->connections.count("\\B") > 0) - new_signals.append(assign_map(cell->connections["\\B"])); - if (cell->connections.count("\\S") > 0) - new_signals.append(assign_map(cell->connections["\\S"])); - if (cell->connections.count("\\Y") > 0) - new_signals.append(assign_map(cell->connections["\\Y"])); + if (cell->hasPort("\\A")) + new_signals.append(assign_map(cell->getPort("\\A"))); + if (cell->hasPort("\\B")) + new_signals.append(assign_map(cell->getPort("\\B"))); + if (cell->hasPort("\\S")) + new_signals.append(assign_map(cell->getPort("\\S"))); + if (cell->hasPort("\\Y")) + new_signals.append(assign_map(cell->getPort("\\Y"))); new_signals.sort_and_unify(); new_signals.remove_const(); - new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"])); - new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"])); + new_signals.remove(assign_map(fsm_cell->getPort("\\CTRL_IN"))); + new_signals.remove(assign_map(fsm_cell->getPort("\\CTRL_OUT"))); - if (new_signals.width > 3) + if (new_signals.size() > 3) return false; - if (cell->connections.count("\\Y") > 0) { - new_signals.append(assign_map(cell->connections["\\Y"])); + if (cell->hasPort("\\Y")) { + new_signals.append(assign_map(cell->getPort("\\Y"))); new_signals.sort_and_unify(); new_signals.remove_const(); - new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"])); - new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"])); + new_signals.remove(assign_map(fsm_cell->getPort("\\CTRL_IN"))); + new_signals.remove(assign_map(fsm_cell->getPort("\\CTRL_OUT"))); } - if (new_signals.width > 2) + if (new_signals.size() > 2) return false; return true; @@ -83,10 +83,10 @@ struct FsmExpand { std::vector<RTLIL::Cell*> cell_list; - for (auto c : sig2driver.find(assign_map(fsm_cell->connections["\\CTRL_IN"]))) + for (auto c : sig2driver.find(assign_map(fsm_cell->getPort("\\CTRL_IN")))) cell_list.push_back(c); - for (auto c : sig2user.find(assign_map(fsm_cell->connections["\\CTRL_OUT"]))) + for (auto c : sig2user.find(assign_map(fsm_cell->getPort("\\CTRL_OUT")))) cell_list.push_back(c); current_set.clear(); @@ -94,7 +94,7 @@ struct FsmExpand { if (merged_set.count(c) > 0 || current_set.count(c) > 0 || no_candidate_set.count(c) > 0) continue; - for (auto &p : c->connections) { + for (auto &p : c->connections()) { if (p.first != "\\A" && p.first != "\\B" && p.first != "\\S" && p.first != "\\Y") goto next_cell; } @@ -135,7 +135,7 @@ struct FsmExpand RTLIL::SigSpec input_sig, output_sig; - for (auto &p : cell->connections) + for (auto &p : cell->connections()) if (ct.cell_output(cell->type, p.first)) output_sig.append(assign_map(p.second)); else @@ -145,38 +145,42 @@ struct FsmExpand std::vector<RTLIL::Const> truth_tab; - for (int i = 0; i < (1 << input_sig.width); i++) { - RTLIL::Const in_val(i, input_sig.width); + for (int i = 0; i < (1 << input_sig.size()); i++) { + RTLIL::Const in_val(i, input_sig.size()); RTLIL::SigSpec A, B, S; - if (cell->connections.count("\\A") > 0) - A = assign_map(cell->connections["\\A"]); - if (cell->connections.count("\\B") > 0) - B = assign_map(cell->connections["\\B"]); - if (cell->connections.count("\\S") > 0) - S = assign_map(cell->connections["\\S"]); + if (cell->hasPort("\\A")) + A = assign_map(cell->getPort("\\A")); + if (cell->hasPort("\\B")) + B = assign_map(cell->getPort("\\B")); + if (cell->hasPort("\\S")) + S = assign_map(cell->getPort("\\S")); A.replace(input_sig, RTLIL::SigSpec(in_val)); B.replace(input_sig, RTLIL::SigSpec(in_val)); S.replace(input_sig, RTLIL::SigSpec(in_val)); - assert(A.is_fully_const()); - assert(B.is_fully_const()); - assert(S.is_fully_const()); + log_assert(A.is_fully_const()); + log_assert(B.is_fully_const()); + log_assert(S.is_fully_const()); truth_tab.push_back(ct.eval(cell, A.as_const(), B.as_const(), S.as_const())); } FsmData fsm_data; fsm_data.copy_from_cell(fsm_cell); - fsm_data.num_inputs += input_sig.width; - fsm_cell->connections["\\CTRL_IN"].append(input_sig); + fsm_data.num_inputs += input_sig.size(); + RTLIL::SigSpec new_ctrl_in = fsm_cell->getPort("\\CTRL_IN"); + new_ctrl_in.append(input_sig); + fsm_cell->setPort("\\CTRL_IN", new_ctrl_in); - fsm_data.num_outputs += output_sig.width; - fsm_cell->connections["\\CTRL_OUT"].append(output_sig); + fsm_data.num_outputs += output_sig.size(); + RTLIL::SigSpec new_ctrl_out = fsm_cell->getPort("\\CTRL_OUT"); + new_ctrl_out.append(output_sig); + fsm_cell->setPort("\\CTRL_OUT", new_ctrl_out); std::vector<FsmData::transition_t> new_transition_table; for (auto &tr : fsm_data.transition_table) { - for (int i = 0; i < (1 << input_sig.width); i++) { + for (int i = 0; i < (1 << input_sig.size()); i++) { FsmData::transition_t new_tr = tr; - RTLIL::Const in_val(i, input_sig.width); + RTLIL::Const in_val(i, input_sig.size()); RTLIL::Const out_val = truth_tab[i]; RTLIL::SigSpec ctrl_in = new_tr.ctrl_in; RTLIL::SigSpec ctrl_out = new_tr.ctrl_out; @@ -201,10 +205,10 @@ struct FsmExpand assign_map.set(module); ct.setup_internals(); - for (auto &cell_it : module->cells) { + for (auto &cell_it : module->cells_) { RTLIL::Cell *c = cell_it.second; if (ct.cell_known(c->type) && design->selected(mod, c)) - for (auto &p : c->connections) { + for (auto &p : c->connections()) { if (ct.cell_output(c->type, p.first)) sig2driver.insert(assign_map(p.second), c); else @@ -226,10 +230,8 @@ struct FsmExpand merge_cell_into_fsm(c); } - for (auto c : merged_set) { - module->cells.erase(c->name); - delete c; - } + for (auto c : merged_set) + module->remove(c); if (merged_set.size() > 0 && !already_optimized) FsmData::optimize_fsm(fsm_cell, module); @@ -256,11 +258,11 @@ struct FsmExpandPass : public Pass { log_header("Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; std::vector<RTLIL::Cell*> fsm_cells; - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) fsm_cells.push_back(cell_it.second); for (auto c : fsm_cells) { diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index cc328ce34..b4a6b3f7b 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -32,10 +32,7 @@ * Convert a signal into a KISS-compatible textual representation. */ std::string kiss_convert_signal(const RTLIL::SigSpec &sig) { - if (!sig.is_fully_const()) { - throw 0; - } - + log_assert(sig.is_fully_const()); return sig.as_const().as_string(); } @@ -59,13 +56,12 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::st attr_it = cell->attributes.find("\\fsm_export"); if (!filename.empty()) { - kiss_name.assign(filename); + kiss_name.assign(filename); } else if (attr_it != cell->attributes.end() && attr_it->second.decode_string() != "") { kiss_name.assign(attr_it->second.decode_string()); } else { - kiss_name.assign(module->name); - kiss_name.append('-' + cell->name + ".kiss2"); + kiss_name.assign(log_id(module) + std::string("-") + log_id(cell) + ".kiss2"); } log("\n"); @@ -174,9 +170,9 @@ struct FsmExportPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) { attr_it = cell_it.second->attributes.find("\\fsm_export"); if (!flag_noauto || (attr_it != cell_it.second->attributes.end())) { diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 9cba904a7..451f00fcb 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -31,56 +31,72 @@ static RTLIL::Module *module; static SigMap assign_map; -typedef std::pair<std::string, std::string> sig2driver_entry_t; +typedef std::pair<RTLIL::IdString, RTLIL::IdString> sig2driver_entry_t; static SigSet<sig2driver_entry_t> sig2driver, sig2trigger; +static std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> exclusive_ctrls; static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL::SigSpec &ctrl, std::map<RTLIL::Const, int> &states, RTLIL::Const *reset_state = NULL) { - sig.extend(dff_out.width, false); + sig.extend(dff_out.size(), false); if (sig == dff_out) return true; assign_map.apply(sig); if (sig.is_fully_const()) { - sig.optimize(); - assert(sig.chunks.size() == 1); - if (states.count(sig.chunks[0].data) == 0) { + if (sig.is_fully_def() && states.count(sig.as_const()) == 0) { log(" found state code: %s\n", log_signal(sig)); - states[sig.chunks[0].data] = -1; + states[sig.as_const()] = -1; } return true; } std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); - for (auto &cellport : cellport_list) { - RTLIL::Cell *cell = module->cells.at(cellport.first); - if ((cell->type != "$mux" && cell->type != "$pmux" && cell->type != "$safe_pmux") || cellport.second != "\\Y") { + for (auto &cellport : cellport_list) + { + RTLIL::Cell *cell = module->cells_.at(cellport.first); + if ((cell->type != "$mux" && cell->type != "$pmux") || cellport.second != "\\Y") { log(" unexpected cell type %s (%s) found in state selection tree.\n", cell->type.c_str(), cell->name.c_str()); return false; } - RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec sig_b = assign_map(cell->connections["\\B"]); - RTLIL::SigSpec sig_s = assign_map(cell->connections["\\S"]); + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S")); + RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + RTLIL::SigSpec sig_aa = sig; + sig_aa.replace(sig_y, sig_a); + + RTLIL::SigSpec sig_bb; + for (int i = 0; i < SIZE(sig_b)/SIZE(sig_a); i++) { + RTLIL::SigSpec s = sig; + s.replace(sig_y, sig_b.extract(i*SIZE(sig_a), SIZE(sig_a))); + sig_bb.append(s); + } + if (reset_state && RTLIL::SigSpec(*reset_state).is_fully_undef()) do { - if (sig_a.is_fully_def()) - *reset_state = sig_a.as_const(); - else if (sig_b.is_fully_def()) - *reset_state = sig_b.as_const(); + if (sig_aa.is_fully_def()) + *reset_state = sig_aa.as_const(); + else if (sig_bb.is_fully_def()) + *reset_state = sig_bb.as_const(); else break; log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state)); } while (0); - if (ctrl.extract(sig_s).width == 0) { + + if (ctrl.extract(sig_s).size() == 0) { log(" found ctrl input: %s\n", log_signal(sig_s)); ctrl.append(sig_s); } - if (!find_states(sig_a, dff_out, ctrl, states)) + + if (!find_states(sig_aa, dff_out, ctrl, states)) return false; - for (int i = 0; i < sig_b.width/sig_a.width; i++) { - if (!find_states(sig_b.extract(i*sig_a.width, sig_a.width), dff_out, ctrl, states)) + + for (int i = 0; i < SIZE(sig_bb)/SIZE(sig_aa); i++) { + if (!find_states(sig_bb.extract(i*SIZE(sig_aa), SIZE(sig_aa)), dff_out, ctrl, states)) return false; } } @@ -90,47 +106,69 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL static RTLIL::Const sig2const(ConstEval &ce, RTLIL::SigSpec sig, RTLIL::State noconst_state, RTLIL::SigSpec dont_care = RTLIL::SigSpec()) { - if (dont_care.width > 0) { - sig.expand(); - for (auto &chunk : sig.chunks) { - assert(chunk.width == 1); - if (dont_care.extract(chunk).width > 0) - chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state); - } - sig.optimize(); + if (dont_care.size() > 0) { + for (int i = 0; i < SIZE(sig); i++) + if (dont_care.extract(sig[i]).size() > 0) + sig[i] = noconst_state; } ce.assign_map.apply(sig); ce.values_map.apply(sig); - sig.expand(); - for (auto &chunk : sig.chunks) { - assert(chunk.width == 1); - if (chunk.wire != NULL) - chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state); - } - sig.optimize(); + for (int i = 0; i < SIZE(sig); i++) + if (sig[i].wire != NULL) + sig[i] = noconst_state; - if (sig.width == 0) - return RTLIL::Const(); - assert(sig.chunks.size() == 1 && sig.chunks[0].wire == NULL); - return sig.chunks[0].data; + return sig.as_const(); } static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_data, std::map<RTLIL::Const, int> &states, int state_in, RTLIL::SigSpec ctrl_in, RTLIL::SigSpec ctrl_out, RTLIL::SigSpec dff_in, RTLIL::SigSpec dont_care) { + bool undef_bit_in_next_state_mode = false; RTLIL::SigSpec undef, constval; - if (ce.eval(ctrl_out, undef) && ce.eval(dff_in, undef)) { - assert(ctrl_out.is_fully_const() && dff_in.is_fully_const()); + if (ce.eval(ctrl_out, undef) && ce.eval(dff_in, undef)) + { + if (0) { +undef_bit_in_next_state: + for (auto &bit : dff_in) + if (bit.wire != nullptr) bit = RTLIL::Sm; + for (auto &bit : ctrl_out) + if (bit.wire != nullptr) bit = RTLIL::Sm; + undef_bit_in_next_state_mode = true; + } + + log_assert(ctrl_out.is_fully_const() && dff_in.is_fully_const()); + FsmData::transition_t tr; - tr.state_in = state_in; - tr.state_out = states[ce.values_map(ce.assign_map(dff_in)).as_const()]; tr.ctrl_in = sig2const(ce, ctrl_in, RTLIL::State::Sa, dont_care); tr.ctrl_out = sig2const(ce, ctrl_out, RTLIL::State::Sx); + + std::map<RTLIL::SigBit, int> ctrl_in_bit_indices; + for (int i = 0; i < SIZE(ctrl_in); i++) + ctrl_in_bit_indices[ctrl_in[i]] = i; + + for (auto &it : ctrl_in_bit_indices) + if (tr.ctrl_in.bits.at(it.second) == RTLIL::S1 && exclusive_ctrls.count(it.first) != 0) + for (auto &dc_bit : exclusive_ctrls.at(it.first)) + if (ctrl_in_bit_indices.count(dc_bit)) + tr.ctrl_in.bits.at(ctrl_in_bit_indices.at(dc_bit)) = RTLIL::State::Sa; + RTLIL::Const log_state_in = RTLIL::Const(RTLIL::State::Sx, fsm_data.state_bits); if (state_in >= 0) - log_state_in = fsm_data.state_table[tr.state_in]; + log_state_in = fsm_data.state_table.at(state_in); + + if (states.count(ce.values_map(ce.assign_map(dff_in)).as_const()) == 0) { + log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transistion!>%s\n", + log_signal(log_state_in), log_signal(tr.ctrl_in), + log_signal(ce.values_map(ce.assign_map(dff_in))), log_signal(tr.ctrl_out), + undef_bit_in_next_state_mode ? " SHORTENED" : ""); + return; + } + + tr.state_in = state_in; + tr.state_out = states.at(ce.values_map(ce.assign_map(dff_in)).as_const()); + if (dff_in.is_fully_def()) { fsm_data.transition_table.push_back(tr); log(" transition: %10s %s -> %10s %s\n", @@ -144,7 +182,11 @@ static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_d return; } - log_assert(undef.width > 0); + for (auto &bit : dff_in) + if (bit == RTLIL::Sx) + goto undef_bit_in_next_state; + + log_assert(undef.size() > 0); log_assert(ce.stop_signals.check_all(undef)); undef = undef.extract(0, 1); @@ -155,21 +197,39 @@ static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_d ce.push(); dont_care.append(undef); ce.set(undef, constval.as_const()); + if (exclusive_ctrls.count(undef) && constval == RTLIL::S1) + for (auto &bit : exclusive_ctrls.at(undef)) { + RTLIL::SigSpec bitval = bit; + if (ce.eval(bitval) && bitval != RTLIL::S0) + goto found_contradiction_1; + else + ce.set(bit, RTLIL::S0); + } find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care); + found_contradiction_1: ce.pop(); } else { ce.push(), ce_nostop.push(); - ce.set(undef, RTLIL::Const(0, 1)); - ce_nostop.set(undef, RTLIL::Const(0, 1)); + ce.set(undef, RTLIL::S0); + ce_nostop.set(undef, RTLIL::S0); find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care); ce.pop(), ce_nostop.pop(); ce.push(), ce_nostop.push(); - ce.set(undef, RTLIL::Const(1, 1)); - ce_nostop.set(undef, RTLIL::Const(1, 1)); + ce.set(undef, RTLIL::S1); + ce_nostop.set(undef, RTLIL::S1); + if (exclusive_ctrls.count(undef)) + for (auto &bit : exclusive_ctrls.at(undef)) { + RTLIL::SigSpec bitval = bit; + if ((ce.eval(bitval) || ce_nostop.eval(bitval)) && bitval != RTLIL::S0) + goto found_contradiction_2; + else + ce.set(bit, RTLIL::S0), ce_nostop.set(bit, RTLIL::S0); + } find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care); + found_contradiction_2: ce.pop(), ce_nostop.pop(); } } @@ -184,24 +244,24 @@ static void extract_fsm(RTLIL::Wire *wire) RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width); RTLIL::Const reset_state(RTLIL::State::Sx, wire->width); - RTLIL::SigSpec clk = RTLIL::SigSpec(0, 1); - RTLIL::SigSpec arst = RTLIL::SigSpec(0, 1); + RTLIL::SigSpec clk = RTLIL::S0; + RTLIL::SigSpec arst = RTLIL::S0; bool clk_polarity = true; bool arst_polarity = true; std::set<sig2driver_entry_t> cellport_list; sig2driver.find(dff_out, cellport_list); for (auto &cellport : cellport_list) { - RTLIL::Cell *cell = module->cells.at(cellport.first); + RTLIL::Cell *cell = module->cells_.at(cellport.first); if ((cell->type != "$dff" && cell->type != "$adff") || cellport.second != "\\Q") continue; log(" found %s cell for state register: %s\n", cell->type.c_str(), cell->name.c_str()); - RTLIL::SigSpec sig_q = assign_map(cell->connections["\\Q"]); - RTLIL::SigSpec sig_d = assign_map(cell->connections["\\D"]); - clk = cell->connections["\\CLK"]; + RTLIL::SigSpec sig_q = assign_map(cell->getPort("\\Q")); + RTLIL::SigSpec sig_d = assign_map(cell->getPort("\\D")); + clk = cell->getPort("\\CLK"); clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); if (cell->type == "$adff") { - arst = cell->connections["\\ARST"]; + arst = cell->getPort("\\ARST"); arst_polarity = cell->parameters["\\ARST_POLARITY"].as_bool(); reset_state = cell->parameters["\\ARST_VALUE"]; } @@ -227,6 +287,10 @@ static void extract_fsm(RTLIL::Wire *wire) log(" fsm extraction failed: state selection tree is not closed.\n"); return; } + if (SIZE(states) <= 1) { + log(" fsm extraction failed: at least two states are required.\n"); + return; + } // find control outputs // (add the state signals to the list of control outputs. if everything goes right, this signals @@ -236,10 +300,10 @@ static void extract_fsm(RTLIL::Wire *wire) cellport_list.clear(); sig2trigger.find(dff_out, cellport_list); for (auto &cellport : cellport_list) { - RTLIL::Cell *cell = module->cells.at(cellport.first); - RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec sig_b = assign_map(cell->connections["\\B"]); - RTLIL::SigSpec sig_y = assign_map(cell->connections["\\Y"]); + RTLIL::Cell *cell = module->cells_.at(cellport.first); + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y")); if (cellport.second == "\\A" && !sig_b.is_fully_const()) continue; if (cellport.second == "\\B" && !sig_a.is_fully_const()) @@ -258,8 +322,8 @@ static void extract_fsm(RTLIL::Wire *wire) // Initialize fsm data struct FsmData fsm_data; - fsm_data.num_inputs = ctrl_in.width; - fsm_data.num_outputs = ctrl_out.width; + fsm_data.num_inputs = ctrl_in.size(); + fsm_data.num_outputs = ctrl_out.size(); fsm_data.state_bits = wire->width; fsm_data.reset_state = -1; for (auto &it : states) { @@ -283,40 +347,34 @@ static void extract_fsm(RTLIL::Wire *wire) // create fsm cell - RTLIL::Cell *fsm_cell = new RTLIL::Cell; - fsm_cell->name = stringf("$fsm$%s$%d", wire->name.c_str(), RTLIL::autoidx++); - fsm_cell->type = "$fsm"; - fsm_cell->connections["\\CLK"] = clk; - fsm_cell->connections["\\ARST"] = arst; - fsm_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity ? 1 : 0, 1); - fsm_cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity ? 1 : 0, 1); - fsm_cell->connections["\\CTRL_IN"] = ctrl_in; - fsm_cell->connections["\\CTRL_OUT"] = ctrl_out; - fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name); + RTLIL::Cell *fsm_cell = module->addCell(stringf("$fsm$%s$%d", wire->name.c_str(), autoidx++), "$fsm"); + fsm_cell->setPort("\\CLK", clk); + fsm_cell->setPort("\\ARST", arst); + fsm_cell->parameters["\\CLK_POLARITY"] = clk_polarity ? RTLIL::S1 : RTLIL::S0; + fsm_cell->parameters["\\ARST_POLARITY"] = arst_polarity ? RTLIL::S1 : RTLIL::S0; + fsm_cell->setPort("\\CTRL_IN", ctrl_in); + fsm_cell->setPort("\\CTRL_OUT", ctrl_out); + fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name.str()); fsm_cell->attributes = wire->attributes; fsm_data.copy_to_cell(fsm_cell); - module->cells[fsm_cell->name] = fsm_cell; // rename original state wire - module->wires.erase(wire->name); + module->wires_.erase(wire->name); wire->attributes.erase("\\fsm_encoding"); wire->name = stringf("$fsm$oldstate%s", wire->name.c_str()); - module->wires[wire->name] = wire; + module->wires_[wire->name] = wire; // unconnect control outputs from old drivers cellport_list.clear(); sig2driver.find(ctrl_out, cellport_list); for (auto &cellport : cellport_list) { - RTLIL::Cell *cell = module->cells.at(cellport.first); - RTLIL::SigSpec port_sig = assign_map(cell->connections[cellport.second]); + RTLIL::Cell *cell = module->cells_.at(cellport.first); + RTLIL::SigSpec port_sig = assign_map(cell->getPort(cellport.second)); RTLIL::SigSpec unconn_sig = port_sig.extract(ctrl_out); - RTLIL::Wire *unconn_wire = new RTLIL::Wire; - unconn_wire->name = stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), RTLIL::autoidx++); - unconn_wire->width = unconn_sig.width; - module->wires[unconn_wire->name] = unconn_wire; - port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cell->connections[cellport.second]); + RTLIL::Wire *unconn_wire = module->addWire(stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), autoidx++), unconn_sig.size()); + port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cell->connections_[cellport.second]); } } @@ -349,7 +407,7 @@ struct FsmExtractPass : public Pass { ct.setup_stdcells(); ct.setup_stdcells_mem(); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; @@ -359,23 +417,32 @@ struct FsmExtractPass : public Pass { sig2driver.clear(); sig2trigger.clear(); - for (auto &cell_it : module->cells) - for (auto &conn_it : cell_it.second->connections) { - if (ct.cell_output(cell_it.second->type, conn_it.first) || !ct.cell_known(cell_it.second->type)) { + exclusive_ctrls.clear(); + for (auto cell : module->cells()) { + for (auto &conn_it : cell->connections()) { + if (ct.cell_output(cell->type, conn_it.first) || !ct.cell_known(cell->type)) { RTLIL::SigSpec sig = conn_it.second; assign_map.apply(sig); - sig2driver.insert(sig, sig2driver_entry_t(cell_it.first, conn_it.first)); + sig2driver.insert(sig, sig2driver_entry_t(cell->name, conn_it.first)); } - if (ct.cell_input(cell_it.second->type, conn_it.first) && cell_it.second->connections.count("\\Y") > 0 && - cell_it.second->connections["\\Y"].width == 1 && (conn_it.first == "\\A" || conn_it.first == "\\B")) { + if (ct.cell_input(cell->type, conn_it.first) && cell->hasPort("\\Y") && + cell->getPort("\\Y").size() == 1 && (conn_it.first == "\\A" || conn_it.first == "\\B")) { RTLIL::SigSpec sig = conn_it.second; assign_map.apply(sig); - sig2trigger.insert(sig, sig2driver_entry_t(cell_it.first, conn_it.first)); + sig2trigger.insert(sig, sig2driver_entry_t(cell->name, conn_it.first)); } } + if (cell->type == "$pmux") { + RTLIL::SigSpec sel_sig = assign_map(cell->getPort("\\S")); + for (auto &bit1 : sel_sig) + for (auto &bit2 : sel_sig) + if (bit1 != bit2) + exclusive_ctrls[bit1].insert(bit2); + } + } std::vector<RTLIL::Wire*> wire_list; - for (auto &wire_it : module->wires) + for (auto &wire_it : module->wires_) if (wire_it.second->attributes.count("\\fsm_encoding") > 0 && wire_it.second->attributes["\\fsm_encoding"].decode_string() != "none") if (design->selected(module, wire_it.second)) wire_list.push_back(wire_it.second); diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index f2d0c1a81..45d68a906 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -43,9 +43,9 @@ struct FsmInfoPass : public Pass { log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) { log("\n"); log("FSM `%s' from module `%s':\n", cell_it.second->name.c_str(), mod_it.first.c_str()); diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index c30cf1fe7..ab6d5671d 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -25,12 +25,26 @@ #include "fsmdata.h" #include <string.h> +static bool pattern_is_subset(const RTLIL::Const &super_pattern, const RTLIL::Const &sub_pattern) +{ + log_assert(SIZE(super_pattern.bits) == SIZE(sub_pattern.bits)); + for (int i = 0; i < SIZE(super_pattern.bits); i++) + if (sub_pattern.bits[i] == RTLIL::State::S0 || sub_pattern.bits[i] == RTLIL::State::S1) { + if (super_pattern.bits[i] == RTLIL::State::S0 || super_pattern.bits[i] == RTLIL::State::S1) { + if (super_pattern.bits[i] != sub_pattern.bits[i]) + return false; + } else + return false; + } + return true; +} + static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const, std::set<int>> &pattern_cache, std::set<int> &fullstate_cache, int num_states, RTLIL::Wire *state_onehot, RTLIL::SigSpec &ctrl_in, RTLIL::SigSpec output) { RTLIL::SigSpec cases_vector; for (int in_state : fullstate_cache) - cases_vector.append(RTLIL::SigSpec(state_onehot, 1, in_state)); + cases_vector.append(RTLIL::SigSpec(state_onehot, in_state)); for (auto &it : pattern_cache) { @@ -42,89 +56,74 @@ static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const eq_sig_a.append(ctrl_in.extract(j, 1)); eq_sig_b.append(RTLIL::SigSpec(pattern.bits[j])); } - eq_sig_a.optimize(); - eq_sig_b.optimize(); for (int in_state : it.second) if (fullstate_cache.count(in_state) == 0) - or_sig.append(RTLIL::SigSpec(state_onehot, 1, in_state)); - or_sig.optimize(); + or_sig.append(RTLIL::SigSpec(state_onehot, in_state)); - if (or_sig.width == 0) + if (or_sig.size() == 0) continue; RTLIL::SigSpec and_sig; - if (eq_sig_a.width > 0) + if (eq_sig_a.size() > 0) { - RTLIL::Wire *eq_wire = new RTLIL::Wire; - eq_wire->name = NEW_ID; - module->add(eq_wire); - - RTLIL::Cell *eq_cell = new RTLIL::Cell; - eq_cell->name = NEW_ID; - eq_cell->type = "$eq"; - eq_cell->connections["\\A"] = eq_sig_a; - eq_cell->connections["\\B"] = eq_sig_b; - eq_cell->connections["\\Y"] = RTLIL::SigSpec(eq_wire); + RTLIL::Wire *eq_wire = module->addWire(NEW_ID); + and_sig.append(RTLIL::SigSpec(eq_wire)); + + RTLIL::Cell *eq_cell = module->addCell(NEW_ID, "$eq"); + eq_cell->setPort("\\A", eq_sig_a); + eq_cell->setPort("\\B", eq_sig_b); + eq_cell->setPort("\\Y", RTLIL::SigSpec(eq_wire)); eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false); - eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(eq_sig_a.width); - eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(eq_sig_b.width); + eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(eq_sig_a.size()); + eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(eq_sig_b.size()); eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->add(eq_cell); - - and_sig.append(RTLIL::SigSpec(eq_wire)); } - if (or_sig.width < num_states-int(fullstate_cache.size())) + std::set<int> complete_in_state_cache = it.second; + + for (auto &it2 : pattern_cache) + if (pattern_is_subset(pattern, it2.first)) + complete_in_state_cache.insert(it2.second.begin(), it2.second.end()); + + if (SIZE(complete_in_state_cache) < num_states) { - if (or_sig.width == 1) + if (or_sig.size() == 1) { and_sig.append(or_sig); } else { - RTLIL::Wire *or_wire = new RTLIL::Wire; - or_wire->name = NEW_ID; - module->add(or_wire); - - RTLIL::Cell *or_cell = new RTLIL::Cell; - or_cell->name = NEW_ID; - or_cell->type = "$reduce_or"; - or_cell->connections["\\A"] = or_sig; - or_cell->connections["\\Y"] = RTLIL::SigSpec(or_wire); + RTLIL::Wire *or_wire = module->addWire(NEW_ID); + and_sig.append(RTLIL::SigSpec(or_wire)); + + RTLIL::Cell *or_cell = module->addCell(NEW_ID, "$reduce_or"); + or_cell->setPort("\\A", or_sig); + or_cell->setPort("\\Y", RTLIL::SigSpec(or_wire)); or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); - or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(or_sig.width); + or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(or_sig.size()); or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->add(or_cell); - - and_sig.append(RTLIL::SigSpec(or_wire)); } } - switch (and_sig.width) + switch (and_sig.size()) { case 2: { - RTLIL::Wire *and_wire = new RTLIL::Wire; - and_wire->name = NEW_ID; - module->add(and_wire); - - RTLIL::Cell *and_cell = new RTLIL::Cell; - and_cell->name = NEW_ID; - and_cell->type = "$and"; - and_cell->connections["\\A"] = and_sig.extract(0, 1); - and_cell->connections["\\B"] = and_sig.extract(1, 1); - and_cell->connections["\\Y"] = RTLIL::SigSpec(and_wire); + RTLIL::Wire *and_wire = module->addWire(NEW_ID); + cases_vector.append(RTLIL::SigSpec(and_wire)); + + RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$and"); + and_cell->setPort("\\A", and_sig.extract(0, 1)); + and_cell->setPort("\\B", and_sig.extract(1, 1)); + and_cell->setPort("\\Y", RTLIL::SigSpec(and_wire)); and_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); and_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false); and_cell->parameters["\\A_WIDTH"] = RTLIL::Const(1); and_cell->parameters["\\B_WIDTH"] = RTLIL::Const(1); and_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->add(and_cell); - - cases_vector.append(RTLIL::SigSpec(and_wire)); break; } case 1: @@ -138,20 +137,17 @@ static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const } } - if (cases_vector.width > 1) { - RTLIL::Cell *or_cell = new RTLIL::Cell; - or_cell->name = NEW_ID; - or_cell->type = "$reduce_or"; - or_cell->connections["\\A"] = cases_vector; - or_cell->connections["\\Y"] = output; + if (cases_vector.size() > 1) { + RTLIL::Cell *or_cell = module->addCell(NEW_ID, "$reduce_or"); + or_cell->setPort("\\A", cases_vector); + or_cell->setPort("\\Y", output); or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); - or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cases_vector.width); + or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cases_vector.size()); or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->add(or_cell); - } else if (cases_vector.width == 1) { - module->connections.push_back(RTLIL::SigSig(output, cases_vector)); + } else if (cases_vector.size() == 1) { + module->connect(RTLIL::SigSig(output, cases_vector)); } else { - module->connections.push_back(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1))); + module->connect(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1))); } } @@ -162,26 +158,16 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) FsmData fsm_data; fsm_data.copy_from_cell(fsm_cell); - RTLIL::SigSpec ctrl_in = fsm_cell->connections["\\CTRL_IN"]; - RTLIL::SigSpec ctrl_out = fsm_cell->connections["\\CTRL_OUT"]; + RTLIL::SigSpec ctrl_in = fsm_cell->getPort("\\CTRL_IN"); + RTLIL::SigSpec ctrl_out = fsm_cell->getPort("\\CTRL_OUT"); // create state register - RTLIL::Wire *state_wire = new RTLIL::Wire; - state_wire->name = fsm_cell->parameters["\\NAME"].decode_string(); - while (module->count_id(state_wire->name) > 0) - state_wire->name += "_"; - state_wire->width = fsm_data.state_bits; - module->add(state_wire); - - RTLIL::Wire *next_state_wire = new RTLIL::Wire; - next_state_wire->name = NEW_ID; - next_state_wire->width = fsm_data.state_bits; - module->add(next_state_wire); - - RTLIL::Cell *state_dff = new RTLIL::Cell; - state_dff->name = NEW_ID; - if (fsm_cell->connections["\\ARST"].is_fully_const()) { + RTLIL::Wire *state_wire = module->addWire(module->uniquify(fsm_cell->parameters["\\NAME"].decode_string()), fsm_data.state_bits); + RTLIL::Wire *next_state_wire = module->addWire(NEW_ID, fsm_data.state_bits); + + RTLIL::Cell *state_dff = module->addCell(NEW_ID, ""); + if (fsm_cell->getPort("\\ARST").is_fully_const()) { state_dff->type = "$dff"; } else { state_dff->type = "$adff"; @@ -190,23 +176,19 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) for (auto &bit : state_dff->parameters["\\ARST_VALUE"].bits) if (bit != RTLIL::State::S1) bit = RTLIL::State::S0; - state_dff->connections["\\ARST"] = fsm_cell->connections["\\ARST"]; + state_dff->setPort("\\ARST", fsm_cell->getPort("\\ARST")); } state_dff->parameters["\\WIDTH"] = RTLIL::Const(fsm_data.state_bits); state_dff->parameters["\\CLK_POLARITY"] = fsm_cell->parameters["\\CLK_POLARITY"]; - state_dff->connections["\\CLK"] = fsm_cell->connections["\\CLK"]; - state_dff->connections["\\D"] = RTLIL::SigSpec(next_state_wire); - state_dff->connections["\\Q"] = RTLIL::SigSpec(state_wire); - module->add(state_dff); + state_dff->setPort("\\CLK", fsm_cell->getPort("\\CLK")); + state_dff->setPort("\\D", RTLIL::SigSpec(next_state_wire)); + state_dff->setPort("\\Q", RTLIL::SigSpec(state_wire)); // decode state register bool encoding_is_onehot = true; - RTLIL::Wire *state_onehot = new RTLIL::Wire; - state_onehot->name = NEW_ID; - state_onehot->width = fsm_data.state_table.size(); - module->add(state_onehot); + RTLIL::Wire *state_onehot = module->addWire(NEW_ID, fsm_data.state_table.size()); for (size_t i = 0; i < fsm_data.state_table.size(); i++) { @@ -215,111 +197,102 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) for (size_t j = 0; j < state.bits.size(); j++) if (state.bits[j] == RTLIL::State::S0 || state.bits[j] == RTLIL::State::S1) { - sig_a.append(RTLIL::SigSpec(state_wire, 1, j)); + sig_a.append(RTLIL::SigSpec(state_wire, j)); sig_b.append(RTLIL::SigSpec(state.bits[j])); } - sig_a.optimize(); - sig_b.optimize(); if (sig_b == RTLIL::SigSpec(RTLIL::State::S1)) { - module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(state_onehot, 1, i), sig_a)); + module->connect(RTLIL::SigSig(RTLIL::SigSpec(state_onehot, i), sig_a)); } else { encoding_is_onehot = false; - RTLIL::Cell *eq_cell = new RTLIL::Cell; - eq_cell->name = NEW_ID; - eq_cell->type = "$eq"; - eq_cell->connections["\\A"] = sig_a; - eq_cell->connections["\\B"] = sig_b; - eq_cell->connections["\\Y"] = RTLIL::SigSpec(state_onehot, 1, i); + RTLIL::Cell *eq_cell = module->addCell(NEW_ID, "$eq"); + eq_cell->setPort("\\A", sig_a); + eq_cell->setPort("\\B", sig_b); + eq_cell->setPort("\\Y", RTLIL::SigSpec(state_onehot, i)); eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false); - eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_a.width); - eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(sig_b.width); + eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_a.size()); + eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(sig_b.size()); eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->add(eq_cell); } } // generate next_state signal - RTLIL::Wire *next_state_onehot = new RTLIL::Wire; - next_state_onehot->name = NEW_ID; - next_state_onehot->width = fsm_data.state_table.size(); - module->add(next_state_onehot); - - for (size_t i = 0; i < fsm_data.state_table.size(); i++) + if (SIZE(fsm_data.state_table) == 1) { - std::map<RTLIL::Const, std::set<int>> pattern_cache; - std::set<int> fullstate_cache; + module->connect(next_state_wire, fsm_data.state_table.front()); + } + else + { + RTLIL::Wire *next_state_onehot = module->addWire(NEW_ID, fsm_data.state_table.size()); - for (size_t j = 0; j < fsm_data.state_table.size(); j++) - fullstate_cache.insert(j); + for (size_t i = 0; i < fsm_data.state_table.size(); i++) + { + std::map<RTLIL::Const, std::set<int>> pattern_cache; + std::set<int> fullstate_cache; - for (auto &tr : fsm_data.transition_table) { - if (tr.state_out == int(i)) - pattern_cache[tr.ctrl_in].insert(tr.state_in); - else - fullstate_cache.erase(tr.state_in); - } + for (size_t j = 0; j < fsm_data.state_table.size(); j++) + fullstate_cache.insert(j); - implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, RTLIL::SigSpec(next_state_onehot, 1, i)); - } + for (auto &tr : fsm_data.transition_table) { + if (tr.state_out == int(i)) + pattern_cache[tr.ctrl_in].insert(tr.state_in); + else + fullstate_cache.erase(tr.state_in); + } - if (encoding_is_onehot) - { - RTLIL::SigSpec next_state_sig(RTLIL::State::Sm, next_state_wire->width); - for (size_t i = 0; i < fsm_data.state_table.size(); i++) { - RTLIL::Const state = fsm_data.state_table[i]; - int bit_idx = -1; - for (size_t j = 0; j < state.bits.size(); j++) - if (state.bits[j] == RTLIL::State::S1) - bit_idx = j; - if (bit_idx >= 0) - next_state_sig.replace(bit_idx, RTLIL::SigSpec(next_state_onehot, 1, i)); + implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, RTLIL::SigSpec(next_state_onehot, i)); } - log_assert(!next_state_sig.has_marked_bits()); - module->connections.push_back(RTLIL::SigSig(next_state_wire, next_state_sig)); - } - else - { - RTLIL::SigSpec sig_a, sig_b, sig_s; - int reset_state = fsm_data.reset_state; - if (reset_state < 0) - reset_state = 0; - - for (size_t i = 0; i < fsm_data.state_table.size(); i++) { - RTLIL::Const state = fsm_data.state_table[i]; - if (int(i) == fsm_data.reset_state) { - sig_a = RTLIL::SigSpec(state); - } else { - sig_b.append(RTLIL::SigSpec(state)); - sig_s.append(RTLIL::SigSpec(next_state_onehot, 1, i)); + + if (encoding_is_onehot) + { + RTLIL::SigSpec next_state_sig(RTLIL::State::Sm, next_state_wire->width); + for (size_t i = 0; i < fsm_data.state_table.size(); i++) { + RTLIL::Const state = fsm_data.state_table[i]; + int bit_idx = -1; + for (size_t j = 0; j < state.bits.size(); j++) + if (state.bits[j] == RTLIL::State::S1) + bit_idx = j; + if (bit_idx >= 0) + next_state_sig.replace(bit_idx, RTLIL::SigSpec(next_state_onehot, i)); } + log_assert(!next_state_sig.has_marked_bits()); + module->connect(RTLIL::SigSig(next_state_wire, next_state_sig)); } + else + { + RTLIL::SigSpec sig_a, sig_b, sig_s; + int reset_state = fsm_data.reset_state; + if (reset_state < 0) + reset_state = 0; + + for (size_t i = 0; i < fsm_data.state_table.size(); i++) { + RTLIL::Const state = fsm_data.state_table[i]; + if (int(i) == fsm_data.reset_state) { + sig_a = RTLIL::SigSpec(state); + } else { + sig_b.append(RTLIL::SigSpec(state)); + sig_s.append(RTLIL::SigSpec(next_state_onehot, i)); + } + } - RTLIL::Cell *mux_cell = new RTLIL::Cell; - mux_cell->name = NEW_ID; - mux_cell->type = "$safe_pmux"; - mux_cell->connections["\\A"] = sig_a; - mux_cell->connections["\\B"] = sig_b; - mux_cell->connections["\\S"] = sig_s; - mux_cell->connections["\\Y"] = RTLIL::SigSpec(next_state_wire); - mux_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_a.width); - mux_cell->parameters["\\S_WIDTH"] = RTLIL::Const(sig_s.width); - module->add(mux_cell); + RTLIL::Cell *mux_cell = module->addCell(NEW_ID, "$pmux"); + mux_cell->setPort("\\A", sig_a); + mux_cell->setPort("\\B", sig_b); + mux_cell->setPort("\\S", sig_s); + mux_cell->setPort("\\Y", RTLIL::SigSpec(next_state_wire)); + mux_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_a.size()); + mux_cell->parameters["\\S_WIDTH"] = RTLIL::Const(sig_s.size()); + } } // Generate ctrl_out signal - RTLIL::Wire *ctrl_out_wire = new RTLIL::Wire; - ctrl_out_wire->name = NEW_ID; - ctrl_out_wire->width = fsm_data.num_outputs; - module->add(ctrl_out_wire); - for (int i = 0; i < fsm_data.num_outputs; i++) { std::map<RTLIL::Const, std::set<int>> pattern_cache; @@ -340,8 +313,7 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) // Remove FSM cell - module->cells.erase(fsm_cell->name); - delete fsm_cell; + module->remove(fsm_cell); } struct FsmMapPass : public Pass { @@ -360,11 +332,11 @@ struct FsmMapPass : public Pass { log_header("Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; std::vector<RTLIL::Cell*> fsm_cells; - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) fsm_cells.push_back(cell_it.second); for (auto cell : fsm_cells) diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 242a505e9..a0e1885ec 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -30,22 +30,62 @@ struct FsmOpt FsmData fsm_data; RTLIL::Cell *cell; RTLIL::Module *module; + + void opt_unreachable_states() + { + while (1) + { + std::set<int> unreachable_states; + std::vector<FsmData::transition_t> new_transition_table; + std::vector<RTLIL::Const> new_state_table; + std::map<int, int> old_to_new_state; + + for (int i = 0; i < SIZE(fsm_data.state_table); i++) + if (i != fsm_data.reset_state) + unreachable_states.insert(i); + + for (auto &trans : fsm_data.transition_table) + unreachable_states.erase(trans.state_out); + + if (unreachable_states.empty()) + break; + + for (int i = 0; i < SIZE(fsm_data.state_table); i++) { + if (unreachable_states.count(i)) { + log(" Removing unreachable state %s.\n", log_signal(fsm_data.state_table[i])); + continue; + } + old_to_new_state[i] = SIZE(new_state_table); + new_state_table.push_back(fsm_data.state_table[i]); + } + + for (auto trans : fsm_data.transition_table) { + if (unreachable_states.count(trans.state_in)) + continue; + trans.state_in = old_to_new_state.at(trans.state_in); + trans.state_out = old_to_new_state.at(trans.state_out); + new_transition_table.push_back(trans); + } + + new_transition_table.swap(fsm_data.transition_table); + new_state_table.swap(fsm_data.state_table); + fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state); + } + } bool signal_is_unused(RTLIL::SigSpec sig) { - assert(sig.width == 1); - sig.optimize(); - - RTLIL::Wire *wire = sig.chunks[0].wire; - int bit = sig.chunks[0].offset; + RTLIL::SigBit bit = sig.to_single_sigbit(); - if (!wire || wire->attributes.count("\\unused_bits") == 0) + if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0) return false; - char *str = strdup(wire->attributes["\\unused_bits"].decode_string().c_str()); + char *str = strdup(bit.wire->attributes["\\unused_bits"].decode_string().c_str()); for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) { - if (tok[0] && bit == atoi(tok)) + if (tok[0] && bit.offset == atoi(tok)) { + free(str); return true; + } } free(str); @@ -54,12 +94,12 @@ struct FsmOpt void opt_const_and_unused_inputs() { - RTLIL::SigSpec ctrl_in = cell->connections["\\CTRL_IN"]; - std::vector<bool> ctrl_in_used(ctrl_in.width); + RTLIL::SigSpec ctrl_in = cell->getPort("\\CTRL_IN"); + std::vector<bool> ctrl_in_used(ctrl_in.size()); std::vector<FsmData::transition_t> new_transition_table; for (auto &tr : fsm_data.transition_table) { - for (int i = 0; i < ctrl_in.width; i++) { + for (int i = 0; i < ctrl_in.size(); i++) { RTLIL::SigSpec ctrl_bit = ctrl_in.extract(i, 1); if (ctrl_bit.is_fully_const()) { if (tr.ctrl_in.bits[i] <= RTLIL::State::S1 && RTLIL::SigSpec(tr.ctrl_in.bits[i]) != ctrl_bit) @@ -75,13 +115,15 @@ struct FsmOpt for (int i = int(ctrl_in_used.size())-1; i >= 0; i--) { if (!ctrl_in_used[i]) { - log(" Removing unused input signal %s.\n", log_signal(cell->connections["\\CTRL_IN"].extract(i, 1))); + log(" Removing unused input signal %s.\n", log_signal(cell->getPort("\\CTRL_IN").extract(i, 1))); for (auto &tr : new_transition_table) { RTLIL::SigSpec tmp(tr.ctrl_in); tmp.remove(i, 1); tr.ctrl_in = tmp.as_const(); } - cell->connections["\\CTRL_IN"].remove(i, 1); + RTLIL::SigSpec new_ctrl_in = cell->getPort("\\CTRL_IN"); + new_ctrl_in.remove(i, 1); + cell->setPort("\\CTRL_IN", new_ctrl_in); fsm_data.num_inputs--; } } @@ -93,10 +135,12 @@ struct FsmOpt void opt_unused_outputs() { for (int i = 0; i < fsm_data.num_outputs; i++) { - RTLIL::SigSpec sig = cell->connections["\\CTRL_OUT"].extract(i, 1); + RTLIL::SigSpec sig = cell->getPort("\\CTRL_OUT").extract(i, 1); if (signal_is_unused(sig)) { log(" Removing unused output signal %s.\n", log_signal(sig)); - cell->connections["\\CTRL_OUT"].remove(i, 1); + RTLIL::SigSpec new_ctrl_out = cell->getPort("\\CTRL_OUT"); + new_ctrl_out.remove(i, 1); + cell->setPort("\\CTRL_OUT", new_ctrl_out); for (auto &tr : fsm_data.transition_table) { RTLIL::SigSpec tmp(tr.ctrl_out); tmp.remove(i, 1); @@ -110,10 +154,10 @@ struct FsmOpt void opt_alias_inputs() { - RTLIL::SigSpec &ctrl_in = cell->connections["\\CTRL_IN"]; + RTLIL::SigSpec &ctrl_in = cell->connections_["\\CTRL_IN"]; - for (int i = 0; i < ctrl_in.width; i++) - for (int j = i+1; j < ctrl_in.width; j++) + for (int i = 0; i < ctrl_in.size(); i++) + for (int j = i+1; j < ctrl_in.size(); j++) if (ctrl_in.extract(i, 1) == ctrl_in.extract(j, 1)) { log(" Optimize handling of signal %s that is connected to inputs %d and %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j); @@ -147,11 +191,11 @@ struct FsmOpt void opt_feedback_inputs() { - RTLIL::SigSpec &ctrl_in = cell->connections["\\CTRL_IN"]; - RTLIL::SigSpec &ctrl_out = cell->connections["\\CTRL_OUT"]; + RTLIL::SigSpec &ctrl_in = cell->connections_["\\CTRL_IN"]; + RTLIL::SigSpec &ctrl_out = cell->connections_["\\CTRL_OUT"]; - for (int j = 0; j < ctrl_out.width; j++) - for (int i = 0; i < ctrl_in.width; i++) + for (int j = 0; j < ctrl_out.size(); j++) + for (int i = 0; i < ctrl_in.size(); i++) if (ctrl_in.extract(i, 1) == ctrl_out.extract(j, 1)) { log(" Optimize handling of signal %s that is connected to input %d and output %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j); @@ -251,6 +295,8 @@ struct FsmOpt this->cell = cell; this->module = module; + opt_unreachable_states(); + opt_unused_outputs(); opt_alias_inputs(); @@ -286,9 +332,9 @@ struct FsmOptPass : public Pass { log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (design->selected(mod_it.second)) - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) if (cell_it.second->type == "$fsm" and design->selected(mod_it.second, cell_it.second)) FsmData::optimize_fsm(cell_it.second, mod_it.second); } diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index 5a4e091cf..873ee7a16 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -23,8 +23,9 @@ #include "kernel/consteval.h" #include "kernel/celltypes.h" #include "fsmdata.h" -#include "math.h" +#include <math.h> #include <string.h> +#include <errno.h> static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &fsm_data, const char *prefix, FILE *f) { @@ -52,10 +53,10 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs std::string encoding = cell->attributes.count("\\fsm_encoding") ? cell->attributes.at("\\fsm_encoding").decode_string() : "auto"; log("Recoding FSM `%s' from module `%s' using `%s' encoding:\n", cell->name.c_str(), module->name.c_str(), encoding.c_str()); - if (encoding != "none" && encoding != "one-hot" && encoding != "binary") { - if (encoding != "auto") - log(" unkown encoding `%s': using auto (%s) instead.\n", encoding.c_str(), default_encoding.c_str()); - encoding = default_encoding; + + if (encoding != "none" && encoding != "one-hot" && encoding != "binary" && encoding != "auto") { + log(" unknown encoding `%s': using auto instead.\n", encoding.c_str()); + encoding = "auto"; } if (encoding == "none") { @@ -69,11 +70,24 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs if (fm_set_fsm_file != NULL) fm_set_fsm_print(cell, module, fsm_data, "r", fm_set_fsm_file); + if (encoding == "auto") { + if (!default_encoding.empty()) + encoding = default_encoding; + else + encoding = SIZE(fsm_data.state_table) < 32 ? "one-hot" : "binary"; + log(" mapping auto encoding to `%s` for this FSM.\n", encoding.c_str()); + } + if (encoding == "one-hot") { fsm_data.state_bits = fsm_data.state_table.size(); } else - if (encoding == "auto" || encoding == "binary") { - fsm_data.state_bits = ceil(log2(fsm_data.state_table.size())); + if (encoding == "binary") { + int new_num_state_bits = ceil(log2(fsm_data.state_table.size())); + if (fsm_data.state_bits == new_num_state_bits) { + log(" existing encoding is already a packed binary encoding.\n"); + return; + } + fsm_data.state_bits = new_num_state_bits; } else log_error("FSM encoding `%s' is not supported!\n", encoding.c_str()); @@ -87,7 +101,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs new_code = RTLIL::Const(RTLIL::State::Sa, fsm_data.state_bits); new_code.bits[state_idx] = RTLIL::State::S1; } else - if (encoding == "auto" || encoding == "binary") { + if (encoding == "binary") { new_code = RTLIL::Const(state_idx, fsm_data.state_bits); } else log_abort(); @@ -123,7 +137,7 @@ struct FsmRecodePass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { FILE *fm_set_fsm_file = NULL; - std::string default_encoding = "one-hot"; + std::string default_encoding; log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); size_t argidx; @@ -143,9 +157,9 @@ struct FsmRecodePass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) - for (auto &cell_it : mod_it.second->cells) + for (auto &cell_it : mod_it.second->cells_) if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) fsm_recode(cell_it.second, mod_it.second, fm_set_fsm_file, default_encoding); diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 225f34a9d..7a44dd452 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -141,29 +141,27 @@ struct FsmData log("\n"); log(" Input signals:\n"); - RTLIL::SigSpec sig_in = cell->connections["\\CTRL_IN"]; - sig_in.expand(); - for (size_t i = 0; i < sig_in.chunks.size(); i++) - log(" %3zd: %s\n", i, log_signal(sig_in.chunks[i])); + RTLIL::SigSpec sig_in = cell->getPort("\\CTRL_IN"); + for (int i = 0; i < SIZE(sig_in); i++) + log(" %3d: %s\n", i, log_signal(sig_in[i])); log("\n"); log(" Output signals:\n"); - RTLIL::SigSpec sig_out = cell->connections["\\CTRL_OUT"]; - sig_out.expand(); - for (size_t i = 0; i < sig_out.chunks.size(); i++) - log(" %3zd: %s\n", i, log_signal(sig_out.chunks[i])); + RTLIL::SigSpec sig_out = cell->getPort("\\CTRL_OUT"); + for (int i = 0; i < SIZE(sig_out); i++) + log(" %3d: %s\n", i, log_signal(sig_out[i])); log("\n"); log(" State encoding:\n"); - for (size_t i = 0; i < state_table.size(); i++) - log(" %3zd: %10s%s\n", i, log_signal(state_table[i], false), + for (int i = 0; i < SIZE(state_table); i++) + log(" %3d: %10s%s\n", i, log_signal(state_table[i], false), int(i) == reset_state ? " <RESET STATE>" : ""); log("\n"); log(" Transition Table (state_in, ctrl_in, state_out, ctrl_out):\n"); - for (size_t i = 0; i < transition_table.size(); i++) { + for (int i = 0; i < SIZE(transition_table); i++) { transition_t &tr = transition_table[i]; - log(" %5zd: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out)); + log(" %5d: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out)); } log("\n"); diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 50d0e6e47..14bf8d1bd 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -28,20 +28,22 @@ namespace { struct generate_port_decl_t { bool input, output; - std::string portname; + RTLIL::IdString portname; int index; }; } static void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, const std::vector<generate_port_decl_t> &portdecls) { - std::set<std::string> found_celltypes; + std::set<RTLIL::IdString> found_celltypes; - for (auto i1 : design->modules) - for (auto i2 : i1.second->cells) + for (auto i1 : design->modules_) + for (auto i2 : i1.second->cells_) { RTLIL::Cell *cell = i2.second; - if (cell->type[0] == '$' || design->modules.count(cell->type) > 0) + if (design->has(cell->type)) + continue; + if (cell->type.substr(0, 1) == "$" && cell->type.substr(0, 3) != "$__") continue; for (auto &pattern : celltypes) if (!fnmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str(), FNM_NOESCAPE)) @@ -50,18 +52,18 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell for (auto &celltype : found_celltypes) { - std::set<std::string> portnames; - std::set<std::string> parameters; - std::map<std::string, int> portwidths; + std::set<RTLIL::IdString> portnames; + std::set<RTLIL::IdString> parameters; + std::map<RTLIL::IdString, int> portwidths; log("Generate module for cell type %s:\n", celltype.c_str()); - for (auto i1 : design->modules) - for (auto i2 : i1.second->cells) + for (auto i1 : design->modules_) + for (auto i2 : i1.second->cells_) if (i2.second->type == celltype) { - for (auto &conn : i2.second->connections) { + for (auto &conn : i2.second->connections()) { if (conn.first[0] != '$') portnames.insert(conn.first); - portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.width); + portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.size()); } for (auto ¶ : i2.second->parameters) parameters.insert(para.first); @@ -92,13 +94,13 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell } while (portnames.size() > 0) { - std::string portname = *portnames.begin(); + RTLIL::IdString portname = *portnames.begin(); for (auto &decl : portdecls) if (decl.index == 0 && !fnmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str(), FNM_NOESCAPE)) { generate_port_decl_t d = decl; d.portname = portname; d.index = *indices.begin(); - assert(!indices.empty()); + log_assert(!indices.empty()); indices.erase(d.index); ports[d.index-1] = d; portwidths[d.portname] = std::max(portwidths[d.portname], 1); @@ -110,23 +112,22 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell portnames.erase(portname); } - assert(indices.empty()); + log_assert(indices.empty()); RTLIL::Module *mod = new RTLIL::Module; mod->name = celltype; mod->attributes["\\blackbox"] = RTLIL::Const(1); - design->modules[mod->name] = mod; + design->add(mod); for (auto &decl : ports) { - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = decl.portname; - wire->width = portwidths.at(decl.portname); + RTLIL::Wire *wire = mod->addWire(decl.portname, portwidths.at(decl.portname)); wire->port_id = decl.index; wire->port_input = decl.input; wire->port_output = decl.output; - mod->add(wire); } + mod->fixup_ports(); + for (auto ¶ : parameters) log(" ignoring parameter %s.\n", RTLIL::id2cstr(para)); @@ -137,14 +138,33 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector<std::string> &libdirs) { bool did_something = false; + std::map<RTLIL::Cell*, std::pair<int, int>> array_cells; std::string filename; - for (auto &cell_it : module->cells) + for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if (design->modules.count(cell->type) == 0) + if (cell->type.substr(0, 7) == "$array:") { + int pos_idx = cell->type.str().find_first_of(':'); + int pos_num = cell->type.str().find_first_of(':', pos_idx + 1); + int pos_type = cell->type.str().find_first_of(':', pos_num + 1); + int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str()); + int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str()); + array_cells[cell] = std::pair<int, int>(idx, num); + cell->type = cell->type.str().substr(pos_type + 1); + } + + if (design->modules_.count(cell->type) == 0) { + if (design->modules_.count("$abstract" + cell->type.str())) + { + cell->type = design->modules_.at("$abstract" + cell->type.str())->derive(design, cell->parameters); + cell->parameters.clear(); + did_something = true; + continue; + } + if (cell->type[0] == '$') continue; @@ -173,7 +193,7 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla continue; loaded_module: - if (design->modules.count(cell->type) == 0) + if (design->modules_.count(cell->type) == 0) log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str()); did_something = true; } @@ -181,15 +201,47 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla if (cell->parameters.size() == 0) continue; - if (design->modules.at(cell->type)->get_bool_attribute("\\blackbox")) + if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) continue; - RTLIL::Module *mod = design->modules[cell->type]; + RTLIL::Module *mod = design->modules_[cell->type]; cell->type = mod->derive(design, cell->parameters); cell->parameters.clear(); did_something = true; } + for (auto &it : array_cells) + { + RTLIL::Cell *cell = it.first; + int idx = it.second.first, num = it.second.second; + + if (design->modules_.count(cell->type) == 0) + log_error("Array cell `%s.%s' of unknown type `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + + RTLIL::Module *mod = design->modules_[cell->type]; + + for (auto &conn : cell->connections_) { + int conn_size = conn.second.size(); + RTLIL::IdString portname = conn.first; + if (portname.substr(0, 1) == "$") { + int port_id = atoi(portname.substr(1).c_str()); + for (auto &wire_it : mod->wires_) + if (wire_it.second->port_id == port_id) { + portname = wire_it.first; + break; + } + } + if (mod->wires_.count(portname) == 0) + log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); + int port_size = mod->wires_.at(portname)->width; + if (conn_size == port_size) + continue; + if (conn_size != port_size*num) + log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); + conn.second = conn.second.extract(port_size*idx, port_size); + } + } + return did_something; } @@ -204,27 +256,29 @@ static void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &us log("Used module: %*s%s\n", indent, "", mod->name.c_str()); used.insert(mod); - for (auto &it : mod->cells) { - if (design->modules.count(it.second->type) > 0) - hierarchy_worker(design, used, design->modules[it.second->type], indent+4); + for (auto &it : mod->cells_) { + if (design->modules_.count(it.second->type) > 0) + hierarchy_worker(design, used, design->modules_[it.second->type], indent+4); } } -static void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) +static void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool first_pass) { std::set<RTLIL::Module*> used; hierarchy_worker(design, used, top, 0); std::vector<RTLIL::Module*> del_modules; - for (auto &it : design->modules) + for (auto &it : design->modules_) if (used.count(it.second) == 0) del_modules.push_back(it.second); for (auto mod : del_modules) { + if (first_pass && mod->name.substr(0, 9) == "$abstract") + continue; if (!purge_lib && mod->get_bool_attribute("\\blackbox")) continue; log("Removing unused module `%s'.\n", mod->name.c_str()); - design->modules.erase(mod->name); + design->modules_.erase(mod->name); delete mod; } @@ -240,7 +294,7 @@ struct HierarchyPass : public Pass { log(" hierarchy [-check] [-top <module>]\n"); log(" hierarchy -generate <cell-types> <port-decls>\n"); log("\n"); - log("In parametric designs, a module might exists in serveral variations with\n"); + log("In parametric designs, a module might exists in several variations with\n"); log("different parameter values. This pass looks at all modules in the current\n"); log("design an re-runs the language frontends for the parametric modules as\n"); log("needed.\n"); @@ -255,7 +309,7 @@ struct HierarchyPass : public Pass { log("\n"); log(" -libdir <directory>\n"); log(" search for files named <module_name>.v in the specified directory\n"); - log(" for unkown modules and automatically run read_verilog for each\n"); + log(" for unknown modules and automatically run read_verilog for each\n"); log(" unknown module.\n"); log("\n"); log(" -keep_positionals\n"); @@ -362,10 +416,12 @@ struct HierarchyPass : public Pass { if (args[argidx] == "-top") { if (++argidx >= args.size()) log_cmd_error("Option -top requires an additional argument!\n"); - if (args[argidx][0] != '$' && args[argidx][0] != '\\') - top_mod = design->modules.count("\\" + args[argidx]) > 0 ? design->modules["\\" + args[argidx]] : NULL; - else - top_mod = design->modules.count(args[argidx]) > 0 ? design->modules[args[argidx]] : NULL; + top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL; + if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) { + std::map<RTLIL::IdString, RTLIL::Const> empty_parameters; + design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters); + top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL; + } if (top_mod == NULL) log_cmd_error("Module `%s' not found!\n", args[argidx].c_str()); continue; @@ -382,25 +438,25 @@ struct HierarchyPass : public Pass { log_push(); if (top_mod == NULL) - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (mod_it.second->get_bool_attribute("\\top")) top_mod = mod_it.second; if (top_mod != NULL) - hierarchy(design, top_mod, purge_lib); + hierarchy(design, top_mod, purge_lib, true); bool did_something = true; bool did_something_once = false; while (did_something) { did_something = false; - std::vector<std::string> modnames; - modnames.reserve(design->modules.size()); - for (auto &mod_it : design->modules) + std::vector<RTLIL::IdString> modnames; + modnames.reserve(design->modules_.size()); + for (auto &mod_it : design->modules_) modnames.push_back(mod_it.first); for (auto &modname : modnames) { - if (design->modules.count(modname) == 0) + if (design->modules_.count(modname) == 0) continue; - if (expand_module(design, design->modules[modname], flag_check, libdirs)) + if (expand_module(design, design->modules_[modname], flag_check, libdirs)) did_something = true; } if (did_something) @@ -409,11 +465,11 @@ struct HierarchyPass : public Pass { if (top_mod != NULL && did_something_once) { log_header("Re-running hierarchy analysis..\n"); - hierarchy(design, top_mod, purge_lib); + hierarchy(design, top_mod, purge_lib, false); } if (top_mod != NULL) { - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (mod_it.second == top_mod) mod_it.second->attributes["\\top"] = RTLIL::Const(1); else @@ -426,21 +482,21 @@ struct HierarchyPass : public Pass { std::map<std::pair<RTLIL::Module*,int>, RTLIL::IdString> pos_map; std::vector<std::pair<RTLIL::Module*,RTLIL::Cell*>> pos_work; - for (auto &mod_it : design->modules) - for (auto &cell_it : mod_it.second->cells) { + for (auto &mod_it : design->modules_) + for (auto &cell_it : mod_it.second->cells_) { RTLIL::Cell *cell = cell_it.second; - if (design->modules.count(cell->type) == 0) + if (design->modules_.count(cell->type) == 0) continue; - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { - pos_mods.insert(design->modules.at(cell->type)); + pos_mods.insert(design->modules_.at(cell->type)); pos_work.push_back(std::pair<RTLIL::Module*,RTLIL::Cell*>(mod_it.second, cell)); break; } } for (auto module : pos_mods) - for (auto &wire_it : module->wires) { + for (auto &wire_it : module->wires_) { RTLIL::Wire *wire = wire_it.second; if (wire->port_id > 0) pos_map[std::pair<RTLIL::Module*,int>(module, wire->port_id)] = wire->name; @@ -452,10 +508,10 @@ struct HierarchyPass : public Pass { log("Mapping positional arguments of cell %s.%s (%s).\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); std::map<RTLIL::IdString, RTLIL::SigSpec> new_connections; - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { int id = atoi(conn.first.c_str()+1); - std::pair<RTLIL::Module*,int> key(design->modules.at(cell->type), id); + std::pair<RTLIL::Module*,int> key(design->modules_.at(cell->type), id); if (pos_map.count(key) == 0) { log(" Failed to map positional argument %d of cell %s.%s (%s).\n", id, RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); @@ -464,7 +520,7 @@ struct HierarchyPass : public Pass { new_connections[pos_map.at(key)] = conn.second; } else new_connections[conn.first] = conn.second; - cell->connections = new_connections; + cell->connections_ = new_connections; } } diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 7d0811254..1b03ab555 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -65,9 +65,9 @@ struct SubmodWorker flag_found_something = true; } - void flag_signal(RTLIL::SigSpec &sig, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) + void flag_signal(const RTLIL::SigSpec &sig, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) { - for (auto &c : sig.chunks) + for (auto &c : sig.chunks()) if (c.wire != NULL) flag_wire(c.wire, create, set_int_driven, set_int_used, set_ext_driven, set_ext_used); } @@ -79,24 +79,24 @@ struct SubmodWorker wire_flags.clear(); for (RTLIL::Cell *cell : submod.cells) { if (ct.cell_known(cell->type)) { - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) flag_signal(conn.second, true, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first), false, false); } else { log("WARNING: Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str()); - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) flag_signal(conn.second, true, true, true, false, false); } } - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (submod.cells.count(cell) > 0) continue; if (ct.cell_known(cell->type)) { - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) flag_signal(conn.second, false, false, false, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first)); } else { flag_found_something = false; - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) flag_signal(conn.second, false, false, false, true, true); if (flag_found_something) log("WARNING: Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str()); @@ -105,10 +105,10 @@ struct SubmodWorker RTLIL::Module *new_mod = new RTLIL::Module; new_mod->name = submod.full_name; - design->modules[new_mod->name] = new_mod; - int port_counter = 1, auto_name_counter = 1; + design->add(new_mod); + int auto_name_counter = 1; - std::set<std::string> all_wire_names; + std::set<RTLIL::IdString> all_wire_names; for (auto &it : wire_flags) { all_wire_names.insert(it.first->name); } @@ -123,31 +123,34 @@ struct SubmodWorker if (wire->port_output) flags.is_ext_used = true; - RTLIL::Wire *new_wire = new RTLIL::Wire; - new_wire->name = wire->name; - new_wire->width = wire->width; - new_wire->start_offset = wire->start_offset; - new_wire->attributes = wire->attributes; + bool new_wire_port_input = false; + bool new_wire_port_output = false; if (flags.is_int_driven && flags.is_ext_used) - new_wire->port_output = true; + new_wire_port_output = true; if (flags.is_ext_driven && flags.is_int_used) - new_wire->port_input = true; + new_wire_port_input = true; if (flags.is_int_driven && flags.is_ext_driven) - new_wire->port_input = true, new_wire->port_output = true; - - if (new_wire->port_input || new_wire->port_output) { - new_wire->port_id = port_counter++; - while (new_wire->name[0] == '$') { - std::string new_wire_name = stringf("\\n%d", auto_name_counter++); - if (all_wire_names.count(new_wire_name) == 0) { - all_wire_names.insert(new_wire_name); - new_wire->name = new_wire_name; + new_wire_port_input = true, new_wire_port_output = true; + + std::string new_wire_name = wire->name.str(); + if (new_wire_port_input || new_wire_port_output) { + while (new_wire_name[0] == '$') { + std::string next_wire_name = stringf("\\n%d", auto_name_counter++); + if (all_wire_names.count(next_wire_name) == 0) { + all_wire_names.insert(next_wire_name); + new_wire_name = next_wire_name; } } } + RTLIL::Wire *new_wire = new_mod->addWire(new_wire_name, wire->width); + new_wire->port_input = new_wire_port_input; + new_wire->port_output = new_wire_port_output; + new_wire->start_offset = wire->start_offset; + new_wire->attributes = wire->attributes; + if (new_wire->port_input && new_wire->port_output) log(" signal %s: inout %s\n", wire->name.c_str(), new_wire->name.c_str()); else if (new_wire->port_input) @@ -157,36 +160,32 @@ struct SubmodWorker else log(" signal %s: internal\n", wire->name.c_str()); - new_mod->wires[new_wire->name] = new_wire; flags.new_wire = new_wire; } + new_mod->fixup_ports(); + for (RTLIL::Cell *cell : submod.cells) { - RTLIL::Cell *new_cell = new RTLIL::Cell(*cell); - for (auto &conn : new_cell->connections) - for (auto &c : conn.second.chunks) - if (c.wire != NULL) { - assert(wire_flags.count(c.wire) > 0); - c.wire = wire_flags[c.wire].new_wire; + RTLIL::Cell *new_cell = new_mod->addCell(cell->name, cell); + for (auto &conn : new_cell->connections_) + for (auto &bit : conn.second) + if (bit.wire != NULL) { + log_assert(wire_flags.count(bit.wire) > 0); + bit.wire = wire_flags[bit.wire].new_wire; } log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str()); - new_mod->cells[new_cell->name] = new_cell; - module->cells.erase(cell->name); - delete cell; + module->remove(cell); } submod.cells.clear(); - RTLIL::Cell *new_cell = new RTLIL::Cell; - new_cell->name = submod.full_name; - new_cell->type = submod.full_name; + RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name); for (auto &it : wire_flags) { RTLIL::Wire *old_wire = it.first; RTLIL::Wire *new_wire = it.second.new_wire; if (new_wire->port_id > 0) - new_cell->connections[new_wire->name] = RTLIL::SigSpec(old_wire); + new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire)); } - module->cells[new_cell->name] = new_cell; } SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, std::string opt_name = std::string()) : design(design), module(module), opt_name(opt_name) @@ -212,10 +211,10 @@ struct SubmodWorker if (opt_name.empty()) { - for (auto &it : module->wires) + for (auto &it : module->wires_) it.second->attributes.erase("\\submod"); - for (auto &it : module->cells) + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (cell->attributes.count("\\submod") == 0 || cell->attributes["\\submod"].bits.size() == 0) { @@ -228,8 +227,8 @@ struct SubmodWorker if (submodules.count(submod_str) == 0) { submodules[submod_str].name = submod_str; - submodules[submod_str].full_name = module->name + "_" + submod_str; - while (design->modules.count(submodules[submod_str].full_name) != 0 || + submodules[submod_str].full_name = module->name.str() + "_" + submod_str; + while (design->modules_.count(submodules[submod_str].full_name) != 0 || module->count_id(submodules[submod_str].full_name) != 0) submodules[submod_str].full_name += "_"; } @@ -239,7 +238,7 @@ struct SubmodWorker } else { - for (auto &it : module->cells) + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (!design->selected(module, cell)) @@ -306,18 +305,18 @@ struct SubmodPass : public Pass { Pass::call(design, "opt_clean"); log_header("Continuing SUBMOD pass.\n"); - std::set<std::string> handled_modules; + std::set<RTLIL::IdString> handled_modules; bool did_something = true; while (did_something) { did_something = false; - std::vector<std::string> queued_modules; - for (auto &mod_it : design->modules) + std::vector<RTLIL::IdString> queued_modules; + for (auto &mod_it : design->modules_) if (handled_modules.count(mod_it.first) == 0 && design->selected_whole_module(mod_it.first)) queued_modules.push_back(mod_it.first); for (auto &modname : queued_modules) - if (design->modules.count(modname) != 0) { - SubmodWorker worker(design, design->modules[modname]); + if (design->modules_.count(modname) != 0) { + SubmodWorker worker(design, design->modules_[modname]); handled_modules.insert(modname); did_something = true; } @@ -328,7 +327,7 @@ struct SubmodPass : public Pass { else { RTLIL::Module *module = NULL; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected_module(mod_it.first)) continue; if (module != NULL) @@ -338,7 +337,7 @@ struct SubmodPass : public Pass { if (module == NULL) log("Nothing selected -> do nothing.\n"); else { - Pass::call_newsel(design, stringf("opt_clean %s", module->name.c_str())); + Pass::call_on_module(design, module, "opt_clean"); log_header("Continuing SUBMOD pass.\n"); SubmodWorker worker(design, module, opt_name); } diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc index 21f17db5b..026c5ff85 100644 --- a/passes/memory/Makefile.inc +++ b/passes/memory/Makefile.inc @@ -1,6 +1,7 @@ OBJS += passes/memory/memory.o OBJS += passes/memory/memory_dff.o +OBJS += passes/memory/memory_share.o OBJS += passes/memory/memory_collect.o OBJS += passes/memory/memory_unpack.o OBJS += passes/memory/memory_map.o diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 680657a79..fc3095535 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -33,6 +33,9 @@ struct MemoryPass : public Pass { log("This pass calls all the other memory_* passes in a useful order:\n"); log("\n"); log(" memory_dff\n"); + log(" opt_clean\n"); + log(" memory_share\n"); + log(" opt_clean\n"); log(" memory_collect\n"); log(" memory_map (skipped if called with -nomap)\n"); log("\n"); @@ -58,6 +61,9 @@ struct MemoryPass : public Pass { extra_args(args, argidx, design); Pass::call(design, "memory_dff"); + Pass::call(design, "opt_clean"); + Pass::call(design, "memory_share"); + Pass::call(design, "opt_clean"); Pass::call(design, "memory_collect"); if (!flag_nomap) diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index 6fe5e162c..9c670f00f 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -22,7 +22,6 @@ #include <sstream> #include <algorithm> #include <stdlib.h> -#include <assert.h> static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) { @@ -58,12 +57,12 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) RTLIL::SigSpec sig_rd_addr; RTLIL::SigSpec sig_rd_data; - std::vector<std::string> del_cell_ids; + std::vector<RTLIL::Cell*> del_cells; std::vector<RTLIL::Cell*> memcells; - for (auto &cell_it : module->cells) { + for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if ((cell->type == "$memwr" || cell->type == "$memrd") && cell->parameters["\\MEMID"].decode_string() == memory->name) + if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string()) memcells.push_back(cell); } @@ -71,24 +70,24 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) for (auto cell : memcells) { - if (cell->type == "$memwr" && cell->parameters["\\MEMID"].decode_string() == memory->name) + if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string()) { wr_ports++; - del_cell_ids.push_back(cell->name); + del_cells.push_back(cell); - RTLIL::SigSpec clk = cell->connections["\\CLK"]; + RTLIL::SigSpec clk = cell->getPort("\\CLK"); RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); - RTLIL::SigSpec addr = cell->connections["\\ADDR"]; - RTLIL::SigSpec data = cell->connections["\\DATA"]; - RTLIL::SigSpec en = cell->connections["\\EN"]; + RTLIL::SigSpec addr = cell->getPort("\\ADDR"); + RTLIL::SigSpec data = cell->getPort("\\DATA"); + RTLIL::SigSpec en = cell->getPort("\\EN"); clk.extend(1, false); clk_enable.extend(1, false); clk_polarity.extend(1, false); addr.extend(addr_bits, false); data.extend(memory->width, false); - en.extend(1, false); + en.extend(memory->width, false); sig_wr_clk.append(clk); sig_wr_clk_enable.append(clk_enable); @@ -98,17 +97,17 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) sig_wr_en.append(en); } - if (cell->type == "$memrd" && cell->parameters["\\MEMID"].decode_string() == memory->name) + if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string()) { rd_ports++; - del_cell_ids.push_back(cell->name); + del_cells.push_back(cell); - RTLIL::SigSpec clk = cell->connections["\\CLK"]; + RTLIL::SigSpec clk = cell->getPort("\\CLK"); RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); RTLIL::SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]); - RTLIL::SigSpec addr = cell->connections["\\ADDR"]; - RTLIL::SigSpec data = cell->connections["\\DATA"]; + RTLIL::SigSpec addr = cell->getPort("\\ADDR"); + RTLIL::SigSpec data = cell->getPort("\\DATA"); clk.extend(1, false); clk_enable.extend(1, false); @@ -127,61 +126,48 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) } std::stringstream sstr; - sstr << "$mem$" << memory->name << "$" << (RTLIL::autoidx++); + sstr << "$mem$" << memory->name.str() << "$" << (autoidx++); - RTLIL::Cell *mem = new RTLIL::Cell; - mem->name = sstr.str(); - mem->type = "$mem"; - - mem->parameters["\\MEMID"] = RTLIL::Const(memory->name); + RTLIL::Cell *mem = module->addCell(sstr.str(), "$mem"); + mem->parameters["\\MEMID"] = RTLIL::Const(memory->name.str()); mem->parameters["\\WIDTH"] = RTLIL::Const(memory->width); mem->parameters["\\OFFSET"] = RTLIL::Const(memory->start_offset); mem->parameters["\\SIZE"] = RTLIL::Const(memory->size); mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits); - sig_wr_clk_enable.optimize(); - sig_wr_clk_polarity.optimize(); - - assert(sig_wr_clk.width == wr_ports); - assert(sig_wr_clk_enable.width == wr_ports && sig_wr_clk_enable.is_fully_const()); - assert(sig_wr_clk_polarity.width == wr_ports && sig_wr_clk_polarity.is_fully_const()); - assert(sig_wr_addr.width == wr_ports * addr_bits); - assert(sig_wr_data.width == wr_ports * memory->width); - assert(sig_wr_en.width == wr_ports); + log_assert(sig_wr_clk.size() == wr_ports); + log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const()); + log_assert(sig_wr_clk_polarity.size() == wr_ports && sig_wr_clk_polarity.is_fully_const()); + log_assert(sig_wr_addr.size() == wr_ports * addr_bits); + log_assert(sig_wr_data.size() == wr_ports * memory->width); + log_assert(sig_wr_en.size() == wr_ports * memory->width); mem->parameters["\\WR_PORTS"] = RTLIL::Const(wr_ports); - mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.chunks[0].data : RTLIL::Const(0, 0); - mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.chunks[0].data : RTLIL::Const(0, 0); + mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : RTLIL::Const(0, 0); - mem->connections["\\WR_CLK"] = sig_wr_clk; - mem->connections["\\WR_ADDR"] = sig_wr_addr; - mem->connections["\\WR_DATA"] = sig_wr_data; - mem->connections["\\WR_EN"] = sig_wr_en; + mem->setPort("\\WR_CLK", sig_wr_clk); + mem->setPort("\\WR_ADDR", sig_wr_addr); + mem->setPort("\\WR_DATA", sig_wr_data); + mem->setPort("\\WR_EN", sig_wr_en); - sig_rd_clk_enable.optimize(); - sig_rd_clk_polarity.optimize(); - sig_rd_transparent.optimize(); - - assert(sig_rd_clk.width == rd_ports); - assert(sig_rd_clk_enable.width == rd_ports && sig_rd_clk_enable.is_fully_const()); - assert(sig_rd_clk_polarity.width == rd_ports && sig_rd_clk_polarity.is_fully_const()); - assert(sig_rd_addr.width == rd_ports * addr_bits); - assert(sig_rd_data.width == rd_ports * memory->width); + log_assert(sig_rd_clk.size() == rd_ports); + log_assert(sig_rd_clk_enable.size() == rd_ports && sig_rd_clk_enable.is_fully_const()); + log_assert(sig_rd_clk_polarity.size() == rd_ports && sig_rd_clk_polarity.is_fully_const()); + log_assert(sig_rd_addr.size() == rd_ports * addr_bits); + log_assert(sig_rd_data.size() == rd_ports * memory->width); mem->parameters["\\RD_PORTS"] = RTLIL::Const(rd_ports); - mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.chunks[0].data : RTLIL::Const(0, 0); - mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.chunks[0].data : RTLIL::Const(0, 0); - mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.chunks[0].data : RTLIL::Const(0, 0); + mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : RTLIL::Const(0, 0); - mem->connections["\\RD_CLK"] = sig_rd_clk; - mem->connections["\\RD_ADDR"] = sig_rd_addr; - mem->connections["\\RD_DATA"] = sig_rd_data; + mem->setPort("\\RD_CLK", sig_rd_clk); + mem->setPort("\\RD_ADDR", sig_rd_addr); + mem->setPort("\\RD_DATA", sig_rd_data); - for (auto &id : del_cell_ids) { - delete module->cells[id]; - module->cells.erase(id); - } - module->cells[mem->name] = mem; + for (auto c : del_cells) + module->remove(c); } static void handle_module(RTLIL::Design *design, RTLIL::Module *module) @@ -213,7 +199,7 @@ struct MemoryCollectPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) handle_module(design, mod_it.second); } diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 2502a8b61..302ab3abf 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -20,51 +20,41 @@ #include "kernel/register.h" #include "kernel/log.h" #include <stdlib.h> -#include <assert.h> #include <sstream> static void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig) { - for (auto &conn : module->connections) + for (auto &conn : module->connections()) sig.replace(conn.first, conn.second); } -static bool find_sig_before_dff(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) +static bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) { normalize_sig(module, sig); - sig.expand(); - for (size_t i = 0; i < sig.chunks.size(); i++) + for (auto &bit : sig) { - RTLIL::SigChunk &chunk = sig.chunks[i]; - - if (chunk.wire == NULL) + if (bit.wire == NULL) continue; - for (auto &cell_it : module->cells) + for (auto cell : dff_cells) { - RTLIL::Cell *cell = cell_it.second; - - if (cell->type != "$dff") - continue; - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - if (cell->connections["\\CLK"] != clk) + if (cell->getPort("\\CLK") != clk) continue; if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity) continue; } - RTLIL::SigSpec q_norm = cell->connections[after ? "\\D" : "\\Q"]; + RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q"); normalize_sig(module, q_norm); - RTLIL::SigSpec d = q_norm.extract(chunk, &cell->connections[after ? "\\Q" : "\\D"]); - if (d.width != 1) + RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D")); + if (d.size() != 1) continue; - assert(d.chunks.size() == 1); - chunk = d.chunks[0]; - clk = cell->connections["\\CLK"]; + bit = d; + clk = cell->getPort("\\CLK"); clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); goto replaced_this_bit; } @@ -73,44 +63,46 @@ static bool find_sig_before_dff(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLI replaced_this_bit:; } - sig.optimize(); return true; } -static void handle_wr_cell(RTLIL::Module *module, RTLIL::Cell *cell) +static void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) { log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); bool clk_polarity = 0; - RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"]; - if (!find_sig_before_dff(module, sig_addr, clk, clk_polarity)) { + RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); + if (!find_sig_before_dff(module, dff_cells, sig_addr, clk, clk_polarity)) { log("no (compatible) $dff for address input found.\n"); return; } - RTLIL::SigSpec sig_data = cell->connections["\\DATA"]; - if (!find_sig_before_dff(module, sig_data, clk, clk_polarity)) { + RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); + if (!find_sig_before_dff(module, dff_cells, sig_data, clk, clk_polarity)) { log("no (compatible) $dff for data input found.\n"); return; } - RTLIL::SigSpec sig_en = cell->connections["\\EN"]; - if (!find_sig_before_dff(module, sig_en, clk, clk_polarity)) { + RTLIL::SigSpec sig_en = cell->getPort("\\EN"); + if (!find_sig_before_dff(module, dff_cells, sig_en, clk, clk_polarity)) { log("no (compatible) $dff for enable input found.\n"); return; } if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - cell->connections["\\CLK"] = clk; - cell->connections["\\ADDR"] = sig_addr; - cell->connections["\\DATA"] = sig_data; - cell->connections["\\EN"] = sig_en; + cell->setPort("\\CLK", clk); + cell->setPort("\\ADDR", sig_addr); + cell->setPort("\\DATA", sig_data); + cell->setPort("\\EN", sig_en); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); log("merged $dff to cell.\n"); + return; } + + log("no (compatible) $dff found.\n"); } static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) @@ -119,36 +111,32 @@ static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) sig.sort_and_unify(); std::stringstream sstr; - sstr << "$memory_dff_disconnected$" << (RTLIL::autoidx++); + sstr << "$memory_dff_disconnected$" << (autoidx++); - RTLIL::Wire *wire = new RTLIL::Wire; - wire->name = sstr.str(); - wire->width = sig.width; - module->wires[wire->name] = wire; + RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size()); - RTLIL::SigSpec newsig(wire); - - for (auto &cell_it : module->cells) { - RTLIL::Cell *cell = cell_it.second; - if (cell->type == "$dff") - cell->connections["\\Q"].replace(sig, newsig); - } + for (auto cell : module->cells()) + if (cell->type == "$dff") { + RTLIL::SigSpec new_q = cell->getPort("\\Q"); + new_q.replace(sig, new_sig); + cell->setPort("\\Q", new_q); + } } -static void handle_rd_cell(RTLIL::Module *module, RTLIL::Cell *cell) +static void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) { log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); bool clk_polarity = 0; RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_data = cell->connections["\\DATA"]; - if (find_sig_before_dff(module, sig_data, clk_data, clk_polarity, true) && + RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); + if (find_sig_before_dff(module, dff_cells, sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) { disconnect_dff(module, sig_data); - cell->connections["\\CLK"] = clk_data; - cell->connections["\\DATA"] = sig_data; + cell->setPort("\\CLK", clk_data); + cell->setPort("\\DATA", sig_data); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); @@ -157,12 +145,12 @@ static void handle_rd_cell(RTLIL::Module *module, RTLIL::Cell *cell) } RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"]; - if (find_sig_before_dff(module, sig_addr, clk_addr, clk_polarity) && + RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); + if (find_sig_before_dff(module, dff_cells, sig_addr, clk_addr, clk_polarity) && clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) { - cell->connections["\\CLK"] = clk_addr; - cell->connections["\\ADDR"] = sig_addr; + cell->setPort("\\CLK", clk_addr); + cell->setPort("\\ADDR", sig_addr); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1); @@ -173,16 +161,22 @@ static void handle_rd_cell(RTLIL::Module *module, RTLIL::Cell *cell) log("no (compatible) $dff found.\n"); } -static void handle_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_wr_only) +static void handle_module(RTLIL::Module *module, bool flag_wr_only) { - for (auto &cell_it : module->cells) { - if (!design->selected(module, cell_it.second)) - continue; - if (cell_it.second->type == "$memwr" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool()) - handle_wr_cell(module, cell_it.second); - if (!flag_wr_only && cell_it.second->type == "$memrd" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool()) - handle_rd_cell(module, cell_it.second); - } + std::vector<RTLIL::Cell*> dff_cells; + + for (auto cell : module->cells()) + if (cell->type == "$dff") + dff_cells.push_back(cell); + + for (auto cell : module->selected_cells()) + if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool()) + handle_wr_cell(module, dff_cells, cell); + + if (!flag_wr_only) + for (auto cell : module->selected_cells()) + if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool()) + handle_rd_cell(module, dff_cells, cell); } struct MemoryDffPass : public Pass { @@ -217,9 +211,8 @@ struct MemoryDffPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules) - if (design->selected(mod_it.second)) - handle_module(design, mod_it.second, flag_wr_only); + for (auto mod : design->selected_modules()) + handle_module(mod, flag_wr_only); } } MemoryDffPass; diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index e0e3802d1..eecb6f35d 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -22,316 +22,302 @@ #include <sstream> #include <set> #include <stdlib.h> -#include <assert.h> -static std::string genid(std::string name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "") +struct MemoryMapWorker { - std::stringstream sstr; - sstr << "$memory" << name << token1; - - if (i >= 0) - sstr << "[" << i << "]"; + RTLIL::Design *design; + RTLIL::Module *module; - sstr << token2; + std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, RTLIL::SigBit> decoder_cache; - if (j >= 0) - sstr << "[" << j << "]"; + std::string genid(RTLIL::IdString name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "") + { + std::stringstream sstr; + sstr << "$memory" << name.str() << token1; + + if (i >= 0) + sstr << "[" << i << "]"; - sstr << token3; + sstr << token2; - if (k >= 0) - sstr << "[" << k << "]"; + if (j >= 0) + sstr << "[" << j << "]"; - sstr << token4 << "$" << (RTLIL::autoidx++); - return sstr.str(); -} + sstr << token3; -static void handle_cell(RTLIL::Module *module, RTLIL::Cell *cell) -{ - std::set<int> static_ports; - std::map<int, RTLIL::SigSpec> static_cells_map; - int mem_size = cell->parameters["\\SIZE"].as_int(); - int mem_width = cell->parameters["\\WIDTH"].as_int(); - int mem_offset = cell->parameters["\\OFFSET"].as_int(); - int mem_abits = cell->parameters["\\ABITS"].as_int(); - - // delete unused memory cell - if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) { - module->cells.erase(cell->name); - delete cell; - return; - } + if (k >= 0) + sstr << "[" << k << "]"; - // all write ports must share the same clock - RTLIL::SigSpec clocks = cell->connections["\\WR_CLK"]; - RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"]; - RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"]; - RTLIL::SigSpec refclock; - RTLIL::State refclock_pol = RTLIL::State::Sx; - for (int i = 0; i < clocks.width; i++) { - RTLIL::SigSpec wr_en = cell->connections["\\WR_EN"].extract(i, 1); - if (wr_en.is_fully_const() && wr_en.as_int() == 0) { - static_ports.insert(i); - continue; - } - if (clocks_en.bits[i] != RTLIL::State::S1) { - RTLIL::SigSpec wr_addr = cell->connections["\\WR_ADDR"].extract(i*mem_abits, mem_abits); - RTLIL::SigSpec wr_data = cell->connections["\\WR_DATA"].extract(i*mem_width, mem_width); - if (wr_addr.is_fully_const()) { - // FIXME: Actually we should check for wr_en.is_fully_const() also and - // create a $adff cell with this ports wr_en input as reset pin when wr_en - // is not a simple static 1. - static_cells_map[wr_addr.as_int()] = wr_data; - static_ports.insert(i); - continue; - } - log("Not mapping memory cell %s in module %s (write port %d has no clock).\n", - cell->name.c_str(), module->name.c_str(), i); - return; - } - if (refclock.width == 0) { - refclock = clocks.extract(i, 1); - refclock_pol = clocks_pol.bits[i]; - } - if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) { - log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n", - cell->name.c_str(), module->name.c_str(), i); - return; - } + sstr << token4 << "$" << (autoidx++); + return sstr.str(); } - log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str()); + RTLIL::Wire *addr_decode(RTLIL::SigSpec addr_sig, RTLIL::SigSpec addr_val) + { + std::pair<RTLIL::SigSpec, RTLIL::SigSpec> key(addr_sig, addr_val); + log_assert(SIZE(addr_sig) == SIZE(addr_val)); - std::vector<RTLIL::SigSpec> data_reg_in; - std::vector<RTLIL::SigSpec> data_reg_out; + if (decoder_cache.count(key) == 0) { + if (SIZE(addr_sig) < 2) { + decoder_cache[key] = module->Eq(NEW_ID, addr_sig, addr_val); + } else { + int split_at = SIZE(addr_sig) / 2; + RTLIL::SigBit left_eq = addr_decode(addr_sig.extract(0, split_at), addr_val.extract(0, split_at)); + RTLIL::SigBit right_eq = addr_decode(addr_sig.extract(split_at, SIZE(addr_sig) - split_at), addr_val.extract(split_at, SIZE(addr_val) - split_at)); + decoder_cache[key] = module->And(NEW_ID, left_eq, right_eq); + } + } - int count_static = 0; + RTLIL::SigBit bit = decoder_cache.at(key); + log_assert(bit.wire != nullptr && SIZE(bit.wire) == 1); + return bit.wire; + } - for (int i = 0; i < mem_size; i++) + void handle_cell(RTLIL::Cell *cell) { - if (static_cells_map.count(i) > 0) - { - data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width)); - data_reg_out.push_back(static_cells_map[i]); - count_static++; + std::set<int> static_ports; + std::map<int, RTLIL::SigSpec> static_cells_map; + int mem_size = cell->parameters["\\SIZE"].as_int(); + int mem_width = cell->parameters["\\WIDTH"].as_int(); + int mem_offset = cell->parameters["\\OFFSET"].as_int(); + int mem_abits = cell->parameters["\\ABITS"].as_int(); + + // delete unused memory cell + if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) { + module->remove(cell); + return; } - else - { - RTLIL::Cell *c = new RTLIL::Cell; - c->name = genid(cell->name, "", i); - c->type = "$dff"; - c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - if (clocks_pol.bits.size() > 0) { - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(clocks_pol.bits[0]); - c->connections["\\CLK"] = clocks.extract(0, 1); - } else { - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(RTLIL::State::S1); - c->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::S0); + + // all write ports must share the same clock + RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK"); + RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"]; + RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"]; + RTLIL::SigSpec refclock; + RTLIL::State refclock_pol = RTLIL::State::Sx; + for (int i = 0; i < clocks.size(); i++) { + RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(i * mem_width, mem_width); + if (wr_en.is_fully_const() && !wr_en.as_bool()) { + static_ports.insert(i); + continue; + } + if (clocks_en.bits[i] != RTLIL::State::S1) { + RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(i*mem_abits, mem_abits); + RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(i*mem_width, mem_width); + if (wr_addr.is_fully_const()) { + // FIXME: Actually we should check for wr_en.is_fully_const() also and + // create a $adff cell with this ports wr_en input as reset pin when wr_en + // is not a simple static 1. + static_cells_map[wr_addr.as_int()] = wr_data; + static_ports.insert(i); + continue; + } + log("Not mapping memory cell %s in module %s (write port %d has no clock).\n", + cell->name.c_str(), module->name.c_str(), i); + return; + } + if (refclock.size() == 0) { + refclock = clocks.extract(i, 1); + refclock_pol = clocks_pol.bits[i]; + } + if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) { + log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n", + cell->name.c_str(), module->name.c_str(), i); + return; } - module->cells[c->name] = c; - - RTLIL::Wire *w_in = new RTLIL::Wire; - w_in->name = genid(cell->name, "", i, "$d"); - w_in->width = mem_width; - module->wires[w_in->name] = w_in; - data_reg_in.push_back(RTLIL::SigSpec(w_in)); - c->connections["\\D"] = data_reg_in.back(); - - RTLIL::Wire *w_out = new RTLIL::Wire; - w_out->name = stringf("%s[%d]", cell->parameters["\\MEMID"].decode_string().c_str(), i); - if (module->wires.count(w_out->name) > 0) - w_out->name = genid(cell->name, "", i, "$q"); - w_out->width = mem_width; - w_out->start_offset = mem_offset; - module->wires[w_out->name] = w_out; - data_reg_out.push_back(RTLIL::SigSpec(w_out)); - c->connections["\\Q"] = data_reg_out.back(); } - } - log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width); + log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str()); - int count_dff = 0, count_mux = 0, count_wrmux = 0; - - for (int i = 0; i < cell->parameters["\\RD_PORTS"].as_int(); i++) - { - RTLIL::SigSpec rd_addr = cell->connections["\\RD_ADDR"].extract(i*mem_abits, mem_abits); + std::vector<RTLIL::SigSpec> data_reg_in; + std::vector<RTLIL::SigSpec> data_reg_out; - std::vector<RTLIL::SigSpec> rd_signals; - rd_signals.push_back(cell->connections["\\RD_DATA"].extract(i*mem_width, mem_width)); + int count_static = 0; - if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1) + for (int i = 0; i < mem_size; i++) { - if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1) + if (static_cells_map.count(i) > 0) { - RTLIL::Cell *c = new RTLIL::Cell; - c->name = genid(cell->name, "$rdreg", i); - c->type = "$dff"; - c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->connections["\\CLK"] = cell->connections["\\RD_CLK"].extract(i, 1); - c->connections["\\D"] = rd_addr; - module->cells[c->name] = c; - count_dff++; - - RTLIL::Wire *w = new RTLIL::Wire; - w->name = genid(cell->name, "$rdreg", i, "$q"); - w->width = mem_abits; - module->wires[w->name] = w; - - c->connections["\\Q"] = RTLIL::SigSpec(w); - rd_addr = RTLIL::SigSpec(w); + data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width)); + data_reg_out.push_back(static_cells_map[i]); + count_static++; } else { - RTLIL::Cell *c = new RTLIL::Cell; - c->name = genid(cell->name, "$rdreg", i); - c->type = "$dff"; + RTLIL::Cell *c = module->addCell(genid(cell->name, "", i), "$dff"); c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->connections["\\CLK"] = cell->connections["\\RD_CLK"].extract(i, 1); - c->connections["\\Q"] = rd_signals.back(); - module->cells[c->name] = c; - count_dff++; - - RTLIL::Wire *w = new RTLIL::Wire; - w->name = genid(cell->name, "$rdreg", i, "$d"); - w->width = mem_width; - module->wires[w->name] = w; - - rd_signals.clear(); - rd_signals.push_back(RTLIL::SigSpec(w)); - c->connections["\\D"] = rd_signals.back(); + if (clocks_pol.bits.size() > 0) { + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(clocks_pol.bits[0]); + c->setPort("\\CLK", clocks.extract(0, 1)); + } else { + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(RTLIL::State::S1); + c->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::S0)); + } + + RTLIL::Wire *w_in = module->addWire(genid(cell->name, "", i, "$d"), mem_width); + data_reg_in.push_back(RTLIL::SigSpec(w_in)); + c->setPort("\\D", data_reg_in.back()); + + std::string w_out_name = stringf("%s[%d]", cell->parameters["\\MEMID"].decode_string().c_str(), i); + if (module->wires_.count(w_out_name) > 0) + w_out_name = genid(cell->name, "", i, "$q"); + + RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width); + w_out->start_offset = mem_offset; + + data_reg_out.push_back(RTLIL::SigSpec(w_out)); + c->setPort("\\Q", data_reg_out.back()); } } - for (int j = 0; j < mem_abits; j++) + log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width); + + int count_dff = 0, count_mux = 0, count_wrmux = 0; + + for (int i = 0; i < cell->parameters["\\RD_PORTS"].as_int(); i++) { - std::vector<RTLIL::SigSpec> next_rd_signals; + RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits); + + std::vector<RTLIL::SigSpec> rd_signals; + rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width)); - for (size_t k = 0; k < rd_signals.size(); k++) + if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1) { - RTLIL::Cell *c = new RTLIL::Cell; - c->name = genid(cell->name, "$rdmux", i, "", j, "", k); - c->type = "$mux"; - c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - c->connections["\\Y"] = rd_signals[k]; - c->connections["\\S"] = rd_addr.extract(mem_abits-j-1, 1); - module->cells[c->name] = c; - count_mux++; - - RTLIL::Wire *w = new RTLIL::Wire; - w->name = genid(cell->name, "$rdmux", i, "", j, "", k, "$a"); - w->width = mem_width; - module->wires[w->name] = w; - c->connections["\\A"] = RTLIL::SigSpec(w); - - w = new RTLIL::Wire; - w->name = genid(cell->name, "$rdmux", i, "", j, "", k, "$b"); - w->width = mem_width; - module->wires[w->name] = w; - c->connections["\\B"] = RTLIL::SigSpec(w); - - next_rd_signals.push_back(c->connections["\\A"]); - next_rd_signals.push_back(c->connections["\\B"]); + if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1) + { + RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + c->setPort("\\D", rd_addr); + count_dff++; + + RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits); + + c->setPort("\\Q", RTLIL::SigSpec(w)); + rd_addr = RTLIL::SigSpec(w); + } + else + { + RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + c->setPort("\\Q", rd_signals.back()); + count_dff++; + + RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width); + + rd_signals.clear(); + rd_signals.push_back(RTLIL::SigSpec(w)); + c->setPort("\\D", rd_signals.back()); + } } - next_rd_signals.swap(rd_signals); - } + for (int j = 0; j < mem_abits; j++) + { + std::vector<RTLIL::SigSpec> next_rd_signals; - for (int j = 0; j < mem_size; j++) - module->connections.push_back(RTLIL::SigSig(rd_signals[j], data_reg_out[j])); - } + for (size_t k = 0; k < rd_signals.size(); k++) + { + RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdmux", i, "", j, "", k), "$mux"); + c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + c->setPort("\\Y", rd_signals[k]); + c->setPort("\\S", rd_addr.extract(mem_abits-j-1, 1)); + count_mux++; - log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux); + c->setPort("\\A", module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$a"), mem_width)); + c->setPort("\\B", module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$b"), mem_width)); - for (int i = 0; i < mem_size; i++) - { - if (static_cells_map.count(i) > 0) - continue; + next_rd_signals.push_back(c->getPort("\\A")); + next_rd_signals.push_back(c->getPort("\\B")); + } - RTLIL::SigSpec sig = data_reg_out[i]; + next_rd_signals.swap(rd_signals); + } - for (int j = 0; j < cell->parameters["\\WR_PORTS"].as_int(); j++) + for (int j = 0; j < mem_size; j++) + module->connect(RTLIL::SigSig(rd_signals[j], data_reg_out[j])); + } + + log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux); + + for (int i = 0; i < mem_size; i++) { - RTLIL::SigSpec wr_addr = cell->connections["\\WR_ADDR"].extract(j*mem_abits, mem_abits); - RTLIL::SigSpec wr_data = cell->connections["\\WR_DATA"].extract(j*mem_width, mem_width); - RTLIL::SigSpec wr_en = cell->connections["\\WR_EN"].extract(j, 1); - - RTLIL::Cell *c = new RTLIL::Cell; - c->name = genid(cell->name, "$wreq", i, "", j); - c->type = "$eq"; - c->parameters["\\A_SIGNED"] = RTLIL::Const(0); - c->parameters["\\B_SIGNED"] = RTLIL::Const(0); - c->parameters["\\A_WIDTH"] = cell->parameters["\\ABITS"]; - c->parameters["\\B_WIDTH"] = cell->parameters["\\ABITS"]; - c->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - c->connections["\\A"] = RTLIL::SigSpec(i, mem_abits); - c->connections["\\B"] = wr_addr; - module->cells[c->name] = c; - count_wrmux++; - - RTLIL::Wire *w = new RTLIL::Wire; - w->name = genid(cell->name, "$wreq", i, "", j, "$y"); - module->wires[w->name] = w; - c->connections["\\Y"] = RTLIL::SigSpec(w); - - if (wr_en != RTLIL::SigSpec(1, 1)) + if (static_cells_map.count(i) > 0) + continue; + + RTLIL::SigSpec sig = data_reg_out[i]; + + for (int j = 0; j < cell->parameters["\\WR_PORTS"].as_int(); j++) { - c = new RTLIL::Cell; - c->name = genid(cell->name, "$wren", i, "", j); - c->type = "$and"; - c->parameters["\\A_SIGNED"] = RTLIL::Const(0); - c->parameters["\\B_SIGNED"] = RTLIL::Const(0); - c->parameters["\\A_WIDTH"] = RTLIL::Const(1); - c->parameters["\\B_WIDTH"] = RTLIL::Const(1); - c->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - c->connections["\\A"] = RTLIL::SigSpec(w); - c->connections["\\B"] = wr_en; - module->cells[c->name] = c; - - w = new RTLIL::Wire; - w->name = genid(cell->name, "$wren", i, "", j, "$y"); - module->wires[w->name] = w; - c->connections["\\Y"] = RTLIL::SigSpec(w); + RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits); + RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width); + RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width); + RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits)); + + int wr_offset = 0; + while (wr_offset < wr_en.size()) + { + int wr_width = 1; + RTLIL::SigSpec wr_bit = wr_en.extract(wr_offset, 1); + + while (wr_offset + wr_width < wr_en.size()) { + RTLIL::SigSpec next_wr_bit = wr_en.extract(wr_offset + wr_width, 1); + if (next_wr_bit != wr_bit) + break; + wr_width++; + } + + RTLIL::Wire *w = w_seladdr; + + if (wr_bit != RTLIL::SigSpec(1, 1)) + { + RTLIL::Cell *c = module->addCell(genid(cell->name, "$wren", i, "", j, "", wr_offset), "$and"); + c->parameters["\\A_SIGNED"] = RTLIL::Const(0); + c->parameters["\\B_SIGNED"] = RTLIL::Const(0); + c->parameters["\\A_WIDTH"] = RTLIL::Const(1); + c->parameters["\\B_WIDTH"] = RTLIL::Const(1); + c->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + c->setPort("\\A", w); + c->setPort("\\B", wr_bit); + + w = module->addWire(genid(cell->name, "$wren", i, "", j, "", wr_offset, "$y")); + c->setPort("\\Y", RTLIL::SigSpec(w)); + } + + RTLIL::Cell *c = module->addCell(genid(cell->name, "$wrmux", i, "", j, "", wr_offset), "$mux"); + c->parameters["\\WIDTH"] = wr_width; + c->setPort("\\A", sig.extract(wr_offset, wr_width)); + c->setPort("\\B", wr_data.extract(wr_offset, wr_width)); + c->setPort("\\S", RTLIL::SigSpec(w)); + + w = module->addWire(genid(cell->name, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width); + c->setPort("\\Y", w); + + sig.replace(wr_offset, w); + wr_offset += wr_width; + count_wrmux++; + } } - c = new RTLIL::Cell; - c->name = genid(cell->name, "$wrmux", i, "", j); - c->type = "$mux"; - c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - c->connections["\\A"] = sig; - c->connections["\\B"] = wr_data; - c->connections["\\S"] = RTLIL::SigSpec(w); - module->cells[c->name] = c; - - w = new RTLIL::Wire; - w->name = genid(cell->name, "$wrmux", i, "", j, "$y"); - w->width = mem_width; - module->wires[w->name] = w; - c->connections["\\Y"] = RTLIL::SigSpec(w); - sig = RTLIL::SigSpec(w); + module->connect(RTLIL::SigSig(data_reg_in[i], sig)); } - module->connections.push_back(RTLIL::SigSig(data_reg_in[i], sig)); - } - - log(" write interface: %d blocks of $eq, $and and $mux cells.\n", count_wrmux); + log(" write interface: %d write mux blocks.\n", count_wrmux); - module->cells.erase(cell->name); - delete cell; - return; -} + module->remove(cell); + } -static void handle_module(RTLIL::Design *design, RTLIL::Module *module) -{ - std::vector<RTLIL::Cell*> cells; - for (auto &it : module->cells) - if (it.second->type == "$mem" && design->selected(module, it.second)) - cells.push_back(it.second); - for (auto cell : cells) - handle_cell(module, cell); -} + MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) + { + std::vector<RTLIL::Cell*> cells; + for (auto cell : module->selected_cells()) + if (cell->type == "$mem" && design->selected(module, cell)) + cells.push_back(cell); + for (auto cell : cells) + handle_cell(cell); + } +}; struct MemoryMapPass : public Pass { MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { } @@ -348,9 +334,8 @@ struct MemoryMapPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) - if (design->selected(mod_it.second)) - handle_module(design, mod_it.second); + for (auto mod : design->selected_modules()) + MemoryMapWorker(design, mod); } } MemoryMapPass; diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc new file mode 100644 index 000000000..3ae0cd2c7 --- /dev/null +++ b/passes/memory/memory_share.cc @@ -0,0 +1,744 @@ +/* + * 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 "kernel/satgen.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" + +PRIVATE_NAMESPACE_BEGIN + +static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +{ + if (a->type == "$memrd" && b->type == "$memrd") + return a->name < b->name; + if (a->type == "$memrd" || b->type == "$memrd") + return (a->type == "$memrd") < (b->type == "$memrd"); + return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int(); +} + +struct MemoryShareWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap sigmap, sigmap_xmux; + ModWalker modwalker; + CellTypes cone_ct; + + std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux; + std::map<std::set<std::map<RTLIL::SigBit, bool>>, RTLIL::SigBit> conditions_logic_cache; + + + // ----------------------------------------------------------------- + // Converting feedbacks to async read ports to proper enable signals + // ----------------------------------------------------------------- + + bool find_data_feedback(const std::set<RTLIL::SigBit> &async_rd_bits, RTLIL::SigBit sig, + std::map<RTLIL::SigBit, bool> &state, std::set<std::map<RTLIL::SigBit, bool>> &conditions) + { + if (async_rd_bits.count(sig)) { + conditions.insert(state); + return true; + } + + if (sig_to_mux.count(sig) == 0) + return false; + + RTLIL::Cell *cell = sig_to_mux.at(sig).first; + int bit_idx = sig_to_mux.at(sig).second; + + std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort("\\A")); + std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort("\\B")); + std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort("\\S")); + std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y")); + log_assert(sig_y.at(bit_idx) == sig); + + for (int i = 0; i < int(sig_s.size()); i++) + if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) { + if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, conditions)) { + RTLIL::SigSpec new_b = cell->getPort("\\B"); + new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); + cell->setPort("\\B", new_b); + } + return false; + } + + + for (int i = 0; i < int(sig_s.size()); i++) + { + if (state.count(sig_s[i]) && state.at(sig_s[i]) == false) + continue; + + std::map<RTLIL::SigBit, bool> new_state = state; + new_state[sig_s[i]] = true; + + if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, conditions)) { + RTLIL::SigSpec new_b = cell->getPort("\\B"); + new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); + cell->setPort("\\B", new_b); + } + } + + std::map<RTLIL::SigBit, bool> new_state = state; + for (int i = 0; i < int(sig_s.size()); i++) + new_state[sig_s[i]] = false; + + if (find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, conditions)) { + RTLIL::SigSpec new_a = cell->getPort("\\A"); + new_a.replace(bit_idx, RTLIL::State::Sx); + cell->setPort("\\A", new_a); + } + + return false; + } + + RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, int &created_conditions) + { + if (conditions_logic_cache.count(conditions)) + return conditions_logic_cache.at(conditions); + + RTLIL::SigSpec terms; + for (auto &cond : conditions) { + RTLIL::SigSpec sig1, sig2; + for (auto &it : cond) { + sig1.append_bit(it.first); + sig2.append_bit(it.second ? RTLIL::State::S1 : RTLIL::State::S0); + } + terms.append(module->Ne(NEW_ID, sig1, sig2)); + created_conditions++; + } + + if (terms.size() > 1) + terms = module->ReduceAnd(NEW_ID, terms); + + return conditions_logic_cache[conditions] = terms; + } + + void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports) + { + std::map<RTLIL::SigSpec, std::vector<std::set<RTLIL::SigBit>>> async_rd_bits; + std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map; + std::set<RTLIL::SigBit> non_feedback_nets; + + for (auto wire_it : module->wires_) + if (wire_it.second->port_output) { + std::vector<RTLIL::SigBit> bits = RTLIL::SigSpec(wire_it.second); + non_feedback_nets.insert(bits.begin(), bits.end()); + } + + for (auto cell_it : module->cells_) + { + RTLIL::Cell *cell = cell_it.second; + bool ignore_data_port = false; + + if (cell->type == "$mux" || cell->type == "$pmux") + { + std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort("\\A")); + std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort("\\B")); + std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort("\\S")); + std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y")); + + non_feedback_nets.insert(sig_s.begin(), sig_s.end()); + + for (int i = 0; i < int(sig_y.size()); i++) { + muxtree_upstream_map[sig_y[i]].insert(sig_a[i]); + for (int j = 0; j < int(sig_s.size()); j++) + muxtree_upstream_map[sig_y[i]].insert(sig_b[i + j*sig_y.size()]); + } + + continue; + } + + if ((cell->type == "$memwr" || cell->type == "$memrd") && + cell->parameters.at("\\MEMID").decode_string() == memid) + ignore_data_port = true; + + for (auto conn : cell_it.second->connections()) + { + if (ignore_data_port && conn.first == "\\DATA") + continue; + std::vector<RTLIL::SigBit> bits = sigmap(conn.second); + non_feedback_nets.insert(bits.begin(), bits.end()); + } + } + + std::set<RTLIL::SigBit> expand_non_feedback_nets = non_feedback_nets; + while (!expand_non_feedback_nets.empty()) + { + std::set<RTLIL::SigBit> new_expand_non_feedback_nets; + + for (auto &bit : expand_non_feedback_nets) + if (muxtree_upstream_map.count(bit)) + for (auto &new_bit : muxtree_upstream_map.at(bit)) + if (!non_feedback_nets.count(new_bit)) { + non_feedback_nets.insert(new_bit); + new_expand_non_feedback_nets.insert(new_bit); + } + + expand_non_feedback_nets.swap(new_expand_non_feedback_nets); + } + + for (auto cell : rd_ports) + { + if (cell->parameters.at("\\CLK_ENABLE").as_bool()) + continue; + + RTLIL::SigSpec sig_addr = sigmap(cell->getPort("\\ADDR")); + std::vector<RTLIL::SigBit> sig_data = sigmap(cell->getPort("\\DATA")); + + for (int i = 0; i < int(sig_data.size()); i++) + if (non_feedback_nets.count(sig_data[i])) + goto not_pure_feedback_port; + + async_rd_bits[sig_addr].resize(std::max(async_rd_bits.size(), sig_data.size())); + for (int i = 0; i < int(sig_data.size()); i++) + async_rd_bits[sig_addr][i].insert(sig_data[i]); + + not_pure_feedback_port:; + } + + if (async_rd_bits.empty()) + return; + + log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(memid)); + + for (auto cell : wr_ports) + { + RTLIL::SigSpec sig_addr = sigmap_xmux(cell->getPort("\\ADDR")); + if (!async_rd_bits.count(sig_addr)) + continue; + + log(" Analyzing write port %s.\n", log_id(cell)); + + std::vector<RTLIL::SigBit> cell_data = cell->getPort("\\DATA"); + std::vector<RTLIL::SigBit> cell_en = cell->getPort("\\EN"); + + int created_conditions = 0; + for (int i = 0; i < int(cell_data.size()); i++) + if (cell_en[i] != RTLIL::SigBit(RTLIL::State::S0)) + { + std::map<RTLIL::SigBit, bool> state; + std::set<std::map<RTLIL::SigBit, bool>> conditions; + + if (cell_en[i].wire != NULL) { + state[cell_en[i]] = false; + conditions.insert(state); + } + + find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); + cell_en[i] = conditions_to_logic(conditions, created_conditions); + } + + if (created_conditions) { + log(" Added enable logic for %d different cases.\n", created_conditions); + cell->setPort("\\EN", cell_en); + } + } + } + + + // ------------------------------------------------------ + // Consolidate write ports that write to the same address + // ------------------------------------------------------ + + RTLIL::SigSpec mask_en_naive(RTLIL::SigSpec do_mask, RTLIL::SigSpec bits, RTLIL::SigSpec mask_bits) + { + // this is the naive version of the function that does not care about grouping the EN bits. + + RTLIL::SigSpec inv_mask_bits = module->Not(NEW_ID, mask_bits); + RTLIL::SigSpec inv_mask_bits_filtered = module->Mux(NEW_ID, RTLIL::SigSpec(RTLIL::State::S1, bits.size()), inv_mask_bits, do_mask); + RTLIL::SigSpec result = module->And(NEW_ID, inv_mask_bits_filtered, bits); + return result; + } + + RTLIL::SigSpec mask_en_grouped(RTLIL::SigSpec do_mask, RTLIL::SigSpec bits, RTLIL::SigSpec mask_bits) + { + // this version of the function preserves the bit grouping in the EN bits. + + std::vector<RTLIL::SigBit> v_bits = bits; + std::vector<RTLIL::SigBit> v_mask_bits = mask_bits; + + std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::pair<int, std::vector<int>>> groups; + RTLIL::SigSpec grouped_bits, grouped_mask_bits; + + for (int i = 0; i < bits.size(); i++) { + std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_bits[i], v_mask_bits[i]); + if (groups.count(key) == 0) { + groups[key].first = grouped_bits.size(); + grouped_bits.append_bit(v_bits[i]); + grouped_mask_bits.append_bit(v_mask_bits[i]); + } + groups[key].second.push_back(i); + } + + std::vector<RTLIL::SigBit> grouped_result = mask_en_naive(do_mask, grouped_bits, grouped_mask_bits); + RTLIL::SigSpec result; + + for (int i = 0; i < bits.size(); i++) { + std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_bits[i], v_mask_bits[i]); + result.append_bit(grouped_result.at(groups.at(key).first)); + } + + return result; + } + + void merge_en_data(RTLIL::SigSpec &merged_en, RTLIL::SigSpec &merged_data, RTLIL::SigSpec next_en, RTLIL::SigSpec next_data) + { + std::vector<RTLIL::SigBit> v_old_en = merged_en; + std::vector<RTLIL::SigBit> v_next_en = next_en; + + // The new merged_en signal is just the old merged_en signal and next_en OR'ed together. + // But of course we need to preserve the bit grouping.. + + std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups; + std::vector<RTLIL::SigBit> grouped_old_en, grouped_next_en; + RTLIL::SigSpec new_merged_en; + + for (int i = 0; i < int(v_old_en.size()); i++) { + std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_old_en[i], v_next_en[i]); + if (groups.count(key) == 0) { + groups[key] = grouped_old_en.size(); + grouped_old_en.push_back(key.first); + grouped_next_en.push_back(key.second); + } + } + + std::vector<RTLIL::SigBit> grouped_new_en = module->Or(NEW_ID, grouped_old_en, grouped_next_en); + + for (int i = 0; i < int(v_old_en.size()); i++) { + std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_old_en[i], v_next_en[i]); + new_merged_en.append_bit(grouped_new_en.at(groups.at(key))); + } + + // Create the new merged_data signal. + + RTLIL::SigSpec new_merged_data(RTLIL::State::Sx, merged_data.size()); + + RTLIL::SigSpec old_data_set = module->And(NEW_ID, merged_en, merged_data); + RTLIL::SigSpec old_data_unset = module->And(NEW_ID, merged_en, module->Not(NEW_ID, merged_data)); + + RTLIL::SigSpec new_data_set = module->And(NEW_ID, next_en, next_data); + RTLIL::SigSpec new_data_unset = module->And(NEW_ID, next_en, module->Not(NEW_ID, next_data)); + + new_merged_data = module->Or(NEW_ID, new_merged_data, old_data_set); + new_merged_data = module->And(NEW_ID, new_merged_data, module->Not(NEW_ID, old_data_unset)); + + new_merged_data = module->Or(NEW_ID, new_merged_data, new_data_set); + new_merged_data = module->And(NEW_ID, new_merged_data, module->Not(NEW_ID, new_data_unset)); + + // Update merged_* signals + + merged_en = new_merged_en; + merged_data = new_merged_data; + } + + void consolidate_wr_by_addr(std::string memid, std::vector<RTLIL::Cell*> &wr_ports) + { + if (wr_ports.size() <= 1) + return; + + log("Consolidating write ports of memory %s.%s by address:\n", log_id(module), log_id(memid)); + + std::map<RTLIL::SigSpec, int> last_port_by_addr; + std::vector<std::vector<bool>> active_bits_on_port; + + bool cache_clk_enable = false; + bool cache_clk_polarity = false; + RTLIL::SigSpec cache_clk; + + for (int i = 0; i < int(wr_ports.size()); i++) + { + RTLIL::Cell *cell = wr_ports.at(i); + RTLIL::SigSpec addr = sigmap_xmux(cell->getPort("\\ADDR")); + + if (cell->parameters.at("\\CLK_ENABLE").as_bool() != cache_clk_enable || + (cache_clk_enable && (sigmap(cell->getPort("\\CLK")) != cache_clk || + cell->parameters.at("\\CLK_POLARITY").as_bool() != cache_clk_polarity))) + { + cache_clk_enable = cell->parameters.at("\\CLK_ENABLE").as_bool(); + cache_clk_polarity = cell->parameters.at("\\CLK_POLARITY").as_bool(); + cache_clk = sigmap(cell->getPort("\\CLK")); + last_port_by_addr.clear(); + + if (cache_clk_enable) + log(" New clock domain: %s %s\n", cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk)); + else + log(" New clock domain: unclocked\n"); + } + + log(" Port %d (%s) has addr %s.\n", i, log_id(cell), log_signal(addr)); + + log(" Active bits: "); + std::vector<RTLIL::SigBit> en_bits = sigmap(cell->getPort("\\EN")); + active_bits_on_port.push_back(std::vector<bool>(en_bits.size())); + for (int k = int(en_bits.size())-1; k >= 0; k--) { + active_bits_on_port[i][k] = en_bits[k].wire != NULL || en_bits[k].data != RTLIL::State::S0; + log("%c", active_bits_on_port[i][k] ? '1' : '0'); + } + log("\n"); + + if (last_port_by_addr.count(addr)) + { + int last_i = last_port_by_addr.at(addr); + log(" Merging port %d into this one.\n", last_i); + + bool found_overlapping_bits = false; + for (int k = 0; k < int(en_bits.size()); k++) { + if (active_bits_on_port[i][k] && active_bits_on_port[last_i][k]) + found_overlapping_bits = true; + active_bits_on_port[i][k] = active_bits_on_port[i][k] || active_bits_on_port[last_i][k]; + } + + // Force this ports addr input to addr directly (skip don't care muxes) + + cell->setPort("\\ADDR", addr); + + // If any of the ports between `last_i' and `i' write to the same address, this + // will have priority over whatever `last_i` wrote. So we need to revisit those + // ports and mask the EN bits accordingly. + + RTLIL::SigSpec merged_en = sigmap(wr_ports[last_i]->getPort("\\EN")); + + for (int j = last_i+1; j < i; j++) + { + if (wr_ports[j] == NULL) + continue; + + for (int k = 0; k < int(en_bits.size()); k++) + if (active_bits_on_port[i][k] && active_bits_on_port[j][k]) + goto found_overlapping_bits_i_j; + + if (0) { + found_overlapping_bits_i_j: + log(" Creating collosion-detect logic for port %d.\n", j); + RTLIL::SigSpec is_same_addr = module->addWire(NEW_ID); + module->addEq(NEW_ID, addr, wr_ports[j]->getPort("\\ADDR"), is_same_addr); + merged_en = mask_en_grouped(is_same_addr, merged_en, sigmap(wr_ports[j]->getPort("\\EN"))); + } + } + + // Then we need to merge the (masked) EN and the DATA signals. + + RTLIL::SigSpec merged_data = wr_ports[last_i]->getPort("\\DATA"); + if (found_overlapping_bits) { + log(" Creating logic for merging DATA and EN ports.\n"); + merge_en_data(merged_en, merged_data, sigmap(cell->getPort("\\EN")), sigmap(cell->getPort("\\DATA"))); + } else { + RTLIL::SigSpec cell_en = sigmap(cell->getPort("\\EN")); + RTLIL::SigSpec cell_data = sigmap(cell->getPort("\\DATA")); + for (int k = 0; k < int(en_bits.size()); k++) + if (!active_bits_on_port[last_i][k]) { + merged_en.replace(k, cell_en.extract(k, 1)); + merged_data.replace(k, cell_data.extract(k, 1)); + } + } + + // Connect the new EN and DATA signals and remove the old write port. + + cell->setPort("\\EN", merged_en); + cell->setPort("\\DATA", merged_data); + + module->remove(wr_ports[last_i]); + wr_ports[last_i] = NULL; + + log(" Active bits: "); + std::vector<RTLIL::SigBit> en_bits = sigmap(cell->getPort("\\EN")); + active_bits_on_port.push_back(std::vector<bool>(en_bits.size())); + for (int k = int(en_bits.size())-1; k >= 0; k--) + log("%c", active_bits_on_port[i][k] ? '1' : '0'); + log("\n"); + } + + last_port_by_addr[addr] = i; + } + + // Clean up `wr_ports': remove all NULL entries + + std::vector<RTLIL::Cell*> wr_ports_with_nulls; + wr_ports_with_nulls.swap(wr_ports); + + for (auto cell : wr_ports_with_nulls) + if (cell != NULL) + wr_ports.push_back(cell); + } + + + // -------------------------------------------------------- + // Consolidate write ports using sat-based resource sharing + // -------------------------------------------------------- + + void consolidate_wr_using_sat(std::string memid, std::vector<RTLIL::Cell*> &wr_ports) + { + if (wr_ports.size() <= 1) + return; + + ezDefaultSAT ez; + SatGen satgen(&ez, &modwalker.sigmap); + + // find list of considered ports and port pairs + + std::set<int> considered_ports; + std::set<int> considered_port_pairs; + + for (int i = 0; i < int(wr_ports.size()); i++) { + std::vector<RTLIL::SigBit> bits = modwalker.sigmap(wr_ports[i]->getPort("\\EN")); + for (auto bit : bits) + if (bit == RTLIL::State::S1) + goto port_is_always_active; + if (modwalker.has_drivers(bits)) + considered_ports.insert(i); + port_is_always_active:; + } + + log("Consolidating write ports of memory %s.%s using sat-based resource sharing:\n", log_id(module), log_id(memid)); + + bool cache_clk_enable = false; + bool cache_clk_polarity = false; + RTLIL::SigSpec cache_clk; + + for (int i = 0; i < int(wr_ports.size()); i++) + { + RTLIL::Cell *cell = wr_ports.at(i); + + if (cell->parameters.at("\\CLK_ENABLE").as_bool() != cache_clk_enable || + (cache_clk_enable && (sigmap(cell->getPort("\\CLK")) != cache_clk || + cell->parameters.at("\\CLK_POLARITY").as_bool() != cache_clk_polarity))) + { + cache_clk_enable = cell->parameters.at("\\CLK_ENABLE").as_bool(); + cache_clk_polarity = cell->parameters.at("\\CLK_POLARITY").as_bool(); + cache_clk = sigmap(cell->getPort("\\CLK")); + } + else if (i > 0 && considered_ports.count(i-1) && considered_ports.count(i)) + considered_port_pairs.insert(i); + + if (cache_clk_enable) + log(" Port %d (%s) on %s %s: %s\n", i, log_id(cell), + cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk), + considered_ports.count(i) ? "considered" : "not considered"); + else + log(" Port %d (%s) unclocked: %s\n", i, log_id(cell), + considered_ports.count(i) ? "considered" : "not considered"); + } + + if (considered_port_pairs.size() < 1) { + log(" No two subsequent ports in same clock domain considered -> nothing to consolidate.\n"); + return; + } + + // create SAT representation of common input cone of all considered EN signals + + std::set<RTLIL::Cell*> sat_cells; + std::set<RTLIL::SigBit> bits_queue; + std::map<int, int> port_to_sat_variable; + + for (int i = 0; i < int(wr_ports.size()); i++) + if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1)) + { + RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort("\\EN")); + port_to_sat_variable[i] = ez.expression(ez.OpOr, satgen.importSigSpec(sig)); + + std::vector<RTLIL::SigBit> bits = sig; + bits_queue.insert(bits.begin(), bits.end()); + } + + while (!bits_queue.empty()) + { + std::set<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, bits_queue); + bits_queue.clear(); + + for (auto &pbit : portbits) + if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) { + std::set<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell]; + bits_queue.insert(cell_inputs.begin(), cell_inputs.end()); + sat_cells.insert(pbit.cell); + } + } + + log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size())); + + for (auto cell : sat_cells) + satgen.importCell(cell); + + log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez.numCnfVariables(), ez.numCnfClauses()); + + // merge subsequent ports if possible + + for (int i = 0; i < int(wr_ports.size()); i++) + { + if (!considered_port_pairs.count(i)) + continue; + + if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) { + log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i); + continue; + } + + log(" Merging port %d into port %d.\n", i-1, i); + port_to_sat_variable.at(i) = ez.OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i)); + + RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort("\\ADDR"); + RTLIL::SigSpec last_data = wr_ports[i-1]->getPort("\\DATA"); + std::vector<RTLIL::SigBit> last_en = modwalker.sigmap(wr_ports[i-1]->getPort("\\EN")); + + RTLIL::SigSpec this_addr = wr_ports[i]->getPort("\\ADDR"); + RTLIL::SigSpec this_data = wr_ports[i]->getPort("\\DATA"); + std::vector<RTLIL::SigBit> this_en = modwalker.sigmap(wr_ports[i]->getPort("\\EN")); + + RTLIL::SigBit this_en_active = module->ReduceOr(NEW_ID, this_en); + + wr_ports[i]->setPort("\\ADDR", module->Mux(NEW_ID, last_addr, this_addr, this_en_active)); + wr_ports[i]->setPort("\\DATA", module->Mux(NEW_ID, last_data, this_data, this_en_active)); + + std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups_en; + RTLIL::SigSpec grouped_last_en, grouped_this_en, en; + RTLIL::Wire *grouped_en = module->addWire(NEW_ID, 0); + + for (int j = 0; j < int(this_en.size()); j++) { + std::pair<RTLIL::SigBit, RTLIL::SigBit> key(last_en[j], this_en[j]); + if (!groups_en.count(key)) { + grouped_last_en.append_bit(last_en[j]); + grouped_this_en.append_bit(this_en[j]); + groups_en[key] = grouped_en->width; + grouped_en->width++; + } + en.append(RTLIL::SigSpec(grouped_en, groups_en[key])); + } + + module->addMux(NEW_ID, grouped_last_en, grouped_this_en, this_en_active, grouped_en); + wr_ports[i]->setPort("\\EN", en); + + module->remove(wr_ports[i-1]); + wr_ports[i-1] = NULL; + } + + // Clean up `wr_ports': remove all NULL entries + + std::vector<RTLIL::Cell*> wr_ports_with_nulls; + wr_ports_with_nulls.swap(wr_ports); + + for (auto cell : wr_ports_with_nulls) + if (cell != NULL) + wr_ports.push_back(cell); + } + + + // ------------- + // Setup and run + // ------------- + + MemoryShareWorker(RTLIL::Design *design, RTLIL::Module *module) : + design(design), module(module), sigmap(module) + { + std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex; + + sigmap_xmux = sigmap; + for (auto &it : module->cells_) + { + RTLIL::Cell *cell = it.second; + + if (cell->type == "$memrd") + memindex[cell->parameters.at("\\MEMID").decode_string()].first.push_back(cell); + + if (cell->type == "$memwr") + memindex[cell->parameters.at("\\MEMID").decode_string()].second.push_back(cell); + + if (cell->type == "$mux") + { + RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = sigmap_xmux(cell->getPort("\\B")); + + if (sig_a.is_fully_undef()) + sigmap_xmux.add(cell->getPort("\\Y"), sig_b); + else if (sig_b.is_fully_undef()) + sigmap_xmux.add(cell->getPort("\\Y"), sig_a); + } + + if (cell->type == "$mux" || cell->type == "$pmux") + { + std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y")); + for (int i = 0; i < int(sig_y.size()); i++) + sig_to_mux[sig_y[i]] = std::pair<RTLIL::Cell*, int>(cell, i); + } + } + + for (auto &it : memindex) { + std::sort(it.second.first.begin(), it.second.first.end(), memcells_cmp); + std::sort(it.second.second.begin(), it.second.second.end(), memcells_cmp); + translate_rd_feedback_to_en(it.first, it.second.first, it.second.second); + consolidate_wr_by_addr(it.first, it.second.second); + } + + cone_ct.setup_internals(); + cone_ct.cell_types.erase("$mul"); + cone_ct.cell_types.erase("$mod"); + cone_ct.cell_types.erase("$div"); + cone_ct.cell_types.erase("$pow"); + cone_ct.cell_types.erase("$shl"); + cone_ct.cell_types.erase("$shr"); + cone_ct.cell_types.erase("$sshl"); + cone_ct.cell_types.erase("$sshr"); + cone_ct.cell_types.erase("$shift"); + cone_ct.cell_types.erase("$shiftx"); + + modwalker.setup(design, module, &cone_ct); + + for (auto &it : memindex) + consolidate_wr_using_sat(it.first, it.second.second); + } +}; + +struct MemorySharePass : public Pass { + MemorySharePass() : Pass("memory_share", "consolidate memory ports") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" memory_share [selection]\n"); + log("\n"); + log("This pass merges share-able memory ports into single memory ports.\n"); + log("\n"); + log("The following methods are used to consolidate the number of memory ports:\n"); + log("\n"); + log(" - When write ports are connected to async read ports accessing the same\n"); + log(" address, then this feedback path is converted to a write port with\n"); + log(" byte/part enable signals.\n"); + log("\n"); + log(" - When multiple write ports access the same address then this is converted\n"); + log(" to a single write port with a more complex data and/or enable logic path.\n"); + log("\n"); + log(" - When multiple write ports are never accessed at the same time (a SAT\n"); + log(" solver is used to determine this), then the ports are merged into a single\n"); + log(" write port.\n"); + log("\n"); + log("Note that in addition to the algorithms implemented in this pass, the $memrd\n"); + log("and $memwr cells are also subject to generic resource sharing passes (and other\n"); + log("optimizations) such as opt_share.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + log_header("Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n"); + extra_args(args, 1, design); + for (auto module : design->selected_modules()) + MemoryShareWorker(design, module); + } +} MemorySharePass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index 782c0cd79..5a4c4eac9 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -22,7 +22,6 @@ #include <sstream> #include <algorithm> #include <stdlib.h> -#include <assert.h> static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) { @@ -32,7 +31,7 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) RTLIL::IdString mem_name = RTLIL::escape_id(memory->parameters.at("\\MEMID").decode_string()); while (module->memories.count(mem_name) != 0) - mem_name += stringf("_%d", RTLIL::autoidx++); + mem_name = mem_name.str() + stringf("_%d", autoidx++); RTLIL::Memory *mem = new RTLIL::Memory; mem->name = mem_name; @@ -47,51 +46,44 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) for (int i = 0; i < num_rd_ports; i++) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$memrd"; - cell->parameters["\\MEMID"] = mem_name; + RTLIL::Cell *cell = module->addCell(NEW_ID, "$memrd"); + cell->parameters["\\MEMID"] = mem_name.str(); cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS"); cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH"); cell->parameters["\\CLK_ENABLE"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_ENABLE")).extract(i, 1).as_const(); cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const(); cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const(); - cell->connections["\\CLK"] = memory->connections.at("\\RD_CLK").extract(i, 1); - cell->connections["\\ADDR"] = memory->connections.at("\\RD_ADDR").extract(i*abits, abits); - cell->connections["\\DATA"] = memory->connections.at("\\RD_DATA").extract(i*mem->width, mem->width); - module->add(cell); + cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1)); + cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits)); + cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width)); } for (int i = 0; i < num_wr_ports; i++) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$memwr"; - cell->parameters["\\MEMID"] = mem_name; + RTLIL::Cell *cell = module->addCell(NEW_ID, "$memwr"); + cell->parameters["\\MEMID"] = mem_name.str(); cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS"); cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH"); cell->parameters["\\CLK_ENABLE"] = RTLIL::SigSpec(memory->parameters.at("\\WR_CLK_ENABLE")).extract(i, 1).as_const(); cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\WR_CLK_POLARITY")).extract(i, 1).as_const(); cell->parameters["\\PRIORITY"] = i; - cell->connections["\\CLK"] = memory->connections.at("\\WR_CLK").extract(i, 1); - cell->connections["\\EN"] = memory->connections.at("\\WR_EN").extract(i, 1); - cell->connections["\\ADDR"] = memory->connections.at("\\WR_ADDR").extract(i*abits, abits); - cell->connections["\\DATA"] = memory->connections.at("\\WR_DATA").extract(i*mem->width, mem->width); - module->add(cell); + cell->setPort("\\CLK", memory->getPort("\\WR_CLK").extract(i, 1)); + cell->setPort("\\EN", memory->getPort("\\WR_EN").extract(i*mem->width, mem->width)); + cell->setPort("\\ADDR", memory->getPort("\\WR_ADDR").extract(i*abits, abits)); + cell->setPort("\\DATA", memory->getPort("\\WR_DATA").extract(i*mem->width, mem->width)); } - module->cells.erase(memory->name); - delete memory; + module->remove(memory); } static void handle_module(RTLIL::Design *design, RTLIL::Module *module) { std::vector<RTLIL::IdString> memcells; - for (auto &cell_it : module->cells) + for (auto &cell_it : module->cells_) if (cell_it.second->type == "$mem" && design->selected(module, cell_it.second)) memcells.push_back(cell_it.first); for (auto &it : memcells) - handle_memory(module, module->cells.at(it)); + handle_memory(module, module->cells_.at(it)); } struct MemoryUnpackPass : public Pass { @@ -109,7 +101,7 @@ struct MemoryUnpackPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { log_header("Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) handle_module(design, mod_it.second); } diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 9dfb32c87..3a8d27f93 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -6,4 +6,6 @@ OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_const.o +OBJS += passes/opt/share.o +OBJS += passes/opt/wreduce.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 8f4725e1c..b20521d1e 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -17,43 +17,55 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/log.h" #include <stdlib.h> #include <stdio.h> -bool OPT_DID_SOMETHING; - struct OptPass : public Pass { OptPass() : Pass("opt", "perform simple optimizations") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt [-purge] [-mux_undef] [-mux_bool] [-undriven] [selection]\n"); + log(" opt [options] [selection]\n"); log("\n"); log("This pass calls all the other opt_* passes in a useful order. This performs\n"); log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_const\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); log(" opt_share -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); - log(" opt_reduce\n"); + log(" opt_reduce [-fine]\n"); + log(" opt_share\n"); + log(" opt_rmdff\n"); + log(" opt_clean [-purge]\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); + log(" while <changed design>\n"); + log("\n"); + log("When called with -fast the following script is used instead:\n"); + log("\n"); + log(" do\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); log(" opt_share\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven]\n"); - log(" while [changed design]\n"); + log(" while <changed design in opt_rmdff>\n"); + log("\n"); + log("Note: Options in square brackets (such as [-keepdc]) are passed through to\n"); + log("the opt_* commands when given to 'opt'.\n"); + log("\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string opt_clean_args; std::string opt_const_args; + std::string opt_reduce_args; + bool fast_mode = false; log_header("Executing OPT pass (performing simple optimizations).\n"); log_push(); @@ -76,32 +88,56 @@ struct OptPass : public Pass { opt_const_args += " -undriven"; continue; } + if (args[argidx] == "-fine") { + opt_const_args += " -fine"; + opt_reduce_args += " -fine"; + continue; + } + if (args[argidx] == "-keepdc") { + opt_const_args += " -keepdc"; + continue; + } + if (args[argidx] == "-fast") { + fast_mode = true; + continue; + } break; } extra_args(args, argidx, design); - log_header("Optimizing in-memory representation of design.\n"); - design->optimize(); - - Pass::call(design, "opt_const"); - Pass::call(design, "opt_share -nomux"); - while (1) { - OPT_DID_SOMETHING = false; - Pass::call(design, "opt_muxtree"); - Pass::call(design, "opt_reduce"); - Pass::call(design, "opt_share"); - Pass::call(design, "opt_rmdff"); + if (fast_mode) + { + while (1) { + Pass::call(design, "opt_const" + opt_const_args); + Pass::call(design, "opt_share"); + design->scratchpad_unset("opt.did_something"); + Pass::call(design, "opt_rmdff"); + if (design->scratchpad_get_bool("opt.did_something") == false) + break; + Pass::call(design, "opt_clean" + opt_clean_args); + log_header("Rerunning OPT passes. (Removed registers in this run.)\n"); + } Pass::call(design, "opt_clean" + opt_clean_args); + } + else + { Pass::call(design, "opt_const" + opt_const_args); - if (OPT_DID_SOMETHING == false) - break; - log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + Pass::call(design, "opt_share -nomux"); + while (1) { + design->scratchpad_unset("opt.did_something"); + Pass::call(design, "opt_muxtree"); + Pass::call(design, "opt_reduce" + opt_reduce_args); + Pass::call(design, "opt_share"); + Pass::call(design, "opt_rmdff"); + Pass::call(design, "opt_clean" + opt_clean_args); + Pass::call(design, "opt_const" + opt_const_args); + if (design->scratchpad_get_bool("opt.did_something") == false) + break; + log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + } } - log_header("Optimizing in-memory representation of design.\n"); - design->optimize(); - - log_header("Finished OPT passes. (There is nothing left to do.)\n"); + log_header(fast_mode ? "Finished fast OPT passes." : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 733a1cbf1..5046752f9 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -17,13 +17,11 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -35,12 +33,12 @@ static int count_rm_cells, count_rm_wires; static void rmunused_module_cells(RTLIL::Module *module, bool verbose) { SigMap assign_map(module); - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> queue, unused; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> queue, unused; SigSet<RTLIL::Cell*> wire2driver; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; - for (auto &it2 : cell->connections) { + for (auto &it2 : cell->connections()) { if (!ct.cell_input(cell->type, it2.first)) { RTLIL::SigSpec sig = it2.second; assign_map.apply(sig); @@ -52,7 +50,7 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) unused.insert(cell); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_output || wire->get_bool_attribute("\\keep")) { std::set<RTLIL::Cell*> cell_list; @@ -66,11 +64,11 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) while (queue.size() > 0) { - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> new_queue; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> new_queue; for (auto cell : queue) unused.erase(cell); for (auto cell : queue) { - for (auto &it : cell->connections) { + for (auto &it : cell->connections()) { if (!ct.cell_output(cell->type, it.first)) { std::set<RTLIL::Cell*> cell_list; RTLIL::SigSpec sig = it.second; @@ -89,10 +87,9 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) for (auto cell : unused) { if (verbose) log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str()); - OPT_DID_SOMETHING = true; - module->cells.erase(cell->name); + module->design->scratchpad_set_bool("opt.did_something", true); + module->remove(cell); count_rm_cells++; - delete cell; } } @@ -104,15 +101,10 @@ static int count_nontrivial_wire_attrs(RTLIL::Wire *w) return count; } -static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2, SigPool ®s, SigPool &conns, std::set<RTLIL::Wire*> &direct_wires) +static bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, std::set<RTLIL::Wire*> &direct_wires) { - assert(s1.width == 1); - assert(s2.width == 1); - assert(s1.chunks.size() == 1); - assert(s2.chunks.size() == 1); - - RTLIL::Wire *w1 = s1.chunks[0].wire; - RTLIL::Wire *w2 = s2.chunks[0].wire; + RTLIL::Wire *w1 = s1.wire; + RTLIL::Wire *w2 = s2.wire; if (w1 == NULL || w2 == NULL) return w2 == NULL; @@ -146,11 +138,12 @@ static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2, SigPool ® static bool check_public_name(RTLIL::IdString id) { - if (id[0] == '$') + const std::string &id_str = id.str(); + if (id_str[0] == '$') return false; - if (id.substr(0, 2) == "\\_" && (id[id.size()-1] == '_' || id.find("_[") != std::string::npos)) + if (id_str.substr(0, 2) == "\\_" && (id_str[id_str.size()-1] == '_' || id_str.find("_[") != std::string::npos)) return false; - if (id.find(".$") != std::string::npos) + if (id_str.find(".$") != std::string::npos) return false; return true; } @@ -161,54 +154,54 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool SigPool connected_signals; if (!purge_mode) - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (ct_reg.cell_known(cell->type)) - for (auto &it2 : cell->connections) + for (auto &it2 : cell->connections()) if (ct_reg.cell_output(cell->type, it2.first)) register_signals.add(it2.second); - for (auto &it2 : cell->connections) + for (auto &it2 : cell->connections()) connected_signals.add(it2.second); } SigMap assign_map(module); std::set<RTLIL::SigSpec> direct_sigs; std::set<RTLIL::Wire*> direct_wires; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (ct_all.cell_known(cell->type)) - for (auto &it2 : cell->connections) + for (auto &it2 : cell->connections()) if (ct_all.cell_output(cell->type, it2.first)) direct_sigs.insert(assign_map(it2.second)); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { if (direct_sigs.count(assign_map(it.second)) || it.second->port_input) direct_wires.insert(it.second); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) { - RTLIL::SigSpec s1 = RTLIL::SigSpec(wire, 1, i), s2 = assign_map(s1); + RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1); if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires)) assign_map.add(s1); } } - module->connections.clear(); + module->connections_.clear(); SigPool used_signals; SigPool used_signals_nodrivers; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; - for (auto &it2 : cell->connections) { + for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); used_signals.add(it2.second); if (!ct.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_id > 0) { RTLIL::SigSpec sig = RTLIL::SigSpec(wire); @@ -224,47 +217,43 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool } } - std::vector<RTLIL::Wire*> del_wires; - for (auto &it : module->wires) { - RTLIL::Wire *wire = it.second; - if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0) { + std::vector<RTLIL::Wire*> maybe_del_wires; + for (auto wire : module->wires()) + { + if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep")) { RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; assign_map.apply(s2); - if (!used_signals.check_any(s2) && wire->port_id == 0) { - del_wires.push_back(wire); + if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { + maybe_del_wires.push_back(wire); } else { - s1.expand(); - s2.expand(); - assert(s1.chunks.size() == s2.chunks.size()); + log_assert(SIZE(s1) == SIZE(s2)); RTLIL::SigSig new_conn; - for (size_t i = 0; i < s1.chunks.size(); i++) - if (s1.chunks[i] != s2.chunks[i]) { - new_conn.first.append(s1.chunks[i]); - new_conn.second.append(s2.chunks[i]); + for (int i = 0; i < SIZE(s1); i++) + if (s1[i] != s2[i]) { + new_conn.first.append_bit(s1[i]); + new_conn.second.append_bit(s2[i]); } - if (new_conn.first.width > 0) { - new_conn.first.optimize(); - new_conn.second.optimize(); + if (new_conn.first.size() > 0) { used_signals.add(new_conn.first); used_signals.add(new_conn.second); - module->connections.push_back(new_conn); + module->connect(new_conn); } } } else { if (!used_signals.check_any(RTLIL::SigSpec(wire))) - del_wires.push_back(wire); + maybe_del_wires.push_back(wire); } + RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); if (!used_signals_nodrivers.check_any(sig)) { std::string unused_bits; - sig.expand(); - for (size_t i = 0; i < sig.chunks.size(); i++) { - if (sig.chunks[i].wire == NULL) + for (int i = 0; i < SIZE(sig); i++) { + if (sig[i].wire == NULL) continue; - if (!used_signals_nodrivers.check_any(sig)) { + if (!used_signals_nodrivers.check(sig[i])) { if (!unused_bits.empty()) unused_bits += " "; - unused_bits += stringf("%zd", i); + unused_bits += stringf("%d", i); } } if (unused_bits.empty() || wire->port_id != 0) @@ -276,18 +265,22 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool } } + + std::set<RTLIL::Wire*> del_wires; + int del_wires_count = 0; - for (auto wire : del_wires) + for (auto wire : maybe_del_wires) if (!used_signals.check_any(RTLIL::SigSpec(wire))) { if (check_public_name(wire->name) && verbose) { log(" removing unused non-port wire %s.\n", wire->name.c_str()); del_wires_count++; } - module->wires.erase(wire->name); - count_rm_wires++; - delete wire; + del_wires.insert(wire); } + module->remove(del_wires); + count_rm_wires += del_wires.size();; + if (del_wires_count > 0) log(" removed %d unused temporary wires.\n", del_wires_count); } @@ -345,7 +338,7 @@ struct OptCleanPass : public Pass { ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected_whole_module(mod_it.first)) { if (design->selected(mod_it.second)) log("Skipping module %s as it is only partially selected.\n", id2cstr(mod_it.second->name)); @@ -374,10 +367,10 @@ struct CleanPass : public Pass { log("\n"); log("This is identical to 'opt_clean', but less verbose.\n"); log("\n"); - log("When commands are seperated using the ';;' token, this command will be executed\n"); + log("When commands are separated using the ';;' token, this command will be executed\n"); log("between the commands.\n"); log("\n"); - log("When commands are seperated using the ';;;' token, this command will be executed\n"); + log("When commands are separated using the ';;;' token, this command will be executed\n"); log("in -purge mode between the commands.\n"); log("\n"); } @@ -409,12 +402,12 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (design->selected_whole_module(mod_it.first) && mod_it.second->processes.size() == 0) do { - OPT_DID_SOMETHING = false; + design->scratchpad_unset("opt.did_something"); rmunused_module(mod_it.second, purge_mode, false); - } while (OPT_DID_SOMETHING); + } while (design->scratchpad_get_bool("opt.did_something")); } if (count_rm_cells > 0 || count_rm_wires > 0) diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc index 34d1a69c1..f9b78c053 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_const.cc @@ -17,19 +17,18 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" +#include "kernel/utils.h" #include "kernel/log.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> -#include <set> +#include <algorithm> static bool did_something; -void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) +static void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) { CellTypes ct(design); SigMap sigmap(module); @@ -37,95 +36,350 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) SigPool used_signals; SigPool all_signals; - for (auto &it : module->cells) - for (auto &conn : it.second->connections) { - if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first)) driven_signals.add(sigmap(conn.second)); - if (!ct.cell_known(it.second->type) || ct.cell_input(it.second->type, conn.first)) + if (!ct.cell_known(cell->type) || ct.cell_input(cell->type, conn.first)) used_signals.add(sigmap(conn.second)); } - for (auto &it : module->wires) { - if (it.second->port_input) - driven_signals.add(sigmap(it.second)); - if (it.second->port_output) - used_signals.add(sigmap(it.second)); - all_signals.add(sigmap(it.second)); + for (auto wire : module->wires()) { + if (wire->port_input) + driven_signals.add(sigmap(wire)); + if (wire->port_output) + used_signals.add(sigmap(wire)); + all_signals.add(sigmap(wire)); } all_signals.del(driven_signals); RTLIL::SigSpec undriven_signals = all_signals.export_all(); - for (auto &c : undriven_signals.chunks) + for (auto &c : undriven_signals.chunks()) { RTLIL::SigSpec sig = c; if (c.wire->name[0] == '$') sig = used_signals.extract(sig); - if (sig.width == 0) + if (sig.size() == 0) continue; log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c)); - module->connections.push_back(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width))); - OPT_DID_SOMETHING = true; + module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width))); + did_something = true; } } -void replace_cell(RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) +static void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) { - RTLIL::SigSpec Y = cell->connections[out_port]; + RTLIL::SigSpec Y = cell->getPort(out_port); + out_val.extend_u0(Y.size(), false); + log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n", cell->type.c_str(), cell->name.c_str(), info.c_str(), module->name.c_str(), log_signal(Y), log_signal(out_val)); - // ILANG_BACKEND::dump_cell(stderr, "--> ", cell); - module->connections.push_back(RTLIL::SigSig(Y, out_val)); - module->cells.erase(cell->name); - delete cell; - OPT_DID_SOMETHING = true; + // log_cell(cell); + assign_map.add(Y, out_val); + module->connect(Y, out_val); + module->remove(cell); did_something = true; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool) +static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap) +{ + std::string b_name = cell->hasPort("\\B") ? "\\B" : "\\A"; + + bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + bool b_signed = cell->parameters.at(b_name + "_SIGNED").as_bool(); + + RTLIL::SigSpec sig_a = sigmap(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = sigmap(cell->getPort(b_name)); + RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + sig_a.extend_u0(sig_y.size(), a_signed); + sig_b.extend_u0(sig_y.size(), b_signed); + + std::vector<RTLIL::SigBit> bits_a = sig_a, bits_b = sig_b, bits_y = sig_y; + + enum { GRP_DYN, GRP_CONST_A, GRP_CONST_B, GRP_CONST_AB, GRP_N }; + std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::set<RTLIL::SigBit>> grouped_bits[GRP_N]; + + for (int i = 0; i < SIZE(bits_y); i++) + { + int group_idx = GRP_DYN; + RTLIL::SigBit bit_a = bits_a[i], bit_b = bits_b[i]; + + if (cell->type == "$or" && (bit_a == RTLIL::State::S1 || bit_b == RTLIL::State::S1)) + bit_a = bit_b = RTLIL::State::S1; + + if (cell->type == "$and" && (bit_a == RTLIL::State::S0 || bit_b == RTLIL::State::S0)) + bit_a = bit_b = RTLIL::State::S0; + + if (bit_a.wire == NULL && bit_b.wire == NULL) + group_idx = GRP_CONST_AB; + else if (bit_a.wire == NULL) + group_idx = GRP_CONST_A; + else if (bit_b.wire == NULL && commutative) + group_idx = GRP_CONST_A, std::swap(bit_a, bit_b); + else if (bit_b.wire == NULL) + group_idx = GRP_CONST_B; + + grouped_bits[group_idx][std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit_a, bit_b)].insert(bits_y[i]); + } + + for (int i = 0; i < GRP_N; i++) + if (SIZE(grouped_bits[i]) == SIZE(bits_y)) + return false; + + log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n", + log_id(cell->type), log_id(cell), log_id(module)); + + for (int i = 0; i < GRP_N; i++) + { + if (grouped_bits[i].empty()) + continue; + + RTLIL::Wire *new_y = module->addWire(NEW_ID, SIZE(grouped_bits[i])); + RTLIL::SigSpec new_a, new_b; + RTLIL::SigSig new_conn; + + for (auto &it : grouped_bits[i]) { + for (auto &bit : it.second) { + new_conn.first.append_bit(bit); + new_conn.second.append_bit(RTLIL::SigBit(new_y, new_a.size())); + } + new_a.append_bit(it.first.first); + new_b.append_bit(it.first.second); + } + + RTLIL::Cell *c = module->addCell(NEW_ID, cell->type); + + c->setPort("\\A", new_a); + c->parameters["\\A_WIDTH"] = new_a.size(); + c->parameters["\\A_SIGNED"] = false; + + if (b_name == "\\B") { + c->setPort("\\B", new_b); + c->parameters["\\B_WIDTH"] = new_b.size(); + c->parameters["\\B_SIGNED"] = false; + } + + c->setPort("\\Y", new_y); + c->parameters["\\Y_WIDTH"] = new_y->width; + c->check(); + + module->connect(new_conn); + + log(" New cell `%s': A=%s", log_id(c), log_signal(new_a)); + if (b_name == "\\B") + log(", B=%s", log_signal(new_b)); + log("\n"); + } + + cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); + + module->remove(cell); + did_something = true; + return true; +} + +static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc) { if (!design->selected(module)) return; + CellTypes ct_combinational; + ct_combinational.setup_internals(); + ct_combinational.setup_stdcells(); + SigMap assign_map(module); std::map<RTLIL::SigSpec, RTLIL::SigSpec> invert_map; - std::vector<RTLIL::Cell*> cells; - cells.reserve(module->cells.size()); - for (auto &cell_it : module->cells) - if (design->selected(module, cell_it.second)) { - if ((cell_it.second->type == "$_INV_" || cell_it.second->type == "$not" || cell_it.second->type == "$logic_not") && - cell_it.second->connections["\\A"].width == 1 && cell_it.second->connections["\\Y"].width == 1) - invert_map[assign_map(cell_it.second->connections["\\Y"])] = assign_map(cell_it.second->connections["\\A"]); - cells.push_back(cell_it.second); + TopoSort<RTLIL::Cell*> cells; + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; + + for (auto cell : module->cells()) + if (design->selected(module, cell) && cell->type[0] == '$') { + if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && + cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1) + invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A")); + if (ct_combinational.cell_known(cell->type)) + for (auto &conn : cell->connections()) { + RTLIL::SigSpec sig = assign_map(conn.second); + sig.remove_const(); + if (ct_combinational.cell_input(cell->type, conn.first)) + cell_to_inbit[cell].insert(sig.begin(), sig.end()); + if (ct_combinational.cell_output(cell->type, conn.first)) + for (auto &bit : sig) + outbit_to_cell[bit].insert(cell); + } + cells.node(cell); } - for (auto cell : cells) + for (auto &it_right : cell_to_inbit) + for (auto &it_sigbit : it_right.second) + for (auto &it_left : outbit_to_cell[it_sigbit]) + cells.edge(it_left, it_right.first); + + cells.sort(); + + for (auto cell : cells.sorted) { -#define ACTION_DO(_p_, _s_) do { replace_cell(module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) - if ((cell->type == "$_INV_" || cell->type == "$not" || cell->type == "$logic_not") && - invert_map.count(assign_map(cell->connections["\\A"])) != 0) { - replace_cell(module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->connections["\\A"]))); + if (do_fine) + { + if (cell->type == "$not" || cell->type == "$pos" || + cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor") + if (group_cell_inputs(module, cell, true, assign_map)) + goto next_cell; + + if (cell->type == "$reduce_and") + { + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + + RTLIL::State new_a = RTLIL::State::S1; + for (auto &bit : sig_a.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) { + if (new_a == RTLIL::State::S1) + new_a = RTLIL::State::Sx; + } else if (bit == RTLIL::State::S0) { + new_a = RTLIL::State::S0; + break; + } else if (bit.wire != NULL) { + new_a = RTLIL::State::Sm; + } + + if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { + cover("opt.opt_const.fine.$reduce_and"); + log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); + cell->setPort("\\A", sig_a = new_a); + cell->parameters.at("\\A_WIDTH") = 1; + did_something = true; + } + } + + if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$reduce_or" || cell->type == "$reduce_bool") + { + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + + RTLIL::State new_a = RTLIL::State::S0; + for (auto &bit : sig_a.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) { + if (new_a == RTLIL::State::S0) + new_a = RTLIL::State::Sx; + } else if (bit == RTLIL::State::S1) { + new_a = RTLIL::State::S1; + break; + } else if (bit.wire != NULL) { + new_a = RTLIL::State::Sm; + } + + if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { + cover_list("opt.opt_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); + log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); + cell->setPort("\\A", sig_a = new_a); + cell->parameters.at("\\A_WIDTH") = 1; + did_something = true; + } + } + + if (cell->type == "$logic_and" || cell->type == "$logic_or") + { + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + + RTLIL::State new_b = RTLIL::State::S0; + for (auto &bit : sig_b.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) { + if (new_b == RTLIL::State::S0) + new_b = RTLIL::State::Sx; + } else if (bit == RTLIL::State::S1) { + new_b = RTLIL::State::S1; + break; + } else if (bit.wire != NULL) { + new_b = RTLIL::State::Sm; + } + + if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) { + cover_list("opt.opt_const.fine.B", "$logic_and", "$logic_or", cell->type.str()); + log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b)); + cell->setPort("\\B", sig_b = new_b); + cell->parameters.at("\\B_WIDTH") = 1; + did_something = true; + } + } + } + + if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) { + cover("opt.opt_const.one_high"); + replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1); goto next_cell; } - if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->connections["\\S"])) != 0) { - RTLIL::SigSpec tmp = cell->connections["\\A"]; - cell->connections["\\A"] = cell->connections["\\B"]; - cell->connections["\\B"] = tmp; - cell->connections["\\S"] = invert_map.at(assign_map(cell->connections["\\S"])); - OPT_DID_SOMETHING = true; + if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) { + cover("opt.opt_const.one_low"); + replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0); + goto next_cell; + } + + if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" || + cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || + cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" || + cell->type == "$neg" || cell->type == "$add" || cell->type == "$sub" || + cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod" || cell->type == "$pow") + { + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = cell->hasPort("\\B") ? assign_map(cell->getPort("\\B")) : RTLIL::SigSpec(); + + if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx") + sig_a = RTLIL::SigSpec(); + + for (auto &bit : sig_a.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) + goto found_the_x_bit; + + for (auto &bit : sig_b.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) + goto found_the_x_bit; + + if (0) { + found_the_x_bit: + cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str()); + if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || + cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt") + replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::State::Sx); + else + replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::SigSpec(RTLIL::State::Sx, cell->getPort("\\Y").size())); + goto next_cell; + } + } + + if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 && + invert_map.count(assign_map(cell->getPort("\\A"))) != 0) { + cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); + replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A")))); + goto next_cell; + } + + if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) { + cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); + log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module)); + RTLIL::SigSpec tmp = cell->getPort("\\A"); + cell->setPort("\\A", cell->getPort("\\B")); + cell->setPort("\\B", tmp); + cell->setPort("\\S", invert_map.at(assign_map(cell->getPort("\\S")))); did_something = true; goto next_cell; } - if (cell->type == "$_INV_") { - RTLIL::SigSpec input = cell->connections["\\A"]; + if (cell->type == "$_NOT_") { + RTLIL::SigSpec input = cell->getPort("\\A"); assign_map.apply(input); if (input.match("1")) ACTION_DO_Y(0); if (input.match("0")) ACTION_DO_Y(1); @@ -134,8 +388,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_AND_") { RTLIL::SigSpec input; - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.match(" 0")) ACTION_DO_Y(0); if (input.match("0 ")) ACTION_DO_Y(0); @@ -153,8 +407,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_OR_") { RTLIL::SigSpec input; - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.match(" 1")) ACTION_DO_Y(1); if (input.match("1 ")) ACTION_DO_Y(1); @@ -172,8 +426,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_XOR_") { RTLIL::SigSpec input; - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.match("00")) ACTION_DO_Y(0); if (input.match("01")) ACTION_DO_Y(1); @@ -187,9 +441,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_MUX_") { RTLIL::SigSpec input; - input.append(cell->connections["\\S"]); - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\S")); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.extract(2, 1) == input.extract(1, 1)) ACTION_DO("\\Y", input.extract(2, 1)); @@ -197,10 +451,11 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); if (input.match("10 ")) { - cell->type = "$_INV_"; - cell->connections["\\A"] = input.extract(0, 1); - cell->connections.erase("\\B"); - cell->connections.erase("\\S"); + cover("opt.opt_const.mux_to_inv"); + cell->type = "$_NOT_"; + cell->setPort("\\A", input.extract(0, 1)); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); goto next_cell; } if (input.match("11 ")) ACTION_DO_Y(1); @@ -217,8 +472,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex") { - RTLIL::SigSpec a = cell->connections["\\A"]; - RTLIL::SigSpec b = cell->connections["\\B"]; + RTLIL::SigSpec a = cell->getPort("\\A"); + RTLIL::SigSpec b = cell->getPort("\\B"); if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); @@ -227,78 +482,189 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } RTLIL::SigSpec new_a, new_b; - a.expand(), b.expand(); - assert(a.chunks.size() == b.chunks.size()); - for (size_t i = 0; i < a.chunks.size(); i++) { - if (a.chunks[i].wire == NULL && b.chunks[i].wire == NULL && a.chunks[i].data.bits[0] != b.chunks[i].data.bits[0] && - a.chunks[i].data.bits[0] <= RTLIL::State::S1 && b.chunks[i].data.bits[0] <= RTLIL::State::S1) { + log_assert(SIZE(a) == SIZE(b)); + for (int i = 0; i < SIZE(a); i++) { + if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) { + cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1); new_y.extend(cell->parameters["\\Y_WIDTH"].as_int(), false); - replace_cell(module, cell, "empty", "\\Y", new_y); + replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); goto next_cell; } - if (a.chunks[i] == b.chunks[i]) + if (a[i] == b[i]) continue; - new_a.append(a.chunks[i]); - new_b.append(b.chunks[i]); + new_a.append(a[i]); + new_b.append(b[i]); } - if (new_a.width == 0) { + if (new_a.size() == 0) { + cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0); new_y.extend(cell->parameters["\\Y_WIDTH"].as_int(), false); - replace_cell(module, cell, "empty", "\\Y", new_y); + replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); goto next_cell; } - if (new_a.width < a.width || new_b.width < b.width) { - new_a.optimize(); - new_b.optimize(); - cell->connections["\\A"] = new_a; - cell->connections["\\B"] = new_b; - cell->parameters["\\A_WIDTH"] = new_a.width; - cell->parameters["\\B_WIDTH"] = new_b.width; + if (new_a.size() < a.size() || new_b.size() < b.size()) { + cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cell->setPort("\\A", new_a); + cell->setPort("\\B", new_b); + cell->parameters["\\A_WIDTH"] = new_a.size(); + cell->parameters["\\B_WIDTH"] = new_b.size(); } } if ((cell->type == "$eq" || cell->type == "$ne") && cell->parameters["\\Y_WIDTH"].as_int() == 1 && cell->parameters["\\A_WIDTH"].as_int() == 1 && cell->parameters["\\B_WIDTH"].as_int() == 1) { - RTLIL::SigSpec a = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec b = assign_map(cell->connections["\\B"]); + RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); if (a.is_fully_const()) { - RTLIL::SigSpec tmp; - tmp = a, a = b, b = tmp; - cell->connections["\\A"] = a; - cell->connections["\\B"] = b; + cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str()); + RTLIL::SigSpec tmp = cell->getPort("\\A"); + cell->setPort("\\A", cell->getPort("\\B")); + cell->setPort("\\B", tmp); } if (b.is_fully_const()) { if (b.as_bool() == (cell->type == "$eq")) { RTLIL::SigSpec input = b; - ACTION_DO("\\Y", cell->connections["\\A"]); + ACTION_DO("\\Y", cell->getPort("\\A")); } else { + cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->type = "$not"; cell->parameters.erase("\\B_WIDTH"); cell->parameters.erase("\\B_SIGNED"); - cell->connections.erase("\\B"); + cell->unsetPort("\\B"); + did_something = true; + } + goto next_cell; + } + } + + if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const()) + { + bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool(); + int shift_bits = assign_map(cell->getPort("\\B")).as_int(cell->type.in("$shift", "$shiftx") && cell->getParam("\\B_SIGNED").as_bool()); + + if (cell->type.in("$shl", "$sshl")) + shift_bits *= -1; + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_y(cell->type == "$shiftx" ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam("\\Y_WIDTH").as_int()); + + if (SIZE(sig_a) < SIZE(sig_y)) + sig_a.extend(SIZE(sig_y), cell->getParam("\\A_SIGNED").as_bool()); + + for (int i = 0; i < SIZE(sig_y); i++) { + int idx = i + shift_bits; + if (0 <= idx && idx < SIZE(sig_a)) + sig_y[i] = sig_a[idx]; + else if (SIZE(sig_a) <= idx && sign_ext) + sig_y[i] = sig_a[SIZE(sig_a)-1]; + } + + cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); + + log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n", + log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y)); + + module->connect(cell->getPort("\\Y"), sig_y); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + if (!keepdc) + { + bool identity_wrt_a = false; + bool identity_wrt_b = false; + + if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor") + { + RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (cell->type != "$sub" && a.is_fully_const() && a.as_bool() == false) + identity_wrt_b = true; + + if (b.is_fully_const() && b.as_bool() == false) + identity_wrt_a = true; + } + + if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx") + { + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (b.is_fully_const() && b.as_bool() == false) + identity_wrt_a = true; + } + + if (cell->type == "$mul") + { + RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1) + identity_wrt_b = true; + + if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + identity_wrt_a = true; + } + + if (cell->type == "$div") + { + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + identity_wrt_a = true; + } + + if (identity_wrt_a || identity_wrt_b) + { + if (identity_wrt_a) + cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + if (identity_wrt_b) + cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + + log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); + + if (!identity_wrt_a) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH"); + cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } + + cell->type = "$pos"; + cell->unsetPort("\\B"); + cell->parameters.erase("\\B_WIDTH"); + cell->parameters.erase("\\B_SIGNED"); + cell->check(); + + did_something = true; goto next_cell; } } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && - cell->connections["\\A"] == RTLIL::SigSpec(0, 1) && cell->connections["\\B"] == RTLIL::SigSpec(1, 1)) { - replace_cell(module, cell, "mux_bool", "\\Y", cell->connections["\\S"]); + cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { + cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S")); goto next_cell; } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && - cell->connections["\\A"] == RTLIL::SigSpec(1, 1) && cell->connections["\\B"] == RTLIL::SigSpec(0, 1)) { - cell->connections["\\A"] = cell->connections["\\S"]; - cell->connections.erase("\\B"); - cell->connections.erase("\\S"); + cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) { + cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\A", cell->getPort("\\S")); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); if (cell->type == "$mux") { cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; cell->parameters["\\Y_WIDTH"] = cell->parameters["\\WIDTH"]; @@ -306,15 +672,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.erase("\\WIDTH"); cell->type = "$not"; } else - cell->type = "$_INV_"; - OPT_DID_SOMETHING = true; + cell->type = "$_NOT_"; did_something = true; goto next_cell; } - if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->connections["\\A"] == RTLIL::SigSpec(0, 1)) { - cell->connections["\\A"] = cell->connections["\\S"]; - cell->connections.erase("\\S"); + if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) { + cover_list("opt.opt_const.mux_and", "$mux", "$_MUX_", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\A", cell->getPort("\\S")); + cell->unsetPort("\\S"); if (cell->type == "$mux") { cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; cell->parameters["\\B_WIDTH"] = cell->parameters["\\WIDTH"]; @@ -325,14 +692,15 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->type = "$and"; } else cell->type = "$_AND_"; - OPT_DID_SOMETHING = true; did_something = true; goto next_cell; } - if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->connections["\\B"] == RTLIL::SigSpec(1, 1)) { - cell->connections["\\B"] = cell->connections["\\S"]; - cell->connections.erase("\\S"); + if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { + cover_list("opt.opt_const.mux_or", "$mux", "$_MUX_", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\B", cell->getPort("\\S")); + cell->unsetPort("\\S"); if (cell->type == "$mux") { cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; cell->parameters["\\B_WIDTH"] = cell->parameters["\\WIDTH"]; @@ -342,87 +710,88 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.erase("\\WIDTH"); cell->type = "$or"; } else - cell->type = "$_or_"; - OPT_DID_SOMETHING = true; + cell->type = "$_OR_"; did_something = true; goto next_cell; } if (mux_undef && (cell->type == "$mux" || cell->type == "$pmux")) { RTLIL::SigSpec new_a, new_b, new_s; - int width = cell->connections.at("\\A").width; - if ((cell->connections.at("\\A").is_fully_undef() && cell->connections.at("\\B").is_fully_undef()) || - cell->connections.at("\\S").is_fully_undef()) { - replace_cell(module, cell, "mux undef", "\\Y", cell->connections.at("\\A")); + int width = cell->getPort("\\A").size(); + if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) || + cell->getPort("\\S").is_fully_undef()) { + cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A")); goto next_cell; } - for (int i = 0; i < cell->connections.at("\\S").width; i++) { - RTLIL::SigSpec old_b = cell->connections.at("\\B").extract(i*width, width); - RTLIL::SigSpec old_s = cell->connections.at("\\S").extract(i, 1); + for (int i = 0; i < cell->getPort("\\S").size(); i++) { + RTLIL::SigSpec old_b = cell->getPort("\\B").extract(i*width, width); + RTLIL::SigSpec old_s = cell->getPort("\\S").extract(i, 1); if (old_b.is_fully_undef() || old_s.is_fully_undef()) continue; new_b.append(old_b); new_s.append(old_s); } - new_a = cell->connections.at("\\A"); - if (new_a.is_fully_undef() && new_s.width > 0) { - new_a = new_b.extract((new_s.width-1)*width, width); - new_b = new_b.extract(0, (new_s.width-1)*width); - new_s = new_s.extract(0, new_s.width-1); + new_a = cell->getPort("\\A"); + if (new_a.is_fully_undef() && new_s.size() > 0) { + new_a = new_b.extract((new_s.size()-1)*width, width); + new_b = new_b.extract(0, (new_s.size()-1)*width); + new_s = new_s.extract(0, new_s.size()-1); } - if (new_s.width == 0) { - replace_cell(module, cell, "mux undef", "\\Y", new_a); + if (new_s.size() == 0) { + cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a); goto next_cell; } if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) { - replace_cell(module, cell, "mux undef", "\\Y", new_s); + cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s); goto next_cell; } - if (cell->connections.at("\\S").width != new_s.width) { - cell->connections.at("\\A") = new_a; - cell->connections.at("\\B") = new_b; - cell->connections.at("\\S") = new_s; - if (new_s.width > 1) { + if (cell->getPort("\\S").size() != new_s.size()) { + cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str()); + log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n", + SIZE(cell->getPort("\\S")) - SIZE(new_s), log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\A", new_a); + cell->setPort("\\B", new_b); + cell->setPort("\\S", new_s); + if (new_s.size() > 1) { cell->type = "$pmux"; - cell->parameters["\\S_WIDTH"] = new_s.width; + cell->parameters["\\S_WIDTH"] = new_s.size(); } else { cell->type = "$mux"; cell->parameters.erase("\\S_WIDTH"); } - OPT_DID_SOMETHING = true; did_something = true; } } #define FOLD_1ARG_CELL(_t) \ if (cell->type == "$" #_t) { \ - RTLIL::SigSpec a = cell->connections["\\A"]; \ + RTLIL::SigSpec a = cell->getPort("\\A"); \ assign_map.apply(a); \ if (a.is_fully_const()) { \ - a.optimize(); \ - if (a.chunks.empty()) a.chunks.push_back(RTLIL::SigChunk()); \ RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \ - RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, dummy_arg, \ + RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \ cell->parameters["\\A_SIGNED"].as_bool(), false, \ cell->parameters["\\Y_WIDTH"].as_int())); \ - replace_cell(module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ + cover("opt.opt_const.const.$" #_t); \ + replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ goto next_cell; \ } \ } #define FOLD_2ARG_CELL(_t) \ if (cell->type == "$" #_t) { \ - RTLIL::SigSpec a = cell->connections["\\A"]; \ - RTLIL::SigSpec b = cell->connections["\\B"]; \ + RTLIL::SigSpec a = cell->getPort("\\A"); \ + RTLIL::SigSpec b = cell->getPort("\\B"); \ assign_map.apply(a), assign_map.apply(b); \ if (a.is_fully_const() && b.is_fully_const()) { \ - a.optimize(), b.optimize(); \ - if (a.chunks.empty()) a.chunks.push_back(RTLIL::SigChunk()); \ - if (b.chunks.empty()) b.chunks.push_back(RTLIL::SigChunk()); \ - RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, b.chunks[0].data, \ + RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), \ cell->parameters["\\A_SIGNED"].as_bool(), \ cell->parameters["\\B_SIGNED"].as_bool(), \ cell->parameters["\\Y_WIDTH"].as_int())); \ - replace_cell(module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ + cover("opt.opt_const.const.$" #_t); \ + replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ goto next_cell; \ } \ } @@ -447,6 +816,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons FOLD_2ARG_CELL(shr) FOLD_2ARG_CELL(sshl) FOLD_2ARG_CELL(sshr) + FOLD_2ARG_CELL(shift) + FOLD_2ARG_CELL(shiftx) FOLD_2ARG_CELL(lt) FOLD_2ARG_CELL(le) @@ -467,13 +838,78 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons // be very conservative with optimizing $mux cells as we do not want to break mux trees if (cell->type == "$mux") { - RTLIL::SigSpec input = assign_map(cell->connections["\\S"]); - RTLIL::SigSpec inA = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec inB = assign_map(cell->connections["\\B"]); + RTLIL::SigSpec input = assign_map(cell->getPort("\\S")); + RTLIL::SigSpec inA = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec inB = assign_map(cell->getPort("\\B")); if (input.is_fully_const()) - ACTION_DO("\\Y", input.as_bool() ? cell->connections["\\B"] : cell->connections["\\A"]); + ACTION_DO("\\Y", input.as_bool() ? cell->getPort("\\B") : cell->getPort("\\A")); else if (inA == inB) - ACTION_DO("\\Y", cell->connections["\\A"]); + ACTION_DO("\\Y", cell->getPort("\\A")); + } + + if (!keepdc && cell->type == "$mul") + { + bool a_signed = cell->parameters["\\A_SIGNED"].as_bool(); + bool b_signed = cell->parameters["\\B_SIGNED"].as_bool(); + bool swapped_ab = false; + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + if (sig_b.is_fully_const() && sig_b.size() <= 32) + std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true; + + if (sig_a.is_fully_def() && sig_a.size() <= 32) + { + int a_val = sig_a.as_int(); + + if (a_val == 0) + { + cover("opt.opt_const.mul_shift.zero"); + + log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n", + cell->name.c_str(), module->name.c_str()); + + module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + for (int i = 1; i < (a_signed ? sig_a.size()-1 : sig_a.size()); i++) + if (a_val == (1 << i)) + { + if (swapped_ab) + cover("opt.opt_const.mul_shift.swapped"); + else + cover("opt.opt_const.mul_shift.unswapped"); + + log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n", + a_val, cell->name.c_str(), module->name.c_str(), i); + + if (!swapped_ab) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->parameters["\\A_WIDTH"] = cell->parameters["\\B_WIDTH"]; + cell->parameters["\\A_SIGNED"] = cell->parameters["\\B_SIGNED"]; + } + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + + while (SIZE(new_b) > 1 && new_b.back() == RTLIL::State::S0) + new_b.pop_back(); + + cell->type = "$shl"; + cell->parameters["\\B_WIDTH"] = SIZE(new_b); + cell->parameters["\\B_SIGNED"] = false; + cell->setPort("\\B", new_b); + cell->check(); + + did_something = true; + goto next_cell; + } + } } next_cell:; @@ -503,12 +939,23 @@ struct OptConstPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); + log(" -keepdc\n"); + log(" some optimizations change the behavior of the circuit with respect to\n"); + log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n"); + log(" all result bits to be set to x. this behavior changes when 'a+0' is\n"); + log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); + log("\n"); + log(" -fine\n"); + log(" perform fine-grain optimizations\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool mux_undef = false; bool mux_bool = false; bool undriven = false; + bool do_fine = false; + bool keepdc = false; log_header("Executing OPT_CONST pass (perform const folding).\n"); log_push(); @@ -527,21 +974,31 @@ struct OptConstPass : public Pass { undriven = true; continue; } + if (args[argidx] == "-fine") { + do_fine = true; + continue; + } + if (args[argidx] == "-keepdc") { + keepdc = true; + continue; + } break; } extra_args(args, argidx, design); - for (auto &mod_it : design->modules) + for (auto module : design->modules()) { if (undriven) - replace_undriven(design, mod_it.second); + replace_undriven(design, module); do { do { did_something = false; - replace_const_cells(design, mod_it.second, false, mux_undef, mux_bool); + replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc); + if (did_something) + design->scratchpad_set_bool("opt.did_something", true); } while (did_something); - replace_const_cells(design, mod_it.second, true, mux_undef, mux_bool); + replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc); } while (did_something); } diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 47100869c..2c5dcf668 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -17,13 +17,11 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -36,7 +34,11 @@ struct OptMuxtreeWorker SigMap assign_map; int removed_count; - typedef std::pair<RTLIL::Wire*,int> bitDef_t; + struct bitDef_t : public std::pair<RTLIL::Wire*, int> { + bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } + bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { } + }; + struct bitinfo_t { int num; @@ -79,21 +81,20 @@ struct OptMuxtreeWorker // .ctrl_sigs // .input_sigs // .const_activated - for (auto &cell_it : module->cells) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = cell_it.second; - if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux") + if (cell->type == "$mux" || cell->type == "$pmux") { - RTLIL::SigSpec sig_a = cell->connections["\\A"]; - RTLIL::SigSpec sig_b = cell->connections["\\B"]; - RTLIL::SigSpec sig_s = cell->connections["\\S"]; - RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_s = cell->getPort("\\S"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); muxinfo_t muxinfo; muxinfo.cell = cell; - for (int i = 0; i < sig_s.width; i++) { - RTLIL::SigSpec sig = sig_b.extract(i*sig_a.width, sig_a.width); + for (int i = 0; i < sig_s.size(); i++) { + RTLIL::SigSpec sig = sig_b.extract(i*sig_a.size(), sig_a.size()); RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1)); portinfo_t portinfo; for (int idx : sig2bits(sig)) { @@ -126,15 +127,15 @@ struct OptMuxtreeWorker } else { - for (auto &it : cell->connections) { + for (auto &it : cell->connections()) { for (int idx : sig2bits(it.second)) bit2info[idx].seen_non_mux = true; } } } - for (auto &it : module->wires) { - if (it.second->port_output) - for (int idx : sig2bits(RTLIL::SigSpec(it.second))) + for (auto wire : module->wires()) { + if (wire->port_output) + for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } @@ -177,7 +178,6 @@ struct OptMuxtreeWorker } else { log(" dead port %zd/%zd on %s %s.\n", port_idx+1, mi.ports.size(), mi.cell->type.c_str(), mi.cell->name.c_str()); - OPT_DID_SOMETHING = true; removed_count++; } } @@ -186,32 +186,30 @@ struct OptMuxtreeWorker continue; if (live_ports.size() == 0) { - module->cells.erase(mi.cell->name); - delete mi.cell; + module->remove(mi.cell); continue; } - RTLIL::SigSpec sig_a = mi.cell->connections["\\A"]; - RTLIL::SigSpec sig_b = mi.cell->connections["\\B"]; - RTLIL::SigSpec sig_s = mi.cell->connections["\\S"]; - RTLIL::SigSpec sig_y = mi.cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = mi.cell->getPort("\\A"); + RTLIL::SigSpec sig_b = mi.cell->getPort("\\B"); + RTLIL::SigSpec sig_s = mi.cell->getPort("\\S"); + RTLIL::SigSpec sig_y = mi.cell->getPort("\\Y"); RTLIL::SigSpec sig_ports = sig_b; sig_ports.append(sig_a); if (live_ports.size() == 1) { - RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.width, sig_a.width); - module->connections.push_back(RTLIL::SigSig(sig_y, sig_in)); - module->cells.erase(mi.cell->name); - delete mi.cell; + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.size(), sig_a.size()); + module->connect(RTLIL::SigSig(sig_y, sig_in)); + module->remove(mi.cell); } else { RTLIL::SigSpec new_sig_a, new_sig_b, new_sig_s; for (size_t i = 0; i < live_ports.size(); i++) { - RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.width, sig_a.width); + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.size(), sig_a.size()); if (i == live_ports.size()-1) { new_sig_a = sig_in; } else { @@ -220,14 +218,14 @@ struct OptMuxtreeWorker } } - mi.cell->connections["\\A"] = new_sig_a; - mi.cell->connections["\\B"] = new_sig_b; - mi.cell->connections["\\S"] = new_sig_s; - if (new_sig_s.width == 1) { + mi.cell->setPort("\\A", new_sig_a); + mi.cell->setPort("\\B", new_sig_b); + mi.cell->setPort("\\S", new_sig_s); + if (new_sig_s.size() == 1) { mi.cell->type = "$mux"; mi.cell->parameters.erase("\\S_WIDTH"); } else { - mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width); + mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size()); } } } @@ -259,10 +257,8 @@ struct OptMuxtreeWorker { std::vector<int> results; assign_map.apply(sig); - sig.expand(); - for (auto &c : sig.chunks) - if (c.wire != NULL) { - bitDef_t bit(c.wire, c.offset); + for (auto &bit : sig) + if (bit.wire != NULL) { if (bit2num.count(bit) == 0) { bitinfo_t info; info.num = bit2info.size(); @@ -309,13 +305,17 @@ struct OptMuxtreeWorker if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) knowledge.known_active.push_back(muxinfo.ports[port_idx].ctrl_sigs); + std::vector<int> parent_muxes; for (int m : muxinfo.ports[port_idx].input_muxes) { if (knowledge.visited_muxes.count(m) > 0) continue; knowledge.visited_muxes.insert(m); + parent_muxes.push_back(m); + } + for (int m : parent_muxes) eval_mux(knowledge, m); + for (int m : parent_muxes) knowledge.visited_muxes.erase(m); - } if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) knowledge.known_active.pop_back(); @@ -393,6 +393,7 @@ struct OptMuxtreeWorker void eval_root_mux(int mux_idx) { knowledge_t knowledge; + knowledge.visited_muxes.insert(mux_idx); eval_mux(knowledge, mux_idx); } }; @@ -418,19 +419,21 @@ struct OptMuxtreePass : public Pass { extra_args(args, 1, design); int total_count = 0; - for (auto &mod_it : design->modules) { - if (!design->selected_whole_module(mod_it.first)) { - if (design->selected(mod_it.second)) - log("Skipping module %s as it is only partially selected.\n", id2cstr(mod_it.second->name)); + for (auto mod : design->modules()) { + if (!design->selected_whole_module(mod)) { + if (design->selected(mod)) + log("Skipping module %s as it is only partially selected.\n", log_id(mod)); continue; } - if (mod_it.second->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", id2cstr(mod_it.second->name)); + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); } else { - OptMuxtreeWorker worker(design, mod_it.second); + OptMuxtreeWorker worker(design, mod); total_count += worker.removed_count; } } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Removed %d multiplexer ports.\n", total_count); } } OptMuxtreePass; diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index dd1299810..e9e2bb399 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -17,14 +17,11 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" -#include "libs/sha1/sha1.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -43,97 +40,95 @@ struct OptReduceWorker return; cells.erase(cell); - RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); - sig_a.sort_and_unify(); - sig_a.expand(); + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + std::set<RTLIL::SigBit> new_sig_a_bits; - RTLIL::SigSpec new_sig_a; - for (auto &chunk : sig_a.chunks) + for (auto &bit : sig_a.to_sigbit_set()) { - if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S0) { + if (bit == RTLIL::State::S0) { if (cell->type == "$reduce_and") { - new_sig_a = RTLIL::SigSpec(RTLIL::State::S0); + new_sig_a_bits.clear(); + new_sig_a_bits.insert(RTLIL::State::S0); break; } continue; } - if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S1) { + if (bit == RTLIL::State::S1) { if (cell->type == "$reduce_or") { - new_sig_a = RTLIL::SigSpec(RTLIL::State::S1); + new_sig_a_bits.clear(); + new_sig_a_bits.insert(RTLIL::State::S1); break; } continue; } - if (chunk.wire == NULL) { - new_sig_a = RTLIL::SigSpec(RTLIL::State::Sx); - break; + if (bit.wire == NULL) { + new_sig_a_bits.insert(bit); + continue; } bool imported_children = false; - for (auto child_cell : drivers.find(chunk)) { + for (auto child_cell : drivers.find(bit)) { if (child_cell->type == cell->type) { opt_reduce(cells, drivers, child_cell); - new_sig_a.append(child_cell->connections["\\A"]); + if (child_cell->getPort("\\Y")[0] == bit) { + std::set<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort("\\A")).to_sigbit_set(); + new_sig_a_bits.insert(child_sig_a_bits.begin(), child_sig_a_bits.end()); + } else + new_sig_a_bits.insert(RTLIL::State::S0); imported_children = true; } } if (!imported_children) - new_sig_a.append(chunk); + new_sig_a_bits.insert(bit); } - new_sig_a.sort_and_unify(); - if (new_sig_a != sig_a || sig_a.width != cell->connections["\\A"].width) { + RTLIL::SigSpec new_sig_a(new_sig_a_bits); + + if (new_sig_a != sig_a || sig_a.size() != cell->getPort("\\A").size()) { log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a)); did_something = true; - OPT_DID_SOMETHING = true; total_count++; } - cell->connections["\\A"] = new_sig_a; - cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.width); + cell->setPort("\\A", new_sig_a); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.size()); return; } void opt_mux(RTLIL::Cell *cell) { - RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec sig_b = assign_map(cell->connections["\\B"]); - RTLIL::SigSpec sig_s = assign_map(cell->connections["\\S"]); + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S")); RTLIL::SigSpec new_sig_b, new_sig_s; std::set<RTLIL::SigSpec> handled_sig; handled_sig.insert(sig_a); - for (int i = 0; i < sig_s.width; i++) + for (int i = 0; i < sig_s.size(); i++) { - RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.width, sig_a.width); + RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.size(), sig_a.size()); if (handled_sig.count(this_b) > 0) continue; RTLIL::SigSpec this_s = sig_s.extract(i, 1); - for (int j = i+1; j < sig_s.width; j++) { - RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.width, sig_a.width); + for (int j = i+1; j < sig_s.size(); j++) { + RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.size(), sig_a.size()); if (this_b == that_b) this_s.append(sig_s.extract(j, 1)); } - if (this_s.width > 1) + if (this_s.size() > 1) { - RTLIL::Wire *reduce_or_wire = new RTLIL::Wire; - reduce_or_wire->name = NEW_ID; - module->wires[reduce_or_wire->name] = reduce_or_wire; - - RTLIL::Cell *reduce_or_cell = new RTLIL::Cell; - reduce_or_cell->name = NEW_ID; - reduce_or_cell->type = "$reduce_or"; - reduce_or_cell->connections["\\A"] = this_s; + RTLIL::Cell *reduce_or_cell = module->addCell(NEW_ID, "$reduce_or"); + reduce_or_cell->setPort("\\A", this_s); reduce_or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); - reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.width); + reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.size()); reduce_or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->cells[reduce_or_cell->name] = reduce_or_cell; + RTLIL::Wire *reduce_or_wire = module->addWire(NEW_ID); this_s = RTLIL::SigSpec(reduce_or_wire); - reduce_or_cell->connections["\\Y"] = this_s; + reduce_or_cell->setPort("\\Y", this_s); } new_sig_b.append(this_b); @@ -141,26 +136,24 @@ struct OptReduceWorker handled_sig.insert(this_b); } - if (new_sig_s.width != sig_s.width) { + if (new_sig_s.size() != sig_s.size()) { log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); did_something = true; - OPT_DID_SOMETHING = true; total_count++; } - if (new_sig_s.width == 0) + if (new_sig_s.size() == 0) { - module->connections.push_back(RTLIL::SigSig(cell->connections["\\Y"], cell->connections["\\A"])); - assign_map.add(cell->connections["\\Y"], cell->connections["\\A"]); - module->cells.erase(cell->name); - delete cell; + module->connect(RTLIL::SigSig(cell->getPort("\\Y"), cell->getPort("\\A"))); + assign_map.add(cell->getPort("\\Y"), cell->getPort("\\A")); + module->remove(cell); } else { - cell->connections["\\B"] = new_sig_b; - cell->connections["\\S"] = new_sig_s; - if (new_sig_s.width > 1) { - cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width); + cell->setPort("\\B", new_sig_b); + cell->setPort("\\S", new_sig_s); + if (new_sig_s.size() > 1) { + cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size()); } else { cell->type = "$mux"; cell->parameters.erase("\\S_WIDTH"); @@ -168,7 +161,85 @@ struct OptReduceWorker } } - OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module) : + void opt_mux_bits(RTLIL::Cell *cell) + { + std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort("\\A")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort("\\B")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort("\\Y")).to_sigbit_vector(); + + std::vector<RTLIL::SigBit> new_sig_y; + RTLIL::SigSig old_sig_conn; + + std::vector<std::vector<RTLIL::SigBit>> consolidated_in_tuples; + std::map<std::vector<RTLIL::SigBit>, RTLIL::SigBit> consolidated_in_tuples_map; + + for (int i = 0; i < int(sig_y.size()); i++) + { + std::vector<RTLIL::SigBit> in_tuple; + bool all_tuple_bits_same = true; + + in_tuple.push_back(sig_a.at(i)); + for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) { + if (sig_b.at(j) != sig_a.at(i)) + all_tuple_bits_same = false; + in_tuple.push_back(sig_b.at(j)); + } + + if (all_tuple_bits_same) + { + old_sig_conn.first.append_bit(sig_y.at(i)); + old_sig_conn.second.append_bit(sig_a.at(i)); + } + else if (consolidated_in_tuples_map.count(in_tuple)) + { + old_sig_conn.first.append_bit(sig_y.at(i)); + old_sig_conn.second.append_bit(consolidated_in_tuples_map.at(in_tuple)); + } + else + { + consolidated_in_tuples_map[in_tuple] = sig_y.at(i); + consolidated_in_tuples.push_back(in_tuple); + new_sig_y.push_back(sig_y.at(i)); + } + } + + if (new_sig_y.size() != sig_y.size()) + { + log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str()); + log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")), + log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y"))); + + cell->setPort("\\A", RTLIL::SigSpec()); + for (auto &in_tuple : consolidated_in_tuples) { + RTLIL::SigSpec new_a = cell->getPort("\\A"); + new_a.append(in_tuple.at(0)); + cell->setPort("\\A", new_a); + } + + cell->setPort("\\B", RTLIL::SigSpec()); + for (int i = 1; i <= cell->getPort("\\S").size(); i++) + for (auto &in_tuple : consolidated_in_tuples) { + RTLIL::SigSpec new_b = cell->getPort("\\B"); + new_b.append(in_tuple.at(i)); + cell->setPort("\\B", new_b); + } + + cell->parameters["\\WIDTH"] = RTLIL::Const(new_sig_y.size()); + cell->setPort("\\Y", new_sig_y); + + log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")), + log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y"))); + log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); + + module->connect(old_sig_conn); + module->check(); + + did_something = true; + total_count++; + } + } + + OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) : design(design), module(module), assign_map(module) { log(" Optimizing cells in module %s.\n", module->name.c_str()); @@ -176,6 +247,35 @@ struct OptReduceWorker total_count = 0; did_something = true; + SigPool mem_wren_sigs; + for (auto &cell_it : module->cells_) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$mem") + mem_wren_sigs.add(assign_map(cell->getPort("\\WR_EN"))); + if (cell->type == "$memwr") + mem_wren_sigs.add(assign_map(cell->getPort("\\EN"))); + } + for (auto &cell_it : module->cells_) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$dff" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Q")))) + mem_wren_sigs.add(assign_map(cell->getPort("\\D"))); + } + + bool keep_expanding_mem_wren_sigs = true; + while (keep_expanding_mem_wren_sigs) { + keep_expanding_mem_wren_sigs = false; + for (auto &cell_it : module->cells_) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$mux" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y")))) { + if (!mem_wren_sigs.check_all(assign_map(cell->getPort("\\A"))) || + !mem_wren_sigs.check_all(assign_map(cell->getPort("\\B")))) + keep_expanding_mem_wren_sigs = true; + mem_wren_sigs.add(assign_map(cell->getPort("\\A"))); + mem_wren_sigs.add(assign_map(cell->getPort("\\B"))); + } + } + } + while (did_something) { did_something = false; @@ -189,11 +289,11 @@ struct OptReduceWorker SigSet<RTLIL::Cell*> drivers; std::set<RTLIL::Cell*> cells; - for (auto &cell_it : module->cells) { + for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; if (cell->type != type || !design->selected(module, cell)) continue; - drivers.insert(assign_map(cell->connections["\\Y"]), cell); + drivers.insert(assign_map(cell->getPort("\\Y")), cell); cells.insert(cell); } @@ -205,11 +305,19 @@ struct OptReduceWorker // merge identical inputs on $mux and $pmux cells - for (auto &cell_it : module->cells) + std::vector<RTLIL::Cell*> cells; + + for (auto &it : module->cells_) + if ((it.second->type == "$mux" || it.second->type == "$pmux") && design->selected(module, it.second)) + cells.push_back(it.second); + + for (auto cell : cells) { - RTLIL::Cell *cell = cell_it.second; - if ((cell->type != "$mux" && cell->type != "$pmux" && cell->type != "$safe_pmux") || !design->selected(module, cell)) - continue; + // this optimization is to aggressive for most coarse-grain applications. + // but we always want it for multiplexers driving write enable ports. + if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y")))) + opt_mux_bits(cell); + opt_mux(cell); } } @@ -222,7 +330,7 @@ struct OptReducePass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_reduce [selection]\n"); + log(" opt_reduce [options] [selection]\n"); log("\n"); log("This pass performs two interlinked optimizations:\n"); log("\n"); @@ -232,20 +340,40 @@ struct OptReducePass : public Pass { log("2. it identifies duplicated inputs to MUXes and replaces them with a single\n"); log("input with the original control signals OR'ed together.\n"); log("\n"); + log(" -fine\n"); + log(" perform fine-grain optimizations\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + bool do_fine = false; + log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); - extra_args(args, 1, design); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-fine") { + do_fine = true; + continue; + } + break; + } + extra_args(args, argidx, design); int total_count = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; - OptReduceWorker worker(design, mod_it.second); - total_count += worker.total_count; + do { + OptReduceWorker worker(design, mod_it.second, do_fine); + total_count += worker.total_count; + if (worker.total_count == 0) + break; + } while (1); } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Performed a total of %d changes.\n", total_count); } } OptReducePass; diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 9a438537c..48f406f65 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -17,7 +17,6 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" @@ -33,34 +32,34 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) RTLIL::Const val_cp, val_rp, val_rv; if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\C"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\C"); val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1); } else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" && (dff->type[6] == 'N' || dff->type[6] == 'P') && (dff->type[7] == 'N' || dff->type[7] == 'P') && (dff->type[8] == '0' || dff->type[8] == '1')) { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\C"]; - sig_r = dff->connections["\\R"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\C"); + sig_r = dff->getPort("\\R"); val_cp = RTLIL::Const(dff->type[6] == 'P', 1); val_rp = RTLIL::Const(dff->type[7] == 'P', 1); val_rv = RTLIL::Const(dff->type[8] == '1', 1); } else if (dff->type == "$dff") { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\CLK"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\CLK"); val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); } else if (dff->type == "$adff") { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\CLK"]; - sig_r = dff->connections["\\ARST"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\CLK"); + sig_r = dff->getPort("\\ARST"); val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1); val_rv = dff->parameters["\\ARST_VALUE"]; @@ -85,55 +84,55 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) std::set<RTLIL::Cell*> muxes; mux_drivers.find(sig_d, muxes); for (auto mux : muxes) { - RTLIL::SigSpec sig_a = assign_map(mux->connections.at("\\A")); - RTLIL::SigSpec sig_b = assign_map(mux->connections.at("\\B")); + RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B")); if (sig_a == sig_q && sig_b.is_fully_const()) { RTLIL::SigSig conn(sig_q, sig_b); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } if (sig_b == sig_q && sig_a.is_fully_const()) { RTLIL::SigSig conn(sig_q, sig_a); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } } } - if (sig_c.is_fully_const() && (!sig_r.width || !has_init)) { + if (sig_c.is_fully_const() && (!sig_r.size() || !has_init)) { if (val_rv.bits.size() == 0) val_rv = val_init; RTLIL::SigSig conn(sig_q, val_rv); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d.is_fully_undef() && sig_r.width && !has_init) { + if (sig_d.is_fully_undef() && sig_r.size() && !has_init) { RTLIL::SigSig conn(sig_q, val_rv); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d.is_fully_undef() && !sig_r.width && has_init) { + if (sig_d.is_fully_undef() && !sig_r.size() && has_init) { RTLIL::SigSig conn(sig_q, val_init); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d.is_fully_const() && !sig_r.width && !has_init) { + if (sig_d.is_fully_const() && !sig_r.size() && !has_init) { RTLIL::SigSig conn(sig_q, sig_d); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d == sig_q && !(sig_r.width && has_init)) { - if (sig_r.width) { + if (sig_d == sig_q && !(sig_r.size() && has_init)) { + if (sig_r.size()) { RTLIL::SigSig conn(sig_q, val_rv); - mod->connections.push_back(conn); + mod->connect(conn); } if (has_init) { RTLIL::SigSig conn(sig_q, val_init); - mod->connections.push_back(conn); + mod->connect(conn); } goto delete_dff; } @@ -142,9 +141,7 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) delete_dff: log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); - OPT_DID_SOMETHING = true; - mod->cells.erase(dff->name); - delete dff; + mod->remove(dff); return true; } @@ -167,23 +164,23 @@ struct OptRmdffPass : public Pass { extra_args(args, 1, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; assign_map.set(mod_it.second); dff_init_map.set(mod_it.second); - for (auto &it : mod_it.second->wires) + for (auto &it : mod_it.second->wires_) if (it.second->attributes.count("\\init") != 0) dff_init_map.add(it.second, it.second->attributes.at("\\init")); mux_drivers.clear(); - std::vector<std::string> dff_list; - for (auto &it : mod_it.second->cells) { + std::vector<RTLIL::IdString> dff_list; + for (auto &it : mod_it.second->cells_) { if (it.second->type == "$mux" || it.second->type == "$pmux") { - if (it.second->connections.at("\\A").width == it.second->connections.at("\\B").width) - mux_drivers.insert(assign_map(it.second->connections.at("\\Y")), it.second); + if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size()) + mux_drivers.insert(assign_map(it.second->getPort("\\Y")), it.second); continue; } if (!design->selected(mod_it.second, it.second)) @@ -203,14 +200,17 @@ struct OptRmdffPass : public Pass { } for (auto &id : dff_list) { - if (mod_it.second->cells.count(id) > 0 && - handle_dff(mod_it.second, mod_it.second->cells[id])) + if (mod_it.second->cells_.count(id) > 0 && + handle_dff(mod_it.second, mod_it.second->cells_[id])) total_count++; } } assign_map.clear(); mux_drivers.clear(); + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Replaced %d DFF cells.\n", total_count); } } OptRmdffPass; diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index eb639d8ab..4b76a5a2d 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -17,14 +17,12 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" #include "libs/sha1/sha1.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -61,12 +59,12 @@ struct OptShareWorker if (cell_hash_cache.count(cell) > 0) return cell_hash_cache[cell]; - std::string hash_string = cell->type + "\n"; + std::string hash_string = cell->type.str() + "\n"; for (auto &it : cell->parameters) - hash_string += "P " + it.first + "=" + it.second.as_string() + "\n"; + hash_string += "P " + it.first.str() + "=" + it.second.as_string() + "\n"; - const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections; + const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections(); std::map<RTLIL::IdString, RTLIL::SigSpec> alt_conn; if (cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || cell->type == "$add" || cell->type == "$mul" || @@ -96,24 +94,19 @@ struct OptShareWorker continue; RTLIL::SigSpec sig = it.second; assign_map.apply(sig); - hash_string += "C " + it.first + "="; - for (auto &chunk : sig.chunks) { + hash_string += "C " + it.first.str() + "="; + for (auto &chunk : sig.chunks()) { if (chunk.wire) - hash_string += "{" + chunk.wire->name + " " + + hash_string += "{" + chunk.wire->name.str() + " " + int_to_hash_string(chunk.offset) + " " + int_to_hash_string(chunk.width) + "}"; else - hash_string += chunk.data.as_string(); + hash_string += RTLIL::Const(chunk.data).as_string(); } hash_string += "\n"; } - unsigned char hash[20]; - char hash_hex_string[41]; - sha1::calc(hash_string.c_str(), hash_string.size(), hash); - sha1::toHexString(hash, hash_hex_string); - cell_hash_cache[cell] = hash_hex_string; - + cell_hash_cache[cell] = sha1(hash_string); return cell_hash_cache[cell]; } #endif @@ -135,8 +128,8 @@ struct OptShareWorker return true; } - std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections; - std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections; + std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections(); + std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); for (auto &it : conn1) { if (ct.cell_output(cell1->type, it.first)) @@ -180,8 +173,8 @@ struct OptShareWorker } if (cell1->type.substr(0, 1) == "$" && conn1.count("\\Q") != 0) { - std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->connections.at("\\Q")).to_sigbit_vector(); - std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->connections.at("\\Q")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort("\\Q")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort("\\Q")).to_sigbit_vector(); for (size_t i = 0; i < q1.size(); i++) if ((q1.at(i).wire == NULL || q2.at(i).wire == NULL) && q1.at(i) != q2.at(i)) { lt = q1.at(i) < q2.at(i); @@ -230,14 +223,13 @@ struct OptShareWorker if (mode_nomux) { ct.cell_types.erase("$mux"); ct.cell_types.erase("$pmux"); - ct.cell_types.erase("$safe_pmux"); } log("Finding identical cells in module `%s'.\n", module->name.c_str()); assign_map.set(module); dff_init_map.set(module); - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->attributes.count("\\init") != 0) dff_init_map.add(it.second, it.second->attributes.at("\\init")); @@ -248,8 +240,8 @@ struct OptShareWorker cell_hash_cache.clear(); #endif std::vector<RTLIL::Cell*> cells; - cells.reserve(module->cells.size()); - for (auto &it : module->cells) { + cells.reserve(module->cells_.size()); + for (auto &it : module->cells_) { if (ct.cell_known(it.second->type) && design->selected(module, it.second)) cells.push_back(it.second); } @@ -261,20 +253,18 @@ struct OptShareWorker if (sharemap.count(cell) > 0) { did_something = true; log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); - for (auto &it : cell->connections) { + for (auto &it : cell->connections()) { if (ct.cell_output(cell->type, it.first)) { - RTLIL::SigSpec other_sig = sharemap[cell]->connections[it.first]; + RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first); log(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); - module->connections.push_back(RTLIL::SigSig(it.second, other_sig)); + module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); } } log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); - module->cells.erase(cell->name); - OPT_DID_SOMETHING = true; + module->remove(cell); total_count++; - delete cell; } else { sharemap[cell] = cell; } @@ -316,13 +306,15 @@ struct OptSharePass : public Pass { extra_args(args, argidx, design); int total_count = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; OptShareWorker worker(design, mod_it.second, mode_nomux); total_count += worker.total_count; } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Removed a total of %d cells.\n", total_count); } } OptSharePass; diff --git a/passes/opt/opt_status.h b/passes/opt/opt_status.h deleted file mode 100644 index 3d12baa7d..000000000 --- a/passes/opt/opt_status.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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. - * - */ - -#ifndef OPT_STATUS_H -#define OPT_STATUS_H - -extern bool OPT_DID_SOMETHING; - -#endif - diff --git a/passes/opt/share.cc b/passes/opt/share.cc new file mode 100644 index 000000000..74b049bb6 --- /dev/null +++ b/passes/opt/share.cc @@ -0,0 +1,1171 @@ +/* + * 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 "kernel/satgen.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" +#include "kernel/utils.h" + +PRIVATE_NAMESPACE_BEGIN + +struct ShareWorkerConfig +{ + int limit; + bool opt_force; + bool opt_aggressive; + bool opt_fast; + std::set<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops; +}; + +struct ShareWorker +{ + ShareWorkerConfig config; + std::set<RTLIL::IdString> generic_ops; + + RTLIL::Design *design; + RTLIL::Module *module; + + CellTypes fwd_ct, cone_ct; + ModWalker modwalker; + ModIndex mi; + + std::set<RTLIL::Cell*> cells_to_remove; + std::set<RTLIL::Cell*> recursion_state; + + SigMap topo_sigmap; + std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> topo_cell_drivers; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> topo_bit_drivers; + + + // ------------------------------------------------------------------------------ + // Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree + // ------------------------------------------------------------------------------ + + std::set<RTLIL::SigBit> terminal_bits; + + void find_terminal_bits() + { + std::set<RTLIL::SigBit> queue_bits; + std::set<RTLIL::Cell*> visited_cells; + + queue_bits.insert(modwalker.signal_outputs.begin(), modwalker.signal_outputs.end()); + + for (auto &it : module->cells_) + if (!fwd_ct.cell_known(it.second->type)) { + std::set<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second]; + queue_bits.insert(bits.begin(), bits.end()); + } + + terminal_bits.insert(queue_bits.begin(), queue_bits.end()); + + while (!queue_bits.empty()) + { + std::set<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, queue_bits); + queue_bits.clear(); + + for (auto &pbit : portbits) { + if (pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") { + std::set<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort("\\S")).to_sigbit_set(); + terminal_bits.insert(bits.begin(), bits.end()); + queue_bits.insert(bits.begin(), bits.end()); + visited_cells.insert(pbit.cell); + } + if (fwd_ct.cell_known(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) { + std::set<RTLIL::SigBit> &bits = modwalker.cell_inputs[pbit.cell]; + terminal_bits.insert(bits.begin(), bits.end()); + queue_bits.insert(bits.begin(), bits.end()); + visited_cells.insert(pbit.cell); + } + } + } + } + + + // --------------------------------------------------- + // Find shareable cells and compatible groups of cells + // --------------------------------------------------- + + std::set<RTLIL::Cell*, RTLIL::sort_by_name_str<RTLIL::Cell>> shareable_cells; + + void find_shareable_cells() + { + for (auto &it : module->cells_) + { + RTLIL::Cell *cell = it.second; + + if (!design->selected(module, cell) || !modwalker.ct.cell_known(cell->type)) + continue; + + for (auto &bit : modwalker.cell_outputs[cell]) + if (terminal_bits.count(bit)) + goto not_a_muxed_cell; + + if (0) + not_a_muxed_cell: + continue; + + if (config.opt_force) { + shareable_cells.insert(cell); + continue; + } + + if (cell->type == "$memrd") { + if (!cell->parameters.at("\\CLK_ENABLE").as_bool()) + shareable_cells.insert(cell); + continue; + } + + if (cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod") { + if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 4) + shareable_cells.insert(cell); + continue; + } + + if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr") { + if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 8) + shareable_cells.insert(cell); + continue; + } + + if (generic_ops.count(cell->type)) { + if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10) + shareable_cells.insert(cell); + continue; + } + } + } + + bool is_shareable_pair(RTLIL::Cell *c1, RTLIL::Cell *c2) + { + if (c1->type != c2->type) + return false; + + if (c1->type == "$memrd") + { + if (c1->parameters.at("\\MEMID").decode_string() != c2->parameters.at("\\MEMID").decode_string()) + return false; + + return true; + } + + if (config.generic_uni_ops.count(c1->type)) + { + if (!config.opt_aggressive) + { + int a1_width = c1->parameters.at("\\A_WIDTH").as_int(); + int y1_width = c1->parameters.at("\\Y_WIDTH").as_int(); + + int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); + int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); + + if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; + if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + } + + return true; + } + + if (config.generic_bin_ops.count(c1->type)) + { + if (!config.opt_aggressive) + { + int a1_width = c1->parameters.at("\\A_WIDTH").as_int(); + int b1_width = c1->parameters.at("\\B_WIDTH").as_int(); + int y1_width = c1->parameters.at("\\Y_WIDTH").as_int(); + + int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); + int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); + int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); + + if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; + if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false; + if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + } + + return true; + } + + if (config.generic_cbin_ops.count(c1->type)) + { + if (!config.opt_aggressive) + { + int a1_width = c1->parameters.at("\\A_WIDTH").as_int(); + int b1_width = c1->parameters.at("\\B_WIDTH").as_int(); + int y1_width = c1->parameters.at("\\Y_WIDTH").as_int(); + + int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); + int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); + int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); + + int min1_width = std::min(a1_width, b1_width); + int max1_width = std::max(a1_width, b1_width); + + int min2_width = std::min(a2_width, b2_width); + int max2_width = std::max(a2_width, b2_width); + + if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false; + if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false; + if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + } + + return true; + } + + for (auto &it : c1->parameters) + if (c2->parameters.count(it.first) == 0 || c2->parameters.at(it.first) != it.second) + return false; + + for (auto &it : c2->parameters) + if (c1->parameters.count(it.first) == 0 || c1->parameters.at(it.first) != it.second) + return false; + + return true; + } + + void find_shareable_partners(std::vector<RTLIL::Cell*> &results, RTLIL::Cell *cell) + { + results.clear(); + for (auto c : shareable_cells) + if (c != cell && is_shareable_pair(c, cell)) + results.push_back(c); + } + + + // ----------------------- + // Create replacement cell + // ----------------------- + + RTLIL::Cell *make_supercell(RTLIL::Cell *c1, RTLIL::Cell *c2, RTLIL::SigSpec act, std::set<RTLIL::Cell*> &supercell_aux) + { + log_assert(c1->type == c2->type); + + if (config.generic_uni_ops.count(c1->type)) + { + if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool()) + { + RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1; + if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) { + unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1; + RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A"); + new_a.append_bit(RTLIL::State::S0); + unsigned_cell->setPort("\\A", new_a); + } + unsigned_cell->parameters.at("\\A_SIGNED") = true; + unsigned_cell->check(); + } + + bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool(); + log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool()); + + RTLIL::SigSpec a1 = c1->getPort("\\A"); + RTLIL::SigSpec y1 = c1->getPort("\\Y"); + + RTLIL::SigSpec a2 = c2->getPort("\\A"); + RTLIL::SigSpec y2 = c2->getPort("\\Y"); + + int a_width = std::max(a1.size(), a2.size()); + int y_width = std::max(y1.size(), y2.size()); + + a1.extend_u0(a_width, a_signed); + a2.extend_u0(a_width, a_signed); + + RTLIL::SigSpec a = module->addWire(NEW_ID, a_width); + supercell_aux.insert(module->addMux(NEW_ID, a2, a1, act, a)); + + RTLIL::Wire *y = module->addWire(NEW_ID, y_width); + + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type); + supercell->parameters["\\A_SIGNED"] = a_signed; + supercell->parameters["\\A_WIDTH"] = a_width; + supercell->parameters["\\Y_WIDTH"] = y_width; + supercell->setPort("\\A", a); + supercell->setPort("\\Y", y); + + supercell_aux.insert(module->addPos(NEW_ID, y, y1)); + supercell_aux.insert(module->addPos(NEW_ID, y, y2)); + + supercell_aux.insert(supercell); + return supercell; + } + + if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type)) + { + bool modified_src_cells = false; + + if (config.generic_cbin_ops.count(c1->type)) + { + int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + + std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); + + int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + + std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); + + if (score_flipped < score_unflipped) + { + RTLIL::SigSpec tmp = c2->getPort("\\A"); + c2->setPort("\\A", c2->getPort("\\B")); + c2->setPort("\\B", tmp); + + std::swap(c2->parameters.at("\\A_WIDTH"), c2->parameters.at("\\B_WIDTH")); + std::swap(c2->parameters.at("\\A_SIGNED"), c2->parameters.at("\\B_SIGNED")); + modified_src_cells = true; + } + } + + if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool()) + + { + RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1; + if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) { + unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1; + RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A"); + new_a.append_bit(RTLIL::State::S0); + unsigned_cell->setPort("\\A", new_a); + } + unsigned_cell->parameters.at("\\A_SIGNED") = true; + modified_src_cells = true; + } + + if (c1->parameters.at("\\B_SIGNED").as_bool() != c2->parameters.at("\\B_SIGNED").as_bool()) + { + RTLIL::Cell *unsigned_cell = c1->parameters.at("\\B_SIGNED").as_bool() ? c2 : c1; + if (unsigned_cell->getPort("\\B").to_sigbit_vector().back() != RTLIL::State::S0) { + unsigned_cell->parameters.at("\\B_WIDTH") = unsigned_cell->parameters.at("\\B_WIDTH").as_int() + 1; + RTLIL::SigSpec new_b = unsigned_cell->getPort("\\B"); + new_b.append_bit(RTLIL::State::S0); + unsigned_cell->setPort("\\B", new_b); + } + unsigned_cell->parameters.at("\\B_SIGNED") = true; + modified_src_cells = true; + } + + if (modified_src_cells) { + c1->check(); + c2->check(); + } + + bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool(); + bool b_signed = c1->parameters.at("\\B_SIGNED").as_bool(); + + log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool()); + log_assert(b_signed == c2->parameters.at("\\B_SIGNED").as_bool()); + + if (c1->type == "$shl" || c1->type == "$shr" || c1->type == "$sshl" || c1->type == "$sshr") + b_signed = false; + + RTLIL::SigSpec a1 = c1->getPort("\\A"); + RTLIL::SigSpec b1 = c1->getPort("\\B"); + RTLIL::SigSpec y1 = c1->getPort("\\Y"); + + RTLIL::SigSpec a2 = c2->getPort("\\A"); + RTLIL::SigSpec b2 = c2->getPort("\\B"); + RTLIL::SigSpec y2 = c2->getPort("\\Y"); + + int a_width = std::max(a1.size(), a2.size()); + int b_width = std::max(b1.size(), b2.size()); + int y_width = std::max(y1.size(), y2.size()); + + if (c1->type == "$shr" && a_signed) + { + a_width = std::max(y_width, a_width); + + if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true); + if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true); + + a1.extend_u0(a_width, false); + a2.extend_u0(a_width, false); + } + else + { + a1.extend_u0(a_width, a_signed); + a2.extend_u0(a_width, a_signed); + } + + b1.extend_u0(b_width, b_signed); + b2.extend_u0(b_width, b_signed); + + RTLIL::SigSpec a = module->addWire(NEW_ID, a_width); + RTLIL::SigSpec b = module->addWire(NEW_ID, b_width); + + supercell_aux.insert(module->addMux(NEW_ID, a2, a1, act, a)); + supercell_aux.insert(module->addMux(NEW_ID, b2, b1, act, b)); + + RTLIL::Wire *y = module->addWire(NEW_ID, y_width); + + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type); + supercell->parameters["\\A_SIGNED"] = a_signed; + supercell->parameters["\\B_SIGNED"] = b_signed; + supercell->parameters["\\A_WIDTH"] = a_width; + supercell->parameters["\\B_WIDTH"] = b_width; + supercell->parameters["\\Y_WIDTH"] = y_width; + supercell->setPort("\\A", a); + supercell->setPort("\\B", b); + supercell->setPort("\\Y", y); + supercell->check(); + + supercell_aux.insert(module->addPos(NEW_ID, y, y1)); + supercell_aux.insert(module->addPos(NEW_ID, y, y2)); + + supercell_aux.insert(supercell); + return supercell; + } + + if (c1->type == "$memrd") + { + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); + supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); + supercell_aux.insert(supercell); + return supercell; + } + + log_abort(); + } + + + // ------------------------------------------- + // Finding forbidden control inputs for a cell + // ------------------------------------------- + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> forbidden_controls_cache; + + const std::set<RTLIL::SigBit> &find_forbidden_controls(RTLIL::Cell *cell) + { + if (recursion_state.count(cell)) { + static std::set<RTLIL::SigBit> empty_controls_set; + return empty_controls_set; + } + + if (forbidden_controls_cache.count(cell)) + return forbidden_controls_cache.at(cell); + + std::set<ModWalker::PortBit> pbits; + std::set<RTLIL::Cell*> consumer_cells; + + modwalker.get_consumers(pbits, modwalker.cell_outputs[cell]); + + for (auto &bit : pbits) { + if ((bit.cell->type == "$mux" || bit.cell->type == "$pmux") && bit.port == "\\S") + forbidden_controls_cache[cell].insert(bit.cell->getPort("\\S").extract(bit.offset, 1)); + consumer_cells.insert(bit.cell); + } + + recursion_state.insert(cell); + + for (auto c : consumer_cells) + if (fwd_ct.cell_known(c->type)) { + const std::set<RTLIL::SigBit> &bits = find_forbidden_controls(c); + forbidden_controls_cache[cell].insert(bits.begin(), bits.end()); + } + + log_assert(recursion_state.count(cell)); + recursion_state.erase(cell); + + return forbidden_controls_cache[cell]; + } + + + // -------------------------------------------------------- + // Finding control inputs and activation pattern for a cell + // -------------------------------------------------------- + + std::map<RTLIL::Cell*, std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>>> activation_patterns_cache; + + bool sort_check_activation_pattern(std::pair<RTLIL::SigSpec, RTLIL::Const> &p) + { + std::map<RTLIL::SigBit, RTLIL::State> p_bits; + + std::vector<RTLIL::SigBit> p_first_bits = p.first; + for (int i = 0; i < SIZE(p_first_bits); i++) { + RTLIL::SigBit b = p_first_bits[i]; + RTLIL::State v = p.second.bits[i]; + if (p_bits.count(b) && p_bits.at(b) != v) + return false; + p_bits[b] = v; + } + + p.first = RTLIL::SigSpec(); + p.second.bits.clear(); + + for (auto &it : p_bits) { + p.first.append_bit(it.first); + p.second.bits.push_back(it.second); + } + + return true; + } + + void optimize_activation_patterns(std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> & /* patterns */) + { + // TODO: Remove patterns that are contained in other patterns + // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit + } + + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) + { + if (recursion_state.count(cell)) { + static std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> empty_patterns_set; + return empty_patterns_set; + } + + if (activation_patterns_cache.count(cell)) + return activation_patterns_cache.at(cell); + + const std::set<RTLIL::SigBit> &cell_out_bits = modwalker.cell_outputs[cell]; + std::set<RTLIL::Cell*> driven_cells, driven_data_muxes; + + for (auto &bit : cell_out_bits) + { + if (terminal_bits.count(bit)) { + // Terminal cells are always active: unconditional activation pattern + activation_patterns_cache[cell].insert(std::pair<RTLIL::SigSpec, RTLIL::Const>()); + return activation_patterns_cache.at(cell); + } + for (auto &pbit : modwalker.signal_consumers[bit]) { + log_assert(fwd_ct.cell_known(pbit.cell->type)); + if ((pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") && (pbit.port == "\\A" || pbit.port == "\\B")) + driven_data_muxes.insert(pbit.cell); + else + driven_cells.insert(pbit.cell); + } + } + + recursion_state.insert(cell); + + for (auto c : driven_data_muxes) + { + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &c_patterns = find_cell_activation_patterns(c, indent); + + bool used_in_a = false; + std::set<int> used_in_b_parts; + + int width = c->parameters.at("\\WIDTH").as_int(); + std::vector<RTLIL::SigBit> sig_a = modwalker.sigmap(c->getPort("\\A")); + std::vector<RTLIL::SigBit> sig_b = modwalker.sigmap(c->getPort("\\B")); + std::vector<RTLIL::SigBit> sig_s = modwalker.sigmap(c->getPort("\\S")); + + for (auto &bit : sig_a) + if (cell_out_bits.count(bit)) + used_in_a = true; + + for (int i = 0; i < SIZE(sig_b); i++) + if (cell_out_bits.count(sig_b[i])) + used_in_b_parts.insert(i / width); + + if (used_in_a) + for (auto p : c_patterns) { + for (int i = 0; i < SIZE(sig_s); i++) + p.first.append_bit(sig_s[i]), p.second.bits.push_back(RTLIL::State::S0); + if (sort_check_activation_pattern(p)) + activation_patterns_cache[cell].insert(p); + } + + for (int idx : used_in_b_parts) + for (auto p : c_patterns) { + p.first.append_bit(sig_s[idx]), p.second.bits.push_back(RTLIL::State::S1); + if (sort_check_activation_pattern(p)) + activation_patterns_cache[cell].insert(p); + } + } + + for (auto c : driven_cells) { + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &c_patterns = find_cell_activation_patterns(c, indent); + activation_patterns_cache[cell].insert(c_patterns.begin(), c_patterns.end()); + } + + log_assert(recursion_state.count(cell)); + recursion_state.erase(cell); + + optimize_activation_patterns(activation_patterns_cache[cell]); + if (activation_patterns_cache[cell].empty()) { + log("%sFound cell that is never activated: %s\n", indent, log_id(cell)); + RTLIL::SigSpec cell_outputs = modwalker.cell_outputs[cell]; + module->connect(RTLIL::SigSig(cell_outputs, RTLIL::SigSpec(RTLIL::State::Sx, cell_outputs.size()))); + cells_to_remove.insert(cell); + } + + return activation_patterns_cache[cell]; + } + + RTLIL::SigSpec bits_from_activation_patterns(const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &activation_patterns) + { + std::set<RTLIL::SigBit> all_bits; + for (auto &it : activation_patterns) { + std::vector<RTLIL::SigBit> bits = it.first; + all_bits.insert(bits.begin(), bits.end()); + } + + RTLIL::SigSpec signal; + for (auto &bit : all_bits) + signal.append_bit(bit); + + return signal; + } + + void filter_activation_patterns(std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &out, + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &in, const std::set<RTLIL::SigBit> &filter_bits) + { + for (auto &p : in) + { + std::vector<RTLIL::SigBit> p_first = p.first; + std::pair<RTLIL::SigSpec, RTLIL::Const> new_p; + + for (int i = 0; i < SIZE(p_first); i++) + if (filter_bits.count(p_first[i]) == 0) { + new_p.first.append_bit(p_first[i]); + new_p.second.bits.push_back(p.second.bits.at(i)); + } + + out.insert(new_p); + } + } + + RTLIL::SigSpec make_cell_activation_logic(const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &activation_patterns, std::set<RTLIL::Cell*> &supercell_aux) + { + RTLIL::Wire *all_cases_wire = module->addWire(NEW_ID, 0); + + for (auto &p : activation_patterns) { + all_cases_wire->width++; + supercell_aux.insert(module->addEq(NEW_ID, p.first, p.second, RTLIL::SigSpec(all_cases_wire, all_cases_wire->width - 1))); + } + + if (all_cases_wire->width == 1) + return all_cases_wire; + + RTLIL::Wire *result_wire = module->addWire(NEW_ID); + supercell_aux.insert(module->addReduceOr(NEW_ID, all_cases_wire, result_wire)); + return result_wire; + } + + + // ------------------------------------------------------------------------------------- + // Helper functions used to make sure that this pass does not introduce new logic loops. + // ------------------------------------------------------------------------------------- + + bool module_has_scc() + { + CellTypes ct; + ct.setup_internals(); + ct.setup_stdcells(); + + TopoSort<RTLIL::Cell*> toposort; + toposort.analyze_loops = false; + + topo_sigmap.set(module); + topo_bit_drivers.clear(); + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bits; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cells; + + for (auto cell : module->cells()) + if (ct.cell_known(cell->type)) + for (auto &conn : cell->connections()) { + if (ct.cell_output(cell->type, conn.first)) + for (auto bit : topo_sigmap(conn.second)) { + cell_to_bits[cell].insert(bit); + topo_bit_drivers[bit].insert(cell); + } + else + for (auto bit : topo_sigmap(conn.second)) + bit_to_cells[bit].insert(cell); + } + + for (auto &it : cell_to_bits) + { + RTLIL::Cell *c1 = it.first; + + for (auto bit : it.second) + for (auto c2 : bit_to_cells[bit]) + toposort.edge(c1, c2); + } + + bool found_scc = !toposort.sort(); + topo_cell_drivers = std::move(toposort.database); + + if (found_scc && toposort.analyze_loops) + for (auto &loop : toposort.loops) { + log("### loop ###\n"); + for (auto &c : loop) + log("%s (%s)\n", log_id(c), log_id(c->type)); + } + + return found_scc; + } + + bool find_in_input_cone_worker(RTLIL::Cell *root, RTLIL::Cell *needle, std::set<RTLIL::Cell*> &stop) + { + if (root == needle) + return true; + + if (stop.count(root)) + return false; + + stop.insert(root); + + for (auto c : topo_cell_drivers[root]) + if (find_in_input_cone_worker(c, needle, stop)) + return true; + return false; + } + + bool find_in_input_cone(RTLIL::Cell *root, RTLIL::Cell *needle) + { + std::set<RTLIL::Cell*> stop; + return find_in_input_cone_worker(root, needle, stop); + } + + bool is_part_of_scc(RTLIL::Cell *cell) + { + CellTypes ct; + ct.setup_internals(); + ct.setup_stdcells(); + + std::set<RTLIL::Cell*> queue, covered; + queue.insert(cell); + + while (!queue.empty()) + { + std::set<RTLIL::Cell*> new_queue; + + for (auto c : queue) { + if (!ct.cell_known(c->type)) + continue; + for (auto &conn : c->connections()) + if (ct.cell_input(c->type, conn.first)) + for (auto bit : conn.second) + for (auto &pi : mi.query_ports(bit)) + if (ct.cell_known(pi.cell->type) && ct.cell_output(pi.cell->type, pi.port)) + new_queue.insert(pi.cell); + covered.insert(c); + } + + queue.clear(); + for (auto c : new_queue) { + if (cells_to_remove.count(c)) + continue; + if (c == cell) + return true; + if (!covered.count(c)) + queue.insert(c); + } + } + + return false; + } + + + // ------------- + // Setup and run + // ------------- + + ShareWorker(ShareWorkerConfig config, RTLIL::Design *design, RTLIL::Module *module) : + config(config), design(design), module(module), mi(module) + { + bool before_scc = module_has_scc(); + + generic_ops.insert(config.generic_uni_ops.begin(), config.generic_uni_ops.end()); + generic_ops.insert(config.generic_bin_ops.begin(), config.generic_bin_ops.end()); + generic_ops.insert(config.generic_cbin_ops.begin(), config.generic_cbin_ops.end()); + + fwd_ct.setup_internals(); + + cone_ct.setup_internals(); + cone_ct.cell_types.erase("$mul"); + cone_ct.cell_types.erase("$mod"); + cone_ct.cell_types.erase("$div"); + cone_ct.cell_types.erase("$pow"); + cone_ct.cell_types.erase("$shl"); + cone_ct.cell_types.erase("$shr"); + cone_ct.cell_types.erase("$sshl"); + cone_ct.cell_types.erase("$sshr"); + + modwalker.setup(design, module); + + find_terminal_bits(); + find_shareable_cells(); + + if (shareable_cells.size() < 2) + return; + + log("Found %d cells in module %s that may be considered for resource sharing.\n", + SIZE(shareable_cells), log_id(module)); + + while (!shareable_cells.empty() && config.limit != 0) + { + RTLIL::Cell *cell = *shareable_cells.begin(); + shareable_cells.erase(cell); + + log(" Analyzing resource sharing options for %s:\n", log_id(cell)); + + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &cell_activation_patterns = find_cell_activation_patterns(cell, " "); + RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns); + + if (cell_activation_patterns.empty()) { + log(" Cell is never active. Sharing is pointless, we simply remove it.\n"); + cells_to_remove.insert(cell); + continue; + } + + if (cell_activation_patterns.count(std::pair<RTLIL::SigSpec, RTLIL::Const>())) { + log(" Cell is always active. Therefore no sharing is possible.\n"); + continue; + } + + log(" Found %d activation_patterns using ctrl signal %s.\n", SIZE(cell_activation_patterns), log_signal(cell_activation_signals)); + + std::vector<RTLIL::Cell*> candidates; + find_shareable_partners(candidates, cell); + + if (candidates.empty()) { + log(" No candidates found.\n"); + continue; + } + + log(" Found %d candidates:", SIZE(candidates)); + for (auto c : candidates) + log(" %s", log_id(c)); + log("\n"); + + for (auto other_cell : candidates) + { + log(" Analyzing resource sharing with %s:\n", log_id(other_cell)); + + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " "); + RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns); + + if (other_cell_activation_patterns.empty()) { + log(" Cell is never active. Sharing is pointless, we simply remove it.\n"); + shareable_cells.erase(other_cell); + cells_to_remove.insert(other_cell); + continue; + } + + if (other_cell_activation_patterns.count(std::pair<RTLIL::SigSpec, RTLIL::Const>())) { + log(" Cell is always active. Therefore no sharing is possible.\n"); + shareable_cells.erase(other_cell); + continue; + } + + log(" Found %d activation_patterns using ctrl signal %s.\n", + SIZE(other_cell_activation_patterns), log_signal(other_cell_activation_signals)); + + const std::set<RTLIL::SigBit> &cell_forbidden_controls = find_forbidden_controls(cell); + const std::set<RTLIL::SigBit> &other_cell_forbidden_controls = find_forbidden_controls(other_cell); + + std::set<RTLIL::SigBit> union_forbidden_controls; + union_forbidden_controls.insert(cell_forbidden_controls.begin(), cell_forbidden_controls.end()); + union_forbidden_controls.insert(other_cell_forbidden_controls.begin(), other_cell_forbidden_controls.end()); + + if (!union_forbidden_controls.empty()) + log(" Forbidden control signals for this pair of cells: %s\n", log_signal(union_forbidden_controls)); + + std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> filtered_cell_activation_patterns; + std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> filtered_other_cell_activation_patterns; + + filter_activation_patterns(filtered_cell_activation_patterns, cell_activation_patterns, union_forbidden_controls); + filter_activation_patterns(filtered_other_cell_activation_patterns, other_cell_activation_patterns, union_forbidden_controls); + + optimize_activation_patterns(filtered_cell_activation_patterns); + optimize_activation_patterns(filtered_other_cell_activation_patterns); + + ezDefaultSAT ez; + SatGen satgen(&ez, &modwalker.sigmap); + + std::set<RTLIL::Cell*> sat_cells; + std::set<RTLIL::SigBit> bits_queue; + + std::vector<int> cell_active, other_cell_active; + RTLIL::SigSpec all_ctrl_signals; + + for (auto &p : filtered_cell_activation_patterns) { + log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); + cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + all_ctrl_signals.append(p.first); + } + + for (auto &p : filtered_other_cell_activation_patterns) { + log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); + other_cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + all_ctrl_signals.append(p.first); + } + + for (auto &bit : cell_activation_signals.to_sigbit_vector()) + bits_queue.insert(bit); + + for (auto &bit : other_cell_activation_signals.to_sigbit_vector()) + bits_queue.insert(bit); + + while (!bits_queue.empty()) + { + std::set<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, bits_queue); + bits_queue.clear(); + + for (auto &pbit : portbits) + if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) { + if (config.opt_fast && modwalker.cell_outputs[pbit.cell].size() >= 4) + continue; + // log(" Adding cell %s (%s) to SAT problem.\n", log_id(pbit.cell), log_id(pbit.cell->type)); + bits_queue.insert(modwalker.cell_inputs[pbit.cell].begin(), modwalker.cell_inputs[pbit.cell].end()); + satgen.importCell(pbit.cell); + sat_cells.insert(pbit.cell); + } + + if (config.opt_fast && sat_cells.size() > 100) + break; + } + + if (!ez.solve(ez.expression(ez.OpOr, cell_active))) { + log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); + cells_to_remove.insert(cell); + break; + } + + if (!ez.solve(ez.expression(ez.OpOr, other_cell_active))) { + log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); + cells_to_remove.insert(other_cell); + shareable_cells.erase(other_cell); + continue; + } + + ez.non_incremental(); + + all_ctrl_signals.sort_and_unify(); + std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals); + std::vector<bool> sat_model_values; + + ez.assume(ez.AND(ez.expression(ez.OpOr, cell_active), ez.expression(ez.OpOr, other_cell_active))); + + log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", + SIZE(sat_cells), ez.numCnfVariables(), ez.numCnfClauses()); + + if (ez.solve(sat_model, sat_model_values)) { + log(" According to the SAT solver this pair of cells can not be shared.\n"); + log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), SIZE(sat_model_values)); + for (int i = SIZE(sat_model_values)-1; i >= 0; i--) + log("%c", sat_model_values[i] ? '1' : '0'); + log("\n"); + continue; + } + + log(" According to the SAT solver this pair of cells can be shared.\n"); + + if (find_in_input_cone(cell, other_cell)) { + log(" Sharing not possible: %s is in input cone of %s.\n", log_id(other_cell), log_id(cell)); + continue; + } + + if (find_in_input_cone(other_cell, cell)) { + log(" Sharing not possible: %s is in input cone of %s.\n", log_id(cell), log_id(other_cell)); + continue; + } + + shareable_cells.erase(other_cell); + + int cell_select_score = 0; + int other_cell_select_score = 0; + + for (auto &p : filtered_cell_activation_patterns) + cell_select_score += p.first.size(); + + for (auto &p : filtered_other_cell_activation_patterns) + other_cell_select_score += p.first.size(); + + RTLIL::Cell *supercell; + std::set<RTLIL::Cell*> supercell_aux; + if (cell_select_score <= other_cell_select_score) { + RTLIL::SigSpec act = make_cell_activation_logic(filtered_cell_activation_patterns, supercell_aux); + supercell = make_supercell(cell, other_cell, act, supercell_aux); + log(" Activation signal for %s: %s\n", log_id(cell), log_signal(act)); + } else { + RTLIL::SigSpec act = make_cell_activation_logic(filtered_other_cell_activation_patterns, supercell_aux); + supercell = make_supercell(other_cell, cell, act, supercell_aux); + log(" Activation signal for %s: %s\n", log_id(other_cell), log_signal(act)); + } + + log(" New cell: %s (%s)\n", log_id(supercell), log_id(supercell->type)); + + cells_to_remove.insert(cell); + cells_to_remove.insert(other_cell); + + for (auto c : supercell_aux) + if (is_part_of_scc(c)) + goto do_rollback; + + if (0) { + do_rollback: + log(" New topology contains loops! Rolling back..\n"); + cells_to_remove.erase(cell); + cells_to_remove.erase(other_cell); + shareable_cells.insert(other_cell); + for (auto cc : supercell_aux) + module->remove(cc); + continue; + } + + std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> supercell_activation_patterns; + supercell_activation_patterns.insert(filtered_cell_activation_patterns.begin(), filtered_cell_activation_patterns.end()); + supercell_activation_patterns.insert(filtered_other_cell_activation_patterns.begin(), filtered_other_cell_activation_patterns.end()); + optimize_activation_patterns(supercell_activation_patterns); + activation_patterns_cache[supercell] = supercell_activation_patterns; + shareable_cells.insert(supercell); + + for (auto bit : topo_sigmap(all_ctrl_signals)) + for (auto c : topo_bit_drivers[bit]) + topo_cell_drivers[supercell].insert(c); + + topo_cell_drivers[supercell].insert(topo_cell_drivers[cell].begin(), topo_cell_drivers[cell].end()); + topo_cell_drivers[supercell].insert(topo_cell_drivers[other_cell].begin(), topo_cell_drivers[other_cell].end()); + + topo_cell_drivers[cell] = { supercell }; + topo_cell_drivers[other_cell] = { supercell }; + + if (config.limit > 0) + config.limit--; + + break; + } + } + + if (!cells_to_remove.empty()) { + log("Removing %d cells in module %s:\n", SIZE(cells_to_remove), log_id(module)); + for (auto c : cells_to_remove) { + log(" Removing cell %s (%s).\n", log_id(c), log_id(c->type)); + module->remove(c); + } + } + + log_assert(recursion_state.empty()); + + bool after_scc = before_scc || module_has_scc(); + log_assert(before_scc == after_scc); + } +}; + +struct SharePass : public Pass { + SharePass() : Pass("share", "perform sat-based resource sharing") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" share [options] [selection]\n"); + log("\n"); + log("This pass merges shareable resources into a single resource. A SAT solver\n"); + log("is used to determine if two resources are share-able.\n"); + log("\n"); + log(" -force\n"); + log(" Per default the selection of cells that is considered for sharing is\n"); + log(" narrowed using a list of cell types. With this option all selected\n"); + log(" cells are considered for resource sharing.\n"); + log("\n"); + log(" IMPORTANT NOTE: If the -all option is used then no cells with internal\n"); + log(" state must be selected!\n"); + log("\n"); + log(" -aggressive\n"); + log(" Per default some heuristics are used to reduce the number of cells\n"); + log(" considered for resource sharing to only large resources. This options\n"); + log(" turns this heuristics off, resulting in much more cells being considered\n"); + log(" for resource sharing.\n"); + log("\n"); + log(" -fast\n"); + log(" Only consider the simple part of the control logic in SAT solving, resulting\n"); + log(" in much easier SAT problems at the cost of maybe missing some oportunities\n"); + log(" for resource sharing.\n"); + log("\n"); + log(" -limit N\n"); + log(" Only perform the first N merges, then stop. This is useful for debugging.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + ShareWorkerConfig config; + + config.limit = -1; + config.opt_force = false; + config.opt_aggressive = false; + config.opt_fast = false; + + config.generic_uni_ops.insert("$not"); + // config.generic_uni_ops.insert("$pos"); + config.generic_uni_ops.insert("$neg"); + + config.generic_cbin_ops.insert("$and"); + config.generic_cbin_ops.insert("$or"); + config.generic_cbin_ops.insert("$xor"); + config.generic_cbin_ops.insert("$xnor"); + + config.generic_bin_ops.insert("$shl"); + config.generic_bin_ops.insert("$shr"); + config.generic_bin_ops.insert("$sshl"); + config.generic_bin_ops.insert("$sshr"); + + config.generic_bin_ops.insert("$lt"); + config.generic_bin_ops.insert("$le"); + config.generic_bin_ops.insert("$eq"); + config.generic_bin_ops.insert("$ne"); + config.generic_bin_ops.insert("$eqx"); + config.generic_bin_ops.insert("$nex"); + config.generic_bin_ops.insert("$ge"); + config.generic_bin_ops.insert("$gt"); + + config.generic_cbin_ops.insert("$add"); + config.generic_cbin_ops.insert("$mul"); + + config.generic_bin_ops.insert("$sub"); + config.generic_bin_ops.insert("$div"); + config.generic_bin_ops.insert("$mod"); + // config.generic_bin_ops.insert("$pow"); + + config.generic_uni_ops.insert("$logic_not"); + config.generic_cbin_ops.insert("$logic_and"); + config.generic_cbin_ops.insert("$logic_or"); + + log_header("Executing SHARE pass (SAT-based resource sharing).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-force") { + config.opt_force = true; + continue; + } + if (args[argidx] == "-aggressive") { + config.opt_aggressive = true; + continue; + } + if (args[argidx] == "-fast") { + config.opt_fast = true; + continue; + } + if (args[argidx] == "-limit" && argidx+1 < args.size()) { + config.limit = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto &mod_it : design->modules_) + if (design->selected(mod_it.second)) + ShareWorker(config, design, mod_it.second); + } +} SharePass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc new file mode 100644 index 000000000..58a6d1b0d --- /dev/null +++ b/passes/opt/wreduce.cc @@ -0,0 +1,350 @@ +/* + * 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 "kernel/sigtools.h" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +using namespace RTLIL; + +PRIVATE_NAMESPACE_BEGIN + +struct WreduceConfig +{ + std::set<IdString> supported_cell_types; + + WreduceConfig() + { + supported_cell_types = { + "$not", "$pos", "$neg", + "$and", "$or", "$xor", "$xnor", + "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$add", "$sub", // "$mul", "$div", "$mod", "$pow", + "$mux", "$pmux" + }; + } +}; + +struct WreduceWorker +{ + WreduceConfig *config; + Module *module; + ModIndex mi; + + std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; + std::set<SigBit> work_queue_bits; + + WreduceWorker(WreduceConfig *config, Module *module) : + config(config), module(module), mi(module) { } + + void run_cell_mux(Cell *cell) + { + // Reduce size of MUX if inputs agree on a value for a bit or a output bit is unused + + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")); + SigSpec sig_b = mi.sigmap(cell->getPort("\\B")); + SigSpec sig_s = mi.sigmap(cell->getPort("\\S")); + SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); + std::vector<SigBit> bits_removed; + + for (int i = SIZE(sig_y)-1; i >= 0; i--) + { + auto info = mi.query(sig_y[i]); + if (!info->is_output && SIZE(info->ports) <= 1) { + bits_removed.push_back(Sx); + continue; + } + + SigBit ref = sig_a[i]; + for (int k = 0; k < SIZE(sig_s); k++) { + if (ref != Sx && sig_b[k*SIZE(sig_a) + i] != Sx && ref != sig_b[k*SIZE(sig_a) + i]) + goto no_match_ab; + if (sig_b[k*SIZE(sig_a) + i] != Sx) + ref = sig_b[k*SIZE(sig_a) + i]; + } + if (0) + no_match_ab: + break; + bits_removed.push_back(ref); + } + + if (bits_removed.empty()) + return; + + SigSpec sig_removed; + for (int i = SIZE(bits_removed)-1; i >= 0; i--) + sig_removed.append_bit(bits_removed[i]); + + if (SIZE(bits_removed) == SIZE(sig_y)) { + log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + module->connect(sig_y, sig_removed); + module->remove(cell); + return; + } + + log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", + SIZE(sig_removed), SIZE(sig_y), log_id(module), log_id(cell), log_id(cell->type)); + + int n_removed = SIZE(sig_removed); + int n_kept = SIZE(sig_y) - SIZE(sig_removed); + + SigSpec new_work_queue_bits; + new_work_queue_bits.append(sig_a.extract(n_kept, n_removed)); + new_work_queue_bits.append(sig_y.extract(n_kept, n_removed)); + + SigSpec new_sig_a = sig_a.extract(0, n_kept); + SigSpec new_sig_y = sig_y.extract(0, n_kept); + SigSpec new_sig_b; + + for (int k = 0; k < SIZE(sig_s); k++) { + new_sig_b.append(sig_b.extract(k*SIZE(sig_a), n_kept)); + new_work_queue_bits.append(sig_b.extract(k*SIZE(sig_a) + n_kept, n_removed)); + } + + for (auto bit : new_work_queue_bits) + work_queue_bits.insert(bit); + + cell->setPort("\\A", new_sig_a); + cell->setPort("\\B", new_sig_b); + cell->setPort("\\Y", new_sig_y); + cell->fixup_parameters(); + + module->connect(sig_y.extract(n_kept, n_removed), sig_removed); + } + + void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something) + { + port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool(); + SigSpec sig = mi.sigmap(cell->getPort(stringf("\\%c", port))); + + if (port == 'B' && cell->type.in("$shl", "$shr", "$sshl", "$sshr")) + port_signed = false; + + int bits_removed = 0; + if (SIZE(sig) > max_port_size) { + bits_removed = SIZE(sig) - max_port_size; + for (auto bit : sig.extract(max_port_size, bits_removed)) + work_queue_bits.insert(bit); + sig = sig.extract(0, max_port_size); + } + + if (port_signed) { + while (SIZE(sig) > 1 && sig[SIZE(sig)-1] == sig[SIZE(sig)-2]) + work_queue_bits.insert(sig[SIZE(sig)-1]), sig.remove(SIZE(sig)-1), bits_removed++; + } else { + while (SIZE(sig) > 1 && sig[SIZE(sig)-1] == S0) + work_queue_bits.insert(sig[SIZE(sig)-1]), sig.remove(SIZE(sig)-1), bits_removed++; + } + + if (bits_removed) { + log("Removed top %d bits (of %d) from port %c of cell %s.%s (%s).\n", + bits_removed, SIZE(sig) + bits_removed, port, log_id(module), log_id(cell), log_id(cell->type)); + cell->setPort(stringf("\\%c", port), sig); + did_something = true; + } + } + + void run_cell(Cell *cell) + { + bool did_something = false; + + if (!cell->type.in(config->supported_cell_types)) + return; + + if (cell->type.in("$mux", "$pmux")) + return run_cell_mux(cell); + + + // Reduce size of ports A and B based on constant input bits and size of output port + + int max_port_a_size = cell->hasPort("\\A") ? SIZE(cell->getPort("\\A")) : -1; + int max_port_b_size = cell->hasPort("\\B") ? SIZE(cell->getPort("\\B")) : -1; + + if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) { + max_port_a_size = std::min(max_port_a_size, SIZE(cell->getPort("\\Y"))); + max_port_b_size = std::min(max_port_b_size, SIZE(cell->getPort("\\Y"))); + } + + bool port_a_signed = false; + bool port_b_signed = false; + + if (max_port_a_size >= 0 && cell->type != "$shiftx") + run_reduce_inport(cell, 'A', max_port_a_size, port_a_signed, did_something); + + if (max_port_b_size >= 0) + run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something); + + + // Reduce size of port Y based on sizes for A and B and unused bits in Y + + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + int bits_removed = 0; + if (port_a_signed && cell->type == "$shr") { + // do not reduce size of output on $shr cells with signed A inputs + } else { + while (SIZE(sig) > 0) + { + auto info = mi.query(sig[SIZE(sig)-1]); + + if (info->is_output || SIZE(info->ports) > 1) + break; + + sig.remove(SIZE(sig)-1); + bits_removed++; + } + } + + if (cell->type.in("$pos", "$add", "$mul", "$and", "$or", "$xor")) + { + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + + int a_size = 0, b_size = 0; + if (cell->hasPort("\\A")) a_size = SIZE(cell->getPort("\\A")); + if (cell->hasPort("\\B")) b_size = SIZE(cell->getPort("\\B")); + + int max_y_size = std::max(a_size, b_size); + + if (cell->type == "$add") + max_y_size++; + + if (cell->type == "$mul") + max_y_size = a_size + b_size; + + while (SIZE(sig) > 1 && SIZE(sig) > max_y_size) { + module->connect(sig[SIZE(sig)-1], is_signed ? sig[SIZE(sig)-2] : S0); + sig.remove(SIZE(sig)-1); + bits_removed++; + } + } + + if (SIZE(sig) == 0) { + log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + module->remove(cell); + return; + } + + if (bits_removed) { + log("Removed top %d bits (of %d) from port Y of cell %s.%s (%s).\n", + bits_removed, SIZE(sig) + bits_removed, log_id(module), log_id(cell), log_id(cell->type)); + cell->setPort("\\Y", sig); + did_something = true; + } + + if (did_something) { + cell->fixup_parameters(); + run_cell(cell); + } + } + + static int count_nontrivial_wire_attrs(RTLIL::Wire *w) + { + int count = w->attributes.size(); + count -= w->attributes.count("\\src"); + count -= w->attributes.count("\\unused_bits"); + return count; + } + + void run() + { + for (auto c : module->selected_cells()) + work_queue_cells.insert(c); + + while (!work_queue_cells.empty()) + { + work_queue_bits.clear(); + for (auto c : work_queue_cells) + run_cell(c); + + work_queue_cells.clear(); + for (auto bit : work_queue_bits) + for (auto port : mi.query_ports(bit)) + if (module->selected(port.cell)) + work_queue_cells.insert(port.cell); + } + + for (auto w : module->selected_wires()) + { + int unused_top_bits = 0; + + if (w->port_id > 0 || count_nontrivial_wire_attrs(w) > 0) + continue; + + for (int i = SIZE(w)-1; i >= 0; i--) { + SigBit bit(w, i); + auto info = mi.query(bit); + if (info && (info->is_input || info->is_output || SIZE(info->ports) > 0)) + break; + unused_top_bits++; + } + + if (0 < unused_top_bits && unused_top_bits < SIZE(w)) { + log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, SIZE(w), log_id(module), log_id(w)); + Wire *nw = module->addWire(NEW_ID, w); + nw->width = SIZE(w) - unused_top_bits; + module->connect(nw, SigSpec(w).extract(0, SIZE(nw))); + module->swap_names(w, nw); + } + } + } +}; + +struct WreducePass : public Pass { + WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" wreduce [options] [selection]\n"); + log("\n"); + log("This command reduces the word size of operations. For example it will replace\n"); + log("the 32 bit adders in the following code with adders of more appropriate widths:\n"); + log("\n"); + log(" module test(input [3:0] a, b, c, output [7:0] y);\n"); + log(" assign y = a + b + c + 1;\n"); + log(" endmodule\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + WreduceConfig config; + + log_header("Executing WREDUCE pass (reducing word size of cells).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + if (module->has_processes_warn()) + continue; + + WreduceWorker worker(&config, module); + worker.run(); + } + } +} WreducePass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 571946573..f11b328f0 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -28,47 +28,52 @@ extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count static bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity) { - if (signal.width != 1) + if (signal.size() != 1) return false; if (signal == ref) return true; - for (auto &cell_it : mod->cells) { - RTLIL::Cell *cell = cell_it.second; - if (cell->type == "$reduce_or" && cell->connections["\\Y"] == signal) - return check_signal(mod, cell->connections["\\A"], ref, polarity); - if (cell->type == "$reduce_bool" && cell->connections["\\Y"] == signal) - return check_signal(mod, cell->connections["\\A"], ref, polarity); - if (cell->type == "$logic_not" && cell->connections["\\Y"] == signal) { + for (auto cell : mod->cells()) + { + if (cell->type == "$reduce_or" && cell->getPort("\\Y") == signal) + return check_signal(mod, cell->getPort("\\A"), ref, polarity); + + if (cell->type == "$reduce_bool" && cell->getPort("\\Y") == signal) + return check_signal(mod, cell->getPort("\\A"), ref, polarity); + + if (cell->type == "$logic_not" && cell->getPort("\\Y") == signal) { polarity = !polarity; - return check_signal(mod, cell->connections["\\A"], ref, polarity); + return check_signal(mod, cell->getPort("\\A"), ref, polarity); } - if (cell->type == "$not" && cell->connections["\\Y"] == signal) { + + if (cell->type == "$not" && cell->getPort("\\Y") == signal) { polarity = !polarity; - return check_signal(mod, cell->connections["\\A"], ref, polarity); + return check_signal(mod, cell->getPort("\\A"), ref, polarity); } - if ((cell->type == "$eq" || cell->type == "$eqx") && cell->connections["\\Y"] == signal) { - if (cell->connections["\\A"].is_fully_const()) { - if (!cell->connections["\\A"].as_bool()) + + if ((cell->type == "$eq" || cell->type == "$eqx") && cell->getPort("\\Y") == signal) { + if (cell->getPort("\\A").is_fully_const()) { + if (!cell->getPort("\\A").as_bool()) polarity = !polarity; - return check_signal(mod, cell->connections["\\B"], ref, polarity); + return check_signal(mod, cell->getPort("\\B"), ref, polarity); } - if (cell->connections["\\B"].is_fully_const()) { - if (!cell->connections["\\B"].as_bool()) + if (cell->getPort("\\B").is_fully_const()) { + if (!cell->getPort("\\B").as_bool()) polarity = !polarity; - return check_signal(mod, cell->connections["\\A"], ref, polarity); + return check_signal(mod, cell->getPort("\\A"), ref, polarity); } } - if ((cell->type == "$ne" || cell->type == "$nex") && cell->connections["\\Y"] == signal) { - if (cell->connections["\\A"].is_fully_const()) { - if (cell->connections["\\A"].as_bool()) + + if ((cell->type == "$ne" || cell->type == "$nex") && cell->getPort("\\Y") == signal) { + if (cell->getPort("\\A").is_fully_const()) { + if (cell->getPort("\\A").as_bool()) polarity = !polarity; - return check_signal(mod, cell->connections["\\B"], ref, polarity); + return check_signal(mod, cell->getPort("\\B"), ref, polarity); } - if (cell->connections["\\B"].is_fully_const()) { - if (cell->connections["\\B"].as_bool()) + if (cell->getPort("\\B").is_fully_const()) { + if (cell->getPort("\\B").as_bool()) polarity = !polarity; - return check_signal(mod, cell->connections["\\A"], ref, polarity); + return check_signal(mod, cell->getPort("\\A"), ref, polarity); } } } @@ -80,13 +85,13 @@ static void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::S { for (auto &action : cs->actions) { if (unknown) - rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.width), &rval); + rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.size()), &rval); else rspec.replace(action.first, action.second, &rval); } for (auto sw : cs->switches) { - if (sw->signal.width == 0) { + if (sw->signal.size() == 0) { for (auto cs2 : sw->cases) apply_const(mod, rspec, rval, cs2, const_sig, polarity, unknown); } @@ -156,16 +161,18 @@ restart_proc_arst: if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { bool polarity = sync->type == RTLIL::SyncType::STp; if (check_signal(mod, root_sig, sync->signal, polarity)) { - log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); - sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; + if (proc->syncs.size() == 1) { + log("Found VHDL-style edge-trigger %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); + } else { + log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); + sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; + } for (auto &action : sync->actions) { RTLIL::SigSpec rspec = action.second; - RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.width); - rspec.expand(), rval.expand(); - for (int i = 0; i < int(rspec.chunks.size()); i++) - if (rspec.chunks[i].wire == NULL) - rval.chunks[i] = rspec.chunks[i]; - rspec.optimize(), rval.optimize(); + RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); + for (int i = 0; i < SIZE(rspec); i++) + if (rspec[i].wire == NULL) + rval[i] = rspec[i]; RTLIL::SigSpec last_rval; for (int count = 0; rval != last_rval; count++) { last_rval = rval; @@ -234,28 +241,28 @@ struct ProcArstPass : public Pass { extra_args(args, argidx, design); - for (auto &mod_it : design->modules) - if (design->selected(mod_it.second)) { - SigMap assign_map(mod_it.second); - for (auto &proc_it : mod_it.second->processes) { - if (!design->selected(mod_it.second, proc_it.second)) + for (auto mod : design->modules()) + if (design->selected(mod)) { + SigMap assign_map(mod); + for (auto &proc_it : mod->processes) { + if (!design->selected(mod, proc_it.second)) continue; - proc_arst(mod_it.second, proc_it.second, assign_map); - if (global_arst.empty() || mod_it.second->wires.count(global_arst) == 0) + proc_arst(mod, proc_it.second, assign_map); + if (global_arst.empty() || mod->wire(global_arst) == nullptr) continue; std::vector<RTLIL::SigSig> arst_actions; for (auto sync : proc_it.second->syncs) if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) for (auto &act : sync->actions) { RTLIL::SigSpec arst_sig, arst_val; - for (auto &chunk : act.first.chunks) + for (auto &chunk : act.first.chunks()) if (chunk.wire && chunk.wire->attributes.count("\\init")) { RTLIL::SigSpec value = chunk.wire->attributes.at("\\init"); value.extend(chunk.wire->width, false); arst_sig.append(chunk); arst_val.append(value.extract(chunk.offset, chunk.width)); } - if (arst_sig.width) { + if (arst_sig.size()) { log("Added global reset to process %s: %s <- %s\n", proc_it.first.c_str(), log_signal(arst_sig), log_signal(arst_val)); arst_actions.push_back(RTLIL::SigSig(arst_sig, arst_val)); @@ -264,7 +271,7 @@ struct ProcArstPass : public Pass { if (!arst_actions.empty()) { RTLIL::SyncRule *sync = new RTLIL::SyncRule; sync->type = global_arst_neg ? RTLIL::SyncType::ST0 : RTLIL::SyncType::ST1; - sync->signal = mod_it.second->wires.at(global_arst); + sync->signal = mod->wire(global_arst); sync->actions = arst_actions; proc_it.second->syncs.push_back(sync); } diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 83554df90..1e3dd9ce7 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -27,7 +27,7 @@ extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count, int max_depth) { - if (sw->signal.width > 0 && sw->signal.is_fully_const()) + if (sw->signal.size() > 0 && sw->signal.is_fully_const()) { int found_matching_case_idx = -1; for (int i = 0; i < int(sw->cases.size()) && found_matching_case_idx < 0; i++) @@ -59,7 +59,8 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did sw->signal = RTLIL::SigSpec(); } - if (sw->cases.size() == 1 && (sw->signal.width == 0 || sw->cases[0]->compare.empty())) + if (parent->switches.front() == sw && sw->cases.size() == 1 && + (sw->signal.size() == 0 || sw->cases[0]->compare.empty())) { did_something = true; for (auto &action : sw->cases[0]->actions) @@ -91,7 +92,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth) { for (size_t i = 0; i < cs->actions.size(); i++) { - if (cs->actions[i].first.width == 0) { + if (cs->actions[i].first.size() == 0) { did_something = true; cs->actions.erase(cs->actions.begin() + (i--)); } @@ -114,7 +115,7 @@ static void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_coun bool did_something = true; for (size_t i = 0; i < proc->syncs.size(); i++) { for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++) - if (proc->syncs[i]->actions[j].first.width == 0) + if (proc->syncs[i]->actions[j].first.size() == 0) proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--)); if (proc->syncs[i]->actions.size() == 0) { delete proc->syncs[i]; @@ -149,23 +150,23 @@ struct ProcCleanPass : public Pass { extra_args(args, 1, design); - for (auto &mod_it : design->modules) { - std::vector<std::string> delme; - if (!design->selected(mod_it.second)) + for (auto mod : design->modules()) { + std::vector<RTLIL::IdString> delme; + if (!design->selected(mod)) continue; - for (auto &proc_it : mod_it.second->processes) { - if (!design->selected(mod_it.second, proc_it.second)) + for (auto &proc_it : mod->processes) { + if (!design->selected(mod, proc_it.second)) continue; - proc_clean(mod_it.second, proc_it.second, total_count); + proc_clean(mod, proc_it.second, total_count); if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 && proc_it.second->root_case.actions.size() == 0) { - log("Removing empty process `%s.%s'.\n", mod_it.first.c_str(), proc_it.second->name.c_str()); + log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); delme.push_back(proc_it.first); } } for (auto &id : delme) { - delete mod_it.second->processes[id]; - mod_it.second->processes.erase(id); + delete mod->processes[id]; + mod->processes.erase(id); } } diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 2ec498fb2..e69e8023d 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -24,7 +24,6 @@ #include <sstream> #include <stdlib.h> #include <stdio.h> -#include <assert.h> static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) { @@ -32,7 +31,7 @@ static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) for (auto sync : proc->syncs) for (auto &action : sync->actions) - if (action.first.width > 0) { + if (action.first.size() > 0) { lvalue = action.first; lvalue.sort_and_unify(); break; @@ -44,7 +43,7 @@ static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) this_lvalue.append(action.first); this_lvalue.sort_and_unify(); RTLIL::SigSpec common_sig = this_lvalue.extract(lvalue); - if (common_sig.width > 0) + if (common_sig.size() > 0) lvalue = common_sig; } @@ -54,8 +53,8 @@ static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) static void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, RTLIL::SigSpec clk, bool clk_polarity, std::map<RTLIL::SigSpec, std::set<RTLIL::SyncRule*>> &async_rules, RTLIL::Process *proc) { - RTLIL::SigSpec sig_sr_set = RTLIL::SigSpec(0, sig_d.width); - RTLIL::SigSpec sig_sr_clr = RTLIL::SigSpec(0, sig_d.width); + RTLIL::SigSpec sig_sr_set = RTLIL::SigSpec(0, sig_d.size()); + RTLIL::SigSpec sig_sr_clr = RTLIL::SigSpec(0, sig_d.size()); for (auto &it : async_rules) { @@ -72,91 +71,70 @@ static void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::S else log_abort(); - if (sync_low_signals.width > 1) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$reduce_or"; + if (sync_low_signals.size() > 1) { + RTLIL::Cell *cell = mod->addCell(NEW_ID, "$reduce_or"); cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_low_signals.width); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_low_signals.size()); cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - cell->connections["\\A"] = sync_low_signals; - cell->connections["\\Y"] = sync_low_signals = NEW_WIRE(mod, 1); - mod->add(cell); + cell->setPort("\\A", sync_low_signals); + cell->setPort("\\Y", sync_low_signals = mod->addWire(NEW_ID)); } - if (sync_low_signals.width > 0) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$not"; + if (sync_low_signals.size() > 0) { + RTLIL::Cell *cell = mod->addCell(NEW_ID, "$not"); cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_low_signals.width); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_low_signals.size()); cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - cell->connections["\\A"] = sync_low_signals; - cell->connections["\\Y"] = NEW_WIRE(mod, 1); - sync_high_signals.append(cell->connections["\\Y"]); - mod->add(cell); + cell->setPort("\\A", sync_low_signals); + cell->setPort("\\Y", mod->addWire(NEW_ID)); + sync_high_signals.append(cell->getPort("\\Y")); } - if (sync_high_signals.width > 1) { - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$reduce_or"; + if (sync_high_signals.size() > 1) { + RTLIL::Cell *cell = mod->addCell(NEW_ID, "$reduce_or"); cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_high_signals.width); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_high_signals.size()); cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - cell->connections["\\A"] = sync_high_signals; - cell->connections["\\Y"] = sync_high_signals = NEW_WIRE(mod, 1); - mod->add(cell); + cell->setPort("\\A", sync_high_signals); + cell->setPort("\\Y", sync_high_signals = mod->addWire(NEW_ID)); } - RTLIL::Cell *inv_cell = new RTLIL::Cell; - inv_cell->name = NEW_ID; - inv_cell->type = "$not"; + RTLIL::Cell *inv_cell = mod->addCell(NEW_ID, "$not"); inv_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); - inv_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_d.width); - inv_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(sig_d.width); - inv_cell->connections["\\A"] = sync_value; - inv_cell->connections["\\Y"] = sync_value_inv = NEW_WIRE(mod, sig_d.width); - mod->add(inv_cell); - - RTLIL::Cell *mux_set_cell = new RTLIL::Cell; - mux_set_cell->name = NEW_ID; - mux_set_cell->type = "$mux"; - mux_set_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.width); - mux_set_cell->connections["\\A"] = sig_sr_set; - mux_set_cell->connections["\\B"] = sync_value; - mux_set_cell->connections["\\S"] = sync_high_signals; - mux_set_cell->connections["\\Y"] = sig_sr_set = NEW_WIRE(mod, sig_d.width); - mod->add(mux_set_cell); - - RTLIL::Cell *mux_clr_cell = new RTLIL::Cell; - mux_clr_cell->name = NEW_ID; - mux_clr_cell->type = "$mux"; - mux_clr_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.width); - mux_clr_cell->connections["\\A"] = sig_sr_clr; - mux_clr_cell->connections["\\B"] = sync_value_inv; - mux_clr_cell->connections["\\S"] = sync_high_signals; - mux_clr_cell->connections["\\Y"] = sig_sr_clr = NEW_WIRE(mod, sig_d.width); - mod->add(mux_clr_cell); + inv_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_d.size()); + inv_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(sig_d.size()); + inv_cell->setPort("\\A", sync_value); + inv_cell->setPort("\\Y", sync_value_inv = mod->addWire(NEW_ID, sig_d.size())); + + RTLIL::Cell *mux_set_cell = mod->addCell(NEW_ID, "$mux"); + mux_set_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.size()); + mux_set_cell->setPort("\\A", sig_sr_set); + mux_set_cell->setPort("\\B", sync_value); + mux_set_cell->setPort("\\S", sync_high_signals); + mux_set_cell->setPort("\\Y", sig_sr_set = mod->addWire(NEW_ID, sig_d.size())); + + RTLIL::Cell *mux_clr_cell = mod->addCell(NEW_ID, "$mux"); + mux_clr_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.size()); + mux_clr_cell->setPort("\\A", sig_sr_clr); + mux_clr_cell->setPort("\\B", sync_value_inv); + mux_clr_cell->setPort("\\S", sync_high_signals); + mux_clr_cell->setPort("\\Y", sig_sr_clr = mod->addWire(NEW_ID, sig_d.size())); } std::stringstream sstr; - sstr << "$procdff$" << (RTLIL::autoidx++); + sstr << "$procdff$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = sstr.str(); - cell->type = "$dffsr"; + RTLIL::Cell *cell = mod->addCell(sstr.str(), "$dffsr"); cell->attributes = proc->attributes; - cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.width); + cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.size()); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1); cell->parameters["\\SET_POLARITY"] = RTLIL::Const(true, 1); cell->parameters["\\CLR_POLARITY"] = RTLIL::Const(true, 1); - cell->connections["\\D"] = sig_d; - cell->connections["\\Q"] = sig_q; - cell->connections["\\CLK"] = clk; - cell->connections["\\SET"] = sig_sr_set; - cell->connections["\\CLR"] = sig_sr_clr; - mod->add(cell); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + cell->setPort("\\CLK", clk); + cell->setPort("\\SET", sig_sr_set); + cell->setPort("\\CLR", sig_sr_clr); log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); @@ -166,56 +144,44 @@ static void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec bool clk_polarity, bool set_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec set, RTLIL::Process *proc) { std::stringstream sstr; - sstr << "$procdff$" << (RTLIL::autoidx++); + sstr << "$procdff$" << (autoidx++); - RTLIL::SigSpec sig_set_inv = NEW_WIRE(mod, sig_in.width); - RTLIL::SigSpec sig_sr_set = NEW_WIRE(mod, sig_in.width); - RTLIL::SigSpec sig_sr_clr = NEW_WIRE(mod, sig_in.width); + RTLIL::SigSpec sig_set_inv = mod->addWire(NEW_ID, sig_in.size()); + RTLIL::SigSpec sig_sr_set = mod->addWire(NEW_ID, sig_in.size()); + RTLIL::SigSpec sig_sr_clr = mod->addWire(NEW_ID, sig_in.size()); - RTLIL::Cell *inv_set = new RTLIL::Cell; - inv_set->name = NEW_ID; - inv_set->type = "$not"; + RTLIL::Cell *inv_set = mod->addCell(NEW_ID, "$not"); inv_set->parameters["\\A_SIGNED"] = RTLIL::Const(0); - inv_set->parameters["\\A_WIDTH"] = RTLIL::Const(sig_in.width); - inv_set->parameters["\\Y_WIDTH"] = RTLIL::Const(sig_in.width); - inv_set->connections["\\A"] = sig_set; - inv_set->connections["\\Y"] = sig_set_inv; - mod->add(inv_set); - - RTLIL::Cell *mux_sr_set = new RTLIL::Cell; - mux_sr_set->name = NEW_ID; - mux_sr_set->type = "$mux"; - mux_sr_set->parameters["\\WIDTH"] = RTLIL::Const(sig_in.width); - mux_sr_set->connections[set_polarity ? "\\A" : "\\B"] = RTLIL::Const(0, sig_in.width); - mux_sr_set->connections[set_polarity ? "\\B" : "\\A"] = sig_set; - mux_sr_set->connections["\\Y"] = sig_sr_set; - mux_sr_set->connections["\\S"] = set; - mod->add(mux_sr_set); - - RTLIL::Cell *mux_sr_clr = new RTLIL::Cell; - mux_sr_clr->name = NEW_ID; - mux_sr_clr->type = "$mux"; - mux_sr_clr->parameters["\\WIDTH"] = RTLIL::Const(sig_in.width); - mux_sr_clr->connections[set_polarity ? "\\A" : "\\B"] = RTLIL::Const(0, sig_in.width); - mux_sr_clr->connections[set_polarity ? "\\B" : "\\A"] = sig_set_inv; - mux_sr_clr->connections["\\Y"] = sig_sr_clr; - mux_sr_clr->connections["\\S"] = set; - mod->add(mux_sr_clr); - - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = sstr.str(); - cell->type = "$dffsr"; + inv_set->parameters["\\A_WIDTH"] = RTLIL::Const(sig_in.size()); + inv_set->parameters["\\Y_WIDTH"] = RTLIL::Const(sig_in.size()); + inv_set->setPort("\\A", sig_set); + inv_set->setPort("\\Y", sig_set_inv); + + RTLIL::Cell *mux_sr_set = mod->addCell(NEW_ID, "$mux"); + mux_sr_set->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size()); + mux_sr_set->setPort(set_polarity ? "\\A" : "\\B", RTLIL::Const(0, sig_in.size())); + mux_sr_set->setPort(set_polarity ? "\\B" : "\\A", sig_set); + mux_sr_set->setPort("\\Y", sig_sr_set); + mux_sr_set->setPort("\\S", set); + + RTLIL::Cell *mux_sr_clr = mod->addCell(NEW_ID, "$mux"); + mux_sr_clr->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size()); + mux_sr_clr->setPort(set_polarity ? "\\A" : "\\B", RTLIL::Const(0, sig_in.size())); + mux_sr_clr->setPort(set_polarity ? "\\B" : "\\A", sig_set_inv); + mux_sr_clr->setPort("\\Y", sig_sr_clr); + mux_sr_clr->setPort("\\S", set); + + RTLIL::Cell *cell = mod->addCell(sstr.str(), "$dffsr"); cell->attributes = proc->attributes; - cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.width); + cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size()); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1); cell->parameters["\\SET_POLARITY"] = RTLIL::Const(true, 1); cell->parameters["\\CLR_POLARITY"] = RTLIL::Const(true, 1); - cell->connections["\\D"] = sig_in; - cell->connections["\\Q"] = sig_out; - cell->connections["\\CLK"] = clk; - cell->connections["\\SET"] = sig_sr_set; - cell->connections["\\CLR"] = sig_sr_clr; - mod->add(cell); + cell->setPort("\\D", sig_in); + cell->setPort("\\Q", sig_out); + cell->setPort("\\CLK", clk); + cell->setPort("\\SET", sig_sr_set); + cell->setPort("\\CLR", sig_sr_clr); log(" created %s cell `%s' with %s edge clock and %s level non-const reset.\n", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative", set_polarity ? "positive" : "negative"); @@ -225,26 +191,23 @@ static void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_ bool clk_polarity, bool arst_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec *arst, RTLIL::Process *proc) { std::stringstream sstr; - sstr << "$procdff$" << (RTLIL::autoidx++); + sstr << "$procdff$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = sstr.str(); - cell->type = arst ? "$adff" : "$dff"; + RTLIL::Cell *cell = mod->addCell(sstr.str(), arst ? "$adff" : "$dff"); cell->attributes = proc->attributes; - mod->cells[cell->name] = cell; - cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.width); + cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size()); if (arst) { cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity, 1); cell->parameters["\\ARST_VALUE"] = val_rst; } cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1); - cell->connections["\\D"] = sig_in; - cell->connections["\\Q"] = sig_out; + cell->setPort("\\D", sig_in); + cell->setPort("\\Q", sig_out); if (arst) - cell->connections["\\ARST"] = *arst; - cell->connections["\\CLK"] = clk; + cell->setPort("\\ARST", *arst); + cell->setPort("\\CLK", clk); log(" created %s cell `%s' with %s edge clock", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); if (arst) @@ -259,14 +222,14 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) RTLIL::SigSpec sig = find_any_lvalue(proc); bool free_sync_level = false; - if (sig.width == 0) + if (sig.size() == 0) break; log("Creating register for signal `%s.%s' using process `%s.%s'.\n", mod->name.c_str(), log_signal(sig), mod->name.c_str(), proc->name.c_str()); - RTLIL::SigSpec insig = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); - RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); + RTLIL::SigSpec insig = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); + RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); RTLIL::SyncRule *sync_level = NULL; RTLIL::SyncRule *sync_edge = NULL; RTLIL::SyncRule *sync_always = NULL; @@ -276,16 +239,16 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) for (auto sync : proc->syncs) for (auto &action : sync->actions) { - if (action.first.extract(sig).width == 0) + if (action.first.extract(sig).size() == 0) continue; if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) { if (sync_level != NULL && sync_level != sync) { // log_error("Multiple level sensitive events found for this signal!\n"); many_async_rules[rstval].insert(sync_level); - rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); + rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); } - rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); + rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); sig.replace(action.first, action.second, &rstval); sync_level = sync; } @@ -315,7 +278,7 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) { sync_level = new RTLIL::SyncRule; sync_level->type = RTLIL::SyncType::ST1; - sync_level->signal = NEW_WIRE(mod, 1); + sync_level->signal = mod->addWire(NEW_ID); sync_level->actions.push_back(RTLIL::SigSig(sig, rstval)); free_sync_level = true; @@ -324,26 +287,23 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) inputs.append(it->signal); compare.append(it->type == RTLIL::SyncType::ST0 ? RTLIL::State::S1 : RTLIL::State::S0); } - assert(inputs.width == compare.width); + log_assert(inputs.size() == compare.size()); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = "$ne"; + RTLIL::Cell *cell = mod->addCell(NEW_ID, "$ne"); cell->parameters["\\A_SIGNED"] = RTLIL::Const(false, 1); cell->parameters["\\B_SIGNED"] = RTLIL::Const(false, 1); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(inputs.width); - cell->parameters["\\B_WIDTH"] = RTLIL::Const(inputs.width); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(inputs.size()); + cell->parameters["\\B_WIDTH"] = RTLIL::Const(inputs.size()); cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - cell->connections["\\A"] = inputs; - cell->connections["\\B"] = compare; - cell->connections["\\Y"] = sync_level->signal; - mod->add(cell); + cell->setPort("\\A", inputs); + cell->setPort("\\B", compare); + cell->setPort("\\Y", sync_level->signal); many_async_rules.clear(); } else { - rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); + rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); sync_level = NULL; } } @@ -352,15 +312,16 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) ce.assign_map.apply(rstval); ce.assign_map.apply(sig); - insig.optimize(); - rstval.optimize(); - sig.optimize(); + if (rstval == sig) { + rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); + sync_level = NULL; + } if (sync_always) { if (sync_edge || sync_level || many_async_rules.size() > 0) log_error("Mixed always event with edge and/or level sensitive events!\n"); log(" created direct connection (no actual register cell created).\n"); - mod->connections.push_back(RTLIL::SigSig(sig, insig)); + mod->connect(RTLIL::SigSig(sig, insig)); continue; } @@ -381,7 +342,7 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) sync_edge->signal, sync_level->signal, proc); } else - gen_dff(mod, insig, rstval.chunks[0].data, sig, + gen_dff(mod, insig, rstval.as_const(), sig, sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge->signal, sync_level ? &sync_level->signal : NULL, proc); @@ -409,12 +370,12 @@ struct ProcDffPass : public Pass { extra_args(args, 1, design); - for (auto &mod_it : design->modules) - if (design->selected(mod_it.second)) { - ConstEval ce(mod_it.second); - for (auto &proc_it : mod_it.second->processes) - if (design->selected(mod_it.second, proc_it.second)) - proc_dff(mod_it.second, proc_it.second, ce); + for (auto mod : design->modules()) + if (design->selected(mod)) { + ConstEval ce(mod); + for (auto &proc_it : mod->processes) + if (design->selected(mod, proc_it.second)) + proc_dff(mod, proc_it.second, ce); } } } ProcDffPass; diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index fca20fda2..c72840c02 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -25,10 +25,9 @@ static void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) { - assert(rule.compare.size() == 0); + log_assert(rule.compare.size() == 0); while (1) { - sig.optimize(); RTLIL::SigSpec tmp = sig; for (auto &it : rule.actions) tmp.replace(it.first, it.second); @@ -53,23 +52,21 @@ static void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) RTLIL::SigSpec lhs = action.first; RTLIL::SigSpec rhs = action.second; - lhs.optimize(); proc_get_const(rhs, proc->root_case); if (!rhs.is_fully_const()) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); int offset = 0; - for (size_t i = 0; i < lhs.chunks.size(); i++) { - if (lhs.chunks[i].wire == NULL) - continue; - RTLIL::Wire *wire = lhs.chunks[i].wire; - RTLIL::SigSpec value = rhs.extract(offset, lhs.chunks[i].width); - if (value.width != wire->width) - log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs.chunks[i]), log_signal(value)); - log(" Setting init value: %s = %s\n", log_signal(wire), log_signal(value)); - wire->attributes["\\init"] = value.as_const(); - offset += wire->width; + for (auto &lhs_c : lhs.chunks()) { + if (lhs_c.wire != NULL) { + RTLIL::SigSpec value = rhs.extract(offset, lhs_c.width); + if (value.size() != lhs_c.wire->width) + log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs_c), log_signal(value)); + log(" Setting init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(value)); + lhs_c.wire->attributes["\\init"] = value.as_const(); + } + offset += lhs_c.width; } } } @@ -104,11 +101,11 @@ struct ProcInitPass : public Pass { extra_args(args, 1, design); - for (auto &mod_it : design->modules) - if (design->selected(mod_it.second)) - for (auto &proc_it : mod_it.second->processes) - if (design->selected(mod_it.second, proc_it.second)) - proc_init(mod_it.second, proc_it.second); + for (auto mod : design->modules()) + if (design->selected(mod)) + for (auto &proc_it : mod->processes) + if (design->selected(mod, proc_it.second)) + proc_init(mod, proc_it.second); } } ProcInitPass; diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 9b2f83885..c00b00a2a 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -23,19 +23,18 @@ #include <sstream> #include <stdlib.h> #include <stdio.h> -#include <assert.h> static RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) { for (auto &action : cs->actions) { - if (action.first.width) + if (action.first.size()) return action.first; } for (auto sw : cs->switches) for (auto cs2 : sw->cases) { RTLIL::SigSpec sig = find_any_lvalue(cs2); - if (sig.width) + if (sig.size()) return sig; } @@ -46,7 +45,7 @@ static void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) { for (auto &action : cs->actions) { RTLIL::SigSpec lvalue = action.first.extract(sig); - if (lvalue.width) + if (lvalue.size()) sig = lvalue; } @@ -58,56 +57,44 @@ static void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) { std::stringstream sstr; - sstr << "$procmux$" << (RTLIL::autoidx++); + sstr << "$procmux$" << (autoidx++); - RTLIL::Wire *cmp_wire = new RTLIL::Wire; - cmp_wire->name = sstr.str() + "_CMP"; - cmp_wire->width = 0; - mod->wires[cmp_wire->name] = cmp_wire; + RTLIL::Wire *cmp_wire = mod->addWire(sstr.str() + "_CMP", 0); for (auto comp : compare) { RTLIL::SigSpec sig = signal; - sig.expand(); - comp.expand(); // get rid of don't-care bits - assert(sig.width == comp.width); - for (int i = 0; i < comp.width; i++) - if (comp.chunks[i].wire == NULL && comp.chunks[i].data.bits[0] == RTLIL::State::Sa) { - sig.remove(i, 1); - comp.remove(i--, 1); + log_assert(sig.size() == comp.size()); + for (int i = 0; i < comp.size(); i++) + if (comp[i] == RTLIL::State::Sa) { + sig.remove(i); + comp.remove(i--); } - if (comp.width == 0) + if (comp.size() == 0) return RTLIL::SigSpec(); - sig.optimize(); - comp.optimize(); - if (sig.width == 1 && comp == RTLIL::SigSpec(1,1)) + if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1)) { - mod->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, 1, cmp_wire->width++), sig)); + mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); } else { // create compare cell - RTLIL::Cell *eq_cell = new RTLIL::Cell; - std::stringstream sstr2; - sstr2 << sstr.str() << "_CMP" << cmp_wire->width; - eq_cell->name = sstr2.str(); - eq_cell->type = "$eq"; + RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), "$eq"); eq_cell->attributes = sw->attributes; - mod->cells[eq_cell->name] = eq_cell; eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0); - eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.width); - eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(comp.width); + eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.size()); + eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(comp.size()); eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - eq_cell->connections["\\A"] = sig; - eq_cell->connections["\\B"] = comp; - eq_cell->connections["\\Y"] = RTLIL::SigSpec(cmp_wire, 1, cmp_wire->width++); + eq_cell->setPort("\\A", sig); + eq_cell->setPort("\\B", comp); + eq_cell->setPort("\\Y", RTLIL::SigSpec(cmp_wire, cmp_wire->width++)); } } @@ -118,24 +105,18 @@ static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, } else { - ctrl_wire = new RTLIL::Wire; - ctrl_wire->name = sstr.str() + "_CTRL"; - ctrl_wire->width = 1; - mod->wires[ctrl_wire->name] = ctrl_wire; + ctrl_wire = mod->addWire(sstr.str() + "_CTRL"); // reduce cmp vector to one logic signal - RTLIL::Cell *any_cell = new RTLIL::Cell; - any_cell->name = sstr.str() + "_ANY"; - any_cell->type = "$reduce_or"; + RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or"); any_cell->attributes = sw->attributes; - mod->cells[any_cell->name] = any_cell; any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width); any_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - any_cell->connections["\\A"] = cmp_wire; - any_cell->connections["\\Y"] = RTLIL::SigSpec(ctrl_wire); + any_cell->setPort("\\A", cmp_wire); + any_cell->setPort("\\Y", RTLIL::SigSpec(ctrl_wire)); } return RTLIL::SigSpec(ctrl_wire); @@ -143,10 +124,10 @@ static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, static RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw) { - assert(when_signal.width == else_signal.width); + log_assert(when_signal.size() == else_signal.size()); std::stringstream sstr; - sstr << "$procmux$" << (RTLIL::autoidx++); + sstr << "$procmux$" << (autoidx++); // the trivial cases if (compare.size() == 0 || when_signal == else_signal) @@ -154,28 +135,22 @@ static RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, // compare results RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); - if (ctrl_sig.width == 0) + if (ctrl_sig.size() == 0) return when_signal; - assert(ctrl_sig.width == 1); + log_assert(ctrl_sig.size() == 1); // prepare multiplexer output signal - RTLIL::Wire *result_wire = new RTLIL::Wire; - result_wire->name = sstr.str() + "_Y"; - result_wire->width = when_signal.width; - mod->wires[result_wire->name] = result_wire; + RTLIL::Wire *result_wire = mod->addWire(sstr.str() + "_Y", when_signal.size()); // create the multiplexer itself - RTLIL::Cell *mux_cell = new RTLIL::Cell; - mux_cell->name = sstr.str(); - mux_cell->type = "$mux"; + RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux"); mux_cell->attributes = sw->attributes; - mod->cells[mux_cell->name] = mux_cell; - mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.width); - mux_cell->connections["\\A"] = else_signal; - mux_cell->connections["\\B"] = when_signal; - mux_cell->connections["\\S"] = ctrl_sig; - mux_cell->connections["\\Y"] = RTLIL::SigSpec(result_wire); + mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size()); + mux_cell->setPort("\\A", else_signal); + mux_cell->setPort("\\B", when_signal); + mux_cell->setPort("\\S", ctrl_sig); + mux_cell->setPort("\\Y", RTLIL::SigSpec(result_wire)); last_mux_cell = mux_cell; return RTLIL::SigSpec(result_wire); @@ -183,15 +158,22 @@ static RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, static void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw) { - assert(last_mux_cell != NULL); - assert(when_signal.width == last_mux_cell->connections["\\A"].width); + log_assert(last_mux_cell != NULL); + log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); - assert(ctrl_sig.width == 1); + log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; - last_mux_cell->connections["\\S"].append(ctrl_sig); - last_mux_cell->connections["\\B"].append(when_signal); - last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->connections["\\S"].width; + + RTLIL::SigSpec new_s = last_mux_cell->getPort("\\S"); + new_s.append(ctrl_sig); + last_mux_cell->setPort("\\S", new_s); + + RTLIL::SigSpec new_b = last_mux_cell->getPort("\\B"); + new_b.append(when_signal); + last_mux_cell->setPort("\\B", new_b); + + last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size(); } static RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) @@ -208,7 +190,7 @@ static RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs // detect groups of parallel cases std::vector<int> pgroups(sw->cases.size()); if (!sw->get_bool_attribute("\\parallel_case")) { - BitPatternPool pool(sw->signal.width); + BitPatternPool pool(sw->signal.size()); bool extra_group_for_next_case = false; for (size_t i = 0; i < sw->cases.size(); i++) { RTLIL::CaseRule *cs2 = sw->cases[i]; @@ -224,7 +206,7 @@ static RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs if (cs2->compare.empty()) pgroups[i] = pgroups[i-1]+1; if (pgroups[i] != pgroups[i-1]) - pool = BitPatternPool(sw->signal.width); + pool = BitPatternPool(sw->signal.size()); } for (auto pat : cs2->compare) if (!pat.is_fully_const()) @@ -258,7 +240,7 @@ static void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) { RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case); - if (sig.width == 0) + if (sig.size() == 0) break; if (first) { @@ -270,8 +252,8 @@ static void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) log(" creating decoder for signal `%s'.\n", log_signal(sig)); - RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.width)); - mod->connections.push_back(RTLIL::SigSig(sig, value)); + RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + mod->connect(RTLIL::SigSig(sig, value)); } } @@ -293,11 +275,11 @@ struct ProcMuxPass : public Pass { extra_args(args, 1, design); - for (auto &mod_it : design->modules) - if (design->selected(mod_it.second)) - for (auto &proc_it : mod_it.second->processes) - if (design->selected(mod_it.second, proc_it.second)) - proc_mux(mod_it.second, proc_it.second); + for (auto mod : design->modules()) + if (design->selected(mod)) + for (auto &proc_it : mod->processes) + if (design->selected(mod, proc_it.second)) + proc_mux(mod, proc_it.second); } } ProcMuxPass; diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index d5fbef0d2..fe3532da8 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -23,7 +23,6 @@ #include <sstream> #include <stdlib.h> #include <stdio.h> -#include <assert.h> #include <set> static void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) @@ -32,7 +31,7 @@ static void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) for (size_t i = 0; i < sw->cases.size(); i++) { - bool is_default = sw->cases[i]->compare.size() == 0 && !pool.empty(); + bool is_default = SIZE(sw->cases[i]->compare) == 0 && (!pool.empty() || SIZE(sw->signal) == 0); for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) { RTLIL::SigSpec sig = sw->cases[i]->compare[j]; @@ -79,18 +78,18 @@ struct ProcRmdeadPass : public Pass { extra_args(args, 1, design); int total_counter = 0; - for (auto &mod_it : design->modules) { - if (!design->selected(mod_it.second)) + for (auto mod : design->modules()) { + if (!design->selected(mod)) continue; - for (auto &proc_it : mod_it.second->processes) { - if (!design->selected(mod_it.second, proc_it.second)) + for (auto &proc_it : mod->processes) { + if (!design->selected(mod, proc_it.second)) continue; int counter = 0; for (auto switch_it : proc_it.second->root_case.switches) proc_rmdead(switch_it, counter); if (counter > 0) log("Removed %d dead cases from process %s in module %s.\n", counter, - proc_it.first.c_str(), mod_it.first.c_str()); + proc_it.first.c_str(), log_id(mod)); total_counter += counter; } } diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 5c38cc2cf..f07ad943c 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -44,7 +44,7 @@ struct BruteForceEquivChecker void run_checker(RTLIL::SigSpec &inputs) { - if (inputs.width < mod1_inputs.width) { + if (inputs.size() < mod1_inputs.size()) { RTLIL::SigSpec inputs0 = inputs, inputs1 = inputs; inputs0.append(RTLIL::Const(0, 1)); inputs1.append(RTLIL::Const(1, 1)); @@ -53,8 +53,6 @@ struct BruteForceEquivChecker return; } - inputs.optimize(); - ConstEval ce1(mod1), ce2(mod2); ce1.set(mod1_inputs, inputs.as_const()); ce2.set(mod2_inputs, inputs.as_const()); @@ -70,11 +68,9 @@ struct BruteForceEquivChecker log_signal(undef2), log_signal(mod1_inputs), log_signal(inputs)); if (ignore_x_mod1) { - sig1.expand(), sig2.expand(); - for (size_t i = 0; i < sig1.chunks.size(); i++) - if (sig1.chunks.at(i) == RTLIL::SigChunk(RTLIL::State::Sx)) - sig2.chunks.at(i) = RTLIL::SigChunk(RTLIL::State::Sx); - sig1.optimize(), sig2.optimize(); + for (int i = 0; i < SIZE(sig1); i++) + if (sig1[i] == RTLIL::State::Sx) + sig2[i] = RTLIL::State::Sx; } if (sig1 != sig2) { @@ -91,16 +87,16 @@ struct BruteForceEquivChecker mod1(mod1), mod2(mod2), counter(0), errors(0), ignore_x_mod1(ignore_x_mod1) { log("Checking for equivialence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str()); - for (auto &w : mod1->wires) + for (auto &w : mod1->wires_) { RTLIL::Wire *wire1 = w.second; if (wire1->port_id == 0) continue; - if (mod2->wires.count(wire1->name) == 0) + if (mod2->wires_.count(wire1->name) == 0) log_cmd_error("Port %s in module 1 has no counterpart in module 2!\n", wire1->name.c_str()); - RTLIL::Wire *wire2 = mod2->wires.at(wire1->name); + RTLIL::Wire *wire2 = mod2->wires_.at(wire1->name); if (wire1->width != wire2->width || wire1->port_input != wire2->port_input || wire1->port_output != wire2->port_output) log_cmd_error("Port %s in module 1 does not match its counterpart in module 2!\n", wire1->name.c_str()); @@ -151,17 +147,17 @@ struct VlogHammerReporter SatGen satgen(&ez, &sigmap); satgen.model_undef = model_undef; - for (auto &c : module->cells) + for (auto &c : module->cells_) if (!satgen.importCell(c.second)) log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); ez.assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); - std::vector<int> y_vec = satgen.importDefSigSpec(module->wires.at("\\y")); + std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y")); std::vector<bool> y_values; if (model_undef) { - std::vector<int> y_undef_vec = satgen.importUndefSigSpec(module->wires.at("\\y")); + std::vector<int> y_undef_vec = satgen.importUndefSigSpec(module->wires_.at("\\y")); y_vec.insert(y_vec.end(), y_undef_vec.begin(), y_undef_vec.end()); } @@ -171,12 +167,11 @@ struct VlogHammerReporter if (!ez.solve(y_vec, y_values)) log_error("Failed to find solution to SAT problem.\n"); - expected_y.expand(); - for (int i = 0; i < expected_y.width; i++) { + for (int i = 0; i < expected_y.size(); i++) { RTLIL::State solution_bit = y_values.at(i) ? RTLIL::State::S1 : RTLIL::State::S0; - RTLIL::State expected_bit = expected_y.chunks.at(i).data.bits.at(0); + RTLIL::State expected_bit = expected_y[i].data; if (model_undef) { - if (y_values.at(expected_y.width+i)) + if (y_values.at(expected_y.size()+i)) solution_bit = RTLIL::State::Sx; } else { if (expected_bit == RTLIL::State::Sx) @@ -184,17 +179,16 @@ struct VlogHammerReporter } if (solution_bit != expected_bit) { std::string sat_bits, rtl_bits; - for (int k = expected_y.width-1; k >= 0; k--) { - if (model_undef && y_values.at(expected_y.width+k)) + for (int k = expected_y.size()-1; k >= 0; k--) { + if (model_undef && y_values.at(expected_y.size()+k)) sat_bits += "x"; else sat_bits += y_values.at(k) ? "1" : "0"; - rtl_bits += expected_y.chunks.at(k).data.bits.at(0) == RTLIL::State::Sx ? "x" : - expected_y.chunks.at(k).data.bits.at(0) == RTLIL::State::S1 ? "1" : "0"; + rtl_bits += expected_y[k] == RTLIL::State::Sx ? "x" : expected_y[k] == RTLIL::State::S1 ? "1" : "0"; } log_error("Found error in SAT model: y[%d] = %s, should be %s:\n SAT: %s\n RTL: %s\n %*s^\n", int(i), log_signal(solution_bit), log_signal(expected_bit), - sat_bits.c_str(), rtl_bits.c_str(), expected_y.width-i-1, ""); + sat_bits.c_str(), rtl_bits.c_str(), expected_y.size()-i-1, ""); } } @@ -203,16 +197,16 @@ struct VlogHammerReporter std::vector<int> cmp_vars; std::vector<bool> cmp_vals; - std::vector<bool> y_undef(y_values.begin() + expected_y.width, y_values.end()); + std::vector<bool> y_undef(y_values.begin() + expected_y.size(), y_values.end()); - for (int i = 0; i < expected_y.width; i++) + for (int i = 0; i < expected_y.size(); i++) if (y_undef.at(i)) { log(" Toggling undef bit %d to test undef gating.\n", i); if (!ez.solve(y_vec, y_values, ez.IFF(y_vec.at(i), y_values.at(i) ? ez.FALSE : ez.TRUE))) log_error("Failed to find solution with toggled bit!\n"); - cmp_vars.push_back(y_vec.at(expected_y.width + i)); + cmp_vars.push_back(y_vec.at(expected_y.size() + i)); cmp_vals.push_back(true); } else @@ -220,7 +214,7 @@ struct VlogHammerReporter cmp_vars.push_back(y_vec.at(i)); cmp_vals.push_back(y_values.at(i)); - cmp_vars.push_back(y_vec.at(expected_y.width + i)); + cmp_vars.push_back(y_vec.at(expected_y.size() + i)); cmp_vals.push_back(false); } @@ -258,10 +252,10 @@ struct VlogHammerReporter std::vector<RTLIL::State> bits(patterns[idx].bits.begin(), patterns[idx].bits.begin() + total_input_width); for (int i = 0; i < int(inputs.size()); i++) { - RTLIL::Wire *wire = module->wires.at(inputs[i]); + RTLIL::Wire *wire = module->wires_.at(inputs[i]); for (int j = input_widths[i]-1; j >= 0; j--) { - ce.set(RTLIL::SigSpec(wire, 1, j), bits.back()); - recorded_set_vars.append(RTLIL::SigSpec(wire, 1, j)); + ce.set(RTLIL::SigSpec(wire, j), bits.back()); + recorded_set_vars.append(RTLIL::SigSpec(wire, j)); recorded_set_vals.bits.push_back(bits.back()); bits.pop_back(); } @@ -274,32 +268,30 @@ struct VlogHammerReporter } } - if (module->wires.count("\\y") == 0) + if (module->wires_.count("\\y") == 0) log_error("No output wire (y) found in module %s!\n", RTLIL::id2cstr(module->name)); - RTLIL::SigSpec sig(module->wires.at("\\y")); + RTLIL::SigSpec sig(module->wires_.at("\\y")); RTLIL::SigSpec undef; while (!ce.eval(sig, undef)) { // log_error("Evaluation of y in module %s failed: sig=%s, undef=%s\n", RTLIL::id2cstr(module->name), log_signal(sig), log_signal(undef)); log("Warning: Setting signal %s in module %s to undef.\n", log_signal(undef), RTLIL::id2cstr(module->name)); - ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.width)); + ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size())); } log("++VAL++ %d %s %s #\n", idx, module_name.c_str(), sig.as_const().as_string().c_str()); if (module_name == "rtl") { rtl_sig = sig; - rtl_sig.expand(); sat_check(module, recorded_set_vars, recorded_set_vals, sig, false); sat_check(module, recorded_set_vars, recorded_set_vals, sig, true); - } else if (rtl_sig.width > 0) { - sig.expand(); - if (rtl_sig.width != sig.width) + } else if (rtl_sig.size() > 0) { + if (rtl_sig.size() != sig.size()) log_error("Output (y) has a different width in module %s compared to rtl!\n", RTLIL::id2cstr(module->name)); - for (int i = 0; i < sig.width; i++) - if (rtl_sig.chunks.at(i).data.bits.at(0) == RTLIL::State::Sx) - sig.chunks.at(i).data.bits.at(0) = RTLIL::State::Sx; + for (int i = 0; i < SIZE(sig); i++) + if (rtl_sig[i] == RTLIL::State::Sx) + sig[i] = RTLIL::State::Sx; } log("++RPT++ %d%s %s %s\n", idx, input_pattern_list.c_str(), sig.as_const().as_string().c_str(), module_name.c_str()); @@ -314,10 +306,10 @@ struct VlogHammerReporter { for (auto name : split(module_list, ",")) { RTLIL::IdString esc_name = RTLIL::escape_id(module_prefix + name); - if (design->modules.count(esc_name) == 0) + if (design->modules_.count(esc_name) == 0) log_error("Can't find module %s in current design!\n", name.c_str()); log("Using module %s (%s).\n", esc_name.c_str(), name.c_str()); - modules.push_back(design->modules.at(esc_name)); + modules.push_back(design->modules_.at(esc_name)); module_names.push_back(name); } @@ -326,9 +318,9 @@ struct VlogHammerReporter int width = -1; RTLIL::IdString esc_name = RTLIL::escape_id(name); for (auto mod : modules) { - if (mod->wires.count(esc_name) == 0) + if (mod->wires_.count(esc_name) == 0) log_error("Can't find input %s in module %s!\n", name.c_str(), RTLIL::id2cstr(mod->name)); - RTLIL::Wire *port = mod->wires.at(esc_name); + RTLIL::Wire *port = mod->wires_.at(esc_name); if (!port->port_input || port->port_output) log_error("Wire %s in module %s is not an input!\n", name.c_str(), RTLIL::id2cstr(mod->name)); if (width >= 0 && width != port->width) @@ -350,7 +342,7 @@ struct VlogHammerReporter } if (!RTLIL::SigSpec::parse(sig, NULL, pattern) || !sig.is_fully_const()) log_error("Failed to parse pattern %s!\n", pattern.c_str()); - if (sig.width < total_input_width) + if (sig.size() < total_input_width) log_error("Pattern %s is to short!\n", pattern.c_str()); patterns.push_back(sig.as_const()); if (invert_pattern) { @@ -424,11 +416,11 @@ struct EvalPass : public Pass { /* this should only be used for regression testing of ConstEval -- see vloghammer */ std::string mod1_name = RTLIL::escape_id(args[++argidx]); std::string mod2_name = RTLIL::escape_id(args[++argidx]); - if (design->modules.count(mod1_name) == 0) + if (design->modules_.count(mod1_name) == 0) log_error("Can't find module `%s'!\n", mod1_name.c_str()); - if (design->modules.count(mod2_name) == 0) + if (design->modules_.count(mod2_name) == 0) log_error("Can't find module `%s'!\n", mod2_name.c_str()); - BruteForceEquivChecker checker(design->modules.at(mod1_name), design->modules.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x"); + BruteForceEquivChecker checker(design->modules_.at(mod1_name), design->modules_.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x"); if (checker.errors > 0) log_cmd_error("Modules are not equivialent!\n"); log("Verified %s = %s (using brute-force check on %d cases).\n", @@ -450,7 +442,7 @@ struct EvalPass : public Pass { extra_args(args, argidx, design); RTLIL::Module *module = NULL; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { if (module) log_cmd_error("Only one module must be selected for the EVAL pass! (selected: %s and %s)\n", @@ -470,16 +462,16 @@ struct EvalPass : public Pass { log_cmd_error("Failed to parse rhs set expression `%s'.\n", it.second.c_str()); if (!rhs.is_fully_const()) log_cmd_error("Right-hand-side set expression `%s' is not constant.\n", it.second.c_str()); - if (lhs.width != rhs.width) + if (lhs.size() != rhs.size()) log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - it.first.c_str(), log_signal(lhs), lhs.width, it.second.c_str(), log_signal(rhs), rhs.width); + it.first.c_str(), log_signal(lhs), lhs.size(), it.second.c_str(), log_signal(rhs), rhs.size()); ce.set(lhs, rhs.as_const()); } if (shows.size() == 0) { - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->port_output) - shows.push_back(it.second->name); + shows.push_back(it.second->name.str()); } if (tables.empty()) @@ -488,12 +480,11 @@ struct EvalPass : public Pass { RTLIL::SigSpec signal, value, undef; if (!RTLIL::SigSpec::parse_sel(signal, design, module, it)) log_cmd_error("Failed to parse show expression `%s'.\n", it.c_str()); - signal.optimize(); value = signal; if (set_undef) { while (!ce.eval(value, undef)) { log("Failed to evaluate signal %s: Missing value for %s. -> setting to undef\n", log_signal(signal), log_signal(undef)); - ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.width)); + ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size())); undef = RTLIL::SigSpec(); } log("Eval result: %s = %s.\n", log_signal(signal), log_signal(value)); @@ -526,15 +517,15 @@ struct EvalPass : public Pass { } std::vector<std::string> tab_line; - for (auto &c : tabsigs.chunks) + for (auto &c : tabsigs.chunks()) tab_line.push_back(log_signal(c)); tab_sep_colidx = tab_line.size(); - for (auto &c : signal.chunks) + for (auto &c : signal.chunks()) tab_line.push_back(log_signal(c)); tab.push_back(tab_line); tab_line.clear(); - RTLIL::Const tabvals(0, tabsigs.width); + RTLIL::Const tabvals(0, tabsigs.size()); do { ce.push(); @@ -548,19 +539,19 @@ struct EvalPass : public Pass { log_signal(tabsigs), log_signal(tabvals), log_signal(this_undef)); return; } - ce.set(this_undef, RTLIL::Const(RTLIL::State::Sx, this_undef.width)); + ce.set(this_undef, RTLIL::Const(RTLIL::State::Sx, this_undef.size())); undef.append(this_undef); this_undef = RTLIL::SigSpec(); } int pos = 0; - for (auto &c : tabsigs.chunks) { + for (auto &c : tabsigs.chunks()) { tab_line.push_back(log_signal(RTLIL::SigSpec(tabvals).extract(pos, c.width))); pos += c.width; } pos = 0; - for (auto &c : signal.chunks) { + for (auto &c : signal.chunks()) { tab_line.push_back(log_signal(value.extract(pos, c.width))); pos += c.width; } @@ -602,7 +593,7 @@ struct EvalPass : public Pass { } log("\n"); - if (undef.width > 0) { + if (undef.size() > 0) { undef.sort_and_unify(); log("Assumend undef (x) value for the following singals: %s\n\n", log_signal(undef)); } diff --git a/passes/sat/example.ys b/passes/sat/example.ys index 11f5b924b..cc72faac0 100644 --- a/passes/sat/example.ys +++ b/passes/sat/example.ys @@ -1,13 +1,14 @@ read_verilog example.v proc; opt_clean +echo on sat -set y 1'b1 example001 sat -set y 1'b1 example002 sat -set y_sshl 8'hf0 -set y_sshr 8'hf0 -set sh 4'd3 example003 -sat -set y 1'b1 example004 +sat -set y 1'b1 -ignore_unknown_cells example004 sat -show rst,counter -set-at 3 y 1'b1 -seq 4 example004 -sat -prove y 1'b0 -show rst,counter,y example004 -sat -prove y 1'b0 -show rst,counter,y -set-at 1 rst 1'b1 -seq 1 example004 +sat -prove y 1'b0 -show rst,counter,y -ignore_unknown_cells example004 +sat -prove y 1'b0 -tempinduct -show rst,counter,y -set-at 1 rst 1'b1 -seq 1 example004 diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 2ac7b35f6..e856fdf76 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -27,7 +27,7 @@ struct dff_map_info_t { RTLIL::SigSpec sig_d, sig_clk, sig_arst; bool clk_polarity, arst_polarity; RTLIL::Const arst_value; - std::vector<std::string> cells; + std::vector<RTLIL::IdString> cells; }; struct dff_map_bit_info_t { @@ -37,7 +37,7 @@ struct dff_map_bit_info_t { RTLIL::Cell *cell; }; -static bool consider_wire(RTLIL::Wire *wire, std::map<std::string, dff_map_info_t> &dff_dq_map) +static bool consider_wire(RTLIL::Wire *wire, std::map<RTLIL::IdString, dff_map_info_t> &dff_dq_map) { if (wire->name[0] == '$' || dff_dq_map.count(wire->name)) return false; @@ -46,11 +46,11 @@ static bool consider_wire(RTLIL::Wire *wire, std::map<std::string, dff_map_info_ return true; } -static bool consider_cell(RTLIL::Design *design, std::set<std::string> &dff_cells, RTLIL::Cell *cell) +static bool consider_cell(RTLIL::Design *design, std::set<RTLIL::IdString> &dff_cells, RTLIL::Cell *cell) { if (cell->name[0] == '$' || dff_cells.count(cell->name)) return false; - if (cell->type.at(0) == '\\' && !design->modules.count(cell->type)) + if (cell->type[0] == '\\' && !design->modules_.count(cell->type)) return false; return true; } @@ -73,7 +73,7 @@ static bool compare_cells(RTLIL::Cell *cell1, RTLIL::Cell *cell2) return true; } -static void find_dff_wires(std::set<std::string> &dff_wires, RTLIL::Module *module) +static void find_dff_wires(std::set<RTLIL::IdString> &dff_wires, RTLIL::Module *module) { CellTypes ct; ct.setup_internals_mem(); @@ -82,23 +82,23 @@ static void find_dff_wires(std::set<std::string> &dff_wires, RTLIL::Module *modu SigMap sigmap(module); SigPool dffsignals; - for (auto &it : module->cells) { - if (ct.cell_known(it.second->type) && it.second->connections.count("\\Q")) - dffsignals.add(sigmap(it.second->connections.at("\\Q"))); + for (auto &it : module->cells_) { + if (ct.cell_known(it.second->type) && it.second->hasPort("\\Q")) + dffsignals.add(sigmap(it.second->getPort("\\Q"))); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { if (dffsignals.check_any(it.second)) dff_wires.insert(it.first); } } -static void create_dff_dq_map(std::map<std::string, dff_map_info_t> &map, RTLIL::Design *design, RTLIL::Module *module) +static void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::Design *design, RTLIL::Module *module) { std::map<RTLIL::SigBit, dff_map_bit_info_t> bit_info; SigMap sigmap(module); - for (auto &it : module->cells) + for (auto &it : module->cells_) { if (!design->selected(module, it.second)) continue; @@ -113,10 +113,10 @@ static void create_dff_dq_map(std::map<std::string, dff_map_info_t> &map, RTLIL: info.cell = it.second; if (info.cell->type == "$dff") { - info.bit_clk = sigmap(info.cell->connections.at("\\CLK")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); - std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->connections.at("\\D")).to_sigbit_vector(); - std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->connections.at("\\Q")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector(); for (size_t i = 0; i < sig_d.size(); i++) { info.bit_d = sig_d.at(i); bit_info[sig_q.at(i)] = info; @@ -125,12 +125,12 @@ static void create_dff_dq_map(std::map<std::string, dff_map_info_t> &map, RTLIL: } if (info.cell->type == "$adff") { - info.bit_clk = sigmap(info.cell->connections.at("\\CLK")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->connections.at("\\ARST")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); + info.bit_arst = sigmap(info.cell->getPort("\\ARST")).to_single_sigbit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool(); - std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->connections.at("\\D")).to_sigbit_vector(); - std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->connections.at("\\Q")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector(); std::vector<RTLIL::State> arst_value = info.cell->parameters.at("\\ARST_VALUE").bits; for (size_t i = 0; i < sig_d.size(); i++) { info.bit_d = sig_d.at(i); @@ -141,27 +141,27 @@ static void create_dff_dq_map(std::map<std::string, dff_map_info_t> &map, RTLIL: } if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") { - info.bit_clk = sigmap(info.cell->connections.at("\\C")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); info.clk_polarity = info.cell->type == "$_DFF_P_"; - info.bit_d = sigmap(info.cell->connections.at("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->connections.at("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); + bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; continue; } if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") { - info.bit_clk = sigmap(info.cell->connections.at("\\C")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->connections.at("\\R")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); + info.bit_arst = sigmap(info.cell->getPort("\\R")).to_single_sigbit(); info.clk_polarity = info.cell->type[6] == 'P'; info.arst_polarity = info.cell->type[7] == 'P'; info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0; - info.bit_d = sigmap(info.cell->connections.at("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->connections.at("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); + bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; continue; } } - std::map<std::string, dff_map_info_t> empty_dq_map; - for (auto &it : module->wires) + std::map<RTLIL::IdString, dff_map_info_t> empty_dq_map; + for (auto &it : module->wires_) { if (!consider_wire(it.second, empty_dq_map)) continue; @@ -208,11 +208,11 @@ static void create_dff_dq_map(std::map<std::string, dff_map_info_t> &map, RTLIL: } } -static void add_new_wire(RTLIL::Module *module, RTLIL::Wire *wire) +static RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width = 1) { - if (module->count_id(wire->name)) - log_error("Attempting to create wire %s, but a wire of this name exists already! Hint: Try another value for -sep.\n", RTLIL::id2cstr(wire->name)); - module->add(wire); + if (module->count_id(name)) + log_error("Attempting to create wire %s, but a wire of this name exists already! Hint: Try another value for -sep.\n", log_id(name)); + return module->addWire(name, width); } struct ExposePass : public Pass { @@ -259,6 +259,8 @@ struct ExposePass : public Pass { bool flag_evert_dff = false; std::string sep = "."; + log_header("Executing EXPOSE pass (exposing internal signals as outputs).\n"); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -292,15 +294,15 @@ struct ExposePass : public Pass { CellTypes ct(design); - std::map<RTLIL::Module*, std::map<std::string, dff_map_info_t>> dff_dq_maps; - std::map<RTLIL::Module*, std::set<std::string>> dff_cells; + std::map<RTLIL::Module*, std::map<RTLIL::IdString, dff_map_info_t>> dff_dq_maps; + std::map<RTLIL::Module*, std::set<RTLIL::IdString>> dff_cells; if (flag_evert_dff) { RTLIL::Module *first_module = NULL; - std::set<std::string> shared_dff_wires; + std::set<RTLIL::IdString> shared_dff_wires; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; @@ -315,11 +317,11 @@ struct ExposePass : public Pass { shared_dff_wires.insert(it.first); first_module = mod_it.second; } else { - std::set<std::string> new_shared_dff_wires; + std::set<RTLIL::IdString> new_shared_dff_wires; for (auto &it : shared_dff_wires) { if (!dff_dq_maps[mod_it.second].count(it)) continue; - if (!compare_wires(first_module->wires.at(it), mod_it.second->wires.at(it))) + if (!compare_wires(first_module->wires_.at(it), mod_it.second->wires_.at(it))) continue; new_shared_dff_wires.insert(it); } @@ -330,7 +332,7 @@ struct ExposePass : public Pass { if (flag_shared) for (auto &map_it : dff_dq_maps) { - std::map<std::string, dff_map_info_t> new_map; + std::map<RTLIL::IdString, dff_map_info_t> new_map; for (auto &it : map_it.second) if (shared_dff_wires.count(it.first)) new_map[it.first] = it.second; @@ -343,33 +345,33 @@ struct ExposePass : public Pass { dff_cells[it1.first].insert(it3); } - std::set<std::string> shared_wires, shared_cells; - std::set<std::string> used_names; + std::set<RTLIL::IdString> shared_wires, shared_cells; + std::set<RTLIL::IdString> used_names; if (flag_shared) { RTLIL::Module *first_module = NULL; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { RTLIL::Module *module = mod_it.second; if (!design->selected(module)) continue; - std::set<std::string> dff_wires; + std::set<RTLIL::IdString> dff_wires; if (flag_dff) find_dff_wires(dff_wires, module); if (first_module == NULL) { - for (auto &it : module->wires) + for (auto &it : module->wires_) if (design->selected(module, it.second) && consider_wire(it.second, dff_dq_maps[module])) if (!flag_dff || dff_wires.count(it.first)) shared_wires.insert(it.first); if (flag_evert) - for (auto &it : module->cells) + for (auto &it : module->cells_) if (design->selected(module, it.second) && consider_cell(design, dff_cells[module], it.second)) shared_cells.insert(it.first); @@ -377,22 +379,22 @@ struct ExposePass : public Pass { } else { - std::vector<std::string> delete_shared_wires, delete_shared_cells; + std::vector<RTLIL::IdString> delete_shared_wires, delete_shared_cells; for (auto &it : shared_wires) { RTLIL::Wire *wire; - if (module->wires.count(it) == 0) + if (module->wires_.count(it) == 0) goto delete_shared_wire; - wire = module->wires.at(it); + wire = module->wires_.at(it); if (!design->selected(module, wire)) goto delete_shared_wire; if (!consider_wire(wire, dff_dq_maps[module])) goto delete_shared_wire; - if (!compare_wires(first_module->wires.at(it), wire)) + if (!compare_wires(first_module->wires_.at(it), wire)) goto delete_shared_wire; if (flag_dff && !dff_wires.count(it)) goto delete_shared_wire; @@ -407,16 +409,16 @@ struct ExposePass : public Pass { { RTLIL::Cell *cell; - if (module->cells.count(it) == 0) + if (module->cells_.count(it) == 0) goto delete_shared_cell; - cell = module->cells.at(it); + cell = module->cells_.at(it); if (!design->selected(module, cell)) goto delete_shared_cell; if (!consider_cell(design, dff_cells[module], cell)) goto delete_shared_cell; - if (!compare_cells(first_module->cells.at(it), cell)) + if (!compare_cells(first_module->cells_.at(it), cell)) goto delete_shared_cell; if (0) @@ -432,23 +434,22 @@ struct ExposePass : public Pass { } } - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { RTLIL::Module *module = mod_it.second; if (!design->selected(module)) continue; - std::set<std::string> dff_wires; + std::set<RTLIL::IdString> dff_wires; if (flag_dff && !flag_shared) find_dff_wires(dff_wires, module); SigMap sigmap(module); SigMap out_to_in_map; - std::vector<RTLIL::Wire*> new_wires; - for (auto &it : module->wires) + for (auto &it : module->wires_) { if (flag_shared) { if (shared_wires.count(it.first) == 0) @@ -466,29 +467,23 @@ struct ExposePass : public Pass { } if (flag_cut) { - RTLIL::Wire *in_wire = new RTLIL::Wire; - in_wire->name = it.second->name + sep + "i"; - in_wire->width = it.second->width; + RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width); in_wire->port_input = true; out_to_in_map.add(sigmap(it.second), in_wire); - new_wires.push_back(in_wire); } } if (flag_cut) { - for (auto it : new_wires) - add_new_wire(module, it); - - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (!ct.cell_known(it.second->type)) continue; - for (auto &conn : it.second->connections) + for (auto &conn : it.second->connections_) if (ct.cell_input(it.second->type, conn.first)) conn.second = out_to_in_map(sigmap(conn.second)); } - for (auto &conn : module->connections) + for (auto &conn : module->connections_) conn.second = out_to_in_map(sigmap(conn.second)); } @@ -496,35 +491,29 @@ struct ExposePass : public Pass { for (auto &dq : dff_dq_maps[module]) { - if (!module->wires.count(dq.first)) + if (!module->wires_.count(dq.first)) continue; - RTLIL::Wire *wire = module->wires.at(dq.first); + RTLIL::Wire *wire = module->wires_.at(dq.first); std::set<RTLIL::SigBit> wire_bits_set = sigmap(wire).to_sigbit_set(); std::vector<RTLIL::SigBit> wire_bits_vec = sigmap(wire).to_sigbit_vector(); dff_map_info_t &info = dq.second; - RTLIL::Wire *wire_dummy_q = new RTLIL::Wire; - wire_dummy_q->name = NEW_ID; - wire_dummy_q->width = 0; - add_new_wire(module, wire_dummy_q); + RTLIL::Wire *wire_dummy_q = add_new_wire(module, NEW_ID, 0); for (auto &cell_name : info.cells) { - RTLIL::Cell *cell = module->cells.at(cell_name); - std::vector<RTLIL::SigBit> cell_q_bits = sigmap(cell->connections.at("\\Q")).to_sigbit_vector(); + RTLIL::Cell *cell = module->cells_.at(cell_name); + std::vector<RTLIL::SigBit> cell_q_bits = sigmap(cell->getPort("\\Q")).to_sigbit_vector(); for (auto &bit : cell_q_bits) if (wire_bits_set.count(bit)) bit = RTLIL::SigBit(wire_dummy_q, wire_dummy_q->width++); - cell->connections.at("\\Q") = cell_q_bits; + cell->setPort("\\Q", cell_q_bits); } - RTLIL::Wire *wire_q = new RTLIL::Wire; - wire_q->name = wire->name + sep + "q"; - wire_q->width = wire->width; + RTLIL::Wire *wire_q = add_new_wire(module, wire->name.str() + sep + "q", wire->width); wire_q->port_input = true; log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_q->name)); - add_new_wire(module, wire_q); RTLIL::SigSig connect_q; for (size_t i = 0; i < wire_bits_vec.size(); i++) { @@ -534,71 +523,55 @@ struct ExposePass : public Pass { connect_q.second.append(RTLIL::SigBit(wire_q, i)); set_q_bits.insert(wire_bits_vec[i]); } - module->connections.push_back(connect_q); + module->connect(connect_q); - RTLIL::Wire *wire_d = new RTLIL::Wire; - wire_d->name = wire->name + sep + "d"; - wire_d->width = wire->width; + RTLIL::Wire *wire_d = add_new_wire(module, wire->name.str() + sep + "d", wire->width); wire_d->port_output = true; log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_d->name)); - add_new_wire(module, wire_d); - module->connections.push_back(RTLIL::SigSig(wire_d, info.sig_d)); + module->connect(RTLIL::SigSig(wire_d, info.sig_d)); - RTLIL::Wire *wire_c = new RTLIL::Wire; - wire_c->name = wire->name + sep + "c"; + RTLIL::Wire *wire_c = add_new_wire(module, wire->name.str() + sep + "c"); wire_c->port_output = true; log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_c->name)); - add_new_wire(module, wire_c); if (info.clk_polarity) { - module->connections.push_back(RTLIL::SigSig(wire_c, info.sig_clk)); + module->connect(RTLIL::SigSig(wire_c, info.sig_clk)); } else { - RTLIL::Cell *c = new RTLIL::Cell; - c->name = NEW_ID; - c->type = "$not"; + RTLIL::Cell *c = module->addCell(NEW_ID, "$not"); c->parameters["\\A_SIGNED"] = 0; c->parameters["\\A_WIDTH"] = 1; c->parameters["\\Y_WIDTH"] = 1; - c->connections["\\A"] = info.sig_clk; - c->connections["\\Y"] = wire_c; - module->add(c); + c->setPort("\\A", info.sig_clk); + c->setPort("\\Y", wire_c); } if (info.sig_arst != RTLIL::State::Sm) { - RTLIL::Wire *wire_r = new RTLIL::Wire; - wire_r->name = wire->name + sep + "r"; + RTLIL::Wire *wire_r = add_new_wire(module, wire->name.str() + sep + "r"); wire_r->port_output = true; log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_r->name)); - add_new_wire(module, wire_r); if (info.arst_polarity) { - module->connections.push_back(RTLIL::SigSig(wire_r, info.sig_arst)); + module->connect(RTLIL::SigSig(wire_r, info.sig_arst)); } else { - RTLIL::Cell *c = new RTLIL::Cell; - c->name = NEW_ID; - c->type = "$not"; + RTLIL::Cell *c = module->addCell(NEW_ID, "$not"); c->parameters["\\A_SIGNED"] = 0; c->parameters["\\A_WIDTH"] = 1; c->parameters["\\Y_WIDTH"] = 1; - c->connections["\\A"] = info.sig_arst; - c->connections["\\Y"] = wire_r; - module->add(c); + c->setPort("\\A", info.sig_arst); + c->setPort("\\Y", wire_r); } - RTLIL::Wire *wire_v = new RTLIL::Wire; - wire_v->name = wire->name + sep + "v"; - wire_v->width = wire->width; + RTLIL::Wire *wire_v = add_new_wire(module, wire->name.str() + sep + "v", wire->width); wire_v->port_output = true; log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_v->name)); - add_new_wire(module, wire_v); - module->connections.push_back(RTLIL::SigSig(wire_v, info.arst_value)); + module->connect(RTLIL::SigSig(wire_v, info.arst_value)); } } if (flag_evert) { - std::vector<std::string> delete_cells; + std::vector<RTLIL::Cell*> delete_cells; - for (auto &it : module->cells) + for (auto &it : module->cells_) { if (flag_shared) { if (shared_cells.count(it.first) == 0) @@ -610,66 +583,59 @@ struct ExposePass : public Pass { RTLIL::Cell *cell = it.second; - if (design->modules.count(cell->type)) + if (design->modules_.count(cell->type)) { - RTLIL::Module *mod = design->modules.at(cell->type); + RTLIL::Module *mod = design->modules_.at(cell->type); - for (auto &it : mod->wires) + for (auto &it : mod->wires_) { RTLIL::Wire *p = it.second; if (!p->port_input && !p->port_output) continue; - RTLIL::Wire *w = new RTLIL::Wire; - w->name = cell->name + sep + RTLIL::unescape_id(p->name); - w->width = p->width; + RTLIL::Wire *w = add_new_wire(module, cell->name.str() + sep + RTLIL::unescape_id(p->name), p->width); if (p->port_input) w->port_output = true; if (p->port_output) w->port_input = true; - add_new_wire(module, w); - log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name)); + log("New module port: %s/%s (%s)\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name), RTLIL::id2cstr(cell->type)); RTLIL::SigSpec sig; - if (cell->connections.count(p->name) != 0) - sig = cell->connections.at(p->name); + if (cell->hasPort(p->name)) + sig = cell->getPort(p->name); sig.extend(w->width); if (w->port_input) - module->connections.push_back(RTLIL::SigSig(sig, w)); + module->connect(RTLIL::SigSig(sig, w)); else - module->connections.push_back(RTLIL::SigSig(w, sig)); + module->connect(RTLIL::SigSig(w, sig)); } } else { - for (auto &it : cell->connections) + for (auto &it : cell->connections()) { - RTLIL::Wire *w = new RTLIL::Wire; - w->name = cell->name + sep + RTLIL::unescape_id(it.first); - w->width = it.second.width; + RTLIL::Wire *w = add_new_wire(module, cell->name.str() + sep + RTLIL::unescape_id(it.first), it.second.size()); if (ct.cell_input(cell->type, it.first)) w->port_output = true; if (ct.cell_output(cell->type, it.first)) w->port_input = true; - add_new_wire(module, w); - log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name)); + log("New module port: %s/%s (%s)\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name), RTLIL::id2cstr(cell->type)); if (w->port_input) - module->connections.push_back(RTLIL::SigSig(it.second, w)); + module->connect(RTLIL::SigSig(it.second, w)); else - module->connections.push_back(RTLIL::SigSig(w, it.second)); + module->connect(RTLIL::SigSig(w, it.second)); } } - delete_cells.push_back(cell->name); + delete_cells.push_back(cell); } - for (auto &it : delete_cells) { - log("Removing cell: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it)); - delete module->cells.at(it); - module->cells.erase(it); + for (auto cell : delete_cells) { + log("Removing cell: %s/%s (%s)\n", log_id(module), log_id(cell), log_id(cell->type)); + module->remove(cell); } } diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 81250b000..bfed0005d 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -31,8 +31,9 @@ namespace { bool inv_mode; -int verbose_level; +int verbose_level, reduce_counter, reduce_stop_at; typedef std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>>> drivers_t; +std::string dump_prefix; struct equiv_bit_t { @@ -112,13 +113,13 @@ struct FindReducedInputs size_t idx_bits = get_bits(idx); if (sat_pi_uniq_bitvec.size() != idx_bits) { - sat_pi_uniq_bitvec.push_back(ez.literal(stringf("uniq_%d", int(idx_bits)-1))); + sat_pi_uniq_bitvec.push_back(ez.frozen_literal(stringf("uniq_%d", int(idx_bits)-1))); for (auto &it : sat_pi) ez.assume(ez.OR(ez.NOT(it.second), ez.NOT(sat_pi_uniq_bitvec.back()))); } log_assert(sat_pi_uniq_bitvec.size() == idx_bits); - sat_pi[bit] = ez.literal(stringf("pi_%s", log_signal(bit))); + sat_pi[bit] = ez.frozen_literal(stringf("p, falsei_%s", log_signal(bit))); ez.assume(ez.IFF(ez.XOR(sat_a, sat_b), sat_pi[bit])); for (size_t i = 0; i < idx_bits; i++) @@ -243,7 +244,6 @@ struct PerformReduction return 0; if (sigdepth.count(out) != 0) return sigdepth.at(out); - sigdepth[out] = 0; if (drivers.count(out) != 0) { std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out); @@ -252,17 +252,18 @@ struct PerformReduction log_error("Can't create SAT model for cell %s (%s)!\n", RTLIL::id2cstr(drv.first->name), RTLIL::id2cstr(drv.first->type)); celldone.insert(drv.first); } - int max_child_dept = 0; + int max_child_depth = 0; for (auto &bit : drv.second) - max_child_dept = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_dept); - sigdepth[out] = max_child_dept + 1; + max_child_depth = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); + sigdepth[out] = max_child_depth + 1; } else { pi_bits.push_back(out); sat_pi.push_back(satgen.importSigSpec(out).front()); ez.assume(ez.NOT(satgen.importUndefSigSpec(out).front())); + sigdepth[out] = 0; } - return sigdepth[out]; + return sigdepth.at(out); } PerformReduction(SigMap &sigmap, drivers_t &drivers, std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs, std::vector<RTLIL::SigBit> &bits, int cone_size) : @@ -348,7 +349,7 @@ struct PerformReduction std::vector<RTLIL::SigBit> bucket_sigbits; for (int idx : bucket) bucket_sigbits.push_back(out_bits[idx]); - log("%s Trying to shatter bucket with %d signals: %s\n", indt, int(bucket.size()), log_signal(RTLIL::SigSpec(bucket_sigbits).optimized())); + log("%s Trying to shatter bucket with %d signals: %s\n", indt, int(bucket.size()), log_signal(bucket_sigbits)); } std::vector<int> sat_set_list, sat_clr_list; @@ -493,7 +494,7 @@ struct PerformReduction std::vector<RTLIL::SigBit> r_sigbits; for (int idx : r) r_sigbits.push_back(out_bits[idx]); - log(" Found group of %d equivialent signals: %s\n", int(r.size()), log_signal(RTLIL::SigSpec(r_sigbits).optimized())); + log(" Found group of %d equivialent signals: %s\n", int(r.size()), log_signal(r_sigbits)); } std::vector<int> undef_slaves; @@ -559,6 +560,38 @@ struct FreduceWorker { } + bool find_bit_in_cone(std::set<RTLIL::Cell*> &celldone, RTLIL::SigBit needle, RTLIL::SigBit haystack) + { + if (needle == haystack) + return true; + if (haystack.wire == NULL || needle.wire == NULL || drivers.count(haystack) == 0) + return false; + + std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(haystack); + + if (celldone.count(drv.first)) + return false; + celldone.insert(drv.first); + + for (auto &bit : drv.second) + if (find_bit_in_cone(celldone, needle, bit)) + return true; + return false; + } + + bool find_bit_in_cone(RTLIL::SigBit needle, RTLIL::SigBit haystack) + { + std::set<RTLIL::Cell*> celldone; + return find_bit_in_cone(celldone, needle, haystack); + } + + void dump() + { + std::string filename = stringf("%s_%s_%05d.il", dump_prefix.c_str(), RTLIL::id2cstr(module->name), reduce_counter); + log("%s Writing dump file `%s'.\n", reduce_counter ? " " : "", filename.c_str()); + Pass::call(design, stringf("dump -outfile %s %s", filename.c_str(), design->selected_active_module.empty() ? module->name.c_str() : "")); + } + int run() { log("Running functional reduction on module %s:\n", RTLIL::id2cstr(module->name)); @@ -569,15 +602,15 @@ struct FreduceWorker int bits_full_total = 0; std::vector<std::set<RTLIL::SigBit>> batches; - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->port_input) { batches.push_back(sigmap(it.second).to_sigbit_set()); bits_full_total += it.second->width; } - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (ct.cell_known(it.second->type)) { std::set<RTLIL::SigBit> inputs, outputs; - for (auto &port : it.second->connections) { + for (auto &port : it.second->connections()) { std::vector<RTLIL::SigBit> bits = sigmap(port.second).to_sigbit_vector(); if (ct.cell_output(it.second->type, port.first)) outputs.insert(bits.begin(), bits.end()); @@ -590,8 +623,8 @@ struct FreduceWorker batches.push_back(outputs); bits_full_total += outputs.size(); } - if (inv_mode && it.second->type == "$_INV_") - inv_pairs.insert(std::pair<RTLIL::SigBit, RTLIL::SigBit>(sigmap(it.second->connections.at("\\A")), sigmap(it.second->connections.at("\\Y")))); + if (inv_mode && it.second->type == "$_NOT_") + inv_pairs.insert(std::pair<RTLIL::SigBit, RTLIL::SigBit>(sigmap(it.second->getPort("\\A")), sigmap(it.second->getPort("\\Y")))); } int bits_count = 0; @@ -607,7 +640,7 @@ struct FreduceWorker found_selected_wire: log(" Finding reduced input cone for signal batch %s%c\n", - log_signal(RTLIL::SigSpec(std::vector<RTLIL::SigBit>(batch.begin(), batch.end())).optimized()), verbose_level ? ':' : '.'); + log_signal(batch), verbose_level ? ':' : '.'); FindReducedInputs infinder(sigmap, drivers); for (auto &bit : batch) { @@ -630,12 +663,12 @@ struct FreduceWorker continue; if (bucket.first.size() == 0) { - log(" Finding const values for bucket %s%c\n", log_signal(RTLIL::SigSpec(bucket.second).optimized()), verbose_level ? ':' : '.'); + log(" Finding const values for bucket %s%c\n", log_signal(bucket.second), verbose_level ? ':' : '.'); PerformReduction worker(sigmap, drivers, inv_pairs, bucket.second, bucket.first.size()); for (size_t idx = 0; idx < bucket.second.size(); idx++) worker.analyze_const(equiv, idx); } else { - log(" Trying to shatter bucket %s%c\n", log_signal(RTLIL::SigSpec(bucket.second).optimized()), verbose_level ? ':' : '.'); + log(" Trying to shatter bucket %s%c\n", log_signal(bucket.second), verbose_level ? ':' : '.'); PerformReduction worker(sigmap, drivers, inv_pairs, bucket.second, bucket.first.size()); worker.analyze(equiv, 100 * bucket_count / (buckets.size() + 1)); } @@ -644,11 +677,14 @@ struct FreduceWorker std::map<RTLIL::SigBit, int> bitusage; module->rewrite_sigspecs(CountBitUsage(sigmap, bitusage)); + if (!dump_prefix.empty()) + dump(); + log(" Rewiring %d equivialent groups:\n", int(equiv.size())); int rewired_sigbits = 0; for (auto &grp : equiv) { - log(" Using as master for group: %s\n", log_signal(grp.front().bit)); + log(" [%05d] Using as master for group: %s\n", ++reduce_counter, log_signal(grp.front().bit)); RTLIL::SigSpec inv_sig; for (size_t i = 1; i < grp.size(); i++) @@ -663,35 +699,45 @@ struct FreduceWorker continue; } + if (find_bit_in_cone(grp[i].bit, grp.front().bit)) { + log(" Skipping dependency of master: %s\n", log_signal(grp[i].bit)); + continue; + } + log(" Connect slave%s: %s\n", grp[i].inverted ? " using inverter" : "", log_signal(grp[i].bit)); RTLIL::Cell *drv = drivers.at(grp[i].bit).first; - RTLIL::Wire *dummy_wire = module->new_wire(1, NEW_ID); - for (auto &port : drv->connections) + RTLIL::Wire *dummy_wire = module->addWire(NEW_ID); + for (auto &port : drv->connections_) if (ct.cell_output(drv->type, port.first)) sigmap(port.second).replace(grp[i].bit, dummy_wire, &port.second); if (grp[i].inverted) { - if (inv_sig.width == 0) + if (inv_sig.size() == 0) { - inv_sig = module->new_wire(1, NEW_ID); - - RTLIL::Cell *inv_cell = new RTLIL::Cell; - inv_cell->name = NEW_ID; - inv_cell->type = "$_INV_"; - inv_cell->connections["\\A"] = grp[0].bit; - inv_cell->connections["\\Y"] = inv_sig; - module->add(inv_cell); + inv_sig = module->addWire(NEW_ID); + + RTLIL::Cell *inv_cell = module->addCell(NEW_ID, "$_NOT_"); + inv_cell->setPort("\\A", grp[0].bit); + inv_cell->setPort("\\Y", inv_sig); } - module->connections.push_back(RTLIL::SigSig(grp[i].bit, inv_sig)); + module->connect(RTLIL::SigSig(grp[i].bit, inv_sig)); } else - module->connections.push_back(RTLIL::SigSig(grp[i].bit, grp[0].bit)); + module->connect(RTLIL::SigSig(grp[i].bit, grp[0].bit)); rewired_sigbits++; } + + if (!dump_prefix.empty()) + dump(); + + if (reduce_counter == reduce_stop_at) { + log(" Reached limit passed using -stop option. Skipping all further reductions.\n"); + break; + } } log(" Rewired a total of %d signal bits in module %s.\n", rewired_sigbits, RTLIL::id2cstr(module->name)); @@ -711,7 +757,7 @@ struct FreducePass : public Pass { log("\n"); log("This pass performs functional reduction in the circuit. I.e. if two nodes are\n"); log("equivialent, they are merged to one node and one of the redundant drivers is\n"); - log("unconnected. A subsequent call to 'clean' will remove the redundant drivers.\n"); + log("disconnected. A subsequent call to 'clean' will remove the redundant drivers.\n"); log("\n"); log(" -v, -vv\n"); log(" enable verbose or very verbose output\n"); @@ -719,6 +765,14 @@ struct FreducePass : public Pass { log(" -inv\n"); log(" enable explicit handling of inverted signals\n"); log("\n"); + log(" -stop <n>\n"); + log(" stop after <n> reduction operations. this is mostly used for\n"); + log(" debugging the freduce command itself.\n"); + log("\n"); + log(" -dump <prefix>\n"); + log(" dump the design to <prefix>_<module>_<num>.il after each reduction\n"); + log(" operation. this is mostly used for debugging the freduce command.\n"); + log("\n"); log("This pass is undef-aware, i.e. it considers don't-care values for detecting\n"); log("equivialent nodes.\n"); log("\n"); @@ -728,8 +782,11 @@ struct FreducePass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + reduce_counter = 0; + reduce_stop_at = 0; verbose_level = 0; inv_mode = false; + dump_prefix = std::string(); log_header("Executing FREDUCE pass (perform functional reduction).\n"); @@ -747,12 +804,20 @@ struct FreducePass : public Pass { inv_mode = true; continue; } + if (args[argidx] == "-stop" && argidx+1 < args.size()) { + reduce_stop_at = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-dump" && argidx+1 < args.size()) { + dump_prefix = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); int bitcount = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { RTLIL::Module *module = mod_it.second; if (design->selected(module)) bitcount += FreduceWorker(design, module).run(); diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index db12cb57d..b3adefb92 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -27,6 +27,9 @@ static void create_miter_equiv(struct Pass *that, std::vector<std::string> args, bool flag_make_outputs = false; bool flag_make_outcmp = false; bool flag_make_assert = false; + bool flag_flatten = false; + + log_header("Executing MITER pass (creating miter circuit).\n"); size_t argidx; for (argidx = 2; argidx < args.size(); argidx++) @@ -47,32 +50,36 @@ static void create_miter_equiv(struct Pass *that, std::vector<std::string> args, flag_make_assert = true; continue; } + if (args[argidx] == "-flatten") { + flag_flatten = true; + continue; + } break; } if (argidx+3 != args.size() || args[argidx].substr(0, 1) == "-") that->cmd_error(args, argidx, "command argument error"); - std::string gold_name = RTLIL::escape_id(args[argidx++]); - std::string gate_name = RTLIL::escape_id(args[argidx++]); - std::string miter_name = RTLIL::escape_id(args[argidx++]); + RTLIL::IdString gold_name = RTLIL::escape_id(args[argidx++]); + RTLIL::IdString gate_name = RTLIL::escape_id(args[argidx++]); + RTLIL::IdString miter_name = RTLIL::escape_id(args[argidx++]); - if (design->modules.count(gold_name) == 0) + if (design->modules_.count(gold_name) == 0) log_cmd_error("Can't find gold module %s!\n", gold_name.c_str()); - if (design->modules.count(gate_name) == 0) + if (design->modules_.count(gate_name) == 0) log_cmd_error("Can't find gate module %s!\n", gate_name.c_str()); - if (design->modules.count(miter_name) != 0) + if (design->modules_.count(miter_name) != 0) log_cmd_error("There is already a module %s!\n", gate_name.c_str()); - RTLIL::Module *gold_module = design->modules.at(gold_name); - RTLIL::Module *gate_module = design->modules.at(gate_name); + RTLIL::Module *gold_module = design->modules_.at(gold_name); + RTLIL::Module *gate_module = design->modules_.at(gate_name); - for (auto &it : gold_module->wires) { + for (auto &it : gold_module->wires_) { RTLIL::Wire *w1 = it.second, *w2; if (w1->port_id == 0) continue; - if (gate_module->wires.count(it.second->name) == 0) + if (gate_module->wires_.count(it.second->name) == 0) goto match_gold_port_error; - w2 = gate_module->wires.at(it.second->name); + w2 = gate_module->wires_.at(it.second->name); if (w1->port_input != w2->port_input) goto match_gold_port_error; if (w1->port_output != w2->port_output) @@ -84,13 +91,13 @@ static void create_miter_equiv(struct Pass *that, std::vector<std::string> args, log_cmd_error("No matching port in gate module was found for %s!\n", it.second->name.c_str()); } - for (auto &it : gate_module->wires) { + for (auto &it : gate_module->wires_) { RTLIL::Wire *w1 = it.second, *w2; if (w1->port_id == 0) continue; - if (gold_module->wires.count(it.second->name) == 0) + if (gold_module->wires_.count(it.second->name) == 0) goto match_gate_port_error; - w2 = gold_module->wires.at(it.second->name); + w2 = gold_module->wires_.at(it.second->name); if (w1->port_input != w2->port_input) goto match_gate_port_error; if (w1->port_output != w2->port_output) @@ -102,187 +109,151 @@ static void create_miter_equiv(struct Pass *that, std::vector<std::string> args, log_cmd_error("No matching port in gold module was found for %s!\n", it.second->name.c_str()); } + log("Creating miter cell \"%s\" with gold cell \"%s\" and gate cell \"%s\".\n", RTLIL::id2cstr(miter_name), RTLIL::id2cstr(gold_name), RTLIL::id2cstr(gate_name)); + RTLIL::Module *miter_module = new RTLIL::Module; miter_module->name = miter_name; - design->modules[miter_name] = miter_module; - - RTLIL::Cell *gold_cell = new RTLIL::Cell; - gold_cell->name = "\\gold"; - gold_cell->type = gold_name; - miter_module->add(gold_cell); + design->add(miter_module); - RTLIL::Cell *gate_cell = new RTLIL::Cell; - gate_cell->name = "\\gate"; - gate_cell->type = gate_name; - miter_module->add(gate_cell); + RTLIL::Cell *gold_cell = miter_module->addCell("\\gold", gold_name); + RTLIL::Cell *gate_cell = miter_module->addCell("\\gate", gate_name); RTLIL::SigSpec all_conditions; - for (auto &it : gold_module->wires) + for (auto &it : gold_module->wires_) { RTLIL::Wire *w1 = it.second; if (w1->port_input) { - RTLIL::Wire *w2 = new RTLIL::Wire; - w2->name = "\\in_" + RTLIL::unescape_id(w1->name); + RTLIL::Wire *w2 = miter_module->addWire("\\in_" + RTLIL::unescape_id(w1->name), w1->width); w2->port_input = true; - w2->width = w1->width; - miter_module->add(w2); - gold_cell->connections[w1->name] = w2; - gate_cell->connections[w1->name] = w2; + gold_cell->setPort(w1->name, w2); + gate_cell->setPort(w1->name, w2); } if (w1->port_output) { - RTLIL::Wire *w2_gold = new RTLIL::Wire; - w2_gold->name = "\\gold_" + RTLIL::unescape_id(w1->name); + RTLIL::Wire *w2_gold = miter_module->addWire("\\gold_" + RTLIL::unescape_id(w1->name), w1->width); w2_gold->port_output = flag_make_outputs; - w2_gold->width = w1->width; - miter_module->add(w2_gold); - RTLIL::Wire *w2_gate = new RTLIL::Wire; - w2_gate->name = "\\gate_" + RTLIL::unescape_id(w1->name); + RTLIL::Wire *w2_gate = miter_module->addWire("\\gate_" + RTLIL::unescape_id(w1->name), w1->width); w2_gate->port_output = flag_make_outputs; - w2_gate->width = w1->width; - miter_module->add(w2_gate); - gold_cell->connections[w1->name] = w2_gold; - gate_cell->connections[w1->name] = w2_gate; + gold_cell->setPort(w1->name, w2_gold); + gate_cell->setPort(w1->name, w2_gate); RTLIL::SigSpec this_condition; if (flag_ignore_gold_x) { - RTLIL::SigSpec gold_x = miter_module->new_wire(w2_gold->width, NEW_ID); + RTLIL::SigSpec gold_x = miter_module->addWire(NEW_ID, w2_gold->width); for (int i = 0; i < w2_gold->width; i++) { - RTLIL::Cell *eqx_cell = new RTLIL::Cell; - eqx_cell->name = NEW_ID; - eqx_cell->type = "$eqx"; + RTLIL::Cell *eqx_cell = miter_module->addCell(NEW_ID, "$eqx"); eqx_cell->parameters["\\A_WIDTH"] = 1; eqx_cell->parameters["\\B_WIDTH"] = 1; eqx_cell->parameters["\\Y_WIDTH"] = 1; eqx_cell->parameters["\\A_SIGNED"] = 0; eqx_cell->parameters["\\B_SIGNED"] = 0; - eqx_cell->connections["\\A"] = RTLIL::SigSpec(w2_gold, 1, i); - eqx_cell->connections["\\B"] = RTLIL::State::Sx; - eqx_cell->connections["\\Y"] = gold_x.extract(i, 1); - miter_module->add(eqx_cell); + eqx_cell->setPort("\\A", RTLIL::SigSpec(w2_gold, i)); + eqx_cell->setPort("\\B", RTLIL::State::Sx); + eqx_cell->setPort("\\Y", gold_x.extract(i, 1)); } - RTLIL::SigSpec gold_masked = miter_module->new_wire(w2_gold->width, NEW_ID); - RTLIL::SigSpec gate_masked = miter_module->new_wire(w2_gate->width, NEW_ID); + RTLIL::SigSpec gold_masked = miter_module->addWire(NEW_ID, w2_gold->width); + RTLIL::SigSpec gate_masked = miter_module->addWire(NEW_ID, w2_gate->width); - RTLIL::Cell *or_gold_cell = new RTLIL::Cell; - or_gold_cell->name = NEW_ID; - or_gold_cell->type = "$or"; + RTLIL::Cell *or_gold_cell = miter_module->addCell(NEW_ID, "$or"); or_gold_cell->parameters["\\A_WIDTH"] = w2_gold->width; or_gold_cell->parameters["\\B_WIDTH"] = w2_gold->width; or_gold_cell->parameters["\\Y_WIDTH"] = w2_gold->width; or_gold_cell->parameters["\\A_SIGNED"] = 0; or_gold_cell->parameters["\\B_SIGNED"] = 0; - or_gold_cell->connections["\\A"] = w2_gold; - or_gold_cell->connections["\\B"] = gold_x; - or_gold_cell->connections["\\Y"] = gold_masked; - miter_module->add(or_gold_cell); - - RTLIL::Cell *or_gate_cell = new RTLIL::Cell; - or_gate_cell->name = NEW_ID; - or_gate_cell->type = "$or"; + or_gold_cell->setPort("\\A", w2_gold); + or_gold_cell->setPort("\\B", gold_x); + or_gold_cell->setPort("\\Y", gold_masked); + + RTLIL::Cell *or_gate_cell = miter_module->addCell(NEW_ID, "$or"); or_gate_cell->parameters["\\A_WIDTH"] = w2_gate->width; or_gate_cell->parameters["\\B_WIDTH"] = w2_gate->width; or_gate_cell->parameters["\\Y_WIDTH"] = w2_gate->width; or_gate_cell->parameters["\\A_SIGNED"] = 0; or_gate_cell->parameters["\\B_SIGNED"] = 0; - or_gate_cell->connections["\\A"] = w2_gate; - or_gate_cell->connections["\\B"] = gold_x; - or_gate_cell->connections["\\Y"] = gate_masked; - miter_module->add(or_gate_cell); - - RTLIL::Cell *eq_cell = new RTLIL::Cell; - eq_cell->name = NEW_ID; - eq_cell->type = "$eqx"; + or_gate_cell->setPort("\\A", w2_gate); + or_gate_cell->setPort("\\B", gold_x); + or_gate_cell->setPort("\\Y", gate_masked); + + RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx"); eq_cell->parameters["\\A_WIDTH"] = w2_gold->width; eq_cell->parameters["\\B_WIDTH"] = w2_gate->width; eq_cell->parameters["\\Y_WIDTH"] = 1; eq_cell->parameters["\\A_SIGNED"] = 0; eq_cell->parameters["\\B_SIGNED"] = 0; - eq_cell->connections["\\A"] = gold_masked; - eq_cell->connections["\\B"] = gate_masked; - eq_cell->connections["\\Y"] = miter_module->new_wire(1, NEW_ID); - this_condition = eq_cell->connections["\\Y"]; - miter_module->add(eq_cell); + eq_cell->setPort("\\A", gold_masked); + eq_cell->setPort("\\B", gate_masked); + eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID)); + this_condition = eq_cell->getPort("\\Y"); } else { - RTLIL::Cell *eq_cell = new RTLIL::Cell; - eq_cell->name = NEW_ID; - eq_cell->type = "$eqx"; + RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx"); eq_cell->parameters["\\A_WIDTH"] = w2_gold->width; eq_cell->parameters["\\B_WIDTH"] = w2_gate->width; eq_cell->parameters["\\Y_WIDTH"] = 1; eq_cell->parameters["\\A_SIGNED"] = 0; eq_cell->parameters["\\B_SIGNED"] = 0; - eq_cell->connections["\\A"] = w2_gold; - eq_cell->connections["\\B"] = w2_gate; - eq_cell->connections["\\Y"] = miter_module->new_wire(1, NEW_ID); - this_condition = eq_cell->connections["\\Y"]; - miter_module->add(eq_cell); + eq_cell->setPort("\\A", w2_gold); + eq_cell->setPort("\\B", w2_gate); + eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID)); + this_condition = eq_cell->getPort("\\Y"); } if (flag_make_outcmp) { - RTLIL::Wire *w_cmp = new RTLIL::Wire; - w_cmp->name = "\\cmp_" + RTLIL::unescape_id(w1->name); + RTLIL::Wire *w_cmp = miter_module->addWire("\\cmp_" + RTLIL::unescape_id(w1->name)); w_cmp->port_output = true; - miter_module->add(w_cmp); - miter_module->connections.push_back(RTLIL::SigSig(w_cmp, this_condition)); + miter_module->connect(RTLIL::SigSig(w_cmp, this_condition)); } all_conditions.append(this_condition); } } - if (all_conditions.width != 1) { - RTLIL::Cell *reduce_cell = new RTLIL::Cell; - reduce_cell->name = NEW_ID; - reduce_cell->type = "$reduce_and"; - reduce_cell->parameters["\\A_WIDTH"] = all_conditions.width; + if (all_conditions.size() != 1) { + RTLIL::Cell *reduce_cell = miter_module->addCell(NEW_ID, "$reduce_and"); + reduce_cell->parameters["\\A_WIDTH"] = all_conditions.size(); reduce_cell->parameters["\\Y_WIDTH"] = 1; reduce_cell->parameters["\\A_SIGNED"] = 0; - reduce_cell->connections["\\A"] = all_conditions; - reduce_cell->connections["\\Y"] = miter_module->new_wire(1, NEW_ID); - all_conditions = reduce_cell->connections["\\Y"]; - miter_module->add(reduce_cell); + reduce_cell->setPort("\\A", all_conditions); + reduce_cell->setPort("\\Y", miter_module->addWire(NEW_ID)); + all_conditions = reduce_cell->getPort("\\Y"); } if (flag_make_assert) { - RTLIL::Cell *assert_cell = new RTLIL::Cell; - assert_cell->name = NEW_ID; - assert_cell->type = "$assert"; - assert_cell->connections["\\A"] = all_conditions; - assert_cell->connections["\\EN"] = RTLIL::SigSpec(1, 1); - miter_module->add(assert_cell); + RTLIL::Cell *assert_cell = miter_module->addCell(NEW_ID, "$assert"); + assert_cell->setPort("\\A", all_conditions); + assert_cell->setPort("\\EN", RTLIL::SigSpec(1, 1)); } - RTLIL::Wire *w_trigger = new RTLIL::Wire; - w_trigger->name = "\\trigger"; + RTLIL::Wire *w_trigger = miter_module->addWire("\\trigger"); w_trigger->port_output = true; - miter_module->add(w_trigger); - RTLIL::Cell *not_cell = new RTLIL::Cell; - not_cell->name = NEW_ID; - not_cell->type = "$not"; - not_cell->parameters["\\A_WIDTH"] = all_conditions.width; - not_cell->parameters["\\A_WIDTH"] = all_conditions.width; + RTLIL::Cell *not_cell = miter_module->addCell(NEW_ID, "$not"); + not_cell->parameters["\\A_WIDTH"] = all_conditions.size(); + not_cell->parameters["\\A_WIDTH"] = all_conditions.size(); not_cell->parameters["\\Y_WIDTH"] = w_trigger->width; not_cell->parameters["\\A_SIGNED"] = 0; - not_cell->connections["\\A"] = all_conditions; - not_cell->connections["\\Y"] = w_trigger; - miter_module->add(not_cell); + not_cell->setPort("\\A", all_conditions); + not_cell->setPort("\\Y", w_trigger); miter_module->fixup_ports(); + + if (flag_flatten) { + log_push(); + Pass::call_on_module(design, miter_module, "flatten; opt_const -keepdc -undriven;;"); + log_pop(); + } } struct MiterPass : public Pass { @@ -313,6 +284,9 @@ struct MiterPass : public Pass { log(" -make_assert\n"); log(" also create an 'assert' cell that checks if trigger is always low.\n"); log("\n"); + log(" -flatten\n"); + log(" call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index c08271590..fd0abf4a5 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -30,6 +30,8 @@ #include <stdlib.h> #include <stdio.h> #include <algorithm> +#include <errno.h> +#include <string.h> namespace { @@ -92,19 +94,35 @@ struct SatHelper RTLIL::SigSpec big_lhs, big_rhs; - for (auto &it : module->wires) + for (auto &it : module->wires_) { if (it.second->attributes.count("\\init") == 0) continue; RTLIL::SigSpec lhs = sigmap(it.second); RTLIL::SigSpec rhs = it.second->attributes.at("\\init"); - log_assert(lhs.width == rhs.width); + log_assert(lhs.size() == rhs.size()); + + RTLIL::SigSpec removed_bits; + for (int i = 0; i < lhs.size(); i++) { + RTLIL::SigSpec bit = lhs.extract(i, 1); + if (!satgen.initial_state.check_all(bit)) { + removed_bits.append(bit); + lhs.remove(i, 1); + rhs.remove(i, 1); + i--; + } + } - log("Import set-constraint from init attribute: %s = %s\n", log_signal(lhs), log_signal(rhs)); - big_lhs.remove2(lhs, &big_rhs); - big_lhs.append(lhs); - big_rhs.append(rhs); + if (removed_bits.size()) + log("Warning: ignoring initial value on non-register: %s\n", log_signal(removed_bits)); + + if (lhs.size()) { + log("Import set-constraint from init attribute: %s = %s\n", log_signal(lhs), log_signal(rhs)); + big_lhs.remove2(lhs, &big_rhs); + big_lhs.append(lhs); + big_rhs.append(rhs); + } } for (auto &s : sets_init) @@ -118,9 +136,9 @@ struct SatHelper show_signal_pool.add(sigmap(lhs)); show_signal_pool.add(sigmap(rhs)); - if (lhs.width != rhs.width) + if (lhs.size() != rhs.size()) log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - s.first.c_str(), log_signal(lhs), lhs.width, s.second.c_str(), log_signal(rhs), rhs.width); + s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); log("Import set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs)); big_lhs.remove2(lhs, &big_rhs); @@ -130,7 +148,6 @@ struct SatHelper if (!satgen.initial_state.check_all(big_lhs)) { RTLIL::SigSpec rem = satgen.initial_state.remove(big_lhs); - rem.optimize(); log_cmd_error("Found -set-init bits that are not part of the initial_state: %s\n", log_signal(rem)); } @@ -144,17 +161,17 @@ struct SatHelper RTLIL::SigSpec rem = satgen.initial_state.export_all(); rem.remove(big_lhs); big_lhs.append(rem); - big_rhs.append(RTLIL::SigSpec(RTLIL::State::Sx, rem.width)); + big_rhs.append(RTLIL::SigSpec(RTLIL::State::Sx, rem.size())); } if (set_init_zero) { RTLIL::SigSpec rem = satgen.initial_state.export_all(); rem.remove(big_lhs); big_lhs.append(rem); - big_rhs.append(RTLIL::SigSpec(RTLIL::State::S0, rem.width)); + big_rhs.append(RTLIL::SigSpec(RTLIL::State::S0, rem.size())); } - if (big_lhs.width == 0) { + if (big_lhs.size() == 0) { log("No constraints for initial state found.\n\n"); return; } @@ -187,9 +204,9 @@ struct SatHelper show_signal_pool.add(sigmap(lhs)); show_signal_pool.add(sigmap(rhs)); - if (lhs.width != rhs.width) + if (lhs.size() != rhs.size()) log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - s.first.c_str(), log_signal(lhs), lhs.width, s.second.c_str(), log_signal(rhs), rhs.width); + s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); log("Import set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs)); big_lhs.remove2(lhs, &big_rhs); @@ -208,11 +225,11 @@ struct SatHelper show_signal_pool.add(sigmap(lhs)); show_signal_pool.add(sigmap(rhs)); - if (lhs.width != rhs.width) + if (lhs.size() != rhs.size()) log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - s.first.c_str(), log_signal(lhs), lhs.width, s.second.c_str(), log_signal(rhs), rhs.width); + s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); - log("Import set-constraint for timestep: %s = %s\n", log_signal(lhs), log_signal(rhs)); + log("Import set-constraint for this timestep: %s = %s\n", log_signal(lhs), log_signal(rhs)); big_lhs.remove2(lhs, &big_rhs); big_lhs.append(lhs); big_rhs.append(rhs); @@ -226,7 +243,7 @@ struct SatHelper log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.c_str()); show_signal_pool.add(sigmap(lhs)); - log("Import unset-constraint for timestep: %s\n", log_signal(lhs)); + log("Import unset-constraint for this timestep: %s\n", log_signal(lhs)); big_lhs.remove2(lhs, &big_rhs); } @@ -289,7 +306,7 @@ struct SatHelper for (int t = 0; t < 3; t++) for (auto &sig : sets_def_undef[t]) { - log("Import %s constraint for timestep: %s\n", t == 0 ? "def" : t == 1 ? "any_undef" : "all_undef", log_signal(sig)); + log("Import %s constraint for this timestep: %s\n", t == 0 ? "def" : t == 1 ? "any_undef" : "all_undef", log_signal(sig)); std::vector<int> undef_sig = satgen.importUndefSigSpec(sig, timestep); if (t == 0) ez.assume(ez.NOT(ez.expression(ezSAT::OpOr, undef_sig))); @@ -300,11 +317,11 @@ struct SatHelper } int import_cell_counter = 0; - for (auto &c : module->cells) + for (auto &c : module->cells_) if (design->selected(module, c.second)) { // log("Import cell: %s\n", RTLIL::id2cstr(c.first)); if (satgen.importCell(c.second, timestep)) { - for (auto &p : c.second->connections) + for (auto &p : c.second->connections()) if (ct.cell_output(c.second->type, p.first)) show_drivers.insert(sigmap(p.second), c.second); import_cell_counter++; @@ -318,7 +335,7 @@ struct SatHelper int setup_proof(int timestep = -1) { - assert(prove.size() || prove_x.size() || prove_asserts); + log_assert(prove.size() || prove_x.size() || prove_asserts); RTLIL::SigSpec big_lhs, big_rhs; std::vector<int> prove_bits; @@ -336,9 +353,9 @@ struct SatHelper show_signal_pool.add(sigmap(lhs)); show_signal_pool.add(sigmap(rhs)); - if (lhs.width != rhs.width) + if (lhs.size() != rhs.size()) log_cmd_error("Proof expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - s.first.c_str(), log_signal(lhs), lhs.width, s.second.c_str(), log_signal(rhs), rhs.width); + s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); log("Import proof-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs)); big_lhs.remove2(lhs, &big_rhs); @@ -364,9 +381,9 @@ struct SatHelper show_signal_pool.add(sigmap(lhs)); show_signal_pool.add(sigmap(rhs)); - if (lhs.width != rhs.width) + if (lhs.size() != rhs.size()) log_cmd_error("Proof-x expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - s.first.c_str(), log_signal(lhs), lhs.width, s.second.c_str(), log_signal(rhs), rhs.width); + s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); log("Import proof-x-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs)); big_lhs.remove2(lhs, &big_rhs); @@ -389,10 +406,8 @@ struct SatHelper if (prove_asserts) { RTLIL::SigSpec asserts_a, asserts_en; satgen.getAsserts(asserts_a, asserts_en, timestep); - asserts_a.expand(); - asserts_en.expand(); - for (size_t i = 0; i < asserts_a.chunks.size(); i++) - log("Import proof for assert: %s when %s.\n", log_signal(asserts_a.chunks[i]), log_signal(asserts_en.chunks[i])); + for (int i = 0; i < SIZE(asserts_a); i++) + log("Import proof for assert: %s when %s.\n", log_signal(asserts_a[i]), log_signal(asserts_en[i])); prove_bits.push_back(satgen.importAsserts(timestep)); } @@ -490,7 +505,7 @@ struct SatHelper final_signals.add(sig); } else { for (auto &d : drivers) - for (auto &p : d->connections) { + for (auto &p : d->connections()) { if (d->type == "$dff" && p.first == "\\CLK") continue; if (d->type.substr(0, 6) == "$_DFF_" && p.first == "\\C") @@ -521,12 +536,12 @@ struct SatHelper std::vector<int> modelUndefExpressions; - for (auto &c : modelSig.chunks) + for (auto &c : modelSig.chunks()) if (c.wire != NULL) { ModelBlockInfo info; RTLIL::SigSpec chunksig = c; - info.width = chunksig.width; + info.width = chunksig.size(); info.description = log_signal(chunksig); for (int timestep = -1; timestep <= max_timestep; timestep++) @@ -551,7 +566,7 @@ struct SatHelper // Add initial state signals as collected by satgen // modelSig = satgen.initial_state.export_all(); - for (auto &c : modelSig.chunks) + for (auto &c : modelSig.chunks()) if (c.wire != NULL) { ModelBlockInfo info; @@ -559,7 +574,7 @@ struct SatHelper info.timestep = 0; info.offset = modelExpressions.size(); - info.width = chunksig.width; + info.width = chunksig.size(); info.description = log_signal(chunksig); modelInfo.insert(info); @@ -631,6 +646,109 @@ struct SatHelper log(" no model variables selected for display.\n"); } + void dump_model_to_vcd(std::string vcd_file_name) + { + FILE *f = fopen(vcd_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno)); + + log("Dumping SAT model to VCD file %s\n", vcd_file_name.c_str()); + + time_t timestamp; + struct tm* now; + char stime[128] = {}; + time(×tamp); + now = localtime(×tamp); + strftime(stime, sizeof(stime), "%c", now); + + std::string module_fname = "unknown"; + auto apos = module->attributes.find("\\src"); + if(apos != module->attributes.end()) + module_fname = module->attributes["\\src"].decode_string(); + + fprintf(f, "$date\n"); + fprintf(f, " %s\n", stime); + fprintf(f, "$end\n"); + fprintf(f, "$version\n"); + fprintf(f, " Generated by %s\n", yosys_version_str); + fprintf(f, "$end\n"); + fprintf(f, "$comment\n"); + fprintf(f, " Generated from SAT problem in module %s (declared at %s)\n", + module->name.c_str(), module_fname.c_str()); + fprintf(f, "$end\n"); + + // VCD has some limits on internal (non-display) identifier names, so make legal ones + std::map<std::string, std::string> vcdnames; + + fprintf(f, "$timescale 1ns\n"); // arbitrary time scale since actual clock period is unknown/unimportant + fprintf(f, "$scope module %s $end\n", module->name.c_str()); + for (auto &info : modelInfo) + { + if (vcdnames.find(info.description) != vcdnames.end()) + continue; + + char namebuf[16]; + snprintf(namebuf, sizeof(namebuf), "v%d", static_cast<int>(vcdnames.size())); + vcdnames[info.description] = namebuf; + + // Even display identifiers can't use some special characters + std::string legal_desc = info.description.c_str(); + for (auto &c : legal_desc) { + if(c == '$') + c = '_'; + if(c == ':') + c = '_'; + } + + fprintf(f, "$var wire %d %s %s $end\n", info.width, namebuf, legal_desc.c_str()); + + // Need to look at first *two* cycles! + // We need to put a name on all variables but those without an initialization clause + // have no value at timestep 0 + if(info.timestep > 1) + break; + } + fprintf(f, "$upscope $end\n"); + fprintf(f, "$enddefinitions $end\n"); + fprintf(f, "$dumpvars\n"); + + static const char bitvals[] = "01xzxx"; + + int last_timestep = -2; + for (auto &info : modelInfo) + { + RTLIL::Const value; + + for (int i = 0; i < info.width; i++) { + value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0); + if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i)) + value.bits.back() = RTLIL::State::Sx; + } + + if (info.timestep != last_timestep) { + if(last_timestep == 0) + fprintf(f, "$end\n"); + else + fprintf(f, "#%d\n", info.timestep); + last_timestep = info.timestep; + } + + if(info.width == 1) { + fprintf(f, "%c%s\n", bitvals[value.bits[0]], vcdnames[info.description].c_str()); + } else { + fprintf(f, "b"); + for(int k=info.width-1; k >= 0; k --) //need to flip bit ordering for VCD + fprintf(f, "%c", bitvals[value.bits[k]]); + fprintf(f, " %s\n", vcdnames[info.description].c_str()); + } + } + + if (last_timestep == -2) + log(" no model variables selected for display.\n"); + + fclose(f); + } + void invalidate_model(bool max_undef) { std::vector<int> clause; @@ -756,7 +874,7 @@ struct SatPass : public Pass { log(" -set-def-at <N> <signal>\n"); log(" -set-any-undef-at <N> <signal>\n"); log(" -set-all-undef-at <N> <signal>\n"); - log(" add undef contraints in the given timestep.\n"); + log(" add undef constraints in the given timestep.\n"); log("\n"); log(" -set-init <signal> <value>\n"); log(" set the initial value for the register driving the signal to the value\n"); @@ -770,6 +888,13 @@ struct SatPass : public Pass { log(" -set-init-zero\n"); log(" set all initial states (not set using -set-init) to zero\n"); log("\n"); + log(" -dump_vcd <vcd-file-name>\n"); + log(" dump SAT model (counter example in proof) to VCD file\n"); + log("\n"); + log(" -dump_cnf <cnf-file-name>\n"); + log(" dump CNF of SAT problem (in DIMACS format). in temporal induction\n"); + log(" proofs this is the CNF of the first induction step.\n"); + log("\n"); log("The following additional options can be used to set up a proof. If also -seq\n"); log("is passed, a temporal induction proof is performed.\n"); log("\n"); @@ -792,9 +917,15 @@ struct SatPass : public Pass { log(" -prove-asserts\n"); log(" Prove that all asserts in the design hold.\n"); log("\n"); + log(" -prove-skip <N>\n"); + log(" Do not enforce the prove-condition for the first <N> time steps.\n"); + log("\n"); log(" -maxsteps <N>\n"); log(" Set a maximum length for the induction.\n"); log("\n"); + log(" -initsteps <N>\n"); + log(" Set initial length for the induction.\n"); + log("\n"); log(" -timeout <N>\n"); log(" Maximum number of seconds a single SAT instance may take.\n"); log("\n"); @@ -817,11 +948,12 @@ struct SatPass : public Pass { std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at; std::map<int, std::vector<std::string>> unsets_at, sets_def_at, sets_any_undef_at, sets_all_undef_at; std::vector<std::string> shows, sets_def, sets_any_undef, sets_all_undef; - int loopcount = 0, seq_len = 0, maxsteps = 0, timeout = 0; + int loopcount = 0, seq_len = 0, maxsteps = 0, initsteps = 0, timeout = 0, prove_skip = 0; bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false; bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false; bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false; bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false; + std::string vcd_file_name, cnf_file_name; log_header("Executing SAT pass (solving SAT problems in the circuit).\n"); @@ -861,6 +993,10 @@ struct SatPass : public Pass { maxsteps = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-initsteps" && argidx+1 < args.size()) { + initsteps = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-ignore_div_by_zero") { ignore_div_by_zero = true; continue; @@ -926,6 +1062,10 @@ struct SatPass : public Pass { prove_asserts = true; continue; } + if (args[argidx] == "-prove-skip" && argidx+1 < args.size()) { + prove_skip = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-seq" && argidx+1 < args.size()) { seq_len = atoi(args[++argidx].c_str()); continue; @@ -995,12 +1135,20 @@ struct SatPass : public Pass { ignore_unknown_cells = true; continue; } + if (args[argidx] == "-dump_vcd" && argidx+1 < args.size()) { + vcd_file_name = args[++argidx]; + continue; + } + if (args[argidx] == "-dump_cnf" && argidx+1 < args.size()) { + cnf_file_name = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); RTLIL::Module *module = NULL; - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { if (module) log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", @@ -1013,25 +1161,31 @@ struct SatPass : public Pass { if (!prove.size() && !prove_x.size() && !prove_asserts && tempinduct) log_cmd_error("Got -tempinduct but nothing to prove!\n"); + if (prove_skip && tempinduct) + log_cmd_error("Options -prove-skip and -tempinduct don't work with each other.\n"); + + if (prove_skip >= seq_len && prove_skip > 0) + log_cmd_error("The value of -prove-skip must be smaller than the one of -seq.\n"); + if (set_init_undef + set_init_zero + set_init_def > 1) log_cmd_error("The options -set-init-undef, -set-init-def, and -set-init-zero are exclusive!\n"); if (set_def_inputs) { - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->port_input) - sets_def.push_back(it.second->name); + sets_def.push_back(it.second->name.str()); } if (show_inputs) { - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->port_input) - shows.push_back(it.second->name); + shows.push_back(it.second->name.str()); } if (show_outputs) { - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->port_output) - shows.push_back(it.second->name); + shows.push_back(it.second->name.str()); } if (tempinduct) @@ -1107,6 +1261,8 @@ struct SatPass : public Pass { log("SAT temporal induction proof finished - model found for base case: FAIL!\n"); print_proof_failed(); basecase.print_model(); + if(!vcd_file_name.empty()) + basecase.dump_model_to_vcd(vcd_file_name); goto tip_failed; } @@ -1125,24 +1281,47 @@ struct SatPass : public Pass { if (inductlen > 1) inductstep.force_unique_state(1, inductlen + 1); - log("\n[induction step] Solving problem with %d variables and %d clauses..\n", - inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); - - if (!inductstep.solve(inductstep.ez.NOT(property))) { - if (inductstep.gotTimeout) - goto timeout; - log("Induction step proven: SUCCESS!\n"); - print_qed(); - goto tip_success; + if (inductlen < initsteps) + { + log("\n[induction step] Skipping problem with %d variables and %d clauses (below initsteps).\n", + inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); + inductstep.ez.assume(property); } + else + { + if (!cnf_file_name.empty()) + { + FILE *f = fopen(cnf_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); - log("Induction step failed. Incrementing induction length.\n"); - inductstep.ez.assume(property); + log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); + cnf_file_name.clear(); + + inductstep.ez.printDIMACS(f, false); + fclose(f); + } - inductstep.print_model(); + log("\n[induction step] Solving problem with %d variables and %d clauses..\n", + inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); + + if (!inductstep.solve(inductstep.ez.NOT(property))) { + if (inductstep.gotTimeout) + goto timeout; + log("Induction step proven: SUCCESS!\n"); + print_qed(); + goto tip_success; + } + + log("Induction step failed. Incrementing induction length.\n"); + inductstep.ez.assume(property); + inductstep.print_model(); + } } log("\nReached maximum number of time steps -> proof failed.\n"); + if(!vcd_file_name.empty()) + inductstep.dump_model_to_vcd(vcd_file_name); print_proof_failed(); tip_failed: @@ -1195,7 +1374,8 @@ struct SatPass : public Pass { for (int timestep = 1; timestep <= seq_len; timestep++) { sathelper.setup(timestep); if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) - prove_bits.push_back(sathelper.setup_proof(timestep)); + if (timestep > prove_skip) + prove_bits.push_back(sathelper.setup_proof(timestep)); } if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) sathelper.ez.assume(sathelper.ez.NOT(sathelper.ez.expression(ezSAT::OpAnd, prove_bits))); @@ -1203,10 +1383,18 @@ struct SatPass : public Pass { } sathelper.generate_model(); -#if 0 - // print CNF for debugging - sathelper.ez.printDIMACS(stdout, true); -#endif + if (!cnf_file_name.empty()) + { + FILE *f = fopen(cnf_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); + + log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); + cnf_file_name.clear(); + + sathelper.ez.printDIMACS(f, false); + fclose(f); + } int rerun_counter = 0; @@ -1230,6 +1418,9 @@ struct SatPass : public Pass { sathelper.print_model(); + if(!vcd_file_name.empty()) + sathelper.dump_model_to_vcd(vcd_file_name); + if (loopcount != 0) { loopcount--, rerun_counter++; sathelper.invalidate_model(max_undef); diff --git a/passes/techmap/.gitignore b/passes/techmap/.gitignore index ca9d3942c..e6dcc6bc0 100644 --- a/passes/techmap/.gitignore +++ b/passes/techmap/.gitignore @@ -1 +1 @@ -stdcells.inc +techmap.inc diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index ae1ebbb56..72998f87b 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -2,24 +2,30 @@ OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o +OBJS += passes/techmap/libparse.o + +ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o OBJS += passes/techmap/hilomap.o -OBJS += passes/techmap/libparse.o OBJS += passes/techmap/extract.o +OBJS += passes/techmap/maccmap.o +OBJS += passes/techmap/alumacc.o +endif -GENFILES += passes/techmap/stdcells.inc +GENFILES += passes/techmap/techmap.inc -passes/techmap/stdcells.inc: techlibs/common/stdcells.v - echo "// autogenerated from $<" > $@.new - od -v -td1 -w1 $< | awk 'BEGIN { print "static char stdcells_code[] = {"; } $$2 != "" { print $$2 ","; } \ - END { print 0 "};"; }' | fmt >> $@.new - mv $@.new $@ +passes/techmap/techmap.inc: techlibs/common/techmap.v + $(P) echo "// autogenerated from $<" > $@.new + $(Q) echo "static char stdcells_code[] = {" >> $@.new + $(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new + $(Q) echo "0};" >> $@.new + $(Q) mv $@.new $@ -passes/techmap/techmap.o: passes/techmap/stdcells.inc +passes/techmap/techmap.o: passes/techmap/techmap.inc TARGETS += yosys-filterlib GENFILES += passes/techmap/filterlib.o yosys-filterlib: passes/techmap/filterlib.o - $(CXX) -o yosys-filterlib $(LDFLAGS) $^ $(LDLIBS) + $(P) $(CXX) -o yosys-filterlib $(LDFLAGS) $^ $(LDLIBS) diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc new file mode 100644 index 000000000..1115eead5 --- /dev/null +++ b/passes/techmap/alumacc.cc @@ -0,0 +1,563 @@ +/* + * 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 "kernel/sigtools.h" +#include "kernel/macc.h" + +struct AlumaccWorker +{ + RTLIL::Module *module; + SigMap sigmap; + + struct maccnode_t { + Macc macc; + RTLIL::Cell *cell; + RTLIL::SigSpec y; + int users; + }; + + struct alunode_t + { + std::vector<RTLIL::Cell*> cells; + RTLIL::SigSpec a, b, c, y; + std::vector<std::tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; + bool is_signed, invert_b; + + RTLIL::Cell *alu_cell; + RTLIL::SigSpec cached_lt, cached_gt, cached_eq, cached_ne; + RTLIL::SigSpec cached_cf, cached_of, cached_sf; + + RTLIL::SigSpec get_lt() { + if (SIZE(cached_lt) == 0) + cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf(); + return cached_lt; + } + + RTLIL::SigSpec get_gt() { + if (SIZE(cached_gt) == 0) + cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq())); + return cached_gt; + } + + RTLIL::SigSpec get_eq() { + if (SIZE(cached_eq) == 0) + cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X")); + return cached_eq; + } + + RTLIL::SigSpec get_ne() { + if (SIZE(cached_ne) == 0) + cached_ne = alu_cell->module->Not(NEW_ID, get_eq()); + return cached_ne; + } + + RTLIL::SigSpec get_cf() { + if (SIZE(cached_cf) == 0) { + cached_cf = alu_cell->getPort("\\CO"); + log_assert(SIZE(cached_cf) >= 1); + cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[SIZE(cached_cf)-1]); + } + return cached_cf; + } + + RTLIL::SigSpec get_of() { + if (SIZE(cached_of) == 0) { + cached_of = {alu_cell->getPort("\\CO"), alu_cell->getPort("\\CI")}; + log_assert(SIZE(cached_of) >= 2); + cached_of = alu_cell->module->Xor(NEW_ID, cached_of[SIZE(cached_of)-1], cached_of[SIZE(cached_of)-2]); + } + return cached_of; + } + + RTLIL::SigSpec get_sf() { + if (SIZE(cached_sf) == 0) { + cached_sf = alu_cell->getPort("\\Y"); + cached_sf = cached_sf[SIZE(cached_sf)-1]; + } + return cached_sf; + } + }; + + std::map<RTLIL::SigBit, int> bit_users; + std::map<RTLIL::SigSpec, maccnode_t*> sig_macc; + std::map<RTLIL::SigSig, std::set<alunode_t*>> sig_alu; + int macc_counter, alu_counter; + + AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) + { + macc_counter = 0; + alu_counter = 0; + } + + void count_bit_users() + { + for (auto port : module->ports) + for (auto bit : sigmap(module->wire(port))) + bit_users[bit]++; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bit_users[bit]++; + } + + void extract_macc() + { + for (auto cell : module->selected_cells()) + { + if (!cell->type.in("$pos", "$neg", "$add", "$sub", "$mul")) + continue; + + log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type)); + + maccnode_t *n = new maccnode_t; + Macc::port_t new_port; + + n->cell = cell; + n->y = sigmap(cell->getPort("\\Y")); + n->users = 0; + + for (auto bit : n->y) + n->users = std::max(n->users, bit_users.at(bit) - 1); + + if (cell->type.in("$pos", "$neg")) + { + new_port.in_a = sigmap(cell->getPort("\\A")); + new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool(); + new_port.do_subtract = cell->type == "$neg"; + n->macc.ports.push_back(new_port); + } + + if (cell->type.in("$add", "$sub")) + { + new_port.in_a = sigmap(cell->getPort("\\A")); + new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool(); + new_port.do_subtract = false; + n->macc.ports.push_back(new_port); + + new_port.in_a = sigmap(cell->getPort("\\B")); + new_port.is_signed = cell->getParam("\\B_SIGNED").as_bool(); + new_port.do_subtract = cell->type == "$sub"; + n->macc.ports.push_back(new_port); + } + + if (cell->type.in("$mul")) + { + new_port.in_a = sigmap(cell->getPort("\\A")); + new_port.in_b = sigmap(cell->getPort("\\B")); + new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool(); + new_port.do_subtract = false; + n->macc.ports.push_back(new_port); + } + + log_assert(sig_macc.count(n->y) == 0); + sig_macc[n->y] = n; + } + } + + static bool macc_may_overflow(Macc &macc, int width, bool is_signed) + { + std::vector<int> port_sizes; + + for (auto &port : macc.ports) { + if (port.is_signed != is_signed) + return true; + if (!port.is_signed && port.do_subtract) + return true; + if (SIZE(port.in_b)) + port_sizes.push_back(SIZE(port.in_a) + SIZE(port.in_b)); + else + port_sizes.push_back(SIZE(port.in_a)); + } + + std::sort(port_sizes.begin(), port_sizes.end()); + + int acc_sum = 0, acc_shift = 0; + for (int sz : port_sizes) { + while ((sz - acc_shift) > 20) { + if (acc_sum & 1) + acc_sum++; + acc_sum = acc_sum >> 1; + acc_shift++; + } + acc_sum += (1 << (sz - acc_shift)) - 1; + } + + while (acc_sum) { + acc_sum = acc_sum >> 1; + acc_shift++; + } + + return acc_shift > width; + } + + void merge_macc() + { + while (1) + { + std::set<maccnode_t*> delete_nodes; + + for (auto &it : sig_macc) + { + auto n = it.second; + + if (delete_nodes.count(n)) + continue; + + for (int i = 0; i < SIZE(n->macc.ports); i++) + { + auto &port = n->macc.ports[i]; + + if (SIZE(port.in_b) > 0 || sig_macc.count(port.in_a) == 0) + continue; + + auto other_n = sig_macc.at(port.in_a); + + if (other_n->users > 1) + continue; + + if (SIZE(other_n->y) != SIZE(n->y) && macc_may_overflow(other_n->macc, SIZE(other_n->y), port.is_signed)) + continue; + + log(" merging $macc model for %s into %s.\n", log_id(other_n->cell), log_id(n->cell)); + + bool do_subtract = port.do_subtract; + for (int j = 0; j < SIZE(other_n->macc.ports); j++) { + if (do_subtract) + other_n->macc.ports[j].do_subtract = !other_n->macc.ports[j].do_subtract; + if (j == 0) + n->macc.ports[i--] = other_n->macc.ports[j]; + else + n->macc.ports.push_back(other_n->macc.ports[j]); + } + + delete_nodes.insert(other_n); + } + } + + if (delete_nodes.empty()) + break; + + for (auto n : delete_nodes) { + sig_macc.erase(n->y); + delete n; + } + } + } + + void macc_to_alu() + { + std::set<maccnode_t*> delete_nodes; + + for (auto &it : sig_macc) + { + auto n = it.second; + RTLIL::SigSpec A, B, C = n->macc.bit_ports; + bool a_signed = false, b_signed = false; + bool subtract_b = false; + alunode_t *alunode; + + for (auto &port : n->macc.ports) + if (SIZE(port.in_b) > 0) { + goto next_macc; + } else if (SIZE(port.in_a) == 1 && !port.is_signed && !port.do_subtract) { + C.append(port.in_a); + } else if (SIZE(A) || port.do_subtract) { + if (SIZE(B)) + goto next_macc; + B = port.in_a; + b_signed = port.is_signed; + subtract_b = port.do_subtract; + } else { + if (SIZE(A)) + goto next_macc; + A = port.in_a; + a_signed = port.is_signed; + } + + if (!a_signed || !b_signed) { + if (SIZE(A) == SIZE(n->y)) + a_signed = false; + if (SIZE(B) == SIZE(n->y)) + b_signed = false; + if (a_signed != b_signed) + goto next_macc; + } + + if (SIZE(A) == 0 && SIZE(C) > 0) { + A = C[0]; + C.remove(0); + } + + if (SIZE(B) == 0 && SIZE(C) > 0) { + B = C[0]; + C.remove(0); + } + + if (subtract_b) + C.append(RTLIL::S1); + + if (SIZE(C) > 1) + goto next_macc; + + if (!subtract_b && B < A && SIZE(B)) + std::swap(A, B); + + log(" creating $alu model for $macc %s.\n", log_id(n->cell)); + + alunode = new alunode_t; + alunode->cells.push_back(n->cell); + alunode->is_signed = a_signed; + alunode->invert_b = subtract_b; + + alunode->a = A; + alunode->b = B; + alunode->c = C; + alunode->y = n->y; + + sig_alu[RTLIL::SigSig(A, B)].insert(alunode); + delete_nodes.insert(n); + next_macc:; + } + + for (auto n : delete_nodes) { + sig_macc.erase(n->y); + delete n; + } + } + + void replace_macc() + { + for (auto &it : sig_macc) + { + auto n = it.second; + auto cell = module->addCell(NEW_ID, "$macc"); + macc_counter++; + + log(" creating $macc cell for %s: %s\n", log_id(n->cell), log_id(cell)); + + n->macc.optimize(SIZE(n->y)); + n->macc.to_cell(cell); + cell->setPort("\\Y", n->y); + cell->fixup_parameters(); + module->remove(n->cell); + delete n; + } + + sig_macc.clear(); + } + + void extract_cmp_alu() + { + std::vector<RTLIL::Cell*> lge_cells, eq_cells; + + for (auto cell : module->selected_cells()) + { + if (cell->type.in("$lt", "$le", "$ge", "$gt")) + lge_cells.push_back(cell); + if (cell->type.in("$eq", "$eqx", "$ne", "$nex")) + eq_cells.push_back(cell); + } + + for (auto cell : lge_cells) + { + log(" creating $alu model for %s (%s):", log_id(cell), log_id(cell->type)); + + bool cmp_less = cell->type.in("$lt", "$le"); + bool cmp_equal = cell->type.in("$le", "$ge"); + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + + RTLIL::SigSpec A = sigmap(cell->getPort("\\A")); + RTLIL::SigSpec B = sigmap(cell->getPort("\\B")); + RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y")); + + if (B < A && SIZE(B)) { + cmp_less = !cmp_less; + std::swap(A, B); + } + + alunode_t *n = nullptr; + + for (auto node : sig_alu[RTLIL::SigSig(A, B)]) + if (node->is_signed == is_signed && node->invert_b && node->c == RTLIL::S1) { + n = node; + break; + } + + if (n == nullptr) { + n = new alunode_t; + n->a = A; + n->b = B; + n->c = RTLIL::S1; + n->y = module->addWire(NEW_ID, std::max(SIZE(A), SIZE(B))); + n->is_signed = is_signed; + n->invert_b = true; + sig_alu[RTLIL::SigSig(A, B)].insert(n); + log(" new $alu\n"); + } else { + log(" merged with %s.\n", log_id(n->cells.front())); + } + + n->cells.push_back(cell); + n->cmp.push_back(std::make_tuple(cmp_less, !cmp_less, cmp_equal, false, Y)); + } + + for (auto cell : eq_cells) + { + bool cmp_equal = cell->type.in("$eq", "$eqx"); + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + + RTLIL::SigSpec A = sigmap(cell->getPort("\\A")); + RTLIL::SigSpec B = sigmap(cell->getPort("\\B")); + RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y")); + + if (B < A && SIZE(B)) + std::swap(A, B); + + alunode_t *n = nullptr; + + for (auto node : sig_alu[RTLIL::SigSig(A, B)]) + if (node->is_signed == is_signed && node->invert_b && node->c == RTLIL::S1) { + n = node; + break; + } + + if (n != nullptr) { + log(" creating $alu model for %s (%s): merged with %s.\n", log_id(cell), log_id(cell->type), log_id(n->cells.front())); + n->cells.push_back(cell); + n->cmp.push_back(std::make_tuple(false, false, cmp_equal, !cmp_equal, Y)); + } + } + } + + void replace_alu() + { + for (auto &it1 : sig_alu) + for (auto n : it1.second) + { + if (SIZE(n->b) == 0 && SIZE(n->c) == 0 && SIZE(n->cmp) == 0) + { + n->alu_cell = module->addPos(NEW_ID, n->a, n->y, n->is_signed); + + log(" creating $pos cell for "); + for (int i = 0; i < SIZE(n->cells); i++) + log("%s%s", i ? ", ": "", log_id(n->cells[i])); + log(": %s\n", log_id(n->alu_cell)); + + goto delete_node; + } + + n->alu_cell = module->addCell(NEW_ID, "$alu"); + alu_counter++; + + log(" creating $alu cell for "); + for (int i = 0; i < SIZE(n->cells); i++) + log("%s%s", i ? ", ": "", log_id(n->cells[i])); + log(": %s\n", log_id(n->alu_cell)); + + n->alu_cell->setPort("\\A", n->a); + n->alu_cell->setPort("\\B", n->b); + n->alu_cell->setPort("\\CI", SIZE(n->c) ? n->c : RTLIL::S0); + n->alu_cell->setPort("\\BI", n->invert_b ? RTLIL::S1 : RTLIL::S0); + n->alu_cell->setPort("\\Y", n->y); + n->alu_cell->setPort("\\X", module->addWire(NEW_ID, SIZE(n->y))); + n->alu_cell->setPort("\\CO", module->addWire(NEW_ID, SIZE(n->y))); + n->alu_cell->fixup_parameters(n->is_signed, n->is_signed); + + for (auto &it : n->cmp) + { + bool cmp_lt = std::get<0>(it); + bool cmp_gt = std::get<1>(it); + bool cmp_eq = std::get<2>(it); + bool cmp_ne = std::get<3>(it); + RTLIL::SigSpec cmp_y = std::get<4>(it); + + RTLIL::SigSpec sig; + if (cmp_lt) sig.append(n->get_lt()); + if (cmp_gt) sig.append(n->get_gt()); + if (cmp_eq) sig.append(n->get_eq()); + if (cmp_ne) sig.append(n->get_ne()); + + if (SIZE(sig) > 1) + sig = module->ReduceOr(NEW_ID, sig); + + sig.extend(SIZE(cmp_y)); + module->connect(cmp_y, sig); + } + + delete_node: + for (auto c : n->cells) + module->remove(c); + delete n; + } + + sig_alu.clear(); + } + + void run() + { + log("Extracting $alu and $macc cells in module %s:\n", log_id(module)); + + count_bit_users(); + extract_macc(); + merge_macc(); + macc_to_alu(); + replace_macc(); + extract_cmp_alu(); + replace_alu(); + + log(" created %d $alu and %d $macc cells.\n", alu_counter, macc_counter); + } +}; + +struct AlumaccPass : public Pass { + AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" alumacc [selection]\n"); + log("\n"); + log("This pass translates arithmetic operations $add, $mul, $lt, etc. to $alu and\n"); + log("$macc cells.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing ALUMACC pass (create $alu and $macc cells).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // foobar_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + if (!mod->has_processes_warn()) { + AlumaccWorker worker(mod); + worker.run(); + } + } +} AlumaccPass; + diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 23d93353f..07993b868 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -21,6 +21,7 @@ #include "kernel/log.h" #include "libparse.h" #include <string.h> +#include <errno.h> using namespace PASS_DFFLIBMAP; @@ -28,7 +29,7 @@ struct cell_mapping { std::string cell_name; std::map<std::string, char> ports; }; -static std::map<std::string, cell_mapping> cell_mappings; +static std::map<RTLIL::IdString, cell_mapping> cell_mappings; static void logmap(std::string dff) { @@ -318,7 +319,7 @@ static bool expand_cellmap(std::string pattern, std::string inv) bool return_status = false; for (auto &it : cell_mappings) { - std::string from = it.first, to = it.first; + std::string from = it.first.str(), to = it.first.str(); if (from.size() != pattern.size()) continue; for (size_t i = 0; i < from.size(); i++) { @@ -342,7 +343,7 @@ static bool expand_cellmap(std::string pattern, std::string inv) static void map_sr_to_arst(const char *from, const char *to) { - if (cell_mappings.count(to) > 0) + if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) return; char from_clk_pol = from[8], from_set_pol = from[9], from_clr_pol = from[10]; @@ -387,45 +388,45 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module) log("Mapping DFF cells in module `%s':\n", module->name.c_str()); std::vector<RTLIL::Cell*> cell_list; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0) cell_list.push_back(it.second); } std::map<std::string, int> stats; - for (auto cell : cell_list) { - cell_mapping &cm = cell_mappings[cell->type]; - RTLIL::Cell *new_cell = new RTLIL::Cell; - new_cell->name = cell->name; - new_cell->type = "\\" + cm.cell_name; + for (auto cell : cell_list) + { + auto cell_type = cell->type; + auto cell_name = cell->name; + auto cell_connections = cell->connections(); + module->remove(cell); + + cell_mapping &cm = cell_mappings[cell_type]; + RTLIL::Cell *new_cell = module->addCell(cell_name, "\\" + cm.cell_name); + for (auto &port : cm.ports) { RTLIL::SigSpec sig; if ('A' <= port.second && port.second <= 'Z') { - sig = cell->connections[std::string("\\") + port.second]; + sig = cell_connections[std::string("\\") + port.second]; + } else + if (port.second == 'q') { + RTLIL::SigSpec old_sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; + sig = module->addWire(NEW_ID, SIZE(old_sig)); + module->addNotGate(NEW_ID, sig, old_sig); } else if ('a' <= port.second && port.second <= 'z') { - sig = cell->connections[std::string("\\") + char(port.second - ('a' - 'A'))]; - RTLIL::Cell *inv_cell = new RTLIL::Cell; - RTLIL::Wire *inv_wire = new RTLIL::Wire; - inv_cell->name = stringf("$dfflibmap$inv$%d", RTLIL::autoidx); - inv_wire->name = stringf("$dfflibmap$sig$%d", RTLIL::autoidx++); - inv_cell->type = "$_INV_"; - inv_cell->connections[port.second == 'q' ? "\\Y" : "\\A"] = sig; - sig = RTLIL::SigSpec(inv_wire); - inv_cell->connections[port.second == 'q' ? "\\A" : "\\Y"] = sig; - module->cells[inv_cell->name] = inv_cell; - module->wires[inv_wire->name] = inv_wire; + sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; + sig = module->NotGate(NEW_ID, sig); } else if (port.second == '0' || port.second == '1') { sig = RTLIL::SigSpec(port.second == '0' ? 0 : 1, 1); } else if (port.second != 0) log_abort(); - new_cell->connections["\\" + port.first] = sig; + new_cell->setPort("\\" + port.first, sig); } - stats[stringf(" mapped %%d %s cells to %s cells.\n", cell->type.c_str(), new_cell->type.c_str())]++; - module->cells[cell->name] = new_cell; - delete cell; + + stats[stringf(" mapped %%d %s cells to %s cells.\n", cell_type.c_str(), new_cell->type.c_str())]++; } for (auto &stat: stats) @@ -467,11 +468,12 @@ struct DfflibmapPass : public Pass { if (liberty_file.empty()) log_cmd_error("Missing `-liberty liberty_file' option!\n"); - FILE *f = fopen(liberty_file.c_str(), "r"); - if (f == NULL) + std::ifstream f; + f.open(liberty_file.c_str()); + if (f.fail()) log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); LibertyParser libparser(f); - fclose(f); + f.close(); find_cell(libparser.ast, "$_DFF_N_", false, false, false, false); find_cell(libparser.ast, "$_DFF_P_", true, false, false, false); @@ -528,7 +530,7 @@ struct DfflibmapPass : public Pass { log(" final dff cell mappings:\n"); logmap_all(); - for (auto &it : design->modules) + for (auto &it : design->modules_) if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox")) dfflibmap(design, it.second); diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index eff14ff01..221e9e49d 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -23,7 +23,6 @@ #include "libs/subcircuit/subcircuit.h" #include <algorithm> #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <string.h> @@ -34,8 +33,14 @@ namespace class SubCircuitSolver : public SubCircuit::Solver { public: + bool ignore_parameters; + std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> ignored_parameters; std::set<RTLIL::IdString> cell_attr, wire_attr; + SubCircuitSolver() : ignore_parameters(false) + { + } + bool compareAttributes(const std::set<RTLIL::IdString> &attr, const std::map<RTLIL::IdString, RTLIL::Const> &needleAttr, const std::map<RTLIL::IdString, RTLIL::Const> &haystackAttr) { for (auto &it : attr) { @@ -46,12 +51,70 @@ namespace return true; } + RTLIL::Const unified_param(RTLIL::IdString cell_type, RTLIL::IdString param, RTLIL::Const value) + { + if (cell_type.substr(0, 1) != "$" || cell_type.substr(0, 2) == "$_") + return value; + + #define param_bool(_n) if (param == _n) return value.as_bool(); + param_bool("\\ARST_POLARITY"); + param_bool("\\A_SIGNED"); + param_bool("\\B_SIGNED"); + param_bool("\\CLK_ENABLE"); + param_bool("\\CLK_POLARITY"); + param_bool("\\CLR_POLARITY"); + param_bool("\\EN_POLARITY"); + param_bool("\\SET_POLARITY"); + param_bool("\\TRANSPARENT"); + #undef param_bool + + #define param_int(_n) if (param == _n) return value.as_int(); + param_int("\\ABITS") + param_int("\\A_WIDTH") + param_int("\\B_WIDTH") + param_int("\\CTRL_IN_WIDTH") + param_int("\\CTRL_OUT_WIDTH") + param_int("\\OFFSET") + param_int("\\PRIORITY") + param_int("\\RD_PORTS") + param_int("\\SIZE") + param_int("\\STATE_BITS") + param_int("\\STATE_NUM") + param_int("\\STATE_NUM_LOG2") + param_int("\\STATE_RST") + param_int("\\S_WIDTH") + param_int("\\TRANS_NUM") + param_int("\\WIDTH") + param_int("\\WR_PORTS") + param_int("\\Y_WIDTH") + #undef param_int + + return value; + } + virtual bool userCompareNodes(const std::string &, const std::string &, void *needleUserData, const std::string &, const std::string &, void *haystackUserData, const std::map<std::string, std::string> &portMapping) { RTLIL::Cell *needleCell = (RTLIL::Cell*) needleUserData; RTLIL::Cell *haystackCell = (RTLIL::Cell*) haystackUserData; + if (!needleCell || !haystackCell) { + log_assert(!needleCell && !haystackCell); + return true; + } + + if (!ignore_parameters) { + std::map<RTLIL::IdString, RTLIL::Const> needle_param, haystack_param; + for (auto &it : needleCell->parameters) + if (!ignored_parameters.count(std::pair<RTLIL::IdString, RTLIL::IdString>(needleCell->type, it.first))) + needle_param[it.first] = unified_param(needleCell->type, it.first, it.second); + for (auto &it : haystackCell->parameters) + if (!ignored_parameters.count(std::pair<RTLIL::IdString, RTLIL::IdString>(haystackCell->type, it.first))) + haystack_param[it.first] = unified_param(haystackCell->type, it.first, it.second); + if (needle_param != haystack_param) + return false; + } + if (cell_attr.size() > 0 && !compareAttributes(cell_attr, needleCell->attributes, haystackCell->attributes)) return false; @@ -61,16 +124,13 @@ namespace RTLIL::Wire *lastHaystackWire = NULL; std::map<RTLIL::IdString, RTLIL::Const> emptyAttr; - for (auto &conn : needleCell->connections) + for (auto &conn : needleCell->connections()) { RTLIL::SigSpec needleSig = conn.second; - RTLIL::SigSpec haystackSig = haystackCell->connections.at(portMapping.at(conn.first)); - - needleSig.expand(); - haystackSig.expand(); + RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str())); - for (int i = 0; i < std::min(needleSig.width, haystackSig.width); i++) { - RTLIL::Wire *needleWire = needleSig.chunks.at(i).wire, *haystackWire = haystackSig.chunks.at(i).wire; + for (int i = 0; i < std::min(needleSig.size(), haystackSig.size()); i++) { + RTLIL::Wire *needleWire = needleSig[i].wire, *haystackWire = haystackSig[i].wire; if (needleWire != lastNeedleWire || haystackWire != lastHaystackWire) if (!compareAttributes(wire_attr, needleWire ? needleWire->attributes : emptyAttr, haystackWire ? haystackWire->attributes : emptyAttr)) return false; @@ -92,7 +152,7 @@ namespace int max_fanout = -1, std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> *split = NULL) { SigMap sigmap(mod); - std::map<RTLIL::SigChunk, bit_ref_t> sig_bit_ref; + std::map<RTLIL::SigBit, bit_ref_t> sig_bit_ref; if (sel && !sel->selected(mod)) { log(" Skipping module %s as it is not selected.\n", id2cstr(mod->name)); @@ -121,111 +181,106 @@ namespace std::map<std::pair<RTLIL::Wire*, int>, int> sig_use_count; if (max_fanout > 0) - for (auto &cell_it : mod->cells) + for (auto &cell_it : mod->cells_) { RTLIL::Cell *cell = cell_it.second; if (!sel || sel->selected(mod, cell)) - for (auto &conn : cell->connections) { + for (auto &conn : cell->connections()) { RTLIL::SigSpec conn_sig = conn.second; sigmap.apply(conn_sig); - conn_sig.expand(); - for (auto &chunk : conn_sig.chunks) - if (chunk.wire != NULL) - sig_use_count[std::pair<RTLIL::Wire*, int>(chunk.wire, chunk.offset)]++; + for (auto &bit : conn_sig) + if (bit.wire != NULL) + sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)]++; } } // create graph nodes from cells - for (auto &cell_it : mod->cells) + for (auto &cell_it : mod->cells_) { RTLIL::Cell *cell = cell_it.second; if (sel && !sel->selected(mod, cell)) continue; - std::string type = cell->type; + std::string type = cell->type.str(); if (sel == NULL && type.substr(0, 2) == "\\$") type = type.substr(1); - graph.createNode(cell->name, type, (void*)cell); + graph.createNode(cell->name.str(), type, (void*)cell); - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) { - graph.createPort(cell->name, conn.first, conn.second.width); + graph.createPort(cell->name.str(), conn.first.str(), conn.second.size()); if (split && split->count(std::pair<RTLIL::IdString, RTLIL::IdString>(cell->type, conn.first)) > 0) continue; RTLIL::SigSpec conn_sig = conn.second; sigmap.apply(conn_sig); - conn_sig.expand(); - for (size_t i = 0; i < conn_sig.chunks.size(); i++) + for (int i = 0; i < conn_sig.size(); i++) { - auto &chunk = conn_sig.chunks[i]; - assert(chunk.width == 1); + auto &bit = conn_sig[i]; - if (chunk.wire == NULL) { + if (bit.wire == NULL) { if (constports) { std::string node = "$const$x"; - if (chunk.data.bits[0] == RTLIL::State::S0) node = "$const$0"; - if (chunk.data.bits[0] == RTLIL::State::S1) node = "$const$1"; - if (chunk.data.bits[0] == RTLIL::State::Sz) node = "$const$z"; - graph.createConnection(cell->name, conn.first, i, node, "\\Y", 0); + if (bit == RTLIL::State::S0) node = "$const$0"; + if (bit == RTLIL::State::S1) node = "$const$1"; + if (bit == RTLIL::State::Sz) node = "$const$z"; + graph.createConnection(cell->name.str(), conn.first.str(), i, node, "\\Y", 0); } else - graph.createConstant(cell->name, conn.first, i, int(chunk.data.bits[0])); + graph.createConstant(cell->name.str(), conn.first.str(), i, int(bit.data)); continue; } - if (max_fanout > 0 && sig_use_count[std::pair<RTLIL::Wire*, int>(chunk.wire, chunk.offset)] > max_fanout) + if (max_fanout > 0 && sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)] > max_fanout) continue; - if (sel && !sel->selected(mod, chunk.wire)) + if (sel && !sel->selected(mod, bit.wire)) continue; - if (sig_bit_ref.count(chunk) == 0) { - bit_ref_t &bit_ref = sig_bit_ref[chunk]; - bit_ref.cell = cell->name; - bit_ref.port = conn.first; + if (sig_bit_ref.count(bit) == 0) { + bit_ref_t &bit_ref = sig_bit_ref[bit]; + bit_ref.cell = cell->name.str(); + bit_ref.port = conn.first.str(); bit_ref.bit = i; } - bit_ref_t &bit_ref = sig_bit_ref[chunk]; - graph.createConnection(bit_ref.cell, bit_ref.port, bit_ref.bit, cell->name, conn.first, i); + bit_ref_t &bit_ref = sig_bit_ref[bit]; + graph.createConnection(bit_ref.cell, bit_ref.port, bit_ref.bit, cell->name.str(), conn.first.str(), i); } } } // mark external signals (used in non-selected cells) - for (auto &cell_it : mod->cells) + for (auto &cell_it : mod->cells_) { RTLIL::Cell *cell = cell_it.second; if (sel && !sel->selected(mod, cell)) - for (auto &conn : cell->connections) + for (auto &conn : cell->connections()) { RTLIL::SigSpec conn_sig = conn.second; sigmap.apply(conn_sig); - conn_sig.expand(); - for (auto &chunk : conn_sig.chunks) - if (sig_bit_ref.count(chunk) != 0) { - bit_ref_t &bit_ref = sig_bit_ref[chunk]; + for (auto &bit : conn_sig) + if (sig_bit_ref.count(bit) != 0) { + bit_ref_t &bit_ref = sig_bit_ref[bit]; graph.markExtern(bit_ref.cell, bit_ref.port, bit_ref.bit); } } } // mark external signals (used in module ports) - for (auto &wire_it : mod->wires) + for (auto &wire_it : mod->wires_) { RTLIL::Wire *wire = wire_it.second; if (wire->port_id > 0) { RTLIL::SigSpec conn_sig(wire); sigmap.apply(conn_sig); - conn_sig.expand(); - for (auto &chunk : conn_sig.chunks) - if (sig_bit_ref.count(chunk) != 0) { - bit_ref_t &bit_ref = sig_bit_ref[chunk]; + for (auto &bit : conn_sig) + if (sig_bit_ref.count(bit) != 0) { + bit_ref_t &bit_ref = sig_bit_ref[bit]; graph.markExtern(bit_ref.cell, bit_ref.port, bit_ref.bit); } } @@ -238,21 +293,18 @@ namespace RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit::Solver::Result &match) { SigMap sigmap(needle); - SigSet<std::pair<std::string, int>> sig2port; + SigSet<std::pair<RTLIL::IdString, int>> sig2port; // create new cell - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = stringf("$extract$%s$%d", needle->name.c_str(), RTLIL::autoidx++); - cell->type = needle->name; - haystack->add(cell); + RTLIL::Cell *cell = haystack->addCell(stringf("$extract$%s$%d", needle->name.c_str(), autoidx++), needle->name); // create cell ports - for (auto &it : needle->wires) { + for (auto &it : needle->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_id > 0) { for (int i = 0; i < wire->width; i++) - sig2port.insert(sigmap(RTLIL::SigSpec(wire, 1, i)), std::pair<std::string, int>(wire->name, i)); - cell->connections[wire->name] = RTLIL::SigSpec(RTLIL::State::Sz, wire->width); + sig2port.insert(sigmap(RTLIL::SigSpec(wire, i)), std::pair<RTLIL::IdString, int>(wire->name, i)); + cell->setPort(wire->name, RTLIL::SigSpec(RTLIL::State::Sz, wire->width)); } } @@ -266,20 +318,20 @@ namespace if (needle_cell == NULL) continue; - for (auto &conn : needle_cell->connections) { + for (auto &conn : needle_cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); - if (mapping.portMapping.count(conn.first) > 0 && sig2port.has(sigmap(sig))) { - sig.expand(); - for (int i = 0; i < sig.width; i++) - for (auto &port : sig2port.find(sig.chunks[i])) { - RTLIL::SigSpec bitsig = haystack_cell->connections.at(mapping.portMapping[conn.first]).extract(i, 1); - cell->connections.at(port.first).replace(port.second, bitsig); + if (mapping.portMapping.count(conn.first.str()) > 0 && sig2port.has(sigmap(sig))) { + for (int i = 0; i < sig.size(); i++) + for (auto &port : sig2port.find(sig[i])) { + RTLIL::SigSpec bitsig = haystack_cell->getPort(mapping.portMapping[conn.first.str()]).extract(i, 1); + RTLIL::SigSpec new_sig = cell->getPort(port.first); + new_sig.replace(port.second, bitsig); + cell->setPort(port.first, new_sig); } } } - haystack->cells.erase(haystack_cell->name); - delete haystack_cell; + haystack->remove(haystack_cell); } return cell; @@ -315,6 +367,10 @@ struct ExtractPass : public Pass { log(" use the modules in this file as reference. This option can be used\n"); log(" multiple times.\n"); log("\n"); + log(" -map %%<design-name>\n"); + log(" use the modules in this in-memory design as reference. This option can\n"); + log(" be used multiple times.\n"); + log("\n"); log(" -verbose\n"); log(" print debug output while analyzing\n"); log("\n"); @@ -347,6 +403,12 @@ struct ExtractPass : public Pass { log(" -wire_attr <attribute_name>\n"); log(" Attributes on wires with the given name must match.\n"); log("\n"); + log(" -ignore_parameters\n"); + log(" Do not use parameters when matching cells.\n"); + log("\n"); + log(" -ignore_param <cell_type> <parameter_name>\n"); + log(" Do not use this parameter when matching cells.\n"); + log("\n"); log("This pass does not operate on modules with uprocessed processes in it.\n"); log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n"); log("\n"); @@ -494,6 +556,15 @@ struct ExtractPass : public Pass { solver.wire_attr.insert(RTLIL::escape_id(args[++argidx])); continue; } + if (args[argidx] == "-ignore_parameters") { + solver.ignore_parameters = true; + continue; + } + if (args[argidx] == "-ignore_param" && argidx+2 < args.size()) { + solver.ignored_parameters.insert(std::pair<RTLIL::IdString, RTLIL::IdString>(RTLIL::escape_id(args[argidx+1]), RTLIL::escape_id(args[argidx+2]))); + argidx += 2; + continue; + } break; } extra_args(args, argidx, design); @@ -524,16 +595,33 @@ struct ExtractPass : public Pass { if (!mine_mode) { map = new RTLIL::Design; - for (auto &filename : map_filenames) { - FILE *f = fopen(filename.c_str(), "rt"); - if (f == NULL) - log_cmd_error("Can't open map file `%s'.\n", filename.c_str()); - Frontend::frontend_call(map, f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog"); - fclose(f); - - if (filename.size() <= 3 || filename.substr(filename.size()-3) != ".il") { - Pass::call(map, "proc"); - Pass::call(map, "opt_clean"); + for (auto &filename : map_filenames) + { + if (filename.substr(0, 1) == "%") + { + if (!saved_designs.count(filename.substr(1))) { + delete map; + log_cmd_error("Can't saved design `%s'.\n", filename.c_str()+1); + } + for (auto mod : saved_designs.at(filename.substr(1))->modules()) + if (!map->has(mod->name)) + map->add(mod->clone()); + } + else + { + std::ifstream f; + f.open(filename.c_str()); + if (f.fail()) { + delete map; + log_cmd_error("Can't open map file `%s'.\n", filename.c_str()); + } + Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog"); + f.close(); + + if (filename.size() <= 3 || filename.substr(filename.size()-3) != ".il") { + Pass::call(map, "proc"); + Pass::call(map, "opt_clean"); + } } } } @@ -544,7 +632,7 @@ struct ExtractPass : public Pass { log_header("Creating graphs for SubCircuit library.\n"); if (!mine_mode) - for (auto &mod_it : map->modules) { + for (auto &mod_it : map->modules_) { SubCircuit::Graph mod_graph; std::string graph_name = "needle_" + RTLIL::unescape_id(mod_it.first); log("Creating needle graph %s.\n", graph_name.c_str()); @@ -555,7 +643,7 @@ struct ExtractPass : public Pass { } } - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { SubCircuit::Graph mod_graph; std::string graph_name = "haystack_" + RTLIL::unescape_id(mod_it.first); log("Creating haystack graph %s.\n", graph_name.c_str()); @@ -613,7 +701,7 @@ struct ExtractPass : public Pass { log("\nFrequent SubCircuit with %d nodes and %d matches:\n", int(result.nodes.size()), result.totalMatchesAfterLimits); log(" primary match in %s:", id2cstr(haystack_map.at(result.graphId)->name)); for (auto &node : result.nodes) - log(" %s", id2cstr(node.nodeId)); + log(" %s", RTLIL::unescape_id(node.nodeId).c_str()); log("\n"); for (auto &it : result.matchesPerGraph) log(" matches in %s: %d\n", id2cstr(haystack_map.at(it.first)->name), it.second); @@ -628,49 +716,44 @@ struct ExtractPass : public Pass { cells.insert((RTLIL::Cell*)node.userData); for (auto cell : cells) - for (auto &conn : cell->connections) { + for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); - for (auto &chunk : sig.chunks) + for (auto &chunk : sig.chunks()) if (chunk.wire != NULL) wires.insert(chunk.wire); } RTLIL::Module *newMod = new RTLIL::Module; newMod->name = stringf("\\needle%05d_%s_%dx", needleCounter++, id2cstr(haystack_map.at(result.graphId)->name), result.totalMatchesAfterLimits); - map->modules[newMod->name] = newMod; + map->add(newMod); - int portCounter = 1; for (auto wire : wires) { - RTLIL::Wire *newWire = new RTLIL::Wire; - newWire->name = wire->name; - newWire->width = wire->width; - newWire->port_id = portCounter++; + RTLIL::Wire *newWire = newMod->addWire(wire->name, wire->width); newWire->port_input = true; newWire->port_output = true; - newMod->add(newWire); } + newMod->fixup_ports(); + for (auto cell : cells) { - RTLIL::Cell *newCell = new RTLIL::Cell; - newCell->name = cell->name; - newCell->type = cell->type; + RTLIL::Cell *newCell = newMod->addCell(cell->name, cell->type); newCell->parameters = cell->parameters; - for (auto &conn : cell->connections) { - RTLIL::SigSpec sig = sigmap(conn.second); - for (auto &chunk : sig.chunks) + for (auto &conn : cell->connections()) { + std::vector<RTLIL::SigChunk> chunks = sigmap(conn.second); + for (auto &chunk : chunks) if (chunk.wire != NULL) - chunk.wire = newMod->wires.at(chunk.wire->name); - newCell->connections[conn.first] = sig; + chunk.wire = newMod->wires_.at(chunk.wire->name); + newCell->setPort(conn.first, chunks); } - newMod->add(newCell); } } - FILE *f = fopen(mine_outfile.c_str(), "wt"); - if (f == NULL) + std::ofstream f; + f.open(mine_outfile.c_str(), std::ofstream::trunc); + if (f.fail()) log_error("Can't open output file `%s'.\n", mine_outfile.c_str()); - Backend::backend_call(map, f, mine_outfile, "ilang"); - fclose(f); + Backend::backend_call(map, &f, mine_outfile, "ilang"); + f.close(); } delete map; diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index bc5caa38c..784c4cf31 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -26,36 +26,28 @@ static std::string locell_celltype, locell_portname; static bool singleton_mode; static RTLIL::Module *module; -static RTLIL::SigChunk last_hi, last_lo; +static RTLIL::SigBit last_hi, last_lo; void hilomap_worker(RTLIL::SigSpec &sig) { - sig.expand(); - for (auto &c : sig.chunks) { - if (c.wire == NULL && (c.data.bits.at(0) == RTLIL::State::S1) && !hicell_celltype.empty()) { - if (!singleton_mode || last_hi.width == 0) { - last_hi = RTLIL::SigChunk(NEW_WIRE(module, 1)); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = RTLIL::escape_id(hicell_celltype); - cell->connections[RTLIL::escape_id(hicell_portname)] = last_hi; - module->add(cell); + for (auto &bit : sig) { + if (bit == RTLIL::State::S1 && !hicell_celltype.empty()) { + if (!singleton_mode || last_hi == RTLIL::State::Sm) { + last_hi = module->addWire(NEW_ID); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(hicell_celltype)); + cell->setPort(RTLIL::escape_id(hicell_portname), last_hi); } - c = last_hi; + bit = last_hi; } - if (c.wire == NULL && (c.data.bits.at(0) == RTLIL::State::S0) && !locell_celltype.empty()) { - if (!singleton_mode || last_lo.width == 0) { - last_lo = RTLIL::SigChunk(NEW_WIRE(module, 1)); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = RTLIL::escape_id(locell_celltype); - cell->connections[RTLIL::escape_id(locell_portname)] = last_lo; - module->add(cell); + if (bit == RTLIL::State::S0 && !locell_celltype.empty()) { + if (!singleton_mode || last_lo == RTLIL::State::Sm) { + last_lo = module->addWire(NEW_ID); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(locell_celltype)); + cell->setPort(RTLIL::escape_id(locell_portname), last_lo); } - c = last_lo; + bit = last_lo; } } - sig.optimize(); } struct HilomapPass : public Pass { @@ -112,15 +104,15 @@ struct HilomapPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules) + for (auto &it : design->modules_) { module = it.second; if (!design->selected(module)) continue; - last_hi = RTLIL::SigChunk(); - last_lo = RTLIL::SigChunk(); + last_hi = RTLIL::State::Sm; + last_lo = RTLIL::State::Sm; module->rewrite_sigspecs(hilomap_worker); } diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index b98214977..9cd23ce6f 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -57,6 +57,11 @@ struct IopadmapPass : public Pass { log(" -nameparam <param_name>\n"); log(" Use the specified parameter to set the port name.\n"); log("\n"); + log(" -bits\n"); + log(" create individual bit-wide buffers even for ports that\n"); + log(" are wider. (the default behavio is to create word-wide\n"); + log(" buffers use -widthparam to set the word size on the cell.)\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -66,6 +71,7 @@ struct IopadmapPass : public Pass { std::string outpad_celltype, outpad_portname, outpad_portname2; std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2; std::string widthparam, nameparam; + bool flag_bits = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -97,18 +103,22 @@ struct IopadmapPass : public Pass { nameparam = args[++argidx]; continue; } + if (arg == "-bits") { + flag_bits = true; + continue; + } break; } extra_args(args, argidx, design); - for (auto &it : design->modules) + for (auto &it : design->modules_) { RTLIL::Module *module = it.second; - if (!design->selected(module)) + if (!design->selected(module) || module->get_bool_attribute("\\blackbox")) continue; - for (auto &it2 : module->wires) + for (auto &it2 : module->wires_) { RTLIL::Wire *wire = it2.second; @@ -146,31 +156,46 @@ struct IopadmapPass : public Pass { } else log_abort(); - if (wire->width != 1 && widthparam.empty()) { - log("Don't map multi-bit port %s.%s: Missing option -widthparam.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name)); + if (!flag_bits && wire->width != 1 && widthparam.empty()) { + log("Don't map multi-bit port %s.%s: Missing option -widthparam or -bits.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name)); continue; } log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype.c_str()); - RTLIL::Cell *cell = new RTLIL::Cell; - cell->name = NEW_ID; - cell->type = RTLIL::escape_id(celltype); - cell->connections[RTLIL::escape_id(portname)] = RTLIL::SigSpec(wire); + RTLIL::Wire *new_wire = NULL; if (!portname2.empty()) { - RTLIL::Wire *new_wire = new RTLIL::Wire; - *new_wire = *wire; - wire->name = NEW_ID; - module->wires[wire->name] = wire; - module->wires[new_wire->name] = new_wire; - cell->connections[RTLIL::escape_id(portname2)] = RTLIL::SigSpec(new_wire); + new_wire = module->addWire(NEW_ID, wire); + module->swap_names(new_wire, wire); + } + + if (flag_bits) + { + for (int i = 0; i < wire->width; i++) + { + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); + cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i)); + if (!portname2.empty()) + cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire, i)); + if (!widthparam.empty()) + cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1); + if (!nameparam.empty()) + cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(stringf("%s[%d]", RTLIL::id2cstr(wire->name), i)); + cell->attributes["\\keep"] = RTLIL::Const(1); + } + } + else + { + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); + cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire)); + if (!portname2.empty()) + cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire)); + if (!widthparam.empty()) + cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width); + if (!nameparam.empty()) + cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(RTLIL::id2cstr(wire->name)); + cell->attributes["\\keep"] = RTLIL::Const(1); } - if (!widthparam.empty()) - cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width); - if (!nameparam.empty()) - cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(RTLIL::id2cstr(wire->name)); - cell->attributes["\\keep"] = RTLIL::Const(1); - module->add(cell); wire->port_id = 0; wire->port_input = false; diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 8cbb8e2be..612fa1117 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -21,6 +21,10 @@ #include <stdlib.h> #include <string.h> +#include <istream> +#include <fstream> +#include <iostream> + #ifndef FILTERLIB #include "kernel/log.h" #endif @@ -63,9 +67,10 @@ void LibertyAst::dump(FILE *f, std::string indent, std::string path, bool path_o } fprintf(f, "%s%s", indent.c_str(), id.c_str()); - if (!args.empty()) { + if (!args.empty() || !children.empty()) { + fprintf(f, "("); for (size_t i = 0; i < args.size(); i++) - fprintf(f, "%s%s", i > 0 ? ", " : "(", args[i].c_str()); + fprintf(f, "%s%s", i > 0 ? ", " : "", args[i].c_str()); fprintf(f, ")"); } if (!value.empty()) @@ -84,19 +89,19 @@ int LibertyParser::lexer(std::string &str) int c; do { - c = fgetc(f); + c = f.get(); } while (c == ' ' || c == '\t' || c == '\r'); if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') { str = c; while (1) { - c = fgetc(f); + c = f.get(); if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') str += c; else break; } - ungetc(c, f); + f.unget(); // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); return 'v'; } @@ -104,7 +109,7 @@ int LibertyParser::lexer(std::string &str) if (c == '"') { str = c; while (1) { - c = fgetc(f); + c = f.get(); if (c == '\n') line++; str += c; @@ -116,34 +121,34 @@ int LibertyParser::lexer(std::string &str) } if (c == '/') { - c = fgetc(f); + c = f.get(); if (c == '*') { int last_c = 0; while (c > 0 && (last_c != '*' || c != '/')) { last_c = c; - c = fgetc(f); + c = f.get(); if (c == '\n') line++; } return lexer(str); } else if (c == '/') { while (c > 0 && c != '\n') - c = fgetc(f); + c = f.get(); line++; return lexer(str); } - ungetc(c, f); + f.unget(); // fprintf(stderr, "LEX: char >>/<<\n"); return '/'; } if (c == '\\') { - c = fgetc(f); + c = f.get(); if (c == '\r') - c = fgetc(f); + c = f.get(); if (c == '\n') return lexer(str); - ungetc(c, f); + f.unget(); return '\\'; } @@ -607,16 +612,20 @@ int main(int argc, char **argv) } } - FILE *f = stdin; + std::istream *f = &std::cin; + if (argc == 3) { - f = fopen(argv[2], "r"); - if (f == NULL) { + std::ifstream *ff = new std::ifstream; + ff->open(argv[2]); + if (ff->fail()) { + delete ff; fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]); usage(); } + f = ff; } - LibertyParser parser(f); + LibertyParser parser(*f); if (parser.ast) { if (flag_verilogsim) gen_verilogsim(parser.ast); @@ -625,7 +634,7 @@ int main(int argc, char **argv) } if (argc == 3) - fclose(f); + delete f; return 0; } diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index eff268bbb..247487424 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -41,10 +41,10 @@ namespace PASS_DFFLIBMAP struct LibertyParser { - FILE *f; + std::istream &f; int line; LibertyAst *ast; - LibertyParser(FILE *f) : f(f), line(1), ast(parse()) {} + LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {} ~LibertyParser() { if (ast) delete ast; } int lexer(std::string &str); LibertyAst *parse(); diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc new file mode 100644 index 000000000..2d625eefe --- /dev/null +++ b/passes/techmap/maccmap.cc @@ -0,0 +1,394 @@ +/* + * 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 "kernel/macc.h" + +extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false); + +struct MaccmapWorker +{ + std::vector<std::set<RTLIL::SigBit>> bits; + RTLIL::Module *module; + int width; + + MaccmapWorker(RTLIL::Module *module, int width) : module(module), width(width) + { + bits.resize(width); + } + + void add(RTLIL::SigBit bit, int position) + { + if (position >= width || bit == RTLIL::S0) + return; + + if (bits.at(position).count(bit)) { + bits.at(position).erase(bit); + add(bit, position+1); + } else { + bits.at(position).insert(bit); + } + } + + void add(RTLIL::SigSpec a, bool is_signed, bool do_subtract) + { + a.extend(width, is_signed); + + if (do_subtract) { + a = module->Not(NEW_ID, a); + add(RTLIL::S1, 0); + } + + for (int i = 0; i < width; i++) + add(a[i], i); + } + + void add(RTLIL::SigSpec a, RTLIL::SigSpec b, bool is_signed, bool do_subtract) + { + if (SIZE(a) < SIZE(b)) + std::swap(a, b); + + a.extend(width, is_signed); + + if (SIZE(b) > width) + b.extend(width, is_signed); + + for (int i = 0; i < SIZE(b); i++) + if (is_signed && i+1 == SIZE(b)) + { + a = {module->Not(NEW_ID, a.extract(i, width-i)), RTLIL::SigSpec(0, i)}; + add(module->And(NEW_ID, a, RTLIL::SigSpec(b[i], width)), false, do_subtract); + add({b[i], RTLIL::SigSpec(0, i)}, false, do_subtract); + } + else + { + add(module->And(NEW_ID, a, RTLIL::SigSpec(b[i], width)), false, do_subtract); + a = {a.extract(0, width-1), RTLIL::S0}; + } + } + + void fulladd(RTLIL::SigSpec &in1, RTLIL::SigSpec &in2, RTLIL::SigSpec &in3, RTLIL::SigSpec &out1, RTLIL::SigSpec &out2) + { + int start_index = 0, stop_index = SIZE(in1); + + while (start_index < stop_index && in1[start_index] == RTLIL::S0 && in2[start_index] == RTLIL::S0 && in3[start_index] == RTLIL::S0) + start_index++; + + while (start_index < stop_index && in1[stop_index-1] == RTLIL::S0 && in2[stop_index-1] == RTLIL::S0 && in3[stop_index-1] == RTLIL::S0) + stop_index--; + + if (start_index == stop_index) + { + out1 = RTLIL::SigSpec(0, SIZE(in1)); + out2 = RTLIL::SigSpec(0, SIZE(in1)); + } + else + { + RTLIL::SigSpec out_zeros_lsb(0, start_index), out_zeros_msb(0, SIZE(in1)-stop_index); + + in1 = in1.extract(start_index, stop_index-start_index); + in2 = in2.extract(start_index, stop_index-start_index); + in3 = in3.extract(start_index, stop_index-start_index); + + int width = SIZE(in1); + RTLIL::Wire *w1 = module->addWire(NEW_ID, width); + RTLIL::Wire *w2 = module->addWire(NEW_ID, width); + + RTLIL::Cell *cell = module->addCell(NEW_ID, "$fa"); + cell->setParam("\\WIDTH", width); + cell->setPort("\\A", in1); + cell->setPort("\\B", in2); + cell->setPort("\\C", in3); + cell->setPort("\\Y", w1); + cell->setPort("\\X", w2); + + out1 = {out_zeros_msb, w1, out_zeros_lsb}; + out2 = {out_zeros_msb, w2, out_zeros_lsb}; + } + } + + int tree_bit_slots(int n) + { + #if 0 + int retval = 1; + while (n > 2) { + retval += n / 3; + n = 2*(n / 3) + (n % 3); + } + return retval; + #else + return std::max(n - 1, 0); + #endif + } + + RTLIL::SigSpec synth() + { + std::vector<RTLIL::SigSpec> summands; + std::vector<RTLIL::SigBit> tree_sum_bits; + int unique_tree_bits = 0; + int count_tree_words = 0; + + while (1) + { + RTLIL::SigSpec summand(0, width); + bool got_data_bits = false; + + for (int i = 0; i < width; i++) + if (!bits.at(i).empty()) { + auto it = bits.at(i).begin(); + summand[i] = *it; + bits.at(i).erase(it); + got_data_bits = true; + } + + if (!got_data_bits) + break; + + summands.push_back(summand); + + while (1) + { + int free_bit_slots = tree_bit_slots(SIZE(summands)) - SIZE(tree_sum_bits); + + int max_depth = 0, max_position = 0; + for (int i = 0; i < width; i++) + if (max_depth <= SIZE(bits.at(i))) { + max_depth = SIZE(bits.at(i)); + max_position = i; + } + + if (max_depth == 0 || max_position > 4) + break; + + int required_bits = 0; + for (int i = 0; i <= max_position; i++) + if (SIZE(bits.at(i)) == max_depth) + required_bits += 1 << i; + + if (required_bits > free_bit_slots) + break; + + for (int i = 0; i <= max_position; i++) + if (SIZE(bits.at(i)) == max_depth) { + auto it = bits.at(i).begin(); + RTLIL::SigBit bit = *it; + for (int k = 0; k < (1 << i); k++, free_bit_slots--) + tree_sum_bits.push_back(bit); + bits.at(i).erase(it); + unique_tree_bits++; + } + + count_tree_words++; + } + } + + if (!tree_sum_bits.empty()) + log(" packed %d (%d) bits / %d words into adder tree\n", SIZE(tree_sum_bits), unique_tree_bits, count_tree_words); + + if (SIZE(summands) == 0) { + log_assert(tree_sum_bits.empty()); + return RTLIL::SigSpec(0, width); + } + + if (SIZE(summands) == 1) { + log_assert(tree_sum_bits.empty()); + return summands.front(); + } + + while (SIZE(summands) > 2) + { + std::vector<RTLIL::SigSpec> new_summands; + for (int i = 0; i < SIZE(summands); i += 3) + if (i+2 < SIZE(summands)) { + RTLIL::SigSpec in1 = summands[i]; + RTLIL::SigSpec in2 = summands[i+1]; + RTLIL::SigSpec in3 = summands[i+2]; + RTLIL::SigSpec out1, out2; + fulladd(in1, in2, in3, out1, out2); + RTLIL::SigBit extra_bit = RTLIL::S0; + if (!tree_sum_bits.empty()) { + extra_bit = tree_sum_bits.back(); + tree_sum_bits.pop_back(); + } + new_summands.push_back(out1); + new_summands.push_back({out2.extract(0, width-1), extra_bit}); + } else { + new_summands.push_back(summands[i]); + i -= 2; + } + summands.swap(new_summands); + } + + + RTLIL::Cell *c = module->addCell(NEW_ID, "$alu"); + c->setPort("\\A", summands.front()); + c->setPort("\\B", summands.back()); + c->setPort("\\CI", RTLIL::S0); + c->setPort("\\BI", RTLIL::S0); + c->setPort("\\Y", module->addWire(NEW_ID, width)); + c->setPort("\\X", module->addWire(NEW_ID, width)); + c->setPort("\\CO", module->addWire(NEW_ID, width)); + c->fixup_parameters(); + + if (!tree_sum_bits.empty()) { + c->setPort("\\CI", tree_sum_bits.back()); + tree_sum_bits.pop_back(); + } + log_assert(tree_sum_bits.empty()); + + return c->getPort("\\Y"); + } +}; + +void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) +{ + int width = SIZE(cell->getPort("\\Y")); + + Macc macc; + macc.from_cell(cell); + + RTLIL::SigSpec all_input_bits; + all_input_bits.append(cell->getPort("\\A")); + all_input_bits.append(cell->getPort("\\B")); + + if (all_input_bits.to_sigbit_set().count(RTLIL::Sx)) { + module->connect(cell->getPort("\\Y"), RTLIL::SigSpec(RTLIL::Sx, width)); + return; + } + + for (auto &port : macc.ports) + if (SIZE(port.in_b) == 0) + log(" %s %s (%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), + SIZE(port.in_a), port.is_signed ? "signed" : "unsigned"); + else + log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b), + SIZE(port.in_a), SIZE(port.in_b), port.is_signed ? "signed" : "unsigned"); + + if (SIZE(macc.bit_ports) != 0) + log(" add bits %s (%d bits)\n", log_signal(macc.bit_ports), SIZE(macc.bit_ports)); + + if (unmap) + { + typedef std::pair<RTLIL::SigSpec, bool> summand_t; + std::vector<summand_t> summands; + + for (auto &port : macc.ports) { + summand_t this_summand; + if (SIZE(port.in_b)) { + this_summand.first = module->addWire(NEW_ID, width); + module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed); + } else if (SIZE(port.in_a) != width) { + this_summand.first = module->addWire(NEW_ID, width); + module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed); + } else { + this_summand.first = port.in_a; + } + this_summand.second = port.do_subtract; + summands.push_back(this_summand); + } + + for (auto &bit : macc.bit_ports) + summands.push_back(summand_t(bit, false)); + + if (SIZE(summands) == 0) + summands.push_back(summand_t(RTLIL::SigSpec(0, width), false)); + + while (SIZE(summands) > 1) + { + std::vector<summand_t> new_summands; + for (int i = 0; i < SIZE(summands); i += 2) { + if (i+1 < SIZE(summands)) { + summand_t this_summand; + this_summand.first = module->addWire(NEW_ID, width); + this_summand.second = summands[i].second && summands[i+1].second; + if (summands[i].second == summands[i+1].second) + module->addAdd(NEW_ID, summands[i].first, summands[i+1].first, this_summand.first); + else if (summands[i].second) + module->addSub(NEW_ID, summands[i+1].first, summands[i].first, this_summand.first); + else if (summands[i+1].second) + module->addSub(NEW_ID, summands[i].first, summands[i+1].first, this_summand.first); + else + log_abort(); + new_summands.push_back(this_summand); + } else + new_summands.push_back(summands[i]); + } + summands.swap(new_summands); + } + + if (summands.front().second) + module->addNeg(NEW_ID, summands.front().first, cell->getPort("\\Y")); + else + module->connect(cell->getPort("\\Y"), summands.front().first); + } + else + { + MaccmapWorker worker(module, width); + + for (auto &port : macc.ports) + if (SIZE(port.in_b) == 0) + worker.add(port.in_a, port.is_signed, port.do_subtract); + else + worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract); + + for (auto &bit : macc.bit_ports) + worker.add(bit, 0); + + module->connect(cell->getPort("\\Y"), worker.synth()); + } +} + +struct MaccmapPass : public Pass { + MaccmapPass() : Pass("maccmap", "mapping macc cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" maccmap [-unmap] [selection]\n"); + log("\n"); + log("This pass maps $macc cells to yosys gate primitives. When the -unmap option is\n"); + log("used then the $macc cell is mapped to $and, $sub, etc. cells instead.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool unmap_mode = false; + + log_header("Executing MACCMAP pass (map $macc cells).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-unmap") { + unmap_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + for (auto cell : mod->selected_cells()) + if (cell->type == "$macc") { + log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type)); + maccmap(mod, cell, unmap_mode); + mod->remove(cell); + } + } +} MaccmapPass; + diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index e67b1e055..f8d5d4584 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -21,84 +21,52 @@ #include "kernel/sigtools.h" #include "kernel/log.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <string.h> -extern void simplemap_get_mappers(std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); +extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); static void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) { - int width = cell->parameters.at("\\Y_WIDTH").as_int(); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - sig_a.extend(width, cell->parameters.at("\\A_SIGNED").as_bool()); - sig_a.expand(); + sig_a.extend(SIZE(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - sig_y.expand(); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = "$_INV_"; - gate->connections["\\A"] = sig_a.chunks.at(i); - gate->connections["\\Y"] = sig_y.chunks.at(i); - module->add(gate); + for (int i = 0; i < SIZE(sig_y); i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->setPort("\\A", sig_a[i]); + gate->setPort("\\Y", sig_y[i]); } } static void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell) { - int width = cell->parameters.at("\\Y_WIDTH").as_int(); - - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - sig_a.extend(width, cell->parameters.at("\\A_SIGNED").as_bool()); - - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - - module->connections.push_back(RTLIL::SigSig(sig_y, sig_a)); -} - -static void simplemap_bu0(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at("\\Y_WIDTH").as_int(); - - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - sig_a.extend_u0(width, cell->parameters.at("\\A_SIGNED").as_bool()); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); + sig_a.extend_u0(SIZE(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); - module->connections.push_back(RTLIL::SigSig(sig_y, sig_a)); + module->connect(RTLIL::SigSig(sig_y, sig_a)); } static void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) { - int width = cell->parameters.at("\\Y_WIDTH").as_int(); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - sig_a.extend_u0(width, cell->parameters.at("\\A_SIGNED").as_bool()); - sig_a.expand(); - - RTLIL::SigSpec sig_b = cell->connections.at("\\B"); - sig_b.extend_u0(width, cell->parameters.at("\\B_SIGNED").as_bool()); - sig_b.expand(); - - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - sig_y.expand(); + sig_a.extend_u0(SIZE(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); + sig_b.extend_u0(SIZE(sig_y), cell->parameters.at("\\B_SIGNED").as_bool()); if (cell->type == "$xnor") { - RTLIL::SigSpec sig_t = module->new_wire(width, NEW_ID); - sig_t.expand(); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = "$_INV_"; - gate->connections["\\A"] = sig_t.chunks.at(i); - gate->connections["\\Y"] = sig_y.chunks.at(i); - module->add(gate); + RTLIL::SigSpec sig_t = module->addWire(NEW_ID, SIZE(sig_y)); + + for (int i = 0; i < SIZE(sig_y); i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->setPort("\\A", sig_t[i]); + gate->setPort("\\Y", sig_y[i]); } sig_y = sig_t; @@ -111,38 +79,33 @@ static void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) if (cell->type == "$xnor") gate_type = "$_XOR_"; log_assert(!gate_type.empty()); - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\A"] = sig_a.chunks.at(i); - gate->connections["\\B"] = sig_b.chunks.at(i); - gate->connections["\\Y"] = sig_y.chunks.at(i); - module->add(gate); + for (int i = 0; i < SIZE(sig_y); i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\A", sig_a[i]); + gate->setPort("\\B", sig_b[i]); + gate->setPort("\\Y", sig_y[i]); } } static void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) { - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - sig_a.expand(); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - - if (sig_y.width == 0) + if (sig_y.size() == 0) return; - if (sig_a.width == 0) { - if (cell->type == "$reduce_and") module->connections.push_back(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.width))); - if (cell->type == "$reduce_or") module->connections.push_back(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.width))); - if (cell->type == "$reduce_xor") module->connections.push_back(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.width))); - if (cell->type == "$reduce_xnor") module->connections.push_back(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.width))); - if (cell->type == "$reduce_bool") module->connections.push_back(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.width))); + if (sig_a.size() == 0) { + if (cell->type == "$reduce_and") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); + if (cell->type == "$reduce_or") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + if (cell->type == "$reduce_xor") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + if (cell->type == "$reduce_xnor") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); + if (cell->type == "$reduce_bool") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); return; } - if (sig_y.width > 1) { - module->connections.push_back(RTLIL::SigSig(sig_y.extract(1, sig_y.width-1), RTLIL::SigSpec(0, sig_y.width-1))); + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); } @@ -154,122 +117,106 @@ static void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) if (cell->type == "$reduce_bool") gate_type = "$_OR_"; log_assert(!gate_type.empty()); - RTLIL::SigSpec *last_output = NULL; + RTLIL::Cell *last_output_cell = NULL; - while (sig_a.width > 1) + while (sig_a.size() > 1) { - RTLIL::SigSpec sig_t = module->new_wire(sig_a.width / 2, NEW_ID); - sig_t.expand(); + RTLIL::SigSpec sig_t = module->addWire(NEW_ID, sig_a.size() / 2); - for (int i = 0; i < sig_a.width; i += 2) + for (int i = 0; i < sig_a.size(); i += 2) { - if (i+1 == sig_a.width) { - sig_t.append(sig_a.chunks.at(i)); + if (i+1 == sig_a.size()) { + sig_t.append(sig_a[i]); continue; } - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\A"] = sig_a.chunks.at(i); - gate->connections["\\B"] = sig_a.chunks.at(i+1); - gate->connections["\\Y"] = sig_t.chunks.at(i/2); - last_output = &gate->connections["\\Y"]; - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\A", sig_a[i]); + gate->setPort("\\B", sig_a[i+1]); + gate->setPort("\\Y", sig_t[i/2]); + last_output_cell = gate; } sig_a = sig_t; } if (cell->type == "$reduce_xnor") { - RTLIL::SigSpec sig_t = module->new_wire(1, NEW_ID); - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = "$_INV_"; - gate->connections["\\A"] = sig_a; - gate->connections["\\Y"] = sig_t; - last_output = &gate->connections["\\Y"]; - module->add(gate); + RTLIL::SigSpec sig_t = module->addWire(NEW_ID); + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->setPort("\\A", sig_a); + gate->setPort("\\Y", sig_t); + last_output_cell = gate; sig_a = sig_t; } - if (last_output == NULL) { - module->connections.push_back(RTLIL::SigSig(sig_y, sig_a)); + if (last_output_cell == NULL) { + module->connect(RTLIL::SigSig(sig_y, sig_a)); } else { - *last_output = sig_y; + last_output_cell->setPort("\\Y", sig_y); } } static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) { - sig.expand(); - - while (sig.width > 1) + while (sig.size() > 1) { - RTLIL::SigSpec sig_t = module->new_wire(sig.width / 2, NEW_ID); - sig_t.expand(); + RTLIL::SigSpec sig_t = module->addWire(NEW_ID, sig.size() / 2); - for (int i = 0; i < sig.width; i += 2) + for (int i = 0; i < sig.size(); i += 2) { - if (i+1 == sig.width) { - sig_t.append(sig.chunks.at(i)); + if (i+1 == sig.size()) { + sig_t.append(sig[i]); continue; } - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = "$_OR_"; - gate->connections["\\A"] = sig.chunks.at(i); - gate->connections["\\B"] = sig.chunks.at(i+1); - gate->connections["\\Y"] = sig_t.chunks.at(i/2); - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_OR_"); + gate->setPort("\\A", sig[i]); + gate->setPort("\\B", sig[i+1]); + gate->setPort("\\Y", sig_t[i/2]); } sig = sig_t; } - if (sig.width == 0) + if (sig.size() == 0) sig = RTLIL::SigSpec(0, 1); } static void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) { - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); logic_reduce(module, sig_a); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - if (sig_y.width == 0) + if (sig_y.size() == 0) return; - if (sig_y.width > 1) { - module->connections.push_back(RTLIL::SigSig(sig_y.extract(1, sig_y.width-1), RTLIL::SigSpec(0, sig_y.width-1))); + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); } - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = "$_INV_"; - gate->connections["\\A"] = sig_a; - gate->connections["\\Y"] = sig_y; - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->setPort("\\A", sig_a); + gate->setPort("\\Y", sig_y); } static void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) { - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); logic_reduce(module, sig_a); - RTLIL::SigSpec sig_b = cell->connections.at("\\B"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); logic_reduce(module, sig_b); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - if (sig_y.width == 0) + if (sig_y.size() == 0) return; - if (sig_y.width > 1) { - module->connections.push_back(RTLIL::SigSig(sig_y.extract(1, sig_y.width-1), RTLIL::SigSpec(0, sig_y.width-1))); + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); } @@ -278,54 +225,41 @@ static void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) if (cell->type == "$logic_or") gate_type = "$_OR_"; log_assert(!gate_type.empty()); - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\A"] = sig_a; - gate->connections["\\B"] = sig_b; - gate->connections["\\Y"] = sig_y; - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\A", sig_a); + gate->setPort("\\B", sig_b); + gate->setPort("\\Y", sig_y); } static void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) { - int width = cell->parameters.at("\\WIDTH").as_int(); - - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - sig_a.expand(); - - RTLIL::SigSpec sig_b = cell->connections.at("\\B"); - sig_b.expand(); - - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - sig_y.expand(); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = "$_MUX_"; - gate->connections["\\A"] = sig_a.chunks.at(i); - gate->connections["\\B"] = sig_b.chunks.at(i); - gate->connections["\\S"] = cell->connections.at("\\S"); - gate->connections["\\Y"] = sig_y.chunks.at(i); - module->add(gate); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + for (int i = 0; i < SIZE(sig_y); i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_"); + gate->setPort("\\A", sig_a[i]); + gate->setPort("\\B", sig_b[i]); + gate->setPort("\\S", cell->getPort("\\S")); + gate->setPort("\\Y", sig_y[i]); } } static void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) { int offset = cell->parameters.at("\\OFFSET").as_int(); - RTLIL::SigSpec sig_a = cell->connections.at("\\A"); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - module->connections.push_back(RTLIL::SigSig(sig_y, sig_a.extract(offset, sig_y.width))); + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + module->connect(RTLIL::SigSig(sig_y, sig_a.extract(offset, sig_y.size()))); } static void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell) { - RTLIL::SigSpec sig_ab = cell->connections.at("\\A"); - sig_ab.append(cell->connections.at("\\B")); - RTLIL::SigSpec sig_y = cell->connections.at("\\Y"); - module->connections.push_back(RTLIL::SigSig(sig_y, sig_ab)); + RTLIL::SigSpec sig_ab = cell->getPort("\\A"); + sig_ab.append(cell->getPort("\\B")); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + module->connect(RTLIL::SigSig(sig_y, sig_ab)); } static void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) @@ -334,25 +268,17 @@ static void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N'; char clr_pol = cell->parameters.at("\\CLR_POLARITY").as_bool() ? 'P' : 'N'; - RTLIL::SigSpec sig_s = cell->connections.at("\\SET"); - sig_s.expand(); - - RTLIL::SigSpec sig_r = cell->connections.at("\\CLR"); - sig_r.expand(); - - RTLIL::SigSpec sig_q = cell->connections.at("\\Q"); - sig_q.expand(); + RTLIL::SigSpec sig_s = cell->getPort("\\SET"); + RTLIL::SigSpec sig_r = cell->getPort("\\CLR"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); std::string gate_type = stringf("$_SR_%c%c_", set_pol, clr_pol); for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\S"] = sig_s.chunks.at(i); - gate->connections["\\R"] = sig_r.chunks.at(i); - gate->connections["\\Q"] = sig_q.chunks.at(i); - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\S", sig_s[i]); + gate->setPort("\\R", sig_r[i]); + gate->setPort("\\Q", sig_q[i]); } } @@ -361,24 +287,17 @@ static void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell) int width = cell->parameters.at("\\WIDTH").as_int(); char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N'; - RTLIL::SigSpec sig_clk = cell->connections.at("\\CLK"); - - RTLIL::SigSpec sig_d = cell->connections.at("\\D"); - sig_d.expand(); - - RTLIL::SigSpec sig_q = cell->connections.at("\\Q"); - sig_q.expand(); + RTLIL::SigSpec sig_clk = cell->getPort("\\CLK"); + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); std::string gate_type = stringf("$_DFF_%c_", clk_pol); for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\C"] = sig_clk; - gate->connections["\\D"] = sig_d.chunks.at(i); - gate->connections["\\Q"] = sig_q.chunks.at(i); - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\C", sig_clk); + gate->setPort("\\D", sig_d[i]); + gate->setPort("\\Q", sig_q[i]); } } @@ -389,32 +308,21 @@ static void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N'; char clr_pol = cell->parameters.at("\\CLR_POLARITY").as_bool() ? 'P' : 'N'; - RTLIL::SigSpec sig_clk = cell->connections.at("\\CLK"); - - RTLIL::SigSpec sig_s = cell->connections.at("\\SET"); - sig_s.expand(); - - RTLIL::SigSpec sig_r = cell->connections.at("\\CLR"); - sig_r.expand(); - - RTLIL::SigSpec sig_d = cell->connections.at("\\D"); - sig_d.expand(); - - RTLIL::SigSpec sig_q = cell->connections.at("\\Q"); - sig_q.expand(); + RTLIL::SigSpec sig_clk = cell->getPort("\\CLK"); + RTLIL::SigSpec sig_s = cell->getPort("\\SET"); + RTLIL::SigSpec sig_r = cell->getPort("\\CLR"); + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); std::string gate_type = stringf("$_DFFSR_%c%c%c_", clk_pol, set_pol, clr_pol); for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\C"] = sig_clk; - gate->connections["\\S"] = sig_s.chunks.at(i); - gate->connections["\\R"] = sig_r.chunks.at(i); - gate->connections["\\D"] = sig_d.chunks.at(i); - gate->connections["\\Q"] = sig_q.chunks.at(i); - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\C", sig_clk); + gate->setPort("\\S", sig_s[i]); + gate->setPort("\\R", sig_r[i]); + gate->setPort("\\D", sig_d[i]); + gate->setPort("\\Q", sig_q[i]); } } @@ -428,27 +336,20 @@ static void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell) while (int(rst_val.size()) < width) rst_val.push_back(RTLIL::State::S0); - RTLIL::SigSpec sig_clk = cell->connections.at("\\CLK"); - RTLIL::SigSpec sig_rst = cell->connections.at("\\ARST"); - - RTLIL::SigSpec sig_d = cell->connections.at("\\D"); - sig_d.expand(); - - RTLIL::SigSpec sig_q = cell->connections.at("\\Q"); - sig_q.expand(); + RTLIL::SigSpec sig_clk = cell->getPort("\\CLK"); + RTLIL::SigSpec sig_rst = cell->getPort("\\ARST"); + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); std::string gate_type_0 = stringf("$_DFF_%c%c0_", clk_pol, rst_pol); std::string gate_type_1 = stringf("$_DFF_%c%c1_", clk_pol, rst_pol); for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0; - gate->connections["\\C"] = sig_clk; - gate->connections["\\R"] = sig_rst; - gate->connections["\\D"] = sig_d.chunks.at(i); - gate->connections["\\Q"] = sig_q.chunks.at(i); - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); + gate->setPort("\\C", sig_clk); + gate->setPort("\\R", sig_rst); + gate->setPort("\\D", sig_d[i]); + gate->setPort("\\Q", sig_q[i]); } } @@ -457,32 +358,24 @@ static void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell) int width = cell->parameters.at("\\WIDTH").as_int(); char en_pol = cell->parameters.at("\\EN_POLARITY").as_bool() ? 'P' : 'N'; - RTLIL::SigSpec sig_en = cell->connections.at("\\EN"); - - RTLIL::SigSpec sig_d = cell->connections.at("\\D"); - sig_d.expand(); - - RTLIL::SigSpec sig_q = cell->connections.at("\\Q"); - sig_q.expand(); + RTLIL::SigSpec sig_en = cell->getPort("\\EN"); + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); std::string gate_type = stringf("$_DLATCH_%c_", en_pol); for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = new RTLIL::Cell; - gate->name = NEW_ID; - gate->type = gate_type; - gate->connections["\\E"] = sig_en; - gate->connections["\\D"] = sig_d.chunks.at(i); - gate->connections["\\Q"] = sig_q.chunks.at(i); - module->add(gate); + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\E", sig_en); + gate->setPort("\\D", sig_d[i]); + gate->setPort("\\Q", sig_q[i]); } } -void simplemap_get_mappers(std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers) +void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers) { mappers["$not"] = simplemap_not; mappers["$pos"] = simplemap_pos; - mappers["$bu0"] = simplemap_bu0; mappers["$and"] = simplemap_bitop; mappers["$or"] = simplemap_bitop; mappers["$xor"] = simplemap_bitop; @@ -516,7 +409,7 @@ struct SimplemapPass : public Pass { log("This pass maps a small selection of simple coarse-grain cells to yosys gate\n"); log("primitives. The following internal cell types are mapped by this pass:\n"); log("\n"); - log(" $not, $pos, $bu0, $and, $or, $xor, $xnor\n"); + log(" $not, $pos, $and, $or, $xor, $xnor\n"); log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n"); log(" $logic_not, $logic_and, $logic_or, $mux\n"); log(" $sr, $dff, $dffsr, $adff, $dlatch\n"); @@ -527,25 +420,21 @@ struct SimplemapPass : public Pass { log_header("Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); - std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; + std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; simplemap_get_mappers(mappers); - for (auto &mod_it : design->modules) { - if (!design->selected(mod_it.second)) + for (auto mod : design->modules()) { + if (!design->selected(mod)) continue; - std::vector<RTLIL::Cell*> delete_cells; - for (auto &cell_it : mod_it.second->cells) { - if (mappers.count(cell_it.second->type) == 0) + std::vector<RTLIL::Cell*> cells = mod->cells(); + for (auto cell : cells) { + if (mappers.count(cell->type) == 0) continue; - if (!design->selected(mod_it.second, cell_it.second)) + if (!design->selected(mod, cell)) continue; - log("Mapping %s.%s (%s).\n", RTLIL::id2cstr(mod_it.first), RTLIL::id2cstr(cell_it.first), RTLIL::id2cstr(cell_it.second->type)); - mappers.at(cell_it.second->type)(mod_it.second, cell_it.second); - delete_cells.push_back(cell_it.second); - } - for (auto &it : delete_cells) { - mod_it.second->cells.erase(it->name); - delete it; + log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type)); + mappers.at(cell->type)(mod, cell); + mod->remove(cell); } } } diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index eeeebd111..ed466faa1 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -17,18 +17,22 @@ * */ -#include "kernel/register.h" +#include "kernel/yosys.h" +#include "kernel/utils.h" #include "kernel/sigtools.h" -#include "kernel/log.h" +#include "libs/sha1/sha1.h" + #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <string.h> -#include "passes/techmap/stdcells.inc" +#include "passes/techmap/techmap.inc" // see simplemap.cc -extern void simplemap_get_mappers(std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); +extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); + +// see maccmap.cc +extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false); static void apply_prefix(std::string prefix, std::string &id) { @@ -40,327 +44,712 @@ static void apply_prefix(std::string prefix, std::string &id) static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) { - for (size_t i = 0; i < sig.chunks.size(); i++) { - if (sig.chunks[i].wire == NULL) - continue; - std::string wire_name = sig.chunks[i].wire->name; - apply_prefix(prefix, wire_name); - assert(module->wires.count(wire_name) > 0); - sig.chunks[i].wire = module->wires[wire_name]; - } + std::vector<RTLIL::SigChunk> chunks = sig; + for (auto &chunk : chunks) + if (chunk.wire != NULL) { + std::string wire_name = chunk.wire->name.str(); + apply_prefix(prefix, wire_name); + log_assert(module->wires_.count(wire_name) > 0); + chunk.wire = module->wires_[wire_name]; + } + sig = chunks; } -std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers; -std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; -std::map<RTLIL::Module*, bool> techmap_do_cache; - -struct TechmapWireData { - RTLIL::Wire *wire; - RTLIL::SigSpec value; -}; +struct TechmapWorker +{ + std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers; + std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; + std::map<RTLIL::Module*, bool> techmap_do_cache; + std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue; -typedef std::map<std::string, std::vector<TechmapWireData>> TechmapWires; + struct TechmapWireData { + RTLIL::Wire *wire; + RTLIL::SigSpec value; + }; -static TechmapWires techmap_find_special_wires(RTLIL::Module *module) -{ - TechmapWires result; + typedef std::map<std::string, std::vector<TechmapWireData>> TechmapWires; - if (module == NULL) - return result; + bool extern_mode; + bool assert_mode; + bool flatten_mode; + bool recursive_mode; + bool autoproc_mode; - for (auto &it : module->wires) { - const char *p = it.first.c_str(); - if (*p == '$') - continue; - - const char *q = strrchr(p+1, '.'); - p = q ? q : p+1; - - if (!strncmp(p, "_TECHMAP_", 9)) { - TechmapWireData record; - record.wire = it.second; - record.value = it.second; - result[p].push_back(record); - it.second->attributes["\\keep"] = RTLIL::Const(1); - it.second->attributes["\\_techmap_special_"] = RTLIL::Const(1); - } + TechmapWorker() + { + extern_mode = false; + assert_mode = false; + flatten_mode = false; + recursive_mode = false; + autoproc_mode = false; } - if (!result.empty()) { - SigMap sigmap(module); - for (auto &it1 : result) - for (auto &it2 : it1.second) - sigmap.apply(it2.value); + std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose) + { + std::string constmap_info; + std::map<RTLIL::SigBit, std::pair<RTLIL::IdString, int>> connbits_map; + + for (auto conn : cell->connections()) + for (int i = 0; i < SIZE(conn.second); i++) { + RTLIL::SigBit bit = sigmap(conn.second[i]); + if (bit.wire == nullptr) { + if (verbose) + log(" Constant input on bit %d of port %s: %s\n", i, log_id(conn.first), log_signal(bit)); + constmap_info += stringf("|%s %d %d", log_id(conn.first), i, bit.data); + } else if (connbits_map.count(bit)) { + if (verbose) + log(" Bit %d of port %s and bit %d of port %s are connected.\n", i, log_id(conn.first), + connbits_map.at(bit).second, log_id(connbits_map.at(bit).first)); + constmap_info += stringf("|%s %d %s %d", log_id(conn.first), i, + log_id(connbits_map.at(bit).first), connbits_map.at(bit).second); + } else + connbits_map[bit] = std::pair<RTLIL::IdString, int>(conn.first, i);stringf("%s %d", log_id(conn.first), i, bit.data); + } + + return stringf("$paramod$constmap:%s%s", sha1(constmap_info).c_str(), tpl->name.c_str()); } - return result; -} + TechmapWires techmap_find_special_wires(RTLIL::Module *module) + { + TechmapWires result; -static void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, bool flatten_mode) -{ - log("Mapping `%s.%s' using `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(tpl->name)); - - if (tpl->memories.size() != 0) - log_error("Technology map yielded memories -> this is not supported.\n"); - - if (tpl->processes.size() != 0) - log_error("Technology map yielded processes -> this is not supported.\n"); - - std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; - - for (auto &it : tpl->wires) { - if (it.second->port_id > 0) - positional_ports[stringf("$%d", it.second->port_id)] = it.first; - RTLIL::Wire *w = new RTLIL::Wire(*it.second); - apply_prefix(cell->name, w->name); - w->port_input = false; - w->port_output = false; - w->port_id = 0; - if (it.second->get_bool_attribute("\\_techmap_special_")) - w->attributes.clear(); - module->wires[w->name] = w; - design->select(module, w); - } + if (module == NULL) + return result; + + for (auto &it : module->wires_) { + const char *p = it.first.c_str(); + if (*p == '$') + continue; - SigMap port_signal_map; + const char *q = strrchr(p+1, '.'); + p = q ? q : p+1; - for (auto &it : cell->connections) { - RTLIL::IdString portname = it.first; - if (positional_ports.count(portname) > 0) - portname = positional_ports.at(portname); - if (tpl->wires.count(portname) == 0 || tpl->wires.at(portname)->port_id == 0) { - if (portname.substr(0, 1) == "$") - log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str()); - continue; + if (!strncmp(p, "_TECHMAP_", 9)) { + TechmapWireData record; + record.wire = it.second; + record.value = it.second; + result[p].push_back(record); + it.second->attributes["\\keep"] = RTLIL::Const(1); + it.second->attributes["\\_techmap_special_"] = RTLIL::Const(1); + } } - RTLIL::Wire *w = tpl->wires.at(portname); - RTLIL::SigSig c; - if (w->port_output) { - c.first = it.second; - c.second = RTLIL::SigSpec(w); - apply_prefix(cell->name, c.second, module); - } else { - c.first = RTLIL::SigSpec(w); - c.second = it.second; - apply_prefix(cell->name, c.first, module); + + if (!result.empty()) { + SigMap sigmap(module); + for (auto &it1 : result) + for (auto &it2 : it1.second) + sigmap.apply(it2.value); } - if (c.second.width > c.first.width) - c.second.remove(c.first.width, c.second.width - c.first.width); - if (c.second.width < c.first.width) - c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.width - c.second.width)); - assert(c.first.width == c.second.width); -#if 0 - // more conservative approach: - // connect internal and external wires - module->connections.push_back(c); -#else - // approach that yields nicer outputs: - // replace internal wires that are connected to external wires - if (w->port_output) - port_signal_map.add(c.second, c.first); - else - port_signal_map.add(c.first, c.second); -#endif + + return result; } - for (auto &it : tpl->cells) { - RTLIL::Cell *c = new RTLIL::Cell(*it.second); - if (!flatten_mode && c->type.substr(0, 2) == "\\$") - c->type = c->type.substr(1); - apply_prefix(cell->name, c->name); - for (auto &it2 : c->connections) { - apply_prefix(cell->name, it2.second, module); - port_signal_map.apply(it2.second); + void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl) + { + if (tpl->memories.size() != 0) + log_error("Technology map yielded memories -> this is not supported.\n"); + + if (tpl->processes.size() != 0) { + log("Technology map yielded processes:\n"); + for (auto &it : tpl->processes) + log(" %s",RTLIL::id2cstr(it.first)); + if (autoproc_mode) { + Pass::call_on_module(tpl->design, tpl, "proc"); + log_assert(SIZE(tpl->processes) == 0); + } else + log_error("Technology map yielded processes -> this is not supported (use -autoproc to run 'proc' automatically).\n"); } - module->cells[c->name] = c; - design->select(module, c); - } - for (auto &it : tpl->connections) { - RTLIL::SigSig c = it; - apply_prefix(cell->name, c.first, module); - apply_prefix(cell->name, c.second, module); - port_signal_map.apply(c.first); - port_signal_map.apply(c.second); - module->connections.push_back(c); - } + std::string orig_cell_name; + if (!flatten_mode) + for (auto &it : tpl->cells_) + if (it.first == "\\_TECHMAP_REPLACE_") { + orig_cell_name = cell->name.str(); + module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); + break; + } - module->cells.erase(cell->name); - delete cell; -} + std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; + + for (auto &it : tpl->wires_) { + if (it.second->port_id > 0) + positional_ports[stringf("$%d", it.second->port_id)] = it.first; + std::string w_name = it.second->name.str(); + apply_prefix(cell->name.str(), w_name); + RTLIL::Wire *w = module->addWire(w_name, it.second); + w->port_input = false; + w->port_output = false; + w->port_id = 0; + if (it.second->get_bool_attribute("\\_techmap_special_")) + w->attributes.clear(); + design->select(module, w); + } -static bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, - const std::map<RTLIL::IdString, std::set<RTLIL::IdString>> &celltypeMap, bool flatten_mode) -{ - if (!design->selected(module)) - return false; + SigMap port_signal_map; + + for (auto &it : cell->connections()) { + RTLIL::IdString portname = it.first; + if (positional_ports.count(portname) > 0) + portname = positional_ports.at(portname); + if (tpl->wires_.count(portname) == 0 || tpl->wires_.at(portname)->port_id == 0) { + if (portname.substr(0, 1) == "$") + log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str()); + continue; + } + RTLIL::Wire *w = tpl->wires_.at(portname); + RTLIL::SigSig c; + if (w->port_output) { + c.first = it.second; + c.second = RTLIL::SigSpec(w); + apply_prefix(cell->name.str(), c.second, module); + } else { + c.first = RTLIL::SigSpec(w); + c.second = it.second; + apply_prefix(cell->name.str(), c.first, module); + } + if (c.second.size() > c.first.size()) + c.second.remove(c.first.size(), c.second.size() - c.first.size()); + if (c.second.size() < c.first.size()) + c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size())); + log_assert(c.first.size() == c.second.size()); + if (flatten_mode) { + // more conservative approach: + // connect internal and external wires + module->connect(c); + } else { + // approach that yields nicer outputs: + // replace internal wires that are connected to external wires + if (w->port_output) + port_signal_map.add(c.second, c.first); + else + port_signal_map.add(c.first, c.second); + } + } + + for (auto &it : tpl->cells_) + { + std::string c_name = it.second->name.str(); + + if (!flatten_mode && c_name == "\\_TECHMAP_REPLACE_") + c_name = orig_cell_name; + else + apply_prefix(cell->name.str(), c_name); + + RTLIL::Cell *c = module->addCell(c_name, it.second); + design->select(module, c); + + if (!flatten_mode && c->type.substr(0, 2) == "\\$") + c->type = c->type.substr(1); - bool log_continue = false; - bool did_something = false; - std::vector<std::string> cell_names; + for (auto &it2 : c->connections_) { + apply_prefix(cell->name.str(), it2.second, module); + port_signal_map.apply(it2.second); + } + } + + for (auto &it : tpl->connections()) { + RTLIL::SigSig c = it; + apply_prefix(cell->name.str(), c.first, module); + apply_prefix(cell->name.str(), c.second, module); + port_signal_map.apply(c.first); + port_signal_map.apply(c.second); + module->connect(c); + } - for (auto &cell_it : module->cells) - cell_names.push_back(cell_it.first); + module->remove(cell); + } - for (auto &cell_name : cell_names) + bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, + const std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> &celltypeMap, bool in_recursion) { - if (module->cells.count(cell_name) == 0) - continue; + std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping"; + + if (!design->selected(module)) + return false; - RTLIL::Cell *cell = module->cells[cell_name]; + bool log_continue = false; + bool did_something = false; - if (!design->selected(module, cell) || handled_cells.count(cell) > 0) - continue; + SigMap sigmap(module); - if (celltypeMap.count(cell->type) == 0) - continue; + TopoSort<RTLIL::Cell*> cells; + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; - for (auto &tpl_name : celltypeMap.at(cell->type)) + for (auto cell : module->cells()) { - std::string derived_name = tpl_name; - RTLIL::Module *tpl = map->modules[tpl_name]; - std::map<RTLIL::IdString, RTLIL::Const> parameters = cell->parameters; + if (!design->selected(module, cell) || handled_cells.count(cell) > 0) + continue; - if (!flatten_mode) - { - if (tpl->get_bool_attribute("\\techmap_simplemap")) { - log("Mapping %s.%s (%s) with simplemap.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - if (simplemap_mappers.count(cell->type) == 0) - log_error("No simplemap mapper for cell type %s found!\n", RTLIL::id2cstr(cell->type)); - simplemap_mappers.at(cell->type)(module, cell); - module->cells.erase(cell->name); - delete cell; - cell = NULL; - did_something = true; - break; - } + std::string cell_type = cell->type.str(); + if (in_recursion && cell_type.substr(0, 2) == "\\$") + cell_type = cell_type.substr(1); - for (auto conn : cell->connections) { - if (conn.first.substr(0, 1) == "$") - continue; - if (tpl->wires.count(conn.first) > 0 && tpl->wires.at(conn.first)->port_id > 0) - continue; - if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0) - goto next_tpl; - parameters[conn.first] = conn.second.as_const(); - } + if (celltypeMap.count(cell_type) == 0) { + if (assert_mode && cell_type.back() != '_') + log_error("(ASSERT MODE) No matching template cell for type %s found.\n", log_id(cell_type)); + continue; + } - if (0) { - next_tpl: - continue; - } + for (auto &conn : cell->connections()) + { + RTLIL::SigSpec sig = sigmap(conn.second); + sig.remove_const(); - if (tpl->avail_parameters.count("\\_TECHMAP_CELLTYPE_") != 0) - parameters["\\_TECHMAP_CELLTYPE_"] = RTLIL::unescape_id(cell->type); - } + if (SIZE(sig) == 0) + continue; - std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters); - if (techmap_cache.count(key) > 0) { - tpl = techmap_cache[key]; - } else { - if (cell->parameters.size() != 0) { - derived_name = tpl->derive(map, parameters); - tpl = map->modules[derived_name]; - log_continue = true; + for (auto &tpl_name : celltypeMap.at(cell_type)) { + RTLIL::Module *tpl = map->modules_[tpl_name]; + RTLIL::Wire *port = tpl->wire(conn.first); + if (port && port->port_input) + cell_to_inbit[cell].insert(sig.begin(), sig.end()); + if (port && port->port_output) + for (auto &bit : sig) + outbit_to_cell[bit].insert(cell); } - techmap_cache[key] = tpl; } - if (flatten_mode) - techmap_do_cache[tpl] = true; + cells.node(cell); + } + + for (auto &it_right : cell_to_inbit) + for (auto &it_sigbit : it_right.second) + for (auto &it_left : outbit_to_cell[it_sigbit]) + cells.edge(it_left, it_right.first); + + cells.sort(); + + for (auto cell : cells.sorted) + { + log_assert(handled_cells.count(cell) == 0); + log_assert(cell == module->cell(cell->name)); + bool mapped_cell = false; + + std::string cell_type = cell->type.str(); + if (in_recursion && cell_type.substr(0, 2) == "\\$") + cell_type = cell_type.substr(1); - if (techmap_do_cache.count(tpl) == 0) + for (auto &tpl_name : celltypeMap.at(cell_type)) { - bool keep_running = true; - techmap_do_cache[tpl] = true; + RTLIL::IdString derived_name = tpl_name; + RTLIL::Module *tpl = map->modules_[tpl_name]; + std::map<RTLIL::IdString, RTLIL::Const> parameters = cell->parameters; + + if (tpl->get_bool_attribute("\\blackbox")) + continue; - while (keep_running) + if (!flatten_mode) { - TechmapWires twd = techmap_find_special_wires(tpl); - keep_running = false; - - for (auto &it : twd["_TECHMAP_FAIL_"]) { - RTLIL::SigSpec value = it.value; - if (value.is_fully_const() && value.as_bool()) { - log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", - derived_name.c_str(), RTLIL::id2cstr(it.wire->name), log_signal(value)); - techmap_do_cache[tpl] = false; + std::string extmapper_name; + + if (tpl->get_bool_attribute("\\techmap_simplemap")) + extmapper_name = "simplemap"; + + if (tpl->get_bool_attribute("\\techmap_maccmap")) + extmapper_name = "maccmap"; + + if (tpl->attributes.count("\\techmap_wrap")) + extmapper_name = "wrap"; + + if (!extmapper_name.empty()) + { + cell->type = cell_type; + + if ((extern_mode && !in_recursion) || extmapper_name == "wrap") + { + std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type)); + + for (auto &c : cell->parameters) + m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second)); + + if (extmapper_name == "wrap") + m_name += ":" + sha1(tpl->attributes.at("\\techmap_wrap").decode_string()); + + RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design; + RTLIL::Module *extmapper_module = extmapper_design->module(m_name); + + if (extmapper_module == nullptr) + { + extmapper_module = extmapper_design->addModule(m_name); + RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell); + + int port_counter = 1; + for (auto &c : extmapper_cell->connections_) { + RTLIL::Wire *w = extmapper_module->addWire(c.first, SIZE(c.second)); + if (w->name == "\\Y" || w->name == "\\Q") + w->port_output = true; + else + w->port_input = true; + w->port_id = port_counter++; + c.second = w; + } + + extmapper_module->fixup_ports(); + extmapper_module->check(); + + if (extmapper_name == "simplemap") { + log("Creating %s with simplemap.\n", log_id(extmapper_module)); + if (simplemap_mappers.count(extmapper_cell->type) == 0) + log_error("No simplemap mapper for cell type %s found!\n", log_id(extmapper_cell->type)); + simplemap_mappers.at(extmapper_cell->type)(extmapper_module, extmapper_cell); + extmapper_module->remove(extmapper_cell); + } + + if (extmapper_name == "maccmap") { + log("Creating %s with maccmap.\n", log_id(extmapper_module)); + if (extmapper_cell->type != "$macc") + log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type)); + maccmap(extmapper_module, extmapper_cell); + extmapper_module->remove(extmapper_cell); + } + + if (extmapper_name == "wrap") { + std::string cmd_string = tpl->attributes.at("\\techmap_wrap").decode_string(); + log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module)); + Pass::call_on_module(extmapper_design, extmapper_module, cmd_string); + log_continue = true; + } + } + + cell->type = extmapper_module->name; + cell->parameters.clear(); + + if (!extern_mode || in_recursion) { + tpl = extmapper_module; + goto use_wrapper_tpl; + } + + log("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module)); + } + else + { + log("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str()); + + if (extmapper_name == "simplemap") { + if (simplemap_mappers.count(cell->type) == 0) + log_error("No simplemap mapper for cell type %s found!\n", RTLIL::id2cstr(cell->type)); + simplemap_mappers.at(cell->type)(module, cell); + } + + if (extmapper_name == "maccmap") { + if (cell->type != "$macc") + log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type)); + maccmap(module, cell); + } + + module->remove(cell); + cell = NULL; } - } - if (!techmap_do_cache[tpl]) + did_something = true; + mapped_cell = true; break; + } - for (auto &it : twd) - { - if (it.first.substr(0, 12) != "_TECHMAP_DO_" || it.second.empty()) + for (auto conn : cell->connections()) { + if (conn.first.substr(0, 1) == "$") + continue; + if (tpl->wires_.count(conn.first) > 0 && tpl->wires_.at(conn.first)->port_id > 0) continue; + if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0) + goto next_tpl; + parameters[conn.first] = conn.second.as_const(); + } + + if (0) { + next_tpl: + continue; + } + + if (tpl->avail_parameters.count("\\_TECHMAP_CELLTYPE_") != 0) + parameters["\\_TECHMAP_CELLTYPE_"] = RTLIL::unescape_id(cell->type); + + for (auto conn : cell->connections()) { + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))) != 0) { + std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector(); + for (auto &bit : v) + bit = RTLIL::SigBit(bit.wire == NULL ? RTLIL::State::S1 : RTLIL::State::S0); + parameters[stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); + } + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))) != 0) { + std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector(); + for (auto &bit : v) + if (bit.wire != NULL) + bit = RTLIL::SigBit(RTLIL::State::Sx); + parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); + } + } + + int unique_bit_id_counter = 0; + std::map<RTLIL::SigBit, int> unique_bit_id; + unique_bit_id[RTLIL::State::S0] = unique_bit_id_counter++; + unique_bit_id[RTLIL::State::S1] = unique_bit_id_counter++; + unique_bit_id[RTLIL::State::Sx] = unique_bit_id_counter++; + unique_bit_id[RTLIL::State::Sz] = unique_bit_id_counter++; + + for (auto conn : cell->connections()) + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) { + for (auto &bit : sigmap(conn.second).to_sigbit_vector()) + if (unique_bit_id.count(bit) == 0) + unique_bit_id[bit] = unique_bit_id_counter++; + } - auto &data = it.second.front(); + int bits = 0; + for (int i = 0; i < 32; i++) + if (((unique_bit_id_counter-1) & (1 << i)) != 0) + bits = i; + if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_")) + parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits; + + for (auto conn : cell->connections()) + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) { + RTLIL::Const value; + for (auto &bit : sigmap(conn.second).to_sigbit_vector()) { + RTLIL::Const chunk(unique_bit_id.at(bit), bits); + value.bits.insert(value.bits.end(), chunk.bits.begin(), chunk.bits.end()); + } + parameters[stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))] = value; + } + } - if (!data.value.is_fully_const()) - log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(data.wire->name), log_signal(data.value)); + if (0) { + use_wrapper_tpl:; + // do not register techmap_wrap modules with techmap_cache + } else { + std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters); + if (techmap_cache.count(key) > 0) { + tpl = techmap_cache[key]; + } else { + if (cell->parameters.size() != 0) { + derived_name = tpl->derive(map, parameters); + tpl = map->module(derived_name); + log_continue = true; + } + techmap_cache[key] = tpl; + } + } - tpl->wires.erase(data.wire->name); - const char *p = data.wire->name.c_str(); - const char *q = strrchr(p+1, '.'); - q = q ? q : p+1; + if (flatten_mode) { + techmap_do_cache[tpl] = true; + } else { + RTLIL::Module *constmapped_tpl = map->module(constmap_tpl_name(sigmap, tpl, cell, false)); + if (constmapped_tpl != nullptr) + tpl = constmapped_tpl; + } - assert(!strncmp(q, "_TECHMAP_DO_", 12)); - std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12); - while (tpl->wires.count(new_name)) - new_name += "_"; - data.wire->name = new_name; - tpl->add(data.wire); + if (techmap_do_cache.count(tpl) == 0) + { + bool keep_running = true; + techmap_do_cache[tpl] = true; - std::string cmd_string = data.value.as_const().decode_string(); + std::set<std::string> techmap_wire_names; - RTLIL::Selection tpl_mod_sel(false); - tpl_mod_sel.select(tpl); - map->selection_stack.push_back(tpl_mod_sel); - Pass::call(map, cmd_string); - map->selection_stack.pop_back(); + while (keep_running) + { + TechmapWires twd = techmap_find_special_wires(tpl); + keep_running = false; + + for (auto &it : twd) + techmap_wire_names.insert(it.first); + + for (auto &it : twd["_TECHMAP_FAIL_"]) { + RTLIL::SigSpec value = it.value; + if (value.is_fully_const() && value.as_bool()) { + log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", + derived_name.c_str(), RTLIL::id2cstr(it.wire->name), log_signal(value)); + techmap_do_cache[tpl] = false; + } + } - keep_running = true; - break; + if (!techmap_do_cache[tpl]) + break; + + for (auto &it : twd) + { + if (it.first.substr(0, 12) != "_TECHMAP_DO_" || it.second.empty()) + continue; + + auto &data = it.second.front(); + + if (!data.value.is_fully_const()) + log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(data.wire->name), log_signal(data.value)); + + techmap_wire_names.erase(it.first); + + const char *p = data.wire->name.c_str(); + const char *q = strrchr(p+1, '.'); + q = q ? q : p+1; + + std::string cmd_string = data.value.as_const().decode_string(); + + restart_eval_cmd_string: + if (cmd_string.rfind("CONSTMAP; ", 0) == 0) + { + cmd_string = cmd_string.substr(strlen("CONSTMAP; ")); + + log("Analyzing pattern of constant bits for this cell:\n"); + RTLIL::IdString new_tpl_name = constmap_tpl_name(sigmap, tpl, cell, true); + log("Creating constmapped module `%s'.\n", log_id(new_tpl_name)); + log_assert(map->module(new_tpl_name) == nullptr); + + RTLIL::Module *new_tpl = map->addModule(new_tpl_name); + tpl->cloneInto(new_tpl); + + techmap_do_cache.erase(tpl); + techmap_do_cache[new_tpl] = true; + tpl = new_tpl; + + std::map<RTLIL::SigBit, RTLIL::SigBit> port_new2old_map; + std::map<RTLIL::SigBit, RTLIL::SigBit> port_connmap; + std::map<RTLIL::SigBit, RTLIL::SigBit> cellbits_to_tplbits; + + for (auto wire : tpl->wires().to_vector()) + { + if (!wire->port_input || wire->port_output) + continue; + + RTLIL::IdString port_name = wire->name; + tpl->rename(wire, NEW_ID); + + RTLIL::Wire *new_wire = tpl->addWire(port_name, wire); + wire->port_input = false; + wire->port_id = 0; + + for (int i = 0; i < wire->width; i++) { + port_new2old_map[RTLIL::SigBit(new_wire, i)] = RTLIL::SigBit(wire, i); + port_connmap[RTLIL::SigBit(wire, i)] = RTLIL::SigBit(new_wire, i); + } + } + + for (auto conn : cell->connections()) + for (int i = 0; i < SIZE(conn.second); i++) + { + RTLIL::SigBit bit = sigmap(conn.second[i]); + RTLIL::SigBit tplbit(tpl->wire(conn.first), i); + + if (bit.wire == nullptr) + { + RTLIL::SigBit oldbit = port_new2old_map.at(tplbit); + port_connmap.at(oldbit) = bit; + } + else if (cellbits_to_tplbits.count(bit)) + { + RTLIL::SigBit oldbit = port_new2old_map.at(tplbit); + port_connmap.at(oldbit) = cellbits_to_tplbits[bit]; + } + else + cellbits_to_tplbits[bit] = tplbit; + } + + RTLIL::SigSig port_conn; + for (auto &it : port_connmap) { + port_conn.first.append_bit(it.first); + port_conn.second.append_bit(it.second); + } + tpl->connect(port_conn); + + tpl->check(); + goto restart_eval_cmd_string; + } + + if (cmd_string.rfind("RECURSION; ", 0) == 0) + { + cmd_string = cmd_string.substr(strlen("RECURSION; ")); + while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { } + goto restart_eval_cmd_string; + } + + Pass::call_on_module(map, tpl, cmd_string); + + log_assert(!strncmp(q, "_TECHMAP_DO_", 12)); + std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12); + while (tpl->wires_.count(new_name)) + new_name += "_"; + tpl->rename(data.wire->name, new_name); + + keep_running = true; + break; + } + } + + TechmapWires twd = techmap_find_special_wires(tpl); + for (auto &it : twd) { + if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_") + log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str()); + if (techmap_do_cache[tpl]) + for (auto &it2 : it.second) + if (!it2.value.is_fully_const()) + log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value)); + techmap_wire_names.erase(it.first); + } + + for (auto &it : techmap_wire_names) + log_error("Techmap special wire %s disappeared. This is considered a fatal error.\n", RTLIL::id2cstr(it)); + + if (recursive_mode) { + if (log_continue) { + log_header("Continuing TECHMAP pass.\n"); + log_continue = false; + } + while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { } } } - TechmapWires twd = techmap_find_special_wires(tpl); - for (auto &it : twd) { - if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_") - log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str()); - if (techmap_do_cache[tpl]) - for (auto &it2 : it.second) - if (!it2.value.is_fully_const()) - log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value)); + if (techmap_do_cache.at(tpl) == false) + continue; + + if (log_continue) { + log_header("Continuing TECHMAP pass.\n"); + log_continue = false; } - } - if (techmap_do_cache.at(tpl) == false) - continue; + if (extern_mode && !in_recursion) + { + std::string m_name = stringf("$extern:%s", log_id(tpl)); - if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); - log_continue = false; + if (!design->module(m_name)) + { + RTLIL::Module *m = design->addModule(m_name); + tpl->cloneInto(m); + + for (auto cell : m->cells()) { + if (cell->type.substr(0, 2) == "\\$") + cell->type = cell->type.substr(1); + } + + module_queue.insert(m); + } + + log("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name)); + cell->type = m_name; + cell->parameters.clear(); + } + else + { + log("%s %s.%s using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(tpl)); + techmap_module_worker(design, module, cell, tpl); + cell = NULL; + } + did_something = true; + mapped_cell = true; + break; } - techmap_module_worker(design, module, cell, tpl, flatten_mode); - did_something = true; - cell = NULL; - break; + if (assert_mode && !mapped_cell) + log_error("(ASSERT MODE) Failed to map cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + + handled_cells.insert(cell); } - handled_cells.insert(cell); - } + if (log_continue) { + log_header("Continuing TECHMAP pass.\n"); + log_continue = false; + } - if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); - log_continue = false; + return did_something; } - - return did_something; -} +}; struct TechmapPass : public Pass { TechmapPass() : Pass("techmap", "generic technology mapper") { } @@ -380,11 +769,34 @@ struct TechmapPass : public Pass { log(" transforms the internal RTL cells to the internal gate\n"); log(" library.\n"); log("\n"); + log(" -map %%<design-name>\n"); + log(" like -map above, but with an in-memory design instead of a file.\n"); + log("\n"); log(" -share_map filename\n"); log(" like -map, but look for the file in the share directory (where the\n"); log(" yosys data files are). this is mainly used internally when techmap\n"); log(" is called from other commands.\n"); log("\n"); + log(" -extern\n"); + log(" load the cell implementations as separate modules into the design\n"); + log(" instead of inlining them.\n"); + log("\n"); + log(" -max_iter <number>\n"); + log(" only run the specified number of iterations.\n"); + log("\n"); + log(" -recursive\n"); + log(" instead of the iterative breadth-first algorithm use a recursive\n"); + log(" depth-first algorithm. both methods should yield equivialent results,\n"); + log(" but may differ in performance.\n"); + log("\n"); + log(" -autoproc\n"); + log(" Automatically call \"proc\" on implementations that contain processes.\n"); + log("\n"); + log(" -assert\n"); + log(" this option will cause techmap to exit with an error if it can't map\n"); + log(" a selected cell. only cell types that end on an underscore are accepted\n"); + log(" as final cell types by this mode.\n"); + log("\n"); log(" -D <define>, -I <incdir>\n"); log(" this options are passed as-is to the verilog frontend for loading the\n"); log(" map file. Note that the verilog frontend is also called with the\n"); @@ -397,6 +809,13 @@ struct TechmapPass : public Pass { log("When a module in the map file has the 'techmap_simplemap' attribute set, techmap\n"); log("will use 'simplemap' (see 'help simplemap') to map cells matching the module.\n"); log("\n"); + log("When a module in the map file has the 'techmap_maccmap' attribute set, techmap\n"); + log("will use 'maccmap' (see 'help maccmap') to map cells matching the module.\n"); + log("\n"); + log("When a module in the map file has the 'techmap_wrap' attribute set, techmap\n"); + log("will create a wrapper for the cell and then run the command string that the\n"); + log("attribute is set to on the wrapper module.\n"); + log("\n"); log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n"); log("or *._TECHMAP_* are special wires that are used to pass instructions from\n"); log("the mapping module to the techmap command. At the moment the following special\n"); @@ -421,6 +840,20 @@ struct TechmapPass : public Pass { log(" wire to start out as non-constant and evaluate to a constant value\n"); log(" during processing of other _TECHMAP_DO_* commands.\n"); log("\n"); + log(" A _TECHMAP_DO_* command may start with the special token 'CONSTMAP; '.\n"); + log(" in this case techmap will create a copy for each distinct configuration\n"); + log(" of constant inputs and shorted inputs at this point and import the\n"); + log(" constant and connected bits into the map module. All further commands\n"); + log(" are executed in this copy. This is a very convenient way of creating\n"); + log(" optimizied specializations of techmap modules without using the special\n"); + log(" parameters described below.\n"); + log("\n"); + log(" A _TECHMAP_DO_* command may start with the special token 'RECURSION; '.\n"); + log(" then techmap will recursively replace the cells in the module with their\n"); + log(" implementation. This is not affected by the -max_iter option.\n"); + log("\n"); + log(" It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n"); + log("\n"); log("In addition to this special wires, techmap also supports special parameters in\n"); log("modules in the map file:\n"); log("\n"); @@ -428,11 +861,28 @@ struct TechmapPass : public Pass { log(" When a parameter with this name exists, it will be set to the type name\n"); log(" of the cell that matches the module.\n"); log("\n"); + log(" _TECHMAP_CONSTMSK_<port-name>_\n"); + log(" _TECHMAP_CONSTVAL_<port-name>_\n"); + log(" When this pair of parameters is available in a module for a port, then\n"); + log(" former has a 1-bit for each constant input bit and the latter has the\n"); + log(" value for this bit. The unused bits of the latter are set to undef (x).\n"); + log("\n"); + log(" _TECHMAP_BITS_CONNMAP_\n"); + log(" _TECHMAP_CONNMAP_<port-name>_\n"); + log(" For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n"); + log(" exists, will be set to an N*_TECHMAP_BITS_CONNMAP_ bit vector containing\n"); + log(" N words (of _TECHMAP_BITS_CONNMAP_ bits each) that assign each single\n"); + log(" bit driver a unique id. The values 0-3 are reserved for 0, 1, x, and z.\n"); + log(" This can be used to detect shorted inputs.\n"); + log("\n"); log("When a module in the map file has a parameter where the according cell in the\n"); log("design has a port, the module from the map file is only used if the port in\n"); log("the design is connected to a constant value. The parameter is then set to the\n"); log("constant value.\n"); log("\n"); + log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n"); + log("of the cell that is beeing replaced.\n"); + log("\n"); log("See 'help extract' for a pass that does the opposite thing.\n"); log("\n"); log("See 'help flatten' for a pass that does flatten the design (which is\n"); @@ -444,17 +894,28 @@ struct TechmapPass : public Pass { log_header("Executing TECHMAP pass (map to technology primitives).\n"); log_push(); + TechmapWorker worker; + simplemap_get_mappers(worker.simplemap_mappers); + std::vector<std::string> map_files; std::string verilog_frontend = "verilog -ignore_redef"; + int max_iter = -1; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-map" && argidx+1 < args.size()) { - map_files.push_back(args[++argidx]); + if (args[argidx+1].substr(0, 2) == "+/") + map_files.push_back(proc_share_dirname() + args[++argidx].substr(2)); + else + map_files.push_back(args[++argidx]); continue; } if (args[argidx] == "-share_map" && argidx+1 < args.size()) { - map_files.push_back(get_share_file_name(args[++argidx])); + map_files.push_back(proc_share_dirname() + args[++argidx]); + continue; + } + if (args[argidx] == "-max_iter" && argidx+1 < args.size()) { + max_iter = atoi(args[++argidx].c_str()); continue; } if (args[argidx] == "-D" && argidx+1 < args.size()) { @@ -465,36 +926,58 @@ struct TechmapPass : public Pass { verilog_frontend += " -I " + args[++argidx]; continue; } + if (args[argidx] == "-assert") { + worker.assert_mode = true; + continue; + } + if (args[argidx] == "-extern") { + worker.extern_mode = true; + continue; + } + if (args[argidx] == "-recursive") { + worker.recursive_mode = true; + continue; + } + if (args[argidx] == "-autoproc") { + worker.autoproc_mode = true; + continue; + } break; } extra_args(args, argidx, design); - simplemap_get_mappers(simplemap_mappers); - RTLIL::Design *map = new RTLIL::Design; if (map_files.empty()) { - FILE *f = fmemopen(stdcells_code, strlen(stdcells_code), "rt"); - Frontend::frontend_call(map, f, "<stdcells.v>", verilog_frontend); - fclose(f); + std::istringstream f(stdcells_code); + Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend); } else - for (auto &fn : map_files) { - FILE *f = fopen(fn.c_str(), "rt"); - if (f == NULL) - log_cmd_error("Can't open map file `%s'\n", fn.c_str()); - Frontend::frontend_call(map, f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend); - fclose(f); - } + for (auto &fn : map_files) + if (fn.substr(0, 1) == "%") { + if (!saved_designs.count(fn.substr(1))) { + delete map; + log_cmd_error("Can't saved design `%s'.\n", fn.c_str()+1); + } + for (auto mod : saved_designs.at(fn.substr(1))->modules()) + if (!map->has(mod->name)) + map->add(mod->clone()); + } else { + std::ifstream f; + f.open(fn.c_str()); + if (f.fail()) + log_cmd_error("Can't open map file `%s'\n", fn.c_str()); + Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend); + } std::map<RTLIL::IdString, RTLIL::Module*> modules_new; - for (auto &it : map->modules) { + for (auto &it : map->modules_) { if (it.first.substr(0, 2) == "\\$") it.second->name = it.first.substr(1); modules_new[it.second->name] = it.second; } - map->modules.swap(modules_new); + map->modules_.swap(modules_new); - std::map<RTLIL::IdString, std::set<RTLIL::IdString>> celltypeMap; - for (auto &it : map->modules) { + std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; + for (auto &it : map->modules_) { if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) { char *p = strdup(it.second->attributes.at("\\techmap_celltype").decode_string().c_str()); for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n")) @@ -504,22 +987,30 @@ struct TechmapPass : public Pass { celltypeMap[it.first].insert(it.first); } - bool did_something = true; - std::set<RTLIL::Cell*> handled_cells; - while (did_something) { - did_something = false; - for (auto &mod_it : design->modules) - if (techmap_module(design, mod_it.second, map, handled_cells, celltypeMap, false)) - did_something = true; - if (did_something) - design->check(); + for (auto module : design->modules()) + worker.module_queue.insert(module); + + while (!worker.module_queue.empty()) + { + RTLIL::Module *module = *worker.module_queue.begin(); + worker.module_queue.erase(module); + + bool did_something = true; + std::set<RTLIL::Cell*> handled_cells; + while (did_something) { + did_something = false; + if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false)) + did_something = true; + if (did_something) + module->check(); + if (max_iter > 0 && --max_iter == 0) + break; + } } log("No more expansions possible.\n"); - techmap_cache.clear(); - techmap_do_cache.clear(); - simplemap_mappers.clear(); delete map; + log_pop(); } } TechmapPass; @@ -544,26 +1035,29 @@ struct FlattenPass : public Pass { extra_args(args, 1, design); - std::map<RTLIL::IdString, std::set<RTLIL::IdString>> celltypeMap; - for (auto &it : design->modules) + TechmapWorker worker; + worker.flatten_mode = true; + + std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; + for (auto &it : design->modules_) celltypeMap[it.first].insert(it.first); RTLIL::Module *top_mod = NULL; if (design->full_selection()) - for (auto &mod_it : design->modules) - if (mod_it.second->get_bool_attribute("\\top")) - top_mod = mod_it.second; + for (auto mod : design->modules()) + if (mod->get_bool_attribute("\\top")) + top_mod = mod; bool did_something = true; std::set<RTLIL::Cell*> handled_cells; while (did_something) { did_something = false; if (top_mod != NULL) { - if (techmap_module(design, top_mod, design, handled_cells, celltypeMap, true)) + if (worker.techmap_module(design, top_mod, design, handled_cells, celltypeMap, false)) did_something = true; } else { - for (auto &mod_it : design->modules) - if (techmap_module(design, mod_it.second, design, handled_cells, celltypeMap, true)) + for (auto mod : design->modules()) + if (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) did_something = true; } } @@ -572,18 +1066,16 @@ struct FlattenPass : public Pass { if (top_mod != NULL) { std::map<RTLIL::IdString, RTLIL::Module*> new_modules; - for (auto &mod_it : design->modules) - if (mod_it.second == top_mod) { - new_modules[mod_it.first] = mod_it.second; + for (auto mod : design->modules()) + if (mod == top_mod || mod->get_bool_attribute("\\blackbox")) { + new_modules[mod->name] = mod; } else { - log("Deleting now unused module %s.\n", RTLIL::id2cstr(mod_it.first)); - delete mod_it.second; + log("Deleting now unused module %s.\n", log_id(mod)); + delete mod; } - design->modules.swap(new_modules); + design->modules_.swap(new_modules); } - techmap_cache.clear(); - techmap_do_cache.clear(); log_pop(); } } FlattenPass; 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; + |