ad5c50cd6adff1e78c333b7ceb0c9a698ef1316c
[yosys.git] / passes / techmap / shregmap.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/yosys.h"
21 #include "kernel/sigtools.h"
22
23 USING_YOSYS_NAMESPACE
24 PRIVATE_NAMESPACE_BEGIN
25
26 struct ShregmapOptions
27 {
28 std::string clkpol;
29 int minlen, maxlen;
30 int keep_before, keep_after;
31
32 ShregmapOptions()
33 {
34 clkpol = "any";
35 minlen = 2;
36 maxlen = 0;
37 keep_before = 0;
38 keep_after = 0;
39 }
40 };
41
42 struct ShregmapWorker
43 {
44 Module *module;
45 SigMap sigmap;
46
47 const ShregmapOptions &opts;
48 int dff_count, shreg_count;
49
50 // next is set to NULL for sigbits that drive non-DFFs
51 dict<SigBit, Cell*> sigbit_chain_next;
52 dict<SigBit, Cell*> sigbit_chain_prev;
53 pool<Cell*> chain_start_cells;
54
55 void make_sigbit_chain_next_prev()
56 {
57 for (auto wire : module->wires()) {
58 if (!wire->port_output)
59 continue;
60 for (auto bit : sigmap(wire))
61 sigbit_chain_next[bit] = nullptr;
62 }
63
64 for (auto cell : module->cells())
65 {
66 if ((opts.clkpol != "pos" && cell->type == "$_DFF_N_") ||
67 (opts.clkpol != "neg" && cell->type == "$_DFF_P_"))
68 {
69 SigBit d_bit = sigmap(cell->getPort("\\D").as_bit());
70 if (sigbit_chain_next.count(d_bit))
71 sigbit_chain_next[d_bit] = nullptr;
72 else
73 sigbit_chain_next[d_bit] = cell;
74
75 SigBit q_bit = sigmap(cell->getPort("\\Q").as_bit());
76 sigbit_chain_prev[q_bit] = cell;
77 continue;
78 }
79
80 for (auto conn : cell->connections())
81 if (cell->input(conn.first))
82 for (auto bit : sigmap(conn.second))
83 sigbit_chain_next[bit] = nullptr;
84 }
85 }
86
87 void find_chain_start_cells()
88 {
89 for (auto it : sigbit_chain_next)
90 {
91 if (it.second == nullptr)
92 continue;
93
94 if (sigbit_chain_prev.count(it.first) != 0)
95 {
96 Cell *c1 = sigbit_chain_prev.at(it.first);
97 Cell *c2 = it.second;
98
99 if (c1->type != c2->type)
100 goto start_cell;
101
102 if (sigmap(c1->getPort("\\C")) != c2->getPort("\\C"))
103 goto start_cell;
104
105 continue;
106 }
107
108 start_cell:
109 chain_start_cells.insert(it.second);
110 }
111 }
112
113 vector<Cell*> create_chain(Cell *start_cell)
114 {
115 vector<Cell*> chain;
116
117 Cell *c = start_cell;
118 while (c != nullptr)
119 {
120 chain.push_back(c);
121
122 SigBit q_bit = sigmap(c->getPort("\\Q").as_bit());
123
124 if (sigbit_chain_next.count(q_bit) == 0)
125 break;
126
127 c = sigbit_chain_next.at(q_bit);
128 if (chain_start_cells.count(c) != 0)
129 break;
130 }
131
132 return chain;
133 }
134
135 void process_chain(vector<Cell*> &chain)
136 {
137 if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after)
138 return;
139
140 int cursor = opts.keep_before;
141 while (cursor < GetSize(chain) - opts.keep_after)
142 {
143 int depth = GetSize(chain) - opts.keep_after - cursor;
144
145 if (opts.maxlen > 0)
146 depth = std::min(opts.maxlen, depth);
147
148 Cell *first_cell = chain[cursor], *last_cell = chain[cursor+depth-1];
149
150 if (depth < 2)
151 return;
152
153 log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n",
154 log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth);
155
156 first_cell->type = "$__DFF_SHREG_" + first_cell->type.substr(6);
157 first_cell->setPort("\\Q", last_cell->getPort("\\Q"));
158 first_cell->setParam("\\DEPTH", depth);
159
160 for (int i = 1; i < depth; i++)
161 module->remove(chain[cursor+i]);
162 cursor += depth;
163 }
164 }
165
166 ShregmapWorker(Module *module, const ShregmapOptions &opts) :
167 module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
168 {
169 make_sigbit_chain_next_prev();
170
171 find_chain_start_cells();
172
173 for (auto c : chain_start_cells) {
174 vector<Cell*> chain = create_chain(c);
175 process_chain(chain);
176 }
177 }
178 };
179
180 struct ShregmapPass : public Pass {
181 ShregmapPass() : Pass("shregmap", "map shift registers") { }
182 virtual void help()
183 {
184 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
185 log("\n");
186 log(" shregmap [options] [selection]\n");
187 log("\n");
188 log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register.\n");
189 log("primitives. The generated shift register will be of type $__DFF_SHREG_[NP]_ and\n");
190 log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n");
191 log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n");
192 log("'techmap' map file to convert those cells to the actual target cells.\n");
193 log("\n");
194 log(" -minlen N\n");
195 log(" minimum length of shift register (default = 2)\n");
196 log(" (this is the length after -keep_before and -keep_after)\n");
197 log("\n");
198 log(" -maxlen N\n");
199 log(" maximum length of shift register (default = no limit)\n");
200 log(" larger chains will be mapped to multiple shift register instances\n");
201 log("\n");
202 log(" -keep_before N\n");
203 log(" number of DFFs to keep before the shift register (default = 0)\n");
204 log("\n");
205 log(" -keep_after N\n");
206 log(" number of DFFs to keep after the shift register (default = 0)\n");
207 log("\n");
208 log(" -clkpol pos|neg|any\n");
209 log(" limit match to only positive or negative edge clocks. (default = any)\n");
210 log("\n");
211 }
212 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
213 {
214 ShregmapOptions opts;
215
216 log_header("Executing SHREGMAP pass (map shift registers).\n");
217
218 size_t argidx;
219 for (argidx = 1; argidx < args.size(); argidx++)
220 {
221 if (args[argidx] == "-clkpol" && argidx+1 < args.size()) {
222 opts.clkpol = args[++argidx];
223 continue;
224 }
225 if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
226 opts.minlen = atoi(args[++argidx].c_str());
227 continue;
228 }
229 if (args[argidx] == "-maxlen" && argidx+1 < args.size()) {
230 opts.maxlen = atoi(args[++argidx].c_str());
231 continue;
232 }
233 if (args[argidx] == "-keep_before" && argidx+1 < args.size()) {
234 opts.keep_before = atoi(args[++argidx].c_str());
235 continue;
236 }
237 if (args[argidx] == "-keep_after" && argidx+1 < args.size()) {
238 opts.keep_after = atoi(args[++argidx].c_str());
239 continue;
240 }
241 break;
242 }
243 extra_args(args, argidx, design);
244
245 if (opts.clkpol != "pos" && opts.clkpol != "neg" && opts.clkpol != "any")
246 log_cmd_error("Invalid value for -clkpol: %s\n", opts.clkpol.c_str());
247
248 int dff_count = 0;
249 int shreg_count = 0;
250
251 for (auto module : design->selected_modules()) {
252 ShregmapWorker worker(module, opts);
253 dff_count += worker.dff_count;
254 shreg_count += worker.shreg_count;
255 }
256
257 log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count);
258 }
259 } ShregmapPass;
260
261 PRIVATE_NAMESPACE_END