/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * (C) 2019 Eddie Hung * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/rtlil.h" #include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SynthXilinxPass : public ScriptPass { SynthXilinxPass() : ScriptPass("synth_xilinx", "synthesis for Xilinx FPGAs") { } void on_register() override { RTLIL::constpad["synth_xilinx.abc9.xc7.W"] = "300"; // Number with which ABC will map a 6-input gate // to one LUT6 (instead of a LUT5 + LUT2) } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" synth_xilinx [options]\n"); log("\n"); log("This command runs synthesis for Xilinx FPGAs. This command does not operate on\n"); log("partly selected designs. At the moment this command creates netlists that are\n"); log("compatible with 7-Series Xilinx devices.\n"); log("\n"); log(" -top \n"); log(" use the specified module as top module\n"); log("\n"); log(" -family \n"); log(" run synthesis for the specified Xilinx architecture\n"); log(" generate the synthesis netlist for the specified family.\n"); log(" supported values:\n"); log(" - xcup: Ultrascale Plus\n"); log(" - xcu: Ultrascale\n"); log(" - xc7: Series 7 (default)\n"); log(" - xc6s: Spartan 6\n"); log(" - xc6v: Virtex 6\n"); log(" - xc5v: Virtex 5 (EXPERIMENTAL)\n"); log(" - xc4v: Virtex 4 (EXPERIMENTAL)\n"); log(" - xc3sda: Spartan 3A DSP (EXPERIMENTAL)\n"); log(" - xc3sa: Spartan 3A (EXPERIMENTAL)\n"); log(" - xc3se: Spartan 3E (EXPERIMENTAL)\n"); log(" - xc3s: Spartan 3 (EXPERIMENTAL)\n"); log(" - xc2vp: Virtex 2 Pro (EXPERIMENTAL)\n"); log(" - xc2v: Virtex 2 (EXPERIMENTAL)\n"); log(" - xcve: Virtex E, Spartan 2E (EXPERIMENTAL)\n"); log(" - xcv: Virtex, Spartan 2 (EXPERIMENTAL)\n"); log("\n"); log(" -edif \n"); log(" write the design to the specified edif file. writing of an output file\n"); log(" is omitted if this parameter is not specified.\n"); log("\n"); log(" -blif \n"); log(" write the design to the specified BLIF file. writing of an output file\n"); log(" is omitted if this parameter is not specified.\n"); log("\n"); log(" -ise\n"); log(" generate an output netlist suitable for
module test(input [31:0]  a, b,
            output [31:0] y);
