diff options
Diffstat (limited to 'passes/pmgen/xilinx_dsp.pmg')
-rw-r--r-- | passes/pmgen/xilinx_dsp.pmg | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg new file mode 100644 index 000000000..7f1958d5d --- /dev/null +++ b/passes/pmgen/xilinx_dsp.pmg @@ -0,0 +1,179 @@ +pattern xilinx_dsp + +state <SigBit> clock +state <std::set<SigBit>> sigAset sigBset +state <SigSpec> sigC sigP sigPused +state <Cell*> addAB + +match dsp + select dsp->type.in(\DSP48E1) +endmatch + +code sigAset sigBset + SigSpec A = port(dsp, \A); + A.remove_const(); + sigAset = A.to_sigbit_set(); + SigSpec B = port(dsp, \B); + B.remove_const(); + sigBset = B.to_sigbit_set(); +endcode + +match ffA + if param(dsp, \AREG).as_int() == 0 + if !sigAset.empty() + select ffA->type.in($dff) + // DSP48E1 does not support clock inversion + select param(ffA, \CLK_POLARITY).as_bool() + filter includes(port(ffA, \Q).to_sigbit_set(), sigAset) + optional +endmatch + +code clock + if (ffA) { + clock = port(ffA, \CLK).as_bit(); + + for (auto b : port(ffA, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + } +endcode + +match ffB + if param(dsp, \BREG).as_int() == 0 + if !sigBset.empty() + select ffB->type.in($dff) + // DSP48E1 does not support clock inversion + select param(ffB, \CLK_POLARITY).as_bool() + filter includes(port(ffB, \Q).to_sigbit_set(), sigBset) + optional +endmatch + +code clock + if (ffB) { + for (auto b : port(ffB, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffB, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + } +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() + index <int> nusers(port(addA, \A)) === 2 + //index <SigSpec> port(addA, \A) === sigP.extract(0, param(addA, \A_WIDTH).as_int()) + filter param(addA, \A_WIDTH).as_int() <= GetSize(sigP) + 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() + index <int> nusers(port(addB, \B)) === 2 + //index <SigSpec> port(addB, \B) === sigP.extract(0, param(addB, \B_WIDTH).as_int()) + filter param(addB, \B_WIDTH).as_int() <= GetSize(sigP) + filter port(addB, \B) == sigP.extract(0, param(addB, \B_WIDTH).as_int()) + optional +endmatch + +code addAB sigC sigP + if (addA) { + addAB = addA; + sigC = port(addAB, \B); + } + if (addB) { + addAB = addB; + sigC = port(addAB, \A); + } + 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); + } +endcode + +// Extract the bits of P that actually have a consumer +// (as opposed to being a dummy) +code sigPused + for (int i = 0; i < GetSize(sigP); i++) + if (sigP[i].wire && nusers(sigP[i]) > 1) + sigPused.append(sigP[i]); +endcode + +match ffP + if param(dsp, \PREG).as_int() == 0 + if !sigPused.empty() + if nusers(sigPused) == 2 + select ffP->type.in($dff) + // 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) { + for (auto b : port(ffP, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffP, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + } +endcode |