Merge pull request #969 from YosysHQ/clifford/pmgenstuff

Improve pmgen, Add "peepopt" pass with shift-mul pattern
This commit is contained in:
Clifford Wolf 2019-05-03 20:39:50 +02:00 committed by GitHub
commit 373b236108
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 508 additions and 150 deletions

View File

@ -531,6 +531,42 @@ struct WreducePass : public Pass {
module->connect(sig, Const(0, GetSize(sig))); module->connect(sig, Const(0, GetSize(sig)));
} }
} }
if (c->type.in("$div", "$mod", "$pow"))
{
SigSpec A = c->getPort("\\A");
int original_a_width = GetSize(A);
if (c->getParam("\\A_SIGNED").as_bool()) {
while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0)
A.remove(GetSize(A)-1, 1);
} else {
while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0)
A.remove(GetSize(A)-1, 1);
}
if (original_a_width != GetSize(A)) {
log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type));
c->setPort("\\A", A);
c->setParam("\\A_WIDTH", GetSize(A));
}
SigSpec B = c->getPort("\\B");
int original_b_width = GetSize(B);
if (c->getParam("\\B_SIGNED").as_bool()) {
while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0)
B.remove(GetSize(B)-1, 1);
} else {
while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0)
B.remove(GetSize(B)-1, 1);
}
if (original_b_width != GetSize(B)) {
log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type));
c->setPort("\\B", B);
c->setParam("\\B_WIDTH", GetSize(B));
}
}
if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) { if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
IdString memid = c->getParam("\\MEMID").decode_string(); IdString memid = c->getParam("\\MEMID").decode_string();
RTLIL::Memory *mem = module->memories.at(memid); RTLIL::Memory *mem = module->memories.at(memid);

View File

@ -1 +1,2 @@
/ice40_dsp_pm.h /ice40_dsp_pm.h
/peepopt_pm.h

View File

@ -1,8 +1,23 @@
OBJS += passes/pmgen/ice40_dsp.o OBJS += passes/pmgen/ice40_dsp.o
OBJS += passes/pmgen/peepopt.o
# --------------------------------------
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
.SECONDARY: passes/pmgen/ice40_dsp_pm.h .SECONDARY: passes/pmgen/ice40_dsp_pm.h
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
$(P) mkdir -p passes/pmgen && python3 $^ $@ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^)
# --------------------------------------
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
EXTRA_OBJS += passes/pmgen/peepopt_pm.h
.SECONDARY: passes/pmgen/peepopt_pm.h
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)

View File

@ -29,19 +29,25 @@ up in any future matches:
pm.blacklist(some_cell); pm.blacklist(some_cell);
The `.run(callback_function)` method searches for all matches and calls the The `.run_<pattern_name>(callback_function)` method searches for all matches
callback function for each found match: for the pattern`<pattern_name>` and calls the callback function for each found
match:
pm.run([&](){ pm.run_foobar([&](){
log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
log(" with 'bar' cell: %s\n", log_id(pm.st.bar)); log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
}); });
The `.pmg` file declares matcher state variables that are accessible via the 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`.) `.st_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is
of type `foobar_pm::state_<pattern_name>_t`.)
Similarly the `.pmg` file declares user data variables that become members of Similarly the `.pmg` file declares user data variables that become members of
`.ud`, a struct of type `foobar_pm::udata_t`. `.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
There are four versions of the `run_<pattern_name>()` method: Without callback,
callback without arguments, callback with reference to `pm`, and callback with
reference to `pm.st_<pattern_name>`.
The .pmg File Format The .pmg File Format
@ -52,6 +58,12 @@ lines consist of whitespace-separated tokens.
Lines in `.pmg` files starting with `//` are comments. Lines in `.pmg` files starting with `//` are comments.
Declaring a pattern
-------------------
A `.pmg` file contains one or more patterns. Each pattern starts with a line
with the `pattern` keyword followed by the name of the pattern.
Declaring state variables Declaring state variables
------------------------- -------------------------
@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith
and saved and restored as needed. and saved and restored as needed.
They are automatically initialized to the default constructed value of their type They are automatically initialized to the default constructed value of their type
when `.run(callback_function)` is called. when `.run_<pattern_name>(callback_function)` is called.
Declaring udata variables Declaring udata variables
------------------------- -------------------------

