Merge pull request #3310 from robinsonb5-PRs/master
[yosys.git] / kernel / macc.h
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
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 int config_width = cell->getParam(ID::CONFIG_WIDTH).as_int();
111 log_assert(GetSize(config_bits) >= config_width);
112
113 int num_bits = 0;
114 if (config_bits[config_cursor++] == State::S1) num_bits |= 1;
115 if (config_bits[config_cursor++] == State::S1) num_bits |= 2;
116 if (config_bits[config_cursor++] == State::S1) num_bits |= 4;
117 if (config_bits[config_cursor++] == State::S1) num_bits |= 8;
118
119 int port_a_cursor = 0;
120 while (port_a_cursor < GetSize(port_a))
121 {
122 log_assert(config_cursor + 2 + 2*num_bits <= config_width);
123
124 port_t this_port;
125 this_port.is_signed = config_bits[config_cursor++] == State::S1;
126 this_port.do_subtract = config_bits[config_cursor++] == State::S1;
127
128 int size_a = 0;
129 for (int i = 0; i < num_bits; i++)
130 if (config_bits[config_cursor++] == State::S1)
131 size_a |= 1 << i;
132
133 this_port.in_a = port_a.extract(port_a_cursor, size_a);
134 port_a_cursor += size_a;
135
136 int size_b = 0;
137 for (int i = 0; i < num_bits; i++)
138 if (config_bits[config_cursor++] == State::S1)
139 size_b |= 1 << i;
140
141 this_port.in_b = port_a.extract(port_a_cursor, size_b);
142 port_a_cursor += size_b;
143
144 if (size_a || size_b)
145 ports.push_back(this_port);
146 }
147
148 log_assert(config_cursor == config_width);
149 log_assert(port_a_cursor == GetSize(port_a));
150 }
151
152 void to_cell(RTLIL::Cell *cell) const
153 {
154 RTLIL::SigSpec port_a;
155 std::vector<RTLIL::State> config_bits;
156 int max_size = 0, num_bits = 0;
157
158 for (auto &port : ports) {
159 max_size = max(max_size, GetSize(port.in_a));
160 max_size = max(max_size, GetSize(port.in_b));
161 }
162
163 while (max_size)
164 num_bits++, max_size /= 2;
165
166 log_assert(num_bits < 16);
167 config_bits.push_back(num_bits & 1 ? State::S1 : State::S0);
168 config_bits.push_back(num_bits & 2 ? State::S1 : State::S0);
169 config_bits.push_back(num_bits & 4 ? State::S1 : State::S0);
170 config_bits.push_back(num_bits & 8 ? State::S1 : State::S0);
171
172 for (auto &port : ports)
173 {
174 if (GetSize(port.in_a) == 0)
175 continue;
176
177 config_bits.push_back(port.is_signed ? State::S1 : State::S0);
178 config_bits.push_back(port.do_subtract ? State::S1 : State::S0);
179
180 int size_a = GetSize(port.in_a);
181 for (int i = 0; i < num_bits; i++)
182 config_bits.push_back(size_a & (1 << i) ? State::S1 : State::S0);
183
184 int size_b = GetSize(port.in_b);
185 for (int i = 0; i < num_bits; i++)
186 config_bits.push_back(size_b & (1 << i) ? State::S1 : State::S0);
187
188 port_a.append(port.in_a);
189 port_a.append(port.in_b);
190 }
191
192 cell->setPort(ID::A, port_a);
193 cell->setPort(ID::B, bit_ports);
194 cell->setParam(ID::CONFIG, config_bits);
195 cell->setParam(ID::CONFIG_WIDTH, GetSize(config_bits));
196 cell->setParam(ID::A_WIDTH, GetSize(port_a));
197 cell->setParam(ID::B_WIDTH, GetSize(bit_ports));
198 }
199
200 bool eval(RTLIL::Const &result) const
201 {
202 for (auto &bit : result.bits)
203 bit = State::S0;
204
205 for (auto &port : ports)
206 {
207 if (!port.in_a.is_fully_const() || !port.in_b.is_fully_const())
208 return false;
209
210 RTLIL::Const summand;
211 if (GetSize(port.in_b) == 0)
212 summand = const_pos(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result));
213 else
214 summand = const_mul(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result));
215
216 if (port.do_subtract)
217 result = const_sub(result, summand, port.is_signed, port.is_signed, GetSize(result));
218 else
219 result = const_add(result, summand, port.is_signed, port.is_signed, GetSize(result));
220 }
221
222 for (auto bit : bit_ports) {
223 if (bit.wire)
224 return false;
225 result = const_add(result, bit.data, false, false, GetSize(result));
226 }
227
228 return true;
229 }
230
231 Macc(RTLIL::Cell *cell = nullptr)
232 {
233 if (cell != nullptr)
234 from_cell(cell);
235 }
236 };
237
238 YOSYS_NAMESPACE_END
239
240 #endif