diff options
author | Ahmed Irfan <irfan@levert.(none)> | 2015-04-03 16:38:07 +0200 |
---|---|---|
committer | Ahmed Irfan <irfan@levert.(none)> | 2015-04-03 16:38:07 +0200 |
commit | bdf6b2b19ab2206f5957ad5b2ec582c2730d45ee (patch) | |
tree | 1d02541701054a1c3b1cdb66478d0cbc31c2d38f /passes | |
parent | 8acdd90bc918b780ad45cdac42b3baf84d2cc476 (diff) | |
parent | 4b4490761949e738dee54bdfc52e080e0a5c9067 (diff) |
Merge branch 'master' of https://github.com/cliffordwolf/yosys
Diffstat (limited to 'passes')
94 files changed, 7274 insertions, 1913 deletions
diff --git a/passes/abc/abc.cc b/passes/abc/abc.cc index f1d56b23..8cd0211c 100644 --- a/passes/abc/abc.cc +++ b/passes/abc/abc.cc @@ -29,33 +29,42 @@ // 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; 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_COMMAND_LIB "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}" +#define ABC_COMMAND_CTR "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; scorr; ifraig; retime; strash; dch -f; if" +#define ABC_COMMAND_DFL "strash; scorr; ifraig; retime; strash; dch -f; map" -#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" +#define ABC_FAST_COMMAND_LIB "retime {D}; map {D}" +#define ABC_FAST_COMMAND_CTR "retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "retime; if" +#define ABC_FAST_COMMAND_DFL "retime; map" #include "kernel/register.h" #include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cost.h" #include "kernel/log.h" -#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <dirent.h> #include <cerrno> #include <sstream> #include <climits> +#ifndef _WIN32 +# include <unistd.h> +# include <dirent.h> +#endif + #include "blifparse.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + enum class gate_type_t { G_NONE, G_FF, + G_BUF, G_NOT, G_AND, G_NAND, @@ -81,16 +90,17 @@ struct gate_t 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::SigBit, int> signal_map; +bool markgroups; +int map_autoidx; +SigMap assign_map; +RTLIL::Module *module; +std::vector<gate_t> signal_list; +std::map<RTLIL::SigBit, int> signal_map; -static bool clk_polarity; -static RTLIL::SigSpec clk_sig; +bool clk_polarity, en_polarity; +RTLIL::SigSpec clk_sig, en_sig; -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) +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) { assign_map.apply(bit); @@ -124,14 +134,14 @@ static int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in return gate.id; } -static void mark_port(RTLIL::SigSpec sig) +void mark_port(RTLIL::SigSpec sig) { 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, bool keepff) +void extract_cell(RTLIL::Cell *cell, bool keepff) { if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") { @@ -139,7 +149,26 @@ static void extract_cell(RTLIL::Cell *cell, bool keepff) return; if (clk_sig != assign_map(cell->getPort("\\C"))) return; + if (GetSize(en_sig) != 0) + return; + goto matching_dff; + } + + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + if (clk_polarity != (cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_")) + return; + if (en_polarity != (cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_")) + return; + if (clk_sig != assign_map(cell->getPort("\\C"))) + return; + if (en_sig != assign_map(cell->getPort("\\E"))) + return; + goto matching_dff; + } + if (0) { + matching_dff: RTLIL::SigSpec sig_d = cell->getPort("\\D"); RTLIL::SigSpec sig_q = cell->getPort("\\Q"); @@ -157,7 +186,7 @@ static void extract_cell(RTLIL::Cell *cell, bool keepff) return; } - if (cell->type == "$_NOT_") + if (cell->type.in("$_BUF_", "$_NOT_")) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); @@ -165,7 +194,7 @@ static void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_a); assign_map.apply(sig_y); - map_signal(sig_y, G(NOT), map_signal(sig_a)); + map_signal(sig_y, cell->type == "$_BUF_" ? G(BUF) : G(NOT), map_signal(sig_a)); module->remove(cell); return; @@ -273,14 +302,14 @@ static void extract_cell(RTLIL::Cell *cell, bool keepff) } } -static std::string remap_name(RTLIL::IdString abc_name) +std::string remap_name(RTLIL::IdString abc_name) { std::stringstream sstr; sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); return sstr.str(); } -static void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts) +void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts) { if (f == NULL) return; @@ -309,7 +338,7 @@ static void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edge fprintf(f, "}\n"); } -static void handle_loops() +void handle_loops() { // http://en.wikipedia.org/wiki/Topological_sorting // (Kahn, Arthur B. (1962), "Topological sorting of large networks") @@ -442,7 +471,7 @@ static void handle_loops() fclose(dot_f); } -static std::string add_echos_to_abc_cmd(std::string str) +std::string add_echos_to_abc_cmd(std::string str) { std::string new_str, token; for (size_t i = 0; i < str.size(); i++) { @@ -450,8 +479,6 @@ static std::string add_echos_to_abc_cmd(std::string str) 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(); } @@ -459,14 +486,14 @@ static std::string add_echos_to_abc_cmd(std::string str) if (!token.empty()) { if (!new_str.empty()) - new_str += "echo; echo + " + token + "; "; + new_str += "echo + " + token + "; "; new_str += token; } return new_str; } -static std::string fold_abc_cmd(std::string str) +std::string fold_abc_cmd(std::string str) { std::string token, new_str = " "; int char_counter = 10; @@ -485,63 +512,165 @@ static std::string fold_abc_cmd(std::string str) 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, - bool keepff, std::string delay_target, bool fast_mode) +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ + if (show_tempdir) + return text; + + while (1) { + size_t pos = text.find(tempdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); + } + + std::string selfdir_name = proc_self_dirname(); + while (1) { + size_t pos = text.find(selfdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); + } + + return text; +} + +struct abc_output_filter +{ + bool got_cr; + int escape_seq_state; + std::string linebuf; + std::string tempdir_name; + bool show_tempdir; + + abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) + { + got_cr = false; + escape_seq_state = 0; + } + + void next_char(char ch) + { + if (escape_seq_state == 0 && ch == '\033') { + escape_seq_state = 1; + return; + } + if (escape_seq_state == 1) { + escape_seq_state = ch == '[' ? 2 : 0; + return; + } + if (escape_seq_state == 2) { + if ((ch < '0' || '9' < ch) && ch != ';') + escape_seq_state = 0; + return; + } + escape_seq_state = 0; + if (ch == '\r') { + got_cr = true; + return; + } + if (ch == '\n') { + log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); + got_cr = false, linebuf.clear(); + return; + } + if (got_cr) + got_cr = false, linebuf.clear(); + linebuf += ch; + } + + void next_line(const std::string &line) + { + for (char ch : line) + next_char(ch); + } +}; + +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, int lut_mode2, bool dff_mode, std::string clk_str, + bool keepff, std::string delay_target, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir) { module = current_module; map_autoidx = autoidx++; signal_map.clear(); signal_list.clear(); - assign_map.set(module); - clk_polarity = true; - clk_sig = RTLIL::SigSpec(); + if (clk_str != "$") + { + assign_map.set(module); + + clk_polarity = true; + clk_sig = RTLIL::SigSpec(); - char tempdir_name[] = "/tmp/yosys-abc-XXXXXX"; + en_polarity = true; + en_sig = RTLIL::SigSpec(); + } + + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; if (!cleanup) tempdir_name[0] = tempdir_name[4] = '_'; - char *p = mkdtemp(tempdir_name); - log_header("Extracting gate netlist of module `%s' to `%s/input.blif'..\n", module->name.c_str(), tempdir_name); - if (p == NULL) - log_error("For some reason mkdtemp() failed!\n"); + tempdir_name = make_temp_dir(tempdir_name); + log_header("Extracting gate netlist of module `%s' to `%s/input.blif'..\n", + module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); + + std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); + + if (!liberty_file.empty()) { + abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + if (!constr_file.empty()) + abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); + } else + if (lut_mode) + abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); + else + abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str()); - std::string abc_command; if (!script_file.empty()) { if (script_file[0] == '+') { for (size_t i = 1; i < script_file.size(); i++) if (script_file[i] == '\'') - abc_command += "'\\''"; + abc_script += "'\\''"; else if (script_file[i] == ',') - abc_command += " "; + abc_script += " "; else - abc_command += script_file[i]; + abc_script += script_file[i]; } else - abc_command = stringf("source %s", script_file.c_str()); + abc_script += stringf("source %s", script_file.c_str()); } else if (lut_mode) - abc_command = fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; + abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; else if (!liberty_file.empty()) - abc_command = constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); + abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); else - abc_command = fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + abc_script += 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); + for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); - abc_command = add_echos_to_abc_cmd(abc_command); + abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); + abc_script = add_echos_to_abc_cmd(abc_script); - 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); - } + for (size_t i = 0; i+1 < abc_script.size(); i++) + if (abc_script[i] == ';' && abc_script[i+1] == ' ') + abc_script[i+1] = '\n'; + + FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); + fprintf(f, "%s\n", abc_script.c_str()); + fclose(f); - if (clk_str.empty()) { + if (!clk_str.empty() && clk_str != "$") + { + if (clk_str.find(',') != std::string::npos) { + int pos = clk_str.find(','); + std::string en_str = clk_str.substr(pos+1); + clk_str = clk_str.substr(0, pos); + if (en_str[0] == '!') { + en_polarity = false; + en_str = en_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) + en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); + } if (clk_str[0] == '!') { clk_polarity = false; clk_str = clk_str.substr(1); @@ -550,41 +679,21 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); } - if (dff_mode && clk_sig.size() == 0) - { - int best_dff_counter = 0; - std::map<std::pair<bool, RTLIL::SigSpec>, int> dff_counters; + if (dff_mode && clk_sig.empty()) + log_error("Clock domain %s not found.\n", clk_str.c_str()); - 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->getPort("\\C"))); - if (++dff_counters[key] > best_dff_counter) { - best_dff_counter = dff_counters[key]; - clk_polarity = key.first; - clk_sig = key.second; - } - } - } - - if (dff_mode || !clk_str.empty()) { + if (dff_mode || !clk_str.empty()) + { 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)); + log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching"); + else { + log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig)); + if (en_sig.size() != 0) + log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig)); + log("\n"); + } } - if (clk_sig.size() != 0) - mark_port(clk_sig); - - std::vector<RTLIL::Cell*> 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, keepff); @@ -596,14 +705,19 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std for (auto &cell_it : module->cells_) for (auto &port_it : cell_it.second->connections()) mark_port(port_it.second); - + + if (clk_sig.size() != 0) + mark_port(clk_sig); + + if (en_sig.size() != 0) + mark_port(en_sig); + handle_loops(); - if (asprintf(&p, "%s/input.blif", tempdir_name) < 0) log_abort(); - FILE *f = fopen(p, "wt"); + std::string buffer = stringf("%s/input.blif", tempdir_name.c_str()); + f = fopen(buffer.c_str(), "wt"); if (f == NULL) - log_error("Opening %s for writing failed: %s\n", p, strerror(errno)); - free(p); + log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); fprintf(f, ".model netlist\n"); @@ -642,7 +756,10 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std int count_gates = 0; for (auto &si : signal_list) { - if (si.type == G(NOT)) { + if (si.type == G(BUF)) { + fprintf(f, ".names n%d n%d\n", si.in1, si.id); + fprintf(f, "1 1\n"); + } else 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 == G(AND)) { @@ -700,132 +817,64 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std fprintf(f, ".end\n"); fclose(f); - log("Extracted %d gates and %zd wires to a netlist network with %d inputs and %d outputs.\n", - count_gates, signal_list.size(), count_input, count_output); + log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", + count_gates, GetSize(signal_list), count_input, count_output); log_push(); - + if (count_output > 0) { log_header("Executing ABC.\n"); - if (asprintf(&p, "%s/stdcells.genlib", tempdir_name) < 0) log_abort(); - f = fopen(p, "wt"); + buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str()); + f = fopen(buffer.c_str(), "wt"); if (f == NULL) - log_error("Opening %s for writing failed: %s\n", p, strerror(errno)); - 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 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"); + log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); + fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); + fprintf(f, "GATE ONE 1 Y=CONST1;\n"); + fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); + fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); fclose(f); - free(p); if (lut_mode) { - if (asprintf(&p, "%s/lutdefs.txt", tempdir_name) < 0) log_abort(); - f = fopen(p, "wt"); + buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); + f = fopen(buffer.c_str(), "wt"); if (f == NULL) - log_error("Opening %s for writing failed: %s\n", p, strerror(errno)); + log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); for (int i = 0; i < lut_mode; i++) fprintf(f, "%d 1.00 1.00\n", i+1); + for (int i = lut_mode; i < lut_mode2; i++) + fprintf(f, "%d %d.00 1.00\n", i+1, 2 << (i - lut_mode)); fclose(f); - free(p); } - std::string buffer; - if (!liberty_file.empty()) { - 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 -v %s; ", constr_file.c_str()); - buffer += abc_command + "; "; - } else - if (lut_mode) - buffer += stringf("%s -s -c 'read_blif %s/input.blif; read_lut %s/lutdefs.txt; %s; ", - exe_file.c_str(), tempdir_name, tempdir_name, abc_command.c_str()); - else - buffer += stringf("%s -s -c 'read_blif %s/input.blif; read_library %s/stdcells.genlib; %s; ", - exe_file.c_str(), tempdir_name, tempdir_name, abc_command.c_str()); - buffer += stringf("write_blif %s/output.blif' 2>&1", tempdir_name); + buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); + log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); - log("%s\n", buffer.c_str()); + abc_output_filter filt(tempdir_name, show_tempdir); + int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); + if (ret != 0) + log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); - errno = ENOMEM; // popen does not set errno if memory allocation fails, therefore set it by hand - f = popen(buffer.c_str(), "r"); + buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); + f = fopen(buffer.c_str(), "rt"); 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) - log_error("Closing pipe to `%s' failed: %s\n", buffer.c_str(), strerror(errno)); - if (WEXITSTATUS(ret) != 0) { - switch (WEXITSTATUS(ret)) { - case 127: log_error("ABC: execution of command \"%s\" failed: Command not found\n", exe_file.c_str()); break; - case 126: log_error("ABC: execution of command \"%s\" failed: Command not executable\n", exe_file.c_str()); break; - default: log_error("ABC: execution of command \"%s\" failed: the shell returned %d\n", exe_file.c_str(), WEXITSTATUS(ret)); break; - } - } - - if (asprintf(&p, "%s/%s", tempdir_name, "output.blif") < 0) log_abort(); - f = fopen(p, "rt"); - if (f == NULL) - log_error("Can't open ABC output file `%s'.\n", p); + log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); bool builtin_lib = liberty_file.empty() && script_file.empty() && !lut_mode; RTLIL::Design *mapped_design = abc_parse_blif(f, builtin_lib ? "\\DFF" : "\\_dff_"); fclose(f); - free(p); log_header("Re-integrating ABC results.\n"); RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"]; @@ -834,6 +883,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std for (auto &it : mapped_mod->wires_) { RTLIL::Wire *w = it.second; RTLIL::Wire *wire = module->addWire(remap_name(w->name)); + if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx; design->select(module, wire); } @@ -859,6 +909,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (c->type == "\\NOT") { RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_NOT_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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); @@ -866,6 +917,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } 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) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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)])); @@ -874,6 +926,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (c->type == "\\MUX") { RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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)])); @@ -883,6 +936,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (c->type == "\\AOI3" || c->type == "\\OAI3") { RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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)])); @@ -892,6 +946,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (c->type == "\\AOI4" || c->type == "\\OAI4") { RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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)])); @@ -902,7 +957,15 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (c->type == "\\DFF") { log_assert(clk_sig.size() == 1); - RTLIL::Cell *cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); + } + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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); @@ -927,7 +990,15 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std } if (c->type == "\\_dff_") { log_assert(clk_sig.size() == 1); - RTLIL::Cell *cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); + } + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; 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); @@ -935,6 +1006,7 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std continue; } RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->parameters = c->parameters; for (auto &conn : c->connections()) { RTLIL::SigSpec newsig; @@ -990,23 +1062,8 @@ static void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std if (cleanup) { - log_header("Removing temp directory `%s':\n", tempdir_name); - - struct dirent **namelist; - int n = scandir(tempdir_name, &namelist, 0, alphasort); - 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(); - log("Removing `%s'.\n", p); - remove(p); - free(p); - } - free(namelist[i]); - } - free(namelist); - log("Removing `%s'.\n", tempdir_name); - rmdir(tempdir_name); + log("Removing temp directory.\n"); + remove_directory(tempdir_name); } log_pop(); @@ -1087,15 +1144,20 @@ struct AbcPass : public Pass { log(" -lut <width>\n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); + log(" -lut <w1>:<w2>\n"); + log(" generate netlist using luts of (max) the specified width <w2>. All\n"); + log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); + log(" the area cost doubles with each additional input bit. the delay cost\n"); + log(" is still constant for all lut widths.\n"); + log("\n"); log(" -dff\n"); - log(" also pass $_DFF_?_ cells through ABC (only one clock domain, if many\n"); - log(" clock domains are present in a module, the one with the largest number\n"); - log(" of $_DFF_?_ cells in it is used)\n"); + log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); + log(" clock domains are automatically partitioned in clock domains and each\n"); + log(" domain is passed through ABC independently.\n"); log("\n"); - log(" -clk [!]<signal-name>\n"); - log(" use the specified clock domain. (when this option is used in combination\n"); - 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(" -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n"); + log(" use only the specified clock domain. this is like -dff, but only FF\n"); + log(" cells that belong to the specified clock domain are used.\n"); log("\n"); log(" -keepff\n"); log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); @@ -1105,6 +1167,15 @@ struct AbcPass : public Pass { log(" when this option is used, the temporary files created by this pass\n"); log(" are not removed. this is useful for debugging.\n"); log("\n"); + log(" -showtmp\n"); + log(" print the temp dir name in log. usually this is suppressed so that the\n"); + log(" command output is identical across runs.\n"); + log("\n"); + log(" -markgroups\n"); + log(" set a 'abcgroup' attribute on all objects created by ABC. The value of\n"); + log(" this attribute is a unique integer for each ABC process started. This\n"); + log(" is useful for debugging the partitioning of clock domains.\n"); + log("\n"); log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n"); log("loaded into ABC before the ABC script is executed.\n"); log("\n"); @@ -1122,7 +1193,14 @@ struct AbcPass : public Pass { 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; + bool show_tempdir = false; + int lut_mode = 0, lut_mode2 = 0; + markgroups = false; + +#ifdef _WIN32 + if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) + exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif size_t argidx; char pwd [PATH_MAX]; @@ -1138,19 +1216,19 @@ struct AbcPass : public Pass { } if (arg == "-script" && argidx+1 < args.size()) { script_file = args[++argidx]; - if (!script_file.empty() && script_file[0] != '/' && script_file[0] != '+') + if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') script_file = std::string(pwd) + "/" + script_file; continue; } if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; - if (!liberty_file.empty() && liberty_file[0] != '/') + if (!liberty_file.empty() && !is_absolute_path(liberty_file)) liberty_file = std::string(pwd) + "/" + liberty_file; continue; } if (arg == "-constr" && argidx+1 < args.size()) { constr_file = args[++argidx]; - if (!constr_file.empty() && constr_file[0] != '/') + if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; continue; } @@ -1159,7 +1237,15 @@ struct AbcPass : public Pass { continue; } if (arg == "-lut" && argidx+1 < args.size()) { - lut_mode = atoi(args[++argidx].c_str()); + string arg = args[++argidx]; + size_t pos = arg.find_first_of(':'); + if (pos != string::npos) { + lut_mode = atoi(arg.substr(0, pos).c_str()); + lut_mode2 = atoi(arg.substr(pos+1).c_str()); + } else { + lut_mode = atoi(arg.c_str()); + lut_mode2 = lut_mode; + } continue; } if (arg == "-fast") { @@ -1172,6 +1258,7 @@ struct AbcPass : public Pass { } if (arg == "-clk" && argidx+1 < args.size()) { clk_str = args[++argidx]; + dff_mode = true; continue; } if (arg == "-keepff") { @@ -1182,6 +1269,14 @@ struct AbcPass : public Pass { cleanup = false; continue; } + if (arg == "-showtmp") { + show_tempdir = true; + continue; + } + if (arg == "-markgroups") { + markgroups = true; + continue; + } break; } extra_args(args, argidx, design); @@ -1191,12 +1286,158 @@ 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_) - 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, keepff, delay_target, fast_mode); + for (auto mod : design->selected_modules()) + if (mod->processes.size() > 0) + log("Skipping module %s as it contains processes.\n", log_id(mod)); + else if (!dff_mode || !clk_str.empty()) + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, dff_mode, clk_str, keepff, delay_target, fast_mode, mod->selected_cells(), show_tempdir); + else + { + assign_map.set(mod); + CellTypes ct(design); + + std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set<RTLIL::Cell*> expand_queue, next_expand_queue; + std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; + std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; + + typedef std::tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; + std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + + for (auto cell : all_cells) + { + clkdomain_t key; + + for (auto &conn : cell->connections()) + for (auto bit : conn.second) { + bit = assign_map(bit); + if (bit.wire != nullptr) { + cell_to_bit[cell].insert(bit); + bit_to_cell[bit].insert(cell); + if (ct.cell_input(cell->type, conn.first)) { + cell_to_bit_up[cell].insert(bit); + bit_to_cell_down[bit].insert(cell); + } + if (ct.cell_output(cell->type, conn.first)) { + cell_to_bit_down[cell].insert(bit); + bit_to_cell_up[bit].insert(cell); + } + } + } + + if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") + { + key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); + } + else + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"; + bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"; + key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E"))); + } + else + continue; + + unassigned_cells.erase(cell); + expand_queue.insert(cell); + expand_queue_up.insert(cell); + expand_queue_down.insert(cell); + + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + while (!expand_queue_up.empty() || !expand_queue_down.empty()) + { + if (!expand_queue_up.empty()) + { + RTLIL::Cell *cell = *expand_queue_up.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_up.erase(cell); + + for (auto bit : cell_to_bit_up[cell]) + for (auto c : bit_to_cell_up[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (!expand_queue_down.empty()) + { + RTLIL::Cell *cell = *expand_queue_down.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_down.erase(cell); + + for (auto bit : cell_to_bit_down[cell]) + for (auto c : bit_to_cell_down[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (expand_queue_up.empty() && expand_queue_down.empty()) { + expand_queue_up.swap(next_expand_queue_up); + expand_queue_down.swap(next_expand_queue_down); + } + } + + while (!expand_queue.empty()) + { + RTLIL::Cell *cell = *expand_queue.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue.erase(cell); + + for (auto bit : cell_to_bit.at(cell)) { + for (auto c : bit_to_cell[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + } + bit_to_cell[bit].clear(); + } + + if (expand_queue.empty()) + expand_queue.swap(next_expand_queue); + } + + clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); + for (auto cell : unassigned_cells) { + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + log_header("Summary of detected clock domains:\n"); + for (auto &it : assigned_cells) + log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), + std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), + std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); + + for (auto &it : assigned_cells) { + clk_polarity = std::get<0>(it.first); + clk_sig = assign_map(std::get<1>(it.first)); + en_polarity = std::get<2>(it.first); + en_sig = assign_map(std::get<3>(it.first)); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, + !clk_sig.empty(), "$", keepff, delay_target, fast_mode, it.second, show_tempdir); + assign_map.set(mod); + } } assign_map.clear(); @@ -1207,3 +1448,4 @@ struct AbcPass : public Pass { } } AbcPass; +PRIVATE_NAMESPACE_END diff --git a/passes/abc/blifparse.cc b/passes/abc/blifparse.cc index 1fbb5720..db87eec4 100644 --- a/passes/abc/blifparse.cc +++ b/passes/abc/blifparse.cc @@ -18,9 +18,8 @@ */ #include "blifparse.h" -#include "kernel/log.h" -#include <stdio.h> -#include <string.h> + +YOSYS_NAMESPACE_BEGIN static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, FILE *f) { @@ -130,7 +129,8 @@ RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) if (p == NULL) goto error; - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(p)); + IdString celltype = RTLIL::escape_id(p); + RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); while ((p = strtok(NULL, " \t\r\n")) != NULL) { char *q = strchr(p, '='); @@ -240,3 +240,5 @@ error: // return NULL; } +YOSYS_NAMESPACE_END + diff --git a/passes/abc/blifparse.h b/passes/abc/blifparse.h index 272e4e64..31f5b2e5 100644 --- a/passes/abc/blifparse.h +++ b/passes/abc/blifparse.h @@ -20,9 +20,12 @@ #ifndef ABC_BLIFPARSE #define ABC_BLIFPARSE -#include "kernel/rtlil.h" +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN extern RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name); -#endif +YOSYS_NAMESPACE_END +#endif diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index eba61d1d..e4b40c41 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -14,11 +14,12 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o -OBJS += passes/cmds/log.o +OBJS += passes/cmds/logcmd.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 +OBJS += passes/cmds/check.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index e3fde855..054cfc1c 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -17,9 +17,10 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output, bool flag_global) { @@ -150,3 +151,4 @@ struct AddPass : public Pass { } } AddPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc new file mode 100644 index 00000000..bb8fe78e --- /dev/null +++ b/passes/cmds/check.cc @@ -0,0 +1,154 @@ +/* + * 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/celltypes.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CheckPass : public Pass { + CheckPass() : Pass("check", "check for obvious problems in the design") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" check [options] [selection]\n"); + log("\n"); + log("This pass identifies the following problems in the current design:\n"); + log("\n"); + log(" - combinatorical loops\n"); + log("\n"); + log(" - two or more conflicting drivers for one wire\n"); + log("\n"); + log(" - used wires that do not have a driver\n"); + log("\n"); + log("When called with -noinit then this command also checks for wires which have\n"); + log("the 'init' attribute set.\n"); + log("\n"); + log("When called with -assert then the command will produce an error if any\n"); + log("problems are found in the current design.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + int counter = 0; + bool noinit = false; + bool assert_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-noinit") { + noinit = true; + continue; + } + if (args[argidx] == "-assert") { + assert_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + log_header("Executing CHECK pass (checking for obvious problems).\n"); + + for (auto module : design->selected_whole_modules_warn()) + { + if (module->has_processes_warn()) + continue; + + log("checking module %s..\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, vector<string>> wire_drivers; + pool<SigBit> used_wires; + TopoSort<string> topo; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + SigSpec sig = sigmap(conn.second); + bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); + if (cell->input(conn.first)) + for (auto bit : sig) + if (bit.wire) { + if (logic_cell) + topo.edge(stringf("wire %s", log_signal(bit)), + stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); + used_wires.insert(bit); + } + if (cell->output(conn.first)) + for (int i = 0; i < GetSize(sig); i++) { + if (logic_cell) + topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), + stringf("wire %s", log_signal(sig[i]))); + wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", + log_id(conn.first), i, log_id(cell), log_id(cell->type))); + } + } + + for (auto wire : module->wires()) { + if (wire->port_input) { + SigSpec sig = sigmap(wire); + for (int i = 0; i < GetSize(sig); i++) + wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); + } + if (wire->port_output) + for (auto bit : sigmap(wire)) + if (bit.wire) used_wires.insert(bit); + if (noinit && wire->attributes.count("\\init")) { + log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); + counter++; + } + } + + for (auto it : wire_drivers) + if (GetSize(it.second) > 1) { + string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first)); + for (auto str : it.second) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + + for (auto bit : used_wires) + if (!wire_drivers.count(bit)) { + log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit)); + counter++; + } + + topo.sort(); + for (auto &loop : topo.loops) { + string message = stringf("found logic loop in module %s:\n", log_id(module)); + for (auto &str : loop) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + } + + log("found and reported %d problems.\n", counter); + + if (assert_mode && counter > 0) + log_error("Found %d problems in 'check -assert'.\n", counter); + } +} CheckPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index 30c80f73..e17c1b1c 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -23,6 +23,9 @@ #include "kernel/celltypes.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, RTLIL::SigSpec &sig) { CellTypes ct(design); @@ -183,3 +186,4 @@ struct ConnectPass : public Pass { } } ConnectPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index aac11716..a65a6364 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -22,6 +22,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct ConnwrappersWorker { struct portdecl_t { @@ -203,3 +206,4 @@ struct ConnwrappersPass : public Pass { } } ConnwrappersPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index be775820..459e5b0e 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -21,6 +21,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct CopyPass : public Pass { CopyPass() : Pass("copy", "copy modules in the design") { } virtual void help() @@ -53,3 +56,4 @@ struct CopyPass : public Pass { } } CopyPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index ac72ba53..5644066a 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -17,14 +17,22 @@ * */ +#include "kernel/yosys.h" #include <sys/types.h> -#include <unistd.h> -#include <fnmatch.h> + +#ifndef _WIN32 +# include <unistd.h> +#else +# include <io.h> +#endif #include "kernel/register.h" #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct CoverPass : public Pass { CoverPass() : Pass("cover", "print code coverage counters") { } virtual void help() @@ -72,7 +80,7 @@ struct CoverPass : public Pass { 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("Coverage counters are only available in Yosys for Linux.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) @@ -92,9 +100,13 @@ struct CoverPass : public Pass { const char *open_mode = args[argidx] == "-a" ? "a+" : "w"; std::string filename = args[++argidx]; if (args[argidx-1] == "-d") { + #ifdef _WIN32 + log_cmd_error("The 'cover -d' option is not supported on win32.\n"); + #else char filename_buffer[4096]; snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid()); filename = mkstemps(filename_buffer, 4); + #endif } FILE *f = fopen(filename.c_str(), open_mode); if (f == NULL) { @@ -116,11 +128,11 @@ struct CoverPass : public Pass { log("\n"); } -#ifdef COVER_ACTIVE +#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) for (auto &it : get_coverage_data()) { if (!patterns.empty()) { for (auto &p : patterns) - if (!fnmatch(p.c_str(), it.first.c_str(), 0)) + if (patmatch(p.c_str(), it.first.c_str())) goto pattern_match; continue; } @@ -134,7 +146,7 @@ struct CoverPass : public Pass { for (auto f : out_files) fclose(f); - log_cmd_error("Coverage counters are only available in debug builds of Yosys for Linux.\n"); + log_cmd_error("This version of Yosys was not built with support for code coverage counters.\n"); #endif for (auto f : out_files) @@ -142,3 +154,4 @@ struct CoverPass : public Pass { } } CoverPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index 2a91bc9e..b4362887 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -17,9 +17,10 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN struct DeletePass : public Pass { DeletePass() : Pass("delete", "delete objects in the design") { } @@ -90,10 +91,10 @@ struct DeletePass : public Pass { continue; } - std::set<RTLIL::Wire*> delete_wires; - std::set<RTLIL::Cell*> delete_cells; - std::set<RTLIL::IdString> delete_procs; - std::set<RTLIL::IdString> delete_mems; + pool<RTLIL::Wire*> delete_wires; + pool<RTLIL::Cell*> delete_cells; + pool<RTLIL::IdString> delete_procs; + pool<RTLIL::IdString> delete_mems; for (auto &it : module->wires_) if (design->selected(module, it.second)) @@ -140,3 +141,4 @@ struct DeletePass : public Pass { } } DeletePass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/log.cc b/passes/cmds/logcmd.cc index 34db0eed..85386f3d 100644 --- a/passes/cmds/log.cc +++ b/passes/cmds/logcmd.cc @@ -22,6 +22,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct LogPass : public Pass { LogPass() : Pass("log", "print text and log files") { } virtual void help() @@ -76,3 +79,4 @@ struct LogPass : public Pass { } } LogPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index 4e8234d1..f7c65bbd 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -28,9 +28,9 @@ YOSYS_NAMESPACE_BEGIN std::map<std::string, void*> loaded_plugins; std::map<std::string, std::string> loaded_plugin_aliases; +#ifdef YOSYS_ENABLE_PLUGINS void load_plugin(std::string filename, std::vector<std::string> aliases) { -#ifdef YOSYS_ENABLE_PLUGINS if (filename.find('/') == std::string::npos) filename = "./" + filename; @@ -44,10 +44,13 @@ void load_plugin(std::string filename, std::vector<std::string> aliases) for (auto &alias : aliases) loaded_plugin_aliases[alias] = filename; +} #else +void load_plugin(std::string, std::vector<std::string>) +{ log_error("This version of yosys is built without plugin support.\n"); -#endif } +#endif struct PluginPass : public Pass { PluginPass() : Pass("plugin", "load and list loaded plugins") { } @@ -112,7 +115,7 @@ struct PluginPass : public Pass { log("\n"); int max_alias_len = 1; for (auto &it : loaded_plugin_aliases) - max_alias_len = std::max(max_alias_len, SIZE(it.first)); + max_alias_len = std::max(max_alias_len, GetSize(it.first)); for (auto &it : loaded_plugin_aliases) log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str()); } diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 91de364f..17d803e9 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -21,6 +21,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name) { from_name = RTLIL::escape_id(from_name); @@ -31,8 +34,11 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: for (auto &it : module->wires_) if (it.first == from_name) { - 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); + Wire *w = it.second; + log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module)); + module->rename(w, to_name); + if (w->port_id) + module->fixup_ports(); return; } @@ -113,7 +119,7 @@ struct RenamePass : public Pass { if (!design->selected(module)) continue; - std::map<RTLIL::IdString, RTLIL::Wire*> new_wires; + dict<RTLIL::IdString, RTLIL::Wire*> new_wires; for (auto &it : module->wires_) { if (it.first[0] == '$' && design->selected(module, it.second)) do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); @@ -121,8 +127,9 @@ struct RenamePass : public Pass { new_wires[it.second->name] = it.second; } module->wires_.swap(new_wires); + module->fixup_ports(); - std::map<RTLIL::IdString, RTLIL::Cell*> new_cells; + dict<RTLIL::IdString, RTLIL::Cell*> new_cells; for (auto &it : module->cells_) { if (it.first[0] == '$' && design->selected(module, it.second)) do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); @@ -143,7 +150,7 @@ struct RenamePass : public Pass { if (!design->selected(module)) continue; - std::map<RTLIL::IdString, RTLIL::Wire*> new_wires; + dict<RTLIL::IdString, RTLIL::Wire*> new_wires; for (auto &it : module->wires_) { if (design->selected(module, it.second)) if (it.first[0] == '\\' && it.second->port_id == 0) @@ -151,8 +158,9 @@ struct RenamePass : public Pass { new_wires[it.second->name] = it.second; } module->wires_.swap(new_wires); + module->fixup_ports(); - std::map<RTLIL::IdString, RTLIL::Cell*> new_cells; + dict<RTLIL::IdString, RTLIL::Cell*> new_cells; for (auto &it : module->cells_) { if (design->selected(module, it.second)) if (it.first[0] == '\\') @@ -196,3 +204,4 @@ struct RenamePass : public Pass { } } RenamePass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index e09c0012..1cd55ecb 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -22,6 +22,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct ScatterPass : public Pass { ScatterPass() : Pass("scatter", "add additional intermediate nets") { } virtual void help() @@ -67,3 +70,4 @@ struct ScatterPass : public Pass { } } ScatterPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 5224f5bc..f4eeac07 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -29,6 +29,9 @@ #include <stdio.h> #include <set> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct SccWorker { RTLIL::Design *design; @@ -97,7 +100,8 @@ struct SccWorker } } - SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool allCellTypes, int maxDepth) : design(design), module(module), sigmap(module) + SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) : + design(design), module(module), sigmap(module) { if (module->processes.size() > 0) { log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str()); @@ -164,11 +168,23 @@ struct SccWorker labelCounter = 0; cellLabels.clear(); - while (workQueue.size() > 0) { + while (workQueue.size() > 0) + { RTLIL::Cell *cell = *workQueue.begin(); log_assert(cellStack.size() == 0); cellDepth.clear(); - run(cell, 0, maxDepth); + + if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { + log("Found an SCC:"); + std::set<RTLIL::Cell*> scc; + log(" %s", RTLIL::id2cstr(cell->name)); + cell2scc[cell] = sccList.size(); + scc.insert(cell); + sccList.push_back(scc); + workQueue.erase(cell); + log("\n"); + } else + run(cell, 0, maxDepth); } log("Found %d SCCs in module %s.\n", int(sccList.size()), RTLIL::id2cstr(module->name)); @@ -209,10 +225,18 @@ struct SccPass : public Pass { log("This command identifies strongly connected components (aka logic loops) in the\n"); log("design.\n"); log("\n"); + log(" -expect <num>\n"); + log(" expect to find exactly <num> SSCs. A different number of SSCs will\n"); + log(" produce an error.\n"); + log("\n"); log(" -max_depth <num>\n"); - log(" limit to loops not longer than the specified number of cells. This can\n"); - log(" e.g. be useful in identifying local loops in a module that turns out\n"); - log(" to be one gigantic SCC.\n"); + log(" limit to loops not longer than the specified number of cells. This\n"); + log(" can e.g. be useful in identifying small local loops in a module that\n"); + log(" implements one large SCC.\n"); + log("\n"); + log(" -nofeedback\n"); + log(" do not count cells that have their output fed back into one of their\n"); + log(" inputs as single-cell scc.\n"); log("\n"); log(" -all_cell_types\n"); log(" Usually this command only considers internal non-memory cells. With\n"); @@ -236,7 +260,9 @@ struct SccPass : public Pass { std::map<std::string, std::string> setCellAttr, setWireAttr; bool allCellTypes = false; bool selectMode = false; + bool nofeedbackMode = false; int maxDepth = -1; + int expect = -1; log_header("Executing SCC pass (detecting logic loops).\n"); @@ -246,6 +272,14 @@ struct SccPass : public Pass { maxDepth = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-expect" && argidx+1 < args.size()) { + expect = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-nofeedback") { + nofeedbackMode = true; + continue; + } if (args[argidx] == "-all_cell_types") { allCellTypes = true; continue; @@ -279,16 +313,26 @@ struct SccPass : public Pass { log_cmd_error("The -set*_attr options are not implemented at the moment!\n"); RTLIL::Selection newSelection(false); + int scc_counter = 0; for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { - SccWorker worker(design, mod_it.second, allCellTypes, maxDepth); + SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); + scc_counter += GetSize(worker.sccList); if (selectMode) worker.select(newSelection); } + if (expect >= 0) { + if (scc_counter == expect) + log("Found and expected %d SCCs.\n", scc_counter); + else + log_error("Found %d SCCs but expected %d.\n", scc_counter, expect); + } else + log("Found %d SCCs.\n", scc_counter); + if (selectMode) { log_assert(origSelectPos >= 0); design->selection_stack[origSelectPos] = newSelection; @@ -297,3 +341,4 @@ struct SccPass : public Pass { } } SccPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 4c540ca6..53ff4d47 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -17,14 +17,15 @@ * */ -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include <string.h> -#include <fnmatch.h> #include <errno.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + using RTLIL::id2cstr; static std::vector<RTLIL::Selection> work_stack; @@ -35,9 +36,9 @@ static bool match_ids(RTLIL::IdString id, std::string pattern) return true; if (id.size() > 0 && id[0] == '\\' && id.substr(1) == pattern) return true; - if (!fnmatch(pattern.c_str(), id.c_str(), 0)) + if (patmatch(pattern.c_str(), id.c_str())) return true; - if (id.size() > 0 && id[0] == '\\' && !fnmatch(pattern.c_str(), id.substr(1).c_str(), 0)) + if (id.size() > 0 && id[0] == '\\' && patmatch(pattern.c_str(), id.substr(1).c_str())) return true; if (id.size() > 0 && id[0] == '$' && pattern.size() > 0 && pattern[0] == '$') { const char *p = id.c_str(); @@ -80,7 +81,7 @@ static bool match_attr_val(const RTLIL::Const &value, std::string pattern, char std::string value_str = value.decode_string(); if (match_op == '=') - if (!fnmatch(pattern.c_str(), value.decode_string().c_str(), FNM_NOESCAPE)) + if (patmatch(pattern.c_str(), value.decode_string().c_str())) return true; if (match_op == '=') @@ -100,13 +101,13 @@ static bool match_attr_val(const RTLIL::Const &value, std::string pattern, char log_abort(); } -static bool match_attr(const std::map<RTLIL::IdString, RTLIL::Const> &attributes, std::string name_pat, std::string value_pat, char match_op) +static bool match_attr(const dict<RTLIL::IdString, RTLIL::Const> &attributes, std::string name_pat, std::string value_pat, char match_op) { if (name_pat.find('*') != std::string::npos || name_pat.find('?') != std::string::npos || name_pat.find('[') != std::string::npos) { for (auto &it : attributes) { - if (!fnmatch(name_pat.c_str(), it.first.c_str(), FNM_NOESCAPE) && match_attr_val(it.second, value_pat, match_op)) + if (patmatch(name_pat.c_str(), it.first.c_str()) && match_attr_val(it.second, value_pat, match_op)) return true; - if (it.first.size() > 0 && it.first[0] == '\\' && !fnmatch(name_pat.c_str(), it.first.substr(1).c_str(), FNM_NOESCAPE) && match_attr_val(it.second, value_pat, match_op)) + if (it.first.size() > 0 && it.first[0] == '\\' && patmatch(name_pat.c_str(), it.first.substr(1).c_str()) && match_attr_val(it.second, value_pat, match_op)) return true; } } else { @@ -118,7 +119,7 @@ static bool match_attr(const std::map<RTLIL::IdString, RTLIL::Const> &attributes return false; } -static bool match_attr(const std::map<RTLIL::IdString, RTLIL::Const> &attributes, std::string match_expr) +static bool match_attr(const dict<RTLIL::IdString, RTLIL::Const> &attributes, std::string match_expr) { size_t pos = match_expr.find_first_of("<!=>"); @@ -364,7 +365,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, std::string str, } } -static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct) +static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only) { int sel_objects = 0; bool is_input, is_output; @@ -375,6 +376,7 @@ 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; + auto selected_members = lhs.selected_members[mod->name]; for (auto &it : mod->wires_) if (lhs.selected_member(mod_it.first, it.first) && limits.count(it.first) == 0) @@ -388,9 +390,9 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v for (size_t i = 0; i < conn_lhs.size(); i++) { if (conn_lhs[i].wire == NULL || conn_rhs[i].wire == NULL) continue; - if (mode != 'i' && selected_wires.count(conn_rhs[i].wire) && lhs.selected_members[mod->name].count(conn_lhs[i].wire->name) == 0) + if (mode != 'i' && selected_wires.count(conn_rhs[i].wire) && selected_members.count(conn_lhs[i].wire->name) == 0) lhs.selected_members[mod->name].insert(conn_lhs[i].wire->name), sel_objects++, max_objects--; - if (mode != 'o' && selected_wires.count(conn_lhs[i].wire) && lhs.selected_members[mod->name].count(conn_rhs[i].wire->name) == 0) + if (mode != 'o' && selected_wires.count(conn_lhs[i].wire) && selected_members.count(conn_rhs[i].wire->name) == 0) lhs.selected_members[mod->name].insert(conn_rhs[i].wire->name), sel_objects++, max_objects--; } } @@ -399,6 +401,8 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v for (auto &conn : cell.second->connections()) { char last_mode = '-'; + if (eval_only && !yosys_celltypes.cell_evaluable(cell.second->type)) + goto exclude_match; for (auto &rule : rules) { last_mode = rule.mode; if (rule.cell_types.size() > 0 && rule.cell_types.count(cell.second->type) == 0) @@ -417,10 +421,10 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v is_output = mode == 'x' || ct.cell_output(cell.second->type, conn.first); 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 (max_objects != 0 && selected_wires.count(chunk.wire) > 0 && selected_members.count(cell.first) == 0) if (mode == 'x' || (mode == 'i' && is_output) || (mode == 'o' && is_input)) lhs.selected_members[mod->name].insert(cell.first), sel_objects++, max_objects--; - if (max_objects != 0 && lhs.selected_members[mod->name].count(cell.first) > 0 && limits.count(cell.first) == 0 && lhs.selected_members[mod->name].count(chunk.wire->name) == 0) + if (max_objects != 0 && selected_members.count(cell.first) > 0 && limits.count(cell.first) == 0 && selected_members.count(chunk.wire->name) == 0) if (mode == 'x' || (mode == 'i' && is_input) || (mode == 'o' && is_output)) lhs.selected_members[mod->name].insert(chunk.wire->name), sel_objects++, max_objects--; } @@ -431,9 +435,10 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v return sel_objects; } -static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) +static void select_op_expand(RTLIL::Design *design, std::string arg, char mode, bool eval_only) { - int pos = mode == 'x' ? 2 : 3, levels = 1, rem_objects = -1; + int pos = (mode == 'x' ? 2 : 3) + (eval_only ? 1 : 0); + int levels = 1, rem_objects = -1; std::vector<expand_rule_t> rules; std::set<RTLIL::IdString> limits; @@ -524,14 +529,14 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) #endif while (levels-- > 0 && rem_objects != 0) { - int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct); + int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct, eval_only); if (num_objects == 0) break; rem_objects -= num_objects; } if (rem_objects == 0) - log("Warning: reached configured limit at `%s'.\n", arg.c_str()); + log_warning("reached configured limit at `%s'.\n", arg.c_str()); } static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &sel) @@ -631,17 +636,32 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%x.\n"); - select_op_expand(design, arg, 'x'); + select_op_expand(design, arg, 'x', false); } else if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%ci.\n"); - select_op_expand(design, arg, 'i'); + select_op_expand(design, arg, 'i', false); } else if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%co.\n"); - select_op_expand(design, arg, 'o'); + select_op_expand(design, arg, 'o', false); + } else + if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%x" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%xe.\n"); + select_op_expand(design, arg, 'x', true); + } else + if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%cie.\n"); + select_op_expand(design, arg, 'i', true); + } else + if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%coe.\n"); + select_op_expand(design, arg, 'o', true); } else log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str()); if (work_stack.size() >= 1) @@ -795,8 +815,11 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_filter_active_mod(design, work_stack.back()); } +PRIVATE_NAMESPACE_END +YOSYS_NAMESPACE_BEGIN + // used in kernel/register.cc and maybe other locations, extern decl. in register.h -void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, size_t args_size, RTLIL::Design *design) +void handle_extra_select_args(Pass *pass, vector<string> args, size_t argidx, size_t args_size, RTLIL::Design *design) { work_stack.clear(); for (; argidx < args_size; argidx++) { @@ -812,20 +835,46 @@ void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t select_op_union(design, work_stack.front(), work_stack.back()); work_stack.pop_back(); } - if (work_stack.size() > 0) - design->selection_stack.push_back(work_stack.back()); - else + if (work_stack.empty()) design->selection_stack.push_back(RTLIL::Selection(false)); + else + design->selection_stack.push_back(work_stack.back()); } +// extern decl. in register.h +RTLIL::Selection eval_select_args(const vector<string> &args, RTLIL::Design *design) +{ + work_stack.clear(); + for (auto &arg : args) + select_stmt(design, arg); + while (work_stack.size() > 1) { + select_op_union(design, work_stack.front(), work_stack.back()); + work_stack.pop_back(); + } + if (work_stack.empty()) + return RTLIL::Selection(false); + return work_stack.back(); +} + +// extern decl. in register.h +void eval_select_op(vector<RTLIL::Selection> &work, const string &op, RTLIL::Design *design) +{ + work_stack.swap(work); + select_stmt(design, op); + work_stack.swap(work); +} + +YOSYS_NAMESPACE_END +PRIVATE_NAMESPACE_BEGIN + struct SelectPass : public Pass { SelectPass() : Pass("select", "modify and view the list of selected objects") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" select [ -add | -del | -set <name> ] <selection>\n"); - log(" select [ -assert-none | -assert-any ] <selection>\n"); + log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n"); + log(" select [ -assert-none | -assert-any ] {-read <filename> | <selection>}\n"); log(" select [ -list | -write <filename> | -count | -clear ]\n"); log(" select -module <modname>\n"); log("\n"); @@ -867,6 +916,9 @@ struct SelectPass : public Pass { log(" -write <filename>\n"); log(" like -list but write the output to the specified file\n"); log("\n"); + log(" -read <filename>\n"); + log(" read the specified file (written by -write)\n"); + log("\n"); log(" -count\n"); log(" count all objects in the current selection\n"); log("\n"); @@ -997,6 +1049,9 @@ struct SelectPass : public Pass { log(" %%co[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); log(" simmilar to %%x, but only select input (%%ci) or output cones (%%co)\n"); log("\n"); + log(" %%xe[...] %%cie[...] %%coe\n"); + log(" like %%x, %%ci, and %%co but only consider combinatorial cells\n"); + log("\n"); log(" %%a\n"); log(" expand top set by selecting all wires that are (at least in part)\n"); log(" aliases for selected wires.\n"); @@ -1026,9 +1081,8 @@ struct SelectPass : public Pass { bool assert_none = false; bool assert_any = false; int assert_count = -1; - std::string write_file; - std::string set_name; - std::string sel_str; + std::string write_file, read_file; + std::string set_name, sel_str; work_stack.clear(); @@ -1072,6 +1126,10 @@ struct SelectPass : public Pass { write_file = args[++argidx]; continue; } + if (arg == "-read" && argidx+1 < args.size()) { + read_file = args[++argidx]; + continue; + } if (arg == "-count") { count_mode = true; continue; @@ -1094,6 +1152,34 @@ struct SelectPass : public Pass { sel_str += " " + arg; } + if (!read_file.empty()) + { + if (!sel_str.empty()) + log_cmd_error("Option -read can not be combined with a selection expression.\n"); + + std::ifstream f(read_file); + if (f.fail()) + log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno)); + + RTLIL::Selection sel(false); + string line; + + while (std::getline(f, line)) { + size_t slash_pos = line.find('/'); + if (slash_pos == string::npos) { + log_warning("Ignoring line without slash in 'select -read': %s\n", line.c_str()); + continue; + } + IdString mod_name = RTLIL::escape_id(line.substr(0, slash_pos)); + IdString obj_name = RTLIL::escape_id(line.substr(slash_pos+1)); + sel.selected_members[mod_name].insert(obj_name); + } + + select_filter_active_mod(design, sel); + sel.optimize(design); + work_stack.push_back(sel); + } + if (clear_mode && args.size() != 2) log_cmd_error("Option -clear can not be combined with any other options.\n"); @@ -1135,7 +1221,7 @@ struct SelectPass : public Pass { if (list_mode || count_mode || !write_file.empty()) { - #define LOG_OBJECT(...) do { if (list_mode) log(__VA_ARGS__); if (f != NULL) fprintf(f, __VA_ARGS__); total_count++; } while (0) + #define LOG_OBJECT(...) { if (list_mode) log(__VA_ARGS__); if (f != NULL) fprintf(f, __VA_ARGS__); total_count++; } int total_count = 0; FILE *f = NULL; if (!write_file.empty()) { @@ -1154,16 +1240,16 @@ struct SelectPass : public Pass { if (sel->selected_module(mod_it.first)) { 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)); + 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)); + LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)) 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)); + LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)) for (auto &it : mod_it.second->processes) if (sel->selected_member(mod_it.first, it.first)) - LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); + LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)) } } if (count_mode) @@ -1320,21 +1406,20 @@ struct CdPass : public Pass { } CdPass; template<typename T> -static int log_matches(const char *title, std::string pattern, T list) +static void log_matches(const char *title, Module *module, T list) { - std::vector<RTLIL::IdString> matches; + std::vector<IdString> matches; for (auto &it : list) - if (pattern.empty() || match_ids(it.first, pattern)) + if (module->selected(it.second)) matches.push_back(it.first); - if (matches.empty()) - return 0; - - log("\n%d %s:\n", int(matches.size()), title); - for (auto &id : matches) - log(" %s\n", RTLIL::id2cstr(id)); - return matches.size(); + if (!matches.empty()) { + log("\n%d %s:\n", int(matches.size()), title); + std::sort(matches.begin(), matches.end(), RTLIL::sort_by_id_str()); + for (auto id : matches) + log(" %s\n", RTLIL::id2cstr(id)); + } } struct LsPass : public Pass { @@ -1343,44 +1428,42 @@ struct LsPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" ls [pattern]\n"); + log(" ls [selection]\n"); log("\n"); - log("When no active module is selected, this prints a list of all modules.\n"); + log("When no active module is selected, this prints a list of modules.\n"); log("\n"); log("When an active module is selected, this prints a list of objects in the module.\n"); log("\n"); - log("If a pattern is given, the objects matching the pattern are printed\n"); - log("\n"); - log("Note that this command does not use the selection mechanism and always operates\n"); - log("on the whole design or whole active module. Use 'select -list' to show a list\n"); - log("of currently selected objects.\n"); - log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - std::string pattern; - int counter = 0; - - if (args.size() != 1 && args.size() != 2) - log_cmd_error("Invalid number of arguments.\n"); - if (args.size() == 2) - pattern = args.at(1); + size_t argidx = 1; + extra_args(args, argidx, design); if (design->selected_active_module.empty()) { - counter += log_matches("modules", pattern, design->modules_); + std::vector<IdString> matches; + + for (auto mod : design->selected_modules()) + matches.push_back(mod->name); + + if (!matches.empty()) { + log("\n%d %s:\n", int(matches.size()), "modules"); + std::sort(matches.begin(), matches.end(), RTLIL::sort_by_id_str()); + for (auto id : matches) + log(" %s%s\n", log_id(id), design->selected_whole_module(design->module(id)) ? "" : "*"); + } } else - if (design->modules_.count(design->selected_active_module) > 0) + if (design->module(design->selected_active_module) != nullptr) { - 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("processes", pattern, module->processes); + RTLIL::Module *module = design->module(design->selected_active_module); + log_matches("wires", module, module->wires_); + log_matches("memories", module, module->memories); + log_matches("cells", module, module->cells_); + log_matches("processes", module, module->processes); } - - // log("\nfound %d item%s.\n", counter, counter == 1 ? "" : "s"); } } LsPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 029c0ec7..9a6d8a03 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -21,6 +21,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct setunset_t { RTLIL::IdString name; @@ -47,7 +50,7 @@ struct setunset_t } }; -static void do_setunset(std::map<RTLIL::IdString, RTLIL::Const> &attrs, std::vector<setunset_t> &list) +static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, std::vector<setunset_t> &list) { for (auto &item : list) if (item.unset) @@ -178,3 +181,4 @@ struct SetparamPass : public Pass { } } SetparamPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index c72e64b8..b9a29b7d 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -23,6 +23,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct SetundefWorker { int next_bit_mode; @@ -153,3 +156,4 @@ struct SetundefPass : public Pass { } } SetundefPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 2218eded..81321665 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -21,12 +21,18 @@ #include "kernel/celltypes.h" #include "kernel/log.h" #include <string.h> -#include <dirent.h> + +#ifndef _WIN32 +# include <dirent.h> +#endif #ifdef YOSYS_ENABLE_READLINE # include <readline/readline.h> #endif +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + using RTLIL::id2cstr; #undef CLUSTER_CELLS_AND_PORTBOXES @@ -58,6 +64,10 @@ struct ShowWorker const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections; const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections; + std::map<RTLIL::Const, int> colorattr_cache; + RTLIL::IdString colorattr; + + static uint32_t xorshift32(uint32_t x) { x ^= x << 13; x ^= x >> 17; @@ -69,7 +79,7 @@ struct ShowWorker { if (currentColor == 0) return "color=\"black\""; - return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1); + return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1, currentColor%8+1); } std::string nextColor(std::string presetColor) @@ -122,7 +132,25 @@ struct ShowWorker dot_escape_store.push_back(stringf(", color=\"%s\"", s.first.c_str())); return dot_escape_store.back().c_str(); } - return ""; + + RTLIL::Const colorattr_value; + RTLIL::Cell *cell = module->cell(member_name); + RTLIL::Wire *wire = module->wire(member_name); + + if (cell && cell->attributes.count(colorattr)) + colorattr_value = cell->attributes.at(colorattr); + else if (wire && wire->attributes.count(colorattr)) + colorattr_value = wire->attributes.at(colorattr); + else + return ""; + + if (colorattr_cache.count(colorattr_value) == 0) { + int next_id = GetSize(colorattr_cache); + colorattr_cache[colorattr_value] = (next_id % 8) + 1; + } + + dot_escape_store.push_back(stringf(", colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value))); + return dot_escape_store.back().c_str(); } const char *findLabel(std::string member_name) @@ -175,7 +203,7 @@ struct ShowWorker std::string gen_signode_simple(RTLIL::SigSpec sig, bool range_check = true) { - if (SIZE(sig) == 0) { + if (GetSize(sig) == 0) { fprintf(f, "v%d [ label=\"\" ];\n", single_idx_count); return stringf("v%d", single_idx_count++); } @@ -203,22 +231,24 @@ struct ShowWorker std::string label_string; int pos = sig.size()-1; int idx = single_idx_count++; - for (int i = int(sig.chunks().size())-1; i >= 0; i--) { + for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) { const RTLIL::SigChunk &c = sig.chunks().at(i); net = gen_signode_simple(c, false); log_assert(!net.empty()); + for (rep = 1; i-rep >= 0 && c == sig.chunks().at(i-rep); rep++) {} + std::string repinfo = rep > 1 ? stringf("%dx ", rep) : ""; if (driver) { - label_string += stringf("<s%d> %d:%d - %d:%d |", i, pos, pos-c.width+1, c.offset+c.width-1, c.offset); + label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset); net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); - net_conn_map[net].bits = c.width; + net_conn_map[net].bits = rep*c.width; net_conn_map[net].color = nextColor(c, net_conn_map[net].color); } else { - label_string += stringf("<s%d> %d:%d - %d:%d |", i, c.offset+c.width-1, c.offset, pos, pos-c.width+1); + label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1); net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i)); - net_conn_map[net].bits = c.width; + net_conn_map[net].bits = rep*c.width; net_conn_map[net].color = nextColor(c, net_conn_map[net].color); } - pos -= c.width; + pos -= rep * c.width; } if (label_string[label_string.size()-1] == '|') label_string = label_string.substr(0, label_string.size()-1); @@ -452,8 +482,8 @@ 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.size()).c_str()); - } else { + 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.size(); net_conn_map[right_node].color = nextColor(conn, net_conn_map[right_node].color); net_conn_map[left_node].bits = conn.first.size(); @@ -499,10 +529,10 @@ struct ShowWorker 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) : + const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections, RTLIL::IdString colorattr) : f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels), genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds), - notitle(notitle), color_selections(color_selections), label_selections(label_selections) + notitle(notitle), color_selections(color_selections), label_selections(label_selections), colorattr(colorattr) { ct.setup_internals(); ct.setup_internals_mem(); @@ -560,6 +590,10 @@ struct ShowPass : public Pass { log(" inputs or outputs. This option can be used multiple times to specify\n"); log(" more than one library.\n"); log("\n"); + log(" note: in most cases it is better to load the library before calling\n"); + log(" show with 'read_verilog -lib <filename>'. it is also possible to\n"); + log(" load liberty files with 'read_liberty -lib <filename>'.\n"); + log("\n"); log(" -prefix <prefix>\n"); log(" generate <prefix>.* instead of ~/.yosys_show.*\n"); log("\n"); @@ -578,6 +612,10 @@ struct ShowPass : public Pass { log(" for the random number generator. Change the seed value if the colored\n"); log(" graph still is ambigous. A seed of zero deactivates the coloring.\n"); log("\n"); + log(" -colorattr <attribute_name>\n"); + log(" Use the specified attribute to assign colors. A unique color is\n"); + log(" assigned to each unique value of this attribute.\n"); + log("\n"); log(" -width\n"); log(" annotate busses with a label indicating the width of the bus.\n"); log("\n"); @@ -607,6 +645,9 @@ struct ShowPass : public Pass { log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n"); log("unless another prefix is specified using -prefix <prefix>.\n"); log("\n"); + log("Yosys on Windows and YosysJS use different defaults: The output is written\n"); + log("to 'show.dot' in the current directory and new viewer is launched.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -616,9 +657,14 @@ struct ShowPass : public Pass { std::vector<std::pair<std::string, RTLIL::Selection>> color_selections; std::vector<std::pair<std::string, RTLIL::Selection>> label_selections; +#if defined(EMSCRIPTEN) || defined(_WIN32) + std::string format = "dot"; + std::string prefix = "show"; +#else std::string format; - std::string viewer_exe; std::string prefix = stringf("%s/.yosys_show", getenv("HOME") ? getenv("HOME") : "."); +#endif + std::string viewer_exe; std::vector<std::string> libfiles; std::vector<RTLIL::Design*> libs; uint32_t colorSeed = 0; @@ -629,6 +675,7 @@ struct ShowPass : public Pass { bool flag_enum = false; bool flag_abbeviate = true; bool flag_notitle = false; + RTLIL::IdString colorattr; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -670,6 +717,10 @@ struct ShowPass : public Pass { colorSeed = ShowWorker::xorshift32(colorSeed); continue; } + if (arg == "-colorattr" && argidx+1 < args.size()) { + colorattr = RTLIL::escape_id(args[++argidx]); + continue; + } if (arg == "-format" && argidx+1 < args.size()) { format = args[++argidx]; continue; @@ -745,7 +796,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_signed, 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, colorattr); fclose(f); for (auto lib : libs) @@ -755,22 +806,22 @@ struct ShowPass : public Pass { log_cmd_error("Nothing there to show.\n"); 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()); + std::string cmd = stringf("dot -T%s -o '%s.new' '%s' && mv '%s.new' '%s'", format.c_str(), out_file.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str()); log("Exec: %s\n", cmd.c_str()); - if (system(cmd.c_str()) != 0) + if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); } if (!viewer_exe.empty()) { std::string cmd = stringf("%s '%s' &", viewer_exe.c_str(), out_file.c_str()); log("Exec: %s\n", cmd.c_str()); - if (system(cmd.c_str()) != 0) + if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); } else if (format.empty()) { - std::string cmd = stringf("fuser -s '%s' || xdot '%s' < '%s' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); + std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), 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) + if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); } @@ -795,3 +846,4 @@ struct ShowPass : public Pass { } } ShowPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index d03aaf3b..3e0158c5 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -24,6 +24,9 @@ #include "kernel/log.h" #include <tuple> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct SpliceWorker { RTLIL::Design *design; @@ -179,11 +182,13 @@ struct SpliceWorker if (design->selected(module, it.second)) selected_bits.add(sigmap(it.second)); - for (auto &it : module->cells_) { - if (!sel_by_wire && !design->selected(module, it.second)) + std::vector<Cell*> mod_cells = module->cells(); + + for (auto cell : mod_cells) { + if (!sel_by_wire && !design->selected(module, cell)) continue; - for (auto &conn : it.second->connections_) - if (ct.cell_input(it.second->type, conn.first)) { + for (auto &conn : cell->connections_) + if (ct.cell_input(cell->type, conn.first)) { if (ports.size() > 0 && !ports.count(conn.first)) continue; if (no_ports.size() > 0 && no_ports.count(conn.first)) @@ -202,24 +207,25 @@ struct SpliceWorker } std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires; + std::vector<Wire*> mod_wires = module->wires(); - for (auto &it : module->wires_) - if (!no_outputs && it.second->port_output) { - if (!design->selected(module, it.second)) + for (auto mod : mod_wires) + if (!no_outputs && mod->port_output) { + if (!design->selected(module, mod)) continue; - RTLIL::SigSpec sig = sigmap(it.second); + RTLIL::SigSpec sig = sigmap(mod); if (driven_chunks.count(sig) > 0) continue; RTLIL::SigSpec new_sig = get_spliced_signal(sig); if (new_sig != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(it.second, new_sig)); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, new_sig)); } else - if (!it.second->port_input) { - RTLIL::SigSpec sig = sigmap(it.second); + if (!mod->port_input) { + RTLIL::SigSpec sig = sigmap(mod); if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(it.second, spliced_signals_cache.at(sig))); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, spliced_signals_cache.at(sig))); else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(it.second, sliced_signals_cache.at(sig))); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, sliced_signals_cache.at(sig))); } for (auto &it : rework_wires) @@ -349,3 +355,4 @@ struct SplicePass : public Pass { } } SplicePass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index 344b03fc..d4e721a5 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -22,6 +22,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct SplitnetsWorker { std::map<RTLIL::Wire*, std::vector<RTLIL::SigBit>> splitmap; @@ -173,7 +176,7 @@ struct SplitnetsPass : public Pass { module->rewrite_sigspecs(worker); - std::set<RTLIL::Wire*> delete_wires; + pool<RTLIL::Wire*> delete_wires; for (auto &it : worker.splitmap) delete_wires.insert(it.first); module->remove(delete_wires); @@ -183,3 +186,4 @@ struct SplitnetsPass : public Pass { } } SplitnetsPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 19cdaa62..bd3a43ac 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -21,145 +21,145 @@ #include "kernel/celltypes.h" #include "kernel/log.h" -namespace +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct statdata_t { - struct statdata_t + #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ + X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) + + #define X(_name) int _name; + STAT_INT_MEMBERS + #undef X + + std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; + + statdata_t operator+(const statdata_t &other) const { - #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ - X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) + statdata_t sum = other; + #define X(_name) sum._name += _name; + STAT_INT_MEMBERS + #undef X + for (auto &it : num_cells_by_type) + sum.num_cells_by_type[it.first] += it.second; + return sum; + } + + statdata_t operator*(int other) const + { + statdata_t sum = *this; + #define X(_name) sum._name *= other; + STAT_INT_MEMBERS + #undef X + for (auto &it : sum.num_cells_by_type) + it.second *= other; + return sum; + } - #define X(_name) int _name; + statdata_t() + { + #define X(_name) _name = 0; STAT_INT_MEMBERS - #undef X + #undef X + } - std::map<RTLIL::IdString, int> num_cells_by_type; + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode) + { + #define X(_name) _name = 0; + STAT_INT_MEMBERS + #undef X - statdata_t operator+(const statdata_t &other) const + for (auto &it : mod->wires_) { - statdata_t sum = other; - #define X(_name) sum._name += _name; - STAT_INT_MEMBERS - #undef X - for (auto &it : num_cells_by_type) - sum.num_cells_by_type[it.first] += it.second; - return sum; - } + if (!design->selected(mod, it.second)) + continue; - statdata_t operator*(int other) const - { - statdata_t sum = *this; - #define X(_name) sum._name *= other; - STAT_INT_MEMBERS - #undef X - for (auto &it : sum.num_cells_by_type) - it.second *= other; - return sum; + if (it.first[0] == '\\') { + num_pub_wires++; + num_pub_wire_bits += it.second->width; + } + + num_wires++; + num_wire_bits += it.second->width; } - statdata_t() - { - #define X(_name) _name = 0; - STAT_INT_MEMBERS - #undef X + for (auto &it : mod->memories) { + if (!design->selected(mod, it.second)) + continue; + num_memories++; + num_memory_bits += it.second->width * it.second->size; } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode) + for (auto &it : mod->cells_) { - #define X(_name) _name = 0; - STAT_INT_MEMBERS - #undef X - - for (auto &it : mod->wires_) - { - if (!design->selected(mod, it.second)) - continue; - - if (it.first[0] == '\\') { - num_pub_wires++; - num_pub_wire_bits += it.second->width; - } - - num_wires++; - num_wire_bits += it.second->width; - } + if (!design->selected(mod, it.second)) + continue; - for (auto &it : mod->memories) { - if (!design->selected(mod, it.second)) - continue; - num_memories++; - num_memory_bits += it.second->width * it.second->size; - } + RTLIL::IdString cell_type = it.second->type; - for (auto &it : mod->cells_) + if (width_mode) { - 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"))); + 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") ? GetSize(it.second->getPort("\\A")) : 0; + int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0; + int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0; + cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y})); } - - num_cells++; - num_cells_by_type[cell_type]++; + else if (cell_type.in("$mux", "$pmux")) + cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y"))); + else if (cell_type.in("$sr", "$dff", "$dffsr", "$adff", "$dlatch", "$dlatchsr")) + cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q"))); } - for (auto &it : mod->processes) { - if (!design->selected(mod, it.second)) - continue; - num_processes++; - } + num_cells++; + num_cells_by_type[cell_type]++; } - void log_data() - { - log(" Number of wires: %6d\n", num_wires); - log(" Number of wire bits: %6d\n", num_wire_bits); - log(" Number of public wires: %6d\n", num_pub_wires); - log(" Number of public wire bits: %6d\n", num_pub_wire_bits); - log(" Number of memories: %6d\n", num_memories); - log(" Number of memory bits: %6d\n", num_memory_bits); - log(" Number of processes: %6d\n", num_processes); - log(" Number of cells: %6d\n", num_cells); - for (auto &it : num_cells_by_type) - log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second); + for (auto &it : mod->processes) { + if (!design->selected(mod, it.second)) + continue; + num_processes++; } - }; + } - statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level) + void log_data() { - statdata_t mod_data = mod_stat.at(mod); - std::map<RTLIL::IdString, int> num_cells_by_type; - num_cells_by_type.swap(mod_data.num_cells_by_type); - + log(" Number of wires: %6d\n", num_wires); + log(" Number of wire bits: %6d\n", num_wire_bits); + log(" Number of public wires: %6d\n", num_pub_wires); + log(" Number of public wire bits: %6d\n", num_pub_wire_bits); + log(" Number of memories: %6d\n", num_memories); + log(" Number of memory bits: %6d\n", num_memory_bits); + log(" Number of processes: %6d\n", num_processes); + log(" Number of cells: %6d\n", num_cells); for (auto &it : num_cells_by_type) - if (mod_stat.count(it.first) > 0) { - log(" %*s%-*s %6d\n", 2*level, "", 26-2*level, RTLIL::id2cstr(it.first), it.second); - mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1) * it.second; - mod_data.num_cells -= it.second; - } else { - mod_data.num_cells_by_type[it.first] += it.second; - } - - return mod_data; + log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second); } +}; + +statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level) +{ + statdata_t mod_data = mod_stat.at(mod); + std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; + num_cells_by_type.swap(mod_data.num_cells_by_type); + + for (auto &it : num_cells_by_type) + if (mod_stat.count(it.first) > 0) { + log(" %*s%-*s %6d\n", 2*level, "", 26-2*level, RTLIL::id2cstr(it.first), it.second); + mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1) * it.second; + mod_data.num_cells -= it.second; + } else { + mod_data.num_cells_by_type[it.first] += it.second; + } + + return mod_data; } struct StatPass : public Pass { @@ -208,20 +208,17 @@ struct StatPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules_) + for (auto mod : design->selected_modules()) { - if (!design->selected_module(it.first)) - continue; - if (!top_mod && design->full_selection()) - if (it.second->get_bool_attribute("\\top")) - top_mod = it.second; + if (mod->get_bool_attribute("\\top")) + top_mod = mod; - statdata_t data(design, it.second, width_mode); - mod_stat[it.first] = data; + statdata_t data(design, mod, width_mode); + mod_stat[mod->name] = data; log("\n"); - log("=== %s%s ===\n", RTLIL::id2cstr(it.first), design->selected_whole_module(it.first) ? "" : " (partially selected)"); + log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); log("\n"); data.log_data(); } @@ -243,3 +240,4 @@ struct StatPass : public Pass { } } StatPass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index 6f80ef72..a0484090 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -22,6 +22,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct TeePass : public Pass { TeePass() : Pass("tee", "redirect command output to file") { } virtual void help() @@ -73,11 +76,11 @@ struct TeePass : public Pass { try { std::vector<std::string> new_args(args.begin() + argidx, args.end()); Pass::call(design, new_args); - } catch (log_cmd_error_expection) { + } catch (...) { for (auto cf : files_to_close) fclose(cf); log_files = backup_log_files; - throw log_cmd_error_expection(); + throw; } for (auto cf : files_to_close) @@ -86,3 +89,4 @@ struct TeePass : public Pass { } } TeePass; +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc index 09293a86..1a5f873f 100644 --- a/passes/cmds/trace.cc +++ b/passes/cmds/trace.cc @@ -20,38 +20,39 @@ #include "kernel/yosys.h" +USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TraceMonitor : public RTLIL::Monitor { - virtual void notify_module_add(RTLIL::Module *module) OVERRIDE + virtual void notify_module_add(RTLIL::Module *module) YS_OVERRIDE { log("#TRACE# Module add: %s\n", log_id(module)); } - virtual void notify_module_del(RTLIL::Module *module) OVERRIDE + virtual void notify_module_del(RTLIL::Module *module) YS_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 + virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_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 + virtual void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_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 + virtual void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_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 + virtual void notify_blackout(RTLIL::Module *module) YS_OVERRIDE { log("#TRACE# Blackout in module %s:\n", log_id(module)); } @@ -84,9 +85,9 @@ struct TracePass : public Pass { try { std::vector<std::string> new_args(args.begin() + argidx, args.end()); Pass::call(design, new_args); - } catch (log_cmd_error_expection) { + } catch (...) { design->monitors.erase(&monitor); - throw log_cmd_error_expection(); + throw; } design->monitors.erase(&monitor); diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index 813e215b..25ec4acc 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -20,6 +20,9 @@ #include "kernel/yosys.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct WriteFileFrontend : public Frontend { WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } virtual void help() @@ -65,12 +68,13 @@ struct WriteFileFrontend : public Frontend { FILE *of = fopen(output_filename.c_str(), append_mode ? "a" : "w"); char buffer[64 * 1024]; - size_t bytes; + int bytes; - while (0 < (bytes = f->readsome(buffer, sizeof(buffer)))) + while (0 < (bytes = readsome(*f, buffer, sizeof(buffer)))) fwrite(buffer, bytes, 1, of); fclose(of); } } WriteFileFrontend; +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc new file mode 100644 index 00000000..548eaca3 --- /dev/null +++ b/passes/equiv/Makefile.inc @@ -0,0 +1,9 @@ + +OBJS += passes/equiv/equiv_make.o +OBJS += passes/equiv/equiv_miter.o +OBJS += passes/equiv/equiv_simple.o +OBJS += passes/equiv/equiv_status.o +OBJS += passes/equiv/equiv_add.o +OBJS += passes/equiv/equiv_remove.o +OBJS += passes/equiv/equiv_induct.o + diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc new file mode 100644 index 00000000..a6e2f01b --- /dev/null +++ b/passes/equiv/equiv_add.cc @@ -0,0 +1,89 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivAddPass : public Pass { + EquivAddPass() : Pass("equiv_add", "add a $equiv cell") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_add gold_sig gate_sig\n"); + log("\n"); + log("This command adds an $equiv cell for the specified signals.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + if (GetSize(args) != 3) + cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); + + if (design->selected_active_module.empty()) + log_cmd_error("This command must be executed in module context!\n"); + + Module *module = design->module(design->selected_active_module); + log_assert(module != nullptr); + + SigSpec gold_signal, gate_signal; + + if (!SigSpec::parse(gate_signal, module, args[2])) + log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + + if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) + log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + + log_assert(GetSize(gold_signal) == GetSize(gate_signal)); + SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + + SigMap sigmap(module); + sigmap.apply(gold_signal); + sigmap.apply(gate_signal); + + dict<SigBit, SigBit> to_equiv_bits; + pool<Cell*> added_equiv_cells; + + for (int i = 0; i < GetSize(gold_signal); i++) { + Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); + equiv_cell->set_bool_attribute("\\keep"); + to_equiv_bits[gold_signal[i]] = equiv_signal[i]; + to_equiv_bits[gate_signal[i]] = equiv_signal[i]; + added_equiv_cells.insert(equiv_cell); + } + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { + SigSpec new_sig; + for (auto bit : conn.second) + if (to_equiv_bits.count(sigmap(bit))) + new_sig.append(to_equiv_bits.at(sigmap(bit))); + else + new_sig.append(bit); + if (conn.second != new_sig) + cell->setPort(conn.first, new_sig); + } + } +} EquivAddPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc new file mode 100644 index 00000000..a56730d4 --- /dev/null +++ b/passes/equiv/equiv_induct.cc @@ -0,0 +1,241 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivInductWorker +{ + Module *module; + SigMap sigmap; + + vector<Cell*> cells; + pool<Cell*> workset; + + ezSatPtr ez; + SatGen satgen; + + int max_seq; + int success_counter; + + dict<int, int> ez_step_is_consistent; + pool<Cell*> cell_warn_cache; + SigPool undriven_signals; + + EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq) : module(module), sigmap(module), + cells(module->selected_cells()), workset(unproven_equiv_cells), + satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0) + { + satgen.model_undef = model_undef; + } + + void create_timestep(int step) + { + vector<int> ez_equal_terms; + + for (auto cell : cells) { + if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) { + log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + cell_warn_cache.insert(cell); + } + if (cell->type == "$equiv") { + SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + if (bit_a != bit_b) { + int ez_a = satgen.importSigBit(bit_a, step); + int ez_b = satgen.importSigBit(bit_b, step); + int cond = ez->IFF(ez_a, ez_b); + if (satgen.model_undef) + cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step)); + ez_equal_terms.push_back(cond); + } + } + } + + if (satgen.model_undef) { + for (auto bit : undriven_signals.export_all()) + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step))); + } + + log_assert(!ez_step_is_consistent.count(step)); + ez_step_is_consistent[step] = ez->expression(ez->OpAnd, ez_equal_terms); + } + + void run() + { + log("Found %d unproven $equiv cells in module %s:\n", GetSize(workset), log_id(module)); + + if (satgen.model_undef) { + for (auto cell : cells) + if (yosys_celltypes.cell_known(cell->type)) + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_input(cell->type, conn.first)) + undriven_signals.add(sigmap(conn.second)); + for (auto cell : cells) + if (yosys_celltypes.cell_known(cell->type)) + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_output(cell->type, conn.first)) + undriven_signals.del(sigmap(conn.second)); + } + + create_timestep(1); + + if (satgen.model_undef) { + for (auto bit : satgen.initial_state.export_all()) + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, 1))); + log(" Undef modelling: force def on %d initial reg values and %d inputs.\n", + GetSize(satgen.initial_state), GetSize(undriven_signals)); + } + + for (int step = 1; step <= max_seq; step++) + { + ez->assume(ez_step_is_consistent[step]); + + log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables()); + if (!ez->solve()) { + log(" Proof for base case failed. Circuit inherently diverges!\n"); + return; + } + + create_timestep(step+1); + int new_step_not_consistent = ez->NOT(ez_step_is_consistent[step+1]); + ez->bind(new_step_not_consistent); + + log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables()); + if (!ez->solve(new_step_not_consistent)) { + log(" Proof for induction step holds. Entire workset of %d cells proven!\n", GetSize(workset)); + for (auto cell : workset) + cell->setPort("\\B", cell->getPort("\\A")); + success_counter += GetSize(workset); + return; + } + + log(" Proof for induction step failed. %s\n", step != max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset."); + } + + workset.sort(); + + for (auto cell : workset) + { + SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + + log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y")))); + + int ez_a = satgen.importSigBit(bit_a, max_seq+1); + int ez_b = satgen.importSigBit(bit_b, max_seq+1); + int cond = ez->XOR(ez_a, ez_b); + + if (satgen.model_undef) + cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1))); + + if (!ez->solve(cond)) { + log(" success!\n"); + cell->setPort("\\B", cell->getPort("\\A")); + success_counter++; + } else { + log(" failed.\n"); + } + } + } +}; + +struct EquivInductPass : public Pass { + EquivInductPass() : Pass("equiv_induct", "proving $equiv cells using temporal induction") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_induct [options] [selection]\n"); + log("\n"); + log("Uses a version of temporal induction to prove $equiv cells.\n"); + log("\n"); + log("Only selected $equiv cells are proven and only selected cells are used to\n"); + log("perform the proof.\n"); + log("\n"); + log(" -undef\n"); + log(" enable modelling of undef states\n"); + log("\n"); + log(" -seq <N>\n"); + log(" the max. number of time steps to be considered (default = 4)\n"); + log("\n"); + log("This command is very effective in proving complex sequential circuits, when\n"); + log("the internal state of the circuit quickly propagates to $equiv cells.\n"); + log("\n"); + log("However, this command uses a weak definition of 'equivalence': This command\n"); + log("proves that the two circuits will not diverge after they produce equal\n"); + log("outputs (observable points via $equiv) for at least <N> cycles (the <N>\n"); + log("specified via -seq).\n"); + log("\n"); + log("Combined with simulation this is very powerful because simulation can give\n"); + log("you confidence that the circuits start out synced for at least <N> cycles\n"); + log("after reset.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + int success_counter = 0; + bool model_undef = false; + int max_seq = 4; + + log_header("Executing EQUIV_INDUCT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-undef") { + model_undef = true; + continue; + } + if (args[argidx] == "-seq" && argidx+1 < args.size()) { + max_seq = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + pool<Cell*> unproven_equiv_cells; + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + if (cell->getPort("\\A") != cell->getPort("\\B")) + unproven_equiv_cells.insert(cell); + } + + if (unproven_equiv_cells.empty()) { + log("No selected unproven $equiv cells found in %s.\n", log_id(module)); + continue; + } + + EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq); + worker.run(); + success_counter += worker.success_counter; + } + + log("Proved %d previously unproven $equiv cells.\n", success_counter); + } +} EquivInductPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc new file mode 100644 index 00000000..5635e7a7 --- /dev/null +++ b/passes/equiv/equiv_make.cc @@ -0,0 +1,474 @@ +/* + * 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/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivMakeWorker +{ + Module *gold_mod, *gate_mod, *equiv_mod; + pool<IdString> wire_names, cell_names; + CellTypes ct; + + bool inames; + vector<string> blacklists; + vector<string> encfiles; + + pool<IdString> blacklist_names; + dict<IdString, dict<Const, Const>> encdata; + + pool<SigBit> undriven_bits; + SigMap assign_map; + + void read_blacklists() + { + for (auto fn : blacklists) + { + std::ifstream f(fn); + if (f.fail()) + log_cmd_error("Can't open blacklist file '%s'!\n", fn.c_str()); + + string line, token; + while (std::getline(f, line)) { + while (1) { + token = next_token(line); + if (token.empty()) + break; + blacklist_names.insert(RTLIL::escape_id(token)); + } + } + } + } + + void read_encfiles() + { + for (auto fn : encfiles) + { + std::ifstream f(fn); + if (f.fail()) + log_cmd_error("Can't open encfile '%s'!\n", fn.c_str()); + + dict<Const, Const> *ed = nullptr; + string line, token; + while (std::getline(f, line)) + { + token = next_token(line); + if (token.empty() || token[0] == '#') + continue; + + if (token == ".fsm") { + IdString modname = RTLIL::escape_id(next_token(line)); + IdString signame = RTLIL::escape_id(next_token(line)); + if (encdata.count(signame)) + log_cmd_error("Re-definition of signal '%s' in encfile '%s'!\n", signame.c_str(), fn.c_str()); + encdata[signame] = dict<Const, Const>(); + ed = &encdata[signame]; + continue; + } + + if (token == ".map") { + Const gold_bits = Const::from_string(next_token(line)); + Const gate_bits = Const::from_string(next_token(line)); + (*ed)[gold_bits] = gate_bits; + continue; + } + + log_cmd_error("Syntax error in encfile '%s'!\n", fn.c_str()); + } + } + } + + void copy_to_equiv() + { + Module *gold_clone = gold_mod->clone(); + Module *gate_clone = gate_mod->clone(); + + for (auto it : gold_clone->wires().to_vector()) { + if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0) + wire_names.insert(it->name); + gold_clone->rename(it, it->name.str() + "_gold"); + } + + for (auto it : gold_clone->cells().to_vector()) { + if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0) + cell_names.insert(it->name); + gold_clone->rename(it, it->name.str() + "_gold"); + } + + for (auto it : gate_clone->wires().to_vector()) { + if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0) + wire_names.insert(it->name); + gate_clone->rename(it, it->name.str() + "_gate"); + } + + for (auto it : gate_clone->cells().to_vector()) { + if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0) + cell_names.insert(it->name); + gate_clone->rename(it, it->name.str() + "_gate"); + } + + gold_clone->cloneInto(equiv_mod); + gate_clone->cloneInto(equiv_mod); + delete gold_clone; + delete gate_clone; + } + + void find_same_wires() + { + SigMap assign_map(equiv_mod); + SigMap rd_signal_map; + + // list of cells without added $equiv cells + auto cells_list = equiv_mod->cells().to_vector(); + + for (auto id : wire_names) + { + IdString gold_id = id.str() + "_gold"; + IdString gate_id = id.str() + "_gate"; + + Wire *gold_wire = equiv_mod->wire(gold_id); + Wire *gate_wire = equiv_mod->wire(gate_id); + + if (encdata.count(id)) + { + log("Creating encoder/decoder for signal %s.\n", log_id(id)); + + Wire *dec_wire = equiv_mod->addWire(id.str() + "_decoded", gold_wire->width); + Wire *enc_wire = equiv_mod->addWire(id.str() + "_encoded", gate_wire->width); + + SigSpec dec_a, dec_b, dec_s; + SigSpec enc_a, enc_b, enc_s; + + dec_a = SigSpec(State::Sx, dec_wire->width); + enc_a = SigSpec(State::Sx, enc_wire->width); + + for (auto &it : encdata.at(id)) + { + SigSpec dec_sig = gate_wire, dec_pat = it.second; + SigSpec enc_sig = dec_wire, enc_pat = it.first; + + if (GetSize(dec_sig) != GetSize(dec_pat)) + log_error("Invalid pattern %s for signal %s of size %d!\n", + log_signal(dec_pat), log_signal(dec_sig), GetSize(dec_sig)); + + if (GetSize(enc_sig) != GetSize(enc_pat)) + log_error("Invalid pattern %s for signal %s of size %d!\n", + log_signal(enc_pat), log_signal(enc_sig), GetSize(enc_sig)); + + SigSpec reduced_dec_sig, reduced_dec_pat; + for (int i = 0; i < GetSize(dec_sig); i++) + if (dec_pat[i] == State::S0 || dec_pat[i] == State::S1) { + reduced_dec_sig.append(dec_sig[i]); + reduced_dec_pat.append(dec_pat[i]); + } + + SigSpec reduced_enc_sig, reduced_enc_pat; + for (int i = 0; i < GetSize(enc_sig); i++) + if (enc_pat[i] == State::S0 || enc_pat[i] == State::S1) { + reduced_enc_sig.append(enc_sig[i]); + reduced_enc_pat.append(enc_pat[i]); + } + + SigSpec dec_result = it.first; + for (auto &bit : dec_result) + if (bit != State::S1) bit = State::S0; + + SigSpec enc_result = it.second; + for (auto &bit : enc_result) + if (bit != State::S1) bit = State::S0; + + SigSpec dec_eq = equiv_mod->addWire(NEW_ID); + SigSpec enc_eq = equiv_mod->addWire(NEW_ID); + + equiv_mod->addEq(NEW_ID, reduced_dec_sig, reduced_dec_pat, dec_eq); + cells_list.push_back(equiv_mod->addEq(NEW_ID, reduced_enc_sig, reduced_enc_pat, enc_eq)); + + dec_s.append(dec_eq); + enc_s.append(enc_eq); + dec_b.append(dec_result); + enc_b.append(enc_result); + } + + equiv_mod->addPmux(NEW_ID, dec_a, dec_b, dec_s, dec_wire); + equiv_mod->addPmux(NEW_ID, enc_a, enc_b, enc_s, enc_wire); + + rd_signal_map.add(assign_map(gate_wire), enc_wire); + gate_wire = dec_wire; + } + + if (gold_wire == nullptr || gate_wire == nullptr || gold_wire->width != gate_wire->width) { + if (gold_wire && gold_wire->port_id) + log_error("Can't match gold port `%s' to a gate port.\n", log_id(gold_wire)); + if (gate_wire && gate_wire->port_id) + log_error("Can't match gate port `%s' to a gold port.\n", log_id(gate_wire)); + continue; + } + + log("Presumably equivalent wires: %s (%s), %s (%s) -> %s\n", + log_id(gold_wire), log_signal(assign_map(gold_wire)), + log_id(gate_wire), log_signal(assign_map(gate_wire)), log_id(id)); + + if (gold_wire->port_output || gate_wire->port_output) + { + Wire *wire = equiv_mod->addWire(id, gold_wire->width); + wire->port_output = true; + gold_wire->port_input = false; + gate_wire->port_input = false; + gold_wire->port_output = false; + gate_wire->port_output = false; + + for (int i = 0; i < wire->width; i++) + equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); + + rd_signal_map.add(assign_map(gold_wire), wire); + rd_signal_map.add(assign_map(gate_wire), wire); + } + else + if (gold_wire->port_input || gate_wire->port_input) + { + Wire *wire = equiv_mod->addWire(id, gold_wire->width); + wire->port_input = true; + gold_wire->port_input = false; + gate_wire->port_input = false; + equiv_mod->connect(gold_wire, wire); + equiv_mod->connect(gate_wire, wire); + } + else + { + Wire *wire = equiv_mod->addWire(id, gold_wire->width); + SigSpec rdmap_gold, rdmap_gate, rdmap_equiv; + + for (int i = 0; i < wire->width; i++) { + if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) { + log(" Skipping signal bit %d: undriven on gold side.\n", i); + continue; + } + if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) { + log(" Skipping signal bit %d: undriven on gate side.\n", i); + continue; + } + equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); + rdmap_gold.append(SigBit(gold_wire, i)); + rdmap_gate.append(SigBit(gate_wire, i)); + rdmap_equiv.append(SigBit(wire, i)); + } + + rd_signal_map.add(rdmap_gold, rdmap_equiv); + rd_signal_map.add(rdmap_gate, rdmap_equiv); + } + } + + for (auto c : cells_list) + for (auto &conn : c->connections()) + if (ct.cell_input(c->type, conn.first)) { + SigSpec old_sig = assign_map(conn.second); + SigSpec new_sig = rd_signal_map(old_sig); + if (old_sig != new_sig) { + log("Changing input %s of cell %s (%s): %s -> %s\n", + log_id(conn.first), log_id(c), log_id(c->type), + log_signal(old_sig), log_signal(new_sig)); + c->setPort(conn.first, new_sig); + } + } + + equiv_mod->fixup_ports(); + } + + void find_same_cells() + { + SigMap assign_map(equiv_mod); + + for (auto id : cell_names) + { + IdString gold_id = id.str() + "_gold"; + IdString gate_id = id.str() + "_gate"; + + Cell *gold_cell = equiv_mod->cell(gold_id); + Cell *gate_cell = equiv_mod->cell(gate_id); + + if (gold_cell == nullptr || gate_cell == nullptr || gold_cell->type != gate_cell->type || !ct.cell_known(gold_cell->type) || + gold_cell->parameters != gate_cell->parameters || GetSize(gold_cell->connections()) != GetSize(gate_cell->connections())) + try_next_cell_name: + continue; + + for (auto gold_conn : gold_cell->connections()) + if (!gate_cell->connections().count(gold_conn.first)) + goto try_next_cell_name; + + log("Presumably equivalent cells: %s %s (%s) -> %s\n", + log_id(gold_cell), log_id(gate_cell), log_id(gold_cell->type), log_id(id)); + + for (auto gold_conn : gold_cell->connections()) + { + SigSpec gold_sig = assign_map(gold_conn.second); + SigSpec gate_sig = assign_map(gate_cell->getPort(gold_conn.first)); + + if (ct.cell_output(gold_cell->type, gold_conn.first)) { + equiv_mod->connect(gate_sig, gold_sig); + continue; + } + + for (int i = 0; i < GetSize(gold_sig); i++) + if (gold_sig[i] != gate_sig[i]) { + Wire *w = equiv_mod->addWire(NEW_ID); + equiv_mod->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], w); + gold_sig[i] = w; + } + + gold_cell->setPort(gold_conn.first, gold_sig); + } + + equiv_mod->remove(gate_cell); + equiv_mod->rename(gold_cell, id); + } + } + + void find_undriven_nets(bool mark) + { + undriven_bits.clear(); + assign_map.set(equiv_mod); + + for (auto wire : equiv_mod->wires()) { + for (auto bit : assign_map(wire)) + if (bit.wire) + undriven_bits.insert(bit); + } + + for (auto wire : equiv_mod->wires()) { + if (wire->port_input) + for (auto bit : assign_map(wire)) + undriven_bits.erase(bit); + } + + for (auto cell : equiv_mod->cells()) { + for (auto &conn : cell->connections()) + if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first)) + for (auto bit : assign_map(conn.second)) + undriven_bits.erase(bit); + } + + if (mark) { + SigSpec undriven_sig(undriven_bits); + undriven_sig.sort_and_unify(); + + for (auto chunk : undriven_sig.chunks()) { + log("Setting undriven nets to undef: %s\n", log_signal(chunk)); + equiv_mod->connect(chunk, SigSpec(State::Sx, chunk.width)); + } + } + } + + void run() + { + copy_to_equiv(); + find_undriven_nets(false); + find_same_wires(); + find_same_cells(); + find_undriven_nets(true); + } +}; + +struct EquivMakePass : public Pass { + EquivMakePass() : Pass("equiv_make", "prepare a circuit for equivalence checking") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_make [options] gold_module gate_module equiv_module\n"); + log("\n"); + log("This creates a module annotated with $equiv cells from two presumably\n"); + log("equivalent modules. Use commands such as 'equiv_simple' and 'equiv_status'\n"); + log("to work with the created equivalent checking module.\n"); + log("\n"); + log(" -inames\n"); + log(" Also match cells and wires with $... names.\n"); + log("\n"); + log(" -blacklist <file>\n"); + log(" Do not match cells or signals that match the names in the file.\n"); + log("\n"); + log(" -encfile <file>\n"); + log(" Match FSM encodings using the desiption from the file.\n"); + log(" See 'help fsm_recode' for details.\n"); + log("\n"); + log("Note: The circuit created by this command is not a miter (with something like\n"); + log("a trigger output), but instead uses $equiv cells to encode the equivalence\n"); + log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + EquivMakeWorker worker; + worker.ct.setup(design); + worker.inames = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-inames") { + worker.inames = true; + continue; + } + if (args[argidx] == "-blacklist" && argidx+1 < args.size()) { + worker.blacklists.push_back(args[++argidx]); + continue; + } + if (args[argidx] == "-encfile" && argidx+1 < args.size()) { + worker.encfiles.push_back(args[++argidx]); + continue; + } + break; + } + + if (argidx+3 != args.size()) + log_cmd_error("Invalid number of arguments.\n"); + + worker.gold_mod = design->module(RTLIL::escape_id(args[argidx])); + worker.gate_mod = design->module(RTLIL::escape_id(args[argidx+1])); + worker.equiv_mod = design->module(RTLIL::escape_id(args[argidx+2])); + + if (worker.gold_mod == nullptr) + log_cmd_error("Can't find gold module %s.\n", args[argidx].c_str()); + + if (worker.gate_mod == nullptr) + log_cmd_error("Can't find gate module %s.\n", args[argidx+1].c_str()); + + if (worker.equiv_mod != nullptr) + log_cmd_error("Equiv module %s already exists.\n", args[argidx+2].c_str()); + + if (worker.gold_mod->has_memories() || worker.gold_mod->has_processes()) + log_cmd_error("Gold module contains memories or procresses. Run 'memory' or 'proc' respectively.\n"); + + if (worker.gate_mod->has_memories() || worker.gate_mod->has_processes()) + log_cmd_error("Gate module contains memories or procresses. Run 'memory' or 'proc' respectively.\n"); + + worker.read_blacklists(); + worker.read_encfiles(); + + log_header("Executing EQUIV_MAKE pass (creating equiv checking module).\n"); + + worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2])); + worker.run(); + } +} EquivMakePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc new file mode 100644 index 00000000..23b34818 --- /dev/null +++ b/passes/equiv/equiv_miter.cc @@ -0,0 +1,343 @@ +/* + * 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/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivMiterWorker +{ + CellTypes ct; + SigMap sigmap; + + bool mode_trigger; + bool mode_cmp; + bool mode_assert; + bool mode_undef; + + IdString miter_name; + Module *miter_module; + Module *source_module; + + dict<SigBit, Cell*> bit_to_driver; + pool<Cell*> seed_cells, miter_cells; + pool<Wire*> miter_wires; + + void follow_cone(pool<Cell*> &cone, pool<Cell*> &leaves, Cell *c, bool gold_mode) + { + if (cone.count(c)) + return; + + if (c->type == "$equiv" && !seed_cells.count(c)) { + leaves.insert(c); + return; + } + + cone.insert(c); + + for (auto &conn : c->connections()) { + if (!ct.cell_input(c->type, conn.first)) + continue; + if (c->type == "$equiv" && (conn.first == "\\A") != gold_mode) + continue; + for (auto bit : sigmap(conn.second)) + if (bit_to_driver.count(bit)) + follow_cone(cone, leaves, bit_to_driver.at(bit), gold_mode); + } + } + + void find_miter_cells_wires() + { + sigmap.set(source_module); + + // initialize bit_to_driver + + for (auto c : source_module->cells()) + for (auto &conn : c->connections()) + if (ct.cell_output(c->type, conn.first)) + for (auto bit : sigmap(conn.second)) + if (bit.wire) + bit_to_driver[bit] = c; + + // find seed cells + + for (auto c : source_module->selected_cells()) + if (c->type == "$equiv") { + log("Seed $equiv cell: %s\n", log_id(c)); + seed_cells.insert(c); + } + + // follow cone from seed cells to next $equiv + + while (1) + { + pool<Cell*> gold_cone, gold_leaves; + pool<Cell*> gate_cone, gate_leaves; + + for (auto c : seed_cells) { + follow_cone(gold_cone, gold_leaves, c, true); + follow_cone(gate_cone, gate_leaves, c, false); + } + + log("Gold cone: %d cells (%d leaves).\n", GetSize(gold_cone), GetSize(gold_leaves)); + log("Gate cone: %d cells (%d leaves).\n", GetSize(gate_cone), GetSize(gate_leaves)); + + // done if all leaves are shared leaves + + if (gold_leaves == gate_leaves) { + miter_cells = gold_cone; + miter_cells.insert(gate_cone.begin(), gate_cone.end()); + log("Selected %d miter cells.\n", GetSize(miter_cells)); + break; + } + + // remove shared leaves + + for (auto it = gold_leaves.begin(); it != gold_leaves.end(); ) { + auto it2 = gate_leaves.find(*it); + if (it2 != gate_leaves.end()) { + it = gold_leaves.erase(it); + gate_leaves.erase(it2); + } else + ++it; + } + + // add remaining leaves to seeds and re-run + + log("Adding %d gold and %d gate seed cells.\n", GetSize(gold_leaves), GetSize(gate_leaves)); + seed_cells.insert(gold_leaves.begin(), gold_leaves.end()); + seed_cells.insert(gate_leaves.begin(), gate_leaves.end()); + } + + for (auto c : miter_cells) + for (auto &conn : c->connections()) + for (auto bit : sigmap(conn.second)) + if (bit.wire) + miter_wires.insert(bit.wire); + log("Selected %d miter wires.\n", GetSize(miter_wires)); + } + + void copy_to_miter() + { + // copy wires and cells + + for (auto w : miter_wires) + miter_module->addWire(w->name, w->width); + for (auto c : miter_cells) { + miter_module->addCell(c->name, c); + auto mc = miter_module->cell(c->name); + for (auto &conn : mc->connections()) + mc->setPort(conn.first, sigmap(conn.second)); + } + + // fixup wire references in cells + + sigmap.clear(); + + struct RewriteSigSpecWorker { + RTLIL::Module * mod; + void operator()(SigSpec &sig) { + vector<RTLIL::SigChunk> chunks = sig.chunks(); + for (auto &c : chunks) + if (c.wire != NULL) + c.wire = mod->wires_.at(c.wire->name); + sig = chunks; + } + }; + + RewriteSigSpecWorker rewriteSigSpecWorker; + rewriteSigSpecWorker.mod = miter_module; + miter_module->rewrite_sigspecs(rewriteSigSpecWorker); + + // find undriven or unused wires + + pool<SigBit> driven_bits, used_bits; + + for (auto c : miter_module->cells()) + for (auto &conn : c->connections()) { + if (ct.cell_input(c->type, conn.first)) + for (auto bit : conn.second) + if (bit.wire) + used_bits.insert(bit); + if (ct.cell_output(c->type, conn.first)) + for (auto bit : conn.second) + if (bit.wire) + driven_bits.insert(bit); + } + + // create ports + + for (auto w : miter_module->wires()) { + for (auto bit : SigSpec(w)) { + if (driven_bits.count(bit) && !used_bits.count(bit)) + w->port_output = true; + if (!driven_bits.count(bit) && used_bits.count(bit)) + w->port_input = true; + } + if (w->port_output && w->port_input) + log("Created miter inout port %s.\n", log_id(w)); + else if (w->port_output) + log("Created miter output port %s.\n", log_id(w)); + else if (w->port_input) + log("Created miter input port %s.\n", log_id(w)); + } + + miter_module->fixup_ports(); + } + + void make_stuff() + { + if (!mode_trigger && !mode_cmp && !mode_assert) + return; + + SigSpec trigger_signals; + vector<Cell*> equiv_cells; + + for (auto c : miter_module->cells()) + if (c->type == "$equiv" && c->getPort("\\A") != c->getPort("\\B")) + equiv_cells.push_back(c); + + for (auto c : equiv_cells) + { + SigSpec cmp = mode_undef ? + miter_module->LogicOr(NEW_ID, miter_module->Eqx(NEW_ID, c->getPort("\\A"), State::Sx), + miter_module->Eqx(NEW_ID, c->getPort("\\A"), c->getPort("\\B"))) : + miter_module->Eq(NEW_ID, c->getPort("\\A"), c->getPort("\\B")); + + if (mode_cmp) { + string cmp_name = string("\\cmp") + log_signal(c->getPort("\\Y")); + for (int i = 1; i < GetSize(cmp_name); i++) + if (cmp_name[i] == '\\') + cmp_name[i] = '_'; + else if (cmp_name[i] == ' ') + cmp_name = cmp_name.substr(0, i) + cmp_name.substr(i+1); + auto w = miter_module->addWire(cmp_name); + w->port_output = true; + miter_module->connect(w, cmp); + } + + if (mode_assert) + miter_module->addAssert(NEW_ID, cmp, State::S1); + + trigger_signals.append(miter_module->Not(NEW_ID, cmp)); + } + + if (mode_trigger) { + auto w = miter_module->addWire("\\trigger"); + w->port_output = true; + miter_module->addReduceOr(NEW_ID, trigger_signals, w); + } + + miter_module->fixup_ports(); + } + + void run() + { + log("Creating miter %s from module %s.\n", log_id(miter_module), log_id(source_module)); + find_miter_cells_wires(); + copy_to_miter(); + make_stuff(); + } +}; + +struct EquivMiterPass : public Pass { + EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_miter [options] miter_module [selection]\n"); + log("\n"); + log("This creates a miter module for further analysis of the selected $equiv cells.\n"); + log("\n"); + log(" -trigger\n"); + log(" Create a trigger output\n"); + log("\n"); + log(" -cmp\n"); + log(" Create cmp_* outputs for individual unproven $equiv cells\n"); + log("\n"); + log(" -assert\n"); + log(" Create a $assert cell for each unproven $equiv cell\n"); + log("\n"); + log(" -undef\n"); + log(" Create compare logic that handles undefs correctly\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + EquivMiterWorker worker; + worker.ct.setup(design); + worker.mode_trigger = false; + worker.mode_cmp = false; + worker.mode_assert = false; + worker.mode_undef = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-trigger") { + worker.mode_trigger = true; + continue; + } + if (args[argidx] == "-cmp") { + worker.mode_cmp = true; + continue; + } + if (args[argidx] == "-assert") { + worker.mode_assert = true; + continue; + } + if (args[argidx] == "-undef") { + worker.mode_undef = true; + continue; + } + break; + } + + if (argidx >= args.size()) + log_cmd_error("Invalid number of arguments.\n"); + + worker.miter_name = RTLIL::escape_id(args[argidx++]); + extra_args(args, argidx, design); + + if (design->module(worker.miter_name)) + log_cmd_error("Miter module %s already exists.\n", log_id(worker.miter_name)); + + worker.source_module = nullptr; + for (auto m : design->selected_modules()) { + if (worker.source_module != nullptr) + goto found_two_modules; + worker.source_module = m; + } + + if (worker.source_module == nullptr) + found_two_modules: + log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n"); + + log_header("Executing EQUIV_MITER pass.\n"); + + worker.miter_module = design->addModule(worker.miter_name); + worker.run(); + } +} EquivMiterPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc new file mode 100644 index 00000000..b6e232f9 --- /dev/null +++ b/passes/equiv/equiv_remove.cc @@ -0,0 +1,83 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivRemovePass : public Pass { + EquivRemovePass() : Pass("equiv_remove", "remove $equiv cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_remove [options] [selection]\n"); + log("\n"); + log("This command removes the selected $equiv cells. If neither -gold nor -gate is\n"); + log("used then only proven cells are removed.\n"); + log("\n"); + log(" -gold\n"); + log(" keep gold circuit\n"); + log("\n"); + log(" -gate\n"); + log(" keep gate circuit\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + bool mode_gold = false; + bool mode_gate = false; + int remove_count = 0; + + log_header("Executing EQUIV_REMOVE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-gold") { + mode_gold = true; + continue; + } + if (args[argidx] == "-gate") { + mode_gate = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (mode_gold && mode_gate) + log_cmd_error("Options -gold and -gate are exclusive.\n"); + + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv" && (mode_gold || mode_gate || cell->getPort("\\A") == cell->getPort("\\B"))) { + log("Removing $equiv cell %s.%s (%s).\n", log_id(module), log_id(cell), log_signal(cell->getPort("\\Y"))); + module->connect(cell->getPort("\\Y"), mode_gate ? cell->getPort("\\B") : cell->getPort("\\A")); + module->remove(cell); + remove_count++; + } + } + + log("Removed a total of %d $equiv cells.\n", remove_count); + } +} EquivRemovePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc new file mode 100644 index 00000000..f1b66432 --- /dev/null +++ b/passes/equiv/equiv_simple.cc @@ -0,0 +1,358 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivSimpleWorker +{ + Module *module; + const vector<Cell*> &equiv_cells; + Cell *equiv_cell; + + SigMap &sigmap; + dict<SigBit, Cell*> &bit2driver; + + ezSatPtr ez; + SatGen satgen; + int max_seq; + bool verbose; + + pool<pair<Cell*, int>> imported_cells_cache; + + EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool verbose, bool model_undef) : + module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr), + sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), verbose(verbose) + { + satgen.model_undef = model_undef; + } + + bool find_input_cone(pool<SigBit> &next_seed, pool<Cell*> &cells_cone, pool<SigBit> &bits_cone, const pool<Cell*> &cells_stop, const pool<SigBit> &bits_stop, pool<SigBit> *input_bits, Cell *cell) + { + if (cells_cone.count(cell)) + return false; + + cells_cone.insert(cell); + + if (cells_stop.count(cell)) + return true; + + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_input(cell->type, conn.first)) + for (auto bit : sigmap(conn.second)) { + if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_")) { + if (!conn.first.in("\\CLK", "\\C")) + next_seed.insert(bit); + } else + find_input_cone(next_seed, cells_cone, bits_cone, cells_stop, bits_stop, input_bits, bit); + } + return false; + } + + void find_input_cone(pool<SigBit> &next_seed, pool<Cell*> &cells_cone, pool<SigBit> &bits_cone, const pool<Cell*> &cells_stop, const pool<SigBit> &bits_stop, pool<SigBit> *input_bits, SigBit bit) + { + if (bits_cone.count(bit)) + return; + + bits_cone.insert(bit); + + if (bits_stop.count(bit)) { + if (input_bits != nullptr) input_bits->insert(bit); + return; + } + + if (!bit2driver.count(bit)) + return; + + if (find_input_cone(next_seed, cells_cone, bits_cone, cells_stop, bits_stop, input_bits, bit2driver.at(bit))) + if (input_bits != nullptr) input_bits->insert(bit); + } + + bool run_cell() + { + SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).to_single_sigbit(); + SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).to_single_sigbit(); + int ez_context = ez->frozen_literal(); + + if (satgen.model_undef) + { + int ez_a = satgen.importSigBit(bit_a, max_seq+1); + int ez_b = satgen.importDefSigBit(bit_b, max_seq+1); + int ez_undef_a = satgen.importUndefSigBit(bit_a, max_seq+1); + + ez->assume(ez->XOR(ez_a, ez_b), ez_context); + ez->assume(ez->NOT(ez_undef_a), ez_context); + } + else + { + int ez_a = satgen.importSigBit(bit_a, max_seq+1); + int ez_b = satgen.importSigBit(bit_b, max_seq+1); + ez->assume(ez->XOR(ez_a, ez_b), ez_context); + } + + pool<SigBit> seed_a = { bit_a }; + pool<SigBit> seed_b = { bit_b }; + + if (verbose) { + log(" Trying to prove $equiv cell %s:\n", log_id(equiv_cell)); + log(" A = %s, B = %s, Y = %s\n", log_signal(bit_a), log_signal(bit_b), log_signal(equiv_cell->getPort("\\Y"))); + } else { + log(" Trying to prove $equiv for %s:", log_signal(equiv_cell->getPort("\\Y"))); + } + + int step = max_seq; + while (1) + { + pool<Cell*> no_stop_cells; + pool<SigBit> no_stop_bits; + + pool<Cell*> full_cells_cone_a, full_cells_cone_b; + pool<SigBit> full_bits_cone_a, full_bits_cone_b; + + pool<SigBit> next_seed_a, next_seed_b; + + for (auto bit_a : seed_a) + find_input_cone(next_seed_a, full_cells_cone_a, full_bits_cone_a, no_stop_cells, no_stop_bits, nullptr, bit_a); + next_seed_a.clear(); + + for (auto bit_b : seed_b) + find_input_cone(next_seed_b, full_cells_cone_b, full_bits_cone_b, no_stop_cells, no_stop_bits, nullptr, bit_b); + next_seed_b.clear(); + + pool<Cell*> short_cells_cone_a, short_cells_cone_b; + pool<SigBit> short_bits_cone_a, short_bits_cone_b; + pool<SigBit> input_bits; + + for (auto bit_a : seed_a) + find_input_cone(next_seed_a, short_cells_cone_a, short_bits_cone_a, full_cells_cone_b, full_bits_cone_b, &input_bits, bit_a); + next_seed_a.swap(seed_a); + + for (auto bit_b : seed_b) + find_input_cone(next_seed_b, short_cells_cone_b, short_bits_cone_b, full_cells_cone_a, full_bits_cone_a, &input_bits, bit_b); + next_seed_b.swap(seed_b); + + pool<Cell*> problem_cells; + problem_cells.insert(short_cells_cone_a.begin(), short_cells_cone_a.end()); + problem_cells.insert(short_cells_cone_b.begin(), short_cells_cone_b.end()); + + if (verbose) + log(" Adding %d new cells to the problem (%d A, %d B, %d shared).\n", + GetSize(problem_cells), GetSize(short_cells_cone_a), GetSize(short_cells_cone_b), + (GetSize(short_cells_cone_a) + GetSize(short_cells_cone_b)) - GetSize(problem_cells)); + + for (auto cell : problem_cells) { + auto key = pair<Cell*, int>(cell, step+1); + if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) + log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + imported_cells_cache.insert(key); + } + + if (satgen.model_undef) { + for (auto bit : input_bits) + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1))); + } + + if (verbose) + log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses()); + + if (!ez->solve(ez_context)) { + log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); + equiv_cell->setPort("\\B", equiv_cell->getPort("\\A")); + ez->assume(ez->NOT(ez_context)); + return true; + } + + if (verbose) + log(" Failed to prove equivalence with sequence length %d.\n", max_seq - step); + + if (--step < 0) { + if (verbose) + log(" Reached sequence limit.\n"); + break; + } + + if (seed_a.empty() && seed_b.empty()) { + if (verbose) + log(" No nets to continue in previous time step.\n"); + break; + } + + if (seed_a.empty()) { + if (verbose) + log(" No nets on A-side to continue in previous time step.\n"); + break; + } + + if (seed_b.empty()) { + if (verbose) + log(" No nets on B-side to continue in previous time step.\n"); + break; + } + + if (verbose) { + #if 0 + log(" Continuing analysis in previous time step with the following nets:\n"); + for (auto bit : seed_a) + log(" A: %s\n", log_signal(bit)); + for (auto bit : seed_b) + log(" B: %s\n", log_signal(bit)); + #else + log(" Continuing analysis in previous time step with %d A- and %d B-nets.\n", GetSize(seed_a), GetSize(seed_b)); + #endif + } + } + + if (!verbose) + log(" failed.\n"); + + ez->assume(ez->NOT(ez_context)); + return false; + } + + int run() + { + if (GetSize(equiv_cells) > 1) { + SigSpec sig; + for (auto c : equiv_cells) + sig.append(sigmap(c->getPort("\\Y"))); + log(" Grouping SAT models for %s:\n", log_signal(sig)); + } + + int counter = 0; + for (auto c : equiv_cells) { + equiv_cell = c; + if (run_cell()) + counter++; + } + return counter; + } + +}; + +struct EquivSimplePass : public Pass { + EquivSimplePass() : Pass("equiv_simple", "try proving simple $equiv instances") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_simple [options] [selection]\n"); + log("\n"); + log("This command tries to prove $equiv cells using a simple direct SAT approach.\n"); + log("\n"); + log(" -v\n"); + log(" verbose output\n"); + log("\n"); + log(" -undef\n"); + log(" enable modelling of undef states\n"); + log("\n"); + log(" -nogroup\n"); + log(" disabling grouping of $equiv cells by output wire\n"); + log("\n"); + log(" -seq <N>\n"); + log(" the max. number of time steps to be considered (default = 1)\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + bool verbose = false, model_undef = false, nogroup = false; + int success_counter = 0; + int max_seq = 1; + + log_header("Executing EQUIV_SIMPLE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-v") { + verbose = true; + continue; + } + if (args[argidx] == "-undef") { + model_undef = true; + continue; + } + if (args[argidx] == "-nogroup") { + nogroup = true; + continue; + } + if (args[argidx] == "-seq" && argidx+1 < args.size()) { + max_seq = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + CellTypes ct; + ct.setup_internals(); + ct.setup_stdcells(); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, Cell*> bit2driver; + dict<SigBit, dict<SigBit, Cell*>> unproven_equiv_cells; + int unproven_cells_counter = 0; + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) { + auto bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + auto bit_group = bit; + if (!nogroup && bit_group.wire) + bit_group.offset = 0; + unproven_equiv_cells[bit_group][bit] = cell; + unproven_cells_counter++; + } + + if (unproven_equiv_cells.empty()) + continue; + + log("Found %d unproven $equiv cells (%d groups) in %s:\n", + unproven_cells_counter, GetSize(unproven_equiv_cells), log_id(module)); + + for (auto cell : module->cells()) { + if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_")) + continue; + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_output(cell->type, conn.first)) + for (auto bit : sigmap(conn.second)) + bit2driver[bit] = cell; + } + + unproven_equiv_cells.sort(); + for (auto it : unproven_equiv_cells) + { + it.second.sort(); + + vector<Cell*> cells; + for (auto it2 : it.second) + cells.push_back(it2.second); + + EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, verbose, model_undef); + success_counter += worker.run(); + } + } + + log("Proved %d previously unproven $equiv cells.\n", success_counter); + } +} EquivSimplePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc new file mode 100644 index 00000000..8ca1aacd --- /dev/null +++ b/passes/equiv/equiv_status.cc @@ -0,0 +1,94 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivStatusPass : public Pass { + EquivStatusPass() : Pass("equiv_status", "print status of equivalent checking module") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_status [options] [selection]\n"); + log("\n"); + log("This command prints status information for all selected $equiv cells.\n"); + log("\n"); + log(" -assert\n"); + log(" produce an error if any unproven $equiv cell is found\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + bool assert_mode = false; + int unproven_count = 0; + + log_header("Executing EQUIV_STATUS pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-assert") { + assert_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + vector<Cell*> unproven_equiv_cells; + int proven_equiv_cells = 0; + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + if (cell->getPort("\\A") != cell->getPort("\\B")) + unproven_equiv_cells.push_back(cell); + else + proven_equiv_cells++; + } + + if (unproven_equiv_cells.empty() && !proven_equiv_cells) { + log("No $equiv cells found in %s.\n", log_id(module)); + continue; + } + + log("Found %d $equiv cells in %s:\n", GetSize(unproven_equiv_cells) + proven_equiv_cells, log_id(module)); + log(" Of those cells %d are proven and %d are unproven.\n", proven_equiv_cells, GetSize(unproven_equiv_cells)); + if (unproven_equiv_cells.empty()) { + log(" Equivalence successfully proven!\n"); + } else { + for (auto cell : unproven_equiv_cells) + log(" Unproven $equiv %s: %s %s\n", log_id(cell), log_signal(cell->getPort("\\A")), log_signal(cell->getPort("\\B"))); + } + + unproven_count += GetSize(unproven_equiv_cells); + } + + if (unproven_count != 0) { + log("Found a total of %d unproven $equiv cells.\n", unproven_count); + if (assert_mode && unproven_count != 0) + log_error("Found %d unproven $equiv cells in 'equiv_status -assert'.\n", unproven_count); + } + } +} EquivStatusPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc index 2fae7609..e76be40c 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -22,6 +22,9 @@ #include <stdlib.h> #include <stdio.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct FsmPass : public Pass { FsmPass() : Pass("fsm", "extract and optimize finite state machines") { } virtual void help() @@ -58,6 +61,7 @@ struct FsmPass : public Pass { log("\n"); log(" -encoding tye\n"); log(" -fm_set_fsm_file file\n"); + log(" -encfile file\n"); log(" passed through to fsm_recode pass\n"); log("\n"); } @@ -69,6 +73,7 @@ struct FsmPass : public Pass { bool flag_expand = false; bool flag_export = false; std::string fm_set_fsm_file_opt; + std::string encfile_opt; std::string encoding_opt; log_header("Executing FSM pass (extract and optimize FSM).\n"); @@ -81,7 +86,11 @@ struct FsmPass : public Pass { fm_set_fsm_file_opt = " -fm_set_fsm_file " + args[++argidx]; continue; } - if (arg == "-encoding" && argidx+1 < args.size() && fm_set_fsm_file_opt.empty()) { + if (arg == "-encfile" && argidx+1 < args.size() && encfile_opt.empty()) { + encfile_opt = " -encfile " + args[++argidx]; + continue; + } + if (arg == "-encoding" && argidx+1 < args.size() && encoding_opt.empty()) { encoding_opt = " -encoding " + args[++argidx]; continue; } @@ -124,7 +133,7 @@ struct FsmPass : public Pass { } if (!flag_norecode) - Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt + encoding_opt); + Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt + encfile_opt + encoding_opt); Pass::call(design, "fsm_info"); if (flag_export) @@ -137,3 +146,4 @@ struct FsmPass : public Pass { } } FsmPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index 2c846a4c..c89553c6 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -24,6 +24,9 @@ #include "kernel/celltypes.h" #include "fsmdata.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static RTLIL::Module *module; static SigMap assign_map; typedef std::pair<RTLIL::Cell*, RTLIL::IdString> sig2driver_entry_t; @@ -40,7 +43,7 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig return true; if (recursion_monitor.check_any(sig)) { - log("Warning: logic loop in mux tree at signal %s in module %s.\n", + log_warning("logic loop in mux tree at signal %s in module %s.\n", log_signal(sig), RTLIL::id2cstr(module->name)); return false; } @@ -189,3 +192,4 @@ struct FsmDetectPass : public Pass { } } FsmDetectPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index d1364391..a261eb22 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -25,6 +25,9 @@ #include "fsmdata.h" #include <string.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct FsmExpand { RTLIL::Module *module; @@ -236,7 +239,7 @@ struct FsmExpand if (merged_set.size() > 0 && !already_optimized) FsmData::optimize_fsm(fsm_cell, module); - log(" merged %zd cells into FSM.\n", merged_set.size()); + log(" merged %d cells into FSM.\n", GetSize(merged_set)); } }; @@ -273,3 +276,4 @@ struct FsmExpandPass : public Pass { } } FsmExpandPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index b4a6b3f7..ad927033 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -28,6 +28,9 @@ #include <iostream> #include <fstream> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + /** * Convert a signal into a KISS-compatible textual representation. */ @@ -47,7 +50,7 @@ std::string kiss_convert_signal(const RTLIL::SigSpec &sig) { * @param cell pointer to the FSM cell which should be exported. */ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::string filename, bool origenc) { - std::map<RTLIL::IdString, RTLIL::Const>::iterator attr_it; + dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it; FsmData fsm_data; FsmData::transition_t tr; std::ofstream kiss_file; @@ -142,7 +145,7 @@ struct FsmExportPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - std::map<RTLIL::IdString, RTLIL::Const>::iterator attr_it; + dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it; std::string arg; bool flag_noauto = false; std::string filename; @@ -182,3 +185,5 @@ struct FsmExportPass : public Pass { } } } FsmExportPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 451f00fc..68667ef0 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -29,6 +29,9 @@ #include "kernel/celltypes.h" #include "fsmdata.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static RTLIL::Module *module; static SigMap assign_map; typedef std::pair<RTLIL::IdString, RTLIL::IdString> sig2driver_entry_t; @@ -37,7 +40,7 @@ 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.size(), false); + sig.extend_u0(dff_out.size(), false); if (sig == dff_out) return true; @@ -70,9 +73,9 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL sig_aa.replace(sig_y, sig_a); RTLIL::SigSpec sig_bb; - for (int i = 0; i < SIZE(sig_b)/SIZE(sig_a); i++) { + for (int i = 0; i < GetSize(sig_b)/GetSize(sig_a); i++) { RTLIL::SigSpec s = sig; - s.replace(sig_y, sig_b.extract(i*SIZE(sig_a), SIZE(sig_a))); + s.replace(sig_y, sig_b.extract(i*GetSize(sig_a), GetSize(sig_a))); sig_bb.append(s); } @@ -95,8 +98,8 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL if (!find_states(sig_aa, dff_out, ctrl, states)) return false; - 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)) + for (int i = 0; i < GetSize(sig_bb)/GetSize(sig_aa); i++) { + if (!find_states(sig_bb.extract(i*GetSize(sig_aa), GetSize(sig_aa)), dff_out, ctrl, states)) return false; } } @@ -107,7 +110,7 @@ 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.size() > 0) { - for (int i = 0; i < SIZE(sig); i++) + for (int i = 0; i < GetSize(sig); i++) if (dont_care.extract(sig[i]).size() > 0) sig[i] = noconst_state; } @@ -115,7 +118,7 @@ static RTLIL::Const sig2const(ConstEval &ce, RTLIL::SigSpec sig, RTLIL::State no ce.assign_map.apply(sig); ce.values_map.apply(sig); - for (int i = 0; i < SIZE(sig); i++) + for (int i = 0; i < GetSize(sig); i++) if (sig[i].wire != NULL) sig[i] = noconst_state; @@ -145,7 +148,7 @@ undef_bit_in_next_state: 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++) + for (int i = 0; i < GetSize(ctrl_in); i++) ctrl_in_bit_indices[ctrl_in[i]] = i; for (auto &it : ctrl_in_bit_indices) @@ -287,7 +290,7 @@ static void extract_fsm(RTLIL::Wire *wire) log(" fsm extraction failed: state selection tree is not closed.\n"); return; } - if (SIZE(states) <= 1) { + if (GetSize(states) <= 1) { log(" fsm extraction failed: at least two states are required.\n"); return; } @@ -456,3 +459,4 @@ struct FsmExtractPass : public Pass { } } FsmExtractPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index 45d68a90..4a1f1d9a 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -25,6 +25,9 @@ #include "fsmdata.h" #include <string.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct FsmInfoPass : public Pass { FsmInfoPass() : Pass("fsm_info", "print information on finite state machines") { } virtual void help() @@ -56,3 +59,4 @@ struct FsmInfoPass : public Pass { } } FsmInfoPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index ab6d5671..155801a3 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -25,10 +25,13 @@ #include "fsmdata.h" #include <string.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + 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++) + log_assert(GetSize(super_pattern.bits) == GetSize(sub_pattern.bits)); + for (int i = 0; i < GetSize(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]) @@ -88,7 +91,7 @@ static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const 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 (GetSize(complete_in_state_cache) < num_states) { if (or_sig.size() == 1) { @@ -221,9 +224,12 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) } } + if (encoding_is_onehot) + state_wire->set_bool_attribute("\\onehot"); + // generate next_state signal - if (SIZE(fsm_data.state_table) == 1) + if (GetSize(fsm_data.state_table) == 1) { module->connect(next_state_wire, fsm_data.state_table.front()); } @@ -345,3 +351,4 @@ struct FsmMapPass : public Pass { } } FsmMapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index a0e1885e..4b93d79f 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -25,6 +25,9 @@ #include "fsmdata.h" #include <string.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct FsmOpt { FsmData fsm_data; @@ -40,7 +43,7 @@ struct FsmOpt 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++) + for (int i = 0; i < GetSize(fsm_data.state_table); i++) if (i != fsm_data.reset_state) unreachable_states.insert(i); @@ -50,12 +53,12 @@ struct FsmOpt if (unreachable_states.empty()) break; - for (int i = 0; i < SIZE(fsm_data.state_table); i++) { + for (int i = 0; i < GetSize(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); + old_to_new_state[i] = GetSize(new_state_table); new_state_table.push_back(fsm_data.state_table[i]); } @@ -309,11 +312,15 @@ struct FsmOpt } }; -void FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module) +PRIVATE_NAMESPACE_END + +void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module) { FsmOpt fsmopt(cell, module); } +PRIVATE_NAMESPACE_BEGIN + struct FsmOptPass : public Pass { FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { } virtual void help() @@ -335,9 +342,10 @@ struct FsmOptPass : public Pass { for (auto &mod_it : design->modules_) { if (design->selected(mod_it.second)) for (auto &cell_it : mod_it.second->cells_) - if (cell_it.second->type == "$fsm" and design->selected(mod_it.second, cell_it.second)) + if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) FsmData::optimize_fsm(cell_it.second, mod_it.second); } } } FsmOptPass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index 873ee7a1..16996810 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -27,6 +27,9 @@ #include <string.h> #include <errno.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &fsm_data, const char *prefix, FILE *f) { std::string name = cell->parameters["\\NAME"].decode_string(); @@ -38,9 +41,9 @@ static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData & prefix, RTLIL::unescape_id(module->name).c_str()); fprintf(f, "set_fsm_encoding {"); - for (size_t i = 0; i < fsm_data.state_table.size(); i++) { - fprintf(f, " s%zd=2#", i); - for (int j = int(fsm_data.state_table[i].bits.size())-1; j >= 0; j--) + for (int i = 0; i < GetSize(fsm_data.state_table); i++) { + fprintf(f, " s%d=2#", i); + for (int j = GetSize(fsm_data.state_table[i].bits)-1; j >= 0; j--) fprintf(f, "%c", fsm_data.state_table[i].bits[j] == RTLIL::State::S1 ? '1' : '0'); } fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n", @@ -48,7 +51,7 @@ static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData & prefix, RTLIL::unescape_id(module->name).c_str()); } -static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file, std::string default_encoding) +static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file, FILE *encfile, std::string default_encoding) { std::string encoding = cell->attributes.count("\\fsm_encoding") ? cell->attributes.at("\\fsm_encoding").decode_string() : "auto"; @@ -74,7 +77,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs if (!default_encoding.empty()) encoding = default_encoding; else - encoding = SIZE(fsm_data.state_table) < 32 ? "one-hot" : "binary"; + encoding = GetSize(fsm_data.state_table) < 32 ? "one-hot" : "binary"; log(" mapping auto encoding to `%s` for this FSM.\n", encoding.c_str()); } @@ -91,6 +94,9 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs } else log_error("FSM encoding `%s' is not supported!\n", encoding.c_str()); + if (encfile) + fprintf(encfile, ".fsm %s %s\n", log_id(module), RTLIL::unescape_id(cell->parameters["\\NAME"].decode_string()).c_str()); + int state_idx_counter = fsm_data.reset_state >= 0 ? 1 : 0; for (int i = 0; i < int(fsm_data.state_table.size()); i++) { @@ -107,6 +113,8 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs log_abort(); log(" %s -> %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str()); + if (encfile) + fprintf(encfile, ".map %s %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str()); fsm_data.state_table[i] = new_code; } @@ -122,21 +130,31 @@ struct FsmRecodePass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" fsm_recode [-encoding type] [-fm_set_fsm_file file] [selection]\n"); + log(" fsm_recode [options] [selection]\n"); log("\n"); log("This pass reassign the state encodings for FSM cells. At the moment only\n"); - log("one-hot encoding and binary encoding is supported. The option -encoding\n"); - log("can be used to specify the encoding scheme used for FSMs without the\n"); - log("`fsm_encoding' attribute (or with the attribute set to `auto'.\n"); + log("one-hot encoding and binary encoding is supported.\n"); + + log(" -encoding <type>\n"); + log(" specify the encoding scheme used for FSMs without the\n"); + log(" 'fsm_encoding' attribute or with the attribute set to `auto'.\n"); + log("\n"); + log(" -fm_set_fsm_file <file>\n"); + log(" generate a file containing the mapping from old to new FSM encoding\n"); + log(" in form of Synopsys Formality set_fsm_* commands.\n"); log("\n"); - log("The option -fm_set_fsm_file can be used to generate a file containing the\n"); - log("mapping from old to new FSM encoding in form of Synopsys Formality set_fsm_*\n"); - log("commands.\n"); + log(" -encfile <file>\n"); + log(" write the mappings from old to new FSM encoding to a file in the\n"); + log(" following format:\n"); + log("\n"); + log(" .fsm <module_name> <state_signal>\n"); + log(" .map <old_bitpattern> <new_bitpattern>\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { FILE *fm_set_fsm_file = NULL; + FILE *encfile = NULL; std::string default_encoding; log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); @@ -149,7 +167,13 @@ struct FsmRecodePass : public Pass { log_error("Can't open fm_set_fsm_file `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno)); continue; } - if (arg == "-encoding" && argidx+1 < args.size() && fm_set_fsm_file == NULL) { + if (arg == "-encfile" && argidx+1 < args.size() && encfile == NULL) { + encfile = fopen(args[++argidx].c_str(), "w"); + if (encfile == NULL) + log_error("Can't open encfile `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno)); + continue; + } + if (arg == "-encoding" && argidx+1 < args.size() && default_encoding.empty()) { default_encoding = args[++argidx]; continue; } @@ -161,10 +185,13 @@ struct FsmRecodePass : public Pass { if (design->selected(mod_it.second)) 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); + fsm_recode(cell_it.second, mod_it.second, fm_set_fsm_file, encfile, default_encoding); if (fm_set_fsm_file != NULL) fclose(fm_set_fsm_file); + if (encfile != NULL) + fclose(encfile); } } FsmRecodePass; +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 7a44dd45..5671d000 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -20,8 +20,9 @@ #ifndef FSMDATA_H #define FSMDATA_H -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN struct FsmData { @@ -142,24 +143,24 @@ struct FsmData log("\n"); log(" Input signals:\n"); RTLIL::SigSpec sig_in = cell->getPort("\\CTRL_IN"); - for (int i = 0; i < SIZE(sig_in); i++) + for (int i = 0; i < GetSize(sig_in); i++) log(" %3d: %s\n", i, log_signal(sig_in[i])); log("\n"); log(" Output signals:\n"); RTLIL::SigSpec sig_out = cell->getPort("\\CTRL_OUT"); - for (int i = 0; i < SIZE(sig_out); i++) + for (int i = 0; i < GetSize(sig_out); i++) log(" %3d: %s\n", i, log_signal(sig_out[i])); log("\n"); log(" State encoding:\n"); - for (int i = 0; i < SIZE(state_table); i++) + for (int i = 0; i < GetSize(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 (int i = 0; i < SIZE(transition_table); i++) { + for (int i = 0; i < GetSize(transition_table); i++) { transition_t &tr = transition_table[i]; log(" %5d: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out)); } @@ -172,4 +173,6 @@ struct FsmData static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module); }; +YOSYS_NAMESPACE_END + #endif diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 14bf8d1b..a393343d 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -17,23 +17,26 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" #include <stdlib.h> #include <stdio.h> -#include <fnmatch.h> #include <set> -#include <unistd.h> - -namespace { - struct generate_port_decl_t { - bool input, output; - 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) +#ifndef _WIN32 +# include <unistd.h> +#endif + + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct generate_port_decl_t { + bool input, output; + string portname; + int index; +}; + +void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, const std::vector<generate_port_decl_t> &portdecls) { std::set<RTLIL::IdString> found_celltypes; @@ -46,7 +49,7 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell 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)) + if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str())) found_celltypes.insert(cell->type); } @@ -96,9 +99,9 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell while (portnames.size() > 0) { 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)) { + if (decl.index == 0 && patmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str())) { generate_port_decl_t d = decl; - d.portname = portname; + d.portname = portname.str(); d.index = *indices.begin(); log_assert(!indices.empty()); indices.erase(d.index); @@ -135,7 +138,7 @@ 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 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; @@ -171,7 +174,7 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla for (auto &dir : libdirs) { filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".v"; - if (access(filename.c_str(), F_OK) == 0) { + if (check_file_exists(filename)) { std::vector<std::string> args; args.push_back(filename); Frontend::frontend_call(design, NULL, filename, "verilog"); @@ -179,7 +182,7 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla } filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".il"; - if (access(filename.c_str(), F_OK) == 0) { + if (check_file_exists(filename)) { std::vector<std::string> args; args.push_back(filename); Frontend::frontend_call(design, NULL, filename, "ilang"); @@ -196,6 +199,19 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla 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; + } else + if (flag_check) + { + RTLIL::Module *mod = design->module(cell->type); + 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); + if (id <= 0 || id > GetSize(mod->ports)) + log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d ports, requested port %d.\n", + log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->ports), id); + } else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0) + log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n", + log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first)); } if (cell->parameters.size() == 0) @@ -245,24 +261,31 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla return did_something; } -static void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent) +void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent) { if (used.count(mod) > 0) return; if (indent == 0) log("Top module: %s\n", mod->name.c_str()); - else + else if (!mod->get_bool_attribute("\\blackbox")) 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 cell : mod->cells()) { + std::string celltype = cell->type.str(); + if (celltype.substr(0, 7) == "$array:") { + int pos_idx = celltype.find_first_of(':'); + int pos_num = celltype.find_first_of(':', pos_idx + 1); + int pos_type = celltype.find_first_of(':', pos_num + 1); + celltype = celltype.substr(pos_type + 1); + } + if (design->module(celltype)) + hierarchy_worker(design, used, design->module(celltype), indent+4); } } -static void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool first_pass) +void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) { std::set<RTLIL::Module*> used; hierarchy_worker(design, used, top, 0); @@ -272,17 +295,41 @@ static void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, if (used.count(it.second) == 0) del_modules.push_back(it.second); + int del_counter = 0; for (auto mod : del_modules) { - if (first_pass && mod->name.substr(0, 9) == "$abstract") + if (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); + del_counter++; delete mod; } - log("Removed %zd unused modules.\n", del_modules.size()); + log("Removed %d unused modules.\n", del_counter); +} + +bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) +{ + if (cache.count(mod) == 0) + for (auto c : mod->cells()) { + RTLIL::Module *m = mod->design->module(c->type); + if ((m != nullptr && set_keep_assert(cache, m)) || c->type == "$assert") + return cache[mod] = true; + } + return cache[mod]; +} + +int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) +{ + if (db.count(module) == 0) { + db[module] = 0; + for (auto cell : module->cells()) + if (design->module(cell->type)) + db[module] = std::max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); + } + return db.at(module); } struct HierarchyPass : public Pass { @@ -305,7 +352,7 @@ struct HierarchyPass : public Pass { log("\n"); log(" -purge_lib\n"); log(" by default the hierarchy command will not remove library (blackbox)\n"); - log(" module. use this options to also remove unused blackbox modules.\n"); + log(" modules. use this option to also remove unused blackbox modules.\n"); log("\n"); log(" -libdir <directory>\n"); log(" search for files named <module_name>.v in the specified directory\n"); @@ -316,6 +363,11 @@ struct HierarchyPass : public Pass { log(" per default this pass also converts positional arguments in cells\n"); log(" to arguments using port names. this option disables this behavior.\n"); log("\n"); + log(" -nokeep_asserts\n"); + log(" per default this pass sets the \"keep\" attribute on all modules\n"); + log(" that directly or indirectly contain one or more $assert cells. this\n"); + log(" option disables this behavior.\n"); + log("\n"); log(" -top <module>\n"); log(" use the specified top module to built a design hierarchy. modules\n"); log(" outside this tree (unused modules) are removed.\n"); @@ -324,6 +376,9 @@ struct HierarchyPass : public Pass { log(" specified top module. otherwise a module with the 'top' attribute set\n"); log(" will implicitly be used as top module, if such a module exists.\n"); log("\n"); + log(" -auto-top\n"); + log(" automatically determine the top of the design hierarchy and mark it.\n"); + log("\n"); log("In -generate mode this pass generates blackbox modules for the given cell\n"); log("types (wildcards supported). For this the design is searched for cells that\n"); log("match the given types and then the given port declarations are used to\n"); @@ -350,8 +405,10 @@ struct HierarchyPass : public Pass { RTLIL::Module *top_mod = NULL; std::vector<std::string> libdirs; + bool auto_top_mode = false; bool generate_mode = false; bool keep_positionals = false; + bool nokeep_asserts = false; std::vector<std::string> generate_cells; std::vector<generate_port_decl_t> generate_ports; @@ -409,6 +466,10 @@ struct HierarchyPass : public Pass { keep_positionals = true; continue; } + if (args[argidx] == "-nokeep_asserts") { + nokeep_asserts = true; + continue; + } if (args[argidx] == "-libdir" && argidx+1 < args.size()) { libdirs.push_back(args[++argidx]); continue; @@ -418,7 +479,7 @@ struct HierarchyPass : public Pass { log_cmd_error("Option -top requires an additional argument!\n"); 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; + dict<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; } @@ -426,6 +487,10 @@ struct HierarchyPass : public Pass { log_cmd_error("Module `%s' not found!\n", args[argidx].c_str()); continue; } + if (args[argidx] == "-auto-top") { + auto_top_mode = true; + continue; + } break; } extra_args(args, argidx, design, false); @@ -437,35 +502,47 @@ struct HierarchyPass : public Pass { log_push(); - if (top_mod == NULL) + if (top_mod == nullptr) 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, true); + if (top_mod == nullptr && auto_top_mode) { + log_header("Finding top of design hierarchy..\n"); + dict<Module*, int> db; + for (Module *mod : design->modules()) { + int score = find_top_mod_score(design, mod, db); + log("root of %3d design levels: %-20s\n", score, log_id(mod)); + if (!top_mod || score > db[top_mod]) + top_mod = mod; + } + if (top_mod != nullptr) + log("Automatically selected %s as design top module.\n", log_id(top_mod)); + } bool did_something = true; - bool did_something_once = false; - while (did_something) { + while (did_something) + { did_something = false; - 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) - continue; - if (expand_module(design, design->modules_[modname], flag_check, libdirs)) + + std::set<RTLIL::Module*> used_modules; + if (top_mod != NULL) { + log_header("Analyzing design hierarchy..\n"); + hierarchy_worker(design, used_modules, top_mod, 0); + } else { + for (auto mod : design->modules()) + used_modules.insert(mod); + } + + for (auto module : used_modules) { + if (expand_module(design, module, flag_check, libdirs)) did_something = true; } - if (did_something) - did_something_once = true; } - if (top_mod != NULL && did_something_once) { - log_header("Re-running hierarchy analysis..\n"); - hierarchy(design, top_mod, purge_lib, false); + if (top_mod != NULL) { + log_header("Analyzing design hierarchy..\n"); + hierarchy_clean(design, top_mod, purge_lib); } if (top_mod != NULL) { @@ -476,6 +553,15 @@ struct HierarchyPass : public Pass { mod_it.second->attributes.erase("\\top"); } + if (!nokeep_asserts) { + std::map<RTLIL::Module*, bool> cache; + for (auto mod : design->modules()) + if (set_keep_assert(cache, mod)) { + log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod)); + mod->set_bool_attribute("\\keep"); + } + } + if (!keep_positionals) { std::set<RTLIL::Module*> pos_mods; @@ -507,7 +593,7 @@ struct HierarchyPass : public Pass { RTLIL::Cell *cell = work.second; 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; + dict<RTLIL::IdString, RTLIL::SigSpec> new_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); @@ -528,3 +614,4 @@ struct HierarchyPass : public Pass { } } HierarchyPass; +PRIVATE_NAMESPACE_END diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 1b03ab55..8d4012c5 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -24,6 +24,9 @@ #include <stdio.h> #include <set> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct SubmodWorker { CellTypes ct; @@ -82,7 +85,7 @@ struct SubmodWorker 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()); + 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()) flag_signal(conn.second, true, true, true, false, false); } @@ -99,7 +102,7 @@ struct SubmodWorker 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()); + log_warning("Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str()); } } @@ -347,3 +350,4 @@ struct SubmodPass : public Pass { } } SubmodPass; +PRIVATE_NAMESPACE_END diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc index 026c5ff8..aeff225d 100644 --- a/passes/memory/Makefile.inc +++ b/passes/memory/Makefile.inc @@ -4,5 +4,6 @@ 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_bram.o OBJS += passes/memory/memory_map.o diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index fc309553..866efae7 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -22,13 +22,16 @@ #include <stdlib.h> #include <stdio.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct MemoryPass : public Pass { MemoryPass() : Pass("memory", "translate memories to basic cells") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory [-nomap] [selection]\n"); + log(" memory [-nomap] [-bram <bram_rules>] [selection]\n"); log("\n"); log("This pass calls all the other memory_* passes in a useful order:\n"); log("\n"); @@ -37,7 +40,8 @@ struct MemoryPass : public Pass { log(" memory_share\n"); log(" opt_clean\n"); log(" memory_collect\n"); - log(" memory_map (skipped if called with -nomap)\n"); + log(" memory_bram -rules <bram_rules> (when called with -bram)\n"); + log(" memory_map (skipped if called with -nomap)\n"); log("\n"); log("This converts memories to word-wide DFFs and address decoders\n"); log("or multiport memory blocks if called with the -nomap option.\n"); @@ -46,6 +50,7 @@ struct MemoryPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool flag_nomap = false; + string memory_bram_opts; log_header("Executing MEMORY pass.\n"); log_push(); @@ -56,6 +61,10 @@ struct MemoryPass : public Pass { flag_nomap = true; continue; } + if (argidx+1 < args.size() && args[argidx] == "-bram") { + memory_bram_opts += " -rules " + args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); @@ -66,6 +75,9 @@ struct MemoryPass : public Pass { Pass::call(design, "opt_clean"); Pass::call(design, "memory_collect"); + if (!memory_bram_opts.empty()) + Pass::call(design, "memory_bram" + memory_bram_opts); + if (!flag_nomap) Pass::call(design, "memory_map"); @@ -73,3 +85,4 @@ struct MemoryPass : public Pass { } } MemoryPass; +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc new file mode 100644 index 00000000..958cc88b --- /dev/null +++ b/passes/memory/memory_bram.cc @@ -0,0 +1,1174 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct rules_t +{ + struct portinfo_t { + int group, index, dupidx; + int wrmode, enable, transp, clocks, clkpol; + + SigBit sig_clock; + SigSpec sig_addr, sig_data, sig_en; + bool effective_clkpol; + bool make_transp; + int mapped_port; + }; + + struct bram_t { + IdString name; + int variant; + + int groups, abits, dbits, init; + vector<int> ports, wrmode, enable, transp, clocks, clkpol; + + void dump_config() const + { + log(" bram %s # variant %d\n", log_id(name), variant); + log(" init %d\n", init); + log(" abits %d\n", abits); + log(" dbits %d\n", dbits); + log(" groups %d\n", groups); + + log(" ports "); for (int v : ports) log("%4d", v); log("\n"); + log(" wrmode"); for (int v : wrmode) log("%4d", v); log("\n"); + log(" enable"); for (int v : enable) log("%4d", v); log("\n"); + log(" transp"); for (int v : transp) log("%4d", v); log("\n"); + log(" clocks"); for (int v : clocks) log("%4d", v); log("\n"); + log(" clkpol"); for (int v : clkpol) log("%4d", v); log("\n"); + log(" endbram\n"); + } + + void check_vectors() const + { + if (groups != GetSize(ports)) log_error("Bram %s variant %d has %d groups but only %d entries in 'ports'.\n", log_id(name), variant, groups, GetSize(ports)); + if (groups != GetSize(wrmode)) log_error("Bram %s variant %d has %d groups but only %d entries in 'wrmode'.\n", log_id(name), variant, groups, GetSize(wrmode)); + if (groups != GetSize(enable)) log_error("Bram %s variant %d has %d groups but only %d entries in 'enable'.\n", log_id(name), variant, groups, GetSize(enable)); + if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp)); + if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks)); + if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol)); + } + + vector<portinfo_t> make_portinfos() const + { + vector<portinfo_t> portinfos; + for (int i = 0; i < groups; i++) + for (int j = 0; j < ports[i]; j++) { + portinfo_t pi; + pi.group = i; + pi.index = j; + pi.dupidx = 0; + pi.wrmode = wrmode[i]; + pi.enable = enable[i]; + pi.transp = transp[i]; + pi.clocks = clocks[i]; + pi.clkpol = clkpol[i]; + pi.mapped_port = -1; + pi.make_transp = false; + pi.effective_clkpol = false; + portinfos.push_back(pi); + } + return portinfos; + } + + void find_variant_params(dict<IdString, Const> &variant_params, const bram_t &other) const + { + log_assert(name == other.name); + + if (groups != other.groups) + log_error("Bram %s variants %d and %d have different values for 'groups'.\n", log_id(name), variant, other.variant); + + if (abits != other.abits) + variant_params["\\CFG_ABITS"] = abits; + if (dbits != other.dbits) + variant_params["\\CFG_DBITS"] = dbits; + if (init != other.init) + variant_params["\\CFG_INIT"] = init; + + for (int i = 0; i < groups; i++) + { + if (ports[i] != other.ports[i]) + log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i); + if (wrmode[i] != other.wrmode[i]) + variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1]; + if (enable[i] != other.enable[i]) + variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1]; + if (transp[i] != other.transp[i]) + variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1]; + if (clocks[i] != other.clocks[i]) + variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1]; + if (clkpol[i] != other.clkpol[i]) + variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1]; + } + } + }; + + struct match_t { + IdString name; + dict<string, int> min_limits, max_limits; + bool or_next_if_better, make_transp; + char shuffle_enable; + }; + + dict<IdString, vector<bram_t>> brams; + vector<match_t> matches; + + std::ifstream infile; + vector<string> tokens; + vector<string> labels; + int linecount; + + void syntax_error() + { + if (tokens.empty()) + log_error("Unexpected end of rules file in line %d.\n", linecount); + log_error("Syntax error in rules file line %d.\n", linecount); + } + + bool next_line() + { + string line; + while (std::getline(infile, line)) { + tokens.clear(); + labels.clear(); + linecount++; + for (string tok = next_token(line); !tok.empty(); tok = next_token(line)) { + if (tok[0] == '@') { + labels.push_back(tok.substr(1)); + continue; + } + if (tok[0] == '#') + break; + tokens.push_back(tok); + } + if (!tokens.empty()) + return true; + } + return false; + } + + bool parse_single_int(const char *stmt, int &value) + { + if (GetSize(tokens) == 2 && tokens[0] == stmt) { + value = atoi(tokens[1].c_str()); + return true; + } + return false; + } + + bool parse_int_vect(const char *stmt, vector<int> &value) + { + if (GetSize(tokens) >= 2 && tokens[0] == stmt) { + value.resize(GetSize(tokens)-1); + for (int i = 1; i < GetSize(tokens); i++) + value[i-1] = atoi(tokens[i].c_str()); + return true; + } + return false; + } + + void parse_bram() + { + IdString bram_name = RTLIL::escape_id(tokens[1]); + + if (GetSize(tokens) != 2) + syntax_error(); + + vector<vector<string>> lines_nolabels; + std::map<string, vector<vector<string>>> lines_labels; + + while (next_line()) + { + if (GetSize(tokens) == 1 && tokens[0] == "endbram") + break; + if (labels.empty()) + lines_nolabels.push_back(tokens); + for (auto lab : labels) + lines_labels[lab].push_back(tokens); + } + + std::map<string, vector<vector<string>>> variant_lines; + + if (lines_labels.empty()) + variant_lines[""] = lines_nolabels; + for (auto &it : lines_labels) { + variant_lines[it.first] = lines_nolabels; + variant_lines[it.first].insert(variant_lines[it.first].end(), it.second.begin(), it.second.end()); + } + + for (auto &it : variant_lines) + { + bram_t data; + data.name = bram_name; + data.variant = GetSize(brams[data.name]) + 1; + data.groups = 0; + data.abits = 0; + data.dbits = 0; + data.init = 0; + + for (auto &line_tokens : it.second) + { + tokens = line_tokens; + + if (parse_single_int("groups", data.groups)) + continue; + + if (parse_single_int("abits", data.abits)) + continue; + + if (parse_single_int("dbits", data.dbits)) + continue; + + if (parse_single_int("init", data.init)) + continue; + + if (parse_int_vect("ports", data.ports)) + continue; + + if (parse_int_vect("wrmode", data.wrmode)) + continue; + + if (parse_int_vect("enable", data.enable)) + continue; + + if (parse_int_vect("transp", data.transp)) + continue; + + if (parse_int_vect("clocks", data.clocks)) + continue; + + if (parse_int_vect("clkpol", data.clkpol)) + continue; + + syntax_error(); + } + + data.check_vectors(); + brams[data.name].push_back(data); + } + } + + void parse_match() + { + if (GetSize(tokens) != 2) + syntax_error(); + + match_t data; + data.name = RTLIL::escape_id(tokens[1]); + data.or_next_if_better = false; + data.make_transp = false; + data.shuffle_enable = 0; + + while (next_line()) + { + if (!labels.empty()) + syntax_error(); + + if (GetSize(tokens) == 1 && tokens[0] == "endmatch") { + matches.push_back(data); + break; + } + + if (GetSize(tokens) == 3 && tokens[0] == "min") { + data.min_limits[tokens[1]] = atoi(tokens[2].c_str()); + continue; + } + + if (GetSize(tokens) == 3 && tokens[0] == "max") { + data.max_limits[tokens[1]] = atoi(tokens[2].c_str()); + continue; + } + + if (GetSize(tokens) == 2 && tokens[0] == "shuffle_enable" && GetSize(tokens[1]) == 1 && 'A' <= tokens[1][0] && tokens[1][0] <= 'Z') { + data.shuffle_enable = tokens[1][0]; + continue; + } + + if (GetSize(tokens) == 1 && tokens[0] == "make_transp") { + data.make_transp = true; + continue; + } + + if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") { + data.or_next_if_better = true; + continue; + } + + syntax_error(); + } + } + + void parse(string filename) + { + if (filename.substr(0, 2) == "+/") + filename = proc_share_dirname() + filename.substr(1); + + infile.open(filename); + linecount = 0; + + if (infile.fail()) + log_error("Can't open rules file `%s'.\n", filename.c_str()); + + while (next_line()) + { + if (!labels.empty()) + syntax_error(); + + if (tokens[0] == "bram") { + parse_bram(); + continue; + } + + if (tokens[0] == "match") { + parse_match(); + continue; + } + + syntax_error(); + } + + infile.close(); + } +}; + +bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode) +{ + Module *module = cell->module; + + auto portinfos = bram.make_portinfos(); + int dup_count = 1; + + pair<SigBit, bool> make_transp_clk; + bool enable_make_transp = false; + int make_transp_enbits = 0; + + dict<int, pair<SigBit, bool>> clock_domains; + dict<int, bool> clock_polarities; + dict<int, bool> read_transp; + pool<int> clocks_wr_ports; + pool<int> clkpol_wr_ports; + int clocks_max = 0; + int clkpol_max = 0; + int transp_max = 0; + + clock_polarities[0] = false; + clock_polarities[1] = true; + + for (auto &pi : portinfos) { + if (pi.wrmode) { + clocks_wr_ports.insert(pi.clocks); + if (pi.clkpol > 1) + clkpol_wr_ports.insert(pi.clkpol); + } + clocks_max = std::max(clocks_max, pi.clocks); + clkpol_max = std::max(clkpol_max, pi.clkpol); + transp_max = std::max(transp_max, pi.transp); + } + + log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant); + // bram.dump_config(); + + int mem_size = cell->getParam("\\SIZE").as_int(); + int mem_abits = cell->getParam("\\ABITS").as_int(); + int mem_width = cell->getParam("\\WIDTH").as_int(); + // int mem_offset = cell->getParam("\\OFFSET").as_int(); + + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE")); + auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY")); + wr_clken.extend_u0(wr_ports); + wr_clkpol.extend_u0(wr_ports); + + SigSpec wr_en = cell->getPort("\\WR_EN"); + SigSpec wr_clk = cell->getPort("\\WR_CLK"); + SigSpec wr_data = cell->getPort("\\WR_DATA"); + SigSpec wr_addr = cell->getPort("\\WR_ADDR"); + + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + auto rd_clken = SigSpec(cell->getParam("\\RD_CLK_ENABLE")); + auto rd_clkpol = SigSpec(cell->getParam("\\RD_CLK_POLARITY")); + auto rd_transp = SigSpec(cell->getParam("\\RD_TRANSPARENT")); + rd_clken.extend_u0(rd_ports); + rd_clkpol.extend_u0(rd_ports); + rd_transp.extend_u0(rd_ports); + + SigSpec rd_clk = cell->getPort("\\RD_CLK"); + SigSpec rd_data = cell->getPort("\\RD_DATA"); + SigSpec rd_addr = cell->getPort("\\RD_ADDR"); + + if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0) + { + int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable; + log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size); + + // extract unshuffled data/enable bits + + std::vector<SigSpec> old_wr_en; + std::vector<SigSpec> old_wr_data; + std::vector<SigSpec> old_rd_data; + + for (int i = 0; i < wr_ports; i++) { + old_wr_en.push_back(wr_en.extract(i*mem_width, mem_width)); + old_wr_data.push_back(wr_data.extract(i*mem_width, mem_width)); + } + + for (int i = 0; i < rd_ports; i++) + old_rd_data.push_back(rd_data.extract(i*mem_width, mem_width)); + + // analyze enable structure + + std::vector<SigSpec> en_order; + dict<SigSpec, vector<int>> bits_wr_en; + + for (int i = 0; i < mem_width; i++) { + SigSpec sig; + for (int j = 0; j < wr_ports; j++) + sig.append(old_wr_en[j][i]); + if (bits_wr_en.count(sig) == 0) + en_order.push_back(sig); + bits_wr_en[sig].push_back(i); + } + + // re-create memory ports + + std::vector<SigSpec> new_wr_en(GetSize(old_wr_en)); + std::vector<SigSpec> new_wr_data(GetSize(old_wr_data)); + std::vector<SigSpec> new_rd_data(GetSize(old_rd_data)); + std::vector<int> shuffle_map; + + for (auto &it : en_order) + { + auto &bits = bits_wr_en.at(it); + int buckets = (GetSize(bits) + bucket_size - 1) / bucket_size; + int fillbits = buckets*bucket_size - GetSize(bits); + SigBit fillbit; + + for (int i = 0; i < GetSize(bits); i++) { + for (int j = 0; j < wr_ports; j++) { + new_wr_en[j].append(old_wr_en[j][bits[i]]); + new_wr_data[j].append(old_wr_data[j][bits[i]]); + fillbit = old_wr_en[j][bits[i]]; + } + for (int j = 0; j < rd_ports; j++) + new_rd_data[j].append(old_rd_data[j][bits[i]]); + shuffle_map.push_back(bits[i]); + } + + for (int i = 0; i < fillbits; i++) { + for (int j = 0; j < wr_ports; j++) { + new_wr_en[j].append(fillbit); + new_wr_data[j].append(State::S0); + } + for (int j = 0; j < rd_ports; j++) + new_rd_data[j].append(State::Sx); + shuffle_map.push_back(-1); + } + } + + log(" Results of bit order shuffling:"); + for (int v : shuffle_map) + log(" %d", v); + log("\n"); + + // update mem_*, wr_*, and rd_* variables + + mem_width = GetSize(new_wr_en.front()); + wr_en = SigSpec(0, wr_ports * mem_width); + wr_data = SigSpec(0, wr_ports * mem_width); + rd_data = SigSpec(0, rd_ports * mem_width); + + for (int i = 0; i < wr_ports; i++) { + wr_en.replace(i*mem_width, new_wr_en[i]); + wr_data.replace(i*mem_width, new_wr_data[i]); + } + + for (int i = 0; i < rd_ports; i++) + rd_data.replace(i*mem_width, new_rd_data[i]); + } + + // assign write ports + + for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++) + { + bool clken = wr_clken[cell_port_i] == State::S1; + bool clkpol = wr_clkpol[cell_port_i] == State::S1; + SigBit clksig = wr_clk[cell_port_i]; + + pair<SigBit, bool> clkdom(clksig, clkpol); + if (!clken) + clkdom = pair<SigBit, bool>(State::S1, false); + + log(" Write port #%d is in clock domain %s%s.\n", + cell_port_i, clkdom.second ? "" : "!", + clken ? log_signal(clkdom.first) : "~async~"); + + for (; bram_port_i < GetSize(portinfos); bram_port_i++) + { + auto &pi = portinfos[bram_port_i]; + make_transp_enbits = pi.enable; + make_transp_clk = clkdom; + + if (pi.wrmode != 1) + skip_bram_wport: + continue; + + if (clken) { + if (pi.clocks == 0) { + log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1); + goto skip_bram_wport; + } + if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) { + log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1); + goto skip_bram_wport; + } + if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) { + log(" Bram port %c%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1); + goto skip_bram_wport; + } + } else { + if (pi.clocks != 0) { + log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1); + goto skip_bram_wport; + } + } + + SigSpec sig_en; + SigBit last_en_bit = State::S1; + for (int i = 0; i < mem_width; i++) { + if (pi.enable && i % (bram.dbits / pi.enable) == 0) { + last_en_bit = wr_en[i + cell_port_i*mem_width]; + sig_en.append(last_en_bit); + } + if (last_en_bit != wr_en[i + cell_port_i*mem_width]) { + log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1); + goto skip_bram_wport; + } + } + + log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1); + pi.mapped_port = cell_port_i; + + if (clken) { + clock_domains[pi.clocks] = clkdom; + clock_polarities[pi.clkpol] = clkdom.second; + pi.sig_clock = clkdom.first; + pi.effective_clkpol = clkdom.second; + } + + pi.sig_en = sig_en; + pi.sig_addr = wr_addr.extract(cell_port_i*mem_abits, mem_abits); + pi.sig_data = wr_data.extract(cell_port_i*mem_width, mem_width); + + bram_port_i++; + goto mapped_wr_port; + } + + log(" Failed to map write port #%d.\n", cell_port_i); + return false; + mapped_wr_port:; + } + + // houskeeping stuff for growing more read ports and restarting read port assignments + + int grow_read_ports_cursor = -1; + bool try_growing_more_read_ports = false; + auto backup_clock_domains = clock_domains; + auto backup_clock_polarities = clock_polarities; + + if (0) { +grow_read_ports:; + vector<rules_t::portinfo_t> new_portinfos; + for (auto &pi : portinfos) { + if (pi.wrmode == 0) { + pi.mapped_port = -1; + pi.sig_clock = SigBit(); + pi.sig_addr = SigSpec(); + pi.sig_data = SigSpec(); + pi.sig_en = SigSpec(); + } + new_portinfos.push_back(pi); + if (pi.dupidx == dup_count-1) { + if (pi.clocks && !clocks_wr_ports[pi.clocks]) + pi.clocks += clocks_max; + if (pi.clkpol > 1 && !clkpol_wr_ports[pi.clkpol]) + pi.clkpol += clkpol_max; + if (pi.transp > 1) + pi.transp += transp_max; + pi.dupidx++; + new_portinfos.push_back(pi); + } + } + try_growing_more_read_ports = false; + portinfos.swap(new_portinfos); + clock_domains = backup_clock_domains; + clock_polarities = backup_clock_polarities; + dup_count++; + } + + read_transp.clear(); + read_transp[0] = false; + read_transp[1] = true; + + // assign read ports + + for (int cell_port_i = 0; cell_port_i < rd_ports; cell_port_i++) + { + bool clken = rd_clken[cell_port_i] == State::S1; + bool clkpol = rd_clkpol[cell_port_i] == State::S1; + bool transp = rd_transp[cell_port_i] == State::S1; + SigBit clksig = rd_clk[cell_port_i]; + + pair<SigBit, bool> clkdom(clksig, clkpol); + if (!clken) + clkdom = pair<SigBit, bool>(State::S1, false); + + log(" Read port #%d is in clock domain %s%s.\n", + cell_port_i, clkdom.second ? "" : "!", + clken ? log_signal(clkdom.first) : "~async~"); + + for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++) + { + auto &pi = portinfos[bram_port_i]; + + if (pi.wrmode != 0 || pi.mapped_port >= 0) + skip_bram_rport: + continue; + + if (clken) { + if (pi.clocks == 0) { + log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) { + log(" Bram port %c%d.%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) { + log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) { + if (match.make_transp && wr_ports <= 1) { + pi.make_transp = true; + enable_make_transp = true; + } else { + log(" Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + } + } else { + if (pi.clocks != 0) { + log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + } + + log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + pi.mapped_port = cell_port_i; + + if (clken) { + clock_domains[pi.clocks] = clkdom; + clock_polarities[pi.clkpol] = clkdom.second; + read_transp[pi.transp] = transp; + pi.sig_clock = clkdom.first; + pi.effective_clkpol = clkdom.second; + } + + pi.sig_addr = rd_addr.extract(cell_port_i*mem_abits, mem_abits); + pi.sig_data = rd_data.extract(cell_port_i*mem_width, mem_width); + + if (grow_read_ports_cursor < cell_port_i) { + grow_read_ports_cursor = cell_port_i; + try_growing_more_read_ports = true; + } + + goto mapped_rd_port; + } + + log(" Failed to map read port #%d.\n", cell_port_i); + if (try_growing_more_read_ports) { + log(" Growing more read ports by duplicating bram cells.\n"); + goto grow_read_ports; + } + return false; + mapped_rd_port:; + } + + // update properties and re-check conditions + + if (mode <= 1) + { + match_properties["dups"] = dup_count; + match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"]; + + int cells = ((mem_width + bram.dbits - 1) / bram.dbits) * ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits)); + match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits)); + + match_properties["dcells"] = ((mem_width + bram.dbits - 1) / bram.dbits); + match_properties["acells"] = ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits)); + match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"]; + + log(" Updated properties: dups=%d waste=%d efficiency=%d\n", + match_properties["dups"], match_properties["waste"], match_properties["efficiency"]); + + for (auto it : match.min_limits) { + if (!match_properties.count(it.first)) + log_error("Unknown property '%s' in match rule for bram type %s.\n", + it.first.c_str(), log_id(match.name)); + if (match_properties[it.first] >= it.second) + continue; + log(" Rule for bram type %s rejected: requirement 'min %s %d' not met.\n", + log_id(match.name), it.first.c_str(), it.second); + return false; + } + for (auto it : match.max_limits) { + if (!match_properties.count(it.first)) + log_error("Unknown property '%s' in match rule for bram type %s.\n", + it.first.c_str(), log_id(match.name)); + if (match_properties[it.first] <= it.second) + continue; + log(" Rule for bram type %s rejected: requirement 'max %s %d' not met.\n", + log_id(match.name), it.first.c_str(), it.second); + return false; + } + + if (mode == 1) + return true; + } + + // prepare variant parameters + + dict<IdString, Const> variant_params; + for (auto &other_bram : rules.brams.at(bram.name)) + bram.find_variant_params(variant_params, other_bram); + + // actually replace that memory cell + + dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache; + + for (int grid_d = 0; grid_d*bram.dbits < mem_width; grid_d++) + { + SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q; + vector<SigSpec> mktr_wren; + + if (enable_make_transp) { + mktr_wraddr = module->addWire(NEW_ID, bram.abits); + mktr_wrdata = module->addWire(NEW_ID, bram.dbits); + mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits); + module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second); + for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++) + mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits)); + } + + for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++) + for (int dupidx = 0; dupidx < dup_count; dupidx++) + { + Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", cell->name.c_str(), grid_d, grid_a, dupidx)), bram.name); + log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c)); + + for (auto &vp : variant_params) + c->setParam(vp.first, vp.second); + + for (auto &pi : portinfos) + { + if (pi.dupidx != dupidx) + continue; + + string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1); + const char *pf = prefix.c_str(); + + if (pi.clocks && (!c->hasPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1)) || pi.sig_clock.wire)) { + c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), pi.sig_clock); + if (pi.clkpol > 1 && pi.sig_clock.wire) + c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol)); + if (pi.transp > 1 && pi.sig_clock.wire) + c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp)); + } + + SigSpec addr_ok; + if (GetSize(pi.sig_addr) > bram.abits) { + SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits); + SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr)); + addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel); + } + + if (pi.enable) + { + SigSpec sig_en = pi.sig_en; + sig_en.extend_u0((grid_d+1) * pi.enable); + sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + + if (!addr_ok.empty()) + sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); + + c->setPort(stringf("\\%sEN", pf), sig_en); + + if (pi.wrmode == 1 && enable_make_transp) + module->connect(mktr_wren[grid_a], sig_en); + } + + SigSpec sig_addr = pi.sig_addr; + sig_addr.extend_u0(bram.abits); + c->setPort(stringf("\\%sADDR", pf), sig_addr); + + if (pi.wrmode == 1 && enable_make_transp && grid_a == 0) + module->connect(mktr_wraddr, sig_addr); + + SigSpec sig_data = pi.sig_data; + sig_data.extend_u0((grid_d+1) * bram.dbits); + sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits); + + if (pi.wrmode == 1) { + c->setPort(stringf("\\%sDATA", pf), sig_data); + if (enable_make_transp && grid_a == 0) + module->connect(mktr_wrdata, sig_data); + } else { + SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits); + c->setPort(stringf("\\%sDATA", pf), bram_dout); + + if (pi.make_transp) + { + log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + + SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits), + mktr_wren[grid_a], module->Eq(NEW_ID, mktr_wraddr, sig_addr)); + + SigSpec transp_en_q = module->addWire(NEW_ID, make_transp_enbits); + module->addDff(NEW_ID, make_transp_clk.first, transp_en_d, transp_en_q, make_transp_clk.second); + + for (int i = 0; i < make_transp_enbits; i++) { + int en_width = bram.dbits / make_transp_enbits; + SigSpec orig_bram_dout = bram_dout.extract(i * en_width, en_width); + SigSpec bypass_dout = mktr_wrdata_q.extract(i * en_width, en_width); + bram_dout.replace(i * en_width, module->Mux(NEW_ID, orig_bram_dout, bypass_dout, transp_en_q[i])); + } + } + + for (int i = bram.dbits-1; i >= 0; i--) + if (sig_data[i].wire == nullptr) { + sig_data.remove(i); + bram_dout.remove(i); + } + + SigSpec addr_ok_q = addr_ok; + if (pi.clocks && !addr_ok.empty()) { + addr_ok_q = module->addWire(NEW_ID); + module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol); + } + + dout_cache[sig_data].first.append(addr_ok_q); + dout_cache[sig_data].second.append(bram_dout); + } + } + } + } + + for (auto &it : dout_cache) + { + if (it.second.first.empty()) + { + log_assert(GetSize(it.first) == GetSize(it.second.second)); + module->connect(it.first, it.second.second); + } + else + { + log_assert(GetSize(it.first)*GetSize(it.second.first) == GetSize(it.second.second)); + module->addPmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), it.second.second, it.second.first, it.first); + } + } + + module->remove(cell); + return true; +} + +void handle_cell(Cell *cell, const rules_t &rules) +{ + log("Processing %s.%s:\n", log_id(cell->module), log_id(cell)); + + if (!SigSpec(cell->getParam("\\INIT")).is_fully_undef()) { + log(" initialized memories are not supported yet."); + return; + } + + dict<string, int> match_properties; + match_properties["words"] = cell->getParam("\\SIZE").as_int(); + match_properties["abits"] = cell->getParam("\\ABITS").as_int(); + match_properties["dbits"] = cell->getParam("\\WIDTH").as_int(); + match_properties["wports"] = cell->getParam("\\WR_PORTS").as_int(); + match_properties["rports"] = cell->getParam("\\RD_PORTS").as_int(); + match_properties["bits"] = match_properties["words"] * match_properties["dbits"]; + match_properties["ports"] = match_properties["wports"] + match_properties["rports"]; + + log(" Properties:"); + for (auto &it : match_properties) + log(" %s=%d", it.first.c_str(), it.second); + log("\n"); + + pool<pair<IdString, int>> failed_brams; + dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache; + + for (int i = 0; i < GetSize(rules.matches); i++) + { + auto &match = rules.matches.at(i); + + if (!rules.brams.count(rules.matches[i].name)) + log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name)); + + for (int vi = 0; vi < GetSize(rules.brams.at(match.name)); vi++) + { + auto &bram = rules.brams.at(match.name).at(vi); + bool or_next_if_better = match.or_next_if_better || vi+1 < GetSize(rules.brams.at(match.name)); + + if (failed_brams.count(pair<IdString, int>(bram.name, bram.variant))) + continue; + + int avail_rd_ports = 0; + int avail_wr_ports = 0; + for (int j = 0; j < bram.groups; j++) { + if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) == 0) + avail_rd_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0; + if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) != 0) + avail_wr_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0; + } + + log(" Checking rule #%d for bram type %s (variant %d):\n", i+1, log_id(bram.name), bram.variant); + log(" Bram geometry: abits=%d dbits=%d wports=%d rports=%d\n", bram.abits, bram.dbits, avail_wr_ports, avail_rd_ports); + + int dups = avail_rd_ports ? (match_properties["rports"] + avail_rd_ports - 1) / avail_rd_ports : 1; + match_properties["dups"] = dups; + + log(" Estimated number of duplicates for more read ports: dups=%d\n", match_properties["dups"]); + + int aover = match_properties["words"] % (1 << bram.abits); + int awaste = aover ? (1 << bram.abits) - aover : 0; + match_properties["awaste"] = awaste; + + int dover = match_properties["dbits"] % bram.dbits; + int dwaste = dover ? bram.dbits - dover : 0; + match_properties["dwaste"] = dwaste; + + int bwaste = awaste * bram.dbits + dwaste * (1 << bram.abits) - awaste * dwaste; + match_properties["bwaste"] = bwaste; + + int waste = match_properties["dups"] * bwaste; + match_properties["waste"] = waste; + + int cells = ((match_properties["dbits"] + bram.dbits - 1) / bram.dbits) * ((match_properties["words"] + (1 << bram.abits) - 1) / (1 << bram.abits)); + int efficiency = (100 * match_properties["bits"]) / (dups * cells * bram.dbits * (1 << bram.abits)); + match_properties["efficiency"] = efficiency; + + log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n", + log_id(match.name), awaste, dwaste, bwaste, waste, efficiency); + + for (auto it : match.min_limits) { + if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells") + continue; + if (!match_properties.count(it.first)) + log_error("Unknown property '%s' in match rule for bram type %s.\n", + it.first.c_str(), log_id(match.name)); + if (match_properties[it.first] >= it.second) + continue; + log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met.\n", + i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second); + goto next_match_rule; + } + for (auto it : match.max_limits) { + if (it.first == "acells" || it.first == "dcells" || it.first == "cells") + continue; + if (!match_properties.count(it.first)) + log_error("Unknown property '%s' in match rule for bram type %s.\n", + it.first.c_str(), log_id(match.name)); + if (match_properties[it.first] <= it.second) + continue; + log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'max %s %d' not met.\n", + i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second); + goto next_match_rule; + } + + log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant); + + if (or_next_if_better || !best_rule_cache.empty()) + { + if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name))) + log_error("Found 'or_next_if_better' in last match rule.\n"); + + if (!replace_cell(cell, rules, bram, match, match_properties, 1)) { + log(" Mapping to bram type %s failed.\n", log_id(match.name)); + failed_brams.insert(pair<IdString, int>(bram.name, bram.variant)); + goto next_match_rule; + } + + log(" Storing for later selection.\n"); + best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); + + if (or_next_if_better) + goto next_match_rule; + + next_match_rule: + if (or_next_if_better || best_rule_cache.empty()) + continue; + + log(" Selecting best of %d rules:\n", GetSize(best_rule_cache)); + pair<int, int> best_rule = best_rule_cache.begin()->first; + + for (auto &it : best_rule_cache) { + if (it.second > best_rule_cache[best_rule]) + best_rule = it.first; + log(" Efficiency for rule %d.%d: efficiency=%d, cells=%d, acells=%d\n", it.first.first+1, it.first.second+1, + std::get<0>(it.second), -std::get<1>(it.second), -std::get<2>(it.second)); + } + + log(" Selected rule %d.%d with efficiency %d.\n", best_rule.first+1, best_rule.second+1, std::get<0>(best_rule_cache[best_rule])); + best_rule_cache.clear(); + + auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second); + if (!replace_cell(cell, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2)) + log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant); + return; + } + + if (!replace_cell(cell, rules, bram, match, match_properties, 0)) { + log(" Mapping to bram type %s failed.\n", log_id(match.name)); + failed_brams.insert(pair<IdString, int>(bram.name, bram.variant)); + goto next_match_rule; + } + return; + } + } + + log(" No acceptable bram resources found.\n"); +} + +struct MemoryBramPass : public Pass { + MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" memory_bram -rules <rule_file> [selection]\n"); + log("\n"); + log("This pass converts the multi-port $mem memory cells into block ram instances.\n"); + log("The given rules file describes the available resources and how they should be\n"); + log("used.\n"); + log("\n"); + log("The rules file contains a set of block ram description and a sequence of match\n"); + log("rules. A block ram description looks like this:\n"); + log("\n"); + log(" bram RAMB1024X32 # name of BRAM cell\n"); + // log(" init 1 # set to '1' if BRAM can be initialized\n"); + log(" abits 10 # number of address bits\n"); + log(" dbits 32 # number of data bits\n"); + log(" groups 2 # number of port groups\n"); + log(" ports 1 1 # number of ports in each group\n"); + log(" wrmode 1 0 # set to '1' if this groups is write ports\n"); + log(" enable 4 0 # number of enable bits (for write ports)\n"); + log(" transp 0 2 # transparatent (for read ports)\n"); + log(" clocks 1 2 # clock configuration\n"); + log(" clkpol 2 2 # clock polarity configuration\n"); + log(" endbram\n"); + log("\n"); + log("For the option 'transp' the value 0 means non-transparent, 1 means transparent\n"); + log("and a value greater than 1 means configurable. All groups with the same\n"); + log("value greater than 1 share the same configuration bit.\n"); + log("\n"); + log("For the option 'clocks' the value 0 means non-clocked, and a value greater\n"); + log("than 0 means clocked. All groups with the same value share the same clock\n"); + log("signal.\n"); + log("\n"); + log("For the option 'clkpol' the value 0 means negative edge, 1 means positive edge\n"); + log("and a value greater than 1 means configurable. All groups with the same value\n"); + log("greater than 1 share the same configuration bit.\n"); + log("\n"); + log("Using the same bram name in different bram blocks will create different variants\n"); + log("of the bram. Verilog configration parameters for the bram are created as needed.\n"); + log("\n"); + log("It is also possible to create variants by repeating statements in the bram block\n"); + log("and appending '@<label>' to the individual statements.\n"); + log("\n"); + log("A match rule looks like this:\n"); + log("\n"); + log(" match RAMB1024X32\n"); + log(" max waste 16384 # only use this bram if <= 16k ram bits are unused\n"); + log(" min efficiency 80 # only use this bram if efficiency is at least 80%%\n"); + log(" endmatch\n"); + log("\n"); + log("It is possible to match against the following values with min/max rules:\n"); + log("\n"); + log(" words ........ number of words in memory in design\n"); + log(" abits ........ number of address bits on memory in design\n"); + log(" dbits ........ number of data bits on memory in design\n"); + log(" wports ....... number of write ports on memory in design\n"); + log(" rports ....... number of read ports on memory in design\n"); + log(" ports ........ number of ports on memory in design\n"); + log(" bits ......... number of bits in memory in design\n"); + log(" dups .......... number of duplications for more read ports\n"); + log("\n"); + log(" awaste ....... number of unused address slots for this match\n"); + log(" dwaste ....... number of unused data bits for this match\n"); + log(" bwaste ....... number of unused bram bits for this match\n"); + log(" waste ........ total number of unused bram bits (bwaste*dups)\n"); + log(" efficiency ... total percentage of used and non-duplicated bits\n"); + log("\n"); + log(" acells ....... number of cells in 'address-direction'\n"); + log(" dcells ....... number of cells in 'data-direction'\n"); + log(" cells ........ total number of cells (acells*dcells*dups)\n"); + log("\n"); + log("The interface for the created bram instances is dervived from the bram\n"); + log("description. Use 'techmap' to convert the created bram instances into\n"); + log("instances of the actual bram cells of your target architecture.\n"); + log("\n"); + log("A match containing the command 'or_next_if_better' is only used if it\n"); + log("has a higher efficiency than the next match (and the one after that if\n"); + log("the next also has 'or_next_if_better' set, and so forth).\n"); + log("\n"); + log("A match containing the command 'make_transp' will add external circuitry\n"); + log("to simulate 'transparent read', if necessary.\n"); + log("\n"); + log("A match containing the command 'shuffle_enable A' will re-organize\n"); + log("the data bits to accommodate the enable pattern of port A.\n"); + log("\n"); + } + virtual void execute(vector<string> args, Design *design) + { + rules_t rules; + + log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-rules" && argidx+1 < args.size()) { + rules.parse(args[++argidx]); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + for (auto cell : mod->selected_cells()) + if (cell->type == "$mem") + handle_cell(cell, rules); + } +} MemoryBramPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index 9c670f00..96d0ada0 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -17,13 +17,13 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" -#include <sstream> -#include <algorithm> -#include <stdlib.h> +#include "kernel/yosys.h" +#include "kernel/sigtools.h" -static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) { if (a->type == "$memrd" && b->type == "$memrd") return a->name < b->name; @@ -32,14 +32,15 @@ static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int(); } -static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) +void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) { - log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n", + log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n", memory->name.c_str(), module->name.c_str()); int addr_bits = 0; - while ((1 << addr_bits) < memory->size) - addr_bits++; + + Const init_data(State::Sx, memory->size * memory->width); + SigMap sigmap(module); int wr_ports = 0; RTLIL::SigSpec sig_wr_clk; @@ -57,37 +58,64 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) RTLIL::SigSpec sig_rd_addr; RTLIL::SigSpec sig_rd_data; - std::vector<RTLIL::Cell*> del_cells; std::vector<RTLIL::Cell*> memcells; for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string()) + if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) { + addr_bits = std::max(addr_bits, cell->getParam("\\ABITS").as_int()); memcells.push_back(cell); + } + } + + if (memcells.empty()) { + log(" no cells found. removing memory.\n"); + return; } std::sort(memcells.begin(), memcells.end(), memcells_cmp); for (auto cell : memcells) { - if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string()) + log(" %s (%s)\n", log_id(cell), log_id(cell->type)); + + if (cell->type == "$meminit") { - wr_ports++; - del_cells.push_back(cell); - - 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->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(memory->width, false); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + + if (!addr.is_fully_const()) + log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell)); + if (!data.is_fully_const()) + log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell)); + + int offset = (addr.as_int() - memory->start_offset) * memory->width; + + if (offset < 0 || offset + GetSize(data) > GetSize(init_data)) + log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell)); + + for (int i = 0; i < GetSize(data); i++) + if (0 <= i+offset && i+offset < GetSize(init_data)) + init_data.bits[i+offset] = data[i].data; + + continue; + } + + if (cell->type == "$memwr") + { + SigSpec clk = sigmap(cell->getPort("\\CLK")); + SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); + SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + SigSpec en = sigmap(cell->getPort("\\EN")); + + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); + en.extend_u0(memory->width, false); sig_wr_clk.append(clk); sig_wr_clk_enable.append(clk_enable); @@ -95,26 +123,26 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) sig_wr_addr.append(addr); sig_wr_data.append(data); sig_wr_en.append(en); + + wr_ports++; + continue; } - if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string()) + if (cell->type == "$memrd") { - rd_ports++; - del_cells.push_back(cell); - - 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->getPort("\\ADDR"); - RTLIL::SigSpec data = cell->getPort("\\DATA"); - - clk.extend(1, false); - clk_enable.extend(1, false); - clk_polarity.extend(1, false); - transparent.extend(1, false); - addr.extend(addr_bits, false); - data.extend(memory->width, false); + SigSpec clk = sigmap(cell->getPort("\\CLK")); + SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); + SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); + SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + transparent.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); sig_rd_clk.append(clk); sig_rd_clk_enable.append(clk_enable); @@ -122,6 +150,9 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) sig_rd_transparent.append(transparent); sig_rd_addr.append(addr); sig_rd_data.append(data); + + rd_ports++; + continue; } } @@ -135,6 +166,10 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) mem->parameters["\\SIZE"] = RTLIL::Const(memory->size); mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits); + while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx) + init_data.bits.pop_back(); + mem->parameters["\\INIT"] = init_data; + 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()); @@ -166,7 +201,7 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) mem->setPort("\\RD_ADDR", sig_rd_addr); mem->setPort("\\RD_DATA", sig_rd_data); - for (auto c : del_cells) + for (auto c : memcells) module->remove(c); } @@ -205,3 +240,4 @@ struct MemoryCollectPass : public Pass { } } MemoryCollectPass; +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 302ab3ab..d3cc681a 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -22,13 +22,16 @@ #include <stdlib.h> #include <sstream> -static void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig) { for (auto &conn : module->connections()) sig.replace(conn.first, conn.second); } -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) +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); @@ -66,7 +69,7 @@ static bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> return true; } -static void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) +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()); @@ -105,7 +108,7 @@ static void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff log("no (compatible) $dff found.\n"); } -static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) +void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) { normalize_sig(module, sig); sig.sort_and_unify(); @@ -123,7 +126,7 @@ static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) } } -static void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) +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()); @@ -161,7 +164,7 @@ static void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff log("no (compatible) $dff found.\n"); } -static void handle_module(RTLIL::Module *module, bool flag_wr_only) +void handle_module(RTLIL::Module *module, bool flag_wr_only) { std::vector<RTLIL::Cell*> dff_cells; @@ -216,3 +219,4 @@ struct MemoryDffPass : public Pass { } } MemoryDffPass; +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index eecb6f35..41c4a7b1 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -23,6 +23,9 @@ #include <set> #include <stdlib.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct MemoryMapWorker { RTLIL::Design *design; @@ -55,21 +58,21 @@ struct MemoryMapWorker 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)); + log_assert(GetSize(addr_sig) == GetSize(addr_val)); if (decoder_cache.count(key) == 0) { - if (SIZE(addr_sig) < 2) { + if (GetSize(addr_sig) < 2) { decoder_cache[key] = module->Eq(NEW_ID, addr_sig, addr_val); } else { - int split_at = SIZE(addr_sig) / 2; + int split_at = GetSize(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)); + RTLIL::SigBit right_eq = addr_decode(addr_sig.extract(split_at, GetSize(addr_sig) - split_at), addr_val.extract(split_at, GetSize(addr_val) - split_at)); decoder_cache[key] = module->And(NEW_ID, left_eq, right_eq); } } RTLIL::SigBit bit = decoder_cache.at(key); - log_assert(bit.wire != nullptr && SIZE(bit.wire) == 1); + log_assert(bit.wire != nullptr && GetSize(bit.wire) == 1); return bit.wire; } @@ -77,11 +80,15 @@ struct MemoryMapWorker { 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(); + SigSpec init_data = cell->getParam("\\INIT"); + init_data.extend_u0(mem_size*mem_width, true); + // delete unused memory cell if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) { module->remove(cell); @@ -107,7 +114,7 @@ struct MemoryMapWorker // 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_cells_map[wr_addr.as_int() - mem_offset] = wr_data; static_ports.insert(i); continue; } @@ -162,7 +169,10 @@ struct MemoryMapWorker 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; + SigSpec w_init = init_data.extract(i*mem_width, mem_width); + + if (!w_init.is_fully_undef()) + w_out->attributes["\\init"] = w_init.as_const(); data_reg_out.push_back(RTLIL::SigSpec(w_out)); c->setPort("\\Q", data_reg_out.back()); @@ -177,6 +187,9 @@ struct MemoryMapWorker { RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits); + if (mem_offset) + rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr))); + std::vector<RTLIL::SigSpec> rd_signals; rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width)); @@ -253,6 +266,10 @@ struct MemoryMapWorker 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); + + if (mem_offset) + wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr))); + RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits)); int wr_offset = 0; @@ -339,3 +356,4 @@ struct MemoryMapPass : public Pass { } } MemoryMapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index 3ae0cd2c..3d241433 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -22,9 +22,10 @@ #include "kernel/sigtools.h" #include "kernel/modtools.h" +USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) { if (a->type == "$memrd" && b->type == "$memrd") return a->name < b->name; @@ -488,8 +489,8 @@ struct MemoryShareWorker if (wr_ports.size() <= 1) return; - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); // find list of considered ports and port pairs @@ -543,6 +544,7 @@ struct MemoryShareWorker // create SAT representation of common input cone of all considered EN signals + pool<Wire*> one_hot_wires; std::set<RTLIL::Cell*> sat_cells; std::set<RTLIL::SigBit> bits_queue; std::map<int, int> port_to_sat_variable; @@ -551,7 +553,7 @@ struct MemoryShareWorker 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)); + 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()); @@ -559,24 +561,36 @@ struct MemoryShareWorker while (!bits_queue.empty()) { - std::set<ModWalker::PortBit> portbits; + for (auto bit : bits_queue) + if (bit.wire && bit.wire->get_bool_attribute("\\onehot")) + one_hot_wires.insert(bit.wire); + + pool<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]; + pool<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell]; bits_queue.insert(cell_inputs.begin(), cell_inputs.end()); sat_cells.insert(pbit.cell); } } + for (auto wire : one_hot_wires) { + log(" Adding one-hot constraint for wire %s.\n", log_id(wire)); + vector<int> ez_wire_bits = satgen.importSigSpec(wire); + for (int i : ez_wire_bits) + for (int j : ez_wire_bits) + if (i != j) ez->assume(ez->NOT(i), j); + } + 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()); + log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses()); // merge subsequent ports if possible @@ -585,13 +599,13 @@ struct MemoryShareWorker if (!considered_port_pairs.count(i)) continue; - if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) { + 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)); + 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"); @@ -741,4 +755,3 @@ struct MemorySharePass : public Pass { } MemorySharePass; PRIVATE_NAMESPACE_END - diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index 5a4c4eac..e650facb 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -23,7 +23,10 @@ #include <algorithm> #include <stdlib.h> -static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) { log("Creating $memrd and $memwr for memory `%s' in module `%s':\n", memory->name.c_str(), module->name.c_str()); @@ -76,7 +79,7 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) module->remove(memory); } -static void handle_module(RTLIL::Design *design, RTLIL::Module *module) +void handle_module(RTLIL::Design *design, RTLIL::Module *module) { std::vector<RTLIL::IdString> memcells; for (auto &cell_it : module->cells_) @@ -107,3 +110,4 @@ struct MemoryUnpackPass : public Pass { } } MemoryUnpackPass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 3a8d27f9..6b075cd9 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -6,6 +6,9 @@ OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_const.o + +ifneq ($(SMALL),1) OBJS += passes/opt/share.o OBJS += passes/opt/wreduce.o +endif diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index b20521d1..5ca57a14 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -22,6 +22,9 @@ #include <stdlib.h> #include <stdio.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct OptPass : public Pass { OptPass() : Pass("opt", "perform simple optimizations") { } virtual void help() @@ -34,22 +37,22 @@ struct OptPass : public Pass { 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 [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); log(" opt_share -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); - log(" opt_reduce [-fine]\n"); + log(" opt_reduce [-fine] [-full]\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(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-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_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); log(" opt_share\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); @@ -93,6 +96,11 @@ struct OptPass : public Pass { opt_reduce_args += " -fine"; continue; } + if (args[argidx] == "-full") { + opt_const_args += " -full"; + opt_reduce_args += " -full"; + continue; + } if (args[argidx] == "-keepdc") { opt_const_args += " -keepdc"; continue; @@ -137,8 +145,13 @@ struct OptPass : public Pass { } } - log_header(fast_mode ? "Finished fast OPT passes." : "Finished OPT passes. (There is nothing left to do.)\n"); + design->optimize(); + design->sort(); + design->check(); + + log_header(fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 5046752f..9d2a262a 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -25,65 +25,61 @@ #include <stdio.h> #include <set> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + using RTLIL::id2cstr; -static CellTypes ct, ct_reg, ct_all; -static int count_rm_cells, count_rm_wires; +CellTypes ct, ct_reg, ct_all; +int count_rm_cells, count_rm_wires; -static void rmunused_module_cells(RTLIL::Module *module, bool verbose) +void rmunused_module_cells(Module *module, bool verbose) { - SigMap assign_map(module); - std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> queue, unused; + SigMap sigmap(module); + pool<Cell*> queue, unused; + dict<SigBit, pool<Cell*>> wire2driver; - SigSet<RTLIL::Cell*> wire2driver; for (auto &it : module->cells_) { - RTLIL::Cell *cell = it.second; + Cell *cell = it.second; for (auto &it2 : cell->connections()) { - if (!ct.cell_input(cell->type, it2.first)) { - RTLIL::SigSpec sig = it2.second; - assign_map.apply(sig); - wire2driver.insert(sig, cell); - } + if (!ct.cell_input(cell->type, it2.first)) + for (auto bit : sigmap(it2.second)) + if (bit.wire != nullptr) + wire2driver[bit].insert(cell); } - if (cell->type == "$memwr" || cell->type == "$assert" || cell->get_bool_attribute("\\keep")) + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume") || cell->has_keep_attr()) queue.insert(cell); - unused.insert(cell); + else + unused.insert(cell); } for (auto &it : module->wires_) { - RTLIL::Wire *wire = it.second; + Wire *wire = it.second; if (wire->port_output || wire->get_bool_attribute("\\keep")) { - std::set<RTLIL::Cell*> cell_list; - RTLIL::SigSpec sig = RTLIL::SigSpec(wire); - assign_map.apply(sig); - wire2driver.find(sig, cell_list); - for (auto cell : cell_list) - queue.insert(cell); + for (auto bit : sigmap(wire)) + for (auto c : wire2driver[bit]) + queue.insert(c), unused.erase(c); } } - while (queue.size() > 0) + while (!queue.empty()) { - std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> new_queue; + pool<SigBit> bits; for (auto cell : queue) - unused.erase(cell); - for (auto cell : queue) { - for (auto &it : cell->connections()) { - if (!ct.cell_output(cell->type, it.first)) { - std::set<RTLIL::Cell*> cell_list; - RTLIL::SigSpec sig = it.second; - assign_map.apply(sig); - wire2driver.find(sig, cell_list); - for (auto cell : cell_list) { - if (unused.count(cell) > 0) - new_queue.insert(cell); - } - } - } - } - queue.swap(new_queue); + for (auto &it : cell->connections()) + if (!ct.cell_output(cell->type, it.first)) + for (auto bit : sigmap(it.second)) + bits.insert(bit); + + queue.clear(); + for (auto bit : bits) + for (auto c : wire2driver[bit]) + if (unused.count(c)) + queue.insert(c), unused.erase(c); } + unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>()); + for (auto cell : unused) { if (verbose) log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str()); @@ -93,7 +89,7 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) } } -static int count_nontrivial_wire_attrs(RTLIL::Wire *w) +int count_nontrivial_wire_attrs(RTLIL::Wire *w) { int count = w->attributes.size(); count -= w->attributes.count("\\src"); @@ -101,7 +97,7 @@ static int count_nontrivial_wire_attrs(RTLIL::Wire *w) return count; } -static bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, std::set<RTLIL::Wire*> &direct_wires) +bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, pool<RTLIL::Wire*> &direct_wires) { RTLIL::Wire *w1 = s1.wire; RTLIL::Wire *w2 = s2.wire; @@ -116,7 +112,7 @@ static bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, if (regs.check_any(s1) != regs.check_any(s2)) return regs.check_any(s2); if (direct_wires.count(w1) != direct_wires.count(w2)) - return direct_wires.count(w2); + return direct_wires.count(w2) != 0; if (conns.check_any(s1) != conns.check_any(s2)) return conns.check_any(s2); } @@ -136,7 +132,7 @@ static bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, return w2->name < w1->name; } -static bool check_public_name(RTLIL::IdString id) +bool check_public_name(RTLIL::IdString id) { const std::string &id_str = id.str(); if (id_str[0] == '$') @@ -148,7 +144,7 @@ static bool check_public_name(RTLIL::IdString id) return true; } -static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose) +void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose) { SigPool register_signals; SigPool connected_signals; @@ -165,8 +161,8 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool } SigMap assign_map(module); - std::set<RTLIL::SigSpec> direct_sigs; - std::set<RTLIL::Wire*> direct_wires; + pool<RTLIL::SigSpec> direct_sigs; + pool<RTLIL::Wire*> direct_wires; for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (ct_all.cell_known(cell->type)) @@ -226,9 +222,9 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { maybe_del_wires.push_back(wire); } else { - log_assert(SIZE(s1) == SIZE(s2)); + log_assert(GetSize(s1) == GetSize(s2)); RTLIL::SigSig new_conn; - for (int i = 0; i < SIZE(s1); i++) + for (int i = 0; i < GetSize(s1); i++) if (s1[i] != s2[i]) { new_conn.first.append_bit(s1[i]); new_conn.second.append_bit(s2[i]); @@ -247,7 +243,7 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); if (!used_signals_nodrivers.check_any(sig)) { std::string unused_bits; - for (int i = 0; i < SIZE(sig); i++) { + for (int i = 0; i < GetSize(sig); i++) { if (sig[i].wire == NULL) continue; if (!used_signals_nodrivers.check(sig[i])) { @@ -266,7 +262,7 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool } - std::set<RTLIL::Wire*> del_wires; + pool<RTLIL::Wire*> del_wires; int del_wires_count = 0; for (auto wire : maybe_del_wires) @@ -285,11 +281,30 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool log(" removed %d unused temporary wires.\n", del_wires_count); } -static void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) +void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) { if (verbose) log("Finding unused cells or wires in module %s..\n", module->name.c_str()); + std::vector<RTLIL::Cell*> delcells; + for (auto cell : module->cells()) + if (cell->type.in("$pos", "$_BUF_")) { + bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool(); + RTLIL::SigSpec a = cell->getPort("\\A"); + RTLIL::SigSpec y = cell->getPort("\\Y"); + a.extend_u0(GetSize(y), is_signed); + module->connect(y, a); + delcells.push_back(cell); + } + for (auto cell : delcells) { + if (verbose) + log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(), + log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A"))); + module->remove(cell); + } + if (!delcells.empty()) + module->design->scratchpad_set_bool("opt.did_something", true); + rmunused_module_cells(module, verbose); rmunused_module_signals(module, purge_mode, verbose); } @@ -338,19 +353,16 @@ struct OptCleanPass : public Pass { ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); - 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 module : design->selected_whole_modules_warn()) { + if (module->has_processes_warn()) continue; - } - if (mod_it.second->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str()); - } else { - rmunused_module(mod_it.second, purge_mode, true); - } + rmunused_module(module, purge_mode, true); } + design->optimize(); + design->sort(); + design->check(); + ct.clear(); ct_reg.clear(); log_pop(); @@ -402,20 +414,23 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto &mod_it : design->modules_) { - if (design->selected_whole_module(mod_it.first) && mod_it.second->processes.size() == 0) - do { - design->scratchpad_unset("opt.did_something"); - rmunused_module(mod_it.second, purge_mode, false); - } while (design->scratchpad_get_bool("opt.did_something")); + for (auto module : design->selected_whole_modules()) { + if (module->has_processes()) + continue; + rmunused_module(module, purge_mode, false); } if (count_rm_cells > 0 || count_rm_wires > 0) log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires); + design->optimize(); + design->sort(); + design->check(); + ct.clear(); ct_reg.clear(); ct_all.clear(); } } CleanPass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc index f9b78c05..1758a34f 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_const.cc @@ -26,9 +26,12 @@ #include <stdio.h> #include <algorithm> -static bool did_something; +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN -static void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) +bool did_something; + +void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) { CellTypes ct(design); SigMap sigmap(module); @@ -70,7 +73,7 @@ static void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) } } -static void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) +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->getPort(out_port); out_val.extend_u0(Y.size(), false); @@ -85,7 +88,7 @@ static void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell did_something = true; } -static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap) +bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap) { std::string b_name = cell->hasPort("\\B") ? "\\B" : "\\A"; @@ -104,7 +107,7 @@ static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool com 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++) + for (int i = 0; i < GetSize(bits_y); i++) { int group_idx = GRP_DYN; RTLIL::SigBit bit_a = bits_a[i], bit_b = bits_b[i]; @@ -128,7 +131,7 @@ static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool com } for (int i = 0; i < GRP_N; i++) - if (SIZE(grouped_bits[i]) == SIZE(bits_y)) + if (GetSize(grouped_bits[i]) == GetSize(bits_y)) return false; log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n", @@ -139,7 +142,7 @@ static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool com if (grouped_bits[i].empty()) continue; - RTLIL::Wire *new_y = module->addWire(NEW_ID, SIZE(grouped_bits[i])); + RTLIL::Wire *new_y = module->addWire(NEW_ID, GetSize(grouped_bits[i])); RTLIL::SigSpec new_a, new_b; RTLIL::SigSig new_conn; @@ -183,7 +186,7 @@ static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool com 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) +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; @@ -193,11 +196,11 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo ct_combinational.setup_stdcells(); SigMap assign_map(module); - std::map<RTLIL::SigSpec, RTLIL::SigSpec> invert_map; + dict<RTLIL::SigSpec, RTLIL::SigSpec> invert_map; - 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; + TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells; + dict<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; + dict<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; for (auto cell : module->cells()) if (design->selected(module, cell) && cell->type[0] == '$') { @@ -483,12 +486,12 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo RTLIL::SigSpec new_a, new_b; - log_assert(SIZE(a) == SIZE(b)); - for (int i = 0; i < SIZE(a); i++) { + log_assert(GetSize(a) == GetSize(b)); + for (int i = 0; i < GetSize(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); + new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); goto next_cell; } @@ -501,7 +504,7 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo 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); + new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); goto next_cell; } @@ -521,11 +524,11 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); - if (a.is_fully_const()) { + if (a.is_fully_const() && !b.is_fully_const()) { 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); + cell->setPort("\\A", b); + cell->setPort("\\B", a); + std::swap(a, b); } if (b.is_fully_const()) { @@ -556,15 +559,15 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo 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()); + if (GetSize(sig_a) < GetSize(sig_y)) + sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool()); - for (int i = 0; i < SIZE(sig_y); i++) { + for (int i = 0; i < GetSize(sig_y); i++) { int idx = i + shift_bits; - if (0 <= idx && idx < SIZE(sig_a)) + if (0 <= idx && idx < GetSize(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]; + else if (GetSize(sig_a) <= idx && sign_ext) + sig_y[i] = sig_a[GetSize(sig_a)-1]; } cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); @@ -666,8 +669,9 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo cell->unsetPort("\\B"); cell->unsetPort("\\S"); if (cell->type == "$mux") { - cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; - cell->parameters["\\Y_WIDTH"] = cell->parameters["\\WIDTH"]; + Const width = cell->parameters["\\WIDTH"]; + cell->parameters["\\A_WIDTH"] = width; + cell->parameters["\\Y_WIDTH"] = width; cell->parameters["\\A_SIGNED"] = 0; cell->parameters.erase("\\WIDTH"); cell->type = "$not"; @@ -683,9 +687,10 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo 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"]; - cell->parameters["\\Y_WIDTH"] = cell->parameters["\\WIDTH"]; + Const width = cell->parameters["\\WIDTH"]; + cell->parameters["\\A_WIDTH"] = width; + cell->parameters["\\B_WIDTH"] = width; + cell->parameters["\\Y_WIDTH"] = width; cell->parameters["\\A_SIGNED"] = 0; cell->parameters["\\B_SIGNED"] = 0; cell->parameters.erase("\\WIDTH"); @@ -702,9 +707,10 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo 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"]; - cell->parameters["\\Y_WIDTH"] = cell->parameters["\\WIDTH"]; + Const width = cell->parameters["\\WIDTH"]; + cell->parameters["\\A_WIDTH"] = width; + cell->parameters["\\B_WIDTH"] = width; + cell->parameters["\\Y_WIDTH"] = width; cell->parameters["\\A_SIGNED"] = 0; cell->parameters["\\B_SIGNED"] = 0; cell->parameters.erase("\\WIDTH"); @@ -751,7 +757,7 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo 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)); + GetSize(cell->getPort("\\S")) - GetSize(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); @@ -891,17 +897,17 @@ static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bo 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"]; + cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH"); + cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); - while (SIZE(new_b) > 1 && new_b.back() == RTLIL::State::S0) + while (GetSize(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_WIDTH"] = GetSize(new_b); cell->parameters["\\B_SIGNED"] = false; cell->setPort("\\B", new_b); cell->check(); @@ -939,15 +945,18 @@ struct OptConstPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); + log(" -fine\n"); + log(" perform fine-grain optimizations\n"); + log("\n"); + log(" -full\n"); + log(" alias for -mux_undef -mux_bool -undriven -fine\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) { @@ -978,6 +987,13 @@ struct OptConstPass : public Pass { do_fine = true; continue; } + if (args[argidx] == "-full") { + mux_undef = true; + mux_bool = true; + undriven = true; + do_fine = true; + continue; + } if (args[argidx] == "-keepdc") { keepdc = true; continue; @@ -986,7 +1002,7 @@ struct OptConstPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->modules()) + for (auto module : design->selected_modules()) { if (undriven) replace_undriven(design, module); @@ -1006,3 +1022,4 @@ struct OptConstPass : public Pass { } } OptConstPass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 2c5dcf66..98287074 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -25,6 +25,9 @@ #include <stdio.h> #include <set> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + using RTLIL::id2cstr; struct OptMuxtreeWorker @@ -34,37 +37,33 @@ struct OptMuxtreeWorker SigMap assign_map; int removed_count; - 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; - bitDef_t bit; bool seen_non_mux; - std::vector<int> mux_users; - std::vector<int> mux_drivers; + pool<int> mux_users; + pool<int> mux_drivers; }; - std::map<bitDef_t, int> bit2num; - std::vector<bitinfo_t> bit2info; + idict<SigBit> bit2num; + vector<bitinfo_t> bit2info; struct portinfo_t { - std::vector<int> ctrl_sigs; - std::vector<int> input_sigs; - std::vector<int> input_muxes; + int ctrl_sig; + pool<int> input_sigs; + pool<int> input_muxes; bool const_activated; + bool const_deactivated; bool enabled; }; struct muxinfo_t { RTLIL::Cell *cell; - std::vector<portinfo_t> ports; + vector<portinfo_t> ports; }; - std::vector<muxinfo_t> mux2info; + vector<muxinfo_t> mux2info; + vector<bool> root_muxes; + vector<bool> root_enable_muxes; + pool<int> root_mux_rerun; OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), assign_map(module), removed_count(0) @@ -78,9 +77,10 @@ struct OptMuxtreeWorker // .mux_users // .mux_drivers // Populate mux2info[].ports[]: - // .ctrl_sigs + // .ctrl_sig // .input_sigs // .const_activated + // .const_deactivated for (auto cell : module->cells()) { if (cell->type == "$mux" || cell->type == "$pmux") @@ -93,32 +93,34 @@ struct OptMuxtreeWorker muxinfo_t muxinfo; muxinfo.cell = cell; - for (int i = 0; i < sig_s.size(); i++) { - RTLIL::SigSpec sig = sig_b.extract(i*sig_a.size(), sig_a.size()); + for (int i = 0; i < GetSize(sig_s); i++) { + RTLIL::SigSpec sig = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1)); portinfo_t portinfo; + portinfo.ctrl_sig = sig2bits(ctrl_sig, false).front(); for (int idx : sig2bits(sig)) { - add_to_list(bit2info[idx].mux_users, mux2info.size()); - add_to_list(portinfo.input_sigs, idx); + bit2info[idx].mux_users.insert(GetSize(mux2info)); + portinfo.input_sigs.insert(idx); } - for (int idx : sig2bits(ctrl_sig)) - add_to_list(portinfo.ctrl_sigs, idx); portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool(); + portinfo.const_deactivated = ctrl_sig.is_fully_const() && !ctrl_sig.as_bool(); portinfo.enabled = false; muxinfo.ports.push_back(portinfo); } portinfo_t portinfo; for (int idx : sig2bits(sig_a)) { - add_to_list(bit2info[idx].mux_users, mux2info.size()); - add_to_list(portinfo.input_sigs, idx); + bit2info[idx].mux_users.insert(GetSize(mux2info)); + portinfo.input_sigs.insert(idx); } + portinfo.ctrl_sig = -1; portinfo.const_activated = false; + portinfo.const_deactivated = false; portinfo.enabled = false; muxinfo.ports.push_back(portinfo); for (int idx : sig2bits(sig_y)) - add_to_list(bit2info[idx].mux_drivers, mux2info.size()); + bit2info[idx].mux_drivers.insert(GetSize(mux2info)); for (int idx : sig2bits(sig_s)) bit2info[idx].seen_non_mux = true; @@ -139,53 +141,78 @@ struct OptMuxtreeWorker bit2info[idx].seen_non_mux = true; } - if (mux2info.size() == 0) { + if (mux2info.empty()) { log(" No muxes found in this module.\n"); return; } // Populate mux2info[].ports[]: // .input_muxes - for (size_t i = 0; i < bit2info.size(); i++) + for (int i = 0; i < GetSize(bit2info); i++) for (int j : bit2info[i].mux_users) for (auto &p : mux2info[j].ports) { - if (is_in_list(p.input_sigs, i)) + if (p.input_sigs.count(i)) for (int k : bit2info[i].mux_drivers) - add_to_list(p.input_muxes, k); + p.input_muxes.insert(k); } log(" Evaluating internal representation of mux trees.\n"); - std::set<int> root_muxes; + dict<int, pool<int>> mux_to_users; + root_muxes.resize(GetSize(mux2info)); + root_enable_muxes.resize(GetSize(mux2info)); + for (auto &bi : bit2info) { + for (int i : bi.mux_drivers) + for (int j : bi.mux_users) + mux_to_users[i].insert(j); if (!bi.seen_non_mux) continue; - for (int mux_idx : bi.mux_drivers) - root_muxes.insert(mux_idx); + for (int mux_idx : bi.mux_drivers) { + root_muxes.at(mux_idx) = true; + root_enable_muxes.at(mux_idx) = true; + } } - for (int mux_idx : root_muxes) + + for (auto &it : mux_to_users) + if (GetSize(it.second) > 1) + root_muxes.at(it.first) = true; + + for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++) + if (root_muxes.at(mux_idx)) { + log(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : ""); + root_mux_rerun.erase(mux_idx); + eval_root_mux(mux_idx); + } + + while (!root_mux_rerun.empty()) { + int mux_idx = *root_mux_rerun.begin(); + log(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell)); + log_assert(root_enable_muxes.at(mux_idx)); + root_mux_rerun.erase(mux_idx); eval_root_mux(mux_idx); + } log(" Analyzing evaluation results.\n"); for (auto &mi : mux2info) { - std::vector<int> live_ports; - for (size_t port_idx = 0; port_idx < mi.ports.size(); port_idx++) { + vector<int> live_ports; + for (int port_idx = 0; port_idx < GetSize(mi.ports); port_idx++) { portinfo_t &pi = mi.ports[port_idx]; if (pi.enabled) { live_ports.push_back(port_idx); } else { - log(" dead port %zd/%zd on %s %s.\n", port_idx+1, mi.ports.size(), + log(" dead port %d/%d on %s %s.\n", port_idx+1, GetSize(mi.ports), mi.cell->type.c_str(), mi.cell->name.c_str()); removed_count++; } } - if (live_ports.size() == mi.ports.size()) + if (GetSize(live_ports) == GetSize(mi.ports)) continue; - if (live_ports.size() == 0) { + if (live_ports.empty()) { module->remove(mi.cell); continue; } @@ -198,9 +225,9 @@ struct OptMuxtreeWorker RTLIL::SigSpec sig_ports = sig_b; sig_ports.append(sig_a); - if (live_ports.size() == 1) + if (GetSize(live_ports) == 1) { - RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.size(), sig_a.size()); + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*GetSize(sig_a), GetSize(sig_a)); module->connect(RTLIL::SigSig(sig_y, sig_in)); module->remove(mi.cell); } @@ -208,9 +235,9 @@ struct OptMuxtreeWorker { 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.size(), sig_a.size()); - if (i == live_ports.size()-1) { + for (int i = 0; i < GetSize(live_ports); i++) { + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*GetSize(sig_a), GetSize(sig_a)); + if (i == GetSize(live_ports)-1) { new_sig_a = sig_in; } else { new_sig_b.append(sig_in); @@ -221,123 +248,162 @@ struct OptMuxtreeWorker 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) { + if (GetSize(new_sig_s) == 1) { mi.cell->type = "$mux"; mi.cell->parameters.erase("\\S_WIDTH"); } else { - mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size()); + mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(GetSize(new_sig_s)); } } } } - bool list_is_subset(const std::vector<int> &sub, const std::vector<int> &super) + vector<int> sig2bits(RTLIL::SigSpec sig, bool skip_non_wires = true) { - for (int v : sub) - if (!is_in_list(super, v)) - return false; - return true; - } - - bool is_in_list(const std::vector<int> &list, int value) - { - for (int v : list) - if (v == value) - return true; - return false; - } - - void add_to_list(std::vector<int> &list, int value) - { - if (!is_in_list(list, value)) - list.push_back(value); - } - - std::vector<int> sig2bits(RTLIL::SigSpec sig) - { - std::vector<int> results; + vector<int> results; assign_map.apply(sig); for (auto &bit : sig) if (bit.wire != NULL) { if (bit2num.count(bit) == 0) { bitinfo_t info; - info.num = bit2info.size(); - info.bit = bit; info.seen_non_mux = false; + bit2num.expect(bit, GetSize(bit2info)); bit2info.push_back(info); - bit2num[info.bit] = info.num; } - results.push_back(bit2num[bit]); - } + results.push_back(bit2num.at(bit)); + } else if (!skip_non_wires) + results.push_back(-1); return results; } struct knowledge_t { // database of known inactive signals - // the 2nd integer is a reference counter used to manage the + // the payload is a reference counter used to manage the // list. when it is non-zero the signal in known to be inactive - std::map<int, int> known_inactive; + vector<int> known_inactive; // database of known active signals - // the 2nd dimension is the list of or-ed signals. so we know that - // for each i there is a j so that known_active[i][j] points to an - // inactive control signal. - std::vector<std::vector<int>> known_active; + vector<int> known_active; // this is just used to keep track of visited muxes in order to prohibit // endless recursion in mux loops - std::set<int> visited_muxes; + vector<bool> visited_muxes; }; - void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx) + void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { muxinfo_t &muxinfo = mux2info[mux_idx]; - muxinfo.ports[port_idx].enabled = true; - for (size_t i = 0; i < muxinfo.ports.size(); i++) { - if (int(i) == port_idx) + if (do_enable_ports) + muxinfo.ports[port_idx].enabled = true; + + for (int i = 0; i < GetSize(muxinfo.ports); i++) { + if (i == port_idx) continue; - for (int b : muxinfo.ports[i].ctrl_sigs) - knowledge.known_inactive[b]++; + if (muxinfo.ports[i].ctrl_sig >= 0) + knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)++; } - 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); + if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) + knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)++; - std::vector<int> parent_muxes; + vector<int> parent_muxes; for (int m : muxinfo.ports[port_idx].input_muxes) { - if (knowledge.visited_muxes.count(m) > 0) + if (knowledge.visited_muxes[m]) continue; - knowledge.visited_muxes.insert(m); + knowledge.visited_muxes[m] = true; parent_muxes.push_back(m); } for (int m : parent_muxes) - eval_mux(knowledge, m); + if (root_enable_muxes.at(m)) + continue; + else if (root_muxes.at(m)) { + if (abort_count == 0) { + root_mux_rerun.insert(m); + root_enable_muxes.at(m) = true; + log(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell)); + } else + eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1); + } else + eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count); for (int m : parent_muxes) - knowledge.visited_muxes.erase(m); + knowledge.visited_muxes[m] = false; + + if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) + knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)--; - if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) - knowledge.known_active.pop_back(); + for (int i = 0; i < GetSize(muxinfo.ports); i++) { + if (i == port_idx) + continue; + if (muxinfo.ports[i].ctrl_sig >= 0) + knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)--; + } + } - for (size_t i = 0; i < muxinfo.ports.size(); i++) { - if (int(i) == port_idx) + void replace_known(knowledge_t &knowledge, muxinfo_t &muxinfo, IdString portname) + { + SigSpec sig = muxinfo.cell->getPort(portname); + bool did_something = false; + + int width = 0; + idict<int> ctrl_bits; + if (portname == "\\B") + width = GetSize(muxinfo.cell->getPort("\\A")); + for (int bit : sig2bits(muxinfo.cell->getPort("\\S"), false)) + ctrl_bits(bit); + + int port_idx = 0, port_off = 0; + vector<int> bits = sig2bits(sig, false); + for (int i = 0; i < GetSize(bits); i++) { + if (bits[i] < 0) continue; - for (int b : muxinfo.ports[i].ctrl_sigs) - knowledge.known_inactive[b]--; + if (knowledge.known_inactive.at(bits[i])) { + sig[i] = State::S0; + did_something = true; + } else + if (knowledge.known_active.at(bits[i])) { + sig[i] = State::S1; + did_something = true; + } + if (width) { + if (ctrl_bits.count(bits[i])) { + sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0; + did_something = true; + } + if (++port_off == width) + port_idx++, port_off=0; + } else { + if (ctrl_bits.count(bits[i])) { + sig[i] = State::S0; + did_something = true; + } + } + } + + if (did_something) { + log(" Replacing known input bits on port %s of cell %s: %s -> %s\n", log_id(portname), + log_id(muxinfo.cell), log_signal(muxinfo.cell->getPort(portname)), log_signal(sig)); + muxinfo.cell->setPort(portname, sig); } } - void eval_mux(knowledge_t &knowledge, int mux_idx) + void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { muxinfo_t &muxinfo = mux2info[mux_idx]; + // set input ports to constants if we find known active or inactive signals + if (do_replace_known) { + replace_known(knowledge, muxinfo, "\\A"); + replace_known(knowledge, muxinfo, "\\B"); + } + // if there is a constant activated port we just use it - for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++) + for (int port_idx = 0; port_idx < GetSize(muxinfo.ports); port_idx++) { portinfo_t &portinfo = muxinfo.ports[port_idx]; if (portinfo.const_activated) { - eval_mux_port(knowledge, mux_idx, port_idx); + eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); return; } } @@ -345,56 +411,39 @@ struct OptMuxtreeWorker // compare ports with known_active signals. if we find a match, only this // port can be active. do not include the last port (its the default port // that has no control signals). - for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++) + for (int port_idx = 0; port_idx < GetSize(muxinfo.ports)-1; port_idx++) { portinfo_t &portinfo = muxinfo.ports[port_idx]; - for (size_t i = 0; i < knowledge.known_active.size(); i++) { - if (list_is_subset(knowledge.known_active[i], portinfo.ctrl_sigs)) { - eval_mux_port(knowledge, mux_idx, port_idx); - return; - } + if (portinfo.const_deactivated) + continue; + if (knowledge.known_active.at(portinfo.ctrl_sig)) { + eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); + return; } } - // compare ports with known_inactive and known_active signals. If all control - // signals of the port are know_inactive or if the control signals of all other - // ports are known_active this port can't be activated. this loop includes the - // default port but no known_inactive match is performed on the default port. - for (size_t port_idx = 0; port_idx < muxinfo.ports.size(); port_idx++) + // eval all ports that could be activated (control signal is not in + // known_inactive or const_deactivated). + for (int port_idx = 0; port_idx < GetSize(muxinfo.ports); port_idx++) { portinfo_t &portinfo = muxinfo.ports[port_idx]; - - if (port_idx < muxinfo.ports.size()-1) { - bool found_non_known_inactive = false; - for (int i : portinfo.ctrl_sigs) - if (knowledge.known_inactive[i] == 0) - found_non_known_inactive = true; - if (!found_non_known_inactive) - continue; - } - - bool port_active = true; - std::vector<int> other_ctrl_sig; - for (size_t i = 0; i < muxinfo.ports.size()-1; i++) { - if (i == port_idx) + if (portinfo.const_deactivated) + continue; + if (port_idx < GetSize(muxinfo.ports)-1) + if (knowledge.known_inactive.at(portinfo.ctrl_sig)) continue; - other_ctrl_sig.insert(other_ctrl_sig.end(), - muxinfo.ports[i].ctrl_sigs.begin(), muxinfo.ports[i].ctrl_sigs.end()); - } - for (size_t i = 0; i < knowledge.known_active.size(); i++) { - if (list_is_subset(knowledge.known_active[i], other_ctrl_sig)) - port_active = false; - } - if (port_active) - eval_mux_port(knowledge, mux_idx, port_idx); + eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); } } void eval_root_mux(int mux_idx) { knowledge_t knowledge; - knowledge.visited_muxes.insert(mux_idx); - eval_mux(knowledge, mux_idx); + knowledge.known_inactive.resize(GetSize(bit2info)); + knowledge.known_active.resize(GetSize(bit2info)); + knowledge.visited_muxes.resize(GetSize(mux2info)); + knowledge.visited_muxes[mux_idx] = true; + eval_mux(knowledge, mux_idx, true, root_enable_muxes.at(mux_idx), 3); } }; @@ -413,24 +462,17 @@ struct OptMuxtreePass : public Pass { log("This pass only operates on completely selected modules without processes.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + virtual void execute(vector<std::string> args, RTLIL::Design *design) { log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); int total_count = 0; - 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)); + for (auto module : design->selected_whole_modules_warn()) { + if (module->has_processes_warn()) continue; - } - if (mod->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", log_id(mod)); - } else { - OptMuxtreeWorker worker(design, mod); - total_count += worker.removed_count; - } + OptMuxtreeWorker worker(design, module); + total_count += worker.removed_count; } if (total_count) design->scratchpad_set_bool("opt.did_something", true); @@ -438,3 +480,4 @@ struct OptMuxtreePass : public Pass { } } OptMuxtreePass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index e9e2bb39..5c36eb26 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -25,6 +25,9 @@ #include <stdio.h> #include <set> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct OptReduceWorker { RTLIL::Design *design; @@ -34,14 +37,14 @@ struct OptReduceWorker int total_count; bool did_something; - void opt_reduce(std::set<RTLIL::Cell*> &cells, SigSet<RTLIL::Cell*> &drivers, RTLIL::Cell *cell) + void opt_reduce(pool<RTLIL::Cell*> &cells, SigSet<RTLIL::Cell*> &drivers, RTLIL::Cell *cell) { if (cells.count(cell) == 0) return; cells.erase(cell); RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); - std::set<RTLIL::SigBit> new_sig_a_bits; + pool<RTLIL::SigBit> new_sig_a_bits; for (auto &bit : sig_a.to_sigbit_set()) { @@ -71,7 +74,7 @@ struct OptReduceWorker if (child_cell->type == cell->type) { opt_reduce(cells, drivers, child_cell); if (child_cell->getPort("\\Y")[0] == bit) { - std::set<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort("\\A")).to_sigbit_set(); + pool<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort("\\A")).to_sigbit_pool(); new_sig_a_bits.insert(child_sig_a_bits.begin(), child_sig_a_bits.end()); } else new_sig_a_bits.insert(RTLIL::State::S0); @@ -102,7 +105,7 @@ struct OptReduceWorker RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S")); RTLIL::SigSpec new_sig_b, new_sig_s; - std::set<RTLIL::SigSpec> handled_sig; + pool<RTLIL::SigSpec> handled_sig; handled_sig.insert(sig_a); for (int i = 0; i < sig_s.size(); i++) @@ -287,7 +290,7 @@ struct OptReduceWorker for (auto type : type_list) { SigSet<RTLIL::Cell*> drivers; - std::set<RTLIL::Cell*> cells; + pool<RTLIL::Cell*> cells; for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; @@ -343,6 +346,9 @@ struct OptReducePass : public Pass { log(" -fine\n"); log(" perform fine-grain optimizations\n"); log("\n"); + log(" -full\n"); + log(" alias for -fine\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -356,21 +362,22 @@ struct OptReducePass : public Pass { do_fine = true; continue; } + if (args[argidx] == "-full") { + do_fine = true; + continue; + } break; } extra_args(args, argidx, design); int total_count = 0; - for (auto &mod_it : design->modules_) { - if (!design->selected(mod_it.second)) - continue; - do { - OptReduceWorker worker(design, mod_it.second, do_fine); + for (auto module : design->selected_modules()) + while (1) { + OptReduceWorker worker(design, module, 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); @@ -378,3 +385,4 @@ struct OptReducePass : public Pass { } } OptReducePass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 48f406f6..5f52bb8d 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -23,10 +23,13 @@ #include <stdlib.h> #include <stdio.h> -static SigMap assign_map, dff_init_map; -static SigSet<RTLIL::Cell*> mux_drivers; +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN -static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) +SigMap assign_map, dff_init_map; +SigSet<RTLIL::Cell*> mux_drivers; + +bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) { RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r; RTLIL::Const val_cp, val_rp, val_rv; @@ -80,7 +83,7 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx); } - if (dff->type == "$dff" && mux_drivers.has(sig_d)) { + if (dff->type == "$dff" && mux_drivers.has(sig_d) && !has_init) { std::set<RTLIL::Cell*> muxes; mux_drivers.find(sig_d, muxes); for (auto mux : muxes) { @@ -215,3 +218,4 @@ struct OptRmdffPass : public Pass { } } OptRmdffPass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index 4b76a5a2..cce97d65 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -28,6 +28,9 @@ #define USE_CELL_HASH_CACHE +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct OptShareWorker { RTLIL::Design *design; @@ -38,7 +41,7 @@ struct OptShareWorker CellTypes ct; int total_count; #ifdef USE_CELL_HASH_CACHE - std::map<const RTLIL::Cell*, std::string> cell_hash_cache; + dict<const RTLIL::Cell*, std::string> cell_hash_cache; #endif #ifdef USE_CELL_HASH_CACHE @@ -64,8 +67,8 @@ struct OptShareWorker for (auto &it : cell->parameters) hash_string += "P " + it.first.str() + "=" + it.second.as_string() + "\n"; - const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections(); - std::map<RTLIL::IdString, RTLIL::SigSpec> alt_conn; + const dict<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections(); + dict<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" || cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") { @@ -124,12 +127,14 @@ struct OptShareWorker #endif if (cell1->parameters != cell2->parameters) { - lt = cell1->parameters < cell2->parameters; + std::map<RTLIL::IdString, RTLIL::Const> p1(cell1->parameters.begin(), cell1->parameters.end()); + std::map<RTLIL::IdString, RTLIL::Const> p2(cell2->parameters.begin(), cell2->parameters.end()); + lt = p1 < p2; return true; } - std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections(); - std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); + dict<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections(); + dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); for (auto &it : conn1) { if (ct.cell_output(cell1->type, it.first)) @@ -168,7 +173,9 @@ struct OptShareWorker } if (conn1 != conn2) { - lt = conn1 < conn2; + std::map<RTLIL::IdString, RTLIL::SigSpec> c1(conn1.begin(), conn1.end()); + std::map<RTLIL::IdString, RTLIL::SigSpec> c2(conn2.begin(), conn2.end()); + lt = c1 < c2; return true; } @@ -193,7 +200,7 @@ struct OptShareWorker if (!ct.cell_known(cell1->type)) return cell1 < cell2; - if (cell1->get_bool_attribute("\\keep") || cell2->get_bool_attribute("\\keep")) + if (cell1->has_keep_attr() || cell2->has_keep_attr()) return cell1 < cell2; bool lt; @@ -263,6 +270,7 @@ struct OptShareWorker } } log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + cell_hash_cache.erase(cell); module->remove(cell); total_count++; } else { @@ -306,10 +314,8 @@ struct OptSharePass : public Pass { extra_args(args, argidx, design); int total_count = 0; - for (auto &mod_it : design->modules_) { - if (!design->selected(mod_it.second)) - continue; - OptShareWorker worker(design, mod_it.second, mode_nomux); + for (auto module : design->selected_modules()) { + OptShareWorker worker(design, module, mode_nomux); total_count += worker.total_count; } @@ -319,3 +325,4 @@ struct OptSharePass : public Pass { } } OptSharePass; +PRIVATE_NAMESPACE_END diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 74b049bb..bf406bcd 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -22,22 +22,27 @@ #include "kernel/sigtools.h" #include "kernel/modtools.h" #include "kernel/utils.h" +#include "kernel/macc.h" +USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +typedef RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell> cell_ptr_cmp; +typedef std::pair<RTLIL::SigSpec, RTLIL::Const> ssc_pair_t; + 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; + pool<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops; }; struct ShareWorker { ShareWorkerConfig config; - std::set<RTLIL::IdString> generic_ops; + pool<RTLIL::IdString> generic_ops; RTLIL::Design *design; RTLIL::Module *module; @@ -46,30 +51,32 @@ struct ShareWorker ModWalker modwalker; ModIndex mi; - std::set<RTLIL::Cell*> cells_to_remove; - std::set<RTLIL::Cell*> recursion_state; + pool<RTLIL::Cell*> cells_to_remove; + pool<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; + std::map<RTLIL::Cell*, std::set<RTLIL::Cell*, cell_ptr_cmp>, cell_ptr_cmp> topo_cell_drivers; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*, cell_ptr_cmp>> topo_bit_drivers; + + std::vector<std::pair<RTLIL::SigBit, RTLIL::SigBit>> exclusive_ctrls; // ------------------------------------------------------------------------------ // Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree // ------------------------------------------------------------------------------ - std::set<RTLIL::SigBit> terminal_bits; + pool<RTLIL::SigBit> terminal_bits; void find_terminal_bits() { - std::set<RTLIL::SigBit> queue_bits; - std::set<RTLIL::Cell*> visited_cells; + pool<RTLIL::SigBit> queue_bits; + pool<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]; + pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second]; queue_bits.insert(bits.begin(), bits.end()); } @@ -77,19 +84,19 @@ struct ShareWorker while (!queue_bits.empty()) { - std::set<ModWalker::PortBit> portbits; + pool<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(); + pool<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort("\\S")).to_sigbit_pool(); 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]; + pool<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); @@ -100,17 +107,251 @@ struct ShareWorker // --------------------------------------------------- + // Code for sharing and comparing MACC cells + // --------------------------------------------------- + + static int bits_macc_port(const Macc::port_t &p, int width) + { + if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) + return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width); + return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2; + } + + static int bits_macc(const Macc &m, int width) + { + int bits = GetSize(m.bit_ports); + for (auto &p : m.ports) + bits += bits_macc_port(p, width); + return bits; + } + + static int bits_macc(RTLIL::Cell *c) + { + Macc m(c); + int width = GetSize(c->getPort("\\Y")); + return bits_macc(m, width); + } + + static bool cmp_macc_ports(const Macc::port_t &p1, const Macc::port_t &p2) + { + bool mul1 = GetSize(p1.in_a) && GetSize(p1.in_b); + bool mul2 = GetSize(p2.in_a) && GetSize(p2.in_b); + + int w1 = mul1 ? GetSize(p1.in_a) * GetSize(p1.in_b) : GetSize(p1.in_a) + GetSize(p1.in_b); + int w2 = mul2 ? GetSize(p2.in_a) * GetSize(p2.in_b) : GetSize(p2.in_a) + GetSize(p2.in_b); + + if (mul1 != mul2) + return mul1; + + if (w1 != w2) + return w1 > w2; + + if (p1.is_signed != p2.is_signed) + return p1.is_signed < p2.is_signed; + + if (p1.do_subtract != p2.do_subtract) + return p1.do_subtract < p2.do_subtract; + + if (p1.in_a != p2.in_a) + return p1.in_a < p2.in_a; + + if (p1.in_b != p2.in_b) + return p1.in_b < p2.in_b; + + return false; + } + + int share_macc_ports(Macc::port_t &p1, Macc::port_t &p2, int w1, int w2, + RTLIL::SigSpec act = RTLIL::SigSpec(), Macc *supermacc = nullptr, pool<RTLIL::Cell*> *supercell_aux = nullptr) + { + if (p1.do_subtract != p2.do_subtract) + return -1; + + bool mul1 = GetSize(p1.in_a) && GetSize(p1.in_b); + bool mul2 = GetSize(p2.in_a) && GetSize(p2.in_b); + + if (mul1 != mul2) + return -1; + + bool force_signed = false, force_not_signed = false; + + if ((GetSize(p1.in_a) && GetSize(p1.in_a) < w1) || (GetSize(p1.in_b) && GetSize(p1.in_b) < w1)) { + if (p1.is_signed) + force_signed = true; + else + force_not_signed = true; + } + + if ((GetSize(p2.in_a) && GetSize(p2.in_a) < w2) || (GetSize(p2.in_b) && GetSize(p2.in_b) < w2)) { + if (p2.is_signed) + force_signed = true; + else + force_not_signed = true; + } + + if (force_signed && force_not_signed) + return -1; + + if (supermacc) + { + RTLIL::SigSpec sig_a1 = p1.in_a, sig_b1 = p1.in_b; + RTLIL::SigSpec sig_a2 = p2.in_a, sig_b2 = p2.in_b; + + RTLIL::SigSpec sig_a = GetSize(sig_a1) > GetSize(sig_a2) ? sig_a1 : sig_a2; + RTLIL::SigSpec sig_b = GetSize(sig_b1) > GetSize(sig_b2) ? sig_b1 : sig_b2; + + sig_a1.extend_u0(GetSize(sig_a), p1.is_signed); + sig_b1.extend_u0(GetSize(sig_b), p1.is_signed); + + sig_a2.extend_u0(GetSize(sig_a), p2.is_signed); + sig_b2.extend_u0(GetSize(sig_b), p2.is_signed); + + if (supercell_aux && GetSize(sig_a)) { + sig_a = module->addWire(NEW_ID, GetSize(sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, sig_a2, sig_a1, act, sig_a)); + } + + if (supercell_aux && GetSize(sig_b)) { + sig_b = module->addWire(NEW_ID, GetSize(sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, sig_b2, sig_b1, act, sig_b)); + } + + Macc::port_t p; + p.in_a = sig_a; + p.in_b = sig_b; + p.is_signed = force_signed; + p.do_subtract = p1.do_subtract; + supermacc->ports.push_back(p); + } + + int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); + + for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) + if (p1.in_a[i] == p2.in_a[i] && score > 0) + score--; + + for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) + if (p1.in_b[i] == p2.in_b[i] && score > 0) + score--; + + return score; + } + + int share_macc(RTLIL::Cell *c1, RTLIL::Cell *c2, + RTLIL::SigSpec act = RTLIL::SigSpec(), RTLIL::Cell *supercell = nullptr, pool<RTLIL::Cell*> *supercell_aux = nullptr) + { + Macc m1(c1), m2(c2), supermacc; + + int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y")); + int width = std::max(w1, w2); + + m1.optimize(w1); + m2.optimize(w2); + + std::sort(m1.ports.begin(), m1.ports.end(), cmp_macc_ports); + std::sort(m2.ports.begin(), m2.ports.end(), cmp_macc_ports); + + std::set<int> m1_unmapped, m2_unmapped; + + for (int i = 0; i < GetSize(m1.ports); i++) + m1_unmapped.insert(i); + + for (int i = 0; i < GetSize(m2.ports); i++) + m2_unmapped.insert(i); + + while (1) + { + int best_i = -1, best_j = -1, best_score = 0; + + for (int i : m1_unmapped) + for (int j : m2_unmapped) { + int score = share_macc_ports(m1.ports[i], m2.ports[j], w1, w2); + if (score >= 0 && (best_i < 0 || best_score > score)) + best_i = i, best_j = j, best_score = score; + } + + if (best_i >= 0) { + m1_unmapped.erase(best_i); + m2_unmapped.erase(best_j); + share_macc_ports(m1.ports[best_i], m2.ports[best_j], w1, w2, act, &supermacc, supercell_aux); + } else + break; + } + + for (int i : m1_unmapped) + { + RTLIL::SigSpec sig_a = m1.ports[i].in_a; + RTLIL::SigSpec sig_b = m1.ports[i].in_b; + + if (supercell_aux && GetSize(sig_a)) { + sig_a = module->addWire(NEW_ID, GetSize(sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_a)), m1.ports[i].in_a, act, sig_a)); + } + + if (supercell_aux && GetSize(sig_b)) { + sig_b = module->addWire(NEW_ID, GetSize(sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_b)), m1.ports[i].in_b, act, sig_b)); + } + + Macc::port_t p; + p.in_a = sig_a; + p.in_b = sig_b; + p.is_signed = m1.ports[i].is_signed; + p.do_subtract = m1.ports[i].do_subtract; + supermacc.ports.push_back(p); + } + + for (int i : m2_unmapped) + { + RTLIL::SigSpec sig_a = m2.ports[i].in_a; + RTLIL::SigSpec sig_b = m2.ports[i].in_b; + + if (supercell_aux && GetSize(sig_a)) { + sig_a = module->addWire(NEW_ID, GetSize(sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_a, RTLIL::SigSpec(0, GetSize(sig_a)), act, sig_a)); + } + + if (supercell_aux && GetSize(sig_b)) { + sig_b = module->addWire(NEW_ID, GetSize(sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_b, RTLIL::SigSpec(0, GetSize(sig_b)), act, sig_b)); + } + + Macc::port_t p; + p.in_a = sig_a; + p.in_b = sig_b; + p.is_signed = m2.ports[i].is_signed; + p.do_subtract = m2.ports[i].do_subtract; + supermacc.ports.push_back(p); + } + + if (supercell) + { + RTLIL::SigSpec sig_y = module->addWire(NEW_ID, width); + + supercell_aux->insert(module->addPos(NEW_ID, sig_y, c1->getPort("\\Y"))); + supercell_aux->insert(module->addPos(NEW_ID, sig_y, c2->getPort("\\Y"))); + + supercell->setParam("\\Y_WIDTH", width); + supercell->setPort("\\Y", sig_y); + + supermacc.optimize(width); + supermacc.to_cell(supercell); + } + + return bits_macc(supermacc, width); + } + + + // --------------------------------------------------- // Find shareable cells and compatible groups of cells // --------------------------------------------------- - std::set<RTLIL::Cell*, RTLIL::sort_by_name_str<RTLIL::Cell>> shareable_cells; + pool<RTLIL::Cell*> shareable_cells; void find_shareable_cells() { - for (auto &it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = it.second; - if (!design->selected(module, cell) || !modwalker.ct.cell_known(cell->type)) continue; @@ -128,7 +369,9 @@ struct ShareWorker } if (cell->type == "$memrd") { - if (!cell->parameters.at("\\CLK_ENABLE").as_bool()) + if (cell->parameters.at("\\CLK_ENABLE").as_bool()) + continue; + if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const()) shareable_cells.insert(cell); continue; } @@ -146,7 +389,7 @@ struct ShareWorker } if (generic_ops.count(cell->type)) { - if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10) + if (config.opt_aggressive) shareable_cells.insert(cell); continue; } @@ -183,7 +426,7 @@ struct ShareWorker return true; } - if (config.generic_bin_ops.count(c1->type)) + if (config.generic_bin_ops.count(c1->type) || c1->type == "$alu") { if (!config.opt_aggressive) { @@ -229,6 +472,14 @@ struct ShareWorker return true; } + if (c1->type == "$macc") + { + if (!config.opt_aggressive) + if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) 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; @@ -253,7 +504,7 @@ struct ShareWorker // Create replacement cell // ----------------------- - RTLIL::Cell *make_supercell(RTLIL::Cell *c1, RTLIL::Cell *c2, RTLIL::SigSpec act, std::set<RTLIL::Cell*> &supercell_aux) + RTLIL::Cell *make_supercell(RTLIL::Cell *c1, RTLIL::Cell *c2, RTLIL::SigSpec act, pool<RTLIL::Cell*> &supercell_aux) { log_assert(c1->type == c2->type); @@ -306,7 +557,7 @@ struct ShareWorker return supercell; } - if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type)) + if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == "$alu") { bool modified_src_cells = false; @@ -409,6 +660,8 @@ struct ShareWorker supercell_aux.insert(module->addMux(NEW_ID, b2, b1, act, b)); RTLIL::Wire *y = module->addWire(NEW_ID, y_width); + RTLIL::Wire *x = c1->type == "$alu" ? module->addWire(NEW_ID, y_width) : nullptr; + RTLIL::Wire *co = c1->type == "$alu" ? module->addWire(NEW_ID, y_width) : nullptr; RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type); supercell->parameters["\\A_SIGNED"] = a_signed; @@ -419,15 +672,39 @@ struct ShareWorker supercell->setPort("\\A", a); supercell->setPort("\\B", b); supercell->setPort("\\Y", y); + if (c1->type == "$alu") { + RTLIL::Wire *ci = module->addWire(NEW_ID), *bi = module->addWire(NEW_ID); + supercell_aux.insert(module->addMux(NEW_ID, c2->getPort("\\CI"), c1->getPort("\\CI"), act, ci)); + supercell_aux.insert(module->addMux(NEW_ID, c2->getPort("\\BI"), c1->getPort("\\BI"), act, bi)); + supercell->setPort("\\CI", ci); + supercell->setPort("\\BI", bi); + supercell->setPort("\\CO", co); + supercell->setPort("\\X", x); + } supercell->check(); supercell_aux.insert(module->addPos(NEW_ID, y, y1)); supercell_aux.insert(module->addPos(NEW_ID, y, y2)); + if (c1->type == "$alu") { + supercell_aux.insert(module->addPos(NEW_ID, co, c1->getPort("\\CO"))); + supercell_aux.insert(module->addPos(NEW_ID, co, c2->getPort("\\CO"))); + supercell_aux.insert(module->addPos(NEW_ID, x, c1->getPort("\\X"))); + supercell_aux.insert(module->addPos(NEW_ID, x, c2->getPort("\\X"))); + } supercell_aux.insert(supercell); return supercell; } + if (c1->type == "$macc") + { + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type); + supercell_aux.insert(supercell); + share_macc(c1, c2, act, supercell, &supercell_aux); + supercell->check(); + return supercell; + } + if (c1->type == "$memrd") { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); @@ -444,20 +721,20 @@ struct ShareWorker // Finding forbidden control inputs for a cell // ------------------------------------------- - std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> forbidden_controls_cache; + std::map<RTLIL::Cell*, pool<RTLIL::SigBit>, cell_ptr_cmp> forbidden_controls_cache; - const std::set<RTLIL::SigBit> &find_forbidden_controls(RTLIL::Cell *cell) + const pool<RTLIL::SigBit> &find_forbidden_controls(RTLIL::Cell *cell) { if (recursion_state.count(cell)) { - static std::set<RTLIL::SigBit> empty_controls_set; + static pool<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; + pool<ModWalker::PortBit> pbits; + pool<RTLIL::Cell*> consumer_cells; modwalker.get_consumers(pbits, modwalker.cell_outputs[cell]); @@ -471,11 +748,11 @@ struct ShareWorker for (auto c : consumer_cells) if (fwd_ct.cell_known(c->type)) { - const std::set<RTLIL::SigBit> &bits = find_forbidden_controls(c); + const pool<RTLIL::SigBit> &bits = find_forbidden_controls(c); forbidden_controls_cache[cell].insert(bits.begin(), bits.end()); } - log_assert(recursion_state.count(cell)); + log_assert(recursion_state.count(cell) != 0); recursion_state.erase(cell); return forbidden_controls_cache[cell]; @@ -486,14 +763,14 @@ struct ShareWorker // Finding control inputs and activation pattern for a cell // -------------------------------------------------------- - std::map<RTLIL::Cell*, std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>>> activation_patterns_cache; + std::map<RTLIL::Cell*, pool<ssc_pair_t>, cell_ptr_cmp> activation_patterns_cache; - bool sort_check_activation_pattern(std::pair<RTLIL::SigSpec, RTLIL::Const> &p) + bool sort_check_activation_pattern(ssc_pair_t &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++) { + for (int i = 0; i < GetSize(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) @@ -512,30 +789,30 @@ struct ShareWorker return true; } - void optimize_activation_patterns(std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> & /* patterns */) + void optimize_activation_patterns(pool<ssc_pair_t> & /* 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) + const pool<ssc_pair_t> &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; + static pool<ssc_pair_t> 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; + const pool<RTLIL::SigBit> &cell_out_bits = modwalker.cell_outputs[cell]; + pool<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>()); + activation_patterns_cache[cell].insert(ssc_pair_t()); return activation_patterns_cache.at(cell); } for (auto &pbit : modwalker.signal_consumers[bit]) { @@ -551,7 +828,7 @@ struct ShareWorker for (auto c : driven_data_muxes) { - const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &c_patterns = find_cell_activation_patterns(c, indent); + const pool<ssc_pair_t> &c_patterns = find_cell_activation_patterns(c, indent); bool used_in_a = false; std::set<int> used_in_b_parts; @@ -565,13 +842,13 @@ struct ShareWorker if (cell_out_bits.count(bit)) used_in_a = true; - for (int i = 0; i < SIZE(sig_b); i++) + for (int i = 0; i < GetSize(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++) + for (int i = 0; i < GetSize(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); @@ -586,11 +863,11 @@ struct ShareWorker } for (auto c : driven_cells) { - const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &c_patterns = find_cell_activation_patterns(c, indent); + const pool<ssc_pair_t> &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)); + log_assert(recursion_state.count(cell) != 0); recursion_state.erase(cell); optimize_activation_patterns(activation_patterns_cache[cell]); @@ -604,7 +881,7 @@ struct ShareWorker return activation_patterns_cache[cell]; } - RTLIL::SigSpec bits_from_activation_patterns(const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &activation_patterns) + RTLIL::SigSpec bits_from_activation_patterns(const pool<ssc_pair_t> &activation_patterns) { std::set<RTLIL::SigBit> all_bits; for (auto &it : activation_patterns) { @@ -619,15 +896,15 @@ struct ShareWorker 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) + void filter_activation_patterns(pool<ssc_pair_t> &out, + const pool<ssc_pair_t> &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; + ssc_pair_t new_p; - for (int i = 0; i < SIZE(p_first); i++) + for (int i = 0; i < GetSize(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)); @@ -637,7 +914,7 @@ struct ShareWorker } } - RTLIL::SigSpec make_cell_activation_logic(const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &activation_patterns, std::set<RTLIL::Cell*> &supercell_aux) + RTLIL::SigSpec make_cell_activation_logic(const pool<ssc_pair_t> &activation_patterns, pool<RTLIL::Cell*> &supercell_aux) { RTLIL::Wire *all_cases_wire = module->addWire(NEW_ID, 0); @@ -665,14 +942,14 @@ struct ShareWorker ct.setup_internals(); ct.setup_stdcells(); - TopoSort<RTLIL::Cell*> toposort; + TopoSort<RTLIL::Cell*, cell_ptr_cmp> 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; + dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_bits; + dict<RTLIL::SigBit, pool<RTLIL::Cell*>> bit_to_cells; for (auto cell : module->cells()) if (ct.cell_known(cell->type)) @@ -709,7 +986,7 @@ struct ShareWorker return found_scc; } - bool find_in_input_cone_worker(RTLIL::Cell *root, RTLIL::Cell *needle, std::set<RTLIL::Cell*> &stop) + bool find_in_input_cone_worker(RTLIL::Cell *root, RTLIL::Cell *needle, pool<RTLIL::Cell*> &stop) { if (root == needle) return true; @@ -727,7 +1004,7 @@ struct ShareWorker bool find_in_input_cone(RTLIL::Cell *root, RTLIL::Cell *needle) { - std::set<RTLIL::Cell*> stop; + pool<RTLIL::Cell*> stop; return find_in_input_cone_worker(root, needle, stop); } @@ -737,12 +1014,12 @@ struct ShareWorker ct.setup_internals(); ct.setup_stdcells(); - std::set<RTLIL::Cell*> queue, covered; + pool<RTLIL::Cell*> queue, covered; queue.insert(cell); while (!queue.empty()) { - std::set<RTLIL::Cell*> new_queue; + pool<RTLIL::Cell*> new_queue; for (auto c : queue) { if (!ct.cell_known(c->type)) @@ -775,14 +1052,25 @@ struct ShareWorker // Setup and run // ------------- + void remove_cell(Cell *cell) + { + shareable_cells.erase(cell); + forbidden_controls_cache.erase(cell); + activation_patterns_cache.erase(cell); + module->remove(cell); + } + ShareWorker(ShareWorkerConfig config, RTLIL::Design *design, RTLIL::Module *module) : config(config), design(design), module(module), mi(module) { + #ifndef NDEBUG bool before_scc = module_has_scc(); + #endif 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()); + generic_ops.insert(config.generic_other_ops.begin(), config.generic_other_ops.end()); fwd_ct.setup_internals(); @@ -805,16 +1093,23 @@ struct ShareWorker return; log("Found %d cells in module %s that may be considered for resource sharing.\n", - SIZE(shareable_cells), log_id(module)); + GetSize(shareable_cells), log_id(module)); + + for (auto cell : module->cells()) + if (cell->type == "$pmux") + for (auto bit : cell->getPort("\\S")) + for (auto other_bit : cell->getPort("\\S")) + if (bit < other_bit) + exclusive_ctrls.push_back(std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit, other_bit)); 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)); + log(" Analyzing resource sharing options for %s (%s):\n", log_id(cell), log_id(cell->type)); - const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &cell_activation_patterns = find_cell_activation_patterns(cell, " "); + const pool<ssc_pair_t> &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()) { @@ -823,12 +1118,12 @@ struct ShareWorker continue; } - if (cell_activation_patterns.count(std::pair<RTLIL::SigSpec, RTLIL::Const>())) { + if (cell_activation_patterns.count(ssc_pair_t())) { 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)); + log(" Found %d activation_patterns using ctrl signal %s.\n", GetSize(cell_activation_patterns), log_signal(cell_activation_signals)); std::vector<RTLIL::Cell*> candidates; find_shareable_partners(candidates, cell); @@ -838,16 +1133,16 @@ struct ShareWorker continue; } - log(" Found %d candidates:", SIZE(candidates)); + log(" Found %d candidates:", GetSize(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)); + log(" Analyzing resource sharing with %s (%s):\n", log_id(other_cell), log_id(other_cell->type)); - const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " "); + const pool<ssc_pair_t> &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()) { @@ -857,17 +1152,17 @@ struct ShareWorker continue; } - if (other_cell_activation_patterns.count(std::pair<RTLIL::SigSpec, RTLIL::Const>())) { + if (other_cell_activation_patterns.count(ssc_pair_t())) { 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)); + GetSize(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); + const pool<RTLIL::SigBit> &cell_forbidden_controls = find_forbidden_controls(cell); + const pool<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()); @@ -876,8 +1171,8 @@ struct ShareWorker 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; + pool<ssc_pair_t> filtered_cell_activation_patterns; + pool<ssc_pair_t> 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); @@ -885,10 +1180,10 @@ struct ShareWorker optimize_activation_patterns(filtered_cell_activation_patterns); optimize_activation_patterns(filtered_other_cell_activation_patterns); - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); - std::set<RTLIL::Cell*> sat_cells; + pool<RTLIL::Cell*> sat_cells; std::set<RTLIL::SigBit> bits_queue; std::vector<int> cell_active, other_cell_active; @@ -896,13 +1191,13 @@ struct ShareWorker 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))); + 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))); + other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); all_ctrl_signals.append(p.first); } @@ -914,7 +1209,7 @@ struct ShareWorker while (!bits_queue.empty()) { - std::set<ModWalker::PortBit> portbits; + pool<ModWalker::PortBit> portbits; modwalker.get_drivers(portbits, bits_queue); bits_queue.clear(); @@ -932,34 +1227,44 @@ struct ShareWorker break; } - if (!ez.solve(ez.expression(ez.OpOr, cell_active))) { + for (auto it : exclusive_ctrls) + if (satgen.importedSigBit(it.first) && satgen.importedSigBit(it.second)) { + log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second)); + int sub1 = satgen.importSigBit(it.first); + int sub2 = satgen.importSigBit(it.second); + ez->assume(ez->NOT(ez->AND(sub1, sub2))); + } + + 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))) { + 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(); + 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))); + int sub1 = ez->expression(ez->OpOr, cell_active); + int sub2 = ez->expression(ez->OpOr, other_cell_active); + ez->assume(ez->AND(sub1, sub2)); log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", - SIZE(sat_cells), ez.numCnfVariables(), ez.numCnfClauses()); + GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses()); - if (ez.solve(sat_model, sat_model_values)) { + 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(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); + for (int i = GetSize(sat_model_values)-1; i >= 0; i--) log("%c", sat_model_values[i] ? '1' : '0'); log("\n"); continue; @@ -989,7 +1294,7 @@ struct ShareWorker other_cell_select_score += p.first.size(); RTLIL::Cell *supercell; - std::set<RTLIL::Cell*> supercell_aux; + pool<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); @@ -1016,11 +1321,11 @@ struct ShareWorker cells_to_remove.erase(other_cell); shareable_cells.insert(other_cell); for (auto cc : supercell_aux) - module->remove(cc); + remove_cell(cc); continue; } - std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> supercell_activation_patterns; + pool<ssc_pair_t> 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); @@ -1045,17 +1350,19 @@ struct ShareWorker } if (!cells_to_remove.empty()) { - log("Removing %d cells in module %s:\n", SIZE(cells_to_remove), log_id(module)); + log("Removing %d cells in module %s:\n", GetSize(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); + remove_cell(c); } } log_assert(recursion_state.empty()); + #ifndef NDEBUG bool after_scc = before_scc || module_has_scc(); log_assert(before_scc == after_scc); + #endif } }; @@ -1137,6 +1444,9 @@ struct SharePass : public Pass { config.generic_cbin_ops.insert("$logic_and"); config.generic_cbin_ops.insert("$logic_or"); + config.generic_other_ops.insert("$alu"); + config.generic_other_ops.insert("$macc"); + log_header("Executing SHARE pass (SAT-based resource sharing).\n"); size_t argidx; @@ -1168,4 +1478,3 @@ struct SharePass : public Pass { } SharePass; PRIVATE_NAMESPACE_END - diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 58a6d1b0..fc91f368 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -28,18 +28,18 @@ PRIVATE_NAMESPACE_BEGIN struct WreduceConfig { - std::set<IdString> supported_cell_types; + pool<IdString> supported_cell_types; WreduceConfig() { - supported_cell_types = { + supported_cell_types = pool<IdString>({ "$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" - }; + }); } }; @@ -65,20 +65,20 @@ struct WreduceWorker SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); std::vector<SigBit> bits_removed; - for (int i = SIZE(sig_y)-1; i >= 0; i--) + for (int i = GetSize(sig_y)-1; i >= 0; i--) { auto info = mi.query(sig_y[i]); - if (!info->is_output && SIZE(info->ports) <= 1) { + if (!info->is_output && GetSize(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]) + for (int k = 0; k < GetSize(sig_s); k++) { + if (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx && ref != sig_b[k*GetSize(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 (sig_b[k*GetSize(sig_a) + i] != Sx) + ref = sig_b[k*GetSize(sig_a) + i]; } if (0) no_match_ab: @@ -90,10 +90,10 @@ struct WreduceWorker return; SigSpec sig_removed; - for (int i = SIZE(bits_removed)-1; i >= 0; i--) + for (int i = GetSize(bits_removed)-1; i >= 0; i--) sig_removed.append_bit(bits_removed[i]); - if (SIZE(bits_removed) == SIZE(sig_y)) { + if (GetSize(bits_removed) == GetSize(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); @@ -101,10 +101,10 @@ struct WreduceWorker } 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)); + GetSize(sig_removed), GetSize(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); + int n_removed = GetSize(sig_removed); + int n_kept = GetSize(sig_y) - GetSize(sig_removed); SigSpec new_work_queue_bits; new_work_queue_bits.append(sig_a.extract(n_kept, n_removed)); @@ -114,9 +114,9 @@ struct WreduceWorker 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 (int k = 0; k < GetSize(sig_s); k++) { + new_sig_b.append(sig_b.extract(k*GetSize(sig_a), n_kept)); + new_work_queue_bits.append(sig_b.extract(k*GetSize(sig_a) + n_kept, n_removed)); } for (auto bit : new_work_queue_bits) @@ -139,24 +139,24 @@ struct WreduceWorker port_signed = false; int bits_removed = 0; - if (SIZE(sig) > max_port_size) { - bits_removed = SIZE(sig) - max_port_size; + if (GetSize(sig) > max_port_size) { + bits_removed = GetSize(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++; + while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == sig[GetSize(sig)-2]) + work_queue_bits.insert(sig[GetSize(sig)-1]), sig.remove(GetSize(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++; + while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == S0) + work_queue_bits.insert(sig[GetSize(sig)-1]), sig.remove(GetSize(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)); + bits_removed, GetSize(sig) + bits_removed, port, log_id(module), log_id(cell), log_id(cell->type)); cell->setPort(stringf("\\%c", port), sig); did_something = true; } @@ -175,12 +175,12 @@ struct WreduceWorker // 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; + int max_port_a_size = cell->hasPort("\\A") ? GetSize(cell->getPort("\\A")) : -1; + int max_port_b_size = cell->hasPort("\\B") ? GetSize(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"))); + max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y"))); + max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y"))); } bool port_a_signed = false; @@ -201,14 +201,14 @@ struct WreduceWorker 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) + while (GetSize(sig) > 0) { - auto info = mi.query(sig[SIZE(sig)-1]); + auto info = mi.query(sig[GetSize(sig)-1]); - if (info->is_output || SIZE(info->ports) > 1) + if (info->is_output || GetSize(info->ports) > 1) break; - sig.remove(SIZE(sig)-1); + sig.remove(GetSize(sig)-1); bits_removed++; } } @@ -218,8 +218,8 @@ struct WreduceWorker 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")); + if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A")); + if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B")); int max_y_size = std::max(a_size, b_size); @@ -229,14 +229,14 @@ struct WreduceWorker 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); + while (GetSize(sig) > 1 && GetSize(sig) > max_y_size) { + module->connect(sig[GetSize(sig)-1], is_signed ? sig[GetSize(sig)-2] : S0); + sig.remove(GetSize(sig)-1); bits_removed++; } } - if (SIZE(sig) == 0) { + if (GetSize(sig) == 0) { log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); module->remove(cell); return; @@ -244,7 +244,7 @@ struct WreduceWorker 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)); + bits_removed, GetSize(sig) + bits_removed, log_id(module), log_id(cell), log_id(cell->type)); cell->setPort("\\Y", sig); did_something = true; } @@ -281,6 +281,10 @@ struct WreduceWorker work_queue_cells.insert(port.cell); } + pool<SigSpec> complete_wires; + for (auto w : module->wires()) + complete_wires.insert(mi.sigmap(w)); + for (auto w : module->selected_wires()) { int unused_top_bits = 0; @@ -288,27 +292,30 @@ struct WreduceWorker if (w->port_id > 0 || count_nontrivial_wire_attrs(w) > 0) continue; - for (int i = SIZE(w)-1; i >= 0; i--) { + for (int i = GetSize(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)) + if (info && (info->is_input || info->is_output || GetSize(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); - } + if (unused_top_bits == 0 || unused_top_bits == GetSize(w)) + continue; + + if (complete_wires[mi.sigmap(w).extract(0, GetSize(w) - unused_top_bits)]) + continue; + + log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); + Wire *nw = module->addWire(NEW_ID, GetSize(w) - unused_top_bits); + module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); + module->swap_names(w, nw); } } }; struct WreducePass : public Pass { - WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { } + WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index dfbc78ea..397fe46a 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -5,5 +5,6 @@ OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_init.o OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o +OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 36d4141b..40b2b30f 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -22,6 +22,9 @@ #include <stdlib.h> #include <stdio.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct ProcPass : public Pass { ProcPass() : Pass("proc", "translate processes to netlists") { } virtual void help() @@ -37,10 +40,12 @@ struct ProcPass : public Pass { log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); + log(" proc_dlatch\n"); log(" proc_dff\n"); log(" proc_clean\n"); log("\n"); - log("This replaces the processes in the design with multiplexers and flip-flops.\n"); + log("This replaces the processes in the design with multiplexers,\n"); + log("flip-flops and latches.\n"); log("\n"); log("The following options are supported:\n"); log("\n"); @@ -74,6 +79,7 @@ struct ProcPass : public Pass { else Pass::call(design, "proc_arst -global_arst " + global_arst); Pass::call(design, "proc_mux"); + Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); Pass::call(design, "proc_clean"); @@ -81,3 +87,4 @@ struct ProcPass : public Pass { } } ProcPass; +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index f11b328f..27c6b3bc 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -23,10 +23,14 @@ #include <stdlib.h> #include <stdio.h> -// defined in proc_clean.cc +YOSYS_NAMESPACE_BEGIN extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth); +YOSYS_NAMESPACE_END -static bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity) { if (signal.size() != 1) return false; @@ -81,7 +85,7 @@ static bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSp return false; } -static void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown) +void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown) { for (auto &action : cs->actions) { if (unknown) @@ -114,7 +118,7 @@ static void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::S } } -static void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity) +void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity) { for (auto sw : cs->switches) { bool this_polarity = polarity; @@ -149,7 +153,7 @@ static void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigS } } -static void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) +void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) { restart_proc_arst: if (proc->root_case.switches.size() != 1) @@ -170,7 +174,7 @@ restart_proc_arst: for (auto &action : sync->actions) { RTLIL::SigSpec rspec = action.second; RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); - for (int i = 0; i < SIZE(rspec); i++) + for (int i = 0; i < GetSize(rspec); i++) if (rspec[i].wire == NULL) rval[i] = rspec[i]; RTLIL::SigSpec last_rval; @@ -258,7 +262,7 @@ struct ProcArstPass : public Pass { 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); + value.extend_u0(chunk.wire->width, false); arst_sig.append(chunk); arst_val.append(value.extract(chunk.offset, chunk.width)); } @@ -280,3 +284,4 @@ struct ProcArstPass : public Pass { } } ProcArstPass; +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 1e3dd9ce..82716cd0 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -22,8 +22,12 @@ #include <stdlib.h> #include <stdio.h> -extern void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count, int max_depth); +YOSYS_NAMESPACE_BEGIN extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth); +YOSYS_NAMESPACE_END + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count, int max_depth) { @@ -89,6 +93,9 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did } } +PRIVATE_NAMESPACE_END +YOSYS_NAMESPACE_BEGIN + 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++) { @@ -109,7 +116,10 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m } } -static void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) +YOSYS_NAMESPACE_END +PRIVATE_NAMESPACE_BEGIN + +void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) { int count = 0; bool did_something = true; @@ -174,3 +184,4 @@ struct ProcCleanPass : public Pass { } } ProcCleanPass; +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index e69e8023..76842da6 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -25,7 +25,10 @@ #include <stdlib.h> #include <stdio.h> -static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) { RTLIL::SigSpec lvalue; @@ -50,7 +53,7 @@ static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) return lvalue; } -static void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, RTLIL::SigSpec clk, bool clk_polarity, +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.size()); @@ -140,7 +143,7 @@ static void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::S cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); } -static void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out, +void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out, bool clk_polarity, bool set_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec set, RTLIL::Process *proc) { std::stringstream sstr; @@ -187,7 +190,7 @@ static void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec clk_polarity ? "positive" : "negative", set_polarity ? "positive" : "negative"); } -static void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_rst, RTLIL::SigSpec sig_out, +void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_rst, RTLIL::SigSpec sig_out, bool clk_polarity, bool arst_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec *arst, RTLIL::Process *proc) { std::stringstream sstr; @@ -215,7 +218,7 @@ static void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_ log(".\n"); } -static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) +void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) { while (1) { @@ -330,12 +333,12 @@ static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) if (many_async_rules.size() > 0) { - log("WARNING: Complex async reset for dff `%s'.\n", log_signal(sig)); + log_warning("Complex async reset for dff `%s'.\n", log_signal(sig)); gen_dffsr_complex(mod, insig, sig, sync_edge->signal, sync_edge->type == RTLIL::SyncType::STp, many_async_rules, proc); } else if (!rstval.is_fully_const() && !ce.eval(rstval)) { - log("WARNING: Async reset value `%s' is not constant!\n", log_signal(rstval)); + log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval)); gen_dffsr(mod, insig, rstval, sig, sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, @@ -380,3 +383,4 @@ struct ProcDffPass : public Pass { } } ProcDffPass; +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc new file mode 100644 index 00000000..e1bbab54 --- /dev/null +++ b/passes/proc/proc_dlatch.cc @@ -0,0 +1,308 @@ +/* + * 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/consteval.h" +#include "kernel/log.h" +#include <sstream> +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct proc_dlatch_db_t +{ + Module *module; + SigMap sigmap; + + dict<SigBit, pair<Cell*, int>> mux_drivers; + dict<SigBit, int> sigusers; + + proc_dlatch_db_t(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux")) { + auto sig_y = sigmap(cell->getPort("\\Y")); + for (int i = 0; i < GetSize(sig_y); i++) + mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i); + } + + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigusers[bit]++; + } + + for (auto wire : module->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sigusers[bit]++; + } + + struct rule_node_t + { + // a node is true if "signal" equals "match" and [any + // of the child nodes is true or "children" is empty] + SigBit signal, match; + vector<int> children; + + bool operator==(const rule_node_t &other) const { + return signal == other.signal && match == other.match && children == other.children; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + mkhash(h, signal.hash()); + mkhash(h, match.hash()); + for (auto i : children) mkhash(h, i); + return h; + } + }; + + enum tf_node_types_t : int { + true_node = 1, + false_node = 2 + }; + + idict<rule_node_t, 3> rules_db; + dict<int, SigBit> rules_sig; + + int make_leaf(SigBit signal, SigBit match) + { + rule_node_t node; + node.signal = signal; + node.match = match; + return rules_db(node); + } + + int make_inner(SigBit signal, SigBit match, int child) + { + rule_node_t node; + node.signal = signal; + node.match = match; + node.children.push_back(child); + return rules_db(node); + } + + int make_inner(const pool<int> &children) + { + rule_node_t node; + node.signal = State::S0; + node.match = State::S0; + node.children = vector<int>(children.begin(), children.end()); + std::sort(node.children.begin(), node.children.end()); + return rules_db(node); + } + + int find_mux_feedback(SigBit haystack, SigBit needle, bool set_undef) + { + if (sigusers[haystack] > 1) + set_undef = false; + + if (haystack == needle) + return true_node; + + auto it = mux_drivers.find(haystack); + if (it == mux_drivers.end()) + return false_node; + + Cell *cell = it->second.first; + int index = it->second.second; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + int width = GetSize(sig_a); + + pool<int> children; + + int n = find_mux_feedback(sig_a[index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_a[index] == needle) { + SigSpec sig = cell->getPort("\\A"); + sig[index] = State::Sx; + cell->setPort("\\A", sig); + } + for (int i = 0; i < GetSize(sig_s); i++) + n = make_inner(sig_s[i], State::S0, n); + children.insert(n); + } + + for (int i = 0; i < GetSize(sig_s); i++) { + n = find_mux_feedback(sig_b[i*width + index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_b[i*width + index] == needle) { + SigSpec sig = cell->getPort("\\B"); + sig[i*width + index] = State::Sx; + cell->setPort("\\B", sig); + } + children.insert(make_inner(sig_s[i], State::S1, n)); + } + } + + if (children.empty()) + return false_node; + + return make_inner(children); + } + + SigBit make_hold(int n) + { + if (n == true_node) + return State::S1; + + if (n == false_node) + return State::S0; + + if (rules_sig.count(n)) + return rules_sig.at(n); + + const rule_node_t &rule = rules_db[n]; + SigSpec and_bits; + + if (rule.signal != rule.match) { + if (rule.match == State::S1) + and_bits.append(rule.signal); + else if (rule.match == State::S0) + and_bits.append(module->Not(NEW_ID, rule.signal)); + else + and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match)); + } + + if (!rule.children.empty()) { + SigSpec or_bits; + for (int k : rule.children) + or_bits.append(make_hold(k)); + and_bits.append(module->ReduceOr(NEW_ID, or_bits)); + } + + if (GetSize(and_bits) == 2) + and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]); + log_assert(GetSize(and_bits) == 1); + + rules_sig[n] = and_bits[0]; + return and_bits[0]; + } +}; + +void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) +{ + std::vector<RTLIL::SyncRule*> new_syncs; + RTLIL::SigSig latches_bits, nolatches_bits; + dict<SigBit, SigBit> latches_out_in; + dict<SigBit, int> latches_hold; + + for (auto sr : proc->syncs) + { + if (sr->type != RTLIL::SyncType::STa) { + new_syncs.push_back(sr); + continue; + } + + for (auto ss : sr->actions) { + db.sigmap.apply(ss.first); + db.sigmap.apply(ss.second); + for (int i = 0; i < GetSize(ss.first); i++) + latches_out_in[ss.first[i]] = ss.second[i]; + } + + delete sr; + } + + latches_out_in.sort(); + for (auto &it : latches_out_in) { + int n = db.find_mux_feedback(it.second, it.first, true); + if (n == db.false_node) { + nolatches_bits.first.append(it.first); + nolatches_bits.second.append(it.second); + } else { + latches_bits.first.append(it.first); + latches_bits.second.append(it.second); + latches_hold[it.first] = n; + } + } + + int offset = 0; + for (auto chunk : nolatches_bits.first.chunks()) { + SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width); + log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + db.module->connect(lhs, rhs); + offset += chunk.width; + } + + offset = 0; + while (offset < GetSize(latches_bits.first)) + { + int width = 1; + int n = latches_hold[latches_bits.first[offset]]; + Wire *w = latches_bits.first[offset].wire; + + if (w != nullptr) + { + while (offset+width < GetSize(latches_bits.first) && + n == latches_hold[latches_bits.first[offset+width]] && + w == latches_bits.first[offset+width].wire) + width++; + + SigSpec lhs = latches_bits.first.extract(offset, width); + SigSpec rhs = latches_bits.second.extract(offset, width); + + Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs); + log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); + } + + offset += width; + } + + new_syncs.swap(proc->syncs); +} + +struct ProcDlatchPass : public Pass { + ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_dlatch [selection]\n"); + log("\n"); + log("This pass identifies latches in the processes and converts them to\n"); + log("d-type latches.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing PROC_DLATCH pass (convert process syncs to latches).\n"); + + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) { + proc_dlatch_db_t db(module); + for (auto &proc_it : module->processes) + if (design->selected(module, proc_it.second)) + proc_dlatch(db, proc_it.second); + } + } +} ProcDlatchPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index c72840c0..dff68159 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -23,7 +23,10 @@ #include <stdlib.h> #include <stdio.h> -static void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) { log_assert(rule.compare.size() == 0); @@ -37,7 +40,7 @@ static void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) } } -static void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) { bool found_init = false; @@ -109,3 +112,4 @@ struct ProcInitPass : public Pass { } } ProcInitPass; +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index c00b00a2..4aa1aab5 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -24,7 +24,10 @@ #include <stdlib.h> #include <stdio.h> -static RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) { for (auto &action : cs->actions) { if (action.first.size()) @@ -41,7 +44,7 @@ static RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) return RTLIL::SigSpec(); } -static void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) +void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) { for (auto &action : cs->actions) { RTLIL::SigSpec lvalue = action.first.extract(sig); @@ -54,7 +57,7 @@ static void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) extract_core_signal(cs2, sig); } -static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) +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$" << (autoidx++); @@ -122,7 +125,7 @@ static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, return RTLIL::SigSpec(ctrl_wire); } -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) +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) { log_assert(when_signal.size() == else_signal.size()); @@ -156,7 +159,7 @@ static RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, return RTLIL::SigSpec(result_wire); } -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) +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) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); @@ -176,7 +179,7 @@ static void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const 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) +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) { RTLIL::SigSpec result = defval; @@ -233,7 +236,7 @@ static RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs return result; } -static void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) { bool first = true; while (1) @@ -283,3 +286,4 @@ struct ProcMuxPass : public Pass { } } ProcMuxPass; +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index fe3532da..427e0d56 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -25,13 +25,16 @@ #include <stdio.h> #include <set> -static void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) { BitPatternPool pool(sw->signal); for (size_t i = 0; i < sw->cases.size(); i++) { - bool is_default = SIZE(sw->cases[i]->compare) == 0 && (!pool.empty() || SIZE(sw->signal) == 0); + bool is_default = GetSize(sw->cases[i]->compare) == 0 && (!pool.empty() || GetSize(sw->signal) == 0); for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) { RTLIL::SigSpec sig = sw->cases[i]->compare[j]; @@ -98,3 +101,4 @@ struct ProcRmdeadPass : public Pass { } } ProcRmdeadPass; +PRIVATE_NAMESPACE_END diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index f07ad943..01d0e031 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -31,7 +31,8 @@ #include <string.h> #include <algorithm> -namespace { +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN /* this should only be used for regression testing of ConstEval -- see vloghammer */ struct BruteForceEquivChecker @@ -68,7 +69,7 @@ struct BruteForceEquivChecker log_signal(undef2), log_signal(mod1_inputs), log_signal(inputs)); if (ignore_x_mod1) { - for (int i = 0; i < SIZE(sig1); i++) + for (int i = 0; i < GetSize(sig1); i++) if (sig1[i] == RTLIL::State::Sx) sig2[i] = RTLIL::State::Sx; } @@ -142,16 +143,16 @@ struct VlogHammerReporter { log("Verifying SAT model (%s)..\n", model_undef ? "with undef" : "without undef"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); satgen.model_undef = model_undef; 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)); + ez->assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y")); std::vector<bool> y_values; @@ -162,9 +163,9 @@ struct VlogHammerReporter } log(" Created SAT problem with %d variables and %d clauses.\n", - ez.numCnfVariables(), ez.numCnfClauses()); + ez->numCnfVariables(), ez->numCnfClauses()); - if (!ez.solve(y_vec, y_values)) + if (!ez->solve(y_vec, y_values)) log_error("Failed to find solution to SAT problem.\n"); for (int i = 0; i < expected_y.size(); i++) { @@ -203,7 +204,7 @@ struct VlogHammerReporter 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))) + if (!ez->solve(y_vec, y_values, ez->IFF(y_vec.at(i), y_values.at(i) ? ez->CONST_FALSE : ez->CONST_TRUE))) log_error("Failed to find solution with toggled bit!\n"); cmp_vars.push_back(y_vec.at(expected_y.size() + i)); @@ -219,15 +220,15 @@ struct VlogHammerReporter } log(" Testing if SAT solution is unique.\n"); - ez.assume(ez.vec_ne(cmp_vars, ez.vec_const(cmp_vals))); - if (ez.solve(y_vec, y_values)) + ez->assume(ez->vec_ne(cmp_vars, ez->vec_const(cmp_vals))); + if (ez->solve(y_vec, y_values)) log_error("Found two distinct solutions to SAT problem.\n"); } else { log(" Testing if SAT solution is unique.\n"); - ez.assume(ez.vec_ne(y_vec, ez.vec_const(y_values))); - if (ez.solve(y_vec, y_values)) + ez->assume(ez->vec_ne(y_vec, ez->vec_const(y_values))); + if (ez->solve(y_vec, y_values)) log_error("Found two distinct solutions to SAT problem.\n"); } @@ -276,7 +277,7 @@ struct VlogHammerReporter 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)); + 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.size())); } @@ -289,7 +290,7 @@ struct VlogHammerReporter } 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 < SIZE(sig); i++) + for (int i = 0; i < GetSize(sig); i++) if (rtl_sig[i] == RTLIL::State::Sx) sig[i] = RTLIL::State::Sx; } @@ -357,8 +358,6 @@ struct VlogHammerReporter } }; -} /* namespace */ - struct EvalPass : public Pass { EvalPass() : Pass("eval", "evaluate the circuit given an input") { } virtual void help() @@ -601,3 +600,4 @@ struct EvalPass : public Pass { } } EvalPass; +PRIVATE_NAMESPACE_END diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index e856fdf7..b012bc6a 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -23,6 +23,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct dff_map_info_t { RTLIL::SigSpec sig_d, sig_clk, sig_arst; bool clk_polarity, arst_polarity; @@ -37,7 +40,7 @@ struct dff_map_bit_info_t { RTLIL::Cell *cell; }; -static bool consider_wire(RTLIL::Wire *wire, std::map<RTLIL::IdString, dff_map_info_t> &dff_dq_map) +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,7 +49,7 @@ static bool consider_wire(RTLIL::Wire *wire, std::map<RTLIL::IdString, dff_map_i return true; } -static bool consider_cell(RTLIL::Design *design, std::set<RTLIL::IdString> &dff_cells, RTLIL::Cell *cell) +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; @@ -55,7 +58,7 @@ static bool consider_cell(RTLIL::Design *design, std::set<RTLIL::IdString> &dff_ return true; } -static bool compare_wires(RTLIL::Wire *wire1, RTLIL::Wire *wire2) +bool compare_wires(RTLIL::Wire *wire1, RTLIL::Wire *wire2) { log_assert(wire1->name == wire2->name); if (wire1->width != wire2->width) @@ -63,7 +66,7 @@ static bool compare_wires(RTLIL::Wire *wire1, RTLIL::Wire *wire2) return true; } -static bool compare_cells(RTLIL::Cell *cell1, RTLIL::Cell *cell2) +bool compare_cells(RTLIL::Cell *cell1, RTLIL::Cell *cell2) { log_assert(cell1->name == cell2->name); if (cell1->type != cell2->type) @@ -73,7 +76,7 @@ static bool compare_cells(RTLIL::Cell *cell1, RTLIL::Cell *cell2) return true; } -static void find_dff_wires(std::set<RTLIL::IdString> &dff_wires, RTLIL::Module *module) +void find_dff_wires(std::set<RTLIL::IdString> &dff_wires, RTLIL::Module *module) { CellTypes ct; ct.setup_internals_mem(); @@ -93,7 +96,7 @@ static void find_dff_wires(std::set<RTLIL::IdString> &dff_wires, RTLIL::Module * } } -static void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::Design *design, RTLIL::Module *module) +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); @@ -208,7 +211,7 @@ static void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RT } } -static RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width = 1) +RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width = 1) { 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)); @@ -604,7 +607,7 @@ struct ExposePass : public Pass { RTLIL::SigSpec sig; if (cell->hasPort(p->name)) sig = cell->getPort(p->name); - sig.extend(w->width); + sig.extend_u0(w->width); if (w->port_input) module->connect(RTLIL::SigSig(sig, w)); else @@ -644,3 +647,4 @@ struct ExposePass : public Pass { } } ExposePass; +PRIVATE_NAMESPACE_END diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index bfed0005..8a5301ec 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -28,7 +28,8 @@ #include <string.h> #include <algorithm> -namespace { +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN bool inv_mode; int verbose_level, reduce_counter, reduce_stop_at; @@ -72,7 +73,7 @@ struct FindReducedInputs SigMap &sigmap; drivers_t &drivers; - ezDefaultSAT ez; + ezSatPtr ez; std::set<RTLIL::Cell*> ez_cells; SatGen satgen; @@ -80,7 +81,7 @@ struct FindReducedInputs std::vector<int> sat_pi_uniq_bitvec; FindReducedInputs(SigMap &sigmap, drivers_t &drivers) : - sigmap(sigmap), drivers(drivers), satgen(&ez, &sigmap) + sigmap(sigmap), drivers(drivers), satgen(ez.get(), &sigmap) { satgen.model_undef = true; } @@ -103,30 +104,30 @@ struct FindReducedInputs satgen.setContext(&sigmap, "A"); int sat_a = satgen.importSigSpec(bit).front(); - ez.assume(ez.NOT(satgen.importUndefSigSpec(bit).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front())); satgen.setContext(&sigmap, "B"); int sat_b = satgen.importSigSpec(bit).front(); - ez.assume(ez.NOT(satgen.importUndefSigSpec(bit).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front())); int idx = sat_pi.size(); size_t idx_bits = get_bits(idx); if (sat_pi_uniq_bitvec.size() != idx_bits) { - sat_pi_uniq_bitvec.push_back(ez.frozen_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()))); + 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.frozen_literal(stringf("p, falsei_%s", log_signal(bit))); - ez.assume(ez.IFF(ez.XOR(sat_a, sat_b), sat_pi[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++) if ((idx & (1 << i)) == 0) - ez.assume(ez.OR(ez.NOT(sat_pi[bit]), ez.NOT(sat_pi_uniq_bitvec[i]))); + ez->assume(ez->OR(ez->NOT(sat_pi[bit]), ez->NOT(sat_pi_uniq_bitvec[i]))); else - ez.assume(ez.OR(ez.NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i])); + ez->assume(ez->OR(ez->NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i])); } void register_cone_worker(std::set<RTLIL::SigBit> &pi, std::set<RTLIL::SigBit> &sigdone, RTLIL::SigBit out) @@ -200,7 +201,7 @@ struct FindReducedInputs model_expr.push_back(sat_pi.at(pi[i])); } - if (!ez.solve(model_expr, model, ez.expression(ezSAT::OpOr, model_expr), ez.XOR(output_a, output_b), ez.NOT(output_undef_a), ez.NOT(output_undef_b))) + if (!ez->solve(model_expr, model, ez->expression(ezSAT::OpOr, model_expr), ez->XOR(output_a, output_b), ez->NOT(output_undef_a), ez->NOT(output_undef_b))) break; int found_count = 0; @@ -229,7 +230,7 @@ struct PerformReduction drivers_t &drivers; std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; std::vector<int> sat_pi, sat_out, sat_def; @@ -259,7 +260,7 @@ struct PerformReduction } else { pi_bits.push_back(out); sat_pi.push_back(satgen.importSigSpec(out).front()); - ez.assume(ez.NOT(satgen.importUndefSigSpec(out).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(out).front())); sigdepth[out] = 0; } @@ -267,7 +268,7 @@ struct PerformReduction } PerformReduction(SigMap &sigmap, drivers_t &drivers, std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs, std::vector<RTLIL::SigBit> &bits, int cone_size) : - sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(&ez, &sigmap), out_bits(bits), cone_size(cone_size) + sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(ez.get(), &sigmap), out_bits(bits), cone_size(cone_size) { satgen.model_undef = true; @@ -277,15 +278,15 @@ struct PerformReduction for (auto &bit : bits) { out_depth.push_back(register_cone_worker(celldone, sigdepth, bit)); sat_out.push_back(satgen.importSigSpec(bit).front()); - sat_def.push_back(ez.NOT(satgen.importUndefSigSpec(bit).front())); + sat_def.push_back(ez->NOT(satgen.importUndefSigSpec(bit).front())); } if (inv_mode && cone_size > 0) { - if (!ez.solve(sat_out, out_inverted, ez.expression(ezSAT::OpAnd, sat_def))) + if (!ez->solve(sat_out, out_inverted, ez->expression(ezSAT::OpAnd, sat_def))) log_error("Solving for initial model failed!\n"); for (size_t i = 0; i < sat_out.size(); i++) if (out_inverted.at(i)) - sat_out[i] = ez.NOT(sat_out[i]); + sat_out[i] = ez->NOT(sat_out[i]); } else out_inverted = std::vector<bool>(sat_out.size(), false); } @@ -295,8 +296,8 @@ struct PerformReduction if (verbose_level == 1) log(" Finding const value for %s.\n", log_signal(out_bits[idx])); - bool can_be_set = ez.solve(ez.AND(sat_out[idx], sat_def[idx])); - bool can_be_clr = ez.solve(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + bool can_be_set = ez->solve(ez->AND(sat_out[idx], sat_def[idx])); + bool can_be_clr = ez->solve(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); log_assert(!can_be_set || !can_be_clr); RTLIL::SigBit value(RTLIL::State::Sx); @@ -354,8 +355,8 @@ struct PerformReduction std::vector<int> sat_set_list, sat_clr_list; for (int idx : bucket) { - sat_set_list.push_back(ez.AND(sat_out[idx], sat_def[idx])); - sat_clr_list.push_back(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx])); + sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); } std::vector<int> modelVars = sat_out; @@ -365,7 +366,7 @@ struct PerformReduction if (verbose_level >= 2) modelVars.insert(modelVars.end(), sat_pi.begin(), sat_pi.end()); - if (ez.solve(modelVars, model, ez.expression(ezSAT::OpOr, sat_set_list), ez.expression(ezSAT::OpOr, sat_clr_list))) + if (ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list))) { int iter_count = 1; @@ -378,13 +379,13 @@ struct PerformReduction for (int idx : bucket) if (!model[sat_out.size() + idx]) { - sat_set_list.push_back(ez.AND(sat_out[idx], sat_def[idx])); - sat_clr_list.push_back(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx])); + sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); } else { sat_def_list.push_back(sat_def[idx]); } - if (!ez.solve(modelVars, model, ez.expression(ezSAT::OpOr, sat_set_list), ez.expression(ezSAT::OpOr, sat_clr_list), ez.expression(ezSAT::OpAnd, sat_def_list))) + if (!ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list), ez->expression(ezSAT::OpAnd, sat_def_list))) break; iter_count++; } @@ -430,7 +431,7 @@ struct PerformReduction for (int idx2 : bucket) if (idx != idx2) sat_def_list.push_back(sat_def[idx2]); - if (ez.solve(ez.NOT(sat_def[idx]), ez.expression(ezSAT::OpOr, sat_def_list))) + if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list))) undef_slaves.push_back(idx); } @@ -504,7 +505,7 @@ struct PerformReduction for (int idx2 : r) if (idx != idx2) sat_def_list.push_back(sat_def[idx2]); - if (ez.solve(ez.NOT(sat_def[idx]), ez.expression(ezSAT::OpOr, sat_def_list))) + if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list))) undef_slaves.push_back(idx); } @@ -745,8 +746,6 @@ struct FreduceWorker } }; -} /* namespace */ - struct FreducePass : public Pass { FreducePass() : Pass("freduce", "perform functional reduction") { } virtual void help() @@ -827,3 +826,4 @@ struct FreducePass : public Pass { } } FreducePass; +PRIVATE_NAMESPACE_END diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index b3adefb9..9853cd0c 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -21,7 +21,10 @@ #include "kernel/rtlil.h" #include "kernel/log.h" -static void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design) { bool flag_ignore_gold_x = false; bool flag_make_outputs = false; @@ -299,3 +302,4 @@ struct MiterPass : public Pass { } } MiterPass; +PRIVATE_NAMESPACE_END diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index fd0abf4a..9e5cc9e9 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -33,23 +33,25 @@ #include <errno.h> #include <string.h> -namespace { +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN struct SatHelper { RTLIL::Design *design; RTLIL::Module *module; - ezDefaultSAT ez; SigMap sigmap; CellTypes ct; + + ezSatPtr ez; SatGen satgen; // additional constraints std::vector<std::pair<std::string, std::string>> sets, prove, prove_x, sets_init; std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at; std::map<int, std::vector<std::string>> unsets_at; - bool prove_asserts; + bool prove_asserts, set_assumes; // undef constraints bool enable_undef, set_init_def, set_init_undef, set_init_zero, ignore_unknown_cells; @@ -64,7 +66,7 @@ struct SatHelper bool gotTimeout; SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef) : - design(design), module(module), sigmap(module), ct(design), satgen(&ez, &sigmap) + design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap) { this->enable_undef = enable_undef; satgen.model_undef = enable_undef; @@ -115,7 +117,7 @@ struct SatHelper } if (removed_bits.size()) - log("Warning: ignoring initial value on non-register: %s\n", log_signal(removed_bits)); + 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)); @@ -154,7 +156,7 @@ struct SatHelper if (set_init_def) { RTLIL::SigSpec rem = satgen.initial_state.export_all(); std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1); - ez.assume(ez.NOT(ez.expression(ezSAT::OpOr, undef_rem))); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem))); } if (set_init_undef) { @@ -178,7 +180,7 @@ struct SatHelper log("Final constraint equation: %s = %s\n\n", log_signal(big_lhs), log_signal(big_rhs)); check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez.assume(satgen.signals_eq(big_lhs, big_rhs, 1)); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, 1)); } void setup(int timestep = -1) @@ -249,7 +251,7 @@ struct SatHelper log("Final constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs)); check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez.assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); // 0 = sets_def // 1 = sets_any_undef @@ -309,28 +311,36 @@ struct SatHelper 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))); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_sig))); if (t == 1) - ez.assume(ez.expression(ezSAT::OpOr, undef_sig)); + ez->assume(ez->expression(ezSAT::OpOr, undef_sig)); if (t == 2) - ez.assume(ez.expression(ezSAT::OpAnd, undef_sig)); + ez->assume(ez->expression(ezSAT::OpAnd, undef_sig)); } int import_cell_counter = 0; - 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()) - if (ct.cell_output(c.second->type, p.first)) - show_drivers.insert(sigmap(p.second), c.second); + for (auto cell : module->cells()) + if (design->selected(module, cell)) { + // log("Import cell: %s\n", RTLIL::id2cstr(cell->name)); + if (satgen.importCell(cell, timestep)) { + for (auto &p : cell->connections()) + if (ct.cell_output(cell->type, p.first)) + show_drivers.insert(sigmap(p.second), cell); import_cell_counter++; } else if (ignore_unknown_cells) - log("Warning: Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); else - log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); } log("Imported %d cells to SAT database.\n", import_cell_counter); + + if (set_assumes) { + RTLIL::SigSpec assumes_a, assumes_en; + satgen.getAssumes(assumes_a, assumes_en, timestep); + for (int i = 0; i < GetSize(assumes_a); i++) + log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i])); + ez->assume(satgen.importAssumes(timestep)); + } } int setup_proof(int timestep = -1) @@ -400,33 +410,33 @@ struct SatHelper std::vector<int> undef_rhs = satgen.importUndefSigSpec(big_rhs, timestep); for (size_t i = 0; i < value_lhs.size(); i++) - prove_bits.push_back(ez.OR(undef_lhs.at(i), ez.AND(ez.NOT(undef_rhs.at(i)), ez.NOT(ez.XOR(value_lhs.at(i), value_rhs.at(i)))))); + prove_bits.push_back(ez->OR(undef_lhs.at(i), ez->AND(ez->NOT(undef_rhs.at(i)), ez->NOT(ez->XOR(value_lhs.at(i), value_rhs.at(i)))))); } if (prove_asserts) { RTLIL::SigSpec asserts_a, asserts_en; satgen.getAsserts(asserts_a, asserts_en, timestep); - for (int i = 0; i < SIZE(asserts_a); i++) + for (int i = 0; i < GetSize(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)); } - return ez.expression(ezSAT::OpAnd, prove_bits); + return ez->expression(ezSAT::OpAnd, prove_bits); } void force_unique_state(int timestep_from, int timestep_to) { RTLIL::SigSpec state_signals = satgen.initial_state.export_all(); for (int i = timestep_from; i < timestep_to; i++) - ez.assume(ez.NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to))); + ez->assume(ez->NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to))); } bool solve(const std::vector<int> &assumptions) { log_assert(gotTimeout == false); - ez.setSolverTimeout(timeout); - bool success = ez.solve(modelExpressions, modelValues, assumptions); - if (ez.getSolverTimoutStatus()) + ez->setSolverTimeout(timeout); + bool success = ez->solve(modelExpressions, modelValues, assumptions); + if (ez->getSolverTimoutStatus()) gotTimeout = true; return success; } @@ -434,9 +444,9 @@ struct SatHelper bool solve(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0) { log_assert(gotTimeout == false); - ez.setSolverTimeout(timeout); - bool success = ez.solve(modelExpressions, modelValues, a, b, c, d, e, f); - if (ez.getSolverTimoutStatus()) + ez->setSolverTimeout(timeout); + bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f); + if (ez->getSolverTimoutStatus()) gotTimeout = true; return success; } @@ -477,7 +487,7 @@ struct SatHelper maybe_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i)); backupValues.swap(modelValues); - if (!solve(ez.expression(ezSAT::OpAnd, must_undef), ez.expression(ezSAT::OpOr, maybe_undef))) + if (!solve(ez->expression(ezSAT::OpAnd, must_undef), ez->expression(ezSAT::OpOr, maybe_undef))) break; } @@ -670,7 +680,7 @@ struct SatHelper fprintf(f, " %s\n", stime); fprintf(f, "$end\n"); fprintf(f, "$version\n"); - fprintf(f, " Generated by %s\n", yosys_version_str); + 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", @@ -749,6 +759,80 @@ struct SatHelper fclose(f); } + void dump_model_to_json(std::string json_file_name) + { + FILE *f = fopen(json_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno)); + + log("Dumping SAT model to WaveJSON file '%s'.\n", json_file_name.c_str()); + + int mintime = 1, maxtime = 0, maxwidth = 0;; + dict<string, pair<int, dict<int, Const>>> wavedata; + + for (auto &info : modelInfo) + { + 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; + } + + wavedata[info.description].first = info.width; + wavedata[info.description].second[info.timestep] = value; + mintime = std::min(mintime, info.timestep); + maxtime = std::max(maxtime, info.timestep); + maxwidth = std::max(maxwidth, info.width); + } + + fprintf(f, "{ \"signal\": ["); + bool fist_wavedata = true; + for (auto &wd : wavedata) + { + fprintf(f, "%s", fist_wavedata ? "\n" : ",\n"); + fist_wavedata = false; + + vector<string> data; + string name = wd.first.c_str(); + while (name.substr(0, 1) == "\\") + name = name.substr(1); + + fprintf(f, " { \"name\": \"%s\", \"wave\": \"", name.c_str()); + for (int i = mintime; i <= maxtime; i++) { + if (wd.second.second.count(i)) { + string this_data = wd.second.second[i].as_string(); + char ch = '='; + if (wd.second.first == 1) + ch = this_data[0]; + if (!data.empty() && data.back() == this_data) { + fprintf(f, "."); + } else { + data.push_back(this_data); + fprintf(f, "%c", ch); + } + } else { + data.push_back(""); + fprintf(f, "4"); + } + } + if (wd.second.first != 1) { + fprintf(f, "\", \"data\": ["); + for (int i = 0; i < GetSize(data); i++) + fprintf(f, "%s\"%s\"", i ? ", " : "", data[i].c_str()); + fprintf(f, "] }"); + } else { + fprintf(f, "\" }"); + } + } + fprintf(f, "\n ],\n"); + fprintf(f, " \"config\": {\n"); + fprintf(f, " \"hscale\": %.2f\n", maxwidth / 4.0); + fprintf(f, " }\n"); + fprintf(f, "}\n"); + fclose(f); + } + void invalidate_model(bool max_undef) { std::vector<int> clause; @@ -757,18 +841,16 @@ struct SatHelper int bit = modelExpressions.at(i), bit_undef = modelExpressions.at(modelExpressions.size()/2 + i); bool val = modelValues.at(i), val_undef = modelValues.at(modelExpressions.size()/2 + i); if (!max_undef || !val_undef) - clause.push_back(val_undef ? ez.NOT(bit_undef) : val ? ez.NOT(bit) : bit); + clause.push_back(val_undef ? ez->NOT(bit_undef) : val ? ez->NOT(bit) : bit); } } else for (size_t i = 0; i < modelExpressions.size(); i++) - clause.push_back(modelValues.at(i) ? ez.NOT(modelExpressions.at(i)) : modelExpressions.at(i)); - ez.assume(ez.expression(ezSAT::OpOr, clause)); + clause.push_back(modelValues.at(i) ? ez->NOT(modelExpressions.at(i)) : modelExpressions.at(i)); + ez->assume(ez->expression(ezSAT::OpOr, clause)); } }; -} /* namespace */ - -static void print_proof_failed() +void print_proof_failed() { log("\n"); log(" ______ ___ ___ _ _ _ _ \n"); @@ -780,7 +862,7 @@ static void print_proof_failed() log("\n"); } -static void print_timeout() +void print_timeout() { log("\n"); log(" _____ _ _ _____ ____ _ _____\n"); @@ -791,7 +873,7 @@ static void print_timeout() log("\n"); } -static void print_qed() +void print_qed() { log("\n"); log(" /$$$$$$ /$$$$$$$$ /$$$$$$$ \n"); @@ -851,8 +933,8 @@ struct SatPass : public Pass { log(" show the model for the specified signal. if no -show option is\n"); log(" passed then a set of signals to be shown is automatically selected.\n"); log("\n"); - log(" -show-inputs, -show-outputs\n"); - log(" add all module input (output) ports to the list of shown signals\n"); + log(" -show-inputs, -show-outputs, -show-ports\n"); + log(" add all module (input/output) ports to the list of shown signals\n"); log("\n"); log(" -ignore_div_by_zero\n"); log(" ignore all solutions that involve a division by zero\n"); @@ -866,11 +948,17 @@ struct SatPass : public Pass { log(" set up a sequential problem with <N> time steps. The steps will\n"); log(" be numbered from 1 to N.\n"); log("\n"); + log(" note: for large <N> it can be significantly faster to use\n"); + log(" -tempinduct-baseonly -maxsteps <N> instead of -seq <N>.\n"); + log("\n"); log(" -set-at <N> <signal> <value>\n"); log(" -unset-at <N> <signal>\n"); log(" set or unset the specified signal to the specified value in the\n"); log(" given timestep. this has priority over a -set for the same signal.\n"); log("\n"); + log(" -set-assumes\n"); + log(" set all assumptions provided via $assume cells\n"); + log("\n"); log(" -set-def-at <N> <signal>\n"); log(" -set-any-undef-at <N> <signal>\n"); log(" -set-all-undef-at <N> <signal>\n"); @@ -891,6 +979,9 @@ struct SatPass : public Pass { log(" -dump_vcd <vcd-file-name>\n"); log(" dump SAT model (counter example in proof) to VCD file\n"); log("\n"); + log(" -dump_json <json-file-name>\n"); + log(" dump SAT model (counter example in proof) to a WaveJSON 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"); @@ -907,6 +998,20 @@ struct SatPass : public Pass { log(" Perform a temporal induction proof. Assume an initial state with all\n"); log(" registers set to defined values for the induction step.\n"); log("\n"); + log(" -tempinduct-baseonly\n"); + log(" Run only the basecase half of temporal induction (requires -maxsteps)\n"); + log("\n"); + log(" -tempinduct-inductonly\n"); + log(" Run only the induction half of temporal induction\n"); + log("\n"); + log(" -tempinduct-skip <N>\n"); + log(" Skip the first <N> steps of the induction proof.\n"); + log("\n"); + log(" note: this will assume that the base case holds for <N> steps.\n"); + log(" this must be proven independently with \"-tempinduct-baseonly\n"); + log(" -maxsteps <N>\". Use -initsteps if you just want to set a\n"); + log(" minimal induction length.\n"); + log("\n"); log(" -prove <signal> <value>\n"); log(" Attempt to proof that <signal> is always <value>.\n"); log("\n"); @@ -925,6 +1030,13 @@ struct SatPass : public Pass { log("\n"); log(" -initsteps <N>\n"); log(" Set initial length for the induction.\n"); + log(" This will speed up the search of the right induction length\n"); + log(" for deep induction proofs.\n"); + log("\n"); + log(" -stepsize <N>\n"); + log(" Increase the size of the induction proof in steps of <N>.\n"); + log(" This will speed up the search of the right induction length\n"); + log(" for deep induction proofs.\n"); log("\n"); log(" -timeout <N>\n"); log(" Maximum number of seconds a single SAT instance may take.\n"); @@ -953,7 +1065,9 @@ struct SatPass : public Pass { 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; + bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false; + int tempinduct_skip = 0, stepsize = 1; + std::string vcd_file_name, json_file_name, cnf_file_name; log_header("Executing SAT pass (solving SAT problems in the circuit).\n"); @@ -997,6 +1111,10 @@ struct SatPass : public Pass { initsteps = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-stepsize" && argidx+1 < args.size()) { + stepsize = std::max(1, atoi(args[++argidx].c_str())); + continue; + } if (args[argidx] == "-ignore_div_by_zero") { ignore_div_by_zero = true; continue; @@ -1036,6 +1154,10 @@ struct SatPass : public Pass { enable_undef = true; continue; } + if (args[argidx] == "-set-assumes") { + set_assumes = true; + continue; + } if (args[argidx] == "-tempinduct") { tempinduct = true; continue; @@ -1045,6 +1167,20 @@ struct SatPass : public Pass { tempinduct_def = true; continue; } + if (args[argidx] == "-tempinduct-baseonly") { + tempinduct = true; + tempinduct_baseonly = true; + continue; + } + if (args[argidx] == "-tempinduct-inductonly") { + tempinduct = true; + tempinduct_inductonly = true; + continue; + } + if (args[argidx] == "-tempinduct-skip" && argidx+1 < args.size()) { + tempinduct_skip = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-prove" && argidx+2 < args.size()) { std::string lhs = args[++argidx]; std::string rhs = args[++argidx]; @@ -1131,6 +1267,11 @@ struct SatPass : public Pass { show_outputs = true; continue; } + if (args[argidx] == "-show-ports") { + show_inputs = true; + show_outputs = true; + continue; + } if (args[argidx] == "-ignore_unknown_cells") { ignore_unknown_cells = true; continue; @@ -1139,6 +1280,10 @@ struct SatPass : public Pass { vcd_file_name = args[++argidx]; continue; } + if (args[argidx] == "-dump_json" && argidx+1 < args.size()) { + json_file_name = args[++argidx]; + continue; + } if (args[argidx] == "-dump_cnf" && argidx+1 < args.size()) { cnf_file_name = args[++argidx]; continue; @@ -1155,14 +1300,14 @@ struct SatPass : public Pass { RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first)); module = mod_it.second; } - if (module == NULL) + if (module == NULL) log_cmd_error("Can't perform SAT on an empty selection!\n"); 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"); + log_cmd_error("Options -prove-skip and -tempinduct don't work with each other. Use -seq instead of -prove-skip.\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"); @@ -1195,8 +1340,10 @@ struct SatPass : public Pass { SatHelper basecase(design, module, enable_undef); SatHelper inductstep(design, module, enable_undef); + bool basecase_setup_init = true; basecase.sets = sets; + basecase.set_assumes = set_assumes; basecase.prove = prove; basecase.prove_x = prove_x; basecase.prove_asserts = prove_asserts; @@ -1218,10 +1365,11 @@ struct SatPass : public Pass { basecase.ignore_unknown_cells = ignore_unknown_cells; for (int timestep = 1; timestep <= seq_len; timestep++) - basecase.setup(timestep); - basecase.setup_init(); + if (!tempinduct_inductonly) + basecase.setup(timestep); inductstep.sets = sets; + inductstep.set_assumes = set_assumes; inductstep.prove = prove; inductstep.prove_x = prove_x; inductstep.prove_asserts = prove_asserts; @@ -1233,12 +1381,14 @@ struct SatPass : public Pass { inductstep.satgen.ignore_div_by_zero = ignore_div_by_zero; inductstep.ignore_unknown_cells = ignore_unknown_cells; - inductstep.setup(1); - inductstep.ez.assume(inductstep.setup_proof(1)); + if (!tempinduct_baseonly) { + inductstep.setup(1); + inductstep.ez->assume(inductstep.setup_proof(1)); + } if (tempinduct_def) { std::vector<int> undef_state = inductstep.satgen.importUndefSigSpec(inductstep.satgen.initial_state.export_all(), 1); - inductstep.ez.assume(inductstep.ez.NOT(inductstep.ez.expression(ezSAT::OpOr, undef_state))); + inductstep.ez->assume(inductstep.ez->NOT(inductstep.ez->expression(ezSAT::OpOr, undef_state))); } for (int inductlen = 1; inductlen <= maxsteps || maxsteps == 0; inductlen++) @@ -1247,81 +1397,120 @@ struct SatPass : public Pass { // phase 1: proving base case - basecase.setup(seq_len + inductlen); - int property = basecase.setup_proof(seq_len + inductlen); - basecase.generate_model(); - - if (inductlen > 1) - basecase.force_unique_state(seq_len + 1, seq_len + inductlen); + if (!tempinduct_inductonly) + { + basecase.setup(seq_len + inductlen); + int property = basecase.setup_proof(seq_len + inductlen); + basecase.generate_model(); - log("\n[base case] Solving problem with %d variables and %d clauses..\n", - basecase.ez.numCnfVariables(), basecase.ez.numCnfClauses()); + if (basecase_setup_init) { + basecase.setup_init(); + basecase_setup_init = false; + } - if (basecase.solve(basecase.ez.NOT(property))) { - 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; - } + if (inductlen > 1) + basecase.force_unique_state(seq_len + 1, seq_len + inductlen); - if (basecase.gotTimeout) - goto timeout; + if (tempinduct_skip < inductlen) + { + log("\n[base case %d] Solving problem with %d variables and %d clauses..\n", + inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + + if (basecase.solve(basecase.ez->NOT(property))) { + 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); + if(!json_file_name.empty()) + basecase.dump_model_to_json(json_file_name); + goto tip_failed; + } + + if (basecase.gotTimeout) + goto timeout; - log("Base case for induction length %d proven.\n", inductlen); - basecase.ez.assume(property); + log("Base case for induction length %d proven.\n", inductlen); + } + else + { + log("\n[base case %d] Skipping prove for this step (-tempinduct-skip %d).", + inductlen, tempinduct_skip); + log("\n[base case %d] Problem size so far: %d variables and %d clauses.\n", + inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + } + basecase.ez->assume(property); + } // phase 2: proving induction step - inductstep.setup(inductlen + 1); - property = inductstep.setup_proof(inductlen + 1); - inductstep.generate_model(); - - if (inductlen > 1) - inductstep.force_unique_state(1, inductlen + 1); - - 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 (!tempinduct_baseonly) { - 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)); + inductstep.setup(inductlen + 1); + int property = inductstep.setup_proof(inductlen + 1); + inductstep.generate_model(); - log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); - cnf_file_name.clear(); + if (inductlen > 1) + inductstep.force_unique_state(1, inductlen + 1); - inductstep.ez.printDIMACS(f, false); - fclose(f); + if (inductlen <= tempinduct_skip || inductlen <= initsteps || inductlen % stepsize != 0) + { + if (inductlen < tempinduct_skip) + log("\n[induction step %d] Skipping prove for this step (-tempinduct-skip %d).", + inductlen, tempinduct_skip); + if (inductlen < initsteps) + log("\n[induction step %d] Skipping prove for this step (-initsteps %d).", + inductlen, tempinduct_skip); + if (inductlen % stepsize != 0) + log("\n[induction step %d] Skipping prove for this step (-stepsize %d).", + inductlen, stepsize); + log("\n[induction step %d] Problem size so far: %d variables and %d clauses.\n", + inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses()); + inductstep.ez->assume(property); } - - 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; + 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("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); + cnf_file_name.clear(); + + inductstep.ez->printDIMACS(f, false); + fclose(f); + } + + log("\n[induction step %d] Solving problem with %d variables and %d clauses..\n", + inductlen, 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("Induction step failed. Incrementing induction length.\n"); - inductstep.ez.assume(property); - inductstep.print_model(); } } + if (tempinduct_baseonly) { + log("\nReached maximum number of time steps -> proved base case for %d steps: SUCCESS!\n", maxsteps); + goto tip_success; + } + log("\nReached maximum number of time steps -> proof failed.\n"); if(!vcd_file_name.empty()) inductstep.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + inductstep.dump_model_to_json(json_file_name); print_proof_failed(); tip_failed: @@ -1345,6 +1534,7 @@ struct SatPass : public Pass { SatHelper sathelper(design, module, enable_undef); sathelper.sets = sets; + sathelper.set_assumes = set_assumes; sathelper.prove = prove; sathelper.prove_x = prove_x; sathelper.prove_asserts = prove_asserts; @@ -1368,7 +1558,7 @@ struct SatPass : public Pass { if (seq_len == 0) { sathelper.setup(); if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) - sathelper.ez.assume(sathelper.ez.NOT(sathelper.setup_proof())); + sathelper.ez->assume(sathelper.ez->NOT(sathelper.setup_proof())); } else { std::vector<int> prove_bits; for (int timestep = 1; timestep <= seq_len; timestep++) { @@ -1378,7 +1568,7 @@ struct SatPass : public Pass { 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))); + sathelper.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits))); sathelper.setup_init(); } sathelper.generate_model(); @@ -1392,7 +1582,7 @@ struct SatPass : public Pass { log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); cnf_file_name.clear(); - sathelper.ez.printDIMACS(f, false); + sathelper.ez->printDIMACS(f, false); fclose(f); } @@ -1400,7 +1590,7 @@ struct SatPass : public Pass { rerun_solver: log("\nSolving problem with %d variables and %d clauses..\n", - sathelper.ez.numCnfVariables(), sathelper.ez.numCnfClauses()); + sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses()); if (sathelper.solve()) { @@ -1420,6 +1610,8 @@ struct SatPass : public Pass { if(!vcd_file_name.empty()) sathelper.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + sathelper.dump_model_to_json(json_file_name); if (loopcount != 0) { loopcount--, rerun_counter++; @@ -1483,4 +1675,5 @@ struct SatPass : public Pass { } } } SatPass; - + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 72998f87..6b6846e2 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -2,14 +2,15 @@ OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o +OBJS += passes/techmap/maccmap.o OBJS += passes/techmap/libparse.o ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o OBJS += passes/techmap/hilomap.o OBJS += passes/techmap/extract.o -OBJS += passes/techmap/maccmap.o OBJS += passes/techmap/alumacc.o +OBJS += passes/techmap/dff2dffe.o endif GENFILES += passes/techmap/techmap.inc @@ -23,9 +24,11 @@ passes/techmap/techmap.inc: techlibs/common/techmap.v passes/techmap/techmap.o: passes/techmap/techmap.inc -TARGETS += yosys-filterlib -GENFILES += passes/techmap/filterlib.o +ifneq ($(CONFIG),emcc) +TARGETS += yosys-filterlib$(EXE) +EXTRA_OBJS += passes/techmap/filterlib.o -yosys-filterlib: passes/techmap/filterlib.o - $(P) $(CXX) -o yosys-filterlib $(LDFLAGS) $^ $(LDLIBS) +yosys-filterlib$(EXE): passes/techmap/filterlib.o + $(P) $(CXX) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) +endif diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 1115eead..dcffed94 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -21,6 +21,9 @@ #include "kernel/sigtools.h" #include "kernel/macc.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct AlumaccWorker { RTLIL::Module *module; @@ -45,51 +48,51 @@ struct AlumaccWorker RTLIL::SigSpec cached_cf, cached_of, cached_sf; RTLIL::SigSpec get_lt() { - if (SIZE(cached_lt) == 0) + if (GetSize(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) + if (GetSize(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) + if (GetSize(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) + if (GetSize(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) { + if (GetSize(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]); + log_assert(GetSize(cached_cf) >= 1); + cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[GetSize(cached_cf)-1]); } return cached_cf; } RTLIL::SigSpec get_of() { - if (SIZE(cached_of) == 0) { + if (GetSize(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]); + log_assert(GetSize(cached_of) >= 2); + cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(cached_of)-1], cached_of[GetSize(cached_of)-2]); } return cached_of; } RTLIL::SigSpec get_sf() { - if (SIZE(cached_sf) == 0) { + if (GetSize(cached_sf) == 0) { cached_sf = alu_cell->getPort("\\Y"); - cached_sf = cached_sf[SIZE(cached_sf)-1]; + cached_sf = cached_sf[GetSize(cached_sf)-1]; } return cached_sf; } @@ -181,10 +184,10 @@ struct AlumaccWorker 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)); + if (GetSize(port.in_b)) + port_sizes.push_back(GetSize(port.in_a) + GetSize(port.in_b)); else - port_sizes.push_back(SIZE(port.in_a)); + port_sizes.push_back(GetSize(port.in_a)); } std::sort(port_sizes.begin(), port_sizes.end()); @@ -221,11 +224,11 @@ struct AlumaccWorker if (delete_nodes.count(n)) continue; - for (int i = 0; i < SIZE(n->macc.ports); i++) + for (int i = 0; i < GetSize(n->macc.ports); i++) { auto &port = n->macc.ports[i]; - if (SIZE(port.in_b) > 0 || sig_macc.count(port.in_a) == 0) + if (GetSize(port.in_b) > 0 || sig_macc.count(port.in_a) == 0) continue; auto other_n = sig_macc.at(port.in_a); @@ -233,13 +236,13 @@ struct AlumaccWorker 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)) + if (GetSize(other_n->y) != GetSize(n->y) && macc_may_overflow(other_n->macc, GetSize(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++) { + for (int j = 0; j < GetSize(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) @@ -275,38 +278,38 @@ struct AlumaccWorker alunode_t *alunode; for (auto &port : n->macc.ports) - if (SIZE(port.in_b) > 0) { + if (GetSize(port.in_b) > 0) { goto next_macc; - } else if (SIZE(port.in_a) == 1 && !port.is_signed && !port.do_subtract) { + } else if (GetSize(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)) + } else if (GetSize(A) || port.do_subtract) { + if (GetSize(B)) goto next_macc; B = port.in_a; b_signed = port.is_signed; subtract_b = port.do_subtract; } else { - if (SIZE(A)) + if (GetSize(A)) goto next_macc; A = port.in_a; a_signed = port.is_signed; } if (!a_signed || !b_signed) { - if (SIZE(A) == SIZE(n->y)) + if (GetSize(A) == GetSize(n->y)) a_signed = false; - if (SIZE(B) == SIZE(n->y)) + if (GetSize(B) == GetSize(n->y)) b_signed = false; if (a_signed != b_signed) goto next_macc; } - if (SIZE(A) == 0 && SIZE(C) > 0) { + if (GetSize(A) == 0 && GetSize(C) > 0) { A = C[0]; C.remove(0); } - if (SIZE(B) == 0 && SIZE(C) > 0) { + if (GetSize(B) == 0 && GetSize(C) > 0) { B = C[0]; C.remove(0); } @@ -314,10 +317,10 @@ struct AlumaccWorker if (subtract_b) C.append(RTLIL::S1); - if (SIZE(C) > 1) + if (GetSize(C) > 1) goto next_macc; - if (!subtract_b && B < A && SIZE(B)) + if (!subtract_b && B < A && GetSize(B)) std::swap(A, B); log(" creating $alu model for $macc %s.\n", log_id(n->cell)); @@ -353,7 +356,7 @@ struct AlumaccWorker log(" creating $macc cell for %s: %s\n", log_id(n->cell), log_id(cell)); - n->macc.optimize(SIZE(n->y)); + n->macc.optimize(GetSize(n->y)); n->macc.to_cell(cell); cell->setPort("\\Y", n->y); cell->fixup_parameters(); @@ -388,7 +391,7 @@ struct AlumaccWorker RTLIL::SigSpec B = sigmap(cell->getPort("\\B")); RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y")); - if (B < A && SIZE(B)) { + if (B < A && GetSize(B)) { cmp_less = !cmp_less; std::swap(A, B); } @@ -406,7 +409,7 @@ struct AlumaccWorker n->a = A; n->b = B; n->c = RTLIL::S1; - n->y = module->addWire(NEW_ID, std::max(SIZE(A), SIZE(B))); + n->y = module->addWire(NEW_ID, std::max(GetSize(A), GetSize(B))); n->is_signed = is_signed; n->invert_b = true; sig_alu[RTLIL::SigSig(A, B)].insert(n); @@ -428,7 +431,7 @@ struct AlumaccWorker RTLIL::SigSpec B = sigmap(cell->getPort("\\B")); RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y")); - if (B < A && SIZE(B)) + if (B < A && GetSize(B)) std::swap(A, B); alunode_t *n = nullptr; @@ -452,12 +455,12 @@ struct AlumaccWorker for (auto &it1 : sig_alu) for (auto n : it1.second) { - if (SIZE(n->b) == 0 && SIZE(n->c) == 0 && SIZE(n->cmp) == 0) + if (GetSize(n->b) == 0 && GetSize(n->c) == 0 && GetSize(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++) + for (int i = 0; i < GetSize(n->cells); i++) log("%s%s", i ? ", ": "", log_id(n->cells[i])); log(": %s\n", log_id(n->alu_cell)); @@ -468,17 +471,17 @@ struct AlumaccWorker alu_counter++; log(" creating $alu cell for "); - for (int i = 0; i < SIZE(n->cells); i++) + for (int i = 0; i < GetSize(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("\\CI", GetSize(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->setPort("\\X", module->addWire(NEW_ID, GetSize(n->y))); + n->alu_cell->setPort("\\CO", module->addWire(NEW_ID, GetSize(n->y))); n->alu_cell->fixup_parameters(n->is_signed, n->is_signed); for (auto &it : n->cmp) @@ -495,10 +498,10 @@ struct AlumaccWorker if (cmp_eq) sig.append(n->get_eq()); if (cmp_ne) sig.append(n->get_ne()); - if (SIZE(sig) > 1) + if (GetSize(sig) > 1) sig = module->ReduceOr(NEW_ID, sig); - sig.extend(SIZE(cmp_y)); + sig.extend_u0(GetSize(cmp_y)); module->connect(cmp_y, sig); } @@ -535,8 +538,8 @@ struct AlumaccPass : public Pass { 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("This pass translates arithmetic operations like $add, $mul, $lt, etc. to $alu\n"); + log("and $macc cells.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) @@ -561,3 +564,4 @@ struct AlumaccPass : public Pass { } } AlumaccPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc new file mode 100644 index 00000000..17549bd0 --- /dev/null +++ b/passes/techmap/dff2dffe.cc @@ -0,0 +1,337 @@ +/* + * 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/celltypes.h" +#include "passes/techmap/simplemap.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct Dff2dffeWorker +{ + RTLIL::Module *module; + SigMap sigmap; + CellTypes ct; + + RTLIL::IdString direct_to; + + typedef std::pair<RTLIL::Cell*, int> cell_int_t; + std::map<RTLIL::SigBit, cell_int_t> bit2mux; + std::vector<RTLIL::Cell*> dff_cells; + std::map<RTLIL::SigBit, int> bitusers; + + typedef std::map<RTLIL::SigBit, bool> pattern_t; + typedef std::set<pattern_t> patterns_t; + + + Dff2dffeWorker(RTLIL::Module *module, RTLIL::IdString direct_from, RTLIL::IdString direct_to) : + module(module), sigmap(module), ct(module->design), direct_to(direct_to) + { + for (auto wire : module->wires()) { + if (wire->port_output) + for (auto bit : sigmap(wire)) + bitusers[bit]++; + } + + for (auto cell : module->cells()) { + if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$_MUX_") { + RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y")); + for (int i = 0; i < GetSize(sig_y); i++) + bit2mux[sig_y[i]] = cell_int_t(cell, i); + } + if (direct_to.empty()) { + if (cell->type == "$dff" || cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") + dff_cells.push_back(cell); + } else { + if (cell->type == direct_from) + dff_cells.push_back(cell); + } + for (auto conn : cell->connections()) { + if (ct.cell_output(cell->type, conn.first)) + continue; + for (auto bit : sigmap(conn.second)) + bitusers[bit]++; + } + } + } + + patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path) + { + patterns_t ret; + + if (d == q) { + ret.insert(path); + return ret; + } + + if (bit2mux.count(d) == 0 || bitusers[d] > 1) + return ret; + + cell_int_t mux_cell_int = bit2mux.at(d); + RTLIL::SigSpec sig_a = sigmap(mux_cell_int.first->getPort("\\A")); + RTLIL::SigSpec sig_b = sigmap(mux_cell_int.first->getPort("\\B")); + RTLIL::SigSpec sig_s = sigmap(mux_cell_int.first->getPort("\\S")); + int width = GetSize(sig_a), index = mux_cell_int.second; + + for (int i = 0; i < GetSize(sig_s); i++) + if (path.count(sig_s[i]) && path.at(sig_s[i])) + { + ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path); + + if (sig_b[i*width + index] == q) { + RTLIL::SigSpec s = mux_cell_int.first->getPort("\\B"); + s[i*width + index] = RTLIL::Sx; + mux_cell_int.first->setPort("\\B", s); + } + + return ret; + } + + pattern_t path_else = path; + + for (int i = 0; i < GetSize(sig_s); i++) + { + if (path.count(sig_s[i])) + continue; + + pattern_t path_this = path; + path_else[sig_s[i]] = false; + path_this[sig_s[i]] = true; + + for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this)) + ret.insert(pat); + + if (sig_b[i*width + index] == q) { + RTLIL::SigSpec s = mux_cell_int.first->getPort("\\B"); + s[i*width + index] = RTLIL::Sx; + mux_cell_int.first->setPort("\\B", s); + } + } + + for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else)) + ret.insert(pat); + + if (sig_a[index] == q) { + RTLIL::SigSpec s = mux_cell_int.first->getPort("\\A"); + s[index] = RTLIL::Sx; + mux_cell_int.first->setPort("\\A", s); + } + + return ret; + } + + void simplify_patterns(patterns_t&) + { + // TBD + } + + RTLIL::SigSpec make_patterns_logic(patterns_t patterns, bool make_gates) + { + RTLIL::SigSpec or_input; + + for (auto pat : patterns) + { + RTLIL::SigSpec s1, s2; + for (auto it : pat) { + s1.append(it.first); + s2.append(it.second); + } + + RTLIL::SigSpec y = module->addWire(NEW_ID); + RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y); + + if (make_gates) { + simplemap(module, c); + module->remove(c); + } + + or_input.append(y); + } + + if (GetSize(or_input) == 0) + return RTLIL::S1; + + if (GetSize(or_input) == 1) + return or_input; + + RTLIL::SigSpec y = module->addWire(NEW_ID); + RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y); + + if (make_gates) { + simplemap(module, c); + module->remove(c); + } + + return y; + } + + void handle_dff_cell(RTLIL::Cell *dff_cell) + { + RTLIL::SigSpec sig_d = sigmap(dff_cell->getPort("\\D")); + RTLIL::SigSpec sig_q = sigmap(dff_cell->getPort("\\Q")); + + std::map<patterns_t, std::set<int>> grouped_patterns; + std::set<int> remaining_indices; + + for (int i = 0 ; i < GetSize(sig_d); i++) { + patterns_t patterns = find_muxtree_feedback_patterns(sig_d[i], sig_q[i], pattern_t()); + if (!patterns.empty()) { + simplify_patterns(patterns); + grouped_patterns[patterns].insert(i); + } else + remaining_indices.insert(i); + } + + for (auto &it : grouped_patterns) { + RTLIL::SigSpec new_sig_d, new_sig_q; + for (int i : it.second) { + new_sig_d.append(sig_d[i]); + new_sig_q.append(sig_q[i]); + } + if (!direct_to.empty()) { + log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_to), log_signal(new_sig_d), log_signal(new_sig_q)); + dff_cell->setPort("\\E", make_patterns_logic(it.first, true)); + dff_cell->type = direct_to; + } else + if (dff_cell->type == "$dff") { + RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort("\\CLK"), make_patterns_logic(it.first, false), + new_sig_d, new_sig_q, dff_cell->getParam("\\CLK_POLARITY").as_bool(), true); + log(" created $dffe cell %s for %s -> %s.\n", log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q)); + } else { + RTLIL::Cell *new_cell = module->addDffeGate(NEW_ID, dff_cell->getPort("\\C"), make_patterns_logic(it.first, true), + new_sig_d, new_sig_q, dff_cell->type == "$_DFF_P_", true); + log(" created %s cell %s for %s -> %s.\n", log_id(new_cell->type), log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q)); + } + } + + if (!direct_to.empty()) + return; + + if (remaining_indices.empty()) { + log(" removing now obsolete cell %s.\n", log_id(dff_cell)); + module->remove(dff_cell); + } else if (GetSize(remaining_indices) != GetSize(sig_d)) { + log(" removing %d now obsolete bits from cell %s.\n", GetSize(sig_d) - GetSize(remaining_indices), log_id(dff_cell)); + RTLIL::SigSpec new_sig_d, new_sig_q; + for (int i : remaining_indices) { + new_sig_d.append(sig_d[i]); + new_sig_q.append(sig_q[i]); + } + dff_cell->setPort("\\D", new_sig_d); + dff_cell->setPort("\\Q", new_sig_q); + dff_cell->setParam("\\WIDTH", GetSize(remaining_indices)); + } + } + + void run() + { + log("Transforming $dff to $dffe cells in module %s:\n", log_id(module)); + for (auto dff_cell : dff_cells) + handle_dff_cell(dff_cell); + } +}; + +struct Dff2dffePass : public Pass { + Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dff2dffe [selection]\n"); + log("\n"); + log("This pass transforms $dff cells driven by a tree of multiplexers with one or\n"); + log("more feedback paths to $dffe cells. It also works on gate-level cells such as\n"); + log("$_DFF_P_, $_DFF_N_ and $_MUX_.\n"); + log("\n"); + log(" -unmap\n"); + log(" operate in the opposite direction: replace $dffe cells with combinations\n"); + log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n"); + log("\n"); + log(" -direct <internal_gate_type> <external_gate_type>\n"); + log(" map directly to external gate type. <internal_gate_type> can\n"); + log(" be any internal gate-level FF cell (except $_DFFE_??_). the\n"); + log(" <external_gate_type> is the cell type name for a cell with an\n"); + log(" identical interface to the <internal_gate_type>, except it\n"); + log(" also has an high-active enable port 'E'.\n"); + log(" Usually <external_gate_type> is an intemediate cell type\n"); + log(" that is then translated to the final type using 'techmap'.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); + + bool unmap_mode = false; + RTLIL::IdString direct_from, direct_to; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-unmap") { + unmap_mode = true; + continue; + } + if (args[argidx] == "-direct" && argidx + 2 < args.size()) { + direct_from = RTLIL::escape_id(args[++argidx]); + direct_to = RTLIL::escape_id(args[++argidx]); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + if (!mod->has_processes_warn()) + { + if (unmap_mode) { + for (auto cell : mod->selected_cells()) { + if (cell->type == "$dffe") { + RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort("\\D"))); + mod->addDff(NEW_ID, cell->getPort("\\CLK"), tmp, cell->getPort("\\Q"), cell->getParam("\\CLK_POLARITY").as_bool()); + if (cell->getParam("\\EN_POLARITY").as_bool()) + mod->addMux(NEW_ID, cell->getPort("\\Q"), cell->getPort("\\D"), cell->getPort("\\EN"), tmp); + else + mod->addMux(NEW_ID, cell->getPort("\\D"), cell->getPort("\\Q"), cell->getPort("\\EN"), tmp); + mod->remove(cell); + continue; + } + if (cell->type.substr(0, 7) == "$_DFFE_") { + bool clk_pol = cell->type.substr(7, 1) == "P"; + bool en_pol = cell->type.substr(8, 1) == "P"; + RTLIL::SigSpec tmp = mod->addWire(NEW_ID); + mod->addDff(NEW_ID, cell->getPort("\\C"), tmp, cell->getPort("\\Q"), clk_pol); + if (en_pol) + mod->addMux(NEW_ID, cell->getPort("\\Q"), cell->getPort("\\D"), cell->getPort("\\E"), tmp); + else + mod->addMux(NEW_ID, cell->getPort("\\D"), cell->getPort("\\Q"), cell->getPort("\\E"), tmp); + mod->remove(cell); + continue; + } + } + continue; + } + + Dff2dffeWorker worker(mod, direct_from, direct_to); + worker.run(); + } + } +} Dff2dffePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 07993b86..b0318a0b 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -23,7 +23,8 @@ #include <string.h> #include <errno.h> -using namespace PASS_DFFLIBMAP; +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN struct cell_mapping { std::string cell_name; @@ -102,12 +103,12 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, return false; } -static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval) +static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode) { LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; - float best_cell_area = 0; + double best_cell_area = 0; if (ast->id != "library") log_error("Format error in liberty file.\n"); @@ -143,7 +144,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool this_cell_ports[cell_rst_pin] = 'R'; this_cell_ports[cell_next_pin] = 'D'; - float area = 0; + double area = 0; LibertyAst *ar = cell->find("area"); if (ar != NULL && !ar->value.empty()) area = atof(ar->value.c_str()); @@ -192,18 +193,27 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.substr(1).c_str()); - cell_mappings[cell_type].cell_name = best_cell->args[0]; - cell_mappings[cell_type].ports = best_cell_ports; + log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + if (prepare_mode) { + cell_mappings[cell_type].cell_name = cell_type; + cell_mappings[cell_type].ports["C"] = 'C'; + if (has_reset) + cell_mappings[cell_type].ports["R"] = 'R'; + cell_mappings[cell_type].ports["D"] = 'D'; + cell_mappings[cell_type].ports["Q"] = 'Q'; + } else { + cell_mappings[cell_type].cell_name = best_cell->args[0]; + cell_mappings[cell_type].ports = best_cell_ports; + } } } -static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bool setpol, bool clrpol) +static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode) { LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; - float best_cell_area = 0; + double best_cell_area = 0; if (ast->id != "library") log_error("Format error in liberty file.\n"); @@ -235,7 +245,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo this_cell_ports[cell_clr_pin] = 'R'; this_cell_ports[cell_next_pin] = 'D'; - float area = 0; + double area = 0; LibertyAst *ar = cell->find("area"); if (ar != NULL && !ar->value.empty()) area = atof(ar->value.c_str()); @@ -284,9 +294,18 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.substr(1).c_str()); - cell_mappings[cell_type].cell_name = best_cell->args[0]; - cell_mappings[cell_type].ports = best_cell_ports; + log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + if (prepare_mode) { + cell_mappings[cell_type].cell_name = cell_type; + cell_mappings[cell_type].ports["C"] = 'C'; + cell_mappings[cell_type].ports["S"] = 'S'; + cell_mappings[cell_type].ports["R"] = 'R'; + cell_mappings[cell_type].ports["D"] = 'D'; + cell_mappings[cell_type].ports["Q"] = 'Q'; + } else { + cell_mappings[cell_type].cell_name = best_cell->args[0]; + cell_mappings[cell_type].ports = best_cell_ports; + } } } @@ -346,8 +365,12 @@ static void map_sr_to_arst(const char *from, const char *to) 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]; - char to_clk_pol = to[6], to_rst_pol = to[7], to_rst_val = to[8]; + char from_clk_pol YS_ATTRIBUTE(unused) = from[8]; + char from_set_pol = from[9]; + char from_clr_pol = from[10]; + char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; + char to_rst_pol YS_ATTRIBUTE(unused) = to[7]; + char to_rst_val = to[8]; log_assert(from_clk_pol == to_clk_pol); log_assert(to_rst_pol == from_set_pol && to_rst_pol == from_clr_pol); @@ -383,7 +406,7 @@ static void map_sr_to_arst(const char *from, const char *to) } } -static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module) +static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode) { log("Mapping DFF cells in module `%s':\n", module->name.c_str()); @@ -402,7 +425,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module) module->remove(cell); cell_mapping &cm = cell_mappings[cell_type]; - RTLIL::Cell *new_cell = module->addCell(cell_name, "\\" + cm.cell_name); + RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name); for (auto &port : cm.ports) { RTLIL::SigSpec sig; @@ -411,7 +434,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module) } 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)); + sig = module->addWire(NEW_ID, GetSize(old_sig)); module->addNotGate(NEW_ID, sig, old_sig); } else if ('a' <= port.second && port.second <= 'z') { @@ -438,7 +461,7 @@ struct DfflibmapPass : public Pass { virtual void help() { log("\n"); - log(" dfflibmap -liberty <file> [selection]\n"); + log(" dfflibmap [-prepare] -liberty <file> [selection]\n"); log("\n"); log("Map internal flip-flop cells to the flip-flop cells in the technology\n"); log("library specified in the given liberty file.\n"); @@ -446,12 +469,17 @@ struct DfflibmapPass : public Pass { log("This pass may add inverters as needed. Therefore it is recommended to\n"); log("first run this pass and then map the logic paths to the target technology.\n"); log("\n"); + log("When called with -prepare, this command will convert the internal FF cells\n"); + log("to the internal cell types that best match the cells found in the given\n"); + log("liberty file.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); std::string liberty_file; + bool prepare_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -461,6 +489,10 @@ struct DfflibmapPass : public Pass { liberty_file = args[++argidx]; continue; } + if (arg == "-prepare") { + prepare_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -475,26 +507,26 @@ struct DfflibmapPass : public Pass { LibertyParser libparser(f); f.close(); - find_cell(libparser.ast, "$_DFF_N_", false, false, false, false); - find_cell(libparser.ast, "$_DFF_P_", true, false, false, false); - - find_cell(libparser.ast, "$_DFF_NN0_", false, true, false, false); - find_cell(libparser.ast, "$_DFF_NN1_", false, true, false, true); - find_cell(libparser.ast, "$_DFF_NP0_", false, true, true, false); - find_cell(libparser.ast, "$_DFF_NP1_", false, true, true, true); - find_cell(libparser.ast, "$_DFF_PN0_", true, true, false, false); - find_cell(libparser.ast, "$_DFF_PN1_", true, true, false, true); - find_cell(libparser.ast, "$_DFF_PP0_", true, true, true, false); - find_cell(libparser.ast, "$_DFF_PP1_", true, true, true, true); - - find_cell_sr(libparser.ast, "$_DFFSR_NNN_", false, false, false); - find_cell_sr(libparser.ast, "$_DFFSR_NNP_", false, false, true); - find_cell_sr(libparser.ast, "$_DFFSR_NPN_", false, true, false); - find_cell_sr(libparser.ast, "$_DFFSR_NPP_", false, true, true); - find_cell_sr(libparser.ast, "$_DFFSR_PNN_", true, false, false); - find_cell_sr(libparser.ast, "$_DFFSR_PNP_", true, false, true); - find_cell_sr(libparser.ast, "$_DFFSR_PPN_", true, true, false); - find_cell_sr(libparser.ast, "$_DFFSR_PPP_", true, true, true); + find_cell(libparser.ast, "$_DFF_N_", false, false, false, false, prepare_mode); + find_cell(libparser.ast, "$_DFF_P_", true, false, false, false, prepare_mode); + + find_cell(libparser.ast, "$_DFF_NN0_", false, true, false, false, prepare_mode); + find_cell(libparser.ast, "$_DFF_NN1_", false, true, false, true, prepare_mode); + find_cell(libparser.ast, "$_DFF_NP0_", false, true, true, false, prepare_mode); + find_cell(libparser.ast, "$_DFF_NP1_", false, true, true, true, prepare_mode); + find_cell(libparser.ast, "$_DFF_PN0_", true, true, false, false, prepare_mode); + find_cell(libparser.ast, "$_DFF_PN1_", true, true, false, true, prepare_mode); + find_cell(libparser.ast, "$_DFF_PP0_", true, true, true, false, prepare_mode); + find_cell(libparser.ast, "$_DFF_PP1_", true, true, true, true, prepare_mode); + + find_cell_sr(libparser.ast, "$_DFFSR_NNN_", false, false, false, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_NNP_", false, false, true, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_NPN_", false, true, false, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_NPP_", false, true, true, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_PNN_", true, false, false, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_PNP_", true, false, true, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_PPN_", true, true, false, prepare_mode); + find_cell_sr(libparser.ast, "$_DFFSR_PPP_", true, true, true, prepare_mode); // try to implement as many cells as possible just by inverting // the SET and RESET pins. If necessary, implement cell types @@ -532,9 +564,10 @@ struct DfflibmapPass : public Pass { for (auto &it : design->modules_) if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox")) - dfflibmap(design, it.second); + dfflibmap(design, it.second, prepare_mode); cell_mappings.clear(); } } DfflibmapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index 221e9e49..ff99040e 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -26,256 +26,240 @@ #include <stdio.h> #include <string.h> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + using RTLIL::id2cstr; -namespace +class SubCircuitSolver : public SubCircuit::Solver { - 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; +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) - { - } + 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) { - size_t nc = needleAttr.count(it), hc = haystackAttr.count(it); - if (nc != hc || (nc > 0 && needleAttr.at(it) != haystackAttr.at(it))) - return false; - } - return true; + bool compareAttributes(const std::set<RTLIL::IdString> &attr, const dict<RTLIL::IdString, RTLIL::Const> &needleAttr, const dict<RTLIL::IdString, RTLIL::Const> &haystackAttr) + { + for (auto &it : attr) { + size_t nc = needleAttr.count(it), hc = haystackAttr.count(it); + if (nc != hc || (nc > 0 && needleAttr.at(it) != haystackAttr.at(it))) + return false; } + 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 - + 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; - } - 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; + #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; + } - if (!needleCell || !haystackCell) { - log_assert(!needleCell && !haystackCell); - return true; - } + 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 (!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 (!needleCell || !haystackCell) { + log_assert(!needleCell && !haystackCell); + return true; + } - if (cell_attr.size() > 0 && !compareAttributes(cell_attr, needleCell->attributes, haystackCell->attributes)) + 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 (wire_attr.size() > 0) - { - RTLIL::Wire *lastNeedleWire = NULL; - RTLIL::Wire *lastHaystackWire = NULL; - std::map<RTLIL::IdString, RTLIL::Const> emptyAttr; + if (cell_attr.size() > 0 && !compareAttributes(cell_attr, needleCell->attributes, haystackCell->attributes)) + return false; - for (auto &conn : needleCell->connections()) - { - RTLIL::SigSpec needleSig = conn.second; - RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str())); - - 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; - lastNeedleWire = needleWire, lastHaystackWire = haystackWire; - } + if (wire_attr.size() > 0) + { + RTLIL::Wire *lastNeedleWire = NULL; + RTLIL::Wire *lastHaystackWire = NULL; + dict<RTLIL::IdString, RTLIL::Const> emptyAttr; + + for (auto &conn : needleCell->connections()) + { + RTLIL::SigSpec needleSig = conn.second; + RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str())); + + 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; + lastNeedleWire = needleWire, lastHaystackWire = haystackWire; } } - - return true; } - }; - struct bit_ref_t { - std::string cell, port; - int bit; - }; + return true; + } +}; - bool module2graph(SubCircuit::Graph &graph, RTLIL::Module *mod, bool constports, RTLIL::Design *sel = NULL, - int max_fanout = -1, std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> *split = NULL) - { - SigMap sigmap(mod); - std::map<RTLIL::SigBit, bit_ref_t> sig_bit_ref; +struct bit_ref_t { + std::string cell, port; + int bit; +}; - if (sel && !sel->selected(mod)) { - log(" Skipping module %s as it is not selected.\n", id2cstr(mod->name)); - return false; - } +bool module2graph(SubCircuit::Graph &graph, RTLIL::Module *mod, bool constports, RTLIL::Design *sel = NULL, + int max_fanout = -1, std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> *split = NULL) +{ + SigMap sigmap(mod); + std::map<RTLIL::SigBit, bit_ref_t> sig_bit_ref; - if (mod->processes.size() > 0) { - log(" Skipping module %s as it contains unprocessed processes.\n", id2cstr(mod->name)); - return false; - } + if (sel && !sel->selected(mod)) { + log(" Skipping module %s as it is not selected.\n", id2cstr(mod->name)); + return false; + } - if (constports) { - graph.createNode("$const$0", "$const$0", NULL, true); - graph.createNode("$const$1", "$const$1", NULL, true); - graph.createNode("$const$x", "$const$x", NULL, true); - graph.createNode("$const$z", "$const$z", NULL, true); - graph.createPort("$const$0", "\\Y", 1); - graph.createPort("$const$1", "\\Y", 1); - graph.createPort("$const$x", "\\Y", 1); - graph.createPort("$const$z", "\\Y", 1); - graph.markExtern("$const$0", "\\Y", 0); - graph.markExtern("$const$1", "\\Y", 0); - graph.markExtern("$const$x", "\\Y", 0); - graph.markExtern("$const$z", "\\Y", 0); - } + if (mod->processes.size() > 0) { + log(" Skipping module %s as it contains unprocessed processes.\n", id2cstr(mod->name)); + return false; + } - std::map<std::pair<RTLIL::Wire*, int>, int> sig_use_count; - if (max_fanout > 0) - for (auto &cell_it : mod->cells_) - { - RTLIL::Cell *cell = cell_it.second; - if (!sel || sel->selected(mod, cell)) - for (auto &conn : cell->connections()) { - RTLIL::SigSpec conn_sig = conn.second; - sigmap.apply(conn_sig); - for (auto &bit : conn_sig) - if (bit.wire != NULL) - sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)]++; - } - } + if (constports) { + graph.createNode("$const$0", "$const$0", NULL, true); + graph.createNode("$const$1", "$const$1", NULL, true); + graph.createNode("$const$x", "$const$x", NULL, true); + graph.createNode("$const$z", "$const$z", NULL, true); + graph.createPort("$const$0", "\\Y", 1); + graph.createPort("$const$1", "\\Y", 1); + graph.createPort("$const$x", "\\Y", 1); + graph.createPort("$const$z", "\\Y", 1); + graph.markExtern("$const$0", "\\Y", 0); + graph.markExtern("$const$1", "\\Y", 0); + graph.markExtern("$const$x", "\\Y", 0); + graph.markExtern("$const$z", "\\Y", 0); + } - // create graph nodes from cells + std::map<std::pair<RTLIL::Wire*, int>, int> sig_use_count; + if (max_fanout > 0) for (auto &cell_it : mod->cells_) { RTLIL::Cell *cell = cell_it.second; - if (sel && !sel->selected(mod, cell)) - continue; + if (!sel || sel->selected(mod, cell)) + for (auto &conn : cell->connections()) { + RTLIL::SigSpec conn_sig = conn.second; + sigmap.apply(conn_sig); + for (auto &bit : conn_sig) + if (bit.wire != NULL) + sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)]++; + } + } - std::string type = cell->type.str(); - if (sel == NULL && type.substr(0, 2) == "\\$") - type = type.substr(1); - graph.createNode(cell->name.str(), type, (void*)cell); + // create graph nodes from cells + for (auto &cell_it : mod->cells_) + { + RTLIL::Cell *cell = cell_it.second; + if (sel && !sel->selected(mod, cell)) + continue; - for (auto &conn : cell->connections()) - { - graph.createPort(cell->name.str(), conn.first.str(), conn.second.size()); + std::string type = cell->type.str(); + if (sel == NULL && type.substr(0, 2) == "\\$") + type = type.substr(1); + graph.createNode(cell->name.str(), type, (void*)cell); - if (split && split->count(std::pair<RTLIL::IdString, RTLIL::IdString>(cell->type, conn.first)) > 0) - continue; + for (auto &conn : cell->connections()) + { + graph.createPort(cell->name.str(), conn.first.str(), conn.second.size()); - RTLIL::SigSpec conn_sig = conn.second; - sigmap.apply(conn_sig); + if (split && split->count(std::pair<RTLIL::IdString, RTLIL::IdString>(cell->type, conn.first)) > 0) + continue; - for (int i = 0; i < conn_sig.size(); i++) - { - auto &bit = conn_sig[i]; - - if (bit.wire == NULL) { - if (constports) { - std::string node = "$const$x"; - 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.str(), conn.first.str(), i, int(bit.data)); - continue; - } + RTLIL::SigSpec conn_sig = conn.second; + sigmap.apply(conn_sig); - if (max_fanout > 0 && sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)] > max_fanout) - continue; + for (int i = 0; i < conn_sig.size(); i++) + { + auto &bit = conn_sig[i]; + + if (bit.wire == NULL) { + if (constports) { + std::string node = "$const$x"; + 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.str(), conn.first.str(), i, int(bit.data)); + continue; + } - if (sel && !sel->selected(mod, bit.wire)) - continue; + if (max_fanout > 0 && sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)] > max_fanout) + continue; - 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; - } + if (sel && !sel->selected(mod, bit.wire)) + continue; + if (sig_bit_ref.count(bit) == 0) { 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); + bit_ref.cell = cell->name.str(); + bit_ref.port = conn.first.str(); + bit_ref.bit = i; } - } - } - - // mark external signals (used in non-selected cells) - for (auto &cell_it : mod->cells_) - { - RTLIL::Cell *cell = cell_it.second; - if (sel && !sel->selected(mod, cell)) - for (auto &conn : cell->connections()) - { - RTLIL::SigSpec conn_sig = conn.second; - sigmap.apply(conn_sig); - 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); - } - } + 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 module ports) - for (auto &wire_it : mod->wires_) - { - RTLIL::Wire *wire = wire_it.second; - if (wire->port_id > 0) + // mark external signals (used in non-selected cells) + for (auto &cell_it : mod->cells_) + { + RTLIL::Cell *cell = cell_it.second; + if (sel && !sel->selected(mod, cell)) + for (auto &conn : cell->connections()) { - RTLIL::SigSpec conn_sig(wire); + RTLIL::SigSpec conn_sig = conn.second; sigmap.apply(conn_sig); for (auto &bit : conn_sig) @@ -284,70 +268,86 @@ namespace graph.markExtern(bit_ref.cell, bit_ref.port, bit_ref.bit); } } - } - - // graph.print(); - return true; } - RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit::Solver::Result &match) + // mark external signals (used in module ports) + for (auto &wire_it : mod->wires_) { - SigMap sigmap(needle); - SigSet<std::pair<RTLIL::IdString, int>> sig2port; - - // create new 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_) { - RTLIL::Wire *wire = it.second; - if (wire->port_id > 0) { - for (int i = 0; i < wire->width; i++) - 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)); - } + RTLIL::Wire *wire = wire_it.second; + if (wire->port_id > 0) + { + RTLIL::SigSpec conn_sig(wire); + sigmap.apply(conn_sig); + + 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); + } } + } - // delete replaced cells and connect new ports - for (auto &it : match.mappings) - { - auto &mapping = it.second; - RTLIL::Cell *needle_cell = (RTLIL::Cell*)mapping.needleUserData; - RTLIL::Cell *haystack_cell = (RTLIL::Cell*)mapping.haystackUserData; + // graph.print(); + return true; +} - if (needle_cell == NULL) - continue; +RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit::Solver::Result &match) +{ + SigMap sigmap(needle); + SigSet<std::pair<RTLIL::IdString, int>> sig2port; + + // create new 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_) { + RTLIL::Wire *wire = it.second; + if (wire->port_id > 0) { + for (int i = 0; i < wire->width; i++) + 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)); + } + } - for (auto &conn : needle_cell->connections()) { - RTLIL::SigSpec sig = sigmap(conn.second); - 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); - } + // delete replaced cells and connect new ports + for (auto &it : match.mappings) + { + auto &mapping = it.second; + RTLIL::Cell *needle_cell = (RTLIL::Cell*)mapping.needleUserData; + RTLIL::Cell *haystack_cell = (RTLIL::Cell*)mapping.haystackUserData; + + if (needle_cell == NULL) + continue; + + for (auto &conn : needle_cell->connections()) { + RTLIL::SigSpec sig = sigmap(conn.second); + 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->remove(haystack_cell); } - return cell; + haystack->remove(haystack_cell); } - bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right) - { - int left_idx = 0, right_idx = 0; - if (left->attributes.count("\\extract_order") > 0) - left_idx = left->attributes.at("\\extract_order").as_int(); - if (right->attributes.count("\\extract_order") > 0) - right_idx = right->attributes.at("\\extract_order").as_int(); - if (left_idx != right_idx) - return left_idx < right_idx; - return left->name < right->name; - } + return cell; +} + +bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right) +{ + int left_idx = 0, right_idx = 0; + if (left->attributes.count("\\extract_order") > 0) + left_idx = left->attributes.at("\\extract_order").as_int(); + if (right->attributes.count("\\extract_order") > 0) + right_idx = right->attributes.at("\\extract_order").as_int(); + if (left_idx != right_idx) + return left_idx < right_idx; + return left->name < right->name; } struct ExtractPass : public Pass { @@ -518,24 +518,21 @@ struct ExtractPass : public Pass { if (args[argidx] == "-swap" && argidx+2 < args.size()) { std::string type = RTLIL::escape_id(args[++argidx]); std::set<std::string> ports; - char *ports_str = strdup(args[++argidx].c_str()); - for (char *sptr, *p = strtok_r(ports_str, ",\t\r\n ", &sptr); p != NULL; p = strtok_r(NULL, ",\t\r\n ", &sptr)) + std::string ports_str = args[++argidx], p; + while (!(p = next_token(ports_str, ",\t\r\n ")).empty()) ports.insert(RTLIL::escape_id(p)); - free(ports_str); solver.addSwappablePorts(type, ports); continue; } if (args[argidx] == "-perm" && argidx+3 < args.size()) { std::string type = RTLIL::escape_id(args[++argidx]); std::vector<std::string> map_left, map_right; - char *left_str = strdup(args[++argidx].c_str()); - char *right_str = strdup(args[++argidx].c_str()); - for (char *sptr, *p = strtok_r(left_str, ",\t\r\n ", &sptr); p != NULL; p = strtok_r(NULL, ",\t\r\n ", &sptr)) + std::string left_str = args[++argidx]; + std::string right_str = args[++argidx], p; + while (!(p = next_token(left_str, ",\t\r\n ")).empty()) map_left.push_back(RTLIL::escape_id(p)); - for (char *sptr, *p = strtok_r(right_str, ",\t\r\n ", &sptr); p != NULL; p = strtok_r(NULL, ",\t\r\n ", &sptr)) + while (!(p = next_token(right_str, ",\t\r\n ")).empty()) map_right.push_back(RTLIL::escape_id(p)); - free(left_str); - free(right_str); if (map_left.size() != map_right.size()) log_cmd_error("Arguments to -perm are not a valid permutation!\n"); std::map<std::string, std::string> map; @@ -665,7 +662,7 @@ struct ExtractPass : public Pass { log("Solving for %s in %s.\n", ("needle_" + RTLIL::unescape_id(needle->name)).c_str(), haystack_it.first.c_str()); solver.solve(results, "needle_" + RTLIL::unescape_id(needle->name), haystack_it.first, false); } - log("Found %zd matches.\n", results.size()); + log("Found %d matches.\n", GetSize(results)); if (results.size() > 0) { @@ -761,3 +758,4 @@ struct ExtractPass : public Pass { } } ExtractPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index 784c4cf3..9a14ffa3 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -21,6 +21,9 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static std::string hicell_celltype, hicell_portname; static std::string locell_celltype, locell_portname; static bool singleton_mode; @@ -57,9 +60,7 @@ struct HilomapPass : public Pass { log("\n"); log(" hilomap [options] [selection]\n"); log("\n"); - log("Map module inputs/outputs to PAD cells from a library. This pass\n"); - log("can only map to very simple PAD cells. Use 'techmap' to further map\n"); - log("the resulting cells to more sophisticated PAD cells.\n"); + log("Map constants to 'tielo' and 'tiehi' driver cells.\n"); log("\n"); log(" -hicell <celltype> <portname>\n"); log(" Replace constant hi bits with this cell.\n"); @@ -75,7 +76,7 @@ struct HilomapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HILOPAD pass (mapping to constant drivers).\n"); + log_header("Executing HILOMAP pass (mapping to constant drivers).\n"); hicell_celltype = std::string(); hicell_portname = std::string(); @@ -119,3 +120,4 @@ struct HilomapPass : public Pass { } } HilomapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 9cd23ce6..3fba0e61 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -21,7 +21,10 @@ #include "kernel/rtlil.h" #include "kernel/log.h" -static void split_portname_pair(std::string &port1, std::string &port2) +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void split_portname_pair(std::string &port1, std::string &port2) { size_t pos = port1.find_first_of(':'); if (pos != std::string::npos) { @@ -59,8 +62,8 @@ struct IopadmapPass : public Pass { 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(" are wider. (the default behavior is to create word-wide\n"); + log(" buffers using -widthparam to set the word size on the cell.)\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) @@ -111,18 +114,11 @@ struct IopadmapPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = it.second; - - if (!design->selected(module) || module->get_bool_attribute("\\blackbox")) - continue; - - for (auto &it2 : module->wires_) + for (auto wire : module->selected_wires()) { - RTLIL::Wire *wire = it2.second; - - if (!wire->port_id || !design->selected(module, wire)) + if (!wire->port_id) continue; std::string celltype, portname, portname2; @@ -207,3 +203,4 @@ struct IopadmapPass : public Pass { } } IopadmapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 612fa111..def48039 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -29,7 +29,7 @@ #include "kernel/log.h" #endif -using namespace PASS_DFFLIBMAP; +using namespace Yosys; std::set<std::string> LibertyAst::blacklist; std::set<std::string> LibertyAst::whitelist; @@ -43,8 +43,6 @@ LibertyAst::~LibertyAst() LibertyAst *LibertyAst::find(std::string name) { - if (this == NULL) - return NULL; for (auto child : children) if (child->id == name) return child; @@ -107,14 +105,14 @@ int LibertyParser::lexer(std::string &str) } if (c == '"') { - str = c; + str = ""; while (1) { c = f.get(); if (c == '\n') line++; - str += c; if (c == '"') break; + str += c; } // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str()); return 'v'; @@ -244,21 +242,6 @@ void LibertyParser::error() /**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/ -// This is to not confuse the VIM syntax highlighting -#define CHECK_VAL_OPEN ( -#define CHECK_VAL_CLOSE ) - -#define CHECK(result, check) \ - CHECK_VAL_OPEN{ \ - auto _R = (result); \ - if (!(_R check)) { \ - fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \ - #result, (long int)_R, #check, __FILE__, __LINE__); \ - abort(); \ - } \ - _R; \ - }CHECK_VAL_CLOSE - #define CHECK_NV(result, check) \ do { \ auto _R = (result); \ @@ -280,6 +263,14 @@ void LibertyParser::error() /**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/ +LibertyAst *find_non_null(LibertyAst *node, const char *name) +{ + LibertyAst *ret = node->find(name); + if (ret == NULL) + fprintf(stderr, "Error: expected to find `%s' node.\n", name); + return ret; +} + std::string func2vl(std::string str) { for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) { @@ -388,7 +379,7 @@ void gen_verilogsim_cell(LibertyAst *ast) if (child->id != "pin") continue; CHECK_NV(child->args.size(), == 1); - LibertyAst *dir = CHECK(child->find("direction"), != NULL); + LibertyAst *dir = find_non_null(child, "direction"); LibertyAst *func = child->find("function"); printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str()); if (func != NULL) @@ -428,8 +419,8 @@ void gen_verilogsim_cell(LibertyAst *ast) const char *else_prefix = ""; if (!clear_expr.empty() && !preset_expr.empty()) { printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str()); - clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value); - clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value); + clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value); + clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value); printf(" end\n"); else_prefix = "else "; } @@ -449,7 +440,7 @@ void gen_verilogsim_cell(LibertyAst *ast) } if (*else_prefix) printf(" %sbegin\n", else_prefix); - std::string expr = CHECK(child->find("next_state"), != NULL)->value; + std::string expr = find_non_null(child, "next_state")->value; printf(" // %s\n", expr.c_str()); printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str()); printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str()); @@ -481,8 +472,8 @@ void gen_verilogsim_cell(LibertyAst *ast) const char *else_prefix = ""; if (!clear_expr.empty() && !preset_expr.empty()) { printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str()); - clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value); - clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value); + clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value); + clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value); printf(" end\n"); else_prefix = "else "; } @@ -502,7 +493,7 @@ void gen_verilogsim_cell(LibertyAst *ast) } if (!enable_expr.empty()) { printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str()); - std::string expr = CHECK(child->find("data_in"), != NULL)->value; + std::string expr = find_non_null(child, "data_in")->value; printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str()); printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str()); printf(" end\n"); diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 24748742..e947bd8c 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -25,7 +25,7 @@ #include <vector> #include <set> -namespace PASS_DFFLIBMAP +namespace Yosys { struct LibertyAst { diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 2d625eef..ffbd6289 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -20,7 +20,8 @@ #include "kernel/yosys.h" #include "kernel/macc.h" -extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false); +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN struct MaccmapWorker { @@ -48,7 +49,7 @@ struct MaccmapWorker void add(RTLIL::SigSpec a, bool is_signed, bool do_subtract) { - a.extend(width, is_signed); + a.extend_u0(width, is_signed); if (do_subtract) { a = module->Not(NEW_ID, a); @@ -61,16 +62,16 @@ struct MaccmapWorker void add(RTLIL::SigSpec a, RTLIL::SigSpec b, bool is_signed, bool do_subtract) { - if (SIZE(a) < SIZE(b)) + if (GetSize(a) < GetSize(b)) std::swap(a, b); - a.extend(width, is_signed); + a.extend_u0(width, is_signed); - if (SIZE(b) > width) - b.extend(width, is_signed); + if (GetSize(b) > width) + b.extend_u0(width, is_signed); - for (int i = 0; i < SIZE(b); i++) - if (is_signed && i+1 == SIZE(b)) + for (int i = 0; i < GetSize(b); i++) + if (is_signed && i+1 == GetSize(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); @@ -85,7 +86,7 @@ struct MaccmapWorker 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); + int start_index = 0, stop_index = GetSize(in1); while (start_index < stop_index && in1[start_index] == RTLIL::S0 && in2[start_index] == RTLIL::S0 && in3[start_index] == RTLIL::S0) start_index++; @@ -95,18 +96,18 @@ struct MaccmapWorker if (start_index == stop_index) { - out1 = RTLIL::SigSpec(0, SIZE(in1)); - out2 = RTLIL::SigSpec(0, SIZE(in1)); + out1 = RTLIL::SigSpec(0, GetSize(in1)); + out2 = RTLIL::SigSpec(0, GetSize(in1)); } else { - RTLIL::SigSpec out_zeros_lsb(0, start_index), out_zeros_msb(0, SIZE(in1)-stop_index); + RTLIL::SigSpec out_zeros_lsb(0, start_index), out_zeros_msb(0, GetSize(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); + int width = GetSize(in1); RTLIL::Wire *w1 = module->addWire(NEW_ID, width); RTLIL::Wire *w2 = module->addWire(NEW_ID, width); @@ -164,12 +165,12 @@ struct MaccmapWorker while (1) { - int free_bit_slots = tree_bit_slots(SIZE(summands)) - SIZE(tree_sum_bits); + int free_bit_slots = tree_bit_slots(GetSize(summands)) - GetSize(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)); + if (max_depth <= GetSize(bits.at(i))) { + max_depth = GetSize(bits.at(i)); max_position = i; } @@ -178,14 +179,14 @@ struct MaccmapWorker int required_bits = 0; for (int i = 0; i <= max_position; i++) - if (SIZE(bits.at(i)) == max_depth) + if (GetSize(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) { + if (GetSize(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--) @@ -199,23 +200,23 @@ struct MaccmapWorker } 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); + log(" packed %d (%d) bits / %d words into adder tree\n", GetSize(tree_sum_bits), unique_tree_bits, count_tree_words); - if (SIZE(summands) == 0) { + if (GetSize(summands) == 0) { log_assert(tree_sum_bits.empty()); return RTLIL::SigSpec(0, width); } - if (SIZE(summands) == 1) { + if (GetSize(summands) == 1) { log_assert(tree_sum_bits.empty()); return summands.front(); } - while (SIZE(summands) > 2) + while (GetSize(summands) > 2) { std::vector<RTLIL::SigSpec> new_summands; - for (int i = 0; i < SIZE(summands); i += 3) - if (i+2 < SIZE(summands)) { + for (int i = 0; i < GetSize(summands); i += 3) + if (i+2 < GetSize(summands)) { RTLIL::SigSpec in1 = summands[i]; RTLIL::SigSpec in2 = summands[i+1]; RTLIL::SigSpec in3 = summands[i+2]; @@ -256,9 +257,14 @@ struct MaccmapWorker } }; +PRIVATE_NAMESPACE_END +YOSYS_NAMESPACE_BEGIN + +extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false); + void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) { - int width = SIZE(cell->getPort("\\Y")); + int width = GetSize(cell->getPort("\\Y")); Macc macc; macc.from_cell(cell); @@ -273,15 +279,15 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) } for (auto &port : macc.ports) - if (SIZE(port.in_b) == 0) + if (GetSize(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"); + GetSize(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"); + GetSize(port.in_a), GetSize(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 (GetSize(macc.bit_ports) != 0) + log(" add bits %s (%d bits)\n", log_signal(macc.bit_ports), GetSize(macc.bit_ports)); if (unmap) { @@ -290,10 +296,10 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) for (auto &port : macc.ports) { summand_t this_summand; - if (SIZE(port.in_b)) { + if (GetSize(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) { + } else if (GetSize(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 { @@ -306,14 +312,14 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) for (auto &bit : macc.bit_ports) summands.push_back(summand_t(bit, false)); - if (SIZE(summands) == 0) + if (GetSize(summands) == 0) summands.push_back(summand_t(RTLIL::SigSpec(0, width), false)); - while (SIZE(summands) > 1) + while (GetSize(summands) > 1) { std::vector<summand_t> new_summands; - for (int i = 0; i < SIZE(summands); i += 2) { - if (i+1 < SIZE(summands)) { + for (int i = 0; i < GetSize(summands); i += 2) { + if (i+1 < GetSize(summands)) { summand_t this_summand; this_summand.first = module->addWire(NEW_ID, width); this_summand.second = summands[i].second && summands[i+1].second; @@ -342,7 +348,7 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) MaccmapWorker worker(module, width); for (auto &port : macc.ports) - if (SIZE(port.in_b) == 0) + if (GetSize(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); @@ -354,6 +360,9 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) } } +YOSYS_NAMESPACE_END +PRIVATE_NAMESPACE_BEGIN + struct MaccmapPass : public Pass { MaccmapPass() : Pass("maccmap", "mapping macc cells") { } virtual void help() @@ -392,3 +401,4 @@ struct MaccmapPass : public Pass { } } MaccmapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index f8d5d458..9cea5f45 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -17,53 +17,53 @@ * */ -#include "kernel/register.h" +#include "simplemap.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include <stdlib.h> #include <stdio.h> #include <string.h> -extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); +USING_YOSYS_NAMESPACE +YOSYS_NAMESPACE_BEGIN -static void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - sig_a.extend(SIZE(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); + sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); - for (int i = 0; i < SIZE(sig_y); i++) { + for (int i = 0; i < GetSize(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) +void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - sig_a.extend_u0(SIZE(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); + sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); module->connect(RTLIL::SigSig(sig_y, sig_a)); } -static void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_b = cell->getPort("\\B"); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); - 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()); + sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool()); + sig_b.extend_u0(GetSize(sig_y), cell->parameters.at("\\B_SIGNED").as_bool()); if (cell->type == "$xnor") { - RTLIL::SigSpec sig_t = module->addWire(NEW_ID, SIZE(sig_y)); + RTLIL::SigSpec sig_t = module->addWire(NEW_ID, GetSize(sig_y)); - for (int i = 0; i < SIZE(sig_y); i++) { + for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); gate->setPort("\\A", sig_t[i]); gate->setPort("\\Y", sig_y[i]); @@ -79,7 +79,7 @@ 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 < SIZE(sig_y); i++) { + for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_b[i]); @@ -87,7 +87,7 @@ static void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); @@ -182,7 +182,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) sig = RTLIL::SigSpec(0, 1); } -static void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); logic_reduce(module, sig_a); @@ -202,7 +202,7 @@ static void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) gate->setPort("\\Y", sig_y); } -static void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); logic_reduce(module, sig_a); @@ -231,13 +231,38 @@ static void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) gate->setPort("\\Y", sig_y); } -static void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_b = cell->getPort("\\B"); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + bool is_ne = cell->type == "$ne" || cell->type == "$nex"; + + RTLIL::SigSpec xor_out = module->addWire(NEW_ID, std::max(GetSize(sig_a), GetSize(sig_b))); + RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed); + simplemap_bitop(module, xor_cell); + module->remove(xor_cell); + + RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID); + RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out); + simplemap_reduce(module, reduce_cell); + module->remove(reduce_cell); + + if (!is_ne) { + RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y); + simplemap_lognot(module, not_cell); + module->remove(not_cell); + } +} - for (int i = 0; i < SIZE(sig_y); i++) { +void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) +{ + 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 < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_"); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_b[i]); @@ -246,7 +271,7 @@ static void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) { int offset = cell->parameters.at("\\OFFSET").as_int(); RTLIL::SigSpec sig_a = cell->getPort("\\A"); @@ -254,7 +279,7 @@ static void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) module->connect(RTLIL::SigSig(sig_y, sig_a.extract(offset, sig_y.size()))); } -static void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_ab = cell->getPort("\\A"); sig_ab.append(cell->getPort("\\B")); @@ -262,7 +287,7 @@ static void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell) module->connect(RTLIL::SigSig(sig_y, sig_ab)); } -static void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) { int width = cell->parameters.at("\\WIDTH").as_int(); char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N'; @@ -282,7 +307,7 @@ static void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell) +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'; @@ -301,7 +326,29 @@ static void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_dffe(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'; + char en_pol = cell->parameters.at("\\EN_POLARITY").as_bool() ? 'P' : 'N'; + + RTLIL::SigSpec sig_clk = cell->getPort("\\CLK"); + 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("$_DFFE_%c%c_", clk_pol, en_pol); + + for (int i = 0; i < width; i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->setPort("\\C", sig_clk); + gate->setPort("\\E", sig_en); + gate->setPort("\\D", sig_d[i]); + gate->setPort("\\Q", sig_q[i]); + } +} + +void simplemap_dffsr(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'; @@ -326,7 +373,7 @@ static void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_adff(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'; @@ -353,7 +400,7 @@ static void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell) +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'; @@ -388,16 +435,37 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL mappers["$logic_not"] = simplemap_lognot; mappers["$logic_and"] = simplemap_logbin; mappers["$logic_or"] = simplemap_logbin; + mappers["$eq"] = simplemap_eqne; + mappers["$eqx"] = simplemap_eqne; + mappers["$ne"] = simplemap_eqne; + mappers["$nex"] = simplemap_eqne; mappers["$mux"] = simplemap_mux; mappers["$slice"] = simplemap_slice; mappers["$concat"] = simplemap_concat; mappers["$sr"] = simplemap_sr; mappers["$dff"] = simplemap_dff; + mappers["$dffe"] = simplemap_dffe; mappers["$dffsr"] = simplemap_dffsr; mappers["$adff"] = simplemap_adff; mappers["$dlatch"] = simplemap_dlatch; } +void simplemap(RTLIL::Module *module, RTLIL::Cell *cell) +{ + static std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; + static bool initialized_mappers = false; + + if (!initialized_mappers) { + simplemap_get_mappers(mappers); + initialized_mappers = true; + } + + mappers.at(cell->type)(module, cell); +} + +YOSYS_NAMESPACE_END +PRIVATE_NAMESPACE_BEGIN + struct SimplemapPass : public Pass { SimplemapPass() : Pass("simplemap", "mapping simple coarse-grain cells") { } virtual void help() @@ -440,3 +508,4 @@ struct SimplemapPass : public Pass { } } SimplemapPass; +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h new file mode 100644 index 00000000..dc2a395d --- /dev/null +++ b/passes/techmap/simplemap.h @@ -0,0 +1,48 @@ +/* + * 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 SIMPLEMAP_H +#define SIMPLEMAP_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +extern void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap(RTLIL::Module *module, RTLIL::Cell *cell); + +extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); + +YOSYS_NAMESPACE_END + +#endif diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index ed466faa..ab748ed7 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -26,15 +26,20 @@ #include <stdio.h> #include <string.h> +#include "simplemap.h" #include "passes/techmap/techmap.inc" -// see simplemap.cc -extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); +YOSYS_NAMESPACE_BEGIN // 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) +YOSYS_NAMESPACE_END + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void apply_prefix(std::string prefix, std::string &id) { if (id[0] == '\\') id = prefix + "." + id.substr(1); @@ -42,7 +47,7 @@ static void apply_prefix(std::string prefix, std::string &id) id = "$techmap" + prefix + "." + id; } -static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) +void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) { std::vector<RTLIL::SigChunk> chunks = sig; for (auto &chunk : chunks) @@ -61,6 +66,11 @@ struct TechmapWorker 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; + dict<Module*, SigMap> sigmaps; + + pool<IdString> flatten_do_list; + pool<IdString> flatten_done_list; + pool<Cell*> flatten_keep_list; struct TechmapWireData { RTLIL::Wire *wire; @@ -90,7 +100,7 @@ struct TechmapWorker 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++) { + for (int i = 0; i < GetSize(conn.second); i++) { RTLIL::SigBit bit = sigmap(conn.second[i]); if (bit.wire == nullptr) { if (verbose) @@ -102,8 +112,10 @@ struct TechmapWorker 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); + } else { + connbits_map[bit] = std::pair<RTLIL::IdString, int>(conn.first, i); + constmap_info += stringf("|%s %d", log_id(conn.first), i); + } } return stringf("$paramod$constmap:%s%s", sha1(constmap_info).c_str(), tpl->name.c_str()); @@ -122,7 +134,7 @@ struct TechmapWorker continue; const char *q = strrchr(p+1, '.'); - p = q ? q : p+1; + p = q ? q+1 : p+1; if (!strncmp(p, "_TECHMAP_", 9)) { TechmapWireData record; @@ -146,16 +158,13 @@ struct TechmapWorker 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); + log_assert(GetSize(tpl->processes) == 0); } else log_error("Technology map yielded processes -> this is not supported (use -autoproc to run 'proc' automatically).\n"); } @@ -169,6 +178,22 @@ struct TechmapWorker break; } + dict<IdString, IdString> memory_renames; + + for (auto &it : tpl->memories) { + std::string m_name = it.first.str(); + apply_prefix(cell->name.str(), m_name); + RTLIL::Memory *m = new RTLIL::Memory; + m->name = m_name; + m->width = it.second->width; + m->start_offset = it.second->start_offset; + m->size = it.second->size; + m->attributes = it.second->attributes; + module->memories[m->name] = m; + memory_renames[it.first] = m->name; + design->select(module, m); + } + std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; for (auto &it : tpl->wires_) { @@ -215,6 +240,11 @@ struct TechmapWorker if (flatten_mode) { // more conservative approach: // connect internal and external wires + if (sigmaps.count(module) == 0) + sigmaps[module].set(module); + if (sigmaps.at(module)(c.first).has_const()) + log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", + log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second)); module->connect(c); } else { // approach that yields nicer outputs: @@ -245,6 +275,12 @@ struct TechmapWorker apply_prefix(cell->name.str(), it2.second, module); port_signal_map.apply(it2.second); } + + if (c->type == "$memrd" || c->type == "$memwr") { + IdString memid = c->getParam("\\MEMID").decode_string(); + log_assert(memory_renames.count(memid)); + c->setParam("\\MEMID", Const(memory_renames[memid].str())); + } } for (auto &it : tpl->connections()) { @@ -272,7 +308,7 @@ struct TechmapWorker SigMap sigmap(module); - TopoSort<RTLIL::Cell*> cells; + TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<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; @@ -291,12 +327,28 @@ struct TechmapWorker continue; } + if (flatten_mode) { + bool keepit = cell->get_bool_attribute("\\keep_hierarchy"); + for (auto &tpl_name : celltypeMap.at(cell_type)) + if (map->modules_[tpl_name]->get_bool_attribute("\\keep_hierarchy")) + keepit = true; + if (keepit) { + if (!flatten_keep_list[cell]) { + log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell)); + flatten_keep_list.insert(cell); + } + if (!flatten_done_list[cell->type]) + flatten_do_list.insert(cell->type); + continue; + } + } + for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); sig.remove_const(); - if (SIZE(sig) == 0) + if (GetSize(sig) == 0) continue; for (auto &tpl_name : celltypeMap.at(cell_type)) { @@ -334,7 +386,7 @@ struct TechmapWorker { RTLIL::IdString derived_name = tpl_name; RTLIL::Module *tpl = map->modules_[tpl_name]; - std::map<RTLIL::IdString, RTLIL::Const> parameters = cell->parameters; + std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end()); if (tpl->get_bool_attribute("\\blackbox")) continue; @@ -376,7 +428,7 @@ struct TechmapWorker int port_counter = 1; for (auto &c : extmapper_cell->connections_) { - RTLIL::Wire *w = extmapper_module->addWire(c.first, SIZE(c.second)); + RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second)); if (w->name == "\\Y" || w->name == "\\Q") w->port_output = true; else @@ -522,7 +574,7 @@ struct TechmapWorker tpl = techmap_cache[key]; } else { if (cell->parameters.size() != 0) { - derived_name = tpl->derive(map, parameters); + derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end())); tpl = map->module(derived_name); log_continue = true; } @@ -623,7 +675,7 @@ struct TechmapWorker } for (auto conn : cell->connections()) - for (int i = 0; i < SIZE(conn.second); i++) + for (int i = 0; i < GetSize(conn.second); i++) { RTLIL::SigBit bit = sigmap(conn.second[i]); RTLIL::SigBit tplbit(tpl->wire(conn.first), i); @@ -968,7 +1020,7 @@ struct TechmapPass : public Pass { 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; + dict<RTLIL::IdString, RTLIL::Module*> modules_new; for (auto &it : map->modules_) { if (it.first.substr(0, 2) == "\\$") it.second->name = it.first.substr(1); @@ -1027,6 +1079,9 @@ struct FlattenPass : public Pass { log("pass is very simmilar to the 'techmap' pass. The only difference is that this\n"); log("pass is using the current design as mapping library.\n"); log("\n"); + log("Cells and/or modules with the 'keep_hiearchy' attribute set will not be\n"); + log("flattened by this command.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -1039,8 +1094,8 @@ struct FlattenPass : public Pass { 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); + for (auto module : design->modules()) + celltypeMap[module->name].insert(module->name); RTLIL::Module *top_mod = NULL; if (design->full_selection()) @@ -1048,26 +1103,40 @@ struct FlattenPass : public Pass { 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 (worker.techmap_module(design, top_mod, design, handled_cells, celltypeMap, false)) - did_something = true; - } else { - for (auto mod : design->modules()) - if (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) - did_something = true; + if (top_mod != NULL) { + worker.flatten_do_list.insert(top_mod->name); + while (!worker.flatten_do_list.empty()) { + auto mod = design->module(*worker.flatten_do_list.begin()); + while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } + worker.flatten_done_list.insert(mod->name); + worker.flatten_do_list.erase(mod->name); } + } else { + for (auto mod : vector<Module*>(design->modules())) + while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } } log("No more expansions possible.\n"); - if (top_mod != NULL) { - std::map<RTLIL::IdString, RTLIL::Module*> new_modules; - for (auto mod : design->modules()) - if (mod == top_mod || mod->get_bool_attribute("\\blackbox")) { + if (top_mod != NULL) + { + pool<RTLIL::IdString> used_modules, new_used_modules; + new_used_modules.insert(top_mod->name); + while (!new_used_modules.empty()) { + pool<RTLIL::IdString> queue; + queue.swap(new_used_modules); + for (auto modname : queue) + used_modules.insert(modname); + for (auto modname : queue) + for (auto cell : design->module(modname)->cells()) + if (design->module(cell->type) && !used_modules[cell->type]) + new_used_modules.insert(cell->type); + } + + dict<RTLIL::IdString, RTLIL::Module*> new_modules; + for (auto mod : vector<Module*>(design->modules())) + if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) { new_modules[mod->name] = mod; } else { log("Deleting now unused module %s.\n", log_id(mod)); @@ -1080,3 +1149,4 @@ struct FlattenPass : public Pass { } } FlattenPass; +PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index 0a0ceb1d..09cb4195 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -21,6 +21,9 @@ #include "kernel/yosys.h" #include "kernel/satgen.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static uint32_t xorshift32_state = 123456789; static uint32_t xorshift32(uint32_t limit) { @@ -33,7 +36,7 @@ static uint32_t xorshift32(uint32_t limit) { static RTLIL::Wire *getw(std::vector<RTLIL::Wire*> &wires, RTLIL::Wire *w) { while (1) { - int idx = xorshift32(SIZE(wires)); + int idx = xorshift32(GetSize(wires)); if (wires[idx] != w && !wires[idx]->port_output) return wires[idx]; } @@ -124,28 +127,28 @@ static void test_abcloop() module->fixup_ports(); Pass::call(design, "clean"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); for (auto c : module->cells()) { - bool ok = satgen.importCell(c); + bool ok YS_ATTRIBUTE(unused) = 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> 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++) + for (int j = 0; j < GetSize(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)) { + if (!ez->solve(out_vec, results, assumptions)) { log("No stable solution for input %d found -> recreate module.\n", i); goto recreate_module; } @@ -153,10 +156,10 @@ static void test_abcloop() for (int j = 0; j < 4; j++) truthtab[i][j] = results[j]; - assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results))); std::vector<bool> results2; - if (ez.solve(out_vec, results2, assumptions)) { + if (ez->solve(out_vec, results2, assumptions)) { log("Two stable solutions for input %d found -> recreate module.\n", i); goto recreate_module; } @@ -174,17 +177,17 @@ static void test_abcloop() log("\n"); log("Pre- and post-abc truth table:\n"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); for (auto c : module->cells()) { - bool ok = satgen.importCell(c); + bool ok YS_ATTRIBUTE(unused) = 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> inverse_in_vec = ez->vec_not(in_vec); std::vector<int> out_vec = satgen.importSigSpec(out_sig); @@ -194,14 +197,14 @@ static void test_abcloop() for (int i = 0; i < 16; i++) { std::vector<int> assumptions; - for (int j = 0; j < SIZE(in_vec); j++) + for (int j = 0; j < GetSize(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)) { + if (!ez->solve(out_vec, results, assumptions)) { log("No stable solution for input %d found.\n", i); found_error = true; continue; @@ -210,10 +213,10 @@ static void test_abcloop() for (int j = 0; j < 4; j++) truthtab2[i][j] = results[j]; - assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results))); std::vector<bool> results2; - if (ez.solve(out_vec, results2, assumptions)) { + if (ez->solve(out_vec, results2, assumptions)) { log("Two stable solutions for input %d found -> recreate module.\n", i); found_error = true; } @@ -262,13 +265,13 @@ struct TestAbcloopPass : public Pass { xorshift32_state = 0; int argidx; - for (argidx = 1; argidx < SIZE(args); argidx++) + for (argidx = 1; argidx < GetSize(args); argidx++) { - if (args[argidx] == "-n" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-n" && argidx+1 < GetSize(args)) { num_iter = atoi(args[++argidx].c_str()); continue; } - if (args[argidx] == "-s" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-s" && argidx+1 < GetSize(args)) { xorshift32_state = atoi(args[++argidx].c_str()); continue; } @@ -283,3 +286,4 @@ struct TestAbcloopPass : public Pass { } } TestAbcloopPass; +PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index eed0f75f..7c1b671c 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -22,6 +22,7 @@ #include <stdio.h> #include <time.h> +USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN static std::string id(std::string internal_id) @@ -91,7 +92,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("end\n"); f << stringf("endtask\n\n"); - for (auto it = design->modules_.begin(); it != design->modules_.end(); it++) + 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; @@ -105,7 +106,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) 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++) { + for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); ++it2) { RTLIL::Wire *wire = it2->second; if (wire->port_output) { count_ports++; @@ -114,8 +115,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) } 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++) { + 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; @@ -134,7 +135,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) } } 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++) { + 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(), @@ -145,23 +146,23 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) 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++) + 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++) + 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++) { + 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++) + 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++) { + 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++) { + 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()); @@ -194,7 +195,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) 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(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1); } f << stringf("end\n"); f << stringf("endtask\n\n"); @@ -292,7 +293,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) 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++) + 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"); @@ -335,9 +336,9 @@ struct TestAutotbBackend : public Backend { log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); int argidx; - for (argidx = 1; argidx < SIZE(args); argidx++) + for (argidx = 1; argidx < GetSize(args); argidx++) { - if (args[argidx] == "-n" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-n" && argidx+1 < GetSize(args)) { num_iter = atoi(args[++argidx].c_str()); continue; } diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 1fa90b54..268f2559 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -24,6 +24,9 @@ #include "kernel/macc.h" #include <algorithm> +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + static uint32_t xorshift32_state = 123456789; static uint32_t xorshift32(uint32_t limit) { @@ -33,7 +36,7 @@ static uint32_t xorshift32(uint32_t limit) { return xorshift32_state % limit; } -static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode) +static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode, bool muxdiv) { RTLIL::Module *module = design->addModule("\\gold"); RTLIL::Cell *cell = module->addCell("\\UUT", cell_type); @@ -199,6 +202,13 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->setPort("\\Y", wire); } + if (muxdiv && (cell_type == "$div" || cell_type == "$mod")) { + auto b_not_zero = module->ReduceBool(NEW_ID, cell->getPort("\\B")); + auto div_out = module->addWire(NEW_ID, GetSize(cell->getPort("\\Y"))); + module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(div_out)), div_out, b_not_zero, cell->getPort("\\Y")); + cell->setPort("\\Y", div_out); + } + if (cell_type == "$alu") { wire = module->addWire("\\CI"); @@ -210,12 +220,12 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->setPort("\\BI", wire); wire = module->addWire("\\X"); - wire->width = SIZE(cell->getPort("\\Y")); + wire->width = GetSize(cell->getPort("\\Y")); wire->port_output = true; cell->setPort("\\X", wire); wire = module->addWire("\\CO"); - wire->width = SIZE(cell->getPort("\\Y")); + wire->width = GetSize(cell->getPort("\\Y")); wire->port_output = true; cell->setPort("\\CO", wire); } @@ -227,25 +237,25 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, { RTLIL::SigSpec sig = conn.second; - if (SIZE(sig) == 0 || sig[0].wire == nullptr || sig[0].wire->port_output) + if (GetSize(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); + n = xorshift32(GetSize(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++) + n = xorshift32(GetSize(sig) + 1); + for (int i = n; i < GetSize(sig); i++) sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; break; case 2: - n = xorshift32(SIZE(sig)); - m = xorshift32(SIZE(sig)); + n = xorshift32(GetSize(sig)); + m = xorshift32(GetSize(sig)); for (int i = std::min(n, m); i < std::max(n, m); i++) sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; break; @@ -268,10 +278,10 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: RTLIL::Module *gate_mod = design->module("\\gate"); ConstEval gold_ce(gold_mod), gate_ce(gate_mod); - ezDefaultSAT ez1, ez2; + ezSatPtr ez1, ez2; SigMap sigmap(gold_mod); - SatGen satgen1(&ez1, &sigmap); - SatGen satgen2(&ez2, &sigmap); + SatGen satgen1(ez1.get(), &sigmap); + SatGen satgen2(ez2.get(), &sigmap); satgen2.model_undef = true; if (!nosat) @@ -287,19 +297,19 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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)); + vlog_file << stringf(" reg [%d:0] %s;\n", GetSize(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(" wire [%d:0] %s_expr, %s_noexpr;\n", GetSize(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++) + for (int i = 0; i < GetSize(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++) + for (int i = 0; i < GetSize(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"); @@ -327,18 +337,18 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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)); + log_assert(GetSize(gold_wire) == GetSize(gate_wire)); if (!gold_wire->port_input) continue; RTLIL::Const in_value; - for (int i = 0; i < SIZE(gold_wire); i++) + for (int i = 0; i < GetSize(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++) + for (int i = 0; i < GetSize(gold_wire); i++) if (xorshift32(inv_chance) == 0) in_value.bits[i] = RTLIL::Sx; } @@ -352,7 +362,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: gold_ce.set(gold_wire, in_value); gate_ce.set(gate_wire, in_value); - if (vlog_file.is_open() && SIZE(in_value) > 0) { + if (vlog_file.is_open() && GetSize(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 += " "; @@ -371,7 +381,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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)); + log_assert(GetSize(gold_wire) == GetSize(gate_wire)); if (!gold_wire->port_output) continue; @@ -386,7 +396,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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++) { + for (int i = 0; i < GetSize(gold_wire); i++) { if (gold_outval[i] == RTLIL::Sx) continue; if (gold_outval[i] == gate_outval[i]) @@ -406,9 +416,9 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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()); + vlog_pattern_info.c_str(), log_id(gold_wire), GetSize(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), GetSize(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), GetSize(gold_outval), gold_outval.as_string().c_str()); } } @@ -423,17 +433,17 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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))) + 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--) + for (int i = GetSize(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++) { + for (int i = 0; i < GetSize(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) @@ -458,18 +468,18 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: 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))) + 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'); + for (int i = GetSize(out_sig)-1; i >= 0; i--) + log("%c", sat2_model_value.at(GetSize(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)) { + for (int i = 0; i < GetSize(out_sig); i++) { + if (sat2_model_value.at(GetSize(out_sig) + i)) { if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1) continue; } else { @@ -505,7 +515,7 @@ struct TestCellPass : public Pass { 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("cell types. Use for example 'all /$add' for all cell types except $add.\n"); log("\n"); log(" -n {integer}\n"); log(" create this number of cell instances and test them (default = 100).\n"); @@ -516,12 +526,20 @@ struct TestCellPass : public Pass { log(" -f {ilang_file}\n"); log(" don't generate circuits. instead load the specified ilang file.\n"); log("\n"); + log(" -w {filename_prefix}\n"); + log(" don't test anything. just generate the circuits and write them\n"); + log(" to ilang files with the specified prefix\n"); + log("\n"); log(" -map {filename}\n"); log(" pass this option to techmap.\n"); log("\n"); - log(" -simplib\n"); + log(" -simlib\n"); log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n"); log("\n"); + log(" -muxdiv\n"); + log(" when creating test benches with dividers, create an additional mux\n"); + log(" to mask out the division-by-zero case\n"); + log("\n"); log(" -script {script_file}\n"); log(" instead of calling \"techmap\", call \"script {script_file}\".\n"); log("\n"); @@ -542,34 +560,39 @@ struct TestCellPass : public Pass { { int num_iter = 100; std::string techmap_cmd = "techmap -assert"; - std::string ilang_file; + std::string ilang_file, write_prefix; xorshift32_state = 0; std::ofstream vlog_file; + bool muxdiv = false; bool verbose = false; bool constmode = false; bool nosat = false; int argidx; - for (argidx = 1; argidx < SIZE(args); argidx++) + for (argidx = 1; argidx < GetSize(args); argidx++) { - if (args[argidx] == "-n" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-n" && argidx+1 < GetSize(args)) { num_iter = atoi(args[++argidx].c_str()); continue; } - if (args[argidx] == "-s" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-s" && argidx+1 < GetSize(args)) { xorshift32_state = atoi(args[++argidx].c_str()); continue; } - if (args[argidx] == "-map" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-map" && argidx+1 < GetSize(args)) { techmap_cmd += " -map " + args[++argidx]; continue; } - if (args[argidx] == "-f" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-f" && argidx+1 < GetSize(args)) { ilang_file = args[++argidx]; num_iter = 1; continue; } - if (args[argidx] == "-script" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-w" && argidx+1 < GetSize(args)) { + write_prefix = args[++argidx]; + continue; + } + if (args[argidx] == "-script" && argidx+1 < GetSize(args)) { techmap_cmd = "script " + args[++argidx]; continue; } @@ -577,6 +600,10 @@ struct TestCellPass : public Pass { techmap_cmd = "techmap -map +/simlib.v -max_iter 2 -autoproc"; continue; } + if (args[argidx] == "-muxdiv") { + muxdiv = true; + continue; + } if (args[argidx] == "-const") { constmode = true; continue; @@ -589,7 +616,7 @@ struct TestCellPass : public Pass { verbose = true; continue; } - if (args[argidx] == "-vlog" && argidx+1 < SIZE(args)) { + if (args[argidx] == "-vlog" && argidx+1 < GetSize(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()); @@ -660,7 +687,7 @@ struct TestCellPass : public Pass { cell_types["$macc"] = "*"; cell_types["$fa"] = "*"; - for (; argidx < SIZE(args); argidx++) + for (; argidx < GetSize(args); argidx++) { if (args[argidx].rfind("-", 0) == 0) log_cmd_error("Unexpected option: %s\n", args[argidx].c_str()); @@ -672,6 +699,15 @@ struct TestCellPass : public Pass { continue; } + if (args[argidx].substr(0, 1) == "/") { + std::vector<std::string> new_selected_cell_types; + for (auto it : selected_cell_types) + if (it != args[argidx].substr(1)) + new_selected_cell_types.push_back(it); + new_selected_cell_types.swap(selected_cell_types); + continue; + } + if (cell_types.count(args[argidx]) == 0) { std::string cell_type_list; int charcount = 100; @@ -681,7 +717,7 @@ struct TestCellPass : public Pass { charcount = 0; } else cell_type_list += " " + it.first; - charcount += SIZE(it.first); + charcount += GetSize(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()); @@ -709,24 +745,28 @@ struct TestCellPass : public Pass { 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); + create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv); + if (!write_prefix.empty()) { + Pass::call(design, stringf("write_ilang %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i)); + } else { + 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); } - run_eval_test(design, verbose, nosat, uut_name, vlog_file); delete design; } @@ -743,3 +783,4 @@ struct TestCellPass : public Pass { } } TestCellPass; +PRIVATE_NAMESPACE_END |