3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-07 09:55:20 +00:00

opt_merge: speedup

This commit is contained in:
Eddie Hung 2020-03-10 16:13:44 -07:00
parent a0cc795e85
commit 9f30d7f843
3 changed files with 189 additions and 127 deletions

View file

@ -26,7 +26,6 @@
#include <stdio.h> #include <stdio.h>
#include <set> #include <set>
#define USE_CELL_HASH_CACHE
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
@ -41,9 +40,7 @@ struct OptMergeWorker
CellTypes ct; CellTypes ct;
int total_count; int total_count;
#ifdef USE_CELL_HASH_CACHE SHA1 checksum;
dict<const RTLIL::Cell*, std::string> cell_hash_cache;
#endif
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) 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) std::string int_to_hash_string(unsigned int v)
{ {
if (v == 0) if (v == 0)
@ -83,14 +79,9 @@ struct OptMergeWorker
std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell) std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell)
{ {
if (cell_hash_cache.count(cell) > 0) vector<string> hash_conn_strings;
return cell_hash_cache[cell];
std::string hash_string = cell->type.str() + "\n"; 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(); const dict<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections();
dict<RTLIL::IdString, RTLIL::SigSpec> alt_conn; dict<RTLIL::IdString, RTLIL::SigSpec> alt_conn;
@ -124,13 +115,22 @@ struct OptMergeWorker
conn = &alt_conn; conn = &alt_conn;
} }
vector<string> hash_conn_strings;
for (auto &it : *conn) { for (auto &it : *conn) {
if (cell->output(it.first)) 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; continue;
RTLIL::SigSpec sig = it.second; }
assign_map.apply(sig); else
sig = assign_map(it.second);
string s = "C " + it.first.str() + "="; string s = "C " + it.first.str() + "=";
for (auto &chunk : sig.chunks()) { for (auto &chunk : sig.chunks()) {
if (chunk.wire) if (chunk.wire)
@ -143,50 +143,59 @@ struct OptMergeWorker
hash_conn_strings.push_back(s + "\n"); 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()); std::sort(hash_conn_strings.begin(), hash_conn_strings.end());
for (auto it : hash_conn_strings) for (auto it : hash_conn_strings)
hash_string += it; hash_string += it;
cell_hash_cache[cell] = sha1(hash_string); checksum.update(hash_string);
return cell_hash_cache[cell]; return checksum.final();
} }
#endif
bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool &lt) bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2)
{ {
#ifdef USE_CELL_HASH_CACHE log_assert(cell1 != cell2);
std::string hash1 = hash_cell_parameters_and_connections(cell1); if (cell1->type != cell2->type) return false;
std::string hash2 = hash_cell_parameters_and_connections(cell2);
if (hash1 != hash2) { if (cell1->parameters != cell2->parameters)
lt = hash1 < hash2; return false;
return true;
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);
} }
#endif else {
conn1[it.first] = RTLIL::SigSpec();
if (cell1->parameters != cell2->parameters) { conn2[it.first] = RTLIL::SigSpec();
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);
} }
else {
for (auto &it : conn2) { conn1[it.first] = assign_map(it.second);
if (cell2->output(it.first)) conn2[it.first] = assign_map(cell2->getPort(it.first));
it.second = RTLIL::SigSpec(); }
else
assign_map.apply(it.second);
} }
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) || 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); sort_pmux_conn(conn2);
} }
if (conn1 != conn2) { return 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;
}
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) : 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) design(design), module(module), assign_map(module), mode_share_all(mode_share_all)
{ {
@ -299,9 +263,6 @@ struct OptMergeWorker
bool did_something = true; bool did_something = true;
while (did_something) while (did_something)
{ {
#ifdef USE_CELL_HASH_CACHE
cell_hash_cache.clear();
#endif
std::vector<RTLIL::Cell*> cells; std::vector<RTLIL::Cell*> cells;
cells.reserve(module->cells_.size()); cells.reserve(module->cells_.size());
for (auto &it : module->cells_) { for (auto &it : module->cells_) {
@ -312,15 +273,28 @@ struct OptMergeWorker
} }
did_something = false; 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) for (auto cell : cells)
{ {
if (sharemap.count(cell) > 0) { 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; did_something = true;
log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); 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()) { for (auto &it : cell->connections()) {
if (cell->output(it.first)) { if (cell->output(it.first)) {
RTLIL::SigSpec other_sig = sharemap[cell]->getPort(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_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(),
log_signal(it.second), log_signal(other_sig)); log_signal(it.second), log_signal(other_sig));
module->connect(RTLIL::SigSig(it.second, other_sig)); module->connect(RTLIL::SigSig(it.second, other_sig));
@ -341,13 +315,9 @@ struct OptMergeWorker
} }
} }
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); 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); module->remove(cell);
total_count++; total_count++;
} else { }
sharemap[cell] = cell;
} }
} }
} }

View file

@ -20,6 +20,7 @@ endmodule
EOT EOT
opt_merge opt_merge
select -assert-count 1 t:$dff
select -assert-count 1 a:init=1'0 select -assert-count 1 a:init=1'0
@ -46,4 +47,31 @@ endmodule
EOT EOT
opt_merge opt_merge
select -assert-count 1 t:$dff
select -assert-count 1 a:init=2'bx1 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

View file

@ -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