Merge $macc cells in alumacc pass
[yosys.git] / passes / techmap / alumacc.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 #include "kernel/macc.h"
23
24 struct AlumaccWorker
25 {
26 RTLIL::Module *module;
27 SigMap sigmap;
28
29 struct maccnode_t {
30 Macc macc;
31 RTLIL::Cell *cell;
32 RTLIL::SigSpec y;
33 int users;
34 };
35
36 std::map<RTLIL::SigBit, int> bit_users;
37 std::map<RTLIL::SigSpec, maccnode_t*> sig_macc;
38
39 AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) { }
40
41 void count_bit_users()
42 {
43 for (auto port : module->ports)
44 for (auto bit : sigmap(module->wire(port)))
45 bit_users[bit]++;
46
47 for (auto cell : module->cells())
48 for (auto &conn : cell->connections())
49 for (auto bit : sigmap(conn.second))
50 bit_users[bit]++;
51 }
52
53 void extract_macc()
54 {
55 for (auto cell : module->selected_cells())
56 {
57 if (!cell->type.in("$pos", "$neg", "$add", "$sub", "$mul"))
58 continue;
59
60 log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type));
61
62 maccnode_t *n = new maccnode_t;
63 Macc::port_t new_port;
64
65 n->cell = cell;
66 n->y = sigmap(cell->getPort("\\Y"));
67 n->users = 0;
68
69 for (auto bit : n->y)
70 n->users = std::max(n->users, bit_users.at(bit) - 1);
71
72 if (cell->type.in("$pos", "$neg"))
73 {
74 new_port.in_a = sigmap(cell->getPort("\\A"));
75 new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
76 new_port.do_subtract = cell->type == "$neg";
77 n->macc.ports.push_back(new_port);
78 }
79
80 if (cell->type.in("$add", "$sub"))
81 {
82 new_port.in_a = sigmap(cell->getPort("\\A"));
83 new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
84 new_port.do_subtract = false;
85 n->macc.ports.push_back(new_port);
86
87 new_port.in_a = sigmap(cell->getPort("\\B"));
88 new_port.is_signed = cell->getParam("\\B_SIGNED").as_bool();
89 new_port.do_subtract = cell->type == "$sub";
90 n->macc.ports.push_back(new_port);
91 }
92
93 if (cell->type.in("$mul"))
94 {
95 new_port.in_a = sigmap(cell->getPort("\\A"));
96 new_port.in_b = sigmap(cell->getPort("\\B"));
97 new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
98 new_port.do_subtract = false;
99 n->macc.ports.push_back(new_port);
100 }
101
102 log_assert(sig_macc.count(n->y) == 0);
103 sig_macc[n->y] = n;
104 }
105 }
106
107 void merge_macc()
108 {
109 while (1)
110 {
111 std::set<maccnode_t*> delete_nodes;
112
113 for (auto &it : sig_macc)
114 {
115 auto n = it.second;
116
117 if (delete_nodes.count(n))
118 continue;
119
120 for (int i = 0; i < SIZE(n->macc.ports); i++)
121 {
122 auto &port = n->macc.ports[i];
123
124 if (SIZE(port.in_b) > 0 || sig_macc.count(port.in_a) == 0)
125 continue;
126
127 auto other_n = sig_macc.at(port.in_a);
128
129 if (other_n->users > 1)
130 continue;
131
132 if (SIZE(other_n->y) != SIZE(n->y))
133 continue;
134
135 log(" merging $macc model for %s into %s.\n", log_id(other_n->cell), log_id(n->cell));
136
137 bool do_subtract = port.do_subtract;
138 for (int j = 0; j < SIZE(other_n->macc.ports); j++) {
139 if (do_subtract)
140 other_n->macc.ports[j].do_subtract = !other_n->macc.ports[j].do_subtract;
141 if (j == 0)
142 n->macc.ports[i--] = other_n->macc.ports[j];
143 else
144 n->macc.ports.push_back(other_n->macc.ports[j]);
145 }
146
147 delete_nodes.insert(other_n);
148 }
149 }
150
151 if (delete_nodes.empty())
152 break;
153
154 for (auto n : delete_nodes) {
155 sig_macc.erase(n->y);
156 delete n;
157 }
158 }
159 }
160
161 void replace_macc()
162 {
163 for (auto &it : sig_macc)
164 {
165 auto n = it.second;
166 auto cell = module->addCell(NEW_ID, "$macc");
167 n->macc.to_cell(cell);
168 cell->setPort("\\Y", n->y);
169 cell->fixup_parameters();
170 module->remove(n->cell);
171 delete n;
172 }
173
174 sig_macc.clear();
175 }
176
177 void run()
178 {
179 log("Extracting $alu and $macc cells in module %s:\n", log_id(module));
180
181 count_bit_users();
182 extract_macc();
183 merge_macc();
184 replace_macc();
185 }
186 };
187
188 struct AlumaccPass : public Pass {
189 AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { }
190 virtual void help()
191 {
192 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
193 log("\n");
194 log(" alumacc [selection]\n");
195 log("\n");
196 log("This pass translates arithmetic operations $add, $mul, $lt, etc. to $alu and\n");
197 log("$macc cells.\n");
198 log("\n");
199 }
200 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
201 {
202 log_header("Executing ALUMACC pass (create $alu and $macc cells).\n");
203
204 size_t argidx;
205 for (argidx = 1; argidx < args.size(); argidx++) {
206 // if (args[argidx] == "-foobar") {
207 // foobar_mode = true;
208 // continue;
209 // }
210 break;
211 }
212 extra_args(args, argidx, design);
213
214 for (auto mod : design->selected_modules())
215 if (!mod->has_processes_warn()) {
216 AlumaccWorker worker(mod);
217 worker.run();
218 }
219 }
220 } AlumaccPass;
221