assign y = a + b;
endmodule
ign->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); if (abc9 && retime) log_cmd_error("-retime option not currently compatible with -abc9!\n"); log_header(design, "Executing SYNTH_XILINX pass.\n"); log_push(); run_script(design, run_from, run_to); log_pop(); } void script() override { std::string lut_size_s = std::to_string(lut_size); if (help_mode) lut_size_s = "[46]"; if (check_label("begin")) { std::string read_args; read_args += " -lib -specify +/xilinx/cells_sim.v"; run("read_verilog" + read_args); run("read_verilog -lib +/xilinx/cells_xtra.v"); run(stringf("hierarchy -check %s", top_opt.c_str())); } if (check_label("prepare")) { run("proc"); if (flatten || help_mode) run("flatten", "(with '-flatten')"); if (active_design) active_design->scratchpad_unset("tribuf.added_something"); run("tribuf -logic"); if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something")) log_error("Tristate buffers are unsupported without the '-iopad' option.\n"); run("deminout"); run("opt_expr"); run("opt_clean"); run("check"); run("opt -nodffe -nosdff"); run("fsm"); run("opt"); if (help_mode) run("wreduce [-keepdc]", "(option for '-widemux')"); else run("wreduce" + std::string(widemux > 0 ? " -keepdc" : "")); run("peepopt"); run("opt_clean"); if (widemux > 0 || help_mode) run("muxpack", " ('-widemux' only)"); // xilinx_srl looks for $shiftx cells for identifying variable-length // shift registers, so attempt to convert $pmux-es to this // Also: wide multiplexer inference benefits from this too if (!(nosrl && widemux == 0) || help_mode) { run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')"); run("clean", " (skip if '-nosrl' and '-widemux=0')"); } } if (check_label("map_dsp", "(skip if '-nodsp')")) { if (!nodsp || help_mode) { run("memory_dff"); // xilinx_dsp will merge registers, reserve memory port registers first // NB: Xilinx multipliers are signed only if (help_mode) run("techmap -map +/mul2dsp.v -map +/xilinx/{family}_dsp_map.v {options}"); else if (family == "xc2v" || family == "xc2vp" || family == "xc3s" || family == "xc3se" || family == "xc3sa") run("techmap -map +/mul2dsp.v -map +/xilinx/xc3s_mult_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL18X18"); else if (family == "xc3sda") run("techmap -map +/mul2dsp.v -map +/xilinx/xc3sda_dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL18X18"); else if (family == "xc6s") run("techmap -map +/mul2dsp.v -map +/xilinx/xc6s_dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL18X18"); else if (family == "xc4v") run("techmap -map +/mul2dsp.v -map +/xilinx/xc4v_dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL18X18"); else if (family == "xc5v") run("techmap -map +/mul2dsp.v -map +/xilinx/xc5v_dsp_map.v -D DSP_A_MAXWIDTH=25 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18"); else if (family == "xc6v" || family == "xc7") run("techmap -map +/mul2dsp.v -map +/xilinx/xc7_dsp_map.v -D DSP_A_MAXWIDTH=25 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MAXWIDTH_PARTIAL=18 " // Partial multipliers are intentionally // limited to 18x18 in order to take // advantage of the (PCOUT << 17) -> PCIN // dedicated cascade chain capability "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18"); else if (family == "xcu" || family == "xcup") run("techmap -map +/mul2dsp.v -map +/xilinx/xcu_dsp_map.v -D DSP_A_MAXWIDTH=27 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MAXWIDTH_PARTIAL=18 " // Partial multipliers are intentionally // limited to 18x18 in order to take // advantage of the (PCOUT << 17) -> PCIN // dedicated cascade chain capability "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL27X18"); run("select a:mul2dsp"); run("setattr -unset mul2dsp"); run("opt_expr -fine"); run("wreduce"); run("select -clear"); if (help_mode) run("xilinx_dsp -family "); else run("xilinx_dsp -family " + family); run("chtype -set $mul t:$__soft_mul"); } } if (check_label("coarse")) { run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=" + lut_size_s); run("alumacc"); run("share"); run("opt"); run("memory -nomap"); run("opt_clean"); } if (check_label("map_uram", "(only if '-uram')")) { if (help_mode) { run("memory_bram -rules +/xilinx/{family}_urams.txt"); run("techmap -map +/xilinx/{family}_urams_map.v"); } else if (uram) { if (family == "xcup") { run("memory_bram -rules +/xilinx/xcup_urams.txt"); run("techmap -map +/xilinx/xcup_urams_map.v"); } else { log_warning("UltraRAM inference not supported for family %s.\n", family.c_str()); } } } if (check_label("map_bram", "(skip if '-nobram')")) { if (help_mode) { run("memory_bram -rules +/xilinx/{family}_brams.txt"); run("techmap -map +/xilinx/{family}_brams_map.v"); } else if (!nobram) { if (family == "xc2v" || family == "xc2vp" || family == "xc3s" || family == "xc3se") { run("memory_bram -rules +/xilinx/xc2v_brams.txt"); run("techmap -map +/xilinx/xc2v_brams_map.v"); } else if (family == "xc3sa") { // Superset of Virtex 2 primitives — uses common map file. run("memory_bram -rules +/xilinx/xc3sa_brams.txt"); run("techmap -map +/xilinx/xc2v_brams_map.v"); } else if (family == "xc3sda") { // Supported block RAMs for Spartan 3A DSP are // a subset of Spartan 6's ones. run("memory_bram -rules +/xilinx/xc3sda_brams.txt"); run("techmap -map +/xilinx/xc6s_brams_map.v"); } else if (family == "xc6s") { run("memory_bram -rules +/xilinx/xc6s_brams.txt"); run("techmap -map +/xilinx/xc6s_brams_map.v"); } else if (family == "xc6v" || family == "xc7") { run("memory_bram -rules +/xilinx/xc7_xcu_brams.txt"); run("techmap -map +/xilinx/xc7_brams_map.v"); } else if (family == "xcu" || family == "xcup") { run("memory_bram -rules +/xilinx/xc7_xcu_brams.txt"); run("techmap -map +/xilinx/xcu_brams_map.v"); } else { log_warning("Block RAM inference not yet supported for family %s.\n", family.c_str()); } } } if (check_label("map_lutram", "(skip if '-nolutram')")) { if (!nolutram || help_mode) { run("memory_bram -rules +/xilinx/lut" + lut_size_s + "_lutrams.txt"); run("techmap -map +/xilinx/lutrams_map.v"); } } if (check_label("map_ffram")) { if (widemux > 0) { run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover // performs less efficiently } else { run("opt -fast -full"); } run("memory_map"); } if (check_label("fine")) { if (help_mode) { run("simplemap t:$mux", "('-widemux' only)"); run("muxcover ", "('-widemux' only)"); } else if (widemux > 0) { run("simplemap t:$mux"); constexpr int cost_mux2 = 100; std::string muxcover_args = stringf(" -nodecode -mux2=%d", cost_mux2); switch (widemux) { case 2: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2+1, cost_mux2+2, cost_mux2+3); break; case 3: case 4: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-2, cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break; case 5: case 6: case 7: case 8: muxcover_args += stringf(" -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break; case 9: case 10: case 11: case 12: case 13: case 14: case 15: default: muxcover_args += stringf(" -mux16=%d", cost_mux2*(widemux-1)-1); break; } run("muxcover " + muxcover_args); } run("opt -full"); if (!nosrl || help_mode) run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')"); std::string techmap_args = " -map +/techmap.v -D LUT_SIZE=" + lut_size_s; if (help_mode) techmap_args += " [-map +/xilinx/mux_map.v]"; else if (widemux > 0) techmap_args += stringf(" -D MIN_MUX_INPUTS=%d -map +/xilinx/mux_map.v", widemux); if (!nocarry) { techmap_args += " -map +/xilinx/arith_map.v"; } run("techmap " + techmap_args); run("opt -fast"); } if (check_label("map_cells")) { // Needs to be done before logic optimization, so that inverters (inserted // here because of negative-polarity output enable) are handled. if (help_mode || !noiopad) run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad OBUFT ~T:I:O -tinoutpad IOBUF ~T:O:I:IO A:top", "(skip if '-noiopad')"); std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; if (widemux > 0) techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); run("techmap " + techmap_args); run("clean"); } if (check_label("map_ffs")) { if (family == "xc6s") run("dfflegalize -cell $_DFFE_?P?P_ r -cell $_SDFFE_?P?P_ r -cell $_DLATCH_?P?_ r", "(for xc6s)"); else if (family == "xc6v" || family == "xc7" || family == "xcu" || family == "xcup") run("dfflegalize -cell $_DFFE_?P?P_ 01 -cell $_SDFFE_?P?P_ 01 -cell $_DLATCH_?P?_ 01", "(for xc6v, xc7, xcu, xcup)"); else run("dfflegalize -cell $_DFFE_?P?P_ 01 -cell $_DFFSRE_?PPP_ 01 -cell $_SDFFE_?P?P_ 01 -cell $_DLATCH_?P?_ 01 -cell $_DLATCHSR_?PP_ 01", "(for xc5v and older)"); if (abc9 || help_mode) { if (dff || help_mode) run("zinit -all w:* t:$_SDFFE_*", "('-dff' only)"); run("techmap -map +/xilinx/ff_map.v", "('-abc9' only)"); } } if (check_label("map_luts")) { run("opt_expr -mux_undef -noclkinv"); if (flatten_before_abc) run("flatten"); if (help_mode) run("abc -luts 2:2,3,6:5[,10,20] [-dff] [-D 1]", "(option for '-nowidelut', '-dff', '-retime')"); else if (abc9) { if (lut_size != 6) log_error("'synth_xilinx -abc9' not currently supported for LUT4-based devices.\n"); if (family != "xc7") log_warning("'synth_xilinx -abc9' not currently supported for the '%s' family, " "will use timing for 'xc7' instead.\n", family.c_str()); run("read_verilog -icells -lib -specify +/xilinx/abc9_model.v"); std::string abc9_opts; std::string k = "synth_xilinx.abc9.W"; if (active_design && active_design->scratchpad.count(k)) abc9_opts += stringf(" -W %s", active_design->scratchpad_get_string(k).c_str()); else { k = stringf("synth_xilinx.abc9.%s.W", family.c_str()); abc9_opts += stringf(" -W %s", RTLIL::constpad.at(k, RTLIL::constpad.at("synth_xilinx.abc9.xc7.W")).c_str()); } if (nowidelut) abc9_opts += stringf(" -maxlut %d", lut_size); if (dff) abc9_opts += " -dff"; run("abc9" + abc9_opts); } else { std::string abc_opts; if (lut_size != 6) { if (nowidelut) abc_opts += " -lut " + lut_size_s; else abc_opts += " -lut " + lut_size_s + ":" + std::to_string(widelut_size); } else { if (nowidelut) abc_opts += " -luts 2:2,3,6:5"; else if (widelut_size == 8) abc_opts += " -luts 2:2,3,6:5,10,20"; else abc_opts += " -luts 2:2,3,6:5,10,20,40"; } if (dff) abc_opts += " -dff"; if (retime) abc_opts += " -D 1"; run("abc" + abc_opts); } run("clean"); if (help_mode || !abc9) run("techmap -map +/xilinx/ff_map.v", "(only if not '-abc9')"); // This shregmap call infers fixed length shift registers after abc // has performed any necessary retiming if (!nosrl || help_mode) run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')"); std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v"; techmap_args += " -D LUT_WIDTH=" + lut_size_s; run("techmap " + techmap_args); if (help_mode) run("xilinx_dffopt [-lut4]"); else if (lut_size == 4) run("xilinx_dffopt -lut4"); else run("xilinx_dffopt"); run("opt_lut_ins -tech xilinx"); } if (check_label("finalize")) { if (help_mode || !noclkbuf) run("clkbufmap -buf BUFG O:I", "(skip if '-noclkbuf')"); if (help_mode || ise) run("extractinv -inv INV O:I", "(only if '-ise')"); run("clean"); } if (check_label("check")) { run("hierarchy -check"); run("stat -tech xilinx"); run("check -noinit"); run("blackbox =A:whitebox"); } if (check_label("edif")) { if (!edif_file.empty() || help_mode) run(stringf("write_edif -pvector bra %s", edif_file.c_str())); } if (check_label("blif")) { if (!blif_file.empty() || help_mode) run(stringf("write_blif %s", blif_file.c_str())); } } } SynthXilinxPass; PRIVATE_NAMESPACE_END