Merge pull request #1577 from gromero/for-yosys
[yosys.git] / passes / proc / proc_arst.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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/register.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/log.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25
26 YOSYS_NAMESPACE_BEGIN
27 extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth);
28 YOSYS_NAMESPACE_END
29
30 USING_YOSYS_NAMESPACE
31 PRIVATE_NAMESPACE_BEGIN
32
33 bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity)
34 {
35 if (signal.size() != 1)
36 return false;
37 if (signal == ref)
38 return true;
39
40 for (auto cell : mod->cells())
41 {
42 if (cell->type == "$reduce_or" && cell->getPort("\\Y") == signal)
43 return check_signal(mod, cell->getPort("\\A"), ref, polarity);
44
45 if (cell->type == "$reduce_bool" && cell->getPort("\\Y") == signal)
46 return check_signal(mod, cell->getPort("\\A"), ref, polarity);
47
48 if (cell->type == "$logic_not" && cell->getPort("\\Y") == signal) {
49 polarity = !polarity;
50 return check_signal(mod, cell->getPort("\\A"), ref, polarity);
51 }
52
53 if (cell->type == "$not" && cell->getPort("\\Y") == signal) {
54 polarity = !polarity;
55 return check_signal(mod, cell->getPort("\\A"), ref, polarity);
56 }
57
58 if (cell->type.in("$eq", "$eqx") && cell->getPort("\\Y") == signal) {
59 if (cell->getPort("\\A").is_fully_const()) {
60 if (!cell->getPort("\\A").as_bool())
61 polarity = !polarity;
62 return check_signal(mod, cell->getPort("\\B"), ref, polarity);
63 }
64 if (cell->getPort("\\B").is_fully_const()) {
65 if (!cell->getPort("\\B").as_bool())
66 polarity = !polarity;
67 return check_signal(mod, cell->getPort("\\A"), ref, polarity);
68 }
69 }
70
71 if (cell->type.in("$ne", "$nex") && cell->getPort("\\Y") == signal) {
72 if (cell->getPort("\\A").is_fully_const()) {
73 if (cell->getPort("\\A").as_bool())
74 polarity = !polarity;
75 return check_signal(mod, cell->getPort("\\B"), ref, polarity);
76 }
77 if (cell->getPort("\\B").is_fully_const()) {
78 if (cell->getPort("\\B").as_bool())
79 polarity = !polarity;
80 return check_signal(mod, cell->getPort("\\A"), ref, polarity);
81 }
82 }
83 }
84
85 return false;
86 }
87
88 void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown)
89 {
90 for (auto &action : cs->actions) {
91 if (unknown)
92 rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.size()), &rval);
93 else
94 rspec.replace(action.first, action.second, &rval);
95 }
96
97 for (auto sw : cs->switches) {
98 if (sw->signal.size() == 0) {
99 for (auto cs2 : sw->cases)
100 apply_const(mod, rspec, rval, cs2, const_sig, polarity, unknown);
101 }
102 bool this_polarity = polarity;
103 if (check_signal(mod, sw->signal, const_sig, this_polarity)) {
104 for (auto cs2 : sw->cases) {
105 for (auto comp : cs2->compare)
106 if (comp == RTLIL::SigSpec(this_polarity, 1))
107 goto matched_case;
108 if (cs2->compare.size() == 0) {
109 matched_case:
110 apply_const(mod, rspec, rval, cs2, const_sig, polarity, false);
111 break;
112 }
113 }
114 } else {
115 for (auto cs2 : sw->cases)
116 apply_const(mod, rspec, rval, cs2, const_sig, polarity, true);
117 }
118 }
119 }
120
121 void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity)
122 {
123 for (auto sw : cs->switches) {
124 bool this_polarity = polarity;
125 if (check_signal(mod, sw->signal, const_sig, this_polarity)) {
126 bool found_rem_path = false;
127 for (size_t i = 0; i < sw->cases.size(); i++) {
128 RTLIL::CaseRule *cs2 = sw->cases[i];
129 for (auto comp : cs2->compare)
130 if (comp == RTLIL::SigSpec(this_polarity, 1))
131 goto matched_case;
132 if (found_rem_path) {
133 matched_case:
134 sw->cases.erase(sw->cases.begin() + (i--));
135 delete cs2;
136 continue;
137 }
138 found_rem_path = true;
139 cs2->compare.clear();
140 }
141 sw->signal = RTLIL::SigSpec();
142 } else {
143 for (auto cs2 : sw->cases)
144 eliminate_const(mod, cs2, const_sig, polarity);
145 }
146 }
147
148 int dummy_count = 0;
149 bool did_something = true;
150 while (did_something) {
151 did_something = false;
152 proc_clean_case(cs, did_something, dummy_count, 1);
153 }
154 }
155
156 void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map)
157 {
158 restart_proc_arst:
159 if (proc->root_case.switches.size() != 1)
160 return;
161
162 RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal;
163
164 for (auto &sync : proc->syncs) {
165 if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) {
166 bool polarity = sync->type == RTLIL::SyncType::STp;
167 if (check_signal(mod, root_sig, sync->signal, polarity)) {
168 if (proc->syncs.size() == 1) {
169 log("Found VHDL-style edge-trigger %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str());
170 } else {
171 log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str());
172 sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0;
173 }
174 for (auto &action : sync->actions) {
175 RTLIL::SigSpec rspec = assign_map(action.second);
176 RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size());
177 for (int i = 0; i < GetSize(rspec); i++)
178 if (rspec[i].wire == NULL)
179 rval[i] = rspec[i];
180 RTLIL::SigSpec last_rval;
181 for (int count = 0; rval != last_rval; count++) {
182 last_rval = rval;
183 apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false);
184 assign_map.apply(rval);
185 if (rval.is_fully_const())
186 break;
187 if (count > 100)
188 log_error("Async reset %s yields endless loop at value %s for signal %s.\n",
189 log_signal(sync->signal), log_signal(rval), log_signal(action.first));
190 rspec = rval;
191 }
192 if (rval.has_marked_bits())
193 log_error("Async reset %s yields non-constant value %s for signal %s.\n",
194 log_signal(sync->signal), log_signal(rval), log_signal(action.first));
195 action.second = rval;
196 }
197 eliminate_const(mod, &proc->root_case, root_sig, polarity);
198 goto restart_proc_arst;
199 }
200 }
201 }
202 }
203
204 struct ProcArstPass : public Pass {
205 ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { }
206 void help() YS_OVERRIDE
207 {
208 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
209 log("\n");
210 log(" proc_arst [-global_arst [!]<netname>] [selection]\n");
211 log("\n");
212 log("This pass identifies asynchronous resets in the processes and converts them\n");
213 log("to a different internal representation that is suitable for generating\n");
214 log("flip-flop cells with asynchronous resets.\n");
215 log("\n");
216 log(" -global_arst [!]<netname>\n");
217 log(" In modules that have a net with the given name, use this net as async\n");
218 log(" reset for registers that have been assign initial values in their\n");
219 log(" declaration ('reg foobar = constant_value;'). Use the '!' modifier for\n");
220 log(" active low reset signals. Note: the frontend stores the default value\n");
221 log(" in the 'init' attribute on the net.\n");
222 log("\n");
223 }
224 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
225 {
226 std::string global_arst;
227 bool global_arst_neg = false;
228
229 log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n");
230
231 size_t argidx;
232 for (argidx = 1; argidx < args.size(); argidx++)
233 {
234 if (args[argidx] == "-global_arst" && argidx+1 < args.size()) {
235 global_arst = args[++argidx];
236 if (!global_arst.empty() && global_arst[0] == '!') {
237 global_arst_neg = true;
238 global_arst = global_arst.substr(1);
239 }
240 global_arst = RTLIL::escape_id(global_arst);
241 continue;
242 }
243 break;
244 }
245
246 extra_args(args, argidx, design);
247 pool<Wire*> delete_initattr_wires;
248
249 for (auto mod : design->modules())
250 if (design->selected(mod)) {
251 SigMap assign_map(mod);
252 for (auto &proc_it : mod->processes) {
253 if (!design->selected(mod, proc_it.second))
254 continue;
255 proc_arst(mod, proc_it.second, assign_map);
256 if (global_arst.empty() || mod->wire(global_arst) == nullptr)
257 continue;
258 std::vector<RTLIL::SigSig> arst_actions;
259 for (auto sync : proc_it.second->syncs)
260 if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn)
261 for (auto &act : sync->actions) {
262 RTLIL::SigSpec arst_sig, arst_val;
263 for (auto &chunk : act.first.chunks())
264 if (chunk.wire && chunk.wire->attributes.count("\\init")) {
265 RTLIL::SigSpec value = chunk.wire->attributes.at("\\init");
266 value.extend_u0(chunk.wire->width, false);
267 arst_sig.append(chunk);
268 arst_val.append(value.extract(chunk.offset, chunk.width));
269 delete_initattr_wires.insert(chunk.wire);
270 }
271 if (arst_sig.size()) {
272 log("Added global reset to process %s: %s <- %s\n",
273 proc_it.first.c_str(), log_signal(arst_sig), log_signal(arst_val));
274 arst_actions.push_back(RTLIL::SigSig(arst_sig, arst_val));
275 }
276 }
277 if (!arst_actions.empty()) {
278 RTLIL::SyncRule *sync = new RTLIL::SyncRule;
279 sync->type = global_arst_neg ? RTLIL::SyncType::ST0 : RTLIL::SyncType::ST1;
280 sync->signal = mod->wire(global_arst);
281 sync->actions = arst_actions;
282 proc_it.second->syncs.push_back(sync);
283 }
284 }
285 }
286
287 for (auto wire : delete_initattr_wires)
288 wire->attributes.erase("\\init");
289 }
290 } ProcArstPass;
291
292 PRIVATE_NAMESPACE_END