3
0
Fork 0
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:
Eddie Hung 2019-02-21 11:23:00 -08:00
commit a8803a1519
16 changed files with 1874 additions and 63 deletions

View file

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

View file

@ -38,7 +38,8 @@ struct WreduceConfig
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
"$mux", "$pmux"
"$mux", "$pmux",
"$dff", "$adff"
});
}
};
@ -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
View file

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

View file

@ -0,0 +1,8 @@
OBJS += passes/pmgen/ice40_dsp.o
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp

224
passes/pmgen/README.md Normal file
View file

@ -0,0 +1,224 @@
Pattern Matcher Generator
=========================
The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
writes a header-only C++ library that implements that pattern matcher.
The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
The algorithm used in the generated pattern matcher is a simple recursive
search with backtracking. It is left to the author of the `.pmg` file to
determine an efficient cell order for the search that allows for maximum
use of indices and early backtracking.
API of Generated Matcher
========================
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
a class `foobar_pm`. That class is 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
View file

@ -0,0 +1,237 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "passes/pmgen/ice40_dsp_pm.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
void create_ice40_dsp(ice40_dsp_pm &pm)
{
#if 0
log("\n");
log("ffA: %s\n", log_id(pm.st.ffA, "--"));
log("ffB: %s\n", log_id(pm.st.ffB, "--"));
log("mul: %s\n", log_id(pm.st.mul, "--"));
log("ffY: %s\n", log_id(pm.st.ffY, "--"));
log("addAB: %s\n", log_id(pm.st.addAB, "--"));
log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
log("ffS: %s\n", log_id(pm.st.ffS, "--"));
#endif
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
if (GetSize(pm.st.sigA) > 16) {
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
return;
}
if (GetSize(pm.st.sigB) > 16) {
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
return;
}
if (GetSize(pm.st.sigS) > 32) {
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
return;
}
if (GetSize(pm.st.sigY) > 32) {
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
return;
}
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
if (mul_signed) {
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
return;
}
log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
pm.module->swap_names(cell, pm.st.mul);
// SB_MAC16 Input Interface
SigSpec A = pm.st.sigA;
A.extend_u0(16, mul_signed);
SigSpec B = pm.st.sigB;
B.extend_u0(16, mul_signed);
SigSpec CD;
if (pm.st.muxA)
CD = pm.st.muxA->getPort("\\B");
if (pm.st.muxB)
CD = pm.st.muxB->getPort("\\A");
CD.extend_u0(32, mul_signed);
cell->setPort("\\A", A);
cell->setPort("\\B", B);
cell->setPort("\\C", CD.extract(0, 16));
cell->setPort("\\D", CD.extract(16, 16));
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
cell->setPort("\\AHOLD", State::S0);
cell->setPort("\\BHOLD", State::S0);
cell->setPort("\\CHOLD", State::S0);
cell->setPort("\\DHOLD", State::S0);
cell->setPort("\\IRSTTOP", State::S0);
cell->setPort("\\IRSTBOT", State::S0);
if (pm.st.clock_vld)
{
cell->setPort("\\CLK", pm.st.clock);
cell->setPort("\\CE", State::S1);
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
if (pm.st.ffA)
log(" ffA:%s", log_id(pm.st.ffA));
if (pm.st.ffB)
log(" ffB:%s", log_id(pm.st.ffB));
if (pm.st.ffY)
log(" ffY:%s", log_id(pm.st.ffY));
if (pm.st.ffS)
log(" ffS:%s", log_id(pm.st.ffS));
log("\n");
}
else
{
cell->setPort("\\CLK", State::S0);
cell->setPort("\\CE", State::S0);
cell->setParam("\\NEG_TRIGGER", State::S0);
}
// SB_MAC16 Cascade Interface
cell->setPort("\\SIGNEXTIN", State::Sx);
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
cell->setPort("\\CI", State::Sx);
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
cell->setPort("\\ACCUMCI", State::Sx);
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
// SB_MAC16 Output Interface
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
if (GetSize(O) < 32)
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
cell->setPort("\\O", O);
if (pm.st.addAB) {
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
} else {
cell->setPort("\\ADDSUBTOP", State::S0);
cell->setPort("\\ADDSUBBOT", State::S0);
}
cell->setPort("\\ORSTTOP", State::S0);
cell->setPort("\\ORSTBOT", State::S0);
cell->setPort("\\OHOLDTOP", State::S0);
cell->setPort("\\OHOLDBOT", State::S0);
SigSpec acc_reset = State::S0;
if (pm.st.muxA)
acc_reset = pm.st.muxA->getPort("\\S");
if (pm.st.muxB)
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
cell->setPort("\\OLOADTOP", acc_reset);
cell->setPort("\\OLOADBOT", acc_reset);
// SB_MAC16 Remaining Parameters
cell->setParam("\\C_REG", State::S0);
cell->setParam("\\D_REG", State::S0);
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
cell->setParam("\\MODE_8x8", State::S0);
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
pm.autoremove(pm.st.mul);
pm.autoremove(pm.st.ffY);
pm.autoremove(pm.st.ffS);
}
struct Ice40DspPass : public Pass {
Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" ice40_dsp [options] [selection]\n");
log("\n");
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-singleton") {
// singleton_mode = true;
// continue;
// }
break;
}
extra_args(args, argidx, design);
for (auto module : design->selected_modules())
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
}
} Ice40DspPass;
PRIVATE_NAMESPACE_END

