mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-22 07:05:51 +00:00
Merge remote-tracking branch 'upstream/main' into silimate
This commit is contained in:
commit
e58125b605
834 changed files with 25281 additions and 8780 deletions
11
passes/CMakeLists.txt
Normal file
11
passes/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
add_subdirectory(cmds)
|
||||
add_subdirectory(equiv)
|
||||
add_subdirectory(fsm)
|
||||
add_subdirectory(hierarchy)
|
||||
add_subdirectory(memory)
|
||||
add_subdirectory(opt)
|
||||
add_subdirectory(pmgen)
|
||||
add_subdirectory(proc)
|
||||
add_subdirectory(sat)
|
||||
add_subdirectory(techmap)
|
||||
add_subdirectory(tests)
|
||||
213
passes/cmds/CMakeLists.txt
Normal file
213
passes/cmds/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
add_subdirectory(sdc)
|
||||
|
||||
yosys_pass(exec
|
||||
exec.cc
|
||||
ENABLE_IF
|
||||
YOSYS_ENABLE_SPAWN
|
||||
)
|
||||
yosys_pass(add
|
||||
add.cc
|
||||
)
|
||||
yosys_pass(delete
|
||||
delete.cc
|
||||
)
|
||||
yosys_pass(design
|
||||
design.cc
|
||||
ESSENTIAL
|
||||
)
|
||||
yosys_pass(design_equal
|
||||
design_equal.cc
|
||||
)
|
||||
yosys_pass(select
|
||||
select.cc
|
||||
PROVIDES
|
||||
cd
|
||||
ls
|
||||
ESSENTIAL
|
||||
)
|
||||
yosys_pass(show
|
||||
show.cc
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
|
||||
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
|
||||
REQUIRES
|
||||
read_rtlil
|
||||
read_verilog
|
||||
)
|
||||
yosys_pass(viz
|
||||
viz.cc
|
||||
)
|
||||
yosys_pass(rename
|
||||
rename.cc
|
||||
REQUIRES
|
||||
write_verilog
|
||||
)
|
||||
yosys_pass(autoname
|
||||
autoname.cc
|
||||
)
|
||||
yosys_pass(connect
|
||||
connect.cc
|
||||
)
|
||||
yosys_pass(scatter
|
||||
scatter.cc
|
||||
)
|
||||
yosys_pass(setundef
|
||||
setundef.cc
|
||||
)
|
||||
yosys_pass(splitnets
|
||||
splitnets.cc
|
||||
)
|
||||
yosys_pass(splitcells
|
||||
splitcells.cc
|
||||
)
|
||||
yosys_pass(stat
|
||||
stat.cc
|
||||
REQUIRES
|
||||
libparse
|
||||
)
|
||||
yosys_pass(internal_stats
|
||||
internal_stats.cc
|
||||
REQUIRES
|
||||
libparse
|
||||
)
|
||||
yosys_pass(setattr
|
||||
setattr.cc
|
||||
PROVIDES
|
||||
wbflip
|
||||
setparam
|
||||
chparam
|
||||
)
|
||||
yosys_pass(copy
|
||||
copy.cc
|
||||
)
|
||||
yosys_pass(splice
|
||||
splice.cc
|
||||
)
|
||||
yosys_pass(scc
|
||||
scc.cc
|
||||
)
|
||||
yosys_pass(glift
|
||||
glift.cc
|
||||
)
|
||||
yosys_pass(torder
|
||||
torder.cc
|
||||
)
|
||||
yosys_pass(logcmd
|
||||
logcmd.cc
|
||||
)
|
||||
yosys_pass(tee
|
||||
tee.cc
|
||||
)
|
||||
yosys_pass(write_file
|
||||
write_file.cc
|
||||
)
|
||||
yosys_pass(connwrappers
|
||||
connwrappers.cc
|
||||
)
|
||||
yosys_pass(trace
|
||||
trace.cc
|
||||
PROVIDES
|
||||
debug
|
||||
)
|
||||
yosys_pass(plugin
|
||||
plugin.cc
|
||||
INCLUDE_DIRS
|
||||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_PLUGINS}:${Dlfcn_LIBRARIES}>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Module>
|
||||
ESSENTIAL
|
||||
)
|
||||
yosys_pass(check
|
||||
check.cc
|
||||
)
|
||||
yosys_pass(edgetypes
|
||||
edgetypes.cc
|
||||
)
|
||||
yosys_pass(portlist
|
||||
portlist.cc
|
||||
)
|
||||
yosys_pass(chformal
|
||||
chformal.cc
|
||||
)
|
||||
yosys_pass(chtype
|
||||
chtype.cc
|
||||
)
|
||||
yosys_pass(blackbox
|
||||
blackbox.cc
|
||||
)
|
||||
yosys_pass(ltp
|
||||
ltp.cc
|
||||
)
|
||||
yosys_pass(linux_perf
|
||||
linux_perf.cc
|
||||
)
|
||||
yosys_pass(bugpoint
|
||||
bugpoint.cc
|
||||
REQUIRES
|
||||
proc_clean
|
||||
opt_clean
|
||||
design
|
||||
ENABLE_IF
|
||||
YOSYS_ENABLE_SPAWN
|
||||
)
|
||||
yosys_pass(scratchpad
|
||||
scratchpad.cc
|
||||
)
|
||||
yosys_pass(logger
|
||||
logger.cc
|
||||
)
|
||||
yosys_pass(printattrs
|
||||
printattrs.cc
|
||||
)
|
||||
yosys_pass(sta
|
||||
sta.cc
|
||||
)
|
||||
yosys_pass(clean_zerowidth
|
||||
clean_zerowidth.cc
|
||||
)
|
||||
yosys_pass(xprop
|
||||
xprop.cc
|
||||
REQUIRES
|
||||
bmuxmap
|
||||
demuxmap
|
||||
)
|
||||
yosys_pass(dft_tag
|
||||
dft_tag.cc
|
||||
)
|
||||
yosys_pass(future
|
||||
future.cc
|
||||
)
|
||||
yosys_pass(box_derive
|
||||
box_derive.cc
|
||||
)
|
||||
yosys_pass(example_dt
|
||||
example_dt.cc
|
||||
)
|
||||
yosys_pass(portarcs
|
||||
portarcs.cc
|
||||
)
|
||||
yosys_pass(wrapcell
|
||||
wrapcell.cc
|
||||
)
|
||||
yosys_pass(setenv
|
||||
setenv.cc
|
||||
)
|
||||
yosys_pass(abstract
|
||||
abstract.cc
|
||||
)
|
||||
yosys_pass(test_select
|
||||
test_select.cc
|
||||
)
|
||||
yosys_pass(timeest
|
||||
timeest.cc
|
||||
)
|
||||
yosys_pass(linecoverage
|
||||
linecoverage.cc
|
||||
)
|
||||
yosys_pass(sort
|
||||
sort.cc
|
||||
)
|
||||
yosys_pass(icell_liberty
|
||||
icell_liberty.cc
|
||||
)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
|
||||
ifeq ($(DISABLE_SPAWN),0)
|
||||
OBJS += passes/cmds/exec.o
|
||||
endif
|
||||
OBJS += passes/cmds/add.o
|
||||
OBJS += passes/cmds/delete.o
|
||||
OBJS += passes/cmds/design.o
|
||||
OBJS += passes/cmds/design_equal.o
|
||||
OBJS += passes/cmds/select.o
|
||||
OBJS += passes/cmds/show.o
|
||||
OBJS += passes/cmds/viz.o
|
||||
OBJS += passes/cmds/rename.o
|
||||
OBJS += passes/cmds/autoname.o
|
||||
OBJS += passes/cmds/connect.o
|
||||
OBJS += passes/cmds/scatter.o
|
||||
OBJS += passes/cmds/setundef.o
|
||||
OBJS += passes/cmds/splitnets.o
|
||||
OBJS += passes/cmds/splitcells.o
|
||||
OBJS += passes/cmds/stat.o
|
||||
OBJS += passes/cmds/internal_stats.o
|
||||
OBJS += passes/cmds/setattr.o
|
||||
OBJS += passes/cmds/copy.o
|
||||
OBJS += passes/cmds/splice.o
|
||||
OBJS += passes/cmds/scc.o
|
||||
OBJS += passes/cmds/glift.o
|
||||
OBJS += passes/cmds/torder.o
|
||||
OBJS += passes/cmds/logcmd.o
|
||||
OBJS += passes/cmds/tee.o
|
||||
OBJS += passes/cmds/write_file.o
|
||||
OBJS += passes/cmds/connwrappers.o
|
||||
OBJS += passes/cmds/trace.o
|
||||
OBJS += passes/cmds/plugin.o
|
||||
OBJS += passes/cmds/check.o
|
||||
OBJS += passes/cmds/edgetypes.o
|
||||
OBJS += passes/cmds/portlist.o
|
||||
OBJS += passes/cmds/chformal.o
|
||||
OBJS += passes/cmds/chtype.o
|
||||
OBJS += passes/cmds/blackbox.o
|
||||
OBJS += passes/cmds/ltp.o
|
||||
OBJS += passes/cmds/linux_perf.o
|
||||
ifeq ($(DISABLE_SPAWN),0)
|
||||
OBJS += passes/cmds/bugpoint.o
|
||||
endif
|
||||
OBJS += passes/cmds/scratchpad.o
|
||||
OBJS += passes/cmds/logger.o
|
||||
OBJS += passes/cmds/printattrs.o
|
||||
OBJS += passes/cmds/sta.o
|
||||
OBJS += passes/cmds/clean_zerowidth.o
|
||||
OBJS += passes/cmds/xprop.o
|
||||
OBJS += passes/cmds/dft_tag.o
|
||||
OBJS += passes/cmds/future.o
|
||||
OBJS += passes/cmds/box_derive.o
|
||||
OBJS += passes/cmds/example_dt.o
|
||||
OBJS += passes/cmds/portarcs.o
|
||||
OBJS += passes/cmds/wrapcell.o
|
||||
OBJS += passes/cmds/setenv.o
|
||||
OBJS += passes/cmds/abstract.o
|
||||
OBJS += passes/cmds/test_select.o
|
||||
OBJS += passes/cmds/timeest.o
|
||||
OBJS += passes/cmds/linecoverage.o
|
||||
OBJS += passes/cmds/sort.o
|
||||
OBJS += passes/cmds/icell_liberty.o
|
||||
|
||||
include $(YOSYS_SRC)/passes/cmds/sdc/Makefile.inc
|
||||
|
|
@ -67,7 +67,7 @@ struct Slice {
|
|||
int wire_offset(RTLIL::Wire *wire, int index) const {
|
||||
int rtl_offset = indices == RtlilSlice ? index : wire->from_hdl_index(index);
|
||||
if (rtl_offset < 0 || rtl_offset >= wire->width) {
|
||||
log_error("Slice %s is out of bounds for wire %s in module %s", to_string(), log_id(wire), log_id(wire->module));
|
||||
log_error("Slice %s is out of bounds for wire %s in module %s", to_string(), wire, wire->module);
|
||||
}
|
||||
return rtl_offset;
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector<S
|
|||
for (int i = 0; i < GetSize(ff.sig_q); i++) {
|
||||
SigBit bit = ff.sig_q[i];
|
||||
if (selected_reps.count(sigmap(bit))) {
|
||||
log_debug("Abstracting state for %s.Q[%i] in module %s due to selections:\n", log_id(ff.cell), i, log_id(mod));
|
||||
log_debug("Abstracting state for %s.Q[%i] in module %s due to selections:\n", ff.cell, i, mod);
|
||||
explain_selections(selected_reps.at(sigmap(bit)));
|
||||
offsets_to_abstract.insert(i);
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector<S
|
|||
for (int i = 0; i < conn.second.size(); i++) {
|
||||
if (selected_reps.count(sigmap(conn.second[i]))) {
|
||||
log_debug("Abstracting value for %s.%s[%i] in module %s due to selections:\n",
|
||||
log_id(cell), log_id(conn.first), i, log_id(mod));
|
||||
cell, conn.first.unescape(), i, mod);
|
||||
explain_selections(selected_reps.at(sigmap(conn.second[i])));
|
||||
offsets_to_abstract.insert(i);
|
||||
}
|
||||
|
|
@ -289,7 +289,7 @@ unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector<S
|
|||
for (auto bit : SigSpec(wire))
|
||||
if (selected_reps.count(sigmap(bit))) {
|
||||
log_debug("Abstracting value for module input port bit %s in module %s due to selections:\n",
|
||||
log_signal(bit), log_id(mod));
|
||||
log_signal(bit), mod);
|
||||
explain_selections(selected_reps.at(sigmap(bit)));
|
||||
offsets_to_abstract.insert(bit.offset);
|
||||
}
|
||||
|
|
@ -469,6 +469,7 @@ struct AbstractPass : public Pass {
|
|||
switch (enable) {
|
||||
case Enable::Always:
|
||||
log_assert(false);
|
||||
YS_FALLTHROUGH
|
||||
case Enable::ActiveLow:
|
||||
case Enable::ActiveHigh: {
|
||||
if (enable_name.empty())
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ int autoname_worker(Module *module, const dict<Wire*, unsigned int>& wire_score)
|
|||
for (auto bit : conn.second)
|
||||
if (bit.wire != nullptr && bit.wire->name[0] != '$') {
|
||||
if (suffix.empty())
|
||||
suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
|
||||
suffix = stringf("_%s_%s", cell->type.unescape(), conn.first.unescape());
|
||||
name_proposal proposed_name(
|
||||
bit.wire->name.str() + suffix,
|
||||
cell->output(conn.first) ? 0 : wire_score.at(bit.wire)
|
||||
|
|
@ -66,7 +66,7 @@ int autoname_worker(Module *module, const dict<Wire*, unsigned int>& wire_score)
|
|||
for (auto bit : conn.second)
|
||||
if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
|
||||
if (suffix.empty())
|
||||
suffix = stringf("_%s", log_id(conn.first));
|
||||
suffix = stringf("_%s", conn.first.unescape());
|
||||
name_proposal proposed_name(
|
||||
cell->name.str() + suffix,
|
||||
cell->output(conn.first) ? 0 : wire_score.at(bit.wire)
|
||||
|
|
@ -90,7 +90,7 @@ int autoname_worker(Module *module, const dict<Wire*, unsigned int>& wire_score)
|
|||
if (best_name < it.second)
|
||||
continue;
|
||||
IdString n = module->uniquify(IdString(it.second.name));
|
||||
log_debug("Rename cell %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
|
||||
log_debug("Rename cell %s in %s to %s.\n", it.first, module, n.unescape());
|
||||
module->rename(it.first, n);
|
||||
count++;
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ int autoname_worker(Module *module, const dict<Wire*, unsigned int>& wire_score)
|
|||
if (best_name < it.second)
|
||||
continue;
|
||||
IdString n = module->uniquify(IdString(it.second.name));
|
||||
log_debug("Rename wire %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
|
||||
log_debug("Rename wire %s in %s to %s.\n", it.first, module, n.unescape());
|
||||
module->rename(it.first, n);
|
||||
count++;
|
||||
}
|
||||
|
|
@ -151,7 +151,7 @@ struct AutonamePass : public Pass {
|
|||
count += n;
|
||||
}
|
||||
if (count > 0)
|
||||
log("Renamed %d objects in module %s (%d iterations).\n", count, log_id(module), iter);
|
||||
log("Renamed %d objects in module %s (%d iterations).\n", count, module, iter);
|
||||
}
|
||||
}
|
||||
} AutonamePass;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ struct BoxDerivePass : Pass {
|
|||
if (!base_name.empty()) {
|
||||
base_override = d->module(base_name);
|
||||
if (!base_override)
|
||||
log_cmd_error("Base module %s not found.\n", log_id(base_name));
|
||||
log_cmd_error("Base module %s not found.\n", base_name.unescape());
|
||||
}
|
||||
|
||||
dict<std::pair<RTLIL::IdString, dict<RTLIL::IdString, RTLIL::Const>>, Module*> done;
|
||||
|
|
@ -109,7 +109,7 @@ struct BoxDerivePass : Pass {
|
|||
IdString new_name = RTLIL::escape_id(derived->get_string_attribute(naming_attr));
|
||||
if (!new_name.isPublic())
|
||||
log_error("Derived module %s cannot be renamed to private name %s.\n",
|
||||
log_id(derived), log_id(new_name));
|
||||
derived, new_name.unescape());
|
||||
derived->attributes.erase(naming_attr);
|
||||
d->rename(derived, new_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove module %s.\n", log_id(module));
|
||||
log_header(design, "Trying to remove module %s.\n", module);
|
||||
removed_module = module;
|
||||
break;
|
||||
}
|
||||
|
|
@ -242,7 +242,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove module port %s.\n", log_id(wire));
|
||||
log_header(design, "Trying to remove module port %s.\n", wire);
|
||||
wire->port_input = wire->port_output = false;
|
||||
mod->fixup_ports();
|
||||
return design_copy;
|
||||
|
|
@ -265,7 +265,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove cell %s.%s.\n", log_id(mod), log_id(cell));
|
||||
log_header(design, "Trying to remove cell %s.%s.\n", mod, cell);
|
||||
removed_cell = cell;
|
||||
break;
|
||||
}
|
||||
|
|
@ -296,7 +296,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove cell port %s.%s.%s.\n", log_id(mod), log_id(cell), log_id(it.first));
|
||||
log_header(design, "Trying to remove cell port %s.%s.%s.\n", mod, cell, it.first.unescape());
|
||||
RTLIL::SigSpec port_x(State::Sx, port.size());
|
||||
cell->unsetPort(it.first);
|
||||
cell->setPort(it.first, port_x);
|
||||
|
|
@ -305,7 +305,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to expose cell port %s.%s.%s as module port.\n", log_id(mod), log_id(cell), log_id(it.first));
|
||||
log_header(design, "Trying to expose cell port %s.%s.%s as module port.\n", mod, cell, it.first.unescape());
|
||||
RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size());
|
||||
wire->set_bool_attribute(ID($bugpoint));
|
||||
wire->port_input = cell->input(it.first);
|
||||
|
|
@ -334,7 +334,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove process %s.%s.\n", log_id(mod), log_id(process.first));
|
||||
log_header(design, "Trying to remove process %s.%s.\n", mod, process.first.unescape());
|
||||
removed_process = process.second;
|
||||
break;
|
||||
}
|
||||
|
|
@ -363,7 +363,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first));
|
||||
log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->first), log_signal(it->second), mod, pr.first.unescape());
|
||||
cs->actions.erase(it);
|
||||
return design_copy;
|
||||
}
|
||||
|
|
@ -389,7 +389,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first));
|
||||
log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->first), log_signal(it->second), mod, pr.first.unescape());
|
||||
sy->actions.erase(it);
|
||||
return design_copy;
|
||||
}
|
||||
|
|
@ -399,7 +399,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove sync %s memwr %s %s %s %s in %s.%s.\n", log_signal(sy->signal), log_id(it->memid), log_signal(it->address), log_signal(it->data), log_signal(it->enable), log_id(mod), log_id(pr.first));
|
||||
log_header(design, "Trying to remove sync %s memwr %s %s %s %s in %s.%s.\n", log_signal(sy->signal), it->memid.unescape(), log_signal(it->address), log_signal(it->data), log_signal(it->enable), mod, pr.first.unescape());
|
||||
sy->mem_write_actions.erase(it);
|
||||
// Remove the bit for removed action from other actions' priority masks.
|
||||
for (auto it2 = sy->mem_write_actions.begin(); it2 != sy->mem_write_actions.end(); ++it2) {
|
||||
|
|
@ -437,7 +437,7 @@ struct BugpointPass : public Pass {
|
|||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log_header(design, "Trying to remove wire %s.%s.\n", log_id(mod), log_id(wire));
|
||||
log_header(design, "Trying to remove wire %s.%s.\n", mod, wire);
|
||||
removed_wire = wire;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celledges.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/log_help.h"
|
||||
#include "kernel/mem.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
|
@ -117,7 +118,7 @@ struct CheckPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_whole_modules_warn())
|
||||
{
|
||||
log("Checking module %s...\n", log_id(module));
|
||||
log("Checking module %s...\n", module);
|
||||
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, vector<string>> wire_drivers;
|
||||
|
|
@ -133,7 +134,7 @@ struct CheckPass : public Pass {
|
|||
for (auto bit : sigmap(action.first))
|
||||
wire_drivers[bit].push_back(
|
||||
stringf("action %s <= %s (case rule) in process %s",
|
||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||
log_signal(action.first), log_signal(action.second), proc_it.first.unescape()));
|
||||
|
||||
for (auto bit : sigmap(action.second))
|
||||
if (bit.wire) used_wires.insert(bit);
|
||||
|
|
@ -154,7 +155,7 @@ struct CheckPass : public Pass {
|
|||
for (auto bit : sigmap(action.first))
|
||||
wire_drivers[bit].push_back(
|
||||
stringf("action %s <= %s (sync rule) in process %s",
|
||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||
log_signal(action.first), log_signal(action.second), proc_it.first.unescape()));
|
||||
for (auto bit : sigmap(action.second))
|
||||
if (bit.wire) used_wires.insert(bit);
|
||||
}
|
||||
|
|
@ -259,7 +260,7 @@ struct CheckPass : public Pass {
|
|||
{
|
||||
if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) {
|
||||
if (allow_tbuf && cell->type == ID($_TBUF_)) goto cell_allowed;
|
||||
log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||
log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", module, cell, cell->type.unescape());
|
||||
counter++;
|
||||
cell_allowed:;
|
||||
}
|
||||
|
|
@ -275,10 +276,10 @@ struct CheckPass : public Pass {
|
|||
if (input && bit.wire)
|
||||
used_wires.insert(bit);
|
||||
if (output && !input && bit.wire)
|
||||
wire_drivers_count[bit]++;
|
||||
wire_drivers_count[bit]++;
|
||||
if (output && (bit.wire || !input))
|
||||
wire_drivers[bit].push_back(stringf("port %s[%d] of cell %s (%s)", log_id(conn.first), i,
|
||||
log_id(cell), log_id(cell->type)));
|
||||
wire_drivers[bit].push_back(stringf("port %s[%d] of cell %s (%s)", conn.first.unescape(), i,
|
||||
cell, cell->type.unescape()));
|
||||
if (output)
|
||||
driver_cells[bit] = cell;
|
||||
}
|
||||
|
|
@ -298,7 +299,7 @@ struct CheckPass : public Pass {
|
|||
SigSpec sig = sigmap(wire);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (sig[i].wire || !wire->port_output)
|
||||
wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
|
||||
wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", wire, i));
|
||||
}
|
||||
if (wire->port_output)
|
||||
for (auto bit : sigmap(wire))
|
||||
|
|
@ -312,7 +313,7 @@ struct CheckPass : public Pass {
|
|||
if (initval[i] == State::S0 || initval[i] == State::S1)
|
||||
init_bits.insert(sigmap(SigBit(wire, i)));
|
||||
if (noinit) {
|
||||
log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire));
|
||||
log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", module, wire);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
|
@ -329,7 +330,7 @@ struct CheckPass : public Pass {
|
|||
|
||||
for (auto it : wire_drivers)
|
||||
if (wire_drivers_count[it.first] > 1) {
|
||||
string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first));
|
||||
string message = stringf("multiple conflicting drivers for %s.%s:\n", module, log_signal(it.first));
|
||||
for (auto str : it.second)
|
||||
message += stringf(" %s\n", str);
|
||||
log_warning("%s", message);
|
||||
|
|
@ -338,13 +339,13 @@ struct CheckPass : public Pass {
|
|||
|
||||
for (auto bit : used_wires)
|
||||
if (!wire_drivers.count(bit)) {
|
||||
log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit));
|
||||
log_warning("Wire %s.%s is used but has no driver.\n", module, log_signal(bit));
|
||||
counter++;
|
||||
}
|
||||
|
||||
topo.sort();
|
||||
for (auto &loop : topo.loops) {
|
||||
string message = stringf("found logic loop in module %s:\n", log_id(module));
|
||||
string message = stringf("found logic loop in module %s:\n", module);
|
||||
|
||||
// `loop` only contains wire bits, or an occasional special helper node for cells for
|
||||
// which we have done the edges fallback. The cell and its ports that led to an edge are
|
||||
|
|
@ -378,8 +379,8 @@ struct CheckPass : public Pass {
|
|||
SigBit edge_to = sigmap(cell->getPort(to_port))[to_bit];
|
||||
|
||||
if (edge_from == from && edge_to == to && nhits++ < HITS_LIMIT)
|
||||
message += stringf(" %s[%d] --> %s[%d]\n", log_id(from_port), from_bit,
|
||||
log_id(to_port), to_bit);
|
||||
message += stringf(" %s[%d] --> %s[%d]\n", from_port.unescape(), from_bit,
|
||||
to_port.unescape(), to_bit);
|
||||
if (nhits == HITS_LIMIT)
|
||||
message += " ...\n";
|
||||
}
|
||||
|
|
@ -397,7 +398,7 @@ struct CheckPass : public Pass {
|
|||
driver_src = stringf(" source: %s", src_attr);
|
||||
}
|
||||
|
||||
message += stringf(" cell %s (%s)%s\n", log_id(driver), log_id(driver->type), driver_src);
|
||||
message += stringf(" cell %s (%s)%s\n", driver, driver->type.unescape(), driver_src);
|
||||
|
||||
if (!coarsened_cells.count(driver)) {
|
||||
MatchingEdgePrinter printer(message, sigmap, prev, bit);
|
||||
|
|
@ -437,7 +438,7 @@ struct CheckPass : public Pass {
|
|||
init_sig.sort_and_unify();
|
||||
|
||||
for (auto chunk : init_sig.chunks()) {
|
||||
log_warning("Wire %s.%s has 'init' attribute and is not driven by an FF cell.\n", log_id(module), log_signal(chunk));
|
||||
log_warning("Wire %s.%s has 'init' attribute and is not driven by an FF cell.\n", module, log_signal(chunk));
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
|
@ -453,4 +454,95 @@ struct CheckPass : public Pass {
|
|||
}
|
||||
} CheckPass;
|
||||
|
||||
struct CheckMemPass : public Pass {
|
||||
CheckMemPass() : Pass("check_mem", "check for obvious memory problems in the design") { }
|
||||
bool formatted_help() override {
|
||||
auto *help = PrettyHelp::get_current();
|
||||
help->set_group("passes/status");
|
||||
|
||||
auto content_root = help->get_root();
|
||||
|
||||
content_root->usage("check_mem [selection]");
|
||||
content_root->paragraph(
|
||||
"This pass identifies the following problems in the current design: "
|
||||
"addressing invalid memory."
|
||||
);
|
||||
|
||||
content_root->option("-non-const", "also check non-const address signals (may produce false-positives)");
|
||||
content_root->option("-assert", "produce a runtime error if any problems are found in the current design");
|
||||
|
||||
return true;
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
int counter = 0;
|
||||
bool assert_mode = false;
|
||||
bool nonconst_mode = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-assert") {
|
||||
assert_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-non-const") {
|
||||
nonconst_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
log_header(design, "Executing CHECK_MEM pass.\n");
|
||||
|
||||
for (auto *module : design->selected_unboxed_modules_warn()) {
|
||||
for (auto mem : Mem::get_selected_memories(module)) {
|
||||
int min_addr = mem.mem->start_offset;
|
||||
int max_addr = mem.mem->size + min_addr - 1;
|
||||
for (auto &init : mem.inits) {
|
||||
int start = init.addr.as_int();
|
||||
if (start < min_addr) {
|
||||
log_warning("Mem %s.%s starts at %d but initializes address %d.\n", log_id(module), log_id(mem.mem), min_addr, start);
|
||||
counter++;
|
||||
}
|
||||
int end = start + (GetSize(init.data) / mem.width) - 1;
|
||||
if (end > max_addr) {
|
||||
log_warning("Mem %s.%s ends at %d but initializes address %d.\n", log_id(module), log_id(mem.mem), max_addr, end);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
auto check_addr = [min_addr, max_addr, &counter, module, &mem, &nonconst_mode](SigSpec &addr_sig, const char* access) {
|
||||
if (addr_sig.is_fully_const()) {
|
||||
auto addr = addr_sig.as_int();
|
||||
if (addr < min_addr || addr > max_addr) {
|
||||
log_warning("Mem %s.%s contains entries for addresses %d..%d but %s address %d.\n", log_id(module), log_id(mem.mem), min_addr, max_addr, access, addr);
|
||||
counter++;
|
||||
}
|
||||
} else if (nonconst_mode) {
|
||||
// TODO check addr_sig.has_const() for constant MSb/LSb that may change effective min/max
|
||||
// TODO consider sat solver for variable addresses
|
||||
int addr_sig_min = 0;
|
||||
int addr_sig_max = (1 << addr_sig.size()) - 1;
|
||||
if (min_addr > addr_sig_min || max_addr < addr_sig_max) {
|
||||
log_warning("Mem %s.%s contains entries for addresses %d..%d but has a potentially dangerous non-const input %s\n", log_id(module), log_id(mem.mem), min_addr, max_addr, log_signal(addr_sig));
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO test ABITS and WIDTH?
|
||||
// TODO can we limit ports via selection?
|
||||
for (auto &rd_port : mem.rd_ports)
|
||||
check_addr(rd_port.addr, "reads");
|
||||
for (auto &wr_port : mem.wr_ports)
|
||||
check_addr(wr_port.addr, "writes");
|
||||
}
|
||||
}
|
||||
|
||||
if (assert_mode && counter > 0)
|
||||
log_error("Found %d problems in 'check_mem -assert'.\n", counter);
|
||||
}
|
||||
} CheckMemPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ struct ChformalPass : public Pass {
|
|||
for (auto cell : constr_cells)
|
||||
{
|
||||
if (is_triggered_check_cell(cell))
|
||||
log_error("Cannot delay edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
|
||||
log_error("Cannot delay edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", cell);
|
||||
|
||||
for (int i = 0; i < mode_arg; i++)
|
||||
{
|
||||
|
|
@ -411,7 +411,7 @@ struct ChformalPass : public Pass {
|
|||
continue;
|
||||
|
||||
if (is_triggered_check_cell(cell))
|
||||
log_error("Cannot lower edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
|
||||
log_error("Cannot lower edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", cell);
|
||||
|
||||
|
||||
Cell *plain_cell = module->addCell(NEW_ID, formal_flavor(cell));
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ struct ConnectPass : public Pass {
|
|||
RTLIL::Module *module = nullptr;
|
||||
for (auto mod : design->selected_modules()) {
|
||||
if (module != nullptr)
|
||||
log_cmd_error("Multiple modules selected: %s, %s\n", log_id(module->name), log_id(mod->name));
|
||||
log_cmd_error("Multiple modules selected: %s, %s\n", module->name.unescape(), mod->name.unescape());
|
||||
module = mod;
|
||||
}
|
||||
if (module == nullptr)
|
||||
|
|
|
|||
|
|
@ -134,8 +134,8 @@ struct ConnwrappersWorker
|
|||
}
|
||||
|
||||
if (old_sig.size())
|
||||
log("Connected extended bits of %s.%s:%s: %s -> %s\n", log_id(module->name), log_id(cell->name),
|
||||
log_id(conn.first), log_signal(old_sig), log_signal(conn.second));
|
||||
log("Connected extended bits of %s.%s:%s: %s -> %s\n", module->name.unescape(), cell->name.unescape(),
|
||||
conn.first.unescape(), log_signal(old_sig), log_signal(conn.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ struct DesignPass : public Pass {
|
|||
|
||||
for (auto mod : copy_src_modules)
|
||||
{
|
||||
log("Importing %s as %s.\n", log_id(mod), log_id(prefix));
|
||||
log("Importing %s as %s.\n", mod, RTLIL::unescape_id(prefix));
|
||||
|
||||
RTLIL::Module *t = mod->clone();
|
||||
t->name = prefix;
|
||||
|
|
@ -295,7 +295,7 @@ struct DesignPass : public Pass {
|
|||
{
|
||||
std::string trg_name = prefix + "." + (cell->type.c_str() + (*cell->type.c_str() == '\\'));
|
||||
|
||||
log("Importing %s as %s.\n", log_id(fmod), log_id(trg_name));
|
||||
log("Importing %s as %s.\n", fmod, RTLIL::unescape_id(trg_name));
|
||||
|
||||
if (copy_to_design->module(trg_name) != nullptr)
|
||||
copy_to_design->remove(copy_to_design->module(trg_name));
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ public:
|
|||
[[noreturn]]
|
||||
void formatted_error(std::string err)
|
||||
{
|
||||
log("Module A: %s\n", log_id(mod_a->name));
|
||||
log("Module A: %s\n", mod_a->name.unescape());
|
||||
log_module(mod_a, " ");
|
||||
log("Module B: %s\n", log_id(mod_b->name));
|
||||
log("Module B: %s\n", mod_b->name.unescape());
|
||||
log_module(mod_b, " ");
|
||||
log_cmd_error("Designs are different: %s\n", err);
|
||||
}
|
||||
|
|
@ -68,20 +68,20 @@ public:
|
|||
{
|
||||
for (const auto &it : a->attributes) {
|
||||
if (b->attributes.count(it.first) == 0)
|
||||
return "missing attribute " + std::string(log_id(it.first)) + " in second design";
|
||||
return "missing attribute " + std::string(it.first.unescape()) + " in second design";
|
||||
if (it.second != b->attributes.at(it.first))
|
||||
return "attribute " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->attributes.at(it.first));
|
||||
return "attribute " + std::string(it.first.unescape()) + " mismatch: " + log_const(it.second) + " != " + log_const(b->attributes.at(it.first));
|
||||
}
|
||||
for (const auto &it : b->attributes)
|
||||
if (a->attributes.count(it.first) == 0)
|
||||
return "missing attribute " + std::string(log_id(it.first)) + " in first design";
|
||||
return "missing attribute " + std::string(it.first.unescape()) + " in first design";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_wires(const RTLIL::Wire *a, const RTLIL::Wire *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
return "name mismatch: " + std::string(a->name.unescape()) + " != " + b->name.unescape();
|
||||
if (a->width != b->width)
|
||||
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
|
||||
if (a->start_offset != b->start_offset)
|
||||
|
|
@ -105,19 +105,19 @@ public:
|
|||
{
|
||||
for (const auto &it : mod_a->wires_) {
|
||||
if (mod_b->wires_.count(it.first) == 0)
|
||||
error("Module %s missing wire %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
error("Module %s missing wire %s in second design.\n", mod_a->name.unescape(), it.first.unescape());
|
||||
if (std::string mismatch = compare_wires(it.second, mod_b->wires_.at(it.first)); !mismatch.empty())
|
||||
error("Module %s wire %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
error("Module %s wire %s %s.\n", mod_a->name.unescape(), it.first.unescape(), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->wires_)
|
||||
if (mod_a->wires_.count(it.first) == 0)
|
||||
error("Module %s missing wire %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
error("Module %s missing wire %s in first design.\n", mod_b->name.unescape(), it.first.unescape());
|
||||
}
|
||||
|
||||
std::string compare_memories(const RTLIL::Memory *a, const RTLIL::Memory *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
return "name mismatch: " + std::string(a->name.unescape()) + " != " + b->name.unescape();
|
||||
if (a->width != b->width)
|
||||
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
|
||||
if (a->start_offset != b->start_offset)
|
||||
|
|
@ -132,31 +132,31 @@ public:
|
|||
std::string compare_cells(const RTLIL::Cell *a, const RTLIL::Cell *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
return "name mismatch: " + std::string(a->name.unescape()) + " != " + b->name.unescape();
|
||||
if (a->type != b->type)
|
||||
return "type mismatch: " + std::string(log_id(a->type)) + " != " + log_id(b->type);
|
||||
return "type mismatch: " + std::string(a->type.unescape()) + " != " + b->type.unescape();
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
|
||||
for (const auto &it : a->parameters) {
|
||||
if (b->parameters.count(it.first) == 0)
|
||||
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in second design";
|
||||
return "parameter mismatch: missing parameter " + std::string(it.first.unescape()) + " in second design";
|
||||
if (it.second != b->parameters.at(it.first))
|
||||
return "parameter mismatch: " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->parameters.at(it.first));
|
||||
return "parameter mismatch: " + std::string(it.first.unescape()) + " mismatch: " + log_const(it.second) + " != " + log_const(b->parameters.at(it.first));
|
||||
}
|
||||
for (const auto &it : b->parameters)
|
||||
if (a->parameters.count(it.first) == 0)
|
||||
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in first design";
|
||||
return "parameter mismatch: missing parameter " + std::string(it.first.unescape()) + " in first design";
|
||||
|
||||
for (const auto &it : a->connections()) {
|
||||
if (b->connections().count(it.first) == 0)
|
||||
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in second design";
|
||||
return "connection mismatch: missing connection " + std::string(it.first.unescape()) + " in second design";
|
||||
if (!compare_sigspec(it.second, b->connections().at(it.first)))
|
||||
return "connection " + std::string(log_id(it.first)) + " mismatch: " + log_signal(it.second) + " != " + log_signal(b->connections().at(it.first));
|
||||
return "connection " + std::string(it.first.unescape()) + " mismatch: " + log_signal(it.second) + " != " + log_signal(b->connections().at(it.first));
|
||||
}
|
||||
for (const auto &it : b->connections())
|
||||
if (a->connections().count(it.first) == 0)
|
||||
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in first design";
|
||||
return "connection mismatch: missing connection " + std::string(it.first.unescape()) + " in first design";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
|
@ -165,26 +165,26 @@ public:
|
|||
{
|
||||
for (const auto &it : mod_a->cells_) {
|
||||
if (mod_b->cells_.count(it.first) == 0)
|
||||
error("Module %s missing cell %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
error("Module %s missing cell %s in second design.\n", mod_a->name.unescape(), it.first.unescape());
|
||||
if (std::string mismatch = compare_cells(it.second, mod_b->cells_.at(it.first)); !mismatch.empty())
|
||||
error("Module %s cell %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
error("Module %s cell %s %s.\n", mod_a->name.unescape(), it.first.unescape(), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->cells_)
|
||||
if (mod_a->cells_.count(it.first) == 0)
|
||||
error("Module %s missing cell %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
error("Module %s missing cell %s in first design.\n", mod_b->name.unescape(), it.first.unescape());
|
||||
}
|
||||
|
||||
void check_memories()
|
||||
{
|
||||
for (const auto &it : mod_a->memories) {
|
||||
if (mod_b->memories.count(it.first) == 0)
|
||||
error("Module %s missing memory %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
error("Module %s missing memory %s in second design.\n", mod_a->name.unescape(), it.first.unescape());
|
||||
if (std::string mismatch = compare_memories(it.second, mod_b->memories.at(it.first)); !mismatch.empty())
|
||||
error("Module %s memory %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
error("Module %s memory %s %s.\n", mod_a->name.unescape(), it.first.unescape(), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->memories)
|
||||
if (mod_a->memories.count(it.first) == 0)
|
||||
error("Module %s missing memory %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
error("Module %s missing memory %s in first design.\n", mod_b->name.unescape(), it.first.unescape());
|
||||
}
|
||||
|
||||
std::string compare_case_rules(const RTLIL::CaseRule *a, const RTLIL::CaseRule *b)
|
||||
|
|
@ -251,7 +251,7 @@ public:
|
|||
const auto &ma = a->mem_write_actions[i];
|
||||
const auto &mb = b->mem_write_actions[i];
|
||||
if (ma.memid != mb.memid)
|
||||
return "mem_write_actions " + std::to_string(i) + " memid mismatch: " + log_id(ma.memid) + " != " + log_id(mb.memid);
|
||||
return "mem_write_actions " + std::to_string(i) + " memid mismatch: " + ma.memid.unescape() + " != " + mb.memid.unescape();
|
||||
if (!compare_sigspec(ma.address, mb.address))
|
||||
return "mem_write_actions " + std::to_string(i) + " address mismatch: " + log_signal(ma.address) + " != " + log_signal(mb.address);
|
||||
if (!compare_sigspec(ma.data, mb.data))
|
||||
|
|
@ -268,7 +268,7 @@ public:
|
|||
|
||||
std::string compare_processes(const RTLIL::Process *a, const RTLIL::Process *b)
|
||||
{
|
||||
if (a->name != b->name) return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->name != b->name) return "name mismatch: " + std::string(a->name.unescape()) + " != " + b->name.unescape();
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
if (std::string mismatch = compare_case_rules(&a->root_case, &b->root_case); !mismatch.empty())
|
||||
|
|
@ -285,13 +285,13 @@ public:
|
|||
{
|
||||
for (auto &it : mod_a->processes) {
|
||||
if (mod_b->processes.count(it.first) == 0)
|
||||
error("Module %s missing process %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
error("Module %s missing process %s in second design.\n", mod_a->name.unescape(), it.first.unescape());
|
||||
if (std::string mismatch = compare_processes(it.second, mod_b->processes.at(it.first)); !mismatch.empty())
|
||||
error("Module %s process %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch.c_str());
|
||||
error("Module %s process %s %s.\n", mod_a->name.unescape(), it.first.unescape(), mismatch.c_str());
|
||||
}
|
||||
for (auto &it : mod_b->processes)
|
||||
if (mod_a->processes.count(it.first) == 0)
|
||||
error("Module %s missing process %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
error("Module %s missing process %s in first design.\n", mod_b->name.unescape(), it.first.unescape());
|
||||
}
|
||||
|
||||
void check_connections()
|
||||
|
|
@ -299,13 +299,13 @@ public:
|
|||
const auto &conns_a = mod_a->connections();
|
||||
const auto &conns_b = mod_b->connections();
|
||||
if (conns_a.size() != conns_b.size()) {
|
||||
error("Module %s connection count differs: %zu != %zu\n", log_id(mod_a->name), conns_a.size(), conns_b.size());
|
||||
error("Module %s connection count differs: %zu != %zu\n", mod_a->name.unescape(), conns_a.size(), conns_b.size());
|
||||
} else {
|
||||
for (size_t i = 0; i < conns_a.size(); i++) {
|
||||
if (!compare_sigspec(conns_a[i].first, conns_b[i].first))
|
||||
error("Module %s connection %zu LHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].first), log_signal(conns_b[i].first));
|
||||
error("Module %s connection %zu LHS %s != %s.\n", mod_a->name.unescape(), i, log_signal(conns_a[i].first), log_signal(conns_b[i].first));
|
||||
if (!compare_sigspec(conns_a[i].second, conns_b[i].second))
|
||||
error("Module %s connection %zu RHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].second), log_signal(conns_b[i].second));
|
||||
error("Module %s connection %zu RHS %s != %s.\n", mod_a->name.unescape(), i, log_signal(conns_a[i].second), log_signal(conns_b[i].second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -313,9 +313,9 @@ public:
|
|||
void check()
|
||||
{
|
||||
if (mod_a->name != mod_b->name)
|
||||
error("Modules have different names: %s != %s\n", log_id(mod_a->name), log_id(mod_b->name));
|
||||
error("Modules have different names: %s != %s\n", mod_a->name.unescape(), mod_b->name.unescape());
|
||||
if (std::string mismatch = compare_attributes(mod_a, mod_b); !mismatch.empty())
|
||||
error("Module %s %s.\n", log_id(mod_a->name), mismatch);
|
||||
error("Module %s %s.\n", mod_a->name.unescape(), mismatch);
|
||||
check_wires();
|
||||
check_cells();
|
||||
check_memories();
|
||||
|
|
@ -349,7 +349,7 @@ struct DesignEqualPass : public Pass {
|
|||
for (auto &it : design->modules_) {
|
||||
RTLIL::Module *mod = it.second;
|
||||
if (!other->has(mod->name))
|
||||
log_error("Second design missing module %s.\n", log_id(mod->name));
|
||||
log_error("Second design missing module %s.\n", mod->name.unescape());
|
||||
|
||||
ModuleComparator cmp(mod, other->module(mod->name));
|
||||
cmp.check();
|
||||
|
|
@ -357,7 +357,7 @@ struct DesignEqualPass : public Pass {
|
|||
for (auto &it : other->modules_) {
|
||||
RTLIL::Module *mod = it.second;
|
||||
if (!design->has(mod->name))
|
||||
log_error("First design missing module %s.\n", log_id(mod->name));
|
||||
log_error("First design missing module %s.\n", mod->name.unescape());
|
||||
}
|
||||
|
||||
log("Designs are identical.\n");
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ struct DftTagWorker {
|
|||
}
|
||||
|
||||
for (auto cell : overwrite_cells) {
|
||||
log_debug("Applying $overwrite_tag %s for signal %s\n", log_id(cell->name), log_signal(cell->getPort(ID::A)));
|
||||
log_debug("Applying $overwrite_tag %s for signal %s\n", cell->name.unescape(), log_signal(cell->getPort(ID::A)));
|
||||
SigSpec orig_signal = cell->getPort(ID::A);
|
||||
SigSpec interposed_signal = divert_users(orig_signal);
|
||||
auto *set_tag_cell = module->addSetTag(NEW_ID, cell->getParam(ID::TAG).decode_string(), orig_signal, cell->getPort(ID::SET), cell->getPort(ID::CLR), interposed_signal);
|
||||
|
|
@ -470,9 +470,9 @@ struct DftTagWorker {
|
|||
if (!warned_cells.insert(cell).second)
|
||||
return;
|
||||
if (cell->type.isPublic())
|
||||
log_warning("Unhandled cell %s (%s) during tag propagation\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Unhandled cell %s (%s) during tag propagation\n", cell, cell->type.unescape());
|
||||
else
|
||||
log_debug("Unhandled cell %s (%s) during tag propagation\n", log_id(cell), log_id(cell->type));
|
||||
log_debug("Unhandled cell %s (%s) during tag propagation\n", cell, cell->type.unescape());
|
||||
}
|
||||
|
||||
void process_cell(IdString tag, Cell *cell)
|
||||
|
|
@ -691,7 +691,7 @@ struct DftTagWorker {
|
|||
// TODO handle some more variants
|
||||
if ((ff.has_clk || ff.has_gclk) && !ff.has_ce && !ff.has_aload && !ff.has_srst && !ff.has_arst && !ff.has_sr) {
|
||||
if (ff.has_clk && !tags(ff.sig_clk).empty())
|
||||
log_warning("Tags on CLK input ignored for %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Tags on CLK input ignored for %s (%s)\n", cell, cell->type.unescape());
|
||||
|
||||
int width = ff.width;
|
||||
|
||||
|
|
@ -709,7 +709,7 @@ struct DftTagWorker {
|
|||
emit_tag_signal(tag, sig_q, ff.sig_q);
|
||||
return;
|
||||
} else {
|
||||
log_warning("Unhandled FF-cell %s (%s), consider running clk2fflogic, async2sync and/or dffunmap\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Unhandled FF-cell %s (%s), consider running clk2fflogic, async2sync and/or dffunmap\n", cell, cell->type.unescape());
|
||||
|
||||
// For unhandled FFs, the default propagation would cause combinational loops
|
||||
emit_tag_signal(tag, ff.sig_q, Const(0, ff.width));
|
||||
|
|
@ -739,7 +739,7 @@ struct DftTagWorker {
|
|||
// which is an over-approximation (unless the cell is a module that
|
||||
// generates tags itself in which case it could be arbitrary).
|
||||
if (warned_cells.insert(cell).second)
|
||||
log_warning("Unhandled cell %s (%s) while emitting tag signals\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Unhandled cell %s (%s) while emitting tag signals\n", cell, cell->type.unescape());
|
||||
}
|
||||
|
||||
void emit_tags()
|
||||
|
|
|
|||
|
|
@ -92,12 +92,12 @@ struct EdgetypePass : public Pass {
|
|||
auto sink_bit_index = std::get<2>(sink);
|
||||
|
||||
string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ?
|
||||
stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) :
|
||||
stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name));
|
||||
stringf("%s.%s[%d]", source_cell_type.unescape(), source_port_name.unescape(), source_bit_index) :
|
||||
stringf("%s.%s", source_cell_type.unescape(), source_port_name.unescape());
|
||||
|
||||
string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ?
|
||||
stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) :
|
||||
stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name));
|
||||
stringf("%s.%s[%d]", sink_cell_type.unescape(), sink_port_name.unescape(), sink_bit_index) :
|
||||
stringf("%s.%s", sink_cell_type.unescape(), sink_port_name.unescape());
|
||||
|
||||
edge_cache.insert(source_str + " " + sink_str);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,13 +226,13 @@ struct ExampleDtPass : public Pass
|
|||
{
|
||||
auto ref = compute_graph[i];
|
||||
log("n%d ", i);
|
||||
log("%s", log_id(ref.function().name));
|
||||
log("%s", ref.function().name.unescape());
|
||||
for (auto const ¶m : ref.function().parameters)
|
||||
{
|
||||
if (param.second.empty())
|
||||
log("[%s]", log_id(param.first));
|
||||
log("[%s]", param.first.unescape());
|
||||
else
|
||||
log("[%s=%s]", log_id(param.first), log_const(param.second));
|
||||
log("[%s=%s]", param.first.unescape(), log_const(param.second));
|
||||
}
|
||||
log("(");
|
||||
|
||||
|
|
@ -244,13 +244,13 @@ struct ExampleDtPass : public Pass
|
|||
}
|
||||
log(")\n");
|
||||
if (ref.has_sparse_attr())
|
||||
log("// wire %s\n", log_id(ref.sparse_attr()));
|
||||
log("// wire %s\n", ref.sparse_attr().unescape());
|
||||
log("// was #%d %s\n", ref.attr(), log_signal(queue[ref.attr()]));
|
||||
}
|
||||
|
||||
for (auto const &key : compute_graph.keys())
|
||||
{
|
||||
log("return %d as %s \n", key.second, log_id(key.first));
|
||||
log("return %d as %s \n", key.second, key.first.unescape());
|
||||
}
|
||||
}
|
||||
log("Plugin test passed!\n");
|
||||
|
|
|
|||
|
|
@ -86,13 +86,13 @@ struct FutureWorker {
|
|||
log_error("Found multiple drivers for future_ff target signal %s\n", log_signal(bit));
|
||||
auto driver = *found_driver->second.begin();
|
||||
if (!driver.cell->is_builtin_ff() && driver.cell->type != ID($anyinit))
|
||||
log_error("Driver for future_ff target signal %s has non-FF cell type %s\n", log_signal(bit), log_id(driver.cell->type));
|
||||
log_error("Driver for future_ff target signal %s has non-FF cell type %s\n", log_signal(bit), driver.cell->type.unescape());
|
||||
|
||||
FfData ff(&initvals, driver.cell);
|
||||
|
||||
if (!ff.has_clk && !ff.has_gclk)
|
||||
log_error("Driver for future_ff target signal %s has cell type %s, which is not clocked\n", log_signal(bit),
|
||||
log_id(driver.cell->type));
|
||||
driver.cell->type.unescape());
|
||||
|
||||
ff.unmap_ce_srst();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/ff.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
|
@ -70,10 +71,10 @@ struct LibertyStubber {
|
|||
std::sort(sorted_ports.begin(), sorted_ports.end(), cmp);
|
||||
std::string clock_pin_name = "";
|
||||
for (auto x : sorted_ports) {
|
||||
std::string port_name = RTLIL::unescape_id(x);
|
||||
std::string port_name = x.unescape();
|
||||
bool is_input = base_type.inputs.count(x);
|
||||
bool is_output = base_type.outputs.count(x);
|
||||
f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n";
|
||||
f << "\t\tpin (" << x.unescape() << ") {\n";
|
||||
if (is_input && !is_output) {
|
||||
i.item("direction", "input");
|
||||
} else if (!is_input && is_output) {
|
||||
|
|
@ -123,7 +124,7 @@ struct LibertyStubber {
|
|||
return;
|
||||
}
|
||||
|
||||
if (RTLIL::builtin_ff_cell_types().count(base_name))
|
||||
if (StaticCellTypes::categories.is_ff(base_name))
|
||||
return liberty_flop(base, derived, f);
|
||||
|
||||
auto& base_type = ct.cell_types[base_name];
|
||||
|
|
@ -131,7 +132,7 @@ struct LibertyStubber {
|
|||
for (auto x : derived->ports) {
|
||||
bool is_input = base_type.inputs.count(x);
|
||||
bool is_output = base_type.outputs.count(x);
|
||||
f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n";
|
||||
f << "\t\tpin (" << x.unescape() << ") {\n";
|
||||
if (is_input && !is_output) {
|
||||
f << "\t\t\tdirection : input;\n";
|
||||
} else if (!is_input && is_output) {
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@ struct CoveragePass : public Pass {
|
|||
|
||||
for (auto module : design->modules())
|
||||
{
|
||||
log_debug("Module %s:\n", log_id(module));
|
||||
log_debug("Module %s:\n", module);
|
||||
for (auto wire: module->wires()) {
|
||||
log_debug("%s\t%s\t%s\n", module->selected(wire) ? "*" : " ", wire->get_src_attribute(), log_id(wire->name));
|
||||
log_debug("%s\t%s\t%s\n", module->selected(wire) ? "*" : " ", wire->get_src_attribute(), wire->name.unescape());
|
||||
for (auto src: wire->get_strpool_attribute(ID::src)) {
|
||||
auto filename = extract_src_filename(src);
|
||||
if (filename.empty()) continue;
|
||||
|
|
@ -109,7 +109,7 @@ struct CoveragePass : public Pass {
|
|||
}
|
||||
}
|
||||
for (auto cell: module->cells()) {
|
||||
log_debug("%s\t%s\t%s\n", module->selected(cell) ? "*" : " ", cell->get_src_attribute(), log_id(cell->name));
|
||||
log_debug("%s\t%s\t%s\n", module->selected(cell) ? "*" : " ", cell->get_src_attribute(), cell->name.unescape());
|
||||
for (auto src: cell->get_strpool_attribute(ID::src)) {
|
||||
auto filename = extract_src_filename(src);
|
||||
if (filename.empty()) continue;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ struct LtpWorker
|
|||
return;
|
||||
|
||||
if (busy.count(bit) > 0) {
|
||||
log_warning("Detected loop at %s in %s\n", log_signal(bit), log_id(module));
|
||||
log_warning("Detected loop at %s in %s\n", log_signal(bit), module);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ struct LtpWorker
|
|||
auto &bitinfo = bits.at(bit);
|
||||
if (get<2>(bitinfo)) {
|
||||
printpath(get<1>(bitinfo));
|
||||
log("%5d: %s (via %s)\n", get<0>(bitinfo), log_signal(bit), log_id(get<2>(bitinfo)));
|
||||
log("%5d: %s (via %s)\n", get<0>(bitinfo), log_signal(bit), get<2>(bitinfo));
|
||||
} else {
|
||||
log("%5d: %s\n", get<0>(bitinfo), log_signal(bit));
|
||||
}
|
||||
|
|
@ -130,13 +130,13 @@ struct LtpWorker
|
|||
runner(it.first, 0, State::Sx, nullptr);
|
||||
|
||||
log("\n");
|
||||
log("Longest topological path in %s (length=%d):\n", log_id(module), maxlvl);
|
||||
log("Longest topological path in %s (length=%d):\n", module, maxlvl);
|
||||
|
||||
if (maxlvl >= 0)
|
||||
printpath(maxbit);
|
||||
|
||||
if (bit2ff.count(maxbit))
|
||||
log("%5s: %s (via %s)\n", "ff", log_signal(get<0>(bit2ff.at(maxbit))), log_id(get<1>(bit2ff.at(maxbit))));
|
||||
log("%5s: %s (via %s)\n", "ff", log_signal(get<0>(bit2ff.at(maxbit))), get<1>(bit2ff.at(maxbit)));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ struct PortarcsPass : Pass {
|
|||
log_assert(w->port_input || w->port_output);
|
||||
if (w->port_input && w->port_output) {
|
||||
log_warning("Module '%s' with ambiguous direction on port %s ignored.\n",
|
||||
log_id(m), log_id(w));
|
||||
m, w);
|
||||
ambiguous_ports = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ struct PortarcsPass : Pass {
|
|||
if (!cell->type.in(ID($buf), ID($input_port), ID($connect), ID($tribuf))) {
|
||||
auto tdata = tinfo.find(cell->type);
|
||||
if (tdata == tinfo.end())
|
||||
log_cmd_error("Missing timing data for module '%s'.\n", log_id(cell->type));
|
||||
log_cmd_error("Missing timing data for module '%s'.\n", cell->type.unescape());
|
||||
for (auto [edge, delay] : tdata->second.comb) {
|
||||
auto from = edge.first.get_connection(cell);
|
||||
auto to = edge.second.get_connection(cell);
|
||||
|
|
@ -141,7 +141,7 @@ struct PortarcsPass : Pass {
|
|||
}
|
||||
|
||||
if (!sort.sort())
|
||||
log_error("Failed to sort instances in module %s.\n", log_id(m));
|
||||
log_error("Failed to sort instances in module %s.\n", m);
|
||||
|
||||
ordering = sort.sorted;
|
||||
}
|
||||
|
|
@ -244,7 +244,7 @@ struct PortarcsPass : Pass {
|
|||
|
||||
if (draw_mode) {
|
||||
auto bit_str = [](SigBit bit) {
|
||||
return stringf("%s%d", RTLIL::unescape_id(bit.wire->name.str()), bit.offset);
|
||||
return stringf("%s%d", bit.wire, bit.offset);
|
||||
};
|
||||
|
||||
std::vector<std::string> headings;
|
||||
|
|
|
|||
|
|
@ -71,9 +71,9 @@ struct PortlistPass : public Pass {
|
|||
ports.push_back(stringf("%s [%d:%d] %s", w->port_input ? w->port_output ? "inout" : "input" : "output",
|
||||
w->upto ? w->start_offset : w->start_offset + w->width - 1,
|
||||
w->upto ? w->start_offset + w->width - 1 : w->start_offset,
|
||||
log_id(w)));
|
||||
w));
|
||||
}
|
||||
log("module %s%s\n", log_id(module), m_mode ? " (" : "");
|
||||
log("module %s%s\n", module, m_mode ? " (" : "");
|
||||
for (int i = 0; i < GetSize(ports); i++)
|
||||
log("%s%s\n", ports[i], m_mode && i+1 < GetSize(ports) ? "," : "");
|
||||
if (m_mode)
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ struct PrintAttrsPass : public Pass {
|
|||
|
||||
static void log_const(RTLIL::IdString s, const RTLIL::Const &x, const unsigned int indent) {
|
||||
if (x.flags & RTLIL::CONST_FLAG_STRING)
|
||||
log("%s(* %s=\"%s\" *)\n", get_indent_str(indent), log_id(s), x.decode_string());
|
||||
log("%s(* %s=\"%s\" *)\n", get_indent_str(indent), s.unescape(), x.decode_string());
|
||||
else if (x.flags == RTLIL::CONST_FLAG_NONE || x.flags == RTLIL::CONST_FLAG_SIGNED)
|
||||
log("%s(* %s=%s *)\n", get_indent_str(indent), log_id(s), x.as_string());
|
||||
log("%s(* %s=%s *)\n", get_indent_str(indent), s.unescape(), x.as_string());
|
||||
else
|
||||
log_assert(x.flags & RTLIL::CONST_FLAG_STRING || x.flags == RTLIL::CONST_FLAG_NONE); //intended to fail
|
||||
}
|
||||
|
|
@ -63,14 +63,14 @@ struct PrintAttrsPass : public Pass {
|
|||
for (auto mod : design->selected_modules())
|
||||
{
|
||||
if (design->selected_whole_module(mod)) {
|
||||
log("%s%s\n", get_indent_str(indent), log_id(mod->name));
|
||||
log("%s%s\n", get_indent_str(indent), mod->name.unescape());
|
||||
indent += 2;
|
||||
for (auto &it : mod->attributes)
|
||||
log_const(it.first, it.second, indent);
|
||||
}
|
||||
|
||||
for (auto cell : mod->selected_cells()) {
|
||||
log("%s%s\n", get_indent_str(indent), log_id(cell->name));
|
||||
log("%s%s\n", get_indent_str(indent), cell->name.unescape());
|
||||
indent += 2;
|
||||
for (auto &it : cell->attributes)
|
||||
log_const(it.first, it.second, indent);
|
||||
|
|
@ -78,7 +78,7 @@ struct PrintAttrsPass : public Pass {
|
|||
}
|
||||
|
||||
for (auto wire : mod->selected_wires()) {
|
||||
log("%s%s\n", get_indent_str(indent), log_id(wire->name));
|
||||
log("%s%s\n", get_indent_str(indent), wire->name.unescape());
|
||||
indent += 2;
|
||||
for (auto &it : wire->attributes)
|
||||
log_const(it.first, it.second, indent);
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
|
|||
to_name = RTLIL::escape_id(to_name);
|
||||
|
||||
if (module->count_id(to_name))
|
||||
log_cmd_error("There is already an object `%s' in module `%s'.\n", to_name, module->name);
|
||||
log_cmd_error("There is already an object `%s' in module `%s'.\n", RTLIL::unescape_id(to_name), module->name);
|
||||
|
||||
RTLIL::Wire *wire_to_rename = module->wire(from_name);
|
||||
RTLIL::Cell *cell_to_rename = module->cell(from_name);
|
||||
|
||||
if (wire_to_rename != nullptr) {
|
||||
log("Renaming wire %s to %s in module %s.\n", log_id(wire_to_rename), log_id(to_name), log_id(module));
|
||||
log("Renaming wire %s to %s in module %s.\n", wire_to_rename, RTLIL::unescape_id(to_name), module);
|
||||
module->rename(wire_to_rename, to_name);
|
||||
if (wire_to_rename->port_id || flag_output) {
|
||||
if (flag_output)
|
||||
|
|
@ -50,12 +50,12 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
|
|||
if (cell_to_rename != nullptr) {
|
||||
if (flag_output)
|
||||
log_cmd_error("Called with -output but the specified object is a cell.\n");
|
||||
log("Renaming cell %s to %s in module %s.\n", log_id(cell_to_rename), log_id(to_name), log_id(module));
|
||||
log("Renaming cell %s to %s in module %s.\n", cell_to_rename, RTLIL::unescape_id(to_name), module);
|
||||
module->rename(cell_to_rename, to_name);
|
||||
return;
|
||||
}
|
||||
|
||||
log_cmd_error("Object `%s' not found!\n", from_name);
|
||||
log_cmd_error("Object `%s' not found!\n", RTLIL::unescape_id(from_name));
|
||||
}
|
||||
|
||||
static std::string derive_name_from_src(const std::string &src, int counter)
|
||||
|
|
@ -254,18 +254,17 @@ struct RenamePass : public Pass {
|
|||
log("\n");
|
||||
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
|
||||
log("\n");
|
||||
log("Assign short auto-generated names to all selected wires and cells with private\n");
|
||||
log("names. The -pattern option can be used to set the pattern for the new names.\n");
|
||||
log("The character %% in the pattern is replaced with a integer number. The default\n");
|
||||
log("pattern is '_%%_'.\n");
|
||||
log("Assigns auto-generated names to objects used in formal verification\n");
|
||||
log("that do not have a public name. This applies to all formal property\n");
|
||||
log("cells, $any*/$all* output wires, and their containing cells.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -witness\n");
|
||||
log("\n");
|
||||
log("Assigns auto-generated names to all $any*/$all* output wires and containing\n");
|
||||
log("cells that do not have a public name. This ensures that, during formal\n");
|
||||
log("verification, a solver-found trace can be fully specified using a public\n");
|
||||
log("hierarchical names.\n");
|
||||
log("Assigns auto-generated names to objects used in formal verification\n");
|
||||
log("that do not have a public name. This applies to all formal property\n");
|
||||
log("cells ($assert, $assume, $cover, $live, $fair, $check), $any*/$all*\n");
|
||||
log("output wires, and their containing cells.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -hide [selection]\n");
|
||||
|
|
@ -519,7 +518,7 @@ struct RenamePass : public Pass {
|
|||
if (module == nullptr)
|
||||
log_cmd_error("No top module found!\n");
|
||||
|
||||
log("Renaming module %s to %s.\n", log_id(module), log_id(new_name));
|
||||
log("Renaming module %s to %s.\n", module, new_name.unescape());
|
||||
design->rename(module, new_name);
|
||||
}
|
||||
else
|
||||
|
|
@ -533,7 +532,7 @@ struct RenamePass : public Pass {
|
|||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (module->memories.size() != 0 || module->processes.size() != 0) {
|
||||
log_warning("Skipping module %s with unprocessed memories or processes\n", log_id(module));
|
||||
log_warning("Skipping module %s with unprocessed memories or processes\n", module);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -622,7 +621,7 @@ struct RenamePass : public Pass {
|
|||
|
||||
RTLIL::Module *module_to_rename = nullptr;
|
||||
for (auto module : design->modules())
|
||||
if (module->name == from_name || RTLIL::unescape_id(module->name) == from_name) {
|
||||
if (module->name == from_name || module->name.unescape() == from_name) {
|
||||
module_to_rename = module;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ struct SccWorker
|
|||
RTLIL::Cell *c = cellStack.back();
|
||||
cellStack.pop_back();
|
||||
cellsOnStack.erase(c);
|
||||
log(" %s", RTLIL::id2cstr(c->name));
|
||||
log(" %s", c);
|
||||
cell2scc[c] = sccList.size();
|
||||
scc.insert(c);
|
||||
}
|
||||
|
|
@ -201,7 +201,7 @@ struct SccWorker
|
|||
if (!nofeedbackMode && cellToNextCell[cell].count(cell)) {
|
||||
log("Found an SCC:");
|
||||
pool<RTLIL::Cell*> scc;
|
||||
log(" %s", RTLIL::id2cstr(cell->name));
|
||||
log(" %s", cell);
|
||||
cell2scc[cell] = sccList.size();
|
||||
scc.insert(cell);
|
||||
sccList.push_back(scc);
|
||||
|
|
@ -221,7 +221,7 @@ struct SccWorker
|
|||
run(cell, 0, maxDepth);
|
||||
}
|
||||
|
||||
log("Found %d SCCs in module %s.\n", int(sccList.size()), RTLIL::id2cstr(module->name));
|
||||
log("Found %d SCCs in module %s.\n", int(sccList.size()), module);
|
||||
}
|
||||
|
||||
void select(RTLIL::Selection &sel)
|
||||
|
|
|
|||
9
passes/cmds/sdc/CMakeLists.txt
Normal file
9
passes/cmds/sdc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
yosys_pass(sdc
|
||||
sdc.cc
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
|
||||
DATA_DIR
|
||||
sdc
|
||||
DATA_FILES
|
||||
graph-stubs.sdc
|
||||
)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
OBJS += passes/cmds/sdc/sdc.o
|
||||
|
||||
$(eval $(call add_share_file,share/sdc,passes/cmds/sdc/graph-stubs.sdc))
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include "kernel/yosys_config.h"
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
|
||||
#include "kernel/register.h"
|
||||
|
|
@ -148,7 +149,7 @@ struct SdcObjects {
|
|||
path += "/";
|
||||
path += name;
|
||||
design_cells.push_back(std::make_pair(path, cell));
|
||||
for (auto pin : cell->connections()) {
|
||||
for (auto& pin : cell->connections()) {
|
||||
IdString pin_name = pin.first;
|
||||
std::string pin_name_sdc = path + "/" + pin.first.str().substr(1);
|
||||
design_pins.push_back(std::make_pair(pin_name_sdc, std::make_pair(cell, pin_name)));
|
||||
|
|
@ -168,7 +169,7 @@ struct SdcObjects {
|
|||
RTLIL::Wire *wire = top->wire(port);
|
||||
if (!wire) {
|
||||
// This should not be possible. See https://github.com/YosysHQ/yosys/pull/5594#issue-3791198573
|
||||
log_error("Port %s doesn't exist", log_id(port));
|
||||
log_error("Port %s doesn't exist", port.unescape());
|
||||
}
|
||||
design_ports.push_back(std::make_pair(port.str().substr(1), wire));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,15 +18,13 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log_help.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
using RTLIL::id2cstr;
|
||||
|
||||
static std::vector<RTLIL::Selection> work_stack;
|
||||
|
||||
static bool match_ids(RTLIL::IdString id, const std::string &pattern)
|
||||
|
|
@ -488,7 +486,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, const std::string
|
|||
}
|
||||
}
|
||||
|
||||
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)
|
||||
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, NewCellTypes &ct, bool eval_only)
|
||||
{
|
||||
int sel_objects = 0;
|
||||
bool is_input, is_output;
|
||||
|
|
@ -564,7 +562,7 @@ static void select_op_expand(RTLIL::Design *design, const std::string &arg, char
|
|||
std::vector<expand_rule_t> rules;
|
||||
std::set<RTLIL::IdString> limits;
|
||||
|
||||
CellTypes ct;
|
||||
NewCellTypes ct;
|
||||
|
||||
if (mode != 'x')
|
||||
ct.setup(design);
|
||||
|
|
@ -1022,9 +1020,9 @@ static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::S
|
|||
for (auto mod : design->all_selected_modules())
|
||||
{
|
||||
if (whole_modules && sel->selected_whole_module(mod->name))
|
||||
desc += stringf("%s\n", id2cstr(mod->name));
|
||||
desc += stringf("%s\n", mod);
|
||||
for (auto it : mod->selected_members())
|
||||
desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it->name));
|
||||
desc += stringf("%s/%s\n", mod, it);
|
||||
}
|
||||
if (push_selection) design->pop_selection();
|
||||
return desc;
|
||||
|
|
@ -1414,7 +1412,7 @@ struct SelectPass : public Pass {
|
|||
if (arg == "-module" && argidx+1 < args.size()) {
|
||||
RTLIL::IdString mod_name = RTLIL::escape_id(args[++argidx]);
|
||||
if (design->module(mod_name) == nullptr)
|
||||
log_cmd_error("No such module: %s\n", id2cstr(mod_name));
|
||||
log_cmd_error("No such module: %s\n", mod_name.unescape());
|
||||
design->selected_active_module = mod_name.str();
|
||||
got_module = true;
|
||||
continue;
|
||||
|
|
@ -1527,10 +1525,10 @@ struct SelectPass : public Pass {
|
|||
for (auto mod : design->all_selected_modules())
|
||||
{
|
||||
if (sel->selected_whole_module(mod->name) && list_mode)
|
||||
log("%s\n", id2cstr(mod->name));
|
||||
log("%s\n", mod);
|
||||
if (!list_mod_mode)
|
||||
for (auto it : mod->selected_members())
|
||||
LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it->name))
|
||||
LOG_OBJECT("%s/%s\n", mod->name.unescape().c_str(), it->name.unescape().c_str())
|
||||
}
|
||||
if (count_mode)
|
||||
{
|
||||
|
|
@ -1654,10 +1652,10 @@ struct SelectPass : public Pass {
|
|||
if (sel.full_selection)
|
||||
log("*\n");
|
||||
for (auto &it : sel.selected_modules)
|
||||
log("%s\n", id2cstr(it));
|
||||
log("%s\n", it.unescape());
|
||||
for (auto &it : sel.selected_members)
|
||||
for (auto &it2 : it.second)
|
||||
log("%s/%s\n", id2cstr(it.first), id2cstr(it2));
|
||||
log("%s/%s\n", it.first.unescape(), it2.unescape());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1779,7 +1777,7 @@ static void log_matches(const char *title, Module *module, const T &list)
|
|||
log("\n%d %s:\n", int(matches.size()), title);
|
||||
std::sort(matches.begin(), matches.end(), RTLIL::sort_by_id_str());
|
||||
for (auto id : matches)
|
||||
log(" %s\n", RTLIL::id2cstr(id));
|
||||
log(" %s\n", id.unescape());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1817,7 +1815,7 @@ struct LsPass : public Pass {
|
|||
log("\n%d %s:\n", int(matches.size()), "modules");
|
||||
std::sort(matches.begin(), matches.end(), RTLIL::sort_by_id_str());
|
||||
for (auto id : matches)
|
||||
log(" %s%s\n", log_id(id), design->selected_whole_module(design->module(id)) ? "" : "*");
|
||||
log(" %s%s\n", id.unescape(), design->selected_whole_module(design->module(id)) ? "" : "*");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -246,9 +246,9 @@ struct ChparamPass : public Pass {
|
|||
if (!new_parameters.empty())
|
||||
log_cmd_error("The options -set and -list cannot be used together.\n");
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("%s:\n", log_id(module));
|
||||
log("%s:\n", module);
|
||||
for (auto param : module->avail_parameters)
|
||||
log(" %s\n", log_id(param));
|
||||
log(" %s\n", param.unescape());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,6 +310,8 @@ struct SetundefPass : public Pass {
|
|||
|
||||
RTLIL::SigSpec sig = undriven_signals.export_all();
|
||||
for (auto &c : sig.chunks()) {
|
||||
if (!design->selected(module, c.wire))
|
||||
continue;
|
||||
RTLIL::Wire * wire;
|
||||
if (c.wire->width == c.width) {
|
||||
wire = c.wire;
|
||||
|
|
@ -328,12 +330,12 @@ struct SetundefPass : public Pass {
|
|||
SigMap sigmap(module);
|
||||
SigPool undriven_signals;
|
||||
|
||||
for (auto &it : module->wires_)
|
||||
undriven_signals.add(sigmap(it.second));
|
||||
for (auto wire : module->selected_wires())
|
||||
undriven_signals.add(sigmap(wire));
|
||||
|
||||
for (auto &it : module->wires_)
|
||||
if (it.second->port_input)
|
||||
undriven_signals.del(sigmap(it.second));
|
||||
for (auto wire : module->selected_wires())
|
||||
if (wire->port_input)
|
||||
undriven_signals.del(sigmap(wire));
|
||||
|
||||
CellTypes ct(design);
|
||||
for (auto &it : module->cells_)
|
||||
|
|
@ -367,6 +369,14 @@ struct SetundefPass : public Pass {
|
|||
if (!cell->is_builtin_ff())
|
||||
continue;
|
||||
|
||||
bool cell_selected = design->selected(module, cell);
|
||||
bool wire_selected = false;
|
||||
for (auto bit : sigmap(cell->getPort(ID::Q)))
|
||||
if (bit.wire && design->selected(module, bit.wire))
|
||||
wire_selected = true;
|
||||
if (!cell_selected && !wire_selected)
|
||||
continue;
|
||||
|
||||
for (auto bit : sigmap(cell->getPort(ID::Q)))
|
||||
ffbits.insert(bit);
|
||||
}
|
||||
|
|
@ -502,14 +512,21 @@ struct SetundefPass : public Pass {
|
|||
}
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_)
|
||||
if (!it.second->get_bool_attribute(ID::xprop_decoder))
|
||||
it.second->rewrite_sigspecs(worker);
|
||||
for (auto &it : module->processes)
|
||||
it.second->rewrite_sigspecs(worker);
|
||||
for (auto cell : module->selected_cells())
|
||||
if (!cell->get_bool_attribute(ID::xprop_decoder))
|
||||
cell->rewrite_sigspecs(worker);
|
||||
for (auto proc : module->selected_processes())
|
||||
proc->rewrite_sigspecs(worker);
|
||||
for (auto &it : module->connections_) {
|
||||
worker(it.first);
|
||||
worker(it.second);
|
||||
SigSpec lhs = it.first;
|
||||
bool selected = false;
|
||||
for (auto &chunk : lhs.chunks())
|
||||
if (chunk.wire && module->design->selected(module, chunk.wire))
|
||||
selected = true;
|
||||
if (selected) {
|
||||
worker(it.first);
|
||||
worker(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ struct ShowWorker
|
|||
id2num(wire->name), shape.c_str(), is_borderless? " margin=0, width=0" : "", findLabel(wire->name.str()),
|
||||
is_borderless
|
||||
? "color=\"none\", fontcolor=\"black\""
|
||||
: nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str(),
|
||||
: nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str(),
|
||||
src_href.c_str());
|
||||
if (wire->port_input)
|
||||
all_sources.insert(stringf("n%d", id2num(wire->name)));
|
||||
|
|
@ -549,7 +549,7 @@ struct ShowWorker
|
|||
net_conn_map[node].color = nextColor(sig, net_conn_map[node].color);
|
||||
}
|
||||
|
||||
std::string proc_src = RTLIL::unescape_id(proc->name);
|
||||
std::string proc_src = proc->name.unescape();
|
||||
if (proc->attributes.count(ID::src) > 0)
|
||||
proc_src = proc->attributes.at(ID::src).decode_string();
|
||||
fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\", %s];\n", pidx, findLabel(proc->name.str()), proc_src.c_str(), findColor(proc->name).c_str());
|
||||
|
|
@ -645,16 +645,16 @@ struct ShowWorker
|
|||
module = mod;
|
||||
if (design->selected_whole_module(module->name)) {
|
||||
if (module->get_blackbox_attribute()) {
|
||||
// log("Skipping blackbox module %s.\n", log_id(module->name));
|
||||
//log("Skipping blackbox module %s.\n", module->name.unescape());
|
||||
continue;
|
||||
} else
|
||||
if (module->cells().size() == 0 && module->connections().empty() && module->processes.empty()) {
|
||||
log("Skipping empty module %s.\n", log_id(module->name));
|
||||
log("Skipping empty module %s.\n", module->name.unescape());
|
||||
continue;
|
||||
} else
|
||||
log("Dumping module %s to page %d.\n", log_id(module->name), ++page_counter);
|
||||
log("Dumping module %s to page %d.\n", module->name.unescape(), ++page_counter);
|
||||
} else
|
||||
log("Dumping selected parts of module %s to page %d.\n", log_id(module->name), ++page_counter);
|
||||
log("Dumping selected parts of module %s to page %d.\n", module->name.unescape(), ++page_counter);
|
||||
handle_module();
|
||||
}
|
||||
}
|
||||
|
|
@ -770,7 +770,7 @@ struct ShowPass : public Pass {
|
|||
std::vector<std::pair<std::string, RTLIL::Selection>> color_selections;
|
||||
std::vector<std::pair<std::string, RTLIL::Selection>> label_selections;
|
||||
|
||||
#if defined(_WIN32) || defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(_WIN32) || !defined(YOSYS_ENABLE_SPAWN)
|
||||
std::string format = "dot";
|
||||
std::string prefix = "show";
|
||||
#else
|
||||
|
|
@ -948,13 +948,13 @@ struct ShowPass : public Pass {
|
|||
std::string cmd = stringf(DOT_CMD, format, dot_file, out_file, out_file, out_file);
|
||||
#undef DOT_CMD
|
||||
log("Exec: %s\n", cmd);
|
||||
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(YOSYS_ENABLE_SPAWN)
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(YOSYS_DISABLE_SPAWN)
|
||||
#if !defined(YOSYS_ENABLE_SPAWN)
|
||||
log_assert(viewer_exe.empty() && !format.empty());
|
||||
#else
|
||||
if (viewer_exe != "none") {
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ struct SpliceWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
log("Splicing signals in module %s:\n", log_id(module->name));
|
||||
log("Splicing signals in module %s:\n", module->name.unescape());
|
||||
|
||||
driven_bits.push_back(RTLIL::State::Sm);
|
||||
driven_bits.push_back(RTLIL::State::Sm);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ struct SplitcellsWorker
|
|||
}
|
||||
slices.push_back(GetSize(outsig));
|
||||
|
||||
log_debug("Splitting %s cell %s/%s into %d slices:\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)-1);
|
||||
log_debug("Splitting %s cell %s/%s into %d slices:\n", cell->type.unescape(), module, cell, GetSize(slices)-1);
|
||||
for (int i = 1; i < GetSize(slices); i++)
|
||||
{
|
||||
int slice_msb = slices[i]-1;
|
||||
|
|
@ -130,7 +130,7 @@ struct SplitcellsWorker
|
|||
if (slice->hasParam(ID::WIDTH))
|
||||
slice->setParam(ID::WIDTH, GetSize(slice->getPort(ID::Y)));
|
||||
|
||||
log_debug(" slice %d: %s => %s\n", i, log_id(slice_name), log_signal(slice->getPort(ID::Y)));
|
||||
log_debug(" slice %d: %s => %s\n", i, slice_name, log_signal(slice->getPort(ID::Y)));
|
||||
}
|
||||
|
||||
module->remove(cell);
|
||||
|
|
@ -176,8 +176,7 @@ struct SplitcellsWorker
|
|||
}
|
||||
slices.push_back(GetSize(outsig));
|
||||
|
||||
log_debug("Splitting %s cell %s/%s into %d slices:\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)-1);
|
||||
int wire_offset = user_index(0);
|
||||
log_debug("Splitting %s cell %s/%s into %d slices:\n", cell->type.unescape(), module, cell, GetSize(slices)-1);
|
||||
for (int i = 1; i < GetSize(slices); i++)
|
||||
{
|
||||
int slice_msb = slices[i]-1;
|
||||
|
|
@ -220,7 +219,7 @@ struct SplitcellsWorker
|
|||
"%c%d%c", format[0], bit_offset, format[1]);
|
||||
} else { // no '[' or '.', so no concatenation using wire, use slice_lsb + name_lsb offset instead
|
||||
wire_indices = stringf(
|
||||
"%c%d%c", format[0], slice_lsb + wire_offset, format[1]);
|
||||
"%c%d%c", format[0], slice_lsb + user_index(0), format[1]);
|
||||
}
|
||||
} else {
|
||||
// Fallback
|
||||
|
|
@ -255,7 +254,7 @@ struct SplitcellsWorker
|
|||
|
||||
slice->setParam(ID::WIDTH, GetSize(slice->getPort(ID::Q)));
|
||||
|
||||
log_debug(" slice %d: %s => %s\n", i, log_id(slice_name), log_signal(slice->getPort(ID::Q)));
|
||||
log_debug(" slice %d: %s => %s\n", i, slice_name.unescape(), log_signal(slice->getPort(ID::Q)));
|
||||
}
|
||||
|
||||
module->remove(cell);
|
||||
|
|
@ -344,7 +343,7 @@ struct SplitcellsPass : public Pass {
|
|||
|
||||
if (count_split_pre)
|
||||
log("Split %d cells in module %s into %d cell slices.\n",
|
||||
count_split_pre, log_id(module), count_split_post);
|
||||
count_split_pre, module, count_split_post);
|
||||
}
|
||||
}
|
||||
} SplitnetsPass;
|
||||
|
|
|
|||
|
|
@ -66,12 +66,12 @@ struct StaWorker
|
|||
Module *inst_module = design->module(cell->type);
|
||||
if (!inst_module) {
|
||||
if (unrecognised_cells.insert(cell->type).second)
|
||||
log_warning("Cell type '%s' not recognised! Ignoring.\n", log_id(cell->type));
|
||||
log_warning("Cell type '%s' not recognised! Ignoring.\n", cell->type.unescape());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inst_module->get_blackbox_attribute()) {
|
||||
log_warning("Cell type '%s' is not a black- nor white-box! Ignoring.\n", log_id(cell->type));
|
||||
log_warning("Cell type '%s' is not a black- nor white-box! Ignoring.\n", cell->type.unescape());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ struct StaWorker
|
|||
if (!timing.count(derived_type)) {
|
||||
auto &t = timing.setup_module(inst_module);
|
||||
if (t.has_inputs && t.comb.empty() && t.arrival.empty() && t.required.empty())
|
||||
log_warning("Module '%s' has no timing arcs!\n", log_id(cell->type));
|
||||
log_warning("Module '%s' has no timing arcs!\n", cell->type.unescape());
|
||||
}
|
||||
|
||||
auto &t = timing.at(derived_type);
|
||||
|
|
@ -203,10 +203,10 @@ struct StaWorker
|
|||
return;
|
||||
}
|
||||
|
||||
log("Latest arrival time in '%s' is %d:\n", log_id(module), maxarrival);
|
||||
log("Latest arrival time in '%s' is %d:\n", module, maxarrival);
|
||||
auto it = endpoints.find(maxbit);
|
||||
if (it != endpoints.end() && it->second.sink)
|
||||
log(" %6d %s (%s.%s)\n", maxarrival, log_id(it->second.sink), log_id(it->second.sink->type), log_id(it->second.port));
|
||||
log(" %6d %s (%s.%s)\n", maxarrival, it->second.sink, it->second.sink->type.unescape(), it->second.port.unescape());
|
||||
else {
|
||||
log(" %6d (%s)\n", maxarrival, b.wire->port_output ? "<primary output>" : "<unknown>");
|
||||
if (!b.wire->port_output)
|
||||
|
|
@ -217,7 +217,7 @@ struct StaWorker
|
|||
int arrival = b.wire->get_intvec_attribute(ID::sta_arrival)[b.offset];
|
||||
if (jt->second.driver) {
|
||||
log(" %s\n", log_signal(b));
|
||||
log(" %6d %s (%s.%s->%s)\n", arrival, log_id(jt->second.driver), log_id(jt->second.driver->type), log_id(jt->second.src_port), log_id(jt->second.dst_port));
|
||||
log(" %6d %s (%s.%s->%s)\n", arrival, jt->second.driver, jt->second.driver->type.unescape(), jt->second.src_port.unescape(), jt->second.dst_port.unescape());
|
||||
}
|
||||
else if (b.wire->port_input)
|
||||
log(" %6d %s (%s)\n", arrival, log_signal(b), "<primary input>");
|
||||
|
|
@ -234,13 +234,13 @@ struct StaWorker
|
|||
continue;
|
||||
|
||||
if (!b.wire->attributes.count(ID::sta_arrival)) {
|
||||
log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", log_id(module), log_signal(b));
|
||||
log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", module, log_signal(b));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto arrival = b.wire->get_intvec_attribute(ID::sta_arrival)[b.offset];
|
||||
if (arrival < 0) {
|
||||
log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", log_id(module), log_signal(b));
|
||||
log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", module, log_signal(b));
|
||||
continue;
|
||||
}
|
||||
arrival += i.second.required;
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ struct statdata_t
|
|||
log(" Number of cells: %6u\n", num_cells);
|
||||
for (auto &it : num_cells_by_type)
|
||||
if (it.second)
|
||||
log(" %-26s %6u\n", log_id(it.first), it.second);
|
||||
log(" %-26s %6u\n", it.first.unescape(), it.second);
|
||||
|
||||
if (!unknown_cell_area.empty()) {
|
||||
log("\n");
|
||||
|
|
@ -267,7 +267,7 @@ struct statdata_t
|
|||
log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0*sequential_area/area);
|
||||
}
|
||||
|
||||
if (tech == "xilinx")
|
||||
if (tech == "xilinx" || tech == "analogdevices")
|
||||
{
|
||||
log("\n");
|
||||
log(" Estimated number of LCs: %10u\n", estimate_xilinx_lc());
|
||||
|
|
@ -305,12 +305,12 @@ struct statdata_t
|
|||
if (it.second) {
|
||||
if (!first_line)
|
||||
log(",\n");
|
||||
log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second);
|
||||
log(" %s: %u", json11::Json(it.first.unescape()).dump().c_str(), it.second);
|
||||
first_line = false;
|
||||
}
|
||||
log("\n");
|
||||
log(" }");
|
||||
if (tech == "xilinx")
|
||||
if (tech == "xilinx" || tech == "analogdevices")
|
||||
{
|
||||
log(",\n");
|
||||
log(" \"estimated_num_lc\": %u", estimate_xilinx_lc());
|
||||
|
|
@ -336,7 +336,7 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL
|
|||
for (auto &it : num_cells_by_type)
|
||||
if (mod_stat.count(it.first) > 0) {
|
||||
if (!quiet)
|
||||
log(" %*s%-*s %6u\n", 2*level, "", 26-2*level, log_id(it.first), it.second);
|
||||
log(" %*s%-*s %6u\n", 2*level, "", 26-2*level, it.first.unescape(), it.second);
|
||||
mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1, quiet) * it.second;
|
||||
mod_data.num_cells -= it.second;
|
||||
} else {
|
||||
|
|
@ -435,7 +435,7 @@ struct StatPass : public Pass {
|
|||
log("\n");
|
||||
log(" -tech <technology>\n");
|
||||
log(" print area estimate for the specified technology. Currently supported\n");
|
||||
log(" values for <technology>: xilinx, cmos\n");
|
||||
log(" values for <technology>: xilinx, analogdevices, cmos\n");
|
||||
log("\n");
|
||||
log(" -width\n");
|
||||
log(" annotate internal cell types with their word width.\n");
|
||||
|
|
@ -494,7 +494,7 @@ struct StatPass : public Pass {
|
|||
if(!json_mode)
|
||||
log_header(design, "Printing statistics.\n");
|
||||
|
||||
if (techname != "" && techname != "xilinx" && techname != "cmos" && !json_mode)
|
||||
if (techname != "" && techname != "xilinx" && techname != "analogdevices" && techname != "cmos" && !json_mode)
|
||||
log_cmd_error("Unsupported technology: '%s'\n", techname);
|
||||
|
||||
if (json_mode) {
|
||||
|
|
@ -521,7 +521,7 @@ struct StatPass : public Pass {
|
|||
first_module = false;
|
||||
} else {
|
||||
log("\n");
|
||||
log("=== %s%s ===\n", log_id(mod->name), mod->is_selected_whole() ? "" : " (partially selected)");
|
||||
log("=== %s%s ===\n", mod->name.unescape(), mod->is_selected_whole() ? "" : " (partially selected)");
|
||||
log("\n");
|
||||
data.log_data(mod->name, false);
|
||||
}
|
||||
|
|
@ -538,7 +538,7 @@ struct StatPass : public Pass {
|
|||
log("\n");
|
||||
log("=== design hierarchy ===\n");
|
||||
log("\n");
|
||||
log(" %-28s %6d\n", log_id(top_mod->name), 1);
|
||||
log(" %-28s %6d\n", top_mod->name.unescape(), 1);
|
||||
}
|
||||
|
||||
statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode);
|
||||
|
|
|
|||
|
|
@ -144,10 +144,10 @@ struct TestSelectPass : public Pass {
|
|||
|
||||
for (auto *mod : sub_sel) {
|
||||
if (mod->is_selected_whole()) {
|
||||
log_debug(" Adding %s.\n", id2cstr(mod->name));
|
||||
log_debug(" Adding %s.\n", mod);
|
||||
selected_modules.insert(mod->name);
|
||||
} else for (auto *memb : mod->selected_members()) {
|
||||
log_debug(" Adding %s.%s.\n", id2cstr(mod->name), id2cstr(memb->name));
|
||||
log_debug(" Adding %s.%s.\n", mod, memb);
|
||||
selected_members[mod->name].insert(memb);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ struct EstimateSta {
|
|||
|
||||
void run()
|
||||
{
|
||||
log("\nModule %s\n", log_id(m));
|
||||
log("\nModule %s\n", m);
|
||||
if (clk.has_value())
|
||||
log("Domain %s\n", log_signal(*clk));
|
||||
|
||||
|
|
@ -97,10 +97,10 @@ struct EstimateSta {
|
|||
FfData ff(nullptr, cell);
|
||||
if (!ff.has_clk) {
|
||||
log_warning("Ignoring unsupported storage element '%s' (%s)\n",
|
||||
log_id(cell), log_id(cell->type));
|
||||
cell, cell->type.unescape());
|
||||
continue;
|
||||
}
|
||||
if (ff.sig_clk != clk)
|
||||
if (!clk || ff.sig_clk.as_bit() != *clk)
|
||||
continue;
|
||||
launch.append(ff.sig_q);
|
||||
sample.append(ff.sig_d);
|
||||
|
|
@ -121,7 +121,7 @@ struct EstimateSta {
|
|||
aigs.emplace(fingerprint, Aig(cell));
|
||||
if (aigs.at(fingerprint).name.empty()) {
|
||||
log_error("Unsupported cell '%s' in module '%s'",
|
||||
log_id(cell->type), log_id(m));
|
||||
cell->type.unescape(), m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,15 +141,15 @@ struct EstimateSta {
|
|||
for (auto &mem : Mem::get_all_memories(m)) {
|
||||
for (auto &rd : mem.rd_ports) {
|
||||
if (!rd.clk_enable) {
|
||||
log_error("Unsupported async memory port '%s'\n", log_id(rd.cell));
|
||||
log_error("Unsupported async memory port '%s'\n", rd.cell);
|
||||
continue;
|
||||
}
|
||||
if (sigmap(rd.clk) != clk)
|
||||
if (!clk || sigmap(rd.clk).as_bit() != *clk)
|
||||
continue;
|
||||
add_seq(rd.cell, rd.data, {rd.addr, rd.srst, rd.en});
|
||||
}
|
||||
for (auto &wr : mem.wr_ports) {
|
||||
if (sigmap(wr.clk) != clk)
|
||||
if (!clk || sigmap(wr.clk).as_bit() != *clk)
|
||||
continue;
|
||||
add_seq(wr.cell, {}, {wr.en, wr.addr, wr.data});
|
||||
}
|
||||
|
|
@ -165,7 +165,7 @@ struct EstimateSta {
|
|||
} else if (port->port_output && !port->port_input) {
|
||||
all_outputs.append(port);
|
||||
} else if (port->port_output && port->port_input) {
|
||||
log_warning("Ignoring bi-directional port %s\n", log_id(port));
|
||||
log_warning("Ignoring bi-directional port %s\n", port);
|
||||
}
|
||||
}
|
||||
add_seq(nullptr, all_inputs, all_outputs);
|
||||
|
|
@ -216,7 +216,7 @@ struct EstimateSta {
|
|||
}
|
||||
|
||||
if (!topo.sort())
|
||||
log_error("Module '%s' contains combinational loops", log_id(m));
|
||||
log_error("Module '%s' contains combinational loops", m);
|
||||
|
||||
// now we determine how long it takes for signals to stabilize
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ struct EstimateSta {
|
|||
std::string src_attr = cell->get_src_attribute();
|
||||
cell_src = stringf(" source: %s", src_attr);
|
||||
}
|
||||
log(" cell %s (%s)%s\n", log_id(cell), log_id(cell->type), cell_src);
|
||||
log(" cell %s (%s)%s\n", cell, cell->type.unescape(), cell_src);
|
||||
printed.insert(cell);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -425,7 +425,7 @@ struct TimeestPass : Pass {
|
|||
|
||||
if (clk_domain_specified) {
|
||||
if (!m->wire(RTLIL::escape_id(clk_name))) {
|
||||
log_warning("No domain '%s' in module %s\n", clk_name.c_str(), log_id(m));
|
||||
log_warning("No domain '%s' in module %s\n", clk_name.c_str(), m);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/log_help.h"
|
||||
|
|
@ -74,7 +74,7 @@ struct TorderPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
log("module %s\n", log_id(module));
|
||||
log("module %s\n", module);
|
||||
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
||||
|
|
@ -116,12 +116,12 @@ struct TorderPass : public Pass {
|
|||
for (auto &it : toposort.loops) {
|
||||
log(" loop");
|
||||
for (auto cell : it)
|
||||
log(" %s", log_id(cell));
|
||||
log(" %s", cell);
|
||||
log("\n");
|
||||
}
|
||||
|
||||
for (auto cell : toposort.sorted)
|
||||
log(" cell %s\n", log_id(cell));
|
||||
log(" cell %s\n", cell);
|
||||
}
|
||||
}
|
||||
} TorderPass;
|
||||
|
|
|
|||
|
|
@ -28,34 +28,34 @@ struct TraceMonitor : public RTLIL::Monitor
|
|||
{
|
||||
void notify_module_add(RTLIL::Module *module) override
|
||||
{
|
||||
log("#TRACE# Module add: %s\n", log_id(module));
|
||||
log("#TRACE# Module add: %s\n", module);
|
||||
}
|
||||
|
||||
void notify_module_del(RTLIL::Module *module) override
|
||||
{
|
||||
log("#TRACE# Module delete: %s\n", log_id(module));
|
||||
log("#TRACE# Module delete: %s\n", module);
|
||||
}
|
||||
|
||||
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
|
||||
{
|
||||
log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig));
|
||||
log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", cell->module, cell, port.unescape(), log_signal(sig), log_signal(old_sig));
|
||||
}
|
||||
|
||||
void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) override
|
||||
{
|
||||
log("#TRACE# Connection in module %s: %s = %s\n", log_id(module), log_signal(sigsig.first), log_signal(sigsig.second));
|
||||
log("#TRACE# Connection in module %s: %s = %s\n", module, log_signal(sigsig.first), log_signal(sigsig.second));
|
||||
}
|
||||
|
||||
void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) override
|
||||
{
|
||||
log("#TRACE# New connections in module %s:\n", log_id(module));
|
||||
log("#TRACE# New connections in module %s:\n", module);
|
||||
for (auto &sigsig : sigsig_vec)
|
||||
log("## %s = %s\n", log_signal(sigsig.first), log_signal(sigsig.second));
|
||||
}
|
||||
|
||||
void notify_blackout(RTLIL::Module *module) override
|
||||
{
|
||||
log("#TRACE# Blackout in module %s:\n", log_id(module));
|
||||
log("#TRACE# Blackout in module %s:\n", module);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ struct Graph {
|
|||
|
||||
Graph(Module *module, const VizConfig &config) : module(module), config(config)
|
||||
{
|
||||
log("Running 'viz -%d' for module %s:\n", config.effort, log_id(module));
|
||||
log("Running 'viz -%d' for module %s:\n", config.effort, module);
|
||||
log(" Phase %d: Construct initial graph\n", phase_counter++);
|
||||
|
||||
SigMap sigmap(module);
|
||||
|
|
@ -718,7 +718,7 @@ struct VizWorker
|
|||
|
||||
void write_dot(FILE *f)
|
||||
{
|
||||
fprintf(f, "digraph \"%s\" {\n", log_id(module));
|
||||
fprintf(f, "digraph \"%s\" {\n", module->name.unescape().c_str());
|
||||
fprintf(f, " rankdir = LR;\n");
|
||||
|
||||
dict<GraphNode*, std::vector<std::vector<std::string>>> extra_lines;
|
||||
|
|
@ -734,7 +734,7 @@ struct VizWorker
|
|||
buffer.emplace_back();
|
||||
|
||||
for (auto name : g->names())
|
||||
buffer.back().push_back(log_id(name));
|
||||
buffer.back().push_back(name.unescape());
|
||||
|
||||
std::sort(buffer.back().begin(), buffer.back().end());
|
||||
std::sort(buffer.begin(), buffer.end());
|
||||
|
|
@ -782,7 +782,7 @@ struct VizWorker
|
|||
g->names().sort();
|
||||
std::string label; // = stringf("vg=%d\\n", g->index);
|
||||
for (auto n : g->names())
|
||||
label = label + (label.empty() ? "" : "\\n") + log_id(n);
|
||||
label = label + (label.empty() ? "" : "\\n") + n.unescape();
|
||||
fprintf(f, "\tn%d [shape=rectangle,label=\"%s\"];\n", g->index, label.c_str());
|
||||
} else {
|
||||
std::string label = stringf("vg=%d | %d cells", g->index, GetSize(g->names()));
|
||||
|
|
@ -898,7 +898,7 @@ struct VizPass : public Pass {
|
|||
log_header(design, "Generating Graphviz representation of design.\n");
|
||||
log_push();
|
||||
|
||||
#if defined(_WIN32) || defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(_WIN32) || !defined(YOSYS_ENABLE_SPAWN)
|
||||
std::string format = "dot";
|
||||
std::string prefix = "show";
|
||||
#else
|
||||
|
|
@ -1029,13 +1029,13 @@ struct VizPass : public Pass {
|
|||
std::string cmd = stringf(DOT_CMD, format, dot_file, out_file, out_file, out_file);
|
||||
#undef DOT_CMD
|
||||
log("Exec: %s\n", cmd);
|
||||
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(YOSYS_ENABLE_SPAWN)
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(YOSYS_DISABLE_SPAWN)
|
||||
#if !defined(YOSYS_ENABLE_SPAWN)
|
||||
log_assert(viewer_exe.empty() && !format.empty());
|
||||
#else
|
||||
if (!viewer_exe.empty()) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ std::optional<std::string> format_with_params(std::string fmt, const dict<IdStri
|
|||
} else {
|
||||
auto id = RTLIL::escape_id(std::string(beg, it));
|
||||
if (!parameters.count(id)) {
|
||||
log("Parameter %s referenced in format string '%s' not found\n", log_id(id), fmt);
|
||||
log("Parameter %s referenced in format string '%s' not found\n", id, fmt);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ struct WrapcellPass : Pass {
|
|||
|
||||
if (!ct.cell_known(cell->type))
|
||||
log_error("Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n",
|
||||
log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->type.unescape(), cell, module);
|
||||
|
||||
std::vector<std::pair<IdString, int>> unused_outputs, used_outputs;
|
||||
for (auto conn : cell->connections()) {
|
||||
|
|
@ -227,13 +227,13 @@ struct WrapcellPass : Pass {
|
|||
if (!unused_outputs.empty()) {
|
||||
context.unused_outputs += "_unused";
|
||||
for (auto chunk : collect_chunks(unused_outputs))
|
||||
context.unused_outputs += "_" + RTLIL::unescape_id(chunk.format(cell));
|
||||
context.unused_outputs += "_" + chunk.format(cell).unescape();
|
||||
}
|
||||
|
||||
std::optional<std::string> unescaped_name = format_with_params(name_fmt, cell->parameters, context);
|
||||
if (!unescaped_name)
|
||||
log_error("Formatting error when processing cell '%s' in module '%s'\n",
|
||||
log_id(cell), log_id(module));
|
||||
cell, module);
|
||||
|
||||
IdString name = RTLIL::escape_id(unescaped_name.value());
|
||||
if (d->module(name))
|
||||
|
|
@ -274,7 +274,7 @@ struct WrapcellPass : Pass {
|
|||
|
||||
if (!value)
|
||||
log_error("Formatting error when processing cell '%s' in module '%s'\n",
|
||||
log_id(cell), log_id(module));
|
||||
cell, module);
|
||||
|
||||
subm->set_string_attribute(rule.name, value.value());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -463,7 +463,11 @@ struct XpropWorker
|
|||
return;
|
||||
}
|
||||
|
||||
log_warning("Unhandled cell %s (%s) during maybe-x marking\n", log_id(cell), log_id(cell->type));
|
||||
if (cell->type.in(ID($scopeinfo))) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_warning("Unhandled cell %s (%s) during maybe-x marking\n", cell, cell->type.unescape());
|
||||
mark_outputs_maybe_x(cell);
|
||||
}
|
||||
|
||||
|
|
@ -858,7 +862,7 @@ struct XpropWorker
|
|||
|
||||
if ((ff.has_clk || ff.has_gclk) && !ff.has_ce && !ff.has_aload && !ff.has_srst && !ff.has_arst && !ff.has_sr) {
|
||||
if (ff.has_clk && maybe_x(ff.sig_clk)) {
|
||||
log_warning("Only non-x CLK inputs are currently supported for %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Only non-x CLK inputs are currently supported for %s (%s)\n", cell, cell->type.unescape());
|
||||
} else {
|
||||
auto init_q = ff.val_init;
|
||||
auto init_q_is_1 = init_q;
|
||||
|
|
@ -903,7 +907,7 @@ struct XpropWorker
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
log_warning("Unhandled FF-cell %s (%s), consider running clk2fflogic, async2sync and/or dffunmap\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Unhandled FF-cell %s (%s), consider running clk2fflogic, async2sync and/or dffunmap\n", cell, cell->type.unescape());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -960,9 +964,9 @@ struct XpropWorker
|
|||
log("Running 'demuxmap' preserves x-propagation and can be run before 'xprop'.\n");
|
||||
|
||||
if (options.required)
|
||||
log_error("Unhandled cell %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
log_error("Unhandled cell %s (%s)\n", cell, cell->type.unescape());
|
||||
else
|
||||
log_warning("Unhandled cell %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
log_warning("Unhandled cell %s (%s)\n", cell, cell->type.unescape());
|
||||
}
|
||||
|
||||
void split_ports()
|
||||
|
|
@ -976,7 +980,7 @@ struct XpropWorker
|
|||
auto wire = module->wire(port);
|
||||
if (module->design->selected(module, wire)) {
|
||||
if (wire->port_input == wire->port_output) {
|
||||
log_warning("Port %s not an input or an output port which is not supported by xprop\n", log_id(wire));
|
||||
log_warning("Port %s not an input or an output port which is not supported by xprop\n", wire);
|
||||
} else if ((options.split_inputs && !options.assume_def_inputs && wire->port_input) || (options.split_outputs && wire->port_output)) {
|
||||
auto port_d = module->uniquify(stringf("%s_d", port));
|
||||
auto port_x = module->uniquify(stringf("%s_x", port));
|
||||
|
|
|
|||
54
passes/equiv/CMakeLists.txt
Normal file
54
passes/equiv/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
yosys_pass(equiv_make
|
||||
equiv_make.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_miter
|
||||
equiv_miter.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_simple
|
||||
equiv_simple.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_status
|
||||
equiv_status.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_add
|
||||
equiv_add.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_remove
|
||||
equiv_remove.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_induct
|
||||
equiv_induct.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_struct
|
||||
equiv_struct.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_purge
|
||||
equiv_purge.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_mark
|
||||
equiv_mark.cc
|
||||
equiv.h
|
||||
)
|
||||
yosys_pass(equiv_opt
|
||||
equiv_opt.cc
|
||||
equiv.h
|
||||
REQUIRES
|
||||
async2sync
|
||||
check
|
||||
clk2fflogic
|
||||
design
|
||||
equiv_induct
|
||||
equiv_make
|
||||
equiv_status
|
||||
hierarchy
|
||||
techmap
|
||||
)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
OBJS += passes/equiv/equiv_make.o
|
||||
OBJS += passes/equiv/equiv_miter.o
|
||||
OBJS += passes/equiv/equiv_simple.o
|
||||
OBJS += passes/equiv/equiv_status.o
|
||||
OBJS += passes/equiv/equiv_add.o
|
||||
OBJS += passes/equiv/equiv_remove.o
|
||||
OBJS += passes/equiv/equiv_induct.o
|
||||
OBJS += passes/equiv/equiv_struct.o
|
||||
OBJS += passes/equiv/equiv_purge.o
|
||||
OBJS += passes/equiv/equiv_mark.o
|
||||
OBJS += passes/equiv/equiv_opt.o
|
||||
68
passes/equiv/equiv.h
Normal file
68
passes/equiv/equiv.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef EQUIV_H
|
||||
#define EQUIV_H
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivBasicConfig {
|
||||
bool model_undef = false;
|
||||
int max_seq = 1;
|
||||
bool set_assumes = false;
|
||||
bool ignore_unknown_cells = false;
|
||||
|
||||
bool parse(const std::vector<std::string>& args, size_t& idx) {
|
||||
if (args[idx] == "-undef") {
|
||||
model_undef = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-seq" && idx+1 < args.size()) {
|
||||
max_seq = atoi(args[++idx].c_str());
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-set-assumes") {
|
||||
set_assumes = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-ignore-unknown-cells") {
|
||||
ignore_unknown_cells = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static std::string help(const char* default_seq) {
|
||||
return stringf(
|
||||
" -undef\n"
|
||||
" enable modelling of undef states\n"
|
||||
"\n"
|
||||
" -seq <N>\n"
|
||||
" the max. number of time steps to be considered (default = %s)\n"
|
||||
"\n"
|
||||
" -set-assumes\n"
|
||||
" set all assumptions provided via $assume cells\n"
|
||||
"\n"
|
||||
" -ignore-unknown-cells\n"
|
||||
" ignore all cells that can not be matched to a SAT model\n"
|
||||
, default_seq);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Config = EquivBasicConfig>
|
||||
struct EquivWorker {
|
||||
RTLIL::Module *module;
|
||||
|
||||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
Config cfg;
|
||||
|
||||
EquivWorker(RTLIL::Module *module, const SigMap *sigmap, Config cfg) : module(module), satgen(ez.get(), sigmap), cfg(cfg) {
|
||||
satgen.model_undef = cfg.model_undef;
|
||||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
#endif // EQUIV_H
|
||||
|
|
@ -18,49 +18,34 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "passes/equiv/equiv.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivInductWorker
|
||||
struct EquivInductWorker : public EquivWorker<>
|
||||
{
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
|
||||
vector<Cell*> cells;
|
||||
pool<Cell*> workset;
|
||||
|
||||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
|
||||
int max_seq;
|
||||
int success_counter;
|
||||
bool set_assumes;
|
||||
|
||||
dict<int, int> ez_step_is_consistent;
|
||||
pool<Cell*> cell_warn_cache;
|
||||
SigPool undriven_signals;
|
||||
|
||||
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq, bool set_assumes) : module(module), sigmap(module),
|
||||
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, EquivBasicConfig cfg) : EquivWorker<>(module, &sigmap, cfg), sigmap(module),
|
||||
cells(module->selected_cells()), workset(unproven_equiv_cells),
|
||||
satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0), set_assumes(set_assumes)
|
||||
{
|
||||
satgen.model_undef = model_undef;
|
||||
}
|
||||
success_counter(0) {}
|
||||
|
||||
void create_timestep(int step)
|
||||
{
|
||||
vector<int> ez_equal_terms;
|
||||
|
||||
for (auto cell : cells) {
|
||||
if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) {
|
||||
if (cell->is_builtin_ff())
|
||||
log_warning("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
|
||||
else
|
||||
log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
|
||||
cell_warn_cache.insert(cell);
|
||||
if (!satgen.importCell(cell, step)) {
|
||||
report_missing_model(cfg.ignore_unknown_cells, cell);
|
||||
}
|
||||
if (cell->type == ID($equiv)) {
|
||||
SigBit bit_a = sigmap(cell->getPort(ID::A)).as_bit();
|
||||
|
|
@ -78,7 +63,7 @@ struct EquivInductWorker
|
|||
}
|
||||
}
|
||||
|
||||
if (set_assumes) {
|
||||
if (cfg.set_assumes) {
|
||||
if (step == 1) {
|
||||
RTLIL::SigSpec assumes_a, assumes_en;
|
||||
satgen.getAssumes(assumes_a, assumes_en, step);
|
||||
|
|
@ -99,7 +84,7 @@ struct EquivInductWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
log("Found %d unproven $equiv cells in module %s:\n", GetSize(workset), log_id(module));
|
||||
log("Found %d unproven $equiv cells in module %s:\n", GetSize(workset), module);
|
||||
|
||||
if (satgen.model_undef) {
|
||||
for (auto cell : cells)
|
||||
|
|
@ -123,7 +108,7 @@ struct EquivInductWorker
|
|||
GetSize(satgen.initial_state), GetSize(undriven_signals));
|
||||
}
|
||||
|
||||
for (int step = 1; step <= max_seq; step++)
|
||||
for (int step = 1; step <= cfg.max_seq; step++)
|
||||
{
|
||||
ez->assume(ez_step_is_consistent[step]);
|
||||
|
||||
|
|
@ -146,7 +131,7 @@ struct EquivInductWorker
|
|||
return;
|
||||
}
|
||||
|
||||
log(" Proof for induction step failed. %s\n", step != max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
|
||||
log(" Proof for induction step failed. %s\n", step != cfg.max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
|
||||
}
|
||||
|
||||
workset.sort();
|
||||
|
|
@ -158,12 +143,12 @@ struct EquivInductWorker
|
|||
|
||||
log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort(ID::Y))));
|
||||
|
||||
int ez_a = satgen.importSigBit(bit_a, max_seq+1);
|
||||
int ez_b = satgen.importSigBit(bit_b, max_seq+1);
|
||||
int ez_a = satgen.importSigBit(bit_a, cfg.max_seq+1);
|
||||
int ez_b = satgen.importSigBit(bit_b, cfg.max_seq+1);
|
||||
int cond = ez->XOR(ez_a, ez_b);
|
||||
|
||||
if (satgen.model_undef)
|
||||
cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1)));
|
||||
cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, cfg.max_seq+1)));
|
||||
|
||||
if (!ez->solve(cond)) {
|
||||
log(" success!\n");
|
||||
|
|
@ -189,14 +174,7 @@ struct EquivInductPass : public Pass {
|
|||
log("Only selected $equiv cells are proven and only selected cells are used to\n");
|
||||
log("perform the proof.\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" enable modelling of undef states\n");
|
||||
log("\n");
|
||||
log(" -seq <N>\n");
|
||||
log(" the max. number of time steps to be considered (default = 4)\n");
|
||||
log("\n");
|
||||
log(" -set-assumes\n");
|
||||
log(" set all assumptions provided via $assume cells\n");
|
||||
log("%s", EquivBasicConfig::help("4"));
|
||||
log("\n");
|
||||
log("This command is very effective in proving complex sequential circuits, when\n");
|
||||
log("the internal state of the circuit quickly propagates to $equiv cells.\n");
|
||||
|
|
@ -214,25 +192,15 @@ struct EquivInductPass : public Pass {
|
|||
void execute(std::vector<std::string> args, Design *design) override
|
||||
{
|
||||
int success_counter = 0;
|
||||
bool model_undef = false, set_assumes = false;
|
||||
int max_seq = 4;
|
||||
EquivBasicConfig cfg {};
|
||||
cfg.max_seq = 4;
|
||||
|
||||
log_header(design, "Executing EQUIV_INDUCT pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-undef") {
|
||||
model_undef = true;
|
||||
if (cfg.parse(args, argidx))
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-seq" && argidx+1 < args.size()) {
|
||||
max_seq = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-set-assumes") {
|
||||
set_assumes = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
|
@ -249,11 +217,11 @@ struct EquivInductPass : public Pass {
|
|||
}
|
||||
|
||||
if (unproven_equiv_cells.empty()) {
|
||||
log("No selected unproven $equiv cells found in %s.\n", log_id(module));
|
||||
log("No selected unproven $equiv cells found in %s.\n", module);
|
||||
continue;
|
||||
}
|
||||
|
||||
EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq, set_assumes);
|
||||
EquivInductWorker worker(module, unproven_equiv_cells, cfg);
|
||||
worker.run();
|
||||
success_counter += worker.success_counter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ struct EquivMakeWorker
|
|||
|
||||
if (token == ".fsm") {
|
||||
IdString modname = RTLIL::escape_id(next_token(line));
|
||||
(void)modname;
|
||||
IdString signame = RTLIL::escape_id(next_token(line));
|
||||
if (encdata.count(signame))
|
||||
log_cmd_error("Re-definition of signal '%s' in encfile '%s'!\n", signame, fn);
|
||||
|
|
@ -160,7 +161,7 @@ struct EquivMakeWorker
|
|||
|
||||
if (encdata.count(id))
|
||||
{
|
||||
log("Creating encoder/decoder for signal %s.\n", log_id(id));
|
||||
log("Creating encoder/decoder for signal %s.\n", id.unescape());
|
||||
|
||||
Wire *dec_wire = equiv_mod->addWire(id.str() + "_decoded", gold_wire->width);
|
||||
Wire *enc_wire = equiv_mod->addWire(id.str() + "_encoded", gate_wire->width);
|
||||
|
|
@ -227,15 +228,15 @@ struct EquivMakeWorker
|
|||
|
||||
if (gold_wire == nullptr || gate_wire == nullptr || gold_wire->width != gate_wire->width) {
|
||||
if (gold_wire && gold_wire->port_id)
|
||||
log_error("Can't match gold port `%s' to a gate port.\n", log_id(gold_wire));
|
||||
log_error("Can't match gold port `%s' to a gate port.\n", gold_wire);
|
||||
if (gate_wire && gate_wire->port_id)
|
||||
log_error("Can't match gate port `%s' to a gold port.\n", log_id(gate_wire));
|
||||
log_error("Can't match gate port `%s' to a gold port.\n", gate_wire);
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Presumably equivalent wires: %s (%s), %s (%s) -> %s\n",
|
||||
log_id(gold_wire), log_signal(assign_map(gold_wire)),
|
||||
log_id(gate_wire), log_signal(assign_map(gate_wire)), log_id(id));
|
||||
gold_wire, log_signal(assign_map(gold_wire)),
|
||||
gate_wire, log_signal(assign_map(gate_wire)), id.unescape());
|
||||
|
||||
if (gold_wire->port_output || gate_wire->port_output)
|
||||
{
|
||||
|
|
@ -285,11 +286,11 @@ struct EquivMakeWorker
|
|||
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) {
|
||||
log(" Skipping signal bit %s [%d]: undriven on gold side.\n", id2cstr(gold_wire->name), i);
|
||||
log(" Skipping signal bit %s [%d]: undriven on gold side.\n", gold_wire, i);
|
||||
continue;
|
||||
}
|
||||
if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) {
|
||||
log(" Skipping signal bit %s [%d]: undriven on gate side.\n", id2cstr(gate_wire->name), i);
|
||||
log(" Skipping signal bit %s [%d]: undriven on gate side.\n", gate_wire, i);
|
||||
continue;
|
||||
}
|
||||
equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i));
|
||||
|
|
@ -314,7 +315,7 @@ struct EquivMakeWorker
|
|||
new_sig[i] = old_sig[i];
|
||||
if (old_sig != new_sig) {
|
||||
log("Changing input %s of cell %s (%s): %s -> %s\n",
|
||||
log_id(conn.first), log_id(c), log_id(c->type),
|
||||
conn.first.unescape(), c, c->type.unescape(),
|
||||
log_signal(old_sig), log_signal(new_sig));
|
||||
c->setPort(conn.first, new_sig);
|
||||
}
|
||||
|
|
@ -345,7 +346,7 @@ struct EquivMakeWorker
|
|||
goto try_next_cell_name;
|
||||
|
||||
log("Presumably equivalent cells: %s %s (%s) -> %s\n",
|
||||
log_id(gold_cell), log_id(gate_cell), log_id(gold_cell->type), log_id(id));
|
||||
gold_cell, gate_cell, gold_cell->type.unescape(), id.unescape());
|
||||
|
||||
for (auto gold_conn : gold_cell->connections())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ struct EquivMarkWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
log("Running equiv_mark on module %s:\n", log_id(module));
|
||||
log("Running equiv_mark on module %s:\n", module);
|
||||
|
||||
// marking region 0
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ struct EquivMiterWorker
|
|||
|
||||
for (auto c : source_module->selected_cells())
|
||||
if (c->type == ID($equiv)) {
|
||||
log("Seed $equiv cell: %s\n", log_id(c));
|
||||
log("Seed $equiv cell: %s\n", c);
|
||||
seed_cells.insert(c);
|
||||
}
|
||||
|
||||
|
|
@ -194,11 +194,11 @@ struct EquivMiterWorker
|
|||
w->port_input = true;
|
||||
}
|
||||
if (w->port_output && w->port_input)
|
||||
log("Created miter inout port %s.\n", log_id(w));
|
||||
log("Created miter inout port %s.\n", w);
|
||||
else if (w->port_output)
|
||||
log("Created miter output port %s.\n", log_id(w));
|
||||
log("Created miter output port %s.\n", w);
|
||||
else if (w->port_input)
|
||||
log("Created miter input port %s.\n", log_id(w));
|
||||
log("Created miter input port %s.\n", w);
|
||||
}
|
||||
|
||||
miter_module->fixup_ports();
|
||||
|
|
@ -252,7 +252,7 @@ struct EquivMiterWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
log("Creating miter %s from module %s.\n", log_id(miter_module), log_id(source_module));
|
||||
log("Creating miter %s from module %s.\n", miter_module, source_module);
|
||||
find_miter_cells_wires();
|
||||
copy_to_miter();
|
||||
make_stuff();
|
||||
|
|
@ -320,7 +320,7 @@ struct EquivMiterPass : public Pass {
|
|||
extra_args(args, argidx, design);
|
||||
|
||||
if (design->module(worker.miter_name))
|
||||
log_cmd_error("Miter module %s already exists.\n", log_id(worker.miter_name));
|
||||
log_cmd_error("Miter module %s already exists.\n", worker.miter_name.unescape());
|
||||
|
||||
worker.source_module = nullptr;
|
||||
for (auto m : design->selected_modules()) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ struct EquivPurgeWorker
|
|||
Wire *wire = sig.as_wire();
|
||||
if (wire->name.isPublic()) {
|
||||
if (!wire->port_output) {
|
||||
log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
|
||||
log(" Module output: %s (%s)\n", log_signal(wire), cellname.unescape());
|
||||
wire->port_output = true;
|
||||
}
|
||||
return wire;
|
||||
|
|
@ -53,7 +53,7 @@ struct EquivPurgeWorker
|
|||
Wire *wire = module->addWire(name, GetSize(sig));
|
||||
wire->port_output = true;
|
||||
module->connect(wire, sig);
|
||||
log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
|
||||
log(" Module output: %s (%s)\n", log_signal(wire), cellname.unescape());
|
||||
return wire;
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ struct EquivPurgeWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
log("Running equiv_purge on module %s:\n", log_id(module));
|
||||
log("Running equiv_purge on module %s:\n", module);
|
||||
|
||||
for (auto wire : module->wires()) {
|
||||
wire->port_input = false;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ struct EquivRemovePass : public Pass {
|
|||
{
|
||||
for (auto cell : module->selected_cells())
|
||||
if (cell->type == ID($equiv) && (mode_gold || mode_gate || cell->getPort(ID::A) == cell->getPort(ID::B))) {
|
||||
log("Removing $equiv cell %s.%s (%s).\n", log_id(module), log_id(cell), log_signal(cell->getPort(ID::Y)));
|
||||
log("Removing $equiv cell %s.%s (%s).\n", module, cell, log_signal(cell->getPort(ID::Y)));
|
||||
module->connect(cell->getPort(ID::Y), mode_gate ? cell->getPort(ID::B) : cell->getPort(ID::A));
|
||||
module->remove(cell);
|
||||
remove_count++;
|
||||
|
|
|
|||
|
|
@ -17,15 +17,51 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "passes/equiv/equiv.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivSimpleWorker
|
||||
struct EquivSimpleConfig : EquivBasicConfig {
|
||||
bool verbose = false;
|
||||
bool short_cones = false;
|
||||
bool group = true;
|
||||
bool parse(const std::vector<std::string>& args, size_t& idx) {
|
||||
if (EquivBasicConfig::parse(args, idx))
|
||||
return true;
|
||||
if (args[idx] == "-v") {
|
||||
verbose = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-short") {
|
||||
short_cones = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-nogroup") {
|
||||
group = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static std::string help(const char* default_seq) {
|
||||
return EquivBasicConfig::help(default_seq) +
|
||||
" -v\n"
|
||||
" verbose output\n"
|
||||
"\n"
|
||||
" -short\n"
|
||||
" create shorter input cones that stop at shared nodes. This yields\n"
|
||||
" simpler SAT problems but sometimes fails to prove equivalence.\n"
|
||||
"\n"
|
||||
" -nogroup\n"
|
||||
" disabling grouping of $equiv cells by output wire\n"
|
||||
"\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct EquivSimpleWorker : public EquivWorker<EquivSimpleConfig>
|
||||
{
|
||||
Module *module;
|
||||
const vector<Cell*> &equiv_cells;
|
||||
const vector<Cell*> &assume_cells;
|
||||
struct Cone {
|
||||
|
|
@ -43,27 +79,11 @@ struct EquivSimpleWorker
|
|||
};
|
||||
DesignModel model;
|
||||
|
||||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
|
||||
struct Config {
|
||||
bool verbose = false;
|
||||
bool short_cones = false;
|
||||
bool model_undef = false;
|
||||
bool nogroup = false;
|
||||
bool set_assumes = false;
|
||||
int max_seq = 1;
|
||||
};
|
||||
Config cfg;
|
||||
|
||||
pool<pair<Cell*, int>> imported_cells_cache;
|
||||
|
||||
EquivSimpleWorker(const vector<Cell*> &equiv_cells, const vector<Cell*> &assume_cells, DesignModel model, Config cfg) :
|
||||
module(equiv_cells.front()->module), equiv_cells(equiv_cells), assume_cells(assume_cells),
|
||||
model(model), satgen(ez.get(), &model.sigmap), cfg(cfg)
|
||||
{
|
||||
satgen.model_undef = cfg.model_undef;
|
||||
}
|
||||
EquivSimpleWorker(const vector<Cell*> &equiv_cells, const vector<Cell*> &assume_cells, DesignModel model, EquivSimpleConfig cfg) :
|
||||
EquivWorker<EquivSimpleConfig>(equiv_cells.front()->module, &model.sigmap, cfg), equiv_cells(equiv_cells), assume_cells(assume_cells),
|
||||
model(model) {}
|
||||
|
||||
struct ConeFinder {
|
||||
DesignModel model;
|
||||
|
|
@ -185,10 +205,10 @@ struct EquivSimpleWorker
|
|||
(GetSize(cone_a.cells) + GetSize(cone_b.cells)) - GetSize(cells));
|
||||
#if 0
|
||||
for (auto cell : short_cells_cone_a)
|
||||
log(" A-side cell: %s\n", log_id(cell));
|
||||
log(" A-side cell: %s\n", cell);
|
||||
|
||||
for (auto cell : short_cells_cone_b)
|
||||
log(" B-side cell: %s\n", log_id(cell));
|
||||
log(" B-side cell: %s\n", cell);
|
||||
#endif
|
||||
}
|
||||
void report_new_assume_cells(const pool<Cell*>& extra_problem_cells, int old_size, const pool<Cell*>& problem_cells) const
|
||||
|
|
@ -199,7 +219,7 @@ struct EquivSimpleWorker
|
|||
old_size - (GetSize(problem_cells) - GetSize(extra_problem_cells)));
|
||||
#if 0
|
||||
for (auto cell : extra_problem_cells)
|
||||
log(" cell: %s\n", log_id(cell));
|
||||
log(" cell: %s\n", cell);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -229,14 +249,6 @@ struct EquivSimpleWorker
|
|||
return extra_problem_cells;
|
||||
}
|
||||
|
||||
static void report_missing_model(Cell* cell)
|
||||
{
|
||||
if (cell->is_builtin_ff())
|
||||
log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
|
||||
else
|
||||
log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
|
||||
}
|
||||
|
||||
void prepare_ezsat(int ez_context, SigBit bit_a, SigBit bit_b)
|
||||
{
|
||||
if (satgen.model_undef)
|
||||
|
|
@ -257,7 +269,9 @@ struct EquivSimpleWorker
|
|||
}
|
||||
void construct_ezsat(const pool<SigBit>& input_bits, int step)
|
||||
{
|
||||
log("ezsat\n");
|
||||
if (cfg.set_assumes) {
|
||||
log("yep assume\n");
|
||||
if (cfg.verbose && step == cfg.max_seq) {
|
||||
RTLIL::SigSpec assumes_a, assumes_en;
|
||||
satgen.getAssumes(assumes_a, assumes_en, step+1);
|
||||
|
|
@ -291,7 +305,7 @@ struct EquivSimpleWorker
|
|||
pool<SigBit> seed_b = { bit_b };
|
||||
|
||||
if (cfg.verbose) {
|
||||
log(" Trying to prove $equiv cell %s:\n", log_id(cell));
|
||||
log(" Trying to prove $equiv cell %s:\n", cell);
|
||||
log(" A = %s, B = %s, Y = %s\n", log_signal(bit_a), log_signal(bit_b), log_signal(cell->getPort(ID::Y)));
|
||||
} else {
|
||||
log(" Trying to prove $equiv for %s:", log_signal(cell->getPort(ID::Y)));
|
||||
|
|
@ -323,7 +337,7 @@ struct EquivSimpleWorker
|
|||
for (auto cell : problem_cells) {
|
||||
auto key = pair<Cell*, int>(cell, step+1);
|
||||
if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) {
|
||||
report_missing_model(cell);
|
||||
report_missing_model(cfg.ignore_unknown_cells, cell);
|
||||
}
|
||||
imported_cells_cache.insert(key);
|
||||
}
|
||||
|
|
@ -414,59 +428,20 @@ struct EquivSimplePass : public Pass {
|
|||
log("\n");
|
||||
log("This command tries to prove $equiv cells using a simple direct SAT approach.\n");
|
||||
log("\n");
|
||||
log(" -v\n");
|
||||
log(" verbose output\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" enable modelling of undef states\n");
|
||||
log("\n");
|
||||
log(" -short\n");
|
||||
log(" create shorter input cones that stop at shared nodes. This yields\n");
|
||||
log(" simpler SAT problems but sometimes fails to prove equivalence.\n");
|
||||
log("\n");
|
||||
log(" -nogroup\n");
|
||||
log(" disabling grouping of $equiv cells by output wire\n");
|
||||
log("\n");
|
||||
log(" -seq <N>\n");
|
||||
log(" the max. number of time steps to be considered (default = 1)\n");
|
||||
log("\n");
|
||||
log(" -set-assumes\n");
|
||||
log(" set all assumptions provided via $assume cells\n");
|
||||
log("%s", EquivSimpleConfig::help("1"));
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, Design *design) override
|
||||
{
|
||||
EquivSimpleWorker::Config cfg = {};
|
||||
EquivSimpleConfig cfg {};
|
||||
int success_counter = 0;
|
||||
|
||||
log_header(design, "Executing EQUIV_SIMPLE pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-v") {
|
||||
cfg.verbose = true;
|
||||
if (cfg.parse(args, argidx))
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-short") {
|
||||
cfg.short_cones = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-undef") {
|
||||
cfg.model_undef = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nogroup") {
|
||||
cfg.nogroup = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-seq" && argidx+1 < args.size()) {
|
||||
cfg.max_seq = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-set-assumes") {
|
||||
cfg.set_assumes = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
|
@ -489,7 +464,7 @@ struct EquivSimplePass : public Pass {
|
|||
if (cell->type == ID($equiv) && cell->getPort(ID::A) != cell->getPort(ID::B)) {
|
||||
auto bit = sigmap(cell->getPort(ID::Y).as_bit());
|
||||
auto bit_group = bit;
|
||||
if (!cfg.nogroup && bit_group.wire)
|
||||
if (cfg.group && bit_group.wire)
|
||||
bit_group.offset = 0;
|
||||
unproven_equiv_cells[bit_group][bit] = cell;
|
||||
unproven_cells_counter++;
|
||||
|
|
@ -502,7 +477,7 @@ struct EquivSimplePass : public Pass {
|
|||
continue;
|
||||
|
||||
log("Found %d unproven $equiv cells (%d groups) in %s:\n",
|
||||
unproven_cells_counter, GetSize(unproven_equiv_cells), log_id(module));
|
||||
unproven_cells_counter, GetSize(unproven_equiv_cells), module);
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
if (!ct.cell_known(cell->type))
|
||||
|
|
|
|||
|
|
@ -67,17 +67,17 @@ struct EquivStatusPass : public Pass {
|
|||
}
|
||||
|
||||
if (unproven_equiv_cells.empty() && !proven_equiv_cells) {
|
||||
log("No $equiv cells found in %s.\n", log_id(module));
|
||||
log("No $equiv cells found in %s.\n", module);
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Found %d $equiv cells in %s:\n", GetSize(unproven_equiv_cells) + proven_equiv_cells, log_id(module));
|
||||
log("Found %d $equiv cells in %s:\n", GetSize(unproven_equiv_cells) + proven_equiv_cells, module);
|
||||
log(" Of those cells %d are proven and %d are unproven.\n", proven_equiv_cells, GetSize(unproven_equiv_cells));
|
||||
if (unproven_equiv_cells.empty()) {
|
||||
log(" Equivalence successfully proven!\n");
|
||||
} else {
|
||||
for (auto cell : unproven_equiv_cells)
|
||||
log(" Unproven $equiv %s: %s %s\n", log_id(cell), log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B)));
|
||||
log(" Unproven $equiv %s: %s %s\n", cell, log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B)));
|
||||
}
|
||||
|
||||
unproven_count += GetSize(unproven_equiv_cells);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ struct EquivStructWorker
|
|||
inputs_a.append(bits_a[i]);
|
||||
inputs_b.append(bits_b[i]);
|
||||
input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
|
||||
stringf("%s[%d]", log_id(port_a.first), i));
|
||||
stringf("%s[%d]", port_a.first.unescape(), i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ struct EquivStructWorker
|
|||
}
|
||||
|
||||
auto merged_attr = cell_b->get_strpool_attribute(ID::equiv_merged);
|
||||
merged_attr.insert(log_id(cell_b));
|
||||
merged_attr.insert(cell_b->name.unescape());
|
||||
cell_a->add_strpool_attribute(ID::equiv_merged, merged_attr);
|
||||
module->remove(cell_b);
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ struct EquivStructWorker
|
|||
SigBit sig_b = sigmap(cell->getPort(ID::B).as_bit());
|
||||
SigBit sig_y = sigmap(cell->getPort(ID::Y).as_bit());
|
||||
if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
|
||||
log(" Purging redundant $equiv cell %s.\n", log_id(cell));
|
||||
log(" Purging redundant $equiv cell %s.\n", cell);
|
||||
module->connect(sig_y, sig_a);
|
||||
module->remove(cell);
|
||||
merge_count++;
|
||||
|
|
@ -266,9 +266,9 @@ struct EquivStructWorker
|
|||
run_strategy:
|
||||
int total_group_size = GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells);
|
||||
log(" %s merging %d %s cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
|
||||
2*GetSize(cell_pairs), log_id(cells_type), total_group_size, strategy);
|
||||
2*GetSize(cell_pairs), cells_type.unescape(), total_group_size, strategy);
|
||||
for (auto it : cell_pairs) {
|
||||
log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second));
|
||||
log(" Merging cells %s and %s.\n", it.first, it.second);
|
||||
merge_cell_pair(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
|
@ -347,7 +347,7 @@ struct EquivStructPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules()) {
|
||||
int module_merge_count = 0;
|
||||
log("Running equiv_struct on module %s:\n", log_id(module));
|
||||
log("Running equiv_struct on module %s:\n", module);
|
||||
for (int iter = 0;; iter++) {
|
||||
if (iter == max_iter) {
|
||||
log(" Reached iteration limit of %d.\n", iter);
|
||||
|
|
@ -359,7 +359,7 @@ struct EquivStructPass : public Pass {
|
|||
module_merge_count += worker.merge_count;
|
||||
}
|
||||
if (module_merge_count)
|
||||
log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
|
||||
log(" Performed a total of %d merges in module %s.\n", module_merge_count, module);
|
||||
}
|
||||
}
|
||||
} EquivStructPass;
|
||||
|
|
|
|||
52
passes/fsm/CMakeLists.txt
Normal file
52
passes/fsm/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
yosys_core(fsmdata INTERFACE
|
||||
fsmdata.h
|
||||
DATA_DIR
|
||||
include/passes/fsm
|
||||
DATA_FILES
|
||||
fsmdata.h
|
||||
)
|
||||
|
||||
function(yosys_fsm_pass)
|
||||
yosys_pass(${ARGV} REQUIRES fsmdata)
|
||||
endfunction()
|
||||
|
||||
yosys_fsm_pass(fsm_detect
|
||||
fsm_detect.cc
|
||||
)
|
||||
yosys_fsm_pass(fsm_extract
|
||||
fsm_extract.cc
|
||||
)
|
||||
yosys_fsm_pass(fsm_opt
|
||||
fsm_opt.cc
|
||||
)
|
||||
yosys_fsm_pass(fsm_expand
|
||||
fsm_expand.cc
|
||||
REQUIRES
|
||||
fsm_opt
|
||||
)
|
||||
yosys_fsm_pass(fsm_recode
|
||||
fsm_recode.cc
|
||||
)
|
||||
yosys_fsm_pass(fsm_info
|
||||
fsm_info.cc
|
||||
)
|
||||
yosys_fsm_pass(fsm_export
|
||||
fsm_export.cc
|
||||
)
|
||||
yosys_fsm_pass(fsm_map
|
||||
fsm_map.cc
|
||||
)
|
||||
|
||||
yosys_pass(fsm
|
||||
fsm.cc
|
||||
REQUIRES
|
||||
fsm_detect
|
||||
fsm_expand
|
||||
fsm_export
|
||||
fsm_extract
|
||||
fsm_info
|
||||
fsm_map
|
||||
fsm_opt
|
||||
fsm_recode
|
||||
opt_clean
|
||||
)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
|
||||
OBJS += passes/fsm/fsm.o
|
||||
OBJS += passes/fsm/fsm_detect.o
|
||||
OBJS += passes/fsm/fsm_extract.o
|
||||
OBJS += passes/fsm/fsm_opt.o
|
||||
OBJS += passes/fsm/fsm_expand.o
|
||||
OBJS += passes/fsm/fsm_recode.o
|
||||
OBJS += passes/fsm/fsm_info.o
|
||||
OBJS += passes/fsm/fsm_export.o
|
||||
OBJS += passes/fsm/fsm_map.o
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ ret_false:
|
|||
|
||||
if (recursion_monitor.count(cellport.first)) {
|
||||
log_warning("logic loop in mux tree at signal %s in module %s.\n",
|
||||
log_signal(sig), RTLIL::id2cstr(module->name));
|
||||
log_signal(sig), module);
|
||||
goto ret_false;
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ static void detect_fsm(RTLIL::Wire *wire, bool ignore_self_reset=false)
|
|||
|
||||
if (wire->width <= 1) {
|
||||
if (has_fsm_encoding_attr) {
|
||||
log_warning("Removing fsm_encoding attribute from 1-bit net: %s.%s\n", log_id(wire->module), log_id(wire));
|
||||
log_warning("Removing fsm_encoding attribute from 1-bit net: %s.%s\n", wire->module, wire);
|
||||
wire->attributes.erase(ID::fsm_encoding);
|
||||
}
|
||||
return;
|
||||
|
|
@ -230,23 +230,23 @@ static void detect_fsm(RTLIL::Wire *wire, bool ignore_self_reset=false)
|
|||
warnings.push_back("FSM seems to be self-resetting. Possible simulation-synthesis mismatch!\n");
|
||||
|
||||
if (!warnings.empty()) {
|
||||
string warnmsg = stringf("Regarding the user-specified fsm_encoding attribute on %s.%s:\n", log_id(wire->module), log_id(wire));
|
||||
string warnmsg = stringf("Regarding the user-specified fsm_encoding attribute on %s.%s:\n", wire->module, wire);
|
||||
for (auto w : warnings) warnmsg += " " + w;
|
||||
log_warning("%s", warnmsg);
|
||||
} else {
|
||||
log("FSM state register %s.%s already has fsm_encoding attribute.\n", log_id(wire->module), log_id(wire));
|
||||
log("FSM state register %s.%s already has fsm_encoding attribute.\n", wire->module, wire);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (looks_like_state_reg && looks_like_good_state_reg && !has_init_attr && !is_module_port && !is_self_resetting)
|
||||
{
|
||||
log("Found FSM state register %s.%s.\n", log_id(wire->module), log_id(wire));
|
||||
log("Found FSM state register %s.%s.\n", wire->module, wire);
|
||||
wire->attributes[ID::fsm_encoding] = RTLIL::Const("auto");
|
||||
}
|
||||
else
|
||||
if (looks_like_state_reg)
|
||||
{
|
||||
log("Not marking %s.%s as FSM state register:\n", log_id(wire->module), log_id(wire));
|
||||
log("Not marking %s.%s as FSM state register:\n", wire->module, wire);
|
||||
|
||||
if (is_module_port)
|
||||
log(" Register is connected to module port.\n");
|
||||
|
|
|
|||
|
|
@ -189,12 +189,12 @@ struct FsmExpand
|
|||
|
||||
if (GetSize(input_sig) > 10)
|
||||
log_warning("Cell %s.%s (%s) has %d input bits, merging into FSM %s.%s might be problematic.\n",
|
||||
log_id(cell->module), log_id(cell), log_id(cell->type),
|
||||
GetSize(input_sig), log_id(fsm_cell->module), log_id(fsm_cell));
|
||||
cell->module, cell, cell->type.unescape(),
|
||||
GetSize(input_sig), fsm_cell->module, fsm_cell);
|
||||
|
||||
if (GetSize(fsm_data.transition_table) > 10000)
|
||||
log_warning("Transition table for FSM %s.%s already has %d rows, merging more cells "
|
||||
"into this FSM might be problematic.\n", log_id(fsm_cell->module), log_id(fsm_cell),
|
||||
"into this FSM might be problematic.\n", fsm_cell->module, fsm_cell,
|
||||
GetSize(fsm_data.transition_table));
|
||||
|
||||
std::vector<FsmData::transition_t> new_transition_table;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::st
|
|||
kiss_name.assign(attr_it->second.decode_string());
|
||||
}
|
||||
else {
|
||||
kiss_name.assign(log_id(module) + std::string("-") + log_id(cell) + ".kiss2");
|
||||
kiss_name.assign(module->name.unescape() + std::string("-") + cell->name.unescape() + ".kiss2");
|
||||
}
|
||||
|
||||
log("\n");
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ struct FsmInfoPass : public Pass {
|
|||
for (auto cell : mod->selected_cells())
|
||||
if (cell->type == ID($fsm)) {
|
||||
log("\n");
|
||||
log("FSM `%s' from module `%s':\n", log_id(cell), log_id(mod));
|
||||
log("FSM `%s' from module `%s':\n", cell, mod);
|
||||
FsmData fsm_data;
|
||||
fsm_data.copy_from_cell(cell);
|
||||
fsm_data.log_info(cell);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &
|
|||
for (int i = fsm_data.state_bits-1; i >= 0; i--)
|
||||
fprintf(f, " %s_reg[%d]", name[0] == '\\' ? name.substr(1).c_str() : name.c_str(), i);
|
||||
fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n", prefix, RTLIL::unescape_id(name).c_str(),
|
||||
prefix, RTLIL::unescape_id(module->name).c_str());
|
||||
prefix, module->name.unescape().c_str());
|
||||
|
||||
fprintf(f, "set_fsm_encoding {");
|
||||
for (int i = 0; i < GetSize(fsm_data.state_table); i++) {
|
||||
|
|
@ -49,7 +49,7 @@ static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &
|
|||
}
|
||||
fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n",
|
||||
prefix, RTLIL::unescape_id(name).c_str(),
|
||||
prefix, RTLIL::unescape_id(module->name).c_str());
|
||||
prefix, module->name.unescape().c_str());
|
||||
}
|
||||
|
||||
static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file, FILE *encfile, std::string default_encoding)
|
||||
|
|
@ -96,7 +96,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs
|
|||
log_error("FSM encoding `%s' is not supported!\n", encoding);
|
||||
|
||||
if (encfile)
|
||||
fprintf(encfile, ".fsm %s %s\n", log_id(module), RTLIL::unescape_id(cell->parameters[ID::NAME].decode_string()).c_str());
|
||||
fprintf(encfile, ".fsm %s %s\n", module->name.unescape().c_str(), RTLIL::unescape_id(cell->parameters[ID::NAME].decode_string()).c_str());
|
||||
|
||||
int state_idx_counter = fsm_data.reset_state >= 0 ? 1 : 0;
|
||||
for (int i = 0; i < int(fsm_data.state_table.size()); i++)
|
||||
|
|
|
|||
22
passes/hierarchy/CMakeLists.txt
Normal file
22
passes/hierarchy/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
yosys_pass(flatten
|
||||
flatten.cc
|
||||
)
|
||||
yosys_pass(hierarchy
|
||||
hierarchy.cc
|
||||
LIBRARIES
|
||||
verific
|
||||
REQUIRES
|
||||
read_rtlil
|
||||
read_verilog
|
||||
)
|
||||
yosys_pass(keep_hierarchy
|
||||
keep_hierarchy.cc
|
||||
)
|
||||
yosys_pass(submod
|
||||
submod.cc
|
||||
REQUIRES
|
||||
opt_clean
|
||||
)
|
||||
yosys_pass(uniquify
|
||||
uniquify.cc
|
||||
)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
OBJS += passes/hierarchy/flatten.o
|
||||
OBJS += passes/hierarchy/hierarchy.o
|
||||
OBJS += passes/hierarchy/uniquify.o
|
||||
OBJS += passes/hierarchy/submod.o
|
||||
OBJS += passes/hierarchy/keep_hierarchy.o
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ struct FlattenWorker
|
|||
hier_wire->attributes.erase(ID::hierconn);
|
||||
if (GetSize(hier_wire) < GetSize(tpl_wire)) {
|
||||
log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n",
|
||||
log_id(module), log_id(hier_wire), log_id(tpl), log_id(tpl_wire), log_id(module), log_id(cell));
|
||||
module, hier_wire, tpl, tpl_wire, module, cell);
|
||||
hier_wire->width = GetSize(tpl_wire);
|
||||
}
|
||||
new_wire = hier_wire;
|
||||
|
|
@ -253,7 +253,7 @@ struct FlattenWorker
|
|||
|
||||
if (sigmap(new_conn.first).has_const())
|
||||
log_error("Cell port %s.%s.%s is driving constant bits: %s <= %s\n",
|
||||
log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second));
|
||||
module, cell, port_it.first.unescape(), log_signal(new_conn.first), log_signal(new_conn.second));
|
||||
|
||||
module->connect(new_conn);
|
||||
sigmap.add(new_conn.first, new_conn.second);
|
||||
|
|
@ -273,13 +273,13 @@ struct FlattenWorker
|
|||
if (attr.first == ID::hdlname)
|
||||
scopeinfo->attributes.insert(attr);
|
||||
else
|
||||
scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first)), attr.second);
|
||||
scopeinfo->attributes.emplace(stringf("\\cell_%s", attr.first.unescape()), attr.second);
|
||||
}
|
||||
|
||||
for (auto const &attr : tpl->attributes)
|
||||
scopeinfo->attributes.emplace(stringf("\\module_%s", RTLIL::unescape_id(attr.first)), attr.second);
|
||||
scopeinfo->attributes.emplace(stringf("\\module_%s", attr.first.unescape()), attr.second);
|
||||
|
||||
scopeinfo->attributes.emplace(ID(module), RTLIL::unescape_id(tpl->name));
|
||||
scopeinfo->attributes.emplace(ID(module), tpl->name.unescape());
|
||||
}
|
||||
|
||||
module->remove(cell);
|
||||
|
|
@ -308,12 +308,12 @@ struct FlattenWorker
|
|||
continue;
|
||||
|
||||
if (cell->get_bool_attribute(ID::keep_hierarchy) || tpl->get_bool_attribute(ID::keep_hierarchy)) {
|
||||
log("Keeping %s.%s (found keep_hierarchy attribute).\n", log_id(module), log_id(cell));
|
||||
log("Keeping %s.%s (found keep_hierarchy attribute).\n", module, cell);
|
||||
used_modules.insert(tpl);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Flattening %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||
log_debug("Flattening %s.%s (%s).\n", module, cell, cell->type.unescape());
|
||||
// If a design is fully selected and has a top module defined, topological sorting ensures that all cells
|
||||
// added during flattening are black boxes, and flattening is finished in one pass. However, when flattening
|
||||
// individual modules, this isn't the case, and the newly added cells might have to be flattened further.
|
||||
|
|
@ -435,7 +435,7 @@ struct FlattenPass : public Pass {
|
|||
if (cleanup && top != nullptr)
|
||||
for (auto module : design->modules().to_vector())
|
||||
if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) {
|
||||
log("Deleting now unused module %s.\n", log_id(module));
|
||||
log("Deleting now unused module %s.\n", module);
|
||||
design->remove(module);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
if (cell->type.begins_with("$") && !cell->type.begins_with("$__"))
|
||||
continue;
|
||||
for (auto &pattern : celltypes)
|
||||
if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str()))
|
||||
if (patmatch(pattern.c_str(), cell->type.unescape().c_str()))
|
||||
found_celltypes.insert(cell->type);
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
if (decl.index > 0) {
|
||||
portwidths[decl.portname] = max(portwidths[decl.portname], 1);
|
||||
portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
|
||||
log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname));
|
||||
log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::unescape_id(decl.portname));
|
||||
if (indices.count(decl.index) > ports.size())
|
||||
log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size()));
|
||||
if (indices.count(decl.index) == 0)
|
||||
|
|
@ -100,7 +100,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
while (portnames.size() > 0) {
|
||||
RTLIL::IdString portname = *portnames.begin();
|
||||
for (auto &decl : portdecls)
|
||||
if (decl.index == 0 && patmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str())) {
|
||||
if (decl.index == 0 && patmatch(decl.portname.c_str(), portname.unescape().c_str())) {
|
||||
generate_port_decl_t d = decl;
|
||||
d.portname = portname.str();
|
||||
d.index = *indices.begin();
|
||||
|
|
@ -108,10 +108,10 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
indices.erase(d.index);
|
||||
ports[d.index-1] = d;
|
||||
portwidths[d.portname] = max(portwidths[d.portname], 1);
|
||||
log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname));
|
||||
log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::unescape_id(d.portname));
|
||||
goto found_matching_decl;
|
||||
}
|
||||
log_error("Can't match port %s.\n", RTLIL::id2cstr(portname));
|
||||
log_error("Can't match port %s.\n", portname.unescape());
|
||||
found_matching_decl:;
|
||||
portnames.erase(portname);
|
||||
}
|
||||
|
|
@ -133,9 +133,9 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
mod->fixup_ports();
|
||||
|
||||
for (auto ¶ : parameters)
|
||||
log(" ignoring parameter %s.\n", RTLIL::id2cstr(para));
|
||||
log(" ignoring parameter %s.\n", para.unescape());
|
||||
|
||||
log(" module %s created.\n", RTLIL::id2cstr(mod->name));
|
||||
log(" module %s created.\n", mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ struct IFExpander
|
|||
// about it and don't set has_interfaces_not_found (to avoid a
|
||||
// loop).
|
||||
log_warning("Could not find interface instance for `%s' in `%s'\n",
|
||||
log_id(interface_name), log_id(&module));
|
||||
interface_name.unescape(), &module);
|
||||
}
|
||||
|
||||
// Handle an interface connection from the module
|
||||
|
|
@ -268,12 +268,12 @@ struct IFExpander
|
|||
|
||||
// Go over all wires in interface, and add replacements to lists.
|
||||
for (auto mod_wire : mod_replace_ports->wires()) {
|
||||
std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name);
|
||||
std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
|
||||
std::string signal_name1 = conn_name.str() + "." + mod_wire->name.unescape();
|
||||
std::string signal_name2 = interface_name.str() + "." + mod_wire->name.unescape();
|
||||
connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
|
||||
if(module.wire(signal_name2) == nullptr) {
|
||||
log_error("Could not find signal '%s' in '%s'\n",
|
||||
signal_name2.c_str(), log_id(module.name));
|
||||
signal_name2.c_str(), module.name.unescape());
|
||||
}
|
||||
else {
|
||||
RTLIL::Wire *wire_in_parent = module.wire(signal_name2);
|
||||
|
|
@ -397,7 +397,7 @@ RTLIL::Module *get_module(RTLIL::Design &design,
|
|||
};
|
||||
|
||||
for (auto &ext : extensions_list) {
|
||||
std::string filename = dir + "/" + RTLIL::unescape_id(cell.type) + ext.first;
|
||||
std::string filename = dir + "/" + cell.type.unescape() + ext.first;
|
||||
if (!check_file_exists(filename))
|
||||
continue;
|
||||
|
||||
|
|
@ -432,7 +432,7 @@ void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLI
|
|||
if (id <= 0 || id > GetSize(mod.ports))
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' "
|
||||
"has only %d ports, requested port %d.\n",
|
||||
log_id(cell.type), log_id(&module), log_id(&cell),
|
||||
cell.type.unescape(), &module, &cell,
|
||||
GetSize(mod.ports), id);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -441,8 +441,8 @@ void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLI
|
|||
if (!wire || wire->port_id == 0) {
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' "
|
||||
"does not have a port named '%s'.\n",
|
||||
log_id(cell.type), log_id(&module), log_id(&cell),
|
||||
log_id(conn.first));
|
||||
cell.type.unescape(), &module, &cell,
|
||||
conn.first.unescape());
|
||||
}
|
||||
}
|
||||
for (auto ¶m : cell.parameters) {
|
||||
|
|
@ -450,7 +450,7 @@ void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLI
|
|||
if (id <= 0 || id > GetSize(mod.avail_parameters))
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' "
|
||||
"has only %d parameters, requested parameter %d.\n",
|
||||
log_id(cell.type), log_id(&module), log_id(&cell),
|
||||
cell.type.unescape(), &module, &cell,
|
||||
GetSize(mod.avail_parameters), id);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -460,8 +460,8 @@ void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLI
|
|||
strchr(param.first.c_str(), '.') == NULL) {
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' "
|
||||
"does not have a parameter named '%s'.\n",
|
||||
log_id(cell.type), log_id(&module), log_id(&cell),
|
||||
log_id(param.first));
|
||||
cell.type.unescape(), &module, &cell,
|
||||
param.first.unescape());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -597,7 +597,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
int idx = it.second.first, num = it.second.second;
|
||||
|
||||
if (design->module(cell->type) == nullptr)
|
||||
log_error("Array cell `%s.%s' of unknown type `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
log_error("Array cell `%s.%s' of unknown type `%s'.\n", module, cell, cell->type.unescape());
|
||||
|
||||
RTLIL::Module *mod = design->module(cell->type);
|
||||
|
||||
|
|
@ -613,12 +613,12 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
}
|
||||
}
|
||||
if (mod->wire(portname) == nullptr)
|
||||
log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
|
||||
log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", module, cell, conn.first.unescape());
|
||||
int port_size = mod->wire(portname)->width;
|
||||
if (conn_size == port_size || conn_size == 0)
|
||||
continue;
|
||||
if (conn_size != port_size*num)
|
||||
log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
|
||||
log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", module, cell, conn.first.unescape());
|
||||
conn.second = conn.second.extract(port_size*idx, port_size);
|
||||
}
|
||||
}
|
||||
|
|
@ -1036,7 +1036,7 @@ struct HierarchyPass : public Pass {
|
|||
if (top_mod == nullptr)
|
||||
for (auto mod : design->modules())
|
||||
if (mod->get_bool_attribute(ID::top)) {
|
||||
log("Attribute `top' found on module `%s'. Setting top module to %s.\n", log_id(mod), log_id(mod));
|
||||
log("Attribute `top' found on module `%s'. Setting top module to %s.\n", mod, mod);
|
||||
top_mod = mod;
|
||||
}
|
||||
|
||||
|
|
@ -1057,12 +1057,12 @@ struct HierarchyPass : public Pass {
|
|||
dict<Module*, int> db;
|
||||
for (Module *mod : design->selected_modules()) {
|
||||
int score = find_top_mod_score(design, mod, db);
|
||||
log("root of %3d design levels: %-20s\n", score, log_id(mod));
|
||||
log("root of %3d design levels: %-20s\n", score, mod);
|
||||
if (!top_mod || score > db[top_mod])
|
||||
top_mod = mod;
|
||||
}
|
||||
if (top_mod != nullptr)
|
||||
log("Automatically selected %s as design top module.\n", log_id(top_mod));
|
||||
log("Automatically selected %s as design top module.\n", top_mod);
|
||||
}
|
||||
|
||||
if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) {
|
||||
|
|
@ -1162,7 +1162,7 @@ struct HierarchyPass : public Pass {
|
|||
std::map<RTLIL::Module*, bool> cache;
|
||||
for (auto mod : design->modules())
|
||||
if (set_keep_print(cache, mod)) {
|
||||
log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", log_id(mod));
|
||||
log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", mod);
|
||||
mod->set_bool_attribute(ID::keep);
|
||||
}
|
||||
}
|
||||
|
|
@ -1171,7 +1171,7 @@ struct HierarchyPass : public Pass {
|
|||
std::map<RTLIL::Module*, bool> cache;
|
||||
for (auto mod : design->modules())
|
||||
if (set_keep_assert(cache, mod)) {
|
||||
log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod));
|
||||
log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", mod);
|
||||
mod->set_bool_attribute(ID::keep);
|
||||
}
|
||||
}
|
||||
|
|
@ -1190,7 +1190,7 @@ struct HierarchyPass : public Pass {
|
|||
src += ": ";
|
||||
|
||||
log_error("%sProperty `%s' in module `%s' uses unsupported SVA constructs. See frontend warnings for details, run `chformal -remove a:unsupported_sva' to ignore.\n",
|
||||
src, log_id(cell->name), log_id(mod->name));
|
||||
src, cell->name.unescape(), mod->name.unescape());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1219,7 +1219,7 @@ struct HierarchyPass : public Pass {
|
|||
if (read_id_num(p.first, &id)) {
|
||||
if (id <= 0 || id > GetSize(cell_mod->avail_parameters)) {
|
||||
log(" Failed to map positional parameter %d of cell %s.%s (%s).\n",
|
||||
id, RTLIL::id2cstr(mod->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
id, mod, cell, cell->type.unescape());
|
||||
} else {
|
||||
params_rename.insert(std::make_pair(p.first, cell_mod->avail_parameters[id - 1]));
|
||||
}
|
||||
|
|
@ -1241,7 +1241,7 @@ struct HierarchyPass : public Pass {
|
|||
RTLIL::Module *module = work.first;
|
||||
RTLIL::Cell *cell = work.second;
|
||||
log("Mapping positional arguments of cell %s.%s (%s).\n",
|
||||
RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
module, cell, cell->type.unescape());
|
||||
dict<RTLIL::IdString, RTLIL::SigSpec> new_connections;
|
||||
for (auto &conn : cell->connections()) {
|
||||
int id;
|
||||
|
|
@ -1249,7 +1249,7 @@ struct HierarchyPass : public Pass {
|
|||
std::pair<RTLIL::Module*,int> key(design->module(cell->type), id);
|
||||
if (pos_map.count(key) == 0) {
|
||||
log(" Failed to map positional argument %d of cell %s.%s (%s).\n",
|
||||
id, RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
id, module, cell, cell->type.unescape());
|
||||
new_connections[conn.first] = conn.second;
|
||||
} else
|
||||
new_connections[pos_map.at(key)] = conn.second;
|
||||
|
|
@ -1283,7 +1283,7 @@ struct HierarchyPass : public Pass {
|
|||
|
||||
if (m == nullptr)
|
||||
log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n",
|
||||
RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
module, cell, cell->type.unescape());
|
||||
|
||||
// Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths
|
||||
if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute(ID::dynports)) {
|
||||
|
|
@ -1312,11 +1312,11 @@ struct HierarchyPass : public Pass {
|
|||
|
||||
if (parent_wire == nullptr)
|
||||
log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n",
|
||||
RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
wire, module, cell, cell->type.unescape());
|
||||
if (parent_wire->width != wire->width)
|
||||
log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n",
|
||||
parent_wire->width, wire->width,
|
||||
RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
wire, module, cell, cell->type.unescape());
|
||||
cell->setPort(wire->name, parent_wire);
|
||||
}
|
||||
cell->attributes.erase(ID::wildcard_port_conns);
|
||||
|
|
@ -1500,7 +1500,7 @@ struct HierarchyPass : public Pass {
|
|||
bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(conn.second);
|
||||
if (resize_widths && verific_mod && boxed_params)
|
||||
log_debug("Ignoring width mismatch on %s.%s.%s from verific, is port width parametrizable?\n",
|
||||
log_id(module), log_id(cell), log_id(conn.first)
|
||||
module, cell, conn.first.unescape()
|
||||
);
|
||||
else if (resize_widths) {
|
||||
if (GetSize(w) < GetSize(conn.second))
|
||||
|
|
@ -1524,14 +1524,14 @@ struct HierarchyPass : public Pass {
|
|||
}
|
||||
|
||||
if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
|
||||
log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
|
||||
log_id(conn.first), GetSize(conn.second), GetSize(sig));
|
||||
log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", module, cell,
|
||||
conn.first.unescape(), GetSize(conn.second), GetSize(sig));
|
||||
cell->setPort(conn.first, sig);
|
||||
}
|
||||
|
||||
if (w->port_output && !w->port_input && sig.has_const())
|
||||
log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
|
||||
log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
|
||||
module, cell, conn.first.unescape(), cell->type.unescape(), log_signal(sig));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ struct ThresholdHierarchyKeeping {
|
|||
return 0;
|
||||
|
||||
if (module->get_blackbox_attribute())
|
||||
log_error("Missing cost information on instanced blackbox %s\n", log_id(module));
|
||||
log_error("Missing cost information on instanced blackbox %s\n", module);
|
||||
|
||||
if (done.count(module))
|
||||
return done.at(module);
|
||||
|
|
@ -61,13 +61,13 @@ struct ThresholdHierarchyKeeping {
|
|||
RTLIL::Module *submodule = design->module(cell->type);
|
||||
if (!submodule)
|
||||
log_error("Hierarchy contains unknown module '%s' (instanced as %s in %s)\n",
|
||||
log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->type.unescape(), cell, module);
|
||||
size += visit(submodule);
|
||||
}
|
||||
}
|
||||
|
||||
if (size > threshold) {
|
||||
log("Keeping %s (estimated size above threshold: %" PRIu64 " > %" PRIu64 ").\n", log_id(module), size, threshold);
|
||||
log("Keeping %s (estimated size above threshold: %" PRIu64 " > %" PRIu64 ").\n", module, size, threshold);
|
||||
module->set_bool_attribute(ID::keep_hierarchy);
|
||||
size = 0;
|
||||
}
|
||||
|
|
@ -124,7 +124,7 @@ struct KeepHierarchyPass : public Pass {
|
|||
worker.visit(top);
|
||||
} else {
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Marking %s.\n", log_id(module));
|
||||
log("Marking %s.\n", module);
|
||||
module->set_bool_attribute(ID::keep_hierarchy);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ struct UniquifyPass : public Pass {
|
|||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
Module *tmod = design->module(cell->type);
|
||||
IdString newname = module->name.str() + "." + log_id(cell->name);
|
||||
IdString newname = module->name.str() + "." + cell->name.unescape();
|
||||
|
||||
if (tmod == nullptr)
|
||||
continue;
|
||||
|
|
@ -82,14 +82,14 @@ struct UniquifyPass : public Pass {
|
|||
if (tmod->get_bool_attribute(ID::unique) && newname == tmod->name)
|
||||
continue;
|
||||
|
||||
log("Creating module %s from %s.\n", log_id(newname), log_id(tmod));
|
||||
log("Creating module %s from %s.\n", newname.unescape(), tmod);
|
||||
|
||||
auto smod = tmod->clone();
|
||||
smod->name = newname;
|
||||
cell->type = newname;
|
||||
smod->set_bool_attribute(ID::unique);
|
||||
if (smod->attributes.count(ID::hdlname) == 0)
|
||||
smod->attributes[ID::hdlname] = string(log_id(tmod->name));
|
||||
smod->attributes[ID::hdlname] = string(tmod->name.unescape());
|
||||
design->add(smod);
|
||||
|
||||
did_something = true;
|
||||
|
|
|
|||
59
passes/memory/CMakeLists.txt
Normal file
59
passes/memory/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
yosys_core(memlib
|
||||
memlib.cc
|
||||
memlib.h
|
||||
)
|
||||
|
||||
function(yosys_memory_pass)
|
||||
yosys_pass(${ARGV} REQUIRES memlib)
|
||||
endfunction()
|
||||
|
||||
yosys_memory_pass(memory_dff
|
||||
memory_dff.cc
|
||||
)
|
||||
yosys_memory_pass(memory_share
|
||||
memory_share.cc
|
||||
)
|
||||
yosys_memory_pass(memory_collect
|
||||
memory_collect.cc
|
||||
)
|
||||
yosys_memory_pass(memory_unpack
|
||||
memory_unpack.cc
|
||||
)
|
||||
yosys_memory_pass(memory_bram
|
||||
memory_bram.cc
|
||||
)
|
||||
yosys_memory_pass(memory_map
|
||||
memory_map.cc
|
||||
)
|
||||
yosys_memory_pass(memory_memx
|
||||
memory_memx.cc
|
||||
)
|
||||
yosys_memory_pass(memory_nordff
|
||||
memory_nordff.cc
|
||||
)
|
||||
yosys_memory_pass(memory_narrow
|
||||
memory_narrow.cc
|
||||
)
|
||||
yosys_memory_pass(memory_libmap
|
||||
memory_libmap.cc
|
||||
)
|
||||
yosys_memory_pass(memory_bmux2rom
|
||||
memory_bmux2rom.cc
|
||||
)
|
||||
|
||||
yosys_pass(memory
|
||||
memory.cc
|
||||
REQUIRES
|
||||
memory_bmux2rom
|
||||
memory_bram
|
||||
memory_collect
|
||||
memory_dff
|
||||
memory_map
|
||||
memory_memx
|
||||
memory_share
|
||||
opt_clean
|
||||
opt_mem
|
||||
opt_mem_feedback
|
||||
opt_mem_priority
|
||||
opt_mem_widen
|
||||
)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
OBJS += passes/memory/memory.o
|
||||
OBJS += passes/memory/memory_dff.o
|
||||
OBJS += passes/memory/memory_share.o
|
||||
OBJS += passes/memory/memory_collect.o
|
||||
OBJS += passes/memory/memory_unpack.o
|
||||
OBJS += passes/memory/memory_bram.o
|
||||
OBJS += passes/memory/memory_map.o
|
||||
OBJS += passes/memory/memory_memx.o
|
||||
OBJS += passes/memory/memory_nordff.o
|
||||
OBJS += passes/memory/memory_narrow.o
|
||||
OBJS += passes/memory/memory_libmap.o
|
||||
OBJS += passes/memory/memory_bmux2rom.o
|
||||
|
||||
OBJS += passes/memory/memlib.o
|
||||
|
|
@ -310,7 +310,7 @@ The port clock is always provided on the memory cell as `PORT_<name>_CLK` signal
|
|||
(even if it is also shared). Shared clocks are also provided as `CLK_<shared_name>`
|
||||
signals.
|
||||
|
||||
For `anyedge` clocks, the cell gets a `PORT_<name>_CLKPOL` parameter that is set
|
||||
For `anyedge` clocks, the cell gets a `PORT_<name>_CLK_POL` parameter that is set
|
||||
to 1 for `posedge` clocks and 0 for `negedge` clocks. If the clock is shared,
|
||||
the same information will also be provided as `CLK_<shared_name>_POL` parameter.
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ struct rules_t
|
|||
|
||||
void dump_config() const
|
||||
{
|
||||
log(" bram %s # variant %d\n", log_id(name), variant);
|
||||
log(" bram %s # variant %d\n", name.unescape(), variant);
|
||||
log(" init %d\n", init);
|
||||
log(" abits %d\n", abits);
|
||||
log(" dbits %d\n", dbits);
|
||||
|
|
@ -61,16 +61,16 @@ struct rules_t
|
|||
|
||||
void check_vectors() const
|
||||
{
|
||||
if (groups != GetSize(ports)) log_error("Bram %s variant %d has %d groups but only %d entries in 'ports'.\n", log_id(name), variant, groups, GetSize(ports));
|
||||
if (groups != GetSize(wrmode)) log_error("Bram %s variant %d has %d groups but only %d entries in 'wrmode'.\n", log_id(name), variant, groups, GetSize(wrmode));
|
||||
if (groups != GetSize(enable)) log_error("Bram %s variant %d has %d groups but only %d entries in 'enable'.\n", log_id(name), variant, groups, GetSize(enable));
|
||||
if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp));
|
||||
if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks));
|
||||
if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol));
|
||||
if (groups != GetSize(ports)) log_error("Bram %s variant %d has %d groups but only %d entries in 'ports'.\n", name.unescape(), variant, groups, GetSize(ports));
|
||||
if (groups != GetSize(wrmode)) log_error("Bram %s variant %d has %d groups but only %d entries in 'wrmode'.\n", name.unescape(), variant, groups, GetSize(wrmode));
|
||||
if (groups != GetSize(enable)) log_error("Bram %s variant %d has %d groups but only %d entries in 'enable'.\n", name.unescape(), variant, groups, GetSize(enable));
|
||||
if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", name.unescape(), variant, groups, GetSize(transp));
|
||||
if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", name.unescape(), variant, groups, GetSize(clocks));
|
||||
if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", name.unescape(), variant, groups, GetSize(clkpol));
|
||||
|
||||
int group = 0;
|
||||
for (auto e : enable)
|
||||
if (e > dbits) log_error("Bram %s variant %d group %d has %d enable bits but only %d dbits.\n", log_id(name), variant, group, e, dbits);
|
||||
if (e > dbits) log_error("Bram %s variant %d group %d has %d enable bits but only %d dbits.\n", name.unescape(), variant, group, e, dbits);
|
||||
}
|
||||
|
||||
vector<portinfo_t> make_portinfos() const
|
||||
|
|
@ -100,7 +100,7 @@ struct rules_t
|
|||
log_assert(name == other.name);
|
||||
|
||||
if (groups != other.groups)
|
||||
log_error("Bram %s variants %d and %d have different values for 'groups'.\n", log_id(name), variant, other.variant);
|
||||
log_error("Bram %s variants %d and %d have different values for 'groups'.\n", name.unescape(), variant, other.variant);
|
||||
|
||||
if (abits != other.abits)
|
||||
variant_params[ID::CFG_ABITS] = abits;
|
||||
|
|
@ -112,7 +112,7 @@ struct rules_t
|
|||
for (int i = 0; i < groups; i++)
|
||||
{
|
||||
if (ports[i] != other.ports[i])
|
||||
log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
|
||||
log_error("Bram %s variants %d and %d have different number of %c-ports.\n", name.unescape(), variant, other.variant, 'A'+i);
|
||||
if (wrmode[i] != other.wrmode[i])
|
||||
variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
|
||||
if (enable[i] != other.enable[i])
|
||||
|
|
@ -428,7 +428,7 @@ bool replace_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals, const
|
|||
transp_max = max(transp_max, pi.transp);
|
||||
}
|
||||
|
||||
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
|
||||
log(" Mapping to bram type %s (variant %d):\n", bram.name.unescape(), bram.variant);
|
||||
// bram.dump_config();
|
||||
|
||||
std::vector<int> shuffle_map;
|
||||
|
|
@ -715,21 +715,21 @@ grow_read_ports:;
|
|||
for (auto it : match.min_limits) {
|
||||
if (!match_properties.count(it.first))
|
||||
log_error("Unknown property '%s' in match rule for bram type %s.\n",
|
||||
it.first.c_str(), log_id(match.name));
|
||||
it.first.c_str(), match.name.unescape());
|
||||
if (match_properties[it.first] >= it.second)
|
||||
continue;
|
||||
log(" Rule for bram type %s rejected: requirement 'min %s %d' not met.\n",
|
||||
log_id(match.name), it.first.c_str(), it.second);
|
||||
match.name.unescape(), it.first.c_str(), it.second);
|
||||
return false;
|
||||
}
|
||||
for (auto it : match.max_limits) {
|
||||
if (!match_properties.count(it.first))
|
||||
log_error("Unknown property '%s' in match rule for bram type %s.\n",
|
||||
it.first.c_str(), log_id(match.name));
|
||||
it.first.c_str(), match.name.unescape());
|
||||
if (match_properties[it.first] <= it.second)
|
||||
continue;
|
||||
log(" Rule for bram type %s rejected: requirement 'max %s %d' not met.\n",
|
||||
log_id(match.name), it.first.c_str(), it.second);
|
||||
match.name.unescape(), it.first.c_str(), it.second);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -759,13 +759,13 @@ grow_read_ports:;
|
|||
if (!exists)
|
||||
ss << "!";
|
||||
IdString key = std::get<1>(sums.front());
|
||||
ss << log_id(key);
|
||||
ss << key.unescape();
|
||||
const Const &value = rules.map_case(std::get<2>(sums.front()));
|
||||
if (exists && value != Const(1))
|
||||
ss << "=\"" << value.decode_string() << "\"";
|
||||
|
||||
log(" Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
|
||||
log_id(match.name), ss.str().c_str());
|
||||
match.name.unescape(), ss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -874,7 +874,7 @@ grow_read_ports:;
|
|||
for (int dupidx = 0; dupidx < dup_count; dupidx++)
|
||||
{
|
||||
Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", mem.memid, grid_d, grid_a, dupidx)), bram.name);
|
||||
log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c));
|
||||
log(" Creating %s cell at grid position <%d %d %d>: %s\n", bram.name.unescape(), grid_d, grid_a, dupidx, c);
|
||||
|
||||
for (auto &vp : variant_params)
|
||||
c->setParam(vp.first, vp.second);
|
||||
|
|
@ -1004,7 +1004,7 @@ grow_read_ports:;
|
|||
|
||||
void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
||||
{
|
||||
log("Processing %s.%s:\n", log_id(mem.module), log_id(mem.memid));
|
||||
log("Processing %s.%s:\n", mem.module, mem.memid.unescape());
|
||||
mem.narrow();
|
||||
|
||||
bool cell_init = !mem.inits.empty();
|
||||
|
|
@ -1031,7 +1031,7 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
auto &match = rules.matches.at(i);
|
||||
|
||||
if (!rules.brams.count(rules.matches[i].name))
|
||||
log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name));
|
||||
log_error("No bram description for resource %s found!\n", rules.matches[i].name.unescape());
|
||||
|
||||
for (int vi = 0; vi < GetSize(rules.brams.at(match.name)); vi++)
|
||||
{
|
||||
|
|
@ -1047,7 +1047,7 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
avail_wr_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
|
||||
}
|
||||
|
||||
log(" Checking rule #%d for bram type %s (variant %d):\n", i+1, log_id(bram.name), bram.variant);
|
||||
log(" Checking rule #%d for bram type %s (variant %d):\n", i+1, bram.name.unescape(), bram.variant);
|
||||
log(" Bram geometry: abits=%d dbits=%d wports=%d rports=%d\n", bram.abits, bram.dbits, avail_wr_ports, avail_rd_ports);
|
||||
|
||||
int dups = avail_rd_ports ? (match_properties["rports"] + avail_rd_ports - 1) / avail_rd_ports : 1;
|
||||
|
|
@ -1077,11 +1077,11 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
goto next_match_rule;
|
||||
|
||||
log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n",
|
||||
log_id(match.name), awaste, dwaste, bwaste, waste, efficiency);
|
||||
match.name.unescape(), awaste, dwaste, bwaste, waste, efficiency);
|
||||
|
||||
if (cell_init && bram.init == 0) {
|
||||
log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n",
|
||||
i+1, log_id(bram.name), bram.variant);
|
||||
i+1, bram.name.unescape(), bram.variant);
|
||||
goto next_match_rule;
|
||||
}
|
||||
|
||||
|
|
@ -1090,11 +1090,11 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
continue;
|
||||
if (!match_properties.count(it.first))
|
||||
log_error("Unknown property '%s' in match rule for bram type %s.\n",
|
||||
it.first.c_str(), log_id(match.name));
|
||||
it.first.c_str(), match.name.unescape());
|
||||
if (match_properties[it.first] >= it.second)
|
||||
continue;
|
||||
log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met.\n",
|
||||
i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
|
||||
i+1, bram.name.unescape(), bram.variant, it.first.c_str(), it.second);
|
||||
goto next_match_rule;
|
||||
}
|
||||
|
||||
|
|
@ -1103,11 +1103,11 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
continue;
|
||||
if (!match_properties.count(it.first))
|
||||
log_error("Unknown property '%s' in match rule for bram type %s.\n",
|
||||
it.first.c_str(), log_id(match.name));
|
||||
it.first.c_str(), match.name.unescape());
|
||||
if (match_properties[it.first] <= it.second)
|
||||
continue;
|
||||
log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'max %s %d' not met.\n",
|
||||
i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
|
||||
i+1, bram.name.unescape(), bram.variant, it.first.c_str(), it.second);
|
||||
goto next_match_rule;
|
||||
}
|
||||
|
||||
|
|
@ -1137,18 +1137,18 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
if (!exists)
|
||||
ss << "!";
|
||||
IdString key = std::get<1>(sums.front());
|
||||
ss << log_id(key);
|
||||
ss << key.unescape();
|
||||
const Const &value = rules.map_case(std::get<2>(sums.front()));
|
||||
if (exists && value != Const(1))
|
||||
ss << "=\"" << value.decode_string() << "\"";
|
||||
|
||||
log(" Rule for bram type %s (variant %d) rejected: requirement 'attribute %s ...' not met.\n",
|
||||
log_id(bram.name), bram.variant, ss.str().c_str());
|
||||
bram.name.unescape(), bram.variant, ss.str().c_str());
|
||||
goto next_match_rule;
|
||||
}
|
||||
}
|
||||
|
||||
log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
|
||||
log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, bram.name.unescape(), bram.variant);
|
||||
|
||||
if (or_next_if_better || !best_rule_cache.empty())
|
||||
{
|
||||
|
|
@ -1156,7 +1156,7 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
log_error("Found 'or_next_if_better' in last match rule.\n");
|
||||
|
||||
if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 1)) {
|
||||
log(" Mapping to bram type %s failed.\n", log_id(match.name));
|
||||
log(" Mapping to bram type %s failed.\n", match.name.unescape());
|
||||
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
|
||||
goto next_match_rule;
|
||||
}
|
||||
|
|
@ -1183,12 +1183,12 @@ void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
|
|||
|
||||
auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second);
|
||||
if (!replace_memory(mem, rules, initvals, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
|
||||
log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant);
|
||||
log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", best_bram.name.unescape(), best_bram.variant);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 0)) {
|
||||
log(" Mapping to bram type %s failed.\n", log_id(match.name));
|
||||
log(" Mapping to bram type %s failed.\n", match.name.unescape());
|
||||
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
|
||||
goto next_match_rule;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct PassOptions {
|
|||
bool no_auto_distributed;
|
||||
bool no_auto_block;
|
||||
bool no_auto_huge;
|
||||
bool force_params;
|
||||
double logic_cost_rom;
|
||||
double logic_cost_ram;
|
||||
};
|
||||
|
|
@ -203,7 +204,7 @@ struct MemMapping {
|
|||
if (!check_init(rdef))
|
||||
continue;
|
||||
if (rdef.prune_rom && mem.wr_ports.empty()) {
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: ROM mapping disabled (prune_rom set)\n", log_id(mem.module->name), log_id(mem.memid), log_id(rdef.id));
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: ROM mapping disabled (prune_rom set)\n", mem.module->name.unescape(), mem.memid.unescape(), rdef.id.unescape());
|
||||
continue;
|
||||
}
|
||||
MemConfig cfg;
|
||||
|
|
@ -322,7 +323,7 @@ struct MemMapping {
|
|||
|
||||
void log_reject(const Ram &ram, std::string message) {
|
||||
if(ys_debug(1)) {
|
||||
rejected_cfg_debug_msgs += stringf("can't map to to %s: ", log_id(ram.id));
|
||||
rejected_cfg_debug_msgs += stringf("can't map to to %s: ", ram.id.unescape());
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
|
|
@ -337,7 +338,7 @@ struct MemMapping {
|
|||
rejected_cfg_debug_msgs += portname;
|
||||
first = false;
|
||||
}
|
||||
rejected_cfg_debug_msgs += stringf("] of %s: ", log_id(ram.id));
|
||||
rejected_cfg_debug_msgs += stringf("] of %s: ", ram.id.unescape());
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
|
|
@ -360,7 +361,7 @@ struct MemMapping {
|
|||
rejected_cfg_debug_msgs += portname;
|
||||
first = false;
|
||||
}
|
||||
rejected_cfg_debug_msgs += stringf("] of %s: ", log_id(ram.id));
|
||||
rejected_cfg_debug_msgs += stringf("] of %s: ", ram.id.unescape());
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
|
|
@ -379,7 +380,7 @@ void MemMapping::dump_configs(int stage) {
|
|||
default:
|
||||
abort();
|
||||
}
|
||||
log_debug("Memory %s.%s mapping candidates (%s):\n", log_id(mem.module->name), log_id(mem.memid), stage_name);
|
||||
log_debug("Memory %s.%s mapping candidates (%s):\n", mem.module->name.unescape(), mem.memid.unescape(), stage_name);
|
||||
if (logic_ok) {
|
||||
log_debug("- logic fallback\n");
|
||||
log_debug(" - cost: %f\n", logic_cost);
|
||||
|
|
@ -390,7 +391,7 @@ void MemMapping::dump_configs(int stage) {
|
|||
}
|
||||
|
||||
void MemMapping::dump_config(MemConfig &cfg) {
|
||||
log_debug("- %s:\n", log_id(cfg.def->id));
|
||||
log_debug("- %s:\n", cfg.def->id.unescape());
|
||||
for (auto &it: cfg.def->options)
|
||||
log_debug(" - option %s %s\n", it.first, log_const(it.second));
|
||||
log_debug(" - emulation score: %d\n", cfg.score_emu);
|
||||
|
|
@ -526,7 +527,7 @@ void MemMapping::determine_style() {
|
|||
auto find_attr = search_for_attribute(mem, ID::lram);
|
||||
if (find_attr.first && find_attr.second.as_bool()) {
|
||||
kind = RamKind::Huge;
|
||||
log("found attribute 'lram' on memory %s.%s, forced mapping to huge RAM\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
log("found attribute 'lram' on memory %s.%s, forced mapping to huge RAM\n", mem.module->name.unescape(), mem.memid.unescape());
|
||||
return;
|
||||
}
|
||||
for (auto attr: {ID::ram_block, ID::rom_block, ID::ram_style, ID::rom_style, ID::ramstyle, ID::romstyle, ID::syn_ramstyle, ID::syn_romstyle}) {
|
||||
|
|
@ -535,7 +536,7 @@ void MemMapping::determine_style() {
|
|||
Const val = find_attr.second;
|
||||
if (val == 1) {
|
||||
kind = RamKind::NotLogic;
|
||||
log("found attribute '%s = 1' on memory %s.%s, disabled mapping to FF\n", log_id(attr), log_id(mem.module->name), log_id(mem.memid));
|
||||
log("found attribute '%s = 1' on memory %s.%s, disabled mapping to FF\n", attr.unescape(), mem.module->name.unescape(), mem.memid.unescape());
|
||||
return;
|
||||
}
|
||||
std::string val_s = val.decode_string();
|
||||
|
|
@ -548,20 +549,20 @@ void MemMapping::determine_style() {
|
|||
// Nothing.
|
||||
} else if (val_s == "logic" || val_s == "registers") {
|
||||
kind = RamKind::Logic;
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to FF\n", log_id(attr), val_s, log_id(mem.module->name), log_id(mem.memid));
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to FF\n", attr.unescape(), val_s, mem.module->name.unescape(), mem.memid.unescape());
|
||||
} else if (val_s == "distributed") {
|
||||
kind = RamKind::Distributed;
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to distributed RAM\n", log_id(attr), val_s, log_id(mem.module->name), log_id(mem.memid));
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to distributed RAM\n", attr.unescape(), val_s, mem.module->name.unescape(), mem.memid.unescape());
|
||||
} else if (val_s == "block" || val_s == "block_ram" || val_s == "ebr") {
|
||||
kind = RamKind::Block;
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to block RAM\n", log_id(attr), val_s, log_id(mem.module->name), log_id(mem.memid));
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to block RAM\n", attr.unescape(), val_s, mem.module->name.unescape(), mem.memid.unescape());
|
||||
} else if (val_s == "huge" || val_s == "ultra") {
|
||||
kind = RamKind::Huge;
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to huge RAM\n", log_id(attr), val_s, log_id(mem.module->name), log_id(mem.memid));
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to huge RAM\n", attr.unescape(), val_s, mem.module->name.unescape(), mem.memid.unescape());
|
||||
} else {
|
||||
kind = RamKind::NotLogic;
|
||||
style = val_s;
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to %s RAM\n", log_id(attr), val_s, log_id(mem.module->name), log_id(mem.memid), val_s);
|
||||
log("found attribute '%s = %s' on memory %s.%s, forced mapping to %s RAM\n", attr.unescape(), val_s, mem.module->name.unescape(), mem.memid.unescape(), val_s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1859,7 +1860,7 @@ void MemMapping::emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, cons
|
|||
cell->setParam(stringf("\\PORT_%s_WR_BE_WIDTH", name), GetSize(hw_wren));
|
||||
} else {
|
||||
cell->setPort(stringf("\\PORT_%s_WR_EN", name), hw_wren);
|
||||
if (cfg.def->byte != 0 && cfg.def->width_mode != WidthMode::Single)
|
||||
if (cfg.def->byte != 0 && (cfg.def->width_mode != WidthMode::Single || opts.force_params))
|
||||
cell->setParam(stringf("\\PORT_%s_WR_EN_WIDTH", name), GetSize(hw_wren));
|
||||
}
|
||||
}
|
||||
|
|
@ -1990,7 +1991,7 @@ void MemMapping::emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, cons
|
|||
}
|
||||
|
||||
void MemMapping::emit(const MemConfig &cfg) {
|
||||
log("mapping memory %s.%s via %s\n", log_id(mem.module->name), log_id(mem.memid), log_id(cfg.def->id));
|
||||
log("mapping memory %s.%s via %s\n", mem.module->name.unescape(), mem.memid.unescape(), cfg.def->id.unescape());
|
||||
// First, handle emulations.
|
||||
if (cfg.emu_read_first)
|
||||
mem.emulate_read_first(&worker.initvals);
|
||||
|
|
@ -2068,8 +2069,10 @@ void MemMapping::emit(const MemConfig &cfg) {
|
|||
std::vector<Cell *> cells;
|
||||
for (int rd = 0; rd < cfg.repl_d; rd++) {
|
||||
Cell *cell = mem.module->addCell(stringf("%s.%d.%d", mem.memid, rp, rd), cfg.def->id);
|
||||
if (cfg.def->width_mode == WidthMode::Global)
|
||||
if (cfg.def->width_mode == WidthMode::Global || opts.force_params)
|
||||
cell->setParam(ID::WIDTH, cfg.def->dbits[cfg.base_width_log2]);
|
||||
if (opts.force_params)
|
||||
cell->setParam(ID::ABITS, cfg.def->abits);
|
||||
if (cfg.def->widthscale) {
|
||||
std::vector<State> val;
|
||||
for (auto &bit: init_swz.bits[rd])
|
||||
|
|
@ -2179,6 +2182,9 @@ struct MemoryLibMapPass : public Pass {
|
|||
log(" Disables automatic mapping of given kind of RAMs. Manual mapping\n");
|
||||
log(" (using ram_style or other attributes) is still supported.\n");
|
||||
log("\n");
|
||||
log(" -force-params\n");
|
||||
log(" Always generate memories with WIDTH and ABITS parameters.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
|
@ -2188,6 +2194,7 @@ struct MemoryLibMapPass : public Pass {
|
|||
opts.no_auto_distributed = false;
|
||||
opts.no_auto_block = false;
|
||||
opts.no_auto_huge = false;
|
||||
opts.force_params = false;
|
||||
opts.logic_cost_ram = 1.0;
|
||||
opts.logic_cost_rom = 1.0/16.0;
|
||||
log_header(design, "Executing MEMORY_LIBMAP pass (mapping memories to cells).\n");
|
||||
|
|
@ -2214,6 +2221,10 @@ struct MemoryLibMapPass : public Pass {
|
|||
opts.no_auto_huge = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-force-params") {
|
||||
opts.force_params = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-logic-cost-rom" && argidx+1 < args.size()) {
|
||||
opts.logic_cost_rom = strtod(args[++argidx].c_str(), nullptr);
|
||||
continue;
|
||||
|
|
@ -2241,9 +2252,9 @@ struct MemoryLibMapPass : public Pass {
|
|||
int best = map.logic_cost;
|
||||
if (!map.logic_ok) {
|
||||
if (map.cfgs.empty()) {
|
||||
log_debug("Rejected candidates for mapping memory %s.%s:\n", log_id(module->name), log_id(mem.memid));
|
||||
log_debug("Rejected candidates for mapping memory %s.%s:\n", module->name.unescape(), mem.memid.unescape());
|
||||
log_debug("%s", map.rejected_cfg_debug_msgs);
|
||||
log_error("no valid mapping found for memory %s.%s\n", log_id(module->name), log_id(mem.memid));
|
||||
log_error("no valid mapping found for memory %s.%s\n", module->name.unescape(), mem.memid.unescape());
|
||||
}
|
||||
idx = 0;
|
||||
best = map.cfgs[0].cost;
|
||||
|
|
@ -2255,7 +2266,7 @@ struct MemoryLibMapPass : public Pass {
|
|||
}
|
||||
}
|
||||
if (idx == -1) {
|
||||
log("using FF mapping for memory %s.%s\n", log_id(module->name), log_id(mem.memid));
|
||||
log("using FF mapping for memory %s.%s\n", module->name.unescape(), mem.memid.unescape());
|
||||
} else {
|
||||
map.emit(map.cfgs[idx]);
|
||||
// Rebuild indices after modifying module
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ struct MemoryMemxPass : public Pass {
|
|||
{
|
||||
if (port.clk_enable)
|
||||
log_error("Memory %s.%s has a synchronous read port. Synchronous read ports are not supported by memory_memx!\n",
|
||||
log_id(module), log_id(mem.memid));
|
||||
module, mem.memid.unescape());
|
||||
|
||||
SigSpec addr_ok = make_addr_check(mem, port.addr);
|
||||
Wire *raw_rdata = module->addWire(NEW_ID, GetSize(port.data));
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ struct MemoryShareWorker
|
|||
if (GetSize(mem.rd_ports) <= 1)
|
||||
return false;
|
||||
|
||||
log("Consolidating read ports of memory %s.%s by address:\n", log_id(module), log_id(mem.memid));
|
||||
log("Consolidating read ports of memory %s.%s by address:\n", module, mem.memid.unescape());
|
||||
|
||||
bool changed = false;
|
||||
int abits = 0;
|
||||
|
|
@ -197,7 +197,7 @@ struct MemoryShareWorker
|
|||
if (GetSize(mem.wr_ports) <= 1)
|
||||
return false;
|
||||
|
||||
log("Consolidating write ports of memory %s.%s by address:\n", log_id(module), log_id(mem.memid));
|
||||
log("Consolidating write ports of memory %s.%s by address:\n", module, mem.memid.unescape());
|
||||
|
||||
bool changed = false;
|
||||
int abits = 0;
|
||||
|
|
@ -316,7 +316,7 @@ struct MemoryShareWorker
|
|||
if (eligible_ports.size() <= 1)
|
||||
return;
|
||||
|
||||
log("Consolidating write ports of memory %s.%s using sat-based resource sharing:\n", log_id(module), log_id(mem.memid));
|
||||
log("Consolidating write ports of memory %s.%s using sat-based resource sharing:\n", module, mem.memid.unescape());
|
||||
|
||||
// Group eligible ports by clock domain and width.
|
||||
|
||||
|
|
|
|||
1
passes/opt/.gitignore
vendored
1
passes/opt/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/peepopt*_pm.h
|
||||
97
passes/opt/CMakeLists.txt
Normal file
97
passes/opt/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
add_subdirectory(opt_clean)
|
||||
|
||||
yosys_pass(opt_merge
|
||||
opt_merge.cc
|
||||
)
|
||||
yosys_pass(opt_mem_widen
|
||||
opt_mem_widen.cc
|
||||
)
|
||||
yosys_pass(opt_muxtree
|
||||
opt_muxtree.cc
|
||||
)
|
||||
yosys_pass(opt_reduce
|
||||
opt_reduce.cc
|
||||
)
|
||||
yosys_pass(opt_dff
|
||||
opt_dff.cc
|
||||
REQUIRES
|
||||
simplemap
|
||||
)
|
||||
yosys_pass(opt_share
|
||||
opt_share.cc
|
||||
)
|
||||
yosys_pass(opt_expr
|
||||
opt_expr.cc
|
||||
)
|
||||
yosys_pass(opt_hier
|
||||
opt_hier.cc
|
||||
)
|
||||
yosys_pass(opt
|
||||
opt.cc
|
||||
REQUIRES
|
||||
opt_clean
|
||||
opt_dff
|
||||
opt_expr
|
||||
opt_hier
|
||||
opt_merge
|
||||
opt_muxtree
|
||||
opt_reduce
|
||||
opt_share
|
||||
)
|
||||
|
||||
yosys_pass(opt_mem
|
||||
opt_mem.cc
|
||||
)
|
||||
yosys_pass(opt_mem_feedback
|
||||
opt_mem_feedback.cc
|
||||
)
|
||||
yosys_pass(opt_mem_priority
|
||||
opt_mem_priority.cc
|
||||
)
|
||||
|
||||
yosys_pass(share
|
||||
share.cc
|
||||
)
|
||||
yosys_pass(wreduce
|
||||
wreduce.cc
|
||||
)
|
||||
yosys_pass(opt_demorgan
|
||||
opt_demorgan.cc
|
||||
)
|
||||
yosys_pass(rmports
|
||||
rmports.cc
|
||||
)
|
||||
yosys_pass(opt_lut
|
||||
opt_lut.cc
|
||||
)
|
||||
yosys_pass(opt_lut_ins
|
||||
opt_lut_ins.cc
|
||||
)
|
||||
yosys_pass(opt_ffinv
|
||||
opt_ffinv.cc
|
||||
)
|
||||
yosys_pass(pmux2shiftx
|
||||
pmux2shiftx.cc
|
||||
)
|
||||
yosys_pass(muxpack
|
||||
muxpack.cc
|
||||
)
|
||||
yosys_pass(opt_balance_tree
|
||||
opt_balance_tree.cc
|
||||
)
|
||||
|
||||
pmgen_command(peepopt
|
||||
peepopt_shiftmul_right.pmg
|
||||
peepopt_shiftmul_left.pmg
|
||||
peepopt_shiftadd.pmg
|
||||
peepopt_shiftpow2.pmg
|
||||
peepopt_muldiv.pmg
|
||||
peepopt_muldiv_c.pmg
|
||||
peepopt_formal_clockgateff.pmg
|
||||
PREFIX
|
||||
peepopt
|
||||
)
|
||||
yosys_pass(peepopt
|
||||
peepopt.cc
|
||||
${PMGEN_peepopt_OUTPUT}
|
||||
)
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
|
||||
OBJS += passes/opt/opt.o
|
||||
OBJS += passes/opt/opt_merge.o
|
||||
OBJS += passes/opt/opt_mem.o
|
||||
OBJS += passes/opt/opt_mem_feedback.o
|
||||
OBJS += passes/opt/opt_mem_priority.o
|
||||
OBJS += passes/opt/opt_mem_widen.o
|
||||
OBJS += passes/opt/opt_muxtree.o
|
||||
OBJS += passes/opt/opt_reduce.o
|
||||
OBJS += passes/opt/opt_dff.o
|
||||
OBJS += passes/opt/opt_share.o
|
||||
OBJS += passes/opt/opt_clean.o
|
||||
OBJS += passes/opt/opt_expr.o
|
||||
OBJS += passes/opt/opt_hier.o
|
||||
|
||||
OBJS += passes/opt/share.o
|
||||
OBJS += passes/opt/wreduce.o
|
||||
OBJS += passes/opt/opt_demorgan.o
|
||||
OBJS += passes/opt/rmports.o
|
||||
OBJS += passes/opt/opt_lut.o
|
||||
OBJS += passes/opt/opt_lut_ins.o
|
||||
OBJS += passes/opt/opt_ffinv.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
OBJS += passes/opt/muxpack.o
|
||||
|
||||
OBJS += passes/opt/opt_addcin.o
|
||||
OBJS += passes/opt/opt_andor_pmux.o
|
||||
OBJS += passes/opt/opt_argmax.o
|
||||
OBJS += passes/opt/opt_balance_tree.o
|
||||
OBJS += passes/opt/opt_parallel_prefix.o
|
||||
OBJS += passes/opt/opt_prienc.o
|
||||
|
||||
OBJS += passes/opt/peepopt.o
|
||||
GENFILES += passes/opt/peepopt_pm.h
|
||||
passes/opt/peepopt.o: passes/opt/peepopt_pm.h
|
||||
$(eval $(call add_extra_objs,passes/opt/peepopt_pm.h))
|
||||
|
||||
PEEPOPT_PATTERN = passes/opt/peepopt_shiftmul_right.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftmul_left.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftadd.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv_c.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_modshr_onehot.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_addsub_c.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muxorder.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_sub_neg.pmg
|
||||
|
||||
passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||
|
|
@ -38,6 +38,9 @@ struct ExclusiveDatabase
|
|||
pool<Cell*> reduce_or;
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type == ID($eq)) {
|
||||
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
|
||||
if (GetSize(y_sig) == 0)
|
||||
continue;
|
||||
nonconst_sig = sigmap(cell->getPort(ID::A));
|
||||
const_sig = sigmap(cell->getPort(ID::B));
|
||||
if (!const_sig.is_fully_const()) {
|
||||
|
|
@ -45,12 +48,15 @@ struct ExclusiveDatabase
|
|||
continue;
|
||||
std::swap(nonconst_sig, const_sig);
|
||||
}
|
||||
y_port = sigmap(cell->getPort(ID::Y));
|
||||
y_port = y_sig[0];
|
||||
}
|
||||
else if (cell->type == ID($logic_not)) {
|
||||
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
|
||||
if (GetSize(y_sig) == 0)
|
||||
continue;
|
||||
nonconst_sig = sigmap(cell->getPort(ID::A));
|
||||
const_sig = Const(State::S0, GetSize(nonconst_sig));
|
||||
y_port = sigmap(cell->getPort(ID::Y));
|
||||
y_port = y_sig[0];
|
||||
}
|
||||
else if (cell->type == ID($reduce_or)) {
|
||||
reduce_or.insert(cell);
|
||||
|
|
@ -84,7 +90,10 @@ struct ExclusiveDatabase
|
|||
}
|
||||
if (nonconst_sig.empty())
|
||||
continue;
|
||||
y_port = sigmap(cell->getPort(ID::Y));
|
||||
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
|
||||
if (GetSize(y_sig) == 0)
|
||||
continue;
|
||||
y_port = y_sig[0];
|
||||
sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values));
|
||||
}
|
||||
}
|
||||
|
|
@ -184,7 +193,7 @@ struct MuxpackWorker
|
|||
{
|
||||
for (auto cell : candidate_cells)
|
||||
{
|
||||
log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
log_debug("Considering %s (%s)\n", cell, cell->type.unescape());
|
||||
|
||||
SigSpec a_sig = sigmap(cell->getPort(ID::A));
|
||||
if (cell->type == ID($mux)) {
|
||||
|
|
@ -254,7 +263,6 @@ struct MuxpackWorker
|
|||
int cases = GetSize(chain) - cursor;
|
||||
|
||||
Cell *first_cell = chain[cursor];
|
||||
dict<int, SigBit> taps_dict;
|
||||
|
||||
if (cases < 2) {
|
||||
cursor++;
|
||||
|
|
@ -264,7 +272,7 @@ struct MuxpackWorker
|
|||
Cell *last_cell = chain[cursor+cases-1];
|
||||
|
||||
log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n",
|
||||
log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases);
|
||||
module, first_cell, module, last_cell, cases);
|
||||
|
||||
mux_count += cases;
|
||||
pmux_count += 1;
|
||||
|
|
|
|||
|
|
@ -105,15 +105,15 @@ struct OptBalanceTreeWorker {
|
|||
// Base case: if we have only one source, return it
|
||||
if (sources.size() == 1)
|
||||
return sources[0];
|
||||
|
||||
|
||||
// Base case: if we have two sources, create a single cell
|
||||
if (sources.size() == 2) {
|
||||
// Create a new cell of the same type
|
||||
Cell* new_cell = module->addCell(NEW_ID2_SUFFIX("tree"), cell_type);
|
||||
|
||||
|
||||
// Copy attributes from reference cell
|
||||
new_cell->attributes = cell->attributes;
|
||||
|
||||
|
||||
// Create output wire
|
||||
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
if (cell_type == ID($add))
|
||||
|
|
@ -121,7 +121,7 @@ struct OptBalanceTreeWorker {
|
|||
else if (cell_type == ID($mul))
|
||||
out_width = sources[0].size() + sources[1].size();
|
||||
Wire* out_wire = module->addWire(NEW_ID2_SUFFIX("tree_out"), out_width);
|
||||
|
||||
|
||||
// Connect ports and fix up parameters
|
||||
new_cell->setPort(ID::A, sources[0]);
|
||||
new_cell->setPort(ID::B, sources[1]);
|
||||
|
|
@ -129,26 +129,26 @@ struct OptBalanceTreeWorker {
|
|||
new_cell->fixup_parameters();
|
||||
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
|
||||
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
|
||||
|
||||
|
||||
// Update count and return output wire
|
||||
cell_count[cell_type]++;
|
||||
return out_wire;
|
||||
}
|
||||
|
||||
|
||||
// Recursive case: split sources into two groups and create subtrees
|
||||
int mid = (sources.size() + 1) / 2;
|
||||
vector<SigSpec> left_sources(sources.begin(), sources.begin() + mid);
|
||||
vector<SigSpec> right_sources(sources.begin() + mid, sources.end());
|
||||
|
||||
|
||||
SigSpec left_tree = create_balanced_tree(left_sources, cell_type, cell);
|
||||
SigSpec right_tree = create_balanced_tree(right_sources, cell_type, cell);
|
||||
|
||||
|
||||
// Create a cell to combine the two subtrees
|
||||
Cell* new_cell = module->addCell(NEW_ID2_SUFFIX("tree"), cell_type);
|
||||
|
||||
|
||||
// Copy attributes from reference cell
|
||||
new_cell->attributes = cell->attributes;
|
||||
|
||||
|
||||
// Create output wire
|
||||
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
if (cell_type == ID($add))
|
||||
|
|
@ -156,7 +156,7 @@ struct OptBalanceTreeWorker {
|
|||
else if (cell_type == ID($mul))
|
||||
out_width = left_tree.size() + right_tree.size();
|
||||
Wire* out_wire = module->addWire(NEW_ID2_SUFFIX("tree_out"), out_width);
|
||||
|
||||
|
||||
// Connect ports and fix up parameters
|
||||
new_cell->setPort(ID::A, left_tree);
|
||||
new_cell->setPort(ID::B, right_tree);
|
||||
|
|
@ -164,7 +164,7 @@ struct OptBalanceTreeWorker {
|
|||
new_cell->fixup_parameters();
|
||||
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
|
||||
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
|
||||
|
||||
|
||||
// Update count and return output wire
|
||||
cell_count[cell_type]++;
|
||||
return out_wire;
|
||||
|
|
@ -512,8 +512,8 @@ struct OptBalanceTreeWorker {
|
|||
if (inner_cells)
|
||||
{
|
||||
// Create a tree
|
||||
log_debug(" Creating tree for %s with %d sources and %d inner cells...\n", log_id(head_cell), GetSize(sources), inner_cells);
|
||||
|
||||
log_debug(" Creating tree for %s with %d sources and %d inner cells...\n", head_cell, GetSize(sources), inner_cells);
|
||||
|
||||
// Build a vector of all source signals
|
||||
vector<SigSpec> source_signals;
|
||||
vector<bool> signed_flags;
|
||||
|
|
@ -528,10 +528,10 @@ struct OptBalanceTreeWorker {
|
|||
if (!std::all_of(signed_flags.begin(), signed_flags.end(), [&](bool flag) { return flag == signed_flags[0]; })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Create the balanced tree
|
||||
SigSpec tree_output = create_balanced_tree(source_signals, cell_type, head_cell);
|
||||
|
||||
|
||||
// Connect the tree output to the head cell's output
|
||||
SigSpec head_output = sigmap(head_cell->getPort(ID::Y));
|
||||
int connect_width = std::min(head_output.size(), tree_output.size());
|
||||
|
|
@ -546,7 +546,7 @@ struct OptBalanceTreeWorker {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove all consumed cells, which now have been replaced by trees
|
||||
for (auto cell : consumed_cells)
|
||||
module->remove(cell);
|
||||
|
|
@ -604,7 +604,7 @@ struct OptBalanceTreePass : public Pass {
|
|||
|
||||
// Log stats
|
||||
for (auto cell_type : cell_types)
|
||||
log("Converted %d %s cells into trees.\n", cell_count[cell_type], log_id(cell_type));
|
||||
log("Converted %d %s cells into trees.\n", cell_count[cell_type], cell_type.unescape());
|
||||
if (std::find(cell_types.begin(), cell_types.end(), ID($add)) != cell_types.end())
|
||||
log("Converted %d sliced $add chains into trees.\n", sliced_add_count);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,800 +0,0 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/ffinit.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
using RTLIL::id2cstr;
|
||||
|
||||
struct keep_cache_t
|
||||
{
|
||||
Design *design;
|
||||
dict<Module*, bool> cache;
|
||||
bool purge_mode = false;
|
||||
|
||||
void reset(Design *design = nullptr, bool purge_mode = false)
|
||||
{
|
||||
this->design = design;
|
||||
this->purge_mode = purge_mode;
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
bool query(Module *module)
|
||||
{
|
||||
log_assert(design != nullptr);
|
||||
|
||||
if (module == nullptr)
|
||||
return false;
|
||||
|
||||
if (cache.count(module))
|
||||
return cache.at(module);
|
||||
|
||||
cache[module] = true;
|
||||
if (!module->get_bool_attribute(ID::keep)) {
|
||||
bool found_keep = false;
|
||||
for (auto cell : module->cells())
|
||||
if (query(cell, true /* ignore_specify */)) {
|
||||
found_keep = true;
|
||||
break;
|
||||
}
|
||||
for (auto wire : module->wires())
|
||||
if (wire->get_bool_attribute(ID::keep)) {
|
||||
found_keep = true;
|
||||
break;
|
||||
}
|
||||
cache[module] = found_keep;
|
||||
}
|
||||
|
||||
return cache[module];
|
||||
}
|
||||
|
||||
bool query(Cell *cell, bool ignore_specify = false)
|
||||
{
|
||||
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
||||
return true;
|
||||
|
||||
if (cell->type.in(ID($overwrite_tag)))
|
||||
return true;
|
||||
|
||||
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
|
||||
return true;
|
||||
|
||||
if (cell->type == ID($print) || cell->type == ID($check))
|
||||
return true;
|
||||
|
||||
if (cell->has_keep_attr())
|
||||
return true;
|
||||
|
||||
if (!purge_mode && cell->type == ID($scopeinfo))
|
||||
return true;
|
||||
|
||||
if (cell->module && cell->module->design)
|
||||
return query(cell->module->design->module(cell->type));
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
keep_cache_t keep_cache;
|
||||
CellTypes ct_reg, ct_all;
|
||||
int count_rm_cells, count_rm_wires;
|
||||
|
||||
void rmunused_module_cells(Module *module, bool verbose)
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
dict<IdString, pool<Cell*>> mem2cells;
|
||||
pool<IdString> mem_unused;
|
||||
pool<Cell*> queue, unused;
|
||||
pool<SigBit> used_raw_bits;
|
||||
dict<SigBit, pool<Cell*>> wire2driver;
|
||||
dict<SigBit, vector<string>> driver_driver_logs;
|
||||
FfInitVals ffinit(&sigmap, module);
|
||||
|
||||
SigMap raw_sigmap;
|
||||
for (auto &it : module->connections_) {
|
||||
for (int i = 0; i < GetSize(it.second); i++) {
|
||||
if (it.second[i].wire != nullptr)
|
||||
raw_sigmap.add(it.first[i], it.second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : module->memories) {
|
||||
mem_unused.insert(it.first);
|
||||
}
|
||||
|
||||
for (Cell *cell : module->cells()) {
|
||||
if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
|
||||
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||
mem2cells[mem_id].insert(cell);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections()) {
|
||||
if (ct_all.cell_known(cell->type) && !ct_all.cell_output(cell->type, it2.first))
|
||||
continue;
|
||||
for (auto raw_bit : it2.second) {
|
||||
if (raw_bit.wire == nullptr)
|
||||
continue;
|
||||
auto bit = sigmap(raw_bit);
|
||||
if (bit.wire == nullptr && ct_all.cell_known(cell->type))
|
||||
driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict "
|
||||
"for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
|
||||
log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)));
|
||||
if (bit.wire != nullptr)
|
||||
wire2driver[bit].insert(cell);
|
||||
}
|
||||
}
|
||||
if (keep_cache.query(cell))
|
||||
queue.insert(cell);
|
||||
else
|
||||
unused.insert(cell);
|
||||
}
|
||||
|
||||
for (auto &it : module->wires_) {
|
||||
Wire *wire = it.second;
|
||||
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
|
||||
for (auto bit : sigmap(wire))
|
||||
for (auto c : wire2driver[bit])
|
||||
queue.insert(c), unused.erase(c);
|
||||
for (auto raw_bit : SigSpec(wire))
|
||||
used_raw_bits.insert(raw_sigmap(raw_bit));
|
||||
}
|
||||
}
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
pool<SigBit> bits;
|
||||
pool<IdString> mems;
|
||||
for (auto cell : queue) {
|
||||
for (auto &it : cell->connections())
|
||||
if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
|
||||
for (auto bit : sigmap(it.second))
|
||||
bits.insert(bit);
|
||||
|
||||
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
|
||||
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||
if (mem_unused.count(mem_id)) {
|
||||
mem_unused.erase(mem_id);
|
||||
mems.insert(mem_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queue.clear();
|
||||
|
||||
for (auto bit : bits)
|
||||
for (auto c : wire2driver[bit])
|
||||
if (unused.count(c))
|
||||
queue.insert(c), unused.erase(c);
|
||||
|
||||
for (auto mem : mems)
|
||||
for (auto c : mem2cells[mem])
|
||||
if (unused.count(c))
|
||||
queue.insert(c), unused.erase(c);
|
||||
}
|
||||
|
||||
unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
|
||||
|
||||
for (auto cell : unused) {
|
||||
if (verbose)
|
||||
log_debug(" removing unused `%s' cell `%s'.\n", cell->type, cell->name);
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
if (cell->is_builtin_ff())
|
||||
ffinit.remove_init(cell->getPort(ID::Q));
|
||||
module->remove(cell);
|
||||
count_rm_cells++;
|
||||
}
|
||||
|
||||
for (auto it : mem_unused)
|
||||
{
|
||||
if (verbose)
|
||||
log_debug(" removing unused memory `%s'.\n", it);
|
||||
delete module->memories.at(it);
|
||||
module->memories.erase(it);
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections()) {
|
||||
if (ct_all.cell_known(cell->type) && !ct_all.cell_input(cell->type, it2.first))
|
||||
continue;
|
||||
for (auto raw_bit : raw_sigmap(it2.second))
|
||||
used_raw_bits.insert(raw_bit);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : driver_driver_logs) {
|
||||
if (used_raw_bits.count(it.first))
|
||||
for (auto msg : it.second)
|
||||
log_warning("%s\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
|
||||
{
|
||||
int count = w->attributes.size();
|
||||
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;
|
||||
}
|
||||
|
||||
// Should we pick `s2` over `s1` to represent a signal?
|
||||
bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, pool<RTLIL::Wire*> &direct_wires)
|
||||
{
|
||||
RTLIL::Wire *w1 = s1.wire;
|
||||
RTLIL::Wire *w2 = s2.wire;
|
||||
|
||||
if (w1 == NULL || w2 == NULL)
|
||||
return w2 == NULL;
|
||||
|
||||
if (w1->port_input != w2->port_input)
|
||||
return w2->port_input;
|
||||
|
||||
if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
|
||||
return !(w2->port_input && w2->port_output);
|
||||
|
||||
if (w1->name.isPublic() && w2->name.isPublic()) {
|
||||
if (regs.check(s1) != regs.check(s2))
|
||||
return regs.check(s2);
|
||||
if (direct_wires.count(w1) != direct_wires.count(w2))
|
||||
return direct_wires.count(w2) != 0;
|
||||
if (conns.check_any(s1) != conns.check_any(s2))
|
||||
return conns.check_any(s2);
|
||||
}
|
||||
|
||||
if (w1->port_output != w2->port_output)
|
||||
return w2->port_output;
|
||||
|
||||
if (w1->name[0] != w2->name[0])
|
||||
return w2->name.isPublic();
|
||||
|
||||
int attrs1 = count_nontrivial_wire_attrs(w1);
|
||||
int attrs2 = count_nontrivial_wire_attrs(w2);
|
||||
|
||||
if (attrs1 != attrs2)
|
||||
return attrs2 > attrs1;
|
||||
|
||||
return w2->name.lt_by_name(w1->name);
|
||||
}
|
||||
|
||||
bool check_public_name(RTLIL::IdString id)
|
||||
{
|
||||
if (id.begins_with("$"))
|
||||
return false;
|
||||
const std::string &id_str = id.str();
|
||||
if (id.begins_with("\\_") && (id.ends_with("_") || id_str.find("_[") != std::string::npos))
|
||||
return false;
|
||||
if (id_str.find(".$") != std::string::npos)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool unusedbitsattr_mode, bool verbose)
|
||||
{
|
||||
// `register_signals` and `connected_signals` will help us decide later on
|
||||
// on picking representatives out of groups of connected signals
|
||||
SigPool register_signals;
|
||||
SigPool connected_signals;
|
||||
if (!purge_mode)
|
||||
for (auto &it : module->cells_) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
if (ct_reg.cell_known(cell->type)) {
|
||||
bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic));
|
||||
for (auto &it2 : cell->connections())
|
||||
if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first))
|
||||
register_signals.add(it2.second);
|
||||
}
|
||||
for (auto &it2 : cell->connections())
|
||||
connected_signals.add(it2.second);
|
||||
}
|
||||
|
||||
SigMap assign_map(module);
|
||||
|
||||
// construct a pool of wires which are directly driven by a known celltype,
|
||||
// this will influence our choice of representatives
|
||||
pool<RTLIL::Wire*> direct_wires;
|
||||
{
|
||||
pool<RTLIL::SigSpec> direct_sigs;
|
||||
for (auto &it : module->cells_) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
if (ct_all.cell_known(cell->type))
|
||||
for (auto &it2 : cell->connections())
|
||||
if (ct_all.cell_output(cell->type, it2.first))
|
||||
direct_sigs.insert(assign_map(it2.second));
|
||||
}
|
||||
for (auto &it : module->wires_) {
|
||||
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
|
||||
direct_wires.insert(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
// weight all options for representatives with `compare_signals`,
|
||||
// the one that wins will be what `assign_map` maps to
|
||||
for (auto &it : module->wires_) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1);
|
||||
if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires))
|
||||
assign_map.add(s1);
|
||||
}
|
||||
}
|
||||
|
||||
// we are removing all connections
|
||||
module->connections_.clear();
|
||||
|
||||
// used signals sigmapped
|
||||
SigPool used_signals;
|
||||
// used signals pre-sigmapped
|
||||
SigPool raw_used_signals;
|
||||
// used signals sigmapped, ignoring drivers (we keep track of this to set `unused_bits`)
|
||||
SigPool used_signals_nodrivers;
|
||||
|
||||
// gather the usage information for cells
|
||||
for (auto &it : module->cells_) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections_) {
|
||||
assign_map.apply(it2.second); // modify the cell connection in place
|
||||
raw_used_signals.add(it2.second);
|
||||
used_signals.add(it2.second);
|
||||
if (!ct_all.cell_output(cell->type, it2.first))
|
||||
used_signals_nodrivers.add(it2.second);
|
||||
}
|
||||
}
|
||||
|
||||
// gather the usage information for ports, wires with `keep`,
|
||||
// also gather init bits
|
||||
dict<RTLIL::SigBit, RTLIL::State> init_bits;
|
||||
for (auto &it : module->wires_) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
if (wire->port_id > 0) {
|
||||
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
|
||||
raw_used_signals.add(sig);
|
||||
assign_map.apply(sig);
|
||||
used_signals.add(sig);
|
||||
if (!wire->port_input)
|
||||
used_signals_nodrivers.add(sig);
|
||||
}
|
||||
if (wire->get_bool_attribute(ID::keep)) {
|
||||
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
|
||||
assign_map.apply(sig);
|
||||
used_signals.add(sig);
|
||||
}
|
||||
auto it2 = wire->attributes.find(ID::init);
|
||||
if (it2 != wire->attributes.end()) {
|
||||
RTLIL::Const &val = it2->second;
|
||||
SigSpec sig = assign_map(wire);
|
||||
for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++)
|
||||
if (val[i] != State::Sx)
|
||||
init_bits[sig[i]] = val[i];
|
||||
wire->attributes.erase(it2);
|
||||
}
|
||||
}
|
||||
|
||||
// set init attributes on all wires of a connected group
|
||||
for (auto wire : module->wires()) {
|
||||
bool found = false;
|
||||
Const val(State::Sx, wire->width);
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
auto it = init_bits.find(RTLIL::SigBit(wire, i));
|
||||
if (it != init_bits.end()) {
|
||||
val.set(i, it->second);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
wire->attributes[ID::init] = val;
|
||||
}
|
||||
|
||||
// now decide for each wire if we should be deleting it
|
||||
pool<RTLIL::Wire*> del_wires_queue;
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
|
||||
log_assert(GetSize(s1) == GetSize(s2));
|
||||
|
||||
Const initval;
|
||||
if (wire->attributes.count(ID::init))
|
||||
initval = wire->attributes.at(ID::init);
|
||||
if (GetSize(initval) != GetSize(wire))
|
||||
initval.resize(GetSize(wire), State::Sx);
|
||||
if (initval.is_fully_undef())
|
||||
wire->attributes.erase(ID::init);
|
||||
|
||||
if (GetSize(wire) == 0) {
|
||||
// delete zero-width wires, unless they are module ports
|
||||
if (wire->port_id == 0)
|
||||
goto delete_this_wire;
|
||||
} else
|
||||
if (wire->port_id != 0 || wire->get_bool_attribute(ID::keep) || !initval.is_fully_undef()) {
|
||||
// do not delete anything with "keep" or module ports or initialized wires
|
||||
} else
|
||||
if (!purge_mode && check_public_name(wire->name) && (raw_used_signals.check_any(s1) || used_signals.check_any(s2) || s1 != s2)) {
|
||||
// do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
|
||||
} else
|
||||
if (!raw_used_signals.check_any(s1)) {
|
||||
// delete wires that aren't used by anything directly
|
||||
goto delete_this_wire;
|
||||
}
|
||||
|
||||
if (0)
|
||||
{
|
||||
delete_this_wire:
|
||||
del_wires_queue.insert(wire);
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::SigSig new_conn;
|
||||
for (int i = 0; i < GetSize(s1); i++)
|
||||
if (s1[i] != s2[i]) {
|
||||
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
|
||||
s2[i] = initval[i];
|
||||
initval.set(i, State::Sx);
|
||||
}
|
||||
new_conn.first.append(s1[i]);
|
||||
new_conn.second.append(s2[i]);
|
||||
}
|
||||
if (new_conn.first.size() > 0) {
|
||||
if (initval.is_fully_undef())
|
||||
wire->attributes.erase(ID::init);
|
||||
else
|
||||
wire->attributes.at(ID::init) = initval;
|
||||
module->connect(new_conn);
|
||||
}
|
||||
|
||||
if (!used_signals_nodrivers.check_all(s2)) {
|
||||
std::string unused_bits;
|
||||
for (int i = 0; i < GetSize(s2); i++) {
|
||||
if (s2[i].wire == NULL)
|
||||
continue;
|
||||
if (!used_signals_nodrivers.check(s2[i])) {
|
||||
if (!unused_bits.empty())
|
||||
unused_bits += " ";
|
||||
unused_bits += stringf("%d", i);
|
||||
}
|
||||
}
|
||||
if (unused_bits.empty() || wire->port_id != 0)
|
||||
wire->attributes.erase(ID::unused_bits);
|
||||
else if (unusedbitsattr_mode)
|
||||
wire->attributes[ID::unused_bits] = RTLIL::Const(unused_bits);
|
||||
} else {
|
||||
wire->attributes.erase(ID::unused_bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int del_temp_wires_count = 0;
|
||||
for (auto wire : del_wires_queue) {
|
||||
if (ys_debug() || (check_public_name(wire->name) && verbose))
|
||||
log_debug(" removing unused non-port wire %s.\n", wire->name);
|
||||
else
|
||||
del_temp_wires_count++;
|
||||
}
|
||||
|
||||
module->remove(del_wires_queue);
|
||||
count_rm_wires += GetSize(del_wires_queue);
|
||||
|
||||
if (verbose && del_temp_wires_count)
|
||||
log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count);
|
||||
|
||||
if (!del_wires_queue.empty())
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
return !del_wires_queue.empty();
|
||||
}
|
||||
|
||||
bool rmunused_module_init(RTLIL::Module *module, bool verbose)
|
||||
{
|
||||
bool did_something = false;
|
||||
CellTypes fftypes;
|
||||
fftypes.setup_internals_mem();
|
||||
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, State> qbits;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
if (fftypes.cell_known(cell->type) && cell->hasPort(ID::Q))
|
||||
{
|
||||
SigSpec sig = cell->getPort(ID::Q);
|
||||
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
{
|
||||
SigBit bit = sig[i];
|
||||
|
||||
if (bit.wire == nullptr || bit.wire->attributes.count(ID::init) == 0)
|
||||
continue;
|
||||
|
||||
Const init = bit.wire->attributes.at(ID::init);
|
||||
|
||||
if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz)
|
||||
continue;
|
||||
|
||||
sigmap.add(bit);
|
||||
qbits[bit] = init[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (wire->attributes.count(ID::init) == 0)
|
||||
continue;
|
||||
|
||||
Const init = wire->attributes.at(ID::init);
|
||||
|
||||
for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++)
|
||||
{
|
||||
if (init[i] == State::Sx || init[i] == State::Sz)
|
||||
continue;
|
||||
|
||||
SigBit wire_bit = SigBit(wire, i);
|
||||
SigBit mapped_wire_bit = sigmap(wire_bit);
|
||||
|
||||
if (wire_bit == mapped_wire_bit)
|
||||
goto next_wire;
|
||||
|
||||
if (mapped_wire_bit.wire) {
|
||||
if (qbits.count(mapped_wire_bit) == 0)
|
||||
goto next_wire;
|
||||
|
||||
if (qbits.at(mapped_wire_bit) != init[i])
|
||||
goto next_wire;
|
||||
}
|
||||
else {
|
||||
if (mapped_wire_bit == State::Sx || mapped_wire_bit == State::Sz)
|
||||
goto next_wire;
|
||||
|
||||
if (mapped_wire_bit != init[i]) {
|
||||
log_warning("Initial value conflict for %s resolving to %s but with init %s.\n", log_signal(wire_bit), log_signal(mapped_wire_bit), log_signal(init[i]));
|
||||
goto next_wire;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
|
||||
|
||||
wire->attributes.erase(ID::init);
|
||||
did_something = true;
|
||||
next_wire:;
|
||||
}
|
||||
|
||||
if (did_something)
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
void rmunused_module(RTLIL::Module *module, bool purge_mode, bool unusedbitsattr_mode, bool verbose, bool rminit)
|
||||
{
|
||||
if (verbose)
|
||||
log("Finding unused cells or wires in module %s..\n", module->name);
|
||||
|
||||
std::vector<RTLIL::Cell*> delcells;
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
|
||||
bool is_signed = cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
|
||||
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec y = cell->getPort(ID::Y);
|
||||
a.extend_u0(GetSize(y), is_signed);
|
||||
|
||||
if (a.has_const(State::Sz)) {
|
||||
SigSpec new_a;
|
||||
SigSpec new_y;
|
||||
for (int i = 0; i < GetSize(a); ++i) {
|
||||
SigBit b = a[i];
|
||||
if (b == State::Sz)
|
||||
continue;
|
||||
new_a.append(b);
|
||||
new_y.append(y[i]);
|
||||
}
|
||||
a = std::move(new_a);
|
||||
y = std::move(new_y);
|
||||
}
|
||||
if (!y.empty())
|
||||
module->connect(y, a);
|
||||
delcells.push_back(cell);
|
||||
} else if (cell->type.in(ID($connect)) && !cell->has_keep_attr()) {
|
||||
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec b = cell->getPort(ID::B);
|
||||
if (a.has_const() && !b.has_const())
|
||||
std::swap(a, b);
|
||||
module->connect(a, b);
|
||||
delcells.push_back(cell);
|
||||
} else if (cell->type.in(ID($input_port)) && !cell->has_keep_attr()) {
|
||||
delcells.push_back(cell);
|
||||
}
|
||||
}
|
||||
for (auto cell : delcells) {
|
||||
if (verbose) {
|
||||
if (cell->type == ID($connect))
|
||||
log_debug(" removing connect cell `%s': %s <-> %s\n", cell->name,
|
||||
log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B)));
|
||||
else if (cell->type == ID($input_port))
|
||||
log_debug(" removing input port marker cell `%s': %s\n", cell->name,
|
||||
log_signal(cell->getPort(ID::Y)));
|
||||
else
|
||||
log_debug(" removing buffer cell `%s': %s = %s\n", cell->name,
|
||||
log_signal(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::A)));
|
||||
}
|
||||
module->remove(cell);
|
||||
}
|
||||
if (!delcells.empty())
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
rmunused_module_cells(module, verbose);
|
||||
while (rmunused_module_signals(module, purge_mode, unusedbitsattr_mode, verbose)) { }
|
||||
|
||||
if (rminit && rmunused_module_init(module, verbose))
|
||||
while (rmunused_module_signals(module, purge_mode, unusedbitsattr_mode, verbose)) { }
|
||||
}
|
||||
|
||||
struct OptCleanPass : public Pass {
|
||||
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_clean [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass identifies wires and cells that are unused and removes them. Other\n");
|
||||
log("passes often remove cells but leave the wires in the design or reconnect the\n");
|
||||
log("wires but leave the old cells in the design. This pass can be used to clean up\n");
|
||||
log("after the passes that do the actual work.\n");
|
||||
log("\n");
|
||||
log("This pass only operates on completely selected modules without processes.\n");
|
||||
log("\n");
|
||||
log(" -purge\n");
|
||||
log(" also remove internal nets if they have a public name\n");
|
||||
log("\n");
|
||||
log(" -unusedbitsattr\n");
|
||||
log(" add an unused_bits attribute onto wires for bits that are not used\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool purge_mode = false;
|
||||
bool unusedbitsattr_mode = false;
|
||||
|
||||
log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n");
|
||||
log_push();
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-purge") {
|
||||
purge_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-unusedbitsattr") {
|
||||
unusedbitsattr_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
keep_cache.reset(design, purge_mode);
|
||||
|
||||
ct_reg.setup_internals_mem();
|
||||
ct_reg.setup_internals_anyinit();
|
||||
ct_reg.setup_stdcells_mem();
|
||||
|
||||
ct_all.setup(design);
|
||||
|
||||
count_rm_cells = 0;
|
||||
count_rm_wires = 0;
|
||||
|
||||
for (auto module : design->selected_whole_modules_warn()) {
|
||||
if (module->has_processes_warn())
|
||||
continue;
|
||||
rmunused_module(module, purge_mode, unusedbitsattr_mode, true, true);
|
||||
}
|
||||
|
||||
if (count_rm_cells > 0 || count_rm_wires > 0)
|
||||
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
|
||||
|
||||
design->optimize();
|
||||
design->sort();
|
||||
design->check();
|
||||
|
||||
keep_cache.reset();
|
||||
ct_reg.clear();
|
||||
ct_all.clear();
|
||||
log_pop();
|
||||
|
||||
request_garbage_collection();
|
||||
}
|
||||
} OptCleanPass;
|
||||
|
||||
struct CleanPass : public Pass {
|
||||
CleanPass() : Pass("clean", "remove unused cells and wires") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" clean [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This is identical to 'opt_clean', but less verbose.\n");
|
||||
log("\n");
|
||||
log("When commands are separated using the ';;' token, this command will be executed\n");
|
||||
log("between the commands.\n");
|
||||
log("\n");
|
||||
log("When commands are separated using the ';;;' token, this command will be executed\n");
|
||||
log("in -purge mode between the commands.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool purge_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-purge") {
|
||||
purge_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
keep_cache.reset(design);
|
||||
|
||||
ct_reg.setup_internals_mem();
|
||||
ct_reg.setup_internals_anyinit();
|
||||
ct_reg.setup_stdcells_mem();
|
||||
|
||||
ct_all.setup(design);
|
||||
|
||||
count_rm_cells = 0;
|
||||
count_rm_wires = 0;
|
||||
|
||||
for (auto module : design->selected_unboxed_whole_modules()) {
|
||||
if (module->has_processes())
|
||||
continue;
|
||||
rmunused_module(module, purge_mode, false, ys_debug(), true);
|
||||
}
|
||||
|
||||
log_suppressed();
|
||||
if (count_rm_cells > 0 || count_rm_wires > 0)
|
||||
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
|
||||
|
||||
design->optimize();
|
||||
design->sort();
|
||||
design->check();
|
||||
|
||||
keep_cache.reset();
|
||||
ct_reg.clear();
|
||||
ct_all.clear();
|
||||
|
||||
request_garbage_collection();
|
||||
}
|
||||
} CleanPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
11
passes/opt/opt_clean/CMakeLists.txt
Normal file
11
passes/opt/opt_clean/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
yosys_pass(opt_clean
|
||||
cells_all.cc
|
||||
cells_temp.cc
|
||||
wires.cc
|
||||
inits.cc
|
||||
opt_clean.cc
|
||||
opt_clean.h
|
||||
keep_cache.h
|
||||
PROVIDES
|
||||
clean
|
||||
)
|
||||
373
passes/opt/opt_clean/cells_all.cc
Normal file
373
passes/opt/opt_clean/cells_all.cc
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/ffinit.h"
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "passes/opt/opt_clean/opt_clean.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
unsigned int hash_bit(const SigBit &bit) {
|
||||
return static_cast<unsigned int>(hash_ops<SigBit>::hash(bit).yield());
|
||||
}
|
||||
|
||||
SigMap wire_sigmap(const RTLIL::Module* mod) {
|
||||
SigMap map;
|
||||
for (auto &it : mod->connections_) {
|
||||
for (int i = 0; i < GetSize(it.second); i++) {
|
||||
if (it.second[i].wire != nullptr)
|
||||
map.add(it.first[i], it.second[i]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
struct WireDrivers;
|
||||
// Maps from a SigBit to a unique driver cell.
|
||||
struct WireDriver {
|
||||
using Accumulated = WireDrivers;
|
||||
SigBit bit;
|
||||
int driver_cell;
|
||||
};
|
||||
// Maps from a SigBit to one or more driver cells.
|
||||
struct WireDrivers {
|
||||
WireDrivers() : driver_cell(0) {}
|
||||
WireDrivers(WireDriver driver) : bit(driver.bit), driver_cell(driver.driver_cell) {}
|
||||
WireDrivers(SigBit bit) : bit(bit), driver_cell(0) {}
|
||||
WireDrivers(WireDrivers &&other) = default;
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
const_iterator(const WireDrivers &drivers, bool end)
|
||||
: driver_cell(drivers.driver_cell), in_extra_cells(end) {
|
||||
if (drivers.extra_driver_cells) {
|
||||
if (end) {
|
||||
extra_it = drivers.extra_driver_cells->end();
|
||||
} else {
|
||||
extra_it = drivers.extra_driver_cells->begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
int operator*() const {
|
||||
if (in_extra_cells)
|
||||
return **extra_it;
|
||||
return driver_cell;
|
||||
}
|
||||
const_iterator& operator++() {
|
||||
if (in_extra_cells)
|
||||
++*extra_it;
|
||||
else
|
||||
in_extra_cells = true;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const const_iterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool operator==(const const_iterator &other) const {
|
||||
return in_extra_cells == other.in_extra_cells &&
|
||||
extra_it == other.extra_it;
|
||||
}
|
||||
private:
|
||||
std::optional<pool<int>::iterator> extra_it;
|
||||
int driver_cell;
|
||||
bool in_extra_cells;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(*this, false); }
|
||||
const_iterator end() const { return const_iterator(*this, true); }
|
||||
|
||||
SigBit bit;
|
||||
int driver_cell;
|
||||
std::unique_ptr<pool<int>> extra_driver_cells;
|
||||
};
|
||||
struct WireDriversKeyEquality {
|
||||
bool operator()(const WireDrivers &a, const WireDrivers &b) const {
|
||||
return a.bit == b.bit;
|
||||
}
|
||||
};
|
||||
struct WireDriversCollisionHandler {
|
||||
void operator()(WireDrivers &incumbent, WireDrivers &new_value) const {
|
||||
log_assert(new_value.extra_driver_cells == nullptr);
|
||||
if (!incumbent.extra_driver_cells)
|
||||
incumbent.extra_driver_cells.reset(new pool<int>());
|
||||
incumbent.extra_driver_cells->insert(new_value.driver_cell);
|
||||
}
|
||||
};
|
||||
using Wire2Drivers = ShardedHashtable<WireDriver, WireDriversKeyEquality, WireDriversCollisionHandler>;
|
||||
|
||||
struct ConflictLogs {
|
||||
ShardedVector<std::pair<SigBit, std::string>> logs;
|
||||
ConflictLogs(ParallelDispatchThreadPool::Subpool &subpool) : logs(subpool) {}
|
||||
void print_warnings(pool<SigBit>& used_raw_bits, const SigMap& wire_map, const RTLIL::Module* mod, CleanRunContext &clean_ctx) {
|
||||
if (!logs.empty()) {
|
||||
// We could do this in parallel but hopefully this is rare.
|
||||
for (auto [_, cell] : mod->cells_) {
|
||||
for (auto &[port, sig] : cell->connections()) {
|
||||
if (clean_ctx.ct_all.cell_known(cell->type) && !clean_ctx.ct_all.cell_input(cell->type, port))
|
||||
continue;
|
||||
for (auto raw_bit : wire_map(sig))
|
||||
used_raw_bits.insert(raw_bit);
|
||||
}
|
||||
}
|
||||
for (std::pair<SigBit, std::string> &it : logs) {
|
||||
if (used_raw_bits.count(it.first))
|
||||
log_warning("%s\n", it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CellTraversal {
|
||||
ConcurrentWorkQueue<int> queue;
|
||||
Wire2Drivers wire2driver;
|
||||
dict<std::string, pool<int>> mem2cells;
|
||||
CellTraversal(int num_threads) : queue(num_threads), wire2driver(), mem2cells() {}
|
||||
};
|
||||
|
||||
struct CellAnalysis {
|
||||
ShardedVector<Wire*> keep_wires;
|
||||
std::vector<std::atomic<bool>> unused;
|
||||
|
||||
CellAnalysis(AnalysisContext& actx)
|
||||
: keep_wires(actx.subpool), unused(actx.mod->cells_size()) {}
|
||||
|
||||
pool<SigBit> analyze_kept_wires(CellTraversal& traversal, const SigMap& sigmap, const SigMap& wire_map, int num_threads) {
|
||||
// Also enqueue cells that drive kept wires into cell_queue
|
||||
// and mark those cells as used
|
||||
// and mark all bits of those wires as used
|
||||
pool<SigBit> used_raw_bits;
|
||||
int i = 0;
|
||||
for (Wire *wire : keep_wires) {
|
||||
for (auto bit : sigmap(wire)) {
|
||||
const WireDrivers *drivers = traversal.wire2driver.find({{bit}, hash_bit(bit)});
|
||||
if (drivers != nullptr)
|
||||
for (int cell_index : *drivers)
|
||||
if (unused[cell_index].exchange(false, std::memory_order_relaxed)) {
|
||||
ThreadIndex fake_thread_index = {i++ % num_threads};
|
||||
traversal.queue.push(fake_thread_index, cell_index);
|
||||
}
|
||||
}
|
||||
for (auto raw_bit : SigSpec(wire))
|
||||
used_raw_bits.insert(wire_map(raw_bit));
|
||||
}
|
||||
return used_raw_bits;
|
||||
}
|
||||
|
||||
void mark_used_and_enqueue(int cell_idx, ConcurrentWorkQueue<int>& queue, const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
if (unused[cell_idx].exchange(false, std::memory_order_relaxed))
|
||||
queue.push(ctx, cell_idx);
|
||||
}
|
||||
};
|
||||
|
||||
ConflictLogs explore(CellAnalysis& analysis, CellTraversal& traversal, const SigMap& wire_map, AnalysisContext& actx, CleanRunContext &clean_ctx) {
|
||||
ConflictLogs logs(actx.subpool);
|
||||
Wire2Drivers::Builder wire2driver_builder(actx.subpool);
|
||||
ShardedVector<std::pair<std::string, int>> mem2cells_vector(actx.subpool);
|
||||
|
||||
// Enqueue kept cells into traversal.queue
|
||||
// Prepare input cone traversal into traversal.wire2driver
|
||||
// Prepare "input cone" traversal from memory to write port or meminit as analysis.mem2cells
|
||||
// Also check driver conflicts
|
||||
// Also mark cells unused to true unless keep (we override this later)
|
||||
actx.subpool.run([&analysis, &traversal, &logs, &wire_map, &mem2cells_vector, &wire2driver_builder, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(actx.mod->cells_size())) {
|
||||
Cell *cell = actx.mod->cell_at(i);
|
||||
if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2)))
|
||||
mem2cells_vector.insert(ctx, {cell->getParam(ID::MEMID).decode_string(), i});
|
||||
|
||||
for (auto &it2 : cell->connections()) {
|
||||
if (clean_ctx.ct_all.cell_known(cell->type) && !clean_ctx.ct_all.cell_output(cell->type, it2.first))
|
||||
continue;
|
||||
for (auto raw_bit : it2.second) {
|
||||
if (raw_bit.wire == nullptr)
|
||||
continue;
|
||||
auto bit = actx.assign_map(raw_bit);
|
||||
if (bit.wire == nullptr && clean_ctx.ct_all.cell_known(cell->type)) {
|
||||
std::string msg = stringf("Driver-driver conflict "
|
||||
"for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
|
||||
log_signal(raw_bit), cell->name.unescape(), it2.first.unescape(), log_signal(bit), actx.mod->name.unescape());
|
||||
logs.logs.insert(ctx, {wire_map(raw_bit), msg});
|
||||
}
|
||||
if (bit.wire != nullptr)
|
||||
wire2driver_builder.insert(ctx, {{bit, i}, hash_bit(bit)});
|
||||
}
|
||||
}
|
||||
bool keep = clean_ctx.keep_cache.query(cell);
|
||||
analysis.unused[i].store(!keep, std::memory_order_relaxed);
|
||||
if (keep)
|
||||
traversal.queue.push(ctx, i);
|
||||
}
|
||||
for (int i : ctx.item_range(actx.mod->wires_size())) {
|
||||
Wire *wire = actx.mod->wire_at(i);
|
||||
if (wire->port_output || wire->get_bool_attribute(ID::keep))
|
||||
analysis.keep_wires.insert(ctx, wire);
|
||||
}
|
||||
});
|
||||
// Finish by merging per-thread collected data
|
||||
actx.subpool.run([&wire2driver_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
wire2driver_builder.process(ctx);
|
||||
});
|
||||
traversal.wire2driver = wire2driver_builder;
|
||||
|
||||
for (std::pair<std::string, int> &mem2cell : mem2cells_vector)
|
||||
traversal.mem2cells[mem2cell.first].insert(mem2cell.second);
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
struct MemAnalysis {
|
||||
std::vector<std::atomic<bool>> unused;
|
||||
dict<std::string, int> indices;
|
||||
MemAnalysis(const RTLIL::Module* mod) : unused(mod->memories.size()), indices() {
|
||||
for (int i = 0; i < GetSize(mod->memories); ++i) {
|
||||
indices[mod->memories.element(i)->first.str()] = i;
|
||||
unused[i].store(true, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void fixup_unused_cells_and_mems(CellAnalysis& analysis, MemAnalysis& mem_analysis, CellTraversal& traversal, AnalysisContext& actx, CleanRunContext &clean_ctx) {
|
||||
// Processes the cell queue in batches, traversing input cones by enqueuing more cells
|
||||
// Discover and mark used memories and cells
|
||||
actx.subpool.run([&analysis, &mem_analysis, &traversal, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
pool<SigBit> bits;
|
||||
pool<std::string> mems;
|
||||
while (true) {
|
||||
std::vector<int> cell_indices = traversal.queue.pop_batch(ctx);
|
||||
if (cell_indices.empty())
|
||||
return;
|
||||
for (auto cell_index : cell_indices) {
|
||||
Cell *cell = actx.mod->cell_at(cell_index);
|
||||
for (auto &it : cell->connections())
|
||||
if (!clean_ctx.ct_all.cell_known(cell->type) || clean_ctx.ct_all.cell_input(cell->type, it.first))
|
||||
for (auto bit : actx.assign_map(it.second))
|
||||
bits.insert(bit);
|
||||
|
||||
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
|
||||
std::string mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||
if (mem_analysis.indices.count(mem_id)) {
|
||||
int mem_index = mem_analysis.indices[mem_id];
|
||||
// Memory fixup
|
||||
if (mem_analysis.unused[mem_index].exchange(false, std::memory_order_relaxed))
|
||||
mems.insert(mem_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto bit : bits) {
|
||||
// Cells fixup
|
||||
const WireDrivers *drivers = traversal.wire2driver.find({{bit}, hash_bit(bit)});
|
||||
if (drivers != nullptr)
|
||||
for (int cell_idx : *drivers)
|
||||
analysis.mark_used_and_enqueue(cell_idx, traversal.queue, ctx);
|
||||
}
|
||||
bits.clear();
|
||||
|
||||
for (auto mem : mems) {
|
||||
if (traversal.mem2cells.count(mem) == 0)
|
||||
continue;
|
||||
// Cells fixup
|
||||
for (int cell_idx : traversal.mem2cells.at(mem))
|
||||
analysis.mark_used_and_enqueue(cell_idx, traversal.queue, ctx);
|
||||
}
|
||||
mems.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pool<Cell*> all_unused_cells(const Module *mod, const CellAnalysis& analysis, Wire2Drivers& wire2driver, ParallelDispatchThreadPool::Subpool &subpool) {
|
||||
pool<Cell*> unused_cells;
|
||||
ShardedVector<int> sharded_unused_cells(subpool);
|
||||
subpool.run([mod, &analysis, &wire2driver, &sharded_unused_cells](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
// Parallel destruction of `wire2driver`
|
||||
wire2driver.clear(ctx);
|
||||
for (int i : ctx.item_range(mod->cells_size()))
|
||||
if (analysis.unused[i].load(std::memory_order_relaxed))
|
||||
sharded_unused_cells.insert(ctx, i);
|
||||
});
|
||||
for (int cell_index : sharded_unused_cells)
|
||||
unused_cells.insert(mod->cell_at(cell_index));
|
||||
unused_cells.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
|
||||
return unused_cells;
|
||||
}
|
||||
|
||||
void remove_cells(RTLIL::Module* mod, FfInitVals& ffinit, const pool<Cell*>& cells, bool verbose, RmStats& stats) {
|
||||
for (auto cell : cells) {
|
||||
if (verbose)
|
||||
log_debug(" removing unused `%s' cell `%s'.\n", cell->type, cell->name);
|
||||
mod->design->scratchpad_set_bool("opt.did_something", true);
|
||||
if (cell->is_builtin_ff())
|
||||
ffinit.remove_init(cell->getPort(ID::Q));
|
||||
mod->remove(cell);
|
||||
stats.count_rm_cells++;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_mems(RTLIL::Module* mod, const MemAnalysis& mem_analysis, bool verbose) {
|
||||
for (const auto &it : mem_analysis.indices) {
|
||||
if (!mem_analysis.unused[it.second].load(std::memory_order_relaxed))
|
||||
continue;
|
||||
RTLIL::IdString id(it.first);
|
||||
if (verbose)
|
||||
log_debug(" removing unused memory `%s'.\n", id.unescape());
|
||||
delete mod->memories.at(id);
|
||||
mod->memories.erase(id);
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
void rmunused_module_cells(Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx)
|
||||
{
|
||||
AnalysisContext actx(module, subpool);
|
||||
|
||||
// Used for logging warnings only
|
||||
SigMap wire_map = wire_sigmap(module);
|
||||
|
||||
CellAnalysis analysis(actx);
|
||||
CellTraversal traversal(subpool.num_threads());
|
||||
// Mark all unkept cells as unused initially
|
||||
// and queue up cell traversal from those cells
|
||||
auto logs = explore(analysis, traversal, wire_map, actx, clean_ctx);
|
||||
// Mark cells that drive kept wires into cell_queue and those bits as used
|
||||
// and queue up cell traversal from those cells
|
||||
pool<SigBit> used_raw_bits = analysis.analyze_kept_wires(traversal, actx.assign_map, wire_map, subpool.num_threads());
|
||||
|
||||
// Mark all memories as unused initially
|
||||
MemAnalysis mem_analysis(module);
|
||||
// Marked all used cells and mems as used by traversing with cell queue
|
||||
fixup_unused_cells_and_mems(analysis, mem_analysis, traversal, actx, clean_ctx);
|
||||
// Analyses are now fully correct
|
||||
|
||||
// unused_cells.contains(foo) iff analysis.used[foo] == true
|
||||
// wire2driver is passed in only to destroy it
|
||||
pool<Cell*> unused_cells = all_unused_cells(module, analysis, traversal.wire2driver, subpool);
|
||||
|
||||
FfInitVals ffinit;
|
||||
ffinit.set_parallel(&actx.assign_map, subpool.thread_pool(), module);
|
||||
// Now we know what to kill
|
||||
remove_cells(module, ffinit, unused_cells, clean_ctx.flags.verbose, clean_ctx.stats);
|
||||
remove_mems(module, mem_analysis, clean_ctx.flags.verbose);
|
||||
logs.print_warnings(used_raw_bits, wire_map, module, clean_ctx);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
104
passes/opt/opt_clean/cells_temp.cc
Normal file
104
passes/opt/opt_clean/cells_temp.cc
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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 "passes/opt/opt_clean/opt_clean.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
bool is_signed(RTLIL::Cell* cell) {
|
||||
return cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
|
||||
}
|
||||
|
||||
bool trim_buf(RTLIL::Cell* cell, ShardedVector<RTLIL::SigSig>& new_connections, const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec y = cell->getPort(ID::Y);
|
||||
a.extend_u0(GetSize(y), is_signed(cell));
|
||||
|
||||
if (a.has_const(State::Sz)) {
|
||||
RTLIL::SigSpec new_a;
|
||||
RTLIL::SigSpec new_y;
|
||||
for (int i = 0; i < GetSize(a); ++i) {
|
||||
RTLIL::SigBit b = a[i];
|
||||
if (b == State::Sz)
|
||||
return false;
|
||||
new_a.append(b);
|
||||
new_y.append(y[i]);
|
||||
}
|
||||
a = std::move(new_a);
|
||||
y = std::move(new_y);
|
||||
}
|
||||
if (!y.empty())
|
||||
new_connections.insert(ctx, {y, a});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(ShardedVector<RTLIL::Cell*>& cells, RTLIL::Module* mod, bool verbose) {
|
||||
bool did_something = false;
|
||||
for (RTLIL::Cell *cell : cells) {
|
||||
if (verbose) {
|
||||
if (cell->type == ID($connect))
|
||||
log_debug(" removing connect cell `%s': %s <-> %s\n", cell->name,
|
||||
log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B)));
|
||||
else if (cell->type == ID($input_port))
|
||||
log_debug(" removing input port marker cell `%s': %s\n", cell->name,
|
||||
log_signal(cell->getPort(ID::Y)));
|
||||
else
|
||||
log_debug(" removing buffer cell `%s': %s = %s\n", cell->name,
|
||||
log_signal(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::A)));
|
||||
}
|
||||
mod->remove(cell);
|
||||
did_something = true;
|
||||
}
|
||||
return did_something;
|
||||
}
|
||||
PRIVATE_NAMESPACE_END
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
void remove_temporary_cells(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose)
|
||||
{
|
||||
ShardedVector<RTLIL::Cell*> delcells(subpool);
|
||||
ShardedVector<RTLIL::SigSig> new_connections(subpool);
|
||||
const RTLIL::Module *const_module = module;
|
||||
subpool.run([const_module, &delcells, &new_connections](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(const_module->cells_size())) {
|
||||
RTLIL::Cell *cell = const_module->cell_at(i);
|
||||
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
|
||||
if (trim_buf(cell, new_connections, ctx))
|
||||
delcells.insert(ctx, cell);
|
||||
} else if (cell->type.in(ID($connect)) && !cell->has_keep_attr()) {
|
||||
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec b = cell->getPort(ID::B);
|
||||
if (a.has_const() && !b.has_const())
|
||||
std::swap(a, b);
|
||||
new_connections.insert(ctx, {a, b});
|
||||
delcells.insert(ctx, cell);
|
||||
} else if (cell->type.in(ID($input_port)) && !cell->has_keep_attr()) {
|
||||
delcells.insert(ctx, cell);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (RTLIL::SigSig &connection : new_connections) {
|
||||
module->connect(connection);
|
||||
}
|
||||
if (remove(delcells, module, verbose))
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
137
passes/opt/opt_clean/inits.cc
Normal file
137
passes/opt/opt_clean/inits.cc
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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 "passes/opt/opt_clean/opt_clean.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
ShardedVector<std::pair<SigBit, State>> build_inits(AnalysisContext& actx) {
|
||||
ShardedVector<std::pair<SigBit, State>> results(actx.subpool);
|
||||
actx.subpool.run([&results, &actx](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(actx.mod->cells_size())) {
|
||||
RTLIL::Cell *cell = actx.mod->cell_at(i);
|
||||
if (StaticCellTypes::Compat::internals_mem_ff(cell->type) && cell->hasPort(ID::Q))
|
||||
{
|
||||
SigSpec sig = cell->getPort(ID::Q);
|
||||
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
{
|
||||
SigBit bit = sig[i];
|
||||
|
||||
if (bit.wire == nullptr || bit.wire->attributes.count(ID::init) == 0)
|
||||
continue;
|
||||
|
||||
Const init = bit.wire->attributes.at(ID::init);
|
||||
|
||||
if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz)
|
||||
continue;
|
||||
|
||||
results.insert(ctx, {bit, init[i]});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
dict<SigBit, State> qbits_from_inits(ShardedVector<std::pair<SigBit, State>>& inits, SigMap& assign_map) {
|
||||
dict<SigBit, State> qbits;
|
||||
for (std::pair<SigBit, State> &p : inits) {
|
||||
assign_map.add(p.first);
|
||||
qbits[p.first] = p.second;
|
||||
}
|
||||
return qbits;
|
||||
}
|
||||
|
||||
ShardedVector<RTLIL::Wire*> deferred_init_transfer(const dict<SigBit, State>& qbits, AnalysisContext& actx) {
|
||||
ShardedVector<RTLIL::Wire*> wire_results(actx.subpool);
|
||||
actx.subpool.run([&actx, &qbits, &wire_results](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int j : ctx.item_range(actx.mod->wires_size())) {
|
||||
RTLIL::Wire *wire = actx.mod->wire_at(j);
|
||||
if (wire->attributes.count(ID::init) == 0)
|
||||
continue;
|
||||
Const init = wire->attributes.at(ID::init);
|
||||
|
||||
for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++)
|
||||
{
|
||||
if (init[i] == State::Sx || init[i] == State::Sz)
|
||||
continue;
|
||||
|
||||
SigBit wire_bit = SigBit(wire, i);
|
||||
SigBit mapped_wire_bit = actx.assign_map(wire_bit);
|
||||
|
||||
if (wire_bit == mapped_wire_bit)
|
||||
goto next_wire;
|
||||
|
||||
if (mapped_wire_bit.wire) {
|
||||
if (qbits.count(mapped_wire_bit) == 0)
|
||||
goto next_wire;
|
||||
|
||||
if (qbits.at(mapped_wire_bit) != init[i])
|
||||
goto next_wire;
|
||||
}
|
||||
else {
|
||||
if (mapped_wire_bit == State::Sx || mapped_wire_bit == State::Sz)
|
||||
goto next_wire;
|
||||
|
||||
if (mapped_wire_bit != init[i]) {
|
||||
log_warning("Initial value conflict for %s resolving to %s but with init %s.\n", log_signal(wire_bit), log_signal(mapped_wire_bit), log_signal(init[i]));
|
||||
goto next_wire;
|
||||
}
|
||||
}
|
||||
}
|
||||
wire_results.insert(ctx, wire);
|
||||
|
||||
next_wire:;
|
||||
}
|
||||
});
|
||||
return wire_results;
|
||||
}
|
||||
|
||||
bool remove_redundant_inits(ShardedVector<RTLIL::Wire*> wires, bool verbose) {
|
||||
bool did_something = false;
|
||||
for (RTLIL::Wire *wire : wires) {
|
||||
if (verbose)
|
||||
log_debug(" removing redundant init attribute on %s.\n", wire);
|
||||
wire->attributes.erase(ID::init);
|
||||
did_something = true;
|
||||
}
|
||||
return did_something;
|
||||
}
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
bool rmunused_module_init(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose)
|
||||
{
|
||||
AnalysisContext actx(module, subpool);
|
||||
|
||||
ShardedVector<std::pair<SigBit, State>> inits = build_inits(actx);
|
||||
dict<SigBit, State> qbits = qbits_from_inits(inits, actx.assign_map);
|
||||
ShardedVector<RTLIL::Wire*> inits_to_transfer = deferred_init_transfer(qbits, actx);
|
||||
|
||||
bool did_something = remove_redundant_inits(inits_to_transfer, verbose);
|
||||
if (did_something)
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
167
passes/opt/opt_clean/keep_cache.h
Normal file
167
passes/opt/opt_clean/keep_cache.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/threading.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/yosys_common.h"
|
||||
|
||||
#ifndef OPT_CLEAN_KEEP_CACHE_H
|
||||
#define OPT_CLEAN_KEEP_CACHE_H
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct KeepCache
|
||||
{
|
||||
dict<Module*, bool> keep_modules;
|
||||
bool purge_mode;
|
||||
|
||||
KeepCache(bool purge_mode, ParallelDispatchThreadPool &thread_pool, const std::vector<RTLIL::Module *> &selected_modules)
|
||||
: purge_mode(purge_mode) {
|
||||
|
||||
std::vector<RTLIL::Module *> scan_modules_worklist;
|
||||
dict<RTLIL::Module *, std::vector<RTLIL::Module*>> dependents;
|
||||
std::vector<RTLIL::Module *> propagate_kept_modules_worklist;
|
||||
for (RTLIL::Module *module : selected_modules) {
|
||||
if (keep_modules.count(module))
|
||||
continue;
|
||||
bool keep = scan_module(module, thread_pool, dependents, ALL_CELLS, scan_modules_worklist);
|
||||
keep_modules[module] = keep;
|
||||
if (keep)
|
||||
propagate_kept_modules_worklist.push_back(module);
|
||||
}
|
||||
|
||||
while (!scan_modules_worklist.empty()) {
|
||||
RTLIL::Module *module = scan_modules_worklist.back();
|
||||
scan_modules_worklist.pop_back();
|
||||
if (keep_modules.count(module))
|
||||
continue;
|
||||
bool keep = scan_module(module, thread_pool, dependents, MINIMUM_CELLS, scan_modules_worklist);
|
||||
keep_modules[module] = keep;
|
||||
if (keep)
|
||||
propagate_kept_modules_worklist.push_back(module);
|
||||
}
|
||||
|
||||
while (!propagate_kept_modules_worklist.empty()) {
|
||||
RTLIL::Module *module = propagate_kept_modules_worklist.back();
|
||||
propagate_kept_modules_worklist.pop_back();
|
||||
for (RTLIL::Module *dependent : dependents[module]) {
|
||||
if (keep_modules[dependent])
|
||||
continue;
|
||||
keep_modules[dependent] = true;
|
||||
propagate_kept_modules_worklist.push_back(dependent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool query(Cell *cell) const
|
||||
{
|
||||
if (keep_cell(cell, purge_mode))
|
||||
return true;
|
||||
if (cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
|
||||
return true;
|
||||
if (cell->module && cell->module->design) {
|
||||
RTLIL::Module *cell_module = cell->module->design->module(cell->type);
|
||||
return cell_module != nullptr && keep_modules.at(cell_module);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
enum ScanCells {
|
||||
// Scan every cell to see if it uses a module that is kept.
|
||||
ALL_CELLS,
|
||||
// Stop scanning cells if we determine early that this module is kept.
|
||||
MINIMUM_CELLS,
|
||||
};
|
||||
bool scan_module(Module *module, ParallelDispatchThreadPool &thread_pool, dict<RTLIL::Module *, std::vector<RTLIL::Module*>> &dependents,
|
||||
ScanCells scan_cells, std::vector<Module*> &worklist) const
|
||||
{
|
||||
MonotonicFlag keep_module;
|
||||
if (module->get_bool_attribute(ID::keep)) {
|
||||
if (scan_cells == MINIMUM_CELLS)
|
||||
return true;
|
||||
keep_module.set();
|
||||
}
|
||||
|
||||
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
|
||||
ShardedVector<Module*> deps(subpool);
|
||||
const RTLIL::Module *const_module = module;
|
||||
bool purge_mode = this->purge_mode;
|
||||
subpool.run([purge_mode, const_module, scan_cells, &deps, &keep_module](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
bool keep = false;
|
||||
for (int i : ctx.item_range(const_module->cells_size())) {
|
||||
Cell *cell = const_module->cell_at(i);
|
||||
if (keep_cell(cell, purge_mode)) {
|
||||
if (scan_cells == MINIMUM_CELLS) {
|
||||
keep_module.set();
|
||||
return;
|
||||
}
|
||||
keep = true;
|
||||
}
|
||||
if (const_module->design) {
|
||||
RTLIL::Module *cell_module = const_module->design->module(cell->type);
|
||||
if (cell_module != nullptr)
|
||||
deps.insert(ctx, cell_module);
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
keep_module.set();
|
||||
return;
|
||||
}
|
||||
for (int i : ctx.item_range(const_module->wires_size())) {
|
||||
Wire *wire = const_module->wire_at(i);
|
||||
if (wire->get_bool_attribute(ID::keep)) {
|
||||
keep_module.set();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (scan_cells == MINIMUM_CELLS && keep_module.load())
|
||||
return true;
|
||||
for (Module *dep : deps) {
|
||||
dependents[dep].push_back(module);
|
||||
worklist.push_back(dep);
|
||||
}
|
||||
return keep_module.load();
|
||||
}
|
||||
|
||||
static bool keep_cell(Cell *cell, bool purge_mode)
|
||||
{
|
||||
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
||||
return true;
|
||||
|
||||
if (cell->type.in(ID($overwrite_tag)))
|
||||
return true;
|
||||
|
||||
if (cell->type == ID($print) || cell->type == ID($check))
|
||||
return true;
|
||||
|
||||
if (cell->has_keep_attr())
|
||||
return true;
|
||||
|
||||
if (!purge_mode && cell->type == ID($scopeinfo))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
#endif /* OPT_CLEAN_KEEP_CACHE_H */
|
||||
164
passes/opt/opt_clean/opt_clean.cc
Normal file
164
passes/opt/opt_clean/opt_clean.cc
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/log_help.h"
|
||||
#include "passes/opt/opt_clean/opt_clean.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void rmunused_module(RTLIL::Module *module, bool rminit, CleanRunContext &clean_ctx)
|
||||
{
|
||||
if (clean_ctx.flags.verbose)
|
||||
log("Finding unused cells or wires in module %s..\n", module->name);
|
||||
|
||||
// Use no more than one worker per thousand cells, rounded down, so
|
||||
// we only start multithreading with at least 2000 cells.
|
||||
int num_worker_threads = ThreadPool::work_pool_size(0, module->cells_size(), 10000);
|
||||
ParallelDispatchThreadPool::Subpool subpool(clean_ctx.thread_pool, num_worker_threads);
|
||||
remove_temporary_cells(module, subpool, clean_ctx.flags.verbose);
|
||||
rmunused_module_cells(module, subpool, clean_ctx);
|
||||
while (rmunused_module_signals(module, subpool, clean_ctx)) { }
|
||||
|
||||
if (rminit && rmunused_module_init(module, subpool, clean_ctx.flags.verbose))
|
||||
while (rmunused_module_signals(module, subpool, clean_ctx)) { }
|
||||
}
|
||||
|
||||
struct OptCleanPass : public Pass {
|
||||
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
|
||||
bool formatted_help() override
|
||||
{
|
||||
auto *help = PrettyHelp::get_current();
|
||||
help->set_group("passes/opt");
|
||||
return false;
|
||||
}
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_clean [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass identifies wires and cells that are unused and removes them. Other\n");
|
||||
log("passes often remove cells but leave the wires in the design or reconnect the\n");
|
||||
log("wires but leave the old cells in the design. This pass can be used to clean up\n");
|
||||
log("after the passes that do the actual work.\n");
|
||||
log("\n");
|
||||
log("This pass only operates on completely selected modules without processes.\n");
|
||||
log("\n");
|
||||
log(" -purge\n");
|
||||
log(" also remove internal nets if they have a public name\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool purge_mode = false;
|
||||
|
||||
log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n");
|
||||
log_push();
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-purge") {
|
||||
purge_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
{
|
||||
std::vector<RTLIL::Module*> selected_modules;
|
||||
for (auto module : design->selected_whole_modules_warn())
|
||||
if (!module->has_processes_warn())
|
||||
selected_modules.push_back(module);
|
||||
CleanRunContext clean_ctx(design, selected_modules, {purge_mode, true});
|
||||
for (auto module : selected_modules)
|
||||
rmunused_module(module, true, clean_ctx);
|
||||
clean_ctx.stats.log();
|
||||
|
||||
design->optimize();
|
||||
design->check();
|
||||
}
|
||||
|
||||
log_pop();
|
||||
|
||||
request_garbage_collection();
|
||||
}
|
||||
} OptCleanPass;
|
||||
|
||||
struct CleanPass : public Pass {
|
||||
CleanPass() : Pass("clean", "remove unused cells and wires") { }
|
||||
bool formatted_help() override
|
||||
{
|
||||
auto *help = PrettyHelp::get_current();
|
||||
help->set_group("passes/opt");
|
||||
return false;
|
||||
}
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" clean [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This is identical to 'opt_clean', but less verbose.\n");
|
||||
log("\n");
|
||||
log("When commands are separated using the ';;' token, this command will be executed\n");
|
||||
log("between the commands.\n");
|
||||
log("\n");
|
||||
log("When commands are separated using the ';;;' token, this command will be executed\n");
|
||||
log("in -purge mode between the commands.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool purge_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-purge") {
|
||||
purge_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
{
|
||||
std::vector<RTLIL::Module*> selected_modules;
|
||||
for (auto module : design->selected_unboxed_whole_modules())
|
||||
if (!module->has_processes())
|
||||
selected_modules.push_back(module);
|
||||
CleanRunContext clean_ctx(design, selected_modules, {purge_mode, ys_debug()});
|
||||
for (auto module : selected_modules)
|
||||
rmunused_module(module, true, clean_ctx);
|
||||
|
||||
log_suppressed();
|
||||
clean_ctx.stats.log();
|
||||
|
||||
design->optimize();
|
||||
design->check();
|
||||
}
|
||||
|
||||
request_garbage_collection();
|
||||
}
|
||||
} CleanPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
92
passes/opt/opt_clean/opt_clean.h
Normal file
92
passes/opt/opt_clean/opt_clean.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/rtlil.h"
|
||||
#include "kernel/threading.h"
|
||||
#include "passes/opt/opt_clean/keep_cache.h"
|
||||
|
||||
#ifndef OPT_CLEAN_SHARED_H
|
||||
#define OPT_CLEAN_SHARED_H
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct AnalysisContext {
|
||||
SigMap assign_map;
|
||||
const RTLIL::Module *mod;
|
||||
ParallelDispatchThreadPool::Subpool &subpool;
|
||||
AnalysisContext(RTLIL::Module* m, ParallelDispatchThreadPool::Subpool &p) : assign_map(m), mod(m), subpool(p) {}
|
||||
};
|
||||
|
||||
struct RmStats {
|
||||
int count_rm_cells = 0;
|
||||
int count_rm_wires = 0;
|
||||
|
||||
void log()
|
||||
{
|
||||
if (count_rm_cells > 0 || count_rm_wires > 0)
|
||||
YOSYS_NAMESPACE_PREFIX log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
|
||||
}
|
||||
};
|
||||
|
||||
struct Flags {
|
||||
bool purge = false;
|
||||
bool verbose = false;
|
||||
};
|
||||
struct CleanRunContext {
|
||||
static constexpr auto ct_reg = StaticCellTypes::Categories::join(
|
||||
StaticCellTypes::Compat::mem_ff,
|
||||
StaticCellTypes::categories.is_anyinit);
|
||||
NewCellTypes ct_all;
|
||||
RmStats stats;
|
||||
ParallelDispatchThreadPool thread_pool;
|
||||
KeepCache keep_cache;
|
||||
Flags flags;
|
||||
|
||||
private:
|
||||
// Helper to compute thread pool size
|
||||
static int compute_thread_pool_size(const std::vector<RTLIL::Module*>& selected_modules) {
|
||||
int thread_pool_size = 0;
|
||||
for (auto module : selected_modules)
|
||||
thread_pool_size = std::max(thread_pool_size,
|
||||
ThreadPool::work_pool_size(0, module->cells_size(), 10000));
|
||||
return thread_pool_size;
|
||||
}
|
||||
|
||||
public:
|
||||
CleanRunContext(RTLIL::Design* design, const std::vector<RTLIL::Module*>& selected_modules, Flags f)
|
||||
: thread_pool(compute_thread_pool_size(selected_modules)),
|
||||
keep_cache(f.purge, thread_pool, selected_modules),
|
||||
flags(f)
|
||||
{
|
||||
ct_all.setup(design);
|
||||
}
|
||||
|
||||
~CleanRunContext() {
|
||||
ct_all.clear();
|
||||
}
|
||||
};
|
||||
|
||||
void remove_temporary_cells(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose);
|
||||
void rmunused_module_cells(Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx);
|
||||
bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx);
|
||||
bool rmunused_module_init(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose);
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif /* OPT_CLEAN_SHARED_H */
|
||||
585
passes/opt/opt_clean/wires.cc
Normal file
585
passes/opt/opt_clean/wires.cc
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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 "passes/opt/opt_clean/opt_clean.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
// No collision handler for these, since we will use them such that collisions don't happen
|
||||
struct ShardedSigBit {
|
||||
using Accumulated = ShardedSigBit;
|
||||
RTLIL::SigBit bit;
|
||||
ShardedSigBit() = default;
|
||||
ShardedSigBit(const RTLIL::SigBit &bit) : bit(bit) {}
|
||||
};
|
||||
struct ShardedSigBitEquality {
|
||||
bool operator()(const ShardedSigBit &b1, const ShardedSigBit &b2) const {
|
||||
return b1.bit == b2.bit;
|
||||
}
|
||||
};
|
||||
using ShardedSigPool = ShardedHashtable<ShardedSigBit, ShardedSigBitEquality, SetCollisionHandler<ShardedSigBit>>;
|
||||
|
||||
struct ShardedSigSpec {
|
||||
using Accumulated = ShardedSigSpec;
|
||||
RTLIL::SigSpec spec;
|
||||
ShardedSigSpec() = default;
|
||||
ShardedSigSpec(RTLIL::SigSpec spec) : spec(std::move(spec)) {}
|
||||
ShardedSigSpec(ShardedSigSpec &&) = default;
|
||||
};
|
||||
struct ShardedSigSpecEquality {
|
||||
bool operator()(const ShardedSigSpec &s1, const ShardedSigSpec &s2) const {
|
||||
return s1.spec == s2.spec;
|
||||
}
|
||||
};
|
||||
using ShardedSigSpecPool = ShardedHashtable<ShardedSigSpec, ShardedSigSpecEquality, SetCollisionHandler<ShardedSigSpec>>;
|
||||
|
||||
struct ExactCellWires {
|
||||
const ShardedSigSpecPool &exact_cells;
|
||||
const SigMap &assign_map;
|
||||
dict<RTLIL::Wire *, bool> cache;
|
||||
|
||||
ExactCellWires(const ShardedSigSpecPool &exact_cells, const SigMap &assign_map) : exact_cells(exact_cells), assign_map(assign_map) {}
|
||||
void cache_result_for_bit(const SigBit &bit) {
|
||||
if (bit.wire != nullptr)
|
||||
(void)is_exactly_cell_driven(bit.wire);
|
||||
}
|
||||
bool is_exactly_cell_driven(RTLIL::Wire *wire) {
|
||||
if (wire->port_input)
|
||||
return true;
|
||||
auto it = cache.find(wire);
|
||||
if (it != cache.end())
|
||||
return it->second;
|
||||
SigSpec sig = assign_map(wire);
|
||||
bool direct = exact_cells.find({sig, sig.hash_into(Hasher()).yield()}) != nullptr;
|
||||
cache.insert({wire, direct});
|
||||
return direct;
|
||||
}
|
||||
void cache_all(ShardedVector<RTLIL::SigBit> &bits) {
|
||||
for (RTLIL::SigBit candidate : bits) {
|
||||
cache_result_for_bit(candidate);
|
||||
cache_result_for_bit(assign_map(candidate));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
|
||||
{
|
||||
int count = w->attributes.size();
|
||||
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;
|
||||
}
|
||||
|
||||
// Should we pick `s2` over `s1` to represent a signal?
|
||||
bool compare_signals(const RTLIL::SigBit &s1, const RTLIL::SigBit &s2, const ShardedSigPool ®s, const ShardedSigPool &conns, ExactCellWires &cell_wires)
|
||||
{
|
||||
if (s1 == s2)
|
||||
return false;
|
||||
|
||||
RTLIL::Wire *w1 = s1.wire;
|
||||
RTLIL::Wire *w2 = s2.wire;
|
||||
|
||||
if (w1 == NULL || w2 == NULL)
|
||||
return w2 == NULL;
|
||||
|
||||
if (w1->port_input != w2->port_input)
|
||||
return w2->port_input;
|
||||
|
||||
if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
|
||||
return !(w2->port_input && w2->port_output);
|
||||
|
||||
if (w1->name.isPublic() && w2->name.isPublic()) {
|
||||
ShardedSigPool::AccumulatedValue s1_val = {s1, s1.hash_top().yield()};
|
||||
ShardedSigPool::AccumulatedValue s2_val = {s2, s2.hash_top().yield()};
|
||||
bool regs1 = regs.find(s1_val) != nullptr;
|
||||
bool regs2 = regs.find(s2_val) != nullptr;
|
||||
if (regs1 != regs2)
|
||||
return regs2;
|
||||
bool w1_exact = cell_wires.is_exactly_cell_driven(w1);
|
||||
bool w2_exact = cell_wires.is_exactly_cell_driven(w2);
|
||||
if (w1_exact != w2_exact)
|
||||
return w2_exact;
|
||||
bool conns1 = conns.find(s1_val) != nullptr;
|
||||
bool conns2 = conns.find(s2_val) != nullptr;
|
||||
if (conns1 != conns2)
|
||||
return conns2;
|
||||
}
|
||||
|
||||
if (w1 == w2)
|
||||
return s2.offset < s1.offset;
|
||||
|
||||
if (w1->port_output != w2->port_output)
|
||||
return w2->port_output;
|
||||
|
||||
if (w1->name[0] != w2->name[0])
|
||||
return w2->name.isPublic();
|
||||
|
||||
int attrs1 = count_nontrivial_wire_attrs(w1);
|
||||
int attrs2 = count_nontrivial_wire_attrs(w2);
|
||||
|
||||
if (attrs1 != attrs2)
|
||||
return attrs2 > attrs1;
|
||||
|
||||
return w2->name.lt_by_name(w1->name);
|
||||
}
|
||||
|
||||
bool check_public_name(RTLIL::IdString id)
|
||||
{
|
||||
if (id.begins_with("$"))
|
||||
return false;
|
||||
const std::string &id_str = id.str();
|
||||
if (id.begins_with("\\_") && (id.ends_with("_") || id_str.find("_[") != std::string::npos))
|
||||
return false;
|
||||
if (id_str.find(".$") != std::string::npos)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void add_spec(ShardedSigPool::Builder &builder, const ThreadIndex &thread, const RTLIL::SigSpec &spec) {
|
||||
for (SigBit bit : spec)
|
||||
if (bit.wire != nullptr)
|
||||
builder.insert(thread, {bit, bit.hash_top().yield()});
|
||||
}
|
||||
|
||||
bool check_any(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) {
|
||||
for (SigBit b : spec)
|
||||
if (sigs.find({b, b.hash_top().yield()}) != nullptr)
|
||||
return true;
|
||||
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;
|
||||
RTLIL::SigSpec spec;
|
||||
};
|
||||
void fixup_cell_ports(ShardedVector<UpdateConnection> &update_connections)
|
||||
{
|
||||
for (UpdateConnection &update : update_connections)
|
||||
update.cell->connections_.at(update.port) = std::move(update.spec);
|
||||
}
|
||||
|
||||
struct InitBits {
|
||||
dict<SigBit, RTLIL::State> values;
|
||||
// Wires that appear in the keys of the `values` dict
|
||||
pool<Wire*> wires;
|
||||
|
||||
// Set init attributes on all wires of a connected group
|
||||
void apply_normalised_inits() {
|
||||
for (RTLIL::Wire *wire : wires) {
|
||||
bool found = false;
|
||||
Const val(State::Sx, wire->width);
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
auto it = values.find(RTLIL::SigBit(wire, i));
|
||||
if (it != values.end()) {
|
||||
val.set(i, it->second);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
wire->attributes[ID::init] = val;
|
||||
}
|
||||
}
|
||||
};
|
||||
static InitBits consume_inits(ShardedVector<RTLIL::Wire*> &initialized_wires, const SigMap &assign_map)
|
||||
{
|
||||
InitBits init_bits;
|
||||
for (RTLIL::Wire *initialized_wire : initialized_wires) {
|
||||
auto it = initialized_wire->attributes.find(ID::init);
|
||||
RTLIL::Const &val = it->second;
|
||||
SigSpec sig = assign_map(initialized_wire);
|
||||
for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++)
|
||||
if (val[i] != State::Sx && sig[i].wire != nullptr) {
|
||||
init_bits.values[sig[i]] = val[i];
|
||||
init_bits.wires.insert(sig[i].wire);
|
||||
}
|
||||
initialized_wire->attributes.erase(it);
|
||||
}
|
||||
return init_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* What kinds of things are signals connected to?
|
||||
* Helps pick representatives out of groups of connected signals */
|
||||
struct SigConnKinds {
|
||||
// Wire bits directly driven by registers (with clk2fflogic exception)
|
||||
ShardedSigPool raw_registers;
|
||||
// Wire bits directly connected to any cell port
|
||||
ShardedSigPool raw_cell_connected;
|
||||
|
||||
// Signals exactly driven by a known cell output,
|
||||
// this will influence only our choice of representatives.
|
||||
// A signal is exactly driven by a cell output iff all its bits are driven by this output
|
||||
// and all bits of this output drive a bit of this signal.
|
||||
// Additionally, all signals that sigmap to this signal are exactly driven by the port, too
|
||||
ShardedSigSpecPool exact_cells;
|
||||
|
||||
SigConnKinds(bool purge_mode, const AnalysisContext& actx, CleanRunContext& clean_ctx) {
|
||||
ShardedSigPool::Builder raw_register_builder(actx.subpool);
|
||||
ShardedSigPool::Builder raw_cell_connected_builder(actx.subpool);
|
||||
ShardedSigSpecPool::Builder exact_cell_output_builder(actx.subpool);
|
||||
actx.subpool.run([&exact_cell_output_builder, &raw_register_builder, &raw_cell_connected_builder, purge_mode, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
|
||||
for (int i : ctx.item_range(actx.mod->cells_size())) {
|
||||
RTLIL::Cell *cell = actx.mod->cell_at(i);
|
||||
if (!purge_mode) {
|
||||
if (clean_ctx.ct_reg(cell->type)) {
|
||||
// Improve witness signal naming when clk2fflogic used
|
||||
// see commit message e36c71b5
|
||||
bool clk2fflogic = cell->get_bool_attribute(ID::clk2fflogic);
|
||||
for (auto &[port, sig] : cell->connections())
|
||||
if (clk2fflogic ? port == ID::D : clean_ctx.ct_all.cell_output(cell->type, port))
|
||||
add_spec(raw_register_builder, ctx, sig);
|
||||
}
|
||||
for (auto &[_, sig] : cell->connections())
|
||||
add_spec(raw_cell_connected_builder, ctx, sig);
|
||||
}
|
||||
if (clean_ctx.ct_all.cell_known(cell->type))
|
||||
for (auto &[port, sig] : cell->connections())
|
||||
if (clean_ctx.ct_all.cell_output(cell->type, port)) {
|
||||
RTLIL::SigSpec spec = actx.assign_map(sig);
|
||||
unsigned int hash = spec.hash_into(Hasher()).yield();
|
||||
exact_cell_output_builder.insert(ctx, {std::move(spec), hash});
|
||||
}
|
||||
}
|
||||
});
|
||||
actx.subpool.run([&raw_register_builder, &raw_cell_connected_builder, &exact_cell_output_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
raw_register_builder.process(ctx);
|
||||
raw_cell_connected_builder.process(ctx);
|
||||
exact_cell_output_builder.process(ctx);
|
||||
});
|
||||
raw_registers = raw_register_builder;
|
||||
raw_cell_connected = raw_cell_connected_builder;
|
||||
exact_cells = exact_cell_output_builder;
|
||||
}
|
||||
void clear(const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
raw_registers.clear(ctx);
|
||||
raw_cell_connected.clear(ctx);
|
||||
exact_cells.clear(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
ShardedVector<RTLIL::SigBit> build_candidates(ExactCellWires& cell_wires, const SigConnKinds& sig_analysis, const AnalysisContext& actx) {
|
||||
ShardedVector<RTLIL::SigBit> candidates(actx.subpool);
|
||||
actx.subpool.run([&actx, &sig_analysis, &candidates, &cell_wires](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
std::optional<ExactCellWires> local_cell_wires;
|
||||
ExactCellWires *this_thread_cell_wires = &cell_wires;
|
||||
if (ctx.thread_num > 0) {
|
||||
local_cell_wires.emplace(sig_analysis.exact_cells, actx.assign_map);
|
||||
this_thread_cell_wires = &local_cell_wires.value();
|
||||
}
|
||||
for (int i : ctx.item_range(actx.mod->wires_size())) {
|
||||
RTLIL::Wire *wire = actx.mod->wire_at(i);
|
||||
for (int j = 0; j < wire->width; ++j) {
|
||||
RTLIL::SigBit s1(wire, j);
|
||||
RTLIL::SigBit s2 = actx.assign_map(s1);
|
||||
if (compare_signals(s2, s1, sig_analysis.raw_registers, sig_analysis.raw_cell_connected, *this_thread_cell_wires))
|
||||
candidates.insert(ctx, s1);
|
||||
}
|
||||
}
|
||||
});
|
||||
return candidates;
|
||||
}
|
||||
|
||||
void update_assign_map(SigMap& assign_map, ShardedVector<RTLIL::SigBit>& sigmap_canonical_candidates, ExactCellWires& cell_wires, const SigConnKinds& sig_analysis) {
|
||||
for (RTLIL::SigBit candidate : sigmap_canonical_candidates) {
|
||||
RTLIL::SigBit current_canonical = assign_map(candidate);
|
||||
// Resolves if two threads in build_candidates found different candidates
|
||||
// for the same set
|
||||
// TODO adds effort for single-threaded?
|
||||
if (compare_signals(current_canonical, candidate, sig_analysis.raw_registers, sig_analysis.raw_cell_connected, cell_wires))
|
||||
assign_map.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
struct DeferredUpdates {
|
||||
// Deferred updates to the assign_map
|
||||
ShardedVector<UpdateConnection> update_connections;
|
||||
// Wires we should remove init from
|
||||
ShardedVector<RTLIL::Wire*> initialized_wires;
|
||||
DeferredUpdates(ParallelDispatchThreadPool::Subpool &subpool) : update_connections(subpool), initialized_wires(subpool) {}
|
||||
};
|
||||
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 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) {
|
||||
// Parallel destruction of these sharded structures
|
||||
sig_analysis.clear(ctx);
|
||||
|
||||
for (int i : ctx.item_range(actx.mod->cells_size())) {
|
||||
RTLIL::Cell *cell = actx.mod->cell_at(i);
|
||||
for (const auto &[port, sig] : cell->connections_) {
|
||||
SigSpec spec = actx.assign_map(sig);
|
||||
if (spec != sig)
|
||||
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())) {
|
||||
RTLIL::Wire *wire = actx.mod->wire_at(i);
|
||||
if (wire->port_id > 0) {
|
||||
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
|
||||
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);
|
||||
actx.assign_map.apply(sig);
|
||||
add_spec(conn_builder, ctx, sig);
|
||||
}
|
||||
auto it = wire->attributes.find(ID::init);
|
||||
if (it != wire->attributes.end())
|
||||
deferred.initialized_wires.insert(ctx, wire);
|
||||
}
|
||||
});
|
||||
actx.subpool.run([&conn_builder, &raw_conn_builder, &used_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};
|
||||
return deferred;
|
||||
}
|
||||
|
||||
struct WireDeleter {
|
||||
pool<RTLIL::Wire*> del_wires_queue;
|
||||
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) {
|
||||
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())) {
|
||||
RTLIL::Wire *wire = actx.mod->wire_at(i);
|
||||
SigSpec s1 = SigSpec(wire), s2 = actx.assign_map(s1);
|
||||
log_assert(GetSize(s1) == GetSize(s2));
|
||||
|
||||
Const initval;
|
||||
bool has_init_attribute = wire->attributes.count(ID::init);
|
||||
bool init_changed = false;
|
||||
if (has_init_attribute)
|
||||
initval = wire->attributes.at(ID::init);
|
||||
if (GetSize(initval) != GetSize(wire)) {
|
||||
initval.resize(GetSize(wire), State::Sx);
|
||||
init_changed = true;
|
||||
}
|
||||
|
||||
if (GetSize(wire) == 0) {
|
||||
// delete zero-width wires, unless they are module ports
|
||||
if (wire->port_id == 0)
|
||||
goto delete_this_wire;
|
||||
} else
|
||||
if (wire->port_id != 0 || wire->get_bool_attribute(ID::keep) || !initval.is_fully_undef()) {
|
||||
// do not delete anything with "keep" or module ports or initialized wires
|
||||
} else
|
||||
if (!purge_mode && check_public_name(wire->name) && (check_any(used_sig_analysis.raw_connected, s1) || check_any(used_sig_analysis.connected, s2) || s1 != s2)) {
|
||||
// do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
|
||||
} else
|
||||
if (!check_any(used_sig_analysis.raw_connected, s1)) {
|
||||
// delete wires that aren't used by anything directly
|
||||
goto delete_this_wire;
|
||||
}
|
||||
|
||||
if (0)
|
||||
{
|
||||
delete_this_wire:
|
||||
del_wires.insert(ctx, wire);
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::SigSig new_conn;
|
||||
for (int i = 0; i < GetSize(s1); i++)
|
||||
if (s1[i] != s2[i]) {
|
||||
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
|
||||
s2[i] = initval[i];
|
||||
initval.set(i, State::Sx);
|
||||
init_changed = true;
|
||||
}
|
||||
new_conn.first.append(s1[i]);
|
||||
new_conn.second.append(s2[i]);
|
||||
}
|
||||
if (new_conn.first.size() > 0)
|
||||
new_connections.insert(ctx, std::move(new_conn));
|
||||
if (initval.is_fully_undef()) {
|
||||
if (has_init_attribute)
|
||||
remove_init.insert(ctx, wire);
|
||||
} 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)});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
del_wires_queue.insert(del_wires.begin(), del_wires.end());
|
||||
}
|
||||
// Decide for each wire if we should be deleting it
|
||||
// and fix up attributes
|
||||
void commit_changes(RTLIL::Module* mod) {
|
||||
for (RTLIL::Wire *wire : remove_init)
|
||||
wire->attributes.erase(ID::init);
|
||||
for (auto &p : set_init)
|
||||
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;
|
||||
for (auto wire : del_wires_queue) {
|
||||
if (ys_debug() || (check_public_name(wire->name) && verbose))
|
||||
log_debug(" removing unused non-port wire %s.\n", wire->name);
|
||||
else
|
||||
deleted_and_unreported++;
|
||||
}
|
||||
mod->remove(del_wires_queue);
|
||||
return deleted_and_unreported;
|
||||
}
|
||||
};
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx)
|
||||
{
|
||||
// Passing actx to function == function does parallel work
|
||||
// Not passing module as function argument == function does not modify module
|
||||
// The above sentence signals intent; it's not enforced due to constness laundering in wire_at / cell_at
|
||||
AnalysisContext actx(module, subpool);
|
||||
SigConnKinds conn_kinds(clean_ctx.flags.purge, actx, clean_ctx);
|
||||
|
||||
ExactCellWires cell_wires(conn_kinds.exact_cells, actx.assign_map);
|
||||
// Collect sigmap representative candidates as built in parallel
|
||||
// With parallel runs, this creates redundant candidates that have to resolve in update_assign_map
|
||||
ShardedVector<RTLIL::SigBit> new_sigmap_rep_candidates = build_candidates(cell_wires, conn_kinds, actx);
|
||||
|
||||
// Cache all the cell_wires results that we might possible need. This avoids the results
|
||||
// changing when we update `assign_map` below.
|
||||
cell_wires.cache_all(new_sigmap_rep_candidates);
|
||||
// Modify assign_map to reflect the connectivity we want, not the one we have
|
||||
// this changes representative selection in assign_map
|
||||
update_assign_map(actx.assign_map, new_sigmap_rep_candidates, cell_wires, conn_kinds);
|
||||
|
||||
// Remove all wire-wire connections
|
||||
module->connections_.clear();
|
||||
|
||||
UsedSignals used;
|
||||
DeferredUpdates deferred = analyse_connectivity(used, conn_kinds, actx, clean_ctx);
|
||||
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
|
||||
consume_inits(deferred.initialized_wires, actx.assign_map).apply_normalised_inits();
|
||||
|
||||
WireDeleter deleter(used, clean_ctx.flags.purge, actx);
|
||||
|
||||
used.clear(subpool);
|
||||
|
||||
deleter.commit_changes(module);
|
||||
int deleted_and_unreported = deleter.delete_wires(module, clean_ctx.flags.verbose);
|
||||
int deleted_total = GetSize(deleter.del_wires_queue);
|
||||
|
||||
clean_ctx.stats.count_rm_wires += deleted_total;
|
||||
|
||||
if (clean_ctx.flags.verbose && deleted_and_unreported)
|
||||
log_debug(" removed %d unused temporary wires.\n", deleted_and_unreported);
|
||||
|
||||
if (deleted_total)
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
return deleted_total != 0;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -43,7 +43,7 @@ void demorgan_worker(
|
|||
if (GetSize(insig) < 1)
|
||||
return;
|
||||
|
||||
log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), GetSize(insig));
|
||||
log("Inspecting %s cell %s (%d inputs)\n", cell->type.unescape(), cell->name.unescape(), GetSize(insig));
|
||||
int num_inverted = 0;
|
||||
for(int i=0; i<GetSize(insig); i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ struct OptDffWorker
|
|||
initvals.remove_init(ff.sig_q[i]);
|
||||
module->connect(ff.sig_q[i], State::S0);
|
||||
log("Handling always-active CLR at position %d on %s (%s) from module %s (changing to const driver).\n",
|
||||
i, log_id(cell), log_id(cell->type), log_id(module));
|
||||
i, cell, cell->type.unescape(), module);
|
||||
sr_removed = true;
|
||||
} else if (is_always_active(ff.sig_set[i], ff.pol_set)) {
|
||||
initvals.remove_init(ff.sig_q[i]);
|
||||
|
|
@ -312,7 +312,7 @@ struct OptDffWorker
|
|||
else
|
||||
module->addNot(NEW_ID2_SUFFIX("not"), ff.sig_clr[i], ff.sig_q[i], false, cell->get_src_attribute());
|
||||
log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n",
|
||||
i, log_id(cell), log_id(cell->type), log_id(module));
|
||||
i, cell, cell->type.unescape(), module);
|
||||
sr_removed = true;
|
||||
} else {
|
||||
keep_bits.push_back(i);
|
||||
|
|
@ -335,7 +335,7 @@ struct OptDffWorker
|
|||
|
||||
if (clr_inactive && signal_all_same(ff.sig_set)) {
|
||||
log("Removing never-active CLR on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_sr = false;
|
||||
ff.has_arst = true;
|
||||
ff.pol_arst = ff.pol_set;
|
||||
|
|
@ -344,7 +344,7 @@ struct OptDffWorker
|
|||
changed = true;
|
||||
} else if (set_inactive && signal_all_same(ff.sig_clr)) {
|
||||
log("Removing never-active SET on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_sr = false;
|
||||
ff.has_arst = true;
|
||||
ff.pol_arst = ff.pol_clr;
|
||||
|
|
@ -370,7 +370,7 @@ struct OptDffWorker
|
|||
|
||||
if (!failed) {
|
||||
log("Converting CLR/SET to ARST on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_sr = false;
|
||||
ff.has_arst = true;
|
||||
ff.val_arst = val_arst_builder.build();
|
||||
|
|
@ -389,7 +389,7 @@ struct OptDffWorker
|
|||
// Converts constant Async Load to ARST
|
||||
if (is_always_inactive(ff.sig_aload, ff.pol_aload)) {
|
||||
log("Removing never-active async load on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_aload = false;
|
||||
changed = true;
|
||||
return false;
|
||||
|
|
@ -398,7 +398,7 @@ struct OptDffWorker
|
|||
if (is_active(ff.sig_aload, ff.pol_aload)) {
|
||||
// ALOAD always active
|
||||
log("Handling always-active async load on %s (%s) from module %s (changing to combinatorial circuit).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
|
||||
if (ff.has_sr) {
|
||||
SigSpec tmp;
|
||||
|
|
@ -433,7 +433,7 @@ struct OptDffWorker
|
|||
// AD is constant -> ARST
|
||||
if (ff.sig_ad.is_fully_const() && !ff.has_arst && !ff.has_sr) {
|
||||
log("Changing const-value async load to async reset on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_arst = true;
|
||||
ff.has_aload = false;
|
||||
ff.sig_arst = ff.sig_aload;
|
||||
|
|
@ -450,12 +450,12 @@ struct OptDffWorker
|
|||
// Removes ARST if never active or replaces FF if always active
|
||||
if (is_inactive(ff.sig_arst, ff.pol_arst)) {
|
||||
log("Removing never-active ARST on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_arst = false;
|
||||
changed = true;
|
||||
} else if (is_always_active(ff.sig_arst, ff.pol_arst)) {
|
||||
log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.remove();
|
||||
module->connect(ff.sig_q, ff.val_arst);
|
||||
return true;
|
||||
|
|
@ -469,12 +469,12 @@ struct OptDffWorker
|
|||
// Removes SRST if never active or forces D to reset value if always active
|
||||
if (is_inactive(ff.sig_srst, ff.pol_srst)) {
|
||||
log("Removing never-active SRST on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_srst = false;
|
||||
changed = true;
|
||||
} else if (is_always_active(ff.sig_srst, ff.pol_srst)) {
|
||||
log("Handling always-active SRST on %s (%s) from module %s (changing to const D).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_srst = false;
|
||||
if (!ff.ce_over_srst)
|
||||
ff.has_ce = false;
|
||||
|
|
@ -489,7 +489,7 @@ struct OptDffWorker
|
|||
if (is_always_inactive(ff.sig_ce, ff.pol_ce)) {
|
||||
if (ff.has_srst && !ff.ce_over_srst) {
|
||||
log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.pol_ce = ff.pol_srst;
|
||||
ff.sig_ce = ff.sig_srst;
|
||||
ff.has_srst = false;
|
||||
|
|
@ -497,7 +497,7 @@ struct OptDffWorker
|
|||
changed = true;
|
||||
} else if (!opt.keepdc || ff.val_init.is_fully_def()) {
|
||||
log("Handling never-active EN on %s (%s) from module %s (removing D path).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_ce = ff.has_clk = ff.has_srst = false;
|
||||
changed = true;
|
||||
} else {
|
||||
|
|
@ -507,7 +507,7 @@ struct OptDffWorker
|
|||
}
|
||||
} else if (is_active(ff.sig_ce, ff.pol_ce)) {
|
||||
log("Removing always-active EN on %s (%s) from module %s.\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_ce = false;
|
||||
changed = true;
|
||||
}
|
||||
|
|
@ -517,7 +517,7 @@ struct OptDffWorker
|
|||
{
|
||||
if (!opt.keepdc || ff.val_init.is_fully_def()) {
|
||||
log("Handling const CLK on %s (%s) from module %s (removing D path).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_ce = ff.has_clk = ff.has_srst = false;
|
||||
changed = true;
|
||||
} else if (ff.has_ce || ff.has_srst || ff.sig_d != ff.sig_q) {
|
||||
|
|
@ -532,7 +532,7 @@ struct OptDffWorker
|
|||
// Detect feedback loops where D is hardwired to Q
|
||||
if (ff.has_clk && ff.has_srst) {
|
||||
log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
if (ff.has_ce && ff.ce_over_srst) {
|
||||
SigSpec ce = ff.pol_ce ? ff.sig_ce : create_not(ff.sig_ce, ff.is_fine, cell);
|
||||
SigSpec srst = ff.pol_srst ? ff.sig_srst : create_not(ff.sig_srst, ff.is_fine, cell);
|
||||
|
|
@ -549,7 +549,7 @@ struct OptDffWorker
|
|||
changed = true;
|
||||
} else if (!opt.keepdc || ff.val_init.is_fully_def()) {
|
||||
log("Handling D = Q on %s (%s) from module %s (removing D path).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_gclk = ff.has_clk = ff.has_ce = false;
|
||||
changed = true;
|
||||
}
|
||||
|
|
@ -628,7 +628,7 @@ struct OptDffWorker
|
|||
dff_cells.push_back(new_cell);
|
||||
|
||||
log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module),
|
||||
cell, cell->type.unescape(), module,
|
||||
log_signal(new_ff.sig_d), log_signal(new_ff.sig_q), log_signal(new_ff.val_srst));
|
||||
}
|
||||
|
||||
|
|
@ -703,7 +703,7 @@ struct OptDffWorker
|
|||
dff_cells.push_back(new_cell);
|
||||
|
||||
log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module),
|
||||
cell, cell->type.unescape(), module,
|
||||
log_signal(new_ff.sig_d), log_signal(new_ff.sig_q));
|
||||
}
|
||||
|
||||
|
|
@ -770,7 +770,7 @@ struct OptDffWorker
|
|||
|
||||
if (ff.has_aload && !ff.has_clk && ff.sig_ad == ff.sig_q) {
|
||||
log_debug("Handling AD = Q on %s (%s) from module %s (removing async load path).\n",
|
||||
log_id(cell), log_id(cell->type), log_id(module));
|
||||
cell, cell->type.unescape(), module);
|
||||
ff.has_aload = false;
|
||||
changed = true;
|
||||
}
|
||||
|
|
@ -887,7 +887,7 @@ struct OptDffWorker
|
|||
}
|
||||
|
||||
log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n",
|
||||
val ? 1 : 0, i, log_id(cell), log_id(cell->type), log_id(module));
|
||||
val ? 1 : 0, i, cell, cell->type.unescape(), module);
|
||||
|
||||
// Replace the Q output with the constant value
|
||||
initvals.remove_init(ff.sig_q[i]);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
|
|
@ -31,7 +32,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
bool did_something;
|
||||
|
||||
void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
|
||||
void replace_undriven(RTLIL::Module *module, const NewCellTypes &ct)
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
SigPool driven_signals;
|
||||
|
|
@ -87,7 +88,7 @@ void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
|
|||
}
|
||||
}
|
||||
|
||||
log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val));
|
||||
log_debug("Setting undriven signal in %s to constant: %s = %s\n", module, log_signal(sig), log_signal(val));
|
||||
module->connect(sig, val);
|
||||
did_something = true;
|
||||
}
|
||||
|
|
@ -104,11 +105,11 @@ void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
|
|||
initval.set(i, State::Sx);
|
||||
}
|
||||
if (initval.is_fully_undef()) {
|
||||
log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire));
|
||||
log_debug("Removing init attribute from %s/%s.\n", module, wire);
|
||||
wire->attributes.erase(ID::init);
|
||||
did_something = true;
|
||||
} else if (initval != wire->attributes.at(ID::init)) {
|
||||
log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
|
||||
log_debug("Updating init attribute on %s/%s: %s\n", module, wire, log_signal(initval));
|
||||
wire->attributes[ID::init] = initval;
|
||||
did_something = true;
|
||||
}
|
||||
|
|
@ -195,7 +196,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
return false;
|
||||
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
|
||||
log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->type.unescape(), cell, module);
|
||||
|
||||
for (int i = 0; i < GRP_N; i++)
|
||||
{
|
||||
|
|
@ -223,7 +224,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
new_a.replace(dict<SigBit,SigBit>{{State::Sx, State::S1}, {State::Sz, State::S1}}, &new_b);
|
||||
else log_abort();
|
||||
}
|
||||
log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
|
||||
log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), cell->type.unescape(), log_signal(new_a));
|
||||
module->connect(new_y, new_b);
|
||||
module->connect(new_conn);
|
||||
continue;
|
||||
|
|
@ -260,7 +261,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
}
|
||||
}
|
||||
if (!undef_y.empty()) {
|
||||
log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(undef_b), log_id(cell->type), log_signal(undef_a));
|
||||
log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(undef_b), cell->type.unescape(), log_signal(undef_a));
|
||||
module->connect(undef_y, undef_b);
|
||||
if (def_y.empty()) {
|
||||
module->connect(new_conn);
|
||||
|
|
@ -294,7 +295,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
|
||||
module->connect(new_conn);
|
||||
|
||||
log_debug(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
|
||||
log_debug(" New cell `%s': A=%s", c, log_signal(new_a));
|
||||
if (b_name == ID::B)
|
||||
log_debug(", B=%s", log_signal(new_b));
|
||||
log_debug("\n");
|
||||
|
|
@ -310,7 +311,7 @@ void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap
|
|||
SigSpec sig = assign_map(cell->getPort(port));
|
||||
if (invert_map.count(sig)) {
|
||||
log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
|
||||
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
|
||||
port.unescape(), cell->type.unescape(), cell, cell->module,
|
||||
log_signal(sig), log_signal(invert_map.at(sig)));
|
||||
cell->setPort(port, (invert_map.at(sig)));
|
||||
cell->setParam(param, !cell->getParam(param).as_bool());
|
||||
|
|
@ -339,7 +340,7 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin
|
|||
SigSpec sig = assign_map(cell->getPort(port));
|
||||
if (invert_map.count(sig)) {
|
||||
log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
|
||||
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
|
||||
port.unescape(), cell->type.unescape(), cell, cell->module,
|
||||
log_signal(sig), log_signal(invert_map.at(sig)));
|
||||
cell->setPort(port, (invert_map.at(sig)));
|
||||
cell->type = cell->type == type1 ? type2 : type1;
|
||||
|
|
@ -410,9 +411,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
}
|
||||
}
|
||||
|
||||
CellTypes ct_memcells;
|
||||
ct_memcells.setup_stdcells_mem();
|
||||
|
||||
if (!noclkinv)
|
||||
for (auto cell : module->cells())
|
||||
if (design->selected(module, cell)) {
|
||||
|
|
@ -436,7 +434,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
|
||||
handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map);
|
||||
|
||||
if (!ct_memcells.cell_known(cell->type))
|
||||
if (!StaticCellTypes::Compat::stdcells_mem(cell->type))
|
||||
continue;
|
||||
|
||||
handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map);
|
||||
|
|
@ -516,7 +514,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
if (!cells.sort()) {
|
||||
// There might be a combinational loop, or there might be constants on the output of cells. 'check' may find out more.
|
||||
// ...unless this is a coarse-grained cell loop, but not a bit loop, in which case it won't, and all is good.
|
||||
log("Couldn't topologically sort cells, optimizing module %s may take a longer time.\n", log_id(module));
|
||||
log("Couldn't topologically sort cells, optimizing module %s may take a longer time.\n", module);
|
||||
}
|
||||
|
||||
for (auto cell : cells.sorted)
|
||||
|
|
@ -636,7 +634,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
if (cell->type == ID($reduce_xnor)) {
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
|
||||
log_id(cell->type), log_id(cell->name), log_id(module));
|
||||
cell->type.unescape(), cell->name.unescape(), module);
|
||||
cell->type = ID($not);
|
||||
did_something = true;
|
||||
} else {
|
||||
|
|
@ -656,7 +654,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
if (a_fully_const != b_fully_const)
|
||||
{
|
||||
log_debug("Replacing %s cell `%s' in module `%s' having one fully constant input\n",
|
||||
log_id(cell->type), log_id(cell->name), log_id(module));
|
||||
cell->type.unescape(), cell->name.unescape(), module);
|
||||
RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
|
||||
|
||||
int width = GetSize(cell->getPort(ID::Y));
|
||||
|
|
@ -952,7 +950,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
break;
|
||||
}
|
||||
if (i > 0) {
|
||||
log_debug("Stripping %d LSB bits of %s cell %s in module %s.\n", i, log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Stripping %d LSB bits of %s cell %s in module %s.\n", i, cell->type.unescape(), cell, module);
|
||||
SigSpec new_a = sig_a.extract_end(i);
|
||||
SigSpec new_b = sig_b.extract_end(i);
|
||||
if (new_a.empty() && is_signed)
|
||||
|
|
@ -1008,7 +1006,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
break;
|
||||
}
|
||||
if (i > 0) {
|
||||
log_debug("Stripping %d LSB bits of %s cell %s in module %s.\n", i, log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Stripping %d LSB bits of %s cell %s in module %s.\n", i, cell->type.unescape(), cell, module);
|
||||
SigSpec new_a = sig_a.extract_end(i);
|
||||
SigSpec new_b = sig_b.extract_end(i);
|
||||
if (new_a.empty() && is_signed)
|
||||
|
|
@ -1082,7 +1080,7 @@ skip_fine_alu:
|
|||
}
|
||||
|
||||
if (cell->type.in(ID($_MUX_), ID($mux)) && invert_map.count(assign_map(cell->getPort(ID::S))) != 0) {
|
||||
log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", cell->type.unescape(), cell, module);
|
||||
RTLIL::SigSpec tmp = cell->getPort(ID::A);
|
||||
cell->setPort(ID::A, cell->getPort(ID::B));
|
||||
cell->setPort(ID::B, tmp);
|
||||
|
|
@ -1261,7 +1259,7 @@ skip_fine_alu:
|
|||
RTLIL::SigSpec input = b;
|
||||
ACTION_DO(ID::Y, cell->getPort(ID::A));
|
||||
} else {
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", cell->type.unescape(), cell, module);
|
||||
cell->type = ID($not);
|
||||
cell->parameters.erase(ID::B_WIDTH);
|
||||
cell->parameters.erase(ID::B_SIGNED);
|
||||
|
|
@ -1275,8 +1273,8 @@ skip_fine_alu:
|
|||
if (cell->type.in(ID($eq), ID($ne)) &&
|
||||
(assign_map(cell->getPort(ID::A)).is_fully_zero() || assign_map(cell->getPort(ID::B)).is_fully_zero()))
|
||||
{
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
|
||||
log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", cell->type.unescape(), cell,
|
||||
module, cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
|
||||
cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
|
||||
if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
|
||||
cell->setPort(ID::A, cell->getPort(ID::B));
|
||||
|
|
@ -1323,7 +1321,7 @@ skip_fine_alu:
|
|||
}
|
||||
|
||||
log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
|
||||
log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort(ID::B))), shift_bits, log_id(module), log_signal(sig_y));
|
||||
cell->type.unescape(), cell, log_signal(assign_map(cell->getPort(ID::B))), shift_bits, module, log_signal(sig_y));
|
||||
|
||||
module->connect(cell->getPort(ID::Y), sig_y);
|
||||
module->remove(cell);
|
||||
|
|
@ -1448,7 +1446,7 @@ skip_identity:
|
|||
|
||||
if (mux_bool && cell->type.in(ID($mux), ID($_MUX_)) &&
|
||||
cell->getPort(ID::A) == State::S1 && cell->getPort(ID::B) == State::S0) {
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", cell->type.unescape(), cell, module);
|
||||
cell->setPort(ID::A, cell->getPort(ID::S));
|
||||
cell->unsetPort(ID::B);
|
||||
cell->unsetPort(ID::S);
|
||||
|
|
@ -1466,7 +1464,7 @@ skip_identity:
|
|||
}
|
||||
|
||||
if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::A) == State::S0) {
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", cell->type.unescape(), cell, module);
|
||||
cell->setPort(ID::A, cell->getPort(ID::S));
|
||||
cell->unsetPort(ID::S);
|
||||
if (cell->type == ID($mux)) {
|
||||
|
|
@ -1485,7 +1483,7 @@ skip_identity:
|
|||
}
|
||||
|
||||
if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::B) == State::S1) {
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", cell->type.unescape(), cell, module);
|
||||
cell->setPort(ID::B, cell->getPort(ID::S));
|
||||
cell->unsetPort(ID::S);
|
||||
if (cell->type == ID($mux)) {
|
||||
|
|
@ -1535,7 +1533,7 @@ skip_identity:
|
|||
}
|
||||
if (cell->getPort(ID::S).size() != new_s.size()) {
|
||||
log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
|
||||
GetSize(cell->getPort(ID::S)) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
|
||||
GetSize(cell->getPort(ID::S)) - GetSize(new_s), cell->type.unescape(), cell, module);
|
||||
cell->setPort(ID::A, new_a);
|
||||
cell->setPort(ID::B, new_b);
|
||||
cell->setPort(ID::S, new_s);
|
||||
|
|
@ -2046,7 +2044,7 @@ skip_alu_split:
|
|||
Const y_value(cell->type.in(ID($eq), ID($eqx)) ? 0 : 1, GetSize(y_sig));
|
||||
|
||||
log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
|
||||
log_id(cell), log_id(module), log_signal(y_value));
|
||||
cell, module, log_signal(y_value));
|
||||
|
||||
module->connect(y_sig, y_value);
|
||||
module->remove(cell);
|
||||
|
|
@ -2058,7 +2056,7 @@ skip_alu_split:
|
|||
if (redundant_bits)
|
||||
{
|
||||
log_debug("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
|
||||
redundant_bits, log_id(cell->type), log_id(cell), log_id(module));
|
||||
redundant_bits, cell->type.unescape(), cell, module);
|
||||
|
||||
cell->setPort(ID::A, sig_a);
|
||||
cell->setPort(ID::B, sig_b);
|
||||
|
|
@ -2209,7 +2207,7 @@ skip_alu_split:
|
|||
if (replace || remove)
|
||||
{
|
||||
log_debug("Replacing %s cell `%s' (implementing %s) with %s.\n",
|
||||
log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
|
||||
cell->type.unescape(), cell, condition.c_str(), replacement.c_str());
|
||||
if (replace)
|
||||
module->connect(cell->getPort(ID::Y), replace_sig);
|
||||
module->remove(cell);
|
||||
|
|
@ -2554,10 +2552,10 @@ struct OptExprPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
CellTypes ct(design);
|
||||
NewCellTypes ct(design);
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
log("Optimizing module %s.\n", log_id(module));
|
||||
log("Optimizing module %s.\n", module);
|
||||
|
||||
if (undriven) {
|
||||
did_something = false;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ struct ModuleIndex {
|
|||
} else {
|
||||
classes[pair.second[i]].append(pair.first[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ struct ModuleIndex {
|
|||
if (!port || (!port->port_input && !port->port_output) || port->width != value.size()) {
|
||||
log_error("Port %s connected on instance %s not found in module %s"
|
||||
" or width is not matching\n",
|
||||
log_id(port_name), log_id(instantiation), log_id(module));
|
||||
port_name.unescape(), instantiation, module);
|
||||
}
|
||||
|
||||
if (port->port_input && port->port_output) {
|
||||
|
|
@ -151,12 +151,12 @@ struct ModuleIndex {
|
|||
|
||||
if (nunused > 0) {
|
||||
log("Disconnected %d input bits of instance '%s' (type '%s') in '%s'\n",
|
||||
nunused, log_id(instantiation), log_id(instantiation->type), log_id(parent.module));
|
||||
nunused, instantiation, instantiation->type.unescape(), parent.module);
|
||||
changed = true;
|
||||
}
|
||||
if (nconstants > 0) {
|
||||
log("Substituting constant for %d output bits of instance '%s' (type '%s') in '%s'\n",
|
||||
nconstants, log_id(instantiation), log_id(instantiation->type), log_id(parent.module));
|
||||
nconstants, instantiation, instantiation->type.unescape(), parent.module);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ struct ModuleIndex {
|
|||
|
||||
if (ntie_togethers > 0) {
|
||||
log("Replacing %d output bits with tie-togethers on instance '%s' of '%s' in '%s'\n",
|
||||
ntie_togethers, log_id(instantiation), log_id(instantiation->type), log_id(parent.module));
|
||||
ntie_togethers, instantiation, instantiation->type.unescape(), parent.module);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ struct UsageData {
|
|||
for (auto port_name : module->ports) {
|
||||
Wire *port = module->wire(port_name);
|
||||
log_assert(port);
|
||||
|
||||
|
||||
if (port->port_input && port->port_output) {
|
||||
// ignore bidirectional: hard to come up with sound handling
|
||||
continue;
|
||||
|
|
@ -296,7 +296,7 @@ struct UsageData {
|
|||
if (!port || (!port->port_input && !port->port_output) || port->width != value.size()) {
|
||||
log_error("Port %s connected on instance %s not found in module %s"
|
||||
" or width is not matching\n",
|
||||
log_id(port_name), log_id(instance), log_id(module));
|
||||
port_name.unescape(), instance, module);
|
||||
}
|
||||
|
||||
if (port->port_input && port->port_output) {
|
||||
|
|
@ -353,7 +353,7 @@ struct UsageData {
|
|||
};
|
||||
module->rewrite_sigspecs(disconnect_rewrite);
|
||||
for (auto chunk : disconnect_outputs.chunks()) {
|
||||
log("Disconnected unused output terminal '%s' in module '%s'\n", log_signal(chunk), log_id(module));
|
||||
log("Disconnected unused output terminal '%s' in module '%s'\n", log_signal(chunk), module);
|
||||
did_something = true;
|
||||
module->connect(chunk, SigSpec(RTLIL::Sx, chunk.size()));
|
||||
}
|
||||
|
|
@ -374,7 +374,7 @@ struct UsageData {
|
|||
SigSpec const_ = chunk;
|
||||
const_.replace(constant_inputs);
|
||||
log("Substituting constant %s for input terminal '%s' in module '%s'\n",
|
||||
log_signal(const_), log_signal(chunk), log_id(module));
|
||||
log_signal(const_), log_signal(chunk), module);
|
||||
}
|
||||
|
||||
// Propagate tied-together inputs
|
||||
|
|
@ -403,7 +403,7 @@ struct UsageData {
|
|||
module->rewrite_sigspecs(ties_rewrite);
|
||||
if (applied_ties.size()) {
|
||||
log("Replacing %zu input terminal bits with tie-togethers in module '%s'\n",
|
||||
applied_ties.size(), log_id(module));
|
||||
applied_ties.size(), module);
|
||||
}
|
||||
return did_something;
|
||||
}
|
||||
|
|
@ -439,7 +439,7 @@ struct OptHierPass : Pass {
|
|||
|
||||
dict<IdString, ModuleIndex> indices;
|
||||
for (auto module : d->modules()) {
|
||||
log_debug("Building index for %s\n", log_id(module));
|
||||
log_debug("Building index for %s\n", module);
|
||||
indices.emplace(module->name, ModuleIndex(module));
|
||||
}
|
||||
|
||||
|
|
@ -448,14 +448,14 @@ struct OptHierPass : Pass {
|
|||
if (module->get_bool_attribute(ID::top) || is_dw_module(module))
|
||||
continue;
|
||||
|
||||
log_debug("Starting usage data for %s\n", log_id(module));
|
||||
log_debug("Starting usage data for %s\n", module);
|
||||
usage_datas.emplace(module->name, UsageData(module));
|
||||
}
|
||||
|
||||
for (auto module : d->modules()) {
|
||||
for (auto cell : module->cells()) {
|
||||
if (usage_datas.count(cell->type)) {
|
||||
log_debug("Account for instance %s of %s in %s\n", log_id(cell), log_id(cell->type), log_id(module));
|
||||
log_debug("Account for instance %s of %s in %s\n", cell, cell->type.unescape(), module);
|
||||
usage_datas.at(cell->type).refine(cell, indices.at(module->name));
|
||||
}
|
||||
}
|
||||
|
|
@ -471,14 +471,14 @@ struct OptHierPass : Pass {
|
|||
ModuleIndex &parent_index = indices.at(module->name);
|
||||
|
||||
if (usage_datas.count(module->name)) {
|
||||
log_debug("Applying usage data changes to %s\n", log_id(module));
|
||||
log_debug("Applying usage data changes to %s\n", module);
|
||||
did_something |= usage_datas.at(module->name).apply_changes(parent_index);
|
||||
}
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
Module *child = d->module(cell->type);
|
||||
if (child != nullptr && !is_dw_module(child) && indices.count(cell->type)) {
|
||||
log_debug("Applying changes to instance %s of %s in %s\n", log_id(cell), log_id(cell->type), log_id(module));
|
||||
log_debug("Applying changes to instance %s of %s in %s\n", cell, cell->type.unescape(), module);
|
||||
did_something |= indices.at(cell->type).apply_changes(parent_index, cell);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ struct OptLutWorker
|
|||
SigSpec lut_input = cell->getPort(ID::A);
|
||||
int lut_arity = 0;
|
||||
|
||||
log_debug("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell));
|
||||
log_debug("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, module, cell);
|
||||
luts.insert(cell);
|
||||
|
||||
// First, find all dedicated logic we're connected to. This results in an overapproximation
|
||||
|
|
@ -162,7 +162,7 @@ struct OptLutWorker
|
|||
{
|
||||
if (lut_width <= dlogic_conn.first)
|
||||
{
|
||||
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type, log_id(module), log_id(lut_dlogic.second));
|
||||
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type, module, lut_dlogic.second);
|
||||
log_debug(" LUT input A[%d] not present.\n", dlogic_conn.first);
|
||||
legal = false;
|
||||
break;
|
||||
|
|
@ -173,7 +173,7 @@ struct OptLutWorker
|
|||
|
||||
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)[0]))
|
||||
{
|
||||
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type, log_id(module), log_id(lut_dlogic.second));
|
||||
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type, module, lut_dlogic.second);
|
||||
log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type, dlogic_conn.second, log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
|
||||
legal = false;
|
||||
break;
|
||||
|
|
@ -182,7 +182,7 @@ struct OptLutWorker
|
|||
|
||||
if (legal)
|
||||
{
|
||||
log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type, log_id(module), log_id(lut_dlogic.second));
|
||||
log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type, module, lut_dlogic.second);
|
||||
lut_legal_dlogics.insert(lut_dlogic);
|
||||
for (auto &dlogic_conn : dlogic_map)
|
||||
lut_dlogic_inputs.insert(dlogic_conn.first);
|
||||
|
|
@ -258,7 +258,7 @@ struct OptLutWorker
|
|||
|
||||
if (const0_match || const1_match || input_match != -1)
|
||||
{
|
||||
log_debug("Found redundant cell %s.%s.\n", log_id(module), log_id(lut));
|
||||
log_debug("Found redundant cell %s.%s.\n", module, lut);
|
||||
|
||||
SigBit value;
|
||||
if (const0_match)
|
||||
|
|
@ -341,7 +341,7 @@ struct OptLutWorker
|
|||
int lutB_arity = luts_arity[lutB];
|
||||
pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB];
|
||||
|
||||
log_debug("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB));
|
||||
log_debug("Found %s.%s (cell A) feeding %s.%s (cell B).\n", module, lutA, module, lutB);
|
||||
|
||||
if (index.query_is_output(lutA->getPort(ID::Y)))
|
||||
{
|
||||
|
|
@ -421,13 +421,12 @@ struct OptLutWorker
|
|||
}
|
||||
|
||||
RTLIL::Cell *lutM, *lutR;
|
||||
pool<SigBit> lutM_inputs, lutR_inputs;
|
||||
pool<SigBit> lutR_inputs;
|
||||
pool<int> lutM_dlogic_inputs;
|
||||
if (combine == COMBINE_A)
|
||||
{
|
||||
log_debug(" Combining LUTs into cell A.\n");
|
||||
lutM = lutA;
|
||||
lutM_inputs = lutA_inputs;
|
||||
lutM_dlogic_inputs = lutA_dlogic_inputs;
|
||||
lutR = lutB;
|
||||
lutR_inputs = lutB_inputs;
|
||||
|
|
@ -436,7 +435,6 @@ struct OptLutWorker
|
|||
{
|
||||
log_debug(" Combining LUTs into cell B.\n");
|
||||
lutM = lutB;
|
||||
lutM_inputs = lutB_inputs;
|
||||
lutM_dlogic_inputs = lutB_dlogic_inputs;
|
||||
lutR = lutA;
|
||||
lutR_inputs = lutA_inputs;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ struct OptLutInsPass : public Pass {
|
|||
log("\n");
|
||||
log(" -tech <technology>\n");
|
||||
log(" Instead of generic $lut cells, operate on LUT cells specific\n");
|
||||
log(" to the given technology. Valid values are: xilinx, lattice, gowin.\n");
|
||||
log(" to the given technology. Valid values are: xilinx, lattice,\n");
|
||||
log(" gowin, analogdevices.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
|
|
@ -58,12 +59,12 @@ struct OptLutInsPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "ecp5" && techname != "gowin")
|
||||
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "analogdevices" && techname != "gowin")
|
||||
log_cmd_error("Unsupported technology: '%s'\n", techname);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
log("Optimizing LUTs in %s.\n", log_id(module));
|
||||
log("Optimizing LUTs in %s.\n", module);
|
||||
|
||||
std::vector<Cell *> remove_cells;
|
||||
// Gather LUTs.
|
||||
|
|
@ -81,7 +82,7 @@ struct OptLutInsPass : public Pass {
|
|||
inputs = cell->getPort(ID::A);
|
||||
output = cell->getPort(ID::Y);
|
||||
lut = cell->getParam(ID::LUT);
|
||||
} else if (techname == "xilinx" || techname == "gowin") {
|
||||
} else if (techname == "xilinx" || techname == "gowin" || techname == "analogdevices") {
|
||||
if (cell->type == ID(LUT1)) {
|
||||
inputs = {
|
||||
cell->getPort(ID(I0)),
|
||||
|
|
@ -126,11 +127,11 @@ struct OptLutInsPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
lut = cell->getParam(ID::INIT);
|
||||
if (techname == "xilinx")
|
||||
if (techname == "xilinx" || techname == "analogdevices")
|
||||
output = cell->getPort(ID::O);
|
||||
else
|
||||
output = cell->getPort(ID::F);
|
||||
} else if (techname == "lattice" || techname == "ecp5") {
|
||||
} else if (techname == "lattice") {
|
||||
if (cell->type == ID(LUT4)) {
|
||||
inputs = {
|
||||
cell->getPort(ID::A),
|
||||
|
|
@ -180,7 +181,7 @@ struct OptLutInsPass : public Pass {
|
|||
}
|
||||
if (!doit)
|
||||
continue;
|
||||
log(" Optimizing lut %s (%d -> %d)\n", log_id(cell), GetSize(inputs), GetSize(new_inputs));
|
||||
log(" Optimizing lut %s (%d -> %d)\n", cell, GetSize(inputs), GetSize(new_inputs));
|
||||
if (techname == "lattice" || techname == "ecp5") {
|
||||
// Pad the LUT to 4 inputs, adding consts from the front.
|
||||
int extra = 4 - GetSize(new_inputs);
|
||||
|
|
@ -236,7 +237,7 @@ struct OptLutInsPass : public Pass {
|
|||
} else {
|
||||
// xilinx, gowin
|
||||
cell->setParam(ID::INIT, new_lut);
|
||||
if (techname == "xilinx")
|
||||
if (techname == "xilinx" || techname == "analogdevices")
|
||||
log_assert(GetSize(new_inputs) <= 6);
|
||||
else
|
||||
log_assert(GetSize(new_inputs) <= 4);
|
||||
|
|
|
|||
|
|
@ -108,13 +108,13 @@ struct OptMemPass : public Pass {
|
|||
}
|
||||
State bit;
|
||||
if (!always_0[i]) {
|
||||
log("%s.%s: removing const-1 lane %d\n", log_id(module->name), log_id(mem.memid), i);
|
||||
log("%s.%s: removing const-1 lane %d\n", module->name.unescape(), mem.memid.unescape(), i);
|
||||
bit = State::S1;
|
||||
} else if (!always_1[i]) {
|
||||
log("%s.%s: removing const-0 lane %d\n", log_id(module->name), log_id(mem.memid), i);
|
||||
log("%s.%s: removing const-0 lane %d\n", module->name.unescape(), mem.memid.unescape(), i);
|
||||
bit = State::S0;
|
||||
} else {
|
||||
log("%s.%s: removing const-x lane %d\n", log_id(module->name), log_id(mem.memid), i);
|
||||
log("%s.%s: removing const-x lane %d\n", module->name.unescape(), mem.memid.unescape(), i);
|
||||
bit = State::Sx;
|
||||
}
|
||||
// Reconnect read port data.
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ struct OptMemFeedbackWorker
|
|||
{
|
||||
auto &port = mem.wr_ports[i];
|
||||
|
||||
log(" Analyzing %s.%s write port %d.\n", log_id(module), log_id(mem.memid), i);
|
||||
log(" Analyzing %s.%s write port %d.\n", module, mem.memid.unescape(), i);
|
||||
|
||||
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
|
||||
{
|
||||
|
|
@ -232,7 +232,7 @@ struct OptMemFeedbackWorker
|
|||
|
||||
// Okay, let's do it.
|
||||
|
||||
log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", log_id(module), log_id(mem.memid));
|
||||
log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", module, mem.memid.unescape());
|
||||
|
||||
// If a write port has a feedback path that we're about to bypass,
|
||||
// but also has priority over some other write port, the feedback
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ struct OptMemWidenPass : public Pass {
|
|||
factor_log2 = port.wide_log2;
|
||||
if (factor_log2 == 0)
|
||||
continue;
|
||||
log("Widening base width of memory %s in module %s by factor %d.\n", log_id(mem.memid), log_id(module->name), 1 << factor_log2);
|
||||
log("Widening base width of memory %s in module %s by factor %d.\n", mem.memid.unescape(), module->name.unescape(), 1 << factor_log2);
|
||||
total_count++;
|
||||
// The inits are too messy to expand one-by-one, for they may
|
||||
// collide with one another after expansion. Just hit it with
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ struct OptMergeThreadWorker
|
|||
CellEqualOp> known_cells(0, CellHashOp(), CellEqualOp(*this));
|
||||
|
||||
std::vector<DuplicateCell> duplicates;
|
||||
for (const std::vector<std::vector<CellHash>> &buckets : in.bucketed_cell_hashes) {
|
||||
for (std::vector<std::vector<CellHash>> &buckets : in.bucketed_cell_hashes) {
|
||||
// Clear out our buckets as we go. This keeps the work of deallocation
|
||||
// off the main thread.
|
||||
std::vector<CellHash> bucket = std::move(buckets[index]);
|
||||
|
|
|
|||
|
|
@ -21,11 +21,9 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include <cstddef>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
|
@ -128,7 +126,7 @@ struct OptMuxtreeWorker
|
|||
// In case of $pmux, port B is multiple slices, concatenated, one per bit of port S
|
||||
for (int i = 0; i < GetSize(sig_s); i++) {
|
||||
RTLIL::SigSpec sig = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a));
|
||||
RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1));
|
||||
RTLIL::SigSpec ctrl_sig = assign_map(SigSpec{sig_s[i]});
|
||||
portinfo_t portinfo = used_port_bit(sig, this_mux_idx);
|
||||
portinfo.ctrl_sig = sig2bits(ctrl_sig, false).front();
|
||||
portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool();
|
||||
|
|
@ -202,6 +200,29 @@ struct OptMuxtreeWorker
|
|||
root_muxes.at(driving_mux) = true;
|
||||
}
|
||||
|
||||
struct knowledge_t
|
||||
{
|
||||
// Known inactive signals
|
||||
// The payload is a reference counter used to manage the list
|
||||
// When it is non-zero, the signal in known to be inactive
|
||||
// When it reaches zero, the map element is removed
|
||||
std::vector<int> known_inactive;
|
||||
|
||||
// database of known active signals
|
||||
std::vector<int> known_active;
|
||||
|
||||
// this is just used to keep track of visited muxes in order to prohibit
|
||||
// endless recursion in mux loops
|
||||
std::vector<bool> visited_muxes;
|
||||
|
||||
// Initialize with the maximum possible sizes
|
||||
knowledge_t(int num_bits, int num_muxes) {
|
||||
known_inactive.assign(num_bits, 0);
|
||||
known_active.assign(num_bits, 0);
|
||||
visited_muxes.assign(num_muxes, false);
|
||||
}
|
||||
};
|
||||
|
||||
OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) :
|
||||
design(design), module(module), assign_map(module), removed_count(0)
|
||||
{
|
||||
|
|
@ -229,11 +250,13 @@ struct OptMuxtreeWorker
|
|||
|
||||
populate_roots();
|
||||
|
||||
knowledge_t shared_knowledge(GetSize(bit2info), GetSize(mux2info));
|
||||
|
||||
for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++)
|
||||
if (root_muxes.at(mux_idx)) {
|
||||
log_debug(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
|
||||
log_debug(" Root of a mux tree: %s%s\n", mux2info[mux_idx].cell, root_enable_muxes.at(mux_idx) ? " (pure)" : "");
|
||||
root_mux_rerun.erase(mux_idx);
|
||||
eval_root_mux(mux_idx);
|
||||
eval_root_mux(shared_knowledge, mux_idx);
|
||||
if (glob_evals_left == 0) {
|
||||
log(" Giving up (too many iterations)\n");
|
||||
return;
|
||||
|
|
@ -242,10 +265,10 @@ struct OptMuxtreeWorker
|
|||
|
||||
while (!root_mux_rerun.empty()) {
|
||||
int mux_idx = *root_mux_rerun.begin();
|
||||
log_debug(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
|
||||
log_debug(" Root of a mux tree: %s (rerun as non-pure)\n", mux2info[mux_idx].cell);
|
||||
log_assert(root_enable_muxes.at(mux_idx));
|
||||
root_mux_rerun.erase(mux_idx);
|
||||
eval_root_mux(mux_idx);
|
||||
eval_root_mux(shared_knowledge, mux_idx);
|
||||
if (glob_evals_left == 0) {
|
||||
log(" Giving up (too many iterations)\n");
|
||||
return;
|
||||
|
|
@ -336,22 +359,6 @@ struct OptMuxtreeWorker
|
|||
return results;
|
||||
}
|
||||
|
||||
struct knowledge_t
|
||||
{
|
||||
// Known inactive signals
|
||||
// The payload is a reference counter used to manage the list
|
||||
// When it is non-zero, the signal in known to be inactive
|
||||
// When it reaches zero, the map element is removed
|
||||
std::unordered_map<int, int> known_inactive;
|
||||
|
||||
// database of known active signals
|
||||
std::unordered_map<int, int> known_active;
|
||||
|
||||
// this is just used to keep track of visited muxes in order to prohibit
|
||||
// endless recursion in mux loops
|
||||
std::unordered_set<int> visited_muxes;
|
||||
};
|
||||
|
||||
static void activate_port(knowledge_t &knowledge, int port_idx, const muxinfo_t &muxinfo) {
|
||||
// First, mark all other ports inactive
|
||||
for (int i = 0; i < GetSize(muxinfo.ports); i++) {
|
||||
|
|
@ -366,11 +373,10 @@ struct OptMuxtreeWorker
|
|||
}
|
||||
|
||||
static void deactivate_port(knowledge_t &knowledge, int port_idx, const muxinfo_t &muxinfo) {
|
||||
auto unlearn = [](std::unordered_map<int, int>& knowns, int i) {
|
||||
auto it = knowns.find(i);
|
||||
if (it != knowns.end())
|
||||
if (--it->second == 0)
|
||||
knowns.erase(it);
|
||||
auto unlearn = [](std::vector<int>& knowns, int bit_idx) {
|
||||
if (knowns[bit_idx] > 0) {
|
||||
--knowns[bit_idx];
|
||||
}
|
||||
};
|
||||
|
||||
if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated)
|
||||
|
|
@ -418,9 +424,9 @@ struct OptMuxtreeWorker
|
|||
|
||||
vector<int> input_mux_queue;
|
||||
for (int m : muxinfo.ports[port_idx].input_muxes) {
|
||||
if (knowledge.visited_muxes.count(m))
|
||||
if (knowledge.visited_muxes[m])
|
||||
continue;
|
||||
knowledge.visited_muxes.insert(m);
|
||||
knowledge.visited_muxes[m] = true;
|
||||
input_mux_queue.push_back(m);
|
||||
}
|
||||
for (int m : input_mux_queue) {
|
||||
|
|
@ -433,7 +439,7 @@ struct OptMuxtreeWorker
|
|||
// Ran out of subtree depth, re-eval this input tree in the next re-run
|
||||
root_mux_rerun.insert(m);
|
||||
root_enable_muxes.at(m) = true;
|
||||
log_debug(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
|
||||
log_debug(" Removing pure flag from root mux %s.\n", mux2info[m].cell);
|
||||
} else {
|
||||
auto new_limits = limits.subtree();
|
||||
// Since our knowledge includes assumption,
|
||||
|
|
@ -453,7 +459,7 @@ struct OptMuxtreeWorker
|
|||
// Allow revisiting input muxes, since evaluating other ports should
|
||||
// revisit these input muxes with different activation assumptions
|
||||
for (int m : input_mux_queue)
|
||||
knowledge.visited_muxes.erase(m);
|
||||
knowledge.visited_muxes[m] = false;
|
||||
|
||||
// Undo our assumptions that the port is active
|
||||
deactivate_port(knowledge, port_idx, muxinfo);
|
||||
|
|
@ -475,11 +481,11 @@ struct OptMuxtreeWorker
|
|||
vector<int> bits = sig2bits(sig, false);
|
||||
for (int i = 0; i < GetSize(bits); i++) {
|
||||
if (bits[i] >= 0) {
|
||||
if (knowledge.known_inactive.count(bits[i]) > 0) {
|
||||
if (knowledge.known_inactive[bits[i]] > 0) {
|
||||
sig[i] = State::S0;
|
||||
did_something = true;
|
||||
} else
|
||||
if (knowledge.known_active.count(bits[i]) > 0) {
|
||||
if (knowledge.known_active[bits[i]] > 0) {
|
||||
sig[i] = State::S1;
|
||||
did_something = true;
|
||||
}
|
||||
|
|
@ -513,8 +519,8 @@ struct OptMuxtreeWorker
|
|||
}
|
||||
|
||||
if (did_something) {
|
||||
log(" Replacing known input bits on port %s of cell %s: %s -> %s\n", log_id(portname),
|
||||
log_id(muxinfo.cell), log_signal(muxinfo.cell->getPort(portname)), log_signal(sig));
|
||||
log(" Replacing known input bits on port %s of cell %s: %s -> %s\n", portname.unescape(),
|
||||
muxinfo.cell, log_signal(muxinfo.cell->getPort(portname)), log_signal(sig));
|
||||
muxinfo.cell->setPort(portname, sig);
|
||||
}
|
||||
}
|
||||
|
|
@ -526,7 +532,7 @@ struct OptMuxtreeWorker
|
|||
glob_evals_left--;
|
||||
|
||||
muxinfo_t &muxinfo = mux2info[mux_idx];
|
||||
log_debug("\t\teval %s (replace %d enable %d)\n", log_id(muxinfo.cell), limits.do_replace_known, limits.do_mark_ports_observable);
|
||||
log_debug("\t\teval %s (replace %d enable %d)\n", muxinfo.cell, limits.do_replace_known, limits.do_mark_ports_observable);
|
||||
|
||||
// set input ports to constants if we find known active or inactive signals
|
||||
if (limits.do_replace_known) {
|
||||
|
|
@ -552,7 +558,7 @@ struct OptMuxtreeWorker
|
|||
portinfo_t &portinfo = muxinfo.ports[port_idx];
|
||||
if (portinfo.const_deactivated)
|
||||
continue;
|
||||
if (knowledge.known_active.count(portinfo.ctrl_sig) > 0) {
|
||||
if (knowledge.known_active[portinfo.ctrl_sig] > 0) {
|
||||
eval_mux_port(knowledge, mux_idx, port_idx, limits);
|
||||
return;
|
||||
}
|
||||
|
|
@ -566,7 +572,7 @@ struct OptMuxtreeWorker
|
|||
if (portinfo.const_deactivated)
|
||||
continue;
|
||||
if (port_idx < GetSize(muxinfo.ports)-1)
|
||||
if (knowledge.known_inactive.count(portinfo.ctrl_sig) > 0)
|
||||
if (knowledge.known_inactive[portinfo.ctrl_sig] > 0)
|
||||
continue;
|
||||
eval_mux_port(knowledge, mux_idx, port_idx, limits);
|
||||
|
||||
|
|
@ -575,14 +581,14 @@ struct OptMuxtreeWorker
|
|||
}
|
||||
}
|
||||
|
||||
void eval_root_mux(int mux_idx)
|
||||
void eval_root_mux(knowledge_t &knowledge, int mux_idx)
|
||||
{
|
||||
log_assert(glob_evals_left > 0);
|
||||
knowledge_t knowledge;
|
||||
knowledge.visited_muxes.insert(mux_idx);
|
||||
knowledge.visited_muxes[mux_idx] = true;
|
||||
limits_t limits = {};
|
||||
limits.do_mark_ports_observable = root_enable_muxes.at(mux_idx);
|
||||
eval_mux(knowledge, mux_idx, limits);
|
||||
knowledge.visited_muxes[mux_idx] = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue