3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-02-14 04:41:48 +00:00

Merge remote-tracking branch 'origin/main' into icells_not_derived

This commit is contained in:
Gus Smith 2026-01-18 17:05:43 -08:00
commit 52845cd82b
1402 changed files with 116443 additions and 39709 deletions

View file

@ -1,21 +1,23 @@
OBJS += passes/techmap/flatten.o
OBJS += passes/techmap/techmap.o
OBJS += passes/techmap/simplemap.o
OBJS += passes/techmap/dfflibmap.o
OBJS += passes/techmap/maccmap.o
OBJS += passes/techmap/booth.o
OBJS += passes/techmap/libparse.o
OBJS += passes/techmap/libcache.o
ifeq ($(ENABLE_ABC),1)
OBJS += passes/techmap/abc.o
OBJS += passes/techmap/abc9.o
OBJS += passes/techmap/abc9_exe.o
OBJS += passes/techmap/abc9_ops.o
OBJS += passes/techmap/abc_new.o
ifneq ($(ABCEXTERNAL),)
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
passes/techmap/abc_new.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
endif
endif
@ -37,10 +39,12 @@ OBJS += passes/techmap/muxcover.o
OBJS += passes/techmap/aigmap.o
OBJS += passes/techmap/tribuf.o
OBJS += passes/techmap/lut2mux.o
OBJS += passes/techmap/lut2bmux.o
OBJS += passes/techmap/nlutmap.o
OBJS += passes/techmap/shregmap.o
OBJS += passes/techmap/deminout.o
OBJS += passes/techmap/insbuf.o
OBJS += passes/techmap/bufnorm.o
OBJS += passes/techmap/attrmvcp.o
OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
@ -48,6 +52,9 @@ OBJS += passes/techmap/dfflegalize.o
OBJS += passes/techmap/dffunmap.o
OBJS += passes/techmap/flowmap.o
OBJS += passes/techmap/extractinv.o
OBJS += passes/techmap/cellmatch.o
OBJS += passes/techmap/clockgate.o
OBJS += passes/techmap/constmap.o
endif
ifeq ($(DISABLE_SPAWN),0)
@ -56,5 +63,5 @@ EXTRA_OBJS += passes/techmap/filterlib.o
$(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o
$(Q) mkdir -p $(dir $@)
$(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(LIBS)
$(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(EXE_LIBS) $(LIBS)
endif

File diff suppressed because it is too large Load diff

View file

@ -38,8 +38,8 @@ struct Abc9Pass : public ScriptPass
Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { }
void on_register() override
{
RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs";
RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -a -v; &mfs";
RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {C} {W} {D} {R} -v; &mfs";
RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {C} {W} {D} {R} -a -v; &mfs";
RTLIL::constpad["abc9.script.default.fast"] = "+&if {C} {W} {D} {R} -v";
// Based on ABC's &flow
RTLIL::constpad["abc9.script.flow"] = "+&scorr; &sweep;" \
@ -106,7 +106,7 @@ struct Abc9Pass : public ScriptPass
#ifdef ABCEXTERNAL
log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
#else
log(" use the specified command instead of \"<yosys-bindir>/%syosys-abc\" to execute ABC.\n", proc_program_prefix().c_str());
log(" use the specified command instead of \"<yosys-bindir>/%syosys-abc\" to execute ABC.\n", proc_program_prefix());
#endif
log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
log("\n");
@ -119,12 +119,12 @@ struct Abc9Pass : public ScriptPass
log(" replaced with blanks before the string is passed to ABC.\n");
log("\n");
log(" if no -script parameter is given, the following scripts are used:\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)));
log("\n");
log(" -fast\n");
log(" use different default scripts that are slightly faster (at the cost\n");
log(" of output quality):\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)));
log("\n");
log(" -D <picoseconds>\n");
log(" set delay target. the string {D} in the default scripts above is\n");
@ -220,7 +220,8 @@ struct Abc9Pass : public ScriptPass
std::string arg = args[argidx];
if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
/*arg == "-S" ||*/ arg == "-lut" || arg == "-luts" ||
/*arg == "-box" ||*/ arg == "-W") &&
/*arg == "-box" ||*/ arg == "-W" || arg == "-genlib" ||
arg == "-constr" || arg == "-dont_use" || arg == "-liberty") &&
argidx+1 < args.size()) {
if (arg == "-lut" || arg == "-luts")
lut_mode = true;
@ -305,9 +306,10 @@ struct Abc9Pass : public ScriptPass
}
run("design -stash $abc9");
run("design -load $abc9_map");
run("proc");
if (help_mode) run("select =*");
else active_design->push_complete_selection();
run("wbflip");
run("techmap -wb -map %$abc9 -map +/techmap.v A:abc9_flop");
run("techmap -autoproc -wb -map %$abc9 -map +/techmap.v A:abc9_flop");
run("opt -nodffe -nosdff");
if (dff_mode || help_mode) {
if (!help_mode)
@ -330,8 +332,8 @@ struct Abc9Pass : public ScriptPass
// Rename all submod-s to _TECHMAP_REPLACE_ to inherit name + attrs
for (auto module : active_design->selected_modules()) {
active_design->selected_active_module = module->name.str();
if (module->cell(stringf("%s_$abc9_flop", module->name.c_str())))
run(stringf("rename %s_$abc9_flop _TECHMAP_REPLACE_", module->name.c_str()));
if (module->cell(stringf("%s_$abc9_flop", module->name)))
run(stringf("rename %s_$abc9_flop _TECHMAP_REPLACE_", module->name));
}
active_design->selected_active_module.clear();
}
@ -368,6 +370,8 @@ struct Abc9Pass : public ScriptPass
if (saved_designs.count("$abc9_holes") || help_mode) {
run("design -stash $abc9");
run("design -load $abc9_holes");
if (help_mode) run("select =*");
else active_design->push_complete_selection();
run("techmap -wb -map %$abc9 -map +/techmap.v");
run("opt -purge");
run("aigmap");
@ -390,7 +394,7 @@ struct Abc9Pass : public ScriptPass
}
else {
auto selected_modules = active_design->selected_modules();
active_design->selection_stack.emplace_back(false);
active_design->push_empty_selection();
for (auto mod : selected_modules) {
if (mod->processes.size() > 0) {
@ -399,8 +403,9 @@ struct Abc9Pass : public ScriptPass
}
log_push();
active_design->selection().select(mod);
active_design->select(mod);
// this check does nothing because the above line adds the whole module to the selection
if (!active_design->selected_whole_module(mod))
log_error("Can't handle partially selected module %s!\n", log_id(mod));
@ -413,10 +418,10 @@ struct Abc9Pass : public ScriptPass
tempdir_name = make_temp_dir(tempdir_name);
if (!lut_mode)
run_nocheck(stringf("abc9_ops -write_lut %s/input.lut", tempdir_name.c_str()));
run_nocheck(stringf("abc9_ops -write_lut %s/input.lut", tempdir_name));
if (box_file.empty())
run_nocheck(stringf("abc9_ops -write_box %s/input.box", tempdir_name.c_str()));
run_nocheck(stringf("write_xaiger -map %s/input.sym %s %s/input.xaig", tempdir_name.c_str(), dff_mode ? "-dff" : "", tempdir_name.c_str()));
run_nocheck(stringf("abc9_ops -write_box %s/input.box", tempdir_name));
run_nocheck(stringf("write_xaiger -map %s/input.sym %s %s/input.xaig", tempdir_name, dff_mode ? "-dff" : "", tempdir_name));
int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs");
@ -428,15 +433,15 @@ struct Abc9Pass : public ScriptPass
num_outputs);
if (num_outputs) {
std::string abc9_exe_cmd;
abc9_exe_cmd += stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str());
abc9_exe_cmd += stringf("%s -cwd %s", exe_cmd.str(), tempdir_name);
if (!lut_mode)
abc9_exe_cmd += stringf(" -lut %s/input.lut", tempdir_name.c_str());
abc9_exe_cmd += stringf(" -lut %s/input.lut", tempdir_name);
if (box_file.empty())
abc9_exe_cmd += stringf(" -box %s/input.box", tempdir_name.c_str());
abc9_exe_cmd += stringf(" -box %s/input.box", tempdir_name);
else
abc9_exe_cmd += stringf(" -box %s", box_file.c_str());
abc9_exe_cmd += stringf(" -box %s", box_file);
run_nocheck(abc9_exe_cmd);
run_nocheck(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str()));
run_nocheck(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name, tempdir_name));
run_nocheck(stringf("abc9_ops -reintegrate %s", dff_mode ? "-dff" : ""));
}
else
@ -451,7 +456,7 @@ struct Abc9Pass : public ScriptPass
log_pop();
}
active_design->selection_stack.pop_back();
active_design->pop_selection();
}
}

View file

