diff options
Diffstat (limited to 'frontends')
36 files changed, 4086 insertions, 1123 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 07e3cd6e0..cb19b8413 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -55,6 +55,17 @@ inline int32_t from_big_endian(int32_t i32) { #define log_debug2(...) ; //#define log_debug2(...) log_debug(__VA_ARGS__) +static int decimal_digits(uint32_t n) { + static uint32_t digit_cutoff[9] = { + 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000, 1000000000 + }; + for (int i = 0; i < 9; ++i) { + if (n < digit_cutoff[i]) return i + 1; + } + return 10; +} + struct ConstEvalAig { RTLIL::Module *module; @@ -515,7 +526,7 @@ void AigerReader::parse_aiger_ascii() unsigned l1, l2, l3; // Parse inputs - int digits = ceil(log10(I)); + int digits = decimal_digits(I); for (unsigned i = 1; i <= I; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an input!\n", line_count); @@ -537,7 +548,7 @@ void AigerReader::parse_aiger_ascii() clk_wire->port_input = true; clk_wire->port_output = false; } - digits = ceil(log10(L)); + digits = decimal_digits(L); for (unsigned i = 0; i < L; ++i, ++line_count) { if (!(f >> l1 >> l2)) log_error("Line %u cannot be interpreted as a latch!\n", line_count); @@ -575,7 +586,7 @@ void AigerReader::parse_aiger_ascii() } // Parse outputs - digits = ceil(log10(O)); + digits = decimal_digits(O); for (unsigned i = 0; i < O; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); @@ -643,7 +654,7 @@ void AigerReader::parse_aiger_binary() std::string line; // Parse inputs - int digits = ceil(log10(I)); + int digits = decimal_digits(I); for (unsigned i = 1; i <= I; ++i) { log_debug2("%d is an input\n", i); RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, i)); @@ -662,7 +673,7 @@ void AigerReader::parse_aiger_binary() clk_wire->port_input = true; clk_wire->port_output = false; } - digits = ceil(log10(L)); + digits = decimal_digits(L); l1 = (I+1) * 2; for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { if (!(f >> l2)) @@ -700,7 +711,7 @@ void AigerReader::parse_aiger_binary() } // Parse outputs - digits = ceil(log10(O)); + digits = decimal_digits(O); for (unsigned i = 0; i < O; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 251a24977..81b955947 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc index 91d917c91..9e6eee1e8 100644 --- a/frontends/ast/Makefile.inc +++ b/frontends/ast/Makefile.inc @@ -3,4 +3,5 @@ OBJS += frontends/ast/ast.o OBJS += frontends/ast/simplify.o OBJS += frontends/ast/genrtlil.o OBJS += frontends/ast/dpicall.o +OBJS += frontends/ast/ast_binding.o diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 9520ae32c..7be8ab565 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -52,8 +52,10 @@ namespace AST_INTERNAL { const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; RTLIL::SigSpec ignoreThisSignalsInInitial; AstNode *current_always, *current_top_block, *current_block, *current_block_child; - AstModule *current_module; + Module *current_module; bool current_always_clocked; + dict<std::string, int> current_memwr_count; + dict<std::string, pool<int>> current_memwr_visible; } // convert node types to string @@ -175,6 +177,7 @@ std::string AST::type2str(AstNodeType type) X(AST_STRUCT) X(AST_UNION) X(AST_STRUCT_ITEM) + X(AST_BIND) #undef X default: log_abort(); @@ -196,7 +199,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) // create new node (AstNode constructor) // (the optional child arguments make it easier to create AST trees) -AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3) +AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3, AstNode *child4) { static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); @@ -233,6 +236,8 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch children.push_back(child2); if (child3) children.push_back(child3); + if (child4) + children.push_back(child4); } // create a (deep recursive) copy of a node @@ -287,8 +292,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) @@ -318,6 +322,8 @@ void AstNode::dumpAst(FILE *f, std::string indent) const fprintf(f, " reg"); if (is_signed) fprintf(f, " signed"); + if (is_unsized) + fprintf(f, " unsized"); if (basic_prep) fprintf(f, " basic_prep"); if (lookahead) @@ -336,6 +342,12 @@ void AstNode::dumpAst(FILE *f, std::string indent) const fprintf(f, " %d", v); fprintf(f, " ]"); } + if (!multirange_swapped.empty()) { + fprintf(f, " multirange_swapped=["); + for (auto v : multirange_swapped) + fprintf(f, " %d", v); + fprintf(f, " ]"); + } if (is_enum) { fprintf(f, " type=enum"); } @@ -542,9 +554,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()); @@ -842,7 +854,7 @@ RTLIL::Const AstNode::bitsAsConst(int width) return bitsAsConst(width, is_signed); } -RTLIL::Const AstNode::asAttrConst() +RTLIL::Const AstNode::asAttrConst() const { log_assert(type == AST_CONSTANT); @@ -857,8 +869,17 @@ RTLIL::Const AstNode::asAttrConst() return val; } -RTLIL::Const AstNode::asParaConst() +RTLIL::Const AstNode::asParaConst() const { + if (type == AST_REALVALUE) + { + AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue)); + RTLIL::Const val = strnode->asAttrConst(); + val.flags |= RTLIL::CONST_FLAG_REAL; + delete strnode; + return val; + } + RTLIL::Const val = asAttrConst(); if (is_signed) val.flags |= RTLIL::CONST_FLAG_SIGNED; @@ -953,8 +974,25 @@ RTLIL::Const AstNode::realAsConst(int width) return result; } -// 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) +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(); +} + +static bool param_has_no_default(const AstNode *param) { + const auto &children = param->children; + log_assert(param->type == AST_PARAMETER); + log_assert(children.size() <= 2); + return children.empty() || + (children.size() == 1 && children[0]->type == AST_RANGE); +} + +static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false) { log_assert(current_scope.empty()); log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); @@ -965,12 +1003,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); } - current_module = new AstModule; - current_module->ast = NULL; - current_module->name = ast->str; - current_module->attributes[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); - current_module->set_bool_attribute(ID::cells_not_processed); + AstModule *module = new AstModule; + current_module = module; + + module->ast = NULL; + module->name = ast->str; + set_src_attr(module, ast); + module->set_bool_attribute(ID::cells_not_processed); current_ast_mod = ast; AstNode *ast_before_simplify; @@ -992,6 +1031,10 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast if (!defer) { + for (const AstNode *node : ast->children) + if (node->type == AST_PARAMETER && param_has_no_default(node)) + log_file_error(node->filename, node->location.first_line, "Parameter `%s' has no default value and has not been overridden!\n", node->str.c_str()); + bool blackbox_module = flag_lib; if (!blackbox_module && !flag_noblackbox) { @@ -1009,7 +1052,11 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast } } + // simplify this module or interface using the current design as context + // for lookup up ports and wires within cells + set_simplify_design_context(design); while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } + set_simplify_design_context(nullptr); if (flag_dump_ast2) { log("Dumping AST after simplification:\n"); @@ -1106,7 +1153,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); - current_module->attributes[attr.first] = attr.second->asAttrConst(); + module->attributes[attr.first] = attr.second->asAttrConst(); } for (size_t i = 0; i < ast->children.size(); i++) { AstNode *node = ast->children[i]; @@ -1134,35 +1181,93 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) continue; - current_module->attributes[attr.first] = attr.second->asAttrConst(); + module->attributes[attr.first] = attr.second->asAttrConst(); } + for (const AstNode *node : ast->children) + if (node->type == AST_PARAMETER) + current_module->avail_parameters(node->str); } if (ast->type == AST_INTERFACE) - current_module->set_bool_attribute(ID::is_interface); - current_module->ast = ast_before_simplify; - current_module->nolatches = flag_nolatches; - current_module->nomeminit = flag_nomeminit; - current_module->nomem2reg = flag_nomem2reg; - current_module->mem2reg = flag_mem2reg; - current_module->noblackbox = flag_noblackbox; - current_module->lib = flag_lib; - current_module->nowb = flag_nowb; - current_module->noopt = flag_noopt; - current_module->icells = flag_icells; - current_module->pwires = flag_pwires; - current_module->autowire = flag_autowire; - current_module->fixup_ports(); + module->set_bool_attribute(ID::is_interface); + module->ast = ast_before_simplify; + module->nolatches = flag_nolatches; + module->nomeminit = flag_nomeminit; + module->nomem2reg = flag_nomem2reg; + module->mem2reg = flag_mem2reg; + module->noblackbox = flag_noblackbox; + module->lib = flag_lib; + module->nowb = flag_nowb; + module->noopt = flag_noopt; + module->icells = flag_icells; + module->pwires = flag_pwires; + module->autowire = flag_autowire; + module->fixup_ports(); if (flag_dump_rtlil) { log("Dumping generated RTLIL:\n"); - log_module(current_module); + log_module(module); log("--- END OF RTLIL DUMP ---\n"); } + design->add(current_module); return current_module; } +RTLIL::Module * +AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, + RTLIL::Module *old_module, + AstNode *new_ast, + AstNode *original_ast) +{ + // The old module will be deleted. Rename and mark for deletion, using + // a static counter to make sure we get a unique name. + static unsigned counter; + std::ostringstream new_name; + new_name << old_module->name.str() + << "_before_process_and_replace_module_" + << counter; + ++counter; + + design->rename(old_module, new_name.str()); + old_module->set_bool_attribute(ID::to_delete); + + // Check if the module was the top module. If it was, we need to remove + // the top attribute and put it on the new module. + bool is_top = false; + if (old_module->get_bool_attribute(ID::initial_top)) { + old_module->attributes.erase(ID::initial_top); + is_top = true; + } + + // Generate RTLIL from AST for the new module and add to the design: + RTLIL::Module* new_module = process_module(design, new_ast, false, original_ast); + + if (is_top) + new_module->set_bool_attribute(ID::top); + + return new_module; +} + +// renames identifiers in tasks and functions within a package +static void rename_in_package_stmts(AstNode *pkg) +{ + std::unordered_set<std::string> idents; + for (AstNode *item : pkg->children) + idents.insert(item->str); + std::function<void(AstNode*)> rename = + [&rename, &idents, pkg](AstNode *node) { + for (AstNode *child : node->children) { + if (idents.count(child->str)) + child->str = pkg->str + "::" + child->str.substr(1); + rename(child); + } + }; + for (AstNode *item : pkg->children) + if (item->type == AST_FUNCTION || item->type == AST_TASK) + rename(item); +} + // create AstModule instances for all modules in the AST tree and add them to 'design' void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) @@ -1188,12 +1293,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_autowire = autowire; log_assert(current_ast->type == AST_DESIGN); - for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) + for (AstNode *child : current_ast->children) { - if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) + if (child->type == AST_MODULE || child->type == AST_INTERFACE) { for (auto n : design->verilog_globals) - (*it)->children.push_back(n->clone()); + child->children.push_back(n->clone()); // append nodes from previous packages using package-qualified names for (auto &n : design->verilog_packages) { @@ -1208,45 +1313,63 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump } else { cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); } - (*it)->children.push_back(cloned_node); + child->children.push_back(cloned_node); } } - if (flag_icells && (*it)->str.compare(0, 2, "\\$") == 0) - (*it)->str = (*it)->str.substr(1); + if (flag_icells && child->str.compare(0, 2, "\\$") == 0) + child->str = child->str.substr(1); + + bool defer_local = defer; + if (!defer_local) + for (const AstNode *node : child->children) + if (node->type == AST_PARAMETER && param_has_no_default(node)) + { + log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str.c_str()); + defer_local = true; + break; + } + - if (defer) - (*it)->str = "$abstract" + (*it)->str; + if (defer_local) + child->str = "$abstract" + child->str; - if (design->has((*it)->str)) { - RTLIL::Module *existing_mod = design->module((*it)->str); + if (design->has(child->str)) { + RTLIL::Module *existing_mod = design->module(child->str); 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()); + log_file_error(child->filename, child->location.first_line, "Re-definition of module `%s'!\n", child->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", + child->str.c_str(), child->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); + child->str.c_str(), child->loc_string().c_str()); design->remove(existing_mod); } } - design->add(process_module(*it, defer)); + process_module(design, child, defer_local); current_ast_mod = nullptr; } - else if ((*it)->type == AST_PACKAGE) { + else if (child->type == AST_PACKAGE) { // process enum/other declarations - (*it)->simplify(true, false, false, 1, -1, false, false); - design->verilog_packages.push_back((*it)->clone()); + child->simplify(true, false, false, 1, -1, false, false); + rename_in_package_stmts(child); + design->verilog_packages.push_back(child->clone()); current_scope.clear(); } + else if (child->type == AST_BIND) { + // top-level bind construct + for (RTLIL::Binding *binding : child->genBindings()) + design->add(binding); + } else { // must be global definition - (*it)->simplify(false, false, false, 1, -1, false, false); //process enum/other declarations - design->verilog_globals.push_back((*it)->clone()); + if (child->type == AST_PARAMETER) + child->type = AST_LOCALPARAM; // cannot be overridden + design->verilog_globals.push_back(child->clone()); current_scope.clear(); } } @@ -1337,13 +1460,32 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule } } +// AstModules may contain cells marked with ID::reprocess_after, which indicates +// that it should be reprocessed once the specified module has been elaborated. +bool AstModule::reprocess_if_necessary(RTLIL::Design *design) +{ + for (const RTLIL::Cell *cell : cells()) + { + std::string modname = cell->get_string_attribute(ID::reprocess_after); + if (modname.empty()) + continue; + if (design->module(modname) || design->module("$abstract" + modname)) { + log("Reprocessing module %s because instantiated module %s has become available.\n", + log_id(name), log_id(modname)); + loadconfig(); + process_and_replace_module(design, this, ast, NULL); + return true; + } + } + return false; +} + // When an interface instance is found in a module, the whole RTLIL for the module will be rederived again // from AST. The interface members are copied into the AST module with the prefix of the interface. -void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces) +void AstModule::expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces) { loadconfig(); - bool is_top = false; AstNode *new_ast = ast->clone(); for (auto &intf : local_interfaces) { std::string intfname = intf.first.str(); @@ -1400,29 +1542,15 @@ void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdStri } } - // The old module will be deleted. Rename and mark for deletion: - std::string original_name = this->name.str(); - std::string changed_name = original_name + "_before_replacing_local_interfaces"; - design->rename(this, changed_name); - this->set_bool_attribute(ID::to_delete); - - // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the - // new module. - if (this->get_bool_attribute(ID::initial_top)) { - this->attributes.erase(ID::initial_top); - is_top = true; - } + // Generate RTLIL from AST for the new module and add to the design, + // renaming this module to move it out of the way. + RTLIL::Module* new_module = + process_and_replace_module(design, this, new_ast, ast_before_replacing_interface_ports); - // Generate RTLIL from AST for the new module and add to the design: - AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports); - delete(new_ast); - design->add(newmod); - RTLIL::Module* mod = design->module(original_name); - if (is_top) - mod->set_bool_attribute(ID::top); + delete new_ast; // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. - mod->set_bool_attribute(ID::interfaces_replaced_in_module); + new_module->set_bool_attribute(ID::interfaces_replaced_in_module); } // create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces @@ -1472,7 +1600,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr explode_interface_port(new_ast, intfmodule, intfname, modport); } - design->add(process_module(new_ast, false)); + process_module(design, new_ast, false); design->module(modname)->check(); RTLIL::Module* mod = design->module(modname); @@ -1505,6 +1633,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr } } else { + modname = new_modname; log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); } @@ -1522,7 +1651,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr if (!design->has(modname)) { new_ast->str = modname; - design->add(process_module(new_ast, false, NULL, quiet)); + process_module(design, new_ast, false, NULL, quiet); design->module(modname)->check(); } else if (!quiet) { log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); @@ -1532,6 +1661,40 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr return modname; } +static std::string serialize_param_value(const RTLIL::Const &val) { + std::string res; + if (val.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) + res.push_back('t'); + if (val.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) + res.push_back('s'); + if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) + res.push_back('r'); + res += stringf("%d", GetSize(val)); + res.push_back('\''); + for (int i = GetSize(val) - 1; i >= 0; i--) { + switch (val.bits[i]) { + case RTLIL::State::S0: res.push_back('0'); break; + case RTLIL::State::S1: res.push_back('1'); break; + case RTLIL::State::Sx: res.push_back('x'); break; + case RTLIL::State::Sz: res.push_back('z'); break; + case RTLIL::State::Sa: res.push_back('?'); break; + case RTLIL::State::Sm: res.push_back('m'); break; + } + } + return res; +} + +std::string AST::derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> ¶meters) { + std::string para_info; + for (const auto &elem : parameters) + para_info += stringf("%s=%s", elem.first.c_str(), serialize_param_value(elem.second).c_str()); + + if (para_info.size() > 60) + return "$paramod$" + sha1(para_info) + stripped_name; + else + return "$paramod" + stripped_name + para_info; +} + // create a new parametric module (when needed) and return the name of the generated module std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, AstNode **new_ast_out, bool quiet) { @@ -1540,9 +1703,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id if (stripped_name.compare(0, 9, "$abstract") == 0) stripped_name = stripped_name.substr(9); - std::string para_info; - int para_counter = 0; + std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters; for (const auto child : ast->children) { if (child->type != AST_PARAMETER) continue; @@ -1551,25 +1713,21 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id if (it != parameters.end()) { if (!quiet) log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second)); - para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second)); + named_parameters.emplace_back(child->str, it->second); continue; } it = parameters.find(stringf("$%d", para_counter)); if (it != parameters.end()) { if (!quiet) log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second)); - para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second)); + named_parameters.emplace_back(child->str, it->second); continue; } } - std::string modname; - if (parameters.size() == 0) - modname = stripped_name; - else if (para_info.size() > 60) - modname = "$paramod$" + sha1(para_info) + stripped_name; - else - modname = "$paramod" + stripped_name + para_info; + std::string modname = stripped_name; + if (parameters.size()) // not named_parameters to cover hierarchical defparams + modname = derived_module_name(stripped_name, named_parameters); if (design->has(modname)) return modname; @@ -1604,6 +1762,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id } continue; rewrite_parameter: + if (param_has_no_default(child)) + child->children.insert(child->children.begin(), nullptr); delete child->children.at(0); if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) { child->children[0] = new AstNode(AST_REALVALUE); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 9a5aa15f9..48ec9a063 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -160,7 +160,8 @@ namespace AST AST_TYPEDEF, AST_STRUCT, AST_UNION, - AST_STRUCT_ITEM + AST_STRUCT_ITEM, + AST_BIND }; struct AstSrcLocType { @@ -202,6 +203,7 @@ namespace AST // if this is a multirange memory then this vector contains offset and length of each dimension std::vector<int> multirange_dimensions; + std::vector<bool> multirange_swapped; // true if range is swapped, not used for structs // this is set by simplify and used during RTLIL generation AstNode *id2ast; @@ -219,7 +221,7 @@ namespace AST AstSrcLocType location; // creating and deleting nodes - AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL); + AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr); AstNode *clone() const; void cloneInto(AstNode *other) const; void delete_children(); @@ -249,9 +251,10 @@ 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); + 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); - 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); @@ -259,19 +262,32 @@ namespace AST void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes); void meminfo(int &mem_width, int &mem_size, int &addr_bits); bool detect_latch(const std::string &var); + const RTLIL::Module* lookup_cell_module(); // 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); - 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; + // Generate RTLIL for a bind construct + std::vector<RTLIL::Binding *> genBindings() const; + // used by genRTLIL() for detecting expression width and sign void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL); void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL); @@ -280,7 +296,7 @@ namespace AST // for expressions the resulting signal vector is returned // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false); - RTLIL::SigSpec genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL); + RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL); // compare AST nodes bool operator==(const AstNode &other) const; @@ -298,8 +314,8 @@ namespace AST RTLIL::Const bitsAsConst(int width, bool is_signed); RTLIL::Const bitsAsConst(int width = -1); RTLIL::Const bitsAsUnsizedConst(int width); - RTLIL::Const asAttrConst(); - RTLIL::Const asParaConst(); + RTLIL::Const asAttrConst() const; + RTLIL::Const asParaConst() const; uint64_t asInt(bool is_signed); bool bits_only_01() const; bool asBool() const; @@ -312,6 +328,12 @@ namespace AST // helpers for enum void allocateDefaultEnumValues(); void annotateTypedEnums(AstNode *template_node); + + // helpers for locations + std::string loc_string() const; + + // Helper for looking up identifiers which are prefixed with the current module name + std::string try_pop_module_prefix() const; }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code @@ -327,7 +349,8 @@ namespace AST RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail) override; RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) override; std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, AstNode **new_ast_out, bool quiet = false); - void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override; + void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override; + bool reprocess_if_necessary(RTLIL::Design *design) override; RTLIL::Module *clone() const override; void loadconfig() const; }; @@ -350,6 +373,20 @@ 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); + + // struct helper exposed from simplify for genrtlil + AstNode *make_struct_member_range(AstNode *node, AstNode *member_node); + + // generate standard $paramod... derived module name; parameters should be + // in the order they are declared in the instantiated module + std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> ¶meters); + + // used to provide simplify() access to the current design for looking up + // modules, ports, wires, etc. + void set_simplify_design_context(const RTLIL::Design *design); } namespace AST_INTERNAL @@ -362,10 +399,24 @@ namespace AST_INTERNAL extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; extern RTLIL::SigSpec ignoreThisSignalsInInitial; extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child; - extern AST::AstModule *current_module; + extern RTLIL::Module *current_module; extern bool current_always_clocked; + extern dict<std::string, int> current_memwr_count; + extern dict<std::string, pool<int>> current_memwr_visible; struct LookaheadRewriter; struct ProcessGenerator; + + // Create and add a new AstModule from new_ast, then use it to replace + // old_module in design, renaming old_module to move it out of the way. + // Return the new module. + // + // If original_ast is not null, it will be used as the AST node for the + // new module. Otherwise, new_ast will be used. + RTLIL::Module * + process_and_replace_module(RTLIL::Design *design, + RTLIL::Module *old_module, + AST::AstNode *new_ast, + AST::AstNode *original_ast = nullptr); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast_binding.cc b/frontends/ast/ast_binding.cc new file mode 100644 index 000000000..c20d1df4d --- /dev/null +++ b/frontends/ast/ast_binding.cc @@ -0,0 +1,49 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "ast_binding.h" +#include "ast.h" + +YOSYS_NAMESPACE_BEGIN + +using namespace AST_INTERNAL; + +AST::Binding::Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name, + const AstNode &cell) + : RTLIL::Binding(target_type, target_name), + ast_node(cell.clone()) +{ + log_assert(cell.type == AST_CELL); +} + +std::string +AST::Binding::describe() const +{ + std::ostringstream oss; + oss << "directive to bind " << ast_node->str + << " to " << target_name.str(); + if (!target_type.empty()) + oss << " (target type: " + << target_type.str() + << ")"; + return oss.str(); +} + +PRIVATE_NAMESPACE_END diff --git a/frontends/ast/ast_binding.h b/frontends/ast/ast_binding.h new file mode 100644 index 000000000..641497d52 --- /dev/null +++ b/frontends/ast/ast_binding.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This header declares the AST::Binding class + * + * This is used to support the bind directive and is to RTLIL::Binding as + * AST::AstModule is to RTLIL::Module, holding a syntax-level representation of + * cells until we get to a stage where they make sense. In the case of a bind + * directive, this is when we elaborate the design in the hierarchy pass. + * + */ + +#ifndef AST_BINDING_H +#define AST_BINDING_H + +#include "kernel/rtlil.h" +#include "kernel/binding.h" + +#include <memory> + +YOSYS_NAMESPACE_BEGIN + +namespace AST +{ + class Binding : public RTLIL::Binding + { + public: + Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name, + const AstNode &cell); + + std::string describe() const override; + + private: + // The syntax-level representation of the cell to be bound. + std::unique_ptr<AstNode> ast_node; + }; +} + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e241142d3..12a7e1183 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -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 e878d0dd2..4c25287ad 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,8 +28,10 @@ #include "kernel/log.h" #include "kernel/utils.h" +#include "kernel/binding.h" #include "libs/sha1/sha1.h" #include "ast.h" +#include "ast_binding.h" #include <sstream> #include <stdarg.h> @@ -45,10 +47,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 +79,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 +106,11 @@ 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) { if (attr.second->type != AST_CONSTANT) @@ -136,10 +141,11 @@ 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) { if (attr.second->type != AST_CONSTANT) @@ -315,16 +321,14 @@ struct AST_INTERNAL::ProcessGenerator LookaheadRewriter la_rewriter(always); // 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); - proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++); + proc = current_module->addProcess(stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++)); + set_src_attr(proc, always); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) log_file_error(always->filename, always->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); proc->attributes[attr.first] = attr.second->asAttrConst(); } - current_module->processes[proc->name] = proc; current_case = &proc->root_case; // create initial temporary signal for all output registers @@ -352,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"); } @@ -395,6 +399,9 @@ struct AST_INTERNAL::ProcessGenerator if (child->type == AST_BLOCK) processAst(child); + for (auto sync: proc->syncs) + processMemWrites(sync); + if (initSyncSignals.size() > 0) { RTLIL::SyncRule *sync = new RTLIL::SyncRule; @@ -452,7 +459,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; @@ -559,7 +566,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_ASSIGN_LE: { RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; - RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap()); + RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), true, &subst_rvalue_map.stdmap()); pool<SigBit> lvalue_sigbits; for (int i = 0; i < GetSize(lvalue); i++) { @@ -586,9 +593,13 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: { + int width_hint; + bool sign_hint; + ast->detectSignWidth(width_hint, sign_hint); + 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); - sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap()); + set_src_attr(sw, ast); + sw->signal = ast->children[0]->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap()); current_case->switches.push_back(sw); for (auto &attr : ast->attributes) { @@ -621,7 +632,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) { @@ -630,7 +641,7 @@ struct AST_INTERNAL::ProcessGenerator else if (node->type == AST_BLOCK) processAst(node); else - current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap())); + current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap())); } if (default_case != current_case) sw->cases.push_back(current_case); @@ -694,8 +705,99 @@ struct AST_INTERNAL::ProcessGenerator log_abort(); } } + + void processMemWrites(RTLIL::SyncRule *sync) + { + // Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array. + dict<std::pair<std::string, int>, int> port_map; + for (auto child : always->children) + if (child->type == AST_MEMWR) + { + std::string memid = child->str; + int portid = child->children[3]->asInt(false); + int cur_idx = GetSize(sync->mem_write_actions); + RTLIL::MemWriteAction action; + set_src_attr(&action, child); + action.memid = memid; + action.address = child->children[0]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap()); + action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, true, &subst_rvalue_map.stdmap()); + action.enable = child->children[2]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap()); + RTLIL::Const orig_priority_mask = child->children[4]->bitsAsConst(); + RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx); + for (int i = 0; i < portid; i++) { + int new_bit = port_map[std::make_pair(memid, i)]; + priority_mask.bits[new_bit] = orig_priority_mask.bits[i]; + } + action.priority_mask = priority_mask; + sync->mem_write_actions.push_back(action); + port_map[std::make_pair(memid, portid)] = cur_idx; + } + } }; +// Generate RTLIL for a bind construct +// +// The AST node will have one or more AST_IDENTIFIER children, which were added +// by bind_target_instance in the parser. After these, it will have one or more +// cells, as parsed by single_cell. These have type AST_CELL. +// +// If there is more than one AST_IDENTIFIER, the first one should be considered +// a module identifier. If there is only one AST_IDENTIFIER, we can't tell at +// this point whether it's a module/interface name or the name of an instance +// because the correct interpretation depends on what's visible at elaboration +// time. For now, we just treat it as a target instance with unknown type, and +// we'll deal with the corner case in the hierarchy pass. +// +// To simplify downstream code, RTLIL::Binding only has a single target and +// single bound instance. If we see the syntax that allows more than one of +// either, we split it into multiple Binding objects. +std::vector<RTLIL::Binding *> AstNode::genBindings() const +{ + // Partition children into identifiers and cells + int num_ids = 0; + for (int i = 0; i < GetSize(children); ++i) { + if (children[i]->type != AST_IDENTIFIER) { + log_assert(i > 0); + num_ids = i; + break; + } + } + + // We should have found at least one child that's not an identifier + log_assert(num_ids > 0); + + // Make sense of the identifiers, extracting a possible type name and a + // list of hierarchical IDs. We represent an unknown type with an empty + // string. + RTLIL::IdString tgt_type; + int first_tgt_inst = 0; + if (num_ids > 1) { + tgt_type = children[0]->str; + first_tgt_inst = 1; + } + + std::vector<RTLIL::Binding *> ret; + + // At this point, we know that children with index >= first_tgt_inst and + // index < num_ids are (hierarchical?) names of target instances. Make a + // binding object for each of them, and fill in the generated instance + // cells each time. + for (int i = first_tgt_inst; i < num_ids; ++i) { + const AstNode &tgt_child = *children[i]; + + for (int j = num_ids; j < GetSize(children); ++j) { + const AstNode &cell_child = *children[j]; + + log_assert(cell_child.type == AST_CELL); + + ret.push_back(new AST::Binding(tgt_type, tgt_child.str, + cell_child)); + } + } + + return ret; +} + // detect sign and width of an expression void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real) { @@ -728,8 +830,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_IDENTIFIER: id_ast = id2ast; - if (id_ast == NULL && current_scope.count(str)) - id_ast = current_scope.at(str); + if (!id_ast) { + if (current_scope.count(str)) + id_ast = current_scope[str]; + else { + std::string alt = try_pop_module_prefix(); + if (current_scope.count(alt)) + id_ast = current_scope[alt]; + } + } if (!id_ast) log_file_error(filename, location.first_line, "Failed to resolve identifier %s for width detection!\n", str.c_str()); if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) { @@ -768,6 +877,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; if (children.size() > 1) range = children[1]; + } else if (id_ast->type == AST_STRUCT_ITEM) { + AstNode *tmp_range = make_struct_member_range(this, id_ast); + this_width = tmp_range->range_left - tmp_range->range_right + 1; + delete tmp_range; } else log_file_error(filename, location.first_line, "Failed to detect width for identifier %s!\n", str.c_str()); if (range) { @@ -819,7 +932,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children.at(0)->type != AST_CONSTANT) log_file_error(filename, location.first_line, "Static cast with non constant expression!\n"); children.at(1)->detectSignWidthWorker(width_hint, sign_hint); - width_hint = children.at(0)->bitsAsConst().as_int(); + this_width = children.at(0)->bitsAsConst().as_int(); + width_hint = max(width_hint, this_width); if (width_hint <= 0) log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); break; @@ -919,6 +1033,40 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun width_hint = max(width_hint, this_width); break; + case AST_CASE: + { + // This detects the _overall_ sign and width to be used for comparing + // the case expression with the case item expressions. The case + // expression and case item expressions are extended to the maximum + // width among them, and are only interpreted as signed if all of them + // are signed. + width_hint = -1; + sign_hint = true; + auto visit_case_expr = [&width_hint, &sign_hint] (AstNode *node) { + int sub_width_hint = -1; + bool sub_sign_hint = true; + node->detectSignWidth(sub_width_hint, sub_sign_hint); + width_hint = max(width_hint, sub_width_hint); + sign_hint &= sub_sign_hint; + }; + visit_case_expr(children[0]); + for (size_t i = 1; i < children.size(); i++) { + AstNode *child = children[i]; + for (AstNode *v : child->children) + if (v->type != AST_DEFAULT && v->type != AST_BLOCK) + visit_case_expr(v); + } + break; + } + + case AST_PREFIX: + // Prefix nodes always resolve to identifiers in generate loops, so we + // can simply perform the resolution to determine the sign and width. + simplify(true, false, false, 1, -1, false, false); + log_assert(type == AST_IDENTIFIER); + detectSignWidthWorker(width_hint, sign_hint, found_real); + break; + case AST_FCALL: if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { if (GetSize(children) == 1) { @@ -940,12 +1088,53 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun } break; } + if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") { + width_hint = 32; + sign_hint = true; + 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. default: + AstNode *current_scope_ast = current_ast_mod == nullptr ? current_ast : current_ast_mod; for (auto f : log_files) - current_ast_mod->dumpAst(f, "verilog-ast> "); + current_scope_ast->dumpAst(f, "verilog-ast> "); log_file_error(filename, location.first_line, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str()); } @@ -961,6 +1150,35 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real if (found_real) *found_real = false; detectSignWidthWorker(width_hint, sign_hint, found_real); + + constexpr int kWidthLimit = 1 << 24; + if (width_hint >= kWidthLimit) + log_file_error(filename, location.first_line, + "Expression width %d exceeds implementation limit of %d!\n", + width_hint, kWidthLimit); +} + +static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id, + const AstNode *node, const char *to_add_kind) +{ + auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) { + std::string src = existing->get_string_attribute(ID::src); + std::string location_str = "earlier"; + if (!src.empty()) + location_str = "at " + src; + log_file_error(node->filename, node->location.first_line, + "Cannot add %s `%s' because a %s with the same name was already created %s!\n", + to_add_kind, id.c_str(), existing_kind, location_str.c_str()); + }; + + if (const RTLIL::Wire *wire = module->wire(id)) + already_exists(wire, "signal"); + if (const RTLIL::Cell *cell = module->cell(id)) + already_exists(cell, "cell"); + if (module->processes.count(id)) + already_exists(module->processes.at(id), "process"); + if (module->memories.count(id)) + already_exists(module->memories.at(id), "memory"); } // create RTLIL from an AST node @@ -1008,8 +1226,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // If a port in a module with unknown type is found, mark it with the attribute 'is_interface' // 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); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "interface port"); + RTLIL::Wire *wire = current_module->addWire(id, 1); + set_src_attr(wire, this); wire->start_offset = 0; wire->port_id = port_id; wire->port_input = true; @@ -1046,10 +1266,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Parameter `%s' with non-constant value!\n", str.c_str()); RTLIL::Const val = children[0]->bitsAsConst(); - RTLIL::Wire *wire = current_module->addWire(str, GetSize(val)); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "pwire"); + RTLIL::Wire *wire = current_module->addWire(id, 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) { @@ -1062,16 +1285,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Wire for an AST_WIRE node case AST_WIRE: { - if (current_module->wires_.count(str) != 0) - log_file_error(filename, location.first_line, "Re-definition of signal `%s'!\n", str.c_str()); if (!range_valid) log_file_error(filename, location.first_line, "Signal `%s' with non-constant width!\n", str.c_str()); if (!(range_left + 1 >= range_right)) 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); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "signal"); + RTLIL::Wire *wire = current_module->addWire(id, range_left - range_right + 1); + set_src_attr(wire, this); wire->start_offset = range_right; wire->port_id = port_id; wire->port_input = is_input; @@ -1092,9 +1315,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Memory for an AST_MEMORY node case AST_MEMORY: { - if (current_module->memories.count(str) != 0) - log_file_error(filename, location.first_line, "Re-definition of memory `%s'!\n", str.c_str()); - log_assert(children.size() >= 2); log_assert(children[0]->type == AST_RANGE); log_assert(children[1]->type == AST_RANGE); @@ -1103,7 +1323,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) { @@ -1113,6 +1333,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) memory->start_offset = children[1]->range_left; memory->size = children[1]->range_right - children[1]->range_left + 1; } + check_unique_id(current_module, memory->name, this, "memory"); current_module->memories[memory->name] = memory; for (auto &attr : attributes) { @@ -1160,9 +1381,17 @@ 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) + + // If we are currently processing a bind directive which wires up + // signals or parameters explicitly, rather than with .*, then + // current_module will start out empty and we don't want to warn the + // user about it: we'll spot broken wiring later, when we run the + // hierarchy pass. + if (dynamic_cast<RTLIL::Binding*>(current_module)) { + /* nothing to do here */ + } else if (flag_autowire) log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str()); else log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); @@ -1302,13 +1531,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // changing the size of signal can be done directly using RTLIL::SigSpec case AST_CAST_SIZE: { RTLIL::SigSpec size = children[0]->genRTLIL(); - RTLIL::SigSpec sig = children[1]->genRTLIL(); if (!size.is_fully_const()) log_file_error(filename, location.first_line, "Static cast with non constant expression!\n"); int width = size.as_int(); if (width <= 0) log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); - sig.extend_u0(width, sign_hint); + // determine the *signedness* of the expression + int sub_width_hint = -1; + bool sub_sign_hint = true; + children[1]->detectSignWidth(sub_width_hint, sub_sign_hint); + // generate the signal given the *cast's* size and the + // *expression's* signedness + RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint); + // context may effect this node's signedness, but not that of the + // casted expression is_signed = sign_hint; return sig; } @@ -1542,13 +1778,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(); @@ -1572,43 +1809,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) return RTLIL::SigSpec(wire); } - // generate $memwr cells for memory write ports - case AST_MEMWR: + // generate $meminit cells case AST_MEMINIT: { std::stringstream sstr; - sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$meminit$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + + SigSpec en_sig = children[2]->genRTLIL(); - 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); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit_v2)); + set_src_attr(cell, this); 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_file_error(filename, location.first_line, "Memory init with non-constant word count!\n"); - num_words = int(children[2]->asInt(false)); - cell->parameters[ID::WORDS] = RTLIL::Const(num_words); - } + if (children[3]->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n"); + int num_words = int(children[3]->asInt(false)); + cell->parameters[ID::WORDS] = RTLIL::Const(num_words); SigSpec addr_sig = children[0]->genRTLIL(); cell->setPort(ID::ADDR, addr_sig); - cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words)); + cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words, true)); + cell->setPort(ID::EN, en_sig); cell->parameters[ID::MEMID] = RTLIL::Const(str); cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig)); cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width); - if (type == AST_MEMWR) { - cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1)); - cell->setPort(ID::EN, children[2]->genRTLIL()); - cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0); - cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0); - } - cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1); } break; @@ -1643,8 +1872,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) else cellname = str; + check_unique_id(current_module, cellname, this, "procedural assertion"); 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) @@ -1661,7 +1891,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_ASSIGN: { RTLIL::SigSpec left = children[0]->genRTLIL(); - RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); + RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size(), true); if (left.has_const()) { RTLIL::SigSpec new_left, new_right; for (int i = 0; i < GetSize(left); i++) @@ -1685,11 +1915,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { int port_counter = 0, para_counter = 0; - if (current_module->count_id(str) != 0) - 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); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "cell"); + RTLIL::Cell *cell = current_module->addCell(id, ""); + 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); @@ -1702,27 +1931,47 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) continue; } if (child->type == AST_PARASET) { - int extra_const_flags = 0; IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; - if (child->children[0]->type == AST_REALVALUE) { + const AstNode *value = child->children[0]; + if (value->type == AST_REALVALUE) log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n", - log_id(cell), log_id(paraname), child->children[0]->realvalue); - extra_const_flags = RTLIL::CONST_FLAG_REAL; - 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_id(cell), log_id(paraname), value->realvalue); + else if (value->type != AST_CONSTANT) log_file_error(filename, location.first_line, "Parameter %s.%s with non-constant value!\n", log_id(cell), log_id(paraname)); - cell->parameters[paraname] = child->children[0]->asParaConst(); - cell->parameters[paraname].flags |= extra_const_flags; + cell->parameters[paraname] = value->asParaConst(); continue; } if (child->type == AST_ARGUMENT) { RTLIL::SigSpec sig; - if (child->children.size() > 0) - sig = child->children[0]->genRTLIL(); + if (child->children.size() > 0) { + AstNode *arg = child->children[0]; + int local_width_hint = -1; + bool local_sign_hint = false; + // don't inadvertently attempt to detect the width of interfaces + if (arg->type != AST_IDENTIFIER || !arg->id2ast || arg->id2ast->type != AST_CELL) + arg->detectSignWidth(local_width_hint, local_sign_hint); + sig = arg->genRTLIL(local_width_hint, local_sign_hint); + log_assert(local_sign_hint == arg->is_signed); + if (sig.is_wire()) { + // if the resulting SigSpec is a wire, its + // signedness should match that of the AstNode + if (arg->type == AST_IDENTIFIER && arg->id2ast && arg->id2ast->is_signed && !arg->is_signed) + // fully-sliced signed wire will be resolved + // once the module becomes available + log_assert(attributes.count(ID::reprocess_after)); + else + log_assert(arg->is_signed == sig.as_wire()->is_signed); + } else if (arg->is_signed) { + // 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, GetSize(sig)); + wire->is_signed = true; + current_module->connect(wire, sig); + sig = wire; + } + } if (child->str.size() == 0) { char buf[100]; snprintf(buf, 100, "$%d", ++port_counter); @@ -1811,6 +2060,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } } break; + case AST_BIND: { + // Read a bind construct. This should have one or more cells as children. + for (RTLIL::Binding *binding : genBindings()) + current_module->add(binding); + break; + } + case AST_FCALL: { if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { @@ -1832,7 +2088,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)) { @@ -1843,7 +2099,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; @@ -1856,8 +2112,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) default: for (auto f : log_files) current_ast_mod->dumpAst(f, "verilog-ast> "); - type_name = type2str(type); - log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str()); + log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type2str(type).c_str()); } return RTLIL::SigSpec(); @@ -1866,14 +2121,14 @@ 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 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) +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr) { const dict<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr; if (new_subst_ptr) genRTLIL_subst_ptr = new_subst_ptr; - bool sign_hint = true; + bool sign_hint = sgn; int width_hint = width; detectSignWidthWorker(width_hint, sign_hint); RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index c4df5c0a0..18b1e1e11 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -89,7 +89,7 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg case 'S': case 'd': case 'D': - if (got_len) + if (got_len && len_value != 0) goto unsupported_format; YS_FALLTHROUGH case 'x': @@ -110,6 +110,12 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg goto unsupported_format; break; + case 'l': + case 'L': + if (got_len) + goto unsupported_format; + break; + default: unsupported_format: log_file_error(filename, location.first_line, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); @@ -155,6 +161,11 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg sout += log_id(current_module->name); break; + case 'l': + case 'L': + sout += log_id(current_module->name); + break; + default: log_abort(); } @@ -181,6 +192,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node) log_assert(current_scope.count(enum_type) == 1); AstNode *enum_node = current_scope.at(enum_type); log_assert(enum_node->type == AST_ENUM); + while (enum_node->simplify(true, false, false, 1, -1, false, true)) { } //get width from 1st enum item: log_assert(enum_node->children.size() >= 1); AstNode *enum_item0 = enum_node->children[0]; @@ -319,6 +331,8 @@ static int size_packed_struct(AstNode *snode, int base_offset) } } // range nodes are now redundant + for (AstNode *child : node->children) + delete child; node->children.clear(); } else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { @@ -333,6 +347,8 @@ static int size_packed_struct(AstNode *snode, int base_offset) save_struct_array_width(node, width); width *= array_count; // range nodes are now redundant + for (AstNode *child : node->children) + delete child; node->children.clear(); } else if (node->range_left < 0) { @@ -380,32 +396,67 @@ static int size_packed_struct(AstNode *snode, int base_offset) static AstNode *node_int(int ival) { - // maybe mkconst_int should have default values for the common integer case - return AstNode::mkconst_int(ival, true, 32); + return AstNode::mkconst_int(ival, true); } -static AstNode *offset_indexed_range(int offset_right, int stride, AstNode *left_expr, AstNode *right_expr) +static AstNode *multiply_by_const(AstNode *expr_node, int stride) +{ + return new AstNode(AST_MUL, expr_node, node_int(stride)); +} + +static AstNode *offset_indexed_range(int offset, int stride, AstNode *left_expr, AstNode *right_expr) { // adjust the range expressions to add an offset into the struct // and maybe index using an array stride auto left = left_expr->clone(); auto right = right_expr->clone(); - if (stride == 1) { - // just add the offset - left = new AstNode(AST_ADD, node_int(offset_right), left); - right = new AstNode(AST_ADD, node_int(offset_right), right); + if (stride > 1) { + // newleft = (left + 1) * stride - 1 + left = new AstNode(AST_SUB, multiply_by_const(new AstNode(AST_ADD, left, node_int(1)), stride), node_int(1)); + // newright = right * stride + right = multiply_by_const(right, stride); + } + // add the offset + if (offset) { + left = new AstNode(AST_ADD, node_int(offset), left); + right = new AstNode(AST_ADD, node_int(offset), right); + } + return new AstNode(AST_RANGE, left, right); +} + +static AstNode *make_struct_index_range(AstNode *node, AstNode *rnode, int stride, int offset) +{ + // generate a range node to perform either bit or array indexing + if (rnode->children.size() == 1) { + // index e.g. s.a[i] + return offset_indexed_range(offset, stride, rnode->children[0], rnode->children[0]); + } + else if (rnode->children.size() == 2) { + // slice e.g. s.a[i:j] + return offset_indexed_range(offset, stride, rnode->children[0], rnode->children[1]); } else { - // newleft = offset_right - 1 + (left + 1) * stride - left = new AstNode(AST_ADD, new AstNode(AST_SUB, node_int(offset_right), node_int(1)), - new AstNode(AST_MUL, node_int(stride), new AstNode(AST_ADD, left, node_int(1)))); - // newright = offset_right + right * stride - right = new AstNode(AST_ADD, node_int(offset_right), new AstNode(AST_MUL, right, node_int(stride))); + struct_op_error(node); } +} + +static AstNode *slice_range(AstNode *rnode, AstNode *snode) +{ + // apply the bit slice indicated by snode to the range rnode + log_assert(rnode->type==AST_RANGE); + auto left = rnode->children[0]; + auto right = rnode->children[1]; + log_assert(snode->type==AST_RANGE); + auto slice_left = snode->children[0]; + auto slice_right = snode->children[1]; + auto width = new AstNode(AST_SUB, slice_left->clone(), slice_right->clone()); + right = new AstNode(AST_ADD, right->clone(), slice_right->clone()); + left = new AstNode(AST_ADD, right->clone(), width); return new AstNode(AST_RANGE, left, right); } -static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node) + +AstNode *AST::make_struct_member_range(AstNode *node, AstNode *member_node) { // Work out the range in the packed array that corresponds to a struct member // taking into account any range operations applicable to the current node @@ -414,27 +465,26 @@ static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node) int range_right = member_node->range_right; if (node->children.empty()) { // no range operations apply, return the whole width + return make_range(range_left, range_right); } - else if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { - auto rnode = node->children[0]; - int stride = get_struct_array_width(member_node); - if (rnode->children.size() == 1) { - // index e.g. s.a[i] - return offset_indexed_range(range_right, stride, rnode->children[0], rnode->children[0]); - } - else if (rnode->children.size() == 2) { - // slice e.g. s.a[i:j] - return offset_indexed_range(range_right, stride, rnode->children[0], rnode->children[1]); - } - else { - struct_op_error(node); - } + int stride = get_struct_array_width(member_node); + if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { + // bit or array indexing e.g. s.a[2] or s.a[1:0] + return make_struct_index_range(node, node->children[0], stride, range_right); + } + else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { + // multirange, i.e. bit slice after array index, e.g. s.a[i][p:q] + log_assert(stride > 1); + auto mrnode = node->children[0]; + auto element_range = make_struct_index_range(node, mrnode->children[0], stride, range_right); + // then apply bit slice range + auto range = slice_range(element_range, mrnode->children[1]); + delete element_range; + return range; } else { - // TODO multirange, i.e. bit slice after array index s.a[i][p:q] struct_op_error(node); } - return make_range(range_left, range_right); } static void add_members_to_scope(AstNode *snode, std::string name) @@ -483,6 +533,258 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name) return wnode; } +// check if a node or its children contains an assignment to the given variable +static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) +{ + if (node->type == AST_ASSIGN_EQ || node->type == AST_ASSIGN_LE) { + // current node is iteslf an assignment + log_assert(node->children.size() >= 2); + const AstNode* lhs = node->children[0]; + if (lhs->type == AST_IDENTIFIER && lhs->str == var->str) + return false; + } + for (const AstNode* child : node->children) { + // if this child shadows the given variable + if (child != var && child->str == var->str && child->type == AST_WIRE) + break; // skip the remainder of this block/scope + // depth-first short circuit + if (!node_contains_assignment_to(child, var)) + return false; + } + 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; +} + +// direct access to this global should be limited to the following two functions +static const RTLIL::Design *simplify_design_context = nullptr; + +void AST::set_simplify_design_context(const RTLIL::Design *design) +{ + log_assert(!simplify_design_context || !design); + simplify_design_context = design; +} + +// lookup the module with the given name in the current design context +static const RTLIL::Module* lookup_module(const std::string &name) +{ + return simplify_design_context->module(name); +} + +const RTLIL::Module* AstNode::lookup_cell_module() +{ + log_assert(type == AST_CELL); + + auto reprocess_after = [this] (const std::string &modname) { + if (!attributes.count(ID::reprocess_after)) + attributes[ID::reprocess_after] = AstNode::mkconst_str(modname); + }; + + const AstNode *celltype = nullptr; + for (const AstNode *child : children) + if (child->type == AST_CELLTYPE) { + celltype = child; + break; + } + log_assert(celltype != nullptr); + + const RTLIL::Module *module = lookup_module(celltype->str); + if (!module) + module = lookup_module("$abstract" + celltype->str); + if (!module) { + if (celltype->str.at(0) != '$') + reprocess_after(celltype->str); + return nullptr; + } + + // build a mapping from true param name to param value + size_t para_counter = 0; + dict<RTLIL::IdString, RTLIL::Const> cell_params_map; + for (AstNode *child : children) { + if (child->type != AST_PARASET) + continue; + + if (child->str.empty() && para_counter >= module->avail_parameters.size()) + return nullptr; // let hierarchy handle this error + IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str; + + const AstNode *value = child->children[0]; + if (value->type != AST_REALVALUE && value->type != AST_CONSTANT) + return nullptr; // let genrtlil handle this error + cell_params_map[paraname] = value->asParaConst(); + } + + // put the parameters in order and generate the derived module name + std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters; + for (RTLIL::IdString param : module->avail_parameters) { + auto it = cell_params_map.find(param); + if (it != cell_params_map.end()) + named_parameters.emplace_back(it->first, it->second); + } + std::string modname = celltype->str; + if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams + modname = derived_module_name(celltype->str, named_parameters); + + // try to find the resolved module + module = lookup_module(modname); + if (!module) { + reprocess_after(modname); + return nullptr; + } + return module; +} + +// returns whether an expression contains an unbased unsized literal; does not +// check the literal exists in a self-determined context +static bool contains_unbased_unsized(const AstNode *node) +{ + if (node->type == AST_CONSTANT) + return node->is_unsized; + for (const AstNode *child : node->children) + if (contains_unbased_unsized(child)) + return true; + return false; +} + +// adds a wire to the current module with the given name that matches the +// dimensions of the given wire reference +void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str) +{ + AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true); + AstNode *right = AstNode::mkconst_int(ref->start_offset, true); + if (ref->upto) + std::swap(left, right); + AstNode *range = new AstNode(AST_RANGE, left, right); + + AstNode *wire = new AstNode(AST_WIRE, range); + wire->is_signed = ref->is_signed; + wire->is_logic = true; + wire->str = str; + + current_ast_mod->children.push_back(wire); + current_scope[str] = wire; +} + +enum class IdentUsage { + NotReferenced, // target variable is neither read or written in the block + Assigned, // target variable is always assigned before use + SyncRequired, // target variable may be used before it has been assigned +}; + +// determines whether a local variable a block is always assigned before it is +// used, meaning the nosync attribute can automatically be added to that +// variable +static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target) +{ + // This variable has been referenced before it has necessarily been assigned + // a value in this procedure. + if (node->type == AST_IDENTIFIER && node->str == target) + return IdentUsage::SyncRequired; + + // For case statements (which are also used for if/else), we check each + // possible branch. If the variable is assigned in all branches, then it is + // assigned, and a sync isn't required. If it used before assignment in any + // branch, then a sync is required. + if (node->type == AST_CASE) { + bool all_defined = true; + bool any_used = false; + bool has_default = false; + for (const AstNode *child : node->children) { + if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT) + has_default = true; + IdentUsage nested = always_asgn_before_use(child, target); + if (nested != IdentUsage::Assigned && child->type == AST_COND) + all_defined = false; + if (nested == IdentUsage::SyncRequired) + any_used = true; + } + if (any_used) + return IdentUsage::SyncRequired; + else if (all_defined && has_default) + return IdentUsage::Assigned; + else + return IdentUsage::NotReferenced; + } + + // Check if this is an assignment to the target variable. For simplicity, we + // don't analyze sub-ranges of the variable. + if (node->type == AST_ASSIGN_EQ) { + const AstNode *ident = node->children.at(0); + if (ident->type == AST_IDENTIFIER && ident->str == target) + return IdentUsage::Assigned; + } + + for (const AstNode *child : node->children) { + IdentUsage nested = always_asgn_before_use(child, target); + if (nested != IdentUsage::NotReferenced) + return nested; + } + return IdentUsage::NotReferenced; +} + +static const std::string auto_nosync_prefix = "\\AutoNosync"; + +// mark a local variable in an always_comb block for automatic nosync +// consideration +static void mark_auto_nosync(AstNode *block, const AstNode *wire) +{ + log_assert(block->type == AST_BLOCK); + log_assert(wire->type == AST_WIRE); + block->attributes[auto_nosync_prefix + wire->str] = AstNode::mkconst_int(1, + false); +} + +// check a procedural block for auto-nosync markings, remove them, and add +// nosync to local variables as necessary +static void check_auto_nosync(AstNode *node) +{ + std::vector<RTLIL::IdString> attrs_to_drop; + for (const auto& elem : node->attributes) { + // skip attributes that don't begin with the prefix + if (elem.first.compare(0, auto_nosync_prefix.size(), + auto_nosync_prefix.c_str())) + continue; + + // delete and remove the attribute once we're done iterating + attrs_to_drop.push_back(elem.first); + + // find the wire based on the attribute + std::string wire_name = elem.first.substr(auto_nosync_prefix.size()); + auto it = current_scope.find(wire_name); + if (it == current_scope.end()) + continue; + + // analyze the usage of the local variable in this block + IdentUsage ident_usage = always_asgn_before_use(node, wire_name); + if (ident_usage != IdentUsage::Assigned) + continue; + + // mark the wire with `nosync` + AstNode *wire = it->second; + log_assert(wire->type == AST_WIRE); + wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + } + + // remove the attributes we've "consumed" + for (const RTLIL::IdString &str : attrs_to_drop) { + auto it = node->attributes.find(str); + delete it->second; + node->attributes.erase(it); + } + + // check local variables in any nested blocks + for (AstNode *child : node->children) + check_auto_nosync(child); +} + // 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(). @@ -495,10 +797,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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"); + log_warning("Deep recursion in AST simplifier.\nDoes this design contain overly long or deeply nested expressions, or excessive recursion?\n"); deep_recursion_warning = false; } + static bool unevaluated_tern_branch = false; + AstNode *newNode = NULL; bool did_something = false; @@ -682,6 +986,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]; @@ -785,6 +1092,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } } + + for (AstNode *child : children) + if (child->type == AST_ALWAYS && + child->attributes.count(ID::always_comb)) + check_auto_nosync(child); } // create name resolution entries for all objects with names @@ -793,7 +1105,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; // these nodes appear at the top level in a package and can define names - if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF) { + if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF || node->type == AST_FUNCTION || node->type == AST_TASK) { current_scope[node->str] = node; } if (node->type == AST_ENUM) { @@ -834,6 +1146,113 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (type == AST_CELL) { + bool lookup_suggested = false; + + for (AstNode *child : children) { + // simplify any parameters to constants + if (child->type == AST_PARASET) + while (child->simplify(true, false, false, 1, -1, false, true)) { } + + // look for patterns which _may_ indicate ambiguity requiring + // resolution of the underlying module + if (child->type == AST_ARGUMENT) { + if (child->children.size() != 1) + continue; + const AstNode *value = child->children[0]; + if (value->type == AST_IDENTIFIER) { + const AstNode *elem = value->id2ast; + if (elem == nullptr) { + if (current_scope.count(value->str)) + elem = current_scope.at(value->str); + else + continue; + } + if (elem->type == AST_MEMORY) + // need to determine is the is a read or wire + lookup_suggested = true; + else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty()) + // this may be a fully sliced signed wire which needs + // to be indirected to produce an unsigned connection + lookup_suggested = true; + } + else if (contains_unbased_unsized(value)) + // unbased unsized literals extend to width of the context + lookup_suggested = true; + } + } + + const RTLIL::Module *module = nullptr; + if (lookup_suggested) + module = lookup_cell_module(); + if (module) { + size_t port_counter = 0; + for (AstNode *child : children) { + if (child->type != AST_ARGUMENT) + continue; + + // determine the full name of port this argument is connected to + RTLIL::IdString port_name; + if (child->str.size()) + port_name = child->str; + else { + if (port_counter >= module->ports.size()) + log_file_error(filename, location.first_line, + "Cell instance has more ports than the module!\n"); + port_name = module->ports[port_counter++]; + } + + // find the port's wire in the underlying module + const RTLIL::Wire *ref = module->wire(port_name); + if (ref == nullptr) + log_file_error(filename, location.first_line, + "Cell instance refers to port %s which does not exist in module %s!.\n", + log_id(port_name), log_id(module->name)); + + // select the argument, if present + log_assert(child->children.size() <= 1); + if (child->children.empty()) + continue; + AstNode *arg = child->children[0]; + + // plain identifiers never need indirection; this also prevents + // adding infinite levels of indirection + if (arg->type == AST_IDENTIFIER && arg->children.empty()) + continue; + + // only add indirection for standard inputs or outputs + if (ref->port_input == ref->port_output) + continue; + + did_something = true; + + // create the indirection wire + std::stringstream sstr; + sstr << "$indirect$" << ref->name.c_str() << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + std::string tmp_str = sstr.str(); + add_wire_for_ref(ref, tmp_str); + + AstNode *asgn = new AstNode(AST_ASSIGN); + current_ast_mod->children.push_back(asgn); + + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = tmp_str; + child->children[0] = ident->clone(); + + if (ref->port_input && !ref->port_output) { + asgn->children.push_back(ident); + asgn->children.push_back(arg); + } else { + log_assert(!ref->port_input && ref->port_output); + asgn->children.push_back(arg); + asgn->children.push_back(ident); + } + } + + + } + } + int backup_width_hint = width_hint; bool backup_sign_hint = sign_hint; @@ -862,7 +1281,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) { @@ -876,7 +1295,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; } @@ -1012,7 +1431,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; @@ -1045,6 +1463,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; @@ -1070,8 +1506,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (const_fold && type == AST_CASE) { + detectSignWidth(width_hint, sign_hint); while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { + RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint); std::vector<AstNode*> new_children; new_children.push_back(children[0]); for (int i = 1; i < GetSize(children); i++) { @@ -1084,7 +1522,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, continue; while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } if (v->type == AST_CONSTANT && v->bits_only_01()) { - if (v->bits == children[0]->bits) { + RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint); + RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1); + log_assert(match.bits.size() == 1); + if (match.bits.front() == RTLIL::State::S1) { while (i+1 < GetSize(children)) delete children[++i]; goto keep_const_cond; @@ -1103,11 +1544,20 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + dict<std::string, pool<int>> backup_memwr_visible; + dict<std::string, pool<int>> final_memwr_visible; + + if (type == AST_CASE && stage == 2) { + backup_memwr_visible = current_memwr_visible; + final_memwr_visible = current_memwr_visible; + } + // simplify all children first // (iterate by index as e.g. auto wires can add new children in the process) for (size_t i = 0; i < children.size(); i++) { bool did_something_here = true; 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) @@ -1120,6 +1570,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; @@ -1159,11 +1613,26 @@ 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; + if (stage == 2 && type == AST_CASE) { + for (auto &x : current_memwr_visible) { + for (int y : x.second) + final_memwr_visible[x.first].insert(y); + } + current_memwr_visible = backup_memwr_visible; + } } for (auto &attr : attributes) { while (attr.second->simplify(true, false, false, stage, -1, false, true)) did_something = true; } + if (type == AST_CASE && stage == 2) { + current_memwr_visible = final_memwr_visible; + } + if (type == AST_ALWAYS && stage == 2) { + current_memwr_visible.clear(); + current_memwr_count.clear(); + } if (reset_width_after_children) { width_hint = backup_width_hint; @@ -1259,6 +1728,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) { // replace with wire representing the packed structure newNode = make_packed_struct(template_node, str); + newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + // add original input/output attribute to resolved wire + newNode->is_input = this->is_input; + newNode->is_output = this->is_output; current_scope[str] = this; goto apply_newNode; } @@ -1304,18 +1777,33 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (is_custom_type) { log_assert(children.size() == 2); log_assert(children[1]->type == AST_WIRETYPE); - if (!current_scope.count(children[1]->str)) - log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str()); - AstNode *resolved_type_node = current_scope.at(children[1]->str); + auto type_name = children[1]->str; + if (!current_scope.count(type_name)) { + log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str()); + } + AstNode *resolved_type_node = current_scope.at(type_name); if (resolved_type_node->type != AST_TYPEDEF) - log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str()); + log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str()); log_assert(resolved_type_node->children.size() == 1); AstNode *template_node = resolved_type_node->children[0]; - delete children[1]; - children.pop_back(); // Ensure typedef itself is fully simplified - while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; + while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; + + if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) { + // replace with wire representing the packed structure + newNode = make_packed_struct(template_node, str); + newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + newNode->type = type; + current_scope[str] = this; + // copy param value, it needs to be 1st value + delete children[1]; + children.pop_back(); + newNode->children.insert(newNode->children.begin(), children[0]->clone()); + goto apply_newNode; + } + delete children[1]; + children.pop_back(); if (template_node->type == AST_MEMORY) log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str()); @@ -1438,11 +1926,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { int total_size = 1; multirange_dimensions.clear(); + multirange_swapped.clear(); for (auto range : children[1]->children) { if (!range->range_valid) log_file_error(filename, location.first_line, "Non-constant range on memory decl.\n"); 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); + multirange_swapped.push_back(range->range_swapped); total_size *= multirange_dimensions.back(); } delete children[1]; @@ -1455,9 +1945,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { AstNode *index_expr = nullptr; + integer = children[0]->children.size(); // save original number of dimensions for $size() etc. for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++) { - if (GetSize(children[0]->children) < i) + if (GetSize(children[0]->children) <= i) log_file_error(filename, location.first_line, "Insufficient number of array indices for %s.\n", log_id(str)); AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); @@ -1533,6 +2024,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, newNode = new AstNode(AST_IDENTIFIER, range); newNode->str = sname; newNode->basic_prep = true; + if (item_node->is_signed) + newNode = new AstNode(AST_TO_SIGNED, newNode); goto apply_newNode; } } @@ -1542,6 +2035,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_IDENTIFIER) { if (current_scope.count(str) == 0) { AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; + str = try_pop_module_prefix(); for (auto node : current_scope_ast->children) { //log("looking at mod scope child %s\n", type2str(node->type).c_str()); switch (node->type) { @@ -1595,7 +2089,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } // split memory access with bit select to individual statements - if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue) + if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2) { if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1) log_file_error(filename, location.first_line, "Invalid bit-select on memory access!\n"); @@ -1646,6 +2140,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, newNode = new AstNode(AST_IDENTIFIER, children[1]->clone()); newNode->str = wire_id; + newNode->integer = integer; // save original number of dimensions for $size() etc. newNode->id2ast = wire; goto apply_newNode; } @@ -1687,25 +2182,27 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK) body_ast = body_ast->children.at(0); + const char* loop_type_str = "procedural"; + const char* var_type_str = "register"; + AstNodeType var_type = AST_WIRE; + if (type == AST_GENFOR) { + loop_type_str = "generate"; + var_type_str = "genvar"; + var_type = AST_GENVAR; + } + if (init_ast->type != AST_ASSIGN_EQ) - log_file_error(filename, location.first_line, "Unsupported 1st expression of generate for-loop!\n"); + log_file_error(filename, location.first_line, "Unsupported 1st expression of %s for-loop!\n", loop_type_str); if (next_ast->type != AST_ASSIGN_EQ) - log_file_error(filename, location.first_line, "Unsupported 3rd expression of generate for-loop!\n"); + log_file_error(filename, location.first_line, "Unsupported 3rd expression of %s for-loop!\n", loop_type_str); - if (type == AST_GENFOR) { - if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR) - log_file_error(filename, location.first_line, "Left hand side of 1st expression of generate for-loop is not a gen var!\n"); - if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR) - log_file_error(filename, location.first_line, "Left hand side of 3rd expression of generate for-loop is not a gen var!\n"); - } else { - if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE) - log_file_error(filename, location.first_line, "Left hand side of 1st expression of generate for-loop is not a register!\n"); - if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE) - log_file_error(filename, location.first_line, "Left hand side of 3rd expression of generate for-loop is not a register!\n"); - } + if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != var_type) + log_file_error(filename, location.first_line, "Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); + if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != var_type) + log_file_error(filename, location.first_line, "Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast) - log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of generate for-loop!\n"); + log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str); // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); @@ -1717,7 +2214,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (varbuf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right hand side of 1st expression of generate for-loop is not constant!\n"); + log_file_error(filename, location.first_line, "Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str); auto resolved = current_scope.at(init_ast->children[0]->str); if (resolved->range_valid) { @@ -1758,7 +2255,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "2nd expression of generate for-loop is not constant!\n"); + log_file_error(filename, location.first_line, "2nd expression of %s for-loop is not constant!\n", loop_type_str); if (buf->integer == 0) { delete buf; @@ -1768,23 +2265,28 @@ 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++) { - buf->children[i]->simplify(false, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } } else { @@ -1804,7 +2306,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right hand side of 3rd expression of generate for-loop is not constant (%s)!\n", type2str(buf->type).c_str()); + log_file_error(filename, location.first_line, "Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str()); delete varbuf->children[0]; varbuf->children[0] = buf; @@ -1828,14 +2330,26 @@ 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 + "."); + + // if this is an autonamed block is in an always_comb + if (current_always && current_always->attributes.count(ID::always_comb) + && str.at(0) == '$') + // track local variables in this block so we can consider adding + // nosync once the block has been fully elaborated + for (AstNode *child : children) + if (child->type == AST_WIRE && + !child->attributes.count(ID::nosync)) + mark_auto_nosync(this, child); std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) @@ -1855,12 +2369,11 @@ 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++) { - children[i]->simplify(false, false, false, stage, -1, false, false); + children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(children[i]); } @@ -1892,12 +2405,11 @@ 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++) { - buf->children[i]->simplify(false, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -1971,12 +2483,11 @@ 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++) { - buf->children[i]->simplify(false, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -2056,6 +2567,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children.push_back(node); did_something = true; } + else if (str == "buf" || str == "not") + { + AstNode *input = children_list.back(); + if (str == "not") + input = new AstNode(AST_BIT_NOT, input); + + newNode = new AstNode(AST_GENBLOCK); + for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) { + newNode->children.push_back(new AstNode(AST_ASSIGN, *it, input->clone())); + newNode->children.back()->was_checked = true; + } + delete input; + + did_something = true; + } else { AstNodeType op_type = AST_NONE; @@ -2073,10 +2599,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, op_type = AST_BIT_XOR; if (str == "xnor") op_type = AST_BIT_XOR, invert_results = true; - if (str == "buf") - op_type = AST_POS; - if (str == "not") - op_type = AST_POS, invert_results = true; log_assert(op_type != AST_NONE); AstNode *node = children_list[1]; @@ -2128,6 +2650,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; + delete left_at_zero_ast; + delete right_at_zero_ast; } bool use_case_method = false; @@ -2430,12 +2954,12 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_addr->str] = wire_addr; while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } - AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false)); + AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false)); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; defNode->children.push_back(assign_addr); - assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; newNode->children.push_back(assign_addr); @@ -2456,7 +2980,7 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_data->str] = wire_data; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); + AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; defNode->children.push_back(assign_data); @@ -2465,25 +2989,20 @@ skip_dynamic_range_lvalue_expansion:; node_data->str = id_data; } - AstNode *node_en = nullptr; - if (current_always->type == AST_INITIAL) { - node_en = AstNode::mkconst_int(1, false); - } else { - 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; - wire_en->was_checked = true; - 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 *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - defNode->children.push_back(assign_en); + 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; + wire_en->was_checked = true; + 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)) { } - node_en = new AstNode(AST_IDENTIFIER); - node_en->str = id_en; - } + AstNode *assign_en_first = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + assign_en_first->children[0]->str = id_en; + assign_en_first->children[0]->was_checked = true; + defNode->children.push_back(assign_en_first); + + AstNode *node_en = new AstNode(AST_IDENTIFIER); + node_en->str = id_en; if (!defNode->children.empty()) current_top_block->children.insert(current_top_block->children.begin(), defNode); @@ -2502,18 +3021,16 @@ skip_dynamic_range_lvalue_expansion:; std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx); - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), + assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone())); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; - 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; - assign_en->children[0]->was_checked = true; - } + 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_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; } else { @@ -2531,19 +3048,17 @@ skip_dynamic_range_lvalue_expansion:; log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), + assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; - 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; - assign_en->children[0]->was_checked = true; - } + 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_EQ, 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; + assign_en->children[0]->was_checked = true; delete left_at_zero_ast; delete right_at_zero_ast; @@ -2553,26 +3068,42 @@ skip_dynamic_range_lvalue_expansion:; else { if (!(children[0]->children.size() == 1 && children[1]->isConst())) { - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); + assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; } - 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; - assign_en->children[0]->was_checked = true; - } + assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; } if (assign_data) newNode->children.push_back(assign_data); if (assign_en) newNode->children.push_back(assign_en); - AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en); + AstNode *wrnode; + if (current_always->type == AST_INITIAL) + wrnode = new AstNode(AST_MEMINIT, node_addr, node_data, node_en, mkconst_int(1, false)); + else + wrnode = new AstNode(AST_MEMWR, node_addr, node_data, node_en); wrnode->str = children[0]->str; wrnode->id2ast = children[0]->id2ast; - current_ast_mod->children.push_back(wrnode); + wrnode->location = location; + if (wrnode->type == AST_MEMWR) { + int portid = current_memwr_count[wrnode->str]++; + wrnode->children.push_back(mkconst_int(portid, false)); + std::vector<RTLIL::State> priority_mask; + for (int i = 0; i < portid; i++) { + bool has_prio = current_memwr_visible[wrnode->str].count(i); + priority_mask.push_back(State(has_prio)); + } + wrnode->children.push_back(mkconst_bits(priority_mask, false)); + current_memwr_visible[wrnode->str].insert(portid); + current_always->children.push_back(wrnode); + } else { + current_ast_mod->children.push_back(wrnode); + } if (newNode->children.empty()) { delete newNode; @@ -2756,26 +3287,28 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (str == "\\$size" || str == "\\$bits") + if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") { - if (str == "\\$bits" && children.size() != 1) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); - - if (str == "\\$size" && children.size() != 1 && children.size() != 2) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1 or 2.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); - int dim = 1; - if (str == "\\$size" && children.size() == 2) { - AstNode *buf = children[1]->clone(); - // Evaluate constant expression - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - dim = buf->asInt(false); - delete buf; + if (str == "\\$bits") { + if (children.size() != 1) + log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + } else { + if (children.size() != 1 && children.size() != 2) + log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1 or 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + if (children.size() == 2) { + AstNode *buf = children[1]->clone(); + // Evaluate constant expression + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + dim = buf->asInt(false); + delete buf; + } } AstNode *buf = children[0]->clone(); int mem_depth = 1; + int result, high = 0, low = 0, left = 0, right = 0, width = 1; // defaults for a simple wire AstNode *id_ast = NULL; // Is this needed? @@ -2788,6 +3321,31 @@ skip_dynamic_range_lvalue_expansion:; id_ast = current_scope.at(buf->str); if (!id_ast) log_file_error(filename, location.first_line, "Failed to resolve identifier %s for width detection!\n", buf->str.c_str()); + // a slice of our identifier means we advance to the next dimension, e.g. $size(a[3]) + if (buf->children.size() > 0) { + // something is hanging below this identifier + if (buf->children[0]->type == AST_RANGE && buf->integer == 0) + // if integer == 0, this node was originally created as AST_RANGE so it's dimension is 1 + dim++; + // more than one range, e.g. $size(a[3][2]) + else // created an AST_MULTIRANGE, converted to AST_RANGE, but original dimension saved in 'integer' field + dim += buf->integer; // increment by multirange size + } + // We have 4 cases: + // wire x; ==> AST_WIRE, no AST_RANGE children + // wire [1:0]x; ==> AST_WIRE, AST_RANGE children + // wire [1:0]x[1:0]; ==> AST_MEMORY, two AST_RANGE children (1st for packed, 2nd for unpacked) + // wire [1:0]x[1:0][1:0]; ==> AST_MEMORY, one AST_RANGE child (0) for packed, then AST_MULTIRANGE child (1) for unpacked + // (updated: actually by the time we are here, AST_MULTIRANGE is converted into one big AST_RANGE) + // case 0 handled by default + if ((id_ast->type == AST_WIRE || id_ast->type == AST_MEMORY) && id_ast->children.size() > 0) { + // handle packed array left/right for case 1, and cases 2/3 when requesting the last dimension (packed side) + AstNode *wire_range = id_ast->children[0]; + left = wire_range->children[0]->integer; + right = wire_range->children[1]->integer; + high = max(left, right); + low = min(left, right); + } if (id_ast->type == AST_MEMORY) { // We got here only if the argument is a memory // Otherwise $size() and $bits() return the expression width @@ -2800,29 +3358,58 @@ skip_dynamic_range_lvalue_expansion:; } else log_file_error(filename, location.first_line, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); } else { - // $size() + // $size(), $left(), $right(), $high(), $low() + int dims = 1; if (mem_range->type == AST_RANGE) { - if (!mem_range->range_valid) - log_file_error(filename, location.first_line, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); - int dims; - if (id_ast->multirange_dimensions.empty()) - dims = 1; - else + if (id_ast->multirange_dimensions.empty()) { + if (!mem_range->range_valid) + log_file_error(filename, location.first_line, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); + if (dim == 1) { + left = mem_range->range_right; + right = mem_range->range_left; + high = max(left, right); + low = min(left, right); + } + } else { dims = GetSize(id_ast->multirange_dimensions)/2; - if (dim == 1) - width_hint = (dims > 1) ? id_ast->multirange_dimensions[1] : (mem_range->range_left - mem_range->range_right + 1); - else if (dim <= dims) { - width_hint = id_ast->multirange_dimensions[2*dim-1]; - } else if ((dim > dims+1) || (dim < 0)) - log_file_error(filename, location.first_line, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1); - } else + if (dim <= dims) { + width_hint = id_ast->multirange_dimensions[2*dim-1]; + high = id_ast->multirange_dimensions[2*dim-2] + id_ast->multirange_dimensions[2*dim-1] - 1; + low = id_ast->multirange_dimensions[2*dim-2]; + if (id_ast->multirange_swapped[dim-1]) { + left = low; + right = high; + } else { + right = low; + left = high; + } + } else if ((dim > dims+1) || (dim < 0)) + log_file_error(filename, location.first_line, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1); + } + } else { log_file_error(filename, location.first_line, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); + } } } + width = high - low + 1; + } else { + width = width_hint; } delete buf; - - newNode = mkconst_int(width_hint * mem_depth, false); + if (str == "\\$high") + result = high; + else if (str == "\\$low") + result = low; + else if (str == "\\$left") + result = left; + else if (str == "\\$right") + result = right; + else if (str == "\\$size") + result = width; + else { + result = width * mem_depth; + } + newNode = mkconst_int(result, false); goto apply_newNode; } @@ -2909,6 +3496,93 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (str == "\\$countbits") { + if (children.size() < 2) + log_file_error(filename, location.first_line, "System function %s got %d arguments, expected at least 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + std::vector<RTLIL::State> control_bits; + + // Determine which bits to count + for (size_t i = 1; i < children.size(); i++) { + AstNode *node = children[i]; + while (node->simplify(true, false, false, stage, -1, false, false)) { } + if (node->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant control bit argument.\n", str.c_str()); + if (node->bits.size() != 1) + log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with control bit width != 1.\n", str.c_str()); + control_bits.push_back(node->bits[0]); + } + + // Detect width of exp (first argument of $countbits) + int exp_width = -1; + bool exp_sign = false; + AstNode *exp = children[0]; + exp->detectSignWidth(exp_width, exp_sign, NULL); + + newNode = mkconst_int(0, false); + + for (int i = 0; i < exp_width; i++) { + // Generate nodes for: exp << i >> ($size(exp) - 1) + // ^^ ^^ + AstNode *lsh_node = new AstNode(AST_SHIFT_LEFT, exp->clone(), mkconst_int(i, false)); + AstNode *rsh_node = new AstNode(AST_SHIFT_RIGHT, lsh_node, mkconst_int(exp_width - 1, false)); + + AstNode *or_node = nullptr; + + for (RTLIL::State control_bit : control_bits) { + // Generate node for: (exp << i >> ($size(exp) - 1)) === control_bit + // ^^^ + AstNode *eq_node = new AstNode(AST_EQX, rsh_node->clone(), mkconst_bits({control_bit}, false)); + + // Or the result for each checked bit value + if (or_node) + or_node = new AstNode(AST_LOGIC_OR, or_node, eq_node); + else + or_node = eq_node; + } + + // We should have at least one element in control_bits, + // because we checked for the number of arguments above + log_assert(or_node != nullptr); + + delete rsh_node; + + // Generate node for adding with result of previous bit + newNode = new AstNode(AST_ADD, newNode, or_node); + } + + goto apply_newNode; + } + + if (str == "\\$countones" || str == "\\$isunknown" || str == "\\$onehot" || str == "\\$onehot0") { + if (children.size() != 1) + log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + AstNode *countbits = clone(); + countbits->str = "\\$countbits"; + + if (str == "\\$countones") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = countbits; + } else if (str == "\\$isunknown") { + countbits->children.push_back(mkconst_bits({RTLIL::Sx}, false)); + countbits->children.push_back(mkconst_bits({RTLIL::Sz}, false)); + newNode = new AstNode(AST_GT, countbits, mkconst_int(0, false)); + } else if (str == "\\$onehot") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = new AstNode(AST_EQ, countbits, mkconst_int(1, false)); + } else if (str == "\\$onehot0") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = new AstNode(AST_LE, countbits, mkconst_int(1, false)); + } else { + log_abort(); + } + + goto apply_newNode; + } + if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) { AstNode *dpi_decl = current_scope[str]; @@ -3016,28 +3690,37 @@ 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)) { } - if (child->type != AST_CONSTANT) + if (child->type != AST_CONSTANT && child->type != AST_REALVALUE) all_args_const = false; } if (all_args_const) { - AstNode *func_workspace = current_scope[str]->clone(); - 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) @@ -3047,8 +3730,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; @@ -3058,16 +3739,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)) { } @@ -3111,7 +3793,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; @@ -3154,14 +3835,15 @@ skip_dynamic_range_lvalue_expansion:; if (wire_cache.count(child->str)) { wire = wire_cache.at(child->str); - if (wire->children.empty()) { + bool contains_value = wire->type == AST_LOCALPARAM; + if (wire->children.size() == contains_value) { for (auto c : child->children) wire->children.push_back(c->clone()); } else if (!child->children.empty()) { while (child->simplify(true, false, false, stage, -1, false, false)) { } - if (GetSize(child->children) == GetSize(wire->children)) { + if (GetSize(child->children) == GetSize(wire->children) - contains_value) { for (int i = 0; i < GetSize(child->children); i++) - if (*child->children.at(i) != *wire->children.at(i)) + if (*child->children.at(i) != *wire->children.at(i + contains_value)) goto tcall_incompatible_wires; } else { tcall_incompatible_wires: @@ -3172,7 +3854,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; @@ -3183,19 +3864,46 @@ 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()) { AstNode *arg = children[arg_count++]->clone(); + // convert purely constant arguments into localparams + if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) { + wire->type = AST_LOCALPARAM; + if (wire->attributes.count(ID::nosync)) + delete wire->attributes.at(ID::nosync); + wire->attributes.erase(ID::nosync); + wire->children.insert(wire->children.begin(), arg->clone()); + // args without a range implicitly have width 1 + if (wire->children.back()->type != AST_RANGE) { + // check if this wire is redeclared with an explicit size + bool uses_explicit_size = false; + for (const AstNode *other_child : decl->children) + if (other_child->type == AST_WIRE && child->str == other_child->str + && !other_child->children.empty() + && other_child->children.back()->type == AST_RANGE) { + uses_explicit_size = true; + break; + } + if (!uses_explicit_size) { + AstNode* range = new AstNode(); + range->type = AST_RANGE; + wire->children.push_back(range); + range->children.push_back(mkconst_int(0, true)); + range->children.push_back(mkconst_int(0, true)); + } + } + // updates the sizing + while (wire->simplify(true, false, false, 1, -1, false, false)) { } + delete arg; + continue; + } AstNode *wire_id = new AstNode(AST_IDENTIFIER); wire_id->str = wire->str; AstNode *assign = child->is_input ? @@ -3209,18 +3917,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()); @@ -3233,10 +3932,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 = ""; @@ -3284,7 +3984,14 @@ replace_fcall_later:; if (current_scope[str]->children[0]->isConst()) newNode = current_scope[str]->children[0]->clone(); } - else if (at_zero && current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)) { + else if (at_zero && current_scope.count(str) > 0) { + AstNode *node = current_scope[str]; + if (node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY) + newNode = mkconst_int(0, sign_hint, width_hint); + } + break; + case AST_MEMRD: + if (at_zero) { newNode = mkconst_int(0, sign_hint, width_hint); } break; @@ -3429,24 +4136,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) { @@ -3491,7 +4183,11 @@ replace_fcall_later:; case AST_CAST_SIZE: if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) { int width = children[0]->bitsAsConst().as_int(); - RTLIL::Const val = children[1]->bitsAsConst(width); + RTLIL::Const val; + if (children[1]->is_unsized) + val = children[1]->bitsAsUnsizedConst(width); + else + val = children[1]->bitsAsConst(width); newNode = mkconst_bits(val.bits, children[1]->is_signed); } break; @@ -3540,12 +4236,12 @@ apply_newNode: return did_something; } -static void replace_result_wire_name_in_function(AstNode *node, std::string &from, std::string &to) +void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to) { - for (auto &it : node->children) - replace_result_wire_name_in_function(it, from, to); - if (node->str == from) - node->str = to; + for (AstNode *child : children) + child->replace_result_wire_name_in_function(from, to); + if (str == from && type != AST_FCALL && type != AST_TCALL) + str = to; } // replace a readmem[bh] TCALL ast node with a block of memory assignments @@ -3559,8 +4255,12 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m AstNode *meminit = nullptr; int next_meminit_cursor=0; vector<State> meminit_bits; + vector<State> en_bits; int meminit_size=0; + for (int i = 0; i < mem_width; i++) + en_bits.push_back(State::S1); + std::ifstream f; f.open(mem_filename.c_str()); if (f.fail()) { @@ -3634,12 +4334,13 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m { if (meminit != nullptr) { meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + meminit->children[3] = 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(AstNode::mkconst_bits(en_bits, false)); meminit->children.push_back(nullptr); meminit->str = memory->str; meminit->id2ast = memory; @@ -3674,116 +4375,163 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m if (meminit != nullptr) { meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + meminit->children[3] = AstNode::mkconst_int(meminit_size, false); } 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) +// 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) { - 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 || type == AST_PREFIX) { + 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; + } + + // 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 ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) && name_map.count(str) > 0) - str = name_map[str]; - - std::map<std::string, std::string> backup_name_map; + 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 + child->str = new_name; + current_scope[new_name] = child; + }; for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; - if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || - child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF || child->type == AST_ENUM_ITEM) { - if (backup_name_map.size() == 0) - backup_name_map = name_map; - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = child->str.rfind('.'); - if (pos == std::string::npos) - pos = child->str[0] == '\\' && 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; - if (child->type == AST_FUNCTION) - replace_result_wire_name_in_function(child, child->str, new_name); - else - child->str = new_name; - current_scope[new_name] = child; - } - if (child->type == AST_ENUM){ + + switch (child->type) { + case AST_WIRE: + case AST_MEMORY: + case AST_PARAMETER: + case AST_LOCALPARAM: + case AST_FUNCTION: + case AST_TASK: + case AST_CELL: + case AST_TYPEDEF: + case AST_ENUM_ITEM: + case AST_GENVAR: + prefix_node(child); + break; + + case AST_BLOCK: + case AST_GENBLOCK: + if (!child->str.empty()) + prefix_node(child); + break; + + case AST_ENUM: current_scope[child->str] = child; for (auto enode : child->children){ log_assert(enode->type == AST_ENUM_ITEM); - if (backup_name_map.size() == 0) - backup_name_map = name_map; - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = enode->str.rfind('.'); - if (pos == std::string::npos) - pos = enode->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; - else - pos = pos + 1; - new_name = enode->str.substr(0, pos) + new_name + enode->str.substr(pos); - if (new_name[0] != '$' && new_name[0] != '\\') - new_name = prefix[0] + new_name; - name_map[enode->str] = new_name; - - enode->str = new_name; - current_scope[new_name] = enode; + prefix_node(enode); } + break; + + default: + break; } } for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; - // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX - // still needs to recursed-into - if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER) + // AST_PREFIX member names should not be prefixed; we recurse into them + // as normal to ensure indices and ranges are properly resolved, and + // then restore the previous string + if (type == AST_PREFIX && i == 1) { + std::string backup_scope_name = child->str; + child->expand_genblock(prefix); + child->str = backup_scope_name; + continue; + } + // functions/tasks may reference wires, constants, etc. in this scope + if (child->type == AST_FUNCTION || child->type == AST_TASK) + 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 (child->type != AST_FUNCTION && child->type != AST_TASK) - child->expand_genblock(index_var, prefix, name_map); - } - - if (backup_name_map.size() > 0) - name_map.swap(backup_name_map); + 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; } } @@ -3977,10 +4725,12 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, log_assert(children[0]->type == AST_CONSTANT); log_assert(children[1]->type == AST_CONSTANT); log_assert(children[2]->type == AST_CONSTANT); + log_assert(children[3]->type == AST_CONSTANT); int cursor = children[0]->asInt(false); Const data = children[1]->bitsAsConst(); - int length = children[2]->asInt(false); + Const en = children[2]->bitsAsConst(); + int length = children[3]->asInt(false); if (length != 0) { @@ -3991,10 +4741,37 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, int wordsz = GetSize(data) / length; for (int i = 0; i < length; i++) { - block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false))); - block->children.back()->children[0]->str = str; - block->children.back()->children[0]->id2ast = id2ast; - block->children.back()->children[0]->was_checked = true; + int pos = 0; + while (pos < wordsz) { + if (en[pos] != State::S1) { + pos++; + } else { + int epos = pos + 1; + while (epos < wordsz && en[epos] == State::S1) + epos++; + int clen = epos - pos; + AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false)); + if (pos != 0 || epos != wordsz) { + int left; + int right; + AstNode *mrange = id2ast->children[0]; + if (mrange->range_left < mrange->range_right) { + right = mrange->range_right - pos; + left = mrange->range_right - epos + 1; + } else { + right = mrange->range_right + pos; + left = mrange->range_right + epos - 1; + } + range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true))); + } + AstNode *target = new AstNode(AST_IDENTIFIER, range); + target->str = str; + target->id2ast = id2ast; + target->was_checked = true; + block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false))); + pos = epos; + } + } } } @@ -4098,11 +4875,48 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, if (children[0]->children[0]->type == AST_CONSTANT) { int id = children[0]->children[0]->integer; - str = stringf("%s[%d]", str.c_str(), id); + int left = id2ast->children[1]->children[0]->integer; + int right = id2ast->children[1]->children[1]->integer; + bool valid_const_access = + (left <= id && id <= right) || + (right <= id && id <= left); + if (valid_const_access) + { + str = stringf("%s[%d]", str.c_str(), id); + delete_children(); + range_valid = false; + id2ast = NULL; + } + else + { + int width; + if (bit_part_sel) + { + bit_part_sel->dumpAst(nullptr, "? "); + if (bit_part_sel->children.size() == 1) + width = 0; + else + width = bit_part_sel->children[0]->integer - + bit_part_sel->children[1]->integer; + delete bit_part_sel; + bit_part_sel = nullptr; + } + else + { + width = id2ast->children[0]->children[0]->integer - + id2ast->children[0]->children[1]->integer; + } + width = abs(width) + 1; - delete_children(); - range_valid = false; - id2ast = NULL; + delete_children(); + + std::vector<RTLIL::State> x_bits; + for (int i = 0; i < width; i++) + x_bits.push_back(RTLIL::State::Sx); + AstNode *constant = AstNode::mkconst_bits(x_bits, false); + constant->cloneInto(this); + delete constant; + } } else { @@ -4231,6 +5045,8 @@ bool AstNode::detect_latch(const std::string &var) case AST_POSEDGE: case AST_NEGEDGE: return false; + case AST_EDGE: + break; case AST_BLOCK: if (!c->detect_latch(var)) return false; @@ -4273,17 +5089,12 @@ bool AstNode::detect_latch(const std::string &var) } } -bool AstNode::has_const_only_constructs(bool &recommend_const_eval) +bool AstNode::has_const_only_constructs() { - 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(recommend_const_eval)) - return true; for (auto child : children) - if (child->AstNode::has_const_only_constructs(recommend_const_eval)) + if (child->has_const_only_constructs()) return true; return false; } @@ -4299,19 +5110,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); } @@ -4321,44 +5139,30 @@ 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; + std::vector<AstNode*> to_delete; AstNode *block = new AstNode(AST_BLOCK); + AstNode *result = nullptr; size_t argidx = 0; for (auto child : children) { - if (child->type == AST_WIRE) - { - while (child->simplify(true, false, false, 1, -1, false, true)) { } - if (!child->range_valid) - log_file_error(child->filename, child->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", - child->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); - 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()); - backup_scope[child->str] = current_scope[child->str]; - current_scope[child->str] = child; - continue; - } - block->children.push_back(child->clone()); } - log_assert(variables.count(str) != 0); - while (!block->children.empty()) { AstNode *stmt = block->children.front(); @@ -4370,36 +5174,104 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) stmt->dumpAst(NULL, "stmt> "); #endif + if (stmt->type == AST_WIRE) + { + while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + 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()) { + 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(variable.arg->type == AST_REALVALUE); + variable.val = variable.arg->realAsConst(width); + } + } + current_scope[stmt->str] = stmt; + + block->children.erase(block->children.begin()); + to_delete.push_back(stmt); + continue; + } + + log_assert(variables.count(str) != 0); + + if (stmt->type == AST_LOCALPARAM) + { + while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + + current_scope[stmt->str] = stmt; + + block->children.erase(block->children.begin()); + to_delete.push_back(stmt); + continue; + } + 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); + 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]; @@ -4426,12 +5298,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()); @@ -4447,12 +5323,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++) @@ -4466,7 +5346,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; @@ -4483,14 +5364,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; @@ -4512,6 +5397,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(); @@ -4519,25 +5407,33 @@ 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(); } + result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + +finished: delete block; + current_scope = backup_scope; - for (auto &it : backup_scope) - if (it.second == NULL) - current_scope.erase(it.first); - else - current_scope[it.first] = it.second; + for (auto it : to_delete) { + delete it; + } + to_delete.clear(); - return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + return result; } void AstNode::allocateDefaultEnumValues() { log_assert(type==AST_ENUM); + log_assert(children.size() > 0); + if (children.front()->attributes.count(ID::enum_base_type)) + return; // already elaborated int last_enum_int = -1; for (auto node : children) { log_assert(node->type==AST_ENUM_ITEM); @@ -4563,4 +5459,71 @@ 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}; +} + +std::string AstNode::try_pop_module_prefix() const +{ + AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; + size_t pos = str.find('.', 1); + if (str[0] == '\\' && pos != std::string::npos) { + std::string new_str = "\\" + str.substr(pos + 1); + if (current_scope.count(new_str)) { + std::string prefix = str.substr(0, pos); + auto it = current_scope_ast->attributes.find(ID::hdlname); + if ((it != current_scope_ast->attributes.end() && it->second->str == prefix) + || prefix == current_scope_ast->str) + return new_str; + } + } + return str; +} + YOSYS_NAMESPACE_END diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 9ae3fac2c..19844bda6 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -65,17 +65,21 @@ static std::pair<RTLIL::IdString, int> wideports_split(std::string name) for (int i = 0; i+1 < GetSize(name); i++) { if (name[i] == '[') pos = i; - else if (name[i] < '0' || name[i] > '9') + else if (name[i] != '-' && (name[i] < '0' || name[i] > '9')) + pos = -1; + else if (name[i] == '-' && ((i != pos+1) || name[i+1] == ']')) + pos = -1; + else if (i == pos+2 && name[i] == '0' && name[i-1] == '-') pos = -1; else if (i == pos+1 && name[i] == '0' && name[i+1] != ']') pos = -1; } if (pos >= 0) - return std::pair<RTLIL::IdString, int>("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1); + return std::pair<RTLIL::IdString, int>("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)); failed: - return std::pair<RTLIL::IdString, int>("\\" + name, 0); + return std::pair<RTLIL::IdString, int>(RTLIL::IdString(), 0); } void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool run_clean, bool sop_mode, bool wideports) @@ -263,8 +267,8 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (wideports) { std::pair<RTLIL::IdString, int> wp = wideports_split(p); - if (wp.second > 0) { - wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second); + if (!wp.first.empty() && wp.second >= 0) { + wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second + 1); wideports_cache[wp.first].second = !strcmp(cmd, ".inputs"); } } @@ -375,6 +379,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool IdString celltype = RTLIL::escape_id(p); RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); + RTLIL::Module *cell_mod = design->module(celltype); dict<RTLIL::IdString, dict<int, SigBit>> cell_wideports_cache; @@ -387,10 +392,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (wideports) { std::pair<RTLIL::IdString, int> wp = wideports_split(p); - if (wp.second > 0) - cell_wideports_cache[wp.first][wp.second-1] = blif_wire(q); - else + if (wp.first.empty()) cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); + else + cell_wideports_cache[wp.first][wp.second] = blif_wire(q); } else { cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); } @@ -399,14 +404,26 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool for (auto &it : cell_wideports_cache) { int width = 0; + int offset = 0; + bool upto = false; for (auto &b : it.second) width = std::max(width, b.first + 1); + if (cell_mod) { + Wire *cell_port = cell_mod->wire(it.first); + if (cell_port && (cell_port->port_input || cell_port->port_output)) { + offset = cell_port->start_offset; + upto = cell_port->upto; + width = cell_port->width; + } + } + SigSpec sig; for (int i = 0; i < width; i++) { - if (it.second.count(i)) - sig.append(it.second.at(i)); + int idx = offset + (upto ? width - 1 - i: i); + if (it.second.count(idx)) + sig.append(it.second.at(idx)); else sig.append(module->addWire(NEW_ID)); } diff --git a/frontends/blif/blifparse.h b/frontends/blif/blifparse.h index 2b84cb795..d7a3c96b1 100644 --- a/frontends/blif/blifparse.h +++ b/frontends/blif/blifparse.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/ilang/.gitignore b/frontends/ilang/.gitignore deleted file mode 100644 index f586b33c7..000000000 --- a/frontends/ilang/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -ilang_lexer.cc -ilang_parser.output -ilang_parser.tab.cc -ilang_parser.tab.hh diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc deleted file mode 100644 index 6f1f0e8fc..000000000 --- a/frontends/ilang/Makefile.inc +++ /dev/null @@ -1,19 +0,0 @@ - -GENFILES += frontends/ilang/ilang_parser.tab.cc -GENFILES += frontends/ilang/ilang_parser.tab.hh -GENFILES += frontends/ilang/ilang_parser.output -GENFILES += frontends/ilang/ilang_lexer.cc - -frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y - $(Q) mkdir -p $(dir $@) - $(P) $(BISON) -o $@ -d -r all -b frontends/ilang/ilang_parser $< - -frontends/ilang/ilang_parser.tab.hh: frontends/ilang/ilang_parser.tab.cc - -frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l - $(Q) mkdir -p $(dir $@) - $(P) flex -o frontends/ilang/ilang_lexer.cc $< - -OBJS += frontends/ilang/ilang_parser.tab.o frontends/ilang/ilang_lexer.o -OBJS += frontends/ilang/ilang_frontend.o - diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 1b34aaf3a..50c25abda 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -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; @@ -531,6 +539,56 @@ void json_import(Design *design, string &modname, JsonNode *node) json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters")); } } + + if (node->data_dict.count("memories")) + { + JsonNode *memories_node = node->data_dict.at("memories"); + + if (memories_node->type != 'D') + log_error("JSON memories node is not a dictionary.\n"); + + for (auto &memory_node_it : memories_node->data_dict) + { + IdString memory_name = RTLIL::escape_id(memory_node_it.first.c_str()); + JsonNode *memory_node = memory_node_it.second; + + RTLIL::Memory *mem = new RTLIL::Memory; + mem->name = memory_name; + + if (memory_node->type != 'D') + log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name)); + + if (memory_node->data_dict.count("width") == 0) + log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name)); + JsonNode *width_node = memory_node->data_dict.at("width"); + if (width_node->type != 'N') + log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name)); + mem->width = width_node->data_number; + + if (memory_node->data_dict.count("size") == 0) + log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name)); + JsonNode *size_node = memory_node->data_dict.at("size"); + if (size_node->type != 'N') + log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name)); + mem->size = size_node->data_number; + + mem->start_offset = 0; + if (memory_node->data_dict.count("start_offset") != 0) { + JsonNode *val = memory_node->data_dict.at("start_offset"); + if (val->type == 'N') + mem->start_offset = val->data_number; + } + + if (memory_node->data_dict.count("attributes")) + json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes")); + + module->memories[mem->name] = mem; + } + } + + // remove duplicates from connections array + pool<RTLIL::SigSig> unique_connections(module->connections_.begin(), module->connections_.end()); + module->connections_ = std::vector<RTLIL::SigSig>(unique_connections.begin(), unique_connections.end()); } struct JsonFrontend : public Frontend { diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index f77d7da56..abf8de4d1 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc index 6d72cbff5..c12640ef0 100644 --- a/frontends/rpc/rpc_frontend.cc +++ b/frontends/rpc/rpc_frontend.cc @@ -378,7 +378,7 @@ struct RpcFrontend : public Pass { log(" -> {\"method\": \"derive\", \"module\": \"<module-name\">, \"parameters\": {\n"); log(" \"<param-name>\": {\"type\": \"[unsigned|signed|string|real]\",\n"); log(" \"value\": \"<param-value>\"}, ...}}\n"); - log(" <- {\"frontend\": \"[ilang|verilog|...]\",\"source\": \"<source>\"}}\n"); + log(" <- {\"frontend\": \"[rtlil|verilog|...]\",\"source\": \"<source>\"}}\n"); log(" <- {\"error\": \"<error-message>\"}\n"); log(" request for the module <module-name> to be derived for a specific set of\n"); log(" parameters. <param-name> starts with \\ for named parameters, and with $\n"); @@ -401,10 +401,11 @@ struct RpcFrontend : public Pass { std::string arg = args[argidx]; if (arg == "-exec" && argidx+1 < args.size()) { command.insert(command.begin(), args.begin() + argidx + 1, args.end()); + argidx = args.size()-1; continue; } if (arg == "-path" && argidx+1 < args.size()) { - path = args[argidx+1]; + path = args[++argidx]; continue; } break; diff --git a/frontends/rtlil/.gitignore b/frontends/rtlil/.gitignore new file mode 100644 index 000000000..d4a322756 --- /dev/null +++ b/frontends/rtlil/.gitignore @@ -0,0 +1,4 @@ +rtlil_lexer.cc +rtlil_parser.output +rtlil_parser.tab.cc +rtlil_parser.tab.hh diff --git a/frontends/rtlil/Makefile.inc b/frontends/rtlil/Makefile.inc new file mode 100644 index 000000000..d0c0cfcf8 --- /dev/null +++ b/frontends/rtlil/Makefile.inc @@ -0,0 +1,19 @@ + +GENFILES += frontends/rtlil/rtlil_parser.tab.cc +GENFILES += frontends/rtlil/rtlil_parser.tab.hh +GENFILES += frontends/rtlil/rtlil_parser.output +GENFILES += frontends/rtlil/rtlil_lexer.cc + +frontends/rtlil/rtlil_parser.tab.cc: frontends/rtlil/rtlil_parser.y + $(Q) mkdir -p $(dir $@) + $(P) $(BISON) -o $@ -d -r all -b frontends/rtlil/rtlil_parser $< + +frontends/rtlil/rtlil_parser.tab.hh: frontends/rtlil/rtlil_parser.tab.cc + +frontends/rtlil/rtlil_lexer.cc: frontends/rtlil/rtlil_lexer.l + $(Q) mkdir -p $(dir $@) + $(P) flex -o frontends/rtlil/rtlil_lexer.cc $< + +OBJS += frontends/rtlil/rtlil_parser.tab.o frontends/rtlil/rtlil_lexer.o +OBJS += frontends/rtlil/rtlil_frontend.o + diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/rtlil/rtlil_frontend.cc index 973e62f2c..5f85ca2b8 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/rtlil/rtlil_frontend.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,30 +18,30 @@ * --- * * A very simple and straightforward frontend for the RTLIL text - * representation (as generated by the 'ilang' backend). + * representation. * */ -#include "ilang_frontend.h" +#include "rtlil_frontend.h" #include "kernel/register.h" #include "kernel/log.h" -void rtlil_frontend_ilang_yyerror(char const *s) +void rtlil_frontend_yyerror(char const *s) { - YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s); + YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s); } YOSYS_NAMESPACE_BEGIN -struct IlangFrontend : public Frontend { - IlangFrontend() : Frontend("ilang", "read modules from ilang file") { } +struct RTLILFrontend : public Frontend { + RTLILFrontend() : Frontend("rtlil", "read modules from RTLIL file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" read_ilang [filename]\n"); + log(" read_rtlil [filename]\n"); log("\n"); - log("Load modules from an ilang file to the current design. (ilang is a text\n"); + log("Load modules from an RTLIL file to the current design. (RTLIL is a text\n"); log("representation of a design in yosys's internal format.)\n"); log("\n"); log(" -nooverwrite\n"); @@ -58,27 +58,27 @@ struct IlangFrontend : public Frontend { } void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { - ILANG_FRONTEND::flag_nooverwrite = false; - ILANG_FRONTEND::flag_overwrite = false; - ILANG_FRONTEND::flag_lib = false; + RTLIL_FRONTEND::flag_nooverwrite = false; + RTLIL_FRONTEND::flag_overwrite = false; + RTLIL_FRONTEND::flag_lib = false; - log_header(design, "Executing ILANG frontend.\n"); + log_header(design, "Executing RTLIL frontend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-nooverwrite") { - ILANG_FRONTEND::flag_nooverwrite = true; - ILANG_FRONTEND::flag_overwrite = false; + RTLIL_FRONTEND::flag_nooverwrite = true; + RTLIL_FRONTEND::flag_overwrite = false; continue; } if (arg == "-overwrite") { - ILANG_FRONTEND::flag_nooverwrite = false; - ILANG_FRONTEND::flag_overwrite = true; + RTLIL_FRONTEND::flag_nooverwrite = false; + RTLIL_FRONTEND::flag_overwrite = true; continue; } if (arg == "-lib") { - ILANG_FRONTEND::flag_lib = true; + RTLIL_FRONTEND::flag_lib = true; continue; } break; @@ -87,12 +87,27 @@ struct IlangFrontend : public Frontend { log("Input filename: %s\n", filename.c_str()); - ILANG_FRONTEND::lexin = f; - ILANG_FRONTEND::current_design = design; - rtlil_frontend_ilang_yydebug = false; - rtlil_frontend_ilang_yyrestart(NULL); - rtlil_frontend_ilang_yyparse(); - rtlil_frontend_ilang_yylex_destroy(); + RTLIL_FRONTEND::lexin = f; + RTLIL_FRONTEND::current_design = design; + rtlil_frontend_yydebug = false; + rtlil_frontend_yyrestart(NULL); + rtlil_frontend_yyparse(); + rtlil_frontend_yylex_destroy(); + } +} RTLILFrontend; + +struct IlangFrontend : public Frontend { + IlangFrontend() : Frontend("ilang", "(deprecated) alias of read_rtlil") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log("See `help read_rtlil`.\n"); + log("\n"); + } + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override + { + RTLILFrontend.execute(f, filename, args, design); } } IlangFrontend; diff --git a/frontends/ilang/ilang_frontend.h b/frontends/rtlil/rtlil_frontend.h index f8a152841..189260605 100644 --- a/frontends/ilang/ilang_frontend.h +++ b/frontends/rtlil/rtlil_frontend.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,18 +18,18 @@ * --- * * A very simple and straightforward frontend for the RTLIL text - * representation (as generated by the 'ilang' backend). + * representation. * */ -#ifndef ILANG_FRONTEND_H -#define ILANG_FRONTEND_H +#ifndef RTLIL_FRONTEND_H +#define RTLIL_FRONTEND_H #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN -namespace ILANG_FRONTEND { +namespace RTLIL_FRONTEND { extern std::istream *lexin; extern RTLIL::Design *current_design; extern bool flag_nooverwrite; @@ -39,13 +39,13 @@ namespace ILANG_FRONTEND { YOSYS_NAMESPACE_END -extern int rtlil_frontend_ilang_yydebug; -int rtlil_frontend_ilang_yylex(void); -void rtlil_frontend_ilang_yyerror(char const *s); -void rtlil_frontend_ilang_yyrestart(FILE *f); -int rtlil_frontend_ilang_yyparse(void); -int rtlil_frontend_ilang_yylex_destroy(void); -int rtlil_frontend_ilang_yyget_lineno(void); +extern int rtlil_frontend_yydebug; +int rtlil_frontend_yylex(void); +void rtlil_frontend_yyerror(char const *s); +void rtlil_frontend_yyrestart(FILE *f); +int rtlil_frontend_yyparse(void); +int rtlil_frontend_yylex_destroy(void); +int rtlil_frontend_yyget_lineno(void); #endif diff --git a/frontends/ilang/ilang_lexer.l b/frontends/rtlil/rtlil_lexer.l index 3362ed641..e16413216 100644 --- a/frontends/ilang/ilang_lexer.l +++ b/frontends/rtlil/rtlil_lexer.l @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,7 +18,7 @@ * --- * * A very simple and straightforward frontend for the RTLIL text - * representation (as generated by the 'ilang' backend). + * representation. * */ @@ -30,20 +30,20 @@ #endif #include <cstdlib> -#include "frontends/ilang/ilang_frontend.h" -#include "ilang_parser.tab.hh" +#include "frontends/rtlil/rtlil_frontend.h" +#include "rtlil_parser.tab.hh" USING_YOSYS_NAMESPACE #define YY_INPUT(buf,result,max_size) \ - result = readsome(*ILANG_FRONTEND::lexin, buf, max_size) + result = readsome(*RTLIL_FRONTEND::lexin, buf, max_size) %} %option yylineno %option noyywrap %option nounput -%option prefix="rtlil_frontend_ilang_yy" +%option prefix="rtlil_frontend_yy" %x STRING @@ -79,16 +79,16 @@ USING_YOSYS_NAMESPACE "global" { return TOK_GLOBAL; } "init" { return TOK_INIT; } "update" { return TOK_UPDATE; } +"memwr" { return TOK_MEMWR; } "process" { return TOK_PROCESS; } "end" { return TOK_END; } [a-z]+ { return TOK_INVALID; } -"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } -"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } -"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } +"\\"[^ \t\r\n]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } +"$"[^ \t\r\n]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } -[0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; } +[0-9]+'[01xzm-]* { rtlil_frontend_yylval.string = strdup(yytext); return TOK_VALUE; } -?[0-9]+ { char *end = nullptr; errno = 0; @@ -98,7 +98,7 @@ USING_YOSYS_NAMESPACE return TOK_INVALID; // literal out of range of long if (value < INT_MIN || value > INT_MAX) return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms) - rtlil_frontend_ilang_yylval.integer = value; + rtlil_frontend_yylval.integer = value; return TOK_INT; } @@ -131,7 +131,7 @@ USING_YOSYS_NAMESPACE yystr[j++] = yystr[i++]; } yystr[j] = 0; - rtlil_frontend_ilang_yylval.string = yystr; + rtlil_frontend_yylval.string = yystr; return TOK_STRING; } <STRING>. { yymore(); } @@ -145,6 +145,6 @@ USING_YOSYS_NAMESPACE %% // this is a hack to avoid the 'yyinput defined but not used' error msgs -void *rtlil_frontend_ilang_avoid_input_warnings() { +void *rtlil_frontend_avoid_input_warnings() { return (void*)&yyinput; } diff --git a/frontends/ilang/ilang_parser.y b/frontends/rtlil/rtlil_parser.y index 879ef4af9..7d99b2c42 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/rtlil/rtlil_parser.y @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,15 +18,17 @@ * --- * * A very simple and straightforward frontend for the RTLIL text - * representation (as generated by the 'ilang' backend). + * representation. * */ +%require "3.0" + %{ #include <list> -#include "frontends/ilang/ilang_frontend.h" +#include "frontends/rtlil/rtlil_frontend.h" YOSYS_NAMESPACE_BEGIN -namespace ILANG_FRONTEND { +namespace RTLIL_FRONTEND { std::istream *lexin; RTLIL::Design *current_design; RTLIL::Module *current_module; @@ -40,12 +42,12 @@ namespace ILANG_FRONTEND { bool flag_nooverwrite, flag_overwrite, flag_lib; bool delete_current_module; } -using namespace ILANG_FRONTEND; +using namespace RTLIL_FRONTEND; YOSYS_NAMESPACE_END USING_YOSYS_NAMESPACE %} -%define api.prefix {rtlil_frontend_ilang_yy} +%define api.prefix {rtlil_frontend_yy} /* The union is defined in the header, so we need to provide all the * includes it requires @@ -53,7 +55,7 @@ USING_YOSYS_NAMESPACE %code requires { #include <string> #include <vector> -#include "frontends/ilang/ilang_frontend.h" +#include "frontends/rtlil/rtlil_frontend.h" } %union { @@ -69,7 +71,7 @@ USING_YOSYS_NAMESPACE %token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC %token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT -%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET +%token TOK_UPDATE TOK_MEMWR TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET %token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO %type <rsigspec> sigspec_list_reversed @@ -87,7 +89,7 @@ input: attrbuf.clear(); } design { if (attrbuf.size() != 0) - rtlil_frontend_ilang_yyerror("dangling attribute"); + rtlil_frontend_yyerror("dangling attribute"); }; EOL: @@ -111,7 +113,7 @@ module: log("Ignoring blackbox re-definition of module %s.\n", $2); delete_current_module = true; } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of module %s.", $2).c_str()); } else if (flag_nooverwrite) { log("Ignoring re-definition of module %s.\n", $2); delete_current_module = true; @@ -129,7 +131,7 @@ module: free($2); } module_body TOK_END { if (attrbuf.size() != 0) - rtlil_frontend_ilang_yyerror("dangling attribute"); + rtlil_frontend_yyerror("dangling attribute"); current_module->fixup_ports(); if (delete_current_module) delete current_module; @@ -155,6 +157,7 @@ param_defval_stmt: TOK_PARAMETER TOK_ID constant EOL { current_module->avail_parameters($2); current_module->parameter_default_values[$2] = *$3; + delete $3; free($2); }; @@ -172,12 +175,12 @@ autoidx_stmt: wire_stmt: TOK_WIRE { - current_wire = current_module->addWire("$__ilang_frontend_tmp__"); + current_wire = current_module->addWire("$__rtlil_frontend_tmp__"); current_wire->attributes = attrbuf; attrbuf.clear(); } wire_options TOK_ID EOL { if (current_module->wire($4) != nullptr) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of wire %s.", $4).c_str()); + rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of wire %s.", $4).c_str()); current_module->rename(current_wire, $4); free($4); }; @@ -187,7 +190,7 @@ wire_options: current_wire->width = $3; } | wire_options TOK_WIDTH TOK_INVALID { - rtlil_frontend_ilang_yyerror("ilang error: invalid wire width"); + rtlil_frontend_yyerror("RTLIL error: invalid wire width"); } | wire_options TOK_UPTO { current_wire->upto = true; @@ -222,7 +225,7 @@ memory_stmt: attrbuf.clear(); } memory_options TOK_ID EOL { if (current_module->memories.count($4) != 0) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of memory %s.", $4).c_str()); + rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of memory %s.", $4).c_str()); current_memory->name = $4; current_module->memories[$4] = current_memory; free($4); @@ -243,7 +246,7 @@ memory_options: cell_stmt: TOK_CELL TOK_ID TOK_ID EOL { if (current_module->cell($3) != nullptr) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell %s.", $3).c_str()); + rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell %s.", $3).c_str()); current_cell = current_module->addCell($3, $2); current_cell->attributes = attrbuf; attrbuf.clear(); @@ -271,7 +274,7 @@ cell_body: } | cell_body TOK_CONNECT TOK_ID sigspec EOL { if (current_cell->hasPort($3)) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str()); + rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell port %s.", $3).c_str()); current_cell->setPort($3, *$4); delete $4; free($3); @@ -281,11 +284,9 @@ cell_body: proc_stmt: TOK_PROCESS TOK_ID EOL { if (current_module->processes.count($2) != 0) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of process %s.", $2).c_str()); - current_process = new RTLIL::Process; - current_process->name = $2; + rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of process %s.", $2).c_str()); + current_process = current_module->addProcess($2); current_process->attributes = attrbuf; - current_module->processes[$2] = current_process; switch_stack.clear(); switch_stack.push_back(¤t_process->root_case.switches); case_stack.clear(); @@ -342,7 +343,7 @@ case_body: assign_stmt: TOK_ASSIGN sigspec sigspec EOL { if (attrbuf.size() != 0) - rtlil_frontend_ilang_yyerror("dangling attribute"); + rtlil_frontend_yyerror("dangling attribute"); case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); delete $2; delete $3; @@ -389,6 +390,22 @@ update_list: delete $3; delete $4; } | + update_list attr_list TOK_MEMWR TOK_ID sigspec sigspec sigspec constant EOL { + RTLIL::MemWriteAction act; + act.attributes = attrbuf; + act.memid = $4; + act.address = *$5; + act.data = *$6; + act.enable = *$7; + act.priority_mask = *$8; + current_process->syncs.back()->mem_write_actions.push_back(std::move(act)); + attrbuf.clear(); + free($4); + delete $5; + delete $6; + delete $7; + delete $8; + } | /* empty */; constant: @@ -438,19 +455,19 @@ sigspec: } | TOK_ID { if (current_module->wire($1) == nullptr) - rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); + rtlil_frontend_yyerror(stringf("RTLIL error: wire %s not found", $1).c_str()); $$ = new RTLIL::SigSpec(current_module->wire($1)); free($1); } | sigspec '[' TOK_INT ']' { if ($3 >= $1->size() || $3 < 0) - rtlil_frontend_ilang_yyerror("bit index out of range"); + rtlil_frontend_yyerror("bit index out of range"); $$ = new RTLIL::SigSpec($1->extract($3)); delete $1; } | sigspec '[' TOK_INT ':' TOK_INT ']' { if ($3 >= $1->size() || $3 < 0 || $3 < $5) - rtlil_frontend_ilang_yyerror("invalid slice"); + rtlil_frontend_yyerror("invalid slice"); $$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1)); delete $1; } | @@ -477,7 +494,7 @@ sigspec_list: sigspec_list_reversed { conn_stmt: TOK_CONNECT sigspec sigspec EOL { if (attrbuf.size() != 0) - rtlil_frontend_ilang_yyerror("dangling attribute"); + rtlil_frontend_yyerror("dangling attribute"); current_module->connect(*$2, *$3); delete $2; delete $3; diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc index 972f4f9f1..c82428613 100644 --- a/frontends/verific/Makefile.inc +++ b/frontends/verific/Makefile.inc @@ -10,9 +10,11 @@ EXTRA_TARGETS += share/verific share/verific: $(P) rm -rf share/verific.new $(Q) mkdir -p share/verific.new +ifneq ($(DISABLE_VERIFIC_VHDL),1) $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008 +endif $(Q) chmod -R a+rX share/verific.new $(Q) mv share/verific.new share/verific diff --git a/frontends/verific/README b/frontends/verific/README index c37d76343..952fb1e0c 100644 --- a/frontends/verific/README +++ b/frontends/verific/README @@ -1,11 +1,11 @@ This directory contains Verific bindings for Yosys. -Use Symbiotic EDA Suite if you need Yosys+Verifc. -https://www.symbioticeda.com/seda-suite +Use Tabby CAD Suite from YosysHQ if you need Yosys+Verifc. +https://www.yosyshq.com/ -Contact office@symbioticeda.com for free evaluation -binaries of Symbiotic EDA Suite. +Contact YosysHQ at contact@yosyshq.com for free evaluation +binaries of Tabby CAD Suite. Verific Features that should be enabled in your Verific library diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 9785b8eff..17dbed067 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,7 @@ #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" +#include "libs/sha1/sha1.h" #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -42,19 +43,26 @@ USING_YOSYS_NAMESPACE #endif #include "veri_file.h" -#include "vhdl_file.h" #include "hier_tree.h" #include "VeriModule.h" #include "VeriWrite.h" -#include "VhdlUnits.h" #include "VeriLibrary.h" -#ifndef SYMBIOTIC_VERIFIC_API_VERSION -# error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." +#ifdef VERIFIC_VHDL_SUPPORT +#include "vhdl_file.h" +#include "VhdlUnits.h" +#endif + +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS +#include "InitialAssertions.h" #endif -#if SYMBIOTIC_VERIFIC_API_VERSION < 202006 -# error "Please update your version of Symbiotic EDA flavored Verific." +#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 < 20210801 +# error "Please update your version of YosysHQ flavored Verific." #endif #ifdef __clang__ @@ -170,8 +178,10 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att return; if (!type_range->IsTypeEnum()) return; +#ifdef VERIFIC_VHDL_SUPPORT if (nl->IsFromVhdl() && strcmp(type_range->GetTypeName(), "STD_LOGIC") == 0) return; +#endif auto type_name = type_range->GetTypeName(); if (!type_name) return; @@ -188,7 +198,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att p = nullptr; else for (auto q = p+2; *q != '\0'; q++) - if (*q != '0' && *q != '1') { + if (*q != '0' && *q != '1' && *q != 'x' && *q != 'z') { p = nullptr; break; } @@ -197,13 +207,19 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att log_error("Expected TypeRange value '%s' to be of form <decimal>'b<binary>.\n", v); attributes.emplace(stringf("\\enum_value_%s", p+2), RTLIL::escape_id(k)); } +#ifdef VERIFIC_VHDL_SUPPORT else if (nl->IsFromVhdl()) { - // Expect "<binary>" + // Expect "<binary>" or plain <binary> auto p = v; if (p) { - if (*p != '"') - p = nullptr; - else { + if (*p != '"') { + auto l = strlen(p); + auto q = (char*)malloc(l+1); + strncpy(q, p, l); + q[l] = '\0'; + for(char *ptr = q; *ptr; ++ptr )*ptr = tolower(*ptr); + attributes.emplace(stringf("\\enum_value_%s", q), RTLIL::escape_id(k)); + } else { auto *q = p+1; for (; *q != '"'; q++) if (*q != '0' && *q != '1') { @@ -212,17 +228,22 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att } if (p && *(q+1) != '\0') p = nullptr; + + if (p != nullptr) + { + auto l = strlen(p); + auto q = (char*)malloc(l+1-2); + strncpy(q, p+1, l-2); + q[l-2] = '\0'; + attributes.emplace(stringf("\\enum_value_%s", q), RTLIL::escape_id(k)); + free(q); + } } } if (p == nullptr) - log_error("Expected TypeRange value '%s' to be of form \"<binary>\".\n", v); - auto l = strlen(p); - auto q = (char*)malloc(l+1-2); - strncpy(q, p+1, l-2); - q[l-2] = '\0'; - attributes.emplace(stringf("\\enum_value_%s", q), RTLIL::escape_id(k)); - free(q); + log_error("Expected TypeRange value '%s' to be of form \"<binary>\" or <binary>.\n", v); } +#endif } } } @@ -357,7 +378,7 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr return true; } - if (inst->Type() == PRIM_TRI) { + if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) { module->addMuxGate(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); return true; } @@ -396,6 +417,42 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == PRIM_DLATCHRS) + { + if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) + module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + else + module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + return true; + } + + if (inst->Type() == PRIM_DFF) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + if (inst->GetAsyncCond()->IsGnd()) + clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + else + clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + return true; + } + + if (inst->Type() == PRIM_DLATCH) + { + if (inst->GetAsyncCond()->IsGnd()) { + module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } else { + RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal())); + RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal()))); + module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } + return true; + } + return false; } @@ -457,7 +514,7 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } - if (inst->Type() == PRIM_TRI) { + if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) { cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; @@ -506,6 +563,34 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == PRIM_DFF) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + if (inst->GetAsyncCond()->IsGnd()) + cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + else + cell = clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == PRIM_DLATCH) + { + if (inst->GetAsyncCond()->IsGnd()) { + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } else { + RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal())); + RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal()))); + cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } + import_attributes(cell->attributes, inst); + return true; + } + #define IN operatorInput(inst) #define IN1 operatorInput1(inst) #define IN2 operatorInput2(inst) @@ -713,28 +798,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr } if (inst->Type() == OPER_NTO1MUX) { - cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); + cell = module->addBmux(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_NTO1MUX) { - SigSpec data = IN2, out = OUT; - - int wordsize_bits = ceil_log2(GetSize(out)); - int wordsize = 1 << wordsize_bits; - - SigSpec sel = {IN1, SigSpec(State::S0, wordsize_bits)}; - - SigSpec padded_data; - for (int i = 0; i < GetSize(data); i += GetSize(out)) { - SigSpec d = data.extract(i, GetSize(out)); - d.extend_u0(wordsize); - padded_data.append(d); - } - - cell = module->addShr(inst_name, padded_data, sel, out); + cell = module->addBmux(inst_name, IN2, IN1, OUT); import_attributes(cell->attributes, inst); return true; } @@ -778,6 +849,74 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == OPER_WIDE_DLATCHRS) + { + RTLIL::SigSpec sig_set = operatorInport(inst, "set"); + RTLIL::SigSpec sig_reset = operatorInport(inst, "reset"); + + if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool()) + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), IN, OUT); + else + cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_reset, IN, OUT); + import_attributes(cell->attributes, inst); + + return true; + } + + if (inst->Type() == OPER_WIDE_DFF) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + RTLIL::SigSpec sig_d = IN; + RTLIL::SigSpec sig_q = OUT; + RTLIL::SigSpec sig_adata = IN1; + RTLIL::SigSpec sig_acond = IN2; + + if (sig_acond.is_fully_const() && !sig_acond.as_bool()) { + cell = clocking.addDff(inst_name, sig_d, sig_q); + import_attributes(cell->attributes, inst); + } else { + int offset = 0, width = 0; + for (offset = 0; offset < GetSize(sig_acond); offset += width) { + for (width = 1; offset+width < GetSize(sig_acond); width++) + if (sig_acond[offset] != sig_acond[offset+width]) break; + cell = clocking.addAldff(module->uniquify(inst_name), sig_acond[offset], sig_adata.extract(offset, width), + sig_d.extract(offset, width), sig_q.extract(offset, width)); + import_attributes(cell->attributes, inst); + } + } + + return true; + } + + if (inst->Type() == OPER_WIDE_DLATCH) + { + RTLIL::SigSpec sig_d = IN; + RTLIL::SigSpec sig_q = OUT; + RTLIL::SigSpec sig_adata = IN1; + RTLIL::SigSpec sig_acond = IN2; + + if (sig_acond.is_fully_const() && !sig_acond.as_bool()) { + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), sig_d, sig_q); + import_attributes(cell->attributes, inst); + } else { + int offset = 0, width = 0; + for (offset = 0; offset < GetSize(sig_acond); offset += width) { + for (width = 1; offset+width < GetSize(sig_acond); width++) + if (sig_acond[offset] != sig_acond[offset+width]) break; + RTLIL::SigSpec sig_set = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), sig_adata.extract(offset, width), sig_acond[offset]); + RTLIL::SigSpec sig_clr = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), module->Not(NEW_ID, sig_adata.extract(offset, width)), sig_acond[offset]); + cell = module->addDlatchsr(module->uniquify(inst_name), net_map_at(inst->GetControl()), sig_set, sig_clr, + sig_d.extract(offset, width), sig_q.extract(offset, width)); + import_attributes(cell->attributes, inst); + } + } + + return true; + } + #undef IN #undef IN1 #undef IN2 @@ -854,6 +993,21 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates) merge_past_ffs_clock(it.second, it.first.first, it.first.second); } +static std::string sha1_if_contain_spaces(std::string str) +{ + if(str.find_first_of(' ') != std::string::npos) { + std::size_t open = str.find_first_of('('); + std::size_t closed = str.find_last_of(')'); + if (open != std::string::npos && closed != std::string::npos) { + std::string content = str.substr(open + 1, closed - open - 1); + return str.substr(0, open + 1) + sha1(content) + str.substr(closed); + } else { + return sha1(str); + } + } + return str; +} + void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool norename) { std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name(); @@ -867,7 +1021,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se module_name += nl->Name(); module_name += ")"; } - module_name = "\\" + module_name; + module_name = "\\" + sha1_if_contain_spaces(module_name); } netlist = nl; @@ -888,6 +1042,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se } else { log("Importing module %s.\n", RTLIL::id2cstr(module->name)); } + import_attributes(module->attributes, nl, nl); SetIter si; MapIter mi, mi2; @@ -936,18 +1091,28 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); import_attributes(wire->attributes, portbus, nl); - if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN) + bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN; + if (portbus_input) wire->port_input = true; if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT) wire->port_output = true; for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) { if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) { + bool bit_input = portbus_input; + if (portbus->GetDir() == DIR_NONE) { + Port *p = portbus->ElementAtIndex(i); + bit_input = p->GetDir() == DIR_INOUT || p->GetDir() == DIR_IN; + if (bit_input) + wire->port_input = true; + if (p->GetDir() == DIR_INOUT || p->GetDir() == DIR_OUT) + wire->port_output = true; + } net = portbus->ElementAtIndex(i)->GetNet(); RTLIL::SigBit bit(wire, i - wire->start_offset); if (net_map.count(net) == 0) net_map[net] = bit; - else if (wire->port_input) + else if (bit_input) module->connect(net_map_at(net), bit); else module->connect(bit, net_map_at(net)); @@ -974,7 +1139,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se module->memories[memory->name] = memory; int number_of_bits = net->Size(); - number_of_bits = 1 << ceil_log2(number_of_bits); int bits_in_word = number_of_bits; FOREACH_PORTREF_OF_NET(net, si, pr) { if (pr->GetInst()->Type() == OPER_READ_PORT) { @@ -1445,6 +1609,18 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se continue; } + if (inst->Type() == PRIM_YOSYSHQ_INITSTATE) + { + if (verific_verbose) + log(" adding YosysHQ init state\n"); + SigBit initstate = module->Initstate(new_verific_id(inst)); + SigBit sig_o = net_map_at(inst->GetOutput()); + module->connect(sig_o, initstate); + + if (!mode_keep) + continue; + } + if (!mode_keep && verific_sva_prims.count(inst->Type())) { if (verific_verbose) log(" skipping SVA cell in non k-mode\n"); @@ -1492,7 +1668,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se inst_type += inst->View()->Name(); inst_type += ")"; } - inst_type = "\\" + inst_type; + inst_type = "\\" + sha1_if_contain_spaces(inst_type); } RTLIL::Cell *cell = module->addCell(inst_name, inst_type); @@ -1716,30 +1892,62 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const { log_assert(GetSize(sig_d) == GetSize(sig_q)); - if (GetSize(init_value) != 0) { - log_assert(GetSize(sig_q) == GetSize(init_value)); - if (sig_q.is_wire()) { - sig_q.as_wire()->attributes[ID::init] = init_value; + auto set_init_attribute = [&](SigSpec &s) { + if (GetSize(init_value) == 0) + return; + log_assert(GetSize(s) == GetSize(init_value)); + if (s.is_wire()) { + s.as_wire()->attributes[ID::init] = init_value; } else { - Wire *w = module->addWire(NEW_ID, GetSize(sig_q)); + Wire *w = module->addWire(NEW_ID, GetSize(s)); w->attributes[ID::init] = init_value; - module->connect(sig_q, w); - sig_q = w; + module->connect(s, w); + s = w; } - } + }; if (enable_sig != State::S1) sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); if (disable_sig != State::S0) { - log_assert(gclk == false); log_assert(GetSize(sig_q) == GetSize(init_value)); + + if (gclk) { + Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d)); + Wire *post_q_w = module->addWire(NEW_ID, GetSize(sig_q)); + + Const initval(State::Sx, GetSize(sig_q)); + int offset = 0; + for (auto c : sig_q.chunks()) { + if (c.wire && c.wire->attributes.count(ID::init)) { + Const val = c.wire->attributes.at(ID::init); + for (int i = 0; i < GetSize(c); i++) + initval[offset+i] = val[c.offset+i]; + } + offset += GetSize(c); + } + + if (!initval.is_fully_undef()) + post_q_w->attributes[ID::init] = initval; + + module->addMux(NEW_ID, sig_d, init_value, disable_sig, pre_d); + module->addMux(NEW_ID, post_q_w, init_value, disable_sig, sig_q); + + SigSpec post_q(post_q_w); + set_init_attribute(post_q); + return module->addFf(name, pre_d, post_q); + } + + set_init_attribute(sig_q); return module->addAdff(name, clock_sig, disable_sig, sig_d, sig_q, init_value, posedge); } - if (gclk) + if (gclk) { + set_init_attribute(sig_q); return module->addFf(name, sig_d, sig_q); + } + set_init_attribute(sig_q); return module->addDff(name, clock_sig, sig_d, sig_q, posedge); } @@ -1748,6 +1956,7 @@ Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec s log_assert(gclk == false); log_assert(disable_sig == State::S0); + // FIXME: Adffe if (enable_sig != State::S1) sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); @@ -1759,12 +1968,48 @@ Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::Si log_assert(gclk == false); log_assert(disable_sig == State::S0); + // FIXME: Dffsre if (enable_sig != State::S1) sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); return module->addDffsr(name, clock_sig, sig_set, sig_clr, sig_d, sig_q, posedge); } +Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q) +{ + log_assert(disable_sig == State::S0); + + // FIXME: Aldffe + if (enable_sig != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); + + if (gclk) { + Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d)); + Wire *post_q = module->addWire(NEW_ID, GetSize(sig_q)); + + Const initval(State::Sx, GetSize(sig_q)); + int offset = 0; + for (auto c : sig_q.chunks()) { + if (c.wire && c.wire->attributes.count(ID::init)) { + Const val = c.wire->attributes.at(ID::init); + for (int i = 0; i < GetSize(c); i++) + initval[offset+i] = val[c.offset+i]; + } + offset += GetSize(c); + } + + if (!initval.is_fully_undef()) + post_q->attributes[ID::init] = initval; + + module->addMux(NEW_ID, sig_d, sig_adata, sig_aload, pre_d); + module->addMux(NEW_ID, post_q, sig_adata, sig_aload, sig_q); + + return module->addFf(name, pre_d, post_q); + } + + return module->addAldff(name, clock_sig, sig_aload, sig_d, sig_q, sig_adata, posedge); +} + // ================================================================== struct VerificExtNets @@ -1911,17 +2156,23 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par std::set<Netlist*> nl_todo, nl_done; - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1); Array *netlists = NULL; Array veri_libs, vhdl_libs; +#ifdef VERIFIC_VHDL_SUPPORT + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); +#endif if (veri_lib) veri_libs.InsertLast(veri_lib); Map verific_params(STRING_HASH); for (const auto &i : parameters) verific_params.Insert(i.first.c_str(), i.second.c_str()); +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + InitialAssertions::Rewrite("work", &verific_params); +#endif + if (top.empty()) { netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params); } @@ -1942,12 +2193,13 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par } } +#ifdef VERIFIC_VHDL_SUPPORT if (vhdl_lib) { VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str()); if (vhdl_unit) vhdl_units.InsertLast(vhdl_unit); } - +#endif netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params); } @@ -1984,7 +2236,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par } veri_file::Reset(); +#ifdef VERIFIC_VHDL_SUPPORT vhdl_file::Reset(); +#endif Libset::Reset(); verific_incdirs.clear(); verific_libdirs.clear(); @@ -2027,7 +2281,7 @@ struct VerificPass : public Pass { log("\n"); log("Additional -D<macro>[=<value>] options may be added after the option indicating\n"); log("the language version (and before file names) to set additional verilog defines.\n"); - log("The macros SYNTHESIS and VERIFIC are defined implicitly.\n"); + log("The macros YOSYS, SYNTHESIS, and VERIFIC are defined implicitly.\n"); log("\n"); log("\n"); log(" verific -formal <verilog-file>..\n"); @@ -2035,11 +2289,41 @@ struct VerificPass : public Pass { log("Like -sv, but define FORMAL instead of SYNTHESIS.\n"); log("\n"); log("\n"); +#ifdef VERIFIC_VHDL_SUPPORT log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} <vhdl-file>..\n"); log("\n"); log("Load the specified VHDL files into Verific.\n"); log("\n"); log("\n"); +#endif + log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal] <command-file>\n"); + log("\n"); + log("Load and execute the specified command file.\n"); + log("Override verilog parsing mode can be set.\n"); + log("The macros YOSYS, SYNTHESIS/FORMAL, and VERIFIC are defined implicitly.\n"); + log("\n"); + log("Command file parser supports following commands:\n"); + log(" +define - defines macro\n"); + log(" -u - upper case all identifier (makes Verilog parser case insensitive)\n"); + log(" -v - register library name (file)\n"); + log(" -y - register library name (directory)\n"); + log(" +incdir - specify include dir\n"); + log(" +libext - specify library extension\n"); + log(" +liborder - add library in ordered list\n"); + log(" +librescan - unresolved modules will be always searched starting with the first\n"); + log(" library specified by -y/-v options.\n"); + log(" -f/-file - nested -f option\n"); + log(" -F - nested -F option\n"); + log("\n"); + log(" parse mode:\n"); + log(" -ams\n"); + log(" +systemverilogext\n"); + log(" +v2k\n"); + log(" +verilog1995ext\n"); + log(" +verilog2001ext\n"); + log(" -sverilog\n"); + log("\n"); + log("\n"); log(" verific [-work <libname>] {-sv|-vhdl|...} <hdl-file>\n"); log("\n"); log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n"); @@ -2144,11 +2428,85 @@ struct VerificPass : public Pass { log(" Dump the Verific netlist as a verilog file.\n"); log("\n"); log("\n"); - log("Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"); - log("https://www.symbioticeda.com/seda-suite\n"); + log(" verific [-work <libname>] -pp [options] <filename> [<module>]..\n"); + log("\n"); + log("Pretty print design (or just module) to the specified file from the\n"); + log("specified library. (default library when -work is not present: \"work\")\n"); + log("\n"); + log("Pretty print options:\n"); + log("\n"); + log(" -verilog\n"); + log(" Save output for Verilog/SystemVerilog design modules (default).\n"); + log("\n"); + log(" -vhdl\n"); + log(" Save output for VHDL design units.\n"); + log("\n"); + log("\n"); + log(" verific -app <application>..\n"); + log("\n"); + log("Execute YosysHQ formal application on loaded Verilog files.\n"); + log("\n"); + log("Application options:\n"); + log("\n"); + log(" -module <module>\n"); + log(" Run formal application only on specified module.\n"); + log("\n"); + log(" -blacklist <filename[:lineno]>\n"); + log(" Do not run application on modules from files that match the filename\n"); + log(" or filename and line number if provided in such format.\n"); + log(" Parameter can also contain comma separated list of file locations.\n"); + log("\n"); + log(" -blfile <file>\n"); + log(" Do not run application on locations specified in file, they can represent filename\n"); + log(" or filename and location in file.\n"); + log("\n"); + log("Applications:\n"); + log("\n"); +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_FORMALAPPS) + VerificFormalApplications vfa; + log("%s\n",vfa.GetHelp().c_str()); +#else + log(" WARNING: Applications only available in commercial build.\n"); + +#endif + log("\n"); + log("\n"); + log(" verific -template <name> <top_module>..\n"); + log("\n"); + log("Generate template for specified top module of loaded design.\n"); + log("\n"); + log("Template options:\n"); + log("\n"); + log(" -out\n"); + log(" Specifies output file for generated template, by default output is stdout\n"); + log("\n"); + log(" -chparam name value \n"); + log(" Generate template using this parameter value. Otherwise default parameter\n"); + log(" values will be used for templat generate functionality. This option\n"); + log(" can be specified multiple times to override multiple parameters.\n"); + log(" String values must be passed in double quotes (\").\n"); log("\n"); - log("Contact office@symbioticeda.com for free evaluation\n"); - log("binaries of Symbiotic EDA Suite.\n"); + log("Templates:\n"); + log("\n"); +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_TEMPLATES) + VerificTemplateGenerator vfg; + log("%s\n",vfg.GetHelp().c_str()); +#else + log(" WARNING: Templates only available in commercial build.\n"); + log("\n"); +#endif + log("\n"); + log("\n"); + log(" verific -cfg [<name> [<value>]]\n"); + log("\n"); + log("Get/set Verific runtime flags.\n"); + log("\n"); + log("\n"); + log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n"); + log("https://www.yosyshq.com/\n"); + log("\n"); + log("Contact office@yosyshq.com for free evaluation\n"); + log("binaries of YosysHQ Tabby CAD Suite.\n"); log("\n"); } #ifdef YOSYS_ENABLE_VERIFIC @@ -2159,11 +2517,11 @@ struct VerificPass : public Pass { if (check_noverific_env()) log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" - "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" - "https://www.symbioticeda.com/seda-suite\n" + "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" + "https://www.yosyshq.com/\n" "\n" - "Contact office@symbioticeda.com for free evaluation\n" - "binaries of Symbiotic EDA Suite.\n"); + "Contact office@yosyshq.com for free evaluation\n" + "binaries of YosysHQ Tabby CAD Suite.\n"); log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n"); @@ -2172,21 +2530,31 @@ struct VerificPass : public Pass { Message::SetConsoleOutput(0); Message::RegisterCallBackMsg(msg_func); + RuntimeFlags::SetVar("db_preserve_user_instances", 1); RuntimeFlags::SetVar("db_preserve_user_nets", 1); + RuntimeFlags::SetVar("db_preserve_x", 1); + RuntimeFlags::SetVar("db_allow_external_nets", 1); RuntimeFlags::SetVar("db_infer_wide_operators", 1); + RuntimeFlags::SetVar("db_infer_set_reset_registers", 0); RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); +#ifdef VERIFIC_VHDL_SUPPORT RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0); RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1); RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); - RuntimeFlags::SetVar("veri_preserve_assignments", 1); RuntimeFlags::SetVar("vhdl_preserve_assignments", 1); + //RuntimeFlags::SetVar("vhdl_preserve_comments", 1); + RuntimeFlags::SetVar("vhdl_preserve_drivers", 1); +#endif + RuntimeFlags::SetVar("veri_preserve_assignments", 1); + RuntimeFlags::SetVar("veri_preserve_comments", 1); + RuntimeFlags::SetVar("veri_preserve_drivers", 1); // Workaround for VIPER #13851 RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); @@ -2197,6 +2565,8 @@ struct VerificPass : public Pass { // https://github.com/YosysHQ/yosys/issues/1055 RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ; + RuntimeFlags::SetVar("verific_produce_verbose_syntax_error_message", 1); + #ifndef DB_PRESERVE_INITIAL_VALUE # warning Verific was built without DB_PRESERVE_INITIAL_VALUE. #endif @@ -2293,6 +2663,65 @@ struct VerificPass : public Pass { break; } + if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F")) + { + unsigned verilog_mode = veri_file::VERILOG_95; // default recommended by Verific + bool is_formal = false; + const char* filename = nullptr; + + Verific::veri_file::f_file_flags flags = (args[argidx] == "-f") ? veri_file::F_FILE_NONE : veri_file::F_FILE_CAPITAL; + + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-vlog95") { + verilog_mode = veri_file::VERILOG_95; + continue; + } else if (args[argidx] == "-vlog2k") { + verilog_mode = veri_file::VERILOG_2K; + continue; + } else if (args[argidx] == "-sv2005") { + verilog_mode = veri_file::SYSTEM_VERILOG_2005; + continue; + } else if (args[argidx] == "-sv2009") { + verilog_mode = veri_file::SYSTEM_VERILOG_2009; + continue; + } else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") { + verilog_mode = veri_file::SYSTEM_VERILOG; + if (args[argidx] == "-formal") is_formal = true; + continue; + } else if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + + if (!filename) { + filename = args[argidx].c_str(); + continue; + } else { + log_cmd_error("Only one filename can be specified.\n"); + } + } + if (!filename) + log_cmd_error("Filname must be specified.\n"); + + unsigned analysis_mode = verilog_mode; // keep default as provided by user if not defined in file + Array *file_names = veri_file::ProcessFFile(filename, flags, analysis_mode); + if (analysis_mode != verilog_mode) + log_warning("Provided verilog mode differs from one specified in file.\n"); + + veri_file::DefineMacro("YOSYS"); + veri_file::DefineMacro("VERIFIC"); + veri_file::DefineMacro(is_formal ? "FORMAL" : "SYNTHESIS"); + + if (!veri_file::AnalyzeMultipleFiles(file_names, verilog_mode, work.c_str(), veri_file::MFCU)) { + verific_error_msg.clear(); + log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n"); + } + + delete file_names; + verific_import_pending = true; + goto check_error; + } + if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" || args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal")) { @@ -2312,6 +2741,7 @@ struct VerificPass : public Pass { else log_abort(); + veri_file::DefineMacro("YOSYS"); veri_file::DefineMacro("VERIFIC"); veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS"); @@ -2340,13 +2770,16 @@ struct VerificPass : public Pass { while (argidx < GetSize(args)) file_names.Insert(args[argidx++].c_str()); - if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU)) + if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU)) { + verific_error_msg.clear(); log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n"); + } verific_import_pending = true; goto check_error; } +#ifdef VERIFIC_VHDL_SUPPORT if (GetSize(args) > argidx && args[argidx] == "-vhdl87") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str()); for (argidx++; argidx < GetSize(args); argidx++) @@ -2382,7 +2815,236 @@ struct VerificPass : public Pass { verific_import_pending = true; goto check_error; } +#endif + +#ifdef YOSYSHQ_VERIFIC_FORMALAPPS + if (argidx < GetSize(args) && args[argidx] == "-app") + { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx, "No formal application specified.\n"); + + VerificFormalApplications vfa; + auto apps = vfa.GetApps(); + std::string app = args[++argidx]; + std::vector<std::string> blacklists; + if (apps.find(app) == apps.end()) + log_cmd_error("Application '%s' does not exist.\n", app.c_str()); + + FormalApplication *application = apps[app]; + application->setLogger([](std::string msg) { log("%s",msg.c_str()); } ); + VeriModule *selected_module = nullptr; + + for (argidx++; argidx < GetSize(args); argidx++) { + std::string error; + if (application->checkParams(args, argidx, error)) { + if (!error.empty()) + cmd_error(args, argidx, error); + continue; + } + + if (args[argidx] == "-module" && argidx < GetSize(args)) { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No module name specified.\n"); + std::string module = args[++argidx]; + VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); + selected_module = veri_lib ? veri_lib->GetModule(module.c_str(), 1) : nullptr; + if (!selected_module) { + log_error("Can't find module '%s'.\n", module.c_str()); + } + continue; + } + if (args[argidx] == "-blacklist" && argidx < GetSize(args)) { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No blacklist specified.\n"); + + std::string line = args[++argidx]; + std::string p; + while (!(p = next_token(line, ",\t\r\n ")).empty()) + blacklists.push_back(p); + continue; + } + if (args[argidx] == "-blfile" && argidx < GetSize(args)) { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No blacklist file specified.\n"); + std::string fn = args[++argidx]; + std::ifstream f(fn); + if (f.fail()) + log_cmd_error("Can't open blacklist file '%s'!\n", fn.c_str()); + + std::string line,p; + while (std::getline(f, line)) { + while (!(p = next_token(line, ",\t\r\n ")).empty()) + blacklists.push_back(p); + } + continue; + } + break; + } + if (argidx < GetSize(args)) + cmd_error(args, argidx, "unknown option/parameter"); + + application->setBlacklists(&blacklists); + application->setSingleModuleMode(selected_module!=nullptr); + + const char *err = application->validate(); + if (err) + cmd_error(args, argidx, err); + + MapIter mi; + VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); + log("Running formal application '%s'.\n", app.c_str()); + + if (selected_module) { + std::string out; + if (!application->execute(selected_module, out)) + log_error("%s", out.c_str()); + } + else { + VeriModule *module ; + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, module) { + std::string out; + if (!application->execute(module, out)) { + log_error("%s", out.c_str()); + break; + } + } + } + goto check_error; + } +#endif + if (argidx < GetSize(args) && args[argidx] == "-pp") + { + const char* filename = nullptr; + const char* module = nullptr; + bool mode_vhdl = false; + for (argidx++; argidx < GetSize(args); argidx++) { +#ifdef VERIFIC_VHDL_SUPPORT + if (args[argidx] == "-vhdl") { + mode_vhdl = true; + continue; + } +#endif + if (args[argidx] == "-verilog") { + mode_vhdl = false; + continue; + } + + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + + if (!filename) { + filename = args[argidx].c_str(); + continue; + } + if (module) + log_cmd_error("Only one module can be specified.\n"); + module = args[argidx].c_str(); + } + + if (argidx < GetSize(args)) + cmd_error(args, argidx, "unknown option/parameter"); + + if (!filename) + log_cmd_error("Filname must be specified.\n"); + + if (mode_vhdl) +#ifdef VERIFIC_VHDL_SUPPORT + vhdl_file::PrettyPrint(filename, module, work.c_str()); +#else + goto check_error; +#endif + else + veri_file::PrettyPrint(filename, module, work.c_str()); + goto check_error; + } + +#ifdef YOSYSHQ_VERIFIC_TEMPLATES + if (argidx < GetSize(args) && args[argidx] == "-template") + { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No template type specified.\n"); + + VerificTemplateGenerator vfg; + auto gens = vfg.GetGenerators(); + std::string app = args[++argidx]; + if (gens.find(app) == gens.end()) + log_cmd_error("Template generator '%s' does not exist.\n", app.c_str()); + TemplateGenerator *generator = gens[app]; + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No top module specified.\n"); + generator->setLogger([](std::string msg) { log("%s",msg.c_str()); } ); + + std::string module = args[++argidx]; + VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); + VeriModule *veri_module = veri_lib ? veri_lib->GetModule(module.c_str(), 1) : nullptr; + if (!veri_module) { + log_error("Can't find module/unit '%s'.\n", module.c_str()); + } + + log("Template '%s' is running for module '%s'.\n", app.c_str(),module.c_str()); + + Map parameters(STRING_HASH); + const char *out_filename = nullptr; + + for (argidx++; argidx < GetSize(args); argidx++) { + std::string error; + if (generator->checkParams(args, argidx, error)) { + if (!error.empty()) + cmd_error(args, argidx, error); + continue; + } + + if (args[argidx] == "-chparam" && argidx < GetSize(args)) { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No param name specified.\n"); + if (!(argidx+2 < GetSize(args))) + cmd_error(args, argidx+2, "No param value specified.\n"); + + const std::string &key = args[++argidx]; + const std::string &value = args[++argidx]; + unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(), + 1 /* force_overwrite */); + if (!new_insertion) + log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str()); + continue; + } + if (args[argidx] == "-out" && argidx < GetSize(args)) { + if (!(argidx+1 < GetSize(args))) + cmd_error(args, argidx+1, "No output file specified.\n"); + out_filename = args[++argidx].c_str(); + continue; + } + + break; + } + if (argidx < GetSize(args)) + cmd_error(args, argidx, "unknown option/parameter"); + + const char *err = generator->validate(); + if (err) + cmd_error(args, argidx, err); + + std::string val; + if (!generator->generate(veri_module, val, ¶meters)) + log_error("%s", val.c_str()); + + FILE *of = stdout; + if (out_filename) { + of = fopen(out_filename, "w"); + if (of == nullptr) + log_error("Can't open '%s' for writing: %s\n", out_filename, strerror(errno)); + log("Writing output to '%s'\n",out_filename); + } + fprintf(of, "%s\n",val.c_str()); + fflush(of); + if (of!=stdout) + fclose(of); + goto check_error; + } +#endif if (GetSize(args) > argidx && args[argidx] == "-import") { std::set<Netlist*> nl_todo, nl_done; @@ -2467,15 +3129,20 @@ struct VerificPass : public Pass { std::set<std::string> top_mod_names; +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + InitialAssertions::Rewrite(work, ¶meters); +#endif if (mode_all) { log("Running hier_tree::ElaborateAll().\n"); - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); Array veri_libs, vhdl_libs; +#ifdef VERIFIC_VHDL_SUPPORT + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); +#endif if (veri_lib) veri_libs.InsertLast(veri_lib); Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, ¶meters); @@ -2491,40 +3158,44 @@ struct VerificPass : public Pass { if (argidx == GetSize(args)) cmd_error(args, argidx, "No top module specified.\n"); + VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); +#ifdef VERIFIC_VHDL_SUPPORT + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); +#endif + Array veri_modules, vhdl_units; for (; argidx < GetSize(args); argidx++) { const char *name = args[argidx].c_str(); top_mod_names.insert(name); - VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); - - if (veri_lib) { - VeriModule *veri_module = veri_lib->GetModule(name, 1); - if (veri_module) { - log("Adding Verilog module '%s' to elaboration queue.\n", name); - veri_modules.InsertLast(veri_module); - continue; - } - // Also elaborate all root modules since they may contain bind statements - MapIter mi; - FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { - if (!veri_module->IsRootModule()) continue; - veri_modules.InsertLast(veri_module); - } + VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr; + if (veri_module) { + log("Adding Verilog module '%s' to elaboration queue.\n", name); + veri_modules.InsertLast(veri_module); + continue; } - - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); - VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(name); +#ifdef VERIFIC_VHDL_SUPPORT + VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr; if (vhdl_unit) { log("Adding VHDL unit '%s' to elaboration queue.\n", name); vhdl_units.InsertLast(vhdl_unit); continue; } - +#endif log_error("Can't find module/unit '%s'.\n", name); } + if (veri_lib) { + // Also elaborate all root modules since they may contain bind statements + MapIter mi; + VeriModule *veri_module; + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module->IsRootModule()) continue; + veri_modules.InsertLast(veri_module); + } + } + log("Running hier_tree::Elaborate().\n"); Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, ¶meters); Netlist *nl; @@ -2571,7 +3242,9 @@ struct VerificPass : public Pass { } veri_file::Reset(); +#ifdef VERIFIC_VHDL_SUPPORT vhdl_file::Reset(); +#endif Libset::Reset(); verific_incdirs.clear(); verific_libdirs.clear(); @@ -2579,6 +3252,65 @@ struct VerificPass : public Pass { goto check_error; } + if (argidx < GetSize(args) && args[argidx] == "-cfg") + { + if (argidx+1 == GetSize(args)) { + MapIter mi; + const char *k, *s; + unsigned long v; + pool<std::string> lines; + FOREACH_MAP_ITEM(RuntimeFlags::GetVarMap(), mi, &k, &v) { + lines.insert(stringf("%s %lu", k, v)); + } + FOREACH_MAP_ITEM(RuntimeFlags::GetStringVarMap(), mi, &k, &s) { + if (s == nullptr) + lines.insert(stringf("%s NULL", k)); + else + lines.insert(stringf("%s \"%s\"", k, s)); + } + lines.sort(); + for (auto &line : lines) + log("verific -cfg %s\n", line.c_str()); + goto check_error; + } + + if (argidx+2 == GetSize(args)) { + const char *k = args[argidx+1].c_str(); + if (RuntimeFlags::HasUnsignedVar(k)) { + log("verific -cfg %s %lu\n", k, RuntimeFlags::GetVar(k)); + goto check_error; + } + if (RuntimeFlags::HasStringVar(k)) { + const char *s = RuntimeFlags::GetStringVar(k); + if (s == nullptr) + log("verific -cfg %s NULL\n", k); + else + log("verific -cfg %s \"%s\"\n", k, s); + goto check_error; + } + log_cmd_error("Can't find Verific Runtime flag '%s'.\n", k); + } + + if (argidx+3 == GetSize(args)) { + const auto &k = args[argidx+1], &v = args[argidx+2]; + if (v == "NULL") { + RuntimeFlags::SetStringVar(k.c_str(), nullptr); + goto check_error; + } + if (v[0] == '"') { + std::string s = v.substr(1, GetSize(v)-2); + RuntimeFlags::SetStringVar(k.c_str(), v.c_str()); + goto check_error; + } + char *endptr; + unsigned long n = strtol(v.c_str(), &endptr, 0); + if (*endptr == 0) { + RuntimeFlags::SetVar(k.c_str(), n); + goto check_error; + } + } + } + cmd_error(args, argidx, "Missing or unsupported mode parameter.\n"); check_error: @@ -2590,11 +3322,11 @@ struct VerificPass : public Pass { void execute(std::vector<std::string>, RTLIL::Design *) override { log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" - "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" - "https://www.symbioticeda.com/seda-suite\n" + "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" + "https://www.yosyshq.com/\n" "\n" - "Contact office@symbioticeda.com for free evaluation\n" - "binaries of Symbiotic EDA Suite.\n"); + "Contact office@yosyshq.com for free evaluation\n" + "binaries of YosysHQ Tabby CAD Suite.\n"); } #endif } VerificPass; @@ -2614,11 +3346,19 @@ struct ReadPass : public Pass { log("the language version (and before file names) to set additional verilog defines.\n"); log("\n"); log("\n"); +#ifdef VERIFIC_VHDL_SUPPORT log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} <vhdl-file>..\n"); log("\n"); log("Load the specified VHDL files. (Requires Verific.)\n"); log("\n"); log("\n"); +#endif + log(" read {-f|-F} <command-file>\n"); + log("\n"); + log("Load and execute the specified command file. (Requires Verific.)\n"); + log("Check verific command for more information about supported commands in file.\n"); + log("\n"); + log("\n"); log(" read -define <macro>[=<value>]..\n"); log("\n"); log("Set global Verilog/SystemVerilog defines.\n"); @@ -2695,6 +3435,7 @@ struct ReadPass : public Pass { return; } +#ifdef VERIFIC_VHDL_SUPPORT if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") { if (use_verific) { args[0] = "verific"; @@ -2704,6 +3445,16 @@ struct ReadPass : public Pass { } return; } +#endif + if (args[1] == "-f" || args[1] == "-F") { + if (use_verific) { + args[0] = "verific"; + Pass::call(design, args); + } else { + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); + } + return; + } if (args[1] == "-define") { if (use_verific) { diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index f168a2588..9d5beb787 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -50,6 +50,7 @@ struct VerificClocking { RTLIL::Cell *addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value = Const()); RTLIL::Cell *addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value); RTLIL::Cell *addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q); + RTLIL::Cell *addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q); bool property_matches_sequence(const VerificClocking &seq) const { if (clock_net != seq.clock_net) diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 49c0c40ac..1bbdcf016 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1040,8 +1040,14 @@ struct VerificSvaImporter [[noreturn]] void parser_error(Instance *inst) { - parser_error(stringf("Verific SVA primitive %s (%s) is currently unsupported in this context", - inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); + std::string msg; + if (inst->Type() == PRIM_SVA_MATCH_ITEM_TRIGGER || inst->Type() == PRIM_SVA_MATCH_ITEM_ASSIGN) + { + msg = "SVA sequences with local variable assignments are currently not supported.\n"; + } + + parser_error(stringf("%sVerific SVA primitive %s (%s) is currently unsupported in this context", + msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); } dict<Net*, bool, hash_ptr_ops> check_expression_cache; @@ -1753,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/Makefile.inc b/frontends/verilog/Makefile.inc index cf9b9531e..2c923f0b7 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -6,7 +6,7 @@ GENFILES += frontends/verilog/verilog_lexer.cc frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y $(Q) mkdir -p $(dir $@) - $(P) $(BISON) -o $@ -d -r all -b frontends/verilog/verilog_parser $< + $(P) $(BISON) -Wall -Werror -o $@ -d -r all -b frontends/verilog/verilog_parser $< frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index 230dfadbf..a4dfbc7ec 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index ea23139e2..883531e78 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -36,6 +36,7 @@ #include "verilog_frontend.h" #include "kernel/log.h" #include <assert.h> +#include <stack> #include <stdarg.h> #include <stdio.h> #include <string.h> @@ -141,6 +142,16 @@ static std::string next_token(bool pass_newline = false) return_char(ch); } } + else if (ch == '\\') + { + while ((ch = next_char()) != 0) { + if (ch < 33 || ch > 126) { + return_char(ch); + break; + } + token += ch; + } + } else if (ch == '/') { if ((ch = next_char()) != 0) { @@ -321,7 +332,6 @@ struct define_body_t define_map_t::define_map_t() { add("YOSYS", "1"); - add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); } // We must define this destructor here (rather than relying on the default), because we need to @@ -335,6 +345,11 @@ define_map_t::add(const std::string &name, const std::string &txt, const arg_map defines[name] = std::unique_ptr<define_body_t>(new define_body_t(txt, args)); } +void define_map_t::add(const std::string &name, const define_body_t &body) +{ + defines[name] = std::unique_ptr<define_body_t>(new define_body_t(body)); +} + void define_map_t::merge(const define_map_t &map) { for (const auto &pr : map.defines) { @@ -391,13 +406,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]); @@ -438,7 +456,17 @@ static bool read_argument(std::string &dest) } } -static bool try_expand_macro(define_map_t &defines, std::string &tok) +using macro_arg_stack_t = std::stack<std::pair<std::string, define_body_t>>; + +static void restore_macro_arg(define_map_t &defines, macro_arg_stack_t ¯o_arg_stack) +{ + log_assert(!macro_arg_stack.empty()); + auto &overwritten_arg = macro_arg_stack.top(); + defines.add(overwritten_arg.first, overwritten_arg.second); + macro_arg_stack.pop(); +} + +static bool try_expand_macro(define_map_t &defines, macro_arg_stack_t ¯o_arg_stack, std::string &tok) { if (tok == "`\"") { std::string literal("\""); @@ -448,7 +476,7 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) if (ntok == "`\"") { insert_input(literal+"\""); return true; - } else if (!try_expand_macro(defines, ntok)) { + } else if (!try_expand_macro(defines, macro_arg_stack, ntok)) { literal += ntok; } } @@ -475,7 +503,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) { @@ -484,6 +521,10 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) args.push_back(arg); } for (const auto &pr : body->args.get_vals(name, args)) { + if (const define_body_t *existing = defines.find(pr.first)) { + macro_arg_stack.push({pr.first, *existing}); + insert_input("`__restore_macro_arg "); + } defines.add(pr.first, pr.second); } } else { @@ -714,9 +755,19 @@ frontend_verilog_preproc(std::istream &f, defines.merge(pre_defines); defines.merge(global_defines_cache); + macro_arg_stack_t macro_arg_stack; std::vector<std::string> filename_stack; + // We are inside pass_level levels of satisfied ifdefs, and then within + // fail_level levels of unsatisfied ifdefs. The unsatisfied ones are + // always within satisfied ones — even if some condition within is true, + // the parent condition failing renders it moot. int ifdef_fail_level = 0; - bool in_elseif = false; + int ifdef_pass_level = 0; + // For the outermost unsatisfied ifdef, true iff that ifdef already + // had a satisfied branch, and further elsif/else branches should be + // considered unsatisfied even if the condition is true. + // Meaningless if ifdef_fail_level == 0. + bool ifdef_already_satisfied = false; output_code.clear(); input_buffer.clear(); @@ -732,42 +783,70 @@ frontend_verilog_preproc(std::istream &f, if (tok == "`endif") { if (ifdef_fail_level > 0) ifdef_fail_level--; - if (ifdef_fail_level == 0) - in_elseif = false; + else if (ifdef_pass_level > 0) + ifdef_pass_level--; + else + log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); continue; } if (tok == "`else") { - if (ifdef_fail_level == 0) + if (ifdef_fail_level == 0) { + if (ifdef_pass_level == 0) + log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); + ifdef_pass_level--; ifdef_fail_level = 1; - else if (ifdef_fail_level == 1 && !in_elseif) + ifdef_already_satisfied = true; + } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied) { ifdef_fail_level = 0; + ifdef_pass_level++; + ifdef_already_satisfied = true; + } continue; } if (tok == "`elsif") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level == 0) - ifdef_fail_level = 1, in_elseif = true; - else if (ifdef_fail_level == 1 && defines.find(name)) - ifdef_fail_level = 0, in_elseif = true; + if (ifdef_fail_level == 0) { + if (ifdef_pass_level == 0) + log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); + ifdef_pass_level--; + ifdef_fail_level = 1; + ifdef_already_satisfied = true; + } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied && defines.find(name)) { + ifdef_fail_level = 0; + ifdef_pass_level++; + ifdef_already_satisfied = true; + } continue; } if (tok == "`ifdef") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level > 0 || !defines.find(name)) + if (ifdef_fail_level > 0 || !defines.find(name)) { ifdef_fail_level++; + } else { + ifdef_pass_level++; + ifdef_already_satisfied = true; + } + if (ifdef_fail_level == 1) + ifdef_already_satisfied = false; continue; } if (tok == "`ifndef") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level > 0 || defines.find(name)) + if (ifdef_fail_level > 0 || defines.find(name)) { ifdef_fail_level++; + } else { + ifdef_pass_level++; + ifdef_already_satisfied = true; + } + if (ifdef_fail_level == 1) + ifdef_already_satisfied = false; continue; } @@ -780,7 +859,7 @@ frontend_verilog_preproc(std::istream &f, if (tok == "`include") { skip_spaces(); std::string fn = next_token(true); - while (try_expand_macro(defines, fn)) { + while (try_expand_macro(defines, macro_arg_stack, fn)) { fn = next_token(); } while (1) { @@ -887,12 +966,21 @@ frontend_verilog_preproc(std::istream &f, continue; } - if (try_expand_macro(defines, tok)) + if (tok == "`__restore_macro_arg") { + restore_macro_arg(defines, macro_arg_stack); + continue; + } + + if (try_expand_macro(defines, macro_arg_stack, tok)) continue; output_code.push_back(tok); } + if (ifdef_fail_level > 0 || ifdef_pass_level > 0) { + log_error("Unterminated preprocessor conditional!\n"); + } + std::string output; for (auto &str : output_code) output += str; diff --git a/frontends/verilog/preproc.h b/frontends/verilog/preproc.h index 673d633c0..330855a92 100644 --- a/frontends/verilog/preproc.h +++ b/frontends/verilog/preproc.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -42,6 +42,7 @@ struct define_map_t // Add a definition, overwriting any existing definition for name. void add(const std::string &name, const std::string &txt, const arg_map_t *args = nullptr); + void add(const std::string &name, const define_body_t &body); // Merge in another map of definitions (which take precedence // over anything currently defined). diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 2e9c9b2e2..9b277c6b9 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,8 +61,6 @@ static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std } } } - user_type_stack.clear(); - user_type_stack.push_back(new UserTypeMap()); } struct VerilogFrontend : public Frontend { @@ -84,6 +82,9 @@ struct VerilogFrontend : public Frontend { log(" enable support for SystemVerilog assertions and some Yosys extensions\n"); log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); log("\n"); + log(" -nosynthesis\n"); + log(" don't add implicit -D SYNTHESIS\n"); + log("\n"); log(" -noassert\n"); log(" ignore assert() statements\n"); log("\n"); @@ -225,8 +226,8 @@ struct VerilogFrontend : public Frontend { log("the syntax of the code, rather than to rely on read_verilog for that.\n"); log("\n"); log("Depending on if read_verilog is run in -formal mode, either the macro\n"); - log("SYNTHESIS or FORMAL is defined automatically. In addition, read_verilog\n"); - log("always defines the macro YOSYS.\n"); + log("SYNTHESIS or FORMAL is defined automatically, unless -nosynthesis is used.\n"); + log("In addition, read_verilog always defines the macro YOSYS.\n"); log("\n"); log("See the Yosys README file for a list of non-standard Verilog features\n"); log("supported by the Yosys Verilog front-end.\n"); @@ -255,6 +256,7 @@ struct VerilogFrontend : public Frontend { bool flag_defer = false; bool flag_noblackbox = false; bool flag_nowb = false; + bool flag_nosynthesis = false; define_map_t defines_map; std::list<std::string> include_dirs; @@ -282,6 +284,10 @@ struct VerilogFrontend : public Frontend { formal_mode = true; continue; } + if (arg == "-nosynthesis") { + flag_nosynthesis = true; + continue; + } if (arg == "-noassert") { noassert_mode = true; continue; @@ -446,6 +452,10 @@ struct VerilogFrontend : public Frontend { } break; } + + if (formal_mode || !flag_nosynthesis) + defines_map.add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); + extra_args(f, filename, args, argidx); log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str()); @@ -472,6 +482,19 @@ struct VerilogFrontend : public Frontend { // make package typedefs available to parser add_package_types(pkg_user_types, design->verilog_packages); + UserTypeMap global_types_map; + for (auto def : design->verilog_globals) { + if (def->type == AST::AST_TYPEDEF) { + global_types_map[def->str] = def; + } + } + + log_assert(user_type_stack.empty()); + // use previous global typedefs as bottom level of user type stack + user_type_stack.push_back(std::move(global_types_map)); + // add a new empty type map to allow overriding existing global definitions + user_type_stack.push_back(UserTypeMap()); + frontend_verilog_yyset_lineno(1); frontend_verilog_yyrestart(NULL); frontend_verilog_yyparse(); @@ -494,6 +517,10 @@ struct VerilogFrontend : public Frontend { if (!flag_nopp) delete lexin; + // only the previous and new global type maps remain + log_assert(user_type_stack.size() == 2); + user_type_stack.clear(); + delete current_ast; current_ast = NULL; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index aa7881038..8454e7999 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -47,7 +47,7 @@ namespace VERILOG_FRONTEND // names of locally typedef'ed types in a stack typedef std::map<std::string, AST::AstNode*> UserTypeMap; - extern std::vector<UserTypeMap *> user_type_stack; + extern std::vector<UserTypeMap> user_type_stack; // names of package typedef'ed types extern dict<std::string, AST::AstNode*> pkg_user_types; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f2241066f..89c1aa895 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -37,6 +37,8 @@ #ifdef __clang__ // bison generates code using the 'register' storage class specifier #pragma clang diagnostic ignored "-Wdeprecated-register" +// flex generates weirdly-indented code +#pragma clang diagnostic ignored "-Wmisleading-indentation" #endif #include "kernel/log.h" @@ -103,7 +105,7 @@ static bool isUserType(std::string &s) { // check current scope then outer scopes for a name for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) { - if ((*it)->count(s) > 0) { + if (it->count(s) > 0) { return true; } } @@ -234,7 +236,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); } @@ -260,6 +262,7 @@ static bool isUserType(std::string &s) "const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } "checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } "endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } +"bind" { if (formal_mode) return TOK_BIND; SV_KEYWORD(TOK_BIND); } "final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } "var" { SV_KEYWORD(TOK_VAR); } @@ -267,6 +270,7 @@ static bool isUserType(std::string &s) "int" { SV_KEYWORD(TOK_INT); } "byte" { SV_KEYWORD(TOK_BYTE); } "shortint" { SV_KEYWORD(TOK_SHORTINT); } +"longint" { SV_KEYWORD(TOK_LONGINT); } "eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } "s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } @@ -275,8 +279,11 @@ static bool isUserType(std::string &s) "output" { return TOK_OUTPUT; } "inout" { return TOK_INOUT; } "wire" { return TOK_WIRE; } +"tri" { return TOK_WIRE; } "wor" { return TOK_WOR; } +"trior" { return TOK_WOR; } "wand" { return TOK_WAND; } +"triand" { return TOK_WAND; } "reg" { return TOK_REG; } "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } @@ -430,8 +437,13 @@ supply1 { return TOK_SUPPLY1; } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { - log_warning("Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n" - "Yosys does support them but it is recommended to use `ifdef constructs instead!\n"); + log_warning( + "Encountered `translate_off' comment! Such legacy hot " + "comments are supported by Yosys, but are not part of " + "any formal language specification. Using a portable " + "and standards-compliant construct such as `ifdef is " + "recommended!\n" + ); printed_warning = true; } BEGIN(SYNOPSYS_TRANSLATE_OFF); @@ -446,8 +458,13 @@ supply1 { return TOK_SUPPLY1; } <SYNOPSYS_FLAGS>full_case { static bool printed_warning = false; if (!printed_warning) { - log_warning("Found one of those horrible `(synopsys|synthesis) full_case' comments.\n" - "Yosys does support them but it is recommended to use Verilog `full_case' attributes instead!\n"); + log_warning( + "Encountered `full_case' comment! Such legacy hot " + "comments are supported by Yosys, but are not part of " + "any formal language specification. Using the Verilog " + "`full_case' attribute or the SystemVerilog `unique' " + "or `unique0' keywords is recommended!\n" + ); printed_warning = true; } return TOK_SYNOPSYS_FULL_CASE; @@ -455,8 +472,13 @@ supply1 { return TOK_SUPPLY1; } <SYNOPSYS_FLAGS>parallel_case { static bool printed_warning = false; if (!printed_warning) { - log_warning("Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n" - "Yosys does support them but it is recommended to use Verilog `parallel_case' attributes instead!\n"); + log_warning( + "Encountered `parallel_case' comment! Such legacy hot " + "comments are supported by Yosys, but are not part of " + "any formal language specification. Using the Verilog " + "`parallel_case' attribute or the SystemVerilog " + "`unique' or `priority' keywords is recommended!\n" + ); printed_warning = true; } return TOK_SYNOPSYS_PARALLEL_CASE; @@ -528,11 +550,18 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { ".*" { return TOK_WILDCARD_CONNECT; } -"|=" { SV_KEYWORD(TOK_OR_ASSIGN); } -"&=" { SV_KEYWORD(TOK_AND_ASSIGN); } -"+=" { SV_KEYWORD(TOK_PLUS_ASSIGN); } +"|=" { SV_KEYWORD(TOK_BIT_OR_ASSIGN); } +"&=" { SV_KEYWORD(TOK_BIT_AND_ASSIGN); } +"+=" { SV_KEYWORD(TOK_ADD_ASSIGN); } "-=" { SV_KEYWORD(TOK_SUB_ASSIGN); } -"^=" { SV_KEYWORD(TOK_XOR_ASSIGN); } +"^=" { SV_KEYWORD(TOK_BIT_XOR_ASSIGN); } +"/=" { SV_KEYWORD(TOK_DIV_ASSIGN); } +"%=" { SV_KEYWORD(TOK_MOD_ASSIGN); } +"*=" { SV_KEYWORD(TOK_MUL_ASSIGN); } +"<<=" { SV_KEYWORD(TOK_SHL_ASSIGN); } +">>=" { SV_KEYWORD(TOK_SHR_ASSIGN); } +"<<<=" { SV_KEYWORD(TOK_SSHL_ASSIGN); } +">>>=" { SV_KEYWORD(TOK_SSHR_ASSIGN); } [-+]?[=*]> { if (!specify_mode) REJECT; diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 656910c0c..171e098a5 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,6 +33,8 @@ * */ +%require "3.0" + %{ #include <list> #include <stack> @@ -54,7 +56,7 @@ namespace VERILOG_FRONTEND { dict<IdString, AstNode*> *attr_list, default_attr_list; std::stack<dict<IdString, AstNode*> *> attr_list_stack; dict<IdString, AstNode*> *albuf; - std::vector<UserTypeMap*> user_type_stack; + std::vector<UserTypeMap> user_type_stack; dict<std::string, AstNode*> pkg_user_types; std::vector<AstNode*> ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; @@ -127,13 +129,22 @@ struct specify_rise_fall { specify_triple fall; }; +static void addWiretypeNode(std::string *name, AstNode *node) +{ + log_assert(node); + node->is_custom_type = true; + node->children.push_back(new AstNode(AST_WIRETYPE)); + node->children.back()->str = *name; + delete name; +} + static void addTypedefNode(std::string *name, AstNode *node) { log_assert(node); auto *tnode = new AstNode(AST_TYPEDEF, node); tnode->str = *name; - auto user_types = user_type_stack.back(); - (*user_types)[*name] = tnode; + auto &user_types = user_type_stack.back(); + user_types[*name] = tnode; if (current_ast_mod && current_ast_mod->type == AST_PACKAGE) { // typedef inside a package so we need the qualified name auto qname = current_ast_mod->str + "::" + (*name).substr(1); @@ -145,8 +156,7 @@ static void addTypedefNode(std::string *name, AstNode *node) static void enterTypeScope() { - auto user_types = new UserTypeMap(); - user_type_stack.push_back(user_types); + user_type_stack.push_back(UserTypeMap()); } static void exitTypeScope() @@ -157,18 +167,24 @@ static void exitTypeScope() static bool isInLocalScope(const std::string *name) { // tests if a name was declared in the current block scope - auto user_types = user_type_stack.back(); - return (user_types->count(*name) > 0); + auto &user_types = user_type_stack.back(); + return (user_types.count(*name) > 0); } static AstNode *getTypeDefinitionNode(std::string type_name) { - // return the definition nodes from the typedef statement - auto user_types = user_type_stack.back(); - log_assert(user_types->count(type_name) > 0); - auto typedef_node = (*user_types)[type_name]; - log_assert(typedef_node->type == AST_TYPEDEF); - return typedef_node->children[0]; + // check current scope then outer scopes for a name + for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) { + if (it->count(type_name) > 0) { + // return the definition nodes from the typedef statement + auto typedef_node = (*it)[type_name]; + log_assert(typedef_node->type == AST_TYPEDEF); + return typedef_node->children[0]; + } + } + + // The lexer recognized the name as a TOK_USER_TYPE, but now we can't find it anymore? + log_error("typedef for user type `%s' not found", type_name.c_str()); } static AstNode *copyTypeDefinition(std::string type_name) @@ -210,17 +226,95 @@ static AstNode *checkRange(AstNode *type_node, AstNode *range_node) return range_node; } -static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) +static void rewriteRange(AstNode *rangeNode) { - node->type = AST_MEMORY; if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { // SV array size [n], rewrite as [n-1:0] rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); rangeNode->children.push_back(AstNode::mkconst_int(0, false)); } +} + +static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) +{ + node->type = AST_MEMORY; + if (rangeNode->type == AST_MULTIRANGE) { + for (auto *itr : rangeNode->children) + rewriteRange(itr); + } else + rewriteRange(rangeNode); node->children.push_back(rangeNode); } +static void checkLabelsMatch(const char *element, const std::string *before, const std::string *after) +{ + if (!before && after) + frontend_verilog_yyerror("%s missing where end label (%s) was given.", + element, after->c_str() + 1); + if (before && after && *before != *after) + frontend_verilog_yyerror("%s (%s) and end label (%s) don't match.", + element, before->c_str() + 1, after->c_str() + 1); +} + +// This transforms a loop like +// for (genvar i = 0; i < 10; i++) begin : blk +// to +// genvar _i; +// for (_i = 0; _i < 10; _i++) begin : blk +// localparam i = _i; +// where `_i` is actually some auto-generated name. +static void rewriteGenForDeclInit(AstNode *loop) +{ + // check if this generate for loop contains an inline declaration + log_assert(loop->type == AST_GENFOR); + AstNode *decl = loop->children[0]; + if (decl->type == AST_ASSIGN_EQ) + return; + log_assert(decl->type == AST_GENVAR); + log_assert(loop->children.size() == 5); + + // identify each component of the loop + AstNode *init = loop->children[1]; + AstNode *cond = loop->children[2]; + AstNode *incr = loop->children[3]; + AstNode *body = loop->children[4]; + log_assert(init->type == AST_ASSIGN_EQ); + log_assert(incr->type == AST_ASSIGN_EQ); + log_assert(body->type == AST_GENBLOCK); + + // create a unique name for the genvar + std::string old_str = decl->str; + std::string new_str = stringf("$genfordecl$%d$%s", autoidx++, old_str.c_str()); + + // rename and move the genvar declaration to the containing description + decl->str = new_str; + loop->children.erase(loop->children.begin()); + log_assert(current_ast_mod != nullptr); + current_ast_mod->children.push_back(decl); + + // create a new localparam with old name so that the items in the loop + // can simply use the old name and shadow it as necessary + AstNode *indirect = new AstNode(AST_LOCALPARAM); + indirect->str = old_str; + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = new_str; + indirect->children.push_back(ident); + + body->children.insert(body->children.begin(), indirect); + + // only perform the renaming for the initialization, guard, and + // incrementation to enable proper shadowing of the synthetic localparam + std::function<void(AstNode*)> substitute = [&](AstNode *node) { + if (node->type == AST_IDENTIFIER && node->str == old_str) + node->str = new_str; + for (AstNode *child : node->children) + substitute(child); + }; + substitute(init); + substitute(cond); + substitute(incr); +} + %} %define api.prefix {frontend_verilog_yy} @@ -244,6 +338,8 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) struct specify_rise_fall *specify_rise_fall_ptr; bool boolean; char ch; + int integer; + YOSYS_NAMESPACE_PREFIX AST::AstNodeType ast_node_type; } %token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE @@ -256,7 +352,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP %token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC -%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_PLUS_ASSIGN TOK_ALWAYS TOK_INITIAL +%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT %token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC @@ -268,18 +364,25 @@ 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_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 - -%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int -%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list +%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_LONGINT TOK_UNION +%token TOK_BIT_OR_ASSIGN TOK_BIT_AND_ASSIGN TOK_BIT_XOR_ASSIGN TOK_ADD_ASSIGN +%token TOK_SUB_ASSIGN TOK_DIV_ASSIGN TOK_MOD_ASSIGN TOK_MUL_ASSIGN +%token TOK_SHL_ASSIGN TOK_SHR_ASSIGN TOK_SSHL_ASSIGN TOK_SSHR_ASSIGN +%token TOK_BIND + +%type <ast> range range_or_multirange non_opt_range non_opt_multirange +%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type %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 <ast> opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type +%type <boolean> opt_property always_comb_or_latch always_or_always_ff +%type <boolean> opt_signedness_default_signed opt_signedness_default_unsigned +%type <integer> integer_atom_type integer_vector_type %type <al> attr case_attr %type <ast> struct_union +%type <ast_node_type> asgn_binop +%type <ast> genvar_identifier %type <specify_target_ptr> specify_target %type <specify_triple_ptr> specify_triple specify_opt_triple @@ -299,14 +402,14 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %left '+' '-' %left '*' '/' '%' %left OP_POW -%left OP_CAST -%right UNARY_OPS +%precedence OP_CAST +%precedence UNARY_OPS %define parse.error verbose %define parse.lac full -%nonassoc FAKE_THEN -%nonassoc TOK_ELSE +%precedence FAKE_THEN +%precedence TOK_ELSE %debug %locations @@ -333,7 +436,8 @@ design: typedef_decl design | package design | interface design | - /* empty */; + bind_directive design | + %empty; attr: { @@ -355,7 +459,7 @@ attr_opt: attr_opt ATTR_BEGIN opt_attr_list ATTR_END { SET_RULE_LOC(@$, @2, @$); }| - /* empty */; + %empty; defattr: DEFATTR_BEGIN { @@ -376,7 +480,7 @@ defattr: } DEFATTR_END; opt_attr_list: - attr_list | /* empty */; + attr_list | %empty; attr_list: attr_assign | @@ -436,7 +540,6 @@ module: port_counter = 0; mod->str = *$4; append_attr(mod, $1); - delete $4; } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE opt_label { if (port_stubs.size() != 0) frontend_verilog_yyerror("Missing details for module port `%s'.", @@ -444,18 +547,21 @@ module: SET_AST_NODE_LOC(ast_stack.back(), @2, @$); ast_stack.pop_back(); log_assert(ast_stack.size() == 1); + checkLabelsMatch("Module name", $4, $11); current_ast_mod = NULL; + delete $4; + delete $11; exitTypeScope(); }; module_para_opt: - '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | /* empty */; + '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | %empty; module_para_list: single_module_para | module_para_list ',' single_module_para; single_module_para: - /* empty */ | + %empty | attr TOK_PARAMETER { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_PARAMETER); @@ -471,33 +577,34 @@ single_module_para: single_param_decl; module_args_opt: - '(' ')' | /* empty */ | '(' module_args optional_comma ')'; + '(' ')' | %empty | '(' module_args optional_comma ')'; module_args: module_arg | module_args ',' module_arg; optional_comma: - ',' | /* empty */; + ',' | %empty; module_arg_opt_assignment: '=' expr { if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { - AstNode *wire = new AstNode(AST_IDENTIFIER); - wire->str = ast_stack.back()->children.back()->str; if (ast_stack.back()->children.back()->is_input) { AstNode *n = ast_stack.back()->children.back(); if (n->attributes.count(ID::defaultvalue)) delete n->attributes.at(ID::defaultvalue); n->attributes[ID::defaultvalue] = $2; - } else - if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) - ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); - else - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); + } else { + AstNode *wire = new AstNode(AST_IDENTIFIER); + wire->str = ast_stack.back()->children.back()->str; + if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) + ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); + else + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); + } } else frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value."); } | - /* empty */; + %empty; module_arg: TOK_ID { @@ -534,8 +641,9 @@ module_arg: node->str = *$4; SET_AST_NODE_LOC(node, @4, @4); node->port_id = ++port_counter; - if ($3 != NULL) - node->children.push_back($3); + AstNode *range = checkRange(node, $3); + if (range != NULL) + node->children.push_back(range); if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str()); if (node->is_reg && node->is_input && !node->is_output && !sv_mode) @@ -560,20 +668,18 @@ package: append_attr(mod, $1); } ';' package_body TOK_ENDPACKAGE opt_label { ast_stack.pop_back(); + checkLabelsMatch("Package name", $4, $9); current_ast_mod = NULL; + delete $4; + delete $9; exitTypeScope(); }; package_body: - package_body package_body_stmt - | // optional - ; + package_body package_body_stmt | %empty; package_body_stmt: - typedef_decl - | localparam_decl - | param_decl - ; + typedef_decl | localparam_decl | param_decl | task_func_decl; interface: TOK_INTERFACE { @@ -599,42 +705,100 @@ interface: }; interface_body: - interface_body interface_body_stmt |; + interface_body interface_body_stmt | %empty; interface_body_stmt: param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | - modport_stmt; + modport_stmt | bind_directive; + +bind_directive: + TOK_BIND { + AstNode *bnode = new AstNode(AST_BIND); + ast_stack.back()->children.push_back(bnode); + ast_stack.push_back(bnode); + } + bind_target { + // bind_target should have added at least one child + log_assert(ast_stack.back()->children.size() >= 1); + } + TOK_ID { + // The single_cell parser in cell_list_no_array uses astbuf1 as + // a sort of template for constructing cells. + astbuf1 = new AstNode(AST_CELL); + astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); + astbuf1->children[0]->str = *$5; + delete $5; + } + cell_parameter_list_opt cell_list_no_array ';' { + // cell_list should have added at least one more child + log_assert(ast_stack.back()->children.size() >= 2); + delete astbuf1; + ast_stack.pop_back(); + }; + +// bind_target matches the target of the bind (everything before +// bind_instantiation in the IEEE 1800 spec). +// +// We can't use the BNF from the spec directly because it's ambiguous: +// something like "bind foo bar_i (.*)" can either be interpreted with "foo" as +// a module or interface identifier (matching bind_target_scope in the spec) or +// by considering foo as a degenerate hierarchical identifier with no '.' +// characters, followed by no bit select (which matches bind_target_instance in +// the spec). +// +// Instead, we resolve everything as an instance name and then deal with the +// ambiguity when converting to RTLIL / in the hierarchy pass. +bind_target: + bind_target_instance opt_bind_target_instance_list; + +// An optional list of target instances for a bind statement, introduced by a +// colon. +opt_bind_target_instance_list: + ':' bind_target_instance_list | + %empty; + +bind_target_instance_list: + bind_target_instance | + bind_target_instance_list ',' bind_target_instance; + +// A single target instance for a bind statement. The top of ast_stack will be +// the bind node where we should add it. +bind_target_instance: + hierarchical_id { + auto *node = new AstNode(AST_IDENTIFIER); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + }; + +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 */; + non_opt_delay | %empty; -wire_type: - { - astbuf3 = new AstNode(AST_WIRE); - current_wire_rand = false; - current_wire_const = false; - } wire_type_token_list { - $$ = astbuf3; - SET_RULE_LOC(@$, @2, @$); - }; +io_wire_type: + { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness + { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; -wire_type_token_list: - wire_type_token | - wire_type_token_list wire_type_token | - wire_type_token_io | - hierarchical_type_id { - astbuf3->is_custom_type = true; - astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); - astbuf3->children.back()->str = *$1; - delete $1; - }; +non_io_wire_type: + { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + wire_type_const_rand wire_type_token wire_type_signedness + { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + +wire_type: + io_wire_type | + non_io_wire_type; wire_type_token_io: TOK_INPUT { @@ -648,29 +812,49 @@ wire_type_token_io: astbuf3->is_output = true; }; -wire_type_token: - TOK_WIRE { +wire_type_signedness: + TOK_SIGNED { astbuf3->is_signed = true; } | + TOK_UNSIGNED { astbuf3->is_signed = false; } | + %empty; + +wire_type_const_rand: + TOK_RAND TOK_CONST { + current_wire_rand = true; + current_wire_const = true; } | - TOK_WOR { - astbuf3->is_wor = true; + TOK_CONST { + current_wire_const = true; } | - TOK_WAND { - astbuf3->is_wand = true; + TOK_RAND { + current_wire_rand = true; + } | + %empty; + +opt_wire_type_token: + wire_type_token | %empty; + +wire_type_token: + // nets + net_type { } | + net_type logic_type { + } | + // regs TOK_REG { astbuf3->is_reg = true; } | - TOK_LOGIC { - astbuf3->is_logic = true; + TOK_VAR TOK_REG { + astbuf3->is_reg = true; } | + // logics TOK_VAR { astbuf3->is_logic = true; } | - TOK_INTEGER { - astbuf3->is_reg = true; - astbuf3->range_left = 31; - astbuf3->range_right = 0; - astbuf3->is_signed = true; + TOK_VAR logic_type { + astbuf3->is_logic = true; + } | + logic_type { + astbuf3->is_logic = true; } | TOK_GENVAR { astbuf3->type = AST_GENVAR; @@ -678,17 +862,40 @@ wire_type_token: astbuf3->is_signed = true; astbuf3->range_left = 31; astbuf3->range_right = 0; + }; + +net_type: + TOK_WOR { + astbuf3->is_wor = true; } | - TOK_SIGNED { - astbuf3->is_signed = true; + TOK_WAND { + astbuf3->is_wand = true; } | - TOK_RAND { - current_wire_rand = true; + TOK_WIRE; + +logic_type: + TOK_LOGIC { } | - TOK_CONST { - current_wire_const = true; + integer_atom_type { + astbuf3->range_left = $1 - 1; + astbuf3->range_right = 0; + astbuf3->is_signed = true; + } | + hierarchical_type_id { + addWiretypeNode($1, astbuf3); }; +integer_atom_type: + TOK_INTEGER { $$ = 32; } | + TOK_INT { $$ = 32; } | + TOK_SHORTINT { $$ = 16; } | + TOK_LONGINT { $$ = 64; } | + TOK_BYTE { $$ = 8; } ; + +integer_vector_type: + TOK_LOGIC { $$ = TOK_LOGIC; } | + TOK_REG { $$ = TOK_REG; } ; + non_opt_range: '[' expr ':' expr ']' { $$ = new AstNode(AST_RANGE); @@ -725,7 +932,7 @@ range: non_opt_range { $$ = $1; } | - /* empty */ { + %empty { $$ = NULL; }; @@ -733,21 +940,18 @@ range_or_multirange: range { $$ = $1; } | non_opt_multirange { $$ = $1; }; -range_or_signed_int: - range { $$ = $1; } - | TOK_INTEGER { $$ = makeRange(); } - ; - module_body: module_body module_body_stmt | /* the following line makes the generate..endgenrate keywords optional */ module_body gen_stmt | - /* empty */; + module_body gen_block | + module_body ';' | + %empty; module_body_stmt: task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | - enum_decl | struct_decl | - always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block | /* empty statement */ ';'; + enum_decl | struct_decl | bind_directive | + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; checker_decl: TOK_CHECKER TOK_ID ';' { @@ -806,29 +1010,64 @@ task_func_decl: current_function_or_task = NULL; ast_stack.pop_back(); } | - attr TOK_FUNCTION opt_automatic opt_signed range_or_signed_int TOK_ID { + attr TOK_FUNCTION opt_automatic func_return_type TOK_ID { current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$6; + current_function_or_task->str = *$5; append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); ast_stack.push_back(current_function_or_task); AstNode *outreg = new AstNode(AST_WIRE); - outreg->str = *$6; - outreg->is_signed = $4; + outreg->str = *$5; + outreg->is_signed = false; outreg->is_reg = true; - if ($5 != NULL) { - outreg->children.push_back($5); - outreg->is_signed = $4 || $5->is_signed; - $5->is_signed = false; + if ($4 != NULL) { + outreg->children.push_back($4); + outreg->is_signed = $4->is_signed; + $4->is_signed = false; + outreg->is_custom_type = $4->type == AST_WIRETYPE; } current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; - delete $6; + delete $5; } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); }; +func_return_type: + hierarchical_type_id { + $$ = new AstNode(AST_WIRETYPE); + $$->str = *$1; + delete $1; + } | + opt_type_vec opt_signedness_default_unsigned { + $$ = makeRange(0, 0, $2); + } | + opt_type_vec opt_signedness_default_unsigned non_opt_range { + $$ = $3; + $$->is_signed = $2; + } | + integer_atom_type opt_signedness_default_signed { + $$ = makeRange($1 - 1, 0, $2); + }; + +opt_type_vec: + %empty + | TOK_REG + | TOK_LOGIC + ; + +opt_signedness_default_signed: + %empty { $$ = true; } + | TOK_SIGNED { $$ = true; } + | TOK_UNSIGNED { $$ = false; } + ; +opt_signedness_default_unsigned: + %empty { $$ = false; } + | TOK_SIGNED { $$ = true; } + | TOK_UNSIGNED { $$ = false; } + ; + dpi_function_arg: TOK_ID TOK_ID { current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); @@ -842,28 +1081,20 @@ dpi_function_arg: opt_dpi_function_args: '(' dpi_function_args ')' | - /* empty */; + %empty; dpi_function_args: dpi_function_args ',' dpi_function_arg | dpi_function_args ',' | dpi_function_arg | - /* empty */; + %empty; opt_automatic: TOK_AUTOMATIC | - /* empty */; - -opt_signed: - TOK_SIGNED { - $$ = true; - } | - /* empty */ { - $$ = false; - }; + %empty; task_func_args_opt: - '(' ')' | /* empty */ | '(' { + '(' ')' | %empty | '(' { albuf = nullptr; astbuf1 = nullptr; astbuf2 = nullptr; @@ -879,7 +1110,11 @@ task_func_args: task_func_port: attr wire_type range { + bool prev_was_input = true; + bool prev_was_output = false; if (albuf) { + prev_was_input = astbuf1->is_input; + prev_was_output = astbuf1->is_output; delete astbuf1; if (astbuf2 != NULL) delete astbuf2; @@ -888,6 +1123,12 @@ task_func_port: albuf = $1; astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); + if (!astbuf1->is_input && !astbuf1->is_output) { + if (!sv_mode) + frontend_verilog_yyerror("task/function argument direction missing"); + astbuf1->is_input = prev_was_input; + astbuf1->is_output = prev_was_output; + } } wire_name | { if (!astbuf1) { @@ -904,7 +1145,7 @@ task_func_port: task_func_body: task_func_body behavioral_stmt | - /* empty */; + %empty; /*************************** specify parser ***************************/ @@ -913,7 +1154,7 @@ specify_block: specify_item_list: specify_item specify_item_list | - /* empty */; + %empty; specify_item: specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' { @@ -1069,13 +1310,15 @@ specify_item: cell->children.back()->str = "\\DST"; delete $1; + delete limit; + delete limit2; }; specify_opt_triple: ',' specify_triple { $$ = $2; } | - /* empty */ { + %empty { $$ = nullptr; }; @@ -1083,7 +1326,7 @@ specify_if: TOK_IF '(' expr ')' { $$ = $3; } | - /* empty */ { + %empty { $$ = nullptr; }; @@ -1091,7 +1334,7 @@ specify_condition: TOK_SPECIFY_AND expr { $$ = $2; } | - /* empty */ { + %empty { $$ = nullptr; }; @@ -1124,7 +1367,7 @@ specify_target: specify_edge: TOK_POSEDGE { $$ = 'p'; } | TOK_NEGEDGE { $$ = 'n'; } | - { $$ = 0; }; + %empty { $$ = 0; }; specify_rise_fall: specify_triple { @@ -1231,7 +1474,7 @@ specparam_assignment: ignspec_id '=' ignspec_expr ; ignspec_opt_cond: - TOK_IF '(' ignspec_expr ')' | /* empty */; + TOK_IF '(' ignspec_expr ')' | %empty; path_declaration : simple_path_declaration ';' @@ -1282,9 +1525,7 @@ list_of_path_outputs : list_of_path_outputs ',' specify_output_terminal_descriptor ; opt_polarity_operator : - '+' - | '-' - | ; + '+' | '-' | %empty; // Good enough for the time being specify_input_terminal_descriptor : @@ -1333,37 +1574,34 @@ param_signed: astbuf1->is_signed = true; } | TOK_UNSIGNED { astbuf1->is_signed = false; - } | /* empty */; + } | %empty; param_integer: - TOK_INTEGER { - if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Internal error in param_integer - should not happen?"); - astbuf1->children.push_back(new AstNode(AST_RANGE)); - astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); - astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); - astbuf1->is_signed = true; - } + type_atom { + astbuf1->is_reg = false; + }; param_real: TOK_REAL { - if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Parameter already declared as integer, cannot set to real."); astbuf1->children.push_back(new AstNode(AST_REALVALUE)); - } + }; param_range: range { if ($1 != NULL) { - if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("integer/real parameters should not have a range."); astbuf1->children.push_back($1); } }; -param_integer_type: param_integer param_signed -param_range_type: type_vec param_signed param_range -param_implicit_type: param_signed param_range +param_integer_type: param_integer param_signed; +param_range_type: + type_vec param_signed { + addRange(astbuf1, 0, 0); + } | + type_vec param_signed non_opt_range { + astbuf1->children.push_back($3); + }; +param_implicit_type: param_signed param_range; param_type: param_integer_type | param_real | param_range_type | param_implicit_type | @@ -1371,6 +1609,7 @@ param_type: astbuf1->is_custom_type = true; astbuf1->children.push_back(new AstNode(AST_WIRETYPE)); astbuf1->children.back()->str = *$1; + delete $1; }; param_decl: @@ -1395,7 +1634,26 @@ param_decl_list: single_param_decl | param_decl_list ',' single_param_decl; single_param_decl: - TOK_ID '=' expr { + single_param_decl_ident '=' expr { + AstNode *decl = ast_stack.back()->children.back(); + log_assert(decl->type == AST_PARAMETER || decl->type == AST_LOCALPARAM); + delete decl->children[0]; + decl->children[0] = $3; + } | + single_param_decl_ident { + AstNode *decl = ast_stack.back()->children.back(); + if (decl->type != AST_PARAMETER) { + log_assert(decl->type == AST_LOCALPARAM); + frontend_verilog_yyerror("localparam initialization is missing!"); + } + if (!sv_mode) + frontend_verilog_yyerror("Parameter defaults can only be omitted in SystemVerilog mode!"); + delete decl->children[0]; + decl->children.erase(decl->children.begin()); + }; + +single_param_decl_ident: + TOK_ID { AstNode *node; if (astbuf1 == nullptr) { if (!sv_mode) @@ -1406,10 +1664,9 @@ single_param_decl: node = astbuf1->clone(); } node->str = *$1; - delete node->children[0]; - node->children[0] = $3; ast_stack.back()->children.push_back(node); delete $1; + SET_AST_NODE_LOC(node, @1, @1); }; defparam_decl: @@ -1442,28 +1699,30 @@ enum_type: TOK_ENUM { // create the template for the names astbuf1 = new AstNode(AST_ENUM_ITEM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } enum_base_type '{' enum_name_list '}' { // create template for the enum vars - auto tnode = astbuf1->clone(); - delete astbuf1; - astbuf1 = tnode; - tnode->type = AST_WIRE; - tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); - // drop constant but keep any range - delete tnode->children[0]; - tnode->children.erase(tnode->children.begin()); - $$ = astbuf1; } - ; + } enum_base_type '{' enum_name_list optional_comma '}' { + // create template for the enum vars + auto tnode = astbuf1->clone(); + delete astbuf1; + astbuf1 = tnode; + tnode->type = AST_WIRE; + tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); + // drop constant but keep any range + delete tnode->children[0]; + tnode->children.erase(tnode->children.begin()); + $$ = astbuf1; + }; enum_base_type: type_atom type_signing | type_vec type_signing range { if ($3) astbuf1->children.push_back($3); } - | /* nothing */ { astbuf1->is_reg = true; addRange(astbuf1); } + | %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: + integer_atom_type { + astbuf1->is_reg = true; + astbuf1->is_signed = true; + addRange(astbuf1, $1 - 1, 0); + }; type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned @@ -1472,7 +1731,7 @@ type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned type_signing: TOK_SIGNED { astbuf1->is_signed = true; } | TOK_UNSIGNED { astbuf1->is_signed = false; } - | // optional + | %empty ; enum_name_list: enum_name_decl @@ -1496,7 +1755,7 @@ enum_name_decl: opt_enum_init: '=' basic_expr { $$ = $2; } // TODO: restrict this - | /* optional */ { $$ = NULL; } + | %empty { $$ = NULL; } ; enum_var_list: @@ -1537,14 +1796,14 @@ struct_union: struct_body: opt_packed '{' struct_member_list '}' ; -opt_packed: TOK_PACKED opt_signed_struct - | { frontend_verilog_yyerror("Only PACKED supported at this time"); } - ; +opt_packed: + TOK_PACKED opt_signed_struct | + %empty { frontend_verilog_yyerror("Only PACKED supported at this time"); }; opt_signed_struct: TOK_SIGNED { astbuf2->is_signed = true; } | TOK_UNSIGNED { astbuf2->is_signed = false; } - | // default is unsigned + | %empty // default is unsigned ; struct_member_list: struct_member @@ -1590,10 +1849,12 @@ member_type_token: delete astbuf1; astbuf1 = template_node; } - | struct_union { + | { + delete astbuf1; + } struct_union { // stash state on ast_stack ast_stack.push_back(astbuf2); - astbuf2 = $1; + astbuf2 = $2; } struct_body { astbuf1 = astbuf2; // recover state @@ -1651,7 +1912,7 @@ wire_decl: } opt_supply_wires ';'; opt_supply_wires: - /* empty */ | + %empty | opt_supply_wires ',' TOK_ID { AstNode *wire_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-2)->clone(); AstNode *assign_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-1)->clone(); @@ -1752,7 +2013,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; } @@ -1767,9 +2034,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); @@ -1796,7 +2060,7 @@ type_name: TOK_ID // first time seen ; typedef_decl: - TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { + TOK_TYPEDEF typedef_base_type range type_name range_or_multirange ';' { astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); if (astbuf2) @@ -1809,10 +2073,33 @@ typedef_decl: rewriteAsMemoryNode(astbuf1, $5); } addTypedefNode($4, astbuf1); } - | TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); } + | TOK_TYPEDEF enum_struct_type type_name ';' { addTypedefNode($3, $2); } ; -non_wire_data_type: +typedef_base_type: + hierarchical_type_id { + $$ = new AstNode(AST_WIRE); + $$->is_logic = true; + addWiretypeNode($1, $$); + } | + integer_vector_type opt_signedness_default_unsigned { + $$ = new AstNode(AST_WIRE); + if ($1 == TOK_REG) { + $$->is_reg = true; + } else { + $$->is_logic = true; + } + $$->is_signed = $2; + } | + integer_atom_type opt_signedness_default_signed { + $$ = new AstNode(AST_WIRE); + $$->is_logic = true; + $$->is_signed = $2; + $$->range_left = $1 - 1; + $$->range_right = 0; + }; + +enum_struct_type: enum_type | struct_type ; @@ -1849,6 +2136,9 @@ cell_list: cell_list ',' single_cell; single_cell: + single_cell_no_array | single_cell_arraylist; + +single_cell_no_array: TOK_ID { astbuf2 = astbuf1->clone(); if (astbuf2->type != AST_PRIMITIVE) @@ -1857,7 +2147,9 @@ single_cell: ast_stack.back()->children.push_back(astbuf2); } '(' cell_port_list ')' { SET_AST_NODE_LOC(astbuf2, @1, @$); - } | + } + +single_cell_arraylist: TOK_ID non_opt_range { astbuf2 = astbuf1->clone(); if (astbuf2->type != AST_PRIMITIVE) @@ -1868,6 +2160,10 @@ single_cell: SET_AST_NODE_LOC(astbuf2, @1, @$); }; +cell_list_no_array: + single_cell_no_array | + cell_list_no_array ',' single_cell_no_array; + prim_list: single_prim | prim_list ',' single_prim; @@ -1882,18 +2178,21 @@ single_prim: } cell_parameter_list_opt: - '#' '(' cell_parameter_list ')' | /* empty */; + '#' '(' cell_parameter_list ')' | %empty; cell_parameter_list: cell_parameter | cell_parameter_list ',' cell_parameter; cell_parameter: - /* empty */ | + %empty | expr { AstNode *node = new AstNode(AST_PARASET); astbuf1->children.push_back(node); node->children.push_back($1); } | + '.' TOK_ID '(' ')' { + // just ignore empty parameters + } | '.' TOK_ID '(' expr ')' { AstNode *node = new AstNode(AST_PARASET); node->str = *$2; @@ -1972,6 +2271,7 @@ cell_port: if (!sv_mode) frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode."); astbuf2->attributes[ID::wildcard_port_conns] = AstNode::mkconst_int(1, false); + free_attr($1); }; always_comb_or_latch: @@ -2046,7 +2346,7 @@ always_cond: '@' ATTR_BEGIN ')' | '@' '(' ATTR_END | '@' '*' | - /* empty */; + %empty; always_events: always_event | @@ -2076,7 +2376,7 @@ opt_label: ':' TOK_ID { $$ = $2; } | - /* empty */ { + %empty { $$ = NULL; }; @@ -2084,7 +2384,7 @@ opt_sva_label: TOK_SVA_LABEL ':' { $$ = $1; } | - /* empty */ { + %empty { $$ = NULL; }; @@ -2095,7 +2395,7 @@ opt_property: TOK_FINAL { $$ = false; } | - /* empty */ { + %empty { $$ = false; }; @@ -2343,45 +2643,81 @@ simple_behavioral_stmt: SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | - attr lvalue TOK_XOR_ASSIGN delay expr { - AstNode *xor_node = new AstNode(AST_BIT_XOR, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, xor_node); - SET_AST_NODE_LOC(xor_node, @2, @5); + attr lvalue asgn_binop delay expr { + AstNode *expr_node = $5; + if ($3 == AST_SHIFT_LEFT || $3 == AST_SHIFT_RIGHT || + $3 == AST_SHIFT_SLEFT || $3 == AST_SHIFT_SRIGHT) { + expr_node = new AstNode(AST_TO_UNSIGNED, expr_node); + SET_AST_NODE_LOC(expr_node, @5, @5); + } + AstNode *op_node = new AstNode($3, $2->clone(), expr_node); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, op_node); + SET_AST_NODE_LOC(op_node, @2, @5); SET_AST_NODE_LOC(node, @2, @5); ast_stack.back()->children.push_back(node); append_attr(node, $1); - } | - attr lvalue TOK_OR_ASSIGN delay expr { - AstNode *or_node = new AstNode(AST_BIT_OR, $2->clone(), $5); - SET_AST_NODE_LOC(or_node, @2, @5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, or_node); - SET_AST_NODE_LOC(node, @2, @5); + }; + +asgn_binop: + TOK_BIT_OR_ASSIGN { $$ = AST_BIT_OR; } | + TOK_BIT_AND_ASSIGN { $$ = AST_BIT_AND; } | + TOK_BIT_XOR_ASSIGN { $$ = AST_BIT_XOR; } | + TOK_ADD_ASSIGN { $$ = AST_ADD; } | + TOK_SUB_ASSIGN { $$ = AST_SUB; } | + TOK_DIV_ASSIGN { $$ = AST_DIV; } | + TOK_MOD_ASSIGN { $$ = AST_MOD; } | + TOK_MUL_ASSIGN { $$ = AST_MUL; } | + TOK_SHL_ASSIGN { $$ = AST_SHIFT_LEFT; } | + TOK_SHR_ASSIGN { $$ = AST_SHIFT_RIGHT; } | + TOK_SSHL_ASSIGN { $$ = AST_SHIFT_SLEFT; } | + TOK_SSHR_ASSIGN { $$ = AST_SHIFT_SRIGHT; } ; + +for_initialization: + TOK_ID '=' expr { + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = *$1; + AstNode *node = new AstNode(AST_ASSIGN_EQ, ident, $3); ast_stack.back()->children.push_back(node); - append_attr(node, $1); + SET_AST_NODE_LOC(node, @1, @3); + delete $1; } | - attr lvalue TOK_PLUS_ASSIGN delay expr { - AstNode *add_node = new AstNode(AST_ADD, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, add_node); - SET_AST_NODE_LOC(node, @2, @5); - SET_AST_NODE_LOC(add_node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); + non_io_wire_type range TOK_ID { + frontend_verilog_yyerror("For loop variable declaration is missing initialization!"); } | - attr lvalue TOK_SUB_ASSIGN delay expr { - AstNode *sub_node = new AstNode(AST_SUB, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, sub_node); - SET_AST_NODE_LOC(node, @2, @5); - SET_AST_NODE_LOC(sub_node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); - } | - attr lvalue TOK_AND_ASSIGN delay expr { - AstNode *and_node = new AstNode(AST_BIT_AND, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, and_node); - SET_AST_NODE_LOC(node, @2, @5); - SET_AST_NODE_LOC(and_node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); + non_io_wire_type range TOK_ID '=' expr { + if (!sv_mode) + frontend_verilog_yyerror("For loop inline variable declaration is only supported in SystemVerilog mode!"); + + // loop variable declaration + AstNode *wire = $1; + AstNode *range = checkRange(wire, $2); + if (range != nullptr) + wire->children.push_back(range); + SET_AST_NODE_LOC(wire, @1, @3); + SET_AST_NODE_LOC(range, @2, @2); + + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = *$3; + wire->str = *$3; + delete $3; + + AstNode *loop = ast_stack.back(); + AstNode *parent = ast_stack.at(ast_stack.size() - 2); + log_assert(parent->children.back() == loop); + + // loop variable initialization + AstNode *asgn = new AstNode(AST_ASSIGN_EQ, ident, $5); + loop->children.push_back(asgn); + SET_AST_NODE_LOC(asgn, @3, @5); + SET_AST_NODE_LOC(ident, @3, @3); + + // inject a wrapping block to declare the loop variable and + // contain the current loop + AstNode *wrapper = new AstNode(AST_BLOCK); + wrapper->str = "$fordecl_block$" + std::to_string(autoidx++); + wrapper->children.push_back(wire); + wrapper->children.push_back(loop); + parent->children.back() = wrapper; // replaces `loop` }; // this production creates the obligatory if-else shift/reduce conflict @@ -2423,8 +2759,17 @@ behavioral_stmt: node->str = *$4; } behavioral_stmt_list TOK_END opt_label { 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); + checkLabelsMatch("Begin label", $4, $8); + 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; @@ -2435,10 +2780,11 @@ behavioral_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); - } simple_behavioral_stmt ';' expr { + } for_initialization ';' expr { 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 { @@ -2505,20 +2851,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; }; @@ -2542,11 +2889,11 @@ opt_synopsys_attr: if (ast_stack.back()->attributes.count(ID::parallel_case) == 0) ast_stack.back()->attributes[ID::parallel_case] = AstNode::mkconst_int(1, false); } | - /* empty */; + %empty; behavioral_stmt_list: behavioral_stmt_list behavioral_stmt | - /* empty */; + %empty; optional_else: TOK_ELSE { @@ -2560,11 +2907,11 @@ optional_else: } behavioral_stmt { SET_AST_NODE_LOC(ast_stack.back(), @3, @3); } | - /* empty */ %prec FAKE_THEN; + %empty %prec FAKE_THEN; case_body: case_body case_item | - /* empty */; + %empty; case_item: { @@ -2587,7 +2934,7 @@ case_item: gen_case_body: gen_case_body gen_case_item | - /* empty */; + %empty; gen_case_item: { @@ -2632,6 +2979,7 @@ rvalue: hierarchical_id '[' expr ']' '.' rvalue { $$ = new AstNode(AST_PREFIX, $3, $6); $$->str = *$1; + SET_AST_NODE_LOC($$, @1, @6); delete $1; } | hierarchical_id range { @@ -2671,11 +3019,11 @@ lvalue_concat_list: opt_arg_list: '(' arg_list optional_comma ')' | - /* empty */; + %empty; arg_list: arg_list2 | - /* empty */; + %empty; arg_list2: single_arg | @@ -2688,7 +3036,8 @@ single_arg: module_gen_body: module_gen_body gen_stmt_or_module_body_stmt | - /* empty */; + module_gen_body gen_block | + %empty; gen_stmt_or_module_body_stmt: gen_stmt | module_body_stmt | @@ -2696,16 +3045,50 @@ gen_stmt_or_module_body_stmt: free_attr($1); }; +genvar_identifier: + TOK_ID { + $$ = new AstNode(AST_IDENTIFIER); + $$->str = *$1; + delete $1; + }; + +genvar_initialization: + TOK_GENVAR genvar_identifier { + frontend_verilog_yyerror("Generate for loop variable declaration is missing initialization!"); + } | + TOK_GENVAR genvar_identifier '=' expr { + if (!sv_mode) + frontend_verilog_yyerror("Generate for loop inline variable declaration is only supported in SystemVerilog mode!"); + AstNode *node = new AstNode(AST_GENVAR); + node->is_reg = true; + node->is_signed = true; + node->range_left = 31; + node->range_right = 0; + node->str = $2->str; + node->children.push_back(checkRange(node, nullptr)); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @1, @4); + node = new AstNode(AST_ASSIGN_EQ, $2, $4); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @1, @4); + } | + genvar_identifier '=' expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @1, @3); + }; + // this production creates the obligatory if-else shift/reduce conflict gen_stmt: TOK_FOR '(' { AstNode *node = new AstNode(AST_GENFOR); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - } simple_behavioral_stmt ';' expr { + } genvar_initialization ';' expr { ast_stack.back()->children.push_back($6); } ';' simple_behavioral_stmt ')' gen_stmt_block { SET_AST_NODE_LOC(ast_stack.back(), @1, @11); + rewriteGenForDeclInit(ast_stack.back()); ast_stack.pop_back(); } | TOK_IF '(' expr ')' { @@ -2713,12 +3096,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(); } | @@ -2731,6 +3109,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 { @@ -2740,22 +3130,14 @@ gen_stmt: ast_stack.push_back(node); } module_gen_body TOK_END opt_label { exitTypeScope(); + checkLabelsMatch("Begin label", $3, $7); 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); @@ -2764,10 +3146,10 @@ 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; + TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; expr: basic_expr { |