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
;
39 int groups
, abits
, dbits
, init
;
40 vector
<int> ports
, wrmode
, enable
, transp
, clocks
, clkpol
;
42 vector
<portinfo_t
> make_portinfos() const
44 vector
<portinfo_t
> portinfos
;
45 for (int i
= 0; i
< groups
&& i
< GetSize(ports
); i
++)
46 for (int j
= 0; j
< ports
[i
]; j
++) {
51 pi
.wrmode
= i
< GetSize(wrmode
) ? wrmode
[i
] : 0;
52 pi
.enable
= i
< GetSize(enable
) ? enable
[i
] : 0;
53 pi
.transp
= i
< GetSize(transp
) ? transp
[i
] : 0;
54 pi
.clocks
= i
< GetSize(clocks
) ? clocks
[i
] : 0;
55 pi
.clkpol
= i
< GetSize(clkpol
) ? clkpol
[i
] : 0;
57 portinfos
.push_back(pi
);
65 dict
<string
, int> min_limits
, max_limits
;
68 dict
<IdString
, bram_t
> brams
;
69 vector
<match_t
> matches
;
72 vector
<string
> tokens
;
79 log_error("Unexpected end of rules file in line %d.\n", linecount
);
80 log_error("Syntax error in rules file line %d: %s\n", linecount
, line
.c_str());
87 while (std::getline(infile
, line
)) {
88 for (string tok
= next_token(line
); !tok
.empty(); tok
= next_token(line
)) {
91 tokens
.push_back(tok
);
99 bool parse_single_int(const char *stmt
, int &value
)
101 if (GetSize(tokens
) == 2 && tokens
[0] == stmt
) {
102 value
= atoi(tokens
[1].c_str());
108 bool parse_int_vect(const char *stmt
, vector
<int> &value
)
110 if (GetSize(tokens
) >= 2 && tokens
[0] == stmt
) {
111 value
.resize(GetSize(tokens
)-1);
112 for (int i
= 1; i
< GetSize(tokens
); i
++)
113 value
[i
-1] = atoi(tokens
[i
].c_str());
121 if (GetSize(tokens
) != 2)
125 data
.name
= RTLIL::escape_id(tokens
[1]);
129 if (GetSize(tokens
) == 1 && tokens
[0] == "endbram") {
130 brams
[data
.name
] = data
;
134 if (parse_single_int("groups", data
.groups
))
137 if (parse_single_int("abits", data
.abits
))
140 if (parse_single_int("dbits", data
.dbits
))
143 if (parse_single_int("init", data
.init
))
146 if (parse_int_vect("ports", data
.ports
))
149 if (parse_int_vect("wrmode", data
.wrmode
))
152 if (parse_int_vect("enable", data
.enable
))
155 if (parse_int_vect("transp", data
.transp
))
158 if (parse_int_vect("clocks", data
.clocks
))
161 if (parse_int_vect("clkpol", data
.clkpol
))
172 if (GetSize(tokens
) != 2)
176 data
.name
= RTLIL::escape_id(tokens
[1]);
180 if (GetSize(tokens
) == 1 && tokens
[0] == "endmatch") {
181 matches
.push_back(data
);
185 if (GetSize(tokens
) == 3 && tokens
[0] == "min") {
186 data
.min_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
190 if (GetSize(tokens
) == 3 && tokens
[0] == "max") {
191 data
.max_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
201 void parse(string filename
)
203 infile
.open(filename
);
207 log_error("Can't open rules file `%s'.\n", filename
.c_str());
211 if (tokens
[0] == "bram") parse_bram();
212 else if (tokens
[0] == "match") parse_match();
220 bool replace_cell(Cell
*cell
, const rules_t::bram_t
&bram
, const rules_t::match_t
&)
222 Module
*module
= cell
->module
;
224 auto portinfos
= bram
.make_portinfos();
227 dict
<int, pair
<SigBit
, bool>> clock_domains
;
228 pool
<int> clocks_wr_ports
;
231 for (auto &pi
: portinfos
) {
233 clocks_wr_ports
.insert(pi
.clocks
);
234 clocks_max
= std::max(clocks_max
, pi
.clocks
);
237 log(" Mapping to bram type %s:\n", log_id(bram
.name
));
239 int mem_size
= cell
->getParam("\\SIZE").as_int();
240 int mem_abits
= cell
->getParam("\\ABITS").as_int();
241 int mem_width
= cell
->getParam("\\WIDTH").as_int();
242 // int mem_offset = cell->getParam("\\OFFSET").as_int();
244 int wr_ports
= cell
->getParam("\\WR_PORTS").as_int();
245 auto wr_clken
= SigSpec(cell
->getParam("\\WR_CLK_ENABLE"));
246 auto wr_clkpol
= SigSpec(cell
->getParam("\\WR_CLK_POLARITY"));
247 wr_clken
.extend_u0(wr_ports
);
248 wr_clkpol
.extend_u0(wr_ports
);
250 SigSpec wr_en
= cell
->getPort("\\WR_EN");
251 SigSpec wr_clk
= cell
->getPort("\\WR_CLK");
252 SigSpec wr_data
= cell
->getPort("\\WR_DATA");
253 SigSpec wr_addr
= cell
->getPort("\\WR_ADDR");
255 int rd_ports
= cell
->getParam("\\RD_PORTS").as_int();
256 auto rd_clken
= SigSpec(cell
->getParam("\\RD_CLK_ENABLE"));
257 auto rd_clkpol
= SigSpec(cell
->getParam("\\RD_CLK_POLARITY"));
258 auto rd_transp
= SigSpec(cell
->getParam("\\RD_TRANSPARENT"));
259 rd_clken
.extend_u0(rd_ports
);
260 rd_clkpol
.extend_u0(rd_ports
);
261 rd_transp
.extend_u0(rd_ports
);
263 SigSpec rd_clk
= cell
->getPort("\\RD_CLK");
264 SigSpec rd_data
= cell
->getPort("\\RD_DATA");
265 SigSpec rd_addr
= cell
->getPort("\\RD_ADDR");
267 for (int cell_port_i
= 0, bram_port_i
= 0; cell_port_i
< wr_ports
; cell_port_i
++)
269 bool clken
= wr_clken
[cell_port_i
] == State::S1
;
270 auto clkpol
= wr_clkpol
[cell_port_i
] == State::S1
;
271 auto clksig
= wr_clk
[cell_port_i
];
273 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
275 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
277 log(" Write port #%d is in clock domain %s%s.\n",
278 cell_port_i
, clkdom
.second
? "" : "!",
279 clken
? log_signal(clkdom
.first
) : "~async~");
281 for (; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
283 auto &pi
= portinfos
[bram_port_i
];
290 if (pi
.clocks
== 0) {
291 log(" Bram port %c%d has incompatible clock type.\n", pi
.group
+ 'A', pi
.index
+ 1);
292 goto skip_bram_wport
;
294 if (clock_domains
.count(pi
.clocks
) && clock_domains
.at(pi
.clocks
) != clkdom
) {
295 log(" Bram port %c%d is in a different clock domain.\n", pi
.group
+ 'A', pi
.index
+ 1);
296 goto skip_bram_wport
;
299 if (pi
.clocks
!= 0) {
300 log(" Bram port %c%d has incompatible clock type.\n", pi
.group
+ 'A', pi
.index
+ 1);
301 goto skip_bram_wport
;
306 SigBit last_en_bit
= State::S1
;
307 for (int i
= 0; i
< mem_width
; i
++) {
308 if (pi
.enable
&& i
% (bram
.dbits
/ pi
.enable
) == 0) {
309 last_en_bit
= wr_en
[i
+ cell_port_i
*mem_width
];
310 sig_en
.append(last_en_bit
);
312 if (last_en_bit
!= wr_en
[i
+ cell_port_i
*mem_width
]) {
313 log(" Bram port %c%d has incompatible enable structure.\n", pi
.group
+ 'A', pi
.index
+ 1);
314 goto skip_bram_wport
;
318 log(" Mapped to bram port %c%d.\n", pi
.group
+ 'A', pi
.index
+ 1);
319 pi
.mapped_port
= cell_port_i
;
322 clock_domains
[pi
.clocks
] = clkdom
;
323 pi
.sig_clock
= clkdom
.first
;
324 pi
.effective_clkpol
= clkdom
.second
;
328 pi
.sig_addr
= wr_addr
.extract(cell_port_i
*mem_abits
, mem_abits
);
329 pi
.sig_data
= wr_data
.extract(cell_port_i
*mem_width
, mem_width
);
335 log(" Failed to map write port #%d.\n", cell_port_i
);
340 int grow_read_ports_cursor
= -1;
341 bool try_growing_more_read_ports
= false;
342 auto backup_clock_domains
= clock_domains
;
346 vector
<rules_t::portinfo_t
> new_portinfos
;
347 for (auto &pi
: portinfos
) {
348 if (pi
.wrmode
== 0) {
350 pi
.sig_clock
= SigBit();
351 pi
.sig_addr
= SigSpec();
352 pi
.sig_data
= SigSpec();
353 pi
.sig_en
= SigSpec();
355 new_portinfos
.push_back(pi
);
356 if (pi
.dupidx
== dup_count
-1) {
357 if (pi
.clocks
&& !clocks_wr_ports
[pi
.clocks
])
358 pi
.clocks
+= clocks_max
;
360 new_portinfos
.push_back(pi
);
363 try_growing_more_read_ports
= false;
364 portinfos
.swap(new_portinfos
);
365 clock_domains
= backup_clock_domains
;
369 for (int cell_port_i
= 0; cell_port_i
< rd_ports
; cell_port_i
++)
371 bool clken
= rd_clken
[cell_port_i
] == State::S1
;
372 auto clkpol
= rd_clkpol
[cell_port_i
] == State::S1
;
373 auto clksig
= rd_clk
[cell_port_i
];
375 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
377 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
379 log(" Read port #%d is in clock domain %s%s.\n",
380 cell_port_i
, clkdom
.second
? "" : "!",
381 clken
? log_signal(clkdom
.first
) : "~async~");
383 for (int bram_port_i
= 0; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
385 auto &pi
= portinfos
[bram_port_i
];
387 if (pi
.wrmode
!= 0 || pi
.mapped_port
>= 0)
392 if (pi
.clocks
== 0) {
393 log(" Bram port %c%d has incompatible clock type.\n", pi
.group
+ 'A', pi
.index
+ 1);
394 goto skip_bram_rport
;
396 if (clock_domains
.count(pi
.clocks
) && clock_domains
.at(pi
.clocks
) != clkdom
) {
397 log(" Bram port %c%d is in a different clock domain.\n", pi
.group
+ 'A', pi
.index
+ 1);
398 goto skip_bram_rport
;
401 if (pi
.clocks
!= 0) {
402 log(" Bram port %c%d has incompatible clock type.\n", pi
.group
+ 'A', pi
.index
+ 1);
403 goto skip_bram_rport
;
407 log(" Mapped to bram port %c%d.%d.\n", pi
.group
+ 'A', pi
.index
+ 1, pi
.dupidx
+ 1);
408 pi
.mapped_port
= cell_port_i
;
411 clock_domains
[pi
.clocks
] = clkdom
;
412 pi
.sig_clock
= clkdom
.first
;
413 pi
.effective_clkpol
= clkdom
.second
;
416 pi
.sig_addr
= rd_addr
.extract(cell_port_i
*mem_abits
, mem_abits
);
417 pi
.sig_data
= rd_data
.extract(cell_port_i
*mem_width
, mem_width
);
419 if (grow_read_ports_cursor
< cell_port_i
) {
420 grow_read_ports_cursor
= cell_port_i
;
421 try_growing_more_read_ports
= true;
426 log(" Failed to map read port #%d.\n", cell_port_i
);
427 if (try_growing_more_read_ports
) {
428 log(" Growing more read ports by duplicating bram cells.\n");
429 goto grow_read_ports
;
435 dict
<SigSpec
, pair
<SigSpec
, SigSpec
>> dout_cache
;
437 for (int grid_d
= 0; grid_d
*bram
.dbits
< mem_width
; grid_d
++)
438 for (int grid_a
= 0; grid_a
*(1 << bram
.abits
) < mem_size
; grid_a
++)
439 for (int dupidx
= 0; dupidx
< dup_count
; dupidx
++)
441 Cell
*c
= module
->addCell(module
->uniquify(stringf("%s.%d.%d.%d", cell
->name
.c_str(), grid_d
, grid_a
, dupidx
)), bram
.name
);
442 log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram
.name
), grid_d
, grid_a
, dupidx
, log_id(c
));
444 dict
<int, SigBit
> clocks
;
446 for (auto &pi
: portinfos
)
448 if (pi
.dupidx
!= dupidx
)
451 string prefix
= stringf("%c%d", pi
.group
+ 'A', pi
.index
+ 1);
452 const char *pf
= prefix
.c_str();
454 if (pi
.clocks
&& (!clocks
.count(pi
.clocks
) || pi
.sig_clock
.wire
))
455 clocks
[pi
.clocks
] = pi
.sig_clock
;
458 if (GetSize(pi
.sig_addr
) > bram
.abits
) {
459 SigSpec extra_addr
= pi
.sig_addr
.extract(bram
.abits
, GetSize(pi
.sig_addr
) - bram
.abits
);
460 SigSpec extra_addr_sel
= SigSpec(grid_a
, GetSize(extra_addr
));
461 addr_ok
= module
->Eq(NEW_ID
, extra_addr
, extra_addr_sel
);
466 SigSpec sig_en
= pi
.sig_en
;
467 sig_en
.extend_u0((grid_d
+1) * pi
.enable
);
468 sig_en
= sig_en
.extract(grid_d
* pi
.enable
, pi
.enable
);
470 if (!addr_ok
.empty())
471 sig_en
= module
->Mux(NEW_ID
, SigSpec(0, GetSize(sig_en
)), sig_en
, addr_ok
);
473 c
->setPort(stringf("\\%sEN", pf
), sig_en
);
476 SigSpec sig_data
= pi
.sig_data
;
477 sig_data
.extend_u0((grid_d
+1) * bram
.dbits
);
478 sig_data
= sig_data
.extract(grid_d
* bram
.dbits
, bram
.dbits
);
480 if (pi
.wrmode
== 1) {
481 c
->setPort(stringf("\\%sDATA", pf
), sig_data
);
483 SigSpec bram_dout
= module
->addWire(NEW_ID
, bram
.dbits
);
484 c
->setPort(stringf("\\%sDATA", pf
), bram_dout
);
486 for (int i
= bram
.dbits
-1; i
>= 0; i
--)
487 if (sig_data
[i
].wire
== nullptr) {
492 SigSpec addr_ok_q
= addr_ok
;
493 if (pi
.clocks
&& !addr_ok
.empty()) {
494 addr_ok_q
= module
->addWire(NEW_ID
);
495 module
->addDff(NEW_ID
, pi
.sig_clock
, addr_ok
, addr_ok_q
, pi
.effective_clkpol
);
498 dout_cache
[sig_data
].first
.append(addr_ok_q
);
499 dout_cache
[sig_data
].second
.append(bram_dout
);
502 SigSpec sig_addr
= pi
.sig_addr
;
503 sig_addr
.extend_u0(bram
.abits
);
504 c
->setPort(stringf("\\%sADDR", pf
), sig_addr
);
507 for (auto &it
: clocks
)
508 c
->setPort(stringf("\\CLK%d", (it
.first
-1) % clocks_max
+ 1), it
.second
);
511 for (auto &it
: dout_cache
)
513 if (it
.second
.first
.empty())
515 log_assert(GetSize(it
.first
) == GetSize(it
.second
.second
));
516 module
->connect(it
.first
, it
.second
.second
);
520 log_assert(GetSize(it
.first
)*GetSize(it
.second
.first
) == GetSize(it
.second
.second
));
521 module
->addPmux(NEW_ID
, SigSpec(State::Sx
, GetSize(it
.first
)), it
.second
.second
, it
.second
.first
, it
.first
);
525 module
->remove(cell
);
529 void handle_cell(Cell
*cell
, const rules_t
&rules
)
531 log("Processing %s.%s:\n", log_id(cell
->module
), log_id(cell
));
533 dict
<string
, int> match_properties
;
534 match_properties
["words"] = cell
->getParam("\\SIZE").as_int();
535 match_properties
["abits"] = cell
->getParam("\\ABITS").as_int();
536 match_properties
["dbits"] = cell
->getParam("\\WIDTH").as_int();
537 match_properties
["wports"] = cell
->getParam("\\WR_PORTS").as_int();
538 match_properties
["rports"] = cell
->getParam("\\RD_PORTS").as_int();
539 match_properties
["bits"] = match_properties
["words"] * match_properties
["dbits"];
540 match_properties
["ports"] = match_properties
["wports"] + match_properties
["rports"];
543 for (auto &it
: match_properties
)
544 log(" %s=%d", it
.first
.c_str(), it
.second
);
547 pool
<IdString
> failed_brams
;
549 for (int i
= 0; i
< GetSize(rules
.matches
); i
++)
551 if (!rules
.brams
.count(rules
.matches
[i
].name
))
552 log_error("No bram description for resource %s found!\n", log_id(rules
.matches
[i
].name
));
554 auto &match
= rules
.matches
.at(i
);
555 auto &bram
= rules
.brams
.at(match
.name
);
557 if (match
.name
.in(failed_brams
))
560 int aover
= match_properties
["words"] % (1 << bram
.abits
);
561 int awaste
= aover
? (1 << bram
.abits
) - aover
: 0;
562 match_properties
["awaste"] = awaste
;
564 int dover
= match_properties
["dbits"] % bram
.dbits
;
565 int dwaste
= dover
? bram
.dbits
- dover
: 0;
566 match_properties
["dwaste"] = dwaste
;
568 int waste
= awaste
* bram
.dbits
+ dwaste
* (1 << bram
.abits
) - awaste
* dwaste
;
569 match_properties
["waste"] = waste
;
571 log(" Wasted bits for bram type %s: awaste=%d dwaste=%d waste=%d\n",
572 log_id(match
.name
), awaste
, dwaste
, waste
);
574 for (auto it
: match
.min_limits
) {
575 if (!match_properties
.count(it
.first
))
576 log_error("Unknown property '%s' in match rule for bram type %s.\n",
577 it
.first
.c_str(), log_id(match
.name
));
578 if (match_properties
[it
.first
] >= it
.second
)
580 log(" Rule #%d for bram type %s rejected: requirement 'min %s %d' not met.\n",
581 i
, log_id(match
.name
), it
.first
.c_str(), it
.second
);
582 goto next_match_rule
;
584 for (auto it
: match
.max_limits
) {
585 if (!match_properties
.count(it
.first
))
586 log_error("Unknown property '%s' in match rule for bram type %s.\n",
587 it
.first
.c_str(), log_id(match
.name
));
588 if (match_properties
[it
.first
] <= it
.second
)
590 log(" Rule #%d for bram type %s rejected: requirement 'max %s %d' not met.\n",
591 i
, log_id(match
.name
), it
.first
.c_str(), it
.second
);
592 goto next_match_rule
;
595 log(" Rule #%d for bram type %s accepted.\n", i
, log_id(match
.name
));
597 if (!replace_cell(cell
, bram
, match
)) {
598 log(" Mapping to bram type %s failed.\n", log_id(match
.name
));
599 failed_brams
.insert(match
.name
);
600 goto next_match_rule
;
607 log(" No acceptable bram resources found.\n");
610 struct MemoryBramPass
: public Pass
{
611 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
614 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
616 log(" memory_bram -rules <rule_file> [selection]\n");
618 log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
619 log("The given rules file describes the available resources and how they should be\n");
622 log("The rules file contains a set of block ram description and a sequence of match\n");
623 log("rules. A block ram description looks like this:\n");
625 log(" bram RAMB1024X32 # name of BRAM cell\n");
626 log(" init 1 # set to '1' if BRAM can be initialized\n");
627 log(" abits 10 # number of address bits\n");
628 log(" dbits 32 # number of data bits\n");
629 log(" groups 2 # number of port groups\n");
630 log(" ports 1 1 # number of ports in each group\n");
631 log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
632 log(" enable 4 0 # number of enable bits (for write ports)\n");
633 log(" transp 0 2 # transparatent (for read ports)\n");
634 log(" clocks 1 2 # clock configuration\n");
635 log(" clkpol 2 2 # clock polarity configuration\n");
638 log("For the option 'transp' the value 0 means non-transparent, 1 means transparent\n");
639 log("and a value greater than 1 means configurable. All groups with the same\n");
640 log("value greater than 1 share the same configuration bit.\n");
642 log("For the option 'clocks' the value 0 means non-clocked, and a value greater\n");
643 log("than 0 means clocked. All groups with the same value share the same clock\n");
646 log("For the option 'clkpol' the value 0 means negative edge, 1 means positive edge\n");
647 log("and a value greater than 1 means configurable. All groups with the same value\n");
648 log("greater than 1 share the same configuration bit.\n");
650 log("A match rule looks like this:\n");
652 log(" match RAMB1024X32\n");
653 log(" max waste 16384 # only use this if <= 16384 bram bits are unused\n");
656 log("It is possible to match against the following values with min/max rules:\n");
658 log(" words .... number of words in memory in design\n");
659 log(" abits .... number of adress bits on memory in design\n");
660 log(" dbits .... number of data bits on memory in design\n");
661 log(" wports ... number of write ports on memory in design\n");
662 log(" rports ... number of read ports on memory in design\n");
663 log(" ports .... number of ports on memory in design\n");
664 log(" bits ..... number of bits in memory in design\n");
666 log(" awaste ... number of unused address slots for this match\n");
667 log(" dwaste ... number of unused data bits for this match\n");
668 log(" waste .... total number of unused bram bits for this match\n");
670 log("The interface for the created bram instances is dervived from the bram\n");
671 log("description. Use 'techmap' to convert the created bram instances into\n");
672 log("instances of the actual bram cells of your target architecture.\n");
675 virtual void execute(vector
<string
> args
, Design
*design
)
679 log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
682 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
683 if (args
[argidx
] == "-rules" && argidx
+1 < args
.size()) {
684 rules
.parse(args
[++argidx
]);
689 extra_args(args
, argidx
, design
);
691 for (auto mod
: design
->selected_modules())
692 for (auto cell
: mod
->selected_cells())
693 if (cell
->type
== "$mem")
694 handle_cell(cell
, rules
);
698 PRIVATE_NAMESPACE_END