View File

@ -19,47 +19,50 @@
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
#include "passes/pmgen/ice40_dsp_pm.h"
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
#include "passes/pmgen/ice40_dsp_pm.h"
void create_ice40_dsp(ice40_dsp_pm &pm) void create_ice40_dsp(ice40_dsp_pm &pm)
{ {
auto &st = pm.st_ice40_dsp;
#if 0 #if 0
log("\n"); log("\n");
log("ffA: %s\n", log_id(pm.st.ffA, "--")); log("ffA: %s\n", log_id(st.ffA, "--"));
log("ffB: %s\n", log_id(pm.st.ffB, "--")); log("ffB: %s\n", log_id(st.ffB, "--"));
log("mul: %s\n", log_id(pm.st.mul, "--")); log("mul: %s\n", log_id(st.mul, "--"));
log("ffY: %s\n", log_id(pm.st.ffY, "--")); log("ffY: %s\n", log_id(st.ffY, "--"));
log("addAB: %s\n", log_id(pm.st.addAB, "--")); log("addAB: %s\n", log_id(st.addAB, "--"));
log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); log("muxAB: %s\n", log_id(st.muxAB, "--"));
log("ffS: %s\n", log_id(pm.st.ffS, "--")); log("ffS: %s\n", log_id(st.ffS, "--"));
#endif #endif
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
if (GetSize(pm.st.sigA) > 16) { if (GetSize(st.sigA) > 16) {
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
return; return;
} }
if (GetSize(pm.st.sigB) > 16) { if (GetSize(st.sigB) > 16) {
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB));
return; return;
} }
if (GetSize(pm.st.sigS) > 32) { if (GetSize(st.sigS) > 32) {
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
return; return;
} }
if (GetSize(pm.st.sigY) > 32) { if (GetSize(st.sigY) > 32) {
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
return; return;
} }
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
if (mul_signed) { if (mul_signed) {
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n"); log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
@ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
log(" replacing $mul with SB_MAC16 cell.\n"); log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
pm.module->swap_names(cell, pm.st.mul); pm.module->swap_names(cell, st.mul);
// SB_MAC16 Input Interface // SB_MAC16 Input Interface
SigSpec A = pm.st.sigA; SigSpec A = st.sigA;
A.extend_u0(16, mul_signed); A.extend_u0(16, mul_signed);
SigSpec B = pm.st.sigB; SigSpec B = st.sigB;
B.extend_u0(16, mul_signed); B.extend_u0(16, mul_signed);
SigSpec CD; SigSpec CD;
if (pm.st.muxA) if (st.muxA)
CD = pm.st.muxA->getPort("\\B"); CD = st.muxA->getPort("\\B");
if (pm.st.muxB) if (st.muxB)
CD = pm.st.muxB->getPort("\\A"); CD = st.muxB->getPort("\\A");
CD.extend_u0(32, mul_signed); CD.extend_u0(32, mul_signed);
cell->setPort("\\A", A); cell->setPort("\\A", A);
@ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\C", CD.extract(0, 16)); cell->setPort("\\C", CD.extract(0, 16));
cell->setPort("\\D", CD.extract(16, 16)); cell->setPort("\\D", CD.extract(16, 16));
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
cell->setPort("\\AHOLD", State::S0); cell->setPort("\\AHOLD", State::S0);
cell->setPort("\\BHOLD", State::S0); cell->setPort("\\BHOLD", State::S0);
@ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\IRSTTOP", State::S0); cell->setPort("\\IRSTTOP", State::S0);
cell->setPort("\\IRSTBOT", State::S0); cell->setPort("\\IRSTBOT", State::S0);
if (pm.st.clock_vld) if (st.clock_vld)
{ {
cell->setPort("\\CLK", pm.st.clock); cell->setPort("\\CLK", st.clock);
cell->setPort("\\CE", State::S1); cell->setPort("\\CE", State::S1);
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
if (pm.st.ffA) if (st.ffA)
log(" ffA:%s", log_id(pm.st.ffA)); log(" ffA:%s", log_id(st.ffA));
if (pm.st.ffB) if (st.ffB)
log(" ffB:%s", log_id(pm.st.ffB)); log(" ffB:%s", log_id(st.ffB));
if (pm.st.ffY) if (st.ffY)
log(" ffY:%s", log_id(pm.st.ffY)); log(" ffY:%s", log_id(st.ffY));
if (pm.st.ffS) if (st.ffS)
log(" ffS:%s", log_id(pm.st.ffS)); log(" ffS:%s", log_id(st.ffS));
log("\n"); log("\n");
} }
@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
// SB_MAC16 Output Interface // SB_MAC16 Output Interface
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; SigSpec O = st.ffS ? st.sigS : st.sigY;
if (GetSize(O) < 32) if (GetSize(O) < 32)
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
cell->setPort("\\O", O); cell->setPort("\\O", O);
if (pm.st.addAB) { if (st.addAB) {
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
} else { } else {
cell->setPort("\\ADDSUBTOP", State::S0); cell->setPort("\\ADDSUBTOP", State::S0);
cell->setPort("\\ADDSUBBOT", State::S0); cell->setPort("\\ADDSUBBOT", State::S0);
@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\OHOLDBOT", State::S0); cell->setPort("\\OHOLDBOT", State::S0);
SigSpec acc_reset = State::S0; SigSpec acc_reset = State::S0;
if (pm.st.muxA) if (st.muxA)
acc_reset = pm.st.muxA->getPort("\\S"); acc_reset = st.muxA->getPort("\\S");
if (pm.st.muxB) if (st.muxB)
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
cell->setPort("\\OLOADTOP", acc_reset); cell->setPort("\\OLOADTOP", acc_reset);
cell->setPort("\\OLOADBOT", acc_reset); cell->setPort("\\OLOADBOT", acc_reset);
@ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\C_REG", State::S0); cell->setParam("\\C_REG", State::S0);
cell->setParam("\\D_REG", State::S0); cell->setParam("\\D_REG", State::S0);
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
@ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
pm.autoremove(pm.st.mul); pm.autoremove(st.mul);
pm.autoremove(pm.st.ffY); pm.autoremove(st.ffY);
pm.autoremove(pm.st.ffS); pm.autoremove(st.ffS);
} }
struct Ice40DspPass : public Pass { struct Ice40DspPass : public Pass {
@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass {
extra_args(args, argidx, design); extra_args(args, argidx, design);
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp);
} }
} Ice40DspPass; } Ice40DspPass;

