aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--backends/smt2/smtio.py8
-rw-r--r--frontends/ast/simplify.cc145
-rw-r--r--frontends/verilog/preproc.cc620
-rw-r--r--frontends/verilog/preproc.h77
-rw-r--r--frontends/verilog/verilog_frontend.cc26
-rw-r--r--frontends/verilog/verilog_frontend.h4
-rw-r--r--kernel/rtlil.cc2
-rw-r--r--kernel/rtlil.h5
-rw-r--r--passes/cmds/add.cc4
-rw-r--r--passes/cmds/design.cc3
-rw-r--r--passes/cmds/exec.cc2
-rw-r--r--passes/cmds/select.cc61
-rw-r--r--passes/techmap/techmap.cc2
-rw-r--r--tests/select/no_warn_assert.ys2
-rw-r--r--tests/select/no_warn_prefixed_arg_memb.ys5
-rw-r--r--tests/select/no_warn_prefixed_empty_select_arg.ys3
-rwxr-xr-xtests/select/run-test.sh6
-rw-r--r--tests/select/warn_empty_select_arg.ys3
-rw-r--r--tests/techmap/techmap_replace.ys18
-rw-r--r--tests/various/sv_defines.ys33
-rw-r--r--tests/various/sv_defines_dup.ys5
-rw-r--r--tests/various/sv_defines_mismatch.ys5
-rw-r--r--tests/various/sv_defines_too_few.ys7
24 files changed, 818 insertions, 229 deletions
diff --git a/Makefile b/Makefile
index 49ab780d5..3c89fed20 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
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/ast/simplify.cc b/frontends/ast/simplify.cc
index 743c7e18f..10e9f2683 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1898,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);
@@ -1907,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);
@@ -1940,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)
@@ -2026,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));
@@ -2036,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/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 &macro_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 &macro_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 1c88d479b..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>
@@ -254,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;
@@ -370,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") {
@@ -422,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) {
@@ -431,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()) {
@@ -461,7 +463,7 @@ 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);
@@ -593,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) {
@@ -602,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 caa6246ef..444cc7297 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -86,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/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/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
index 399cb0ebb..7eeefe705 100644
--- a/passes/cmds/exec.cc
+++ b/passes/cmds/exec.cc
@@ -22,11 +22,13 @@
#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
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/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/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/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/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