@ -140,7 +140,7 @@ struct abc9_output_filter
return;
}
if (ch == '\n') {
log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str());
log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir));
got_cr = false, linebuf.clear();
return;
}
@ -167,21 +167,36 @@ struct abc9_output_filter
void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file,
vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
std::string wire_delay, std::string tempdir_name
)
std::vector<std::string> liberty_files, std::string wire_delay, std::string tempdir_name,
std::string constr_file, std::vector<std::string> dont_use_cells, std::vector<std::string> genlib_files)
{
std::string abc9_script;
if (!lut_costs.empty())
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name);
else if (!lut_file.empty())
abc9_script += stringf("read_lut \"%s\"; ", lut_file.c_str());
else
log_abort();
abc9_script += stringf("read_lut \"%s\"; ", lut_file);
else if (!liberty_files.empty()) {
std::string dont_use_args;
for (std::string dont_use_cell : dont_use_cells) {
dont_use_args += stringf("-X \"%s\" ", dont_use_cell);
}
bool first_lib = true;
for (std::string liberty_file : liberty_files) {
abc9_script += stringf("read_lib %s %s -w \"%s\" ; ", dont_use_args, first_lib ? "" : "-m", liberty_file);
first_lib = false;
}
if (!constr_file.empty())
abc9_script += stringf("read_constr -v \"%s\"; ", constr_file);
} else if (!genlib_files.empty()) {
for (std::string genlib_file : genlib_files) {
abc9_script += stringf("read_genlib \"%s\"; ", genlib_file);
}
}
log_assert(!box_file.empty());
abc9_script += stringf("read_box \"%s\"; ", box_file.c_str());
abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
abc9_script += stringf("read_box \"%s\"; ", box_file);
abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name);
if (!script_file.empty()) {
if (script_file[0] == '+') {
@ -193,7 +208,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
else
abc9_script += script_file[i];
} else
abc9_script += stringf("source %s", script_file.c_str());
abc9_script += stringf("source %s", script_file);
} else if (!lut_costs.empty() || !lut_file.empty()) {
abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)
: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
@ -225,14 +240,14 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos))
abc9_script = abc9_script.erase(pos, strlen("&mfs"));
else {
auto s = stringf("&write -n %s/output.aig; ", tempdir_name.c_str());
auto s = stringf("&write -n %s/output.aig; ", tempdir_name);
for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos)) {
abc9_script = abc9_script.insert(pos, s);
pos += GetSize(s) + strlen("&mfs");
}
}
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str());
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name);
if (design->scratchpad_get_bool("abc9.verify")) {
if (dff_mode)
abc9_script += "; &verify -s";
@ -255,23 +270,23 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
log_header(design, "Executing ABC9.\n");
if (!lut_costs.empty()) {
buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
buffer = stringf("%s/lutdefs.txt", tempdir_name);
f = fopen(buffer.c_str(), "wt");
if (f == NULL)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
for (int i = 0; i < GetSize(lut_costs); i++)
fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
fclose(f);
}
buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", exe_file, tempdir_name);
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir));
#ifndef YOSYS_LINK_ABC
abc9_output_filter filt(tempdir_name, show_tempdir);
int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1));
#else
string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name.c_str());
string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name);
FILE *temp_stdouterr_w = fopen(temp_stdouterr_name.c_str(), "w");
if (temp_stdouterr_w == NULL)
log_error("ABC: cannot open a temporary file for output redirection");
@ -291,7 +306,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
fclose(temp_stdouterr_w);
// These needs to be mutable, supposedly due to getopt
char *abc9_argv[5];
string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
string tmp_script_name = stringf("%s/abc.script", tempdir_name);
abc9_argv[0] = strdup(exe_file.c_str());
abc9_argv[1] = strdup("-s");
abc9_argv[2] = strdup("-f");
@ -315,10 +330,10 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
temp_stdouterr_r.close();
#endif
if (ret != 0) {
if (check_file_exists(stringf("%s/output.aig", tempdir_name.c_str())))
log_warning("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
if (check_file_exists(stringf("%s/output.aig", tempdir_name)))
log_warning("ABC: execution of command \"%s\" failed: return code %d.\n", buffer, ret);
else
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer, ret);
}
}
@ -339,7 +354,7 @@ struct Abc9ExePass : public Pass {
#ifdef ABCEXTERNAL
log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
#else
log(" use the specified command instead of \"<yosys-bindir>/%syosys-abc\" to execute ABC.\n", proc_program_prefix().c_str());
log(" use the specified command instead of \"<yosys-bindir>/%syosys-abc\" to execute ABC.\n", proc_program_prefix());
#endif
log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
log("\n");
@ -352,12 +367,37 @@ struct Abc9ExePass : public Pass {
log(" replaced with blanks before the string is passed to ABC.\n");
log("\n");
log(" if no -script parameter is given, the following scripts are used:\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)));
log("\n");
log(" -fast\n");
log(" use different default scripts that are slightly faster (at the cost\n");
log(" of output quality):\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)));
log("\n");
log(" -constr <file>\n");
log(" pass this file with timing constraints to ABC.\n");
log(" use with -liberty.\n");
log("\n");
log(" a constr file contains two lines:\n");
log(" set_driving_cell <cell_name>\n");
log(" set_load <floating_point_number>\n");
log("\n");
log(" the set_driving_cell statement defines which cell type is assumed to\n");
log(" drive the primary inputs and the set_load statement sets the load in\n");
log(" femtofarads for each primary output.\n");
log("\n");
log(" -liberty <file>\n");
log(" read the given Liberty file as a description of the target cell library.\n");
log(" this option can be used multiple times.\n");
log("\n");
log(" -genlib <file>\n");
log(" read the given genlib file as a description of the target cell library.\n");
log(" this option can be used multiple times.\n");
log("\n");
log(" -dont_use <cell_name>\n");
log(" avoid usage of the technology cell <cell_name> when mapping the design.\n");
log(" this option can be used multiple times. only supported with Liberty\n");
log(" cell libraries.\n");
log("\n");
log(" -D <picoseconds>\n");
log(" set delay target. the string {D} in the default scripts above is\n");
@ -411,7 +451,8 @@ struct Abc9ExePass : public Pass {
log_header(design, "Executing ABC9_EXE pass (technology mapping using ABC9).\n");
std::string exe_file = yosys_abc_executable;
std::string script_file, clk_str, box_file, lut_file;
std::string script_file, clk_str, box_file, lut_file, constr_file;
std::vector<std::string> liberty_files, genlib_files, dont_use_cells;
std::string delay_target, lutin_shared = "-S 1", wire_delay;
std::string tempdir_name;
bool fast_mode = false, dff_mode = false;
@ -499,6 +540,24 @@ struct Abc9ExePass : public Pass {
tempdir_name = args[++argidx];
continue;
}
if (arg == "-liberty" && argidx+1 < args.size()) {
rewrite_filename(args[argidx+1]);
liberty_files.push_back(args[++argidx]);
continue;
}
if (arg == "-genlib" && argidx+1 < args.size()) {
rewrite_filename(args[argidx+1]);
genlib_files.push_back(args[++argidx]);
continue;
}
if (arg == "-dont_use" && argidx+1 < args.size()) {
dont_use_cells.push_back(args[++argidx]);
continue;
}
if (arg == "-constr" && argidx+1 < args.size()) {
constr_file = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, design);
@ -559,10 +618,13 @@ struct Abc9ExePass : public Pass {
if (tempdir_name.empty())
log_cmd_error("abc9_exe '-cwd' option is mandatory.\n");
if (!genlib_files.empty() && !dont_use_cells.empty())
log_cmd_error("abc9_exe '-genlib' is incompatible with '-dont_use'.\n");
abc9_module(design, script_file, exe_file, lut_costs, dff_mode,
delay_target, lutin_shared, fast_mode, show_tempdir,
box_file, lut_file, wire_delay, tempdir_name);
box_file, lut_file, liberty_files, wire_delay, tempdir_name,
constr_file, dont_use_cells, genlib_files);
}
} Abc9ExePass;

View file

@ -123,7 +123,7 @@ void check(RTLIL::Design *design, bool dff_mode)
log_error("Module '%s' with (* abc9_flop *) is a blackbox.\n", log_id(derived_type));
if (derived_module->has_processes())
Pass::call_on_module(design, derived_module, "proc");
Pass::call_on_module(design, derived_module, "proc -noopt");
bool found = false;
for (auto derived_cell : derived_module->cells()) {
@ -204,7 +204,7 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
if (!unmap_design->module(derived_type)) {
if (derived_module->has_processes())
Pass::call_on_module(design, derived_module, "proc");
Pass::call_on_module(design, derived_module, "proc -noopt");
if (derived_module->get_bool_attribute(ID::abc9_flop)) {
for (auto derived_cell : derived_module->cells())
@ -224,7 +224,7 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
}
else if (derived_module->get_bool_attribute(ID::abc9_box)) {
for (auto derived_cell : derived_module->cells())
if (derived_cell->is_mem_cell() || RTLIL::builtin_ff_cell_types().count(derived_cell->type)) {
if (derived_cell->is_mem_cell() || derived_cell->is_builtin_ff()) {
derived_module->set_bool_attribute(ID::abc9_box, false);
derived_module->set_bool_attribute(ID::abc9_bypass);
break;
@ -454,7 +454,7 @@ void prep_bypass(RTLIL::Design *design)
void prep_dff(RTLIL::Design *design)
{
auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection(false)));
auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection::EmptySelection(design)));
auto &modules_sel = r.first->second;
for (auto module : design->selected_modules())
@ -787,7 +787,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
for (auto cell_name : it) {
auto cell = module->cell(cell_name);
log_assert(cell);
log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute());
}
}
}
@ -834,7 +834,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
holes_cell = holes_module->addCell(NEW_ID, cell->type);
if (box_module->has_processes())
Pass::call_on_module(design, box_module, "proc");
Pass::call_on_module(design, box_module, "proc -noopt");
int box_inputs = 0;
for (auto port_name : box_ports.at(cell->type)) {
@ -856,7 +856,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
}
}
else if (w->port_output)
conn = holes_module->addWire(stringf("%s.%s", cell->type.c_str(), log_id(port_name)), GetSize(w));
conn = holes_module->addWire(stringf("%s.%s", cell->type, log_id(port_name)), GetSize(w));
}
}
else // box_module is a blackbox
@ -868,7 +868,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
log_assert(w);
if (!w->port_output)
continue;
Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w));
Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name, log_id(port_name)), GetSize(w));
holes_wire->port_output = true;
holes_wire->port_id = port_id++;
holes_module->ports.push_back(holes_wire->name);
@ -969,13 +969,10 @@ void prep_box(RTLIL::Design *design)
if (it == module->attributes.end())
continue;
bool box = it->second.as_bool();
module->attributes.erase(it);
if (!box)
continue;
auto r = module->attributes.insert(ID::abc9_box_id);
if (!r.second)
continue;
r.first->second = abc9_box_id++;
if (module->get_bool_attribute(ID::abc9_flop)) {
@ -1078,7 +1075,8 @@ void prep_box(RTLIL::Design *design)
}
ss << log_id(module) << " " << module->attributes.at(ID::abc9_box_id).as_int();
ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0");
bool has_model = module->get_bool_attribute(ID::whitebox) || !module->get_bool_attribute(ID::blackbox);
ss << " " << (has_model ? "1" : "0");
ss << " " << GetSize(inputs) << " " << GetSize(outputs) << std::endl;
bool first = true;
@ -1096,8 +1094,9 @@ void prep_box(RTLIL::Design *design)
ss << std::endl;
auto &t = timing.setup_module(module);
if (t.comb.empty())
if (t.comb.empty() && !outputs.empty() && !inputs.empty()) {
log_error("Module '%s' with (* abc9_box *) has no timing (and thus no connectivity) information.\n", log_id(module));
}
for (const auto &o : outputs) {
first = true;
@ -1144,7 +1143,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
map_autoidx = autoidx++;
RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str()));
RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name));
if (mapped_mod == NULL)
log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module));
@ -1217,7 +1216,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
auto Qi = initmap(Q);
auto it = Qi.wire->attributes.find(ID::init);
if (it != Qi.wire->attributes.end())
it->second[Qi.offset] = State::Sx;
it->second.set(Qi.offset, State::Sx);
}
else if (cell->type.in(ID($_AND_), ID($_NOT_)))
module->remove(cell);
@ -1272,16 +1271,16 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
// (TODO: Optimise by not cloning unless will increase depth)
RTLIL::IdString driver_name;
if (GetSize(a_bit.wire) == 1)
driver_name = stringf("$lut%s", a_bit.wire->name.c_str());
driver_name = stringf("$lut%s", a_bit.wire->name);
else
driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset);
driver_name = stringf("$lut%s[%d]", a_bit.wire->name, a_bit.offset);
driver_lut = mapped_mod->cell(driver_name);
}
if (!driver_lut) {
// If a driver couldn't be found (could be from PI or box CI)
// then implement using a LUT
RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())),
RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name)),
RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset),
RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset),
RTLIL::Const::from_string("01"));
@ -1429,13 +1428,13 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
// Copy connections (and rename) from mapped_mod to module
for (auto conn : mapped_mod->connections()) {
if (!conn.first.is_fully_const()) {
auto chunks = conn.first.chunks();
std::vector<RTLIL::SigChunk> chunks = conn.first.chunks();
for (auto &c : chunks)
c.wire = module->wires_.at(remap_name(c.wire->name));
conn.first = std::move(chunks);
}
if (!conn.second.is_fully_const()) {
auto chunks = conn.second.chunks();
std::vector<RTLIL::SigChunk> chunks = conn.second.chunks();
for (auto &c : chunks)
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));
@ -1445,7 +1444,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
}
for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
log("ABC RESULTS: %15s cells: %8d\n", it.first, it.second);
int in_wires = 0, out_wires = 0;
// Stitch in mapped_mod's inputs/outputs into module
@ -1527,8 +1526,11 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
log_assert(index < GetSize(A));
int i = 0;
while (i < GetSize(mask)) {
for (int j = 0; j < (1 << index); j++)
std::swap(mask[i+j], mask[i+j+(1 << index)]);
for (int j = 0; j < (1 << index); j++) {
State bit = mask[i+j];
mask.set(i+j, mask[i+j+(1 << index)]);
mask.set(i+j+(1 << index), bit);
}
i += 1 << (index+1);
}
A[index] = y_bit;
@ -1543,7 +1545,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
// and get cleaned away
clone_lut:
driver_mask = driver_lut->getParam(ID::LUT);
for (auto &b : driver_mask.bits) {
for (auto b : driver_mask) {
if (b == RTLIL::State::S0) b = RTLIL::State::S1;
else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
}
@ -1564,6 +1566,69 @@ clone_lut:
design->remove(mapped_mod);
}
static void replace_zbufs(Design *design)
{
design->bufNormalize(true);
std::vector<Cell *> zbufs;
for (auto mod : design->modules()) {
zbufs.clear();
for (auto cell : mod->cells()) {
if (cell->type != ID($buf))
continue;
auto &sig = cell->getPort(ID::A);
for (int i = 0; i < GetSize(sig); ++i) {
if (sig[i] == State::Sz) {
zbufs.push_back(cell);
break;
}
}
}
for (auto cell : zbufs) {
auto sig = cell->getPort(ID::A);
for (int i = 0; i < GetSize(sig); ++i) {
if (sig[i] == State::Sz) {
Wire *w = mod->addWire(NEW_ID);
Cell *ud = mod->addCell(NEW_ID, ID($tribuf));
ud->set_bool_attribute(ID(aiger2_zbuf));
ud->setParam(ID::WIDTH, 1);
ud->setPort(ID::Y, w);
ud->setPort(ID::EN, State::S0);
ud->setPort(ID::A, State::S0);
sig[i] = w;
}
}
cell->setPort(ID::A, sig);
}
mod->bufNormalize();
}
design->bufNormalize(false);
}
static void restore_zbufs(Design *design)
{
std::vector<Cell *> to_remove;
for (auto mod : design->modules()) {
to_remove.clear();
for (auto cell : mod->cells())
if (cell->type == ID($tribuf) && cell->has_attribute(ID(aiger2_zbuf)))
to_remove.push_back(cell);
for (auto cell : to_remove) {
SigSpec sig_y = cell->getPort(ID::Y);
mod->addBuf(NEW_ID, Const(State::Sz, GetSize(sig_y)), sig_y);
mod->remove(cell);
}
}
}
struct Abc9OpsPass : public Pass {
Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
void help() override
@ -1666,6 +1731,8 @@ struct Abc9OpsPass : public Pass {
bool prep_lut_mode = false;
bool prep_box_mode = false;
bool reintegrate_mode = false;
bool replace_zbufs_mode = false;
bool restore_zbufs_mode = false;
bool dff_mode = false;
std::string write_lut_dst;
int maxlut = 0;
@ -1752,16 +1819,30 @@ struct Abc9OpsPass : public Pass {
dff_mode = true;
continue;
}
if (arg == "-replace_zbufs") {
replace_zbufs_mode = true;
valid = true;
continue;
}
if (arg == "-restore_zbufs") {
restore_zbufs_mode = true;
valid = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (!valid)
log_cmd_error("At least one of -check, -break_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate must be specified.\n");
log_cmd_error("At least one of -check, -break_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate, -{replace,restore}_zbufs must be specified.\n");
if (dff_mode && !check_mode && !prep_hier_mode && !prep_delays_mode && !prep_xaiger_mode && !reintegrate_mode)
log_cmd_error("'-dff' option is only relevant for -prep_{hier,delay,xaiger} or -reintegrate.\n");
if (replace_zbufs_mode)
replace_zbufs(design);
if (restore_zbufs_mode)
restore_zbufs(design);
if (check_mode)
check(design, dff_mode);
if (prep_hier_mode)

210
passes/techmap/abc_new.cc Normal file
View file

@ -0,0 +1,210 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Martin Povišer <povik@cutebit.org>
*
* 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/rtlil.h"
#include "kernel/utils.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
std::vector<Module*> order_modules(Design *design, std::vector<Module *> modules)
{
std::set<Module *> modules_set(modules.begin(), modules.end());
TopoSort<Module*> sort;
for (auto m : modules) {
sort.node(m);
for (auto cell : m->cells()) {
Module *submodule = design->module(cell->type);
if (modules_set.count(submodule))
sort.edge(submodule, m);
}
}
bool is_sorted = sort.sort();
log_assert(is_sorted);
return sort.sorted;
}
struct AbcNewPass : public ScriptPass {
AbcNewPass() : ScriptPass("abc_new", "(experimental) use ABC for SC technology mapping (new)")
{
experimental();
}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" abc_new [options] [selection]\n");
log("\n");
log("This command uses the ABC tool [1] to optimize the current design and map it to\n");
log("the target standard cell library.\n");
log("\n");
log(" -run <from_label>:<to_label>\n");
log(" only run the commands between the labels (see below). an empty\n");
log(" from label is synonymous to 'begin', and empty to label is\n");
log(" synonymous to the end of the command list.\n");
log("\n");
log(" -exe <command>\n");
log(" -script <file>\n");
log(" -D <picoseconds>\n");
log(" -constr <file>\n");
log(" -dont_use <cell_name>\n");
log(" -liberty <file>\n");
log(" -genlib <file>\n");
log(" these options are passed on to the 'abc9_exe' command which invokes\n");
log(" the ABC tool on individual modules of the design. please see\n");
log(" 'help abc9_exe' for more details\n");
log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
log("\n");
help_script();
log("\n");
}
bool cleanup;
std::string abc_exe_options;
void execute(std::vector<std::string> args, RTLIL::Design *d) override
{
std::string run_from, run_to;
cleanup = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-exe" || args[argidx] == "-script" ||
args[argidx] == "-D" ||
args[argidx] == "-constr" || args[argidx] == "-dont_use" ||
args[argidx] == "-liberty" || args[argidx] == "-genlib") {
abc_exe_options += " " + args[argidx] + " " + args[argidx + 1];
argidx++;
} else if (args[argidx] == "-run" && argidx + 1 < args.size()) {
size_t pos = args[++argidx].find(':');
if (pos == std::string::npos)
break;
run_from = args[argidx].substr(0, pos);
run_to = args[argidx].substr(pos + 1);
} else if (args[argidx] == "-nocleanup") {
cleanup = false;
} else {
break;
}
}
extra_args(args, argidx, d);
log_header(d, "Executing ABC_NEW pass.\n");
log_push();
run_script(d, run_from, run_to);
log_pop();
}
void script() override
{
if (check_label("check")) {
run("abc9_ops -check");
}
if (check_label("prep_boxes")) {
if (!help_mode) {
for (auto mod : active_design->selected_whole_modules_warn()) {
if (mod->get_bool_attribute(ID::abc9_box)) {
mod->set_bool_attribute(ID::abc9_box, false);
mod->set_bool_attribute(ID(abc9_deferred_box), true);
}
}
}
run("box_derive");
run("abc9_ops -prep_box");
}
if (check_label("map")) {
std::vector<Module *> selected_modules;
if (!help_mode) {
selected_modules = order_modules(active_design,
active_design->selected_whole_modules_warn());
active_design->push_empty_selection();
}
run("abc9_ops -replace_zbufs");
if (help_mode) {
selected_modules = {nullptr};
run("foreach module in selection");
}
for (auto mod : selected_modules) {
std::string tmpdir = "<abc-temp-dir>";
std::string modname = "<module>";
std::string exe_options = "[options]";
if (!help_mode) {
tmpdir = cleanup ? (get_base_tmpdir() + "/") : "_tmp_";
tmpdir += proc_program_prefix() + "yosys-abc-XXXXXX";
tmpdir = make_temp_dir(tmpdir);
modname = mod->name.str();
exe_options = abc_exe_options;
log_header(active_design, "Mapping module '%s'.\n", log_id(mod));
log_push();
active_design->select(mod);
}
std::string script_save;
if (!help_mode && mod->has_attribute(ID(abc9_script))) {
script_save = active_design->scratchpad_get_string("abc9.script");
active_design->scratchpad_set_string("abc9.script",
mod->get_string_attribute(ID(abc9_script)));
}
run(stringf(" abc9_ops -write_box %s/input.box", tmpdir));
run(stringf(" write_xaiger2 -mapping_prep -map2 %s/input.map2 %s/input.xaig", tmpdir, tmpdir));
run(stringf(" abc9_exe %s -cwd %s -box %s/input.box", exe_options, tmpdir, tmpdir));
run(stringf(" read_xaiger2 -sc_mapping -module_name %s -map2 %s/input.map2 %s/output.aig",
modname.c_str(), tmpdir.c_str(), tmpdir.c_str()));
if (!help_mode && mod->has_attribute(ID(abc9_script))) {
if (script_save.empty())
active_design->scratchpad_unset("abc9.script");
else
active_design->scratchpad_set_string("abc9.script", script_save);
}
if (!help_mode) {
active_design->selection().selected_modules.clear();
log_pop();
if (mod->get_bool_attribute(ID(abc9_deferred_box))) {
mod->set_bool_attribute(ID(abc9_deferred_box), false);
mod->set_bool_attribute(ID::abc9_box, true);
Pass::call_on_module(active_design, mod, "portarcs -draw -write");
run("abc9_ops -prep_box");
}
}
}
run("abc9_ops -restore_zbufs");
if (!help_mode) {
active_design->pop_selection();
}
}
}
} AbcNewPass;
PRIVATE_NAMESPACE_END

View file

