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 <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 &lt)
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);
log_assert(cell1 != cell2);
if (cell1->type != cell2->type) return false;
if (hash1 != hash2) {
lt = hash1 < hash2;
return true;
}
#endif
if (cell1->parameters != cell2->parameters)
return false;
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;
}
if (cell1->connections_.size() != cell2->connections_.size())
return false;
for (const auto &it : cell1->connections_)
if (!cell2->connections_.count(it.first))
return false;
dict<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections();
dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections();
decltype(Cell::connections_) conn1, conn2;
conn1.reserve(cell1->connections_.size());
conn2.reserve(cell1->connections_.size());
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);
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 ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
continue;
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;
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;
}
}
}

View File

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

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