mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-03 18:00:24 +00:00
Merge remote-tracking branch 'origin/master' into xaig
This commit is contained in:
commit
a8803a1519
16 changed files with 1874 additions and 63 deletions
|
@ -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 ¶meter : cell->parameters) {
|
||||
for (auto &bit : parameter.second.bits) {
|
||||
if (bit > RTLIL::State::S1)
|
||||
bit = worker.next_bit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (undriven_mode)
|
||||
{
|
||||
if (!module->processes.empty())
|
||||
|
|
|
@ -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,71 @@ 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);
|
||||
|
||||
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 +242,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())
|
||||
|
|
1
passes/pmgen/.gitignore
vendored
Normal file
1
passes/pmgen/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/ice40_dsp_pm.h
|
8
passes/pmgen/Makefile.inc
Normal file
8
passes/pmgen/Makefile.inc
Normal file
|
@ -0,0 +1,8 @@
|
|||
OBJS += passes/pmgen/ice40_dsp.o
|
||||
|
||||
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
|
||||
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
|
||||
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
|
||||
|
||||
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
|
||||
$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
|
224
passes/pmgen/README.md
Normal file
224
passes/pmgen/README.md
Normal file
|
@ -0,0 +1,224 @@
|
|||
Pattern Matcher Generator
|
||||
=========================
|
||||
|
||||
The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
|
||||
writes a header-only C++ library that implements that pattern matcher.
|
||||
|
||||
The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
|
||||
|
||||
The algorithm used in the generated pattern matcher is a simple recursive
|
||||
search with backtracking. It is left to the author of the `.pmg` file to
|
||||
determine an efficient cell order for the search that allows for maximum
|
||||
use of indices and early backtracking.
|
||||
|
||||
|
||||
API of Generated Matcher
|
||||
========================
|
||||
|
||||
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
|
||||
a class `foobar_pm`. That class is 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.
|
237
passes/pmgen/ice40_dsp.cc
Normal file
237
passes/pmgen/ice40_dsp.cc
Normal file
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||
{
|
||||
#if 0
|
||||
log("\n");
|
||||
log("ffA: %s\n", log_id(pm.st.ffA, "--"));
|
||||
log("ffB: %s\n", log_id(pm.st.ffB, "--"));
|
||||
log("mul: %s\n", log_id(pm.st.mul, "--"));
|
||||
log("ffY: %s\n", log_id(pm.st.ffY, "--"));
|
||||
log("addAB: %s\n", log_id(pm.st.addAB, "--"));
|
||||
log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
|
||||
log("ffS: %s\n", log_id(pm.st.ffS, "--"));
|
||||
#endif
|
||||
|
||||
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
|
||||
|
||||
if (GetSize(pm.st.sigA) > 16) {
|
||||
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigB) > 16) {
|
||||
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigS) > 32) {
|
||||
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigY) > 32) {
|
||||
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
|
||||
return;
|
||||
}
|
||||
|
||||
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
|
||||
|
||||
if (mul_signed) {
|
||||
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log(" replacing $mul with SB_MAC16 cell.\n");
|
||||
|
||||
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
|
||||
pm.module->swap_names(cell, pm.st.mul);
|
||||
|
||||
// SB_MAC16 Input Interface
|
||||
|
||||
SigSpec A = pm.st.sigA;
|
||||
A.extend_u0(16, mul_signed);
|
||||
|
||||
SigSpec B = pm.st.sigB;
|
||||
B.extend_u0(16, mul_signed);
|
||||
|
||||
SigSpec CD;
|
||||
if (pm.st.muxA)
|
||||
CD = pm.st.muxA->getPort("\\B");
|
||||
if (pm.st.muxB)
|
||||
CD = pm.st.muxB->getPort("\\A");
|
||||
CD.extend_u0(32, mul_signed);
|
||||
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\C", CD.extract(0, 16));
|
||||
cell->setPort("\\D", CD.extract(16, 16));
|
||||
|
||||
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
|
||||
|
||||
cell->setPort("\\AHOLD", State::S0);
|
||||
cell->setPort("\\BHOLD", State::S0);
|
||||
cell->setPort("\\CHOLD", State::S0);
|
||||
cell->setPort("\\DHOLD", State::S0);
|
||||
|
||||
cell->setPort("\\IRSTTOP", State::S0);
|
||||
cell->setPort("\\IRSTBOT", State::S0);
|
||||
|
||||
if (pm.st.clock_vld)
|
||||
{
|
||||
cell->setPort("\\CLK", pm.st.clock);
|
||||
cell->setPort("\\CE", State::S1);
|
||||
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
|
||||
|
||||
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
|
||||
|
||||
if (pm.st.ffA)
|
||||
log(" ffA:%s", log_id(pm.st.ffA));
|
||||
|
||||
if (pm.st.ffB)
|
||||
log(" ffB:%s", log_id(pm.st.ffB));
|
||||
|
||||
if (pm.st.ffY)
|
||||
log(" ffY:%s", log_id(pm.st.ffY));
|
||||
|
||||
if (pm.st.ffS)
|
||||
log(" ffS:%s", log_id(pm.st.ffS));
|
||||
|
||||
log("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
cell->setPort("\\CLK", State::S0);
|
||||
cell->setPort("\\CE", State::S0);
|
||||
cell->setParam("\\NEG_TRIGGER", State::S0);
|
||||
}
|
||||
|
||||
// SB_MAC16 Cascade Interface
|
||||
|
||||
cell->setPort("\\SIGNEXTIN", State::Sx);
|
||||
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\CI", State::Sx);
|
||||
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\ACCUMCI", State::Sx);
|
||||
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
|
||||
|
||||
// SB_MAC16 Output Interface
|
||||
|
||||
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
|
||||
if (GetSize(O) < 32)
|
||||
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
||||
|
||||
cell->setPort("\\O", O);
|
||||
|
||||
if (pm.st.addAB) {
|
||||
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
|
||||
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
} else {
|
||||
cell->setPort("\\ADDSUBTOP", State::S0);
|
||||
cell->setPort("\\ADDSUBBOT", State::S0);
|
||||
}
|
||||
|
||||
cell->setPort("\\ORSTTOP", State::S0);
|
||||
cell->setPort("\\ORSTBOT", State::S0);
|
||||
|
||||
cell->setPort("\\OHOLDTOP", State::S0);
|
||||
cell->setPort("\\OHOLDBOT", State::S0);
|
||||
|
||||
SigSpec acc_reset = State::S0;
|
||||
if (pm.st.muxA)
|
||||
acc_reset = pm.st.muxA->getPort("\\S");
|
||||
if (pm.st.muxB)
|
||||
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
|
||||
|
||||
cell->setPort("\\OLOADTOP", acc_reset);
|
||||
cell->setPort("\\OLOADBOT", acc_reset);
|
||||
|
||||
// SB_MAC16 Remaining Parameters
|
||||
|
||||
cell->setParam("\\C_REG", State::S0);
|
||||
cell->setParam("\\D_REG", State::S0);
|
||||
|
||||
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
|
||||
|
||||
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
|
||||
|
||||
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
|
||||
|
||||
cell->setParam("\\MODE_8x8", State::S0);
|
||||
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
|
||||
pm.autoremove(pm.st.mul);
|
||||
pm.autoremove(pm.st.ffY);
|
||||
pm.autoremove(pm.st.ffS);
|
||||
}
|
||||
|
||||
struct Ice40DspPass : public Pass {
|
||||
Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ice40_dsp [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-singleton") {
|
||||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
|
||||
}
|
||||
} Ice40DspPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
160
passes/pmgen/ice40_dsp.pmg
Normal file
160
passes/pmgen/ice40_dsp.pmg
Normal file
|
@ -0,0 +1,160 @@
|
|||
state <SigBit> clock
|
||||
state <bool> clock_pol clock_vld
|
||||
state <SigSpec> sigA sigB sigY sigS
|
||||
state <Cell*> addAB muxAB
|
||||
|
||||
match mul
|
||||
select mul->type.in($mul)
|
||||
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||
select GetSize(mul->getPort(\Y)) > 10
|
||||
endmatch
|
||||
|
||||
match ffA
|
||||
select ffA->type.in($dff)
|
||||
// select nusers(port(ffA, \Q)) == 2
|
||||
index <SigSpec> port(ffA, \Q) === port(mul, \A)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigA clock clock_pol clock_vld
|
||||
sigA = port(mul, \A);
|
||||
|
||||
if (ffA) {
|
||||
sigA = port(ffA, \D);
|
||||
|
||||
clock = port(ffA, \CLK).as_bit();
|
||||
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffB
|
||||
select ffB->type.in($dff)
|
||||
// select nusers(port(ffB, \Q)) == 2
|
||||
index <SigSpec> port(ffB, \Q) === port(mul, \B)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigB clock clock_pol clock_vld
|
||||
sigB = port(mul, \B);
|
||||
|
||||
if (ffB) {
|
||||
sigB = port(ffB, \D);
|
||||
SigBit c = port(ffB, \CLK).as_bit();
|
||||
bool cp = param(ffB, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffY
|
||||
select ffY->type.in($dff)
|
||||
select nusers(port(ffY, \D)) == 2
|
||||
index <SigSpec> port(ffY, \D) === port(mul, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigY clock clock_pol clock_vld
|
||||
sigY = port(mul, \Y);
|
||||
|
||||
if (ffY) {
|
||||
sigY = port(ffY, \Q);
|
||||
SigBit c = port(ffY, \CLK).as_bit();
|
||||
bool cp = param(ffY, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match addA
|
||||
select addA->type.in($add)
|
||||
select nusers(port(addA, \A)) == 2
|
||||
index <SigSpec> port(addA, \A) === sigY
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match addB
|
||||
if !addA
|
||||
select addB->type.in($add, $sub)
|
||||
select nusers(port(addB, \B)) == 2
|
||||
index <SigSpec> port(addB, \B) === sigY
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code addAB sigS
|
||||
if (addA) {
|
||||
addAB = addA;
|
||||
sigS = port(addA, \B);
|
||||
}
|
||||
if (addB) {
|
||||
addAB = addB;
|
||||
sigS = port(addB, \A);
|
||||
}
|
||||
if (addAB) {
|
||||
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||
int actual_mul_width = GetSize(sigY);
|
||||
int actual_acc_width = GetSize(sigS);
|
||||
|
||||
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||
reject;
|
||||
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match muxA
|
||||
if addAB
|
||||
select muxA->type.in($mux)
|
||||
select nusers(port(muxA, \A)) == 2
|
||||
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match muxB
|
||||
if addAB
|
||||
if !muxA
|
||||
select muxB->type.in($mux)
|
||||
select nusers(port(muxB, \B)) == 2
|
||||
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code muxAB
|
||||
muxAB = addAB;
|
||||
if (muxA)
|
||||
muxAB = muxA;
|
||||
if (muxB)
|
||||
muxAB = muxB;
|
||||
endcode
|
||||
|
||||
match ffS
|
||||
if muxAB
|
||||
select ffS->type.in($dff)
|
||||
select nusers(port(ffS, \D)) == 2
|
||||
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
|
||||
index <SigSpec> port(ffS, \Q) === sigS
|
||||
endmatch
|
||||
|
||||
code clock clock_pol clock_vld
|
||||
if (ffS) {
|
||||
SigBit c = port(ffS, \CLK).as_bit();
|
||||
bool cp = param(ffS, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
482
passes/pmgen/pmgen.py
Normal file
482
passes/pmgen/pmgen.py
Normal file
|
@ -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)
|
Loading…
Add table
Add a link
Reference in a new issue