diff options
-rw-r--r-- | kernel/rtlil.cc | 6 | ||||
-rw-r--r-- | kernel/rtlil.h | 4 | ||||
-rw-r--r-- | passes/pmgen/.gitignore | 3 | ||||
-rw-r--r-- | passes/pmgen/Makefile.inc | 9 | ||||
-rw-r--r-- | passes/pmgen/ice40_dsp.cc | 28 | ||||
-rw-r--r-- | passes/pmgen/ice40_dsp.pmg | 55 | ||||
-rw-r--r-- | passes/pmgen/xilinx_dsp.cc | 147 | ||||
-rw-r--r-- | passes/pmgen/xilinx_dsp.pmg | 102 | ||||
-rw-r--r-- | techlibs/common/Makefile.inc | 1 | ||||
-rw-r--r-- | techlibs/common/mul2dsp.v | 273 | ||||
-rw-r--r-- | techlibs/ecp5/Makefile.inc | 1 | ||||
-rw-r--r-- | techlibs/ecp5/dsp_map.v | 10 | ||||
-rw-r--r-- | techlibs/ecp5/synth_ecp5.cc | 33 | ||||
-rw-r--r-- | techlibs/ice40/cells_sim.v | 16 | ||||
-rw-r--r-- | techlibs/ice40/synth_ice40.cc | 4 | ||||
-rw-r--r-- | techlibs/ice40/tests/test_dsp_model.sh | 9 | ||||
-rw-r--r-- | techlibs/ice40/tests/test_dsp_model.v | 225 | ||||
-rw-r--r-- | techlibs/xilinx/Makefile.inc | 1 | ||||
-rw-r--r-- | techlibs/xilinx/cells_sim.v | 147 | ||||
-rw-r--r-- | techlibs/xilinx/cells_xtra.v | 82 | ||||
-rw-r--r-- | techlibs/xilinx/dsp_map.v | 46 | ||||
-rw-r--r-- | techlibs/xilinx/synth_xilinx.cc | 24 |
22 files changed, 1083 insertions, 143 deletions
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 85b013bdc..5d992ef2d 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -3297,7 +3297,7 @@ void RTLIL::SigSpec::replace(int offset, const RTLIL::SigSpec &with) check(); } -void RTLIL::SigSpec::remove_const() +RTLIL::SigSpec& RTLIL::SigSpec::remove_const() { if (packed()) { @@ -3331,6 +3331,7 @@ void RTLIL::SigSpec::remove_const() } check(); + return *this; } void RTLIL::SigSpec::remove(int offset, int length) @@ -3426,7 +3427,7 @@ void RTLIL::SigSpec::append_bit(const RTLIL::SigBit &bit) check(); } -void RTLIL::SigSpec::extend_u0(int width, bool is_signed) +RTLIL::SigSpec& RTLIL::SigSpec::extend_u0(int width, bool is_signed) { cover("kernel.rtlil.sigspec.extend_u0"); @@ -3443,6 +3444,7 @@ void RTLIL::SigSpec::extend_u0(int width, bool is_signed) append(padding); } + return *this; } RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f9412e776..868aaaa14 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -767,7 +767,7 @@ public: void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other); void remove(int offset, int length = 1); - void remove_const(); + RTLIL::SigSpec& remove_const(); RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other = NULL) const; RTLIL::SigSpec extract(const pool<RTLIL::SigBit> &pattern, const RTLIL::SigSpec *other = NULL) const; @@ -776,7 +776,7 @@ public: void append(const RTLIL::SigSpec &signal); void append_bit(const RTLIL::SigBit &bit); - void extend_u0(int width, bool is_signed = false); + RTLIL::SigSpec& extend_u0(int width, bool is_signed = false); RTLIL::SigSpec repeat(int num) const; diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index 0ad36ea2c..e52f3282f 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1,2 +1 @@ -/ice40_dsp_pm.h -/peepopt_pm.h +/*_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 7911132db..e33866670 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -1,14 +1,19 @@ OBJS += passes/pmgen/ice40_dsp.o +OBJS += passes/pmgen/xilinx_dsp.o OBJS += passes/pmgen/peepopt.o # -------------------------------------- +passes/pmgen/%.o: passes/pmgen/%_pm.h passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h +passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h +EXTRA_OBJS += passes/pmgen/xilinx_dsp_pm.h .SECONDARY: passes/pmgen/ice40_dsp_pm.h +.SECONDARY: passes/pmgen/xilinx_dsp_pm.h -passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg - $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^) +passes/pmgen/%_pm.h: passes/pmgen/pmgen.py passes/pmgen/%.pmg + $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p $* $(filter-out $<,$^) # -------------------------------------- diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index 39d033a04..f6a701540 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -23,13 +23,16 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +template<class T> bool includes(const T &lhs, const T &rhs) { + return std::includes(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} #include "passes/pmgen/ice40_dsp_pm.h" void create_ice40_dsp(ice40_dsp_pm &pm) { auto &st = pm.st_ice40_dsp; -#if 0 +#if 1 log("\n"); log("ffA: %s\n", log_id(st.ffA, "--")); log("ffB: %s\n", log_id(st.ffB, "--")); @@ -62,32 +65,27 @@ void create_ice40_dsp(ice40_dsp_pm &pm) return; } - bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool(); - - if (mul_signed) { - log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n"); - return; - } - - log(" replacing $mul with SB_MAC16 cell.\n"); + log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type)); Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); pm.module->swap_names(cell, st.mul); // SB_MAC16 Input Interface + bool a_signed = st.mul->getParam("\\A_SIGNED").as_bool(); + bool b_signed = st.mul->getParam("\\B_SIGNED").as_bool(); SigSpec A = st.sigA; - A.extend_u0(16, mul_signed); + A.extend_u0(16, a_signed); SigSpec B = st.sigB; - B.extend_u0(16, mul_signed); + B.extend_u0(16, b_signed); SigSpec CD; if (st.muxA) CD = st.muxA->getPort("\\B"); if (st.muxB) CD = st.muxB->getPort("\\A"); - CD.extend_u0(32, mul_signed); + CD.extend_u0(32, a_signed && b_signed); cell->setPort("\\A", A); cell->setPort("\\B", B); @@ -105,7 +103,7 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\IRSTTOP", State::S0); cell->setPort("\\IRSTBOT", State::S0); - if (st.clock_vld) + if (st.clock != SigBit()) { cell->setPort("\\CLK", st.clock); cell->setPort("\\CE", State::S1); @@ -198,8 +196,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); cell->setParam("\\MODE_8x8", State::S0); - cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); - cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); + cell->setParam("\\A_SIGNED", a_signed); + cell->setParam("\\B_SIGNED", b_signed); pm.autoremove(st.mul); pm.autoremove(st.ffY); diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 1f3590d4e..471b8b519 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -1,80 +1,90 @@ pattern ice40_dsp state <SigBit> clock -state <bool> clock_pol clock_vld +state <bool> clock_pol state <SigSpec> sigA sigB sigY sigS +state <SigSpec> sigYused state <Cell*> addAB muxAB match mul - select mul->type.in($mul) + select mul->type.in($mul, $__MUL16X16) select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 select GetSize(mul->getPort(\Y)) > 10 endmatch match ffA select ffA->type.in($dff) - // select nusers(port(ffA, \Q)) == 2 - index <SigSpec> port(ffA, \Q) === port(mul, \A) + filter !port(mul, \A).remove_const().empty() + filter includes(port(ffA, \Q).to_sigbit_set(), port(mul, \A).remove_const().to_sigbit_set()) optional endmatch -code sigA clock clock_pol clock_vld +code sigA clock clock_pol sigA = port(mul, \A); if (ffA) { - sigA = port(ffA, \D); + sigA.replace(port(ffA, \Q), port(ffA, \D)); clock = port(ffA, \CLK).as_bit(); clock_pol = param(ffA, \CLK_POLARITY).as_bool(); - clock_vld = true; } endcode match ffB select ffB->type.in($dff) - // select nusers(port(ffB, \Q)) == 2 - index <SigSpec> port(ffB, \Q) === port(mul, \B) + filter !port(mul, \B).remove_const().empty() + filter includes(port(ffB, \Q).to_sigbit_set(), port(mul, \B).remove_const().to_sigbit_set()) optional endmatch -code sigB clock clock_pol clock_vld +code sigB clock clock_pol sigB = port(mul, \B); if (ffB) { - sigB = port(ffB, \D); + sigB.replace(port(ffB, \Q), port(ffB, \D)); + SigBit c = port(ffB, \CLK).as_bit(); bool cp = param(ffB, \CLK_POLARITY).as_bool(); - if (clock_vld && (c != clock || cp != clock_pol)) + if (clock != SigBit() && (c != clock || cp != clock_pol)) reject; clock = c; clock_pol = cp; - clock_vld = true; } endcode +// Extract the bits of Y that actually have a consumer +// (as opposed to being a sign extension) +code sigY sigYused + sigY = port(mul, \Y); + int i; + for (i = GetSize(sigY); i > 0; i--) + if (nusers(sigY[i-1]) > 1) + break; + sigYused = sigY.extract(0, i).remove_const(); +endcode + match ffY select ffY->type.in($dff) select nusers(port(ffY, \D)) == 2 - index <SigSpec> port(ffY, \D) === port(mul, \Y) + filter param(ffY, \WIDTH).as_int() >= GetSize(sigYused) + filter includes(port(ffY, \D).to_sigbit_set(), sigYused.to_sigbit_set()) optional endmatch -code sigY clock clock_pol clock_vld - sigY = port(mul, \Y); - +code clock clock_pol sigY if (ffY) { - sigY = port(ffY, \Q); + sigY.replace(port(ffY, \D), port(ffY, \Q)); + SigBit c = port(ffY, \CLK).as_bit(); bool cp = param(ffY, \CLK_POLARITY).as_bool(); - if (clock_vld && (c != clock || cp != clock_pol)) + if (clock != SigBit() && (c != clock || cp != clock_pol)) reject; clock = c; clock_pol = cp; - clock_vld = true; } endcode @@ -147,16 +157,15 @@ match ffS index <SigSpec> port(ffS, \Q) === sigS endmatch -code clock clock_pol clock_vld +code clock clock_pol if (ffS) { SigBit c = port(ffS, \CLK).as_bit(); bool cp = param(ffS, \CLK_POLARITY).as_bool(); - if (clock_vld && (c != clock || cp != clock_pol)) + if (clock != SigBit() && (c != clock || cp != clock_pol)) reject; clock = c; clock_pol = cp; - clock_vld = true; } endcode diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc new file mode 100644 index 000000000..d87d63670 --- /dev/null +++ b/passes/pmgen/xilinx_dsp.cc @@ -0,0 +1,147 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +template<class T> bool includes(const T &lhs, const T &rhs) { + return std::includes(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} +#include "passes/pmgen/xilinx_dsp_pm.h" + +void pack_xilinx_dsp(xilinx_dsp_pm &pm) +{ + auto &st = pm.st_xilinx_dsp; + +#if 1 + log("\n"); + log("ffA: %s\n", log_id(st.ffA, "--")); + log("ffB: %s\n", log_id(st.ffB, "--")); + log("dsp: %s\n", log_id(st.dsp, "--")); + log("ffP: %s\n", log_id(st.ffP, "--")); + log("muxP: %s\n", log_id(st.muxP, "--")); + log("sigPused: %s\n", log_signal(st.sigPused)); + log_module(pm.module); +#endif + + log("Analysing %s.%s for Xilinx DSP register packing.\n", log_id(pm.module), log_id(st.dsp)); + + Cell *cell = st.dsp; + log_assert(cell); + + if (st.clock != SigBit()) + { + cell->setPort("\\CLK", st.clock); + + if (st.ffA) { + SigSpec A = cell->getPort("\\A"); + SigSpec D = st.ffA->getPort("\\D"); + SigSpec Q = st.ffA->getPort("\\Q"); + A.replace(Q, D); + cell->setPort("\\A", A); + cell->setParam("\\AREG", State::S1); + if (st.ffA->type == "$dff") + cell->setPort("\\CEA2", State::S1); + else if (st.ffA->type == "$dffe") + cell->setPort("\\CEA2", st.ffA->getPort("\\EN")); + else log_abort(); + } + if (st.ffB) { + SigSpec B = cell->getPort("\\B"); + SigSpec D = st.ffB->getPort("\\D"); + SigSpec Q = st.ffB->getPort("\\Q"); + B.replace(Q, D); + cell->setPort("\\B", B); + cell->setParam("\\BREG", State::S1); + if (st.ffB->type == "$dff") + cell->setPort("\\CEB2", State::S1); + else if (st.ffB->type == "$dffe") + cell->setPort("\\CEB2", st.ffB->getPort("\\EN")); + else log_abort(); + } + if (st.ffP) { + SigSpec P = cell->getPort("\\P"); + SigSpec D; + if (st.muxP) + D = st.muxP->getPort("\\B"); + else + D = st.ffP->getPort("\\D"); + SigSpec Q = st.ffP->getPort("\\Q"); + P.replace(D, Q); + cell->setPort("\\P", Q); + cell->setParam("\\PREG", State::S1); + if (st.ffP->type == "$dff") + cell->setPort("\\CEP", State::S1); + else if (st.ffP->type == "$dffe") + cell->setPort("\\CEP", st.ffP->getPort("\\EN")); + else log_abort(); + } + + log(" clock: %s (%s)", log_signal(st.clock), "posedge"); + + if (st.ffA) + log(" ffA:%s", log_id(st.ffA)); + + if (st.ffB) + log(" ffB:%s", log_id(st.ffB)); + + if (st.ffP) + log(" ffY:%s", log_id(st.ffP)); + + log("\n"); + } + + pm.blacklist(cell); +} + +struct Ice40DspPass : public Pass { + Ice40DspPass() : Pass("xilinx_dsp", "Xilinx: pack DSP registers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" xilinx_dsp [options] [selection]\n"); + log("\n"); + log("Pack registers into Xilinx DSPs\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing XILINX_DSP pass (pack DSPs).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + xilinx_dsp_pm(module, module->selected_cells()).run_xilinx_dsp(pack_xilinx_dsp); + } +} Ice40DspPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg new file mode 100644 index 000000000..a97ab4dd5 --- /dev/null +++ b/passes/pmgen/xilinx_dsp.pmg @@ -0,0 +1,102 @@ +pattern xilinx_dsp + +state <SigBit> clock +state <SigSpec> sigPused + +match dsp + select dsp->type.in(\DSP48E1) +endmatch + +match ffA + select ffA->type.in($dff, $dffe) + // DSP48E1 does not support clock inversion + select param(ffA, \CLK_POLARITY).as_bool() + filter !port(dsp, \A).remove_const().empty() + filter includes(port(ffA, \Q).to_sigbit_set(), port(dsp, \A).remove_const().to_sigbit_set()) + optional +endmatch + +code clock + if (ffA) + clock = port(ffA, \CLK).as_bit(); +endcode + +match ffB + select ffB->type.in($dff, $dffe) + // DSP48E1 does not support clock inversion + select param(ffB, \CLK_POLARITY).as_bool() + filter !port(dsp, \B).remove_const().empty() + filter includes(port(ffB, \Q).to_sigbit_set(), port(dsp, \B).remove_const().to_sigbit_set()) + optional +endmatch + +code clock + if (ffB) { + SigBit c = port(ffB, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + } +endcode + +// Extract the bits of P that actually have a consumer +// (as opposed to being a sign extension) +code sigPused + SigSpec P = port(dsp, \P); + int i; + for (i = GetSize(P); i > 0; i--) + if (nusers(P[i-1]) > 1) + break; + sigPused = P.extract(0, i).remove_const(); +endcode + +match ffP + if !sigPused.empty() + select ffP->type.in($dff, $dffe) + select nusers(port(ffP, \D)) == 2 + // DSP48E1 does not support clock inversion + select param(ffP, \CLK_POLARITY).as_bool() + filter param(ffP, \WIDTH).as_int() >= GetSize(sigPused) + filter includes(port(ffP, \D).to_sigbit_set(), sigPused.to_sigbit_set()) + optional +endmatch + +// $mux cell left behind by dff2dffe +// would prefer not to run 'opt_expr -mux_undef' +// since that would lose information helpful for +// efficient wide-mux inference +match muxP + if !sigPused.empty() && !ffP + select muxP->type.in($mux) + select nusers(port(muxP, \B)) == 2 + select port(muxP, \A).is_fully_undef() + filter param(muxP, \WIDTH).as_int() >= GetSize(sigPused) + filter includes(port(muxP, \B).to_sigbit_set(), sigPused.to_sigbit_set()) + optional +endmatch + +match ffY + if muxP + select ffY->type.in($dff, $dffe) + select nusers(port(ffY, \D)) == 2 + // DSP48E1 does not support clock inversion + select param(ffY, \CLK_POLARITY).as_bool() + filter param(ffY, \WIDTH).as_int() >= GetSize(sigPused) + filter includes(port(ffY, \D).to_sigbit_set(), port(muxP, \Y).to_sigbit_set()) +endmatch + +code ffP clock + if (ffY) + ffP = ffY; + + if (ffP) { + SigBit c = port(ffP, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + } +endcode diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 0e05620bc..e6d1c2f29 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -28,3 +28,4 @@ $(eval $(call add_share_file,share,techlibs/common/dff2ff.v)) $(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cells.lib)) +$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v)) diff --git a/techlibs/common/mul2dsp.v b/techlibs/common/mul2dsp.v new file mode 100644 index 000000000..5444d842a --- /dev/null +++ b/techlibs/common/mul2dsp.v @@ -0,0 +1,273 @@ +// From Eddie Hung
+// extracted from: https://github.com/eddiehung/vtr-with-yosys/blob/vtr7-with-yosys/vtr_flow/misc/yosys_models.v#L220
+// revised by Andre DeHon
+// further revised by David Shah
+`ifndef DSP_A_MAXWIDTH
+$error("Macro DSP_A_MAXWIDTH must be defined");
+`endif
+`ifndef DSP_B_MAXWIDTH
+$error("Macro DSP_B_MAXWIDTH must be defined");
+`endif
+
+`ifndef DSP_NAME
+$error("Macro DSP_NAME must be defined");
+`endif
+
+`define MAX(a,b) (a > b ? a : b)
+`define MIN(a,b) (a < b ? a : b)
+
+module \$mul (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ generate
+ if (A_SIGNED != B_SIGNED)
+ wire _TECHMAP_FAIL_ = 1;
+ else if (A_WIDTH <= `DSP_A_MAXWIDTH && B_WIDTH <= `DSP_B_MAXWIDTH)
+ wire _TECHMAP_FAIL_ = 1;
+ // NB: A_SIGNED == B_SIGNED == 0 from here
+ else if (A_WIDTH >= B_WIDTH)
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A),
+ .B(B),
+ .Y(Y)
+ );
+ else
+ \$__mul #(
+ .A_SIGNED(B_SIGNED),
+ .B_SIGNED(A_SIGNED),
+ .A_WIDTH(B_WIDTH),
+ .B_WIDTH(A_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(B),
+ .B(A),
+ .Y(Y)
+ );
+ endgenerate
+endmodule
+
+module \$__mul (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+`ifdef DSP_SIGNEDONLY
+ localparam sign_headroom = 1;
+`else
+ localparam sign_headroom = 0;
+`endif
+
+ genvar i;
+ generate
+ if (A_WIDTH <= 1 || B_WIDTH <= 1)
+ wire _TECHMAP_FAIL_ = 1;
+`ifdef DSP_MINWIDTH
+ else if (A_WIDTH+B_WIDTH < `DSP_MINWIDTH || Y_WIDTH < `DSP_MINWIDTH)
+ wire _TECHMAP_FAIL_ = 1;
+`endif
+ else if (A_WIDTH > `DSP_A_MAXWIDTH) begin
+ localparam n = (A_WIDTH+`DSP_A_MAXWIDTH-sign_headroom-1) / (`DSP_A_MAXWIDTH-sign_headroom);
+ localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH);
+ if (A_SIGNED && B_SIGNED) begin
+ wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire signed [Y_WIDTH-1:0] partial_sum [n-1:0];
+ end
+ else begin
+ wire [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire [Y_WIDTH-1:0] partial_sum [n-1:0];
+ end
+
+ \$__mul #(
+ .A_SIGNED(sign_headroom),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(`DSP_A_MAXWIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul_slice_first (
+ .A({{sign_headroom{1'b0}}, A[`DSP_A_MAXWIDTH-sign_headroom-1 : 0]}),
+ .B(B),
+ .Y(partial[0])
+ );
+ assign partial_sum[0] = partial[0];
+
+ for (i = 1; i < n-1; i=i+1) begin:slice
+ \$__mul #(
+ .A_SIGNED(sign_headroom),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(`DSP_A_MAXWIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul_slice (
+ .A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH-sign_headroom) +: `DSP_A_MAXWIDTH-sign_headroom]}),
+ .B(B),
+ .Y(partial[i])
+ );
+ assign partial_sum[i] = (partial[i] << i*(`DSP_A_MAXWIDTH-sign_headroom)) + partial_sum[i-1];
+ end
+
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH-(n-1)*(`DSP_A_MAXWIDTH-sign_headroom)),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul_slice_last (
+ .A(A[A_WIDTH-1 : (n-1)*(`DSP_A_MAXWIDTH-sign_headroom)]),
+ .B(B),
+ .Y(partial[n-1])
+ );
+ assign partial_sum[n-1] = (partial[n-1] << (n-1)*(`DSP_A_MAXWIDTH-sign_headroom)) + partial_sum[n-2];
+ assign Y = partial_sum[n-1];
+ end
+ else if (B_WIDTH > `DSP_B_MAXWIDTH) begin
+ localparam n = (B_WIDTH+`DSP_B_MAXWIDTH-sign_headroom-1) / (`DSP_B_MAXWIDTH-sign_headroom);
+ localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH);
+ if (A_SIGNED && B_SIGNED) begin
+ wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire signed [Y_WIDTH-1:0] partial_sum [n-1:0];
+ end
+ else begin
+ wire [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire [Y_WIDTH-1:0] partial_sum [n-1:0];
+ end
+
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(sign_headroom),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(`DSP_B_MAXWIDTH),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul_first (
+ .A(A),
+ .B({{sign_headroom{1'b0}}, B[`DSP_B_MAXWIDTH-sign_headroom-1 : 0]}),
+ .Y(partial[0])
+ );
+ assign partial_sum[0] = partial[0];
+
+ for (i = 1; i < n-1; i=i+1) begin:slice
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(sign_headroom),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(`DSP_B_MAXWIDTH),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul (
+ .A(A),
+ .B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH-sign_headroom) +: `DSP_B_MAXWIDTH-sign_headroom]}),
+ .Y(partial[i])
+ );
+ assign partial_sum[i] = (partial[i] << i*(`DSP_B_MAXWIDTH-sign_headroom)) + partial_sum[i-1];
+ end
+
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH-(n-1)*(`DSP_B_MAXWIDTH-sign_headroom)),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul_last (
+ .A(A),
+ .B(B[B_WIDTH-1 : (n-1)*(`DSP_B_MAXWIDTH-sign_headroom)]),
+ .Y(partial[n-1])
+ );
+ assign partial_sum[n-1] = (partial[n-1] << (n-1)*(`DSP_B_MAXWIDTH-sign_headroom)) + partial_sum[n-2];
+ assign Y = partial_sum[n-1];
+ end
+ else begin
+ if (A_SIGNED)
+ wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
+ else
+ wire [`DSP_A_MAXWIDTH-1:0] Aext = A;
+ if (B_SIGNED)
+ wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
+ else
+ wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
+
+ `DSP_NAME #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(`DSP_A_MAXWIDTH),
+ .B_WIDTH(`DSP_B_MAXWIDTH),
+ .Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
+ ) _TECHMAP_REPLACE_ (
+ .A(Aext),
+ .B(Bext),
+ .Y(Y)
+ );
+ end
+ endgenerate
+endmodule
+
+(* techmap_celltype = "$__mul" *)
+module _90_internal_mul_to_external (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ generate
+ if (A_SIGNED && !B_SIGNED)
+ \$mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(1),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH+1),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A),
+ .B({1'b0, B}),
+ .Y(Y)
+ );
+ else if (!A_SIGNED && B_SIGNED)
+ \$mul #(
+ .A_SIGNED(1),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH+1),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A({1'b0, A}),
+ .B(B),
+ .Y(Y)
+ );
+ else
+ \$mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A),
+ .B(B),
+ .Y(Y)
+ );
+ endgenerate
+endmodule
diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc index 73e18112f..c7d6eee02 100644 --- a/techlibs/ecp5/Makefile.inc +++ b/techlibs/ecp5/Makefile.inc @@ -10,6 +10,7 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut)) diff --git a/techlibs/ecp5/dsp_map.v b/techlibs/ecp5/dsp_map.v new file mode 100644 index 000000000..24e28869e --- /dev/null +++ b/techlibs/ecp5/dsp_map.v @@ -0,0 +1,10 @@ +module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); + MULT18X18D _TECHMAP_REPLACE_ ( + .A0(A[0]), .A1(A[1]), .A2(A[2]), .A3(A[3]), .A4(A[4]), .A5(A[5]), .A6(A[6]), .A7(A[7]), .A8(A[8]), .A9(A[9]), .A10(A[10]), .A11(A[11]), .A12(A[12]), .A13(A[13]), .A14(A[14]), .A15(A[15]), .A16(A[16]), .A17(A[17]), + .B0(B[0]), .B1(B[1]), .B2(B[2]), .B3(B[3]), .B4(B[4]), .B5(B[5]), .B6(B[6]), .B7(B[7]), .B8(B[8]), .B9(B[9]), .B10(B[10]), .B11(B[11]), .B12(B[12]), .B13(B[13]), .B14(B[14]), .B15(B[15]), .B16(B[16]), .B17(B[17]), + .C17(1'b0), .C16(1'b0), .C15(1'b0), .C14(1'b0), .C13(1'b0), .C12(1'b0), .C11(1'b0), .C10(1'b0), .C9(1'b0), .C8(1'b0), .C7(1'b0), .C6(1'b0), .C5(1'b0), .C4(1'b0), .C3(1'b0), .C2(1'b0), .C1(1'b0), .C0(1'b0), + .SIGNEDA(1'b0), .SIGNEDB(1'b0), .SOURCEA(1'b0), .SOURCEB(1'b0), + + .P0(Y[0]), .P1(Y[1]), .P2(Y[2]), .P3(Y[3]), .P4(Y[4]), .P5(Y[5]), .P6(Y[6]), .P7(Y[7]), .P8(Y[8]), .P9(Y[9]), .P10(Y[10]), .P11(Y[11]), .P12(Y[12]), .P13(Y[13]), .P14(Y[14]), .P15(Y[15]), .P16(Y[16]), .P17(Y[17]), .P18(Y[18]), .P19(Y[19]), .P20(Y[20]), .P21(Y[21]), .P22(Y[22]), .P23(Y[23]), .P24(Y[24]), .P25(Y[25]), .P26(Y[26]), .P27(Y[27]), .P28(Y[28]), .P29(Y[29]), .P30(Y[30]), .P31(Y[31]), .P32(Y[32]), .P33(Y[33]), .P34(Y[34]), .P35(Y[35]) + ); +endmodule diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index 143d1f95c..3129ba929 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -89,6 +89,9 @@ struct SynthEcp5Pass : public ScriptPass log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); log("\n"); + log(" -dsp\n"); + log(" map multipliers to MULT18X18D (EXPERIMENTAL)\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); @@ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass } string top_opt, blif_file, edif_file, json_file; - bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr; + bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, dsp, vpr; void clear_flags() YS_OVERRIDE { @@ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass abc2 = false; vpr = false; abc9 = false; + dsp = false; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass abc9 = true; continue; } + if (args[argidx] == "-dsp") { + dsp = true; + continue; + } break; } extra_args(args, argidx, design); @@ -228,7 +236,28 @@ struct SynthEcp5Pass : public ScriptPass if (check_label("coarse")) { - run("synth -run coarse"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt"); + run("wreduce"); + run("peepopt"); + run("opt_clean"); + run("share"); + run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); + run("opt_expr"); + run("opt_clean"); + if (dsp) { + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_NAME=$__MUL18X18"); + run("clean"); + run("techmap -map +/ecp5/dsp_map.v"); + } + run("alumacc"); + run("opt"); + run("fsm"); + run("opt -fast"); + run("memory -nomap"); + run("opt_clean"); } if (!nobram && check_label("map_bram", "(skip if -nobram)")) diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 609facc93..2205be27d 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -1363,13 +1363,13 @@ module SB_MAC16 ( wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl; wire [15:0] Ah, Al, Bh, Bl; assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]}; - assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]}; + assign Al = {A_SIGNED && MODE_8x8 ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]}; assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]}; - assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; - assign p_Ah_Bh = Ah * Bh; - assign p_Al_Bh = Al * Bh; - assign p_Ah_Bl = Ah * Bl; - assign p_Al_Bl = Al * Bl; + assign Bl = {B_SIGNED && MODE_8x8 ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; + assign p_Ah_Bh = Ah * Bh; // F + assign p_Al_Bh = {8'b0, Al[7:0]} * Bh; // J + assign p_Ah_Bl = Ah * {8'b0, Bl[7:0]}; // K + assign p_Al_Bl = Al * Bl; // G // Regs F and J reg [15:0] rF, rJ; @@ -1400,7 +1400,9 @@ module SB_MAC16 ( assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl; // Adder Stage - assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16); + wire [23:0] iK_e = {A_SIGNED ? {8{iK[15]}} : 8'b0, iK}; + wire [23:0] iJ_e = {B_SIGNED ? {8{iJ[15]}} : 8'b0, iJ}; + assign iL = iG + (iK_e << 8) + (iJ_e << 8) + (iF << 16); // Reg H reg [31:0] rH; diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index be60a0071..77bd0ac81 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -265,8 +265,10 @@ struct SynthIce40Pass : public ScriptPass run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); run("opt_expr"); run("opt_clean"); - if (help_mode || dsp) + if (help_mode || dsp) { + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 -D DSP_MINWIDTH=11 -D DSP_NAME=$__MUL16X16", "(if -dsp)"); run("ice40_dsp", "(if -dsp)"); + } run("alumacc"); run("opt"); run("fsm"); diff --git a/techlibs/ice40/tests/test_dsp_model.sh b/techlibs/ice40/tests/test_dsp_model.sh index 1bc0cc688..1e564d1b2 100644 --- a/techlibs/ice40/tests/test_dsp_model.sh +++ b/techlibs/ice40/tests/test_dsp_model.sh @@ -1,10 +1,15 @@ #!/bin/bash set -ex sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v -cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v +if [ ! -f "test_dsp_model_ref.v" ]; then + cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v +fi for tb in testbench \ testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \ - testbench_seq_16x16_A testbench_seq_16x16_B + testbench_seq_16x16_A testbench_seq_16x16_B \ + testbench_comb_8x8_A_signedA testbench_comb_8x8_A_signedB testbench_comb_8x8_A_signedAB \ + testbench_comb_8x8_B_signedA testbench_comb_8x8_B_signedB testbench_comb_8x8_B_signedAB \ + testbench_comb_16x16_signedA testbench_comb_16x16_signedB testbench_comb_16x16_signedAB do iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v vvp -N ./test_dsp_model diff --git a/techlibs/ice40/tests/test_dsp_model.v b/techlibs/ice40/tests/test_dsp_model.v index 594bd4ad3..f4f6858f0 100644 --- a/techlibs/ice40/tests/test_dsp_model.v +++ b/techlibs/ice40/tests/test_dsp_model.v @@ -241,6 +241,81 @@ module testbench_comb_8x8_A; ) testbench (); endmodule +module testbench_comb_8x8_A_signedA; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_8x8_A_signedB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (1) + ) testbench (); +endmodule + +module testbench_comb_8x8_A_signedAB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (1) + ) testbench (); +endmodule + module testbench_comb_8x8_B; testbench #( .NEG_TRIGGER (0), @@ -266,6 +341,81 @@ module testbench_comb_8x8_B; ) testbench (); endmodule +module testbench_comb_8x8_B_signedA; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_8x8_B_signedB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (1) + ) testbench (); +endmodule + +module testbench_comb_8x8_B_signedAB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (1) + ) testbench (); +endmodule + module testbench_comb_16x16; testbench #( .NEG_TRIGGER (0), @@ -291,6 +441,81 @@ module testbench_comb_16x16; ) testbench (); endmodule +module testbench_comb_16x16_signedA; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_16x16_signedB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (1) + ) testbench (); +endmodule + +module testbench_comb_16x16_signedAB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (1) + ) testbench (); +endmodule + module testbench_seq_16x16_A; testbench #( .NEG_TRIGGER (0), diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index 2c6e7432e..b0251d621 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -38,6 +38,7 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/dsp_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut)) diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 05e46b4e7..33b2a8f62 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -378,3 +378,150 @@ module SRLC32E ( always @(posedge CLK) if (CE) r <= { r[30:0], D }; endgenerate endmodule + +module DSP48E1 ( + output [29:0] ACOUT, + output [17:0] BCOUT, + output CARRYCASCOUT, + output [3:0] CARRYOUT, + output MULTSIGNOUT, + output OVERFLOW, + output reg signed [47:0] P, + output PATTERNBDETECT, + output PATTERNDETECT, + output [47:0] PCOUT, + output UNDERFLOW, + input signed [29:0] A, + input [29:0] ACIN, + input [3:0] ALUMODE, + input signed [17:0] B, + input [17:0] BCIN, + input [47:0] C, + input CARRYCASCIN, + input CARRYIN, + input [2:0] CARRYINSEL, + input CEA1, + input CEA2, + input CEAD, + input CEALUMODE, + input CEB1, + input CEB2, + input CEC, + input CECARRYIN, + input CECTRL, + input CED, + input CEINMODE, + input CEM, + input CEP, + input CLK, + input [24:0] D, + input [4:0] INMODE, + input MULTSIGNIN, + input [6:0] OPMODE, + input [47:0] PCIN, + input RSTA, + input RSTALLCARRYIN, + input RSTALUMODE, + input RSTB, + input RSTC, + input RSTCTRL, + input RSTD, + input RSTINMODE, + input RSTM, + input RSTP +); + parameter integer ACASCREG = 1; + parameter integer ADREG = 1; + parameter integer ALUMODEREG = 1; + parameter integer AREG = 1; + parameter AUTORESET_PATDET = "NO_RESET"; + parameter A_INPUT = "DIRECT"; + parameter integer BCASCREG = 1; + parameter integer BREG = 1; + parameter B_INPUT = "DIRECT"; + parameter integer CARRYINREG = 1; + parameter integer CARRYINSELREG = 1; + parameter integer CREG = 1; + parameter integer DREG = 1; + parameter integer INMODEREG = 1; + parameter integer MREG = 1; + parameter integer OPMODEREG = 1; + parameter integer PREG = 1; + parameter SEL_MASK = "MASK"; + parameter SEL_PATTERN = "PATTERN"; + parameter USE_DPORT = "FALSE"; + parameter USE_MULT = "MULTIPLY"; + parameter USE_PATTERN_DETECT = "NO_PATDET"; + parameter USE_SIMD = "ONE48"; + parameter [47:0] MASK = 48'h3FFFFFFFFFFF; + parameter [47:0] PATTERN = 48'h000000000000; + parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; + parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + parameter [4:0] IS_INMODE_INVERTED = 5'b0; + parameter [6:0] IS_OPMODE_INVERTED = 7'b0; + + initial begin +`ifdef __ICARUS__ + if (ACASCREG != 0) $fatal(1, "Unsupported ACASCREG value"); + if (ADREG != 0) $fatal(1, "Unsupported ADREG value"); + if (ALUMODEREG != 0) $fatal(1, "Unsupported ALUMODEREG value"); + if (AREG == 2) $fatal(1, "Unsupported AREG value"); + if (AUTORESET_PATDET != "NO_RESET") $fatal(1, "Unsupported AUTORESET_PATDET value"); + if (A_INPUT != "DIRECT") $fatal(1, "Unsupported A_INPUT value"); + if (BCASCREG != 0) $fatal(1, "Unsupported BCASCREG value"); + if (BREG == 2) $fatal(1, "Unsupported BREG value"); + if (B_INPUT != "DIRECT") $fatal(1, "Unsupported B_INPUT value"); + if (CARRYINREG != 0) $fatal(1, "Unsupported CARRYINREG value"); + if (CARRYINSELREG != 0) $fatal(1, "Unsupported CARRYINSELREG value"); + if (CREG != 0) $fatal(1, "Unsupported CREG value"); + if (DREG != 0) $fatal(1, "Unsupported DREG value"); + if (INMODEREG != 0) $fatal(1, "Unsupported INMODEREG value"); + if (MREG != 0) $fatal(1, "Unsupported MREG value"); + if (OPMODEREG != 0) $fatal(1, "Unsupported OPMODEREG value"); + //if (PREG != 0) $fatal(1, "Unsupported PREG value"); + if (SEL_MASK != "MASK") $fatal(1, "Unsupported SEL_MASK value"); + if (SEL_PATTERN != "PATTERN") $fatal(1, "Unsupported SEL_PATTERN value"); + if (USE_DPORT != "FALSE") $fatal(1, "Unsupported USE_DPORT value"); + if (USE_MULT != "MULTIPLY") $fatal(1, "Unsupported USE_MULT value"); + if (USE_PATTERN_DETECT != "NO_PATDET") $fatal(1, "Unsupported USE_PATTERN_DETECT value"); + if (USE_SIMD != "ONE48") $fatal(1, "Unsupported USE_SIMD value"); + if (IS_ALUMODE_INVERTED != 4'b0) $fatal(1, "Unsupported IS_ALUMODE_INVERTED value"); + if (IS_CARRYIN_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CARRYIN_INVERTED value"); + if (IS_CLK_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CLK_INVERTED value"); + if (IS_INMODE_INVERTED != 5'b0) $fatal(1, "Unsupported IS_INMODE_INVERTED value"); + if (IS_OPMODE_INVERTED != 7'b0) $fatal(1, "Unsupported IS_OPMODE_INVERTED value"); +`endif + end + + reg signed [29:0] Ar; + reg signed [17:0] Br; + reg signed [47:0] Pr; + generate + if (AREG == 1) begin always @(posedge CLK) if (CEA2) Ar <= A; end + else always @* Ar <= A; + if (BREG == 1) begin always @(posedge CLK) if (CEB2) Br <= B; end + else always @* Br <= B; + endgenerate + + always @* begin + Pr <= {48{1'bx}}; +`ifdef __ICARUS__ + if (INMODE != 4'b0000) $fatal(1, "Unsupported INMODE value"); + if (ALUMODE != 4'b0000) $fatal(1, "Unsupported ALUMODE value"); + if (OPMODE != 7'b000101) $fatal(1, "Unsupported OPMODE value"); + if (CARRYINSEL != 3'b000) $fatal(1, "Unsupported CARRYINSEL value"); + if (ACIN != 30'b0) $fatal(1, "Unsupported ACIN value"); + if (BCIN != 18'b0) $fatal(1, "Unsupported BCIN value"); + if (PCIN != 48'b0) $fatal(1, "Unsupported PCIN value"); + if (CARRYIN != 1'b0) $fatal(1, "Unsupported CARRYIN value"); +`endif + Pr[42:0] <= $signed(Ar[24:0]) * Br; + end + + generate + if (PREG == 1) begin always @(posedge CLK) if (CEP) P <= Pr; end + else always @* P <= Pr; + endgenerate + +endmodule diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index 15fa1b63a..d79349225 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -111,88 +111,6 @@ module DNA_PORT (...); input CLK, DIN, READ, SHIFT; endmodule -module DSP48E1 (...); - parameter integer ACASCREG = 1; - parameter integer ADREG = 1; - parameter integer ALUMODEREG = 1; - parameter integer AREG = 1; - parameter AUTORESET_PATDET = "NO_RESET"; - parameter A_INPUT = "DIRECT"; - parameter integer BCASCREG = 1; - parameter integer BREG = 1; - parameter B_INPUT = "DIRECT"; - parameter integer CARRYINREG = 1; - parameter integer CARRYINSELREG = 1; - parameter integer CREG = 1; - parameter integer DREG = 1; - parameter integer INMODEREG = 1; - parameter integer MREG = 1; - parameter integer OPMODEREG = 1; - parameter integer PREG = 1; - parameter SEL_MASK = "MASK"; - parameter SEL_PATTERN = "PATTERN"; - parameter USE_DPORT = "FALSE"; - parameter USE_MULT = "MULTIPLY"; - parameter USE_PATTERN_DETECT = "NO_PATDET"; - parameter USE_SIMD = "ONE48"; - parameter [47:0] MASK = 48'h3FFFFFFFFFFF; - parameter [47:0] PATTERN = 48'h000000000000; - parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; - parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; - parameter [0:0] IS_CLK_INVERTED = 1'b0; - parameter [4:0] IS_INMODE_INVERTED = 5'b0; - parameter [6:0] IS_OPMODE_INVERTED = 7'b0; - output [29:0] ACOUT; - output [17:0] BCOUT; - output CARRYCASCOUT; - output [3:0] CARRYOUT; - output MULTSIGNOUT; - output OVERFLOW; - output [47:0] P; - output PATTERNBDETECT; - output PATTERNDETECT; - output [47:0] PCOUT; - output UNDERFLOW; - input [29:0] A; - input [29:0] ACIN; - input [3:0] ALUMODE; - input [17:0] B; - input [17:0] BCIN; - input [47:0] C; - input CARRYCASCIN; - input CARRYIN; - input [2:0] CARRYINSEL; - input CEA1; - input CEA2; - input CEAD; - input CEALUMODE; - input CEB1; - input CEB2; - input CEC; - input CECARRYIN; - input CECTRL; - input CED; - input CEINMODE; - input CEM; - input CEP; - input CLK; - input [24:0] D; - input [4:0] INMODE; - input MULTSIGNIN; - input [6:0] OPMODE; - input [47:0] PCIN; - input RSTA; - input RSTALLCARRYIN; - input RSTALUMODE; - input RSTB; - input RSTC; - input RSTCTRL; - input RSTD; - input RSTINMODE; - input RSTM; - input RSTP; -endmodule - module EFUSE_USR (...); parameter [31:0] SIM_EFUSE_VALUE = 32'h00000000; output [31:0] EFUSEUSR; diff --git a/techlibs/xilinx/dsp_map.v b/techlibs/xilinx/dsp_map.v new file mode 100644 index 000000000..3d7b09d69 --- /dev/null +++ b/techlibs/xilinx/dsp_map.v @@ -0,0 +1,46 @@ +module \$__MUL25X18 (input signed [24:0] A, input signed [17:0] B, output signed [42:0] Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + wire [47:0] P_48; + DSP48E1 #( + // Disable all registers + .ACASCREG(0), + .ADREG(0), + .A_INPUT("DIRECT"), + .ALUMODEREG(0), + .AREG(0), + .BCASCREG(0), + .B_INPUT("DIRECT"), + .BREG(0), + .CARRYINREG(0), + .CARRYINSELREG(0), + .CREG(0), + .DREG(0), + .INMODEREG(0), + .MREG(0), + .OPMODEREG(0), + .PREG(0) + ) _TECHMAP_REPLACE_ ( + //Data path + .A({{5{A[24]}}, A}), + .B(B), + .C(48'b0), + .D(24'b0), + .P(P_48), + + .INMODE(4'b0000), + .ALUMODE(4'b0000), + .OPMODE(7'b000101), + .CARRYINSEL(3'b000), + + .ACIN(30'b0), + .BCIN(18'b0), + .PCIN(48'b0), + .CARRYIN(1'b0) + ); + assign Y = P_48; +endmodule diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index b672a0d4f..e5a27015a 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -78,6 +78,9 @@ struct SynthXilinxPass : public ScriptPass log(" -nowidelut\n"); log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n"); log("\n"); + log(" -nodsp\n"); + log(" do not use DSP48E1s to implement multipliers and associated logic\n"); + log("\n"); log(" -widemux <int>\n"); log(" enable inference of hard multiplexer resources (MUXF[78]) for muxes at or\n"); log(" above this number of inputs (minimum value 2, recommended value >= 5).\n"); @@ -104,7 +107,7 @@ struct SynthXilinxPass : public ScriptPass } std::string top_opt, edif_file, blif_file, family; - bool flatten, retime, vpr, nobram, nodram, nosrl, nocarry, nowidelut, abc9; + bool flatten, retime, vpr, nobram, nodram, nosrl, nocarry, nowidelut, nodsp, abc9; int widemux; void clear_flags() YS_OVERRIDE @@ -122,6 +125,7 @@ struct SynthXilinxPass : public ScriptPass nosrl = false; nocarry = false; nowidelut = false; + nodsp = false; abc9 = false; widemux = 0; } @@ -202,6 +206,10 @@ struct SynthXilinxPass : public ScriptPass abc9 = true; continue; } + if (args[argidx] == "-nodsp") { + nodsp = true; + continue; + } break; } extra_args(args, argidx, design); @@ -249,8 +257,8 @@ struct SynthXilinxPass : public ScriptPass if (check_label("coarse")) { run("proc"); - if (help_mode || flatten) - run("flatten", "(if -flatten)"); + if (flatten || help_mode) + run("flatten", "(with '-flatten')"); run("opt_expr"); run("opt_clean"); run("check"); @@ -275,6 +283,12 @@ struct SynthXilinxPass : public ScriptPass } run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6"); + + if (!nodsp || help_mode) { + // NB: Xilinx multipliers are signed only + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=25 -D DSP_B_MAXWIDTH=18 -D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18"); + } + run("alumacc"); run("share"); run("opt"); @@ -317,6 +331,10 @@ struct SynthXilinxPass : public ScriptPass run("memory_map"); run("dffsr2dff"); run("dff2dffe"); + if (help_mode || !nodsp) { + run("techmap -map +/xilinx/dsp_map.v", "(skip if '-nodsp')"); + run("xilinx_dsp", " (skip if '-nodsp')"); + } if (help_mode) { run("simplemap t:$mux", " ('-widemux' only)"); run("muxcover <internal options>, ('-widemux' only)"); |