dee48597fbef6513b4d212b585425b4a44eb6ef3
[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/register.h"
21 #include "kernel/log.h"
22 #include <stdlib.h>
23 #include <assert.h>
24 #include <sstream>
25
26 static void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig)
27 {
28 for (auto &conn : module->connections)
29 sig.replace(conn.first, conn.second);
30 }
31
32 static bool find_sig_before_dff(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
33 {
34 normalize_sig(module, sig);
35 sig.expand();
36
37 for (size_t i = 0; i < sig.chunks().size(); i++)
38 {
39 RTLIL::SigChunk &chunk = sig.chunks_rw()[i];
40
41 if (chunk.wire == NULL)
42 continue;
43
44 for (auto &cell_it : module->cells)
45 {
46 RTLIL::Cell *cell = cell_it.second;
47
48 if (cell->type != "$dff")
49 continue;
50
51 if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
52 if (cell->connections["\\CLK"] != clk)
53 continue;
54 if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity)
55 continue;
56 }
57
58 RTLIL::SigSpec q_norm = cell->connections[after ? "\\D" : "\\Q"];
59 normalize_sig(module, q_norm);
60
61 RTLIL::SigSpec d = q_norm.extract(chunk, &cell->connections[after ? "\\Q" : "\\D"]);
62 if (d.size() != 1)
63 continue;
64
65 assert(d.chunks().size() == 1);
66 chunk = d.chunks()[0];
67 clk = cell->connections["\\CLK"];
68 clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
69 goto replaced_this_bit;
70 }
71
72 return false;
73 replaced_this_bit:;
74 }
75
76 sig.optimize();
77 return true;
78 }
79
80 static void handle_wr_cell(RTLIL::Module *module, RTLIL::Cell *cell)
81 {
82 log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
83
84 RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
85 bool clk_polarity = 0;
86
87 RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"];
88 if (!find_sig_before_dff(module, sig_addr, clk, clk_polarity)) {
89 log("no (compatible) $dff for address input found.\n");
90 return;
91 }
92
93 RTLIL::SigSpec sig_data = cell->connections["\\DATA"];
94 if (!find_sig_before_dff(module, sig_data, clk, clk_polarity)) {
95 log("no (compatible) $dff for data input found.\n");
96 return;
97 }
98
99 RTLIL::SigSpec sig_en = cell->connections["\\EN"];
100 if (!find_sig_before_dff(module, sig_en, clk, clk_polarity)) {
101 log("no (compatible) $dff for enable input found.\n");
102 return;
103 }
104
105 if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
106 cell->connections["\\CLK"] = clk;
107 cell->connections["\\ADDR"] = sig_addr;
108 cell->connections["\\DATA"] = sig_data;
109 cell->connections["\\EN"] = sig_en;
110 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
111 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
112 log("merged $dff to cell.\n");
113 }
114
115 log("no (compatible) $dff found.\n");
116 }
117
118 static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig)
119 {
120 normalize_sig(module, sig);
121 sig.sort_and_unify();
122
123 std::stringstream sstr;
124 sstr << "$memory_dff_disconnected$" << (RTLIL::autoidx++);
125
126 RTLIL::Wire *wire = new RTLIL::Wire;
127 wire->name = sstr.str();
128 wire->width = sig.size();
129 module->wires[wire->name] = wire;
130
131 RTLIL::SigSpec newsig(wire);
132
133 for (auto &cell_it : module->cells) {
134 RTLIL::Cell *cell = cell_it.second;
135 if (cell->type == "$dff")
136 cell->connections["\\Q"].replace(sig, newsig);
137 }
138 }
139
140 static void handle_rd_cell(RTLIL::Module *module, RTLIL::Cell *cell)
141 {
142 log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
143
144 bool clk_polarity = 0;
145
146 RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
147 RTLIL::SigSpec sig_data = cell->connections["\\DATA"];
148 if (find_sig_before_dff(module, sig_data, clk_data, clk_polarity, true) &&
149 clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
150 {
151 disconnect_dff(module, sig_data);
152 cell->connections["\\CLK"] = clk_data;
153 cell->connections["\\DATA"] = sig_data;
154 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
155 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
156 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
157 log("merged data $dff to cell.\n");
158 return;
159 }
160
161 RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
162 RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"];
163 if (find_sig_before_dff(module, sig_addr, clk_addr, clk_polarity) &&
164 clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
165 {
166 cell->connections["\\CLK"] = clk_addr;
167 cell->connections["\\ADDR"] = sig_addr;
168 cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
169 cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
170 cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
171 log("merged address $dff to cell.\n");
172 return;
173 }
174
175 log("no (compatible) $dff found.\n");
176 }
177
178 static void handle_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_wr_only)
179 {
180 for (auto &cell_it : module->cells) {
181 if (!design->selected(module, cell_it.second))
182 continue;
183 if (cell_it.second->type == "$memwr" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool())
184 handle_wr_cell(module, cell_it.second);
185 if (!flag_wr_only && cell_it.second->type == "$memrd" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool())
186 handle_rd_cell(module, cell_it.second);
187 }
188 }
189
190 struct MemoryDffPass : public Pass {
191 MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
192 virtual void help()
193 {
194 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
195 log("\n");
196 log(" memory_dff [options] [selection]\n");
197 log("\n");
198 log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
199 log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
200 log("interface and yields a synchronous memory port.\n");
201 log("\n");
202 log(" -wr_only\n");
203 log(" do not merge registers on read ports\n");
204 log("\n");
205 }
206 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
207 {
208 bool flag_wr_only = false;
209
210 log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
211
212 size_t argidx;
213 for (argidx = 1; argidx < args.size(); argidx++) {
214 if (args[argidx] == "-wr_only") {
215 flag_wr_only = true;
216 continue;
217 }
218 break;
219 }
220 extra_args(args, argidx, design);
221
222 for (auto &mod_it : design->modules)
223 if (design->selected(mod_it.second))
224 handle_module(design, mod_it.second, flag_wr_only);
225 }
226 } MemoryDffPass;
227