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/register.h"
21 #include "kernel/log.h"
29 struct generate_port_decl_t
{
36 static void generate(RTLIL::Design
*design
, const std::vector
<std::string
> &celltypes
, const std::vector
<generate_port_decl_t
> &portdecls
)
38 std::set
<std::string
> found_celltypes
;
40 for (auto i1
: design
->modules
)
41 for (auto i2
: i1
.second
->cells
)
43 RTLIL::Cell
*cell
= i2
.second
;
44 if (cell
->type
[0] == '$' || design
->modules
.count(cell
->type
) > 0)
46 for (auto &pattern
: celltypes
)
47 if (!fnmatch(pattern
.c_str(), RTLIL::unescape_id(cell
->type
).c_str(), FNM_NOESCAPE
))
48 found_celltypes
.insert(cell
->type
);
51 for (auto &celltype
: found_celltypes
)
53 std::set
<std::string
> portnames
;
54 std::set
<std::string
> parameters
;
55 std::map
<std::string
, int> portwidths
;
56 log("Generate module for cell type %s:\n", celltype
.c_str());
58 for (auto i1
: design
->modules
)
59 for (auto i2
: i1
.second
->cells
)
60 if (i2
.second
->type
== celltype
) {
61 for (auto &conn
: i2
.second
->connections
) {
62 if (conn
.first
[0] != '$')
63 portnames
.insert(conn
.first
);
64 portwidths
[conn
.first
] = std::max(portwidths
[conn
.first
], conn
.second
.size());
66 for (auto ¶
: i2
.second
->parameters
)
67 parameters
.insert(para
.first
);
70 for (auto &decl
: portdecls
)
72 portnames
.insert(decl
.portname
);
74 std::set
<int> indices
;
75 for (int i
= 0; i
< int(portnames
.size()); i
++)
78 std::vector
<generate_port_decl_t
> ports(portnames
.size());
80 for (auto &decl
: portdecls
)
82 portwidths
[decl
.portname
] = std::max(portwidths
[decl
.portname
], 1);
83 portwidths
[decl
.portname
] = std::max(portwidths
[decl
.portname
], portwidths
[stringf("$%d", decl
.index
)]);
84 log(" port %d: %s [%d:0] %s\n", decl
.index
, decl
.input
? decl
.output
? "inout" : "input" : "output", portwidths
[decl
.portname
]-1, RTLIL::id2cstr(decl
.portname
));
85 if (indices
.count(decl
.index
) > ports
.size())
86 log_error("Port index (%d) exceeds number of found ports (%d).\n", decl
.index
, int(ports
.size()));
87 if (indices
.count(decl
.index
) == 0)
88 log_error("Conflict on port index %d.\n", decl
.index
);
89 indices
.erase(decl
.index
);
90 portnames
.erase(decl
.portname
);
91 ports
[decl
.index
-1] = decl
;
94 while (portnames
.size() > 0) {
95 std::string portname
= *portnames
.begin();
96 for (auto &decl
: portdecls
)
97 if (decl
.index
== 0 && !fnmatch(decl
.portname
.c_str(), RTLIL::unescape_id(portname
).c_str(), FNM_NOESCAPE
)) {
98 generate_port_decl_t d
= decl
;
99 d
.portname
= portname
;
100 d
.index
= *indices
.begin();
101 assert(!indices
.empty());
102 indices
.erase(d
.index
);
103 ports
[d
.index
-1] = d
;
104 portwidths
[d
.portname
] = std::max(portwidths
[d
.portname
], 1);
105 log(" port %d: %s [%d:0] %s\n", d
.index
, d
.input
? d
.output
? "inout" : "input" : "output", portwidths
[d
.portname
]-1, RTLIL::id2cstr(d
.portname
));
106 goto found_matching_decl
;
108 log_error("Can't match port %s.\n", RTLIL::id2cstr(portname
));
109 found_matching_decl
:;
110 portnames
.erase(portname
);
113 assert(indices
.empty());
115 RTLIL::Module
*mod
= new RTLIL::Module
;
116 mod
->name
= celltype
;
117 mod
->attributes
["\\blackbox"] = RTLIL::Const(1);
118 design
->modules
[mod
->name
] = mod
;
120 for (auto &decl
: ports
) {
121 RTLIL::Wire
*wire
= new RTLIL::Wire
;
122 wire
->name
= decl
.portname
;
123 wire
->width
= portwidths
.at(decl
.portname
);
124 wire
->port_id
= decl
.index
;
125 wire
->port_input
= decl
.input
;
126 wire
->port_output
= decl
.output
;
130 for (auto ¶
: parameters
)
131 log(" ignoring parameter %s.\n", RTLIL::id2cstr(para
));
133 log(" module %s created.\n", RTLIL::id2cstr(mod
->name
));
137 static bool expand_module(RTLIL::Design
*design
, RTLIL::Module
*module
, bool flag_check
, std::vector
<std::string
> &libdirs
)
139 bool did_something
= false;
140 std::map
<RTLIL::Cell
*, std::pair
<int, int>> array_cells
;
141 std::string filename
;
143 for (auto &cell_it
: module
->cells
)
145 RTLIL::Cell
*cell
= cell_it
.second
;
147 if (cell
->type
.substr(0, 7) == "$array:") {
148 int pos_idx
= cell
->type
.find_first_of(':');
149 int pos_num
= cell
->type
.find_first_of(':', pos_idx
+ 1);
150 int pos_type
= cell
->type
.find_first_of(':', pos_num
+ 1);
151 int idx
= atoi(cell
->type
.substr(pos_idx
+ 1, pos_num
).c_str());
152 int num
= atoi(cell
->type
.substr(pos_num
+ 1, pos_type
).c_str());
153 array_cells
[cell
] = std::pair
<int, int>(idx
, num
);
154 cell
->type
= cell
->type
.substr(pos_type
+ 1);
157 if (design
->modules
.count(cell
->type
) == 0)
159 if (design
->modules
.count("$abstract" + cell
->type
))
161 cell
->type
= design
->modules
.at("$abstract" + cell
->type
)->derive(design
, cell
->parameters
);
162 cell
->parameters
.clear();
163 did_something
= true;
167 if (cell
->type
[0] == '$')
170 for (auto &dir
: libdirs
)
172 filename
= dir
+ "/" + RTLIL::unescape_id(cell
->type
) + ".v";
173 if (access(filename
.c_str(), F_OK
) == 0) {
174 std::vector
<std::string
> args
;
175 args
.push_back(filename
);
176 Frontend::frontend_call(design
, NULL
, filename
, "verilog");
180 filename
= dir
+ "/" + RTLIL::unescape_id(cell
->type
) + ".il";
181 if (access(filename
.c_str(), F_OK
) == 0) {
182 std::vector
<std::string
> args
;
183 args
.push_back(filename
);
184 Frontend::frontend_call(design
, NULL
, filename
, "ilang");
189 if (flag_check
&& cell
->type
[0] != '$')
190 log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n",
191 cell
->type
.c_str(), module
->name
.c_str(), cell
->name
.c_str());
195 if (design
->modules
.count(cell
->type
) == 0)
196 log_error("File `%s' from libdir does not declare module `%s'.\n", filename
.c_str(), cell
->type
.c_str());
197 did_something
= true;
200 if (cell
->parameters
.size() == 0)
203 if (design
->modules
.at(cell
->type
)->get_bool_attribute("\\blackbox"))
206 RTLIL::Module
*mod
= design
->modules
[cell
->type
];
207 cell
->type
= mod
->derive(design
, cell
->parameters
);
208 cell
->parameters
.clear();
209 did_something
= true;
212 for (auto &it
: array_cells
)
214 RTLIL::Cell
*cell
= it
.first
;
215 int idx
= it
.second
.first
, num
= it
.second
.second
;
217 if (design
->modules
.count(cell
->type
) == 0)
218 log_error("Array cell `%s.%s' of unkown type `%s'.\n", RTLIL::id2cstr(module
->name
), RTLIL::id2cstr(cell
->name
), RTLIL::id2cstr(cell
->type
));
220 RTLIL::Module
*mod
= design
->modules
[cell
->type
];
222 for (auto &conn
: cell
->connections
) {
223 int conn_size
= conn
.second
.size();
224 std::string portname
= conn
.first
;
225 if (portname
.substr(0, 1) == "$") {
226 int port_id
= atoi(portname
.substr(1).c_str());
227 for (auto &wire_it
: mod
->wires
)
228 if (wire_it
.second
->port_id
== port_id
) {
229 portname
= wire_it
.first
;
233 if (mod
->wires
.count(portname
) == 0)
234 log_error("Array cell `%s.%s' connects to unkown port `%s'.\n", RTLIL::id2cstr(module
->name
), RTLIL::id2cstr(cell
->name
), RTLIL::id2cstr(conn
.first
));
235 int port_size
= mod
->wires
.at(portname
)->width
;
236 if (conn_size
== port_size
)
238 if (conn_size
!= port_size
*num
)
239 log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module
->name
), RTLIL::id2cstr(cell
->name
), RTLIL::id2cstr(conn
.first
));
240 conn
.second
= conn
.second
.extract(port_size
*idx
, port_size
);
244 return did_something
;
247 static void hierarchy_worker(RTLIL::Design
*design
, std::set
<RTLIL::Module
*> &used
, RTLIL::Module
*mod
, int indent
)
249 if (used
.count(mod
) > 0)
253 log("Top module: %s\n", mod
->name
.c_str());
255 log("Used module: %*s%s\n", indent
, "", mod
->name
.c_str());
258 for (auto &it
: mod
->cells
) {
259 if (design
->modules
.count(it
.second
->type
) > 0)
260 hierarchy_worker(design
, used
, design
->modules
[it
.second
->type
], indent
+4);
264 static void hierarchy(RTLIL::Design
*design
, RTLIL::Module
*top
, bool purge_lib
, bool first_pass
)
266 std::set
<RTLIL::Module
*> used
;
267 hierarchy_worker(design
, used
, top
, 0);
269 std::vector
<RTLIL::Module
*> del_modules
;
270 for (auto &it
: design
->modules
)
271 if (used
.count(it
.second
) == 0)
272 del_modules
.push_back(it
.second
);
274 for (auto mod
: del_modules
) {
275 if (first_pass
&& mod
->name
.substr(0, 9) == "$abstract")
277 if (!purge_lib
&& mod
->get_bool_attribute("\\blackbox"))
279 log("Removing unused module `%s'.\n", mod
->name
.c_str());
280 design
->modules
.erase(mod
->name
);
284 log("Removed %zd unused modules.\n", del_modules
.size());
287 struct HierarchyPass
: public Pass
{
288 HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
291 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
293 log(" hierarchy [-check] [-top <module>]\n");
294 log(" hierarchy -generate <cell-types> <port-decls>\n");
296 log("In parametric designs, a module might exists in serveral variations with\n");
297 log("different parameter values. This pass looks at all modules in the current\n");
298 log("design an re-runs the language frontends for the parametric modules as\n");
302 log(" also check the design hierarchy. this generates an error when\n");
303 log(" an unknown module is used as cell type.\n");
305 log(" -purge_lib\n");
306 log(" by default the hierarchy command will not remove library (blackbox)\n");
307 log(" module. use this options to also remove unused blackbox modules.\n");
309 log(" -libdir <directory>\n");
310 log(" search for files named <module_name>.v in the specified directory\n");
311 log(" for unkown modules and automatically run read_verilog for each\n");
312 log(" unknown module.\n");
314 log(" -keep_positionals\n");
315 log(" per default this pass also converts positional arguments in cells\n");
316 log(" to arguments using port names. this option disables this behavior.\n");
318 log(" -top <module>\n");
319 log(" use the specified top module to built a design hierarchy. modules\n");
320 log(" outside this tree (unused modules) are removed.\n");
322 log(" when the -top option is used, the 'top' attribute will be set on the\n");
323 log(" specified top module. otherwise a module with the 'top' attribute set\n");
324 log(" will implicitly be used as top module, if such a module exists.\n");
326 log("In -generate mode this pass generates blackbox modules for the given cell\n");
327 log("types (wildcards supported). For this the design is searched for cells that\n");
328 log("match the given types and then the given port declarations are used to\n");
329 log("determine the direction of the ports. The syntax for a port declaration is:\n");
331 log(" {i|o|io}[@<num>]:<portname>\n");
333 log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n");
334 log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n");
335 log("the position of the port in the parameter list (needed when instanciated\n");
336 log("using positional arguments). When <num> is not specified, the <portname> can\n");
337 log("also contain wildcard characters.\n");
339 log("This pass ignores the current selection and always operates on all modules\n");
340 log("in the current design.\n");
343 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
345 log_header("Executing HIERARCHY pass (managing design hierarchy).\n");
347 bool flag_check
= false;
348 bool purge_lib
= false;
349 RTLIL::Module
*top_mod
= NULL
;
350 std::vector
<std::string
> libdirs
;
352 bool generate_mode
= false;
353 bool keep_positionals
= false;
354 std::vector
<std::string
> generate_cells
;
355 std::vector
<generate_port_decl_t
> generate_ports
;
358 for (argidx
= 1; argidx
< args
.size(); argidx
++)
360 if (args
[argidx
] == "-generate" && !flag_check
&& !top_mod
) {
361 generate_mode
= true;
362 log("Entering generate mode.\n");
363 while (++argidx
< args
.size()) {
364 const char *p
= args
[argidx
].c_str();
365 generate_port_decl_t decl
;
366 if (p
[0] == 'i' && p
[1] == 'o')
367 decl
.input
= true, decl
.output
= true, p
+= 2;
369 decl
.input
= true, decl
.output
= false, p
++;
371 decl
.input
= false, decl
.output
= true, p
++;
376 decl
.index
= strtol(++p
, &endptr
, 10);
387 log("Port declaration: %s", decl
.input
? decl
.output
? "inout" : "input" : "output");
389 log(" [at position %d]", decl
.index
);
390 log(" %s\n", decl
.portname
.c_str());
391 generate_ports
.push_back(decl
);
394 log("Celltype: %s\n", args
[argidx
].c_str());
395 generate_cells
.push_back(RTLIL::unescape_id(args
[argidx
]));
399 if (args
[argidx
] == "-check") {
403 if (args
[argidx
] == "-purge_lib") {
407 if (args
[argidx
] == "-keep_positionals") {
408 keep_positionals
= true;
411 if (args
[argidx
] == "-libdir" && argidx
+1 < args
.size()) {
412 libdirs
.push_back(args
[++argidx
]);
415 if (args
[argidx
] == "-top") {
416 if (++argidx
>= args
.size())
417 log_cmd_error("Option -top requires an additional argument!\n");
418 top_mod
= design
->modules
.count(RTLIL::escape_id(args
[argidx
])) ? design
->modules
.at(RTLIL::escape_id(args
[argidx
])) : NULL
;
419 if (top_mod
== NULL
&& design
->modules
.count("$abstract" + RTLIL::escape_id(args
[argidx
]))) {
420 std::map
<RTLIL::IdString
, RTLIL::Const
> empty_parameters
;
421 design
->modules
.at("$abstract" + RTLIL::escape_id(args
[argidx
]))->derive(design
, empty_parameters
);
422 top_mod
= design
->modules
.count(RTLIL::escape_id(args
[argidx
])) ? design
->modules
.at(RTLIL::escape_id(args
[argidx
])) : NULL
;
425 log_cmd_error("Module `%s' not found!\n", args
[argidx
].c_str());
430 extra_args(args
, argidx
, design
, false);
433 generate(design
, generate_cells
, generate_ports
);
440 for (auto &mod_it
: design
->modules
)
441 if (mod_it
.second
->get_bool_attribute("\\top"))
442 top_mod
= mod_it
.second
;
445 hierarchy(design
, top_mod
, purge_lib
, true);
447 bool did_something
= true;
448 bool did_something_once
= false;
449 while (did_something
) {
450 did_something
= false;
451 std::vector
<std::string
> modnames
;
452 modnames
.reserve(design
->modules
.size());
453 for (auto &mod_it
: design
->modules
)
454 modnames
.push_back(mod_it
.first
);
455 for (auto &modname
: modnames
) {
456 if (design
->modules
.count(modname
) == 0)
458 if (expand_module(design
, design
->modules
[modname
], flag_check
, libdirs
))
459 did_something
= true;
462 did_something_once
= true;
465 if (top_mod
!= NULL
&& did_something_once
) {
466 log_header("Re-running hierarchy analysis..\n");
467 hierarchy(design
, top_mod
, purge_lib
, false);
470 if (top_mod
!= NULL
) {
471 for (auto &mod_it
: design
->modules
)
472 if (mod_it
.second
== top_mod
)
473 mod_it
.second
->attributes
["\\top"] = RTLIL::Const(1);
475 mod_it
.second
->attributes
.erase("\\top");
478 if (!keep_positionals
)
480 std::set
<RTLIL::Module
*> pos_mods
;
481 std::map
<std::pair
<RTLIL::Module
*,int>, RTLIL::IdString
> pos_map
;
482 std::vector
<std::pair
<RTLIL::Module
*,RTLIL::Cell
*>> pos_work
;
484 for (auto &mod_it
: design
->modules
)
485 for (auto &cell_it
: mod_it
.second
->cells
) {
486 RTLIL::Cell
*cell
= cell_it
.second
;
487 if (design
->modules
.count(cell
->type
) == 0)
489 for (auto &conn
: cell
->connections
)
490 if (conn
.first
[0] == '$' && '0' <= conn
.first
[1] && conn
.first
[1] <= '9') {
491 pos_mods
.insert(design
->modules
.at(cell
->type
));
492 pos_work
.push_back(std::pair
<RTLIL::Module
*,RTLIL::Cell
*>(mod_it
.second
, cell
));
497 for (auto module
: pos_mods
)
498 for (auto &wire_it
: module
->wires
) {
499 RTLIL::Wire
*wire
= wire_it
.second
;
500 if (wire
->port_id
> 0)
501 pos_map
[std::pair
<RTLIL::Module
*,int>(module
, wire
->port_id
)] = wire
->name
;
504 for (auto &work
: pos_work
) {
505 RTLIL::Module
*module
= work
.first
;
506 RTLIL::Cell
*cell
= work
.second
;
507 log("Mapping positional arguments of cell %s.%s (%s).\n",
508 RTLIL::id2cstr(module
->name
), RTLIL::id2cstr(cell
->name
), RTLIL::id2cstr(cell
->type
));
509 std::map
<RTLIL::IdString
, RTLIL::SigSpec
> new_connections
;
510 for (auto &conn
: cell
->connections
)
511 if (conn
.first
[0] == '$' && '0' <= conn
.first
[1] && conn
.first
[1] <= '9') {
512 int id
= atoi(conn
.first
.c_str()+1);
513 std::pair
<RTLIL::Module
*,int> key(design
->modules
.at(cell
->type
), id
);
514 if (pos_map
.count(key
) == 0) {
515 log(" Failed to map positional argument %d of cell %s.%s (%s).\n",
516 id
, RTLIL::id2cstr(module
->name
), RTLIL::id2cstr(cell
->name
), RTLIL::id2cstr(cell
->type
));
517 new_connections
[conn
.first
] = conn
.second
;
519 new_connections
[pos_map
.at(key
)] = conn
.second
;
521 new_connections
[conn
.first
] = conn
.second
;
522 cell
->connections
= new_connections
;