3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-05-15 06:35:34 +00:00

Merge commit 'ab316c14d2' into abc-liberty-args

This commit is contained in:
Tianji Liu 2026-05-07 18:12:49 +08:00
commit f8a50e7174
371 changed files with 15324 additions and 3587 deletions

View file

@ -55,6 +55,7 @@ OBJS += passes/techmap/extractinv.o
OBJS += passes/techmap/cellmatch.o
OBJS += passes/techmap/clockgate.o
OBJS += passes/techmap/constmap.o
OBJS += passes/techmap/arith_tree.o
endif
ifeq ($(DISABLE_SPAWN),0)

View file

@ -43,7 +43,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/ffinit.h"
#include "kernel/ff.h"
#include "kernel/cost.h"
@ -63,13 +63,19 @@
# include <fcntl.h>
# include <spawn.h>
# include <sys/wait.h>
# include <sys/stat.h>
#endif
#ifndef _WIN32
# include <unistd.h>
# include <dirent.h>
# include <sys/stat.h>
#endif
#if defined(__wasm)
#include <wasi/libc.h>
#endif
#include "frontends/blif/blifparse.h"
#include "liberty_cache.h"
#ifdef YOSYS_LINK_ABC
namespace abc {
@ -130,7 +136,6 @@ struct AbcConfig
std::string delay_target;
std::string sop_inputs;
std::string sop_products;
std::string lutin_shared;
std::vector<std::string> dont_use_cells;
bool cleanup = true;
bool keepff = false;
@ -292,6 +297,8 @@ struct RunAbcState {
bool err = false;
DeferredLogs logs;
dict<int, std::string> pi_map, po_map;
std::string abc_script;
std::string dont_use_args;
RunAbcState(const AbcConfig &config) : config(config) {}
void run(ConcurrentStack<AbcProcess> &process_pool);
@ -1016,89 +1023,95 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
module->name.c_str(), replace_tempdir(run_abc.per_run_tempdir_name, config.global_tempdir_name, run_abc.per_run_tempdir_name, config.show_tempdir).c_str());
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.per_run_tempdir_name);
run_abc.abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.per_run_tempdir_name);
if (!config.liberty_files.empty() || !config.genlib_files.empty()) {
std::string dont_use_args;
run_abc.dont_use_args = "";
for (std::string dont_use_cell : config.dont_use_cells) {
dont_use_args += stringf("-X \"%s\" ", dont_use_cell);
run_abc.dont_use_args += stringf("-X \"%s\" ", dont_use_cell);
}
bool first_lib = true;
for (std::string liberty_file : config.liberty_files) {
abc_script += stringf("read_lib %s %s %s -w \"%s\" ; ", dont_use_args, first_lib ? "" : "-m", config.abc_liberty_args, liberty_file);
first_lib = false;
std::string merged_scl = convert_liberty_files_to_merged_scl(config.liberty_files, run_abc.dont_use_args, config.exe_file);
if (!merged_scl.empty()) {
run_abc.abc_script += stringf("read_scl \"%s\" ; ", merged_scl.c_str());
} else if(!config.liberty_files.empty()) {
log_warning("ABC: Merged scl conversion failed, using liberty format\n");
bool first_lib = true;
for (std::string liberty_file : config.liberty_files) {
run_abc.abc_script += stringf("read_lib %s %s %s -w \"%s\" ; ", run_abc.dont_use_args, first_lib ? "" : "-m", config.abc_liberty_args, liberty_file);
first_lib = false;
}
}
for (std::string liberty_file : config.genlib_files)
abc_script += stringf("read_library \"%s\"; ", liberty_file);
run_abc.abc_script += stringf("read_library \"%s\"; ", liberty_file);
if (!config.constr_file.empty())
abc_script += stringf("read_constr -v \"%s\"; ", config.constr_file);
run_abc.abc_script += stringf("read_constr -v \"%s\"; ", config.constr_file);
} else
if (!config.lut_costs.empty())
abc_script += stringf("read_lut %s/lutdefs.txt; ", config.global_tempdir_name);
run_abc.abc_script += stringf("read_lut %s/lutdefs.txt; ", config.global_tempdir_name);
else
abc_script += stringf("read_library %s/stdcells.genlib; ", config.global_tempdir_name);
run_abc.abc_script += stringf("read_library %s/stdcells.genlib; ", config.global_tempdir_name);
if (!config.script_file.empty()) {
const std::string &script_file = config.script_file;
if (script_file[0] == '+') {
for (size_t i = 1; i < script_file.size(); i++)
if (script_file[i] == '\'')
abc_script += "'\\''";
run_abc.abc_script += "'\\''";
else if (script_file[i] == ',')
abc_script += " ";
run_abc.abc_script += " ";
else
abc_script += script_file[i];
run_abc.abc_script += script_file[i];
} else
abc_script += stringf("source %s", script_file);
run_abc.abc_script += stringf("source %s", script_file);
} else if (!config.lut_costs.empty()) {
bool all_luts_cost_same = true;
for (int this_cost : config.lut_costs)
if (this_cost != config.lut_costs.front())
all_luts_cost_same = false;
abc_script += config.fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
run_abc.abc_script += config.fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
if (all_luts_cost_same && !config.fast_mode)
abc_script += "; lutpack {S}";
run_abc.abc_script += "; lutpack -S 1";
} else if (!config.liberty_files.empty() || !config.genlib_files.empty())
abc_script += config.constr_file.empty() ?
run_abc.abc_script += config.constr_file.empty() ?
(config.fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (config.fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR);
else if (config.sop_mode)
abc_script += config.fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP;
run_abc.abc_script += config.fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP;
else
abc_script += config.fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
run_abc.abc_script += config.fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
if (config.script_file.empty() && !config.delay_target.empty())
for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1))
abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
for (size_t pos = run_abc.abc_script.find("dretime;"); pos != std::string::npos; pos = run_abc.abc_script.find("dretime;", pos+1))
run_abc.abc_script = run_abc.abc_script.substr(0, pos) + "dretime; retime -o {D};" + run_abc.abc_script.substr(pos+8);
for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
abc_script = abc_script.substr(0, pos) + config.delay_target + abc_script.substr(pos+3);
for (size_t pos = run_abc.abc_script.find("{D}"); pos != std::string::npos; pos = run_abc.abc_script.find("{D}", pos))
run_abc.abc_script = run_abc.abc_script.substr(0, pos) + config.delay_target + run_abc.abc_script.substr(pos+3);
for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{I}", pos))
abc_script = abc_script.substr(0, pos) + config.sop_inputs + abc_script.substr(pos+3);
for (size_t pos = run_abc.abc_script.find("{I}"); pos != std::string::npos; pos = run_abc.abc_script.find("{I}", pos))
run_abc.abc_script = run_abc.abc_script.substr(0, pos) + config.sop_inputs + run_abc.abc_script.substr(pos+3);
for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{P}", pos))
abc_script = abc_script.substr(0, pos) + config.sop_products + abc_script.substr(pos+3);
for (size_t pos = run_abc.abc_script.find("{P}"); pos != std::string::npos; pos = run_abc.abc_script.find("{P}", pos))
run_abc.abc_script = run_abc.abc_script.substr(0, pos) + config.sop_products + run_abc.abc_script.substr(pos+3);
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3);
if (config.abc_dress)
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.per_run_tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", run_abc.per_run_tempdir_name);
abc_script = add_echos_to_abc_cmd(abc_script);
run_abc.abc_script += stringf("; dress \"%s/input.blif\"", run_abc.per_run_tempdir_name);
run_abc.abc_script += stringf("; write_blif %s/output.blif", run_abc.per_run_tempdir_name);
run_abc.abc_script = add_echos_to_abc_cmd(run_abc.abc_script);
#if defined(REUSE_YOSYS_ABC_PROCESSES)
if (config.is_yosys_abc())
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
run_abc.abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
#endif
for (size_t i = 0; i+1 < abc_script.size(); i++)
if (abc_script[i] == ';' && abc_script[i+1] == ' ')
abc_script[i+1] = '\n';
for (size_t i = 0; i+1 < run_abc.abc_script.size(); i++)
if (run_abc.abc_script[i] == ';' && run_abc.abc_script[i+1] == ' ')
run_abc.abc_script[i+1] = '\n';
std::string buffer = stringf("%s/abc.script", run_abc.per_run_tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
fprintf(f, "%s\n", abc_script.c_str());
fprintf(f, "%s\n", run_abc.abc_script.c_str());
fclose(f);
if (dff_mode || !clk_str.empty())
@ -1353,7 +1366,7 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &)
logs.log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
count_gates, GetSize(signal_list), count_input, count_output);
if (count_output == 0) {
log("Don't call ABC as there is nothing to map.\n");
logs.log("Don't call ABC as there is nothing to map.\n");
return;
}
int ret;
@ -1367,13 +1380,13 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &)
string temp_stdouterr_name = stringf("%s/stdouterr.txt", per_run_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");
logs.log_error("ABC: cannot open a temporary file for output redirection");
fflush(stdout);
fflush(stderr);
FILE *old_stdout = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
FILE *old_stderr = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
#if defined(__wasm)
#define fd_renumber(from, to) (void)__wasi_fd_renumber(from, to)
#define fd_renumber(from, to) (void)__wasilibc_fd_renumber(from, to)
#else
#define fd_renumber(from, to) dup2(from, to)
#endif
@ -1412,10 +1425,10 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &)
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
process = std::move(process_opt.value());
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
process = std::move(process_opt.value());
} else {
return;
}
process = std::move(process_opt.value());
} else {
return;
}
std::string cmd = stringf(
"empty\n"
"source %s\n", tmp_script_name);
@ -1880,7 +1893,7 @@ struct AbcPass : public Pass {
log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR));
log("\n");
log(" for -lut/-luts (only one LUT size):\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack {S}"));
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack -S 1"));
log("\n");
log(" for -lut/-luts (different LUT sizes):\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT));
@ -1948,10 +1961,6 @@ struct AbcPass : public Pass {
log(" maximum number of SOP products.\n");
log(" (replaces {P} in the default scripts above)\n");
log("\n");
log(" -S <num>\n");
log(" maximum number of LUT inputs shared.\n");
log(" (replaces {S} in the default scripts above, default: -S 1)\n");
log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
@ -2066,11 +2075,6 @@ struct AbcPass : public Pass {
if (design->scratchpad.count("abc.P")) {
config.sop_products = "-P " + design->scratchpad_get_string("abc.P");
}
if (design->scratchpad.count("abc.S")) {
config.lutin_shared = "-S " + design->scratchpad_get_string("abc.S");
} else {
config.lutin_shared = "-S 1";
}
lut_arg = design->scratchpad_get_string("abc.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc.luts", luts_arg);
config.sop_mode = design->scratchpad_get_bool("abc.sop", false);
@ -2153,10 +2157,6 @@ struct AbcPass : public Pass {
config.sop_products = "-P " + args[++argidx];
continue;
}
if (arg == "-S" && argidx+1 < args.size()) {
config.lutin_shared = "-S " + args[++argidx];
continue;
}
if (arg == "-lut" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
@ -2468,7 +2468,7 @@ struct AbcPass : public Pass {
continue;
}
CellTypes ct(design);
NewCellTypes ct(design);
std::vector<RTLIL::Cell*> all_cells = mod->selected_cells();
pool<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end());

View file

@ -38,53 +38,53 @@ 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 -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";
RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {W} {D} {R} -v; &mfs";
RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {W} {D} {R} -a -v; &mfs";
RTLIL::constpad["abc9.script.default.fast"] = "+&if {W} {D} {R} -v";
// Based on ABC's &flow
RTLIL::constpad["abc9.script.flow"] = "+&scorr; &sweep;" \
"&dch -C 500;" \
/* Round 1 */ \
/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 1 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &dsdb;" \
/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 2 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &syn2 -m -R 10; &dsdb;" \
"&blut -a -K 6;" \
/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 3 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
/* Round 2 */ \
"&st; &sopb;" \
/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 1 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &dsdb;" \
/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 2 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &syn2 -m -R 10; &dsdb;" \
"&blut -a -K 6;" \
/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 3 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
/* Round 3 */ \
/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 1 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &dsdb;" \
/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 2 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &syn2 -m -R 10; &dsdb;" \
"&blut -a -K 6;" \
/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;";
/* Map 3 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;";
// Based on ABC's &flow2
RTLIL::constpad["abc9.script.flow2"] = "+&scorr; &sweep;" \
/* Comm1 */ "&synch2 -K 6 -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm1 */ "&synch2 -K 6 -C 500; &if -m {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
"&load; &st; &sopb -R 10 -C 4; " \
/* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\
/* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\
"&load";
// Based on ABC's &flow3 -m
RTLIL::constpad["abc9.script.flow3"] = "+&scorr; &sweep;" \
"&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&if {W} {D}; &save; &st; &syn2; &if {W} {D} {R} -v; &save; &load;"\
"&st; &if -g -K 6; &dch -f; &if {W} {D} {R} -v; &save; &load;"\
"&st; &if -g -K 6; &synch2; &if {W} {D} {R} -v; &save; &load;"\
"&mfs";
// As above, but with &mfs calls as in the original &flow3
RTLIL::constpad["abc9.script.flow3mfs"] = "+&scorr; &sweep;" \
"&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &mfs; &save; &load;"\
"&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &mfs; &save; &load;"\
"&if {W} {D}; &save; &st; &syn2; &if {W} {D} {R} -v; &save; &load;"\
"&st; &if -g -K 6; &dch -f; &if {W} {D} {R} -v; &mfs; &save; &load;"\
"&st; &if -g -K 6; &synch2; &if {W} {D} {R} -v; &mfs; &save; &load;"\
"&mfs";
}
void help() override
@ -121,20 +121,11 @@ struct Abc9Pass : public ScriptPass
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)));
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)));
log("\n");
log(" -D <picoseconds>\n");
log(" set delay target. the string {D} in the default scripts above is\n");
log(" replaced by this option when used, and an empty string otherwise\n");
log(" (indicating best possible delay).\n");
log("\n");
// log(" -S <num>\n");
// log(" maximum number of LUT inputs shared.\n");
// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
// log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
@ -226,8 +217,7 @@ struct Abc9Pass : public ScriptPass
exe_cmd << " " << arg << " " << args[++argidx];
continue;
}
if (arg == "-fast" || /* arg == "-dff" || */
/* arg == "-nocleanup" || */ arg == "-showtmp") {
if (arg == "-showtmp") {
exe_cmd << " " << arg;
continue;
}

View file

@ -24,11 +24,15 @@
#include "kernel/register.h"
#include "kernel/log.h"
#include "liberty_cache.h"
#ifndef _WIN32
# include <unistd.h>
# include <dirent.h>
#endif
#if defined(__wasm)
#include <wasi/libc.h>
#endif
#ifdef YOSYS_LINK_ABC
namespace abc {
@ -165,7 +169,7 @@ 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,
vector<int> lut_costs, bool dff_mode, std::string delay_target,
bool show_tempdir, std::string box_file, std::string lut_file,
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)
@ -181,11 +185,19 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
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;
std::string merged_scl = convert_liberty_files_to_merged_scl(liberty_files, dont_use_args, exe_file);
if (!merged_scl.empty()) {
abc9_script += stringf("read_scl \"%s\" ; ", merged_scl.c_str());
} else if(!liberty_files.empty()) {
log_warning("ABC: Merged scl conversion failed, using liberty format\n");
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()) {
@ -210,26 +222,18 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
} else
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);
abc9_script += RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
} else if (!liberty_files.empty() || !genlib_files.empty()) {
abc9_script += RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
} else
log_abort();
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
// abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
std::string C;
if (design->scratchpad.count("abc9.if.C"))
C = "-C " + design->scratchpad_get_string("abc9.if.C");
for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos))
abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3);
std::string R;
if (design->scratchpad.count("abc9.if.R"))
R = "-R " + design->scratchpad_get_string("abc9.if.R");
@ -295,7 +299,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
FILE *old_stdout = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
FILE *old_stderr = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
#if defined(__wasm)
#define fd_renumber(from, to) (void)__wasi_fd_renumber(from, to)
#define fd_renumber(from, to) (void)__wasilibc_fd_renumber(from, to)
#else
#define fd_renumber(from, to) dup2(from, to)
#endif
@ -369,11 +373,6 @@ struct Abc9ExePass : public Pass {
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)));
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)));
log("\n");
log(" -constr <file>\n");
log(" pass this file with timing constraints to ABC.\n");
log(" use with -liberty.\n");
@ -453,9 +452,9 @@ struct Abc9ExePass : public Pass {
std::string exe_file = yosys_abc_executable;
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 delay_target, wire_delay;
std::string tempdir_name;
bool fast_mode = false, dff_mode = false;
bool dff_mode = false;
bool show_tempdir = false;
vector<int> lut_costs;
@ -472,7 +471,6 @@ struct Abc9ExePass : public Pass {
}
lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg);
fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);
dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);
show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir);
box_file = design->scratchpad_get_string("abc9.box", box_file);
@ -504,20 +502,12 @@ struct Abc9ExePass : public Pass {
delay_target = "-D " + args[++argidx];
continue;
}
//if (arg == "-S" && argidx+1 < args.size()) {
// lutin_shared = "-S " + args[++argidx];
// continue;
//}
if (arg == "-lut" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
}
if (arg == "-luts" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
}
if (arg == "-fast") {
fast_mode = true;
luts_arg = args[++argidx];
continue;
}
if (arg == "-dff") {
@ -622,7 +612,7 @@ struct Abc9ExePass : public Pass {
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,
delay_target, show_tempdir,
box_file, lut_file, liberty_files, wire_delay, tempdir_name,
constr_file, dont_use_cells, genlib_files);
}

