diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc
index 389f0cb56..cd88f9449 100644
--- a/passes/pmgen/xilinx_dsp.cc
+++ b/passes/pmgen/xilinx_dsp.cc
@@ -37,16 +37,26 @@ void pack_xilinx_dsp(xilinx_dsp_pm &pm)
 	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("addAB: %s\n", log_id(st.addAB, "--"));
 	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));
+	log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp));
 
 	Cell *cell = st.dsp;
-	log_assert(cell);
+	SigSpec P = st.sigP;
+
+	if (st.addAB) {
+		log("  adder %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
+		cell->setPort("\\C", st.sigC.extend_u0(48, true));
+		SigSpec &opmode = cell->connections_.at("\\OPMODE");
+		opmode[6] = State::S0;
+		opmode[5] = State::S1;
+		opmode[4] = State::S1;
+		pm.autoremove(st.addAB);
+	}
 
 	if (st.clock != SigBit())
 	{
@@ -79,7 +89,6 @@ void pack_xilinx_dsp(xilinx_dsp_pm &pm)
 			else log_abort();
 		}
 		if (st.ffP) {
-			SigSpec P = cell->getPort("\\P");
 			SigSpec D;
 			//if (st.muxP)
 			//	D = st.muxP->getPort("\\B");
@@ -87,7 +96,6 @@ void pack_xilinx_dsp(xilinx_dsp_pm &pm)
 				D = st.ffP->getPort("\\D");
 			SigSpec Q = st.ffP->getPort("\\Q");
 			P.replace(pm.sigmap(D), Q);
-			cell->setPort("\\P", P);
 			cell->setParam("\\PREG", State::S1);
 			if (st.ffP->type == "$dff")
 				cell->setPort("\\CEP", State::S1);
@@ -112,6 +120,10 @@ void pack_xilinx_dsp(xilinx_dsp_pm &pm)
 		log("\n");
 	}
 
+	if (GetSize(P) < 48)
+		P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
+	cell->setPort("\\P", P);
+
 	pm.blacklist(cell);
 }
 
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg
index f95de9410..4f5fae8df 100644
--- a/passes/pmgen/xilinx_dsp.pmg
+++ b/passes/pmgen/xilinx_dsp.pmg
@@ -1,7 +1,8 @@
 pattern xilinx_dsp
 
 state <SigBit> clock
-state <SigSpec> sigPused
+state <SigSpec> sigC sigP sigPused
+state <Cell*> addAB
 
 match dsp
 	select dsp->type.in(\DSP48E1)
@@ -43,13 +44,69 @@ code clock
 	}
 endcode
 
+code sigP
+	sigP = port(dsp, \P);
+endcode
+
+match addA
+	select addA->type.in($add)
+	select param(addA, \A_SIGNED).as_bool() && param(addA, \B_SIGNED).as_bool()
+	select nusers(port(addA, \A)) == 2
+	//index <SigSpec> port(addA, \A) === sigP.extract(0, param(addA, \A_WIDTH).as_int())
+	filter GetSize(sigP) >= param(addA, \A_WIDTH).as_int()
+	filter port(addA, \A) == sigP.extract(0, param(addA, \A_WIDTH).as_int())
+	optional
+endmatch
+
+match addB
+	if !addA
+	select addB->type.in($add, $sub)
+	select param(addB, \A_SIGNED).as_bool() && param(addB, \B_SIGNED).as_bool()
+	select nusers(port(addB, \B)) == 2
+	//index <SigSpec> port(addB, \B) === sigP.extract(0, param(addB, \B_WIDTH).as_int())
+	filter GetSize(sigP) >= param(addB, \B_WIDTH).as_int()
+	filter port(addB, \B) ==  sigP.extract(0, param(addB, \B_WIDTH).as_int())
+	optional
+endmatch
+
+code addAB sigC sigP
+	bool C_SIGNED = false;
+	if (addA) {
+		addAB = addA;
+		sigC = port(addAB, \B);
+		C_SIGNED = param(addAB, \B_SIGNED).as_bool();
+	}
+	if (addB) {
+		addAB = addB;
+		sigC = port(addAB, \A);
+		C_SIGNED = param(addAB, \B_SIGNED).as_bool();
+	}
+	if (addAB) {
+		// Ensure that adder is not used
+		SigSpec opmodeZ = port(dsp, \OPMODE).extract(4,3);
+		if (!opmodeZ.is_fully_zero())
+			reject;
+
+		int natural_mul_width = GetSize(port(dsp, \A)) + GetSize(port(dsp, \B));
+		int actual_mul_width = GetSize(sigP);
+		int actual_acc_width = GetSize(sigC);
+
+		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
+			reject;
+		//if ((actual_acc_width != actual_mul_width) && (param(dsp, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
+		//	reject;
+
+		sigP = port(addAB, \Y);
+                sigC.extend_u0(32, C_SIGNED);
+	}
+endcode
+
 // Extract the bits of P that actually have a consumer
 // (as opposed to being a dummy)
 code sigPused
-	SigSpec P = port(dsp, \P);
-	for (int i = 0; i < GetSize(P); i++)
-		if (P[i].wire && nusers(P[i]) > 1)
-			sigPused.append(P[i]);
+	for (int i = 0; i < GetSize(sigP); i++)
+		if (sigP[i].wire && nusers(sigP[i]) > 1)
+			sigPused.append(sigP[i]);
 endcode
 
 match ffP
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 7b1fe5e3b..a54b3ac52 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -287,6 +287,8 @@ struct SynthXilinxPass : public ScriptPass
 			if (!nodsp || help_mode) {
 				// NB: Xilinx multipliers are signed only
 				run("techmap -map +/mul2dsp.v -map +/xilinx/dsp_map.v -D DSP_A_MAXWIDTH=25 -D DSP_B_MAXWIDTH=18 -D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18", "(skip if '-nodsp')");
+				run("opt_expr -fine", "                 (skip if '-nodsp')");
+				run("wreduce", "                        (skip if '-nodsp')");
 				run("xilinx_dsp", "                     (skip if '-nodsp')");
 				run("chtype -set $mul t:$__soft_mul","  (skip if '-nodsp')");
 			}