3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-01 14:47:53 +00:00
This commit is contained in:
nella 2026-04-01 08:55:20 +02:00 committed by nella
parent b6d656e932
commit 42c309347b
2 changed files with 23 additions and 86 deletions

View file

@ -1,3 +1,6 @@
// Replaces chains of $add/$sub and $macc cells with carry-save adder trees, reducing multi-operand
// addition to logarithmic depth. ref. paper: Zimmermann, "Architectures for Adders"
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
#include "kernel/macc.h" #include "kernel/macc.h"
@ -88,6 +91,7 @@ struct AluInfo {
return GetSize(bi) == 1 && bi[0] == State::S0 && GetSize(ci) == 1 && ci[0] == State::S0; return GetSize(bi) == 1 && bi[0] == State::S0 && GetSize(ci) == 1 && ci[0] == State::S0;
} }
// A cell is chainable if it's a plain add/sub with unused carries.
bool is_chainable(Cell* cell) bool is_chainable(Cell* cell)
{ {
if (!(is_add(cell) || is_subtract(cell))) if (!(is_add(cell) || is_subtract(cell)))
@ -134,6 +138,7 @@ struct Rewriter
return consumer; return consumer;
} }
// Find cells that consumes another cell's output.
dict<Cell*, Cell*> find_parents(const pool<Cell*>& candidates) dict<Cell*, Cell*> find_parents(const pool<Cell*>& candidates)
{ {
dict<Cell*, Cell*> parent_of; dict<Cell*, Cell*> parent_of;
@ -207,7 +212,7 @@ struct Rewriter
const pool<Cell*>& chain, const pool<Cell*>& chain,
Cell* root, Cell* root,
const dict<Cell*, Cell*>& parent_of, const dict<Cell*, Cell*>& parent_of,
int& correction int& neg_compensation
) { ) {
pool<SigBit> chain_bits = internal_bits(chain); pool<SigBit> chain_bits = internal_bits(chain);
dict<Cell*, bool> negated; dict<Cell*, bool> negated;
@ -229,7 +234,7 @@ struct Rewriter
} }
std::vector<Operand> operands; std::vector<Operand> operands;
correction = 0; neg_compensation = 0;
for (auto cell : chain) { for (auto cell : chain) {
bool cell_neg; bool cell_neg;
@ -247,21 +252,21 @@ struct Rewriter
if (!overlaps(a, chain_bits)) { if (!overlaps(a, chain_bits)) {
bool neg = cell_neg; bool neg = cell_neg;
operands.push_back({a, a_signed, neg}); operands.push_back({a, a_signed, neg});
if (neg) correction++; if (neg) neg_compensation++;
} }
if (!overlaps(b, chain_bits)) { if (!overlaps(b, chain_bits)) {
bool neg = cell_neg ^ b_sub; bool neg = cell_neg ^ b_sub;
operands.push_back({b, b_signed, neg}); operands.push_back({b, b_signed, neg});
if (neg) correction++; if (neg) neg_compensation++;
} }
} }
return operands; return operands;
} }
bool extract_macc_operands(Cell* cell, std::vector<Operand>& operands, int& correction) bool extract_macc_operands(Cell* cell, std::vector<Operand>& operands, int& neg_compensation)
{ {
Macc macc(cell); Macc macc(cell);
correction = 0; neg_compensation = 0;
for (auto& term : macc.terms) { for (auto& term : macc.terms) {
// Bail on multiplication // Bail on multiplication
@ -269,7 +274,7 @@ struct Rewriter
return false; return false;
operands.push_back({term.in_a, term.is_signed, term.do_subtract}); operands.push_back({term.in_a, term.is_signed, term.do_subtract});
if (term.do_subtract) if (term.do_subtract)
correction++; neg_compensation++;
} }
return true; return true;
} }
@ -307,6 +312,7 @@ struct Rewriter
int depth; int depth;
}; };
// Group ready operands into triplets and compress via full adders until two operands remain.
std::pair<SigSpec, SigSpec> reduce_wallace(std::vector<SigSpec>& sigs, int width, int& fa_count) std::pair<SigSpec, SigSpec> reduce_wallace(std::vector<SigSpec>& sigs, int width, int& fa_count)
{ {
std::vector<DepthSig> ops; std::vector<DepthSig> ops;
@ -356,7 +362,7 @@ struct Rewriter
void replace_with_csa_tree( void replace_with_csa_tree(
std::vector<Operand>& operands, std::vector<Operand>& operands,
SigSpec result_y, SigSpec result_y,
int correction, int neg_compensation,
const char* desc const char* desc
) { ) {
int width = GetSize(result_y); int width = GetSize(result_y);
@ -370,8 +376,8 @@ struct Rewriter
extended.push_back(s); extended.push_back(s);
} }
if (correction > 0) if (neg_compensation > 0)
extended.push_back(SigSpec(correction, width)); extended.push_back(SigSpec(neg_compensation, width));
int fa_count; int fa_count;
auto [a, b] = reduce_wallace(extended, width, fa_count); auto [a, b] = reduce_wallace(extended, width, fa_count);
@ -416,14 +422,12 @@ struct Rewriter
for (auto c : chain) for (auto c : chain)
processed.insert(c); processed.insert(c);
int correction; int neg_compensation;
auto operands = extract_chain_operands( auto operands = extract_chain_operands(chain, root, parent_of, neg_compensation);
chain, root, parent_of, correction);
if (operands.size() < 3) if (operands.size() < 3)
continue; continue;
replace_with_csa_tree(operands, root->getPort(ID::Y), replace_with_csa_tree(operands, root->getPort(ID::Y), neg_compensation, "Replaced add/sub chain");
correction, "Replaced add/sub chain");
for (auto cell : chain) for (auto cell : chain)
module->remove(cell); module->remove(cell);
} }
@ -433,14 +437,13 @@ struct Rewriter
{ {
for (auto cell : cells.macc) { for (auto cell : cells.macc) {
std::vector<Operand> operands; std::vector<Operand> operands;
int correction; int neg_compensation;
if (!extract_macc_operands(cell, operands, correction)) if (!extract_macc_operands(cell, operands, neg_compensation))
continue; continue;
if (operands.size() < 3) if (operands.size() < 3)
continue; continue;
replace_with_csa_tree(operands, cell->getPort(ID::Y), replace_with_csa_tree(operands, cell->getPort(ID::Y), neg_compensation, "Replaced $macc");
correction, "Replaced $macc");
module->remove(cell); module->remove(cell);
} }
} }
@ -458,8 +461,7 @@ void run(Module* module) {
} }
struct CsaTreePass : public Pass { struct CsaTreePass : public Pass {
CsaTreePass() : Pass("csa_tree", CsaTreePass() : Pass("csa_tree", "convert add/sub/macc chains to carry-save adder trees") {}
"convert add/sub/macc chains to carry-save adder trees") {}
void help() override void help() override
{ {

View file

@ -1,65 +0,0 @@
read_verilog <<EOT
module bench(
input [7:0] a, b, c, d, e, f, g, h,
output [7:0] y
);
assign y = a + b + c + d + e + f + g + h;
endmodule
EOT
hierarchy -auto-top
proc
opt
techmap
abc -g AND,OR,XOR
select -assert-min 236 t:$_AND_ t:$_OR_ t:$_XOR_ %u
design -reset
read_verilog <<EOT
module bench(
input [7:0] a, b, c, d, e, f, g, h,
output [7:0] y
);
assign y = a + b + c + d + e + f + g + h;
endmodule
EOT
hierarchy -auto-top
proc
opt
csa_tree
techmap
abc -g AND,OR,XOR
select -assert-max 235 t:$_AND_ t:$_OR_ t:$_XOR_ %u
design -reset
read_verilog <<EOT
module bench(
input [7:0] a, b, c, d, e, f, g, h,
output [7:0] y
);
assign y = a + b + c + d + e + f + g + h;
endmodule
EOT
hierarchy -auto-top
proc
opt
techmap
abc -D 1
select -assert-min 240 t:$_AND_ t:$_NAND_ t:$_OR_ t:$_NOR_ t:$_XOR_ t:$_XNOR_ t:$_NOT_ %u
design -reset
read_verilog <<EOT
module bench(
input [7:0] a, b, c, d, e, f, g, h,
output [7:0] y
);
assign y = a + b + c + d + e + f + g + h;
endmodule
EOT
hierarchy -auto-top
proc
opt
csa_tree
techmap
abc -D 1
select -assert-max 236 t:$_AND_ t:$_NAND_ t:$_OR_ t:$_NOR_ t:$_XOR_ t:$_XNOR_ t:$_NOT_ %u
design -reset