Use wrap_async_control_gate if ff is fine
[yosys.git] / passes / sat / clk2fflogic.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
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 #include "kernel/ffinit.h"
23 #include "kernel/ff.h"
24 #include "kernel/mem.h"
25
26 USING_YOSYS_NAMESPACE
27 PRIVATE_NAMESPACE_BEGIN
28
29 struct Clk2fflogicPass : public Pass {
30 Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
31 void help() override
32 {
33 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
34 log("\n");
35 log(" clk2fflogic [options] [selection]\n");
36 log("\n");
37 log("This command replaces clocked flip-flops with generic $ff cells that use the\n");
38 log("implicit global clock. This is useful for formal verification of designs with\n");
39 log("multiple clocks.\n");
40 log("\n");
41 }
42 SigSpec wrap_async_control(Module *module, SigSpec sig, bool polarity, bool is_fine, IdString past_sig_id) {
43 if (!is_fine)
44 return wrap_async_control(module, sig, polarity, past_sig_id);
45 return wrap_async_control_gate(module, sig, polarity, past_sig_id);
46 }
47 SigSpec wrap_async_control(Module *module, SigSpec sig, bool polarity, IdString past_sig_id) {
48 Wire *past_sig = module->addWire(past_sig_id, GetSize(sig));
49 past_sig->attributes[ID::init] = RTLIL::Const(polarity ? State::S0 : State::S1, GetSize(sig));
50 module->addFf(NEW_ID, sig, past_sig);
51 if (polarity)
52 sig = module->Or(NEW_ID, sig, past_sig);
53 else
54 sig = module->And(NEW_ID, sig, past_sig);
55 if (polarity)
56 return sig;
57 else
58 return module->Not(NEW_ID, sig);
59 }
60 SigSpec wrap_async_control_gate(Module *module, SigSpec sig, bool polarity, IdString past_sig_id) {
61 Wire *past_sig = module->addWire(past_sig_id);
62 past_sig->attributes[ID::init] = polarity ? State::S0 : State::S1;
63 module->addFfGate(NEW_ID, sig, past_sig);
64 if (polarity)
65 sig = module->OrGate(NEW_ID, sig, past_sig);
66 else
67 sig = module->AndGate(NEW_ID, sig, past_sig);
68 if (polarity)
69 return sig;
70 else
71 return module->NotGate(NEW_ID, sig);
72 }
73 void execute(std::vector<std::string> args, RTLIL::Design *design) override
74 {
75 // bool flag_noinit = false;
76
77 log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
78
79 size_t argidx;
80 for (argidx = 1; argidx < args.size(); argidx++)
81 {
82 // if (args[argidx] == "-noinit") {
83 // flag_noinit = true;
84 // continue;
85 // }
86 break;
87 }
88 extra_args(args, argidx, design);
89
90 for (auto module : design->selected_modules())
91 {
92 SigMap sigmap(module);
93 FfInitVals initvals(&sigmap, module);
94
95 for (auto &mem : Mem::get_selected_memories(module))
96 {
97 for (int i = 0; i < GetSize(mem.rd_ports); i++) {
98 auto &port = mem.rd_ports[i];
99 if (port.clk_enable)
100 log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
101 "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(mem.memid), log_id(module));
102 }
103
104 for (int i = 0; i < GetSize(mem.wr_ports); i++)
105 {
106 auto &port = mem.wr_ports[i];
107
108 if (!port.clk_enable)
109 continue;
110
111 log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
112 i, log_id(module), log_id(mem.memid), log_signal(port.clk),
113 log_signal(port.addr), log_signal(port.data));
114
115 Wire *past_clk = module->addWire(NEW_ID_SUFFIX(stringf("%s#%d#past_clk#%s", log_id(mem.memid), i, log_signal(port.clk))));
116 past_clk->attributes[ID::init] = port.clk_polarity ? State::S1 : State::S0;
117 module->addFf(NEW_ID, port.clk, past_clk);
118
119 SigSpec clock_edge_pattern;
120
121 if (port.clk_polarity) {
122 clock_edge_pattern.append(State::S0);
123 clock_edge_pattern.append(State::S1);
124 } else {
125 clock_edge_pattern.append(State::S1);
126 clock_edge_pattern.append(State::S0);
127 }
128
129 SigSpec clock_edge = module->Eqx(NEW_ID, {port.clk, SigSpec(past_clk)}, clock_edge_pattern);
130
131 SigSpec en_q = module->addWire(NEW_ID_SUFFIX(stringf("%s#%d#en_q", log_id(mem.memid), i)), GetSize(port.en));
132 module->addFf(NEW_ID, port.en, en_q);
133
134 SigSpec addr_q = module->addWire(NEW_ID_SUFFIX(stringf("%s#%d#addr_q", log_id(mem.memid), i)), GetSize(port.addr));
135 module->addFf(NEW_ID, port.addr, addr_q);
136
137 SigSpec data_q = module->addWire(NEW_ID_SUFFIX(stringf("%s#%d#data_q", log_id(mem.memid), i)), GetSize(port.data));
138 module->addFf(NEW_ID, port.data, data_q);
139
140 port.clk = State::S0;
141 port.en = module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge);
142 port.addr = addr_q;
143 port.data = data_q;
144
145 port.clk_enable = false;
146 port.clk_polarity = false;
147 }
148
149 mem.emit();
150 }
151
152 for (auto cell : vector<Cell*>(module->selected_cells()))
153 {
154 SigSpec qval;
155 if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
156 FfData ff(&initvals, cell);
157
158 if (ff.has_gclk) {
159 // Already a $ff or $_FF_ cell.
160 continue;
161 }
162
163 if (ff.has_clk) {
164 log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
165 log_id(module), log_id(cell), log_id(cell->type),
166 log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
167 } else if (ff.has_aload) {
168 log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
169 log_id(module), log_id(cell), log_id(cell->type),
170 log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q));
171 } else {
172 // $sr.
173 log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
174 log_id(module), log_id(cell), log_id(cell->type),
175 log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
176 }
177
178 ff.remove();
179
180 // Strip spaces from signal name, since Yosys IDs can't contain spaces
181 // Spaces only occur when we have a signal that's a slice of a larger bus,
182 // e.g. "\myreg [5:0]", so removing spaces shouldn't result in loss of uniqueness
183 std::string sig_q_str = log_signal(ff.sig_q);
184 sig_q_str.erase(std::remove(sig_q_str.begin(), sig_q_str.end(), ' '), sig_q_str.end());
185
186 Wire *past_q = module->addWire(NEW_ID_SUFFIX(stringf("%s#past_q_wire", sig_q_str.c_str())), ff.width);
187
188 if (!ff.is_fine) {
189 module->addFf(NEW_ID, ff.sig_q, past_q);
190 } else {
191 module->addFfGate(NEW_ID, ff.sig_q, past_q);
192 }
193 if (!ff.val_init.is_fully_undef())
194 initvals.set_init(past_q, ff.val_init);
195
196 if (ff.has_clk) {
197 ff.unmap_ce_srst();
198
199 Wire *past_clk = module->addWire(NEW_ID_SUFFIX(stringf("%s#past_clk#%s", sig_q_str.c_str(), log_signal(ff.sig_clk))));
200 initvals.set_init(past_clk, ff.pol_clk ? State::S1 : State::S0);
201
202 if (!ff.is_fine)
203 module->addFf(NEW_ID, ff.sig_clk, past_clk);
204 else
205 module->addFfGate(NEW_ID, ff.sig_clk, past_clk);
206
207 SigSpec clock_edge_pattern;
208
209 if (ff.pol_clk) {
210 clock_edge_pattern.append(State::S0);
211 clock_edge_pattern.append(State::S1);
212 } else {
213 clock_edge_pattern.append(State::S1);
214 clock_edge_pattern.append(State::S0);
215 }
216
217 SigSpec clock_edge = module->Eqx(NEW_ID, {ff.sig_clk, SigSpec(past_clk)}, clock_edge_pattern);
218
219 Wire *past_d = module->addWire(NEW_ID_SUFFIX(stringf("%s#past_d_wire", sig_q_str.c_str())), ff.width);
220 if (!ff.is_fine)
221 module->addFf(NEW_ID, ff.sig_d, past_d);
222 else
223 module->addFfGate(NEW_ID, ff.sig_d, past_d);
224
225 if (!ff.val_init.is_fully_undef())
226 initvals.set_init(past_d, ff.val_init);
227
228 if (!ff.is_fine)
229 qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
230 else
231 qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
232 } else {
233 qval = past_q;
234 }
235
236 if (ff.has_aload) {
237 SigSpec sig_aload = wrap_async_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine, NEW_ID);
238
239 if (!ff.is_fine)
240 qval = module->Mux(NEW_ID, qval, ff.sig_ad, sig_aload);
241 else
242 qval = module->MuxGate(NEW_ID, qval, ff.sig_ad, sig_aload);
243 }
244
245 if (ff.has_sr) {
246 SigSpec setval = wrap_async_control(module, ff.sig_set, ff.pol_set, ff.is_fine, NEW_ID);
247 SigSpec clrval = wrap_async_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine, NEW_ID);
248 if (!ff.is_fine) {
249 clrval = module->Not(NEW_ID, clrval);
250 qval = module->Or(NEW_ID, qval, setval);
251 module->addAnd(NEW_ID, qval, clrval, ff.sig_q);
252 } else {
253 clrval = module->NotGate(NEW_ID, clrval);
254 qval = module->OrGate(NEW_ID, qval, setval);
255 module->addAndGate(NEW_ID, qval, clrval, ff.sig_q);
256 }
257 } else if (ff.has_arst) {
258 IdString id = NEW_ID_SUFFIX(stringf("%s#past_arst#%s", sig_q_str.c_str(), log_signal(ff.sig_arst)));
259 SigSpec arst = wrap_async_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine, id);
260 if (!ff.is_fine)
261 module->addMux(NEW_ID, qval, ff.val_arst, arst, ff.sig_q);
262 else
263 module->addMuxGate(NEW_ID, qval, ff.val_arst[0], arst, ff.sig_q);
264 } else {
265 module->connect(ff.sig_q, qval);
266 }
267 }
268 }
269 }
270
271 }
272 } Clk2fflogicPass;
273
274 PRIVATE_NAMESPACE_END