@ -171,8 +171,7 @@ struct AigmapPass : public Pass {
module->remove(cell);
if (select_mode) {
log_assert(!design->selection_stack.empty());
RTLIL::Selection& sel = design->selection_stack.back();
RTLIL::Selection& sel = design->selection();
sel.selected_members[module->name] = std::move(new_sel);
}

View file

@ -40,34 +40,51 @@ struct AlumaccWorker
{
std::vector<RTLIL::Cell*> cells;
RTLIL::SigSpec a, b, c, y;
std::vector<tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp;
std::vector<tuple<bool, bool, bool, bool, bool, RTLIL::SigSpec>> cmp;
bool is_signed, invert_b;
RTLIL::Cell *alu_cell;
RTLIL::SigSpec cached_lt, cached_gt, cached_eq, cached_ne;
RTLIL::SigSpec cached_lt, cached_slt, cached_gt, cached_sgt, cached_eq, cached_ne;
RTLIL::SigSpec cached_cf, cached_of, cached_sf;
RTLIL::SigSpec get_lt() {
if (GetSize(cached_lt) == 0) {
if (is_signed) {
RTLIL::SigSpec get_lt(bool is_signed) {
if (is_signed) {
if (GetSize(cached_slt) == 0) {
get_of();
get_sf();
cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
cached_slt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
}
else
return cached_slt;
} else {
if (GetSize(cached_lt) == 0) {
cached_lt = get_cf();
}
return cached_lt;
}
return cached_lt;
}
RTLIL::SigSpec get_gt() {
if (GetSize(cached_gt) == 0) {
get_lt();
get_eq();
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
RTLIL::SigSpec get_gt(bool is_signed) {
if (is_signed) {
if (GetSize(cached_sgt) == 0) {
get_lt(is_signed);
get_eq();
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_slt, cached_eq);
cached_sgt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
}
return cached_sgt;
} else {
if (GetSize(cached_gt) == 0) {
get_lt(is_signed);
get_eq();
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
}
return cached_gt;
}
return cached_gt;
}
RTLIL::SigSpec get_eq() {
@ -111,7 +128,7 @@ struct AlumaccWorker
dict<RTLIL::SigBit, int> bit_users;
dict<RTLIL::SigSpec, maccnode_t*> sig_macc;
dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu;
dict<RTLIL::SigSig, pool<alunode_t*>> sig_alu;
int macc_counter, alu_counter;
AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
@ -142,7 +159,7 @@ struct AlumaccWorker
log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type));
maccnode_t *n = new maccnode_t;
Macc::port_t new_port;
Macc::term_t new_term;
n->cell = cell;
n->y = sigmap(cell->getPort(ID::Y));
@ -153,32 +170,32 @@ struct AlumaccWorker
if (cell->type.in(ID($pos), ID($neg)))
{
new_port.in_a = sigmap(cell->getPort(ID::A));
new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool();
new_port.do_subtract = cell->type == ID($neg);
n->macc.ports.push_back(new_port);
new_term.in_a = sigmap(cell->getPort(ID::A));
new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool();
new_term.do_subtract = cell->type == ID($neg);
n->macc.terms.push_back(new_term);
}
if (cell->type.in(ID($add), ID($sub)))
{
new_port.in_a = sigmap(cell->getPort(ID::A));
new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool();
new_port.do_subtract = false;
n->macc.ports.push_back(new_port);
new_term.in_a = sigmap(cell->getPort(ID::A));
new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool();
new_term.do_subtract = false;
n->macc.terms.push_back(new_term);
new_port.in_a = sigmap(cell->getPort(ID::B));
new_port.is_signed = cell->getParam(ID::B_SIGNED).as_bool();
new_port.do_subtract = cell->type == ID($sub);
n->macc.ports.push_back(new_port);
new_term.in_a = sigmap(cell->getPort(ID::B));
new_term.is_signed = cell->getParam(ID::B_SIGNED).as_bool();
new_term.do_subtract = cell->type == ID($sub);
n->macc.terms.push_back(new_term);
}
if (cell->type.in(ID($mul)))
{
new_port.in_a = sigmap(cell->getPort(ID::A));
new_port.in_b = sigmap(cell->getPort(ID::B));
new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool();
new_port.do_subtract = false;
n->macc.ports.push_back(new_port);
new_term.in_a = sigmap(cell->getPort(ID::A));
new_term.in_b = sigmap(cell->getPort(ID::B));
new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool();
new_term.do_subtract = false;
n->macc.terms.push_back(new_term);
}
log_assert(sig_macc.count(n->y) == 0);
@ -190,7 +207,7 @@ struct AlumaccWorker
{
std::vector<int> port_sizes;
for (auto &port : macc.ports) {
for (auto &port : macc.terms) {
if (port.is_signed != is_signed)
return true;
if (!port.is_signed && port.do_subtract)
@ -226,7 +243,7 @@ struct AlumaccWorker
{
while (1)
{
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc)
{
@ -235,9 +252,9 @@ struct AlumaccWorker
if (delete_nodes.count(n))
continue;
for (int i = 0; i < GetSize(n->macc.ports); i++)
for (int i = 0; i < GetSize(n->macc.terms); i++)
{
auto &port = n->macc.ports[i];
auto &port = n->macc.terms[i];
if (GetSize(port.in_b) > 0 || sig_macc.count(port.in_a) == 0)
continue;
@ -253,13 +270,13 @@ struct AlumaccWorker
log(" merging $macc model for %s into %s.\n", log_id(other_n->cell), log_id(n->cell));
bool do_subtract = port.do_subtract;
for (int j = 0; j < GetSize(other_n->macc.ports); j++) {
for (int j = 0; j < GetSize(other_n->macc.terms); j++) {
if (do_subtract)
other_n->macc.ports[j].do_subtract = !other_n->macc.ports[j].do_subtract;
other_n->macc.terms[j].do_subtract = !other_n->macc.terms[j].do_subtract;
if (j == 0)
n->macc.ports[i--] = other_n->macc.ports[j];
n->macc.terms[i--] = other_n->macc.terms[j];
else
n->macc.ports.push_back(other_n->macc.ports[j]);
n->macc.terms.push_back(other_n->macc.terms[j]);
}
delete_nodes.insert(other_n);
@ -278,17 +295,17 @@ struct AlumaccWorker
void macc_to_alu()
{
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc)
{
auto n = it.second;
RTLIL::SigSpec A, B, C = n->macc.bit_ports;
RTLIL::SigSpec A, B, C;
bool a_signed = false, b_signed = false;
bool subtract_b = false;
alunode_t *alunode;
for (auto &port : n->macc.ports)
for (auto &port : n->macc.terms)
if (GetSize(port.in_b) > 0) {
goto next_macc;
} else if (GetSize(port.in_a) == 1 && !port.is_signed && !port.do_subtract) {
@ -405,19 +422,24 @@ struct AlumaccWorker
RTLIL::SigSpec B = sigmap(cell->getPort(ID::B));
RTLIL::SigSpec Y = sigmap(cell->getPort(ID::Y));
if (B < A && GetSize(B)) {
cmp_less = !cmp_less;
std::swap(A, B);
}
alunode_t *n = nullptr;
for (auto node : sig_alu[RTLIL::SigSig(A, B)])
if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
if (node->invert_b && node->c == State::S1) {
n = node;
break;
}
if (n == nullptr) {
for (auto node : sig_alu[RTLIL::SigSig(B, A)])
if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
n = node;
cmp_less = !cmp_less;
std::swap(A, B);
break;
}
}
if (n == nullptr) {
n = new alunode_t;
n->a = A;
@ -433,7 +455,7 @@ struct AlumaccWorker
}
n->cells.push_back(cell);
n->cmp.push_back(std::make_tuple(cmp_less, !cmp_less, cmp_equal, false, Y));
n->cmp.push_back(std::make_tuple(cmp_less, !cmp_less, cmp_equal, false, is_signed, Y));
}
for (auto cell : eq_cells)
@ -445,21 +467,26 @@ struct AlumaccWorker
RTLIL::SigSpec B = sigmap(cell->getPort(ID::B));
RTLIL::SigSpec Y = sigmap(cell->getPort(ID::Y));
if (B < A && GetSize(B))
std::swap(A, B);
alunode_t *n = nullptr;
for (auto node : sig_alu[RTLIL::SigSig(A, B)])
if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
if (node->invert_b && node->c == State::S1) {
n = node;
break;
}
if (n == nullptr) {
for (auto node : sig_alu[RTLIL::SigSig(B, A)])
if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
n = node;
break;
}
}
if (n != nullptr) {
log(" creating $alu model for %s (%s): merged with %s.\n", log_id(cell), log_id(cell->type), log_id(n->cells.front()));
n->cells.push_back(cell);
n->cmp.push_back(std::make_tuple(false, false, cmp_equal, !cmp_equal, Y));
n->cmp.push_back(std::make_tuple(false, false, cmp_equal, !cmp_equal, false, Y));
}
}
}
@ -508,11 +535,12 @@ struct AlumaccWorker
bool cmp_gt = std::get<1>(it);
bool cmp_eq = std::get<2>(it);
bool cmp_ne = std::get<3>(it);
RTLIL::SigSpec cmp_y = std::get<4>(it);
bool is_signed = std::get<4>(it);
RTLIL::SigSpec cmp_y = std::get<5>(it);
RTLIL::SigSpec sig;
if (cmp_lt) sig.append(n->get_lt());
if (cmp_gt) sig.append(n->get_gt());
if (cmp_lt) sig.append(n->get_lt(is_signed));
if (cmp_gt) sig.append(n->get_gt(is_signed));
if (cmp_eq) sig.append(n->get_eq());
if (cmp_ne) sig.append(n->get_ne());

View file

@ -130,14 +130,14 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio
goto delete_this_attr;
if (new_attr != attr)
log("Changed attribute on %s: %s=%s -> %s=%s\n", objname.c_str(),
log("Changed attribute on %s: %s=%s -> %s=%s\n", objname,
log_id(attr.first), log_const(attr.second), log_id(new_attr.first), log_const(new_attr.second));
new_attributes[new_attr.first] = new_attr.second;
if (0)
delete_this_attr:
log("Removed attribute on %s: %s=%s\n", objname.c_str(), log_id(attr.first), log_const(attr.second));
log("Removed attribute on %s: %s=%s\n", objname, log_id(attr.first), log_const(attr.second));
}
attributes.swap(new_attributes);
@ -263,33 +263,27 @@ struct AttrmapPass : public Pass {
if (modattr_mode)
{
for (auto module : design->selected_whole_modules())
for (auto module : design->all_selected_whole_modules())
attrmap_apply(stringf("%s", log_id(module)), actions, module->attributes);
}
else
{
for (auto module : design->selected_modules())
for (auto module : design->all_selected_modules())
{
for (auto wire : module->selected_wires())
attrmap_apply(stringf("%s.%s", log_id(module), log_id(wire)), actions, wire->attributes);
for (auto memb : module->selected_members())
attrmap_apply(stringf("%s.%s", log_id(module), log_id(memb)), actions, memb->attributes);
for (auto cell : module->selected_cells())
attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes);
for (auto proc : module->processes)
// attrmap already applied to process itself during above loop, but not its children
for (auto proc : module->selected_processes())
{
if (!design->selected(module, proc.second))
continue;
attrmap_apply(stringf("%s.%s", log_id(module), log_id(proc.first)), actions, proc.second->attributes);
std::vector<RTLIL::CaseRule*> all_cases = {&proc.second->root_case};
std::vector<RTLIL::CaseRule*> all_cases = {&proc->root_case};
while (!all_cases.empty()) {
RTLIL::CaseRule *cs = all_cases.back();
all_cases.pop_back();
attrmap_apply(stringf("%s.%s (case)", log_id(module), log_id(proc.first)), actions, cs->attributes);
attrmap_apply(stringf("%s.%s (case)", log_id(module), log_id(proc)), actions, cs->attributes);
for (auto &sw : cs->switches) {
attrmap_apply(stringf("%s.%s (switch)", log_id(module), log_id(proc.first)), actions, sw->attributes);
attrmap_apply(stringf("%s.%s (switch)", log_id(module), log_id(proc)), actions, sw->attributes);
all_cases.insert(all_cases.end(), sw->cases.begin(), sw->cases.end());
}
}

View file

@ -57,6 +57,7 @@ synth -top my_design -booth
#include "kernel/sigtools.h"
#include "kernel/yosys.h"
#include "kernel/macc.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -200,19 +201,40 @@ struct BoothPassWorker {
log_assert(sig_a.size() == sig_y.size());
for (int i = 0; i < sig_a.size(); i++)
mod->addFa(stringf("%s[%d]", name.c_str(), i), sig_a[i], sig_b[i],
mod->addFa(stringf("%s[%d]", name, i), sig_a[i], sig_b[i],
sig_c[i], sig_x[i], sig_y[i], src);
}
void run()
{
for (auto cell : module->selected_cells()) {
if (cell->type != ID($mul))
continue;
SigSpec A, B, Y;
bool is_signed;
if (cell->type == ID($mul)) {
A = cell->getPort(ID::A);
B = cell->getPort(ID::B);
Y = cell->getPort(ID::Y);
log_assert(cell->getParam(ID::A_SIGNED).as_bool() == cell->getParam(ID::B_SIGNED).as_bool());
is_signed = cell->getParam(ID::A_SIGNED).as_bool();
} else if (cell->type.in(ID($macc), ID($macc_v2))) {
Macc macc;
macc.from_cell(cell);
if (!macc.is_simple_product()) {
log_debug("Not mapping cell %s: not a simple macc cell\n", log_id(cell));
continue;
}
A = macc.terms[0].in_a;
B = macc.terms[0].in_b;
is_signed = macc.terms[0].is_signed;
Y = cell->getPort(ID::Y);
} else {
continue;
}
SigSpec A = cell->getPort(ID::A);
SigSpec B = cell->getPort(ID::B);
SigSpec Y = cell->getPort(ID::Y);
int x_sz = GetSize(A), y_sz = GetSize(B), z_sz = GetSize(Y);
if (x_sz < 4 || y_sz < 4 || z_sz < 8) {
@ -221,9 +243,6 @@ struct BoothPassWorker {
continue;
}
log_assert(cell->getParam(ID::A_SIGNED).as_bool() == cell->getParam(ID::B_SIGNED).as_bool());
bool is_signed = cell->getParam(ID::A_SIGNED).as_bool();
log("Mapping cell %s to %s Booth multiplier\n", log_id(cell), is_signed ? "signed" : "unsigned");
// To simplify the generator size the arguments

524
passes/techmap/bufnorm.cc Normal file
View file

@ -0,0 +1,524 @@
/*
* 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/yosys.h"
#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct BufnormPass : public Pass {
BufnormPass() : Pass("bufnorm", "(experimental) convert design into buffered-normalized form") {
experimental();
}
void help() override
{
log("\n");
log(" bufnorm [options] [selection]\n");
log("\n");
log("Insert buffer cells into the design as needed, to make sure that each wire\n");
log("has exactly one driving cell port, and aliasing wires are buffered using\n");
log("buffer cells, than can be chained in a canonical order.\n");
log("\n");
log("Running 'bufnorm' on the whole design enters 'buffered-normalized mode'.\n");
log("\n");
log(" -buf\n");
log(" Create $buf cells for all buffers. The default is to use $_BUF_ cells\n");
log(" for sigle-bit buffers and $buf cells only for multi-bit buffers.\n");
log("\n");
log(" -chain\n");
log(" Chain all alias wires. By default only wires with positive-valued\n");
log(" 'chain' or 'keep' attribute on them are chained.\n");
log("\n");
log(" -output\n");
log(" Enable chaining of ouput ports wires.\n");
log("\n");
log(" -public\n");
log(" Enable chaining of wires wth public names.\n");
log("\n");
log(" -nochain\n");
log(" Disable chaining of wires with 'chain' attribute.\n");
log("\n");
log(" -nokeep\n");
log(" Disable chaining of wires with 'keep' attribute.\n");
log("\n");
log(" -flat\n");
log(" Alias for -nokeep and -nochain.\n");
log("\n");
log(" -nosticky\n");
log(" Disable 'sticky' behavior of output ports already driving whole\n");
log(" wires, and always enforce canonical sort order instead.\n");
log("\n");
log(" -alphasort\n");
log(" Strictly use alphanumeric sort for chain-order. (Default is\n");
log(" to chain 'keep' wires first, then ports in declaration order,\n");
log(" and then the other wires in alphanumeric sort order.)\n");
log("\n");
// log(" -noinit\n");
// log(" Do not move 'init' attributes to the wires on FF output ports.\n");
// log("\n");
log("Run 'bufnorm' with -pos, -bits, or -conn on the whole design to remove all\n");
log("$buf buffer cells and exit 'buffered-normalized mode' again.\n");
log("\n");
log(" -pos\n");
log(" Create (multi- and single-bit) $pos cells instead $buf and $_BUF_.\n");
log("\n");
log(" -bits\n");
log(" Create arrays of $_BUF_ cells instead of multi-bit $buf cells.\n");
log("\n");
log(" -conn\n");
log(" Create 'direct connections' instead of buffer cells.\n");
log("\n");
log(" -nomode\n");
log(" Do not automatically enter or leave 'buffered-normalized mode'.\n");
log("\n");
log("The 'bufnorm' command can also be used to just switch in and out of\n");
log("'buffered-normalized mode' and run the low-level re-normalizer.\n");
log("\n");
log(" -update\n");
log(" Enter 'buffered-normalized mode' and (re-)normalize.\n");
log("\n");
log(" -reset\n");
log(" Leave 'buffered-normalized mode' without changing the netlist.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool buf_mode = false;
bool chain_mode = false;
bool output_mode = false;
bool public_mode = false;
bool nochain_mode = false;
bool nokeep_mode = false;
bool nosticky_mode = false;
bool alphasort_mode = false;
// bool noinit_mode = false; // FIXME: Actually move init attributes
bool nomode_mode = false;
bool pos_mode = false;
bool bits_mode = false;
bool conn_mode = false;
bool update_mode = false;
bool reset_mode = false;
bool got_non_update_reset_opt = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
std::string arg = args[argidx];
if (arg == "-buf") {
buf_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-chain") {
chain_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-output") {
output_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-public") {
public_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-nochain") {
nochain_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-nokeep") {
nokeep_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-flat") {
nochain_mode = true;
nokeep_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-nosticky") {
nosticky_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-alphasort") {
alphasort_mode = true;
got_non_update_reset_opt = true;
continue;
}
// if (arg == "-noinit") {
// noinit_mode = true;
// got_non_update_reset_opt = true;
// continue;
// }
if (arg == "-pos") {
pos_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-bits") {
bits_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-conn") {
conn_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-nomode") {
nomode_mode = true;
got_non_update_reset_opt = true;
continue;
}
if (arg == "-update") {
update_mode = true;
continue;
}
if (arg == "-reset") {
reset_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (buf_mode && pos_mode)
log_cmd_error("Options -buf and -pos are exclusive.\n");
if (buf_mode && conn_mode)
log_cmd_error("Options -buf and -conn are exclusive.\n");
if (pos_mode && conn_mode)
log_cmd_error("Options -pos and -conn are exclusive.\n");
if (update_mode && reset_mode)
log_cmd_error("Options -update and -reset are exclusive.\n");
if (update_mode && got_non_update_reset_opt)
log_cmd_error("Option -update can't be mixed with other options.\n");
if (reset_mode && got_non_update_reset_opt)
log_cmd_error("Option -reset can't be mixed with other options.\n");
if (update_mode) {
design->bufNormalize();
return;
}
if (reset_mode) {
design->bufNormalize(false);
return;
}
log_header(design, "Executing BUFNORM pass (convert to buffer-normalized form).\n");
int count_removed_buffers = 0;
int count_updated_buffers = 0;
int count_kept_buffers = 0;
int count_created_buffers = 0;
int count_updated_cellports = 0;
if (!nomode_mode && (pos_mode || bits_mode || conn_mode)) {
if (design->selection().full_selection)
design->bufNormalize(false);
}
for (auto module : design->selected_modules())
{
log("Buffer-normalizing module %s.\n", log_id(module));
SigMap sigmap(module);
module->new_connections({});
dict<pair<IdString, SigSpec>, Cell*> old_buffers;
{
vector<Cell*> old_dup_buffers;
for (auto cell : module->cells())
{
if (!cell->type.in(ID($buf), ID($_BUF_)))
continue;
SigSpec insig = cell->getPort(ID::A);
SigSpec outsig = cell->getPort(ID::Y);
for (int i = 0; i < GetSize(insig) && i < GetSize(outsig); i++)
sigmap.add(insig[i], outsig[i]);
pair<IdString,Wire*> key(cell->type, outsig.as_wire());
if (old_buffers.count(key))
old_dup_buffers.push_back(cell);
else
old_buffers[key] = cell;
}
for (auto cell : old_dup_buffers)
module->remove(cell);
count_removed_buffers += GetSize(old_dup_buffers);
}
dict<SigBit, pool<Wire*>> bit2wires;
dict<SigSpec, pool<Wire*>> whole_wires;
dict<SigBit, SigBit> mapped_bits;
pool<Wire*> unmapped_wires;
for (auto wire : module->wires())
{
SigSpec keysig = sigmap(wire);
whole_wires[keysig].insert(wire);
for (auto keybit : sigmap(wire))
bit2wires[keybit].insert(wire);
if (wire->port_input) {
log(" primary input: %s\n", log_id(wire));
for (auto bit : SigSpec(wire))
mapped_bits[sigmap(bit)] = bit;
} else {
unmapped_wires.insert(wire);
}
}
auto chain_this_wire_f = [&](Wire *wire)
{
if (chain_mode)
return true;
if (output_mode && wire->port_output)
return true;
if (public_mode && wire->name.isPublic())
return true;
if (!nokeep_mode && wire->get_bool_attribute(ID::keep))
return true;
if (!nochain_mode && wire->get_bool_attribute(ID::chain))
return true;
return false;
};
auto compare_wires_f = [&](Wire *a, Wire *b)
{
// Chaining wires first, then flat wires
bool chain_a = chain_this_wire_f(a);
bool chain_b = chain_this_wire_f(b);
if (chain_a != chain_b) return chain_a;
if (!alphasort_mode)
{
// Wires with 'chain' attribute first, high values before low values
if (!nochain_mode) {
int chain_a_val = a->attributes.at(ID::chain, Const(0)).as_int();
int chain_b_val = b->attributes.at(ID::chain, Const(0)).as_int();
if (chain_a_val != chain_b_val) return chain_a_val > chain_b_val;
}
// Then wires with 'keep' attribute
if (!nokeep_mode) {
bool keep_a = a->get_bool_attribute(ID::keep);
bool keep_b = b->get_bool_attribute(ID::keep);
if (keep_a != keep_b) return keep_a;
}
// Ports before non-ports
if ((a->port_id != 0) != (b->port_id != 0))
return a->port_id != 0;
// Ports in declaration order
if (a->port_id != b->port_id)
return a->port_id < b->port_id;
}
// Nets with public names first
if (a->name.isPublic() != b->name.isPublic())
return a->name.isPublic();
// Otherwise just sort by name alphanumerically
return a->name.str() < b->name.str();
};
for (auto cell : module->cells())
{
if (cell->type.in(ID($buf), ID($_BUF_)))
continue;
for (auto &conn : cell->connections())
{
if (!cell->output(conn.first))
continue;
Wire *w = nullptr;
if (!nosticky_mode && conn.second.is_wire())
w = conn.second.as_wire();
if (w == nullptr)
{
SigSpec keysig = sigmap(conn.second);
auto it = whole_wires.find(keysig);
if (it != whole_wires.end()) {
it->second.sort(compare_wires_f);
w = *(it->second.begin());
} else {
w = module->addWire(NEW_ID, GetSize(conn.second));
for (int i = 0; i < GetSize(w); i++)
sigmap.add(SigBit(w, i), keysig[i]);
}
}
if (w->name.isPublic())
log(" directly driven by cell %s port %s: %s\n",
log_id(cell), log_id(conn.first), log_id(w));
for (auto bit : SigSpec(w))
mapped_bits[sigmap(bit)] = bit;
unmapped_wires.erase(w);
cell->setPort(conn.first, w);
}
}
pool<Cell*> added_buffers;
const auto lookup_mapping = [&mapped_bits](const SigBit bit, bool default_sx = false)
{
if (!bit.is_wire())
return bit;
if (default_sx)
return mapped_bits.at(bit, State::Sx);
return mapped_bits.at(bit);
};
auto make_buffer_f = [&](IdString type, const SigSpec &src, const SigSpec &dst)
{
auto it = old_buffers.find(pair<IdString, SigSpec>(type, dst));
if (it != old_buffers.end())
{
Cell *cell = it->second;
old_buffers.erase(it);
added_buffers.insert(cell);
if (cell->getPort(ID::A) == src) {
count_kept_buffers++;
} else {
cell->setPort(ID::A, src);
count_updated_buffers++;
}
return;
}
Cell *cell = module->addCell(NEW_ID, type);
added_buffers.insert(cell);
cell->setPort(ID::A, src);
cell->setPort(ID::Y, dst);
cell->fixup_parameters();
count_created_buffers++;
};
unmapped_wires.sort(compare_wires_f);
for (auto wire : unmapped_wires)
{
bool chain_this_wire = chain_this_wire_f(wire);
SigSpec keysig = sigmap(wire), insig = wire, outsig = wire;
for (int i = 0; i < GetSize(insig); i++)
insig[i] = lookup_mapping(keysig[i], true);
if (chain_this_wire) {
for (int i = 0; i < GetSize(outsig); i++)
mapped_bits[keysig[i]] = outsig[i];
}
log(" %s %s for %s -> %s\n",
chain_this_wire ? "chaining" : "adding",
conn_mode ? "connection" : "buffer",
log_signal(insig), log_signal(outsig));
if (conn_mode) {
if (bits_mode) {
for (int i = 0; i < GetSize(insig) && i < GetSize(outsig); i++)
module->connect(outsig[i], insig[i]);
} else {
module->connect(outsig, insig);
}
} else {
if (bits_mode) {
IdString celltype = pos_mode ? ID($pos) : buf_mode ? ID($buf) : ID($_BUF_);
for (int i = 0; i < GetSize(insig) && i < GetSize(outsig); i++)
make_buffer_f(celltype, insig[i], outsig[i]);
} else {
IdString celltype = pos_mode ? ID($pos) : buf_mode ? ID($buf) :
GetSize(outsig) == 1 ? ID($_BUF_) : ID($buf);
make_buffer_f(celltype, insig, outsig);
}
}
}
for (auto &it : old_buffers)
module->remove(it.second);
count_removed_buffers += GetSize(old_buffers);
for (auto cell : module->cells())
{
if (added_buffers.count(cell))
continue;
for (auto &conn : cell->connections())
{
if (cell->output(conn.first))
continue;
SigSpec newsig = conn.second;
for (auto &bit : newsig)
bit = lookup_mapping(sigmap(bit));
if (conn.second != newsig) {
log(" fixing input signal on cell %s port %s: %s\n",
log_id(cell), log_id(conn.first), log_signal(newsig));
cell->setPort(conn.first, newsig);
count_updated_cellports++;
}
}
}
}
log("Summary: removed %d, updated %d, kept %d, and created %d buffers, and updated %d cell ports.\n",
count_removed_buffers, count_updated_buffers, count_kept_buffers,
count_created_buffers, count_updated_cellports);
if (!nomode_mode && !(pos_mode || bits_mode || conn_mode)) {
if (design->selection().full_selection)
design->bufNormalize(true);
}
}
} BufnormPass;
PRIVATE_NAMESPACE_END

316
passes/techmap/cellmatch.cc Normal file
View file

@ -0,0 +1,316 @@
#include "kernel/celltypes.h"
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/consteval.h"
#include "kernel/utils.h"
#include <algorithm>
USING_YOSYS_NAMESPACE
YOSYS_NAMESPACE_BEGIN
// return module's inputs in canonical order
SigSpec module_inputs(Module *m)
{
SigSpec ret;
for (auto port : m->ports) {
Wire *w = m->wire(port);
if (!w->port_input)
continue;
if (w->width != 1)
log_error("Unsupported wide port (%s) of non-unit width found in module %s.\n",
log_id(w), log_id(m));
ret.append(w);
}
return ret;
}
// return module's outputs in canonical order
SigSpec module_outputs(Module *m)
{
SigSpec ret;
for (auto port : m->ports) {
Wire *w = m->wire(port);
if (!w->port_output)
continue;
if (w->width != 1)
log_error("Unsupported wide port (%s) of non-unit width found in module %s.\n",
log_id(w), log_id(m));
ret.append(w);
}
return ret;
}
// Permute the inputs of a single-output k-LUT according to varmap
uint64_t permute_lut(uint64_t lut, const std::vector<int> &varmap)
{
int k = varmap.size();
uint64_t ret = 0;
// Index j iterates over all bits in lut.
// When (j & 1 << n) is true,
// (lut & 1 << j) represents an output value where input var n is set.
// We use this fact to permute the LUT such that
// every variable n is remapped to varmap[n].
for (int j = 0; j < 1 << k; j++) {
int m = 0;
for (int l = 0; l < k; l++)
if (j & 1 << l)
m |= 1 << varmap[l];
if (lut & 1 << m)
ret |= 1 << j;
}
return ret;
}
// Find the LUT with the minimum integer representation
// such that it is a permutation of the given lut.
// The resulting LUT becomes the "fingerprint" of the "permutation class".
// This function checks all possible input permutations.
uint64_t p_class(int k, uint64_t lut)
{
std::vector<int> map;
for (int j = 0; j < k; j++)
map.push_back(j);
uint64_t repr = ~(uint64_t) 0;
std::vector<int> repr_vars;
while (true) {
uint64_t perm = permute_lut(lut, map);
if (perm <= repr) {
repr = perm;
repr_vars = map;
}
if (!std::next_permutation(map.begin(), map.end()))
break;
}
return repr;
}
// Represent module m as N single-output k-LUTs
// where k is the number of module inputs,
// and N is the number of module outputs.
bool derive_module_luts(Module *m, std::vector<uint64_t> &luts)
{
CellTypes ff_types;
ff_types.setup_stdcells_mem();
for (auto cell : m->cells()) {
if (ff_types.cell_known(cell->type)) {
log("Ignoring module '%s' which isn't purely combinational.\n", log_id(m));
return false;
}
}
SigSpec inputs = module_inputs(m);
SigSpec outputs = module_outputs(m);
int ninputs = inputs.size(), noutputs = outputs.size();
if (ninputs > 6) {
log_warning("Skipping module %s with more than 6 inputs bits.\n", log_id(m));
return false;
}
luts.clear();
luts.resize(noutputs);
ConstEval ceval(m);
for (int i = 0; i < 1 << ninputs; i++) {
ceval.clear();
for (int j = 0; j < ninputs; j++)
ceval.set(inputs[j], (i & (1 << j)) ? State::S1 : State::S0);
for (int j = 0; j < noutputs; j++) {
SigSpec bit = outputs[j];
if (!ceval.eval(bit)) {
log("Failed to evaluate output '%s' in module '%s'.\n",
log_signal(outputs[j]), log_id(m));
return false;
}
log_assert(ceval.eval(bit));
if (bit[0] == State::S1)
luts[j] |= 1 << i;
}
}
return true;
}
struct CellmatchPass : Pass {
CellmatchPass() : Pass("cellmatch", "match cells to their targets in cell library") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" cellmatch -lib <design> [module selection]\n");
log("\n");
log("This pass identifies functionally equivalent counterparts between each of the\n");
log("selected modules and a module from the secondary design <design>. For every such\n");
log("correspondence found, a techmap rule is generated for mapping instances of the\n");
log("former to instances of the latter. This techmap rule is saved in yet another\n");
log("design called '$cellmatch', which is created if non-existent.\n");
log("\n");
log("This pass restricts itself to combinational modules. Modules are functionally\n");
log("equivalent as long as their truth tables are identical upto a permutation of\n");
log("inputs and outputs. The supported number of inputs is limited to 6.\n");
log("\n");
log(" cellmatch -derive_luts [module selection]\n");
log("\n");
log("For every port in each selected module, characterize its combinational\n");
log("function with a 'lut' attribute if possible.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *d) override
{
log_header(d, "Executing CELLMATCH pass. (match cells)\n");
size_t argidx;
bool derive_luts = false;
Design *lib = NULL;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-derive_luts") {
derive_luts = true;
} else if (args[argidx] == "-lib" && argidx + 1 < args.size()) {
if (!saved_designs.count(args[++argidx]))
log_cmd_error("No design '%s' found!\n", args[argidx]);
lib = saved_designs.at(args[argidx]);
} else {
break;
}
}
extra_args(args, argidx, d);
if (!lib && !derive_luts)
log_cmd_error("Missing required -lib or -derive_luts option.\n");
struct Target {
Module *module;
std::vector<uint64_t> luts;
};
dict<pool<uint64_t>, std::vector<Target>> targets;
if (lib)
for (auto m : lib->modules()) {
pool<uint64_t> p_classes;
// produce a fingerprint in p_classes
int ninputs = module_inputs(m).size();
std::vector<uint64_t> luts;
if (!derive_module_luts(m, luts))
continue;
for (auto lut : luts)
p_classes.insert(p_class(ninputs, lut));
log_debug("Registered %s\n", log_id(m));
// save as a viable target
targets[p_classes].push_back(Target{m, luts});
}
auto r = saved_designs.emplace("$cellmatch", nullptr);
if (r.second)
r.first->second = new Design;
Design *map_design = r.first->second;
for (auto m : d->selected_whole_modules_warn(/* visit whiteboxes */derive_luts)) {
std::vector<uint64_t> luts;
if (!derive_module_luts(m, luts))
continue;
SigSpec inputs = module_inputs(m);
SigSpec outputs = module_outputs(m);
if (derive_luts) {
int no = 0;
for (auto bit : outputs) {
log_assert(bit.is_wire());
bit.wire->attributes[ID(p_class)] = p_class(inputs.size(), luts[no]);
bit.wire->attributes[ID(lut)] = Const(luts[no++], 1 << inputs.size());
}
}
// fingerprint
pool<uint64_t> p_classes;
for (auto lut : luts)
p_classes.insert(p_class(inputs.size(), lut));
for (auto target : targets[p_classes]) {
log_debug("Candidate %s for matching to %s\n", log_id(target.module), log_id(m));
SigSpec target_inputs = module_inputs(target.module);
SigSpec target_outputs = module_outputs(target.module);
if (target_inputs.size() != inputs.size())
continue;
if (target_outputs.size() != outputs.size())
continue;
std::vector<int> input_map;
for (int i = 0; i < inputs.size(); i++)
input_map.push_back(i);
bool found_match = false;
// For each input_map
while (!found_match) {
std::vector<int> output_map;
for (int i = 0; i < outputs.size(); i++)
output_map.push_back(i);
// For each output_map
while (!found_match) {
int out_no = 0;
bool match = true;
for (auto lut : luts) {
if (permute_lut(target.luts[output_map[out_no++]], input_map) != lut) {
match = false;
break;
}
}
if (match) {
log("Module %s matches %s\n", log_id(m), log_id(target.module));
// Add target.module to map_design ("$cellmatch")
// as a techmap rule to match m and replace it with target.module
Module *map = map_design->addModule(stringf("\\_60_%s_%s", log_id(m), log_id(target.module)));
Cell *cell = map->addCell(ID::_TECHMAP_REPLACE_, target.module->name);
map->attributes[ID(techmap_celltype)] = m->name.str();
for (int i = 0; i < outputs.size(); i++) {
log_assert(outputs[i].is_wire());
Wire *w = map->addWire(outputs[i].wire->name, 1);
w->port_id = outputs[i].wire->port_id;
w->port_output = true;
log_assert(target_outputs[output_map[i]].is_wire());
cell->setPort(target_outputs[output_map[i]].wire->name, w);
}
for (int i = 0; i < inputs.size(); i++) {
log_assert(inputs[i].is_wire());
Wire *w = map->addWire(inputs[i].wire->name, 1);
w->port_id = inputs[i].wire->port_id;
w->port_input = true;
log_assert(target_inputs[input_map[i]].is_wire());
cell->setPort(target_inputs[input_map[i]].wire->name, w);
}
map->fixup_ports();
found_match = true;
}
if (!std::next_permutation(output_map.begin(), output_map.end()))
break;
}
if (!std::next_permutation(input_map.begin(), input_map.end()))
break;
}
}
}
}
} CellmatchPass;
YOSYS_NAMESPACE_END

View file

@ -257,14 +257,14 @@ struct ClkbufmapPass : public Pass {
RTLIL::Cell *cell = nullptr;
bool is_input = wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute(ID::top);
if (!buf_celltype.empty() && (!is_input || buffer_inputs)) {
log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i);
log("Inserting %s on %s.%s[%d].\n", buf_celltype, log_id(module), log_id(wire), i);
cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype));
iwire = module->addWire(NEW_ID);
cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit);
cell->setPort(RTLIL::escape_id(buf_portname2), iwire);
}
if (is_input) {
log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i);
log("Inserting %s on %s.%s[%d].\n", inpad_celltype, log_id(module), log_id(wire), i);
RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype));
if (iwire) {
cell2->setPort(RTLIL::escape_id(inpad_portname), iwire);

417
passes/techmap/clockgate.cc Normal file
View file

@ -0,0 +1,417 @@
#include "kernel/yosys.h"
#include "kernel/ff.h"
#include "kernel/gzip.h"
#include "libparse.h"
#include <optional>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct ClockGateCell {
IdString name;
IdString ce_pin;
IdString clk_in_pin;
IdString clk_out_pin;
std::vector<IdString> tie_lo_pins;
};
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
ClockGateCell c;
c.name = RTLIL::escape_id(name);
char delimiter = ':';
size_t pos1 = str.find(delimiter);
if (pos1 == std::string::npos)
log_cmd_error("Not enough ports in descriptor string");
size_t pos2 = str.find(delimiter, pos1 + 1);
if (pos2 == std::string::npos)
log_cmd_error("Not enough ports in descriptor string");
size_t pos3 = str.find(delimiter, pos2 + 1);
if (pos3 != std::string::npos)
log_cmd_error("Too many ports in descriptor string");
std::string ce = str.substr(0, pos1);
c.ce_pin = RTLIL::escape_id(ce);
std::string clk_in = str.substr(pos1 + 1, pos2 - (pos1 + 1));
c.clk_in_pin = RTLIL::escape_id(clk_in);
std::string clk_out = str.substr(pos2 + 1, str.size() - (pos2 + 1));
c.clk_out_pin = RTLIL::escape_id(clk_out);
return c;
}
static std::pair<std::optional<ClockGateCell>, std::optional<ClockGateCell>>
find_icgs(std::vector<const LibertyAst *> cells, std::vector<std::string> const& dont_use_cells) {
// We will pick the most suitable ICG absed on tie_lo count and area
struct ICGRankable : public ClockGateCell { double area; };
std::optional<ICGRankable> best_pos;
std::optional<ICGRankable> best_neg;
// This is a lot of boilerplate, isn't it?
for (auto cell : cells)
{
const LibertyAst *dn = cell->find("dont_use");
if (dn != nullptr && dn->value == "true")
continue;
bool dont_use = false;
for (auto dont_use_cell : dont_use_cells)
{
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
{
dont_use = true;
break;
}
}
if (dont_use)
continue;
const LibertyAst *icg_kind_ast = cell->find("clock_gating_integrated_cell");
if (icg_kind_ast == nullptr)
continue;
auto cell_name = cell->args[0];
auto icg_kind = icg_kind_ast->value;
auto starts_with = [&](std::string prefix) {
return icg_kind.compare(0, prefix.size(), prefix) == 0;
};
bool clk_pol;
if (icg_kind == "latch_posedge" || starts_with("latch_posedge_")) {
clk_pol = true;
} else if (icg_kind == "latch_negedge" || starts_with("latch_negedge_")) {
clk_pol = false;
} else {
log("Ignoring ICG primitive %s of kind '%s'\n", cell_name, icg_kind);
continue;
}
log_debug("maybe valid icg: %s\n", cell_name);
ClockGateCell icg_interface;
icg_interface.name = RTLIL::escape_id(cell_name);
for (auto pin : cell->children) {
if (pin->id != "pin" || pin->args.size() != 1)
continue;
if (pin->find("clock_gate_clock_pin")) {
if (!icg_interface.clk_in_pin.empty()) {
log_warning("Malformed liberty file - multiple clock_gate_clock_pin in cell %s\n",
cell_name.c_str());
continue;
} else
icg_interface.clk_in_pin = RTLIL::escape_id(pin->args[0]);
} else if (pin->find("clock_gate_out_pin")) {
if (!icg_interface.clk_out_pin.empty()) {
log_warning("Malformed liberty file - multiple clock_gate_out_pin in cell %s\n",
cell_name.c_str());
continue;
} else
icg_interface.clk_out_pin = RTLIL::escape_id(pin->args[0]);
} else if (pin->find("clock_gate_enable_pin")) {
if (!icg_interface.ce_pin.empty()) {
log_warning("Malformed liberty file - multiple clock_gate_enable_pin in cell %s\n",
cell_name.c_str());
continue;
} else
icg_interface.ce_pin = RTLIL::escape_id(pin->args[0]);
} else if (pin->find("clock_gate_test_pin")) {
icg_interface.tie_lo_pins.push_back(RTLIL::escape_id(pin->args[0]));
} else {
const LibertyAst *dir = pin->find("direction");
if (dir->value == "internal")
continue;
log_warning("Malformed liberty file - extra pin %s in cell %s\n",
pin->args[0].c_str(), cell_name.c_str());
continue;
}
}
if (icg_interface.clk_in_pin.empty()) {
log_warning("Malformed liberty file - missing clock_gate_clock_pin in cell %s",
cell_name.c_str());
continue;
}
if (icg_interface.clk_out_pin.empty()) {
log_warning("Malformed liberty file - missing clock_gate_out_pin in cell %s",
cell_name.c_str());
continue;
}
if (icg_interface.ce_pin.empty()) {
log_warning("Malformed liberty file - missing clock_gate_enable_pin in cell %s",
cell_name.c_str());
continue;
}
double area = 0;
const LibertyAst *ar = cell->find("area");
if (ar != nullptr && !ar->value.empty())
area = atof(ar->value.c_str());
std::optional<ICGRankable>& icg_to_beat = clk_pol ? best_pos : best_neg;
bool winning = false;
if (icg_to_beat) {
log_debug("ties: %zu ? %zu\n", icg_to_beat->tie_lo_pins.size(),
icg_interface.tie_lo_pins.size());
log_debug("area: %f ? %f\n", icg_to_beat->area, area);
// Prefer fewer test enables over area reduction (unlikely to matter)
auto goal = std::make_pair(icg_to_beat->tie_lo_pins.size(), icg_to_beat->area);
auto cost = std::make_pair(icg_interface.tie_lo_pins.size(), area);
winning = cost < goal;
if (winning)
log_debug("%s beats %s\n", icg_interface.name, icg_to_beat->name);
} else {
log_debug("%s is the first of its polarity\n", icg_interface.name);
winning = true;
}
if (winning) {
ICGRankable new_icg {icg_interface, area};
icg_to_beat.emplace(new_icg);
}
}
std::optional<ClockGateCell> pos;
std::optional<ClockGateCell> neg;
if (best_pos) {
log("Selected rising edge ICG %s from Liberty file\n", best_pos->name);
pos.emplace(*best_pos);
}
if (best_neg) {
log("Selected falling edge ICG %s from Liberty file\n", best_neg->name);
neg.emplace(*best_neg);
}
return std::make_pair(pos, neg);
}
struct ClockgatePass : public Pass {
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
void help() override {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" clockgate [options] [selection]\n");
log("\n");
log("This pass transforms each set of FFs sharing the same clock and\n");
log("enable signal into a clock-gating cell and a set of enable-less FFs.\n");
log("Primarily a power-saving transformation on ASIC designs.\n");
log("\n");
log(" -pos <celltype> <ce>:<clk>:<gclk>\n");
log(" If specified, rising-edge FFs will have CE inputs\n");
log(" removed and a gated clock will be created by the\n");
log(" user-specified <celltype> ICG (integrated clock gating)\n");
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
log(" The ICG's clock enable pin must be active high.\n");
log(" -neg <celltype> <ce>:<clk>:<gclk>\n");
log(" If specified, falling-edge FFs will have CE inputs\n");
log(" removed and a gated clock will be created by the\n");
log(" user-specified <celltype> ICG (integrated clock gating)\n");
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
log(" The ICG's clock enable pin must be active high.\n");
log(" -liberty <filename>\n");
log(" If specified, ICGs will be selected from the liberty files\n");
log(" if available. Priority is given to cells with fewer tie_lo\n");
log(" inputs and smaller size. This removes the need to manually\n");
log(" specify -pos or -neg and -tie_lo.\n");
log(" -dont_use <celltype>\n");
log(" Cells <celltype> won't be considered when searching for ICGs\n");
log(" in the liberty file specified by -liberty.\n");
log(" -tie_lo <port_name>\n");
log(" Port <port_name> of the ICG will be tied to zero.\n");
log(" Intended for DFT scan-enable pins.\n");
log(" -min_net_size <n>\n");
log(" Only transform sets of at least <n> eligible FFs.\n");
log(" \n");
}
// One ICG will be generated per ClkNetInfo
// if the number of FFs associated with it is sufficent
struct ClkNetInfo {
// Original, ungated clock into enabled FF
SigBit clk_bit;
// Original clock enable into enabled FF
SigBit ce_bit;
bool pol_clk;
bool pol_ce;
[[nodiscard]] Hasher hash_into(Hasher h) const {
auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce);
h.eat(t);
return h;
}
bool operator==(const ClkNetInfo& other) const {
return (clk_bit == other.clk_bit) &&
(ce_bit == other.ce_bit) &&
(pol_clk == other.pol_clk) &&
(pol_ce == other.pol_ce);
}
};
struct GClkNetInfo {
// How many CE FFs on this CLK net have we seen?
int net_size;
// After ICG generation, we have new gated CLK signals
Wire* new_net;
};
ClkNetInfo clk_info_from_ff(FfData& ff) {
SigBit clk = ff.sig_clk[0];
SigBit ce = ff.sig_ce[0];
ClkNetInfo info{clk, ce, ff.pol_clk, ff.pol_ce};
return info;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing CLOCK_GATE pass (extract clock gating out of flip flops).\n");
std::optional<ClockGateCell> pos_icg_desc;
std::optional<ClockGateCell> neg_icg_desc;
std::vector<std::string> tie_lo_pins;
std::vector<std::string> liberty_files;
std::vector<std::string> dont_use_cells;
int min_net_size = 0;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-pos" && argidx+2 < args.size()) {
auto name = args[++argidx];
auto rest = args[++argidx];
pos_icg_desc = icg_from_arg(name, rest);
continue;
}
if (args[argidx] == "-neg" && argidx+2 < args.size()) {
auto name = args[++argidx];
auto rest = args[++argidx];
neg_icg_desc = icg_from_arg(name, rest);
continue;
}
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
tie_lo_pins.push_back(RTLIL::escape_id(args[++argidx]));
continue;
}
if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
append_globbed(liberty_files, args[++argidx]);
continue;
}
if (args[argidx] == "-dont_use" && argidx+1 < args.size()) {
dont_use_cells.push_back(args[++argidx]);
continue;
}
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
min_net_size = atoi(args[++argidx].c_str());
continue;
}
break;
}
if (!liberty_files.empty()) {
LibertyMergedCells merged;
for (auto path : liberty_files) {
std::istream* f = uncompressed(path);
LibertyParser p(*f, path);
merged.merge(p);
delete f;
}
std::tie(pos_icg_desc, neg_icg_desc) =
find_icgs(merged.cells, dont_use_cells);
} else {
for (auto pin : tie_lo_pins) {
if (pos_icg_desc)
pos_icg_desc->tie_lo_pins.push_back(pin);
if (neg_icg_desc)
neg_icg_desc->tie_lo_pins.push_back(pin);
}
}
extra_args(args, argidx, design);
pool<Cell*> ce_ffs;
dict<ClkNetInfo, GClkNetInfo> clk_nets;
int gated_flop_count = 0;
for (auto module : design->selected_unboxed_whole_modules()) {
for (auto cell : module->cells()) {
if (!cell->is_builtin_ff())
continue;
FfData ff(nullptr, cell);
// It would be odd to get constants, but we better handle it
if (ff.has_ce) {
if (!ff.sig_clk.is_bit() || !ff.sig_ce.is_bit())
continue;
if (!ff.sig_clk[0].is_wire() || !ff.sig_ce[0].is_wire())
continue;
ce_ffs.insert(cell);
ClkNetInfo info = clk_info_from_ff(ff);
auto it = clk_nets.find(info);
if (it == clk_nets.end())
clk_nets[info] = GClkNetInfo();
clk_nets[info].net_size++;
}
}
for (auto& clk_net : clk_nets) {
auto& clk = clk_net.first;
auto& gclk = clk_net.second;
if (gclk.net_size < min_net_size)
continue;
std::optional<ClockGateCell> matching_icg_desc;
if (pos_icg_desc && clk.pol_clk)
matching_icg_desc = pos_icg_desc;
else if (neg_icg_desc && !clk.pol_clk)
matching_icg_desc = neg_icg_desc;
if (!matching_icg_desc)
continue;
Cell* icg = module->addCell(NEW_ID, matching_icg_desc->name);
icg->setPort(matching_icg_desc->ce_pin, clk.ce_bit);
icg->setPort(matching_icg_desc->clk_in_pin, clk.clk_bit);
gclk.new_net = module->addWire(NEW_ID);
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
// Tie low DFT ports like scan chain enable
for (auto port : matching_icg_desc->tie_lo_pins)
icg->setPort(port, Const(0, 1));
// Fix CE polarity if needed
if (!clk.pol_ce) {
SigBit ce_fixed_pol = module->NotGate(NEW_ID, clk.ce_bit);
icg->setPort(matching_icg_desc->ce_pin, ce_fixed_pol);
}
}
for (auto cell : ce_ffs) {
FfData ff(nullptr, cell);
ClkNetInfo info = clk_info_from_ff(ff);
auto it = clk_nets.find(info);
log_assert(it != clk_nets.end() && "Bug: desync ce_ffs and clk_nets");
if (!it->second.new_net)
continue;
log_debug("Fix up FF %s\n", cell->name);
// Now we start messing with the design
ff.has_ce = false;
// Construct the clock gate
// ICG = integrated clock gate, industry shorthand
ff.sig_clk = (*it).second.new_net;
// Rebuild the flop
(void)ff.emit();
gated_flop_count++;
}
ce_ffs.clear();
clk_nets.clear();
}
log("Converted %d FFs.\n", gated_flop_count);
}
} ClockgatePass;
PRIVATE_NAMESPACE_END