160
passes/pmgen/ice40_dsp.pmg Normal file
View file

@ -0,0 +1,160 @@
state <SigBit> clock
state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS
state <Cell*> addAB muxAB
match mul
select mul->type.in($mul)
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
select GetSize(mul->getPort(\Y)) > 10
endmatch
match ffA
select ffA->type.in($dff)
// select nusers(port(ffA, \Q)) == 2
index <SigSpec> port(ffA, \Q) === port(mul, \A)
optional
endmatch
code sigA clock clock_pol clock_vld
sigA = port(mul, \A);
if (ffA) {
sigA = port(ffA, \D);
clock = port(ffA, \CLK).as_bit();
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
clock_vld = true;
}
endcode
match ffB
select ffB->type.in($dff)
// select nusers(port(ffB, \Q)) == 2
index <SigSpec> port(ffB, \Q) === port(mul, \B)
optional
endmatch
code sigB clock clock_pol clock_vld
sigB = port(mul, \B);
if (ffB) {
sigB = port(ffB, \D);
SigBit c = port(ffB, \CLK).as_bit();
bool cp = param(ffB, \CLK_POLARITY).as_bool();
if (clock_vld && (c != clock || cp != clock_pol))
reject;
clock = c;
clock_pol = cp;
clock_vld = true;
}
endcode
match ffY
select ffY->type.in($dff)
select nusers(port(ffY, \D)) == 2
index <SigSpec> port(ffY, \D) === port(mul, \Y)
optional
endmatch
code sigY clock clock_pol clock_vld
sigY = port(mul, \Y);
if (ffY) {
sigY = port(ffY, \Q);
SigBit c = port(ffY, \CLK).as_bit();
bool cp = param(ffY, \CLK_POLARITY).as_bool();
if (clock_vld && (c != clock || cp != clock_pol))
reject;
clock = c;
clock_pol = cp;
clock_vld = true;
}
endcode
match addA
select addA->type.in($add)
select nusers(port(addA, \A)) == 2
index <SigSpec> port(addA, \A) === sigY
optional
endmatch
match addB
if !addA
select addB->type.in($add, $sub)
select nusers(port(addB, \B)) == 2
index <SigSpec> port(addB, \B) === sigY
optional
endmatch
code addAB sigS
if (addA) {
addAB = addA;
sigS = port(addA, \B);
}
if (addB) {
addAB = addB;
sigS = port(addB, \A);
}
if (addAB) {
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
int actual_mul_width = GetSize(sigY);
int actual_acc_width = GetSize(sigS);
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
reject;
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
reject;
}
endcode
match muxA
if addAB
select muxA->type.in($mux)
select nusers(port(muxA, \A)) == 2
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
optional
endmatch
match muxB
if addAB
if !muxA
select muxB->type.in($mux)
select nusers(port(muxB, \B)) == 2
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
optional
endmatch
code muxAB
muxAB = addAB;
if (muxA)
muxAB = muxA;
if (muxB)
muxAB = muxB;
endcode
match ffS
if muxAB
select ffS->type.in($dff)
select nusers(port(ffS, \D)) == 2
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
index <SigSpec> port(ffS, \Q) === sigS
endmatch
code clock clock_pol clock_vld
if (ffS) {
SigBit c = port(ffS, \CLK).as_bit();
bool cp = param(ffS, \CLK_POLARITY).as_bool();
if (clock_vld && (c != clock || cp != clock_pol))
reject;
clock = c;
clock_pol = cp;
clock_vld = true;
}
endcode

482
passes/pmgen/pmgen.py Normal file
View 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)