aboutsummaryrefslogtreecommitdiffstats
path: root/passes/techmap/shregmap.cc
blob: f20863ba0f7cf34425c186da292021050cf42b19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)

#include "gmock/gmock.h"
#include "gmock/internal/gmock-port.h"

namespace testing {

// TODO(wan@google.com): support using environment variables to
// control the flag values, like what Google Test does.

GMOCK_DEFINE_bool_(catch_leaked_mocks, true,
                   "true iff Google Mock should report leaked mock objects "
                   "as failures.");

GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity,
                     "Controls how verbose Google Mock's output is."
                     "  Valid values:\n"
                     "  info    - prints all messages.\n"
                     "  warning - prints warnings and errors.\n"
                     "  error   - prints errors only.");

namespace internal {

// Parses a string as a command line flag.  The string should have the
// format "--gmock_flag=value".  When def_optional is true, the
// "=value" part can be omitted.
//
// Returns the value of the flag, or NULL if the parsing failed.
static const char* ParseGoogleMockFlagValue(const char* str,
                                            const char* flag,
                                            bool def_optional) {
  // str and flag must not be NULL.
  if (str == NULL || flag == NULL) return NULL;

  // The flag must start with "--gmock_".
  const std::string flag_str = std::string("--gmock_") + flag;
  const size_t flag_len = flag_str.length();
  if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;

  // Skips the flag name.
  const char* flag_end = str + flag_len;

  // When def_optional is true, it's OK to not have a "=value" part.
  if (def_optional && (flag_end[0] == '\0')) {
    return flag_end;
  }

  // If def_optional is true and there are more characters after the
  // flag name, or if def_optional is false, there must be a '=' after
  // the flag name.
  pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  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

struct ShregmapTech
{
	virtual ~ShregmapTech() { }
	virtual bool analyze(vector<int> &taps) = 0;
	virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};

struct ShregmapOptions
{
	int minlen, maxlen;
	int keep_before, keep_after;
	bool zinit, init, params, ffe;
	dict<IdString, pair<IdString, IdString>> ffcells;
	ShregmapTech *tech;

	ShregmapOptions()
	{
		minlen = 2;
		maxlen = 0;
		keep_before = 0;
		keep_after = 0;
		zinit = false;
		init = false;
		params = false;
		ffe = false;
		tech = nullptr;
	}
};

struct ShregmapTechGreenpak4 : ShregmapTech
{
	bool analyze(vector<int> &taps)
	{
		if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
			taps.clear();
			return true;
		}

		if (GetSize(taps) > 2)
			return false;

		if (taps.back() > 16) return false;

		return true;
	}

	bool fixup(Cell *cell, dict<int, SigBit> &taps)
	{
		auto D = cell->getPort("\\D");
		auto C = cell->getPort("\\C");

		auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
		newcell->setPort("\\nRST", State::S1);
		newcell->setPort("\\CLK", C);
		newcell->setPort("\\IN", D);

		int i = 0;
		for (auto tap : taps) {
			newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second);
			newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1);
			i++;
		}

		cell->setParam("\\OUTA_INVERT", 0);
		return false;
	}
};

struct ShregmapWorker
{
	Module *module;
	SigMap sigmap;

	const ShregmapOptions &opts;
	int dff_count, shreg_count;

	pool<Cell*> remove_cells;
	pool<SigBit> remove_init;

	dict<SigBit, bool> sigbit_init;
	dict<SigBit, Cell*> sigbit_chain_next;
	dict<SigBit, Cell*> sigbit_chain_prev;
	pool<SigBit> sigbit_with_non_chain_users;
	pool<Cell*> chain_start_cells;

