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:
commit
52845cd82b
1402 changed files with 116443 additions and 39709 deletions
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
210
passes/techmap/abc_new.cc
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
524
passes/techmap/bufnorm.cc
Normal 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
316
passes/techmap/cellmatch.cc
Normal 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
|
||||
|
|
@ -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
417
passes/techmap/clockgate.cc
Normal 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
106
passes/techmap/constmap.cc
Normal 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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
149
passes/techmap/libcache.cc
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
|
|||
58
passes/techmap/lut2bmux.cc
Normal file
58
passes/techmap/lut2bmux.cc
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue