Skip to content

Commit aac2ead

Browse files
committed
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
1 parent e2657a4 commit aac2ead

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

passes/cmds/Makefile.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ OBJS += passes/cmds/future.o
5151
OBJS += passes/cmds/box_derive.o
5252
OBJS += passes/cmds/example_dt.o
5353
OBJS += passes/cmds/portarcs.o
54+
OBJS += passes/cmds/optbarriers.o

passes/cmds/optbarriers.cc

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* yosys -- Yosys Open SYnthesis Suite
3+
*
4+
* Copyright (C) 2024 George Rennie <[email protected]>
5+
*
6+
* Permission to use, copy, modify, and/or distribute this software for any
7+
* purpose with or without fee is hereby granted, provided that the above
8+
* copyright notice and this permission notice appear in all copies.
9+
*
10+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*
18+
*/
19+
20+
#include "kernel/yosys.h"
21+
#include "kernel/sigtools.h"
22+
23+
USING_YOSYS_NAMESPACE
24+
PRIVATE_NAMESPACE_BEGIN
25+
26+
// Standard visitor helper
27+
template<class... Ts>
28+
struct overloaded : Ts... { using Ts::operator()...; };
29+
template<class... Ts>
30+
overloaded(Ts...) -> overloaded<Ts...>;
31+
32+
struct OptBarriersPass : public Pass {
33+
OptBarriersPass() : Pass("optbarriers", "insert optimization barriers") {}
34+
35+
void help() override {
36+
log("\n");
37+
log(" optbarriers [options] [selection]\n");
38+
log("\n");
39+
log("Insert optimization barriers to drivers of selected public wires.\n");
40+
log("\n");
41+
log("\n");
42+
log(" -nocells\n");
43+
log(" don't add optimization barriers to the outputs of cells\n");
44+
log("\n");
45+
log(" -noprocs\n");
46+
log(" don't add optimization barriers to the outputs of processes\n");
47+
log("\n");
48+
log(" -private\n");
49+
log(" also add optimization barriers to private wires\n");
50+
log("\n");
51+
log(" -remove\n");
52+
log(" replace selected optimization barriers with connections\n");
53+
log("\n");
54+
}
55+
56+
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
57+
log_header(design, "Executing OPTBARRIERS pass (insert optimization barriers).\n");
58+
59+
bool nocells_mode = false;
60+
bool noprocs_mode = false;
61+
bool private_mode = false;
62+
bool remove_mode = false;
63+
64+
size_t argidx;
65+
for (argidx = 1; argidx < args.size(); argidx++) {
66+
std::string arg = args[argidx];
67+
if (arg == "-nocells") {
68+
nocells_mode = true;
69+
continue;
70+
}
71+
if (arg == "-noprocs") {
72+
noprocs_mode = true;
73+
continue;
74+
}
75+
if (arg == "-private") {
76+
private_mode = true;
77+
continue;
78+
}
79+
if (arg == "-remove") {
80+
remove_mode = true;
81+
continue;
82+
}
83+
break;
84+
}
85+
extra_args(args, argidx, design);
86+
87+
if (remove_mode) {
88+
log("Replacing optimization barriers with connections.\n");
89+
remove_barriers(design);
90+
return;
91+
}
92+
93+
for (auto* module : design->selected_modules()) {
94+
// We can't just sigmap and iterate through wires for rewriting as
95+
// we want to maintain the structure in connections, and sigmap
96+
// will just return a canonical wire which does not have to be one
97+
// that is directly driving the wire. Therefore for each type of
98+
// object that could be driving the wires (cells, processes,
99+
// connections) we rewrite the sigspecs.
100+
101+
std::vector<RTLIL::SigSig> new_barriers;
102+
103+
// Skip constants, unselected wires and private wires when not in
104+
// private mode. This works for SigChunk or SigBit input.
105+
const auto skip = [&](const auto& chunk) {
106+
if (!chunk.is_wire())
107+
return true;
108+
109+
if (!design->selected(module, chunk.wire))
110+
return true;
111+
112+
if (!private_mode && !chunk.wire->name.isPublic())
113+
return true;
114+
115+
return false;
116+
};
117+
118+
const auto rewrite_sigspec = [&](const SigSpec& sig) {
119+
RTLIL::SigSpec new_output;
120+
for (const auto& chunk : sig.chunks()) {
121+
if (skip(chunk)) {
122+
new_output.append(chunk);
123+
continue;
124+
}
125+
126+
// Rewrite output to drive new wire, and schedule adding
127+
// barrier from new wire to original
128+
auto* new_output_wire = module->addWire(NEW_ID, GetSize(chunk));
129+
new_output.append(new_output_wire);
130+
new_barriers.emplace_back(chunk, new_output_wire);
131+
}
132+
133+
return new_output;
134+
};
135+
136+
// Rewrite cell outputs
137+
if (!nocells_mode)
138+
for (auto* cell : module->cells())
139+
if (cell->type != ID($barrier))
140+
for (const auto& [name, sig] : cell->connections())
141+
if (cell->output(name))
142+
cell->setPort(name, rewrite_sigspec(sig));
143+
144+
// Rewrite connections in processes
145+
if (!noprocs_mode) {
146+
const auto proc_rewriter = overloaded{
147+
// Don't do anything for input sigspecs
148+
[&](const SigSpec&) {},
149+
// Rewrite connections to drive barrier if needed
150+
[&](SigSpec& lhs, const SigSpec&) {
151+
lhs = rewrite_sigspec(lhs);
152+
}
153+
};
154+
155+
for (auto& proc : module->processes)
156+
proc.second->rewrite_sigspecs2(proc_rewriter);
157+
}
158+
159+
// Rewrite connections
160+
std::vector<RTLIL::SigSig> new_connections;
161+
for (const auto& conn : module->connections()) {
162+
RTLIL::SigSig skip_conn, barrier_conn;
163+
164+
for (int i = 0; i < GetSize(conn.first); i++) {
165+
auto& sigsig = skip(conn.first[i]) ? skip_conn : barrier_conn;
166+
sigsig.first.append(conn.first[i]);
167+
sigsig.second.append(conn.second[i]);
168+
}
169+
170+
if (!skip_conn.first.empty())
171+
new_connections.emplace_back(std::move(skip_conn));
172+
173+
if (!barrier_conn.first.empty())
174+
new_barriers.emplace_back(std::move(barrier_conn));
175+
}
176+
module->new_connections(new_connections);
177+
178+
// Add all the scheduled barriers
179+
for (const auto& conn : new_barriers)
180+
module->addBarrier(NEW_ID, conn.second, conn.first);
181+
}
182+
}
183+
184+
void remove_barriers(RTLIL::Design* design) {
185+
for (auto* module : design->selected_modules()) {
186+
std::vector<RTLIL::Cell*> barriers;
187+
188+
for (auto* cell : module->selected_cells())
189+
if (cell->type == ID($barrier))
190+
barriers.emplace_back(cell);
191+
192+
for (auto* cell : barriers) {
193+
const auto lhs = cell->getPort(ID::Y), rhs = cell->getPort(ID::A);
194+
module->connect(lhs, rhs);
195+
module->remove(cell);
196+
}
197+
}
198+
}
199+
200+
} OptBarriersPass;
201+
202+
PRIVATE_NAMESPACE_END

0 commit comments

Comments
 (0)