View File

@ -1,3 +1,5 @@
pattern ice40_dsp
state <SigBit> clock state <SigBit> clock
state <bool> clock_pol clock_vld state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS state <SigSpec> sigA sigB sigY sigS

68
passes/pmgen/peepopt.cc Normal file
View File

@ -0,0 +1,68 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
bool did_something;
#include "passes/pmgen/peepopt_pm.h"
struct PeepoptPass : public Pass {
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" peepopt [options] [selection]\n");
log("\n");
log("This pass applies a collection of peephole optimizers to the current design.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\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()) {
did_something = true;
while (did_something) {
did_something = false;
peepopt_pm pm(module, module->selected_cells());
pm.run_shiftmul();
pm.run_muldiv();
}
}
}
} PeepoptPass;
PRIVATE_NAMESPACE_END

View File

@ -0,0 +1,36 @@
pattern muldiv
state <SigSpec> t x y
match mul
select mul->type == $mul
select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y))
endmatch
code t x y
t = port(mul, \Y);
x = port(mul, \A);
y = port(mul, \B);
branch;
std::swap(x, y);
endcode
match div
select div->type.in($div)
index <SigSpec> port(div, \A) === t
index <SigSpec> port(div, \B) === x
endmatch
code
SigSpec div_y = port(div, \Y);
SigSpec val_y = y;
if (GetSize(div_y) != GetSize(val_y))
val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool());
did_something = true;
log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
module->connect(div_y, val_y);
autoremove(div);
reject;
endcode

View File