	void make_sigbit_chain_next_prev()
	{
		for (auto wire : module->wires())
		{
			if (wire->port_output || wire->get_bool_attribute("\\keep")) {
				for (auto bit : sigmap(wire))
					sigbit_with_non_chain_users.insert(bit);
			}

			if (wire->attributes.count("\\init")) {
				SigSpec initsig = sigmap(wire);
				Const initval = wire->attributes.at("\\init");
				for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
					if (initval[i] == State::S0 && !opts.zinit)
						sigbit_init[initsig[i]] = false;
					else if (initval[i] == State::S1)
						sigbit_init[initsig[i]] = true;
			}
		}

		for (auto cell : module->cells())
		{
			if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
			{
				IdString d_port = opts.ffcells.at(cell->type).first;
				IdString q_port = opts.ffcells.at(cell->type).second;

				SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
				SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());

				if (opts.init || sigbit_init.count(q_bit) == 0)
				{
					if (sigbit_chain_next.count(d_bit)) {
						sigbit_with_non_chain_users.insert(d_bit);
					} else
						sigbit_chain_next[d_bit] = cell;

					sigbit_chain_prev[q_bit] = cell;
					continue;
				}
			}

			for (auto conn : cell->connections())
				if (cell->input(conn.first))
					for (auto bit : sigmap(conn.second))
						sigbit_with_non_chain_users.insert(bit);
		}
	}

	void find_chain_start_cells()
	{
		for (auto it : sigbit_chain_next)
		{
			if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first))
				goto start_cell;

			if (sigbit_chain_prev.count(it.first) != 0)
			{
				Cell *c1 = sigbit_chain_prev.at(it.first);
				Cell *c2 = it.second;

				if (c1->type != c2->type)
					goto start_cell;

				if (c1->parameters != c2->parameters)
					goto start_cell;

				IdString d_port = opts.ffcells.at(c1->type).first;
				IdString q_port = opts.ffcells.at(c1->type).second;

				auto c1_conn = c1->connections();
				auto c2_conn = c1->connections();

				c1_conn.erase(d_port);
				c1_conn.erase(q_port);

				c2_conn.erase(d_port);
				c2_conn.erase(q_port);

				if (c1_conn != c2_conn)
					goto start_cell;

				continue;
			}

		start_cell:
			chain_start_cells.insert(it.second);
		}
	}

	vector<Cell*> create_chain(Cell *start_cell)
	{
		vector<Cell*> chain;

		Cell *c = start_cell;
		while (c != nullptr)
		{
			chain.push_back(c);

			IdString q_port = opts.ffcells.at(c->type).second;
			SigBit q_bit = sigmap(c->getPort(q_port).as_bit());

			if (sigbit_chain_next.count(q_bit) == 0)
				break;

			c = sigbit_chain_next.at(q_bit);
			if (chain_start_cells.count(c) != 0)
				break;
		}

		return chain;
	}

	void process_chain(vector<Cell*> &chain)
	{
		if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after)
			return;

		int cursor = opts.keep_before;
		while (cursor < GetSize(chain) - opts.keep_after)
		{
			int depth = GetSize(chain) - opts.keep_after - cursor;

			if (opts.maxlen > 0)
				depth = std::min(opts.maxlen, depth);

			Cell *first_cell = chain[cursor];
			IdString q_port = opts.ffcells.at(first_cell->type).second;
			dict<int, SigBit> taps_dict;

			if (opts.tech)
			{
				vector<SigBit> qbits;
				vector<int> taps;

				for (int i = 0; i < depth; i++)
				{
					Cell *cell = chain[cursor+i];
					auto qbit = sigmap(cell->getPort(q_port));
					qbits.push_back(qbit);

					if (sigbit_with_non_chain_users.count(qbit))
						taps.push_back(i);
				}

				while (depth > 0)
				{
					if (taps.empty() || taps.back() < depth-1)
						taps.push_back(depth-1);

					if (opts.tech->analyze(taps))
						break;

					taps.pop_back();
					depth--;
				}

				depth = 0;
				for (auto tap : taps) {
					taps_dict[tap] = qbits.at(tap);
					log_assert(depth < tap+1);
					depth = tap+1;
				}
			}

			if (depth < 2) {
				cursor++;
				continue;
			}

			Cell *last_cell = chain[cursor+depth-1];

			log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n",
				log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth);

			dff_count += depth;
			shreg_count += 1;

			string shreg_cell_type_str = "$__SHREG";
			if (opts.params) {
				shreg_cell_type_str += "_";
			} else {
				if (first_cell->type[1] != '_')
					shreg_cell_type_str += "_";
				shreg_cell_type_str += first_cell->type.substr(1);
			}

			if (opts.init) {
				vector<State> initval;
				for (int i = depth-1; i >= 0; i--) {
					SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
					if (sigbit_init.count(bit) == 0)
						initval.push_back(State::Sx);
					else if (sigbit_init.at(bit))
						initval.push_back(State::S1);
					else
						initval.push_back(State::S0);
					remove_init.insert(bit);
				}
				first_cell->setParam("\\INIT", initval);
			}

			if (opts.zinit)
				for (int i = depth-1; i >= 0; i--) {
					SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
					remove_init.insert(bit);
				}

			if (opts.params)
			{
				int param_clkpol = -1;
				int param_enpol = 2;

				if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
				if (first_cell->type == "$_DFF_P_") param_clkpol = 1;

				if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
				if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
				if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
				if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;

				log_assert(param_clkpol >= 0);
				first_cell->setParam("\\CLKPOL", param_clkpol);
				if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
			}

			first_cell->type = shreg_cell_type_str;
			first_cell->setPort(q_port, last_cell->getPort(q_port));
			first_cell->setParam("\\DEPTH", depth);

			if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict))
				remove_cells.insert(first_cell);

			for (int i = 1; i < depth; i++)
				remove_cells.insert(chain[cursor+i]);
			cursor += depth;
		}
	}

	void cleanup()
	{
		for (auto cell : remove_cells)
			module->remove(cell);

		for (auto wire : module->wires())
		{
			if (wire->attributes.count("\\init") == 0)
				continue;

			SigSpec initsig = sigmap(wire);
			Const &initval = wire->attributes.at("\\init");

			for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
				if (remove_init.count(initsig[i]))
					initval[i] = State::Sx;

			if (SigSpec(initval).is_fully_undef())
				wire->attributes.erase("\\init");
		}

		remove_cells.clear();
		sigbit_chain_next.clear();
		sigbit_chain_prev.clear();
		chain_start_cells.clear();
	}

	ShregmapWorker(Module *module, const ShregmapOptions &opts) :
			module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
	{
		make_sigbit_chain_next_prev();
		find_chain_start_cells();

		for (auto c : chain_start_cells) {
			vector<Cell*> chain = create_chain(c);
			process_chain(chain);
		}

		cleanup();
	}
};

