Merge pull request #2328 from YosysHQ/mwk/opt_dff-cleanup

Remove passes redundant with opt_dff
This commit is contained in:
clairexen 2020-08-20 16:21:58 +02:00 committed by GitHub
commit 6a68b8ed54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 129 additions and 1661 deletions

View File

@ -4,7 +4,6 @@ OBJS += passes/opt/opt_merge.o
OBJS += passes/opt/opt_mem.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
OBJS += passes/opt/opt_rmdff.o
OBJS += passes/opt/opt_dff.o
OBJS += passes/opt/opt_share.o
OBJS += passes/opt/opt_clean.o

View File

@ -1,711 +0,0 @@
/*
* 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/log.h"
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/satgen.h"
#include "kernel/sigtools.h"
#include <stdio.h>
#include <stdlib.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
SigMap assign_map, dff_init_map;
SigSet<RTLIL::Cell*> mux_drivers;
dict<SigBit, RTLIL::Cell*> bit2driver;
dict<SigBit, pool<SigBit>> init_attributes;
bool keepdc;
bool sat;
void remove_init_attr(SigSpec sig)
{
for (auto bit : assign_map(sig))
if (init_attributes.count(bit))
for (auto wbit : init_attributes.at(bit))
wbit.wire->attributes.at(ID::init)[wbit.offset] = State::Sx;
}
bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
{
SigSpec sig_set, sig_clr;
State pol_set, pol_clr;
if (cell->hasPort(ID::S))
sig_set = cell->getPort(ID::S);
if (cell->hasPort(ID::R))
sig_clr = cell->getPort(ID::R);
if (cell->hasPort(ID::SET))
sig_set = cell->getPort(ID::SET);
if (cell->hasPort(ID::CLR))
sig_clr = cell->getPort(ID::CLR);
log_assert(GetSize(sig_set) == GetSize(sig_clr));
if (cell->type.begins_with("$_DFFSR_")) {
pol_set = cell->type[9] == 'P' ? State::S1 : State::S0;
pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0;
} else
if (cell->type.begins_with("$_DLATCHSR_")) {
pol_set = cell->type[12] == 'P' ? State::S1 : State::S0;
pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0;
} else
if (cell->type.in(ID($dffsr), ID($dlatchsr))) {
pol_set = cell->parameters[ID::SET_POLARITY].as_bool() ? State::S1 : State::S0;
pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool() ? State::S1 : State::S0;
} else
log_abort();
State npol_set = pol_set == State::S0 ? State::S1 : State::S0;
State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0;
SigSpec sig_d = cell->getPort(ID::D);
SigSpec sig_q = cell->getPort(ID::Q);
bool did_something = false;
bool proper_sr = false;
bool used_pol_set = false;
bool used_pol_clr = false;
bool hasreset = false;
Const reset_val;
SigSpec sig_reset;
for (int i = 0; i < GetSize(sig_set); i++)
{
SigBit s = sig_set[i], c = sig_clr[i];
if (s != npol_set || c != npol_clr)
hasreset = true;
if (s == pol_set || c == pol_clr)
{
log("Constantly %s Q bit %s for SR cell %s (%s) from module %s.\n",
s == pol_set ? "set" : "cleared", log_signal(sig_q[i]),
log_id(cell), log_id(cell->type), log_id(mod));
remove_init_attr(sig_q[i]);
mod->connect(sig_q[i], s == pol_set ? State::S1 : State::S0);
sig_set.remove(i);
sig_clr.remove(i);
sig_d.remove(i);
sig_q.remove(i--);
did_something = true;
continue;
}
if (sig_reset.empty() && s.wire != nullptr) sig_reset = s;
if (sig_reset.empty() && c.wire != nullptr) sig_reset = c;
if (s.wire != nullptr && s != sig_reset) proper_sr = true;
if (c.wire != nullptr && c != sig_reset) proper_sr = true;
if ((s.wire == nullptr) != (c.wire == nullptr)) {
if (s.wire != nullptr) used_pol_set = true;
if (c.wire != nullptr) used_pol_clr = true;
reset_val.bits.push_back(c.wire == nullptr ? State::S1 : State::S0);
} else
proper_sr = true;
}
if (!hasreset)
proper_sr = false;
if (GetSize(sig_set) == 0)
{
log("Removing %s (%s) from module %s.\n", log_id(cell), log_id(cell->type), log_id(mod));
mod->remove(cell);
return true;
}
if (cell->type.in(ID($dffsr), ID($dlatchsr)))
{
cell->setParam(ID::WIDTH, GetSize(sig_d));
cell->setPort(ID::SET, sig_set);
cell->setPort(ID::CLR, sig_clr);
cell->setPort(ID::D, sig_d);
cell->setPort(ID::Q, sig_q);
}
else
{
cell->setPort(ID::S, sig_set);
cell->setPort(ID::R, sig_clr);
cell->setPort(ID::D, sig_d);
cell->setPort(ID::Q, sig_q);
}
if (proper_sr)
return did_something;
if (used_pol_set && used_pol_clr && pol_set != pol_clr)
return did_something;
if (cell->type == ID($dlatchsr))
return did_something;
State unified_pol = used_pol_set ? pol_set : pol_clr;
if (cell->type == ID($dffsr))
{
if (hasreset)
{
log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod));
cell->type = ID($adff);
cell->setParam(ID::ARST_POLARITY, unified_pol);
cell->setParam(ID::ARST_VALUE, reset_val);
cell->setPort(ID::ARST, sig_reset);
cell->unsetParam(ID::SET_POLARITY);
cell->unsetParam(ID::CLR_POLARITY);
cell->unsetPort(ID::SET);
cell->unsetPort(ID::CLR);
}
else
{
log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod));
cell->type = ID($dff);
cell->unsetParam(ID::SET_POLARITY);
cell->unsetParam(ID::CLR_POLARITY);
cell->unsetPort(ID::SET);
cell->unsetPort(ID::CLR);
}
return true;
}
if (!hasreset)
{
IdString new_type;
if (cell->type.begins_with("$_DFFSR_"))
new_type = stringf("$_DFF_%c_", cell->type[8]);
else if (cell->type.begins_with("$_DLATCHSR_"))
new_type = stringf("$_DLATCH_%c_", cell->type[11]);
else
log_abort();
log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod));
cell->type = new_type;
cell->unsetPort(ID::S);
cell->unsetPort(ID::R);
return true;
}
return did_something;
}
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
{
SigSpec sig_e;
State on_state, off_state;
if (dlatch->type == ID($dlatch)) {
sig_e = assign_map(dlatch->getPort(ID::EN));
on_state = dlatch->getParam(ID::EN_POLARITY).as_bool() ? State::S1 : State::S0;
off_state = dlatch->getParam(ID::EN_POLARITY).as_bool() ? State::S0 : State::S1;
} else
if (dlatch->type == ID($_DLATCH_P_)) {
sig_e = assign_map(dlatch->getPort(ID::E));
on_state = State::S1;
off_state = State::S0;
} else
if (dlatch->type == ID($_DLATCH_N_)) {
sig_e = assign_map(dlatch->getPort(ID::E));
on_state = State::S0;
off_state = State::S1;
} else
log_abort();
if (sig_e == off_state)
{
RTLIL::Const val_init;
for (auto bit : dff_init_map(dlatch->getPort(ID::Q)))
val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
mod->connect(dlatch->getPort(ID::Q), val_init);
goto delete_dlatch;
}
if (sig_e == on_state)
{
mod->connect(dlatch->getPort(ID::Q), dlatch->getPort(ID::D));
goto delete_dlatch;
}
return false;
delete_dlatch:
log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod));
remove_init_attr(dlatch->getPort(ID::Q));
mod->remove(dlatch);
return true;
}
bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
{
RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e;
RTLIL::Const val_cp, val_rp, val_rv, val_ep;
if (dff->type == ID($_FF_)) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
}
else if (dff->type == ID($_DFF_N_) || dff->type == ID($_DFF_P_)) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
sig_c = dff->getPort(ID::C);
val_cp = RTLIL::Const(dff->type == ID($_DFF_P_), 1);
}
else if (dff->type.begins_with("$_DFF_") && dff->type.compare(9, 1, "_") == 0 &&
(dff->type[6] == 'N' || dff->type[6] == 'P') &&
(dff->type[7] == 'N' || dff->type[7] == 'P') &&
(dff->type[8] == '0' || dff->type[8] == '1')) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
sig_c = dff->getPort(ID::C);
sig_r = dff->getPort(ID::R);
val_cp = RTLIL::Const(dff->type[6] == 'P', 1);
val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
val_rv = RTLIL::Const(dff->type[8] == '1', 1);
}
else if (dff->type.begins_with("$_DFFE_") && dff->type.compare(9, 1, "_") == 0 &&
(dff->type[7] == 'N' || dff->type[7] == 'P') &&
(dff->type[8] == 'N' || dff->type[8] == 'P')) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
sig_c = dff->getPort(ID::C);
sig_e = dff->getPort(ID::E);
val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
}
else if (dff->type == ID($ff)) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
}
else if (dff->type == ID($dff)) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
sig_c = dff->getPort(ID::CLK);
val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
}
else if (dff->type == ID($dffe)) {
sig_e = dff->getPort(ID::EN);
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
sig_c = dff->getPort(ID::CLK);
val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
val_ep = RTLIL::Const(dff->parameters[ID::EN_POLARITY].as_bool(), 1);
}
else if (dff->type == ID($adff)) {
sig_d = dff->getPort(ID::D);
sig_q = dff->getPort(ID::Q);
sig_c = dff->getPort(ID::CLK);
sig_r = dff->getPort(ID::ARST);
val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
val_rp = RTLIL::Const(dff->parameters[ID::ARST_POLARITY].as_bool(), 1);
val_rv = dff->parameters[ID::ARST_VALUE];
}
else
log_abort();
assign_map.apply(sig_d);
assign_map.apply(sig_q);
assign_map.apply(sig_c);
assign_map.apply(sig_r);
bool has_init = false;
RTLIL::Const val_init;
for (auto bit : dff_init_map(sig_q).to_sigbit_vector()) {
if (bit.wire == NULL || keepdc)
has_init = true;
val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
}
if (dff->type.in(ID($ff), ID($dff)) && mux_drivers.has(sig_d)) {
std::set<RTLIL::Cell*> muxes;
mux_drivers.find(sig_d, muxes);
for (auto mux : muxes) {
RTLIL::SigSpec sig_a = assign_map(mux->getPort(ID::A));
RTLIL::SigSpec sig_b = assign_map(mux->getPort(ID::B));
if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
mod->connect(sig_q, sig_b);
goto delete_dff;
}
if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) {
mod->connect(sig_q, sig_a);
goto delete_dff;
}
}
}
// If clock is driven by a constant and (i) no reset signal
// (ii) Q has no initial value
// (iii) initial value is same as reset value
if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
if (val_rv.bits.size() == 0)
val_rv = val_init;
// Q is permanently reset value or initial value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
// If D is fully undefined and reset signal present and (i) Q has no initial value
// (ii) initial value is same as reset value
if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
// Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
// If D is fully undefined and no reset signal and Q has an initial value
if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
// Q is permanently initial value
mod->connect(sig_q, val_init);
goto delete_dff;
}
// If D is fully constant and (i) no reset signal
// (ii) reset value is same as constant D
// and (a) has no initial value
// (b) initial value same as constant D
if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
// Q is permanently D
mod->connect(sig_q, sig_d);
goto delete_dff;
}
// If D input is same as Q output and (i) no reset signal
// (ii) no initial signal
// (iii) initial value is same as reset value
if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
// Q is permanently reset value or initial value
if (sig_r.size())
mod->connect(sig_q, val_rv);
else if (has_init)
mod->connect(sig_q, val_init);
goto delete_dff;
}
// If reset signal is present, and is fully constant
if (!sig_r.empty() && sig_r.is_fully_const())
{
// If reset value is permanently active or if reset is undefined
if (sig_r == val_rp || sig_r.is_fully_undef()) {
// Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
if (dff->type == ID($adff)) {
dff->type = ID($dff);
dff->unsetPort(ID::ARST);
dff->unsetParam(ID::ARST_POLARITY);
dff->unsetParam(ID::ARST_VALUE);
return true;
}
log_assert(dff->type.begins_with("$_DFF_"));
dff->type = stringf("$_DFF_%c_", + dff->type[6]);
dff->unsetPort(ID::R);
}
// If enable signal is present, and is fully constant
if (!sig_e.empty() && sig_e.is_fully_const())
{
// If enable value is permanently inactive
if (sig_e != val_ep) {
// Q is permanently initial value
mod->connect(sig_q, val_init);
goto delete_dff;
}
log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
if (dff->type == ID($dffe)) {
dff->type = ID($dff);
dff->unsetPort(ID::EN);
dff->unsetParam(ID::EN_POLARITY);
return true;
}
log_assert(dff->type.begins_with("$_DFFE_"));
dff->type = stringf("$_DFF_%c_", + dff->type[7]);
dff->unsetPort(ID::E);
}
if (sat && has_init && (!sig_r.size() || val_init == val_rv))
{
bool removed_sigbits = false;
ezSatPtr ez;
SatGen satgen(ez.get(), &assign_map);
pool<Cell*> sat_cells;
std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
if (!sat_cells.insert(c).second)
return;
if (!satgen.importCell(c))
return;
for (auto &conn : c->connections()) {
if (!c->input(conn.first))
continue;
for (auto bit : assign_map(conn.second))
if (bit2driver.count(bit))
sat_import_cell(bit2driver.at(bit));
}
};
// For each register bit, try to prove that it cannot change from the initial value. If so, remove it
for (int position = 0; position < GetSize(sig_d); position += 1) {
RTLIL::SigBit q_sigbit = sig_q[position];
RTLIL::SigBit d_sigbit = sig_d[position];
if ((!q_sigbit.wire) || (!d_sigbit.wire))
continue;
if (!bit2driver.count(d_sigbit))
continue;
sat_import_cell(bit2driver.at(d_sigbit));
RTLIL::State sigbit_init_val = val_init[position];
if (sigbit_init_val != State::S0 && sigbit_init_val != State::S1)
continue;
int init_sat_pi = satgen.importSigSpec(sigbit_init_val).front();
int q_sat_pi = satgen.importSigBit(q_sigbit);
int d_sat_pi = satgen.importSigBit(d_sigbit);
// Try to find out whether the register bit can change under some circumstances
bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
// If the register bit cannot change, we can replace it with a constant
if (!counter_example_found)
{
log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", sigbit_init_val ? 1 : 0,
position, log_id(dff), log_id(dff->type), log_id(mod));
SigSpec tmp = dff->getPort(ID::D);
tmp[position] = sigbit_init_val;
dff->setPort(ID::D, tmp);
removed_sigbits = true;
}
}
if (removed_sigbits) {
handle_dff(mod, dff);
return true;
}
}
return false;
delete_dff:
log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
remove_init_attr(dff->getPort(ID::Q));
mod->remove(dff);
for (auto &entry : bit2driver)
if (entry.second == dff)
bit2driver.erase(entry.first);
return true;
}
struct OptRmdffPass : public Pass {
OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_rmdff [-keepdc] [-sat] [selection]\n");
log("\n");
log("This pass identifies flip-flops with constant inputs and replaces them with\n");
log("a constant driver.\n");
log("\n");
log(" -sat\n");
log(" additionally invoke SAT solver to detect and remove flip-flops (with \n");
log(" non-constant inputs) that can also be replaced with a constant driver\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
int total_count = 0, total_initdrv = 0;
log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
keepdc = false;
sat = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-keepdc") {
keepdc = true;
continue;
}
if (args[argidx] == "-sat") {
sat = true;
continue;
}
break;
}
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
pool<SigBit> driven_bits;
dict<SigBit, State> init_bits;
assign_map.set(module);
dff_init_map.set(module);
mux_drivers.clear();
bit2driver.clear();
init_attributes.clear();
for (auto wire : module->wires())
{
if (wire->attributes.count(ID::init) != 0) {
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++)
if (initval[i] == State::S0 || initval[i] == State::S1)
dff_init_map.add(SigBit(wire, i), initval[i]);
for (int i = 0; i < GetSize(wire); i++) {
SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit);
if (mapped_bit.wire) {
init_attributes[mapped_bit].insert(wire_bit);
if (i < GetSize(initval))
init_bits[mapped_bit] = initval[i];
}
}
}
if (wire->port_input) {
for (auto bit : assign_map(wire))
driven_bits.insert(bit);
}
}
std::vector<RTLIL::IdString> dff_list;
std::vector<RTLIL::IdString> dffsr_list;
std::vector<RTLIL::IdString> dlatch_list;
for (auto cell : module->cells())
{
for (auto &conn : cell->connections()) {
bool is_output = cell->output(conn.first);
if (is_output || !cell->known())
for (auto bit : assign_map(conn.second)) {
if (is_output)
bit2driver[bit] = cell;
driven_bits.insert(bit);
}
}
if (cell->type.in(ID($mux), ID($pmux))) {
if (cell->getPort(ID::A).size() == cell->getPort(ID::B).size())
mux_drivers.insert(assign_map(cell->getPort(ID::Y)), cell);
continue;
}
if (!design->selected(module, cell))
continue;
if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_), ID($dffsr),
ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_),
ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), ID($dlatchsr)))
dffsr_list.push_back(cell->name);
if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_),
ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_),
ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_),
ID($ff), ID($dff), ID($dffe), ID($adff)))
dff_list.push_back(cell->name);
if (cell->type.in(ID($dlatch), ID($_DLATCH_P_), ID($_DLATCH_N_)))
dlatch_list.push_back(cell->name);
}
for (auto &id : dffsr_list) {
if (module->cell(id) != nullptr &&
handle_dffsr(module, module->cells_[id]))
total_count++;
}
for (auto &id : dff_list) {
if (module->cell(id) != nullptr &&
handle_dff(module, module->cells_[id]))
total_count++;
}
for (auto &id : dlatch_list) {
if (module->cell(id) != nullptr &&
handle_dlatch(module, module->cells_[id]))
total_count++;
}
SigSpec const_init_sigs;
for (auto bit : init_bits)
if (!driven_bits.count(bit.first))
const_init_sigs.append(bit.first);
const_init_sigs.sort_and_unify();
for (SigSpec sig : const_init_sigs.chunks())
{
Const val;
for (auto bit : sig)
val.bits.push_back(init_bits.at(bit));
log("Promoting init spec %s = %s to constant driver in module %s.\n",
log_signal(sig), log_signal(val), log_id(module));
module->connect(sig, val);
remove_init_attr(sig);
total_initdrv++;
}
}
assign_map.clear();
mux_drivers.clear();
bit2driver.clear();
init_attributes.clear();
if (total_count || total_initdrv)
design->scratchpad_set_bool("opt.did_something", true);
if (total_initdrv)
log("Promoted %d init specs to constant drivers.\n", total_initdrv);
if (total_count)
log("Replaced %d DFF cells.\n", total_count);
}
} OptRmdffPass;
PRIVATE_NAMESPACE_END

View File

@ -36,7 +36,6 @@ $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)

View File

@ -67,8 +67,6 @@ struct PeepoptPass : public Pass {
GENERATE_PATTERN(peepopt_pm, shiftmul);
else if (genmode == "muldiv")
GENERATE_PATTERN(peepopt_pm, muldiv);
else if (genmode == "dffmux")
GENERATE_PATTERN(peepopt_pm, dffmux);
else
log_abort();
return;
@ -106,7 +104,6 @@ struct PeepoptPass : public Pass {
pm.run_shiftmul();
pm.run_muldiv();
pm.run_dffmux();
for (auto w : module->wires()) {
auto it = w->attributes.find(ID::init);

View File

@ -1,171 +0,0 @@
pattern dffmux
state <IdString> cemuxAB rstmuxBA
state <SigSpec> sigD
match dff
select dff->type == $dff
select GetSize(port(dff, \D)) > 1
endmatch
code sigD
sigD = port(dff, \D);
endcode
match rstmux
select rstmux->type == $mux
select GetSize(port(rstmux, \Y)) > 1
index <SigSpec> port(rstmux, \Y) === sigD
choice <IdString> BA {\B, \A}
select port(rstmux, BA).is_fully_const()
set rstmuxBA BA
semioptional
endmatch
code sigD
if (rstmux)
sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
endcode
match cemux
select cemux->type == $mux
select GetSize(port(cemux, \Y)) > 1
index <SigSpec> port(cemux, \Y) === sigD
choice <IdString> AB {\A, \B}
index <SigSpec> port(cemux, AB) === port(dff, \Q)
set cemuxAB AB
semioptional
endmatch
code
if (!cemux && !rstmux)
reject;
endcode
code
Const rst;
SigSpec D;
if (cemux) {
D = port(cemux, cemuxAB == \A ? \B : \A);
if (rstmux)
rst = port(rstmux, rstmuxBA).as_const();
else
rst = Const(State::Sx, GetSize(D));
}
else {
log_assert(rstmux);
D = port(rstmux, rstmuxBA == \B ? \A : \B);
rst = port(rstmux, rstmuxBA).as_const();
}
SigSpec Q = port(dff, \Q);
int width = GetSize(D);
SigSpec dffD = dff->getPort(\D);
SigSpec dffQ = dff->getPort(\Q);
Const initval;
for (auto b : Q) {
auto it = initbits.find(b);
initval.bits.push_back(it == initbits.end() ? State::Sx : it->second);
}
auto cmpx = [=](State lhs, State rhs) {
if (lhs == State::Sx || rhs == State::Sx)
return true;
return lhs == rhs;
};
int i = width-1;
while (i > 1) {
if (D[i] != D[i-1])
break;
if (!cmpx(rst[i], rst[i-1]))
break;
if (!cmpx(initval[i], initval[i-1]))
break;
if (!cmpx(rst[i], initval[i]))
break;
rminitbits.insert(Q[i]);
module->connect(Q[i], Q[i-1]);
i--;
}
if (i < width-1) {
did_something = true;
if (cemux) {
SigSpec ceA = cemux->getPort(\A);
SigSpec ceB = cemux->getPort(\B);
SigSpec ceY = cemux->getPort(\Y);
ceA.remove(i, width-1-i);
ceB.remove(i, width-1-i);
ceY.remove(i, width-1-i);
cemux->setPort(\A, ceA);
cemux->setPort(\B, ceB);
cemux->setPort(\Y, ceY);
cemux->fixup_parameters();
blacklist(cemux);
}
if (rstmux) {
SigSpec rstA = rstmux->getPort(\A);
SigSpec rstB = rstmux->getPort(\B);
SigSpec rstY = rstmux->getPort(\Y);
rstA.remove(i, width-1-i);
rstB.remove(i, width-1-i);
rstY.remove(i, width-1-i);
rstmux->setPort(\A, rstA);
rstmux->setPort(\B, rstB);
rstmux->setPort(\Y, rstY);
rstmux->fixup_parameters();
blacklist(rstmux);
}
dffD.remove(i, width-1-i);
dffQ.remove(i, width-1-i);
dff->setPort(\D, dffD);
dff->setPort(\Q, dffQ);
dff->fixup_parameters();
blacklist(dff);
log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux, "n/a"), log_id(rstmux, "n/a"), width-1-i);
width = i+1;
}
if (cemux) {
SigSpec ceA = cemux->getPort(\A);
SigSpec ceB = cemux->getPort(\B);
SigSpec ceY = cemux->getPort(\Y);
int count = 0;
for (int i = width-1; i >= 0; i--) {
if (D[i].wire)
continue;
if (cmpx(rst[i], D[i].data) && cmpx(initval[i], D[i].data)) {
count++;
rminitbits.insert(Q[i]);
module->connect(Q[i], D[i]);
ceA.remove(i);
ceB.remove(i);
ceY.remove(i);
dffD.remove(i);
dffQ.remove(i);
}
}
if (count > 0)
{
did_something = true;
cemux->setPort(\A, ceA);
cemux->setPort(\B, ceB);
cemux->setPort(\Y, ceY);
cemux->fixup_parameters();
blacklist(cemux);
dff->setPort(\D, dffD);
dff->setPort(\Q, dffQ);
dff->fixup_parameters();
blacklist(dff);
log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), log_id(rstmux, "n/a"), count);
}
}
if (did_something)
accept;
endcode

View File

@ -27,7 +27,6 @@ OBJS += passes/techmap/extract_fa.o
OBJS += passes/techmap/extract_counter.o
OBJS += passes/techmap/extract_reduce.o
OBJS += passes/techmap/alumacc.o
OBJS += passes/techmap/dff2dffe.o
OBJS += passes/techmap/dffinit.o
OBJS += passes/techmap/pmuxtree.o
OBJS += passes/techmap/muxcover.o
@ -42,7 +41,6 @@ OBJS += passes/techmap/attrmvcp.o
OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
OBJS += passes/techmap/dfflegalize.o
OBJS += passes/techmap/dff2dffs.o
OBJS += passes/techmap/dffunmap.o
OBJS += passes/techmap/flowmap.o
OBJS += passes/techmap/extractinv.o

View File

@ -1,414 +0,0 @@
/*
* 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
{
const dict<IdString, IdString> &direct_dict;
RTLIL::Module *module;
SigMap sigmap;
CellTypes ct;
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, const dict<IdString, IdString> &direct_dict) :
direct_dict(direct_dict), module(module), sigmap(module), ct(module->design)
{
for (auto wire : module->wires()) {
if (wire->port_output)
for (auto bit : sigmap(wire))
bitusers[bit]++;
}
for (auto cell : module->cells()) {
if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) {
RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
for (int i = 0; i < GetSize(sig_y); i++)
bit2mux[sig_y[i]] = cell_int_t(cell, i);
}
if (direct_dict.empty()) {
if (cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_)))
dff_cells.push_back(cell);
} else {
if (direct_dict.count(cell->type))
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(ID::A));
RTLIL::SigSpec sig_b = sigmap(mux_cell_int.first->getPort(ID::B));
RTLIL::SigSpec sig_s = sigmap(mux_cell_int.first->getPort(ID::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(ID::B);
s[i*width + index] = RTLIL::Sx;
mux_cell_int.first->setPort(ID::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(ID::B);
s[i*width + index] = RTLIL::Sx;
mux_cell_int.first->setPort(ID::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(ID::A);
s[index] = RTLIL::Sx;
mux_cell_int.first->setPort(ID::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 State::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(ID::D));
RTLIL::SigSpec sig_q = sigmap(dff_cell->getPort(ID::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_dict.empty()) {
log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q));
dff_cell->setPort(ID::E, make_patterns_logic(it.first, true));
dff_cell->type = direct_dict.at(dff_cell->type);
} else
if (dff_cell->type == ID($dff)) {
RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort(ID::CLK), make_patterns_logic(it.first, false),
new_sig_d, new_sig_q, dff_cell->getParam(ID::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(ID::C), make_patterns_logic(it.first, true),
new_sig_d, new_sig_q, dff_cell->type == ID($_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_dict.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(ID::D, new_sig_d);
dff_cell->setPort(ID::Q, new_sig_q);
dff_cell->setParam(ID::WIDTH, GetSize(remaining_indices));
}
}
void run()
{
log("Transforming FF to FF+Enable cells in module %s:\n", log_id(module));
for (auto dff_cell : dff_cells) {
// log("Handling candidate %s:\n", log_id(dff_cell));
handle_dff_cell(dff_cell);
}
}
};
struct Dff2dffePass : public Pass {
Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" dff2dffe [options] [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 ignored in unmap mode.\n");
log("\n");
log(" -unmap-mince N\n");
log(" Same as -unmap but only unmap $dffe where the clock enable port\n");
log(" signal is used by less $dffe than the specified number\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 intermediate cell type\n");
log(" that is then translated to the final type using 'techmap'.\n");
log("\n");
log(" -direct-match <pattern>\n");
log(" like -direct for all DFF cell types matching the expression.\n");
log(" this will use $_DFFE_* as <external_gate_type> matching the\n");
log(" internal gate type $_DFF_*_, and $_SDFFE_* for those matching\n");
log(" $_SDFF_*_, except for $_DFF_[NP]_, which is converted to \n");
log(" $_DFFE_[NP]_.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
bool unmap_mode = false;
int min_ce_use = -1;
dict<IdString, IdString> direct_dict;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-unmap") {
unmap_mode = true;
continue;
}
if (args[argidx] == "-unmap-mince" && argidx + 1 < args.size()) {
unmap_mode = true;
min_ce_use = atoi(args[++argidx].c_str());
continue;
}
if (args[argidx] == "-direct" && argidx + 2 < args.size()) {
string direct_from = RTLIL::escape_id(args[++argidx]);
string direct_to = RTLIL::escape_id(args[++argidx]);
direct_dict[direct_from] = direct_to;
continue;
}
if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) {
bool found_match = false;
const char *pattern = args[++argidx].c_str();
if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict[ID($_DFF_P_) ] = ID($_DFFE_PP_);
if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict[ID($_DFF_N_) ] = ID($_DFFE_NP_);
if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict[ID($_DFF_NN0_)] = ID($_DFFE_NN0P_);
if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict[ID($_DFF_NN1_)] = ID($_DFFE_NN1P_);
if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict[ID($_DFF_NP0_)] = ID($_DFFE_NP0P_);
if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict[ID($_DFF_NP1_)] = ID($_DFFE_NP1P_);
if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict[ID($_DFF_PN0_)] = ID($_DFFE_PN0P_);
if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict[ID($_DFF_PN1_)] = ID($_DFFE_PN1P_);
if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict[ID($_DFF_PP0_)] = ID($_DFFE_PP0P_);
if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict[ID($_DFF_PP1_)] = ID($_DFFE_PP1P_);
if (patmatch(pattern, "$_SDFF_NN0_")) found_match = true, direct_dict[ID($_SDFF_NN0_)] = ID($_SDFFE_NN0P_);
if (patmatch(pattern, "$_SDFF_NN1_")) found_match = true, direct_dict[ID($_SDFF_NN1_)] = ID($_SDFFE_NN1P_);
if (patmatch(pattern, "$_SDFF_NP0_")) found_match = true, direct_dict[ID($_SDFF_NP0_)] = ID($_SDFFE_NP0P_);
if (patmatch(pattern, "$_SDFF_NP1_")) found_match = true, direct_dict[ID($_SDFF_NP1_)] = ID($_SDFFE_NP1P_);
if (patmatch(pattern, "$_SDFF_PN0_")) found_match = true, direct_dict[ID($_SDFF_PN0_)] = ID($_SDFFE_PN0P_);
if (patmatch(pattern, "$_SDFF_PN1_")) found_match = true, direct_dict[ID($_SDFF_PN1_)] = ID($_SDFFE_PN1P_);
if (patmatch(pattern, "$_SDFF_PP0_")) found_match = true, direct_dict[ID($_SDFF_PP0_)] = ID($_SDFFE_PP0P_);
if (patmatch(pattern, "$_SDFF_PP1_")) found_match = true, direct_dict[ID($_SDFF_PP1_)] = ID($_SDFFE_PP1P_);
if (!found_match)
log_cmd_error("No cell types matched pattern '%s'.\n", pattern);
continue;
}
break;
}
extra_args(args, argidx, design);
if (!direct_dict.empty()) {
log("Selected cell types for direct conversion:\n");
for (auto &it : direct_dict)
log(" %s -> %s\n", log_id(it.first), log_id(it.second));
}
for (auto mod : design->selected_modules())
if (!mod->has_processes_warn())
{
if (unmap_mode) {
SigMap sigmap(mod);
for (auto cell : mod->selected_cells()) {
if (cell->type == ID($dffe)) {
if (min_ce_use >= 0) {
int ce_use = 0;
for (auto cell_other : mod->selected_cells()) {
if (cell_other->type != cell->type)
continue;
if (sigmap(cell->getPort(ID::EN)) == sigmap(cell_other->getPort(ID::EN)))
ce_use++;
}
if (ce_use >= min_ce_use)
continue;
}
RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort(ID::D)));
mod->addDff(NEW_ID, cell->getPort(ID::CLK), tmp, cell->getPort(ID::Q), cell->getParam(ID::CLK_POLARITY).as_bool());
if (cell->getParam(ID::EN_POLARITY).as_bool())
mod->addMux(NEW_ID, cell->getPort(ID::Q), cell->getPort(ID::D), cell->getPort(ID::EN), tmp);
else
mod->addMux(NEW_ID, cell->getPort(ID::D), cell->getPort(ID::Q), cell->getPort(ID::EN), tmp);
mod->remove(cell);
continue;
}
if (cell->type.begins_with("$_DFFE_")) {
if (min_ce_use >= 0) {
int ce_use = 0;
for (auto cell_other : mod->selected_cells()) {
if (cell_other->type != cell->type)
continue;
if (sigmap(cell->getPort(ID::E)) == sigmap(cell_other->getPort(ID::E)))
ce_use++;
}
if (ce_use >= min_ce_use)
continue;
}
bool clk_pol = cell->type.compare(7, 1, "P") == 0;
bool en_pol = cell->type.compare(8, 1, "P") == 0;
RTLIL::SigSpec tmp = mod->addWire(NEW_ID);
mod->addDff(NEW_ID, cell->getPort(ID::C), tmp, cell->getPort(ID::Q), clk_pol);
if (en_pol)
mod->addMux(NEW_ID, cell->getPort(ID::Q), cell->getPort(ID::D), cell->getPort(ID::E), tmp);
else
mod->addMux(NEW_ID, cell->getPort(ID::D), cell->getPort(ID::Q), cell->getPort(ID::E), tmp);
mod->remove(cell);
continue;
}
}
continue;
}
Dff2dffeWorker worker(mod, direct_dict);
worker.run();
}
}
} Dff2dffePass;
PRIVATE_NAMESPACE_END

View File

@ -1,165 +0,0 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2018 David Shah <dave@ds0.me>
*
* 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 Dff2dffsPass : public Pass {
Dff2dffsPass() : Pass("dff2dffs", "process sync set/reset with SR over CE priority") { }
void help() override
{
log("\n");
log(" dff2dffs [options] [selection]\n");
log("\n");
log("Merge synchronous set/reset $_MUX_ cells to create $_SDFF_[NP][NP][01]_, to be run before\n");
log("dff2dffe for SR over CE priority.\n");
log("\n");
log(" -match-init\n");
log(" Disallow merging synchronous set/reset that has polarity opposite of the\n");
log(" output wire's init attribute (if any).\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n");
bool match_init = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-singleton") {
// singleton_mode = true;
// continue;
// }
if (args[argidx] == "-match-init") {
match_init = true;
continue;
}
break;
}
extra_args(args, argidx, design);
pool<IdString> dff_types;
dff_types.insert(ID($_DFF_N_));
dff_types.insert(ID($_DFF_P_));
for (auto module : design->selected_modules())
{
log("Merging set/reset $_MUX_ cells into DFFs in %s.\n", log_id(module));
SigMap sigmap(module);
dict<SigBit, Cell*> sr_muxes;
vector<Cell*> ff_cells;
for (auto cell : module->selected_cells())
{
if (dff_types.count(cell->type)) {
ff_cells.push_back(cell);
continue;
}
if (cell->type != ID($_MUX_))
continue;
SigBit bit_a = sigmap(cell->getPort(ID::A));
SigBit bit_b = sigmap(cell->getPort(ID::B));
if (bit_a.wire == nullptr || bit_b.wire == nullptr)
sr_muxes[sigmap(cell->getPort(ID::Y))] = cell;
}
for (auto cell : ff_cells)
{
SigSpec sig_d = cell->getPort(ID::D);
if (GetSize(sig_d) < 1)
continue;
SigBit bit_d = sigmap(sig_d[0]);
if (sr_muxes.count(bit_d) == 0)
continue;
Cell *mux_cell = sr_muxes.at(bit_d);
SigBit bit_a = sigmap(mux_cell->getPort(ID::A));
SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
SigBit bit_s = sigmap(mux_cell->getPort(ID::S));
SigBit sr_val, sr_sig;
bool invert_sr;
sr_sig = bit_s;
if (bit_a.wire == nullptr) {
bit_d = bit_b;
sr_val = bit_a;
invert_sr = true;
} else {
log_assert(bit_b.wire == nullptr);
bit_d = bit_a;
sr_val = bit_b;
invert_sr = false;
}
if (match_init) {
SigBit bit_q = cell->getPort(ID::Q);
if (bit_q.wire) {
auto it = bit_q.wire->attributes.find(ID::init);
if (it != bit_q.wire->attributes.end()) {
auto init_val = it->second[bit_q.offset];
if (init_val == State::S1 && sr_val != State::S1)
continue;
if (init_val == State::S0 && sr_val != State::S0)
continue;
}
}
}
log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
if (sr_val == State::S1) {
if (cell->type == ID($_DFF_N_)) {
if (invert_sr) cell->type = ID($_SDFF_NN1_);
else cell->type = ID($_SDFF_NP1_);
} else {
log_assert(cell->type == ID($_DFF_P_));
if (invert_sr) cell->type = ID($_SDFF_PN1_);
else cell->type = ID($_SDFF_PP1_);
}
} else {
if (cell->type == ID($_DFF_N_)) {
if (invert_sr) cell->type = ID($_SDFF_NN0_);
else cell->type = ID($_SDFF_NP0_);
} else {
log_assert(cell->type == ID($_DFF_P_));
if (invert_sr) cell->type = ID($_SDFF_PN0_);
else cell->type = ID($_SDFF_PP0_);
}
}
cell->setPort(ID::R, sr_sig);
cell->setPort(ID::D, bit_d);
}
}
}
} Dff2dffsPass;
PRIVATE_NAMESPACE_END

129
tests/opt/opt_dff_dffmux.ys Normal file
View File

@ -0,0 +1,129 @@
design -reset
read_verilog <<EOT
module opt_dffmuxext_unsigned(input clk, ce, input [1:0] i, output reg [3:0] o);
always @(posedge clk) if (ce) o <= i;
endmodule
EOT
proc
equiv_opt -assert opt
design -load postopt
select -assert-count 1 t:$dffe r:WIDTH=2 %i
select -assert-count 0 t:$dffe %% t:* %D
####################
design -reset
read_verilog <<EOT
module opt_dffmuxext_signed(input clk, ce, input signed [1:0] i, output reg signed [3:0] o);
always @(posedge clk) if (ce) o <= i;
endmodule
EOT
proc
equiv_opt -assert opt
design -load postopt
wreduce
select -assert-count 1 t:$dffe r:WIDTH=2 %i
select -assert-count 0 t:$dffe %% t:* %D
###################
design -reset
read_verilog <<EOT
module opt_dffmuxext_const(input clk, ce, input [1:0] i, output reg [5:0] o);
always @(posedge clk) if (ce) o <= {1'b0, i[1], 2'b1x, i[0], 1'bz};
endmodule
EOT
proc
equiv_opt -assert opt
design -load postopt
select -assert-count 1 t:$dffe r:WIDTH=2 %i
select -assert-count 0 t:$dffe %% t:* %D
###################
design -reset
read_verilog <<EOT
module opt_dffmuxext_const_init(input clk, ce, input [1:0] i, (* init=6'b0x00x1 *) output reg [5:0] o);
always @(posedge clk) if (ce) o <= {1'b0, i[1], 2'b1x, i[0], 1'bz};
endmodule
EOT
proc
equiv_opt -assert opt
design -load postopt
select -assert-count 1 t:$dffe r:WIDTH=4 %i
select -assert-count 0 t:$dffe %% t:* %D
####################
design -reset
read_verilog <<EOT
module opt_dffmuxext_unsigned_rst(input clk, ce, rst, input [1:0] i, output reg [3:0] o);
always @(posedge clk) if (rst) o <= 0; else if (ce) o <= i;
endmodule
EOT
proc
equiv_opt -assert opt
design -load postopt
wreduce
select -assert-count 1 t:$sdffe r:WIDTH=2 %i
select -assert-count 0 t:$sdffe %% t:* %D
####################
design -reset
read_verilog <<EOT
module opt_dffmuxext_signed_rst(input clk, ce, rstn, input signed [1:0] i, output reg signed [3:0] o);
always @(posedge clk) begin
if (ce) o <= i;
if (!rstn) o <= 4'b1111;
end
endmodule
EOT
proc
equiv_opt -assert opt
design -load postopt
wreduce
select -assert-count 1 t:$sdffe r:WIDTH=2 %i
select -assert-count 0 t:$sdffe %% t:* %D
####################
design -reset
read_verilog <<EOT
module opt_dffmuxext_signed_rst_init(input clk, ce, rstn, input signed [1:0] i, output reg signed [3:0] o);
initial o <= 4'b0010;
always @(posedge clk) begin
if (ce) o <= i;
if (!rstn) o <= 4'b1111;
end
endmodule
EOT
proc
# NB: equiv_opt uses equiv_induct which covers
# only the induction half of temporal induction
# --- missing the base-case half
# This makes it akin to `sat -tempinduct-inductonly`
# instead of `sat -tempinduct-baseonly` or
# `sat -tempinduct` which is necessary for this
# testcase
#equiv_opt -assert opt
design -save gold
opt
wreduce
design -stash gate
design -import gold -as gold
design -import gate -as gate
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -tempinduct -verify -prove-asserts -show-ports miter
design -load gate
select -assert-count 1 t:$sdffe r:WIDTH=3 %i
select -assert-count 0 t:$sdffe %% t:* %D

View File

@ -1,50 +0,0 @@
read_verilog << EOT
module top(...);
input clk;
input d;
input sr;
output reg q0, q1, q2, q3, q4, q5;
initial q0 = 1'b0;
initial q1 = 1'b0;
initial q2 = 1'b1;
initial q3 = 1'b1;
initial q4 = 1'bx;
initial q5 = 1'bx;
always @(posedge clk) begin
q0 <= sr ? 1'b0 : d;
q1 <= sr ? 1'b1 : d;
q2 <= sr ? 1'b0 : d;
q3 <= sr ? 1'b1 : d;
q4 <= sr ? 1'b0 : d;
q5 <= sr ? 1'b1 : d;
end
endmodule
EOT
proc
simplemap
design -save ref
dff2dffs
clean
select -assert-count 1 w:q0 %x t:$_SDFF_PP0_ %i
select -assert-count 1 w:q1 %x t:$_SDFF_PP1_ %i
select -assert-count 1 w:q2 %x t:$_SDFF_PP0_ %i
select -assert-count 1 w:q3 %x t:$_SDFF_PP1_ %i
select -assert-count 1 w:q4 %x t:$_SDFF_PP0_ %i
select -assert-count 1 w:q5 %x t:$_SDFF_PP1_ %i
design -load ref
dff2dffs -match-init
clean
select -assert-count 1 w:q0 %x t:$_SDFF_PP0_ %i
select -assert-count 0 w:q1 %x t:$_SDFF_PP1_ %i
select -assert-count 0 w:q2 %x t:$_SDFF_PP0_ %i
select -assert-count 1 w:q3 %x t:$_SDFF_PP1_ %i
select -assert-count 1 w:q4 %x t:$_SDFF_PP0_ %i
select -assert-count 1 w:q5 %x t:$_SDFF_PP1_ %i

View File

@ -68,146 +68,3 @@ equiv_opt -assert peepopt
design -load postopt
clean
select -assert-count 0 t:*
####################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_unsigned(input clk, ce, input [1:0] i, output reg [3:0] o);
always @(posedge clk) if (ce) o <= i;
endmodule
EOT
proc
equiv_opt -assert peepopt
design -load postopt
clean
select -assert-count 1 t:$dff r:WIDTH=2 %i
select -assert-count 1 t:$mux r:WIDTH=2 %i
select -assert-count 0 t:$dff t:$mux %% t:* %D
####################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_signed(input clk, ce, input signed [1:0] i, output reg signed [3:0] o);
always @(posedge clk) if (ce) o <= i;
endmodule
EOT
proc
equiv_opt -assert peepopt
design -load postopt
clean
select -assert-count 1 t:$dff r:WIDTH=2 %i
select -assert-count 1 t:$mux r:WIDTH=2 %i
select -assert-count 0 t:$dff t:$mux %% t:* %D
###################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_const(input clk, ce, input [1:0] i, output reg [5:0] o);
always @(posedge clk) if (ce) o <= {1'b0, i[1], 2'b1x, i[0], 1'bz};
endmodule
EOT
proc
equiv_opt -assert peepopt
design -load postopt
select -assert-count 1 t:$dff r:WIDTH=2 %i
select -assert-count 1 t:$mux r:WIDTH=2 %i
select -assert-count 0 t:$dff t:$mux %% t:* %D
###################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_const_init(input clk, ce, input [1:0] i, (* init=6'b0x00x1 *) output reg [5:0] o);
always @(posedge clk) if (ce) o <= {1'b0, i[1], 2'b1x, i[0], 1'bz};
endmodule
EOT
proc
equiv_opt -assert peepopt
design -load postopt
select -assert-count 1 t:$dff r:WIDTH=4 %i
select -assert-count 1 t:$mux r:WIDTH=4 %i
select -assert-count 0 t:$dff t:$mux %% t:* %D
####################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_unsigned_rst(input clk, ce, rst, input [1:0] i, output reg [3:0] o);
always @(posedge clk) if (rst) o <= 0; else if (ce) o <= i;
endmodule
EOT
proc
equiv_opt -assert peepopt
design -load postopt
wreduce
select -assert-count 1 t:$dff r:WIDTH=2 %i
select -assert-count 2 t:$mux
select -assert-count 2 t:$mux r:WIDTH=2 %i
select -assert-count 0 t:$dff t:$mux %% t:* %D
####################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_signed_rst(input clk, ce, rstn, input signed [1:0] i, output reg signed [3:0] o);
always @(posedge clk) begin
if (ce) o <= i;
if (!rstn) o <= 4'b1111;
end
endmodule
EOT
proc
equiv_opt -assert peepopt
design -load postopt
wreduce
select -assert-count 1 t:$dff r:WIDTH=2 %i
select -assert-count 2 t:$mux
select -assert-count 2 t:$mux r:WIDTH=2 %i
select -assert-count 0 t:$logic_not t:$dff t:$mux %% t:* %D
####################
design -reset
read_verilog <<EOT
module peepopt_dffmuxext_signed_rst_init(input clk, ce, rstn, input signed [1:0] i, output reg signed [3:0] o);
initial o <= 4'b0010;
always @(posedge clk) begin
if (ce) o <= i;
if (!rstn) o <= 4'b1111;
end
endmodule
EOT
proc
# NB: equiv_opt uses equiv_induct which covers
# only the induction half of temporal induction
# --- missing the base-case half
# This makes it akin to `sat -tempinduct-inductonly`
# instead of `sat -tempinduct-baseonly` or
# `sat -tempinduct` which is necessary for this
# testcase
#equiv_opt -assert peepopt
design -save gold
peepopt
wreduce
design -stash gate
design -import gold -as gold
design -import gate -as gate
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -tempinduct -verify -prove-asserts -show-ports miter
design -load gate
select -assert-count 1 t:$dff r:WIDTH=4 %i
select -assert-count 2 t:$mux
select -assert-count 2 t:$mux r:WIDTH=4 %i
select -assert-count 0 t:$logic_not t:$dff t:$mux %% t:* %D