mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-23 03:27:53 +00:00
Merge remote-tracking branch 'origin/master' into feature/python_bindings
This commit is contained in:
commit
03d1606b42
428 changed files with 23388 additions and 2479 deletions
|
@ -29,4 +29,4 @@ OBJS += passes/cmds/chformal.o
|
|||
OBJS += passes/cmds/chtype.o
|
||||
OBJS += passes/cmds/blackbox.o
|
||||
OBJS += passes/cmds/ltp.o
|
||||
|
||||
OBJS += passes/cmds/bugpoint.o
|
||||
|
|
|
@ -83,7 +83,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n
|
|||
|
||||
struct AddPass : public Pass {
|
||||
AddPass() : Pass("add", "add objects to the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -106,7 +106,7 @@ struct AddPass : public Pass {
|
|||
log("selected modules.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string command;
|
||||
std::string arg_name;
|
||||
|
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct BlackboxPass : public Pass {
|
||||
BlackboxPass() : Pass("blackbox", "change type of cells in the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -34,7 +34,7 @@ struct BlackboxPass : public Pass {
|
|||
log("module attribute).\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
|
|
369
passes/cmds/bugpoint.cc
Normal file
369
passes/cmds/bugpoint.cc
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2018 whitequark <whitequark@whitequark.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/yosys.h"
|
||||
#include "backends/ilang/ilang_backend.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
using namespace ILANG_BACKEND;
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct BugpointPass : public Pass {
|
||||
BugpointPass() : Pass("bugpoint", "minimize testcases") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" bugpoint [options]\n");
|
||||
log("\n");
|
||||
log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n");
|
||||
log("of the design and recursively invokes Yosys with a given script, repeating these\n");
|
||||
log("steps while it can find a smaller design that still causes a crash. Once this\n");
|
||||
log("command finishes, it replaces the current design with the smallest testcase it\n");
|
||||
log("was able to produce.\n");
|
||||
log("\n");
|
||||
log("It is possible to specify the kinds of design part that will be removed. If none\n");
|
||||
log("are specified, all parts of design will be removed.\n");
|
||||
log("\n");
|
||||
log(" -yosys <filename>\n");
|
||||
log(" use this Yosys binary. if not specified, `yosys` is used.\n");
|
||||
log("\n");
|
||||
log(" -script <filename>\n");
|
||||
log(" use this script to crash Yosys. required.\n");
|
||||
log("\n");
|
||||
log(" -grep <string>\n");
|
||||
log(" only consider crashes that place this string in the log file.\n");
|
||||
log("\n");
|
||||
log(" -fast\n");
|
||||
log(" run `clean -purge` after each minimization step. converges faster, but\n");
|
||||
log(" produces larger testcases, and may fail to produce any testcase at all if\n");
|
||||
log(" the crash is related to dangling wires.\n");
|
||||
log("\n");
|
||||
log(" -clean\n");
|
||||
log(" run `clean -purge` before checking testcase and after finishing. produces\n");
|
||||
log(" smaller and more useful testcases, but may fail to produce any testcase\n");
|
||||
log(" at all if the crash is related to dangling wires.\n");
|
||||
log("\n");
|
||||
log(" -modules\n");
|
||||
log(" try to remove modules.\n");
|
||||
log("\n");
|
||||
log(" -ports\n");
|
||||
log(" try to remove module ports.\n");
|
||||
log("\n");
|
||||
log(" -cells\n");
|
||||
log(" try to remove cells.\n");
|
||||
log("\n");
|
||||
log(" -connections\n");
|
||||
log(" try to reconnect ports to 'x.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script)
|
||||
{
|
||||
design->sort();
|
||||
|
||||
std::ofstream f("bugpoint-case.il");
|
||||
ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
|
||||
f.close();
|
||||
|
||||
string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str());
|
||||
return run_command(yosys_cmdline) == 0;
|
||||
}
|
||||
|
||||
bool check_logfile(string grep)
|
||||
{
|
||||
if (grep.empty())
|
||||
return true;
|
||||
|
||||
std::ifstream f("bugpoint-case.log");
|
||||
while (!f.eof())
|
||||
{
|
||||
string line;
|
||||
getline(f, line);
|
||||
if (line.find(grep) != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false)
|
||||
{
|
||||
if (!do_clean)
|
||||
return design;
|
||||
|
||||
RTLIL::Design *design_copy = new RTLIL::Design;
|
||||
for (auto &it : design->modules_)
|
||||
design_copy->add(it.second->clone());
|
||||
Pass::call(design_copy, "clean -purge");
|
||||
|
||||
if (do_delete)
|
||||
delete design;
|
||||
return design_copy;
|
||||
}
|
||||
|
||||
RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections)
|
||||
{
|
||||
RTLIL::Design *design_copy = new RTLIL::Design;
|
||||
for (auto &it : design->modules_)
|
||||
design_copy->add(it.second->clone());
|
||||
|
||||
int index = 0;
|
||||
if (modules)
|
||||
{
|
||||
for (auto &it : design_copy->modules_)
|
||||
{
|
||||
if (it.second->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log("Trying to remove module %s.\n", it.first.c_str());
|
||||
design_copy->remove(it.second);
|
||||
return design_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ports)
|
||||
{
|
||||
for (auto mod : design_copy->modules())
|
||||
{
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
for (auto wire : mod->wires())
|
||||
{
|
||||
if (!stage2 && wire->get_bool_attribute("$bugpoint"))
|
||||
continue;
|
||||
|
||||
if (wire->port_input || wire->port_output)
|
||||
{
|
||||
if (index++ == seed)
|
||||
{
|
||||
log("Trying to remove module port %s.\n", log_signal(wire));
|
||||
wire->port_input = wire->port_output = false;
|
||||
mod->fixup_ports();
|
||||
return design_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cells)
|
||||
{
|
||||
for (auto mod : design_copy->modules())
|
||||
{
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
for (auto &it : mod->cells_)
|
||||
{
|
||||
if (index++ == seed)
|
||||
{
|
||||
log("Trying to remove cell %s.%s.\n", mod->name.c_str(), it.first.c_str());
|
||||
mod->remove(it.second);
|
||||
return design_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connections)
|
||||
{
|
||||
for (auto mod : design_copy->modules())
|
||||
{
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
for (auto cell : mod->cells())
|
||||
{
|
||||
for (auto it : cell->connections_)
|
||||
{
|
||||
RTLIL::SigSpec port = cell->getPort(it.first);
|
||||
bool is_undef = port.is_fully_undef();
|
||||
bool is_port = port.is_wire() && (port.as_wire()->port_input || port.as_wire()->port_output);
|
||||
|
||||
if(is_undef || (!stage2 && is_port))
|
||||
continue;
|
||||
|
||||
if (index++ == seed)
|
||||
{
|
||||
log("Trying to remove cell port %s.%s.%s.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
|
||||
RTLIL::SigSpec port_x(State::Sx, port.size());
|
||||
cell->unsetPort(it.first);
|
||||
cell->setPort(it.first, port_x);
|
||||
return design_copy;
|
||||
}
|
||||
|
||||
if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed)
|
||||
{
|
||||
log("Trying to expose cell port %s.%s.%s as module port.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
|
||||
RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size());
|
||||
wire->set_bool_attribute("$bugpoint");
|
||||
wire->port_input = cell->input(it.first);
|
||||
wire->port_output = cell->output(it.first);
|
||||
cell->unsetPort(it.first);
|
||||
cell->setPort(it.first, wire);
|
||||
mod->fixup_ports();
|
||||
return design_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
string yosys_cmd = "yosys", script, grep;
|
||||
bool fast = false, clean = false;
|
||||
bool modules = false, ports = false, cells = false, connections = false, has_part = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-yosys" && argidx + 1 < args.size()) {
|
||||
yosys_cmd = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-script" && argidx + 1 < args.size()) {
|
||||
script = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-grep" && argidx + 1 < args.size()) {
|
||||
grep = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-fast") {
|
||||
fast = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-clean") {
|
||||
clean = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-modules") {
|
||||
modules = true;
|
||||
has_part = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-ports") {
|
||||
ports = true;
|
||||
has_part = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-cells") {
|
||||
cells = true;
|
||||
has_part = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-connections") {
|
||||
connections = true;
|
||||
has_part = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (!has_part)
|
||||
{
|
||||
modules = true;
|
||||
ports = true;
|
||||
cells = true;
|
||||
connections = true;
|
||||
}
|
||||
|
||||
if (!design->full_selection())
|
||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||
|
||||
RTLIL::Design *crashing_design = clean_design(design, clean);
|
||||
if (run_yosys(crashing_design, yosys_cmd, script))
|
||||
log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
|
||||
if (!check_logfile(grep))
|
||||
log_cmd_error("The provided grep string is not found in the log file!\n");
|
||||
|
||||
int seed = 0, crashing_seed = seed;
|
||||
bool found_something = false, stage2 = false;
|
||||
while (true)
|
||||
{
|
||||
if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections))
|
||||
{
|
||||
simplified = clean_design(simplified, fast, /*do_delete=*/true);
|
||||
|
||||
bool crashes;
|
||||
if (clean)
|
||||
{
|
||||
RTLIL::Design *testcase = clean_design(simplified);
|
||||
crashes = !run_yosys(testcase, yosys_cmd, script);
|
||||
delete testcase;
|
||||
}
|
||||
else
|
||||
{
|
||||
crashes = !run_yosys(simplified, yosys_cmd, script);
|
||||
}
|
||||
|
||||
if (crashes && check_logfile(grep))
|
||||
{
|
||||
log("Testcase crashes.\n");
|
||||
if (crashing_design != design)
|
||||
delete crashing_design;
|
||||
crashing_design = simplified;
|
||||
crashing_seed = seed;
|
||||
found_something = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Testcase does not crash.\n");
|
||||
delete simplified;
|
||||
seed++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
seed = 0;
|
||||
if (found_something)
|
||||
found_something = false;
|
||||
else
|
||||
{
|
||||
if (!stage2)
|
||||
{
|
||||
log("Demoting introduced module ports.\n");
|
||||
stage2 = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Simplifications exhausted.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (crashing_design != design)
|
||||
{
|
||||
Pass::call(design, "design -reset");
|
||||
crashing_design = clean_design(crashing_design, clean, /*do_delete=*/true);
|
||||
for (auto &it : crashing_design->modules_)
|
||||
design->add(it.second->clone());
|
||||
delete crashing_design;
|
||||
}
|
||||
}
|
||||
} BugpointPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct CheckPass : public Pass {
|
||||
CheckPass() : Pass("check", "check for obvious problems in the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -51,7 +51,7 @@ struct CheckPass : public Pass {
|
|||
log("problems are found in the current design.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
int counter = 0;
|
||||
bool noinit = false;
|
||||
|
|
|
@ -25,14 +25,14 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct ChformalPass : public Pass {
|
||||
ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" chformal [types] [mode] [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Make changes to the formal constraints of the design. The [types] options\n");
|
||||
log("the type of constraint to operate on. If none of the folling options is given,\n");
|
||||
log("the type of constraint to operate on. If none of the following options are given,\n");
|
||||
log("the command will operate on all constraint types:\n");
|
||||
log("\n");
|
||||
log(" -assert $assert cells, representing assert(...) constraints\n");
|
||||
|
@ -59,10 +59,10 @@ struct ChformalPass : public Pass {
|
|||
log(" -assume2assert\n");
|
||||
log(" -live2fair\n");
|
||||
log(" -fair2live\n");
|
||||
log(" change the roles of cells as indicated. this options can be combined\n");
|
||||
log(" change the roles of cells as indicated. these options can be combined\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool assert2assume = false;
|
||||
bool assume2assert = false;
|
||||
|
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct ChtypePass : public Pass {
|
||||
ChtypePass() : Pass("chtype", "change type of cells in the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -40,7 +40,7 @@ struct ChtypePass : public Pass {
|
|||
log("\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
IdString set_type;
|
||||
dict<IdString, IdString> map_types;
|
||||
|
|
|
@ -43,7 +43,7 @@ static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap &
|
|||
|
||||
struct ConnectPass : public Pass {
|
||||
ConnectPass() : Pass("connect", "create or remove connections") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -75,7 +75,7 @@ struct ConnectPass : public Pass {
|
|||
log("This command does not operate on module with processes.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
RTLIL::Module *module = NULL;
|
||||
for (auto &it : design->modules_) {
|
||||
|
@ -137,7 +137,7 @@ struct ConnectPass : public Pass {
|
|||
if (!set_lhs.empty())
|
||||
{
|
||||
if (!unset_expr.empty() || !port_cell.empty())
|
||||
log_cmd_error("Cant use -set together with -unset and/or -port.\n");
|
||||
log_cmd_error("Can't use -set together with -unset and/or -port.\n");
|
||||
|
||||
RTLIL::SigSpec sig_lhs, sig_rhs;
|
||||
if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs))
|
||||
|
@ -157,7 +157,7 @@ struct ConnectPass : public Pass {
|
|||
if (!unset_expr.empty())
|
||||
{
|
||||
if (!port_cell.empty() || flag_nounset)
|
||||
log_cmd_error("Cant use -unset together with -port and/or -nounset.\n");
|
||||
log_cmd_error("Can't use -unset together with -port and/or -nounset.\n");
|
||||
|
||||
RTLIL::SigSpec sig;
|
||||
if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr))
|
||||
|
@ -170,7 +170,7 @@ struct ConnectPass : public Pass {
|
|||
if (!port_cell.empty())
|
||||
{
|
||||
if (flag_nounset)
|
||||
log_cmd_error("Cant use -port together with -nounset.\n");
|
||||
log_cmd_error("Can't use -port together with -nounset.\n");
|
||||
|
||||
if (module->cells_.count(RTLIL::escape_id(port_cell)) == 0)
|
||||
log_cmd_error("Can't find cell %s.\n", port_cell.c_str());
|
||||
|
|
|
@ -150,7 +150,7 @@ struct ConnwrappersWorker
|
|||
|
||||
struct ConnwrappersPass : public Pass {
|
||||
ConnwrappersPass() : Pass("connwrappers", "match width of input-output port pairs") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -172,7 +172,7 @@ struct ConnwrappersPass : public Pass {
|
|||
log("The options -signed, -unsigned, and -port can be specified multiple times.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
ConnwrappersWorker worker;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct CopyPass : public Pass {
|
||||
CopyPass() : Pass("copy", "copy modules in the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -36,7 +36,7 @@ struct CopyPass : public Pass {
|
|||
log("by this command.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
if (args.size() != 3)
|
||||
log_cmd_error("Invalid number of arguments!\n");
|
||||
|
|
|
@ -35,7 +35,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct CoverPass : public Pass {
|
||||
CoverPass() : Pass("cover", "print code coverage counters") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -83,7 +83,7 @@ struct CoverPass : public Pass {
|
|||
log("Coverage counters are only available in Yosys for Linux.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::vector<FILE*> out_files;
|
||||
std::vector<std::string> patterns;
|
||||
|
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct DeletePass : public Pass {
|
||||
DeletePass() : Pass("delete", "delete objects in the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -40,7 +40,7 @@ struct DeletePass : public Pass {
|
|||
log("selected wires, thus 'deleting' module ports.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_input = false;
|
||||
bool flag_output = false;
|
||||
|
|
|
@ -27,7 +27,7 @@ std::vector<RTLIL::Design*> pushed_designs;
|
|||
|
||||
struct DesignPass : public Pass {
|
||||
DesignPass() : Pass("design", "save, restore and reset current design") { }
|
||||
virtual ~DesignPass() {
|
||||
~DesignPass() YS_OVERRIDE {
|
||||
for (auto &it : saved_designs)
|
||||
delete it.second;
|
||||
saved_designs.clear();
|
||||
|
@ -35,7 +35,7 @@ struct DesignPass : public Pass {
|
|||
delete it;
|
||||
pushed_designs.clear();
|
||||
}
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -94,7 +94,7 @@ struct DesignPass : public Pass {
|
|||
log("between calls to 'read_verilog'. This command resets this memory.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool got_mode = false;
|
||||
bool reset_mode = false;
|
||||
|
|
|
@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct EdgetypePass : public Pass {
|
||||
EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -35,7 +35,7 @@ struct EdgetypePass : public Pass {
|
|||
log("is a 4-tuple of source and sink cell type and port name.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct LogPass : public Pass {
|
||||
LogPass() : Pass("log", "print text and log files") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -52,7 +52,7 @@ struct LogPass : public Pass {
|
|||
log(" do not append a newline\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design*)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
bool to_stdout = false;
|
||||
|
|
|
@ -141,7 +141,7 @@ struct LtpWorker
|
|||
|
||||
struct LtpPass : public Pass {
|
||||
LtpPass() : Pass("ltp", "print longest topological path") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -154,7 +154,7 @@ struct LtpPass : public Pass {
|
|||
log(" automatically exclude FF cell types\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool noff = false;
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ void load_plugin(std::string, std::vector<std::string>)
|
|||
|
||||
struct PluginPass : public Pass {
|
||||
PluginPass() : Pass("plugin", "load and list loaded plugins") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -118,7 +118,7 @@ struct PluginPass : public Pass {
|
|||
log(" List loaded plugins\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string plugin_filename;
|
||||
std::vector<std::string> plugin_aliases;
|
||||
|
|
|
@ -778,7 +778,7 @@ struct QwpWorker
|
|||
|
||||
struct QwpPass : public Pass {
|
||||
QwpPass() : Pass("qwp", "quadratic wirelength placer") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -808,7 +808,7 @@ struct QwpPass : public Pass {
|
|||
log("dense matrix operations. It is only a toy-placer for small circuits.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
QwpConfig config;
|
||||
xorshift32_state = 123456789;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
|
||||
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output)
|
||||
{
|
||||
from_name = RTLIL::escape_id(from_name);
|
||||
to_name = RTLIL::escape_id(to_name);
|
||||
|
@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
|
|||
Wire *w = it.second;
|
||||
log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
|
||||
module->rename(w, to_name);
|
||||
if (w->port_id)
|
||||
if (w->port_id || flag_output) {
|
||||
if (flag_output)
|
||||
w->port_output = true;
|
||||
module->fixup_ports();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_)
|
||||
if (it.first == from_name) {
|
||||
if (flag_output)
|
||||
log_cmd_error("Called with -output but the specified object is a cell.\n");
|
||||
log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
|
||||
module->rename(it.second, to_name);
|
||||
return;
|
||||
|
@ -52,9 +57,54 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
|
|||
log_cmd_error("Object `%s' not found!\n", from_name.c_str());
|
||||
}
|
||||
|
||||
static std::string derive_name_from_src(const std::string &src, int counter)
|
||||
{
|
||||
std::string src_base = src.substr(0, src.find('|'));
|
||||
if (src_base.empty())
|
||||
return stringf("$%d", counter);
|
||||
else
|
||||
return stringf("\\%s$%d", src_base.c_str(), counter);
|
||||
}
|
||||
|
||||
static IdString derive_name_from_wire(const RTLIL::Cell &cell)
|
||||
{
|
||||
// Find output
|
||||
const SigSpec *output = nullptr;
|
||||
int num_outputs = 0;
|
||||
for (auto &connection : cell.connections()) {
|
||||
if (cell.output(connection.first)) {
|
||||
output = &connection.second;
|
||||
num_outputs++;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_outputs != 1) // Skip cells thad drive multiple outputs
|
||||
return cell.name;
|
||||
|
||||
std::string name = "";
|
||||
for (auto &chunk : output->chunks()) {
|
||||
// Skip cells that drive privately named wires
|
||||
if (!chunk.wire || chunk.wire->name.str()[0] == '$')
|
||||
return cell.name;
|
||||
|
||||
if (name != "")
|
||||
name += "$";
|
||||
|
||||
name += chunk.wire->name.str();
|
||||
if (chunk.wire->width != chunk.width) {
|
||||
name += "[";
|
||||
if (chunk.width != 1)
|
||||
name += std::to_string(chunk.offset + chunk.width) + ":";
|
||||
name += std::to_string(chunk.offset) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
return name + cell.type.str();
|
||||
}
|
||||
|
||||
struct RenamePass : public Pass {
|
||||
RenamePass() : Pass("rename", "rename object in the design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -64,6 +114,25 @@ struct RenamePass : public Pass {
|
|||
log("by this command.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -output old_name new_name\n");
|
||||
log("\n");
|
||||
log("Like above, but also make the wire an output. This will fail if the object is\n");
|
||||
log("not a wire.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -src [selection]\n");
|
||||
log("\n");
|
||||
log("Assign names auto-generated from the src attribute to all selected wires and\n");
|
||||
log("cells with private names.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -wire [selection]\n");
|
||||
log("\n");
|
||||
log("Assign auto-generated names based on the wires they drive to all selected\n");
|
||||
log("cells with private names. Ignores cells driving privatly named wires.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
|
||||
log("\n");
|
||||
log("Assign short auto-generated names to all selected wires and cells with private\n");
|
||||
|
@ -71,28 +140,48 @@ struct RenamePass : public Pass {
|
|||
log("The character %% in the pattern is replaced with a integer number. The default\n");
|
||||
log("pattern is '_%%_'.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -hide [selection]\n");
|
||||
log("\n");
|
||||
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
|
||||
log("with public names. This ignores all selected ports.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -top new_name\n");
|
||||
log("\n");
|
||||
log("Rename top module.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string pattern_prefix = "_", pattern_suffix = "_";
|
||||
bool flag_src = false;
|
||||
bool flag_wire = false;
|
||||
bool flag_enumerate = false;
|
||||
bool flag_hide = false;
|
||||
bool flag_top = false;
|
||||
bool flag_output = false;
|
||||
bool got_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-src" && !got_mode) {
|
||||
flag_src = true;
|
||||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-output" && !got_mode) {
|
||||
flag_output = true;
|
||||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-wire" && !got_mode) {
|
||||
flag_wire = true;
|
||||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-enumerate" && !got_mode) {
|
||||
flag_enumerate = true;
|
||||
got_mode = true;
|
||||
|
@ -117,6 +206,57 @@ struct RenamePass : public Pass {
|
|||
break;
|
||||
}
|
||||
|
||||
if (flag_src)
|
||||
{
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto &mod : design->modules_)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
RTLIL::Module *module = mod.second;
|
||||
if (!design->selected(module))
|
||||
continue;
|
||||
|
||||
dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
|
||||
for (auto &it : module->wires_) {
|
||||
if (it.first[0] == '$' && design->selected(module, it.second))
|
||||
it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
|
||||
new_wires[it.second->name] = it.second;
|
||||
}
|
||||
module->wires_.swap(new_wires);
|
||||
module->fixup_ports();
|
||||
|
||||
dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
|
||||
for (auto &it : module->cells_) {
|
||||
if (it.first[0] == '$' && design->selected(module, it.second))
|
||||
it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
|
||||
new_cells[it.second->name] = it.second;
|
||||
}
|
||||
module->cells_.swap(new_cells);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (flag_wire)
|
||||
{
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto &mod : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = mod.second;
|
||||
if (!design->selected(module))
|
||||
continue;
|
||||
|
||||
dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
|
||||
for (auto &it : module->cells_) {
|
||||
if (it.first[0] == '$' && design->selected(module, it.second))
|
||||
it.second->name = derive_name_from_wire(*it.second);
|
||||
new_cells[it.second->name] = it.second;
|
||||
}
|
||||
module->cells_.swap(new_cells);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (flag_enumerate)
|
||||
{
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -206,10 +346,12 @@ struct RenamePass : public Pass {
|
|||
if (!design->selected_active_module.empty())
|
||||
{
|
||||
if (design->modules_.count(design->selected_active_module) > 0)
|
||||
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name);
|
||||
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flag_output)
|
||||
log_cmd_error("Mode -output requires that there is an active module selected.\n");
|
||||
for (auto &mod : design->modules_) {
|
||||
if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
|
||||
to_name = RTLIL::escape_id(to_name);
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct ScatterPass : public Pass {
|
||||
ScatterPass() : Pass("scatter", "add additional intermediate nets") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -41,7 +41,7 @@ struct ScatterPass : public Pass {
|
|||
log("Use the opt_clean command to get rid of the additional nets.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
CellTypes ct(design);
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -218,7 +218,7 @@ struct SccWorker
|
|||
|
||||
struct SccPass : public Pass {
|
||||
SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -255,7 +255,7 @@ struct SccPass : public Pass {
|
|||
log(" that are part of a found logic loop\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::map<std::string, std::string> setAttr;
|
||||
bool allCellTypes = false;
|
||||
|
|
|
@ -896,6 +896,29 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
|
|||
select_filter_active_mod(design, work_stack.back());
|
||||
}
|
||||
|
||||
static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel)
|
||||
{
|
||||
std::string desc = "Selection contains:\n";
|
||||
for (auto mod_it : design->modules_)
|
||||
{
|
||||
if (sel->selected_module(mod_it.first)) {
|
||||
for (auto &it : mod_it.second->wires_)
|
||||
if (sel->selected_member(mod_it.first, it.first))
|
||||
desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
|
||||
for (auto &it : mod_it.second->memories)
|
||||
if (sel->selected_member(mod_it.first, it.first))
|
||||
desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
|
||||
for (auto &it : mod_it.second->cells_)
|
||||
if (sel->selected_member(mod_it.first, it.first))
|
||||
desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
|
||||
for (auto &it : mod_it.second->processes)
|
||||
if (sel->selected_member(mod_it.first, it.first))
|
||||
desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -950,7 +973,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct SelectPass : public Pass {
|
||||
SelectPass() : Pass("select", "modify and view the list of selected objects") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -1167,7 +1190,7 @@ struct SelectPass : public Pass {
|
|||
log(" select */t:SWITCH %%x:+[GATE] */t:SWITCH %%d\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool add_mode = false;
|
||||
bool del_mode = false;
|
||||
|
@ -1394,7 +1417,12 @@ struct SelectPass : public Pass {
|
|||
log_cmd_error("No selection to check.\n");
|
||||
work_stack.back().optimize(design);
|
||||
if (!work_stack.back().empty())
|
||||
log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str());
|
||||
{
|
||||
RTLIL::Selection *sel = &work_stack.back();
|
||||
sel->optimize(design);
|
||||
std::string desc = describe_selection_for_assert(design, sel);
|
||||
log_error("Assertion failed: selection is not empty:%s\n%s", sel_str.c_str(), desc.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1404,7 +1432,12 @@ struct SelectPass : public Pass {
|
|||
log_cmd_error("No selection to check.\n");
|
||||
work_stack.back().optimize(design);
|
||||
if (work_stack.back().empty())
|
||||
log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str());
|
||||
{
|
||||
RTLIL::Selection *sel = &work_stack.back();
|
||||
sel->optimize(design);
|
||||
std::string desc = describe_selection_for_assert(design, sel);
|
||||
log_error("Assertion failed: selection is empty:%s\n%s", sel_str.c_str(), desc.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1431,14 +1464,23 @@ struct SelectPass : public Pass {
|
|||
total_count++;
|
||||
}
|
||||
if (assert_count >= 0 && assert_count != total_count)
|
||||
log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n",
|
||||
total_count, assert_count, sel_str.c_str());
|
||||
{
|
||||
std::string desc = describe_selection_for_assert(design, sel);
|
||||
log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n%s",
|
||||
total_count, assert_count, sel_str.c_str(), desc.c_str());
|
||||
}
|
||||
if (assert_max >= 0 && assert_max < total_count)
|
||||
log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n",
|
||||
total_count, assert_max, sel_str.c_str());
|
||||
{
|
||||
std::string desc = describe_selection_for_assert(design, sel);
|
||||
log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n%s",
|
||||
total_count, assert_max, sel_str.c_str(), desc.c_str());
|
||||
}
|
||||
if (assert_min >= 0 && assert_min > total_count)
|
||||
log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n",
|
||||
total_count, assert_min, sel_str.c_str());
|
||||
{
|
||||
std::string desc = describe_selection_for_assert(design, sel);
|
||||
log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n%s",
|
||||
total_count, assert_min, sel_str.c_str(), desc.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1470,7 +1512,7 @@ struct SelectPass : public Pass {
|
|||
|
||||
struct CdPass : public Pass {
|
||||
CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -1496,7 +1538,7 @@ struct CdPass : public Pass {
|
|||
log("This is just a shortcut for 'select -clear'.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
if (args.size() != 1 && args.size() != 2)
|
||||
log_cmd_error("Invalid number of arguments.\n");
|
||||
|
@ -1578,7 +1620,7 @@ static void log_matches(const char *title, Module *module, T list)
|
|||
|
||||
struct LsPass : public Pass {
|
||||
LsPass() : Pass("ls", "list modules or objects in modules") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -1589,7 +1631,7 @@ struct LsPass : public Pass {
|
|||
log("When an active module is selected, this prints a list of objects in the module.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
|
|
@ -56,7 +56,7 @@ static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::v
|
|||
|
||||
struct SetattrPass : public Pass {
|
||||
SetattrPass() : Pass("setattr", "set/unset attributes on objects") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -69,7 +69,7 @@ struct SetattrPass : public Pass {
|
|||
log("instead of objects within modules.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::vector<setunset_t> setunset_list;
|
||||
bool flag_mod = false;
|
||||
|
@ -130,7 +130,7 @@ struct SetattrPass : public Pass {
|
|||
|
||||
struct SetparamPass : public Pass {
|
||||
SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -142,7 +142,7 @@ struct SetparamPass : public Pass {
|
|||
log("The -type option can be used to change the cell type of the selected cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
vector<setunset_t> setunset_list;
|
||||
string new_cell_type;
|
||||
|
@ -188,7 +188,7 @@ struct SetparamPass : public Pass {
|
|||
|
||||
struct ChparamPass : public Pass {
|
||||
ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -203,7 +203,7 @@ struct ChparamPass : public Pass {
|
|||
log("List the available parameters of the selected modules.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::vector<setunset_t> setunset_list;
|
||||
dict<RTLIL::IdString, RTLIL::Const> new_parameters;
|
||||
|
|
|
@ -33,6 +33,34 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static RTLIL::Wire * add_wire(RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output)
|
||||
{
|
||||
RTLIL::Wire *wire = NULL;
|
||||
name = RTLIL::escape_id(name);
|
||||
|
||||
if (module->count_id(name) != 0)
|
||||
{
|
||||
log("Module %s already has such an object %s.\n", module->name.c_str(), name.c_str());
|
||||
name += "$";
|
||||
return add_wire(module, name, width, flag_input, flag_output);
|
||||
}
|
||||
else
|
||||
{
|
||||
wire = module->addWire(name, width);
|
||||
wire->port_input = flag_input;
|
||||
wire->port_output = flag_output;
|
||||
|
||||
if (flag_input || flag_output) {
|
||||
wire->port_id = module->wires_.size();
|
||||
module->fixup_ports();
|
||||
}
|
||||
|
||||
log("Added wire %s to module %s.\n", name.c_str(), module->name.c_str());
|
||||
}
|
||||
|
||||
return wire;
|
||||
}
|
||||
|
||||
struct SetundefWorker
|
||||
{
|
||||
int next_bit_mode;
|
||||
|
@ -79,7 +107,7 @@ struct SetundefWorker
|
|||
|
||||
struct SetundefPass : public Pass {
|
||||
SetundefPass() : Pass("setundef", "replace undef values with defined constants") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -90,6 +118,9 @@ struct SetundefPass : public Pass {
|
|||
log(" -undriven\n");
|
||||
log(" also set undriven nets to constant values\n");
|
||||
log("\n");
|
||||
log(" -expose\n");
|
||||
log(" also expose undriven nets as inputs (use with -undriven)\n");
|
||||
log("\n");
|
||||
log(" -zero\n");
|
||||
log(" replace with bits cleared (0)\n");
|
||||
log("\n");
|
||||
|
@ -106,18 +137,23 @@ struct SetundefPass : public Pass {
|
|||
log(" replace with $anyconst drivers (for formal)\n");
|
||||
log("\n");
|
||||
log(" -random <seed>\n");
|
||||
log(" replace with random bits using the specified integer als seed\n");
|
||||
log(" replace with random bits using the specified integer as seed\n");
|
||||
log(" value for the random number generator.\n");
|
||||
log("\n");
|
||||
log(" -init\n");
|
||||
log(" also create/update init values for flip-flops\n");
|
||||
log("\n");
|
||||
log(" -params\n");
|
||||
log(" replace undef in cell parameters\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool got_value = false;
|
||||
bool undriven_mode = false;
|
||||
bool expose_mode = false;
|
||||
bool init_mode = false;
|
||||
bool params_mode = false;
|
||||
SetundefWorker worker;
|
||||
|
||||
log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
|
||||
|
@ -129,6 +165,10 @@ struct SetundefPass : public Pass {
|
|||
undriven_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-expose") {
|
||||
expose_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-zero") {
|
||||
got_value = true;
|
||||
worker.next_bit_mode = MODE_ZERO;
|
||||
|
@ -163,6 +203,10 @@ struct SetundefPass : public Pass {
|
|||
init_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-params") {
|
||||
params_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
|
||||
got_value = true;
|
||||
worker.next_bit_mode = MODE_RANDOM;
|
||||
|
@ -175,6 +219,15 @@ struct SetundefPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (!got_value && expose_mode) {
|
||||
log("Using default as -undef with -expose.\n");
|
||||
got_value = true;
|
||||
worker.next_bit_mode = MODE_UNDEF;
|
||||
worker.next_bit_state = 0;
|
||||
}
|
||||
|
||||
if (expose_mode && !undriven_mode)
|
||||
log_cmd_error("Option -expose must be used with option -undriven.\n");
|
||||
if (!got_value)
|
||||
log_cmd_error("One of the options -zero, -one, -anyseq, -anyconst, or -random <seed> must be specified.\n");
|
||||
|
||||
|
@ -183,38 +236,120 @@ struct SetundefPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (params_mode)
|
||||
{
|
||||
for (auto *cell : module->selected_cells()) {
|
||||
for (auto ¶meter : cell->parameters) {
|
||||
for (auto &bit : parameter.second.bits) {
|
||||
if (bit > RTLIL::State::S1)
|
||||
bit = worker.next_bit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (undriven_mode)
|
||||
{
|
||||
if (!module->processes.empty())
|
||||
log_error("The 'setundef' command can't operate in -undriven mode on modules with processes. Run 'proc' first.\n");
|
||||
|
||||
SigMap sigmap(module);
|
||||
SigPool undriven_signals;
|
||||
if (expose_mode)
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, bool> wire_drivers;
|
||||
pool<SigBit> used_wires;
|
||||
SigPool undriven_signals;
|
||||
|
||||
for (auto &it : module->wires_)
|
||||
undriven_signals.add(sigmap(it.second));
|
||||
for (auto cell : module->cells())
|
||||
for (auto &conn : cell->connections()) {
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
if (cell->input(conn.first))
|
||||
for (auto bit : sig)
|
||||
if (bit.wire)
|
||||
used_wires.insert(bit);
|
||||
if (cell->output(conn.first))
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (sig[i].wire)
|
||||
wire_drivers[sig[i]] = true;
|
||||
}
|
||||
|
||||
for (auto &it : module->wires_)
|
||||
if (it.second->port_input)
|
||||
undriven_signals.del(sigmap(it.second));
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->port_input) {
|
||||
SigSpec sig = sigmap(wire);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
wire_drivers[sig[i]] = true;
|
||||
}
|
||||
if (wire->port_output) {
|
||||
SigSpec sig = sigmap(wire);
|
||||
for (auto bit : sig)
|
||||
if (bit.wire)
|
||||
used_wires.insert(bit);
|
||||
}
|
||||
}
|
||||
|
||||
CellTypes ct(design);
|
||||
for (auto &it : module->cells_)
|
||||
for (auto &conn : it.second->connections())
|
||||
if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
|
||||
undriven_signals.del(sigmap(conn.second));
|
||||
pool<RTLIL::Wire*> undriven_wires;
|
||||
for (auto bit : used_wires)
|
||||
if (!wire_drivers.count(bit))
|
||||
undriven_wires.insert(bit.wire);
|
||||
|
||||
RTLIL::SigSpec sig = undriven_signals.export_all();
|
||||
for (auto &c : sig.chunks()) {
|
||||
RTLIL::SigSpec bits;
|
||||
if (worker.next_bit_mode == MODE_ANYSEQ)
|
||||
bits = module->Anyseq(NEW_ID, c.width);
|
||||
else if (worker.next_bit_mode == MODE_ANYCONST)
|
||||
bits = module->Anyconst(NEW_ID, c.width);
|
||||
else
|
||||
for (int i = 0; i < c.width; i++)
|
||||
bits.append(worker.next_bit());
|
||||
module->connect(RTLIL::SigSig(c, bits));
|
||||
for (auto &it : undriven_wires)
|
||||
undriven_signals.add(sigmap(it));
|
||||
|
||||
for (auto &it : undriven_wires)
|
||||
if (it->port_input)
|
||||
undriven_signals.del(sigmap(it));
|
||||
|
||||
CellTypes ct(design);
|
||||
for (auto &it : module->cells_)
|
||||
for (auto &conn : it.second->connections())
|
||||
if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
|
||||
undriven_signals.del(sigmap(conn.second));
|
||||
|
||||
RTLIL::SigSpec sig = undriven_signals.export_all();
|
||||
for (auto &c : sig.chunks()) {
|
||||
RTLIL::Wire * wire;
|
||||
if (c.wire->width == c.width) {
|
||||
wire = c.wire;
|
||||
wire->port_input = true;
|
||||
} else {
|
||||
string name = c.wire->name.str() + "$[" + std::to_string(c.width + c.offset) + ":" + std::to_string(c.offset) + "]";
|
||||
wire = add_wire(module, name, c.width, true, false);
|
||||
module->connect(RTLIL::SigSig(c, wire));
|
||||
}
|
||||
log("Exposing undriven wire %s as input.\n", wire->name.c_str());
|
||||
}
|
||||
module->fixup_ports();
|
||||
}
|
||||
else
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
SigPool undriven_signals;
|
||||
|
||||
for (auto &it : module->wires_)
|
||||
undriven_signals.add(sigmap(it.second));
|
||||
|
||||
for (auto &it : module->wires_)
|
||||
if (it.second->port_input)
|
||||
undriven_signals.del(sigmap(it.second));
|
||||
|
||||
CellTypes ct(design);
|
||||
for (auto &it : module->cells_)
|
||||
for (auto &conn : it.second->connections())
|
||||
if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
|
||||
undriven_signals.del(sigmap(conn.second));
|
||||
|
||||
RTLIL::SigSpec sig = undriven_signals.export_all();
|
||||
for (auto &c : sig.chunks()) {
|
||||
RTLIL::SigSpec bits;
|
||||
if (worker.next_bit_mode == MODE_ANYSEQ)
|
||||
bits = module->Anyseq(NEW_ID, c.width);
|
||||
else if (worker.next_bit_mode == MODE_ANYCONST)
|
||||
bits = module->Anyconst(NEW_ID, c.width);
|
||||
else
|
||||
for (int i = 0; i < c.width; i++)
|
||||
bits.append(worker.next_bit());
|
||||
module->connect(RTLIL::SigSig(c, bits));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -573,7 +573,7 @@ struct ShowWorker
|
|||
|
||||
struct ShowPass : public Pass {
|
||||
ShowPass() : Pass("show", "generate schematics using graphviz") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -584,6 +584,7 @@ struct ShowPass : public Pass {
|
|||
log("\n");
|
||||
log(" -viewer <viewer>\n");
|
||||
log(" Run the specified command with the graphics file as parameter.\n");
|
||||
log(" On Windows, this pauses yosys until the viewer exits.\n");
|
||||
log("\n");
|
||||
log(" -format <format>\n");
|
||||
log(" Generate a graphics file in the specified format. Use 'dot' to just\n");
|
||||
|
@ -622,7 +623,7 @@ struct ShowPass : public Pass {
|
|||
log(" assigned to each unique value of this attribute.\n");
|
||||
log("\n");
|
||||
log(" -width\n");
|
||||
log(" annotate busses with a label indicating the width of the bus.\n");
|
||||
log(" annotate buses with a label indicating the width of the bus.\n");
|
||||
log("\n");
|
||||
log(" -signed\n");
|
||||
log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n");
|
||||
|
@ -645,7 +646,7 @@ struct ShowPass : public Pass {
|
|||
log(" do not add the module name as graph title to the dot file\n");
|
||||
log("\n");
|
||||
log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n");
|
||||
log("specified, 'xdot' is used to display the schematic.\n");
|
||||
log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n");
|
||||
log("\n");
|
||||
log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n");
|
||||
log("unless another prefix is specified using -prefix <prefix>.\n");
|
||||
|
@ -655,7 +656,7 @@ struct ShowPass : public Pass {
|
|||
log("the 'show' command is executed.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Generating Graphviz representation of design.\n");
|
||||
log_push();
|
||||
|
@ -817,14 +818,30 @@ struct ShowPass : public Pass {
|
|||
log_cmd_error("Nothing there to show.\n");
|
||||
|
||||
if (format != "dot" && !format.empty()) {
|
||||
std::string cmd = stringf("dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'", format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str());
|
||||
#ifdef _WIN32
|
||||
// system()/cmd.exe does not understand single quotes on Windows.
|
||||
#define DOT_CMD "dot -T%s \"%s\" > \"%s.new\" && move \"%s.new\" \"%s\""
|
||||
#else
|
||||
#define DOT_CMD "dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'"
|
||||
#endif
|
||||
std::string cmd = stringf(DOT_CMD, format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str());
|
||||
#undef DOT_CMD
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
}
|
||||
|
||||
if (!viewer_exe.empty()) {
|
||||
std::string cmd = stringf("%s '%s' &", viewer_exe.c_str(), out_file.c_str());
|
||||
#ifdef _WIN32
|
||||
// system()/cmd.exe does not understand single quotes nor
|
||||
// background tasks on Windows. So we have to pause yosys
|
||||
// until the viewer exits.
|
||||
#define VIEW_CMD "%s \"%s\""
|
||||
#else
|
||||
#define VIEW_CMD "%s '%s' &"
|
||||
#endif
|
||||
std::string cmd = stringf(VIEW_CMD, viewer_exe.c_str(), out_file.c_str());
|
||||
#undef VIEW_CMD
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
|
|
|
@ -247,7 +247,7 @@ struct SpliceWorker
|
|||
|
||||
struct SplicePass : public Pass {
|
||||
SplicePass() : Pass("splice", "create explicit splicing cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -288,7 +288,7 @@ struct SplicePass : public Pass {
|
|||
log("by selected wires are rewired.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool sel_by_cell = false;
|
||||
bool sel_by_wire = false;
|
||||
|
|
|
@ -87,7 +87,7 @@ struct SplitnetsWorker
|
|||
|
||||
struct SplitnetsPass : public Pass {
|
||||
SplitnetsPass() : Pass("splitnets", "split up multi-bit nets") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -109,7 +109,7 @@ struct SplitnetsPass : public Pass {
|
|||
log(" and split nets so that no driver drives only part of a net.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_ports = false;
|
||||
bool flag_driver = false;
|
||||
|
|
|
@ -209,7 +209,7 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil
|
|||
|
||||
struct StatPass : public Pass {
|
||||
StatPass() : Pass("stat", "print some statistics") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -231,7 +231,7 @@ struct StatPass : public Pass {
|
|||
log(" e.g. $add_8 for an 8 bit wide $add cell.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Printing statistics.\n");
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct TeePass : public Pass {
|
||||
TeePass() : Pass("tee", "redirect command output to file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -37,7 +37,7 @@ struct TeePass : public Pass {
|
|||
log("specified logfile(s).\n");
|
||||
log("\n");
|
||||
log(" -q\n");
|
||||
log(" Do not print output to the normal destination (console and/or log file)\n");
|
||||
log(" Do not print output to the normal destination (console and/or log file).\n");
|
||||
log("\n");
|
||||
log(" -o logfile\n");
|
||||
log(" Write output to this file, truncate if exists.\n");
|
||||
|
@ -46,10 +46,10 @@ struct TeePass : public Pass {
|
|||
log(" Write output to this file, append if exists.\n");
|
||||
log("\n");
|
||||
log(" +INT, -INT\n");
|
||||
log(" Add/subract INT from the -v setting for this command.\n");
|
||||
log(" Add/subtract INT from the -v setting for this command.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::vector<FILE*> backup_log_files, files_to_close;
|
||||
int backup_log_verbose_level = log_verbose_level;
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct TorderPass : public Pass {
|
||||
TorderPass() : Pass("torder", "print cells in topological order") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -43,7 +43,7 @@ struct TorderPass : public Pass {
|
|||
log(" are not used in topological sorting. this option deactivates that.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool noautostop = false;
|
||||
dict<IdString, pool<IdString>> stop_db;
|
||||
|
|
|
@ -25,34 +25,34 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct TraceMonitor : public RTLIL::Monitor
|
||||
{
|
||||
virtual void notify_module_add(RTLIL::Module *module) YS_OVERRIDE
|
||||
void notify_module_add(RTLIL::Module *module) YS_OVERRIDE
|
||||
{
|
||||
log("#TRACE# Module add: %s\n", log_id(module));
|
||||
}
|
||||
|
||||
virtual void notify_module_del(RTLIL::Module *module) YS_OVERRIDE
|
||||
void notify_module_del(RTLIL::Module *module) YS_OVERRIDE
|
||||
{
|
||||
log("#TRACE# Module delete: %s\n", log_id(module));
|
||||
}
|
||||
|
||||
virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE
|
||||
void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE
|
||||
{
|
||||
log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig));
|
||||
}
|
||||
|
||||
virtual void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE
|
||||
void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE
|
||||
{
|
||||
log("#TRACE# Connection in module %s: %s = %s\n", log_id(module), log_signal(sigsig.first), log_signal(sigsig.second));
|
||||
}
|
||||
|
||||
virtual void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE
|
||||
void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE
|
||||
{
|
||||
log("#TRACE# New connections in module %s:\n", log_id(module));
|
||||
for (auto &sigsig : sigsig_vec)
|
||||
log("## %s = %s\n", log_signal(sigsig.first), log_signal(sigsig.second));
|
||||
}
|
||||
|
||||
virtual void notify_blackout(RTLIL::Module *module) YS_OVERRIDE
|
||||
void notify_blackout(RTLIL::Module *module) YS_OVERRIDE
|
||||
{
|
||||
log("#TRACE# Blackout in module %s:\n", log_id(module));
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ struct TraceMonitor : public RTLIL::Monitor
|
|||
|
||||
struct TracePass : public Pass {
|
||||
TracePass() : Pass("trace", "redirect command output to file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -70,7 +70,7 @@ struct TracePass : public Pass {
|
|||
log("the design in real time.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
|
@ -95,4 +95,3 @@ struct TracePass : public Pass {
|
|||
} TracePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct WriteFileFrontend : public Frontend {
|
||||
WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -44,7 +44,7 @@ struct WriteFileFrontend : public Frontend {
|
|||
log(" EOT\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*)
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
|
||||
{
|
||||
bool append_mode = false;
|
||||
std::string output_filename;
|
||||
|
|
|
@ -9,4 +9,4 @@ OBJS += passes/equiv/equiv_induct.o
|
|||
OBJS += passes/equiv/equiv_struct.o
|
||||
OBJS += passes/equiv/equiv_purge.o
|
||||
OBJS += passes/equiv/equiv_mark.o
|
||||
|
||||
OBJS += passes/equiv/equiv_opt.o
|
||||
|
|
|
@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct EquivAddPass : public Pass {
|
||||
EquivAddPass() : Pass("equiv_add", "add a $equiv cell") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -39,7 +39,7 @@ struct EquivAddPass : public Pass {
|
|||
log("This command adds $equiv cells for the ports of the specified cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool try_mode = false;
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ struct EquivInductWorker
|
|||
|
||||
struct EquivInductPass : public Pass {
|
||||
EquivInductPass() : Pass("equiv_induct", "proving $equiv cells using temporal induction") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -192,7 +192,7 @@ struct EquivInductPass : public Pass {
|
|||
log("after reset.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
int success_counter = 0;
|
||||
bool model_undef = false;
|
||||
|
|
|
@ -40,6 +40,16 @@ struct EquivMakeWorker
|
|||
pool<SigBit> undriven_bits;
|
||||
SigMap assign_map;
|
||||
|
||||
dict<SigBit, pool<Cell*>> bit2driven; // map: bit <--> and its driven cells
|
||||
|
||||
CellTypes comb_ct;
|
||||
|
||||
EquivMakeWorker()
|
||||
{
|
||||
comb_ct.setup_internals();
|
||||
comb_ct.setup_stdcells();
|
||||
}
|
||||
|
||||
void read_blacklists()
|
||||
{
|
||||
for (auto fn : blacklists)
|
||||
|
@ -278,16 +288,31 @@ struct EquivMakeWorker
|
|||
}
|
||||
}
|
||||
|
||||
init_bit2driven();
|
||||
|
||||
pool<Cell*> visited_cells;
|
||||
for (auto c : cells_list)
|
||||
for (auto &conn : c->connections())
|
||||
if (!ct.cell_output(c->type, conn.first)) {
|
||||
SigSpec old_sig = assign_map(conn.second);
|
||||
SigSpec new_sig = rd_signal_map(old_sig);
|
||||
if (old_sig != new_sig) {
|
||||
log("Changing input %s of cell %s (%s): %s -> %s\n",
|
||||
log_id(conn.first), log_id(c), log_id(c->type),
|
||||
log_signal(old_sig), log_signal(new_sig));
|
||||
c->setPort(conn.first, new_sig);
|
||||
|
||||
if(old_sig != new_sig) {
|
||||
SigSpec tmp_sig = old_sig;
|
||||
for (int i = 0; i < GetSize(old_sig); i++) {
|
||||
SigBit old_bit = old_sig[i], new_bit = new_sig[i];
|
||||
|
||||
visited_cells.clear();
|
||||
if (check_signal_in_fanout(visited_cells, old_bit, new_bit))
|
||||
continue;
|
||||
|
||||
log("Changing input %s of cell %s (%s): %s -> %s\n",
|
||||
log_id(conn.first), log_id(c), log_id(c->type),
|
||||
log_signal(old_bit), log_signal(new_bit));
|
||||
|
||||
tmp_sig[i] = new_bit;
|
||||
}
|
||||
c->setPort(conn.first, tmp_sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,6 +403,57 @@ struct EquivMakeWorker
|
|||
}
|
||||
}
|
||||
|
||||
void init_bit2driven()
|
||||
{
|
||||
for (auto cell : equiv_mod->cells()) {
|
||||
if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_"))
|
||||
continue;
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (yosys_celltypes.cell_input(cell->type, conn.first))
|
||||
for (auto bit : assign_map(conn.second))
|
||||
{
|
||||
bit2driven[bit].insert(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool check_signal_in_fanout(pool<Cell*> & visited_cells, SigBit source_bit, SigBit target_bit)
|
||||
{
|
||||
if (source_bit == target_bit)
|
||||
return true;
|
||||
|
||||
if (bit2driven.count(source_bit) == 0)
|
||||
return false;
|
||||
|
||||
auto driven_cells = bit2driven.at(source_bit);
|
||||
for (auto driven_cell: driven_cells)
|
||||
{
|
||||
bool is_comb = comb_ct.cell_known(driven_cell->type);
|
||||
if (!is_comb)
|
||||
continue;
|
||||
|
||||
if (visited_cells.count(driven_cell) > 0)
|
||||
continue;
|
||||
visited_cells.insert(driven_cell);
|
||||
|
||||
for (auto &conn: driven_cell->connections())
|
||||
{
|
||||
if (yosys_celltypes.cell_input(driven_cell->type, conn.first))
|
||||
continue;
|
||||
|
||||
for (auto bit: conn.second) {
|
||||
bool is_in_fanout = check_signal_in_fanout(visited_cells, bit, target_bit);
|
||||
if (is_in_fanout == true)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
copy_to_equiv();
|
||||
|
@ -390,7 +466,7 @@ struct EquivMakeWorker
|
|||
|
||||
struct EquivMakePass : public Pass {
|
||||
EquivMakePass() : Pass("equiv_make", "prepare a circuit for equivalence checking") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -415,7 +491,7 @@ struct EquivMakePass : public Pass {
|
|||
log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
EquivMakeWorker worker;
|
||||
worker.ct.setup(design);
|
||||
|
|
|
@ -204,7 +204,7 @@ struct EquivMarkWorker
|
|||
|
||||
struct EquivMarkPass : public Pass {
|
||||
EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -216,7 +216,7 @@ struct EquivMarkPass : public Pass {
|
|||
log("wires and cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing EQUIV_MARK pass.\n");
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ struct EquivMiterWorker
|
|||
|
||||
struct EquivMiterPass : public Pass {
|
||||
EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -282,7 +282,7 @@ struct EquivMiterPass : public Pass {
|
|||
log(" Create compare logic that handles undefs correctly\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
EquivMiterWorker worker;
|
||||
worker.ct.setup(design);
|
||||
|
|
157
passes/equiv/equiv_opt.cc
Normal file
157
passes/equiv/equiv_opt.cc
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2018 whitequark <whitequark@whitequark.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"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivOptPass:public ScriptPass
|
||||
{
|
||||
EquivOptPass() : ScriptPass("equiv_opt", "prove equivalence for optimized circuit") { }
|
||||
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" equiv_opt [options] [command]\n");
|
||||
log("\n");
|
||||
log("This command checks circuit equivalence before and after an optimization pass.\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 the start of the command list, and empty to\n");
|
||||
log(" label is synonymous to the end of the command list.\n");
|
||||
log("\n");
|
||||
log(" -map <filename>\n");
|
||||
log(" expand the modules in this file before proving equivalence. this is\n");
|
||||
log(" useful for handling architecture-specific primitives.\n");
|
||||
log("\n");
|
||||
log(" -assert\n");
|
||||
log(" produce an error if the circuits are not equivalent\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this verification command:\n");
|
||||
help_script();
|
||||
log("\n");
|
||||
}
|
||||
|
||||
std::string command, techmap_opts;
|
||||
bool assert;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
command = "";
|
||||
techmap_opts = "";
|
||||
assert = false;
|
||||
}
|
||||
|
||||
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
|
||||
{
|
||||
string run_from, run_to;
|
||||
clear_flags();
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-run" && argidx + 1 < args.size()) {
|
||||
size_t pos = args[argidx + 1].find(':');
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
run_from = args[++argidx].substr(0, pos);
|
||||
run_to = args[argidx].substr(pos + 1);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-map" && argidx + 1 < args.size()) {
|
||||
techmap_opts += " -map " + args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-assert") {
|
||||
assert = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (; argidx < args.size(); argidx++) {
|
||||
if (command.empty()) {
|
||||
if (args[argidx].substr(0, 1) == "-")
|
||||
cmd_error(args, argidx, "Unknown option.");
|
||||
} else {
|
||||
command += " ";
|
||||
}
|
||||
command += args[argidx];
|
||||
}
|
||||
|
||||
if (command.empty())
|
||||
log_cmd_error("No optimization pass specified!\n");
|
||||
|
||||
if (!design->full_selection())
|
||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||
|
||||
log_header(design, "Executing EQUIV_OPT pass.\n");
|
||||
log_push();
|
||||
|
||||
run_script(design, run_from, run_to);
|
||||
|
||||
log_pop();
|
||||
}
|
||||
|
||||
void script() YS_OVERRIDE
|
||||
{
|
||||
if (check_label("run_pass")) {
|
||||
run("hierarchy -auto-top");
|
||||
run("design -save preopt");
|
||||
if (help_mode)
|
||||
run("[command]");
|
||||
else
|
||||
run(command);
|
||||
run("design -stash postopt");
|
||||
}
|
||||
|
||||
if (check_label("prepare")) {
|
||||
run("design -copy-from preopt -as gold A:top");
|
||||
run("design -copy-from postopt -as gate A:top");
|
||||
}
|
||||
|
||||
if ((!techmap_opts.empty() || help_mode) && check_label("techmap", "(only with -map)")) {
|
||||
string opts;
|
||||
if (help_mode)
|
||||
opts = " -map <filename> ...";
|
||||
else
|
||||
opts = techmap_opts;
|
||||
run("techmap -D EQUIV -autoproc" + opts);
|
||||
}
|
||||
|
||||
if (check_label("prove")) {
|
||||
run("equiv_make gold gate equiv");
|
||||
run("equiv_induct equiv");
|
||||
if (help_mode)
|
||||
run("equiv_status [-assert] equiv");
|
||||
else if (assert)
|
||||
run("equiv_status -assert equiv");
|
||||
else
|
||||
run("equiv_status equiv");
|
||||
}
|
||||
|
||||
if (check_label("restore")) {
|
||||
run("design -load preopt");
|
||||
}
|
||||
}
|
||||
} EquivOptPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -176,7 +176,7 @@ struct EquivPurgeWorker
|
|||
|
||||
struct EquivPurgePass : public Pass {
|
||||
EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -187,7 +187,7 @@ struct EquivPurgePass : public Pass {
|
|||
log("ports as needed.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing EQUIV_PURGE pass.\n");
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct EquivRemovePass : public Pass {
|
||||
EquivRemovePass() : Pass("equiv_remove", "remove $equiv cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -40,7 +40,7 @@ struct EquivRemovePass : public Pass {
|
|||
log(" keep gate circuit\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool mode_gold = false;
|
||||
bool mode_gate = false;
|
||||
|
|
|
@ -273,7 +273,7 @@ struct EquivSimpleWorker
|
|||
|
||||
struct EquivSimplePass : public Pass {
|
||||
EquivSimplePass() : Pass("equiv_simple", "try proving simple $equiv instances") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -298,7 +298,7 @@ struct EquivSimplePass : public Pass {
|
|||
log(" the max. number of time steps to be considered (default = 1)\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool verbose = false, short_cones = false, model_undef = false, nogroup = false;
|
||||
int success_counter = 0;
|
||||
|
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct EquivStatusPass : public Pass {
|
||||
EquivStatusPass() : Pass("equiv_status", "print status of equivalent checking module") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -36,7 +36,7 @@ struct EquivStatusPass : public Pass {
|
|||
log(" produce an error if any unproven $equiv cell is found\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool assert_mode = false;
|
||||
int unproven_count = 0;
|
||||
|
|
|
@ -283,7 +283,7 @@ struct EquivStructWorker
|
|||
|
||||
struct EquivStructPass : public Pass {
|
||||
EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -314,7 +314,7 @@ struct EquivStructPass : public Pass {
|
|||
log(" maximum number of iterations to run before aborting\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
pool<IdString> fwonly_cells({ "$equiv" });
|
||||
bool mode_icells = false;
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct FsmPass : public Pass {
|
||||
FsmPass() : Pass("fsm", "extract and optimize finite state machines") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -68,7 +68,7 @@ struct FsmPass : public Pass {
|
|||
log(" passed through to fsm_recode pass\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_nomap = false;
|
||||
bool flag_norecode = false;
|
||||
|
|
|
@ -196,13 +196,13 @@ static void detect_fsm(RTLIL::Wire *wire)
|
|||
vector<string> warnings;
|
||||
|
||||
if (is_module_port)
|
||||
warnings.push_back("Forcing fsm recoding on module port might result in larger circuit.\n");
|
||||
warnings.push_back("Forcing FSM recoding on module port might result in larger circuit.\n");
|
||||
|
||||
if (!looks_like_good_state_reg)
|
||||
warnings.push_back("Users of state reg look like fsm recoding might result in larger circuit.\n");
|
||||
warnings.push_back("Users of state reg look like FSM recoding might result in larger circuit.\n");
|
||||
|
||||
if (has_init_attr)
|
||||
warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!");
|
||||
warnings.push_back("Initialization value on FSM state register is ignored. Possible simulation-synthesis mismatch!\n");
|
||||
|
||||
if (!looks_like_state_reg)
|
||||
warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n");
|
||||
|
@ -236,7 +236,7 @@ static void detect_fsm(RTLIL::Wire *wire)
|
|||
log(" Users of register don't seem to benefit from recoding.\n");
|
||||
|
||||
if (has_init_attr)
|
||||
log(" Register has an initialization value.");
|
||||
log(" Register has an initialization value.\n");
|
||||
|
||||
if (is_self_resetting)
|
||||
log(" Circuit seems to be self-resetting.\n");
|
||||
|
@ -245,7 +245,7 @@ static void detect_fsm(RTLIL::Wire *wire)
|
|||
|
||||
struct FsmDetectPass : public Pass {
|
||||
FsmDetectPass() : Pass("fsm_detect", "finding FSMs in design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -261,7 +261,7 @@ struct FsmDetectPass : public Pass {
|
|||
log("'fsm_encoding' attribute to \"none\".\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -265,7 +265,7 @@ struct FsmExpand
|
|||
|
||||
struct FsmExpandPass : public Pass {
|
||||
FsmExpandPass() : Pass("fsm_expand", "expand FSM cells by merging logic into it") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -279,7 +279,7 @@ struct FsmExpandPass : public Pass {
|
|||
log("word-wide cells. Call with -full to consider all cells for merging.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool full_mode = false;
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::st
|
|||
*/
|
||||
struct FsmExportPass : public Pass {
|
||||
FsmExportPass() : Pass("fsm_export", "exporting FSMs to KISS2 files") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -143,7 +143,7 @@ struct FsmExportPass : public Pass {
|
|||
log(" use binary state encoding as state names instead of s0, s1, ...\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it;
|
||||
std::string arg;
|
||||
|
|
|
@ -178,7 +178,7 @@ undef_bit_in_next_state:
|
|||
log_state_in = fsm_data.state_table.at(state_in);
|
||||
|
||||
if (states.count(ce.values_map(ce.assign_map(dff_in)).as_const()) == 0) {
|
||||
log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transistion!>%s\n",
|
||||
log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transition!>%s\n",
|
||||
log_signal(log_state_in), log_signal(tr.ctrl_in),
|
||||
log_signal(ce.values_map(ce.assign_map(dff_in))), log_signal(tr.ctrl_out),
|
||||
undef_bit_in_next_state_mode ? " SHORTENED" : "");
|
||||
|
@ -194,7 +194,7 @@ undef_bit_in_next_state:
|
|||
log_signal(log_state_in), log_signal(tr.ctrl_in),
|
||||
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
|
||||
} else {
|
||||
log(" transition: %10s %s -> %10s %s <ignored undef transistion!>\n",
|
||||
log(" transition: %10s %s -> %10s %s <ignored undef transition!>\n",
|
||||
log_signal(log_state_in), log_signal(tr.ctrl_in),
|
||||
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
|
||||
}
|
||||
|
@ -401,7 +401,7 @@ static void extract_fsm(RTLIL::Wire *wire)
|
|||
|
||||
struct FsmExtractPass : public Pass {
|
||||
FsmExtractPass() : Pass("fsm_extract", "extracting FSMs in design") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -417,7 +417,7 @@ struct FsmExtractPass : public Pass {
|
|||
log("'opt_clean' pass to eliminate this signal.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -30,7 +30,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct FsmInfoPass : public Pass {
|
||||
FsmInfoPass() : Pass("fsm_info", "print information on finite state machines") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -41,7 +41,7 @@ struct FsmInfoPass : public Pass {
|
|||
log("pass so that this information is included in the synthesis log file.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -322,7 +322,7 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module)
|
|||
|
||||
struct FsmMapPass : public Pass {
|
||||
FsmMapPass() : Pass("fsm_map", "mapping FSMs to basic logic") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -331,7 +331,7 @@ struct FsmMapPass : public Pass {
|
|||
log("This pass translates FSM cells to flip-flops and logic.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -72,7 +72,8 @@ struct FsmOpt
|
|||
|
||||
new_transition_table.swap(fsm_data.transition_table);
|
||||
new_state_table.swap(fsm_data.state_table);
|
||||
fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
|
||||
if (fsm_data.reset_state != -1)
|
||||
fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +324,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct FsmOptPass : public Pass {
|
||||
FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -334,7 +335,7 @@ struct FsmOptPass : public Pass {
|
|||
log("combination with the 'opt_clean' pass (see also 'help fsm').\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -126,7 +126,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs
|
|||
|
||||
struct FsmRecodePass : public Pass {
|
||||
FsmRecodePass() : Pass("fsm_recode", "recoding finite state machines") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -151,7 +151,7 @@ struct FsmRecodePass : public Pass {
|
|||
log(" .map <old_bitpattern> <new_bitpattern>\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
FILE *fm_set_fsm_file = NULL;
|
||||
FILE *encfile = NULL;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.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
|
||||
|
@ -139,25 +140,73 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
}
|
||||
}
|
||||
|
||||
// Return the "basic" type for an array item.
|
||||
std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
|
||||
std::string basicType = celltype;
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
int pos_idx = celltype.find_first_of(':');
|
||||
int pos_num = celltype.find_first_of(':', pos_idx + 1);
|
||||
int pos_type = celltype.find_first_of(':', pos_num + 1);
|
||||
basicType = celltype.substr(pos_type + 1);
|
||||
if (pos != nullptr) {
|
||||
pos[0] = pos_idx;
|
||||
pos[1] = pos_num;
|
||||
pos[2] = pos_type;
|
||||
}
|
||||
}
|
||||
return basicType;
|
||||
}
|
||||
|
||||
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
|
||||
{
|
||||
bool did_something = false;
|
||||
std::map<RTLIL::Cell*, std::pair<int, int>> array_cells;
|
||||
std::string filename;
|
||||
|
||||
bool has_interface_ports = false;
|
||||
|
||||
// If any of the ports are actually interface ports, we will always need to
|
||||
// reprocess the module:
|
||||
if(!module->get_bool_attribute("\\interfaces_replaced_in_module")) {
|
||||
for (auto &wire : module->wires_) {
|
||||
if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface"))
|
||||
has_interface_ports = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Always keep track of all derived interfaces available in the current module in 'interfaces_in_module':
|
||||
dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
|
||||
for (auto &cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if(cell->get_bool_attribute("\\is_interface")) {
|
||||
RTLIL::Module *intf_module = design->modules_[cell->type];
|
||||
interfaces_in_module[cell->name] = intf_module;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
bool has_interfaces_not_found = false;
|
||||
|
||||
std::vector<RTLIL::IdString> connections_to_remove;
|
||||
std::vector<RTLIL::IdString> connections_to_add_name;
|
||||
std::vector<RTLIL::SigSpec> connections_to_add_signal;
|
||||
|
||||
if (cell->type.substr(0, 7) == "$array:") {
|
||||
int pos_idx = cell->type.str().find_first_of(':');
|
||||
int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
|
||||
int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
|
||||
int pos[3];
|
||||
basic_cell_type(cell->type.str(), pos);
|
||||
int pos_idx = pos[0];
|
||||
int pos_num = pos[1];
|
||||
int pos_type = pos[2];
|
||||
int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
|
||||
int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
|
||||
array_cells[cell] = std::pair<int, int>(idx, num);
|
||||
cell->type = cell->type.str().substr(pos_type + 1);
|
||||
}
|
||||
dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
|
||||
dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
|
||||
|
||||
if (design->modules_.count(cell->type) == 0)
|
||||
{
|
||||
|
@ -200,11 +249,85 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
if (design->modules_.count(cell->type) == 0)
|
||||
log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
|
||||
did_something = true;
|
||||
} else
|
||||
} else {
|
||||
|
||||
RTLIL::Module *mod = design->module(cell->type);
|
||||
|
||||
// Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to
|
||||
// some lists, so that the ports for sub-modules can be replaced further down:
|
||||
for (auto &conn : cell->connections()) {
|
||||
if(mod->wires_.count(conn.first) != 0 && mod->wire(conn.first)->get_bool_attribute("\\is_interface")) { // Check if the connection is present as an interface in the sub-module's port list
|
||||
//const pool<string> &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_type");
|
||||
//for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness)
|
||||
//}
|
||||
|
||||
// Find if the sub-module has set a modport for the current interface connection:
|
||||
const pool<string> &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_modport");
|
||||
std::string interface_modport = "";
|
||||
for (auto &d : interface_modport_pool) {
|
||||
interface_modport = "\\" + d;
|
||||
}
|
||||
if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute("\\is_interface")) { // Check if the connected wire is a potential interface in the parent module
|
||||
std::string interface_name_str = conn.second.bits()[0].wire->name.str();
|
||||
interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
|
||||
interface_name_str = "\\" + interface_name_str;
|
||||
RTLIL::IdString interface_name = interface_name_str;
|
||||
bool not_found_interface = false;
|
||||
if(module->get_bool_attribute("\\interfaces_replaced_in_module")) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either
|
||||
// Check if the interface instance is present in module:
|
||||
// Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'.
|
||||
// Check for both of them here
|
||||
int nexactmatch = interfaces_in_module.count(interface_name) > 0;
|
||||
std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
|
||||
RTLIL::IdString interface_name2 = interface_name_str2;
|
||||
int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
|
||||
if (nexactmatch > 0 || nmatch2 > 0) {
|
||||
if (nexactmatch != 0) // Choose the one with the plain name if it exists
|
||||
interface_name2 = interface_name;
|
||||
RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
|
||||
for (auto &mod_wire : mod_replace_ports->wires_) { // Go over all wires in interface, and add replacements to lists.
|
||||
std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire.first);
|
||||
std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire.first);
|
||||
connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
|
||||
if(module->wires_.count(signal_name2) == 0) {
|
||||
log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name));
|
||||
}
|
||||
else {
|
||||
RTLIL::Wire *wire_in_parent = module->wire(signal_name2);
|
||||
connections_to_add_signal.push_back(wire_in_parent);
|
||||
}
|
||||
}
|
||||
connections_to_remove.push_back(conn.first);
|
||||
interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
|
||||
|
||||
// Add modports to a dict which will be passed to AstModule::derive
|
||||
if (interface_modport != "") {
|
||||
modports_used_in_submodule[conn.first] = interface_modport;
|
||||
}
|
||||
}
|
||||
else not_found_interface = true;
|
||||
}
|
||||
else not_found_interface = true;
|
||||
// If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found"
|
||||
// which will delay the expansion of this cell:
|
||||
if (not_found_interface) {
|
||||
// If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error:
|
||||
if(!(module->get_bool_attribute("\\cells_not_processed"))) {
|
||||
log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module));
|
||||
}
|
||||
else {
|
||||
// Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop:
|
||||
has_interfaces_not_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
if (flag_check || flag_simcheck)
|
||||
{
|
||||
RTLIL::Module *mod = design->module(cell->type);
|
||||
for (auto &conn : cell->connections())
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
|
||||
int id = atoi(conn.first.c_str()+1);
|
||||
if (id <= 0 || id > GetSize(mod->ports))
|
||||
|
@ -213,11 +336,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
} else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0)
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n",
|
||||
log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first));
|
||||
}
|
||||
for (auto ¶m : cell->parameters)
|
||||
if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n",
|
||||
log_id(cell->type), log_id(module), log_id(cell), log_id(param.first));
|
||||
|
||||
}
|
||||
}
|
||||
RTLIL::Module *mod = design->modules_[cell->type];
|
||||
|
||||
if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
|
||||
if (flag_simcheck)
|
||||
|
@ -226,14 +353,61 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
continue;
|
||||
}
|
||||
|
||||
if (cell->parameters.size() == 0)
|
||||
// If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
|
||||
if(has_interfaces_not_found) {
|
||||
did_something = true; // waiting for interfaces to be handled
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Module *mod = design->modules_[cell->type];
|
||||
cell->type = mod->derive(design, cell->parameters);
|
||||
// Do the actual replacements of the SV interface port connection with the individual signal connections:
|
||||
for(unsigned int i=0;i<connections_to_add_name.size();i++) {
|
||||
cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
|
||||
}
|
||||
// Remove the connection for the interface itself:
|
||||
for(unsigned int i=0;i<connections_to_remove.size();i++) {
|
||||
cell->connections_.erase(connections_to_remove[i]);
|
||||
}
|
||||
|
||||
// If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type
|
||||
// for the cell:
|
||||
if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute("\\module_not_derived")))) {
|
||||
// If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:",
|
||||
// so that the signals of the interface are added to the parent module.
|
||||
if (mod->get_bool_attribute("\\is_interface")) {
|
||||
goto handle_interface_instance;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule);
|
||||
cell->parameters.clear();
|
||||
did_something = true;
|
||||
|
||||
handle_interface_instance:
|
||||
|
||||
// We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter
|
||||
// an interface instance:
|
||||
if (mod->get_bool_attribute("\\is_interface") && cell->get_bool_attribute("\\module_not_derived")) {
|
||||
cell->set_bool_attribute("\\is_interface");
|
||||
RTLIL::Module *derived_module = design->modules_[cell->type];
|
||||
interfaces_in_module[cell->name] = derived_module;
|
||||
did_something = true;
|
||||
}
|
||||
// We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell)
|
||||
cell->attributes.erase("\\module_not_derived");
|
||||
}
|
||||
// Clear the attribute 'cells_not_processed' such that it can be known that we
|
||||
// have been through all cells at least once, and that we can know whether
|
||||
// to flag an error because of interface instances not found:
|
||||
module->attributes.erase("\\cells_not_processed");
|
||||
|
||||
|
||||
// If any interface instances or interface ports were found in the module, we need to rederive it completely:
|
||||
if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute("\\interfaces_replaced_in_module")) {
|
||||
module->reprocess_module(design, interfaces_in_module);
|
||||
return did_something;
|
||||
}
|
||||
|
||||
|
||||
for (auto &it : array_cells)
|
||||
{
|
||||
|
@ -284,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
|
|||
for (auto cell : mod->cells()) {
|
||||
std::string celltype = cell->type.str();
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
int pos_idx = celltype.find_first_of(':');
|
||||
int pos_num = celltype.find_first_of(':', pos_idx + 1);
|
||||
int pos_type = celltype.find_first_of(':', pos_num + 1);
|
||||
celltype = celltype.substr(pos_type + 1);
|
||||
celltype = basic_cell_type(celltype);
|
||||
}
|
||||
if (design->module(celltype))
|
||||
hierarchy_worker(design, used, design->module(celltype), indent+4);
|
||||
|
@ -303,6 +474,20 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
|
|||
for (auto &it : design->modules_)
|
||||
if (used.count(it.second) == 0)
|
||||
del_modules.push_back(it.second);
|
||||
else {
|
||||
// Now all interface ports must have been exploded, and it is hence
|
||||
// safe to delete all of the remaining dummy interface ports:
|
||||
pool<RTLIL::Wire*> del_wires;
|
||||
for(auto &wire : it.second->wires_) {
|
||||
if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) {
|
||||
del_wires.insert(wire.second);
|
||||
}
|
||||
}
|
||||
if (del_wires.size() > 0) {
|
||||
it.second->remove(del_wires);
|
||||
it.second->fixup_ports();
|
||||
}
|
||||
}
|
||||
|
||||
int del_counter = 0;
|
||||
for (auto mod : del_modules) {
|
||||
|
@ -333,17 +518,41 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
|
|||
if (db.count(module) == 0) {
|
||||
int score = 0;
|
||||
db[module] = 0;
|
||||
for (auto cell : module->cells())
|
||||
if (design->module(cell->type))
|
||||
score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
|
||||
for (auto cell : module->cells()) {
|
||||
std::string celltype = cell->type.str();
|
||||
// Is this an array instance
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
celltype = basic_cell_type(celltype);
|
||||
}
|
||||
// Is this cell a module instance?
|
||||
auto instModule = design->module(celltype);
|
||||
// If there is no instance for this, issue a warning.
|
||||
if (instModule != nullptr) {
|
||||
score = max(score, find_top_mod_score(design, instModule, db) + 1);
|
||||
}
|
||||
}
|
||||
db[module] = score;
|
||||
}
|
||||
return db.at(module);
|
||||
}
|
||||
|
||||
RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod)
|
||||
{
|
||||
if(top_mod != NULL && top_mod->get_bool_attribute("\\initial_top"))
|
||||
return top_mod;
|
||||
else {
|
||||
for (auto mod : design->modules()) {
|
||||
if (mod->get_bool_attribute("\\top")) {
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct HierarchyPass : public Pass {
|
||||
HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -360,7 +569,7 @@ struct HierarchyPass : public Pass {
|
|||
log(" an unknown module is used as cell type.\n");
|
||||
log("\n");
|
||||
log(" -simcheck\n");
|
||||
log(" like -check, but also thow an error if blackbox modules are\n");
|
||||
log(" like -check, but also throw an error if blackbox modules are\n");
|
||||
log(" instantiated, and throw an error if the design has no top module\n");
|
||||
log("\n");
|
||||
log(" -purge_lib\n");
|
||||
|
@ -414,7 +623,7 @@ struct HierarchyPass : public Pass {
|
|||
log("in the current design.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n");
|
||||
|
||||
|
@ -568,6 +777,14 @@ struct HierarchyPass : public Pass {
|
|||
if (flag_simcheck && top_mod == nullptr)
|
||||
log_error("Design has no top module.\n");
|
||||
|
||||
if (top_mod != NULL) {
|
||||
for (auto &mod_it : design->modules_)
|
||||
if (mod_it.second == top_mod)
|
||||
mod_it.second->attributes["\\initial_top"] = RTLIL::Const(1);
|
||||
else
|
||||
mod_it.second->attributes.erase("\\initial_top");
|
||||
}
|
||||
|
||||
bool did_something = true;
|
||||
while (did_something)
|
||||
{
|
||||
|
@ -586,19 +803,43 @@ struct HierarchyPass : public Pass {
|
|||
if (expand_module(design, module, flag_check, flag_simcheck, libdirs))
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
|
||||
// The top module might have changed if interface instances have been detected in it:
|
||||
RTLIL::Module *tmp_top_mod = check_if_top_has_changed(design, top_mod);
|
||||
if (tmp_top_mod != NULL) {
|
||||
if (tmp_top_mod != top_mod){
|
||||
top_mod = tmp_top_mod;
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete modules marked as 'to_delete':
|
||||
std::vector<RTLIL::Module *> modules_to_delete;
|
||||
for(auto &mod_it : design->modules_) {
|
||||
if (mod_it.second->get_bool_attribute("\\to_delete")) {
|
||||
modules_to_delete.push_back(mod_it.second);
|
||||
}
|
||||
}
|
||||
for(size_t i=0; i<modules_to_delete.size(); i++) {
|
||||
design->remove(modules_to_delete[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (top_mod != NULL) {
|
||||
log_header(design, "Analyzing design hierarchy..\n");
|
||||
hierarchy_clean(design, top_mod, purge_lib);
|
||||
}
|
||||
|
||||
if (top_mod != NULL) {
|
||||
for (auto &mod_it : design->modules_)
|
||||
for (auto &mod_it : design->modules_) {
|
||||
if (mod_it.second == top_mod)
|
||||
mod_it.second->attributes["\\top"] = RTLIL::Const(1);
|
||||
else
|
||||
mod_it.second->attributes.erase("\\top");
|
||||
mod_it.second->attributes.erase("\\initial_top");
|
||||
}
|
||||
}
|
||||
|
||||
if (!nokeep_asserts) {
|
||||
|
@ -669,7 +910,7 @@ struct HierarchyPass : public Pass {
|
|||
if (m == nullptr)
|
||||
continue;
|
||||
|
||||
if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
|
||||
if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
|
||||
IdString new_m_name = m->derive(design, cell->parameters, true);
|
||||
if (new_m_name.empty())
|
||||
continue;
|
||||
|
|
|
@ -269,7 +269,7 @@ struct SubmodWorker
|
|||
|
||||
struct SubmodPass : public Pass {
|
||||
SubmodPass() : Pass("submod", "moving part of a module to a new submodule") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -297,7 +297,7 @@ struct SubmodPass : public Pass {
|
|||
log("with -copy to not modify the source module.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n");
|
||||
log_push();
|
||||
|
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct UniquifyPass : public Pass {
|
||||
UniquifyPass() : Pass("uniquify", "create unique copies of modules") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -41,7 +41,7 @@ struct UniquifyPass : public Pass {
|
|||
log("attribute set (the 'top' module is unique implicitly).\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing UNIQUIFY pass (creating unique copies of modules).\n");
|
||||
|
||||
|
@ -87,6 +87,8 @@ struct UniquifyPass : public Pass {
|
|||
smod->name = newname;
|
||||
cell->type = newname;
|
||||
smod->set_bool_attribute("\\unique");
|
||||
if (smod->attributes.count("\\hdlname") == 0)
|
||||
smod->attributes["\\hdlname"] = string(log_id(tmod->name));
|
||||
design->add(smod);
|
||||
|
||||
did_something = true;
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct MemoryPass : public Pass {
|
||||
MemoryPass() : Pass("memory", "translate memories to basic cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -48,7 +48,7 @@ struct MemoryPass : public Pass {
|
|||
log("or multiport memory blocks if called with the -nomap option.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_nomap = false;
|
||||
bool flag_nordff = false;
|
||||
|
|
|
@ -472,8 +472,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
|
||||
std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
|
||||
std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
|
||||
std::vector<std::vector<State>> new_initdata;
|
||||
std::vector<int> shuffle_map;
|
||||
|
||||
if (cell_init)
|
||||
new_initdata.resize(mem_size);
|
||||
|
||||
for (auto &it : en_order)
|
||||
{
|
||||
auto &bits = bits_wr_en.at(it);
|
||||
|
@ -489,6 +493,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
}
|
||||
for (int j = 0; j < rd_ports; j++)
|
||||
new_rd_data[j].append(old_rd_data[j][bits[i]]);
|
||||
if (cell_init) {
|
||||
for (int j = 0; j < mem_size; j++)
|
||||
new_initdata[j].push_back(initdata[j][bits[i]]);
|
||||
}
|
||||
shuffle_map.push_back(bits[i]);
|
||||
}
|
||||
|
||||
|
@ -499,6 +507,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
}
|
||||
for (int j = 0; j < rd_ports; j++)
|
||||
new_rd_data[j].append(State::Sx);
|
||||
if (cell_init) {
|
||||
for (int j = 0; j < mem_size; j++)
|
||||
new_initdata[j].push_back(State::Sx);
|
||||
}
|
||||
shuffle_map.push_back(-1);
|
||||
}
|
||||
}
|
||||
|
@ -522,10 +534,15 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
rd_data.replace(i*mem_width, new_rd_data[i]);
|
||||
|
||||
if (cell_init) {
|
||||
for (int i = 0; i < mem_size; i++)
|
||||
initdata[i] = Const(new_initdata[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// assign write ports
|
||||
|
||||
pair<SigBit, bool> wr_clkdom;
|
||||
for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
|
||||
{
|
||||
bool clken = wr_clken[cell_port_i] == State::S1;
|
||||
|
@ -535,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
pair<SigBit, bool> clkdom(clksig, clkpol);
|
||||
if (!clken)
|
||||
clkdom = pair<SigBit, bool>(State::S1, false);
|
||||
|
||||
wr_clkdom = clkdom;
|
||||
log(" Write port #%d is in clock domain %s%s.\n",
|
||||
cell_port_i, clkdom.second ? "" : "!",
|
||||
clken ? log_signal(clkdom.first) : "~async~");
|
||||
|
@ -623,6 +640,8 @@ grow_read_ports:;
|
|||
pi.sig_addr = SigSpec();
|
||||
pi.sig_data = SigSpec();
|
||||
pi.sig_en = SigSpec();
|
||||
pi.make_outreg = false;
|
||||
pi.make_transp = false;
|
||||
}
|
||||
new_portinfos.push_back(pi);
|
||||
if (pi.dupidx == dup_count-1) {
|
||||
|
@ -700,7 +719,13 @@ grow_read_ports:;
|
|||
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
|
||||
if (match.make_transp && wr_ports <= 1) {
|
||||
pi.make_transp = true;
|
||||
enable_make_transp = true;
|
||||
if (pi.clocks != 0) {
|
||||
if (wr_ports == 1 && wr_clkdom != clkdom) {
|
||||
log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
}
|
||||
enable_make_transp = true;
|
||||
}
|
||||
} else {
|
||||
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
|
@ -895,17 +920,18 @@ grow_read_ports:;
|
|||
} else {
|
||||
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
|
||||
c->setPort(stringf("\\%sDATA", pf), bram_dout);
|
||||
|
||||
if (pi.make_outreg) {
|
||||
if (pi.make_outreg && pi.make_transp) {
|
||||
log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
|
||||
module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
|
||||
c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
|
||||
} else if (pi.make_outreg) {
|
||||
SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
|
||||
if (!pi.sig_en.empty())
|
||||
bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
|
||||
module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
|
||||
bram_dout = bram_dout_q;
|
||||
}
|
||||
|
||||
if (pi.make_transp)
|
||||
{
|
||||
} else if (pi.make_transp) {
|
||||
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
|
||||
SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
|
||||
|
@ -1120,7 +1146,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
|
|||
|
||||
struct MemoryBramPass : public Pass {
|
||||
MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -1210,7 +1236,7 @@ struct MemoryBramPass : public Pass {
|
|||
log("the data bits to accommodate the enable pattern of port A.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(vector<string> args, Design *design)
|
||||
void execute(vector<string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
rules_t rules;
|
||||
|
||||
|
|
|
@ -184,9 +184,6 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
|
|||
mem->parameters["\\OFFSET"] = Const(memory->start_offset);
|
||||
mem->parameters["\\SIZE"] = Const(memory->size);
|
||||
mem->parameters["\\ABITS"] = Const(addr_bits);
|
||||
|
||||
while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx)
|
||||
init_data.bits.pop_back();
|
||||
mem->parameters["\\INIT"] = init_data;
|
||||
|
||||
log_assert(sig_wr_clk.size() == wr_ports);
|
||||
|
@ -246,7 +243,7 @@ static void handle_module(Design *design, Module *module)
|
|||
|
||||
struct MemoryCollectPass : public Pass {
|
||||
MemoryCollectPass() : Pass("memory_collect", "creating multi-port memory cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -256,7 +253,7 @@ struct MemoryCollectPass : public Pass {
|
|||
log("memory cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
|
||||
log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto &mod_it : design->modules_)
|
||||
|
|
|
@ -41,7 +41,7 @@ struct MemoryDffWorker
|
|||
if (wire->attributes.count("\\init") == 0)
|
||||
continue;
|
||||
SigSpec sig = sigmap(wire);
|
||||
Const initval = wire->attributes.count("\\init");
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
|
||||
if (initval[i] == State::S0 || initval[i] == State::S1)
|
||||
init_bits.insert(sig[i]);
|
||||
|
@ -283,7 +283,7 @@ struct MemoryDffWorker
|
|||
|
||||
struct MemoryDffPass : public Pass {
|
||||
MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -297,7 +297,7 @@ struct MemoryDffPass : public Pass {
|
|||
log(" do not merge registers on read ports\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_wr_only = false;
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ struct MemoryMapWorker
|
|||
|
||||
struct MemoryMapPass : public Pass {
|
||||
MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -362,7 +362,7 @@ struct MemoryMapPass : public Pass {
|
|||
log("pass to word-wide DFFs and address decoders.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
|
||||
log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto mod : design->selected_modules())
|
||||
|
|
|
@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct MemoryMemxPass : public Pass {
|
||||
MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -38,7 +38,7 @@ struct MemoryMemxPass : public Pass {
|
|||
log("behavior for out-of-bounds memory reads and writes.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
|
||||
log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct MemoryNordffPass : public Pass {
|
||||
MemoryNordffPass() : Pass("memory_nordff", "extract read port FFs from memories") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -35,7 +35,7 @@ struct MemoryNordffPass : public Pass {
|
|||
log("similar to what one would get from calling memory_dff with -nordff.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing MEMORY_NORDFF pass (extracting $dff cells from $mem).\n");
|
||||
|
||||
|
|
|
@ -726,7 +726,7 @@ struct MemoryShareWorker
|
|||
|
||||
struct MemorySharePass : public Pass {
|
||||
MemorySharePass() : Pass("memory_share", "consolidate memory ports") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -752,7 +752,7 @@ struct MemorySharePass : public Pass {
|
|||
log("optimizations) such as \"share\" and \"opt_merge\".\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
|
||||
log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto module : design->selected_modules())
|
||||
|
|
|
@ -127,7 +127,7 @@ void handle_module(RTLIL::Design *design, RTLIL::Module *module)
|
|||
|
||||
struct MemoryUnpackPass : public Pass {
|
||||
MemoryUnpackPass() : Pass("memory_unpack", "unpack multi-port memory cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -137,7 +137,7 @@ struct MemoryUnpackPass : public Pass {
|
|||
log("$memwr cells. It is the counterpart to the memory_collect pass.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
|
||||
log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto &mod_it : design->modules_)
|
||||
|
|
|
@ -12,5 +12,6 @@ OBJS += passes/opt/share.o
|
|||
OBJS += passes/opt/wreduce.o
|
||||
OBJS += passes/opt/opt_demorgan.o
|
||||
OBJS += passes/opt/rmports.o
|
||||
OBJS += passes/opt/opt_lut.o
|
||||
endif
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct OptPass : public Pass {
|
||||
OptPass() : Pass("opt", "perform simple optimizations") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -63,7 +63,7 @@ struct OptPass : public Pass {
|
|||
log("\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string opt_clean_args;
|
||||
std::string opt_expr_args;
|
||||
|
|
|
@ -442,7 +442,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
|
|||
|
||||
struct OptCleanPass : public Pass {
|
||||
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -459,7 +459,7 @@ struct OptCleanPass : public Pass {
|
|||
log(" also remove internal nets if they have a public name\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool purge_mode = false;
|
||||
|
||||
|
@ -505,7 +505,7 @@ struct OptCleanPass : public Pass {
|
|||
|
||||
struct CleanPass : public Pass {
|
||||
CleanPass() : Pass("clean", "remove unused cells and wires") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -520,7 +520,7 @@ struct CleanPass : public Pass {
|
|||
log("in -purge mode between the commands.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool purge_mode = false;
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ void demorgan_worker(
|
|||
|
||||
struct OptDemorganPass : public Pass {
|
||||
OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -179,7 +179,7 @@ struct OptDemorganPass : public Pass {
|
|||
log("overall gate count of the circuit\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n");
|
||||
|
||||
|
|
|
@ -155,6 +155,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
new_b.append_bit(it.first.second);
|
||||
}
|
||||
|
||||
if (cell->type.in("$and", "$or") && i == GRP_CONST_A) {
|
||||
log(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
|
||||
module->connect(new_y, new_b);
|
||||
module->connect(new_conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);
|
||||
|
||||
c->setPort("\\A", new_a);
|
||||
|
@ -259,6 +266,22 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
|
|||
return last_bit_one;
|
||||
}
|
||||
|
||||
int get_highest_hot_index(RTLIL::SigSpec signal)
|
||||
{
|
||||
for (int i = GetSize(signal) - 1; i >= 0; i--)
|
||||
{
|
||||
if (signal[i] == RTLIL::State::S0)
|
||||
continue;
|
||||
|
||||
if (signal[i] == RTLIL::State::S1)
|
||||
return i;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if the signal has only one bit set, return the index of that bit.
|
||||
// otherwise return -1
|
||||
int get_onehot_bit_index(RTLIL::SigSpec signal)
|
||||
|
@ -1344,119 +1367,140 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
}
|
||||
}
|
||||
|
||||
// replace a<0 or a>=0 with the top bit of a
|
||||
// simplify comparisons
|
||||
if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le"))
|
||||
{
|
||||
//used to decide whether the signal needs to be negated
|
||||
bool is_lt = false;
|
||||
IdString cmp_type = cell->type;
|
||||
SigSpec var_sig = cell->getPort("\\A");
|
||||
SigSpec const_sig = cell->getPort("\\B");
|
||||
int var_width = cell->parameters["\\A_WIDTH"].as_int();
|
||||
int const_width = cell->parameters["\\B_WIDTH"].as_int();
|
||||
bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
|
||||
|
||||
//references the variable signal in the comparison
|
||||
RTLIL::SigSpec sigVar;
|
||||
|
||||
//references the constant signal in the comparison
|
||||
RTLIL::SigSpec sigConst;
|
||||
|
||||
// note that this signal must be constant for the optimization
|
||||
// to take place, but it is not checked beforehand.
|
||||
// If new passes are added, this signal must be checked for const-ness
|
||||
|
||||
//width of the variable port
|
||||
int width;
|
||||
int const_width;
|
||||
|
||||
bool var_signed;
|
||||
|
||||
if (cell->type == "$lt" || cell->type == "$ge") {
|
||||
is_lt = cell->type == "$lt" ? 1 : 0;
|
||||
sigVar = cell->getPort("\\A");
|
||||
sigConst = cell->getPort("\\B");
|
||||
width = cell->parameters["\\A_WIDTH"].as_int();
|
||||
const_width = cell->parameters["\\B_WIDTH"].as_int();
|
||||
var_signed = cell->parameters["\\A_SIGNED"].as_bool();
|
||||
} else
|
||||
if (cell->type == "$gt" || cell->type == "$le") {
|
||||
is_lt = cell->type == "$gt" ? 1 : 0;
|
||||
sigVar = cell->getPort("\\B");
|
||||
sigConst = cell->getPort("\\A");
|
||||
width = cell->parameters["\\B_WIDTH"].as_int();
|
||||
const_width = cell->parameters["\\A_WIDTH"].as_int();
|
||||
var_signed = cell->parameters["\\B_SIGNED"].as_bool();
|
||||
} else
|
||||
log_abort();
|
||||
|
||||
// replace a(signed) < 0 with the high bit of a
|
||||
if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true)
|
||||
if (!const_sig.is_fully_const())
|
||||
{
|
||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, cell->parameters["\\Y_WIDTH"].as_int());
|
||||
a_prime[0] = sigVar[width - 1];
|
||||
if (is_lt) {
|
||||
log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n",
|
||||
log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
|
||||
module->connect(cell->getPort("\\Y"), a_prime);
|
||||
module->remove(cell);
|
||||
} else {
|
||||
log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n",
|
||||
log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
|
||||
module->addNot(NEW_ID, a_prime, cell->getPort("\\Y"));
|
||||
module->remove(cell);
|
||||
}
|
||||
did_something = true;
|
||||
goto next_cell;
|
||||
} else
|
||||
if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false)
|
||||
std::swap(var_sig, const_sig);
|
||||
std::swap(var_width, const_width);
|
||||
if (cmp_type == "$gt")
|
||||
cmp_type = "$lt";
|
||||
else if (cmp_type == "$lt")
|
||||
cmp_type = "$gt";
|
||||
else if (cmp_type == "$ge")
|
||||
cmp_type = "$le";
|
||||
else if (cmp_type == "$le")
|
||||
cmp_type = "$ge";
|
||||
}
|
||||
|
||||
if (const_sig.is_fully_def() && const_sig.is_fully_const())
|
||||
{
|
||||
if (sigConst.is_fully_zero()) {
|
||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
|
||||
if (is_lt) {
|
||||
log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n",
|
||||
log_id(cell->type), log_id(cell));
|
||||
a_prime[0] = RTLIL::State::S0;
|
||||
} else {
|
||||
log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n",
|
||||
log_id(cell->type), log_id(cell));
|
||||
a_prime[0] = RTLIL::State::S1;
|
||||
std::string condition, replacement;
|
||||
SigSpec replace_sig(State::S0, GetSize(cell->getPort("\\Y")));
|
||||
bool replace = false;
|
||||
bool remove = false;
|
||||
|
||||
if (!is_signed)
|
||||
{ /* unsigned */
|
||||
if (const_sig.is_fully_zero() && cmp_type == "$lt") {
|
||||
condition = "unsigned X<0";
|
||||
replacement = "constant 0";
|
||||
replace_sig[0] = State::S0;
|
||||
replace = true;
|
||||
}
|
||||
if (const_sig.is_fully_zero() && cmp_type == "$ge") {
|
||||
condition = "unsigned X>=0";
|
||||
replacement = "constant 1";
|
||||
replace_sig[0] = State::S1;
|
||||
replace = true;
|
||||
}
|
||||
if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") {
|
||||
condition = "unsigned X>~0";
|
||||
replacement = "constant 0";
|
||||
replace_sig[0] = State::S0;
|
||||
replace = true;
|
||||
}
|
||||
if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") {
|
||||
condition = "unsigned X<=~0";
|
||||
replacement = "constant 1";
|
||||
replace_sig[0] = State::S1;
|
||||
replace = true;
|
||||
}
|
||||
|
||||
int const_bit_hot = get_onehot_bit_index(const_sig);
|
||||
if (const_bit_hot >= 0 && const_bit_hot < var_width)
|
||||
{
|
||||
RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot);
|
||||
for (int i = const_bit_hot; i < var_width; i++) {
|
||||
var_high_sig[i - const_bit_hot] = var_sig[i];
|
||||
}
|
||||
|
||||
if (cmp_type == "$lt")
|
||||
{
|
||||
condition = stringf("unsigned X<%s", log_signal(const_sig));
|
||||
replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_hot);
|
||||
module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y"));
|
||||
remove = true;
|
||||
}
|
||||
if (cmp_type == "$ge")
|
||||
{
|
||||
condition = stringf("unsigned X>=%s", log_signal(const_sig));
|
||||
replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_hot);
|
||||
module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y"));
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
|
||||
int const_bit_set = get_highest_hot_index(const_sig);
|
||||
if(const_bit_set >= var_width)
|
||||
{
|
||||
string cmp_name;
|
||||
if (cmp_type == "$lt" || cmp_type == "$le")
|
||||
{
|
||||
if (cmp_type == "$lt") cmp_name = "<";
|
||||
if (cmp_type == "$le") cmp_name = "<=";
|
||||
condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
|
||||
replacement = "constant 1";
|
||||
replace_sig[0] = State::S1;
|
||||
replace = true;
|
||||
}
|
||||
if (cmp_type == "$gt" || cmp_type == "$ge")
|
||||
{
|
||||
if (cmp_type == "$gt") cmp_name = ">";
|
||||
if (cmp_type == "$ge") cmp_name = ">=";
|
||||
condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
|
||||
replacement = "constant 0";
|
||||
replace_sig[0] = State::S0;
|
||||
replace = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* signed */
|
||||
if (const_sig.is_fully_zero() && cmp_type == "$lt")
|
||||
{
|
||||
condition = "signed X<0";
|
||||
replacement = stringf("X[%d]", var_width - 1);
|
||||
replace_sig[0] = var_sig[var_width - 1];
|
||||
replace = true;
|
||||
}
|
||||
if (const_sig.is_fully_zero() && cmp_type == "$ge")
|
||||
{
|
||||
condition = "signed X>=0";
|
||||
replacement = stringf("X[%d]", var_width - 1);
|
||||
module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort("\\Y"));
|
||||
remove = true;
|
||||
}
|
||||
module->connect(cell->getPort("\\Y"), a_prime);
|
||||
module->remove(cell);
|
||||
did_something = true;
|
||||
goto next_cell;
|
||||
}
|
||||
|
||||
int const_bit_set = get_onehot_bit_index(sigConst);
|
||||
if (const_bit_set >= 0 && const_bit_set < width) {
|
||||
int bit_set = const_bit_set;
|
||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set);
|
||||
for (int i = bit_set; i < width; i++) {
|
||||
a_prime[i - bit_set] = sigVar[i];
|
||||
}
|
||||
if (is_lt) {
|
||||
log("Replacing %s cell `%s' (implementing unsigned X<%s) with !X[%d:%d]: %s.\n",
|
||||
log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
|
||||
module->addLogicNot(NEW_ID, a_prime, cell->getPort("\\Y"));
|
||||
} else {
|
||||
log("Replacing %s cell `%s' (implementing unsigned X>=%s) with |X[%d:%d]: %s.\n",
|
||||
log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
|
||||
module->addReduceOr(NEW_ID, a_prime, cell->getPort("\\Y"));
|
||||
}
|
||||
if (replace || remove)
|
||||
{
|
||||
log("Replacing %s cell `%s' (implementing %s) with %s.\n",
|
||||
log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
|
||||
if (replace)
|
||||
module->connect(cell->getPort("\\Y"), replace_sig);
|
||||
module->remove(cell);
|
||||
did_something = true;
|
||||
goto next_cell;
|
||||
}
|
||||
else if(const_bit_set >= width && const_bit_set >= 0){
|
||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
|
||||
if(is_lt){
|
||||
a_prime[0] = RTLIL::State::S1;
|
||||
log("Replacing %s cell `%s' (implementing unsigned X[%d:0] < %s[%d:0]) with constant 0.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
|
||||
}
|
||||
else{
|
||||
log("Replacing %s cell `%s' (implementing unsigned X[%d:0]>= %s[%d:0]) with constant 1.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
|
||||
}
|
||||
module->connect(cell->getPort("\\Y"), a_prime);
|
||||
module->remove(cell);
|
||||
did_something = true;
|
||||
goto next_cell;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1470,14 +1514,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
struct OptExprPass : public Pass {
|
||||
OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_expr [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass performs const folding on internal cell types with constant inputs.\n");
|
||||
log("It also performs some simple expression rewritring.\n");
|
||||
log("It also performs some simple expression rewriting.\n");
|
||||
log("\n");
|
||||
log(" -mux_undef\n");
|
||||
log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
|
||||
|
@ -1504,7 +1548,7 @@ struct OptExprPass : public Pass {
|
|||
log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool mux_undef = false;
|
||||
bool mux_bool = false;
|
||||
|
|
607
passes/opt/opt_lut.cc
Normal file
607
passes/opt/opt_lut.cc
Normal file
|
@ -0,0 +1,607 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2018 whitequark <whitequark@whitequark.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/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/modtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct OptLutWorker
|
||||
{
|
||||
dict<IdString, dict<int, IdString>> &dlogic;
|
||||
RTLIL::Module *module;
|
||||
ModIndex index;
|
||||
SigMap sigmap;
|
||||
|
||||
pool<RTLIL::Cell*> luts;
|
||||
dict<RTLIL::Cell*, int> luts_arity;
|
||||
dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
|
||||
dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
|
||||
|
||||
int eliminated_count = 0, combined_count = 0;
|
||||
|
||||
bool evaluate_lut(RTLIL::Cell *lut, dict<SigBit, bool> inputs)
|
||||
{
|
||||
SigSpec lut_input = sigmap(lut->getPort("\\A"));
|
||||
int lut_width = lut->getParam("\\WIDTH").as_int();
|
||||
Const lut_table = lut->getParam("\\LUT");
|
||||
int lut_index = 0;
|
||||
|
||||
for (int i = 0; i < lut_width; i++)
|
||||
{
|
||||
SigBit input = sigmap(lut_input[i]);
|
||||
if (inputs.count(input))
|
||||
{
|
||||
lut_index |= inputs[input] << i;
|
||||
}
|
||||
else
|
||||
{
|
||||
lut_index |= SigSpec(lut_input[i]).as_bool() << i;
|
||||
}
|
||||
}
|
||||
|
||||
return lut_table.extract(lut_index).as_bool();
|
||||
}
|
||||
|
||||
void show_stats_by_arity()
|
||||
{
|
||||
dict<int, int> arity_counts;
|
||||
dict<IdString, int> dlogic_counts;
|
||||
int max_arity = 0;
|
||||
|
||||
for (auto lut_arity : luts_arity)
|
||||
{
|
||||
max_arity = max(max_arity, lut_arity.second);
|
||||
arity_counts[lut_arity.second]++;
|
||||
}
|
||||
|
||||
for (auto &lut_dlogics : luts_dlogics)
|
||||
{
|
||||
for (auto &lut_dlogic : lut_dlogics.second)
|
||||
{
|
||||
dlogic_counts[lut_dlogic->type]++;
|
||||
}
|
||||
}
|
||||
|
||||
log("Number of LUTs: %8zu\n", luts.size());
|
||||
for (int arity = 1; arity <= max_arity; arity++)
|
||||
{
|
||||
if (arity_counts[arity])
|
||||
log(" %d-LUT %16d\n", arity, arity_counts[arity]);
|
||||
}
|
||||
for (auto &dlogic_count : dlogic_counts)
|
||||
{
|
||||
log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
|
||||
}
|
||||
}
|
||||
|
||||
OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) :
|
||||
dlogic(dlogic), module(module), index(module), sigmap(module)
|
||||
{
|
||||
log("Discovering LUTs.\n");
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
if (cell->type == "$lut")
|
||||
{
|
||||
int lut_width = cell->getParam("\\WIDTH").as_int();
|
||||
SigSpec lut_input = cell->getPort("\\A");
|
||||
int lut_arity = 0;
|
||||
|
||||
log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell));
|
||||
luts.insert(cell);
|
||||
|
||||
// First, find all dedicated logic we're connected to. This results in an overapproximation
|
||||
// of such connections.
|
||||
pool<RTLIL::Cell*> lut_all_dlogics;
|
||||
for (int i = 0; i < lut_width; i++)
|
||||
{
|
||||
SigBit bit = lut_input[i];
|
||||
for (auto &port : index.query_ports(bit))
|
||||
{
|
||||
if (dlogic.count(port.cell->type))
|
||||
{
|
||||
auto &dlogic_map = dlogic[port.cell->type];
|
||||
if (dlogic_map.count(i))
|
||||
{
|
||||
if (port.port == dlogic_map[i])
|
||||
{
|
||||
lut_all_dlogics.insert(port.cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second, make sure that the connection to dedicated logic is legal. If it is not legal,
|
||||
// it means one of the two things:
|
||||
// * The connection is spurious. I.e. this is dedicated logic that will be packed
|
||||
// with some other LUT, and it just happens to be connected to this LUT as well.
|
||||
// * The connection is illegal.
|
||||
// In either of these cases, we don't need to concern ourselves with preserving the connection
|
||||
// between this LUT and this dedicated logic cell.
|
||||
pool<RTLIL::Cell*> lut_legal_dlogics;
|
||||
pool<int> lut_dlogic_inputs;
|
||||
for (auto lut_dlogic : lut_all_dlogics)
|
||||
{
|
||||
auto &dlogic_map = dlogic[lut_dlogic->type];
|
||||
bool legal = true;
|
||||
for (auto &dlogic_conn : dlogic_map)
|
||||
{
|
||||
if (lut_width <= dlogic_conn.first)
|
||||
{
|
||||
log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
|
||||
log(" LUT input A[%d] not present.\n", dlogic_conn.first);
|
||||
legal = false;
|
||||
break;
|
||||
}
|
||||
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
|
||||
{
|
||||
log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
|
||||
log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
|
||||
legal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (legal)
|
||||
{
|
||||
log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
|
||||
lut_legal_dlogics.insert(lut_dlogic);
|
||||
for (auto &dlogic_conn : dlogic_map)
|
||||
lut_dlogic_inputs.insert(dlogic_conn.first);
|
||||
}
|
||||
}
|
||||
|
||||
// Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated
|
||||
// logic implements an (n-k-m)-ary function.
|
||||
for (int i = 0; i < lut_width; i++)
|
||||
{
|
||||
SigBit bit = lut_input[i];
|
||||
if (bit.wire || lut_dlogic_inputs.count(i))
|
||||
lut_arity++;
|
||||
}
|
||||
|
||||
log(" Cell implements a %d-LUT.\n", lut_arity);
|
||||
luts_arity[cell] = lut_arity;
|
||||
luts_dlogics[cell] = lut_legal_dlogics;
|
||||
luts_dlogic_inputs[cell] = lut_dlogic_inputs;
|
||||
}
|
||||
}
|
||||
show_stats_by_arity();
|
||||
|
||||
log("\n");
|
||||
log("Eliminating LUTs.\n");
|
||||
pool<RTLIL::Cell*> worklist = luts;
|
||||
while (worklist.size())
|
||||
{
|
||||
if (limit == 0)
|
||||
{
|
||||
log("Limit reached.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
auto lut = worklist.pop();
|
||||
SigSpec lut_input = sigmap(lut->getPort("\\A"));
|
||||
pool<int> &lut_dlogic_inputs = luts_dlogic_inputs[lut];
|
||||
|
||||
vector<SigBit> lut_inputs;
|
||||
for (auto &bit : lut_input)
|
||||
{
|
||||
if (bit.wire)
|
||||
lut_inputs.push_back(sigmap(bit));
|
||||
}
|
||||
|
||||
bool const0_match = true;
|
||||
bool const1_match = true;
|
||||
vector<bool> input_matches;
|
||||
for (size_t i = 0; i < lut_inputs.size(); i++)
|
||||
input_matches.push_back(true);
|
||||
|
||||
for (int eval = 0; eval < 1 << lut_inputs.size(); eval++)
|
||||
{
|
||||
dict<SigBit, bool> eval_inputs;
|
||||
for (size_t i = 0; i < lut_inputs.size(); i++)
|
||||
eval_inputs[lut_inputs[i]] = (eval >> i) & 1;
|
||||
bool value = evaluate_lut(lut, eval_inputs);
|
||||
if (value != 0)
|
||||
const0_match = false;
|
||||
if (value != 1)
|
||||
const1_match = false;
|
||||
for (size_t i = 0; i < lut_inputs.size(); i++)
|
||||
{
|
||||
if (value != eval_inputs[lut_inputs[i]])
|
||||
input_matches[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int input_match = -1;
|
||||
for (size_t i = 0; i < lut_inputs.size(); i++)
|
||||
if (input_matches[i])
|
||||
input_match = i;
|
||||
|
||||
if (const0_match || const1_match || input_match != -1)
|
||||
{
|
||||
log("Found redundant cell %s.%s.\n", log_id(module), log_id(lut));
|
||||
|
||||
SigBit value;
|
||||
if (const0_match)
|
||||
{
|
||||
log(" Cell evaluates constant 0.\n");
|
||||
value = State::S0;
|
||||
}
|
||||
if (const1_match)
|
||||
{
|
||||
log(" Cell evaluates constant 1.\n");
|
||||
value = State::S1;
|
||||
}
|
||||
if (input_match != -1) {
|
||||
log(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match]));
|
||||
value = lut_inputs[input_match];
|
||||
}
|
||||
|
||||
if (lut_dlogic_inputs.size())
|
||||
{
|
||||
log(" Not eliminating cell (connected to dedicated logic).\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
SigSpec lut_output = lut->getPort("\\Y");
|
||||
for (auto &port : index.query_ports(lut_output))
|
||||
{
|
||||
if (port.cell != lut && luts.count(port.cell))
|
||||
worklist.insert(port.cell);
|
||||
}
|
||||
|
||||
module->connect(lut_output, value);
|
||||
sigmap.add(lut_output, value);
|
||||
|
||||
module->remove(lut);
|
||||
luts.erase(lut);
|
||||
luts_arity.erase(lut);
|
||||
luts_dlogics.erase(lut);
|
||||
luts_dlogic_inputs.erase(lut);
|
||||
|
||||
eliminated_count++;
|
||||
if (limit > 0)
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
}
|
||||
show_stats_by_arity();
|
||||
|
||||
log("\n");
|
||||
log("Combining LUTs.\n");
|
||||
worklist = luts;
|
||||
while (worklist.size())
|
||||
{
|
||||
if (limit == 0)
|
||||
{
|
||||
log("Limit reached.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
auto lutA = worklist.pop();
|
||||
SigSpec lutA_input = sigmap(lutA->getPort("\\A"));
|
||||
SigSpec lutA_output = sigmap(lutA->getPort("\\Y")[0]);
|
||||
int lutA_width = lutA->getParam("\\WIDTH").as_int();
|
||||
int lutA_arity = luts_arity[lutA];
|
||||
pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
|
||||
|
||||
auto lutA_output_ports = index.query_ports(lutA->getPort("\\Y"));
|
||||
if (lutA_output_ports.size() != 2)
|
||||
continue;
|
||||
|
||||
for (auto &port : lutA_output_ports)
|
||||
{
|
||||
if (port.cell == lutA)
|
||||
continue;
|
||||
|
||||
if (luts.count(port.cell))
|
||||
{
|
||||
auto lutB = port.cell;
|
||||
SigSpec lutB_input = sigmap(lutB->getPort("\\A"));
|
||||
SigSpec lutB_output = sigmap(lutB->getPort("\\Y")[0]);
|
||||
int lutB_width = lutB->getParam("\\WIDTH").as_int();
|
||||
int lutB_arity = luts_arity[lutB];
|
||||
pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB];
|
||||
|
||||
log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB));
|
||||
|
||||
if (index.query_is_output(lutA->getPort("\\Y")))
|
||||
{
|
||||
log(" Not combining LUTs (cascade connection feeds module output).\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
pool<SigBit> lutA_inputs;
|
||||
pool<SigBit> lutB_inputs;
|
||||
for (auto &bit : lutA_input)
|
||||
{
|
||||
if (bit.wire)
|
||||
lutA_inputs.insert(sigmap(bit));
|
||||
}
|
||||
for (auto &bit : lutB_input)
|
||||
{
|
||||
if (bit.wire)
|
||||
lutB_inputs.insert(sigmap(bit));
|
||||
}
|
||||
|
||||
pool<SigBit> common_inputs;
|
||||
for (auto &bit : lutA_inputs)
|
||||
{
|
||||
if (lutB_inputs.count(bit))
|
||||
common_inputs.insert(bit);
|
||||
}
|
||||
|
||||
int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size();
|
||||
if (lutA_dlogic_inputs.size())
|
||||
log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size());
|
||||
else
|
||||
log(" Cell A is a %d-LUT. ", lutA_arity);
|
||||
if (lutB_dlogic_inputs.size())
|
||||
log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size());
|
||||
else
|
||||
log("Cell B is a %d-LUT.\n", lutB_arity);
|
||||
log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity);
|
||||
|
||||
const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B;
|
||||
int combine_mask = 0;
|
||||
if (lutM_arity > lutA_width)
|
||||
{
|
||||
log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
|
||||
}
|
||||
else if (lutB_dlogic_inputs.size() > 0)
|
||||
{
|
||||
log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
|
||||
}
|
||||
else if (lutB->get_bool_attribute("\\lut_keep"))
|
||||
{
|
||||
log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
combine_mask |= COMBINE_A;
|
||||
}
|
||||
if (lutM_arity > lutB_width)
|
||||
{
|
||||
log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
|
||||
}
|
||||
else if (lutA_dlogic_inputs.size() > 0)
|
||||
{
|
||||
log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
|
||||
}
|
||||
else if (lutA->get_bool_attribute("\\lut_keep"))
|
||||
{
|
||||
log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
combine_mask |= COMBINE_B;
|
||||
}
|
||||
|
||||
int combine = combine_mask;
|
||||
if (combine == COMBINE_EITHER)
|
||||
{
|
||||
log(" Can combine into either cell.\n");
|
||||
if (lutA_arity == 1)
|
||||
{
|
||||
log(" Cell A is a buffer or inverter, combining into cell B.\n");
|
||||
combine = COMBINE_B;
|
||||
}
|
||||
else if (lutB_arity == 1)
|
||||
{
|
||||
log(" Cell B is a buffer or inverter, combining into cell A.\n");
|
||||
combine = COMBINE_A;
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" Arbitrarily combining into cell A.\n");
|
||||
combine = COMBINE_A;
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *lutM, *lutR;
|
||||
pool<SigBit> lutM_inputs, lutR_inputs;
|
||||
pool<int> lutM_dlogic_inputs;
|
||||
if (combine == COMBINE_A)
|
||||
{
|
||||
log(" Combining LUTs into cell A.\n");
|
||||
lutM = lutA;
|
||||
lutM_inputs = lutA_inputs;
|
||||
lutM_dlogic_inputs = lutA_dlogic_inputs;
|
||||
lutR = lutB;
|
||||
lutR_inputs = lutB_inputs;
|
||||
}
|
||||
else if (combine == COMBINE_B)
|
||||
{
|
||||
log(" Combining LUTs into cell B.\n");
|
||||
lutM = lutB;
|
||||
lutM_inputs = lutB_inputs;
|
||||
lutM_dlogic_inputs = lutB_dlogic_inputs;
|
||||
lutR = lutA;
|
||||
lutR_inputs = lutA_inputs;
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" Cannot combine LUTs.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
pool<SigBit> lutR_unique;
|
||||
for (auto &bit : lutR_inputs)
|
||||
{
|
||||
if (!common_inputs.count(bit) && bit != lutA_output)
|
||||
lutR_unique.insert(bit);
|
||||
}
|
||||
|
||||
int lutM_width = lutM->getParam("\\WIDTH").as_int();
|
||||
SigSpec lutM_input = sigmap(lutM->getPort("\\A"));
|
||||
std::vector<SigBit> lutM_new_inputs;
|
||||
for (int i = 0; i < lutM_width; i++)
|
||||
{
|
||||
bool input_unused = false;
|
||||
if (sigmap(lutM_input[i]) == lutA_output)
|
||||
input_unused = true;
|
||||
if (!lutM_input[i].wire && !lutM_dlogic_inputs.count(i))
|
||||
input_unused = true;
|
||||
|
||||
if (input_unused && lutR_unique.size())
|
||||
{
|
||||
SigBit new_input = lutR_unique.pop();
|
||||
log(" Connecting input %d as %s.\n", i, log_signal(new_input));
|
||||
lutM_new_inputs.push_back(new_input);
|
||||
}
|
||||
else if (sigmap(lutM_input[i]) == lutA_output)
|
||||
{
|
||||
log(" Disconnecting cascade input %d.\n", i);
|
||||
lutM_new_inputs.push_back(SigBit());
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i]));
|
||||
lutM_new_inputs.push_back(lutM_input[i]);
|
||||
}
|
||||
}
|
||||
log_assert(lutR_unique.size() == 0);
|
||||
|
||||
RTLIL::Const lutM_new_table(State::Sx, 1 << lutM_width);
|
||||
for (int eval = 0; eval < 1 << lutM_width; eval++)
|
||||
{
|
||||
dict<SigBit, bool> eval_inputs;
|
||||
for (size_t i = 0; i < lutM_new_inputs.size(); i++)
|
||||
{
|
||||
eval_inputs[lutM_new_inputs[i]] = (eval >> i) & 1;
|
||||
}
|
||||
eval_inputs[lutA_output] = evaluate_lut(lutA, eval_inputs);
|
||||
lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs);
|
||||
}
|
||||
|
||||
log(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str());
|
||||
log(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str());
|
||||
log(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str());
|
||||
|
||||
lutM->setParam("\\LUT", lutM_new_table);
|
||||
lutM->setPort("\\A", lutM_new_inputs);
|
||||
lutM->setPort("\\Y", lutB_output);
|
||||
|
||||
luts_arity[lutM] = lutM_arity;
|
||||
luts.erase(lutR);
|
||||
luts_arity.erase(lutR);
|
||||
lutR->module->remove(lutR);
|
||||
|
||||
worklist.insert(lutM);
|
||||
worklist.erase(lutR);
|
||||
|
||||
combined_count++;
|
||||
if (limit > 0)
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
}
|
||||
show_stats_by_arity();
|
||||
}
|
||||
};
|
||||
|
||||
static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
|
||||
{
|
||||
size_t start = 0, end = 0;
|
||||
while ((end = text.find(sep, start)) != std::string::npos) {
|
||||
tokens.push_back(text.substr(start, end - start));
|
||||
start = end + 1;
|
||||
}
|
||||
tokens.push_back(text.substr(start));
|
||||
}
|
||||
|
||||
struct OptLutPass : public Pass {
|
||||
OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_lut [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass combines cascaded $lut cells with unused inputs.\n");
|
||||
log("\n");
|
||||
log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n");
|
||||
log(" preserve connections to dedicated logic cell <type> that has ports\n");
|
||||
log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n");
|
||||
log(" the case where both LUT and dedicated logic input are connected to\n");
|
||||
log(" the same constant.\n");
|
||||
log("\n");
|
||||
log(" -limit N\n");
|
||||
log(" only perform the first N combines, then stop. useful for debugging.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
|
||||
|
||||
dict<IdString, dict<int, IdString>> dlogic;
|
||||
int limit = -1;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-dlogic" && argidx+1 < args.size())
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
split(tokens, args[++argidx], ':');
|
||||
if (tokens.size() < 2)
|
||||
log_cmd_error("The -dlogic option requires at least one connection.\n");
|
||||
IdString type = "\\" + tokens[0];
|
||||
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
|
||||
std::vector<std::string> conn_tokens;
|
||||
split(conn_tokens, *it, '=');
|
||||
if (conn_tokens.size() != 2)
|
||||
log_cmd_error("Invalid format of -dlogic signal mapping.\n");
|
||||
IdString logic_port = "\\" + conn_tokens[0];
|
||||
int lut_input = atoi(conn_tokens[1].c_str());
|
||||
dlogic[type][lut_input] = logic_port;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-limit" && argidx + 1 < args.size())
|
||||
{
|
||||
limit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int eliminated_count = 0, combined_count = 0;
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
OptLutWorker worker(dlogic, module, limit - eliminated_count - combined_count);
|
||||
eliminated_count += worker.eliminated_count;
|
||||
combined_count += worker.combined_count;
|
||||
}
|
||||
if (eliminated_count)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
if (combined_count)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
log("\n");
|
||||
log("Eliminated %d LUTs.\n", eliminated_count);
|
||||
log("Combined %d LUTs.\n", combined_count);
|
||||
}
|
||||
} OptLutPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -341,7 +341,7 @@ struct OptMergeWorker
|
|||
|
||||
struct OptMergePass : public Pass {
|
||||
OptMergePass() : Pass("opt_merge", "consolidate identical cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -357,7 +357,7 @@ struct OptMergePass : public Pass {
|
|||
log(" Operate on all cell types, not just built-in types.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n");
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ struct OptMuxtreeWorker
|
|||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
int removed_count;
|
||||
int glob_abort_cnt = 100000;
|
||||
|
||||
struct bitinfo_t {
|
||||
bool seen_non_mux;
|
||||
|
@ -293,6 +294,9 @@ struct OptMuxtreeWorker
|
|||
|
||||
void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
|
||||
{
|
||||
if (glob_abort_cnt == 0)
|
||||
return;
|
||||
|
||||
muxinfo_t &muxinfo = mux2info[mux_idx];
|
||||
|
||||
if (do_enable_ports)
|
||||
|
@ -315,7 +319,7 @@ struct OptMuxtreeWorker
|
|||
knowledge.visited_muxes[m] = true;
|
||||
parent_muxes.push_back(m);
|
||||
}
|
||||
for (int m : parent_muxes)
|
||||
for (int m : parent_muxes) {
|
||||
if (root_enable_muxes.at(m))
|
||||
continue;
|
||||
else if (root_muxes.at(m)) {
|
||||
|
@ -327,6 +331,9 @@ struct OptMuxtreeWorker
|
|||
eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
|
||||
} else
|
||||
eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count);
|
||||
if (glob_abort_cnt == 0)
|
||||
return;
|
||||
}
|
||||
for (int m : parent_muxes)
|
||||
knowledge.visited_muxes[m] = false;
|
||||
|
||||
|
@ -390,6 +397,12 @@ struct OptMuxtreeWorker
|
|||
|
||||
void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
|
||||
{
|
||||
if (glob_abort_cnt == 0) {
|
||||
log(" Giving up (too many iterations)\n");
|
||||
return;
|
||||
}
|
||||
glob_abort_cnt--;
|
||||
|
||||
muxinfo_t &muxinfo = mux2info[mux_idx];
|
||||
|
||||
// set input ports to constants if we find known active or inactive signals
|
||||
|
@ -433,6 +446,9 @@ struct OptMuxtreeWorker
|
|||
if (knowledge.known_inactive.at(portinfo.ctrl_sig))
|
||||
continue;
|
||||
eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
|
||||
|
||||
if (glob_abort_cnt == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -449,7 +465,7 @@ struct OptMuxtreeWorker
|
|||
|
||||
struct OptMuxtreePass : public Pass {
|
||||
OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -462,7 +478,7 @@ struct OptMuxtreePass : public Pass {
|
|||
log("This pass only operates on completely selected modules without processes.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
|
|
@ -329,7 +329,7 @@ struct OptReduceWorker
|
|||
|
||||
struct OptReducePass : public Pass {
|
||||
OptReducePass() : Pass("opt_reduce", "simplify large MUXes and AND/OR gates") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -350,7 +350,7 @@ struct OptReducePass : public Pass {
|
|||
log(" alias for -fine\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool do_fine = false;
|
||||
|
||||
|
|
|
@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
if (!hasreset)
|
||||
{
|
||||
IdString new_type;
|
||||
|
||||
|
@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetPort("\\S");
|
||||
cell->unsetPort("\\R");
|
||||
|
||||
return did_something;
|
||||
return true;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
|
||||
|
@ -399,7 +400,7 @@ delete_dff:
|
|||
|
||||
struct OptRmdffPass : public Pass {
|
||||
OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -409,7 +410,7 @@ struct OptRmdffPass : public Pass {
|
|||
log("a constant driver.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
int total_count = 0, total_initdrv = 0;
|
||||
log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
|
||||
|
|
|
@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct RmportsPassPass : public Pass {
|
||||
RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -39,7 +39,7 @@ struct RmportsPassPass : public Pass {
|
|||
log("\n");
|
||||
}
|
||||
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing RMPORTS pass (remove ports with no connections).\n");
|
||||
|
||||
|
|
|
@ -710,8 +710,12 @@ struct ShareWorker
|
|||
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
|
||||
RTLIL::SigSpec addr1 = c1->getPort("\\ADDR");
|
||||
RTLIL::SigSpec addr2 = c2->getPort("\\ADDR");
|
||||
if (addr1 != addr2)
|
||||
supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act));
|
||||
if (GetSize(addr1) < GetSize(addr2))
|
||||
addr1.extend_u0(GetSize(addr2));
|
||||
else
|
||||
addr2.extend_u0(GetSize(addr1));
|
||||
supercell->setPort("\\ADDR", addr1 != addr2 ? module->Mux(NEW_ID, addr2, addr1, act) : addr1);
|
||||
supercell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr1));
|
||||
supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
|
||||
supercell_aux.insert(supercell);
|
||||
return supercell;
|
||||
|
@ -1421,7 +1425,7 @@ struct ShareWorker
|
|||
|
||||
struct SharePass : public Pass {
|
||||
SharePass() : Pass("share", "perform sat-based resource sharing") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -1453,7 +1457,7 @@ struct SharePass : public Pass {
|
|||
log(" Only perform the first N merges, then stop. This is useful for debugging.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
ShareWorkerConfig config;
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ struct WreduceConfig
|
|||
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
|
||||
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
|
||||
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
|
||||
"$mux", "$pmux"
|
||||
"$mux", "$pmux",
|
||||
"$dff", "$adff"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -52,6 +53,8 @@ struct WreduceWorker
|
|||
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
|
||||
std::set<SigBit> work_queue_bits;
|
||||
pool<SigBit> keep_bits;
|
||||
dict<SigBit, State> init_bits;
|
||||
pool<SigBit> remove_init_bits;
|
||||
|
||||
WreduceWorker(WreduceConfig *config, Module *module) :
|
||||
config(config), module(module), mi(module) { }
|
||||
|
@ -134,6 +137,91 @@ struct WreduceWorker
|
|||
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
|
||||
}
|
||||
|
||||
void run_cell_dff(Cell *cell)
|
||||
{
|
||||
// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
|
||||
|
||||
SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
|
||||
SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
|
||||
Const initval;
|
||||
|
||||
int width_before = GetSize(sig_q);
|
||||
|
||||
if (width_before == 0)
|
||||
return;
|
||||
|
||||
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
||||
bool sign_ext = !zero_ext;
|
||||
|
||||
for (int i = 0; i < GetSize(sig_q); i++) {
|
||||
SigBit bit = sig_q[i];
|
||||
if (init_bits.count(bit))
|
||||
initval.bits.push_back(init_bits.at(bit));
|
||||
else
|
||||
initval.bits.push_back(State::Sx);
|
||||
}
|
||||
|
||||
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
||||
{
|
||||
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
|
||||
module->connect(sig_q[i], State::S0);
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
|
||||
module->connect(sig_q[i], sig_q[i-1]);
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = mi.query(sig_q[i]);
|
||||
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
zero_ext = false;
|
||||
sign_ext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (width_before == GetSize(sig_q))
|
||||
return;
|
||||
|
||||
if (GetSize(sig_q) == 0) {
|
||||
log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||
module->remove(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
|
||||
log_id(module), log_id(cell), log_id(cell->type));
|
||||
|
||||
for (auto bit : sig_d)
|
||||
work_queue_bits.insert(bit);
|
||||
|
||||
for (auto bit : sig_q)
|
||||
work_queue_bits.insert(bit);
|
||||
|
||||
// Narrow ARST_VALUE parameter to new size.
|
||||
if (cell->parameters.count("\\ARST_VALUE")) {
|
||||
Const arst_value = cell->getParam("\\ARST_VALUE");
|
||||
arst_value.bits.resize(GetSize(sig_q));
|
||||
cell->setParam("\\ARST_VALUE", arst_value);
|
||||
}
|
||||
|
||||
cell->setPort("\\D", sig_d);
|
||||
cell->setPort("\\Q", sig_q);
|
||||
cell->fixup_parameters();
|
||||
}
|
||||
|
||||
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
|
||||
{
|
||||
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
|
||||
|
@ -176,6 +264,9 @@ struct WreduceWorker
|
|||
if (cell->type.in("$mux", "$pmux"))
|
||||
return run_cell_mux(cell);
|
||||
|
||||
if (cell->type.in("$dff", "$adff"))
|
||||
return run_cell_dff(cell);
|
||||
|
||||
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
|
||||
|
||||
if (sig.has_const())
|
||||
|
@ -235,8 +326,11 @@ struct WreduceWorker
|
|||
} else {
|
||||
while (GetSize(sig) > 0)
|
||||
{
|
||||
auto info = mi.query(sig[GetSize(sig)-1]);
|
||||
auto bit = sig[GetSize(sig)-1];
|
||||
if (keep_bits.count(bit))
|
||||
break;
|
||||
|
||||
auto info = mi.query(bit);
|
||||
if (info->is_output || GetSize(info->ports) > 1)
|
||||
break;
|
||||
|
||||
|
@ -297,10 +391,21 @@ struct WreduceWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
for (auto w : module->wires())
|
||||
// create a copy as mi.sigmap will be updated as we process the module
|
||||
SigMap init_attr_sigmap = mi.sigmap;
|
||||
|
||||
for (auto w : module->wires()) {
|
||||
if (w->get_bool_attribute("\\keep"))
|
||||
for (auto bit : mi.sigmap(w))
|
||||
keep_bits.insert(bit);
|
||||
if (w->attributes.count("\\init")) {
|
||||
Const initval = w->attributes.at("\\init");
|
||||
SigSpec initsig = init_attr_sigmap(w);
|
||||
int width = std::min(GetSize(initval), GetSize(initsig));
|
||||
for (int i = 0; i < width; i++)
|
||||
init_bits[initsig[i]] = initval[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (auto c : module->selected_cells())
|
||||
work_queue_cells.insert(c);
|
||||
|
@ -348,12 +453,30 @@ struct WreduceWorker
|
|||
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
|
||||
module->swap_names(w, nw);
|
||||
}
|
||||
|
||||
if (!remove_init_bits.empty()) {
|
||||
for (auto w : module->wires()) {
|
||||
if (w->attributes.count("\\init")) {
|
||||
Const initval = w->attributes.at("\\init");
|
||||
Const new_initval(State::Sx, GetSize(w));
|
||||
SigSpec initsig = init_attr_sigmap(w);
|
||||
int width = std::min(GetSize(initval), GetSize(initsig));
|
||||
for (int i = 0; i < width; i++) {
|
||||
log_dump(initsig[i], remove_init_bits.count(initsig[i]));
|
||||
if (!remove_init_bits.count(initsig[i]))
|
||||
new_initval[i] = initval[i];
|
||||
}
|
||||
w->attributes.at("\\init") = new_initval;
|
||||
log_dump(w->name, initval, new_initval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WreducePass : public Pass {
|
||||
WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -373,7 +496,7 @@ struct WreducePass : public Pass {
|
|||
log(" flows that use the 'memory_memx' pass.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, Design *design)
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
WreduceConfig config;
|
||||
bool opt_memx = false;
|
||||
|
|
1
passes/pmgen/.gitignore
vendored
Normal file
1
passes/pmgen/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/ice40_dsp_pm.h
|
8
passes/pmgen/Makefile.inc
Normal file
8
passes/pmgen/Makefile.inc
Normal file
|
@ -0,0 +1,8 @@
|
|||
OBJS += passes/pmgen/ice40_dsp.o
|
||||
|
||||
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
|
||||
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
|
||||
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
|
||||
|
||||
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
|
||||
$(P) mkdir -p passes/pmgen && python3 $^ $@
|
224
passes/pmgen/README.md
Normal file
224
passes/pmgen/README.md
Normal file
|
@ -0,0 +1,224 @@
|
|||
Pattern Matcher Generator
|
||||
=========================
|
||||
|
||||
The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
|
||||
writes a header-only C++ library that implements that pattern matcher.
|
||||
|
||||
The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
|
||||
|
||||
The algorithm used in the generated pattern matcher is a simple recursive
|
||||
search with backtracking. It is left to the author of the `.pmg` file to
|
||||
determine an efficient cell order for the search that allows for maximum
|
||||
use of indices and early backtracking.
|
||||
|
||||
|
||||
API of Generated Matcher
|
||||
========================
|
||||
|
||||
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
|
||||
a class `foobar_pm`. That class is instantiated with an RTLIL module and a
|
||||
list of cells from that module:
|
||||
|
||||
foobar_pm pm(module, module->selected_cells());
|
||||
|
||||
The caller must make sure that none of the cells in the 2nd argument are
|
||||
deleted for as long as the patter matcher instance is used.
|
||||
|
||||
At any time it is possible to disable cells, preventing them from showing
|
||||
up in any future matches:
|
||||
|
||||
pm.blacklist(some_cell);
|
||||
|
||||
The `.run(callback_function)` method searches for all matches and calls the
|
||||
callback function for each found match:
|
||||
|
||||
pm.run([&](){
|
||||
log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
|
||||
log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
|
||||
});
|
||||
|
||||
The `.pmg` file declares matcher state variables that are accessible via the
|
||||
`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
|
||||
|
||||
Similarly the `.pmg` file declares user data variables that become members of
|
||||
`.ud`, a struct of type `foobar_pm::udata_t`.
|
||||
|
||||
|
||||
The .pmg File Format
|
||||
====================
|
||||
|
||||
The `.pmg` file format is a simple line-based file format. For the most part
|
||||
lines consist of whitespace-separated tokens.
|
||||
|
||||
Lines in `.pmg` files starting with `//` are comments.
|
||||
|
||||
Declaring state variables
|
||||
-------------------------
|
||||
|
||||
One or more state variables can be declared using the `state` statement,
|
||||
followed by a C++ type in angle brackets, followed by a whitespace separated
|
||||
list of variable names. For example:
|
||||
|
||||
state <bool> flag1 flag2 happy big
|
||||
state <SigSpec> sigA sigB sigY
|
||||
|
||||
State variables are automatically managed by the generated backtracking algorithm
|
||||
and saved and restored as needed.
|
||||
|
||||
They are automatically initialized to the default constructed value of their type
|
||||
when `.run(callback_function)` is called.
|
||||
|
||||
Declaring udata variables
|
||||
-------------------------
|
||||
|
||||
Udata (user-data) variables can be used for example to configure the matcher or
|
||||
the callback function used to perform actions on found matches.
|
||||
|
||||
There is no automatic management of udata variables. For this reason it is
|
||||
recommended that the user-supplied matcher code treats them as read-only
|
||||
variables.
|
||||
|
||||
They are declared like state variables, just using the `udata` statement:
|
||||
|
||||
udata <int> min_data_width max_data_width
|
||||
udata <IdString> data_port_name
|
||||
|
||||
They are atomatically initialzed to the default constructed value of their type
|
||||
when ther pattern matcher object is constructed.
|
||||
|
||||
Embedded C++ code
|
||||
-----------------
|
||||
|
||||
Many statements in a `.pmg` file contain C++ code. However, there are some
|
||||
slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
|
||||
write matchers:
|
||||
|
||||
- Identifiers starting with a dollar sign or backslash are automatically
|
||||
converted to special IdString variables that are initialized when the
|
||||
matcher object is constructed.
|
||||
|
||||
- The `port(<cell>, <portname>)` function is a handy alias for
|
||||
`sigmap(<cell>->getPort(<portname>))`.
|
||||
|
||||
- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
|
||||
|
||||
- The function `nusers(<sigspec>)` returns the number of different cells
|
||||
connected to any of the given signal bits, plus one if any of the signal
|
||||
bits is also a primary input or primary output.
|
||||
|
||||
- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
|
||||
statements.
|
||||
|
||||
- In `index` statements there is a special `===` operator for the index
|
||||
lookup.
|
||||
|
||||
Matching cells
|
||||
--------------
|
||||
|
||||
Cells are matched using `match..endmatch` blocks. For example:
|
||||
|
||||
match mul
|
||||
if ff
|
||||
select mul->type == $mul
|
||||
select nusers(port(mul, \Y) == 2
|
||||
index <SigSpec> port(mul, \Y) === port(ff, \D)
|
||||
filter some_weird_function(mul) < other_weird_function(ff)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
A `match` block starts with `match <statevar>` and implicitly generates
|
||||
a state variable `<statevar>` of type `RTLIL::Cell*`.
|
||||
|
||||
All statements in the match block are optional. (An empty match block
|
||||
would simply match each and every cell in the module.)
|
||||
|
||||
The `if <expression>` statement makes the match block conditional. If
|
||||
`<expression>` evaluates to `false` then the match block will be ignored
|
||||
and the corresponding state variable is set to `nullptr`. In our example
|
||||
we only try to match the `mul` cell if the `ff` state variable points
|
||||
to a cell. (Presumably `ff` is provided by a prior `match` block.)
|
||||
|
||||
The `select` lines are evaluated once for each cell when the matcher is
|
||||
initialized. A `match` block will only consider cells for which all `select`
|
||||
expressions evaluated to `true`. Note that the state variable corresponding to
|
||||
the match (in the example `mul`) is the only state variable that may be used
|
||||
in `select` lines.
|
||||
|
||||
Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
|
||||
evaluated during matcher initialization and the same restrictions apply as for
|
||||
`select` expressions. `expr2` is evaluated when the match is calulated. It is a
|
||||
function of any state variables assigned to by previous blocks. Both expression
|
||||
are converted to the given type and compared for equality. Only cells for which
|
||||
all `index` statements in the block pass are considered by the match.
|
||||
|
||||
Note that `select` and `index` are fast operations. Thus `select` and `index`
|
||||
should be used whenever possible to create efficient matchers.
|
||||
|
||||
Finally, `filter <expression>` narrows down the remaining list of cells. For
|
||||
performance reasons `filter` statements should only be used for things that
|
||||
can't be done using `select` and `index`.
|
||||
|
||||
The `optional` statement marks optional matches. I.e. the matcher will also
|
||||
explore the case where `mul` is set to `nullptr`. Without the `optional`
|
||||
statement a match may only be assigned nullptr when one of the `if` expressions
|
||||
evaluates to `false`.
|
||||
|
||||
Additional code
|
||||
---------------
|
||||
|
||||
Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
|
||||
Such a block starts with the keyword `code` followed by a list of state variables
|
||||
that the block may modify. For example:
|
||||
|
||||
code addAB sigS
|
||||
if (addA) {
|
||||
addAB = addA;
|
||||
sigS = port(addA, \B);
|
||||
}
|
||||
if (addB) {
|
||||
addAB = addB;
|
||||
sigS = port(addB, \A);
|
||||
}
|
||||
endcode
|
||||
|
||||
The special keyword `reject` can be used to reject the current state and
|
||||
backtrack. For example:
|
||||
|
||||
code
|
||||
if (ffA && ffB) {
|
||||
if (port(ffA, \CLK) != port(ffB, \CLK))
|
||||
reject;
|
||||
if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
Similarly, the special keyword `accept` can be used to accept the current
|
||||
state. (`accept` will not backtrack. This means it continues with the current
|
||||
branch and may accept a larger match later.)
|
||||
|
||||
The special keyword `branch` can be used to explore different cases. Note that
|
||||
each code block has an implicit `branch` at the end. So most use-cases of the
|
||||
`branch` keyword need to end the block with `reject` to avoid the implicit
|
||||
branch at the end. For example:
|
||||
|
||||
state <int> mode
|
||||
|
||||
code mode
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
branch;
|
||||
reject;
|
||||
endcode
|
||||
|
||||
But in some cases it is more natural to utilize the implicit branch statement:
|
||||
|
||||
state <IdString> portAB
|
||||
|
||||
code portAB
|
||||
portAB = \A;
|
||||
branch;
|
||||
portAB = \B;
|
||||
endcode
|
||||
|
||||
There is an implicit `code..endcode` block at the end of each `.pgm` file
|
||||
that just accepts everything that gets all the way there.
|
237
passes/pmgen/ice40_dsp.cc
Normal file
237
passes/pmgen/ice40_dsp.cc
Normal file
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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"
|
||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||
{
|
||||
#if 0
|
||||
log("\n");
|
||||
log("ffA: %s\n", log_id(pm.st.ffA, "--"));
|
||||
log("ffB: %s\n", log_id(pm.st.ffB, "--"));
|
||||
log("mul: %s\n", log_id(pm.st.mul, "--"));
|
||||
log("ffY: %s\n", log_id(pm.st.ffY, "--"));
|
||||
log("addAB: %s\n", log_id(pm.st.addAB, "--"));
|
||||
log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
|
||||
log("ffS: %s\n", log_id(pm.st.ffS, "--"));
|
||||
#endif
|
||||
|
||||
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
|
||||
|
||||
if (GetSize(pm.st.sigA) > 16) {
|
||||
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigB) > 16) {
|
||||
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigS) > 32) {
|
||||
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigY) > 32) {
|
||||
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
|
||||
return;
|
||||
}
|
||||
|
||||
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
|
||||
|
||||
if (mul_signed) {
|
||||
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log(" replacing $mul with SB_MAC16 cell.\n");
|
||||
|
||||
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
|
||||
pm.module->swap_names(cell, pm.st.mul);
|
||||
|
||||
// SB_MAC16 Input Interface
|
||||
|
||||
SigSpec A = pm.st.sigA;
|
||||
A.extend_u0(16, mul_signed);
|
||||
|
||||
SigSpec B = pm.st.sigB;
|
||||
B.extend_u0(16, mul_signed);
|
||||
|
||||
SigSpec CD;
|
||||
if (pm.st.muxA)
|
||||
CD = pm.st.muxA->getPort("\\B");
|
||||
if (pm.st.muxB)
|
||||
CD = pm.st.muxB->getPort("\\A");
|
||||
CD.extend_u0(32, mul_signed);
|
||||
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\C", CD.extract(0, 16));
|
||||
cell->setPort("\\D", CD.extract(16, 16));
|
||||
|
||||
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
|
||||
|
||||
cell->setPort("\\AHOLD", State::S0);
|
||||
cell->setPort("\\BHOLD", State::S0);
|
||||
cell->setPort("\\CHOLD", State::S0);
|
||||
cell->setPort("\\DHOLD", State::S0);
|
||||
|
||||
cell->setPort("\\IRSTTOP", State::S0);
|
||||
cell->setPort("\\IRSTBOT", State::S0);
|
||||
|
||||
if (pm.st.clock_vld)
|
||||
{
|
||||
cell->setPort("\\CLK", pm.st.clock);
|
||||
cell->setPort("\\CE", State::S1);
|
||||
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
|
||||
|
||||
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
|
||||
|
||||
if (pm.st.ffA)
|
||||
log(" ffA:%s", log_id(pm.st.ffA));
|
||||
|
||||
if (pm.st.ffB)
|
||||
log(" ffB:%s", log_id(pm.st.ffB));
|
||||
|
||||
if (pm.st.ffY)
|
||||
log(" ffY:%s", log_id(pm.st.ffY));
|
||||
|
||||
if (pm.st.ffS)
|
||||
log(" ffS:%s", log_id(pm.st.ffS));
|
||||
|
||||
log("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
cell->setPort("\\CLK", State::S0);
|
||||
cell->setPort("\\CE", State::S0);
|
||||
cell->setParam("\\NEG_TRIGGER", State::S0);
|
||||
}
|
||||
|
||||
// SB_MAC16 Cascade Interface
|
||||
|
||||
cell->setPort("\\SIGNEXTIN", State::Sx);
|
||||
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\CI", State::Sx);
|
||||
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\ACCUMCI", State::Sx);
|
||||
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
|
||||
|
||||
// SB_MAC16 Output Interface
|
||||
|
||||
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
|
||||
if (GetSize(O) < 32)
|
||||
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
||||
|
||||
cell->setPort("\\O", O);
|
||||
|
||||
if (pm.st.addAB) {
|
||||
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
|
||||
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
} else {
|
||||
cell->setPort("\\ADDSUBTOP", State::S0);
|
||||
cell->setPort("\\ADDSUBBOT", State::S0);
|
||||
}
|
||||
|
||||
cell->setPort("\\ORSTTOP", State::S0);
|
||||
cell->setPort("\\ORSTBOT", State::S0);
|
||||
|
||||
cell->setPort("\\OHOLDTOP", State::S0);
|
||||
cell->setPort("\\OHOLDBOT", State::S0);
|
||||
|
||||
SigSpec acc_reset = State::S0;
|
||||
if (pm.st.muxA)
|
||||
acc_reset = pm.st.muxA->getPort("\\S");
|
||||
if (pm.st.muxB)
|
||||
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
|
||||
|
||||
cell->setPort("\\OLOADTOP", acc_reset);
|
||||
cell->setPort("\\OLOADBOT", acc_reset);
|
||||
|
||||
// SB_MAC16 Remaining Parameters
|
||||
|
||||
cell->setParam("\\C_REG", State::S0);
|
||||
cell->setParam("\\D_REG", State::S0);
|
||||
|
||||
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
|
||||
|
||||
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
|
||||
|
||||
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
|
||||
|
||||
cell->setParam("\\MODE_8x8", State::S0);
|
||||
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
|
||||
pm.autoremove(pm.st.mul);
|
||||
pm.autoremove(pm.st.ffY);
|
||||
pm.autoremove(pm.st.ffS);
|
||||
}
|
||||
|
||||
struct Ice40DspPass : public Pass {
|
||||
Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ice40_dsp [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-singleton") {
|
||||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
|
||||
}
|
||||
} Ice40DspPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
160
passes/pmgen/ice40_dsp.pmg
Normal file
160
passes/pmgen/ice40_dsp.pmg
Normal file
|
@ -0,0 +1,160 @@
|
|||
state <SigBit> clock
|
||||
state <bool> clock_pol clock_vld
|
||||
state <SigSpec> sigA sigB sigY sigS
|
||||
state <Cell*> addAB muxAB
|
||||
|
||||
match mul
|
||||
select mul->type.in($mul)
|
||||
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||
select GetSize(mul->getPort(\Y)) > 10
|
||||
endmatch
|
||||
|
||||
match ffA
|
||||
select ffA->type.in($dff)
|
||||
// select nusers(port(ffA, \Q)) == 2
|
||||
index <SigSpec> port(ffA, \Q) === port(mul, \A)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigA clock clock_pol clock_vld
|
||||
sigA = port(mul, \A);
|
||||
|
||||
if (ffA) {
|
||||
sigA = port(ffA, \D);
|
||||
|
||||
clock = port(ffA, \CLK).as_bit();
|
||||
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffB
|
||||
select ffB->type.in($dff)
|
||||
// select nusers(port(ffB, \Q)) == 2
|
||||
index <SigSpec> port(ffB, \Q) === port(mul, \B)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigB clock clock_pol clock_vld
|
||||
sigB = port(mul, \B);
|
||||
|
||||
if (ffB) {
|
||||
sigB = port(ffB, \D);
|
||||
SigBit c = port(ffB, \CLK).as_bit();
|
||||
bool cp = param(ffB, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffY
|
||||
select ffY->type.in($dff)
|
||||
select nusers(port(ffY, \D)) == 2
|
||||
index <SigSpec> port(ffY, \D) === port(mul, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigY clock clock_pol clock_vld
|
||||
sigY = port(mul, \Y);
|
||||
|
||||
if (ffY) {
|
||||
sigY = port(ffY, \Q);
|
||||
SigBit c = port(ffY, \CLK).as_bit();
|
||||
bool cp = param(ffY, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match addA
|
||||
select addA->type.in($add)
|
||||
select nusers(port(addA, \A)) == 2
|
||||
index <SigSpec> port(addA, \A) === sigY
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match addB
|
||||
if !addA
|
||||
select addB->type.in($add, $sub)
|
||||
select nusers(port(addB, \B)) == 2
|
||||
index <SigSpec> port(addB, \B) === sigY
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code addAB sigS
|
||||
if (addA) {
|
||||
addAB = addA;
|
||||
sigS = port(addA, \B);
|
||||
}
|
||||
if (addB) {
|
||||
addAB = addB;
|
||||
sigS = port(addB, \A);
|
||||
}
|
||||
if (addAB) {
|
||||
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||
int actual_mul_width = GetSize(sigY);
|
||||
int actual_acc_width = GetSize(sigS);
|
||||
|
||||
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||
reject;
|
||||
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match muxA
|
||||
if addAB
|
||||
select muxA->type.in($mux)
|
||||
select nusers(port(muxA, \A)) == 2
|
||||
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match muxB
|
||||
if addAB
|
||||
if !muxA
|
||||
select muxB->type.in($mux)
|
||||
select nusers(port(muxB, \B)) == 2
|
||||
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code muxAB
|
||||
muxAB = addAB;
|
||||
if (muxA)
|
||||
muxAB = muxA;
|
||||
if (muxB)
|
||||
muxAB = muxB;
|
||||
endcode
|
||||
|
||||
match ffS
|
||||
if muxAB
|
||||
select ffS->type.in($dff)
|
||||
select nusers(port(ffS, \D)) == 2
|
||||
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
|
||||
index <SigSpec> port(ffS, \Q) === sigS
|
||||
endmatch
|
||||
|
||||
code clock clock_pol clock_vld
|
||||
if (ffS) {
|
||||
SigBit c = port(ffS, \CLK).as_bit();
|
||||
bool cp = param(ffS, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
486
passes/pmgen/pmgen.py
Normal file
486
passes/pmgen/pmgen.py
Normal file
|
@ -0,0 +1,486 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import sys
|
||||
import pprint
|
||||
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
|
||||
pmgfile = sys.argv[1]
|
||||
assert pmgfile.endswith(".pmg")
|
||||
prefix = pmgfile[0:-4]
|
||||
prefix = prefix.split('/')[-1]
|
||||
outfile = sys.argv[2]
|
||||
|
||||
state_types = dict()
|
||||
udata_types = dict()
|
||||
blocks = list()
|
||||
ids = dict()
|
||||
|
||||
def rewrite_cpp(s):
|
||||
t = list()
|
||||
i = 0
|
||||
while i < len(s):
|
||||
if s[i] in ("'", '"') and i + 1 < len(s):
|
||||
j = i + 1
|
||||
while j + 1 < len(s) and s[j] != s[i]:
|
||||
if s[j] == '\\' and j + 1 < len(s):
|
||||
j += 1
|
||||
j += 1
|
||||
t.append(s[i:j+1])
|
||||
i = j + 1
|
||||
continue
|
||||
|
||||
if s[i] in ('$', '\\') and i + 1 < len(s):
|
||||
j = i + 1
|
||||
while True:
|
||||
if j == len(s):
|
||||
j -= 1
|
||||
break
|
||||
if ord('a') <= ord(s[j]) <= ord('z'):
|
||||
j += 1
|
||||
continue
|
||||
if ord('A') <= ord(s[j]) <= ord('Z'):
|
||||
j += 1
|
||||
continue
|
||||
if ord('0') <= ord(s[j]) <= ord('9'):
|
||||
j += 1
|
||||
continue
|
||||
if s[j] == '_':
|
||||
j += 1
|
||||
continue
|
||||
j -= 1
|
||||
break
|
||||
|
||||
n = s[i:j+1]
|
||||
i = j + 1
|
||||
|
||||
if n[0] == '$':
|
||||
v = "id_d_" + n[1:]
|
||||
else:
|
||||
v = "id_b_" + n[1:]
|
||||
|
||||
if v not in ids:
|
||||
ids[v] = n
|
||||
else:
|
||||
assert ids[v] == n
|
||||
|
||||
t.append(v)
|
||||
continue
|
||||
|
||||
if s[i] == "\t":
|
||||
t.append(" ")
|
||||
else:
|
||||
t.append(s[i])
|
||||
|
||||
i += 1
|
||||
|
||||
return "".join(t)
|
||||
|
||||
with open(pmgfile, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line == "": break
|
||||
line = line.strip()
|
||||
|
||||
cmd = line.split()
|
||||
if len(cmd) == 0 or cmd[0].startswith("//"): continue
|
||||
cmd = cmd[0]
|
||||
|
||||
if cmd == "state":
|
||||
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||
assert m
|
||||
type_str = m.group(1)
|
||||
states_str = m.group(2)
|
||||
for s in re.split(r"\s+", states_str):
|
||||
assert s not in state_types
|
||||
state_types[s] = type_str
|
||||
continue
|
||||
|
||||
if cmd == "udata":
|
||||
m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||
assert m
|
||||
type_str = m.group(1)
|
||||
udatas_str = m.group(2)
|
||||
for s in re.split(r"\s+", udatas_str):
|
||||
assert s not in udata_types
|
||||
udata_types[s] = type_str
|
||||
continue
|
||||
|
||||
if cmd == "match":
|
||||
block = dict()
|
||||
block["type"] = "match"
|
||||
|
||||
line = line.split()
|
||||
assert len(line) == 2
|
||||
assert line[1] not in state_types
|
||||
block["cell"] = line[1]
|
||||
state_types[line[1]] = "Cell*";
|
||||
|
||||
block["if"] = list()
|
||||
block["select"] = list()
|
||||
block["index"] = list()
|
||||
block["filter"] = list()
|
||||
block["optional"] = False
|
||||
|
||||
while True:
|
||||
l = f.readline()
|
||||
assert l != ""
|
||||
a = l.split()
|
||||
if len(a) == 0 or a[0].startswith("//"): continue
|
||||
if a[0] == "endmatch": break
|
||||
|
||||
if a[0] == "if":
|
||||
b = l.lstrip()[2:]
|
||||
block["if"].append(rewrite_cpp(b.strip()))
|
||||
continue
|
||||
|
||||
if a[0] == "select":
|
||||
b = l.lstrip()[6:]
|
||||
block["select"].append(rewrite_cpp(b.strip()))
|
||||
continue
|
||||
|
||||
if a[0] == "index":
|
||||
m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
|
||||
assert m
|
||||
block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
|
||||
continue
|
||||
|
||||
if a[0] == "filter":
|
||||
b = l.lstrip()[6:]
|
||||
block["filter"].append(rewrite_cpp(b.strip()))
|
||||
continue
|
||||
|
||||
if a[0] == "optional":
|
||||
block["optional"] = True
|
||||
continue
|
||||
|
||||
assert False
|
||||
|
||||
blocks.append(block)
|
||||
|
||||
if cmd == "code":
|
||||
block = dict()
|
||||
block["type"] = "code"
|
||||
block["code"] = list()
|
||||
block["states"] = set()
|
||||
|
||||
for s in line.split()[1:]:
|
||||
assert s in state_types
|
||||
block["states"].add(s)
|
||||
|
||||
while True:
|
||||
l = f.readline()
|
||||
assert l != ""
|
||||
a = l.split()
|
||||
if len(a) == 0: continue
|
||||
if a[0] == "endcode": break
|
||||
|
||||
block["code"].append(rewrite_cpp(l.rstrip()))
|
||||
|
||||
blocks.append(block)
|
||||
|
||||
with open(outfile, "w") as f:
|
||||
print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("#include \"kernel/yosys.h\"", file=f)
|
||||
print("#include \"kernel/sigtools.h\"", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("YOSYS_NAMESPACE_BEGIN", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("struct {}_pm {{".format(prefix), file=f)
|
||||
print(" Module *module;", file=f)
|
||||
print(" SigMap sigmap;", file=f)
|
||||
print(" std::function<void()> on_accept;".format(prefix), file=f)
|
||||
print("", file=f)
|
||||
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
if block["type"] == "match":
|
||||
index_types = list()
|
||||
for entry in block["index"]:
|
||||
index_types.append(entry[0])
|
||||
print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
|
||||
print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
|
||||
print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
|
||||
print(" pool<Cell*> blacklist_cells;", file=f)
|
||||
print(" pool<Cell*> autoremove_cells;", file=f)
|
||||
print(" bool blacklist_dirty;", file=f)
|
||||
print(" int rollback;", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" struct state_t {", file=f)
|
||||
for s, t in sorted(state_types.items()):
|
||||
print(" {} {};".format(t, s), file=f)
|
||||
print(" } st;", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" struct udata_t {", file=f)
|
||||
for s, t in sorted(udata_types.items()):
|
||||
print(" {} {};".format(t, s), file=f)
|
||||
print(" } ud;", file=f)
|
||||
print("", file=f)
|
||||
|
||||
for v, n in sorted(ids.items()):
|
||||
if n[0] == "\\":
|
||||
print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f)
|
||||
else:
|
||||
print(" IdString {}{{\"{}\"}};".format(v, n), file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
|
||||
print(" for (auto bit : sigmap(sig)) {", file=f)
|
||||
print(" if (bit.wire == nullptr) continue;", file=f)
|
||||
print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
|
||||
print(" sigusers[bit].insert(nullptr);", file=f)
|
||||
print(" sigusers[bit].insert(cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void blacklist(Cell *cell) {", file=f)
|
||||
print(" if (cell != nullptr) {", file=f)
|
||||
print(" if (blacklist_cells.insert(cell).second)", file=f)
|
||||
print(" blacklist_dirty = true;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void autoremove(Cell *cell) {", file=f)
|
||||
print(" if (cell != nullptr) {", file=f)
|
||||
print(" if (blacklist_cells.insert(cell).second)", file=f)
|
||||
print(" blacklist_dirty = true;", file=f)
|
||||
print(" autoremove_cells.insert(cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void check_blacklist() {", file=f)
|
||||
print(" if (!blacklist_dirty)", file=f)
|
||||
print(" return;", file=f)
|
||||
print(" blacklist_dirty = false;", file=f)
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
if block["type"] == "match":
|
||||
print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
|
||||
print(" rollback = {};".format(index+1), file=f)
|
||||
print(" return;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" rollback = 0;", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
|
||||
print(" return sigmap(cell->getPort(portname));", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" Const param(Cell *cell, IdString paramname) {", file=f)
|
||||
print(" return cell->getParam(paramname);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" int nusers(const SigSpec &sig) {", file=f)
|
||||
print(" pool<Cell*> users;", file=f)
|
||||
print(" for (auto bit : sigmap(sig))", file=f)
|
||||
print(" for (auto user : sigusers[bit])", file=f)
|
||||
print(" users.insert(user);", file=f)
|
||||
print(" return GetSize(users);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
|
||||
print(" module(module), sigmap(module) {", file=f)
|
||||
for s, t in sorted(udata_types.items()):
|
||||
if t.endswith("*"):
|
||||
print(" ud.{} = nullptr;".format(s), file=f)
|
||||
else:
|
||||
print(" ud.{} = {}();".format(s, t), file=f)
|
||||
print(" for (auto cell : module->cells()) {", file=f)
|
||||
print(" for (auto &conn : cell->connections())", file=f)
|
||||
print(" add_siguser(conn.second, cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print(" for (auto cell : cells) {", file=f)
|
||||
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
if block["type"] == "match":
|
||||
print(" do {", file=f)
|
||||
print(" Cell *{} = cell;".format(block["cell"]), file=f)
|
||||
for expr in block["select"]:
|
||||
print(" if (!({})) break;".format(expr), file=f)
|
||||
print(" index_{}_key_type key;".format(index), file=f)
|
||||
for field, entry in enumerate(block["index"]):
|
||||
print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
|
||||
print(" index_{}[key].push_back(cell);".format(index), file=f)
|
||||
print(" } while (0);", file=f)
|
||||
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" ~{}_pm() {{".format(prefix), file=f)
|
||||
print(" for (auto cell : autoremove_cells)", file=f)
|
||||
print(" module->remove(cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void run(std::function<void()> on_accept_f) {", file=f)
|
||||
print(" on_accept = on_accept_f;", file=f)
|
||||
print(" rollback = 0;", file=f)
|
||||
print(" blacklist_dirty = false;", file=f)
|
||||
for s, t in sorted(state_types.items()):
|
||||
if t.endswith("*"):
|
||||
print(" st.{} = nullptr;".format(s), file=f)
|
||||
else:
|
||||
print(" st.{} = {}();".format(s, t), file=f)
|
||||
print(" block_0();", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
|
||||
print(" run([&](){on_accept_f(*this);});", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
|
||||
print(" void block_{}() {{".format(index), file=f)
|
||||
|
||||
const_st = set()
|
||||
nonconst_st = set()
|
||||
restore_st = set()
|
||||
|
||||
for i in range(index):
|
||||
if blocks[i]["type"] == "code":
|
||||
for s in blocks[i]["states"]:
|
||||
const_st.add(s)
|
||||
elif blocks[i]["type"] == "match":
|
||||
const_st.add(blocks[i]["cell"])
|
||||
else:
|
||||
assert False
|
||||
|
||||
if block["type"] == "code":
|
||||
for s in block["states"]:
|
||||
if s in const_st:
|
||||
const_st.remove(s)
|
||||
restore_st.add(s)
|
||||
nonconst_st.add(s)
|
||||
elif block["type"] == "match":
|
||||
s = block["cell"]
|
||||
assert s not in const_st
|
||||
nonconst_st.add(s)
|
||||
else:
|
||||
assert False
|
||||
|
||||
for s in sorted(const_st):
|
||||
t = state_types[s]
|
||||
if t.endswith("*"):
|
||||
print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||
else:
|
||||
print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||
|
||||
for s in sorted(nonconst_st):
|
||||
t = state_types[s]
|
||||
print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||
|
||||
if len(restore_st):
|
||||
print("", file=f)
|
||||
for s in sorted(restore_st):
|
||||
t = state_types[s]
|
||||
print(" {} backup_{} = {};".format(t, s, s), file=f)
|
||||
|
||||
if block["type"] == "code":
|
||||
print("", file=f)
|
||||
print(" do {", file=f)
|
||||
print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
|
||||
print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
|
||||
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
|
||||
|
||||
for line in block["code"]:
|
||||
print(" " + line, file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
print("#undef reject", file=f)
|
||||
print("#undef accept", file=f)
|
||||
print("#undef branch", file=f)
|
||||
print(" } while (0);", file=f)
|
||||
print("", file=f)
|
||||
print("rollback_label:", file=f)
|
||||
print(" YS_ATTRIBUTE(unused);", file=f)
|
||||
|
||||
if len(restore_st) or len(nonconst_st):
|
||||
print("", file=f)
|
||||
for s in sorted(restore_st):
|
||||
t = state_types[s]
|
||||
print(" {} = backup_{};".format(s, s), file=f)
|
||||
for s in sorted(nonconst_st):
|
||||
if s not in restore_st:
|
||||
t = state_types[s]
|
||||
if t.endswith("*"):
|
||||
print(" {} = nullptr;".format(s), file=f)
|
||||
else:
|
||||
print(" {} = {}();".format(s, t), file=f)
|
||||
|
||||
elif block["type"] == "match":
|
||||
assert len(restore_st) == 0
|
||||
|
||||
if len(block["if"]):
|
||||
for expr in block["if"]:
|
||||
print("", file=f)
|
||||
print(" if (!({})) {{".format(expr), file=f)
|
||||
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
print(" return;", file=f)
|
||||
print(" }", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" index_{}_key_type key;".format(index), file=f)
|
||||
for field, entry in enumerate(block["index"]):
|
||||
print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
|
||||
print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
|
||||
print(" {} = cells[idx];".format(block["cell"]), file=f)
|
||||
print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
|
||||
for expr in block["filter"]:
|
||||
print(" if (!({})) continue;".format(expr), file=f)
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
print(" if (rollback) {", file=f)
|
||||
print(" if (rollback != {}) {{".format(index+1), file=f)
|
||||
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||
print(" return;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" rollback = 0;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||
|
||||
if block["optional"]:
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void block_{}() {{".format(len(blocks)), file=f)
|
||||
print(" on_accept();", file=f)
|
||||
print(" check_blacklist();", file=f)
|
||||
print(" }", file=f)
|
||||
print("};", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print("YOSYS_NAMESPACE_END", file=f)
|
||||
|
||||
# pp.pprint(blocks)
|
|
@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct ProcPass : public Pass {
|
||||
ProcPass() : Pass("proc", "translate processes to netlists") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -57,7 +57,7 @@ struct ProcPass : public Pass {
|
|||
log(" executed in -ifx mode.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string global_arst;
|
||||
bool ifxmode = false;
|
||||
|
|
|
@ -203,7 +203,7 @@ restart_proc_arst:
|
|||
|
||||
struct ProcArstPass : public Pass {
|
||||
ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -221,7 +221,7 @@ struct ProcArstPass : public Pass {
|
|||
log(" in the 'init' attribute on the net.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string global_arst;
|
||||
bool global_arst_neg = false;
|
||||
|
|
|
@ -77,18 +77,42 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
|
|||
}
|
||||
else
|
||||
{
|
||||
bool all_cases_are_empty = true;
|
||||
for (auto cs : sw->cases) {
|
||||
if (cs->actions.size() != 0 || cs->switches.size() != 0)
|
||||
all_cases_are_empty = false;
|
||||
bool all_fully_def = true;
|
||||
for (auto cs : sw->cases)
|
||||
{
|
||||
if (max_depth != 0)
|
||||
proc_clean_case(cs, did_something, count, max_depth-1);
|
||||
int size = 0;
|
||||
for (auto cmp : cs->compare)
|
||||
{
|
||||
size += cmp.size();
|
||||
if (!cmp.is_fully_def())
|
||||
all_fully_def = false;
|
||||
}
|
||||
if (sw->signal.size() != size)
|
||||
all_fully_def = false;
|
||||
}
|
||||
if (all_cases_are_empty) {
|
||||
did_something = true;
|
||||
for (auto cs : sw->cases)
|
||||
delete cs;
|
||||
sw->cases.clear();
|
||||
if (all_fully_def)
|
||||
{
|
||||
for (auto cs = sw->cases.begin(); cs != sw->cases.end();)
|
||||
{
|
||||
if ((*cs)->empty())
|
||||
{
|
||||
did_something = true;
|
||||
delete *cs;
|
||||
cs = sw->cases.erase(cs);
|
||||
}
|
||||
else ++cs;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!sw->cases.empty() && sw->cases.back()->empty())
|
||||
{
|
||||
did_something = true;
|
||||
delete sw->cases.back();
|
||||
sw->cases.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +130,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m
|
|||
}
|
||||
for (size_t i = 0; i < cs->switches.size(); i++) {
|
||||
RTLIL::SwitchRule *sw = cs->switches[i];
|
||||
if (sw->cases.size() == 0) {
|
||||
if (sw->empty()) {
|
||||
cs->switches.erase(cs->switches.begin() + (i--));
|
||||
did_something = true;
|
||||
delete sw;
|
||||
|
@ -143,7 +167,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
|
|||
|
||||
struct ProcCleanPass : public Pass {
|
||||
ProcCleanPass() : Pass("proc_clean", "remove empty parts of processes") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -153,7 +177,7 @@ struct ProcCleanPass : public Pass {
|
|||
log("if it contains only empty structures.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
int total_count = 0;
|
||||
log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
|
||||
|
|
|
@ -370,7 +370,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
|
|||
|
||||
struct ProcDffPass : public Pass {
|
||||
ProcDffPass() : Pass("proc_dff", "extract flip-flops from processes") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -380,7 +380,7 @@ struct ProcDffPass : public Pass {
|
|||
log("d-type flip-flop cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n");
|
||||
|
||||
|
|
|
@ -422,7 +422,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
|||
|
||||
struct ProcDlatchPass : public Pass {
|
||||
ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -432,7 +432,7 @@ struct ProcDlatchPass : public Pass {
|
|||
log("d-type latches.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n");
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
|
|||
|
||||
struct ProcInitPass : public Pass {
|
||||
ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -113,7 +113,7 @@ struct ProcInitPass : public Pass {
|
|||
log("respective wire.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing PROC_INIT pass (extract init attributes).\n");
|
||||
|
||||
|
|
|
@ -382,7 +382,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
|
|||
|
||||
struct ProcMuxPass : public Pass {
|
||||
ProcMuxPass() : Pass("proc_mux", "convert decision trees to multiplexers") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -396,7 +396,7 @@ struct ProcMuxPass : public Pass {
|
|||
log(" 'case' expressions and 'if' conditions.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool ifxmode = false;
|
||||
log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n");
|
||||
|
|
|
@ -65,7 +65,7 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
|
|||
|
||||
struct ProcRmdeadPass : public Pass {
|
||||
ProcRmdeadPass() : Pass("proc_rmdead", "eliminate dead trees in decision trees") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -74,7 +74,7 @@ struct ProcRmdeadPass : public Pass {
|
|||
log("This pass identifies unreachable branches in decision trees and removes them.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n");
|
||||
|
||||
|
|
|
@ -7,4 +7,9 @@ OBJS += passes/sat/miter.o
|
|||
OBJS += passes/sat/expose.o
|
||||
OBJS += passes/sat/assertpmux.o
|
||||
OBJS += passes/sat/clk2fflogic.o
|
||||
OBJS += passes/sat/async2sync.o
|
||||
OBJS += passes/sat/supercover.o
|
||||
OBJS += passes/sat/fmcombine.o
|
||||
OBJS += passes/sat/mutate.o
|
||||
OBJS += passes/sat/cutpoint.o
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ struct AssertpmuxWorker
|
|||
|
||||
struct AssertpmuxPass : public Pass {
|
||||
AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -199,7 +199,7 @@ struct AssertpmuxPass : public Pass {
|
|||
log(" additional constrained and check the $pmux condition always.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_noinit = false;
|
||||
bool flag_always = false;
|
||||
|
|
196
passes/sat/async2sync.cc
Normal file
196
passes/sat/async2sync.cc
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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 Async2syncPass : public Pass {
|
||||
Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" async2sync [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command replaces async FF inputs with sync circuits emulating the same\n");
|
||||
log("behavior for when the async signals are actually synchronized to the clock.\n");
|
||||
log("\n");
|
||||
log("This pass assumes negative hold time for the async FF inputs. For example when\n");
|
||||
log("a reset deasserts with the clock edge, then the FF output will still drive the\n");
|
||||
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
||||
log("the clock edge.\n");
|
||||
log("\n");
|
||||
log("Currently only $adff and $dffsr cells are supported by this pass.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
// bool flag_noinit = false;
|
||||
|
||||
log_header(design, "Executing ASYNC2SYNC pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-noinit") {
|
||||
// flag_noinit = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, State> initbits;
|
||||
pool<SigBit> del_initbits;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
if (wire->attributes.count("\\init") > 0)
|
||||
{
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
SigSpec initsig = sigmap(wire);
|
||||
|
||||
for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
|
||||
if (initval[i] == State::S0 || initval[i] == State::S1)
|
||||
initbits[initsig[i]] = initval[i];
|
||||
}
|
||||
|
||||
for (auto cell : vector<Cell*>(module->selected_cells()))
|
||||
{
|
||||
if (cell->type.in("$adff"))
|
||||
{
|
||||
// bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
|
||||
bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();
|
||||
Const arst_val = cell->parameters["\\ARST_VALUE"];
|
||||
|
||||
// SigSpec sig_clk = cell->getPort("\\CLK");
|
||||
SigSpec sig_arst = cell->getPort("\\ARST");
|
||||
SigSpec sig_d = cell->getPort("\\D");
|
||||
SigSpec sig_q = cell->getPort("\\Q");
|
||||
|
||||
log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(sig_arst), log_signal(sig_d), log_signal(sig_q));
|
||||
|
||||
Const init_val;
|
||||
for (int i = 0; i < GetSize(sig_q); i++) {
|
||||
SigBit bit = sigmap(sig_q[i]);
|
||||
init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
|
||||
del_initbits.insert(bit);
|
||||
}
|
||||
|
||||
Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
|
||||
Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
|
||||
new_q->attributes["\\init"] = init_val;
|
||||
|
||||
if (arst_pol) {
|
||||
module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d);
|
||||
module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q);
|
||||
} else {
|
||||
module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d);
|
||||
module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q);
|
||||
}
|
||||
|
||||
cell->setPort("\\D", new_d);
|
||||
cell->setPort("\\Q", new_q);
|
||||
cell->unsetPort("\\ARST");
|
||||
cell->unsetParam("\\ARST_POLARITY");
|
||||
cell->unsetParam("\\ARST_VALUE");
|
||||
cell->type = "$dff";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$dffsr"))
|
||||
{
|
||||
// bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
|
||||
bool set_pol = cell->parameters["\\SET_POLARITY"].as_bool();
|
||||
bool clr_pol = cell->parameters["\\CLR_POLARITY"].as_bool();
|
||||
|
||||
// SigSpec sig_clk = cell->getPort("\\CLK");
|
||||
SigSpec sig_set = cell->getPort("\\SET");
|
||||
SigSpec sig_clr = cell->getPort("\\CLR");
|
||||
SigSpec sig_d = cell->getPort("\\D");
|
||||
SigSpec sig_q = cell->getPort("\\Q");
|
||||
|
||||
log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
|
||||
|
||||
Const init_val;
|
||||
for (int i = 0; i < GetSize(sig_q); i++) {
|
||||
SigBit bit = sigmap(sig_q[i]);
|
||||
init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
|
||||
del_initbits.insert(bit);
|
||||
}
|
||||
|
||||
Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
|
||||
Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
|
||||
new_q->attributes["\\init"] = init_val;
|
||||
|
||||
if (!set_pol)
|
||||
sig_set = module->Not(NEW_ID, sig_set);
|
||||
|
||||
if (clr_pol)
|
||||
sig_clr = module->Not(NEW_ID, sig_clr);
|
||||
|
||||
SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
|
||||
module->addAnd(NEW_ID, tmp, sig_clr, new_d);
|
||||
|
||||
tmp = module->Or(NEW_ID, new_q, sig_set);
|
||||
module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
|
||||
|
||||
cell->setPort("\\D", new_d);
|
||||
cell->setPort("\\Q", new_q);
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
cell->unsetParam("\\SET_POLARITY");
|
||||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->type = "$dff";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wire : module->wires())
|
||||
if (wire->attributes.count("\\init") > 0)
|
||||
{
|
||||
bool delete_initattr = true;
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
SigSpec initsig = sigmap(wire);
|
||||
|
||||
for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
|
||||
if (del_initbits.count(initsig[i]) > 0)
|
||||
initval[i] = State::Sx;
|
||||
else if (initval[i] != State::Sx)
|
||||
delete_initattr = false;
|
||||
|
||||
if (delete_initattr)
|
||||
wire->attributes.erase("\\init");
|
||||
else
|
||||
wire->attributes.at("\\init") = initval;
|
||||
}
|
||||
}
|
||||
}
|
||||
} Async2syncPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct Clk2fflogicPass : public Pass {
|
||||
Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -36,7 +36,7 @@ struct Clk2fflogicPass : public Pass {
|
|||
log("multiple clocks.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
// bool flag_noinit = false;
|
||||
|
||||
|
|
168
passes/sat/cutpoint.cc
Normal file
168
passes/sat/cutpoint.cc
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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 CutpointPass : public Pass {
|
||||
CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" cutpoint [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command adds formal cut points to the design.\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" set cupoint nets to undef (x). the default behavior is to create a\n");
|
||||
log(" $anyseq cell and drive the cutpoint net from that\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_undef = false;
|
||||
|
||||
log_header(design, "Executing CUTPOINT pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-undef") {
|
||||
flag_undef = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (design->selected_whole_module(module->name)) {
|
||||
log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module));
|
||||
module->new_connections(std::vector<RTLIL::SigSig>());
|
||||
for (auto cell : vector<Cell*>(module->cells()))
|
||||
module->remove(cell);
|
||||
vector<Wire*> output_wires;
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_output)
|
||||
output_wires.push_back(wire);
|
||||
for (auto wire : output_wires)
|
||||
module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire)));
|
||||
continue;
|
||||
}
|
||||
|
||||
SigMap sigmap(module);
|
||||
pool<SigBit> cutpoint_bits;
|
||||
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type == "$anyseq")
|
||||
continue;
|
||||
log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell));
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (cell->output(conn.first))
|
||||
module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second)));
|
||||
}
|
||||
module->remove(cell);
|
||||
}
|
||||
|
||||
for (auto wire : module->selected_wires()) {
|
||||
if (wire->port_output) {
|
||||
log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
|
||||
Wire *new_wire = module->addWire(NEW_ID, wire);
|
||||
module->swap_names(wire, new_wire);
|
||||
module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire)));
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
continue;
|
||||
}
|
||||
log("Making wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
|
||||
for (auto bit : sigmap(wire))
|
||||
cutpoint_bits.insert(bit);
|
||||
}
|
||||
|
||||
if (!cutpoint_bits.empty())
|
||||
{
|
||||
for (auto cell : module->cells()) {
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!cell->output(conn.first))
|
||||
continue;
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
int bit_count = 0;
|
||||
for (auto &bit : sig) {
|
||||
if (cutpoint_bits.count(bit))
|
||||
bit_count++;
|
||||
}
|
||||
if (bit_count == 0)
|
||||
continue;
|
||||
SigSpec dummy = module->addWire(NEW_ID, bit_count);
|
||||
bit_count = 0;
|
||||
for (auto &bit : sig) {
|
||||
if (cutpoint_bits.count(bit))
|
||||
bit = dummy[bit_count++];
|
||||
}
|
||||
cell->setPort(conn.first, sig);
|
||||
}
|
||||
}
|
||||
|
||||
vector<Wire*> rewrite_wires;
|
||||
for (auto wire : module->wires()) {
|
||||
if (!wire->port_input)
|
||||
continue;
|
||||
int bit_count = 0;
|
||||
for (auto &bit : sigmap(wire))
|
||||
if (cutpoint_bits.count(bit))
|
||||
bit_count++;
|
||||
if (bit_count)
|
||||
rewrite_wires.push_back(wire);
|
||||
}
|
||||
|
||||
for (auto wire : rewrite_wires) {
|
||||
Wire *new_wire = module->addWire(NEW_ID, wire);
|
||||
SigSpec lhs, rhs, sig = sigmap(wire);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (!cutpoint_bits.count(sig[i])) {
|
||||
lhs.append(SigBit(wire, i));
|
||||
rhs.append(SigBit(new_wire, i));
|
||||
}
|
||||
if (GetSize(lhs))
|
||||
module->connect(lhs, rhs);
|
||||
module->swap_names(wire, new_wire);
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
}
|
||||
|
||||
SigSpec sig(cutpoint_bits);
|
||||
sig.sort_and_unify();
|
||||
|
||||
for (auto chunk : sig.chunks()) {
|
||||
SigSpec s(chunk);
|
||||
module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} CutpointPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -360,7 +360,7 @@ struct VlogHammerReporter
|
|||
|
||||
struct EvalPass : public Pass {
|
||||
EvalPass() : Pass("eval", "evaluate the circuit given an input") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -383,7 +383,7 @@ struct EvalPass : public Pass {
|
|||
log(" then all output ports of the current module are used.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> sets;
|
||||
std::vector<std::string> shows, tables;
|
||||
|
|
|
@ -220,7 +220,7 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width
|
|||
|
||||
struct ExposePass : public Pass {
|
||||
ExposePass() : Pass("expose", "convert internal signals to module ports") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -257,7 +257,7 @@ struct ExposePass : public Pass {
|
|||
log(" designator for the exposed signal.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_shared = false;
|
||||
bool flag_evert = false;
|
||||
|
|
341
passes/sat/fmcombine.cc
Normal file
341
passes/sat/fmcombine.cc
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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"
|
||||
#include "kernel/celltypes.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct opts_t
|
||||
{
|
||||
bool fwd = false;
|
||||
bool bwd = false;
|
||||
bool nop = false;
|
||||
};
|
||||
|
||||
struct FmcombineWorker
|
||||
{
|
||||
const opts_t &opts;
|
||||
Design *design;
|
||||
Module *original = nullptr;
|
||||
Module *module = nullptr;
|
||||
IdString orig_type, combined_type;
|
||||
|
||||
FmcombineWorker(Design *design, IdString orig_type, const opts_t &opts) :
|
||||
opts(opts), design(design), original(design->module(orig_type)),
|
||||
orig_type(orig_type), combined_type("$fmcombine" + orig_type.str())
|
||||
{
|
||||
}
|
||||
|
||||
SigSpec import_sig(SigSpec sig, const string &suffix)
|
||||
{
|
||||
SigSpec newsig;
|
||||
for (auto chunk : sig.chunks()) {
|
||||
if (chunk.wire != nullptr)
|
||||
chunk.wire = module->wire(chunk.wire->name.str() + suffix);
|
||||
newsig.append(chunk);
|
||||
}
|
||||
return newsig;
|
||||
}
|
||||
|
||||
void import_prim_cell(Cell *cell, const string &suffix)
|
||||
{
|
||||
Cell *c = module->addCell(cell->name.str() + suffix, cell->type);
|
||||
c->parameters = cell->parameters;
|
||||
c->attributes = cell->attributes;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
c->setPort(conn.first, import_sig(conn.second, suffix));
|
||||
}
|
||||
|
||||
void import_hier_cell(Cell *cell)
|
||||
{
|
||||
if (!cell->parameters.empty())
|
||||
log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell));
|
||||
|
||||
FmcombineWorker sub_worker(design, cell->type, opts);
|
||||
sub_worker.generate();
|
||||
|
||||
Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type);
|
||||
// c->parameters = cell->parameters;
|
||||
c->attributes = cell->attributes;
|
||||
|
||||
for (auto &conn : cell->connections()) {
|
||||
c->setPort(conn.first.str() + "_gold", import_sig(conn.second, "_gold"));
|
||||
c->setPort(conn.first.str() + "_gate", import_sig(conn.second, "_gate"));
|
||||
}
|
||||
}
|
||||
|
||||
void generate()
|
||||
{
|
||||
if (design->module(combined_type)) {
|
||||
// log("Combined module %s already exists.\n", log_id(combined_type));
|
||||
return;
|
||||
}
|
||||
|
||||
log("Generating combined module %s from module %s.\n", log_id(combined_type), log_id(orig_type));
|
||||
module = design->addModule(combined_type);
|
||||
|
||||
for (auto wire : original->wires()) {
|
||||
module->addWire(wire->name.str() + "_gold", wire);
|
||||
module->addWire(wire->name.str() + "_gate", wire);
|
||||
}
|
||||
module->fixup_ports();
|
||||
|
||||
for (auto cell : original->cells()) {
|
||||
if (design->module(cell->type) == nullptr) {
|
||||
import_prim_cell(cell, "_gold");
|
||||
import_prim_cell(cell, "_gate");
|
||||
} else {
|
||||
import_hier_cell(cell);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &conn : original->connections()) {
|
||||
module->connect(import_sig(conn.first, "_gold"), import_sig(conn.second, "_gold"));
|
||||
module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate"));
|
||||
}
|
||||
|
||||
if (opts.nop)
|
||||
return;
|
||||
|
||||
CellTypes ct;
|
||||
ct.setup_internals_eval();
|
||||
ct.setup_stdcells_eval();
|
||||
|
||||
SigMap sigmap(module);
|
||||
|
||||
dict<SigBit, SigBit> data_bit_to_eq_net;
|
||||
dict<Cell*, SigSpec> cell_to_eq_nets;
|
||||
dict<SigSpec, SigSpec> reduce_db;
|
||||
dict<SigSpec, SigSpec> invert_db;
|
||||
|
||||
for (auto cell : original->cells())
|
||||
{
|
||||
if (!ct.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (!cell->output(conn.first))
|
||||
continue;
|
||||
|
||||
SigSpec A = import_sig(conn.second, "_gold");
|
||||
SigSpec B = import_sig(conn.second, "_gate");
|
||||
SigBit EQ = module->Eq(NEW_ID, A, B);
|
||||
|
||||
for (auto bit : sigmap({A, B}))
|
||||
data_bit_to_eq_net[bit] = EQ;
|
||||
|
||||
cell_to_eq_nets[cell].append(EQ);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : original->cells())
|
||||
{
|
||||
if (!ct.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
bool skip_cell = !cell_to_eq_nets.count(cell);
|
||||
pool<SigBit> src_eq_bits;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (skip_cell)
|
||||
break;
|
||||
|
||||
if (cell->output(conn.first))
|
||||
continue;
|
||||
|
||||
SigSpec A = import_sig(conn.second, "_gold");
|
||||
SigSpec B = import_sig(conn.second, "_gate");
|
||||
|
||||
for (auto bit : sigmap({A, B})) {
|
||||
if (data_bit_to_eq_net.count(bit))
|
||||
src_eq_bits.insert(data_bit_to_eq_net.at(bit));
|
||||
else
|
||||
skip_cell = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip_cell) {
|
||||
SigSpec antecedent = SigSpec(src_eq_bits);
|
||||
antecedent.sort_and_unify();
|
||||
|
||||
if (GetSize(antecedent) > 1) {
|
||||
if (reduce_db.count(antecedent) == 0)
|
||||
reduce_db[antecedent] = module->ReduceAnd(NEW_ID, antecedent);
|
||||
antecedent = reduce_db.at(antecedent);
|
||||
}
|
||||
|
||||
SigSpec consequent = cell_to_eq_nets.at(cell);
|
||||
consequent.sort_and_unify();
|
||||
|
||||
if (GetSize(consequent) > 1) {
|
||||
if (reduce_db.count(consequent) == 0)
|
||||
reduce_db[consequent] = module->ReduceAnd(NEW_ID, consequent);
|
||||
consequent = reduce_db.at(consequent);
|
||||
}
|
||||
|
||||
if (opts.fwd)
|
||||
module->addAssume(NEW_ID, consequent, antecedent);
|
||||
|
||||
if (opts.bwd)
|
||||
{
|
||||
if (invert_db.count(antecedent) == 0)
|
||||
invert_db[antecedent] = module->Not(NEW_ID, antecedent);
|
||||
|
||||
if (invert_db.count(consequent) == 0)
|
||||
invert_db[consequent] = module->Not(NEW_ID, consequent);
|
||||
|
||||
module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FmcombinePass : public Pass {
|
||||
FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" fmcombine [options] module_name gold_cell gate_cell\n");
|
||||
// log(" fmcombine [options] @gold_cell @gate_cell\n");
|
||||
log("\n");
|
||||
log("This pass takes two cells, which are instances of the same module, and replaces\n");
|
||||
log("them with one instance of a special 'combined' module, that effectively\n");
|
||||
log("contains two copies of the original module, plus some formal properties.\n");
|
||||
log("\n");
|
||||
log("This is useful for formal test benches that check what differences in behavior\n");
|
||||
log("a slight difference in input causes in a module.\n");
|
||||
log("\n");
|
||||
log(" -fwd\n");
|
||||
log(" Insert forward hint assumptions into the combined module.\n");
|
||||
log("\n");
|
||||
log(" -bwd\n");
|
||||
log(" Insert backward hint assumptions into the combined module.\n");
|
||||
log(" (Backward hints are logically equivalend to fordward hits, but\n");
|
||||
log(" some solvers are faster with bwd hints, or even both -bwd and -fwd.)\n");
|
||||
log("\n");
|
||||
log(" -nop\n");
|
||||
log(" Don't insert hint assumptions into the combined module.\n");
|
||||
log(" (This should not provide any speedup over the original design, but\n");
|
||||
log(" strangely sometimes it does.)\n");
|
||||
log("\n");
|
||||
log("If none of -fwd, -bwd, and -nop is given, then -fwd is used as default.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
opts_t opts;
|
||||
Module *module = nullptr;
|
||||
Cell *gold_cell = nullptr;
|
||||
Cell *gate_cell = nullptr;
|
||||
|
||||
log_header(design, "Executing FMCOMBINE pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-o" && argidx+1 < args.size()) {
|
||||
// filename = args[++argidx];
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-fwd") {
|
||||
opts.fwd = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-bwd") {
|
||||
opts.bwd = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nop") {
|
||||
opts.nop = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (argidx+2 == args.size())
|
||||
{
|
||||
string gold_name = args[argidx++];
|
||||
string gate_name = args[argidx++];
|
||||
log_cmd_error("fmcombine @gold_cell @gate_cell call style is not implemented yet.");
|
||||
}
|
||||
else if (argidx+3 == args.size())
|
||||
{
|
||||
IdString module_name = RTLIL::escape_id(args[argidx++]);
|
||||
IdString gold_name = RTLIL::escape_id(args[argidx++]);
|
||||
IdString gate_name = RTLIL::escape_id(args[argidx++]);
|
||||
|
||||
module = design->module(module_name);
|
||||
if (module == nullptr)
|
||||
log_cmd_error("Module %s not found.\n", log_id(module_name));
|
||||
|
||||
gold_cell = module->cell(gold_name);
|
||||
if (gold_cell == nullptr)
|
||||
log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gold_name), log_id(module));
|
||||
|
||||
gate_cell = module->cell(gate_name);
|
||||
if (gate_cell == nullptr)
|
||||
log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_cmd_error("Invalid number of arguments.\n");
|
||||
}
|
||||
// extra_args(args, argidx, design);
|
||||
|
||||
if (opts.nop && (opts.fwd || opts.bwd))
|
||||
log_cmd_error("Option -nop can not be combined with -fwd and/or -bwd.\n");
|
||||
|
||||
if (!opts.nop && !opts.fwd && !opts.bwd)
|
||||
opts.fwd = true;
|
||||
|
||||
if (gold_cell->type != gate_cell->type)
|
||||
log_cmd_error("Types of gold and gate cells do not match.\n");
|
||||
if (!gold_cell->parameters.empty())
|
||||
log_cmd_error("Gold cell has unresolved instance parameters.\n");
|
||||
if (!gate_cell->parameters.empty())
|
||||
log_cmd_error("Gold cell has unresolved instance parameters.\n");
|
||||
|
||||
FmcombineWorker worker(design, gold_cell->type, opts);
|
||||
worker.generate();
|
||||
IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell)));
|
||||
|
||||
Cell *cell = module->addCell(combined_cell_name, worker.combined_type);
|
||||
cell->attributes = gold_cell->attributes;
|
||||
cell->add_strpool_attribute("\\src", gate_cell->get_strpool_attribute("\\src"));
|
||||
|
||||
log("Combining cells %s and %s in module %s into new cell %s.\n", log_id(gold_cell), log_id(gate_cell), log_id(module), log_id(cell));
|
||||
|
||||
for (auto &conn : gold_cell->connections())
|
||||
cell->setPort(conn.first.str() + "_gold", conn.second);
|
||||
module->remove(gold_cell);
|
||||
|
||||
for (auto &conn : gate_cell->connections())
|
||||
cell->setPort(conn.first.str() + "_gate", conn.second);
|
||||
module->remove(gate_cell);
|
||||
}
|
||||
} FmcombinePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue