<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  <NAME> <<EMAIL>>
 *            (C) 2019  <NAME>    <<EMAIL>>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

// for peepopt_pm
bool did_something;

#include "passes/pmgen/xilinx_srl_pm.h"
#include "passes/pmgen/ice40_dsp_pm.h"
#include "passes/pmgen/peepopt_pm.h"

void run_fixed(xilinx_srl_pm &pm)
{
	auto &st = pm.st_fixed;
	auto &ud = pm.ud_fixed;
	auto param_def = [&ud](Cell *cell, IdString param) {
		auto def = ud.default_params.at(std::make_pair(cell->type,param));
		return cell->parameters.at(param, def);
	};

	log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));

	auto first_cell = ud.longest_chain.back();

	SigSpec initval;
	for (auto cell : ud.longest_chain) {
		log_debug("    %s\n", log_id(cell));
		if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) {
			SigBit Q = cell->getPort(ID(Q));
			log_assert(Q.wire);
			auto it = Q.wire->attributes.find(ID(init));
			if (it != Q.wire->attributes.end()) {
				auto &i = it->second[Q.offset];
				initval.append(i);
				i = State::Sx;
			}
			else
				initval.append(State::Sx);
		}
		else if (cell->type.in(ID(FDRE), ID(FDRE_1)))
			initval.append(param_def(cell, ID(INIT)));
		else
			log_abort();
		if (cell != first_cell)
			pm.autoremove(cell);
	}

	auto last_cell = ud.longest_chain.front();
	Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
	pm.module->swap_names(c, first_cell);

	if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) {
		c->setParam(ID(DEPTH), GetSize(ud.longest_chain));
		c->setParam(ID(INIT), initval.as_const());
		if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			c->setParam(ID(CLKPOL), 1);
		else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1)))
			c->setParam(ID(CLKPOL), 0);
		else if (first_cell->type.in(ID(FDRE)))
			c->setParam(ID(CLKPOL), param_def(first_cell, ID(IS_C_INVERTED)).as_bool() ? 0 : 1);
		else
			log_abort();
		if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
			c->setParam(ID(ENPOL), 1);
		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
			c->setParam(ID(ENPOL), 0);
		else
			c->setParam(ID(ENPOL), 2);

		c->setPort(ID(C), first_cell->getPort(ID(C)));
		c->setPort(ID(D), first_cell->getPort(ID(D)));
		c->setPort(ID(Q), last_cell->getPort(ID(Q)));
		c->setPort(ID(L), GetSize(ud.longest_chain)-1);
		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
			c->setPort(ID(E), State::S1);
		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			c->setPort(ID(E), first_cell->getPort(ID(E)));
		else if (first_cell->type.in(ID(FDRE), ID(FDRE_1)))
			c->setPort(ID(E), first_cell->getPort(ID(CE)));
		else
			log_abort();
	}
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

void run_variable(xilinx_srl_pm &pm)
{
	auto &st = pm.st_variable;
	auto &ud = pm.ud_variable;

	log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type));

	auto first_cell = ud.chain.back().first;
	auto first_slice = ud.chain.back().second;

	SigSpec initval;
	for (const auto &i : ud.chain) {
		auto cell = i.first;
		auto slice = i.second;
		log_debug("    %s\n", log_id(cell));
		if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
			SigBit Q = cell->getPort(ID(Q))[slice];
			log_assert(Q.wire);
			auto it = Q.wire->attributes.find(ID(init));
			if (it != Q.wire->attributes.end()) {
				auto &i = it->second[Q.offset];
				initval.append(i);
				i = State::Sx;
			}
			else
				initval.append(State::Sx);
		}
		else
			log_abort();
		if (cell != first_cell)
			cell->connections_.at(ID(Q))[slice] = pm.module->addWire(NEW_ID);
	}
	pm.autoremove(st.shiftx);

	auto last_cell = ud.chain.front().first;
	auto last_slice = ud.chain.front().second;

	Cell *c = last_cell;
	if (c->type.in(ID($dff), ID($dffe))) {
		auto &Q = last_cell->connections_.at(ID(Q));
		Q = Q[last_slice];
		last_cell->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]);
	}

	if (c->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
		Const clkpol, enpol;
		if (c->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			clkpol = 1;
		else if (c->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_)))
			clkpol = 0;
		else if (c->type.in(ID($dff), ID($dffe))) {
			clkpol = c->getParam(ID(CLK_POLARITY));
			c->setPort(ID(C), c->getPort(ID(CLK)));
			c->unsetPort(ID(CLK));
		}
		else
			log_abort();
		if (c->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
			enpol = 1;
		else if (c->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
			enpol = 0;
		else if (c->type.in(ID($dffe))) {
			enpol = c->getParam(ID(EN_POLARITY));
			c->setPort(ID(E), c->getPort(ID(EN)));
			c->unsetPort(ID(EN));
		}
		else
			enpol = 2;
		c->parameters.clear();
		c->setParam(ID(DEPTH), GetSize(ud.chain));
		c->setParam(ID(INIT), initval.as_const());
		c->setParam(ID(CLKPOL), clkpol);
		c->setParam(ID(ENPOL), enpol);
		if (c->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff)))
			c->setPort(ID(E), State::S1);
		c->setPort(ID(L), st.shiftx->getPort(ID(B)));
		c->setPort(ID(Q), st.shiftx->getPort(ID(Y)));
		c->type = ID($__XILINX_SHREG_);
	}
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