@ -0,0 +1,83 @@
pattern shiftmul
state <SigSpec> shamt
match shift
select shift->type.in($shift, $shiftx, $shr)
endmatch
code shamt
shamt = port(shift, \B);
if (shamt[GetSize(shamt)-1] == State::S0) {
do {
shamt.remove(GetSize(shamt)-1);
} while (shamt[GetSize(shamt)-1] == State::S0);
} else
if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) {
reject;
}
if (GetSize(shamt) > 20)
reject;
endcode
match mul
select mul->type.in($mul)
select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
index <SigSpec> port(mul, \Y) === shamt
endmatch
code
IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
Const const_factor_cnst = port(mul, const_factor_port).as_const();
int const_factor = const_factor_cnst.as_int();
if (GetSize(const_factor_cnst) == 0)
reject;
if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 &&
param(mul, const_factor_signed).as_bool())
reject;
if (GetSize(const_factor_cnst) > 20)
reject;
if (GetSize(port(shift, \Y)) > const_factor)
reject;
did_something = true;
log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
int new_const_factor_log2 = ceil_log2(const_factor);
int new_const_factor = 1 << new_const_factor_log2;
SigSpec padding(State::Sx, new_const_factor-const_factor);
SigSpec old_a = port(shift, \A), new_a;
int trunc = 0;
if (GetSize(old_a) % const_factor != 0) {
trunc = const_factor - GetSize(old_a) % const_factor;
old_a.append(SigSpec(State::Sx, trunc));
}
for (int i = 0; i*const_factor < GetSize(old_a); i++) {
SigSpec slice = old_a.extract(i*const_factor, const_factor);
new_a.append(slice);
new_a.append(padding);
}
if (trunc > 0)
new_a.remove(GetSize(new_a)-trunc, trunc);
SigSpec new_b = {port(mul, const_factor_port == \A ? \B : \A), SigSpec(State::S0, new_const_factor_log2)};
if (param(shift, \B_SIGNED).as_bool())
new_b.append(State::S0);
shift->setPort(\A, new_a);
shift->setParam(\A_WIDTH, GetSize(new_a));
shift->setPort(\B, new_b);
shift->setParam(\B_WIDTH, GetSize(new_b));
blacklist(shift);
reject;
endcode

View File

