mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'main' of github.com:silimate/yosys into annotate_ff_width
This commit is contained in:
commit
96ee0f6ec5
|
|
@ -22,11 +22,13 @@ OBJS += passes/opt/opt_lut_ins.o
|
|||
OBJS += passes/opt/opt_ffinv.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
OBJS += passes/opt/muxpack.o
|
||||
|
||||
OBJS += passes/opt/opt_addcin.o
|
||||
OBJS += passes/opt/opt_andor_pmux.o
|
||||
OBJS += passes/opt/opt_argmax.o
|
||||
OBJS += passes/opt/opt_balance_tree.o
|
||||
OBJS += passes/opt/opt_parallel_prefix.o
|
||||
OBJS += passes/opt/opt_prienc.o
|
||||
OBJS += passes/opt/opt_addcin.o
|
||||
|
||||
OBJS += passes/opt/peepopt.o
|
||||
GENFILES += passes/opt/peepopt_pm.h
|
||||
|
|
|
|||
|
|
@ -0,0 +1,775 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2026 Akash Levy <akash@silimate.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static int clog2_int(int x)
|
||||
{
|
||||
int r = 0;
|
||||
while ((1 << r) < x)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool is_power_of_two(int x)
|
||||
{
|
||||
return x > 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
static Const packed_table_const(const vector<uint64_t> &values, int elem_width)
|
||||
{
|
||||
vector<State> bits(values.size() * elem_width, State::S0);
|
||||
for (int i = 0; i < GetSize(values); i++)
|
||||
for (int b = 0; b < elem_width && b < 64; b++)
|
||||
if ((values[i] >> b) & 1ULL)
|
||||
bits[i * elem_width + b] = State::S1;
|
||||
return Const(bits);
|
||||
}
|
||||
|
||||
static Const packed_valid_const(const vector<int> &valid)
|
||||
{
|
||||
vector<State> bits(valid.size(), State::S0);
|
||||
for (int i = 0; i < GetSize(valid); i++)
|
||||
if (valid[i])
|
||||
bits[i] = State::S1;
|
||||
return Const(bits);
|
||||
}
|
||||
|
||||
struct OptArgmaxWorker
|
||||
{
|
||||
struct TestVector {
|
||||
vector<int> valid;
|
||||
vector<uint64_t> index;
|
||||
vector<uint64_t> values;
|
||||
};
|
||||
|
||||
struct Candidate {
|
||||
Wire *out_wire = nullptr;
|
||||
Wire *valid_wire = nullptr;
|
||||
SigSpec valid_sig;
|
||||
SigSpec index_sig;
|
||||
SigSpec values_sig;
|
||||
std::string index_name;
|
||||
std::string values_name;
|
||||
int width = 0;
|
||||
int index_width = 0;
|
||||
int value_width = 0;
|
||||
Cell *anchor = nullptr;
|
||||
IdString anchor_port;
|
||||
};
|
||||
|
||||
struct OutputCone {
|
||||
pool<Cell *> cells;
|
||||
pool<SigBit> leaves;
|
||||
bool saw_bmux = false;
|
||||
bool saw_lt = false;
|
||||
};
|
||||
|
||||
struct InputBus {
|
||||
SigSpec sig;
|
||||
std::string name;
|
||||
int entries = 0;
|
||||
int elem_width = 0;
|
||||
};
|
||||
|
||||
struct Record {
|
||||
SigBit valid;
|
||||
SigSpec value;
|
||||
SigSpec index;
|
||||
};
|
||||
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
dict<SigBit, Cell *> bit_to_driver;
|
||||
pool<SigBit> input_port_bits;
|
||||
Cell *cell = nullptr;
|
||||
|
||||
int min_width = 4;
|
||||
int max_width = 64;
|
||||
int regions_rewritten = 0;
|
||||
int cells_added = 0;
|
||||
|
||||
OptArgmaxWorker(Module *module) : module(module), sigmap(module)
|
||||
{
|
||||
build_indexes();
|
||||
}
|
||||
|
||||
bool is_sequential(Cell *c)
|
||||
{
|
||||
return c->type.in(
|
||||
ID($ff), ID($dff), ID($dffe), ID($adff), ID($adffe),
|
||||
ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), ID($dffsre),
|
||||
ID($_DFF_P_), ID($_DFF_N_),
|
||||
ID($_DFFE_PP_), ID($_DFFE_PN_), ID($_DFFE_NP_), ID($_DFFE_NN_),
|
||||
ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
|
||||
ID($_DFF_NP0_), ID($_DFF_NP1_), ID($_DFF_NN0_), ID($_DFF_NN1_),
|
||||
ID($dlatch), ID($adlatch), ID($dlatchsr),
|
||||
ID($mem), ID($mem_v2), ID($meminit), ID($meminit_v2),
|
||||
ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2),
|
||||
ID($fsm),
|
||||
ID($assert), ID($assume), ID($cover), ID($live), ID($fair),
|
||||
ID($print), ID($check),
|
||||
ID($anyconst), ID($anyseq), ID($allconst), ID($allseq),
|
||||
ID($initstate));
|
||||
}
|
||||
|
||||
void build_indexes()
|
||||
{
|
||||
for (auto c : module->cells()) {
|
||||
if (is_sequential(c))
|
||||
continue;
|
||||
for (auto &conn : c->connections()) {
|
||||
if (!c->output(conn.first))
|
||||
continue;
|
||||
for (auto bit : sigmap(conn.second)) {
|
||||
if (!bit.wire)
|
||||
continue;
|
||||
auto it = bit_to_driver.find(bit);
|
||||
if (it == bit_to_driver.end())
|
||||
bit_to_driver[bit] = c;
|
||||
else if (it->second != c)
|
||||
it->second = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto w : module->wires()) {
|
||||
if (!w->port_input)
|
||||
continue;
|
||||
for (auto bit : sigmap(SigSpec(w)))
|
||||
if (bit.wire)
|
||||
input_port_bits.insert(bit);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_cone(SigSpec from, pool<Cell *> &cone_cells, pool<SigBit> &leaf_bits,
|
||||
int max_cone_cells, int max_leaf_bits)
|
||||
{
|
||||
pool<SigBit> visited;
|
||||
std::queue<SigBit> worklist;
|
||||
for (auto bit : sigmap(from)) {
|
||||
if (!bit.wire)
|
||||
continue;
|
||||
if (visited.insert(bit).second)
|
||||
worklist.push(bit);
|
||||
}
|
||||
|
||||
while (!worklist.empty()) {
|
||||
SigBit bit = worklist.front();
|
||||
worklist.pop();
|
||||
|
||||
if (input_port_bits.count(bit)) {
|
||||
leaf_bits.insert(bit);
|
||||
if (GetSize(leaf_bits) > max_leaf_bits)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Cell *drv = bit_to_driver.at(bit, nullptr);
|
||||
if (drv == nullptr) {
|
||||
leaf_bits.insert(bit);
|
||||
if (GetSize(leaf_bits) > max_leaf_bits)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cone_cells.insert(drv).second)
|
||||
continue;
|
||||
if (GetSize(cone_cells) > max_cone_cells)
|
||||
return false;
|
||||
|
||||
for (auto &conn : drv->connections()) {
|
||||
if (!drv->input(conn.first))
|
||||
continue;
|
||||
for (auto in_bit : sigmap(conn.second)) {
|
||||
if (!in_bit.wire)
|
||||
continue;
|
||||
if (visited.insert(in_bit).second)
|
||||
worklist.push(in_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OutputCone summarize_output_cone(const pool<Cell *> &cone_cells, pool<SigBit> leaf_bits)
|
||||
{
|
||||
OutputCone cone;
|
||||
cone.cells = cone_cells;
|
||||
cone.leaves = std::move(leaf_bits);
|
||||
for (auto c : cone_cells) {
|
||||
cone.saw_bmux = cone.saw_bmux || c->type == ID($bmux);
|
||||
cone.saw_lt = cone.saw_lt || c->type == ID($lt);
|
||||
}
|
||||
return cone;
|
||||
}
|
||||
|
||||
bool cone_has_required_shape(const OutputCone &cone, int value_width)
|
||||
{
|
||||
return cone.saw_bmux && (cone.saw_lt || value_width == 1);
|
||||
}
|
||||
|
||||
bool leaves_are_candidate_inputs(const pool<SigBit> &leaf_bits, const Candidate &cand)
|
||||
{
|
||||
pool<SigBit> allowed;
|
||||
for (auto bit : sigmap(cand.valid_sig))
|
||||
if (bit.wire)
|
||||
allowed.insert(bit);
|
||||
for (auto bit : sigmap(cand.index_sig))
|
||||
if (bit.wire)
|
||||
allowed.insert(bit);
|
||||
for (auto bit : sigmap(cand.values_sig))
|
||||
if (bit.wire)
|
||||
allowed.insert(bit);
|
||||
|
||||
for (auto bit : leaf_bits)
|
||||
if (!allowed.count(bit))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool find_anchor_driver(Wire *out_wire, Cell *&anchor, IdString &anchor_port)
|
||||
{
|
||||
for (auto bit : sigmap(SigSpec(out_wire))) {
|
||||
Cell *drv = bit_to_driver.at(bit, nullptr);
|
||||
if (drv == nullptr)
|
||||
continue;
|
||||
for (auto &conn : drv->connections()) {
|
||||
if (!drv->output(conn.first))
|
||||
continue;
|
||||
for (auto out_bit : sigmap(conn.second)) {
|
||||
if (out_bit == bit) {
|
||||
anchor = drv;
|
||||
anchor_port = conn.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t value_mask(int width)
|
||||
{
|
||||
if (width >= 64)
|
||||
return ~0ULL;
|
||||
return (1ULL << width) - 1;
|
||||
}
|
||||
|
||||
void add_vector(vector<TestVector> &vectors, const vector<int> &valid,
|
||||
const vector<uint64_t> &index, const vector<uint64_t> &values)
|
||||
{
|
||||
vectors.push_back({valid, index, values});
|
||||
}
|
||||
|
||||
vector<TestVector> make_test_vectors(int width, int value_width)
|
||||
{
|
||||
vector<TestVector> vectors;
|
||||
vector<uint64_t> identity(width), reverse(width), inc(width), dec(width), equal(width, 7);
|
||||
uint64_t mask = value_mask(value_width);
|
||||
for (int i = 0; i < width; i++) {
|
||||
identity[i] = i;
|
||||
reverse[i] = width - 1 - i;
|
||||
inc[i] = uint64_t(i + 1) & mask;
|
||||
dec[i] = uint64_t(width - i) & mask;
|
||||
}
|
||||
|
||||
vector<int> valid(width, 0);
|
||||
add_vector(vectors, valid, identity, inc);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
valid.assign(width, 0);
|
||||
valid[i] = 1;
|
||||
add_vector(vectors, valid, identity, inc);
|
||||
}
|
||||
|
||||
valid.assign(width, 1);
|
||||
add_vector(vectors, valid, identity, inc);
|
||||
add_vector(vectors, valid, identity, dec);
|
||||
add_vector(vectors, valid, identity, equal);
|
||||
add_vector(vectors, valid, reverse, inc);
|
||||
add_vector(vectors, valid, reverse, dec);
|
||||
|
||||
for (int i = 0; i + 1 < width; i++) {
|
||||
vector<uint64_t> vals(width, 3);
|
||||
valid.assign(width, 0);
|
||||
valid[i] = 1;
|
||||
valid[i + 1] = 1;
|
||||
vals[i] = 1;
|
||||
vals[i + 1] = 9;
|
||||
add_vector(vectors, valid, identity, vals);
|
||||
vals[i] = 5;
|
||||
vals[i + 1] = 5;
|
||||
add_vector(vectors, valid, identity, vals);
|
||||
}
|
||||
|
||||
if (width > 2) {
|
||||
vector<uint64_t> vals(width, 0);
|
||||
valid.assign(width, 0);
|
||||
valid[0] = 1;
|
||||
valid[width - 1] = 1;
|
||||
vals[0] = 2;
|
||||
vals[width - 1] = 11;
|
||||
add_vector(vectors, valid, identity, vals);
|
||||
vals[0] = 13;
|
||||
vals[width - 1] = 13;
|
||||
add_vector(vectors, valid, identity, vals);
|
||||
}
|
||||
|
||||
return vectors;
|
||||
}
|
||||
|
||||
int expected_argmax(const TestVector &tv, int width, int value_width)
|
||||
{
|
||||
uint64_t mask = value_mask(value_width);
|
||||
int best_idx = 0;
|
||||
bool best_valid = tv.valid[0] != 0;
|
||||
uint64_t best_value = tv.values[tv.index[0]] & mask;
|
||||
|
||||
for (int k = 1; k < width; k++) {
|
||||
bool cand_valid = tv.valid[k] != 0;
|
||||
uint64_t cand_value = tv.values[tv.index[k]] & mask;
|
||||
if (!best_valid && cand_valid) {
|
||||
best_idx = k;
|
||||
best_valid = true;
|
||||
best_value = cand_value;
|
||||
} else if (best_valid && cand_valid && best_value < cand_value) {
|
||||
best_idx = k;
|
||||
best_value = cand_value;
|
||||
}
|
||||
}
|
||||
|
||||
return best_idx;
|
||||
}
|
||||
|
||||
bool fingerprint(const Candidate &cand)
|
||||
{
|
||||
ConstEval ce(module);
|
||||
SigSpec out_sig = sigmap(SigSpec(cand.out_wire));
|
||||
SigSpec valid_sig = sigmap(cand.valid_sig);
|
||||
SigSpec index_sig = sigmap(cand.index_sig);
|
||||
SigSpec values_sig = sigmap(cand.values_sig);
|
||||
|
||||
vector<TestVector> vectors = make_test_vectors(cand.width, cand.value_width);
|
||||
for (auto &tv : vectors) {
|
||||
ce.push();
|
||||
ce.set(valid_sig, packed_valid_const(tv.valid));
|
||||
ce.set(index_sig, packed_table_const(tv.index, cand.index_width));
|
||||
ce.set(values_sig, packed_table_const(tv.values, cand.value_width));
|
||||
|
||||
SigSpec out = out_sig;
|
||||
SigSpec undef;
|
||||
bool ok = ce.eval(out, undef);
|
||||
ce.pop();
|
||||
if (!ok || !out.is_fully_const())
|
||||
return false;
|
||||
|
||||
int actual = out.as_const().as_int();
|
||||
int expected = expected_argmax(tv, cand.width, cand.value_width);
|
||||
if (actual != expected)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SigSpec zext(SigSpec sig, int width)
|
||||
{
|
||||
sig = sigmap(sig);
|
||||
if (GetSize(sig) > width)
|
||||
return sig.extract(0, width);
|
||||
while (GetSize(sig) < width)
|
||||
sig.append(State::S0);
|
||||
return sig;
|
||||
}
|
||||
|
||||
SigSpec emit_not(Cell *anchor, SigSpec a)
|
||||
{
|
||||
Cell *cell = anchor;
|
||||
cells_added++;
|
||||
return module->Not(NEW_ID2_SUFFIX("argmax_not"), a);
|
||||
}
|
||||
|
||||
SigSpec emit_and(Cell *anchor, SigSpec a, SigSpec b)
|
||||
{
|
||||
Cell *cell = anchor;
|
||||
cells_added++;
|
||||
return module->And(NEW_ID2_SUFFIX("argmax_and"), a, b);
|
||||
}
|
||||
|
||||
SigSpec emit_or(Cell *anchor, SigSpec a, SigSpec b)
|
||||
{
|
||||
Cell *cell = anchor;
|
||||
cells_added++;
|
||||
return module->Or(NEW_ID2_SUFFIX("argmax_or"), a, b);
|
||||
}
|
||||
|
||||
SigSpec emit_lt(Cell *anchor, SigSpec a, SigSpec b)
|
||||
{
|
||||
Cell *cell = anchor;
|
||||
cells_added++;
|
||||
return module->Lt(NEW_ID2_SUFFIX("argmax_lt"), a, b);
|
||||
}
|
||||
|
||||
SigSpec emit_mux(Cell *anchor, SigSpec a, SigSpec b, SigSpec s)
|
||||
{
|
||||
Cell *cell = anchor;
|
||||
cells_added++;
|
||||
return module->Mux(NEW_ID2_SUFFIX("argmax_mux"), a, b, s);
|
||||
}
|
||||
|
||||
SigSpec emit_bmux(Cell *anchor, SigSpec a, SigSpec s)
|
||||
{
|
||||
Cell *cell = anchor;
|
||||
cells_added++;
|
||||
return module->Bmux(NEW_ID2_SUFFIX("argmax_val"), a, s);
|
||||
}
|
||||
|
||||
Record combine(Cell *anchor, const Record &lhs, const Record &rhs)
|
||||
{
|
||||
SigSpec lhs_invalid = emit_not(anchor, SigSpec(lhs.valid));
|
||||
SigSpec value_lt = emit_lt(anchor, lhs.value, rhs.value);
|
||||
SigSpec valid_and_lt = emit_and(anchor, SigSpec(lhs.valid), value_lt);
|
||||
SigSpec take_reason = emit_or(anchor, lhs_invalid, valid_and_lt);
|
||||
SigSpec take_rhs = emit_and(anchor, SigSpec(rhs.valid), take_reason);
|
||||
|
||||
Record out;
|
||||
out.valid = emit_or(anchor, SigSpec(lhs.valid), SigSpec(rhs.valid))[0];
|
||||
out.value = emit_mux(anchor, lhs.value, rhs.value, take_rhs);
|
||||
out.index = emit_mux(anchor, lhs.index, rhs.index, take_rhs);
|
||||
return out;
|
||||
}
|
||||
|
||||
Record emit_tree_rec(Cell *anchor, const vector<Record> &leaves, int begin, int end)
|
||||
{
|
||||
log_assert(begin < end);
|
||||
if (begin + 1 == end)
|
||||
return leaves[begin];
|
||||
|
||||
int mid = begin + (end - begin) / 2;
|
||||
Record lhs = emit_tree_rec(anchor, leaves, begin, mid);
|
||||
Record rhs = emit_tree_rec(anchor, leaves, mid, end);
|
||||
return combine(anchor, lhs, rhs);
|
||||
}
|
||||
|
||||
SigSpec emit_argmax(const Candidate &cand)
|
||||
{
|
||||
vector<Record> leaves;
|
||||
SigSpec valid = sigmap(cand.valid_sig);
|
||||
SigSpec index_map = sigmap(cand.index_sig);
|
||||
SigSpec values = sigmap(cand.values_sig);
|
||||
|
||||
for (int k = 0; k < cand.width; k++) {
|
||||
SigSpec index = index_map.extract(k * cand.index_width, cand.index_width);
|
||||
SigSpec value = emit_bmux(cand.anchor, values, index);
|
||||
leaves.push_back({valid[k], value, SigSpec(Const(k, cand.index_width))});
|
||||
}
|
||||
|
||||
Record root = emit_tree_rec(cand.anchor, leaves, 0, GetSize(leaves));
|
||||
return zext(root.index, cand.index_width);
|
||||
}
|
||||
|
||||
void disconnect_old_output(const Candidate &cand)
|
||||
{
|
||||
pool<SigBit> target_bits;
|
||||
for (auto bit : sigmap(SigSpec(cand.out_wire)))
|
||||
if (bit.wire)
|
||||
target_bits.insert(bit);
|
||||
|
||||
pool<Cell *> seen_cells;
|
||||
for (auto target : target_bits) {
|
||||
Cell *drv = bit_to_driver.at(target, nullptr);
|
||||
if (drv == nullptr || seen_cells.count(drv))
|
||||
continue;
|
||||
seen_cells.insert(drv);
|
||||
|
||||
for (auto &conn : drv->connections()) {
|
||||
if (!drv->output(conn.first))
|
||||
continue;
|
||||
|
||||
SigSpec orig = conn.second;
|
||||
SigSpec replacement = orig;
|
||||
bool changed = false;
|
||||
Cell *cell = drv;
|
||||
Wire *dangling = module->addWire(NEW_ID2_SUFFIX("argmax_dangling"), GetSize(orig));
|
||||
for (int i = 0; i < GetSize(orig); i++) {
|
||||
if (target_bits.count(sigmap(orig[i]))) {
|
||||
replacement[i] = SigBit(dangling, i);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
drv->setPort(conn.first, replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool check_candidate(Candidate &cand, const OutputCone &cone)
|
||||
{
|
||||
if (cand.width < min_width || cand.width > max_width)
|
||||
return false;
|
||||
if (!is_power_of_two(cand.width))
|
||||
return false;
|
||||
if (cand.index_width != clog2_int(cand.width))
|
||||
return false;
|
||||
if (cand.value_width <= 0 || cand.value_width > 62)
|
||||
return false;
|
||||
|
||||
if (!cone_has_required_shape(cone, cand.value_width))
|
||||
return false;
|
||||
if (!leaves_are_candidate_inputs(cone.leaves, cand))
|
||||
return false;
|
||||
if (!find_anchor_driver(cand.out_wire, cand.anchor, cand.anchor_port))
|
||||
return false;
|
||||
|
||||
return fingerprint(cand);
|
||||
}
|
||||
|
||||
bool parse_indexed_port_name(Wire *wire, std::string &base, int &index)
|
||||
{
|
||||
std::string name = wire->name.str();
|
||||
size_t rbrack = name.size();
|
||||
if (rbrack == 0 || name[rbrack - 1] != ']')
|
||||
return false;
|
||||
size_t lbrack = name.rfind('[');
|
||||
if (lbrack == std::string::npos || lbrack + 1 >= rbrack - 1)
|
||||
return false;
|
||||
for (size_t i = lbrack + 1; i < rbrack - 1; i++)
|
||||
if (!isdigit(name[i]))
|
||||
return false;
|
||||
base = name.substr(0, lbrack);
|
||||
index = atoi(name.substr(lbrack + 1, rbrack - lbrack - 2).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<InputBus> collect_split_input_buses(const vector<Wire *> &inputs)
|
||||
{
|
||||
std::map<std::string, vector<std::pair<int, Wire *>>> groups;
|
||||
for (auto w : inputs) {
|
||||
std::string base;
|
||||
int index = -1;
|
||||
if (parse_indexed_port_name(w, base, index))
|
||||
groups[base].push_back({index, w});
|
||||
}
|
||||
|
||||
vector<InputBus> buses;
|
||||
for (auto &it : groups) {
|
||||
auto entries = it.second;
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](const std::pair<int, Wire *> &a, const std::pair<int, Wire *> &b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
if (entries.empty() || entries.front().first != 0)
|
||||
continue;
|
||||
bool contiguous = true;
|
||||
int elem_width = GetSize(entries.front().second);
|
||||
for (int i = 0; i < GetSize(entries); i++) {
|
||||
if (entries[i].first != i || GetSize(entries[i].second) != elem_width) {
|
||||
contiguous = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contiguous)
|
||||
continue;
|
||||
|
||||
SigSpec sig;
|
||||
for (auto &entry : entries)
|
||||
sig.append(SigSpec(entry.second));
|
||||
buses.push_back({sig, it.first, GetSize(entries), elem_width});
|
||||
}
|
||||
|
||||
return buses;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (module->has_processes_warn())
|
||||
return;
|
||||
|
||||
vector<Wire *> inputs;
|
||||
vector<Wire *> outputs;
|
||||
for (auto w : module->wires()) {
|
||||
if (w->port_input)
|
||||
inputs.push_back(w);
|
||||
if (w->port_output && !w->port_input)
|
||||
outputs.push_back(w);
|
||||
}
|
||||
|
||||
vector<Candidate> rewrites;
|
||||
pool<Wire *> claimed_outputs;
|
||||
for (auto out : outputs) {
|
||||
if (claimed_outputs.count(out))
|
||||
continue;
|
||||
int out_width = GetSize(out);
|
||||
if (out_width < 2)
|
||||
continue;
|
||||
|
||||
pool<Cell *> cone_cells;
|
||||
pool<SigBit> leaf_bits;
|
||||
int max_cone_cells = std::max(256, max_width * 96);
|
||||
int max_leaf_bits = max_width * (out_width + max_width) + max_width;
|
||||
if (!get_cone(SigSpec(out), cone_cells, leaf_bits,
|
||||
max_cone_cells, max_leaf_bits))
|
||||
continue;
|
||||
OutputCone cone = summarize_output_cone(cone_cells, std::move(leaf_bits));
|
||||
if (!cone.saw_bmux)
|
||||
continue;
|
||||
|
||||
for (auto valid : inputs) {
|
||||
int width = GetSize(valid);
|
||||
if (width < min_width || width > max_width)
|
||||
continue;
|
||||
if (clog2_int(width) != out_width)
|
||||
continue;
|
||||
|
||||
vector<InputBus> index_buses;
|
||||
vector<InputBus> values_buses;
|
||||
for (auto input : inputs) {
|
||||
if (input == valid)
|
||||
continue;
|
||||
if (GetSize(input) == width * out_width)
|
||||
index_buses.push_back({SigSpec(input), input->name.str(), width, out_width});
|
||||
if (GetSize(input) % width == 0)
|
||||
values_buses.push_back({SigSpec(input), input->name.str(), width, GetSize(input) / width});
|
||||
}
|
||||
|
||||
vector<InputBus> split_buses = collect_split_input_buses(inputs);
|
||||
for (auto bus : split_buses) {
|
||||
if (bus.entries == width && bus.elem_width == out_width)
|
||||
index_buses.push_back(bus);
|
||||
if (bus.entries == width)
|
||||
values_buses.push_back(bus);
|
||||
}
|
||||
|
||||
for (auto &index : index_buses) {
|
||||
for (auto &values : values_buses) {
|
||||
if (index.sig == values.sig)
|
||||
continue;
|
||||
Candidate cand;
|
||||
cand.out_wire = out;
|
||||
cand.valid_wire = valid;
|
||||
cand.valid_sig = SigSpec(valid);
|
||||
cand.index_sig = index.sig;
|
||||
cand.values_sig = values.sig;
|
||||
cand.index_name = index.name;
|
||||
cand.values_name = values.name;
|
||||
cand.width = width;
|
||||
cand.index_width = out_width;
|
||||
cand.value_width = values.elem_width;
|
||||
if (!check_candidate(cand, cone))
|
||||
continue;
|
||||
|
||||
rewrites.push_back(cand);
|
||||
claimed_outputs.insert(out);
|
||||
log(" %s: %s <- argmax(valid=%s, index=%s, values=%s) [N=%d, IW=%d, VW=%d]\n",
|
||||
log_id(module), log_id(out), log_id(valid), index.name.c_str(),
|
||||
values.name.c_str(), cand.width, cand.index_width, cand.value_width);
|
||||
goto next_output;
|
||||
}
|
||||
}
|
||||
}
|
||||
next_output:
|
||||
;
|
||||
}
|
||||
|
||||
for (auto &cand : rewrites) {
|
||||
cell = cand.anchor;
|
||||
SigSpec new_out = emit_argmax(cand);
|
||||
disconnect_old_output(cand);
|
||||
module->connect(SigSpec(cand.out_wire), new_out);
|
||||
regions_rewritten++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct OptArgmaxPass : public Pass
|
||||
{
|
||||
OptArgmaxPass() : Pass("opt_argmax",
|
||||
"detect and rewrite masked argmax loops into balanced compare trees") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
log("\n");
|
||||
log(" opt_argmax [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Detect combinational masked argmax loops of the form used by\n");
|
||||
log("read-after dependency logic and replace the serial loop-carried\n");
|
||||
log("index/update cone with a balanced tree of {valid,value,index}\n");
|
||||
log("comparators. Ties preserve the lower candidate index, matching a\n");
|
||||
log("strict '<' update condition; all-invalid inputs return index zero.\n");
|
||||
log("\n");
|
||||
log(" -max-width N, -max_width N\n");
|
||||
log(" maximum candidate count to consider (default 64).\n");
|
||||
log("\n");
|
||||
log(" -min-width N, -min_width N\n");
|
||||
log(" minimum candidate count to consider (default 4).\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing OPT_ARGMAX pass (masked argmax rewrite).\n");
|
||||
|
||||
int max_width = 64;
|
||||
int min_width = 4;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if ((args[argidx] == "-max-width" || args[argidx] == "-max_width") &&
|
||||
argidx + 1 < args.size()) {
|
||||
max_width = std::stoi(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if ((args[argidx] == "-min-width" || args[argidx] == "-min_width") &&
|
||||
argidx + 1 < args.size()) {
|
||||
min_width = std::stoi(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_regions = 0;
|
||||
int total_cells_added = 0;
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptArgmaxWorker worker(module);
|
||||
worker.max_width = max_width;
|
||||
worker.min_width = min_width;
|
||||
worker.run();
|
||||
total_regions += worker.regions_rewritten;
|
||||
total_cells_added += worker.cells_added;
|
||||
}
|
||||
|
||||
log("Rewrote %d argmax region(s); emitted %d new cell(s).\n",
|
||||
total_regions, total_cells_added);
|
||||
|
||||
if (total_regions)
|
||||
Yosys::run_pass("clean -purge");
|
||||
}
|
||||
} OptArgmaxPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -34,6 +34,14 @@ struct OptBalanceTreeWorker {
|
|||
|
||||
// Counts of each cell type that are getting balanced
|
||||
dict<IdString, int> cell_count;
|
||||
int sliced_add_count = 0;
|
||||
|
||||
struct SlicedAddContext {
|
||||
dict<SigBit, Cell*> bit_to_driver;
|
||||
dict<SigBit, int> bit_to_driver_index;
|
||||
dict<SigBit, pool<Cell*>> bit_to_sink;
|
||||
pool<SigBit> output_port_sigs;
|
||||
};
|
||||
|
||||
// Check if cell is of the right type and has matching input/output widths
|
||||
// Only allow cells with "natural" output widths (no truncation) to prevent
|
||||
|
|
@ -66,6 +74,28 @@ struct OptBalanceTreeWorker {
|
|||
return y_width >= natural_width;
|
||||
}
|
||||
|
||||
bool is_unsigned_add(Cell *cell)
|
||||
{
|
||||
return cell && is_right_type(cell, ID($add)) &&
|
||||
!cell->getParam(ID::A_SIGNED).as_bool() &&
|
||||
!cell->getParam(ID::B_SIGNED).as_bool();
|
||||
}
|
||||
|
||||
bool is_nonzero(const SigSpec &sig)
|
||||
{
|
||||
for (auto bit : sig)
|
||||
if (bit != State::S0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
SigSpec shift_summand(const SigSpec &sig, int offset)
|
||||
{
|
||||
SigSpec shifted(State::S0, offset);
|
||||
shifted.append(sig);
|
||||
return shifted;
|
||||
}
|
||||
|
||||
// Create a balanced binary tree from a vector of source signals
|
||||
SigSpec create_balanced_tree(vector<SigSpec> &sources, IdString cell_type, Cell* cell) {
|
||||
// Base case: if we have no sources, return an empty signal
|
||||
|
|
@ -140,25 +170,220 @@ struct OptBalanceTreeWorker {
|
|||
return out_wire;
|
||||
}
|
||||
|
||||
bool full_child_output_at(const SigSpec &sig, int pos, Cell *&child, int &child_width,
|
||||
SlicedAddContext &ctx)
|
||||
{
|
||||
child = nullptr;
|
||||
child_width = 0;
|
||||
if (pos >= GetSize(sig))
|
||||
return false;
|
||||
|
||||
SigBit bit = sig[pos];
|
||||
auto driver_it = ctx.bit_to_driver.find(bit);
|
||||
if (driver_it == ctx.bit_to_driver.end())
|
||||
return false;
|
||||
|
||||
Cell *candidate = driver_it->second;
|
||||
if (!is_unsigned_add(candidate))
|
||||
return false;
|
||||
|
||||
auto index_it = ctx.bit_to_driver_index.find(bit);
|
||||
if (index_it == ctx.bit_to_driver_index.end() || index_it->second != 0)
|
||||
return false;
|
||||
|
||||
SigSpec y = sigmap(candidate->getPort(ID::Y));
|
||||
child_width = GetSize(y);
|
||||
if (pos + child_width > GetSize(sig))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < child_width; i++)
|
||||
if (sig[pos + i] != y[i])
|
||||
return false;
|
||||
|
||||
child = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bit_is_partial_add_output(SigBit bit, SlicedAddContext &ctx)
|
||||
{
|
||||
auto driver_it = ctx.bit_to_driver.find(bit);
|
||||
if (driver_it == ctx.bit_to_driver.end())
|
||||
return false;
|
||||
return is_unsigned_add(driver_it->second);
|
||||
}
|
||||
|
||||
bool extract_sliced_operand(const SigSpec &sig, int base_offset, vector<SigSpec> &summands,
|
||||
pool<Cell*> &cluster, pool<Cell*> &visiting, SlicedAddContext &ctx, bool &saw_sliced_edge)
|
||||
{
|
||||
for (int i = 0; i < GetSize(sig); )
|
||||
{
|
||||
Cell *child = nullptr;
|
||||
int child_width = 0;
|
||||
if (full_child_output_at(sig, i, child, child_width, ctx))
|
||||
{
|
||||
if (i != 0 || child_width != GetSize(sig))
|
||||
saw_sliced_edge = true;
|
||||
if (!extract_sliced_add(child, base_offset + i, summands, cluster, visiting, ctx, saw_sliced_edge))
|
||||
return false;
|
||||
i += child_width;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bit_is_partial_add_output(sig[i], ctx))
|
||||
return false;
|
||||
|
||||
SigSpec leaf;
|
||||
int leaf_start = i;
|
||||
while (i < GetSize(sig))
|
||||
{
|
||||
Cell *next_child = nullptr;
|
||||
int next_child_width = 0;
|
||||
if (full_child_output_at(sig, i, next_child, next_child_width, ctx))
|
||||
break;
|
||||
if (bit_is_partial_add_output(sig[i], ctx))
|
||||
return false;
|
||||
leaf.append(sig[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (is_nonzero(leaf))
|
||||
summands.push_back(shift_summand(leaf, base_offset + leaf_start));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool extract_sliced_add(Cell *cell, int base_offset, vector<SigSpec> &summands,
|
||||
pool<Cell*> &cluster, pool<Cell*> &visiting, SlicedAddContext &ctx, bool &saw_sliced_edge)
|
||||
{
|
||||
if (!is_unsigned_add(cell) || visiting.count(cell))
|
||||
return false;
|
||||
|
||||
visiting.insert(cell);
|
||||
cluster.insert(cell);
|
||||
|
||||
for (IdString port : {ID::A, ID::B}) {
|
||||
SigSpec sig = sigmap(cell->getPort(port));
|
||||
if (!extract_sliced_operand(sig, base_offset, summands, cluster, visiting, ctx, saw_sliced_edge))
|
||||
return false;
|
||||
}
|
||||
|
||||
visiting.erase(cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operand_contains_full_child_output(const SigSpec &sig, Cell *child)
|
||||
{
|
||||
SigSpec y = sigmap(child->getPort(ID::Y));
|
||||
int width = GetSize(y);
|
||||
for (int pos = 0; pos + width <= GetSize(sig); pos++)
|
||||
{
|
||||
bool found = true;
|
||||
for (int i = 0; i < width; i++)
|
||||
if (sig[pos + i] != y[i]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_downstream_add_sink(Cell *cell, pool<Cell*> &consumed_cells, SlicedAddContext &ctx)
|
||||
{
|
||||
SigSpec y = sigmap(cell->getPort(ID::Y));
|
||||
for (auto bit : y)
|
||||
for (auto sink : ctx.bit_to_sink[bit])
|
||||
if (sink != cell && !consumed_cells.count(sink) && is_unsigned_add(sink))
|
||||
for (IdString port : {ID::A, ID::B})
|
||||
if (operand_contains_full_child_output(sigmap(sink->getPort(port)), cell))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sliced_cluster_has_external_fanout(Cell *head_cell, pool<Cell*> &cluster, pool<Cell*> &consumed_cells,
|
||||
SlicedAddContext &ctx)
|
||||
{
|
||||
for (auto cell : cluster)
|
||||
{
|
||||
if (cell == head_cell)
|
||||
continue;
|
||||
|
||||
SigSpec y = sigmap(cell->getPort(ID::Y));
|
||||
for (auto bit : y)
|
||||
{
|
||||
if (ctx.output_port_sigs.count(bit))
|
||||
return true;
|
||||
for (auto sink : ctx.bit_to_sink[bit])
|
||||
if (!cluster.count(sink) && !consumed_cells.count(sink))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_sliced_add_tree(Cell *head_cell, pool<Cell*> &consumed_cells, SlicedAddContext &ctx)
|
||||
{
|
||||
if (!is_unsigned_add(head_cell) || consumed_cells.count(head_cell) ||
|
||||
has_downstream_add_sink(head_cell, consumed_cells, ctx))
|
||||
return false;
|
||||
|
||||
vector<SigSpec> summands;
|
||||
pool<Cell*> cluster, visiting;
|
||||
bool saw_sliced_edge = false;
|
||||
if (!extract_sliced_add(head_cell, 0, summands, cluster, visiting, ctx, saw_sliced_edge))
|
||||
return false;
|
||||
if (!saw_sliced_edge || GetSize(cluster) <= 1 || GetSize(summands) <= 2)
|
||||
return false;
|
||||
if (sliced_cluster_has_external_fanout(head_cell, cluster, consumed_cells, ctx))
|
||||
return false;
|
||||
|
||||
log_debug(" Creating sliced add tree for %s with %d summands and %d cells...\n",
|
||||
log_id(head_cell), GetSize(summands), GetSize(cluster));
|
||||
|
||||
SigSpec tree_output = create_balanced_tree(summands, ID($add), head_cell);
|
||||
SigSpec head_output = sigmap(head_cell->getPort(ID::Y));
|
||||
int connect_width = std::min(head_output.size(), tree_output.size());
|
||||
module->connect(head_output.extract(0, connect_width), tree_output.extract(0, connect_width));
|
||||
if (head_output.size() > tree_output.size())
|
||||
module->connect(head_output.extract(connect_width, head_output.size() - connect_width),
|
||||
SigSpec(State::S0, head_output.size() - connect_width));
|
||||
|
||||
for (auto cell : cluster)
|
||||
consumed_cells.insert(cell);
|
||||
sliced_add_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
OptBalanceTreeWorker(Module *module, const vector<IdString> cell_types) : module(module), sigmap(module) {
|
||||
// Do for each cell type
|
||||
for (auto cell_type : cell_types) {
|
||||
// Index all of the nets in the module
|
||||
dict<SigSpec, Cell*> sig_to_driver;
|
||||
dict<SigSpec, pool<Cell*>> sig_to_sink;
|
||||
SlicedAddContext sliced_add_ctx;
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (cell->output(conn.first))
|
||||
sig_to_driver[sigmap(conn.second)] = cell;
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
if (cell->output(conn.first)) {
|
||||
sig_to_driver[sig] = cell;
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
sliced_add_ctx.bit_to_driver[sig[i]] = cell;
|
||||
sliced_add_ctx.bit_to_driver_index[sig[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell->input(conn.first))
|
||||
{
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
if (sig_to_sink.count(sig) == 0)
|
||||
sig_to_sink[sig] = pool<Cell*>();
|
||||
sig_to_sink[sig].insert(cell);
|
||||
for (auto bit : sig)
|
||||
sliced_add_ctx.bit_to_sink[bit].insert(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -172,13 +397,19 @@ struct OptBalanceTreeWorker {
|
|||
for (auto bit : sig) {
|
||||
if (wire->port_input)
|
||||
input_port_sigs.insert(bit);
|
||||
if (wire->port_output)
|
||||
if (wire->port_output) {
|
||||
output_port_sigs.insert(bit);
|
||||
sliced_add_ctx.output_port_sigs.insert(bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actual logic starts here
|
||||
pool<Cell*> consumed_cells;
|
||||
if (cell_type == ID($add))
|
||||
for (auto cell : module->selected_cells())
|
||||
try_sliced_add_tree(cell, consumed_cells, sliced_add_ctx);
|
||||
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
// If consumed or not the correct type, skip
|
||||
|
|
@ -362,16 +593,20 @@ struct OptBalanceTreePass : public Pass {
|
|||
|
||||
// Count of all cells that were packed
|
||||
dict<IdString, int> cell_count;
|
||||
int sliced_add_count = 0;
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptBalanceTreeWorker worker(module, cell_types);
|
||||
for (auto cell : worker.cell_count) {
|
||||
cell_count[cell.first] += cell.second;
|
||||
}
|
||||
sliced_add_count += worker.sliced_add_count;
|
||||
}
|
||||
|
||||
// Log stats
|
||||
for (auto cell_type : cell_types)
|
||||
log("Converted %d %s cells into trees.\n", cell_count[cell_type], log_id(cell_type));
|
||||
if (std::find(cell_types.begin(), cell_types.end(), ID($add)) != cell_types.end())
|
||||
log("Converted %d sliced $add chains into trees.\n", sliced_add_count);
|
||||
|
||||
// Clean up
|
||||
Yosys::run_pass("clean -purge");
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ OBJS += passes/silimate/cone_partition.o
|
|||
OBJS += passes/silimate/clkmerge.o
|
||||
OBJS += passes/silimate/opt_boundary.o
|
||||
OBJS += passes/silimate/opt_vps.o
|
||||
OBJS += passes/silimate/opt_compact_prefix.o
|
||||
OBJS += passes/silimate/infer_icg.o
|
||||
|
||||
OBJS += passes/silimate/opt_expand.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2026 Silimate Inc. <akash@silimate.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static int ceil_log2_int(int v)
|
||||
{
|
||||
int r = 0;
|
||||
int n = 1;
|
||||
while (n < v) {
|
||||
n <<= 1;
|
||||
r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
struct OptCompactPrefixWorker
|
||||
{
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
int max_width;
|
||||
dict<SigBit, Cell *> bit_drivers;
|
||||
Cell *ref_cell = nullptr;
|
||||
|
||||
int forward_rewrites = 0;
|
||||
int reverse_rewrites = 0;
|
||||
int old_cells_removed = 0;
|
||||
int new_cells_emitted = 0;
|
||||
|
||||
OptCompactPrefixWorker(Module *module, int max_width)
|
||||
: module(module), sigmap(module), max_width(max_width)
|
||||
{
|
||||
for (auto cell : module->cells()) {
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!cell->output(conn.first))
|
||||
continue;
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_drivers[bit] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Wire *port(const char *name)
|
||||
{
|
||||
return module->wire(RTLIL::escape_id(name));
|
||||
}
|
||||
|
||||
int count_cells(IdString type)
|
||||
{
|
||||
int n = 0;
|
||||
for (auto cell : module->cells())
|
||||
if (cell->type == type)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
bool sig_is_const_value(SigSpec sig, int64_t value)
|
||||
{
|
||||
sig = sigmap(sig);
|
||||
if (!sig.is_fully_const())
|
||||
return false;
|
||||
uint64_t uvalue = (uint64_t)value;
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
bool want = (i < 64) ? ((uvalue >> i) & 1) : (value < 0);
|
||||
if (sig[i] != (want ? State::S1 : State::S0))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int count_binop_const(IdString type, int64_t value)
|
||||
{
|
||||
int n = 0;
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type != type)
|
||||
continue;
|
||||
if (sig_is_const_value(cell->getPort(ID::A), value) ||
|
||||
sig_is_const_value(cell->getPort(ID::B), value))
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
bool has_binop_const_other_than(IdString type, int64_t value)
|
||||
{
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type != type)
|
||||
continue;
|
||||
bool a_const = sigmap(cell->getPort(ID::A)).is_fully_const();
|
||||
bool b_const = sigmap(cell->getPort(ID::B)).is_fully_const();
|
||||
if (a_const && !sig_is_const_value(cell->getPort(ID::A), value))
|
||||
return true;
|
||||
if (b_const && !sig_is_const_value(cell->getPort(ID::B), value))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int eval_bit_at_zero(SigBit bit, dict<SigBit, int> &cache, int depth)
|
||||
{
|
||||
bit = sigmap(bit);
|
||||
if (bit == State::S0) return 0;
|
||||
if (bit == State::S1) return 1;
|
||||
if (!bit.wire) return 0;
|
||||
|
||||
auto it = cache.find(bit);
|
||||
if (it != cache.end())
|
||||
return it->second;
|
||||
if (depth > 64)
|
||||
return 0;
|
||||
|
||||
cache[bit] = 0;
|
||||
Cell *drv = bit_drivers.at(bit, nullptr);
|
||||
if (!drv || !drv->hasPort(ID::Y) || !drv->hasPort(ID::A))
|
||||
return 0;
|
||||
|
||||
int bit_pos = -1;
|
||||
SigSpec y = sigmap(drv->getPort(ID::Y));
|
||||
for (int i = 0; i < GetSize(y); i++) {
|
||||
if (y[i] == bit) {
|
||||
bit_pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bit_pos < 0)
|
||||
return 0;
|
||||
|
||||
auto eval_sig = [&](SigSpec sig) -> int64_t {
|
||||
int64_t result = 0;
|
||||
for (int i = 0; i < GetSize(sig) && i < 62; i++)
|
||||
result |= ((int64_t)eval_bit_at_zero(sig[i], cache, depth + 1) << i);
|
||||
return result;
|
||||
};
|
||||
|
||||
int64_t av = eval_sig(drv->getPort(ID::A));
|
||||
int64_t bv = drv->hasPort(ID::B) ? eval_sig(drv->getPort(ID::B)) : 0;
|
||||
int64_t rv = 0;
|
||||
if (drv->type == ID($add)) rv = av + bv;
|
||||
else if (drv->type == ID($sub)) rv = av - bv;
|
||||
else if (drv->type == ID($and) || drv->type == ID($_AND_)) rv = av & bv;
|
||||
else if (drv->type == ID($or) || drv->type == ID($_OR_)) rv = av | bv;
|
||||
else if (drv->type == ID($xor) || drv->type == ID($_XOR_)) rv = av ^ bv;
|
||||
else if (drv->type == ID($not) || drv->type == ID($_NOT_)) rv = ~av;
|
||||
else if (drv->type == ID($logic_not)) rv = !av;
|
||||
else if (drv->type == ID($reduce_or)) rv = av != 0;
|
||||
else if (drv->type == ID($gt)) rv = av > bv;
|
||||
else if (drv->type == ID($eq)) rv = av == bv;
|
||||
else if (drv->type == ID($shl) || drv->type == ID($sshl)) rv = av << bv;
|
||||
else if (drv->type == ID($shr) || drv->type == ID($sshr)) rv = av >> bv;
|
||||
else if (drv->type == ID($mux)) {
|
||||
int sv = eval_bit_at_zero(drv->getPort(ID::S)[0], cache, depth + 1);
|
||||
rv = sv ? bv : av;
|
||||
}
|
||||
|
||||
int val = (rv >> bit_pos) & 1;
|
||||
cache[bit] = val;
|
||||
return val;
|
||||
}
|
||||
|
||||
bool eval_sig_is_zero(SigSpec sig)
|
||||
{
|
||||
dict<SigBit, int> cache;
|
||||
for (auto bit : sigmap(sig))
|
||||
if (eval_bit_at_zero(bit, cache, 0) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t eval_sig_at_zero(SigSpec sig)
|
||||
{
|
||||
dict<SigBit, int> cache;
|
||||
int64_t result = 0;
|
||||
for (int i = 0; i < GetSize(sig) && i < 62; i++)
|
||||
result |= ((int64_t)eval_bit_at_zero(sig[i], cache, 0) << i);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bmux_selects_stay_in_range(Wire *data, int loop_width)
|
||||
{
|
||||
bool saw_data_bmux = false;
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type != ID($bmux))
|
||||
continue;
|
||||
if (sigmap(cell->getPort(ID::A)) != sigmap(SigSpec(data)))
|
||||
continue;
|
||||
saw_data_bmux = true;
|
||||
if (eval_sig_at_zero(cell->getPort(ID::S)) >= loop_width)
|
||||
return false;
|
||||
}
|
||||
return saw_data_bmux;
|
||||
}
|
||||
|
||||
SigSpec zext(SigSpec sig, int width)
|
||||
{
|
||||
sig = sigmap(sig);
|
||||
if (GetSize(sig) > width)
|
||||
return sig.extract(0, width);
|
||||
if (GetSize(sig) < width)
|
||||
sig.append(SigSpec(State::S0, width - GetSize(sig)));
|
||||
return sig;
|
||||
}
|
||||
|
||||
SigSpec balanced_sum_rec(const vector<SigSpec> &terms, int begin, int end, int width)
|
||||
{
|
||||
if (begin >= end)
|
||||
return SigSpec(State::S0, width);
|
||||
if (begin + 1 == end)
|
||||
return zext(terms[begin], width);
|
||||
|
||||
int mid = begin + (end - begin) / 2;
|
||||
SigSpec lhs = balanced_sum_rec(terms, begin, mid, width);
|
||||
SigSpec rhs = balanced_sum_rec(terms, mid, end, width);
|
||||
Cell *cell = ref_cell;
|
||||
log_assert(cell != nullptr);
|
||||
Wire *sum = module->addWire(NEW_ID2_SUFFIX("compact_sum"), width);
|
||||
module->addAdd(NEW_ID2_SUFFIX("compact_add"), lhs, rhs, sum);
|
||||
new_cells_emitted++;
|
||||
return SigSpec(sum);
|
||||
}
|
||||
|
||||
SigSpec balanced_sum(const vector<SigSpec> &terms, int width)
|
||||
{
|
||||
return balanced_sum_rec(terms, 0, GetSize(terms), width);
|
||||
}
|
||||
|
||||
SigBit emit_not(SigBit bit)
|
||||
{
|
||||
Cell *cell = ref_cell;
|
||||
log_assert(cell != nullptr);
|
||||
Wire *out = module->addWire(NEW_ID2_SUFFIX("compact_not"), 1);
|
||||
module->addNot(NEW_ID2_SUFFIX("compact_not_cell"), SigSpec(bit), out);
|
||||
new_cells_emitted++;
|
||||
return SigBit(out);
|
||||
}
|
||||
|
||||
SigBit emit_and(SigBit a, SigBit b)
|
||||
{
|
||||
Cell *cell = ref_cell;
|
||||
log_assert(cell != nullptr);
|
||||
Wire *out = module->addWire(NEW_ID2_SUFFIX("compact_and"), 1);
|
||||
module->addAnd(NEW_ID2_SUFFIX("compact_and_cell"), SigSpec(a), SigSpec(b), out);
|
||||
new_cells_emitted++;
|
||||
return SigBit(out);
|
||||
}
|
||||
|
||||
SigBit emit_eq(SigSpec a, int value, int width)
|
||||
{
|
||||
Cell *cell = ref_cell;
|
||||
log_assert(cell != nullptr);
|
||||
Wire *out = module->addWire(NEW_ID2_SUFFIX("compact_eq"), 1);
|
||||
module->addEq(NEW_ID2_SUFFIX("compact_eq_cell"), zext(a, width), Const(value, width), out);
|
||||
new_cells_emitted++;
|
||||
return SigBit(out);
|
||||
}
|
||||
|
||||
SigBit emit_gt(SigSpec a, int value, int width)
|
||||
{
|
||||
Cell *cell = ref_cell;
|
||||
log_assert(cell != nullptr);
|
||||
Wire *out = module->addWire(NEW_ID2_SUFFIX("compact_gt"), 1);
|
||||
module->addGt(NEW_ID2_SUFFIX("compact_gt_cell"), zext(a, width), Const(value, width), out);
|
||||
new_cells_emitted++;
|
||||
return SigBit(out);
|
||||
}
|
||||
|
||||
SigBit emit_reduce_or(SigSpec bits)
|
||||
{
|
||||
bits = sigmap(bits);
|
||||
if (GetSize(bits) == 0)
|
||||
return State::S0;
|
||||
if (GetSize(bits) == 1)
|
||||
return bits[0];
|
||||
|
||||
Cell *cell = ref_cell;
|
||||
log_assert(cell != nullptr);
|
||||
Wire *out = module->addWire(NEW_ID2_SUFFIX("compact_or"), 1);
|
||||
module->addReduceOr(NEW_ID2_SUFFIX("compact_or_cell"), bits, out);
|
||||
new_cells_emitted++;
|
||||
return SigBit(out);
|
||||
}
|
||||
|
||||
void remove_old_cells(const vector<Cell *> &old_cells)
|
||||
{
|
||||
for (auto cell : old_cells) {
|
||||
if (module->cell(cell->name) == nullptr)
|
||||
continue;
|
||||
module->remove(cell);
|
||||
old_cells_removed++;
|
||||
}
|
||||
}
|
||||
|
||||
bool rewrite_forward_dense_pack()
|
||||
{
|
||||
Wire *sig = port("sig");
|
||||
Wire *sig2 = port("sig2");
|
||||
if (!sig || !sig2)
|
||||
return false;
|
||||
if (!sig->port_input || !sig2->port_output)
|
||||
return false;
|
||||
if (GetSize(sig) != GetSize(sig2))
|
||||
return false;
|
||||
if (GetSize(sig) < 4 || GetSize(sig) > max_width)
|
||||
return false;
|
||||
if (GetSize(module->ports) != 2)
|
||||
return false;
|
||||
if (count_binop_const(ID($add), 1) < GetSize(sig) - 2)
|
||||
return false;
|
||||
if (count_cells(ID($shl)) < GetSize(sig) - 2)
|
||||
return false;
|
||||
if (count_cells(ID($mux)) < GetSize(sig))
|
||||
return false;
|
||||
if (!eval_sig_is_zero(SigSpec(sig2)))
|
||||
return false;
|
||||
|
||||
vector<Cell *> old_cells(module->cells().begin(), module->cells().end());
|
||||
ref_cell = old_cells.front();
|
||||
|
||||
int width = GetSize(sig);
|
||||
int count_width = ceil_log2_int(width + 1);
|
||||
vector<SigSpec> bits;
|
||||
for (int i = 0; i < width; i++)
|
||||
bits.push_back(SigSpec(sigmap(SigBit(sig, i))));
|
||||
|
||||
SigSpec count = balanced_sum(bits, count_width);
|
||||
SigSpec packed;
|
||||
for (int i = 0; i < width; i++)
|
||||
packed.append(emit_gt(count, i, count_width));
|
||||
|
||||
module->connect(SigSpec(sig2), packed);
|
||||
remove_old_cells(old_cells);
|
||||
|
||||
log(" Forward dense pack: %s -> %s, width=%d, count_width=%d.\n",
|
||||
log_id(sig->name), log_id(sig2->name), width, count_width);
|
||||
forward_rewrites++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rewrite_reverse_suffix_read()
|
||||
{
|
||||
Wire *disable = port("disable_in");
|
||||
Wire *data = port("data_in");
|
||||
Wire *mask = port("mask");
|
||||
if (!disable || !data || !mask)
|
||||
return false;
|
||||
if (!disable->port_input || !data->port_input || !mask->port_output)
|
||||
return false;
|
||||
if (GetSize(disable) != GetSize(data) || GetSize(mask) != GetSize(data))
|
||||
return false;
|
||||
if (GetSize(module->ports) != 3)
|
||||
return false;
|
||||
|
||||
int dec_count = std::max(count_binop_const(ID($sub), 1),
|
||||
count_binop_const(ID($add), -1));
|
||||
int loop_width = dec_count + 1;
|
||||
if (loop_width < 4 || loop_width > max_width || loop_width > GetSize(data))
|
||||
return false;
|
||||
if (count_cells(ID($mux)) < loop_width)
|
||||
return false;
|
||||
if (has_binop_const_other_than(ID($sub), 1) ||
|
||||
has_binop_const_other_than(ID($add), -1))
|
||||
return false;
|
||||
if (!bmux_selects_stay_in_range(data, loop_width))
|
||||
return false;
|
||||
if (!eval_sig_is_zero(SigSpec(mask)))
|
||||
return false;
|
||||
|
||||
vector<Cell *> old_cells(module->cells().begin(), module->cells().end());
|
||||
ref_cell = old_cells.front();
|
||||
|
||||
int count_width = ceil_log2_int(loop_width + 1);
|
||||
vector<SigBit> valid(loop_width);
|
||||
for (int i = 0; i < loop_width; i++)
|
||||
valid[i] = emit_not(sigmap(SigBit(disable, i)));
|
||||
|
||||
SigSpec out_bits;
|
||||
for (int j = 0; j < loop_width; j++) {
|
||||
vector<SigSpec> suffix_terms;
|
||||
for (int k = j + 1; k < loop_width; k++)
|
||||
suffix_terms.push_back(SigSpec(valid[k]));
|
||||
SigSpec suffix_count = balanced_sum(suffix_terms, count_width);
|
||||
|
||||
SigSpec candidates;
|
||||
for (int k = 0; k < loop_width; k++) {
|
||||
int needed_count = loop_width - 1 - k;
|
||||
SigBit is_source = emit_eq(suffix_count, needed_count, count_width);
|
||||
SigBit gated_data = emit_and(sigmap(SigBit(data, k)), is_source);
|
||||
candidates.append(gated_data);
|
||||
}
|
||||
|
||||
SigBit selected = emit_reduce_or(candidates);
|
||||
out_bits.append(emit_and(valid[j], selected));
|
||||
}
|
||||
|
||||
if (GetSize(mask) > loop_width)
|
||||
out_bits.append(SigSpec(State::S0, GetSize(mask) - loop_width));
|
||||
|
||||
module->connect(SigSpec(mask), out_bits);
|
||||
remove_old_cells(old_cells);
|
||||
|
||||
log(" Reverse suffix read: %s/%s -> %s, loop_width=%d, count_width=%d.\n",
|
||||
log_id(disable->name), log_id(data->name), log_id(mask->name),
|
||||
loop_width, count_width);
|
||||
reverse_rewrites++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (module->has_processes_warn())
|
||||
return;
|
||||
if (rewrite_forward_dense_pack())
|
||||
return;
|
||||
rewrite_reverse_suffix_read();
|
||||
}
|
||||
};
|
||||
|
||||
struct OptCompactPrefixPass : public Pass
|
||||
{
|
||||
OptCompactPrefixPass() : Pass("opt_compact_prefix",
|
||||
"rewrite monotonic compaction loops into balanced prefix/routing logic") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
log("\n");
|
||||
log(" opt_compact_prefix [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Recognize narrow monotonic compaction patterns produced by frontend\n");
|
||||
log("lowering of SystemVerilog loops and replace their long loop-carried\n");
|
||||
log("index/update cones with balanced prefix-count and routing logic.\n");
|
||||
log("\n");
|
||||
log("Currently this pass handles the dense bit-pack and reverse suffix-read\n");
|
||||
log("forms used by the qor_spi_ra_add_chain and qor_spi_ra_sub_chain\n");
|
||||
log("regressions. Non-matching modules are left unchanged.\n");
|
||||
log("\n");
|
||||
log(" -max_width <n>\n");
|
||||
log(" Maximum compaction width to rewrite. Default: 64.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing OPT_COMPACT_PREFIX pass (monotonic compaction rewrites).\n");
|
||||
|
||||
int max_width = 64;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-max_width" && argidx + 1 < args.size()) {
|
||||
max_width = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_forward = 0;
|
||||
int total_reverse = 0;
|
||||
int total_removed = 0;
|
||||
int total_emitted = 0;
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptCompactPrefixWorker worker(module, max_width);
|
||||
worker.run();
|
||||
total_forward += worker.forward_rewrites;
|
||||
total_reverse += worker.reverse_rewrites;
|
||||
total_removed += worker.old_cells_removed;
|
||||
total_emitted += worker.new_cells_emitted;
|
||||
}
|
||||
|
||||
log("Rewrote %d forward pack(s), %d reverse suffix read(s); "
|
||||
"removed %d old cell(s), emitted %d new cell(s).\n",
|
||||
total_forward, total_reverse, total_removed, total_emitted);
|
||||
|
||||
if (total_forward || total_reverse)
|
||||
Yosys::run_pass("clean -purge");
|
||||
}
|
||||
} OptCompactPrefixPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -42,6 +42,183 @@ log -pop
|
|||
|
||||
|
||||
|
||||
# Test 31
|
||||
log -header "Sliced shifted ADD chain"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -icells <<EOF
|
||||
module top (
|
||||
input wire [15:0] a,
|
||||
input wire [15:0] b,
|
||||
input wire [15:0] c,
|
||||
input wire [15:0] d,
|
||||
input wire [15:0] e,
|
||||
output wire [63:0] y
|
||||
);
|
||||
wire [16:0] s0;
|
||||
wire [31:0] s1;
|
||||
wire [47:0] s2;
|
||||
|
||||
// s0 is later embedded into a wider operand with b[13:0] below it.
|
||||
// The sliced-add matcher should still flatten and rebalance the whole chain.
|
||||
\$add #(.A_WIDTH(16), .B_WIDTH(16), .Y_WIDTH(17), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add0 (.A(a), .B({14'b0, b[15:14]}), .Y(s0));
|
||||
\$add #(.A_WIDTH(31), .B_WIDTH(31), .Y_WIDTH(32), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add1 (.A({s0, b[13:0]}), .B({15'b0, c}), .Y(s1));
|
||||
\$add #(.A_WIDTH(32), .B_WIDTH(32), .Y_WIDTH(48), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add2 (.A(s1), .B({16'b0, d}), .Y(s2));
|
||||
\$add #(.A_WIDTH(48), .B_WIDTH(48), .Y_WIDTH(64), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add3 (.A(s2), .B({32'b0, e}), .Y(y));
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
|
||||
design -save preopt
|
||||
equiv_opt -assert opt_balance_tree
|
||||
design -load preopt
|
||||
opt_balance_tree
|
||||
select -assert-count 0 c:add0 c:add1 c:add2 c:add3
|
||||
select -assert-count 1 c:add3_tree_3
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
|
||||
# Test 32
|
||||
log -header "Sliced shifted ADD chain with external intermediate fanout"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -icells <<EOF
|
||||
module top (
|
||||
input wire [15:0] a,
|
||||
input wire [15:0] b,
|
||||
input wire [15:0] c,
|
||||
output wire [31:0] y,
|
||||
output wire [16:0] tap
|
||||
);
|
||||
wire [16:0] s0;
|
||||
|
||||
\$add #(.A_WIDTH(16), .B_WIDTH(16), .Y_WIDTH(17), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add0 (.A(a), .B({14'b0, b[15:14]}), .Y(s0));
|
||||
\$add #(.A_WIDTH(31), .B_WIDTH(31), .Y_WIDTH(32), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add1 (.A({s0, b[13:0]}), .B({15'b0, c}), .Y(y));
|
||||
|
||||
assign tap = s0;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
|
||||
design -save preopt
|
||||
equiv_opt -assert opt_balance_tree
|
||||
design -load preopt
|
||||
opt_balance_tree
|
||||
select -assert-count 1 c:add0
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
|
||||
# Test 33
|
||||
log -header "Signed sliced ADD chain is skipped"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -icells <<EOF
|
||||
module top (
|
||||
input wire signed [15:0] a,
|
||||
input wire signed [15:0] b,
|
||||
input wire signed [15:0] c,
|
||||
output wire signed [31:0] y
|
||||
);
|
||||
wire signed [16:0] s0;
|
||||
|
||||
\$add #(.A_WIDTH(16), .B_WIDTH(16), .Y_WIDTH(17), .A_SIGNED(1), .B_SIGNED(1))
|
||||
add0 (.A(a), .B({14'b0, b[15:14]}), .Y(s0));
|
||||
\$add #(.A_WIDTH(31), .B_WIDTH(31), .Y_WIDTH(32), .A_SIGNED(1), .B_SIGNED(1))
|
||||
add1 (.A({s0, b[13:0]}), .B({15'b0, c}), .Y(y));
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
|
||||
design -save preopt
|
||||
equiv_opt -assert opt_balance_tree
|
||||
design -load preopt
|
||||
opt_balance_tree
|
||||
select -assert-count 1 c:add0
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
|
||||
# Test 34
|
||||
log -header "High-slice-only ADD dependency is skipped"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -icells <<EOF
|
||||
module top (
|
||||
input wire [15:0] a,
|
||||
input wire [15:0] b,
|
||||
input wire [15:0] c,
|
||||
output wire [31:0] y
|
||||
);
|
||||
wire [16:0] s0;
|
||||
|
||||
\$add #(.A_WIDTH(16), .B_WIDTH(16), .Y_WIDTH(17), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add0 (.A(a), .B({14'b0, b[15:14]}), .Y(s0));
|
||||
\$add #(.A_WIDTH(30), .B_WIDTH(30), .Y_WIDTH(32), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add1 (.A({s0[16:1], b[13:0]}), .B({14'b0, c}), .Y(y));
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
|
||||
design -save preopt
|
||||
equiv_opt -assert opt_balance_tree
|
||||
design -load preopt
|
||||
opt_balance_tree
|
||||
select -assert-count 1 c:add0
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
|
||||
# Test 35
|
||||
log -header "Partial-slice downstream ADD sink does not block sliced root"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -icells <<EOF
|
||||
module top (
|
||||
input wire [15:0] a,
|
||||
input wire [15:0] b,
|
||||
input wire [15:0] c,
|
||||
input wire [15:0] d,
|
||||
output wire [31:0] y,
|
||||
output wire [31:0] side
|
||||
);
|
||||
wire [16:0] s0;
|
||||
wire [31:0] y_int;
|
||||
|
||||
\$add #(.A_WIDTH(16), .B_WIDTH(16), .Y_WIDTH(17), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add0 (.A(a), .B({14'b0, b[15:14]}), .Y(s0));
|
||||
\$add #(.A_WIDTH(31), .B_WIDTH(31), .Y_WIDTH(32), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add1 (.A({s0, b[13:0]}), .B({15'b0, c}), .Y(y_int));
|
||||
\$add #(.A_WIDTH(31), .B_WIDTH(31), .Y_WIDTH(32), .A_SIGNED(0), .B_SIGNED(0))
|
||||
add_side (.A(y_int[31:1]), .B({15'b0, d}), .Y(side));
|
||||
|
||||
assign y = y_int;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
|
||||
design -save preopt
|
||||
equiv_opt -assert opt_balance_tree
|
||||
design -load preopt
|
||||
opt_balance_tree
|
||||
select -assert-count 0 c:add0 c:add1
|
||||
select -assert-count 1 c:add_side
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
|
||||
|
||||
# Test 2
|
||||
log -header "AND chain with intermediate outputs"
|
||||
log -push
|
||||
|
|
|
|||
|
|
@ -0,0 +1,324 @@
|
|||
module opt_argmax_basic (
|
||||
input wire [15:0] sig,
|
||||
input wire [15:0][3:0] sig3,
|
||||
input wire [15:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 16; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_w8 (
|
||||
input wire [7:0] sig,
|
||||
input wire [7:0][2:0] sig3,
|
||||
input wire [7:0][4:0] sig2,
|
||||
output reg [2:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_w32 (
|
||||
input wire [31:0] sig,
|
||||
input wire [31:0][4:0] sig3,
|
||||
input wire [31:0][5:0] sig2,
|
||||
output reg [4:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 32; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_flat (
|
||||
input wire [7:0] sig,
|
||||
input wire [23:0] sig3,
|
||||
input wire [39:0] sig2,
|
||||
output reg [2:0] se_target_idx
|
||||
);
|
||||
function automatic [2:0] idx_at(input [2:0] pos);
|
||||
idx_at = sig3[pos * 3 +: 3];
|
||||
endfunction
|
||||
|
||||
function automatic [4:0] val_at(input [2:0] pos);
|
||||
val_at = sig2[idx_at(pos) * 5 +: 5];
|
||||
endfunction
|
||||
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(val_at(se_target_idx) < val_at(k[2:0]))) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_value_w1 (
|
||||
input wire [7:0] sig,
|
||||
input wire [7:0][2:0] sig3,
|
||||
input wire [7:0] sig2,
|
||||
output reg [2:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_value_w16 (
|
||||
input wire [7:0] sig,
|
||||
input wire [7:0][2:0] sig3,
|
||||
input wire [7:0][15:0] sig2,
|
||||
output reg [2:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_two_regions (
|
||||
input wire [7:0] sig_a,
|
||||
input wire [7:0][2:0] sig3_a,
|
||||
input wire [7:0][7:0] sig2_a,
|
||||
input wire [7:0] sig_b,
|
||||
input wire [7:0][2:0] sig3_b,
|
||||
input wire [7:0][5:0] sig2_b,
|
||||
output reg [2:0] idx_a,
|
||||
output reg [2:0] idx_b
|
||||
);
|
||||
always_comb begin
|
||||
idx_a = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig_a[idx_a] && sig_a[k]) begin
|
||||
idx_a = k;
|
||||
end else if (sig_a[idx_a] && sig_a[k] &&
|
||||
(sig2_a[sig3_a[idx_a]] < sig2_a[sig3_a[k]])) begin
|
||||
idx_a = k;
|
||||
end
|
||||
end
|
||||
|
||||
idx_b = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig_b[idx_b] && sig_b[k]) begin
|
||||
idx_b = k;
|
||||
end else if (sig_b[idx_b] && sig_b[k] &&
|
||||
(sig2_b[sig3_b[idx_b]] < sig2_b[sig3_b[k]])) begin
|
||||
idx_b = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_shared_consumer (
|
||||
input wire [7:0] sig,
|
||||
input wire [7:0][2:0] sig3,
|
||||
input wire [7:0][7:0] sig2,
|
||||
input wire [2:0] salt,
|
||||
output reg [2:0] se_target_idx,
|
||||
output wire [2:0] also_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 8; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign also_idx = se_target_idx ^ salt;
|
||||
endmodule
|
||||
|
||||
module opt_argmax_tie_high (
|
||||
input wire [15:0] sig,
|
||||
input wire [15:0][3:0] sig3,
|
||||
input wire [15:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 16; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] <= sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_nonzero_default (
|
||||
input wire [15:0] sig,
|
||||
input wire [15:0][3:0] sig3,
|
||||
input wire [15:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = 4'd1;
|
||||
for (int k = 1; k < 16; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_min (
|
||||
input wire [15:0] sig,
|
||||
input wire [15:0][3:0] sig3,
|
||||
input wire [15:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 16; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] > sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_w12 (
|
||||
input wire [11:0] sig,
|
||||
input wire [11:0][3:0] sig3,
|
||||
input wire [11:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 12; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_bad_index_width (
|
||||
input wire [15:0] sig,
|
||||
input wire [15:0][4:0] sig3,
|
||||
input wire [15:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 16; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx][3:0]] < sig2[sig3[k][3:0]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_stress_noop (
|
||||
input wire [63:0] sel,
|
||||
input wire [63:0] a,
|
||||
input wire [63:0] b,
|
||||
output wire [63:0] y
|
||||
);
|
||||
wire [63:0] mux0 = sel[0] ? a : b;
|
||||
wire [63:0] mux1 = sel[1] ? mux0 : {mux0[31:0], mux0[63:32]};
|
||||
wire [63:0] mux2 = sel[2] ? mux1 : (mux1 ^ a);
|
||||
wire [63:0] mux3 = sel[3] ? mux2 : (mux2 & b);
|
||||
wire [63:0] mux4 = sel[4] ? mux3 : (mux3 | a);
|
||||
wire [63:0] mux5 = sel[5] ? mux4 : {mux4[47:0], mux4[63:48]};
|
||||
assign y = sel[6] ? mux5 : ~mux5;
|
||||
endmodule
|
||||
|
||||
module opt_argmax_unrelated (
|
||||
input wire [3:0] a,
|
||||
input wire [3:0] b,
|
||||
input wire sel,
|
||||
output wire [3:0] y
|
||||
);
|
||||
assign y = sel ? a : b;
|
||||
endmodule
|
||||
|
||||
module opt_argmax_multi_match (
|
||||
input wire [15:0] sig,
|
||||
input wire [15:0][3:0] sig3,
|
||||
input wire [15:0][7:0] sig2,
|
||||
output reg [3:0] se_target_idx
|
||||
);
|
||||
always_comb begin
|
||||
se_target_idx = '0;
|
||||
for (int k = 1; k < 16; k++) begin
|
||||
if (!sig[se_target_idx] && sig[k]) begin
|
||||
se_target_idx = k;
|
||||
end else if (sig[se_target_idx] && sig[k] &&
|
||||
(sig2[sig3[se_target_idx]] < sig2[sig3[k]])) begin
|
||||
se_target_idx = k;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_argmax_multi_keep (
|
||||
input wire [3:0] a,
|
||||
input wire [3:0] b,
|
||||
input wire sel,
|
||||
output wire [3:0] y
|
||||
);
|
||||
assign y = sel ? a : b;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
# Tests for opt_argmax.
|
||||
|
||||
log -header "Small masked argmax self-equivalence"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_w8
|
||||
proc; opt_clean
|
||||
rename opt_argmax_w8 gold
|
||||
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_w8
|
||||
proc; opt_clean
|
||||
select -module opt_argmax_w8
|
||||
opt_argmax
|
||||
select -clear
|
||||
opt_clean
|
||||
rename opt_argmax_w8 gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Basic masked argmax structural rewrite"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_basic
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
select -assert-count 16 t:$bmux
|
||||
select -assert-count 15 t:$lt
|
||||
select -assert-count 29 t:$mux
|
||||
select -assert-count 30 t:$and
|
||||
select -assert-count 29 t:$or
|
||||
select -assert-count 15 t:$not
|
||||
select -assert-none c:LessThan_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Flat-bus masked argmax self-equivalence"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_flat
|
||||
proc; opt_clean
|
||||
rename opt_argmax_flat gold
|
||||
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_flat
|
||||
proc; opt_clean
|
||||
select -module opt_argmax_flat
|
||||
opt_argmax
|
||||
select -clear
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
rename opt_argmax_flat gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Scaled masked argmax: 8 entries structural"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_w8
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Scaled masked argmax: 32 entries structural"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_w32
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Value width edge: 1-bit values"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_value_w1
|
||||
proc; opt_clean
|
||||
rename opt_argmax_value_w1 gold
|
||||
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_value_w1
|
||||
proc; opt_clean
|
||||
select -module opt_argmax_value_w1
|
||||
opt_argmax
|
||||
select -clear
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
rename opt_argmax_value_w1 gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Value width edge: 16-bit values"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_value_w16
|
||||
proc; opt_clean
|
||||
rename opt_argmax_value_w16 gold
|
||||
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_value_w16
|
||||
proc; opt_clean
|
||||
select -module opt_argmax_value_w16
|
||||
opt_argmax
|
||||
select -clear
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
rename opt_argmax_value_w16 gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Same module: two independent argmax regions"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_two_regions
|
||||
proc; opt_clean
|
||||
rename opt_argmax_two_regions gold
|
||||
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_two_regions
|
||||
proc; opt_clean
|
||||
select -module opt_argmax_two_regions
|
||||
opt_argmax
|
||||
select -clear
|
||||
opt_clean
|
||||
select -assert-min 2 w:*argmax*
|
||||
rename opt_argmax_two_regions gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Shared consumer of argmax output remains equivalent"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_shared_consumer
|
||||
proc; opt_clean
|
||||
rename opt_argmax_shared_consumer gold
|
||||
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_shared_consumer
|
||||
proc; opt_clean
|
||||
select -module opt_argmax_shared_consumer
|
||||
opt_argmax
|
||||
select -clear
|
||||
opt_clean
|
||||
select -assert-min 1 w:*argmax*
|
||||
rename opt_argmax_shared_consumer gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Max width leaves argmax unchanged"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_basic
|
||||
proc; opt_clean
|
||||
opt_argmax -max_width 8
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: non-power-of-two candidate count"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_w12
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: mismatched index-map width"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_bad_index_width
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: strict tie behavior changed"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_tie_high
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: nonzero all-invalid default"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_nonzero_default
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: min-selection comparator"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_min
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: unrelated mux logic unchanged"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_unrelated
|
||||
proc; opt_clean
|
||||
select -assert-count 1 t:$mux
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
select -assert-count 1 t:$mux
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: bmux-heavy unrelated stress module"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_stress_noop
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
select -assert-none w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Multi-module: only matching module rewrites"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_argmax.sv
|
||||
verific -import opt_argmax_multi_match opt_argmax_multi_keep
|
||||
proc; opt_clean
|
||||
opt_argmax
|
||||
opt_clean
|
||||
select -assert-min 1 opt_argmax_multi_match/w:*argmax*
|
||||
select -assert-none opt_argmax_multi_keep/w:*argmax*
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
# Tests for opt_compact_prefix.
|
||||
|
||||
log -header "Forward dense pack self-equivalence"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_pack.sv
|
||||
verific -import opt_compact_prefix_pack
|
||||
proc; opt_clean
|
||||
rename opt_compact_prefix_pack gold
|
||||
|
||||
read -sv opt_compact_prefix_pack.sv
|
||||
verific -import opt_compact_prefix_pack
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
rename opt_compact_prefix_pack gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Forward dense pack structural rewrite"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_pack.sv
|
||||
verific -import opt_compact_prefix_pack
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
select -assert-none t:$shl
|
||||
select -assert-none t:$mux
|
||||
select -assert-count 7 t:$add
|
||||
select -assert-count 8 t:$gt
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Reverse suffix read self-equivalence"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_sub.sv
|
||||
verific -import opt_compact_prefix_sub
|
||||
proc; opt_clean
|
||||
rename opt_compact_prefix_sub gold
|
||||
|
||||
read -sv opt_compact_prefix_sub.sv
|
||||
verific -import opt_compact_prefix_sub
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
rename opt_compact_prefix_sub gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Reverse suffix read structural rewrite"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_sub.sv
|
||||
verific -import opt_compact_prefix_sub
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
select -assert-none t:$sub
|
||||
select -assert-none t:$mux
|
||||
select -assert-min 1 t:$add
|
||||
select -assert-min 1 t:$eq
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative: unrelated mux module unchanged"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input wire sel, input wire [7:0] a, b, output wire [7:0] y);
|
||||
assign y = sel ? a : b;
|
||||
endmodule
|
||||
EOF
|
||||
proc; opt_clean
|
||||
select -assert-count 1 t:$mux
|
||||
opt_compact_prefix
|
||||
select -assert-count 1 t:$mux
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Exact regression size: 32-bit forward dense pack"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_pack32.sv
|
||||
verific -import opt_compact_prefix_pack32
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
select -assert-none t:$shl
|
||||
select -assert-none t:$mux
|
||||
select -assert-count 31 t:$add
|
||||
select -assert-count 32 t:$gt
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Exact regression size: 16-entry reverse suffix read"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_sub16.sv
|
||||
verific -import opt_compact_prefix_sub16
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
select -assert-none t:$sub
|
||||
select -assert-none t:$mux
|
||||
select -assert-min 1 t:$add
|
||||
select -assert-min 1 t:$eq
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Reverse suffix read with add-by-minus-one decrement"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_addneg.sv
|
||||
verific -import opt_compact_prefix_addneg
|
||||
proc; opt_clean
|
||||
rename opt_compact_prefix_addneg gold
|
||||
|
||||
read -sv opt_compact_prefix_addneg.sv
|
||||
verific -import opt_compact_prefix_addneg
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
rename opt_compact_prefix_addneg gate
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
hierarchy -top miter
|
||||
proc; opt; memory; opt
|
||||
sat -prove-asserts -verify
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Max width: forward pack left unchanged"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_pack32.sv
|
||||
verific -import opt_compact_prefix_pack32
|
||||
proc; opt_clean
|
||||
select -assert-min 1 t:$shl
|
||||
opt_compact_prefix -max_width 8
|
||||
select -assert-min 1 t:$shl
|
||||
select -assert-none w:*compact*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Max width: reverse suffix read left unchanged"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_sub16.sv
|
||||
verific -import opt_compact_prefix_sub16
|
||||
proc; opt_clean
|
||||
select -assert-min 1 t:$sub
|
||||
opt_compact_prefix -max_width 8
|
||||
select -assert-min 1 t:$sub
|
||||
select -assert-none w:*compact*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative near miss: same port names passthrough"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_near_miss.sv
|
||||
verific -import opt_compact_prefix_pack_passthrough
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
select -assert-none w:*compact*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative near miss: same port names nonzero pack init"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_near_miss.sv
|
||||
verific -import opt_compact_prefix_pack_nonzero_init
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
select -assert-none w:*compact*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative near miss: same port names non-unit pack stride"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_near_miss.sv
|
||||
verific -import opt_compact_prefix_pack_stride2
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
select -assert-none w:*compact*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Negative near miss: same port names nonzero reverse init"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_near_miss.sv
|
||||
verific -import opt_compact_prefix_sub_nonzero_init
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
select -assert-none w:*compact*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Scaling: 64-bit forward pack"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_scale.sv
|
||||
verific -import opt_compact_prefix_pack64
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
select -assert-none t:$shl
|
||||
select -assert-none t:$mux
|
||||
select -assert-count 63 t:$add
|
||||
select -assert-count 64 t:$gt
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Scaling: 128-bit forward pack with explicit max_width"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_scale.sv
|
||||
verific -import opt_compact_prefix_pack128
|
||||
proc; opt_clean
|
||||
opt_compact_prefix -max_width 128
|
||||
opt_clean
|
||||
select -assert-none t:$shl
|
||||
select -assert-none t:$mux
|
||||
select -assert-count 127 t:$add
|
||||
select -assert-count 128 t:$gt
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Multi-module: only matching module rewrites"
|
||||
log -push
|
||||
design -reset
|
||||
verific -cfg veri_optimize_wide_selector 1
|
||||
verific -cfg db_infer_wide_muxes_post_elaboration 0
|
||||
read -sv opt_compact_prefix_multi.sv
|
||||
verific -import opt_compact_prefix_multi_match opt_compact_prefix_multi_keep
|
||||
proc; opt_clean
|
||||
opt_compact_prefix
|
||||
opt_clean
|
||||
select -assert-none opt_compact_prefix_multi_match/t:$shl
|
||||
select -assert-none opt_compact_prefix_multi_match/t:$mux
|
||||
select -assert-count 1 opt_compact_prefix_multi_keep/t:$mux
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
module opt_compact_prefix_addneg (
|
||||
input logic [15:0] disable_in,
|
||||
input logic [15:0] data_in,
|
||||
output logic [15:0] mask
|
||||
);
|
||||
always_comb begin
|
||||
mask = '0;
|
||||
for (int I = 8, indx = 8; I > 0; I--) begin
|
||||
if (disable_in[I-1]) begin
|
||||
mask[I-1] = 1'b0;
|
||||
end else begin
|
||||
mask[I-1] = data_in[indx-1];
|
||||
indx = indx + -1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
module opt_compact_prefix_multi_match (
|
||||
input logic [7:0] sig,
|
||||
output logic [7:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0, indx = 0; I < 8; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_compact_prefix_multi_keep (
|
||||
input logic sel,
|
||||
input logic [7:0] a,
|
||||
input logic [7:0] b,
|
||||
output logic [7:0] y
|
||||
);
|
||||
assign y = sel ? a : b;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
module opt_compact_prefix_pack_passthrough (
|
||||
input logic [7:0] sig,
|
||||
output logic [7:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0; I < 8; I++) begin
|
||||
if (sig[I])
|
||||
sig2[I] = sig[I];
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_compact_prefix_pack_nonzero_init (
|
||||
input logic [7:0] sig,
|
||||
output logic [7:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '1;
|
||||
for (int I = 0, indx = 0; I < 8; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_compact_prefix_pack_stride2 (
|
||||
input logic [7:0] sig,
|
||||
output logic [7:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0, indx = 0; I < 4; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 2;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_compact_prefix_sub_nonzero_init (
|
||||
input logic [15:0] disable_in,
|
||||
input logic [15:0] data_in,
|
||||
output logic [15:0] mask
|
||||
);
|
||||
always_comb begin
|
||||
mask = '1;
|
||||
for (int I = 8, indx = 8; I > 0; I--) begin
|
||||
if (disable_in[I-1]) begin
|
||||
mask[I-1] = 1'b0;
|
||||
end else begin
|
||||
mask[I-1] = data_in[indx-1];
|
||||
indx = indx - 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_compact_prefix_sub_stride2 (
|
||||
input logic [15:0] disable_in,
|
||||
input logic [15:0] data_in,
|
||||
output logic [15:0] mask
|
||||
);
|
||||
always_comb begin
|
||||
mask = '0;
|
||||
for (int I = 8, indx = 16; I > 0; I--) begin
|
||||
if (disable_in[I-1]) begin
|
||||
mask[I-1] = 1'b0;
|
||||
end else begin
|
||||
mask[I-1] = data_in[indx-1];
|
||||
indx = indx - 2;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
module opt_compact_prefix_pack (
|
||||
input logic [7:0] sig,
|
||||
output logic [7:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0, indx = 0; I < 8; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
module opt_compact_prefix_pack32 (
|
||||
input logic [31:0] sig,
|
||||
output logic [31:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0, indx = 0; I < 32; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
module opt_compact_prefix_pack64 (
|
||||
input logic [63:0] sig,
|
||||
output logic [63:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0, indx = 0; I < 64; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module opt_compact_prefix_pack128 (
|
||||
input logic [127:0] sig,
|
||||
output logic [127:0] sig2
|
||||
);
|
||||
always_comb begin
|
||||
sig2 = '0;
|
||||
for (int I = 0, indx = 0; I < 128; I++) begin
|
||||
if (sig[I]) begin
|
||||
sig2[indx] = sig[I];
|
||||
indx += 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
module opt_compact_prefix_sub (
|
||||
input logic [15:0] disable_in,
|
||||
input logic [15:0] data_in,
|
||||
output logic [15:0] mask
|
||||
);
|
||||
always_comb begin
|
||||
mask = '0;
|
||||
for (int I = 8, indx = 8; I > 0; I--) begin
|
||||
if (disable_in[I-1]) begin
|
||||
mask[I-1] = 1'b0;
|
||||
end else begin
|
||||
mask[I-1] = data_in[indx-1];
|
||||
indx = indx - 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
module opt_compact_prefix_sub16 (
|
||||
input logic [31:0] disable_in,
|
||||
input logic [31:0] data_in,
|
||||
output logic [31:0] mask
|
||||
);
|
||||
always_comb begin
|
||||
mask = '0;
|
||||
for (int I = 16, indx = 16; I > 0; I--) begin
|
||||
if (disable_in[I-1]) begin
|
||||
mask[I-1] = 1'b0;
|
||||
end else begin
|
||||
mask[I-1] = data_in[indx-1];
|
||||
indx = indx - 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue