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"
22 #include "kernel/ffinit.h"
23 #include "kernel/ff.h"
24 #include "kernel/mem.h"
27 PRIVATE_NAMESPACE_BEGIN
29 struct Clk2fflogicPass
: public Pass
{
30 Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
33 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
35 log(" clk2fflogic [options] [selection]\n");
37 log("This command replaces clocked flip-flops with generic $ff cells that use the\n");
38 log("implicit global clock. This is useful for formal verification of designs with\n");
39 log("multiple clocks.\n");
42 SigSpec
wrap_async_control(Module
*module
, SigSpec sig
, bool polarity
) {
43 Wire
*past_sig
= module
->addWire(NEW_ID
, GetSize(sig
));
44 module
->addFf(NEW_ID
, sig
, past_sig
);
46 sig
= module
->Or(NEW_ID
, sig
, past_sig
);
48 sig
= module
->And(NEW_ID
, sig
, past_sig
);
52 return module
->Not(NEW_ID
, sig
);
54 SigSpec
wrap_async_control_gate(Module
*module
, SigSpec sig
, bool polarity
) {
55 Wire
*past_sig
= module
->addWire(NEW_ID
);
56 module
->addFfGate(NEW_ID
, sig
, past_sig
);
58 sig
= module
->OrGate(NEW_ID
, sig
, past_sig
);
60 sig
= module
->AndGate(NEW_ID
, sig
, past_sig
);
64 return module
->NotGate(NEW_ID
, sig
);
66 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) override
68 // bool flag_noinit = false;
70 log_header(design
, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
73 for (argidx
= 1; argidx
< args
.size(); argidx
++)
75 // if (args[argidx] == "-noinit") {
76 // flag_noinit = true;
81 extra_args(args
, argidx
, design
);
83 for (auto module
: design
->selected_modules())
85 SigMap
sigmap(module
);
86 FfInitVals
initvals(&sigmap
, module
);
88 for (auto &mem
: Mem::get_selected_memories(module
))
90 for (int i
= 0; i
< GetSize(mem
.rd_ports
); i
++) {
91 auto &port
= mem
.rd_ports
[i
];
93 log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
94 "Call \"memory\" with -nordff to avoid this error.\n", i
, log_id(mem
.memid
), log_id(module
));
97 for (int i
= 0; i
< GetSize(mem
.wr_ports
); i
++)
99 auto &port
= mem
.wr_ports
[i
];
101 if (!port
.clk_enable
)
104 log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
105 i
, log_id(module
), log_id(mem
.memid
), log_signal(port
.clk
),
106 log_signal(port
.addr
), log_signal(port
.data
));
108 Wire
*past_clk
= module
->addWire(NEW_ID
);
109 past_clk
->attributes
[ID::init
] = port
.clk_polarity
? State::S1
: State::S0
;
110 module
->addFf(NEW_ID
, port
.clk
, past_clk
);
112 SigSpec clock_edge_pattern
;
114 if (port
.clk_polarity
) {
115 clock_edge_pattern
.append(State::S0
);
116 clock_edge_pattern
.append(State::S1
);
118 clock_edge_pattern
.append(State::S1
);
119 clock_edge_pattern
.append(State::S0
);
122 SigSpec clock_edge
= module
->Eqx(NEW_ID
, {port
.clk
, SigSpec(past_clk
)}, clock_edge_pattern
);
124 SigSpec en_q
= module
->addWire(NEW_ID
, GetSize(port
.en
));
125 module
->addFf(NEW_ID
, port
.en
, en_q
);
127 SigSpec addr_q
= module
->addWire(NEW_ID
, GetSize(port
.addr
));
128 module
->addFf(NEW_ID
, port
.addr
, addr_q
);
130 SigSpec data_q
= module
->addWire(NEW_ID
, GetSize(port
.data
));
131 module
->addFf(NEW_ID
, port
.data
, data_q
);
133 port
.clk
= State::S0
;
134 port
.en
= module
->Mux(NEW_ID
, Const(0, GetSize(en_q
)), en_q
, clock_edge
);
138 port
.clk_enable
= false;
139 port
.clk_polarity
= false;
145 for (auto cell
: vector
<Cell
*>(module
->selected_cells()))
148 if (RTLIL::builtin_ff_cell_types().count(cell
->type
)) {
149 FfData
ff(&initvals
, cell
);
151 if (ff
.has_d
&& !ff
.has_clk
&& !ff
.has_en
) {
152 // Already a $ff or $_FF_ cell.
156 Wire
*past_q
= module
->addWire(NEW_ID
, ff
.width
);
158 module
->addFf(NEW_ID
, ff
.sig_q
, past_q
);
160 module
->addFfGate(NEW_ID
, ff
.sig_q
, past_q
);
162 if (!ff
.val_init
.is_fully_undef())
163 initvals
.set_init(past_q
, ff
.val_init
);
166 ff
.unmap_ce_srst(module
);
168 Wire
*past_clk
= module
->addWire(NEW_ID
);
169 initvals
.set_init(past_clk
, ff
.pol_clk
? State::S1
: State::S0
);
172 module
->addFf(NEW_ID
, ff
.sig_clk
, past_clk
);
174 module
->addFfGate(NEW_ID
, ff
.sig_clk
, past_clk
);
176 log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
177 log_id(module
), log_id(cell
), log_id(cell
->type
),
178 log_signal(ff
.sig_clk
), log_signal(ff
.sig_d
), log_signal(ff
.sig_q
));
180 SigSpec clock_edge_pattern
;
183 clock_edge_pattern
.append(State::S0
);
184 clock_edge_pattern
.append(State::S1
);
186 clock_edge_pattern
.append(State::S1
);
187 clock_edge_pattern
.append(State::S0
);
190 SigSpec clock_edge
= module
->Eqx(NEW_ID
, {ff
.sig_clk
, SigSpec(past_clk
)}, clock_edge_pattern
);
192 Wire
*past_d
= module
->addWire(NEW_ID
, ff
.width
);
194 module
->addFf(NEW_ID
, ff
.sig_d
, past_d
);
196 module
->addFfGate(NEW_ID
, ff
.sig_d
, past_d
);
198 if (!ff
.val_init
.is_fully_undef())
199 initvals
.set_init(past_d
, ff
.val_init
);
202 qval
= module
->Mux(NEW_ID
, past_q
, past_d
, clock_edge
);
204 qval
= module
->MuxGate(NEW_ID
, past_q
, past_d
, clock_edge
);
205 } else if (ff
.has_d
) {
207 log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
208 log_id(module
), log_id(cell
), log_id(cell
->type
),
209 log_signal(ff
.sig_en
), log_signal(ff
.sig_d
), log_signal(ff
.sig_q
));
211 SigSpec sig_en
= wrap_async_control(module
, ff
.sig_en
, ff
.pol_en
);
214 qval
= module
->Mux(NEW_ID
, past_q
, ff
.sig_d
, sig_en
);
216 qval
= module
->MuxGate(NEW_ID
, past_q
, ff
.sig_d
, sig_en
);
219 log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
220 log_id(module
), log_id(cell
), log_id(cell
->type
),
221 log_signal(ff
.sig_set
), log_signal(ff
.sig_clr
), log_signal(ff
.sig_q
));
227 SigSpec setval
= wrap_async_control(module
, ff
.sig_set
, ff
.pol_set
);
228 SigSpec clrval
= wrap_async_control(module
, ff
.sig_clr
, ff
.pol_clr
);
230 clrval
= module
->Not(NEW_ID
, clrval
);
231 qval
= module
->Or(NEW_ID
, qval
, setval
);
232 module
->addAnd(NEW_ID
, qval
, clrval
, ff
.sig_q
);
234 clrval
= module
->NotGate(NEW_ID
, clrval
);
235 qval
= module
->OrGate(NEW_ID
, qval
, setval
);
236 module
->addAndGate(NEW_ID
, qval
, clrval
, ff
.sig_q
);
238 } else if (ff
.has_arst
) {
239 SigSpec arst
= wrap_async_control(module
, ff
.sig_arst
, ff
.pol_arst
);
241 module
->addMux(NEW_ID
, qval
, ff
.val_arst
, arst
, ff
.sig_q
);
243 module
->addMuxGate(NEW_ID
, qval
, ff
.val_arst
[0], arst
, ff
.sig_q
);
245 module
->connect(ff
.sig_q
, qval
);
248 initvals
.remove_init(ff
.sig_q
);
249 module
->remove(cell
);
258 PRIVATE_NAMESPACE_END