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:
tondapusili 2026-02-03 17:09:03 -08:00
parent 7c70026610
commit 643427d9c9
18 changed files with 1084 additions and 0 deletions

View File

@ -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 $<,$^)

119
passes/silimate/negopt.cc Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

44
tests/silimate/muxneg.ys Normal file
View File

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

41
tests/silimate/neg2sub.ys Normal file
View File

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

View File

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

69
tests/silimate/negmux.ys Normal file
View File

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

60
tests/silimate/negneg.ys Normal file
View File

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

View File

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

39
tests/silimate/sub2neg.ys Normal file
View File

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