diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/ast/ast.cc | 28 | ||||
-rw-r--r-- | frontends/ast/ast.h | 28 | ||||
-rw-r--r-- | frontends/ast/dpicall.cc | 17 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 91 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 593 | ||||
-rw-r--r-- | frontends/json/jsonparse.cc | 12 | ||||
-rw-r--r-- | frontends/verific/verific.cc | 24 | ||||
-rw-r--r-- | frontends/verific/verificsva.cc | 5 | ||||
-rw-r--r-- | frontends/verilog/preproc.cc | 18 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 104 |
11 files changed, 575 insertions, 347 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 1c0a8b34d..57552d86c 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -287,8 +287,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) const } std::string type_name = type2str(type); - fprintf(f, "%s%s <%s:%d.%d-%d.%d>", indent.c_str(), type_name.c_str(), filename.c_str(), location.first_line, - location.first_column, location.last_line, location.last_column); + fprintf(f, "%s%s <%s>", indent.c_str(), type_name.c_str(), loc_string().c_str()); if (!flag_no_dump_ptr) { if (id2ast) @@ -548,9 +547,9 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_CASE: - if (!children.empty() && children[0]->type == AST_CONDX) + if (children.size() > 1 && children[1]->type == AST_CONDX) fprintf(f, "%s" "casex (", indent.c_str()); - else if (!children.empty() && children[0]->type == AST_CONDZ) + else if (children.size() > 1 && children[1]->type == AST_CONDZ) fprintf(f, "%s" "casez (", indent.c_str()); else fprintf(f, "%s" "case (", indent.c_str()); @@ -959,6 +958,16 @@ RTLIL::Const AstNode::realAsConst(int width) return result; } +std::string AstNode::loc_string() const +{ + return stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +} + +void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) +{ + obj->attributes[ID::src] = ast->loc_string(); +} + // create a new AstModule from an AST_MODULE AST node static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false) { @@ -974,8 +983,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast current_module = new AstModule; current_module->ast = NULL; current_module->name = ast->str; - current_module->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, - ast->location.first_column, ast->location.last_line, ast->location.last_column); + set_src_attr(current_module, ast); current_module->set_bool_attribute(ID::cells_not_processed); current_ast_mod = ast; @@ -1229,13 +1237,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) { log_file_error((*it)->filename, (*it)->location.first_line, "Re-definition of module `%s'!\n", (*it)->str.c_str()); } else if (nooverwrite) { - log("Ignoring re-definition of module `%s' at %s:%d.%d-%d.%d.\n", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); + log("Ignoring re-definition of module `%s' at %s.\n", + (*it)->str.c_str(), (*it)->loc_string().c_str()); continue; } else { - log("Replacing existing%s module `%s' at %s:%d.%d-%d.%d.\n", + log("Replacing existing%s module `%s' at %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); + (*it)->str.c_str(), (*it)->loc_string().c_str()); design->remove(existing_mod); } } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 907392166..1c9a6ee47 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -252,8 +252,8 @@ namespace AST bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); void replace_result_wire_name_in_function(const std::string &from, const std::string &to); 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, bool original_scope = true); - void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); + void expand_genblock(const std::string &prefix); + void label_genblks(std::set<std::string>& existing, int &counter); 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); bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); @@ -263,14 +263,22 @@ namespace AST bool detect_latch(const std::string &var); // additional functionality for evaluating constant functions - struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; - bool has_const_only_constructs(bool &recommend_const_eval); - bool has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval); - void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); - AstNode *eval_const_function(AstNode *fcall); + struct varinfo_t { + RTLIL::Const val; + int offset; + bool is_signed; + AstNode *arg = nullptr; + bool explicitly_sized; + }; + bool has_const_only_constructs(); + bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); + AstNode *eval_const_function(AstNode *fcall, bool must_succeed); bool is_simple_const_expr(); std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); + bool is_recursive_function() const; + std::pair<AstNode*, AstNode*> get_tern_choice(); + // create a human-readable text representation of the AST (for debugging) void dumpAst(FILE *f, std::string indent) const; void dumpVlog(FILE *f, std::string indent) const; @@ -315,6 +323,9 @@ namespace AST // helpers for enum void allocateDefaultEnumValues(); void annotateTypedEnums(AstNode *template_node); + + // helpers for locations + std::string loc_string() const; }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code @@ -353,6 +364,9 @@ namespace AST std::pair<std::string,std::string> split_modport_from_type(std::string name_type); AstNode * find_modport(AstNode *intf, std::string name); void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport); + + // Helper for setting the src attribute. + void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast); } namespace AST_INTERNAL diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e241142d3..948c9083c 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -67,7 +67,7 @@ static ffi_fptr resolve_fn (std::string symbol_name) AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args) { AST::AstNode *newNode = nullptr; - union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; + union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1]; ffi_type *types [args.size() + 1]; void *values [args.size() + 1]; ffi_cif cif; @@ -92,6 +92,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, value_store[i].i32 = args[i]->asInt(args[i]->is_signed); values[i] = &value_store[i].i32; types[i] = &ffi_type_sint32; + } else if (argtypes[i] == "chandle") { + log(" arg %d (%s): %llx\n", i, argtypes[i].c_str(), (unsigned long long)args[i]->asInt(false)); + value_store[i].ptr = (void *)args[i]->asInt(args[i]->is_signed); + values[i] = &value_store[i].ptr; + types[i] = &ffi_type_pointer; } else { log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i); } @@ -106,6 +111,9 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, } else if (rtype == "real") { types[args.size()] = &ffi_type_double; values[args.size()] = &value_store[args.size()].f64; + } else if (rtype == "chandle") { + types[args.size()] = &ffi_type_pointer; + values[args.size()] = &value_store[args.size()].ptr; } else { log_error("invalid rtype '%s'.\n", rtype.c_str()); } @@ -123,6 +131,13 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, newNode = new AstNode(AST_REALVALUE); newNode->realvalue = value_store[args.size()].f32; log(" return realvalue: %g\n", newNode->asReal(true)); + } else if (rtype == "chandle") { + uint64_t rawval = (uint64_t)value_store[args.size()].ptr; + std::vector<RTLIL::State> bits(64); + for (int i = 0; i < 64; i++) + bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0; + newNode = AstNode::mkconst_bits(bits, false); + log(" return chandle: %llx\n", (unsigned long long)newNode->asInt(false)); } else { newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); log(" return integer: %lld\n", (long long)newNode->asInt(true)); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index b8bfdf65e..449f8c38e 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -45,10 +45,11 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width { IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; if (gen_attributes) for (auto &attr : that->attributes) { @@ -76,10 +77,11 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; if (that != NULL) for (auto &attr : that->attributes) { @@ -102,10 +104,10 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width { IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { @@ -137,10 +139,10 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const sstr << "$ternary$" << that->filename << ":" << that->location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { @@ -318,7 +320,7 @@ struct AST_INTERNAL::ProcessGenerator // generate process and simple root case proc = new RTLIL::Process; - proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + set_src_attr(proc, always); proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) @@ -354,7 +356,7 @@ struct AST_INTERNAL::ProcessGenerator if (found_anyedge_syncs) { if (found_global_syncs) log_file_error(always->filename, always->location.first_line, "Found non-synthesizable event list!\n"); - log("Note: Assuming pure combinatorial block at %s:%d.%d-%d.%d in\n", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string().c_str()); log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n"); log("use of @* instead of @(...) for better match of synthesis and simulation.\n"); } @@ -454,7 +456,7 @@ struct AST_INTERNAL::ProcessGenerator } while (current_module->wires_.count(wire_name) > 0); RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + set_src_attr(wire, always); chunk.wire = wire; chunk.offset = 0; @@ -589,7 +591,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: { RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; - sw->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, ast->location.first_column, ast->location.last_line, ast->location.last_column); + set_src_attr(sw, ast); sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap()); current_case->switches.push_back(sw); @@ -623,7 +625,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; - current_case->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", child->filename.c_str(), child->location.first_line, child->location.first_column, child->location.last_line, child->location.last_column); + set_src_attr(current_case, child); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); for (auto node : child->children) { @@ -942,6 +944,41 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun } break; } + if (current_scope.count(str)) + { + // This width detection is needed for function calls which are + // unelaborated, which currently only applies to calls to recursive + // functions reached by unevaluated ternary branches. + const AstNode *func = current_scope.at(str); + if (func->type != AST_FUNCTION) + log_file_error(filename, location.first_line, "Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str()); + const AstNode *wire = nullptr; + for (const AstNode *child : func->children) + if (child->str == func->str) { + wire = child; + break; + } + log_assert(wire && wire->type == AST_WIRE); + sign_hint = wire->is_signed; + width_hint = 1; + if (!wire->children.empty()) + { + log_assert(wire->children.size() == 1); + const AstNode *range = wire->children.at(0); + log_assert(range->type == AST_RANGE && range->children.size() == 2); + AstNode *left = range->children.at(0)->clone(); + AstNode *right = range->children.at(1)->clone(); + while (left->simplify(true, false, false, 1, -1, false, true)) { } + while (right->simplify(true, false, false, 1, -1, false, true)) { } + if (left->type != AST_CONSTANT || right->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Function %s has non-constant width!", + RTLIL::unescape_id(str).c_str()); + width_hint = abs(int(left->asInt(true) - right->asInt(true))); + delete left; + delete right; + } + break; + } YS_FALLTHROUGH // everything should have been handled above -> print error if not. @@ -1011,7 +1048,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // This is used by the hierarchy pass to know when it can replace interface connection with the individual // signals. RTLIL::Wire *wire = current_module->addWire(str, 1); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->start_offset = 0; wire->port_id = port_id; wire->port_input = true; @@ -1050,8 +1087,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::Const val = children[0]->bitsAsConst(); RTLIL::Wire *wire = current_module->addWire(str, GetSize(val)); current_module->connect(wire, val); + wire->is_signed = children[0]->is_signed; - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->attributes[type == AST_PARAMETER ? ID::parameter : ID::localparam] = 1; for (auto &attr : attributes) { @@ -1073,7 +1111,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->start_offset = range_right; wire->port_id = port_id; wire->port_input = is_input; @@ -1105,7 +1143,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Memory `%s' with non-constant width or size!\n", str.c_str()); RTLIL::Memory *memory = new RTLIL::Memory; - memory->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(memory, this); memory->name = str; memory->width = children[0]->range_left - children[0]->range_right + 1; if (children[1]->range_right < children[1]->range_left) { @@ -1162,7 +1200,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { RTLIL::Wire *wire = current_module->addWire(str); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->name = str; if (flag_autowire) log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str()); @@ -1544,13 +1582,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) sstr << "$memrd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); int mem_width, mem_size, addr_bits; is_signed = id2ast->is_signed; + wire->is_signed = is_signed; id2ast->meminfo(mem_width, mem_size, addr_bits); RTLIL::SigSpec addr_sig = children[0]->genRTLIL(); @@ -1582,7 +1621,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1646,7 +1685,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) cellname = str; RTLIL::Cell *cell = current_module->addCell(cellname, celltype); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) @@ -1691,7 +1730,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Re-definition of cell `%s'!\n", str.c_str()); RTLIL::Cell *cell = current_module->addCell(str, ""); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass cell->set_bool_attribute(ID::module_not_derived); @@ -1740,7 +1779,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // non-trivial signed nodes are indirected through // signed wires to enable sign extension RTLIL::IdString wire_name = NEW_ID; - RTLIL::Wire *wire = current_module->addWire(wire_name, arg->bits.size()); + RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig)); wire->is_signed = true; current_module->connect(wire, sig); sig = wire; @@ -1855,7 +1894,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); Cell *cell = current_module->addCell(myid, str.substr(1)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); cell->parameters[ID::WIDTH] = width; if (attributes.count(ID::reg)) { @@ -1866,7 +1905,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } Wire *wire = current_module->addWire(myid + "_wire", width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); cell->setPort(ID::Y, wire); is_signed = sign_hint; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index fc2976c83..6b4b9e045 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) return true; } +static std::string prefix_id(const std::string &prefix, const std::string &str) +{ + log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); + log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); + log_assert(prefix.back() == '.'); + if (str.front() == '\\') + return prefix + str.substr(1); + return prefix + str; +} + // 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(). @@ -565,6 +575,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, deep_recursion_warning = false; } + static bool unevaluated_tern_branch = false; + AstNode *newNode = NULL; bool did_something = false; @@ -748,6 +760,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;") if (type == AST_MODULE) { current_scope.clear(); + std::set<std::string> existing; + int counter = 0; + label_genblks(existing, counter); std::map<std::string, AstNode*> this_wire_scope; for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; @@ -928,7 +943,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->id2ast->is_logic) children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg) - log_warning("wire '%s' is assigned in a block at %s:%d.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + log_warning("wire '%s' is assigned in a block at %s.\n", children[0]->str.c_str(), loc_string().c_str()); if (type == AST_ASSIGN && children[0]->id2ast->is_reg) { bool is_rand_reg = false; if (children[1]->type == AST_FCALL) { @@ -942,7 +957,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, is_rand_reg = true; } if (!is_rand_reg) - log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", children[0]->str.c_str(), loc_string().c_str()); } children[0]->was_checked = true; } @@ -1078,7 +1093,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; case AST_TERNARY: - detect_width_simple = true; child_0_is_self_determined = true; break; @@ -1111,6 +1125,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, detectSignWidth(width_hint, sign_hint); if (type == AST_TERNARY) { + if (width_hint < 0) { + while (!children[0]->basic_prep && children[0]->simplify(true, false, in_lvalue, stage, -1, false, in_param)) + did_something = true; + + bool backup_unevaluated_tern_branch = unevaluated_tern_branch; + AstNode *chosen = get_tern_choice().first; + + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2]; + while (!children[1]->basic_prep && children[1]->simplify(false, false, in_lvalue, stage, -1, false, in_param)) + did_something = true; + + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1]; + while (!children[2]->basic_prep && children[2]->simplify(false, false, in_lvalue, stage, -1, false, in_param)) + did_something = true; + + unevaluated_tern_branch = backup_unevaluated_tern_branch; + detectSignWidth(width_hint, sign_hint); + } int width_hint_left, width_hint_right; bool sign_hint_left, sign_hint_right; bool found_real_left, found_real_right; @@ -1174,6 +1206,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (size_t i = 0; i < children.size(); i++) { bool did_something_here = true; bool backup_flag_autowire = flag_autowire; + bool backup_unevaluated_tern_branch = unevaluated_tern_branch; if ((type == AST_GENFOR || type == AST_FOR) && i >= 3) break; if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1) @@ -1186,6 +1219,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; if (type == AST_DEFPARAM && i == 0) flag_autowire = true; + if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) { + AstNode *chosen = get_tern_choice().first; + unevaluated_tern_branch = chosen && chosen != children[i]; + } while (did_something_here && i < children.size()) { bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; int width_hint_here = width_hint; @@ -1205,11 +1242,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_block = this; current_block_child = children[i]; } - if (!in_param_here && type == AST_FCALL) { - bool recommend_const_eval = false; - bool require_const_eval = has_const_only_constructs(recommend_const_eval); - in_param_here = recommend_const_eval || require_const_eval; - } if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) current_top_block = children[i]; if (i == 0 && child_0_is_self_determined) @@ -1230,6 +1262,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; } flag_autowire = backup_flag_autowire; + unevaluated_tern_branch = backup_unevaluated_tern_branch; } for (auto &attr : attributes) { while (attr.second->simplify(true, false, false, stage, -1, false, true)) @@ -1855,19 +1888,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // expand body int index = varbuf->children[0]->integer; - if (body_ast->type == AST_GENBLOCK) - buf = body_ast->clone(); - else - buf = new AstNode(AST_GENBLOCK, body_ast->clone()); - if (buf->str.empty()) { - std::stringstream sstr; - sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++); - buf->str = sstr.str(); - } - std::map<std::string, std::string> name_map; + log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); + log_assert(!body_ast->str.empty()); + buf = body_ast->clone(); + std::stringstream sstr; sstr << buf->str << "[" << index << "]."; - buf->expand_genblock(varbuf->str, sstr.str(), name_map); + std::string prefix = sstr.str(); + + // create a scoped localparam for the current value of the loop variable + AstNode *local_index = varbuf->clone(); + size_t pos = local_index->str.rfind('.'); + if (pos != std::string::npos) // remove outer prefix + local_index->str = "\\" + local_index->str.substr(pos + 1); + local_index->str = prefix_id(prefix, local_index->str); + current_scope[local_index->str] = local_index; + current_ast_mod->children.push_back(local_index); + + buf->expand_genblock(prefix); if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { @@ -1915,14 +1953,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) - log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); + { + log_assert(!VERILOG_FRONTEND::sv_mode); + log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); + } } // transform block with name if (type == AST_BLOCK && !str.empty()) { - std::map<std::string, std::string> name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) @@ -1942,8 +1982,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENBLOCK && children.size() != 0) { if (!str.empty()) { - std::map<std::string, std::string> name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); } for (size_t i = 0; i < children.size(); i++) { @@ -1979,8 +2018,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = new AstNode(AST_GENBLOCK, buf); if (!buf->str.empty()) { - std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { @@ -2058,8 +2096,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = selected_case->clone(); if (!buf->str.empty()) { - std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { @@ -3159,16 +3196,21 @@ skip_dynamic_range_lvalue_expansion:; log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str()); } - AstNode *decl = current_scope[str]; std::stringstream sstr; - sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; + sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); - bool recommend_const_eval = false; - bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); - if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) + AstNode *decl = current_scope[str]; + if (unevaluated_tern_branch && decl->is_recursive_function()) + goto replace_fcall_later; + decl = decl->clone(); + decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion + decl->expand_genblock(prefix); + + if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype)) { + bool require_const_eval = decl->has_const_only_constructs(); bool all_args_const = true; for (auto child : children) { while (child->simplify(true, false, false, 1, -1, false, true)) { } @@ -3177,12 +3219,14 @@ skip_dynamic_range_lvalue_expansion:; } if (all_args_const) { - AstNode *func_workspace = current_scope[str]->clone(); - func_workspace->str = NEW_ID.str(); - func_workspace->replace_result_wire_name_in_function(str, func_workspace->str); - newNode = func_workspace->eval_const_function(this); + AstNode *func_workspace = decl->clone(); + func_workspace->str = prefix_id(prefix, "$result"); + newNode = func_workspace->eval_const_function(this, in_param || require_const_eval); delete func_workspace; - goto apply_newNode; + if (newNode) { + delete decl; + goto apply_newNode; + } } if (in_param) @@ -3192,8 +3236,6 @@ 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; vector<AstNode*> new_stmts; vector<AstNode*> output_assignments; @@ -3203,16 +3245,17 @@ skip_dynamic_range_lvalue_expansion:; log_assert(type == AST_FCALL); AstNode *wire = NULL; + std::string res_name = prefix_id(prefix, "$result"); for (auto child : decl->children) - if (child->type == AST_WIRE && child->str == str) + if (child->type == AST_WIRE && child->str == res_name) wire = child->clone(); log_assert(wire != NULL); - wire->str = prefix + str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); while (wire->simplify(true, false, false, 1, -1, false, false)) { } @@ -3256,7 +3299,6 @@ skip_dynamic_range_lvalue_expansion:; if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) { AstNode *wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; @@ -3318,7 +3360,6 @@ skip_dynamic_range_lvalue_expansion:; else { wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; @@ -3329,15 +3370,11 @@ skip_dynamic_range_lvalue_expansion:; wire_cache[child->str] = wire; + current_scope[wire->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; + while (wire->simplify(true, false, false, 1, -1, false, false)) { } if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -3366,6 +3403,8 @@ skip_dynamic_range_lvalue_expansion:; range->children.push_back(mkconst_int(0, true)); } } + // updates the sizing + while (wire->simplify(true, false, false, 1, -1, false, false)) { } continue; } AstNode *wire_id = new AstNode(AST_IDENTIFIER); @@ -3381,18 +3420,9 @@ 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 && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) - { - AstNode *stmt = child->clone(); - stmt->replace_ids(prefix, replace_rules); - new_stmts.push_back(stmt); - } + new_stmts.push_back(child->clone()); new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); @@ -3405,10 +3435,11 @@ skip_dynamic_range_lvalue_expansion:; } replace_fcall_with_id: + delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; - str = prefix + str; + str = prefix_id(prefix, "$result"); } if (type == AST_TCALL) str = ""; @@ -3608,24 +3639,9 @@ replace_fcall_later:; case AST_TERNARY: if (children[0]->isConst()) { - bool found_sure_true = false; - bool found_maybe_true = false; - - if (children[0]->type == AST_CONSTANT) - for (auto &bit : children[0]->bits) { - if (bit == RTLIL::State::S1) - found_sure_true = true; - if (bit > RTLIL::State::S1) - found_maybe_true = true; - } - else - found_sure_true = children[0]->asReal(sign_hint) != 0; - - AstNode *choice = NULL, *not_choice = NULL; - if (found_sure_true) - choice = children[1], not_choice = children[2]; - else if (!found_maybe_true) - choice = children[2], not_choice = children[1]; + auto pair = get_tern_choice(); + AstNode *choice = pair.first; + AstNode *not_choice = pair.second; if (choice != NULL) { if (choice->type == AST_CONSTANT) { @@ -3859,63 +3875,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m return block; } -// annotate the names of all wires and other named objects in a generate block -void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) +// annotate the names of all wires and other named objects in a named generate +// or procedural block; nested blocks are themselves annotated such that the +// prefix is carried forward, but resolution of their children is deferred +void AstNode::expand_genblock(const std::string &prefix) { - // `original_scope` defaults to false, and is used to prevent the premature - // prefixing of items in named sub-blocks - - if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { - if (children.empty()) { - current_scope[index_var]->children[0]->cloneInto(this); - } else { - AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); - p->str = stringf("$genval$%d", autoidx++); - current_ast_mod->children.push_back(p); - str = p->str; - id2ast = p; - } - } - if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { - if (name_map.count(str) > 0) { - str = name_map[str]; - } else { - // remap the prefix of this ident if it is a local generate scope - size_t pos = str.rfind('.'); - if (pos != std::string::npos) { - std::string existing_prefix = str.substr(0, pos); - if (name_map.count(existing_prefix) > 0) { - str = name_map[existing_prefix] + str.substr(pos); - } + log_assert(!str.empty()); + + // search starting in the innermost scope and then stepping outward + for (size_t ppos = prefix.size() - 1; ppos; --ppos) { + if (prefix.at(ppos) != '.') continue; + + std::string new_prefix = prefix.substr(0, ppos + 1); + auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { + std::string new_name = prefix_id(new_prefix, ident); + if (current_scope.count(new_name)) + return new_name; + return {}; + }; + + // attempt to resolve the full identifier + std::string resolved = attempt_resolve(str); + if (!resolved.empty()) { + str = resolved; + break; } - } - } - - std::map<std::string, std::string> backup_name_map; - auto prefix_node = [&](AstNode* child) { - if (backup_name_map.size() == 0) - backup_name_map = name_map; + // attempt to resolve hierarchical prefixes within the identifier, + // as the prefix could refer to a local scope which exists but + // hasn't yet been elaborated + for (size_t spos = str.size() - 1; spos; --spos) { + if (str.at(spos) != '.') continue; + resolved = attempt_resolve(str.substr(0, spos)); + if (!resolved.empty()) { + str = resolved + str.substr(spos); + ppos = 1; // break outer loop + break; + } + } - // if within a nested scope - if (!original_scope) { - // this declaration shadows anything in the parent scope(s) - name_map[child->str] = child->str; - return; } + } - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = child->str.rfind('.'); - if (pos == std::string::npos) - pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; - else - pos = pos + 1; - new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); - if (new_name[0] != '$' && new_name[0] != '\\') - new_name = prefix[0] + new_name; - - name_map[child->str] = new_name; + auto prefix_node = [&prefix](AstNode* child) { + if (child->str.empty()) return; + std::string new_name = prefix_id(prefix, child->str); if (child->type == AST_FUNCTION) child->replace_result_wire_name_in_function(child->str, new_name); else @@ -3967,43 +3972,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma continue; // functions/tasks may reference wires, constants, etc. in this scope if (child->type == AST_FUNCTION || child->type == AST_TASK) - child->expand_genblock(index_var, prefix, name_map, false); - // continue prefixing if this child block is anonymous - else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) - child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); - else - child->expand_genblock(index_var, prefix, name_map, original_scope); - } - + continue; + // named blocks pick up the current prefix and will expanded later + if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) + continue; - if (backup_name_map.size() > 0) - name_map.swap(backup_name_map); + child->expand_genblock(prefix); + } } -// 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) +// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or +// IEEE 1800-2017 Section 27.6 +void AstNode::label_genblks(std::set<std::string>& existing, int &counter) { - if (type == AST_BLOCK) - { - std::map<std::string, std::string> new_rules = rules; - std::string new_prefix = prefix + str; - - for (auto child : children) - if (child->type == AST_WIRE) { - new_rules[child->str] = new_prefix + child->str; - child->str = new_prefix + child->str; - } + switch (type) { + case AST_GENIF: + case AST_GENFOR: + case AST_GENCASE: + // seeing a proper generate control flow construct increments the + // counter once + ++counter; + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; - for (auto child : children) - if (child->type != AST_WIRE) - child->replace_ids(new_prefix, new_rules); + case AST_GENBLOCK: { + // if this block is unlabeled, generate its corresponding unique name + for (int padding = 0; str.empty(); ++padding) { + std::string candidate = "\\genblk"; + for (int i = 0; i < padding; ++i) + candidate += '0'; + candidate += std::to_string(counter); + if (!existing.count(candidate)) + str = candidate; + } + // within a genblk, the counter starts fresh + std::set<std::string> existing_local = existing; + int counter_local = 0; + for (AstNode *child : children) + child->label_genblks(existing_local, counter_local); + break; } - else - { - if (type == AST_IDENTIFIER && rules.count(str) > 0) - str = rules.at(str); - for (auto child : children) - child->replace_ids(prefix, rules); + + default: + // track names which could conflict with implicit genblk names + if (str.rfind("\\genblk", 0) == 0) + existing.insert(str); + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; } } @@ -4495,33 +4512,12 @@ bool AstNode::detect_latch(const std::string &var) } } -bool AstNode::has_const_only_constructs(bool &recommend_const_eval) -{ - std::set<std::string> visited; - return has_const_only_constructs(visited, recommend_const_eval); -} - -bool AstNode::has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval) +bool AstNode::has_const_only_constructs() { - if (type == AST_FUNCTION || type == AST_TASK) - { - if (visited.count(str)) - { - recommend_const_eval = true; - return false; - } - visited.insert(str); - } - - if (type == AST_FOR) - recommend_const_eval = true; if (type == AST_WHILE || type == AST_REPEAT) return true; - if (type == AST_FCALL && current_scope.count(str)) - if (current_scope[str]->has_const_only_constructs(visited, recommend_const_eval)) - return true; for (auto child : children) - if (child->AstNode::has_const_only_constructs(visited, recommend_const_eval)) + if (child->has_const_only_constructs()) return true; return false; } @@ -4537,19 +4533,26 @@ bool AstNode::is_simple_const_expr() } // helper function for AstNode::eval_const_function() -void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) +bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed) { if (type == AST_IDENTIFIER && variables.count(str)) { int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); if (!children.empty()) { - if (children.size() != 1 || children.at(0)->type != AST_RANGE) - log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - children.at(0)->replace_variables(variables, fcall); + if (children.size() != 1 || children.at(0)->type != AST_RANGE) { + if (!must_succeed) + return false; + log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s: ...called from here.\n", + fcall->loc_string().c_str()); + } + if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) + return false; while (simplify(true, false, false, 1, -1, false, true)) { } - if (!children.at(0)->range_valid) - log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!children.at(0)->range_valid) { + if (!must_succeed) + return false; + log_file_error(filename, location.first_line, "Non-constant range\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } 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); } @@ -4559,19 +4562,22 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); newNode->cloneInto(this); delete newNode; - return; + return true; } for (auto &child : children) - child->replace_variables(variables, fcall); + if (!child->replace_variables(variables, fcall, must_succeed)) + return false; + return true; } -// evaluate functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall) +// attempt to statically evaluate a functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { - std::map<std::string, AstNode*> backup_scope; + std::map<std::string, AstNode*> backup_scope = current_scope; std::map<std::string, AstNode::varinfo_t> variables; AstNode *block = new AstNode(AST_BLOCK); + AstNode *result = nullptr; size_t argidx = 0; for (auto child : children) @@ -4593,24 +4599,37 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_WIRE) { while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - if (!stmt->range_valid) - log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", - stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); - variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); - variables[stmt->str].is_signed = stmt->is_signed; + if (!stmt->range_valid) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s: ... called from here.\n", + stmt->str.c_str(), fcall->loc_string().c_str()); + } + AstNode::varinfo_t &variable = variables[stmt->str]; + int width = abs(stmt->range_left - stmt->range_right) + 1; + // if this variable has already been declared as an input, check the + // sizes match if it already had an explicit size + if (variable.arg && variable.explicitly_sized && variable.val.size() != width) { + log_file_error(filename, location.first_line, "Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str()); + } + variable.val = RTLIL::Const(RTLIL::State::Sx, width); + variable.offset = min(stmt->range_left, stmt->range_right); + variable.is_signed = stmt->is_signed; + variable.explicitly_sized = stmt->children.size() && + stmt->children.back()->type == AST_RANGE; + // identify the argument corresponding to this wire, if applicable if (stmt->is_input && argidx < fcall->children.size()) { - int width = variables[stmt->str].val.bits.size(); - auto* arg_node = fcall->children.at(argidx++); - if (arg_node->type == AST_CONSTANT) { - variables[stmt->str].val = arg_node->bitsAsConst(width); + variable.arg = fcall->children.at(argidx++); + } + // load the constant arg's value into this variable + if (variable.arg) { + if (variable.arg->type == AST_CONSTANT) { + variable.val = variable.arg->bitsAsConst(width); } else { - log_assert(arg_node->type == AST_REALVALUE); - variables[stmt->str].val = arg_node->realAsConst(width); + log_assert(variable.arg->type == AST_REALVALUE); + variable.val = variable.arg->realAsConst(width); } } - if (!backup_scope.count(stmt->str)) - backup_scope[stmt->str] = current_scope[stmt->str]; current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); @@ -4623,8 +4642,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) { while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - if (!backup_scope.count(stmt->str)) - backup_scope[stmt->str] = current_scope[stmt->str]; current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); @@ -4635,32 +4652,46 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) { 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); + if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) + goto finished; + if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) + goto finished; while (stmt->simplify(true, false, false, 1, -1, false, true)) { } if (stmt->type != AST_ASSIGN_EQ) continue; - if (stmt->children.at(1)->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (stmt->children.at(1)->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here. X\n", + fcall->loc_string().c_str()); + } - if (stmt->children.at(0)->type != AST_IDENTIFIER) - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (stmt->children.at(0)->type != AST_IDENTIFIER) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } - if (!variables.count(stmt->children.at(0)->str)) - log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!variables.count(stmt->children.at(0)->str)) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } if (stmt->children.at(0)->children.empty()) { variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); } else { AstNode *range = stmt->children.at(0)->children.at(0); - if (!range->range_valid) - log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!range->range_valid) { + if (!must_succeed) + goto finished; + log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } 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]; @@ -4687,12 +4718,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_WHILE) { AstNode *cond = stmt->children.at(0)->clone(); - cond->replace_variables(variables, fcall); + if (!cond->replace_variables(variables, fcall, must_succeed)) + goto finished; while (cond->simplify(true, false, false, 1, -1, false, true)) { } - if (cond->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (cond->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } if (cond->asBool()) { block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -4708,12 +4743,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_REPEAT) { AstNode *num = stmt->children.at(0)->clone(); - num->replace_variables(variables, fcall); + if (!num->replace_variables(variables, fcall, must_succeed)) + goto finished; while (num->simplify(true, false, false, 1, -1, false, true)) { } - if (num->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (num->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } block->children.erase(block->children.begin()); for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -4727,7 +4766,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_CASE) { AstNode *expr = stmt->children.at(0)->clone(); - expr->replace_variables(variables, fcall); + if (!expr->replace_variables(variables, fcall, must_succeed)) + goto finished; while (expr->simplify(true, false, false, 1, -1, false, true)) { } AstNode *sel_case = NULL; @@ -4744,14 +4784,18 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) { AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); - cond->replace_variables(variables, fcall); + if (!cond->replace_variables(variables, fcall, must_succeed)) + goto finished; cond = new AstNode(AST_EQ, expr->clone(), cond); while (cond->simplify(true, false, false, 1, -1, false, true)) { } - if (cond->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (cond->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } found_match = cond->asBool(); delete cond; @@ -4773,6 +4817,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_BLOCK) { + if (!stmt->str.empty()) + stmt->expand_genblock(stmt->str + "."); + block->children.erase(block->children.begin()); block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); stmt->children.clear(); @@ -4780,20 +4827,20 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) continue; } - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); log_abort(); } - delete block; + result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); - for (auto &it : backup_scope) - if (it.second == NULL) - current_scope.erase(it.first); - else - current_scope[it.first] = it.second; +finished: + delete block; + current_scope = backup_scope; - return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + return result; } void AstNode::allocateDefaultEnumValues() @@ -4824,4 +4871,54 @@ void AstNode::allocateDefaultEnumValues() } } +bool AstNode::is_recursive_function() const +{ + std::set<const AstNode *> visited; + std::function<bool(const AstNode *node)> visit = [&](const AstNode *node) { + if (visited.count(node)) + return node == this; + visited.insert(node); + if (node->type == AST_FCALL) { + auto it = current_scope.find(node->str); + if (it != current_scope.end() && visit(it->second)) + return true; + } + for (const AstNode *child : node->children) { + if (visit(child)) + return true; + } + return false; + }; + + log_assert(type == AST_FUNCTION); + return visit(this); +} + +std::pair<AstNode*, AstNode*> AstNode::get_tern_choice() +{ + if (!children[0]->isConst()) + return {}; + + bool found_sure_true = false; + bool found_maybe_true = false; + + if (children[0]->type == AST_CONSTANT) + for (auto &bit : children[0]->bits) { + if (bit == RTLIL::State::S1) + found_sure_true = true; + if (bit > RTLIL::State::S1) + found_maybe_true = true; + } + else + found_sure_true = children[0]->asReal(true) != 0; + + AstNode *choice = nullptr, *not_choice = nullptr; + if (found_sure_true) + choice = children[1], not_choice = children[2]; + else if (!found_maybe_true) + choice = children[2], not_choice = children[1]; + + return {choice, not_choice}; +} + YOSYS_NAMESPACE_END diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 1b34aaf3a..312f6d3be 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -72,10 +72,17 @@ struct JsonNode break; } - if ('0' <= ch && ch <= '9') + if (('0' <= ch && ch <= '9') || ch == '-') { + bool negative = false; type = 'N'; - data_number = ch - '0'; + if (ch == '-') { + data_number = 0; + negative = true; + } else { + data_number = ch - '0'; + } + data_string += ch; while (1) @@ -97,6 +104,7 @@ struct JsonNode data_string += ch; } + data_number = negative ? -data_number : data_number; data_string = ""; break; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 614124a29..7aa3ebcbb 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -49,13 +49,16 @@ USING_YOSYS_NAMESPACE #include "VeriWrite.h" #include "VhdlUnits.h" #include "VeriLibrary.h" + +#if defined(YOSYSHQ_VERIFIC_INITSTATE) || defined(YOSYSHQ_VERIFIC_TEMPLATES) || defined(YOSYSHQ_VERIFIC_FORMALAPPS) #include "VeriExtensions.h" +#endif #ifndef YOSYSHQ_VERIFIC_API_VERSION # error "Only YosysHQ flavored Verific is supported. Please contact office@yosyshq.com for commercial support for Yosys+Verific." #endif -#if YOSYSHQ_VERIFIC_API_VERSION < 20201201 +#if YOSYSHQ_VERIFIC_API_VERSION < 20210103 # error "Please update your version of YosysHQ flavored Verific." #endif @@ -1471,6 +1474,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se continue; } +#ifdef YOSYSHQ_VERIFIC_INITSTATE if (inst->Type() == PRIM_YOSYSHQ_INITSTATE) { SigBit initstate = module->Initstate(new_verific_id(inst)); @@ -1480,7 +1484,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (!mode_keep) continue; } - +#endif if (!mode_keep && verific_sva_prims.count(inst->Type())) { if (verific_verbose) log(" skipping SVA cell in non k-mode\n"); @@ -1958,9 +1962,10 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par for (const auto &i : parameters) verific_params.Insert(i.first.c_str(), i.second.c_str()); +#ifdef YOSYSHQ_VERIFIC_INITSTATE InitialAssertionRewriter rw; rw.RegisterCallBack(); - +#endif if (top.empty()) { netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params); } @@ -2217,7 +2222,7 @@ struct VerificPass : public Pass { log("\n"); log("Applications:\n"); log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_FORMALAPPS) VerificFormalApplications vfa; log("%s\n",vfa.GetHelp().c_str()); #else @@ -2243,7 +2248,7 @@ struct VerificPass : public Pass { log("\n"); log("Templates:\n"); log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_TEMPLATES) VerificTemplateGenerator vfg; log("%s\n",vfg.GetHelp().c_str()); #else @@ -2494,6 +2499,7 @@ struct VerificPass : public Pass { goto check_error; } +#ifdef YOSYSHQ_VERIFIC_FORMALAPPS if (argidx < GetSize(args) && args[argidx] == "-app") { if (!(argidx+1 < GetSize(args))) @@ -2587,7 +2593,7 @@ struct VerificPass : public Pass { } goto check_error; } - +#endif if (argidx < GetSize(args) && args[argidx] == "-pp") { const char* filename = nullptr; @@ -2630,6 +2636,7 @@ struct VerificPass : public Pass { goto check_error; } +#ifdef YOSYSHQ_VERIFIC_TEMPLATES if (argidx < GetSize(args) && args[argidx] == "-template") { if (!(argidx+1 < GetSize(args))) @@ -2713,7 +2720,7 @@ struct VerificPass : public Pass { fclose(of); goto check_error; } - +#endif if (GetSize(args) > argidx && args[argidx] == "-import") { std::set<Netlist*> nl_todo, nl_done; @@ -2798,9 +2805,10 @@ struct VerificPass : public Pass { std::set<std::string> top_mod_names; +#ifdef YOSYSHQ_VERIFIC_INITSTATE InitialAssertionRewriter rw; rw.RegisterCallBack(); - +#endif if (mode_all) { log("Running hier_tree::ElaborateAll().\n"); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 632043b6f..1f5da1b1d 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1759,6 +1759,11 @@ struct VerificSvaImporter clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0); } + // accept in disable case + + if (clocking.disable_sig != State::S0) + sig_a_q = module->Or(NEW_ID, sig_a_q, clocking.disable_sig); + // generate fair/live cell RTLIL::Cell *c = nullptr; diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 752f7a7a8..de707593f 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -390,13 +390,16 @@ static void input_file(std::istream &f, std::string filename) // the argument list); false if we finished with ','. static bool read_argument(std::string &dest) { + skip_spaces(); std::vector<char> openers; for (;;) { - skip_spaces(); std::string tok = next_token(true); if (tok == ")") { - if (openers.empty()) + if (openers.empty()) { + while (dest.size() && (dest.back() == ' ' || dest.back() == '\t')) + dest = dest.substr(0, dest.size() - 1); return true; + } if (openers.back() != '(') log_error("Mismatched brackets in macro argument: %c and %c.\n", openers.back(), tok[0]); @@ -474,7 +477,16 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) std::string name = tok.substr(1); std::string skipped_spaces = skip_spaces(); tok = next_token(false); - if (tok == "(" && body->has_args) { + if (body->has_args) { + if (tok != "(") { + if (tok.size() == 1 && iscntrl(tok[0])) { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02x", tok[0]); + tok = buf; + } + log_error("Expected to find '(' to begin macro arguments for '%s', but instead found '%s'\n", + name.c_str(), tok.c_str()); + } std::vector<std::string> args; bool done = false; while (!done) { diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f2241066f..eeb7440f8 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -234,7 +234,7 @@ static bool isUserType(std::string &s) "automatic" { return TOK_AUTOMATIC; } "unique" { SV_KEYWORD(TOK_UNIQUE); } -"unique0" { SV_KEYWORD(TOK_UNIQUE); } +"unique0" { SV_KEYWORD(TOK_UNIQUE0); } "priority" { SV_KEYWORD(TOK_PRIORITY); } "always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 8bd58d24c..dc7ec8348 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -277,7 +277,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED %token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF %token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY -%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY +%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_UNIQUE0 TOK_PRIORITY %token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_UNION %token TOK_OR_ASSIGN TOK_XOR_ASSIGN TOK_AND_ASSIGN TOK_SUB_ASSIGN @@ -286,7 +286,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number %type <string> type_name %type <ast> opt_enum_init enum_type struct_type non_wire_data_type -%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff +%type <boolean> opt_signed opt_property always_comb_or_latch always_or_always_ff %type <al> attr case_attr %type <ast> struct_union @@ -609,12 +609,17 @@ interface_body_stmt: param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | modport_stmt; +mintypmax_expr: + expr { delete $1; } | + expr ':' expr ':' expr { delete $1; delete $3; delete $5; }; + non_opt_delay: '#' TOK_ID { delete $2; } | '#' TOK_CONSTVAL { delete $2; } | '#' TOK_REALVAL { delete $2; } | - '#' '(' expr ')' { delete $3; } | - '#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; }; + '#' '(' mintypmax_expr ')' | + '#' '(' mintypmax_expr ',' mintypmax_expr ')' | + '#' '(' mintypmax_expr ',' mintypmax_expr ',' mintypmax_expr ')'; delay: non_opt_delay | %empty; @@ -770,6 +775,7 @@ module_body: module_body module_body_stmt | /* the following line makes the generate..endgenrate keywords optional */ module_body gen_stmt | + module_body gen_block | module_body ';' | %empty; @@ -1490,10 +1496,10 @@ enum_base_type: type_atom type_signing | %empty { astbuf1->is_reg = true; addRange(astbuf1); } ; -type_atom: TOK_INTEGER { astbuf1->is_reg = true; addRange(astbuf1); } // 4-state signed - | TOK_INT { astbuf1->is_reg = true; addRange(astbuf1); } // 2-state signed - | TOK_SHORTINT { astbuf1->is_reg = true; addRange(astbuf1, 15, 0); } // 2-state signed - | TOK_BYTE { astbuf1->is_reg = true; addRange(astbuf1, 7, 0); } // 2-state signed +type_atom: TOK_INTEGER { astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1); } // 4-state signed + | TOK_INT { astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1); } // 2-state signed + | TOK_SHORTINT { astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1, 15, 0); } // 2-state signed + | TOK_BYTE { astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1, 7, 0); } // 2-state signed ; type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned @@ -1783,7 +1789,13 @@ wire_name: } rewriteAsMemoryNode(node, $2); } - if (current_function_or_task == NULL) { + if (current_function_or_task) { + if (node->is_input || node->is_output) + node->port_id = current_function_or_task_port_id++; + } else if (ast_stack.back()->type == AST_GENBLOCK) { + if (node->is_input || node->is_output) + frontend_verilog_yyerror("Cannot declare module port `%s' within a generate block.", $1->c_str()); + } else { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { port_stubs[*$1] = ++port_counter; } @@ -1798,9 +1810,6 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); } - } else { - if (node->is_input || node->is_output) - node->port_id = current_function_or_task_port_id++; } //FIXME: for some reason, TOK_ID has a location which always points to one column *after* the real last column... SET_AST_NODE_LOC(node, @1, @1); @@ -2459,6 +2468,16 @@ behavioral_stmt: exitTypeScope(); if ($4 != NULL && $8 != NULL && *$4 != *$8) frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); + AstNode *node = ast_stack.back(); + // In SystemVerilog, unnamed blocks with block item declarations + // create an implicit hierarchy scope + if (sv_mode && node->str.empty()) + for (const AstNode* child : node->children) + if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER + || child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { + node->str = "$unnamed_block$" + std::to_string(autoidx++); + break; + } SET_AST_NODE_LOC(ast_stack.back(), @2, @8); delete $4; delete $8; @@ -2473,6 +2492,7 @@ behavioral_stmt: ast_stack.back()->children.push_back($7); } ';' simple_behavioral_stmt ')' { AstNode *block = new AstNode(AST_BLOCK); + block->str = "$for_loop$" + std::to_string(autoidx++); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { @@ -2539,20 +2559,21 @@ behavioral_stmt: ast_stack.pop_back(); }; -unique_case_attr: - %empty { - $$ = false; +case_attr: + attr { + $$ = $1; } | - TOK_PRIORITY case_attr { - $$ = $2; + attr TOK_UNIQUE0 { + (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + $$ = $1; } | - TOK_UNIQUE case_attr { - $$ = true; - }; - -case_attr: - attr unique_case_attr { - if ($2) (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + attr TOK_PRIORITY { + (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + $$ = $1; + } | + attr TOK_UNIQUE { + (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); $$ = $1; }; @@ -2722,6 +2743,7 @@ single_arg: module_gen_body: module_gen_body gen_stmt_or_module_body_stmt | + module_gen_body gen_block | %empty; gen_stmt_or_module_body_stmt: @@ -2747,12 +2769,7 @@ gen_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); ast_stack.back()->children.push_back($3); - AstNode *block = new AstNode(AST_GENBLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); - } gen_stmt_block { - ast_stack.pop_back(); - } opt_gen_else { + } gen_stmt_block opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | @@ -2765,6 +2782,18 @@ gen_stmt: SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | + TOK_MSG_TASKS { + AstNode *node = new AstNode(AST_TECALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } opt_arg_list ';'{ + SET_AST_NODE_LOC(ast_stack.back(), @1, @3); + ast_stack.pop_back(); + }; + +gen_block: TOK_BEGIN { enterTypeScope(); } opt_label { @@ -2774,22 +2803,15 @@ gen_stmt: ast_stack.push_back(node); } module_gen_body TOK_END opt_label { exitTypeScope(); + if ($3 != NULL && $7 != NULL && *$3 != *$7) + frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1); delete $3; delete $7; SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); - } | - TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TECALL); - node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @1, @3); - ast_stack.pop_back(); }; +// result is wrapped in a genblock only if necessary gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); @@ -2798,7 +2820,7 @@ gen_stmt_block: } gen_stmt_or_module_body_stmt { SET_AST_NODE_LOC(ast_stack.back(), @2, @2); ast_stack.pop_back(); - }; + } | gen_block; opt_gen_else: TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; |