struct ShregmapPass : public Pass {
	ShregmapPass() : Pass("shregmap", "map shift registers") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    shregmap [options] [selection]\n");
		log("\n");
		log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n");
		log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n");
		log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n");
		log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n");
		log("'techmap' map file to convert those cells to the actual target cells.\n");
		log("\n");
		log("    -minlen N\n");
		log("        minimum length of shift register (default = 2)\n");
		log("        (this is the length after -keep_before and -keep_after)\n");
		log("\n");
		log("    -maxlen N\n");
		log("        maximum length of shift register (default = no limit)\n");
		log("        larger chains will be mapped to multiple shift register instances\n");
		log("\n");
		log("    -keep_before N\n");
		log("        number of DFFs to keep before the shift register (default = 0)\n");
		log("\n");
		log("    -keep_after N\n");
		log("        number of DFFs to keep after the shift register (default = 0)\n");
		log("\n");
		log("    -clkpol pos|neg|any\n");
		log("        limit match to only positive or negative edge clocks. (default = any)\n");
		log("\n");
		log("    -enpol pos|neg|none|any_or_none|any\n");
		log("        limit match to FFs with the specified enable polarity. (default = none)\n");
		log("\n");
		log("    -match <cell_type>[:<d_port_name>:<q_port_name>]\n");
		log("        match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n");
		log("        ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n");
		log("        by default. E.g. the option '-clkpol pos' is just an alias for\n");
		log("        '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n");
		log("\n");
		log("    -params\n");
		log("        instead of encoding the clock and enable polarity in the cell name by\n");
		log("        deriving from the original cell name, simply name all generated cells\n");
		log("        $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n");
		log("        used to denote cells without enable input. The ENPOL parameter is\n");
		log("        omitted when '-enpol none' (or no -enpol option) is passed.\n");
		log("\n");
		log("    -zinit\n");
		log("        assume the shift register is automatically zero-initialized, so it\n");
		log("        becomes legal to merge zero initialized FFs into the shift register.\n");
		log("\n");
		log("    -init\n");
		log("        map initialized registers to the shift reg, add an INIT parameter to\n");
		log("        generated cells with the initialization value. (first bit to shift out\n");
		log("        in LSB position)\n");
		log("\n");
		log("    -tech greenpak4\n");
		log("        map to greenpak4 shift registers.\n");
		log("\n");
	}
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		ShregmapOptions opts;
		string clkpol, enpol;

		log_header(design, "Executing SHREGMAP pass (map shift registers).\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			if (args[argidx] == "-clkpol" && argidx+1 < args.size()) {
				clkpol = args[++argidx];
				continue;
			}
			if (args[argidx] == "-enpol" && argidx+1 < args.size()) {
				enpol = args[++argidx];
				continue;
			}
			if (args[argidx] == "-match" && argidx+1 < args.size()) {
				vector<string> match_args = split_tokens(args[++argidx], ":");
				if (GetSize(match_args) < 2)
					match_args.push_back("D");
				if (GetSize(match_args) < 3)
					match_args.push_back("Q");
				IdString id_cell_type(RTLIL::escape_id(match_args[0]));
				IdString id_d_port_name(RTLIL::escape_id(match_args[1]));
				IdString id_q_port_name(RTLIL::escape_id(match_args[2]));
				opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name);
				continue;
			}
			if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
				opts.minlen = atoi(args[++argidx].c_str());
				continue;
			}
			if (args[argidx] == "-maxlen" && argidx+1 < args.size()) {
				opts.maxlen = atoi(args[++argidx].c_str());
				continue;
			}
			if (args[argidx] == "-keep_before" && argidx+1 < args.size()) {
				opts.keep_before = atoi(args[++argidx].c_str());
				continue;
			}
			if (args[argidx] == "-keep_after" && argidx+1 < args.size()) {
				opts.keep_after = atoi(args[++argidx].c_str());
				continue;
			}
			if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) {
				string tech = args[++argidx];
				if (tech == "greenpak4") {
					clkpol = "pos";
					opts.zinit = true;
					opts.tech = new ShregmapTechGreenpak4;
				} else {
					argidx--;
					break;
				}
				continue;
			}
			if (args[argidx] == "-zinit") {
				opts.zinit = true;
				continue;
			}
			if (args[argidx] == "-init") {
				opts.init = true;
				continue;
			}
			if (args[argidx] == "-params") {
				opts.params = true;
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		if (opts.zinit && opts.init)
			log_cmd_error("Options -zinit and -init are exclusive!\n");

		if (opts.ffcells.empty())
		{
			bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any";
			bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any";

			bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none";
			bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none";
			bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";

			if (clk_pos && en_none)
				opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
			if (clk_neg && en_none)
				opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));

			if (clk_pos && en_pos)
				opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
			if (clk_pos && en_neg)
				opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));

			if (clk_neg && en_pos)
				opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
			if (clk_neg && en_neg)
				opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));

			if (en_pos || en_neg)
				opts.ffe = true;
		}
		else
		{
			if (!clkpol.empty())
				log_cmd_error("Options -clkpol and -match are exclusive!\n");
			if (!enpol.empty())
				log_cmd_error("Options -enpol and -match are exclusive!\n");
			if (opts.params)
				log_cmd_error("Options -params and -match are exclusive!\n");
		}

		int dff_count = 0;
		int shreg_count = 0;

		for (auto module : design->selected_modules()) {
			ShregmapWorker worker(module, opts);
			dff_count += worker.dff_count;
			shreg_count += worker.shreg_count;
		}

		log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count);

		if (opts.tech != nullptr) {
			delete opts.tech;
			opts.tech = nullptr;
		}
	}
} ShregmapPass;

PRIVATE_NAMESPACE_END