Merge branch 'master' of github.com:YosysHQ/yosys into clifford/pmgen
[yosys.git] / kernel / macc.h
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 #ifndef MACC_H
21 #define MACC_H
22
23 #include "kernel/yosys.h"
24
25 YOSYS_NAMESPACE_BEGIN
26
27 struct Macc
28 {
29 struct port_t {
30 RTLIL::SigSpec in_a, in_b;
31 bool is_signed, do_subtract;
32 };
33
34 std::vector<port_t> ports;
35 RTLIL::SigSpec bit_ports;
36
37 void optimize(int width)
38 {
39 std::vector<port_t> new_ports;
40 RTLIL::SigSpec new_bit_ports;
41 RTLIL::Const off(0, width);
42
43 for (auto &port : ports)
44 {
45 if (GetSize(port.in_a) == 0 && GetSize(port.in_b) == 0)
46 continue;
47
48 if (GetSize(port.in_a) < GetSize(port.in_b))
49 std::swap(port.in_a, port.in_b);
50
51 if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) {
52 bit_ports.append(port.in_a);
53 continue;
54 }
55
56 if (port.in_a.is_fully_const() && port.in_b.is_fully_const()) {
57 RTLIL::Const v = port.in_a.as_const();
58 if (GetSize(port.in_b))
59 v = const_mul(v, port.in_b.as_const(), port.is_signed, port.is_signed, width);
60 if (port.do_subtract)
61 off = const_sub(off, v, port.is_signed, port.is_signed, width);
62 else
63 off = const_add(off, v, port.is_signed, port.is_signed, width);
64 continue;
65 }
66
67 if (port.is_signed) {
68 while (GetSize(port.in_a) > 1 && port.in_a[GetSize(port.in_a)-1] == port.in_a[GetSize(port.in_a)-2])
69 port.in_a.remove(GetSize(port.in_a)-1);
70 while (GetSize(port.in_b) > 1 && port.in_b[GetSize(port.in_b)-1] == port.in_b[GetSize(port.in_b)-2])
71 port.in_b.remove(GetSize(port.in_b)-1);
72 } else {
73 while (GetSize(port.in_a) > 1 && port.in_a[GetSize(port.in_a)-1] == State::S0)
74 port.in_a.remove(GetSize(port.in_a)-1);
75 while (GetSize(port.in_b) > 1 && port.in_b[GetSize(port.in_b)-1] == State::S0)
76 port.in_b.remove(GetSize(port.in_b)-1);
77 }
78
79 new_ports.push_back(port);
80 }
81
82 for (auto &bit : bit_ports)
83 if (bit == State::S1)
84 off = const_add(off, RTLIL::Const(1, width), false, false, width);
85 else if (bit != State::S0)
86 new_bit_ports.append(bit);
87
88 if (off.as_bool()) {
89 port_t port;
90 port.in_a = off;
91 port.is_signed = false;
92 port.do_subtract = false;
93 new_ports.push_back(port);
94 }
95
96 new_ports.swap(ports);
97 bit_ports = new_bit_ports;
98 }
99
100 void from_cell(RTLIL::Cell *cell)
101 {
102 RTLIL::SigSpec port_a = cell->getPort(ID::A);
103
104 ports.clear();
105 bit_ports = cell->getPort(ID::B);
106
107 std::vector<RTLIL::State> config_bits = cell->getParam(ID(CONFIG)).bits;
108 int config_cursor = 0;
109
110 #ifndef NDEBUG
111 int config_width = cell->getParam(ID(CONFIG_WIDTH)).as_int();
112 log_assert(GetSize(config_bits) >= config_width);
113 #endif
114
115 int num_bits = 0;
116 if (config_bits[config_cursor++] == State::S1) num_bits |= 1;
117 if (config_bits[config_cursor++] == State::S1) num_bits |= 2;
118 if (config_bits[config_cursor++] == State::S1) num_bits |= 4;
119 if (config_bits[config_cursor++] == State::S1) num_bits |= 8;
120
121 int port_a_cursor = 0;
122 while (port_a_cursor < GetSize(port_a))
123 {
124 log_assert(config_cursor + 2 + 2*num_bits <= config_width);
125
126 port_t this_port;
127 this_port.is_signed = config_bits[config_cursor++] == State::S1;
128 this_port.do_subtract = config_bits[config_cursor++] == State::S1;
129
130 int size_a = 0;
131 for (int i = 0; i < num_bits; i++)
132 if (config_bits[config_cursor++] == State::S1)
133 size_a |= 1 << i;
134
135 this_port.in_a = port_a.extract(port_a_cursor, size_a);
136 port_a_cursor += size_a;
137
138 int size_b = 0;
139 for (int i = 0; i < num_bits; i++)
140 if (config_bits[config_cursor++] == State::S1)
141 size_b |= 1 << i;
142
143 this_port.in_b = port_a.extract(port_a_cursor, size_b);
144 port_a_cursor += size_b;
145
146 if (size_a || size_b)
147 ports.push_back(this_port);
148 }
149
150 log_assert(config_cursor == config_width);
151 log_assert(port_a_cursor == GetSize(port_a));
152 }
153
154 void to_cell(RTLIL::Cell *cell) const
155 {
156 RTLIL::SigSpec port_a;
157 std::vector<RTLIL::State> config_bits;
158 int max_size = 0, num_bits = 0;
159
160 for (auto &port : ports) {
161 max_size = max(max_size, GetSize(port.in_a));
162 max_size = max(max_size, GetSize(port.in_b));
163 }
164
165 while (max_size)
166 num_bits++, max_size /= 2;
167
168 log_assert(num_bits < 16);
169 config_bits.push_back(num_bits & 1 ? State::S1 : State::S0);
170 config_bits.push_back(num_bits & 2 ? State::S1 : State::S0);
171 config_bits.push_back(num_bits & 4 ? State::S1 : State::S0);
172 config_bits.push_back(num_bits & 8 ? State::S1 : State::S0);
173
174 for (auto &port : ports)
175 {
176 if (GetSize(port.in_a) == 0)
177 continue;
178
179 config_bits.push_back(port.is_signed ? State::S1 : State::S0);
180 config_bits.push_back(port.do_subtract ? State::S1 : State::S0);
181
182 int size_a = GetSize(port.in_a);
183 for (int i = 0; i < num_bits; i++)
184 config_bits.push_back(size_a & (1 << i) ? State::S1 : State::S0);
185
186 int size_b = GetSize(port.in_b);
187 for (int i = 0; i < num_bits; i++)
188 config_bits.push_back(size_b & (1 << i) ? State::S1 : State::S0);
189
190 port_a.append(port.in_a);
191 port_a.append(port.in_b);
192 }
193
194 cell->setPort(ID::A, port_a);
195 cell->setPort(ID::B, bit_ports);
196 cell->setParam(ID(CONFIG), config_bits);
197 cell->setParam(ID(CONFIG_WIDTH), GetSize(config_bits));
198 cell->setParam(ID(A_WIDTH), GetSize(port_a));
199 cell->setParam(ID(B_WIDTH), GetSize(bit_ports));
200 }
201
202 bool eval(RTLIL::Const &result) const
203 {
204 for (auto &bit : result.bits)
205 bit = State::S0;
206
207 for (auto &port : ports)
208 {
209 if (!port.in_a.is_fully_const() || !port.in_b.is_fully_const())
210 return false;
211
212 RTLIL::Const summand;
213 if (GetSize(port.in_b) == 0)
214 summand = const_pos(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result));
215 else
216 summand = const_mul(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result));
217
218 if (port.do_subtract)
219 result = const_sub(result, summand, port.is_signed, port.is_signed, GetSize(result));
220 else
221 result = const_add(result, summand, port.is_signed, port.is_signed, GetSize(result));
222 }
223
224 for (auto bit : bit_ports) {
225 if (bit.wire)
226 return false;
227 result = const_add(result, bit.data, false, false, GetSize(result));
228 }
229
230 return true;
231 }
232
233 Macc(RTLIL::Cell *cell = nullptr)
234 {
235 if (cell != nullptr)
236 from_cell(cell);
237 }
238 };
239
240 YOSYS_NAMESPACE_END
241
242 #endif