diff options
author | Clifford Wolf <clifford@clifford.at> | 2013-01-05 11:13:26 +0100 |
---|---|---|
committer | Clifford Wolf <clifford@clifford.at> | 2013-01-05 11:13:26 +0100 |
commit | 7764d0ba1dcf064ae487ee985c43083a0909e7f4 (patch) | |
tree | 18c05b8729df381af71b707748ce1d605e0df764 /frontends/verilog/preproc.cc |
initial import
Diffstat (limited to 'frontends/verilog/preproc.cc')
-rw-r--r-- | frontends/verilog/preproc.cc | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc new file mode 100644 index 00000000..e6fdc1ff --- /dev/null +++ b/frontends/verilog/preproc.cc @@ -0,0 +1,360 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * Ad-hoc implementation of a Verilog preprocessor. The directives `define, + * `include, `ifdef, `ifndef, `else and `endif are handled here. All other + * directives are handled by the lexer (see lexer.l). + * + */ + +#include "verilog_frontend.h" +#include "kernel/log.h" +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <list> + +static std::list<std::string> output_code; +static std::list<std::string> input_buffer; +static size_t input_buffer_charp; + +static void return_char(char ch) +{ + if (input_buffer_charp == 0) + input_buffer.push_front(std::string() + ch); + else + input_buffer.front()[--input_buffer_charp] = ch; +} + +static void insert_input(std::string str) +{ + if (input_buffer_charp != 0) { + input_buffer.front() = input_buffer.front().substr(input_buffer_charp); + input_buffer_charp = 0; + } + input_buffer.push_front(str); +} + +static char next_char() +{ + if (input_buffer.size() == 0) + return 0; + + assert(input_buffer_charp <= input_buffer.front().size()); + if (input_buffer_charp == input_buffer.front().size()) { + input_buffer_charp = 0; + input_buffer.pop_front(); + return next_char(); + } + + char ch = input_buffer.front()[input_buffer_charp++]; + return ch == '\r' ? next_char() : ch; +} + +static void skip_spaces() +{ + while (1) { + char ch = next_char(); + if (ch == 0) + break; + if (ch != ' ' && ch != '\t') { + return_char(ch); + break; + } + } +} + +static std::string next_token(bool pass_newline = false) +{ + std::string token; + + char ch = next_char(); + if (ch == 0) + return token; + + token += ch; + if (ch == '\n') { + if (pass_newline) { + output_code.push_back(token); + return ""; + } + return token; + } + + if (ch == ' ' || ch == '\t') + { + while ((ch = next_char()) != 0) { + if (ch != ' ' && ch != '\t') { + return_char(ch); + break; + } + token += ch; + } + } + else if (ch == '"') + { + while ((ch = next_char()) != 0) { + token += ch; + if (ch == '"') + break; + if (ch == '\\') { + if ((ch = next_char()) != 0) + token += ch; + } + } + } + else if (ch == '/') + { + if ((ch = next_char()) != 0) { + if (ch == '/') { + token += '*'; + char last_ch = 0; + while ((ch = next_char()) != 0) { + if (ch == '\n') { + return_char(ch); + break; + } + if (last_ch != '*' || ch != '/') { + token += ch; + last_ch = ch; + } + } + token += " */"; + } + else if (ch == '*') { + token += '*'; + int newline_count = 0; + char last_ch = 0; + while ((ch = next_char()) != 0) { + if (ch == '\n') { + newline_count++; + token += ' '; + } else + token += ch; + if (last_ch == '*' && ch == '/') + break; + last_ch = ch; + } + while (newline_count-- > 0) + return_char('\n'); + } + else + return_char(ch); + } + } + else + { + const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789"; + while ((ch = next_char()) != 0) { + if (strchr(ok, ch) == NULL) { + return_char(ch); + break; + } + token += ch; + } + } + + return token; +} + +static void input_file(FILE *f, std::string filename) +{ + char buffer[513]; + int rc; + + insert_input(""); + auto it = input_buffer.begin(); + + input_buffer.insert(it, "`file_push " + filename + "\n"); + while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) { + buffer[rc] = 0; + input_buffer.insert(it, buffer); + } + input_buffer.insert(it, "`file_pop\n"); +} + +static std::string define_to_feature(std::string defname) +{ + if (defname == "__YOSYS_ENABLE_DEFATTR__") + return "defattr"; + return std::string(); +} + +std::string frontend_verilog_preproc(FILE *f, std::string filename) +{ + std::map<std::string, std::string> defines_map; + int ifdef_fail_level = 0; + + output_code.clear(); + input_buffer.clear(); + input_buffer_charp = 0; + + input_file(f, filename); + defines_map["__YOSYS__"] = "1"; + + while (!input_buffer.empty()) + { + std::string tok = next_token(); + // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); + + if (tok == "`endif") { + if (ifdef_fail_level > 0) + ifdef_fail_level--; + continue; + } + + if (tok == "`else") { + if (ifdef_fail_level == 0) + ifdef_fail_level = 1; + else if (ifdef_fail_level == 1) + ifdef_fail_level = 0; + continue; + } + + if (tok == "`ifdef") { + skip_spaces(); + std::string name = next_token(true); + if (ifdef_fail_level > 0 || defines_map.count(name) == 0) + ifdef_fail_level++; + continue; + } + + if (tok == "`ifndef") { + skip_spaces(); + std::string name = next_token(true); + if (ifdef_fail_level > 0 || defines_map.count(name) != 0) + ifdef_fail_level++; + continue; + } + + if (ifdef_fail_level > 0) { + if (tok == "\n") + output_code.push_back(tok); + continue; + } + + if (tok == "`include") { + skip_spaces(); + std::string fn = next_token(true); + while (1) { + size_t pos = fn.find('"'); + if (pos == std::string::npos) + break; + if (pos == 0) + fn = fn.substr(1); + else + fn = fn.substr(0, pos) + fn.substr(pos+1); + } + FILE *fp = fopen(fn.c_str(), "r"); + if (fp == NULL && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { + std::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn; + fp = fopen(fn2.c_str(), "r"); + } + if (fp != NULL) { + input_file(fp, fn); + fclose(fp); + } else + output_code.push_back("`file_notfound " + fn + "\n"); + continue; + } + + if (tok == "`define") { + std::string name, value; + skip_spaces(); + name = next_token(true); + if (!define_to_feature(name).empty()) + output_code.push_back("`yosys_enable_" + define_to_feature(name)); + skip_spaces(); + int newline_count = 0; + while (!tok.empty()) { + tok = next_token(); + if (tok == "\n") { + return_char('\n'); + break; + } + if (tok == "\\") { + char ch = next_char(); + if (ch == '\n') { + value += " "; + newline_count++; + } else { + value += std::string("\\"); + return_char(ch); + } + } else + value += tok; + } + while (newline_count-- > 0) + return_char('\n'); + // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); + defines_map[name] = value; + continue; + } + + if (tok == "`undef") { + std::string name; + skip_spaces(); + name = next_token(true); + if (!define_to_feature(name).empty()) + output_code.push_back("`yosys_disable_" + define_to_feature(name)); + // printf("undef: >>%s<<\n", name.c_str()); + defines_map.erase(name); + continue; + } + + if (tok == "`timescale") { + std::string name; + skip_spaces(); + while (!tok.empty() && tok != "\n") + tok = next_token(true); + if (tok == "\n") + return_char('\n'); + continue; + } + + if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) { + // printf("expand: >>%s<< -> >>%s<<\n", tok.c_str(), defines_map[tok.substr(1)].c_str()); + insert_input(defines_map[tok.substr(1)]); + continue; + } + + output_code.push_back(tok); + } + + std::string output; + for (auto &str : output_code) + output += str; + + output_code.clear(); + input_buffer.clear(); + input_buffer_charp = 0; + + return output; +} + |