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, dupidx;
29 int wrmode, enable, transp, clocks, clkpol;
30
31 SigBit sig_clock;
32 SigSpec sig_addr, sig_data, sig_en;
33 bool effective_clkpol;
34 int mapped_port;
35 };
36
37 struct bram_t {
38 IdString name;
39 int groups, abits, dbits, init;
40 vector<int> ports, wrmode, enable, transp, clocks, clkpol;
41
42 vector<portinfo_t> make_portinfos() const
43 {
44 vector<portinfo_t> portinfos;
45 for (int i = 0; i < groups && i < GetSize(ports); i++)
46 for (int j = 0; j < ports[i]; j++) {
47 portinfo_t pi;
48 pi.group = i;
49 pi.index = j;
50 pi.dupidx = 0;
51 pi.wrmode = i < GetSize(wrmode) ? wrmode[i] : 0;
52 pi.enable = i < GetSize(enable) ? enable[i] : 0;
53 pi.transp = i < GetSize(transp) ? transp[i] : 0;
54 pi.clocks = i < GetSize(clocks) ? clocks[i] : 0;
55 pi.clkpol = i < GetSize(clkpol) ? clkpol[i] : 0;
56 pi.mapped_port = -1;
57 portinfos.push_back(pi);
58 }
59 return portinfos;
60 }
61 };
62
63 struct match_t {
64 IdString name;
65 dict<string, int> min_limits, max_limits;
66 };
67
68 dict<IdString, bram_t> brams;
69 vector<match_t> matches;
70
71 std::ifstream infile;
72 vector<string> tokens;
73 int linecount;
74 string line;
75
76 void syntax_error()
77 {
78 if (line.empty())
79 log_error("Unexpected end of rules file in line %d.\n", linecount);
80 log_error("Syntax error in rules file line %d: %s\n", linecount, line.c_str());
81 }
82
83 bool next_line()
84 {
85 linecount++;
86 tokens.clear();
87 while (std::getline(infile, line)) {
88 for (string tok = next_token(line); !tok.empty(); tok = next_token(line)) {
89 if (tok[0] == '#')
90 break;
91 tokens.push_back(tok);
92 }
93 if (!tokens.empty())
94 return true;
95 }
96 return false;
97 }
98
99 bool parse_single_int(const char *stmt, int &value)
100 {
101 if (GetSize(tokens) == 2 && tokens[0] == stmt) {
102 value = atoi(tokens[1].c_str());
103 return true;
104 }
105 return false;
106 }
107
108 bool parse_int_vect(const char *stmt, vector<int> &value)
109 {
110 if (GetSize(tokens) >= 2 && tokens[0] == stmt) {
111 value.resize(GetSize(tokens)-1);
112 for (int i = 1; i < GetSize(tokens); i++)
113 value[i-1] = atoi(tokens[i].c_str());
114 return true;
115 }
116 return false;
117 }
118
119 void parse_bram()
120 {
121 if (GetSize(tokens) != 2)
122 syntax_error();
123
124 bram_t data;
125 data.name = RTLIL::escape_id(tokens[1]);
126
127 while (next_line())
128 {
129 if (GetSize(tokens) == 1 && tokens[0] == "endbram") {
130 brams[data.name] = data;
131 return;
132 }
133
134 if (parse_single_int("groups", data.groups))
135 continue;
136
137 if (parse_single_int("abits", data.abits))
138 continue;
139
140 if (parse_single_int("dbits", data.dbits))
141 continue;
142
143 if (parse_single_int("init", data.init))
144 continue;
145
146 if (parse_int_vect("ports", data.ports))
147 continue;
148
149 if (parse_int_vect("wrmode", data.wrmode))
150 continue;
151
152 if (parse_int_vect("enable", data.enable))
153 continue;
154
155 if (parse_int_vect("transp", data.transp))
156 continue;
157
158 if (parse_int_vect("clocks", data.clocks))
159 continue;
160
161 if (parse_int_vect("clkpol", data.clkpol))
162 continue;
163
164 break;
165 }
166
167 syntax_error();
168 }
169
170 void parse_match()
171 {
172 if (GetSize(tokens) != 2)
173 syntax_error();
174
175 match_t data;
176 data.name = RTLIL::escape_id(tokens[1]);
177
178 while (next_line())
179 {
180 if (GetSize(tokens) == 1 && tokens[0] == "endmatch") {
181 matches.push_back(data);
182 return;
183 }
184
185 if (GetSize(tokens) == 3 && tokens[0] == "min") {
186 data.min_limits[tokens[1]] = atoi(tokens[2].c_str());
187 continue;
188 }
189
190 if (GetSize(tokens) == 3 && tokens[0] == "max") {
191 data.max_limits[tokens[1]] = atoi(tokens[2].c_str());
192 continue;
193 }
194
195 break;
196 }
197
198 syntax_error();
199 }
200
201 void parse(string filename)
202 {
203 infile.open(filename);
204 linecount = 0;
205
206 if (infile.fail())
207 log_error("Can't open rules file `%s'.\n", filename.c_str());
208
209 while (next_line())
210 {
211 if (tokens[0] == "bram") parse_bram();
212 else if (tokens[0] == "match") parse_match();
213 else syntax_error();
214 }
215
216 infile.close();
217 }
218 };
219
220 bool replace_cell(Cell *cell, const rules_t::bram_t &bram, const rules_t::match_t&)
221 {
222 Module *module = cell->module;
223
224 auto portinfos = bram.make_portinfos();
225 int dup_count = 1;
226
227 dict<int, pair<SigBit, bool>> clock_domains;
228 pool<int> clocks_wr_ports;
229 int clocks_max = 0;
230
231 for (auto &pi : portinfos) {
232 if (pi.wrmode)
233 clocks_wr_ports.insert(pi.clocks);
234 clocks_max = std::max(clocks_max, pi.clocks);
235 }
236
237 log(" Mapping to bram type %s:\n", log_id(bram.name));
238
239 int mem_size = cell->getParam("\\SIZE").as_int();
240 int mem_abits = cell->getParam("\\ABITS").as_int();
241 int mem_width = cell->getParam("\\WIDTH").as_int();
242 // int mem_offset = cell->getParam("\\OFFSET").as_int();
243
244 int wr_ports = cell->getParam("\\WR_PORTS").as_int();
245 auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
246 auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
247 wr_clken.extend_u0(wr_ports);
248 wr_clkpol.extend_u0(wr_ports);
249
250 SigSpec wr_en = cell->getPort("\\WR_EN");
251 SigSpec wr_clk = cell->getPort("\\WR_CLK");
252 SigSpec wr_data = cell->getPort("\\WR_DATA");
253 SigSpec wr_addr = cell->getPort("\\WR_ADDR");
254
255 int rd_ports = cell->getParam("\\RD_PORTS").as_int();
256 auto rd_clken = SigSpec(cell->getParam("\\RD_CLK_ENABLE"));
257 auto rd_clkpol = SigSpec(cell->getParam("\\RD_CLK_POLARITY"));
258 auto rd_transp = SigSpec(cell->getParam("\\RD_TRANSPARENT"));
259 rd_clken.extend_u0(rd_ports);
260 rd_clkpol.extend_u0(rd_ports);
261 rd_transp.extend_u0(rd_ports);
262
263 SigSpec rd_clk = cell->getPort("\\RD_CLK");
264 SigSpec rd_data = cell->getPort("\\RD_DATA");
265 SigSpec rd_addr = cell->getPort("\\RD_ADDR");
266
267 for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
268 {
269 bool clken = wr_clken[cell_port_i] == State::S1;
270 auto clkpol = wr_clkpol[cell_port_i] == State::S1;
271 auto clksig = wr_clk[cell_port_i];
272
273 pair<SigBit, bool> clkdom(clksig, clkpol);
274 if (!clken)
275 clkdom = pair<SigBit, bool>(State::S1, false);
276
277 log(" Write port #%d is in clock domain %s%s.\n",
278 cell_port_i, clkdom.second ? "" : "!",
279 clken ? log_signal(clkdom.first) : "~async~");
280
281 for (; bram_port_i < GetSize(portinfos); bram_port_i++)
282 {
283 auto &pi = portinfos[bram_port_i];
284
285 if (pi.wrmode != 1)
286 skip_bram_wport:
287 continue;
288
289 if (clken) {
290 if (pi.clocks == 0) {
291 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
292 goto skip_bram_wport;
293 }
294 if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
295 log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
296 goto skip_bram_wport;
297 }
298 } else {
299 if (pi.clocks != 0) {
300 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
301 goto skip_bram_wport;
302 }
303 }
304
305 SigSpec sig_en;
306 SigBit last_en_bit = State::S1;
307 for (int i = 0; i < mem_width; i++) {
308 if (pi.enable && i % (bram.dbits / pi.enable) == 0) {
309 last_en_bit = wr_en[i + cell_port_i*mem_width];
310 sig_en.append(last_en_bit);
311 }
312 if (last_en_bit != wr_en[i + cell_port_i*mem_width]) {
313 log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
314 goto skip_bram_wport;
315 }
316 }
317
318 log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
319 pi.mapped_port = cell_port_i;
320
321 if (clken) {
322 clock_domains[pi.clocks] = clkdom;
323 pi.sig_clock = clkdom.first;
324 pi.effective_clkpol = clkdom.second;
325 }
326
327 pi.sig_en = sig_en;
328 pi.sig_addr = wr_addr.extract(cell_port_i*mem_abits, mem_abits);
329 pi.sig_data = wr_data.extract(cell_port_i*mem_width, mem_width);
330
331 bram_port_i++;
332 goto mapped_wr_port;
333 }
334
335 log(" Failed to map write port #%d.\n", cell_port_i);
336 return false;
337 mapped_wr_port:;
338 }
339
340 int grow_read_ports_cursor = -1;
341 bool try_growing_more_read_ports = false;
342 auto backup_clock_domains = clock_domains;
343
344 if (0) {
345 grow_read_ports:;
346 vector<rules_t::portinfo_t> new_portinfos;
347 for (auto &pi : portinfos) {
348 if (pi.wrmode == 0) {
349 pi.mapped_port = -1;
350 pi.sig_clock = SigBit();
351 pi.sig_addr = SigSpec();
352 pi.sig_data = SigSpec();
353 pi.sig_en = SigSpec();
354 }
355 new_portinfos.push_back(pi);
356 if (pi.dupidx == dup_count-1) {
357 if (pi.clocks && !clocks_wr_ports[pi.clocks])
358 pi.clocks += clocks_max;
359 pi.dupidx++;
360 new_portinfos.push_back(pi);
361 }
362 }
363 try_growing_more_read_ports = false;
364 portinfos.swap(new_portinfos);
365 clock_domains = backup_clock_domains;
366 dup_count++;
367 }
368
369 for (int cell_port_i = 0; cell_port_i < rd_ports; cell_port_i++)
370 {
371 bool clken = rd_clken[cell_port_i] == State::S1;
372 auto clkpol = rd_clkpol[cell_port_i] == State::S1;
373 auto clksig = rd_clk[cell_port_i];
374
375 pair<SigBit, bool> clkdom(clksig, clkpol);
376 if (!clken)
377 clkdom = pair<SigBit, bool>(State::S1, false);
378
379 log(" Read port #%d is in clock domain %s%s.\n",
380 cell_port_i, clkdom.second ? "" : "!",
381 clken ? log_signal(clkdom.first) : "~async~");
382
383 for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++)
384 {
385 auto &pi = portinfos[bram_port_i];
386
387 if (pi.wrmode != 0 || pi.mapped_port >= 0)
388 skip_bram_rport:
389 continue;
390
391 if (clken) {
392 if (pi.clocks == 0) {
393 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
394 goto skip_bram_rport;
395 }
396 if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
397 log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
398 goto skip_bram_rport;
399 }
400 } else {
401 if (pi.clocks != 0) {
402 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
403 goto skip_bram_rport;
404 }
405 }
406
407 log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
408 pi.mapped_port = cell_port_i;
409
410 if (clken) {
411 clock_domains[pi.clocks] = clkdom;
412 pi.sig_clock = clkdom.first;
413 pi.effective_clkpol = clkdom.second;
414 }
415
416 pi.sig_addr = rd_addr.extract(cell_port_i*mem_abits, mem_abits);
417 pi.sig_data = rd_data.extract(cell_port_i*mem_width, mem_width);
418
419 if (grow_read_ports_cursor < cell_port_i) {
420 grow_read_ports_cursor = cell_port_i;
421 try_growing_more_read_ports = true;
422 }
423 goto mapped_rd_port;
424 }
425
426 log(" Failed to map read port #%d.\n", cell_port_i);
427 if (try_growing_more_read_ports) {
428 log(" Growing more read ports by duplicating bram cells.\n");
429 goto grow_read_ports;
430 }
431 return false;
432 mapped_rd_port:;
433 }
434
435 dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
436
437 for (int grid_d = 0; grid_d*bram.dbits < mem_width; grid_d++)
438 for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
439 for (int dupidx = 0; dupidx < dup_count; dupidx++)
440 {
441 Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", cell->name.c_str(), grid_d, grid_a, dupidx)), bram.name);
442 log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c));
443
444 dict<int, SigBit> clocks;
445
446 for (auto &pi : portinfos)
447 {
448 if (pi.dupidx != dupidx)
449 continue;
450
451 string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1);
452 const char *pf = prefix.c_str();
453
454 if (pi.clocks && (!clocks.count(pi.clocks) || pi.sig_clock.wire))
455 clocks[pi.clocks] = pi.sig_clock;
456
457 SigSpec addr_ok;
458 if (GetSize(pi.sig_addr) > bram.abits) {
459 SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits);
460 SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr));
461 addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel);
462 }
463
464 if (pi.enable)
465 {
466 SigSpec sig_en = pi.sig_en;
467 sig_en.extend_u0((grid_d+1) * pi.enable);
468 sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
469
470 if (!addr_ok.empty())
471 sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
472
473 c->setPort(stringf("\\%sEN", pf), sig_en);
474 }
475
476 SigSpec sig_data = pi.sig_data;
477 sig_data.extend_u0((grid_d+1) * bram.dbits);
478 sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits);
479
480 if (pi.wrmode == 1) {
481 c->setPort(stringf("\\%sDATA", pf), sig_data);
482 } else {
483 SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
484 c->setPort(stringf("\\%sDATA", pf), bram_dout);
485
486 for (int i = bram.dbits-1; i >= 0; i--)
487 if (sig_data[i].wire == nullptr) {
488 sig_data.remove(i);
489 bram_dout.remove(i);
490 }
491
492 SigSpec addr_ok_q = addr_ok;
493 if (pi.clocks && !addr_ok.empty()) {
494 addr_ok_q = module->addWire(NEW_ID);
495 module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
496 }
497
498 dout_cache[sig_data].first.append(addr_ok_q);
499 dout_cache[sig_data].second.append(bram_dout);
500 }
501
502 SigSpec sig_addr = pi.sig_addr;
503 sig_addr.extend_u0(bram.abits);
504 c->setPort(stringf("\\%sADDR", pf), sig_addr);
505 }
506
507 for (auto &it : clocks)
508 c->setPort(stringf("\\CLK%d", (it.first-1) % clocks_max + 1), it.second);
509 }
510
511 for (auto &it : dout_cache)
512 {
513 if (it.second.first.empty())
514 {
515 log_assert(GetSize(it.first) == GetSize(it.second.second));
516 module->connect(it.first, it.second.second);
517 }
518 else
519 {
520 log_assert(GetSize(it.first)*GetSize(it.second.first) == GetSize(it.second.second));
521 module->addPmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), it.second.second, it.second.first, it.first);
522 }
523 }
524
525 module->remove(cell);
526 return true;
527 }
528
529 void handle_cell(Cell *cell, const rules_t &rules)
530 {
531 log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
532
533 dict<string, int> match_properties;
534 match_properties["words"] = cell->getParam("\\SIZE").as_int();
535 match_properties["abits"] = cell->getParam("\\ABITS").as_int();
536 match_properties["dbits"] = cell->getParam("\\WIDTH").as_int();
537 match_properties["wports"] = cell->getParam("\\WR_PORTS").as_int();
538 match_properties["rports"] = cell->getParam("\\RD_PORTS").as_int();
539 match_properties["bits"] = match_properties["words"] * match_properties["dbits"];
540 match_properties["ports"] = match_properties["wports"] + match_properties["rports"];
541
542 log(" Properties:");
543 for (auto &it : match_properties)
544 log(" %s=%d", it.first.c_str(), it.second);
545 log("\n");
546
547 pool<IdString> failed_brams;
548
549 for (int i = 0; i < GetSize(rules.matches); i++)
550 {
551 if (!rules.brams.count(rules.matches[i].name))
552 log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name));
553
554 auto &match = rules.matches.at(i);
555 auto &bram = rules.brams.at(match.name);
556
557 if (match.name.in(failed_brams))
558 continue;
559
560 int aover = match_properties["words"] % (1 << bram.abits);
561 int awaste = aover ? (1 << bram.abits) - aover : 0;
562 match_properties["awaste"] = awaste;
563
564 int dover = match_properties["dbits"] % bram.dbits;
565 int dwaste = dover ? bram.dbits - dover : 0;
566 match_properties["dwaste"] = dwaste;
567
568 int waste = awaste * bram.dbits + dwaste * (1 << bram.abits) - awaste * dwaste;
569 match_properties["waste"] = waste;
570
571 log(" Wasted bits for bram type %s: awaste=%d dwaste=%d waste=%d\n",
572 log_id(match.name), awaste, dwaste, waste);
573
574 for (auto it : match.min_limits) {
575 if (!match_properties.count(it.first))
576 log_error("Unknown property '%s' in match rule for bram type %s.\n",
577 it.first.c_str(), log_id(match.name));
578 if (match_properties[it.first] >= it.second)
579 continue;
580 log(" Rule #%d for bram type %s rejected: requirement 'min %s %d' not met.\n",
581 i, log_id(match.name), it.first.c_str(), it.second);
582 goto next_match_rule;
583 }
584 for (auto it : match.max_limits) {
585 if (!match_properties.count(it.first))
586 log_error("Unknown property '%s' in match rule for bram type %s.\n",
587 it.first.c_str(), log_id(match.name));
588 if (match_properties[it.first] <= it.second)
589 continue;
590 log(" Rule #%d for bram type %s rejected: requirement 'max %s %d' not met.\n",
591 i, log_id(match.name), it.first.c_str(), it.second);
592 goto next_match_rule;
593 }
594
595 log(" Rule #%d for bram type %s accepted.\n", i, log_id(match.name));
596
597 if (!replace_cell(cell, bram, match)) {
598 log(" Mapping to bram type %s failed.\n", log_id(match.name));
599 failed_brams.insert(match.name);
600 goto next_match_rule;
601 }
602 return;
603
604 next_match_rule:;
605 }
606
607 log(" No acceptable bram resources found.\n");
608 }
609
610 struct MemoryBramPass : public Pass {
611 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
612 virtual void help()
613 {
614 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
615 log("\n");
616 log(" memory_bram -rules <rule_file> [selection]\n");
617 log("\n");
618 log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
619 log("The given rules file describes the available resources and how they should be\n");
620 log("used.\n");
621 log("\n");
622 log("The rules file contains a set of block ram description and a sequence of match\n");
623 log("rules. A block ram description looks like this:\n");
624 log("\n");
625 log(" bram RAMB1024X32 # name of BRAM cell\n");
626 log(" init 1 # set to '1' if BRAM can be initialized\n");
627 log(" abits 10 # number of address bits\n");
628 log(" dbits 32 # number of data bits\n");
629 log(" groups 2 # number of port groups\n");
630 log(" ports 1 1 # number of ports in each group\n");
631 log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
632 log(" enable 4 0 # number of enable bits (for write ports)\n");
633 log(" transp 0 2 # transparatent (for read ports)\n");
634 log(" clocks 1 2 # clock configuration\n");
635 log(" clkpol 2 2 # clock polarity configuration\n");
636 log(" endbram\n");
637 log("\n");
638 log("For the option 'transp' the value 0 means non-transparent, 1 means transparent\n");
639 log("and a value greater than 1 means configurable. All groups with the same\n");
640 log("value greater than 1 share the same configuration bit.\n");
641 log("\n");
642 log("For the option 'clocks' the value 0 means non-clocked, and a value greater\n");
643 log("than 0 means clocked. All groups with the same value share the same clock\n");
644 log("signal.\n");
645 log("\n");
646 log("For the option 'clkpol' the value 0 means negative edge, 1 means positive edge\n");
647 log("and a value greater than 1 means configurable. All groups with the same value\n");
648 log("greater than 1 share the same configuration bit.\n");
649 log("\n");
650 log("A match rule looks like this:\n");
651 log("\n");
652 log(" match RAMB1024X32\n");
653 log(" max waste 16384 # only use this if <= 16384 bram bits are unused\n");
654 log(" endmatch\n");
655 log("\n");
656 log("It is possible to match against the following values with min/max rules:\n");
657 log("\n");
658 log(" words .... number of words in memory in design\n");
659 log(" abits .... number of adress bits on memory in design\n");
660 log(" dbits .... number of data bits on memory in design\n");
661 log(" wports ... number of write ports on memory in design\n");
662 log(" rports ... number of read ports on memory in design\n");
663 log(" ports .... number of ports on memory in design\n");
664 log(" bits ..... number of bits in memory in design\n");
665 log("\n");
666 log(" awaste ... number of unused address slots for this match\n");
667 log(" dwaste ... number of unused data bits for this match\n");
668 log(" waste .... total number of unused bram bits for this match\n");
669 log("\n");
670 log("The interface for the created bram instances is dervived from the bram\n");
671 log("description. Use 'techmap' to convert the created bram instances into\n");
672 log("instances of the actual bram cells of your target architecture.\n");
673 log("\n");
674 }
675 virtual void execute(vector<string> args, Design *design)
676 {
677 rules_t rules;
678
679 log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
680
681 size_t argidx;
682 for (argidx = 1; argidx < args.size(); argidx++) {
683 if (args[argidx] == "-rules" && argidx+1 < args.size()) {
684 rules.parse(args[++argidx]);
685 continue;
686 }
687 break;
688 }
689 extra_args(args, argidx, design);
690
691 for (auto mod : design->selected_modules())
692 for (auto cell : mod->selected_cells())
693 if (cell->type == "$mem")
694 handle_cell(cell, rules);
695 }
696 } MemoryBramPass;
697
698 PRIVATE_NAMESPACE_END