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 * The Verilog frontend.
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.
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).
35 #include "verilog_frontend.h"
36 #include "kernel/log.h"
42 using namespace VERILOG_FRONTEND
;
44 static std::list
<std::string
> output_code
;
45 static std::list
<std::string
> input_buffer
;
46 static size_t input_buffer_charp
;
48 static void return_char(char ch
)
50 if (input_buffer_charp
== 0)
51 input_buffer
.push_front(std::string() + ch
);
53 input_buffer
.front()[--input_buffer_charp
] = ch
;
56 static void insert_input(std::string str
)
58 if (input_buffer_charp
!= 0) {
59 input_buffer
.front() = input_buffer
.front().substr(input_buffer_charp
);
60 input_buffer_charp
= 0;
62 input_buffer
.push_front(str
);
65 static char next_char()
67 if (input_buffer
.empty())
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();
77 char ch
= input_buffer
.front()[input_buffer_charp
++];
78 return ch
== '\r' ? next_char() : ch
;
81 static std::string
skip_spaces()
85 char ch
= next_char();
88 if (ch
!= ' ' && ch
!= '\t') {
97 static std::string
next_token(bool pass_newline
= false)
101 char ch
= next_char();
108 output_code
.push_back(token
);
114 if (ch
== ' ' || ch
== '\t')
116 while ((ch
= next_char()) != 0) {
117 if (ch
!= ' ' && ch
!= '\t') {
126 while ((ch
= next_char()) != 0) {
131 if ((ch
= next_char()) != 0)
135 if (token
== "\"\"" && (ch
= next_char()) != 0) {
144 if ((ch
= next_char()) != 0) {
148 while ((ch
= next_char()) != 0) {
153 if (last_ch
!= '*' || ch
!= '/') {
160 else if (ch
== '*') {
162 int newline_count
= 0;
164 while ((ch
= next_char()) != 0) {
170 if (last_ch
== '*' && ch
== '/')
174 while (newline_count
-- > 0)
183 const char *ok
= "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789";
184 if (ch
== '`' || strchr(ok
, ch
) != NULL
)
188 if (first
== '`' && (ch
== '"' || ch
== '`')) {
191 if (strchr(ok
, ch
) == NULL
) {
196 } while ((ch
= next_char()) != 0);
202 static void input_file(std::istream
&f
, std::string filename
)
208 auto it
= input_buffer
.begin();
210 input_buffer
.insert(it
, "`file_push \"" + filename
+ "\"\n");
211 while ((rc
= readsome(f
, buffer
, sizeof(buffer
)-1)) > 0) {
213 input_buffer
.insert(it
, buffer
);
215 input_buffer
.insert(it
, "\n`file_pop\n");
219 static bool try_expand_macro(std::set
<std::string
> &defines_with_args
,
220 std::map
<std::string
, std::string
> &defines_map
,
225 std::string
literal("\"");
226 // Expand string literal
227 while (!input_buffer
.empty()) {
228 std::string ntok
= next_token();
230 insert_input(literal
+"\"");
232 } else if (!try_expand_macro(defines_with_args
, defines_map
, ntok
)) {
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) {
244 std::vector
<std::string
> args
;
245 args
.push_back(std::string());
249 tok
= next_token(true);
250 if (tok
== ")" || tok
== "}" || tok
== "]")
254 if (level
== 1 && tok
== ",")
255 args
.push_back(std::string());
258 if (tok
== "(" || tok
== "{" || tok
== "[")
261 for (int i
= 0; i
< GetSize(args
); i
++)
262 defines_map
[stringf("macro_%s_arg%d", name
.c_str(), i
+1)] = args
[i
];
265 insert_input(skipped_spaces
);
267 insert_input(defines_map
[name
]);
269 } else if (tok
== "``") {
270 // Swallow `` in macro expansion
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
)
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;
285 input_buffer
.clear();
286 input_buffer_charp
= 0;
288 input_file(f
, filename
);
290 defines_map
["YOSYS"] = "1";
291 defines_map
[formal_mode
? "FORMAL" : "SYNTHESIS"] = "1";
293 for (auto &it
: pre_defines_map
)
294 defines_map
[it
.first
] = it
.second
;
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
;
302 while (!input_buffer
.empty())
304 std::string tok
= next_token();
305 // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE");
307 if (tok
== "`endif") {
308 if (ifdef_fail_level
> 0)
310 if (ifdef_fail_level
== 0)
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;
323 if (tok
== "`elsif") {
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;
333 if (tok
== "`ifdef") {
335 std::string name
= next_token(true);
336 if (ifdef_fail_level
> 0 || defines_map
.count(name
) == 0)
341 if (tok
== "`ifndef") {
343 std::string name
= next_token(true);
344 if (ifdef_fail_level
> 0 || defines_map
.count(name
) != 0)
349 if (ifdef_fail_level
> 0) {
351 output_code
.push_back(tok
);
355 if (tok
== "`include") {
357 std::string fn
= next_token(true);
358 while (try_expand_macro(defines_with_args
, defines_map
, fn
)) {
362 size_t pos
= fn
.find('"');
363 if (pos
== std::string::npos
)
368 fn
= fn
.substr(0, pos
) + fn
.substr(pos
+1);
372 std::string fixed_fn
= fn
;
373 ff
.open(fixed_fn
.c_str());
375 bool filename_path_sep_found
;
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] == '\\'));
383 filename_path_sep_found
= (filename
.find('/') != std::string::npos
);
384 fn_relative
= (fn
[0] != '/');
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
392 fixed_fn
= filename
.substr(0, filename
.find_last_of("/\\")+1) + fn
;
394 fixed_fn
= filename
.substr(0, filename
.rfind('/')+1) + fn
;
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
) {
403 fixed_fn
= incdir
+ '/' + fn
;
405 if (!ff
.fail()) break;
409 output_code
.push_back("`file_notfound " + fn
);
411 input_file(ff
, fixed_fn
);
412 yosys_input_files
.insert(fixed_fn
);
417 if (tok
== "`file_push") {
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
);
428 if (tok
== "`file_pop") {
429 output_code
.push_back(tok
);
430 filename
= filename_stack
.back();
431 filename_stack
.pop_back();
435 if (tok
== "`define") {
436 std::string name
, value
;
437 std::map
<std::string
, int> args
;
439 name
= next_token(true);
440 bool here_doc_mode
= false;
441 int newline_count
= 0;
443 if (skip_spaces() != "")
445 while (!tok
.empty()) {
447 if (tok
== "\"\"\"") {
448 here_doc_mode
= !here_doc_mode
;
451 if (state
== 0 && tok
== "(") {
458 else if (tok
!= ",") {
459 int arg_idx
= args
.size()+1;
476 char ch
= next_char();
481 value
+= std::string("\\");
485 if (args
.count(tok
) > 0)
486 value
+= stringf("`macro_%s_arg%d", name
.c_str(), args
.at(tok
));
491 while (newline_count
-- > 0)
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
;
497 defines_with_args
.insert(name
);
499 defines_with_args
.erase(name
);
500 global_defines_cache
[name
] = std::pair
<std::string
, bool>(value
, state
== 2);
502 log_file_error(filename
, 0, "Invalid name for macro definition: >>%s<<.\n", name
.c_str());
507 if (tok
== "`undef") {
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
);
518 if (tok
== "`timescale") {
520 while (!tok
.empty() && tok
!= "\n")
521 tok
= next_token(true);
527 if (tok
== "`resetall") {
529 defines_with_args
.clear();
530 global_defines_cache
.clear();
534 if (try_expand_macro(defines_with_args
, defines_map
, tok
))
537 output_code
.push_back(tok
);
541 for (auto &str
: output_code
)
545 input_buffer
.clear();
546 input_buffer_charp
= 0;