Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decode logic for muxpack #38

Merged
merged 13 commits into from
Jan 11, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 194 additions & 25 deletions passes/opt/muxpack.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ struct ExclusiveDatabase

dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev;

ExclusiveDatabase(Module *module, const SigMap &sigmap, bool assume_excl) : module(module), sigmap(sigmap)
ExclusiveDatabase(Module *module, const SigMap &sigmap, bool assume_excl, bool make_excl) : module(module), sigmap(sigmap)
{
if (assume_excl) return;
if (assume_excl || make_excl) return;
SigSpec const_sig, nonconst_sig;
SigBit y_port;
pool<Cell*> reduce_or;
Expand Down Expand Up @@ -123,6 +123,10 @@ struct MuxpackWorker
int mux_count, pmux_count;

pool<Cell*> remove_cells;
// Driver data
dict<SigBit, tuple<IdString, IdString, int>> bit_drivers_db;
// Load data
dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_users_db;

dict<SigSpec, Cell*> sig_chain_next;
dict<SigSpec, Cell*> sig_chain_prev;
Expand All @@ -131,6 +135,26 @@ struct MuxpackWorker
pool<Cell*> candidate_cells;

ExclusiveDatabase excl_db;
// Splitfanout limit
int limit = -1;

bool fanout_in_range(SigSpec outsig)
{
// Check if output signal is "bit-split", skip if so
// This is a lookahead for the splitfanout pass that has this limitation
auto bit_users = bit_users_db[outsig[0]];
for (int i = 0; i < GetSize(outsig); i++) {
if (bit_users_db[outsig[i]] != bit_users) {
return false;
}
}

// Skip if fanout is above limit
if (limit != -1 && GetSize(bit_users) > limit) {
return false;
}
return true;
}

void make_sig_chain_next_prev()
{
Expand All @@ -156,20 +180,29 @@ struct MuxpackWorker
for (auto a_bit : a_sig)
sigbit_with_non_chain_users.insert(a_bit);
else {
sig_chain_next[a_sig] = cell;
if (fanout_in_range(y_sig)) {
sig_chain_next[a_sig] = cell;
candidate_cells.insert(cell);
}
}

if (!b_sig.empty()) {
if (sig_chain_next.count(b_sig))
for (auto b_bit : b_sig)
sigbit_with_non_chain_users.insert(b_bit);
else {
sig_chain_next[b_sig] = cell;
if (fanout_in_range(y_sig)) {
sig_chain_next[b_sig] = cell;
candidate_cells.insert(cell);
}
}
}
candidate_cells.insert(cell);

sig_chain_prev[y_sig] = cell;
if (fanout_in_range(y_sig)) {

// Mark cell as the previous in the chain relative to y_sig
sig_chain_prev[y_sig] = cell;
}
continue;
}

Expand Down Expand Up @@ -243,7 +276,7 @@ struct MuxpackWorker
return chain;
}

void process_chain(vector<Cell*> &chain)
void process_chain(vector<Cell*> &chain, bool make_excl)
{
if (GetSize(chain) < 2)
return;
Expand Down Expand Up @@ -289,19 +322,78 @@ struct MuxpackWorker
remove_cells.insert(cursor_cell);
}

if (make_excl) {
/* We create the following one-hot select line decoder
S0 S1 S2 S3 ...
| | | |
+--------+ +----------+ +-------------+ |
| _|_ | _|_ | _|_ |
| \_/ | \_/ | \_/ |
| o | o | o |
| | | | | ___ | |
| +----------+ | | / | | |
| | | |___| | / |___| |
| |___| | & | / / | & | / ...
| | & | \___/ / / \___/ / /
| \___/ | | / | | /
| | +------+ +-------+
| | | | | |
| | |___| |___|
| | | & | | & |
| | \___/ \___/
| | | |
S0 S0'S1 S0'S1'S2 S0'S1'S2'S3 ...
*/
SigSpec decodedSelect;
Cell *cell = last_cell;
std::vector<RTLIL::SigBit> select_bits = s_sig.bits();
RTLIL::SigBit prevSigNot = RTLIL::State::S1;
RTLIL::SigBit prevSigAnd = RTLIL::State::S1;
for (int i = (int) (select_bits.size() -1); i >= 0; i--) {
Yosys::RTLIL::SigBit sigbit = select_bits[i];
if (i == (int) (select_bits.size() -1)) {
decodedSelect.append(sigbit);
Wire *not_y = module->addWire(NEW_ID, 1);
module->addNot(NEW_ID2_SUFFIX("not"), sigbit, not_y, false, last_cell->get_src_attribute());
prevSigNot = not_y;
} else if (i == (int) (select_bits.size() -2)) {
Wire *and_y = module->addWire(NEW_ID, 1);
module->addAndGate(NEW_ID2_SUFFIX("sel"), sigbit, prevSigNot, and_y, last_cell->get_src_attribute());
decodedSelect.append(and_y);
Wire *not_y = module->addWire(NEW_ID, 1);
module->addNot(NEW_ID2_SUFFIX("not"), sigbit, not_y, false, last_cell->get_src_attribute());
prevSigAnd = prevSigNot;
prevSigNot = not_y;
} else {
Wire *and_y1 = module->addWire(NEW_ID, 1);
module->addAndGate(NEW_ID2_SUFFIX("sel"), prevSigAnd, prevSigNot, and_y1, last_cell->get_src_attribute());
Wire *and_y2 = module->addWire(NEW_ID, 1);
module->addAndGate(NEW_ID2_SUFFIX("sel"), sigbit, and_y1, and_y2, last_cell->get_src_attribute());
decodedSelect.append(and_y2);
Wire *not_y = module->addWire(NEW_ID, 1);
module->addNot(NEW_ID2_SUFFIX("not"), sigbit, not_y, false, last_cell->get_src_attribute());
prevSigAnd = and_y1;
prevSigNot = not_y;
}
}
decodedSelect.reverse();
first_cell->setPort(ID::S, decodedSelect);
} else {
first_cell->setPort(ID::S, s_sig);
}
first_cell->setPort(ID::B, b_sig);
first_cell->setPort(ID::S, s_sig);
first_cell->setParam(ID::S_WIDTH, GetSize(s_sig));
first_cell->setPort(ID::Y, last_cell->getPort(ID::Y));

