summaryrefslogtreecommitdiff
path: root/frontends/verilog/preproc.cc
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2013-01-05 11:13:26 +0100
committerClifford Wolf <clifford@clifford.at>2013-01-05 11:13:26 +0100
commit7764d0ba1dcf064ae487ee985c43083a0909e7f4 (patch)
tree18c05b8729df381af71b707748ce1d605e0df764 /frontends/verilog/preproc.cc
initial import
Diffstat (limited to 'frontends/verilog/preproc.cc')
-rw-r--r--frontends/verilog/preproc.cc360
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;
+}
+