mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #2328 from YosysHQ/mwk/opt_dff-cleanup
Remove passes redundant with opt_dff
This commit is contained in:
commit
6a68b8ed54
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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 $<,$^)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue