aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/rtlil.cc5
-rw-r--r--kernel/rtlil.h2
-rw-r--r--passes/pmgen/.gitignore3
-rw-r--r--passes/pmgen/Makefile.inc9
-rw-r--r--passes/pmgen/xilinx_dsp.cc136
-rw-r--r--passes/pmgen/xilinx_dsp.pmg94
-rw-r--r--techlibs/common/mul2dsp.v4
-rw-r--r--techlibs/ecp5/dsp_map.v4
-rw-r--r--techlibs/xilinx/Makefile.inc1
-rw-r--r--techlibs/xilinx/cells_map.v41
-rw-r--r--techlibs/xilinx/cells_sim.v26
-rw-r--r--techlibs/xilinx/dsp_map.v40
-rw-r--r--techlibs/xilinx/synth_xilinx.cc4
13 files changed, 312 insertions, 57 deletions
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index a09f4a0d1..6f5082138 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -3353,7 +3353,7 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
{
unpack();
cover("kernel.rtlil.sigspec.extract_pos");
- return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);
+ return std::vector<RTLIL::SigBit>(bits_.begin() + offset, length >= 0 ? bits_.begin() + offset + length : bits_.end() + length + 1);
}
void RTLIL::SigSpec::append(const RTLIL::SigSpec &signal)
@@ -3426,7 +3426,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 +3443,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 82cbfaf28..8d88cc97c 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -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..10e245e00 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/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc
new file mode 100644
index 000000000..a09f96a7f
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp.cc
@@ -0,0 +1,136 @@
+/*
+ * 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
+
+#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("P_WIDTH: %d\n", st.P_WIDTH);
+#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 D = st.ffA->getPort("\\D");
+ cell->setPort("\\A", D.extend_u0(30));
+ 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 D = st.ffB->getPort("\\D");
+ cell->setPort("\\B", D.extend_u0(18));
+ 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 Q = st.ffP->getPort("\\Q");
+ Q.append(P.extract(GetSize(Q), -1));
+ 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.autoremove(st.ffA);
+ pm.autoremove(st.ffB);
+ pm.autoremove(st.ffP);
+ pm.autoremove(st.muxP);
+ 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..ceed64b30
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp.pmg
@@ -0,0 +1,94 @@
+pattern xilinx_dsp
+
+state <SigBit> clock
+state <int> P_WIDTH
+
+match dsp
+ select dsp->type.in(\DSP48E1)
+endmatch
+
+match ffA
+ select ffA->type.in($dff, $dffe)
+ // select nusers(port(ffA, \Q)) == 2
+ index <SigSpec> port(ffA, \Q).extend_u0(30) === port(dsp, \A)
+ // DSP48E1 does not support clock inversion
+ index <Const> param(ffA, \CLK_POLARITY).as_bool() === true
+ optional
+endmatch
+
+code clock
+ if (ffA)
+ clock = port(ffA, \CLK).as_bit();
+endcode
+
+match ffB
+ select ffB->type.in($dff, $dffe)
+ // select nusers(port(ffB, \Q)) == 2
+ index <SigSpec> port(ffB, \Q).extend_u0(18) === port(dsp, \B)
+ index <Const> param(ffB, \CLK_POLARITY).as_bool() === true
+ optional
+endmatch
+
+code clock
+ if (ffB) {
+ SigBit c = port(ffB, \CLK).as_bit();
+
+ if (clock != SigBit() && c != clock)
+ reject;
+
+ clock = c;
+ }
+endcode
+
+code P_WIDTH
+ SigSpec P = port(dsp, \P);
+ int i;
+ for (i = GetSize(P); i > 0; i--)
+ if (nusers(P[i-1]) > 1)
+ break;
+ P_WIDTH = i;
+endcode
+
+match ffP
+ select ffP->type.in($dff, $dffe)
+ select nusers(port(ffP, \D)) == 2
+ filter param(ffP, \WIDTH).as_int() == P_WIDTH
+ filter port(ffP, \D) == port(dsp, \P).extract(0, P_WIDTH)
+ index <Const> param(ffP, \CLK_POLARITY) === State::S1
+ 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 !ffP
+ select muxP->type.in($mux)
+ select port(muxP, \A).is_fully_undef()
+ filter param(muxP, \WIDTH).as_int() == P_WIDTH
+ filter port(muxP, \B) == port(dsp, \P).extract(0, P_WIDTH)
+ select nusers(port(muxP, \B)) == 2
+ optional
+endmatch
+
+match ffY
+ if muxP
+ select ffY->type.in($dff, $dffe)
+ select nusers(port(ffY, \D)) == 2
+ index <SigSpec> port(ffY, \D) === port(muxP, \Y)
+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/mul2dsp.v b/techlibs/common/mul2dsp.v
index 262e29986..6f2281c0a 100644
--- a/techlibs/common/mul2dsp.v
+++ b/techlibs/common/mul2dsp.v
@@ -33,7 +33,7 @@ module \$mul (A, B, Y);
output [Y_WIDTH-1:0] Y;
generate
- if (B_WIDTH < A_WIDTH)
+ if (A_WIDTH >= B_WIDTH)
\$__mul_gen #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
@@ -213,7 +213,7 @@ module \$__mul_gen (A, B, Y);
`DSP_NAME _TECHMAP_REPLACE_ (
.A({ {{`DSP_A_MAXWIDTH-A_WIDTH}{Asign}}, A }),
.B({ {{`DSP_B_MAXWIDTH-B_WIDTH}{Bsign}}, B }),
- .OUT({dummy, out})
+ .Y({dummy, out})
);
if (Y_WIDTH < A_WIDTH+B_WIDTH)
assign Y = out[Y_WIDTH-1:0];
diff --git a/techlibs/ecp5/dsp_map.v b/techlibs/ecp5/dsp_map.v
index 5f7755afb..24e28869e 100644
--- a/techlibs/ecp5/dsp_map.v
+++ b/techlibs/ecp5/dsp_map.v
@@ -1,10 +1,10 @@
-module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] OUT);
+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(OUT[0]), .P1(OUT[1]), .P2(OUT[2]), .P3(OUT[3]), .P4(OUT[4]), .P5(OUT[5]), .P6(OUT[6]), .P7(OUT[7]), .P8(OUT[8]), .P9(OUT[9]), .P10(OUT[10]), .P11(OUT[11]), .P12(OUT[12]), .P13(OUT[13]), .P14(OUT[14]), .P15(OUT[15]), .P16(OUT[16]), .P17(OUT[17]), .P18(OUT[18]), .P19(OUT[19]), .P20(OUT[20]), .P21(OUT[21]), .P22(OUT[22]), .P23(OUT[23]), .P24(OUT[24]), .P25(OUT[25]), .P26(OUT[26]), .P27(OUT[27]), .P28(OUT[28]), .P29(OUT[29]), .P30(OUT[30]), .P31(OUT[31]), .P32(OUT[32]), .P33(OUT[33]), .P34(OUT[34]), .P35(OUT[35])
+ .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/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_map.v b/techlibs/xilinx/cells_map.v
index 8302e0b3a..2eb9fa2c1 100644
--- a/techlibs/xilinx/cells_map.v
+++ b/techlibs/xilinx/cells_map.v
@@ -365,44 +365,3 @@ module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1);
MUXF8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O));
endmodule
`endif
-
-module \$__MUL25X18 (input [23:0] A, input [16:0] B, output [40:0] OUT);
- 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({6'b0, A}),
- .B({1'b0, 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 OUT = P_48;
-endmodule
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index ea5a3b788..1262fc8c1 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -466,11 +466,11 @@ module DSP48E1 (
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 != 0) $fatal(1, "Unsupported AREG 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 != 0) $fatal(1, "Unsupported BREG 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");
@@ -479,7 +479,7 @@ module DSP48E1 (
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 (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");
@@ -494,8 +494,18 @@ module DSP48E1 (
`endif
end
+ reg [29:0] Ar;
+ reg [17:0] Br;
+ reg [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
- P <= {48{1'bx}};
+ Pr <= {48{1'bx}};
`ifdef __ICARUS__
if (INMODE != 4'b0000) $fatal(1, "Unsupported INMODE value");
if (ALUMODE != 4'b0000) $fatal(1, "Unsupported ALUMODE value");
@@ -506,6 +516,12 @@ module DSP48E1 (
if (PCIN != 48'b0) $fatal(1, "Unsupported PCIN value");
if (CARRYIN != 1'b0) $fatal(1, "Unsupported CARRYIN value");
`endif
- P[42:0] <= $signed(A[24:0]) * $signed(B);
+ Pr[42:0] <= $signed(Ar[24:0]) * $signed(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/dsp_map.v b/techlibs/xilinx/dsp_map.v
new file mode 100644
index 000000000..2063c45e2
--- /dev/null
+++ b/techlibs/xilinx/dsp_map.v
@@ -0,0 +1,40 @@
+module \$__MUL25X18 (input [23:0] A, input [16:0] B, output [40:0] Y);
+ 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({6'b0, A}),
+ .B({1'b0, 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 5bfbd1583..815bf0848 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -333,6 +333,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)");