pattern fixed state clk_port en_port udata > chain longest_chain udata > non_first_cells udata minlen code non_first_cells.clear(); subpattern(setup); endcode match first select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) select !first->has_keep_attr() select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED).as_bool() select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED).as_bool() select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero() filter !non_first_cells.count(first) generate SigSpec C = module->addWire(NEW_ID); SigSpec D = module->addWire(NEW_ID); SigSpec Q = module->addWire(NEW_ID); auto r = rng(8); Cell* cell; switch (r) { case 0: case 1: cell = module->addCell(NEW_ID, \FDRE); cell->setPort(\C, C); cell->setPort(\D, D); cell->setPort(\Q, Q); cell->setPort(\CE, module->addWire(NEW_ID)); if (r & 1) cell->setPort(\R, module->addWire(NEW_ID)); else { if (rng(2) == 0) cell->setPort(\R, State::S0); } break; case 2: case 3: cell = module->addDffGate(NEW_ID, C, D, Q, r & 1); break; case 4: case 5: case 6: case 7: cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2); break; default: log_abort(); } endmatch code clk_port en_port if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)) clk_port = \C; else log_abort(); if (first->type.in($_DFF_N_, $_DFF_P_)) en_port = IdString(); else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) en_port = \E; else if (first->type.in(\FDRE, \FDRE_1)) en_port = \CE; else log_abort(); longest_chain.clear(); chain.push_back(first); subpattern(tail); finally chain.pop_back(); log_assert(chain.empty()); if (GetSize(longest_chain) >= minlen) accept; endcode // ------------------------------------------------------------------ subpattern setup arg clk_port arg en_port match first select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) select !first->has_keep_attr() select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED).as_bool() select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED).as_bool() select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero() endmatch code clk_port en_port if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)) clk_port = \C; else log_abort(); if (first->type.in($_DFF_N_, $_DFF_P_)) en_port = IdString(); else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) en_port = \E; else if (first->type.in(\FDRE, \FDRE_1)) en_port = \CE; else log_abort(); endcode match next select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) select !next->has_keep_attr() select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep) select nusers(port(next, \Q)) == 2 index next->type === first->type index port(next, \Q) === port(first, \D) filter port(next, clk_port) == port(first, clk_port) filter en_port == IdString() || port(next, en_port) == port(first, en_port) filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED).as_bool() == param(first, \IS_C_INVERTED).as_bool() filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED).as_bool() == param(first, \IS_D_INVERTED).as_bool() filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED).as_bool() == param(first, \IS_R_INVERTED).as_bool() filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero() endmatch code non_first_cells.insert(next); endcode // ------------------------------------------------------------------ subpattern tail arg first arg clk_port arg en_port match next semioptional select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) select !next->has_keep_attr() select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep) select nusers(port(next, \Q)) == 2 index next->type === chain.back()->type index port(next, \Q) === port(chain.back(), \D) filter port(next, clk_port) == port(first, clk_port) filter en_port == IdString() || port(next, en_port) == port(first, en_port) filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED).as_bool() == param(first, \IS_C_INVERTED).as_bool() filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED).as_bool() == param(first, \IS_D_INVERTED).as_bool() filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED).as_bool() == param(first, \IS_R_INVERTED).as_bool() filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero() generate Cell *cell = module->addCell(NEW_ID, chain.back()->type); cell->setPort(\C, chain.back()->getPort(\C)); cell->setPort(\D, module->addWire(NEW_ID)); cell->setPort(\Q, chain.back()->getPort(\D)); if (cell->type == \FDRE) { if (rng(2) == 0) cell->setPort(\R, port(chain.back(), \R, State::S0)); cell->setPort(\CE, chain.back()->getPort(\CE)); } else if (cell->type.begins_with("$_DFFE_")) cell->setPort(\E, chain.back()->getPort(\E)); endmatch code if (next) { chain.push_back(next); subpattern(tail); } else { if (GetSize(chain) > GetSize(longest_chain)) longest_chain = chain; } finally if (next) chain.pop_back(); endcode // ----------- pattern variable state clk_port en_port state shiftx_width state slice udata minlen udata >> chain udata > chain_bits code chain_bits.clear(); endcode match shiftx select shiftx->type.in($shiftx) select !shiftx->has_keep_attr() select param(shiftx, \Y_WIDTH).as_int() == 1 filter param(shiftx, \A_WIDTH).as_int() >= minlen generate minlen = 3; module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID)); endmatch code shiftx_width shiftx_width = param(shiftx, \A_WIDTH).as_int(); endcode match first select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe) select !first->has_keep_attr() select port(first, \Q)[0].wire && !port(first, \Q)[0].wire->get_bool_attribute(\keep) slice idx GetSize(port(first, \Q)) select nusers(port(first, \Q)[idx]) <= 2 index port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1] set slice idx generate SigSpec C = module->addWire(NEW_ID); auto WIDTH = rng(3)+1; SigSpec D = module->addWire(NEW_ID, WIDTH); SigSpec Q = module->addWire(NEW_ID, WIDTH); auto r = rng(8); Cell *cell = nullptr; switch (r) { case 0: case 1: cell = module->addDff(NEW_ID, C, D, Q, r & 1); break; case 2: case 3: case 4: case 5: //cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4); //break; case 6: case 7: WIDTH = 1; cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1); break; default: log_abort(); } shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)]; endmatch code clk_port en_port if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) clk_port = \C; else if (first->type.in($dff, $dffe)) clk_port = \CLK; else log_abort(); if (first->type.in($_DFF_N_, $_DFF_P_, $dff)) en_port = IdString(); else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) en_port = \E; else if (first->type.in($dffe)) en_port = \EN; else log_abort(); chain_bits.insert(port(first, \Q)[slice]); chain.emplace_back(first, slice); subpattern(tail); finally if (GetSize(chain) == shiftx_width) accept; chain.clear(); endcode // ------------------------------------------------------------------ subpattern tail arg first arg shiftx arg shiftx_width arg slice arg clk_port arg en_port match next semioptional select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe) select !next->has_keep_attr() select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep) slice idx GetSize(port(next, \Q)) select nusers(port(next, \Q)[idx]) <= 3 index next->type === chain.back().first->type index port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second] index port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)] filter port(next, clk_port) == port(first, clk_port) filter en_port == IdString() || port(next, en_port) == port(first, en_port) filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool() filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool() filter !chain_bits.count(port(next, \D)[idx]) set slice idx generate if (GetSize(chain) < shiftx_width) { auto back = chain.back().first; auto slice = chain.back().second; if (back->type.in($dff, $dffe)) { auto WIDTH = GetSize(port(back, \D)); if (rng(2) == 0 && slice < WIDTH-1) { auto new_slice = slice + rng(WIDTH-1-slice); back->connections_.at(\D)[slice] = port(back, \Q)[new_slice]; } else { auto D = module->addWire(NEW_ID, WIDTH); if (back->type == $dff) module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool()); else if (back->type == $dffe) module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool()); else log_abort(); } } else if (back->type.begins_with("$_DFF_")) { Cell *cell = module->addCell(NEW_ID, back->type); cell->setPort(\C, back->getPort(\C)); cell->setPort(\D, module->addWire(NEW_ID)); cell->setPort(\Q, back->getPort(\D)); } else log_abort(); shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice]; } endmatch code if (next) { chain_bits.insert(port(next, \Q)[slice]); chain.emplace_back(next, slice); if (GetSize(chain) < shiftx_width) subpattern(tail); } endcode