Progress in memory_bram
[yosys.git] / passes / memory / memory_bram.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
22 USING_YOSYS_NAMESPACE
23 PRIVATE_NAMESPACE_BEGIN
24
25 struct rules_t
26 {
27 struct portinfo_t {
28 int group, index;
29 int wrmode, enable, transp, clocks, clkpol;
30 };
31
32 struct bram_t {
33 IdString name;
34 int groups, abits, dbits, init;
35 vector<int> ports, wrmode, enable, transp, clocks, clkpol;
36
37 vector<portinfo_t> make_portinfos() const
38 {
39 vector<portinfo_t> portinfos;
40 for (int i = 0; i < groups && i < GetSize(ports); i++)
41 for (int j = 0; j < ports[i]; j++) {
42 portinfo_t pi;
43 pi.group = i;
44 pi.index = j;
45 pi.wrmode = i < GetSize(wrmode) ? wrmode[i] : 0;
46 pi.enable = i < GetSize(enable) ? enable[i] : 0;
47 pi.transp = i < GetSize(transp) ? transp[i] : 0;
48 pi.clocks = i < GetSize(clocks) ? clocks[i] : 0;
49 pi.clkpol = i < GetSize(clkpol) ? clkpol[i] : 0;
50 portinfos.push_back(pi);
51 }
52 return portinfos;
53 }
54 };
55
56 struct match_t {
57 IdString name;
58 dict<string, int> min_limits, max_limits;
59 };
60
61 dict<IdString, bram_t> brams;
62 vector<match_t> matches;
63
64 std::ifstream infile;
65 vector<string> tokens;
66 int linecount;
67 string line;
68
69 void syntax_error()
70 {
71 if (line.empty())
72 log_error("Unexpected end of rules file in line %d.\n", linecount);
73 log_error("Syntax error in rules file line %d: %s\n", linecount, line.c_str());
74 }
75
76 bool next_line()
77 {
78 linecount++;
79 tokens.clear();
80 while (std::getline(infile, line)) {
81 for (string tok = next_token(line); !tok.empty(); tok = next_token(line)) {
82 if (tok[0] == '#')
83 break;
84 tokens.push_back(tok);
85 }
86 if (!tokens.empty())
87 return true;
88 }
89 return false;
90 }
91
92 bool parse_single_int(const char *stmt, int &value)
93 {
94 if (GetSize(tokens) == 2 && tokens[0] == stmt) {
95 value = atoi(tokens[1].c_str());
96 return true;
97 }
98 return false;
99 }
100
101 bool parse_int_vect(const char *stmt, vector<int> &value)
102 {
103 if (GetSize(tokens) >= 2 && tokens[0] == stmt) {
104 value.resize(GetSize(tokens)-1);
105 for (int i = 1; i < GetSize(tokens); i++)
106 value[i-1] = atoi(tokens[i].c_str());
107 return true;
108 }
109 return false;
110 }
111
112 void parse_bram()
113 {
114 if (GetSize(tokens) != 2)
115 syntax_error();
116
117 bram_t data;
118 data.name = RTLIL::escape_id(tokens[1]);
119
120 while (next_line())
121 {
122 if (GetSize(tokens) == 1 && tokens[0] == "endbram") {
123 brams[data.name] = data;
124 return;
125 }
126
127 if (parse_single_int("groups", data.groups))
128 continue;
129
130 if (parse_single_int("abits", data.abits))
131 continue;
132
133 if (parse_single_int("dbits", data.dbits))
134 continue;
135
136 if (parse_single_int("init", data.init))
137 continue;
138
139 if (parse_int_vect("ports", data.ports))
140 continue;
141
142 if (parse_int_vect("wrmode", data.wrmode))
143 continue;
144
145 if (parse_int_vect("enable", data.enable))
146 continue;
147
148 if (parse_int_vect("transp", data.transp))
149 continue;
150
151 if (parse_int_vect("clocks", data.clocks))
152 continue;
153
154 if (parse_int_vect("clkpol", data.clkpol))
155 continue;
156
157 break;
158 }
159
160 syntax_error();
161 }
162
163 void parse_match()
164 {
165 if (GetSize(tokens) != 2)
166 syntax_error();
167
168 match_t data;
169 data.name = RTLIL::escape_id(tokens[1]);
170
171 while (next_line())
172 {
173 if (GetSize(tokens) == 1 && tokens[0] == "endmatch") {
174 matches.push_back(data);
175 return;
176 }
177
178 if (GetSize(tokens) == 3 && tokens[0] == "min") {
179 data.min_limits[tokens[1]] = atoi(tokens[2].c_str());
180 continue;
181 }
182
183 if (GetSize(tokens) == 3 && tokens[0] == "max") {
184 data.max_limits[tokens[1]] = atoi(tokens[2].c_str());
185 continue;
186 }
187
188 break;
189 }
190
191 syntax_error();
192 }
193
194 void parse(std::string filename)
195 {
196 infile.open(filename);
197 linecount = 0;
198
199 if (infile.fail())
200 log_error("Can't open rules file `%s'.\n", filename.c_str());
201
202 while (next_line())
203 {
204 if (tokens[0] == "bram") parse_bram();
205 else if (tokens[0] == "match") parse_match();
206 else syntax_error();
207 }
208
209 infile.close();
210 }
211 };
212
213 bool replace_cell(Cell *cell, const rules_t::bram_t &bram, const rules_t::match_t&)
214 {
215 auto portinfos = bram.make_portinfos();
216 dict<int, pair<SigBit, bool>> clock_domains;
217 vector<int> mapped_wr_ports;
218
219 log(" Mapping to bram type %s:\n", log_id(bram.name));
220
221 int wr_ports_n = cell->getParam("\\WR_PORTS").as_int();
222 auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
223 auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
224 wr_clken.extend_u0(wr_ports_n);
225 wr_clkpol.extend_u0(wr_ports_n);
226
227 SigSpec wr_clk = cell->getPort("\\WR_CLK");
228 SigSpec wr_en = cell->getPort("\\WR_EN");
229
230 for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports_n; cell_port_i++)
231 {
232 bool clken = wr_clken[cell_port_i] == State::S1;
233 auto clkpol = wr_clkpol[cell_port_i] == State::S1;
234 auto clksig = wr_clk[cell_port_i];
235
236 pair<SigBit, bool> clkdom(clksig, clkpol);
237 if (!clken)
238 clkdom = pair<SigBit, bool>(State::S1, false);
239
240 log(" Write port #%d is in clock domain %s%s.\n",
241 cell_port_i, clkdom.second ? "" : "!",
242 clken ? log_signal(clkdom.first) : "~async~");
243
244 for (; bram_port_i < GetSize(portinfos); bram_port_i++)
245 {
246 auto &pi = portinfos[bram_port_i];
247
248 if (pi.wrmode != 1)
249 skip_bram_wport:
250 continue;
251
252 if (clken) {
253 if (pi.clocks == 0) {
254 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
255 goto skip_bram_wport;
256 }
257 if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
258 log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
259 goto skip_bram_wport;
260 }
261 } else {
262 if (pi.clocks != 0) {
263 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
264 goto skip_bram_wport;
265 }
266 }
267
268 SigBit last_en_bit = State::S1;
269 for (int i = 0; i < bram.dbits; i++) {
270 if (pi.enable && i % (bram.dbits / pi.enable) == 0)
271 last_en_bit = wr_en[i];
272 if (last_en_bit != wr_en[i]) {
273 log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
274 goto skip_bram_wport;
275 }
276 }
277
278 log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
279 if (clken)
280 clock_domains[pi.clocks] = clkdom;
281 mapped_wr_ports.push_back(bram_port_i);
282 goto mapped_port;
283 }
284
285 log(" Failed to map write port #%d.\n", cell_port_i);
286 return false;
287 mapped_port:;
288 }
289
290 log(" FIXME: The core of memory_bram is not implemented yet.\n");
291 return false;
292 }
293
294 void handle_cell(Cell *cell, const rules_t &rules)
295 {
296 log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
297
298 dict<string, int> mem_properties;
299 mem_properties["words"] = cell->getParam("\\SIZE").as_int();
300 mem_properties["abits"] = cell->getParam("\\ABITS").as_int();
301 mem_properties["dbits"] = cell->getParam("\\WIDTH").as_int();
302 mem_properties["wports"] = cell->getParam("\\WR_PORTS").as_int();
303 mem_properties["rports"] = cell->getParam("\\RD_PORTS").as_int();
304 mem_properties["bits"] = mem_properties["words"] * mem_properties["dbits"];
305 mem_properties["ports"] = mem_properties["wports"] + mem_properties["rports"];
306
307 log(" Properties:");
308 for (auto &it : mem_properties)
309 log(" %s=%d", it.first.c_str(), it.second);
310 log("\n");
311
312 pool<IdString> failed_brams;
313
314 for (int i = 0; i < GetSize(rules.matches); i++)
315 {
316 if (rules.matches[i].name.in(failed_brams))
317 continue;
318
319 for (auto it : rules.matches[i].min_limits) {
320 if (!mem_properties.count(it.first))
321 log_error("Unknown property '%s' in match rule for bram type %s.\n",
322 it.first.c_str(), log_id(rules.matches[i].name));
323 if (mem_properties[it.first] >= it.second)
324 continue;
325 log(" Rule #%d for bram type %s rejected: requirement 'min %s %d' not met.\n",
326 i, log_id(rules.matches[i].name), it.first.c_str(), it.second);
327 goto next_match_rule;
328 }
329 for (auto it : rules.matches[i].max_limits) {
330 if (!mem_properties.count(it.first))
331 log_error("Unknown property '%s' in match rule for bram type %s.\n",
332 it.first.c_str(), log_id(rules.matches[i].name));
333 if (mem_properties[it.first] <= it.second)
334 continue;
335 log(" Rule #%d for bram type %s rejected: requirement 'max %s %d' not met.\n",
336 i, log_id(rules.matches[i].name), it.first.c_str(), it.second);
337 goto next_match_rule;
338 }
339
340 log(" Rule #%d for bram type %s accepted.\n", i, log_id(rules.matches[i].name));
341 if (!rules.brams.count(rules.matches[i].name))
342 log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name));
343
344 if (!replace_cell(cell, rules.brams.at(rules.matches[i].name), rules.matches[i])) {
345 log(" Mapping to bram type %s failed.\n", log_id(rules.matches[i].name));
346 failed_brams.insert(rules.matches[i].name);
347 goto next_match_rule;
348 }
349 return;
350
351 next_match_rule:;
352 }
353
354 log(" No acceptable bram resources found.\n");
355 }
356
357 struct MemoryBramPass : public Pass {
358 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
359 virtual void help()
360 {
361 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
362 log("\n");
363 log(" memory_bram -rules <rule_file> [selection]\n");
364 log("\n");
365 log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
366 log("The given rules file describes the available resources and how they should be\n");
367 log("used.\n");
368 log("\n");
369 }
370 virtual void execute(vector<string> args, Design *design)
371 {
372 rules_t rules;
373
374 log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
375
376 size_t argidx;
377 for (argidx = 1; argidx < args.size(); argidx++) {
378 if (args[argidx] == "-rules" && argidx+1 < args.size()) {
379 rules.parse(args[++argidx]);
380 continue;
381 }
382 break;
383 }
384 extra_args(args, argidx, design);
385
386 for (auto mod : design->selected_modules())
387 for (auto cell : mod->selected_cells())
388 if (cell->type == "$mem")
389 handle_cell(cell, rules);
390 }
391 } MemoryBramPass;
392
393 PRIVATE_NAMESPACE_END