106
passes/techmap/constmap.cc Normal file
View file

@ -0,0 +1,106 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2025 King Lok Chung <king.chung@manchester.ac.uk>
*
* 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/rtlil.h"
#include "kernel/log.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
static std::string celltype, cell_portname, cell_paramname;
static RTLIL::Module *module;
static RTLIL::SigChunk value;
void constmap_worker(RTLIL::SigSpec &sig)
{
if (sig.is_fully_const()){
value = module->addWire(NEW_ID, sig.size());
RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
cell->setParam(cell_paramname, sig.as_const());
cell->setPort(cell_portname, value);
sig = value;
}
}
struct ConstmapPass : public Pass {
ConstmapPass() : Pass("constmap", "technology mapping of coarse constant value") { }
void help() override
{
log("\n");
log(" constmap [options] [selection]\n");
log("\n");
log("Map constants to a driver cell.\n");
log("\n");
log(" -cell <celltype> <portname> <paramname>\n");
log(" Replace constant bits with this cell.\n");
log(" The value of the constant will be stored to the parameter specified.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing CONSTMAP pass (mapping to constant driver).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-cell" && argidx+3 < args.size()){
celltype = RTLIL::escape_id(args[++argidx]);
cell_portname = RTLIL::escape_id(args[++argidx]);
cell_paramname = RTLIL::escape_id(args[++argidx]);
continue;
}
break;
}
extra_args(args, argidx, design);
if (design->has(celltype)) {
Module *existing = design->module(celltype);
bool has_port = false;
for (auto &p : existing->ports){
if (p == cell_portname){
has_port = true;
break;
}
}
if (!has_port)
log_cmd_error("Cell type '%s' does not have port '%s'.\n", celltype, cell_portname);
bool has_param = false;
for (auto &p : existing->avail_parameters){
if (p == cell_paramname)
has_param = true;
}
if (!has_param)
log_cmd_error("Cell type '%s' does not have parameter '%s'.\n", celltype, cell_paramname);
}
for (auto mod : design->selected_modules())
{
module = mod;
module->rewrite_sigspecs(constmap_worker);
}
}
} ConstmapPass;
PRIVATE_NAMESPACE_END

