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/rtlil.h"
21 #include "kernel/register.h"
22 #include "kernel/sigtools.h"
23 #include "kernel/celltypes.h"
24 #include "kernel/log.h"
28 PRIVATE_NAMESPACE_BEGIN
34 RTLIL::Module
*module
;
39 dict
<IdString
, shared_str
> idcache
;
40 pool
<shared_str
> used_names
;
41 vector
<shared_str
> strbuf
;
43 struct assign_rhs_chunk
{
46 bool operator<(const assign_rhs_chunk
&other
) const { return offset
< other
.offset
; }
49 dict
<Wire
*, vector
<assign_rhs_chunk
>> partial_assignments
;
50 vector
<string
> assignments
;
55 shared_str
s(stringf("_%d", idcounter
++));
56 if (!used_names
.count(s
)) {
63 const char *cid(IdString id
, bool precache
= false)
65 if (!idcache
.count(id
))
67 string name
= stringf("_%s", id
.c_str());
69 if (name
.substr(0, 2) == "_\\")
70 name
= "_" + name
.substr(2);
72 for (auto &c
: name
) {
73 if (c
== '|' || c
== '$' || c
== '_') continue;
74 if (c
>= 'a' && c
<='z') continue;
75 if (c
>= 'A' && c
<='Z') continue;
76 if (c
>= '0' && c
<='9') continue;
77 if (precache
) return nullptr;
84 while (used_names
.count(name
))
92 return idcache
.at(id
).c_str();
95 SmvWorker(RTLIL::Module
*module
, bool verbose
, std::ostream
&f
) :
96 ct(module
->design
), sigmap(module
), module(module
), f(f
), verbose(verbose
), idcounter(0)
98 for (auto mod
: module
->design
->modules())
101 for (auto wire
: module
->wires())
102 cid(wire
->name
, true);
104 for (auto cell
: module
->cells()) {
105 cid(cell
->name
, true);
106 cid(cell
->type
, true);
107 for (auto &conn
: cell
->connections())
108 cid(conn
.first
, true);
112 const char *rvalue(SigSpec sig
, bool skip_sigmap
= false, int width
= -1, bool is_signed
= false)
118 for (auto &c
: sig
.chunks()) {
122 if (c
.offset
!= 0 || c
.width
!= c
.wire
->width
)
123 s
= stringf("%s[%d:%d]", cid(c
.wire
->name
), c
.offset
+c
.width
-1, c
.offset
) + s
;
125 s
= cid(c
.wire
->name
) + s
;
127 string v
= stringf("0ub%d_", c
.width
);
128 for (int i
= c
.width
-1; i
>= 0; i
--)
129 v
+= c
.data
.at(i
) == State::S1
? '1' : '0';
136 if (GetSize(sig
) > width
)
137 s
= stringf("signed(resize(%s, %d))", s
.c_str(), width
);
139 s
= stringf("resize(signed(%s), %d)", s
.c_str(), width
);
141 s
= stringf("resize(%s, %d)", s
.c_str(), width
);
142 } else if (is_signed
)
143 s
= stringf("signed(%s)", s
.c_str());
146 return strbuf
.back().c_str();
149 const char *rvalue_u(SigSpec sig
, int width
= -1)
151 return rvalue(sig
, false, width
, false);
154 const char *rvalue_s(SigSpec sig
, int width
= -1, bool is_signed
= true)
156 return rvalue(sig
, false, width
, is_signed
);
159 const char *lvalue(SigSpec sig
, bool skip_sigmap
= false)
165 return rvalue(sig
, true);
167 const char *temp_id
= cid();
168 f
<< stringf(" %s : unsigned word[%d]; -- %s\n", temp_id
, GetSize(sig
), log_signal(sig
));
171 for (auto &c
: sig
.chunks())
173 log_assert(c
.wire
!= nullptr);
175 assign_rhs_chunk rhs
;
176 if (offset
!= 0 || c
.width
!= GetSize(sig
))
177 rhs
.rhs_expr
= stringf("%s[%d:%d]", temp_id
, offset
+c
.width
-1, offset
);
179 rhs
.rhs_expr
= temp_id
;
180 rhs
.offset
= c
.offset
;
183 partial_assignments
[c
.wire
].push_back(rhs
);
192 f
<< stringf("MODULE %s\n", cid(module
->name
));
193 f
<< stringf(" VAR\n");
195 for (auto wire
: module
->wires())
197 SigSpec this_sig
= wire
, driver_sig
= sigmap(wire
);
198 SigSpec unused_bits_in_this_sig
;
199 SigSpec driver_for_unused_bits
;
201 for (int i
= 0; i
< GetSize(this_sig
); i
++)
202 if (this_sig
[i
] != driver_sig
[i
]) {
203 unused_bits_in_this_sig
.append(this_sig
[i
]);
204 driver_for_unused_bits
.append(driver_sig
[i
]);
207 if (GetSize(unused_bits_in_this_sig
) == GetSize(this_sig
))
209 f
<< stringf(" -- unused -- %s : unsigned word[%d]; -- %s\n", cid(wire
->name
), wire
->width
, log_id(wire
));
213 f
<< stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire
->name
), wire
->width
, log_id(wire
));
215 if (!unused_bits_in_this_sig
.empty())
216 assignments
.push_back(stringf("%s := 0ub%d_0; -- unused %s -- using %s instead", lvalue(unused_bits_in_this_sig
, true),
217 GetSize(unused_bits_in_this_sig
), log_signal(unused_bits_in_this_sig
), log_signal(driver_for_unused_bits
)));
221 for (auto cell
: module
->cells())
223 // FIXME: $slice, $concat, $mem
225 if (cell
->type
.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx"))
227 int width_y
= GetSize(cell
->getPort("\\Y"));
228 int width
= std::max(GetSize(cell
->getPort("\\A")), width_y
);
229 bool signed_a
= cell
->getParam("\\A_SIGNED").as_bool();
230 string op
= cell
->type
.in("$shl", "$sshl") ? "<<" : ">>";
234 expr_a
= stringf("resize(signed(%s), %d)", rvalue(cell
->getPort("\\A")), width
);
236 expr_a
= stringf("resize(%s, %d)", rvalue(cell
->getPort("\\A")), width
);
238 if (cell
->type
== "$sshr" && signed_a
) {
239 expr
= stringf("resize(%s %s %s, %d)", expr_a
.c_str(), op
.c_str(), rvalue(cell
->getPort("\\B")), width_y
);
242 expr_a
= "unsigned(" + expr_a
+ ")";
243 if (cell
->type
.in("$shift", "$shiftx") && cell
->getParam("\\B_SIGNED").as_bool())
244 expr
= stringf("resize(%s %s signed(%s), %d)", expr_a
.c_str(), op
.c_str(), rvalue(cell
->getPort("\\B")), width_y
);
246 expr
= stringf("resize(%s %s %s, %d)", expr_a
.c_str(), op
.c_str(), rvalue(cell
->getPort("\\B")), width_y
);
249 assignments
.push_back(stringf("%s := %s;", lvalue(cell
->getPort("\\Y")), expr
.c_str()));
254 if (cell
->type
.in("$not", "$pos", "$neg"))
256 int width
= GetSize(cell
->getPort("\\Y"));
259 if (cell
->type
== "$not") op
= "!";
260 if (cell
->type
== "$pos") op
= "";
261 if (cell
->type
== "$neg") op
= "-";
263 if (cell
->getParam("\\A_SIGNED").as_bool())
265 assignments
.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell
->getPort("\\Y")),
266 op
.c_str(), rvalue_s(cell
->getPort("\\A"), width
)));
270 assignments
.push_back(stringf("%s := %s%s;", lvalue(cell
->getPort("\\Y")),
271 op
.c_str(), rvalue_u(cell
->getPort("\\A"), width
)));
277 if (cell
->type
.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor"))
279 int width
= GetSize(cell
->getPort("\\Y"));
280 string expr_a
, expr_b
, op
;
282 if (cell
->type
== "$add") op
= "+";
283 if (cell
->type
== "$sub") op
= "-";
284 if (cell
->type
== "$mul") op
= "*";
285 if (cell
->type
== "$and") op
= "&";
286 if (cell
->type
== "$or") op
= "|";
287 if (cell
->type
== "$xor") op
= "xor";
288 if (cell
->type
== "$xnor") op
= "xnor";
290 if (cell
->getParam("\\A_SIGNED").as_bool())
292 assignments
.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell
->getPort("\\Y")),
293 rvalue_s(cell
->getPort("\\A"), width
), op
.c_str(), rvalue_s(cell
->getPort("\\B"), width
)));
297 assignments
.push_back(stringf("%s := %s %s %s;", lvalue(cell
->getPort("\\Y")),
298 rvalue_u(cell
->getPort("\\A"), width
), op
.c_str(), rvalue_u(cell
->getPort("\\B"), width
)));
304 if (cell
->type
.in("$div", "$mod"))
306 int width_y
= GetSize(cell
->getPort("\\Y"));
307 int width
= std::max(width_y
, GetSize(cell
->getPort("\\A")));
308 width
= std::max(width
, GetSize(cell
->getPort("\\B")));
309 string expr_a
, expr_b
, op
;
311 if (cell
->type
== "$div") op
= "/";
312 if (cell
->type
== "$mod") op
= "mod";
314 if (cell
->getParam("\\A_SIGNED").as_bool())
316 assignments
.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell
->getPort("\\Y")),
317 rvalue_s(cell
->getPort("\\A"), width
), op
.c_str(), rvalue_s(cell
->getPort("\\B"), width
), width_y
));
321 assignments
.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell
->getPort("\\Y")),
322 rvalue_u(cell
->getPort("\\A"), width
), op
.c_str(), rvalue_u(cell
->getPort("\\B"), width
), width_y
));
328 if (cell
->type
.in("$eq", "$ne", "$eqx", "$nex", "$lt", "$le", "$ge", "$gt"))
330 int width
= std::max(GetSize(cell
->getPort("\\A")), GetSize(cell
->getPort("\\B")));
331 string expr_a
, expr_b
, op
;
333 if (cell
->type
== "$eq") op
= "=";
334 if (cell
->type
== "$ne") op
= "!=";
335 if (cell
->type
== "$eqx") op
= "=";
336 if (cell
->type
== "$nex") op
= "!=";
337 if (cell
->type
== "$lt") op
= "<";
338 if (cell
->type
== "$le") op
= "<=";
339 if (cell
->type
== "$ge") op
= ">=";
340 if (cell
->type
== "$gt") op
= ">";
342 if (cell
->getParam("\\A_SIGNED").as_bool())
344 expr_a
= stringf("resize(signed(%s), %d)", rvalue(cell
->getPort("\\A")), width
);
345 expr_b
= stringf("resize(signed(%s), %d)", rvalue(cell
->getPort("\\B")), width
);
349 expr_a
= stringf("resize(%s, %d)", rvalue(cell
->getPort("\\A")), width
);
350 expr_b
= stringf("resize(%s, %d)", rvalue(cell
->getPort("\\B")), width
);
353 assignments
.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell
->getPort("\\Y")),
354 expr_a
.c_str(), op
.c_str(), expr_b
.c_str(), GetSize(cell
->getPort("\\Y"))));
359 if (cell
->type
.in("$reduce_and", "$reduce_or", "$reduce_bool"))
361 int width_a
= GetSize(cell
->getPort("\\A"));
362 int width_y
= GetSize(cell
->getPort("\\Y"));
363 const char *expr_a
= rvalue(cell
->getPort("\\A"));
364 const char *expr_y
= lvalue(cell
->getPort("\\Y"));
367 if (cell
->type
== "$reduce_and") expr
= stringf("%s = !0ub%d_0", expr_a
, width_a
);
368 if (cell
->type
== "$reduce_or") expr
= stringf("%s != 0ub%d_0", expr_a
, width_a
);
369 if (cell
->type
== "$reduce_bool") expr
= stringf("%s != 0ub%d_0", expr_a
, width_a
);
371 assignments
.push_back(stringf("%s := resize(word1(%s), %d);", expr_y
, expr
.c_str(), width_y
));
375 if (cell
->type
.in("$reduce_xor", "$reduce_xnor"))
377 int width_y
= GetSize(cell
->getPort("\\Y"));
378 const char *expr_y
= lvalue(cell
->getPort("\\Y"));
381 for (auto bit
: cell
->getPort("\\A")) {
387 if (cell
->type
== "$reduce_xnor")
388 expr
= "!(" + expr
+ ")";
390 assignments
.push_back(stringf("%s := resize(%s, %d);", expr_y
, expr
.c_str(), width_y
));
394 if (cell
->type
.in("$logic_and", "$logic_or"))
396 int width_a
= GetSize(cell
->getPort("\\A"));
397 int width_b
= GetSize(cell
->getPort("\\B"));
398 int width_y
= GetSize(cell
->getPort("\\Y"));
400 string expr_a
= stringf("(%s != 0ub%d_0)", rvalue(cell
->getPort("\\A")), width_a
);
401 string expr_b
= stringf("(%s != 0ub%d_0)", rvalue(cell
->getPort("\\B")), width_b
);
402 const char *expr_y
= lvalue(cell
->getPort("\\Y"));
405 if (cell
->type
== "$logic_and") expr
= expr_a
+ " & " + expr_b
;
406 if (cell
->type
== "$logic_or") expr
= expr_a
+ " | " + expr_b
;
408 assignments
.push_back(stringf("%s := resize(word1(%s), %d);", expr_y
, expr
.c_str(), width_y
));
412 if (cell
->type
.in("$logic_not"))
414 int width_a
= GetSize(cell
->getPort("\\A"));
415 int width_y
= GetSize(cell
->getPort("\\Y"));
417 string expr_a
= stringf("(%s = 0ub%d_0)", rvalue(cell
->getPort("\\A")), width_a
);
418 const char *expr_y
= lvalue(cell
->getPort("\\Y"));
420 assignments
.push_back(stringf("%s := resize(word1(%s), %d);", expr_y
, expr_a
.c_str(), width_y
));
424 if (cell
->type
.in("$mux", "$pmux"))
426 int width
= GetSize(cell
->getPort("\\Y"));
427 SigSpec sig_a
= cell
->getPort("\\A");
428 SigSpec sig_b
= cell
->getPort("\\B");
429 SigSpec sig_s
= cell
->getPort("\\S");
432 for (int i
= 0; i
< GetSize(sig_s
); i
++)
433 expr
+= stringf("bool(%s) ? %s : ", rvalue(sig_s
[i
]), rvalue(sig_b
.extract(i
*width
, width
)));
434 expr
+= rvalue(sig_a
);
436 assignments
.push_back(stringf("%s := %s;", lvalue(cell
->getPort("\\Y")), expr
.c_str()));
440 if (cell
->type
== "$dff")
442 // FIXME: use init property
443 assignments
.push_back(stringf("next(%s) := %s;", lvalue(cell
->getPort("\\Q")), rvalue(cell
->getPort("\\D"))));
447 if (cell
->type
.in("$_BUF_", "$_NOT_"))
449 string op
= cell
->type
== "$_NOT_" ? "!" : "";
450 assignments
.push_back(stringf("%s := %s%s;", lvalue(cell
->getPort("\\Y")), op
.c_str(), rvalue(cell
->getPort("\\A"))));
454 if (cell
->type
.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
458 if (cell
->type
.in("$_AND_", "$_NAND_")) op
= "&";
459 if (cell
->type
.in("$_OR_", "$_NOR_")) op
= "|";
460 if (cell
->type
.in("$_XOR_")) op
= "xor";
461 if (cell
->type
.in("$_XNOR_")) op
= "xnor";
463 if (cell
->type
.in("$_NAND_", "$_NOR_"))
464 assignments
.push_back(stringf("%s := !(%s %s %s);", lvalue(cell
->getPort("\\Y")),
465 rvalue(cell
->getPort("\\A")), op
.c_str(), rvalue(cell
->getPort("\\B"))));
467 assignments
.push_back(stringf("%s := %s %s %s;", lvalue(cell
->getPort("\\Y")),
468 rvalue(cell
->getPort("\\A")), op
.c_str(), rvalue(cell
->getPort("\\B"))));
472 if (cell
->type
== "$_MUX_")
474 assignments
.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell
->getPort("\\Y")),
475 rvalue(cell
->getPort("\\S")), rvalue(cell
->getPort("\\B")), rvalue(cell
->getPort("\\A"))));
479 if (cell
->type
== "$_AOI3_")
481 assignments
.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell
->getPort("\\Y")),
482 rvalue(cell
->getPort("\\A")), rvalue(cell
->getPort("\\B")), rvalue(cell
->getPort("\\C"))));
486 if (cell
->type
== "$_OAI3_")
488 assignments
.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell
->getPort("\\Y")),
489 rvalue(cell
->getPort("\\A")), rvalue(cell
->getPort("\\B")), rvalue(cell
->getPort("\\C"))));
493 if (cell
->type
== "$_AOI4_")
495 assignments
.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell
->getPort("\\Y")),
496 rvalue(cell
->getPort("\\A")), rvalue(cell
->getPort("\\B")), rvalue(cell
->getPort("\\C")), rvalue(cell
->getPort("\\D"))));
500 if (cell
->type
== "$_OAI4_")
502 assignments
.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell
->getPort("\\Y")),
503 rvalue(cell
->getPort("\\A")), rvalue(cell
->getPort("\\B")), rvalue(cell
->getPort("\\C")), rvalue(cell
->getPort("\\D"))));
507 if (cell
->type
[0] == '$')
508 log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell
->type
), log_id(module
), log_id(cell
));
510 f
<< stringf(" %s : %s;\n", cid(cell
->name
), cid(cell
->type
));
512 for (auto &conn
: cell
->connections())
513 if (cell
->output(conn
.first
))
514 assignments
.push_back(stringf("%s := %s.%s;", lvalue(conn
.second
), cid(cell
->name
), cid(conn
.first
)));
516 assignments
.push_back(stringf("%s.%s := %s;", cid(cell
->name
), cid(conn
.first
), rvalue(conn
.second
)));
519 for (auto &it
: partial_assignments
)
521 std::sort(it
.second
.begin(), it
.second
.end());
525 for (auto rhs
: it
.second
) {
527 expr
= " :: " + expr
;
528 if (offset
< rhs
.offset
)
529 expr
= stringf(" :: 0ub%d_0 ", rhs
.offset
- offset
) + expr
;
530 expr
= rhs
.rhs_expr
+ expr
;
531 offset
= rhs
.offset
+ rhs
.width
;
533 if (offset
< it
.first
->width
)
534 expr
= stringf("0ub%d_0 :: ", it
.first
->width
- offset
) + expr
;
535 assignments
.push_back(stringf("%s := %s;", cid(it
.first
->name
), expr
.c_str()));
538 f
<< stringf(" ASSIGN\n");
539 for (const string
&line
: assignments
)
540 f
<< stringf(" %s\n", line
.c_str());
544 struct SmvBackend
: public Backend
{
545 SmvBackend() : Backend("smv", "write design to SMV file") { }
548 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
550 log(" write_smv [options] [filename]\n");
552 log("Write an SMV description of the current design.\n");
555 log(" this will print the recursive walk used to export the modules.\n");
557 log(" -tpl <template_file>\n");
558 log(" use the given template file. the line containing only the token '%%%%'\n");
559 log(" is replaced with the regular output of this command.\n");
561 log("THIS COMMAND IS UNDER CONSTRUCTION\n");
564 virtual void execute(std::ostream
*&f
, std::string filename
, std::vector
<std::string
> args
, RTLIL::Design
*design
)
566 std::ifstream template_f
;
567 bool verbose
= false;
569 log_header("Executing SMV backend.\n");
572 for (argidx
= 1; argidx
< args
.size(); argidx
++)
574 if (args
[argidx
] == "-tpl" && argidx
+1 < args
.size()) {
575 template_f
.open(args
[++argidx
]);
576 if (template_f
.fail())
577 log_error("Can't open template file `%s'.\n", args
[argidx
].c_str());
580 if (args
[argidx
] == "-verbose") {
586 extra_args(f
, filename
, args
, argidx
);
588 pool
<Module
*> modules
;
590 for (auto module
: design
->modules())
591 if (!module
->get_bool_attribute("\\blackbox") && !module
->has_memories_warn() && !module
->has_processes_warn())
592 modules
.insert(module
);
594 if (template_f
.is_open())
597 while (std::getline(template_f
, line
))
600 while (indent
< GetSize(line
) && (line
[indent
] == ' ' || line
[indent
] == '\t'))
603 if (line
[indent
] == '%')
605 vector
<string
> stmt
= split_tokens(line
);
607 if (GetSize(stmt
) == 1 && stmt
[0] == "%%")
610 if (GetSize(stmt
) == 2 && stmt
[0] == "%module")
612 Module
*module
= design
->module(RTLIL::escape_id(stmt
[1]));
613 modules
.erase(module
);
615 if (module
== nullptr)
616 log_error("Module '%s' not found.\n", stmt
[1].c_str());
618 *f
<< stringf("-- SMV description generated by %s\n", yosys_version_str
);
620 log("Creating SMV representation of module %s.\n", log_id(module
));
621 SmvWorker
worker(module
, verbose
, *f
);
624 *f
<< stringf("-- end of yosys output\n");
628 log_error("Unknown template statement: '%s'", line
.c_str() + indent
);
631 *f
<< line
<< std::endl
;
635 if (!modules
.empty())
637 *f
<< stringf("-- SMV description generated by %s\n", yosys_version_str
);
639 for (auto module
: modules
) {
640 log("Creating SMV representation of module %s.\n", log_id(module
));
641 SmvWorker
worker(module
, verbose
, *f
);
645 *f
<< stringf("-- end of yosys output\n");
648 if (template_f
.is_open()) {
650 while (std::getline(template_f
, line
))
651 *f
<< line
<< std::endl
;
656 PRIVATE_NAMESPACE_END