diff options
Diffstat (limited to 'passes/pmgen/xilinx_dsp_cascade.pmg')
-rw-r--r-- | passes/pmgen/xilinx_dsp_cascade.pmg | 193 |
1 files changed, 126 insertions, 67 deletions
diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/passes/pmgen/xilinx_dsp_cascade.pmg index 7a32df2b7..b14a1ee0a 100644 --- a/passes/pmgen/xilinx_dsp_cascade.pmg +++ b/passes/pmgen/xilinx_dsp_cascade.pmg @@ -62,12 +62,11 @@ code #define MAX_DSP_CASCADE 20 endcode -// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer -// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already -// use the 'PCOUT' port +// (1) Starting from a DSP48* cell that (a) has the Z multiplexer +// (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1) +// set to zero and (b) doesn't already use the 'PCOUT' port match first - select first->type.in(\DSP48E1) - select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000") + select (first->type.in(\DSP48A, \DSP48A1) && port(first, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(\DSP48E1) && port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")) select nusers(port(first, \PCOUT, SigSpec())) <= 1 endmatch @@ -100,14 +99,21 @@ finally add_siguser(cascade, dsp); SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); - if (P == 17) - opmode[6] = State::S1; - else if (P == 0) - opmode[6] = State::S0; - else log_abort(); - - opmode[5] = State::S0; - opmode[4] = State::S1; + if (dsp->type.in(\DSP48A, \DSP48A1)) { + log_assert(P == 0); + opmode[3] = State::S0; + opmode[2] = State::S1; + } + else if (dsp->type.in(\DSP48E1)) { + if (P == 17) + opmode[6] = State::S1; + else if (P == 0) + opmode[6] = State::S0; + else log_abort(); + + opmode[5] = State::S0; + opmode[4] = State::S1; + } dsp_pcin->setPort(\OPMODE, opmode); log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); @@ -120,21 +126,42 @@ finally add_siguser(cascade, dsp_pcin); add_siguser(cascade, dsp); - dsp->setParam(ID(ACASCREG), AREG); + if (dsp->type.in(\DSP48E1)) + dsp->setParam(ID(ACASCREG), AREG); dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); } if (BREG >= 0) { Wire *cascade = module->addWire(NEW_ID, 18); - dsp_pcin->setPort(ID(B), Const(0, 18)); - dsp_pcin->setPort(ID(BCIN), cascade); + if (dsp->type.in(\DSP48A, \DSP48A1)) { + // According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf] + // "The DSP48A1 component uses this input when cascading + // BCOUT from an adjacent DSP48A1 slice. The tools then + // translate BCOUT cascading to the dedicated BCIN input + // and set the B_INPUT attribute for implementation." + dsp_pcin->setPort(ID(B), cascade); + } + else { + dsp_pcin->setPort(ID(B), Const(0, 18)); + dsp_pcin->setPort(ID(BCIN), cascade); + } dsp->setPort(ID(BCOUT), cascade); add_siguser(cascade, dsp_pcin); add_siguser(cascade, dsp); - dsp->setParam(ID(BCASCREG), BREG); - dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); + if (dsp->type.in(\DSP48E1)) { + dsp->setParam(ID(BCASCREG), BREG); + // According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf] + // "The attribute is only used by place and route tools and + // is not necessary for the users to set for synthesis. The + // attribute is determined by the connection to the B port + // of the DSP48A1 slice. If the B port is connected to the + // BCOUT of another DSP48A1 slice, then the tools automatically + // set the attribute to 'CASCADE', otherwise it is set to + // 'DIRECT'". + dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); + } log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); } @@ -156,22 +183,21 @@ subpattern tail arg first arg next -// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled, +// (2.1) Match another DSP48* cell that (a) does not have the CREG enabled, // (b) has its Z multiplexer output set to the 'C' port, which is // driven by the 'P' output of the previous DSP cell, and (c) has its // 'PCIN' port unused match nextP - select nextP->type.in(\DSP48E1) select !param(nextP, \CREG, State::S1).as_bool() - select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") + select (nextP->type.in(\DSP48A, \DSP48A1) && port(nextP, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(\DSP48E1) && port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")) select nusers(port(nextP, \C, SigSpec())) > 1 select nusers(port(nextP, \PCIN, SigSpec())) == 0 index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] semioptional endmatch -// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the -// previous DSP cell right-shifted by 17 bits +// (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven +// by the 'P' output of the previous DSP cell right-shifted by 17 bits match nextP_shift17 if !nextP select nextP_shift17->type.in(\DSP48E1) @@ -188,6 +214,8 @@ code next if (!nextP) next = nextP_shift17; if (next) { + if (next->type != first->type) + reject; unextend = [](const SigSpec &sig) { int i; for (i = GetSize(sig)-1; i > 0; i--) @@ -202,38 +230,50 @@ code next endcode // (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists) -// if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this -// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already -// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already -// use its ACOUT port, then examine if an ACOUT -> ACIN cascade -// opportunity exists by matching for a $dff-with-optional-clock-enable- -// or-reset and checking that the 'D' input of this register is the same -// as the 'A' input of the previous DSP +// if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade, +// (b) the previous DSP does not already use its ACOUT port, then +// examine if an ACOUT -> ACIN cascade opportunity exists if +// (i) A ports are identical, or (ii) separated by a +// $dff-with-optional-clock-enable-or-reset and checking that the 'D' input +// of this register is the same as the 'A' input of the previous DSP +// TODO: Check for two levels of flops, instead of just one code argQ clock AREG AREG = -1; - if (next) { + if (next && next->type.in(\DSP48E1)) { Cell *prev = std::get<0>(chain.back()); - if (param(prev, \AREG, 2).as_int() > 0 && - param(next, \AREG, 2).as_int() > 0 && - param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && + + if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && + port(next, \ACIN, SigSpec()).is_fully_zero() && nusers(port(prev, \ACOUT, SigSpec())) <= 1) { - argQ = unextend(port(next, \A)); - clock = port(prev, \CLK); - subpattern(in_dffe); - if (dff) { - if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) - goto reject_AREG; - if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) - goto reject_AREG; - if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) - goto reject_AREG; - if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) - goto reject_AREG; - if (dffD == unextend(port(prev, \A))) - AREG = 1; -reject_AREG: ; + if (param(prev, \AREG, 2) == 0) { + if (port(prev, \A) == port(next, \A)) + AREG = 0; + } + else { + argQ = unextend(port(next, \A)); + clock = port(prev, \CLK); + subpattern(in_dffe); + if (dff) { + if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) + goto reject_AREG; + if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) + goto reject_AREG; + IdString CEA; + if (param(prev, \AREG, 2) == 1) + CEA = \CEA2; + else if (param(prev, \AREG, 2) == 2) + CEA = \CEA1; + else log_abort(); + if (!dffcemux && port(prev, CEA, State::S0) != State::S1) + goto reject_AREG; + if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0)) + goto reject_AREG; + if (dffD == unextend(port(prev, \A))) + AREG = 1; + } } } +reject_AREG: ; } endcode @@ -242,28 +282,47 @@ code argQ clock BREG BREG = -1; if (next) { Cell *prev = std::get<0>(chain.back()); - if (param(prev, \BREG, 2).as_int() > 0 && - param(next, \BREG, 2).as_int() > 0 && - param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && + if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && port(next, \BCIN, SigSpec()).is_fully_zero() && nusers(port(prev, \BCOUT, SigSpec())) <= 1) { - argQ = unextend(port(next, \B)); - clock = port(prev, \CLK); - subpattern(in_dffe); - if (dff) { - if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) - goto reject_BREG; - if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) - goto reject_BREG; - if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) - goto reject_BREG; - if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) - goto reject_BREG; - if (dffD == unextend(port(prev, \B))) - BREG = 1; -reject_BREG: ; + if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) || + (next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) { + if (port(prev, \B) == port(next, \B)) + BREG = 0; + } + else { + argQ = unextend(port(next, \B)); + clock = port(prev, \CLK); + subpattern(in_dffe); + if (dff) { + if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) + goto reject_BREG; + if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) + goto reject_BREG; + IdString CEB; + if (next->type.in(\DSP48A, \DSP48A1)) + CEB = \CEB; + else if (next->type.in(\DSP48E1)) { + if (param(prev, \BREG, 2) == 1) + CEB = \CEB2; + else if (param(prev, \BREG, 2) == 2) + CEB = \CEB1; + else log_abort(); + } + else log_abort(); + if (!dffcemux && port(prev, CEB, State::S0) != State::S1) + goto reject_BREG; + if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0)) + goto reject_BREG; + if (dffD == unextend(port(prev, \B))) { + if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) != 0) + goto reject_BREG; + BREG = 1; + } + } } } +reject_BREG: ; } endcode |