View file

@ -118,13 +118,13 @@ struct DffinitPass : public Pass {
for (int i = 0; i < GetSize(sig); i++) {
if (initval[i] == State::Sx)
continue;
while (GetSize(value.bits) <= i)
value.bits.push_back(State::S0);
if (noreinit && value.bits[i] != State::Sx && value.bits[i] != initval[i])
if (GetSize(value) <= i)
value.resize(i + 1, State::S0);
if (noreinit && value[i] != State::Sx && value[i] != initval[i])
log_error("Trying to assign a different init value for %s.%s.%s which technically "
"have a conflicted init value.\n",
log_id(module), log_id(cell), log_id(it.second));
value.bits[i] = initval[i];
value.set(i, initval[i]);
}
if (highlow_mode && GetSize(value) != 0) {

View file

@ -869,17 +869,17 @@ struct DffLegalizePass : public Pass {
if (ff.has_arst) {
if (ff.val_arst[i] == State::Sx) {
if (!(supported & (mask << 8)))
ff.val_arst[i] = State::S0;
ff.val_arst.set(i, State::S0);
if (!(supported & (mask << 4)))
ff.val_arst[i] = State::S1;
ff.val_arst.set(i, State::S1);
}
}
if (ff.has_srst) {
if (ff.val_srst[i] == State::Sx) {
if (!(supported & (mask << 8)))
ff.val_srst[i] = State::S0;
ff.val_srst.set(i, State::S0);
if (!(supported & (mask << 4)))
ff.val_srst[i] = State::S1;
ff.val_srst.set(i, State::S1);
}
}
}
@ -1111,7 +1111,7 @@ struct DffLegalizePass : public Pass {
pol_r = celltype[13];
} else {
unrecognized:
log_error("unrecognized cell type %s.\n", celltype.c_str());
log_error("unrecognized cell type %s.\n", celltype);
}
int mask = 0;
int match = 0;
@ -1140,12 +1140,12 @@ unrecognized:
initmask = 0x555;
} else if (inittype == "r") {
if (srval == 0)
log_error("init type r not valid for cell type %s.\n", celltype.c_str());
log_error("init type r not valid for cell type %s.\n", celltype);
initmask = 0x537;
} else if (inittype == "01") {
initmask = 0x777;
} else {
log_error("unrecognized init type %s for cell type %s.\n", inittype.c_str(), celltype.c_str());
log_error("unrecognized init type %s for cell type %s.\n", inittype, celltype);
}
if (srval == '0') {
initmask &= 0x0ff;
@ -1196,7 +1196,7 @@ unrecognized:
srst_used.clear();
for (auto cell : module->cells()) {
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
if (!cell->is_builtin_ff())
continue;
FfData ff(&initvals, cell);
@ -1208,7 +1208,7 @@ unrecognized:
}
for (auto cell : module->selected_cells())
{
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
if (!cell->is_builtin_ff())
continue;
FfData ff(&initvals, cell);
legalize_ff(ff);

View file

@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/gzip.h"
#include "libparse.h"
#include <string.h>
#include <errno.h>
@ -35,9 +36,9 @@ static std::map<RTLIL::IdString, cell_mapping> cell_mappings;
static void logmap(IdString dff)
{
if (cell_mappings.count(dff) == 0) {
log(" unmapped dff cell: %s\n", dff.c_str());
log(" unmapped dff cell: %s\n", dff);
} else {
log(" %s %s (", cell_mappings[dff].cell_name.c_str(), dff.substr(1).c_str());
log(" %s %s (", cell_mappings[dff].cell_name, dff.substr(1));
bool first = true;
for (auto &port : cell_mappings[dff].ports) {
char arg[3] = { port.second, 0, 0 };
@ -45,7 +46,7 @@ static void logmap(IdString dff)
arg[1] = arg[0] - ('a' - 'A'), arg[0] = '~';
else
arg[1] = arg[0], arg[0] = ' ';
log("%s.%s(%s)", first ? "" : ", ", port.first.c_str(), arg);
log("%s.%s(%s)", first ? "" : ", ", port.first, arg);
first = false;
}
log(");\n");
@ -66,6 +67,11 @@ static void logmap_all()
logmap(ID($_DFF_PP0_));
logmap(ID($_DFF_PP1_));
logmap(ID($_DFFE_NN_));
logmap(ID($_DFFE_NP_));
logmap(ID($_DFFE_PN_));
logmap(ID($_DFFE_PP_));
logmap(ID($_DFFSR_NNN_));
logmap(ID($_DFFSR_NNP_));
logmap(ID($_DFFSR_NPN_));
@ -76,7 +82,120 @@ static void logmap_all()
logmap(ID($_DFFSR_PPP_));
}
static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, bool &pin_pol)
static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std::string &data_name, bool &data_not_inverted, std::string &enable_name, bool &enable_not_inverted)
{
static pool<std::string> warned_cells{};
if (cell == nullptr || attr == nullptr || attr->value.empty())
return false;
auto expr = attr->value;
auto cell_name = cell->args[0];
for (size_t pos = expr.find_first_of("\"\t"); pos != std::string::npos; pos = expr.find_first_of("\"\t"))
expr.erase(pos, 1);
// if this isn't an enable flop, the next_state variable is usually just the input pin name.
if (expr[expr.size()-1] == '\'') {
data_name = expr.substr(0, expr.size()-1);
data_not_inverted = false;
} else if (expr[0] == '!') {
data_name = expr.substr(1, expr.size()-1);
data_not_inverted = false;
} else if (expr[0] == '(' && expr[expr.size() - 1] == ')') {
data_name = expr.substr(1, expr.size() - 2);
data_not_inverted = true;
} else {
data_name = expr;
data_not_inverted = true;
}
for (auto child : cell->children)
if (child->id == "pin" && child->args.size() == 1 && child->args[0] == data_name)
return true;
// the next_state variable isn't just a pin name; perhaps this is an enable?
auto helper = LibertyExpression::Lexer(expr);
auto tree = LibertyExpression::parse(helper);
// log_debug("liberty expression:\n%s\n", tree.str());
if (tree.kind == LibertyExpression::Kind::EMPTY) {
if (!warned_cells.count(cell_name)) {
log_debug("Invalid expression '%s' in next_state attribute of cell '%s' - skipping.\n", expr, cell_name);
warned_cells.insert(cell_name);
}
return false;
}
auto pin_names = std::unordered_set<std::string>{};
tree.get_pin_names(pin_names);
// from the `ff` block, we know the flop output signal name for loopback.
auto ff = cell->find("ff");
if (ff == nullptr || ff->args.size() != 2)
return false;
auto ff_output = ff->args.at(0);
// This test is redundant with the one in enable_pin, but we're in a
// position that gives better diagnostics here.
if (!pin_names.count(ff_output)) {
if (!warned_cells.count(cell_name)) {
log_debug("Inference failed on expression '%s' in next_state attribute of cell '%s' because it does not contain ff output '%s' - skipping.\n", expr, cell_name, ff_output);
warned_cells.insert(cell_name);
}
return false;
}
data_not_inverted = true;
data_name = "";
enable_not_inverted = true;
enable_name = "";
if (pin_names.size() == 3 && pin_names.count(ff_output)) {
pin_names.erase(ff_output);
auto pins = std::vector<std::string>(pin_names.begin(), pin_names.end());
int lut = 0;
for (int n = 0; n < 8; n++) {
auto values = std::unordered_map<std::string, bool>{};
values.insert(std::make_pair(pins[0], (n & 1) == 1));
values.insert(std::make_pair(pins[1], (n & 2) == 2));
values.insert(std::make_pair(ff_output, (n & 4) == 4));
if (tree.eval(values))
lut |= 1 << n;
}
// the ff output Q is in a known bit location, so we now just have to compare the LUT mask to known values to find the enable pin and polarity.
if (lut == 0xD8) {
data_name = pins[1];
enable_name = pins[0];
return true;
}
if (lut == 0xB8) {
data_name = pins[0];
enable_name = pins[1];
return true;
}
enable_not_inverted = false;
if (lut == 0xE4) {
data_name = pins[1];
enable_name = pins[0];
return true;
}
if (lut == 0xE2) {
data_name = pins[0];
enable_name = pins[1];
return true;
}
// this does not match an enable flop.
}
if (!warned_cells.count(cell_name)) {
log_debug("Inference failed on expression '%s' in next_state attribute of cell '%s' because it does not evaluate to an enable flop - skipping.\n", expr, cell_name);
warned_cells.insert(cell_name);
}
return false;
}
static bool parse_pin(const LibertyAst *cell, const LibertyAst *attr, std::string &pin_name, bool &pin_pol)
{
if (cell == nullptr || attr == nullptr || attr->value.empty())
return false;
@ -106,32 +225,26 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
For now, we'll simply produce a warning to let the user know something is up.
*/
if (pin_name.find_first_of("^*|&") == std::string::npos) {
log_warning("Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str());
log_debug("Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping.\n", pin_name, cell->args[0]);
}
else {
log_warning("Found unsupported expression '%s' in pin attribute of cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str());
log_debug("Found unsupported expression '%s' in pin attribute of cell '%s' - skipping.\n", pin_name, cell->args[0]);
}
return false;
}
static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, std::vector<std::string> &dont_use_cells)
static void find_cell(std::vector<const LibertyAst *> cells, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool has_enable, bool enapol, std::vector<std::string> &dont_use_cells)
{
LibertyAst *best_cell = nullptr;
const LibertyAst *best_cell = nullptr;
std::map<std::string, char> best_cell_ports;
int best_cell_pins = 0;
bool best_cell_noninv = false;
double best_cell_area = 0;
if (ast->id != "library")
log_error("Format error in liberty file.\n");
for (auto cell : ast->children)
for (auto cell : cells)
{
if (cell->id != "cell" || cell->args.size() != 1)
continue;
LibertyAst *dn = cell->find("dont_use");
const LibertyAst *dn = cell->find("dont_use");
if (dn != nullptr && dn->value == "true")
continue;
@ -147,17 +260,24 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
if (dont_use)
continue;
LibertyAst *ff = cell->find("ff");
const LibertyAst *ff = cell->find("ff");
if (ff == nullptr)
continue;
std::string cell_clk_pin, cell_rst_pin, cell_next_pin;
bool cell_clk_pol, cell_rst_pol, cell_next_pol;
std::string cell_clk_pin, cell_rst_pin, cell_next_pin, cell_enable_pin;
bool cell_clk_pol, cell_rst_pol, cell_next_pol, cell_enable_pol;
if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
continue;
if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
if (!parse_next_state(cell, ff->find("next_state"), cell_next_pin, cell_next_pol, cell_enable_pin, cell_enable_pol) || (has_enable && (cell_enable_pin.empty() || cell_enable_pol != enapol)))
continue;
if (has_reset && !cell_next_pol) {
// next_state is negated
// we later propagate this inversion to the output,
// which requires the negation of the reset value
rstval = !rstval;
}
if (has_reset && rstval == false) {
if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol)
continue;
@ -171,10 +291,12 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
this_cell_ports[cell_clk_pin] = 'C';
if (has_reset)
this_cell_ports[cell_rst_pin] = 'R';
if (has_enable)
this_cell_ports[cell_enable_pin] = 'E';
this_cell_ports[cell_next_pin] = 'D';
double area = 0;
LibertyAst *ar = cell->find("area");
const LibertyAst *ar = cell->find("area");
if (ar != nullptr && !ar->value.empty())
area = atof(ar->value.c_str());
@ -186,7 +308,7 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
if (pin->id != "pin" || pin->args.size() != 1)
continue;
LibertyAst *dir = pin->find("direction");
const LibertyAst *dir = pin->find("direction");
if (dir == nullptr || dir->value == "internal")
continue;
num_pins++;
@ -194,7 +316,7 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0)
goto continue_cell_loop;
LibertyAst *func = pin->find("function");
const LibertyAst *func = pin->find("function");
if (dir->value == "output" && func != nullptr) {
std::string value = func->value;
for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
@ -239,23 +361,19 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
}
}
static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, std::vector<std::string> &dont_use_cells)
static void find_cell_sr(std::vector<const LibertyAst *> cells, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool has_enable, bool enapol, std::vector<std::string> &dont_use_cells)
{
LibertyAst *best_cell = nullptr;
const LibertyAst *best_cell = nullptr;
std::map<std::string, char> best_cell_ports;
int best_cell_pins = 0;
bool best_cell_noninv = false;
double best_cell_area = 0;
if (ast->id != "library")
log_error("Format error in liberty file.\n");
log_assert(!enapol && "set/reset cell with enable is unimplemented due to lack of cells for testing");
for (auto cell : ast->children)
for (auto cell : cells)
{
if (cell->id != "cell" || cell->args.size() != 1)
continue;
LibertyAst *dn = cell->find("dont_use");
const LibertyAst *dn = cell->find("dont_use");
if (dn != nullptr && dn->value == "true")
continue;
@ -271,30 +389,44 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
if (dont_use)
continue;
LibertyAst *ff = cell->find("ff");
const LibertyAst *ff = cell->find("ff");
if (ff == nullptr)
continue;
std::string cell_clk_pin, cell_set_pin, cell_clr_pin, cell_next_pin;
bool cell_clk_pol, cell_set_pol, cell_clr_pol, cell_next_pol;
std::string cell_clk_pin, cell_set_pin, cell_clr_pin, cell_next_pin, cell_enable_pin;
bool cell_clk_pol, cell_set_pol, cell_clr_pol, cell_next_pol, cell_enable_pol;
if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
continue;
if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
if (!parse_next_state(cell, ff->find("next_state"), cell_next_pin, cell_next_pol, cell_enable_pin, cell_enable_pol))
continue;
if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol) || cell_set_pol != setpol)
if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol))
continue;
if (!parse_pin(cell, ff->find("clear"), cell_clr_pin, cell_clr_pol) || cell_clr_pol != clrpol)
if (!parse_pin(cell, ff->find("clear"), cell_clr_pin, cell_clr_pol))
continue;
if (!cell_next_pol) {
// next_state is negated
// we later propagate this inversion to the output,
// which requires the swap of set and reset
std::swap(cell_set_pin, cell_clr_pin);
std::swap(cell_set_pol, cell_clr_pol);
}
if (cell_set_pol != setpol)
continue;
if (cell_clr_pol != clrpol)
continue;
std::map<std::string, char> this_cell_ports;
this_cell_ports[cell_clk_pin] = 'C';
this_cell_ports[cell_set_pin] = 'S';
this_cell_ports[cell_clr_pin] = 'R';
if (has_enable)
this_cell_ports[cell_enable_pin] = 'E';
this_cell_ports[cell_next_pin] = 'D';
double area = 0;
LibertyAst *ar = cell->find("area");
const LibertyAst *ar = cell->find("area");
if (ar != nullptr && !ar->value.empty())
area = atof(ar->value.c_str());
@ -306,7 +438,7 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
if (pin->id != "pin" || pin->args.size() != 1)
continue;
LibertyAst *dir = pin->find("direction");
const LibertyAst *dir = pin->find("direction");
if (dir == nullptr || dir->value == "internal")
continue;
num_pins++;
@ -314,18 +446,20 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0)
goto continue_cell_loop;
LibertyAst *func = pin->find("function");
const LibertyAst *func = pin->find("function");
if (dir->value == "output" && func != nullptr) {
std::string value = func->value;
for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
value.erase(pos, 1);
if (value == ff->args[0]) {
// next_state negation propagated to output
this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q';
if (cell_next_pol)
found_noninv_output = true;
found_output = true;
} else
if (value == ff->args[1]) {
// next_state negation propagated to output
this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q';
if (!cell_next_pol)
found_noninv_output = true;
@ -361,7 +495,7 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module)
{
log("Mapping DFF cells in module `%s':\n", module->name.c_str());
log("Mapping DFF cells in module `%s':\n", module->name);
dict<SigBit, pool<Cell*>> notmap;
SigMap sigmap(module);
@ -426,11 +560,11 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module)
new_cell->setPort("\\" + port.first, sig);
}
stats[stringf(" mapped %%d %s cells to %s cells.\n", cell_type.c_str(), new_cell->type.c_str())]++;
stats[stringf("%s cells to %s cells", cell_type, new_cell->type)]++;
}
for (auto &stat: stats)
log(stat.first.c_str(), stat.second);
log(" mapped %d %s.\n", stat.second, stat.first);
}
struct DfflibmapPass : public Pass {
@ -441,7 +575,7 @@ struct DfflibmapPass : public Pass {
log(" dfflibmap [-prepare] [-map-only] [-info] [-dont_use <cell_name>] -liberty <file> [selection]\n");
log("\n");
log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
log("library specified in the given liberty file.\n");
log("library specified in the given liberty files.\n");
log("\n");
log("This pass may add inverters as needed. Therefore it is recommended to\n");
log("first run this pass and then map the logic paths to the target technology.\n");
@ -470,11 +604,11 @@ struct DfflibmapPass : public Pass {
log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
log_push();
std::string liberty_file;
bool prepare_mode = false;
bool map_only_mode = false;
bool info_mode = false;
std::vector<std::string> liberty_files;
std::vector<std::string> dont_use_cells;
size_t argidx;
@ -482,8 +616,7 @@ struct DfflibmapPass : public Pass {
{
std::string arg = args[argidx];
if (arg == "-liberty" && argidx+1 < args.size()) {
liberty_file = args[++argidx];
rewrite_filename(liberty_file);
append_globbed(liberty_files, args[++argidx]);
continue;
}
if (arg == "-prepare") {
@ -516,36 +649,42 @@ struct DfflibmapPass : public Pass {
if (modes > 1)
log_cmd_error("Only one of -prepare, -map-only, or -info options should be given!\n");
if (liberty_file.empty())
if (liberty_files.empty())
log_cmd_error("Missing `-liberty liberty_file' option!\n");
std::ifstream f;
f.open(liberty_file.c_str());
if (f.fail())
log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
LibertyParser libparser(f);
f.close();
LibertyMergedCells merged;
for (auto path : liberty_files) {
std::istream* f = uncompressed(path);
LibertyParser p(*f, path);
merged.merge(p);
delete f;
}
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_N_), false, false, false, false, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_P_), true, false, false, false, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, dont_use_cells);
find_cell(merged.cells, ID($_DFF_NN0_), false, true, false, false, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_NN1_), false, true, false, true, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_NP0_), false, true, true, false, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_NP1_), false, true, true, true, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_PN0_), true, true, false, false, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_PN1_), true, true, false, true, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_PP0_), true, true, true, false, false, false, dont_use_cells);
find_cell(merged.cells, ID($_DFF_PP1_), true, true, true, true, false, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, dont_use_cells);
find_cell(merged.cells, ID($_DFFE_NN_), false, false, false, false, true, false, dont_use_cells);
find_cell(merged.cells, ID($_DFFE_NP_), false, false, false, false, true, true, dont_use_cells);
find_cell(merged.cells, ID($_DFFE_PN_), true, false, false, false, true, false, dont_use_cells);
find_cell(merged.cells, ID($_DFFE_PP_), true, false, false, false, true, true, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_NNN_), false, false, false, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_NNP_), false, false, true, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_NPN_), false, true, false, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_NPP_), false, true, true, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_PNN_), true, false, false, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_PNP_), true, false, true, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_PPN_), true, true, false, false, false, dont_use_cells);
find_cell_sr(merged.cells, ID($_DFFSR_PPP_), true, true, true, false, false, dont_use_cells);
log(" final dff cell mappings:\n");
logmap_all();
@ -553,10 +692,10 @@ struct DfflibmapPass : public Pass {
if (!map_only_mode) {
std::string dfflegalize_cmd = "dfflegalize";
for (auto it : cell_mappings)
dfflegalize_cmd += stringf(" -cell %s 01", it.first.c_str());
dfflegalize_cmd += stringf(" -cell %s 01", it.first);
dfflegalize_cmd += " t:$_DFF* t:$_SDFF*";
if (info_mode) {
log("dfflegalize command line: %s\n", dfflegalize_cmd.c_str());
log("dfflegalize command line: %s\n", dfflegalize_cmd);
} else {
Pass::call(design, dfflegalize_cmd);
}

View file

@ -74,7 +74,7 @@ struct DffunmapPass : public Pass {
for (auto cell : mod->selected_cells())
{
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
if (!cell->is_builtin_ff())
continue;
FfData ff(&initvals, cell);

View file

@ -292,7 +292,7 @@ RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit:
SigSet<std::pair<RTLIL::IdString, int>> sig2port;
// create new cell
RTLIL::Cell *cell = haystack->addCell(stringf("$extract$%s$%d", needle->name.c_str(), autoidx++), needle->name);
RTLIL::Cell *cell = haystack->addCell(stringf("$extract$%s$%d", needle->name, autoidx++), needle->name);
// create cell ports
for (auto wire : needle->wires()) {
@ -605,7 +605,7 @@ struct ExtractPass : public Pass {
f.open(filename.c_str());
if (f.fail()) {
delete map;
log_cmd_error("Can't open map file `%s'.\n", filename.c_str());
log_cmd_error("Can't open map file `%s'.\n", filename);
}
Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog"));
f.close();
@ -627,7 +627,7 @@ struct ExtractPass : public Pass {
for (auto module : map->modules()) {
SubCircuit::Graph mod_graph;
std::string graph_name = "needle_" + RTLIL::unescape_id(module->name);
log("Creating needle graph %s.\n", graph_name.c_str());
log("Creating needle graph %s.\n", graph_name);
if (module2graph(mod_graph, module, constports)) {
solver.addGraph(graph_name, mod_graph);
needle_map[graph_name] = module;
@ -638,7 +638,7 @@ struct ExtractPass : public Pass {
for (auto module : design->modules()) {
SubCircuit::Graph mod_graph;
std::string graph_name = "haystack_" + RTLIL::unescape_id(module->name);
log("Creating haystack graph %s.\n", graph_name.c_str());
log("Creating haystack graph %s.\n", graph_name);
if (module2graph(mod_graph, module, constports, design, mine_mode ? mine_max_fanout : -1, mine_mode ? &mine_split : nullptr)) {
solver.addGraph(graph_name, mod_graph);
haystack_map[graph_name] = module;
@ -654,7 +654,7 @@ struct ExtractPass : public Pass {
for (auto needle : needle_list)
for (auto &haystack_it : haystack_map) {
log("Solving for %s in %s.\n", ("needle_" + RTLIL::unescape_id(needle->name)).c_str(), haystack_it.first.c_str());
log("Solving for %s in %s.\n", ("needle_" + RTLIL::unescape_id(needle->name)), haystack_it.first);
solver.solve(results, "needle_" + RTLIL::unescape_id(needle->name), haystack_it.first, false);
}
log("Found %d matches.\n", GetSize(results));
@ -665,11 +665,11 @@ struct ExtractPass : public Pass {
for (int i = 0; i < int(results.size()); i++) {
auto &result = results[i];
log("\nMatch #%d: (%s in %s)\n", i, result.needleGraphId.c_str(), result.haystackGraphId.c_str());
log("\nMatch #%d: (%s in %s)\n", i, result.needleGraphId, result.haystackGraphId);
for (const auto &it : result.mappings) {
log(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
log(" %s -> %s", it.first, it.second.haystackNodeId);
for (const auto & it2 : it.second.portMapping)
log(" %s:%s", it2.first.c_str(), it2.second.c_str());
log(" %s:%s", it2.first, it2.second);
log("\n");
}
RTLIL::Cell *new_cell = replace(needle_map.at(result.needleGraphId), haystack_map.at(result.haystackGraphId), result);
@ -693,7 +693,7 @@ struct ExtractPass : public Pass {
log("\nFrequent SubCircuit with %d nodes and %d matches:\n", int(result.nodes.size()), result.totalMatchesAfterLimits);
log(" primary match in %s:", log_id(haystack_map.at(result.graphId)->name));
for (auto &node : result.nodes)
log(" %s", RTLIL::unescape_id(node.nodeId).c_str());
log(" %s", RTLIL::unescape_id(node.nodeId));
log("\n");
for (auto &it : result.matchesPerGraph)
log(" matches in %s: %d\n", log_id(haystack_map.at(it.first)->name), it.second);
@ -744,7 +744,7 @@ struct ExtractPass : public Pass {
rewrite_filename(mine_outfile);
f.open(mine_outfile.c_str(), std::ofstream::trunc);
if (f.fail())
log_error("Can't open output file `%s'.\n", mine_outfile.c_str());
log_error("Can't open output file `%s'.\n", mine_outfile);
Backend::backend_call(map, &f, mine_outfile, "rtlil");
f.close();
}

View file

@ -847,7 +847,7 @@ struct ExtractCounterPass : public Pass {
else if (arg == "no")
settings.allow_arst = false;
else
log_error("Invalid -allow_arst value \"%s\"\n", arg.c_str());
log_error("Invalid -allow_arst value \"%s\"\n", arg);
continue;
}
@ -861,7 +861,7 @@ struct ExtractCounterPass : public Pass {
else if (arg == "both")
settings.allowed_dirs = 2;
else
log_error("Invalid -dir value \"%s\"\n", arg.c_str());
log_error("Invalid -dir value \"%s\"\n", arg);
continue;
}
}
@ -893,7 +893,7 @@ struct ExtractCounterPass : public Pass {
for(auto cpair : cells_to_rename)
{
//log("Renaming cell %s to %s\n", log_id(cpair.first->name), cpair.second.c_str());
//log("Renaming cell %s to %s\n", log_id(cpair.first->name), cpair.second);
module->rename(cpair.first, cpair.second);
}
}

View file

@ -40,10 +40,10 @@ int bindec(unsigned char v)
r += (~((v & 2) - 1)) & 10;
r += (~((v & 4) - 1)) & 100;
r += (~((v & 8) - 1)) & 1000;
r += (~((v & 16) - 1)) & 10000;
r += (~((v & 32) - 1)) & 100000;
r += (~((v & 64) - 1)) & 1000000;
r += (~((v & 128) - 1)) & 10000000;
r += (~((v & 16) - 1)) & 10'000;
r += (~((v & 32) - 1)) & 100'000;
r += (~((v & 64) - 1)) & 1'000'000;
r += (~((v & 128) - 1)) & 10'000'000;
return r;
}
@ -412,14 +412,15 @@ struct ExtractFaWorker
facache[fakey] = make_tuple(X, Y, cell);
}
bool invert_y = f3i.inv_a ^ f3i.inv_b ^ f3i.inv_c;
if (func3.at(key).count(xor3_func)) {
SigBit YY = invert_xy ? module->NotGate(NEW_ID, Y) : Y;
SigBit YY = invert_xy ^ invert_y ? module->NotGate(NEW_ID, Y) : Y;
for (auto bit : func3.at(key).at(xor3_func))
assign_new_driver(bit, YY);
}
if (func3.at(key).count(xnor3_func)) {
SigBit YY = invert_xy ? Y : module->NotGate(NEW_ID, Y);
SigBit YY = invert_xy ^ invert_y ? Y : module->NotGate(NEW_ID, Y);
for (auto bit : func3.at(key).at(xnor3_func))
assign_new_driver(bit, YY);
}

View file

@ -133,7 +133,7 @@ struct ExtractReducePass : public Pass
else
continue;
log("Working on cell %s...\n", cell->name.c_str());
log("Working on cell %s...\n", cell->name);
// If looking for a single chain, follow linearly to the sink
pool<Cell*> sinks;
@ -220,7 +220,7 @@ struct ExtractReducePass : public Pass
//We have our list, go act on it
for(auto head_cell : sinks)
{
log(" Head cell is %s\n", head_cell->name.c_str());
log(" Head cell is %s\n", head_cell->name);
//Avoid duplication if we already were covered
if(consumed_cells.count(head_cell))

View file

@ -111,7 +111,7 @@ struct ExtractinvPass : public Pass {
RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype));
icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i));
icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]);
log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i);
log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype, log_id(module), log_id(cell->type), log_id(port.first), i);
sig[i] = SigBit(iwire, i);
}
cell->setPort(port.first, sig);

View file

@ -1,417 +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/yosys.h"
#include "kernel/utils.h"
#include "kernel/sigtools.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
IdString concat_name(RTLIL::Cell *cell, IdString object_name)
{
if (object_name[0] == '\\')
return stringf("%s.%s", cell->name.c_str(), object_name.c_str() + 1);
else {
std::string object_name_str = object_name.str();
if (object_name_str.substr(0, 8) == "$flatten")
object_name_str.erase(0, 8);
return stringf("$flatten%s.%s", cell->name.c_str(), object_name_str.c_str());
}
}
template<class T>
IdString map_name(RTLIL::Cell *cell, T *object)
{
return cell->module->uniquify(concat_name(cell, object->name));
}
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
{
vector<SigChunk> chunks = sig;
for (auto &chunk : chunks)
if (chunk.wire != nullptr && chunk.wire->module != into)
chunk.wire = map.at(chunk.wire);
sig = chunks;
}
struct FlattenWorker
{
bool ignore_wb = false;
bool create_scopeinfo = true;
bool create_scopename = false;
template<class T>
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
{
if (!create_scopeinfo && object->has_attribute(ID::src))
object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
// Preserve original names via the hdlname attribute, but only for objects with a fully public name.
// If the '-scopename' option is used, also preserve the containing scope of private objects if their scope is fully public.
if (cell->name[0] == '\\') {
if (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\') {
std::string new_hdlname;
if (cell->has_attribute(ID::hdlname)) {
new_hdlname = cell->get_string_attribute(ID(hdlname));
} else {
log_assert(!cell->name.empty());
new_hdlname = cell->name.c_str() + 1;
}
new_hdlname += ' ';
if (object->has_attribute(ID::hdlname)) {
new_hdlname += object->get_string_attribute(ID(hdlname));
} else {
log_assert(!orig_object_name.empty());
new_hdlname += orig_object_name.c_str() + 1;
}
object->set_string_attribute(ID(hdlname), new_hdlname);
} else if (object->has_attribute(ID(scopename))) {
std::string new_scopename;
if (cell->has_attribute(ID::hdlname)) {
new_scopename = cell->get_string_attribute(ID(hdlname));
} else {
log_assert(!cell->name.empty());
new_scopename = cell->name.c_str() + 1;
}
new_scopename += ' ';
new_scopename += object->get_string_attribute(ID(scopename));
object->set_string_attribute(ID(scopename), new_scopename);
} else if (create_scopename) {
log_assert(!cell->name.empty());
object->set_string_attribute(ID(scopename), cell->name.c_str() + 1);
}
}
}
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells)
{
// Copy the contents of the flattened cell
dict<IdString, IdString> memory_map;
for (auto &tpl_memory_it : tpl->memories) {
RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second), tpl_memory_it.second);
map_attributes(cell, new_memory, tpl_memory_it.second->name);
memory_map[tpl_memory_it.first] = new_memory->name;
design->select(module, new_memory);
}
dict<RTLIL::Wire*, RTLIL::Wire*> wire_map;
dict<IdString, IdString> positional_ports;
for (auto tpl_wire : tpl->wires()) {
if (tpl_wire->port_id > 0)
positional_ports.emplace(stringf("$%d", tpl_wire->port_id), tpl_wire->name);
RTLIL::Wire *new_wire = nullptr;
if (tpl_wire->name[0] == '\\') {
RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name));
if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) {
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));
hier_wire->width = GetSize(tpl_wire);
}
new_wire = hier_wire;
}
}
if (new_wire == nullptr) {
new_wire = module->addWire(map_name(cell, tpl_wire), tpl_wire);
new_wire->port_input = new_wire->port_output = false;
new_wire->port_id = false;
}
map_attributes(cell, new_wire, tpl_wire->name);
wire_map[tpl_wire] = new_wire;
design->select(module, new_wire);
}
for (auto &tpl_proc_it : tpl->processes) {
RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second), tpl_proc_it.second);
map_attributes(cell, new_proc, tpl_proc_it.second->name);
for (auto new_proc_sync : new_proc->syncs)
for (auto &memwr_action : new_proc_sync->mem_write_actions)
memwr_action.memid = memory_map.at(memwr_action.memid).str();
auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); };
new_proc->rewrite_sigspecs(rewriter);
design->select(module, new_proc);
}
for (auto tpl_cell : tpl->cells()) {
RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell), tpl_cell);
map_attributes(cell, new_cell, tpl_cell->name);
if (new_cell->has_memid()) {
IdString memid = new_cell->getParam(ID::MEMID).decode_string();
new_cell->setParam(ID::MEMID, Const(memory_map.at(memid).str()));
} else if (new_cell->is_mem_cell()) {
IdString memid = new_cell->getParam(ID::MEMID).decode_string();
new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid).str()));
}
auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); };
new_cell->rewrite_sigspecs(rewriter);
design->select(module, new_cell);
new_cells.push_back(new_cell);
}
for (auto &tpl_conn_it : tpl->connections()) {
RTLIL::SigSig new_conn = tpl_conn_it;
map_sigspec(wire_map, new_conn.first);
map_sigspec(wire_map, new_conn.second);
module->connect(new_conn);
}
// Attach port connections of the flattened cell
pool<SigBit> tpl_driven;
for (auto tpl_cell : tpl->cells())
for (auto &tpl_conn : tpl_cell->connections())
if (tpl_cell->output(tpl_conn.first))
for (auto bit : tpl_conn.second)
tpl_driven.insert(bit);
for (auto &tpl_conn : tpl->connections())
for (auto bit : tpl_conn.first)
tpl_driven.insert(bit);
for (auto &port_it : cell->connections())
{
IdString port_name = port_it.first;
if (positional_ports.count(port_name) > 0)
port_name = positional_ports.at(port_name);
if (tpl->wire(port_name) == nullptr || tpl->wire(port_name)->port_id == 0) {
if (port_name.begins_with("$"))
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n",
port_name.c_str(), cell->name.c_str(), tpl->name.c_str());
continue;
}
if (GetSize(port_it.second) == 0)
continue;
RTLIL::Wire *tpl_wire = tpl->wire(port_name);
RTLIL::SigSig new_conn;
bool is_signed = false;
if (tpl_wire->port_output && !tpl_wire->port_input) {
new_conn.first = port_it.second;
new_conn.second = tpl_wire;
is_signed = tpl_wire->is_signed;
} else if (!tpl_wire->port_output && tpl_wire->port_input) {
new_conn.first = tpl_wire;
new_conn.second = port_it.second;
is_signed = new_conn.second.is_wire() && new_conn.second.as_wire()->is_signed;
} else {
SigSpec sig_tpl = tpl_wire, sig_mod = port_it.second;
for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
if (tpl_driven.count(sig_tpl[i])) {
new_conn.first.append(sig_mod[i]);
new_conn.second.append(sig_tpl[i]);
} else {
new_conn.first.append(sig_tpl[i]);
new_conn.second.append(sig_mod[i]);
}
}
}
map_sigspec(wire_map, new_conn.first, module);
map_sigspec(wire_map, new_conn.second, module);
if (new_conn.second.size() > new_conn.first.size())
new_conn.second.remove(new_conn.first.size(), new_conn.second.size() - new_conn.first.size());
if (new_conn.second.size() < new_conn.first.size())
new_conn.second.extend_u0(new_conn.first.size(), is_signed);
log_assert(new_conn.first.size() == new_conn.second.size());
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->connect(new_conn);
sigmap.add(new_conn.first, new_conn.second);
}
RTLIL::Cell *scopeinfo = nullptr;
RTLIL::IdString cell_name = cell->name;
if (create_scopeinfo && cell_name.isPublic())
{
// The $scopeinfo's name will be changed below after removing the flattened cell
scopeinfo = module->addCell(NEW_ID, ID($scopeinfo));
scopeinfo->setParam(ID::TYPE, RTLIL::Const("module"));
for (auto const &attr : cell->attributes)
{
if (attr.first == ID::hdlname)
scopeinfo->attributes.insert(attr);
else
scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
}
for (auto const &attr : tpl->attributes)
scopeinfo->attributes.emplace(stringf("\\module_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
scopeinfo->attributes.emplace(ID(module), RTLIL::unescape_id(tpl->name));
}
module->remove(cell);
if (scopeinfo != nullptr)
module->rename(scopeinfo, cell_name);
}
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
{
if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb))
return;
SigMap sigmap(module);
std::vector<RTLIL::Cell*> worklist = module->selected_cells();
while (!worklist.empty())
{
RTLIL::Cell *cell = worklist.back();
worklist.pop_back();
if (!design->has(cell->type))
continue;
RTLIL::Module *tpl = design->module(cell->type);
if (tpl->get_blackbox_attribute(ignore_wb))
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));
used_modules.insert(tpl);
continue;
}
log_debug("Flattening %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
// 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.
flatten_cell(design, module, cell, tpl, sigmap, worklist);
}
}
};
struct FlattenPass : public Pass {
FlattenPass() : Pass("flatten", "flatten design") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" flatten [options] [selection]\n");
log("\n");
log("This pass flattens the design by replacing cells by their implementation. This\n");
log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
log("pass is using the current design as mapping library.\n");
log("\n");
log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
log("flattened by this command.\n");
log("\n");
log(" -wb\n");
log(" Ignore the 'whitebox' attribute on cell implementations.\n");
log("\n");
log(" -noscopeinfo\n");
log(" Do not create '$scopeinfo' cells that preserve attributes of cells and\n");
log(" modules that were removed during flattening. With this option, the\n");
log(" 'src' attribute of a given cell is merged into all objects replacing\n");
log(" that cell, with multiple distinct 'src' locations separated by '|'.\n");
log(" Without this option these 'src' locations can be found via the\n");
log(" cell_src' and 'module_src' attribute of '$scopeinfo' cells.\n");
log("\n");
log(" -scopename\n");
log(" Create 'scopename' attributes for objects with a private name. This\n");
log(" attribute records the 'hdlname' of the enclosing scope. For objects\n");
log(" with a public name the enclosing scope can be found via their\n");
log(" 'hdlname' attribute.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing FLATTEN pass (flatten design).\n");
log_push();
FlattenWorker worker;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-wb") {
worker.ignore_wb = true;
continue;
}
if (args[argidx] == "-noscopeinfo") {
worker.create_scopeinfo = false;
continue;
}
if (args[argidx] == "-scopename") {
worker.create_scopename = true;
continue;
}
break;
}
extra_args(args, argidx, design);
RTLIL::Module *top = nullptr;
if (design->full_selection())
for (auto module : design->modules())
if (module->get_bool_attribute(ID::top))
top = module;
pool<RTLIL::Module*> used_modules;
if (top == nullptr)
used_modules = design->modules();
else
used_modules.insert(top);
TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules;
pool<RTLIL::Module*> worklist = used_modules;
while (!worklist.empty()) {
RTLIL::Module *module = worklist.pop();
for (auto cell : module->selected_cells()) {
RTLIL::Module *tpl = design->module(cell->type);
if (tpl != nullptr) {
if (!topo_modules.has_node(tpl))
worklist.insert(tpl);
topo_modules.edge(tpl, module);
}
}
}
if (!topo_modules.sort())
log_error("Cannot flatten a design containing recursive instantiations.\n");
for (auto module : topo_modules.sorted)
worker.flatten_module(design, module, used_modules);
if (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));
design->remove(module);
}
log_pop();
}
} FlattenPass;
PRIVATE_NAMESPACE_END

