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
));
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
);
77 vector
<portinfo_t
> make_portinfos() const
79 vector
<portinfo_t
> portinfos
;
80 for (int i
= 0; i
< groups
; i
++)
81 for (int j
= 0; j
< ports
[i
]; j
++) {
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
];
92 pi
.make_transp
= false;
93 pi
.make_outreg
= false;
94 pi
.effective_clkpol
= false;
95 portinfos
.push_back(pi
);
100 void find_variant_params(dict
<IdString
, Const
> &variant_params
, const bram_t
&other
) const
102 log_assert(name
== other
.name
);
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
);
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
;
114 for (int i
= 0; i
< groups
; i
++)
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
];
134 dict
<string
, int> min_limits
, max_limits
;
135 bool or_next_if_better
, make_transp
, make_outreg
;
137 vector
<vector
<std::tuple
<bool,IdString
,Const
>>> attributes
;
141 dict
<IdString
, vector
<bram_t
>> brams
;
142 vector
<match_t
> matches
;
144 std::string
map_case(std::string value
) const
147 for (char &c
: value
)
153 RTLIL::Const
map_case(RTLIL::Const value
) const
155 if (value
.flags
& RTLIL::CONST_FLAG_STRING
)
156 return map_case(value
.decode_string());
160 std::ifstream infile
;
161 vector
<string
> tokens
;
162 vector
<string
> labels
;
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
);
175 while (std::getline(infile
, line
)) {
179 for (string tok
= next_token(line
); !tok
.empty(); tok
= next_token(line
)) {
181 labels
.push_back(tok
.substr(1));
186 tokens
.push_back(tok
);
194 bool parse_single_int(const char *stmt
, int &value
)
196 if (GetSize(tokens
) == 2 && tokens
[0] == stmt
) {
197 value
= atoi(tokens
[1].c_str());
203 bool parse_int_vect(const char *stmt
, vector
<int> &value
)
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());
216 IdString bram_name
= RTLIL::escape_id(tokens
[1]);
218 if (GetSize(tokens
) != 2)
221 vector
<vector
<string
>> lines_nolabels
;
222 std::map
<string
, vector
<vector
<string
>>> lines_labels
;
226 if (GetSize(tokens
) == 1 && tokens
[0] == "endbram")
229 lines_nolabels
.push_back(tokens
);
230 for (auto lab
: labels
)
231 lines_labels
[lab
].push_back(tokens
);
234 std::map
<string
, vector
<vector
<string
>>> variant_lines
;
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());
243 for (auto &it
: variant_lines
)
246 data
.name
= bram_name
;
247 data
.variant
= GetSize(brams
[data
.name
]) + 1;
253 for (auto &line_tokens
: it
.second
)
255 tokens
= line_tokens
;
257 if (parse_single_int("groups", data
.groups
))
260 if (parse_single_int("abits", data
.abits
))
263 if (parse_single_int("dbits", data
.dbits
))
266 if (parse_single_int("init", data
.init
))
269 if (parse_int_vect("ports", data
.ports
))
272 if (parse_int_vect("wrmode", data
.wrmode
))
275 if (parse_int_vect("enable", data
.enable
))
278 if (parse_int_vect("transp", data
.transp
))
281 if (parse_int_vect("clocks", data
.clocks
))
284 if (parse_int_vect("clkpol", data
.clkpol
))
290 data
.check_vectors();
291 brams
[data
.name
].push_back(data
);
297 if (GetSize(tokens
) != 2)
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;
312 if (GetSize(tokens
) == 1 && tokens
[0] == "endmatch") {
313 matches
.push_back(data
);
317 if (GetSize(tokens
) == 3 && tokens
[0] == "min") {
318 data
.min_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
322 if (GetSize(tokens
) == 3 && tokens
[0] == "max") {
323 data
.max_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
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];
332 if (GetSize(tokens
) == 1 && tokens
[0] == "make_transp") {
333 data
.make_transp
= true;
337 if (GetSize(tokens
) == 1 && tokens
[0] == "make_outreg") {
338 data
.make_transp
= true;
339 data
.make_outreg
= true;
343 if (GetSize(tokens
) == 1 && tokens
[0] == "or_next_if_better") {
344 data
.or_next_if_better
= true;
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);
357 data
.attributes
.back().emplace_back(exists
, key
, map_case(val
));
366 void parse(string filename
)
368 rewrite_filename(filename
);
369 infile
.open(filename
);
374 log_error("Can't open rules file `%s'.\n", filename
.c_str());
381 if (GetSize(tokens
) == 2 && tokens
[0] == "attr_icase") {
382 attr_icase
= atoi(tokens
[1].c_str());
386 if (tokens
[0] == "bram") {
391 if (tokens
[0] == "match") {
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
)
405 Module
*module
= cell
->module
;
407 auto portinfos
= bram
.make_portinfos();
410 pair
<SigBit
, bool> make_transp_clk
;
411 bool enable_make_transp
= false;
412 int make_transp_enbits
= 0;
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
;
423 clock_polarities
[0] = false;
424 clock_polarities
[1] = true;
426 for (auto &pi
: portinfos
) {
428 clocks_wr_ports
.insert(pi
.clocks
);
430 clkpol_wr_ports
.insert(pi
.clkpol
);
432 clocks_max
= max(clocks_max
, pi
.clocks
);
433 clkpol_max
= max(clkpol_max
, pi
.clkpol
);
434 transp_max
= max(transp_max
, pi
.transp
);
437 log(" Mapping to bram type %s (variant %d):\n", log_id(bram
.name
), bram
.variant
);
438 // bram.dump_config();
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();
445 bool cell_init
= !SigSpec(cell
->getParam(ID::INIT
)).is_fully_undef();
446 vector
<Const
> initdata
;
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
));
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
);
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
);
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
);
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
);
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)
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
);
484 // extract unshuffled data/enable bits
486 std::vector
<SigSpec
> old_wr_en
;
487 std::vector
<SigSpec
> old_wr_data
;
488 std::vector
<SigSpec
> old_rd_data
;
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
));
495 for (int i
= 0; i
< rd_ports
; i
++)
496 old_rd_data
.push_back(rd_data
.extract(i
*mem_width
, mem_width
));
498 // analyze enable structure
500 std::vector
<SigSpec
> en_order
;
501 dict
<SigSpec
, vector
<int>> bits_wr_en
;
503 for (int i
= 0; i
< mem_width
; i
++) {
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
);
512 // re-create memory ports
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
;
521 new_initdata
.resize(mem_size
);
523 for (auto &it
: en_order
)
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
);
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
]];
536 for (int j
= 0; j
< rd_ports
; j
++)
537 new_rd_data
[j
].append(old_rd_data
[j
][bits
[i
]]);
539 for (int j
= 0; j
< mem_size
; j
++)
540 new_initdata
[j
].push_back(initdata
[j
][bits
[i
]]);
542 shuffle_map
.push_back(bits
[i
]);
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
);
550 for (int j
= 0; j
< rd_ports
; j
++)
551 new_rd_data
[j
].append(State::Sx
);
553 for (int j
= 0; j
< mem_size
; j
++)
554 new_initdata
[j
].push_back(State::Sx
);
556 shuffle_map
.push_back(-1);
560 log(" Results of bit order shuffling:");
561 for (int v
: shuffle_map
)
565 // update mem_*, wr_*, and rd_* variables
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
);
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
]);
577 for (int i
= 0; i
< rd_ports
; i
++)
578 rd_data
.replace(i
*mem_width
, new_rd_data
[i
]);
581 for (int i
= 0; i
< mem_size
; i
++)
582 initdata
[i
] = Const(new_initdata
[i
]);
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
++)
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
];
594 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
596 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
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~");
602 for (; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
604 auto &pi
= portinfos
[bram_port_i
];
605 make_transp_enbits
= pi
.enable
;
606 make_transp_clk
= clkdom
;
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
;
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
;
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
;
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
;
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
);
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
;
645 log(" Mapped to bram port %c%d.\n", pi
.group
+ 'A', pi
.index
+ 1);
646 pi
.mapped_port
= cell_port_i
;
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
;
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
);
663 log(" Failed to map write port #%d.\n", cell_port_i
);
668 // housekeeping stuff for growing more read ports and restarting read port assignments
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
;
677 vector
<rules_t::portinfo_t
> new_portinfos
;
678 for (auto &pi
: portinfos
) {
679 if (pi
.wrmode
== 0) {
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;
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
;
695 pi
.transp
+= transp_max
;
697 new_portinfos
.push_back(pi
);
700 try_growing_more_read_ports
= false;
701 portinfos
.swap(new_portinfos
);
702 clock_domains
= backup_clock_domains
;
703 clock_polarities
= backup_clock_polarities
;
708 read_transp
[0] = false;
709 read_transp
[1] = true;
713 for (int cell_port_i
= 0; cell_port_i
< rd_ports
; cell_port_i
++)
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
];
723 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
725 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
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~");
731 for (int bram_port_i
= 0; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
733 auto &pi
= portinfos
[bram_port_i
];
735 if (pi
.wrmode
!= 0 || pi
.mapped_port
>= 0)
740 if (pi
.clocks
== 0) {
741 if (match
.make_outreg
) {
742 pi
.make_outreg
= true;
743 goto skip_bram_rport_clkcheck
;
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
;
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
;
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
;
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
;
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
;
769 enable_make_transp
= true;
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
;
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
;
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
;
787 clock_domains
[pi
.clocks
] = clkdom
;
788 clock_polarities
[pi
.clkpol
] = clkdom
.second
;
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
;
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
);
799 if (grow_read_ports_cursor
< cell_port_i
) {
800 grow_read_ports_cursor
= cell_port_i
;
801 try_growing_more_read_ports
= true;
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
;
816 // update properties and re-check conditions
820 match_properties
["dups"] = dup_count
;
821 match_properties
["waste"] = match_properties
["dups"] * match_properties
["bwaste"];
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
));
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"];
830 log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
831 match_properties
["dups"], match_properties
["waste"], match_properties
["efficiency"]);
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
)
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
);
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
)
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
);
854 for (const auto &sums
: match
.attributes
) {
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()) {
869 if (rules
.map_case(it
->second
) != value
)
875 std::stringstream ss
;
876 bool exists
= std::get
<0>(sums
.front());
879 IdString key
= std::get
<1>(sums
.front());
881 const Const
&value
= rules
.map_case(std::get
<2>(sums
.front()));
882 if (exists
&& value
!= Const(1))
883 ss
<< "=\"" << value
.decode_string() << "\"";
885 log(" Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
886 log_id(match
.name
), ss
.str().c_str());
895 // prepare variant parameters
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
);
901 // actually replace that memory cell
903 dict
<SigSpec
, pair
<SigSpec
, SigSpec
>> dout_cache
;
905 for (int grid_d
= 0; grid_d
*bram
.dbits
< mem_width
; grid_d
++)
907 SigSpec mktr_wraddr
, mktr_wrdata
, mktr_wrdata_q
;
908 vector
<SigSpec
> mktr_wren
;
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
));
919 for (int grid_a
= 0; grid_a
*(1 << bram
.abits
) < mem_size
; grid_a
++)
920 for (int dupidx
= 0; dupidx
< dup_count
; dupidx
++)
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
));
925 for (auto &vp
: variant_params
)
926 c
->setParam(vp
.first
, vp
.second
);
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
];
939 initparam
[i
*bram
.dbits
+j
] = padding
;
941 c
->setParam(ID::INIT
, initparam
);
944 for (auto &pi
: portinfos
)
946 if (pi
.dupidx
!= dupidx
)
949 string prefix
= stringf("%c%d", pi
.group
+ 'A', pi
.index
+ 1);
950 const char *pf
= prefix
.c_str();
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
));
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
);
969 SigSpec sig_en
= pi
.sig_en
;
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
);
976 if (!addr_ok
.empty())
977 sig_en
= module
->Mux(NEW_ID
, SigSpec(0, GetSize(sig_en
)), sig_en
, addr_ok
);
979 c
->setPort(stringf("\\%sEN", pf
), sig_en
);
981 if (pi
.wrmode
== 1 && enable_make_transp
)
982 module
->connect(mktr_wren
[grid_a
], sig_en
);
985 SigSpec sig_addr
= pi
.sig_addr
;
986 sig_addr
.extend_u0(bram
.abits
);
987 c
->setPort(stringf("\\%sADDR", pf
), sig_addr
);
989 if (pi
.wrmode
== 1 && enable_make_transp
&& grid_a
== 0)
990 module
->connect(mktr_wraddr
, sig_addr
);
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
);
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
);
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);
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
));
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
);
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
]));
1031 for (int i
= bram
.dbits
-1; i
>= 0; i
--)
1032 if (sig_data
[i
].wire
== nullptr) {
1034 bram_dout
.remove(i
);
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
);
1045 dout_cache
[sig_data
].first
.append(addr_ok_q
);
1046 dout_cache
[sig_data
].second
.append(bram_dout
);
1052 for (auto &it
: dout_cache
)
1054 if (it
.second
.first
.empty())
1056 log_assert(GetSize(it
.first
) == GetSize(it
.second
.second
));
1057 module
->connect(it
.first
, it
.second
.second
);
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
);
1066 module
->remove(cell
);
1070 void handle_cell(Cell
*cell
, const rules_t
&rules
)
1072 log("Processing %s.%s:\n", log_id(cell
->module
), log_id(cell
));
1074 bool cell_init
= !SigSpec(cell
->getParam(ID::INIT
)).is_fully_undef();
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"];
1085 log(" Properties:");
1086 for (auto &it
: match_properties
)
1087 log(" %s=%d", it
.first
.c_str(), it
.second
);
1090 pool
<pair
<IdString
, int>> failed_brams
;
1091 dict
<pair
<int, int>, tuple
<int, int, int>> best_rule_cache
;
1093 for (int i
= 0; i
< GetSize(rules
.matches
); i
++)
1095 auto &match
= rules
.matches
.at(i
);
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
));
1100 for (int vi
= 0; vi
< GetSize(rules
.brams
.at(match
.name
)); vi
++)
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
));
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;
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
);
1117 int dups
= avail_rd_ports
? (match_properties
["rports"] + avail_rd_ports
- 1) / avail_rd_ports
: 1;
1118 match_properties
["dups"] = dups
;
1120 log(" Estimated number of duplicates for more read ports: dups=%d\n", match_properties
["dups"]);
1122 int aover
= match_properties
["words"] % (1 << bram
.abits
);
1123 int awaste
= aover
? (1 << bram
.abits
) - aover
: 0;
1124 match_properties
["awaste"] = awaste
;
1126 int dover
= match_properties
["dbits"] % bram
.dbits
;
1127 int dwaste
= dover
? bram
.dbits
- dover
: 0;
1128 match_properties
["dwaste"] = dwaste
;
1130 int bwaste
= awaste
* bram
.dbits
+ dwaste
* (1 << bram
.abits
) - awaste
* dwaste
;
1131 match_properties
["bwaste"] = bwaste
;
1133 int waste
= match_properties
["dups"] * bwaste
;
1134 match_properties
["waste"] = waste
;
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
;
1140 if (failed_brams
.count(pair
<IdString
, int>(bram
.name
, bram
.variant
)))
1141 goto next_match_rule
;
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
);
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
;
1152 for (auto it
: match
.min_limits
) {
1153 if (it
.first
== "waste" || it
.first
== "dups" || it
.first
== "acells" || it
.first
== "dcells" || it
.first
== "cells")
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
)
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
;
1165 for (auto it
: match
.max_limits
) {
1166 if (it
.first
== "acells" || it
.first
== "dcells" || it
.first
== "cells")
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
)
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
;
1178 for (const auto &sums
: match
.attributes
) {
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()) {
1193 if (rules
.map_case(it
->second
) != value
)
1199 std::stringstream ss
;
1200 bool exists
= std::get
<0>(sums
.front());
1203 IdString key
= std::get
<1>(sums
.front());
1205 const Const
&value
= rules
.map_case(std::get
<2>(sums
.front()));
1206 if (exists
&& value
!= Const(1))
1207 ss
<< "=\"" << value
.decode_string() << "\"";
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
;
1215 log(" Rule #%d for bram type %s (variant %d) accepted.\n", i
+1, log_id(bram
.name
), bram
.variant
);
1217 if (or_next_if_better
|| !best_rule_cache
.empty())
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");
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
;
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"]);
1232 if (or_next_if_better
|| best_rule_cache
.empty())
1235 log(" Selecting best of %d rules:\n", GetSize(best_rule_cache
));
1236 pair
<int, int> best_rule
= best_rule_cache
.begin()->first
;
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
));
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();
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
);
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
;
1263 log(" No acceptable bram resources found.\n");
1266 struct MemoryBramPass
: public Pass
{
1267 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
1268 void help() YS_OVERRIDE
1270 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
1272 log(" memory_bram -rules <rule_file> [selection]\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");
1278 log("The rules file contains configuration options, a set of block ram description\n");
1279 log("and a sequence of match rules.\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");
1284 log("A block ram description looks like this:\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");
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");
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");
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");
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");
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");
1317 log("A match rule looks like this:\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");
1324 log("It is possible to match against the following values with min/max rules:\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");
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");
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");
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");
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");
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");
1360 log("A match containing the command 'make_transp' will add external circuitry\n");
1361 log("to simulate 'transparent read', if necessary.\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");
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");
1370 void execute(vector
<string
> args
, Design
*design
) YS_OVERRIDE
1374 log_header(design
, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
1377 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
1378 if (args
[argidx
] == "-rules" && argidx
+1 < args
.size()) {
1379 rules
.parse(args
[++argidx
]);
1384 extra_args(args
, argidx
, design
);
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
);
1393 PRIVATE_NAMESPACE_END