mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #6107 from YosysHQ/emil/opt_clean-no-unused_bits
opt_clean: move unused_bits analysis out
This commit is contained in:
commit
edbc62a44e
|
|
@ -406,7 +406,6 @@ struct EdifBackend : public Backend {
|
|||
auto count_nontrivial_attr = [](Wire *w) {
|
||||
int count = w->attributes.size();
|
||||
count -= w->attributes.count(ID::src);
|
||||
count -= w->attributes.count(ID::unused_bits);
|
||||
return count;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -108,9 +108,7 @@ FSM optimization
|
|||
The `fsm_opt` pass performs basic optimizations on `$fsm` cells (not including
|
||||
state recoding). The following optimizations are performed (in this order):
|
||||
|
||||
- Unused control outputs are removed from the `$fsm` cell. The attribute
|
||||
``unused_bits`` (that is usually set by the `opt_clean` pass) is used to
|
||||
determine which control outputs are unused.
|
||||
- Unused control outputs are removed from the `$fsm` cell.
|
||||
|
||||
- Control inputs that are connected to the same driver are merged.
|
||||
|
||||
|
|
|
|||
|
|
@ -203,8 +203,6 @@ Removing unused cells and wires - `opt_clean` pass
|
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This pass identifies unused signals and cells and removes them from the design.
|
||||
It also creates an ``unused_bits`` attribute on wires with unused bits. This
|
||||
attribute can be used for debugging or by other optimization passes.
|
||||
|
||||
When to use `opt` or `clean`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ yosys_core(kernel
|
|||
threading.h
|
||||
timinginfo.h
|
||||
topo_scc.h
|
||||
unused_bits.h
|
||||
utils.h
|
||||
version.cc
|
||||
yosys.cc
|
||||
|
|
@ -145,6 +146,7 @@ yosys_core(kernel
|
|||
sigtools.h
|
||||
threading.h
|
||||
timinginfo.h
|
||||
unused_bits.h
|
||||
utils.h
|
||||
yosys.h
|
||||
yosys_common.h
|
||||
|
|
|
|||
|
|
@ -1029,7 +1029,6 @@ X(unsigned_a_i)
|
|||
X(unsigned_b)
|
||||
X(unsigned_b_i)
|
||||
X(unsupported_sva)
|
||||
X(unused_bits)
|
||||
X(use_dsp)
|
||||
X(value)
|
||||
X(via_celltype)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef UNUSED_BITS_H
|
||||
#define UNUSED_BITS_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct UnusedBits
|
||||
{
|
||||
SigMap sigmap;
|
||||
pool<RTLIL::SigBit> used;
|
||||
|
||||
UnusedBits(RTLIL::Module *module) : sigmap(module)
|
||||
{
|
||||
NewCellTypes ct(module->design);
|
||||
collect_used(module, ct);
|
||||
}
|
||||
|
||||
UnusedBits(RTLIL::Module *module, const NewCellTypes &ct) : sigmap(module)
|
||||
{
|
||||
collect_used(module, ct);
|
||||
}
|
||||
|
||||
void collect_used(RTLIL::Module *module, const NewCellTypes &ct)
|
||||
{
|
||||
for (auto cell : module->cells())
|
||||
for (auto &conn : cell->connections())
|
||||
if (!ct.cell_output(cell->type, conn.first))
|
||||
add_used(conn.second);
|
||||
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_output)
|
||||
add_used(wire);
|
||||
}
|
||||
|
||||
void add_used(const RTLIL::SigSpec &sig)
|
||||
{
|
||||
for (auto bit : sigmap(sig))
|
||||
if (bit.wire != nullptr)
|
||||
used.insert(bit);
|
||||
}
|
||||
|
||||
bool check(RTLIL::SigBit bit) const
|
||||
{
|
||||
RTLIL::SigBit mapped = sigmap(bit);
|
||||
return mapped.wire != nullptr && !used.count(mapped);
|
||||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/unused_bits.h"
|
||||
#include "backends/rtlil/rtlil_backend.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
|
@ -190,19 +191,10 @@ struct WrapcellPass : Pass {
|
|||
|
||||
bool tracking_unused = has_fmt_field(name_fmt, "%unused");
|
||||
|
||||
for (auto module : d->selected_modules()) {
|
||||
SigPool unused;
|
||||
NewCellTypes ct_all(d);
|
||||
|
||||
for (auto wire : module->wires())
|
||||
if (wire->has_attribute(ID::unused_bits)) {
|
||||
std::string str = wire->get_string_attribute(ID::unused_bits);
|
||||
for (auto it = str.begin(); it != str.end();) {
|
||||
auto sep = it;
|
||||
for (; sep != str.end() && *sep != ' '; sep++);
|
||||
unused.add(SigBit(wire, std::stoi(std::string(it, sep))));
|
||||
for (it = sep; it != str.end() && *it == ' '; it++);
|
||||
}
|
||||
}
|
||||
for (auto module : d->selected_modules()) {
|
||||
UnusedBits unused(module, ct_all);
|
||||
|
||||
for (auto cell : module->selected_cells()) {
|
||||
Module *subm;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/unused_bits.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ struct FsmExpand
|
|||
{
|
||||
RTLIL::Module *module;
|
||||
RTLIL::Cell *fsm_cell;
|
||||
UnusedBits& unused_bits;
|
||||
bool full_mode;
|
||||
|
||||
SigMap assign_map;
|
||||
|
|
@ -127,7 +129,7 @@ struct FsmExpand
|
|||
if (trans_num > limit_transitions)
|
||||
{
|
||||
log(" grown transition table to %d entries -> optimize.\n", trans_num);
|
||||
FsmData::optimize_fsm(fsm_cell, module);
|
||||
FsmData::optimize_fsm(fsm_cell, module, unused_bits);
|
||||
already_optimized = true;
|
||||
|
||||
trans_num = fsm_cell->parameters[ID::TRANS_NUM].as_int();
|
||||
|
|
@ -218,7 +220,7 @@ struct FsmExpand
|
|||
fsm_data.copy_to_cell(fsm_cell);
|
||||
}
|
||||
|
||||
FsmExpand(RTLIL::Cell *cell, RTLIL::Design *design, RTLIL::Module *mod, bool full)
|
||||
FsmExpand(RTLIL::Cell *cell, RTLIL::Design *design, RTLIL::Module *mod, UnusedBits& unused_bits, bool full) : unused_bits(unused_bits)
|
||||
{
|
||||
module = mod;
|
||||
fsm_cell = cell;
|
||||
|
|
@ -257,7 +259,7 @@ struct FsmExpand
|
|||
module->remove(c);
|
||||
|
||||
if (merged_set.size() > 0 && !already_optimized)
|
||||
FsmData::optimize_fsm(fsm_cell, module);
|
||||
FsmData::optimize_fsm(fsm_cell, module, unused_bits);
|
||||
|
||||
log(" merged %d cells into FSM.\n", GetSize(merged_set));
|
||||
}
|
||||
|
|
@ -296,12 +298,13 @@ struct FsmExpandPass : public Pass {
|
|||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto mod : design->selected_modules()) {
|
||||
UnusedBits unused_bits(mod);
|
||||
std::vector<RTLIL::Cell*> fsm_cells;
|
||||
for (auto cell : mod->selected_cells())
|
||||
if (cell->type == ID($fsm))
|
||||
fsm_cells.push_back(cell);
|
||||
for (auto c : fsm_cells) {
|
||||
FsmExpand fsm_expand(c, design, mod, full_mode);
|
||||
FsmExpand fsm_expand(c, design, mod, unused_bits, full_mode);
|
||||
fsm_expand.execute();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/unused_bits.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
|
@ -33,6 +33,7 @@ struct FsmOpt
|
|||
FsmData fsm_data;
|
||||
RTLIL::Cell *cell;
|
||||
RTLIL::Module *module;
|
||||
UnusedBits& unused_bits;
|
||||
|
||||
void opt_unreachable_states()
|
||||
{
|
||||
|
|
@ -79,21 +80,7 @@ struct FsmOpt
|
|||
|
||||
bool signal_is_unused(RTLIL::SigSpec sig)
|
||||
{
|
||||
RTLIL::SigBit bit = sig.as_bit();
|
||||
|
||||
if (bit.wire == NULL || bit.wire->attributes.count(ID::unused_bits) == 0)
|
||||
return false;
|
||||
|
||||
char *str = strdup(bit.wire->attributes[ID::unused_bits].decode_string().c_str());
|
||||
for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) {
|
||||
if (tok[0] && bit.offset == atoi(tok)) {
|
||||
free(str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
free(str);
|
||||
|
||||
return false;
|
||||
return unused_bits.check(sig.as_bit());
|
||||
}
|
||||
|
||||
void opt_const_and_unused_inputs()
|
||||
|
|
@ -294,7 +281,7 @@ struct FsmOpt
|
|||
}
|
||||
}
|
||||
|
||||
FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module)
|
||||
FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module, UnusedBits& unused_bits) : unused_bits(unused_bits)
|
||||
{
|
||||
log("Optimizing FSM `%s' from module `%s'.\n", cell->name, module->name);
|
||||
|
||||
|
|
@ -318,9 +305,9 @@ struct FsmOpt
|
|||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module)
|
||||
void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module, UnusedBits& unused_bits)
|
||||
{
|
||||
FsmOpt fsmopt(cell, module);
|
||||
FsmOpt fsmopt(cell, module, unused_bits);
|
||||
}
|
||||
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
|
@ -343,10 +330,12 @@ struct FsmOptPass : public Pass {
|
|||
log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto mod : design->selected_modules())
|
||||
for (auto mod : design->selected_modules()) {
|
||||
UnusedBits unused_bits(mod);
|
||||
for (auto cell : mod->selected_cells())
|
||||
if (cell->type == ID($fsm))
|
||||
FsmData::optimize_fsm(cell, mod);
|
||||
FsmData::optimize_fsm(cell, mod, unused_bits);
|
||||
}
|
||||
}
|
||||
} FsmOptPass;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#define FSMDATA_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/unused_bits.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -155,7 +156,7 @@ struct FsmData
|
|||
}
|
||||
|
||||
// implemented in fsm_opt.cc
|
||||
static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module);
|
||||
static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module, UnusedBits& unused_bits);
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ int count_nontrivial_wire_attrs(RTLIL::Wire *w)
|
|||
count -= w->attributes.count(ID::src);
|
||||
count -= w->attributes.count(ID::hdlname);
|
||||
count -= w->attributes.count(ID::scopename);
|
||||
count -= w->attributes.count(ID::unused_bits);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
@ -168,13 +167,6 @@ bool check_any(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool check_all(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) {
|
||||
for (SigBit b : spec)
|
||||
if (sigs.find({b, b.hash_top().yield()}) == nullptr)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct UpdateConnection {
|
||||
RTLIL::Cell *cell;
|
||||
RTLIL::IdString port;
|
||||
|
|
@ -328,33 +320,28 @@ struct DeferredUpdates {
|
|||
};
|
||||
struct UsedSignals {
|
||||
// here, "connected" means "driven or driving something"
|
||||
// meanwhile, "used" means "driving something"
|
||||
// sigmapped
|
||||
ShardedSigPool connected;
|
||||
// pre-sigmapped
|
||||
ShardedSigPool raw_connected;
|
||||
// sigmapped
|
||||
ShardedSigPool used;
|
||||
|
||||
void clear(ParallelDispatchThreadPool::Subpool &subpool) {
|
||||
subpool.run([this](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
connected.clear(ctx);
|
||||
raw_connected.clear(ctx);
|
||||
used.clear(ctx);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analysis, const AnalysisContext& actx, CleanRunContext &clean_ctx) {
|
||||
DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analysis, const AnalysisContext& actx) {
|
||||
DeferredUpdates deferred(actx.subpool);
|
||||
ShardedSigPool::Builder conn_builder(actx.subpool);
|
||||
ShardedSigPool::Builder raw_conn_builder(actx.subpool);
|
||||
ShardedSigPool::Builder used_builder(actx.subpool);
|
||||
|
||||
// gather the usage information for cells and update cell connections with the altered sigmap
|
||||
// also gather the usage information for ports, wires with `keep`
|
||||
// also gather init bits
|
||||
actx.subpool.run([&deferred, &conn_builder, &raw_conn_builder, &used_builder, &sig_analysis, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
actx.subpool.run([&deferred, &conn_builder, &raw_conn_builder, &sig_analysis, &actx](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
// Parallel destruction of these sharded structures
|
||||
sig_analysis.clear(ctx);
|
||||
|
||||
|
|
@ -366,8 +353,6 @@ DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analys
|
|||
deferred.update_connections.insert(ctx, {cell, port, spec});
|
||||
add_spec(raw_conn_builder, ctx, spec);
|
||||
add_spec(conn_builder, ctx, spec);
|
||||
if (!clean_ctx.ct_all.cell_output(cell->type, port))
|
||||
add_spec(used_builder, ctx, spec);
|
||||
}
|
||||
}
|
||||
for (int i : ctx.item_range(actx.mod->wires_size())) {
|
||||
|
|
@ -377,8 +362,6 @@ DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analys
|
|||
add_spec(raw_conn_builder, ctx, sig);
|
||||
actx.assign_map.apply(sig);
|
||||
add_spec(conn_builder, ctx, sig);
|
||||
if (!wire->port_input)
|
||||
add_spec(used_builder, ctx, sig);
|
||||
}
|
||||
if (wire->get_bool_attribute(ID::keep)) {
|
||||
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
|
||||
|
|
@ -390,12 +373,11 @@ DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analys
|
|||
deferred.initialized_wires.insert(ctx, wire);
|
||||
}
|
||||
});
|
||||
actx.subpool.run([&conn_builder, &raw_conn_builder, &used_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
actx.subpool.run([&conn_builder, &raw_conn_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
conn_builder.process(ctx);
|
||||
raw_conn_builder.process(ctx);
|
||||
used_builder.process(ctx);
|
||||
});
|
||||
used = {conn_builder, raw_conn_builder, used_builder};
|
||||
used = {conn_builder, raw_conn_builder};
|
||||
return deferred;
|
||||
}
|
||||
|
||||
|
|
@ -404,14 +386,10 @@ struct WireDeleter {
|
|||
ShardedVector<RTLIL::Wire*> remove_init;
|
||||
ShardedVector<std::pair<RTLIL::Wire*, RTLIL::Const>> set_init;
|
||||
ShardedVector<RTLIL::SigSig> new_connections;
|
||||
ShardedVector<RTLIL::Wire*> remove_unused_bits;
|
||||
ShardedVector<std::pair<RTLIL::Wire*, RTLIL::Const>> set_unused_bits;
|
||||
WireDeleter(UsedSignals& used_sig_analysis, bool purge_mode, const AnalysisContext& actx) :
|
||||
remove_init(actx.subpool),
|
||||
set_init(actx.subpool),
|
||||
new_connections(actx.subpool),
|
||||
remove_unused_bits(actx.subpool),
|
||||
set_unused_bits(actx.subpool) {
|
||||
new_connections(actx.subpool) {
|
||||
ShardedVector<RTLIL::Wire*> del_wires(actx.subpool);
|
||||
actx.subpool.run([&actx, purge_mode, &del_wires, &used_sig_analysis, this](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(actx.mod->wires_size())) {
|
||||
|
|
@ -471,32 +449,6 @@ struct WireDeleter {
|
|||
} else
|
||||
if (init_changed)
|
||||
set_init.insert(ctx, {wire, std::move(initval)});
|
||||
|
||||
std::string unused_bits;
|
||||
if (!check_all(used_sig_analysis.used, s2)) {
|
||||
for (int i = 0; i < GetSize(s2); i++) {
|
||||
if (s2[i].wire == NULL)
|
||||
continue;
|
||||
SigBit b = s2[i];
|
||||
if (used_sig_analysis.used.find({b, b.hash_top().yield()}) == nullptr) {
|
||||
if (!unused_bits.empty())
|
||||
unused_bits += " ";
|
||||
unused_bits += stringf("%d", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unused_bits.empty() || wire->port_id != 0) {
|
||||
if (wire->attributes.count(ID::unused_bits))
|
||||
remove_unused_bits.insert(ctx, wire);
|
||||
} else {
|
||||
RTLIL::Const unused_bits_const(std::move(unused_bits));
|
||||
if (wire->attributes.count(ID::unused_bits)) {
|
||||
RTLIL::Const &unused_bits_attr = wire->attributes.at(ID::unused_bits);
|
||||
if (unused_bits_attr != unused_bits_const)
|
||||
set_unused_bits.insert(ctx, {wire, std::move(unused_bits_const)});
|
||||
} else
|
||||
set_unused_bits.insert(ctx, {wire, std::move(unused_bits_const)});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -511,10 +463,6 @@ struct WireDeleter {
|
|||
p.first->attributes[ID::init] = std::move(p.second);
|
||||
for (auto &conn : new_connections)
|
||||
mod->connect(std::move(conn));
|
||||
for (RTLIL::Wire *wire : remove_unused_bits)
|
||||
wire->attributes.erase(ID::unused_bits);
|
||||
for (auto &p : set_unused_bits)
|
||||
p.first->attributes[ID::unused_bits] = std::move(p.second);
|
||||
}
|
||||
int delete_wires(RTLIL::Module* mod, bool verbose) {
|
||||
int deleted_and_unreported = 0;
|
||||
|
|
@ -557,7 +505,7 @@ bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool::
|
|||
module->connections_.clear();
|
||||
|
||||
UsedSignals used;
|
||||
DeferredUpdates deferred = analyse_connectivity(used, conn_kinds, actx, clean_ctx);
|
||||
DeferredUpdates deferred = analyse_connectivity(used, conn_kinds, actx);
|
||||
fixup_cell_ports(deferred.update_connections);
|
||||
// Rip up and re-apply init attributes onto representative wires with x-bits
|
||||
// in place of unset init bits
|
||||
|
|
|
|||
|
|
@ -453,7 +453,6 @@ struct WreduceWorker
|
|||
{
|
||||
int count = w->attributes.size();
|
||||
count -= w->attributes.count(ID::src);
|
||||
count -= w->attributes.count(ID::unused_bits);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue