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).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 <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).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 <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).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 <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
