abc9: -reintegrate recover type from existing cell, check against boxid
[yosys.git] / frontends / verilog / preproc.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 * The Verilog frontend.
21 *
22 * This frontend is using the AST frontend library (see frontends/ast/).
23 * Thus this frontend does not generate RTLIL code directly but creates an
24 * AST directly from the Verilog parse tree and then passes this AST to
25 * the AST frontend library.
26 *
27 * ---
28 *
29 * Ad-hoc implementation of a Verilog preprocessor. The directives `define,
30 * `include, `ifdef, `ifndef, `else and `endif are handled here. All other
31 * directives are handled by the lexer (see verilog_lexer.l).
32 *
33 */
34
35 #include "verilog_frontend.h"
36 #include "kernel/log.h"
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40
41 YOSYS_NAMESPACE_BEGIN
42 using namespace VERILOG_FRONTEND;
43
44 static std::list<std::string> output_code;
45 static std::list<std::string> input_buffer;
46 static size_t input_buffer_charp;
47
48 static void return_char(char ch)
49 {
50 if (input_buffer_charp == 0)
51 input_buffer.push_front(std::string() + ch);
52 else
53 input_buffer.front()[--input_buffer_charp] = ch;
54 }
55
56 static void insert_input(std::string str)
57 {
58 if (input_buffer_charp != 0) {
59 input_buffer.front() = input_buffer.front().substr(input_buffer_charp);
60 input_buffer_charp = 0;
61 }
62 input_buffer.push_front(str);
63 }
64
65 static char next_char()
66 {
67 if (input_buffer.empty())
68 return 0;
69
70 log_assert(input_buffer_charp <= input_buffer.front().size());
71 if (input_buffer_charp == input_buffer.front().size()) {
72 input_buffer_charp = 0;
73 input_buffer.pop_front();
74 return next_char();
75 }
76
77 char ch = input_buffer.front()[input_buffer_charp++];
78 return ch == '\r' ? next_char() : ch;
79 }
80
81 static std::string skip_spaces()
82 {
83 std::string spaces;
84 while (1) {
85 char ch = next_char();
86 if (ch == 0)
87 break;
88 if (ch != ' ' && ch != '\t') {
89 return_char(ch);
90 break;
91 }
92 spaces += ch;
93 }
94 return spaces;
95 }
96
97 static std::string next_token(bool pass_newline = false)
98 {
99 std::string token;
100
101 char ch = next_char();
102 if (ch == 0)
103 return token;
104
105 token += ch;
106 if (ch == '\n') {
107 if (pass_newline) {
108 output_code.push_back(token);
109 return "";
110 }
111 return token;
112 }
113
114 if (ch == ' ' || ch == '\t')
115 {
116 while ((ch = next_char()) != 0) {
117 if (ch != ' ' && ch != '\t') {
118 return_char(ch);
119 break;
120 }
121 token += ch;
122 }
123 }
124 else if (ch == '"')
125 {
126 while ((ch = next_char()) != 0) {
127 token += ch;
128 if (ch == '"')
129 break;
130 if (ch == '\\') {
131 if ((ch = next_char()) != 0)
132 token += ch;
133 }
134 }
135 if (token == "\"\"" && (ch = next_char()) != 0) {
136 if (ch == '"')
137 token += ch;
138 else
139 return_char(ch);
140 }
141 }
142 else if (ch == '/')
143 {
144 if ((ch = next_char()) != 0) {
145 if (ch == '/') {
146 token += '*';
147 char last_ch = 0;
148 while ((ch = next_char()) != 0) {
149 if (ch == '\n') {
150 return_char(ch);
151 break;
152 }
153 if (last_ch != '*' || ch != '/') {
154 token += ch;
155 last_ch = ch;
156 }
157 }
158 token += " */";
159 }
160 else if (ch == '*') {
161 token += '*';
162 int newline_count = 0;
163 char last_ch = 0;
164 while ((ch = next_char()) != 0) {
165 if (ch == '\n') {
166 newline_count++;
167 token += ' ';
168 } else
169 token += ch;
170 if (last_ch == '*' && ch == '/')
171 break;
172 last_ch = ch;
173 }
174 while (newline_count-- > 0)
175 return_char('\n');
176 }
177 else
178 return_char(ch);
179 }
180 }
181 else
182 {
183 const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789";
184 if (ch == '`' || strchr(ok, ch) != NULL)
185 {
186 char first = ch;
187 ch = next_char();
188 if (first == '`' && (ch == '"' || ch == '`')) {
189 token += ch;
190 } else do {
191 if (strchr(ok, ch) == NULL) {
192 return_char(ch);
193 break;
194 }
195 token += ch;
196 } while ((ch = next_char()) != 0);
197 }
198 }
199 return token;
200 }
201
202 static void input_file(std::istream &f, std::string filename)
203 {
204 char buffer[513];
205 int rc;
206
207 insert_input("");
208 auto it = input_buffer.begin();
209
210 input_buffer.insert(it, "`file_push \"" + filename + "\"\n");
211 while ((rc = readsome(f, buffer, sizeof(buffer)-1)) > 0) {
212 buffer[rc] = 0;
213 input_buffer.insert(it, buffer);
214 }
215 input_buffer.insert(it, "\n`file_pop\n");
216 }
217
218
219 static bool try_expand_macro(std::set<std::string> &defines_with_args,
220 std::map<std::string, std::string> &defines_map,
221 std::string &tok
222 )
223 {
224 if (tok == "`\"") {
225 std::string literal("\"");
226 // Expand string literal
227 while (!input_buffer.empty()) {
228 std::string ntok = next_token();
229 if (ntok == "`\"") {
230 insert_input(literal+"\"");
231 return true;
232 } else if (!try_expand_macro(defines_with_args, defines_map, ntok)) {
233 literal += ntok;
234 }
235 }
236 return false; // error - unmatched `"
237 } else if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
238 std::string name = tok.substr(1);
239 // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str());
240 std::string skipped_spaces = skip_spaces();
241 tok = next_token(false);
242 if (tok == "(" && defines_with_args.count(name) > 0) {
243 int level = 1;
244 std::vector<std::string> args;
245 args.push_back(std::string());
246 while (1)
247 {
248 skip_spaces();
249 tok = next_token(true);
250 if (tok == ")" || tok == "}" || tok == "]")
251 level--;
252 if (level == 0)
253 break;
254 if (level == 1 && tok == ",")
255 args.push_back(std::string());
256 else
257 args.back() += tok;
258 if (tok == "(" || tok == "{" || tok == "[")
259 level++;
260 }
261 for (int i = 0; i < GetSize(args); i++)
262 defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i];
263 } else {
264 insert_input(tok);
265 insert_input(skipped_spaces);
266 }
267 insert_input(defines_map[name]);
268 return true;
269 } else if (tok == "``") {
270 // Swallow `` in macro expansion
271 return true;
272 } else return false;
273 }
274
275 std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> &pre_defines_map,
276 dict<std::string, std::pair<std::string, bool>> &global_defines_cache, const std::list<std::string> &include_dirs)
277 {
278 std::set<std::string> defines_with_args;
279 std::map<std::string, std::string> defines_map(pre_defines_map);
280 std::vector<std::string> filename_stack;
281 int ifdef_fail_level = 0;
282 bool in_elseif = false;
283
284 output_code.clear();
285 input_buffer.clear();
286 input_buffer_charp = 0;
287
288 input_file(f, filename);
289
290 defines_map["YOSYS"] = "1";
291 defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1";
292
293 for (auto &it : pre_defines_map)
294 defines_map[it.first] = it.second;
295
296 for (auto &it : global_defines_cache) {
297 if (it.second.second)
298 defines_with_args.insert(it.first);
299 defines_map[it.first] = it.second.first;
300 }
301
302 while (!input_buffer.empty())
303 {
304 std::string tok = next_token();
305 // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE");
306
307 if (tok == "`endif") {
308 if (ifdef_fail_level > 0)
309 ifdef_fail_level--;
310 if (ifdef_fail_level == 0)
311 in_elseif = false;
312 continue;
313 }
314
315 if (tok == "`else") {
316 if (ifdef_fail_level == 0)
317 ifdef_fail_level = 1;
318 else if (ifdef_fail_level == 1 && !in_elseif)
319 ifdef_fail_level = 0;
320 continue;
321 }
322
323 if (tok == "`elsif") {
324 skip_spaces();
325 std::string name = next_token(true);
326 if (ifdef_fail_level == 0)
327 ifdef_fail_level = 1, in_elseif = true;
328 else if (ifdef_fail_level == 1 && defines_map.count(name) != 0)
329 ifdef_fail_level = 0, in_elseif = true;
330 continue;
331 }
332
333 if (tok == "`ifdef") {
334 skip_spaces();
335 std::string name = next_token(true);
336 if (ifdef_fail_level > 0 || defines_map.count(name) == 0)
337 ifdef_fail_level++;
338 continue;
339 }
340
341 if (tok == "`ifndef") {
342 skip_spaces();
343 std::string name = next_token(true);
344 if (ifdef_fail_level > 0 || defines_map.count(name) != 0)
345 ifdef_fail_level++;
346 continue;
347 }
348
349 if (ifdef_fail_level > 0) {
350 if (tok == "\n")
351 output_code.push_back(tok);
352 continue;
353 }
354
355 if (tok == "`include") {
356 skip_spaces();
357 std::string fn = next_token(true);
358 while (try_expand_macro(defines_with_args, defines_map, fn)) {
359 fn = next_token();
360 }
361 while (1) {
362 size_t pos = fn.find('"');
363 if (pos == std::string::npos)
364 break;
365 if (pos == 0)
366 fn = fn.substr(1);
367 else
368 fn = fn.substr(0, pos) + fn.substr(pos+1);
369 }
370 std::ifstream ff;
371 ff.clear();
372 std::string fixed_fn = fn;
373 ff.open(fixed_fn.c_str());
374
375 bool filename_path_sep_found;
376 bool fn_relative;
377 #ifdef _WIN32
378 // Both forward and backslash are acceptable separators on Windows.
379 filename_path_sep_found = (filename.find_first_of("/\\") != std::string::npos);
380 // Easier just to invert the check for an absolute path (e.g. C:\ or C:/)
381 fn_relative = !(fn[1] == ':' && (fn[2] == '/' || fn[2] == '\\'));
382 #else
383 filename_path_sep_found = (filename.find('/') != std::string::npos);
384 fn_relative = (fn[0] != '/');
385 #endif
386
387 if (ff.fail() && fn.size() > 0 && fn_relative && filename_path_sep_found) {
388 // if the include file was not found, it is not given with an absolute path, and the
389 // currently read file is given with a path, then try again relative to its directory
390 ff.clear();
391 #ifdef _WIN32
392 fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn;
393 #else
394 fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn;
395 #endif
396 ff.open(fixed_fn);
397 }
398 if (ff.fail() && fn.size() > 0 && fn_relative) {
399 // if the include file was not found and it is not given with an absolute path, then
400 // search it in the include path
401 for (auto incdir : include_dirs) {
402 ff.clear();
403 fixed_fn = incdir + '/' + fn;
404 ff.open(fixed_fn);
405 if (!ff.fail()) break;
406 }
407 }
408 if (ff.fail()) {
409 output_code.push_back("`file_notfound " + fn);
410 } else {
411 input_file(ff, fixed_fn);
412 yosys_input_files.insert(fixed_fn);
413 }
414 continue;
415 }
416
417 if (tok == "`file_push") {
418 skip_spaces();
419 std::string fn = next_token(true);
420 if (!fn.empty() && fn.front() == '"' && fn.back() == '"')
421 fn = fn.substr(1, fn.size()-2);
422 output_code.push_back(tok + " \"" + fn + "\"");
423 filename_stack.push_back(filename);
424 filename = fn;
425 continue;
426 }
427
428 if (tok == "`file_pop") {
429 output_code.push_back(tok);
430 filename = filename_stack.back();
431 filename_stack.pop_back();
432 continue;
433 }
434
435 if (tok == "`define") {
436 std::string name, value;
437 std::map<std::string, int> args;
438 skip_spaces();
439 name = next_token(true);
440 bool here_doc_mode = false;
441 int newline_count = 0;
442 int state = 0;
443 if (skip_spaces() != "")
444 state = 3;
445 while (!tok.empty()) {
446 tok = next_token();
447 if (tok == "\"\"\"") {
448 here_doc_mode = !here_doc_mode;
449 continue;
450 }
451 if (state == 0 && tok == "(") {
452 state = 1;
453 skip_spaces();
454 } else
455 if (state == 1) {
456 if (tok == ")")
457 state = 2;
458 else if (tok != ",") {
459 int arg_idx = args.size()+1;
460 args[tok] = arg_idx;
461 }
462 skip_spaces();
463 } else {
464 if (state != 2)
465 state = 3;
466 if (tok == "\n") {
467 if (here_doc_mode) {
468 value += " ";
469 newline_count++;
470 } else {
471 return_char('\n');
472 break;
473 }
474 } else
475 if (tok == "\\") {
476 char ch = next_char();
477 if (ch == '\n') {
478 value += " ";
479 newline_count++;
480 } else {
481 value += std::string("\\");
482 return_char(ch);
483 }
484 } else
485 if (args.count(tok) > 0)
486 value += stringf("`macro_%s_arg%d", name.c_str(), args.at(tok));
487 else
488 value += tok;
489 }
490 }
491 while (newline_count-- > 0)
492 return_char('\n');
493 if (strchr("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789", name[0])) {
494 // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
495 defines_map[name] = value;
496 if (state == 2)
497 defines_with_args.insert(name);
498 else
499 defines_with_args.erase(name);
500 global_defines_cache[name] = std::pair<std::string, bool>(value, state == 2);
501 } else {
502 log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str());
503 }
504 continue;
505 }
506
507 if (tok == "`undef") {
508 std::string name;
509 skip_spaces();
510 name = next_token(true);
511 // printf("undef: >>%s<<\n", name.c_str());
512 defines_map.erase(name);
513 defines_with_args.erase(name);
514 global_defines_cache.erase(name);
515 continue;
516 }
517
518 if (tok == "`timescale") {
519 skip_spaces();
520 while (!tok.empty() && tok != "\n")
521 tok = next_token(true);
522 if (tok == "\n")
523 return_char('\n');
524 continue;
525 }
526
527 if (tok == "`resetall") {
528 defines_map.clear();
529 defines_with_args.clear();
530 global_defines_cache.clear();
531 continue;
532 }
533
534 if (try_expand_macro(defines_with_args, defines_map, tok))
535 continue;
536
537 output_code.push_back(tok);
538 }
539
540 std::string output;
541 for (auto &str : output_code)
542 output += str;
543
544 output_code.clear();
545 input_buffer.clear();
546 input_buffer_charp = 0;
547
548 return output;
549 }
550
551 YOSYS_NAMESPACE_END