summaryrefslogtreecommitdiff
path: root/frontends
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
initial import
Diffstat (limited to 'frontends')
-rw-r--r--frontends/ast/Makefile.inc5
-rw-r--r--frontends/ast/ast.cc859
-rw-r--r--frontends/ast/ast.h228
-rw-r--r--frontends/ast/genrtlil.cc1054
-rw-r--r--frontends/ast/simplify.cc1081
-rw-r--r--frontends/ilang/Makefile.inc16
-rw-r--r--frontends/ilang/ilang_frontend.cc49
-rw-r--r--frontends/ilang/ilang_frontend.h45
-rw-r--r--frontends/ilang/lexer.l122
-rw-r--r--frontends/ilang/parser.y416
-rw-r--r--frontends/verilog/Makefile.inc19
-rw-r--r--frontends/verilog/const2ast.cc197
-rw-r--r--frontends/verilog/lexer.l264
-rw-r--r--frontends/verilog/parser.y1074
-rw-r--r--frontends/verilog/preproc.cc360
-rw-r--r--frontends/verilog/verilog_frontend.cc148
-rw-r--r--frontends/verilog/verilog_frontend.h62
17 files changed, 5999 insertions, 0 deletions
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc
new file mode 100644
index 00000000..993ead92
--- /dev/null
+++ b/frontends/ast/Makefile.inc
@@ -0,0 +1,5 @@
+
+OBJS += frontends/ast/ast.o
+OBJS += frontends/ast/simplify.o
+OBJS += frontends/ast/genrtlil.o
+
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
new file mode 100644
index 00000000..160e9c42
--- /dev/null
+++ b/frontends/ast/ast.cc
@@ -0,0 +1,859 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#include "kernel/log.h"
+#include "kernel/sha1.h"
+#include "ast.h"
+
+#include <sstream>
+#include <stdarg.h>
+#include <assert.h>
+
+using namespace AST;
+using namespace AST_INTERNAL;
+
+// instanciate global variables (public API)
+namespace AST {
+ std::string current_filename;
+ void (*set_line_num)(int) = NULL;
+ int (*get_line_num)() = NULL;
+}
+
+// instanciate global variables (private API)
+namespace AST_INTERNAL {
+ bool flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg;
+ AstNode *current_ast, *current_ast_mod;
+ std::map<std::string, AstNode*> current_scope;
+ RTLIL::SigSpec *genRTLIL_subst_from = NULL;
+ RTLIL::SigSpec *genRTLIL_subst_to = NULL;
+ AstNode *current_top_block, *current_block, *current_block_child;
+ AstModule *current_module;
+}
+
+// convert node types to string
+std::string AST::type2str(AstNodeType type)
+{
+ switch (type)
+ {
+#define X(_item) case _item: return #_item;
+ X(AST_NONE)
+ X(AST_DESIGN)
+ X(AST_MODULE)
+ X(AST_TASK)
+ X(AST_FUNCTION)
+ X(AST_WIRE)
+ X(AST_MEMORY)
+ X(AST_AUTOWIRE)
+ X(AST_PARAMETER)
+ X(AST_LOCALPARAM)
+ X(AST_PARASET)
+ X(AST_ARGUMENT)
+ X(AST_RANGE)
+ X(AST_CONSTANT)
+ X(AST_CELLTYPE)
+ X(AST_IDENTIFIER)
+ X(AST_FCALL)
+ X(AST_TO_SIGNED)
+ X(AST_TO_UNSIGNED)
+ X(AST_CONCAT)
+ X(AST_REPLICATE)
+ X(AST_BIT_NOT)
+ X(AST_BIT_AND)
+ X(AST_BIT_OR)
+ X(AST_BIT_XOR)
+ X(AST_BIT_XNOR)
+ X(AST_REDUCE_AND)
+ X(AST_REDUCE_OR)
+ X(AST_REDUCE_XOR)
+ X(AST_REDUCE_XNOR)
+ X(AST_REDUCE_BOOL)
+ X(AST_SHIFT_LEFT)
+ X(AST_SHIFT_RIGHT)
+ X(AST_SHIFT_SLEFT)
+ X(AST_SHIFT_SRIGHT)
+ X(AST_LT)
+ X(AST_LE)
+ X(AST_EQ)
+ X(AST_NE)
+ X(AST_GE)
+ X(AST_GT)
+ X(AST_ADD)
+ X(AST_SUB)
+ X(AST_MUL)
+ X(AST_DIV)
+ X(AST_MOD)
+ X(AST_POW)
+ X(AST_POS)
+ X(AST_NEG)
+ X(AST_LOGIC_AND)
+ X(AST_LOGIC_OR)
+ X(AST_LOGIC_NOT)
+ X(AST_TERNARY)
+ X(AST_MEMRD)
+ X(AST_MEMWR)
+ X(AST_TCALL)
+ X(AST_ASSIGN)
+ X(AST_CELL)
+ X(AST_PRIMITIVE)
+ X(AST_ALWAYS)
+ X(AST_BLOCK)
+ X(AST_ASSIGN_EQ)
+ X(AST_ASSIGN_LE)
+ X(AST_CASE)
+ X(AST_COND)
+ X(AST_DEFAULT)
+ X(AST_FOR)
+ X(AST_GENVAR)
+ X(AST_GENFOR)
+ X(AST_GENIF)
+ X(AST_GENBLOCK)
+ X(AST_POSEDGE)
+ X(AST_NEGEDGE)
+ X(AST_EDGE)
+#undef X
+ default:
+ assert(!"Missing enum to string def in AST::type2str().");
+ abort();
+ }
+}
+
+// create new node (AstNode constructor)
+// (the optional child arguments make it easier to create AST trees)
+AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2)
+{
+ this->type = type;
+ filename = current_filename;
+ linenum = get_line_num();
+ is_input = false;
+ is_output = false;
+ is_reg = false;
+ is_signed = false;
+ range_valid = false;
+ port_id = 0;
+ range_left = -1;
+ range_right = 0;
+ integer = 0;
+ id2ast = NULL;
+
+ if (child1)
+ children.push_back(child1);
+ if (child2)
+ children.push_back(child2);
+}
+
+// create a (deep recursive) copy of a node
+AstNode *AstNode::clone()
+{
+ AstNode *that = new AstNode;
+ *that = *this;
+ for (auto &it : that->children)
+ it = it->clone();
+ for (auto &it : that->attributes)
+ it.second = it.second->clone();
+ return that;
+}
+
+// create a (deep recursive) copy of a node use 'other' as target root node
+void AstNode::cloneInto(AstNode *other)
+{
+ AstNode *tmp = clone();
+ other->delete_children();
+ *other = *tmp;
+ tmp->children.clear();
+ tmp->attributes.clear();
+ delete tmp;
+}
+
+// delete all children in this node
+void AstNode::delete_children()
+{
+ for (auto &it : children)
+ delete it;
+ children.clear();
+
+ for (auto &it : attributes)
+ delete it.second;
+ attributes.clear();
+}
+
+// AstNode destructor
+AstNode::~AstNode()
+{
+ delete_children();
+}
+
+// create a nice text representation of the node
+// (traverse tree by recursion, use 'other' pointer for diffing two AST trees)
+void AstNode::dumpAst(FILE *f, std::string indent, AstNode *other)
+{
+ if (f == NULL) {
+ for (auto f : log_files)
+ dumpAst(f, indent, other);
+ return;
+ }
+ if (other != NULL) {
+ if (type != other->type)
+ goto found_diff_to_other;
+ if (children.size() != other->children.size())
+ goto found_diff_to_other;
+ if (str != other->str)
+ goto found_diff_to_other;
+ if (bits != other->bits)
+ goto found_diff_to_other;
+ if (is_input != other->is_input)
+ goto found_diff_to_other;
+ if (is_output != other->is_output)
+ goto found_diff_to_other;
+ if (is_reg != other->is_reg)
+ goto found_diff_to_other;
+ if (is_signed != other->is_signed)
+ goto found_diff_to_other;
+ if (range_valid != other->range_valid)
+ goto found_diff_to_other;
+ if (port_id != other->port_id)
+ goto found_diff_to_other;
+ if (range_left != other->range_left)
+ goto found_diff_to_other;
+ if (range_right != other->range_right)
+ goto found_diff_to_other;
+ if (integer != other->integer)
+ goto found_diff_to_other;
+ if (0) {
+ found_diff_to_other:
+ other->dumpAst(f, indent + "- ");
+ this->dumpAst(f, indent + "+ ");
+ return;
+ }
+ }
+
+ std::string type_name = type2str(type);
+ fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
+ if (!str.empty())
+ fprintf(f, " str='%s'", str.c_str());
+ if (!bits.empty()) {
+ fprintf(f, " bits='");
+ for (size_t i = bits.size(); i > 0; i--)
+ fprintf(f, "%c", bits[i-1] == RTLIL::S0 ? '0' :
+ bits[i-1] == RTLIL::S1 ? '1' :
+ bits[i-1] == RTLIL::Sx ? 'x' :
+ bits[i-1] == RTLIL::Sz ? 'z' : '?');
+ fprintf(f, "'(%zd)", bits.size());
+ }
+ if (is_input)
+ fprintf(f, " input");
+ if (is_output)
+ fprintf(f, " output");
+ if (is_reg)
+ fprintf(f, " reg");
+ if (is_signed)
+ fprintf(f, " signed");
+ if (port_id > 0)
+ fprintf(f, " port=%d", port_id);
+ if (range_valid || range_left != -1 || range_right != 0)
+ fprintf(f, " range=[%d:%d]%s", range_left, range_right, range_valid ? "" : "!");
+ if (integer != 0)
+ fprintf(f, " int=%u", (int)integer);
+ fprintf(f, "\n");
+
+ for (size_t i = 0; i < children.size(); i++)
+ children[i]->dumpAst(f, indent + " ", other ? other->children[i] : NULL);
+}
+
+// helper function for AstNode::dumpVlog()
+static std::string id2vl(std::string txt)
+{
+ if (txt.size() > 1 && txt[0] == '\\')
+ txt = txt.substr(1);
+ for (size_t i = 0; i < txt.size(); i++) {
+ if ('A' <= txt[i] && txt[i] <= 'Z') continue;
+ if ('a' <= txt[i] && txt[i] <= 'z') continue;
+ if ('0' <= txt[i] && txt[i] <= '9') continue;
+ if (txt[i] == '_') continue;
+ txt = "\\" + txt + " ";
+ break;
+ }
+ return txt;
+}
+
+// dump AST node as verilog pseudo-code
+void AstNode::dumpVlog(FILE *f, std::string indent)
+{
+ bool first = true;
+ std::string txt;
+ std::vector<AstNode*> rem_children1, rem_children2;
+
+ if (f == NULL) {
+ for (auto f : log_files)
+ dumpVlog(f, indent);
+ return;
+ }
+
+ switch (type)
+ {
+ case AST_MODULE:
+ fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str());
+ for (auto child : children)
+ if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
+ fprintf(f, "%s%s", first ? "" : ", ", id2vl(child->str).c_str());
+ first = false;
+ }
+ fprintf(f, ");\n");
+
+ for (auto child : children)
+ if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
+ child->dumpVlog(f, indent + " ");
+ else
+ rem_children1.push_back(child);
+
+ for (auto child : rem_children1)
+ if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY)
+ child->dumpVlog(f, indent + " ");
+ else
+ rem_children2.push_back(child);
+ rem_children1.clear();
+
+ for (auto child : rem_children2)
+ if (child->type == AST_TASK || child->type == AST_FUNCTION)
+ child->dumpVlog(f, indent + " ");
+ else
+ rem_children1.push_back(child);
+ rem_children2.clear();
+
+ for (auto child : rem_children1)
+ child->dumpVlog(f, indent + " ");
+ rem_children1.clear();
+
+ fprintf(f, "%s" "endmodule\n", indent.c_str());
+ break;
+
+ case AST_WIRE:
+ if (is_input && is_output)
+ fprintf(f, "%s" "inout", indent.c_str());
+ else if (is_input)
+ fprintf(f, "%s" "input", indent.c_str());
+ else if (is_output)
+ fprintf(f, "%s" "output", indent.c_str());
+ else if (!is_reg)
+ fprintf(f, "%s" "wire", indent.c_str());
+ if (is_reg)
+ fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str());
+ if (is_signed)
+ fprintf(f, " signed");
+ for (auto child : children) {
+ fprintf(f, " ");
+ child->dumpVlog(f, "");
+ }
+ fprintf(f, " %s", id2vl(str).c_str());
+ fprintf(f, ";\n");
+ break;
+
+ case AST_MEMORY:
+ fprintf(f, "%s" "memory", indent.c_str());
+ if (is_signed)
+ fprintf(f, " signed");
+ for (auto child : children) {
+ fprintf(f, " ");
+ child->dumpVlog(f, "");
+ if (first)
+ fprintf(f, " %s", id2vl(str).c_str());
+ first = false;
+ }
+ fprintf(f, ";\n");
+ break;
+
+ case AST_RANGE:
+ if (range_valid)
+ fprintf(f, "[%d:%d]", range_left, range_right);
+ else {
+ for (auto child : children) {
+ fprintf(f, "%c", first ? '[' : ':');
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ fprintf(f, "]");
+ }
+ break;
+
+ case AST_ALWAYS:
+ fprintf(f, "%s" "always @(", indent.c_str());
+ for (auto child : children) {
+ if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
+ continue;
+ if (!first)
+ fprintf(f, ", ");
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ fprintf(f, ")\n");
+ for (auto child : children) {
+ if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
+ child->dumpVlog(f, indent + " ");
+ }
+ break;
+
+ case AST_POSEDGE:
+ case AST_NEGEDGE:
+ case AST_EDGE:
+ if (type == AST_POSEDGE)
+ fprintf(f, "posedge ");
+ if (type == AST_NEGEDGE)
+ fprintf(f, "negedge ");
+ for (auto child : children)
+ child->dumpVlog(f, "");
+ break;
+
+ case AST_IDENTIFIER:
+ fprintf(f, "%s", id2vl(str).c_str());
+ for (auto child : children)
+ child->dumpVlog(f, "");
+ break;
+
+ case AST_CONSTANT:
+ if (!str.empty())
+ fprintf(f, "\"%s\"", str.c_str());
+ else if (bits.size() == 32)
+ fprintf(f, "%d", RTLIL::Const(bits).as_int());
+ else
+ fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str());
+ break;
+
+ case AST_BLOCK:
+ if (children.size() == 1) {
+ children[0]->dumpVlog(f, indent);
+ } else {
+ fprintf(f, "%s" "begin\n", indent.c_str());
+ for (auto child : children)
+ child->dumpVlog(f, indent + " ");
+ fprintf(f, "%s" "end\n", indent.c_str());
+ }
+ break;
+
+ case AST_CASE:
+ fprintf(f, "%s" "case (", indent.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ")\n");
+ for (size_t i = 1; i < children.size(); i++) {
+ AstNode *child = children[i];
+ child->dumpVlog(f, indent + " ");
+ }
+ fprintf(f, "%s" "endcase\n", indent.c_str());
+ break;
+
+ case AST_COND:
+ for (auto child : children) {
+ if (child->type == AST_BLOCK) {
+ fprintf(f, ":\n");
+ child->dumpVlog(f, indent + " ");
+ first = true;
+ } else {
+ fprintf(f, "%s", first ? indent.c_str() : ", ");
+ if (child->type == AST_DEFAULT)
+ fprintf(f, "default");
+ else
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ }
+ break;
+
+ case AST_ASSIGN_EQ:
+ case AST_ASSIGN_LE:
+ fprintf(f, "%s", indent.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, " %s ", type == AST_ASSIGN_EQ ? "=" : "<=");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ";\n");
+ break;
+
+ case AST_CONCAT:
+ fprintf(f, "{");
+ for (auto child : children) {
+ if (!first)
+ fprintf(f, ", ");
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ fprintf(f, "}");
+ break;
+
+ case AST_REPLICATE:
+ fprintf(f, "{");
+ children[0]->dumpVlog(f, "");
+ fprintf(f, "{");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, "}}");
+ break;
+
+ if (0) { case AST_BIT_NOT: txt = "~"; }
+ if (0) { case AST_REDUCE_AND: txt = "&"; }
+ if (0) { case AST_REDUCE_OR: txt = "|"; }
+ if (0) { case AST_REDUCE_XOR: txt = "^"; }
+ if (0) { case AST_REDUCE_XNOR: txt = "~^"; }
+ if (0) { case AST_REDUCE_BOOL: txt = "|"; }
+ if (0) { case AST_POS: txt = "+"; }
+ if (0) { case AST_NEG: txt = "-"; }
+ if (0) { case AST_LOGIC_NOT: txt = "!"; }
+ fprintf(f, "%s(", txt.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ")");
+ break;
+
+ if (0) { case AST_BIT_AND: txt = "&"; }
+ if (0) { case AST_BIT_OR: txt = "|"; }
+ if (0) { case AST_BIT_XOR: txt = "^"; }
+ if (0) { case AST_BIT_XNOR: txt = "~^"; }
+ if (0) { case AST_SHIFT_LEFT: txt = "<<"; }
+ if (0) { case AST_SHIFT_RIGHT: txt = ">>"; }
+ if (0) { case AST_SHIFT_SLEFT: txt = "<<<"; }
+ if (0) { case AST_SHIFT_SRIGHT: txt = ">>>"; }
+ if (0) { case AST_LT: txt = "<"; }
+ if (0) { case AST_LE: txt = "<="; }
+ if (0) { case AST_EQ: txt = "=="; }
+ if (0) { case AST_NE: txt = "!="; }
+ if (0) { case AST_GE: txt = ">="; }
+ if (0) { case AST_GT: txt = ">"; }
+ if (0) { case AST_ADD: txt = "+"; }
+ if (0) { case AST_SUB: txt = "-"; }
+ if (0) { case AST_MUL: txt = "*"; }
+ if (0) { case AST_DIV: txt = "/"; }
+ if (0) { case AST_MOD: txt = "%"; }
+ if (0) { case AST_POW: txt = "**"; }
+ if (0) { case AST_LOGIC_AND: txt = "&&"; }
+ if (0) { case AST_LOGIC_OR: txt = "||"; }
+ fprintf(f, "(");
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ")%s(", txt.c_str());
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ")");
+ break;
+
+ case AST_TERNARY:
+ fprintf(f, "(");
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ") ? (");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ") : (");
+ children[2]->dumpVlog(f, "");
+ fprintf(f, ")");
+ break;
+
+ default:
+ std::string type_name = type2str(type);
+ fprintf(f, "%s" "/** %s **/%s", indent.c_str(), type_name.c_str(), indent.empty() ? "" : "\n");
+ // dumpAst(f, indent, NULL);
+ }
+}
+
+// check if two AST nodes are identical
+bool AstNode::operator==(const AstNode &other) const
+{
+ if (type != other.type)
+ return false;
+ if (children.size() != other.children.size())
+ return false;
+ if (str != other.str)
+ return false;
+ if (bits != other.bits)
+ return false;
+ if (is_input != other.is_input)
+ return false;
+ if (is_output != other.is_output)
+ return false;
+ if (is_reg != other.is_reg)
+ return false;
+ if (is_signed != other.is_signed)
+ return false;
+ if (range_valid != other.range_valid)
+ return false;
+ if (port_id != other.port_id)
+ return false;
+ if (range_left != other.range_left)
+ return false;
+ if (range_right != other.range_right)
+ return false;
+ if (integer != other.integer)
+ return false;
+ for (size_t i = 0; i < children.size(); i++)
+ if (*children[i] != *other.children[i])
+ return false;
+ return true;
+}
+
+// check if two AST nodes are not identical
+bool AstNode::operator!=(const AstNode &other) const
+{
+ return !(*this == other);
+}
+
+// check if this AST contains the given node
+bool AstNode::contains(const AstNode *other) const
+{
+ if (this == other)
+ return true;
+ for (auto child : children)
+ if (child->contains(other))
+ return true;
+ return false;
+}
+
+// create an AST node for a constant (using a 32 bit int as value)
+AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width)
+{
+ AstNode *node = new AstNode(AST_CONSTANT);
+ node->integer = v;
+ node->is_signed = is_signed;
+ for (int i = 0; i < width; i++) {
+ node->bits.push_back((v & 1) ? RTLIL::S1 : RTLIL::S0);
+ v = v >> 1;
+ }
+ node->range_valid = true;
+ node->range_left = width-1;
+ node->range_right = 0;
+ return node;
+}
+
+// create an AST node for a constant (using a bit vector as value)
+AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
+{
+ AstNode *node = new AstNode(AST_CONSTANT);
+ node->is_signed = is_signed;
+ node->bits = v;
+ for (size_t i = 0; i < 32; i++) {
+ if (i < node->bits.size())
+ node->integer |= (node->bits[i] == RTLIL::S1) << i;
+ else if (is_signed)
+ node->integer |= (node->bits.back() == RTLIL::S1) << i;
+ }
+ node->range_valid = true;
+ node->range_left = node->bits.size();
+ node->range_right = 0;
+ return node;
+}
+
+// create a new AstModule from an AST_MODULE AST node
+static AstModule* process_module(AstNode *ast)
+{
+ assert(ast->type == AST_MODULE);
+ log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str());
+
+ current_ast_mod = ast;
+ AstNode *ast_before_simplify = ast->clone();
+
+ while (ast->simplify(false, false, false, 0)) { }
+
+ if (flag_dump_ast) {
+ log("Dumping verilog AST (as requested by %s option):\n", flag_dump_ast_diff ? "dump_ast_diff" : "dump_ast");
+ ast->dumpAst(NULL, " ", flag_dump_ast_diff ? ast_before_simplify : NULL);
+ log("--- END OF AST DUMP ---\n");
+ }
+
+ if (flag_dump_vlog) {
+ log("Dumping verilog AST (as requested by dump_vlog option):\n");
+ ast->dumpVlog(NULL, " ");
+ log("--- END OF AST DUMP ---\n");
+ }
+
+ current_module = new AstModule;
+ current_module->ast = NULL;
+ current_module->name = ast->str;
+ current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
+ for (auto &attr : ast->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), ast->filename.c_str(), ast->linenum);
+ current_module->attributes[attr.first].str = attr.second->str;
+ current_module->attributes[attr.first].bits = attr.second->bits;
+ }
+ for (size_t i = 0; i < ast->children.size(); i++) {
+ AstNode *node = ast->children[i];
+ if (node->type == AST_WIRE || node->type == AST_MEMORY)
+ node->genRTLIL();
+ }
+ for (size_t i = 0; i < ast->children.size(); i++) {
+ AstNode *node = ast->children[i];
+ if (node->type != AST_WIRE && node->type != AST_MEMORY)
+ node->genRTLIL();
+ }
+
+ current_module->ast = ast_before_simplify;
+ current_module->nolatches = flag_nolatches;
+ current_module->nomem2reg = flag_nomem2reg;
+ return current_module;
+}
+
+// create AstModule instances for all modules in the AST tree and add them to 'design'
+void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast, bool dump_ast_diff, bool dump_vlog, bool nolatches, bool nomem2reg)
+{
+ current_ast = ast;
+ flag_dump_ast = dump_ast;
+ flag_dump_ast_diff = dump_ast_diff;
+ flag_dump_vlog = dump_vlog;
+ flag_nolatches = nolatches;
+ flag_nomem2reg = nomem2reg;
+
+ assert(current_ast->type == AST_DESIGN);
+ for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) {
+ if (design->modules.count((*it)->str) != 0)
+ log_error("Re-definition of module `%s' at %s:%d!\n",
+ (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
+ design->modules[(*it)->str] = process_module(*it);
+ }
+}
+
+// AstModule destructor
+AstModule::~AstModule()
+{
+ if (ast != NULL)
+ delete ast;
+}
+
+// create a new parametric module (when needed) and return the name of the generated module
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters)
+{
+ log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", name.c_str());
+
+ current_ast = NULL;
+ flag_dump_ast = false;
+ flag_dump_ast_diff = false;
+ flag_dump_vlog = false;
+ flag_nolatches = nolatches;
+ flag_nomem2reg = nomem2reg;
+ use_internal_line_num();
+
+ std::vector<unsigned char> hash_data;
+ hash_data.insert(hash_data.end(), name.begin(), name.end());
+ hash_data.push_back(0);
+
+ AstNode *new_ast = ast->clone();
+
+ int para_counter = 0;
+ for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) {
+ AstNode *child = *it;
+ if (child->type != AST_PARAMETER)
+ continue;
+ para_counter++;
+ std::string para_id = child->str;
+ if (parameters.count(child->str) > 0) {
+ log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
+ rewrite_parameter:
+ child->delete_children();
+ child->children.push_back(AstNode::mkconst_bits(parameters[para_id].bits, false));
+ hash_data.insert(hash_data.end(), child->str.begin(), child->str.end());
+ hash_data.push_back(0);
+ hash_data.insert(hash_data.end(), parameters[para_id].bits.begin(), parameters[para_id].bits.end());
+ hash_data.push_back(0xff);
+ parameters.erase(para_id);
+ continue;
+ }
+ char buf[100];
+ snprintf(buf, 100, "$%d", para_counter);
+ if (parameters.count(buf) > 0) {
+ para_id = buf;
+ log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ goto rewrite_parameter;
+ }
+ }
+ if (parameters.size() > 0)
+ log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), name.c_str());
+
+ unsigned char hash[20];
+ unsigned char *hash_data2 = new unsigned char[hash_data.size()];
+ for (size_t i = 0; i < hash_data.size(); i++)
+ hash_data2[i] = hash_data[i];
+ sha1::calc(hash_data2, hash_data.size(), hash);
+ delete[] hash_data2;
+
+ char hexstring[41];
+ sha1::toHexString(hash, hexstring);
+
+ std::string modname = "$paramod$" + std::string(hexstring) + "$" + name;
+
+ if (design->modules.count(modname) == 0) {
+ new_ast->str = modname;
+ design->modules[modname] = process_module(new_ast);
+ } else {
+ log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
+ }
+
+ delete new_ast;
+ return modname;
+}
+
+// recompile a module from AST with updated widths for auto-wires
+// (auto-wires are wires that are used but not declared an thus have an automatically determined width)
+void AstModule::update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes)
+{
+ log_header("Executing AST frontend in update_auto_wires mode using pre-parsed AST for module `%s'.\n", name.c_str());
+
+ current_ast = NULL;
+ flag_dump_ast = false;
+ flag_dump_ast_diff = false;
+ flag_dump_vlog = false;
+ flag_nolatches = nolatches;
+ flag_nomem2reg = nomem2reg;
+ use_internal_line_num();
+
+ for (auto it = auto_sizes.begin(); it != auto_sizes.end(); it++) {
+ log("Adding extra wire declaration to AST: wire [%d:0] %s\n", it->second - 1, it->first.c_str());
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(it->second - 1, true), AstNode::mkconst_int(0, true)));
+ wire->str = it->first;
+ ast->children.insert(ast->children.begin(), wire);
+ }
+
+ AstModule *newmod = process_module(ast);
+
+ delete ast;
+ ast = newmod->ast;
+ newmod->ast = NULL;
+
+ wires.swap(newmod->wires);
+ cells.swap(newmod->cells);
+ processes.swap(newmod->processes);
+ connections.swap(newmod->connections);
+ attributes.swap(newmod->attributes);
+ delete newmod;
+}
+
+// internal dummy line number callbacks
+namespace {
+ int internal_line_num;
+ void internal_set_line_num(int n) {
+ internal_line_num = n;
+ }
+ int internal_get_line_num() {
+ return internal_line_num;
+ }
+}
+
+// use internal dummy line number callbacks
+void AST::use_internal_line_num()
+{
+ set_line_num = &internal_set_line_num;
+ get_line_num = &internal_get_line_num;
+}
+
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
new file mode 100644
index 00000000..f7c9328c
--- /dev/null
+++ b/frontends/ast/ast.h
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#ifndef AST_H
+#define AST_H
+
+#include "kernel/rtlil.h"
+#include <stdint.h>
+#include <set>
+
+namespace AST
+{
+ // all node types, type2str() must be extended
+ // whenever a new node type is added here
+ enum AstNodeType
+ {
+ AST_NONE,
+ AST_DESIGN,
+ AST_MODULE,
+ AST_TASK,
+ AST_FUNCTION,
+
+ AST_WIRE,
+ AST_MEMORY,
+ AST_AUTOWIRE,
+ AST_PARAMETER,
+ AST_LOCALPARAM,
+ AST_PARASET,
+ AST_ARGUMENT,
+ AST_RANGE,
+ AST_CONSTANT,
+ AST_CELLTYPE,
+ AST_IDENTIFIER,
+
+ AST_FCALL,
+ AST_TO_SIGNED,
+ AST_TO_UNSIGNED,
+ AST_CONCAT,
+ AST_REPLICATE,
+ AST_BIT_NOT,
+ AST_BIT_AND,
+ AST_BIT_OR,
+ AST_BIT_XOR,
+ AST_BIT_XNOR,
+ AST_REDUCE_AND,
+ AST_REDUCE_OR,
+ AST_REDUCE_XOR,
+ AST_REDUCE_XNOR,
+ AST_REDUCE_BOOL,
+ AST_SHIFT_LEFT,
+ AST_SHIFT_RIGHT,
+ AST_SHIFT_SLEFT,
+ AST_SHIFT_SRIGHT,
+ AST_LT,
+ AST_LE,
+ AST_EQ,
+ AST_NE,
+ AST_GE,
+ AST_GT,
+ AST_ADD,
+ AST_SUB,
+ AST_MUL,
+ AST_DIV,
+ AST_MOD,
+ AST_POW,
+ AST_POS,
+ AST_NEG,
+ AST_LOGIC_AND,
+ AST_LOGIC_OR,
+ AST_LOGIC_NOT,
+ AST_TERNARY,
+ AST_MEMRD,
+ AST_MEMWR,
+
+ AST_TCALL,
+ AST_ASSIGN,
+ AST_CELL,
+ AST_PRIMITIVE,
+ AST_ALWAYS,
+ AST_BLOCK,
+ AST_ASSIGN_EQ,
+ AST_ASSIGN_LE,
+ AST_CASE,
+ AST_COND,
+ AST_DEFAULT,
+ AST_FOR,
+
+ AST_GENVAR,
+ AST_GENFOR,
+ AST_GENIF,
+ AST_GENBLOCK,
+
+ AST_POSEDGE,
+ AST_NEGEDGE,
+ AST_EDGE
+ };
+
+ // convert an node type to a string (e.g. for debug output)
+ std::string type2str(AstNodeType type);
+
+ // The AST is built using instances of this struct
+ struct AstNode
+ {
+ // this nodes type
+ AstNodeType type;
+
+ // the list of child nodes for this node
+ std::vector<AstNode*> children;
+
+ // the list of attributes assigned to this node
+ std::map<RTLIL::IdString, AstNode*> attributes;
+
+ // node content - most of it is unused in most node types
+ std::string str;
+ std::vector<RTLIL::State> bits;
+ bool is_input, is_output, is_reg, is_signed, range_valid;
+ int port_id, range_left, range_right;
+ uint32_t integer;
+
+ // this is set by simplify and used during RTLIL generation
+ AstNode *id2ast;
+
+ // this is the original sourcecode location that resulted in this AST node
+ // it is automatically set by the constructor using AST::current_filename and
+ // the AST::get_line_num() callback function.
+ std::string filename;
+ int linenum;
+
+ // creating and deleting nodes
+ AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL);
+ AstNode *clone();
+ void cloneInto(AstNode *other);
+ void delete_children();
+ ~AstNode();
+
+ // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
+ // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
+ bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage);
+ void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map);
+ void replace_ids(std::map<std::string, std::string> &rules);
+ void mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc);
+ void mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block);
+ void meminfo(int &mem_width, int &mem_size, int &addr_bits);
+
+ // create a human-readable text representation of the AST (for debugging)
+ void dumpAst(FILE *f, std::string indent, AstNode *other = NULL);
+ void dumpVlog(FILE *f, std::string indent);
+
+ // create RTLIL code for this AST node
+ // for expressions the resulting signal vector is returned
+ // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
+ RTLIL::SigSpec genRTLIL(int width_hint = -1);
+ RTLIL::SigSpec genWidthRTLIL(int width, RTLIL::SigSpec *subst_from = NULL, RTLIL::SigSpec *subst_to = NULL);
+
+ // compare AST nodes
+ bool operator==(const AstNode &other) const;
+ bool operator!=(const AstNode &other) const;
+ bool contains(const AstNode *other) const;
+
+ // helper functions for creating AST nodes for constants
+ static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
+ static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
+ };
+
+ // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
+ void process(RTLIL::Design *design, AstNode *ast, bool dump_ast = false, bool dump_ast_diff = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false);
+
+ // parametric modules are supported directly by the AST library
+ // therfore we need our own derivate of RTLIL::Module with overloaded virtual functions
+ struct AstModule : RTLIL::Module {
+ AstNode *ast;
+ bool nolatches, nomem2reg;
+ virtual ~AstModule();
+ virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters);
+ virtual void update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes);
+ };
+
+ // this must be set by the language frontend before parsing the sources
+ // the AstNode constructor then uses current_filename and get_line_num()
+ // to initialize the filename and linenum properties of new nodes
+ extern std::string current_filename;
+ extern void (*set_line_num)(int);
+ extern int (*get_line_num)();
+
+ // set set_line_num and get_line_num to internal dummy functions
+ // (done by simplify(), AstModule::derive and AstModule::update_auto_wires to control
+ // the filename and linenum properties of new nodes not generated by a frontend parser)
+ void use_internal_line_num();
+}
+
+namespace AST_INTERNAL
+{
+ // internal state variables
+ extern bool flag_dump_ast, flag_dump_ast_diff, flag_nolatches, flag_nomem2reg;
+ extern AST::AstNode *current_ast, *current_ast_mod;
+ extern std::map<std::string, AST::AstNode*> current_scope;
+ extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to;
+ extern AST::AstNode *current_top_block, *current_block, *current_block_child;
+ extern AST::AstModule *current_module;
+ struct ProcessGenerator;
+}
+
+#endif
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
new file mode 100644
index 00000000..9f1acb61
--- /dev/null
+++ b/frontends/ast/genrtlil.cc
@@ -0,0 +1,1054 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#include "kernel/log.h"
+#include "kernel/sha1.h"
+#include "ast.h"
+
+#include <sstream>
+#include <stdarg.h>
+#include <assert.h>
+
+using namespace AST;
+using namespace AST_INTERNAL;
+
+// helper function for creating RTLIL code for unary operations
+static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true)
+{
+ std::stringstream sstr;
+ sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++);
+
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+ cell->name = sstr.str();
+ cell->type = type;
+ current_module->cells[cell->name] = cell;
+
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+ wire->name = cell->name + "_Y";
+ wire->width = result_width;
+ current_module->wires[wire->name] = wire;
+
+ RTLIL::SigChunk chunk;
+ chunk.wire = wire;
+ chunk.width = wire->width;
+ chunk.offset = 0;
+
+ RTLIL::SigSpec sig;
+ sig.chunks.push_back(chunk);
+ sig.width = chunk.width;
+
+ if (gen_attributes)
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first].str = attr.second->str;
+ cell->attributes[attr.first].bits = attr.second->bits;
+ }
+
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.width);
+ cell->connections["\\A"] = arg;
+
+ cell->parameters["\\Y_WIDTH"] = result_width;
+ cell->connections["\\Y"] = sig;
+ return sig;
+}
+
+// helper function for creating RTLIL code for binary operations
+static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ std::stringstream sstr;
+ sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++);
+
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+ cell->name = sstr.str();
+ cell->type = type;
+ current_module->cells[cell->name] = cell;
+
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+ wire->name = cell->name + "_Y";
+ wire->width = result_width;
+ current_module->wires[wire->name] = wire;
+
+ RTLIL::SigChunk chunk;
+ chunk.wire = wire;
+ chunk.width = wire->width;
+ chunk.offset = 0;
+
+ RTLIL::SigSpec sig;
+ sig.chunks.push_back(chunk);
+ sig.width = chunk.width;
+
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first].str = attr.second->str;
+ cell->attributes[attr.first].bits = attr.second->bits;
+ }
+
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed);
+ cell->parameters["\\B_SIGNED"] = RTLIL::Const(that->children[1]->is_signed);
+
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.width);
+ cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.width);
+
+ cell->connections["\\A"] = left;
+ cell->connections["\\B"] = right;
+
+ cell->parameters["\\Y_WIDTH"] = result_width;
+ cell->connections["\\Y"] = sig;
+ return sig;
+}
+
+// helper function for creating RTLIL code for multiplexers
+static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ assert(cond.width == 1);
+
+ std::stringstream sstr;
+ sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++);
+
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+ cell->name = sstr.str();
+ cell->type = "$mux";
+ current_module->cells[cell->name] = cell;
+
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+ wire->name = cell->name + "_Y";
+ wire->width = left.width;
+ current_module->wires[wire->name] = wire;
+
+ RTLIL::SigChunk chunk;
+ chunk.wire = wire;
+ chunk.width = wire->width;
+ chunk.offset = 0;
+
+ RTLIL::SigSpec sig;
+ sig.chunks.push_back(chunk);
+ sig.width = chunk.width;
+
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first].str = attr.second->str;
+ cell->attributes[attr.first].bits = attr.second->bits;
+ }
+
+ cell->parameters["\\WIDTH"] = RTLIL::Const(left.width);
+
+ cell->connections["\\A"] = right;
+ cell->connections["\\B"] = left;
+ cell->connections["\\S"] = cond;
+ cell->connections["\\Y"] = sig;
+
+ return sig;
+}
+
+// helper class for converting AST always nodes to RTLIL processes
+struct AST_INTERNAL::ProcessGenerator
+{
+ // input and output structures
+ AstNode *always;
+ RTLIL::Process *proc;
+
+ // This always points to the RTLIL::CaseRule beeing filled at the moment
+ RTLIL::CaseRule *current_case;
+
+ // This two variables contain the replacement pattern to be used in the right hand side
+ // of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right
+ // hand side of the 2nd assignment needs to be replace with the temporary signal holding
+ // the value assigned in the first assignment. So when the first assignement is processed
+ // the according information is appended to subst_rvalue_from and subst_rvalue_to.
+ RTLIL::SigSpec subst_rvalue_from, subst_rvalue_to;
+
+ // This two variables contain the replacement pattern to be used in the left hand side
+ // of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar
+ // should not be connected to the signal foo. Instead it must be connected to the temporary
+ // signal that is used as input for the register that drives the signal foo.
+ RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to;
+
+ // The code here generates a number of temprorary signal for each output register. This
+ // map helps generating nice numbered names for all this temporary signals.
+ std::map<RTLIL::Wire*, int> new_temp_count;
+
+ ProcessGenerator(AstNode *always) : always(always)
+ {
+ // generate process and simple root case
+ proc = new RTLIL::Process;
+ proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, RTLIL::autoidx++);
+ for (auto &attr : always->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), always->filename.c_str(), always->linenum);
+ proc->attributes[attr.first].str = attr.second->str;
+ proc->attributes[attr.first].bits = attr.second->bits;
+ }
+ current_module->processes[proc->name] = proc;
+ current_case = &proc->root_case;
+
+ // create initial temporary signal for all output registers
+ collect_lvalues(subst_lvalue_from, always, true, true);
+ subst_lvalue_to = new_temp_signal(subst_lvalue_from);
+
+ bool found_anyedge_syncs = false;
+ for (auto child : always->children)
+ if (child->type == AST_EDGE)
+ found_anyedge_syncs = true;
+
+ if (found_anyedge_syncs) {
+ log("Note: Assuming pure combinatorial block at %s:%d in\n", always->filename.c_str(), always->linenum);
+ log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");
+ log("use of @* instead of @(...) for better match of synthesis and simulation.\n");
+ }
+
+ // create syncs for the process
+ bool found_clocked_sync = false;
+ for (auto child : always->children)
+ if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) {
+ found_clocked_sync = true;
+ if (found_anyedge_syncs)
+ log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
+ RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
+ syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
+ syncrule->signal = child->children[0]->genRTLIL();
+ addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to);
+ proc->syncs.push_back(syncrule);
+ }
+ if (proc->syncs.empty()) {
+ RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
+ syncrule->type = RTLIL::STa;
+ syncrule->signal = RTLIL::SigSpec();
+ addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to);
+ proc->syncs.push_back(syncrule);
+ }
+
+ // create initial assignments for the temporary signals
+ if ((flag_nolatches || always->attributes.count("\\nolatches") > 0 || current_module->attributes.count("\\nolatches")) && !found_clocked_sync) {
+ subst_rvalue_from = subst_lvalue_from;
+ subst_rvalue_to = RTLIL::SigSpec(RTLIL::State::Sx, subst_rvalue_from.width);
+ } else {
+ addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
+ }
+
+ // process the AST
+ for (auto child : always->children)
+ if (child->type == AST_BLOCK)
+ processAst(child);
+ }
+
+ // create new temporary signals
+ RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig)
+ {
+ sig.optimize();
+ for (size_t i = 0; i < sig.chunks.size(); i++)
+ {
+ RTLIL::SigChunk &chunk = sig.chunks[i];
+ if (chunk.wire == NULL)
+ continue;
+
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum);
+ do {
+ wire->name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++,
+ chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);;
+ } while (current_module->wires.count(wire->name) > 0);
+ wire->width = chunk.width;
+ current_module->wires[wire->name] = wire;
+
+ chunk.wire = wire;
+ chunk.offset = 0;
+ }
+ return sig;
+ }
+
+ // recursively traverse the AST an collect all assigned signals
+ void collect_lvalues(RTLIL::SigSpec &reg, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true)
+ {
+ switch (ast->type)
+ {
+ case AST_CASE:
+ for (auto child : ast->children)
+ if (child != ast->children[0]) {
+ assert(child->type == AST_COND);
+ collect_lvalues(reg, child, type_eq, type_le, false);
+ }
+ break;
+
+ case AST_COND:
+ case AST_ALWAYS:
+ for (auto child : ast->children)
+ if (child->type == AST_BLOCK)
+ collect_lvalues(reg, child, type_eq, type_le, false);
+ break;
+
+ case AST_BLOCK:
+ for (auto child : ast->children) {
+ if (child->type == AST_ASSIGN_EQ && type_eq)
+ reg.append(child->children[0]->genRTLIL());
+ if (child->type == AST_ASSIGN_LE && type_le)
+ reg.append(child->children[0]->genRTLIL());
+ if (child->type == AST_CASE || child->type == AST_BLOCK)
+ collect_lvalues(reg, child, type_eq, type_le, false);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (run_sort_and_unify)
+ reg.sort_and_unify();
+ }
+
+ // remove all assignments to the given signal pattern in a case and all its children
+ // when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this
+ // function is acalled to clean up the first two assignments as they are overwritten by
+ // the third assignment.
+ void removeSignalFromCaseTree(RTLIL::SigSpec pattern, RTLIL::CaseRule *cs)
+ {
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
+ it->first.remove2(pattern, &it->second);
+
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
+ for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
+ removeSignalFromCaseTree(pattern, *it2);
+ }
+
+ // add an assignment (aka "action") but split it up in chunks. this way huge assignments
+ // are avoided and the generated $mux cells have a more "natural" size.
+ void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue)
+ {
+ assert(lvalue.width == rvalue.width);
+ lvalue.optimize();
+ rvalue.optimize();
+
+ int offset = 0;
+ for (size_t i = 0; i < lvalue.chunks.size(); i++) {
+ RTLIL::SigSpec lhs = lvalue.chunks[i];
+ RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue.chunks[i].width);
+ actions.push_back(RTLIL::SigSig(lhs, rhs));
+ offset += lhs.width;
+ }
+ }
+
+ // recursively process the AST and fill the RTLIL::Process
+ void processAst(AstNode *ast)
+ {
+ switch (ast->type)
+ {
+ case AST_BLOCK:
+ for (auto child : ast->children)
+ processAst(child);
+ break;
+
+ case AST_ASSIGN_EQ:
+ case AST_ASSIGN_LE:
+ {
+ RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue;
+ RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.width, &subst_rvalue_from, &subst_rvalue_to);
+ lvalue.replace(subst_lvalue_from, subst_lvalue_to);
+
+ if (ast->type == AST_ASSIGN_EQ) {
+ subst_rvalue_from.remove2(unmapped_lvalue, &subst_rvalue_to);
+ subst_rvalue_from.append(unmapped_lvalue);
+ subst_rvalue_from.optimize();
+ subst_rvalue_to.append(rvalue);
+ subst_rvalue_to.optimize();
+ }
+
+ removeSignalFromCaseTree(lvalue, current_case);
+ current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
+ }
+ break;
+
+ case AST_CASE:
+ {
+ RTLIL::SwitchRule *sw = new RTLIL::SwitchRule;
+ sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_from, &subst_rvalue_to);
+ current_case->switches.push_back(sw);
+
+ for (auto &attr : ast->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), ast->filename.c_str(), ast->linenum);
+ sw->attributes[attr.first].str = attr.second->str;
+ sw->attributes[attr.first].bits = attr.second->bits;
+ }
+
+ RTLIL::SigSpec this_case_eq_lvalue;
+ collect_lvalues(this_case_eq_lvalue, ast, true, false);
+
+ RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue);
+
+ RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue;
+ this_case_eq_rvalue.replace(subst_rvalue_from, subst_rvalue_to);
+
+ RTLIL::SigSpec backup_subst_lvalue_from = subst_lvalue_from;
+ RTLIL::SigSpec backup_subst_lvalue_to = subst_lvalue_to;
+
+ RTLIL::SigSpec backup_subst_rvalue_from = subst_rvalue_from;
+ RTLIL::SigSpec backup_subst_rvalue_to = subst_rvalue_to;
+
+ bool generated_default_case = false;
+ RTLIL::CaseRule *last_generated_case = NULL;
+ for (auto child : ast->children)
+ {
+ if (child == ast->children[0] || generated_default_case)
+ continue;
+ assert(child->type == AST_COND);
+
+ subst_lvalue_from = backup_subst_lvalue_from;
+ subst_lvalue_to = backup_subst_lvalue_to;
+
+ subst_rvalue_from = backup_subst_rvalue_from;
+ subst_rvalue_to = backup_subst_rvalue_to;
+
+ subst_lvalue_from.remove2(this_case_eq_lvalue, &subst_lvalue_to);
+ subst_lvalue_from.append(this_case_eq_lvalue);
+ subst_lvalue_from.optimize();
+ subst_lvalue_to.append(this_case_eq_ltemp);
+ subst_lvalue_to.optimize();
+
+ RTLIL::CaseRule *backup_case = current_case;
+ current_case = new RTLIL::CaseRule;
+ last_generated_case = current_case;
+ addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
+ for (auto node : child->children) {
+ if (node->type == AST_DEFAULT) {
+ generated_default_case = true;
+ current_case->compare.clear();
+ } else if (node->type == AST_BLOCK) {
+ processAst(node);
+ } else if (!generated_default_case)
+ current_case->compare.push_back(node->genWidthRTLIL(sw->signal.width));
+ }
+ sw->cases.push_back(current_case);
+ current_case = backup_case;
+ }
+
+ if (last_generated_case != NULL && ast->attributes.count("\\full_case") > 0) {
+ last_generated_case->compare.clear();
+ } else if (!generated_default_case) {
+ RTLIL::CaseRule *default_case = new RTLIL::CaseRule;
+ addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
+ sw->cases.push_back(default_case);
+ }
+
+ subst_lvalue_from = backup_subst_lvalue_from;
+ subst_lvalue_to = backup_subst_lvalue_to;
+
+ subst_rvalue_from = backup_subst_rvalue_from;
+ subst_rvalue_to = backup_subst_rvalue_to;
+
+ subst_rvalue_from.remove2(this_case_eq_lvalue, &subst_rvalue_to);
+ subst_rvalue_from.append(this_case_eq_lvalue);
+ subst_rvalue_from.optimize();
+ subst_rvalue_to.append(this_case_eq_ltemp);
+ subst_rvalue_to.optimize();
+
+ this_case_eq_lvalue.replace(subst_lvalue_from, subst_lvalue_to);
+ removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
+ addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
+ }
+ break;
+
+ case AST_TCALL:
+ case AST_FOR:
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+};
+
+// create RTLIL from an AST node
+// all generated cells, wires and processes are added to the module pointed to by 'current_module'
+// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
+//
+// note that this function is influenced by a number of global variables that might be set when
+// called from genWidthRTLIL(). also note that this function recursively calls itself to transform
+// larger expressions into a netlist of cells.
+RTLIL::SigSpec AstNode::genRTLIL(int width_hint)
+{
+ // in the following big switch() statement there are some uses of
+ // Clifford's Device (http://www.clifford.at/cfun/cliffdev/). In this
+ // cases this variable is used to hold the type of the cell that should
+ // be instanciated for this type of AST node.
+ std::string type_name;
+
+ current_filename = filename;
+ set_line_num(linenum);
+
+ switch (type)
+ {
+ // simply ignore this nodes.
+ // they are eighter leftovers from simplify() or are referenced by other nodes
+ // and are only accessed here thru this references
+ case AST_TASK:
+ case AST_FUNCTION:
+ case AST_AUTOWIRE:
+ case AST_PARAMETER:
+ case AST_LOCALPARAM:
+ case AST_GENVAR:
+ case AST_GENFOR:
+ case AST_GENIF:
+ break;
+
+ // create an RTLIL::Wire for an AST_WIRE node
+ case AST_WIRE: {
+ if (current_module->wires.count(str) != 0)
+ log_error("Re-definition of signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ if (!range_valid)
+ log_error("Signal `%s' with non-constant width at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ if (range_left < range_right && (range_left != -1 || range_right != 0)) {
+ int tmp = range_left;
+ range_left = range_right;
+ range_right = tmp;
+ }
+
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->name = str;
+ wire->width = range_left - range_right + 1;
+ wire->start_offset = range_right;
+ wire->port_id = port_id;
+ wire->port_input = is_input;
+ wire->port_output = is_output;
+ current_module->wires[wire->name] = wire;
+
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ wire->attributes[attr.first].str = attr.second->str;
+ wire->attributes[attr.first].bits = attr.second->bits;
+ }
+ }
+ break;
+
+ // create an RTLIL::Memory for an AST_MEMORY node
+ case AST_MEMORY: {
+ if (current_module->memories.count(str) != 0)
+ log_error("Re-definition of memory `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ assert(children.size() >= 2);
+ assert(children[0]->type == AST_RANGE);
+ assert(children[1]->type == AST_RANGE);
+
+ if (!children[0]->range_valid || !children[1]->range_valid)
+ log_error("Memory `%s' with non-constant width or size at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ RTLIL::Memory *memory = new RTLIL::Memory;
+ memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ memory->name = str;
+ memory->width = children[0]->range_left - children[0]->range_right + 1;
+ memory->start_offset = children[0]->range_right;
+ memory->size = children[1]->range_left - children[1]->range_right;
+ current_module->memories[memory->name] = memory;
+
+ if (memory->size < 0)
+ memory->size *= -1;
+ memory->size += std::min(children[1]->range_left, children[1]->range_right) + 1;
+
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ memory->attributes[attr.first].str = attr.second->str;
+ memory->attributes[attr.first].bits = attr.second->bits;
+ }
+ }
+ break;
+
+ // simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node
+ case AST_CONSTANT:
+ {
+ RTLIL::SigChunk chunk;
+ chunk.wire = NULL;
+ chunk.data.bits = bits;
+ chunk.width = bits.size();
+ chunk.offset = 0;
+
+ RTLIL::SigSpec sig;
+ sig.chunks.push_back(chunk);
+ sig.width = chunk.width;
+ return sig;
+ }
+
+ // simply return the corresponding RTLIL::SigSpec for an AST_IDENTIFIER node
+ // for identifiers with dynamic bit ranges (e.g. "foo[bar]" or "foo[bar+3:bar]") a
+ // shifter cell is created and the output signal of this cell is returned
+ case AST_IDENTIFIER:
+ {
+ if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires.count(str) == 0) {
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->name = str;
+ if (width_hint >= 0) {
+ wire->width = width_hint;
+ log("Warning: Identifier `%s' is implicitly declared with width %d at %s:%d.\n",
+ str.c_str(), width_hint, filename.c_str(), linenum);
+ } else {
+ log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n",
+ str.c_str(), filename.c_str(), linenum);
+ }
+ wire->auto_width = true;
+ current_module->wires[str] = wire;
+ }
+ else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
+ id2ast->type != AST_MEMORY) || current_module->wires.count(str) == 0)
+ log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ if (id2ast->type == AST_MEMORY)
+ log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ RTLIL::Wire *wire = current_module->wires[str];
+
+ RTLIL::SigChunk chunk;
+ chunk.wire = wire;
+ chunk.width = wire->width;
+ chunk.offset = 0;
+
+ if (children.size() != 0) {
+ assert(children[0]->type == AST_RANGE);
+ if (!children[0]->range_valid) {
+ AstNode *left_at_zero_ast = children[0]->children[0]->clone();
+ AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone();
+ while (left_at_zero_ast->simplify(true, true, false, 1)) { }
+ while (right_at_zero_ast->simplify(true, true, false, 1)) { }
+ if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+ log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
+ children[0]->children[1]->clone() : children[0]->children[0]->clone());
+ fake_ast->children[0]->delete_children();
+ RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shr", width,
+ fake_ast->children[0]->genRTLIL(), fake_ast->children[1]->genRTLIL());
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
+ delete fake_ast;
+ return sig;
+ } else {
+ chunk.offset = children[0]->range_right - id2ast->range_right;
+ chunk.width = children[0]->range_left - children[0]->range_right + 1;
+ if (children[0]->range_left > id2ast->range_left || id2ast->range_right > children[0]->range_right)
+ log_error("Range select out of bounds on signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ }
+ }
+
+ RTLIL::SigSpec sig;
+ sig.chunks.push_back(chunk);
+ sig.width = chunk.width;
+
+ if (genRTLIL_subst_from && genRTLIL_subst_to)
+ sig.replace(*genRTLIL_subst_from, *genRTLIL_subst_to);
+
+ is_signed = id2ast->is_signed;
+ if (children.size() != 0)
+ is_signed = false;
+
+ return sig;
+ }
+
+ // just pass thru the signal. the parent will evaluated the is_signed property and inperpret the SigSpec accordingly
+ case AST_TO_SIGNED:
+ case AST_TO_UNSIGNED: {
+ RTLIL::SigSpec sig = children[0]->genRTLIL(width_hint);
+ is_signed = type == AST_TO_SIGNED;
+ return sig;
+ }
+
+ // concatenation of signals can be done directly using RTLIL::SigSpec
+ case AST_CONCAT: {
+ RTLIL::SigSpec sig;
+ sig.width = 0;
+ for (auto it = children.begin(); it != children.end(); it++) {
+ RTLIL::SigSpec s = (*it)->genRTLIL();
+ for (size_t i = 0; i < s.chunks.size(); i++) {
+ sig.chunks.push_back(s.chunks[i]);
+ sig.width += s.chunks[i].width;
+ }
+ }
+ return sig;
+ }
+
+ // replication of signals can be done directly using RTLIL::SigSpec
+ case AST_REPLICATE: {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ if (!left.is_fully_const())
+ log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ int count = left.as_int();
+ RTLIL::SigSpec sig;
+ for (int i = 0; i < count; i++)
+ sig.append(right);
+ is_signed = false;
+ return sig;
+ }
+
+ // generate cells for unary operations: $not, $pos, $neg
+ if (0) { case AST_BIT_NOT: type_name = "$not"; }
+ if (0) { case AST_POS: type_name = "$pos"; }
+ if (0) { case AST_NEG: type_name = "$neg"; }
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint);
+ is_signed = type == AST_NEG || (type == AST_POS && children[0]->is_signed);
+ int width = type == AST_NEG && arg.width < width_hint ? arg.width+1 : arg.width;
+ if (width > width_hint && width_hint > 0)
+ width = width_hint;
+ return uniop2rtlil(this, type_name, width, arg);
+ }
+
+ // generate cells for binary operations: $and, $or, $xor, $xnor
+ if (0) { case AST_BIT_AND: type_name = "$and"; }
+ if (0) { case AST_BIT_OR: type_name = "$or"; }
+ if (0) { case AST_BIT_XOR: type_name = "$xor"; }
+ if (0) { case AST_BIT_XNOR: type_name = "$xnor"; }
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(width_hint);
+ int width = std::max(left.width, right.width);
+ if (width > width_hint && width_hint > 0)
+ width = width_hint;
+ return binop2rtlil(this, type_name, width, left, right);
+ }
+
+ // generate cells for unary operations: $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor
+ if (0) { case AST_REDUCE_AND: type_name = "$reduce_and"; }
+ if (0) { case AST_REDUCE_OR: type_name = "$reduce_or"; }
+ if (0) { case AST_REDUCE_XOR: type_name = "$reduce_xor"; }
+ if (0) { case AST_REDUCE_XNOR: type_name = "$reduce_xnor"; }
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL();
+ RTLIL::SigSpec sig = uniop2rtlil(this, type_name, 1, arg);
+ return sig;
+ }
+
+ // generate cells for unary operations: $reduce_bool
+ // (this is actually just an $reduce_or, but for clearity a different cell type is used)
+ if (0) { case AST_REDUCE_BOOL: type_name = "$reduce_bool"; }
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL();
+ RTLIL::SigSpec sig = arg.width > 1 ? uniop2rtlil(this, type_name, 1, arg) : arg;
+ return sig;
+ }
+
+ // generate cells for binary operations: $shl, $shr, $sshl, $sshr
+ if (0) { case AST_SHIFT_LEFT: type_name = "$shl"; }
+ if (0) { case AST_SHIFT_RIGHT: type_name = "$shr"; }
+ if (0) { case AST_SHIFT_SLEFT: type_name = "$sshl"; is_signed = true; }
+ if (0) { case AST_SHIFT_SRIGHT: type_name = "$sshr"; is_signed = true; }
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(width_hint);
+ int width = width_hint > 0 ? width_hint : left.width;
+ return binop2rtlil(this, type_name, width, left, right);
+ }
+
+ // generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt
+ if (0) { case AST_LT: type_name = "$lt"; }
+ if (0) { case AST_LE: type_name = "$le"; }
+ if (0) { case AST_EQ: type_name = "$eq"; }
+ if (0) { case AST_NE: type_name = "$ne"; }
+ if (0) { case AST_GE: type_name = "$ge"; }
+ if (0) { case AST_GT: type_name = "$gt"; }
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ RTLIL::SigSpec sig = binop2rtlil(this, type_name, 1, left, right);
+ return sig;
+ }
+
+ // generate cells for binary operations: $add, $sub, $mul, $div, $mod, $pow
+ if (0) { case AST_ADD: type_name = "$add"; }
+ if (0) { case AST_SUB: type_name = "$sub"; }
+ if (0) { case AST_MUL: type_name = "$mul"; }
+ if (0) { case AST_DIV: type_name = "$div"; }
+ if (0) { case AST_MOD: type_name = "$mod"; }
+ if (0) { case AST_POW: type_name = "$pow"; }
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(width_hint);
+ int width = std::max(left.width, right.width);
+ if (width > width_hint && width_hint > 0)
+ width = width_hint;
+ if (width < width_hint) {
+ if (type == AST_ADD || type == AST_SUB) {
+ width++;
+ if (width < width_hint && children[0]->is_signed != children[1]->is_signed)
+ width++;
+ }
+ if (type == AST_SUB && !children[0]->is_signed && !children[1]->is_signed)
+ width = width_hint;
+ if (type == AST_MUL)
+ width = std::min(left.width + right.width, width_hint);
+ }
+ is_signed = children[0]->is_signed || children[1]->is_signed;
+ return binop2rtlil(this, type_name, width, left, right);
+ }
+
+ // generate cells for binary operations: $logic_and, $logic_or
+ if (0) { case AST_LOGIC_AND: type_name = "$logic_and"; }
+ if (0) { case AST_LOGIC_OR: type_name = "$logic_or"; }
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ return binop2rtlil(this, type_name, 1, left, right);
+ }
+
+ // generate cells for unary operations: $logic_not
+ case AST_LOGIC_NOT:
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL();
+ return uniop2rtlil(this, "$logic_not", 1, arg);
+ }
+
+ // generate multiplexer for ternary operator (aka ?:-operator)
+ case AST_TERNARY:
+ {
+ RTLIL::SigSpec cond = children[0]->genRTLIL();
+ RTLIL::SigSpec val1 = children[1]->genRTLIL();
+ RTLIL::SigSpec val2 = children[2]->genRTLIL();
+
+ if (cond.width > 1)
+ cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false);
+
+ int width = std::max(val1.width, val2.width);
+ if (children[1]->is_signed && children[2]->is_signed) {
+ is_signed = true;
+ val1.extend(width, children[1]->is_signed);
+ val2.extend(width, children[2]->is_signed);
+ } else {
+ is_signed = false;
+ val1.extend(width);
+ val2.extend(width);
+ }
+
+ return mux2rtlil(this, cond, val1, val2);
+ }
+
+ // generate $memrd cells for memory read ports
+ case AST_MEMRD:
+ {
+ std::stringstream sstr;
+ sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ cell->name = sstr.str();
+ cell->type = "$memrd";
+ current_module->cells[cell->name] = cell;
+
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->name = cell->name + "_DATA";
+ wire->width = current_module->memories[str]->width;
+ current_module->wires[wire->name] = wire;
+
+ int addr_bits = 1;
+ while ((1 << addr_bits) < current_module->memories[str]->size)
+ addr_bits++;
+
+ cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1);
+ cell->connections["\\ADDR"] = children[0]->genRTLIL();
+ cell->connections["\\DATA"] = RTLIL::SigSpec(wire);
+
+ cell->parameters["\\MEMID"] = RTLIL::Const(str);
+ cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
+ cell->parameters["\\WIDTH"] = RTLIL::Const(wire->width);
+
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0);
+
+ return RTLIL::SigSpec(wire);
+ }
+
+ // generate $memwr cells for memory write ports
+ case AST_MEMWR:
+ {
+ std::stringstream sstr;
+ sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ cell->name = sstr.str();
+ cell->type = "$memwr";
+ current_module->cells[cell->name] = cell;
+
+ int addr_bits = 1;
+ while ((1 << addr_bits) < current_module->memories[str]->size)
+ addr_bits++;
+
+ cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1);
+ cell->connections["\\ADDR"] = children[0]->genRTLIL();
+ cell->connections["\\DATA"] = children[1]->genRTLIL();
+ cell->connections["\\EN"] = children[2]->genRTLIL();
+
+ cell->parameters["\\MEMID"] = RTLIL::Const(str);
+ cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
+ cell->parameters["\\WIDTH"] = RTLIL::Const(current_module->memories[str]->width);
+
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0);
+ }
+ break;
+
+ // add entries to current_module->connections for assignments (outside of always blocks)
+ case AST_ASSIGN:
+ {
+ if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_AUTOWIRE) {
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.width);
+ current_module->connections.push_back(RTLIL::SigSig(left, right));
+ } else {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.width);
+ current_module->connections.push_back(RTLIL::SigSig(left, right));
+ }
+ }
+ break;
+
+ // create an RTLIL::Cell for an AST_CELL
+ case AST_CELL:
+ {
+ int port_counter = 0, para_counter = 0;
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ cell->name = str;
+ for (auto it = children.begin(); it != children.end(); it++) {
+ AstNode *child = *it;
+ if (child->type == AST_CELLTYPE) {
+ cell->type = child->str;
+ continue;
+ }
+ if (child->type == AST_PARASET) {
+ if (child->children[0]->type != AST_CONSTANT)
+ log_error("Parameter `%s' with non-constant value at %s:%d!\n",
+ child->str.c_str(), filename.c_str(), linenum);
+ if (child->str.size() == 0) {
+ char buf[100];
+ snprintf(buf, 100, "$%d", ++para_counter);
+ cell->parameters[buf].str = child->children[0]->str;
+ cell->parameters[buf].bits = child->children[0]->bits;
+ } else {
+ cell->parameters[child->str].str = child->children[0]->str;
+ cell->parameters[child->str].bits = child->children[0]->bits;
+ }
+ continue;
+ }
+ if (child->type == AST_ARGUMENT) {
+ RTLIL::SigSpec sig;
+ if (child->children.size() > 0)
+ sig = child->children[0]->genRTLIL();
+ if (child->str.size() == 0) {
+ char buf[100];
+ snprintf(buf, 100, "$%d", ++port_counter);
+ cell->connections[buf] = sig;
+ } else {
+ cell->connections[child->str] = sig;
+ }
+ continue;
+ }
+ assert(0);
+ }
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ cell->attributes[attr.first].str = attr.second->str;
+ cell->attributes[attr.first].bits = attr.second->bits;
+ }
+ if (current_module->cells.count(cell->name) != 0)
+ log_error("Re-definition of cell `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ current_module->cells[str] = cell;
+ }
+ break;
+
+ // use ProcessGenerator for always blocks
+ case AST_ALWAYS: {
+ AstNode *always = this->clone();
+ ProcessGenerator generator(always);
+ delete always;
+ } break;
+
+ // everything should have been handled above -> print error if not.
+ default:
+ for (auto f : log_files)
+ current_ast->dumpAst(f, "verilog-ast> ");
+ type_name = type2str(type);
+ log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n",
+ type_name.c_str(), filename.c_str(), linenum);
+ }
+
+ return RTLIL::SigSpec();
+}
+
+// this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or
+// signals must be substituted before beeing used as input values (used by ProcessGenerator)
+// note that this is using some global variables to communicate this special settings to AstNode::genRTLIL().
+RTLIL::SigSpec AstNode::genWidthRTLIL(int width, RTLIL::SigSpec *subst_from, RTLIL::SigSpec *subst_to)
+{
+ RTLIL::SigSpec *backup_subst_from = genRTLIL_subst_from;
+ RTLIL::SigSpec *backup_subst_to = genRTLIL_subst_to;
+
+ if (subst_from)
+ genRTLIL_subst_from = subst_from;
+ if (subst_to)
+ genRTLIL_subst_to = subst_to;
+
+ RTLIL::SigSpec sig = genRTLIL(width);
+
+ genRTLIL_subst_from = backup_subst_from;
+ genRTLIL_subst_to = backup_subst_to;
+
+ if (width >= 0)
+ sig.extend(width, is_signed);
+
+ return sig;
+}
+
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
new file mode 100644
index 00000000..cb8b1043
--- /dev/null
+++ b/frontends/ast/simplify.cc
@@ -0,0 +1,1081 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#include "kernel/log.h"
+#include "kernel/sha1.h"
+#include "ast.h"
+
+#include <sstream>
+#include <stdarg.h>
+#include <assert.h>
+
+using namespace AST;
+using namespace AST_INTERNAL;
+
+// convert the AST into a simpler AST that has all parameters subsitited by their
+// values, unrolled for-loops, expanded generate blocks, etc. when this function
+// is done with an AST it can be converted into RTLIL using genRTLIL().
+//
+// this function also does all name resolving and sets the id2ast member of all
+// nodes that link to a different node using names and lexical scoping.
+bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage)
+{
+ AstNode *newNode = NULL;
+ bool did_something = false;
+
+ if (stage == 0)
+ {
+ assert(type == AST_MODULE);
+
+ while (simplify(const_fold, at_zero, in_lvalue, 1)) { }
+
+ if (!flag_nomem2reg && attributes.count("\\nomem2reg") == 0)
+ {
+ std::set<AstNode*> mem2reg_set, mem2reg_candidates;
+ mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, false, false);
+
+ for (auto node : mem2reg_set)
+ {
+ int mem_width, mem_size, addr_bits;
+ node->meminfo(mem_width, mem_size, addr_bits);
+
+ for (int i = 0; i < mem_size; i++) {
+ AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE,
+ mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ reg->str = stringf("%s[%d]", node->str.c_str(), i);
+ reg->is_reg = true;
+ reg->is_signed = node->is_signed;
+ children.push_back(reg);
+ }
+ }
+
+ mem2reg_as_needed_pass2(mem2reg_set, this, NULL);
+
+ for (size_t i = 0; i < children.size(); i++) {
+ if (mem2reg_set.count(children[i]) > 0) {
+ delete children[i];
+ children.erase(children.begin() + (i--));
+ }
+ }
+ }
+
+ while (simplify(const_fold, at_zero, in_lvalue, 2)) { }
+ return false;
+ }
+
+ current_filename = filename;
+ set_line_num(linenum);
+
+ // we do not look inside a task or function
+ // (but as soon as a task of function is instanciated we process the generated AST as usual)
+ if (type == AST_FUNCTION || type == AST_TASK)
+ return false;
+
+ // deactivate all calls non-synthesis system taks
+ if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$stop" || str == "$finish")) {
+ delete_children();
+ str = std::string();
+ }
+
+ // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
+ if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_PARASET || type == AST_RANGE)
+ const_fold = true;
+ if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
+ const_fold = true;
+
+ std::map<std::string, AstNode*> backup_scope;
+
+ // create name resolution entries for all objects with names
+ // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")
+ if (type == AST_MODULE) {
+ current_scope.clear();
+ std::map<std::string, AstNode*> this_wire_scope;
+ for (size_t i = 0; i < children.size(); i++) {
+ AstNode *node = children[i];
+ if (node->type == AST_WIRE) {
+ if (this_wire_scope.count(node->str) > 0) {
+ AstNode *first_node = this_wire_scope[node->str];
+ if (first_node->children.size() != node->children.size())
+ goto wires_are_incompatible;
+ for (size_t j = 0; j < node->children.size(); j++) {
+ AstNode *n1 = first_node->children[j], *n2 = node->children[j];
+ if (n1->type == AST_RANGE && n2->type == AST_RANGE && n1->range_valid && n2->range_valid) {
+ if (n1->range_left != n2->range_left)
+ goto wires_are_incompatible;
+ if (n1->range_right != n2->range_right)
+ goto wires_are_incompatible;
+ } else if (*n1 != *n2)
+ goto wires_are_incompatible;
+ }
+ if (first_node->range_left != node->range_left)
+ goto wires_are_incompatible;
+ if (first_node->range_right != node->range_right)
+ goto wires_are_incompatible;
+ if (first_node->port_id == 0 && (node->is_input || node->is_output))
+ goto wires_are_incompatible;
+ if (node->is_input)
+ first_node->is_input = true;
+ if (node->is_output)
+ first_node->is_output = true;
+ if (node->is_reg)
+ first_node->is_reg = true;
+ if (node->is_signed)
+ first_node->is_signed = true;
+ for (auto &it : node->attributes) {
+ if (first_node->attributes.count(it.first) > 0)
+ delete first_node->attributes[it.first];
+ first_node->attributes[it.first] = it.second->clone();
+ }
+ children.erase(children.begin()+(i--));
+ did_something = true;
+ delete node;
+ continue;
+ }
+ this_wire_scope[node->str] = node;
+ }
+ wires_are_incompatible:
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
+ node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) {
+ backup_scope[node->str] = current_scope[node->str];
+ current_scope[node->str] = node;
+ }
+ }
+ }
+
+ auto backup_current_block = current_block;
+ auto backup_current_block_child = current_block_child;
+ auto backup_current_top_block = current_top_block;
+
+ // simplify all children first
+ // (iterate by index as e.g. auto wires can add new children in the process)
+ for (size_t i = 0; i < children.size(); i++) {
+ bool did_something_here = true;
+ if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
+ break;
+ if (type == AST_GENIF && i >= 1)
+ break;
+ while (did_something_here && i < children.size()) {
+ bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
+ if (i == 0 && type == AST_REPLICATE)
+ const_fold_here = true;
+ if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE))
+ in_lvalue_here = true;
+ if (type == AST_BLOCK) {
+ current_block = this;
+ current_block_child = children[i];
+ }
+ if (type == AST_ALWAYS && children[i]->type == AST_BLOCK)
+ current_top_block = children[i];
+ did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage);
+ if (did_something_here)
+ did_something = true;
+ }
+ }
+ for (auto &attr : attributes) {
+ while (attr.second->simplify(true, false, false, stage)) { }
+ }
+
+ current_block = backup_current_block;
+ current_block_child = backup_current_block_child;
+ current_top_block = backup_current_top_block;
+
+ for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) {
+ if (it->second == NULL)
+ current_scope.erase(it->first);
+ else
+ current_scope[it->first] = it->second;
+ }
+
+ current_filename = filename;
+ set_line_num(linenum);
+
+ if (type == AST_MODULE)
+ current_scope.clear();
+
+ // annotate constant ranges
+ if (type == AST_RANGE) {
+ bool old_range_valid = range_valid;
+ range_valid = false;
+ range_left = -1;
+ range_right = 0;
+ assert(children.size() >= 1);
+ if (children[0]->type == AST_CONSTANT) {
+ range_valid = true;
+ range_left = children[0]->integer;
+ if (children.size() == 1)
+ range_right = range_left;
+ }
+ if (children.size() >= 2) {
+ if (children[1]->type == AST_CONSTANT)
+ range_right = children[1]->integer;
+ else
+ range_valid = false;
+ }
+ if (old_range_valid != range_valid)
+ did_something = true;
+ if (range_valid && range_left >= 0 && range_right > range_left) {
+ int tmp = range_right;
+ range_right = range_left;
+ range_left = tmp;
+ }
+ }
+
+ // annotate wires with their ranges
+ if (type == AST_WIRE) {
+ if (children.size() > 0) {
+ if (children[0]->range_valid) {
+ if (!range_valid)
+ did_something = true;
+ range_valid = true;
+ range_left = children[0]->range_left;
+ range_right = children[0]->range_right;
+ }
+ } else {
+ if (!range_valid)
+ did_something = true;
+ range_valid = true;
+ range_left = 0;
+ range_right = 0;
+ }
+ }
+
+ // annotate identifiers using scope resolution and create auto-wires as needed
+ if (type == AST_IDENTIFIER) {
+ if (current_scope.count(str) == 0) {
+ for (auto node : current_ast_mod->children) {
+ if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
+ node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) && str == node->str) {
+ current_scope[node->str] = node;
+ break;
+ }
+ }
+ }
+ if (current_scope.count(str) == 0) {
+ log("Warning: Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str());
+ AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
+ auto_wire->str = str;
+ current_ast_mod->children.push_back(auto_wire);
+ current_scope[str] = auto_wire;
+ did_something = true;
+ }
+ id2ast = current_scope[str];
+ }
+
+ // unroll for loops and generate-for blocks
+ if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
+ {
+ AstNode *init_ast = children[0];
+ AstNode *while_ast = children[1];
+ AstNode *next_ast = children[2];
+ AstNode *body_ast = children[3];
+
+ if (init_ast->type != AST_ASSIGN_EQ)
+ log_error("Unsupported 1st expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+ if (next_ast->type != AST_ASSIGN_EQ)
+ log_error("Unsupported 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+
+ if (type == AST_GENFOR) {
+ if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR)
+ log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum);
+ if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR)
+ log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum);
+ } else {
+ if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE)
+ log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum);
+ if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE)
+ log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum);
+ }
+
+ if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast)
+ log_error("Incompatible left-hand sides in 1st and 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+
+ // eval 1st expression
+ AstNode *varbuf = init_ast->children[1]->clone();
+ while (varbuf->simplify(true, false, false, stage)) { }
+
+ if (varbuf->type != AST_CONSTANT)
+ log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+
+ varbuf = new AstNode(AST_LOCALPARAM, varbuf);
+ varbuf->str = init_ast->children[0]->str;
+
+ AstNode *backup_scope_varbuf = current_scope[varbuf->str];
+ current_scope[varbuf->str] = varbuf;
+
+ size_t current_block_idx = 0;
+ if (type == AST_FOR) {
+ while (current_block_idx < current_block->children.size() &&
+ current_block->children[current_block_idx] != current_block_child)
+ current_block_idx++;
+ }
+
+ while (1)
+ {
+ // eval 2nd expression
+ AstNode *buf = while_ast->clone();
+ while (buf->simplify(true, false, false, stage)) { }
+
+ if (buf->type != AST_CONSTANT)
+ log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+
+ if (buf->integer == 0) {
+ delete buf;
+ break;
+ }
+ delete buf;
+
+ // expand body
+ int index = varbuf->children[0]->integer;
+ if (body_ast->type == AST_GENBLOCK)
+ buf = body_ast->clone();
+ else
+ buf = new AstNode(AST_GENBLOCK, body_ast->clone());
+ if (buf->str.empty()) {
+ std::stringstream sstr;
+ sstr << "$genblock$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ buf->str = sstr.str();
+ }
+ std::map<std::string, std::string> name_map;
+ std::stringstream sstr;
+ sstr << buf->str << "[" << index << "].";
+ buf->expand_genblock(varbuf->str, sstr.str(), name_map);
+
+ if (type == AST_GENFOR) {
+ for (size_t i = 0; i < buf->children.size(); i++)
+ current_ast_mod->children.push_back(buf->children[i]);
+ } else {
+ for (size_t i = 0; i < buf->children.size(); i++)
+ current_block->children.insert(current_block->children.begin() + current_block_idx++, buf->children[i]);
+ }
+ buf->children.clear();
+ delete buf;
+
+ // eval 3rd expression
+ buf = next_ast->children[1]->clone();
+ while (buf->simplify(true, false, false, stage)) { }
+
+ if (buf->type != AST_CONSTANT)
+ log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+
+ delete varbuf->children[0];
+ varbuf->children[0] = buf;
+ }
+
+ current_scope[varbuf->str] = backup_scope_varbuf;
+ delete varbuf;
+ delete_children();
+ did_something = true;
+ }
+
+ // simplify generate-if blocks
+ if (type == AST_GENIF && children.size() != 0)
+ {
+ AstNode *buf = children[0]->clone();
+ while (buf->simplify(true, false, false, stage)) { }
+ if (buf->type != AST_CONSTANT) {
+ for (auto f : log_files)
+ dumpAst(f, "verilog-ast> ");
+ log_error("Condition for generate if at %s:%d is not constant!\n", filename.c_str(), linenum);
+ }
+ if (buf->integer != 0) {
+ delete buf;
+ buf = children[1]->clone();
+ } else {
+ delete buf;
+ buf = children.size() > 2 ? children[2]->clone() : NULL;
+ }
+
+ if (buf)
+ {
+ if (buf->type != AST_GENBLOCK)
+ buf = new AstNode(AST_GENBLOCK, buf);
+
+ if (!buf->str.empty()) {
+ std::map<std::string, std::string> name_map;
+ buf->expand_genblock(std::string(), buf->str, name_map);
+ }
+
+ for (size_t i = 0; i < buf->children.size(); i++)
+ current_ast_mod->children.push_back(buf->children[i]);
+
+ buf->children.clear();
+ delete buf;
+ }
+
+ delete_children();
+ did_something = true;
+ }
+
+ // replace primitives with assignmens
+ if (type == AST_PRIMITIVE)
+ {
+ if (children.size() < 2)
+ log_error("Insufficient number of arguments for primitive `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ std::vector<AstNode*> children_list;
+ for (auto child : children) {
+ assert(child->type == AST_ARGUMENT);
+ assert(child->children.size() == 1);
+ children_list.push_back(child->children[0]);
+ child->children.clear();
+ delete child;
+ }
+ children.clear();
+
+ AstNodeType op_type = AST_NONE;
+ bool invert_results = false;
+
+ if (str == "and")
+ op_type = AST_BIT_AND;
+ if (str == "nand")
+ op_type = AST_BIT_AND, invert_results = true;
+ if (str == "or")
+ op_type = AST_BIT_OR;
+ if (str == "nor")
+ op_type = AST_BIT_OR, invert_results = true;
+ if (str == "xor")
+ op_type = AST_BIT_XOR;
+ if (str == "xnor")
+ op_type = AST_BIT_XOR, invert_results = true;
+ if (str == "buf")
+ op_type = AST_POS;
+ if (str == "not")
+ op_type = AST_POS, invert_results = true;
+ assert(op_type != AST_NONE);
+
+ AstNode *node = children_list[1];
+ if (op_type != AST_POS)
+ for (size_t i = 2; i < children_list.size(); i++)
+ node = new AstNode(op_type, node, children_list[i]);
+ if (invert_results)
+ node = new AstNode(AST_BIT_NOT, node);
+
+ str.clear();
+ type = AST_ASSIGN;
+ children.push_back(children_list[0]);
+ children.push_back(node);
+ did_something = true;
+ }
+
+ // replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with
+ // a big case block that selects the correct single-bit assignment.
+ if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) {
+ if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0)
+ goto skip_dynamic_range_lvalue_expansion;
+ if (children[0]->children[0]->range_valid || did_something)
+ goto skip_dynamic_range_lvalue_expansion;
+ if (children[0]->id2ast == NULL || children[0]->id2ast->type != AST_WIRE)
+ goto skip_dynamic_range_lvalue_expansion;
+ if (!children[0]->id2ast->range_valid)
+ goto skip_dynamic_range_lvalue_expansion;
+ int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1;
+ int result_width = 1;
+ AstNode *shift_expr = NULL;
+ AstNode *range = children[0]->children[0];
+ if (range->children.size() == 1) {
+ shift_expr = range->children[0]->clone();
+ } else {
+ shift_expr = range->children[1]->clone();
+ AstNode *left_at_zero_ast = range->children[0]->clone();
+ AstNode *right_at_zero_ast = range->children[1]->clone();
+ while (left_at_zero_ast->simplify(true, true, false, stage)) { }
+ while (right_at_zero_ast->simplify(true, true, false, stage)) { }
+ if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+ log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ result_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ }
+ did_something = true;
+ newNode = new AstNode(AST_CASE, shift_expr);
+ for (int i = 0; i <= source_width-result_width; i++) {
+ int start_bit = children[0]->id2ast->range_right + i;
+ AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
+ AstNode *lvalue = children[0]->clone();
+ lvalue->delete_children();
+ lvalue->children.push_back(new AstNode(AST_RANGE,
+ mkconst_int(start_bit+result_width-1, true), mkconst_int(start_bit, true)));
+ cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone())));
+ newNode->children.push_back(cond);
+ }
+ goto apply_newNode;
+ }
+skip_dynamic_range_lvalue_expansion:;
+
+ // found right-hand side identifier for memory -> replace with memory read port
+ if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue &&
+ children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
+ newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone());
+ newNode->str = str;
+ goto apply_newNode;
+ }
+
+ // assignment with memory in left-hand side expression -> replace with memory write port
+ if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
+ children[0]->children.size() == 1 && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY &&
+ children[0]->id2ast->children.size() >= 2 && children[0]->id2ast->children[0]->range_valid &&
+ children[0]->id2ast->children[1]->range_valid)
+ {
+ std::stringstream sstr;
+ sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
+
+ if (type == AST_ASSIGN_EQ)
+ log("Warining: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
+ filename.c_str(), linenum);
+
+ int mem_width, mem_size, addr_bits;
+ children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
+ wire_addr->str = id_addr;
+ current_ast_mod->children.push_back(wire_addr);
+ current_scope[wire_addr->str] = wire_addr;
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_data->str = id_data;
+ current_ast_mod->children.push_back(wire_data);
+ current_scope[wire_data->str] = wire_data;
+
+ AstNode *wire_en = new AstNode(AST_WIRE);
+ wire_en->str = id_en;
+ current_ast_mod->children.push_back(wire_en);
+ current_scope[wire_en->str] = wire_en;
+
+ std::vector<RTLIL::State> x_bits;
+ x_bits.push_back(RTLIL::State::Sx);
+
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false));
+ assign_addr->children[0]->str = id_addr;
+
+ AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false));
+ assign_data->children[0]->str = id_data;
+
+ AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1));
+ assign_en->children[0]->str = id_en;
+
+ AstNode *default_signals = new AstNode(AST_BLOCK);
+ default_signals->children.push_back(assign_addr);
+ default_signals->children.push_back(assign_data);
+ default_signals->children.push_back(assign_en);
+ current_top_block->children.insert(current_top_block->children.begin(), default_signals);
+
+ assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
+ assign_addr->children[0]->str = id_addr;
+
+ assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
+ assign_data->children[0]->str = id_data;
+
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
+ assign_en->children[0]->str = id_en;
+
+ newNode = new AstNode(AST_BLOCK);
+ newNode->children.push_back(assign_addr);
+ newNode->children.push_back(assign_data);
+ newNode->children.push_back(assign_en);
+
+ AstNode *wrnode = new AstNode(AST_MEMWR);
+ wrnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ wrnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ wrnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ wrnode->str = children[0]->str;
+ wrnode->children[0]->str = id_addr;
+ wrnode->children[1]->str = id_data;
+ wrnode->children[2]->str = id_en;
+ current_ast_mod->children.push_back(wrnode);
+
+ goto apply_newNode;
+ }
+
+ // replace function and task calls with the code from the function or task
+ if ((type == AST_FCALL || type == AST_TCALL) && !str.empty())
+ {
+ if (type == AST_FCALL) {
+ if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)
+ log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+ if (type == AST_TCALL) {
+ if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK)
+ log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+
+ AstNode *decl = current_scope[str];
+ std::stringstream sstr;
+ sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++) << "$";
+ std::string prefix = sstr.str();
+
+ size_t arg_count = 0;
+ std::map<std::string, std::string> replace_rules;
+
+ if (current_block == NULL)
+ {
+ assert(type == AST_FCALL);
+
+ AstNode *wire = NULL;
+ for (auto child : decl->children)
+ if (child->type == AST_WIRE && child->str == str)
+ wire = child->clone();
+ assert(wire != NULL);
+
+ wire->str = prefix + str;
+ wire->port_id = 0;
+ wire->is_input = false;
+ wire->is_output = false;
+
+ current_ast_mod->children.push_back(wire);
+
+ AstNode *lvalue = new AstNode(AST_IDENTIFIER);
+ lvalue->str = wire->str;
+
+ AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK,
+ new AstNode(AST_ASSIGN_EQ, lvalue, clone())));
+ current_ast_mod->children.push_back(always);
+
+ goto replace_fcall_with_id;
+ }
+
+ for (auto child : decl->children)
+ {
+ if (child->type == AST_WIRE)
+ {
+ AstNode *wire = child->clone();
+ wire->str = prefix + wire->str;
+ wire->port_id = 0;
+ wire->is_input = false;
+ wire->is_output = false;
+ current_ast_mod->children.push_back(wire);
+
+ replace_rules[child->str] = wire->str;
+
+ if (child->is_input && arg_count < children.size())
+ {
+ AstNode *arg = children[arg_count++]->clone();
+ AstNode *wire_id = new AstNode(AST_IDENTIFIER);
+ wire_id->str = wire->str;
+ AstNode *assign = new AstNode(AST_ASSIGN_EQ, wire_id, arg);
+
+ for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
+ if (*it != current_block_child)
+ continue;
+ current_block->children.insert(it, assign);
+ break;
+ }
+ }
+ }
+ else
+ {
+ AstNode *stmt = child->clone();
+ stmt->replace_ids(replace_rules);
+
+ for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
+ if (*it != current_block_child)
+ continue;
+ current_block->children.insert(it, stmt);
+ break;
+ }
+ }
+ }
+
+ replace_fcall_with_id:
+ if (type == AST_FCALL) {
+ delete_children();
+ type = AST_IDENTIFIER;
+ str = prefix + str;
+ }
+ if (type == AST_TCALL)
+ str = "";
+ did_something = true;
+ }
+
+ // perform const folding when activated
+ if (const_fold && newNode == NULL)
+ {
+ RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, int);
+ RTLIL::Const dummy_arg;
+
+ switch (type)
+ {
+ case AST_IDENTIFIER:
+ if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) {
+ if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
+ if (current_scope[str]->children[0]->type == AST_CONSTANT) {
+ std::vector<RTLIL::State> data;
+ for (int i = children[0]->range_right; i <= children[0]->range_left; i++)
+ data.push_back(current_scope[str]->children[0]->bits[i]);
+ newNode = mkconst_bits(data, false);
+ }
+ } else
+ if (children.size() == 0)
+ newNode = current_scope[str]->children[0]->clone();
+ }
+ else if (at_zero && current_module->wires.count(str) > 0) {
+ assert(current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE));
+ if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid)
+ newNode = mkconst_int(0, false, children[0]->range_left - children[0]->range_right + 1);
+ else
+ if (children.size() == 0)
+ newNode = mkconst_int(0, current_scope[str]->is_signed, current_module->wires[str]->width);
+ }
+ break;
+ case AST_BIT_NOT:
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = RTLIL::const_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; }
+ if (0) { case AST_BIT_OR: const_func = RTLIL::const_or; }
+ if (0) { case AST_BIT_XOR: const_func = RTLIL::const_xor; }
+ if (0) { case AST_BIT_XNOR: const_func = RTLIL::const_xnor; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
+ children[0]->is_signed, children[1]->is_signed, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; }
+ if (0) { case AST_REDUCE_OR: const_func = RTLIL::const_reduce_or; }
+ if (0) { case AST_REDUCE_XOR: const_func = RTLIL::const_reduce_xor; }
+ if (0) { case AST_REDUCE_XNOR: const_func = RTLIL::const_reduce_xnor; }
+ if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ case AST_LOGIC_NOT:
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; }
+ if (0) { case AST_LOGIC_OR: const_func = RTLIL::const_logic_or; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
+ children[0]->is_signed, children[1]->is_signed, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; }
+ if (0) { case AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; }
+ if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; }
+ if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), children[0]->is_signed, false, -1);
+ newNode = mkconst_bits(y.bits, children[0]->is_signed);
+ }
+ break;
+ if (0) { case AST_LT: const_func = RTLIL::const_lt; }
+ if (0) { case AST_LE: const_func = RTLIL::const_le; }
+ if (0) { case AST_EQ: const_func = RTLIL::const_eq; }
+ if (0) { case AST_NE: const_func = RTLIL::const_ne; }
+ if (0) { case AST_GE: const_func = RTLIL::const_ge; }
+ if (0) { case AST_GT: const_func = RTLIL::const_gt; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
+ children[0]->is_signed, children[1]->is_signed, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ if (0) { case AST_ADD: const_func = RTLIL::const_add; }
+ if (0) { case AST_SUB: const_func = RTLIL::const_sub; }
+ if (0) { case AST_MUL: const_func = RTLIL::const_mul; }
+ if (0) { case AST_DIV: const_func = RTLIL::const_div; }
+ if (0) { case AST_MOD: const_func = RTLIL::const_mod; }
+ if (0) { case AST_POW: const_func = RTLIL::const_pow; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
+ children[0]->is_signed, children[1]->is_signed, -1);
+ newNode = mkconst_bits(y.bits, children[0]->is_signed && children[1]->is_signed);
+ }
+ break;
+ if (0) { case AST_POS: const_func = RTLIL::const_pos; }
+ if (0) { case AST_NEG: const_func = RTLIL::const_neg; }
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
+ newNode = mkconst_bits(y.bits, children[0]->is_signed);
+ }
+ break;
+ case AST_TERNARY:
+ if (children[0]->type == AST_CONSTANT) {
+ if (children[0]->integer)
+ newNode = children[1]->clone();
+ else
+ newNode = children[2]->clone();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // if any of the above set 'newNode' -> use 'newNode' as template to update 'this'
+ if (newNode) {
+apply_newNode:
+ // fprintf(stderr, "----\n");
+ // dumpAst(stderr, "- ");
+ // newNode->dumpAst(stderr, "+ ");
+ assert(newNode != NULL);
+ newNode->filename = filename;
+ newNode->linenum = linenum;
+ newNode->cloneInto(this);
+ delete newNode;
+ did_something = true;
+ }
+
+ return did_something;
+}
+
+// annotate the names of all wires and other named objects in a generate block
+void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map)
+{
+ if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) {
+ current_scope[index_var]->children[0]->cloneInto(this);
+ return;
+ }
+
+ if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0) {
+ str = name_map[str];
+ return;
+ }
+
+ std::map<std::string, std::string> backup_name_map;
+
+ for (size_t i = 0; i < children.size(); i++) {
+ AstNode *child = children[i];
+ if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
+ child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) {
+ if (backup_name_map.size() == 0)
+ backup_name_map = name_map;
+ std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
+ size_t pos = child->str.rfind('.');
+ if (pos == std::string::npos)
+ pos = child->str[0] == '\\' ? 1 : 0;
+ else
+ pos = pos + 1;
+ new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos);
+ if (new_name[0] != '$' && new_name[0] != '\\')
+ new_name = prefix[0] + new_name;
+ name_map[child->str] = new_name;
+ child->str = new_name;
+ }
+ }
+
+ for (size_t i = 0; i < children.size(); i++) {
+ AstNode *child = children[i];
+ if (child->type != AST_FUNCTION && child->type != AST_TASK)
+ child->expand_genblock(index_var, prefix, name_map);
+ }
+
+ if (backup_name_map.size() > 0)
+ name_map.swap(backup_name_map);
+}
+
+// rename stuff (used when tasks of functions are instanciated)
+void AstNode::replace_ids(std::map<std::string, std::string> &rules)
+{
+ if (type == AST_IDENTIFIER && rules.count(str) > 0)
+ str = rules[str];
+ for (auto child : children)
+ child->replace_ids(rules);
+}
+
+// find memories that should be replaced by registers
+void AstNode::mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc)
+{
+ if ((type == AST_ASSIGN_LE && async_proc) || (type == AST_ASSIGN_EQ && (sync_proc || async_proc)))
+ if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY &&
+ children[0]->id2ast->attributes.count("\\nomem2reg") == 0) {
+ if (async_proc || mem2reg_candidates.count(children[0]->id2ast) > 0) {
+ if (mem2reg_set.count(children[0]->id2ast) == 0)
+ log("Warning: Replacing memory %s with list of registers because of assignment in line %s:%d.\n",
+ children[0]->str.c_str(), filename.c_str(), linenum);
+ mem2reg_set.insert(children[0]->id2ast);
+ }
+ mem2reg_candidates.insert(children[0]->id2ast);
+ }
+
+ if (type == AST_ALWAYS) {
+ for (auto child : children) {
+ if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
+ sync_proc = true;
+ }
+ async_proc = !sync_proc;
+ }
+
+ for (auto child : children)
+ child->mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, sync_proc, async_proc);
+}
+
+// actually replace memories with registers
+void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block)
+{
+ if (type == AST_BLOCK)
+ block = this;
+
+ if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL &&
+ children[0]->id2ast && mem2reg_set.count(children[0]->id2ast) > 0)
+ {
+ std::stringstream sstr;
+ sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
+
+ int mem_width, mem_size, addr_bits;
+ children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
+ wire_addr->str = id_addr;
+ wire_addr->is_reg = true;
+ mod->children.push_back(wire_addr);
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_data->str = id_data;
+ wire_data->is_reg = true;
+ mod->children.push_back(wire_data);
+
+ assert(block != NULL);
+ size_t assign_idx = 0;
+ while (assign_idx < block->children.size() && block->children[assign_idx] != this)
+ assign_idx++;
+ assert(assign_idx < block->children.size());
+
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
+ assign_addr->children[0]->str = id_addr;
+ block->children.insert(block->children.begin()+assign_idx+1, assign_addr);
+
+ AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
+ case_node->children[0]->str = id_addr;
+ for (int i = 0; i < mem_size; i++) {
+ if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i)
+ continue;
+ AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
+ AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
+ assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i);
+ assign_reg->children[1]->str = id_data;
+ cond_node->children[1]->children.push_back(assign_reg);
+ case_node->children.push_back(cond_node);
+ }
+ block->children.insert(block->children.begin()+assign_idx+2, case_node);
+
+ children[0]->delete_children();
+ children[0]->range_valid = false;
+ children[0]->id2ast = NULL;
+ children[0]->str = id_data;
+ }
+
+ if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0)
+ {
+ std::stringstream sstr;
+ sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
+
+ int mem_width, mem_size, addr_bits;
+ id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
+ wire_addr->str = id_addr;
+ mod->children.push_back(wire_addr);
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_data->str = id_data;
+ mod->children.push_back(wire_data);
+
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
+ assign_addr->children[0]->str = id_addr;
+
+ AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
+ case_node->children[0]->str = id_addr;
+
+ for (int i = 0; i < mem_size; i++) {
+ if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i)
+ continue;
+ AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
+ AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
+ assign_reg->children[0]->str = id_data;
+ assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
+ cond_node->children[1]->children.push_back(assign_reg);
+ case_node->children.push_back(cond_node);
+ }
+
+ std::vector<RTLIL::State> x_bits;
+ x_bits.push_back(RTLIL::State::Sx);
+ AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK));
+ AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
+ assign_reg->children[0]->str = id_data;
+ cond_node->children[1]->children.push_back(assign_reg);
+ case_node->children.push_back(cond_node);
+
+ if (block)
+ {
+ size_t assign_idx = 0;
+ while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
+ assign_idx++;
+ assert(assign_idx < block->children.size());
+ block->children.insert(block->children.begin()+assign_idx, case_node);
+ block->children.insert(block->children.begin()+assign_idx, assign_addr);
+ wire_addr->is_reg = true;
+ wire_data->is_reg = true;
+ }
+ else
+ {
+ AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
+ proc->children[0]->children.push_back(case_node);
+ mod->children.push_back(proc);
+ mod->children.push_back(assign_addr);
+ }
+
+ delete_children();
+ range_valid = false;
+ id2ast = NULL;
+ str = id_data;
+ }
+
+ assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
+
+ for (size_t i = 0; i < children.size(); i++)
+ children[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block);
+}
+
+// calulate memory dimensions
+void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)
+{
+ assert(type == AST_MEMORY);
+
+ mem_width = children[0]->range_left - children[0]->range_right + 1;
+ mem_size = children[1]->range_left - children[1]->range_right;
+
+ if (mem_size < 0)
+ mem_size *= -1;
+ mem_size += std::min(children[1]->range_left, children[1]->range_right) + 1;
+
+ addr_bits = 1;
+ while ((1 << addr_bits) < mem_size)
+ addr_bits++;
+}
+
diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc
new file mode 100644
index 00000000..07ebf085
--- /dev/null
+++ b/frontends/ilang/Makefile.inc
@@ -0,0 +1,16 @@
+
+GENFILES += frontends/ilang/parser.tab.cc
+GENFILES += frontends/ilang/parser.tab.h
+GENFILES += frontends/ilang/parser.output
+GENFILES += frontends/ilang/lexer.cc
+
+frontends/ilang/parser.tab.cc frontends/ilang/parser.tab.h: frontends/ilang/parser.y
+ bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y
+ mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc
+
+frontends/ilang/lexer.cc: frontends/ilang/lexer.l
+ flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l
+
+OBJS += frontends/ilang/parser.tab.o frontends/ilang/lexer.o
+OBJS += frontends/ilang/ilang_frontend.o
+
diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc
new file mode 100644
index 00000000..f3ad3a19
--- /dev/null
+++ b/frontends/ilang/ilang_frontend.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+#include "ilang_frontend.h"
+#include "kernel/register.h"
+#include "kernel/log.h"
+
+void rtlil_frontend_ilang_yyerror(char const *s)
+{
+ log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s);
+}
+
+struct IlangFrontend : public Frontend {
+ IlangFrontend() : Frontend("ilang") { }
+ virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing ILANG frontend.\n");
+ extra_args(f, filename, args, 1);
+ log("Input filename: %s\n", filename.c_str());
+
+ ILANG_FRONTEND::current_design = design;
+ rtlil_frontend_ilang_yydebug = false;
+ rtlil_frontend_ilang_yyrestart(f);
+ rtlil_frontend_ilang_yyparse();
+ rtlil_frontend_ilang_yylex_destroy();
+ }
+} IlangFrontend;
+
diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h
new file mode 100644
index 00000000..5e768c3b
--- /dev/null
+++ b/frontends/ilang/ilang_frontend.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+#ifndef ILANG_FRONTEND_H
+#define ILANG_FRONTEND_H
+
+#include "kernel/rtlil.h"
+#include <stdio.h>
+
+namespace ILANG_FRONTEND {
+ void ilang_frontend(FILE *f, RTLIL::Design *design);
+ extern RTLIL::Design *current_design;
+}
+
+extern int rtlil_frontend_ilang_yydebug;
+int rtlil_frontend_ilang_yylex(void);
+void rtlil_frontend_ilang_yyerror(char const *s);
+void rtlil_frontend_ilang_yyrestart(FILE *f);
+int rtlil_frontend_ilang_yyparse(void);
+void rtlil_frontend_ilang_yylex_destroy(void);
+int rtlil_frontend_ilang_yyget_lineno(void);
+
+#endif
+
diff --git a/frontends/ilang/lexer.l b/frontends/ilang/lexer.l
new file mode 100644
index 00000000..e331c267
--- /dev/null
+++ b/frontends/ilang/lexer.l
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+%{
+#include "kernel/rtlil.h"
+#include "parser.tab.h"
+%}
+
+%option yylineno
+%option noyywrap
+%option nounput
+%option prefix="rtlil_frontend_ilang_yy"
+
+%x STRING
+
+%%
+
+"module" { return TOK_MODULE; }
+"attribute" { return TOK_ATTRIBUTE; }
+"parameter" { return TOK_PARAMETER; }
+"wire" { return TOK_WIRE; }
+"memory" { return TOK_MEMORY; }
+"auto" { return TOK_AUTO; }
+"width" { return TOK_WIDTH; }
+"offset" { return TOK_OFFSET; }
+"size" { return TOK_SIZE; }
+"input" { return TOK_INPUT; }
+"output" { return TOK_OUTPUT; }
+"inout" { return TOK_INOUT; }
+"cell" { return TOK_CELL; }
+"connect" { return TOK_CONNECT; }
+"switch" { return TOK_SWITCH; }
+"case" { return TOK_CASE; }
+"assign" { return TOK_ASSIGN; }
+"sync" { return TOK_SYNC; }
+"low" { return TOK_LOW; }
+"high" { return TOK_HIGH; }
+"posedge" { return TOK_POSEDGE; }
+"negedge" { return TOK_NEGEDGE; }
+"edge" { return TOK_EDGE; }
+"always" { return TOK_ALWAYS; }
+"update" { return TOK_UPDATE; }
+"process" { return TOK_PROCESS; }
+"end" { return TOK_END; }
+
+[a-z]+ { return TOK_INVALID; }
+
+"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
+"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
+"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
+
+[0-9]+'[01xzm-]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; }
+[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; }
+
+\" { BEGIN(STRING); }
+<STRING>\\. { yymore(); }
+<STRING>\" {
+ BEGIN(0);
+ char *yystr = strdup(yytext);
+ yystr[strlen(yytext) - 1] = 0;
+ int i = 0, j = 0;
+ while (yystr[i]) {
+ if (yystr[i] == '\\' && yystr[i + 1]) {
+ i++;
+ if (yystr[i] == 'n')
+ yystr[i] = '\n';
+ else if (yystr[i] == 't')
+ yystr[i] = '\t';
+ else if ('0' <= yystr[i] && yystr[i] <= '7') {
+ yystr[i] = yystr[i] - '0';
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ }
+ }
+ yystr[j++] = yystr[i++];
+ }
+ yystr[j] = 0;
+ rtlil_frontend_ilang_yylval.string = yystr;
+ return TOK_STRING;
+}
+<STRING>. { yymore(); }
+
+"#"[^\n]*\n /* ignore comments */
+[ \t] /* ignore non-newline whitespaces */
+[\r\n]+ { return TOK_EOL; }
+
+. { return *yytext; }
+
+%%
+
+// this is a hack to avoid the 'yyinput defined but not used' error msgs
+void *rtlil_frontend_ilang_avoid_input_warnings() {
+ return (void*)&yyinput;
+}
+
diff --git a/frontends/ilang/parser.y b/frontends/ilang/parser.y
new file mode 100644
index 00000000..61bac830
--- /dev/null
+++ b/frontends/ilang/parser.y
@@ -0,0 +1,416 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+%{
+#include <list>
+#include "ilang_frontend.h"
+namespace ILANG_FRONTEND {
+ RTLIL::Design *current_design;
+ RTLIL::Module *current_module;
+ RTLIL::Wire *current_wire;
+ RTLIL::Memory *current_memory;
+ RTLIL::Cell *current_cell;
+ RTLIL::Process *current_process;
+ std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
+ std::vector<RTLIL::CaseRule*> case_stack;
+ std::map<RTLIL::IdString, RTLIL::Const> attrbuf;
+}
+using namespace ILANG_FRONTEND;
+%}
+
+%name-prefix="rtlil_frontend_ilang_yy"
+
+%union {
+ char *string;
+ int integer;
+ RTLIL::Const *data;
+ RTLIL::SigSpec *sigspec;
+}
+
+%token <string> TOK_ID TOK_VALUE TOK_STRING
+%token <integer> TOK_INT
+%token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
+%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
+%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS
+%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
+%token TOK_PARAMETER TOK_ATTRIBUTE TOK_AUTO TOK_MEMORY TOK_SIZE
+
+%type <sigspec> sigspec sigspec_list
+%type <integer> sync_type
+%type <data> constant
+
+%expect 0
+%debug
+
+%%
+
+input:
+ optional_eol {
+ attrbuf.clear();
+ } design {
+ if (attrbuf.size() != 0)
+ rtlil_frontend_ilang_yyerror("dangling attribute");
+ };
+
+optional_eol:
+ optional_eol TOK_EOL | /* empty */;
+
+design:
+ design module |
+ design attr_stmt |
+ /* empty */;
+
+module:
+ TOK_MODULE TOK_ID TOK_EOL {
+ if (current_design->modules.count($2) != 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ current_module = new RTLIL::Module;
+ current_module->name = $2;
+ current_module->attributes = attrbuf;
+ current_design->modules[$2] = current_module;
+ attrbuf.clear();
+ free($2);
+ } module_body TOK_END {
+ if (attrbuf.size() != 0)
+ rtlil_frontend_ilang_yyerror("dangling attribute");
+ } TOK_EOL;
+
+module_body:
+ module_body module_stmt |
+ /* empty */;
+
+module_stmt:
+ attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
+
+attr_stmt:
+ TOK_ATTRIBUTE TOK_ID constant TOK_EOL {
+ attrbuf[$2] = *$3;
+ delete $3;
+ };
+
+wire_stmt:
+ TOK_WIRE {
+ current_wire = new RTLIL::Wire;
+ current_wire->attributes = attrbuf;
+ attrbuf.clear();
+ } wire_options TOK_ID TOK_EOL {
+ if (current_module->wires.count($4) != 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ current_wire->name = $4;
+ current_module->wires[$4] = current_wire;
+ free($4);
+ };
+
+wire_options:
+ wire_options TOK_AUTO {
+ current_wire->auto_width = true;
+ } |
+ wire_options TOK_WIDTH TOK_INT {
+ current_wire->width = $3;
+ } |
+ wire_options TOK_OFFSET TOK_INT {
+ current_wire->start_offset = $3;
+ } |
+ wire_options TOK_INPUT TOK_INT {
+ current_wire->port_id = $3;
+ current_wire->port_input = true;
+ current_wire->port_output = false;
+ } |
+ wire_options TOK_OUTPUT TOK_INT {
+ current_wire->port_id = $3;
+ current_wire->port_input = false;
+ current_wire->port_output = true;
+ } |
+ wire_options TOK_INOUT TOK_INT {
+ current_wire->port_id = $3;
+ current_wire->port_input = true;
+ current_wire->port_output = true;
+ } |
+ /* empty */;
+
+memory_stmt:
+ TOK_MEMORY {
+ current_memory = new RTLIL::Memory;
+ current_memory->attributes = attrbuf;
+ attrbuf.clear();
+ } memory_options TOK_ID TOK_EOL {
+ if (current_module->memories.count($4) != 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ current_memory->name = $4;
+ current_module->memories[$4] = current_memory;
+ free($4);
+ };
+
+memory_options:
+ memory_options TOK_WIDTH TOK_INT {
+ current_wire->width = $3;
+ } |
+ memory_options TOK_SIZE TOK_INT {
+ current_memory->size = $3;
+ } |
+ /* empty */;
+
+cell_stmt:
+ TOK_CELL TOK_ID TOK_ID TOK_EOL {
+ if (current_module->cells.count($3) != 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ current_cell = new RTLIL::Cell;
+ current_cell->type = $2;
+ current_cell->name = $3;
+ current_cell->attributes = attrbuf;
+ current_module->cells[$3] = current_cell;
+ attrbuf.clear();
+ free($2);
+ free($3);
+ } cell_body TOK_END TOK_EOL;
+
+cell_body:
+ cell_body TOK_PARAMETER TOK_ID constant TOK_EOL {
+ current_cell->parameters[$3] = *$4;
+ free($3);
+ delete $4;
+ } |
+ cell_body TOK_CONNECT TOK_ID sigspec TOK_EOL {
+ if (current_cell->connections.count($3) != 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ current_cell->connections[$3] = *$4;
+ delete $4;
+ free($3);
+ } |
+ /* empty */;
+
+proc_stmt:
+ TOK_PROCESS TOK_ID TOK_EOL {
+ if (current_module->processes.count($2) != 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ current_process = new RTLIL::Process;
+ current_process->name = $2;
+ current_process->attributes = attrbuf;
+ current_module->processes[$2] = current_process;
+ switch_stack.clear();
+ switch_stack.push_back(&current_process->root_case.switches);
+ case_stack.clear();
+ case_stack.push_back(&current_process->root_case);
+ free($2);
+ } case_body sync_list TOK_END TOK_EOL;
+
+switch_stmt:
+ attr_list TOK_SWITCH sigspec TOK_EOL {
+ RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
+ rule->signal = *$3;
+ rule->attributes = attrbuf;
+ switch_stack.back()->push_back(rule);
+ attrbuf.clear();
+ delete $3;
+ } switch_body TOK_END TOK_EOL;
+
+attr_list:
+ /* empty */ |
+ attr_list attr_stmt;
+
+switch_body:
+ switch_body TOK_CASE {
+ RTLIL::CaseRule *rule = new RTLIL::CaseRule;
+ switch_stack.back()->back()->cases.push_back(rule);
+ switch_stack.push_back(&rule->switches);
+ case_stack.push_back(rule);
+ } compare_list TOK_EOL case_body {
+ switch_stack.pop_back();
+ case_stack.pop_back();
+ } |
+ /* empty */;
+
+compare_list:
+ sigspec {
+ case_stack.back()->compare.push_back(*$1);
+ delete $1;
+ } |
+ compare_list ',' sigspec {
+ case_stack.back()->compare.push_back(*$3);
+ delete $3;
+ } |
+ /* empty */;
+
+case_body:
+ switch_stmt case_body |
+ assign_stmt case_body |
+ /* empty */;
+
+assign_stmt:
+ TOK_ASSIGN sigspec sigspec TOK_EOL {
+ case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
+ delete $2;
+ delete $3;
+ };
+
+sync_list:
+ sync_list TOK_SYNC sync_type sigspec TOK_EOL {
+ RTLIL::SyncRule *rule = new RTLIL::SyncRule;
+ rule->type = RTLIL::SyncType($3);
+ rule->signal = *$4;
+ current_process->syncs.push_back(rule);
+ delete $4;
+ } update_list |
+ sync_list TOK_SYNC TOK_ALWAYS TOK_EOL {
+ RTLIL::SyncRule *rule = new RTLIL::SyncRule;
+ rule->type = RTLIL::SyncType::STa;
+ rule->signal = RTLIL::SigSpec();
+ current_process->syncs.push_back(rule);
+ } update_list |
+ /* empty */;
+
+sync_type:
+ TOK_LOW { $$ = RTLIL::ST0; } |
+ TOK_HIGH { $$ = RTLIL::ST1; } |
+ TOK_POSEDGE { $$ = RTLIL::STp; } |
+ TOK_NEGEDGE { $$ = RTLIL::STn; } |
+ TOK_EDGE { $$ = RTLIL::STe; };
+
+update_list:
+ update_list TOK_UPDATE sigspec sigspec TOK_EOL {
+ current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4));
+ delete $3;
+ delete $4;
+ } |
+ /* empty */;
+
+constant:
+ TOK_VALUE {
+ char *ep;
+ int width = strtol($1, &ep, 10);
+ std::list<RTLIL::State> bits;
+ while (*(++ep) != 0) {
+ RTLIL::State bit = RTLIL::Sx;
+ switch (*ep) {
+ case '0': bit = RTLIL::S0; break;
+ case '1': bit = RTLIL::S1; break;
+ case 'x': bit = RTLIL::Sx; break;
+ case 'z': bit = RTLIL::Sz; break;
+ case '-': bit = RTLIL::Sa; break;
+ case 'm': bit = RTLIL::Sm; break;
+ }
+ bits.push_front(bit);
+ }
+ if (bits.size() == 0)
+ bits.push_back(RTLIL::Sx);
+ while ((int)bits.size() < width) {
+ RTLIL::State bit = bits.back();
+ if (bit == RTLIL::S1)
+ bit = RTLIL::S0;
+ bits.push_back(bit);
+ }
+ while ((int)bits.size() > width)
+ bits.pop_back();
+ $$ = new RTLIL::Const;
+ for (auto it = bits.begin(); it != bits.end(); it++)
+ $$->bits.push_back(*it);
+ free($1);
+ } |
+ TOK_INT {
+ $$ = new RTLIL::Const($1, 32);
+ } |
+ TOK_STRING {
+ $$ = new RTLIL::Const($1);
+ free($1);
+ };
+
+sigspec:
+ constant {
+ RTLIL::SigChunk chunk;
+ chunk.wire = NULL;
+ chunk.width = $1->bits.size();
+ chunk.offset = 0;
+ chunk.data = *$1;
+ $$ = new RTLIL::SigSpec;
+ $$->chunks.push_back(chunk);
+ $$->width = chunk.width;
+ delete $1;
+ } |
+ TOK_ID {
+ if (current_module->wires.count($1) == 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ RTLIL::SigChunk chunk;
+ chunk.wire = current_module->wires[$1];
+ chunk.width = current_module->wires[$1]->width;
+ chunk.offset = 0;
+ $$ = new RTLIL::SigSpec;
+ $$->chunks.push_back(chunk);
+ $$->width = chunk.width;
+ free($1);
+ } |
+ TOK_ID '[' TOK_INT ']' {
+ if (current_module->wires.count($1) == 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ RTLIL::SigChunk chunk;
+ chunk.wire = current_module->wires[$1];
+ chunk.offset = $3;
+ chunk.width = 1;
+ $$ = new RTLIL::SigSpec;
+ $$->chunks.push_back(chunk);
+ $$->width = 1;
+ free($1);
+ } |
+ TOK_ID '[' TOK_INT ':' TOK_INT ']' {
+ if (current_module->wires.count($1) == 0)
+ rtlil_frontend_ilang_yyerror("scope error");
+ RTLIL::SigChunk chunk;
+ chunk.wire = current_module->wires[$1];
+ chunk.width = $3 - $5 + 1;
+ chunk.offset = $5;
+ $$ = new RTLIL::SigSpec;
+ $$->chunks.push_back(chunk);
+ $$->width = chunk.width;
+ free($1);
+ } |
+ '{' sigspec_list '}' {
+ $$ = $2;
+ };
+
+sigspec_list:
+ sigspec_list sigspec {
+ $$ = new RTLIL::SigSpec;
+ for (auto it = $2->chunks.begin(); it != $2->chunks.end(); it++) {
+ $$->chunks.push_back(*it);
+ $$->width += it->width;
+ }
+ for (auto it = $1->chunks.begin(); it != $1->chunks.end(); it++) {
+ $$->chunks.push_back(*it);
+ $$->width += it->width;
+ }
+ delete $1;
+ delete $2;
+ } |
+ /* empty */ {
+ $$ = new RTLIL::SigSpec;
+ };
+
+conn_stmt:
+ TOK_CONNECT sigspec sigspec TOK_EOL {
+ if (attrbuf.size() != 0)
+ rtlil_frontend_ilang_yyerror("dangling attribute");
+ current_module->connections.push_back(RTLIL::SigSig(*$2, *$3));
+ delete $2;
+ delete $3;
+ };
+
diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc
new file mode 100644
index 00000000..6693f2d1
--- /dev/null
+++ b/frontends/verilog/Makefile.inc
@@ -0,0 +1,19 @@
+
+GENFILES += frontends/verilog/parser.tab.cc
+GENFILES += frontends/verilog/parser.tab.h
+GENFILES += frontends/verilog/parser.output
+GENFILES += frontends/verilog/lexer.cc
+
+frontends/verilog/parser.tab.cc frontends/verilog/parser.tab.h: frontends/verilog/parser.y
+ bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y
+ mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc
+
+frontends/verilog/lexer.cc: frontends/verilog/lexer.l
+ flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l
+
+OBJS += frontends/verilog/parser.tab.o
+OBJS += frontends/verilog/lexer.o
+OBJS += frontends/verilog/preproc.o
+OBJS += frontends/verilog/verilog_frontend.o
+OBJS += frontends/verilog/const2ast.o
+
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
new file mode 100644
index 00000000..e5beaead
--- /dev/null
+++ b/frontends/verilog/const2ast.cc
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * This file contains an ad-hoc parser for Verilog constants. The Verilog
+ * lexer does only recognize a constant but does not actually split it to its
+ * components. I.e. it just passes the Verilog code for the constant to the
+ * bison parser. The parser then uses the function const2ast() from this file
+ * to create an AST node for the constant.
+ *
+ */
+
+#include "verilog_frontend.h"
+#include "kernel/log.h"
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+using namespace AST;
+
+// divide an arbitrary length decimal number by two and return the rest
+static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
+{
+ int carry = 0;
+ for (size_t i = 0; i < digits.size(); i++) {
+ assert(digits[i] < 10);
+ digits[i] += carry * 10;
+ carry = digits[i] % 2;
+ digits[i] /= 2;
+ }
+ return carry;
+}
+
+// find the number of significant bits in a binary number (not including the sign bit)
+static int my_ilog2(int x)
+{
+ int ret = 0;
+ while (x != 0 && x != -1) {
+ x = x >> 1;
+ ret++;
+ }
+ return ret;
+}
+
+// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
+static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
+{
+ // all digits in string (MSB at index 0)
+ std::vector<uint8_t> digits;
+
+ while (*str) {
+ if ('0' <= *str && *str <= '9')
+ digits.push_back(*str - '0');
+ else if ('a' <= *str && *str <= 'f')
+ digits.push_back(10 + *str - 'a');
+ else if ('A' <= *str && *str <= 'F')
+ digits.push_back(10 + *str - 'A');
+ else if (*str == 'x' || *str == 'X')
+ digits.push_back(0xf0);
+ else if (*str == 'z' || *str == 'Z')
+ digits.push_back(0xf1);
+ else if (*str == '?')
+ digits.push_back(0xf2);
+ str++;
+ }
+
+ if (base == 10) {
+ data.clear();
+ if (len_in_bits < 0)
+ len_in_bits = ceil(digits.size()/log10(2));
+ for (int i = 0; i < len_in_bits; i++)
+ data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
+ return;
+ }
+
+ int bits_per_digit = my_ilog2(base-1);
+ if (len_in_bits < 0)
+ len_in_bits = digits.size() * bits_per_digit;
+
+ data.clear();
+ data.resize(len_in_bits);
+
+ for (int i = 0; i < len_in_bits; i++) {
+ int bitmask = 1 << (i % bits_per_digit);
+ int digitidx = digits.size() - (i / bits_per_digit) - 1;
+ if (digitidx < 0) {
+ if (i > 0 && (data[i-1] == RTLIL::Sz || data[i-1] == RTLIL::Sx || data[i-1] == RTLIL::Sa))
+ data[i] = data[i-1];
+ else
+ data[i] = RTLIL::S0;
+ } else if (digits[digitidx] == 0xf0)
+ data[i] = case_type == 'x' ? RTLIL::Sa : RTLIL::Sx;
+ else if (digits[digitidx] == 0xf1)
+ data[i] = case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz;
+ else if (digits[digitidx] == 0xf2)
+ data[i] = RTLIL::Sa;
+ else
+ data[i] = (digits[digitidx] & bitmask) ? RTLIL::S1 : RTLIL::S0;
+ }
+}
+
+// convert the verilog code for a constant to an AST node
+AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type)
+{
+ const char *str = code.c_str();
+
+ // Strings
+ if (*str == '"') {
+ int len = strlen(str) - 2;
+ std::vector<RTLIL::State> data;
+ data.reserve(len * 8);
+ for (int i = 0; i < len; i++) {
+ unsigned char ch = str[len - i];
+ for (int j = 0; j < 8; j++) {
+ data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
+ ch = ch >> 1;
+ }
+ }
+ AstNode *ast = AstNode::mkconst_bits(data, false);
+ ast->str = code;
+ return ast;
+ }
+
+ for (size_t i = 0; i < code.size(); i++)
+ if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
+ code.erase(code.begin()+(i--));
+ str = code.c_str();
+
+ char *endptr;
+ long intval = strtol(str, &endptr, 10);
+
+ // Simple 32 bit integer
+ if (*endptr == 0)
+ return AstNode::mkconst_int(intval, true);
+
+ // variable length constant
+ if (str == endptr)
+ intval = -1;
+
+ // The "<bits>'[bodh]<digits>" syntax
+ if (*endptr == '\'')
+ {
+ int len_in_bits = intval;
+ std::vector<RTLIL::State> data;
+ bool is_signed = false;
+ if (*(endptr+1) == 's') {
+ is_signed = true;
+ endptr++;
+ }
+ switch (*(endptr+1))
+ {
+ case 'b':
+ my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
+ break;
+ case 'o':
+ my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
+ break;
+ case 'd':
+ my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
+ break;
+ case 'h':
+ my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
+ break;
+ default:
+ goto error;
+ }
+ return AstNode::mkconst_bits(data, is_signed);
+ }
+
+error:
+ log_error("Value conversion failed: `%s'\n", code.c_str());
+}
+
diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l
new file mode 100644
index 00000000..a269c072
--- /dev/null
+++ b/frontends/verilog/lexer.l
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A simple lexer for Verilog code. Non-preprocessor compiler directives are
+ * handled here. The preprocessor stuff is handled in preproc.cc. Everything
+ * else is left to the bison parser (see parser.y).
+ *
+ */
+
+%{
+
+#include "kernel/log.h"
+#include "verilog_frontend.h"
+#include "frontends/ast/ast.h"
+#include "parser.tab.h"
+
+using namespace AST;
+using namespace VERILOG_FRONTEND;
+
+namespace VERILOG_FRONTEND {
+ std::vector<std::string> fn_stack;
+ std::vector<int> ln_stack;
+ bool lexer_feature_defattr;
+}
+
+%}
+
+%option yylineno
+%option noyywrap
+%option nounput
+%option prefix="frontend_verilog_yy"
+
+%x COMMENT
+%x STRING
+%x SYNOPSYS_TRANSLATE_OFF
+%x SYNOPSYS_FLAGS
+
+%%
+
+"`file_push "[^\n]* {
+ fn_stack.push_back(current_filename);
+ ln_stack.push_back(frontend_verilog_yyget_lineno());
+ current_filename = yytext+11;
+ frontend_verilog_yyset_lineno(0);
+}
+
+"`file_pop"[^\n]*\n {
+ current_filename = fn_stack.back();
+ frontend_verilog_yyset_lineno(ln_stack.back());
+}
+
+"`file_notfound "[^\n]* {
+ log_error("Can't open include file `%s'!\n", yytext + 15);
+}
+
+"`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */
+
+"`yosys_enable_defattr" lexer_feature_defattr = true;
+"`yosys_disable_defattr" lexer_feature_defattr = false;
+
+"`"[a-zA-Z_$][a-zA-Z0-9_$]* {
+ frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext);
+}
+
+"module" { return TOK_MODULE; }
+"endmodule" { return TOK_ENDMODULE; }
+"function" { return TOK_FUNCTION; }
+"endfunction" { return TOK_ENDFUNCTION; }
+"task" { return TOK_TASK; }
+"endtask" { return TOK_ENDTASK; }
+"parameter" { return TOK_PARAMETER; }
+"localparam" { return TOK_LOCALPARAM; }
+"assign" { return TOK_ASSIGN; }
+"always" { return TOK_ALWAYS; }
+"initial" { return TOK_INITIAL; }
+"begin" { return TOK_BEGIN; }
+"end" { return TOK_END; }
+"if" { return TOK_IF; }
+"else" { return TOK_ELSE; }
+"for" { return TOK_FOR; }
+"posedge" { return TOK_POSEDGE; }
+"negedge" { return TOK_NEGEDGE; }
+"or" { return TOK_OR; }
+"case" { return TOK_CASE; }
+"casex" { return TOK_CASEX; }
+"casez" { return TOK_CASEZ; }
+"endcase" { return TOK_ENDCASE; }
+"default" { return TOK_DEFAULT; }
+"generate" { return TOK_GENERATE; }
+"endgenerate" { return TOK_ENDGENERATE; }
+
+"input" { return TOK_INPUT; }
+"output" { return TOK_OUTPUT; }
+"inout" { return TOK_INOUT; }
+"wire" { return TOK_WIRE; }
+"reg" { return TOK_REG; }
+"integer" { return TOK_INTEGER; }
+"signed" { return TOK_SIGNED; }
+"genvar" { return TOK_GENVAR; }
+
+[0-9]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_CONST;
+}
+
+[0-9]*[ \t]*\'s?[bodh][ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_CONST;
+}
+
+\" { BEGIN(STRING); }
+<STRING>\\. { yymore(); }
+<STRING>\" {
+ BEGIN(0);
+ char *yystr = strdup(yytext);
+ yystr[strlen(yytext) - 1] = 0;
+ int i = 0, j = 0;
+ while (yystr[i]) {
+ if (yystr[i] == '\\' && yystr[i + 1]) {
+ i++;
+ if (yystr[i] == 'n')
+ yystr[i] = '\n';
+ else if (yystr[i] == 't')
+ yystr[i] = '\t';
+ else if ('0' <= yystr[i] && yystr[i] <= '7') {
+ yystr[i] = yystr[i] - '0';
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ }
+ }
+ yystr[j++] = yystr[i++];
+ }
+ yystr[j] = 0;
+ frontend_verilog_yylval.string = new std::string(yystr);
+ free(yystr);
+ return TOK_STRING;
+}
+<STRING>. { yymore(); }
+
+and|nand|or|nor|xor|xnor|not|buf {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_PRIMITIVE;
+}
+
+supply0 { return TOK_SUPPLY0; }
+supply1 { return TOK_SUPPLY1; }
+
+"$"(display|time|stop|finish) {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_ID;
+}
+
+"$signed" { return TOK_TO_SIGNED; }
+"$unsigned" { return TOK_TO_UNSIGNED; }
+
+[a-zA-Z_$][a-zA-Z0-9_$]* {
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
+ return TOK_ID;
+}
+
+"/*"[ \t]*synopsys[ \t]*translate_off[ \t]*"*/" {
+ log("Warning: Found one of those horrible `synopsys translate_off' comments.\n");
+ log("It is strongly suggested to use `ifdef constructs instead!\n");
+ BEGIN(SYNOPSYS_TRANSLATE_OFF);
+}
+<SYNOPSYS_TRANSLATE_OFF>. /* ignore synopsys translate_off body */
+<SYNOPSYS_TRANSLATE_OFF>\n /* ignore synopsys translate_off body */
+<SYNOPSYS_TRANSLATE_OFF>"/*"[ \t]*"synopsys"[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); }
+
+"/*"[ \t]*"synopsys"[ \t]+ {
+ BEGIN(SYNOPSYS_FLAGS);
+}
+<SYNOPSYS_FLAGS>full_case {
+ log("Warning: Found one of those horrible `synopsys full_case' comments.\n");
+ log("It is strongly suggested to use verilog x-values and default branches instead!\n");
+ return TOK_SYNOPSYS_FULL_CASE;
+}
+<SYNOPSYS_FLAGS>parallel_case {
+ log("Warning: Found one of those horrible `synopsys parallel_case' comments.\n");
+ log("It is strongly suggested to use verilog `parallel_case' attributes instead!\n");
+ return TOK_SYNOPSYS_PARALLEL_CASE;
+}
+<SYNOPSYS_FLAGS>. /* ignore everything else */
+<SYNOPSYS_FLAGS>"*/" { BEGIN(0); }
+
+"\\"[^ \t\r\n]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_ID;
+}
+
+"(*" { return ATTR_BEGIN; }
+"*)" { return ATTR_END; }
+
+"{*" { if (lexer_feature_defattr) return DEFATTR_BEGIN; else REJECT; }
+"*}" { if (lexer_feature_defattr) return DEFATTR_END; else REJECT; }
+
+"**" { return OP_POW; }
+"||" { return OP_LOR; }
+"&&" { return OP_LAND; }
+"==" { return OP_EQ; }
+"!=" { return OP_NE; }
+"<=" { return OP_LE; }
+">=" { return OP_GE; }
+
+ /* "~&" { return OP_NAND; } */
+ /* "~|" { return OP_NOR; } */
+"~^" { return OP_XNOR; }
+"^~" { return OP_XNOR; }
+
+"<<" { return OP_SHL; }
+">>" { return OP_SHR; }
+"<<<" { return OP_SSHL; }
+">>>" { return OP_SSHR; }
+
+"/*" { BEGIN(COMMENT); }
+<COMMENT>. /* ignore comment body */
+<COMMENT>\n /* ignore comment body */
+<COMMENT>"*/" { BEGIN(0); }
+
+[ \t\r\n] /* ignore whitespaces */
+\\[\r\n] /* ignore continuation sequence */
+"//"[^\r\n]* /* ignore one-line comments */
+"#"[$a-zA-Z_0-9\.]+ /* ignore simulation timings */
+
+. { return *yytext; }
+
+%%
+
+// this is a hack to avoid the 'yyinput defined but not used' error msgs
+void *frontend_verilog_avoid_input_warnings() {
+ return (void*)&yyinput;
+}
+
diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y
new file mode 100644
index 00000000..7c12bd56
--- /dev/null
+++ b/frontends/verilog/parser.y
@@ -0,0 +1,1074 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * This is the actual bison parser for Verilog code. The AST ist created directly
+ * from the bison reduce functions here. Note that this code uses a few global
+ * variables to hold the state of the AST generator and therefore this parser is
+ * not reentrant.
+ *
+ */
+
+%{
+#include <list>
+#include <assert.h>
+#include "verilog_frontend.h"
+#include "kernel/log.h"
+
+using namespace AST;
+using namespace VERILOG_FRONTEND;
+
+namespace VERILOG_FRONTEND {
+ int port_counter;
+ std::map<std::string, int> port_stubs;
+ std::map<std::string, AstNode*> attr_list, default_attr_list;
+ std::map<std::string, AstNode*> *albuf;
+ std::vector<AstNode*> ast_stack;
+ struct AstNode *astbuf1, *astbuf2, *astbuf3;
+ struct AstNode *current_function_or_task;
+ struct AstNode *current_ast, *current_ast_mod;
+ int current_function_or_task_port_id;
+ std::vector<char> case_type_stack;
+}
+
+static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al)
+{
+ for (auto &it : *al) {
+ if (ast->attributes.count(it.first) > 0)
+ delete ast->attributes[it.first];
+ ast->attributes[it.first] = it.second;
+ }
+ delete al;
+}
+
+static void append_attr_clone(AstNode *ast, std::map<std::string, AstNode*> *al)
+{
+ for (auto &it : *al) {
+ if (ast->attributes.count(it.first) > 0)
+ delete ast->attributes[it.first];
+ ast->attributes[it.first] = it.second->clone();
+ }
+}
+
+static void free_attr(std::map<std::string, AstNode*> *al)
+{
+ for (auto &it : *al)
+ delete it.second;
+ delete al;
+}
+
+%}
+
+%name-prefix="frontend_verilog_yy"
+
+%union {
+ std::string *string;
+ struct AstNode *ast;
+ std::map<std::string, AstNode*> *al;
+ bool boolean;
+}
+
+%token <string> TOK_STRING TOK_ID TOK_CONST TOK_PRIMITIVE
+%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
+%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM
+%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG
+%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
+%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR
+%token TOK_POSEDGE TOK_NEGEDGE TOK_OR
+%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
+%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK
+%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR
+%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
+%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
+
+%type <ast> wire_type range expr basic_expr concat_list lvalue lvalue_concat_list
+%type <string> opt_label tok_prim_wrapper
+%type <boolean> opt_signed
+%type <al> attr
+
+// operator precedence from low to high
+%left OP_LOR
+%left OP_LAND
+%left '|'
+%left '^' OP_XNOR
+%left '&'
+%left OP_EQ OP_NE
+%left '<' OP_LE OP_GE '>'
+%left OP_SHL OP_SHR OP_SSHL OP_SSHR
+%left '+' '-'
+%left '*' '/' '%'
+%left OP_POW
+%right UNARY_OPS
+
+%expect 2
+%debug
+
+%%
+
+input:
+ module input |
+ defattr input |
+ /* empty */ {
+ for (auto &it : default_attr_list)
+ delete it.second;
+ default_attr_list.clear();
+ };
+
+attr:
+ {
+ for (auto &it : attr_list)
+ delete it.second;
+ attr_list.clear();
+ for (auto &it : default_attr_list)
+ attr_list[it.first] = it.second->clone();
+ } attr_opt {
+ std::map<std::string, AstNode*> *al = new std::map<std::string, AstNode*>;
+ al->swap(attr_list);
+ $$ = al;
+ };
+
+attr_opt:
+ attr_opt ATTR_BEGIN opt_attr_list ATTR_END |
+ /* empty */;
+
+defattr:
+ DEFATTR_BEGIN {
+ for (auto &it : default_attr_list)
+ delete it.second;
+ default_attr_list.clear();
+ for (auto &it : attr_list)
+ delete it.second;
+ attr_list.clear();
+ } opt_attr_list {
+ default_attr_list = attr_list;
+ attr_list.clear();
+ } DEFATTR_END;
+
+opt_attr_list:
+ attr_list | /* empty */;
+
+attr_list:
+ attr_assign |
+ attr_list ',' attr_assign;
+
+attr_assign:
+ TOK_ID {
+ if (attr_list.count(*$1) != 0)
+ delete attr_list[*$1];
+ attr_list[*$1] = AstNode::mkconst_int(0, false, 0);
+ delete $1;
+ } |
+ TOK_ID '=' expr {
+ if (attr_list.count(*$1) != 0)
+ delete attr_list[*$1];
+ attr_list[*$1] = $3;
+ delete $1;
+ };
+
+module:
+ attr TOK_MODULE TOK_ID {
+ AstNode *mod = new AstNode(AST_MODULE);
+ current_ast->children.push_back(mod);
+ current_ast_mod = mod;
+ ast_stack.push_back(mod);
+ port_stubs.clear();
+ port_counter = 0;
+ mod->str = *$3;
+ append_attr(mod, $1);
+ delete $3;
+ } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE {
+ if (port_stubs.size() != 0)
+ frontend_verilog_yyerror("Missing details for module port `%s'.",
+ port_stubs.begin()->first.c_str());
+ ast_stack.pop_back();
+ assert(ast_stack.size() == 0);
+ };
+
+module_para_opt:
+ '#' '(' TOK_PARAMETER param_decl_list optional_comma ')' | /* empty */;
+
+module_args_opt:
+ '(' ')' | /* empty */ | '(' module_args optional_comma ')';
+
+module_args:
+ module_arg | module_args ',' module_arg;
+
+optional_comma:
+ ',' | /* empty */;
+
+module_arg:
+ TOK_ID range {
+ if (port_stubs.count(*$1) != 0)
+ frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str());
+ port_stubs[*$1] = ++port_counter;
+ if ($2 != NULL)
+ delete $2;
+ delete $1;
+ } |
+ attr wire_type range TOK_ID {
+ AstNode *node = $2;
+ node->str = *$4;
+ node->port_id = ++port_counter;
+ if ($3 != NULL)
+ node->children.push_back($3);
+ if (!node->is_input && !node->is_output)
+ frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str());
+ if (node->is_reg && node->is_input && !node->is_output)
+ frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str());
+ ast_stack.back()->children.push_back(node);
+ append_attr(node, $1);
+ delete $4;
+ };
+
+wire_type:
+ {
+ astbuf3 = new AstNode(AST_WIRE);
+ } wire_type_token_list {
+ $$ = astbuf3;
+ };
+
+wire_type_token_list:
+ wire_type_token | wire_type_token_list wire_type_token;
+
+wire_type_token:
+ TOK_INPUT {
+ astbuf3->is_input = true;
+ } |
+ TOK_OUTPUT {
+ astbuf3->is_output = true;
+ } |
+ TOK_INOUT {
+ astbuf3->is_input = true;
+ astbuf3->is_output = true;
+ } |
+ TOK_WIRE {
+ } |
+ TOK_REG {
+ astbuf3->is_reg = true;
+ } |
+ TOK_INTEGER {
+ astbuf3->is_reg = true;
+ astbuf3->range_left = 31;
+ astbuf3->range_right = 0;
+ } |
+ TOK_GENVAR {
+ astbuf3->type = AST_GENVAR;
+ astbuf3->is_reg = true;
+ astbuf3->range_left = 31;
+ astbuf3->range_right = 0;
+ } |
+ TOK_SIGNED {
+ astbuf3->is_signed = true;
+ };
+
+range:
+ '[' expr ':' expr ']' {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back($2);
+ $$->children.push_back($4);
+ } |
+ '[' expr ']' {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back($2);
+ } |
+ /* empty */ {
+ $$ = NULL;
+ };
+
+module_body:
+ module_body module_body_stmt |
+ /* empty */;
+
+module_body_stmt:
+ task_func_decl | param_decl | localparam_decl | wire_decl | assign_stmt | cell_stmt |
+ always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr;
+
+task_func_decl:
+ TOK_TASK TOK_ID ';' {
+ current_function_or_task = new AstNode(AST_TASK);
+ current_function_or_task->str = *$2;
+ ast_stack.back()->children.push_back(current_function_or_task);
+ ast_stack.push_back(current_function_or_task);
+ current_function_or_task_port_id = 1;
+ delete $2;
+ } task_func_body TOK_ENDTASK {
+ current_function_or_task = NULL;
+ ast_stack.pop_back();
+ } |
+ TOK_FUNCTION opt_signed range TOK_ID ';' {
+ current_function_or_task = new AstNode(AST_FUNCTION);
+ current_function_or_task->str = *$4;
+ ast_stack.back()->children.push_back(current_function_or_task);
+ ast_stack.push_back(current_function_or_task);
+ AstNode *outreg = new AstNode(AST_WIRE);
+ if ($3 != NULL)
+ outreg->children.push_back($3);
+ outreg->str = *$4;
+ outreg->is_signed = $2;
+ current_function_or_task->children.push_back(outreg);
+ current_function_or_task_port_id = 1;
+ delete $4;
+ } task_func_body TOK_ENDFUNCTION {
+ current_function_or_task = NULL;
+ ast_stack.pop_back();
+ };
+
+opt_signed:
+ TOK_SIGNED {
+ $$ = true;
+ } |
+ /* empty */ {
+ $$ = false;
+ };
+
+task_func_body:
+ task_func_body wire_decl |
+ task_func_body behavioral_stmt |
+ /* empty */;
+
+param_decl:
+ TOK_PARAMETER param_decl_list ';';
+
+param_decl_list:
+ single_param_decl | param_decl_list ',' single_param_decl;
+
+single_param_decl:
+ range TOK_ID '=' expr {
+ AstNode *node = new AstNode(AST_PARAMETER);
+ node->str = *$2;
+ node->children.push_back($4);
+ if ($1 != NULL)
+ node->children.push_back($1);
+ ast_stack.back()->children.push_back(node);
+ delete $2;
+ };
+
+localparam_decl:
+ TOK_LOCALPARAM localparam_decl_list ';';
+
+localparam_decl_list:
+ single_localparam_decl | localparam_decl_list ',' single_localparam_decl;
+
+single_localparam_decl:
+ range TOK_ID '=' expr {
+ AstNode *node = new AstNode(AST_LOCALPARAM);
+ node->str = *$2;
+ node->children.push_back($4);
+ if ($1 != NULL)
+ node->children.push_back($1);
+ ast_stack.back()->children.push_back(node);
+ delete $2;
+ };
+
+wire_decl:
+ attr wire_type range {
+ albuf = $1;
+ astbuf1 = $2;
+ astbuf2 = $3;
+ if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
+ if (astbuf2) {
+ frontend_verilog_yyerror("Syntax error.");
+ } else {
+ astbuf2 = new AstNode(AST_RANGE);
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
+ }
+ }
+ if (astbuf2 && astbuf2->children.size() != 2)
+ frontend_verilog_yyerror("Syntax error.");
+ } wire_name_list ';' {
+ delete astbuf1;
+ if (astbuf2 != NULL)
+ delete astbuf2;
+ free_attr(albuf);
+ } |
+ attr TOK_SUPPLY0 TOK_ID ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_WIRE));
+ ast_stack.back()->children.back()->str = *$3;
+ append_attr(ast_stack.back()->children.back(), $1);
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)));
+ ast_stack.back()->children.back()->children[0]->str = *$3;
+ delete $3;
+ } |
+ attr TOK_SUPPLY1 TOK_ID ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_WIRE));
+ ast_stack.back()->children.back()->str = *$3;
+ append_attr(ast_stack.back()->children.back(), $1);
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1)));
+ ast_stack.back()->children.back()->children[0]->str = *$3;
+ delete $3;
+ };
+
+wire_name_list:
+ wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign;
+
+wire_name_and_opt_assign:
+ wire_name |
+ wire_name '=' expr {
+ if (!astbuf1->is_reg) {
+ AstNode *wire = new AstNode(AST_IDENTIFIER);
+ wire->str = ast_stack.back()->children.back()->str;
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3));
+ }
+ };
+
+wire_name:
+ TOK_ID range {
+ AstNode *node = astbuf1->clone();
+ node->str = *$1;
+ append_attr_clone(node, albuf);
+ if (astbuf2 != NULL)
+ node->children.push_back(astbuf2->clone());
+ if ($2 != NULL) {
+ if (node->is_input || node->is_output)
+ frontend_verilog_yyerror("Syntax error.");
+ if (!astbuf2) {
+ AstNode *rng = new AstNode(AST_RANGE);
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ node->children.push_back(rng);
+ }
+ node->type = AST_MEMORY;
+ node->children.push_back($2);
+ }
+ if (current_function_or_task == NULL) {
+ if (port_stubs.count(*$1) != 0) {
+ if (!node->is_input && !node->is_output)
+ frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str());
+ if (node->is_reg && node->is_input && !node->is_output)
+ frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str());
+ node->port_id = port_stubs[*$1];
+ port_stubs.erase(*$1);
+ } else {
+ if (node->is_input || node->is_output)
+ frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str());
+ }
+ ast_stack.back()->children.push_back(node);
+ } else {
+ if (node->is_input || node->is_output)
+ node->port_id = current_function_or_task_port_id++;
+ current_function_or_task->children.push_back(node);
+ }
+ delete $1;
+ };
+
+assign_stmt:
+ TOK_ASSIGN assign_expr_list ';';
+
+assign_expr_list:
+ assign_expr | assign_expr_list ',' assign_expr;
+
+assign_expr:
+ expr '=' expr {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3));
+ };
+
+cell_stmt:
+ attr TOK_ID {
+ astbuf1 = new AstNode(AST_CELL);
+ append_attr(astbuf1, $1);
+ astbuf1->children.push_back(new AstNode(AST_CELLTYPE));
+ astbuf1->children[0]->str = *$2;
+ delete $2;
+ } cell_parameter_list_opt cell_list ';' {
+ delete astbuf1;
+ } |
+ attr tok_prim_wrapper {
+ astbuf1 = new AstNode(AST_PRIMITIVE);
+ astbuf1->str = *$2;
+ append_attr(astbuf1, $1);
+ delete $2;
+ } prim_list ';' {
+ delete astbuf1;
+ };
+
+tok_prim_wrapper:
+ TOK_PRIMITIVE {
+ $$ = $1;
+ } |
+ TOK_OR {
+ $$ = new std::string("or");
+ };
+
+cell_list:
+ single_cell |
+ cell_list ',' single_cell;
+
+single_cell:
+ TOK_ID {
+ astbuf2 = astbuf1->clone();
+ if (astbuf2->type != AST_PRIMITIVE)
+ astbuf2->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(astbuf2);
+ } '(' cell_port_list ')';
+
+prim_list:
+ single_prim |
+ prim_list ',' single_prim;
+
+single_prim:
+ single_cell |
+ /* no name */ {
+ astbuf2 = astbuf1->clone();
+ ast_stack.back()->children.push_back(astbuf2);
+ } '(' cell_port_list ')';
+
+cell_parameter_list_opt:
+ '#' '(' cell_parameter_list ')' | /* empty */;
+
+cell_parameter_list:
+ /* empty */ | cell_parameter |
+ cell_parameter ',' cell_parameter_list;
+
+cell_parameter:
+ expr {
+ AstNode *node = new AstNode(AST_PARASET);
+ astbuf1->children.push_back(node);
+ node->children.push_back($1);
+ } |
+ '.' TOK_ID '(' expr ')' {
+ AstNode *node = new AstNode(AST_PARASET);
+ node->str = *$2;
+ astbuf1->children.push_back(node);
+ node->children.push_back($4);
+ delete $2;
+ };
+
+cell_port_list:
+ /* empty */ | cell_port |
+ cell_port ',' cell_port_list |
+ /* empty */ ',' {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ astbuf2->children.push_back(node);
+ } cell_port_list;
+
+cell_port:
+ expr {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ astbuf2->children.push_back(node);
+ node->children.push_back($1);
+ } |
+ '.' TOK_ID '(' expr ')' {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ node->str = *$2;
+ astbuf2->children.push_back(node);
+ node->children.push_back($4);
+ delete $2;
+ } |
+ '.' TOK_ID '(' ')' {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ node->str = *$2;
+ astbuf2->children.push_back(node);
+ delete $2;
+ };
+
+always_stmt:
+ attr TOK_ALWAYS {
+ AstNode *node = new AstNode(AST_ALWAYS);
+ append_attr(node, $1);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } always_cond {
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_INITIAL {
+ AstNode *node = new AstNode(AST_ALWAYS);
+ append_attr(node, $1);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+always_cond:
+ '@' '(' always_events ')' |
+ '@' '*' |
+ /* empty */;
+
+always_events:
+ always_event |
+ always_events TOK_OR always_event |
+ always_events ',' always_event;
+
+always_event:
+ TOK_POSEDGE expr {
+ AstNode *node = new AstNode(AST_POSEDGE);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back($2);
+ } |
+ TOK_NEGEDGE expr {
+ AstNode *node = new AstNode(AST_NEGEDGE);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back($2);
+ } |
+ expr {
+ AstNode *node = new AstNode(AST_EDGE);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back($1);
+ };
+
+opt_label:
+ ':' TOK_ID {
+ $$ = $2;
+ } |
+ /* empty */ {
+ $$ = NULL;
+ };
+
+simple_behavioral_stmt:
+ lvalue '=' expr {
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3);
+ ast_stack.back()->children.push_back(node);
+ } |
+ lvalue OP_LE expr {
+ AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $3);
+ ast_stack.back()->children.push_back(node);
+ };
+
+// this production creates the obligatory if-else shift/reduce conflict
+behavioral_stmt:
+ defattr |
+ simple_behavioral_stmt ';' |
+ TOK_ID attr {
+ AstNode *node = new AstNode(AST_TCALL);
+ node->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $2);
+ } opt_arg_list ';'{
+ ast_stack.pop_back();
+ } |
+ attr TOK_BEGIN opt_label {
+ AstNode *node = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ } behavioral_stmt_list TOK_END opt_label {
+ if ($3 != NULL)
+ delete $3;
+ if ($7 != NULL)
+ delete $7;
+ ast_stack.pop_back();
+ } |
+ attr TOK_FOR '(' {
+ AstNode *node = new AstNode(AST_FOR);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ } simple_behavioral_stmt ';' expr {
+ ast_stack.back()->children.push_back($7);
+ } ';' simple_behavioral_stmt ')' {
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_IF '(' expr ')' {
+ AstNode *node = new AstNode(AST_CASE);
+ AstNode *block = new AstNode(AST_BLOCK);
+ AstNode *cond = new AstNode(AST_COND, AstNode::mkconst_int(1, false, 1), block);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back(new AstNode(AST_REDUCE_BOOL, $4));
+ node->children.push_back(cond);
+ ast_stack.push_back(node);
+ ast_stack.push_back(block);
+ append_attr(node, $1);
+ } behavioral_stmt optional_else {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr case_type '(' expr ')' {
+ AstNode *node = new AstNode(AST_CASE, $4);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ } opt_synopsys_attr case_body TOK_ENDCASE {
+ case_type_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+case_type:
+ TOK_CASE {
+ case_type_stack.push_back(0);
+ } |
+ TOK_CASEX {
+ case_type_stack.push_back('x');
+ } |
+ TOK_CASEZ {
+ case_type_stack.push_back('z');
+ };
+
+opt_synopsys_attr:
+ opt_synopsys_attr TOK_SYNOPSYS_FULL_CASE {
+ if (ast_stack.back()->attributes.count("\\full_case") == 0)
+ ast_stack.back()->attributes["\\full_case"] = AstNode::mkconst_int(0, false, 0);
+ } |
+ opt_synopsys_attr TOK_SYNOPSYS_PARALLEL_CASE {
+ if (ast_stack.back()->attributes.count("\\parallel_case") == 0)
+ ast_stack.back()->attributes["\\parallel_case"] = AstNode::mkconst_int(0, false, 0);
+ } |
+ /* empty */;
+
+behavioral_stmt_opt:
+ behavioral_stmt |
+ ';' ;
+
+behavioral_stmt_list:
+ behavioral_stmt_list behavioral_stmt |
+ /* empty */;
+
+optional_else:
+ TOK_ELSE {
+ AstNode *block = new AstNode(AST_BLOCK);
+ AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block);
+ ast_stack.pop_back();
+ ast_stack.back()->children.push_back(cond);
+ ast_stack.push_back(block);
+ } behavioral_stmt |
+ /* empty */;
+
+case_body:
+ case_body case_item |
+ /* empty */;
+
+case_item:
+ {
+ AstNode *node = new AstNode(AST_COND);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } case_select {
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ case_type_stack.push_back(0);
+ } behavioral_stmt_opt {
+ case_type_stack.pop_back();
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+case_select:
+ case_expr_list ':' |
+ TOK_DEFAULT;
+
+case_expr_list:
+ TOK_DEFAULT {
+ ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT));
+ } |
+ expr {
+ ast_stack.back()->children.push_back($1);
+ } |
+ case_expr_list ',' expr {
+ ast_stack.back()->children.push_back($3);
+ };
+
+lvalue:
+ TOK_ID range {
+ $$ = new AstNode(AST_IDENTIFIER);
+ $$->str = *$1;
+ if ($2)
+ $$->children.push_back($2);
+ delete $1;
+ } |
+ '{' lvalue_concat_list '}' {
+ $$ = $2;
+ };
+
+lvalue_concat_list:
+ expr {
+ $$ = new AstNode(AST_CONCAT);
+ $$->children.push_back($1);
+ } |
+ expr ',' lvalue_concat_list {
+ $$ = $3;
+ $$->children.push_back($1);
+ };
+
+opt_arg_list:
+ '(' arg_list optional_comma ')' |
+ /* empty */;
+
+arg_list:
+ arg_list2 |
+ /* empty */;
+
+arg_list2:
+ single_arg |
+ arg_list ',' single_arg;
+
+single_arg:
+ expr {
+ ast_stack.back()->children.push_back($1);
+ };
+
+module_gen_body:
+ module_gen_body gen_stmt |
+ module_gen_body module_body_stmt |
+ /* empty */;
+
+// this production creates the obligatory if-else shift/reduce conflict
+gen_stmt:
+ TOK_FOR '(' {
+ AstNode *node = new AstNode(AST_GENFOR);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } simple_behavioral_stmt ';' expr {
+ ast_stack.back()->children.push_back($6);
+ } ';' simple_behavioral_stmt ')' gen_stmt {
+ ast_stack.pop_back();
+ } |
+ TOK_IF '(' expr ')' {
+ AstNode *node = new AstNode(AST_GENIF);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ ast_stack.back()->children.push_back($3);
+ } gen_stmt opt_gen_else {
+ ast_stack.pop_back();
+ } |
+ TOK_BEGIN opt_label {
+ AstNode *node = new AstNode(AST_GENBLOCK);
+ node->str = $2 ? *$2 : std::string();
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } module_gen_body TOK_END opt_label {
+ if ($2 != NULL)
+ delete $2;
+ if ($6 != NULL)
+ delete $6;
+ ast_stack.pop_back();
+ };
+
+opt_gen_else:
+ TOK_ELSE gen_stmt | /* empty */;
+
+expr:
+ basic_expr {
+ $$ = $1;
+ } |
+ basic_expr '?' attr expr ':' expr {
+ $$ = new AstNode(AST_TERNARY);
+ $$->children.push_back($1);
+ $$->children.push_back($4);
+ $$->children.push_back($6);
+ append_attr($$, $3);
+ };
+
+basic_expr:
+ TOK_CONST {
+ $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back());
+ delete $1;
+ } |
+ TOK_STRING {
+ std::string str = *$1;
+ std::vector<RTLIL::State> data;
+ data.reserve(str.size() * 8);
+ for (size_t i = 0; i < str.size(); i++) {
+ unsigned char ch = str[str.size() - i - 1];
+ for (int j = 0; j < 8; j++) {
+ data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
+ ch = ch >> 1;
+ }
+ }
+ $$ = AstNode::mkconst_bits(data, false);
+ $$->str = str;
+ delete $1;
+ } |
+ TOK_ID range {
+ $$ = new AstNode(AST_IDENTIFIER, $2);
+ $$->str = *$1;
+ delete $1;
+ } |
+ TOK_ID attr {
+ AstNode *node = new AstNode(AST_FCALL);
+ node->str = *$1;
+ delete $1;
+ ast_stack.push_back(node);
+ append_attr(node, $2);
+ } '(' arg_list optional_comma ')' {
+ $$ = ast_stack.back();
+ ast_stack.pop_back();
+ } |
+ TOK_TO_SIGNED attr '(' expr ')' {
+ $$ = new AstNode(AST_TO_SIGNED, $4);
+ append_attr($$, $2);
+ } |
+ TOK_TO_UNSIGNED attr '(' expr ')' {
+ $$ = new AstNode(AST_TO_UNSIGNED, $4);
+ append_attr($$, $2);
+ } |
+ '(' expr ')' {
+ $$ = $2;
+ } |
+ '{' concat_list '}' {
+ $$ = $2;
+ } |
+ '{' expr '{' expr '}' '}' {
+ $$ = new AstNode(AST_REPLICATE, $2, $4);
+ } |
+ '~' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_BIT_NOT, $3);
+ append_attr($$, $2);
+ } |
+ basic_expr '&' attr basic_expr {
+ $$ = new AstNode(AST_BIT_AND, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '|' attr basic_expr {
+ $$ = new AstNode(AST_BIT_OR, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '^' attr basic_expr {
+ $$ = new AstNode(AST_BIT_XOR, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_XNOR attr basic_expr {
+ $$ = new AstNode(AST_BIT_XNOR, $1, $4);
+ append_attr($$, $3);
+ } |
+ '&' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_AND, $3);
+ append_attr($$, $2);
+ } |
+ '|' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_OR, $3);
+ append_attr($$, $2);
+ } |
+ '^' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_XOR, $3);
+ append_attr($$, $2);
+ } |
+ OP_XNOR attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_XNOR, $3);
+ append_attr($$, $2);
+ } |
+ basic_expr OP_SHL attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_LEFT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_SHR attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_RIGHT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_SSHL attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_SLEFT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_SSHR attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_SRIGHT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '<' attr basic_expr {
+ $$ = new AstNode(AST_LT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_LE attr basic_expr {
+ $$ = new AstNode(AST_LE, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_EQ attr basic_expr {
+ $$ = new AstNode(AST_EQ, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_NE attr basic_expr {
+ $$ = new AstNode(AST_NE, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_GE attr basic_expr {
+ $$ = new AstNode(AST_GE, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '>' attr basic_expr {
+ $$ = new AstNode(AST_GT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '+' attr basic_expr {
+ $$ = new AstNode(AST_ADD, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '-' attr basic_expr {
+ $$ = new AstNode(AST_SUB, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '*' attr basic_expr {
+ $$ = new AstNode(AST_MUL, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '/' attr basic_expr {
+ $$ = new AstNode(AST_DIV, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '%' attr basic_expr {
+ $$ = new AstNode(AST_MOD, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_POW attr basic_expr {
+ $$ = new AstNode(AST_POW, $1, $4);
+ append_attr($$, $3);
+ } |
+ '+' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_POS, $3);
+ append_attr($$, $2);
+ } |
+ '-' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_NEG, $3);
+ append_attr($$, $2);
+ } |
+ basic_expr OP_LAND attr basic_expr {
+ $$ = new AstNode(AST_LOGIC_AND, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_LOR attr basic_expr {
+ $$ = new AstNode(AST_LOGIC_OR, $1, $4);
+ append_attr($$, $3);
+ } |
+ '!' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_LOGIC_NOT, $3);
+ append_attr($$, $2);
+ };
+
+concat_list:
+ expr {
+ $$ = new AstNode(AST_CONCAT, $1);
+ } |
+ expr ',' concat_list {
+ $$ = $3;
+ $$->children.push_back($1);
+ };
+
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;
+}
+
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
new file mode 100644
index 00000000..c1823379
--- /dev/null
+++ b/frontends/verilog/verilog_frontend.cc
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ *
+ */
+
+#include "verilog_frontend.h"
+#include "kernel/register.h"
+#include "kernel/log.h"
+#include "kernel/sha1.h"
+#include <sstream>
+#include <stdarg.h>
+#include <assert.h>
+
+using namespace VERILOG_FRONTEND;
+
+// use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL
+
+struct VerilogFrontend : public Frontend {
+ VerilogFrontend() : Frontend("verilog") { }
+ virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_dump_ast = false;
+ bool flag_dump_ast_diff = false;
+ bool flag_dump_vlog = false;
+ bool flag_nolatches = false;
+ bool flag_nomem2reg = false;
+ bool flag_ppdump = false;
+ bool flag_nopp = false;
+ frontend_verilog_yydebug = false;
+
+ log_header("Executing Verilog-2005 frontend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-dump_ast") {
+ flag_dump_ast = true;
+ continue;
+ }
+ if (arg == "-dump_ast_diff") {
+ flag_dump_ast = true;
+ flag_dump_ast_diff = true;
+ continue;
+ }
+ if (arg == "-dump_vlog") {
+ flag_dump_vlog = true;
+ continue;
+ }
+ if (arg == "-yydebug") {
+ frontend_verilog_yydebug = true;
+ continue;
+ }
+ if (arg == "-nolatches") {
+ flag_nolatches = true;
+ continue;
+ }
+ if (arg == "-nomem2reg") {
+ flag_nomem2reg = true;
+ continue;
+ }
+ if (arg == "-ppdump") {
+ flag_ppdump = true;
+ continue;
+ }
+ if (arg == "-nopp") {
+ flag_nopp = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ log("Parsing Verilog input from `%s' to AST representation.\n", filename.c_str());
+
+ AST::current_filename = filename;
+ AST::set_line_num = &frontend_verilog_yyset_lineno;
+ AST::get_line_num = &frontend_verilog_yyget_lineno;
+
+ current_ast = new AST::AstNode(AST::AST_DESIGN);
+
+ FILE *fp = f;
+ std::string code_after_preproc;
+
+ if (!flag_nopp) {
+ code_after_preproc = frontend_verilog_preproc(f, filename);
+ if (flag_ppdump)
+ log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str());
+ fp = fmemopen((void*)code_after_preproc.c_str(), code_after_preproc.size(), "r");
+ }
+
+ lexer_feature_defattr = false;
+
+ frontend_verilog_yyset_lineno(1);
+ frontend_verilog_yyrestart(fp);
+ frontend_verilog_yyparse();
+ frontend_verilog_yylex_destroy();
+
+ AST::process(design, current_ast, flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg);
+
+ if (!flag_nopp)
+ fclose(fp);
+
+ delete current_ast;
+ current_ast = NULL;
+
+ log("Successfully finished Verilog frontend.\n");
+ }
+} VerilogFrontend;
+
+// the yyerror function used by bison to report parser errors
+void frontend_verilog_yyerror(char const *fmt, ...)
+{
+ va_list ap;
+ char buffer[1024];
+ char *p = buffer;
+ p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ",
+ AST::current_filename.c_str(), frontend_verilog_yyget_lineno());
+ va_start(ap, fmt);
+ p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap);
+ va_end(ap);
+ p += snprintf(p, buffer + sizeof(buffer) - p, "\n");
+ log_error("%s", buffer);
+ exit(1);
+}
+
diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h
new file mode 100644
index 00000000..808edfc7
--- /dev/null
+++ b/frontends/verilog/verilog_frontend.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef VERILOG_FRONTEND_H
+#define VERILOG_FRONTEND_H
+
+#include "kernel/rtlil.h"
+#include "frontends/ast/ast.h"
+#include <stdio.h>
+#include <stdint.h>
+
+namespace VERILOG_FRONTEND
+{
+ // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser
+ extern struct AST::AstNode *current_ast;
+
+ // this function converts a Verilog constant to an AST_CONSTANT node
+ AST::AstNode *const2ast(std::string code, char case_type = 0);
+
+ // lexer state variables
+ extern bool lexer_feature_defattr;
+}
+
+// the pre-processor
+std::string frontend_verilog_preproc(FILE *f, std::string filename);
+
+// the usual bison/flex stuff
+extern int frontend_verilog_yydebug;
+int frontend_verilog_yylex(void);
+void frontend_verilog_yyerror(char const *fmt, ...);
+void frontend_verilog_yyrestart(FILE *f);
+int frontend_verilog_yyparse(void);
+int frontend_verilog_yylex_destroy(void);
+int frontend_verilog_yyget_lineno(void);
+void frontend_verilog_yyset_lineno (int);
+
+#endif