Merge pull request #1004 from YosysHQ/clifford/fix1002
[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 bool make_transp;
35 bool make_outreg;
36 int mapped_port;
37 };
38
39 struct bram_t {
40 IdString name;
41 int variant;
42
43 int groups, abits, dbits, init;
44 vector<int> ports, wrmode, enable, transp, clocks, clkpol;
45
46 void dump_config() const
47 {
48 log(" bram %s # variant %d\n", log_id(name), variant);
49 log(" init %d\n", init);
50 log(" abits %d\n", abits);
51 log(" dbits %d\n", dbits);
52 log(" groups %d\n", groups);
53
54 log(" ports "); for (int v : ports) log("%4d", v); log("\n");
55 log(" wrmode"); for (int v : wrmode) log("%4d", v); log("\n");
56 log(" enable"); for (int v : enable) log("%4d", v); log("\n");
57 log(" transp"); for (int v : transp) log("%4d", v); log("\n");
58 log(" clocks"); for (int v : clocks) log("%4d", v); log("\n");
59 log(" clkpol"); for (int v : clkpol) log("%4d", v); log("\n");
60 log(" endbram\n");
61 }
62
63 void check_vectors() const
64 {
65 if (groups != GetSize(ports)) log_error("Bram %s variant %d has %d groups but only %d entries in 'ports'.\n", log_id(name), variant, groups, GetSize(ports));
66 if (groups != GetSize(wrmode)) log_error("Bram %s variant %d has %d groups but only %d entries in 'wrmode'.\n", log_id(name), variant, groups, GetSize(wrmode));
67 if (groups != GetSize(enable)) log_error("Bram %s variant %d has %d groups but only %d entries in 'enable'.\n", log_id(name), variant, groups, GetSize(enable));
68 if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp));
69 if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks));
70 if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol));
71 }
72
73 vector<portinfo_t> make_portinfos() const
74 {
75 vector<portinfo_t> portinfos;
76 for (int i = 0; i < groups; i++)
77 for (int j = 0; j < ports[i]; j++) {
78 portinfo_t pi;
79 pi.group = i;
80 pi.index = j;
81 pi.dupidx = 0;
82 pi.wrmode = wrmode[i];
83 pi.enable = enable[i];
84 pi.transp = transp[i];
85 pi.clocks = clocks[i];
86 pi.clkpol = clkpol[i];
87 pi.mapped_port = -1;
88 pi.make_transp = false;
89 pi.make_outreg = false;
90 pi.effective_clkpol = false;
91 portinfos.push_back(pi);
92 }
93 return portinfos;
94 }
95
96 void find_variant_params(dict<IdString, Const> &variant_params, const bram_t &other) const
97 {
98 log_assert(name == other.name);
99
100 if (groups != other.groups)
101 log_error("Bram %s variants %d and %d have different values for 'groups'.\n", log_id(name), variant, other.variant);
102
103 if (abits != other.abits)
104 variant_params["\\CFG_ABITS"] = abits;
105 if (dbits != other.dbits)
106 variant_params["\\CFG_DBITS"] = dbits;
107 if (init != other.init)
108 variant_params["\\CFG_INIT"] = init;
109
110 for (int i = 0; i < groups; i++)
111 {
112 if (ports[i] != other.ports[i])
113 log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
114 if (wrmode[i] != other.wrmode[i])
115 variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
116 if (enable[i] != other.enable[i])
117 variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i];
118 if (transp[i] != other.transp[i])
119 variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i];
120 if (clocks[i] != other.clocks[i])
121 variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i];
122 if (clkpol[i] != other.clkpol[i])
123 variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i];
124 }
125 }
126 };
127
128 struct match_t {
129 IdString name;
130 dict<string, int> min_limits, max_limits;
131 bool or_next_if_better, make_transp, make_outreg;
132 char shuffle_enable;
133 };
134
135 dict<IdString, vector<bram_t>> brams;
136 vector<match_t> matches;
137
138 std::ifstream infile;
139 vector<string> tokens;
140 vector<string> labels;
141 int linecount;
142
143 void syntax_error()
144 {
145 if (tokens.empty())
146 log_error("Unexpected end of rules file in line %d.\n", linecount);
147 log_error("Syntax error in rules file line %d.\n", linecount);
148 }
149
150 bool next_line()
151 {
152 string line;
153 while (std::getline(infile, line)) {
154 tokens.clear();
155 labels.clear();
156 linecount++;
157 for (string tok = next_token(line); !tok.empty(); tok = next_token(line)) {
158 if (tok[0] == '@') {
159 labels.push_back(tok.substr(1));
160 continue;
161 }
162 if (tok[0] == '#')
163 break;
164 tokens.push_back(tok);
165 }
166 if (!tokens.empty())
167 return true;
168 }
169 return false;
170 }
171
172 bool parse_single_int(const char *stmt, int &value)
173 {
174 if (GetSize(tokens) == 2 && tokens[0] == stmt) {
175 value = atoi(tokens[1].c_str());
176 return true;
177 }
178 return false;
179 }
180
181 bool parse_int_vect(const char *stmt, vector<int> &value)
182 {
183 if (GetSize(tokens) >= 2 && tokens[0] == stmt) {
184 value.resize(GetSize(tokens)-1);
185 for (int i = 1; i < GetSize(tokens); i++)
186 value[i-1] = atoi(tokens[i].c_str());
187 return true;
188 }
189 return false;
190 }
191
192 void parse_bram()
193 {
194 IdString bram_name = RTLIL::escape_id(tokens[1]);
195
196 if (GetSize(tokens) != 2)
197 syntax_error();
198
199 vector<vector<string>> lines_nolabels;
200 std::map<string, vector<vector<string>>> lines_labels;
201
202 while (next_line())
203 {
204 if (GetSize(tokens) == 1 && tokens[0] == "endbram")
205 break;
206 if (labels.empty())
207 lines_nolabels.push_back(tokens);
208 for (auto lab : labels)
209 lines_labels[lab].push_back(tokens);
210 }
211
212 std::map<string, vector<vector<string>>> variant_lines;
213
214 if (lines_labels.empty())
215 variant_lines[""] = lines_nolabels;
216 for (auto &it : lines_labels) {
217 variant_lines[it.first] = lines_nolabels;
218 variant_lines[it.first].insert(variant_lines[it.first].end(), it.second.begin(), it.second.end());
219 }
220
221 for (auto &it : variant_lines)
222 {
223 bram_t data;
224 data.name = bram_name;
225 data.variant = GetSize(brams[data.name]) + 1;
226 data.groups = 0;
227 data.abits = 0;
228 data.dbits = 0;
229 data.init = 0;
230
231 for (auto &line_tokens : it.second)
232 {
233 tokens = line_tokens;
234
235 if (parse_single_int("groups", data.groups))
236 continue;
237
238 if (parse_single_int("abits", data.abits))
239 continue;
240
241 if (parse_single_int("dbits", data.dbits))
242 continue;
243
244 if (parse_single_int("init", data.init))
245 continue;
246
247 if (parse_int_vect("ports", data.ports))
248 continue;
249
250 if (parse_int_vect("wrmode", data.wrmode))
251 continue;
252
253 if (parse_int_vect("enable", data.enable))
254 continue;
255
256 if (parse_int_vect("transp", data.transp))
257 continue;
258
259 if (parse_int_vect("clocks", data.clocks))
260 continue;
261
262 if (parse_int_vect("clkpol", data.clkpol))
263 continue;
264
265 syntax_error();
266 }
267
268 data.check_vectors();
269 brams[data.name].push_back(data);
270 }
271 }
272
273 void parse_match()
274 {
275 if (GetSize(tokens) != 2)
276 syntax_error();
277
278 match_t data;
279 data.name = RTLIL::escape_id(tokens[1]);
280 data.or_next_if_better = false;
281 data.make_transp = false;
282 data.make_outreg = false;
283 data.shuffle_enable = 0;
284
285 while (next_line())
286 {
287 if (!labels.empty())
288 syntax_error();
289
290 if (GetSize(tokens) == 1 && tokens[0] == "endmatch") {
291 matches.push_back(data);
292 break;
293 }
294
295 if (GetSize(tokens) == 3 && tokens[0] == "min") {
296 data.min_limits[tokens[1]] = atoi(tokens[2].c_str());
297 continue;
298 }
299
300 if (GetSize(tokens) == 3 && tokens[0] == "max") {
301 data.max_limits[tokens[1]] = atoi(tokens[2].c_str());
302 continue;
303 }
304
305 if (GetSize(tokens) == 2 && tokens[0] == "shuffle_enable" && GetSize(tokens[1]) == 1 && 'A' <= tokens[1][0] && tokens[1][0] <= 'Z') {
306 data.shuffle_enable = tokens[1][0];
307 continue;
308 }
309
310 if (GetSize(tokens) == 1 && tokens[0] == "make_transp") {
311 data.make_transp = true;
312 continue;
313 }
314
315 if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") {
316 data.make_transp = true;
317 data.make_outreg = true;
318 continue;
319 }
320
321 if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") {
322 data.or_next_if_better = true;
323 continue;
324 }
325
326 syntax_error();
327 }
328 }
329
330 void parse(string filename)
331 {
332 rewrite_filename(filename);
333 infile.open(filename);
334 linecount = 0;
335
336 if (infile.fail())
337 log_error("Can't open rules file `%s'.\n", filename.c_str());
338
339 while (next_line())
340 {
341 if (!labels.empty())
342 syntax_error();
343
344 if (tokens[0] == "bram") {
345 parse_bram();
346 continue;
347 }
348
349 if (tokens[0] == "match") {
350 parse_match();
351 continue;
352 }
353
354 syntax_error();
355 }
356
357 infile.close();
358 }
359 };
360
361 bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
362 {
363 Module *module = cell->module;
364
365 auto portinfos = bram.make_portinfos();
366 int dup_count = 1;
367
368 pair<SigBit, bool> make_transp_clk;
369 bool enable_make_transp = false;
370 int make_transp_enbits = 0;
371
372 dict<int, pair<SigBit, bool>> clock_domains;
373 dict<int, bool> clock_polarities;
374 dict<int, bool> read_transp;
375 pool<int> clocks_wr_ports;
376 pool<int> clkpol_wr_ports;
377 int clocks_max = 0;
378 int clkpol_max = 0;
379 int transp_max = 0;
380
381 clock_polarities[0] = false;
382 clock_polarities[1] = true;
383
384 for (auto &pi : portinfos) {
385 if (pi.wrmode) {
386 clocks_wr_ports.insert(pi.clocks);
387 if (pi.clkpol > 1)
388 clkpol_wr_ports.insert(pi.clkpol);
389 }
390 clocks_max = max(clocks_max, pi.clocks);
391 clkpol_max = max(clkpol_max, pi.clkpol);
392 transp_max = max(transp_max, pi.transp);
393 }
394
395 log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
396 // bram.dump_config();
397
398 int mem_size = cell->getParam("\\SIZE").as_int();
399 int mem_abits = cell->getParam("\\ABITS").as_int();
400 int mem_width = cell->getParam("\\WIDTH").as_int();
401 // int mem_offset = cell->getParam("\\OFFSET").as_int();
402
403 bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
404 vector<Const> initdata;
405
406 if (cell_init) {
407 Const initparam = cell->getParam("\\INIT");
408 initdata.reserve(mem_size);
409 for (int i=0; i < mem_size; i++)
410 initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx));
411 }
412
413 int wr_ports = cell->getParam("\\WR_PORTS").as_int();
414 auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
415 auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
416 wr_clken.extend_u0(wr_ports);
417 wr_clkpol.extend_u0(wr_ports);
418
419 SigSpec wr_en = cell->getPort("\\WR_EN");
420 SigSpec wr_clk = cell->getPort("\\WR_CLK");
421 SigSpec wr_data = cell->getPort("\\WR_DATA");
422 SigSpec wr_addr = cell->getPort("\\WR_ADDR");
423
424 int rd_ports = cell->getParam("\\RD_PORTS").as_int();
425 auto rd_clken = SigSpec(cell->getParam("\\RD_CLK_ENABLE"));
426 auto rd_clkpol = SigSpec(cell->getParam("\\RD_CLK_POLARITY"));
427 auto rd_transp = SigSpec(cell->getParam("\\RD_TRANSPARENT"));
428 rd_clken.extend_u0(rd_ports);
429 rd_clkpol.extend_u0(rd_ports);
430 rd_transp.extend_u0(rd_ports);
431
432 SigSpec rd_en = cell->getPort("\\RD_EN");
433 SigSpec rd_clk = cell->getPort("\\RD_CLK");
434 SigSpec rd_data = cell->getPort("\\RD_DATA");
435 SigSpec rd_addr = cell->getPort("\\RD_ADDR");
436
437 if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
438 {
439 int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
440 log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
441
442 // extract unshuffled data/enable bits
443
444 std::vector<SigSpec> old_wr_en;
445 std::vector<SigSpec> old_wr_data;
446 std::vector<SigSpec> old_rd_data;
447
448 for (int i = 0; i < wr_ports; i++) {
449 old_wr_en.push_back(wr_en.extract(i*mem_width, mem_width));
450 old_wr_data.push_back(wr_data.extract(i*mem_width, mem_width));
451 }
452
453 for (int i = 0; i < rd_ports; i++)
454 old_rd_data.push_back(rd_data.extract(i*mem_width, mem_width));
455
456 // analyze enable structure
457
458 std::vector<SigSpec> en_order;
459 dict<SigSpec, vector<int>> bits_wr_en;
460
461 for (int i = 0; i < mem_width; i++) {
462 SigSpec sig;
463 for (int j = 0; j < wr_ports; j++)
464 sig.append(old_wr_en[j][i]);
465 if (bits_wr_en.count(sig) == 0)
466 en_order.push_back(sig);
467 bits_wr_en[sig].push_back(i);
468 }
469
470 // re-create memory ports
471
472 std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
473 std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
474 std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
475 std::vector<std::vector<State>> new_initdata;
476 std::vector<int> shuffle_map;
477
478 if (cell_init)
479 new_initdata.resize(mem_size);
480
481 for (auto &it : en_order)
482 {
483 auto &bits = bits_wr_en.at(it);
484 int buckets = (GetSize(bits) + bucket_size - 1) / bucket_size;
485 int fillbits = buckets*bucket_size - GetSize(bits);
486 SigBit fillbit;
487
488 for (int i = 0; i < GetSize(bits); i++) {
489 for (int j = 0; j < wr_ports; j++) {
490 new_wr_en[j].append(old_wr_en[j][bits[i]]);
491 new_wr_data[j].append(old_wr_data[j][bits[i]]);
492 fillbit = old_wr_en[j][bits[i]];
493 }
494 for (int j = 0; j < rd_ports; j++)
495 new_rd_data[j].append(old_rd_data[j][bits[i]]);
496 if (cell_init) {
497 for (int j = 0; j < mem_size; j++)
498 new_initdata[j].push_back(initdata[j][bits[i]]);
499 }
500 shuffle_map.push_back(bits[i]);
501 }
502
503 for (int i = 0; i < fillbits; i++) {
504 for (int j = 0; j < wr_ports; j++) {
505 new_wr_en[j].append(fillbit);
506 new_wr_data[j].append(State::S0);
507 }
508 for (int j = 0; j < rd_ports; j++)
509 new_rd_data[j].append(State::Sx);
510 if (cell_init) {
511 for (int j = 0; j < mem_size; j++)
512 new_initdata[j].push_back(State::Sx);
513 }
514 shuffle_map.push_back(-1);
515 }
516 }
517
518 log(" Results of bit order shuffling:");
519 for (int v : shuffle_map)
520 log(" %d", v);
521 log("\n");
522
523 // update mem_*, wr_*, and rd_* variables
524
525 mem_width = GetSize(new_wr_en.front());
526 wr_en = SigSpec(0, wr_ports * mem_width);
527 wr_data = SigSpec(0, wr_ports * mem_width);
528 rd_data = SigSpec(0, rd_ports * mem_width);
529
530 for (int i = 0; i < wr_ports; i++) {
531 wr_en.replace(i*mem_width, new_wr_en[i]);
532 wr_data.replace(i*mem_width, new_wr_data[i]);
533 }
534
535 for (int i = 0; i < rd_ports; i++)
536 rd_data.replace(i*mem_width, new_rd_data[i]);
537
538 if (cell_init) {
539 for (int i = 0; i < mem_size; i++)
540 initdata[i] = Const(new_initdata[i]);
541 }
542 }
543
544 // assign write ports
545 pair<SigBit, bool> wr_clkdom;
546 for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
547 {
548 bool clken = wr_clken[cell_port_i] == State::S1;
549 bool clkpol = wr_clkpol[cell_port_i] == State::S1;
550 SigBit clksig = wr_clk[cell_port_i];
551
552 pair<SigBit, bool> clkdom(clksig, clkpol);
553 if (!clken)
554 clkdom = pair<SigBit, bool>(State::S1, false);
555 wr_clkdom = clkdom;
556 log(" Write port #%d is in clock domain %s%s.\n",
557 cell_port_i, clkdom.second ? "" : "!",
558 clken ? log_signal(clkdom.first) : "~async~");
559
560 for (; bram_port_i < GetSize(portinfos); bram_port_i++)
561 {
562 auto &pi = portinfos[bram_port_i];
563 make_transp_enbits = pi.enable;
564 make_transp_clk = clkdom;
565
566 if (pi.wrmode != 1)
567 skip_bram_wport:
568 continue;
569
570 if (clken) {
571 if (pi.clocks == 0) {
572 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
573 goto skip_bram_wport;
574 }
575 if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
576 log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
577 goto skip_bram_wport;
578 }
579 if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
580 log(" Bram port %c%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1);
581 goto skip_bram_wport;
582 }
583 } else {
584 if (pi.clocks != 0) {
585 log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
586 goto skip_bram_wport;
587 }
588 }
589
590 SigSpec sig_en;
591 SigBit last_en_bit = State::S1;
592 for (int i = 0; i < mem_width; i++) {
593 if (pi.enable && i % (bram.dbits / pi.enable) == 0) {
594 last_en_bit = wr_en[i + cell_port_i*mem_width];
595 sig_en.append(last_en_bit);
596 }
597 if (last_en_bit != wr_en[i + cell_port_i*mem_width]) {
598 log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
599 goto skip_bram_wport;
600 }
601 }
602
603 log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
604 pi.mapped_port = cell_port_i;
605
606 if (clken) {
607 clock_domains[pi.clocks] = clkdom;
608 clock_polarities[pi.clkpol] = clkdom.second;
609 pi.sig_clock = clkdom.first;
610 pi.effective_clkpol = clkdom.second;
611 }
612
613 pi.sig_en = sig_en;
614 pi.sig_addr = wr_addr.extract(cell_port_i*mem_abits, mem_abits);
615 pi.sig_data = wr_data.extract(cell_port_i*mem_width, mem_width);
616
617 bram_port_i++;
618 goto mapped_wr_port;
619 }
620
621 log(" Failed to map write port #%d.\n", cell_port_i);
622 return false;
623 mapped_wr_port:;
624 }
625
626 // housekeeping stuff for growing more read ports and restarting read port assignments
627
628 int grow_read_ports_cursor = -1;
629 bool try_growing_more_read_ports = false;
630 auto backup_clock_domains = clock_domains;
631 auto backup_clock_polarities = clock_polarities;
632
633 if (0) {
634 grow_read_ports:;
635 vector<rules_t::portinfo_t> new_portinfos;
636 for (auto &pi : portinfos) {
637 if (pi.wrmode == 0) {
638 pi.mapped_port = -1;
639 pi.sig_clock = SigBit();
640 pi.sig_addr = SigSpec();
641 pi.sig_data = SigSpec();
642 pi.sig_en = SigSpec();
643 pi.make_outreg = false;
644 pi.make_transp = false;
645 }
646 new_portinfos.push_back(pi);
647 if (pi.dupidx == dup_count-1) {
648 if (pi.clocks && !clocks_wr_ports[pi.clocks])
649 pi.clocks += clocks_max;
650 if (pi.clkpol > 1 && !clkpol_wr_ports[pi.clkpol])
651 pi.clkpol += clkpol_max;
652 if (pi.transp > 1)
653 pi.transp += transp_max;
654 pi.dupidx++;
655 new_portinfos.push_back(pi);
656 }
657 }
658 try_growing_more_read_ports = false;
659 portinfos.swap(new_portinfos);
660 clock_domains = backup_clock_domains;
661 clock_polarities = backup_clock_polarities;
662 dup_count++;
663 }
664
665 read_transp.clear();
666 read_transp[0] = false;
667 read_transp[1] = true;
668
669 // assign read ports
670
671 for (int cell_port_i = 0; cell_port_i < rd_ports; cell_port_i++)
672 {
673 bool clken = rd_clken[cell_port_i] == State::S1;
674 bool clkpol = rd_clkpol[cell_port_i] == State::S1;
675 bool transp = rd_transp[cell_port_i] == State::S1;
676 SigBit clksig = rd_clk[cell_port_i];
677
678 if (wr_ports == 0)
679 transp = false;
680
681 pair<SigBit, bool> clkdom(clksig, clkpol);
682 if (!clken)
683 clkdom = pair<SigBit, bool>(State::S1, false);
684
685 log(" Read port #%d is in clock domain %s%s.\n",
686 cell_port_i, clkdom.second ? "" : "!",
687 clken ? log_signal(clkdom.first) : "~async~");
688
689 for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++)
690 {
691 auto &pi = portinfos[bram_port_i];
692
693 if (pi.wrmode != 0 || pi.mapped_port >= 0)
694 skip_bram_rport:
695 continue;
696
697 if (clken) {
698 if (pi.clocks == 0) {
699 if (match.make_outreg) {
700 pi.make_outreg = true;
701 goto skip_bram_rport_clkcheck;
702 }
703 log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
704 goto skip_bram_rport;
705 }
706 if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
707 log(" Bram port %c%d.%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
708 goto skip_bram_rport;
709 }
710 if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
711 log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
712 goto skip_bram_rport;
713 }
714 if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) {
715 log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
716 goto skip_bram_rport;
717 }
718 skip_bram_rport_clkcheck:
719 if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
720 if (match.make_transp && wr_ports <= 1) {
721 pi.make_transp = true;
722 if (pi.clocks != 0) {
723 if (wr_ports == 1 && wr_clkdom != clkdom) {
724 log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
725 goto skip_bram_rport;
726 }
727 enable_make_transp = true;
728 }
729 } else {
730 log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
731 goto skip_bram_rport;
732 }
733 }
734 } else {
735 if (pi.clocks != 0) {
736 log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
737 goto skip_bram_rport;
738 }
739 }
740
741 log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
742 pi.mapped_port = cell_port_i;
743
744 if (clken) {
745 clock_domains[pi.clocks] = clkdom;
746 clock_polarities[pi.clkpol] = clkdom.second;
747 if (!pi.make_transp)
748 read_transp[pi.transp] = transp;
749 pi.sig_clock = clkdom.first;
750 pi.sig_en = rd_en[cell_port_i];
751 pi.effective_clkpol = clkdom.second;
752 }
753
754 pi.sig_addr = rd_addr.extract(cell_port_i*mem_abits, mem_abits);
755 pi.sig_data = rd_data.extract(cell_port_i*mem_width, mem_width);
756
757 if (grow_read_ports_cursor < cell_port_i) {
758 grow_read_ports_cursor = cell_port_i;
759 try_growing_more_read_ports = true;
760 }
761
762 goto mapped_rd_port;
763 }
764
765 log(" Failed to map read port #%d.\n", cell_port_i);
766 if (try_growing_more_read_ports) {
767 log(" Growing more read ports by duplicating bram cells.\n");
768 goto grow_read_ports;
769 }
770 return false;
771 mapped_rd_port:;
772 }
773
774 // update properties and re-check conditions
775
776 if (mode <= 1)
777 {
778 match_properties["dups"] = dup_count;
779 match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"];
780
781 int cells = ((mem_width + bram.dbits - 1) / bram.dbits) * ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
782 match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits));
783
784 match_properties["dcells"] = ((mem_width + bram.dbits - 1) / bram.dbits);
785 match_properties["acells"] = ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
786 match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"];
787
788 log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
789 match_properties["dups"], match_properties["waste"], match_properties["efficiency"]);
790
791 for (auto it : match.min_limits) {
792 if (!match_properties.count(it.first))
793 log_error("Unknown property '%s' in match rule for bram type %s.\n",
794 it.first.c_str(), log_id(match.name));
795 if (match_properties[it.first] >= it.second)
796 continue;
797 log(" Rule for bram type %s rejected: requirement 'min %s %d' not met.\n",
798 log_id(match.name), it.first.c_str(), it.second);
799 return false;
800 }
801 for (auto it : match.max_limits) {
802 if (!match_properties.count(it.first))
803 log_error("Unknown property '%s' in match rule for bram type %s.\n",
804 it.first.c_str(), log_id(match.name));
805 if (match_properties[it.first] <= it.second)
806 continue;
807 log(" Rule for bram type %s rejected: requirement 'max %s %d' not met.\n",
808 log_id(match.name), it.first.c_str(), it.second);
809 return false;
810 }
811
812 if (mode == 1)
813 return true;
814 }
815
816 // prepare variant parameters
817
818 dict<IdString, Const> variant_params;
819 for (auto &other_bram : rules.brams.at(bram.name))
820 bram.find_variant_params(variant_params, other_bram);
821
822 // actually replace that memory cell
823
824 dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
825
826 for (int grid_d = 0; grid_d*bram.dbits < mem_width; grid_d++)
827 {
828 SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q;
829 vector<SigSpec> mktr_wren;
830
831 if (enable_make_transp) {
832 mktr_wraddr = module->addWire(NEW_ID, bram.abits);
833 mktr_wrdata = module->addWire(NEW_ID, bram.dbits);
834 mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits);
835 module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second);
836 for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
837 mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits));
838 }
839
840 for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
841 for (int dupidx = 0; dupidx < dup_count; dupidx++)
842 {
843 Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", cell->name.c_str(), grid_d, grid_a, dupidx)), bram.name);
844 log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c));
845
846 for (auto &vp : variant_params)
847 c->setParam(vp.first, vp.second);
848
849 if (cell_init) {
850 int init_offset = grid_a*(1 << bram.abits);
851 int init_shift = grid_d*bram.dbits;
852 int init_size = (1 << bram.abits);
853 Const initparam(State::Sx, init_size*bram.dbits);
854 for (int i = 0; i < init_size; i++) {
855 State padding = State::Sx;
856 for (int j = 0; j < bram.dbits; j++)
857 if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
858 initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
859 else
860 initparam[i*bram.dbits+j] = padding;
861 }
862 c->setParam("\\INIT", initparam);
863 }
864
865 for (auto &pi : portinfos)
866 {
867 if (pi.dupidx != dupidx)
868 continue;
869
870 string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1);
871 const char *pf = prefix.c_str();
872
873 if (pi.clocks && (!c->hasPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1)) || pi.sig_clock.wire)) {
874 c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), pi.sig_clock);
875 if (pi.clkpol > 1 && pi.sig_clock.wire)
876 c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol));
877 if (pi.transp > 1 && pi.sig_clock.wire)
878 c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp));
879 }
880
881 SigSpec addr_ok;
882 if (GetSize(pi.sig_addr) > bram.abits) {
883 SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits);
884 SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr));
885 addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel);
886 }
887
888 if (pi.enable)
889 {
890 SigSpec sig_en = pi.sig_en;
891
892 if (pi.wrmode == 1) {
893 sig_en.extend_u0((grid_d+1) * pi.enable);
894 sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
895 }
896
897 if (!addr_ok.empty())
898 sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
899
900 c->setPort(stringf("\\%sEN", pf), sig_en);
901
902 if (pi.wrmode == 1 && enable_make_transp)
903 module->connect(mktr_wren[grid_a], sig_en);
904 }
905
906 SigSpec sig_addr = pi.sig_addr;
907 sig_addr.extend_u0(bram.abits);
908 c->setPort(stringf("\\%sADDR", pf), sig_addr);
909
910 if (pi.wrmode == 1 && enable_make_transp && grid_a == 0)
911 module->connect(mktr_wraddr, sig_addr);
912
913 SigSpec sig_data = pi.sig_data;
914 sig_data.extend_u0((grid_d+1) * bram.dbits);
915 sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits);
916
917 if (pi.wrmode == 1) {
918 c->setPort(stringf("\\%sDATA", pf), sig_data);
919 if (enable_make_transp && grid_a == 0)
920 module->connect(mktr_wrdata, sig_data);
921 } else {
922 SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
923 c->setPort(stringf("\\%sDATA", pf), bram_dout);
924 if (pi.make_outreg && pi.make_transp) {
925 log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
926 SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
927 module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
928 c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
929 } else if (pi.make_outreg) {
930 SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
931 if (!pi.sig_en.empty())
932 bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
933 module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
934 bram_dout = bram_dout_q;
935 } else if (pi.make_transp) {
936 log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
937
938 SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
939 mktr_wren[grid_a], module->Eq(NEW_ID, mktr_wraddr, sig_addr));
940
941 SigSpec transp_en_q = module->addWire(NEW_ID, make_transp_enbits);
942 module->addDff(NEW_ID, make_transp_clk.first, transp_en_d, transp_en_q, make_transp_clk.second);
943
944 for (int i = 0; i < make_transp_enbits; i++) {
945 int en_width = bram.dbits / make_transp_enbits;
946 SigSpec orig_bram_dout = bram_dout.extract(i * en_width, en_width);
947 SigSpec bypass_dout = mktr_wrdata_q.extract(i * en_width, en_width);
948 bram_dout.replace(i * en_width, module->Mux(NEW_ID, orig_bram_dout, bypass_dout, transp_en_q[i]));
949 }
950 }
951
952 for (int i = bram.dbits-1; i >= 0; i--)
953 if (sig_data[i].wire == nullptr) {
954 sig_data.remove(i);
955 bram_dout.remove(i);
956 }
957
958 SigSpec addr_ok_q = addr_ok;
959 if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
960 addr_ok_q = module->addWire(NEW_ID);
961 if (!pi.sig_en.empty())
962 addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
963 module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
964 }
965
966 dout_cache[sig_data].first.append(addr_ok_q);
967 dout_cache[sig_data].second.append(bram_dout);
968 }
969 }
970 }
971 }
972
973 for (auto &it : dout_cache)
974 {
975 if (it.second.first.empty())
976 {
977 log_assert(GetSize(it.first) == GetSize(it.second.second));
978 module->connect(it.first, it.second.second);
979 }
980 else
981 {
982 log_assert(GetSize(it.first)*GetSize(it.second.first) == GetSize(it.second.second));
983 module->addPmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), it.second.second, it.second.first, it.first);
984 }
985 }
986
987 module->remove(cell);
988 return true;
989 }
990
991 void handle_cell(Cell *cell, const rules_t &rules)
992 {
993 log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
994
995 bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
996
997 dict<string, int> match_properties;
998 match_properties["words"] = cell->getParam("\\SIZE").as_int();
999 match_properties["abits"] = cell->getParam("\\ABITS").as_int();
1000 match_properties["dbits"] = cell->getParam("\\WIDTH").as_int();
1001 match_properties["wports"] = cell->getParam("\\WR_PORTS").as_int();
1002 match_properties["rports"] = cell->getParam("\\RD_PORTS").as_int();
1003 match_properties["bits"] = match_properties["words"] * match_properties["dbits"];
1004 match_properties["ports"] = match_properties["wports"] + match_properties["rports"];
1005
1006 log(" Properties:");
1007 for (auto &it : match_properties)
1008 log(" %s=%d", it.first.c_str(), it.second);
1009 log("\n");
1010
1011 pool<pair<IdString, int>> failed_brams;
1012 dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
1013
1014 for (int i = 0; i < GetSize(rules.matches); i++)
1015 {
1016 auto &match = rules.matches.at(i);
1017
1018 if (!rules.brams.count(rules.matches[i].name))
1019 log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name));
1020
1021 for (int vi = 0; vi < GetSize(rules.brams.at(match.name)); vi++)
1022 {
1023 auto &bram = rules.brams.at(match.name).at(vi);
1024 bool or_next_if_better = match.or_next_if_better || vi+1 < GetSize(rules.brams.at(match.name));
1025
1026 if (failed_brams.count(pair<IdString, int>(bram.name, bram.variant)))
1027 continue;
1028
1029 int avail_rd_ports = 0;
1030 int avail_wr_ports = 0;
1031 for (int j = 0; j < bram.groups; j++) {
1032 if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) == 0)
1033 avail_rd_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
1034 if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) != 0)
1035 avail_wr_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
1036 }
1037
1038 log(" Checking rule #%d for bram type %s (variant %d):\n", i+1, log_id(bram.name), bram.variant);
1039 log(" Bram geometry: abits=%d dbits=%d wports=%d rports=%d\n", bram.abits, bram.dbits, avail_wr_ports, avail_rd_ports);
1040
1041 int dups = avail_rd_ports ? (match_properties["rports"] + avail_rd_ports - 1) / avail_rd_ports : 1;
1042 match_properties["dups"] = dups;
1043
1044 log(" Estimated number of duplicates for more read ports: dups=%d\n", match_properties["dups"]);
1045
1046 int aover = match_properties["words"] % (1 << bram.abits);
1047 int awaste = aover ? (1 << bram.abits) - aover : 0;
1048 match_properties["awaste"] = awaste;
1049
1050 int dover = match_properties["dbits"] % bram.dbits;
1051 int dwaste = dover ? bram.dbits - dover : 0;
1052 match_properties["dwaste"] = dwaste;
1053
1054 int bwaste = awaste * bram.dbits + dwaste * (1 << bram.abits) - awaste * dwaste;
1055 match_properties["bwaste"] = bwaste;
1056
1057 int waste = match_properties["dups"] * bwaste;
1058 match_properties["waste"] = waste;
1059
1060 int cells = ((match_properties["dbits"] + bram.dbits - 1) / bram.dbits) * ((match_properties["words"] + (1 << bram.abits) - 1) / (1 << bram.abits));
1061 int efficiency = (100 * match_properties["bits"]) / (dups * cells * bram.dbits * (1 << bram.abits));
1062 match_properties["efficiency"] = efficiency;
1063
1064 log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n",
1065 log_id(match.name), awaste, dwaste, bwaste, waste, efficiency);
1066
1067 if (cell_init && bram.init == 0) {
1068 log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n",
1069 i+1, log_id(bram.name), bram.variant);
1070 goto next_match_rule;
1071 }
1072
1073 for (auto it : match.min_limits) {
1074 if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells")
1075 continue;
1076 if (!match_properties.count(it.first))
1077 log_error("Unknown property '%s' in match rule for bram type %s.\n",
1078 it.first.c_str(), log_id(match.name));
1079 if (match_properties[it.first] >= it.second)
1080 continue;
1081 log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met.\n",
1082 i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
1083 goto next_match_rule;
1084 }
1085
1086 for (auto it : match.max_limits) {
1087 if (it.first == "acells" || it.first == "dcells" || it.first == "cells")
1088 continue;
1089 if (!match_properties.count(it.first))
1090 log_error("Unknown property '%s' in match rule for bram type %s.\n",
1091 it.first.c_str(), log_id(match.name));
1092 if (match_properties[it.first] <= it.second)
1093 continue;
1094 log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'max %s %d' not met.\n",
1095 i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
1096 goto next_match_rule;
1097 }
1098
1099 log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
1100
1101 if (or_next_if_better || !best_rule_cache.empty())
1102 {
1103 if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name)))
1104 log_error("Found 'or_next_if_better' in last match rule.\n");
1105
1106 if (!replace_cell(cell, rules, bram, match, match_properties, 1)) {
1107 log(" Mapping to bram type %s failed.\n", log_id(match.name));
1108 failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
1109 goto next_match_rule;
1110 }
1111
1112 log(" Storing for later selection.\n");
1113 best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
1114
1115 next_match_rule:
1116 if (or_next_if_better || best_rule_cache.empty())
1117 continue;
1118
1119 log(" Selecting best of %d rules:\n", GetSize(best_rule_cache));
1120 pair<int, int> best_rule = best_rule_cache.begin()->first;
1121
1122 for (auto &it : best_rule_cache) {
1123 if (it.second > best_rule_cache[best_rule])
1124 best_rule = it.first;
1125 log(" Efficiency for rule %d.%d: efficiency=%d, cells=%d, acells=%d\n", it.first.first+1, it.first.second+1,
1126 std::get<0>(it.second), -std::get<1>(it.second), -std::get<2>(it.second));
1127 }
1128
1129 log(" Selected rule %d.%d with efficiency %d.\n", best_rule.first+1, best_rule.second+1, std::get<0>(best_rule_cache[best_rule]));
1130 best_rule_cache.clear();
1131
1132 auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second);
1133 if (!replace_cell(cell, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
1134 log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant);
1135 return;
1136 }
1137
1138 if (!replace_cell(cell, rules, bram, match, match_properties, 0)) {
1139 log(" Mapping to bram type %s failed.\n", log_id(match.name));
1140 failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
1141 goto next_match_rule;
1142 }
1143 return;
1144 }
1145 }
1146
1147 log(" No acceptable bram resources found.\n");
1148 }
1149
1150 struct MemoryBramPass : public Pass {
1151 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
1152 void help() YS_OVERRIDE
1153 {
1154 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
1155 log("\n");
1156 log(" memory_bram -rules <rule_file> [selection]\n");
1157 log("\n");
1158 log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
1159 log("The given rules file describes the available resources and how they should be\n");
1160 log("used.\n");
1161 log("\n");
1162 log("The rules file contains a set of block ram description and a sequence of match\n");
1163 log("rules. A block ram description looks like this:\n");
1164 log("\n");
1165 log(" bram RAMB1024X32 # name of BRAM cell\n");
1166 log(" init 1 # set to '1' if BRAM can be initialized\n");
1167 log(" abits 10 # number of address bits\n");
1168 log(" dbits 32 # number of data bits\n");
1169 log(" groups 2 # number of port groups\n");
1170 log(" ports 1 1 # number of ports in each group\n");
1171 log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
1172 log(" enable 4 1 # number of enable bits\n");
1173 log(" transp 0 2 # transparent (for read ports)\n");
1174 log(" clocks 1 2 # clock configuration\n");
1175 log(" clkpol 2 2 # clock polarity configuration\n");
1176 log(" endbram\n");
1177 log("\n");
1178 log("For the option 'transp' the value 0 means non-transparent, 1 means transparent\n");
1179 log("and a value greater than 1 means configurable. All groups with the same\n");
1180 log("value greater than 1 share the same configuration bit.\n");
1181 log("\n");
1182 log("For the option 'clocks' the value 0 means non-clocked, and a value greater\n");
1183 log("than 0 means clocked. All groups with the same value share the same clock\n");
1184 log("signal.\n");
1185 log("\n");
1186 log("For the option 'clkpol' the value 0 means negative edge, 1 means positive edge\n");
1187 log("and a value greater than 1 means configurable. All groups with the same value\n");
1188 log("greater than 1 share the same configuration bit.\n");
1189 log("\n");
1190 log("Using the same bram name in different bram blocks will create different variants\n");
1191 log("of the bram. Verilog configuration parameters for the bram are created as needed.\n");
1192 log("\n");
1193 log("It is also possible to create variants by repeating statements in the bram block\n");
1194 log("and appending '@<label>' to the individual statements.\n");
1195 log("\n");
1196 log("A match rule looks like this:\n");
1197 log("\n");
1198 log(" match RAMB1024X32\n");
1199 log(" max waste 16384 # only use this bram if <= 16k ram bits are unused\n");
1200 log(" min efficiency 80 # only use this bram if efficiency is at least 80%%\n");
1201 log(" endmatch\n");
1202 log("\n");
1203 log("It is possible to match against the following values with min/max rules:\n");
1204 log("\n");
1205 log(" words ........ number of words in memory in design\n");
1206 log(" abits ........ number of address bits on memory in design\n");
1207 log(" dbits ........ number of data bits on memory in design\n");
1208 log(" wports ....... number of write ports on memory in design\n");
1209 log(" rports ....... number of read ports on memory in design\n");
1210 log(" ports ........ number of ports on memory in design\n");
1211 log(" bits ......... number of bits in memory in design\n");
1212 log(" dups .......... number of duplications for more read ports\n");
1213 log("\n");
1214 log(" awaste ....... number of unused address slots for this match\n");
1215 log(" dwaste ....... number of unused data bits for this match\n");
1216 log(" bwaste ....... number of unused bram bits for this match\n");
1217 log(" waste ........ total number of unused bram bits (bwaste*dups)\n");
1218 log(" efficiency ... total percentage of used and non-duplicated bits\n");
1219 log("\n");
1220 log(" acells ....... number of cells in 'address-direction'\n");
1221 log(" dcells ....... number of cells in 'data-direction'\n");
1222 log(" cells ........ total number of cells (acells*dcells*dups)\n");
1223 log("\n");
1224 log("The interface for the created bram instances is derived from the bram\n");
1225 log("description. Use 'techmap' to convert the created bram instances into\n");
1226 log("instances of the actual bram cells of your target architecture.\n");
1227 log("\n");
1228 log("A match containing the command 'or_next_if_better' is only used if it\n");
1229 log("has a higher efficiency than the next match (and the one after that if\n");
1230 log("the next also has 'or_next_if_better' set, and so forth).\n");
1231 log("\n");
1232 log("A match containing the command 'make_transp' will add external circuitry\n");
1233 log("to simulate 'transparent read', if necessary.\n");
1234 log("\n");
1235 log("A match containing the command 'make_outreg' will add external flip-flops\n");
1236 log("to implement synchronous read ports, if necessary.\n");
1237 log("\n");
1238 log("A match containing the command 'shuffle_enable A' will re-organize\n");
1239 log("the data bits to accommodate the enable pattern of port A.\n");
1240 log("\n");
1241 }
1242 void execute(vector<string> args, Design *design) YS_OVERRIDE
1243 {
1244 rules_t rules;
1245
1246 log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
1247
1248 size_t argidx;
1249 for (argidx = 1; argidx < args.size(); argidx++) {
1250 if (args[argidx] == "-rules" && argidx+1 < args.size()) {
1251 rules.parse(args[++argidx]);
1252 continue;
1253 }
1254 break;
1255 }
1256 extra_args(args, argidx, design);
1257
1258 for (auto mod : design->selected_modules())
1259 for (auto cell : mod->selected_cells())
1260 if (cell->type == "$mem")
1261 handle_cell(cell, rules);
1262 }
1263 } MemoryBramPass;
1264
1265 PRIVATE_NAMESPACE_END