| pattern fixed | |
| state <IdString> clk_port en_port | |
| udata <vector<Cell*>> chain longest_chain | |
| udata <pool<Cell*>> non_first_cells | |
| udata <int> 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, State::S0).as_bool() | |
| select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).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, State::S0).as_bool() | |
| select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).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 <IdString> next->type === first->type | |
| index <SigBit> 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, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool() | |
| filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool() | |
| filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).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 <IdString> next->type === chain.back()->type | |
| index <SigBit> 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, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool() | |
| filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool() | |
| filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).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 <IdString> clk_port en_port | |
| state <int> shiftx_width | |
| state <int> slice | |
| udata <int> minlen | |
| udata <vector<pair<Cell*,int>>> chain | |
| udata <pool<SigBit>> 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 <SigBit> 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 <IdString> next->type === chain.back().first->type | |
| index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second] | |
| index <SigBit> 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 |