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
29 int wrmode
, enable
, transp
, clocks
, clkpol
;
34 int groups
, abits
, dbits
, init
;
35 vector
<int> ports
, wrmode
, enable
, transp
, clocks
, clkpol
;
37 vector
<portinfo_t
> make_portinfos() const
39 vector
<portinfo_t
> portinfos
;
40 for (int i
= 0; i
< groups
&& i
< GetSize(ports
); i
++)
41 for (int j
= 0; j
< ports
[i
]; j
++) {
45 pi
.wrmode
= i
< GetSize(wrmode
) ? wrmode
[i
] : 0;
46 pi
.enable
= i
< GetSize(enable
) ? enable
[i
] : 0;
47 pi
.transp
= i
< GetSize(transp
) ? transp
[i
] : 0;
48 pi
.clocks
= i
< GetSize(clocks
) ? clocks
[i
] : 0;
49 pi
.clkpol
= i
< GetSize(clkpol
) ? clkpol
[i
] : 0;
50 portinfos
.push_back(pi
);
58 dict
<string
, int> min_limits
, max_limits
;
61 dict
<IdString
, bram_t
> brams
;
62 vector
<match_t
> matches
;
65 vector
<string
> tokens
;
72 log_error("Unexpected end of rules file in line %d.\n", linecount
);
73 log_error("Syntax error in rules file line %d: %s\n", linecount
, line
.c_str());
80 while (std::getline(infile
, line
)) {
81 for (string tok
= next_token(line
); !tok
.empty(); tok
= next_token(line
)) {
84 tokens
.push_back(tok
);
92 bool parse_single_int(const char *stmt
, int &value
)
94 if (GetSize(tokens
) == 2 && tokens
[0] == stmt
) {
95 value
= atoi(tokens
[1].c_str());
101 bool parse_int_vect(const char *stmt
, vector
<int> &value
)
103 if (GetSize(tokens
) >= 2 && tokens
[0] == stmt
) {
104 value
.resize(GetSize(tokens
)-1);
105 for (int i
= 1; i
< GetSize(tokens
); i
++)
106 value
[i
-1] = atoi(tokens
[i
].c_str());
114 if (GetSize(tokens
) != 2)
118 data
.name
= RTLIL::escape_id(tokens
[1]);
122 if (GetSize(tokens
) == 1 && tokens
[0] == "endbram") {
123 brams
[data
.name
] = data
;
127 if (parse_single_int("groups", data
.groups
))
130 if (parse_single_int("abits", data
.abits
))
133 if (parse_single_int("dbits", data
.dbits
))
136 if (parse_single_int("init", data
.init
))
139 if (parse_int_vect("ports", data
.ports
))
142 if (parse_int_vect("wrmode", data
.wrmode
))
145 if (parse_int_vect("enable", data
.enable
))
148 if (parse_int_vect("transp", data
.transp
))
151 if (parse_int_vect("clocks", data
.clocks
))
154 if (parse_int_vect("clkpol", data
.clkpol
))
165 if (GetSize(tokens
) != 2)
169 data
.name
= RTLIL::escape_id(tokens
[1]);
173 if (GetSize(tokens
) == 1 && tokens
[0] == "endmatch") {
174 matches
.push_back(data
);
178 if (GetSize(tokens
) == 3 && tokens
[0] == "min") {
179 data
.min_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
183 if (GetSize(tokens
) == 3 && tokens
[0] == "max") {
184 data
.max_limits
[tokens
[1]] = atoi(tokens
[2].c_str());
194 void parse(std::string filename
)
196 infile
.open(filename
);
200 log_error("Can't open rules file `%s'.\n", filename
.c_str());
204 if (tokens
[0] == "bram") parse_bram();
205 else if (tokens
[0] == "match") parse_match();
213 bool replace_cell(Cell
*cell
, const rules_t::bram_t
&bram
, const rules_t::match_t
&)
215 auto portinfos
= bram
.make_portinfos();
216 dict
<int, pair
<SigBit
, bool>> clock_domains
;
217 vector
<int> mapped_wr_ports
;
219 log(" Mapping to bram type %s:\n", log_id(bram
.name
));
221 int wr_ports_n
= cell
->getParam("\\WR_PORTS").as_int();
222 auto wr_clken
= SigSpec(cell
->getParam("\\WR_CLK_ENABLE"));
223 auto wr_clkpol
= SigSpec(cell
->getParam("\\WR_CLK_POLARITY"));
224 wr_clken
.extend_u0(wr_ports_n
);
225 wr_clkpol
.extend_u0(wr_ports_n
);
227 SigSpec wr_clk
= cell
->getPort("\\WR_CLK");
228 SigSpec wr_en
= cell
->getPort("\\WR_EN");
230 for (int cell_port_i
= 0, bram_port_i
= 0; cell_port_i
< wr_ports_n
; cell_port_i
++)
232 bool clken
= wr_clken
[cell_port_i
] == State::S1
;
233 auto clkpol
= wr_clkpol
[cell_port_i
] == State::S1
;
234 auto clksig
= wr_clk
[cell_port_i
];
236 pair
<SigBit
, bool> clkdom(clksig
, clkpol
);
238 clkdom
= pair
<SigBit
, bool>(State::S1
, false);
240 log(" Write port #%d is in clock domain %s%s.\n",
241 cell_port_i
, clkdom
.second
? "" : "!",
242 clken
? log_signal(clkdom
.first
) : "~async~");
244 for (; bram_port_i
< GetSize(portinfos
); bram_port_i
++)
246 auto &pi
= portinfos
[bram_port_i
];
253 if (pi
.clocks
== 0) {
254 log(" Bram port %c%d has incompatible clock type.\n", pi
.group
+ 'A', pi
.index
+ 1);
255 goto skip_bram_wport
;
257 if (clock_domains
.count(pi
.clocks
) && clock_domains
.at(pi
.clocks
) != clkdom
) {
258 log(" Bram port %c%d is in a different clock domain.\n", pi
.group
+ 'A', pi
.index
+ 1);
259 goto skip_bram_wport
;
262 if (pi
.clocks
!= 0) {
263 log(" Bram port %c%d has incompatible clock type.\n", pi
.group
+ 'A', pi
.index
+ 1);
264 goto skip_bram_wport
;
268 SigBit last_en_bit
= State::S1
;
269 for (int i
= 0; i
< bram
.dbits
; i
++) {
270 if (pi
.enable
&& i
% (bram
.dbits
/ pi
.enable
) == 0)
271 last_en_bit
= wr_en
[i
];
272 if (last_en_bit
!= wr_en
[i
]) {
273 log(" Bram port %c%d has incompatible enable structure.\n", pi
.group
+ 'A', pi
.index
+ 1);
274 goto skip_bram_wport
;
278 log(" Mapped to bram port %c%d.\n", pi
.group
+ 'A', pi
.index
+ 1);
280 clock_domains
[pi
.clocks
] = clkdom
;
281 mapped_wr_ports
.push_back(bram_port_i
);
285 log(" Failed to map write port #%d.\n", cell_port_i
);
290 log(" FIXME: The core of memory_bram is not implemented yet.\n");
294 void handle_cell(Cell
*cell
, const rules_t
&rules
)
296 log("Processing %s.%s:\n", log_id(cell
->module
), log_id(cell
));
298 dict
<string
, int> mem_properties
;
299 mem_properties
["words"] = cell
->getParam("\\SIZE").as_int();
300 mem_properties
["abits"] = cell
->getParam("\\ABITS").as_int();
301 mem_properties
["dbits"] = cell
->getParam("\\WIDTH").as_int();
302 mem_properties
["wports"] = cell
->getParam("\\WR_PORTS").as_int();
303 mem_properties
["rports"] = cell
->getParam("\\RD_PORTS").as_int();
304 mem_properties
["bits"] = mem_properties
["words"] * mem_properties
["dbits"];
305 mem_properties
["ports"] = mem_properties
["wports"] + mem_properties
["rports"];
308 for (auto &it
: mem_properties
)
309 log(" %s=%d", it
.first
.c_str(), it
.second
);
312 pool
<IdString
> failed_brams
;
314 for (int i
= 0; i
< GetSize(rules
.matches
); i
++)
316 if (rules
.matches
[i
].name
.in(failed_brams
))
319 for (auto it
: rules
.matches
[i
].min_limits
) {
320 if (!mem_properties
.count(it
.first
))
321 log_error("Unknown property '%s' in match rule for bram type %s.\n",
322 it
.first
.c_str(), log_id(rules
.matches
[i
].name
));
323 if (mem_properties
[it
.first
] >= it
.second
)
325 log(" Rule #%d for bram type %s rejected: requirement 'min %s %d' not met.\n",
326 i
, log_id(rules
.matches
[i
].name
), it
.first
.c_str(), it
.second
);
327 goto next_match_rule
;
329 for (auto it
: rules
.matches
[i
].max_limits
) {
330 if (!mem_properties
.count(it
.first
))
331 log_error("Unknown property '%s' in match rule for bram type %s.\n",
332 it
.first
.c_str(), log_id(rules
.matches
[i
].name
));
333 if (mem_properties
[it
.first
] <= it
.second
)
335 log(" Rule #%d for bram type %s rejected: requirement 'max %s %d' not met.\n",
336 i
, log_id(rules
.matches
[i
].name
), it
.first
.c_str(), it
.second
);
337 goto next_match_rule
;
340 log(" Rule #%d for bram type %s accepted.\n", i
, log_id(rules
.matches
[i
].name
));
341 if (!rules
.brams
.count(rules
.matches
[i
].name
))
342 log_error("No bram description for resource %s found!\n", log_id(rules
.matches
[i
].name
));
344 if (!replace_cell(cell
, rules
.brams
.at(rules
.matches
[i
].name
), rules
.matches
[i
])) {
345 log(" Mapping to bram type %s failed.\n", log_id(rules
.matches
[i
].name
));
346 failed_brams
.insert(rules
.matches
[i
].name
);
347 goto next_match_rule
;
354 log(" No acceptable bram resources found.\n");
357 struct MemoryBramPass
: public Pass
{
358 MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
361 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
363 log(" memory_bram -rules <rule_file> [selection]\n");
365 log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
366 log("The given rules file describes the available resources and how they should be\n");
370 virtual void execute(vector
<string
> args
, Design
*design
)
374 log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
377 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
378 if (args
[argidx
] == "-rules" && argidx
+1 < args
.size()) {
379 rules
.parse(args
[++argidx
]);
384 extra_args(args
, argidx
, design
);
386 for (auto mod
: design
->selected_modules())
387 for (auto cell
: mod
->selected_cells())
388 if (cell
->type
== "$mem")
389 handle_cell(cell
, rules
);
393 PRIVATE_NAMESPACE_END