View file

@ -250,9 +250,11 @@ struct FlowGraph
{
return !(*this == other);
}
unsigned int hash() const
[[nodiscard]] Hasher hash_into(Hasher h) const
{
return hash_ops<pair<RTLIL::SigBit, int>>::hash({node, is_bottom});
std::pair<RTLIL::SigBit, int> p = {node, is_bottom};
h.eat(p);
return h;
}
static NodePrime top(RTLIL::SigBit node)
@ -596,7 +598,7 @@ struct FlowmapWorker
continue;
if (!cell->known())
log_error("Cell %s (%s.%s) is unknown.\n", cell->type.c_str(), log_id(module), log_id(cell));
log_error("Cell %s (%s.%s) is unknown.\n", cell->type, log_id(module), log_id(cell));
pool<RTLIL::SigBit> fanout;
for (auto conn : cell->connections())
@ -1399,7 +1401,7 @@ struct FlowmapWorker
log_signal(node), log_signal(undef), env.c_str());
}
lut_table[i] = value.as_bool() ? State::S1 : State::S0;
lut_table.set(i, value.as_bool() ? State::S1 : State::S0);
ce.pop();
}

View file

@ -324,7 +324,7 @@ struct IopadmapPass : public Pass {
if (wire->port_input)
{
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype);
Cell *cell = module->addCell(
module->uniquify(stringf("$iopadmap$%s.%s[%d]", log_id(module), log_id(wire), i)),
@ -348,7 +348,7 @@ struct IopadmapPass : public Pass {
if (!tinoutpad_portname_pad.empty())
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad));
} else {
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype);
Cell *cell = module->addCell(
module->uniquify(stringf("$iopadmap$%s.%s[%d]", log_id(module), log_id(wire), i)),
@ -421,7 +421,7 @@ struct IopadmapPass : public Pass {
continue;
}
log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype.c_str());
log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype);
if (flag_bits)
{

149
passes/techmap/libcache.cc Normal file
View file

@ -0,0 +1,149 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2025 Jannis Harder <jix@yosyshq.com> <me@jix.one>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "passes/techmap/libparse.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct LibcachePass : public Pass {
LibcachePass() : Pass("libcache", "control caching of technology library data parsed from liberty files") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" libcache {-enable|-disable|-purge} { -all | [path]... }\n");
log("\n");
log("Controls the default and per path caching of liberty file data.\n");
log("\n");
log(" -enable Enable caching.\n");
log(" -disable Disable caching.\n");
log(" -purge Reset cache setting and forget cached data.\n");
log("\n");
log("This mode takes a list of paths as argument. If no paths are provided, this\n");
log("command does nothing. The -all option can be used to change the default cache\n");
log("setting for -enable/-disable or to reset and forget about all paths.\n");
log("\n");
log("By default caching is disabled.\n");
log("\n");
log(" libcache -list\n");
log("\n");
log("Displays the current cache settings and cached paths.\n");
log("\n");
log(" libcache {-verbose|-quiet}\n");
log("\n");
log("Controls cache use logging.\n");
log("\n");
log(" -verbose Enable printing info when cache is used\n");
log(" -quiet Disable printing info when cache is used (default)\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *) override
{
bool enable = false;
bool disable = false;
bool purge = false;
bool all = false;
bool list = false;
bool verbose = false;
bool quiet = false;
std::vector<std::string> paths;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-enable") {
enable = true;
continue;
}
if (args[argidx] == "-disable") {
enable = true;
continue;
}
if (args[argidx] == "-purge") {
purge = true;
continue;
}
if (args[argidx] == "-all") {
all = true;
continue;
}
if (args[argidx] == "-list") {
list = true;
continue;
}
if (args[argidx] == "-verbose") {
verbose = true;
continue;
}
if (args[argidx] == "-quiet") {
quiet = true;
continue;
}
append_globbed(paths, args[argidx]);
break;
}
int modes = enable + disable + purge + list + verbose + quiet;
if (modes == 0)
log_cmd_error("At least one of -enable, -disable, -purge, -list,\n-verbose, or -quiet is required.\n");
if (modes > 1)
log_cmd_error("Only one of -enable, -disable, -purge, -list,\n-verbose, or -quiet may be present.\n");
if (all && !paths.empty())
log_cmd_error("The -all option cannot be combined with a list of paths.\n");
if (list && (all || !paths.empty()))
log_cmd_error("The -list mode takes no further options.\n");
if (!list && !all && paths.empty())
log("No paths specified, use -all to %s\n", purge ? "purge all paths" : "change the default setting");
if (list) {
log("Caching is %s by default.\n", LibertyAstCache::instance.cache_by_default ? "enabled" : "disabled");
for (auto const &entry : LibertyAstCache::instance.cache_path)
log("Caching is %s for `%s'.\n", entry.second ? "enabled" : "disabled", entry.first);
for (auto const &entry : LibertyAstCache::instance.cached)
log("Data for `%s' is currently cached.\n", entry.first);
} else if (enable || disable) {
if (all) {
LibertyAstCache::instance.cache_by_default = enable;
} else {
for (auto const &path : paths) {
LibertyAstCache::instance.cache_path[path] = enable;
}
}
} else if (purge) {
if (all) {
LibertyAstCache::instance.cached.clear();
LibertyAstCache::instance.cache_path.clear();
} else {
for (auto const &path : paths) {
LibertyAstCache::instance.cached.erase(path);
LibertyAstCache::instance.cache_path.erase(path);
}
}
} else if (verbose) {
LibertyAstCache::instance.verbose = true;
} else if (quiet) {
LibertyAstCache::instance.verbose = false;
} else {
log_assert(false);
}
}
} LibcachePass;
PRIVATE_NAMESPACE_END

