Merge pull request #1085 from YosysHQ/eddie/shregmap_improve
[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 bool enable_invert = mux_cells_a.count(sig_data) != 0;
186 Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
187 SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
188
189 sig_data = sigmap(mux->getPort("\\Y"));
190 for (auto bit : sig_data)
191 if (sigbit_users_count[bit] > 1)
192 goto skip_ff_after_read_merging;
193
194 if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
195 {
196 disconnect_dff(sig_data);
197 cell->setPort("\\CLK", clk_data);
198 cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
199 cell->setPort("\\DATA", sig_data);
200 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
201 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
202 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
203 log("merged data $dff with rd enable to cell.\n");
204 return;
205 }
206 }
207 else
208 {
209 if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
210 {
211 disconnect_dff(sig_data);
212 cell->setPort("\\CLK", clk_data);
213 cell->setPort("\\EN", State::S1);
214 cell->setPort("\\DATA", sig_data);
215 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
216 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
217 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
218 log("merged data $dff to cell.\n");
219 return;
220 }
221 }
222
223 skip_ff_after_read_merging:;
224 RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
225 RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
226 if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
227 clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
228 {
229 cell->setPort("\\CLK", clk_addr);
230 cell->setPort("\\EN", State::S1);
231 cell->setPort("\\ADDR", sig_addr);
232 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
233 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
234 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
235 log("merged address $dff to cell.\n");
236 return;
237 }
238
239 log("no (compatible) $dff found.\n");
240 }
241
242 void run(bool flag_wr_only)
243 {
244 for (auto wire : module->wires()) {
245 if (wire->port_output)
246 for (auto bit : sigmap(wire))
247 sigbit_users_count[bit]++;
248 }
249
250 for (auto cell : module->cells()) {
251 if (cell->type == "$dff")
252 dff_cells.push_back(cell);
253 if (cell->type == "$mux") {
254 mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
255 mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
256 }
257 if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
258 SigSpec sig_a = cell->getPort("\\A");
259 SigSpec sig_y = cell->getPort("\\Y");
260 if (cell->type == "$not")
261 sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
262 if (cell->type == "$logic_not")
263 sig_y.extend_u0(1);
264 for (int i = 0; i < GetSize(sig_y); i++)
265 invbits[sig_y[i]] = sig_a[i];
266 }
267 for (auto &conn : cell->connections())
268 if (!cell->known() || cell->input(conn.first))
269 for (auto bit : sigmap(conn.second))
270 sigbit_users_count[bit]++;
271 }
272
273 for (auto cell : module->selected_cells())
274 if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
275 handle_wr_cell(cell);
276
277 if (!flag_wr_only)
278 for (auto cell : module->selected_cells())
279 if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
280 handle_rd_cell(cell);
281 }
282 };
283
284 struct MemoryDffPass : public Pass {
285 MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
286 void help() YS_OVERRIDE
287 {
288 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
289 log("\n");
290 log(" memory_dff [options] [selection]\n");
291 log("\n");
292 log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
293 log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
294 log("interface and yields a synchronous memory port.\n");
295 log("\n");
296 log(" -nordfff\n");
297 log(" do not merge registers on read ports\n");
298 log("\n");
299 }
300 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
301 {
302 bool flag_wr_only = false;
303
304 log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
305
306 size_t argidx;
307 for (argidx = 1; argidx < args.size(); argidx++) {
308 if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
309 flag_wr_only = true;
310 continue;
311 }
312 break;
313 }
314 extra_args(args, argidx, design);
315
316 for (auto mod : design->selected_modules()) {
317 MemoryDffWorker worker(mod);
318 worker.run(flag_wr_only);
319 }
320 }
321 } MemoryDffPass;
322
323 PRIVATE_NAMESPACE_END