diff options
Diffstat (limited to 'frontends/ast')
-rw-r--r-- | frontends/ast/ast.cc | 66 | ||||
-rw-r--r-- | frontends/ast/ast.h | 27 | ||||
-rw-r--r-- | frontends/ast/dpicall.cc | 4 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 167 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 496 |
5 files changed, 563 insertions, 197 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 68b3327f9..57de725d8 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -2,11 +2,11 @@ * 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 @@ -30,15 +30,6 @@ #include "libs/sha1/sha1.h" #include "ast.h" -#include <sstream> -#include <stdarg.h> - -#if defined(__APPLE__) -# include <cmath> -#else -# include <math.h> -#endif - YOSYS_NAMESPACE_BEGIN using namespace AST; @@ -53,12 +44,12 @@ namespace AST { // instanciate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, 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_top_block, *current_block, *current_block_child; + AstNode *current_always, *current_top_block, *current_block, *current_block_child; AstModule *current_module; } @@ -90,6 +81,7 @@ std::string AST::type2str(AstNodeType type) X(AST_IDENTIFIER) X(AST_PREFIX) X(AST_ASSERT) + X(AST_ASSUME) X(AST_FCALL) X(AST_TO_BITS) X(AST_TO_SIGNED) @@ -132,6 +124,7 @@ std::string AST::type2str(AstNodeType type) X(AST_TERNARY) X(AST_MEMRD) X(AST_MEMWR) + X(AST_MEMINIT) X(AST_TCALL) X(AST_ASSIGN) X(AST_CELL) @@ -144,6 +137,8 @@ std::string AST::type2str(AstNodeType type) 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) @@ -156,6 +151,7 @@ std::string AST::type2str(AstNodeType type) X(AST_POSEDGE) X(AST_NEGEDGE) X(AST_EDGE) + X(AST_PACKAGE) #undef X default: log_abort(); @@ -327,7 +323,7 @@ static std::string id2vl(std::string txt) return txt; } -// dump AST node as verilog pseudo-code +// dump AST node as Verilog pseudo-code void AstNode::dumpVlog(FILE *f, std::string indent) { bool first = true; @@ -499,7 +495,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) break; case AST_CASE: - fprintf(f, "%s" "case (", indent.c_str()); + 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++) { @@ -510,6 +511,8 @@ void AstNode::dumpVlog(FILE *f, std::string indent) break; case AST_COND: + case AST_CONDX: + case AST_CONDZ: for (auto child : children) { if (child->type == AST_BLOCK) { fprintf(f, ":\n"); @@ -553,7 +556,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) 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 = "|"; } @@ -697,7 +700,7 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe 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) + else if (is_signed && !node->bits.empty()) node->integer |= (node->bits.back() == RTLIL::S1) << i; } node->range_valid = true; @@ -818,7 +821,7 @@ uint64_t AstNode::asInt(bool is_signed) } if (type == AST_REALVALUE) - return realvalue; + return uint64_t(realvalue); log_abort(); } @@ -829,7 +832,7 @@ double AstNode::asReal(bool is_signed) { RTLIL::Const val(bits); - bool is_negative = is_signed && val.bits.back() == RTLIL::State::S1; + 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()); @@ -892,7 +895,7 @@ static AstModule* process_module(AstNode *ast, bool defer) AstNode *ast_before_simplify = ast->clone(); if (flag_dump_ast1) { - log("Dumping verilog AST before simplification:\n"); + log("Dumping Verilog AST before simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } @@ -902,13 +905,13 @@ static AstModule* process_module(AstNode *ast, bool defer) while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } if (flag_dump_ast2) { - log("Dumping verilog AST after simplification:\n"); + log("Dumping Verilog AST after simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } if (flag_dump_vlog) { - log("Dumping verilog AST (as requested by dump_vlog option):\n"); + log("Dumping Verilog AST (as requested by dump_vlog option):\n"); ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } @@ -957,6 +960,7 @@ static AstModule* process_module(AstNode *ast, bool defer) 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->lib = flag_lib; @@ -968,13 +972,14 @@ static AstModule* process_module(AstNode *ast, bool defer) } // 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 dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; flag_dump_vlog = dump_vlog; flag_nolatches = nolatches; + flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; @@ -992,6 +997,14 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump for (auto n : global_decls) (*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.substr(0, 2) == "\\$") (*it)->str = (*it)->str.substr(1); @@ -1009,6 +1022,9 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump design->add(process_module(*it, defer)); } + else if ((*it)->type == AST_PACKAGE){ + design->verilog_packages.push_back((*it)->clone()); + } else global_decls.push_back(*it); } @@ -1029,13 +1045,14 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R if (stripped_name.substr(0, 9) == "$abstract") stripped_name = stripped_name.substr(9); - log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); + log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); current_ast = NULL; flag_dump_ast1 = false; flag_dump_ast2 = false; flag_dump_vlog = false; flag_nolatches = nolatches; + flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; @@ -1102,6 +1119,7 @@ RTLIL::Module *AstModule::clone() const new_mod->ast = ast->clone(); new_mod->nolatches = nolatches; + new_mod->nomeminit = nomeminit; new_mod->nomem2reg = nomem2reg; new_mod->mem2reg = mem2reg; new_mod->lib = lib; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 180646267..3dcd32bd4 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -2,11 +2,11 @@ * 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 @@ -64,6 +64,7 @@ namespace AST AST_IDENTIFIER, AST_PREFIX, AST_ASSERT, + AST_ASSUME, AST_FCALL, AST_TO_BITS, @@ -107,6 +108,7 @@ namespace AST AST_TERNARY, AST_MEMRD, AST_MEMWR, + AST_MEMINIT, AST_TCALL, AST_ASSIGN, @@ -120,6 +122,8 @@ namespace AST AST_ASSIGN_LE, AST_CASE, AST_COND, + AST_CONDX, + AST_CONDZ, AST_DEFAULT, AST_FOR, AST_WHILE, @@ -133,7 +137,9 @@ namespace AST AST_POSEDGE, AST_NEGEDGE, - AST_EDGE + AST_EDGE, + + AST_PACKAGE }; // convert an node type to a string (e.g. for debug output) @@ -208,13 +214,14 @@ namespace AST // 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, int width_hint, bool sign_hint, bool in_param); - AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr); + AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags); - void mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); + bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); bool mem2reg_check(pool<AstNode*> &mem2reg_set); + void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes); void meminfo(int &mem_width, int &mem_size, int &addr_bits); // additional functionality for evaluating constant functions @@ -264,13 +271,13 @@ namespace AST }; // 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_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); // parametric modules are supported directly by the AST library - // therfore we need our own derivate of RTLIL::Module with overloaded virtual functions + // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomem2reg, mem2reg, lib, noopt, icells, autowire; + bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; virtual ~AstModule(); virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters); virtual RTLIL::Module *clone() const; @@ -294,12 +301,12 @@ namespace AST namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map<std::string, AST::AstNode*> current_scope; extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; extern RTLIL::SigSpec ignoreThisSignalsInInitial; - extern AST::AstNode *current_top_block, *current_block, *current_block_child; + extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child; extern AST::AstModule *current_module; struct ProcessGenerator; } diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e566d653d..e241142d3 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -2,11 +2,11 @@ * 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 diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 71248663e..3e359170b 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -2,11 +2,11 @@ * 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 @@ -176,13 +176,13 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::Process *proc; RTLIL::SigSpec outputSignals; - // This always points to the RTLIL::CaseRule beeing filled at the moment + // This always points to the RTLIL::CaseRule being filled at the moment RTLIL::CaseRule *current_case; // This map contains 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 value assigned in the first assignment. So when the first assignment is processed // the according information is appended to subst_rvalue_from and subst_rvalue_to. stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_rvalue_map; @@ -192,7 +192,7 @@ struct AST_INTERNAL::ProcessGenerator // signal that is used as input for the register that drives the signal foo. stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_lvalue_map; - // The code here generates a number of temprorary signal for each output register. This + // The code here generates a number of temporary 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; @@ -338,12 +338,14 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: for (auto child : ast->children) if (child != ast->children[0]) { - log_assert(child->type == AST_COND); + log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); collect_lvalues(reg, child, type_eq, type_le, false); } break; case AST_COND: + case AST_CONDX: + case AST_CONDZ: case AST_ALWAYS: case AST_INITIAL: for (auto child : ast->children) @@ -379,7 +381,7 @@ struct AST_INTERNAL::ProcessGenerator // e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this // function is called to clean up the first two assignments as they are overwritten by // the third assignment. - void removeSignalFromCaseTree(const std::set<RTLIL::SigBit> &pattern, RTLIL::CaseRule *cs) + void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) it->first.remove2(pattern, &it->second); @@ -427,6 +429,17 @@ struct AST_INTERNAL::ProcessGenerator { RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap()); + + pool<SigBit> lvalue_sigbits; + for (int i = 0; i < GetSize(lvalue); i++) { + if (lvalue_sigbits.count(lvalue[i]) > 0) { + unmapped_lvalue.remove(i); + lvalue.remove(i); + rvalue.remove(i--); + } else + lvalue_sigbits.insert(lvalue[i]); + } + lvalue.replace(subst_lvalue_map.stdmap()); if (ast->type == AST_ASSIGN_EQ) { @@ -434,7 +447,7 @@ struct AST_INTERNAL::ProcessGenerator subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]); } - removeSignalFromCaseTree(lvalue.to_sigbit_set(), current_case); + removeSignalFromCaseTree(lvalue, current_case); remove_unwanted_lvalue_bits(lvalue, rvalue); current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue)); } @@ -467,7 +480,7 @@ struct AST_INTERNAL::ProcessGenerator { if (child == ast->children[0]) continue; - log_assert(child->type == AST_COND); + log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); subst_lvalue_map.save(); subst_rvalue_map.save(); @@ -511,7 +524,7 @@ struct AST_INTERNAL::ProcessGenerator subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); - removeSignalFromCaseTree(this_case_eq_lvalue.to_sigbit_set(), current_case); + removeSignalFromCaseTree(this_case_eq_lvalue, current_case); addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp); } break; @@ -520,6 +533,11 @@ struct AST_INTERNAL::ProcessGenerator log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); break; + case AST_PARAMETER: + case AST_LOCALPARAM: + log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); + break; + case AST_TCALL: case AST_FOR: break; @@ -547,14 +565,14 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun switch (type) { case AST_CONSTANT: - width_hint = std::max(width_hint, int(bits.size())); + width_hint = max(width_hint, int(bits.size())); if (!is_signed) sign_hint = false; break; case AST_REALVALUE: *found_real = true; - width_hint = std::max(width_hint, 32); + width_hint = max(width_hint, 32); break; case AST_IDENTIFIER: @@ -567,9 +585,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; } else - if (id_ast->children[0]->type == AST_CONSTANT) { + if (id_ast->children[0]->type != AST_CONSTANT) + while (id_ast->simplify(true, false, false, 1, -1, false, true)) { } + if (id_ast->children[0]->type == AST_CONSTANT) this_width = id_ast->children[0]->bits.size(); - } else + else log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); if (children.size() != 0) range = children[0]; @@ -582,7 +602,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun // log("---\n"); // id_ast->dumpAst(NULL, "decl> "); // dumpAst(NULL, "ref> "); - log_error("Failed to detect with of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_error("Failed to detect width of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); } } else { this_width = id_ast->range_left - id_ast->range_right + 1; @@ -593,7 +613,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = 32; } else if (id_ast->type == AST_MEMORY) { if (!id_ast->children[0]->range_valid) - log_error("Failed to detect with of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; } else log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); @@ -615,7 +635,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = range->range_left - range->range_right + 1; sign_hint = false; } - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); if (!id_ast->is_signed) sign_hint = false; break; @@ -625,7 +645,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children[0]->type != AST_CONSTANT) log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); - width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int()); + width_hint = max(width_hint, children[0]->bitsAsConst().as_int()); break; case AST_TO_SIGNED: @@ -644,7 +664,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); this_width += sub_width_hint; } - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); sign_hint = false; break; @@ -653,7 +673,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children[0]->type != AST_CONSTANT) log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); - width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); + width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); sign_hint = false; break; @@ -676,7 +696,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_REDUCE_XOR: case AST_REDUCE_XNOR: case AST_REDUCE_BOOL: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -696,7 +716,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_NEX: case AST_GE: case AST_GT: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -712,7 +732,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_LOGIC_AND: case AST_LOGIC_OR: case AST_LOGIC_NOT: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -725,9 +745,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id2ast->is_signed) sign_hint = false; if (!id2ast->children[0]->range_valid) - log_error("Failed to detect with of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); break; // everything should have been handled above -> print error if not. @@ -764,7 +784,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_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. + // be instantiated for this type of AST node. std::string type_name; current_filename = filename; @@ -773,7 +793,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) switch (type) { // simply ignore this nodes. - // they are eighter leftovers from simplify() or are referenced by other nodes + // they are either leftovers from simplify() or are referenced by other nodes // and are only accessed here thru this references case AST_TASK: case AST_FUNCTION: @@ -786,6 +806,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENBLOCK: case AST_GENIF: case AST_GENCASE: + case AST_PACKAGE: break; // remember the parameter, needed for example in techmap @@ -1052,7 +1073,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); - int width = std::max(left.size(), right.size()); + int width = max(left.size(), right.size()); if (width_hint > 0) width = width_hint; is_signed = children[0]->is_signed && children[1]->is_signed; @@ -1066,16 +1087,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_REDUCE_XNOR: type_name = "$reduce_xnor"; } { RTLIL::SigSpec arg = children[0]->genRTLIL(); - RTLIL::SigSpec sig = uniop2rtlil(this, type_name, std::max(width_hint, 1), arg); + RTLIL::SigSpec sig = uniop2rtlil(this, type_name, max(width_hint, 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) + // (this is actually just an $reduce_or, but for clarity 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.size() > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg; + RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, max(width_hint, 1), arg) : arg; return sig; } @@ -1121,7 +1142,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_GE: type_name = "$ge"; } if (0) { case AST_GT: type_name = "$gt"; } { - int width = std::max(width_hint, 1); + int width = max(width_hint, 1); width_hint = -1, sign_hint = true; children[0]->detectSignWidthWorker(width_hint, sign_hint); children[1]->detectSignWidthWorker(width_hint, sign_hint); @@ -1143,7 +1164,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); #if 0 - int width = std::max(left.size(), right.size()); + int width = max(left.size(), right.size()); if (width > width_hint && width_hint > 0) width = width_hint; if (width < width_hint) { @@ -1152,10 +1173,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed)) width = width_hint; if (type == AST_MUL) - width = std::min(left.size() + right.size(), width_hint); + width = min(left.size() + right.size(), width_hint); } #else - int width = std::max(std::max(left.size(), right.size()), width_hint); + int width = max(max(left.size(), right.size()), width_hint); #endif is_signed = children[0]->is_signed && children[1]->is_signed; return binop2rtlil(this, type_name, width, left, right); @@ -1167,14 +1188,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); - return binop2rtlil(this, type_name, std::max(width_hint, 1), left, right); + return binop2rtlil(this, type_name, max(width_hint, 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", std::max(width_hint, 1), arg); + return uniop2rtlil(this, "$logic_not", max(width_hint, 1), arg); } // generate multiplexer for ternary operator (aka ?:-operator) @@ -1190,7 +1211,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (cond.size() > 1) cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false); - int width = std::max(val1.size(), val2.size()); + int width = max(val1.size(), val2.size()); is_signed = children[1]->is_signed && children[2]->is_signed; widthExtend(this, val1, width, is_signed); widthExtend(this, val2, width, is_signed); @@ -1214,11 +1235,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - int addr_bits = 1; - while ((1 << addr_bits) < current_module->memories[str]->size) - addr_bits++; + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\EN", RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); cell->setPort("\\DATA", RTLIL::SigSpec(wire)); @@ -1235,28 +1256,38 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $memwr cells for memory write ports case AST_MEMWR: + case AST_MEMINIT: { std::stringstream sstr; - sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memwr"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? "$memwr" : "$meminit"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - int addr_bits = 1; - while ((1 << addr_bits) < current_module->memories[str]->size) - addr_bits++; + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); + + int num_words = 1; + if (type == AST_MEMINIT) { + if (children[2]->type != AST_CONSTANT) + log_error("Memory init with non-constant word count at %s:%d!\n", filename.c_str(), linenum); + num_words = int(children[2]->asInt(false)); + cell->parameters["\\WORDS"] = RTLIL::Const(num_words); + } - cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); - cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width)); - cell->setPort("\\EN", children[2]->genRTLIL()); + cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words)); 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); + if (type == AST_MEMWR) { + cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\EN", children[2]->genRTLIL()); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + } cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1); } @@ -1264,19 +1295,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $assert cells case AST_ASSERT: + case AST_ASSUME: { log_assert(children.size() == 2); RTLIL::SigSpec check = children[0]->genRTLIL(); - log_assert(check.size() == 1); + if (GetSize(check) != 1) + check = current_module->ReduceBool(NEW_ID, check); RTLIL::SigSpec en = children[1]->genRTLIL(); - log_assert(en.size() == 1); + if (GetSize(en) != 1) + en = current_module->ReduceBool(NEW_ID, en); std::stringstream sstr; - sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << (type == AST_ASSERT ? "$assert$" : "$assume$") << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$assert"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_ASSERT ? "$assert" : "$assume"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); for (auto &attr : attributes) { @@ -1335,16 +1369,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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] = child->children[0]->asParaConst(); - } else { - cell->parameters[child->str] = child->children[0]->asParaConst(); + IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; + if (child->children[0]->type == AST_REALVALUE) { + log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n", + log_id(cell), log_id(paraname), child->children[0]->realvalue, + filename.c_str(), linenum); + auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); + strnode->cloneInto(child->children[0]); + delete strnode; } + if (child->children[0]->type != AST_CONSTANT) + log_error("Parameter %s.%s with non-constant value at %s:%d!\n", + log_id(cell), log_id(paraname), filename.c_str(), linenum); + cell->parameters[paraname] = child->children[0]->asParaConst(); continue; } if (child->type == AST_ARGUMENT) { @@ -1398,7 +1435,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } // 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) +// signals must be substituted before being 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, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr) { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index e9750eba6..c09b912c2 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -2,11 +2,11 @@ * 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 @@ -41,7 +41,7 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// convert the AST into a simpler AST that has all parameters subsitited by their +// convert the AST into a simpler AST that has all parameters substituted 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(). // @@ -49,15 +49,24 @@ using namespace AST_INTERNAL; // 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, int width_hint, bool sign_hint, bool in_param) { + static int recursion_counter = 0; + static pair<string, int> last_blocking_assignment_warn; + static bool deep_recursion_warning = false; + + if (recursion_counter++ == 1000 && deep_recursion_warning) { + log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n"); + deep_recursion_warning = false; + } + AstNode *newNode = NULL; bool did_something = false; - static pair<string, int> last_blocking_assignment_warn; #if 0 log("-------------\n"); + log("AST simplify[%d] depth %d at %s:%d,\n", stage, recursion_counter, filename.c_str(), linenum); log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); - dumpAst(NULL, "> "); + // dumpAst(NULL, "> "); #endif if (stage == 0) @@ -65,6 +74,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(type == AST_MODULE); last_blocking_assignment_warn = pair<string, int>(); + deep_recursion_warning = true; while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg")) @@ -79,11 +89,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { AstNode *mem = it.first; uint32_t memflags = it.second; + bool this_nomeminit = flag_nomeminit; log_assert((memflags & ~0x00ffff00) == 0); if (mem->get_bool_attribute("\\nomem2reg")) continue; + if (mem->get_bool_attribute("\\nomeminit") || get_bool_attribute("\\nomeminit")) + this_nomeminit = true; + if (memflags & AstNode::MEM2REG_FL_FORCED) goto silent_activate; @@ -93,7 +107,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (memflags & AstNode::MEM2REG_FL_SET_ASYNC) goto verbose_activate; - if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE)) + if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit) goto verbose_activate; if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) @@ -134,17 +148,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } - mem2reg_as_needed_pass2(mem2reg_set, this, NULL); + while (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--)); - } - } + vector<AstNode*> delnodes; + mem2reg_remove(mem2reg_set, delnodes); + + for (auto node : delnodes) + delete node; } while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { } + recursion_counter--; return false; } @@ -152,18 +166,144 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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) + // (but as soon as a task or function is instantiated we process the generated AST as usual) + if (type == AST_FUNCTION || type == AST_TASK) { + recursion_counter--; return false; + } - // deactivate all calls to non-synthesis system taks - if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$finish" || + // deactivate all calls to non-synthesis system tasks + // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list + if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" || str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) { log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum); delete_children(); str = std::string(); } + if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) { + log_warning("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + delete_children(); + str = std::string(); + } + + // print messages if this a call to $display() or $write() + // This code implements only a small subset of Verilog-2005 $display() format specifiers, + // but should be good enough for most uses + if ((type == AST_TCALL) && ((str == "$display") || (str == "$write"))) + { + int nargs = GetSize(children); + if (nargs < 1) + log_error("System task `%s' got %d arguments, expected >= 1 at %s:%d.\n", + str.c_str(), int(children.size()), filename.c_str(), linenum); + + // First argument is the format string + AstNode *node_string = children[0]; + while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_string->type != AST_CONSTANT) + log_error("Failed to evaluate system task `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + std::string sformat = node_string->bitsAsConst().decode_string(); + + // Other arguments are placeholders. Process the string as we go through it + std::string sout; + int next_arg = 1; + for (size_t i = 0; i < sformat.length(); i++) + { + // format specifier + if (sformat[i] == '%') + { + // If there's no next character, that's a problem + if (i+1 >= sformat.length()) + log_error("System task `%s' called with `%%' at end of string at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + char cformat = sformat[++i]; + + // %% is special, does not need a matching argument + if (cformat == '%') + { + sout += '%'; + continue; + } + + // Simplify the argument + AstNode *node_arg = nullptr; + + // Everything from here on depends on the format specifier + switch (cformat) + { + case 's': + case 'S': + case 'd': + case 'D': + case 'x': + case 'X': + if (next_arg >= GetSize(children)) + log_error("Missing argument for %%%c format specifier in system task `%s' at %s:%d.\n", + cformat, str.c_str(), filename.c_str(), linenum); + + node_arg = children[next_arg++]; + while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_arg->type != AST_CONSTANT) + log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + break; + + case 'm': + case 'M': + break; + + default: + log_error("System task `%s' called with invalid/unsupported format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + break; + } + + switch (cformat) + { + case 's': + case 'S': + sout += node_arg->bitsAsConst().decode_string(); + break; + + case 'd': + case 'D': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'x': + case 'X': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'm': + case 'M': + sout += log_id(current_module->name); + break; + + default: + log_abort(); + } + } + + // not a format specifier + else + sout += sformat[i]; + } + + // Finally, print the message (only include a \n for $display, not for $write) + log("%s", sout.c_str()); + if (str == "$display") + log("\n"); + 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_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX) const_fold = true; @@ -255,6 +395,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, auto backup_current_block = current_block; auto backup_current_block_child = current_block_child; auto backup_current_top_block = current_top_block; + auto backup_current_always = current_always; + + if (type == AST_ALWAYS || type == AST_INITIAL) + current_always = this; int backup_width_hint = width_hint; bool backup_sign_hint = sign_hint; @@ -277,7 +421,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); children[1]->detectSignWidth(width_hint, sign_hint); - width_hint = std::max(width_hint, backup_width_hint); + width_hint = max(width_hint, backup_width_hint); child_0_is_self_determined = true; break; @@ -291,7 +435,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; if (!children[1]->range_valid) log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); - width_hint = std::max(width_hint, children[1]->range_left - children[1]->range_right + 1); + width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; @@ -362,7 +506,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, detect_width_simple = true; child_0_is_self_determined = true; break; - + case AST_MEMRD: detect_width_simple = true; children_are_self_determined = true; @@ -395,6 +539,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (type == AST_CONDX && children.size() > 0 && children.at(0)->type == AST_CONSTANT) { + for (auto &bit : children.at(0)->bits) + if (bit == State::Sz || bit == State::Sx) + bit = State::Sa; + } + + if (type == AST_CONDZ && children.size() > 0 && children.at(0)->type == AST_CONSTANT) { + for (auto &bit : children.at(0)->bits) + if (bit == State::Sz) + bit = State::Sa; + } + if (const_fold && type == AST_CASE) { while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } @@ -403,7 +559,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, new_children.push_back(children[0]); for (int i = 1; i < GetSize(children); i++) { AstNode *child = children[i]; - log_assert(child->type == AST_COND); + log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); for (auto v : child->children) { if (v->type == AST_DEFAULT) goto keep_const_cond; @@ -494,6 +650,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_block = backup_current_block; current_block_child = backup_current_block_child; current_top_block = backup_current_top_block; + current_always = backup_current_always; for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { if (it->second == NULL) @@ -530,6 +687,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // dumpAst(NULL, "> "); log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum); } + if (children[1]->type == AST_PREFIX) + children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param); log_assert(children[1]->type == AST_IDENTIFIER); newNode = children[1]->clone(); const char *second_part = children[1]->str.c_str(); @@ -609,8 +768,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (auto range : children[1]->children) { if (!range->range_valid) log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum); - multirange_dimensions.push_back(std::min(range->range_left, range->range_right)); - multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1); + multirange_dimensions.push_back(min(range->range_left, range->range_right)); + multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); total_size *= multirange_dimensions.back(); } delete children[1]; @@ -636,10 +795,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (i == 0) index_expr = new_index_expr; else - index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr); + index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1], true)), new_index_expr); } - for (int i = GetSize(id2ast->multirange_dimensions)/1; i < GetSize(children[0]->children); i++) + for (int i = GetSize(id2ast->multirange_dimensions)/2; i < GetSize(children[0]->children); i++) children.push_back(children[0]->children[i]->clone()); delete children[0]; @@ -656,7 +815,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (children.size() > 1 && children[1]->type == AST_RANGE) { if (!children[1]->range_valid) log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); - int width = children[1]->range_left - children[1]->range_right + 1; + int width = std::abs(children[1]->range_left - children[1]->range_right) + 1; if (children[0]->type == AST_REALVALUE) { RTLIL::Const constvalue = children[0]->realAsConst(width); log_warning("converting real value %e to binary %s at %s:%d.\n", @@ -670,7 +829,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, RTLIL::SigSpec sig(children[0]->bits); sig.extend_u0(width, children[0]->is_signed); AstNode *old_child_0 = children[0]; - children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed); + children[0] = mkconst_bits(sig.as_const().bits, is_signed); delete old_child_0; } children[0]->is_signed = is_signed; @@ -803,7 +962,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); - while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (varbuf->simplify(true, false, false, stage, 32, true, false)) { } 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); @@ -866,7 +1025,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 3rd expression buf = next_ast->children[1]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, false, false, stage, 32, true, false)) { } 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); @@ -889,7 +1048,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) - if (children[i]->type == AST_WIRE) { + if (children[i]->type == AST_WIRE || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) { children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(children[i]); current_scope[children[i]->str] = children[i]; @@ -977,7 +1136,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *selected_case = NULL; for (size_t i = 1; i < children.size(); i++) { - log_assert(children.at(i)->type == AST_COND); + log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ); AstNode *this_genblock = NULL; for (auto child : children.at(i)->children) { @@ -1045,7 +1204,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum); newNode = new AstNode(AST_GENBLOCK); - int num = std::max(children.at(0)->range_left, children.at(0)->range_right) - std::min(children.at(0)->range_left, children.at(0)->range_right) + 1; + int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1; for (int i = 0; i < num; i++) { int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; @@ -1063,7 +1222,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, goto apply_newNode; } - // replace primitives with assignmens + // replace primitives with assignments if (type == AST_PRIMITIVE) { if (children.size() < 2) @@ -1189,7 +1348,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } skip_dynamic_range_lvalue_expansion:; - if (stage > 1 && type == AST_ASSERT && current_block != NULL) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL) { std::stringstream sstr; sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); @@ -1233,7 +1392,7 @@ skip_dynamic_range_lvalue_expansion:; newNode->children.push_back(assign_check); newNode->children.push_back(assign_en); - AstNode *assertnode = new AstNode(AST_ASSERT); + AstNode *assertnode = new AstNode(type); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children[0]->str = id_check; @@ -1244,16 +1403,15 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (stage > 1 && type == AST_ASSERT && children.size() == 1) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1) { - children[0] = new AstNode(AST_REDUCE_BOOL, children[0]->clone()); children.push_back(mkconst_int(1, false, 1)); did_something = true; } // 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) { + children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) { newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone()); newNode->str = str; newNode->id2ast = id2ast; @@ -1293,11 +1451,14 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_data->str] = wire_data; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_en->str = id_en; - current_ast_mod->children.push_back(wire_en); - current_scope[wire_en->str] = wire_en; - while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } + AstNode *wire_en = nullptr; + if (current_always->type != AST_INITIAL) { + wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_en->str = id_en; + current_ast_mod->children.push_back(wire_en); + current_scope[wire_en->str] = wire_en; + while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } + } std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en; for (int i = 0; i < addr_bits; i++) @@ -1313,13 +1474,17 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); assign_data->children[0]->str = id_data; - AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); - assign_en->children[0]->str = id_en; + AstNode *assign_en = nullptr; + if (current_always->type != AST_INITIAL) { + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + 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); + if (current_always->type != AST_INITIAL) + 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()); @@ -1334,15 +1499,16 @@ skip_dynamic_range_lvalue_expansion:; std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx); - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone())); assign_data->children[0]->str = id_data; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } } else { @@ -1357,16 +1523,17 @@ skip_dynamic_range_lvalue_expansion:; 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; - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); assign_data->children[0]->str = id_data; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), - new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), + new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); + assign_en->children[0]->str = id_en; + } delete left_at_zero_ast; delete right_at_zero_ast; @@ -1378,23 +1545,31 @@ skip_dynamic_range_lvalue_expansion:; 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_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + 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); + if (current_always->type != AST_INITIAL) + newNode->children.push_back(assign_en); - AstNode *wrnode = new AstNode(AST_MEMWR); - wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR); wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + if (current_always->type != AST_INITIAL) + wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + else + wrnode->children.push_back(AstNode::mkconst_int(1, false)); wrnode->str = children[0]->str; + wrnode->id2ast = children[0]->id2ast; wrnode->children[0]->str = id_addr; wrnode->children[1]->str = id_data; - wrnode->children[2]->str = id_en; + if (current_always->type != AST_INITIAL) + wrnode->children[2]->str = id_en; current_ast_mod->children.push_back(wrnode); goto apply_newNode; @@ -1531,7 +1706,17 @@ skip_dynamic_range_lvalue_expansion:; 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 (type == AST_TCALL) + { + if (str == "$finish" || str == "$stop") + { + if (!current_always || current_always->type != AST_INITIAL) + log_error("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + log_error("System task `%s' executed at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + } + if (str == "\\$readmemh" || str == "\\$readmemb") { if (GetSize(children) < 2 || GetSize(children) > 4) @@ -1555,7 +1740,7 @@ skip_dynamic_range_lvalue_expansion:; while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (node_addr->type != AST_CONSTANT) log_error("Failed to evaluate system function `%s' with non-constant 3rd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); - start_addr = node_addr->asInt(false); + start_addr = int(node_addr->asInt(false)); } if (GetSize(children) > 3) { @@ -1563,10 +1748,27 @@ skip_dynamic_range_lvalue_expansion:; while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (node_addr->type != AST_CONSTANT) log_error("Failed to evaluate system function `%s' with non-constant 4th argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); - finish_addr = node_addr->asInt(false); + finish_addr = int(node_addr->asInt(false)); } - newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr); + bool unconditional_init = false; + if (current_always->type == AST_INITIAL) { + pool<AstNode*> queue; + log_assert(current_always->children[0]->type == AST_BLOCK); + queue.insert(current_always->children[0]); + while (!unconditional_init && !queue.empty()) { + pool<AstNode*> next_queue; + for (auto n : queue) + for (auto c : n->children) { + if (c == this) + unconditional_init = true; + next_queue.insert(c); + } + next_queue.swap(queue); + } + } + + newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); goto apply_newNode; } @@ -1606,6 +1808,8 @@ skip_dynamic_range_lvalue_expansion:; size_t arg_count = 0; std::map<std::string, std::string> replace_rules; + vector<AstNode*> added_mod_children; + dict<std::string, AstNode*> wire_cache; if (current_block == NULL) { @@ -1698,17 +1902,41 @@ skip_dynamic_range_lvalue_expansion:; } for (auto child : decl->children) - if (child->type == AST_WIRE) + if (child->type == AST_WIRE || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) { - 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); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + AstNode *wire = nullptr; + + if (wire_cache.count(child->str)) + { + wire = wire_cache.at(child->str); + if (wire->children.empty()) { + for (auto c : child->children) + wire->children.push_back(c->clone()); + } else { + if (!child->children.empty()) + log_error("Incompatible re-declaration of wire %s at %s:%d.\n", child->str.c_str(), filename.c_str(), linenum); + } + } + else + { + wire = child->clone(); + wire->str = prefix + wire->str; + wire->port_id = 0; + wire->is_input = false; + wire->is_output = false; + if (!child->is_output) + wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); + wire_cache[child->str] = wire; + + current_ast_mod->children.push_back(wire); + added_mod_children.push_back(wire); + } + + if (child->type == AST_WIRE) + while (wire->simplify(true, false, false, 1, -1, false, false)) { } replace_rules[child->str] = wire->str; + current_scope[wire->str] = wire; if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -1728,8 +1956,13 @@ skip_dynamic_range_lvalue_expansion:; } } + for (auto child : added_mod_children) { + child->replace_ids(prefix, replace_rules); + while (child->simplify(true, false, false, 1, -1, false, false)) { } + } + for (auto child : decl->children) - if (child->type != AST_WIRE) + if (child->type != AST_WIRE && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) { AstNode *stmt = child->clone(); stmt->replace_ids(prefix, replace_rules); @@ -1876,7 +2109,7 @@ skip_dynamic_range_lvalue_expansion:; 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) { - int cmp_width = std::max(children[0]->bits.size(), children[1]->bits.size()); + int cmp_width = max(children[0]->bits.size(), children[1]->bits.size()); bool cmp_signed = children[0]->is_signed && children[1]->is_signed; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); @@ -2036,6 +2269,7 @@ apply_newNode: if (!did_something) basic_prep = true; + recursion_counter--; return did_something; } @@ -2048,10 +2282,18 @@ static void replace_result_wire_name_in_function(AstNode *node, std::string &fro } // replace a readmem[bh] TCALL ast node with a block of memory assignments -AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr) +AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) { + int mem_width, mem_size, addr_bits; + memory->meminfo(mem_width, mem_size, addr_bits); + AstNode *block = new AstNode(AST_BLOCK); + AstNode *meminit = nullptr; + int next_meminit_cursor=0; + vector<State> meminit_bits; + int meminit_size=0; + std::ifstream f; f.open(mem_filename.c_str()); @@ -2060,13 +2302,13 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right; - int range_min = std::min(range_left, range_right), range_max = std::max(range_left, range_right); + int range_min = min(range_left, range_right), range_max = max(range_left, range_right); if (start_addr < 0) start_addr = range_min; if (finish_addr < 0) - finish_addr = range_max; + finish_addr = range_max + 1; bool in_comment = false; int increment = start_addr <= finish_addr ? +1 : -1; @@ -2106,21 +2348,56 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m continue; } - AstNode *value = VERILOG_FRONTEND::const2ast((is_readmemh ? "'h" : "'b") + token); + AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); + + if (unconditional_init) + { + if (meminit == nullptr || cursor != next_meminit_cursor) + { + if (meminit != nullptr) { + meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); + meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + } + + meminit = new AstNode(AST_MEMINIT); + meminit->children.push_back(AstNode::mkconst_int(cursor, false)); + meminit->children.push_back(nullptr); + meminit->children.push_back(nullptr); + meminit->str = memory->str; + meminit->id2ast = memory; + meminit_bits.clear(); + meminit_size = 0; + + current_ast_mod->children.push_back(meminit); + next_meminit_cursor = cursor; + } - block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); - block->children.back()->children[0]->str = memory->str; - block->children.back()->children[0]->id2ast = memory; + meminit_size++; + next_meminit_cursor++; + meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end()); + delete value; + } + else + { + block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); + block->children.back()->children[0]->str = memory->str; + block->children.back()->children[0]->id2ast = memory; + } - if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; cursor += increment; } - if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; } + if (meminit != nullptr) { + meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); + meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + } + return block; } @@ -2171,7 +2448,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma name_map.swap(backup_name_map); } -// rename stuff (used when tasks of functions are instanciated) +// rename stuff (used when tasks of functions are instantiated) void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) { if (type == AST_BLOCK) @@ -2328,9 +2605,28 @@ bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set) return true; } +void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes) +{ + log_assert(mem2reg_set.count(this) == 0); + + if (mem2reg_set.count(id2ast)) + id2ast = nullptr; + + for (size_t i = 0; i < children.size(); i++) { + if (mem2reg_set.count(children[i]) > 0) { + delnodes.push_back(children[i]); + children.erase(children.begin() + (i--)); + } else { + children[i]->mem2reg_remove(mem2reg_set, delnodes); + } + } +} + // actually replace memories with registers -void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block) +bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block) { + bool did_something = false; + if (type == AST_BLOCK) block = this; @@ -2389,6 +2685,8 @@ void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, children[0]->id2ast = NULL; children[0]->str = id_data; type = AST_ASSIGN_EQ; + + did_something = true; } if (mem2reg_check(mem2reg_set)) @@ -2489,10 +2787,13 @@ void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, auto children_list = children; for (size_t i = 0; i < children_list.size(); i++) - children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block); + if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block)) + did_something = true; + + return did_something; } -// calulate memory dimensions +// calculate memory dimensions void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) { log_assert(type == AST_MEMORY); @@ -2502,7 +2803,7 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) if (mem_size < 0) mem_size *= -1; - mem_size += std::min(children[1]->range_left, children[1]->range_right) + 1; + mem_size += min(children[1]->range_left, children[1]->range_right) + 1; addr_bits = 1; while ((1 << addr_bits) < mem_size) @@ -2538,8 +2839,8 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia if (!children.at(0)->range_valid) log_error("Non-constant range in %s:%d (called from %s:%d).\n", filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum); - offset = std::min(children.at(0)->range_left, children.at(0)->range_right); - width = std::min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); + offset = min(children.at(0)->range_left, children.at(0)->range_right); + width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); } offset -= variables.at(str).offset; std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits; @@ -2579,7 +2880,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n", child->str.c_str(), child->filename.c_str(), child->linenum, fcall->filename.c_str(), fcall->linenum); variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); - variables[child->str].offset = std::min(child->range_left, child->range_right); + variables[child->str].offset = min(child->range_left, child->range_right); variables[child->str].is_signed = child->is_signed; if (child->is_input && argidx < fcall->children.size()) variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size()); @@ -2610,6 +2911,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_ASSIGN_EQ) { + if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && + stmt->children.at(0)->children.at(0)->type == AST_RANGE) + stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); stmt->children.at(1)->replace_variables(variables, fcall); while (stmt->simplify(true, false, false, 1, -1, false, true)) { } @@ -2635,7 +2939,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (!range->range_valid) log_error("Non-constant range in %s:%d (called from %s:%d).\n", range->filename.c_str(), range->linenum, fcall->filename.c_str(), fcall->linenum); - int offset = std::min(range->range_left, range->range_right); + int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; varinfo_t &v = variables[stmt->children.at(0)->str]; RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); @@ -2708,7 +3012,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) for (size_t i = 1; i < stmt->children.size(); i++) { bool found_match = false; - log_assert(stmt->children.at(i)->type == AST_COND); + log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ); if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { sel_case = stmt->children.at(i)->children.back(); |