mirror of https://github.com/YosysHQ/yosys.git
Add negopt pass with comprehensive pattern matching
This commit introduces the negopt pass with pre/post optimization modes for handling negation patterns in arithmetic circuits. Pre-optimization patterns (expose for tree balancing): - manual2sub: (a + ~b) + 1 → a - b - sub2neg: a - b → a + (-b) - negexpand: -(a + b) → (-a) + (-b) [with output width fix] - negneg: -(-a) → a - negmux: -(s ? a : b) → s ? (-a) : (-b) Post-optimization patterns (cleanup/rebuild): - negrebuild: (-a) + (-b) → -(a + b) - muxneg: s ? (-a) : (-b) → -(s ? a : b) - neg2sub: a + (-b) → a - b All patterns use nusers() for fanout checking (standard Yosys style). Comprehensive test coverage with positive/negative cases and formal verification via equiv_opt.
This commit is contained in:
parent
7c70026610
commit
643427d9c9
|
|
@ -33,3 +33,20 @@ PEEPOPT_PATTERN += passes/silimate/peepopt_muxinvprop.pmg
|
|||
|
||||
passes/silimate/peepopt_muxmode.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||
|
||||
OBJS += passes/silimate/negopt.o
|
||||
GENFILES += passes/silimate/peepopt_negopt.h
|
||||
passes/silimate/negopt.o: passes/silimate/peepopt_negopt.h
|
||||
$(eval $(call add_extra_objs,passes/silimate/peepopt_negopt.h))
|
||||
|
||||
PEEPOPT_PATTERN = passes/silimate/peepopt_manual2sub.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_sub2neg.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_negexpand.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_negneg.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_negmux.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_negrebuild.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_muxneg.pmg
|
||||
PEEPOPT_PATTERN += passes/silimate/peepopt_neg2sub.pmg
|
||||
|
||||
passes/silimate/peepopt_negopt.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Silimate, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc.
|
||||
//
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
bool did_something;
|
||||
|
||||
#include "passes/silimate/peepopt_negopt.h"
|
||||
|
||||
struct NegoptPass : public Pass {
|
||||
NegoptPass() : Pass("negopt", "optimize negation patterns in arithmetic") { }
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" negopt [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass optimizes negation patterns in arithmetic expressions.\n");
|
||||
log("\n");
|
||||
log(" -pre\n");
|
||||
log(" Run pre-optimization transformations:\n" );
|
||||
log(" - manual2sub: a + ~b + 1 => a - b\n" );
|
||||
log(" - sub2neg: a - b => a + (-b)\n" );
|
||||
log(" - negexpand: -(a + b) => (-a) + (-b)\n");
|
||||
log(" - negneg: -(-a) => a\n" );
|
||||
log(" - negmux: -(s?a:b) => s?(-a):(-b)\n");
|
||||
log("\n");
|
||||
log(" -post\n");
|
||||
log(" Run post-optimization transformations:\n" );
|
||||
log(" - negrebuild: (-a)+(-b) => -(a + b)\n");
|
||||
log(" - muxneg: s?(-a):(-b) => -(s?a:b)\n");
|
||||
log(" - neg2sub: a + (-b) => a - b\n" );
|
||||
log("\n");
|
||||
log("When called without options, both -pre and -post are executed.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool run_pre = false;
|
||||
bool run_post = false;
|
||||
|
||||
log_header(design, "Executing NEGOPT pass (optimize negation patterns).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-pre") {
|
||||
run_pre = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-post") {
|
||||
run_post = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (!run_pre && !run_post) {
|
||||
run_pre = true;
|
||||
run_post = true;
|
||||
}
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
if (run_pre) {
|
||||
did_something = true;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
peepopt_pm pm(module);
|
||||
pm.setup(module->selected_cells());
|
||||
|
||||
pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first
|
||||
pm.run_sub2neg();
|
||||
pm.run_negexpand();
|
||||
pm.run_negneg();
|
||||
pm.run_negmux();
|
||||
}
|
||||
}
|
||||
|
||||
if (run_post) {
|
||||
did_something = true;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
peepopt_pm pm(module);
|
||||
pm.setup(module->selected_cells());
|
||||
|
||||
pm.run_negrebuild();
|
||||
pm.run_muxneg();
|
||||
pm.run_neg2sub();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} NegoptPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
pattern manual2sub
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Canonicalize manual 2's complement subtraction:
|
||||
// Case A: (a + ~b) + 1 ===> a - b
|
||||
// Case B: a + (~b + 1) ===> a - b
|
||||
//
|
||||
// Note: Fanout checking includes module connections to avoid breaking
|
||||
// designs where intermediate results are used by output assignments.
|
||||
//
|
||||
|
||||
state <SigSpec> minuend subtrahend result_sig
|
||||
state <bool> is_signed
|
||||
state <SigSpec> inner_y
|
||||
|
||||
// 1. Match the "root" add (the one that produces the final result)
|
||||
match root_add
|
||||
select root_add->type == $add
|
||||
set result_sig port(root_add, \Y)
|
||||
set is_signed root_add->getParam(ID::A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
// 2. Case A: (a + ~b) + 1
|
||||
// Check if root_add has a constant 1
|
||||
code root_add inner_y
|
||||
{
|
||||
SigSpec pa = root_add->getPort(ID::A);
|
||||
SigSpec pb = root_add->getPort(ID::B);
|
||||
|
||||
auto is_one = [](SigSpec s) {
|
||||
if (!s.is_fully_const()) return false;
|
||||
Const c = s.as_const();
|
||||
for (int i = 0; i < c.size(); i++) {
|
||||
if (i == 0 && c[i] != State::S1) return false;
|
||||
if (i > 0 && c[i] != State::S0) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (is_one(pa)) {
|
||||
inner_y = pb;
|
||||
} else if (is_one(pb)) {
|
||||
inner_y = pa;
|
||||
} else {
|
||||
branch;
|
||||
reject;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// Find the inner add
|
||||
match inner_add_A
|
||||
select inner_add_A->type == $add
|
||||
index <SigSpec> port(inner_add_A, \Y) === inner_y
|
||||
select nusers(port(inner_add_A, \Y)) == 2
|
||||
endmatch
|
||||
|
||||
// Find the NOT gate on one of the ports of inner_add_A
|
||||
match not_gate_A
|
||||
select not_gate_A->type == $not
|
||||
filter not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::A) || not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::B)
|
||||
set subtrahend port(not_gate_A, \A)
|
||||
endmatch
|
||||
|
||||
code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
|
||||
{
|
||||
|
||||
// Require consistent signedness on the root add.
|
||||
if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
if (inner_add_A->getParam(ID::A_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
if (not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::A))
|
||||
minuend = inner_add_A->getPort(ID::B);
|
||||
else
|
||||
minuend = inner_add_A->getPort(ID::A);
|
||||
|
||||
// Create the subtraction cell
|
||||
log("manual2sub in %s: Found (a + ~b) + 1 pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig));
|
||||
Cell *sub = module->addSub(NEW_ID, minuend, subtrahend, result_sig, is_signed);
|
||||
|
||||
// Let fixup_parameters handle width adjustments
|
||||
sub->fixup_parameters();
|
||||
|
||||
// Remove old cells
|
||||
autoremove(root_add);
|
||||
autoremove(inner_add_A);
|
||||
autoremove(not_gate_A);
|
||||
|
||||
did_something = true;
|
||||
accept;
|
||||
}
|
||||
endcode
|
||||
|
||||
// 3. Case B: a + (~b + 1)
|
||||
code root_add
|
||||
// Just fall through to the next match
|
||||
endcode
|
||||
|
||||
// Find the inner add on either port of root_add
|
||||
match inner_add_B
|
||||
select inner_add_B->type == $add
|
||||
filter inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A) || inner_add_B->getPort(ID::Y) == root_add->getPort(ID::B)
|
||||
select nusers(port(inner_add_B, \Y)) == 2
|
||||
endmatch
|
||||
|
||||
// Check if inner_add_B has a constant 1 and a NOT gate
|
||||
match not_gate_B
|
||||
select not_gate_B->type == $not
|
||||
filter not_gate_B->getPort(ID::Y) == inner_add_B->getPort(ID::A) || not_gate_B->getPort(ID::Y) == inner_add_B->getPort(ID::B)
|
||||
endmatch
|
||||
|
||||
code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
|
||||
{
|
||||
|
||||
// Require consistent signedness on the root add.
|
||||
if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
if (inner_add_B->getParam(ID::A_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
SigSpec pa = inner_add_B->getPort(ID::A);
|
||||
SigSpec pb = inner_add_B->getPort(ID::B);
|
||||
SigSpec not_y = not_gate_B->getPort(ID::Y);
|
||||
|
||||
auto is_one = [](SigSpec s) {
|
||||
if (!s.is_fully_const()) return false;
|
||||
Const c = s.as_const();
|
||||
for (int i = 0; i < c.size(); i++) {
|
||||
if (i == 0 && c[i] != State::S1) return false;
|
||||
if (i > 0 && c[i] != State::S0) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool valid = false;
|
||||
if (is_one(pa) && pb == not_y) valid = true;
|
||||
if (is_one(pb) && pa == not_y) valid = true;
|
||||
|
||||
if (!valid) reject;
|
||||
|
||||
subtrahend = not_gate_B->getPort(ID::A);
|
||||
if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A))
|
||||
minuend = root_add->getPort(ID::B);
|
||||
else
|
||||
minuend = root_add->getPort(ID::A);
|
||||
|
||||
// Create the subtraction cell
|
||||
log("manual2sub in %s: Found a + (~b + 1) pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig));
|
||||
Cell *sub = module->addSub(NEW_ID, minuend, subtrahend, result_sig, is_signed);
|
||||
|
||||
// Let fixup_parameters handle width adjustments
|
||||
sub->fixup_parameters();
|
||||
|
||||
// Remove old cells
|
||||
autoremove(root_add);
|
||||
autoremove(inner_add_B);
|
||||
autoremove(not_gate_B);
|
||||
|
||||
did_something = true;
|
||||
accept;
|
||||
}
|
||||
endcode
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
pattern muxneg
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Factor negation out of mux
|
||||
//
|
||||
// s ? (-a) : (-b) ===> -(s ? a : b)
|
||||
//
|
||||
|
||||
state <SigSpec> mux_a mux_b mux_s mux_y neg_a_in neg_b_in
|
||||
state <bool> neg_a_signed neg_b_signed
|
||||
|
||||
match mux
|
||||
select mux->type == $mux
|
||||
set mux_a port(mux, \A)
|
||||
set mux_b port(mux, \B)
|
||||
set mux_s port(mux, \S)
|
||||
set mux_y port(mux, \Y)
|
||||
endmatch
|
||||
|
||||
match neg_a
|
||||
select neg_a->type == $neg
|
||||
select nusers(port(neg_a, \Y)) == 2
|
||||
index <SigSpec> port(neg_a, \Y) === mux_a
|
||||
set neg_a_in port(neg_a, \A)
|
||||
set neg_a_signed neg_a->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
match neg_b
|
||||
select neg_b->type == $neg
|
||||
select nusers(port(neg_b, \Y)) == 2
|
||||
index <SigSpec> port(neg_b, \Y) === mux_b
|
||||
set neg_b_in port(neg_b, \A)
|
||||
set neg_b_signed neg_b->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
code mux_a mux_b mux_s mux_y neg_a_in neg_b_in neg_a_signed neg_b_signed
|
||||
if (neg_a_signed != neg_b_signed)
|
||||
reject;
|
||||
|
||||
{
|
||||
int width = GetSize(mux_y);
|
||||
|
||||
SigSpec mux_out = module->addWire(NEW_ID, width);
|
||||
Cell *new_mux = module->addMux(NEW_ID, neg_a_in, neg_b_in, mux_s, mux_out);
|
||||
Cell *new_neg = module->addNeg(NEW_ID, mux_out, mux_y, neg_a_signed);
|
||||
|
||||
log("muxneg pattern in %s: mux=%s, neg_a=%s, neg_b=%s\n",
|
||||
log_id(module), log_id(mux), log_id(neg_a), log_id(neg_b));
|
||||
|
||||
new_mux->fixup_parameters();
|
||||
new_neg->fixup_parameters();
|
||||
autoremove(mux);
|
||||
autoremove(neg_a);
|
||||
autoremove(neg_b);
|
||||
did_something = true;
|
||||
}
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
pattern neg2sub
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Convert addition with negation back to subtraction
|
||||
//
|
||||
// a + (-b) ===> a - b
|
||||
//
|
||||
|
||||
state <SigSpec> add_a add_b add_y neg_a
|
||||
state <bool> add_a_signed add_b_signed neg_signed
|
||||
state <bool> neg_on_a
|
||||
|
||||
match add
|
||||
select add->type == $add
|
||||
set add_a port(add, \A)
|
||||
set add_b port(add, \B)
|
||||
set add_y port(add, \Y)
|
||||
set add_a_signed add->getParam(\A_SIGNED).as_bool()
|
||||
set add_b_signed add->getParam(\B_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
code neg_on_a
|
||||
neg_on_a = true;
|
||||
branch;
|
||||
neg_on_a = false;
|
||||
endcode
|
||||
|
||||
match neg
|
||||
select neg->type == $neg
|
||||
select nusers(port(neg, \Y)) == 2
|
||||
index <SigSpec> port(neg, \Y) === (neg_on_a ? add_a : add_b)
|
||||
set neg_a port(neg, \A)
|
||||
set neg_signed neg->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
code add_a add_b add_y neg_a neg_on_a add_a_signed add_b_signed neg_signed
|
||||
if (add_a_signed != add_b_signed)
|
||||
reject;
|
||||
|
||||
if (neg_signed != (neg_on_a ? add_a_signed : add_b_signed))
|
||||
reject;
|
||||
|
||||
{
|
||||
Cell *new_sub;
|
||||
if (neg_on_a)
|
||||
new_sub = module->addSub(NEW_ID, add_b, neg_a, add_y, add_b_signed);
|
||||
else
|
||||
new_sub = module->addSub(NEW_ID, add_a, neg_a, add_y, add_a_signed);
|
||||
|
||||
log("neg2sub pattern in %s: add=%s, neg=%s\n",
|
||||
log_id(module), log_id(add), log_id(neg));
|
||||
|
||||
new_sub->fixup_parameters();
|
||||
autoremove(add);
|
||||
autoremove(neg);
|
||||
did_something = true;
|
||||
}
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
pattern negexpand
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Expand negation over addition
|
||||
//
|
||||
// -(a + b) ===> (-a) + (-b)
|
||||
//
|
||||
|
||||
state <SigSpec> neg_a neg_y add_a add_b
|
||||
state <bool> a_signed
|
||||
|
||||
match neg
|
||||
select neg->type == $neg
|
||||
set neg_a port(neg, \A)
|
||||
set neg_y port(neg, \Y)
|
||||
set a_signed neg->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
match add
|
||||
select add->type == $add
|
||||
index <SigSpec> port(add, \Y) === neg_a
|
||||
select nusers(port(add, \Y)) == 2
|
||||
set add_a port(add, \A)
|
||||
set add_b port(add, \B)
|
||||
endmatch
|
||||
|
||||
code neg_a neg_y add_a add_b a_signed
|
||||
if (add->getParam(\A_SIGNED).as_bool() != a_signed)
|
||||
reject;
|
||||
if (add->getParam(\B_SIGNED).as_bool() != a_signed)
|
||||
reject;
|
||||
|
||||
{
|
||||
// Use output width for negations to handle overflow correctly
|
||||
int width = GetSize(neg_y);
|
||||
SigSpec neg_add_a = module->addWire(NEW_ID, width);
|
||||
Cell *neg_a_cell = module->addNeg(NEW_ID, add_a, neg_add_a, a_signed);
|
||||
|
||||
SigSpec neg_add_b = module->addWire(NEW_ID, width);
|
||||
Cell *neg_b_cell = module->addNeg(NEW_ID, add_b, neg_add_b, a_signed);
|
||||
|
||||
Cell *new_add = module->addAdd(NEW_ID, neg_add_a, neg_add_b, neg_y, a_signed);
|
||||
|
||||
log("negexpand pattern in %s: neg=%s, add=%s\n",
|
||||
log_id(module), log_id(neg), log_id(add));
|
||||
|
||||
neg_a_cell->fixup_parameters();
|
||||
neg_b_cell->fixup_parameters();
|
||||
new_add->fixup_parameters();
|
||||
autoremove(neg);
|
||||
autoremove(add);
|
||||
did_something = true;
|
||||
}
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
pattern negmux
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Distribute negation over mux
|
||||
//
|
||||
// -(s ? a : b) ===> s ? (-a) : (-b)
|
||||
//
|
||||
|
||||
state <SigSpec> neg_a neg_y mux_a mux_b mux_s
|
||||
state <bool> a_signed
|
||||
|
||||
match neg
|
||||
select neg->type == $neg
|
||||
set neg_a port(neg, \A)
|
||||
set neg_y port(neg, \Y)
|
||||
set a_signed neg->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
match mux
|
||||
select mux->type == $mux
|
||||
index <SigSpec> port(mux, \Y) === neg_a
|
||||
select nusers(port(mux, \Y)) == 2
|
||||
set mux_a port(mux, \A)
|
||||
set mux_b port(mux, \B)
|
||||
set mux_s port(mux, \S)
|
||||
endmatch
|
||||
|
||||
code neg_a neg_y mux_a mux_b mux_s a_signed
|
||||
{
|
||||
int width = GetSize(neg_y);
|
||||
|
||||
SigSpec neg_mux_a = module->addWire(NEW_ID, width);
|
||||
Cell *neg_a_cell = module->addNeg(NEW_ID, mux_a, neg_mux_a, a_signed);
|
||||
|
||||
SigSpec neg_mux_b = module->addWire(NEW_ID, width);
|
||||
Cell *neg_b_cell = module->addNeg(NEW_ID, mux_b, neg_mux_b, a_signed);
|
||||
|
||||
Cell *new_mux = module->addMux(NEW_ID, neg_mux_a, neg_mux_b, mux_s, neg_y);
|
||||
|
||||
log("negmux pattern in %s: neg=%s, mux=%s\n",
|
||||
log_id(module), log_id(neg), log_id(mux));
|
||||
|
||||
neg_a_cell->fixup_parameters();
|
||||
neg_b_cell->fixup_parameters();
|
||||
new_mux->fixup_parameters();
|
||||
autoremove(neg);
|
||||
autoremove(mux);
|
||||
did_something = true;
|
||||
}
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
pattern negneg
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Simplify double negation
|
||||
//
|
||||
// -(-a) ===> a
|
||||
//
|
||||
|
||||
state <SigSpec> neg1_a neg1_y neg2_a
|
||||
|
||||
match neg1
|
||||
select neg1->type == $neg
|
||||
set neg1_a port(neg1, \A)
|
||||
set neg1_y port(neg1, \Y)
|
||||
endmatch
|
||||
|
||||
match neg2
|
||||
select neg2->type == $neg
|
||||
index <SigSpec> port(neg2, \Y) === neg1_a
|
||||
select nusers(port(neg2, \Y)) == 2
|
||||
set neg2_a port(neg2, \A)
|
||||
endmatch
|
||||
|
||||
code neg1_a neg1_y neg2_a
|
||||
module->connect(neg1_y, neg2_a);
|
||||
|
||||
log("negneg pattern in %s: neg1=%s, neg2=%s\n",
|
||||
log_id(module), log_id(neg1), log_id(neg2));
|
||||
|
||||
autoremove(neg1);
|
||||
autoremove(neg2);
|
||||
did_something = true;
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
pattern negrebuild
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Rebuild addition of negations into negation of addition
|
||||
//
|
||||
// (-a) + (-b) ===> -(a + b)
|
||||
//
|
||||
|
||||
state <SigSpec> add_a add_b add_y neg1_a neg2_a
|
||||
state <bool> add_signed add_b_signed neg1_signed neg2_signed
|
||||
|
||||
match add
|
||||
select add->type == $add
|
||||
set add_a port(add, \A)
|
||||
set add_b port(add, \B)
|
||||
set add_y port(add, \Y)
|
||||
set add_signed add->getParam(\A_SIGNED).as_bool()
|
||||
set add_b_signed add->getParam(\B_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
match neg1
|
||||
select neg1->type == $neg
|
||||
select nusers(port(neg1, \Y)) == 2
|
||||
index <SigSpec> port(neg1, \Y) === add_a
|
||||
set neg1_a port(neg1, \A)
|
||||
set neg1_signed neg1->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
match neg2
|
||||
select neg2->type == $neg
|
||||
select nusers(port(neg2, \Y)) == 2
|
||||
index <SigSpec> port(neg2, \Y) === add_b
|
||||
set neg2_a port(neg2, \A)
|
||||
set neg2_signed neg2->getParam(\A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
code add_a add_b add_y neg1_a neg2_a add_signed add_b_signed neg1_signed neg2_signed
|
||||
if (add_signed != add_b_signed)
|
||||
reject;
|
||||
|
||||
if (neg1_signed != add_signed || neg2_signed != add_signed)
|
||||
reject;
|
||||
|
||||
{
|
||||
int width = GetSize(add_y);
|
||||
|
||||
SigSpec sum = module->addWire(NEW_ID, width);
|
||||
Cell *new_add = module->addAdd(NEW_ID, neg1_a, neg2_a, sum, add_signed);
|
||||
Cell *new_neg = module->addNeg(NEW_ID, sum, add_y, add_signed);
|
||||
|
||||
log("negrebuild pattern in %s: add=%s, neg1=%s, neg2=%s\n",
|
||||
log_id(module), log_id(add), log_id(neg1), log_id(neg2));
|
||||
|
||||
new_add->fixup_parameters();
|
||||
new_neg->fixup_parameters();
|
||||
autoremove(add);
|
||||
autoremove(neg1);
|
||||
autoremove(neg2);
|
||||
did_something = true;
|
||||
}
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
pattern sub2neg
|
||||
//
|
||||
// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license.
|
||||
//
|
||||
// Convert subtraction to addition with negation
|
||||
//
|
||||
// a - b ===> a + (-b)
|
||||
//
|
||||
|
||||
state <SigSpec> sub_a sub_b sub_y
|
||||
state <bool> a_signed b_signed
|
||||
|
||||
match sub
|
||||
select sub->type == $sub
|
||||
filter !sub->getPort(\B).is_fully_const()
|
||||
set sub_a port(sub, \A)
|
||||
set sub_b port(sub, \B)
|
||||
set sub_y port(sub, \Y)
|
||||
set a_signed sub->getParam(\A_SIGNED).as_bool()
|
||||
set b_signed sub->getParam(\B_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
code sub_a sub_b sub_y a_signed b_signed
|
||||
if (a_signed != b_signed)
|
||||
reject;
|
||||
|
||||
{
|
||||
int width = GetSize(sub_y);
|
||||
SigSpec neg_y = module->addWire(NEW_ID, width);
|
||||
Cell *neg = module->addNeg(NEW_ID, sub_b, neg_y, b_signed);
|
||||
Cell *add = module->addAdd(NEW_ID, sub_a, neg_y, sub_y, a_signed);
|
||||
|
||||
log("sub2neg pattern in %s: sub=%s -> neg=%s, add=%s\n",
|
||||
log_id(module), log_id(sub), log_id(neg), log_id(add));
|
||||
|
||||
neg->fixup_parameters();
|
||||
add->fixup_parameters();
|
||||
autoremove(sub);
|
||||
did_something = true;
|
||||
}
|
||||
accept;
|
||||
endcode
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
log -header "Positive case: (a + ~b) + 1 => a - b => a + (-b)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire [7:0] a;
|
||||
input wire [7:0] b;
|
||||
output wire [7:0] y;
|
||||
|
||||
assign y = (a + ~b) + 1;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 1 t:$neg
|
||||
select -assert-none t:$not
|
||||
select -assert-none t:$sub
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Positive case B: 1 + (a + ~b) => a - b => a + (-b)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire [7:0] a;
|
||||
input wire [7:0] b;
|
||||
output wire [7:0] y;
|
||||
|
||||
assign y = 1 + (a + ~b);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 1 t:$neg
|
||||
select -assert-none t:$not
|
||||
select -assert-none t:$sub
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative case: fanout on inner add output"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y, z);
|
||||
input wire [7:0] a;
|
||||
input wire [7:0] b;
|
||||
output wire [7:0] y;
|
||||
output wire [7:0] z;
|
||||
(* keep *) wire [7:0] s;
|
||||
assign s = a + ~b;
|
||||
assign y = s + 1;
|
||||
assign z = s + a;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
# Should NOT transform due to fanout on inner add output (marked with keep)
|
||||
select -assert-count 3 t:$add
|
||||
select -assert-count 1 t:$not
|
||||
select -assert-none t:$sub
|
||||
select -assert-none t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
log -header "Simple positive case"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
input wire s;
|
||||
output wire signed [7:0] y;
|
||||
assign y = s ? (-a) : (-b);
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert negopt -post
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mux
|
||||
select -assert-count 1 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "With intermediate signals"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
input wire s;
|
||||
output wire signed [7:0] y;
|
||||
wire signed [7:0] neg_a;
|
||||
wire signed [7:0] neg_b;
|
||||
assign neg_a = -a;
|
||||
assign neg_b = -b;
|
||||
assign y = s ? neg_a : neg_b;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -post
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mux
|
||||
select -assert-count 1 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
log -header "Simple positive case (negation on port B)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [8:0] y;
|
||||
assign y = a + (-b);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -post
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$sub
|
||||
select -assert-none t:$add
|
||||
select -assert-none t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Positive case (negation on port A)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [8:0] y;
|
||||
assign y = (-a) + b;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -post
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$sub
|
||||
select -assert-none t:$add
|
||||
select -assert-none t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
log -header "Simple positive case (same width)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [7:0] y;
|
||||
assign y = -(a + b);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 2 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Width extension case (output wider than inputs)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [9:0] y;
|
||||
assign y = -(a + b);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
# Negations should use output width (9 bits) not input width (8 bits)
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 2 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative case: fanout on add output"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y, z);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [7:0] y;
|
||||
output wire signed [7:0] z;
|
||||
(* keep *) wire signed [7:0] sum;
|
||||
assign sum = a + b;
|
||||
assign y = -sum;
|
||||
assign z = sum;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
# Should NOT transform due to extra fanout on add output
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 1 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
log -header "Simple positive case"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
input wire s;
|
||||
output wire signed [7:0] y;
|
||||
assign y = -(s ? a : b);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mux
|
||||
select -assert-count 2 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "With intermediate signal"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
input wire s;
|
||||
output wire signed [7:0] y;
|
||||
wire signed [7:0] m;
|
||||
assign m = s ? a : b;
|
||||
assign y = -m;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mux
|
||||
select -assert-count 2 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative case: mux output has extra fanout"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y, z);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
input wire s;
|
||||
output wire signed [7:0] y;
|
||||
output wire signed [7:0] z;
|
||||
(* keep *) wire signed [7:0] m;
|
||||
assign m = s ? a : b;
|
||||
assign y = -m;
|
||||
assign z = m;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
# Should NOT transform due to extra fanout on mux output
|
||||
select -assert-count 1 t:$mux
|
||||
select -assert-count 1 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
log -header "Simple positive case"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, y);
|
||||
input wire signed [7:0] a;
|
||||
output wire signed [7:0] y;
|
||||
assign y = -(-a);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-none t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "With intermediate signal"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, y);
|
||||
input wire signed [7:0] a;
|
||||
output wire signed [7:0] y;
|
||||
wire signed [7:0] neg_a;
|
||||
assign neg_a = -a;
|
||||
assign y = -neg_a;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-none t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative case: extra fanout on inner neg"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, y, z);
|
||||
input wire signed [7:0] a;
|
||||
output wire signed [7:0] y;
|
||||
output wire signed [7:0] z;
|
||||
(* keep *) wire signed [7:0] neg_a;
|
||||
assign neg_a = -a;
|
||||
assign y = -neg_a;
|
||||
assign z = neg_a;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
# Should NOT transform due to extra fanout on inner neg output
|
||||
select -assert-count 2 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
log -header "Simple positive case (same width)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [7:0] y;
|
||||
assign y = (-a) + (-b);
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt
|
||||
check -assert
|
||||
equiv_opt -assert negopt -post
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 1 t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
log -header "Simple positive case"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, y);
|
||||
input wire signed [7:0] a;
|
||||
input wire signed [7:0] b;
|
||||
output wire signed [7:0] y;
|
||||
assign y = a - b;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$add
|
||||
select -assert-count 1 t:$neg
|
||||
select -assert-none t:$sub
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative case: constant subtrahend"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, y);
|
||||
input wire signed [7:0] a;
|
||||
output wire signed [7:0] y;
|
||||
assign y = a - 8'd5;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert negopt -pre
|
||||
design -load postopt
|
||||
# Should NOT transform because constant subtraction is kept as $sub
|
||||
select -assert-count 1 t:$sub
|
||||
select -assert-none t:$add
|
||||
select -assert-none t:$neg
|
||||
design -reset
|
||||
log -pop
|
||||
Loading…
Reference in New Issue