Fixed dumping of timing() { .. } block in libparse
[yosys.git] / passes / techmap / libparse.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
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.
9 *
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.
17 *
18 */
19
20 #include "libparse.h"
21 #include <stdlib.h>
22 #include <string.h>
23
24 #ifndef FILTERLIB
25 #include "kernel/log.h"
26 #endif
27
28 using namespace PASS_DFFLIBMAP;
29
30 std::set<std::string> LibertyAst::blacklist;
31 std::set<std::string> LibertyAst::whitelist;
32
33 LibertyAst::~LibertyAst()
34 {
35 for (auto child : children)
36 delete child;
37 children.clear();
38 }
39
40 LibertyAst *LibertyAst::find(std::string name)
41 {
42 if (this == NULL)
43 return NULL;
44 for (auto child : children)
45 if (child->id == name)
46 return child;
47 return NULL;
48 }
49
50 void LibertyAst::dump(FILE *f, std::string indent, std::string path, bool path_ok)
51 {
52 if (whitelist.count(path + "/*") > 0)
53 path_ok = true;
54
55 path += "/" + id;
56
57 if (blacklist.count(id) > 0 || blacklist.count(path) > 0)
58 return;
59 if (whitelist.size() > 0 && whitelist.count(id) == 0 && whitelist.count(path) == 0 && !path_ok) {
60 fprintf(stderr, "Automatically added to blacklist: %s\n", path.c_str());
61 blacklist.insert(id);
62 return;
63 }
64
65 fprintf(f, "%s%s", indent.c_str(), id.c_str());
66 if (!args.empty() || !children.empty()) {
67 fprintf(f, "(");
68 for (size_t i = 0; i < args.size(); i++)
69 fprintf(f, "%s%s", i > 0 ? ", " : "", args[i].c_str());
70 fprintf(f, ")");
71 }
72 if (!value.empty())
73 fprintf(f, " : %s", value.c_str());
74 if (!children.empty()) {
75 fprintf(f, " {\n");
76 for (size_t i = 0; i < children.size(); i++)
77 children[i]->dump(f, indent + " ", path, path_ok);
78 fprintf(f, "%s}\n", indent.c_str());
79 } else
80 fprintf(f, " ;\n");
81 }
82
83 int LibertyParser::lexer(std::string &str)
84 {
85 int c;
86
87 do {
88 c = fgetc(f);
89 } while (c == ' ' || c == '\t' || c == '\r');
90
91 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') {
92 str = c;
93 while (1) {
94 c = fgetc(f);
95 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
96 str += c;
97 else
98 break;
99 }
100 ungetc(c, f);
101 // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
102 return 'v';
103 }
104
105 if (c == '"') {
106 str = c;
107 while (1) {
108 c = fgetc(f);
109 if (c == '\n')
110 line++;
111 str += c;
112 if (c == '"')
113 break;
114 }
115 // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str());
116 return 'v';
117 }
118
119 if (c == '/') {
120 c = fgetc(f);
121 if (c == '*') {
122 int last_c = 0;
123 while (c > 0 && (last_c != '*' || c != '/')) {
124 last_c = c;
125 c = fgetc(f);
126 if (c == '\n')
127 line++;
128 }
129 return lexer(str);
130 } else if (c == '/') {
131 while (c > 0 && c != '\n')
132 c = fgetc(f);
133 line++;
134 return lexer(str);
135 }
136 ungetc(c, f);
137 // fprintf(stderr, "LEX: char >>/<<\n");
138 return '/';
139 }
140
141 if (c == '\\') {
142 c = fgetc(f);
143 if (c == '\r')
144 c = fgetc(f);
145 if (c == '\n')
146 return lexer(str);
147 ungetc(c, f);
148 return '\\';
149 }
150
151 if (c == '\n') {
152 line++;
153 return ';';
154 }
155
156 // if (c >= 32 && c < 255)
157 // fprintf(stderr, "LEX: char >>%c<<\n", c);
158 // else
159 // fprintf(stderr, "LEX: char %d\n", c);
160 return c;
161 }
162
163 LibertyAst *LibertyParser::parse()
164 {
165 std::string str;
166
167 int tok = lexer(str);
168
169 while (tok == ';')
170 tok = lexer(str);
171
172 if (tok == '}' || tok < 0)
173 return NULL;
174
175 if (tok != 'v')
176 error();
177
178 LibertyAst *ast = new LibertyAst;
179 ast->id = str;
180
181 while (1)
182 {
183 tok = lexer(str);
184
185 if (tok == ';')
186 break;
187
188 if (tok == ':' && ast->value.empty()) {
189 tok = lexer(ast->value);
190 if (tok != 'v')
191 error();
192 continue;
193 }
194
195 if (tok == '(') {
196 while (1) {
197 std::string arg;
198 tok = lexer(arg);
199 if (tok == ',')
200 continue;
201 if (tok == ')')
202 break;
203 if (tok != 'v')
204 error();
205 ast->args.push_back(arg);
206 }
207 continue;
208 }
209
210 if (tok == '{') {
211 while (1) {
212 LibertyAst *child = parse();
213 if (child == NULL)
214 break;
215 ast->children.push_back(child);
216 }
217 break;
218 }
219
220 error();
221 }
222
223 return ast;
224 }
225
226 #ifndef FILTERLIB
227
228 void LibertyParser::error()
229 {
230 log_error("Syntax error in line %d.\n", line);
231 }
232
233 #else
234
235 void LibertyParser::error()
236 {
237 fprintf(stderr, "Syntax error in line %d.\n", line);
238 exit(1);
239 }
240
241 /**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/
242
243 // This is to not confuse the VIM syntax highlighting
244 #define CHECK_VAL_OPEN (
245 #define CHECK_VAL_CLOSE )
246
247 #define CHECK(result, check) \
248 CHECK_VAL_OPEN{ \
249 auto _R = (result); \
250 if (!(_R check)) { \
251 fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
252 #result, (long int)_R, #check, __FILE__, __LINE__); \
253 abort(); \
254 } \
255 _R; \
256 }CHECK_VAL_CLOSE
257
258 #define CHECK_NV(result, check) \
259 do { \
260 auto _R = (result); \
261 if (!(_R check)) { \
262 fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
263 #result, (long int)_R, #check, __FILE__, __LINE__); \
264 abort(); \
265 } \
266 } while(0)
267
268 #define CHECK_COND(result) \
269 do { \
270 if (!(result)) { \
271 fprintf(stderr, "Error from '%s' in %s:%d.\n", \
272 #result, __FILE__, __LINE__); \
273 abort(); \
274 } \
275 } while(0)
276
277 /**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/
278
279 std::string func2vl(std::string str)
280 {
281 for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
282 char c_left = pos > 0 ? str[pos-1] : ' ';
283 char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
284 if (std::string("\" \t*+").find(c_left) != std::string::npos)
285 str.erase(pos, 1);
286 else if (std::string("\" \t*+").find(c_right) != std::string::npos)
287 str.erase(pos, 1);
288 else
289 str[pos] = '*';
290 }
291
292 std::vector<size_t> group_start;
293 for (size_t pos = 0; pos < str.size(); pos++) {
294 if (str[pos] == '(')
295 group_start.push_back(pos);
296 if (str[pos] == ')' && group_start.size() > 0) {
297 if (pos+1 < str.size() && str[pos+1] == '\'') {
298 std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
299 str[group_start.back()] = '~';
300 str.replace(group_start.back()+1, group.size(), group);
301 pos++;
302 }
303 group_start.pop_back();
304 }
305 if (str[pos] == '\'' && pos > 0) {
306 size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
307 std::string group = str.substr(start, pos-start);
308 str[start] = '~';
309 str.replace(start+1, group.size(), group);
310 }
311 if (str[pos] == '*')
312 str[pos] = '&';
313 if (str[pos] == '+')
314 str[pos] = '|';
315 }
316
317 return str;
318 }
319
320 void event2vl(LibertyAst *ast, std::string &edge, std::string &expr)
321 {
322 edge.clear();
323 expr.clear();
324
325 if (ast != NULL) {
326 expr = func2vl(ast->value);
327 if (expr.size() > 0 && expr[0] == '~')
328 edge = "negedge " + expr.substr(1);
329 else
330 edge = "posedge " + expr;
331 }
332 }
333
334 void clear_preset_var(std::string var, std::string type)
335 {
336 if (type.find('L') != std::string::npos) {
337 printf(" %s <= 0;\n", var.c_str());
338 return;
339 }
340 if (type.find('H') != std::string::npos) {
341 printf(" %s <= 1;\n", var.c_str());
342 return;
343 }
344 if (type.find('T') != std::string::npos) {
345 printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
346 return;
347 }
348 if (type.find('X') != std::string::npos) {
349 printf(" %s <= 'bx;\n", var.c_str());
350 return;
351 }
352 }
353
354 void gen_verilogsim_cell(LibertyAst *ast)
355 {
356 if (ast->find("statetable") != NULL)
357 return;
358
359 CHECK_NV(ast->args.size(), == 1);
360 printf("module %s (", ast->args[0].c_str());
361 bool first = true;
362 for (auto child : ast->children) {
363 if (child->id != "pin")
364 continue;
365 CHECK_NV(child->args.size(), == 1);
366 printf("%s%s", first ? "" : ", ", child->args[0].c_str());
367 first = false;
368 }
369 printf(");\n");
370
371 for (auto child : ast->children) {
372 if (child->id != "ff" && child->id != "latch")
373 continue;
374 printf(" reg ");
375 first = true;
376 for (auto arg : child->args) {
377 printf("%s%s", first ? "" : ", ", arg.c_str());
378 first = false;
379 }
380 printf(";\n");
381 }
382
383 for (auto child : ast->children) {
384 if (child->id != "pin")
385 continue;
386 CHECK_NV(child->args.size(), == 1);
387 LibertyAst *dir = CHECK(child->find("direction"), != NULL);
388 LibertyAst *func = child->find("function");
389 printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
390 if (func != NULL)
391 printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
392 }
393
394 for (auto child : ast->children)
395 {
396 if (child->id != "ff" || child->args.size() != 2)
397 continue;
398
399 std::string iq_var = child->args[0];
400 std::string iqn_var = child->args[1];
401
402 std::string clock_edge, clock_expr;
403 event2vl(child->find("clocked_on"), clock_edge, clock_expr);
404
405 std::string clear_edge, clear_expr;
406 event2vl(child->find("clear"), clear_edge, clear_expr);
407
408 std::string preset_edge, preset_expr;
409 event2vl(child->find("preset"), preset_edge, preset_expr);
410
411 std::string edge = "";
412 if (!clock_edge.empty())
413 edge += (edge.empty() ? "" : ", ") + clock_edge;
414 if (!clear_edge.empty())
415 edge += (edge.empty() ? "" : ", ") + clear_edge;
416 if (!preset_edge.empty())
417 edge += (edge.empty() ? "" : ", ") + preset_edge;
418
419 if (edge.empty())
420 continue;
421
422 printf(" always @(%s) begin\n", edge.c_str());
423
424 const char *else_prefix = "";
425 if (!clear_expr.empty() && !preset_expr.empty()) {
426 printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
427 clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value);
428 clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value);
429 printf(" end\n");
430 else_prefix = "else ";
431 }
432 if (!clear_expr.empty()) {
433 printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
434 printf(" %s <= 0;\n", iq_var.c_str());
435 printf(" %s <= 1;\n", iqn_var.c_str());
436 printf(" end\n");
437 else_prefix = "else ";
438 }
439 if (!preset_expr.empty()) {
440 printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
441 printf(" %s <= 1;\n", iq_var.c_str());
442 printf(" %s <= 0;\n", iqn_var.c_str());
443 printf(" end\n");
444 else_prefix = "else ";
445 }
446 if (*else_prefix)
447 printf(" %sbegin\n", else_prefix);
448 std::string expr = CHECK(child->find("next_state"), != NULL)->value;
449 printf(" // %s\n", expr.c_str());
450 printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
451 printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
452 if (*else_prefix)
453 printf(" end\n");
454
455 printf(" end\n");
456 }
457
458 for (auto child : ast->children)
459 {
460 if (child->id != "latch" || child->args.size() != 2)
461 continue;
462
463 std::string iq_var = child->args[0];
464 std::string iqn_var = child->args[1];
465
466 std::string enable_edge, enable_expr;
467 event2vl(child->find("enable"), enable_edge, enable_expr);
468
469 std::string clear_edge, clear_expr;
470 event2vl(child->find("clear"), clear_edge, clear_expr);
471
472 std::string preset_edge, preset_expr;
473 event2vl(child->find("preset"), preset_edge, preset_expr);
474
475 printf(" always @* begin\n");
476
477 const char *else_prefix = "";
478 if (!clear_expr.empty() && !preset_expr.empty()) {
479 printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
480 clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value);
481 clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value);
482 printf(" end\n");
483 else_prefix = "else ";
484 }
485 if (!clear_expr.empty()) {
486 printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
487 printf(" %s <= 0;\n", iq_var.c_str());
488 printf(" %s <= 1;\n", iqn_var.c_str());
489 printf(" end\n");
490 else_prefix = "else ";
491 }
492 if (!preset_expr.empty()) {
493 printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
494 printf(" %s <= 1;\n", iq_var.c_str());
495 printf(" %s <= 0;\n", iqn_var.c_str());
496 printf(" end\n");
497 else_prefix = "else ";
498 }
499 if (!enable_expr.empty()) {
500 printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str());
501 std::string expr = CHECK(child->find("data_in"), != NULL)->value;
502 printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
503 printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
504 printf(" end\n");
505 else_prefix = "else ";
506 }
507
508 printf(" end\n");
509 }
510
511 printf("endmodule\n");
512 }
513
514 void gen_verilogsim(LibertyAst *ast)
515 {
516 CHECK_COND(ast->id == "library");
517
518 for (auto child : ast->children)
519 if (child->id == "cell" && !child->find("dont_use"))
520 gen_verilogsim_cell(child);
521 }
522
523 void usage()
524 {
525 fprintf(stderr, "Usage: filterlib [rules-file [liberty-file]]\n");
526 fprintf(stderr, " or: filterlib -verilogsim [liberty-file]\n");
527 exit(1);
528 }
529
530 int main(int argc, char **argv)
531 {
532 bool flag_verilogsim = false;
533
534 if (argc > 3)
535 usage();
536
537 if (argc > 1)
538 {
539 if (!strcmp(argv[1], "-verilogsim"))
540 flag_verilogsim = true;
541 if (!strcmp(argv[1], "-") || !strcmp(argv[1], "-verilogsim"))
542 {
543 LibertyAst::whitelist.insert("/library");
544 LibertyAst::whitelist.insert("/library/cell");
545 LibertyAst::whitelist.insert("/library/cell/area");
546 LibertyAst::whitelist.insert("/library/cell/cell_footprint");
547 LibertyAst::whitelist.insert("/library/cell/dont_touch");
548 LibertyAst::whitelist.insert("/library/cell/dont_use");
549 LibertyAst::whitelist.insert("/library/cell/ff");
550 LibertyAst::whitelist.insert("/library/cell/ff/*");
551 LibertyAst::whitelist.insert("/library/cell/latch");
552 LibertyAst::whitelist.insert("/library/cell/latch/*");
553 LibertyAst::whitelist.insert("/library/cell/pin");
554 LibertyAst::whitelist.insert("/library/cell/pin/clock");
555 LibertyAst::whitelist.insert("/library/cell/pin/direction");
556 LibertyAst::whitelist.insert("/library/cell/pin/driver_type");
557 LibertyAst::whitelist.insert("/library/cell/pin/function");
558 LibertyAst::whitelist.insert("/library/cell/pin_opposite");
559 LibertyAst::whitelist.insert("/library/cell/pin/state_function");
560 LibertyAst::whitelist.insert("/library/cell/pin/three_state");
561 LibertyAst::whitelist.insert("/library/cell/statetable");
562 LibertyAst::whitelist.insert("/library/cell/statetable/*");
563 }
564 else
565 {
566 FILE *f = fopen(argv[1], "r");
567 if (f == NULL) {
568 fprintf(stderr, "Can't open rules file `%s'.\n", argv[1]);
569 usage();
570 }
571
572 char buffer[1024];
573 while (fgets(buffer, 1024, f) != NULL)
574 {
575 char mode = 0;
576 std::string id;
577 for (char *p = buffer; *p; p++)
578 {
579 if (*p == '-' || *p == '+') {
580 if (mode != 0)
581 goto syntax_error;
582 mode = *p;
583 continue;
584 }
585 if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == '#') {
586 if (!id.empty()) {
587 if (mode == '-')
588 LibertyAst::blacklist.insert(id);
589 else
590 if (mode == '+')
591 LibertyAst::whitelist.insert(id);
592 else
593 goto syntax_error;
594 }
595 id.clear();
596 if (*p == '#')
597 break;
598 continue;
599 }
600 id += *p;
601 continue;
602
603 syntax_error:
604 fprintf(stderr, "Syntax error in rules file:\n%s", buffer);
605 exit(1);
606 }
607 }
608 }
609 }
610
611 FILE *f = stdin;
612 if (argc == 3) {
613 f = fopen(argv[2], "r");
614 if (f == NULL) {
615 fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]);
616 usage();
617 }
618 }
619
620 LibertyParser parser(f);
621 if (parser.ast) {
622 if (flag_verilogsim)
623 gen_verilogsim(parser.ast);
624 else
625 parser.ast->dump(stdout);
626 }
627
628 if (argc == 3)
629 fclose(f);
630
631 return 0;
632 }
633
634 #endif
635