File diff suppressed because it is too large Load diff

View file

@ -20,11 +20,17 @@
#ifndef LIBPARSE_H
#define LIBPARSE_H
#include "kernel/yosys.h"
#include <stdio.h>
#include <string>
#include <vector>
#include <set>
/**
* This file is likely to change in the near future.
* Rely on it in your plugins at your own peril
*/
namespace Yosys
{
struct LibertyAst
@ -33,32 +39,201 @@ namespace Yosys
std::vector<std::string> args;
std::vector<LibertyAst*> children;
~LibertyAst();
LibertyAst *find(std::string name);
void dump(FILE *f, std::string indent = "", std::string path = "", bool path_ok = false);
static std::set<std::string> blacklist;
static std::set<std::string> whitelist;
const LibertyAst *find(std::string name) const;
typedef std::set<std::string> sieve;
void dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string indent = "", std::string path = "", bool path_ok = false) const;
};
struct LibertyParser
struct LibertyExpression
{
std::istream &f;
int line;
LibertyAst *ast;
LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {}
~LibertyParser() { if (ast) delete ast; }
/* lexer return values:
'v': identifier, string, array range [...] -> str holds the token string
'n': newline
anything else is a single character.
*/
int lexer(std::string &str);
LibertyAst *parse();
void error();
void error(const std::string &str);
struct Lexer {
std::string s, expr;
Lexer(std::string s) : s{s}, expr{s} {}
bool empty() { return s.empty();}
char peek() { return s[0]; }
std::string full_expr() { return expr; }
char next() {
char c = s[0];
s = s.substr(1, s.size());
return c;
}
std::string pin() {
auto length = s.find_first_of("\t()'!^*& +|\"");
if (length == std::string::npos) {
// nothing found so use size of s
length = s.size();
}
auto pin = s.substr(0, length);
s = s.substr(length, s.size());
return pin;
}
};
enum Kind {
AND,
OR,
NOT,
XOR,
// the standard specifies constants, but they're probably rare in practice.
PIN,
EMPTY
};
Kind kind;
std::string name;
std::vector<LibertyExpression> children;
LibertyExpression() : kind(Kind::EMPTY) {}
static LibertyExpression parse(Lexer &s, int min_prio = 0);
void get_pin_names(std::unordered_set<std::string>& names);
bool eval(std::unordered_map<std::string, bool>& values);
std::string sexpr_str(int indent = 0);
std::string vlog_str();
private:
static bool char_is_nice_binop(char c);
bool is_binop();
};
class LibertyInputStream {
std::istream &f;
std::vector<unsigned char> buffer;
size_t buf_pos = 0;
size_t buf_end = 0;
bool eof = false;
bool extend_buffer_once();
bool extend_buffer_at_least(size_t size = 1);
YS_COLD int get_cold();
YS_COLD int peek_cold(size_t offset);
public:
LibertyInputStream(std::istream &f) : f(f) {}
size_t buffered_size() { return buf_end - buf_pos; }
const unsigned char *buffered_data() { return buffer.data() + buf_pos; }
int get() {
if (buf_pos == buf_end)
return get_cold();
int c = buffer[buf_pos];
buf_pos += 1;
return c;
}
int peek(size_t offset = 0) {
if (buf_pos + offset >= buf_end)
return peek_cold(offset);
return buffer[buf_pos + offset];
}
void consume(size_t n = 1) {
buf_pos += n;
}
void unget() {
buf_pos -= 1;
}
};
#ifndef FILTERLIB
class LibertyAstCache {
LibertyAstCache() {};
~LibertyAstCache() {};
public:
dict<std::string, std::shared_ptr<const LibertyAst>> cached;
bool cache_by_default = false;
bool verbose = false;
dict<std::string, bool> cache_path;
std::shared_ptr<const LibertyAst> cached_ast(const std::string &fname);
void parsed_ast(const std::string &fname, const std::shared_ptr<const LibertyAst> &ast);
static LibertyAstCache instance;
};
#endif
class LibertyMergedCells;
class LibertyParser
{
friend class LibertyMergedCells;
private:
LibertyInputStream f;
int line;
/* lexer return values:
'v': identifier, string, array range [...] -> str holds the token string
'n': newline
anything else is a single character.
*/
int lexer_inner(std::string &str);
int lexer(std::string &str);
void report_unexpected_token(int tok);
void parse_vector_range(int tok);
int consume_wrecked_str(int tok, std::string& out_str);
LibertyAst *parse(bool top_level);
void error() const;
void error(const std::string &str) const;
public:
std::shared_ptr<const LibertyAst> shared_ast;
const LibertyAst *ast = nullptr;
LibertyParser(std::istream &f) : f(f), line(1) {
shared_ast.reset(parse(true));
ast = shared_ast.get();
if (!ast) {
#ifdef FILTERLIB
fprintf(stderr, "No entries found in liberty file.\n");
exit(1);
#else
log_error("No entries found in liberty file.\n");
#endif
}
}
#ifndef FILTERLIB
LibertyParser(std::istream &f, const std::string &fname) : f(f), line(1) {
shared_ast = LibertyAstCache::instance.cached_ast(fname);
if (!shared_ast) {
shared_ast.reset(parse(true));
LibertyAstCache::instance.parsed_ast(fname, shared_ast);
}
ast = shared_ast.get();
if (!ast) {
log_error("No entries found in liberty file `%s'.\n", fname);
}
}
#endif
};
class LibertyMergedCells
{
std::vector<std::shared_ptr<const LibertyAst>> asts;
public:
std::vector<const LibertyAst *> cells;
void merge(LibertyParser &parser)
{
if (parser.ast) {
const LibertyAst *ast = parser.ast;
asts.push_back(parser.shared_ast);
if (ast->id != "library")
parser.error("Top level entity isn't \"library\".\n");
for (const LibertyAst *cell : ast->children)
if (cell->id == "cell" && cell->args.size() == 1)
cells.push_back(cell);
}
}
};
}
#endif

