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 #include "passes/pmgen/ice40_dsp_pm.h"
28 void create_ice40_dsp(ice40_dsp_pm
&pm
)
30 auto &st
= pm
.st_ice40_dsp
;
32 log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm
.module
), log_id(st
.mul
));
34 log_debug("ffA: %s %s %s\n", log_id(st
.ffA
, "--"), log_id(st
.ffAholdmux
, "--"), log_id(st
.ffArstmux
, "--"));
35 log_debug("ffB: %s %s %s\n", log_id(st
.ffB
, "--"), log_id(st
.ffBholdmux
, "--"), log_id(st
.ffBrstmux
, "--"));
36 log_debug("ffCD: %s %s\n", log_id(st
.ffCD
, "--"), log_id(st
.ffCDholdmux
, "--"));
37 log_debug("mul: %s\n", log_id(st
.mul
, "--"));
38 log_debug("ffFJKG: %s\n", log_id(st
.ffFJKG
, "--"));
39 log_debug("ffH: %s\n", log_id(st
.ffH
, "--"));
40 log_debug("add: %s\n", log_id(st
.add
, "--"));
41 log_debug("mux: %s\n", log_id(st
.mux
, "--"));
42 log_debug("ffO: %s %s %s\n", log_id(st
.ffO
, "--"), log_id(st
.ffOholdmux
, "--"), log_id(st
.ffOrstmux
, "--"));
45 if (GetSize(st
.sigA
) > 16) {
46 log(" input A (%s) is too large (%d > 16).\n", log_signal(st
.sigA
), GetSize(st
.sigA
));
50 if (GetSize(st
.sigB
) > 16) {
51 log(" input B (%s) is too large (%d > 16).\n", log_signal(st
.sigB
), GetSize(st
.sigB
));
55 if (GetSize(st
.sigO
) > 33) {
56 log(" adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st
.sigO
), GetSize(st
.sigO
));
60 if (GetSize(st
.sigH
) > 32) {
61 log(" output (%s) is too large (%d > 32).\n", log_signal(st
.sigH
), GetSize(st
.sigH
));
66 if (cell
->type
== ID($mul
)) {
67 log(" replacing %s with SB_MAC16 cell.\n", log_id(st
.mul
->type
));
69 cell
= pm
.module
->addCell(NEW_ID
, ID(SB_MAC16
));
70 pm
.module
->swap_names(cell
, st
.mul
);
72 else log_assert(cell
->type
== ID(SB_MAC16
));
74 // SB_MAC16 Input Interface
76 A
.extend_u0(16, st
.mul
->getParam(ID::A_SIGNED
).as_bool());
77 log_assert(GetSize(A
) == 16);
80 B
.extend_u0(16, st
.mul
->getParam(ID::B_SIGNED
).as_bool());
81 log_assert(GetSize(B
) == 16);
83 SigSpec CD
= st
.sigCD
;
85 CD
= RTLIL::Const(0, 32);
87 log_assert(GetSize(CD
) == 32);
89 cell
->setPort(ID::A
, A
);
90 cell
->setPort(ID::B
, B
);
91 cell
->setPort(ID::C
, CD
.extract(16, 16));
92 cell
->setPort(ID::D
, CD
.extract(0, 16));
94 cell
->setParam(ID(A_REG
), st
.ffA
? State::S1
: State::S0
);
95 cell
->setParam(ID(B_REG
), st
.ffB
? State::S1
: State::S0
);
96 cell
->setParam(ID(C_REG
), st
.ffCD
? State::S1
: State::S0
);
97 cell
->setParam(ID(D_REG
), st
.ffCD
? State::S1
: State::S0
);
99 SigSpec AHOLD
, BHOLD
, CDHOLD
;
101 AHOLD
= st
.ffAholdpol
? st
.ffAholdmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffAholdmux
->getPort(ID::S
));
105 BHOLD
= st
.ffBholdpol
? st
.ffBholdmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffBholdmux
->getPort(ID::S
));
109 CDHOLD
= st
.ffCDholdpol
? st
.ffCDholdmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffCDholdmux
->getPort(ID::S
));
112 cell
->setPort(ID(AHOLD
), AHOLD
);
113 cell
->setPort(ID(BHOLD
), BHOLD
);
114 cell
->setPort(ID(CHOLD
), CDHOLD
);
115 cell
->setPort(ID(DHOLD
), CDHOLD
);
117 SigSpec IRSTTOP
, IRSTBOT
;
119 IRSTTOP
= st
.ffArstpol
? st
.ffArstmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffArstmux
->getPort(ID::S
));
123 IRSTBOT
= st
.ffBrstpol
? st
.ffBrstmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffBrstmux
->getPort(ID::S
));
126 cell
->setPort(ID(IRSTTOP
), IRSTTOP
);
127 cell
->setPort(ID(IRSTBOT
), IRSTBOT
);
129 if (st
.clock
!= SigBit())
131 cell
->setPort(ID::CLK
, st
.clock
);
132 cell
->setPort(ID(CE
), State::S1
);
133 cell
->setParam(ID(NEG_TRIGGER
), st
.clock_pol
? State::S0
: State::S1
);
135 log(" clock: %s (%s)", log_signal(st
.clock
), st
.clock_pol
? "posedge" : "negedge");
138 log(" ffA:%s", log_id(st
.ffA
));
141 log(" ffB:%s", log_id(st
.ffB
));
144 log(" ffCD:%s", log_id(st
.ffCD
));
147 log(" ffFJKG:%s", log_id(st
.ffFJKG
));
150 log(" ffH:%s", log_id(st
.ffH
));
153 log(" ffO:%s", log_id(st
.ffO
));
159 cell
->setPort(ID::CLK
, State::S0
);
160 cell
->setPort(ID(CE
), State::S0
);
161 cell
->setParam(ID(NEG_TRIGGER
), State::S0
);
164 // SB_MAC16 Cascade Interface
166 cell
->setPort(ID(SIGNEXTIN
), State::Sx
);
167 cell
->setPort(ID(SIGNEXTOUT
), pm
.module
->addWire(NEW_ID
));
169 cell
->setPort(ID::CI
, State::Sx
);
171 cell
->setPort(ID(ACCUMCI
), State::Sx
);
172 cell
->setPort(ID(ACCUMCO
), pm
.module
->addWire(NEW_ID
));
174 // SB_MAC16 Output Interface
177 int O_width
= GetSize(O
);
180 // If we have a signed multiply-add, then perform sign extension
181 if (st
.add
->getParam(ID::A_SIGNED
).as_bool() && st
.add
->getParam(ID::B_SIGNED
).as_bool())
182 pm
.module
->connect(O
[32], O
[31]);
184 cell
->setPort(ID::CO
, O
[32]);
188 cell
->setPort(ID::CO
, pm
.module
->addWire(NEW_ID
));
189 log_assert(GetSize(O
) <= 32);
191 O
.append(pm
.module
->addWire(NEW_ID
, 32-GetSize(O
)));
193 cell
->setPort(ID::O
, O
);
197 accum
= (st
.ffO
&& st
.add
->getPort(st
.addAB
== ID::A
? ID::B
: ID::A
) == st
.sigO
);
199 log(" accumulator %s (%s)\n", log_id(st
.add
), log_id(st
.add
->type
));
201 log(" adder %s (%s)\n", log_id(st
.add
), log_id(st
.add
->type
));
202 cell
->setPort(ID(ADDSUBTOP
), st
.add
->type
== ID($add
) ? State::S0
: State::S1
);
203 cell
->setPort(ID(ADDSUBBOT
), st
.add
->type
== ID($add
) ? State::S0
: State::S1
);
205 cell
->setPort(ID(ADDSUBTOP
), State::S0
);
206 cell
->setPort(ID(ADDSUBBOT
), State::S0
);
211 OHOLD
= st
.ffOholdpol
? st
.ffOholdmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffOholdmux
->getPort(ID::S
));
214 cell
->setPort(ID(OHOLDTOP
), OHOLD
);
215 cell
->setPort(ID(OHOLDBOT
), OHOLD
);
219 ORST
= st
.ffOrstpol
? st
.ffOrstmux
->getPort(ID::S
) : pm
.module
->Not(NEW_ID
, st
.ffOrstmux
->getPort(ID::S
));
222 cell
->setPort(ID(ORSTTOP
), ORST
);
223 cell
->setPort(ID(ORSTBOT
), ORST
);
225 SigSpec acc_reset
= State::S0
;
227 if (st
.muxAB
== ID::A
)
228 acc_reset
= st
.mux
->getPort(ID::S
);
230 acc_reset
= pm
.module
->Not(NEW_ID
, st
.mux
->getPort(ID::S
));
232 cell
->setPort(ID(OLOADTOP
), acc_reset
);
233 cell
->setPort(ID(OLOADBOT
), acc_reset
);
235 // SB_MAC16 Remaining Parameters
237 cell
->setParam(ID(TOP_8x8_MULT_REG
), st
.ffFJKG
? State::S1
: State::S0
);
238 cell
->setParam(ID(BOT_8x8_MULT_REG
), st
.ffFJKG
? State::S1
: State::S0
);
239 cell
->setParam(ID(PIPELINE_16x16_MULT_REG1
), st
.ffFJKG
? State::S1
: State::S0
);
240 cell
->setParam(ID(PIPELINE_16x16_MULT_REG2
), st
.ffH
? State::S1
: State::S0
);
242 cell
->setParam(ID(TOPADDSUB_LOWERINPUT
), Const(2, 2));
243 cell
->setParam(ID(TOPADDSUB_UPPERINPUT
), accum
? State::S0
: State::S1
);
244 cell
->setParam(ID(TOPADDSUB_CARRYSELECT
), Const(3, 2));
246 cell
->setParam(ID(BOTADDSUB_LOWERINPUT
), Const(2, 2));
247 cell
->setParam(ID(BOTADDSUB_UPPERINPUT
), accum
? State::S0
: State::S1
);
248 cell
->setParam(ID(BOTADDSUB_CARRYSELECT
), Const(0, 2));
250 cell
->setParam(ID(MODE_8x8
), State::S0
);
251 cell
->setParam(ID::A_SIGNED
, st
.mul
->getParam(ID::A_SIGNED
).as_bool());
252 cell
->setParam(ID::B_SIGNED
, st
.mul
->getParam(ID::B_SIGNED
).as_bool());
256 cell
->setParam(ID(TOPOUTPUT_SELECT
), Const(st
.add
? 0 : 3, 2));
258 cell
->setParam(ID(TOPOUTPUT_SELECT
), Const(1, 2));
260 st
.ffO
->connections_
.at(ID::Q
).replace(O
, pm
.module
->addWire(NEW_ID
, GetSize(O
)));
261 cell
->setParam(ID(BOTOUTPUT_SELECT
), Const(1, 2));
264 cell
->setParam(ID(TOPOUTPUT_SELECT
), Const(st
.add
? 0 : 3, 2));
265 cell
->setParam(ID(BOTOUTPUT_SELECT
), Const(st
.add
? 0 : 3, 2));
269 pm
.autoremove(st
.mul
);
271 pm
.blacklist(st
.mul
);
272 pm
.autoremove(st
.ffFJKG
);
273 pm
.autoremove(st
.add
);
276 struct Ice40DspPass
: public Pass
{
277 Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
278 void help() YS_OVERRIDE
280 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
282 log(" ice40_dsp [options] [selection]\n");
284 log("Map multipliers ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add)\n");
285 log("cells into iCE40 DSP resources.\n");
286 log("Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode.\n");
288 log("Pack input registers (A, B, {C,D}; with optional hold), pipeline registers\n");
289 log("({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with\n");
290 log("optional hold), and post-adder into into the SB_MAC16 resource.\n");
292 log("Multiply-accumulate operations using the post-adder with feedback on the {C,D}\n");
293 log("input will be folded into the DSP. In this scenario only, resetting the\n");
294 log("the accumulator to an arbitrary value can be inferred to use the {C,D} input.\n");
297 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
299 log_header(design
, "Executing ICE40_DSP pass (map multipliers).\n");
302 for (argidx
= 1; argidx
< args
.size(); argidx
++)
304 // if (args[argidx] == "-singleton") {
305 // singleton_mode = true;
310 extra_args(args
, argidx
, design
);
312 for (auto module
: design
->selected_modules())
313 ice40_dsp_pm(module
, module
->selected_cells()).run_ice40_dsp(create_ice40_dsp
);
317 PRIVATE_NAMESPACE_END