struct XilinxSrlPass : public Pass {
	XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    xilinx_srl [options] [selection]\n");
		log("\n");
		log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n");<commit_msg>Create new $__XILINX_SHREG_ cell for variable length too<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  <NAME> <<EMAIL>>
 *            (C) 2019  <NAME>    <<EMAIL>>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

// for peepopt_pm
bool did_something;

#include "passes/pmgen/xilinx_srl_pm.h"
#include "passes/pmgen/ice40_dsp_pm.h"
#include "passes/pmgen/peepopt_pm.h"

void run_fixed(xilinx_srl_pm &pm)
{
	auto &st = pm.st_fixed;
	auto &ud = pm.ud_fixed;
	auto param_def = [&ud](Cell *cell, IdString param) {
		auto def = ud.default_params.at(std::make_pair(cell->type,param));
		return cell->parameters.at(param, def);
	};

	log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));

	auto first_cell = ud.longest_chain.back();

	SigSpec initval;
	for (auto cell : ud.longest_chain) {
		log_debug("    %s\n", log_id(cell));
		if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) {
			SigBit Q = cell->getPort(ID(Q));
			log_assert(Q.wire);
			auto it = Q.wire->attributes.find(ID(init));
			if (it != Q.wire->attributes.end()) {
				auto &i = it->second[Q.offset];
				initval.append(i);
				i = State::Sx;
			}
			else
				initval.append(State::Sx);
		}
		else if (cell->type.in(ID(FDRE), ID(FDRE_1)))
			initval.append(param_def(cell, ID(INIT)));
		else
			log_abort();
		if (cell != first_cell)
			pm.autoremove(cell);
	}

	auto last_cell = ud.longest_chain.front();
	Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
	pm.module->swap_names(c, first_cell);

	if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) {
		c->setParam(ID(DEPTH), GetSize(ud.longest_chain));
		c->setParam(ID(INIT), initval.as_const());
		if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			c->setParam(ID(CLKPOL), 1);
		else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1)))
			c->setParam(ID(CLKPOL), 0);
		else if (first_cell->type.in(ID(FDRE)))
			c->setParam(ID(CLKPOL), param_def(first_cell, ID(IS_C_INVERTED)).as_bool() ? 0 : 1);
		else
			log_abort();
		if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
			c->setParam(ID(ENPOL), 1);
		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
			c->setParam(ID(ENPOL), 0);
		else
			c->setParam(ID(ENPOL), 2);

		c->setPort(ID(C), first_cell->getPort(ID(C)));
		c->setPort(ID(D), first_cell->getPort(ID(D)));
		c->setPort(ID(Q), last_cell->getPort(ID(Q)));
		c->setPort(ID(L), GetSize(ud.longest_chain)-1);
		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
			c->setPort(ID(E), State::S1);
		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			c->setPort(ID(E), first_cell->getPort(ID(E)));
		else if (first_cell->type.in(ID(FDRE), ID(FDRE_1)))
			c->setPort(ID(E), first_cell->getPort(ID(CE)));
		else
			log_abort();
	}
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

void run_variable(xilinx_srl_pm &pm)
{
	auto &st = pm.st_variable;
	auto &ud = pm.ud_variable;

	log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type));

	auto first_cell = ud.chain.back().first;
	auto first_slice = ud.chain.back().second;

	SigSpec initval;
	for (const auto &i : ud.chain) {
		auto cell = i.first;
		auto slice = i.second;
		log_debug("    %s\n", log_id(cell));
		if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
			SigBit Q = cell->getPort(ID(Q))[slice];
			log_assert(Q.wire);
			auto it = Q.wire->attributes.find(ID(init));
			if (it != Q.wire->attributes.end()) {
				auto &i = it->second[Q.offset];
				initval.append(i);
				i = State::Sx;
			}
			else
				initval.append(State::Sx);
		}
		else
			log_abort();
		if (cell != first_cell)
			cell->connections_.at(ID(Q))[slice] = pm.module->addWire(NEW_ID);
	}
	pm.autoremove(st.shiftx);

	Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
	pm.module->swap_names(c, first_cell);

	if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
		c->setParam(ID(DEPTH), GetSize(ud.chain));
		c->setParam(ID(INIT), initval.as_const());
		Const clkpol, enpol;
		if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			clkpol = 1;
		else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_)))
			clkpol = 0;
		else if (first_cell->type.in(ID($dff), ID($dffe)))
			clkpol = first_cell->getParam(ID(CLK_POLARITY));
		else
			log_abort();
		if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
			enpol = 1;
		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
			enpol = 0;
		else if (first_cell->type.in(ID($dffe)))
			enpol = first_cell->getParam(ID(EN_POLARITY));
		else
			enpol = 2;
		c->setParam(ID(CLKPOL), clkpol);
		c->setParam(ID(ENPOL), enpol);

		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			c->setPort(ID(C), first_cell->getPort(ID(C)));
		else if (first_cell->type.in(ID($dff), ID($dffe)))
			c->setPort(ID(C), first_cell->getPort(ID(CLK)));
		else
			log_abort();
		c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]);
		c->setPort(ID(Q), st.shiftx->getPort(ID(Y)));
		c->setPort(ID(L), st.shiftx->getPort(ID(B)));
		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff)))
			c->setPort(ID(E), State::S1);
		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
			c->setPort(ID(E), first_cell->getPort(ID(E)));
		else if (first_cell->type.in(ID($dffe)))
			c->setPort(ID(E), first_cell->getPort(ID(EN)));
		else
			log_abort();
	}
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

struct XilinxSrlPass : public Pass {
	XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    xilinx_srl [options] [selection]\n");
		log("\n");
		log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n");
{'fraction_non_alphanumeric': 0.20591394000143196, 'fraction_numerical': 0.004152645521586597, 'mean_word_length': 4.374374759522893, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 185, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1330424', 'n_tokens_mistral': 6397, 'n_tokens_neox': 5918, 'n_words': 1072}