mirror of https://github.com/YosysHQ/yosys.git
modshr onehot pass
This commit is contained in:
parent
242d87079a
commit
b4e94d9f13
|
|
@ -34,6 +34,7 @@ PEEPOPT_PATTERN += passes/opt/peepopt_shiftmul_left.pmg
|
|||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftadd.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv_c.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_modshr_onehot.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_addsub_c.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muxorder.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ struct PeepoptPass : public Pass {
|
|||
log("\n");
|
||||
log(" * muldiv_c - Replace (A*B)/C with A*(B/C) when C is a const divisible by B.\n");
|
||||
log("\n");
|
||||
log(" * modshr_onehot - Replace a%%(mbase>>shiftamt) with a&((mbase-1)>>shiftamt)\n");
|
||||
log(" when mbase is a constant one-hot signal.\n");
|
||||
log("\n");
|
||||
log(" * shiftmul - Replace A>>(B*C) with A'>>(B<<K) where C and K are constants\n");
|
||||
log(" and A' is derived from A by appropriately inserting padding\n");
|
||||
log(" into the signal. (right variant)\n");
|
||||
|
|
@ -124,13 +127,14 @@ struct PeepoptPass : public Pass {
|
|||
if (formalclk) {
|
||||
pm.run_formal_clockgateff();
|
||||
} else {
|
||||
pm.run_shiftadd();
|
||||
pm.run_shiftmul_right();
|
||||
pm.run_shiftmul_left();
|
||||
pm.run_muldiv();
|
||||
pm.run_muldiv_c();
|
||||
pm.run_addsub_c();
|
||||
pm.run_sub_neg();
|
||||
pm.run_shiftadd();
|
||||
pm.run_shiftmul_right();
|
||||
pm.run_shiftmul_left();
|
||||
pm.run_muldiv();
|
||||
pm.run_muldiv_c();
|
||||
pm.run_modshr_onehot();
|
||||
pm.run_addsub_c();
|
||||
pm.run_sub_neg();
|
||||
if (muxorder)
|
||||
pm.run_muxorder();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
pattern modshr_onehot
|
||||
//
|
||||
// Authored by Akash Levy of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Rewrite unsigned modulo by a right-shifted constant one-hot into a
|
||||
// bitwise mask:
|
||||
//
|
||||
// y = a % (mbase >> shiftamt) ===> y = a & ((mbase - 1) >> shiftamt)
|
||||
//
|
||||
// where `mbase` is a fully-constant one-hot signal. This eliminates a
|
||||
// variable divider, replacing it with a single constant-A right shift
|
||||
// and a bitwise AND.
|
||||
//
|
||||
// Correctness:
|
||||
// Because `mbase = 1 << B` for some constant bit index B,
|
||||
// `mbase >> shiftamt` is always either a power of two or zero.
|
||||
// For unsigned operands, `a % 2^k = a & (2^k - 1)`, and
|
||||
// `(mbase - 1) >> shiftamt` is exactly the lower-k-bits mask.
|
||||
// When `shiftamt > B` the original `a % 0` is undefined (Yosys
|
||||
// `const_mod` returns Sx); the rewrite yields `a & 0 = 0`, which is
|
||||
// a legal refinement of Sx.
|
||||
//
|
||||
// We match $mod first and then look for a $shr/$shiftx whose Y's
|
||||
// lower bits drive mod->B. This is critical because Verilog often
|
||||
// infers the shifter as 32 bits wide (signed integer parameters) and
|
||||
// only the lower bits actually feed the modulo.
|
||||
//
|
||||
|
||||
state <SigSpec> mod_b
|
||||
state <int> mod_b_width
|
||||
state <Const> mbase_const
|
||||
state <int> onehot_pos
|
||||
|
||||
match mod
|
||||
select mod->type.in($mod, $modfloor)
|
||||
// Signed modulo has different semantics for negative dividends;
|
||||
// the bitmask identity holds only for unsigned a.
|
||||
filter !param(mod, \A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
code mod_b mod_b_width
|
||||
mod_b = port(mod, \B);
|
||||
mod_b_width = GetSize(mod_b);
|
||||
endcode
|
||||
|
||||
match shift
|
||||
// $shr / $shiftx perform logical operations regardless of A_SIGNED,
|
||||
// so we accept any A_SIGNED. $sshr is excluded because with
|
||||
// A_SIGNED=1 and a one-hot at the MSB it would sign-extend with
|
||||
// 1s, breaking the power-of-two assumption.
|
||||
select shift->type.in($shr, $shiftx)
|
||||
// B_SIGNED must be false so shiftamt is non-negative; for $shiftx
|
||||
// in particular a negative B would shift left, invalidating the
|
||||
// rewrite.
|
||||
filter !param(shift, \B_SIGNED).as_bool()
|
||||
// A must be a constant one-hot signal.
|
||||
filter port(shift, \A).is_fully_const()
|
||||
filter port(shift, \A).is_onehot()
|
||||
// shift->Y must be at least as wide as mod->B (we look at its low
|
||||
// bits only), and the lower `mod_b_width` bits of Y must exactly
|
||||
// equal mod->B. The shifter output is allowed to fan out to many
|
||||
// $mod cells (each one is rewritten independently); we do not
|
||||
// constrain nusers here.
|
||||
filter GetSize(port(shift, \Y)) >= mod_b_width
|
||||
filter port(shift, \Y).extract(0, mod_b_width) == mod_b
|
||||
endmatch
|
||||
|
||||
code mbase_const onehot_pos
|
||||
mbase_const = port(shift, \A).as_const();
|
||||
mbase_const.is_onehot(&onehot_pos);
|
||||
endcode
|
||||
|
||||
code
|
||||
{
|
||||
// If mod sees B as signed, the divisor bit pattern must encode a
|
||||
// non-negative value; reject when the one-hot bit sits at the MSB
|
||||
// of mbase (which would make it negative).
|
||||
if (param(mod, \B_SIGNED).as_bool() && onehot_pos == GetSize(mbase_const) - 1)
|
||||
reject;
|
||||
|
||||
// Build mask_const = mbase - 1: bits [onehot_pos-1:0] = 1, rest = 0.
|
||||
Const mask_const(State::S0, GetSize(mbase_const));
|
||||
for (int i = 0; i < onehot_pos; i++)
|
||||
mask_const.set(i, State::S1);
|
||||
|
||||
// `cell` is read by NEW_ID2_SUFFIX (kernel/yosys_common.h:321);
|
||||
// use the soon-to-be-converted $mod as the naming parent so the
|
||||
// new mask wire and shifter inherit a stable, debuggable name.
|
||||
Cell *cell = mod;
|
||||
|
||||
// New shifter producing shifted_mask = mask_const >> shiftamt,
|
||||
// of the same type as the original shift but width-trimmed to
|
||||
// match mod->B.
|
||||
Wire *shifted_mask = module->addWire(NEW_ID2_SUFFIX("mask"), mod_b_width);
|
||||
Cell *new_shift = module->addCell(NEW_ID2_SUFFIX("maskshift"), shift->type);
|
||||
new_shift->setPort(\A, mask_const);
|
||||
new_shift->setPort(\B, port(shift, \B));
|
||||
new_shift->setPort(\Y, shifted_mask);
|
||||
new_shift->setParam(\A_SIGNED, false);
|
||||
new_shift->setParam(\B_SIGNED, false);
|
||||
new_shift->fixup_parameters();
|
||||
new_shift->set_src_attribute(mod->get_src_attribute());
|
||||
|
||||
// Convert $mod -> $and in place: keep A and Y, retarget B to the
|
||||
// new shifted_mask wire, and refresh widths.
|
||||
mod->type = $and;
|
||||
mod->setPort(\B, shifted_mask);
|
||||
mod->setParam(\B_SIGNED, false);
|
||||
mod->fixup_parameters();
|
||||
module->rename(mod, NEW_ID2_SUFFIX("modmask"));
|
||||
|
||||
// Do NOT autoremove(shift): a single shifter often fans out to
|
||||
// many $mod cells (e.g. inside a generate loop), and autoremove
|
||||
// blacklists the cell, preventing further matches for siblings.
|
||||
// Leave shift in place -- opt_clean will prune it later if all
|
||||
// of its Y bits have become dead.
|
||||
|
||||
log("modshr_onehot pattern in %s: shift=%s, mod=%s\n",
|
||||
log_id(module), log_id(shift), log_id(mod));
|
||||
did_something = true;
|
||||
accept;
|
||||
}
|
||||
endcode
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
log -header "Positive: mbase MSB one-hot (8'b1000_0000), shiftamt 3 bits"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [2:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b1000_0000;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: mbase non-MSB one-hot (8'b0010_0000)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [1:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0010_0000;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: shiftamt clog2(N) bits, divisor never zero (mbase = 1<<(N-1))"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [15:0] a,
|
||||
input [3:0] shiftamt,
|
||||
output [15:0] y
|
||||
);
|
||||
localparam [15:0] MBASE = 16'h8000;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: low-position one-hot, shiftamt bounded so divisor != 0"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [7:0] a,
|
||||
input shiftamt,
|
||||
output [7:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0000_0010;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: wider mbase than a, shiftamt sized so divisor != 0"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [3:0] a,
|
||||
input [1:0] shiftamt,
|
||||
output [3:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0001_0000;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: \$shr A_SIGNED=1 (signed parameter), Y wider than mod->B"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [1:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
// FIFO_DEPTH is inferred as a signed integer parameter, so the
|
||||
// synthesized \$shr cell ends up with A_SIGNED=1 even though the
|
||||
// value is positive. Verilog also widens the shifter Y to 32 bits;
|
||||
// only the low bits feed the modulo. shiftamt range 0..3 keeps
|
||||
// (FIFO_DEPTH >> shiftamt) in {32,16,8,4} -- never zero.
|
||||
parameter FIFO_DEPTH = 32;
|
||||
wire [5:0] divisor = FIFO_DEPTH >> shiftamt;
|
||||
assign y = a % divisor;
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: shared shifter Y fans out to many \$mod cells (generate loop)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOT
|
||||
module top(
|
||||
input [11:0] a0, a1, a2, a3,
|
||||
input [1:0] shiftamt,
|
||||
output [11:0] y0, y1, y2, y3
|
||||
);
|
||||
parameter FIFO_DEPTH = 32;
|
||||
wire [5:0] divisor = FIFO_DEPTH >> shiftamt;
|
||||
assign y0 = a0 % divisor;
|
||||
assign y1 = a1 % divisor;
|
||||
assign y2 = a2 % divisor;
|
||||
assign y3 = a3 % divisor;
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 4 t:$and
|
||||
# 4 mask shifters + 1 original (still drives the shared divisor wire
|
||||
# whose remaining bits may be dead). opt_clean keeps the original
|
||||
# while any of its Y bits has a name attached; check >= 4 shifters.
|
||||
select -assert-min 4 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: rewrite still applied even when divisor can be 0; assert via peepopt only"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [7:0] a,
|
||||
input [2:0] shiftamt,
|
||||
output [7:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0000_0001;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
peepopt
|
||||
opt_clean
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 1 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Negative: mbase is NOT one-hot (8'b0011_0000)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [2:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0011_0000;
|
||||
assign y = a % (MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mod
|
||||
select -assert-count 0 t:$and
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Negative: mbase is a wire (not constant) -- pattern must not match"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [7:0] mbase,
|
||||
input [2:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
assign y = a % (mbase >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mod
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Negative: signed dividend (A_SIGNED=1 on $mod)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [11:0] a,
|
||||
input [2:0] shiftamt,
|
||||
output signed [11:0] y
|
||||
);
|
||||
localparam signed [7:0] MBASE = 8'b0010_0000;
|
||||
assign y = a % $signed(MBASE >> shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mod
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Negative: shifter is $shl (left shift) -- not the right pattern"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [2:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0000_0001;
|
||||
assign y = a % (MBASE << shiftamt);
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mod
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Positive: shifter Y also drives an external probe (extra fanout OK)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [1:0] shiftamt,
|
||||
output [11:0] y,
|
||||
output [7:0] probe
|
||||
);
|
||||
localparam [7:0] MBASE = 8'b0010_0000;
|
||||
wire [7:0] divisor = MBASE >> shiftamt;
|
||||
assign y = a % divisor;
|
||||
assign probe = divisor;
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
opt_clean
|
||||
# The original $shr must be preserved because it drives \probe; the
|
||||
# rewrite only retargets the $mod, replacing it with $and + a new mask
|
||||
# shifter.
|
||||
select -assert-count 0 t:$mod
|
||||
select -assert-count 1 t:$and
|
||||
select -assert-count 2 t:$shr
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
log -header "Negative: B_SIGNED=1 on $mod with one-hot at MSB of mbase"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [11:0] a,
|
||||
input [2:0] shiftamt,
|
||||
output [11:0] y
|
||||
);
|
||||
wire signed [7:0] divisor = $signed(8'b1000_0000) >>> shiftamt;
|
||||
assign y = a % divisor;
|
||||
endmodule
|
||||
EOT
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mod
|
||||
design -reset
|
||||
log -pop
|
||||
Loading…
Reference in New Issue