Fix spacing
[yosys.git] / passes / memory / memory_dff.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 MemoryDffWorker
27 {
28 Module *module;
29 SigMap sigmap;
30
31 vector<Cell*> dff_cells;
32 dict<SigBit, SigBit> invbits;
33 dict<SigBit, int> sigbit_users_count;
34 dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
35 pool<Cell*> forward_merged_dffs, candidate_dffs;
36 pool<SigBit> init_bits;
37
38 MemoryDffWorker(Module *module) : module(module), sigmap(module)
39 {
40 for (auto wire : module->wires()) {
41 if (wire->attributes.count("\\init") == 0)
42 continue;
43 SigSpec sig = sigmap(wire);
44 Const initval = wire->attributes.at("\\init");
45 for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
46 if (initval[i] == State::S0 || initval[i] == State::S1)
47 init_bits.insert(sig[i]);
48 }
49 }
50
51 bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
52 {
53 sigmap.apply(sig);
54
55 for (auto &bit : sig)
56 {
57 if (bit.wire == NULL)
58 continue;
59
60 if (!after && init_bits.count(sigmap(bit)))
61 return false;
62
63 for (auto cell : dff_cells)
64 {
65 if (after && forward_merged_dffs.count(cell))
66 continue;
67
68 SigSpec this_clk = cell->getPort("\\CLK");
69 bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
70
71 if (invbits.count(this_clk)) {
72 this_clk = invbits.at(this_clk);
73 this_clk_polarity = !this_clk_polarity;
74 }
75
76 if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
77 if (this_clk != clk)
78 continue;
79 if (this_clk_polarity != clk_polarity)
80 continue;
81 }
82
83 RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q");
84 sigmap.apply(q_norm);
85
86 RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D"));
87 if (d.size() != 1)
88 continue;
89
90 if (after && init_bits.count(d))
91 return false;
92
93 bit = d;
94 clk = this_clk;
95 clk_polarity = this_clk_polarity;
96 candidate_dffs.insert(cell);
97 goto replaced_this_bit;
98 }
99
100 return false;
101 replaced_this_bit:;
102 }
103
104 return true;
105 }
106
107 void handle_wr_cell(RTLIL::Cell *cell)
108 {
109 log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
110
111 RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
112 bool clk_polarity = 0;
113 candidate_dffs.clear();
114
115 RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
116 if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
117 log("no (compatible) $dff for address input found.\n");
118 return;
119 }
120
121 RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
122 if (!find_sig_before_dff(sig_data, clk, clk_polarity)) {
123 log("no (compatible) $dff for data input found.\n");
124 return;
125 }
126
127 RTLIL::SigSpec sig_en = cell->getPort("\\EN");
128 if (!find_sig_before_dff(sig_en, clk, clk_polarity)) {
129 log("no (compatible) $dff for enable input found.\n");
130 return;
131 }
132
133 if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
134 {
135 for (auto cell : candidate_dffs)
136 forward_merged_dffs.insert(cell);
137
138 cell->setPort("\\CLK", clk);
139 cell->setPort("\\ADDR", sig_addr);
140 cell->setPort("\\DATA", sig_data);
141 cell->setPort("\\EN", sig_en);
142 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
143 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
144
145 log("merged $dff to cell.\n");
146 return;
147 }
148
149 log("no (compatible) $dff found.\n");
150 }
151
152 void disconnect_dff(RTLIL::SigSpec sig)
153 {
154 sigmap.apply(sig);
155 sig.sort_and_unify();
156
157 std::stringstream sstr;
158 sstr << "$memory_dff_disconnected$" << (autoidx++);
159
160 RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
161
162 for (auto cell : module->cells())
163 if (cell->type == "$dff") {
164 RTLIL::SigSpec new_q = cell->getPort("\\Q");
165 new_q.replace(sig, new_sig);
166 cell->setPort("\\Q", new_q);
167 }
168 }
169
170 void handle_rd_cell(RTLIL::Cell *cell)
171 {
172 log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
173
174 bool clk_polarity = 0;
175
176 RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
177 RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
178
179 for (auto bit : sigmap(sig_data))
180 if (sigbit_users_count[bit] > 1)
181 goto skip_ff_after_read_merging;
182
183 if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
184 {
185 RTLIL::SigSpec en;
186 RTLIL::SigSpec check_q;
187
188 do {
189 bool enable_invert = mux_cells_a.count(sig_data) != 0;
190 Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
191 check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
192 sig_data = sigmap(mux->getPort("\\Y"));
193 en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
194 } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data));
195
196 for (auto bit : sig_data)
197 if (sigbit_users_count[bit] > 1)
198 goto skip_ff_after_read_merging;
199
200 if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
201 {
202 disconnect_dff(sig_data);
203 cell->setPort("\\CLK", clk_data);
204 cell->setPort("\\EN", en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
205 cell->setPort("\\DATA", sig_data);
206 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
207 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
208 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
209 log("merged data $dff with rd enable to cell.\n");
210 return;
211 }
212 }
213 else
214 {
215 if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
216 {
217 disconnect_dff(sig_data);
218 cell->setPort("\\CLK", clk_data);
219 cell->setPort("\\EN", State::S1);
220 cell->setPort("\\DATA", sig_data);
221 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
222 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
223 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
224 log("merged data $dff to cell.\n");
225 return;
226 }
227 }
228
229 skip_ff_after_read_merging:;
230 RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
231 RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
232 if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
233 clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
234 {
235 cell->setPort("\\CLK", clk_addr);
236 cell->setPort("\\EN", State::S1);
237 cell->setPort("\\ADDR", sig_addr);
238 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
239 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
240 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
241 log("merged address $dff to cell.\n");
242 return;
243 }
244
245 log("no (compatible) $dff found.\n");
246 }
247
248 void run(bool flag_wr_only)
249 {
250 for (auto wire : module->wires()) {
251 if (wire->port_output)
252 for (auto bit : sigmap(wire))
253 sigbit_users_count[bit]++;
254 }
255
256 for (auto cell : module->cells()) {
257 if (cell->type == "$dff")
258 dff_cells.push_back(cell);
259 if (cell->type == "$mux") {
260 mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
261 mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
262 }
263 if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
264 SigSpec sig_a = cell->getPort("\\A");
265 SigSpec sig_y = cell->getPort("\\Y");
266 if (cell->type == "$not")
267 sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
268 if (cell->type == "$logic_not")
269 sig_y.extend_u0(1);
270 for (int i = 0; i < GetSize(sig_y); i++)
271 invbits[sig_y[i]] = sig_a[i];
272 }
273 for (auto &conn : cell->connections())
274 if (!cell->known() || cell->input(conn.first))
275 for (auto bit : sigmap(conn.second))
276 sigbit_users_count[bit]++;
277 }
278
279 for (auto cell : module->selected_cells())
280 if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
281 handle_wr_cell(cell);
282
283 if (!flag_wr_only)
284 for (auto cell : module->selected_cells())
285 if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
286 handle_rd_cell(cell);
287 }
288 };
289
290 struct MemoryDffPass : public Pass {
291 MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
292 void help() YS_OVERRIDE
293 {
294 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
295 log("\n");
296 log(" memory_dff [options] [selection]\n");
297 log("\n");
298 log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
299 log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
300 log("interface and yields a synchronous memory port.\n");
301 log("\n");
302 log(" -nordfff\n");
303 log(" do not merge registers on read ports\n");
304 log("\n");
305 }
306 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
307 {
308 bool flag_wr_only = false;
309
310 log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
311
312 size_t argidx;
313 for (argidx = 1; argidx < args.size(); argidx++) {
314 if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
315 flag_wr_only = true;
316 continue;
317 }
318 break;
319 }
320 extra_args(args, argidx, design);
321
322 for (auto mod : design->selected_modules()) {
323 MemoryDffWorker worker(mod);
324 worker.run(flag_wr_only);
325 }
326 }
327 } MemoryDffPass;
328
329 PRIVATE_NAMESPACE_END