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
))
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"));
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
;
194 if (find_sig_before_dff(sig_data
, clk_data
, clk_polarity
, true) && clk_data
!= RTLIL::SigSpec(RTLIL::State::Sx
) && sig_data
== check_q
)
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");
209 if (find_sig_before_dff(sig_data
, clk_data
, clk_polarity
, true) && clk_data
!= RTLIL::SigSpec(RTLIL::State::Sx
))
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");
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
))
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");
239 log("no (compatible) $dff found.\n");
242 void run(bool flag_wr_only
)
244 for (auto wire
: module
->wires()) {
245 if (wire
->port_output
)
246 for (auto bit
: sigmap(wire
))
247 sigbit_users_count
[bit
]++;
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
;
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")
264 for (int i
= 0; i
< GetSize(sig_y
); i
++)
265 invbits
[sig_y
[i
]] = sig_a
[i
];
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
]++;
273 for (auto cell
: module
->selected_cells())
274 if (cell
->type
== "$memwr" && !cell
->parameters
["\\CLK_ENABLE"].as_bool())
275 handle_wr_cell(cell
);
278 for (auto cell
: module
->selected_cells())
279 if (cell
->type
== "$memrd" && !cell
->parameters
["\\CLK_ENABLE"].as_bool())
280 handle_rd_cell(cell
);
284 struct MemoryDffPass
: public Pass
{
285 MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
286 void help() YS_OVERRIDE
288 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
290 log(" memory_dff [options] [selection]\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");
297 log(" do not merge registers on read ports\n");
300 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
302 bool flag_wr_only
= false;
304 log_header(design
, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
307 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
308 if (args
[argidx
] == "-nordff" || args
[argidx
] == "-wr_only") {
314 extra_args(args
, argidx
, design
);
316 for (auto mod
: design
->selected_modules()) {
317 MemoryDffWorker
worker(mod
);
318 worker
.run(flag_wr_only
);
323 PRIVATE_NAMESPACE_END