2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
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
, bool is_fine
, IdString past_sig_id
) {
44 return wrap_async_control(module
, sig
, polarity
, past_sig_id
);
45 return wrap_async_control_gate(module
, sig
, polarity
, past_sig_id
);
47 SigSpec
wrap_async_control(Module
*module
, SigSpec sig
, bool polarity
, IdString past_sig_id
) {
48 Wire
*past_sig
= module
->addWire(past_sig_id
, GetSize(sig
));
49 past_sig
->attributes
[ID::init
] = RTLIL::Const(polarity
? State::S0
: State::S1
, GetSize(sig
));
50 module
->addFf(NEW_ID
, sig
, past_sig
);
52 sig
= module
->Or(NEW_ID
, sig
, past_sig
);
54 sig
= module
->And(NEW_ID
, sig
, past_sig
);
58 return module
->Not(NEW_ID
, sig
);
60 SigSpec
wrap_async_control_gate(Module
*module
, SigSpec sig
, bool polarity
, IdString past_sig_id
) {
61 Wire
*past_sig
= module
->addWire(past_sig_id
);
62 past_sig
->attributes
[ID::init
] = polarity
? State::S0
: State::S1
;
63 module
->addFfGate(NEW_ID
, sig
, past_sig
);
65 sig
= module
->OrGate(NEW_ID
, sig
, past_sig
);
67 sig
= module
->AndGate(NEW_ID
, sig
, past_sig
);
71 return module
->NotGate(NEW_ID
, sig
);
73 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) override
75 // bool flag_noinit = false;
77 log_header(design
, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
80 for (argidx
= 1; argidx
< args
.size(); argidx
++)
82 // if (args[argidx] == "-noinit") {
83 // flag_noinit = true;
88 extra_args(args
, argidx
, design
);
90 for (auto module
: design
->selected_modules())
92 SigMap
sigmap(module
);
93 FfInitVals
initvals(&sigmap
, module
);
95 for (auto &mem
: Mem::get_selected_memories(module
))
97 for (int i
= 0; i
< GetSize(mem
.rd_ports
); i
++) {
98 auto &port
= mem
.rd_ports
[i
];
100 log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
101 "Call \"memory\" with -nordff to avoid this error.\n", i
, log_id(mem
.memid
), log_id(module
));
104 for (int i
= 0; i
< GetSize(mem
.wr_ports
); i
++)
106 auto &port
= mem
.wr_ports
[i
];
108 if (!port
.clk_enable
)
111 log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
112 i
, log_id(module
), log_id(mem
.memid
), log_signal(port
.clk
),
113 log_signal(port
.addr
), log_signal(port
.data
));
115 Wire
*past_clk
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#%d#past_clk#%s", log_id(mem
.memid
), i
, log_signal(port
.clk
))));
116 past_clk
->attributes
[ID::init
] = port
.clk_polarity
? State::S1
: State::S0
;
117 module
->addFf(NEW_ID
, port
.clk
, past_clk
);
119 SigSpec clock_edge_pattern
;
121 if (port
.clk_polarity
) {
122 clock_edge_pattern
.append(State::S0
);
123 clock_edge_pattern
.append(State::S1
);
125 clock_edge_pattern
.append(State::S1
);
126 clock_edge_pattern
.append(State::S0
);
129 SigSpec clock_edge
= module
->Eqx(NEW_ID
, {port
.clk
, SigSpec(past_clk
)}, clock_edge_pattern
);
131 SigSpec en_q
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#%d#en_q", log_id(mem
.memid
), i
)), GetSize(port
.en
));
132 module
->addFf(NEW_ID
, port
.en
, en_q
);
134 SigSpec addr_q
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#%d#addr_q", log_id(mem
.memid
), i
)), GetSize(port
.addr
));
135 module
->addFf(NEW_ID
, port
.addr
, addr_q
);
137 SigSpec data_q
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#%d#data_q", log_id(mem
.memid
), i
)), GetSize(port
.data
));
138 module
->addFf(NEW_ID
, port
.data
, data_q
);
140 port
.clk
= State::S0
;
141 port
.en
= module
->Mux(NEW_ID
, Const(0, GetSize(en_q
)), en_q
, clock_edge
);
145 port
.clk_enable
= false;
146 port
.clk_polarity
= false;
152 for (auto cell
: vector
<Cell
*>(module
->selected_cells()))
155 if (RTLIL::builtin_ff_cell_types().count(cell
->type
)) {
156 FfData
ff(&initvals
, cell
);
159 // Already a $ff or $_FF_ cell.
164 log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
165 log_id(module
), log_id(cell
), log_id(cell
->type
),
166 log_signal(ff
.sig_clk
), log_signal(ff
.sig_d
), log_signal(ff
.sig_q
));
167 } else if (ff
.has_aload
) {
168 log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
169 log_id(module
), log_id(cell
), log_id(cell
->type
),
170 log_signal(ff
.sig_aload
), log_signal(ff
.sig_ad
), log_signal(ff
.sig_q
));
173 log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
174 log_id(module
), log_id(cell
), log_id(cell
->type
),
175 log_signal(ff
.sig_set
), log_signal(ff
.sig_clr
), log_signal(ff
.sig_q
));
180 // Strip spaces from signal name, since Yosys IDs can't contain spaces
181 // Spaces only occur when we have a signal that's a slice of a larger bus,
182 // e.g. "\myreg [5:0]", so removing spaces shouldn't result in loss of uniqueness
183 std::string sig_q_str
= log_signal(ff
.sig_q
);
184 sig_q_str
.erase(std::remove(sig_q_str
.begin(), sig_q_str
.end(), ' '), sig_q_str
.end());
186 Wire
*past_q
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#past_q_wire", sig_q_str
.c_str())), ff
.width
);
189 module
->addFf(NEW_ID
, ff
.sig_q
, past_q
);
191 module
->addFfGate(NEW_ID
, ff
.sig_q
, past_q
);
193 if (!ff
.val_init
.is_fully_undef())
194 initvals
.set_init(past_q
, ff
.val_init
);
199 Wire
*past_clk
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#past_clk#%s", sig_q_str
.c_str(), log_signal(ff
.sig_clk
))));
200 initvals
.set_init(past_clk
, ff
.pol_clk
? State::S1
: State::S0
);
203 module
->addFf(NEW_ID
, ff
.sig_clk
, past_clk
);
205 module
->addFfGate(NEW_ID
, ff
.sig_clk
, past_clk
);
207 SigSpec clock_edge_pattern
;
210 clock_edge_pattern
.append(State::S0
);
211 clock_edge_pattern
.append(State::S1
);
213 clock_edge_pattern
.append(State::S1
);
214 clock_edge_pattern
.append(State::S0
);
217 SigSpec clock_edge
= module
->Eqx(NEW_ID
, {ff
.sig_clk
, SigSpec(past_clk
)}, clock_edge_pattern
);
219 Wire
*past_d
= module
->addWire(NEW_ID_SUFFIX(stringf("%s#past_d_wire", sig_q_str
.c_str())), ff
.width
);
221 module
->addFf(NEW_ID
, ff
.sig_d
, past_d
);
223 module
->addFfGate(NEW_ID
, ff
.sig_d
, past_d
);
225 if (!ff
.val_init
.is_fully_undef())
226 initvals
.set_init(past_d
, ff
.val_init
);
229 qval
= module
->Mux(NEW_ID
, past_q
, past_d
, clock_edge
);
231 qval
= module
->MuxGate(NEW_ID
, past_q
, past_d
, clock_edge
);
237 SigSpec sig_aload
= wrap_async_control(module
, ff
.sig_aload
, ff
.pol_aload
, ff
.is_fine
, NEW_ID
);
240 qval
= module
->Mux(NEW_ID
, qval
, ff
.sig_ad
, sig_aload
);
242 qval
= module
->MuxGate(NEW_ID
, qval
, ff
.sig_ad
, sig_aload
);
246 SigSpec setval
= wrap_async_control(module
, ff
.sig_set
, ff
.pol_set
, ff
.is_fine
, NEW_ID
);
247 SigSpec clrval
= wrap_async_control(module
, ff
.sig_clr
, ff
.pol_clr
, ff
.is_fine
, NEW_ID
);
249 clrval
= module
->Not(NEW_ID
, clrval
);
250 qval
= module
->Or(NEW_ID
, qval
, setval
);
251 module
->addAnd(NEW_ID
, qval
, clrval
, ff
.sig_q
);
253 clrval
= module
->NotGate(NEW_ID
, clrval
);
254 qval
= module
->OrGate(NEW_ID
, qval
, setval
);
255 module
->addAndGate(NEW_ID
, qval
, clrval
, ff
.sig_q
);
257 } else if (ff
.has_arst
) {
258 IdString id
= NEW_ID_SUFFIX(stringf("%s#past_arst#%s", sig_q_str
.c_str(), log_signal(ff
.sig_arst
)));
259 SigSpec arst
= wrap_async_control(module
, ff
.sig_arst
, ff
.pol_arst
, ff
.is_fine
, id
);
261 module
->addMux(NEW_ID
, qval
, ff
.val_arst
, arst
, ff
.sig_q
);
263 module
->addMuxGate(NEW_ID
, qval
, ff
.val_arst
[0], arst
, ff
.sig_q
);
265 module
->connect(ff
.sig_q
, qval
);
274 PRIVATE_NAMESPACE_END