View file

@ -0,0 +1,58 @@
/*
* 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/yosys.h"
#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct Lut2BmuxPass : public Pass {
Lut2BmuxPass() : Pass("lut2bmux", "convert $lut to $bmux") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" lut2bmux [options] [selection]\n");
log("\n");
log("This pass converts $lut cells to $bmux cells.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing LUT2BMUX pass (convert $lut to $bmux).\n");
size_t argidx = 1;
extra_args(args, argidx, design);
for (auto module : design->selected_modules())
for (auto cell : module->selected_cells()) {
if (cell->type == ID($lut)) {
cell->type = ID($bmux);
cell->setPort(ID::S, cell->getPort(ID::A));
cell->setPort(ID::A, cell->getParam(ID::LUT));
cell->unsetParam(ID::LUT);
cell->fixup_parameters();
log("Converted %s.%s to BMUX cell.\n", log_id(module), log_id(cell));
}
}
}
} Lut2BmuxPass;
PRIVATE_NAMESPACE_END

View file

@ -278,38 +278,42 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
return;
}
for (auto &port : macc.ports)
if (GetSize(port.in_b) == 0)
log(" %s %s (%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a),
GetSize(port.in_a), port.is_signed ? "signed" : "unsigned");
for (auto &term : macc.terms)
if (GetSize(term.in_b) == 0)
log(" %s %s (%d bits, %s)\n", term.do_subtract ? "sub" : "add", log_signal(term.in_a),
GetSize(term.in_a), term.is_signed ? "signed" : "unsigned");
else
log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b),
GetSize(port.in_a), GetSize(port.in_b), port.is_signed ? "signed" : "unsigned");
if (GetSize(macc.bit_ports) != 0)
log(" add bits %s (%d bits)\n", log_signal(macc.bit_ports), GetSize(macc.bit_ports));
log(" %s %s * %s (%dx%d bits, %s)\n", term.do_subtract ? "sub" : "add", log_signal(term.in_a), log_signal(term.in_b),
GetSize(term.in_a), GetSize(term.in_b), term.is_signed ? "signed" : "unsigned");
if (unmap)
{
typedef std::pair<RTLIL::SigSpec, bool> summand_t;
std::vector<summand_t> summands;
for (auto &port : macc.ports) {
RTLIL::SigSpec bit_terms;
for (auto &term : macc.terms) {
summand_t this_summand;
if (GetSize(port.in_b)) {
if (GetSize(term.in_b)) {
this_summand.first = module->addWire(NEW_ID, width);
module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed);
} else if (GetSize(port.in_a) != width) {
module->addMul(NEW_ID, term.in_a, term.in_b, this_summand.first, term.is_signed);
} else if (GetSize(term.in_a) == 1 && GetSize(term.in_b) == 0 && !term.is_signed && !term.do_subtract) {
// Mimic old 'bit_terms' treatment in case it's relevant for performance,
// i.e. defer single-bit summands to be the last ones
bit_terms.append(term.in_a);
continue;
} else if (GetSize(term.in_a) != width) {
this_summand.first = module->addWire(NEW_ID, width);
module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed);
module->addPos(NEW_ID, term.in_a, this_summand.first, term.is_signed);
} else {
this_summand.first = port.in_a;
this_summand.first = term.in_a;
}
this_summand.second = port.do_subtract;
this_summand.second = term.do_subtract;
summands.push_back(this_summand);
}
for (auto &bit : macc.bit_ports)
for (auto &bit : bit_terms)
summands.push_back(summand_t(bit, false));
if (GetSize(summands) == 0)
@ -346,14 +350,20 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
else
{
MaccmapWorker worker(module, width);
RTLIL::SigSpec bit_terms;
for (auto &port : macc.ports)
if (GetSize(port.in_b) == 0)
worker.add(port.in_a, port.is_signed, port.do_subtract);
for (auto &term : macc.terms) {
// Mimic old 'bit_terms' treatment in case it's relevant for performance,
// i.e. defer single-bit summands to be the last ones
if (GetSize(term.in_a) == 1 && GetSize(term.in_b) == 0 && !term.is_signed && !term.do_subtract)
bit_terms.append(term.in_a);
else if (GetSize(term.in_b) == 0)
worker.add(term.in_a, term.is_signed, term.do_subtract);
else
worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract);
worker.add(term.in_a, term.in_b, term.is_signed, term.do_subtract);
}
for (auto &bit : macc.bit_ports)
for (auto bit : bit_terms)
worker.add(bit, 0);
module->connect(cell->getPort(ID::Y), worker.synth());
@ -393,7 +403,7 @@ struct MaccmapPass : public Pass {
for (auto mod : design->selected_modules())
for (auto cell : mod->selected_cells())
if (cell->type == ID($macc)) {
if (cell->type.in(ID($macc), ID($macc_v2))) {
log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type));
maccmap(mod, cell, unmap_mode);
mod->remove(cell);

View file

@ -42,7 +42,7 @@ struct NlutmapWorker
RTLIL::Selection get_selection()
{
RTLIL::Selection sel(false);
auto sel = RTLIL::Selection::EmptySelection(module->design);
for (auto cell : module->cells())
if (!mapped_cells.count(cell))
sel.select(module, cell);

View file

@ -27,6 +27,14 @@
USING_YOSYS_NAMESPACE
YOSYS_NAMESPACE_BEGIN
static void transfer_attr (Cell* to, const Cell* from, IdString attr) {
if (from->has_attribute(attr))
to->attributes[attr] = from->attributes.at(attr);
}
static void transfer_src (Cell* to, const Cell* from) {
transfer_attr(to, from, ID::src);
}
void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
@ -36,12 +44,35 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::Y, sig_y[i]);
}
}
void simplemap_buf(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
if (sig_a.has_const(State::Sz)) {
SigSpec new_a;
SigSpec new_y;
for (int i = 0; i < GetSize(sig_a); ++i) {
SigBit b = sig_a[i];
if (b == State::Sz)
continue;
new_a.append(b);
new_y.append(sig_y[i]);
}
sig_a = std::move(new_a);
sig_y = std::move(new_y);
}
if (!sig_y.empty())
module->connect(RTLIL::SigSig(sig_y, sig_a));
}
void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
@ -73,7 +104,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::Y, sig_y[i]);
@ -124,7 +155,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_a[i+1]);
gate->setPort(ID::Y, sig_t[i/2]);
@ -137,7 +168,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
if (cell->type == ID($reduce_xnor)) {
RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a);
gate->setPort(ID::Y, sig_t);
last_output_cell = gate;
@ -165,7 +196,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell
}
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_OR_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig[i]);
gate->setPort(ID::B, sig[i+1]);
gate->setPort(ID::Y, sig_t[i/2]);
@ -194,7 +225,7 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a);
gate->setPort(ID::Y, sig_y);
}
@ -223,7 +254,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
log_assert(!gate_type.empty());
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a);
gate->setPort(ID::B, sig_b);
gate->setPort(ID::Y, sig_y);
@ -239,20 +270,20 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b)));
RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed);
xor_cell->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(xor_cell, cell);
simplemap_bitop(module, xor_cell);
module->remove(xor_cell);
RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out);
reduce_cell->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(reduce_cell, cell);
simplemap_reduce(module, reduce_cell);
module->remove(reduce_cell);
if (!is_ne) {
RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y);
not_cell->attributes[ID::src] = cell->attributes[ID::src];
simplemap_lognot(module, not_cell);
transfer_src(not_cell, cell);
simplemap_lognot(module, not_cell);
module->remove(not_cell);
}
}
@ -265,7 +296,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::S, cell->getPort(ID::S));
@ -282,7 +313,7 @@ void simplemap_bwmux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::S, sig_s[i]);
@ -298,7 +329,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_TBUF_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::E, sig_e);
gate->setPort(ID::Y, sig_y[i]);
@ -316,7 +347,7 @@ void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(new_data); i += width) {
for (int k = 0; k < width; k++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, data[i*2+k]);
gate->setPort(ID::B, data[i*2+width+k]);
gate->setPort(ID::S, sel[idx]);
@ -339,7 +370,7 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
for (int i = 0; i < GetSize(lut_data); i += 2) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->attributes[ID::src] = cell->attributes[ID::src];
transfer_src(gate, cell);
gate->setPort(ID::A, lut_data[i]);
gate->setPort(ID::B, lut_data[i+1]);
gate->setPort(ID::S, lut_ctrl[idx]);
@ -411,6 +442,7 @@ void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)>
{
mappers[ID($not)] = simplemap_not;
mappers[ID($pos)] = simplemap_pos;
mappers[ID($buf)] = simplemap_buf;
mappers[ID($and)] = simplemap_bitop;
mappers[ID($or)] = simplemap_bitop;
mappers[ID($xor)] = simplemap_bitop;

View file

@ -42,9 +42,9 @@ PRIVATE_NAMESPACE_BEGIN
void apply_prefix(IdString prefix, IdString &id)
{
if (id[0] == '\\')
id = stringf("%s.%s", prefix.c_str(), id.c_str()+1);
id = stringf("%s.%s", prefix, id.c_str()+1);
else
id = stringf("$techmap%s.%s", prefix.c_str(), id.c_str());
id = stringf("$techmap%s.%s", prefix, id);
}
void apply_prefix(IdString prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
@ -107,7 +107,7 @@ struct TechmapWorker
}
}
return stringf("$paramod$constmap:%s%s", sha1(constmap_info).c_str(), tpl->name.c_str());
return stringf("$paramod$constmap:%s%s", sha1(constmap_info), tpl->name);
}
TechmapWires techmap_find_special_wires(RTLIL::Module *module)
@ -222,7 +222,7 @@ struct TechmapWorker
design->select(module, w);
if (const char *p = strstr(tpl_w->name.c_str(), "_TECHMAP_REPLACE_.")) {
IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_"));
IdString replace_name = stringf("%s%s", orig_cell_name, p + strlen("_TECHMAP_REPLACE_"));
Wire *replace_w = module->addWire(replace_name, tpl_w);
module->connect(replace_w, w);
}
@ -247,7 +247,7 @@ struct TechmapWorker
portname = positional_ports.at(portname);
if (tpl->wire(portname) == nullptr || tpl->wire(portname)->port_id == 0) {
if (portname.begins_with("$"))
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname, cell->name, tpl->name);
continue;
}
@ -327,13 +327,18 @@ struct TechmapWorker
if (techmap_replace_cell)
c_name = orig_cell_name;
else if (const char *p = strstr(tpl_cell->name.c_str(), "_TECHMAP_REPLACE_."))
c_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_"));
c_name = stringf("%s%s", orig_cell_name, p + strlen("_TECHMAP_REPLACE_"));
else
apply_prefix(cell->name, c_name);
RTLIL::Cell *c = module->addCell(c_name, tpl_cell);
design->select(module, c);
if (c->type == ID::_TECHMAP_PLACEHOLDER_ && tpl_cell->has_attribute(ID::techmap_chtype)) {
c->type = RTLIL::escape_id(tpl_cell->get_string_attribute(ID::techmap_chtype));
c->attributes.erase(ID::techmap_chtype);
}
vector<IdString> autopurge_ports;
for (auto &conn : c->connections())
@ -493,7 +498,7 @@ struct TechmapWorker
{
if ((extern_mode && !in_recursion) || extmapper_name == "wrap")
{
std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type));
std::string m_name = stringf("$extern:%s:%s", extmapper_name, log_id(cell->type));
for (auto &c : cell->parameters)
m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second));
@ -535,15 +540,15 @@ struct TechmapWorker
if (extmapper_name == "maccmap") {
log("Creating %s with maccmap.\n", log_id(extmapper_module));
if (extmapper_cell->type != ID($macc))
log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type));
if (!extmapper_cell->type.in(ID($macc), ID($macc_v2)))
log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(extmapper_cell->type));
maccmap(extmapper_module, extmapper_cell);
extmapper_module->remove(extmapper_cell);
}
if (extmapper_name == "wrap") {
std::string cmd_string = tpl->attributes.at(ID::techmap_wrap).decode_string();
log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
log("Running \"%s\" on wrapper %s.\n", cmd_string, log_id(extmapper_module));
mkdebug.on();
Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
log_continue = true;
@ -561,18 +566,18 @@ struct TechmapWorker
auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type));
if (!log_msg_cache.count(msg)) {
log_msg_cache.insert(msg);
log("%s\n", msg.c_str());
log("%s\n", msg);
}
log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix, log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
}
else
{
auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type));
auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name, log_id(cell->type));
if (!log_msg_cache.count(msg)) {
log_msg_cache.insert(msg);
log("%s\n", msg.c_str());
log("%s\n", msg);
}
log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix, log_id(module), log_id(cell), log_id(cell->type), extmapper_name);
if (extmapper_name == "simplemap") {
if (simplemap_mappers.count(cell->type) == 0)
@ -581,8 +586,8 @@ struct TechmapWorker
}
if (extmapper_name == "maccmap") {
if (cell->type != ID($macc))
log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type));
if (!cell->type.in(ID($macc), ID($macc_v2)))
log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(cell->type));
maccmap(module, cell);
}
@ -661,15 +666,16 @@ struct TechmapWorker
for (auto &conn : cell->connections())
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first))) != 0) {
RTLIL::Const value;
for (auto &bit : sigmap(conn.second)) {
SigSpec sm = sigmap(conn.second);
RTLIL::Const::Builder builder(GetSize(sm) * bits);
for (auto &bit : sm) {
int val = unique_bit_id.at(bit);
for (int i = 0; i < bits; i++) {
value.bits.push_back((val & 1) != 0 ? State::S1 : State::S0);
builder.push_back((val & 1) != 0 ? State::S1 : State::S0);
val = val >> 1;
}
}
parameters.emplace(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first)), value);
parameters.emplace(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first)), builder.build());
}
}
@ -918,7 +924,7 @@ struct TechmapWorker
module_queue.insert(m);
}
log_debug("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
log_debug("%s %s.%s to imported %s.\n", mapmsg_prefix, log_id(module), log_id(cell), log_id(m_name));
cell->type = m_name;
cell->parameters.clear();
}
@ -927,9 +933,9 @@ struct TechmapWorker
auto msg = stringf("Using template %s for cells of type %s.", log_id(tpl), log_id(cell->type));
if (!log_msg_cache.count(msg)) {
log_msg_cache.insert(msg);
log("%s\n", msg.c_str());
log("%s\n", msg);
}
log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(tpl));
log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix, log_id(module), log_id(cell), log_id(cell->type), log_id(tpl));
techmap_module_worker(design, module, cell, tpl);
cell = nullptr;
}
@ -1004,6 +1010,13 @@ struct TechmapPass : public Pass {
log(" map file. Note that the Verilog frontend is also called with the\n");
log(" '-nooverwrite' option set.\n");
log("\n");
log(" -dont_map <celltype>\n");
log(" leave the given cell type unmapped by ignoring any mapping rules for it\n");
log("\n");
log(" -relativeshare\n");
log(" use paths relative to share directory for source locations\n");
log(" where possible (experimental).\n");
log("\n");
log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n");
log("match cells with a type that match the text value of this attribute. Otherwise\n");
log("the module name will be used to match the cell. Multiple space-separated cell\n");
@ -1022,8 +1035,8 @@ struct TechmapPass : public Pass {
log("\n");
log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n");
log("set, and that port is not connected in the instantiation that is mapped, then\n");
log("then a cell port connected only to such wires will be omitted in the mapped\n");
log("version of the circuit.\n");
log("a cell port connected only to such wires will be omitted in the mapped version\n");
log("of the circuit.\n");
log("\n");
log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
@ -1116,6 +1129,10 @@ struct TechmapPass : public Pass {
log("new wire alias to be created and named as above but with the `_TECHMAP_REPLACE_'\n");
log("prefix also substituted.\n");
log("\n");
log("A cell with the type _TECHMAP_PLACEHOLDER_ in the map file will have its type\n");
log("changed to the content of the techmap_chtype attribute. This allows for choosing\n");
log("the cell type dynamically.\n");
log("\n");
log("See 'help extract' for a pass that does the opposite thing.\n");
log("\n");
log("See 'help flatten' for a pass that does flatten the design (which is\n");
@ -1131,6 +1148,7 @@ struct TechmapPass : public Pass {
simplemap_get_mappers(worker.simplemap_mappers);
std::vector<std::string> map_files;
std::vector<RTLIL::IdString> dont_map;
std::string verilog_frontend = "verilog -nooverwrite -noblackbox -icells";
int max_iter = -1;
@ -1152,6 +1170,11 @@ struct TechmapPass : public Pass {
verilog_frontend += " -I " + args[++argidx];
continue;
}
if (args[argidx] == "-relativeshare") {
verilog_frontend += " -relativeshare";
log_experimental("techmap -relativeshare");
continue;
}
if (args[argidx] == "-assert") {
worker.assert_mode = true;
continue;
@ -1172,6 +1195,10 @@ struct TechmapPass : public Pass {
worker.ignore_wb = true;
continue;
}
if (args[argidx] == "-dont_map" && argidx+1 < args.size()) {
dont_map.push_back(RTLIL::escape_id(args[++argidx]));
continue;
}
break;
}
extra_args(args, argidx, design);
@ -1198,7 +1225,7 @@ struct TechmapPass : public Pass {
dict<IdString, pool<IdString>> celltypeMap;
for (auto module : map->modules()) {
if (module->attributes.count(ID::techmap_celltype) && !module->attributes.at(ID::techmap_celltype).bits.empty()) {
if (module->attributes.count(ID::techmap_celltype) && !module->attributes.at(ID::techmap_celltype).empty()) {
char *p = strdup(module->attributes.at(ID::techmap_celltype).decode_string().c_str());
for (char *q = strtok(p, " \t\r\n"); q; q = strtok(nullptr, " \t\r\n")) {
std::vector<std::string> queue;
@ -1228,13 +1255,18 @@ struct TechmapPass : public Pass {
celltypeMap[module_name].insert(module->name);
}
}
// Erase any rules disabled with a -dont_map argument
for (auto type : dont_map)
celltypeMap.erase(type);
log_debug("Cell type mappings to use:\n");
for (auto &i : celltypeMap) {
i.second.sort(RTLIL::sort_by_id_str());
std::string maps = "";
for (auto &map : i.second)
maps += stringf(" %s", log_id(map));
log_debug(" %s:%s\n", log_id(i.first), maps.c_str());
log_debug(" %s:%s\n", log_id(i.first), maps);
}
log_debug("\n");

View file

@ -63,7 +63,7 @@ struct ZinitPass : public Pass {
for (auto cell : module->selected_cells())
{
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
if (!cell->is_builtin_ff())
continue;
FfData ff(&initvals, cell);
@ -73,10 +73,10 @@ struct ZinitPass : public Pass {
pool<int> bits;
for (int i = 0; i < ff.width; i++) {
if (ff.val_init.bits[i] == State::S1)
if (ff.val_init[i] == State::S1)
bits.insert(i);
else if (ff.val_init.bits[i] != State::S0 && all_mode)
ff.val_init.bits[i] = State::S0;
else if (ff.val_init[i] != State::S0 && all_mode)
ff.val_init.set(i, State::S0);
}
ff.flip_bits(bits);
ff.emit();