3
0
Fork 0
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:
Benedikt Tutzer 2019-03-28 12:16:39 +01:00
commit 03d1606b42
428 changed files with 23388 additions and 2479 deletions

View file

@ -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

View file

@ -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;

View file

@ -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
View 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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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());

View file

@ -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;

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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++) {

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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 &parameter : 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));
}
}
}

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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");

View file

@ -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
View 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

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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 &param : 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;

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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_)

View file

@ -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;

View file

@ -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())

View file

@ -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);

View file

@ -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");

View file

@ -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())

View file

@ -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_)

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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");

View file

@ -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
View 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

View file

@ -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");

View file

@ -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);

View file

@ -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;

View file

@ -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");

View file

@ -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");

View file

@ -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;

View file

@ -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
View file

@ -0,0 +1 @@
/ice40_dsp_pm.h

View 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
View 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
View 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
View 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
View 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)

View file

@ -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;

View file

@ -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;

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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

View file

@ -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
View 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

View file

@ -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
View 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

View file

@ -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;

View file

@ -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
View 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