forked from YosysHQ/yosys
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
optbarriers: add command to add/remove optimization barriers
* This can optionally ignore rewriting the outputs of cells or processes * This by default rewrites drivers of wires with public names but can also optionally rewrite drivers of wires with private names * A -remove flag allows cleaning up the design by replacing barriers with connections
- Loading branch information
1 parent
a9eb583
commit f6c81ab
Showing
2 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
/* | ||
* yosys -- Yosys Open SYnthesis Suite | ||
* | ||
* Copyright (C) 2024 George Rennie <[email protected]> | ||
* | ||
* 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 | ||
|
||
// Standard visitor helper | ||
template<class... Ts> | ||
struct overloaded : Ts... { using Ts::operator()...; }; | ||
template<class... Ts> | ||
overloaded(Ts...) -> overloaded<Ts...>; | ||
|
||
struct OptBarriersPass : public Pass { | ||
OptBarriersPass() : Pass("optbarriers", "insert optimization barriers") {} | ||
|
||
void help() override { | ||
log("\n"); | ||
log(" optbarriers [options] [selection]\n"); | ||
log("\n"); | ||
log("Insert optimization barriers to drivers of selected public wires.\n"); | ||
log("\n"); | ||
log("\n"); | ||
log(" -nocells\n"); | ||
log(" don't add optimization barriers to the outputs of cells\n"); | ||
log("\n"); | ||
log(" -noprocs\n"); | ||
log(" don't add optimization barriers to the outputs of processes\n"); | ||
log("\n"); | ||
log(" -private\n"); | ||
log(" also add optimization barriers to private wires\n"); | ||
log("\n"); | ||
log(" -remove\n"); | ||
log(" replace selected optimization barriers with connections\n"); | ||
log("\n"); | ||
} | ||
|
||
void execute(std::vector<std::string> args, RTLIL::Design *design) override { | ||
log_header(design, "Executing OPTBARRIERS pass (insert optimization barriers).\n"); | ||
|
||
bool nocells_mode = false; | ||
bool noprocs_mode = false; | ||
bool private_mode = false; | ||
bool remove_mode = false; | ||
|
||
size_t argidx; | ||
for (argidx = 1; argidx < args.size(); argidx++) { | ||
std::string arg = args[argidx]; | ||
if (arg == "-nocells") { | ||
nocells_mode = true; | ||
continue; | ||
} | ||
if (arg == "-noprocs") { | ||
noprocs_mode = true; | ||
continue; | ||
} | ||
if (arg == "-private") { | ||
private_mode = true; | ||
continue; | ||
} | ||
if (arg == "-remove") { | ||
remove_mode = true; | ||
continue; | ||
} | ||
break; | ||
} | ||
extra_args(args, argidx, design); | ||
|
||
if (remove_mode) { | ||
log("Replacing optimization barriers with connections.\n"); | ||
remove_barriers(design); | ||
return; | ||
} | ||
|
||
for (auto* module : design->selected_modules()) { | ||
// We can't just sigmap and iterate through wires for rewriting as | ||
// we want to maintain the structure in connections, and sigmap | ||
// will just return a canonical wire which does not have to be one | ||
// that is directly driving the wire. Therefore for each type of | ||
// object that could be driving the wires (cells, processes, | ||
// connections) we rewrite the sigspecs. | ||
|
||
std::vector<RTLIL::SigSig> new_barriers; | ||
|
||
// Skip constants, unselected wires and private wires when not in | ||
// private mode. This works for SigChunk or SigBit input. | ||
const auto skip = [&](const auto& chunk) { | ||
if (!chunk.is_wire()) | ||
return true; | ||
|
||
if (!design->selected(module, chunk.wire)) | ||
return true; | ||
|
||
if (!private_mode && !chunk.wire->name.isPublic()) | ||
return true; | ||
|
||
return false; | ||
}; | ||
|
||
const auto rewrite_sigspec = [&](const SigSpec& sig) { | ||
RTLIL::SigSpec new_output; | ||
for (const auto& chunk : sig.chunks()) { | ||
if (skip(chunk)) { | ||
new_output.append(chunk); | ||
continue; | ||
} | ||
|
||
// Rewrite output to drive new wire, and schedule adding | ||
// barrier from new wire to original | ||
auto* new_output_wire = module->addWire(NEW_ID, GetSize(chunk)); | ||
new_output.append(new_output_wire); | ||
new_barriers.emplace_back(chunk, new_output_wire); | ||
} | ||
|
||
return new_output; | ||
}; | ||
|
||
// Rewrite cell outputs | ||
if (!nocells_mode) | ||
for (auto* cell : module->cells()) | ||
if (cell->type != ID($barrier)) | ||
for (const auto& [name, sig] : cell->connections()) | ||
if (cell->output(name)) | ||
cell->setPort(name, rewrite_sigspec(sig)); | ||
|
||
// Rewrite connections in processes | ||
if (!noprocs_mode) { | ||
const auto proc_rewriter = overloaded{ | ||
// Don't do anything for input sigspecs | ||
[&](const SigSpec&) {}, | ||
// Rewrite connections to drive barrier if needed | ||
[&](SigSpec& lhs, const SigSpec&) { | ||
lhs = rewrite_sigspec(lhs); | ||
} | ||
}; | ||
|
||
for (auto& proc : module->processes) | ||
proc.second->rewrite_sigspecs2(proc_rewriter); | ||
} | ||
|
||
// Rewrite connections | ||
std::vector<RTLIL::SigSig> new_connections; | ||
for (const auto& conn : module->connections()) { | ||
RTLIL::SigSig skip_conn, barrier_conn; | ||
|
||
for (int i = 0; i < GetSize(conn.first); i++) { | ||
auto& sigsig = skip(conn.first[i]) ? skip_conn : barrier_conn; | ||
sigsig.first.append(conn.first[i]); | ||
sigsig.second.append(conn.second[i]); | ||
} | ||
|
||
if (!skip_conn.first.empty()) | ||
new_connections.emplace_back(std::move(skip_conn)); | ||
|
||
if (!barrier_conn.first.empty()) | ||
new_barriers.emplace_back(std::move(barrier_conn)); | ||
} | ||
module->new_connections(new_connections); | ||
|
||
// Add all the scheduled barriers | ||
for (const auto& conn : new_barriers) | ||
module->addBarrier(NEW_ID, conn.second, conn.first); | ||
} | ||
} | ||
|
||
void remove_barriers(RTLIL::Design* design) { | ||
for (auto* module : design->selected_modules()) { | ||
std::vector<RTLIL::Cell*> barriers; | ||
|
||
for (auto* cell : module->selected_cells()) | ||
if (cell->type == ID($barrier)) | ||
barriers.emplace_back(cell); | ||
|
||
for (auto* cell : barriers) { | ||
const auto lhs = cell->getPort(ID::Y), rhs = cell->getPort(ID::A); | ||
module->connect(lhs, rhs); | ||
module->remove(cell); | ||
} | ||
} | ||
} | ||
|
||
} OptBarriersPass; | ||
|
||
PRIVATE_NAMESPACE_END |