View file

@ -21,8 +21,9 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/utils.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/timinginfo.h"
#include <optional>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -587,6 +588,7 @@ void break_scc(RTLIL::Module *module)
auto id = it->second;
auto r = ids_seen.insert(id);
cell->attributes.erase(it);
// Cut exactly one representative cell per SCC id.
if (!r.second)
continue;
for (auto &c : cell->connections_) {
@ -710,8 +712,6 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
SigMap sigmap(module);
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
dict<IdString, std::vector<IdString>> box_ports;
for (auto cell : module->cells()) {
@ -750,39 +750,100 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
}
}
}
else if (!yosys_celltypes.cell_known(cell->type))
continue;
// TODO: Speed up toposort -- we care about box ordering only
for (auto conn : cell->connections()) {
if (cell->input(conn.first))
for (auto bit : sigmap(conn.second))
bit_users[bit].insert(cell->name);
if (cell->output(conn.first) && !abc9_flop)
for (auto bit : sigmap(conn.second))
bit_drivers[bit].insert(cell->name);
}
toposort.node(cell->name);
}
if (box_ports.empty())
return;
for (auto &it : bit_users)
if (bit_drivers.count(it.first))
for (auto driver_cell : bit_drivers.at(it.first))
for (auto user_cell : it.second)
toposort.edge(driver_cell, user_cell);
// Build the same topo graph for the initial pass and the optional retry.
auto build_toposort = [&](TopoSort<IdString, RTLIL::sort_by_id_str> &toposort) {
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
if (ys_debug(1))
toposort.analyze_loops = true;
for (auto cell : module->cells()) {
if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
continue;
if (cell->has_keep_attr())
continue;
bool no_loops = toposort.sort();
auto inst_module = design->module(cell->type);
bool abc9_flop = inst_module && inst_module->get_bool_attribute(ID::abc9_flop);
if (abc9_flop && !dff)
continue;
if (!(inst_module && inst_module->get_bool_attribute(ID::abc9_box)) && !yosys_celltypes.cell_known(cell->type))
continue;
// TODO: Speed up toposort -- we care about box ordering only
for (auto conn : cell->connections()) {
if (cell->input(conn.first))
for (auto bit : sigmap(conn.second))
bit_users[bit].insert(cell->name);
if (cell->output(conn.first) && !abc9_flop)
for (auto bit : sigmap(conn.second))
bit_drivers[bit].insert(cell->name);
}
toposort.node(cell->name);
}
// Build producer -> consumer edges on sigmapped nets.
for (auto &it : bit_users)
if (bit_drivers.count(it.first))
for (auto driver_cell : bit_drivers.at(it.first))
for (auto user_cell : it.second)
toposort.edge(driver_cell, user_cell);
if (ys_debug(1))
toposort.analyze_loops = true;
return toposort.sort();
};
// Build TopoSort in a container, as we may need to conditionally rebuild it on retry.
std::optional<TopoSort<IdString, RTLIL::sort_by_id_str>> toposort;
toposort.emplace();
bool no_loops = build_toposort(toposort.value());
// Fallback for residual loops after SCC cutting: insert additional
// breakers on non-box loop cells, then re-run toposort checks.
if (!no_loops) {
SigSpec I, O;
pool<IdString> broken_cells;
for (auto &loop : toposort.value().loops)
for (auto cell_name : loop) {
// Loop reports can overlap; cut each cell at most once.
if (!broken_cells.insert(cell_name).second)
continue;
auto cell = module->cell(cell_name);
log_assert(cell);
auto inst_module = design->module(cell->type);
if (inst_module && inst_module->get_bool_attribute(ID::abc9_box))
continue;
for (auto &c : cell->connections_) {
if (c.second.is_fully_const()) continue;
if (cell->output(c.first)) {
Wire *w = module->addWire(NEW_ID, GetSize(c.second));
I.append(w);
O.append(c.second);
c.second = w;
}
}
}
if (!I.empty()) {
auto cell = module->addCell(NEW_ID, ID($__ABC9_SCC_BREAKER));
log_assert(GetSize(I) == GetSize(O));
cell->setParam(ID::WIDTH, GetSize(I));
cell->setPort(ID::I, std::move(I));
cell->setPort(ID::O, std::move(O));
// Rebuild topo ordering after inserting the additional breakers.
toposort.emplace();
no_loops = build_toposort(toposort.value());
}
}
if (ys_debug(1)) {
unsigned i = 0;
for (auto &it : toposort.loops) {
for (auto &it : toposort.value().loops) {
log(" loop %d\n", i++);
for (auto cell_name : it) {
auto cell = module->cell(cell_name);
@ -806,7 +867,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
TimingInfo timing;
int port_id = 1, box_count = 0;
for (auto cell_name : toposort.sorted) {
for (auto cell_name : toposort.value().sorted) {
RTLIL::Cell *cell = module->cell(cell_name);
log_assert(cell);
@ -1559,7 +1620,6 @@ clone_lut:
}
}
//log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
log("ABC RESULTS: input signals: %8d\n", in_wires);
log("ABC RESULTS: output signals: %8d\n", out_wires);
@ -1592,7 +1652,7 @@ static void replace_zbufs(Design *design)
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->set_bool_attribute(ID::aiger2_zbuf);
ud->setParam(ID::WIDTH, 1);
ud->setPort(ID::Y, w);
ud->setPort(ID::EN, State::S0);

View file

@ -18,7 +18,7 @@
*/
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/yosys_common.h"
#include "kernel/utils.h"
USING_YOSYS_NAMESPACE
@ -27,7 +27,8 @@ 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;
using Order = IdString::compare_ptr_by_name<RTLIL::NamedObject>;
TopoSort<Module*, Order> sort;
for (auto m : modules) {
sort.node(m);
@ -49,6 +50,17 @@ struct AbcNewPass : public ScriptPass {
experimental();
}
void on_register() override
{
RTLIL::constpad["abc_new.script.speed"] = "+&st; &dch -r;" \
"&nf; &st; &syn2; &if -g -K 6; &synch2 -r;" \
"&nf; &st; &syn2; &if -g -K 6; &synch2 -r;" \
"&nf; &st; &syn2; &if -g -K 6; &synch2 -r;" \
"&nf; &st; &syn2; &if -g -K 6; &synch2 -r;" \
"&nf; &st; &syn2; &if -g -K 6; &synch2 -r;" \
"&nf";
}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
@ -109,6 +121,11 @@ struct AbcNewPass : public ScriptPass {
}
extra_args(args, argidx, d);
// If no script provided, use a default.
if (abc_exe_options.find("-script") == std::string::npos) {
d->scratchpad_set_string("abc9.script", RTLIL::constpad["abc_new.script.speed"]);
}
log_header(d, "Executing ABC_NEW pass.\n");
log_push();
run_script(d, run_from, run_to);

View file

@ -0,0 +1,426 @@
/**
* Replaces chains of $add/$sub and $macc cells with carry-save adder trees
*
* Terminology:
* - parent: Cells that consume another cell's output
* - chainable: Adds/subs with no carry-out usage
* - chain: Connected path of chainable cells
*/
#include "kernel/macc.h"
#include "kernel/sigtools.h"
#include "kernel/wallace_tree.h"
#include "kernel/yosys.h"
#include <queue>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct Operand {
SigSpec sig;
bool is_signed;
bool negate;
};
struct Traversal {
SigMap sigmap;
dict<SigBit, pool<Cell *>> bit_consumers;
dict<SigBit, int> fanout;
Traversal(Module *module) : sigmap(module)
{
for (auto cell : module->cells())
for (auto &conn : cell->connections())
if (cell->input(conn.first))
for (auto bit : sigmap(conn.second))
bit_consumers[bit].insert(cell);
for (auto &pair : bit_consumers)
fanout[pair.first] = pair.second.size();
for (auto wire : module->wires())
if (wire->port_output)
for (auto bit : sigmap(SigSpec(wire)))
fanout[bit]++;
}
};
struct Cells {
pool<Cell *> addsub;
pool<Cell *> alu;
pool<Cell *> macc;
static bool is_addsub(Cell *cell) { return cell->type == ID($add) || cell->type == ID($sub); }
static bool is_alu(Cell *cell) { return cell->type == ID($alu); }
static bool is_macc(Cell *cell) { return cell->type == ID($macc) || cell->type == ID($macc_v2); }
bool empty() { return addsub.empty() && alu.empty() && macc.empty(); }
Cells(Module *module)
{
for (auto cell : module->cells()) {
if (is_addsub(cell))
addsub.insert(cell);
else if (is_alu(cell))
alu.insert(cell);
else if (is_macc(cell))
macc.insert(cell);
}
}
};
struct AluInfo {
Cells &cells;
Traversal &traversal;
bool is_subtract(Cell *cell)
{
SigSpec bi = traversal.sigmap(cell->getPort(ID::BI));
SigSpec ci = traversal.sigmap(cell->getPort(ID::CI));
return GetSize(bi) == 1 && bi[0] == State::S1 && GetSize(ci) == 1 && ci[0] == State::S1;
}
bool is_add(Cell *cell)
{
SigSpec bi = traversal.sigmap(cell->getPort(ID::BI));
SigSpec ci = traversal.sigmap(cell->getPort(ID::CI));
return GetSize(bi) == 1 && bi[0] == State::S0 && GetSize(ci) == 1 && ci[0] == State::S0;
}
bool is_chainable(Cell *cell)
{
if (!(is_add(cell) || is_subtract(cell)))
return false;
for (auto bit : traversal.sigmap(cell->getPort(ID::X)))
if (traversal.fanout.count(bit) && traversal.fanout[bit] > 0)
return false;
for (auto bit : traversal.sigmap(cell->getPort(ID::CO)))
if (traversal.fanout.count(bit) && traversal.fanout[bit] > 0)
return false;
return true;
}
};
struct Rewriter {
Module *module;
Cells &cells;
Traversal traversal;
AluInfo alu_info;
Rewriter(Module *module, Cells &cells) : module(module), cells(cells), traversal(module), alu_info{cells, traversal} {}
Cell *sole_chainable_consumer(SigSpec sig, const pool<Cell *> &candidates)
{
Cell *consumer = nullptr;
for (auto bit : sig) {
if (!traversal.fanout.count(bit) || traversal.fanout[bit] != 1)
return nullptr;
if (!traversal.bit_consumers.count(bit) || traversal.bit_consumers[bit].size() != 1)
return nullptr;
Cell *c = *traversal.bit_consumers[bit].begin();
if (!candidates.count(c))
return nullptr;
if (consumer == nullptr)
consumer = c;
else if (consumer != c)
return nullptr;
}
return consumer;
}
dict<Cell *, Cell *> find_parents(const pool<Cell *> &candidates)
{
dict<Cell *, Cell *> parent_of;
for (auto cell : candidates) {
Cell *consumer = sole_chainable_consumer(traversal.sigmap(cell->getPort(ID::Y)), candidates);
if (consumer && consumer != cell)
parent_of[cell] = consumer;
}
return parent_of;
}
std::pair<dict<Cell *, pool<Cell *>>, pool<Cell *>> invert_parent_map(const dict<Cell *, Cell *> &parent_of)
{
dict<Cell *, pool<Cell *>> children_of;
pool<Cell *> has_parent;
for (auto &[child, parent] : parent_of) {
children_of[parent].insert(child);
has_parent.insert(child);
}
return {children_of, has_parent};
}
pool<Cell *> collect_chain(Cell *root, const dict<Cell *, pool<Cell *>> &children_of)
{
pool<Cell *> chain;
std::queue<Cell *> q;
q.push(root);
while (!q.empty()) {
Cell *cur = q.front();
q.pop();
if (!chain.insert(cur).second)
continue;
auto it = children_of.find(cur);
if (it != children_of.end())
for (auto child : it->second)
q.push(child);
}
return chain;
}
pool<SigBit> internal_bits(const pool<Cell *> &chain)
{
pool<SigBit> bits;
for (auto cell : chain)
for (auto bit : traversal.sigmap(cell->getPort(ID::Y)))
bits.insert(bit);
return bits;
}
static bool overlaps(SigSpec sig, const pool<SigBit> &bits)
{
for (auto bit : sig)
if (bits.count(bit))
return true;
return false;
}
bool feeds_subtracted_port(Cell *child, Cell *parent)
{
bool parent_subtracts;
if (parent->type == ID($sub))
parent_subtracts = true;
else if (cells.is_alu(parent))
parent_subtracts = alu_info.is_subtract(parent);
else
return false;
if (!parent_subtracts)
return false;
// Check if any bit of child's Y connects to parent's B
SigSpec child_y = traversal.sigmap(child->getPort(ID::Y));
SigSpec parent_b = traversal.sigmap(parent->getPort(ID::B));
for (auto bit : child_y)
for (auto pbit : parent_b)
if (bit == pbit)
return true;
return false;
}
std::vector<Operand> extract_chain_operands(const pool<Cell *> &chain, Cell *root, const dict<Cell *, Cell *> &parent_of, int &neg_compensation)
{
pool<SigBit> chain_bits = internal_bits(chain);
// Propagate negation flags through chain
dict<Cell *, bool> negated;
negated[root] = false;
{
std::queue<Cell *> q;
q.push(root);
while (!q.empty()) {
Cell *cur = q.front();
q.pop();
for (auto cell : chain) {
if (!parent_of.count(cell) || parent_of.at(cell) != cur)
continue;
if (negated.count(cell))
continue;
negated[cell] = negated[cur] ^ feeds_subtracted_port(cell, cur);
q.push(cell);
}
}
}
// Extract leaf operands
std::vector<Operand> operands;
neg_compensation = 0;
for (auto cell : chain) {
bool cell_neg = negated.count(cell) ? negated[cell] : false;
SigSpec a = traversal.sigmap(cell->getPort(ID::A));
SigSpec b = traversal.sigmap(cell->getPort(ID::B));
bool a_signed = cell->getParam(ID::A_SIGNED).as_bool();
bool b_signed = cell->getParam(ID::B_SIGNED).as_bool();
bool b_sub = (cell->type == ID($sub)) || (cells.is_alu(cell) && alu_info.is_subtract(cell));
// Only add operands not produced by other chain cells
if (!overlaps(a, chain_bits)) {
operands.push_back({a, a_signed, cell_neg});
if (cell_neg)
neg_compensation++;
}
if (!overlaps(b, chain_bits)) {
bool neg = cell_neg ^ b_sub;
operands.push_back({b, b_signed, neg});
if (neg)
neg_compensation++;
}
}
return operands;
}
bool extract_macc_operands(Cell *cell, std::vector<Operand> &operands, int &neg_compensation)
{
Macc macc(cell);
neg_compensation = 0;
for (auto &term : macc.terms) {
// Bail on multiplication
if (GetSize(term.in_b) != 0)
return false;
operands.push_back({term.in_a, term.is_signed, term.do_subtract});
if (term.do_subtract)
neg_compensation++;
}
return true;
}
SigSpec extend_operand(SigSpec sig, bool is_signed, int width)
{
if (GetSize(sig) < width) {
SigBit pad;
if (is_signed && GetSize(sig) > 0)
pad = sig[GetSize(sig) - 1];
else
pad = State::S0;
sig.append(SigSpec(pad, width - GetSize(sig)));
}
if (GetSize(sig) > width)
sig = sig.extract(0, width);
return sig;
}
void replace_with_carry_save_tree(std::vector<Operand> &operands, SigSpec result_y, int neg_compensation, const char *desc)
{
int width = GetSize(result_y);
std::vector<SigSpec> extended;
extended.reserve(operands.size() + 1);
for (auto &op : operands) {
SigSpec s = extend_operand(op.sig, op.is_signed, width);
if (op.negate)
s = module->Not(NEW_ID, s);
extended.push_back(s);
}
// Add correction for negated operands (-x = ~x + 1 so 1 per negation)
if (neg_compensation > 0)
extended.push_back(SigSpec(neg_compensation, width));
int compressor_count;
auto [a, b] = wallace_reduce_scheduled(module, extended, width, &compressor_count);
log(" %s -> %d $fa + 1 $add (%d operands, module %s)\n", desc, compressor_count, (int)operands.size(), log_id(module));
// Emit final add
module->addAdd(NEW_ID, a, b, result_y, false);
}
void process_chains()
{
pool<Cell *> candidates;
for (auto cell : cells.addsub)
candidates.insert(cell);
for (auto cell : cells.alu)
if (alu_info.is_chainable(cell))
candidates.insert(cell);
if (candidates.empty())
return;
auto parent_of = find_parents(candidates);
auto [children_of, has_parent] = invert_parent_map(parent_of);
pool<Cell *> to_remove;
for (auto root : candidates) {
if (has_parent.count(root) || to_remove.count(root))
continue; // Not a tree root
pool<Cell *> chain = collect_chain(root, children_of);
if (chain.size() < 2)
continue;
int neg_compensation;
auto operands = extract_chain_operands(chain, root, parent_of, neg_compensation);
if (operands.size() < 3)
continue;
for (auto c : chain)
to_remove.insert(c);
replace_with_carry_save_tree(operands, root->getPort(ID::Y), neg_compensation, "Replaced add/sub chain");
}
for (auto cell : to_remove)
module->remove(cell);
}
void process_maccs()
{
for (auto cell : cells.macc) {
std::vector<Operand> operands;
int neg_compensation;
if (!extract_macc_operands(cell, operands, neg_compensation))
continue;
if (operands.size() < 3)
continue;
replace_with_carry_save_tree(operands, cell->getPort(ID::Y), neg_compensation, "Replaced $macc");
module->remove(cell);
}
}
};
void run(Module *module)
{
Cells cells(module);
if (cells.empty())
return;
Rewriter rewriter{module, cells};
rewriter.process_chains();
rewriter.process_maccs();
}
struct ArithTreePass : public Pass {
ArithTreePass() : Pass("arith_tree", "convert add/sub/macc chains to carry-save adder trees") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" arith_tree [selection]\n");
log("\n");
log("This pass replaces chains of $add/$sub cells, $alu cells (with constant\n");
log("BI/CI), and $macc/$macc_v2 cells (without multiplications) with carry-save\n");
log("adder trees using $fa cells and a single final $add.\n");
log("\n");
log("The tree uses Wallace-tree scheduling: at each level, ready operands are\n");
log("grouped into triplets and compressed via full adders, giving\n");
log("O(log_{1.5} N) depth for N input operands.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing ARITH_TREE pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
break;
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
run(module);
}
}
} ArithTreePass;
PRIVATE_NAMESPACE_END

View file

@ -58,6 +58,7 @@ synth -top my_design -booth
#include "kernel/sigtools.h"
#include "kernel/yosys.h"
#include "kernel/macc.h"
#include "kernel/wallace_tree.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -317,36 +318,6 @@ struct BoothPassWorker {
}
}
SigSig WallaceSum(int width, std::vector<SigSpec> summands)
{
for (auto &s : summands)
s.extend_u0(width);
while (summands.size() > 2) {
std::vector<SigSpec> new_summands;
int i;
for (i = 0; i < (int) summands.size() - 2; i += 3) {
SigSpec x = module->addWire(NEW_ID, width);
SigSpec y = module->addWire(NEW_ID, width);
BuildBitwiseFa(module, NEW_ID.str(), summands[i], summands[i + 1],
summands[i + 2], x, y);
new_summands.push_back(y);
new_summands.push_back({x.extract(0, width - 1), State::S0});
}
new_summands.insert(new_summands.begin(), summands.begin() + i, summands.end());
std::swap(summands, new_summands);
}
if (!summands.size())
return SigSig(SigSpec(width, State::S0), SigSpec(width, State::S0));
else if (summands.size() == 1)
return SigSig(summands[0], SigSpec(width, State::S0));
else
return SigSig(summands[0], summands[1]);
}
/*
Build Multiplier.
-------------------------
@ -415,16 +386,16 @@ struct BoothPassWorker {
// Later on yosys will clean up unused constants
// DebugDumpAlignPP(aligned_pp);
SigSig wtree_sum = WallaceSum(z_sz, aligned_pp);
auto [wtree_a, wtree_b] = wallace_reduce_scheduled(module, aligned_pp, z_sz);
// Debug code: Dump out the csa trees
// DumpCSATrees(debug_csa_trees);
// Build the CPA to do the final accumulation.
log_assert(wtree_sum.second[0] == State::S0);
log_assert(wtree_b[0] == State::S0);
if (mapped_cpa)
BuildCPA(module, wtree_sum.first, {State::S0, wtree_sum.second.extract_end(1)}, Z);
BuildCPA(module, wtree_a, wtree_b, Z);
else
module->addAdd(NEW_ID, wtree_sum.first, {wtree_sum.second.extract_end(1), State::S0}, Z);
module->addAdd(NEW_ID, wtree_a, wtree_b, Z);
}
/*

View file

@ -71,6 +71,8 @@ struct ConstmapPass : public Pass {
}
extra_args(args, argidx, design);
if (celltype.empty())
log_cmd_error("Missing required option -cell.\n");
if (design->has(celltype)) {
Module *existing = design->module(celltype);

View file

@ -0,0 +1,145 @@
#ifndef LIBERTY_CACHE_H
#define LIBERTY_CACHE_H
#include "kernel/yosys.h"
#ifdef YOSYS_LINK_ABC
namespace abc {
int Abc_RealMain(int argc, char *argv[]);
}
#endif
YOSYS_NAMESPACE_BEGIN
/*
* convert_liberty_files_to_merged_scl() - Convert multiple Liberty files to a single merged SCL cache file.
* @liberty_files: Vector of liberty file paths to merge
* @dont_use_args: Pre-built ABC -X flags string
* @abc_exe: Path to ABC executable for conversion
*
* Return: Path to merged SCL cache file, or empty string if conversion fails
*/
inline std::string convert_liberty_files_to_merged_scl(const std::vector<std::string> &liberty_files, const std::string &dont_use_args, const std::string &abc_exe)
{
if (liberty_files.empty())
return "";
std::string cache_dir = get_base_tmpdir() + "/yosys-liberty-scl-cache";
if (!create_directory(cache_dir)) {
log_warning("ABC: cannot create cache directory %s, falling back to liberty format\n", cache_dir.c_str());
return "";
}
// Sort to ensure consistent hash regardless of order
std::vector<std::string> sorted_files = liberty_files;
std::sort(sorted_files.begin(), sorted_files.end());
std::string hash_input;
time_t newest_mtime = 0;
for (const std::string &liberty_file : sorted_files) {
struct stat liberty_stat;
if (stat(liberty_file.c_str(), &liberty_stat) != 0) {
log_error("ABC: cannot stat liberty file: %s\n", liberty_file.c_str());
return "";
}
hash_input += liberty_file + "|";
if (liberty_stat.st_mtime > newest_mtime)
newest_mtime = liberty_stat.st_mtime;
}
hash_input += dont_use_args;
unsigned int hash = 0;
for (char c : hash_input)
hash = hash * 31 + c;
std::string merged_scl = stringf("%s/yosys_merged_%08x.scl", cache_dir.c_str(), hash);
bool need_convert = true;
struct stat scl_stat;
// Check if merged SCL exists and is newer than all liberty files
if (stat(merged_scl.c_str(), &scl_stat) == 0) {
if (scl_stat.st_mtime >= newest_mtime) {
log("ABC: using cached merged SCL: %s (%zu files)\n", merged_scl.c_str(), liberty_files.size());
need_convert = false;
}
}
if (need_convert) {
// read_lib -X cell1 -X cell2 file1 ; read_lib -X cell1 -X cell2 -m file2 ; ... ; write_scl merged.scl
std::string temp_scl = merged_scl + ".tmp";
#ifdef YOSYS_LINK_ABC
std::string script_path = stringf("%s/yosys_merged_scl_convert_%08x.script", cache_dir.c_str(), hash);
FILE *f = fopen(script_path.c_str(), "w");
if (f == NULL) {
log_warning("ABC: cannot open %s for writing, falling back to liberty format\n", script_path.c_str());
return "";
}
bool first = true;
for (const std::string &liberty_file : liberty_files) {
fprintf(f, "read_lib %s%s-w \"%s\"\n", dont_use_args.c_str(), first ? "" : "-m ", liberty_file.c_str());
first = false;
}
fprintf(f, "write_scl \"%s\"\n", temp_scl.c_str());
fclose(f);
char *abc_argv[5];
abc_argv[0] = strdup(abc_exe.empty() ? "yosys-abc" : abc_exe.c_str());
abc_argv[1] = strdup("-s");
abc_argv[2] = strdup("-f");
abc_argv[3] = strdup(script_path.c_str());
abc_argv[4] = 0;
int ret = abc::Abc_RealMain(4, abc_argv);
free(abc_argv[0]);
free(abc_argv[1]);
free(abc_argv[2]);
free(abc_argv[3]);
remove(script_path.c_str());
if (ret != 0) {
log_warning("ABC: merged SCL conversion failed (ret=%d), falling back to liberty format\n", ret);
remove(temp_scl.c_str());
return "";
}
#else
std::string abc_script;
bool first = true;
for (const std::string &liberty_file : liberty_files) {
abc_script += stringf("read_lib %s%s-w \\\"%s\\\" ; ", dont_use_args.c_str(), first ? "" : "-m ", liberty_file.c_str());
first = false;
}
abc_script += stringf("write_scl \\\"%s\\\"", temp_scl.c_str());
std::string cmd = stringf("\"%s\" -c \"%s\" 2>&1", abc_exe.c_str(), abc_script.c_str());
std::string abc_output;
int ret = run_command(cmd, [&abc_output](const std::string &line) { abc_output += line + "\n"; });
if (ret != 0) {
log_warning("ABC: merged SCL conversion failed, falling back to liberty format\n");
if (!abc_output.empty()) {
log("ABC: conversion output:\n%s", abc_output.c_str());
}
remove(temp_scl.c_str());
return "";
}
#endif
if (rename(temp_scl.c_str(), merged_scl.c_str()) != 0) {
log_warning("ABC: failed to rename %s to %s, falling back to liberty format\n", temp_scl.c_str(), merged_scl.c_str());
remove(temp_scl.c_str());
return "";
}
}
return merged_scl;
}
YOSYS_NAMESPACE_END
#endif // LIBERTY_CACHE_H

