1// This file describes the main pattern matcher setup (of three total) that 2// forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for 3// DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6). 4// At a high level, it works as follows: 5// ( 1) Starting from a DSP48A/DSP48A1 cell 6// ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG) 7// If B1REG matched, treat 'B' input as input of B1REG 8// ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell 9// (pre-adder) 10// ( 4) Match 'B' input for B0REG 11// ( 5) Match 'A' input for A1REG 12// If A1REG, then match 'A' input for A0REG 13// ( 6) Match 'D' input for DREG 14// ( 7) Match 'P' output that exclusively drives an MREG 15// ( 8) Match 'P' output that exclusively drives one of two inputs to an $add 16// cell (post-adder). 17// The other input to the adder is assumed to come in from the 'C' input 18// (note: 'P' -> 'C' connections that exist for accumulators are 19// recognised in xilinx_dsp.cc). 20// ( 9) Match 'P' output that exclusively drives a PREG 21// (10) If post-adder and PREG both present, match for a $mux cell driving 22// the 'C' input, where one of the $mux's inputs is the PREG output. 23// This indicates an accumulator situation, and one where a $mux exists 24// to override the accumulated value: 25// +--------------------------------+ 26// | ____ | 27// +--| \ | 28// |$mux|-+ | 29// 'C' ---|____/ | | 30// | /-------\ +----+ | 31// +----+ +-| post- |___|PREG|---+ 'P' 32// |MREG|------ | adder | +----+ 33// +----+ \-------/ 34// Notes: see the notes in xilinx_dsp.pmg 35 36pattern xilinx_dsp48a_pack 37 38state <SigBit> clock 39state <SigSpec> sigA sigB sigC sigD sigM sigP 40state <IdString> postAddAB postAddMuxAB 41state <Cell*> ffA0 ffA1 42state <Cell*> ffB0 ffB1 43state <Cell*> ffD ffM ffP 44 45// Variables used for subpatterns 46state <SigSpec> argQ argD 47udata <SigSpec> dffD dffQ 48udata <SigBit> dffclock 49udata <Cell*> dff 50 51// (1) Starting from a DSP48A/DSP48A1 cell 52match dsp 53 select dsp->type.in(\DSP48A, \DSP48A1) 54endmatch 55 56code sigA sigB sigC sigD sigM clock 57 auto unextend = [](const SigSpec &sig) { 58 int i; 59 for (i = GetSize(sig)-1; i > 0; i--) 60 if (sig[i] != sig[i-1]) 61 break; 62 // Do not remove non-const sign bit 63 if (sig[i].wire) 64 ++i; 65 return sig.extract(0, i); 66 }; 67 sigA = unextend(port(dsp, \A)); 68 sigB = unextend(port(dsp, \B)); 69 70 sigC = port(dsp, \C, SigSpec()); 71 sigD = port(dsp, \D, SigSpec()); 72 73 SigSpec P = port(dsp, \P); 74 // Only care about those bits that are used 75 int i; 76 for (i = GetSize(P)-1; i >= 0; i--) 77 if (nusers(P[i]) > 1) 78 break; 79 i++; 80 log_assert(nusers(P.extract_end(i)) <= 1); 81 // This sigM could have no users if downstream sinks (e.g. $add) is 82 // narrower than $mul result, for example 83 if (i == 0) 84 reject; 85 sigM = P.extract(0, i); 86 87 clock = port(dsp, \CLK, SigBit()); 88endcode 89 90// (2) Match the driver of the 'B' input to a possible $dff cell (B1REG) 91// (attached to at most two $mux cells that implement clock-enable or 92// reset functionality, using a subpattern discussed above) 93// If matched, treat 'B' input as input of B1REG 94code argQ ffB1 sigB clock 95 if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) { 96 argQ = sigB; 97 subpattern(in_dffe); 98 if (dff) { 99 ffB1 = dff; 100 clock = dffclock; 101 sigB = dffD; 102 } 103 } 104endcode 105 106// (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell 107// (pre-adder) 108match preAdd 109 if sigD.empty() || sigD.is_fully_zero() 110 if param(dsp, \B0REG).as_int() == 0 111 // Ensure that preAdder not already used 112 if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero() 113 114 select preAdd->type.in($add, $sub) 115 // Output has to be 18 bits or less 116 select GetSize(port(preAdd, \Y)) <= 18 117 select nusers(port(preAdd, \Y)) == 2 118 // D port has to be 18 bits or less 119 select GetSize(port(preAdd, \A)) <= 18 120 // B port has to be 18 bits or less 121 select GetSize(port(preAdd, \B)) <= 18 122 index <SigSpec> port(preAdd, \Y) === sigB 123 124 optional 125endmatch 126 127code sigB sigD 128 if (preAdd) { 129 sigD = port(preAdd, \A); 130 sigB = port(preAdd, \B); 131 } 132endcode 133 134// (4) Match 'B' input for B0REG 135code argQ ffB0 sigB clock 136 if (param(dsp, \B0REG).as_int() == 0) { 137 argQ = sigB; 138 subpattern(in_dffe); 139 if (dff) { 140 if (ffB1) { 141 if (dff->type != ffB1->type) 142 goto ffB0_end; 143 if (dff->type.in($sdff, $sdffe, $sdffce)) { 144 if (param(dff, \SRST_POLARITY) != param(ffB1, \SRST_POLARITY)) 145 goto ffB0_end; 146 if (port(dff, \SRST) != port(ffB1, \SRST)) 147 goto ffB0_end; 148 } 149 if (dff->type.in($dffe, $sdffe, $sdffce)) { 150 if (param(dff, \EN_POLARITY) != param(ffB1, \EN_POLARITY)) 151 goto ffB0_end; 152 if (port(dff, \EN) != port(ffB1, \EN)) 153 goto ffB0_end; 154 } 155 } 156 ffB0 = dff; 157 clock = dffclock; 158 sigB = dffD; 159 } 160 } 161ffB0_end: 162endcode 163 164// (5) Match 'A' input for A1REG 165// If A1REG, then match 'A' input for A0REG 166code argQ ffA1 sigA clock ffA0 167 if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) { 168 argQ = sigA; 169 subpattern(in_dffe); 170 if (dff) { 171 ffA1 = dff; 172 clock = dffclock; 173 sigA = dffD; 174 175 // Now attempt to match A0 176 if (ffA1) { 177 argQ = sigA; 178 subpattern(in_dffe); 179 if (dff) { 180 if (dff->type != ffA1->type) 181 goto ffA0_end; 182 if (dff->type.in($sdff, $sdffe, $sdffce)) { 183 if (param(dff, \SRST_POLARITY) != param(ffA1, \SRST_POLARITY)) 184 goto ffA0_end; 185 if (port(dff, \SRST) != port(ffA1, \SRST)) 186 goto ffA0_end; 187 } 188 if (dff->type.in($dffe, $sdffe, $sdffce)) { 189 if (param(dff, \EN_POLARITY) != param(ffA1, \EN_POLARITY)) 190 goto ffA0_end; 191 if (port(dff, \EN) != port(ffA1, \EN)) 192 goto ffA0_end; 193 } 194 195 ffA0 = dff; 196 clock = dffclock; 197 sigA = dffD; 198 199ffA0_end: ; 200 } 201 } 202 203 } 204 } 205endcode 206 207// (6) Match 'D' input for DREG 208code argQ ffD sigD clock 209 if (param(dsp, \DREG).as_int() == 0) { 210 argQ = sigD; 211 subpattern(in_dffe); 212 if (dff) { 213 ffD = dff; 214 clock = dffclock; 215 sigD = dffD; 216 } 217 } 218endcode 219 220// (7) Match 'P' output that exclusively drives an MREG 221code argD ffM sigM sigP clock 222 if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { 223 argD = sigM; 224 subpattern(out_dffe); 225 if (dff) { 226 ffM = dff; 227 clock = dffclock; 228 sigM = dffQ; 229 } 230 } 231 sigP = sigM; 232endcode 233 234// (8) Match 'P' output that exclusively drives one of two inputs to an $add 235// cell (post-adder). 236// The other input to the adder is assumed to come in from the 'C' input 237// (note: 'P' -> 'C' connections that exist for accumulators are 238// recognised in xilinx_dsp.cc). 239match postAdd 240 // Ensure that Z mux is not already used 241 if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero() 242 243 select postAdd->type.in($add) 244 select GetSize(port(postAdd, \Y)) <= 48 245 choice <IdString> AB {\A, \B} 246 select nusers(port(postAdd, AB)) == 2 247 248 index <SigBit> port(postAdd, AB)[0] === sigP[0] 249 filter GetSize(port(postAdd, AB)) >= GetSize(sigP) 250 filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP 251 // Check that remainder of AB is a sign- or zero-extension 252 filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)) 253 254 set postAddAB AB 255 optional 256endmatch 257 258code sigC sigP 259 if (postAdd) { 260 sigC = port(postAdd, postAddAB == \A ? \B : \A); 261 sigP = port(postAdd, \Y); 262 } 263endcode 264 265// (9) Match 'P' output that exclusively drives a PREG 266code argD ffP sigP clock 267 if (param(dsp, \PREG).as_int() == 0) { 268 if (nusers(sigP) == 2) { 269 argD = sigP; 270 subpattern(out_dffe); 271 if (dff) { 272 ffP = dff; 273 clock = dffclock; 274 sigP = dffQ; 275 } 276 } 277 } 278endcode 279 280// (10) If post-adder and PREG both present, match for a $mux cell driving 281// the 'C' input, where one of the $mux's inputs is the PREG output. 282// This indicates an accumulator situation, and one where a $mux exists 283// to override the accumulated value: 284// +--------------------------------+ 285// | ____ | 286// +--| \ | 287// |$mux|-+ | 288// 'C' ---|____/ | | 289// | /-------\ +----+ | 290// +----+ +-| post- |___|PREG|---+ 'P' 291// |MREG|------ | adder | +----+ 292// +----+ \-------/ 293match postAddMux 294 if postAdd 295 if ffP 296 select postAddMux->type.in($mux) 297 select nusers(port(postAddMux, \Y)) == 2 298 choice <IdString> AB {\A, \B} 299 index <SigSpec> port(postAddMux, AB) === sigP 300 index <SigSpec> port(postAddMux, \Y) === sigC 301 set postAddMuxAB AB 302 optional 303endmatch 304 305code sigC 306 if (postAddMux) 307 sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A); 308endcode 309 310code 311 accept; 312endcode 313 314// ####################### 315 316// Subpattern for matching against input registers, based on knowledge of the 317// 'Q' input. 318subpattern in_dffe 319arg argQ clock 320 321code 322 dff = nullptr; 323 if (argQ.empty()) 324 reject; 325 for (const auto &c : argQ.chunks()) { 326 // Abandon matches when 'Q' is a constant 327 if (!c.wire) 328 reject; 329 // Abandon matches when 'Q' has the keep attribute set 330 if (c.wire->get_bool_attribute(\keep)) 331 reject; 332 // Abandon matches when 'Q' has a non-zero init attribute set 333 // (not supported by DSP48E1) 334 Const init = c.wire->attributes.at(\init, Const()); 335 if (!init.empty()) 336 for (auto b : init.extract(c.offset, c.width)) 337 if (b != State::Sx && b != State::S0) 338 reject; 339 } 340endcode 341 342match ff 343 select ff->type.in($dff, $dffe, $sdff, $sdffe) 344 // DSP48E1 does not support clock inversion 345 select param(ff, \CLK_POLARITY).as_bool() 346 347 // Check that reset value, if present, is fully 0. 348 filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() 349 350 slice offset GetSize(port(ff, \D)) 351 index <SigBit> port(ff, \Q)[offset] === argQ[0] 352 353 // Check that the rest of argQ is present 354 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) 355 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ 356 357 filter clock == SigBit() || port(ff, \CLK) == clock 358endmatch 359 360code argQ 361 SigSpec Q = port(ff, \Q); 362 dff = ff; 363 dffclock = port(ff, \CLK); 364 dffD = argQ; 365 SigSpec D = port(ff, \D); 366 argQ = Q; 367 dffD.replace(argQ, D); 368endcode 369 370// ####################### 371 372// Subpattern for matching against output registers, based on knowledge of the 373// 'D' input. 374// At a high level: 375// (1) Starting from an optional $mux cell that implements clock enable 376// semantics --- one where the given 'D' argument (partially or fully) 377// drives one of its two inputs 378// (2) Starting from, or continuing onto, another optional $mux cell that 379// implements synchronous reset semantics --- one where the given 'D' 380// argument (or the clock enable $mux output) drives one of its two inputs 381// and where the other input is fully zero 382// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the 383// output of the previous clock enable or reset $mux cells) 384subpattern out_dffe 385arg argD argQ clock 386 387code 388 dff = nullptr; 389 for (auto c : argD.chunks()) 390 // Abandon matches when 'D' has the keep attribute set 391 if (c.wire->get_bool_attribute(\keep)) 392 reject; 393endcode 394 395match ff 396 select ff->type.in($dff, $dffe, $sdff, $sdffe) 397 // DSP48E1 does not support clock inversion 398 select param(ff, \CLK_POLARITY).as_bool() 399 400 slice offset GetSize(port(ff, \D)) 401 index <SigBit> port(ff, \D)[offset] === argD[0] 402 403 // Check that the rest of argD is present 404 filter GetSize(port(ff, \D)) >= offset + GetSize(argD) 405 filter port(ff, \D).extract(offset, GetSize(argD)) == argD 406 407 filter clock == SigBit() || port(ff, \CLK) == clock 408endmatch 409 410code argQ 411 SigSpec D = port(ff, \D); 412 SigSpec Q = port(ff, \Q); 413 argQ = argD; 414 argQ.replace(D, Q); 415 416 // Abandon matches when 'Q' has a non-zero init attribute set 417 // (not supported by DSP48E1) 418 for (auto c : argQ.chunks()) { 419 Const init = c.wire->attributes.at(\init, Const()); 420 if (!init.empty()) 421 for (auto b : init.extract(c.offset, c.width)) 422 if (b != State::Sx && b != State::S0) 423 reject; 424 } 425 426 dff = ff; 427 dffQ = argQ; 428 dffclock = port(ff, \CLK); 429endcode 430