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.
30 #include "kernel/log.h"
33 using namespace Yosys
;
35 std::set
<std::string
> LibertyAst::blacklist
;
36 std::set
<std::string
> LibertyAst::whitelist
;
38 LibertyAst::~LibertyAst()
40 for (auto child
: children
)
45 LibertyAst
*LibertyAst::find(std::string name
)
47 for (auto child
: children
)
48 if (child
->id
== name
)
53 void LibertyAst::dump(FILE *f
, std::string indent
, std::string path
, bool path_ok
)
55 if (whitelist
.count(path
+ "/*") > 0)
60 if (blacklist
.count(id
) > 0 || blacklist
.count(path
) > 0)
62 if (whitelist
.size() > 0 && whitelist
.count(id
) == 0 && whitelist
.count(path
) == 0 && !path_ok
) {
63 fprintf(stderr
, "Automatically added to blacklist: %s\n", path
.c_str());
68 fprintf(f
, "%s%s", indent
.c_str(), id
.c_str());
69 if (!args
.empty() || !children
.empty()) {
71 for (size_t i
= 0; i
< args
.size(); i
++)
72 fprintf(f
, "%s%s", i
> 0 ? ", " : "", args
[i
].c_str());
76 fprintf(f
, " : %s", value
.c_str());
77 if (!children
.empty()) {
79 for (size_t i
= 0; i
< children
.size(); i
++)
80 children
[i
]->dump(f
, indent
+ " ", path
, path_ok
);
81 fprintf(f
, "%s}\n", indent
.c_str());
86 int LibertyParser::lexer(std::string
&str
)
93 } while (c
== ' ' || c
== '\t' || c
== '\r');
95 // search for identifiers, numbers, plus or minus.
96 if (('a' <= c
&& c
<= 'z') || ('A' <= c
&& c
<= 'Z') || ('0' <= c
&& c
<= '9') || c
== '_' || c
== '-' || c
== '+' || c
== '.') {
97 str
= static_cast<char>(c
);
100 if (('a' <= c
&& c
<= 'z') || ('A' <= c
&& c
<= 'Z') || ('0' <= c
&& c
<= '9') || c
== '_' || c
== '-' || c
== '+' || c
== '.')
106 if (str
== "+" || str
== "-") {
107 /* Single operator is not an identifier */
108 // fprintf(stderr, "LEX: char >>%s<<\n", str.c_str());
112 // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
117 // if it wasn't an identifer, number of array range,
118 // maybe it's a string?
129 // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str());
133 // if it wasn't a string, perhaps it's a comment or a forward slash?
136 if (c
== '*') { // start of '/*' block comment
138 while (c
> 0 && (last_c
!= '*' || c
!= '/')) {
145 } else if (c
== '/') { // start of '//' line comment
146 while (c
> 0 && c
!= '\n')
152 // fprintf(stderr, "LEX: char >>/<<\n");
153 return '/'; // a single '/' charater.
156 // check for a backslash
169 // check for a new line
175 // anything else, such as ';' will get passed
176 // through as literal items.
178 // if (c >= 32 && c < 255)
179 // fprintf(stderr, "LEX: char >>%c<<\n", c);
181 // fprintf(stderr, "LEX: char %d\n", c);
185 LibertyAst
*LibertyParser::parse()
189 int tok
= lexer(str
);
191 // there are liberty files in the wild that
192 // have superfluous ';' at the end of
193 // a { ... }. We simply ignore a ';' here.
194 // and get to the next statement.
196 while ((tok
== 'n') || (tok
== ';'))
199 if (tok
== '}' || tok
< 0)
207 error("Unexpected newline.");
215 eReport
= "Unexpected '";
216 eReport
+= static_cast<char>(tok
);
225 LibertyAst
*ast
= new LibertyAst
;
232 // allow both ';' and new lines to
233 // terminate a statement.
234 if ((tok
== ';') || (tok
== 'n'))
237 if (tok
== ':' && ast
->value
.empty()) {
238 tok
= lexer(ast
->value
);
242 while (tok
== '+' || tok
== '-' || tok
== '*' || tok
== '/') {
251 // In a liberty file, all key : value pairs should end in ';'
252 // However, there are some liberty files in the wild that
253 // just have a newline. We'll be kind and accept a newline
254 // instead of the ';' too..
255 if ((tok
== ';') || (tok
== 'n'))
271 // FIXME: the AST needs to be extended to store
272 // these vector ranges.
275 // parse vector range [A] or [A:B]
280 // expected a vector array index
281 error("Expected a number.");
285 // fixme: check for number A
288 // optionally check for : in case of [A:B]
289 // if it isn't we just expect ']'
296 // expected a vector array index
297 error("Expected a number.");
301 // fixme: check for number B
305 // expect a closing bracket of array range
308 error("Expected ']' on array range.");
317 error("Unexpected newline.");
325 eReport
= "Unexpected '";
326 eReport
+= static_cast<char>(tok
);
334 ast
->args
.push_back(arg
);
341 LibertyAst
*child
= parse();
344 ast
->children
.push_back(child
);
357 void LibertyParser::error()
359 log_error("Syntax error in liberty file on line %d.\n", line
);
362 void LibertyParser::error(const std::string
&str
)
364 std::stringstream ss
;
365 ss
<< "Syntax error in liberty file on line " << line
<< ".\n";
366 ss
<< " " << str
<< "\n";
367 log_error("%s", ss
.str().c_str());
372 void LibertyParser::error()
374 fprintf(stderr
, "Syntax error in liberty file on line %d.\n", line
);
378 void LibertyParser::error(const std::string
&str
)
380 std::stringstream ss
;
381 ss
<< "Syntax error in liberty file on line " << line
<< ".\n";
382 ss
<< " " << str
<< "\n";
383 printf("%s", ss
.str().c_str());
387 /**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/
389 #define CHECK_NV(result, check) \
391 auto _R = (result); \
393 fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
394 #result, (long int)_R, #check, __FILE__, __LINE__); \
399 #define CHECK_COND(result) \
402 fprintf(stderr, "Error from '%s' in %s:%d.\n", \
403 #result, __FILE__, __LINE__); \
408 /**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/
410 LibertyAst
*find_non_null(LibertyAst
*node
, const char *name
)
412 LibertyAst
*ret
= node
->find(name
);
414 fprintf(stderr
, "Error: expected to find `%s' node.\n", name
);
418 std::string
func2vl(std::string str
)
420 for (size_t pos
= str
.find_first_of("\" \t"); pos
!= std::string::npos
; pos
= str
.find_first_of("\" \t")) {
421 char c_left
= pos
> 0 ? str
[pos
-1] : ' ';
422 char c_right
= pos
+1 < str
.size() ? str
[pos
+1] : ' ';
423 if (std::string("\" \t*+").find(c_left
) != std::string::npos
)
425 else if (std::string("\" \t*+").find(c_right
) != std::string::npos
)
431 std::vector
<size_t> group_start
;
432 for (size_t pos
= 0; pos
< str
.size(); pos
++) {
434 group_start
.push_back(pos
);
435 if (str
[pos
] == ')' && group_start
.size() > 0) {
436 if (pos
+1 < str
.size() && str
[pos
+1] == '\'') {
437 std::string group
= str
.substr(group_start
.back(), pos
-group_start
.back()+1);
438 str
[group_start
.back()] = '~';
439 str
.replace(group_start
.back()+1, group
.size(), group
);
442 group_start
.pop_back();
444 if (str
[pos
] == '\'' && pos
> 0) {
445 size_t start
= str
.find_last_of("()'*+^&| ", pos
-1)+1;
446 std::string group
= str
.substr(start
, pos
-start
);
448 str
.replace(start
+1, group
.size(), group
);
459 void event2vl(LibertyAst
*ast
, std::string
&edge
, std::string
&expr
)
465 expr
= func2vl(ast
->value
);
466 if (expr
.size() > 0 && expr
[0] == '~')
467 edge
= "negedge " + expr
.substr(1);
469 edge
= "posedge " + expr
;
473 void clear_preset_var(std::string var
, std::string type
)
475 if (type
.find('L') != std::string::npos
) {
476 printf(" %s <= 0;\n", var
.c_str());
479 if (type
.find('H') != std::string::npos
) {
480 printf(" %s <= 1;\n", var
.c_str());
483 if (type
.find('T') != std::string::npos
) {
484 printf(" %s <= ~%s;\n", var
.c_str(), var
.c_str());
487 if (type
.find('X') != std::string::npos
) {
488 printf(" %s <= 'bx;\n", var
.c_str());
493 void gen_verilogsim_cell(LibertyAst
*ast
)
495 if (ast
->find("statetable") != NULL
)
498 CHECK_NV(ast
->args
.size(), == 1);
499 printf("module %s (", ast
->args
[0].c_str());
501 for (auto child
: ast
->children
) {
502 if (child
->id
!= "pin")
504 CHECK_NV(child
->args
.size(), == 1);
505 printf("%s%s", first
? "" : ", ", child
->args
[0].c_str());
510 for (auto child
: ast
->children
) {
511 if (child
->id
!= "ff" && child
->id
!= "latch")
515 for (auto arg
: child
->args
) {
516 printf("%s%s", first
? "" : ", ", arg
.c_str());
522 for (auto child
: ast
->children
) {
523 if (child
->id
!= "pin")
525 CHECK_NV(child
->args
.size(), == 1);
526 LibertyAst
*dir
= find_non_null(child
, "direction");
527 LibertyAst
*func
= child
->find("function");
528 printf(" %s %s;\n", dir
->value
.c_str(), child
->args
[0].c_str());
530 printf(" assign %s = %s; // %s\n", child
->args
[0].c_str(), func2vl(func
->value
).c_str(), func
->value
.c_str());
533 for (auto child
: ast
->children
)
535 if (child
->id
!= "ff" || child
->args
.size() != 2)
538 std::string iq_var
= child
->args
[0];
539 std::string iqn_var
= child
->args
[1];
541 std::string clock_edge
, clock_expr
;
542 event2vl(child
->find("clocked_on"), clock_edge
, clock_expr
);
544 std::string clear_edge
, clear_expr
;
545 event2vl(child
->find("clear"), clear_edge
, clear_expr
);
547 std::string preset_edge
, preset_expr
;
548 event2vl(child
->find("preset"), preset_edge
, preset_expr
);
550 std::string edge
= "";
551 if (!clock_edge
.empty())
552 edge
+= (edge
.empty() ? "" : ", ") + clock_edge
;
553 if (!clear_edge
.empty())
554 edge
+= (edge
.empty() ? "" : ", ") + clear_edge
;
555 if (!preset_edge
.empty())
556 edge
+= (edge
.empty() ? "" : ", ") + preset_edge
;
561 printf(" always @(%s) begin\n", edge
.c_str());
563 const char *else_prefix
= "";
564 if (!clear_expr
.empty() && !preset_expr
.empty()) {
565 printf(" %sif ((%s) && (%s)) begin\n", else_prefix
, clear_expr
.c_str(), preset_expr
.c_str());
566 clear_preset_var(iq_var
, find_non_null(child
, "clear_preset_var1")->value
);
567 clear_preset_var(iqn_var
, find_non_null(child
, "clear_preset_var2")->value
);
569 else_prefix
= "else ";
571 if (!clear_expr
.empty()) {
572 printf(" %sif (%s) begin\n", else_prefix
, clear_expr
.c_str());
573 printf(" %s <= 0;\n", iq_var
.c_str());
574 printf(" %s <= 1;\n", iqn_var
.c_str());
576 else_prefix
= "else ";
578 if (!preset_expr
.empty()) {
579 printf(" %sif (%s) begin\n", else_prefix
, preset_expr
.c_str());
580 printf(" %s <= 1;\n", iq_var
.c_str());
581 printf(" %s <= 0;\n", iqn_var
.c_str());
583 else_prefix
= "else ";
586 printf(" %sbegin\n", else_prefix
);
587 std::string expr
= find_non_null(child
, "next_state")->value
;
588 printf(" // %s\n", expr
.c_str());
589 printf(" %s <= %s;\n", iq_var
.c_str(), func2vl(expr
).c_str());
590 printf(" %s <= ~(%s);\n", iqn_var
.c_str(), func2vl(expr
).c_str());
597 for (auto child
: ast
->children
)
599 if (child
->id
!= "latch" || child
->args
.size() != 2)
602 std::string iq_var
= child
->args
[0];
603 std::string iqn_var
= child
->args
[1];
605 std::string enable_edge
, enable_expr
;
606 event2vl(child
->find("enable"), enable_edge
, enable_expr
);
608 std::string clear_edge
, clear_expr
;
609 event2vl(child
->find("clear"), clear_edge
, clear_expr
);
611 std::string preset_edge
, preset_expr
;
612 event2vl(child
->find("preset"), preset_edge
, preset_expr
);
614 printf(" always @* begin\n");
616 const char *else_prefix
= "";
617 if (!clear_expr
.empty() && !preset_expr
.empty()) {
618 printf(" %sif ((%s) && (%s)) begin\n", else_prefix
, clear_expr
.c_str(), preset_expr
.c_str());
619 clear_preset_var(iq_var
, find_non_null(child
, "clear_preset_var1")->value
);
620 clear_preset_var(iqn_var
, find_non_null(child
, "clear_preset_var2")->value
);
622 else_prefix
= "else ";
624 if (!clear_expr
.empty()) {
625 printf(" %sif (%s) begin\n", else_prefix
, clear_expr
.c_str());
626 printf(" %s <= 0;\n", iq_var
.c_str());
627 printf(" %s <= 1;\n", iqn_var
.c_str());
629 else_prefix
= "else ";
631 if (!preset_expr
.empty()) {
632 printf(" %sif (%s) begin\n", else_prefix
, preset_expr
.c_str());
633 printf(" %s <= 1;\n", iq_var
.c_str());
634 printf(" %s <= 0;\n", iqn_var
.c_str());
636 else_prefix
= "else ";
638 if (!enable_expr
.empty()) {
639 printf(" %sif (%s) begin\n", else_prefix
, enable_expr
.c_str());
640 std::string expr
= find_non_null(child
, "data_in")->value
;
641 printf(" %s <= %s;\n", iq_var
.c_str(), func2vl(expr
).c_str());
642 printf(" %s <= ~(%s);\n", iqn_var
.c_str(), func2vl(expr
).c_str());
644 else_prefix
= "else ";
650 printf("endmodule\n");
653 void gen_verilogsim(LibertyAst
*ast
)
655 CHECK_COND(ast
->id
== "library");
657 for (auto child
: ast
->children
)
658 if (child
->id
== "cell" && !child
->find("dont_use"))
659 gen_verilogsim_cell(child
);
664 fprintf(stderr
, "Usage: filterlib [rules-file [liberty-file]]\n");
665 fprintf(stderr
, " or: filterlib -verilogsim [liberty-file]\n");
669 int main(int argc
, char **argv
)
671 bool flag_verilogsim
= false;
678 if (!strcmp(argv
[1], "-verilogsim"))
679 flag_verilogsim
= true;
680 if (!strcmp(argv
[1], "-") || !strcmp(argv
[1], "-verilogsim"))
682 LibertyAst::whitelist
.insert("/library");
683 LibertyAst::whitelist
.insert("/library/cell");
684 LibertyAst::whitelist
.insert("/library/cell/area");
685 LibertyAst::whitelist
.insert("/library/cell/cell_footprint");
686 LibertyAst::whitelist
.insert("/library/cell/dont_touch");
687 LibertyAst::whitelist
.insert("/library/cell/dont_use");
688 LibertyAst::whitelist
.insert("/library/cell/ff");
689 LibertyAst::whitelist
.insert("/library/cell/ff/*");
690 LibertyAst::whitelist
.insert("/library/cell/latch");
691 LibertyAst::whitelist
.insert("/library/cell/latch/*");
692 LibertyAst::whitelist
.insert("/library/cell/pin");
693 LibertyAst::whitelist
.insert("/library/cell/pin/clock");
694 LibertyAst::whitelist
.insert("/library/cell/pin/direction");
695 LibertyAst::whitelist
.insert("/library/cell/pin/driver_type");
696 LibertyAst::whitelist
.insert("/library/cell/pin/function");
697 LibertyAst::whitelist
.insert("/library/cell/pin_opposite");
698 LibertyAst::whitelist
.insert("/library/cell/pin/state_function");
699 LibertyAst::whitelist
.insert("/library/cell/pin/three_state");
700 LibertyAst::whitelist
.insert("/library/cell/statetable");
701 LibertyAst::whitelist
.insert("/library/cell/statetable/*");
705 FILE *f
= fopen(argv
[1], "r");
707 fprintf(stderr
, "Can't open rules file `%s'.\n", argv
[1]);
712 while (fgets(buffer
, 1024, f
) != NULL
)
716 for (char *p
= buffer
; *p
; p
++)
718 if (*p
== '-' || *p
== '+') {
724 if (*p
== ' ' || *p
== '\t' || *p
== '\r' || *p
== '\n' || *p
== '#') {
727 LibertyAst::blacklist
.insert(id
);
730 LibertyAst::whitelist
.insert(id
);
743 fprintf(stderr
, "Syntax error in rules file:\n%s", buffer
);
750 std::istream
*f
= &std::cin
;
753 std::ifstream
*ff
= new std::ifstream
;
757 fprintf(stderr
, "Can't open liberty file `%s'.\n", argv
[2]);
763 LibertyParser
parser(*f
);
766 gen_verilogsim(parser
.ast
);
768 parser
.ast
->dump(stdout
);