nexus: Add MULTADDSUB9X9WIDE sim model
[yosys.git] / techlibs / nexus / synth_nexus.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2020 David Shah <dave@ds0.me>
5 *
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.
9 *
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.
17 *
18 */
19
20 #include "kernel/register.h"
21 #include "kernel/celltypes.h"
22 #include "kernel/rtlil.h"
23 #include "kernel/log.h"
24
25 USING_YOSYS_NAMESPACE
26 PRIVATE_NAMESPACE_BEGIN
27
28 struct SynthNexusPass : public ScriptPass
29 {
30 SynthNexusPass() : ScriptPass("synth_nexus", "synthesis for Lattice Nexus FPGAs") { }
31
32 void on_register() override
33 {
34 RTLIL::constpad["synth_nexus.abc9.W"] = "300";
35 }
36
37 void help() override
38 {
39 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
40 log("\n");
41 log(" synth_nexus [options]\n");
42 log("\n");
43 log("This command runs synthesis for Lattice Nexus FPGAs.\n");
44 log("\n");
45 log(" -top <module>\n");
46 log(" use the specified module as top module\n");
47 log("\n");
48 log(" -family <device>\n");
49 log(" run synthesis for the specified Nexus device\n");
50 log(" supported values: lifcl, lfd2nx\n");
51 log("\n");
52 log(" -json <file>\n");
53 log(" write the design to the specified JSON file. writing of an output file\n");
54 log(" is omitted if this parameter is not specified.\n");
55 log("\n");
56 log(" -vm <file>\n");
57 log(" write the design to the specified structural Verilog file. writing of\n");
58 log(" an output file is omitted if this parameter is not specified.\n");
59 log("\n");
60 log(" -run <from_label>:<to_label>\n");
61 log(" only run the commands between the labels (see below). an empty\n");
62 log(" from label is synonymous to 'begin', and empty to label is\n");
63 log(" synonymous to the end of the command list.\n");
64 log("\n");
65 log(" -noflatten\n");
66 log(" do not flatten design before synthesis\n");
67 log("\n");
68 log(" -dff\n");
69 log(" run 'abc'/'abc9' with -dff option\n");
70 log("\n");
71 log(" -retime\n");
72 log(" run 'abc' with '-dff -D 1' options\n");
73 log("\n");
74 log(" -noccu2\n");
75 log(" do not use CCU2 cells in output netlist\n");
76 log("\n");
77 log(" -nodffe\n");
78 log(" do not use flipflops with CE in output netlist\n");
79 log("\n");
80 log(" -nolram\n");
81 log(" do not use large RAM cells in output netlist\n");
82 log(" note that large RAM must be explicitly requested with a (* lram *)\n");
83 log(" attribute on the memory.\n");
84 log("\n");
85 log(" -nobram\n");
86 log(" do not use block RAM cells in output netlist\n");
87 log("\n");
88 log(" -nolutram\n");
89 log(" do not use LUT RAM cells in output netlist\n");
90 log("\n");
91 log(" -nowidelut\n");
92 log(" do not use PFU muxes to implement LUTs larger than LUT4s\n");
93 log("\n");
94 log(" -noiopad\n");
95 log(" do not insert IO buffers\n");
96 log("\n");
97 log(" -nodsp\n");
98 log(" do not infer DSP multipliers\n");
99 log("\n");
100 log(" -abc9\n");
101 log(" use new ABC9 flow (EXPERIMENTAL)\n");
102 log("\n");
103 log("The following commands are executed by this synthesis command:\n");
104 help_script();
105 log("\n");
106 }
107
108 string top_opt, json_file, vm_file, family;
109 bool noccu2, nodffe, nolram, nobram, nolutram, nowidelut, noiopad, nodsp, flatten, dff, retime, abc9;
110
111 void clear_flags() override
112 {
113 top_opt = "-auto-top";
114 family = "lifcl";
115 json_file = "";
116 vm_file = "";
117 noccu2 = false;
118 nodffe = false;
119 nolram = false;
120 nobram = false;
121 nolutram = false;
122 nowidelut = false;
123 noiopad = false;
124 nodsp = false;
125 flatten = true;
126 dff = false;
127 retime = false;
128 }
129
130 void execute(std::vector<std::string> args, RTLIL::Design *design) override
131 {
132 string run_from, run_to;
133 clear_flags();
134
135 size_t argidx;
136 for (argidx = 1; argidx < args.size(); argidx++)
137 {
138 if (args[argidx] == "-top" && argidx+1 < args.size()) {
139 top_opt = "-top " + args[++argidx];
140 continue;
141 }
142 if (args[argidx] == "-json" && argidx+1 < args.size()) {
143 json_file = args[++argidx];
144 continue;
145 }
146 if (args[argidx] == "-vm" && argidx+1 < args.size()) {
147 vm_file = args[++argidx];
148 continue;
149 }
150 if (args[argidx] == "-run" && argidx+1 < args.size()) {
151 size_t pos = args[argidx+1].find(':');
152 if (pos == std::string::npos)
153 break;
154 run_from = args[++argidx].substr(0, pos);
155 run_to = args[argidx].substr(pos+1);
156 continue;
157 }
158 if ((args[argidx] == "-family") && argidx+1 < args.size()) {
159 family = args[++argidx];
160 continue;
161 }
162 if (args[argidx] == "-flatten") {
163 flatten = true;
164 continue;
165 }
166 if (args[argidx] == "-noflatten") {
167 flatten = false;
168 continue;
169 }
170 if (args[argidx] == "-dff") {
171 dff = true;
172 continue;
173 }
174 if (args[argidx] == "-nodsp") {
175 nodsp = true;
176 continue;
177 }
178 if (args[argidx] == "-retime") {
179 retime = true;
180 continue;
181 }
182 if (args[argidx] == "-noccu2") {
183 noccu2 = true;
184 continue;
185 }
186 if (args[argidx] == "-nodffe") {
187 nodffe = true;
188 continue;
189 }
190 if (args[argidx] == "-nolram") {
191 nolram = true;
192 continue;
193 }
194 if (args[argidx] == "-nobram") {
195 nobram = true;
196 continue;
197 }
198 if (args[argidx] == "-nolutram") {
199 nolutram = true;
200 continue;
201 }
202 if (args[argidx] == "-nowidelut") {
203 nowidelut = true;
204 continue;
205 }
206 if (args[argidx] == "-noiopad") {
207 noiopad = true;
208 continue;
209 }
210 if (args[argidx] == "-abc9") {
211 abc9 = true;
212 continue;
213 }
214 break;
215 }
216 extra_args(args, argidx, design);
217
218 if (!design->full_selection())
219 log_cmd_error("This command only operates on fully selected designs!\n");
220
221 if (abc9 && retime)
222 log_cmd_error("-retime option not currently compatible with -abc9!\n");
223
224 log_header(design, "Executing SYNTH_NEXUS pass.\n");
225 log_push();
226
227 run_script(design, run_from, run_to);
228
229 log_pop();
230 }
231
232 struct DSPRule {
233 int a_maxwidth;
234 int b_maxwidth;
235 int a_minwidth;
236 int b_minwidth;
237 std::string prim;
238 };
239
240 const std::vector<DSPRule> dsp_rules = {
241 {36, 36, 22, 22, "$__NX_MUL36X36"},
242 {36, 18, 22, 10, "$__NX_MUL36X18"},
243 {18, 18, 10, 4, "$__NX_MUL18X18"},
244 {18, 18, 4, 10, "$__NX_MUL18X18"},
245 { 9, 9, 4, 4, "$__NX_MUL9X9"},
246 };
247
248 void script() override
249 {
250
251 if (family != "lifcl" && family != "lfd2nx")
252 log_cmd_error("Invalid Nexus -family setting: '%s'.\n", family.c_str());
253
254 if (check_label("begin"))
255 {
256 run("read_verilog -lib -specify +/nexus/cells_sim.v +/nexus/cells_xtra.v");
257 run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
258 }
259
260 if (check_label("coarse"))
261 {
262 run("proc");
263 if (flatten || help_mode)
264 run("flatten");
265 run("tribuf -logic");
266 run("deminout");
267 run("opt_expr");
268 run("opt_clean");
269 run("check");
270 run("opt -nodffe -nosdff");
271 run("fsm");
272 run("opt");
273 run("wreduce");
274 run("peepopt");
275 run("opt_clean");
276 run("share");
277 run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
278 run("opt_expr");
279 run("opt_clean");
280
281 if (help_mode) {
282 run("techmap -map +/mul2dsp.v [...]", "(unless -nodsp)");
283 run("techmap -map +/nexus/dsp_map.v", "(unless -nodsp)");
284 } else if (!nodsp) {
285 for (const auto &rule : dsp_rules) {
286 run(stringf("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=%d -D DSP_B_MAXWIDTH=%d -D DSP_A_MINWIDTH=%d -D DSP_B_MINWIDTH=%d -D DSP_NAME=%s",
287 rule.a_maxwidth, rule.b_maxwidth, rule.a_minwidth, rule.b_minwidth, rule.prim.c_str()));
288 run("chtype -set $mul t:$__soft_mul");
289 }
290 run("techmap -map +/nexus/dsp_map.v");
291 }
292
293 run("alumacc");
294 run("opt");
295 run("memory -nomap");
296 run("opt_clean");
297 }
298
299 if (!nolram && check_label("map_lram", "(skip if -nolram)"))
300 {
301 run("memory_bram -rules +/nexus/lrams.txt");
302 run("setundef -zero -params t:$__NX_PDPSC512K");
303 run("techmap -map +/nexus/lrams_map.v");
304 }
305
306 if (!nobram && check_label("map_bram", "(skip if -nobram)"))
307 {
308 run("memory_bram -rules +/nexus/brams.txt");
309 run("setundef -zero -params t:$__NX_PDP16K");
310 run("techmap -map +/nexus/brams_map.v");
311 }
312
313 if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
314 {
315 run("memory_bram -rules +/nexus/lutrams.txt");
316 run("setundef -zero -params t:$__NEXUS_DPR16X4");
317 run("techmap -map +/nexus/lutrams_map.v");
318 }
319
320 if (check_label("map_ffram"))
321 {
322 run("opt -fast -mux_undef -undriven -fine");
323 run("memory_map -iattr -attr !ram_block -attr !rom_block -attr logic_block "
324 "-attr syn_ramstyle=auto -attr syn_ramstyle=registers "
325 "-attr syn_romstyle=auto -attr syn_romstyle=logic");
326 run("opt -undriven -fine");
327 }
328
329 if (check_label("map_gates"))
330 {
331 if (noccu2)
332 run("techmap");
333 else
334 run("techmap -map +/techmap.v -map +/nexus/arith_map.v");
335 if (help_mode || !noiopad)
336 run("iopadmap -bits -outpad OB I:O -inpad IB O:I -toutpad $__NX_TOUTPAD OE:I:O -tinoutpad $__NX_TINOUTPAD OE:O:I:B A:top", "(skip if '-noiopad')");
337 run("opt -fast");
338 if (retime || help_mode)
339 run("abc -dff -D 1", "(only if -retime)");
340 }
341
342 if (check_label("map_ffs"))
343 {
344 run("opt_clean");
345 std::string dfflegalize_args = " -cell $_DFF_P_ 01 -cell $_DFF_PP?_ r -cell $_SDFF_PP?_ r -cell $_DLATCH_?_ x";
346 if (help_mode) {
347 dfflegalize_args += " [-cell $_DFFE_PP_ 01 -cell $_DFFE_PP?P_ r -cell $_SDFFE_PP?P_ r]";
348 } else if (!nodffe) {
349 dfflegalize_args += " -cell $_DFFE_PP_ 01 -cell $_DFFE_PP?P_ r -cell $_SDFFE_PP?P_ r";
350 }
351 run("dfflegalize" + dfflegalize_args, "($_*DFFE_* only if not -nodffe)");
352 if ((abc9 && dff) || help_mode)
353 run("zinit -all w:* t:$_DFF_?_ t:$_DFFE_??_ t:$_SDFF*", "(only if -abc9 and -dff");
354 run("techmap -D NO_LUT -map +/nexus/cells_map.v");
355 run("opt_expr -undriven -mux_undef");
356 run("simplemap");
357 run("attrmvcp -copy -attr syn_useioff");
358 run("opt_clean");
359 }
360
361 if (check_label("map_luts"))
362 {
363 run("techmap -map +/nexus/latches_map.v");
364
365 if (abc9) {
366 std::string abc9_opts;
367 if (nowidelut)
368 abc9_opts += " -maxlut 4";
369 std::string k = "synth_nexus.abc9.W";
370 if (active_design && active_design->scratchpad.count(k))
371 abc9_opts += stringf(" -W %s", active_design->scratchpad_get_string(k).c_str());
372 else
373 abc9_opts += stringf(" -W %s", RTLIL::constpad.at(k).c_str());
374 if (nowidelut)
375 abc9_opts += " -maxlut 4";
376 if (dff)
377 abc9_opts += " -dff";
378 run("abc9" + abc9_opts);
379 } else {
380 std::string abc_args = " -dress";
381 if (nowidelut)
382 abc_args += " -lut 4";
383 else
384 abc_args += " -lut 4:5";
385 if (dff)
386 abc_args += " -dff";
387 run("abc" + abc_args);
388 }
389 run("clean");
390 }
391
392 if (check_label("map_cells"))
393 {
394 run("techmap -map +/nexus/cells_map.v");
395
396 // This is needed for Radiant, but perhaps not optimal for nextpnr...
397 run("setundef -zero");
398
399 run("hilomap -singleton -hicell VHI Z -locell VLO Z");
400 run("clean");
401 }
402
403 if (check_label("check"))
404 {
405 run("autoname");
406 run("hierarchy -check");
407 run("stat");
408 run("check -noinit");
409 }
410
411 if (check_label("json"))
412 {
413 if (!json_file.empty() || help_mode)
414 run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
415 }
416
417 if (check_label("vm"))
418 {
419 if (!vm_file.empty() || help_mode)
420 run(stringf("write_verilog %s", help_mode ? "<file-name>" : vm_file.c_str()));
421 }
422 }
423 } SynthNexusPass;
424
425 PRIVATE_NAMESPACE_END