cursor += cases;
}
}

void cleanup()
void cleanup(bool remove_cell)
{
for (auto cell : remove_cells)
module->remove(cell);
if (remove_cell)
for (auto cell : remove_cells)
module->remove(cell);

remove_cells.clear();
sig_chain_next.clear();
Expand All @@ -310,18 +402,90 @@ struct MuxpackWorker
candidate_cells.clear();
}

MuxpackWorker(Module *module, bool assume_excl) :
module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap, assume_excl)
MuxpackWorker(Design *design, Module *module, bool assume_excl, bool make_excl, int limit)
: module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap, assume_excl, make_excl), limit(limit)
{

// Build bit_drivers_db
log("Building bit_drivers_db...\n");
for (auto cell : module->cells()) {
for (auto conn : cell->connections()) {
if (!cell->output(conn.first))
continue;
for (int i = 0; i < GetSize(conn.second); i++) {
SigBit bit(sigmap(conn.second[i]));
bit_drivers_db[bit] = tuple<IdString, IdString, int>(cell->name, conn.first, i);
}
}
}

// Build bit_users_db
log("Building bit_users_db...\n");
for (auto cell : module->cells()) {
for (auto conn : cell->connections()) {
if (!cell->input(conn.first))
continue;
for (int i = 0; i < GetSize(conn.second); i++) {
SigBit bit(sigmap(conn.second[i]));
if (!bit_drivers_db.count(bit))
continue;
bit_users_db[bit].insert(
tuple<IdString, IdString, int>(cell->name, conn.first, i - std::get<2>(bit_drivers_db[bit])));
}
}
}

// Build bit_users_db for output ports
log("Building bit_users_db for output ports...\n");
for (auto wire : module->wires()) {
if (!wire->port_output)
continue;
SigSpec sig(sigmap(wire));
for (int i = 0; i < GetSize(sig); i++) {
SigBit bit(sig[i]);
if (!bit_drivers_db.count(bit))
continue;
bit_users_db[bit].insert(
tuple<IdString, IdString, int>(wire->name, IdString(), i - std::get<2>(bit_drivers_db[bit])));
}
}

make_sig_chain_next_prev();
find_chain_start_cells(assume_excl);

// Deselect all cells
Pass::call(design, "select -none");
bool has_cell_to_split = false;
for (auto c : chain_start_cells) {
vector<Cell*> chain = create_chain(c);
process_chain(chain);
vector<Cell *> chain = create_chain(c);
for (auto cell : chain) {
has_cell_to_split = true;
// Select the cells that are candidate
design->select(module, cell);
}
}
// Clean up
cleanup(false);

// Make sure we dup the cells with fanout, else the resulting
// transform is not logically equivalent
if (has_cell_to_split)
Pass::call(design, "splitfanout");
// Reset selection for other passes
Pass::call(design, "select -clear");
// Recreate sigmap
sigmap.set(module);

cleanup();
make_sig_chain_next_prev();
find_chain_start_cells(assume_excl);

// Make the actual transform
for (auto c : chain_start_cells) {
vector<Cell *> chain = create_chain(c);
process_chain(chain, make_excl);
}
// Clean up
cleanup(true);
}
};

Expand All @@ -341,43 +505,48 @@ struct MuxpackPass : public Pass {
log("whose select lines are driven by '$eq' cells with other such cells if it can be\n");
log("certain that their select inputs are mutually exclusive.\n");
log("\n");
log(" -splitfanout\n");
log(" run splitfanout pass first\n");
log(" -fanout_limit n\n");
log(" max fanout to split.\n");
log("\n");
log(" -assume_excl\n");
log(" assume mutually exclusive constraint when packing (may result in inequivalence)\n");
log(" -make_excl\n");
log(" Adds a one-hot decoder on the control signals\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool splitfanout = false;
bool assume_excl = false;
bool make_excl = false;
int limit = -1;

log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n");

size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-splitfanout") {
splitfanout = true;
if (args[argidx] == "-fanout_limit" && argidx + 1 < args.size()) {
limit = std::stoi(args[++argidx]);
continue;
}
if (args[argidx] == "-assume_excl") {
assume_excl = true;
continue;
}
if (args[argidx] == "-make_excl") {
make_excl = true;
assume_excl = true;
continue;
}
break;
}
extra_args(args, argidx, design);

if (splitfanout)
Pass::call(design, "splitfanout -limit 256 t:$mux t:$pmux");

int mux_count = 0;
int pmux_count = 0;

for (auto module : design->selected_modules()) {
MuxpackWorker worker(module, assume_excl);
MuxpackWorker worker(design, module, assume_excl, make_excl, limit);
mux_count += worker.mux_count;
pmux_count += worker.pmux_count;
}
Expand Down
Loading