View file

@ -438,6 +438,48 @@ void simplemap_ff(RTLIL::Module *, RTLIL::Cell *cell)
}
}
void simplemap_pmux(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
RTLIL::SigSpec sig_b = cell->getPort(ID::B);
RTLIL::SigSpec sig_s = cell->getPort(ID::S);
RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
int width = GetSize(sig_a);
int s_width = GetSize(sig_s);
// Implement: |S
RTLIL::SigSpec any_s = sig_s;
logic_reduce(module, any_s, cell);
for (int i = 0; i < width; i++) {
RTLIL::SigSpec b_and_bits;
// Implement: B_AND_BITS = B_AND_S[WIDTH*j+i]
for (int j = 0; j < s_width; j++) {
RTLIL::Cell *and_gate = module->addCell(NEW_ID, ID($_AND_));
transfer_src(and_gate, cell);
and_gate->setPort(ID::A, sig_b[j * width + i]);
and_gate->setPort(ID::B, sig_s[j]);
RTLIL::SigSpec and_y = module->addWire(NEW_ID, 1);
and_gate->setPort(ID::Y, and_y);
b_and_bits.append(and_y);
}
// Implement: Y_B[i] = |B_AND_BITS
logic_reduce(module, b_and_bits, cell);
// Implement: Y[i] = |S ? Y_B[i] : A[i]
RTLIL::Cell *mux_gate = module->addCell(NEW_ID, ID($_MUX_));
transfer_src(mux_gate, cell);
mux_gate->setPort(ID::A, sig_a[i]);
mux_gate->setPort(ID::B, b_and_bits);
mux_gate->setPort(ID::S, any_s);
mux_gate->setPort(ID::Y, sig_y[i]);
}
}
void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers)
{
mappers[ID($not)] = simplemap_not;
@ -461,6 +503,7 @@ void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)>
mappers[ID($ne)] = simplemap_eqne;
mappers[ID($nex)] = simplemap_eqne;
mappers[ID($mux)] = simplemap_mux;
mappers[ID($pmux)] = simplemap_pmux;
mappers[ID($bwmux)] = simplemap_bwmux;
mappers[ID($tribuf)] = simplemap_tribuf;
mappers[ID($bmux)] = simplemap_bmux;
@ -515,7 +558,7 @@ struct SimplemapPass : public Pass {
log("\n");
log(" $not, $pos, $and, $or, $xor, $xnor\n");
log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n");
log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n");
log(" $logic_not, $logic_and, $logic_or, $mux, $pmux, $tribuf\n");
log(" $sr, $ff, $dff, $dffe, $dffsr, $dffsre, $adff, $adffe, $aldff, $aldffe, $sdff,\n");
log(" $sdffe, $sdffce, $dlatch, $adlatch, $dlatchsr\n");
log("\n");