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