| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com> |
| * |
| * 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/yosys.h" |
| #include "libs/sha1/sha1.h" |
| #include "ast.h" |
| |
| YOSYS_NAMESPACE_BEGIN |
| |
| using namespace AST; |
| using namespace AST_INTERNAL; |
| |
| // instantiate global variables (public API) |
| namespace AST { |
| std::string current_filename; |
| void (*set_line_num)(int) = NULL; |
| int (*get_line_num)() = NULL; |
| } |
| |
| // instantiate global variables (private API) |
| namespace AST_INTERNAL { |
| bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; |
| bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire; |
| AstNode *current_ast, *current_ast_mod; |
| std::map<std::string, AstNode*> current_scope; |
| const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; |
| RTLIL::SigSpec ignoreThisSignalsInInitial; |
| AstNode *current_always, *current_top_block, *current_block, *current_block_child; |
| AstModule *current_module; |
| bool current_always_clocked; |
| } |
| |
| // 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_DPI_FUNCTION) |
| X(AST_WIRE) |
| X(AST_MEMORY) |
| X(AST_AUTOWIRE) |
| X(AST_PARAMETER) |
| X(AST_LOCALPARAM) |
| X(AST_DEFPARAM) |
| X(AST_PARASET) |
| X(AST_ARGUMENT) |
| X(AST_RANGE) |
| X(AST_MULTIRANGE) |
| X(AST_CONSTANT) |
| X(AST_REALVALUE) |
| X(AST_CELLTYPE) |
| X(AST_IDENTIFIER) |
| X(AST_PREFIX) |
| X(AST_ASSERT) |
| X(AST_ASSUME) |
| X(AST_LIVE) |
| X(AST_FAIR) |
| X(AST_COVER) |
| X(AST_FCALL) |
| X(AST_TO_BITS) |
| 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_EQX) |
| X(AST_NEX) |
| 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_MEMINIT) |
| X(AST_TCALL) |
| X(AST_ASSIGN) |
| X(AST_CELL) |
| X(AST_PRIMITIVE) |
| X(AST_CELLARRAY) |
| X(AST_ALWAYS) |
| X(AST_INITIAL) |
| X(AST_BLOCK) |
| X(AST_ASSIGN_EQ) |
| X(AST_ASSIGN_LE) |
| X(AST_CASE) |
| X(AST_COND) |
| X(AST_CONDX) |
| X(AST_CONDZ) |
| X(AST_DEFAULT) |
| X(AST_FOR) |
| X(AST_WHILE) |
| X(AST_REPEAT) |
| X(AST_GENVAR) |
| X(AST_GENFOR) |
| X(AST_GENIF) |
| X(AST_GENCASE) |
| X(AST_GENBLOCK) |
| X(AST_TECALL) |
| X(AST_POSEDGE) |
| X(AST_NEGEDGE) |
| X(AST_EDGE) |
| X(AST_INTERFACE) |
| X(AST_INTERFACEPORT) |
| X(AST_INTERFACEPORTTYPE) |
| X(AST_MODPORT) |
| X(AST_MODPORTMEMBER) |
| X(AST_PACKAGE) |
| X(AST_WIRETYPE) |
| X(AST_TYPEDEF) |
| #undef X |
| default: |
| log_abort(); |
| } |
| } |
| |
| // check if attribute exists and has non-zero value |
| bool AstNode::get_bool_attribute(RTLIL::IdString id) |
| { |
| if (attributes.count(id) == 0) |
| return false; |
| |
| AstNode *attr = attributes.at(id); |
| if (attr->type != AST_CONSTANT) |
| log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", id.c_str()); |
| |
| return attr->integer != 0; |
| } |
| |
| // create new node (AstNode constructor) |
| // (the optional child arguments make it easier to create AST trees) |
| AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3) |
| { |
| static unsigned int hashidx_count = 123456789; |
| hashidx_count = mkhash_xorshift(hashidx_count); |
| hashidx_ = hashidx_count; |
| |
| this->type = type; |
| filename = current_filename; |
| linenum = get_line_num(); |
| is_input = false; |
| is_output = false; |
| is_reg = false; |
| is_logic = false; |
| is_signed = false; |
| is_string = false; |
| is_wand = false; |
| is_wor = false; |
| is_unsized = false; |
| was_checked = false; |
| range_valid = false; |
| range_swapped = false; |
| is_custom_type = false; |
| port_id = 0; |
| range_left = -1; |
| range_right = 0; |
| integer = 0; |
| realvalue = 0; |
| id2ast = NULL; |
| basic_prep = false; |
| |
| if (child1) |
| children.push_back(child1); |
| if (child2) |
| children.push_back(child2); |
| if (child3) |
| children.push_back(child3); |
| } |
| |
| // create a (deep recursive) copy of a node |
| AstNode *AstNode::clone() const |
| { |
| 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) const |
| { |
| 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) const |
| { |
| if (f == NULL) { |
| for (auto f : log_files) |
| 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 (!flag_no_dump_ptr) { |
| if (id2ast) |
| fprintf(f, " [%p -> %p]", this, id2ast); |
| else |
| fprintf(f, " [%p]", this); |
| } |
| |
| 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] == State::S0 ? '0' : |
| bits[i-1] == State::S1 ? '1' : |
| bits[i-1] == RTLIL::Sx ? 'x' : |
| bits[i-1] == RTLIL::Sz ? 'z' : '?'); |
| fprintf(f, "'(%d)", GetSize(bits)); |
| } |
| if (is_input) |
| fprintf(f, " input"); |
| if (is_output) |
| fprintf(f, " output"); |
| if (is_logic) |
| fprintf(f, " logic"); |
| if (is_reg) // this is an AST dump, not Verilog - if we see "logic reg" that's fine. |
| 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, " %srange=[%d:%d]%s", range_swapped ? "swapped_" : "", range_left, range_right, range_valid ? "" : "!"); |
| if (integer != 0) |
| fprintf(f, " int=%u", (int)integer); |
| if (realvalue != 0) |
| fprintf(f, " real=%e", realvalue); |
| if (!multirange_dimensions.empty()) { |
| fprintf(f, " multirange=["); |
| for (int v : multirange_dimensions) |
| fprintf(f, " %d", v); |
| fprintf(f, " ]"); |
| } |
| fprintf(f, "\n"); |
| |
| for (auto &it : attributes) { |
| fprintf(f, "%s ATTR %s:\n", indent.c_str(), it.first.c_str()); |
| it.second->dumpAst(f, indent + " "); |
| } |
| |
| for (size_t i = 0; i < children.size(); i++) |
| children[i]->dumpAst(f, indent + " "); |
| |
| fflush(f); |
| } |
| |
| // 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) const |
| { |
| 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; |
| } |
| |
| for (auto &it : attributes) { |
| fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first.str()).c_str()); |
| it.second->dumpVlog(f, ""); |
| fprintf(f, " *)%s", indent.empty() ? "" : "\n"); |
| } |
| |
| 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->type == AST_DEFPARAM) |
| 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) { |
| if (range_swapped) |
| fprintf(f, "[%d:%d]", range_right, range_left); |
| else |
| 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; |
| fprintf(f, first ? "(" : ", "); |
| child->dumpVlog(f, ""); |
| first = false; |
| } |
| fprintf(f, first ? "*\n" : ")\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_INITIAL: |
| fprintf(f, "%s" "initial\n", indent.c_str()); |
| 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, "%d'b %s", GetSize(bits), RTLIL::Const(bits).as_string().c_str()); |
| break; |
| |
| case AST_REALVALUE: |
| fprintf(f, "%e", realvalue); |
| 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: |
| if (!children.empty() && children[0]->type == AST_CONDX) |
| fprintf(f, "%s" "casex (", indent.c_str()); |
| else if (!children.empty() && children[0]->type == AST_CONDZ) |
| fprintf(f, "%s" "casez (", indent.c_str()); |
| else |
| 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: |
| case AST_CONDX: |
| case AST_CONDZ: |
| 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: |
| fprintf(f, "%sassign ", indent.c_str()); |
| children[0]->dumpVlog(f, ""); |
| fprintf(f, " = "); |
| children[1]->dumpVlog(f, ""); |
| fprintf(f, ";\n"); |
| 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 (int i = GetSize(children)-1; i >= 0; i--) { |
| auto child = children[i]; |
| 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_EQX: txt = "==="; } |
| if (0) { case AST_NEX: 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); |
| } |
| |
| fflush(f); |
| } |
| |
| // 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_logic != other.is_logic) |
| return false; |
| if (is_reg != other.is_reg) |
| return false; |
| if (is_signed != other.is_signed) |
| return false; |
| if (is_string != other.is_string) |
| return false; |
| if (range_valid != other.range_valid) |
| return false; |
| if (range_swapped != other.range_swapped) |
| 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) ? State::S1 : State::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, bool is_unsized) |
| { |
| 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] == State::S1) << i; |
| else if (is_signed && !node->bits.empty()) |
| node->integer |= (node->bits.back() == State::S1) << i; |
| } |
| node->range_valid = true; |
| node->range_left = node->bits.size()-1; |
| node->range_right = 0; |
| node->is_unsized = is_unsized; |
| return node; |
| } |
| |
| AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed) |
| { |
| return mkconst_bits(v, is_signed, false); |
| } |
| |
| // create an AST node for a constant (using a string in bit vector form as value) |
| AstNode *AstNode::mkconst_str(const std::vector<RTLIL::State> &v) |
| { |
| AstNode *node = mkconst_str(RTLIL::Const(v).decode_string()); |
| while (GetSize(node->bits) < GetSize(v)) |
| node->bits.push_back(RTLIL::State::S0); |
| log_assert(node->bits == v); |
| return node; |
| } |
| |
| // create an AST node for a constant (using a string as value) |
| AstNode *AstNode::mkconst_str(const std::string &str) |
| { |
| std::vector<RTLIL::State> data; |
| data.reserve(str.size() * 8); |
| for (size_t i = 0; i < str.size(); i++) { |
| unsigned char ch = str[str.size() - i - 1]; |
| for (int j = 0; j < 8; j++) { |
| data.push_back((ch & 1) ? State::S1 : State::S0); |
| ch = ch >> 1; |
| } |
| } |
| AstNode *node = AstNode::mkconst_bits(data, false); |
| node->is_string = true; |
| node->str = str; |
| return node; |
| } |
| |
| bool AstNode::bits_only_01() const |
| { |
| for (auto bit : bits) |
| if (bit != State::S0 && bit != State::S1) |
| return false; |
| return true; |
| } |
| |
| RTLIL::Const AstNode::bitsAsUnsizedConst(int width) |
| { |
| RTLIL::State extbit = bits.back(); |
| while (width > int(bits.size())) |
| bits.push_back(extbit); |
| return RTLIL::Const(bits); |
| } |
| |
| RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed) |
| { |
| std::vector<RTLIL::State> bits = this->bits; |
| if (width >= 0 && width < int(bits.size())) |
| bits.resize(width); |
| if (width >= 0 && width > int(bits.size())) { |
| RTLIL::State extbit = RTLIL::State::S0; |
| if (is_signed && !bits.empty()) |
| extbit = bits.back(); |
| while (width > int(bits.size())) |
| bits.push_back(extbit); |
| } |
| return RTLIL::Const(bits); |
| } |
| |
| RTLIL::Const AstNode::bitsAsConst(int width) |
| { |
| return bitsAsConst(width, is_signed); |
| } |
| |
| RTLIL::Const AstNode::asAttrConst() |
| { |
| log_assert(type == AST_CONSTANT); |
| |
| RTLIL::Const val; |
| val.bits = bits; |
| |
| if (is_string) { |
| val.flags |= RTLIL::CONST_FLAG_STRING; |
| log_assert(val.decode_string() == str); |
| } |
| |
| return val; |
| } |
| |
| RTLIL::Const AstNode::asParaConst() |
| { |
| RTLIL::Const val = asAttrConst(); |
| if (is_signed) |
| val.flags |= RTLIL::CONST_FLAG_SIGNED; |
| return val; |
| } |
| |
| bool AstNode::asBool() const |
| { |
| log_assert(type == AST_CONSTANT); |
| for (auto &bit : bits) |
| if (bit == RTLIL::State::S1) |
| return true; |
| return false; |
| } |
| |
| int AstNode::isConst() const |
| { |
| if (type == AST_CONSTANT) |
| return 1; |
| if (type == AST_REALVALUE) |
| return 2; |
| return 0; |
| } |
| |
| uint64_t AstNode::asInt(bool is_signed) |
| { |
| if (type == AST_CONSTANT) |
| { |
| RTLIL::Const v = bitsAsConst(64, is_signed); |
| uint64_t ret = 0; |
| |
| for (int i = 0; i < 64; i++) |
| if (v.bits.at(i) == RTLIL::State::S1) |
| ret |= uint64_t(1) << i; |
| |
| return ret; |
| } |
| |
| if (type == AST_REALVALUE) |
| return uint64_t(realvalue); |
| |
| log_abort(); |
| } |
| |
| double AstNode::asReal(bool is_signed) |
| { |
| if (type == AST_CONSTANT) |
| { |
| RTLIL::Const val(bits); |
| |
| bool is_negative = is_signed && !val.bits.empty() && val.bits.back() == RTLIL::State::S1; |
| if (is_negative) |
| val = const_neg(val, val, false, false, val.bits.size()); |
| |
| double v = 0; |
| for (size_t i = 0; i < val.bits.size(); i++) |
| // IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in |
| // the net or the variable shall be treated as zero upon conversion. |
| if (val.bits.at(i) == RTLIL::State::S1) |
| v += exp2(i); |
| if (is_negative) |
| v *= -1; |
| |
| return v; |
| } |
| |
| if (type == AST_REALVALUE) |
| return realvalue; |
| |
| log_abort(); |
| } |
| |
| RTLIL::Const AstNode::realAsConst(int width) |
| { |
| double v = round(realvalue); |
| RTLIL::Const result; |
| #ifdef EMSCRIPTEN |
| if (!isfinite(v)) { |
| #else |
| if (!std::isfinite(v)) { |
| #endif |
| result.bits = std::vector<RTLIL::State>(width, RTLIL::State::Sx); |
| } else { |
| bool is_negative = v < 0; |
| if (is_negative) |
| v *= -1; |
| for (int i = 0; i < width; i++, v /= 2) |
| result.bits.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); |
| if (is_negative) |
| result = const_neg(result, result, false, false, result.bits.size()); |
| } |
| return result; |
| } |
| |
| // create a new AstModule from an AST_MODULE AST node |
| static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL) |
| { |
| log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); |
| |
| if (defer) |
| log("Storing AST representation for module `%s'.\n", ast->str.c_str()); |
| else |
| log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); |
| |
| 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); |
| current_module->set_bool_attribute("\\cells_not_processed"); |
| |
| current_ast_mod = ast; |
| AstNode *ast_before_simplify; |
| if (original_ast != NULL) |
| ast_before_simplify = original_ast; |
| else |
| ast_before_simplify = ast->clone(); |
| |
| if (flag_dump_ast1) { |
| log("Dumping AST before simplification:\n"); |
| ast->dumpAst(NULL, " "); |
| log("--- END OF AST DUMP ---\n"); |
| } |
| if (flag_dump_vlog1) { |
| log("Dumping Verilog AST before simplification:\n"); |
| ast->dumpVlog(NULL, " "); |
| log("--- END OF AST DUMP ---\n"); |
| } |
| |
| if (!defer) |
| { |
| bool blackbox_module = flag_lib; |
| |
| if (!blackbox_module && !flag_noblackbox) { |
| blackbox_module = true; |
| for (auto child : ast->children) { |
| if (child->type == AST_WIRE && (child->is_input || child->is_output)) |
| continue; |
| if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) |
| continue; |
| if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && |
| (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) |
| continue; |
| blackbox_module = false; |
| break; |
| } |
| } |
| |
| while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } |
| |
| if (flag_dump_ast2) { |
| log("Dumping AST after simplification:\n"); |
| ast->dumpAst(NULL, " "); |
| log("--- END OF AST DUMP ---\n"); |
| } |
| |
| if (flag_dump_vlog2) { |
| log("Dumping Verilog AST after simplification:\n"); |
| ast->dumpVlog(NULL, " "); |
| log("--- END OF AST DUMP ---\n"); |
| } |
| |
| if (flag_nowb && ast->attributes.count("\\whitebox")) { |
| delete ast->attributes.at("\\whitebox"); |
| ast->attributes.erase("\\whitebox"); |
| } |
| |
| if (ast->attributes.count("\\lib_whitebox")) { |
| if (!flag_lib || flag_nowb) { |
| delete ast->attributes.at("\\lib_whitebox"); |
| ast->attributes.erase("\\lib_whitebox"); |
| } else { |
| if (ast->attributes.count("\\whitebox")) { |
| delete ast->attributes.at("\\whitebox"); |
| ast->attributes.erase("\\whitebox"); |
| } |
| AstNode *n = ast->attributes.at("\\lib_whitebox"); |
| ast->attributes["\\whitebox"] = n; |
| ast->attributes.erase("\\lib_whitebox"); |
| } |
| } |
| |
| if (!blackbox_module && ast->attributes.count("\\blackbox")) { |
| AstNode *n = ast->attributes.at("\\blackbox"); |
| if (n->type != AST_CONSTANT) |
| log_file_error(ast->filename, ast->linenum, "Got blackbox attribute with non-constant value!\n"); |
| blackbox_module = n->asBool(); |
| } |
| |
| if (blackbox_module && ast->attributes.count("\\whitebox")) { |
| AstNode *n = ast->attributes.at("\\whitebox"); |
| if (n->type != AST_CONSTANT) |
| log_file_error(ast->filename, ast->linenum, "Got whitebox attribute with non-constant value!\n"); |
| blackbox_module = !n->asBool(); |
| } |
| |
| if (ast->attributes.count("\\noblackbox")) { |
| if (blackbox_module) { |
| AstNode *n = ast->attributes.at("\\noblackbox"); |
| if (n->type != AST_CONSTANT) |
| log_file_error(ast->filename, ast->linenum, "Got noblackbox attribute with non-constant value!\n"); |
| blackbox_module = !n->asBool(); |
| } |
| delete ast->attributes.at("\\noblackbox"); |
| ast->attributes.erase("\\noblackbox"); |
| } |
| |
| if (blackbox_module) |
| { |
| if (ast->attributes.count("\\whitebox")) { |
| delete ast->attributes.at("\\whitebox"); |
| ast->attributes.erase("\\whitebox"); |
| } |
| |
| if (ast->attributes.count("\\lib_whitebox")) { |
| delete ast->attributes.at("\\lib_whitebox"); |
| ast->attributes.erase("\\lib_whitebox"); |
| } |
| |
| std::vector<AstNode*> new_children; |
| for (auto child : ast->children) { |
| if (child->type == AST_WIRE && (child->is_input || child->is_output)) { |
| new_children.push_back(child); |
| } else if (child->type == AST_PARAMETER) { |
| child->delete_children(); |
| child->children.push_back(AstNode::mkconst_int(0, false, 0)); |
| new_children.push_back(child); |
| } else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && |
| (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) { |
| new_children.push_back(child); |
| } else { |
| delete child; |
| } |
| } |
| |
| ast->children.swap(new_children); |
| |
| if (ast->attributes.count("\\blackbox") == 0) { |
| ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); |
| } |
| } |
| |
| ignoreThisSignalsInInitial = RTLIL::SigSpec(); |
| |
| for (auto &attr : ast->attributes) { |
| if (attr.second->type != AST_CONSTANT) |
| log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); |
| current_module->attributes[attr.first] = attr.second->asAttrConst(); |
| } |
| 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->type != AST_INITIAL) |
| node->genRTLIL(); |
| } |
| |
| ignoreThisSignalsInInitial.sort_and_unify(); |
| |
| for (size_t i = 0; i < ast->children.size(); i++) { |
| AstNode *node = ast->children[i]; |
| if (node->type == AST_INITIAL) |
| node->genRTLIL(); |
| } |
| |
| ignoreThisSignalsInInitial = RTLIL::SigSpec(); |
| } |
| else { |
| for (auto &attr : ast->attributes) { |
| if (attr.second->type != AST_CONSTANT) |
| continue; |
| current_module->attributes[attr.first] = attr.second->asAttrConst(); |
| } |
| } |
| |
| if (ast->type == AST_INTERFACE) |
| current_module->set_bool_attribute("\\is_interface"); |
| current_module->ast = ast_before_simplify; |
| current_module->nolatches = flag_nolatches; |
| current_module->nomeminit = flag_nomeminit; |
| current_module->nomem2reg = flag_nomem2reg; |
| current_module->mem2reg = flag_mem2reg; |
| current_module->noblackbox = flag_noblackbox; |
| current_module->lib = flag_lib; |
| current_module->nowb = flag_nowb; |
| current_module->noopt = flag_noopt; |
| current_module->icells = flag_icells; |
| current_module->pwires = flag_pwires; |
| current_module->autowire = flag_autowire; |
| current_module->fixup_ports(); |
| |
| if (flag_dump_rtlil) { |
| log("Dumping generated RTLIL:\n"); |
| log_module(current_module); |
| log("--- END OF RTLIL DUMP ---\n"); |
| } |
| |
| 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_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, |
| bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) |
| { |
| current_ast = ast; |
| flag_dump_ast1 = dump_ast1; |
| flag_dump_ast2 = dump_ast2; |
| flag_no_dump_ptr = no_dump_ptr; |
| flag_dump_vlog1 = dump_vlog1; |
| flag_dump_vlog2 = dump_vlog2; |
| flag_dump_rtlil = dump_rtlil; |
| flag_nolatches = nolatches; |
| flag_nomeminit = nomeminit; |
| flag_nomem2reg = nomem2reg; |
| flag_mem2reg = mem2reg; |
| flag_noblackbox = noblackbox; |
| flag_lib = lib; |
| flag_nowb = nowb; |
| flag_noopt = noopt; |
| flag_icells = icells; |
| flag_pwires = pwires; |
| flag_autowire = autowire; |
| |
| log_assert(current_ast->type == AST_DESIGN); |
| for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) |
| { |
| if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) |
| { |
| for (auto n : design->verilog_globals) |
| (*it)->children.push_back(n->clone()); |
| |
| for (auto n : design->verilog_packages){ |
| for (auto o : n->children) { |
| AstNode *cloned_node = o->clone(); |
| cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); |
| (*it)->children.push_back(cloned_node); |
| } |
| } |
| |
| if (flag_icells && (*it)->str.compare(0, 2, "\\$") == 0) |
| (*it)->str = (*it)->str.substr(1); |
| |
| if (defer) |
| (*it)->str = "$abstract" + (*it)->str; |
| |
| if (design->has((*it)->str)) { |
| RTLIL::Module *existing_mod = design->module((*it)->str); |
| if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) { |
| log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", (*it)->str.c_str()); |
| } else if (nooverwrite) { |
| log("Ignoring re-definition of module `%s' at %s:%d.\n", |
| (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); |
| continue; |
| } else { |
| log("Replacing existing%s module `%s' at %s:%d.\n", |
| existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", |
| (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); |
| design->remove(existing_mod); |
| } |
| } |
| |
| design->add(process_module(*it, defer)); |
| } |
| else if ((*it)->type == AST_PACKAGE) |
| design->verilog_packages.push_back((*it)->clone()); |
| else |
| design->verilog_globals.push_back((*it)->clone()); |
| } |
| } |
| |
| // AstModule destructor |
| AstModule::~AstModule() |
| { |
| if (ast != NULL) |
| delete ast; |
| } |
| |
| |
| // An interface port with modport is specified like this: |
| // <interface_name>.<modport_name> |
| // This function splits the interface_name from the modport_name, and fails if it is not a valid combination |
| std::pair<std::string,std::string> AST::split_modport_from_type(std::string name_type) |
| { |
| std::string interface_type = ""; |
| std::string interface_modport = ""; |
| size_t ndots = std::count(name_type.begin(), name_type.end(), '.'); |
| // Separate the interface instance name from any modports: |
| if (ndots == 0) { // Does not have modport |
| interface_type = name_type; |
| } |
| else { |
| std::stringstream name_type_stream(name_type); |
| std::string segment; |
| std::vector<std::string> seglist; |
| while(std::getline(name_type_stream, segment, '.')) { |
| seglist.push_back(segment); |
| } |
| if (ndots == 1) { // Has modport |
| interface_type = seglist[0]; |
| interface_modport = seglist[1]; |
| } |
| else { // Erroneous port type |
| log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); |
| } |
| } |
| return std::pair<std::string,std::string>(interface_type, interface_modport); |
| |
| } |
| |
| AstNode * AST::find_modport(AstNode *intf, std::string name) |
| { |
| for (auto &ch : intf->children) |
| if (ch->type == AST_MODPORT) |
| if (ch->str == name) // Modport found |
| return ch; |
| return NULL; |
| } |
| |
| // Iterate over all wires in an interface and add them as wires in the AST module: |
| void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport) |
| { |
| for (auto &wire_it : intfmodule->wires_){ |
| AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); |
| std::string origname = log_id(wire_it.first); |
| std::string newname = intfname + "." + origname; |
| wire->str = newname; |
| if (modport != NULL) { |
| bool found_in_modport = false; |
| // Search for the current wire in the wire list for the current modport |
| for (auto &ch : modport->children) { |
| if (ch->type == AST_MODPORTMEMBER) { |
| std::string compare_name = "\\" + origname; |
| if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output |
| found_in_modport = true; |
| wire->is_input = ch->is_input; |
| wire->is_output = ch->is_output; |
| break; |
| } |
| } |
| } |
| if (found_in_modport) { |
| module_ast->children.push_back(wire); |
| } |
| else { // If not found in modport, do not create port |
| delete wire; |
| } |
| } |
| else { // If no modport, set inout |
| wire->is_input = true; |
| wire->is_output = true; |
| module_ast->children.push_back(wire); |
| } |
| } |
| } |
| |
| // When an interface instance is found in a module, the whole RTLIL for the module will be rederived again |
| // from AST. The interface members are copied into the AST module with the prefix of the interface. |
| void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) |
| { |
| loadconfig(); |
| |
| bool is_top = false; |
| AstNode *new_ast = ast->clone(); |
| for (auto &intf : local_interfaces) { |
| std::string intfname = intf.first.str(); |
| RTLIL::Module *intfmodule = intf.second; |
| for (auto &wire_it : intfmodule->wires_){ |
| AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); |
| std::string newname = log_id(wire_it.first); |
| newname = intfname + "." + newname; |
| wire->str = newname; |
| new_ast->children.push_back(wire); |
| } |
| } |
| |
| AstNode *ast_before_replacing_interface_ports = new_ast->clone(); |
| |
| // Explode all interface ports. Note this will only have an effect on 'top |
| // level' modules. Other sub-modules will have their interface ports |
| // exploded via the derive(..) function |
| for (size_t i =0; i<new_ast->children.size(); i++) |
| { |
| AstNode *ch2 = new_ast->children[i]; |
| if (ch2->type == AST_INTERFACEPORT) { // Is an interface port |
| std::string name_port = ch2->str; // Name of the interface port |
| if (ch2->children.size() > 0) { |
| for(size_t j=0; j<ch2->children.size();j++) { |
| AstNode *ch = ch2->children[j]; |
| if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface |
| std::pair<std::string,std::string> res = split_modport_from_type(ch->str); |
| std::string interface_type = res.first; |
| std::string interface_modport = res.second; // Is "", if no modport |
| if (design->modules_.count(interface_type) > 0) { |
| // Add a cell to the module corresponding to the interface port such that |
| // it can further propagated down if needed: |
| AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE); |
| celltype_for_intf->str = interface_type; |
| AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf); |
| cell_for_intf->str = name_port + "_inst_from_top_dummy"; |
| new_ast->children.push_back(cell_for_intf); |
| |
| // Get all members of this non-overridden dummy interface instance: |
| RTLIL::Module *intfmodule = design->modules_[interface_type]; // All interfaces should at this point in time (assuming |
| // reprocess_module is called from the hierarchy pass) be |
| // present in design->modules_ |
| AstModule *ast_module_of_interface = (AstModule*)intfmodule; |
| std::string interface_modport_compare_str = "\\" + interface_modport; |
| AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport |
| // Iterate over all wires in the interface and add them to the module: |
| explode_interface_port(new_ast, intfmodule, name_port, modport); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| // The old module will be deleted. Rename and mark for deletion: |
| std::string original_name = this->name.str(); |
| std::string changed_name = original_name + "_before_replacing_local_interfaces"; |
| design->rename(this, changed_name); |
| this->set_bool_attribute("\\to_delete"); |
| |
| // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the |
| // new module. |
| if (this->get_bool_attribute("\\initial_top")) { |
| this->attributes.erase("\\initial_top"); |
| is_top = true; |
| } |
| |
| // Generate RTLIL from AST for the new module and add to the design: |
| AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports); |
| delete(new_ast); |
| design->add(newmod); |
| RTLIL::Module* mod = design->module(original_name); |
| if (is_top) |
| mod->set_bool_attribute("\\top"); |
| |
| // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. |
| mod->set_bool_attribute("\\interfaces_replaced_in_module"); |
| } |
| |
| // create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces |
| // This method is used to explode the interface when the interface is a port of the module (not instantiated inside) |
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool /*mayfail*/) |
| { |
| AstNode *new_ast = NULL; |
| std::string modname = derive_common(design, parameters, &new_ast); |
| |
| // Since interfaces themselves may be instantiated with different parameters, |
| // "modname" must also take those into account, so that unique modules |
| // are derived for any variant of interface connections: |
| std::string interf_info = ""; |
| |
| bool has_interfaces = false; |
| for(auto &intf : interfaces) { |
| interf_info += log_id(intf.second->name); |
| has_interfaces = true; |
| } |
| |
| std::string new_modname = modname; |
| if (has_interfaces) |
| new_modname += "$interfaces$" + interf_info; |
| |
| |
| if (!design->has(new_modname)) { |
| if (!new_ast) { |
| auto mod = dynamic_cast<AstModule*>(design->module(modname)); |
| new_ast = mod->ast->clone(); |
| } |
| modname = new_modname; |
| new_ast->str = modname; |
| |
| // Iterate over all interfaces which are ports in this module: |
| for(auto &intf : interfaces) { |
| RTLIL::Module * intfmodule = intf.second; |
| std::string intfname = intf.first.str(); |
| // Check if a modport applies for the interface port: |
| AstNode *modport = NULL; |
| if (modports.count(intfname) > 0) { |
| std::string interface_modport = modports.at(intfname).str(); |
| AstModule *ast_module_of_interface = (AstModule*)intfmodule; |
| AstNode *ast_node_of_interface = ast_module_of_interface->ast; |
| modport = find_modport(ast_node_of_interface, interface_modport); |
| } |
| // Iterate over all wires in the interface and add them to the module: |
| explode_interface_port(new_ast, intfmodule, intfname, modport); |
| } |
| |
| design->add(process_module(new_ast, false)); |
| design->module(modname)->check(); |
| |
| RTLIL::Module* mod = design->module(modname); |
| |
| // Now that the interfaces have been exploded, we can delete the dummy port related to every interface. |
| for(auto &intf : interfaces) { |
| if(mod->wires_.count(intf.first)) { |
| mod->wires_.erase(intf.first); |
| mod->fixup_ports(); |
| // We copy the cell of the interface to the sub-module such that it can further be found if it is propagated |
| // down to sub-sub-modules etc. |
| RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name); |
| new_subcell->set_bool_attribute("\\is_interface"); |
| } |
| else { |
| log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str()); |
| } |
| } |
| |
| // If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module': |
| if (interfaces.size() > 0) { |
| mod->set_bool_attribute("\\interfaces_replaced_in_module"); |
| } |
| |
| } else { |
| log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); |
| } |
| |
| delete new_ast; |
| return modname; |
| } |
| |
| // create a new parametric module (when needed) and return the name of the generated module - without support for interfaces |
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/) |
| { |
| AstNode *new_ast = NULL; |
| std::string modname = derive_common(design, parameters, &new_ast); |
| |
| if (!design->has(modname)) { |
| new_ast->str = modname; |
| design->add(process_module(new_ast, false)); |
| design->module(modname)->check(); |
| } else { |
| log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); |
| } |
| |
| delete new_ast; |
| return modname; |
| } |
| |
| // create a new parametric module (when needed) and return the name of the generated module |
| std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out) |
| { |
| std::string stripped_name = name.str(); |
| |
| if (stripped_name.compare(0, 9, "$abstract") == 0) |
| stripped_name = stripped_name.substr(9); |
| |
| std::string para_info; |
| |
| int para_counter = 0; |
| for (const auto child : ast->children) { |
| if (child->type != AST_PARAMETER) |
| continue; |
| para_counter++; |
| std::string para_id = child->str; |
| if (parameters.count(para_id) > 0) { |
| log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); |
| para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); |
| continue; |
| } |
| para_id = stringf("$%d", para_counter); |
| if (parameters.count(para_id) > 0) { |
| log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); |
| para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); |
| continue; |
| } |
| } |
| |
| std::string modname; |
| if (parameters.size() == 0) |
| modname = stripped_name; |
| else if (para_info.size() > 60) |
| modname = "$paramod$" + sha1(para_info) + stripped_name; |
| else |
| modname = "$paramod" + stripped_name + para_info; |
| |
| if (design->has(modname)) |
| return modname; |
| |
| log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); |
| loadconfig(); |
| |
| AstNode *new_ast = ast->clone(); |
| para_counter = 0; |
| for (auto child : new_ast->children) { |
| if (child->type != AST_PARAMETER) |
| continue; |
| para_counter++; |
| std::string para_id = child->str; |
| if (parameters.count(para_id) > 0) { |
| log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); |
| goto rewrite_parameter; |
| } |
| para_id = stringf("$%d", para_counter); |
| if (parameters.count(para_id) > 0) { |
| log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); |
| goto rewrite_parameter; |
| } |
| continue; |
| rewrite_parameter: |
| delete child->children.at(0); |
| if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) { |
| child->children[0] = new AstNode(AST_REALVALUE); |
| child->children[0]->realvalue = std::stod(parameters[para_id].decode_string()); |
| } else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0) |
| child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string()); |
| else |
| child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); |
| parameters.erase(para_id); |
| } |
| |
| for (auto param : parameters) { |
| AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER)); |
| defparam->children[0]->str = param.first.str(); |
| if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0) |
| defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string())); |
| else |
| defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); |
| new_ast->children.push_back(defparam); |
| } |
| |
| (*new_ast_out) = new_ast; |
| return modname; |
| } |
| |
| RTLIL::Module *AstModule::clone() const |
| { |
| AstModule *new_mod = new AstModule; |
| new_mod->name = name; |
| cloneInto(new_mod); |
| |
| new_mod->ast = ast->clone(); |
| new_mod->nolatches = nolatches; |
| new_mod->nomeminit = nomeminit; |
| new_mod->nomem2reg = nomem2reg; |
| new_mod->mem2reg = mem2reg; |
| new_mod->noblackbox = noblackbox; |
| new_mod->lib = lib; |
| new_mod->nowb = nowb; |
| new_mod->noopt = noopt; |
| new_mod->icells = icells; |
| new_mod->pwires = pwires; |
| new_mod->autowire = autowire; |
| |
| return new_mod; |
| } |
| |
| void AstModule::loadconfig() const |
| { |
| current_ast = NULL; |
| flag_dump_ast1 = false; |
| flag_dump_ast2 = false; |
| flag_dump_vlog1 = false; |
| flag_dump_vlog2 = false; |
| flag_nolatches = nolatches; |
| flag_nomeminit = nomeminit; |
| flag_nomem2reg = nomem2reg; |
| flag_mem2reg = mem2reg; |
| flag_noblackbox = noblackbox; |
| flag_lib = lib; |
| flag_nowb = nowb; |
| flag_noopt = noopt; |
| flag_icells = icells; |
| flag_pwires = pwires; |
| flag_autowire = autowire; |
| use_internal_line_num(); |
| } |
| |
| // 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; |
| } |
| |
| YOSYS_NAMESPACE_END |