diff options
52 files changed, 1445 insertions, 441 deletions
@@ -716,6 +716,7 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT) +cd tests/bram && bash run-test.sh $(SEEDOPT) +cd tests/various && bash run-test.sh + +cd tests/select && bash run-test.sh +cd tests/sat && bash run-test.sh +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) +cd tests/svtypes && bash run-test.sh $(SEEDOPT) @@ -541,8 +541,6 @@ from SystemVerilog: SystemVerilog files being read into the same design afterwards. - typedefs are supported (including inside packages) - - type identifiers must currently be enclosed in (parentheses) when declaring - signals of that type (this is syntactically incorrect SystemVerilog) - type casts are currently not supported - enums are supported (including inside packages) diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index ea252b6b9..628765831 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -1394,7 +1394,7 @@ struct Smt2Backend : public Backend { log("\n"); log("For this proof we create the following template (test.tpl).\n"); log("\n"); - log(" ; we need QF_UFBV for this poof\n"); + log(" ; we need QF_UFBV for this proof\n"); log(" (set-logic QF_UFBV)\n"); log("\n"); log(" ; insert the auto-generated code here\n"); diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 4c691716e..69f59df79 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -704,8 +704,12 @@ class SmtIo: if msg is not None: print("%s waiting for solver (%s)" % (self.timestamp(), msg), flush=True) - result = "" - while result not in ["sat", "unsat"]: + if self.forall: + result = self.read() + while result not in ["sat", "unsat", "unknown"]: + print("%s %s: %s" % (self.timestamp(), self.solver, result)) + result = self.read() + else: result = self.read() if self.debug_file: diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index a42569301..50c2a3ce6 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -444,7 +444,7 @@ void AigerReader::parse_xaiger() } } else if (c == 'r') { - uint32_t dataSize = parse_xaiger_literal(f); + uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); flopNum = parse_xaiger_literal(f); log_debug("flopNum = %u\n", flopNum); log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 650c7a937..632a4d4f9 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1179,12 +1179,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump for (auto n : design->verilog_globals) (*it)->children.push_back(n->clone()); - for (auto n : design->verilog_packages){ - for (auto o : n->children) { + // append nodes from previous packages using package-qualified names + for (auto &n : design->verilog_packages) { + for (auto &o : n->children) { AstNode *cloned_node = o->clone(); - log("cloned node %s\n", type2str(cloned_node->type).c_str()); - if (cloned_node->type == AST_ENUM){ - for (auto e : cloned_node->children){ + // log("cloned node %s\n", type2str(cloned_node->type).c_str()); + if (cloned_node->type == AST_ENUM) { + for (auto &e : cloned_node->children) { log_assert(e->type == AST_ENUM_ITEM); e->str = n->str + std::string("::") + e->str.substr(1); } @@ -1220,6 +1221,8 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump design->add(process_module(*it, defer)); } else if ((*it)->type == AST_PACKAGE) { + // process enum/other declarations + (*it)->simplify(true, false, false, 1, -1, false, false); design->verilog_packages.push_back((*it)->clone()); } else { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 04c02d893..10e9f2683 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -432,7 +432,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) did_something = true; if (node->type == AST_ENUM) { - for (auto enode : node->children){ + for (auto enode YS_ATTRIBUTE(unused) : node->children){ log_assert(enode->type==AST_ENUM_ITEM); while (node->simplify(true, false, false, 1, -1, false, in_param)) did_something = true; @@ -1811,6 +1811,7 @@ skip_dynamic_range_lvalue_expansion:; newNode->children.push_back(assign_en); AstNode *assertnode = new AstNode(type); + assertnode->location = location; assertnode->str = str; assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); @@ -1897,6 +1898,9 @@ skip_dynamic_range_lvalue_expansion:; bool mem_signed = children[0]->id2ast->is_signed; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); + newNode = new AstNode(AST_BLOCK); + AstNode *defNode = new AstNode(AST_BLOCK); + int data_range_left = children[0]->id2ast->children[0]->range_left; int data_range_right = children[0]->id2ast->children[0]->range_right; int mem_data_range_offset = std::min(data_range_left, data_range_right); @@ -1906,31 +1910,6 @@ skip_dynamic_range_lvalue_expansion:; children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint, addr_sign_hint); addr_bits = std::max(addr_bits, addr_width_hint); - AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); - wire_addr->str = id_addr; - wire_addr->was_checked = true; - current_ast_mod->children.push_back(wire_addr); - current_scope[wire_addr->str] = wire_addr; - while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } - - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_data->str = id_data; - wire_data->was_checked = true; - wire_data->is_signed = mem_signed; - current_ast_mod->children.push_back(wire_data); - current_scope[wire_data->str] = wire_data; - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - - AstNode *wire_en = nullptr; - if (current_always->type != AST_INITIAL) { - wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_en->str = id_en; - 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)) { } - } - std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en; for (int i = 0; i < addr_bits; i++) x_bits_addr.push_back(RTLIL::State::Sx); @@ -1939,32 +1918,79 @@ skip_dynamic_range_lvalue_expansion:; for (int i = 0; i < mem_width; i++) set_bits_en.push_back(RTLIL::State::S1); - AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false)); - assign_addr->children[0]->str = id_addr; - assign_addr->children[0]->was_checked = true; + AstNode *node_addr = nullptr; + if (children[0]->children[0]->children[0]->isConst()) { + node_addr = children[0]->children[0]->children[0]->clone(); + } else { + AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + wire_addr->str = id_addr; + wire_addr->was_checked = true; + current_ast_mod->children.push_back(wire_addr); + current_scope[wire_addr->str] = wire_addr; + while (wire_addr->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)); - assign_data->children[0]->str = id_data; - assign_data->children[0]->was_checked = true; + AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, 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); - AstNode *assign_en = nullptr; - if (current_always->type != AST_INITIAL) { - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr->children[0]->str = id_addr; + assign_addr->children[0]->was_checked = true; + newNode->children.push_back(assign_addr); + + node_addr = new AstNode(AST_IDENTIFIER); + node_addr->str = id_addr; + } + + AstNode *node_data = nullptr; + if (children[0]->children.size() == 1 && children[1]->isConst()) { + node_data = children[1]->clone(); + } else { + AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_data->str = id_data; + wire_data->was_checked = true; + wire_data->is_signed = mem_signed; + current_ast_mod->children.push_back(wire_data); + 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)); + assign_data->children[0]->str = id_data; + assign_data->children[0]->was_checked = true; + defNode->children.push_back(assign_data); + + node_data = new AstNode(AST_IDENTIFIER); + 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 *default_signals = new AstNode(AST_BLOCK); - default_signals->children.push_back(assign_addr); - default_signals->children.push_back(assign_data); - if (current_always->type != AST_INITIAL) - default_signals->children.push_back(assign_en); - current_top_block->children.insert(current_top_block->children.begin(), default_signals); + node_en = new AstNode(AST_IDENTIFIER); + node_en->str = id_en; + } - assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); - assign_addr->children[0]->str = id_addr; - assign_addr->children[0]->was_checked = true; + if (!defNode->children.empty()) + current_top_block->children.insert(current_top_block->children.begin(), defNode); + else + delete defNode; + AstNode *assign_data = nullptr; + AstNode *assign_en = nullptr; if (children[0]->children.size() == 2) { if (children[0]->children[1]->range_valid) @@ -2025,9 +2051,11 @@ skip_dynamic_range_lvalue_expansion:; } else { - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); - assign_data->children[0]->str = id_data; - assign_data->children[0]->was_checked = true; + 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->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)); @@ -2035,28 +2063,20 @@ skip_dynamic_range_lvalue_expansion:; assign_en->children[0]->was_checked = true; } } - - newNode = new AstNode(AST_BLOCK); - newNode->children.push_back(assign_addr); - newNode->children.push_back(assign_data); - if (current_always->type != AST_INITIAL) + 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); - wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); - wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); - if (current_always->type != AST_INITIAL) - wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); - else - wrnode->children.push_back(AstNode::mkconst_int(1, false)); + AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en); wrnode->str = children[0]->str; wrnode->id2ast = children[0]->id2ast; - wrnode->children[0]->str = id_addr; - wrnode->children[1]->str = id_data; - if (current_always->type != AST_INITIAL) - wrnode->children[2]->str = id_en; current_ast_mod->children.push_back(wrnode); + if (newNode->children.empty()) { + delete newNode; + newNode = new AstNode(); + } goto apply_newNode; } diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 6a8462b41..cf9b9531e 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -10,7 +10,7 @@ frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc -frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l +frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l frontends/verilog/verilog_parser.tab.cc $(Q) mkdir -p $(dir $@) $(P) flex -o frontends/verilog/verilog_lexer.cc $< diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 161253a99..7905ea598 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -32,8 +32,10 @@ * */ +#include "preproc.h" #include "verilog_frontend.h" #include "kernel/log.h" +#include <assert.h> #include <stdarg.h> #include <stdio.h> #include <string.h> @@ -199,6 +201,175 @@ static std::string next_token(bool pass_newline = false) return token; } +struct macro_arg_t +{ + macro_arg_t(const std::string &name_, const char *default_value_) + : name(name_), + has_default(default_value_ != nullptr), + default_value(default_value_ ? default_value_ : "") + {} + + std::string name; + bool has_default; + std::string default_value; +}; + +static bool all_white(const std::string &str) +{ + for (char c : str) + if (!isspace(c)) + return false; + return true; +} + +struct arg_map_t +{ + arg_map_t() + {} + + void add_arg(const std::string &name, const char *default_value) + { + if (find(name)) { + log_error("Duplicate macro arguments with name `%s'.\n", name.c_str()); + } + + name_to_pos[name] = args.size(); + args.push_back(macro_arg_t(name, default_value)); + } + + // Find an argument by name; return nullptr if it doesn't exist. If pos is not null, write + // the argument's position to it on success. + const macro_arg_t *find(const std::string &name, int *pos = nullptr) const + { + auto it = name_to_pos.find(name); + if (it == name_to_pos.end()) + return nullptr; + + if (pos) *pos = it->second; + return &args[it->second]; + } + + // Construct the name for the local macro definition we use for the given argument + // (something like macro_foobar_arg2). This doesn't include the leading backtick. + static std::string str_token(const std::string ¯o_name, int pos) + { + return stringf("macro_%s_arg%d", macro_name.c_str(), pos); + } + + // Return definitions for the macro arguments (so that substituting in the macro body and + // then performing macro expansion will do argument substitution properly). + std::vector<std::pair<std::string, std::string>> + get_vals(const std::string ¯o_name, const std::vector<std::string> &arg_vals) const + { + std::vector<std::pair<std::string, std::string>> ret; + for (int i = 0; i < GetSize(args); ++ i) { + // The SystemVerilog rules are: + // + // - If the call site specifies an argument and it's not whitespace, use + // it. + // + // - Otherwise, if the argument has a default value, use it. + // + // - Otherwise, if the call site specified whitespace, use that. + // + // - Otherwise, error. + const std::string *dflt = nullptr; + if (args[i].has_default) + dflt = &args[i].default_value; + + const std::string *given = nullptr; + if (i < GetSize(arg_vals)) + given = &arg_vals[i]; + + const std::string *val = nullptr; + if (given && (! (dflt && all_white(*given)))) + val = given; + else if (dflt) + val = dflt; + else if (given) + val = given; + else + log_error("Cannot expand macro `%s by giving only %d argument%s " + "(argument %d has no default).\n", + macro_name.c_str(), GetSize(arg_vals), + (GetSize(arg_vals) == 1 ? "" : "s"), i + 1); + + assert(val); + ret.push_back(std::make_pair(str_token(macro_name, i), * val)); + } + return ret; + } + + + std::vector<macro_arg_t> args; + std::map<std::string, int> name_to_pos; +}; + +struct define_body_t +{ + define_body_t(const std::string &body, const arg_map_t *args = nullptr) + : body(body), + has_args(args != nullptr), + args(args ? *args : arg_map_t()) + {} + + std::string body; + bool has_args; + arg_map_t args; +}; + +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 +// define it somewhere we've got a complete definition of define_body_t. +define_map_t::~define_map_t() +{} + +void +define_map_t::add(const std::string &name, const std::string &txt, const arg_map_t *args) +{ + defines[name] = std::unique_ptr<define_body_t>(new define_body_t(txt, args)); +} + +void define_map_t::merge(const define_map_t &map) +{ + for (const auto &pr : map.defines) { + // These contortions are so that we take a copy of each definition body in + // map.defines. + defines[pr.first] = std::unique_ptr<define_body_t>(new define_body_t(*pr.second)); + } +} + +const define_body_t *define_map_t::find(const std::string &name) const +{ + auto it = defines.find(name); + return (it == defines.end()) ? nullptr : it->second.get(); +} + +void define_map_t::erase(const std::string &name) +{ + defines.erase(name); +} + +void define_map_t::clear() +{ + defines.clear(); +} + +void define_map_t::log() const +{ + for (auto &it : defines) { + const std::string &name = it.first; + const define_body_t &body = *it.second; + Yosys::log("`define %s%s %s\n", + name.c_str(), body.has_args ? "()" : "", body.body.c_str()); + } +} + static void input_file(std::istream &f, std::string filename) { char buffer[513]; @@ -215,11 +386,59 @@ static void input_file(std::istream &f, std::string filename) input_buffer.insert(it, "\n`file_pop\n"); } +// Read tokens to get one argument (either a macro argument at a callsite or a default argument in a +// macro definition). Writes the argument to dest. Returns true if we finished with ')' (the end of +// the argument list); false if we finished with ','. +static bool read_argument(std::string &dest) +{ + std::vector<char> openers; + for (;;) { + skip_spaces(); + std::string tok = next_token(true); + if (tok == ")") { + if (openers.empty()) + return true; + if (openers.back() != '(') + log_error("Mismatched brackets in macro argument: %c and %c.\n", + openers.back(), tok[0]); + + openers.pop_back(); + dest += tok; + continue; + } + if (tok == "]") { + char opener = openers.empty() ? '(' : openers.back(); + if (opener != '[') + log_error("Mismatched brackets in macro argument: %c and %c.\n", + opener, tok[0]); + + openers.pop_back(); + dest += tok; + continue; + } + if (tok == "}") { + char opener = openers.empty() ? '(' : openers.back(); + if (opener != '{') + log_error("Mismatched brackets in macro argument: %c and %c.\n", + opener, tok[0]); + + openers.pop_back(); + dest += tok; + continue; + } + + if (tok == "," && openers.empty()) { + return false; + } + + if (tok == "(" || tok == "[" || tok == "{") + openers.push_back(tok[0]); -static bool try_expand_macro(std::set<std::string> &defines_with_args, - std::map<std::string, std::string> &defines_map, - std::string &tok - ) + dest += tok; + } +} + +static bool try_expand_macro(define_map_t &defines, std::string &tok) { if (tok == "`\"") { std::string literal("\""); @@ -229,54 +448,272 @@ static bool try_expand_macro(std::set<std::string> &defines_with_args, if (ntok == "`\"") { insert_input(literal+"\""); return true; - } else if (!try_expand_macro(defines_with_args, defines_map, ntok)) { + } else if (!try_expand_macro(defines, ntok)) { literal += ntok; } } return false; // error - unmatched `" - } else if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) { - std::string name = tok.substr(1); - // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str()); - std::string skipped_spaces = skip_spaces(); - tok = next_token(false); - if (tok == "(" && defines_with_args.count(name) > 0) { - int level = 1; - std::vector<std::string> args; - args.push_back(std::string()); - while (1) - { - skip_spaces(); - tok = next_token(true); - if (tok == ")" || tok == "}" || tok == "]") - level--; - if (level == 0) - break; - if (level == 1 && tok == ",") - args.push_back(std::string()); - else - args.back() += tok; - if (tok == "(" || tok == "{" || tok == "[") - level++; - } - for (int i = 0; i < GetSize(args); i++) - defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i]; - } else { - insert_input(tok); - insert_input(skipped_spaces); - } - insert_input(defines_map[name]); - return true; - } else if (tok == "``") { + } + + if (tok == "``") { // Swallow `` in macro expansion return true; - } else return false; + } + + if (tok.size() <= 1 || tok[0] != '`') + return false; + + // This token looks like a macro name (`foo). + std::string macro_name = tok.substr(1); + const define_body_t *body = defines.find(tok.substr(1)); + + if (! body) { + // Apparently not a name we know. + return false; + } + + std::string name = tok.substr(1); + std::string skipped_spaces = skip_spaces(); + tok = next_token(false); + if (tok == "(" && body->has_args) { + std::vector<std::string> args; + bool done = false; + while (!done) { + std::string arg; + done = read_argument(arg); + args.push_back(arg); + } + for (const auto &pr : body->args.get_vals(name, args)) { + defines.add(pr.first, pr.second); + } + } else { + insert_input(tok); + insert_input(skipped_spaces); + } + insert_input(body->body); + return true; } -std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> &pre_defines_map, - dict<std::string, std::pair<std::string, bool>> &global_defines_cache, const std::list<std::string> &include_dirs) +// Read the arguments for a `define preprocessor directive with formal arguments. This is called +// just after reading the token containing "(". Returns the number of newlines to emit afterwards to +// keep line numbers in sync, together with the map from argument name to data (pos and default +// value). +static std::pair<int, arg_map_t> +read_define_args() { - std::set<std::string> defines_with_args; - std::map<std::string, std::string> defines_map(pre_defines_map); + // Each argument looks like one of the following: + // + // identifier + // identifier = default_text + // identifier = + // + // The first example is an argument with no default value. The second is an argument whose + // default value is default_text. The third is an argument with default value the empty + // string. + + int newline_count = 0; + arg_map_t args; + + // FSM state. + // + // 0: At start of identifier + // 1: After identifier (stored in arg_name) + // 2: After closing paren + int state = 0; + + std::string arg_name, default_val; + + skip_spaces(); + for (;;) { + if (state == 2) + // We've read the closing paren. + break; + + std::string tok = next_token(); + + // Cope with escaped EOLs + if (tok == "\\") { + char ch = next_char(); + if (ch == '\n') { + // Eat the \, the \n and any trailing space and keep going. + skip_spaces(); + continue; + } else { + // There aren't any other situations where a backslash makes sense. + log_error("Backslash in macro arguments (not at end of line).\n"); + } + } + + switch (state) { + case 0: + // At start of argument. If the token is ')', we've presumably just seen + // something like "`define foo() ...". Set state to 2 to finish. Otherwise, + // the token should be a valid simple identifier, but we'll allow anything + // here. + if (tok == ")") { + state = 2; + } else { + arg_name = tok; + state = 1; + } + skip_spaces(); + break; + + case 1: + // After argument. The token should either be an equals sign or a comma or + // closing paren. + if (tok == "=") { + std::string default_val; + //Read an argument into default_val and set state to 2 if we're at + // the end; 0 if we hit a comma. + state = read_argument(default_val) ? 2 : 0; + args.add_arg(arg_name, default_val.c_str()); + skip_spaces(); + break; + } + if (tok == ",") { + // Take the identifier as an argument with no default value. + args.add_arg(arg_name, nullptr); + state = 0; + skip_spaces(); + break; + } + if (tok == ")") { + // As with comma, but set state to 2 (end of args) + args.add_arg(arg_name, nullptr); + state = 2; + skip_spaces(); + break; + } + log_error("Trailing contents after identifier in macro argument `%s': " + "expected '=', ',' or ')'.\n", + arg_name.c_str()); + + default: + // The only FSM states are 0-2 and we dealt with 2 at the start of the loop. + __builtin_unreachable(); + } + } + + return std::make_pair(newline_count, args); +} + +// Read a `define preprocessor directive. This is called just after reading the token containing +// "`define". +static void +read_define(const std::string &filename, + define_map_t &defines_map, + define_map_t &global_defines_cache) +{ + std::string name, value; + arg_map_t args; + + skip_spaces(); + name = next_token(true); + + bool here_doc_mode = false; + int newline_count = 0; + + // The FSM state starts at 0. If it sees space (or enters here_doc_mode), it assumes this is + // a macro without formal arguments and jumps to state 1. + // + // In state 0, if it sees an opening parenthesis, it assumes this is a macro with formal + // arguments. It reads the arguments with read_define_args() and then jumps to state 2. + // + // In states 1 or 2, the FSM reads tokens to the end of line (or end of here_doc): this is + // the body of the macro definition. + int state = 0; + + if (skip_spaces() != "") + state = 1; + + for (;;) { + std::string tok = next_token(); + if (tok.empty()) + break; + + // printf("define-tok: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); + + if (tok == "\"\"\"") { + here_doc_mode = !here_doc_mode; + continue; + } + + if (state == 0 && tok == "(") { + auto pr = read_define_args(); + newline_count += pr.first; + args = pr.second; + + state = 2; + continue; + } + + // This token isn't an opening parenthesis immediately following the macro name, so + // it's presumably at or after the start of the macro body. If state isn't already 2 + // (which would mean we'd parsed an argument list), set it to 1. + if (state == 0) { + state = 1; + } + + if (tok == "\n") { + if (here_doc_mode) { + value += " "; + newline_count++; + } else { + return_char('\n'); + break; + } + continue; + } + + if (tok == "\\") { + char ch = next_char(); + if (ch == '\n') { + value += " "; + newline_count++; + } else { + value += std::string("\\"); + return_char(ch); + } + continue; + } + + // Is this token the name of a macro argument? If so, replace it with a magic symbol + // that we'll replace with the argument value. + int arg_pos; + if (args.find(tok, &arg_pos)) { + value += '`' + args.str_token(name, arg_pos); + continue; + } + + // This token is nothing special. Insert it verbatim into the macro body. + value += tok; + } + + // Append some newlines so that we don't mess up line counts in error messages. + while (newline_count-- > 0) + return_char('\n'); + + if (strchr("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789", name[0])) { + // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); + defines_map.add(name, value, (state == 2) ? &args : nullptr); + global_defines_cache.add(name, value, (state == 2) ? &args : nullptr); + } else { + log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str()); + } +} + +std::string +frontend_verilog_preproc(std::istream &f, + std::string filename, + const define_map_t &pre_defines, + define_map_t &global_defines_cache, + const std::list<std::string> &include_dirs) +{ + define_map_t defines; + defines.merge(pre_defines); + defines.merge(global_defines_cache); + std::vector<std::string> filename_stack; int ifdef_fail_level = 0; bool in_elseif = false; @@ -287,18 +724,6 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons input_file(f, filename); - defines_map["YOSYS"] = "1"; - defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1"; - - for (auto &it : pre_defines_map) - defines_map[it.first] = it.second; - - for (auto &it : global_defines_cache) { - if (it.second.second) - defines_with_args.insert(it.first); - defines_map[it.first] = it.second.first; - } - while (!input_buffer.empty()) { std::string tok = next_token(); @@ -325,7 +750,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons 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_map.count(name) != 0) + else if (ifdef_fail_level == 1 && defines.find(name)) ifdef_fail_level = 0, in_elseif = true; continue; } @@ -333,7 +758,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons if (tok == "`ifdef") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level > 0 || defines_map.count(name) == 0) + if (ifdef_fail_level > 0 || !defines.find(name)) ifdef_fail_level++; continue; } @@ -341,7 +766,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons if (tok == "`ifndef") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level > 0 || defines_map.count(name) != 0) + if (ifdef_fail_level > 0 || defines.find(name)) ifdef_fail_level++; continue; } @@ -355,7 +780,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons if (tok == "`include") { skip_spaces(); std::string fn = next_token(true); - while (try_expand_macro(defines_with_args, defines_map, fn)) { + while (try_expand_macro(defines, fn)) { fn = next_token(); } while (1) { @@ -433,74 +858,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons } if (tok == "`define") { - std::string name, value; - std::map<std::string, int> args; - skip_spaces(); - name = next_token(true); - bool here_doc_mode = false; - int newline_count = 0; - int state = 0; - if (skip_spaces() != "") - state = 3; - while (!tok.empty()) { - tok = next_token(); - if (tok == "\"\"\"") { - here_doc_mode = !here_doc_mode; - continue; - } - if (state == 0 && tok == "(") { - state = 1; - skip_spaces(); - } else - if (state == 1) { - if (tok == ")") - state = 2; - else if (tok != ",") { - int arg_idx = args.size()+1; - args[tok] = arg_idx; - } - skip_spaces(); - } else { - if (state != 2) - state = 3; - if (tok == "\n") { - if (here_doc_mode) { - value += " "; - newline_count++; - } else { - return_char('\n'); - break; - } - } else - if (tok == "\\") { - char ch = next_char(); - if (ch == '\n') { - value += " "; - newline_count++; - } else { - value += std::string("\\"); - return_char(ch); - } - } else - if (args.count(tok) > 0) - value += stringf("`macro_%s_arg%d", name.c_str(), args.at(tok)); - else - value += tok; - } - } - while (newline_count-- > 0) - return_char('\n'); - if (strchr("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789", name[0])) { - // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); - defines_map[name] = value; - if (state == 2) - defines_with_args.insert(name); - else - defines_with_args.erase(name); - global_defines_cache[name] = std::pair<std::string, bool>(value, state == 2); - } else { - log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str()); - } + read_define(filename, defines, global_defines_cache); continue; } @@ -509,8 +867,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons skip_spaces(); name = next_token(true); // printf("undef: >>%s<<\n", name.c_str()); - defines_map.erase(name); - defines_with_args.erase(name); + defines.erase(name); global_defines_cache.erase(name); continue; } @@ -525,13 +882,12 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons } if (tok == "`resetall") { - defines_map.clear(); - defines_with_args.clear(); + defines.clear(); global_defines_cache.clear(); continue; } - if (try_expand_macro(defines_with_args, defines_map, tok)) + if (try_expand_macro(defines, tok)) continue; output_code.push_back(tok); diff --git a/frontends/verilog/preproc.h b/frontends/verilog/preproc.h new file mode 100644 index 000000000..673d633c0 --- /dev/null +++ b/frontends/verilog/preproc.h @@ -0,0 +1,77 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog preprocessor. + * + */ +#ifndef VERILOG_PREPROC_H +#define VERILOG_PREPROC_H + +#include "kernel/yosys.h" + +#include <iosfwd> +#include <list> +#include <memory> +#include <string> + +YOSYS_NAMESPACE_BEGIN + +struct define_body_t; +struct arg_map_t; + +struct define_map_t +{ + define_map_t(); + ~ 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); + + // Merge in another map of definitions (which take precedence + // over anything currently defined). + void merge(const define_map_t &map); + + // Find a definition by name. If no match, returns null. + const define_body_t *find(const std::string &name) const; + + // Erase a definition by name (no effect if not defined). + void erase(const std::string &name); + + // Clear any existing definitions + void clear(); + + // Print a list of definitions, using the log function + void log() const; + + std::map<std::string, std::unique_ptr<define_body_t>> defines; +}; + + +struct define_map_t; + +std::string +frontend_verilog_preproc(std::istream &f, + std::string filename, + const define_map_t &pre_defines, + define_map_t &global_defines_cache, + const std::list<std::string> &include_dirs); + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 42eabc02d..6879e0943 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -27,6 +27,7 @@ */ #include "verilog_frontend.h" +#include "preproc.h" #include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include <stdarg.h> @@ -47,6 +48,23 @@ static void error_on_dpi_function(AST::AstNode *node) error_on_dpi_function(child); } +static void add_package_types(std::map<std::string, AST::AstNode *> &user_types, std::vector<AST::AstNode *> &package_list) +{ + // prime the parser's user type lookup table with the package qualified names + // of typedefed names in the packages seen so far. + for (const auto &pkg : package_list) { + log_assert(pkg->type==AST::AST_PACKAGE); + for (const auto &node: pkg->children) { + if (node->type == AST::AST_TYPEDEF) { + std::string s = pkg->str + "::" + node->str.substr(1); + user_types[s] = node; + } + } + } + user_type_stack.clear(); + user_type_stack.push_back(new UserTypeMap()); +} + struct VerilogFrontend : public Frontend { VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { } void help() YS_OVERRIDE @@ -237,7 +255,8 @@ struct VerilogFrontend : public Frontend { bool flag_defer = false; bool flag_noblackbox = false; bool flag_nowb = false; - std::map<std::string, std::string> defines_map; + define_map_t defines_map; + std::list<std::string> include_dirs; std::list<std::string> attributes; @@ -353,7 +372,7 @@ struct VerilogFrontend : public Frontend { } if (arg == "-lib") { lib_mode = true; - defines_map["BLACKBOX"] = string(); + defines_map.add("BLACKBOX", ""); continue; } if (arg == "-nowb") { @@ -405,7 +424,7 @@ struct VerilogFrontend : public Frontend { value = name.substr(equal+1); name = name.substr(0, equal); } - defines_map[name] = value; + defines_map.add(name, value); continue; } if (arg.compare(0, 2, "-D") == 0) { @@ -414,7 +433,7 @@ struct VerilogFrontend : public Frontend { std::string value; if (equal != std::string::npos) value = arg.substr(equal+1); - defines_map[name] = value; + defines_map.add(name, value); continue; } if (arg == "-I" && argidx+1 < args.size()) { @@ -444,12 +463,15 @@ struct VerilogFrontend : public Frontend { std::string code_after_preproc; if (!flag_nopp) { - code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, design->verilog_defines, include_dirs); + code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs); if (flag_ppdump) log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str()); lexin = new std::istringstream(code_after_preproc); } + // make package typedefs available to parser + add_package_types(pkg_user_types, design->verilog_packages); + frontend_verilog_yyset_lineno(1); frontend_verilog_yyrestart(NULL); frontend_verilog_yyparse(); @@ -468,6 +490,7 @@ struct VerilogFrontend : public Frontend { AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + if (!flag_nopp) delete lexin; @@ -572,7 +595,7 @@ struct VerilogDefines : public Pass { value = name.substr(equal+1); name = name.substr(0, equal); } - design->verilog_defines[name] = std::pair<std::string, bool>(value, false); + design->verilog_defines->add(name, value); continue; } if (arg.compare(0, 2, "-D") == 0) { @@ -581,27 +604,25 @@ struct VerilogDefines : public Pass { std::string value; if (equal != std::string::npos) value = arg.substr(equal+1); - design->verilog_defines[name] = std::pair<std::string, bool>(value, false); + design->verilog_defines->add(name, value); continue; } if (arg == "-U" && argidx+1 < args.size()) { std::string name = args[++argidx]; - design->verilog_defines.erase(name); + design->verilog_defines->erase(name); continue; } if (arg.compare(0, 2, "-U") == 0) { std::string name = arg.substr(2); - design->verilog_defines.erase(name); + design->verilog_defines->erase(name); continue; } if (arg == "-reset") { - design->verilog_defines.clear(); + design->verilog_defines->clear(); continue; } if (arg == "-list") { - for (auto &it : design->verilog_defines) { - log("`define %s%s %s\n", it.first.c_str(), it.second.second ? "()" : "", it.second.first.c_str()); - } + design->verilog_defines->log(); continue; } break; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index a2e06f0e4..444cc7297 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -45,6 +45,13 @@ namespace VERILOG_FRONTEND // this function converts a Verilog constant to an AST_CONSTANT node AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false); + // names of locally typedef'ed types in a stack + typedef std::map<std::string, AST::AstNode*> UserTypeMap; + extern std::vector<UserTypeMap *> user_type_stack; + + // names of package typedef'ed types + extern std::map<std::string, AST::AstNode*> pkg_user_types; + // state of `default_nettype extern bool default_nettype_wire; @@ -79,10 +86,6 @@ namespace VERILOG_FRONTEND extern std::istream *lexin; } -// the pre-processor -std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> &pre_defines_map, - dict<std::string, std::pair<std::string, bool>> &global_defines_cache, const std::list<std::string> &include_dirs); - YOSYS_NAMESPACE_END // the usual bison/flex stuff diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index d22a18458..f6a3ac4db 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -99,6 +99,18 @@ YYLTYPE old_location; #define YY_BUF_SIZE 65536 extern int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); + +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) { + return true; + } + } + return false; +} + %} %option yylineno @@ -372,9 +384,34 @@ supply1 { return TOK_SUPPLY1; } "$signed" { return TOK_TO_SIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; } +[a-zA-Z_][a-zA-Z0-9_]*::[a-zA-Z_$][a-zA-Z0-9_$]* { + // package qualifier + auto s = std::string("\\") + yytext; + if (pkg_user_types.count(s) > 0) { + // package qualified typedefed name + yylval->string = new std::string(s); + return TOK_PKG_USER_TYPE; + } + else { + // backup before :: just return first part + size_t len = strchr(yytext, ':') - yytext; + yyless(len); + yylval->string = new std::string(std::string("\\") + yytext); + return TOK_ID; + } +} + [a-zA-Z_$][a-zA-Z0-9_$]* { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto s = std::string("\\") + yytext; + if (isUserType(s)) { + // previously typedefed name + yylval->string = new std::string(s); + return TOK_USER_TYPE; + } + else { + yylval->string = new std::string(std::string("\\") + yytext); + return TOK_ID; + } } [a-zA-Z_$][a-zA-Z0-9_$\.]* { diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index d04b32509..be2872e59 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -54,6 +54,8 @@ namespace VERILOG_FRONTEND { std::map<std::string, AstNode*> *attr_list, default_attr_list; std::stack<std::map<std::string, AstNode*> *> attr_list_stack; std::map<std::string, AstNode*> *albuf; + std::vector<UserTypeMap*> user_type_stack; + std::map<std::string, AstNode*> pkg_user_types; std::vector<AstNode*> ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; struct AstNode *current_function_or_task; @@ -125,6 +127,40 @@ struct specify_rise_fall { specify_triple fall; }; +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; + 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); + pkg_user_types[qname] = tnode; + } + delete name; + ast_stack.back()->children.push_back(tnode); +} + +static void enterTypeScope() +{ + auto user_types = new UserTypeMap(); + user_type_stack.push_back(user_types); +} + +static void exitTypeScope() +{ + user_type_stack.pop_back(); +} + +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); +} + static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true) { auto range = new AstNode(AST_RANGE); @@ -167,6 +203,7 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = %token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE %token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS %token <string> TOK_BASE TOK_BASED_CONSTVAL TOK_UNBASED_UNSIZED_CONSTVAL +%token <string> TOK_USER_TYPE TOK_PKG_USER_TYPE %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM @@ -190,6 +227,7 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = %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 %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 %type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff %type <al> attr case_attr @@ -330,10 +368,15 @@ hierarchical_id: }; hierarchical_type_id: - '(' hierarchical_id ')' { $$ = $2; }; + TOK_USER_TYPE + | TOK_PKG_USER_TYPE // package qualified type name + | '(' TOK_USER_TYPE ')' { $$ = $2; } // non-standard grammar + ; module: - attr TOK_MODULE TOK_ID { + attr TOK_MODULE { + enterTypeScope(); + } TOK_ID { do_not_require_port_stubs = false; AstNode *mod = new AstNode(AST_MODULE); ast_stack.back()->children.push_back(mod); @@ -341,9 +384,9 @@ module: current_ast_mod = mod; port_stubs.clear(); port_counter = 0; - mod->str = *$3; + mod->str = *$4; append_attr(mod, $1); - delete $3; + delete $4; } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE { if (port_stubs.size() != 0) frontend_verilog_yyerror("Missing details for module port `%s'.", @@ -352,6 +395,7 @@ module: ast_stack.pop_back(); log_assert(ast_stack.size() == 1); current_ast_mod = NULL; + exitTypeScope(); }; module_para_opt: @@ -455,16 +499,19 @@ module_arg: }; package: - attr TOK_PACKAGE TOK_ID { + attr TOK_PACKAGE { + enterTypeScope(); + } TOK_ID { AstNode *mod = new AstNode(AST_PACKAGE); ast_stack.back()->children.push_back(mod); ast_stack.push_back(mod); current_ast_mod = mod; - mod->str = *$3; + mod->str = *$4; append_attr(mod, $1); } ';' package_body TOK_ENDPACKAGE { ast_stack.pop_back(); current_ast_mod = NULL; + exitTypeScope(); }; package_body: @@ -477,7 +524,9 @@ package_body_stmt: localparam_decl; interface: - TOK_INTERFACE TOK_ID { + TOK_INTERFACE { + enterTypeScope(); + } TOK_ID { do_not_require_port_stubs = false; AstNode *intf = new AstNode(AST_INTERFACE); ast_stack.back()->children.push_back(intf); @@ -485,8 +534,8 @@ interface: current_ast_mod = intf; port_stubs.clear(); port_counter = 0; - intf->str = *$2; - delete $2; + intf->str = *$3; + delete $3; } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE { if (port_stubs.size() != 0) frontend_verilog_yyerror("Missing details for module port `%s'.", @@ -494,6 +543,7 @@ interface: ast_stack.pop_back(); log_assert(ast_stack.size() == 1); current_ast_mod = NULL; + exitTypeScope(); }; interface_body: @@ -1591,8 +1641,12 @@ assign_expr: ast_stack.back()->children.push_back(node); }; +type_name: TOK_ID // first time seen + | TOK_USER_TYPE { if (isInLocalScope($1)) frontend_verilog_yyerror("Duplicate declaration of TYPEDEF '%s'", $1->c_str()+1); } + ; + typedef_decl: - TOK_TYPEDEF wire_type range TOK_ID range_or_multirange ';' { + TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { astbuf1 = $2; astbuf2 = $3; if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { @@ -1625,13 +1679,10 @@ typedef_decl: } astbuf1->children.push_back(rangeNode); } - - ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1)); - ast_stack.back()->children.back()->str = *$4; + addTypedefNode($4, astbuf1); } | - TOK_TYPEDEF enum_type TOK_ID ';' { - ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1)); - ast_stack.back()->children.back()->str = *$3; + TOK_TYPEDEF enum_type type_name ';' { + addTypedefNode($3, astbuf1); } ; @@ -1955,6 +2006,7 @@ assert: delete $5; } else { AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + SET_AST_NODE_LOC(node, @1, @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -1967,6 +2019,7 @@ assert: delete $5; } else { AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); + SET_AST_NODE_LOC(node, @1, @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -1979,6 +2032,7 @@ assert: delete $6; } else { AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + SET_AST_NODE_LOC(node, @1, @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -1991,6 +2045,7 @@ assert: delete $6; } else { AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); + SET_AST_NODE_LOC(node, @1, @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2000,6 +2055,7 @@ assert: } | opt_sva_label TOK_COVER opt_property '(' expr ')' ';' { AstNode *node = new AstNode(AST_COVER, $5); + SET_AST_NODE_LOC(node, @1, @6); if ($1 != nullptr) { node->str = *$1; delete $1; @@ -2008,6 +2064,7 @@ assert: } | opt_sva_label TOK_COVER opt_property '(' ')' ';' { AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); + SET_AST_NODE_LOC(node, @1, @5); if ($1 != nullptr) { node->str = *$1; delete $1; @@ -2016,6 +2073,7 @@ assert: } | opt_sva_label TOK_COVER ';' { AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); + SET_AST_NODE_LOC(node, @1, @2); if ($1 != nullptr) { node->str = *$1; delete $1; @@ -2027,6 +2085,7 @@ assert: delete $5; } else { AstNode *node = new AstNode(AST_ASSUME, $5); + SET_AST_NODE_LOC(node, @1, @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2041,6 +2100,7 @@ assert: delete $6; } else { AstNode *node = new AstNode(AST_FAIR, $6); + SET_AST_NODE_LOC(node, @1, @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2053,35 +2113,45 @@ assert: assert_property: opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5)); + AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + SET_AST_NODE_LOC(node, @1, @6); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + AstNode *node = new AstNode(AST_ASSUME, $5); + SET_AST_NODE_LOC(node, @1, @6); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6)); + AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + SET_AST_NODE_LOC(node, @1, @7); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + AstNode *node = new AstNode(AST_FAIR, $6); + SET_AST_NODE_LOC(node, @1, @7); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5)); + AstNode *node = new AstNode(AST_COVER, $5); + SET_AST_NODE_LOC(node, @1, @6); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; @@ -2091,7 +2161,9 @@ assert_property: if (norestrict_mode) { delete $5; } else { - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + AstNode *node = new AstNode(AST_ASSUME, $5); + SET_AST_NODE_LOC(node, @1, @6); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; @@ -2102,7 +2174,9 @@ assert_property: if (norestrict_mode) { delete $6; } else { - ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + AstNode *node = new AstNode(AST_FAIR, $6); + SET_AST_NODE_LOC(node, @1, @7); + ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; @@ -2157,20 +2231,21 @@ behavioral_stmt: } opt_arg_list ';'{ ast_stack.pop_back(); } | - attr TOK_BEGIN opt_label { + attr TOK_BEGIN { + enterTypeScope(); + } opt_label { AstNode *node = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); - if ($3 != NULL) - node->str = *$3; + if ($4 != NULL) + node->str = *$4; } behavioral_stmt_list TOK_END opt_label { - if ($3 != NULL && $7 != NULL && *$3 != *$7) - frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1); - if ($3 != NULL) - delete $3; - if ($7 != NULL) - delete $7; + 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); + delete $4; + delete $8; ast_stack.pop_back(); } | attr TOK_FOR '(' { @@ -2248,6 +2323,8 @@ behavioral_stmt: ast_stack.pop_back(); }; + ; + unique_case_attr: /* empty */ { $$ = false; @@ -2463,16 +2540,17 @@ gen_stmt: case_type_stack.pop_back(); ast_stack.pop_back(); } | - TOK_BEGIN opt_label { + TOK_BEGIN { + enterTypeScope(); + } opt_label { AstNode *node = new AstNode(AST_GENBLOCK); - node->str = $2 ? *$2 : std::string(); + node->str = $3 ? *$3 : std::string(); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } module_gen_body TOK_END opt_label { - if ($2 != NULL) - delete $2; - if ($6 != NULL) - delete $6; + exitTypeScope(); + delete $3; + delete $7; ast_stack.pop_back(); } | TOK_MSG_TASKS { diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 06181b763..79e50cccd 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -21,6 +21,7 @@ #include "kernel/macc.h" #include "kernel/celltypes.h" #include "frontends/verilog/verilog_frontend.h" +#include "frontends/verilog/preproc.h" #include "backends/ilang/ilang_backend.h" #include <string.h> @@ -379,6 +380,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) } RTLIL::Design::Design() + : verilog_defines (new define_map_t) { static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 58c5d9674..4afe4304f 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -952,6 +952,9 @@ struct RTLIL::Monitor virtual void notify_blackout(RTLIL::Module*) { } }; +// Forward declaration; defined in preproc.h. +struct define_map_t; + struct RTLIL::Design { unsigned int hashidx_; @@ -963,7 +966,7 @@ struct RTLIL::Design int refcount_modules_; dict<RTLIL::IdString, RTLIL::Module*> modules_; std::vector<AST::AstNode*> verilog_packages, verilog_globals; - dict<std::string, std::pair<std::string, bool>> verilog_defines; + std::unique_ptr<define_map_t> verilog_defines; std::vector<RTLIL::Selection> selection_stack; dict<RTLIL::IdString, RTLIL::Selection> selection_vars; diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex index bed6326e2..4925defe3 100644 --- a/manual/command-reference-manual.tex +++ b/manual/command-reference-manual.tex @@ -5350,7 +5350,7 @@ never transition from a non-zero value to a zero value. For this proof we create the following template (test.tpl). - ; we need QF_UFBV for this poof + ; we need QF_UFBV for this proof (set-logic QF_UFBV) ; insert the auto-generated code here diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 20b38bf8e..60f20fa6d 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -1,4 +1,5 @@ +OBJS += passes/cmds/exec.o OBJS += passes/cmds/add.o OBJS += passes/cmds/delete.o OBJS += passes/cmds/design.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index 7b76f3d4a..c49b8bf5d 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -206,6 +206,7 @@ struct AddPass : public Pass { extra_args(args, argidx, design); + bool selected_anything = false; for (auto module : design->modules()) { log_assert(module != nullptr); @@ -214,11 +215,14 @@ struct AddPass : public Pass { if (module->get_bool_attribute("\\blackbox")) continue; + selected_anything = true; if (is_formal_celltype(command)) add_formal(module, command, arg_name, enable_name); else if (command == "wire") add_wire(design, module, arg_name, arg_width, arg_flag_input, arg_flag_output, arg_flag_global); } + if (!selected_anything) + log_warning("No modules selected, or only blackboxes. Nothing was added.\n"); } } AddPass; diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 172addcc1..fab23fc1d 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "frontends/verilog/preproc.h" #include "frontends/ast/ast.h" YOSYS_NAMESPACE_BEGIN @@ -346,7 +347,7 @@ struct DesignPass : public Pass { delete node; design->verilog_globals.clear(); - design->verilog_defines.clear(); + design->verilog_defines->clear(); } if (!load_name.empty() || pop_mode) diff --git a/passes/cmds/exec.cc b/passes/cmds/exec.cc new file mode 100644 index 000000000..7eeefe705 --- /dev/null +++ b/passes/cmds/exec.cc @@ -0,0 +1,205 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 - 2020 Claire Wolf <claire@symbioticeda.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 "kernel/register.h" +#include "kernel/log.h" +#include <cstdio> + +#if defined(_WIN32) +# include <csignal> +# define WIFEXITED(x) 1 +# define WIFSIGNALED(x) 0 +# define WIFSTOPPED(x) 0 +# define WEXITSTATUS(x) ((x) & 0xff) +# define WTERMSIG(x) SIGTERM +# define WSTOPSIG(x) 0 +#else +# include <sys/wait.h> +#endif + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ExecPass : public Pass { + ExecPass() : Pass("exec", "execute commands in the operating system shell") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" exec [options] -- [command]\n"); + log("\n"); + log("Execute a command in the operating system shell. All supplied arguments are\n"); + log("concatenated and passed as a command to popen(3). Whitespace is not guaranteed\n"); + log("to be preserved, even if quoted. stdin and stderr are not connected, while stdout is\n"); + log("logged unless the \"-q\" option is specified.\n"); + log("\n"); + log("\n"); + log(" -q\n"); + log(" Suppress stdout and stderr from subprocess\n"); + log("\n"); + log(" -expect-return <int>\n"); + log(" Generate an error if popen() does not return specified value.\n"); + log(" May only be specified once; the final specified value is controlling\n"); + log(" if specified multiple times.\n"); + log("\n"); + log(" -expect-stdout <regex>\n"); + log(" Generate an error if the specified regex does not match any line\n"); + log(" in subprocess's stdout. May be specified multiple times.\n"); + log("\n"); + log(" -not-expect-stdout <regex>\n"); + log(" Generate an error if the specified regex matches any line\n"); + log(" in subprocess's stdout. May be specified multiple times.\n"); + log("\n"); + log("\n"); + log(" Example: exec -q -expect-return 0 -- echo \"bananapie\" | grep \"nana\"\n"); + log("\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + std::string cmd = ""; + char buf[1024] = {}; + std::string linebuf = ""; + bool flag_cmd = false; + bool flag_quiet = false; + bool flag_expect_return = false; + int expect_return_value = 0; + bool flag_expect_stdout = false; + struct expect_stdout_elem { + bool matched; + bool polarity; //true: this regex must match at least one line + //false: this regex must not match any line + std::string str; + YS_REGEX_TYPE re; + + expect_stdout_elem() : matched(false), polarity(true), str(), re(){}; + }; + std::vector<expect_stdout_elem> expect_stdout; + + if(args.size() == 0) + log_cmd_error("No command provided.\n"); + + for(size_t argidx = 1; argidx < args.size(); ++argidx) { + if (flag_cmd) { + cmd += args[argidx] + (argidx != (args.size() - 1)? " " : ""); + } else { + if (args[argidx] == "--") + flag_cmd = true; + else if (args[argidx] == "-q") + flag_quiet = true; + else if (args[argidx] == "-expect-return") { + flag_expect_return = true; + ++argidx; + if (argidx >= args.size()) + log_cmd_error("No expected return value specified.\n"); + + expect_return_value = atoi(args[argidx].c_str()); + } else if (args[argidx] == "-expect-stdout") { + flag_expect_stdout = true; + ++argidx; + if (argidx >= args.size()) + log_cmd_error("No expected regular expression specified.\n"); + + try{ + expect_stdout_elem x; + x.str = args[argidx]; + x.re = YS_REGEX_COMPILE(args[argidx]); + expect_stdout.push_back(x); + } catch (const YS_REGEX_NS::regex_error& e) { + log_cmd_error("Error in regex expression '%s' !\n", args[argidx].c_str()); + } + } else if (args[argidx] == "-not-expect-stdout") { + flag_expect_stdout = true; + ++argidx; + if (argidx >= args.size()) + log_cmd_error("No expected regular expression specified.\n"); + + try{ + expect_stdout_elem x; + x.str = args[argidx]; + x.re = YS_REGEX_COMPILE(args[argidx]); + x.polarity = false; + expect_stdout.push_back(x); + } catch (const YS_REGEX_NS::regex_error& e) { + log_cmd_error("Error in regex expression '%s' !\n", args[argidx].c_str()); + } + + } else + log_cmd_error("Unknown option \"%s\" or \"--\" doesn\'t precede command.", args[argidx].c_str()); + } + } + + log_header(design, "Executing command \"%s\".\n", cmd.c_str()); + log_push(); + + fflush(stdout); + bool keep_reading = true; + int status = 0; + int retval = 0; + +#ifndef EMSCRIPTEN + FILE *f = popen(cmd.c_str(), "r"); + if (f == nullptr) + log_cmd_error("errno %d after popen() returned NULL.\n", errno); + while (keep_reading) { + keep_reading = (fgets(buf, sizeof(buf), f) != nullptr); + linebuf += buf; + memset(buf, 0, sizeof(buf)); + + auto pos = linebuf.find('\n'); + while (pos != std::string::npos) { + std::string line = linebuf.substr(0, pos); + linebuf.erase(0, pos + 1); + if (!flag_quiet) + log("%s\n", line.c_str()); + + if (flag_expect_stdout) + for(auto &x : expect_stdout) + if (YS_REGEX_NS::regex_search(line, x.re)) + x.matched = true; + + pos = linebuf.find('\n'); + } + } + status = pclose(f); +#endif + + if(WIFEXITED(status)) { + retval = WEXITSTATUS(status); + } + else if(WIFSIGNALED(status)) { + retval = WTERMSIG(status); + } + else if(WIFSTOPPED(status)) { + retval = WSTOPSIG(status); + } + + if (flag_expect_return && retval != expect_return_value) + log_cmd_error("Return value %d did not match expected return value %d.\n", retval, expect_return_value); + + if (flag_expect_stdout) + for (auto &x : expect_stdout) + if (x.polarity ^ x.matched) + log_cmd_error("Command stdout did%s have a line matching given regex \"%s\".\n", (x.polarity? " not" : ""), x.str.c_str()); + + log_pop(); + } +} ExecPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 1657ef818..b64b077e4 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -625,9 +625,13 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se } } -static void select_stmt(RTLIL::Design *design, std::string arg) +static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_empty_warning = false) { std::string arg_mod, arg_memb; + std::unordered_map<std::string, bool> arg_mod_found; + std::unordered_map<std::string, bool> arg_memb_found; + auto isalpha = [](const char &x) { return ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z')); }; + bool prefixed = GetSize(arg) >= 2 && isalpha(arg[0]) && arg[1] == ':'; if (arg.size() == 0) return; @@ -758,19 +762,21 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (!design->selected_active_module.empty()) { arg_mod = design->selected_active_module; arg_memb = arg; + if (!prefixed) arg_memb_found[arg_memb] = false; } else - if (GetSize(arg) >= 2 && arg[0] >= 'a' && arg[0] <= 'z' && arg[1] == ':') { + if (prefixed && arg[0] >= 'a' && arg[0] <= 'z') { arg_mod = "*", arg_memb = arg; } else { size_t pos = arg.find('/'); if (pos == std::string::npos) { - if (arg.find(':') == std::string::npos || arg.compare(0, 1, "A") == 0) - arg_mod = arg; - else - arg_mod = "*", arg_memb = arg; + arg_mod = arg; + if (!prefixed) arg_mod_found[arg_mod] = false; } else { arg_mod = arg.substr(0, pos); + if (!prefixed) arg_mod_found[arg_mod] = false; arg_memb = arg.substr(pos+1); + bool arg_memb_prefixed = GetSize(arg_memb) >= 2 && isalpha(arg_memb[0]) && arg_memb[1] == ':'; + if (!arg_memb_prefixed) arg_memb_found[arg_memb] = false; } } @@ -789,8 +795,14 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (!match_attr(mod->attributes, arg_mod.substr(2))) continue; } else + if (arg_mod.compare(0, 2, "N:") == 0) { + if (!match_ids(mod->name, arg_mod.substr(2))) + continue; + } else if (!match_ids(mod->name, arg_mod)) continue; + else + arg_mod_found[arg_mod] = true; if (arg_memb == "") { sel.selected_modules.insert(mod->name); @@ -839,7 +851,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (match_ids(it.first, arg_memb.substr(2))) sel.selected_members[mod->name].insert(it.first); } else - if (arg_memb.compare(0, 2, "c:") ==0) { + if (arg_memb.compare(0, 2, "c:") == 0) { for (auto cell : mod->cells()) if (match_ids(cell->name, arg_memb.substr(2))) sel.selected_members[mod->name].insert(cell->name); @@ -873,24 +885,44 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (match_attr(cell->parameters, arg_memb.substr(2))) sel.selected_members[mod->name].insert(cell->name); } else { + std::string orig_arg_memb = arg_memb; if (arg_memb.compare(0, 2, "n:") == 0) arg_memb = arg_memb.substr(2); for (auto wire : mod->wires()) - if (match_ids(wire->name, arg_memb)) + if (match_ids(wire->name, arg_memb)) { sel.selected_members[mod->name].insert(wire->name); + arg_memb_found[orig_arg_memb] = true; + } for (auto &it : mod->memories) - if (match_ids(it.first, arg_memb)) + if (match_ids(it.first, arg_memb)) { sel.selected_members[mod->name].insert(it.first); + arg_memb_found[orig_arg_memb] = true; + } for (auto cell : mod->cells()) - if (match_ids(cell->name, arg_memb)) + if (match_ids(cell->name, arg_memb)) { sel.selected_members[mod->name].insert(cell->name); + arg_memb_found[orig_arg_memb] = true; + } for (auto &it : mod->processes) - if (match_ids(it.first, arg_memb)) + if (match_ids(it.first, arg_memb)) { sel.selected_members[mod->name].insert(it.first); + arg_memb_found[orig_arg_memb] = true; + } } } select_filter_active_mod(design, work_stack.back()); + + for (auto &it : arg_mod_found) { + if (it.second == false && !disable_empty_warning) { + log_warning("Selection \"%s\" did not match any module.\n", it.first.c_str()); + } + } + for (auto &it : arg_memb_found) { + if (it.second == false && !disable_empty_warning) { + log_warning("Selection \"%s\" did not match any object.\n", it.first.c_str()); + } + } } static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel) @@ -1074,6 +1106,10 @@ struct SelectPass : public Pass { log(" all modules with an attribute matching the given pattern\n"); log(" in addition to = also <, <=, >=, and > are supported\n"); log("\n"); + log(" N:<pattern>\n"); + log(" all modules with a name matching the given pattern\n"); + log(" (i.e. 'N:' is optional as it is the default matching rule)\n"); + log("\n"); log("An <obj_pattern> can be an object name, wildcard expression, or one of\n"); log("the following:\n"); log("\n"); @@ -1276,7 +1312,8 @@ struct SelectPass : public Pass { } if (arg.size() > 0 && arg[0] == '-') log_cmd_error("Unknown option %s.\n", arg.c_str()); - select_stmt(design, arg); + bool disable_empty_warning = count_mode || assert_none || assert_any || (assert_count != -1) || (assert_max != -1) || (assert_min != -1); + select_stmt(design, arg, disable_empty_warning); sel_str += " " + arg; } diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index a85c3bec0..0f7b4d106 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -422,11 +422,7 @@ struct FsmExtractPass : public Pass { log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n"); extra_args(args, 1, design); - CellTypes ct; - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + CellTypes ct(design); for (auto &mod_it : design->modules_) { diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index e0bb439f4..148480d55 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -88,25 +88,24 @@ struct BruteForceEquivChecker mod1(mod1), mod2(mod2), counter(0), errors(0), ignore_x_mod1(ignore_x_mod1) { log("Checking for equivalence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str()); - for (auto &w : mod1->wires_) + for (auto w : mod1->wires()) { - RTLIL::Wire *wire1 = w.second; - if (wire1->port_id == 0) + if (w->port_id == 0) continue; - if (mod2->wires_.count(wire1->name) == 0) - log_cmd_error("Port %s in module 1 has no counterpart in module 2!\n", wire1->name.c_str()); + if (mod2->wire(w->name) == nullptr) + log_cmd_error("Port %s in module 1 has no counterpart in module 2!\n", w->name.c_str()); - RTLIL::Wire *wire2 = mod2->wires_.at(wire1->name); - if (wire1->width != wire2->width || wire1->port_input != wire2->port_input || wire1->port_output != wire2->port_output) - log_cmd_error("Port %s in module 1 does not match its counterpart in module 2!\n", wire1->name.c_str()); + RTLIL::Wire *w2 = mod2->wire(w->name); + if (w->width != w2->width || w->port_input != w2->port_input || w->port_output != w2->port_output) + log_cmd_error("Port %s in module 1 does not match its counterpart in module 2!\n", w->name.c_str()); - if (wire1->port_input) { - mod1_inputs.append(wire1); - mod2_inputs.append(wire2); + if (w->port_input) { + mod1_inputs.append(w); + mod2_inputs.append(w2); } else { - mod1_outputs.append(wire1); - mod2_outputs.append(wire2); + mod1_outputs.append(w); + mod2_outputs.append(w2); } } @@ -148,17 +147,17 @@ struct VlogHammerReporter SatGen satgen(ez.get(), &sigmap); satgen.model_undef = model_undef; - for (auto &c : module->cells_) - if (!satgen.importCell(c.second)) - log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + for (auto c : module->cells()) + if (!satgen.importCell(c)) + log_error("Failed to import cell %s (type %s) to SAT database.\n", log_id(c->name), log_id(c->type)); ez->assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); - std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y")); + std::vector<int> y_vec = satgen.importDefSigSpec(module->wire("\\y")); std::vector<bool> y_values; if (model_undef) { - std::vector<int> y_undef_vec = satgen.importUndefSigSpec(module->wires_.at("\\y")); + std::vector<int> y_undef_vec = satgen.importUndefSigSpec(module->wire("\\y")); y_vec.insert(y_vec.end(), y_undef_vec.begin(), y_undef_vec.end()); } @@ -253,7 +252,7 @@ struct VlogHammerReporter std::vector<RTLIL::State> bits(patterns[idx].bits.begin(), patterns[idx].bits.begin() + total_input_width); for (int i = 0; i < int(inputs.size()); i++) { - RTLIL::Wire *wire = module->wires_.at(inputs[i]); + RTLIL::Wire *wire = module->wire(inputs[i]); for (int j = input_widths[i]-1; j >= 0; j--) { ce.set(RTLIL::SigSpec(wire, j), bits.back()); recorded_set_vars.append(RTLIL::SigSpec(wire, j)); @@ -263,21 +262,21 @@ struct VlogHammerReporter if (module == modules.front()) { RTLIL::SigSpec sig(wire); if (!ce.eval(sig)) - log_error("Can't read back value for port %s!\n", RTLIL::id2cstr(inputs[i])); + log_error("Can't read back value for port %s!\n", log_id(inputs[i])); input_pattern_list += stringf(" %s", sig.as_const().as_string().c_str()); - log("++PAT++ %d %s %s #\n", idx, RTLIL::id2cstr(inputs[i]), sig.as_const().as_string().c_str()); + log("++PAT++ %d %s %s #\n", idx, log_id(inputs[i]), sig.as_const().as_string().c_str()); } } - if (module->wires_.count("\\y") == 0) - log_error("No output wire (y) found in module %s!\n", RTLIL::id2cstr(module->name)); + if (module->wire("\\y") == nullptr) + log_error("No output wire (y) found in module %s!\n", log_id(module->name)); - RTLIL::SigSpec sig(module->wires_.at("\\y")); + RTLIL::SigSpec sig(module->wire("\\y")); RTLIL::SigSpec undef; while (!ce.eval(sig, undef)) { - // log_error("Evaluation of y in module %s failed: sig=%s, undef=%s\n", RTLIL::id2cstr(module->name), log_signal(sig), log_signal(undef)); - log_warning("Setting signal %s in module %s to undef.\n", log_signal(undef), RTLIL::id2cstr(module->name)); + // log_error("Evaluation of y in module %s failed: sig=%s, undef=%s\n", log_id(module->name), log_signal(sig), log_signal(undef)); + log_warning("Setting signal %s in module %s to undef.\n", log_signal(undef), log_id(module->name)); ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size())); } @@ -289,7 +288,7 @@ struct VlogHammerReporter sat_check(module, recorded_set_vars, recorded_set_vals, sig, true); } else if (rtl_sig.size() > 0) { if (rtl_sig.size() != sig.size()) - log_error("Output (y) has a different width in module %s compared to rtl!\n", RTLIL::id2cstr(module->name)); + log_error("Output (y) has a different width in module %s compared to rtl!\n", log_id(module->name)); for (int i = 0; i < GetSize(sig); i++) if (rtl_sig[i] == RTLIL::State::Sx) sig[i] = RTLIL::State::Sx; @@ -307,10 +306,10 @@ struct VlogHammerReporter { for (auto name : split(module_list, ",")) { RTLIL::IdString esc_name = RTLIL::escape_id(module_prefix + name); - if (design->modules_.count(esc_name) == 0) + if (design->module(esc_name) == nullptr) log_error("Can't find module %s in current design!\n", name.c_str()); log("Using module %s (%s).\n", esc_name.c_str(), name.c_str()); - modules.push_back(design->modules_.at(esc_name)); + modules.push_back(design->module(esc_name)); module_names.push_back(name); } @@ -319,11 +318,11 @@ struct VlogHammerReporter int width = -1; RTLIL::IdString esc_name = RTLIL::escape_id(name); for (auto mod : modules) { - if (mod->wires_.count(esc_name) == 0) - log_error("Can't find input %s in module %s!\n", name.c_str(), RTLIL::id2cstr(mod->name)); - RTLIL::Wire *port = mod->wires_.at(esc_name); + if (mod->wire(esc_name) == nullptr) + log_error("Can't find input %s in module %s!\n", name.c_str(), log_id(mod->name)); + RTLIL::Wire *port = mod->wire(esc_name); if (!port->port_input || port->port_output) - log_error("Wire %s in module %s is not an input!\n", name.c_str(), RTLIL::id2cstr(mod->name)); + log_error("Wire %s in module %s is not an input!\n", name.c_str(), log_id(mod->name)); if (width >= 0 && width != port->width) log_error("Port %s has different sizes in the different modules!\n", name.c_str()); width = port->width; @@ -415,11 +414,11 @@ struct EvalPass : public Pass { /* this should only be used for regression testing of ConstEval -- see vloghammer */ std::string mod1_name = RTLIL::escape_id(args[++argidx]); std::string mod2_name = RTLIL::escape_id(args[++argidx]); - if (design->modules_.count(mod1_name) == 0) + if (design->module(mod1_name) == nullptr) log_error("Can't find module `%s'!\n", mod1_name.c_str()); - if (design->modules_.count(mod2_name) == 0) + if (design->module(mod2_name) == nullptr) log_error("Can't find module `%s'!\n", mod2_name.c_str()); - BruteForceEquivChecker checker(design->modules_.at(mod1_name), design->modules_.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x"); + BruteForceEquivChecker checker(design->module(mod1_name), design->module(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x"); if (checker.errors > 0) log_cmd_error("Modules are not equivalent!\n"); log("Verified %s = %s (using brute-force check on %d cases).\n", @@ -441,13 +440,12 @@ struct EvalPass : public Pass { extra_args(args, argidx, design); RTLIL::Module *module = NULL; - for (auto &mod_it : design->modules_) - if (design->selected(mod_it.second)) { - if (module) - log_cmd_error("Only one module must be selected for the EVAL pass! (selected: %s and %s)\n", - RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first)); - module = mod_it.second; - } + for (auto mod : design->selected_modules()) { + if (module) + log_cmd_error("Only one module must be selected for the EVAL pass! (selected: %s and %s)\n", + log_id(module->name), log_id(mod->name)); + module = mod; + } if (module == NULL) log_cmd_error("Can't perform EVAL on an empty selection!\n"); @@ -468,9 +466,9 @@ struct EvalPass : public Pass { } if (shows.size() == 0) { - for (auto &it : module->wires_) - if (it.second->port_output) - shows.push_back(it.second->name.str()); + for (auto w : module->wires()) + if (w->port_output) + shows.push_back(w->name.str()); } if (tables.empty()) diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index f29631639..54016e528 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -614,29 +614,29 @@ struct FreduceWorker int bits_full_total = 0; std::vector<std::set<RTLIL::SigBit>> batches; - for (auto &it : module->wires_) - if (it.second->port_input) { - batches.push_back(sigmap(it.second).to_sigbit_set()); - bits_full_total += it.second->width; + for (auto w : module->wires()) + if (w->port_input) { + batches.push_back(sigmap(w).to_sigbit_set()); + bits_full_total += w->width; } - for (auto &it : module->cells_) { - if (ct.cell_known(it.second->type)) { + for (auto cell : module->cells()) { + if (ct.cell_known(cell->type)) { std::set<RTLIL::SigBit> inputs, outputs; - for (auto &port : it.second->connections()) { + for (auto &port : cell->connections()) { std::vector<RTLIL::SigBit> bits = sigmap(port.second).to_sigbit_vector(); - if (ct.cell_output(it.second->type, port.first)) + if (ct.cell_output(cell->type, port.first)) outputs.insert(bits.begin(), bits.end()); else inputs.insert(bits.begin(), bits.end()); } - std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> drv(it.second, inputs); + std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> drv(cell, inputs); for (auto &bit : outputs) drivers[bit] = drv; batches.push_back(outputs); bits_full_total += outputs.size(); } - if (inv_mode && it.second->type == "$_NOT_") - inv_pairs.insert(std::pair<RTLIL::SigBit, RTLIL::SigBit>(sigmap(it.second->getPort("\\A")), sigmap(it.second->getPort("\\Y")))); + if (inv_mode && cell->type == "$_NOT_") + inv_pairs.insert(std::pair<RTLIL::SigBit, RTLIL::SigBit>(sigmap(cell->getPort("\\A")), sigmap(cell->getPort("\\Y")))); } int bits_count = 0; @@ -828,10 +828,8 @@ struct FreducePass : public Pass { extra_args(args, argidx, design); int bitcount = 0; - for (auto &mod_it : design->modules_) { - RTLIL::Module *module = mod_it.second; - if (design->selected(module)) - bitcount += FreduceWorker(design, module).run(); + for (auto module : design->selected_modules()) { + bitcount += FreduceWorker(design, module).run(); } log("Rewired a total of %d signal bits.\n", bitcount); diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 49ef40061..742433935 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -66,50 +66,48 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: RTLIL::IdString gate_name = RTLIL::escape_id(args[argidx++]); RTLIL::IdString miter_name = RTLIL::escape_id(args[argidx++]); - if (design->modules_.count(gold_name) == 0) + if (design->module(gold_name) == nullptr) log_cmd_error("Can't find gold module %s!\n", gold_name.c_str()); - if (design->modules_.count(gate_name) == 0) + if (design->module(gate_name) == nullptr) log_cmd_error("Can't find gate module %s!\n", gate_name.c_str()); - if (design->modules_.count(miter_name) != 0) + if (design->module(miter_name) != nullptr) log_cmd_error("There is already a module %s!\n", miter_name.c_str()); - RTLIL::Module *gold_module = design->modules_.at(gold_name); - RTLIL::Module *gate_module = design->modules_.at(gate_name); + RTLIL::Module *gold_module = design->module(gold_name); + RTLIL::Module *gate_module = design->module(gate_name); - for (auto &it : gold_module->wires_) { - RTLIL::Wire *w1 = it.second, *w2; - if (w1->port_id == 0) + for (auto gold_wire : gold_module->wires()) { + if (gold_wire->port_id == 0) continue; - if (gate_module->wires_.count(it.second->name) == 0) + RTLIL::Wire *gate_wire = gate_module->wire(gold_wire->name); + if (gate_wire == nullptr) goto match_gold_port_error; - w2 = gate_module->wires_.at(it.second->name); - if (w1->port_input != w2->port_input) + if (gold_wire->port_input != gate_wire->port_input) goto match_gold_port_error; - if (w1->port_output != w2->port_output) + if (gold_wire->port_output != gate_wire->port_output) goto match_gold_port_error; - if (w1->width != w2->width) + if (gold_wire->width != gate_wire->width) goto match_gold_port_error; continue; match_gold_port_error: - log_cmd_error("No matching port in gate module was found for %s!\n", it.second->name.c_str()); + log_cmd_error("No matching port in gate module was found for %s!\n", gold_wire->name.c_str()); } - for (auto &it : gate_module->wires_) { - RTLIL::Wire *w1 = it.second, *w2; - if (w1->port_id == 0) + for (auto gate_wire : gate_module->wires()) { + if (gate_wire->port_id == 0) continue; - if (gold_module->wires_.count(it.second->name) == 0) + RTLIL::Wire *gold_wire = gold_module->wire(gate_wire->name); + if (gold_wire == nullptr) goto match_gate_port_error; - w2 = gold_module->wires_.at(it.second->name); - if (w1->port_input != w2->port_input) + if (gate_wire->port_input != gold_wire->port_input) goto match_gate_port_error; - if (w1->port_output != w2->port_output) + if (gate_wire->port_output != gold_wire->port_output) goto match_gate_port_error; - if (w1->width != w2->width) + if (gate_wire->width != gold_wire->width) goto match_gate_port_error; continue; match_gate_port_error: - log_cmd_error("No matching port in gold module was found for %s!\n", it.second->name.c_str()); + log_cmd_error("No matching port in gold module was found for %s!\n", gate_wire->name.c_str()); } log("Creating miter cell \"%s\" with gold cell \"%s\" and gate cell \"%s\".\n", RTLIL::id2cstr(miter_name), RTLIL::id2cstr(gold_name), RTLIL::id2cstr(gate_name)); @@ -123,73 +121,71 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: RTLIL::SigSpec all_conditions; - for (auto &it : gold_module->wires_) + for (auto gold_wire : gold_module->wires()) { - RTLIL::Wire *w1 = it.second; - - if (w1->port_input) + if (gold_wire->port_input) { - RTLIL::Wire *w2 = miter_module->addWire("\\in_" + RTLIL::unescape_id(w1->name), w1->width); - w2->port_input = true; + RTLIL::Wire *w = miter_module->addWire("\\in_" + RTLIL::unescape_id(gold_wire->name), gold_wire->width); + w->port_input = true; - gold_cell->setPort(w1->name, w2); - gate_cell->setPort(w1->name, w2); + gold_cell->setPort(gold_wire->name, w); + gate_cell->setPort(gold_wire->name, w); } - if (w1->port_output) + if (gold_wire->port_output) { - RTLIL::Wire *w2_gold = miter_module->addWire("\\gold_" + RTLIL::unescape_id(w1->name), w1->width); - w2_gold->port_output = flag_make_outputs; + RTLIL::Wire *w_gold = miter_module->addWire("\\gold_" + RTLIL::unescape_id(gold_wire->name), gold_wire->width); + w_gold->port_output = flag_make_outputs; - RTLIL::Wire *w2_gate = miter_module->addWire("\\gate_" + RTLIL::unescape_id(w1->name), w1->width); - w2_gate->port_output = flag_make_outputs; + RTLIL::Wire *w_gate = miter_module->addWire("\\gate_" + RTLIL::unescape_id(gold_wire->name), gold_wire->width); + w_gate->port_output = flag_make_outputs; - gold_cell->setPort(w1->name, w2_gold); - gate_cell->setPort(w1->name, w2_gate); + gold_cell->setPort(gold_wire->name, w_gold); + gate_cell->setPort(gold_wire->name, w_gate); RTLIL::SigSpec this_condition; if (flag_ignore_gold_x) { - RTLIL::SigSpec gold_x = miter_module->addWire(NEW_ID, w2_gold->width); - for (int i = 0; i < w2_gold->width; i++) { + RTLIL::SigSpec gold_x = miter_module->addWire(NEW_ID, w_gold->width); + for (int i = 0; i < w_gold->width; i++) { RTLIL::Cell *eqx_cell = miter_module->addCell(NEW_ID, "$eqx"); eqx_cell->parameters["\\A_WIDTH"] = 1; eqx_cell->parameters["\\B_WIDTH"] = 1; eqx_cell->parameters["\\Y_WIDTH"] = 1; eqx_cell->parameters["\\A_SIGNED"] = 0; eqx_cell->parameters["\\B_SIGNED"] = 0; - eqx_cell->setPort("\\A", RTLIL::SigSpec(w2_gold, i)); + eqx_cell->setPort("\\A", RTLIL::SigSpec(w_gold, i)); eqx_cell->setPort("\\B", RTLIL::State::Sx); eqx_cell->setPort("\\Y", gold_x.extract(i, 1)); } - RTLIL::SigSpec gold_masked = miter_module->addWire(NEW_ID, w2_gold->width); - RTLIL::SigSpec gate_masked = miter_module->addWire(NEW_ID, w2_gate->width); + RTLIL::SigSpec gold_masked = miter_module->addWire(NEW_ID, w_gold->width); + RTLIL::SigSpec gate_masked = miter_module->addWire(NEW_ID, w_gate->width); RTLIL::Cell *or_gold_cell = miter_module->addCell(NEW_ID, "$or"); - or_gold_cell->parameters["\\A_WIDTH"] = w2_gold->width; - or_gold_cell->parameters["\\B_WIDTH"] = w2_gold->width; - or_gold_cell->parameters["\\Y_WIDTH"] = w2_gold->width; + or_gold_cell->parameters["\\A_WIDTH"] = w_gold->width; + or_gold_cell->parameters["\\B_WIDTH"] = w_gold->width; + or_gold_cell->parameters["\\Y_WIDTH"] = w_gold->width; or_gold_cell->parameters["\\A_SIGNED"] = 0; or_gold_cell->parameters["\\B_SIGNED"] = 0; - or_gold_cell->setPort("\\A", w2_gold); + or_gold_cell->setPort("\\A", w_gold); or_gold_cell->setPort("\\B", gold_x); or_gold_cell->setPort("\\Y", gold_masked); RTLIL::Cell *or_gate_cell = miter_module->addCell(NEW_ID, "$or"); - or_gate_cell->parameters["\\A_WIDTH"] = w2_gate->width; - or_gate_cell->parameters["\\B_WIDTH"] = w2_gate->width; - or_gate_cell->parameters["\\Y_WIDTH"] = w2_gate->width; + or_gate_cell->parameters["\\A_WIDTH"] = w_gate->width; + or_gate_cell->parameters["\\B_WIDTH"] = w_gate->width; + or_gate_cell->parameters["\\Y_WIDTH"] = w_gate->width; or_gate_cell->parameters["\\A_SIGNED"] = 0; or_gate_cell->parameters["\\B_SIGNED"] = 0; - or_gate_cell->setPort("\\A", w2_gate); + or_gate_cell->setPort("\\A", w_gate); or_gate_cell->setPort("\\B", gold_x); or_gate_cell->setPort("\\Y", gate_masked); RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx"); - eq_cell->parameters["\\A_WIDTH"] = w2_gold->width; - eq_cell->parameters["\\B_WIDTH"] = w2_gate->width; + eq_cell->parameters["\\A_WIDTH"] = w_gold->width; + eq_cell->parameters["\\B_WIDTH"] = w_gate->width; eq_cell->parameters["\\Y_WIDTH"] = 1; eq_cell->parameters["\\A_SIGNED"] = 0; eq_cell->parameters["\\B_SIGNED"] = 0; @@ -201,20 +197,20 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: else { RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx"); - eq_cell->parameters["\\A_WIDTH"] = w2_gold->width; - eq_cell->parameters["\\B_WIDTH"] = w2_gate->width; + eq_cell->parameters["\\A_WIDTH"] = w_gold->width; + eq_cell->parameters["\\B_WIDTH"] = w_gate->width; eq_cell->parameters["\\Y_WIDTH"] = 1; eq_cell->parameters["\\A_SIGNED"] = 0; eq_cell->parameters["\\B_SIGNED"] = 0; - eq_cell->setPort("\\A", w2_gold); - eq_cell->setPort("\\B", w2_gate); + eq_cell->setPort("\\A", w_gold); + eq_cell->setPort("\\B", w_gate); eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID)); this_condition = eq_cell->getPort("\\Y"); } if (flag_make_outcmp) { - RTLIL::Wire *w_cmp = miter_module->addWire("\\cmp_" + RTLIL::unescape_id(w1->name)); + RTLIL::Wire *w_cmp = miter_module->addWire("\\cmp_" + RTLIL::unescape_id(gold_wire->name)); w_cmp->port_output = true; miter_module->connect(RTLIL::SigSig(w_cmp, this_condition)); } @@ -285,9 +281,9 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL IdString module_name = RTLIL::escape_id(args[argidx++]); IdString miter_name = argidx < args.size() ? RTLIL::escape_id(args[argidx++]) : ""; - if (design->modules_.count(module_name) == 0) + if (design->module(module_name) == nullptr) log_cmd_error("Can't find module %s!\n", module_name.c_str()); - if (!miter_name.empty() && design->modules_.count(miter_name) != 0) + if (!miter_name.empty() && design->module(miter_name) != nullptr) log_cmd_error("There is already a module %s!\n", miter_name.c_str()); Module *module = design->module(module_name); diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index f63012d1a..8b1862237 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -308,7 +308,9 @@ struct IopadmapPass : public Pass { { log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); - Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); + Cell *cell = module->addCell( + module->uniquify(stringf("$iopadmap$%s.%s[%d]", log_id(module), log_id(wire), i)), + RTLIL::escape_id(tinoutpad_celltype)); cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig); cell->attributes[ID::keep] = RTLIL::Const(1); @@ -328,7 +330,9 @@ struct IopadmapPass : public Pass { } else { log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); - Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); + Cell *cell = module->addCell( + module->uniquify(stringf("$iopadmap$%s.%s[%d]", log_id(module), log_id(wire), i)), + RTLIL::escape_id(toutpad_celltype)); cell->setPort(RTLIL::escape_id(toutpad_portname_oe), en_sig); cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig); @@ -406,7 +410,9 @@ struct IopadmapPass : public Pass { SigBit wire_bit(wire, i); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); + RTLIL::Cell *cell = module->addCell( + module->uniquify(stringf("$iopadmap$%s.%s", log_id(module->name), log_id(wire->name))), + RTLIL::escape_id(celltype)); cell->setPort(RTLIL::escape_id(portname_int), wire_bit); if (!portname_pad.empty()) @@ -420,12 +426,16 @@ struct IopadmapPass : public Pass { } else { - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); + RTLIL::Cell *cell = module->addCell( + module->uniquify(stringf("$iopadmap$%s.%s", log_id(module->name), log_id(wire->name))), + RTLIL::escape_id(celltype)); cell->setPort(RTLIL::escape_id(portname_int), RTLIL::SigSpec(wire)); if (!portname_pad.empty()) { RTLIL::Wire *new_wire = NULL; - new_wire = module->addWire(NEW_ID, wire); + new_wire = module->addWire( + module->uniquify(stringf("$iopadmap$%s", log_id(wire))), + wire); module->swap_names(new_wire, wire); wire->attributes.clear(); cell->setPort(RTLIL::escape_id(portname_pad), RTLIL::SigSpec(new_wire)); @@ -446,7 +456,9 @@ struct IopadmapPass : public Pass { for (auto &it : rewrite_bits) { RTLIL::Wire *wire = it.first; - RTLIL::Wire *new_wire = module->addWire(NEW_ID, wire); + RTLIL::Wire *new_wire = module->addWire( + module->uniquify(stringf("$iopadmap$%s", log_id(wire))), + wire); module->swap_names(new_wire, wire); wire->attributes.clear(); for (int i = 0; i < wire->width; i++) diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 0c57733d4..10001baaa 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -177,10 +177,10 @@ struct TechmapWorker std::string orig_cell_name; pool<string> extra_src_attrs = cell->get_strpool_attribute(ID(src)); + orig_cell_name = cell->name.str(); if (!flatten_mode) { for (auto &it : tpl->cells_) if (it.first == ID(_TECHMAP_REPLACE_)) { - orig_cell_name = cell->name.str(); module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); break; } diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index aa1d7aa86..6a0e3031e 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -2382,9 +2382,9 @@ module SB_SPRAM256KA ( // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13167 //$setup(negedge STANDBY, posedge CLOCK, 1715); // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13206 - $setup(WREN, posedge CLK, 289); + $setup(WREN, posedge CLOCK, 289); // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13207-L13222 - (posedge RCLK => (DATAOUT : 16'bx)) = 1821; + (posedge CLOCK => (DATAOUT : 16'bx)) = 1821; // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13223-L13238 (posedge SLEEP => (DATAOUT : 16'b0)) = 1099; endspecify diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 80bd05a84..b8aedaadf 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -345,6 +345,7 @@ struct SynthIce40Pass : public ScriptPass if (min_ce_use >= 0) { run("opt_merge"); run(stringf("dff2dffe -unmap-mince %d", min_ce_use)); + run("simplemap t:$dff"); } run("techmap -D NO_LUT -D NO_ADDER -map +/ice40/cells_map.v"); run("opt_expr -mux_undef"); diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py index 749b1e0a7..f086291ab 100644 --- a/techlibs/xilinx/cells_xtra.py +++ b/techlibs/xilinx/cells_xtra.py @@ -302,7 +302,7 @@ CELLS = [ Cell('IOBUF_DCIEN', port_attrs={'IO': ['iopad_external_pin']}), Cell('IOBUF_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin']}), Cell('IOBUFE3', port_attrs={'IO': ['iopad_external_pin']}), - Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin']}), + Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), Cell('IOBUFDS_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), Cell('IOBUFDS_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), Cell('IOBUFDS_DIFF_OUT', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index ac4ad4e36..3021f6b5a 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -7072,6 +7072,7 @@ module IOBUFDS (...); output O; (* iopad_external_pin *) inout IO; + (* iopad_external_pin *) inout IOB; input I; input T; diff --git a/tests/select/no_warn_assert.ys b/tests/select/no_warn_assert.ys new file mode 100644 index 000000000..889315826 --- /dev/null +++ b/tests/select/no_warn_assert.ys @@ -0,0 +1,2 @@ +logger -expect-no-warnings +select -assert-count 0 top/t:ff4 top/w:d0 %co:+[d] %i diff --git a/tests/select/no_warn_prefixed_arg_memb.ys b/tests/select/no_warn_prefixed_arg_memb.ys new file mode 100644 index 000000000..596a6ed70 --- /dev/null +++ b/tests/select/no_warn_prefixed_arg_memb.ys @@ -0,0 +1,5 @@ +logger -expect-no-warnings +read_verilog ../../examples/igloo2/example.v +hierarchy +proc +select example/t:$add diff --git a/tests/select/no_warn_prefixed_empty_select_arg.ys b/tests/select/no_warn_prefixed_empty_select_arg.ys new file mode 100644 index 000000000..617e0d63e --- /dev/null +++ b/tests/select/no_warn_prefixed_empty_select_arg.ys @@ -0,0 +1,3 @@ +logger -expect-no-warnings +select n:foo/bar* +select t:$assert diff --git a/tests/select/run-test.sh b/tests/select/run-test.sh new file mode 100755 index 000000000..44ce7e674 --- /dev/null +++ b/tests/select/run-test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +for x in *.ys; do + echo "Running $x.." + ../../yosys -ql ${x%.ys}.log $x +done diff --git a/tests/select/warn_empty_select_arg.ys b/tests/select/warn_empty_select_arg.ys new file mode 100644 index 000000000..55aca8eb6 --- /dev/null +++ b/tests/select/warn_empty_select_arg.ys @@ -0,0 +1,3 @@ +logger -expect warning "did not match any module." 1 +logger -expect warning "did not match any object." 1 +select foo/bar diff --git a/tests/svtypes/enum_simple.sv b/tests/svtypes/enum_simple.sv index ccaf50da0..4e4d5871c 100644 --- a/tests/svtypes/enum_simple.sv +++ b/tests/svtypes/enum_simple.sv @@ -5,8 +5,9 @@ module enum_simple(input clk, input rst); typedef enum logic [1:0] { ts0, ts1, ts2, ts3 } states_t; - (states_t) state; - (states_t) enum_const = ts1; + states_t state; + (states_t) state1; + states_t enum_const = ts1; always @(posedge clk) begin if (rst) begin diff --git a/tests/svtypes/typedef_memory.sv b/tests/svtypes/typedef_memory.sv index 577e484ad..37e63c1d0 100644 --- a/tests/svtypes/typedef_memory.sv +++ b/tests/svtypes/typedef_memory.sv @@ -1,7 +1,7 @@ module top(input [3:0] addr, wdata, input clk, wen, output reg [3:0] rdata); typedef logic [3:0] ram16x4_t[0:15]; - (ram16x4_t) mem; + ram16x4_t mem; always @(posedge clk) begin if (wen) mem[addr] <= wdata; diff --git a/tests/svtypes/typedef_memory_2.sv b/tests/svtypes/typedef_memory_2.sv index f3089bf55..6d65131db 100644 --- a/tests/svtypes/typedef_memory_2.sv +++ b/tests/svtypes/typedef_memory_2.sv @@ -1,7 +1,7 @@ module top(input [3:0] addr, wdata, input clk, wen, output reg [3:0] rdata); typedef logic [3:0] nibble; - (nibble) mem[0:15]; + nibble mem[0:15]; always @(posedge clk) begin if (wen) mem[addr] <= wdata; diff --git a/tests/svtypes/typedef_package.sv b/tests/svtypes/typedef_package.sv index b766f10cf..57a78c53a 100644 --- a/tests/svtypes/typedef_package.sv +++ b/tests/svtypes/typedef_package.sv @@ -5,8 +5,8 @@ endpackage module top; - (* keep *) (pkg::uint8_t) a = 8'hAA; - (* keep *) (pkg::enum8_t) b_enum = pkg::bb; + (* keep *) pkg::uint8_t a = 8'hAA; + (* keep *) pkg::enum8_t b_enum = pkg::bb; always @* assert(a == 8'hAA); always @* assert(b_enum == 8'hBB); diff --git a/tests/svtypes/typedef_param.sv b/tests/svtypes/typedef_param.sv index ddbd471e0..d838dd828 100644 --- a/tests/svtypes/typedef_param.sv +++ b/tests/svtypes/typedef_param.sv @@ -6,12 +6,12 @@ module top; typedef logic [1:0] uint2_t; typedef logic signed [3:0] int4_t; typedef logic signed [7:0] int8_t; - typedef (int8_t) char_t; + typedef int8_t char_t; - parameter (uint2_t) int2 = 2'b10; - localparam (int4_t) int4 = -1; - localparam (int8_t) int8 = int4; - localparam (char_t) ch = int8; + parameter uint2_t int2 = 2'b10; + localparam int4_t int4 = -1; + localparam int8_t int8 = int4; + localparam char_t ch = int8; `STATIC_ASSERT(int2 == 2'b10); diff --git a/tests/svtypes/typedef_scopes.sv b/tests/svtypes/typedef_scopes.sv index 1c45c7057..5507d84f2 100644 --- a/tests/svtypes/typedef_scopes.sv +++ b/tests/svtypes/typedef_scopes.sv @@ -4,32 +4,39 @@ typedef enum logic {s0, s1} outer_enum_t; module top; - (outer_uint4_t) u4_i = 8'hA5; - (outer_enum_t) enum4_i = s0; + outer_uint4_t u4_i = 8'hA5; + outer_enum_t enum4_i = s0; always @(*) assert(u4_i == 4'h5); always @(*) assert(enum4_i == 1'b0); typedef logic [3:0] inner_type; typedef enum logic [2:0] {s2=2, s3, s4} inner_enum_t; - (inner_type) inner_i1 = 8'h5A; - (inner_enum_t) inner_enum1 = s3; + inner_type inner_i1 = 8'h5A; + inner_enum_t inner_enum1 = s3; always @(*) assert(inner_i1 == 4'hA); always @(*) assert(inner_enum1 == 3'h3); if (1) begin: genblock typedef logic [7:0] inner_type; - parameter (inner_type) inner_const = 8'hA5; + parameter inner_type inner_const = 8'hA5; typedef enum logic [2:0] {s5=5, s6, s7} inner_enum_t; - (inner_type) inner_gb_i = inner_const; //8'hA5; - (inner_enum_t) inner_gb_enum1 = s7; + inner_type inner_gb_i = inner_const; //8'hA5; + inner_enum_t inner_gb_enum1 = s7; always @(*) assert(inner_gb_i == 8'hA5); always @(*) assert(inner_gb_enum1 == 3'h7); end - (inner_type) inner_i2 = 8'h42; - (inner_enum_t) inner_enum2 = s4; + inner_type inner_i2 = 8'h42; + inner_enum_t inner_enum2 = s4; always @(*) assert(inner_i2 == 4'h2); always @(*) assert(inner_enum2 == 3'h4); +endmodule + +typedef logic[7:0] between_t; +module other; + between_t a = 8'h42; + always @(*) assert(a == 8'h42); endmodule + diff --git a/tests/svtypes/typedef_simple.sv b/tests/svtypes/typedef_simple.sv index 7e760dee4..8f89910e5 100644 --- a/tests/svtypes/typedef_simple.sv +++ b/tests/svtypes/typedef_simple.sv @@ -3,12 +3,12 @@ module top; typedef logic [1:0] uint2_t; typedef logic signed [3:0] int4_t; typedef logic signed [7:0] int8_t; - typedef (int8_t) char_t; + typedef int8_t char_t; - (* keep *) (uint2_t) int2 = 2'b10; - (* keep *) (int4_t) int4 = -1; - (* keep *) (int8_t) int8 = int4; - (* keep *) (char_t) ch = int8; + (* keep *) uint2_t int2 = 2'b10; + (* keep *) int4_t int4 = -1; + (* keep *) int8_t int8 = int4; + (* keep *) char_t ch = int8; always @* assert(int2 == 2'b10); diff --git a/tests/techmap/techmap_replace.ys b/tests/techmap/techmap_replace.ys index c2f42d50b..8403586bd 100644 --- a/tests/techmap/techmap_replace.ys +++ b/tests/techmap/techmap_replace.ys @@ -16,3 +16,21 @@ EOT techmap -map %techmap select -assert-any w:s0.asdf select -assert-any c:s0.blah + +read_verilog <<EOT +module sub(input i, output o, input j); +wire _TECHMAP_REPLACE_.asdf = i ; +barfoo _TECHMAP_REPLACE_.blah (i, o, j); +endmodule +EOT +design -stash techmap + +read_verilog <<EOT +module top(input i, output o); +sub s0(i, o); +endmodule +EOT + +techmap -map %techmap +select -assert-any w:s0.asdf +select -assert-any c:s0.blah diff --git a/tests/various/bug1781.ys b/tests/various/bug1781.ys new file mode 100644 index 000000000..60dcc0830 --- /dev/null +++ b/tests/various/bug1781.ys @@ -0,0 +1,33 @@ +read_verilog <<EOT + +module top(input clk, input rst); + +reg [1:0] state; + +always @(posedge clk, posedge rst) begin + if (rst) + state <= 0; + else + case (state) + 2'b00: state <= 2'b01; + 2'b01: state <= 2'b10; + 2'b10: state <= 2'b00; + endcase +end + +sub sub_i(.i(state == 0)); + +endmodule + + +(* blackbox, keep *) +module sub(input i); +endmodule + +EOT + +proc +fsm + +# Make sure there is a driver +select -assert-any t:sub %ci %a w:* %i %ci c:* %i diff --git a/tests/various/exec.ys b/tests/various/exec.ys new file mode 100644 index 000000000..0eec00719 --- /dev/null +++ b/tests/various/exec.ys @@ -0,0 +1,6 @@ +exec -expect-return 0 -- exit 0 +exec -expect-return 27 -- exit 27 +exec -expect-stdout nana -expect-stdout api -not-expect-stdout giraffe -- echo "bananapie" + +logger -expect error "stdout did have a line" 1 +exec -not-expect-stdout giraffe -- echo "giraffe" diff --git a/tests/various/ice40_mince_abc9.ys b/tests/various/ice40_mince_abc9.ys new file mode 100644 index 000000000..408e16f05 --- /dev/null +++ b/tests/various/ice40_mince_abc9.ys @@ -0,0 +1,17 @@ +read_verilog <<EOT + +module top(input clk, ce, input [2:0] a, b, output reg [2:0] q); + + reg [2:0] aa, bb; + + always @(posedge clk) begin + if (ce) begin + aa <= a; + end + bb <= b; + q <= aa + bb; + end +endmodule +EOT + +synth_ice40 -abc9 -dffe_min_ce_use 4 diff --git a/tests/various/sv_defines.ys b/tests/various/sv_defines.ys new file mode 100644 index 000000000..8e70ee0ee --- /dev/null +++ b/tests/various/sv_defines.ys @@ -0,0 +1,33 @@ +# Check that basic macro expansions do what you'd expect + +read_verilog <<EOT +`define empty_arglist() 123 +`define one_arg(x) 123+x +`define opt_arg(x = 1) 123+x +`define two_args(x, y = (1+23)) x+y +`define nested_comma(x = {31'b0, 1'b1}, y=3) x+y + +module top; + localparam a = `empty_arglist(); + localparam b = `one_arg(10); + localparam c = `opt_arg(10); + localparam d = `opt_arg(); + localparam e = `two_args(1,2); + localparam f = `two_args(1); + localparam g = `nested_comma(1, 2); + localparam h = `nested_comma({31'b0, (1'b0)}); + localparam i = `nested_comma(, 1); + + generate + if (a != 123) $error("a bad"); + if (b != 133) $error("b bad"); + if (c != 133) $error("c bad"); + if (d != 124) $error("d bad"); + if (e != 3) $error("e bad"); + if (f != 25) $error("f bad"); + if (g != 3) $error("g bad"); + if (h != 3) $error("h bad"); + if (i != 2) $error("i bad"); + endgenerate +endmodule +EOT diff --git a/tests/various/sv_defines_dup.ys b/tests/various/sv_defines_dup.ys new file mode 100644 index 000000000..38418ba8f --- /dev/null +++ b/tests/various/sv_defines_dup.ys @@ -0,0 +1,5 @@ +# Check for duplicate arguments +logger -expect error "Duplicate macro arguments with name `x'" 1 +read_verilog <<EOT +`define duplicate_arg(x, x) +EOT diff --git a/tests/various/sv_defines_mismatch.ys b/tests/various/sv_defines_mismatch.ys new file mode 100644 index 000000000..ab6e899de --- /dev/null +++ b/tests/various/sv_defines_mismatch.ys @@ -0,0 +1,5 @@ +# Check that we spot mismatched brackets +logger -expect error "Mismatched brackets in macro argument: \[ and }." 1 +read_verilog <<EOT +`define foo(x=[1,2}) +EOT diff --git a/tests/various/sv_defines_too_few.ys b/tests/various/sv_defines_too_few.ys new file mode 100644 index 000000000..295884809 --- /dev/null +++ b/tests/various/sv_defines_too_few.ys @@ -0,0 +1,7 @@ +# Check that we don't allow passing too few arguments (and, while we're at it, check that passing "no" +# arguments actually passes 1 empty argument). +logger -expect error "Cannot expand macro `foo by giving only 1 argument \(argument 2 has no default\)." 1 +read_verilog <<EOT +`define foo(x=1, y) +`foo() +EOT |