@ -3,15 +3,42 @@
import re import re
import sys import sys
import pprint import pprint
import getopt
pp = pprint.PrettyPrinter(indent=4) pp = pprint.PrettyPrinter(indent=4)
pmgfile = sys.argv[1] prefix = None
assert pmgfile.endswith(".pmg") pmgfiles = list()
prefix = pmgfile[0:-4] outfile = None
prefix = prefix.split('/')[-1] debug = False
outfile = sys.argv[2] genhdr = False
opts, args = getopt.getopt(sys.argv[1:], "p:o:dg")
for o, a in opts:
if o == "-p":
prefix = a
elif o == "-o":
outfile = a
elif o == "-d":
debug = True
elif o == "-g":
genhdr = True
if outfile is None:
outfile = "/dev/stdout"
for a in args:
assert a.endswith(".pmg")
if prefix is None and len(args) == 1:
prefix = a[0:-4]
prefix = prefix.split('/')[-1]
pmgfiles.append(a)
assert prefix is not None
current_pattern = None
patterns = dict()
state_types = dict() state_types = dict()
udata_types = dict() udata_types = dict()
blocks = list() blocks = list()
@ -77,7 +104,8 @@ def rewrite_cpp(s):
return "".join(t) return "".join(t)
with open(pmgfile, "r") as f: def process_pmgfile(f):
global current_pattern
while True: while True:
line = f.readline() line = f.readline()
if line == "": break if line == "": break
@ -87,14 +115,31 @@ with open(pmgfile, "r") as f:
if len(cmd) == 0 or cmd[0].startswith("//"): continue if len(cmd) == 0 or cmd[0].startswith("//"): continue
cmd = cmd[0] cmd = cmd[0]
if cmd == "pattern":
if current_pattern is not None:
block = dict()
block["type"] = "final"
block["pattern"] = current_pattern
blocks.append(block)
line = line.split()
assert len(line) == 2
assert line[1] not in patterns
current_pattern = line[1]
patterns[current_pattern] = len(blocks)
state_types[current_pattern] = dict()
udata_types[current_pattern] = dict()
continue
assert current_pattern is not None
if cmd == "state": 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) 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 assert m
type_str = m.group(1) type_str = m.group(1)
states_str = m.group(2) states_str = m.group(2)
for s in re.split(r"\s+", states_str): for s in re.split(r"\s+", states_str):
assert s not in state_types assert s not in state_types[current_pattern]
state_types[s] = type_str state_types[current_pattern][s] = type_str
continue continue
if cmd == "udata": if cmd == "udata":
@ -103,19 +148,20 @@ with open(pmgfile, "r") as f:
type_str = m.group(1) type_str = m.group(1)
udatas_str = m.group(2) udatas_str = m.group(2)
for s in re.split(r"\s+", udatas_str): for s in re.split(r"\s+", udatas_str):
assert s not in udata_types assert s not in udata_types[current_pattern]
udata_types[s] = type_str udata_types[current_pattern][s] = type_str
continue continue
if cmd == "match": if cmd == "match":
block = dict() block = dict()
block["type"] = "match" block["type"] = "match"
block["pattern"] = current_pattern
line = line.split() line = line.split()
assert len(line) == 2 assert len(line) == 2
assert line[1] not in state_types assert line[1] not in state_types[current_pattern]
block["cell"] = line[1] block["cell"] = line[1]
state_types[line[1]] = "Cell*"; state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list() block["if"] = list()
block["select"] = list() block["select"] = list()
@ -158,15 +204,18 @@ with open(pmgfile, "r") as f:
assert False assert False
blocks.append(block) blocks.append(block)
continue
if cmd == "code": if cmd == "code":
block = dict() block = dict()
block["type"] = "code" block["type"] = "code"
block["pattern"] = current_pattern
block["code"] = list() block["code"] = list()
block["states"] = set() block["states"] = set()
for s in line.split()[1:]: for s in line.split()[1:]:
assert s in state_types assert s in state_types[current_pattern]
block["states"].add(s) block["states"].add(s)
while True: while True:
@ -179,17 +228,36 @@ with open(pmgfile, "r") as f:
block["code"].append(rewrite_cpp(l.rstrip())) block["code"].append(rewrite_cpp(l.rstrip()))
blocks.append(block) blocks.append(block)
continue
assert False
for fn in pmgfiles:
with open(fn, "r") as f:
process_pmgfile(f)
if current_pattern is not None:
block = dict()
block["type"] = "final"
block["pattern"] = current_pattern
blocks.append(block)
current_pattern = None
if debug:
pp.pprint(blocks)
with open(outfile, "w") as f: with open(outfile, "w") as f:
print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) for fn in pmgfiles:
print("// Generated by pmgen.py from {}".format(fn), file=f)
print("", file=f) print("", file=f)
print("#include \"kernel/yosys.h\"", file=f) if genhdr:
print("#include \"kernel/sigtools.h\"", file=f) print("#include \"kernel/yosys.h\"", file=f)
print("", file=f) print("#include \"kernel/sigtools.h\"", file=f)
print("", file=f)
print("YOSYS_NAMESPACE_BEGIN", file=f) print("YOSYS_NAMESPACE_BEGIN", file=f)
print("", file=f) print("", file=f)
print("struct {}_pm {{".format(prefix), file=f) print("struct {}_pm {{".format(prefix), file=f)
print(" Module *module;", file=f) print(" Module *module;", file=f)
@ -212,17 +280,19 @@ with open(outfile, "w") as f:
print(" int rollback;", file=f) print(" int rollback;", file=f)
print("", file=f) print("", file=f)
print(" struct state_t {", file=f) for current_pattern in sorted(patterns.keys()):
for s, t in sorted(state_types.items()): print(" struct state_{}_t {{".format(current_pattern), file=f)
print(" {} {};".format(t, s), file=f) for s, t in sorted(state_types[current_pattern].items()):
print(" } st;", file=f) print(" {} {};".format(t, s), file=f)
print("", file=f) print(" }} st_{};".format(current_pattern), file=f)
print("", file=f)
print(" struct udata_t {", file=f) print(" struct udata_{}_t {{".format(current_pattern), file=f)
for s, t in sorted(udata_types.items()): for s, t in sorted(udata_types[current_pattern].items()):
print(" {} {};".format(t, s), file=f) print(" {} {};".format(t, s), file=f)
print(" } ud;", file=f) print(" }} ud_{};".format(current_pattern), file=f)
print("", file=f) print("", file=f)
current_pattern = None
for v, n in sorted(ids.items()): for v, n in sorted(ids.items()):
if n[0] == "\\": if n[0] == "\\":
@ -258,20 +328,24 @@ with open(outfile, "w") as f:
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void check_blacklist() {", file=f) for current_pattern in sorted(patterns.keys()):
print(" if (!blacklist_dirty)", file=f) print(" void check_blacklist_{}() {{".format(current_pattern), file=f)
print(" return;", file=f) print(" if (!blacklist_dirty)", file=f)
print(" blacklist_dirty = false;", file=f) print(" return;", file=f)
for index in range(len(blocks)): print(" blacklist_dirty = false;", file=f)
block = blocks[index] for index in range(len(blocks)):
if block["type"] == "match": block = blocks[index]
print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) if block["pattern"] != current_pattern:
print(" rollback = {};".format(index+1), file=f) continue
print(" return;", file=f) if block["type"] == "match":
print(" }", file=f) print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f)
print(" rollback = 0;", file=f) print(" rollback = {};".format(index+1), file=f)
print(" }", file=f) print(" return;", file=f)
print("", file=f) print(" }", file=f)
print(" rollback = 0;", file=f)
print(" }", file=f)
print("", file=f)
current_pattern = None
print(" SigSpec port(Cell *cell, IdString portname) {", file=f) print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
print(" return sigmap(cell->getPort(portname));", file=f) print(" return sigmap(cell->getPort(portname));", file=f)
@ -294,11 +368,13 @@ with open(outfile, "w") as f:
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
print(" module(module), sigmap(module) {", file=f) print(" module(module), sigmap(module) {", file=f)
for s, t in sorted(udata_types.items()): for current_pattern in sorted(patterns.keys()):
if t.endswith("*"): for s, t in sorted(udata_types[current_pattern].items()):
print(" ud.{} = nullptr;".format(s), file=f) if t.endswith("*"):
else: print(" ud_{}.{} = nullptr;".format(current_pattern,s), file=f)
print(" ud.{} = {}();".format(s, t), file=f) else:
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
current_pattern = None
print(" for (auto cell : module->cells()) {", file=f) print(" for (auto cell : module->cells()) {", file=f)
print(" for (auto &conn : cell->connections())", file=f) print(" for (auto &conn : cell->connections())", file=f)
print(" add_siguser(conn.second, cell);", file=f) print(" add_siguser(conn.second, cell);", file=f)
@ -328,34 +404,52 @@ with open(outfile, "w") as f:
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void run(std::function<void()> on_accept_f) {", file=f) for current_pattern in sorted(patterns.keys()):
print(" on_accept = on_accept_f;", file=f) print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
print(" rollback = 0;", file=f) print(" on_accept = on_accept_f;", file=f)
print(" blacklist_dirty = false;", file=f) print(" rollback = 0;", file=f)
for s, t in sorted(state_types.items()): print(" blacklist_dirty = false;", file=f)
if t.endswith("*"): for s, t in sorted(state_types[current_pattern].items()):
print(" st.{} = nullptr;".format(s), file=f) if t.endswith("*"):
else: print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f)
print(" st.{} = {}();".format(s, t), file=f) else:
print(" block_0();", file=f) print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f)
print(" }", file=f) print(" block_{}();".format(patterns[current_pattern]), file=f)
print("", file=f) print(" }", file=f)
print("", file=f)
print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
print(" run([&](){on_accept_f(*this);});", file=f) print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f)
print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f)
print(" }", file=f)
print("", file=f)
print(" void run_{}() {{".format(current_pattern), file=f)
print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
print(" }", file=f)
print("", file=f)
current_pattern = None
for index in range(len(blocks)): for index in range(len(blocks)):
block = blocks[index] block = blocks[index]
print(" void block_{}() {{".format(index), file=f) print(" void block_{}() {{".format(index), file=f)
current_pattern = block["pattern"]
if block["type"] == "final":
print(" on_accept();", file=f)
print(" check_blacklist_{}();".format(current_pattern), file=f)
print(" }", file=f)
if index+1 != len(blocks):
print("", file=f)
continue
const_st = set() const_st = set()
nonconst_st = set() nonconst_st = set()
restore_st = set() restore_st = set()
for i in range(index): for i in range(patterns[current_pattern], index):
if blocks[i]["type"] == "code": if blocks[i]["type"] == "code":
for s in blocks[i]["states"]: for s in blocks[i]["states"]:
const_st.add(s) const_st.add(s)
@ -378,27 +472,27 @@ with open(outfile, "w") as f:
assert False assert False
for s in sorted(const_st): for s in sorted(const_st):
t = state_types[s] t = state_types[current_pattern][s]
if t.endswith("*"): if t.endswith("*"):
print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) print(" {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
else: else:
print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) print(" const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
for s in sorted(nonconst_st): for s in sorted(nonconst_st):
t = state_types[s] t = state_types[current_pattern][s]
print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
if len(restore_st): if len(restore_st):
print("", file=f) print("", file=f)
for s in sorted(restore_st): for s in sorted(restore_st):
t = state_types[s] t = state_types[current_pattern][s]
print(" {} backup_{} = {};".format(t, s, s), file=f) print(" {} backup_{} = {};".format(t, s, s), file=f)
if block["type"] == "code": if block["type"] == "code":
print("", file=f) print("", file=f)
print(" do {", file=f) print(" do {", file=f)
print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f)
print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
for line in block["code"]: for line in block["code"]:
@ -417,11 +511,11 @@ with open(outfile, "w") as f:
if len(restore_st) or len(nonconst_st): if len(restore_st) or len(nonconst_st):
print("", file=f) print("", file=f)
for s in sorted(restore_st): for s in sorted(restore_st):
t = state_types[s] t = state_types[current_pattern][s]
print(" {} = backup_{};".format(s, s), file=f) print(" {} = backup_{};".format(s, s), file=f)
for s in sorted(nonconst_st): for s in sorted(nonconst_st):
if s not in restore_st: if s not in restore_st:
t = state_types[s] t = state_types[current_pattern][s]
if t.endswith("*"): if t.endswith("*"):
print(" {} = nullptr;".format(s), file=f) print(" {} = nullptr;".format(s), file=f)
else: else:
@ -470,17 +564,12 @@ with open(outfile, "w") as f:
else: else:
assert False assert False
current_pattern = None
print(" }", file=f) print(" }", file=f)
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("", file=f) if genhdr:
print("YOSYS_NAMESPACE_END", file=f) print("", file=f)
print("YOSYS_NAMESPACE_END", file=f)
# pp.pprint(blocks)

View File

@ -201,6 +201,8 @@ struct SynthPass : public ScriptPass
run("check"); run("check");
run("opt"); run("opt");
run("wreduce"); run("wreduce");
run("peepopt");
run("opt_clean");
if (help_mode) if (help_mode)
run("techmap -map +/cmp2lut.v", " (if -lut)"); run("techmap -map +/cmp2lut.v", " (if -lut)");
else else

View File

@ -241,6 +241,8 @@ struct SynthIce40Pass : public ScriptPass
run("check"); run("check");
run("opt"); run("opt");
run("wreduce"); run("wreduce");
run("peepopt");
run("opt_clean");
run("share"); run("share");
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
run("opt_expr"); run("opt_expr");

9
tests/simple/peepopt.v Normal file
View File

@ -0,0 +1,9 @@
module peepopt_shiftmul_0 #(parameter N=3, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output [W-1:0] o);
assign o = i[s*W+:W];
endmodule
module peepopt_muldiv_0(input [1:0] i, output [1:0] o);
wire [3:0] t;
assign t = i * 3;
assign o = t / 3;
endmodule