Fix a bug in clk2fflogic memory handling
[yosys.git] / passes / sat / clk2fflogic.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/yosys.h"
21 #include "kernel/sigtools.h"
22
23 USING_YOSYS_NAMESPACE
24 PRIVATE_NAMESPACE_BEGIN
25
26 struct Clk2fflogicPass : public Pass {
27 Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
28 virtual void help()
29 {
30 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
31 log("\n");
32 log(" clk2fflogic [options] [selection]\n");
33 log("\n");
34 log("This command replaces clocked flip-flops with generic $ff cells that use the\n");
35 log("implicit global clock. This is useful for formal verification of designs with\n");
36 log("multiple clocks.\n");
37 log("\n");
38 }
39 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
40 {
41 // bool flag_noinit = false;
42
43 log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
44
45 size_t argidx;
46 for (argidx = 1; argidx < args.size(); argidx++)
47 {
48 // if (args[argidx] == "-noinit") {
49 // flag_noinit = true;
50 // continue;
51 // }
52 break;
53 }
54 extra_args(args, argidx, design);
55
56 for (auto module : design->selected_modules())
57 {
58 SigMap sigmap(module);
59 dict<SigBit, State> initbits;
60 pool<SigBit> del_initbits;
61
62 for (auto wire : module->wires())
63 if (wire->attributes.count("\\init") > 0)
64 {
65 Const initval = wire->attributes.at("\\init");
66 SigSpec initsig = sigmap(wire);
67
68 for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
69 if (initval[i] == State::S0 || initval[i] == State::S1)
70 initbits[initsig[i]] = initval[i];
71 }
72
73 for (auto cell : vector<Cell*>(module->selected_cells()))
74 {
75 if (cell->type.in("$mem"))
76 {
77 int abits = cell->getParam("\\ABITS").as_int();
78 int width = cell->getParam("\\WIDTH").as_int();
79 int rd_ports = cell->getParam("\\RD_PORTS").as_int();
80 int wr_ports = cell->getParam("\\WR_PORTS").as_int();
81
82 for (int i = 0; i < rd_ports; i++) {
83 if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
84 log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
85 "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(cell), log_id(module));
86 }
87
88 Const wr_clk_en_param = cell->getParam("\\WR_CLK_ENABLE");
89 Const wr_clk_pol_param = cell->getParam("\\WR_CLK_POLARITY");
90
91 SigSpec wr_clk_port = cell->getPort("\\WR_CLK");
92 SigSpec wr_en_port = cell->getPort("\\WR_EN");
93 SigSpec wr_addr_port = cell->getPort("\\WR_ADDR");
94 SigSpec wr_data_port = cell->getPort("\\WR_DATA");
95
96 for (int wport = 0; wport < wr_ports; wport++)
97 {
98 bool clken = wr_clk_en_param[wport] == State::S1;
99 bool clkpol = wr_clk_pol_param[wport] == State::S1;
100
101 if (!clken)
102 continue;
103
104 SigBit clk = wr_clk_port[wport];
105 SigSpec en = wr_en_port.extract(wport*width, width);
106 SigSpec addr = wr_addr_port.extract(wport*abits, abits);
107 SigSpec data = wr_data_port.extract(wport*width, width);
108
109 log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
110 wport, log_id(module), log_id(cell), log_signal(clk),
111 log_signal(addr), log_signal(data));
112
113 Wire *past_clk = module->addWire(NEW_ID);
114 past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0;
115 module->addFf(NEW_ID, clk, past_clk);
116
117 SigSpec clock_edge_pattern;
118
119 if (clkpol) {
120 clock_edge_pattern.append_bit(State::S0);
121 clock_edge_pattern.append_bit(State::S1);
122 } else {
123 clock_edge_pattern.append_bit(State::S1);
124 clock_edge_pattern.append_bit(State::S0);
125 }
126
127 SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
128
129 SigSpec en_q = module->addWire(NEW_ID, GetSize(en));
130 module->addFf(NEW_ID, en, en_q);
131
132 SigSpec addr_q = module->addWire(NEW_ID, GetSize(addr));
133 module->addFf(NEW_ID, addr, addr_q);
134
135 SigSpec data_q = module->addWire(NEW_ID, GetSize(data));
136 module->addFf(NEW_ID, data, data_q);
137
138 wr_clk_port[wport] = State::S0;
139 wr_en_port.replace(wport*width, module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge));
140 wr_addr_port.replace(wport*abits, addr_q);
141 wr_data_port.replace(wport*width, data_q);
142
143 wr_clk_en_param[wport] = State::S0;
144 wr_clk_pol_param[wport] = State::S0;
145 }
146
147 cell->setParam("\\WR_CLK_ENABLE", wr_clk_en_param);
148 cell->setParam("\\WR_CLK_POLARITY", wr_clk_pol_param);
149
150 cell->setPort("\\WR_CLK", wr_clk_port);
151 cell->setPort("\\WR_EN", wr_en_port);
152 cell->setPort("\\WR_ADDR", wr_addr_port);
153 cell->setPort("\\WR_DATA", wr_data_port);
154 }
155
156 if (cell->type.in("$dlatch"))
157 {
158 bool enpol = cell->parameters["\\EN_POLARITY"].as_bool();
159
160 SigSpec sig_en = cell->getPort("\\EN");
161 SigSpec sig_d = cell->getPort("\\D");
162 SigSpec sig_q = cell->getPort("\\Q");
163
164 log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
165 log_id(module), log_id(cell), log_id(cell->type),
166 log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
167
168 Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
169 module->addFf(NEW_ID, sig_q, past_q);
170
171 if (enpol)
172 module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
173 else
174 module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q);
175
176 Const initval;
177 bool assign_initval = false;
178 for (int i = 0; i < GetSize(sig_d); i++) {
179 SigBit qbit = sigmap(sig_q[i]);
180 if (initbits.count(qbit)) {
181 initval.bits.push_back(initbits.at(qbit));
182 del_initbits.insert(qbit);
183 } else
184 initval.bits.push_back(State::Sx);
185 if (initval.bits.back() != State::Sx)
186 assign_initval = true;
187 }
188
189 if (assign_initval)
190 past_q->attributes["\\init"] = initval;
191
192 module->remove(cell);
193 continue;
194 }
195
196 if (cell->type.in("$dff", "$adff", "$dffsr"))
197 {
198 bool clkpol = cell->parameters["\\CLK_POLARITY"].as_bool();
199
200 SigSpec clk = cell->getPort("\\CLK");
201 Wire *past_clk = module->addWire(NEW_ID);
202 past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0;
203 module->addFf(NEW_ID, clk, past_clk);
204
205 SigSpec sig_d = cell->getPort("\\D");
206 SigSpec sig_q = cell->getPort("\\Q");
207
208 log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
209 log_id(module), log_id(cell), log_id(cell->type),
210 log_signal(clk), log_signal(sig_d), log_signal(sig_q));
211
212 SigSpec clock_edge_pattern;
213
214 if (clkpol) {
215 clock_edge_pattern.append_bit(State::S0);
216 clock_edge_pattern.append_bit(State::S1);
217 } else {
218 clock_edge_pattern.append_bit(State::S1);
219 clock_edge_pattern.append_bit(State::S0);
220 }
221
222 SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
223
224 Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d));
225 Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
226 module->addFf(NEW_ID, sig_d, past_d);
227 module->addFf(NEW_ID, sig_q, past_q);
228
229 if (cell->type == "$adff")
230 {
231 SigSpec arst = cell->getPort("\\ARST");
232 SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
233 Const rstval = cell->parameters["\\ARST_VALUE"];
234
235 if (cell->parameters["\\ARST_POLARITY"].as_bool())
236 module->addMux(NEW_ID, qval, rstval, arst, sig_q);
237 else
238 module->addMux(NEW_ID, rstval, qval, arst, sig_q);
239 }
240 else
241 if (cell->type == "$dffsr")
242 {
243 SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
244 SigSpec setval = cell->getPort("\\SET");
245 SigSpec clrval = cell->getPort("\\CLR");
246
247 if (!cell->parameters["\\SET_POLARITY"].as_bool())
248 setval = module->Not(NEW_ID, setval);
249
250 if (cell->parameters["\\CLR_POLARITY"].as_bool())
251 clrval = module->Not(NEW_ID, clrval);
252
253 qval = module->Or(NEW_ID, qval, setval);
254 module->addAnd(NEW_ID, qval, clrval, sig_q);
255 }
256 else
257 {
258 module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q);
259 }
260
261 Const initval;
262 bool assign_initval = false;
263 for (int i = 0; i < GetSize(sig_d); i++) {
264 SigBit qbit = sigmap(sig_q[i]);
265 if (initbits.count(qbit)) {
266 initval.bits.push_back(initbits.at(qbit));
267 del_initbits.insert(qbit);
268 } else
269 initval.bits.push_back(State::Sx);
270 if (initval.bits.back() != State::Sx)
271 assign_initval = true;
272 }
273
274 if (assign_initval) {
275 past_d->attributes["\\init"] = initval;
276 past_q->attributes["\\init"] = initval;
277 }
278
279 module->remove(cell);
280 continue;
281 }
282 }
283
284 for (auto wire : module->wires())
285 if (wire->attributes.count("\\init") > 0)
286 {
287 bool delete_initattr = true;
288 Const initval = wire->attributes.at("\\init");
289 SigSpec initsig = sigmap(wire);
290
291 for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
292 if (del_initbits.count(initsig[i]) > 0)
293 initval[i] = State::Sx;
294 else if (initval[i] != State::Sx)
295 delete_initattr = false;
296
297 if (delete_initattr)
298 wire->attributes.erase("\\init");
299 else
300 wire->attributes.at("\\init") = initval;
301 }
302 }
303
304 }
305 } Clk2fflogicPass;
306
307 PRIVATE_NAMESPACE_END