diff options
-rw-r--r-- | passes/opt/opt_merge.cc | 234 | ||||
-rw-r--r-- | tests/opt/opt_merge_init.ys | 28 | ||||
-rw-r--r-- | tests/opt/opt_merge_keep.ys | 64 |
3 files changed, 194 insertions, 132 deletions
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 8823a9061..270e49a96 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -26,7 +26,6 @@ #include <stdio.h> #include <set> -#define USE_CELL_HASH_CACHE USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -41,9 +40,7 @@ struct OptMergeWorker CellTypes ct; int total_count; -#ifdef USE_CELL_HASH_CACHE - dict<const RTLIL::Cell*, std::string> cell_hash_cache; -#endif + SHA1 checksum; static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) { @@ -68,7 +65,6 @@ struct OptMergeWorker } } -#ifdef USE_CELL_HASH_CACHE std::string int_to_hash_string(unsigned int v) { if (v == 0) @@ -83,14 +79,9 @@ struct OptMergeWorker std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell) { - if (cell_hash_cache.count(cell) > 0) - return cell_hash_cache[cell]; - + vector<string> hash_conn_strings; std::string hash_string = cell->type.str() + "\n"; - for (auto &it : cell->parameters) - hash_string += "P " + it.first.str() + "=" + it.second.as_string() + "\n"; - const dict<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections(); dict<RTLIL::IdString, RTLIL::SigSpec> alt_conn; @@ -124,13 +115,22 @@ struct OptMergeWorker conn = &alt_conn; } - vector<string> hash_conn_strings; - for (auto &it : *conn) { - if (cell->output(it.first)) - continue; - RTLIL::SigSpec sig = it.second; - assign_map.apply(sig); + RTLIL::SigSpec sig; + if (cell->output(it.first)) { + if (it.first == ID(Q) && (cell->type.begins_with("$dff") || cell->type.begins_with("$dlatch") || + cell->type.begins_with("$_DFF") || cell->type.begins_with("$_DLATCH") || cell->type.begins_with("$_SR_") || + cell->type.in("$adff", "$sr", "$ff", "$_FF_"))) { + // For the 'Q' output of state elements, + // use its (* init *) attribute value + for (const auto &b : dff_init_map(it.second)) + sig.append(b.wire ? State::Sx : b); + } + else + continue; + } + else + sig = assign_map(it.second); string s = "C " + it.first.str() + "="; for (auto &chunk : sig.chunks()) { if (chunk.wire) @@ -143,50 +143,59 @@ struct OptMergeWorker hash_conn_strings.push_back(s + "\n"); } + for (auto &it : cell->parameters) + hash_conn_strings.push_back("P " + it.first.str() + "=" + it.second.as_string() + "\n"); + std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); for (auto it : hash_conn_strings) hash_string += it; - cell_hash_cache[cell] = sha1(hash_string); - return cell_hash_cache[cell]; + checksum.update(hash_string); + return checksum.final(); } -#endif - bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool <) + bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) { -#ifdef USE_CELL_HASH_CACHE - std::string hash1 = hash_cell_parameters_and_connections(cell1); - std::string hash2 = hash_cell_parameters_and_connections(cell2); - - if (hash1 != hash2) { - lt = hash1 < hash2; - return true; - } -#endif - - if (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; - } - - dict<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections(); - dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); - - for (auto &it : conn1) { - if (cell1->output(it.first)) - it.second = RTLIL::SigSpec(); - else - assign_map.apply(it.second); - } - - for (auto &it : conn2) { - if (cell2->output(it.first)) - it.second = RTLIL::SigSpec(); - else - assign_map.apply(it.second); + log_assert(cell1 != cell2); + if (cell1->type != cell2->type) return false; + + if (cell1->parameters != cell2->parameters) + return false; + + if (cell1->connections_.size() != cell2->connections_.size()) + return false; + for (const auto &it : cell1->connections_) + if (!cell2->connections_.count(it.first)) + return false; + + decltype(Cell::connections_) conn1, conn2; + conn1.reserve(cell1->connections_.size()); + conn2.reserve(cell1->connections_.size()); + + for (const auto &it : cell1->connections_) { + if (cell1->output(it.first)) { + if (it.first == ID(Q) && (cell1->type.begins_with("$dff") || cell1->type.begins_with("$dlatch") || + cell1->type.begins_with("$_DFF") || cell1->type.begins_with("$_DLATCH") || cell1->type.begins_with("$_SR_") || + cell1->type.in("$adff", "$sr", "$ff", "$_FF_"))) { + // For the 'Q' output of state elements, + // use the (* init *) attribute value + auto &sig1 = conn1[it.first]; + for (const auto &b : dff_init_map(it.second)) + sig1.append(b.wire ? State::Sx : b); + auto &sig2 = conn2[it.first]; + for (const auto &b : dff_init_map(cell2->getPort(it.first))) + sig2.append(b.wire ? State::Sx : b); + } + else { + conn1[it.first] = RTLIL::SigSpec(); + conn2[it.first] = RTLIL::SigSpec(); + } + } + else { + conn1[it.first] = assign_map(it.second); + conn2[it.first] = assign_map(cell2->getPort(it.first)); + } } if (cell1->type == ID($and) || cell1->type == ID($or) || cell1->type == ID($xor) || cell1->type == ID($xnor) || cell1->type == ID($add) || cell1->type == ID($mul) || @@ -215,54 +224,9 @@ struct OptMergeWorker sort_pmux_conn(conn2); } - if (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; - } - - if (conn1.count(ID(Q)) != 0 && (cell1->type.begins_with("$dff") || cell1->type.begins_with("$dlatch") || - cell1->type.begins_with("$_DFF") || cell1->type.begins_with("$_DLATCH") || cell1->type.begins_with("$_SR_") || - cell1->type.in("$adff", "$sr", "$ff", "$_FF_"))) { - std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort(ID(Q))).to_sigbit_vector(); - std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort(ID(Q))).to_sigbit_vector(); - for (size_t i = 0; i < q1.size(); i++) - if ((q1.at(i).wire == NULL || q2.at(i).wire == NULL) && q1.at(i) != q2.at(i)) { - lt = q1.at(i) < q2.at(i); - return true; - } - } - - return false; + return conn1 == conn2; } - bool compare_cells(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) - { - if (cell1->type != cell2->type) - return cell1->type < cell2->type; - - if ((!mode_share_all && !ct.cell_known(cell1->type)) || !cell1->known()) - return cell1 < cell2; - - if (cell1->has_keep_attr() || cell2->has_keep_attr()) - return cell1 < cell2; - - bool lt; - if (compare_cell_parameters_and_connections(cell1, cell2, lt)) - return lt; - - return false; - } - - struct CompareCells { - OptMergeWorker *that; - CompareCells(OptMergeWorker *that) : that(that) {} - bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { - return that->compare_cells(cell1, cell2); - } - }; - OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) : design(design), module(module), assign_map(module), mode_share_all(mode_share_all) { @@ -299,9 +263,6 @@ struct OptMergeWorker bool did_something = true; while (did_something) { -#ifdef USE_CELL_HASH_CACHE - cell_hash_cache.clear(); -#endif std::vector<RTLIL::Cell*> cells; cells.reserve(module->cells_.size()); for (auto &it : module->cells_) { @@ -312,42 +273,51 @@ struct OptMergeWorker } did_something = false; - std::map<RTLIL::Cell*, RTLIL::Cell*, CompareCells> sharemap(CompareCells(this)); + std::unordered_map<std::string, RTLIL::Cell*> sharemap; for (auto cell : cells) { - if (sharemap.count(cell) > 0) { - did_something = true; - log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); - for (auto &it : cell->connections()) { - if (cell->output(it.first)) { - RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first); - log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), - log_signal(it.second), log_signal(other_sig)); - module->connect(RTLIL::SigSig(it.second, other_sig)); - assign_map.add(it.second, other_sig); - - if (it.first == ID(Q) && (cell->type.begins_with("$dff") || cell->type.begins_with("$dlatch") || - cell->type.begins_with("$_DFF") || cell->type.begins_with("$_DLATCH") || cell->type.begins_with("$_SR_") || - cell->type.in("$adff", "$sr", "$ff", "$_FF_"))) { - for (auto c : it.second.chunks()) { - auto jt = c.wire->attributes.find(ID(init)); - if (jt == c.wire->attributes.end()) - continue; - for (int i = c.offset; i < c.offset + c.width; i++) - jt->second[i] = State::Sx; + if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) + continue; + + auto hash = hash_cell_parameters_and_connections(cell); + auto r = sharemap.insert(std::make_pair(hash, cell)); + if (!r.second) { + if (compare_cell_parameters_and_connections(cell, r.first->second)) { + if (cell->has_keep_attr()) { + if (r.first->second->has_keep_attr()) + continue; + std::swap(r.first->second, cell); + } + + + did_something = true; + log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), r.first->second->name.c_str()); + for (auto &it : cell->connections()) { + if (cell->output(it.first)) { + RTLIL::SigSpec other_sig = r.first->second->getPort(it.first); + log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), + log_signal(it.second), log_signal(other_sig)); + module->connect(RTLIL::SigSig(it.second, other_sig)); + assign_map.add(it.second, other_sig); + + if (it.first == ID(Q) && (cell->type.begins_with("$dff") || cell->type.begins_with("$dlatch") || + cell->type.begins_with("$_DFF") || cell->type.begins_with("$_DLATCH") || cell->type.begins_with("$_SR_") || + cell->type.in("$adff", "$sr", "$ff", "$_FF_"))) { + for (auto c : it.second.chunks()) { + auto jt = c.wire->attributes.find(ID(init)); + if (jt == c.wire->attributes.end()) + continue; + for (int i = c.offset; i < c.offset + c.width; i++) + jt->second[i] = State::Sx; + } + dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second))); } - dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second))); } } + log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + module->remove(cell); + total_count++; } - log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); -#ifdef USE_CELL_HASH_CACHE - cell_hash_cache.erase(cell); -#endif - module->remove(cell); - total_count++; - } else { - sharemap[cell] = cell; } } } diff --git a/tests/opt/opt_merge_init.ys b/tests/opt/opt_merge_init.ys index a29c29df6..0176f09c7 100644 --- a/tests/opt/opt_merge_init.ys +++ b/tests/opt/opt_merge_init.ys @@ -20,6 +20,7 @@ endmodule EOT opt_merge +select -assert-count 1 t:$dff select -assert-count 1 a:init=1'0 @@ -46,4 +47,31 @@ endmodule EOT opt_merge +select -assert-count 1 t:$dff select -assert-count 1 a:init=2'bx1 + + +design -reset +read_verilog -icells <<EOT +module top(input clk, i, (* init = 1'b0 *) output o, /* NB: no init here! */ output p); + \$dff #( + .CLK_POLARITY(1'h1), + .WIDTH(32'd1) + ) ffo ( + .CLK(clk), + .D(i), + .Q(o) + ); + \$dff #( + .CLK_POLARITY(1'h1), + .WIDTH(32'd1) + ) ffp ( + .CLK(clk), + .D(i), + .Q(p) + ); +endmodule +EOT + +opt_merge +select -assert-count 2 t:$dff diff --git a/tests/opt/opt_merge_keep.ys b/tests/opt/opt_merge_keep.ys new file mode 100644 index 000000000..2a9202901 --- /dev/null +++ b/tests/opt/opt_merge_keep.ys @@ -0,0 +1,64 @@ +read_verilog -icells <<EOT +module top(input clk, i, output o, p); + (* keep *) + \$_DFF_P_ ffo ( + .C(clk), + .D(i), + .Q(o) + ); + \$_DFF_P_ ffp ( + .C(clk), + .D(i), + .Q(p) + ); +endmodule +EOT + +opt_merge +select -assert-count 1 t:$_DFF_P_ +select -assert-count 1 a:keep + + +design -reset +read_verilog -icells <<EOT +module top(input clk, i, output o, p); + \$_DFF_P_ ffo ( + .C(clk), + .D(i), + .Q(o) + ); + (* keep *) + \$_DFF_P_ ffp ( + .C(clk), + .D(i), + .Q(p) + ); +endmodule +EOT + +opt_merge +select -assert-count 1 t:$_DFF_P_ +select -assert-count 1 a:keep + + +design -reset +read_verilog -icells <<EOT +module top(input clk, i, output o, p); + (* keep *) + \$_DFF_P_ ffo ( + .C(clk), + .D(i), + .Q(o) + ); + (* keep *) + \$_DFF_P_ ffp ( + .C(clk), + .D(i), + .Q(p) + ); +endmodule +EOT + +opt_merge +select -assert-count 2 t:$_DFF_P_ +select -assert-count 2 a:keep |