mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #1046 from bogdanvuk/master
Optimizing DFFs whose initial value prevents their value from changing
This commit is contained in:
commit
1c7ce251f3
|
@ -246,24 +246,24 @@ struct CellTypes
|
||||||
cell_types.clear();
|
cell_types.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cell_known(RTLIL::IdString type)
|
bool cell_known(RTLIL::IdString type) const
|
||||||
{
|
{
|
||||||
return cell_types.count(type) != 0;
|
return cell_types.count(type) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cell_output(RTLIL::IdString type, RTLIL::IdString port)
|
bool cell_output(RTLIL::IdString type, RTLIL::IdString port) const
|
||||||
{
|
{
|
||||||
auto it = cell_types.find(type);
|
auto it = cell_types.find(type);
|
||||||
return it != cell_types.end() && it->second.outputs.count(port) != 0;
|
return it != cell_types.end() && it->second.outputs.count(port) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cell_input(RTLIL::IdString type, RTLIL::IdString port)
|
bool cell_input(RTLIL::IdString type, RTLIL::IdString port) const
|
||||||
{
|
{
|
||||||
auto it = cell_types.find(type);
|
auto it = cell_types.find(type);
|
||||||
return it != cell_types.end() && it->second.inputs.count(port) != 0;
|
return it != cell_types.end() && it->second.inputs.count(port) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cell_evaluable(RTLIL::IdString type)
|
bool cell_evaluable(RTLIL::IdString type) const
|
||||||
{
|
{
|
||||||
auto it = cell_types.find(type);
|
auto it = cell_types.find(type);
|
||||||
return it != cell_types.end() && it->second.is_evaluable;
|
return it != cell_types.end() && it->second.is_evaluable;
|
||||||
|
@ -482,4 +482,3 @@ extern CellTypes yosys_celltypes;
|
||||||
YOSYS_NAMESPACE_END
|
YOSYS_NAMESPACE_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ struct OptPass : public Pass {
|
||||||
log(" opt_muxtree\n");
|
log(" opt_muxtree\n");
|
||||||
log(" opt_reduce [-fine] [-full]\n");
|
log(" opt_reduce [-fine] [-full]\n");
|
||||||
log(" opt_merge [-share_all]\n");
|
log(" opt_merge [-share_all]\n");
|
||||||
log(" opt_rmdff [-keepdc]\n");
|
log(" opt_rmdff [-keepdc] [-sat]\n");
|
||||||
log(" opt_clean [-purge]\n");
|
log(" opt_clean [-purge]\n");
|
||||||
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
|
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
|
||||||
log(" while <changed design>\n");
|
log(" while <changed design>\n");
|
||||||
|
@ -54,7 +54,7 @@ struct OptPass : public Pass {
|
||||||
log(" do\n");
|
log(" do\n");
|
||||||
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
|
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
|
||||||
log(" opt_merge [-share_all]\n");
|
log(" opt_merge [-share_all]\n");
|
||||||
log(" opt_rmdff [-keepdc]\n");
|
log(" opt_rmdff [-keepdc] [-sat]\n");
|
||||||
log(" opt_clean [-purge]\n");
|
log(" opt_clean [-purge]\n");
|
||||||
log(" while <changed design in opt_rmdff>\n");
|
log(" while <changed design in opt_rmdff>\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -112,6 +112,10 @@ struct OptPass : public Pass {
|
||||||
opt_rmdff_args += " -keepdc";
|
opt_rmdff_args += " -keepdc";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-sat") {
|
||||||
|
opt_rmdff_args += " -sat";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-share_all") {
|
if (args[argidx] == "-share_all") {
|
||||||
opt_merge_args += " -share_all";
|
opt_merge_args += " -share_all";
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -17,19 +17,24 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "kernel/register.h"
|
|
||||||
#include "kernel/sigtools.h"
|
|
||||||
#include "kernel/log.h"
|
#include "kernel/log.h"
|
||||||
#include <stdlib.h>
|
#include "kernel/register.h"
|
||||||
|
#include "kernel/rtlil.h"
|
||||||
|
#include "kernel/satgen.h"
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
SigMap assign_map, dff_init_map;
|
SigMap assign_map, dff_init_map;
|
||||||
SigSet<RTLIL::Cell*> mux_drivers;
|
SigSet<RTLIL::Cell*> mux_drivers;
|
||||||
|
dict<SigBit, RTLIL::Cell*> bit2driver;
|
||||||
dict<SigBit, pool<SigBit>> init_attributes;
|
dict<SigBit, pool<SigBit>> init_attributes;
|
||||||
|
|
||||||
bool keepdc;
|
bool keepdc;
|
||||||
|
bool sat;
|
||||||
|
|
||||||
void remove_init_attr(SigSpec sig)
|
void remove_init_attr(SigSpec sig)
|
||||||
{
|
{
|
||||||
|
@ -452,12 +457,84 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
|
||||||
dff->unsetPort("\\E");
|
dff->unsetPort("\\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("\\D");
|
||||||
|
tmp[position] = sigbit_init_val;
|
||||||
|
dff->setPort("\\D", tmp);
|
||||||
|
|
||||||
|
removed_sigbits = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed_sigbits) {
|
||||||
|
handle_dff(mod, dff);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
delete_dff:
|
delete_dff:
|
||||||
log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
|
log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
|
||||||
remove_init_attr(dff->getPort("\\Q"));
|
remove_init_attr(dff->getPort("\\Q"));
|
||||||
mod->remove(dff);
|
mod->remove(dff);
|
||||||
|
|
||||||
|
for (auto &entry : bit2driver)
|
||||||
|
if (entry.second == dff)
|
||||||
|
bit2driver.erase(entry.first);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,11 +544,15 @@ struct OptRmdffPass : public Pass {
|
||||||
{
|
{
|
||||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" opt_rmdff [-keepdc] [selection]\n");
|
log(" opt_rmdff [-keepdc] [-sat] [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("This pass identifies flip-flops with constant inputs and replaces them with\n");
|
log("This pass identifies flip-flops with constant inputs and replaces them with\n");
|
||||||
log("a constant driver.\n");
|
log("a constant driver.\n");
|
||||||
log("\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) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -479,6 +560,7 @@ struct OptRmdffPass : public Pass {
|
||||||
log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
|
log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
|
||||||
|
|
||||||
keepdc = false;
|
keepdc = false;
|
||||||
|
sat = false;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
@ -486,18 +568,22 @@ struct OptRmdffPass : public Pass {
|
||||||
keepdc = true;
|
keepdc = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-sat") {
|
||||||
|
sat = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules()) {
|
||||||
{
|
|
||||||
pool<SigBit> driven_bits;
|
pool<SigBit> driven_bits;
|
||||||
dict<SigBit, State> init_bits;
|
dict<SigBit, State> init_bits;
|
||||||
|
|
||||||
assign_map.set(module);
|
assign_map.set(module);
|
||||||
dff_init_map.set(module);
|
dff_init_map.set(module);
|
||||||
mux_drivers.clear();
|
mux_drivers.clear();
|
||||||
|
bit2driver.clear();
|
||||||
init_attributes.clear();
|
init_attributes.clear();
|
||||||
|
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
|
@ -522,17 +608,21 @@ struct OptRmdffPass : public Pass {
|
||||||
driven_bits.insert(bit);
|
driven_bits.insert(bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mux_drivers.clear();
|
|
||||||
|
|
||||||
std::vector<RTLIL::IdString> dff_list;
|
std::vector<RTLIL::IdString> dff_list;
|
||||||
std::vector<RTLIL::IdString> dffsr_list;
|
std::vector<RTLIL::IdString> dffsr_list;
|
||||||
std::vector<RTLIL::IdString> dlatch_list;
|
std::vector<RTLIL::IdString> dlatch_list;
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
for (auto &conn : cell->connections())
|
for (auto &conn : cell->connections()) {
|
||||||
if (cell->output(conn.first) || !cell->known())
|
bool is_output = cell->output(conn.first);
|
||||||
for (auto bit : assign_map(conn.second))
|
if (is_output || !cell->known())
|
||||||
|
for (auto bit : assign_map(conn.second)) {
|
||||||
|
if (is_output)
|
||||||
|
bit2driver[bit] = cell;
|
||||||
driven_bits.insert(bit);
|
driven_bits.insert(bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->type == "$mux" || cell->type == "$pmux") {
|
if (cell->type == "$mux" || cell->type == "$pmux") {
|
||||||
if (cell->getPort("\\A").size() == cell->getPort("\\B").size())
|
if (cell->getPort("\\A").size() == cell->getPort("\\B").size())
|
||||||
|
@ -604,6 +694,7 @@ struct OptRmdffPass : public Pass {
|
||||||
|
|
||||||
assign_map.clear();
|
assign_map.clear();
|
||||||
mux_drivers.clear();
|
mux_drivers.clear();
|
||||||
|
bit2driver.clear();
|
||||||
init_attributes.clear();
|
init_attributes.clear();
|
||||||
|
|
||||||
if (total_count || total_initdrv)
|
if (total_count || total_initdrv)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
module top (
|
||||||
|
input clk,
|
||||||
|
output reg [7:0] cnt
|
||||||
|
);
|
||||||
|
initial cnt = 0;
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (cnt < 20)
|
||||||
|
cnt <= cnt + 1;
|
||||||
|
else
|
||||||
|
cnt <= 0;
|
||||||
|
end
|
||||||
|
endmodule
|
|
@ -0,0 +1,5 @@
|
||||||
|
read_verilog opt_ff_sat.v
|
||||||
|
prep -flatten
|
||||||
|
opt_rmdff -sat
|
||||||
|
synth
|
||||||
|
select -assert-count 5 t:$_DFF_P_
|
Loading…
Reference in New Issue