yosys-master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
preproc.cc
Go to the documentation of this file.
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 
42 
43 static std::list<std::string> output_code;
44 static std::list<std::string> input_buffer;
45 static size_t input_buffer_charp;
46 
47 static void return_char(char ch)
48 {
49  if (input_buffer_charp == 0)
50  input_buffer.push_front(std::string() + ch);
51  else
52  input_buffer.front()[--input_buffer_charp] = ch;
53 }
54 
55 static void insert_input(std::string str)
56 {
57  if (input_buffer_charp != 0) {
58  input_buffer.front() = input_buffer.front().substr(input_buffer_charp);
60  }
61  input_buffer.push_front(str);
62 }
63 
64 static char next_char()
65 {
66  if (input_buffer.empty())
67  return 0;
68 
69  log_assert(input_buffer_charp <= input_buffer.front().size());
70  if (input_buffer_charp == input_buffer.front().size()) {
72  input_buffer.pop_front();
73  return next_char();
74  }
75 
76  char ch = input_buffer.front()[input_buffer_charp++];
77  return ch == '\r' ? next_char() : ch;
78 }
79 
80 static std::string skip_spaces()
81 {
82  std::string spaces;
83  while (1) {
84  char ch = next_char();
85  if (ch == 0)
86  break;
87  if (ch != ' ' && ch != '\t') {
88  return_char(ch);
89  break;
90  }
91  spaces += ch;
92  }
93  return spaces;
94 }
95 
96 static std::string next_token(bool pass_newline = false)
97 {
98  std::string token;
99 
100  char ch = next_char();
101  if (ch == 0)
102  return token;
103 
104  token += ch;
105  if (ch == '\n') {
106  if (pass_newline) {
107  output_code.push_back(token);
108  return "";
109  }
110  return token;
111  }
112 
113  if (ch == ' ' || ch == '\t')
114  {
115  while ((ch = next_char()) != 0) {
116  if (ch != ' ' && ch != '\t') {
117  return_char(ch);
118  break;
119  }
120  token += ch;
121  }
122  }
123  else if (ch == '"')
124  {
125  while ((ch = next_char()) != 0) {
126  token += ch;
127  if (ch == '"')
128  break;
129  if (ch == '\\') {
130  if ((ch = next_char()) != 0)
131  token += ch;
132  }
133  }
134  if (token == "\"\"" && (ch = next_char()) != 0) {
135  if (ch == '"')
136  token += ch;
137  else
138  return_char(ch);
139  }
140  }
141  else if (ch == '/')
142  {
143  if ((ch = next_char()) != 0) {
144  if (ch == '/') {
145  token += '*';
146  char last_ch = 0;
147  while ((ch = next_char()) != 0) {
148  if (ch == '\n') {
149  return_char(ch);
150  break;
151  }
152  if (last_ch != '*' || ch != '/') {
153  token += ch;
154  last_ch = ch;
155  }
156  }
157  token += " */";
158  }
159  else if (ch == '*') {
160  token += '*';
161  int newline_count = 0;
162  char last_ch = 0;
163  while ((ch = next_char()) != 0) {
164  if (ch == '\n') {
165  newline_count++;
166  token += ' ';
167  } else
168  token += ch;
169  if (last_ch == '*' && ch == '/')
170  break;
171  last_ch = ch;
172  }
173  while (newline_count-- > 0)
174  return_char('\n');
175  }
176  else
177  return_char(ch);
178  }
179  }
180  else
181  {
182  const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789";
183  if (ch == '`' || strchr(ok, ch) != NULL)
184  while ((ch = next_char()) != 0) {
185  if (strchr(ok, ch) == NULL) {
186  return_char(ch);
187  break;
188  }
189  token += ch;
190  }
191  }
192 
193  return token;
194 }
195 
196 static void input_file(std::istream &f, std::string filename)
197 {
198  char buffer[513];
199  int rc;
200 
201  insert_input("");
202  auto it = input_buffer.begin();
203 
204  input_buffer.insert(it, "`file_push " + filename + "\n");
205  while ((rc = readsome(f, buffer, sizeof(buffer)-1)) > 0) {
206  buffer[rc] = 0;
207  input_buffer.insert(it, buffer);
208  }
209  input_buffer.insert(it, "\n`file_pop\n");
210 }
211 
212 std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs)
213 {
214  std::set<std::string> defines_with_args;
215  std::map<std::string, std::string> defines_map(pre_defines_map);
216  int ifdef_fail_level = 0;
217  bool in_elseif = false;
218 
219  output_code.clear();
220  input_buffer.clear();
221  input_buffer_charp = 0;
222 
223  input_file(f, filename);
224  defines_map["__YOSYS__"] = "1";
225 
226  while (!input_buffer.empty())
227  {
228  std::string tok = next_token();
229  // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE");
230 
231  if (tok == "`endif") {
232  if (ifdef_fail_level > 0)
233  ifdef_fail_level--;
234  if (ifdef_fail_level == 0)
235  in_elseif = false;
236  continue;
237  }
238 
239  if (tok == "`else") {
240  if (ifdef_fail_level == 0)
241  ifdef_fail_level = 1;
242  else if (ifdef_fail_level == 1 && !in_elseif)
243  ifdef_fail_level = 0;
244  continue;
245  }
246 
247  if (tok == "`elsif") {
248  skip_spaces();
249  std::string name = next_token(true);
250  if (ifdef_fail_level == 0)
251  ifdef_fail_level = 1, in_elseif = true;
252  else if (ifdef_fail_level == 1 && defines_map.count(name) != 0)
253  ifdef_fail_level = 0, in_elseif = true;
254  continue;
255  }
256 
257  if (tok == "`ifdef") {
258  skip_spaces();
259  std::string name = next_token(true);
260  if (ifdef_fail_level > 0 || defines_map.count(name) == 0)
261  ifdef_fail_level++;
262  continue;
263  }
264 
265  if (tok == "`ifndef") {
266  skip_spaces();
267  std::string name = next_token(true);
268  if (ifdef_fail_level > 0 || defines_map.count(name) != 0)
269  ifdef_fail_level++;
270  continue;
271  }
272 
273  if (ifdef_fail_level > 0) {
274  if (tok == "\n")
275  output_code.push_back(tok);
276  continue;
277  }
278 
279  if (tok == "`include") {
280  skip_spaces();
281  std::string fn = next_token(true);
282  while (1) {
283  size_t pos = fn.find('"');
284  if (pos == std::string::npos)
285  break;
286  if (pos == 0)
287  fn = fn.substr(1);
288  else
289  fn = fn.substr(0, pos) + fn.substr(pos+1);
290  }
291  std::ifstream ff;
292  ff.clear();
293  ff.open(fn.c_str());
294  if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
295  // if the include file was not found, it is not given with an absolute path, and the
296  // currently read file is given with a path, then try again relative to its directory
297  ff.clear();
298  ff.open(filename.substr(0, filename.rfind('/')+1) + fn);
299  }
300  if (ff.fail() && fn.size() > 0 && fn[0] != '/') {
301  // if the include file was not found and it is not given with an absolute path, then
302  // search it in the include path
303  for (auto incdir : include_dirs) {
304  ff.clear();
305  ff.open(incdir + '/' + fn);
306  if (!ff.fail()) break;
307  }
308  }
309  if (ff.fail())
310  output_code.push_back("`file_notfound " + fn);
311  else
312  input_file(ff, fn);
313  continue;
314  }
315 
316  if (tok == "`define") {
317  std::string name, value;
318  std::map<std::string, int> args;
319  skip_spaces();
320  name = next_token(true);
321  bool here_doc_mode = false;
322  int newline_count = 0;
323  int state = 0;
324  if (skip_spaces() != "")
325  state = 3;
326  while (!tok.empty()) {
327  tok = next_token();
328  if (tok == "\"\"\"") {
329  here_doc_mode = !here_doc_mode;
330  continue;
331  }
332  if (state == 0 && tok == "(") {
333  state = 1;
334  skip_spaces();
335  } else
336  if (state == 1) {
337  if (tok == ")")
338  state = 2;
339  else if (tok != ",") {
340  int arg_idx = args.size()+1;
341  args[tok] = arg_idx;
342  }
343  skip_spaces();
344  } else {
345  if (state != 2)
346  state = 3;
347  if (tok == "\n") {
348  if (here_doc_mode) {
349  value += " ";
350  newline_count++;
351  } else {
352  return_char('\n');
353  break;
354  }
355  } else
356  if (tok == "\\") {
357  char ch = next_char();
358  if (ch == '\n') {
359  value += " ";
360  newline_count++;
361  } else {
362  value += std::string("\\");
363  return_char(ch);
364  }
365  } else
366  if (args.count(tok) > 0)
367  value += stringf("`macro_%s_arg%d", name.c_str(), args.at(tok));
368  else
369  value += tok;
370  }
371  }
372  while (newline_count-- > 0)
373  return_char('\n');
374  // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
375  defines_map[name] = value;
376  if (state == 2)
377  defines_with_args.insert(name);
378  else
379  defines_with_args.erase(name);
380  continue;
381  }
382 
383  if (tok == "`undef") {
384  std::string name;
385  skip_spaces();
386  name = next_token(true);
387  // printf("undef: >>%s<<\n", name.c_str());
388  defines_map.erase(name);
389  defines_with_args.erase(name);
390  continue;
391  }
392 
393  if (tok == "`timescale") {
394  skip_spaces();
395  while (!tok.empty() && tok != "\n")
396  tok = next_token(true);
397  if (tok == "\n")
398  return_char('\n');
399  continue;
400  }
401 
402  if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
403  std::string name = tok.substr(1);
404  // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str());
405  std::string skipped_spaces = skip_spaces();
406  tok = next_token(false);
407  if (tok == "(" && defines_with_args.count(name) > 0) {
408  int level = 1;
409  std::vector<std::string> args;
410  args.push_back(std::string());
411  while (1)
412  {
413  tok = next_token(true);
414  if (tok == ")" || tok == "}" || tok == "]")
415  level--;
416  if (level == 0)
417  break;
418  if (level == 1 && tok == ",")
419  args.push_back(std::string());
420  else
421  args.back() += tok;
422  if (tok == "(" || tok == "{" || tok == "[")
423  level++;
424  }
425  for (int i = 0; i < GetSize(args); i++)
426  defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i];
427  } else {
428  insert_input(tok);
429  insert_input(skipped_spaces);
430  }
431  insert_input(defines_map[name]);
432  continue;
433  }
434 
435  output_code.push_back(tok);
436  }
437 
438  std::string output;
439  for (auto &str : output_code)
440  output += str;
441 
442  output_code.clear();
443  input_buffer.clear();
444  input_buffer_charp = 0;
445 
446  return output;
447 }
448 
450 
static std::string next_token(bool pass_newline=false)
Definition: preproc.cc:96
std::string stringf(const char *fmt,...)
Definition: yosys.cc:58
#define YOSYS_NAMESPACE_END
Definition: yosys.h:100
std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map< std::string, std::string > pre_defines_map, const std::list< std::string > include_dirs)
Definition: preproc.cc:212
static std::string skip_spaces()
Definition: preproc.cc:80
int GetSize(RTLIL::Wire *wire)
Definition: yosys.cc:334
#define log_assert(_assert_expr_)
Definition: log.h:85
int readsome(std::istream &f, char *s, int n)
Definition: yosys.cc:100
static void return_char(char ch)
Definition: preproc.cc:47
static size_t input_buffer_charp
Definition: preproc.cc:45
#define NULL
#define YOSYS_NAMESPACE_BEGIN
Definition: yosys.h:99
static char next_char()
Definition: preproc.cc:64
static std::list< std::string > input_buffer
Definition: preproc.cc:44
static void insert_input(std::string str)
Definition: preproc.cc:55
static void input_file(std::istream &f, std::string filename)
Definition: preproc.cc:196
static YOSYS_NAMESPACE_BEGIN std::list< std::string > output_code
Definition: preproc.cc:43