1// This file describes the second of three pattern matcher setups that 2// forms the `xilinx_dsp` pass described in xilinx_dsp.cc 3// At a high level, it works as follows: 4// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already, 5// and (b) uses the 'C' port 6// (2) Match the driver of the 'C' input to a possible $dff cell (CREG) 7// (attached to at most two $mux cells that implement clock-enable or 8// reset functionality, using a subpattern discussed below) 9// Notes: 10// - Running CREG packing after xilinx_dsp_pack is necessary since there is no 11// guarantee that the cell ordering corresponds to the "expected" case (i.e. 12// the order in which they appear in the source) thus the possiblity existed 13// that a register got packed as a CREG into a downstream DSP that should 14// have otherwise been a PREG of an upstream DSP that had not been visited 15// yet 16// - The reason this is separated out from the xilinx_dsp.pmg file is 17// for efficiency --- each *.pmg file creates a class of the same basename, 18// which when constructed, creates a custom database tailored to the 19// pattern(s) contained within. Since the pattern in this file must be 20// executed after the pattern contained in xilinx_dsp.pmg, it is necessary 21// to reconstruct this database. Separating the two patterns into 22// independent files causes two smaller, more specific, databases. 23 24pattern xilinx_dsp_packC 25 26udata <std::function<SigSpec(const SigSpec&)>> unextend 27state <SigBit> clock 28state <SigSpec> sigC sigP 29state <Cell*> ffC 30 31// Variables used for subpatterns 32state <SigSpec> argQ argD 33state <int> ffoffset 34udata <SigSpec> dffD dffQ 35udata <SigBit> dffclock 36udata <Cell*> dff 37 38// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already, 39// and (b) uses the 'C' port 40match dsp 41 select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1) 42 select param(dsp, \CREG).as_int() == 0 43 select nusers(port(dsp, \C, SigSpec())) > 1 44endmatch 45 46code sigC sigP clock 47 unextend = [](const SigSpec &sig) { 48 int i; 49 for (i = GetSize(sig)-1; i > 0; i--) 50 if (sig[i] != sig[i-1]) 51 break; 52 // Do not remove non-const sign bit 53 if (sig[i].wire) 54 ++i; 55 return sig.extract(0, i); 56 }; 57 sigC = unextend(port(dsp, \C, SigSpec())); 58 59 SigSpec P = port(dsp, \P); 60 if (!dsp->type.in(\DSP48E1) || 61 param(dsp, \USE_MULT).decode_string() == "MULTIPLY") { 62 // Only care about those bits that are used 63 int i; 64 for (i = GetSize(P)-1; i >= 0; i--) 65 if (nusers(P[i]) > 1) 66 break; 67 i++; 68 log_assert(nusers(P.extract_end(i)) <= 1); 69 sigP = P.extract(0, i); 70 } 71 else 72 sigP = P; 73 74 clock = port(dsp, \CLK, SigBit()); 75endcode 76 77// (2) Match the driver of the 'C' input to a possible $dff cell (CREG) 78// (attached to at most two $mux cells that implement clock-enable or 79// reset functionality, using the in_dffe subpattern) 80code argQ ffC sigC clock 81 argQ = sigC; 82 subpattern(in_dffe); 83 if (dff) { 84 ffC = dff; 85 clock = dffclock; 86 sigC = dffD; 87 } 88endcode 89 90code 91 if (ffC) 92 accept; 93endcode 94 95// ####################### 96 97// Subpattern for matching against input registers, based on knowledge of the 98// 'Q' input. 99subpattern in_dffe 100arg argQ clock 101 102code 103 dff = nullptr; 104 if (argQ.empty()) 105 reject; 106 for (const auto &c : argQ.chunks()) { 107 // Abandon matches when 'Q' is a constant 108 if (!c.wire) 109 reject; 110 // Abandon matches when 'Q' has the keep attribute set 111 if (c.wire->get_bool_attribute(\keep)) 112 reject; 113 // Abandon matches when 'Q' has a non-zero init attribute set 114 // (not supported by DSP48E1) 115 Const init = c.wire->attributes.at(\init, Const()); 116 if (!init.empty()) 117 for (auto b : init.extract(c.offset, c.width)) 118 if (b != State::Sx && b != State::S0) 119 reject; 120 } 121endcode 122 123match ff 124 select ff->type.in($dff, $dffe, $sdff, $sdffe) 125 // DSP48E1 does not support clock inversion 126 select param(ff, \CLK_POLARITY).as_bool() 127 128 // Check that reset value, if present, is fully 0. 129 filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() 130 131 slice offset GetSize(port(ff, \D)) 132 index <SigBit> port(ff, \Q)[offset] === argQ[0] 133 134 // Check that the rest of argQ is present 135 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) 136 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ 137 138 filter clock == SigBit() || port(ff, \CLK) == clock 139endmatch 140 141code argQ 142 SigSpec Q = port(ff, \Q); 143 dff = ff; 144 dffclock = port(ff, \CLK); 145 dffD = argQ; 146 SigSpec D = port(ff, \D); 147 argQ = Q; 148 dffD.replace(argQ, D); 149endcode 150