aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
authorJim Lawson <ucbjrl@berkeley.edu>2019-02-25 16:04:20 -0800
committerJim Lawson <ucbjrl@berkeley.edu>2019-02-25 16:04:20 -0800
commit81abb2517c3d6e8fd2b31ff6d9d019d956a6bc14 (patch)
treec64df65f19414b166128231218f0f595d93fa58a /passes
parent970f854c2ad271098b841e61b1d37a61cd04e252 (diff)
parentc258b99040c8414952a3aceae874dc47563540dc (diff)
downloadyosys-81abb2517c3d6e8fd2b31ff6d9d019d956a6bc14.tar.gz
yosys-81abb2517c3d6e8fd2b31ff6d9d019d956a6bc14.tar.bz2
yosys-81abb2517c3d6e8fd2b31ff6d9d019d956a6bc14.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/setundef.cc20
-rw-r--r--passes/hierarchy/hierarchy.cc46
-rw-r--r--passes/opt/opt_rmdff.cc13
-rw-r--r--passes/opt/wreduce.cc78
-rw-r--r--passes/pmgen/.gitignore1
-rw-r--r--passes/pmgen/Makefile.inc8
-rw-r--r--passes/pmgen/README.md224
-rw-r--r--passes/pmgen/ice40_dsp.cc237
-rw-r--r--passes/pmgen/ice40_dsp.pmg160
-rw-r--r--passes/pmgen/pmgen.py482
-rw-r--r--passes/techmap/abc.cc93
11 files changed, 1314 insertions, 48 deletions
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index 56ef2d125..f6949c820 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -143,6 +143,9 @@ struct SetundefPass : public Pass {
log(" -init\n");
log(" also create/update init values for flip-flops\n");
log("\n");
+ log(" -params\n");
+ log(" replace undef in cell parameters\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -150,6 +153,7 @@ struct SetundefPass : public Pass {
bool undriven_mode = false;
bool expose_mode = false;
bool init_mode = false;
+ bool params_mode = false;
SetundefWorker worker;
log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
@@ -199,6 +203,10 @@ struct SetundefPass : public Pass {
init_mode = true;
continue;
}
+ if (args[argidx] == "-params") {
+ params_mode = true;
+ continue;
+ }
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
got_value = true;
worker.next_bit_mode = MODE_RANDOM;
@@ -228,6 +236,18 @@ struct SetundefPass : public Pass {
for (auto module : design->selected_modules())
{
+ if (params_mode)
+ {
+ for (auto *cell : module->selected_cells()) {
+ for (auto &parameter : cell->parameters) {
+ for (auto &bit : parameter.second.bits) {
+ if (bit > RTLIL::State::S1)
+ bit = worker.next_bit();
+ }
+ }
+ }
+ }
+
if (undriven_mode)
{
if (!module->processes.empty())
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 0e28dbca2..2d8edebb5 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -140,6 +140,23 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
}
}
+// Return the "basic" type for an array item.
+std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
+ std::string basicType = celltype;
+ if (celltype.substr(0, 7) == "$array:") {
+ int pos_idx = celltype.find_first_of(':');
+ int pos_num = celltype.find_first_of(':', pos_idx + 1);
+ int pos_type = celltype.find_first_of(':', pos_num + 1);
+ basicType = celltype.substr(pos_type + 1);
+ if (pos != nullptr) {
+ pos[0] = pos_idx;
+ pos[1] = pos_num;
+ pos[2] = pos_type;
+ }
+ }
+ return basicType;
+}
+
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
{
bool did_something = false;
@@ -178,9 +195,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
std::vector<RTLIL::SigSpec> connections_to_add_signal;
if (cell->type.substr(0, 7) == "$array:") {
- int pos_idx = cell->type.str().find_first_of(':');
- int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
- int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
+ int pos[3];
+ basic_cell_type(cell->type.str(), pos);
+ int pos_idx = pos[0];
+ int pos_num = pos[1];
+ int pos_type = pos[2];
int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
array_cells[cell] = std::pair<int, int>(idx, num);
@@ -439,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
for (auto cell : mod->cells()) {
std::string celltype = cell->type.str();
if (celltype.substr(0, 7) == "$array:") {
- int pos_idx = celltype.find_first_of(':');
- int pos_num = celltype.find_first_of(':', pos_idx + 1);
- int pos_type = celltype.find_first_of(':', pos_num + 1);
- celltype = celltype.substr(pos_type + 1);
+ celltype = basic_cell_type(celltype);
}
if (design->module(celltype))
hierarchy_worker(design, used, design->module(celltype), indent+4);
@@ -502,9 +518,19 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
if (db.count(module) == 0) {
int score = 0;
db[module] = 0;
- for (auto cell : module->cells())
- if (design->module(cell->type))
- score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
+ for (auto cell : module->cells()) {
+ std::string celltype = cell->type.str();
+ // Is this an array instance
+ if (celltype.substr(0, 7) == "$array:") {
+ celltype = basic_cell_type(celltype);
+ }
+ // Is this cell a module instance?
+ auto instModule = design->module(celltype);
+ // If there is no instance for this, issue a warning.
+ if (instModule != nullptr) {
+ score = max(score, find_top_mod_score(design, instModule, db) + 1);
+ }
+ }
db[module] = score;
}
return db.at(module);
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index 5880254c1..e8570f0eb 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetParam("\\CLR_POLARITY");
cell->unsetPort("\\SET");
cell->unsetPort("\\CLR");
-
- return true;
}
else
{
@@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetParam("\\CLR_POLARITY");
cell->unsetPort("\\SET");
cell->unsetPort("\\CLR");
-
- return true;
}
+
+ return true;
}
- else
+
+ if (!hasreset)
{
IdString new_type;
@@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetPort("\\S");
cell->unsetPort("\\R");
- return did_something;
+ return true;
}
+
+ return did_something;
}
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 8063b86a6..09983bc67 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -38,7 +38,8 @@ struct WreduceConfig
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
- "$mux", "$pmux"
+ "$mux", "$pmux",
+ "$dff", "$adff"
});
}
};
@@ -134,6 +135,78 @@ struct WreduceWorker
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
}
+ void run_cell_dff(Cell *cell)
+ {
+ // Reduce size of FF if inputs are just sign/zero extended or output bit is not used
+
+ SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
+ SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
+
+ int width_before = GetSize(sig_q);
+
+ if (width_before == 0)
+ return;
+
+ bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
+ bool sign_ext = !zero_ext;
+
+ for (int i = GetSize(sig_q)-1; i >= 0; i--)
+ {
+ if (zero_ext && sig_d[i] == State::S0) {
+ module->connect(sig_q[i], State::S0);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ continue;
+ }
+
+ if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) {
+ module->connect(sig_q[i], sig_q[i-1]);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ continue;
+ }
+
+ auto info = mi.query(sig_q[i]);
+ if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
+ sig_d.remove(i);
+ sig_q.remove(i);
+ zero_ext = false;
+ sign_ext = false;
+ continue;
+ }
+
+ break;
+ }
+
+ if (width_before == GetSize(sig_q))
+ return;
+
+ if (GetSize(sig_q) == 0) {
+ log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+ return;
+ }
+
+ log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
+ log_id(module), log_id(cell), log_id(cell->type));
+
+ for (auto bit : sig_d)
+ work_queue_bits.insert(bit);
+
+ for (auto bit : sig_q)
+ work_queue_bits.insert(bit);
+
+ // Narrow ARST_VALUE parameter to new size.
+ if (cell->parameters.count("\\ARST_VALUE")) {
+ Const arst_value = cell->getParam("\\ARST_VALUE");
+ arst_value.bits.resize(GetSize(sig_q));
+ cell->setParam("\\ARST_VALUE", arst_value);
+ }
+
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ cell->fixup_parameters();
+ }
+
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
{
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
@@ -176,6 +249,9 @@ struct WreduceWorker
if (cell->type.in("$mux", "$pmux"))
return run_cell_mux(cell);
+ if (cell->type.in("$dff", "$adff"))
+ return run_cell_dff(cell);
+
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
if (sig.has_const())
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
new file mode 100644
index 000000000..c9263057e
--- /dev/null
+++ b/passes/pmgen/.gitignore
@@ -0,0 +1 @@
+/ice40_dsp_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
new file mode 100644
index 000000000..33baaca30
--- /dev/null
+++ b/passes/pmgen/Makefile.inc
@@ -0,0 +1,8 @@
+OBJS += passes/pmgen/ice40_dsp.o
+
+passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
+EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
+.SECONDARY: passes/pmgen/ice40_dsp_pm.h
+
+passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
+ $(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
new file mode 100644
index 000000000..a1007dc62
--- /dev/null
+++ b/passes/pmgen/README.md
@@ -0,0 +1,224 @@
+Pattern Matcher Generator
+=========================
+
+The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
+writes a header-only C++ library that implements that pattern matcher.
+
+The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
+
+The algorithm used in the generated pattern matcher is a simple recursive
+search with backtracking. It is left to the author of the `.pmg` file to
+determine an efficient cell order for the search that allows for maximum
+use of indices and early backtracking.
+
+
+API of Generated Matcher
+========================
+
+When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
+a class `foobar_pm`. That class is instanciated with an RTLIL module and a
+list of cells from that module:
+
+ foobar_pm pm(module, module->selected_cells());
+
+The caller must make sure that none of the cells in the 2nd argument are
+deleted for as long as the patter matcher instance is used.
+
+At any time it is possible to disable cells, preventing them from showing
+up in any future matches:
+
+ pm.blacklist(some_cell);
+
+The `.run(callback_function)` method searches for all matches and calls the
+callback function for each found match:
+
+ pm.run([&](){
+ log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
+ log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
+ });
+
+The `.pmg` file declares matcher state variables that are accessible via the
+`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
+
+Similarly the `.pmg` file declares user data variables that become members of
+`.ud`, a struct of type `foobar_pm::udata_t`.
+
+
+The .pmg File Format
+====================
+
+The `.pmg` file format is a simple line-based file format. For the most part
+lines consist of whitespace-separated tokens.
+
+Lines in `.pmg` files starting with `//` are comments.
+
+Declaring state variables
+-------------------------
+
+One or more state variables can be declared using the `state` statement,
+followed by a C++ type in angle brackets, followed by a whitespace separated
+list of variable names. For example:
+
+ state <bool> flag1 flag2 happy big
+ state <SigSpec> sigA sigB sigY
+
+State variables are automatically managed by the generated backtracking algorithm
+and saved and restored as needed.
+
+They are automatically initialized to the default constructed value of their type
+when `.run(callback_function)` is called.
+
+Declaring udata variables
+-------------------------
+
+Udata (user-data) variables can be used for example to configure the matcher or
+the callback function used to perform actions on found matches.
+
+There is no automatic management of udata variables. For this reason it is
+recommended that the user-supplied matcher code treats them as read-only
+variables.
+
+They are declared like state variables, just using the `udata` statement:
+
+ udata <int> min_data_width max_data_width
+ udata <IdString> data_port_name
+
+They are atomatically initialzed to the default constructed value of their type
+when ther pattern matcher object is constructed.
+
+Embedded C++ code
+-----------------
+
+Many statements in a `.pmg` file contain C++ code. However, there are some
+slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
+write matchers:
+
+- Identifiers starting with a dollar sign or backslash are automatically
+ converted to special IdString variables that are initialized when the
+ matcher object is constructed.
+
+- The `port(<cell>, <portname>)` function is a handy alias for
+ `sigmap(<cell>->getPort(<portname>))`.
+
+- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
+
+- The function `nusers(<sigspec>)` returns the number of different cells
+ connected to any of the given signal bits, plus one if any of the signal
+ bits is also a primary input or primary output.
+
+- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
+ statements.
+
+- In `index` statements there is a special `===` operator for the index
+ lookup.
+
+Matching cells
+--------------
+
+Cells are matched using `match..endmatch` blocks. For example:
+
+ match mul
+ if ff
+ select mul->type == $mul
+ select nusers(port(mul, \Y) == 2
+ index <SigSpec> port(mul, \Y) === port(ff, \D)
+ filter some_weird_function(mul) < other_weird_function(ff)
+ optional
+ endmatch
+
+A `match` block starts with `match <statevar>` and implicitly generates
+a state variable `<statevar>` of type `RTLIL::Cell*`.
+
+All statements in the match block are optional. (An empty match block
+would simply match each and every cell in the module.)
+
+The `if <expression>` statement makes the match block conditional. If
+`<expression>` evaluates to `false` then the match block will be ignored
+and the corresponding state variable is set to `nullptr`. In our example
+we only try to match the `mul` cell if the `ff` state variable points
+to a cell. (Presumably `ff` is provided by a prior `match` block.)
+
+The `select` lines are evaluated once for each cell when the matcher is
+initialized. A `match` block will only consider cells for which all `select`
+expressions evaluated to `true`. Note that the state variable corresponding to
+the match (in the example `mul`) is the only state variable that may be used
+`select` lines.
+
+Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
+evaluated during matcher initialization and the same restrictions apply as for
+`select` expressions. `expr2` is evaluated when the match is calulated. It is a
+function of any state variables assigned to by previous blocks. Both expression
+are converted to the given type and compared for equality. Only cells for which
+all `index` statements in the block pass are considered by the match.
+
+Note that `select` and `index` are fast operations. Thus `select` and `index`
+should be used whenever possible to create efficient matchers.
+
+Finally, `filter <expression>` narrows down the remaining list of cells. For
+performance reasons `filter` statements should only be used for things that
+can't be done using `select` and `index`.
+
+The `optional` statement marks optional matches. I.e. the matcher will also
+explore the case where `mul` is set to `nullptr`. Without the `optional`
+statement a match may only be assigned nullptr when one of the `if` expressions
+evaluates to `false`.
+
+Additional code
+---------------
+
+Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
+Such a block starts with the keyword `code` followed by a list of state variables
+that the block may modify. For example:
+
+ code addAB sigS
+ if (addA) {
+ addAB = addA;
+ sigS = port(addA, \B);
+ }
+ if (addB) {
+ addAB = addB;
+ sigS = port(addB, \A);
+ }
+ endcode
+
+The special keyword `reject` can be used to reject the current state and
+backtrack. For example:
+
+ code
+ if (ffA && ffB) {
+ if (port(ffA, \CLK) != port(ffB, \CLK))
+ reject;
+ if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
+ reject;
+ }
+ endcode
+
+Similarly, the special keyword `accept` can be used to accept the current
+state. (`accept` will not backtrack. This means it continues with the current
+branch and may accept a larger match later.)
+
+The special keyword `branch` can be used to explore different cases. Note that
+each code block has an implicit `branch` at the end. So most use-cases of the
+`branch` keyword need to end the block with `reject` to avoid the implicit
+branch at the end. For example:
+
+ state <int> mode
+
+ code mode
+ for (mode = 0; mode < 8; mode++)
+ branch;
+ reject;
+ endcode
+
+But in some cases it is more natural to utilize the implicit branch statement:
+
+ state <IdString> portAB
+
+ code portAB
+ portAB = \A;
+ branch;
+ portAB = \B;
+ endcode
+
+There is an implicit `code..endcode` block at the end of each `.pgm` file
+that just accepts everything that gets all the way there.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
new file mode 100644
index 000000000..3a054a463
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.cc
@@ -0,0 +1,237 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "passes/pmgen/ice40_dsp_pm.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void create_ice40_dsp(ice40_dsp_pm &pm)
+{
+#if 0
+ log("\n");
+ log("ffA: %s\n", log_id(pm.st.ffA, "--"));
+ log("ffB: %s\n", log_id(pm.st.ffB, "--"));
+ log("mul: %s\n", log_id(pm.st.mul, "--"));
+ log("ffY: %s\n", log_id(pm.st.ffY, "--"));
+ log("addAB: %s\n", log_id(pm.st.addAB, "--"));
+ log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
+ log("ffS: %s\n", log_id(pm.st.ffS, "--"));
+#endif
+
+ log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
+
+ if (GetSize(pm.st.sigA) > 16) {
+ log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
+ return;
+ }
+
+ if (GetSize(pm.st.sigB) > 16) {
+ log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
+ return;
+ }
+
+ if (GetSize(pm.st.sigS) > 32) {
+ log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
+ return;
+ }
+
+ if (GetSize(pm.st.sigY) > 32) {
+ log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
+ return;
+ }
+
+ bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+
+ if (mul_signed) {
+ log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
+ return;
+ }
+
+ log(" replacing $mul with SB_MAC16 cell.\n");
+
+ Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
+ pm.module->swap_names(cell, pm.st.mul);
+
+ // SB_MAC16 Input Interface
+
+ SigSpec A = pm.st.sigA;
+ A.extend_u0(16, mul_signed);
+
+ SigSpec B = pm.st.sigB;
+ B.extend_u0(16, mul_signed);
+
+ SigSpec CD;
+ if (pm.st.muxA)
+ CD = pm.st.muxA->getPort("\\B");
+ if (pm.st.muxB)
+ CD = pm.st.muxB->getPort("\\A");
+ CD.extend_u0(32, mul_signed);
+
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\C", CD.extract(0, 16));
+ cell->setPort("\\D", CD.extract(16, 16));
+
+ cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
+ cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
+
+ cell->setPort("\\AHOLD", State::S0);
+ cell->setPort("\\BHOLD", State::S0);
+ cell->setPort("\\CHOLD", State::S0);
+ cell->setPort("\\DHOLD", State::S0);
+
+ cell->setPort("\\IRSTTOP", State::S0);
+ cell->setPort("\\IRSTBOT", State::S0);
+
+ if (pm.st.clock_vld)
+ {
+ cell->setPort("\\CLK", pm.st.clock);
+ cell->setPort("\\CE", State::S1);
+ cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+
+ log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+
+ if (pm.st.ffA)
+ log(" ffA:%s", log_id(pm.st.ffA));
+
+ if (pm.st.ffB)
+ log(" ffB:%s", log_id(pm.st.ffB));
+
+ if (pm.st.ffY)
+ log(" ffY:%s", log_id(pm.st.ffY));
+
+ if (pm.st.ffS)
+ log(" ffS:%s", log_id(pm.st.ffS));
+
+ log("\n");
+ }
+ else
+ {
+ cell->setPort("\\CLK", State::S0);
+ cell->setPort("\\CE", State::S0);
+ cell->setParam("\\NEG_TRIGGER", State::S0);
+ }
+
+ // SB_MAC16 Cascade Interface
+
+ cell->setPort("\\SIGNEXTIN", State::Sx);
+ cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
+
+ cell->setPort("\\CI", State::Sx);
+ cell->setPort("\\CO", pm.module->addWire(NEW_ID));
+
+ cell->setPort("\\ACCUMCI", State::Sx);
+ cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
+
+ // SB_MAC16 Output Interface
+
+ SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
+ if (GetSize(O) < 32)
+ O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
+
+ cell->setPort("\\O", O);
+
+ if (pm.st.addAB) {
+ log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
+ cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+ cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+ } else {
+ cell->setPort("\\ADDSUBTOP", State::S0);
+ cell->setPort("\\ADDSUBBOT", State::S0);
+ }
+
+ cell->setPort("\\ORSTTOP", State::S0);
+ cell->setPort("\\ORSTBOT", State::S0);
+
+ cell->setPort("\\OHOLDTOP", State::S0);
+ cell->setPort("\\OHOLDBOT", State::S0);
+
+ SigSpec acc_reset = State::S0;
+ if (pm.st.muxA)
+ acc_reset = pm.st.muxA->getPort("\\S");
+ if (pm.st.muxB)
+ acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
+
+ cell->setPort("\\OLOADTOP", acc_reset);
+ cell->setPort("\\OLOADBOT", acc_reset);
+
+ // SB_MAC16 Remaining Parameters
+
+ cell->setParam("\\C_REG", State::S0);
+ cell->setParam("\\D_REG", State::S0);
+
+ cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
+
+ cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
+ cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
+ cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
+
+ cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
+ cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
+ cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
+
+ cell->setParam("\\MODE_8x8", State::S0);
+ cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
+ cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
+
+ pm.autoremove(pm.st.mul);
+ pm.autoremove(pm.st.ffY);
+ pm.autoremove(pm.st.ffS);
+}
+
+struct Ice40DspPass : public Pass {
+ Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ice40_dsp [options] [selection]\n");
+ log("\n");
+ log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
+ }
+} Ice40DspPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
new file mode 100644
index 000000000..96c62e313
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -0,0 +1,160 @@
+state <SigBit> clock
+state <bool> clock_pol clock_vld
+state <SigSpec> sigA sigB sigY sigS
+state <Cell*> addAB muxAB
+
+match mul
+ select mul->type.in($mul)
+ select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
+ select GetSize(mul->getPort(\Y)) > 10
+endmatch
+
+match ffA
+ select ffA->type.in($dff)
+ // select nusers(port(ffA, \Q)) == 2
+ index <SigSpec> port(ffA, \Q) === port(mul, \A)
+ optional
+endmatch
+
+code sigA clock clock_pol clock_vld
+ sigA = port(mul, \A);
+
+ if (ffA) {
+ sigA = port(ffA, \D);
+
+ clock = port(ffA, \CLK).as_bit();
+ clock_pol = param(ffA, \CLK_POLARITY).as_bool();
+ clock_vld = true;
+ }
+endcode
+
+match ffB
+ select ffB->type.in($dff)
+ // select nusers(port(ffB, \Q)) == 2
+ index <SigSpec> port(ffB, \Q) === port(mul, \B)
+ optional
+endmatch
+
+code sigB clock clock_pol clock_vld
+ sigB = port(mul, \B);
+
+ if (ffB) {
+ sigB = port(ffB, \D);
+ SigBit c = port(ffB, \CLK).as_bit();
+ bool cp = param(ffB, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
+
+match ffY
+ select ffY->type.in($dff)
+ select nusers(port(ffY, \D)) == 2
+ index <SigSpec> port(ffY, \D) === port(mul, \Y)
+ optional
+endmatch
+
+code sigY clock clock_pol clock_vld
+ sigY = port(mul, \Y);
+
+ if (ffY) {
+ sigY = port(ffY, \Q);
+ SigBit c = port(ffY, \CLK).as_bit();
+ bool cp = param(ffY, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
+
+match addA
+ select addA->type.in($add)
+ select nusers(port(addA, \A)) == 2
+ index <SigSpec> port(addA, \A) === sigY
+ optional
+endmatch
+
+match addB
+ if !addA
+ select addB->type.in($add, $sub)
+ select nusers(port(addB, \B)) == 2
+ index <SigSpec> port(addB, \B) === sigY
+ optional
+endmatch
+
+code addAB sigS
+ if (addA) {
+ addAB = addA;
+ sigS = port(addA, \B);
+ }
+ if (addB) {
+ addAB = addB;
+ sigS = port(addB, \A);
+ }
+ if (addAB) {
+ int natural_mul_width = GetSize(sigA) + GetSize(sigB);
+ int actual_mul_width = GetSize(sigY);
+ int actual_acc_width = GetSize(sigS);
+
+ if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
+ reject;
+ if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
+ reject;
+ }
+endcode
+
+match muxA
+ if addAB
+ select muxA->type.in($mux)
+ select nusers(port(muxA, \A)) == 2
+ index <SigSpec> port(muxA, \A) === port(addAB, \Y)
+ optional
+endmatch
+
+match muxB
+ if addAB
+ if !muxA
+ select muxB->type.in($mux)
+ select nusers(port(muxB, \B)) == 2
+ index <SigSpec> port(muxB, \B) === port(addAB, \Y)
+ optional
+endmatch
+
+code muxAB
+ muxAB = addAB;
+ if (muxA)
+ muxAB = muxA;
+ if (muxB)
+ muxAB = muxB;
+endcode
+
+match ffS
+ if muxAB
+ select ffS->type.in($dff)
+ select nusers(port(ffS, \D)) == 2
+ index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
+ index <SigSpec> port(ffS, \Q) === sigS
+endmatch
+
+code clock clock_pol clock_vld
+ if (ffS) {
+ SigBit c = port(ffS, \CLK).as_bit();
+ bool cp = param(ffS, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
new file mode 100644
index 000000000..e688a4567
--- /dev/null
+++ b/passes/pmgen/pmgen.py
@@ -0,0 +1,482 @@
+#!/usr/bin/env python3
+
+import re
+import sys
+import pprint
+
+pp = pprint.PrettyPrinter(indent=4)
+
+prefix = sys.argv[1]
+
+state_types = dict()
+udata_types = dict()
+blocks = list()
+ids = dict()
+
+def rewrite_cpp(s):
+ t = list()
+ i = 0
+ while i < len(s):
+ if s[i] in ("'", '"') and i + 1 < len(s):
+ j = i + 1
+ while j + 1 < len(s) and s[j] != s[i]:
+ if s[j] == '\\' and j + 1 < len(s):
+ j += 1
+ j += 1
+ t.append(s[i:j+1])
+ i = j + 1
+ continue
+
+ if s[i] in ('$', '\\') and i + 1 < len(s):
+ j = i + 1
+ while True:
+ if j == len(s):
+ j -= 1
+ break
+ if ord('a') <= ord(s[j]) <= ord('z'):
+ j += 1
+ continue
+ if ord('A') <= ord(s[j]) <= ord('Z'):
+ j += 1
+ continue
+ if ord('0') <= ord(s[j]) <= ord('9'):
+ j += 1
+ continue
+ if s[j] == '_':
+ j += 1
+ continue
+ j -= 1
+ break
+
+ n = s[i:j+1]
+ i = j + 1
+
+ if n[0] == '$':
+ v = "id_d_" + n[1:]
+ else:
+ v = "id_b_" + n[1:]
+
+ if v not in ids:
+ ids[v] = n
+ else:
+ assert ids[v] == n
+
+ t.append(v)
+ continue
+
+ if s[i] == "\t":
+ t.append(" ")
+ else:
+ t.append(s[i])
+
+ i += 1
+
+ return "".join(t)
+
+with open("%s.pmg" % prefix, "r") as f:
+ while True:
+ line = f.readline()
+ if line == "": break
+ line = line.strip()
+
+ cmd = line.split()
+ if len(cmd) == 0 or cmd[0].startswith("//"): continue
+ cmd = cmd[0]
+
+ if cmd == "state":
+ m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
+ assert m
+ type_str = m.group(1)
+ states_str = m.group(2)
+ for s in re.split(r"\s+", states_str):
+ assert s not in state_types
+ state_types[s] = type_str
+ continue
+
+ if cmd == "udata":
+ m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
+ assert m
+ type_str = m.group(1)
+ udatas_str = m.group(2)
+ for s in re.split(r"\s+", udatas_str):
+ assert s not in udata_types
+ udata_types[s] = type_str
+ continue
+
+ if cmd == "match":
+ block = dict()
+ block["type"] = "match"
+
+ line = line.split()
+ assert len(line) == 2
+ assert line[1] not in state_types
+ block["cell"] = line[1]
+ state_types[line[1]] = "Cell*";
+
+ block["if"] = list()
+ block["select"] = list()
+ block["index"] = list()
+ block["filter"] = list()
+ block["optional"] = False
+
+ while True:
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0 or a[0].startswith("//"): continue
+ if a[0] == "endmatch": break
+
+ if a[0] == "if":
+ b = l.lstrip()[2:]
+ block["if"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "select":
+ b = l.lstrip()[6:]
+ block["select"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "index":
+ m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
+ assert m
+ block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
+ continue
+
+ if a[0] == "filter":
+ b = l.lstrip()[6:]
+ block["filter"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "optional":
+ block["optional"] = True
+ continue
+
+ assert False
+
+ blocks.append(block)
+
+ if cmd == "code":
+ block = dict()
+ block["type"] = "code"
+ block["code"] = list()
+ block["states"] = set()
+
+ for s in line.split()[1:]:
+ assert s in state_types
+ block["states"].add(s)
+
+ while True:
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0: continue
+ if a[0] == "endcode": break
+
+ block["code"].append(rewrite_cpp(l.rstrip()))
+
+ blocks.append(block)
+
+with open("%s_pm.h" % prefix, "w") as f:
+ print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
+ print("", file=f)
+
+ print("#include \"kernel/yosys.h\"", file=f)
+ print("#include \"kernel/sigtools.h\"", file=f)
+ print("", file=f)
+
+ print("YOSYS_NAMESPACE_BEGIN", file=f)
+ print("", file=f)
+
+ print("struct {}_pm {{".format(prefix), file=f)
+ print(" Module *module;", file=f)
+ print(" SigMap sigmap;", file=f)
+ print(" std::function<void()> on_accept;".format(prefix), file=f)
+ print("", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ index_types = list()
+ for entry in block["index"]:
+ index_types.append(entry[0])
+ print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
+ print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
+ print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
+ print(" pool<Cell*> blacklist_cells;", file=f)
+ print(" pool<Cell*> autoremove_cells;", file=f)
+ print(" bool blacklist_dirty;", file=f)
+ print(" int rollback;", file=f)
+ print("", file=f)
+
+ print(" struct state_t {", file=f)
+ for s, t in sorted(state_types.items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" } st;", file=f)
+ print("", file=f)
+
+ print(" struct udata_t {", file=f)
+ for s, t in sorted(udata_types.items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" } ud;", file=f)
+ print("", file=f)
+
+ for v, n in sorted(ids.items()):
+ if n[0] == "\\":
+ print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f)
+ else:
+ print(" IdString {}{{\"{}\"}};".format(v, n), file=f)
+ print("", file=f)
+
+ print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
+ print(" for (auto bit : sigmap(sig)) {", file=f)
+ print(" if (bit.wire == nullptr) continue;", file=f)
+ print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
+ print(" sigusers[bit].insert(nullptr);", file=f)
+ print(" sigusers[bit].insert(cell);", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void blacklist(Cell *cell) {", file=f)
+ print(" if (cell != nullptr) {", file=f)
+ print(" if (blacklist_cells.insert(cell).second)", file=f)
+ print(" blacklist_dirty = true;", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void autoremove(Cell *cell) {", file=f)
+ print(" if (cell != nullptr) {", file=f)
+ print(" if (blacklist_cells.insert(cell).second)", file=f)
+ print(" blacklist_dirty = true;", file=f)
+ print(" autoremove_cells.insert(cell);", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void check_blacklist() {", file=f)
+ print(" if (!blacklist_dirty)", file=f)
+ print(" return;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
+ print(" rollback = {};".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
+ print(" return sigmap(cell->getPort(portname));", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" Const param(Cell *cell, IdString paramname) {", file=f)
+ print(" return cell->getParam(paramname);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" int nusers(const SigSpec &sig) {", file=f)
+ print(" pool<Cell*> users;", file=f)
+ print(" for (auto bit : sigmap(sig))", file=f)
+ print(" for (auto user : sigusers[bit])", file=f)
+ print(" users.insert(user);", file=f)
+ print(" return GetSize(users);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
+ print(" module(module), sigmap(module) {", file=f)
+ for s, t in sorted(udata_types.items()):
+ if t.endswith("*"):
+ print(" ud.{} = nullptr;".format(s), file=f)
+ else:
+ print(" ud.{} = {}();".format(s, t), file=f)
+ print(" for (auto cell : module->cells()) {", file=f)
+ print(" for (auto &conn : cell->connections())", file=f)
+ print(" add_siguser(conn.second, cell);", file=f)
+ print(" }", file=f)
+ print(" for (auto cell : cells) {", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ print(" do {", file=f)
+ print(" Cell *{} = cell;".format(block["cell"]), file=f)
+ for expr in block["select"]:
+ print(" if (!({})) break;".format(expr), file=f)
+ print(" index_{}_key_type key;".format(index), file=f)
+ for field, entry in enumerate(block["index"]):
+ print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
+ print(" index_{}[key].push_back(cell);".format(index), file=f)
+ print(" } while (0);", file=f)
+
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" ~{}_pm() {{".format(prefix), file=f)
+ print(" for (auto cell : autoremove_cells)", file=f)
+ print(" module->remove(cell);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void run(std::function<void()> on_accept_f) {", file=f)
+ print(" on_accept = on_accept_f;", file=f)
+ print(" rollback = 0;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for s, t in sorted(state_types.items()):
+ if t.endswith("*"):
+ print(" st.{} = nullptr;".format(s), file=f)
+ else:
+ print(" st.{} = {}();".format(s, t), file=f)
+ print(" block_0();", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
+ print(" run([&](){on_accept_f(*this);});", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+
+ print(" void block_{}() {{".format(index), file=f)
+
+ const_st = set()
+ nonconst_st = set()
+ restore_st = set()
+
+ for i in range(index):
+ if blocks[i]["type"] == "code":
+ for s in blocks[i]["states"]:
+ const_st.add(s)
+ elif blocks[i]["type"] == "match":
+ const_st.add(blocks[i]["cell"])
+ else:
+ assert False
+
+ if block["type"] == "code":
+ for s in block["states"]:
+ if s in const_st:
+ const_st.remove(s)
+ restore_st.add(s)
+ nonconst_st.add(s)
+ elif block["type"] == "match":
+ s = block["cell"]
+ assert s not in const_st
+ nonconst_st.add(s)
+ else:
+ assert False
+
+ for s in sorted(const_st):
+ t = state_types[s]
+ if t.endswith("*"):
+ print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ else:
+ print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+
+ for s in sorted(nonconst_st):
+ t = state_types[s]
+ print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+
+ if len(restore_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[s]
+ print(" {} backup_{} = {};".format(t, s, s), file=f)
+
+ if block["type"] == "code":
+ print("", file=f)
+ print(" do {", file=f)
+ print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
+ print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+ print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
+
+ for line in block["code"]:
+ print(" " + line, file=f)
+
+ print("", file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print("#undef reject", file=f)
+ print("#undef accept", file=f)
+ print("#undef branch", file=f)
+ print(" } while (0);", file=f)
+ print("", file=f)
+ print("rollback_label:", file=f)
+ print(" YS_ATTRIBUTE(unused);", file=f)
+
+ if len(restore_st) or len(nonconst_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[s]
+ print(" {} = backup_{};".format(s, s), file=f)
+ for s in sorted(nonconst_st):
+ if s not in restore_st:
+ t = state_types[s]
+ if t.endswith("*"):
+ print(" {} = nullptr;".format(s), file=f)
+ else:
+ print(" {} = {}();".format(s, t), file=f)
+
+ elif block["type"] == "match":
+ assert len(restore_st) == 0
+
+ if len(block["if"]):
+ for expr in block["if"]:
+ print("", file=f)
+ print(" if (!({})) {{".format(expr), file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+
+ print("", file=f)
+ print(" index_{}_key_type key;".format(index), file=f)
+ for field, entry in enumerate(block["index"]):
+ print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
+ print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
+
+ print("", file=f)
+ print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
+ print(" {} = cells[idx];".format(block["cell"]), file=f)
+ print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
+ for expr in block["filter"]:
+ print(" if (!({})) continue;".format(expr), file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print(" if (rollback) {", file=f)
+ print(" if (rollback != {}) {{".format(index+1), file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+
+ print("", file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+
+ if block["optional"]:
+ print(" block_{}();".format(index+1), file=f)
+
+ else:
+ assert False
+
+
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void block_{}() {{".format(len(blocks)), file=f)
+ print(" on_accept();", file=f)
+ print(" check_blacklist();", file=f)
+ print(" }", file=f)
+ print("};", file=f)
+
+ print("", file=f)
+ print("YOSYS_NAMESPACE_END", file=f)
+
+# pp.pprint(blocks)
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index d2d15a4a9..21b70f492 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -327,8 +327,26 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
}
}
-std::string remap_name(RTLIL::IdString abc_name)
+std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
{
+ std::string abc_sname = abc_name.substr(1);
+ if (abc_sname.substr(0, 5) == "ys__n") {
+ int sid = std::stoi(abc_sname.substr(5));
+ bool inv = abc_sname.back() == 'v';
+ for (auto sig : signal_list) {
+ if (sig.id == sid && sig.bit.wire != nullptr) {
+ std::stringstream sstr;
+ sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
+ if (sig.bit.wire->width != 1)
+ sstr << "[" << sig.bit.offset << "]";
+ if (inv)
+ sstr << "_inv";
+ if (orig_wire != nullptr)
+ *orig_wire = sig.bit.wire;
+ return sstr.str();
+ }
+ }
+ }
std::stringstream sstr;
sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
return sstr.str();
@@ -353,12 +371,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std:
}
for (auto n : nodes)
- fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
+ fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
for (auto &e : edges)
for (auto n : e.second)
- fprintf(f, " n%d -> n%d;\n", e.first, n);
+ fprintf(f, " ys__n%d -> ys__n%d;\n", e.first, n);
fprintf(f, "}\n");
}
@@ -624,7 +642,7 @@ struct abc_output_filter
void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode,
- const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
+ const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress)
{
module = current_module;
map_autoidx = autoidx++;
@@ -728,7 +746,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
-
+ if (abc_dress)
+ abc_script += "; dress";
abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
abc_script = add_echos_to_abc_cmd(abc_script);
@@ -784,7 +803,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list) {
if (!si.is_port || si.type != G(NONE))
continue;
- fprintf(f, " n%d", si.id);
+ fprintf(f, " ys__n%d", si.id);
pi_map[count_input++] = log_signal(si.bit);
}
if (count_input == 0)
@@ -796,17 +815,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list) {
if (!si.is_port || si.type == G(NONE))
continue;
- fprintf(f, " n%d", si.id);
+ fprintf(f, " ys__n%d", si.id);
po_map[count_output++] = log_signal(si.bit);
}
fprintf(f, "\n");
for (auto &si : signal_list)
- fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit));
+ fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));
for (auto &si : signal_list) {
if (si.bit.wire == NULL) {
- fprintf(f, ".names n%d\n", si.id);
+ fprintf(f, ".names ys__n%d\n", si.id);
if (si.bit == RTLIL::State::S1)
fprintf(f, "1\n");
}
@@ -815,68 +834,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
int count_gates = 0;
for (auto &si : signal_list) {
if (si.type == G(BUF)) {
- fprintf(f, ".names n%d n%d\n", si.in1, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
fprintf(f, "1 1\n");
} else if (si.type == G(NOT)) {
- fprintf(f, ".names n%d n%d\n", si.in1, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
fprintf(f, "0 1\n");
} else if (si.type == G(AND)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "11 1\n");
} else if (si.type == G(NAND)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "0- 1\n");
fprintf(f, "-0 1\n");
} else if (si.type == G(OR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "-1 1\n");
fprintf(f, "1- 1\n");
} else if (si.type == G(NOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "00 1\n");
} else if (si.type == G(XOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "01 1\n");
fprintf(f, "10 1\n");
} else if (si.type == G(XNOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "00 1\n");
fprintf(f, "11 1\n");
} else if (si.type == G(ANDNOT)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "10 1\n");
} else if (si.type == G(ORNOT)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "1- 1\n");
fprintf(f, "-0 1\n");
} else if (si.type == G(MUX)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "1-0 1\n");
fprintf(f, "-11 1\n");
} else if (si.type == G(AOI3)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "-00 1\n");
fprintf(f, "0-0 1\n");
} else if (si.type == G(OAI3)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "00- 1\n");
fprintf(f, "--0 1\n");
} else if (si.type == G(AOI4)) {
- fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
fprintf(f, "-0-0 1\n");
fprintf(f, "-00- 1\n");
fprintf(f, "0--0 1\n");
fprintf(f, "0-0- 1\n");
} else if (si.type == G(OAI4)) {
- fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
fprintf(f, "00-- 1\n");
fprintf(f, "--00 1\n");
} else if (si.type == G(FF)) {
if (si.init == State::S0 || si.init == State::S1) {
- fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
+ fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
recover_init = true;
} else
- fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id);
+ fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id);
} else if (si.type != G(NONE))
log_abort();
if (si.type != G(NONE))
@@ -889,7 +908,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
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);
log_push();
-
if (count_output > 0)
{
log_header(design, "Executing ABC.\n");
@@ -988,7 +1006,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
log_error("ABC output file does not contain a module `netlist'.\n");
for (auto &it : mapped_mod->wires_) {
RTLIL::Wire *w = it.second;
- RTLIL::Wire *wire = module->addWire(remap_name(w->name));
+ RTLIL::Wire *orig_wire = nullptr;
+ RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
+ if (orig_wire != nullptr && orig_wire->attributes.count("\\src"))
+ wire->attributes["\\src"] = orig_wire->attributes["\\src"];
if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
design->select(module, wire);
}
@@ -1213,7 +1234,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list)
if (si.is_port) {
char buffer[100];
- snprintf(buffer, 100, "\\n%d", si.id);
+ snprintf(buffer, 100, "\\ys__n%d", si.id);
RTLIL::SigSig conn;
if (si.type != G(NONE)) {
conn.first = si.bit;
@@ -1407,6 +1428,11 @@ struct AbcPass : public Pass {
log(" this attribute is a unique integer for each ABC process started. This\n");
log(" is useful for debugging the partitioning of clock domains.\n");
log("\n");
+ log(" -dress\n");
+ log(" run the 'dress' command after all other ABC commands. This aims to\n");
+ log(" preserve naming by an equivalence check between the original and post-ABC\n");
+ log(" netlists (experimental).\n");
+ log("\n");
log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n");
log("loaded into ABC before the ABC script is executed.\n");
log("\n");
@@ -1441,6 +1467,7 @@ struct AbcPass : public Pass {
std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
bool show_tempdir = false, sop_mode = false;
+ bool abc_dress = false;
vector<int> lut_costs;
markgroups = false;
@@ -1555,6 +1582,10 @@ struct AbcPass : public Pass {
map_mux16 = true;
continue;
}
+ if (arg == "-dress") {
+ abc_dress = true;
+ continue;
+ }
if (arg == "-g" && argidx+1 < args.size()) {
for (auto g : split_tokens(args[++argidx], ",")) {
vector<string> gate_list;
@@ -1704,7 +1735,7 @@ struct AbcPass : public Pass {
if (!dff_mode || !clk_str.empty()) {
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
- delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
+ delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress);
continue;
}
@@ -1849,7 +1880,7 @@ struct AbcPass : public Pass {
en_polarity = std::get<2>(it.first);
en_sig = assign_map(std::get<3>(it.first));
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$",
- keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode);
+ keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress);
assign_map.set(mod);
}
}