This commit is contained in:
Krystine Dawn Sherwin 2026-07-29 13:25:46 +12:00 committed by GitHub
commit 017e4b83dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 160 additions and 19 deletions

View File

@ -41,18 +41,22 @@ struct AlumaccWorker
std::vector<RTLIL::Cell*> cells;
RTLIL::SigSpec a, b, c, y;
std::vector<tuple<bool, bool, bool, bool, bool, RTLIL::SigSpec>> cmp;
bool is_signed, invert_b;
bool is_signed, invert_b, oversized;
RTLIL::Cell *alu_cell;
RTLIL::SigSpec cached_lt, cached_slt, cached_gt, cached_sgt, cached_eq, cached_ne;
RTLIL::SigSpec cached_cf, cached_of, cached_sf;
RTLIL::SigSpec get_lt(bool is_signed) {
if (is_signed) {
RTLIL::SigSpec get_lt(bool this_signed) {
if (this_signed) {
if (GetSize(cached_slt) == 0) {
get_of();
get_sf();
cached_slt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
if (is_signed && oversized) {
cached_slt = cached_sf;
} else {
get_of();
cached_slt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
}
}
return cached_slt;
@ -65,10 +69,10 @@ struct AlumaccWorker
}
}
RTLIL::SigSpec get_gt(bool is_signed) {
if (is_signed) {
RTLIL::SigSpec get_gt(bool this_signed) {
if (this_signed) {
if (GetSize(cached_sgt) == 0) {
get_lt(is_signed);
get_lt(this_signed);
get_eq();
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_slt, cached_eq);
cached_sgt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
@ -77,7 +81,7 @@ struct AlumaccWorker
return cached_sgt;
} else {
if (GetSize(cached_gt) == 0) {
get_lt(is_signed);
get_lt(this_signed);
get_eq();
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
@ -112,7 +116,14 @@ struct AlumaccWorker
if (GetSize(cached_of) == 0) {
cached_of = {alu_cell->getPort(ID::CO), alu_cell->getPort(ID::CI)};
log_assert(GetSize(cached_of) >= 2);
cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(cached_of)-1], cached_of[GetSize(cached_of)-2]);
if (oversized) {
auto max_input = max(GetSize(a), GetSize(b));
log_assert(max_input >= 1);
cached_of = alu_cell->module->Xor(NEW_ID, cached_of[max_input], cached_of[max_input-1]);
} else {
log_assert(GetSize(y) >= 1);
cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(y)], cached_of[GetSize(y)-1]);
}
}
return cached_of;
}
@ -120,7 +131,12 @@ struct AlumaccWorker
RTLIL::SigSpec get_sf() {
if (GetSize(cached_sf) == 0) {
cached_sf = alu_cell->getPort(ID::Y);
cached_sf = cached_sf[GetSize(cached_sf)-1];
log_assert(GetSize(cached_sf) >= 1);
if (oversized && !is_signed) {
cached_sf = cached_sf[max(GetSize(a), GetSize(b))-1];
} else {
cached_sf = cached_sf[GetSize(y)-1];
}
}
return cached_sf;
}
@ -357,13 +373,17 @@ struct AlumaccWorker
alunode->cells.push_back(n->cell);
alunode->is_signed = a_signed;
alunode->invert_b = subtract_b;
alunode->oversized = max(GetSize(A), GetSize(B)) < GetSize(n->y);
alunode->a = A;
alunode->b = B;
alunode->c = C;
alunode->y = n->y;
sig_alu[RTLIL::SigSig(A, B)].insert(alunode);
// only consider merging if the output is at least as wide as the inputs
if (max(GetSize(A), GetSize(B)) <= GetSize(n->y)) {
sig_alu[RTLIL::SigSig(A, B)].insert(alunode);
}
delete_nodes.insert(n);
next_macc:;
}
@ -410,6 +430,20 @@ struct AlumaccWorker
eq_cells.push_back(cell);
}
auto cmp_mergeable = [](const alunode_t *node, const SigSpec cmp_A, const SigSpec cmp_B, bool is_signed) {
if (!node->invert_b || node->c != State::S1) {
// comparators need subtraction
return false;
}
if (is_signed != node->is_signed) {
if (GetSize(cmp_A) != GetSize(cmp_B)) {
// mismatched input widths are problematic when mixing signedness
return false;
}
}
return true;
};
for (auto cell : lge_cells)
{
log(" creating $alu model for %s (%s):", cell, cell->type.unescape());
@ -425,14 +459,14 @@ struct AlumaccWorker
alunode_t *n = nullptr;
for (auto node : sig_alu[RTLIL::SigSig(A, B)])
if (node->invert_b && node->c == State::S1) {
if (cmp_mergeable(node, A, B, is_signed)) {
n = node;
break;
}
if (n == nullptr) {
for (auto node : sig_alu[RTLIL::SigSig(B, A)])
if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
if (cmp_mergeable(node, B, A, is_signed)) {
n = node;
cmp_less = !cmp_less;
std::swap(A, B);
@ -448,6 +482,7 @@ struct AlumaccWorker
n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B)));
n->is_signed = is_signed;
n->invert_b = true;
n->oversized = false;
sig_alu[RTLIL::SigSig(A, B)].insert(n);
log(" new $alu\n");
} else {
@ -470,14 +505,14 @@ struct AlumaccWorker
alunode_t *n = nullptr;
for (auto node : sig_alu[RTLIL::SigSig(A, B)])
if (node->invert_b && node->c == State::S1) {
if (cmp_mergeable(node, A, B, is_signed)) {
n = node;
break;
}
if (n == nullptr) {
for (auto node : sig_alu[RTLIL::SigSig(B, A)])
if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
if (cmp_mergeable(node, B, A, is_signed)) {
n = node;
break;
}

1
tests/alumacc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
uut_*.*

View File

@ -26,8 +26,18 @@ assign ne = ra != rb;
endmodule
EOT
proc
equiv_opt -assert alumacc
alumacc
design -load postopt
select -assert-count 1 t:$alu
design -reset
read_verilog << EOT
module top(input signed a, b, output x, output [1:0] y);
assign y = $unsigned(a) - $unsigned(b);
assign x = a > b;
endmodule
EOT
hierarchy
wreduce
equiv_opt -assert alumacc

View File

@ -5,4 +5,99 @@ sys.path.append("..")
import gen_tests_makefile
import argparse
import random
from itertools import product
TEMPLATE=r"""read_rtlil << EOT
module \top
wire width {a_width} input 1 signed \a
wire width {b_width} input 2 signed \b
wire output 3 \x
wire width {y_width} output 4 \y
cell ${c1} \c1
parameter \A_SIGNED {c1_signed}
parameter \B_SIGNED {c1_signed}
parameter \A_WIDTH {a_width}
parameter \B_WIDTH {b_width}
parameter \Y_WIDTH 1
connect \A \a
connect \B \b
connect \Y \x
end
cell ${c2} \c2
parameter \A_SIGNED {c2_signed}
parameter \B_SIGNED {c2_signed}
parameter \A_WIDTH {a_width}
parameter \B_WIDTH {b_width}
parameter \Y_WIDTH {y_width}
connect \A \a
connect \B \b
connect \Y \y
end
end
EOT
equiv_opt -assert alumacc
design -load postopt
{postopt_assert}
"""
EQ = ["eq", "eqx", "ne", "nex"]
LGE = ["lt", "gt"]
CMP = EQ + LGE
ARITH = ["add", "sub"]
SIGNED = [0, 1]
WIDTH = list(range(1, 4))
parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-S', '--seed', type = int, help = 'seed for PRNG')
args = parser.parse_args()
seed = args.seed
if seed is None:
seed = random.randrange(sys.maxsize)
print("alumacc PRNG seed: %d" % seed)
random.seed(seed)
count = 0
for c1_signed, c2, c2_signed, a_width in product(
SIGNED, [EQ, LGE, ARITH], SIGNED, WIDTH
):
c1 = random.choice(LGE)
if isinstance(c2, list):
c2 = random.choice(c2)
b_widths: list[int]
if a_width == 2:
b_widths = [1, 2, 3]
else:
b_widths = [a_width]
y_widths: list[int]
if c2 in CMP:
y_widths = [1]
else:
y_widths = b_widths
for b_width, y_width in product(b_widths, y_widths):
out_cells = "t:$alu"
for c in c1, c2:
if c in EQ:
# LGE cells will always remap to $alu, but EQ cells only will if merged
out_cells += f" t:${c}"
undersized = c2 in ARITH and y_width < max(a_width, b_width)
mixed_sign = c1_signed != c2_signed and a_width != b_width
if c2 != "add" and not undersized and not mixed_sign:
# cells correctly merged
postopt_assert = f"select -assert-count 1 {out_cells}"
else:
# currently these all produce 2, but assert-max allows for future improvement
postopt_assert = f"select -assert-max 2 {out_cells}"
with open(f"uut_{'s' if c1_signed else 'u'}{c1}_{'s' if c2_signed else 'u'}{c2}_{a_width}_{b_width}_{y_width}.ys", "w") as f:
print(str.format_map(TEMPLATE, locals()), file=f)
count = count + 1
# print(count)
gen_tests_makefile.generate(["--yosys-scripts"])