2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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.
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.
20 #include "kernel/yosys.h"
21 #include "kernel/sigtools.h"
24 PRIVATE_NAMESPACE_BEGIN
26 struct MemoryDffWorker
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
;
38 MemoryDffWorker(Module
*module
) : module(module
), sigmap(module
)
40 for (auto wire
: module
->wires()) {
41 if (wire
->attributes
.count("\\init") == 0)
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
]);
51 bool find_sig_before_dff(RTLIL::SigSpec
&sig
, RTLIL::SigSpec
&clk
, bool &clk_polarity
, bool after
= false)
60 if (!after
&& init_bits
.count(sigmap(bit
)))
63 for (auto cell
: dff_cells
)
65 if (after
&& forward_merged_dffs
.count(cell
))
68 SigSpec this_clk
= cell
->getPort("\\CLK");
69 bool this_clk_polarity
= cell
->parameters
["\\CLK_POLARITY"].as_bool();
71 if (invbits
.count(this_clk
)) {
72 this_clk
= invbits
.at(this_clk
);
73 this_clk_polarity
= !this_clk_polarity
;
76 if (clk
!= RTLIL::SigSpec(RTLIL::State::Sx
)) {
79 if (this_clk_polarity
!= clk_polarity
)
83 RTLIL::SigSpec q_norm
= cell
->getPort(after
? "\\D" : "\\Q");
86 RTLIL::SigSpec d
= q_norm
.extract(bit
, &cell
->getPort(after
? "\\Q" : "\\D"));
90 if (after
&& init_bits
.count(d
))
95 clk_polarity
= this_clk_polarity
;
96 candidate_dffs
.insert(cell
);
97 goto replaced_this_bit
;
107 void handle_wr_cell(RTLIL::Cell
*cell
)
109 log("Checking cell `%s' in module `%s': ", cell
->name
.c_str(), module
->name
.c_str());
111 RTLIL::SigSpec clk
= RTLIL::SigSpec(RTLIL::State::Sx
);
112 bool clk_polarity
= 0;
113 candidate_dffs
.clear();
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");
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");
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");
133 if (clk
!= RTLIL::SigSpec(RTLIL::State::Sx
))
135 for (auto cell
: candidate_dffs
)
136 forward_merged_dffs
.insert(cell
);
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
);
145 log("merged $dff to cell.\n");
149 log("no (compatible) $dff found.\n");
152 void disconnect_dff(RTLIL::SigSpec sig
)
155 sig
.sort_and_unify();
157 std::stringstream sstr
;
158 sstr
<< "$memory_dff_disconnected$" << (autoidx
++);
160 RTLIL::SigSpec new_sig
= module
->addWire(sstr
.str(), sig
.size());
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
);
170 void handle_rd_cell(RTLIL::Cell
*cell
)
172 log("Checking cell `%s' in module `%s': ", cell
->name
.c_str(), module
->name
.c_str());
174 bool clk_polarity
= 0;
176 RTLIL::SigSpec clk_data
= RTLIL::SigSpec(RTLIL::State::Sx
);
177 RTLIL::SigSpec sig_data
= cell
->getPort("\\DATA");
179 for (auto bit
: sigmap(sig_data
))
180 if (sigbit_users_count
[bit
] > 1)
181 goto skip_ff_after_read_merging
;
183 if (mux_cells_a
.count(sig_data
) || mux_cells_b
.count(sig_data
))
186 RTLIL::SigSpec check_q
;
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
));
196 for (auto bit
: sig_data
)
197 if (sigbit_users_count
[bit
] > 1)
198 goto skip_ff_after_read_merging
;
200 if (find_sig_before_dff(sig_data
, clk_data
, clk_polarity
, true) && clk_data
!= RTLIL::SigSpec(RTLIL::State::Sx
) && sig_data
== check_q
)
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");
215 if (find_sig_before_dff(sig_data
, clk_data
, clk_polarity
, true) && clk_data
!= RTLIL::SigSpec(RTLIL::State::Sx
))
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");
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
))
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");
245 log("no (compatible) $dff found.\n");
248 void run(bool flag_wr_only
)
250 for (auto wire
: module
->wires()) {
251 if (wire
->port_output
)
252 for (auto bit
: sigmap(wire
))
253 sigbit_users_count
[bit
]++;
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
;
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")
270 for (int i
= 0; i
< GetSize(sig_y
); i
++)
271 invbits
[sig_y
[i
]] = sig_a
[i
];
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
]++;
279 for (auto cell
: module
->selected_cells())
280 if (cell
->type
== "$memwr" && !cell
->parameters
["\\CLK_ENABLE"].as_bool())
281 handle_wr_cell(cell
);
284 for (auto cell
: module
->selected_cells())
285 if (cell
->type
== "$memrd" && !cell
->parameters
["\\CLK_ENABLE"].as_bool())
286 handle_rd_cell(cell
);
290 struct MemoryDffPass
: public Pass
{
291 MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
292 void help() YS_OVERRIDE
294 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
296 log(" memory_dff [options] [selection]\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");
303 log(" do not merge registers on read ports\n");
306 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
308 bool flag_wr_only
= false;
310 log_header(design
, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
313 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
314 if (args
[argidx
] == "-nordff" || args
[argidx
] == "-wr_only") {
320 extra_args(args
, argidx
, design
);
322 for (auto mod
: design
->selected_modules()) {
323 MemoryDffWorker
worker(mod
);
324 worker
.run(flag_wr_only
);
329 PRIVATE_NAMESPACE_END