2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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.
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.
20 #include "kernel/yosys.h"
23 PRIVATE_NAMESPACE_BEGIN
28 int group
, index
, dupidx
;
29 int wrmode
, enable
, transp
, clocks
, clkpol
;
32 SigSpec sig_addr
, sig_data
, sig_en
;
33 bool effective_clkpol
;
43 int groups
, abits
, dbits
, init
;
44 vector
<int> ports
, wrmode
, enable
, transp
, clocks
, clkpol
;
46 void dump_config() const
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
);
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");
63 void check_vectors() const
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
));
73 vector
<portinfo_t
> make_portinfos() const
75 vector
<portinfo_t
> portinfos
;
76 for (int i
= 0; i
< groups
; i
++)
77 for (int j
= 0; j
< ports
[i
]; j
++) {
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
];
88 pi
.make_transp
= false;
89 pi
.make_outreg
= false;
90 pi
.effective_clkpol
= false;
91 portinfos
.push_back(pi
);
96 void find_variant_params(dict
<IdString
, Const
> &variant_params
, const bram_t
&other
) const
98 log_assert(name
== other
.name
);
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
);
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
;
110 for (int i
= 0; i
< groups
; i
++)
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
];
130 dict
<string
, int> min_limits
, max_limits
;
131 bool or_next_if_better
, make_transp
, make_outreg
;
135 dict
<IdString
, vector
<bram_t
>> brams
;
136 vector
<match_t
> matches
;
138 std::ifstream infile
;
139 vector
<string
> tokens
;
140 vector
<string
> labels
;
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
);
153 while (std::getline(infile
, line
)) {
157 for (string tok
= next_token(line
); !tok
.empty(); tok
= next_token(line
)) {
159 labels
.push_back(tok
.substr(1));
164 tokens
.push_back(tok
);
172 bool parse_single_int(const char *stmt
, int &value
)
174 if (GetSize(tokens
) == 2 && tokens
[0] == stmt
) {
175 value
= atoi(tokens
[1].c_str());
181 bool parse_int_vect(const char *stmt
, vector
<int> &value
)
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());
194 IdString bram_name
= RTLIL::escape_id(tokens
[1]);
196 if (GetSize(tokens
) != 2)
199 vector
<vector
<string
>> lines_nolabels
;
200 std::map
<string
, vector
<vector
<string
>>> lines_labels
;
204 if (GetSize(tokens
) == 1 && tokens
[0] == "endbram")
207 lines_nolabels
.push_back(tokens
);
208 for (auto lab
: labels
)
209 lines_labels
[lab
].push_back(tokens
);
212 std::map
<string
, vector
<vector
<string
>>> variant_lines
;
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());
221 for (auto &it
: variant_lines
)
224 data
.name
= bram_name
;
225 data
.variant
= GetSize(brams
[data
.name
]) + 1;
231 for (auto &line_tokens
: it
.second
)
233 tokens
= line_tokens
;
235 if (parse_single_int("groups", data
.groups
))
238 if (parse_single_int("abits", data
.abits
))
241 if (parse_single_int("dbits", data
.dbits
))
244 if (parse_single_int("init", data
.init
))
247 if (parse_int_vect("ports", data
.ports
))
250 if (parse_int_vect("wrmode", data
.wrmode
))
253 if (parse_int_vect("enable", data
.enable
))
256 if (parse_int_vect("transp", data
.transp
))
259 if (parse_int_vect("clocks", data
.clocks
))
262 if (parse_int_vect("clkpol", data
.clkpol
))
268 data
.check_vectors();
269 brams
[data
.name
].push_back(data
);
275 if (GetSize(tokens
) != 2)
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;
290 if (GetSize(tokens
) == 1 && tokens
[0] == "endmatch") {
291 matches
.push_back(data
);
295 if (GetSize(tokens
) == 3 && tokens
[0] == "min") {
296 data
.min_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
300 if (GetSize(tokens
) == 3 && tokens
[0] == "max") {
301 data
.max_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
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];
310 if (GetSize(tokens
) == 1 && tokens
[0] == "make_transp") {
311 data
.make_transp
= true;
315 if (GetSize(tokens
) == 1 && tokens
[0] == "make_outreg") {
316 data
.make_transp
= true;
317 data
.make_outreg
= true;
321 if (GetSize(tokens
) == 1 && tokens
[0] == "or_next_if_better") {
322 data
.or_next_if_better
= true;
330 void parse(string filename
)
332 rewrite_filename(filename
);
333 infile
.open(filename
);
337 log_error("Can't open rules file `%s'.\n", filename
.c_str());
344 if (tokens
[0] == "bram") {
349 if (tokens
[0] == "match") {
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
)
363 Module
*module
= cell
->module
;
365 auto portinfos
= bram
.make_portinfos();
368 pair
<SigBit
, bool> make_transp_clk
;
369 bool enable_make_transp
= false;
370 int make_transp_enbits
= 0;
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
;
381 clock_polarities
[0] = false;
382 clock_polarities
[1] = true;
384 for (auto &pi
: portinfos
) {
386 clocks_wr_ports
.insert(pi
.clocks
);
388 clkpol_wr_ports
.insert(pi
.clkpol
);
390 clocks_max
= max(clocks_max
, pi
.clocks
);
391 clkpol_max
= max(clkpol_max
, pi
.clkpol
);
392 transp_max
= max(transp_max
, pi
.transp
);
395 log(" Mapping to bram type %s (variant %d):\n", log_id(bram
.name
), bram
.variant
);
396 // bram.dump_config();
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();
403 bool cell_init
= !SigSpec(cell
->getParam("\\INIT")).is_fully_undef();
404 vector
<Const
> initdata
;
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
));
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
);
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");
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
);
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");
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)
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
);
442 // extract unshuffled data/enable bits
444 std::vector
<SigSpec
> old_wr_en
;
445 std::vector
<SigSpec
> old_wr_data
;
446 std::vector
<SigSpec
> old_rd_data
;
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
));
453 for (int i
= 0; i
< rd_ports
; i
++)
454 old_rd_data
.push_back(rd_data
.extract(i
*mem_width
, mem_width
));
456 // analyze enable structure
458 std::vector
<SigSpec
> en_order
;
459 dict
<SigSpec
, vector
<int>> bits_wr_en
;
461 for (int i
= 0; i
< mem_width
; i
++) {
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
);
470 // re-create memory ports
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
;
479 new_initdata
.resize(mem_size
);
481 for (auto &it
: en_order
)
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
);
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
]];
494 for (int j
= 0; j
< rd_ports
; j
++)
495 new_rd_data
[j
].append(old_rd_data
[j
][bits
[i
]]);
497 for (int j
= 0; j
< mem_size
; j
++)
498 new_initdata
[j
].push_back(initdata
[j
][bits
[i
]]);
500 shuffle_map
.push_back(bits
[i
]);
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
);
508 for (int j
= 0; j
< rd_ports
; j
++)
509 new_rd_data
[j
].append(State::Sx
);
511 for (int j
= 0; j
< mem_size
; j
++)
512 new_initdata
[j
].push_back(State::Sx
);
514 shuffle_map
.push_back(-1);
518 log(" Results of bit order shuffling:");
519 for (int v
: shuffle_map
)
523 // update mem_*, wr_*, and rd_* variables
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
);
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
]);
535 for (int i
= 0; i
< rd_ports
; i
++)
536 rd_data
.replace(i
*mem_width
, new_rd_data
[i
]);
539 for (int i
= 0; i
< mem_size
; i
++)
540 initdata
[i
] = Const(new_initdata
[i
]);
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
++)
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
];
552 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
554 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
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~");
560 for (; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
562 auto &pi
= portinfos
[bram_port_i
];
563 make_transp_enbits
= pi
.enable
;
564 make_transp_clk
= clkdom
;
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
;
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
;
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
;
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
;
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
);
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
;
603 log(" Mapped to bram port %c%d.\n", pi
.group
+ 'A', pi
.index
+ 1);
604 pi
.mapped_port
= cell_port_i
;
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
;
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
);
621 log(" Failed to map write port #%d.\n", cell_port_i
);
626 // housekeeping stuff for growing more read ports and restarting read port assignments
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
;
635 vector
<rules_t::portinfo_t
> new_portinfos
;
636 for (auto &pi
: portinfos
) {
637 if (pi
.wrmode
== 0) {
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;
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
;
653 pi
.transp
+= transp_max
;
655 new_portinfos
.push_back(pi
);
658 try_growing_more_read_ports
= false;
659 portinfos
.swap(new_portinfos
);
660 clock_domains
= backup_clock_domains
;
661 clock_polarities
= backup_clock_polarities
;
666 read_transp
[0] = false;
667 read_transp
[1] = true;
671 for (int cell_port_i
= 0; cell_port_i
< rd_ports
; cell_port_i
++)
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
];
681 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
683 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
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~");
689 for (int bram_port_i
= 0; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
691 auto &pi
= portinfos
[bram_port_i
];
693 if (pi
.wrmode
!= 0 || pi
.mapped_port
>= 0)
698 if (pi
.clocks
== 0) {
699 if (match
.make_outreg
) {
700 pi
.make_outreg
= true;
701 goto skip_bram_rport_clkcheck
;
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
;
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
;
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
;
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
;
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
;
727 enable_make_transp
= true;
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
;
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
;
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
;
745 clock_domains
[pi
.clocks
] = clkdom
;
746 clock_polarities
[pi
.clkpol
] = clkdom
.second
;
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
;
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
);
757 if (grow_read_ports_cursor
< cell_port_i
) {
758 grow_read_ports_cursor
= cell_port_i
;
759 try_growing_more_read_ports
= true;
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
;
774 // update properties and re-check conditions
778 match_properties
["dups"] = dup_count
;
779 match_properties
["waste"] = match_properties
["dups"] * match_properties
["bwaste"];
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
));
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"];
788 log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
789 match_properties
["dups"], match_properties
["waste"], match_properties
["efficiency"]);
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
)
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
);
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
)
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
);
816 // prepare variant parameters
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
);
822 // actually replace that memory cell
824 dict
<SigSpec
, pair
<SigSpec
, SigSpec
>> dout_cache
;
826 for (int grid_d
= 0; grid_d
*bram
.dbits
< mem_width
; grid_d
++)
828 SigSpec mktr_wraddr
, mktr_wrdata
, mktr_wrdata_q
;
829 vector
<SigSpec
> mktr_wren
;
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
));
840 for (int grid_a
= 0; grid_a
*(1 << bram
.abits
) < mem_size
; grid_a
++)
841 for (int dupidx
= 0; dupidx
< dup_count
; dupidx
++)
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
));
846 for (auto &vp
: variant_params
)
847 c
->setParam(vp
.first
, vp
.second
);
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
];
860 initparam
[i
*bram
.dbits
+j
] = padding
;
862 c
->setParam("\\INIT", initparam
);
865 for (auto &pi
: portinfos
)
867 if (pi
.dupidx
!= dupidx
)
870 string prefix
= stringf("%c%d", pi
.group
+ 'A', pi
.index
+ 1);
871 const char *pf
= prefix
.c_str();
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
));
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
);
890 SigSpec sig_en
= pi
.sig_en
;
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
);
897 if (!addr_ok
.empty())
898 sig_en
= module
->Mux(NEW_ID
, SigSpec(0, GetSize(sig_en
)), sig_en
, addr_ok
);
900 c
->setPort(stringf("\\%sEN", pf
), sig_en
);
902 if (pi
.wrmode
== 1 && enable_make_transp
)
903 module
->connect(mktr_wren
[grid_a
], sig_en
);
906 SigSpec sig_addr
= pi
.sig_addr
;
907 sig_addr
.extend_u0(bram
.abits
);
908 c
->setPort(stringf("\\%sADDR", pf
), sig_addr
);
910 if (pi
.wrmode
== 1 && enable_make_transp
&& grid_a
== 0)
911 module
->connect(mktr_wraddr
, sig_addr
);
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
);
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
);
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);
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
));
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
);
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
]));
952 for (int i
= bram
.dbits
-1; i
>= 0; i
--)
953 if (sig_data
[i
].wire
== nullptr) {
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
);
966 dout_cache
[sig_data
].first
.append(addr_ok_q
);
967 dout_cache
[sig_data
].second
.append(bram_dout
);
973 for (auto &it
: dout_cache
)
975 if (it
.second
.first
.empty())
977 log_assert(GetSize(it
.first
) == GetSize(it
.second
.second
));
978 module
->connect(it
.first
, it
.second
.second
);
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
);
987 module
->remove(cell
);
991 void handle_cell(Cell
*cell
, const rules_t
&rules
)
993 log("Processing %s.%s:\n", log_id(cell
->module
), log_id(cell
));
995 bool cell_init
= !SigSpec(cell
->getParam("\\INIT")).is_fully_undef();
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"];
1006 log(" Properties:");
1007 for (auto &it
: match_properties
)
1008 log(" %s=%d", it
.first
.c_str(), it
.second
);
1011 pool
<pair
<IdString
, int>> failed_brams
;
1012 dict
<pair
<int, int>, tuple
<int, int, int>> best_rule_cache
;
1014 for (int i
= 0; i
< GetSize(rules
.matches
); i
++)
1016 auto &match
= rules
.matches
.at(i
);
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
));
1021 for (int vi
= 0; vi
< GetSize(rules
.brams
.at(match
.name
)); vi
++)
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
));
1026 if (failed_brams
.count(pair
<IdString
, int>(bram
.name
, bram
.variant
)))
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;
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
);
1041 int dups
= avail_rd_ports
? (match_properties
["rports"] + avail_rd_ports
- 1) / avail_rd_ports
: 1;
1042 match_properties
["dups"] = dups
;
1044 log(" Estimated number of duplicates for more read ports: dups=%d\n", match_properties
["dups"]);
1046 int aover
= match_properties
["words"] % (1 << bram
.abits
);
1047 int awaste
= aover
? (1 << bram
.abits
) - aover
: 0;
1048 match_properties
["awaste"] = awaste
;
1050 int dover
= match_properties
["dbits"] % bram
.dbits
;
1051 int dwaste
= dover
? bram
.dbits
- dover
: 0;
1052 match_properties
["dwaste"] = dwaste
;
1054 int bwaste
= awaste
* bram
.dbits
+ dwaste
* (1 << bram
.abits
) - awaste
* dwaste
;
1055 match_properties
["bwaste"] = bwaste
;
1057 int waste
= match_properties
["dups"] * bwaste
;
1058 match_properties
["waste"] = waste
;
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
;
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
);
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
;
1073 for (auto it
: match
.min_limits
) {
1074 if (it
.first
== "waste" || it
.first
== "dups" || it
.first
== "acells" || it
.first
== "dcells" || it
.first
== "cells")
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
)
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
;
1086 for (auto it
: match
.max_limits
) {
1087 if (it
.first
== "acells" || it
.first
== "dcells" || it
.first
== "cells")
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
)
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
;
1099 log(" Rule #%d for bram type %s (variant %d) accepted.\n", i
+1, log_id(bram
.name
), bram
.variant
);
1101 if (or_next_if_better
|| !best_rule_cache
.empty())
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");
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
;
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"]);
1116 if (or_next_if_better
|| best_rule_cache
.empty())
1119 log(" Selecting best of %d rules:\n", GetSize(best_rule_cache
));
1120 pair
<int, int> best_rule
= best_rule_cache
.begin()->first
;
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
));
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();
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
);
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
;
1147 log(" No acceptable bram resources found.\n");
1150 struct MemoryBramPass
: public Pass
{
1151 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
1152 void help() YS_OVERRIDE
1154 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
1156 log(" memory_bram -rules <rule_file> [selection]\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");
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");
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");
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");
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");
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");
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");
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");
1196 log("A match rule looks like this:\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");
1203 log("It is possible to match against the following values with min/max rules:\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");
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");
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");
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");
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");
1232 log("A match containing the command 'make_transp' will add external circuitry\n");
1233 log("to simulate 'transparent read', if necessary.\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");
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");
1242 void execute(vector
<string
> args
, Design
*design
) YS_OVERRIDE
1246 log_header(design
, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
1249 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
1250 if (args
[argidx
] == "-rules" && argidx
+1 < args
.size()) {
1251 rules
.parse(args
[++argidx
]);
1256 extra_args(args
, argidx
, design
);
1258 for (auto mod
: design
->selected_modules())
1259 for (auto cell
: mod
->selected_cells())
1260 if (cell
->type
== "$mem")
1261 handle_cell(cell
, rules
);
1265 PRIVATE_NAMESPACE_END