Remember global declarations and defines accross read_verilog calls
[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 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 while ((ch = next_char()) != 0) {
186 if (strchr(ok, ch) == NULL) {
187 return_char(ch);
188 break;
189 }
190 token += ch;
191 }
192 }
193
194 return token;
195 }
196
197 static void input_file(std::istream &f, std::string filename)
198 {
199 char buffer[513];
200 int rc;
201
202 insert_input("");
203 auto it = input_buffer.begin();
204
205 input_buffer.insert(it, "`file_push \"" + filename + "\"\n");
206 while ((rc = readsome(f, buffer, sizeof(buffer)-1)) > 0) {
207 buffer[rc] = 0;
208 input_buffer.insert(it, buffer);
209 }
210 input_buffer.insert(it, "\n`file_pop\n");
211 }
212
213 std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> &pre_defines_map,
214 dict<std::string, std::pair<std::string, bool>> &global_defines_cache, const std::list<std::string> &include_dirs)
215 {
216 std::set<std::string> defines_with_args;
217 std::map<std::string, std::string> defines_map(pre_defines_map);
218 int ifdef_fail_level = 0;
219 bool in_elseif = false;
220
221 output_code.clear();
222 input_buffer.clear();
223 input_buffer_charp = 0;
224
225 input_file(f, filename);
226
227 defines_map["YOSYS"] = "1";
228 defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1";
229
230 for (auto &it : pre_defines_map)
231 defines_map[it.first] = it.second;
232
233 for (auto &it : global_defines_cache) {
234 if (it.second.second)
235 defines_with_args.insert(it.first);
236 defines_map[it.first] = it.second.first;
237 }
238
239 while (!input_buffer.empty())
240 {
241 std::string tok = next_token();
242 // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE");
243
244 if (tok == "`endif") {
245 if (ifdef_fail_level > 0)
246 ifdef_fail_level--;
247 if (ifdef_fail_level == 0)
248 in_elseif = false;
249 continue;
250 }
251
252 if (tok == "`else") {
253 if (ifdef_fail_level == 0)
254 ifdef_fail_level = 1;
255 else if (ifdef_fail_level == 1 && !in_elseif)
256 ifdef_fail_level = 0;
257 continue;
258 }
259
260 if (tok == "`elsif") {
261 skip_spaces();
262 std::string name = next_token(true);
263 if (ifdef_fail_level == 0)
264 ifdef_fail_level = 1, in_elseif = true;
265 else if (ifdef_fail_level == 1 && defines_map.count(name) != 0)
266 ifdef_fail_level = 0, in_elseif = true;
267 continue;
268 }
269
270 if (tok == "`ifdef") {
271 skip_spaces();
272 std::string name = next_token(true);
273 if (ifdef_fail_level > 0 || defines_map.count(name) == 0)
274 ifdef_fail_level++;
275 continue;
276 }
277
278 if (tok == "`ifndef") {
279 skip_spaces();
280 std::string name = next_token(true);
281 if (ifdef_fail_level > 0 || defines_map.count(name) != 0)
282 ifdef_fail_level++;
283 continue;
284 }
285
286 if (ifdef_fail_level > 0) {
287 if (tok == "\n")
288 output_code.push_back(tok);
289 continue;
290 }
291
292 if (tok == "`include") {
293 skip_spaces();
294 std::string fn = next_token(true);
295 while (1) {
296 size_t pos = fn.find('"');
297 if (pos == std::string::npos)
298 break;
299 if (pos == 0)
300 fn = fn.substr(1);
301 else
302 fn = fn.substr(0, pos) + fn.substr(pos+1);
303 }
304 std::ifstream ff;
305 ff.clear();
306 ff.open(fn.c_str());
307 if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
308 // if the include file was not found, it is not given with an absolute path, and the
309 // currently read file is given with a path, then try again relative to its directory
310 ff.clear();
311 ff.open(filename.substr(0, filename.rfind('/')+1) + fn);
312 }
313 if (ff.fail() && fn.size() > 0 && fn[0] != '/') {
314 // if the include file was not found and it is not given with an absolute path, then
315 // search it in the include path
316 for (auto incdir : include_dirs) {
317 ff.clear();
318 ff.open(incdir + '/' + fn);
319 if (!ff.fail()) break;
320 }
321 }
322 if (ff.fail())
323 output_code.push_back("`file_notfound " + fn);
324 else
325 input_file(ff, fn);
326 continue;
327 }
328
329 if (tok == "`define") {
330 std::string name, value;
331 std::map<std::string, int> args;
332 skip_spaces();
333 name = next_token(true);
334 bool here_doc_mode = false;
335 int newline_count = 0;
336 int state = 0;
337 if (skip_spaces() != "")
338 state = 3;
339 while (!tok.empty()) {
340 tok = next_token();
341 if (tok == "\"\"\"") {
342 here_doc_mode = !here_doc_mode;
343 continue;
344 }
345 if (state == 0 && tok == "(") {
346 state = 1;
347 skip_spaces();
348 } else
349 if (state == 1) {
350 if (tok == ")")
351 state = 2;
352 else if (tok != ",") {
353 int arg_idx = args.size()+1;
354 args[tok] = arg_idx;
355 }
356 skip_spaces();
357 } else {
358 if (state != 2)
359 state = 3;
360 if (tok == "\n") {
361 if (here_doc_mode) {
362 value += " ";
363 newline_count++;
364 } else {
365 return_char('\n');
366 break;
367 }
368 } else
369 if (tok == "\\") {
370 char ch = next_char();
371 if (ch == '\n') {
372 value += " ";
373 newline_count++;
374 } else {
375 value += std::string("\\");
376 return_char(ch);
377 }
378 } else
379 if (args.count(tok) > 0)
380 value += stringf("`macro_%s_arg%d", name.c_str(), args.at(tok));
381 else
382 value += tok;
383 }
384 }
385 while (newline_count-- > 0)
386 return_char('\n');
387 // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
388 defines_map[name] = value;
389 if (state == 2)
390 defines_with_args.insert(name);
391 else
392 defines_with_args.erase(name);
393 global_defines_cache[name] = std::pair<std::string, bool>(value, state == 2);
394 continue;
395 }
396
397 if (tok == "`undef") {
398 std::string name;
399 skip_spaces();
400 name = next_token(true);
401 // printf("undef: >>%s<<\n", name.c_str());
402 defines_map.erase(name);
403 defines_with_args.erase(name);
404 global_defines_cache.erase(name);
405 continue;
406 }
407
408 if (tok == "`timescale") {
409 skip_spaces();
410 while (!tok.empty() && tok != "\n")
411 tok = next_token(true);
412 if (tok == "\n")
413 return_char('\n');
414 continue;
415 }
416
417 if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
418 std::string name = tok.substr(1);
419 // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str());
420 std::string skipped_spaces = skip_spaces();
421 tok = next_token(false);
422 if (tok == "(" && defines_with_args.count(name) > 0) {
423 int level = 1;
424 std::vector<std::string> args;
425 args.push_back(std::string());
426 while (1)
427 {
428 tok = next_token(true);
429 if (tok == ")" || tok == "}" || tok == "]")
430 level--;
431 if (level == 0)
432 break;
433 if (level == 1 && tok == ",")
434 args.push_back(std::string());
435 else
436 args.back() += tok;
437 if (tok == "(" || tok == "{" || tok == "[")
438 level++;
439 }
440 for (int i = 0; i < GetSize(args); i++)
441 defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i];
442 } else {
443 insert_input(tok);
444 insert_input(skipped_spaces);
445 }
446 insert_input(defines_map[name]);
447 continue;
448 }
449
450 output_code.push_back(tok);
451 }
452
453 std::string output;
454 for (auto &str : output_code)
455 output += str;
456
457 output_code.clear();
458 input_buffer.clear();
459 input_buffer_charp = 0;
460
461 return output;
462 }
463
464 YOSYS_NAMESPACE_END
465