summaryrefslogtreecommitdiff
path: root/frontends/ast
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/ast
initial import
Diffstat (limited to 'frontends/ast')
-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
5 files changed, 3227 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++;
+}
+