mirror of https://github.com/YosysHQ/yosys.git
Major refactoring of equiv_struct
This commit is contained in:
parent
207736b4ee
commit
d014ba2d0e
|
@ -162,6 +162,11 @@ struct hash_obj_ops {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline unsigned int mkhash(const T &v) {
|
||||||
|
return hash_ops<T>().hash(v);
|
||||||
|
}
|
||||||
|
|
||||||
inline int hashtable_size(int min_size)
|
inline int hashtable_size(int min_size)
|
||||||
{
|
{
|
||||||
static std::vector<int> zero_and_some_primes = {
|
static std::vector<int> zero_and_some_primes = {
|
||||||
|
|
|
@ -28,98 +28,100 @@ struct EquivStructWorker
|
||||||
Module *module;
|
Module *module;
|
||||||
SigMap sigmap;
|
SigMap sigmap;
|
||||||
SigMap equiv_bits;
|
SigMap equiv_bits;
|
||||||
|
bool mode_fwd;
|
||||||
bool mode_icells;
|
bool mode_icells;
|
||||||
int merge_count;
|
int merge_count;
|
||||||
|
|
||||||
dict<IdString, pool<IdString>> cells_by_type;
|
struct merge_key_t
|
||||||
|
|
||||||
void handle_cell_pair(Cell *cell_a, Cell *cell_b)
|
|
||||||
{
|
{
|
||||||
if (cell_a->parameters != cell_b->parameters)
|
IdString type;
|
||||||
return;
|
vector<pair<IdString, Const>> parameters;
|
||||||
|
vector<pair<IdString, int>> port_sizes;
|
||||||
|
vector<tuple<IdString, int, SigBit>> connections;
|
||||||
|
|
||||||
bool merge_this_cells = false;
|
bool operator==(const merge_key_t &other) const {
|
||||||
bool found_diff_inputs = false;
|
return type == other.type && connections == other.connections &&
|
||||||
vector<SigSpec> inputs_a, inputs_b;
|
parameters == other.parameters && port_sizes == other.port_sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int hash() const {
|
||||||
|
unsigned int h = mkhash_init;
|
||||||
|
h = mkhash(h, mkhash(type));
|
||||||
|
h = mkhash(h, mkhash(parameters));
|
||||||
|
h = mkhash(h, mkhash(connections));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dict<merge_key_t, pool<IdString>> merge_cache;
|
||||||
|
pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;
|
||||||
|
|
||||||
|
void merge_cell_pair(Cell *cell_a, Cell *cell_b)
|
||||||
|
{
|
||||||
|
SigMap merged_map;
|
||||||
|
merge_count++;
|
||||||
|
|
||||||
|
SigSpec inputs_a, inputs_b;
|
||||||
|
vector<string> input_names;
|
||||||
|
|
||||||
for (auto &port_a : cell_a->connections())
|
for (auto &port_a : cell_a->connections())
|
||||||
{
|
{
|
||||||
SigSpec bits_a = equiv_bits(port_a.second);
|
SigSpec bits_a = sigmap(port_a.second);
|
||||||
SigSpec bits_b = equiv_bits(cell_b->getPort(port_a.first));
|
SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));
|
||||||
|
|
||||||
if (GetSize(bits_a) != GetSize(bits_b))
|
log_assert(GetSize(bits_a) == GetSize(bits_b));
|
||||||
return;
|
|
||||||
|
|
||||||
if (cell_a->output(port_a.first)) {
|
if (!cell_a->output(port_a.first))
|
||||||
for (int i = 0; i < GetSize(bits_a); i++)
|
|
||||||
if (bits_a[i] == bits_b[i])
|
|
||||||
merge_this_cells = true;
|
|
||||||
} else {
|
|
||||||
SigSpec diff_bits_a, diff_bits_b;
|
|
||||||
for (int i = 0; i < GetSize(bits_a); i++)
|
for (int i = 0; i < GetSize(bits_a); i++)
|
||||||
if (bits_a[i] != bits_b[i]) {
|
if (bits_a[i] != bits_b[i]) {
|
||||||
diff_bits_a.append(bits_a[i]);
|
inputs_a.append(bits_a[i]);
|
||||||
diff_bits_b.append(bits_b[i]);
|
inputs_b.append(bits_b[i]);
|
||||||
|
input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
|
||||||
|
stringf("%s[%d]", log_id(port_a.first), i));
|
||||||
}
|
}
|
||||||
if (!diff_bits_a.empty()) {
|
|
||||||
inputs_a.push_back(diff_bits_a);
|
|
||||||
inputs_b.push_back(diff_bits_b);
|
|
||||||
found_diff_inputs = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found_diff_inputs)
|
for (int i = 0; i < GetSize(inputs_a); i++) {
|
||||||
merge_this_cells = true;
|
SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
|
||||||
|
SigBit bit_y = module->addWire(NEW_ID);
|
||||||
if (merge_this_cells)
|
log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n",
|
||||||
{
|
input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
|
||||||
SigMap merged_map;
|
module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
|
||||||
|
merged_map.add(bit_a, bit_y);
|
||||||
log(" Merging cells %s and %s.\n", log_id(cell_a), log_id(cell_b));
|
merged_map.add(bit_b, bit_y);
|
||||||
merge_count++;
|
|
||||||
|
|
||||||
for (int i = 0; i < GetSize(inputs_a); i++) {
|
|
||||||
SigSpec &sig_a = inputs_a[i], &sig_b = inputs_b[i];
|
|
||||||
SigSpec sig_y = module->addWire(NEW_ID, GetSize(sig_a));
|
|
||||||
log(" A: %s, B: %s, Y: %s\n", log_signal(sig_a), log_signal(sig_b), log_signal(sig_y));
|
|
||||||
module->addEquiv(NEW_ID, sig_a, sig_b, sig_y);
|
|
||||||
merged_map.add(sig_a, sig_y);
|
|
||||||
merged_map.add(sig_b, sig_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<IdString> outport_names, inport_names;
|
|
||||||
|
|
||||||
for (auto &port_a : cell_a->connections())
|
|
||||||
if (cell_a->output(port_a.first))
|
|
||||||
outport_names.push_back(port_a.first);
|
|
||||||
else
|
|
||||||
inport_names.push_back(port_a.first);
|
|
||||||
|
|
||||||
for (auto &pn : inport_names)
|
|
||||||
cell_a->setPort(pn, merged_map(equiv_bits(cell_a->getPort(pn))));
|
|
||||||
|
|
||||||
for (auto &pn : outport_names) {
|
|
||||||
SigSpec sig_a = cell_a->getPort(pn);
|
|
||||||
SigSpec sig_b = cell_b->getPort(pn);
|
|
||||||
module->connect(sig_b, sig_a);
|
|
||||||
sigmap.add(sig_b, sig_a);
|
|
||||||
equiv_bits.add(sig_b, sig_a);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
|
|
||||||
merged_attr.insert(log_id(cell_b));
|
|
||||||
cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
|
|
||||||
module->remove(cell_b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<IdString> outport_names, inport_names;
|
||||||
|
|
||||||
|
for (auto &port_a : cell_a->connections())
|
||||||
|
if (cell_a->output(port_a.first))
|
||||||
|
outport_names.push_back(port_a.first);
|
||||||
|
else
|
||||||
|
inport_names.push_back(port_a.first);
|
||||||
|
|
||||||
|
for (auto &pn : inport_names)
|
||||||
|
cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));
|
||||||
|
|
||||||
|
for (auto &pn : outport_names) {
|
||||||
|
SigSpec sig_a = cell_a->getPort(pn);
|
||||||
|
SigSpec sig_b = cell_b->getPort(pn);
|
||||||
|
module->connect(sig_b, sig_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
|
||||||
|
merged_attr.insert(log_id(cell_b));
|
||||||
|
cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
|
||||||
|
module->remove(cell_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
EquivStructWorker(Module *module, bool mode_icells) :
|
EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells) :
|
||||||
module(module), sigmap(module), equiv_bits(module), mode_icells(mode_icells), merge_count(0)
|
module(module), sigmap(module), equiv_bits(module),
|
||||||
|
mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0)
|
||||||
{
|
{
|
||||||
log(" Starting new iteration.\n");
|
log(" Starting new iteration.\n");
|
||||||
|
|
||||||
pool<SigBit> equiv_inputs;
|
pool<SigBit> equiv_inputs;
|
||||||
|
pool<IdString> cells;
|
||||||
|
|
||||||
for (auto cell : module->selected_cells())
|
for (auto cell : module->selected_cells())
|
||||||
if (cell->type == "$equiv") {
|
if (cell->type == "$equiv") {
|
||||||
|
@ -128,45 +130,104 @@ struct EquivStructWorker
|
||||||
equiv_bits.add(sig_b, sig_a);
|
equiv_bits.add(sig_b, sig_a);
|
||||||
equiv_inputs.insert(sig_a);
|
equiv_inputs.insert(sig_a);
|
||||||
equiv_inputs.insert(sig_b);
|
equiv_inputs.insert(sig_b);
|
||||||
cells_by_type[cell->type].insert(cell->name);
|
cells.insert(cell->name);
|
||||||
} else
|
} else {
|
||||||
if (module->design->selected(module, cell)) {
|
|
||||||
if (mode_icells || module->design->module(cell->type))
|
if (mode_icells || module->design->module(cell->type))
|
||||||
cells_by_type[cell->type].insert(cell->name);
|
cells.insert(cell->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto cell_name : cells_by_type["$equiv"]) {
|
for (auto cell : module->selected_cells())
|
||||||
Cell *cell = module->cell(cell_name);
|
if (cell->type == "$equiv") {
|
||||||
SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
|
SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
|
||||||
SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
|
SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
|
||||||
SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
|
SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
|
||||||
if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
|
if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
|
||||||
log(" Purging redundant $equiv cell %s.\n", log_id(cell));
|
log(" Purging redundant $equiv cell %s.\n", log_id(cell));
|
||||||
module->remove(cell);
|
module->remove(cell);
|
||||||
merge_count++;
|
merge_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (merge_count > 0)
|
if (merge_count > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto &it : cells_by_type)
|
for (auto cell_name : cells)
|
||||||
{
|
{
|
||||||
if (it.second.size() <= 1)
|
merge_key_t key;
|
||||||
continue;
|
vector<tuple<IdString, int, SigBit>> fwd_connections;
|
||||||
|
|
||||||
log(" Merging %s cells..\n", log_id(it.first));
|
Cell *cell = module->cell(cell_name);
|
||||||
|
key.type = cell->type;
|
||||||
|
|
||||||
// FIXME: O(n^2)
|
for (auto &it : cell->parameters)
|
||||||
for (auto cell_name_a : it.second)
|
key.parameters.push_back(it);
|
||||||
for (auto cell_name_b : it.second)
|
std::sort(key.parameters.begin(), key.parameters.end());
|
||||||
if (cell_name_a < cell_name_b) {
|
|
||||||
Cell *cell_a = module->cell(cell_name_a);
|
for (auto &it : cell->connections())
|
||||||
Cell *cell_b = module->cell(cell_name_b);
|
key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
|
||||||
if (cell_a && cell_b)
|
std::sort(key.port_sizes.begin(), key.port_sizes.end());
|
||||||
handle_cell_pair(cell_a, cell_b);
|
|
||||||
}
|
for (auto &conn : cell->connections())
|
||||||
|
{
|
||||||
|
SigSpec sig = equiv_bits(conn.second);
|
||||||
|
|
||||||
|
if (cell->input(conn.first))
|
||||||
|
for (int i = 0; i < GetSize(sig); i++)
|
||||||
|
fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
|
||||||
|
|
||||||
|
if (cell->output(conn.first))
|
||||||
|
for (int i = 0; i < GetSize(sig); i++) {
|
||||||
|
key.connections.clear();
|
||||||
|
key.connections.push_back(make_tuple(conn.first, i, sig[i]));
|
||||||
|
|
||||||
|
if (merge_cache.count(key))
|
||||||
|
bwd_merge_cache.insert(key);
|
||||||
|
merge_cache[key].insert(cell_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(fwd_connections.begin(), fwd_connections.end());
|
||||||
|
key.connections.swap(fwd_connections);
|
||||||
|
|
||||||
|
if (merge_cache.count(key))
|
||||||
|
fwd_merge_cache.insert(key);
|
||||||
|
merge_cache[key].insert(cell_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int phase = 0; phase < 2; phase++)
|
||||||
|
{
|
||||||
|
auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;
|
||||||
|
|
||||||
|
for (auto &key : queue)
|
||||||
|
{
|
||||||
|
Cell *gold_cell = nullptr;
|
||||||
|
pool<Cell*> cells;
|
||||||
|
|
||||||
|
for (auto cell_name : merge_cache[key]) {
|
||||||
|
Cell *c = module->cell(cell_name);
|
||||||
|
if (c != nullptr) {
|
||||||
|
string n = cell_name.str();
|
||||||
|
if (gold_cell == nullptr || (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold"))
|
||||||
|
gold_cell = c;
|
||||||
|
cells.insert(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSize(cells) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (auto gate_cell : cells)
|
||||||
|
if (gate_cell != gold_cell) {
|
||||||
|
log(" %s merging cells %s and %s.\n", phase ? "Bwd" : "Fwd", log_id(gold_cell), log_id(gate_cell));
|
||||||
|
merge_cell_pair(gold_cell, gate_cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (merge_count > 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(" Nothing to merge.\n");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -184,6 +245,12 @@ struct EquivStructPass : public Pass {
|
||||||
log("for example when analyzing circuits with cells with commutative inputs. This\n");
|
log("for example when analyzing circuits with cells with commutative inputs. This\n");
|
||||||
log("command will also de-duplicate gates.\n");
|
log("command will also de-duplicate gates.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -fwd\n");
|
||||||
|
log(" by default this command performans forward sweeps until nothing can\n");
|
||||||
|
log(" be merged by forwards sweeps, the backward sweeps until forward\n");
|
||||||
|
log(" sweeps are effective again. with this option set only forward sweeps\n");
|
||||||
|
log(" are performed.\n");
|
||||||
|
log("\n");
|
||||||
log(" -icells\n");
|
log(" -icells\n");
|
||||||
log(" by default, the internal RTL and gate cell types are ignored. add\n");
|
log(" by default, the internal RTL and gate cell types are ignored. add\n");
|
||||||
log(" this option to also process those cell types with this command.\n");
|
log(" this option to also process those cell types with this command.\n");
|
||||||
|
@ -192,11 +259,16 @@ struct EquivStructPass : public Pass {
|
||||||
virtual void execute(std::vector<std::string> args, Design *design)
|
virtual void execute(std::vector<std::string> args, Design *design)
|
||||||
{
|
{
|
||||||
bool mode_icells = false;
|
bool mode_icells = false;
|
||||||
|
bool mode_fwd = false;
|
||||||
|
|
||||||
log_header("Executing EQUIV_STRUCT pass.\n");
|
log_header("Executing EQUIV_STRUCT pass.\n");
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
if (args[argidx] == "-fwd") {
|
||||||
|
mode_fwd = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-icells") {
|
if (args[argidx] == "-icells") {
|
||||||
mode_icells = true;
|
mode_icells = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -206,9 +278,9 @@ struct EquivStructPass : public Pass {
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
for (auto module : design->selected_modules()) {
|
for (auto module : design->selected_modules()) {
|
||||||
log("Running equiv_struct on module %s:", log_id(module));
|
log("Running equiv_struct on module %s:\n", log_id(module));
|
||||||
while (1) {
|
while (1) {
|
||||||
EquivStructWorker worker(module, mode_icells);
|
EquivStructWorker worker(module, mode_fwd, mode_icells);
|
||||||
if (worker.merge_count == 0)
|
if (worker.merge_count == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue