aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
authorEddie Hung <eddie@fpgeh.com>2019-09-27 15:14:31 -0700
committerEddie Hung <eddie@fpgeh.com>2019-09-27 15:14:31 -0700
commit8f5710c464b2b3b91f0c7f29a9420dcb798be4c5 (patch)
tree59bde082cba0f8f76e0e297ed57980592df7e73b /passes
parentf1a206ba03c5b6fba2672754d09cc649a60beeb8 (diff)
parentfd0e3a2c43d96ba31beede9865d5000230029994 (diff)
downloadyosys-8f5710c464b2b3b91f0c7f29a9420dcb798be4c5.tar.gz
yosys-8f5710c464b2b3b91f0c7f29a9420dcb798be4c5.tar.bz2
yosys-8f5710c464b2b3b91f0c7f29a9420dcb798be4c5.zip
Merge remote-tracking branch 'origin/master' into xaig_dff
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/Makefile.inc1
-rw-r--r--passes/cmds/add.cc18
-rw-r--r--passes/cmds/portlist.cc93
-rw-r--r--passes/cmds/select.cc4
-rw-r--r--passes/cmds/show.cc8
-rw-r--r--passes/equiv/equiv_make.cc4
-rw-r--r--passes/equiv/equiv_opt.cc12
-rw-r--r--passes/hierarchy/hierarchy.cc24
-rw-r--r--passes/opt/opt_expr.cc34
-rw-r--r--passes/opt/opt_share.cc7
-rw-r--r--passes/pmgen/Makefile.inc11
-rw-r--r--passes/pmgen/README.md50
-rw-r--r--passes/pmgen/ice40_dsp.cc5
-rw-r--r--passes/pmgen/peepopt.cc1
-rw-r--r--passes/pmgen/peepopt_dffmux.pmg113
-rw-r--r--passes/pmgen/pmgen.py123
-rw-r--r--passes/pmgen/test_pmgen.cc65
-rw-r--r--passes/pmgen/test_pmgen.pmg87
-rw-r--r--passes/pmgen/xilinx_srl.cc258
-rw-r--r--passes/pmgen/xilinx_srl.pmg326
-rw-r--r--passes/sat/async2sync.cc37
-rw-r--r--passes/sat/sat.cc2
-rw-r--r--passes/techmap/Makefile.inc2
-rw-r--r--passes/techmap/abc9.cc152
-rw-r--r--passes/techmap/alumacc.cc19
-rw-r--r--passes/techmap/attrmap.cc187
-rw-r--r--passes/techmap/clkbufmap.cc298
-rw-r--r--passes/techmap/dff2dffs.cc29
-rw-r--r--passes/techmap/extractinv.cc123
-rw-r--r--passes/techmap/iopadmap.cc58
-rw-r--r--passes/techmap/shregmap.cc181
-rw-r--r--passes/techmap/techmap.cc189
-rw-r--r--passes/tests/test_autotb.cc3
33 files changed, 2070 insertions, 454 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index c8067a8be..cf9663d1d 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -25,6 +25,7 @@ OBJS += passes/cmds/plugin.o
OBJS += passes/cmds/check.o
OBJS += passes/cmds/qwp.o
OBJS += passes/cmds/edgetypes.o
+OBJS += passes/cmds/portlist.o
OBJS += passes/cmds/chformal.o
OBJS += passes/cmds/chtype.o
OBJS += passes/cmds/blackbox.o
diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc
index af6f7043d..dd05ac81f 100644
--- a/passes/cmds/add.cc
+++ b/passes/cmds/add.cc
@@ -105,6 +105,11 @@ struct AddPass : public Pass {
log("Like 'add -input', but also connect the signal between instances of the\n");
log("selected modules.\n");
log("\n");
+ log("\n");
+ log(" add -mod <name[s]>\n");
+ log("\n");
+ log("Add module[s] with the specified name[s].\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -113,6 +118,7 @@ struct AddPass : public Pass {
bool arg_flag_input = false;
bool arg_flag_output = false;
bool arg_flag_global = false;
+ bool mod_mode = false;
int arg_width = 0;
size_t argidx;
@@ -133,8 +139,20 @@ struct AddPass : public Pass {
arg_width = atoi(args[++argidx].c_str());
continue;
}
+ if (arg == "-mod") {
+ mod_mode = true;
+ argidx++;
+ break;
+ }
break;
}
+
+ if (mod_mode) {
+ for (; argidx < args.size(); argidx++)
+ design->addModule(RTLIL::escape_id(args[argidx]));
+ return;
+ }
+
extra_args(args, argidx, design);
for (auto &mod : design->modules_)
diff --git a/passes/cmds/portlist.cc b/passes/cmds/portlist.cc
new file mode 100644
index 000000000..38c4a8597
--- /dev/null
+++ b/passes/cmds/portlist.cc
@@ -0,0 +1,93 @@
+/*
+ * 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 PortlistPass : public Pass {
+ PortlistPass() : Pass("portlist", "list (top-level) ports") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" portlist [options] [selection]\n");
+ log("\n");
+ log("This command lists all module ports found in the selected modules.\n");
+ log("\n");
+ log("If no selection is provided then it lists the ports on the top module.\n");
+ log("\n");
+ log(" -m\n");
+ log(" print verilog blackbox module definitions instead of port lists\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool m_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-m") {
+ m_mode = true;
+ continue;
+ }
+ break;
+ }
+
+ bool first_module = true;
+
+ auto handle_module = [&](RTLIL::Module *module) {
+ vector<string> ports;
+ if (first_module)
+ first_module = false;
+ else
+ log("\n");
+ for (auto port : module->ports) {
+ auto *w = module->wire(port);
+ ports.push_back(stringf("%s [%d:%d] %s", w->port_input ? w->port_output ? "inout" : "input" : "output",
+ w->upto ? w->start_offset : w->start_offset + w->width - 1,
+ w->upto ? w->start_offset + w->width - 1 : w->start_offset,
+ log_id(w)));
+ }
+ log("module %s%s\n", log_id(module), m_mode ? " (" : "");
+ for (int i = 0; i < GetSize(ports); i++)
+ log("%s%s\n", ports[i].c_str(), m_mode && i+1 < GetSize(ports) ? "," : "");
+ if (m_mode)
+ log(");\nendmodule\n");
+ };
+
+ if (argidx == args.size())
+ {
+ auto *top = design->top_module();
+ if (top == nullptr)
+ log_cmd_error("Can't find top module in current design!\n");
+ handle_module(top);
+ }
+ else
+ {
+ extra_args(args, argidx, design);
+ for (auto module : design->selected_modules())
+ handle_module(module);
+ }
+ }
+} PortlistPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index 59d10a1b8..0f1f05ccb 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -664,7 +664,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
} else
if (arg == "%D") {
if (work_stack.size() < 2)
- log_cmd_error("Must have at least two elements on the stack for operator %%d.\n");
+ log_cmd_error("Must have at least two elements on the stack for operator %%D.\n");
select_op_diff(design, work_stack[work_stack.size()-1], work_stack[work_stack.size()-2]);
work_stack[work_stack.size()-2] = work_stack[work_stack.size()-1];
work_stack.pop_back();
@@ -693,7 +693,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
} else
if (arg == "%C") {
if (work_stack.size() < 1)
- log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ log_cmd_error("Must have at least one element on the stack for operator %%C.\n");
select_op_module_to_cells(design, work_stack[work_stack.size()-1]);
} else
if (arg == "%c") {
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index 2e9fc72af..a3e969ef1 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -26,6 +26,10 @@
# include <dirent.h>
#endif
+#ifdef __APPLE__
+# include <unistd.h>
+#endif
+
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
#endif
@@ -866,7 +870,11 @@ struct ShowPass : public Pass {
log_cmd_error("Shell command failed!\n");
} else
if (format.empty()) {
+ #ifdef __APPLE__
+ std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
+ #else
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
+ #endif
log("Exec: %s\n", cmd.c_str());
if (run_command(cmd) != 0)
log_cmd_error("Shell command failed!\n");
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index dbd8682e6..4855ce29e 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -532,10 +532,10 @@ struct EquivMakePass : public Pass {
log_cmd_error("Equiv module %s already exists.\n", args[argidx+2].c_str());
if (worker.gold_mod->has_memories() || worker.gold_mod->has_processes())
- log_cmd_error("Gold module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+ log_cmd_error("Gold module contains memories or processes. Run 'memory' or 'proc' respectively.\n");
if (worker.gate_mod->has_memories() || worker.gate_mod->has_processes())
- log_cmd_error("Gate module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+ log_cmd_error("Gate module contains memories or processes. Run 'memory' or 'proc' respectively.\n");
worker.read_blacklists();
worker.read_encfiles();
diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
index 19d1c25ac..d4c7f7953 100644
--- a/passes/equiv/equiv_opt.cc
+++ b/passes/equiv/equiv_opt.cc
@@ -46,6 +46,9 @@ struct EquivOptPass:public ScriptPass
log(" -assert\n");
log(" produce an error if the circuits are not equivalent.\n");
log("\n");
+ log(" -multiclock\n");
+ log(" run clk2fflogic before equivalence checking.\n");
+ log("\n");
log(" -undef\n");
log(" enable modelling of undef states during equiv_induct.\n");
log("\n");
@@ -55,7 +58,7 @@ struct EquivOptPass:public ScriptPass
}
std::string command, techmap_opts;
- bool assert, undef;
+ bool assert, undef, multiclock;
void clear_flags() YS_OVERRIDE
{
@@ -63,6 +66,7 @@ struct EquivOptPass:public ScriptPass
techmap_opts = "";
assert = false;
undef = false;
+ multiclock = false;
}
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
@@ -92,6 +96,10 @@ struct EquivOptPass:public ScriptPass
undef = true;
continue;
}
+ if (args[argidx] == "-multiclock") {
+ multiclock = true;
+ continue;
+ }
break;
}
@@ -146,6 +154,8 @@ struct EquivOptPass:public ScriptPass
}
if (check_label("prove")) {
+ if (multiclock || help_mode)
+ run("clk2fflogic", "(only with -multiclock)");
run("equiv_make gold gate equiv");
if (help_mode)
run("equiv_induct [-undef] equiv");
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index fd95b94b2..d8a628448 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -808,6 +808,30 @@ struct HierarchyPass : public Pass {
if (mod_it.second->get_bool_attribute("\\top"))
top_mod = mod_it.second;
+ if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) {
+ IdString top_name = top_mod->name.substr(strlen("$abstract"));
+
+ dict<RTLIL::IdString, RTLIL::Const> top_parameters;
+ for (auto &para : parameters) {
+ SigSpec sig_value;
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
+ log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
+ top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
+ }
+
+ top_mod = design->module(top_mod->derive(design, top_parameters));
+
+ if (top_mod != nullptr && top_mod->name != top_name) {
+ Module *m = top_mod->clone();
+ m->name = top_name;
+ Module *old_mod = design->module(top_name);
+ if (old_mod)
+ design->remove(old_mod);
+ design->add(m);
+ top_mod = m;
+ }
+ }
+
if (top_mod == nullptr && auto_top_mode) {
log_header(design, "Finding top of design hierarchy..\n");
dict<Module*, int> db;
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 858b3560c..6cf66fb95 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -369,7 +369,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto cell : module->cells())
if (design->selected(module, cell) && cell->type[0] == '$') {
if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) &&
- cell->getPort(ID::A).size() == 1 && cell->getPort(ID::Y).size() == 1)
+ GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::Y)) == 1)
invert_map[assign_map(cell->getPort(ID::Y))] = assign_map(cell->getPort(ID::A));
if (cell->type.in(ID($mux), ID($_MUX_)) &&
cell->getPort(ID::A) == SigSpec(State::S1) && cell->getPort(ID::B) == SigSpec(State::S0))
@@ -740,12 +740,34 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
else
- replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::SigSpec(RTLIL::State::Sx, cell->getPort(ID::Y).size()));
+ replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::SigSpec(RTLIL::State::Sx, GetSize(cell->getPort(ID::Y))));
goto next_cell;
}
}
- if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && cell->getPort(ID::Y).size() == 1 &&
+ if (cell->type.in(ID($shiftx), ID($shift))) {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ int width;
+ bool trim_x = cell->type == ID($shiftx) || !keepdc;
+ bool trim_0 = cell->type == ID($shift);
+ for (width = GetSize(sig_a); width > 1; width--) {
+ if ((trim_x && sig_a[width-1] == State::Sx) ||
+ (trim_0 && sig_a[width-1] == State::S0))
+ continue;
+ break;
+ }
+
+ if (width < GetSize(sig_a)) {
+ cover_list("opt.opt_expr.trim", "$shiftx", "$shift", cell->type.str());
+ sig_a.remove(width, GetSize(sig_a)-width);
+ cell->setPort(ID::A, sig_a);
+ cell->setParam(ID(A_WIDTH), width);
+ did_something = true;
+ goto next_cell;
+ }
+ }
+
+ if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && GetSize(cell->getPort(ID::Y)) == 1 &&
invert_map.count(assign_map(cell->getPort(ID::A))) != 0) {
cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
replace_cell(assign_map, module, cell, "double_invert", ID::Y, invert_map.at(assign_map(cell->getPort(ID::A))));
@@ -931,6 +953,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (b.is_fully_const()) {
+ if (b.is_fully_undef()) {
+ RTLIL::SigSpec input = b;
+ ACTION_DO(ID::Y, Const(State::Sx, GetSize(cell->getPort(ID::Y))));
+ } else
if (b.as_bool() == (cell->type == ID($eq))) {
RTLIL::SigSpec input = b;
ACTION_DO(ID::Y, cell->getPort(ID::A));
@@ -1142,7 +1168,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (mux_undef && cell->type.in(ID($mux), ID($pmux))) {
RTLIL::SigSpec new_a, new_b, new_s;
- int width = cell->getPort(ID::A).size();
+ int width = GetSize(cell->getPort(ID::A));
if ((cell->getPort(ID::A).is_fully_undef() && cell->getPort(ID::B).is_fully_undef()) ||
cell->getPort(ID(S)).is_fully_undef()) {
cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str());
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index c53fb3113..2c456705c 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -108,12 +108,13 @@ bool cell_supported(RTLIL::Cell *cell)
return false;
}
-std::map<IdString, IdString> mergeable_type_map{
- {ID($sub), ID($add)},
-};
+std::map<IdString, IdString> mergeable_type_map;
bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
{
+ if (mergeable_type_map.empty()) {
+ mergeable_type_map.insert({ID($sub), ID($add)});
+ }
auto a_type = a->type;
if (mergeable_type_map.count(a_type))
a_type = mergeable_type_map.at(a_type);
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index 790811d4c..98691d0fe 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -4,7 +4,7 @@
# --------------------------------------
OBJS += passes/pmgen/test_pmgen.o
-passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h
+passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h
$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h))
# --------------------------------------
@@ -17,7 +17,7 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h))
OBJS += passes/pmgen/ice40_wrapcarry.o
passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h
-$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h))
+$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
# --------------------------------------
@@ -27,6 +27,13 @@ $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
+PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
+
+# --------------------------------------
+
+OBJS += passes/pmgen/xilinx_srl.o
+passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h))
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
index 5f6a8ab1b..2f5b8d0b2 100644
--- a/passes/pmgen/README.md
+++ b/passes/pmgen/README.md
@@ -178,6 +178,45 @@ evaluates to `false`.
The `semioptional` statement marks matches that must match if at least one
matching cell exists, but if no matching cell exists it is set to `nullptr`.
+Slices and choices
+------------------
+
+Cell matches can contain "slices" and "choices". Slices can be used to
+create matches for different sections of a cell. For example:
+
+ state <int> pmux_slice
+
+ match pmux
+ select pmux->type == $pmux
+ slice idx GetSize(port(pmux, \S))
+ index <SigBit> port(pmux, \S)[idx] === port(eq, \Y)
+ set pmux_slice idx
+ endmatch
+
+The first argument to `slice` is the local variable name used to identify the
+slice. The second argument is the number of slices that should be created for
+this cell. The `set` statement can be used to copy that index into a state
+variable so that later matches and/or code blocks can refer to it.
+
+A similar mechanism is "choices", where a list of options is given as
+second argument, and the matcher will iterate over those options:
+
+ state <SigSpec> foo bar
+ state <IdString> eq_ab eq_ba
+
+ match eq
+ select eq->type == $eq
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(eq, AB) === foo
+ index <SigSpec> port(eq, BA) === bar
+ set eq_ab AB
+ set eq_ba BA
+ generate
+
+Notice how `define` can be used to define additional local variables similar
+to the loop variables defined by `slice` and `choice`.
+
Additional code
---------------
@@ -313,7 +352,7 @@ state variables used to pass arguments.
subpattern tail
...
-Subpatterns cann be called recursively.
+Subpatterns can be called recursively.
If a `subpattern` statement is preceded by a `fallthrough` statement, this is
equivalent to calling the subpattern at the end of the preceding block.
@@ -326,7 +365,7 @@ test-case generation. For example:
match mul
...
- generate 10
+ generate 10 0
SigSpec Y = port(ff, \D);
SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
SigSpec B = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
@@ -335,8 +374,11 @@ test-case generation. For example:
The expression `rng(n)` returns a non-negative integer less than `n`.
-The argument to `generate` is the chance of this generate block being executed
-when the match block did not match anything, in percent.
+The first argument to `generate` is the chance of this generate block being
+executed when the match block did not match anything, in percent.
+
+The second argument to `generate` is the chance of this generate block being
+executed when the match block did match something, in percent.
The special statement `finish` can be used within generate blocks to terminate
the current pattern matcher run.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index 39d033a04..16bfe537f 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -64,11 +64,6 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
bool mul_signed = 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");
diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc
index e7f95cf85..72b02127a 100644
--- a/passes/pmgen/peepopt.cc
+++ b/passes/pmgen/peepopt.cc
@@ -60,6 +60,7 @@ struct PeepoptPass : public Pass {
peepopt_pm pm(module, module->selected_cells());
pm.run_shiftmul();
pm.run_muldiv();
+ pm.run_dffmux();
}
}
}
diff --git a/passes/pmgen/peepopt_dffmux.pmg b/passes/pmgen/peepopt_dffmux.pmg
new file mode 100644
index 000000000..c88a52226
--- /dev/null
+++ b/passes/pmgen/peepopt_dffmux.pmg
@@ -0,0 +1,113 @@
+pattern dffmux
+
+state <IdString> cemuxAB rstmuxBA
+state <SigSpec> sigD
+
+match dff
+ select dff->type == $dff
+ select GetSize(port(dff, \D)) > 1
+endmatch
+
+match rstmux
+ select rstmux->type == $mux
+ select GetSize(port(rstmux, \Y)) > 1
+ index <SigSpec> port(rstmux, \Y) === port(dff, \D)
+ choice <IdString> BA {\B, \A}
+ select port(rstmux, BA).is_fully_const()
+ set rstmuxBA BA
+ optional
+endmatch
+
+code sigD
+ if (rstmux)
+ sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
+ else
+ sigD = port(dff, \D);
+endcode
+
+match cemux
+ select cemux->type == $mux
+ select GetSize(port(cemux, \Y)) > 1
+ index <SigSpec> port(cemux, \Y) === sigD
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(cemux, AB) === port(dff, \Q)
+ set cemuxAB AB
+endmatch
+
+code
+ SigSpec D = port(cemux, cemuxAB == \A ? \B : \A);
+ SigSpec Q = port(dff, \Q);
+ Const rst;
+ if (rstmux)
+ rst = port(rstmux, rstmuxBA).as_const();
+ int width = GetSize(D);
+
+ SigSpec &ceA = cemux->connections_.at(\A);
+ SigSpec &ceB = cemux->connections_.at(\B);
+ SigSpec &ceY = cemux->connections_.at(\Y);
+ SigSpec &dffD = dff->connections_.at(\D);
+ SigSpec &dffQ = dff->connections_.at(\Q);
+
+ if (D[width-1] == D[width-2]) {
+ did_something = true;
+
+ SigBit sign = D[width-1];
+ bool is_signed = sign.wire;
+ int i;
+ for (i = width-1; i >= 2; i--) {
+ if (!is_signed) {
+ module->connect(Q[i], sign);
+ if (D[i-1] != sign || (rst.size() && rst[i-1] != rst[width-1]))
+ break;
+ }
+ else {
+ module->connect(Q[i], Q[i-1]);
+ if (D[i-2] != sign || (rst.size() && rst[i-1] != rst[width-1]))
+ break;
+ }
+ }
+
+ ceA.remove(i, width-i);
+ ceB.remove(i, width-i);
+ ceY.remove(i, width-i);
+ cemux->fixup_parameters();
+ dffD.remove(i, width-i);
+ dffQ.remove(i, width-i);
+ dff->fixup_parameters();
+
+ log("dffcemux pattern in %s: dff=%s, cemux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux), width-i);
+ accept;
+ }
+ else {
+ int count = 0;
+ for (int i = width-1; i >= 0; i--) {
+ if (D[i].wire)
+ continue;
+ Wire *w = Q[i].wire;
+ auto it = w->attributes.find(\init);
+ State init;
+ if (it != w->attributes.end())
+ init = it->second[Q[i].offset];
+ else
+ init = State::Sx;
+
+ if (init == State::Sx || init == D[i].data) {
+ count++;
+ module->connect(Q[i], D[i]);
+ ceA.remove(i);
+ ceB.remove(i);
+ ceY.remove(i);
+ dffD.remove(i);
+ dffQ.remove(i);
+ }
+ }
+ if (count > 0) {
+ did_something = true;
+ cemux->fixup_parameters();
+ dff->fixup_parameters();
+ log("dffcemux pattern in %s: dff=%s, cemux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), count);
+ }
+
+ accept;
+ }
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
index 18c3bf5a5..573722d68 100644
--- a/passes/pmgen/pmgen.py
+++ b/passes/pmgen/pmgen.py
@@ -207,9 +207,10 @@ def process_pmgfile(f, filename):
state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list()
- block["select"] = list()
+ block["setup"] = list()
block["index"] = list()
block["filter"] = list()
+ block["sets"] = list()
block["optional"] = False
block["semioptional"] = False
@@ -228,7 +229,22 @@ def process_pmgfile(f, filename):
if a[0] == "select":
b = l.lstrip()[6:]
- block["select"].append(rewrite_cpp(b.strip()))
+ block["setup"].append(("select", rewrite_cpp(b.strip())))
+ continue
+
+ if a[0] == "slice":
+ m = re.match(r"^\s*slice\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("slice", m.group(1), rewrite_cpp(m.group(2))))
+ continue
+
+ if a[0] == "choice":
+ m = re.match(r"^\s*choice\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("choice", m.group(1), m.group(2), rewrite_cpp(m.group(3))))
+ continue
+
+ if a[0] == "define":
+ m = re.match(r"^\s*define\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("define", m.group(1), m.group(2), rewrite_cpp(m.group(3))))
continue
if a[0] == "index":
@@ -242,6 +258,11 @@ def process_pmgfile(f, filename):
block["filter"].append(rewrite_cpp(b.strip()))
continue
+ if a[0] == "set":
+ m = re.match(r"^\s*set\s+(\S+)\s+(.*?)\s*$", l)
+ block["sets"].append((m.group(1), rewrite_cpp(m.group(2))))
+ continue
+
if a[0] == "optional":
block["optional"] = True
continue
@@ -252,14 +273,16 @@ def process_pmgfile(f, filename):
if a[0] == "generate":
block["genargs"] = list([int(s) for s in a[1:]])
+ if len(block["genargs"]) == 0: block["genargs"].append(100)
+ if len(block["genargs"]) == 1: block["genargs"].append(0)
+ assert len(block["genargs"]) == 2
block["gencode"] = list()
- assert len(block["genargs"]) < 2
while True:
linenr += 1
l = f.readline()
assert l != ""
a = l.split()
- if a[0] == "endmatch": break
+ if len(a) == 1 and a[0] == "endmatch": break
block["gencode"].append(rewrite_cpp(l.rstrip()))
break
@@ -357,8 +380,17 @@ with open(outfile, "w") as f:
index_types = list()
for entry in block["index"]:
index_types.append(entry[0])
+ value_types = ["Cell*"]
+ for entry in block["setup"]:
+ if entry[0] == "slice":
+ value_types.append("int")
+ if entry[0] == "choice":
+ value_types.append(entry[1])
+ if entry[0] == "define":
+ value_types.append(entry[1])
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(" typedef std::tuple<{}> index_{}_value_type;".format(", ".join(value_types), index), file=f)
+ print(" dict<index_{}_key_type, vector<index_{}_value_type>> index_{};".format(index, 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)
@@ -390,8 +422,6 @@ with open(outfile, "w") as 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)
@@ -446,10 +476,11 @@ with open(outfile, "w") as f:
else:
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
current_pattern = None
- print(" for (auto cell : module->cells()) {", file=f)
+ print(" for (auto port : module->ports)", file=f)
+ print(" add_siguser(module->wire(port), nullptr);", 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)):
@@ -457,12 +488,34 @@ with open(outfile, "w") as f:
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_{}_value_type value;".format(index), file=f)
+ print(" std::get<0>(value) = cell;", file=f)
+ loopcnt = 0
+ valueidx = 1
+ for item in block["setup"]:
+ if item[0] == "select":
+ print(" if (!({})) continue;".format(item[1]), file=f)
+ if item[0] == "slice":
+ print(" int &{} = std::get<{}>(value);".format(item[1], valueidx), file=f)
+ print(" for ({} = 0; {} < {}; {}++) {{".format(item[1], item[1], item[2], item[1]), file=f)
+ valueidx += 1
+ loopcnt += 1
+ if item[0] == "choice":
+ print(" vector<{}> _pmg_choices_{} = {};".format(item[1], item[2], item[3]), file=f)
+ print(" for (const {} &{} : _pmg_choices_{}) {{".format(item[1], item[2], item[2]), file=f)
+ print(" std::get<{}>(value) = {};".format(valueidx, item[2]), file=f)
+ valueidx += 1
+ loopcnt += 1
+ if item[0] == "define":
+ print(" {} &{} = std::get<{}>(value);".format(item[1], item[2], valueidx), file=f)
+ print(" {} = {};".format(item[2], item[3]), file=f)
+ valueidx += 1
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(" index_{}[key].push_back(value);".format(index), file=f)
+ for i in range(loopcnt):
+ print(" }", file=f)
print(" } while (0);", file=f)
print(" }", file=f)
@@ -535,6 +588,8 @@ with open(outfile, "w") as f:
const_st.add(s)
elif blocks[i]["type"] == "match":
const_st.add(blocks[i]["cell"])
+ for item in blocks[i]["sets"]:
+ const_st.add(item[0])
else:
assert False
@@ -548,6 +603,10 @@ with open(outfile, "w") as f:
s = block["cell"]
assert s not in const_st
nonconst_st.add(s)
+ for item in block["sets"]:
+ if item[0] in const_st:
+ const_st.remove(item[0])
+ nonconst_st.add(item[0])
else:
assert False
@@ -570,7 +629,7 @@ with open(outfile, "w") as f:
print("", file=f)
for s in sorted(restore_st):
t = state_types[current_pattern][s]
- print(" {} backup_{} = {};".format(t, s, s), file=f)
+ print(" {} _pmg_backup_{} = {};".format(t, s, s), file=f)
if block["type"] == "code":
print("", file=f)
@@ -610,7 +669,7 @@ with open(outfile, "w") as f:
print("", file=f)
for s in sorted(restore_st):
t = state_types[current_pattern][s]
- print(" {} = backup_{};".format(s, s), file=f)
+ print(" {} = _pmg_backup_{};".format(s, s), file=f)
for s in sorted(nonconst_st):
if s not in restore_st:
t = state_types[current_pattern][s]
@@ -622,7 +681,7 @@ with open(outfile, "w") as f:
elif block["type"] == "match":
assert len(restore_st) == 0
- print(" Cell* backup_{} = {};".format(block["cell"], block["cell"]), file=f)
+ print(" Cell* _pmg_backup_{} = {};".format(block["cell"], block["cell"]), file=f)
if len(block["if"]):
for expr in block["if"]:
@@ -630,7 +689,7 @@ with open(outfile, "w") as f:
print(" if (!({})) {{".format(expr), file=f)
print(" {} = nullptr;".format(block["cell"]), file=f)
print(" block_{}(recursion+1);".format(index+1), file=f)
- print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
print(" return;", file=f)
print(" }", file=f)
@@ -645,21 +704,37 @@ with open(outfile, "w") as f:
print("", file=f)
print(" if (cells_ptr != index_{}.end()) {{".format(index), file=f)
- print(" const vector<Cell*> &cells = cells_ptr->second;".format(index), file=f)
- print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
- print(" {} = cells[idx];".format(block["cell"]), file=f)
+ print(" const vector<index_{}_value_type> &cells = cells_ptr->second;".format(index), file=f)
+ print(" for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {", file=f)
+ print(" {} = std::get<0>(cells[_pmg_idx]);".format(block["cell"]), file=f)
+ valueidx = 1
+ for item in block["setup"]:
+ if item[0] == "slice":
+ print(" const int &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], valueidx), file=f)
+ valueidx += 1
+ if item[0] == "choice":
+ print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f)
+ valueidx += 1
+ if item[0] == "define":
+ print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f)
+ valueidx += 1
print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
for expr in block["filter"]:
print(" if (!({})) continue;".format(expr), file=f)
if block["semioptional"] or block["genargs"] is not None:
print(" found_any_match = true;", file=f)
- print(" auto rollback_ptr = rollback_cache.insert(make_pair(cells[idx], recursion));", file=f)
+ for item in block["sets"]:
+ print(" auto _pmg_backup_{} = {};".format(item[0], item[0]), file=f)
+ print(" {} = {};".format(item[0], item[1]), file=f)
+ print(" auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));", file=f)
print(" block_{}(recursion+1);".format(index+1), file=f)
+ for item in block["sets"]:
+ print(" {} = _pmg_backup_{};".format(item[0], item[0]), file=f)
print(" if (rollback_ptr.second)", file=f)
print(" rollback_cache.erase(rollback_ptr.first);", file=f)
print(" if (rollback) {", file=f)
print(" if (rollback != recursion) {{".format(index+1), file=f)
- print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
print(" return;", file=f)
print(" }", file=f)
print(" rollback = 0;", file=f)
@@ -676,13 +751,11 @@ with open(outfile, "w") as f:
if block["semioptional"]:
print(" if (!found_any_match) block_{}(recursion+1);".format(index+1), file=f)
- print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
if block["genargs"] is not None:
print("#define finish do { rollback = -1; return; } while(0)", file=f)
- print(" if (generate_mode && !found_any_match) {", file=f)
- if len(block["genargs"]) == 1:
- print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f)
+ print(" if (generate_mode && rng(100) < (found_any_match ? {} : {})) {{".format(block["genargs"][1], block["genargs"][0]), file=f)
for line in block["gencode"]:
print(" " + line, file=f)
print(" }", file=f)
diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc
index 9f42a95d0..4f3eec935 100644
--- a/passes/pmgen/test_pmgen.cc
+++ b/passes/pmgen/test_pmgen.cc
@@ -28,6 +28,7 @@ bool did_something;
#include "passes/pmgen/test_pmgen_pm.h"
#include "passes/pmgen/ice40_dsp_pm.h"
+#include "passes/pmgen/xilinx_srl_pm.h"
#include "passes/pmgen/peepopt_pm.h"
void reduce_chain(test_pmgen_pm &pm)
@@ -99,6 +100,24 @@ void reduce_tree(test_pmgen_pm &pm)
log(" -> %s (%s)\n", log_id(c), log_id(c->type));
}
+void opt_eqpmux(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_eqpmux;
+
+ SigSpec Y = st.pmux->getPort(ID::Y);
+ int width = GetSize(Y);
+
+ SigSpec EQ = st.pmux->getPort(ID::B).extract(st.pmux_slice_eq*width, width);
+ SigSpec NE = st.pmux->getPort(ID::B).extract(st.pmux_slice_ne*width, width);
+
+ log("Found eqpmux circuit driving %s (eq=%s, ne=%s, pmux=%s).\n",
+ log_signal(Y), log_id(st.eq), log_id(st.ne), log_id(st.pmux));
+
+ pm.autoremove(st.pmux);
+ Cell *c = pm.module->addMux(NEW_ID, NE, EQ, st.eq->getPort(ID::Y), Y);
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
#define GENERATE_PATTERN(pmclass, pattern) \
generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)
@@ -149,19 +168,20 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const
log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);
int modcnt = 0;
+ int maxmodcnt = 100;
int maxsubcnt = 4;
int timeout = 0;
vector<Module*> mods;
- while (modcnt < 100)
+ while (modcnt < maxmodcnt)
{
int submodcnt = 0, itercnt = 0, cellcnt = 0;
Module *mod = design->addModule(NEW_ID);
- while (modcnt < 100 && submodcnt < maxsubcnt && itercnt++ < 1000)
+ while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
{
if (timeout++ > 10000)
- log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n");
+ log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
pm matcher(mod, mod->cells());
@@ -197,7 +217,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const
run(matcher, [](){});
}
- if (submodcnt)
+ if (submodcnt && maxsubcnt < (1 << 16))
maxsubcnt *= 2;
design->remove(mod);
@@ -233,6 +253,12 @@ struct TestPmgenPass : public Pass {
log("\n");
log("\n");
+ log(" test_pmgen -eqpmux [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Optimize EQ/NE/PMUX circuits.\n");
+ log("\n");
+
+ log("\n");
log(" test_pmgen -generate [options] <pattern_name>\n");
log("\n");
log("Create modules that match the specified pattern.\n");
@@ -277,6 +303,25 @@ struct TestPmgenPass : public Pass {
test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree);
}
+ void execute_eqpmux(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-eqpmux).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ test_pmgen_pm(module, module->selected_cells()).run_eqpmux(opt_eqpmux);
+ }
+
void execute_generate(std::vector<std::string> args, RTLIL::Design *design)
{
log_header(design, "Executing TEST_PMGEN pass (-generate).\n");
@@ -299,16 +344,24 @@ struct TestPmgenPass : public Pass {
if (pattern == "reduce")
return GENERATE_PATTERN(test_pmgen_pm, reduce);
+ if (pattern == "eqpmux")
+ return GENERATE_PATTERN(test_pmgen_pm, eqpmux);
+
if (pattern == "ice40_dsp")
return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
+ if (pattern == "xilinx_srl.fixed")
+ return GENERATE_PATTERN(xilinx_srl_pm, fixed);
+ if (pattern == "xilinx_srl.variable")
+ return GENERATE_PATTERN(xilinx_srl_pm, variable);
+
if (pattern == "peepopt-muldiv")
return GENERATE_PATTERN(peepopt_pm, muldiv);
if (pattern == "peepopt-shiftmul")
return GENERATE_PATTERN(peepopt_pm, shiftmul);
- log_cmd_error("Unkown pattern: %s\n", pattern.c_str());
+ log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -319,6 +372,8 @@ struct TestPmgenPass : public Pass {
return execute_reduce_chain(args, design);
if (args[1] == "-reduce_tree")
return execute_reduce_tree(args, design);
+ if (args[1] == "-eqpmux")
+ return execute_eqpmux(args, design);
if (args[1] == "-generate")
return execute_generate(args, design);
}
diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg
index 211477a62..287ed97d8 100644
--- a/passes/pmgen/test_pmgen.pmg
+++ b/passes/pmgen/test_pmgen.pmg
@@ -60,8 +60,8 @@ code portname
endcode
match next
- select nusers(port(next, \Y)) == 2
select next->type.in($_AND_, $_OR_, $_XOR_)
+ select nusers(port(next, \Y)) == 2
index <IdString> next->type === first->type
index <SigSpec> port(next, \Y) === port(first, portname)
endmatch
@@ -77,8 +77,8 @@ arg first
match next
semioptional
- select nusers(port(next, \Y)) == 2
select next->type.in($_AND_, $_OR_, $_XOR_)
+ select nusers(port(next, \Y)) == 2
index <IdString> next->type === chain.back().first->type
index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second)
generate 10
@@ -104,3 +104,86 @@ finally
if (next)
chain.pop_back();
endcode
+
+// ==================================================================
+
+pattern eqpmux
+
+state <bool> eq_ne_signed
+state <SigSpec> eq_inA eq_inB
+state <int> pmux_slice_eq pmux_slice_ne
+
+match eq
+ select eq->type == $eq
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA AB == \A ? \B : \A
+ set eq_inA port(eq, \A)
+ set eq_inB port(eq, \B)
+ set eq_ne_signed param(eq, \A_SIGNED).as_bool()
+generate 100 10
+ SigSpec A = module->addWire(NEW_ID, rng(7)+1);
+ SigSpec B = module->addWire(NEW_ID, rng(7)+1);
+ SigSpec Y = module->addWire(NEW_ID);
+ module->addEq(NEW_ID, A, B, Y, rng(2));
+endmatch
+
+match pmux
+ select pmux->type == $pmux
+ slice idx GetSize(port(pmux, \S))
+ index <SigBit> port(pmux, \S)[idx] === port(eq, \Y)
+ set pmux_slice_eq idx
+generate 100 10
+ int width = rng(7) + 1;
+ int numsel = rng(4) + 1;
+ int idx = rng(numsel);
+
+ SigSpec A = module->addWire(NEW_ID, width);
+ SigSpec Y = module->addWire(NEW_ID, width);
+
+ SigSpec B, S;
+ for (int i = 0; i < numsel; i++) {
+ B.append(module->addWire(NEW_ID, width));
+ S.append(i == idx ? port(eq, \Y) : module->addWire(NEW_ID));
+ }
+
+ module->addPmux(NEW_ID, A, B, S, Y);
+endmatch
+
+match ne
+ select ne->type == $ne
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(ne, AB) === eq_inA
+ index <SigSpec> port(ne, BA) === eq_inB
+ index <int> param(ne, \A_SIGNED).as_bool() === eq_ne_signed
+generate 100 10
+ SigSpec A = eq_inA, B = eq_inB, Y;
+ if (rng(2)) {
+ std::swap(A, B);
+ }
+ if (rng(2)) {
+ for (auto bit : port(pmux, \S)) {
+ if (nusers(bit) < 2)
+ Y.append(bit);
+ }
+ if (GetSize(Y))
+ Y = Y[rng(GetSize(Y))];
+ else
+ Y = module->addWire(NEW_ID);
+ } else {
+ Y = module->addWire(NEW_ID);
+ }
+ module->addNe(NEW_ID, A, B, Y, rng(2));
+endmatch
+
+match pmux2
+ select pmux2->type == $pmux
+ slice idx GetSize(port(pmux2, \S))
+ index <Cell*> pmux2 === pmux
+ index <SigBit> port(pmux2, \S)[idx] === port(ne, \Y)
+ set pmux_slice_ne idx
+endmatch
+
+code
+ accept;
+endcode
diff --git a/passes/pmgen/xilinx_srl.cc b/passes/pmgen/xilinx_srl.cc
new file mode 100644
index 000000000..3d264e8d4
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.cc
@@ -0,0 +1,258 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * (C) 2019 Eddie Hung <eddie@fpgeh.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+#include "passes/pmgen/xilinx_srl_pm.h"
+
+void run_fixed(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_fixed;
+ auto &ud = pm.ud_fixed;
+ log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (auto cell : ud.longest_chain) {
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) {
+ SigBit Q = cell->getPort(ID(Q));
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else if (cell->type.in(ID(FDRE), ID(FDRE_1))) {
+ if (cell->parameters.at(ID(INIT), State::S0).as_bool())
+ initval.append(State::S1);
+ else
+ initval.append(State::S0);
+ }
+ else
+ log_abort();
+ pm.autoremove(cell);
+ }
+
+ auto first_cell = ud.longest_chain.back();
+ auto last_cell = ud.longest_chain.front();
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) {
+ c->setParam(ID(DEPTH), GetSize(ud.longest_chain));
+ c->setParam(ID(INIT), initval.as_const());
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setParam(ID(CLKPOL), 1);
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1)))
+ c->setParam(ID(CLKPOL), 0);
+ else if (first_cell->type.in(ID(FDRE))) {
+ if (!first_cell->parameters.at(ID(IS_C_INVERTED), State::S0).as_bool())
+ c->setParam(ID(CLKPOL), 1);
+ else
+ c->setParam(ID(CLKPOL), 0);
+ }
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ c->setParam(ID(ENPOL), 1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ c->setParam(ID(ENPOL), 0);
+ else
+ c->setParam(ID(ENPOL), 2);
+
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ c->setPort(ID(D), first_cell->getPort(ID(D)));
+ c->setPort(ID(Q), last_cell->getPort(ID(Q)));
+ c->setPort(ID(L), GetSize(ud.longest_chain)-1);
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID(FDRE), ID(FDRE_1)))
+ c->setPort(ID(E), first_cell->getPort(ID(CE)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void run_variable(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_variable;
+ auto &ud = pm.ud_variable;
+
+ log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (const auto &i : ud.chain) {
+ auto cell = i.first;
+ auto slice = i.second;
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ SigBit Q = cell->getPort(ID(Q))[slice];
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else
+ log_abort();
+ }
+ pm.autoremove(st.shiftx);
+
+ auto first_cell = ud.chain.back().first;
+ auto first_slice = ud.chain.back().second;
+
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ c->setParam(ID(DEPTH), GetSize(ud.chain));
+ c->setParam(ID(INIT), initval.as_const());
+ Const clkpol, enpol;
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ clkpol = 1;
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_)))
+ clkpol = 0;
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ clkpol = first_cell->getParam(ID(CLK_POLARITY));
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ enpol = 1;
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ enpol = 0;
+ else if (first_cell->type.in(ID($dffe)))
+ enpol = first_cell->getParam(ID(EN_POLARITY));
+ else
+ enpol = 2;
+ c->setParam(ID(CLKPOL), clkpol);
+ c->setParam(ID(ENPOL), enpol);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ c->setPort(ID(C), first_cell->getPort(ID(CLK)));
+ else
+ log_abort();
+ c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]);
+ c->setPort(ID(Q), st.shiftx->getPort(ID(Y)));
+ c->setPort(ID(L), st.shiftx->getPort(ID(B)));
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID($dffe)))
+ c->setPort(ID(E), first_cell->getPort(ID(EN)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+struct XilinxSrlPass : public Pass {
+ XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" xilinx_srl [options] [selection]\n");
+ log("\n");
+ log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n");
+ log("and word-level: $dff, $dffe) as well as Xilinx flops (FDRE, FDRE_1) into a\n");
+ log("$__XILINX_SHREG cell. Chains must be of the same cell type, clock, clock polarity,\n");
+ log("enable, and enable polarity (where relevant).\n");
+ log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred.");
+ log("\n");
+ log(" -minlen N\n");
+ log(" min length of shift register (default = 3)\n");
+ log("\n");
+ log(" -fixed\n");
+ log(" infer fixed-length shift registers.\n");
+ log("\n");
+ log(" -variable\n");
+ log(" infer variable-length shift registers (i.e. fixed-length shifts where\n");
+ log(" each element also fans-out to a $shiftx cell).\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing XILINX_SRL pass (Xilinx shift register extraction).\n");
+
+ bool fixed = false;
+ bool variable = false;
+ int minlen = 3;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
+ minlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-fixed") {
+ fixed = true;
+ continue;
+ }
+ if (args[argidx] == "-variable") {
+ variable = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!fixed && !variable)
+ log_cmd_error("'-fixed' and/or '-variable' must be specified.\n");
+
+ for (auto module : design->selected_modules()) {
+ auto pm = xilinx_srl_pm(module, module->selected_cells());
+ pm.ud_fixed.minlen = minlen;
+ pm.ud_variable.minlen = minlen;
+
+ if (fixed)
+ pm.run_fixed(run_fixed);
+ if (variable)
+ pm.run_variable(run_variable);
+ }
+ }
+} XilinxSrlPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_srl.pmg b/passes/pmgen/xilinx_srl.pmg
new file mode 100644
index 000000000..b18119b87
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.pmg
@@ -0,0 +1,326 @@
+pattern fixed
+
+state <IdString> clk_port en_port
+udata <vector<Cell*>> chain longest_chain
+udata <pool<Cell*>> non_first_cells
+udata <int> minlen
+
+code
+ non_first_cells.clear();
+ subpattern(setup);
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
+ filter !non_first_cells.count(first)
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ SigSpec D = module->addWire(NEW_ID);
+ SigSpec Q = module->addWire(NEW_ID);
+ auto r = rng(8);
+ Cell* cell;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addCell(NEW_ID, \FDRE);
+ cell->setPort(\C, C);
+ cell->setPort(\D, D);
+ cell->setPort(\Q, Q);
+ cell->setPort(\CE, module->addWire(NEW_ID));
+ if (r & 1)
+ cell->setPort(\R, module->addWire(NEW_ID));
+ else {
+ if (rng(2) == 0)
+ cell->setPort(\R, State::S0);
+ }
+ break;
+ case 2:
+ case 3:
+ cell = module->addDffGate(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2);
+ break;
+ default: log_abort();
+ }
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+
+ longest_chain.clear();
+ chain.push_back(first);
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) >= minlen)
+ accept;
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern setup
+arg clk_port
+arg en_port
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+endcode
+
+match next
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === first->type
+ index <SigBit> port(next, \Q) === port(first, \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
+endmatch
+
+code
+ non_first_cells.insert(next);
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === chain.back()->type
+ index <SigBit> port(next, \Q) === port(chain.back(), \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
+generate
+ Cell *cell = module->addCell(NEW_ID, chain.back()->type);
+ cell->setPort(\C, chain.back()->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, chain.back()->getPort(\D));
+ if (cell->type == \FDRE) {
+ if (rng(2) == 0)
+ cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
+ cell->setPort(\CE, chain.back()->getPort(\CE));
+ }
+ else if (cell->type.begins_with("$_DFFE_"))
+ cell->setPort(\E, chain.back()->getPort(\E));
+endmatch
+
+code
+ if (next) {
+ chain.push_back(next);
+ subpattern(tail);
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// -----------
+
+pattern variable
+
+state <IdString> clk_port en_port
+state <int> shiftx_width
+state <int> slice
+udata <int> minlen
+udata <vector<pair<Cell*,int>>> chain
+udata <pool<SigBit>> chain_bits
+
+code
+ chain_bits.clear();
+endcode
+
+match shiftx
+ select shiftx->type.in($shiftx)
+ select !shiftx->has_keep_attr()
+ select param(shiftx, \Y_WIDTH).as_int() == 1
+ filter param(shiftx, \A_WIDTH).as_int() >= minlen
+generate
+ minlen = 3;
+ module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID));
+endmatch
+
+code shiftx_width
+ shiftx_width = param(shiftx, \A_WIDTH).as_int();
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !first->has_keep_attr()
+ select port(first, \Q)[0].wire && !port(first, \Q)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(first, \Q))
+ select nusers(port(first, \Q)[idx]) <= 2
+ index <SigBit> port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1]
+ set slice idx
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ auto WIDTH = rng(3)+1;
+ SigSpec D = module->addWire(NEW_ID, WIDTH);
+ SigSpec Q = module->addWire(NEW_ID, WIDTH);
+ auto r = rng(8);
+ Cell *cell = nullptr;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addDff(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ //cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4);
+ //break;
+ case 6:
+ case 7:
+ WIDTH = 1;
+ cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1);
+ break;
+ default: log_abort();
+ }
+ shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)];
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ clk_port = \C;
+ else if (first->type.in($dff, $dffe))
+ clk_port = \CLK;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_, $dff))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in($dffe))
+ en_port = \EN;
+ else log_abort();
+
+ chain_bits.insert(port(first, \Q)[slice]);
+ chain.emplace_back(first, slice);
+ subpattern(tail);
+finally
+ if (GetSize(chain) == shiftx_width)
+ accept;
+ chain.clear();
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg shiftx
+arg shiftx_width
+arg slice
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(next, \Q))
+ select nusers(port(next, \Q)[idx]) <= 3
+ index <IdString> next->type === chain.back().first->type
+ index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second]
+ index <SigBit> port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)]
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool()
+ filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool()
+ filter !chain_bits.count(port(next, \D)[idx])
+ set slice idx
+generate
+ if (GetSize(chain) < shiftx_width) {
+ auto back = chain.back().first;
+ auto slice = chain.back().second;
+ if (back->type.in($dff, $dffe)) {
+ auto WIDTH = GetSize(port(back, \D));
+ if (rng(2) == 0 && slice < WIDTH-1) {
+ auto new_slice = slice + rng(WIDTH-1-slice);
+ back->connections_.at(\D)[slice] = port(back, \Q)[new_slice];
+ }
+ else {
+ auto D = module->addWire(NEW_ID, WIDTH);
+ if (back->type == $dff)
+ module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool());
+ else if (back->type == $dffe)
+ module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool());
+ else
+ log_abort();
+ }
+ }
+ else if (back->type.begins_with("$_DFF_")) {
+ Cell *cell = module->addCell(NEW_ID, back->type);
+ cell->setPort(\C, back->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, back->getPort(\D));
+ }
+ else
+ log_abort();
+ shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice];
+ }
+endmatch
+
+code
+ if (next) {
+ chain_bits.insert(port(next, \Q)[slice]);
+ chain.emplace_back(next, slice);
+ if (GetSize(chain) < shiftx_width)
+ subpattern(tail);
+ }
+endcode
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index d045d0dcb..24ae6e448 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {
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("Currently only $adff, $dffsr, and $dlatch cells are supported by this pass.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -169,6 +169,41 @@ struct Async2syncPass : public Pass {
cell->type = "$dff";
continue;
}
+
+ if (cell->type.in("$dlatch"))
+ {
+ bool en_pol = cell->parameters["\\EN_POLARITY"].as_bool();
+
+ SigSpec sig_en = cell->getPort("\\EN");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_en), 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_q = module->addWire(NEW_ID, GetSize(sig_q));
+ new_q->attributes["\\init"] = init_val;
+
+ if (en_pol) {
+ module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q);
+ } else {
+ module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
+ }
+
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\EN");
+ cell->unsetParam("\\EN_POLARITY");
+ cell->type = "$ff";
+ continue;
+ }
}
for (auto wire : module->wires())
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
index dd56d8c71..430bba1e8 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -268,7 +268,7 @@ struct SatHelper
RTLIL::SigSpec removed_bits;
for (int i = 0; i < lhs.size(); i++) {
RTLIL::SigSpec bit = lhs.extract(i, 1);
- if (!satgen.initial_state.check_all(bit)) {
+ if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) {
removed_bits.append(bit);
lhs.remove(i, 1);
rhs.remove(i, 1);
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 56f05eca4..cd357d72a 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -16,6 +16,7 @@ endif
ifneq ($(SMALL),1)
OBJS += passes/techmap/iopadmap.o
+OBJS += passes/techmap/clkbufmap.o
OBJS += passes/techmap/hilomap.o
OBJS += passes/techmap/extract.o
OBJS += passes/techmap/extract_fa.o
@@ -39,6 +40,7 @@ OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
OBJS += passes/techmap/dff2dffs.o
OBJS += passes/techmap/flowmap.o
+OBJS += passes/techmap/extractinv.o
endif
GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 29929f80b..97d4c5ef3 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -76,8 +76,7 @@ inline std::string remap_name(RTLIL::IdString abc_name)
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
}
-void handle_loops(RTLIL::Design *design,
- const dict<IdString,pool<IdString>> &scc_break_inputs)
+void handle_loops(RTLIL::Design *design)
{
Pass::call(design, "scc -set_attr abc_scc_id {}");
@@ -85,7 +84,7 @@ void handle_loops(RTLIL::Design *design,
// cell in the component, and select (and mark) all its output
// wires
pool<RTLIL::Const> ids_seen;
- for (auto cell : module->selected_cells()) {
+ for (auto cell : module->cells()) {
auto it = cell->attributes.find(ID(abc_scc_id));
if (it != cell->attributes.end()) {
auto r = ids_seen.insert(it->second);
@@ -114,30 +113,6 @@ void handle_loops(RTLIL::Design *design,
}
cell->attributes.erase(it);
}
-
- auto jt = scc_break_inputs.find(cell->type);
- if (jt != scc_break_inputs.end())
- for (auto port_name : jt->second) {
- RTLIL::SigSpec sig;
- auto &rhs = cell->connections_.at(port_name);
- for (auto b : rhs) {
- Wire *w = b.wire;
- if (!w) continue;
- w->port_output = true;
- w->set_bool_attribute(ID(abc_scc_break));
- w = module->wire(stringf("%s.abci", w->name.c_str()));
- if (!w) {
- w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
- w->port_input = true;
- }
- else {
- log_assert(b.offset < GetSize(w));
- log_assert(w->port_input);
- }
- sig.append(RTLIL::SigBit(w, b.offset));
- }
- rhs = sig;
- }
}
module->fixup_ports();
@@ -269,11 +244,10 @@ struct abc_output_filter
};
void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
- bool cleanup, vector<int> lut_costs, bool /*dff_mode*/, std::string clk_str,
+ bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
- std::string wire_delay, const dict<int,IdString> &box_lookup,
- const dict<IdString,pool<IdString>> &scc_break_inputs
+ std::string wire_delay, const dict<int,IdString> &box_lookup
)
{
module = current_module;
@@ -309,8 +283,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0));
}
- //if (dff_mode && clk_sig.empty())
- // log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());
+ if (dff_mode && clk_sig.empty())
+ log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());
std::string tempdir_name = "/tmp/yosys-abc-XXXXXX";
if (!cleanup)
@@ -383,7 +357,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
fprintf(f, "%s\n", abc_script.c_str());
fclose(f);
- if (/*dff_mode ||*/ !clk_str.empty())
+ if (dff_mode || !clk_str.empty())
{
if (clk_sig.size() == 0)
log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching");
@@ -413,16 +387,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
RTLIL::Selection& sel = design->selection_stack.back();
sel.select(module);
- handle_loops(design, scc_break_inputs);
+ handle_loops(design);
Pass::call(design, "aigmap");
//log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
// count_gates, GetSize(signal_list), count_input, count_output);
-#if 0
- Pass::call(design, stringf("write_verilog -noexpr -norename %s/before.v", tempdir_name.c_str()));
-#endif
Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
std::string buffer;
@@ -531,12 +502,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
for (int i = 0; i < GetSize(w); i++)
output_bits.insert({wire, i});
}
-
- auto jt = w->attributes.find("\\init");
- if (jt != w->attributes.end()) {
- auto r = remap_wire->attributes.insert(std::make_pair("\\init", jt->second));
- log_assert(r.second);
- }
}
for (auto &it : module->connections_) {
@@ -578,6 +543,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
if (mapped_cell->type == ID($_NOT_)) {
RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
+ bit_users[a_bit].insert(mapped_cell->name);
+ bit_drivers[y_bit].insert(mapped_cell->name);
if (!a_bit.wire) {
mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
@@ -585,8 +552,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
log_assert(wire);
module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
}
- else {
- RTLIL::Cell* driving_lut = nullptr;
+ else if (!lut_costs.empty() || !lut_file.empty()) {
+ RTLIL::Cell* driver_lut = nullptr;
// ABC can return NOT gates that drive POs
if (!a_bit.wire->port_input) {
// If it's not a NOT gate that that comes from a PI directly,
@@ -598,10 +565,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
driver_name = stringf("%s$lut", a_bit.wire->name.c_str());
else
driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset);
- driving_lut = mapped_mod->cell(driver_name);
+ driver_lut = mapped_mod->cell(driver_name);
}
- if (!driving_lut) {
+ if (!driver_lut) {
// If a driver couldn't be found (could be from PI or box CI)
// then implement using a LUT
cell = module->addLut(remap_name(stringf("%s$lut", mapped_cell->name.c_str())),
@@ -610,13 +577,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
RTLIL::Const::from_string("01"));
bit2sinks[cell->getPort(ID::A)].push_back(cell);
cell_stats[ID($lut)]++;
- bit_users[a_bit].insert(mapped_cell->name);
- bit_drivers[y_bit].insert(mapped_cell->name);
}
else
- not2drivers[mapped_cell] = driving_lut;
+ not2drivers[mapped_cell] = driver_lut;
continue;
}
+ else
+ log_abort();
if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
continue;
}
@@ -700,32 +667,31 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
}
for (auto &it : cell_stats)
- log("ABC RESULTS: %15s cells: %8d\n", log_id(it.first), it.second);
+ log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
int in_wires = 0, out_wires = 0;
// Stitch in mapped_mod's inputs/outputs into module
- for (auto port_name : mapped_mod->ports) {
- RTLIL::Wire *port = mapped_mod->wire(port_name);
- log_assert(port);
- RTLIL::Wire *wire = module->wire(port->name);
+ for (auto port : mapped_mod->ports) {
+ RTLIL::Wire *w = mapped_mod->wire(port);
+ RTLIL::Wire *wire = module->wire(port);
log_assert(wire);
- RTLIL::Wire *remap_wire = module->wire(remap_name(port->name));
+ RTLIL::Wire *remap_wire = module->wire(remap_name(port));
RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
log_assert(GetSize(signal) >= GetSize(remap_wire));
RTLIL::SigSig conn;
- if (port->port_input) {
- conn.first = remap_wire;
- conn.second = signal;
- in_wires++;
- module->connect(conn);
- }
- if (port->port_output) {
+ if (w->port_output) {
conn.first = signal;
conn.second = remap_wire;
out_wires++;
module->connect(conn);
}
+ else if (w->port_input) {
+ conn.first = remap_wire;
+ conn.second = signal;
+ in_wires++;
+ module->connect(conn);
+ }
}
for (auto &it : bit_users)
@@ -733,21 +699,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
for (auto driver_cell : bit_drivers.at(it.first))
for (auto user_cell : it.second)
toposort.edge(driver_cell, user_cell);
-#if 0
- toposort.analyze_loops = true;
-#endif
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
-#if 0
- unsigned i = 0;
- for (auto &it : toposort.loops) {
- log(" loop %d\n", i++);
- for (auto cell_name : it) {
- auto cell = mapped_mod->cell(cell_name);
- log_assert(cell);
- log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
- }
- }
-#endif
log_assert(no_loops);
for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
@@ -1048,7 +1000,7 @@ struct Abc9Pass : public Pass {
fast_mode = true;
continue;
}
- //if (arg == "-retime") {
+ //if (arg == "-dff") {
// dff_mode = true;
// continue;
//}
@@ -1075,9 +1027,6 @@ struct Abc9Pass : public Pass {
}
if (arg == "-box" && argidx+1 < args.size()) {
box_file = args[++argidx];
- rewrite_filename(box_file);
- if (!box_file.empty() && !is_absolute_path(box_file))
- box_file = std::string(pwd) + "/" + box_file;
continue;
}
if (arg == "-W" && argidx+1 < args.size()) {
@@ -1088,11 +1037,15 @@ struct Abc9Pass : public Pass {
}
extra_args(args, argidx, design);
- if (lut_costs.empty() && lut_file.empty())
- log_cmd_error("abc9 must be called with '-lut' or '-luts'\n");
+ // ABC expects a box file for XAIG
+ if (box_file.empty())
+ box_file = "+/dummy.box";
+
+ rewrite_filename(box_file);
+ if (!box_file.empty() && !is_absolute_path(box_file))
+ box_file = std::string(pwd) + "/" + box_file;
dict<int,IdString> box_lookup;
- dict<IdString,pool<IdString>> scc_break_inputs;
for (auto m : design->modules()) {
auto it = m->attributes.find(ID(abc_box_id));
if (it == m->attributes.end())
@@ -1110,17 +1063,13 @@ struct Abc9Pass : public Pass {
for (auto p : m->ports) {
auto w = m->wire(p);
log_assert(w);
- if (w->port_input) {
- if (w->attributes.count(ID(abc_scc_break)))
- scc_break_inputs[m->name].insert(p);
- if (w->attributes.count(ID(abc_carry))) {
+ if (w->attributes.count(ID(abc_carry))) {
+ if (w->port_input) {
if (carry_in)
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
carry_in = w;
}
- }
- if (w->port_output) {
- if (w->attributes.count(ID(abc_carry))) {
+ else if (w->port_output) {
if (carry_out)
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
carry_out = w;
@@ -1177,7 +1126,8 @@ struct Abc9Pass : public Pass {
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, false, clk_str, keepff,
delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
+ box_file, lut_file, wire_delay, box_lookup);
+
design->selection_stack.pop_back();
continue;
}
@@ -1361,36 +1311,20 @@ struct Abc9Pass : public Pass {
std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first)));
- design->selection_stack.emplace_back(false);
-
for (auto &it : assigned_cells) {
- // FIXME: abc9_module calls below can delete cells,
- // leaving a dangling pointer here...
clk_polarity = std::get<0>(it.first);
clk_sig = assign_map(std::get<1>(it.first));
en_polarity = std::get<2>(it.first);
en_sig = assign_map(std::get<3>(it.first));
-
- pool<RTLIL::IdString> assigned_names;
- for (auto i : it.second)
- assigned_names.insert(i->name);
- RTLIL::Selection& sel = design->selection_stack.back();
- sel.selected_members[mod->name] = std::move(assigned_names);
-
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
+ box_file, lut_file, wire_delay, box_lookup);
assign_map.set(mod);
}
-
- design->selection_stack.pop_back();
}
assign_map.clear();
- // The "clean" pass also contains a design->check() call
- Pass::call(design, "clean");
-
log_pop();
}
} Abc9Pass;
diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc
index 5b168d524..034731b87 100644
--- a/passes/techmap/alumacc.cc
+++ b/passes/techmap/alumacc.cc
@@ -48,14 +48,25 @@ struct AlumaccWorker
RTLIL::SigSpec cached_cf, cached_of, cached_sf;
RTLIL::SigSpec get_lt() {
- if (GetSize(cached_lt) == 0)
- cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf();
+ if (GetSize(cached_lt) == 0) {
+ if (is_signed) {
+ get_of();
+ get_sf();
+ cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
+ }
+ else
+ cached_lt = get_cf();
+ }
return cached_lt;
}
RTLIL::SigSpec get_gt() {
- if (GetSize(cached_gt) == 0)
- cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute());
+ if (GetSize(cached_gt) == 0) {
+ get_lt();
+ get_eq();
+ SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
+ cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
+ }
return cached_gt;
}
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index a38638e0b..5f30817d4 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -143,6 +143,82 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio
attributes.swap(new_attributes);
}
+void log_attrmap_paramap_options()
+{
+ log(" -tocase <name>\n");
+ log(" Match attribute names case-insensitively and set it to the specified\n");
+ log(" name.\n");
+ log("\n");
+ log(" -rename <old_name> <new_name>\n");
+ log(" Rename attributes as specified\n");
+ log("\n");
+ log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Map key/value pairs as indicated.\n");
+ log("\n");
+ log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Like -map, but use case-insensitive match for <old_value> when\n");
+ log(" it is a string value.\n");
+ log("\n");
+ log(" -remove <name>=<value>\n");
+ log(" Remove attributes matching this pattern.\n");
+}
+
+bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions)
+{
+ std::string arg = args[argidx];
+ if (arg == "-tocase" && argidx+1 < args.size()) {
+ auto action = new AttrmapTocase;
+ action->name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-rename" && argidx+2 < args.size()) {
+ auto action = new AttrmapRename;
+ action->old_name = args[++argidx];
+ action->new_name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
+ string arg1 = args[++argidx];
+ string arg2 = args[++argidx];
+ string val1, val2;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ p = arg2.find("=");
+ if (p != string::npos) {
+ val2 = arg2.substr(p+1);
+ arg2 = arg2.substr(0, p);
+ }
+ auto action = new AttrmapMap;
+ action->imap = (arg == "-map");
+ action->old_name = arg1;
+ action->new_name = arg2;
+ action->old_value = val1;
+ action->new_value = val2;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-remove" && argidx+1 < args.size()) {
+ string arg1 = args[++argidx], val1;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ auto action = new AttrmapRemove;
+ action->name = arg1;
+ action->has_value = (p != string::npos);
+ action->value = val1;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ return false;
+}
+
struct AttrmapPass : public Pass {
AttrmapPass() : Pass("attrmap", "renaming attributes") { }
void help() YS_OVERRIDE
@@ -151,25 +227,10 @@ struct AttrmapPass : public Pass {
log("\n");
log(" attrmap [options] [selection]\n");
log("\n");
- log("This command renames attributes and/or mapps key/value pairs to\n");
+ log("This command renames attributes and/or maps key/value pairs to\n");
log("other key/value pairs.\n");
log("\n");
- log(" -tocase <name>\n");
- log(" Match attribute names case-insensitively and set it to the specified\n");
- log(" name.\n");
- log("\n");
- log(" -rename <old_name> <new_name>\n");
- log(" Rename attributes as specified\n");
- log("\n");
- log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Map key/value pairs as indicated.\n");
- log("\n");
- log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Like -map, but use case-insensitive match for <old_value> when\n");
- log(" it is a string value.\n");
- log("\n");
- log(" -remove <name>=<value>\n");
- log(" Remove attributes matching this pattern.\n");
+ log_attrmap_paramap_options();
log("\n");
log(" -modattr\n");
log(" Operate on module attributes instead of attributes on wires and cells.\n");
@@ -190,58 +251,9 @@ struct AttrmapPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- std::string arg = args[argidx];
- if (arg == "-tocase" && argidx+1 < args.size()) {
- auto action = new AttrmapTocase;
- action->name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-rename" && argidx+2 < args.size()) {
- auto action = new AttrmapRename;
- action->old_name = args[++argidx];
- action->new_name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ if (parse_attrmap_paramap_options(argidx, args, actions))
continue;
- }
- if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
- string arg1 = args[++argidx];
- string arg2 = args[++argidx];
- string val1, val2;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- p = arg2.find("=");
- if (p != string::npos) {
- val2 = arg2.substr(p+1);
- arg2 = arg2.substr(0, p);
- }
- auto action = new AttrmapMap;
- action->imap = (arg == "-map");
- action->old_name = arg1;
- action->new_name = arg2;
- action->old_value = val1;
- action->new_value = val2;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-remove" && argidx+1 < args.size()) {
- string arg1 = args[++argidx], val1;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- auto action = new AttrmapRemove;
- action->name = arg1;
- action->has_value = (p != string::npos);
- action->value = val1;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-modattr") {
+ if (args[argidx] == "-modattr") {
modattr_mode = true;
continue;
}
@@ -287,4 +299,43 @@ struct AttrmapPass : public Pass {
}
} AttrmapPass;
+struct ParamapPass : public Pass {
+ ParamapPass() : Pass("paramap", "renaming cell parameters") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" paramap [options] [selection]\n");
+ log("\n");
+ log("This command renames cell parameters and/or maps key/value pairs to\n");
+ log("other key/value pairs.\n");
+ log("\n");
+ log_attrmap_paramap_options();
+ log("\n");
+ log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n");
+ log("\n");
+ log(" paramap -tocase INIT t:LUT4\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n");
+
+ vector<std::unique_ptr<AttrmapAction>> actions;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (parse_attrmap_paramap_options(argidx, args, actions))
+ continue;
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters);
+ }
+} ParamapPass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc
new file mode 100644
index 000000000..246932d81
--- /dev/null
+++ b/passes/techmap/clkbufmap.cc
@@ -0,0 +1,298 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Marcin Koƛcielnicki <mwk@0x04.net>
+ *
+ * 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
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct ClkbufmapPass : public Pass {
+ ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clkbufmap [options] [selection]\n");
+ log("\n");
+ log("Inserts global buffers between nets connected to clock inputs and their drivers.\n");
+ log("\n");
+ log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n");
+ log("attribute will be considered for global buffer insertion.\n");
+ log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n");
+ log("'none' or 'bufr' one would specify:\n");
+ log(" 'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n");
+ log("as the selection.\n");
+ log("\n");
+ log(" -buf <celltype> <portname_out>:<portname_in>\n");
+ log(" Specifies the cell type to use for the global buffers\n");
+ log(" and its port names. The first port will be connected to\n");
+ log(" the clock network sinks, and the second will be connected\n");
+ log(" to the actual clock source. This option is required.\n");
+ log("\n");
+ log(" -inpad <celltype> <portname_out>:<portname_in>\n");
+ log(" If specified, a PAD cell of the given type is inserted on\n");
+ log(" clock nets that are also top module's inputs (in addition\n");
+ log(" to the global buffer).\n");
+ log("\n");
+ }
+
+ void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) {
+ if (modules_processed.count(module))
+ return;
+ for (auto cell : module->cells()) {
+ Module *submodule = design->module(cell->type);
+ if (!submodule)
+ continue;
+ module_queue(design, submodule, modules_sorted, modules_processed);
+ }
+ modules_sorted.push_back(module);
+ modules_processed.insert(module);
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n");
+
+ std::string buf_celltype, buf_portname, buf_portname2;
+ std::string inpad_celltype, inpad_portname, inpad_portname2;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-buf" && argidx+2 < args.size()) {
+ buf_celltype = args[++argidx];
+ buf_portname = args[++argidx];
+ split_portname_pair(buf_portname, buf_portname2);
+ continue;
+ }
+ if (arg == "-inpad" && argidx+2 < args.size()) {
+ inpad_celltype = args[++argidx];
+ inpad_portname = args[++argidx];
+ split_portname_pair(inpad_portname, inpad_portname2);
+ continue;
+ }
+ break;
+ }
+
+ bool select = false;
+ if (argidx < args.size()) {
+ if (args[argidx].compare(0, 1, "-") != 0)
+ select = true;
+ extra_args(args, argidx, design);
+ }
+
+ if (buf_celltype.empty())
+ log_error("The -buf option is required.\n");
+
+ // Cell type, port name, bit index.
+ pool<pair<IdString, pair<IdString, int>>> sink_ports;
+ pool<pair<IdString, pair<IdString, int>>> buf_ports;
+
+ // Process submodules before module using them.
+ std::vector<Module *> modules_sorted;
+ pool<Module *> modules_processed;
+ for (auto module : design->selected_modules())
+ module_queue(design, module, modules_sorted, modules_processed);
+
+ for (auto module : modules_sorted)
+ {
+ if (module->get_blackbox_attribute()) {
+ for (auto port : module->ports) {
+ auto wire = module->wire(port);
+ if (wire->get_bool_attribute("\\clkbuf_driver"))
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ if (wire->get_bool_attribute("\\clkbuf_sink"))
+ for (int i = 0; i < GetSize(wire); i++)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ continue;
+ }
+ pool<SigBit> sink_wire_bits;
+ pool<SigBit> buf_wire_bits;
+ pool<SigBit> driven_wire_bits;
+ SigMap sigmap(module);
+ // bit -> (buffer, buffer's input)
+ dict<SigBit, pair<Cell *, Wire *>> buffered_bits;
+
+ // First, collect nets that could use a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ sink_wire_bits.insert(sigmap(port.second[i]));
+
+ // Second, collect ones that already have a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ buf_wire_bits.insert(sigmap(port.second[i]));
+
+ // Collect all driven bits.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ if (cell->output(port.first))
+ for (int i = 0; i < port.second.size(); i++)
+ driven_wire_bits.insert(port.second[i]);
+
+ // Insert buffers.
+ std::vector<pair<Wire *, Wire *>> input_queue;
+ // Copy current wire list, as we will be adding new ones during iteration.
+ std::vector<Wire *> wires(module->wires());
+ for (auto wire : wires)
+ {
+ // Should not happen.
+ if (wire->port_input && wire->port_output)
+ continue;
+ bool process_wire = module->selected(wire);
+ if (!select && wire->get_bool_attribute("\\clkbuf_inhibit"))
+ process_wire = false;
+ if (!process_wire) {
+ // This wire is supposed to be bypassed, so make sure we don't buffer it in
+ // some buffer higher up in the hierarchy.
+ if (wire->port_output)
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ continue;
+ }
+
+ pool<int> input_bits;
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buf_wire_bits.count(mapped_wire_bit)) {
+ // Already buffered downstream. If this is an output, mark it.
+ if (wire->port_output)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ } else if (!sink_wire_bits.count(mapped_wire_bit)) {
+ // Nothing to do.
+ } else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) {
+ // Clock network not yet buffered, driven by one of
+ // our cells or a top-level input -- buffer it.
+
+ log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype));
+ Wire *iwire = module->addWire(NEW_ID);
+ cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit);
+ cell->setPort(RTLIL::escape_id(buf_portname2), iwire);
+ if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) {
+ log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype));
+ cell2->setPort(RTLIL::escape_id(inpad_portname), iwire);
+ iwire = module->addWire(NEW_ID);
+ cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire);
+ }
+ buffered_bits[mapped_wire_bit] = make_pair(cell, iwire);
+
+ if (wire->port_input) {
+ input_bits.insert(i);
+ }
+ } else if (wire->port_input) {
+ // A clock input in a submodule -- mark it, let higher level
+ // worry about it.
+ if (wire->port_input)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+ if (!input_bits.empty()) {
+ // This is an input port and some buffers were inserted -- we need
+ // to create a new input wire and transfer attributes.
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+
+ for (int i = 0; i < wire->width; i++) {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ auto it = buffered_bits.find(mapped_wire_bit);
+ if (it != buffered_bits.end()) {
+
+ module->connect(it->second.second, SigSpec(new_wire, i));
+ } else {
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ }
+ }
+ input_queue.push_back(make_pair(wire, new_wire));
+ }
+ }
+
+ // Mark any newly-buffered output ports as such.
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_input || !wire->port_output)
+ continue;
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buffered_bits.count(mapped_wire_bit))
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+
+ // Reconnect the drivers to buffer inputs.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections()) {
+ if (!cell->output(port.first))
+ continue;
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig) {
+ const auto it = buffered_bits.find(sigmap(bit));
+ if (it == buffered_bits.end())
+ continue;
+ // Avoid substituting buffer's own output pin.
+ if (cell == it->second.first)
+ continue;
+ bit = it->second.second;
+ newsig = true;
+ }
+ if (newsig)
+ cell->setPort(port.first, sig);
+ }
+
+ // This has to be done last, to avoid upsetting sigmap before the port reconnections.
+ for (auto &it : input_queue) {
+ Wire *wire = it.first;
+ Wire *new_wire = it.second;
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ module->fixup_ports();
+ }
+ }
+} ClkbufmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dff2dffs.cc b/passes/techmap/dff2dffs.cc
index 0ea033513..3fa1ed5cf 100644
--- a/passes/techmap/dff2dffs.cc
+++ b/passes/techmap/dff2dffs.cc
@@ -34,11 +34,16 @@ struct Dff2dffsPass : public Pass {
log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n");
log("dff2dffe for SR over CE priority.\n");
log("\n");
+ log(" -match-init\n");
+ log(" Disallow merging synchronous set/reset that has polarity opposite of the\n");
+ log(" output wire's init attribute (if any).\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n");
+ bool match_init = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
@@ -46,6 +51,10 @@ struct Dff2dffsPass : public Pass {
// singleton_mode = true;
// continue;
// }
+ if (args[argidx] == "-match-init") {
+ match_init = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -96,9 +105,6 @@ struct Dff2dffsPass : public Pass {
SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
SigBit bit_s = sigmap(mux_cell->getPort(ID(S)));
- log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
- log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
-
SigBit sr_val, sr_sig;
bool invert_sr;
sr_sig = bit_s;
@@ -113,6 +119,23 @@ struct Dff2dffsPass : public Pass {
invert_sr = false;
}
+ if (match_init) {
+ SigBit bit_q = cell->getPort(ID(Q));
+ if (bit_q.wire) {
+ auto it = bit_q.wire->attributes.find(ID(init));
+ if (it != bit_q.wire->attributes.end()) {
+ auto init_val = it->second[bit_q.offset];
+ if (init_val == State::S1 && sr_val != State::S1)
+ continue;
+ if (init_val == State::S0 && sr_val != State::S0)
+ continue;
+ }
+ }
+ }
+
+ log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
+ log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
+
if (sr_val == State::S1) {
if (cell->type == ID($_DFF_N_)) {
if (invert_sr) cell->type = ID($__DFFS_NN1_);
diff --git a/passes/techmap/extractinv.cc b/passes/techmap/extractinv.cc
new file mode 100644
index 000000000..dda71f12a
--- /dev/null
+++ b/passes/techmap/extractinv.cc
@@ -0,0 +1,123 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Marcin Koƛcielnicki <mwk@0x04.net>
+ *
+ * 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
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct ExtractinvPass : public Pass {
+ ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" extractinv [options] [selection]\n");
+ log("\n");
+ log("Searches the design for all cells with invertible pins controlled by a cell\n");
+ log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n");
+ log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n");
+ log("the pin instead. Normally used for output to ISE, which does not support the\n");
+ log("inversion parameters.\n");
+ log("\n");
+ log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n");
+ log("on the wire in the blackbox module. The parameter value should have\n");
+ log("the same width as the port, and will be effectively XORed with it.\n");
+ log("\n");
+ log(" -inv <celltype> <portname_out>:<portname_in>\n");
+ log(" Specifies the cell type to use for the inverters and its port names.\n");
+ log(" This option is required.\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n");
+
+ std::string inv_celltype, inv_portname, inv_portname2;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-inv" && argidx+2 < args.size()) {
+ inv_celltype = args[++argidx];
+ inv_portname = args[++argidx];
+ split_portname_pair(inv_portname, inv_portname2);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (inv_celltype.empty())
+ log_error("The -inv option is required.\n");
+
+ for (auto module : design->selected_modules())
+ {
+ for (auto cell : module->selected_cells())
+ for (auto port : cell->connections()) {
+ auto cell_module = design->module(cell->type);
+ if (!cell_module)
+ continue;
+ auto cell_wire = cell_module->wire(port.first);
+ if (!cell_wire)
+ continue;
+ auto it = cell_wire->attributes.find("\\invertible_pin");
+ if (it == cell_wire->attributes.end())
+ continue;
+ IdString param_name = RTLIL::escape_id(it->second.decode_string());
+ auto it2 = cell->parameters.find(param_name);
+ // Inversion not used -- skip.
+ if (it2 == cell->parameters.end())
+ continue;
+ SigSpec sig = port.second;
+ if (it2->second.size() != sig.size())
+ log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name));
+ RTLIL::Const invmask = it2->second;
+ cell->parameters.erase(param_name);
+ if (invmask.is_fully_zero())
+ continue;
+ Wire *iwire = module->addWire(NEW_ID, sig.size());
+ for (int i = 0; i < sig.size(); i++)
+ if (invmask[i] == State::S1) {
+ RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype));
+ icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i));
+ icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]);
+ log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i);
+ sig[i] = SigBit(iwire, i);
+ }
+ cell->setPort(port.first, sig);
+ }
+ }
+ }
+} ExtractinvPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index a2551316f..c868b9a87 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -64,6 +64,11 @@ struct IopadmapPass : public Pass {
log(" of the tristate driver and the 2nd portname is the internal output\n");
log(" buffering the external signal.\n");
log("\n");
+ log(" -ignore <celltype> <portname>[:<portname>]*\n");
+ log(" Skips mapping inputs/outputs that are already connected to given\n");
+ log(" ports of the given cell. Can be used multiple times. This is in\n");
+ log(" addition to the cells specified as mapping targets.\n");
+ log("\n");
log(" -widthparam <param_name>\n");
log(" Use the specified parameter name to set the port width.\n");
log("\n");
@@ -88,6 +93,7 @@ struct IopadmapPass : public Pass {
std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
std::string widthparam, nameparam;
+ pool<pair<IdString, IdString>> ignore;
bool flag_bits = false;
size_t argidx;
@@ -127,6 +133,18 @@ struct IopadmapPass : public Pass {
split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
continue;
}
+ if (arg == "-ignore" && argidx+2 < args.size()) {
+ std::string ignore_celltype = args[++argidx];
+ std::string ignore_portname = args[++argidx];
+ std::string ignore_portname2;
+ while (!ignore_portname.empty()) {
+ split_portname_pair(ignore_portname, ignore_portname2);
+ ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname)));
+
+ ignore_portname = ignore_portname2;
+ }
+ continue;
+ }
if (arg == "-widthparam" && argidx+1 < args.size()) {
widthparam = args[++argidx];
continue;
@@ -143,6 +161,23 @@ struct IopadmapPass : public Pass {
}
extra_args(args, argidx, design);
+ if (!inpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
+ if (!outpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
+ if (!inoutpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
+ if (!toutpad_portname3.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
+ if (!tinoutpad_portname4.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
+
+ for (auto module : design->modules())
+ if (module->get_blackbox_attribute())
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute("\\iopad_external_pin"))
+ ignore.insert(make_pair(module->name, wire->name));
+
for (auto module : design->selected_modules())
{
dict<IdString, pool<int>> skip_wires;
@@ -150,28 +185,11 @@ struct IopadmapPass : public Pass {
SigMap sigmap(module);
for (auto cell : module->cells())
- {
- if (cell->type == RTLIL::escape_id(inpad_celltype) && cell->hasPort(RTLIL::escape_id(inpad_portname2)))
- for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inpad_portname2))))
- skip_wire_bits.insert(bit);
-
- if (cell->type == RTLIL::escape_id(outpad_celltype) && cell->hasPort(RTLIL::escape_id(outpad_portname2)))
- for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(outpad_portname2))))
+ for (auto port : cell->connections())
+ if (ignore.count(make_pair(cell->type, port.first)))
+ for (auto bit : sigmap(port.second))
skip_wire_bits.insert(bit);
- if (cell->type == RTLIL::escape_id(inoutpad_celltype) && cell->hasPort(RTLIL::escape_id(inoutpad_portname2)))
- for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inoutpad_portname2))))
- skip_wire_bits.insert(bit);
-
- if (cell->type == RTLIL::escape_id(toutpad_celltype) && cell->hasPort(RTLIL::escape_id(toutpad_portname3)))
- for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(toutpad_portname3))))
- skip_wire_bits.insert(bit);
-
- if (cell->type == RTLIL::escape_id(tinoutpad_celltype) && cell->hasPort(RTLIL::escape_id(tinoutpad_portname4)))
- for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(tinoutpad_portname4))))
- skip_wire_bits.insert(bit);
- }
-
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
{
dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 5e298d8dd..be00e5030 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
- virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
+ virtual bool analyze(vector<int> &taps) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -56,7 +54,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
+ bool analyze(vector<int> &taps)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech
}
};
-struct ShregmapTechXilinx7 : ShregmapTech
-{
- dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
- const ShregmapOptions &opts;
-
- ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
-
- virtual void init(const Module* module, const SigMap &sigmap) override
- {
- for (const auto &i : module->cells_) {
- auto cell = i.second;
- if (cell->type == ID($shiftx)) {
- if (cell->getParam(ID(Y_WIDTH)) != 1) continue;
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
- log_assert(j == cell->getParam(ID(A_WIDTH)).as_int());
- }
- else if (cell->type == ID($mux)) {
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
- j = 0;
- for (auto bit : sigmap(cell->getPort(ID::B)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
- }
- }
- }
-
- virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
- {
- auto it = sigbit_to_shiftx_offset.find(bit);
- if (it == sigbit_to_shiftx_offset.end())
- return;
- if (cell) {
- if (cell->type == ID($shiftx) && port == ID::A)
- return;
- if (cell->type == ID($mux) && port.in(ID::A, ID::B))
- return;
- }
- sigbit_to_shiftx_offset.erase(it);
- }
-
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
- {
- if (GetSize(taps) == 1)
- return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
-
- if (taps.back() < opts.minlen-1)
- return false;
-
- Cell *shiftx = nullptr;
- int group = 0;
- for (int i = 0; i < GetSize(taps); ++i) {
- auto it = sigbit_to_shiftx_offset.find(qbits[i]);
- if (it == sigbit_to_shiftx_offset.end())
- return false;
-
- // Check taps are sequential
- if (i != taps[i])
- return false;
- // Check taps are not connected to a shift register,
- // or sequential to the same shift register
- if (i == 0) {
- int offset;
- std::tie(shiftx,offset,group) = it->second;
- if (offset != i)
- return false;
- }
- else {
- Cell *shiftx_ = std::get<0>(it->second);
- if (shiftx_ != shiftx)
- return false;
- int offset = std::get<1>(it->second);
- if (offset != i)
- return false;
- int group_ = std::get<2>(it->second);
- if (group_ != group)
- return false;
- }
- }
- log_assert(shiftx);
-
- // Only map if $shiftx exclusively covers the shift register
- if (shiftx->type == ID($shiftx)) {
- if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- // Due to padding the most significant bits of A may be 1'bx,
- // and if so, discount them
- if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) {
- const SigSpec A = shiftx->getPort(ID::A);
- const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int();
- for (int i = GetSize(taps); i < A_width; ++i)
- if (A[i] != RTLIL::Sx) return false;
- }
- else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- }
- else if (shiftx->type == ID($mux)) {
- if (GetSize(taps) != 2)
- return false;
- }
- else log_abort();
-
- return true;
- }
-
- virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
- {
- const auto &tap = *taps.begin();
- auto bit = tap.second;
-
- auto it = sigbit_to_shiftx_offset.find(bit);
- log_assert(it != sigbit_to_shiftx_offset.end());
-
- auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_));
- newcell->set_src_attribute(cell->get_src_attribute());
- newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH)));
- newcell->setParam(ID(INIT), cell->getParam(ID(INIT)));
- newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL)));
- newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL)));
-
- newcell->setPort(ID(C), cell->getPort(ID(C)));
- newcell->setPort(ID(D), cell->getPort(ID(D)));
- if (cell->hasPort(ID(E)))
- newcell->setPort(ID(E), cell->getPort(ID(E)));
-
- Cell* shiftx = std::get<0>(it->second);
- RTLIL::SigSpec l_wire, q_wire;
- if (shiftx->type == ID($shiftx)) {
- l_wire = shiftx->getPort(ID::B);
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else if (shiftx->type == ID($mux)) {
- l_wire = shiftx->getPort(ID(S));
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else log_abort();
-
- newcell->setPort(ID(Q), q_wire);
- newcell->setPort(ID(L), l_wire);
-
- return false;
- }
-};
-
-
struct ShregmapWorker
{
Module *module;
@@ -264,10 +113,8 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
- for (auto bit : sigmap(wire)) {
+ for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
- }
}
if (wire->attributes.count(ID(init))) {
@@ -317,10 +164,8 @@ struct ShregmapWorker
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second)) {
+ for (auto bit : sigmap(conn.second))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
- }
}
}
@@ -346,7 +191,7 @@ struct ShregmapWorker
IdString q_port = opts.ffcells.at(c1->type).second;
auto c1_conn = c1->connections();
- auto c2_conn = c1->connections();
+ auto c2_conn = c2->connections();
c1_conn.erase(d_port);
c1_conn.erase(q_port);
@@ -425,7 +270,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps, qbits))
+ if (opts.tech->analyze(taps))
break;
taps.pop_back();
@@ -544,9 +389,6 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
- if (opts.tech)
- opts.tech->init(module, sigmap);
-
make_sigbit_chain_next_prev();
find_chain_start_cells();
@@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {
log("\n");
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
- log(" this option also implies -clkpol pos -zinit\n");
- log("\n");
- log(" -tech xilinx\n");
- log(" map to xilinx dynamic-length shift registers.\n");
- log(" this option also implies -params -init\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -676,12 +513,6 @@ struct ShregmapPass : public Pass {
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
- }
- else if (tech == "xilinx") {
- opts.init = true;
- opts.params = true;
- enpol = "any_or_none";
- opts.tech = new ShregmapTechXilinx7(opts);
} else {
argidx--;
break;
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index b271c8781..08a1af2d5 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -205,20 +205,57 @@ struct TechmapWorker
}
std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
+ dict<Wire*, IdString> temp_renamed_wires;
+ pool<SigBit> autopurge_tpl_bits;
- for (auto &it : tpl->wires_) {
+ for (auto &it : tpl->wires_)
+ {
if (it.second->port_id > 0)
- positional_ports[stringf("$%d", it.second->port_id)] = it.first;
+ {
+ IdString posportname = stringf("$%d", it.second->port_id);
+ positional_ports[posportname] = it.first;
+
+ if (!flatten_mode && it.second->get_bool_attribute(ID(techmap_autopurge)) &&
+ (!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) &&
+ (!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname))))
+ {
+ if (sigmaps.count(tpl) == 0)
+ sigmaps[tpl].set(tpl);
+
+ for (auto bit : sigmaps.at(tpl)(it.second))
+ if (bit.wire != nullptr)
+ autopurge_tpl_bits.insert(bit);
+ }
+ }
IdString w_name = it.second->name;
apply_prefix(cell->name, w_name);
- RTLIL::Wire *w = module->addWire(w_name, it.second);
- w->port_input = false;
- w->port_output = false;
- w->port_id = 0;
- if (it.second->get_bool_attribute(ID(_techmap_special_)))
- w->attributes.clear();
- if (w->attributes.count(ID(src)))
- w->add_strpool_attribute(ID(src), extra_src_attrs);
+ RTLIL::Wire *w = module->wire(w_name);
+ if (w != nullptr) {
+ if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) {
+ temp_renamed_wires[w] = w->name;
+ module->rename(w, NEW_ID);
+ w = nullptr;
+ } else {
+ w->attributes.erase(ID(hierconn));
+ if (GetSize(w) < GetSize(it.second)) {
+ log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w),
+ log_id(tpl), log_id(it.second), log_id(module), log_id(cell));
+ w->width = GetSize(it.second);
+ }
+ }
+ }
+ if (w == nullptr) {
+ w = module->addWire(w_name, it.second);
+ w->port_input = false;
+ w->port_output = false;
+ w->port_id = 0;
+ if (!flatten_mode)
+ w->attributes.erase(ID(techmap_autopurge));
+ if (it.second->get_bool_attribute(ID(_techmap_special_)))
+ w->attributes.clear();
+ if (w->attributes.count(ID(src)))
+ w->add_strpool_attribute(ID(src), extra_src_attrs);
+ }
design->select(module, w);
}
@@ -322,6 +359,12 @@ struct TechmapWorker
for (auto &attr : w->attributes) {
if (attr.first == ID(src))
continue;
+ auto lhs = GetSize(extra_connect.first);
+ auto rhs = GetSize(extra_connect.second);
+ if (lhs > rhs)
+ extra_connect.first.remove(rhs, lhs-rhs);
+ else if (rhs > lhs)
+ extra_connect.second.remove(lhs, rhs-lhs);
module->connect(extra_connect);
break;
}
@@ -344,11 +387,31 @@ struct TechmapWorker
if (!flatten_mode && c->type.begins_with("\\$"))
c->type = c->type.substr(1);
- for (auto &it2 : c->connections_) {
- apply_prefix(cell->name, it2.second, module);
- port_signal_map.apply(it2.second);
+ vector<IdString> autopurge_ports;
+
+ for (auto &it2 : c->connections_)
+ {
+ bool autopurge = false;
+ if (!autopurge_tpl_bits.empty()) {
+ autopurge = GetSize(it2.second) != 0;
+ for (auto &bit : sigmaps.at(tpl)(it2.second))
+ if (!autopurge_tpl_bits.count(bit)) {
+ autopurge = false;
+ break;
+ }
+ }
+
+ if (autopurge) {
+ autopurge_ports.push_back(it2.first);
+ } else {
+ apply_prefix(cell->name, it2.second, module);
+ port_signal_map.apply(it2.second);
+ }
}
+ for (auto &it2 : autopurge_ports)
+ c->unsetPort(it2);
+
if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
IdString memid = c->getParam(ID(MEMID)).decode_string();
log_assert(memory_renames.count(memid) != 0);
@@ -380,6 +443,16 @@ struct TechmapWorker
}
module->remove(cell);
+
+ for (auto &it : temp_renamed_wires)
+ {
+ Wire *w = it.first;
+ IdString name = it.second;
+ IdString altname = module->uniquify(name);
+ Wire *other_w = module->wire(name);
+ module->rename(other_w, altname);
+ module->rename(w, name);
+ }
}
bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
@@ -396,6 +469,18 @@ struct TechmapWorker
SigMap sigmap(module);
+ dict<SigBit, State> init_bits;
+ pool<SigBit> remove_init_bits;
+
+ for (auto wire : module->wires()) {
+ if (wire->attributes.count("\\init")) {
+ Const value = wire->attributes.at("\\init");
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
+ if (value[i] != State::Sx)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
+ }
+ }
+
TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
@@ -633,6 +718,17 @@ struct TechmapWorker
bit = RTLIL::SigBit(RTLIL::State::Sx);
parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
}
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ auto sig = sigmap(conn.second);
+ RTLIL::Const value(State::Sx, sig.size());
+ for (int i = 0; i < sig.size(); i++) {
+ auto it = init_bits.find(sig[i]);
+ if (it != init_bits.end()) {
+ value[i] = it->second;
+ }
+ }
+ parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value;
+ }
}
int unique_bit_id_counter = 0;
@@ -833,7 +929,7 @@ struct TechmapWorker
TechmapWires twd = techmap_find_special_wires(tpl);
for (auto &it : twd) {
- if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
+ if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
if (techmap_do_cache[tpl])
for (auto &it2 : it.second)
@@ -864,6 +960,23 @@ struct TechmapWorker
mkdebug.off();
}
+ TechmapWires twd = techmap_find_special_wires(tpl);
+ for (auto &it : twd) {
+ if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") {
+ for (auto &it2 : it.second) {
+ auto val = it2.value.as_const();
+ auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1));
+ auto it = cell->connections().find(wirename);
+ if (it != cell->connections().end()) {
+ auto sig = sigmap(it->second);
+ for (int i = 0; i < sig.size(); i++)
+ if (val[i] == State::S1)
+ remove_init_bits.insert(sig[i]);
+ }
+ }
+ }
+ }
+
if (extern_mode && !in_recursion)
{
std::string m_name = stringf("$extern:%s", log_id(tpl));
@@ -907,6 +1020,25 @@ struct TechmapWorker
handled_cells.insert(cell);
}
+ if (!remove_init_bits.empty()) {
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init")) {
+ Const &value = wire->attributes.at("\\init");
+ bool do_cleanup = true;
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
+ SigBit bit = sigmap(SigBit(wire, i));
+ if (remove_init_bits.count(bit))
+ value[i] = State::Sx;
+ else if (value[i] != State::Sx)
+ do_cleanup = false;
+ }
+ if (do_cleanup) {
+ log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
+ wire->attributes.erase("\\init");
+ }
+ }
+ }
+
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
@@ -943,7 +1075,8 @@ struct TechmapPass : public Pass {
log(" instead of inlining them.\n");
log("\n");
log(" -max_iter <number>\n");
- log(" only run the specified number of iterations.\n");
+ log(" only run the specified number of iterations on each module.\n");
+ log(" default: unlimited\n");
log("\n");
log(" -recursive\n");
log(" instead of the iterative breadth-first algorithm use a recursive\n");
@@ -980,6 +1113,11 @@ struct TechmapPass : public Pass {
log("will create a wrapper for the cell and then run the command string that the\n");
log("attribute is set to on the wrapper module.\n");
log("\n");
+ log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n");
+ log("set, and that port is not connected in the instantiation that is mapped, then\n");
+ log("then a cell port connected only to such wires will be omitted in the mapped\n");
+ log("version of the circuit.\n");
+ log("\n");
log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
log("the mapping module to the techmap command. At the moment the following special\n");
@@ -1018,6 +1156,13 @@ struct TechmapPass : public Pass {
log("\n");
log(" It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");
log("\n");
+ log(" _TECHMAP_REMOVEINIT_<port-name>_\n");
+ log(" When this wire is set to a constant value, the init attribute of the wire(s)\n");
+ log(" connected to this port will be consumed. This wire must have the same\n");
+ log(" width as the given port, and for every bit that is set to 1 in the value,\n");
+ log(" the corresponding init attribute bit will be changed to 1'bx. If all\n");
+ log(" bits of an init attribute are left as x, it will be removed.\n");
+ log("\n");
log("In addition to this special wires, techmap also supports special parameters in\n");
log("modules in the map file:\n");
log("\n");
@@ -1031,6 +1176,13 @@ struct TechmapPass : public Pass {
log(" former has a 1-bit for each constant input bit and the latter has the\n");
log(" value for this bit. The unused bits of the latter are set to undef (x).\n");
log("\n");
+ log(" _TECHMAP_WIREINIT_<port-name>_\n");
+ log(" When a parameter with this name exists, it will be set to the initial\n");
+ log(" value of the wire(s) connected to the given port, as specified by the init\n");
+ log(" attribute. If the attribute doesn't exist, x will be filled for the\n");
+ log(" missing bits. To remove the init attribute bits used, use the\n");
+ log(" _TECHMAP_REMOVEINIT_*_ wires.\n");
+ log("\n");
log(" _TECHMAP_BITS_CONNMAP_\n");
log(" _TECHMAP_CONNMAP_<port-name>_\n");
log(" For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n");
@@ -1157,15 +1309,16 @@ struct TechmapPass : public Pass {
RTLIL::Module *module = *worker.module_queue.begin();
worker.module_queue.erase(module);
+ int module_max_iter = max_iter;
bool did_something = true;
std::set<RTLIL::Cell*> handled_cells;
while (did_something) {
did_something = false;
- if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
- did_something = true;
+ if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
+ did_something = true;
if (did_something)
module->check();
- if (max_iter > 0 && --max_iter == 0)
+ if (module_max_iter > 0 && --module_max_iter == 0)
break;
}
}
diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc
index bfb1d6642..a5ac3130f 100644
--- a/passes/tests/test_autotb.cc
+++ b/passes/tests/test_autotb.cc
@@ -345,6 +345,9 @@ struct TestAutotbBackend : public Backend {
log("value after initialization. This can e.g. be used to force a reset signal\n");
log("low in order to explore more inner states in a state machine.\n");
log("\n");
+ log("The attribute 'gentb_skip' can be attached to modules to suppress testbench\n");
+ log("generation.\n");
+ log("\n");
log(" -n <int>\n");
log(" number of iterations the test bench should run (default = 1000)\n");
log("\n");